github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/src/cmd/compile/internal/ssa/sparsetreemap.go (about)

     1  // Copyright 2016 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  // A SparseTreeMap encodes a subset of nodes within a tree
    10  // used for sparse-ancestor queries.
    11  //
    12  // Combined with a SparseTreeHelper, this supports an Insert
    13  // to add a tree node to the set and a Find operation to locate
    14  // the nearest tree ancestor of a given node such that the
    15  // ancestor is also in the set.
    16  //
    17  // Given a set of blocks {B1, B2, B3} within the dominator tree, established by
    18  // stm.Insert()ing B1, B2, B3, etc, a query at block B
    19  // (performed with stm.Find(stm, B, adjust, helper))
    20  // will return the member of the set that is the nearest strict
    21  // ancestor of B within the dominator tree, or nil if none exists.
    22  // The expected complexity of this operation is the log of the size
    23  // the set, given certain assumptions about sparsity (the log complexity
    24  // could be guaranteed with additional data structures whose constant-
    25  // factor overhead has not yet been justified.)
    26  //
    27  // The adjust parameter allows positioning of the insertion
    28  // and lookup points within a block -- one of
    29  // AdjustBefore, AdjustWithin, AdjustAfter,
    30  // where lookups at AdjustWithin can find insertions at
    31  // AdjustBefore in the same block, and lookups at AdjustAfter
    32  // can find insertions at either AdjustBefore or AdjustWithin
    33  // in the same block.  (Note that this assumes a gappy numbering
    34  // such that exit number or exit number is separated from its
    35  // nearest neighbor by at least 3).
    36  //
    37  // The Sparse Tree lookup algorithm is described by
    38  // Paul F. Dietz. Maintaining order in a linked list. In
    39  // Proceedings of the Fourteenth Annual ACM Symposium on
    40  // Theory of Computing, pages 122–127, May 1982.
    41  // and by
    42  // Ben Wegbreit. Faster retrieval from context trees.
    43  // Communications of the ACM, 19(9):526–529, September 1976.
    44  type SparseTreeMap RBTint32
    45  
    46  // A SparseTreeHelper contains indexing and allocation data
    47  // structures common to a collection of SparseTreeMaps, as well
    48  // as exposing some useful control-flow-related data to other
    49  // packages, such as gc.
    50  type SparseTreeHelper struct {
    51  	Sdom   []SparseTreeNode // indexed by block.ID
    52  	Po     []*Block         // exported data
    53  	Dom    []*Block         // exported data
    54  	Ponums []int32          // exported data
    55  }
    56  
    57  // NewSparseTreeHelper returns a SparseTreeHelper for use
    58  // in the gc package, for example in phi-function placement.
    59  func NewSparseTreeHelper(f *Func) *SparseTreeHelper {
    60  	dom := dominators(f)
    61  	ponums := make([]int32, f.NumBlocks())
    62  	po := postorderWithNumbering(f, ponums)
    63  	return makeSparseTreeHelper(newSparseTree(f, dom), dom, po, ponums)
    64  }
    65  
    66  func (h *SparseTreeHelper) NewTree() *SparseTreeMap {
    67  	return &SparseTreeMap{}
    68  }
    69  
    70  func makeSparseTreeHelper(sdom SparseTree, dom, po []*Block, ponums []int32) *SparseTreeHelper {
    71  	helper := &SparseTreeHelper{Sdom: []SparseTreeNode(sdom),
    72  		Dom:    dom,
    73  		Po:     po,
    74  		Ponums: ponums,
    75  	}
    76  	return helper
    77  }
    78  
    79  // A sparseTreeMapEntry contains the data stored in a binary search
    80  // data structure indexed by (dominator tree walk) entry and exit numbers.
    81  // Each entry is added twice, once keyed by entry-1/entry/entry+1 and
    82  // once keyed by exit+1/exit/exit-1. (there are three choices of paired indices, not 9, and they properly nest)
    83  type sparseTreeMapEntry struct {
    84  	index *SparseTreeNode
    85  	block *Block // TODO: store this in a separate index.
    86  	data  interface{}
    87  }
    88  
    89  // Insert creates a definition within b with data x.
    90  // adjust indicates where in the block should be inserted:
    91  // AdjustBefore means defined at a phi function (visible Within or After in the same block)
    92  // AdjustWithin means defined within the block (visible After in the same block)
    93  // AdjustAfter means after the block (visible within child blocks)
    94  func (m *SparseTreeMap) Insert(b *Block, adjust int32, x interface{}, helper *SparseTreeHelper) {
    95  	rbtree := (*RBTint32)(m)
    96  	blockIndex := &helper.Sdom[b.ID]
    97  	if blockIndex.entry == 0 {
    98  		// assert unreachable
    99  		return
   100  	}
   101  	entry := &sparseTreeMapEntry{index: blockIndex, data: x}
   102  	right := blockIndex.exit - adjust
   103  	_ = rbtree.Insert(right, entry)
   104  
   105  	left := blockIndex.entry + adjust
   106  	_ = rbtree.Insert(left, entry)
   107  }
   108  
   109  // Find returns the definition visible from block b, or nil if none can be found.
   110  // Adjust indicates where the block should be searched.
   111  // AdjustBefore searches before the phi functions of b.
   112  // AdjustWithin searches starting at the phi functions of b.
   113  // AdjustAfter searches starting at the exit from the block, including normal within-block definitions.
   114  //
   115  // Note that Finds are properly nested with Inserts:
   116  // m.Insert(b, a) followed by m.Find(b, a) will not return the result of the insert,
   117  // but m.Insert(b, AdjustBefore) followed by m.Find(b, AdjustWithin) will.
   118  //
   119  // Another way to think of this is that Find searches for inputs, Insert defines outputs.
   120  func (m *SparseTreeMap) Find(b *Block, adjust int32, helper *SparseTreeHelper) interface{} {
   121  	rbtree := (*RBTint32)(m)
   122  	if rbtree == nil {
   123  		return nil
   124  	}
   125  	blockIndex := &helper.Sdom[b.ID]
   126  	_, v := rbtree.Glb(blockIndex.entry + adjust)
   127  	for v != nil {
   128  		otherEntry := v.(*sparseTreeMapEntry)
   129  		otherIndex := otherEntry.index
   130  		// Two cases -- either otherIndex brackets blockIndex,
   131  		// or it doesn't.
   132  		//
   133  		// Note that if otherIndex and blockIndex are
   134  		// the same block, then the glb test only passed
   135  		// because the definition is "before",
   136  		// i.e., k == blockIndex.entry-1
   137  		// allowing equality is okay on the blocks check.
   138  		if otherIndex.exit >= blockIndex.exit {
   139  			// bracketed.
   140  			return otherEntry.data
   141  		}
   142  		// In the not-bracketed case, we could memoize the results of
   143  		// walking up the tree, but for now we won't.
   144  		// Memoize plan is to take the gap (inclusive)
   145  		// from otherIndex.exit+1 to blockIndex.entry-1
   146  		// and insert it into this or a second tree.
   147  		// Said tree would then need adjusting whenever
   148  		// an insertion occurred.
   149  
   150  		// Expectation is that per-variable tree is sparse,
   151  		// therefore probe siblings instead of climbing up.
   152  		// Note that each sibling encountered in this walk
   153  		// to find a defining ancestor shares that ancestor
   154  		// because the walk skips over the interior -- each
   155  		// Glb will be an exit, and the iteration is to the
   156  		// Glb of the entry.
   157  		_, v = rbtree.Glb(otherIndex.entry - 1)
   158  	}
   159  	return nil // nothing found
   160  }
   161  
   162  func (m *SparseTreeMap) String() string {
   163  	tree := (*RBTint32)(m)
   164  	return tree.String()
   165  }
   166  
   167  func (e *sparseTreeMapEntry) String() string {
   168  	return fmt.Sprintf("index=%v, data=%v", e.index, e.data)
   169  }