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" import "git.tebibyte.media/fspl/fspl/entity"
type loopEntry struct { type loopEntry struct {
value llvm.Value breakBlocks []*llvm.Incoming // incoming value/block pairs of breaks
stub *llvm.Block mode resultMode // result mode for break statements
mode resultMode loc bool // for when mode is resultModeAny
loc bool }
// 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 { type blockManager struct {

View File

@ -76,7 +76,7 @@ testString (test,
`) `)
} }
func TestLoop (test *testing.T) { func TestLoopSimple (test *testing.T) {
testString (test, testString (test,
`define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() { `define void @"0zNZN147MN2wzMAQ6NS2dQ==::main"() {
0: 0:
@ -105,7 +105,8 @@ testString (test,
2: 2:
br label %3 br label %3
3: 3:
store i64 5, ptr %1 %4 = phi i64 [ 5, %2 ]
store i64 %4, ptr %1
ret void ret void
} }
`, `,
@ -128,19 +129,20 @@ testString (test,
2: 2:
%3 = load i64, ptr %1 %3 = load i64, ptr %1
%4 = icmp slt i64 %3, 3 %4 = icmp slt i64 %3, 3
br i1 %4, label %5, label %8 br i1 %4, label %7, label %10
5: 5:
%6 = load i64, ptr %1 %6 = phi i64 [ %8, %7 ]
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:
ret i64 %6 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: 3:
%4 = load i64, ptr %1 %4 = load i64, ptr %1
%5 = icmp eq i64 %4, 5 %5 = icmp eq i64 %4, 5
br i1 %5, label %6, label %9 br i1 %5, label %8, label %11
6: 6:
br label %11 %7 = phi i64 [ 0, %8 ]
7: ret i64 %7
%8 = phi i64 [ %10, %9 ] 8:
store i64 %8, ptr %2 br label %6
br label %3
9: 9:
%10 = load i64, ptr %1 %10 = phi i64 [ %12, %11 ]
br label %7 store i64 %10, ptr %2
br label %3
11: 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) { func (this *generator) generateBreak (brk *entity.Break) (llvm.Value, error) {
loopEntry := this.blockManager.topLoop() loopEntry := this.blockManager.topLoop()
var irValue llvm.Value
if brk.Value != nil { 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 } 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 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) { 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 previous := this.blockManager.Block
body := this.blockManager.newBlock() body := this.blockManager.newBlock()
exit := this.blockManager.newBlock()
previous.NewBr(body) previous.NewBr(body)
loopEntry := this.blockManager.pushLoop(mode) loopEntry := this.blockManager.pushLoop(mode)
defer this.blockManager.popLoop() 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 } if err != nil { return nil, false, err }
final := this.blockManager.Block
this.blockManager.NewBr(body) // discard/obtain results
exit := this.blockManager.newBlock() this.blockManager.Block = exit
var irValue llvm.Value
if loopEntry.stub != nil { if _, void := irResultType.(*llvm.TypeVoid); void {
loopEntry.stub.SetTerminator(&llvm.TerminatorBr { loopEntry.tieUp(exit)
Target: exit, } else {
}) irValue = loopEntry.tieUpPhi(irResultType, exit)
} }
// FIXME: loopEntry needs to have a list of phi nodes, it's probably if !final.Terminated () {
// completely broken final.NewBr(body)
return loopEntry.value, loopEntry.loc, nil }
return irValue, loopEntry.loc, nil
} }
func (this *generator) generateFor (loop *entity.For, mode resultMode) (llvm.Value, bool, error) { func (this *generator) generateFor (loop *entity.For, mode resultMode) (llvm.Value, bool, error) {
irIndexType, err := this.generateTypeIndex() irIndexType, err := this.generateTypeIndex()
if err != nil { return nil, false, err } if err != nil { return nil, false, err }
irResultType, err := this.generateType(loop.Type())
if err != nil { return nil, false, err }
// element index // element index
var irIndexLoc llvm.Value var irIndexLoc llvm.Value
@ -277,21 +300,29 @@ func (this *generator) generateFor (loop *entity.For, mode resultMode) (llvm.Val
_, _, err = this.generateExpressionAny(loop.Body) _, _, err = this.generateExpressionAny(loop.Body)
if err != nil { return nil, false, err } if err != nil { return nil, false, err }
// increment index and loop back around // increment index
this.blockManager.NewStore ( this.blockManager.NewStore (
this.blockManager.NewAdd ( this.blockManager.NewAdd (
this.blockManager.NewLoad(irIndexType, irIndexLoc), this.blockManager.NewLoad(irIndexType, irIndexLoc),
llvm.NewConstInt(irIndexType, 1)), llvm.NewConstInt(irIndexType, 1)),
irIndexLoc) irIndexLoc)
this.blockManager.NewBr(body)
// discard/obtain results
final := this.blockManager.Block
this.blockManager.Block = exit this.blockManager.Block = exit
if loopEntry.stub != nil { var irValue llvm.Value
loopEntry.stub.SetTerminator(&llvm.TerminatorBr { if _, void := irResultType.(*llvm.TypeVoid); void {
Target: exit, 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 // loop back around
return loopEntry.value, loopEntry.loc, nil if !final.Terminated () {
final.NewBr(body)
}
return irValue, loopEntry.loc, nil
} }