mirror of
https://github.com/golang/go
synced 2024-10-05 16:41:21 -06:00
[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 <khr@golang.org>
This commit is contained in:
parent
8a1f6217c5
commit
2a29576562
@ -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 {
|
||||
|
113
src/cmd/compile/internal/ssa/sparsetree.go
Normal file
113
src/cmd/compile/internal/ssa/sparsetree.go
Normal file
@ -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
|
||||
}
|
Loading…
Reference in New Issue
Block a user