"cmd/compile/internal/ssa.Type %s": "",
"cmd/compile/internal/ssa.Type %v": "",
"cmd/compile/internal/ssa.ValAndOff %s": "",
- "cmd/compile/internal/ssa.markKind %d": "",
"cmd/compile/internal/ssa.rbrank %d": "",
"cmd/compile/internal/ssa.regMask %d": "",
"cmd/compile/internal/ssa.register %d": "",
}
}
+ // Check loop construction
+ if f.RegAlloc == nil && f.pass != nil { // non-nil pass allows better-targeted debug printing
+ ln := f.loopnest()
+ po := f.postorder() // use po to avoid unreachable blocks.
+ for _, b := range po {
+ for _, s := range b.Succs {
+ bb := s.Block()
+ 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())
+ }
+ }
+ }
+ }
+
// Check use counts
uses := make([]int32, f.NumValues())
for _, b := range f.Blocks {
func postorder(f *Func) []*Block {
return postorderWithNumbering(f, []int32{})
}
+
+type blockAndIndex struct {
+ b *Block
+ index int // index is the number of successor edges of b that have already been explored.
+}
+
+// postorderWithNumbering provides a DFS postordering.
+// This seems to make loop-finding more robust.
func postorderWithNumbering(f *Func, ponums []int32) []*Block {
mark := make([]markKind, f.NumBlocks())
// result ordering
var order []*Block
- // stack of blocks
- var s []*Block
- s = append(s, f.Entry)
- mark[f.Entry.ID] = notExplored
+ // stack of blocks and next child to visit
+ var s []blockAndIndex
+ s = append(s, blockAndIndex{b: f.Entry})
+ mark[f.Entry.ID] = explored
for len(s) > 0 {
- b := s[len(s)-1]
- switch mark[b.ID] {
- case explored:
- // Children have all been visited. Pop & output block.
- s = s[:len(s)-1]
- mark[b.ID] = done
+ tos := len(s) - 1
+ x := s[tos]
+ b := x.b
+ i := x.index
+ if i < len(b.Succs) {
+ s[tos].index++
+ bb := b.Succs[i].Block()
+ if mark[bb.ID] == notFound {
+ mark[bb.ID] = explored
+ s = append(s, blockAndIndex{b: bb})
+ }
+ } else {
+ s = s[:tos]
if len(ponums) > 0 {
ponums[b.ID] = int32(len(order))
}
order = append(order, b)
- case notExplored:
- // Children have not been visited yet. Mark as explored
- // and queue any children we haven't seen yet.
- mark[b.ID] = explored
- for _, e := range b.Succs {
- c := e.b
- if mark[c.ID] == notFound {
- mark[c.ID] = notExplored
- s = append(s, c)
- }
- }
- default:
- b.Fatalf("bad stack state %v %d", b, mark[b.ID])
}
}
return order
return doms
}
-func TestDominatorsPostTricky(t *testing.T) {
+func TestDominatorsPostTrickyA(t *testing.T) {
+ testDominatorsPostTricky(t, "b8", "b11", "b10", "b8", "b14", "b15")
+}
+
+func TestDominatorsPostTrickyB(t *testing.T) {
+ testDominatorsPostTricky(t, "b11", "b8", "b10", "b8", "b14", "b15")
+}
+
+func TestDominatorsPostTrickyC(t *testing.T) {
+ testDominatorsPostTricky(t, "b8", "b11", "b8", "b10", "b14", "b15")
+}
+
+func TestDominatorsPostTrickyD(t *testing.T) {
+ testDominatorsPostTricky(t, "b11", "b8", "b8", "b10", "b14", "b15")
+}
+
+func TestDominatorsPostTrickyE(t *testing.T) {
+ testDominatorsPostTricky(t, "b8", "b11", "b10", "b8", "b15", "b14")
+}
+
+func TestDominatorsPostTrickyF(t *testing.T) {
+ testDominatorsPostTricky(t, "b11", "b8", "b10", "b8", "b15", "b14")
+}
+
+func TestDominatorsPostTrickyG(t *testing.T) {
+ testDominatorsPostTricky(t, "b8", "b11", "b8", "b10", "b15", "b14")
+}
+
+func TestDominatorsPostTrickyH(t *testing.T) {
+ testDominatorsPostTricky(t, "b11", "b8", "b8", "b10", "b15", "b14")
+}
+
+func testDominatorsPostTricky(t *testing.T, b7then, b7else, b12then, b12else, b13then, b13else string) {
c := testConfig(t)
fun := c.Fun("b1",
Bloc("b1",
Bloc("b5",
Goto("b7")),
Bloc("b7",
- If("p", "b8", "b11")),
+ If("p", b7then, b7else)),
Bloc("b8",
Goto("b13")),
Bloc("b13",
- If("p", "b14", "b15")),
+ If("p", b13then, b13else)),
Bloc("b14",
Goto("b10")),
Bloc("b15",
Bloc("b11",
Goto("b12")),
Bloc("b12",
- If("p", "b10", "b8")),
+ If("p", b12then, b12else)),
Bloc("b10",
Goto("b6")),
Bloc("b6",
func (sdom SparseTree) outerinner(outer, inner *loop) {
// There could be other outer loops found in some random order,
// locate the new outer loop appropriately among them.
+
+ // Outer loop headers dominate inner loop headers.
+ // Use this to put the "new" "outer" loop in the right place.
oldouter := inner.outer
for oldouter != nil && sdom.isAncestor(outer.header, oldouter.header) {
inner = oldouter
return
}
if oldouter != nil {
- outer.outer = oldouter
+ sdom.outerinner(oldouter, outer)
}
inner.outer = outer
return fmt.Sprintf("hdr:%s%s%s", l.header, i, o)
}
+func (l *loop) isWithinOrEq(ll *loop) bool {
+ if ll == nil { // nil means whole program
+ return true
+ }
+ for ; l != nil; l = l.outer {
+ if l == ll {
+ return true
+ }
+ }
+ return false
+}
+
// nearestOuterLoop returns the outer loop of loop most nearly
// containing block b; the header must dominate b. loop itself
// is assumed to not be that loop. For acceptable performance,
// Reducible-loop-nest-finding.
for _, b := range po {
- if f.pass.debug > 3 {
- fmt.Printf("loop finding (0) at %s\n", b)
+ if f.pass != nil && f.pass.debug > 3 {
+ fmt.Printf("loop finding at %s\n", b)
}
var innermost *loop // innermost header reachable from this block
l := b2l[bb.ID]
if sdom.isAncestorEq(bb, b) { // Found a loop header
+ if f.pass != nil && f.pass.debug > 4 {
+ fmt.Printf("loop finding succ %s of %s is header\n", bb.String(), b.String())
+ }
if l == nil {
l = &loop{header: bb, isInner: true}
loops = append(loops, l)
if l != nil && !sdom.isAncestorEq(l.header, b) {
l = l.nearestOuterLoop(sdom, b)
}
+ if f.pass != nil && f.pass.debug > 4 {
+ if l == nil {
+ fmt.Printf("loop finding succ %s of %s has no loop\n", bb.String(), b.String())
+ } else {
+ fmt.Printf("loop finding succ %s of %s provides loop with header %s\n", bb.String(), b.String(), l.header.String())
+ }
+ }
}
if l == nil || innermost == l {
ln := &loopnest{f: f, b2l: b2l, po: po, sdom: sdom, loops: loops}
// Curious about the loopiness? "-d=ssa/likelyadjust/stats"
- if f.pass.stats > 0 && len(loops) > 0 {
+ if f.pass != nil && f.pass.stats > 0 && len(loops) > 0 {
ln.assembleChildren()
ln.calculateDepths()
ln.findExits()
}
}
- if f.pass.debug > 1 && len(loops) > 0 {
+ if f.pass != nil && f.pass.debug > 1 && len(loops) > 0 {
fmt.Printf("Loops in %s:\n", f.Name)
for _, l := range loops {
fmt.Printf("%s, b=", l.LongString())