]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.ssa] cmd/compile: fix N^2 dominator queries in CSE
authorDavid Chase <drchase@google.com>
Mon, 7 Sep 2015 01:32:24 +0000 (21:32 -0400)
committerDavid Chase <drchase@google.com>
Wed, 9 Sep 2015 23:31:42 +0000 (23:31 +0000)
Added tree numbering data structure.
Changed dominator query in CSE.
Removed skip-for-too-big patch in CSE.
Passes all.bash.

Change-Id: I98d7c61b6015c81f5edab553615db17bc7a58d68
Reviewed-on: https://go-review.googlesource.com/14326
Reviewed-by: Keith Randall <khr@golang.org>
src/cmd/compile/internal/ssa/cse.go
src/cmd/compile/internal/ssa/sparsetree.go [new file with mode: 0644]

index 003530a9d309bc64189010f6e98de15a5868116a..3b007c619269b297da0660e5eb0aec9d74aa9199 100644 (file)
@@ -10,11 +10,6 @@ import "sort"
 // Values are just relinked, nothing is deleted.  A subsequent deadcode
 // pass is required to actually remove duplicate expressions.
 func cse(f *Func) {
-       if f.NumBlocks() > 10000 {
-               f.Unimplementedf("too many blocks: %d", f.NumBlocks())
-               return
-       }
-
        // Two values are equivalent if they satisfy the following definition:
        // equivalent(v, w):
        //   v.op == w.op
@@ -132,6 +127,7 @@ func cse(f *Func) {
 
        // Compute dominator tree
        idom := dominators(f)
+       sdom := newSparseTree(f, idom)
 
        // Compute substitutions we would like to do.  We substitute v for w
        // if v and w are in the same equivalence class and v dominates w.
@@ -142,7 +138,7 @@ func cse(f *Func) {
                        // Find a maximal dominant element in e
                        v := e[0]
                        for _, w := range e[1:] {
-                               if dom(w.Block, v.Block, idom) {
+                               if sdom.isAncestorEq(w.Block, v.Block) {
                                        v = w
                                }
                        }
@@ -152,7 +148,7 @@ func cse(f *Func) {
                                w := e[i]
                                if w == v {
                                        e, e[i] = e[:len(e)-1], e[len(e)-1]
-                               } else if dom(v.Block, w.Block, idom) {
+                               } else if sdom.isAncestorEq(v.Block, w.Block) {
                                        rewrite[w.ID] = v
                                        e, e[i] = e[:len(e)-1], e[len(e)-1]
                                } else {
@@ -176,7 +172,7 @@ func cse(f *Func) {
 }
 
 // returns true if b dominates c.
-// TODO(khr): faster
+// simple and iterative, has O(depth) complexity in tall trees.
 func dom(b, c *Block, idom []*Block) bool {
        // Walk up from c in the dominator tree looking for b.
        for c != nil {
diff --git a/src/cmd/compile/internal/ssa/sparsetree.go b/src/cmd/compile/internal/ssa/sparsetree.go
new file mode 100644 (file)
index 0000000..14bcb44
--- /dev/null
@@ -0,0 +1,113 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ssa
+
+type sparseTreeNode struct {
+       block   *Block
+       child   *Block
+       sibling *Block
+       parent  *Block
+
+       // Every block has 6 numbers associated with it:
+       // entry-1, entry, entry+1, exit-1, and exit, exit+1.
+       // entry and exit are conceptually the top of the block (phi functions)
+       // entry+1 and exit-1 are conceptually the bottom of the block (ordinary defs)
+       // entry-1 and exit+1 are conceptually "just before" the block (conditions flowing in)
+       //
+       // This simplifies life if we wish to query information about x
+       // when x is both an input to and output of a block.
+       entry, exit int32
+}
+
+const (
+       // When used to lookup up definitions in a sparse tree,
+       // these adjustments to a block's entry (+adjust) and
+       // exit (-adjust) numbers allow a distinction to be made
+       // between assignments (typically branch-dependent
+       // conditionals) occurring "before" phi functions, the
+       // phi functions, and at the bottom of a block.
+       ADJUST_BEFORE = -1 // defined before phi
+       ADJUST_TOP    = 0  // defined by phi
+       ADJUST_BOTTOM = 1  // defined within block
+)
+
+// A sparseTree is a tree of Blocks.
+// It allows rapid ancestor queries,
+// such as whether one block dominates another.
+type sparseTree []sparseTreeNode
+
+// newSparseTree creates a sparseTree from a block-to-parent map (array indexed by Block.ID)
+func newSparseTree(f *Func, parentOf []*Block) sparseTree {
+       t := make(sparseTree, f.NumBlocks())
+       for _, b := range f.Blocks {
+               n := &t[b.ID]
+               n.block = b
+               if p := parentOf[b.ID]; p != nil {
+                       n.parent = p
+                       n.sibling = t[p.ID].child
+                       t[p.ID].child = b
+               }
+       }
+       t.numberBlock(f.Entry, 1)
+       return t
+}
+
+// numberBlock assigns entry and exit numbers for b and b's
+// children in an in-order walk from a gappy sequence, where n
+// is the first number not yet assigned or reserved. N should
+// be larger than zero. For each entry and exit number, the
+// values one larger and smaller are reserved to indicate
+// "strictly above" and "strictly below". numberBlock returns
+// the smallest number not yet assigned or reserved (i.e., the
+// exit number of the last block visited, plus two, because
+// last.exit+1 is a reserved value.)
+//
+// examples:
+//
+// single node tree Root, call with n=1
+//         entry=2 Root exit=5; returns 7
+//
+// two node tree, Root->Child, call with n=1
+//         entry=2 Root exit=11; returns 13
+//         entry=5 Child exit=8
+//
+// three node tree, Root->(Left, Right), call with n=1
+//         entry=2 Root exit=17; returns 19
+// entry=5 Left exit=8;  entry=11 Right exit=14
+//
+// This is the in-order sequence of assigned and reserved numbers
+// for the last example:
+//   root     left     left      right       right       root
+//  1 2e 3 | 4 5e 6 | 7 8x 9 | 10 11e 12 | 13 14x 15 | 16 17x 18
+
+func (t sparseTree) numberBlock(b *Block, n int32) int32 {
+       // reserve n for entry-1, assign n+1 to entry
+       n++
+       t[b.ID].entry = n
+       // reserve n+1 for entry+1, n+2 is next free number
+       n += 2
+       for c := t[b.ID].child; c != nil; c = t[c.ID].sibling {
+               n = t.numberBlock(c, n) // preserves n = next free number
+       }
+       // reserve n for exit-1, assign n+1 to exit
+       n++
+       t[b.ID].exit = n
+       // reserve n+1 for exit+1, n+2 is next free number, returned.
+       return n + 2
+}
+
+// isAncestorEq reports whether x is an ancestor of or equal to y.
+func (t sparseTree) isAncestorEq(x, y *Block) bool {
+       xx := &t[x.ID]
+       yy := &t[y.ID]
+       return xx.entry <= yy.entry && yy.exit <= xx.exit
+}
+
+// isAncestor reports whether x is a strict ancestor of y.
+func (t sparseTree) isAncestor(x, y *Block) bool {
+       xx := &t[x.ID]
+       yy := &t[y.ID]
+       return xx.entry < yy.entry && yy.exit < xx.exit
+}