Commit 61aa0953 authored by Josh Bleecher Snyder's avatar Josh Bleecher Snyder

[dev.ssa] cmd/compile: implement control flow handling

Add label and goto checks and improve test coverage.

Implement OSWITCH and OSELECT.

Implement OBREAK and OCONTINUE.

Allow generation of code in dead blocks.

Change-Id: Ibebb7c98b4b2344f46d38db7c9dce058c56beaac
Reviewed-on: https://go-review.googlesource.com/12445Reviewed-by: 's avatarKeith Randall <khr@golang.org>
parent 3e7e519c
...@@ -141,6 +141,8 @@ func newlab(n *Node) *Label { ...@@ -141,6 +141,8 @@ func newlab(n *Node) *Label {
return lab return lab
} }
// There is a copy of checkgoto in the new SSA backend.
// Please keep them in sync.
func checkgoto(from *Node, to *Node) { func checkgoto(from *Node, to *Node) {
if from.Sym == to.Sym { if from.Sym == to.Sym {
return return
......
This diff is collapsed.
...@@ -8,23 +8,24 @@ import ( ...@@ -8,23 +8,24 @@ import (
"bytes" "bytes"
"internal/testenv" "internal/testenv"
"os/exec" "os/exec"
"path/filepath"
"runtime" "runtime"
"strings" "strings"
"testing" "testing"
) )
// Tests OANDAND and OOROR expressions and short circuiting. // TODO: move all these tests elsewhere?
// TODO: move these tests elsewhere? perhaps teach test/run.go how to run them // Perhaps teach test/run.go how to run them with a new action verb.
// with a new action verb. func runTest(t *testing.T, filename string) {
func TestShortCircuit(t *testing.T) {
if runtime.GOARCH != "amd64" { if runtime.GOARCH != "amd64" {
t.Skipf("skipping SSA tests on %s for now", runtime.GOARCH) t.Skipf("skipping SSA tests on %s for now", runtime.GOARCH)
} }
testenv.MustHaveGoBuild(t) testenv.MustHaveGoBuild(t)
var stdout, stderr bytes.Buffer var stdout, stderr bytes.Buffer
cmd := exec.Command("go", "run", "testdata/short_ssa.go") cmd := exec.Command("go", "run", filepath.Join("testdata", filename))
cmd.Stdout = &stdout cmd.Stdout = &stdout
cmd.Stderr = &stderr cmd.Stderr = &stderr
// TODO: set GOGC=off until we have stackmaps
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
t.Fatalf("Failed: %v:\nOut: %s\nStderr: %s\n", err, &stdout, &stderr) t.Fatalf("Failed: %v:\nOut: %s\nStderr: %s\n", err, &stdout, &stderr)
} }
...@@ -35,3 +36,9 @@ func TestShortCircuit(t *testing.T) { ...@@ -35,3 +36,9 @@ func TestShortCircuit(t *testing.T) {
t.Errorf("Unimplemented message found in stderr:\n%s", s) t.Errorf("Unimplemented message found in stderr:\n%s", s)
} }
} }
// TestShortCircuit tests OANDAND and OOROR expressions and short circuiting.
func TestShortCircuit(t *testing.T) { runTest(t, "short_ssa.go") }
// TestBreakContinue tests that continue and break statements do what they say.
func TestBreakContinue(t *testing.T) { runTest(t, "break_ssa.go") }
// run
// 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 continue and break.
package main
func continuePlain_ssa() int {
var n int
for i := 0; i < 10; i++ {
if i == 6 {
continue
}
n = i
}
return n
}
func continueLabeled_ssa() int {
var n int
Next:
for i := 0; i < 10; i++ {
if i == 6 {
continue Next
}
n = i
}
return n
}
func continuePlainInner_ssa() int {
var n int
for j := 0; j < 30; j += 10 {
for i := 0; i < 10; i++ {
if i == 6 {
continue
}
n = i
}
n += j
}
return n
}
func continueLabeledInner_ssa() int {
var n int
for j := 0; j < 30; j += 10 {
Next:
for i := 0; i < 10; i++ {
if i == 6 {
continue Next
}
n = i
}
n += j
}
return n
}
func continueLabeledOuter_ssa() int {
var n int
Next:
for j := 0; j < 30; j += 10 {
for i := 0; i < 10; i++ {
if i == 6 {
continue Next
}
n = i
}
n += j
}
return n
}
func breakPlain_ssa() int {
var n int
for i := 0; i < 10; i++ {
if i == 6 {
break
}
n = i
}
return n
}
func breakLabeled_ssa() int {
var n int
Next:
for i := 0; i < 10; i++ {
if i == 6 {
break Next
}
n = i
}
return n
}
func breakPlainInner_ssa() int {
var n int
for j := 0; j < 30; j += 10 {
for i := 0; i < 10; i++ {
if i == 6 {
break
}
n = i
}
n += j
}
return n
}
func breakLabeledInner_ssa() int {
var n int
for j := 0; j < 30; j += 10 {
Next:
for i := 0; i < 10; i++ {
if i == 6 {
break Next
}
n = i
}
n += j
}
return n
}
func breakLabeledOuter_ssa() int {
var n int
Next:
for j := 0; j < 30; j += 10 {
for i := 0; i < 10; i++ {
if i == 6 {
break Next
}
n = i
}
n += j
}
return n
}
var g, h int // globals to ensure optimizations don't collapse our switch statements
func switchPlain_ssa() int {
var n int
switch g {
case 0:
n = 1
break
n = 2
}
return n
}
func switchLabeled_ssa() int {
var n int
Done:
switch g {
case 0:
n = 1
break Done
n = 2
}
return n
}
func switchPlainInner_ssa() int {
var n int
switch g {
case 0:
n = 1
switch h {
case 0:
n += 10
break
}
n = 2
}
return n
}
func switchLabeledInner_ssa() int {
var n int
switch g {
case 0:
n = 1
Done:
switch h {
case 0:
n += 10
break Done
}
n = 2
}
return n
}
func switchLabeledOuter_ssa() int {
var n int
Done:
switch g {
case 0:
n = 1
switch h {
case 0:
n += 10
break Done
}
n = 2
}
return n
}
func main() {
tests := [...]struct {
name string
fn func() int
want int
}{
{"continuePlain_ssa", continuePlain_ssa, 9},
{"continueLabeled_ssa", continueLabeled_ssa, 9},
{"continuePlainInner_ssa", continuePlainInner_ssa, 29},
{"continueLabeledInner_ssa", continueLabeledInner_ssa, 29},
{"continueLabeledOuter_ssa", continueLabeledOuter_ssa, 5},
{"breakPlain_ssa", breakPlain_ssa, 5},
{"breakLabeled_ssa", breakLabeled_ssa, 5},
{"breakPlainInner_ssa", breakPlainInner_ssa, 25},
{"breakLabeledInner_ssa", breakLabeledInner_ssa, 25},
{"breakLabeledOuter_ssa", breakLabeledOuter_ssa, 5},
{"switchPlain_ssa", switchPlain_ssa, 1},
{"switchLabeled_ssa", switchLabeled_ssa, 1},
{"switchPlainInner_ssa", switchPlainInner_ssa, 2},
{"switchLabeledInner_ssa", switchLabeledInner_ssa, 2},
{"switchLabeledOuter_ssa", switchLabeledOuter_ssa, 11},
// no select tests; they're identical to switch
}
var failed bool
for _, test := range tests {
if got := test.fn(); test.fn() != test.want {
print(test.name, "()=", got, ", want ", test.want, "\n")
failed = true
}
}
if failed {
panic("failed")
}
}
...@@ -50,6 +50,7 @@ type pass struct { ...@@ -50,6 +50,7 @@ type pass struct {
var passes = [...]pass{ var passes = [...]pass{
{"phielim", phielim}, {"phielim", phielim},
{"copyelim", copyelim}, {"copyelim", copyelim},
{"early deadcode", deadcode}, // remove generated dead code to avoid doing pointless work during opt
{"opt", opt}, {"opt", opt},
{"opt deadcode", deadcode}, // remove any blocks orphaned during opt {"opt deadcode", deadcode}, // remove any blocks orphaned during opt
{"generic cse", cse}, {"generic cse", cse},
......
...@@ -58,4 +58,8 @@ L10: ...@@ -58,4 +58,8 @@ L10:
default: default:
break L10 break L10
} }
goto L10
goto go2 // ERROR "label go2 not defined"
} }
...@@ -31,11 +31,17 @@ L2: ...@@ -31,11 +31,17 @@ L2:
break L2 break L2
} }
if x == 1 { if x == 1 {
continue L2 // ERROR "invalid continue label .*L2" continue L2 // ERROR "invalid continue label .*L2|continue is not in a loop"
} }
goto L2 goto L2
} }
for {
if x == 1 {
continue L2 // ERROR "invalid continue label .*L2"
}
}
L3: L3:
switch { switch {
case x > 10: case x > 10:
...@@ -43,7 +49,7 @@ L3: ...@@ -43,7 +49,7 @@ L3:
break L3 break L3
} }
if x == 12 { if x == 12 {
continue L3 // ERROR "invalid continue label .*L3" continue L3 // ERROR "invalid continue label .*L3|continue is not in a loop"
} }
goto L3 goto L3
} }
...@@ -54,7 +60,7 @@ L4: ...@@ -54,7 +60,7 @@ L4:
break L4 // ERROR "invalid break label .*L4" break L4 // ERROR "invalid break label .*L4"
} }
if x == 14 { if x == 14 {
continue L4 // ERROR "invalid continue label .*L4" continue L4 // ERROR "invalid continue label .*L4|continue is not in a loop"
} }
if x == 15 { if x == 15 {
goto L4 goto L4
...@@ -67,7 +73,7 @@ L5: ...@@ -67,7 +73,7 @@ L5:
break L5 // ERROR "invalid break label .*L5" break L5 // ERROR "invalid break label .*L5"
} }
if x == 17 { if x == 17 {
continue L5 // ERROR "invalid continue label .*L5" continue L5 // ERROR "invalid continue label .*L5|continue is not in a loop"
} }
if x == 18 { if x == 18 {
goto L5 goto L5
...@@ -84,4 +90,21 @@ L5: ...@@ -84,4 +90,21 @@ L5:
goto L1 goto L1
} }
} }
continue // ERROR "continue is not in a loop"
for {
continue on // ERROR "continue label not defined: on"
}
break // ERROR "break is not in a loop"
for {
break dance // ERROR "break label not defined: dance"
}
for {
switch x {
case 1:
continue
}
}
} }
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