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 } 134