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 {
return lab
}
// There is a copy of checkgoto in the new SSA backend.
// Please keep them in sync.
func checkgoto(from *Node, to *Node) {
if from.Sym == to.Sym {
return
......
This diff is collapsed.
......@@ -8,23 +8,24 @@ import (
"bytes"
"internal/testenv"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
)
// Tests OANDAND and OOROR expressions and short circuiting.
// TODO: move these tests elsewhere? perhaps teach test/run.go how to run them
// with a new action verb.
func TestShortCircuit(t *testing.T) {
// TODO: move all these tests elsewhere?
// Perhaps teach test/run.go how to run them with a new action verb.
func runTest(t *testing.T, filename string) {
if runtime.GOARCH != "amd64" {
t.Skipf("skipping SSA tests on %s for now", runtime.GOARCH)
}
testenv.MustHaveGoBuild(t)
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.Stderr = &stderr
// TODO: set GOGC=off until we have stackmaps
if err := cmd.Run(); err != nil {
t.Fatalf("Failed: %v:\nOut: %s\nStderr: %s\n", err, &stdout, &stderr)
}
......@@ -35,3 +36,9 @@ func TestShortCircuit(t *testing.T) {
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 {
var passes = [...]pass{
{"phielim", phielim},
{"copyelim", copyelim},
{"early deadcode", deadcode}, // remove generated dead code to avoid doing pointless work during opt
{"opt", opt},
{"opt deadcode", deadcode}, // remove any blocks orphaned during opt
{"generic cse", cse},
......
......@@ -58,4 +58,8 @@ L10:
default:
break L10
}
goto L10
goto go2 // ERROR "label go2 not defined"
}
......@@ -31,11 +31,17 @@ L2:
break L2
}
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
}
for {
if x == 1 {
continue L2 // ERROR "invalid continue label .*L2"
}
}
L3:
switch {
case x > 10:
......@@ -43,7 +49,7 @@ L3:
break L3
}
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
}
......@@ -54,7 +60,7 @@ L4:
break L4 // ERROR "invalid break label .*L4"
}
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 {
goto L4
......@@ -67,7 +73,7 @@ L5:
break L5 // ERROR "invalid break label .*L5"
}
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 {
goto L5
......@@ -84,4 +90,21 @@ L5:
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