github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/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 Fun(c *Config, entry string, blocs ...bloc) fun { 147 f := c.NewFunc() 148 f.pass = &emptyPass 149 150 blocks := make(map[string]*Block) 151 values := make(map[string]*Value) 152 // Create all the blocks and values. 153 for _, bloc := range blocs { 154 b := f.NewBlock(bloc.control.kind) 155 blocks[bloc.name] = b 156 for _, valu := range bloc.valus { 157 // args are filled in the second pass. 158 values[valu.name] = b.NewValue0IA(src.NoXPos, valu.op, valu.t, valu.auxint, valu.aux) 159 } 160 } 161 // Connect the blocks together and specify control values. 162 f.Entry = blocks[entry] 163 for _, bloc := range blocs { 164 b := blocks[bloc.name] 165 c := bloc.control 166 // Specify control values. 167 if c.control != "" { 168 cval, ok := values[c.control] 169 if !ok { 170 f.Fatalf("control value for block %s missing", bloc.name) 171 } 172 b.SetControl(cval) 173 } 174 // Fill in args. 175 for _, valu := range bloc.valus { 176 v := values[valu.name] 177 for _, arg := range valu.args { 178 a, ok := values[arg] 179 if !ok { 180 b.Fatalf("arg %s missing for value %s in block %s", 181 arg, valu.name, bloc.name) 182 } 183 v.AddArg(a) 184 } 185 } 186 // Connect to successors. 187 for _, succ := range c.succs { 188 b.AddEdgeTo(blocks[succ]) 189 } 190 } 191 return fun{f, blocks, values} 192 } 193 194 // Bloc defines a block for Fun. The bloc name should be unique 195 // across the containing Fun. entries should consist of calls to valu, 196 // as well as one call to Goto, If, or Exit to specify the block kind. 197 func Bloc(name string, entries ...interface{}) bloc { 198 b := bloc{} 199 b.name = name 200 seenCtrl := false 201 for _, e := range entries { 202 switch v := e.(type) { 203 case ctrl: 204 // there should be exactly one Ctrl entry. 205 if seenCtrl { 206 panic(fmt.Sprintf("already seen control for block %s", name)) 207 } 208 b.control = v 209 seenCtrl = true 210 case valu: 211 b.valus = append(b.valus, v) 212 } 213 } 214 if !seenCtrl { 215 panic(fmt.Sprintf("block %s doesn't have control", b.name)) 216 } 217 return b 218 } 219 220 // Valu defines a value in a block. 221 func Valu(name string, op Op, t Type, auxint int64, aux interface{}, args ...string) valu { 222 return valu{name, op, t, auxint, aux, args} 223 } 224 225 // Goto specifies that this is a BlockPlain and names the single successor. 226 // TODO(matloob): choose a better name. 227 func Goto(succ string) ctrl { 228 return ctrl{BlockPlain, "", []string{succ}} 229 } 230 231 // If specifies a BlockIf. 232 func If(cond, sub, alt string) ctrl { 233 return ctrl{BlockIf, cond, []string{sub, alt}} 234 } 235 236 // Exit specifies a BlockExit. 237 func Exit(arg string) ctrl { 238 return ctrl{BlockExit, arg, []string{}} 239 } 240 241 // Eq specifies a BlockAMD64EQ. 242 func Eq(cond, sub, alt string) ctrl { 243 return ctrl{BlockAMD64EQ, cond, []string{sub, alt}} 244 } 245 246 // bloc, ctrl, and valu are internal structures used by Bloc, Valu, Goto, 247 // If, and Exit to help define blocks. 248 249 type bloc struct { 250 name string 251 control ctrl 252 valus []valu 253 } 254 255 type ctrl struct { 256 kind BlockKind 257 control string 258 succs []string 259 } 260 261 type valu struct { 262 name string 263 op Op 264 t Type 265 auxint int64 266 aux interface{} 267 args []string 268 } 269 270 func TestArgs(t *testing.T) { 271 c := testConfig(t) 272 fun := Fun(c, "entry", 273 Bloc("entry", 274 Valu("a", OpConst64, TypeInt64, 14, nil), 275 Valu("b", OpConst64, TypeInt64, 26, nil), 276 Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), 277 Valu("mem", OpInitMem, TypeMem, 0, nil), 278 Goto("exit")), 279 Bloc("exit", 280 Exit("mem"))) 281 sum := fun.values["sum"] 282 for i, name := range []string{"a", "b"} { 283 if sum.Args[i] != fun.values[name] { 284 t.Errorf("arg %d for sum is incorrect: want %s, got %s", 285 i, sum.Args[i], fun.values[name]) 286 } 287 } 288 } 289 290 func TestEquiv(t *testing.T) { 291 equivalentCases := []struct{ f, g fun }{ 292 // simple case 293 { 294 Fun(testConfig(t), "entry", 295 Bloc("entry", 296 Valu("a", OpConst64, TypeInt64, 14, nil), 297 Valu("b", OpConst64, TypeInt64, 26, nil), 298 Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), 299 Valu("mem", OpInitMem, TypeMem, 0, nil), 300 Goto("exit")), 301 Bloc("exit", 302 Exit("mem"))), 303 Fun(testConfig(t), "entry", 304 Bloc("entry", 305 Valu("a", OpConst64, TypeInt64, 14, nil), 306 Valu("b", OpConst64, TypeInt64, 26, nil), 307 Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), 308 Valu("mem", OpInitMem, TypeMem, 0, nil), 309 Goto("exit")), 310 Bloc("exit", 311 Exit("mem"))), 312 }, 313 // block order changed 314 { 315 Fun(testConfig(t), "entry", 316 Bloc("entry", 317 Valu("a", OpConst64, TypeInt64, 14, nil), 318 Valu("b", OpConst64, TypeInt64, 26, nil), 319 Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), 320 Valu("mem", OpInitMem, TypeMem, 0, nil), 321 Goto("exit")), 322 Bloc("exit", 323 Exit("mem"))), 324 Fun(testConfig(t), "entry", 325 Bloc("exit", 326 Exit("mem")), 327 Bloc("entry", 328 Valu("a", OpConst64, TypeInt64, 14, nil), 329 Valu("b", OpConst64, TypeInt64, 26, nil), 330 Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), 331 Valu("mem", OpInitMem, TypeMem, 0, nil), 332 Goto("exit"))), 333 }, 334 } 335 for _, c := range equivalentCases { 336 if !Equiv(c.f.f, c.g.f) { 337 t.Error("expected equivalence. Func definitions:") 338 t.Error(c.f.f) 339 t.Error(c.g.f) 340 } 341 } 342 343 differentCases := []struct{ f, g fun }{ 344 // different shape 345 { 346 Fun(testConfig(t), "entry", 347 Bloc("entry", 348 Valu("mem", OpInitMem, TypeMem, 0, nil), 349 Goto("exit")), 350 Bloc("exit", 351 Exit("mem"))), 352 Fun(testConfig(t), "entry", 353 Bloc("entry", 354 Valu("mem", OpInitMem, TypeMem, 0, nil), 355 Exit("mem"))), 356 }, 357 // value order changed 358 { 359 Fun(testConfig(t), "entry", 360 Bloc("entry", 361 Valu("mem", OpInitMem, TypeMem, 0, nil), 362 Valu("b", OpConst64, TypeInt64, 26, nil), 363 Valu("a", OpConst64, TypeInt64, 14, nil), 364 Exit("mem"))), 365 Fun(testConfig(t), "entry", 366 Bloc("entry", 367 Valu("mem", OpInitMem, TypeMem, 0, nil), 368 Valu("a", OpConst64, TypeInt64, 14, nil), 369 Valu("b", OpConst64, TypeInt64, 26, nil), 370 Exit("mem"))), 371 }, 372 // value auxint different 373 { 374 Fun(testConfig(t), "entry", 375 Bloc("entry", 376 Valu("mem", OpInitMem, TypeMem, 0, nil), 377 Valu("a", OpConst64, TypeInt64, 14, nil), 378 Exit("mem"))), 379 Fun(testConfig(t), "entry", 380 Bloc("entry", 381 Valu("mem", OpInitMem, TypeMem, 0, nil), 382 Valu("a", OpConst64, TypeInt64, 26, nil), 383 Exit("mem"))), 384 }, 385 // value aux different 386 { 387 Fun(testConfig(t), "entry", 388 Bloc("entry", 389 Valu("mem", OpInitMem, TypeMem, 0, nil), 390 Valu("a", OpConst64, TypeInt64, 0, 14), 391 Exit("mem"))), 392 Fun(testConfig(t), "entry", 393 Bloc("entry", 394 Valu("mem", OpInitMem, TypeMem, 0, nil), 395 Valu("a", OpConst64, TypeInt64, 0, 26), 396 Exit("mem"))), 397 }, 398 // value args different 399 { 400 Fun(testConfig(t), "entry", 401 Bloc("entry", 402 Valu("mem", OpInitMem, TypeMem, 0, nil), 403 Valu("a", OpConst64, TypeInt64, 14, nil), 404 Valu("b", OpConst64, TypeInt64, 26, nil), 405 Valu("sum", OpAdd64, TypeInt64, 0, nil, "a", "b"), 406 Exit("mem"))), 407 Fun(testConfig(t), "entry", 408 Bloc("entry", 409 Valu("mem", OpInitMem, TypeMem, 0, nil), 410 Valu("a", OpConst64, TypeInt64, 0, nil), 411 Valu("b", OpConst64, TypeInt64, 14, nil), 412 Valu("sum", OpAdd64, TypeInt64, 0, nil, "b", "a"), 413 Exit("mem"))), 414 }, 415 } 416 for _, c := range differentCases { 417 if Equiv(c.f.f, c.g.f) { 418 t.Error("expected difference. Func definitions:") 419 t.Error(c.f.f) 420 t.Error(c.g.f) 421 } 422 } 423 } 424 425 // TestConstCache ensures that the cache will not return 426 // reused free'd values with a non-matching AuxInt 427 func TestConstCache(t *testing.T) { 428 f := Fun(testConfig(t), "entry", 429 Bloc("entry", 430 Valu("mem", OpInitMem, TypeMem, 0, nil), 431 Exit("mem"))) 432 v1 := f.f.ConstBool(src.NoXPos, TypeBool, false) 433 v2 := f.f.ConstBool(src.NoXPos, TypeBool, true) 434 f.f.freeValue(v1) 435 f.f.freeValue(v2) 436 v3 := f.f.ConstBool(src.NoXPos, TypeBool, false) 437 v4 := f.f.ConstBool(src.NoXPos, TypeBool, true) 438 if v3.AuxInt != 0 { 439 t.Errorf("expected %s to have auxint of 0\n", v3.LongString()) 440 } 441 if v4.AuxInt != 1 { 442 t.Errorf("expected %s to have auxint of 1\n", v4.LongString()) 443 } 444 445 } 446 447 // opcodeMap returns a map from opcode to the number of times that opcode 448 // appears in the function. 449 func opcodeMap(f *Func) map[Op]int { 450 m := map[Op]int{} 451 for _, b := range f.Blocks { 452 for _, v := range b.Values { 453 m[v.Op]++ 454 } 455 } 456 return m 457 } 458 459 // opcodeCounts checks that the number of opcodes listed in m agree with the 460 // number of opcodes that appear in the function. 461 func checkOpcodeCounts(t *testing.T, f *Func, m map[Op]int) { 462 n := opcodeMap(f) 463 for op, cnt := range m { 464 if n[op] != cnt { 465 t.Errorf("%s appears %d times, want %d times", op, n[op], cnt) 466 } 467 } 468 }