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"
|
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 {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user