github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/compile/internal/ssa/shortcircuit.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  // Shortcircuit finds situations where branch directions
     8  // are always correlated and rewrites the CFG to take
     9  // advantage of that fact.
    10  // This optimization is useful for compiling && and || expressions.
    11  func shortcircuit(f *Func) {
    12  	// Step 1: Replace a phi arg with a constant if that arg
    13  	// is the control value of a preceding If block.
    14  	// b1:
    15  	//    If a goto b2 else b3
    16  	// b2: <- b1 ...
    17  	//    x = phi(a, ...)
    18  	//
    19  	// We can replace the "a" in the phi with the constant true.
    20  	var ct, cf *Value
    21  	for _, b := range f.Blocks {
    22  		for _, v := range b.Values {
    23  			if v.Op != OpPhi {
    24  				continue
    25  			}
    26  			if !v.Type.IsBoolean() {
    27  				continue
    28  			}
    29  			for i, a := range v.Args {
    30  				e := b.Preds[i]
    31  				p := e.b
    32  				if p.Kind != BlockIf {
    33  					continue
    34  				}
    35  				if p.Controls[0] != a {
    36  					continue
    37  				}
    38  				if e.i == 0 {
    39  					if ct == nil {
    40  						ct = f.ConstBool(f.Config.Types.Bool, true)
    41  					}
    42  					v.SetArg(i, ct)
    43  				} else {
    44  					if cf == nil {
    45  						cf = f.ConstBool(f.Config.Types.Bool, false)
    46  					}
    47  					v.SetArg(i, cf)
    48  				}
    49  			}
    50  		}
    51  	}
    52  
    53  	// Step 2: Redirect control flow around known branches.
    54  	// p:
    55  	//   ... goto b ...
    56  	// b: <- p ...
    57  	//   v = phi(true, ...)
    58  	//   if v goto t else u
    59  	// We can redirect p to go directly to t instead of b.
    60  	// (If v is not live after b).
    61  	for changed := true; changed; {
    62  		changed = false
    63  		for i := len(f.Blocks) - 1; i >= 0; i-- {
    64  			b := f.Blocks[i]
    65  			if fuseBlockPlain(b) {
    66  				changed = true
    67  				continue
    68  			}
    69  			changed = shortcircuitBlock(b) || changed
    70  		}
    71  		if changed {
    72  			f.invalidateCFG()
    73  		}
    74  	}
    75  }
    76  
    77  // shortcircuitBlock checks for a CFG of the form
    78  //
    79  //   p   other pred(s)
    80  //    \ /
    81  //     b
    82  //    / \
    83  //   s   other succ
    84  //
    85  // in which b is an If block containing a single phi value with a single use,
    86  // which has a ConstBool arg.
    87  // The only use of the phi value must be the control value of b.
    88  // p is the predecessor determined by the argument slot in which the ConstBool is found.
    89  //
    90  // It rewrites this into
    91  //
    92  //   p   other pred(s)
    93  //   |  /
    94  //   | b
    95  //   |/ \
    96  //   s   other succ
    97  //
    98  // and removes the appropriate phi arg(s).
    99  func shortcircuitBlock(b *Block) bool {
   100  	if b.Kind != BlockIf {
   101  		return false
   102  	}
   103  	// Look for control values of the form Copy(Not(Copy(Phi(const, ...)))).
   104  	// Those must be the only values in the b, and they each must be used only by b.
   105  	// Track the negations so that we can swap successors as needed later.
   106  	v := b.Controls[0]
   107  	nval := 1 // the control value
   108  	swap := false
   109  	for v.Uses == 1 && v.Block == b && (v.Op == OpCopy || v.Op == OpNot) {
   110  		if v.Op == OpNot {
   111  			swap = !swap
   112  		}
   113  		v = v.Args[0]
   114  		nval++ // wrapper around control value
   115  	}
   116  	if len(b.Values) != nval || v.Op != OpPhi || v.Block != b || v.Uses != 1 {
   117  		return false
   118  	}
   119  
   120  	// Check for const phi args.
   121  	var changed bool
   122  	for i := 0; i < len(v.Args); i++ {
   123  		a := v.Args[i]
   124  		if a.Op != OpConstBool {
   125  			continue
   126  		}
   127  		// The predecessor we come in from.
   128  		e1 := b.Preds[i]
   129  		p := e1.b
   130  		pi := e1.i
   131  
   132  		// The successor we always go to when coming in
   133  		// from that predecessor.
   134  		si := 1 - a.AuxInt
   135  		if swap {
   136  			si = 1 - si
   137  		}
   138  		e2 := b.Succs[si]
   139  		t := e2.b
   140  		if p == b || t == b {
   141  			// This is an infinite loop; we can't remove it. See issue 33903.
   142  			continue
   143  		}
   144  		ti := e2.i
   145  
   146  		// Update CFG and Phis.
   147  		changed = true
   148  
   149  		// Remove b's incoming edge from p.
   150  		b.removePred(i)
   151  		n := len(b.Preds)
   152  		v.Args[i].Uses--
   153  		v.Args[i] = v.Args[n]
   154  		v.Args[n] = nil
   155  		v.Args = v.Args[:n]
   156  
   157  		// Redirect p's outgoing edge to t.
   158  		p.Succs[pi] = Edge{t, len(t.Preds)}
   159  
   160  		// Fix up t to have one more predecessor.
   161  		t.Preds = append(t.Preds, Edge{p, pi})
   162  		for _, w := range t.Values {
   163  			if w.Op != OpPhi {
   164  				continue
   165  			}
   166  			w.AddArg(w.Args[ti])
   167  		}
   168  		i--
   169  	}
   170  
   171  	if !changed {
   172  		return false
   173  	}
   174  
   175  	if len(b.Preds) == 0 {
   176  		// Block is now dead.
   177  		b.Kind = BlockInvalid
   178  		return true
   179  	}
   180  
   181  	phielimValue(v)
   182  	return true
   183  }