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