162 lines
4.3 KiB
Go
162 lines
4.3 KiB
Go
package generator
|
|
|
|
import "fmt"
|
|
import "errors"
|
|
import "git.tebibyte.media/fspl/fspl/llvm"
|
|
import "git.tebibyte.media/fspl/fspl/entity"
|
|
|
|
type loopEntry struct {
|
|
breakBlocks []*llvm.Incoming // incoming value/block pairs of breaks
|
|
mode resultMode // result mode for break statements
|
|
loc bool // for when mode is resultModeAny
|
|
}
|
|
|
|
// tieUp branches all breakBlocks to exit.
|
|
func (this *loopEntry) tieUp (exit *llvm.Block) {
|
|
for _, incoming := range this.breakBlocks {
|
|
predecessor := incoming.Predecessor.(*llvm.Block)
|
|
predecessor.SetTerminator(&llvm.TerminatorBr {
|
|
Target: exit,
|
|
})
|
|
}
|
|
}
|
|
|
|
// tieUpPhi branches all breakBlocks to exit, creates a new phi in exit, and
|
|
// returns the phi.
|
|
func (this *loopEntry) tieUpPhi (ty llvm.Type, exit *llvm.Block, extraIncomings... *llvm.Incoming) llvm.Value {
|
|
for _, incoming := range this.breakBlocks {
|
|
predecessor := incoming.Predecessor.(*llvm.Block)
|
|
predecessor.SetTerminator(&llvm.TerminatorBr {
|
|
Target: exit,
|
|
})
|
|
}
|
|
|
|
totalIncomings := make([]*llvm.Incoming, len(this.breakBlocks))
|
|
copy(totalIncomings, this.breakBlocks)
|
|
totalIncomings = append(totalIncomings, extraIncomings...)
|
|
|
|
if len(totalIncomings) == 0 {
|
|
return llvm.NewConstZeroInitializer(ty)
|
|
} else {
|
|
return exit.NewPhi(totalIncomings...)
|
|
}
|
|
}
|
|
|
|
type blockManager struct {
|
|
*llvm.Block
|
|
generator *generator
|
|
function *llvm.Function
|
|
declarations map[*entity.Declaration] llvm.Value
|
|
|
|
loops []*loopEntry
|
|
}
|
|
|
|
func (this *blockManager) String () string {
|
|
return fmt.Sprint(this.function.Name())
|
|
}
|
|
|
|
func (this *generator) pushBlockManager (function *llvm.Function) *blockManager {
|
|
manager := new(blockManager)
|
|
manager.generator = this
|
|
manager.function = function
|
|
manager.newBlock()
|
|
manager.declarations = make(map[*entity.Declaration] llvm.Value)
|
|
|
|
this.managerStack = append(this.managerStack, manager)
|
|
this.blockManager = manager
|
|
return manager
|
|
}
|
|
|
|
func (this *generator) popBlockManager () {
|
|
this.managerStack = this.managerStack[:len(this.managerStack) - 1]
|
|
if len(this.managerStack) > 0 {
|
|
this.blockManager = this.managerStack[len(this.managerStack) - 1]
|
|
} else {
|
|
this.blockManager = nil
|
|
}
|
|
}
|
|
|
|
func (this *blockManager) pushLoop (mode resultMode) *loopEntry {
|
|
entry := &loopEntry { mode: mode }
|
|
this.loops = append(this.loops, entry)
|
|
return entry
|
|
}
|
|
|
|
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 (ty llvm.Type) llvm.Value {
|
|
alloca := llvm.NewAlloca(ty)
|
|
|
|
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) (llvm.Value, error) {
|
|
ty, err := this.generator.generateType(declaration.Type())
|
|
if err != nil { return nil, err }
|
|
location := this.newAllocaFront(ty)
|
|
this.declarations[declaration] = location
|
|
if initial != nil {
|
|
this.NewStore(initial, location)
|
|
}
|
|
return location, nil
|
|
}
|
|
|
|
func (this *blockManager) addFunctionArgumentDeclarations (scope entity.Scoped) error {
|
|
var err error
|
|
|
|
switch scope := scope.(type) {
|
|
case *entity.Function:
|
|
for index, argument := range scope.Signature.Arguments {
|
|
if argument.Name == argument.Name {
|
|
this.addDeclaration (
|
|
argument,
|
|
this.function.Parameters[index])
|
|
}
|
|
}
|
|
|
|
case *entity.Method:
|
|
this.addDeclaration (
|
|
scope.This,
|
|
this.function.Parameters[0])
|
|
|
|
for index, argument := range scope.Signature.Arguments {
|
|
if argument.Name == argument.Name {
|
|
this.addDeclaration (
|
|
argument,
|
|
this.function.Parameters[index + 1])
|
|
}
|
|
}
|
|
}
|
|
|
|
return err
|
|
}
|