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.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.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.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)) } }