This should have been way more commits

This commit is contained in:
Sasha Koshka 2023-11-26 04:02:22 -05:00
parent e71b9951e9
commit 85e77c36c6
10 changed files with 649 additions and 156 deletions

49
generator/assignment.go Normal file
View File

@ -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
}
}

View File

@ -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
}

View File

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

View File

@ -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
}

View File

@ -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)]
// }
// }

106
generator/literal.go Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
`)
}