Loops now handle multiple break statements correctly

This commit is contained in:
Sasha Koshka 2024-03-20 04:19:47 -04:00
parent 50f088842a
commit ecd6eba434
3 changed files with 114 additions and 50 deletions

View File

@ -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 {

View File

@ -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
}
`,
`

View File

@ -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
}