fspl/generator/blockmanager.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
}