...

Source file src/pkg/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	func newSparseTree(f *Func, parentOf []*Block) SparseTree {
    60		t := make(SparseTree, f.NumBlocks())
    61		for _, b := range f.Blocks {
    62			n := &t[b.ID]
    63			if p := parentOf[b.ID]; p != nil {
    64				n.parent = p
    65				n.sibling = t[p.ID].child
    66				t[p.ID].child = b
    67			}
    68		}
    69		t.numberBlock(f.Entry, 1)
    70		return t
    71	}
    72	
    73	// newSparseOrderedTree creates a SparseTree from a block-to-parent map (array indexed by Block.ID)
    74	// children will appear in the reverse of their order in reverseOrder
    75	// in particular, if reverseOrder is a dfs-reversePostOrder, then the root-to-children
    76	// walk of the tree will yield a pre-order.
    77	func newSparseOrderedTree(f *Func, parentOf, reverseOrder []*Block) SparseTree {
    78		t := make(SparseTree, f.NumBlocks())
    79		for _, b := range reverseOrder {
    80			n := &t[b.ID]
    81			if p := parentOf[b.ID]; p != nil {
    82				n.parent = p
    83				n.sibling = t[p.ID].child
    84				t[p.ID].child = b
    85			}
    86		}
    87		t.numberBlock(f.Entry, 1)
    88		return t
    89	}
    90	
    91	// treestructure provides a string description of the dominator
    92	// tree and flow structure of block b and all blocks that it
    93	// dominates.
    94	func (t SparseTree) treestructure(b *Block) string {
    95		return t.treestructure1(b, 0)
    96	}
    97	func (t SparseTree) treestructure1(b *Block, i int) string {
    98		s := "\n" + strings.Repeat("\t", i) + b.String() + "->["
    99		for i, e := range b.Succs {
   100			if i > 0 {
   101				s += ","
   102			}
   103			s += e.b.String()
   104		}
   105		s += "]"
   106		if c0 := t[b.ID].child; c0 != nil {
   107			s += "("
   108			for c := c0; c != nil; c = t[c.ID].sibling {
   109				if c != c0 {
   110					s += " "
   111				}
   112				s += t.treestructure1(c, i+1)
   113			}
   114			s += ")"
   115		}
   116		return s
   117	}
   118	
   119	// numberBlock assigns entry and exit numbers for b and b's
   120	// children in an in-order walk from a gappy sequence, where n
   121	// is the first number not yet assigned or reserved. N should
   122	// be larger than zero. For each entry and exit number, the
   123	// values one larger and smaller are reserved to indicate
   124	// "strictly above" and "strictly below". numberBlock returns
   125	// the smallest number not yet assigned or reserved (i.e., the
   126	// exit number of the last block visited, plus two, because
   127	// last.exit+1 is a reserved value.)
   128	//
   129	// examples:
   130	//
   131	// single node tree Root, call with n=1
   132	//         entry=2 Root exit=5; returns 7
   133	//
   134	// two node tree, Root->Child, call with n=1
   135	//         entry=2 Root exit=11; returns 13
   136	//         entry=5 Child exit=8
   137	//
   138	// three node tree, Root->(Left, Right), call with n=1
   139	//         entry=2 Root exit=17; returns 19
   140	// entry=5 Left exit=8;  entry=11 Right exit=14
   141	//
   142	// This is the in-order sequence of assigned and reserved numbers
   143	// for the last example:
   144	//   root     left     left      right       right       root
   145	//  1 2e 3 | 4 5e 6 | 7 8x 9 | 10 11e 12 | 13 14x 15 | 16 17x 18
   146	
   147	func (t SparseTree) numberBlock(b *Block, n int32) int32 {
   148		// reserve n for entry-1, assign n+1 to entry
   149		n++
   150		t[b.ID].entry = n
   151		// reserve n+1 for entry+1, n+2 is next free number
   152		n += 2
   153		for c := t[b.ID].child; c != nil; c = t[c.ID].sibling {
   154			n = t.numberBlock(c, n) // preserves n = next free number
   155		}
   156		// reserve n for exit-1, assign n+1 to exit
   157		n++
   158		t[b.ID].exit = n
   159		// reserve n+1 for exit+1, n+2 is next free number, returned.
   160		return n + 2
   161	}
   162	
   163	// Sibling returns a sibling of x in the dominator tree (i.e.,
   164	// a node with the same immediate dominator) or nil if there
   165	// are no remaining siblings in the arbitrary but repeatable
   166	// order chosen. Because the Child-Sibling order is used
   167	// to assign entry and exit numbers in the treewalk, those
   168	// numbers are also consistent with this order (i.e.,
   169	// Sibling(x) has entry number larger than x's exit number).
   170	func (t SparseTree) Sibling(x *Block) *Block {
   171		return t[x.ID].sibling
   172	}
   173	
   174	// Child returns a child of x in the dominator tree, or
   175	// nil if there are none. The choice of first child is
   176	// arbitrary but repeatable.
   177	func (t SparseTree) Child(x *Block) *Block {
   178		return t[x.ID].child
   179	}
   180	
   181	// isAncestorEq reports whether x is an ancestor of or equal to y.
   182	func (t SparseTree) isAncestorEq(x, y *Block) bool {
   183		if x == y {
   184			return true
   185		}
   186		xx := &t[x.ID]
   187		yy := &t[y.ID]
   188		return xx.entry <= yy.entry && yy.exit <= xx.exit
   189	}
   190	
   191	// isAncestor reports whether x is a strict ancestor of y.
   192	func (t SparseTree) isAncestor(x, y *Block) bool {
   193		if x == y {
   194			return false
   195		}
   196		xx := &t[x.ID]
   197		yy := &t[y.ID]
   198		return xx.entry < yy.entry && yy.exit < xx.exit
   199	}
   200	
   201	// domorder returns a value for dominator-oriented sorting.
   202	// Block domination does not provide a total ordering,
   203	// but domorder two has useful properties.
   204	// (1) If domorder(x) > domorder(y) then x does not dominate y.
   205	// (2) If domorder(x) < domorder(y) and domorder(y) < domorder(z) and x does not dominate y,
   206	//     then x does not dominate z.
   207	// Property (1) means that blocks sorted by domorder always have a maximal dominant block first.
   208	// Property (2) allows searches for dominated blocks to exit early.
   209	func (t SparseTree) domorder(x *Block) int32 {
   210		// Here is an argument that entry(x) provides the properties documented above.
   211		//
   212		// Entry and exit values are assigned in a depth-first dominator tree walk.
   213		// For all blocks x and y, one of the following holds:
   214		//
   215		// (x-dom-y) x dominates y => entry(x) < entry(y) < exit(y) < exit(x)
   216		// (y-dom-x) y dominates x => entry(y) < entry(x) < exit(x) < exit(y)
   217		// (x-then-y) neither x nor y dominates the other and x walked before y => entry(x) < exit(x) < entry(y) < exit(y)
   218		// (y-then-x) neither x nor y dominates the other and y walked before y => entry(y) < exit(y) < entry(x) < exit(x)
   219		//
   220		// entry(x) > entry(y) eliminates case x-dom-y. This provides property (1) above.
   221		//
   222		// For property (2), assume entry(x) < entry(y) and entry(y) < entry(z) and x does not dominate y.
   223		// entry(x) < entry(y) allows cases x-dom-y and x-then-y.
   224		// But by supposition, x does not dominate y. So we have x-then-y.
   225		//
   226		// For contractidion, assume x dominates z.
   227		// Then entry(x) < entry(z) < exit(z) < exit(x).
   228		// But we know x-then-y, so entry(x) < exit(x) < entry(y) < exit(y).
   229		// Combining those, entry(x) < entry(z) < exit(z) < exit(x) < entry(y) < exit(y).
   230		// By supposition, entry(y) < entry(z), which allows cases y-dom-z and y-then-z.
   231		// y-dom-z requires entry(y) < entry(z), but we have entry(z) < entry(y).
   232		// y-then-z requires exit(y) < entry(z), but we have entry(z) < exit(y).
   233		// We have a contradiction, so x does not dominate z, as required.
   234		return t[x.ID].entry
   235	}
   236	

View as plain text