github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/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  				p := b.Preds[i]
    32  				if p.Kind != BlockIf {
    33  					continue
    34  				}
    35  				if p.Control != a {
    36  					continue
    37  				}
    38  				if p.Succs[0] == b {
    39  					v.SetArg(i, ct)
    40  				} else {
    41  					v.SetArg(i, cf)
    42  				}
    43  			}
    44  		}
    45  	}
    46  
    47  	// Step 2: Compute which values are live across blocks.
    48  	live := make([]bool, f.NumValues())
    49  	for _, b := range f.Blocks {
    50  		for _, v := range b.Values {
    51  			for _, a := range v.Args {
    52  				if a.Block != v.Block {
    53  					live[a.ID] = true
    54  				}
    55  			}
    56  		}
    57  		if b.Control != nil && b.Control.Block != b {
    58  			live[b.Control.ID] = true
    59  		}
    60  	}
    61  
    62  	// Step 3: Redirect control flow around known branches.
    63  	// p:
    64  	//   ... goto b ...
    65  	// b: <- p ...
    66  	//   v = phi(true, ...)
    67  	//   if v goto t else u
    68  	// We can redirect p to go directly to t instead of b.
    69  	// (If v is not live after b).
    70  	for _, b := range f.Blocks {
    71  		if b.Kind != BlockIf {
    72  			continue
    73  		}
    74  		if len(b.Values) != 1 {
    75  			continue
    76  		}
    77  		v := b.Values[0]
    78  		if v.Op != OpPhi {
    79  			continue
    80  		}
    81  		if b.Control != v {
    82  			continue
    83  		}
    84  		if live[v.ID] {
    85  			continue
    86  		}
    87  		for i := 0; i < len(v.Args); i++ {
    88  			a := v.Args[i]
    89  			if a.Op != OpConstBool {
    90  				continue
    91  			}
    92  
    93  			// The predecessor we come in from.
    94  			p := b.Preds[i]
    95  			// The successor we always go to when coming in
    96  			// from that predecessor.
    97  			t := b.Succs[1-a.AuxInt]
    98  
    99  			// Change the edge p->b to p->t.
   100  			for j, x := range p.Succs {
   101  				if x == b {
   102  					p.Succs[j] = t
   103  					break
   104  				}
   105  			}
   106  
   107  			// Fix up t to have one more predecessor.
   108  			j := predIdx(t, b)
   109  			t.Preds = append(t.Preds, p)
   110  			for _, w := range t.Values {
   111  				if w.Op != OpPhi {
   112  					continue
   113  				}
   114  				w.AddArg(w.Args[j])
   115  			}
   116  
   117  			// Fix up b to have one less predecessor.
   118  			n := len(b.Preds) - 1
   119  			b.Preds[i] = b.Preds[n]
   120  			b.Preds[n] = nil
   121  			b.Preds = b.Preds[:n]
   122  			v.Args[i].Uses--
   123  			v.Args[i] = v.Args[n]
   124  			v.Args[n] = nil
   125  			v.Args = v.Args[:n]
   126  			if n == 1 {
   127  				v.Op = OpCopy
   128  				// No longer a phi, stop optimizing here.
   129  				break
   130  			}
   131  			i--
   132  		}
   133  	}
   134  }
   135  
   136  // predIdx returns the index where p appears in the predecessor list of b.
   137  // p must be in the predecessor list of b.
   138  func predIdx(b, p *Block) int {
   139  	for i, x := range b.Preds {
   140  		if x == p {
   141  			return i
   142  		}
   143  	}
   144  	panic("predecessor not found")
   145  }