github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/cmd/compile/internal/ssa/critical.go (about)

     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  // critical splits critical edges (those that go from a block with
     8  // more than one outedge to a block with more than one inedge).
     9  // Regalloc wants a critical-edge-free CFG so it can implement phi values.
    10  func critical(f *Func) {
    11  	// maps from phi arg ID to the new block created for that argument
    12  	blocks := make([]*Block, f.NumValues())
    13  	// need to iterate over f.Blocks without range, as we might
    14  	// need to split critical edges on newly constructed blocks
    15  	for j := 0; j < len(f.Blocks); j++ {
    16  		b := f.Blocks[j]
    17  		if len(b.Preds) <= 1 {
    18  			continue
    19  		}
    20  
    21  		var phi *Value
    22  		// determine if we've only got a single phi in this
    23  		// block, this is easier to handle than the general
    24  		// case of a block with multiple phi values.
    25  		for _, v := range b.Values {
    26  			if v.Op == OpPhi {
    27  				if phi != nil {
    28  					phi = nil
    29  					break
    30  				}
    31  				phi = v
    32  			}
    33  		}
    34  
    35  		// reset our block map
    36  		if phi != nil {
    37  			for _, v := range phi.Args {
    38  				blocks[v.ID] = nil
    39  			}
    40  		}
    41  
    42  		// split input edges coming from multi-output blocks.
    43  		for i := 0; i < len(b.Preds); i++ {
    44  			c := b.Preds[i]
    45  			if c.Kind == BlockPlain {
    46  				continue // only single output block
    47  			}
    48  
    49  			var d *Block         // new block used to remove critical edge
    50  			reusedBlock := false // if true, then this is not the first use of this block
    51  			if phi != nil {
    52  				argID := phi.Args[i].ID
    53  				// find or record the block that we used to split
    54  				// critical edges for this argument
    55  				if d = blocks[argID]; d == nil {
    56  					// splitting doesn't necessarily remove the critical edge,
    57  					// since we're iterating over len(f.Blocks) above, this forces
    58  					// the new blocks to be re-examined.
    59  					d = f.NewBlock(BlockPlain)
    60  					d.Line = c.Line
    61  					blocks[argID] = d
    62  					if f.pass.debug > 0 {
    63  						f.Config.Warnl(c.Line, "split critical edge")
    64  					}
    65  				} else {
    66  					reusedBlock = true
    67  				}
    68  			} else {
    69  				// no existing block, so allocate a new block
    70  				// to place on the edge
    71  				d = f.NewBlock(BlockPlain)
    72  				d.Line = c.Line
    73  				if f.pass.debug > 0 {
    74  					f.Config.Warnl(c.Line, "split critical edge")
    75  				}
    76  			}
    77  
    78  			// if this not the first argument for the
    79  			// block, then we need to remove the
    80  			// corresponding elements from the block
    81  			// predecessors and phi args
    82  			if reusedBlock {
    83  				d.Preds = append(d.Preds, c)
    84  				b.Preds[i] = nil
    85  				phi.Args[i].Uses--
    86  				phi.Args[i] = nil
    87  			} else {
    88  				// splice it in
    89  				d.Preds = append(d.Preds, c)
    90  				d.Succs = append(d.Succs, b)
    91  				b.Preds[i] = d
    92  			}
    93  
    94  			// replace b with d in c's successor list.
    95  			for j, b2 := range c.Succs {
    96  				if b2 == b {
    97  					c.Succs[j] = d
    98  					break
    99  				}
   100  			}
   101  		}
   102  
   103  		// clean up phi's args and b's predecessor list
   104  		if phi != nil {
   105  			phi.Args = filterNilValues(phi.Args)
   106  			b.Preds = filterNilBlocks(b.Preds)
   107  			// splitting occasionally leads to a phi having
   108  			// a single argument (occurs with -N)
   109  			if len(phi.Args) == 1 {
   110  				phi.Op = OpCopy
   111  			}
   112  		}
   113  	}
   114  }
   115  
   116  // filterNilValues preserves the order of v, while filtering out nils.
   117  func filterNilValues(v []*Value) []*Value {
   118  	nv := v[:0]
   119  	for i := range v {
   120  		if v[i] != nil {
   121  			nv = append(nv, v[i])
   122  		}
   123  	}
   124  	return nv
   125  }
   126  
   127  // filterNilBlocks preserves the order of b, while filtering out nils.
   128  func filterNilBlocks(b []*Block) []*Block {
   129  	nb := b[:0]
   130  	for i := range b {
   131  		if b[i] != nil {
   132  			nb = append(nb, b[i])
   133  		}
   134  	}
   135  	return nb
   136  }