From: David Chase Date: Mon, 7 Sep 2015 01:32:24 +0000 (-0400) Subject: [dev.ssa] cmd/compile: fix N^2 dominator queries in CSE X-Git-Tag: go1.7beta1~1623^2^2~181 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=2a2957656270cf409d11eb2df1d316e97cef2b62;p=gostls13.git [dev.ssa] cmd/compile: fix N^2 dominator queries in CSE 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 --- diff --git a/src/cmd/compile/internal/ssa/cse.go b/src/cmd/compile/internal/ssa/cse.go index 003530a9d3..3b007c6192 100644 --- a/src/cmd/compile/internal/ssa/cse.go +++ b/src/cmd/compile/internal/ssa/cse.go @@ -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 index 0000000000..14bcb44b1b --- /dev/null +++ b/src/cmd/compile/internal/ssa/sparsetree.go @@ -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 +}