diff --git a/generator/assignment.go b/generator/assignment.go new file mode 100644 index 0000000..a79f752 --- /dev/null +++ b/generator/assignment.go @@ -0,0 +1,49 @@ +package generator + +import "git.tebibyte.media/sashakoshka/fspl/llvm" +import "git.tebibyte.media/sashakoshka/fspl/entity" +import "git.tebibyte.media/sashakoshka/fspl/analyzer" + +func (this *generator) generateAssignment (assignment *entity.Assignment) (llvm.Value, error) { + source, err := this.generateAssignmentSource ( + assignment.Value, + assignment.Location.Type()) + if err != nil { return nil, err } + destination, err := this.generateExpressionLoc(assignment.Location) + if err != nil { return nil, err } + this.blockManager.NewStore(source, destination) + return nil, nil +} + +// generateAssignmentSource performs type coercions if necessary, mainly +// interface assignment. this should be called instead of generateExpression +// when the type to assign to is known. +func (this *generator) generateAssignmentSource ( + from entity.Expression, + to entity.Type, +) ( + llvm.Value, + error, +) { + source, err := this.generateExpression(from) + if err != nil { return nil, err } + ty := analyzer.ReduceToBase(to) + + switch ty := ty.(type) { + // case *entity.TypeInterface: + // TODO +// sourceTy := analyzer.ReduceToBase(from.Type()) +// irSourceTy, err := this,generateType(sourceTy) +// if err != nil { return nil, err } +// +// if +// +// irType, err := this.generateType(to) +// if err != nil { return nil, err } +// pointer := this. + + default: + ty = ty + return source, nil + } +} diff --git a/generator/blockmanager.go b/generator/blockmanager.go index e1ecba5..e159f07 100644 --- a/generator/blockmanager.go +++ b/generator/blockmanager.go @@ -1,13 +1,23 @@ package generator +import "fmt" +import "errors" import "git.tebibyte.media/sashakoshka/fspl/llvm" import "git.tebibyte.media/sashakoshka/fspl/entity" +type loopEntry struct { + value llvm.Value + stub *llvm.Block + wantLocation bool +} + type blockManager struct { *llvm.Block generator *generator function *llvm.Function declarations map[*entity.Declaration] llvm.Value + + loops []*loopEntry } func (this *generator) pushBlockManager (function *llvm.Function) *blockManager { @@ -30,25 +40,56 @@ func (this *generator) popBlockManager () { } } -func (this *blockManager) newBlock () *llvm.Block { - if this.Block != nil && len(this.Block.Instructions) == 0 { return nil } - - previous := this.Block - this.Block = this.function.NewBlock("") - return previous +func (this *blockManager) pushLoop (wantLocation bool) *loopEntry { + entry := &loopEntry { wantLocation: wantLocation } + this.loops = append(this.loops, entry) + return entry } -func (this *blockManager) variable (declaration *entity.Declaration) llvm.Value { - return this.declarations[declaration] +func (this *blockManager) popLoop () { + this.loops = this.loops[:len(this.loops) - 1] +} + +func (this *blockManager) topLoop () *loopEntry { + return this.loops[len(this.loops) - 1] +} + +func (this *blockManager) newAllocaFront (element llvm.Type) llvm.Value { + alloca := llvm.NewAlloca(element) + + firstBlock := this.function.Blocks[0] + if firstBlock.Terminated() { + lastIndex := len(firstBlock.Instructions) - 1 + firstBlock.Instructions = append ( + firstBlock.Instructions, + firstBlock.Instructions[lastIndex]) + firstBlock.Instructions[lastIndex] = alloca + } else { + firstBlock.AddInstruction(alloca) + } + return alloca +} + +func (this *blockManager) newBlock () *llvm.Block { + this.Block = this.function.NewBlock() + return this.Block +} + +func (this *blockManager) variable (declaration *entity.Declaration) (llvm.Value, error) { + value, ok := this.declarations[declaration] + if !ok { + return nil, errors.New(fmt.Sprintf("declaration of %v does not exist", declaration.Name)) + } + return value, nil } func (this *blockManager) addDeclaration (declaration *entity.Declaration, initial llvm.Value) error { ty, err := this.generator.generateType(declaration.Type()) if err != nil { return err } - location := this.Block.NewAlloca(ty) + location := this.newAllocaFront(ty) this.declarations[declaration] = location if initial != nil { - this.Block.NewStore(initial, location) + this.NewStore(initial, location) } return nil } diff --git a/generator/expression-multiplex.go b/generator/expression-multiplex.go index 2ab952f..6a08522 100644 --- a/generator/expression-multiplex.go +++ b/generator/expression-multiplex.go @@ -4,51 +4,66 @@ import "fmt" import "git.tebibyte.media/sashakoshka/fspl/llvm" import "git.tebibyte.media/sashakoshka/fspl/entity" +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 +} + +// generateExpression generates an expression and returns a register +// representing the result. func (this *generator) generateExpression (expression entity.Expression) (llvm.Value, error) { - switch expression.(type) { + // 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.LiteralStruct: + + 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.Variable: - return this.generateVariable(expression.(*entity.Variable)) + return this.generateVariable(expression) case *entity.Call: - return this.generateCall(expression.(*entity.Call)) + return this.generateCall(expression) case *entity.MethodCall: - return this.generateMethodCall(expression.(*entity.MethodCall)) - // case *entity.Subscript: - // return this.generateSubscript(expression.(*entity.Subscript)) - // case *entity.Slice: - // return this.generateSlice(expression.(*entity.Slice)) - // case *entity.Dereference: - // return this.generateDereference(expression.(*entity.Dereference)) - // case *entity.Reference: - // return this.generateReference(expression.(*entity.Reference)) + return this.generateMethodCall(expression) + case *entity.Reference: + return this.generateReference(expression) // case *entity.ValueCast: - // return this.generateValueCast(expression.(*entity.ValueCast)) + // return this.generateValueCast(expression) // case *entity.BitCast: - // return this.generateBitCast(expression.(*entity.BitCast)) + // return this.generateBitCast(expression) // case *entity.Operation: - // return this.generateOperation(expression.(*entity.Operation)) + // return this.generateOperation(expression) case *entity.Block: - return this.generateBlock(expression.(*entity.Block)) - // case *entity.MemberAccess: - // return this.generateMemberAccess(expression.(*entity.MemberAccess)) - // case *entity.IfElse: - // return this.generateIfElse(expression.(*entity.IfElse)) - // case *entity.Loop: - // return this.generateLoop(expression.(*entity.Loop)) - // case *entity.Break: - // return this.generateBreak(expression.(*entity.Break)) - // case *entity.Return: - // return this.generateReturn(expression.(*entity.Return)) + return this.generateBlock(expression) + case *entity.IfElse: + return this.generateIfElse(expression, false) + case *entity.Loop: + return this.generateLoop(expression, false) + case *entity.Break: + return this.generateBreak(expression) + case *entity.Return: + return this.generateReturn(expression) case *entity.LiteralInt: - return this.generateLiteralInt(expression.(*entity.LiteralInt)) + return this.generateLiteralInt(expression) case *entity.LiteralFloat: - return this.generateLiteralFloat(expression.(*entity.LiteralFloat)) - case *entity.LiteralArray: - return this.generateLiteralArray(expression.(*entity.LiteralArray)) - // case *entity.LiteralStruct: - // return this.generateLiteralStruct(expression.(*entity.LiteralStruct)) + return this.generateLiteralFloat(expression) case *entity.LiteralBoolean: - return this.generateLiteralBoolean(expression.(*entity.LiteralBoolean)) + return this.generateLiteralBoolean(expression) default: panic(fmt.Sprint ( "BUG: generator doesnt know about expression ", @@ -57,9 +72,9 @@ func (this *generator) generateExpression (expression entity.Expression) (llvm.V } func (this *generator) generateStatement (statement entity.Statement) (llvm.Value, error) { - /*if assignment, ok := statement.(*entity.Assignment); ok { + if assignment, ok := statement.(*entity.Assignment); ok { return this.generateAssignment(assignment) - } else*/ if expression, ok := statement.(entity.Expression); ok { + } else if expression, ok := statement.(entity.Expression); ok { return this.generateExpression(expression) } else { panic(fmt.Sprint ( @@ -67,3 +82,70 @@ func (this *generator) generateStatement (statement entity.Statement) (llvm.Valu statement)) } } + +// 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.ValueCast, + *entity.BitCast, + *entity.Operation, + *entity.LiteralInt, + *entity.LiteralFloat, + *entity.LiteralBoolean: + + value, err := this.generateExpression(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.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: + return this.generateBlockLoc(expression) + case *entity.IfElse: + return this.generateIfElse(expression, true) + case *entity.Loop: + return this.generateLoop(expression, true) + case *entity.Break: + return this.generateBreak(expression) + case *entity.Return: + return this.generateReturn(expression) + case *entity.LiteralArray: + return this.generateLiteralArrayLoc(expression) + case *entity.LiteralStruct: + return this.generateLiteralStructLoc(expression) + default: + panic(fmt.Sprint ( + "BUG: generator doesnt know about location expression ", + expression)) + } +} + +func (this *generator) generateStatementLoc (statement entity.Statement) (llvm.Value, error) { + if assignment, ok := statement.(*entity.Assignment); ok { + return this.generateAssignment(assignment) + } else if expression, ok := statement.(entity.Expression); ok { + return this.generateExpressionLoc(expression) + } else { + panic(fmt.Sprint ( + "BUG: generator doesnt know about statement ", + statement)) + } +} + diff --git a/generator/expression.go b/generator/expression.go index 9545e10..56c041b 100644 --- a/generator/expression.go +++ b/generator/expression.go @@ -4,13 +4,13 @@ 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) { - value := this.blockManager.variable(variable.Declaration) + 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, value), nil + return this.blockManager.NewLoad(irType, location), nil } func (this *generator) generateCall (call *entity.Call) (llvm.Value, error) { @@ -19,8 +19,11 @@ func (this *generator) generateCall (call *entity.Call) (llvm.Value, error) { args := make([]llvm.Value, len(call.Arguments)) for index, argument := range call.Arguments { - irArgument, err := this.generateExpression(argument) + 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 @@ -30,10 +33,16 @@ func (this *generator) generateMethodCall (call *entity.MethodCall) (llvm.Value, 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.generateExpression(argument) + irArgument, err := this.generateAssignmentSource ( + argument, + signature.Arguments[index].Type()) if err != nil { return nil, err } args[index + 1] = irArgument } @@ -96,6 +105,10 @@ func (this *generator) generateMethodCall (call *entity.MethodCall) (llvm.Value, 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) generateBlock (block *entity.Block) (llvm.Value, error) { var last llvm.Value var err error @@ -106,80 +119,91 @@ func (this *generator) generateBlock (block *entity.Block) (llvm.Value, error) { return last, nil } -func (this *generator) generateLiteralInt (literal *entity.LiteralInt) (llvm.Value, error) { - irType, err := this.generateType(analyzer.ReduceToBase(literal.Type())) +func (this *generator) generateIfElse (ifelse *entity.IfElse, loc bool) (llvm.Value, error) { + condition, err := this.generateExpression(ifelse.Condition) if err != nil { return nil, err } - return llvm.NewConstInt(irType.(*llvm.TypeInt), int64(literal.Value)), nil -} + previous := this.blockManager.Block -func (this *generator) generateLiteralFloat (literal *entity.LiteralFloat) (llvm.Value, error) { - irType, err := this.generateType(analyzer.ReduceToBase(literal.Type())) - if err != nil { return nil, err } - return llvm.NewConstFloat(irType.(*llvm.TypeFloat), literal.Value), nil -} - -func (this *generator) generateLiteralArray (literal *entity.LiteralArray) (llvm.Value, error) { - ty := analyzer.ReduceToBase(literal.Type()) - - makeData := func (element entity.Type) (llvm.Value, error) { - irArrayType, err := this.generateType(&entity.TypeArray { - Element: element, - }) - if err != nil { return nil, err } - array := this.blockManager.NewAlloca(irArrayType) - for index, element := range literal.Elements { - irElement, err := this.generateExpression(element) - if err != nil { return nil, err } - elementPointer := this.blockManager.NewGetElementPtr ( - irArrayType, array, - llvm.NewConstInt(llvm.I32, 0), - llvm.NewConstInt(llvm.I32, int64(index))) - this.blockManager.NewStore(irElement, elementPointer) - } - return array, nil - } - - switch ty.(type) { - case *entity.TypeArray: - ty := ty.(*entity.TypeArray) - return makeData(ty.Element) - - case *entity.TypeSlice: - ty := ty.(*entity.TypeSlice) - array, err := makeData(ty.Element) - if err != nil { return nil, err } - irType, err := this.generateTypeSlice(ty) - if err != nil { return nil, err } - slice := this.blockManager.NewAlloca(irType) - data := this.blockManager.NewGetElementPtr ( - irType, slice, - llvm.NewConstInt(llvm.I32, 0), - llvm.NewConstInt(llvm.I32, 0)) - this.blockManager.NewStore(array, data) - length := this.blockManager.NewGetElementPtr ( - irType, slice, - llvm.NewConstInt(llvm.I32, 0), - llvm.NewConstInt(llvm.I32, 1)) - sizeType, err := this.typedef("Size") - if err != nil { return nil, err } - this.blockManager.NewStore ( - llvm.NewConstInt ( - sizeType.(*llvm.TypeInt), - int64(len(literal.Elements))), - length) - return slice, nil + var trueBlock, falseBlock *llvm.Block + var tru, fals llvm.Value - case *entity.TypePointer: - ty := ty.(*entity.TypePointer) - array, err := makeData(ty.Referenced) + 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 { + 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 } - return array, nil - default: - return nil, errors.New(fmt.Sprintln("array can't be used as ", ty)) + trueIncoming := &llvm.Incoming { + X: tru, + Predecessor: trueBlock, + } + falseIncoming := &llvm.Incoming { + X: fals, + Predecessor: falseBlock, + } + + previous.NewCondBr(condition, trueBlock, falseBlock) + this.blockManager.newBlock() + return this.blockManager.NewPhi(trueIncoming, falseIncoming), nil + } else { + exitBlock := this.blockManager.newBlock() + falseBlock.NewBr(exitBlock) + previous.NewCondBr(condition, trueBlock, falseBlock) + return nil, nil } } -func (this *generator) generateLiteralBoolean (literal *entity.LiteralBoolean) (llvm.Value, error) { - return llvm.NewConstBool(bool(*literal.Value)), 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.NewBr(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 + this.blockManager.newBlock() + 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 } diff --git a/generator/function_test.go b/generator/function_test.go index fe829f0..8c8bb8b 100644 --- a/generator/function_test.go +++ b/generator/function_test.go @@ -8,10 +8,15 @@ testString (test, `, ` [puts string:*Byte]:I32 -[main argc:I32 argv:**Byte]:I32 = { - [puts [. argv]] - 0 -} +[main] = loop [puts (* 104 111 109 101 10 115 109 101 101 116 0)] + `) } // [write 1 (* 72 101 108 108 111 114 108 100 0) 7] + +// [puts string:*Byte]:I32 +// [main argc:I32 argv:**Byte] = { +// loop { +// [puts (* 100 100 0)] +// } +// } diff --git a/generator/literal.go b/generator/literal.go new file mode 100644 index 0000000..b50fccb --- /dev/null +++ b/generator/literal.go @@ -0,0 +1,106 @@ +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) generateLiteralInt (literal *entity.LiteralInt) (llvm.Value, error) { + irType, err := this.generateType(analyzer.ReduceToBase(literal.Type())) + if err != nil { return nil, err } + return llvm.NewConstInt(irType.(*llvm.TypeInt), int64(literal.Value)), nil +} + +func (this *generator) generateLiteralFloat (literal *entity.LiteralFloat) (llvm.Value, error) { + irType, err := this.generateType(analyzer.ReduceToBase(literal.Type())) + if err != nil { return nil, err } + return llvm.NewConstFloat(irType.(*llvm.TypeFloat), literal.Value), nil +} + +func (this *generator) generateLiteralArrayLoc (literal *entity.LiteralArray) (llvm.Value, error) { + ty := analyzer.ReduceToBase(literal.Type()) + + makeData := func (elementType entity.Type) (llvm.Value, error) { + irArrayType, err := this.generateType(&entity.TypeArray { + Element: elementType, + Length: len(literal.Elements), + }) + if err != nil { return nil, err } + array := this.blockManager.newAllocaFront(irArrayType) + for index, element := range literal.Elements { + irElement, err := this.generateAssignmentSource ( + element, + elementType) + if err != nil { return nil, err } + elementPointer := this.blockManager.NewGetElementPtr ( + irArrayType, array, + llvm.NewConstInt(llvm.I32, 0), + llvm.NewConstInt(llvm.I32, int64(index))) + this.blockManager.NewStore(irElement, elementPointer) + } + return array, nil + } + + switch ty := ty.(type) { + case *entity.TypeArray: + return makeData(ty.Element) + + case *entity.TypeSlice: + array, err := makeData(ty.Element) + if err != nil { return nil, err } + irType, err := this.generateTypeSlice(ty) + if err != nil { return nil, err } + slice := this.blockManager.newAllocaFront(irType) + data := this.blockManager.NewGetElementPtr ( + irType, slice, + llvm.NewConstInt(llvm.I32, 0), + llvm.NewConstInt(llvm.I32, 0)) + this.blockManager.NewStore(array, data) + length := this.blockManager.NewGetElementPtr ( + irType, slice, + llvm.NewConstInt(llvm.I32, 0), + llvm.NewConstInt(llvm.I32, 1)) + sizeType, err := this.typedef("Size") + if err != nil { return nil, err } + this.blockManager.NewStore ( + llvm.NewConstInt ( + sizeType.(*llvm.TypeInt), + int64(len(literal.Elements))), + length) + return slice, nil + + case *entity.TypePointer: + array, err := makeData(ty.Referenced) + if err != nil { return nil, err } + return this.valueToLocation(array), nil + + default: + return nil, errors.New(fmt.Sprintln("array can't be used as ", ty)) + } +} + +func (this *generator) generateLiteralStructLoc (literal *entity.LiteralStruct) (llvm.Value, error) { + ty := analyzer.ReduceToBase(literal.Type()) + irType, err := this.generateType(ty) + if err != nil { return nil, err } + + structure := this.blockManager.newAllocaFront(irType) + + for index, member := range literal.Members { + irMember, err := this.generateExpression(member.Value) + if err != nil { return nil, err } + + elementPointer := this.blockManager.NewGetElementPtr ( + irType, structure, + llvm.NewConstInt(llvm.I32, 0), + llvm.NewConstInt(llvm.I32, int64(index))) + this.blockManager.NewStore(irMember, elementPointer) + } + + return structure, nil +} + +func (this *generator) generateLiteralBoolean (literal *entity.LiteralBoolean) (llvm.Value, error) { + return llvm.NewConstBool(bool(*literal.Value)), nil +} diff --git a/generator/location-expression.go b/generator/location-expression.go new file mode 100644 index 0000000..a9a35a2 --- /dev/null +++ b/generator/location-expression.go @@ -0,0 +1,190 @@ +package generator + +import "fmt" +import "git.tebibyte.media/sashakoshka/fspl/llvm" +import "git.tebibyte.media/sashakoshka/fspl/entity" +import "git.tebibyte.media/sashakoshka/fspl/analyzer" + +func (this *generator) generateVariableLoc (variable *entity.Variable) (llvm.Value, error) { + return this.blockManager.variable(variable.Declaration) +} + +func (this *generator) generateSliceLoc (slice *entity.Slice) (llvm.Value, error) { + var err error + var start, end, dataAddress llvm.Value + var sizeType *llvm.TypeInt; { + ty, err := this.typedef("Index") + if err != nil { return nil, err } + sizeType = ty.(*llvm.TypeInt) + } + + if slice.Start == nil { + start = llvm.NewConstInt(sizeType, 0) + } else { + start, err = this.generateExpression(slice.Start) + if err != nil { return nil, err } + } + if slice.End != nil { + end, err = this.generateExpression(slice.End) + if err != nil { return nil, err } + } + + sourceType := analyzer.ReduceToBase(slice.Slice.Type()) + switch sourceType := sourceType.(type) { + case *entity.TypeSlice: + source, err := this.generateExpression(slice.Slice) + if err != nil { return nil, err } + irSourceType, err := this.generateType(sourceType) + if err != nil { return nil, err } + dataAddress = this.blockManager.NewGetElementPtr ( + irSourceType, + source, + llvm.NewConstInt(llvm.I32, 0), + llvm.NewConstInt(llvm.I32, 0)) + if end == nil { + endAddr := this.blockManager.NewGetElementPtr ( + irSourceType, + source, + llvm.NewConstInt(llvm.I32, 0), + llvm.NewConstInt(llvm.I32, 2)) + end = this.blockManager.NewLoad(sizeType, endAddr) + } + + case *entity.TypeArray: + dataAddress, err = this.generateExpressionLoc(slice.Slice) + if err != nil { return nil, err } + if end == nil { + end = llvm.NewConstInt(sizeType, int64(sourceType.Length)) + } + + default: + panic(fmt.Sprint ( + "BUG: generator can't slice expression ", + slice.Slice)) + } + + + destinationType, err := this.generateType(slice.Type()) + if err != nil { return nil, err } + newSlice := this.blockManager.newAllocaFront(destinationType) + + // create new slice descriptor + dataAddressFieldAddress := this.blockManager.NewGetElementPtr ( + destinationType, + newSlice, + llvm.NewConstInt(llvm.I32, 0), + llvm.NewConstInt(llvm.I32, 0)) + startFieldAddress := this.blockManager.NewGetElementPtr ( + destinationType, + newSlice, + llvm.NewConstInt(llvm.I32, 0), + llvm.NewConstInt(llvm.I32, 1)) + endFieldAddress := this.blockManager.NewGetElementPtr ( + destinationType, + newSlice, + llvm.NewConstInt(llvm.I32, 0), + llvm.NewConstInt(llvm.I32, 2)) + this.blockManager.NewStore(dataAddressFieldAddress, dataAddress) + this.blockManager.NewStore(startFieldAddress, start) + this.blockManager.NewStore(endFieldAddress, end) + + return newSlice, nil +} + +func (this *generator) generateSubscriptLoc (subscript *entity.Subscript) (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(subscript.Slice) + if err != nil { return nil, err } + offset, err := this.generateExpression(subscript.Offset) + if err != nil { return nil, err } + + var elementType entity.Type + var dataAddress llvm.Value + + sourceType := analyzer.ReduceToBase(subscript.Type()) + switch sourceType := sourceType.(type) { + case *entity.TypeSlice: + irSourceType, err := this.generateType(sourceType) + if err != nil { return nil, err } + + // add index to slice start + startFieldAddress := this.blockManager.NewGetElementPtr ( + irSourceType, + source, + llvm.NewConstInt(llvm.I32, 0), + llvm.NewConstInt(llvm.I32, 1)) + start := this.blockManager.NewLoad(sizeType, startFieldAddress) + offset = this.blockManager.NewAdd(start, offset) + + // get slice element + elementType = sourceType.Element + dataAddress = this.blockManager.NewGetElementPtr ( + irSourceType, + source, + llvm.NewConstInt(llvm.I32, 0), + llvm.NewConstInt(llvm.I32, 0)) + + case *entity.TypeArray: + elementType = sourceType.Element + dataAddress = source + + default: + panic(fmt.Sprint ( + "BUG: generator can't subscript expression ", + subscript.Slice)) + } + + irElementType, err := this.generateType(elementType) + if err != nil { return nil, err } + return this.blockManager.NewGetElementPtr ( + irElementType, + dataAddress, + offset), nil +} + +func (this *generator) generateDereferenceLoc (dereference *entity.Dereference) (llvm.Value, error) { + return this.generateExpression(dereference.Pointer) +} + +func (this *generator) generateMemberAccessLoc (access *entity.MemberAccess) (llvm.Value, error) { + source, err := this.generateExpressionLoc(access.Source) + if err != nil { return nil, err } + sourceType := analyzer.ReduceToBase(access.Source.Type()).(*entity.TypeStruct) + irSourceType, err := this.generateType(sourceType) + if err != nil { return nil, err } + + offset := -1 + for index, name := range sourceType.MemberOrder { + if name == access.Member { + offset = index + break + } + } + + return this.blockManager.NewGetElementPtr ( + irSourceType, + source, + llvm.NewConstInt(llvm.I32, 0), + llvm.NewConstInt(llvm.I32, int64(offset))), nil +} + +func (this *generator) generateBlockLoc (block *entity.Block) (llvm.Value, error) { + var last llvm.Value + var err error + for index, step := range block.Steps { + // prefer generating a normal statement to avoid allocations + if index == len(block.Steps) - 1 { + last, err = this.generateStatementLoc(step) + } else { + last, err = this.generateStatement(step) + } + if err != nil { return nil, err } + } + return last, nil +} + diff --git a/generator/test-common.go b/generator/test-common.go index 9d0c405..66a8f9a 100644 --- a/generator/test-common.go +++ b/generator/test-common.go @@ -16,7 +16,7 @@ func testReader (test *testing.T, correct string, inputs ...io.Reader) { for index, stream := range inputs { err := ast.Parse(fmt.Sprintf("stream%d.fspl", index), stream) if err != nil && err != io.EOF{ - test.Error("parser returned error: ", err) + test.Error("parser returned error:", err) return } } @@ -24,19 +24,19 @@ func testReader (test *testing.T, correct string, inputs ...io.Reader) { tree := analyzer.Tree { } err := tree.Analyze(ast) if err != nil { - test.Error("analyzer returned error: ", err) + test.Error("analyzer returned error:", err) return } module, err := NativeTarget().Generate(tree) if err != nil { - test.Error("generator returned error: ", err) + test.Error("generator returned error:", err) return } output := new(strings.Builder) _, err = module.WriteTo(output) if err != nil { - test.Error("generator returned error: ", err) + test.Error("generator returned error:", err) return } diff --git a/generator/type.go b/generator/type.go index 00efc12..03c20a2 100644 --- a/generator/type.go +++ b/generator/type.go @@ -5,23 +5,6 @@ import "errors" import "git.tebibyte.media/sashakoshka/fspl/llvm" import "git.tebibyte.media/sashakoshka/fspl/entity" -// type pointer struct { TypeName string } -// func (this *pointer) Name () string { return this.TypeName } -// func (this *pointer) SetName (name string) { this.TypeName = name } -// func (*pointer) LLString () string { return "ptr" } -// func (this *pointer) String () string { -// if this.TypeName == "" { -// return this.LLString() -// } else { -// return "%" + this.TypeName -// } -// } -// func (*pointer) Equal (ty llvm.Type) bool { -// _, isGenericPointer := ty.(*pointer) -// _, isTypedPointer := ty.(*types.PointerType) -// return isGenericPointer || isTypedPointer -// } - func (this *generator) generateTypeNamed (ty *entity.TypeNamed) (llvm.Type, error) { return this.typedef(ty.Name) } @@ -31,12 +14,13 @@ func (this *generator) generateTypePointer (ty *entity.TypePointer) (llvm.Type, } func (this *generator) generateTypeSlice (ty *entity.TypeSlice) (llvm.Type, error) { + indexType, err := this.typedef("Index") + if err != nil { return nil, err } return &llvm.TypeStruct { Fields: []llvm.Type { - /* data */ llvm.Pointer, - /* len */ &llvm.TypeInt { - BitSize: this.target.WordSize, - }, + /* data */ llvm.Pointer, + /* start */ indexType, + /* end */ indexType, }, }, nil } diff --git a/generator/type_test.go b/generator/type_test.go index 4e76b40..8b571f9 100644 --- a/generator/type_test.go +++ b/generator/type_test.go @@ -4,18 +4,20 @@ import "testing" func TestType (test *testing.T) { testString (test, -`%AllFloats = type { float, double } -%Bool = type i8 -%Byte = type i8 -%Index = type i64 -%Rune = type i32 -%AllInts = type { %Bool, %Byte, %Index, %Rune, i64, i64, i8, i16, i32, i64, i8, i16, i32, i64 } -%Path = type { ptr, i64 } +`%Index = type i64 +%String = type { ptr, %Index, %Index } %Pegasus = type { ptr, ptr, ptr, ptr, ptr } %Point = type { i64, i64 } -%Quadrangle = type [4 x %Point] %Rectangle = type { %Point, %Point } -%String = type { ptr, i64 } +%Bool = type i8 +%Byte = type i8 +%Rune = type i32 +%AllInts = type { %Bool, %Byte, %Index, %Rune, i64, i64, i8, i16, i32, i64, i8, i16, i32, i64 } +%AllFloats = type { float, double } +%Path = type { ptr, %Index, %Index } +%Quadrangle = type [4 x %Point] +%AllTypes = type { %String, %Pegasus, %Rectangle, %AllInts, %AllFloats, %Path, %Quadrangle } +declare %AllTypes @x() `, ` Point: (x:Int y:Int) @@ -47,5 +49,15 @@ AllFloats: ( f64:F64) Path: *:Point Quadrangle: 4:Point +AllTypes: ( + string:String + pegasus:Pegasus + rectangle:Rectangle + ints:AllInts + floats:AllFloats + path:Path + quadrangle:Quadrangle +) +[x]:AllTypes `) }