github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/test/testdata/flowgraph_generator1.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "fmt" 9 "strings" 10 ) 11 12 // make fake flow graph. 13 14 // The blocks of the flow graph are designated with letters A 15 // through Z, always including A (start block) and Z (exit 16 // block) The specification of a flow graph is a comma- 17 // separated list of block successor words, for blocks ordered 18 // A, B, C etc, where each block except Z has one or two 19 // successors, and any block except A can be a target. Within 20 // the generated code, each block with two successors includes 21 // a conditional testing x & 1 != 0 (x is the input parameter 22 // to the generated function) and also unconditionally shifts x 23 // right by one, so that different inputs generate different 24 // execution paths, including loops. Every block inverts a 25 // global binary to ensure it is not empty. For a flow graph 26 // with J words (J+1 blocks), a J-1 bit serial number specifies 27 // which blocks (not including A and Z) include an increment of 28 // the return variable y by increasing powers of 10, and a 29 // different version of the test function is created for each 30 // of the 2-to-the-(J-1) serial numbers. 31 32 // For each generated function a compact summary is also 33 // created so that the generated function can be simulated 34 // with a simple interpreter to sanity check the behavior of 35 // the compiled code. 36 37 // For example: 38 39 // func BC_CD_BE_BZ_CZ101(x int64) int64 { 40 // y := int64(0) 41 // var b int64 42 // _ = b 43 // b = x & 1 44 // x = x >> 1 45 // if b != 0 { 46 // goto C 47 // } 48 // goto B 49 // B: 50 // glob_ = !glob_ 51 // y += 1 52 // b = x & 1 53 // x = x >> 1 54 // if b != 0 { 55 // goto D 56 // } 57 // goto C 58 // C: 59 // glob_ = !glob_ 60 // // no y increment 61 // b = x & 1 62 // x = x >> 1 63 // if b != 0 { 64 // goto E 65 // } 66 // goto B 67 // D: 68 // glob_ = !glob_ 69 // y += 10 70 // b = x & 1 71 // x = x >> 1 72 // if b != 0 { 73 // goto Z 74 // } 75 // goto B 76 // E: 77 // glob_ = !glob_ 78 // // no y increment 79 // b = x & 1 80 // x = x >> 1 81 // if b != 0 { 82 // goto Z 83 // } 84 // goto C 85 // Z: 86 // return y 87 // } 88 89 // {f:BC_CD_BE_BZ_CZ101, 90 // maxin:32, blocks:[]blo{ 91 // blo{inc:0, cond:true, succs:[2]int64{1, 2}}, 92 // blo{inc:1, cond:true, succs:[2]int64{2, 3}}, 93 // blo{inc:0, cond:true, succs:[2]int64{1, 4}}, 94 // blo{inc:10, cond:true, succs:[2]int64{1, 25}}, 95 // blo{inc:0, cond:true, succs:[2]int64{2, 25}},}}, 96 97 var labels string = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 98 99 func blocks(spec string) (blocks []string, fnameBase string) { 100 spec = strings.ToUpper(spec) 101 blocks = strings.Split(spec, ",") 102 fnameBase = strings.Replace(spec, ",", "_", -1) 103 return 104 } 105 106 func makeFunctionFromFlowGraph(blocks []blo, fname string) string { 107 s := "" 108 109 for j := range blocks { 110 // begin block 111 if j == 0 { 112 // block A, implicit label 113 s += ` 114 func ` + fname + `(x int64) int64 { 115 y := int64(0) 116 var b int64 117 _ = b` 118 } else { 119 // block B,C, etc, explicit label w/ conditional increment 120 l := labels[j : j+1] 121 yeq := ` 122 // no y increment` 123 if blocks[j].inc != 0 { 124 yeq = ` 125 y += ` + fmt.Sprintf("%d", blocks[j].inc) 126 } 127 128 s += ` 129 ` + l + `: 130 glob = !glob` + yeq 131 } 132 133 // edges to successors 134 if blocks[j].cond { // conditionally branch to second successor 135 s += ` 136 b = x & 1 137 x = x >> 1 138 if b != 0 {` + ` 139 goto ` + string(labels[blocks[j].succs[1]]) + ` 140 }` 141 142 } 143 // branch to first successor 144 s += ` 145 goto ` + string(labels[blocks[j].succs[0]]) 146 } 147 148 // end block (Z) 149 s += ` 150 Z: 151 return y 152 } 153 ` 154 return s 155 } 156 157 var graphs []string = []string{ 158 "Z", "BZ,Z", "B,BZ", "BZ,BZ", 159 "ZB,Z", "B,ZB", "ZB,BZ", "ZB,ZB", 160 161 "BC,C,Z", "BC,BC,Z", "BC,BC,BZ", 162 "BC,Z,Z", "BC,ZC,Z", "BC,ZC,BZ", 163 "BZ,C,Z", "BZ,BC,Z", "BZ,CZ,Z", 164 "BZ,C,BZ", "BZ,BC,BZ", "BZ,CZ,BZ", 165 "BZ,C,CZ", "BZ,BC,CZ", "BZ,CZ,CZ", 166 167 "BC,CD,BE,BZ,CZ", 168 "BC,BD,CE,CZ,BZ", 169 "BC,BD,CE,FZ,GZ,F,G", 170 "BC,BD,CE,FZ,GZ,G,F", 171 172 "BC,DE,BE,FZ,FZ,Z", 173 "BC,DE,BE,FZ,ZF,Z", 174 "BC,DE,BE,ZF,FZ,Z", 175 "BC,DE,EB,FZ,FZ,Z", 176 "BC,ED,BE,FZ,FZ,Z", 177 "CB,DE,BE,FZ,FZ,Z", 178 179 "CB,ED,BE,FZ,FZ,Z", 180 "BC,ED,EB,FZ,ZF,Z", 181 "CB,DE,EB,ZF,FZ,Z", 182 "CB,ED,EB,FZ,FZ,Z", 183 184 "BZ,CD,CD,CE,BZ", 185 "EC,DF,FG,ZC,GB,BE,FD", 186 "BH,CF,DG,HE,BF,CG,DH,BZ", 187 } 188 189 // blo describes a block in the generated/interpreted code 190 type blo struct { 191 inc int64 // increment amount 192 cond bool // block ends in conditional 193 succs [2]int64 194 } 195 196 // strings2blocks converts a slice of strings specifying 197 // successors into a slice of blo encoding the blocks in a 198 // common form easy to execute or interpret. 199 func strings2blocks(blocks []string, fname string, i int) (bs []blo, cond uint) { 200 bs = make([]blo, len(blocks)) 201 edge := int64(1) 202 cond = 0 203 k := uint(0) 204 for j, s := range blocks { 205 if j == 0 { 206 } else { 207 if (i>>k)&1 != 0 { 208 bs[j].inc = edge 209 edge *= 10 210 } 211 k++ 212 } 213 if len(s) > 1 { 214 bs[j].succs[1] = int64(blocks[j][1] - 'A') 215 bs[j].cond = true 216 cond++ 217 } 218 bs[j].succs[0] = int64(blocks[j][0] - 'A') 219 } 220 return bs, cond 221 } 222 223 // fmtBlocks writes out the blocks for consumption in the generated test 224 func fmtBlocks(bs []blo) string { 225 s := "[]blo{" 226 for _, b := range bs { 227 s += fmt.Sprintf("blo{inc:%d, cond:%v, succs:[2]int64{%d, %d}},", b.inc, b.cond, b.succs[0], b.succs[1]) 228 } 229 s += "}" 230 return s 231 } 232 233 func main() { 234 fmt.Printf(`// This is a machine-generated test file from flowgraph_generator1.go. 235 package main 236 import "fmt" 237 var glob bool 238 `) 239 s := "var funs []fun = []fun{" 240 for _, g := range graphs { 241 split, fnameBase := blocks(g) 242 nconfigs := 1 << uint(len(split)-1) 243 244 for i := 0; i < nconfigs; i++ { 245 fname := fnameBase + fmt.Sprintf("%b", i) 246 bs, k := strings2blocks(split, fname, i) 247 fmt.Printf("%s", makeFunctionFromFlowGraph(bs, fname)) 248 s += ` 249 {f:` + fname + `, maxin:` + fmt.Sprintf("%d", 1<<k) + `, blocks:` + fmtBlocks(bs) + `},` 250 } 251 252 } 253 s += `} 254 ` 255 // write types for name+array tables. 256 fmt.Printf("%s", 257 ` 258 type blo struct { 259 inc int64 260 cond bool 261 succs [2]int64 262 } 263 type fun struct { 264 f func(int64) int64 265 maxin int64 266 blocks []blo 267 } 268 `) 269 // write table of function names and blo arrays. 270 fmt.Printf("%s", s) 271 272 // write interpreter and main/test 273 fmt.Printf("%s", ` 274 func interpret(blocks []blo, x int64) (int64, bool) { 275 y := int64(0) 276 last := int64(25) // 'Z'-'A' 277 j := int64(0) 278 for i := 0; i < 4*len(blocks); i++ { 279 b := blocks[j] 280 y += b.inc 281 next := b.succs[0] 282 if b.cond { 283 c := x&1 != 0 284 x = x>>1 285 if c { 286 next = b.succs[1] 287 } 288 } 289 if next == last { 290 return y, true 291 } 292 j = next 293 } 294 return -1, false 295 } 296 297 func main() { 298 sum := int64(0) 299 for i, f := range funs { 300 for x := int64(0); x < 16*f.maxin; x++ { 301 y, ok := interpret(f.blocks, x) 302 if ok { 303 yy := f.f(x) 304 if y != yy { 305 fmt.Printf("y(%d) != yy(%d), x=%b, i=%d, blocks=%v\n", y, yy, x, i, f.blocks) 306 return 307 } 308 sum += y 309 } 310 } 311 } 312 // fmt.Printf("Sum of all returns over all terminating inputs is %d\n", sum) 313 } 314 `) 315 }