github.com/bir3/gocompiler@v0.9.2202/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 // 11 // x := false 12 // if b { 13 // x = true 14 // } 15 // 16 // into x = b. 17 // 18 // In SSA code this appears as 19 // 20 // b0 21 // If b -> b1 b2 22 // b1 23 // Plain -> b2 24 // b2 25 // x = (OpPhi (ConstBool [true]) (ConstBool [false])) 26 // 27 // In this case we can replace x with a copy of b. 28 func phiopt(f *Func) { 29 sdom := f.Sdom() 30 for _, b := range f.Blocks { 31 if len(b.Preds) != 2 || len(b.Values) == 0 { 32 // TODO: handle more than 2 predecessors, e.g. a || b || c. 33 continue 34 } 35 36 pb0, b0 := b, b.Preds[0].b 37 for len(b0.Succs) == 1 && len(b0.Preds) == 1 { 38 pb0, b0 = b0, b0.Preds[0].b 39 } 40 if b0.Kind != BlockIf { 41 continue 42 } 43 pb1, b1 := b, b.Preds[1].b 44 for len(b1.Succs) == 1 && len(b1.Preds) == 1 { 45 pb1, b1 = b1, b1.Preds[0].b 46 } 47 if b1 != b0 { 48 continue 49 } 50 // b0 is the if block giving the boolean value. 51 // reverse is the predecessor from which the truth value comes. 52 var reverse int 53 if b0.Succs[0].b == pb0 && b0.Succs[1].b == pb1 { 54 reverse = 0 55 } else if b0.Succs[0].b == pb1 && b0.Succs[1].b == pb0 { 56 reverse = 1 57 } else { 58 b.Fatalf("invalid predecessors\n") 59 } 60 61 for _, v := range b.Values { 62 if v.Op != OpPhi { 63 continue 64 } 65 66 // Look for conversions from bool to 0/1. 67 if v.Type.IsInteger() { 68 phioptint(v, b0, reverse) 69 } 70 71 if !v.Type.IsBoolean() { 72 continue 73 } 74 75 // Replaces 76 // if a { x = true } else { x = false } with x = a 77 // and 78 // if a { x = false } else { x = true } with x = !a 79 if v.Args[0].Op == OpConstBool && v.Args[1].Op == OpConstBool { 80 if v.Args[reverse].AuxInt != v.Args[1-reverse].AuxInt { 81 ops := [2]Op{OpNot, OpCopy} 82 v.reset(ops[v.Args[reverse].AuxInt]) 83 v.AddArg(b0.Controls[0]) 84 if f.pass.debug > 0 { 85 f.Warnl(b.Pos, "converted OpPhi to %v", v.Op) 86 } 87 continue 88 } 89 } 90 91 // Replaces 92 // if a { x = true } else { x = value } with x = a || value. 93 // Requires that value dominates x, meaning that regardless of a, 94 // value is always computed. This guarantees that the side effects 95 // of value are not seen if a is false. 96 if v.Args[reverse].Op == OpConstBool && v.Args[reverse].AuxInt == 1 { 97 if tmp := v.Args[1-reverse]; sdom.IsAncestorEq(tmp.Block, b) { 98 v.reset(OpOrB) 99 v.SetArgs2(b0.Controls[0], tmp) 100 if f.pass.debug > 0 { 101 f.Warnl(b.Pos, "converted OpPhi to %v", v.Op) 102 } 103 continue 104 } 105 } 106 107 // Replaces 108 // if a { x = value } else { x = false } with x = a && value. 109 // Requires that value dominates x, meaning that regardless of a, 110 // value is always computed. This guarantees that the side effects 111 // of value are not seen if a is false. 112 if v.Args[1-reverse].Op == OpConstBool && v.Args[1-reverse].AuxInt == 0 { 113 if tmp := v.Args[reverse]; sdom.IsAncestorEq(tmp.Block, b) { 114 v.reset(OpAndB) 115 v.SetArgs2(b0.Controls[0], tmp) 116 if f.pass.debug > 0 { 117 f.Warnl(b.Pos, "converted OpPhi to %v", v.Op) 118 } 119 continue 120 } 121 } 122 } 123 } 124 // strengthen phi optimization. 125 // Main use case is to transform: 126 // x := false 127 // if c { 128 // x = true 129 // ... 130 // } 131 // into 132 // x := c 133 // if x { ... } 134 // 135 // For example, in SSA code a case appears as 136 // b0 137 // If c -> b, sb0 138 // sb0 139 // If d -> sd0, sd1 140 // sd1 141 // ... 142 // sd0 143 // Plain -> b 144 // b 145 // x = (OpPhi (ConstBool [true]) (ConstBool [false])) 146 // 147 // In this case we can also replace x with a copy of c. 148 // 149 // The optimization idea: 150 // 1. block b has a phi value x, x = OpPhi (ConstBool [true]) (ConstBool [false]), 151 // and len(b.Preds) is equal to 2. 152 // 2. find the common dominator(b0) of the predecessors(pb0, pb1) of block b, and the 153 // dominator(b0) is a If block. 154 // Special case: one of the predecessors(pb0 or pb1) is the dominator(b0). 155 // 3. the successors(sb0, sb1) of the dominator need to dominate the predecessors(pb0, pb1) 156 // of block b respectively. 157 // 4. replace this boolean Phi based on dominator block. 158 // 159 // b0(pb0) b0(pb1) b0 160 // | \ / | / \ 161 // | sb1 sb0 | sb0 sb1 162 // | ... ... | ... ... 163 // | pb1 pb0 | pb0 pb1 164 // | / \ | \ / 165 // b b b 166 // 167 var lca *lcaRange 168 for _, b := range f.Blocks { 169 if len(b.Preds) != 2 || len(b.Values) == 0 { 170 // TODO: handle more than 2 predecessors, e.g. a || b || c. 171 continue 172 } 173 174 for _, v := range b.Values { 175 // find a phi value v = OpPhi (ConstBool [true]) (ConstBool [false]). 176 // TODO: v = OpPhi (ConstBool [true]) (Arg <bool> {value}) 177 if v.Op != OpPhi { 178 continue 179 } 180 if v.Args[0].Op != OpConstBool || v.Args[1].Op != OpConstBool { 181 continue 182 } 183 if v.Args[0].AuxInt == v.Args[1].AuxInt { 184 continue 185 } 186 187 pb0 := b.Preds[0].b 188 pb1 := b.Preds[1].b 189 if pb0.Kind == BlockIf && pb0 == sdom.Parent(b) { 190 // special case: pb0 is the dominator block b0. 191 // b0(pb0) 192 // | \ 193 // | sb1 194 // | ... 195 // | pb1 196 // | / 197 // b 198 // if another successor sb1 of b0(pb0) dominates pb1, do replace. 199 ei := b.Preds[0].i 200 sb1 := pb0.Succs[1-ei].b 201 if sdom.IsAncestorEq(sb1, pb1) { 202 convertPhi(pb0, v, ei) 203 break 204 } 205 } else if pb1.Kind == BlockIf && pb1 == sdom.Parent(b) { 206 // special case: pb1 is the dominator block b0. 207 // b0(pb1) 208 // / | 209 // sb0 | 210 // ... | 211 // pb0 | 212 // \ | 213 // b 214 // if another successor sb0 of b0(pb0) dominates pb0, do replace. 215 ei := b.Preds[1].i 216 sb0 := pb1.Succs[1-ei].b 217 if sdom.IsAncestorEq(sb0, pb0) { 218 convertPhi(pb1, v, 1-ei) 219 break 220 } 221 } else { 222 // b0 223 // / \ 224 // sb0 sb1 225 // ... ... 226 // pb0 pb1 227 // \ / 228 // b 229 // 230 // Build data structure for fast least-common-ancestor queries. 231 if lca == nil { 232 lca = makeLCArange(f) 233 } 234 b0 := lca.find(pb0, pb1) 235 if b0.Kind != BlockIf { 236 break 237 } 238 sb0 := b0.Succs[0].b 239 sb1 := b0.Succs[1].b 240 var reverse int 241 if sdom.IsAncestorEq(sb0, pb0) && sdom.IsAncestorEq(sb1, pb1) { 242 reverse = 0 243 } else if sdom.IsAncestorEq(sb1, pb0) && sdom.IsAncestorEq(sb0, pb1) { 244 reverse = 1 245 } else { 246 break 247 } 248 if len(sb0.Preds) != 1 || len(sb1.Preds) != 1 { 249 // we can not replace phi value x in the following case. 250 // if gp == nil || sp < lo { x = true} 251 // if a || b { x = true } 252 // so the if statement can only have one condition. 253 break 254 } 255 convertPhi(b0, v, reverse) 256 } 257 } 258 } 259 } 260 261 func phioptint(v *Value, b0 *Block, reverse int) { 262 a0 := v.Args[0] 263 a1 := v.Args[1] 264 if a0.Op != a1.Op { 265 return 266 } 267 268 switch a0.Op { 269 case OpConst8, OpConst16, OpConst32, OpConst64: 270 default: 271 return 272 } 273 274 negate := false 275 switch { 276 case a0.AuxInt == 0 && a1.AuxInt == 1: 277 negate = true 278 case a0.AuxInt == 1 && a1.AuxInt == 0: 279 default: 280 return 281 } 282 283 if reverse == 1 { 284 negate = !negate 285 } 286 287 a := b0.Controls[0] 288 if negate { 289 a = v.Block.NewValue1(v.Pos, OpNot, a.Type, a) 290 } 291 v.AddArg(a) 292 293 cvt := v.Block.NewValue1(v.Pos, OpCvtBoolToUint8, v.Block.Func.Config.Types.UInt8, a) 294 switch v.Type.Size() { 295 case 1: 296 v.reset(OpCopy) 297 case 2: 298 v.reset(OpZeroExt8to16) 299 case 4: 300 v.reset(OpZeroExt8to32) 301 case 8: 302 v.reset(OpZeroExt8to64) 303 default: 304 v.Fatalf("bad int size %d", v.Type.Size()) 305 } 306 v.AddArg(cvt) 307 308 f := b0.Func 309 if f.pass.debug > 0 { 310 f.Warnl(v.Block.Pos, "converted OpPhi bool -> int%d", v.Type.Size()*8) 311 } 312 } 313 314 // b is the If block giving the boolean value. 315 // v is the phi value v = (OpPhi (ConstBool [true]) (ConstBool [false])). 316 // reverse is the predecessor from which the truth value comes. 317 func convertPhi(b *Block, v *Value, reverse int) { 318 f := b.Func 319 ops := [2]Op{OpNot, OpCopy} 320 v.reset(ops[v.Args[reverse].AuxInt]) 321 v.AddArg(b.Controls[0]) 322 if f.pass.debug > 0 { 323 f.Warnl(b.Pos, "converted OpPhi to %v", v.Op) 324 } 325 }