github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/src/cmd/compile/internal/ssa/sparsetree.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  import "fmt"
     8  
     9  type SparseTreeNode struct {
    10  	child   *Block
    11  	sibling *Block
    12  	parent  *Block
    13  
    14  	// Every block has 6 numbers associated with it:
    15  	// entry-1, entry, entry+1, exit-1, and exit, exit+1.
    16  	// entry and exit are conceptually the top of the block (phi functions)
    17  	// entry+1 and exit-1 are conceptually the bottom of the block (ordinary defs)
    18  	// entry-1 and exit+1 are conceptually "just before" the block (conditions flowing in)
    19  	//
    20  	// This simplifies life if we wish to query information about x
    21  	// when x is both an input to and output of a block.
    22  	entry, exit int32
    23  }
    24  
    25  func (s *SparseTreeNode) String() string {
    26  	return fmt.Sprintf("[%d,%d]", s.entry, s.exit)
    27  }
    28  
    29  func (s *SparseTreeNode) Entry() int32 {
    30  	return s.entry
    31  }
    32  
    33  func (s *SparseTreeNode) Exit() int32 {
    34  	return s.exit
    35  }
    36  
    37  const (
    38  	// When used to lookup up definitions in a sparse tree,
    39  	// these adjustments to a block's entry (+adjust) and
    40  	// exit (-adjust) numbers allow a distinction to be made
    41  	// between assignments (typically branch-dependent
    42  	// conditionals) occurring "before" the block (e.g., as inputs
    43  	// to the block and its phi functions), "within" the block,
    44  	// and "after" the block.
    45  	AdjustBefore = -1 // defined before phi
    46  	AdjustWithin = 0  // defined by phi
    47  	AdjustAfter  = 1  // defined within block
    48  )
    49  
    50  // A SparseTree is a tree of Blocks.
    51  // It allows rapid ancestor queries,
    52  // such as whether one block dominates another.
    53  type SparseTree []SparseTreeNode
    54  
    55  // newSparseTree creates a SparseTree from a block-to-parent map (array indexed by Block.ID)
    56  func newSparseTree(f *Func, parentOf []*Block) SparseTree {
    57  	t := make(SparseTree, f.NumBlocks())
    58  	for _, b := range f.Blocks {
    59  		n := &t[b.ID]
    60  		if p := parentOf[b.ID]; p != nil {
    61  			n.parent = p
    62  			n.sibling = t[p.ID].child
    63  			t[p.ID].child = b
    64  		}
    65  	}
    66  	t.numberBlock(f.Entry, 1)
    67  	return t
    68  }
    69  
    70  // numberBlock assigns entry and exit numbers for b and b's
    71  // children in an in-order walk from a gappy sequence, where n
    72  // is the first number not yet assigned or reserved. N should
    73  // be larger than zero. For each entry and exit number, the
    74  // values one larger and smaller are reserved to indicate
    75  // "strictly above" and "strictly below". numberBlock returns
    76  // the smallest number not yet assigned or reserved (i.e., the
    77  // exit number of the last block visited, plus two, because
    78  // last.exit+1 is a reserved value.)
    79  //
    80  // examples:
    81  //
    82  // single node tree Root, call with n=1
    83  //         entry=2 Root exit=5; returns 7
    84  //
    85  // two node tree, Root->Child, call with n=1
    86  //         entry=2 Root exit=11; returns 13
    87  //         entry=5 Child exit=8
    88  //
    89  // three node tree, Root->(Left, Right), call with n=1
    90  //         entry=2 Root exit=17; returns 19
    91  // entry=5 Left exit=8;  entry=11 Right exit=14
    92  //
    93  // This is the in-order sequence of assigned and reserved numbers
    94  // for the last example:
    95  //   root     left     left      right       right       root
    96  //  1 2e 3 | 4 5e 6 | 7 8x 9 | 10 11e 12 | 13 14x 15 | 16 17x 18
    97  
    98  func (t SparseTree) numberBlock(b *Block, n int32) int32 {
    99  	// reserve n for entry-1, assign n+1 to entry
   100  	n++
   101  	t[b.ID].entry = n
   102  	// reserve n+1 for entry+1, n+2 is next free number
   103  	n += 2
   104  	for c := t[b.ID].child; c != nil; c = t[c.ID].sibling {
   105  		n = t.numberBlock(c, n) // preserves n = next free number
   106  	}
   107  	// reserve n for exit-1, assign n+1 to exit
   108  	n++
   109  	t[b.ID].exit = n
   110  	// reserve n+1 for exit+1, n+2 is next free number, returned.
   111  	return n + 2
   112  }
   113  
   114  // Sibling returns a sibling of x in the dominator tree (i.e.,
   115  // a node with the same immediate dominator) or nil if there
   116  // are no remaining siblings in the arbitrary but repeatable
   117  // order chosen. Because the Child-Sibling order is used
   118  // to assign entry and exit numbers in the treewalk, those
   119  // numbers are also consistent with this order (i.e.,
   120  // Sibling(x) has entry number larger than x's exit number).
   121  func (t SparseTree) Sibling(x *Block) *Block {
   122  	return t[x.ID].sibling
   123  }
   124  
   125  // Child returns a child of x in the dominator tree, or
   126  // nil if there are none. The choice of first child is
   127  // arbitrary but repeatable.
   128  func (t SparseTree) Child(x *Block) *Block {
   129  	return t[x.ID].child
   130  }
   131  
   132  // isAncestorEq reports whether x is an ancestor of or equal to y.
   133  func (t SparseTree) isAncestorEq(x, y *Block) bool {
   134  	if x == y {
   135  		return true
   136  	}
   137  	xx := &t[x.ID]
   138  	yy := &t[y.ID]
   139  	return xx.entry <= yy.entry && yy.exit <= xx.exit
   140  }
   141  
   142  // isAncestor reports whether x is a strict ancestor of y.
   143  func (t SparseTree) isAncestor(x, y *Block) bool {
   144  	if x == y {
   145  		return false
   146  	}
   147  	xx := &t[x.ID]
   148  	yy := &t[y.ID]
   149  	return xx.entry < yy.entry && yy.exit < xx.exit
   150  }
   151  
   152  // domorder returns a value for dominator-oriented sorting.
   153  // Block domination does not provide a total ordering,
   154  // but domorder two has useful properties.
   155  // (1) If domorder(x) > domorder(y) then x does not dominate y.
   156  // (2) If domorder(x) < domorder(y) and domorder(y) < domorder(z) and x does not dominate y,
   157  //     then x does not dominate z.
   158  // Property (1) means that blocks sorted by domorder always have a maximal dominant block first.
   159  // Property (2) allows searches for dominated blocks to exit early.
   160  func (t SparseTree) domorder(x *Block) int32 {
   161  	// Here is an argument that entry(x) provides the properties documented above.
   162  	//
   163  	// Entry and exit values are assigned in a depth-first dominator tree walk.
   164  	// For all blocks x and y, one of the following holds:
   165  	//
   166  	// (x-dom-y) x dominates y => entry(x) < entry(y) < exit(y) < exit(x)
   167  	// (y-dom-x) y dominates x => entry(y) < entry(x) < exit(x) < exit(y)
   168  	// (x-then-y) neither x nor y dominates the other and x walked before y => entry(x) < exit(x) < entry(y) < exit(y)
   169  	// (y-then-x) neither x nor y dominates the other and y walked before y => entry(y) < exit(y) < entry(x) < exit(x)
   170  	//
   171  	// entry(x) > entry(y) eliminates case x-dom-y. This provides property (1) above.
   172  	//
   173  	// For property (2), assume entry(x) < entry(y) and entry(y) < entry(z) and x does not dominate y.
   174  	// entry(x) < entry(y) allows cases x-dom-y and x-then-y.
   175  	// But by supposition, x does not dominate y. So we have x-then-y.
   176  	//
   177  	// For contractidion, assume x dominates z.
   178  	// Then entry(x) < entry(z) < exit(z) < exit(x).
   179  	// But we know x-then-y, so entry(x) < exit(x) < entry(y) < exit(y).
   180  	// Combining those, entry(x) < entry(z) < exit(z) < exit(x) < entry(y) < exit(y).
   181  	// By supposition, entry(y) < entry(z), which allows cases y-dom-z and y-then-z.
   182  	// y-dom-z requires entry(y) < entry(z), but we have entry(z) < entry(y).
   183  	// y-then-z requires exit(y) < entry(z), but we have entry(z) < exit(y).
   184  	// We have a contradiction, so x does not dominate z, as required.
   185  	return t[x.ID].entry
   186  }