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