github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/src/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  	ct := f.ConstBool(f.Entry.Line, f.Config.fe.TypeBool(), true)
    21  	cf := f.ConstBool(f.Entry.Line, f.Config.fe.TypeBool(), false)
    22  	for _, b := range f.Blocks {
    23  		for _, v := range b.Values {
    24  			if v.Op != OpPhi {
    25  				continue
    26  			}
    27  			if !v.Type.IsBoolean() {
    28  				continue
    29  			}
    30  			for i, a := range v.Args {
    31  				e := b.Preds[i]
    32  				p := e.b
    33  				if p.Kind != BlockIf {
    34  					continue
    35  				}
    36  				if p.Control != a {
    37  					continue
    38  				}
    39  				if e.i == 0 {
    40  					v.SetArg(i, ct)
    41  				} else {
    42  					v.SetArg(i, cf)
    43  				}
    44  			}
    45  		}
    46  	}
    47  
    48  	// Step 2: Compute which values are live across blocks.
    49  	live := make([]bool, f.NumValues())
    50  	for _, b := range f.Blocks {
    51  		for _, v := range b.Values {
    52  			for _, a := range v.Args {
    53  				if a.Block != v.Block {
    54  					live[a.ID] = true
    55  				}
    56  			}
    57  		}
    58  		if b.Control != nil && b.Control.Block != b {
    59  			live[b.Control.ID] = true
    60  		}
    61  	}
    62  
    63  	// Step 3: Redirect control flow around known branches.
    64  	// p:
    65  	//   ... goto b ...
    66  	// b: <- p ...
    67  	//   v = phi(true, ...)
    68  	//   if v goto t else u
    69  	// We can redirect p to go directly to t instead of b.
    70  	// (If v is not live after b).
    71  	for _, b := range f.Blocks {
    72  		if b.Kind != BlockIf {
    73  			continue
    74  		}
    75  		if len(b.Values) != 1 {
    76  			continue
    77  		}
    78  		v := b.Values[0]
    79  		if v.Op != OpPhi {
    80  			continue
    81  		}
    82  		if b.Control != v {
    83  			continue
    84  		}
    85  		if live[v.ID] {
    86  			continue
    87  		}
    88  		for i := 0; i < len(v.Args); i++ {
    89  			a := v.Args[i]
    90  			if a.Op != OpConstBool {
    91  				continue
    92  			}
    93  
    94  			// The predecessor we come in from.
    95  			e1 := b.Preds[i]
    96  			p := e1.b
    97  			pi := e1.i
    98  
    99  			// The successor we always go to when coming in
   100  			// from that predecessor.
   101  			e2 := b.Succs[1-a.AuxInt]
   102  			t := e2.b
   103  			ti := e2.i
   104  
   105  			// Remove b's incoming edge from p.
   106  			b.removePred(i)
   107  			n := len(b.Preds)
   108  			v.Args[i].Uses--
   109  			v.Args[i] = v.Args[n]
   110  			v.Args[n] = nil
   111  			v.Args = v.Args[:n]
   112  
   113  			// Redirect p's outgoing edge to t.
   114  			p.Succs[pi] = Edge{t, len(t.Preds)}
   115  
   116  			// Fix up t to have one more predecessor.
   117  			t.Preds = append(t.Preds, Edge{p, pi})
   118  			for _, w := range t.Values {
   119  				if w.Op != OpPhi {
   120  					continue
   121  				}
   122  				w.AddArg(w.Args[ti])
   123  			}
   124  
   125  			if len(b.Preds) == 1 {
   126  				v.Op = OpCopy
   127  				// No longer a phi, stop optimizing here.
   128  				break
   129  			}
   130  			i--
   131  		}
   132  	}
   133  }