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 package generator
import "fmt"
import "errors"
import "git.tebibyte.media/sashakoshka/fspl/llvm" import "git.tebibyte.media/sashakoshka/fspl/llvm"
import "git.tebibyte.media/sashakoshka/fspl/entity" import "git.tebibyte.media/sashakoshka/fspl/entity"
type loopEntry struct {
value llvm.Value
stub *llvm.Block
wantLocation bool
}
type blockManager struct { type blockManager struct {
*llvm.Block *llvm.Block
generator *generator generator *generator
function *llvm.Function function *llvm.Function
declarations map[*entity.Declaration] llvm.Value declarations map[*entity.Declaration] llvm.Value
loops []*loopEntry
} }
func (this *generator) pushBlockManager (function *llvm.Function) *blockManager { func (this *generator) pushBlockManager (function *llvm.Function) *blockManager {
@ -30,25 +40,56 @@ func (this *generator) popBlockManager () {
} }
} }
func (this *blockManager) newBlock () *llvm.Block { func (this *blockManager) pushLoop (wantLocation bool) *loopEntry {
if this.Block != nil && len(this.Block.Instructions) == 0 { return nil } entry := &loopEntry { wantLocation: wantLocation }
this.loops = append(this.loops, entry)
previous := this.Block return entry
this.Block = this.function.NewBlock("")
return previous
} }
func (this *blockManager) variable (declaration *entity.Declaration) llvm.Value { func (this *blockManager) popLoop () {
return this.declarations[declaration] 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 { func (this *blockManager) addDeclaration (declaration *entity.Declaration, initial llvm.Value) error {
ty, err := this.generator.generateType(declaration.Type()) ty, err := this.generator.generateType(declaration.Type())
if err != nil { return err } if err != nil { return err }
location := this.Block.NewAlloca(ty) location := this.newAllocaFront(ty)
this.declarations[declaration] = location this.declarations[declaration] = location
if initial != nil { if initial != nil {
this.Block.NewStore(initial, location) this.NewStore(initial, location)
} }
return nil return nil
} }

View File

@ -4,51 +4,66 @@ import "fmt"
import "git.tebibyte.media/sashakoshka/fspl/llvm" import "git.tebibyte.media/sashakoshka/fspl/llvm"
import "git.tebibyte.media/sashakoshka/fspl/entity" 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) { 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: case *entity.Variable:
return this.generateVariable(expression.(*entity.Variable)) return this.generateVariable(expression)
case *entity.Call: case *entity.Call:
return this.generateCall(expression.(*entity.Call)) return this.generateCall(expression)
case *entity.MethodCall: case *entity.MethodCall:
return this.generateMethodCall(expression.(*entity.MethodCall)) return this.generateMethodCall(expression)
// case *entity.Subscript: case *entity.Reference:
// return this.generateSubscript(expression.(*entity.Subscript)) return this.generateReference(expression)
// 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))
// case *entity.ValueCast: // case *entity.ValueCast:
// return this.generateValueCast(expression.(*entity.ValueCast)) // return this.generateValueCast(expression)
// case *entity.BitCast: // case *entity.BitCast:
// return this.generateBitCast(expression.(*entity.BitCast)) // return this.generateBitCast(expression)
// case *entity.Operation: // case *entity.Operation:
// return this.generateOperation(expression.(*entity.Operation)) // return this.generateOperation(expression)
case *entity.Block: case *entity.Block:
return this.generateBlock(expression.(*entity.Block)) return this.generateBlock(expression)
// case *entity.MemberAccess: case *entity.IfElse:
// return this.generateMemberAccess(expression.(*entity.MemberAccess)) return this.generateIfElse(expression, false)
// case *entity.IfElse: case *entity.Loop:
// return this.generateIfElse(expression.(*entity.IfElse)) return this.generateLoop(expression, false)
// case *entity.Loop: case *entity.Break:
// return this.generateLoop(expression.(*entity.Loop)) return this.generateBreak(expression)
// case *entity.Break: case *entity.Return:
// return this.generateBreak(expression.(*entity.Break)) return this.generateReturn(expression)
// case *entity.Return:
// return this.generateReturn(expression.(*entity.Return))
case *entity.LiteralInt: case *entity.LiteralInt:
return this.generateLiteralInt(expression.(*entity.LiteralInt)) return this.generateLiteralInt(expression)
case *entity.LiteralFloat: case *entity.LiteralFloat:
return this.generateLiteralFloat(expression.(*entity.LiteralFloat)) return this.generateLiteralFloat(expression)
case *entity.LiteralArray:
return this.generateLiteralArray(expression.(*entity.LiteralArray))
// case *entity.LiteralStruct:
// return this.generateLiteralStruct(expression.(*entity.LiteralStruct))
case *entity.LiteralBoolean: case *entity.LiteralBoolean:
return this.generateLiteralBoolean(expression.(*entity.LiteralBoolean)) return this.generateLiteralBoolean(expression)
default: default:
panic(fmt.Sprint ( panic(fmt.Sprint (
"BUG: generator doesnt know about expression ", "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) { 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) return this.generateAssignment(assignment)
} else*/ if expression, ok := statement.(entity.Expression); ok { } else if expression, ok := statement.(entity.Expression); ok {
return this.generateExpression(expression) return this.generateExpression(expression)
} else { } else {
panic(fmt.Sprint ( panic(fmt.Sprint (
@ -67,3 +82,70 @@ func (this *generator) generateStatement (statement entity.Statement) (llvm.Valu
statement)) 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 "errors"
import "git.tebibyte.media/sashakoshka/fspl/llvm" import "git.tebibyte.media/sashakoshka/fspl/llvm"
import "git.tebibyte.media/sashakoshka/fspl/entity" import "git.tebibyte.media/sashakoshka/fspl/entity"
import "git.tebibyte.media/sashakoshka/fspl/analyzer"
func (this *generator) generateVariable (variable *entity.Variable) (llvm.Value, error) { 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()) irType, err := this.generateType(variable.Type())
if err != nil { return nil, err } 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) { 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)) args := make([]llvm.Value, len(call.Arguments))
for index, argument := range 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 } if err != nil { return nil, err }
args[index] = irArgument args[index] = irArgument
} }
return this.blockManager.NewCall(function, function.Signature, args...), nil 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) source, err := this.generateExpression(call.Source)
if err != nil { return nil, err } 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 // build list of args
args := make([]llvm.Value, len(call.Arguments) + 1) args := make([]llvm.Value, len(call.Arguments) + 1)
for index, argument := range call.Arguments { 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 } if err != nil { return nil, err }
args[index + 1] = irArgument 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)) 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) { func (this *generator) generateBlock (block *entity.Block) (llvm.Value, error) {
var last llvm.Value var last llvm.Value
var err error var err error
@ -106,80 +119,91 @@ func (this *generator) generateBlock (block *entity.Block) (llvm.Value, error) {
return last, nil return last, nil
} }
func (this *generator) generateLiteralInt (literal *entity.LiteralInt) (llvm.Value, error) { func (this *generator) generateIfElse (ifelse *entity.IfElse, loc bool) (llvm.Value, error) {
irType, err := this.generateType(analyzer.ReduceToBase(literal.Type())) condition, err := this.generateExpression(ifelse.Condition)
if err != nil { return nil, err } 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) { var trueBlock, falseBlock *llvm.Block
irType, err := this.generateType(analyzer.ReduceToBase(literal.Type())) var tru, fals llvm.Value
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
case *entity.TypePointer: trueBlock = this.blockManager.newBlock()
ty := ty.(*entity.TypePointer) if loc {
array, err := makeData(ty.Referenced) 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 } if err != nil { return nil, err }
return array, nil
default: trueIncoming := &llvm.Incoming {
return nil, errors.New(fmt.Sprintln("array can't be used as ", ty)) 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) { func (this *generator) generateLoop (loop *entity.Loop, loc bool) (llvm.Value, error) {
return llvm.NewConstBool(bool(*literal.Value)), nil 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 [puts string:*Byte]:I32
[main argc:I32 argv:**Byte]:I32 = { [main] = loop [puts (* 104 111 109 101 10 115 109 101 101 116 0)]
[puts [. argv]]
0
}
`) `)
} }
// [write 1 (* 72 101 108 108 111 114 108 100 0) 7] // [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 { for index, stream := range inputs {
err := ast.Parse(fmt.Sprintf("stream%d.fspl", index), stream) err := ast.Parse(fmt.Sprintf("stream%d.fspl", index), stream)
if err != nil && err != io.EOF{ if err != nil && err != io.EOF{
test.Error("parser returned error: ", err) test.Error("parser returned error:", err)
return return
} }
} }
@ -24,19 +24,19 @@ func testReader (test *testing.T, correct string, inputs ...io.Reader) {
tree := analyzer.Tree { } tree := analyzer.Tree { }
err := tree.Analyze(ast) err := tree.Analyze(ast)
if err != nil { if err != nil {
test.Error("analyzer returned error: ", err) test.Error("analyzer returned error:", err)
return return
} }
module, err := NativeTarget().Generate(tree) module, err := NativeTarget().Generate(tree)
if err != nil { if err != nil {
test.Error("generator returned error: ", err) test.Error("generator returned error:", err)
return return
} }
output := new(strings.Builder) output := new(strings.Builder)
_, err = module.WriteTo(output) _, err = module.WriteTo(output)
if err != nil { if err != nil {
test.Error("generator returned error: ", err) test.Error("generator returned error:", err)
return return
} }

View File

@ -5,23 +5,6 @@ import "errors"
import "git.tebibyte.media/sashakoshka/fspl/llvm" import "git.tebibyte.media/sashakoshka/fspl/llvm"
import "git.tebibyte.media/sashakoshka/fspl/entity" 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) { func (this *generator) generateTypeNamed (ty *entity.TypeNamed) (llvm.Type, error) {
return this.typedef(ty.Name) 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) { func (this *generator) generateTypeSlice (ty *entity.TypeSlice) (llvm.Type, error) {
indexType, err := this.typedef("Index")
if err != nil { return nil, err }
return &llvm.TypeStruct { return &llvm.TypeStruct {
Fields: []llvm.Type { Fields: []llvm.Type {
/* data */ llvm.Pointer, /* data */ llvm.Pointer,
/* len */ &llvm.TypeInt { /* start */ indexType,
BitSize: this.target.WordSize, /* end */ indexType,
},
}, },
}, nil }, nil
} }

View File

@ -4,18 +4,20 @@ import "testing"
func TestType (test *testing.T) { func TestType (test *testing.T) {
testString (test, testString (test,
`%AllFloats = type { float, double } `%Index = type i64
%Bool = type i8 %String = type { ptr, %Index, %Index }
%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 }
%Pegasus = type { ptr, ptr, ptr, ptr, ptr } %Pegasus = type { ptr, ptr, ptr, ptr, ptr }
%Point = type { i64, i64 } %Point = type { i64, i64 }
%Quadrangle = type [4 x %Point]
%Rectangle = type { %Point, %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) Point: (x:Int y:Int)
@ -47,5 +49,15 @@ AllFloats: (
f64:F64) f64:F64)
Path: *:Point Path: *:Point
Quadrangle: 4:Point Quadrangle: 4:Point
AllTypes: (
string:String
pegasus:Pegasus
rectangle:Rectangle
ints:AllInts
floats:AllFloats
path:Path
quadrangle:Quadrangle
)
[x]:AllTypes
`) `)
} }