From b0741477ba7e1e20b78efb4903f5e88ca2a7b2c9 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Wed, 20 Mar 2024 04:19:47 -0400 Subject: [PATCH] Loops now handle multiple break statements correctly --- generator/blockmanager.go | 38 ++++++++++++++-- generator/control-flow_test.go | 47 ++++++++++---------- generator/expression.go | 79 +++++++++++++++++++++++----------- 3 files changed, 114 insertions(+), 50 deletions(-) diff --git a/generator/blockmanager.go b/generator/blockmanager.go index 109d7ab..471978d 100644 --- a/generator/blockmanager.go +++ b/generator/blockmanager.go @@ -6,10 +6,40 @@ import "git.tebibyte.media/fspl/fspl/llvm" import "git.tebibyte.media/fspl/fspl/entity" type loopEntry struct { - value llvm.Value - stub *llvm.Block - mode resultMode - loc bool + 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 { diff --git a/generator/control-flow_test.go b/generator/control-flow_test.go index bb7ea18..592ba1c 100644 --- a/generator/control-flow_test.go +++ b/generator/control-flow_test.go @@ -76,7 +76,7 @@ testString (test, `) } -func TestLoop (test *testing.T) { +func TestLoopSimple (test *testing.T) { testString (test, `define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() { 0: @@ -105,7 +105,8 @@ testString (test, 2: br label %3 3: - store i64 5, ptr %1 + %4 = phi i64 [ 5, %2 ] + store i64 %4, ptr %1 ret void } `, @@ -128,19 +129,20 @@ testString (test, 2: %3 = load i64, ptr %1 %4 = icmp slt i64 %3, 3 - br i1 %4, label %5, label %8 + br i1 %4, label %7, label %10 5: - %6 = load i64, ptr %1 - br label %11 -7: - br label %2 -8: - %9 = load i64, ptr %1 - %10 = sub i64 %9, 1 - store i64 %10, ptr %1 - br label %7 -11: + %6 = phi i64 [ %8, %7 ] ret i64 %6 +7: + %8 = load i64, ptr %1 + br label %5 +9: + br label %2 +10: + %11 = load i64, ptr %1 + %12 = sub i64 %11, 1 + store i64 %12, ptr %1 + br label %9 } `, ` @@ -375,18 +377,19 @@ testString (test, 3: %4 = load i64, ptr %1 %5 = icmp eq i64 %4, 5 - br i1 %5, label %6, label %9 + br i1 %5, label %8, label %11 6: - br label %11 -7: - %8 = phi i64 [ %10, %9 ] - store i64 %8, ptr %2 - br label %3 + %7 = phi i64 [ 0, %8 ] + ret i64 %7 +8: + br label %6 9: - %10 = load i64, ptr %1 - br label %7 + %10 = phi i64 [ %12, %11 ] + store i64 %10, ptr %2 + br label %3 11: - ret i64 0 + %12 = load i64, ptr %1 + br label %9 } `, ` diff --git a/generator/expression.go b/generator/expression.go index 1544f29..83a88e6 100644 --- a/generator/expression.go +++ b/generator/expression.go @@ -19,14 +19,28 @@ func (this *generator) generateBlock (block *entity.Block, mode resultMode) (llv func (this *generator) generateBreak (brk *entity.Break) (llvm.Value, error) { loopEntry := this.blockManager.topLoop() + var irValue llvm.Value if brk.Value != nil { - value, loc, err := this.generateExpression(brk.Value, loopEntry.mode) + var err error + var loc bool + + thisMode := loopEntry.mode + if thisMode == resultModeAny && len(loopEntry.breakBlocks) > 0 { + // there have been previous break statements, so be + // consistent with the already decided result mode + loc := loopEntry.loc + if loc { thisMode = resultModeLoc + } else { thisMode = resultModeVal } + } + irValue, loc, err = this.generateExpression(brk.Value, loopEntry.mode) if err != nil { return nil, err } - loopEntry.value = value - loopEntry.loc = loc + loopEntry.loc = loc } - loopEntry.stub = this.blockManager.Block + loopEntry.breakBlocks = append(loopEntry.breakBlocks, &llvm.Incoming { + X: irValue, + Predecessor: this.blockManager.Block, + }) return nil, nil } @@ -178,32 +192,41 @@ func (this *generator) generateMatch (match *entity.Match, mode resultMode) (llv } func (this *generator) generateLoop (loop *entity.Loop, mode resultMode) (llvm.Value, bool, error) { + irResultType, err := this.generateType(loop.Type()) + if err != nil { return nil, false, err } + previous := this.blockManager.Block body := this.blockManager.newBlock() + exit := this.blockManager.newBlock() previous.NewBr(body) loopEntry := this.blockManager.pushLoop(mode) defer this.blockManager.popLoop() - - _, _, err := this.generateExpressionAny(loop.Body) + + this.blockManager.Block = body + _, _, err = this.generateExpressionAny(loop.Body) if err != nil { return nil, false, err } + final := this.blockManager.Block - this.blockManager.NewBr(body) - exit := this.blockManager.newBlock() - - if loopEntry.stub != nil { - loopEntry.stub.SetTerminator(&llvm.TerminatorBr { - Target: exit, - }) + // discard/obtain results + this.blockManager.Block = exit + var irValue llvm.Value + if _, void := irResultType.(*llvm.TypeVoid); void { + loopEntry.tieUp(exit) + } else { + irValue = loopEntry.tieUpPhi(irResultType, exit) } - // FIXME: loopEntry needs to have a list of phi nodes, it's probably - // completely broken - return loopEntry.value, loopEntry.loc, nil + if !final.Terminated () { + final.NewBr(body) + } + return irValue, loopEntry.loc, nil } func (this *generator) generateFor (loop *entity.For, mode resultMode) (llvm.Value, bool, error) { irIndexType, err := this.generateTypeIndex() if err != nil { return nil, false, err } + irResultType, err := this.generateType(loop.Type()) + if err != nil { return nil, false, err } // element index var irIndexLoc llvm.Value @@ -277,21 +300,29 @@ func (this *generator) generateFor (loop *entity.For, mode resultMode) (llvm.Val _, _, err = this.generateExpressionAny(loop.Body) if err != nil { return nil, false, err } - // increment index and loop back around + // increment index this.blockManager.NewStore ( this.blockManager.NewAdd ( this.blockManager.NewLoad(irIndexType, irIndexLoc), llvm.NewConstInt(irIndexType, 1)), irIndexLoc) - this.blockManager.NewBr(body) + // discard/obtain results + final := this.blockManager.Block this.blockManager.Block = exit - if loopEntry.stub != nil { - loopEntry.stub.SetTerminator(&llvm.TerminatorBr { - Target: exit, + var irValue llvm.Value + if _, void := irResultType.(*llvm.TypeVoid); void { + loopEntry.tieUp(exit) + } else { + irValue = loopEntry.tieUpPhi(irResultType, exit, &llvm.Incoming { + X: llvm.NewConstZeroInitializer(irResultType), + Predecessor: header, }) } - // FIXME: loopEntry needs to have a list of phi nodes, it's probably - // completely broken - return loopEntry.value, loopEntry.loc, nil + + // loop back around + if !final.Terminated () { + final.NewBr(body) + } + return irValue, loopEntry.loc, nil }