]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.ssa] cmd/compile/internal/ssa: simplify boolean phis
authorAlexandru Moșoi <mosoi@google.com>
Mon, 29 Feb 2016 18:29:04 +0000 (19:29 +0100)
committerAlexandru Moșoi <alexandru@mosoi.ro>
Tue, 1 Mar 2016 17:56:13 +0000 (17:56 +0000)
* 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/20090
Reviewed-by: Keith Randall <khr@golang.org>
Run-TryBot: Alexandru Moșoi <alexandru@mosoi.ro>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/cmd/compile/internal/ssa/compile.go
src/cmd/compile/internal/ssa/phiopt.go [new file with mode: 0644]
test/phiopt.go [new file with mode: 0644]

index 5e68ea004e84914d3331e14eb3e6f6ae2902f3e9..2780e5bcfc1b3b28bde1ab4251c274a1cb96f3c6 100644 (file)
@@ -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},
diff --git a/src/cmd/compile/internal/ssa/phiopt.go b/src/cmd/compile/internal/ssa/phiopt.go
new file mode 100644 (file)
index 0000000..fb17727
--- /dev/null
@@ -0,0 +1,86 @@
+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
+                       }
+               }
+       }
+
+}
diff --git a/test/phiopt.go b/test/phiopt.go
new file mode 100644 (file)
index 0000000..9b9b701
--- /dev/null
@@ -0,0 +1,43 @@
+// +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() {
+}