aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/ssa/likelyadjust.go
diff options
context:
space:
mode:
authorDavid Chase <drchase@google.com>2016-03-21 11:32:04 -0400
committerDavid Chase <drchase@google.com>2016-04-13 15:59:42 +0000
commit6b85a45edc94786c7669823ee47a6ce1156d6a9a (patch)
tree19a3f32cee6a26073cd1f4f6beeb053599e40663 /src/cmd/compile/internal/ssa/likelyadjust.go
parentc4807d4cc759025854e354fee99ac20d125f0d79 (diff)
downloadgo-6b85a45edc94786c7669823ee47a6ce1156d6a9a.tar.gz
go-6b85a45edc94786c7669823ee47a6ce1156d6a9a.zip
cmd/compile: move spills to loop exits when easy.
For call-free inner loops. Revised statistics: 85 inner loop spills sunk 341 inner loop spills remaining 1162 inner loop spills that were candidates for sinking ended up completely register allocated 119 inner loop spills could have been sunk were used in "shuffling" at the bottom of the loop. 1 inner loop spill not sunk because the register assigned changed between def and exit, Understanding how to make an inner loop definition not be a candidate for from-memory shuffling (to force the shuffle code to choose some other value) should pick up some of the 119 other spills disqualified for this reason. Modified the stats printing based on feedback from Austin. Change-Id: If3fb9b5d5a028f42ccc36c4e3d9e0da39db5ca60 Reviewed-on: https://go-review.googlesource.com/21037 Reviewed-by: Keith Randall <khr@golang.org> Run-TryBot: David Chase <drchase@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'src/cmd/compile/internal/ssa/likelyadjust.go')
-rw-r--r--src/cmd/compile/internal/ssa/likelyadjust.go142
1 files changed, 136 insertions, 6 deletions
diff --git a/src/cmd/compile/internal/ssa/likelyadjust.go b/src/cmd/compile/internal/ssa/likelyadjust.go
index 76251bdd14..2f52c4c6e6 100644
--- a/src/cmd/compile/internal/ssa/likelyadjust.go
+++ b/src/cmd/compile/internal/ssa/likelyadjust.go
@@ -11,11 +11,24 @@ import (
type loop struct {
header *Block // The header node of this (reducible) loop
outer *loop // loop containing this loop
- // Next two fields not currently used, but cheap to maintain,
- // and aid in computation of inner-ness and list of blocks.
- nBlocks int32 // Number of blocks in this loop but not within inner loops
- isInner bool // True if never discovered to contain a loop
- containsCall bool // if any block in this loop or any loop it contains is a BlockCall or BlockDefer
+
+ // By default, children exits, and depth are not initialized.
+ 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().
+
+ // Loops aren't that common, so rather than force regalloc to keep
+ // a map or slice for its data, just put it here.
+ spills []*Value
+ scratch int32
+
+ // Next three fields used by regalloc and/or
+ // aid in computation of inner-ness and list of blocks.
+ nBlocks int32 // Number of blocks in this loop but not within inner loops
+ depth int16 // Nesting depth of the loop; 1 is outermost. Initialized by calculateDepths().
+ isInner bool // True if never discovered to contain a loop
+
+ // register allocation uses this.
+ containsCall bool // if any block in this loop or any loop it contains is a BlockCall or BlockDefer
}
// outerinner records that outer contains inner
@@ -48,6 +61,9 @@ type loopnest struct {
po []*Block
sdom sparseTree
loops []*loop
+
+ // Record which of the lazily initialized fields have actually been initialized.
+ initializedChildren, initializedDepth, initializedExits bool
}
func min8(a, b int8) int8 {
@@ -295,6 +311,35 @@ func loopnestfor(f *Func) *loopnest {
innermost.nBlocks++
}
}
+
+ 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 {
+ ln.assembleChildren()
+ ln.calculateDepths()
+ ln.findExits()
+
+ // Note stats for non-innermost loops are slightly flawed because
+ // they don't account for inner loop exits that span multiple levels.
+
+ for _, l := range loops {
+ x := len(l.exits)
+ cf := 0
+ if !l.containsCall {
+ cf = 1
+ }
+ inner := 0
+ if l.isInner {
+ inner++
+ }
+
+ f.logStat("loopstats:",
+ l.depth, "depth", x, "exits",
+ inner, "is_inner", cf, "is_callfree", l.nBlocks, "n_blocks")
+ }
+ }
+
if f.pass.debug > 1 && len(loops) > 0 {
fmt.Printf("Loops in %s:\n", f.Name)
for _, l := range loops {
@@ -314,5 +359,90 @@ func loopnestfor(f *Func) *loopnest {
}
fmt.Print("\n")
}
- return &loopnest{f, b2l, po, sdom, loops}
+ return ln
+}
+
+// assembleChildren initializes the children field of each
+// loop in the nest. Loop A is a child of loop B if A is
+// directly nested within B (based on the reducible-loops
+// detection above)
+func (ln *loopnest) assembleChildren() {
+ if ln.initializedChildren {
+ return
+ }
+ for _, l := range ln.loops {
+ if l.outer != nil {
+ l.outer.children = append(l.outer.children, l)
+ }
+ }
+ ln.initializedChildren = true
+}
+
+// calculateDepths uses the children field of loops
+// to determine the nesting depth (outer=1) of each
+// loop. This is helpful for finding exit edges.
+func (ln *loopnest) calculateDepths() {
+ if ln.initializedDepth {
+ return
+ }
+ ln.assembleChildren()
+ for _, l := range ln.loops {
+ if l.outer == nil {
+ l.setDepth(1)
+ }
+ }
+ ln.initializedDepth = true
+}
+
+// findExits uses loop depth information to find the
+// exits from a loop.
+func (ln *loopnest) findExits() {
+ if ln.initializedExits {
+ return
+ }
+ ln.calculateDepths()
+ b2l := ln.b2l
+ for _, b := range ln.po {
+ l := b2l[b.ID]
+ if l != nil && len(b.Succs) == 2 {
+ sl := b2l[b.Succs[0].ID]
+ if recordIfExit(l, sl, b.Succs[0]) {
+ continue
+ }
+ sl = b2l[b.Succs[1].ID]
+ if recordIfExit(l, sl, b.Succs[1]) {
+ continue
+ }
+ }
+ }
+ ln.initializedExits = true
+}
+
+// recordIfExit checks sl (the loop containing b) to see if it
+// is outside of loop l, and if so, records b as an exit block
+// from l and returns true.
+func recordIfExit(l, sl *loop, b *Block) bool {
+ if sl != l {
+ if sl == nil || sl.depth <= l.depth {
+ l.exits = append(l.exits, b)
+ return true
+ }
+ // sl is not nil, and is deeper than l
+ // it's possible for this to be a goto into an irreducible loop made from gotos.
+ for sl.depth > l.depth {
+ sl = sl.outer
+ }
+ if sl != l {
+ l.exits = append(l.exits, b)
+ return true
+ }
+ }
+ return false
+}
+
+func (l *loop) setDepth(d int16) {
+ l.depth = d
+ for _, c := range l.children {
+ c.setDepth(d + 1)
+ }
}