github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/cmd/compile/internal/ssa/dom.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ssa
     6  
     7  // mark values
     8  const (
     9  	notFound    = 0 // block has not been discovered yet
    10  	notExplored = 1 // discovered and in queue, outedges not processed yet
    11  	explored    = 2 // discovered and in queue, outedges processed
    12  	done        = 3 // all done, in output ordering
    13  )
    14  
    15  // This file contains code to compute the dominator tree
    16  // of a control-flow graph.
    17  
    18  // postorder computes a postorder traversal ordering for the
    19  // basic blocks in f. Unreachable blocks will not appear.
    20  func postorder(f *Func) []*Block {
    21  	mark := make([]byte, f.NumBlocks())
    22  
    23  	// result ordering
    24  	var order []*Block
    25  
    26  	// stack of blocks
    27  	var s []*Block
    28  	s = append(s, f.Entry)
    29  	mark[f.Entry.ID] = notExplored
    30  	for len(s) > 0 {
    31  		b := s[len(s)-1]
    32  		switch mark[b.ID] {
    33  		case explored:
    34  			// Children have all been visited. Pop & output block.
    35  			s = s[:len(s)-1]
    36  			mark[b.ID] = done
    37  			order = append(order, b)
    38  		case notExplored:
    39  			// Children have not been visited yet. Mark as explored
    40  			// and queue any children we haven't seen yet.
    41  			mark[b.ID] = explored
    42  			for _, c := range b.Succs {
    43  				if mark[c.ID] == notFound {
    44  					mark[c.ID] = notExplored
    45  					s = append(s, c)
    46  				}
    47  			}
    48  		default:
    49  			b.Fatalf("bad stack state %v %d", b, mark[b.ID])
    50  		}
    51  	}
    52  	return order
    53  }
    54  
    55  type linkedBlocks func(*Block) []*Block
    56  
    57  const nscratchslices = 8
    58  
    59  // experimentally, functions with 512 or fewer blocks account
    60  // for 75% of memory (size) allocation for dominator computation
    61  // in make.bash.
    62  const minscratchblocks = 512
    63  
    64  func (cfg *Config) scratchBlocksForDom(maxBlockID int) (a, b, c, d, e, f, g, h []ID) {
    65  	tot := maxBlockID * nscratchslices
    66  	scratch := cfg.domblockstore
    67  	if len(scratch) < tot {
    68  		// req = min(1.5*tot, nscratchslices*minscratchblocks)
    69  		// 50% padding allows for graph growth in later phases.
    70  		req := (tot * 3) >> 1
    71  		if req < nscratchslices*minscratchblocks {
    72  			req = nscratchslices * minscratchblocks
    73  		}
    74  		scratch = make([]ID, req)
    75  		cfg.domblockstore = scratch
    76  	} else {
    77  		// Clear as much of scratch as we will (re)use
    78  		scratch = scratch[0:tot]
    79  		for i := range scratch {
    80  			scratch[i] = 0
    81  		}
    82  	}
    83  
    84  	a = scratch[0*maxBlockID : 1*maxBlockID]
    85  	b = scratch[1*maxBlockID : 2*maxBlockID]
    86  	c = scratch[2*maxBlockID : 3*maxBlockID]
    87  	d = scratch[3*maxBlockID : 4*maxBlockID]
    88  	e = scratch[4*maxBlockID : 5*maxBlockID]
    89  	f = scratch[5*maxBlockID : 6*maxBlockID]
    90  	g = scratch[6*maxBlockID : 7*maxBlockID]
    91  	h = scratch[7*maxBlockID : 8*maxBlockID]
    92  
    93  	return
    94  }
    95  
    96  // dfs performs a depth first search over the blocks starting at the set of
    97  // blocks in the entries list (in arbitrary order). dfnum contains a mapping
    98  // from block id to an int indicating the order the block was reached or
    99  // notFound if the block was not reached.  order contains a mapping from dfnum
   100  // to block.
   101  func (f *Func) dfs(entries []*Block, succFn linkedBlocks, dfnum, order, parent []ID) (fromID []*Block) {
   102  	maxBlockID := entries[0].Func.NumBlocks()
   103  
   104  	fromID = make([]*Block, maxBlockID)
   105  
   106  	for _, entry := range entries[0].Func.Blocks {
   107  		eid := entry.ID
   108  		if fromID[eid] != nil {
   109  			panic("Colliding entry IDs")
   110  		}
   111  		fromID[eid] = entry
   112  	}
   113  
   114  	n := ID(0)
   115  	s := make([]*Block, 0, 256)
   116  	for _, entry := range entries {
   117  		if dfnum[entry.ID] != notFound {
   118  			continue // already found from a previous entry
   119  		}
   120  		s = append(s, entry)
   121  		parent[entry.ID] = entry.ID
   122  		for len(s) > 0 {
   123  			node := s[len(s)-1]
   124  			s = s[:len(s)-1]
   125  
   126  			n++
   127  			for _, w := range succFn(node) {
   128  				// if it has a dfnum, we've already visited it
   129  				if dfnum[w.ID] == notFound {
   130  					s = append(s, w)
   131  					parent[w.ID] = node.ID
   132  					dfnum[w.ID] = notExplored
   133  				}
   134  			}
   135  			dfnum[node.ID] = n
   136  			order[n] = node.ID
   137  		}
   138  	}
   139  
   140  	return
   141  }
   142  
   143  // dominators computes the dominator tree for f. It returns a slice
   144  // which maps block ID to the immediate dominator of that block.
   145  // Unreachable blocks map to nil. The entry block maps to nil.
   146  func dominators(f *Func) []*Block {
   147  	preds := func(b *Block) []*Block { return b.Preds }
   148  	succs := func(b *Block) []*Block { return b.Succs }
   149  
   150  	//TODO: benchmark and try to find criteria for swapping between
   151  	// dominatorsSimple and dominatorsLT
   152  	return f.dominatorsLT([]*Block{f.Entry}, preds, succs)
   153  }
   154  
   155  // postDominators computes the post-dominator tree for f.
   156  func postDominators(f *Func) []*Block {
   157  	preds := func(b *Block) []*Block { return b.Preds }
   158  	succs := func(b *Block) []*Block { return b.Succs }
   159  
   160  	if len(f.Blocks) == 0 {
   161  		return nil
   162  	}
   163  
   164  	// find the exit blocks
   165  	var exits []*Block
   166  	for _, b := range f.Blocks {
   167  		switch b.Kind {
   168  		case BlockExit, BlockRet, BlockRetJmp, BlockCall, BlockCheck:
   169  			exits = append(exits, b)
   170  		}
   171  	}
   172  
   173  	// infinite loop with no exit
   174  	if exits == nil {
   175  		return make([]*Block, f.NumBlocks())
   176  	}
   177  	return f.dominatorsLT(exits, succs, preds)
   178  }
   179  
   180  // dominatorsLt runs Lengauer-Tarjan to compute a dominator tree starting at
   181  // entry and using predFn/succFn to find predecessors/successors to allow
   182  // computing both dominator and post-dominator trees.
   183  func (f *Func) dominatorsLT(entries []*Block, predFn linkedBlocks, succFn linkedBlocks) []*Block {
   184  	// Based on Lengauer-Tarjan from Modern Compiler Implementation in C -
   185  	// Appel with optimizations from Finding Dominators in Practice -
   186  	// Georgiadis
   187  
   188  	maxBlockID := entries[0].Func.NumBlocks()
   189  
   190  	dfnum, vertex, parent, semi, samedom, ancestor, best, bucket := f.Config.scratchBlocksForDom(maxBlockID)
   191  
   192  	// dfnum := make([]ID, maxBlockID) // conceptually int32, but punning for allocation purposes.
   193  	// vertex := make([]ID, maxBlockID)
   194  	// parent := make([]ID, maxBlockID)
   195  
   196  	// semi := make([]ID, maxBlockID)
   197  	// samedom := make([]ID, maxBlockID)
   198  	// ancestor := make([]ID, maxBlockID)
   199  	// best := make([]ID, maxBlockID)
   200  	// bucket := make([]ID, maxBlockID)
   201  
   202  	// Step 1. Carry out a depth first search of the problem graph. Number
   203  	// the vertices from 1 to n as they are reached during the search.
   204  	fromID := f.dfs(entries, succFn, dfnum, vertex, parent)
   205  
   206  	idom := make([]*Block, maxBlockID)
   207  
   208  	// Step 2. Compute the semidominators of all vertices by applying
   209  	// Theorem 4.  Carry out the computation vertex by vertex in decreasing
   210  	// order by number.
   211  	for i := maxBlockID - 1; i > 0; i-- {
   212  		w := vertex[i]
   213  		if w == 0 {
   214  			continue
   215  		}
   216  
   217  		if dfnum[w] == notFound {
   218  			// skip unreachable node
   219  			continue
   220  		}
   221  
   222  		// Step 3. Implicitly define the immediate dominator of each
   223  		// vertex by applying Corollary 1. (reordered)
   224  		for v := bucket[w]; v != 0; v = bucket[v] {
   225  			u := eval(v, ancestor, semi, dfnum, best)
   226  			if semi[u] == semi[v] {
   227  				idom[v] = fromID[w] // true dominator
   228  			} else {
   229  				samedom[v] = u // v has same dominator as u
   230  			}
   231  		}
   232  
   233  		p := parent[w]
   234  		s := p // semidominator
   235  
   236  		var sp ID
   237  		// calculate the semidominator of w
   238  		for _, v := range predFn(fromID[w]) {
   239  			if dfnum[v.ID] == notFound {
   240  				// skip unreachable predecessor
   241  				continue
   242  			}
   243  
   244  			if dfnum[v.ID] <= dfnum[w] {
   245  				sp = v.ID
   246  			} else {
   247  				sp = semi[eval(v.ID, ancestor, semi, dfnum, best)]
   248  			}
   249  
   250  			if dfnum[sp] < dfnum[s] {
   251  				s = sp
   252  			}
   253  		}
   254  
   255  		// link
   256  		ancestor[w] = p
   257  		best[w] = w
   258  
   259  		semi[w] = s
   260  		if semi[s] != parent[s] {
   261  			bucket[w] = bucket[s]
   262  			bucket[s] = w
   263  		}
   264  	}
   265  
   266  	// Final pass of step 3
   267  	for v := bucket[0]; v != 0; v = bucket[v] {
   268  		idom[v] = fromID[bucket[0]]
   269  	}
   270  
   271  	// Step 4. Explicitly define the immediate dominator of each vertex,
   272  	// carrying out the computation vertex by vertex in increasing order by
   273  	// number.
   274  	for i := 1; i < maxBlockID-1; i++ {
   275  		w := vertex[i]
   276  		if w == 0 {
   277  			continue
   278  		}
   279  		// w has the same dominator as samedom[w]
   280  		if samedom[w] != 0 {
   281  			idom[w] = idom[samedom[w]]
   282  		}
   283  	}
   284  	return idom
   285  }
   286  
   287  // eval function from LT paper with path compression
   288  func eval(v ID, ancestor []ID, semi []ID, dfnum []ID, best []ID) ID {
   289  	a := ancestor[v]
   290  	if ancestor[a] != 0 {
   291  		bid := eval(a, ancestor, semi, dfnum, best)
   292  		ancestor[v] = ancestor[a]
   293  		if dfnum[semi[bid]] < dfnum[semi[best[v]]] {
   294  			best[v] = bid
   295  		}
   296  	}
   297  	return best[v]
   298  }
   299  
   300  // dominators computes the dominator tree for f. It returns a slice
   301  // which maps block ID to the immediate dominator of that block.
   302  // Unreachable blocks map to nil. The entry block maps to nil.
   303  func dominatorsSimple(f *Func) []*Block {
   304  	// A simple algorithm for now
   305  	// Cooper, Harvey, Kennedy
   306  	idom := make([]*Block, f.NumBlocks())
   307  
   308  	// Compute postorder walk
   309  	post := postorder(f)
   310  
   311  	// Make map from block id to order index (for intersect call)
   312  	postnum := make([]int, f.NumBlocks())
   313  	for i, b := range post {
   314  		postnum[b.ID] = i
   315  	}
   316  
   317  	// Make the entry block a self-loop
   318  	idom[f.Entry.ID] = f.Entry
   319  	if postnum[f.Entry.ID] != len(post)-1 {
   320  		f.Fatalf("entry block %v not last in postorder", f.Entry)
   321  	}
   322  
   323  	// Compute relaxation of idom entries
   324  	for {
   325  		changed := false
   326  
   327  		for i := len(post) - 2; i >= 0; i-- {
   328  			b := post[i]
   329  			var d *Block
   330  			for _, p := range b.Preds {
   331  				if idom[p.ID] == nil {
   332  					continue
   333  				}
   334  				if d == nil {
   335  					d = p
   336  					continue
   337  				}
   338  				d = intersect(d, p, postnum, idom)
   339  			}
   340  			if d != idom[b.ID] {
   341  				idom[b.ID] = d
   342  				changed = true
   343  			}
   344  		}
   345  		if !changed {
   346  			break
   347  		}
   348  	}
   349  	// Set idom of entry block to nil instead of itself.
   350  	idom[f.Entry.ID] = nil
   351  	return idom
   352  }
   353  
   354  // intersect finds the closest dominator of both b and c.
   355  // It requires a postorder numbering of all the blocks.
   356  func intersect(b, c *Block, postnum []int, idom []*Block) *Block {
   357  	// TODO: This loop is O(n^2). See BenchmarkNilCheckDeep*.
   358  	for b != c {
   359  		if postnum[b.ID] < postnum[c.ID] {
   360  			b = idom[b.ID]
   361  		} else {
   362  			c = idom[c.ID]
   363  		}
   364  	}
   365  	return b
   366  }