github.com/euank/go@v0.0.0-20160829210321-495514729181/src/cmd/compile/internal/ssa/phiopt.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 // phiopt eliminates boolean Phis based on the previous if. 8 // 9 // Main use case is to transform: 10 // x := false 11 // if b { 12 // x = true 13 // } 14 // into x = b. 15 // 16 // In SSA code this appears as 17 // 18 // b0 19 // If b -> b1 b2 20 // b1 21 // Plain -> b2 22 // b2 23 // x = (OpPhi (ConstBool [true]) (ConstBool [false])) 24 // 25 // In this case we can replace x with a copy of b. 26 func phiopt(f *Func) { 27 for _, b := range f.Blocks { 28 if len(b.Preds) != 2 || len(b.Values) == 0 { 29 // TODO: handle more than 2 predecessors, e.g. a || b || c. 30 continue 31 } 32 33 pb0, b0 := b, b.Preds[0].b 34 for len(b0.Succs) == 1 && len(b0.Preds) == 1 { 35 pb0, b0 = b0, b0.Preds[0].b 36 } 37 if b0.Kind != BlockIf { 38 continue 39 } 40 pb1, b1 := b, b.Preds[1].b 41 for len(b1.Succs) == 1 && len(b1.Preds) == 1 { 42 pb1, b1 = b1, b1.Preds[0].b 43 } 44 if b1 != b0 { 45 continue 46 } 47 // b0 is the if block giving the boolean value. 48 49 // reverse is the predecessor from which the truth value comes. 50 var reverse int 51 if b0.Succs[0].b == pb0 && b0.Succs[1].b == pb1 { 52 reverse = 0 53 } else if b0.Succs[0].b == pb1 && b0.Succs[1].b == pb0 { 54 reverse = 1 55 } else { 56 b.Fatalf("invalid predecessors\n") 57 } 58 59 for _, v := range b.Values { 60 if v.Op != OpPhi { 61 continue 62 } 63 64 // Look for conversions from bool to 0/1. 65 if v.Type.IsInteger() { 66 phioptint(v, b0, reverse) 67 } 68 69 if !v.Type.IsBoolean() { 70 continue 71 } 72 73 // Replaces 74 // if a { x = true } else { x = false } with x = a 75 // and 76 // if a { x = false } else { x = true } with x = !a 77 if v.Args[0].Op == OpConstBool && v.Args[1].Op == OpConstBool { 78 if v.Args[reverse].AuxInt != v.Args[1-reverse].AuxInt { 79 ops := [2]Op{OpNot, OpCopy} 80 v.reset(ops[v.Args[reverse].AuxInt]) 81 v.AddArg(b0.Control) 82 if f.pass.debug > 0 { 83 f.Config.Warnl(b.Line, "converted OpPhi to %v", v.Op) 84 } 85 continue 86 } 87 } 88 89 // Replaces 90 // if a { x = true } else { x = value } with x = a || value. 91 // Requires that value dominates x, meaning that regardless of a, 92 // value is always computed. This guarantees that the side effects 93 // of value are not seen if a is false. 94 if v.Args[reverse].Op == OpConstBool && v.Args[reverse].AuxInt == 1 { 95 if tmp := v.Args[1-reverse]; f.sdom.isAncestorEq(tmp.Block, b) { 96 v.reset(OpOrB) 97 v.SetArgs2(b0.Control, tmp) 98 if f.pass.debug > 0 { 99 f.Config.Warnl(b.Line, "converted OpPhi to %v", v.Op) 100 } 101 continue 102 } 103 } 104 105 // Replaces 106 // if a { x = value } else { x = false } with x = a && value. 107 // Requires that value dominates x, meaning that regardless of a, 108 // value is always computed. This guarantees that the side effects 109 // of value are not seen if a is false. 110 if v.Args[1-reverse].Op == OpConstBool && v.Args[1-reverse].AuxInt == 0 { 111 if tmp := v.Args[reverse]; f.sdom.isAncestorEq(tmp.Block, b) { 112 v.reset(OpAndB) 113 v.SetArgs2(b0.Control, tmp) 114 if f.pass.debug > 0 { 115 f.Config.Warnl(b.Line, "converted OpPhi to %v", v.Op) 116 } 117 continue 118 } 119 } 120 } 121 } 122 } 123 124 func phioptint(v *Value, b0 *Block, reverse int) { 125 a0 := v.Args[0] 126 a1 := v.Args[1] 127 if a0.Op != a1.Op { 128 return 129 } 130 131 switch a0.Op { 132 case OpConst8, OpConst16, OpConst32, OpConst64: 133 default: 134 return 135 } 136 137 negate := false 138 switch { 139 case a0.AuxInt == 0 && a1.AuxInt == 1: 140 negate = true 141 case a0.AuxInt == 1 && a1.AuxInt == 0: 142 default: 143 return 144 } 145 146 if reverse == 1 { 147 negate = !negate 148 } 149 150 switch v.Type.Size() { 151 case 1: 152 v.reset(OpCopy) 153 case 2: 154 v.reset(OpZeroExt8to16) 155 case 4: 156 v.reset(OpZeroExt8to32) 157 case 8: 158 v.reset(OpZeroExt8to64) 159 default: 160 v.Fatalf("bad int size %d", v.Type.Size()) 161 } 162 163 a := b0.Control 164 if negate { 165 a = v.Block.NewValue1(v.Line, OpNot, a.Type, a) 166 } 167 v.AddArg(a) 168 169 f := b0.Func 170 if f.pass.debug > 0 { 171 f.Config.Warnl(v.Block.Line, "converted OpPhi bool -> int%d", v.Type.Size()*8) 172 } 173 }