Commit e197f467 authored by Alexandru Moșoi's avatar Alexandru Moșoi Committed by Alexandru Moșoi

[dev.ssa] cmd/compile/internal/ssa: simplify boolean phis

* Decreases the generated code slightly.
* Similar to phiopt pass from gcc, except it only handles
booleans. Handling Eq/Neq had no impact on the generated code.

name       old time/op     new time/op     delta
Template       453ms ± 4%      451ms ± 4%    ~           (p=0.468 n=24+24)
GoTypes        1.55s ± 1%      1.55s ± 2%    ~           (p=0.287 n=24+25)
Compiler       6.53s ± 2%      6.56s ± 1%  +0.46%        (p=0.050 n=23+23)
MakeBash       45.8s ± 2%      45.7s ± 2%    ~           (p=0.866 n=24+25)

name       old text-bytes  new text-bytes  delta
HelloSize       676k ± 0%       676k ± 0%    ~     (all samples are equal)
CmdGoSize      8.07M ± 0%      8.07M ± 0%  -0.03%        (p=0.000 n=25+25)

Change-Id: Ia62477b7554127958a14cb27f85849b095d63663
Reviewed-on: https://go-review.googlesource.com/20090Reviewed-by: 's avatarKeith Randall <khr@golang.org>
Run-TryBot: Alexandru Moșoi <alexandru@mosoi.ro>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 1f6e9e36
......@@ -164,6 +164,7 @@ var passes = [...]pass{
{name: "zero arg cse", fn: zcse, required: true}, // required to merge OpSB values
{name: "opt deadcode", fn: deadcode}, // remove any blocks orphaned during opt
{name: "generic cse", fn: cse},
{name: "phiopt", fn: phiopt},
{name: "nilcheckelim", fn: nilcheckelim},
{name: "prove", fn: prove},
{name: "generic deadcode", fn: deadcode},
......
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 b0.Kind != BlockIf && len(b0.Preds) == 1 {
pb0, b0 = b0, b0.Preds[0]
}
if b0.Kind != BlockIf {
continue
}
pb1, b1 := b, b.Preds[1]
for b1.Kind != BlockIf && 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(int(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(int(b.Line), "converted OpPhi to OpNot")
}
v.reset(OpNot)
v.AddArg(b0.Control)
continue
}
}
}
}
// +build amd64
// errorcheck -0 -d=ssa/phiopt/debug=3
package main
func f0(a bool) bool {
x := false
if a {
x = true
} else {
x = false
}
return x // ERROR "converted OpPhi to OpCopy$"
}
func f1(a bool) bool {
x := false
if a {
x = false
} else {
x = true
}
return x // ERROR "converted OpPhi to OpNot$"
}
func f2(a, b int) bool {
x := true
if a == b {
x = false
}
return x // ERROR "converted OpPhi to OpNot$"
}
func f3(a, b int) bool {
x := false
if a == b {
x = true
}
return x // ERROR "converted OpPhi to OpCopy$"
}
func main() {
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment