fspl/generator/expression.go

683 lines
20 KiB
Go

package generator
import "fmt"
import "errors"
import "git.tebibyte.media/sashakoshka/fspl/llvm"
import "git.tebibyte.media/sashakoshka/fspl/entity"
import "git.tebibyte.media/sashakoshka/fspl/analyzer"
func (this *generator) generateVariable (variable *entity.Variable) (llvm.Value, error) {
location, err := this.generateVariableLoc(variable)
if err != nil { return nil, err }
irType, err := this.generateType(variable.Type())
if err != nil { return nil, err }
return this.blockManager.NewLoad(irType, location), nil
}
func (this *generator) generateCall (call *entity.Call) (llvm.Value, error) {
function, err := this.function(call.Name)
if err != nil { return nil, err }
args := make([]llvm.Value, len(call.Arguments))
for index, argument := range call.Arguments {
irArgument, err := this.generateAssignmentSource (
argument,
call.Function.Signature.Arguments[index].Type())
if err != nil { return nil, err }
args[index] = irArgument
}
return this.blockManager.NewCall(function, function.Signature, args...), nil
}
func (this *generator) generateMethodCall (call *entity.MethodCall) (llvm.Value, error) {
source, err := this.generateExpression(call.Source)
if err != nil { return nil, err }
var signature *entity.Signature
if call.Method != nil { signature = call.Method.Signature }
if call.Behavior != nil { signature = call.Behavior }
// build list of args
args := make([]llvm.Value, len(call.Arguments) + 1)
for index, argument := range call.Arguments {
irArgument, err := this.generateAssignmentSource (
argument,
signature.Arguments[index].Type())
if err != nil { return nil, err }
args[index + 1] = irArgument
}
// check for methods on named type
if sourceType, ok := call.Source.Type().(*entity.TypeNamed); ok {
method, err := this.method(sourceType.Name, call.Name)
if err != nil { return nil, err }
args[0] = source
return this.blockManager.NewCall(method, method.Signature, args...), nil
}
// check for methods on pointer to named type
if pointerType, ok := call.Source.Type().(*entity.TypePointer); ok {
if sourceType, ok := pointerType.Referenced.(*entity.TypeNamed); ok {
method, err := this.method(sourceType.Name, call.Name)
if err != nil { return nil, err }
args[0] = this.blockManager.NewLoad(source.Type(), source)
return this.blockManager.NewCall(method, method.Signature, args...), nil
}
}
// check for interface behaviors
if sourceType := getInterface(call.Source.Type()); sourceType != nil {
irSourceType, err := this.generateType(sourceType)
if err != nil { return nil, err }
irBehavior := this.getInterfaceBehavior(sourceType, source, irSourceType, call.Name)
if irBehavior != nil {
irValue := this.getInterfaceData(source, irSourceType)
signature, err := this.getInterfaceBehaviorSignature(sourceType, call.Name)
if err != nil { return nil, err }
args[0] = this.blockManager.NewLoad(irValue.Type(), irValue)
return this.blockManager.NewCall (
irBehavior,
signature.(*llvm.TypeFunction),
args...), nil
}
}
return nil, errors.New(fmt.Sprintln("no method", call.Name, "for", call.Source))
}
func (this *generator) generateReference (reference *entity.Reference) (llvm.Value, error) {
return this.generateExpressionLoc(reference.Value)
}
func (this *generator) generateLength (length *entity.Length) (llvm.Value, error) {
var sizeType *llvm.TypeInt; {
ty, err := this.typedef("Index")
if err != nil { return nil, err }
sizeType = ty.(*llvm.TypeInt)
}
source, err := this.generateExpressionLoc(length.Slice)
if err != nil { return nil, err }
sourceType := analyzer.ReduceToBase(length.Slice.Type())
switch sourceType := sourceType.(type) {
case *entity.TypeSlice:
irSourceType, err := this.generateType(sourceType)
if err != nil { return nil, err }
lengthFieldAddress := this.blockManager.NewGetElementPtr (
irSourceType,
source,
llvm.NewConstInt(llvm.I32, 0),
llvm.NewConstInt(llvm.I32, 1))
return this.blockManager.NewLoad(sizeType, lengthFieldAddress), nil
case *entity.TypeArray:
return llvm.NewConstInt(sizeType, int64(sourceType.Length)), nil
default:
panic(fmt.Sprint (
"BUG: generator can't get length of expression ",
length.Slice))
}
}
func (this *generator) generateValueCast (cast *entity.ValueCast) (llvm.Value, error) {
generateFrom := func () (llvm.Value, llvm.Type, error) {
from, err := this.generateExpression(cast.Value)
if err != nil { return nil, nil, err }
to, err := this.generateType(cast.Type())
if err != nil { return nil, nil,err }
return from, to, nil
}
generateFromLoc := func () (llvm.Value, llvm.Type, error) {
from, err := this.generateExpressionLoc(cast.Value)
if err != nil { return nil, nil, err }
to, err := this.generateType(cast.Type())
if err != nil { return nil, nil,err }
return from, to, nil
}
fail := func () (llvm.Value, error) {
return nil, errors.New(fmt.Sprintf (
"cant convert %v to %v",
cast.Value.Type(), cast.Type()))
}
fromType := analyzer.ReduceToBase(cast.Value.Type())
toType := analyzer.ReduceToBase(cast.Type())
switch toType.(type) {
case *entity.TypeInt, *entity.TypeWord:
from, to, err := generateFrom()
if err != nil { return nil, err }
switch fromType := fromType.(type) {
case *entity.TypeInt, *entity.TypeWord:
return this.blockManager.NewTrunc(from, to), nil
case *entity.TypeFloat:
if analyzer.IsUnsigned(fromType) {
return this.blockManager.NewFPToUI(from, to), nil
} else {
return this.blockManager.NewFPToSI(from, to), nil
}
default: return fail()
}
case *entity.TypeFloat:
from, to, err := generateFrom()
if err != nil { return nil, err }
switch fromType := fromType.(type) {
case *entity.TypeFloat:
return this.blockManager.NewFPTrunc(from, to), nil
case *entity.TypeInt, *entity.TypeWord:
if analyzer.IsUnsigned(fromType) {
return this.blockManager.NewUIToFP(from, to), nil
} else {
return this.blockManager.NewSIToFP(from, to), nil
}
default: return fail()
}
case *entity.TypePointer:
switch fromType := fromType.(type) {
case *entity.TypeSlice:
from, _, err := generateFromLoc()
if err != nil { return nil, err }
irFromType, err := this.generateType(fromType)
return this.blockManager.NewGetElementPtr (
irFromType, from,
llvm.NewConstInt(llvm.I32, 0),
llvm.NewConstInt(llvm.I32, 0)), nil
case *entity.TypePointer:
from, to, err := generateFrom()
if err != nil { return nil, err }
return this.blockManager.NewBitCast(from, to), nil
default: return fail()
}
case *entity.TypeSlice,
*entity.TypeArray,
*entity.TypeStruct:
from, to, err := generateFrom()
if err != nil { return nil, err }
return this.blockManager.NewBitCast(from, to), nil
default: return fail()
}
}
func (this *generator) generateBitCast (cast *entity.BitCast) (llvm.Value, error) {
from, err := this.generateExpression(cast.Value)
if err != nil { return nil, err }
to, err := this.generateType(cast.Type())
if err != nil { return nil, err }
return this.blockManager.NewBitCast(from, to), nil
}
func (this *generator) generateOperation (operation *entity.Operation) (llvm.Value, error) {
nSameType := func (binary func (x, y llvm.Value) llvm.Value) (llvm.Value, error) {
irX, err := this.generateExpression(operation.Arguments[0])
if err != nil { return nil, err }
for _, argument := range operation.Arguments[1:] {
irY, err := this.generateExpression(argument)
if err != nil { return nil, err }
irX = binary(irX, irY)
}
return irX, nil
}
ty := analyzer.ReduceToBase(operation.Arguments[0].Type())
irType, err := this.generateType(ty)
if err != nil { return nil, err }
switch operation.Operator {
// math
case entity.OperatorAdd:
switch ty.(type) {
case *entity.TypeInt, *entity.TypeWord:
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewAdd(x, y)
})
case *entity.TypeFloat:
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewFAdd(x, y)
})
}
case entity.OperatorSubtract:
switch ty.(type) {
case *entity.TypeInt, *entity.TypeWord:
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewSub(x, y)
})
case *entity.TypeFloat:
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewFSub(x, y)
})
}
case entity.OperatorMultiply:
switch ty.(type) {
case *entity.TypeInt, *entity.TypeWord:
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewMul(x, y)
})
case *entity.TypeFloat:
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewFMul(x, y)
})
}
case entity.OperatorDivide:
switch ty.(type) {
case *entity.TypeInt, *entity.TypeWord:
if analyzer.IsUnsigned(ty) {
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewUDiv(x, y)
})
} else {
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewSDiv(x, y)
})
}
case *entity.TypeFloat:
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewFDiv(x, y)
})
}
case entity.OperatorIncrement:
irX, err := this.generateExpression(operation.Arguments[0])
if err != nil { return nil, err }
irType := irType.(*llvm.TypeInt)
switch ty.(type) {
case *entity.TypeInt, *entity.TypeWord:
return this.blockManager.NewAdd (
irX,
llvm.NewConstInt(irType, 1)), nil
case *entity.TypeFloat:
return this.blockManager.NewFAdd (
irX,
llvm.NewConstInt(irType, 1)), nil
}
case entity.OperatorDecrement:
irX, err := this.generateExpression(operation.Arguments[0])
if err != nil { return nil, err }
irType := irType.(*llvm.TypeInt)
switch ty.(type) {
case *entity.TypeInt, *entity.TypeWord:
return this.blockManager.NewSub (
irX,
llvm.NewConstInt(irType, 1)), nil
case *entity.TypeFloat:
return this.blockManager.NewFSub (
irX,
llvm.NewConstInt(irType, 1)), nil
}
case entity.OperatorModulo:
switch ty.(type) {
case *entity.TypeInt, *entity.TypeWord:
if analyzer.IsUnsigned(ty) {
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewURem(x, y)
})
} else {
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewSRem(x, y)
})
}
case *entity.TypeFloat:
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewFDiv(x, y)
})
}
// logic
case entity.OperatorLogicalNot:
irX, err := this.generateExpression(operation.Arguments[0])
if err != nil { return nil, err }
return this.blockManager.NewICmp(llvm.IPredicateEQ, irX, llvm.NewConstBool(false)), nil
case entity.OperatorLogicalOr:
incomings := make([]*llvm.Incoming, len(operation.Arguments) + 1)
exit := this.blockManager.newBlock()
for index, argument := range operation.Arguments {
irX, err := this.generateExpression(argument)
if err != nil { return nil, err }
incomings[index] = &llvm.Incoming {
X: llvm.NewConstBool(true),
Predecessor: this.blockManager.Block,
}
previous := this.blockManager.Block
block := this.blockManager.newBlock()
previous.NewCondBr(irX, exit, block)
}
this.blockManager.NewBr(exit)
incomings[len(incomings) - 1] = &llvm.Incoming {
X: llvm.NewConstBool(false),
Predecessor: this.blockManager.Block,
}
this.blockManager.Block = exit
return this.blockManager.NewPhi(incomings...), nil
case entity.OperatorLogicalAnd:
incomings := make([]*llvm.Incoming, len(operation.Arguments) + 1)
exit := this.blockManager.newBlock()
for index, argument := range operation.Arguments {
irX, err := this.generateExpression(argument)
if err != nil { return nil, err }
incomings[index] = &llvm.Incoming {
X: llvm.NewConstBool(false),
Predecessor: this.blockManager.Block,
}
previous := this.blockManager.Block
block := this.blockManager.newBlock()
previous.NewCondBr(irX, block, exit)
}
this.blockManager.NewBr(exit)
incomings[len(incomings) - 1] = &llvm.Incoming {
X: llvm.NewConstBool(true),
Predecessor: this.blockManager.Block,
}
this.blockManager.Block = exit
return this.blockManager.NewPhi(incomings...), nil
case entity.OperatorLogicalXor:
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewICmp (
llvm.IPredicateNE,
this.blockManager.NewICmp(llvm.IPredicateEQ, x, llvm.NewConstBool(true)),
this.blockManager.NewICmp(llvm.IPredicateEQ, x, llvm.NewConstBool(true)))
})
// bit manipulation
case entity.OperatorNot:
irX, err := this.generateExpression(operation.Arguments[0])
if err != nil { return nil, err }
return this.blockManager.NewXor (
irX,
llvm.NewConstInt(irType.(*llvm.TypeInt), -1)), nil
case entity.OperatorOr:
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewOr(x, y)
})
case entity.OperatorAnd:
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewAnd(x, y)
})
case entity.OperatorXor:
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewXor(x, y)
})
case entity.OperatorLeftShift:
irX, err := this.generateExpression(operation.Arguments[0])
if err != nil { return nil, err }
irY, err := this.generateExpression(operation.Arguments[1])
if err != nil { return nil, err }
return this.blockManager.NewShl(irX, irY), nil
case entity.OperatorRightShift:
irX, err := this.generateExpression(operation.Arguments[0])
if err != nil { return nil, err }
irY, err := this.generateExpression(operation.Arguments[1])
if err != nil { return nil, err }
if analyzer.IsUnsigned(ty) {
return this.blockManager.NewLShr(irX, irY), nil
} else {
return this.blockManager.NewAShr(irX, irY), nil
}
// comparison
case entity.OperatorLess:
switch ty.(type) {
case *entity.TypeInt, *entity.TypeWord:
var predicate llvm.IPredicate
if analyzer.IsUnsigned(ty) {
predicate = llvm.IPredicateULT
} else {
predicate = llvm.IPredicateSLT
}
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewICmp(predicate, x, y)
})
case *entity.TypeFloat:
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewFCmp (
llvm.FPredicateOLT,
x, y)
})
}
case entity.OperatorGreater:
switch ty.(type) {
case *entity.TypeInt, *entity.TypeWord:
var predicate llvm.IPredicate
if analyzer.IsUnsigned(ty) {
predicate = llvm.IPredicateUGT
} else {
predicate = llvm.IPredicateSGT
}
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewICmp(predicate, x, y)
})
case *entity.TypeFloat:
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewFCmp (
llvm.FPredicateOGT,
x, y)
})
}
case entity.OperatorLessEqual:
switch ty.(type) {
case *entity.TypeInt, *entity.TypeWord:
var predicate llvm.IPredicate
if analyzer.IsUnsigned(ty) {
predicate = llvm.IPredicateULE
} else {
predicate = llvm.IPredicateSLE
}
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewICmp(predicate, x, y)
})
case *entity.TypeFloat:
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewFCmp (
llvm.FPredicateOLE,
x, y)
})
}
case entity.OperatorGreaterEqual:
switch ty.(type) {
case *entity.TypeInt, *entity.TypeWord:
var predicate llvm.IPredicate
if analyzer.IsUnsigned(ty) {
predicate = llvm.IPredicateUGE
} else {
predicate = llvm.IPredicateSGE
}
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewICmp(predicate, x, y)
})
case *entity.TypeFloat:
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewFCmp (
llvm.FPredicateOGE,
x, y)
})
}
case entity.OperatorEqual:
switch ty.(type) {
case *entity.TypeInt, *entity.TypeWord:
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewICmp (
llvm.IPredicateEQ,
x, y)
})
case *entity.TypeFloat:
return nSameType(func (x, y llvm.Value) llvm.Value {
return this.blockManager.NewFCmp (
llvm.FPredicateOEQ,
x, y)
})
}
default:
panic(fmt.Sprint (
"BUG: generator doesnt know about operator",
operation.Operator))
}
panic(fmt.Sprint (
"BUG: generator failed to generate operation",
operation))
}
func (this *generator) generateBlock (block *entity.Block) (llvm.Value, error) {
var last llvm.Value
var err error
for _, step := range block.Steps {
last, err = this.generateStatement(step)
if err != nil { return nil, err }
}
return last, nil
}
func (this *generator) generateIfElse (ifelse *entity.IfElse, loc bool) (llvm.Value, error) {
condition, err := this.generateExpression(ifelse.Condition)
if err != nil { return nil, err }
previous := this.blockManager.Block
var trueBlock, falseBlock *llvm.Block
var tru, fals llvm.Value
trueBlock = this.blockManager.newBlock()
if loc {
tru, err = this.generateExpressionLoc(ifelse.True)
} else {
tru, err = this.generateExpression(ifelse.True)
}
if err != nil { return nil, err }
if ifelse.False != nil {
if ifelse.Type() == nil {
falseBlock = this.blockManager.newBlock()
if loc {
fals, err = this.generateExpressionLoc(ifelse.False)
} else {
fals, err = this.generateExpression(ifelse.False)
}
if err != nil { return nil, err }
previous.NewCondBr(condition, trueBlock, falseBlock)
exitBlock := this.blockManager.newBlock()
falseBlock.NewBr(exitBlock)
trueBlock.NewBr(exitBlock)
return nil, nil
} else {
falseBlock = this.blockManager.newBlock()
if loc {
fals, err = this.generateExpressionLoc(ifelse.False)
} else {
fals, err = this.generateExpression(ifelse.False)
}
if err != nil { return nil, err }
trueIncoming := &llvm.Incoming {
X: tru,
Predecessor: trueBlock,
}
falseIncoming := &llvm.Incoming {
X: fals,
Predecessor: falseBlock,
}
previous.NewCondBr(condition, trueBlock, falseBlock)
exitBlock := this.blockManager.newBlock()
falseBlock.NewBr(exitBlock)
trueBlock.NewBr(exitBlock)
return this.blockManager.NewPhi(trueIncoming, falseIncoming), nil
}
} else {
exitBlock := this.blockManager.newBlock()
trueBlock.NewBr(exitBlock)
if falseBlock == nil {
previous.NewCondBr(condition, trueBlock, exitBlock)
} else {
previous.NewCondBr(condition, trueBlock, falseBlock)
}
return nil, nil
}
}
func (this *generator) generateLoop (loop *entity.Loop, loc bool) (llvm.Value, error) {
previous := this.blockManager.Block
body := this.blockManager.newBlock()
previous.NewBr(body)
loopEntry := this.blockManager.pushLoop(loc)
defer this.blockManager.popLoop()
_, err := this.generateExpression(loop.Body)
if err != nil { return nil, err }
this.blockManager.NewBr(body)
exit := this.blockManager.newBlock()
if loopEntry.stub != nil {
loopEntry.stub.SetTerminator(&llvm.TerminatorBr {
Target: exit,
})
}
return loopEntry.value, nil
}
func (this *generator) generateBreak (brk *entity.Break) (llvm.Value, error) {
loopEntry := this.blockManager.topLoop()
if brk.Value != nil {
value, err := this.generateExpression(brk.Value)
if err != nil { return nil, err }
loopEntry.value = value
}
loopEntry.stub = this.blockManager.Block
return nil, nil
}
func (this *generator) generateReturn (ret *entity.Return) (llvm.Value, error) {
if ret.Value == nil {
this.blockManager.NewRet(nil)
} else {
value, err := this.generateExpression(ret.Value)
if err != nil { return nil, err }
this.blockManager.NewRet(value)
}
return nil, nil
}