]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: transform some Phis into Or8.
authorAlexandru Moșoi <mosoi@google.com>
Tue, 12 Apr 2016 16:24:34 +0000 (18:24 +0200)
committerAlexandru Moșoi <alexandru@mosoi.ro>
Tue, 19 Apr 2016 22:04:30 +0000 (22:04 +0000)
func f(a, b bool) bool {
          return a || b
}

is now a single instructions (excluding loading and unloading the arguments):
      v10 = ORB <bool> v11 v12 : AX

Change-Id: Iff63399410cb46909f4318ea1c3f45a029f4aa5e
Reviewed-on: https://go-review.googlesource.com/21872
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
src/cmd/compile/internal/ssa/compile.go
src/cmd/compile/internal/ssa/phiopt.go
test/phiopt.go
test/prove.go

index a0b5ff71cf8c8890e218a324785a37d879031624..bc9c830ee958aa5b48c9798d79ad151eb590bac1 100644 (file)
@@ -289,8 +289,9 @@ var passOrder = [...]constraint{
        {"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"},
index 2d0a45733ad39ba4b7cee2c0f8bb704bb0e8371c..4efd497bdb8525d065f6355455a0148c50867fa1 100644 (file)
@@ -45,44 +45,51 @@ func phiopt(f *Func) {
                }
                // 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
                        }
                }
        }
index 9b9b701124f1f7deac6ed67f4a09edfbd107b3bf..37caab0b513cbc18ffd0aeb898a017e6d6c0559b 100644 (file)
@@ -1,8 +1,13 @@
 // +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 {
@@ -10,9 +15,10 @@ func f0(a bool) bool {
        } 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 {
@@ -20,23 +26,49 @@ func f1(a bool) bool {
        } 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() {
index a78adf03dce370cb015cdcc40ecc92f090bb034f..8bcc9ae61406f36d11bede3e90df673dc6749ad3 100644 (file)
@@ -1,6 +1,10 @@
 // +build amd64
 // errorcheck -0 -d=ssa/prove/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
 
 import "math"