2015-03-27 13:41:30 -07:00
|
|
|
// 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
|
|
|
|
|
|
2015-07-05 18:23:25 -05:00
|
|
|
// mark values
|
|
|
|
|
const (
|
|
|
|
|
notFound = 0 // block has not been discovered yet
|
|
|
|
|
notExplored = 1 // discovered and in queue, outedges not processed yet
|
|
|
|
|
explored = 2 // discovered and in queue, outedges processed
|
|
|
|
|
done = 3 // all done, in output ordering
|
|
|
|
|
)
|
|
|
|
|
|
2015-03-27 13:41:30 -07:00
|
|
|
// This file contains code to compute the dominator tree
|
|
|
|
|
// of a control-flow graph.
|
|
|
|
|
|
|
|
|
|
// postorder computes a postorder traversal ordering for the
|
|
|
|
|
// basic blocks in f. Unreachable blocks will not appear.
|
|
|
|
|
func postorder(f *Func) []*Block {
|
|
|
|
|
mark := make([]byte, f.NumBlocks())
|
|
|
|
|
|
|
|
|
|
// result ordering
|
|
|
|
|
var order []*Block
|
|
|
|
|
|
|
|
|
|
// stack of blocks
|
|
|
|
|
var s []*Block
|
|
|
|
|
s = append(s, f.Entry)
|
|
|
|
|
mark[f.Entry.ID] = notExplored
|
|
|
|
|
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
|
|
|
|
|
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 _, c := range b.Succs {
|
|
|
|
|
if mark[c.ID] == notFound {
|
|
|
|
|
mark[c.ID] = notExplored
|
|
|
|
|
s = append(s, c)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
default:
|
2015-06-24 14:03:39 -07:00
|
|
|
b.Fatalf("bad stack state %v %d", b, mark[b.ID])
|
2015-03-27 13:41:30 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return order
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-05 18:23:25 -05:00
|
|
|
type linkedBlocks func(*Block) []*Block
|
|
|
|
|
|
2016-02-10 17:43:31 -05:00
|
|
|
const nscratchslices = 8
|
|
|
|
|
|
|
|
|
|
// experimentally, functions with 512 or fewer blocks account
|
|
|
|
|
// for 75% of memory (size) allocation for dominator computation
|
|
|
|
|
// in make.bash.
|
|
|
|
|
const minscratchblocks = 512
|
|
|
|
|
|
|
|
|
|
func (cfg *Config) scratchBlocksForDom(maxBlockID int) (a, b, c, d, e, f, g, h []ID) {
|
|
|
|
|
tot := maxBlockID * nscratchslices
|
|
|
|
|
scratch := cfg.domblockstore
|
|
|
|
|
if len(scratch) < tot {
|
|
|
|
|
// req = min(1.5*tot, nscratchslices*minscratchblocks)
|
|
|
|
|
// 50% padding allows for graph growth in later phases.
|
|
|
|
|
req := (tot * 3) >> 1
|
|
|
|
|
if req < nscratchslices*minscratchblocks {
|
|
|
|
|
req = nscratchslices * minscratchblocks
|
|
|
|
|
}
|
|
|
|
|
scratch = make([]ID, req)
|
|
|
|
|
cfg.domblockstore = scratch
|
|
|
|
|
} else {
|
|
|
|
|
// Clear as much of scratch as we will (re)use
|
|
|
|
|
scratch = scratch[0:tot]
|
|
|
|
|
for i := range scratch {
|
|
|
|
|
scratch[i] = 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a = scratch[0*maxBlockID : 1*maxBlockID]
|
|
|
|
|
b = scratch[1*maxBlockID : 2*maxBlockID]
|
|
|
|
|
c = scratch[2*maxBlockID : 3*maxBlockID]
|
|
|
|
|
d = scratch[3*maxBlockID : 4*maxBlockID]
|
|
|
|
|
e = scratch[4*maxBlockID : 5*maxBlockID]
|
|
|
|
|
f = scratch[5*maxBlockID : 6*maxBlockID]
|
|
|
|
|
g = scratch[6*maxBlockID : 7*maxBlockID]
|
|
|
|
|
h = scratch[7*maxBlockID : 8*maxBlockID]
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-09 18:03:41 -07:00
|
|
|
// dfs performs a depth first search over the blocks starting at the set of
|
|
|
|
|
// blocks in the entries list (in arbitrary order). dfnum contains a mapping
|
2015-07-05 18:23:25 -05:00
|
|
|
// from block id to an int indicating the order the block was reached or
|
|
|
|
|
// notFound if the block was not reached. order contains a mapping from dfnum
|
2015-09-09 18:03:41 -07:00
|
|
|
// to block.
|
2016-02-10 17:43:31 -05:00
|
|
|
func (f *Func) dfs(entries []*Block, succFn linkedBlocks, dfnum, order, parent []ID) (fromID []*Block) {
|
2015-09-09 18:03:41 -07:00
|
|
|
maxBlockID := entries[0].Func.NumBlocks()
|
2015-07-05 18:23:25 -05:00
|
|
|
|
2016-01-30 17:37:38 -05:00
|
|
|
fromID = make([]*Block, maxBlockID)
|
2015-07-05 18:23:25 -05:00
|
|
|
|
2016-01-30 17:37:38 -05:00
|
|
|
for _, entry := range entries[0].Func.Blocks {
|
|
|
|
|
eid := entry.ID
|
|
|
|
|
if fromID[eid] != nil {
|
|
|
|
|
panic("Colliding entry IDs")
|
|
|
|
|
}
|
|
|
|
|
fromID[eid] = entry
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-10 17:43:31 -05:00
|
|
|
n := ID(0)
|
2015-07-05 18:23:25 -05:00
|
|
|
s := make([]*Block, 0, 256)
|
2015-09-09 18:03:41 -07:00
|
|
|
for _, entry := range entries {
|
|
|
|
|
if dfnum[entry.ID] != notFound {
|
|
|
|
|
continue // already found from a previous entry
|
|
|
|
|
}
|
|
|
|
|
s = append(s, entry)
|
2016-01-30 17:37:38 -05:00
|
|
|
parent[entry.ID] = entry.ID
|
2015-09-09 18:03:41 -07:00
|
|
|
for len(s) > 0 {
|
|
|
|
|
node := s[len(s)-1]
|
|
|
|
|
s = s[:len(s)-1]
|
|
|
|
|
|
|
|
|
|
n++
|
|
|
|
|
for _, w := range succFn(node) {
|
|
|
|
|
// if it has a dfnum, we've already visited it
|
|
|
|
|
if dfnum[w.ID] == notFound {
|
|
|
|
|
s = append(s, w)
|
2016-01-30 17:37:38 -05:00
|
|
|
parent[w.ID] = node.ID
|
2015-09-09 18:03:41 -07:00
|
|
|
dfnum[w.ID] = notExplored
|
|
|
|
|
}
|
2015-07-05 18:23:25 -05:00
|
|
|
}
|
2015-09-09 18:03:41 -07:00
|
|
|
dfnum[node.ID] = n
|
2016-01-30 17:37:38 -05:00
|
|
|
order[n] = node.ID
|
2015-07-05 18:23:25 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-27 13:41:30 -07:00
|
|
|
// dominators computes the dominator tree for f. It returns a slice
|
|
|
|
|
// which maps block ID to the immediate dominator of that block.
|
|
|
|
|
// Unreachable blocks map to nil. The entry block maps to nil.
|
|
|
|
|
func dominators(f *Func) []*Block {
|
2015-07-05 18:23:25 -05:00
|
|
|
preds := func(b *Block) []*Block { return b.Preds }
|
|
|
|
|
succs := func(b *Block) []*Block { return b.Succs }
|
|
|
|
|
|
|
|
|
|
//TODO: benchmark and try to find criteria for swapping between
|
|
|
|
|
// dominatorsSimple and dominatorsLT
|
2016-02-10 17:43:31 -05:00
|
|
|
return f.dominatorsLT([]*Block{f.Entry}, preds, succs)
|
2015-07-05 18:23:25 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// postDominators computes the post-dominator tree for f.
|
|
|
|
|
func postDominators(f *Func) []*Block {
|
|
|
|
|
preds := func(b *Block) []*Block { return b.Preds }
|
|
|
|
|
succs := func(b *Block) []*Block { return b.Succs }
|
|
|
|
|
|
|
|
|
|
if len(f.Blocks) == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-09 18:03:41 -07:00
|
|
|
// find the exit blocks
|
|
|
|
|
var exits []*Block
|
2015-07-05 18:23:25 -05:00
|
|
|
for i := len(f.Blocks) - 1; i >= 0; i-- {
|
2015-09-09 18:03:41 -07:00
|
|
|
switch f.Blocks[i].Kind {
|
2015-10-23 19:12:49 -07:00
|
|
|
case BlockExit, BlockRet, BlockRetJmp, BlockCall, BlockCheck:
|
2015-09-09 18:03:41 -07:00
|
|
|
exits = append(exits, f.Blocks[i])
|
2015-07-05 18:23:25 -05:00
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-09 18:03:41 -07:00
|
|
|
// infinite loop with no exit
|
|
|
|
|
if exits == nil {
|
2015-07-05 18:23:25 -05:00
|
|
|
return make([]*Block, f.NumBlocks())
|
|
|
|
|
}
|
2016-02-10 17:43:31 -05:00
|
|
|
return f.dominatorsLT(exits, succs, preds)
|
2015-07-05 18:23:25 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// dominatorsLt runs Lengauer-Tarjan to compute a dominator tree starting at
|
|
|
|
|
// entry and using predFn/succFn to find predecessors/successors to allow
|
|
|
|
|
// computing both dominator and post-dominator trees.
|
2016-02-10 17:43:31 -05:00
|
|
|
func (f *Func) dominatorsLT(entries []*Block, predFn linkedBlocks, succFn linkedBlocks) []*Block {
|
2015-07-05 18:23:25 -05:00
|
|
|
// Based on Lengauer-Tarjan from Modern Compiler Implementation in C -
|
|
|
|
|
// Appel with optimizations from Finding Dominators in Practice -
|
|
|
|
|
// Georgiadis
|
|
|
|
|
|
2016-02-10 17:43:31 -05:00
|
|
|
maxBlockID := entries[0].Func.NumBlocks()
|
|
|
|
|
|
|
|
|
|
dfnum, vertex, parent, semi, samedom, ancestor, best, bucket := f.Config.scratchBlocksForDom(maxBlockID)
|
|
|
|
|
|
|
|
|
|
// dfnum := make([]ID, maxBlockID) // conceptually int32, but punning for allocation purposes.
|
|
|
|
|
// vertex := make([]ID, maxBlockID)
|
|
|
|
|
// parent := make([]ID, maxBlockID)
|
|
|
|
|
|
|
|
|
|
// semi := make([]ID, maxBlockID)
|
|
|
|
|
// samedom := make([]ID, maxBlockID)
|
|
|
|
|
// ancestor := make([]ID, maxBlockID)
|
|
|
|
|
// best := make([]ID, maxBlockID)
|
|
|
|
|
// bucket := make([]ID, maxBlockID)
|
|
|
|
|
|
2015-07-05 18:23:25 -05:00
|
|
|
// Step 1. Carry out a depth first search of the problem graph. Number
|
|
|
|
|
// the vertices from 1 to n as they are reached during the search.
|
2016-02-10 17:43:31 -05:00
|
|
|
fromID := f.dfs(entries, succFn, dfnum, vertex, parent)
|
2015-07-05 18:23:25 -05:00
|
|
|
|
|
|
|
|
idom := make([]*Block, maxBlockID)
|
|
|
|
|
|
|
|
|
|
// Step 2. Compute the semidominators of all vertices by applying
|
|
|
|
|
// Theorem 4. Carry out the computation vertex by vertex in decreasing
|
|
|
|
|
// order by number.
|
|
|
|
|
for i := maxBlockID - 1; i > 0; i-- {
|
|
|
|
|
w := vertex[i]
|
2016-01-30 17:37:38 -05:00
|
|
|
if w == 0 {
|
2015-07-05 18:23:25 -05:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-30 17:37:38 -05:00
|
|
|
if dfnum[w] == notFound {
|
2015-07-05 18:23:25 -05:00
|
|
|
// skip unreachable node
|
|
|
|
|
continue
|
|
|
|
|
}
|
2015-06-25 14:04:55 -07:00
|
|
|
|
2015-07-05 18:23:25 -05:00
|
|
|
// Step 3. Implicitly define the immediate dominator of each
|
|
|
|
|
// vertex by applying Corollary 1. (reordered)
|
2016-01-30 17:37:38 -05:00
|
|
|
for v := bucket[w]; v != 0; v = bucket[v] {
|
2015-07-05 18:23:25 -05:00
|
|
|
u := eval(v, ancestor, semi, dfnum, best)
|
2016-01-30 17:37:38 -05:00
|
|
|
if semi[u] == semi[v] {
|
|
|
|
|
idom[v] = fromID[w] // true dominator
|
2015-07-05 18:23:25 -05:00
|
|
|
} else {
|
2016-01-30 17:37:38 -05:00
|
|
|
samedom[v] = u // v has same dominator as u
|
2015-07-05 18:23:25 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-30 17:37:38 -05:00
|
|
|
p := parent[w]
|
2015-07-05 18:23:25 -05:00
|
|
|
s := p // semidominator
|
|
|
|
|
|
2016-01-30 17:37:38 -05:00
|
|
|
var sp ID
|
2015-07-05 18:23:25 -05:00
|
|
|
// calculate the semidominator of w
|
2016-01-30 17:37:38 -05:00
|
|
|
for _, v := range predFn(fromID[w]) {
|
2015-07-05 18:23:25 -05:00
|
|
|
if dfnum[v.ID] == notFound {
|
|
|
|
|
// skip unreachable predecessor
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-30 17:37:38 -05:00
|
|
|
if dfnum[v.ID] <= dfnum[w] {
|
|
|
|
|
sp = v.ID
|
2015-07-05 18:23:25 -05:00
|
|
|
} else {
|
2016-01-30 17:37:38 -05:00
|
|
|
sp = semi[eval(v.ID, ancestor, semi, dfnum, best)]
|
2015-07-05 18:23:25 -05:00
|
|
|
}
|
|
|
|
|
|
2016-01-30 17:37:38 -05:00
|
|
|
if dfnum[sp] < dfnum[s] {
|
2015-07-05 18:23:25 -05:00
|
|
|
s = sp
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// link
|
2016-01-30 17:37:38 -05:00
|
|
|
ancestor[w] = p
|
|
|
|
|
best[w] = w
|
2015-07-05 18:23:25 -05:00
|
|
|
|
2016-01-30 17:37:38 -05:00
|
|
|
semi[w] = s
|
|
|
|
|
if semi[s] != parent[s] {
|
|
|
|
|
bucket[w] = bucket[s]
|
|
|
|
|
bucket[s] = w
|
2015-07-05 18:23:25 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Final pass of step 3
|
2016-01-30 17:37:38 -05:00
|
|
|
for v := bucket[0]; v != 0; v = bucket[v] {
|
|
|
|
|
idom[v] = fromID[bucket[0]]
|
2015-07-05 18:23:25 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Step 4. Explictly define the immediate dominator of each vertex,
|
|
|
|
|
// carrying out the computation vertex by vertex in increasing order by
|
|
|
|
|
// number.
|
|
|
|
|
for i := 1; i < maxBlockID-1; i++ {
|
|
|
|
|
w := vertex[i]
|
2016-01-30 17:37:38 -05:00
|
|
|
if w == 0 {
|
2015-07-05 18:23:25 -05:00
|
|
|
continue
|
|
|
|
|
}
|
2016-01-30 17:37:38 -05:00
|
|
|
// w has the same dominator as samedom[w]
|
|
|
|
|
if samedom[w] != 0 {
|
|
|
|
|
idom[w] = idom[samedom[w]]
|
2015-07-05 18:23:25 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return idom
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// eval function from LT paper with path compression
|
2016-02-10 17:43:31 -05:00
|
|
|
func eval(v ID, ancestor []ID, semi []ID, dfnum []ID, best []ID) ID {
|
2016-01-30 17:37:38 -05:00
|
|
|
a := ancestor[v]
|
|
|
|
|
if ancestor[a] != 0 {
|
|
|
|
|
bid := eval(a, ancestor, semi, dfnum, best)
|
|
|
|
|
ancestor[v] = ancestor[a]
|
|
|
|
|
if dfnum[semi[bid]] < dfnum[semi[best[v]]] {
|
|
|
|
|
best[v] = bid
|
2015-07-05 18:23:25 -05:00
|
|
|
}
|
|
|
|
|
}
|
2016-01-30 17:37:38 -05:00
|
|
|
return best[v]
|
2015-07-05 18:23:25 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// dominators computes the dominator tree for f. It returns a slice
|
|
|
|
|
// which maps block ID to the immediate dominator of that block.
|
|
|
|
|
// Unreachable blocks map to nil. The entry block maps to nil.
|
|
|
|
|
func dominatorsSimple(f *Func) []*Block {
|
2015-03-27 13:41:30 -07:00
|
|
|
// A simple algorithm for now
|
|
|
|
|
// Cooper, Harvey, Kennedy
|
|
|
|
|
idom := make([]*Block, f.NumBlocks())
|
|
|
|
|
|
|
|
|
|
// Compute postorder walk
|
|
|
|
|
post := postorder(f)
|
|
|
|
|
|
|
|
|
|
// Make map from block id to order index (for intersect call)
|
|
|
|
|
postnum := make([]int, f.NumBlocks())
|
|
|
|
|
for i, b := range post {
|
|
|
|
|
postnum[b.ID] = i
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make the entry block a self-loop
|
|
|
|
|
idom[f.Entry.ID] = f.Entry
|
|
|
|
|
if postnum[f.Entry.ID] != len(post)-1 {
|
2015-06-24 14:03:39 -07:00
|
|
|
f.Fatalf("entry block %v not last in postorder", f.Entry)
|
2015-03-27 13:41:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Compute relaxation of idom entries
|
|
|
|
|
for {
|
|
|
|
|
changed := false
|
|
|
|
|
|
|
|
|
|
for i := len(post) - 2; i >= 0; i-- {
|
|
|
|
|
b := post[i]
|
|
|
|
|
var d *Block
|
|
|
|
|
for _, p := range b.Preds {
|
|
|
|
|
if idom[p.ID] == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if d == nil {
|
|
|
|
|
d = p
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
d = intersect(d, p, postnum, idom)
|
|
|
|
|
}
|
|
|
|
|
if d != idom[b.ID] {
|
|
|
|
|
idom[b.ID] = d
|
|
|
|
|
changed = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !changed {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Set idom of entry block to nil instead of itself.
|
|
|
|
|
idom[f.Entry.ID] = nil
|
|
|
|
|
return idom
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// intersect finds the closest dominator of both b and c.
|
|
|
|
|
// It requires a postorder numbering of all the blocks.
|
|
|
|
|
func intersect(b, c *Block, postnum []int, idom []*Block) *Block {
|
2015-06-25 14:04:55 -07:00
|
|
|
// TODO: This loop is O(n^2). See BenchmarkNilCheckDeep*.
|
2015-03-27 13:41:30 -07:00
|
|
|
for b != c {
|
|
|
|
|
if postnum[b.ID] < postnum[c.ID] {
|
|
|
|
|
b = idom[b.ID]
|
|
|
|
|
} else {
|
|
|
|
|
c = idom[c.ID]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return b
|
|
|
|
|
}
|