{"opt", "nilcheckelim"},
// tighten should happen before lowering to avoid splitting naturally paired instructions such as CMP/SET
{"tighten", "lower"},
- // cse, nilcheckelim, prove and loopbce share idom.
+ // cse, phiopt, nilcheckelim, prove and loopbce share idom.
{"generic domtree", "generic cse"},
+ {"generic domtree", "phiopt"},
{"generic domtree", "nilcheckelim"},
{"generic domtree", "prove"},
{"generic domtree", "loopbce"},
}
// b0 is the if block giving the boolean value.
- var reverse bool
+ // reverse is the predecessor from which the truth value comes.
+ var reverse int
if b0.Succs[0] == pb0 && b0.Succs[1] == pb1 {
- reverse = false
+ reverse = 0
} else if b0.Succs[0] == pb1 && b0.Succs[1] == pb0 {
- reverse = true
+ reverse = 1
} 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 {
+ if v.Op != OpPhi || !v.Type.IsBoolean() {
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")
+ // 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.Config.Warnl(b.Line, "converted OpPhi to %v", v.Op)
+ }
+ continue
}
- 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")
+
+ // 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]; f.sdom.isAncestorEq(tmp.Block, b) {
+ v.reset(OpOr8)
+ v.SetArgs2(b0.Control, tmp)
+ if f.pass.debug > 0 {
+ f.Config.Warnl(b.Line, "converted OpPhi to %v", v.Op)
+ }
+ continue
}
- v.reset(OpNot)
- v.AddArg(b0.Control)
- continue
}
}
}
// +build amd64
// errorcheck -0 -d=ssa/phiopt/debug=3
+// 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 main
+//go:noinline
func f0(a bool) bool {
x := false
if a {
} else {
x = false
}
- return x // ERROR "converted OpPhi to OpCopy$"
+ return x // ERROR "converted OpPhi to Copy$"
}
+//go:noinline
func f1(a bool) bool {
x := false
if a {
} else {
x = true
}
- return x // ERROR "converted OpPhi to OpNot$"
+ return x // ERROR "converted OpPhi to Not$"
}
+//go:noinline
func f2(a, b int) bool {
x := true
if a == b {
x = false
}
- return x // ERROR "converted OpPhi to OpNot$"
+ return x // ERROR "converted OpPhi to Not$"
}
+//go:noinline
func f3(a, b int) bool {
x := false
if a == b {
x = true
}
- return x // ERROR "converted OpPhi to OpCopy$"
+ return x // ERROR "converted OpPhi to Copy$"
+}
+
+//go:noinline
+func f4(a, b bool) bool {
+ return a || b // ERROR "converted OpPhi to Or8$"
+}
+
+//go:noinline
+func f5(a int, b bool) bool {
+ x := b
+ if a == 0 {
+ x = true
+ }
+ return x // ERROR "converted OpPhi to Or8$"
+}
+
+//go:noinline
+func f6(a int, b bool) bool {
+ x := b
+ if a == 0 {
+ // f6 has side effects so the OpPhi should not be converted.
+ x = f6(a, b)
+ }
+ return x
}
func main() {