| // 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) { |
| for _, b := range f.Blocks { |
| if len(b.Preds) != 2 || len(b.Values) == 0 { |
| continue |
| } |
| |
| pb0, b0 := b, b.Preds[0] |
| for len(b0.Succs) == 1 && len(b0.Preds) == 1 { |
| pb0, b0 = b0, b0.Preds[0] |
| } |
| if b0.Kind != BlockIf { |
| continue |
| } |
| pb1, b1 := b, b.Preds[1] |
| for len(b1.Succs) == 1 && len(b1.Preds) == 1 { |
| pb1, b1 = b1, b1.Preds[0] |
| } |
| if b1 != b0 { |
| continue |
| } |
| // b0 is the if block giving the boolean value. |
| |
| var reverse bool |
| if b0.Succs[0] == pb0 && b0.Succs[1] == pb1 { |
| reverse = false |
| } else if b0.Succs[0] == pb1 && b0.Succs[1] == pb0 { |
| reverse = true |
| } else { |
| b.Fatalf("invalid predecessors\n") |
| } |
| |
| for _, v := range b.Values { |
| if v.Op != OpPhi || !v.Type.IsBoolean() || v.Args[0].Op != OpConstBool || v.Args[1].Op != OpConstBool { |
| continue |
| } |
| |
| ok, isCopy := false, false |
| if v.Args[0].AuxInt == 1 && v.Args[1].AuxInt == 0 { |
| ok, isCopy = true, !reverse |
| } else if v.Args[0].AuxInt == 0 && v.Args[1].AuxInt == 1 { |
| ok, isCopy = true, reverse |
| } |
| |
| // (Phi (ConstBool [x]) (ConstBool [x])) is already handled by opt / phielim. |
| |
| if ok && isCopy { |
| if f.pass.debug > 0 { |
| f.Config.Warnl(b.Line, "converted OpPhi to OpCopy") |
| } |
| v.reset(OpCopy) |
| v.AddArg(b0.Control) |
| continue |
| } |
| if ok && !isCopy { |
| if f.pass.debug > 0 { |
| f.Config.Warnl(b.Line, "converted OpPhi to OpNot") |
| } |
| v.reset(OpNot) |
| v.AddArg(b0.Control) |
| continue |
| } |
| } |
| } |
| |
| } |