// 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 function 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<>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) } `) }