Loops now handle multiple break statements correctly
This commit is contained in:
parent
50f088842a
commit
ecd6eba434
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
`,
|
||||
`
|
||||
|
@ -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.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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user