blob: 132366cfc11f5c7995275f30679654c73610c11f [file] [log] [blame]
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
}
}
}
}