fspl/generator/expression-multiplex.go

272 lines
8.2 KiB
Go

package generator
import "fmt"
import "git.tebibyte.media/fspl/fspl/llvm"
import "git.tebibyte.media/fspl/fspl/entity"
type resultMode int; const (
resultModeAny resultMode = iota
resultModeVal
resultModeLoc
)
func (mode resultMode) String () string {
switch(mode) {
case resultModeAny: return "resultModeAny"
case resultModeVal: return "resultModeVal"
case resultModeLoc: return "resultModeLoc"
default: return fmt.Sprintf("resultMode(%d)", mode)
}
}
func (this *generator) valueToLocation (value llvm.Value) llvm.Value {
pointer := this.blockManager.newAllocaFront(value.Type())
this.blockManager.NewStore(value, pointer)
return pointer
}
func (this *generator) locationToValue (pointer llvm.Value, ty entity.Type) (llvm.Value, error) {
irType, err := this.generateType(ty)
if err != nil { return nil, err }
return this.blockManager.NewLoad(irType, pointer), nil
}
func (this *generator) generateExpression (
expression entity.Expression,
mode resultMode,
) (
register llvm.Value,
location bool,
err error,
) {
switch mode {
case resultModeAny:
return this.generateExpressionAny(expression)
case resultModeVal:
val, err := this.generateExpressionVal(expression)
return val, false, err
case resultModeLoc:
loc, err := this.generateExpressionLoc(expression)
return loc, true, err
default: panic("unknown resultMode")
}
}
// generateExpression generates an expression and either returns a register
// representing the result, or a register containing the location of the result,
// whichever will generate the least IR. In the latter case, the boolean
// "location" will be true.
func (this *generator) generateExpressionAny (expression entity.Expression) (register llvm.Value, location bool, err error) {
switch expression := expression.(type) {
// these give us an address
case *entity.Dereference,
*entity.MemberAccess,
*entity.Slice,
*entity.Subscript,
*entity.LiteralArray,
*entity.LiteralString,
*entity.LiteralStruct,
*entity.Variable,
*entity.Declaration:
pointer, err := this.generateExpressionLoc(expression)
return pointer, true, err
// these give us a value
case *entity.Call,
*entity.MethodCall,
*entity.Reference,
*entity.Length,
*entity.ValueCast,
*entity.BitCast,
*entity.Operation,
*entity.LiteralInt,
*entity.LiteralFloat,
*entity.LiteralBoolean,
*entity.LiteralNil:
value, err := this.generateExpressionVal(expression)
return value, true, err
// these are capable of giving us both
case *entity.Block:
return this.generateBlock(expression, resultModeAny)
case *entity.IfElse:
return this.generateIfElse(expression, resultModeAny)
case *entity.Match:
return this.generateMatch(expression, resultModeAny)
case *entity.Switch:
return this.generateSwitch(expression, resultModeAny)
case *entity.Loop:
return this.generateLoop(expression, resultModeAny)
case *entity.For:
return this.generateFor(expression, resultModeAny)
// we get nothing from these
case *entity.Assignment:
_, err := this.generateAssignment(expression)
return nil, false, err
case *entity.Break:
_, err := this.generateBreak(expression)
return nil, false, err
case *entity.Return:
_, err := this.generateReturn(expression)
return nil, false, err
default:
panic(fmt.Sprintf (
"BUG: generator doesnt know about expression %v, ty: %T",
expression, expression))
}
}
// generateExpressionVal generates an expression and returns a register
// representing the result.
func (this *generator) generateExpressionVal (expression entity.Expression) (llvm.Value, error) {
// we get an address from these, so we need to load the value
switch expression := expression.(type) {
case *entity.Dereference,
*entity.MemberAccess,
*entity.Slice,
*entity.Subscript,
*entity.LiteralArray,
*entity.LiteralString,
*entity.LiteralStruct,
*entity.Variable,
*entity.Declaration:
pointer, err := this.generateExpressionLoc(expression)
if err != nil { return nil, err }
return this.locationToValue(pointer, expression.Type())
// we get a value from these, so we can return them as-is
case *entity.Call:
return this.generateCallVal(expression)
case *entity.MethodCall:
return this.generateMethodCallVal(expression)
case *entity.Reference:
return this.generateReferenceVal(expression)
case *entity.Length:
return this.generateLengthVal(expression)
case *entity.ValueCast:
return this.generateValueCastVal(expression)
case *entity.BitCast:
return this.generateBitCastVal(expression)
case *entity.Operation:
return this.generateOperationVal(expression)
case *entity.Block:
val, _, err := this.generateBlock(expression, resultModeVal)
return val, err
case *entity.IfElse:
val, _, err := this.generateIfElse(expression, resultModeVal)
return val, err
case *entity.Match:
val, _, err := this.generateMatch(expression, resultModeVal)
return val, err
case *entity.Switch:
val, _, err := this.generateSwitch(expression, resultModeVal)
return val, err
case *entity.Loop:
val, _, err := this.generateLoop(expression, resultModeVal)
return val, err
case *entity.For:
val, _, err := this.generateFor(expression, resultModeVal)
return val, err
case *entity.LiteralInt:
return this.generateLiteralInt(expression)
case *entity.LiteralFloat:
return this.generateLiteralFloat(expression)
case *entity.LiteralBoolean:
return this.generateLiteralBoolean(expression)
case *entity.LiteralNil:
return this.generateLiteralNil(expression)
// we get nothing from these
case *entity.Assignment:
return this.generateAssignment(expression)
case *entity.Break:
return this.generateBreak(expression)
case *entity.Return:
return this.generateReturn(expression)
default:
panic(fmt.Sprintf (
"BUG: generator doesnt know about value expression %v, ty: %T",
expression, expression))
}
}
// generateExpressionLoc generates an expression and returns a register
// representing the address of the result. This function avoids adding alloca
// instructions whenever possible, trying to refer to the original data.
func (this *generator) generateExpressionLoc (expression entity.Expression) (llvm.Value, error) {
switch expression := expression.(type) {
// we get a value from these, so we need to store the value and then
// return an address to that
case *entity.Call,
*entity.MethodCall,
*entity.Reference,
*entity.Length,
*entity.ValueCast,
*entity.BitCast,
*entity.Operation,
*entity.LiteralInt,
*entity.LiteralFloat,
*entity.LiteralBoolean,
*entity.LiteralNil:
value, err := this.generateExpressionVal(expression)
if err != nil { return nil, err }
return this.valueToLocation(value), nil
// we get an address from these, so we can return them as-is
case *entity.Variable:
return this.generateVariableLoc(expression)
case *entity.Declaration:
return this.generateDeclarationLoc(expression)
case *entity.Slice:
return this.generateSliceLoc(expression)
case *entity.Subscript:
return this.generateSubscriptLoc(expression)
case *entity.Dereference:
return this.generateDereferenceLoc(expression)
case *entity.MemberAccess:
return this.generateMemberAccessLoc(expression)
case *entity.Block:
loc, _, err := this.generateBlock(expression, resultModeLoc)
return loc, err
case *entity.IfElse:
loc, _, err := this.generateIfElse(expression, resultModeLoc)
return loc, err
case *entity.Match:
loc, _, err := this.generateMatch(expression, resultModeLoc)
return loc, err
case *entity.Switch:
loc, _, err := this.generateSwitch(expression, resultModeLoc)
return loc, err
case *entity.Loop:
loc, _, err := this.generateLoop(expression, resultModeLoc)
return loc, err
case *entity.For:
loc, _, err := this.generateFor(expression, resultModeLoc)
return loc, err
case *entity.LiteralArray:
return this.generateLiteralArrayLoc(expression, nil)
case *entity.LiteralString:
return this.generateLiteralStringLoc(expression, nil)
case *entity.LiteralStruct:
return this.generateLiteralStructLoc(expression, nil)
// we get nothing from these
case *entity.Assignment:
return this.generateAssignment(expression)
case *entity.Break:
return this.generateBreak(expression)
case *entity.Return:
return this.generateReturn(expression)
default:
panic(fmt.Sprintf (
"BUG: generator doesnt know about location expression %v, ty: %T",
expression, expression))
}
}