github.com/bir3/gocompiler@v0.9.2202/src/cmd/compile/internal/ssagen/phi.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 ssagen
     6  
     7  import (
     8  	"container/heap"
     9  	"fmt"
    10  
    11  	"github.com/bir3/gocompiler/src/cmd/compile/internal/ir"
    12  	"github.com/bir3/gocompiler/src/cmd/compile/internal/ssa"
    13  	"github.com/bir3/gocompiler/src/cmd/compile/internal/types"
    14  	"github.com/bir3/gocompiler/src/cmd/internal/src"
    15  )
    16  
    17  // This file contains the algorithm to place phi nodes in a function.
    18  // For small functions, we use Braun, Buchwald, Hack, Leißa, Mallon, and Zwinkau.
    19  // https://pp.info.uni-karlsruhe.de/uploads/publikationen/braun13cc.pdf
    20  // For large functions, we use Sreedhar & Gao: A Linear Time Algorithm for Placing Φ-Nodes.
    21  // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.8.1979&rep=rep1&type=pdf
    22  
    23  const smallBlocks = 500
    24  
    25  const debugPhi = false
    26  
    27  // fwdRefAux wraps an arbitrary ir.Node as an ssa.Aux for use with OpFwdref.
    28  type fwdRefAux struct {
    29  	_	[0]func()	// ensure ir.Node isn't compared for equality
    30  	N	ir.Node
    31  }
    32  
    33  func (fwdRefAux) CanBeAnSSAAux()	{}
    34  
    35  // insertPhis finds all the places in the function where a phi is
    36  // necessary and inserts them.
    37  // Uses FwdRef ops to find all uses of variables, and s.defvars to find
    38  // all definitions.
    39  // Phi values are inserted, and all FwdRefs are changed to a Copy
    40  // of the appropriate phi or definition.
    41  // TODO: make this part of cmd/compile/internal/ssa somehow?
    42  func (s *state) insertPhis() {
    43  	if len(s.f.Blocks) <= smallBlocks {
    44  		sps := simplePhiState{s: s, f: s.f, defvars: s.defvars}
    45  		sps.insertPhis()
    46  		return
    47  	}
    48  	ps := phiState{s: s, f: s.f, defvars: s.defvars}
    49  	ps.insertPhis()
    50  }
    51  
    52  type phiState struct {
    53  	s	*state				// SSA state
    54  	f	*ssa.Func			// function to work on
    55  	defvars	[]map[ir.Node]*ssa.Value	// defined variables at end of each block
    56  
    57  	varnum	map[ir.Node]int32	// variable numbering
    58  
    59  	// properties of the dominator tree
    60  	idom	[]*ssa.Block	// dominator parents
    61  	tree	[]domBlock	// dominator child+sibling
    62  	level	[]int32		// level in dominator tree (0 = root or unreachable, 1 = children of root, ...)
    63  
    64  	// scratch locations
    65  	priq	blockHeap	// priority queue of blocks, higher level (toward leaves) = higher priority
    66  	q	[]*ssa.Block	// inner loop queue
    67  	queued	*sparseSet	// has been put in q
    68  	hasPhi	*sparseSet	// has a phi
    69  	hasDef	*sparseSet	// has a write of the variable we're processing
    70  
    71  	// miscellaneous
    72  	placeholder	*ssa.Value	// value to use as a "not set yet" placeholder.
    73  }
    74  
    75  func (s *phiState) insertPhis() {
    76  	if debugPhi {
    77  		fmt.Println(s.f.String())
    78  	}
    79  
    80  	// Find all the variables for which we need to match up reads & writes.
    81  	// This step prunes any basic-block-only variables from consideration.
    82  	// Generate a numbering for these variables.
    83  	s.varnum = map[ir.Node]int32{}
    84  	var vars []ir.Node
    85  	var vartypes []*types.Type
    86  	for _, b := range s.f.Blocks {
    87  		for _, v := range b.Values {
    88  			if v.Op != ssa.OpFwdRef {
    89  				continue
    90  			}
    91  			var_ := v.Aux.(fwdRefAux).N
    92  
    93  			// Optimization: look back 1 block for the definition.
    94  			if len(b.Preds) == 1 {
    95  				c := b.Preds[0].Block()
    96  				if w := s.defvars[c.ID][var_]; w != nil {
    97  					v.Op = ssa.OpCopy
    98  					v.Aux = nil
    99  					v.AddArg(w)
   100  					continue
   101  				}
   102  			}
   103  
   104  			if _, ok := s.varnum[var_]; ok {
   105  				continue
   106  			}
   107  			s.varnum[var_] = int32(len(vartypes))
   108  			if debugPhi {
   109  				fmt.Printf("var%d = %v\n", len(vartypes), var_)
   110  			}
   111  			vars = append(vars, var_)
   112  			vartypes = append(vartypes, v.Type)
   113  		}
   114  	}
   115  
   116  	if len(vartypes) == 0 {
   117  		return
   118  	}
   119  
   120  	// Find all definitions of the variables we need to process.
   121  	// defs[n] contains all the blocks in which variable number n is assigned.
   122  	defs := make([][]*ssa.Block, len(vartypes))
   123  	for _, b := range s.f.Blocks {
   124  		for var_ := range s.defvars[b.ID] {	// TODO: encode defvars some other way (explicit ops)? make defvars[n] a slice instead of a map.
   125  			if n, ok := s.varnum[var_]; ok {
   126  				defs[n] = append(defs[n], b)
   127  			}
   128  		}
   129  	}
   130  
   131  	// Make dominator tree.
   132  	s.idom = s.f.Idom()
   133  	s.tree = make([]domBlock, s.f.NumBlocks())
   134  	for _, b := range s.f.Blocks {
   135  		p := s.idom[b.ID]
   136  		if p != nil {
   137  			s.tree[b.ID].sibling = s.tree[p.ID].firstChild
   138  			s.tree[p.ID].firstChild = b
   139  		}
   140  	}
   141  	// Compute levels in dominator tree.
   142  	// With parent pointers we can do a depth-first walk without
   143  	// any auxiliary storage.
   144  	s.level = make([]int32, s.f.NumBlocks())
   145  	b := s.f.Entry
   146  levels:
   147  	for {
   148  		if p := s.idom[b.ID]; p != nil {
   149  			s.level[b.ID] = s.level[p.ID] + 1
   150  			if debugPhi {
   151  				fmt.Printf("level %s = %d\n", b, s.level[b.ID])
   152  			}
   153  		}
   154  		if c := s.tree[b.ID].firstChild; c != nil {
   155  			b = c
   156  			continue
   157  		}
   158  		for {
   159  			if c := s.tree[b.ID].sibling; c != nil {
   160  				b = c
   161  				continue levels
   162  			}
   163  			b = s.idom[b.ID]
   164  			if b == nil {
   165  				break levels
   166  			}
   167  		}
   168  	}
   169  
   170  	// Allocate scratch locations.
   171  	s.priq.level = s.level
   172  	s.q = make([]*ssa.Block, 0, s.f.NumBlocks())
   173  	s.queued = newSparseSet(s.f.NumBlocks())
   174  	s.hasPhi = newSparseSet(s.f.NumBlocks())
   175  	s.hasDef = newSparseSet(s.f.NumBlocks())
   176  	s.placeholder = s.s.entryNewValue0(ssa.OpUnknown, types.TypeInvalid)
   177  
   178  	// Generate phi ops for each variable.
   179  	for n := range vartypes {
   180  		s.insertVarPhis(n, vars[n], defs[n], vartypes[n])
   181  	}
   182  
   183  	// Resolve FwdRefs to the correct write or phi.
   184  	s.resolveFwdRefs()
   185  
   186  	// Erase variable numbers stored in AuxInt fields of phi ops. They are no longer needed.
   187  	for _, b := range s.f.Blocks {
   188  		for _, v := range b.Values {
   189  			if v.Op == ssa.OpPhi {
   190  				v.AuxInt = 0
   191  			}
   192  			// Any remaining FwdRefs are dead code.
   193  			if v.Op == ssa.OpFwdRef {
   194  				v.Op = ssa.OpUnknown
   195  				v.Aux = nil
   196  			}
   197  		}
   198  	}
   199  }
   200  
   201  func (s *phiState) insertVarPhis(n int, var_ ir.Node, defs []*ssa.Block, typ *types.Type) {
   202  	priq := &s.priq
   203  	q := s.q
   204  	queued := s.queued
   205  	queued.clear()
   206  	hasPhi := s.hasPhi
   207  	hasPhi.clear()
   208  	hasDef := s.hasDef
   209  	hasDef.clear()
   210  
   211  	// Add defining blocks to priority queue.
   212  	for _, b := range defs {
   213  		priq.a = append(priq.a, b)
   214  		hasDef.add(b.ID)
   215  		if debugPhi {
   216  			fmt.Printf("def of var%d in %s\n", n, b)
   217  		}
   218  	}
   219  	heap.Init(priq)
   220  
   221  	// Visit blocks defining variable n, from deepest to shallowest.
   222  	for len(priq.a) > 0 {
   223  		currentRoot := heap.Pop(priq).(*ssa.Block)
   224  		if debugPhi {
   225  			fmt.Printf("currentRoot %s\n", currentRoot)
   226  		}
   227  		// Walk subtree below definition.
   228  		// Skip subtrees we've done in previous iterations.
   229  		// Find edges exiting tree dominated by definition (the dominance frontier).
   230  		// Insert phis at target blocks.
   231  		if queued.contains(currentRoot.ID) {
   232  			s.s.Fatalf("root already in queue")
   233  		}
   234  		q = append(q, currentRoot)
   235  		queued.add(currentRoot.ID)
   236  		for len(q) > 0 {
   237  			b := q[len(q)-1]
   238  			q = q[:len(q)-1]
   239  			if debugPhi {
   240  				fmt.Printf("  processing %s\n", b)
   241  			}
   242  
   243  			currentRootLevel := s.level[currentRoot.ID]
   244  			for _, e := range b.Succs {
   245  				c := e.Block()
   246  				// TODO: if the variable is dead at c, skip it.
   247  				if s.level[c.ID] > currentRootLevel {
   248  					// a D-edge, or an edge whose target is in currentRoot's subtree.
   249  					continue
   250  				}
   251  				if hasPhi.contains(c.ID) {
   252  					continue
   253  				}
   254  				// Add a phi to block c for variable n.
   255  				hasPhi.add(c.ID)
   256  				v := c.NewValue0I(currentRoot.Pos, ssa.OpPhi, typ, int64(n))	// TODO: line number right?
   257  				// Note: we store the variable number in the phi's AuxInt field. Used temporarily by phi building.
   258  				if var_.Op() == ir.ONAME {
   259  					s.s.addNamedValue(var_.(*ir.Name), v)
   260  				}
   261  				for range c.Preds {
   262  					v.AddArg(s.placeholder)	// Actual args will be filled in by resolveFwdRefs.
   263  				}
   264  				if debugPhi {
   265  					fmt.Printf("new phi for var%d in %s: %s\n", n, c, v)
   266  				}
   267  				if !hasDef.contains(c.ID) {
   268  					// There's now a new definition of this variable in block c.
   269  					// Add it to the priority queue to explore.
   270  					heap.Push(priq, c)
   271  					hasDef.add(c.ID)
   272  				}
   273  			}
   274  
   275  			// Visit children if they have not been visited yet.
   276  			for c := s.tree[b.ID].firstChild; c != nil; c = s.tree[c.ID].sibling {
   277  				if !queued.contains(c.ID) {
   278  					q = append(q, c)
   279  					queued.add(c.ID)
   280  				}
   281  			}
   282  		}
   283  	}
   284  }
   285  
   286  // resolveFwdRefs links all FwdRef uses up to their nearest dominating definition.
   287  func (s *phiState) resolveFwdRefs() {
   288  	// Do a depth-first walk of the dominator tree, keeping track
   289  	// of the most-recently-seen value for each variable.
   290  
   291  	// Map from variable ID to SSA value at the current point of the walk.
   292  	values := make([]*ssa.Value, len(s.varnum))
   293  	for i := range values {
   294  		values[i] = s.placeholder
   295  	}
   296  
   297  	// Stack of work to do.
   298  	type stackEntry struct {
   299  		b	*ssa.Block	// block to explore
   300  
   301  		// variable/value pair to reinstate on exit
   302  		n	int32	// variable ID
   303  		v	*ssa.Value
   304  
   305  		// Note: only one of b or n,v will be set.
   306  	}
   307  	var stk []stackEntry
   308  
   309  	stk = append(stk, stackEntry{b: s.f.Entry})
   310  	for len(stk) > 0 {
   311  		work := stk[len(stk)-1]
   312  		stk = stk[:len(stk)-1]
   313  
   314  		b := work.b
   315  		if b == nil {
   316  			// On exit from a block, this case will undo any assignments done below.
   317  			values[work.n] = work.v
   318  			continue
   319  		}
   320  
   321  		// Process phis as new defs. They come before FwdRefs in this block.
   322  		for _, v := range b.Values {
   323  			if v.Op != ssa.OpPhi {
   324  				continue
   325  			}
   326  			n := int32(v.AuxInt)
   327  			// Remember the old assignment so we can undo it when we exit b.
   328  			stk = append(stk, stackEntry{n: n, v: values[n]})
   329  			// Record the new assignment.
   330  			values[n] = v
   331  		}
   332  
   333  		// Replace a FwdRef op with the current incoming value for its variable.
   334  		for _, v := range b.Values {
   335  			if v.Op != ssa.OpFwdRef {
   336  				continue
   337  			}
   338  			n := s.varnum[v.Aux.(fwdRefAux).N]
   339  			v.Op = ssa.OpCopy
   340  			v.Aux = nil
   341  			v.AddArg(values[n])
   342  		}
   343  
   344  		// Establish values for variables defined in b.
   345  		for var_, v := range s.defvars[b.ID] {
   346  			n, ok := s.varnum[var_]
   347  			if !ok {
   348  				// some variable not live across a basic block boundary.
   349  				continue
   350  			}
   351  			// Remember the old assignment so we can undo it when we exit b.
   352  			stk = append(stk, stackEntry{n: n, v: values[n]})
   353  			// Record the new assignment.
   354  			values[n] = v
   355  		}
   356  
   357  		// Replace phi args in successors with the current incoming value.
   358  		for _, e := range b.Succs {
   359  			c, i := e.Block(), e.Index()
   360  			for j := len(c.Values) - 1; j >= 0; j-- {
   361  				v := c.Values[j]
   362  				if v.Op != ssa.OpPhi {
   363  					break	// All phis will be at the end of the block during phi building.
   364  				}
   365  				// Only set arguments that have been resolved.
   366  				// For very wide CFGs, this significantly speeds up phi resolution.
   367  				// See golang.org/issue/8225.
   368  				if w := values[v.AuxInt]; w.Op != ssa.OpUnknown {
   369  					v.SetArg(i, w)
   370  				}
   371  			}
   372  		}
   373  
   374  		// Walk children in dominator tree.
   375  		for c := s.tree[b.ID].firstChild; c != nil; c = s.tree[c.ID].sibling {
   376  			stk = append(stk, stackEntry{b: c})
   377  		}
   378  	}
   379  }
   380  
   381  // domBlock contains extra per-block information to record the dominator tree.
   382  type domBlock struct {
   383  	firstChild	*ssa.Block	// first child of block in dominator tree
   384  	sibling		*ssa.Block	// next child of parent in dominator tree
   385  }
   386  
   387  // A block heap is used as a priority queue to implement the PiggyBank
   388  // from Sreedhar and Gao.  That paper uses an array which is better
   389  // asymptotically but worse in the common case when the PiggyBank
   390  // holds a sparse set of blocks.
   391  type blockHeap struct {
   392  	a	[]*ssa.Block	// block IDs in heap
   393  	level	[]int32		// depth in dominator tree (static, used for determining priority)
   394  }
   395  
   396  func (h *blockHeap) Len() int		{ return len(h.a) }
   397  func (h *blockHeap) Swap(i, j int)	{ a := h.a; a[i], a[j] = a[j], a[i] }
   398  
   399  func (h *blockHeap) Push(x interface{}) {
   400  	v := x.(*ssa.Block)
   401  	h.a = append(h.a, v)
   402  }
   403  func (h *blockHeap) Pop() interface{} {
   404  	old := h.a
   405  	n := len(old)
   406  	x := old[n-1]
   407  	h.a = old[:n-1]
   408  	return x
   409  }
   410  func (h *blockHeap) Less(i, j int) bool {
   411  	return h.level[h.a[i].ID] > h.level[h.a[j].ID]
   412  }
   413  
   414  // TODO: stop walking the iterated domininance frontier when
   415  // the variable is dead. Maybe detect that by checking if the
   416  // node we're on is reverse dominated by all the reads?
   417  // Reverse dominated by the highest common successor of all the reads?
   418  
   419  // copy of ../ssa/sparseset.go
   420  // TODO: move this file to ../ssa, then use sparseSet there.
   421  type sparseSet struct {
   422  	dense	[]ssa.ID
   423  	sparse	[]int32
   424  }
   425  
   426  // newSparseSet returns a sparseSet that can represent
   427  // integers between 0 and n-1.
   428  func newSparseSet(n int) *sparseSet {
   429  	return &sparseSet{dense: nil, sparse: make([]int32, n)}
   430  }
   431  
   432  func (s *sparseSet) contains(x ssa.ID) bool {
   433  	i := s.sparse[x]
   434  	return i < int32(len(s.dense)) && s.dense[i] == x
   435  }
   436  
   437  func (s *sparseSet) add(x ssa.ID) {
   438  	i := s.sparse[x]
   439  	if i < int32(len(s.dense)) && s.dense[i] == x {
   440  		return
   441  	}
   442  	s.dense = append(s.dense, x)
   443  	s.sparse[x] = int32(len(s.dense)) - 1
   444  }
   445  
   446  func (s *sparseSet) clear() {
   447  	s.dense = s.dense[:0]
   448  }
   449  
   450  // Variant to use for small functions.
   451  type simplePhiState struct {
   452  	s		*state				// SSA state
   453  	f		*ssa.Func			// function to work on
   454  	fwdrefs		[]*ssa.Value			// list of FwdRefs to be processed
   455  	defvars		[]map[ir.Node]*ssa.Value	// defined variables at end of each block
   456  	reachable	[]bool				// which blocks are reachable
   457  }
   458  
   459  func (s *simplePhiState) insertPhis() {
   460  	s.reachable = ssa.ReachableBlocks(s.f)
   461  
   462  	// Find FwdRef ops.
   463  	for _, b := range s.f.Blocks {
   464  		for _, v := range b.Values {
   465  			if v.Op != ssa.OpFwdRef {
   466  				continue
   467  			}
   468  			s.fwdrefs = append(s.fwdrefs, v)
   469  			var_ := v.Aux.(fwdRefAux).N
   470  			if _, ok := s.defvars[b.ID][var_]; !ok {
   471  				s.defvars[b.ID][var_] = v	// treat FwdDefs as definitions.
   472  			}
   473  		}
   474  	}
   475  
   476  	var args []*ssa.Value
   477  
   478  loop:
   479  	for len(s.fwdrefs) > 0 {
   480  		v := s.fwdrefs[len(s.fwdrefs)-1]
   481  		s.fwdrefs = s.fwdrefs[:len(s.fwdrefs)-1]
   482  		b := v.Block
   483  		var_ := v.Aux.(fwdRefAux).N
   484  		if b == s.f.Entry {
   485  			// No variable should be live at entry.
   486  			s.s.Fatalf("value %v (%v) incorrectly live at entry", var_, v)
   487  		}
   488  		if !s.reachable[b.ID] {
   489  			// This block is dead.
   490  			// It doesn't matter what we use here as long as it is well-formed.
   491  			v.Op = ssa.OpUnknown
   492  			v.Aux = nil
   493  			continue
   494  		}
   495  		// Find variable value on each predecessor.
   496  		args = args[:0]
   497  		for _, e := range b.Preds {
   498  			args = append(args, s.lookupVarOutgoing(e.Block(), v.Type, var_, v.Pos))
   499  		}
   500  
   501  		// Decide if we need a phi or not. We need a phi if there
   502  		// are two different args (which are both not v).
   503  		var w *ssa.Value
   504  		for _, a := range args {
   505  			if a == v {
   506  				continue	// self-reference
   507  			}
   508  			if a == w {
   509  				continue	// already have this witness
   510  			}
   511  			if w != nil {
   512  				// two witnesses, need a phi value
   513  				v.Op = ssa.OpPhi
   514  				v.AddArgs(args...)
   515  				v.Aux = nil
   516  				continue loop
   517  			}
   518  			w = a	// save witness
   519  		}
   520  		if w == nil {
   521  			s.s.Fatalf("no witness for reachable phi %s", v)
   522  		}
   523  		// One witness. Make v a copy of w.
   524  		v.Op = ssa.OpCopy
   525  		v.Aux = nil
   526  		v.AddArg(w)
   527  	}
   528  }
   529  
   530  // lookupVarOutgoing finds the variable's value at the end of block b.
   531  func (s *simplePhiState) lookupVarOutgoing(b *ssa.Block, t *types.Type, var_ ir.Node, line src.XPos) *ssa.Value {
   532  	for {
   533  		if v := s.defvars[b.ID][var_]; v != nil {
   534  			return v
   535  		}
   536  		// The variable is not defined by b and we haven't looked it up yet.
   537  		// If b has exactly one predecessor, loop to look it up there.
   538  		// Otherwise, give up and insert a new FwdRef and resolve it later.
   539  		if len(b.Preds) != 1 {
   540  			break
   541  		}
   542  		b = b.Preds[0].Block()
   543  		if !s.reachable[b.ID] {
   544  			// This is rare; it happens with oddly interleaved infinite loops in dead code.
   545  			// See issue 19783.
   546  			break
   547  		}
   548  	}
   549  	// Generate a FwdRef for the variable and return that.
   550  	v := b.NewValue0A(line, ssa.OpFwdRef, t, fwdRefAux{N: var_})
   551  	s.defvars[b.ID][var_] = v
   552  	if var_.Op() == ir.ONAME {
   553  		s.s.addNamedValue(var_.(*ir.Name), v)
   554  	}
   555  	s.fwdrefs = append(s.fwdrefs, v)
   556  	return v
   557  }