github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/src/cmd/compile/internal/ssa/func_test.go (about) 1 // Copyright 2015 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 // This file contains some utility functions to help define Funcs for testing. 6 // As an example, the following func 7 // 8 // b1: 9 // v1 = InitMem <mem> 10 // Plain -> b2 11 // b2: 12 // Exit v1 13 // b3: 14 // v2 = Const <bool> [true] 15 // If v2 -> b3 b2 16 // 17 // can be defined as 18 // 19 // fun := Fun("entry", 20 // Bloc("entry", 21 // Valu("mem", OpInitMem, TypeMem, 0, nil), 22 // Goto("exit")), 23 // Bloc("exit", 24 // Exit("mem")), 25 // Bloc("deadblock", 26 // Valu("deadval", OpConstBool, TypeBool, 0, true), 27 // If("deadval", "deadblock", "exit"))) 28 // 29 // and the Blocks or Values used in the Func can be accessed 30 // like this: 31 // fun.blocks["entry"] or fun.values["deadval"] 32 33 package ssa 34 35 // TODO(matloob): Choose better names for Fun, Bloc, Goto, etc. 36 // TODO(matloob): Write a parser for the Func disassembly. Maybe 37 // the parser can be used instead of Fun. 38 39 import ( 40 "cmd/internal/src" 41 "fmt" 42 "reflect" 43 "testing" 44 ) 45 46 // Compare two Funcs for equivalence. Their CFGs must be isomorphic, 47 // and their values must correspond. 48 // Requires that values and predecessors are in the same order, even 49 // though Funcs could be equivalent when they are not. 50 // TODO(matloob): Allow values and predecessors to be in different 51 // orders if the CFG are otherwise equivalent. 52 func Equiv(f, g *Func) bool { 53 valcor := make(map[*Value]*Value) 54 var checkVal func(fv, gv *Value) bool 55 checkVal = func(fv, gv *Value) bool { 56 if fv == nil && gv == nil { 57 return true 58 } 59 if valcor[fv] == nil && valcor[gv] == nil { 60 valcor[fv] = gv 61 valcor[gv] = fv 62 // Ignore ids. Ops and Types are compared for equality. 63 // TODO(matloob): Make sure types are canonical and can 64 // be compared for equality. 65 if fv.Op != gv.Op || fv.Type != gv.Type || fv.AuxInt != gv.AuxInt { 66 return false 67 } 68 if !reflect.DeepEqual(fv.Aux, gv.Aux) { 69 // This makes the assumption that aux values can be compared 70 // using DeepEqual. 71 // TODO(matloob): Aux values may be *gc.Sym pointers in the near 72 // future. Make sure they are canonical. 73 return false 74 } 75 if len(fv.Args) != len(gv.Args) { 76 return false 77 } 78 for i := range fv.Args { 79 if !checkVal(fv.Args[i], gv.Args[i]) { 80 return false 81 } 82 } 83 } 84 return valcor[fv] == gv && valcor[gv] == fv 85 } 86 blkcor := make(map[*Block]*Block) 87 var checkBlk func(fb, gb *Block) bool 88 checkBlk = func(fb, gb *Block) bool { 89 if blkcor[fb] == nil && blkcor[gb] == nil { 90 blkcor[fb] = gb 91 blkcor[gb] = fb 92 // ignore ids 93 if fb.Kind != gb.Kind { 94 return false 95 } 96 if len(fb.Values) != len(gb.Values) { 97 return false 98 } 99 for i := range fb.Values { 100 if !checkVal(fb.Values[i], gb.Values[i]) { 101 return false 102 } 103 } 104 if len(fb.Succs) != len(gb.Succs) { 105 return false 106 } 107 for i := range fb.Succs { 108 if !checkBlk(fb.Succs[i].b, gb.Succs[i].b) { 109 return false 110 } 111 } 112 if len(fb.Preds) != len(gb.Preds) { 113 return false 114 } 115 for i := range fb.Preds { 116 if !checkBlk(fb.Preds[i].b, gb.Preds[i].b) { 117 return false 118 } 119 } 120 return true 121 122 } 123 return blkcor[fb] == gb && blkcor[gb] == fb 124 } 125 126 return checkBlk(f.Entry, g.Entry) 127 } 128 129 // fun is the return type of Fun. It contains the created func 130 // itself as well as indexes from block and value names into the 131 // corresponding Blocks and Values. 132 type fun struct { 133 f *Func 134 blocks map[string]*Block 135 values map[string]*Value 136 } 137 138 var emptyPass pass = pass{ 139 name: "empty pass", 140 } 141 142 // Fun takes the name of an entry bloc and a series of Bloc calls, and 143 // returns a fun containing the composed Func. entry must be a name 144 // supplied to one of the Bloc functions. Each of the bloc names and 145 // valu names should be unique across the Fun. 146 func (c *Conf) Fun(entry string, blocs ...bloc) fun { 147 f := NewFunc(c.Frontend()) 148 f.Config = c.config 149 // TODO: Either mark some SSA tests as t.Parallel, 150 // or set up a shared Cache and Reset it between tests. 151 // But not both. 152 f.Cache = new(Cache) 153 f.pass = &emptyPass 154 155 blocks := make(map[string]*Block) 156 values := make(map[string]*Value) 157 // Create all the blocks and values. 158 for _, bloc := range blocs { 159 b := f.NewBlock(bloc.control.kind) 160 blocks[bloc.name] = b 161 for _, valu := range bloc.valus { 162 // args are filled in the second pass. 163 values[valu.name] = b.NewValue0IA(src.NoXPos, valu.op, valu.t, valu.auxint, valu.aux) 164 } 165 } 166 // Connect the blocks together and specify control values. 167 f.Entry = blocks[entry] 168 for _, bloc := range blocs { 169 b := blocks[bloc.name] 170 c := bloc.control 171 // Specify control values. 172 if c.control != "" { 173 cval, ok := values[c.control] 174 if !ok { 175 f.Fatalf("control value for block %s missing", bloc.name) 176 } 177 b.SetControl(cval) 178 } 179 // Fill in args. 180 for _, valu := range bloc.valus { 181 v := values[valu.name] 182 for _, arg := range valu.args { 183 a, ok := values[arg] 184 if !ok { 185 b.Fatalf("arg %s missing for value %s in block %s", 186 arg, valu.name, bloc.name) 187 } 188 v.AddArg(a) 189 } 190 } 191 // Connect to successors. 192 for _, succ := range c.succs { 193 b.AddEdgeTo(blocks[succ]) 194 } 195 } 196 return fun{f, blocks, values} 197 } 198 199 // Bloc defines a block for Fun. The bloc name should be unique 200 // across the containing Fun. entries should consist of calls to valu, 201 // as well as one call to Goto, If, or Exit to specify the block kind. 202 func Bloc(name string, entries ...interface{}) bloc { 203 b := bloc{} 204 b.name = name 205 seenCtrl := false 206 for _, e := range entries { 207 switch v := e.(type) { 208 case ctrl: 209 // there should be exactly one Ctrl entry. 210 if seenCtrl { 211 panic(fmt.Sprintf("already seen control for block %s", name)) 212 } 213 b.control = v 214 seenCtrl = true 215 case valu: 216 b.valus = append(b.valus, v) 217 } 218 } 219 if !seenCtrl { 220 panic(fmt.Sprintf("block %s doesn't have control", b.name)) 221 } 222 return b 223 } 224 225 // Valu defines a value in a block. 226 func Valu(name string, op Op, t Type, auxint int64, aux interface{}, args ...string) valu { 227 return valu{name, op, t, auxint, aux, args} 228 } 229 230 // Goto specifies that this is a BlockPlain and names the single successor. 231 // TODO(matloob): choose a better name. 232 func Goto(succ string) ctrl { 233 return ctrl{BlockPlain, "", []string{succ}} 234 } 235 236 // If specifies a BlockIf. 237 func If(cond, sub, alt string) ctrl { 238 return ctrl{BlockIf, cond, []string{sub, alt}} 239 } 240 241 // Exit specifies a BlockExit. 242 func Exit(arg string) ctrl { 243 return ctrl{BlockExit, arg, []string{}} 244 } 245 246 // Eq specifies a BlockAMD64EQ. 247 func Eq(cond, sub, alt string) ctrl { 248 return ctrl{BlockAMD64EQ, cond, []string{sub, alt}} 249 } 250 251 // bloc, ctrl, and valu are internal structures used by Bloc, Valu, Goto, 252 // If, and Exit to help define blocks. 253 254 type bloc struct { 255 name string 256 control ctrl 257 valus []valu 258 } 259 260 type ctrl struct { 261 kind BlockKind 262 control string 263 succs []string 264 } 265 266 type valu struct { 267 name string 268 op Op 269 t Type 270 auxint int64 271 aux interface{} 272 args []string 273 } 274 275 func TestArgs(t *testing.T) { 276 c := testConfig(t) 277 fun := c.Fun("entry", 278 Bloc("entry", 279 Valu("a", OpConst64, TypeInt64, 14, nil), 280 Valu("b", OpConst64, TypeInt64, 26, nil), 281 Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), 282 Valu("mem", OpInitMem, TypeMem, 0, nil), 283 Goto("exit")), 284 Bloc("exit", 285 Exit("mem"))) 286 sum := fun.values["sum"] 287 for i, name := range []string{"a", "b"} { 288 if sum.Args[i] != fun.values[name] { 289 t.Errorf("arg %d for sum is incorrect: want %s, got %s", 290 i, sum.Args[i], fun.values[name]) 291 } 292 } 293 } 294 295 func TestEquiv(t *testing.T) { 296 cfg := testConfig(t) 297 equivalentCases := []struct{ f, g fun }{ 298 // simple case 299 { 300 cfg.Fun("entry", 301 Bloc("entry", 302 Valu("a", OpConst64, TypeInt64, 14, nil), 303 Valu("b", OpConst64, TypeInt64, 26, nil), 304 Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), 305 Valu("mem", OpInitMem, TypeMem, 0, nil), 306 Goto("exit")), 307 Bloc("exit", 308 Exit("mem"))), 309 cfg.Fun("entry", 310 Bloc("entry", 311 Valu("a", OpConst64, TypeInt64, 14, nil), 312 Valu("b", OpConst64, TypeInt64, 26, nil), 313 Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), 314 Valu("mem", OpInitMem, TypeMem, 0, nil), 315 Goto("exit")), 316 Bloc("exit", 317 Exit("mem"))), 318 }, 319 // block order changed 320 { 321 cfg.Fun("entry", 322 Bloc("entry", 323 Valu("a", OpConst64, TypeInt64, 14, nil), 324 Valu("b", OpConst64, TypeInt64, 26, nil), 325 Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), 326 Valu("mem", OpInitMem, TypeMem, 0, nil), 327 Goto("exit")), 328 Bloc("exit", 329 Exit("mem"))), 330 cfg.Fun("entry", 331 Bloc("exit", 332 Exit("mem")), 333 Bloc("entry", 334 Valu("a", OpConst64, TypeInt64, 14, nil), 335 Valu("b", OpConst64, TypeInt64, 26, nil), 336 Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), 337 Valu("mem", OpInitMem, TypeMem, 0, nil), 338 Goto("exit"))), 339 }, 340 } 341 for _, c := range equivalentCases { 342 if !Equiv(c.f.f, c.g.f) { 343 t.Error("expected equivalence. Func definitions:") 344 t.Error(c.f.f) 345 t.Error(c.g.f) 346 } 347 } 348 349 differentCases := []struct{ f, g fun }{ 350 // different shape 351 { 352 cfg.Fun("entry", 353 Bloc("entry", 354 Valu("mem", OpInitMem, TypeMem, 0, nil), 355 Goto("exit")), 356 Bloc("exit", 357 Exit("mem"))), 358 cfg.Fun("entry", 359 Bloc("entry", 360 Valu("mem", OpInitMem, TypeMem, 0, nil), 361 Exit("mem"))), 362 }, 363 // value order changed 364 { 365 cfg.Fun("entry", 366 Bloc("entry", 367 Valu("mem", OpInitMem, TypeMem, 0, nil), 368 Valu("b", OpConst64, TypeInt64, 26, nil), 369 Valu("a", OpConst64, TypeInt64, 14, nil), 370 Exit("mem"))), 371 cfg.Fun("entry", 372 Bloc("entry", 373 Valu("mem", OpInitMem, TypeMem, 0, nil), 374 Valu("a", OpConst64, TypeInt64, 14, nil), 375 Valu("b", OpConst64, TypeInt64, 26, nil), 376 Exit("mem"))), 377 }, 378 // value auxint different 379 { 380 cfg.Fun("entry", 381 Bloc("entry", 382 Valu("mem", OpInitMem, TypeMem, 0, nil), 383 Valu("a", OpConst64, TypeInt64, 14, nil), 384 Exit("mem"))), 385 cfg.Fun("entry", 386 Bloc("entry", 387 Valu("mem", OpInitMem, TypeMem, 0, nil), 388 Valu("a", OpConst64, TypeInt64, 26, nil), 389 Exit("mem"))), 390 }, 391 // value aux different 392 { 393 cfg.Fun("entry", 394 Bloc("entry", 395 Valu("mem", OpInitMem, TypeMem, 0, nil), 396 Valu("a", OpConst64, TypeInt64, 0, 14), 397 Exit("mem"))), 398 cfg.Fun("entry", 399 Bloc("entry", 400 Valu("mem", OpInitMem, TypeMem, 0, nil), 401 Valu("a", OpConst64, TypeInt64, 0, 26), 402 Exit("mem"))), 403 }, 404 // value args different 405 { 406 cfg.Fun("entry", 407 Bloc("entry", 408 Valu("mem", OpInitMem, TypeMem, 0, nil), 409 Valu("a", OpConst64, TypeInt64, 14, nil), 410 Valu("b", OpConst64, TypeInt64, 26, nil), 411 Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), 412 Exit("mem"))), 413 cfg.Fun("entry", 414 Bloc("entry", 415 Valu("mem", OpInitMem, TypeMem, 0, nil), 416 Valu("a", OpConst64, TypeInt64, 0, nil), 417 Valu("b", OpConst64, TypeInt64, 14, nil), 418 Valu("sum", OpAdd64, TypeInt64, 0, nil, "b", "a"), 419 Exit("mem"))), 420 }, 421 } 422 for _, c := range differentCases { 423 if Equiv(c.f.f, c.g.f) { 424 t.Error("expected difference. Func definitions:") 425 t.Error(c.f.f) 426 t.Error(c.g.f) 427 } 428 } 429 } 430 431 // TestConstCache ensures that the cache will not return 432 // reused free'd values with a non-matching AuxInt 433 func TestConstCache(t *testing.T) { 434 c := testConfig(t) 435 f := c.Fun("entry", 436 Bloc("entry", 437 Valu("mem", OpInitMem, TypeMem, 0, nil), 438 Exit("mem"))) 439 v1 := f.f.ConstBool(src.NoXPos, TypeBool, false) 440 v2 := f.f.ConstBool(src.NoXPos, TypeBool, true) 441 f.f.freeValue(v1) 442 f.f.freeValue(v2) 443 v3 := f.f.ConstBool(src.NoXPos, TypeBool, false) 444 v4 := f.f.ConstBool(src.NoXPos, TypeBool, true) 445 if v3.AuxInt != 0 { 446 t.Errorf("expected %s to have auxint of 0\n", v3.LongString()) 447 } 448 if v4.AuxInt != 1 { 449 t.Errorf("expected %s to have auxint of 1\n", v4.LongString()) 450 } 451 452 } 453 454 // opcodeMap returns a map from opcode to the number of times that opcode 455 // appears in the function. 456 func opcodeMap(f *Func) map[Op]int { 457 m := map[Op]int{} 458 for _, b := range f.Blocks { 459 for _, v := range b.Values { 460 m[v.Op]++ 461 } 462 } 463 return m 464 } 465 466 // opcodeCounts checks that the number of opcodes listed in m agree with the 467 // number of opcodes that appear in the function. 468 func checkOpcodeCounts(t *testing.T, f *Func, m map[Op]int) { 469 n := opcodeMap(f) 470 for op, cnt := range m { 471 if n[op] != cnt { 472 t.Errorf("%s appears %d times, want %d times", op, n[op], cnt) 473 } 474 } 475 }