github.com/euank/go@v0.0.0-20160829210321-495514729181/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  type markKind uint8
     9  
    10  const (
    11  	notFound    markKind = 0 // block has not been discovered yet
    12  	notExplored markKind = 1 // discovered and in queue, outedges not processed yet
    13  	explored    markKind = 2 // discovered and in queue, outedges processed
    14  	done        markKind = 3 // all done, in output ordering
    15  )
    16  
    17  // This file contains code to compute the dominator tree
    18  // of a control-flow graph.
    19  
    20  // postorder computes a postorder traversal ordering for the
    21  // basic blocks in f. Unreachable blocks will not appear.
    22  func postorder(f *Func) []*Block {
    23  	return postorderWithNumbering(f, []int32{})
    24  }
    25  func postorderWithNumbering(f *Func, ponums []int32) []*Block {
    26  	mark := make([]markKind, f.NumBlocks())
    27  
    28  	// result ordering
    29  	var order []*Block
    30  
    31  	// stack of blocks
    32  	var s []*Block
    33  	s = append(s, f.Entry)
    34  	mark[f.Entry.ID] = notExplored
    35  	for len(s) > 0 {
    36  		b := s[len(s)-1]
    37  		switch mark[b.ID] {
    38  		case explored:
    39  			// Children have all been visited. Pop & output block.
    40  			s = s[:len(s)-1]
    41  			mark[b.ID] = done
    42  			if len(ponums) > 0 {
    43  				ponums[b.ID] = int32(len(order))
    44  			}
    45  			order = append(order, b)
    46  		case notExplored:
    47  			// Children have not been visited yet. Mark as explored
    48  			// and queue any children we haven't seen yet.
    49  			mark[b.ID] = explored
    50  			for _, e := range b.Succs {
    51  				c := e.b
    52  				if mark[c.ID] == notFound {
    53  					mark[c.ID] = notExplored
    54  					s = append(s, c)
    55  				}
    56  			}
    57  		default:
    58  			b.Fatalf("bad stack state %v %d", b, mark[b.ID])
    59  		}
    60  	}
    61  	return order
    62  }
    63  
    64  type linkedBlocks func(*Block) []Edge
    65  
    66  const nscratchslices = 7
    67  
    68  // experimentally, functions with 512 or fewer blocks account
    69  // for 75% of memory (size) allocation for dominator computation
    70  // in make.bash.
    71  const minscratchblocks = 512
    72  
    73  func (cfg *Config) scratchBlocksForDom(maxBlockID int) (a, b, c, d, e, f, g []ID) {
    74  	tot := maxBlockID * nscratchslices
    75  	scratch := cfg.domblockstore
    76  	if len(scratch) < tot {
    77  		// req = min(1.5*tot, nscratchslices*minscratchblocks)
    78  		// 50% padding allows for graph growth in later phases.
    79  		req := (tot * 3) >> 1
    80  		if req < nscratchslices*minscratchblocks {
    81  			req = nscratchslices * minscratchblocks
    82  		}
    83  		scratch = make([]ID, req)
    84  		cfg.domblockstore = scratch
    85  	} else {
    86  		// Clear as much of scratch as we will (re)use
    87  		scratch = scratch[0:tot]
    88  		for i := range scratch {
    89  			scratch[i] = 0
    90  		}
    91  	}
    92  
    93  	a = scratch[0*maxBlockID : 1*maxBlockID]
    94  	b = scratch[1*maxBlockID : 2*maxBlockID]
    95  	c = scratch[2*maxBlockID : 3*maxBlockID]
    96  	d = scratch[3*maxBlockID : 4*maxBlockID]
    97  	e = scratch[4*maxBlockID : 5*maxBlockID]
    98  	f = scratch[5*maxBlockID : 6*maxBlockID]
    99  	g = scratch[6*maxBlockID : 7*maxBlockID]
   100  
   101  	return
   102  }
   103  
   104  func dominators(f *Func) []*Block {
   105  	preds := func(b *Block) []Edge { return b.Preds }
   106  	succs := func(b *Block) []Edge { return b.Succs }
   107  
   108  	//TODO: benchmark and try to find criteria for swapping between
   109  	// dominatorsSimple and dominatorsLT
   110  	return f.dominatorsLTOrig(f.Entry, preds, succs)
   111  }
   112  
   113  // dominatorsLTOrig runs Lengauer-Tarjan to compute a dominator tree starting at
   114  // entry and using predFn/succFn to find predecessors/successors to allow
   115  // computing both dominator and post-dominator trees.
   116  func (f *Func) dominatorsLTOrig(entry *Block, predFn linkedBlocks, succFn linkedBlocks) []*Block {
   117  	// Adapted directly from the original TOPLAS article's "simple" algorithm
   118  
   119  	maxBlockID := entry.Func.NumBlocks()
   120  	semi, vertex, label, parent, ancestor, bucketHead, bucketLink := f.Config.scratchBlocksForDom(maxBlockID)
   121  
   122  	// This version uses integers for most of the computation,
   123  	// to make the work arrays smaller and pointer-free.
   124  	// fromID translates from ID to *Block where that is needed.
   125  	fromID := make([]*Block, maxBlockID)
   126  	for _, v := range f.Blocks {
   127  		fromID[v.ID] = v
   128  	}
   129  	idom := make([]*Block, maxBlockID)
   130  
   131  	// Step 1. Carry out a depth first search of the problem graph. Number
   132  	// the vertices from 1 to n as they are reached during the search.
   133  	n := f.dfsOrig(entry, succFn, semi, vertex, label, parent)
   134  
   135  	for i := n; i >= 2; i-- {
   136  		w := vertex[i]
   137  
   138  		// step2 in TOPLAS paper
   139  		for _, e := range predFn(fromID[w]) {
   140  			v := e.b
   141  			if semi[v.ID] == 0 {
   142  				// skip unreachable predecessor
   143  				// not in original, but we're using existing pred instead of building one.
   144  				continue
   145  			}
   146  			u := evalOrig(v.ID, ancestor, semi, label)
   147  			if semi[u] < semi[w] {
   148  				semi[w] = semi[u]
   149  			}
   150  		}
   151  
   152  		// add w to bucket[vertex[semi[w]]]
   153  		// implement bucket as a linked list implemented
   154  		// in a pair of arrays.
   155  		vsw := vertex[semi[w]]
   156  		bucketLink[w] = bucketHead[vsw]
   157  		bucketHead[vsw] = w
   158  
   159  		linkOrig(parent[w], w, ancestor)
   160  
   161  		// step3 in TOPLAS paper
   162  		for v := bucketHead[parent[w]]; v != 0; v = bucketLink[v] {
   163  			u := evalOrig(v, ancestor, semi, label)
   164  			if semi[u] < semi[v] {
   165  				idom[v] = fromID[u]
   166  			} else {
   167  				idom[v] = fromID[parent[w]]
   168  			}
   169  		}
   170  	}
   171  	// step 4 in toplas paper
   172  	for i := ID(2); i <= n; i++ {
   173  		w := vertex[i]
   174  		if idom[w].ID != vertex[semi[w]] {
   175  			idom[w] = idom[idom[w].ID]
   176  		}
   177  	}
   178  
   179  	return idom
   180  }
   181  
   182  // dfs performs a depth first search over the blocks starting at entry block
   183  // (in arbitrary order).  This is a de-recursed version of dfs from the
   184  // original Tarjan-Lengauer TOPLAS article.  It's important to return the
   185  // same values for parent as the original algorithm.
   186  func (f *Func) dfsOrig(entry *Block, succFn linkedBlocks, semi, vertex, label, parent []ID) ID {
   187  	n := ID(0)
   188  	s := make([]*Block, 0, 256)
   189  	s = append(s, entry)
   190  
   191  	for len(s) > 0 {
   192  		v := s[len(s)-1]
   193  		s = s[:len(s)-1]
   194  		// recursing on v
   195  
   196  		if semi[v.ID] != 0 {
   197  			continue // already visited
   198  		}
   199  		n++
   200  		semi[v.ID] = n
   201  		vertex[n] = v.ID
   202  		label[v.ID] = v.ID
   203  		// ancestor[v] already zero
   204  		for _, e := range succFn(v) {
   205  			w := e.b
   206  			// if it has a dfnum, we've already visited it
   207  			if semi[w.ID] == 0 {
   208  				// yes, w can be pushed multiple times.
   209  				s = append(s, w)
   210  				parent[w.ID] = v.ID // keep overwriting this till it is visited.
   211  			}
   212  		}
   213  	}
   214  	return n
   215  }
   216  
   217  // compressOrig is the "simple" compress function from LT paper
   218  func compressOrig(v ID, ancestor, semi, label []ID) {
   219  	if ancestor[ancestor[v]] != 0 {
   220  		compressOrig(ancestor[v], ancestor, semi, label)
   221  		if semi[label[ancestor[v]]] < semi[label[v]] {
   222  			label[v] = label[ancestor[v]]
   223  		}
   224  		ancestor[v] = ancestor[ancestor[v]]
   225  	}
   226  }
   227  
   228  // evalOrig is the "simple" eval function from LT paper
   229  func evalOrig(v ID, ancestor, semi, label []ID) ID {
   230  	if ancestor[v] == 0 {
   231  		return v
   232  	}
   233  	compressOrig(v, ancestor, semi, label)
   234  	return label[v]
   235  }
   236  
   237  func linkOrig(v, w ID, ancestor []ID) {
   238  	ancestor[w] = v
   239  }
   240  
   241  // dominators computes the dominator tree for f. It returns a slice
   242  // which maps block ID to the immediate dominator of that block.
   243  // Unreachable blocks map to nil. The entry block maps to nil.
   244  func dominatorsSimple(f *Func) []*Block {
   245  	// A simple algorithm for now
   246  	// Cooper, Harvey, Kennedy
   247  	idom := make([]*Block, f.NumBlocks())
   248  
   249  	// Compute postorder walk
   250  	post := postorder(f)
   251  
   252  	// Make map from block id to order index (for intersect call)
   253  	postnum := make([]int, f.NumBlocks())
   254  	for i, b := range post {
   255  		postnum[b.ID] = i
   256  	}
   257  
   258  	// Make the entry block a self-loop
   259  	idom[f.Entry.ID] = f.Entry
   260  	if postnum[f.Entry.ID] != len(post)-1 {
   261  		f.Fatalf("entry block %v not last in postorder", f.Entry)
   262  	}
   263  
   264  	// Compute relaxation of idom entries
   265  	for {
   266  		changed := false
   267  
   268  		for i := len(post) - 2; i >= 0; i-- {
   269  			b := post[i]
   270  			var d *Block
   271  			for _, e := range b.Preds {
   272  				p := e.b
   273  				if idom[p.ID] == nil {
   274  					continue
   275  				}
   276  				if d == nil {
   277  					d = p
   278  					continue
   279  				}
   280  				d = intersect(d, p, postnum, idom)
   281  			}
   282  			if d != idom[b.ID] {
   283  				idom[b.ID] = d
   284  				changed = true
   285  			}
   286  		}
   287  		if !changed {
   288  			break
   289  		}
   290  	}
   291  	// Set idom of entry block to nil instead of itself.
   292  	idom[f.Entry.ID] = nil
   293  	return idom
   294  }
   295  
   296  // intersect finds the closest dominator of both b and c.
   297  // It requires a postorder numbering of all the blocks.
   298  func intersect(b, c *Block, postnum []int, idom []*Block) *Block {
   299  	// TODO: This loop is O(n^2). See BenchmarkNilCheckDeep*.
   300  	for b != c {
   301  		if postnum[b.ID] < postnum[c.ID] {
   302  			b = idom[b.ID]
   303  		} else {
   304  			c = idom[c.ID]
   305  		}
   306  	}
   307  	return b
   308  }
   309  
   310  // build immediate dominators.
   311  func domTree(f *Func) {
   312  	f.idom = dominators(f)
   313  	f.sdom = newSparseTree(f, f.idom)
   314  }