import (
"bytes"
"internal/testenv"
+ "io/ioutil"
"os/exec"
"path/filepath"
+ "runtime"
"strings"
"testing"
)
}
}
+// runGenTest runs a test-generator, then runs the generated test.
+// Generated test can either fail in compilation or execution.
+// The environment variable parameter(s) is passed to the run
+// of the generated test.
+func runGenTest(t *testing.T, filename, tmpname string, ev ...string) {
+ testenv.MustHaveGoRun(t)
+ var stdout, stderr bytes.Buffer
+ cmd := exec.Command("go", "run", filepath.Join("testdata", filename))
+ cmd.Stdout = &stdout
+ cmd.Stderr = &stderr
+ if err := cmd.Run(); err != nil {
+ t.Fatalf("Failed: %v:\nOut: %s\nStderr: %s\n", err, &stdout, &stderr)
+ }
+ // Write stdout into a temporary file
+ tmpdir, ok := ioutil.TempDir("", tmpname)
+ if ok != nil {
+ t.Fatalf("Failed to create temporary directory")
+ }
+
+ rungo := filepath.Join(tmpdir, "run.go")
+ ok = ioutil.WriteFile(rungo, stdout.Bytes(), 0600)
+ if ok != nil {
+ t.Fatalf("Failed to create temporary file " + rungo)
+ }
+
+ stdout.Reset()
+ stderr.Reset()
+ cmd = exec.Command("go", "run", "-gcflags", "-d=ssa/check/on", rungo)
+ cmd.Stdout = &stdout
+ cmd.Stderr = &stderr
+ cmd.Env = append(cmd.Env, ev...)
+ if err := cmd.Run(); err != nil {
+ t.Fatalf("Failed: %v:\nOut: %s\nStderr: %s\n", err, &stdout, &stderr)
+ }
+ if s := stderr.String(); s != "" {
+ t.Errorf("Stderr = %s\nWant empty", s)
+ }
+ if s := stdout.String(); s != "" {
+ t.Errorf("Stdout = %s\nWant empty", s)
+ }
+
+}
+
+func TestGenFlowGraph(t *testing.T) {
+ runGenTest(t, "flowgraph_generator1.go", "ssa_fg_tmp1")
+ if runtime.GOOS != "windows" {
+ runGenTest(t, "flowgraph_generator1.go", "ssa_fg_tmp2", "GO_SSA_PHI_LOC_CUTOFF=0")
+ }
+}
+
// TestShortCircuit tests OANDAND and OOROR expressions and short circuiting.
func TestShortCircuit(t *testing.T) { runTest(t, "short.go") }
--- /dev/null
+// 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 (
+ "fmt"
+ "strings"
+)
+
+// make fake flow graph.
+
+// The blocks of the flow graph are designated with letters A
+// through Z, always including A (start block) and Z (exit
+// block) The specification of a flow graph is a comma-
+// separated list of block successor words, for blocks ordered
+// A, B, C etc, where each block except Z has one or two
+// successors, and any block except A can be a target. Within
+// the generated code, each block with two successors includes
+// a conditional testing x & 1 != 0 (x is the input parameter
+// to the generated function) and also unconditionally shifts x
+// right by one, so that different inputs generate different
+// execution paths, including loops. Every block inverts a
+// global binary to ensure it is not empty. For a flow graph
+// with J words (J+1 blocks), a J-1 bit serial number specifies
+// which blocks (not including A and Z) include an increment of
+// the return variable y by increasing powers of 10, and a
+// different version of the test function is created for each
+// of the 2-to-the-(J-1) serial numbers.
+
+// For each generated function a compact summary is also
+// created so that the generated funtion can be simulated
+// with a simple interpreter to sanity check the behavior of
+// the compiled code.
+
+// For example:
+
+// func BC_CD_BE_BZ_CZ101(x int64) int64 {
+// y := int64(0)
+// var b int64
+// _ = b
+// b = x & 1
+// x = x >> 1
+// if b != 0 {
+// goto C
+// }
+// goto B
+// B:
+// glob_ = !glob_
+// y += 1
+// b = x & 1
+// x = x >> 1
+// if b != 0 {
+// goto D
+// }
+// goto C
+// C:
+// glob_ = !glob_
+// // no y increment
+// b = x & 1
+// x = x >> 1
+// if b != 0 {
+// goto E
+// }
+// goto B
+// D:
+// glob_ = !glob_
+// y += 10
+// b = x & 1
+// x = x >> 1
+// if b != 0 {
+// goto Z
+// }
+// goto B
+// E:
+// glob_ = !glob_
+// // no y increment
+// b = x & 1
+// x = x >> 1
+// if b != 0 {
+// goto Z
+// }
+// goto C
+// Z:
+// return y
+// }
+
+// {f:BC_CD_BE_BZ_CZ101,
+// maxin:32, blocks:[]blo{
+// blo{inc:0, cond:true, succs:[2]int64{1, 2}},
+// blo{inc:1, cond:true, succs:[2]int64{2, 3}},
+// blo{inc:0, cond:true, succs:[2]int64{1, 4}},
+// blo{inc:10, cond:true, succs:[2]int64{1, 25}},
+// blo{inc:0, cond:true, succs:[2]int64{2, 25}},}},
+
+var labels string = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+func blocks(spec string) (blocks []string, fnameBase string) {
+ spec = strings.ToUpper(spec)
+ blocks = strings.Split(spec, ",")
+ fnameBase = strings.Replace(spec, ",", "_", -1)
+ return
+}
+
+func makeFunctionFromFlowGraph(blocks []blo, fname string) string {
+ s := ""
+
+ for j := range blocks {
+ // begin block
+ if j == 0 {
+ // block A, implicit label
+ s += `
+func ` + fname + `(x int64) int64 {
+ y := int64(0)
+ var b int64
+ _ = b`
+ } else {
+ // block B,C, etc, explicit label w/ conditional increment
+ l := labels[j : j+1]
+ yeq := `
+ // no y increment`
+ if blocks[j].inc != 0 {
+ yeq = `
+ y += ` + fmt.Sprintf("%d", blocks[j].inc)
+ }
+
+ s += `
+` + l + `:
+ glob = !glob` + yeq
+ }
+
+ // edges to successors
+ if blocks[j].cond { // conditionally branch to second successor
+ s += `
+ b = x & 1
+ x = x >> 1
+ if b != 0 {` + `
+ goto ` + string(labels[blocks[j].succs[1]]) + `
+ }`
+
+ }
+ // branch to first successor
+ s += `
+ goto ` + string(labels[blocks[j].succs[0]])
+ }
+
+ // end block (Z)
+ s += `
+Z:
+ return y
+}
+`
+ return s
+}
+
+var graphs []string = []string{
+ "Z", "BZ,Z", "B,BZ", "BZ,BZ",
+ "ZB,Z", "B,ZB", "ZB,BZ", "ZB,ZB",
+
+ "BC,C,Z", "BC,BC,Z", "BC,BC,BZ",
+ "BC,Z,Z", "BC,ZC,Z", "BC,ZC,BZ",
+ "BZ,C,Z", "BZ,BC,Z", "BZ,CZ,Z",
+ "BZ,C,BZ", "BZ,BC,BZ", "BZ,CZ,BZ",
+ "BZ,C,CZ", "BZ,BC,CZ", "BZ,CZ,CZ",
+
+ "BC,CD,BE,BZ,CZ",
+ "BC,BD,CE,CZ,BZ",
+ "BC,BD,CE,FZ,GZ,F,G",
+ "BC,BD,CE,FZ,GZ,G,F",
+
+ "BC,DE,BE,FZ,FZ,Z",
+ "BC,DE,BE,FZ,ZF,Z",
+ "BC,DE,BE,ZF,FZ,Z",
+ "BC,DE,EB,FZ,FZ,Z",
+ "BC,ED,BE,FZ,FZ,Z",
+ "CB,DE,BE,FZ,FZ,Z",
+
+ "CB,ED,BE,FZ,FZ,Z",
+ "BC,ED,EB,FZ,ZF,Z",
+ "CB,DE,EB,ZF,FZ,Z",
+ "CB,ED,EB,FZ,FZ,Z",
+
+ "BZ,CD,CD,CE,BZ",
+ "EC,DF,FG,ZC,GB,BE,FD",
+ "BH,CF,DG,HE,BF,CG,DH,BZ",
+}
+
+// blo describes a block in the generated/interpreted code
+type blo struct {
+ inc int64 // increment amount
+ cond bool // block ends in conditional
+ succs [2]int64
+}
+
+// strings2blocks converts a slice of strings specifying
+// successors into a slice of blo encoding the blocks in a
+// common form easy to execute or interpret.
+func strings2blocks(blocks []string, fname string, i int) (bs []blo, cond uint) {
+ bs = make([]blo, len(blocks))
+ edge := int64(1)
+ cond = 0
+ k := uint(0)
+ for j, s := range blocks {
+ if j == 0 {
+ } else {
+ if (i>>k)&1 != 0 {
+ bs[j].inc = edge
+ edge *= 10
+ }
+ k++
+ }
+ if len(s) > 1 {
+ bs[j].succs[1] = int64(blocks[j][1] - 'A')
+ bs[j].cond = true
+ cond++
+ }
+ bs[j].succs[0] = int64(blocks[j][0] - 'A')
+ }
+ return bs, cond
+}
+
+// fmtBlocks writes out the blocks for consumption in the generated test
+func fmtBlocks(bs []blo) string {
+ s := "[]blo{"
+ for _, b := range bs {
+ s += fmt.Sprintf("blo{inc:%d, cond:%v, succs:[2]int64{%d, %d}},", b.inc, b.cond, b.succs[0], b.succs[1])
+ }
+ s += "}"
+ return s
+}
+
+func main() {
+ fmt.Printf(`// This is a machine-generated test file from flowgraph_generator1.go.
+package main
+import "fmt"
+var glob bool
+`)
+ s := "var funs []fun = []fun{"
+ for _, g := range graphs {
+ split, fnameBase := blocks(g)
+ nconfigs := 1 << uint(len(split)-1)
+
+ for i := 0; i < nconfigs; i++ {
+ fname := fnameBase + fmt.Sprintf("%b", i)
+ bs, k := strings2blocks(split, fname, i)
+ fmt.Printf("%s", makeFunctionFromFlowGraph(bs, fname))
+ s += `
+ {f:` + fname + `, maxin:` + fmt.Sprintf("%d", 1<<k) + `, blocks:` + fmtBlocks(bs) + `},`
+ }
+
+ }
+ s += `}
+`
+ // write types for name+array tables.
+ fmt.Printf("%s",
+ `
+type blo struct {
+ inc int64
+ cond bool
+ succs [2]int64
+}
+type fun struct {
+ f func(int64) int64
+ maxin int64
+ blocks []blo
+}
+`)
+ // write table of function names and blo arrays.
+ fmt.Printf("%s", s)
+
+ // write interpreter and main/test
+ fmt.Printf("%s", `
+func interpret(blocks []blo, x int64) (int64, bool) {
+ y := int64(0)
+ last := int64(25) // 'Z'-'A'
+ j := int64(0)
+ for i := 0; i < 4*len(blocks); i++ {
+ b := blocks[j]
+ y += b.inc
+ next := b.succs[0]
+ if b.cond {
+ c := x&1 != 0
+ x = x>>1
+ if c {
+ next = b.succs[1]
+ }
+ }
+ if next == last {
+ return y, true
+ }
+ j = next
+ }
+ return -1, false
+}
+
+func main() {
+ sum := int64(0)
+ for i, f := range funs {
+ for x := int64(0); x < 16*f.maxin; x++ {
+ y, ok := interpret(f.blocks, x)
+ if ok {
+ yy := f.f(x)
+ if y != yy {
+ fmt.Printf("y(%d) != yy(%d), x=%b, i=%d, blocks=%v\n", y, yy, x, i, f.blocks)
+ return
+ }
+ sum += y
+ }
+ }
+ }
+// fmt.Printf("Sum of all returns over all terminating inputs is %d\n", sum)
+}
+`)
+}