| // Copyright 2016 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package ssa | |
| // phiopt eliminates boolean Phis based on the previous if. | |
| // | |
| // Main use case is to transform: | |
| // x := false | |
| // if b { | |
| // x = true | |
| // } | |
| // into x = b. | |
| // | |
| // In SSA code this appears as | |
| // | |
| // b0 | |
| // If b -> b1 b2 | |
| // b1 | |
| // Plain -> b2 | |
| // b2 | |
| // x = (OpPhi (ConstBool [true]) (ConstBool [false])) | |
| // | |
| // In this case we can replace x with a copy of b. | |
| func phiopt(f *Func) { | |
| sdom := f.sdom() | |
| for _, b := range f.Blocks { | |
| if len(b.Preds) != 2 || len(b.Values) == 0 { | |
| // TODO: handle more than 2 predecessors, e.g. a || b || c. | |
| continue | |
| } | |
| pb0, b0 := b, b.Preds[0].b | |
| for len(b0.Succs) == 1 && len(b0.Preds) == 1 { | |
| pb0, b0 = b0, b0.Preds[0].b | |
| } | |
| if b0.Kind != BlockIf { | |
| continue | |
| } | |
| pb1, b1 := b, b.Preds[1].b | |
| for len(b1.Succs) == 1 && len(b1.Preds) == 1 { | |
| pb1, b1 = b1, b1.Preds[0].b | |
| } | |
| if b1 != b0 { | |
| continue | |
| } | |
| // b0 is the if block giving the boolean value. | |
| // reverse is the predecessor from which the truth value comes. | |
| var reverse int | |
| if b0.Succs[0].b == pb0 && b0.Succs[1].b == pb1 { | |
| reverse = 0 | |
| } else if b0.Succs[0].b == pb1 && b0.Succs[1].b == pb0 { | |
| reverse = 1 | |
| } else { | |
| b.Fatalf("invalid predecessors\n") | |
| } | |
| for _, v := range b.Values { | |
| if v.Op != OpPhi { | |
| continue | |
| } | |
| // Look for conversions from bool to 0/1. | |
| if v.Type.IsInteger() { | |
| phioptint(v, b0, reverse) | |
| } | |
| if !v.Type.IsBoolean() { | |
| continue | |
| } | |
| // Replaces | |
| // if a { x = true } else { x = false } with x = a | |
| // and | |
| // if a { x = false } else { x = true } with x = !a | |
| if v.Args[0].Op == OpConstBool && v.Args[1].Op == OpConstBool { | |
| if v.Args[reverse].AuxInt != v.Args[1-reverse].AuxInt { | |
| ops := [2]Op{OpNot, OpCopy} | |
| v.reset(ops[v.Args[reverse].AuxInt]) | |
| v.AddArg(b0.Control) | |
| if f.pass.debug > 0 { | |
| f.Warnl(b.Pos, "converted OpPhi to %v", v.Op) | |
| } | |
| continue | |
| } | |
| } | |
| // Replaces | |
| // if a { x = true } else { x = value } with x = a || value. | |
| // Requires that value dominates x, meaning that regardless of a, | |
| // value is always computed. This guarantees that the side effects | |
| // of value are not seen if a is false. | |
| if v.Args[reverse].Op == OpConstBool && v.Args[reverse].AuxInt == 1 { | |
| if tmp := v.Args[1-reverse]; sdom.isAncestorEq(tmp.Block, b) { | |
| v.reset(OpOrB) | |
| v.SetArgs2(b0.Control, tmp) | |
| if f.pass.debug > 0 { | |
| f.Warnl(b.Pos, "converted OpPhi to %v", v.Op) | |
| } | |
| continue | |
| } | |
| } | |
| // Replaces | |
| // if a { x = value } else { x = false } with x = a && value. | |
| // Requires that value dominates x, meaning that regardless of a, | |
| // value is always computed. This guarantees that the side effects | |
| // of value are not seen if a is false. | |
| if v.Args[1-reverse].Op == OpConstBool && v.Args[1-reverse].AuxInt == 0 { | |
| if tmp := v.Args[reverse]; sdom.isAncestorEq(tmp.Block, b) { | |
| v.reset(OpAndB) | |
| v.SetArgs2(b0.Control, tmp) | |
| if f.pass.debug > 0 { | |
| f.Warnl(b.Pos, "converted OpPhi to %v", v.Op) | |
| } | |
| continue | |
| } | |
| } | |
| } | |
| } | |
| } | |
| func phioptint(v *Value, b0 *Block, reverse int) { | |
| a0 := v.Args[0] | |
| a1 := v.Args[1] | |
| if a0.Op != a1.Op { | |
| return | |
| } | |
| switch a0.Op { | |
| case OpConst8, OpConst16, OpConst32, OpConst64: | |
| default: | |
| return | |
| } | |
| negate := false | |
| switch { | |
| case a0.AuxInt == 0 && a1.AuxInt == 1: | |
| negate = true | |
| case a0.AuxInt == 1 && a1.AuxInt == 0: | |
| default: | |
| return | |
| } | |
| if reverse == 1 { | |
| negate = !negate | |
| } | |
| switch v.Type.Size() { | |
| case 1: | |
| v.reset(OpCopy) | |
| case 2: | |
| v.reset(OpZeroExt8to16) | |
| case 4: | |
| v.reset(OpZeroExt8to32) | |
| case 8: | |
| v.reset(OpZeroExt8to64) | |
| default: | |
| v.Fatalf("bad int size %d", v.Type.Size()) | |
| } | |
| a := b0.Control | |
| if negate { | |
| a = v.Block.NewValue1(v.Pos, OpNot, a.Type, a) | |
| } | |
| v.AddArg(a) | |
| f := b0.Func | |
| if f.pass.debug > 0 { | |
| f.Warnl(v.Block.Pos, "converted OpPhi bool -> int%d", v.Type.Size()*8) | |
| } | |
| } |