Commit 9e21e9c5 authored by David Chase's avatar David Chase

cmd/compile: make loop finder more aware of irreducible loops

The loop finder doesn't return good information if it
encounters an irreducible loop.  Make a start on improving
this, and set a function-level flag to indicate when there
is such a loop (and the returned information might be flaky).

Use that flag to prevent the loop rotater from getting
confused; the existing code seems to depend on artifacts
of the previous loop-finding algorithm. (There is one
irreducible loop in the go library, in "inflate.go").

Change-Id: If6e26feab38d9b009d2252d556e1470c803bde40
Reviewed-on: https://go-review.googlesource.com/42150
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarCherry Zhang <cherryyz@google.com>
parent acdb4476
...@@ -280,15 +280,17 @@ func checkFunc(f *Func) { ...@@ -280,15 +280,17 @@ func checkFunc(f *Func) {
// Check loop construction // Check loop construction
if f.RegAlloc == nil && f.pass != nil { // non-nil pass allows better-targeted debug printing if f.RegAlloc == nil && f.pass != nil { // non-nil pass allows better-targeted debug printing
ln := f.loopnest() ln := f.loopnest()
po := f.postorder() // use po to avoid unreachable blocks. if !ln.hasIrreducible {
for _, b := range po { po := f.postorder() // use po to avoid unreachable blocks.
for _, s := range b.Succs { for _, b := range po {
bb := s.Block() for _, s := range b.Succs {
if ln.b2l[b.ID] == nil && ln.b2l[bb.ID] != nil && bb != ln.b2l[bb.ID].header { bb := s.Block()
f.Fatalf("block %s not in loop branches to non-header block %s in loop", b.String(), bb.String()) if ln.b2l[b.ID] == nil && ln.b2l[bb.ID] != nil && bb != ln.b2l[bb.ID].header {
} f.Fatalf("block %s not in loop branches to non-header block %s in loop", b.String(), bb.String())
if ln.b2l[b.ID] != nil && ln.b2l[bb.ID] != nil && bb != ln.b2l[bb.ID].header && !ln.b2l[b.ID].isWithinOrEq(ln.b2l[bb.ID]) { }
f.Fatalf("block %s in loop branches to non-header block %s in non-containing loop", b.String(), bb.String()) if ln.b2l[b.ID] != nil && ln.b2l[bb.ID] != nil && bb != ln.b2l[bb.ID].header && !ln.b2l[b.ID].isWithinOrEq(ln.b2l[bb.ID]) {
f.Fatalf("block %s in loop branches to non-header block %s in non-containing loop", b.String(), bb.String())
}
} }
} }
} }
......
...@@ -12,7 +12,7 @@ type loop struct { ...@@ -12,7 +12,7 @@ type loop struct {
header *Block // The header node of this (reducible) loop header *Block // The header node of this (reducible) loop
outer *loop // loop containing this loop outer *loop // loop containing this loop
// By default, children exits, and depth are not initialized. // By default, children, exits, and depth are not initialized.
children []*loop // loops nested directly within this loop. Initialized by assembleChildren(). children []*loop // loops nested directly within this loop. Initialized by assembleChildren().
exits []*Block // exits records blocks reached by exits from this loop. Initialized by findExits(). exits []*Block // exits records blocks reached by exits from this loop. Initialized by findExits().
...@@ -23,7 +23,7 @@ type loop struct { ...@@ -23,7 +23,7 @@ type loop struct {
isInner bool // True if never discovered to contain a loop isInner bool // True if never discovered to contain a loop
// register allocation uses this. // register allocation uses this.
containsCall bool // if any block in this loop or any loop it contains has a call containsCall bool // if any block in this loop or any loop within it contains has a call
} }
// outerinner records that outer contains inner // outerinner records that outer contains inner
...@@ -72,11 +72,12 @@ func (l *loop) checkContainsCall(bb *Block) { ...@@ -72,11 +72,12 @@ func (l *loop) checkContainsCall(bb *Block) {
} }
type loopnest struct { type loopnest struct {
f *Func f *Func
b2l []*loop b2l []*loop
po []*Block po []*Block
sdom SparseTree sdom SparseTree
loops []*loop loops []*loop
hasIrreducible bool // TODO current treatment of irreducible loops is very flaky, if accurate loops are needed, must punt at function level.
// Record which of the lazily initialized fields have actually been initialized. // Record which of the lazily initialized fields have actually been initialized.
initializedChildren, initializedDepth, initializedExits bool initializedChildren, initializedDepth, initializedExits bool
...@@ -285,6 +286,12 @@ func loopnestfor(f *Func) *loopnest { ...@@ -285,6 +286,12 @@ func loopnestfor(f *Func) *loopnest {
sdom := f.sdom() sdom := f.sdom()
b2l := make([]*loop, f.NumBlocks()) b2l := make([]*loop, f.NumBlocks())
loops := make([]*loop, 0) loops := make([]*loop, 0)
visited := make([]bool, f.NumBlocks())
sawIrred := false
if f.pass.debug > 2 {
fmt.Printf("loop finding in %s\n", f.Name)
}
// Reducible-loop-nest-finding. // Reducible-loop-nest-finding.
for _, b := range po { for _, b := range po {
...@@ -318,10 +325,17 @@ func loopnestfor(f *Func) *loopnest { ...@@ -318,10 +325,17 @@ func loopnestfor(f *Func) *loopnest {
b2l[bb.ID] = l b2l[bb.ID] = l
l.checkContainsCall(bb) l.checkContainsCall(bb)
} }
} else { // Perhaps a loop header is inherited. } else if !visited[bb.ID] { // Found an irreducible loop
sawIrred = true
if f.pass != nil && f.pass.debug > 4 {
fmt.Printf("loop finding succ %s of %s is IRRED, in %s\n", bb.String(), b.String(), f.Name)
}
} else if l != nil {
// TODO handle case where l is irreducible.
// Perhaps a loop header is inherited.
// is there any loop containing our successor whose // is there any loop containing our successor whose
// header dominates b? // header dominates b?
if l != nil && !sdom.isAncestorEq(l.header, b) { if !sdom.isAncestorEq(l.header, b) {
l = l.nearestOuterLoop(sdom, b) l = l.nearestOuterLoop(sdom, b)
} }
if f.pass != nil && f.pass.debug > 4 { if f.pass != nil && f.pass.debug > 4 {
...@@ -331,6 +345,11 @@ func loopnestfor(f *Func) *loopnest { ...@@ -331,6 +345,11 @@ func loopnestfor(f *Func) *loopnest {
fmt.Printf("loop finding succ %s of %s provides loop with header %s\n", bb.String(), b.String(), l.header.String()) fmt.Printf("loop finding succ %s of %s provides loop with header %s\n", bb.String(), b.String(), l.header.String())
} }
} }
} else { // No loop
if f.pass != nil && f.pass.debug > 4 {
fmt.Printf("loop finding succ %s of %s has no loop\n", bb.String(), b.String())
}
} }
if l == nil || innermost == l { if l == nil || innermost == l {
...@@ -355,9 +374,10 @@ func loopnestfor(f *Func) *loopnest { ...@@ -355,9 +374,10 @@ func loopnestfor(f *Func) *loopnest {
innermost.checkContainsCall(b) innermost.checkContainsCall(b)
innermost.nBlocks++ innermost.nBlocks++
} }
visited[b.ID] = true
} }
ln := &loopnest{f: f, b2l: b2l, po: po, sdom: sdom, loops: loops} ln := &loopnest{f: f, b2l: b2l, po: po, sdom: sdom, loops: loops, hasIrreducible: sawIrred}
// Curious about the loopiness? "-d=ssa/likelyadjust/stats" // Curious about the loopiness? "-d=ssa/likelyadjust/stats"
if f.pass != nil && f.pass.stats > 0 && len(loops) > 0 { if f.pass != nil && f.pass.stats > 0 && len(loops) > 0 {
......
...@@ -23,6 +23,9 @@ package ssa ...@@ -23,6 +23,9 @@ package ssa
// JLT loop // JLT loop
func loopRotate(f *Func) { func loopRotate(f *Func) {
loopnest := f.loopnest() loopnest := f.loopnest()
if loopnest.hasIrreducible {
return
}
if len(loopnest.loops) == 0 { if len(loopnest.loops) == 0 {
return return
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment