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 }