fspl/generator/expression-val.go

651 lines
19 KiB
Go

package generator
import "fmt"
import "errors"
import "git.tebibyte.media/fspl/fspl/llvm"
import "git.tebibyte.media/fspl/fspl/entity"
import "git.tebibyte.media/fspl/fspl/analyzer"
func (this *generator) generateCallVal (call *entity.Call) (llvm.Value, error) {
function, err := this.function(entity.Key {
Unit: call.Unit,
Name: call.Name,
})
if err != nil { return nil, err }
args := make([]llvm.Value, len(call.Arguments))
for index, argument := range call.Arguments {
irArgument, err := this.generateAssignmentToDestination (
argument,
call.Function.Signature.Arguments[index].Type(),
nil)
if err != nil { return nil, err }
args[index] = irArgument
}
return this.blockManager.NewCall(function, function.Signature, args...), nil
}
func (this *generator) generateMethodCallVal (call *entity.MethodCall) (llvm.Value, error) {
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.generateAssignmentToDestination (
argument,
signature.Arguments[index].Type(),
nil)
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 {
methodKey := entity.Key {
Unit: sourceType.Type.Unit(),
Name: sourceType.Name,
Method: call.Name,
}
method, err := this.method(methodKey)
if _, notFound := err.(errNotFound); !notFound {
source, err := this.generateExpressionLoc(call.Source)
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(entity.Key {
Unit: sourceType.Type.Unit(),
Name: sourceType.Name,
Method: call.Name,
})
if _, notFound := err.(errNotFound); !notFound {
source, err := this.generateExpressionVal(call.Source)
if err != nil { return nil, err }
args[0] = source
return this.blockManager.NewCall(method, method.Signature, args...), nil
}
if err != nil { return nil, err }
}
}
// check for interface behaviors
if sourceType := getInterface(call.Source.Type()); sourceType != nil {
irSourceType, err := this.generateType(call.Source.Type())
if err != nil { return nil, err }
source, err := this.generateExpressionLoc(call.Source)
if err != nil { return nil, err }
irBehavior := this.getInterfaceBehaviorFieldLoc(sourceType, source, irSourceType, call.Name)
if irBehavior != nil {
irValue := this.getInterfaceDataFieldLoc(source, irSourceType)
signature, err := this.getInterfaceBehaviorSignature(sourceType, call.Name)
if err != nil { return nil, err }
args[0] = this.blockManager.NewLoad(irValue.Type(), irValue)
irBehavior := this.blockManager.NewLoad(llvm.Pointer, irBehavior)
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) generateReferenceVal (reference *entity.Reference) (llvm.Value, error) {
return this.generateExpressionLoc(reference.Value)
}
func (this *generator) generateLengthVal (length *entity.Length) (llvm.Value, error) {
sizeType, err := this.generateTypeIndex()
if err != nil { return nil, err }
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) generateValueCastVal (cast *entity.ValueCast) (llvm.Value, error) {
generateFrom := func () (llvm.Value, error) {
return this.generateExpressionVal(cast.Value)
}
generateFromLoc := func () (llvm.Value, error) {
return this.generateExpressionLoc(cast.Value)
}
irFromType, err := this.generateType(cast.Value.Type())
if err != nil { return nil, err }
irToType, err := this.generateType(cast.Type())
if err != nil { return nil, err }
// Attempt to short circuit if types are equal to avoid LLVM bitching at
// us
if llvm.TypesEqual(irFromType, irToType) {
return this.generateExpressionVal(cast.Value)
}
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, err := generateFrom()
if err != nil { return nil, err }
switch fromType := fromType.(type) {
case *entity.TypeInt, *entity.TypeWord:
// cast from integer to integer:
// basic truncated value assignment
return this.blockManager.NewTrunc(from, irToType), nil
case *entity.TypeFloat:
if analyzer.IsUnsigned(fromType) {
// cast from float to unsigned integer:
// convert float to unsigned integer
return this.blockManager.NewFPToUI(from, irToType), nil
} else {
// cast from float to signed integer:
// convert float to signed integer
return this.blockManager.NewFPToSI(from, irToType), nil
}
default: return fail()
}
case *entity.TypeFloat:
from, err := generateFrom()
if err != nil { return nil, err }
switch fromType := fromType.(type) {
case *entity.TypeFloat:
// cast from float to float:
// basic truncated value assignment
return this.blockManager.NewFPTrunc(from, irToType), nil
case *entity.TypeInt, *entity.TypeWord:
if analyzer.IsUnsigned(fromType) {
// cast from unsigned integer to float:
// convert unsigned integer to float
return this.blockManager.NewUIToFP(from, irToType), nil
} else {
// cast from signed integer to float:
// convert signed integer to float
return this.blockManager.NewSIToFP(from, irToType), nil
}
default: return fail()
}
case *entity.TypePointer:
switch fromType := fromType.(type) {
case *entity.TypeSlice:
// cast from slice to pointer:
// pointer will point to the first element of the slice
from, err := generateFromLoc()
if err != nil { return nil, err }
irFromType, err := this.generateType(fromType)
return this.getSliceDataAddress(from, irFromType), nil
case *entity.TypePointer:
// cast from pointer to pointer:
// basic forceful value assignment
// (all IR pointer types are equal)
return this.generateExpressionVal(cast.Value)
default: return fail()
}
case *entity.TypeSlice:
switch fromType.(type) {
case *entity.TypeSlice:
// cast from slice to slice:
// basic forceful value assignment
// (assume structural equivalence)
return this.generateExpressionVal(cast.Value)
default: return fail()
}
case *entity.TypeArray:
switch fromType.(type) {
case *entity.TypeArray:
// cast from array to array:
// basic forceful value assignment
// (assume structural equivalence)
return this.generateExpressionVal(cast.Value)
default: return fail()
}
case *entity.TypeStruct:
switch fromType.(type) {
case *entity.TypeArray:
// cast from struct to struct:
// basic forceful value assignment
// (assume structural equivalence)
return this.generateExpressionVal(cast.Value)
default: return fail()
}
default: return fail()
}
}
func (this *generator) generateBitCastVal (cast *entity.BitCast) (llvm.Value, error) {
irFromType, err := this.generateType(cast.Value.Type())
if err != nil { return nil, err }
irToType, err := this.generateType(cast.Type())
if err != nil { return nil, err }
// attempt to short circuit if types are equal, or else LLVM complains
if llvm.TypesEqual(irFromType, irToType) {
return this.generateExpressionVal(cast.Value)
}
from, err := this.generateExpressionVal(cast.Value)
if err != nil { return nil, err }
// determine how to *bit cast*. because of course its not that simple.
// thanks LLVM!
fromType := analyzer.ReduceToBase(cast.Value.Type())
toType := analyzer.ReduceToBase(cast.Type())
switch toType.(type) {
case *entity.TypeInt, *entity.TypeWord:
switch fromType.(type) {
case *entity.TypePointer:
// cast from pointer to int
return this.blockManager.NewPtrToInt(from, irToType), nil
}
case *entity.TypePointer:
switch fromType.(type) {
case *entity.TypeInt, *entity.TypeWord:
// cast from int to pointer
return this.blockManager.NewIntToPtr(from, irToType), nil
default:
// cast from something else to pointer
irIndexType, err := this.generateTypeIndex()
if err != nil { return nil, err }
temporaryInteger := this.blockManager.NewBitCast(from, irIndexType)
return this.blockManager.NewIntToPtr(temporaryInteger, irToType), nil
}
default:
switch fromType.(type) {
case *entity.TypePointer:
// cast from pointer to something else
irIndexType, err := this.generateTypeIndex()
if err != nil { return nil, err }
temporaryInteger := this.blockManager.NewPtrToInt(from, irIndexType)
return this.blockManager.NewBitCast(temporaryInteger, irToType), nil
}
}
// default to a normal bit cast
return this.blockManager.NewBitCast(from, irToType), nil
}
func (this *generator) generateOperationVal (operation *entity.Operation) (llvm.Value, error) {
nSameType := func (binary func (x, y llvm.Value) llvm.Value) (llvm.Value, error) {
irX, err := this.generateExpressionVal(operation.Arguments[0])
if err != nil { return nil, err }
for _, argument := range operation.Arguments[1:] {
irY, err := this.generateExpressionVal(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.generateExpressionVal(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.generateExpressionVal(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.generateExpressionVal(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.generateExpressionVal(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.generateExpressionVal(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.generateExpressionVal(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.generateExpressionVal(operation.Arguments[0])
if err != nil { return nil, err }
irY, err := this.generateExpressionVal(operation.Arguments[1])
if err != nil { return nil, err }
return this.blockManager.NewShl(irX, irY), nil
case entity.OperatorRightShift:
irX, err := this.generateExpressionVal(operation.Arguments[0])
if err != nil { return nil, err }
irY, err := this.generateExpressionVal(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))
}