github.com/euank/go@v0.0.0-20160829210321-495514729181/src/cmd/compile/internal/gc/sparselocatephifunctions.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 gc
     6  
     7  import (
     8  	"cmd/compile/internal/ssa"
     9  	"fmt"
    10  	"math"
    11  )
    12  
    13  // sparseDefState contains a Go map from ONAMEs (*Node) to sparse definition trees, and
    14  // a search helper for the CFG's dominator tree in which those definitions are embedded.
    15  // Once initialized, given a use of an ONAME within a block, the ssa definition for
    16  // that ONAME can be discovered in time roughly proportional to the log of the number
    17  // of SSA definitions of that ONAME (thus avoiding pathological quadratic behavior for
    18  // very large programs).  The helper contains state (a dominator tree numbering) common
    19  // to all the sparse definition trees, as well as some necessary data obtained from
    20  // the ssa package.
    21  //
    22  // This algorithm has improved asymptotic complexity, but the constant factor is
    23  // rather large and thus it is only preferred for very large inputs containing
    24  // 1000s of blocks and variables.
    25  type sparseDefState struct {
    26  	helper         *ssa.SparseTreeHelper // contains one copy of information needed to do sparse mapping
    27  	defmapForOname map[*Node]*onameDefs  // for each ONAME, its definition set (normal and phi)
    28  }
    29  
    30  // onameDefs contains a record of definitions (ordinary and implied phi function) for a single OName.
    31  // stm is the set of definitions for the OName.
    32  // firstdef and lastuse are postorder block numberings that
    33  // conservatively bracket the entire lifetime of the OName.
    34  type onameDefs struct {
    35  	stm *ssa.SparseTreeMap
    36  	// firstdef and lastuse define an interval in the postorder numbering
    37  	// that is guaranteed to include the entire lifetime of an ONAME.
    38  	// In the postorder numbering, math.MaxInt32 is before anything,
    39  	// and 0 is after-or-equal all exit nodes and infinite loops.
    40  	firstdef int32 // the first definition of this ONAME *in the postorder numbering*
    41  	lastuse  int32 // the last use of this ONAME *in the postorder numbering*
    42  }
    43  
    44  // defsFor finds or creates-and-inserts-in-map the definition information
    45  // (sparse tree and live range) for a given OName.
    46  func (m *sparseDefState) defsFor(n *Node) *onameDefs {
    47  	d := m.defmapForOname[n]
    48  	if d != nil {
    49  		return d
    50  	}
    51  	// Reminder: firstdef/lastuse are postorder indices, not block indices,
    52  	// so these default values define an empty interval, not the entire one.
    53  	d = &onameDefs{stm: m.helper.NewTree(), firstdef: 0, lastuse: math.MaxInt32}
    54  	m.defmapForOname[n] = d
    55  	return d
    56  }
    57  
    58  // Insert adds a definition at b (with specified before/within/after adjustment)
    59  // to sparse tree onameDefs.  The lifetime is extended as necessary.
    60  func (m *sparseDefState) Insert(tree *onameDefs, b *ssa.Block, adjust int32) {
    61  	bponum := m.helper.Ponums[b.ID]
    62  	if bponum > tree.firstdef {
    63  		tree.firstdef = bponum
    64  	}
    65  	tree.stm.Insert(b, adjust, b, m.helper)
    66  }
    67  
    68  // Use updates tree to record a use within b, extending the lifetime as necessary.
    69  func (m *sparseDefState) Use(tree *onameDefs, b *ssa.Block) {
    70  	bponum := m.helper.Ponums[b.ID]
    71  	if bponum < tree.lastuse {
    72  		tree.lastuse = bponum
    73  	}
    74  }
    75  
    76  // locatePotentialPhiFunctions finds all the places where phi functions
    77  // will be inserted into a program and records those and ordinary definitions
    78  // in a "map" (not a Go map) that given an OName and use site, returns the
    79  // SSA definition for that OName that will reach the use site (that is,
    80  // the use site's nearest def/phi site in the dominator tree.)
    81  func (s *state) locatePotentialPhiFunctions(fn *Node) *sparseDefState {
    82  	// s.config.SparsePhiCutoff() is compared with product of numblocks and numvalues,
    83  	// if product is smaller than cutoff, use old non-sparse method.
    84  	// cutoff == 0 implies all sparse
    85  	// cutoff == uint(-1) implies all non-sparse
    86  	if uint64(s.f.NumValues())*uint64(s.f.NumBlocks()) < s.config.SparsePhiCutoff() {
    87  		return nil
    88  	}
    89  
    90  	helper := ssa.NewSparseTreeHelper(s.f)
    91  	po := helper.Po // index by block.ID to obtain postorder # of block.
    92  	trees := make(map[*Node]*onameDefs)
    93  	dm := &sparseDefState{defmapForOname: trees, helper: helper}
    94  
    95  	// Process params, taking note of their special lifetimes
    96  	b := s.f.Entry
    97  	for _, n := range fn.Func.Dcl {
    98  		switch n.Class {
    99  		case PPARAM, PPARAMOUT:
   100  			t := dm.defsFor(n)
   101  			dm.Insert(t, b, ssa.AdjustBefore) // define param at entry block
   102  			if n.Class == PPARAMOUT {
   103  				dm.Use(t, po[0]) // Explicitly use PPARAMOUT at very last block
   104  			}
   105  		default:
   106  		}
   107  	}
   108  
   109  	// Process memory variable.
   110  	t := dm.defsFor(&memVar)
   111  	dm.Insert(t, b, ssa.AdjustBefore) // define memory at entry block
   112  	dm.Use(t, po[0])                  // Explicitly use memory at last block
   113  
   114  	// Next load the map w/ basic definitions for ONames recorded per-block
   115  	// Iterate over po to avoid unreachable blocks.
   116  	for i := len(po) - 1; i >= 0; i-- {
   117  		b := po[i]
   118  		m := s.defvars[b.ID]
   119  		for n := range m { // no specified order, but per-node trees are independent.
   120  			t := dm.defsFor(n)
   121  			dm.Insert(t, b, ssa.AdjustWithin)
   122  		}
   123  	}
   124  
   125  	// Find last use of each variable
   126  	for _, v := range s.fwdRefs {
   127  		b := v.Block
   128  		name := v.Aux.(*Node)
   129  		t := dm.defsFor(name)
   130  		dm.Use(t, b)
   131  	}
   132  
   133  	for _, t := range trees {
   134  		// iterating over names in the outer loop
   135  		for change := true; change; {
   136  			change = false
   137  			for i := t.firstdef; i >= t.lastuse; i-- {
   138  				// Iterating in reverse of post-order reduces number of 'change' iterations;
   139  				// all possible forward flow goes through each time.
   140  				b := po[i]
   141  				// Within tree t, would a use at b require a phi function to ensure a single definition?
   142  				// TODO: perhaps more efficient to record specific use sites instead of range?
   143  				if len(b.Preds) < 2 {
   144  					continue // no phi possible
   145  				}
   146  				phi := t.stm.Find(b, ssa.AdjustWithin, helper) // Look for defs in earlier block or AdjustBefore in this one.
   147  				if phi != nil && phi.(*ssa.Block) == b {
   148  					continue // has a phi already in this block.
   149  				}
   150  				var defseen interface{}
   151  				// Do preds see different definitions? if so, need a phi function.
   152  				for _, e := range b.Preds {
   153  					p := e.Block()
   154  					dm.Use(t, p)                                // always count phi pred as "use"; no-op except for loop edges, which matter.
   155  					x := t.stm.Find(p, ssa.AdjustAfter, helper) // Look for defs reaching or within predecessors.
   156  					if x == nil {                               // nil def from a predecessor means a backedge that will be visited soon.
   157  						continue
   158  					}
   159  					if defseen == nil {
   160  						defseen = x
   161  					}
   162  					if defseen != x {
   163  						// Need to insert a phi function here because predecessors's definitions differ.
   164  						change = true
   165  						// Phi insertion is at AdjustBefore, visible with find in same block at AdjustWithin or AdjustAfter.
   166  						dm.Insert(t, b, ssa.AdjustBefore)
   167  						break
   168  					}
   169  				}
   170  			}
   171  		}
   172  	}
   173  	return dm
   174  }
   175  
   176  // FindBetterDefiningBlock tries to find a better block for a definition of OName name
   177  // reaching (or within) p than p itself.  If it cannot, it returns p instead.
   178  // This aids in more efficient location of phi functions, since it can skip over
   179  // branch code that might contain a definition of name if it actually does not.
   180  func (m *sparseDefState) FindBetterDefiningBlock(name *Node, p *ssa.Block) *ssa.Block {
   181  	if m == nil {
   182  		return p
   183  	}
   184  	t := m.defmapForOname[name]
   185  	// For now this is fail-soft, since the old algorithm still works using the unimproved block.
   186  	if t == nil {
   187  		return p
   188  	}
   189  	x := t.stm.Find(p, ssa.AdjustAfter, m.helper)
   190  	if x == nil {
   191  		return p
   192  	}
   193  	b := x.(*ssa.Block)
   194  	if b == nil {
   195  		return p
   196  	}
   197  	return b
   198  }
   199  
   200  func (d *onameDefs) String() string {
   201  	return fmt.Sprintf("onameDefs:first=%d,last=%d,tree=%s", d.firstdef, d.lastuse, d.stm.String())
   202  }