Source file src/cmd/compile/internal/ssa/sparsetree.go

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

View as plain text