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