]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.ssa] cmd/compile: OANDAND, OOROR
authorBrad Fitzpatrick <bradfitz@golang.org>
Fri, 10 Jul 2015 18:58:53 +0000 (12:58 -0600)
committerBrad Fitzpatrick <bradfitz@golang.org>
Mon, 13 Jul 2015 04:08:16 +0000 (04:08 +0000)
Joint hacking with josharian. Hints from matloob and Todd Neal.

Now with tests, and OROR.

Change-Id: Iff8826fde475691fb72a3eea7396a640b6274af9
Reviewed-on: https://go-review.googlesource.com/12041
Reviewed-by: Keith Randall <khr@golang.org>
src/cmd/compile/internal/gc/ssa.go
src/cmd/compile/internal/gc/ssa_test.go [new file with mode: 0644]
src/cmd/compile/internal/gc/testdata/short_ssa.go [new file with mode: 0644]
src/cmd/compile/internal/ssa/gen/AMD64.rules
src/cmd/compile/internal/ssa/print.go
src/cmd/compile/internal/ssa/rewriteAMD64.go

index d47680bf8ab610ef65f3d876c07f7abbf9922f2c..c4bfb2e731f1ba0794eb1925619aaf64d1442328 100644 (file)
@@ -500,6 +500,46 @@ func (s *state) expr(n *Node) *ssa.Value {
                a := s.expr(n.Left)
                b := s.expr(n.Right)
                return s.newValue2(binOpToSSA[n.Op], a.Type, a, b)
+       case OANDAND, OOROR:
+               // To implement OANDAND (and OOROR), we introduce a
+               // new temporary variable to hold the result. The
+               // variable is associated with the OANDAND node in the
+               // s.vars table (normally variables are only
+               // associated with ONAME nodes). We convert
+               //     A && B
+               // to
+               //     var = A
+               //     if var {
+               //         var = B
+               //     }
+               // Using var in the subsequent block introduces the
+               // necessary phi variable.
+               el := s.expr(n.Left)
+               s.vars[n] = el
+
+               b := s.endBlock()
+               b.Kind = ssa.BlockIf
+               b.Control = el
+
+               bRight := s.f.NewBlock(ssa.BlockPlain)
+               bResult := s.f.NewBlock(ssa.BlockPlain)
+               if n.Op == OANDAND {
+                       addEdge(b, bRight)
+                       addEdge(b, bResult)
+               } else if n.Op == OOROR {
+                       addEdge(b, bResult)
+                       addEdge(b, bRight)
+               }
+
+               s.startBlock(bRight)
+               er := s.expr(n.Right)
+               s.vars[n] = er
+
+               b = s.endBlock()
+               addEdge(b, bResult)
+
+               s.startBlock(bResult)
+               return s.variable(n, n.Type)
 
        // unary ops
        case ONOT:
diff --git a/src/cmd/compile/internal/gc/ssa_test.go b/src/cmd/compile/internal/gc/ssa_test.go
new file mode 100644 (file)
index 0000000..bcc7725
--- /dev/null
@@ -0,0 +1,30 @@
+// Copyright 2015 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 gc
+
+import (
+       "bytes"
+       "internal/testenv"
+       "os/exec"
+       "strings"
+       "testing"
+)
+
+func TestShortCircuit(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+       var stdout, stderr bytes.Buffer
+       cmd := exec.Command("go", "run", "testdata/short_ssa.go")
+       cmd.Stdout = &stdout
+       cmd.Stderr = &stderr
+       if err := cmd.Run(); err != nil {
+               t.Fatalf("Failed: %v:\nOut: %s\nStderr: %s\n", err, &stdout, &stderr)
+       }
+       if s := stdout.String(); s != "" {
+               t.Errorf("Stdout = %s\nWant empty", s)
+       }
+       if s := stderr.String(); strings.Contains(s, "SSA unimplemented") {
+               t.Errorf("Unimplemented message found in stderr:\n%s", s)
+       }
+}
diff --git a/src/cmd/compile/internal/gc/testdata/short_ssa.go b/src/cmd/compile/internal/gc/testdata/short_ssa.go
new file mode 100644 (file)
index 0000000..9427423
--- /dev/null
@@ -0,0 +1,60 @@
+// compile
+
+// Copyright 2015 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.
+
+// Tests short circuiting.
+
+package main
+
+func and_ssa(arg1, arg2 bool) bool {
+       return arg1 && rightCall(arg2)
+}
+
+func or_ssa(arg1, arg2 bool) bool {
+       return arg1 || rightCall(arg2)
+}
+
+var rightCalled bool
+
+func rightCall(v bool) bool {
+       rightCalled = true
+       return v
+       select {} // hack to prevent inlining
+       panic("unreached")
+}
+
+func testAnd(arg1, arg2, wantRes bool) { testShortCircuit("AND", arg1, arg2, and_ssa, arg1, wantRes) }
+func testOr(arg1, arg2, wantRes bool)  { testShortCircuit("OR", arg1, arg2, or_ssa, !arg1, wantRes) }
+
+func testShortCircuit(opName string, arg1, arg2 bool, fn func(bool, bool) bool, wantRightCall, wantRes bool) {
+       rightCalled = false
+       got := fn(arg1, arg2)
+       if rightCalled != wantRightCall {
+               println("failed for", arg1, opName, arg2, "; rightCalled=", rightCalled, "want=", wantRightCall)
+               failed = true
+       }
+       if wantRes != got {
+               println("failed for", arg1, opName, arg2, "; res=", got, "want=", wantRes)
+               failed = true
+       }
+}
+
+var failed = false
+
+func main() {
+       testAnd(false, false, false)
+       testAnd(false, true, false)
+       testAnd(true, false, false)
+       testAnd(true, true, true)
+
+       testOr(false, false, false)
+       testOr(false, true, true)
+       testOr(true, false, true)
+       testOr(true, true, true)
+
+       if failed {
+               panic("failed")
+       }
+}
index 02b68b2e3ce04ca3b4fd92f7e52a8dac9d5b7e3b..aa0f6a7943d72dca22653c467f22f47f2b42e08a 100644 (file)
@@ -28,6 +28,7 @@
 (MOVBstore ptr (MOVBQSX x) mem) -> (MOVBstore ptr x mem)
 
 (Convert <t> x) && t.IsInteger() && x.Type.IsInteger() -> (Copy x)
+(ConvNop <t> x) && t == x.Type -> (Copy x)
 
 // Lowering shifts
 // Note: unsigned shifts need to return 0 if shift amount is >= 64.
index 14d88dccd54c8c92eb67643d6d93d17a5801c5bf..c8b90c6f93031c7630baff056d0bab708dcc40cc 100644 (file)
@@ -26,7 +26,14 @@ func fprintFunc(w io.Writer, f *Func) {
        fmt.Fprintln(w, f.Type)
        printed := make([]bool, f.NumValues())
        for _, b := range f.Blocks {
-               fmt.Fprintf(w, "  b%d:\n", b.ID)
+               fmt.Fprintf(w, "  b%d:", b.ID)
+               if len(b.Preds) > 0 {
+                       io.WriteString(w, " <-")
+                       for _, pred := range b.Preds {
+                               fmt.Fprintf(w, " b%d", pred.ID)
+                       }
+               }
+               io.WriteString(w, "\n")
                n := 0
 
                // print phis first since all value cycles contain a phi
index 95964d10bba2c623861e8665cc5e67aa334fec27..d4447ea49a203c98045d90b66ed094ed28fd91d3 100644 (file)
@@ -499,6 +499,26 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
                goto end4c8bfe9df26fc5aa2bd76b211792732a
        end4c8bfe9df26fc5aa2bd76b211792732a:
                ;
+       case OpConvNop:
+               // match: (ConvNop <t> x)
+               // cond: t == x.Type
+               // result: (Copy x)
+               {
+                       t := v.Type
+                       x := v.Args[0]
+                       if !(t == x.Type) {
+                               goto end6c588ed8aedc7dca8c06b4ada77e3ddd
+                       }
+                       v.Op = OpCopy
+                       v.AuxInt = 0
+                       v.Aux = nil
+                       v.resetArgs()
+                       v.AddArg(x)
+                       return true
+               }
+               goto end6c588ed8aedc7dca8c06b4ada77e3ddd
+       end6c588ed8aedc7dca8c06b4ada77e3ddd:
+               ;
        case OpConvert:
                // match: (Convert <t> x)
                // cond: t.IsInteger() && x.Type.IsInteger()