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 }