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 package ssa 6 7 import "testing" 8 9 func BenchmarkDominatorsLinear(b *testing.B) { benchmarkDominators(b, 10000, genLinear) } 10 func BenchmarkDominatorsFwdBack(b *testing.B) { benchmarkDominators(b, 10000, genFwdBack) } 11 func BenchmarkDominatorsManyPred(b *testing.B) { benchmarkDominators(b, 10000, genManyPred) } 12 func BenchmarkDominatorsMaxPred(b *testing.B) { benchmarkDominators(b, 10000, genMaxPred) } 13 func BenchmarkDominatorsMaxPredVal(b *testing.B) { benchmarkDominators(b, 10000, genMaxPredValue) } 14 15 type blockGen func(size int) []bloc 16 17 // genLinear creates an array of blocks that succeed one another 18 // b_n -> [b_n+1]. 19 func genLinear(size int) []bloc { 20 var blocs []bloc 21 blocs = append(blocs, 22 Bloc("entry", 23 Valu("mem", OpInitMem, TypeMem, 0, nil), 24 Goto(blockn(0)), 25 ), 26 ) 27 for i := 0; i < size; i++ { 28 blocs = append(blocs, Bloc(blockn(i), 29 Goto(blockn(i+1)))) 30 } 31 32 blocs = append(blocs, 33 Bloc(blockn(size), Goto("exit")), 34 Bloc("exit", Exit("mem")), 35 ) 36 37 return blocs 38 } 39 40 // genLinear creates an array of blocks that alternate between 41 // b_n -> [b_n+1], b_n -> [b_n+1, b_n-1] , b_n -> [b_n+1, b_n+2] 42 func genFwdBack(size int) []bloc { 43 var blocs []bloc 44 blocs = append(blocs, 45 Bloc("entry", 46 Valu("mem", OpInitMem, TypeMem, 0, nil), 47 Valu("p", OpConstBool, TypeBool, 1, nil), 48 Goto(blockn(0)), 49 ), 50 ) 51 for i := 0; i < size; i++ { 52 switch i % 2 { 53 case 0: 54 blocs = append(blocs, Bloc(blockn(i), 55 If("p", blockn(i+1), blockn(i+2)))) 56 case 1: 57 blocs = append(blocs, Bloc(blockn(i), 58 If("p", blockn(i+1), blockn(i-1)))) 59 } 60 } 61 62 blocs = append(blocs, 63 Bloc(blockn(size), Goto("exit")), 64 Bloc("exit", Exit("mem")), 65 ) 66 67 return blocs 68 } 69 70 // genManyPred creates an array of blocks where 1/3rd have a successor of the 71 // first block, 1/3rd the last block, and the remaining third are plain. 72 func genManyPred(size int) []bloc { 73 var blocs []bloc 74 blocs = append(blocs, 75 Bloc("entry", 76 Valu("mem", OpInitMem, TypeMem, 0, nil), 77 Valu("p", OpConstBool, TypeBool, 1, nil), 78 Goto(blockn(0)), 79 ), 80 ) 81 82 // We want predecessor lists to be long, so 2/3rds of the blocks have a 83 // successor of the first or last block. 84 for i := 0; i < size; i++ { 85 switch i % 3 { 86 case 0: 87 blocs = append(blocs, Bloc(blockn(i), 88 Valu("a", OpConstBool, TypeBool, 1, nil), 89 Goto(blockn(i+1)))) 90 case 1: 91 blocs = append(blocs, Bloc(blockn(i), 92 Valu("a", OpConstBool, TypeBool, 1, nil), 93 If("p", blockn(i+1), blockn(0)))) 94 case 2: 95 blocs = append(blocs, Bloc(blockn(i), 96 Valu("a", OpConstBool, TypeBool, 1, nil), 97 If("p", blockn(i+1), blockn(size)))) 98 } 99 } 100 101 blocs = append(blocs, 102 Bloc(blockn(size), Goto("exit")), 103 Bloc("exit", Exit("mem")), 104 ) 105 106 return blocs 107 } 108 109 // genMaxPred maximizes the size of the 'exit' predecessor list. 110 func genMaxPred(size int) []bloc { 111 var blocs []bloc 112 blocs = append(blocs, 113 Bloc("entry", 114 Valu("mem", OpInitMem, TypeMem, 0, nil), 115 Valu("p", OpConstBool, TypeBool, 1, nil), 116 Goto(blockn(0)), 117 ), 118 ) 119 120 for i := 0; i < size; i++ { 121 blocs = append(blocs, Bloc(blockn(i), 122 If("p", blockn(i+1), "exit"))) 123 } 124 125 blocs = append(blocs, 126 Bloc(blockn(size), Goto("exit")), 127 Bloc("exit", Exit("mem")), 128 ) 129 130 return blocs 131 } 132 133 // genMaxPredValue is identical to genMaxPred but contains an 134 // additional value. 135 func genMaxPredValue(size int) []bloc { 136 var blocs []bloc 137 blocs = append(blocs, 138 Bloc("entry", 139 Valu("mem", OpInitMem, TypeMem, 0, nil), 140 Valu("p", OpConstBool, TypeBool, 1, nil), 141 Goto(blockn(0)), 142 ), 143 ) 144 145 for i := 0; i < size; i++ { 146 blocs = append(blocs, Bloc(blockn(i), 147 Valu("a", OpConstBool, TypeBool, 1, nil), 148 If("p", blockn(i+1), "exit"))) 149 } 150 151 blocs = append(blocs, 152 Bloc(blockn(size), Goto("exit")), 153 Bloc("exit", Exit("mem")), 154 ) 155 156 return blocs 157 } 158 159 // sink for benchmark 160 var domBenchRes []*Block 161 162 func benchmarkDominators(b *testing.B, size int, bg blockGen) { 163 c := NewConfig("amd64", DummyFrontend{b}, nil, true) 164 fun := Fun(c, "entry", bg(size)...) 165 166 CheckFunc(fun.f) 167 b.SetBytes(int64(size)) 168 b.ResetTimer() 169 for i := 0; i < b.N; i++ { 170 domBenchRes = dominators(fun.f) 171 } 172 } 173 174 type domFunc func(f *Func) []*Block 175 176 // verifyDominators verifies that the dominators of fut (function under test) 177 // as determined by domFn, match the map node->dominator 178 func verifyDominators(t *testing.T, fut fun, domFn domFunc, doms map[string]string) { 179 blockNames := map[*Block]string{} 180 for n, b := range fut.blocks { 181 blockNames[b] = n 182 } 183 184 calcDom := domFn(fut.f) 185 186 for n, d := range doms { 187 nblk, ok := fut.blocks[n] 188 if !ok { 189 t.Errorf("invalid block name %s", n) 190 } 191 dblk, ok := fut.blocks[d] 192 if !ok { 193 t.Errorf("invalid block name %s", d) 194 } 195 196 domNode := calcDom[nblk.ID] 197 switch { 198 case calcDom[nblk.ID] == dblk: 199 calcDom[nblk.ID] = nil 200 continue 201 case calcDom[nblk.ID] != dblk: 202 t.Errorf("expected %s as dominator of %s, found %s", d, n, blockNames[domNode]) 203 default: 204 t.Fatal("unexpected dominator condition") 205 } 206 } 207 208 for id, d := range calcDom { 209 // If nil, we've already verified it 210 if d == nil { 211 continue 212 } 213 for _, b := range fut.blocks { 214 if int(b.ID) == id { 215 t.Errorf("unexpected dominator of %s for %s", blockNames[d], blockNames[b]) 216 } 217 } 218 } 219 220 } 221 222 func TestDominatorsSingleBlock(t *testing.T) { 223 c := testConfig(t) 224 fun := Fun(c, "entry", 225 Bloc("entry", 226 Valu("mem", OpInitMem, TypeMem, 0, nil), 227 Exit("mem"))) 228 229 doms := map[string]string{} 230 231 CheckFunc(fun.f) 232 verifyDominators(t, fun, dominators, doms) 233 verifyDominators(t, fun, dominatorsSimple, doms) 234 235 } 236 237 func TestDominatorsSimple(t *testing.T) { 238 c := testConfig(t) 239 fun := Fun(c, "entry", 240 Bloc("entry", 241 Valu("mem", OpInitMem, TypeMem, 0, nil), 242 Goto("a")), 243 Bloc("a", 244 Goto("b")), 245 Bloc("b", 246 Goto("c")), 247 Bloc("c", 248 Goto("exit")), 249 Bloc("exit", 250 Exit("mem"))) 251 252 doms := map[string]string{ 253 "a": "entry", 254 "b": "a", 255 "c": "b", 256 "exit": "c", 257 } 258 259 CheckFunc(fun.f) 260 verifyDominators(t, fun, dominators, doms) 261 verifyDominators(t, fun, dominatorsSimple, doms) 262 263 } 264 265 func TestDominatorsMultPredFwd(t *testing.T) { 266 c := testConfig(t) 267 fun := Fun(c, "entry", 268 Bloc("entry", 269 Valu("mem", OpInitMem, TypeMem, 0, nil), 270 Valu("p", OpConstBool, TypeBool, 1, nil), 271 If("p", "a", "c")), 272 Bloc("a", 273 If("p", "b", "c")), 274 Bloc("b", 275 Goto("c")), 276 Bloc("c", 277 Goto("exit")), 278 Bloc("exit", 279 Exit("mem"))) 280 281 doms := map[string]string{ 282 "a": "entry", 283 "b": "a", 284 "c": "entry", 285 "exit": "c", 286 } 287 288 CheckFunc(fun.f) 289 verifyDominators(t, fun, dominators, doms) 290 verifyDominators(t, fun, dominatorsSimple, doms) 291 } 292 293 func TestDominatorsDeadCode(t *testing.T) { 294 c := testConfig(t) 295 fun := Fun(c, "entry", 296 Bloc("entry", 297 Valu("mem", OpInitMem, TypeMem, 0, nil), 298 Valu("p", OpConstBool, TypeBool, 0, nil), 299 If("p", "b3", "b5")), 300 Bloc("b2", Exit("mem")), 301 Bloc("b3", Goto("b2")), 302 Bloc("b4", Goto("b2")), 303 Bloc("b5", Goto("b2"))) 304 305 doms := map[string]string{ 306 "b2": "entry", 307 "b3": "entry", 308 "b5": "entry", 309 } 310 311 CheckFunc(fun.f) 312 verifyDominators(t, fun, dominators, doms) 313 verifyDominators(t, fun, dominatorsSimple, doms) 314 } 315 316 func TestDominatorsMultPredRev(t *testing.T) { 317 c := testConfig(t) 318 fun := Fun(c, "entry", 319 Bloc("entry", 320 Goto("first")), 321 Bloc("first", 322 Valu("mem", OpInitMem, TypeMem, 0, nil), 323 Valu("p", OpConstBool, TypeBool, 1, nil), 324 Goto("a")), 325 Bloc("a", 326 If("p", "b", "first")), 327 Bloc("b", 328 Goto("c")), 329 Bloc("c", 330 If("p", "exit", "b")), 331 Bloc("exit", 332 Exit("mem"))) 333 334 doms := map[string]string{ 335 "first": "entry", 336 "a": "first", 337 "b": "a", 338 "c": "b", 339 "exit": "c", 340 } 341 342 CheckFunc(fun.f) 343 verifyDominators(t, fun, dominators, doms) 344 verifyDominators(t, fun, dominatorsSimple, doms) 345 } 346 347 func TestDominatorsMultPred(t *testing.T) { 348 c := testConfig(t) 349 fun := Fun(c, "entry", 350 Bloc("entry", 351 Valu("mem", OpInitMem, TypeMem, 0, nil), 352 Valu("p", OpConstBool, TypeBool, 1, nil), 353 If("p", "a", "c")), 354 Bloc("a", 355 If("p", "b", "c")), 356 Bloc("b", 357 Goto("c")), 358 Bloc("c", 359 If("p", "b", "exit")), 360 Bloc("exit", 361 Exit("mem"))) 362 363 doms := map[string]string{ 364 "a": "entry", 365 "b": "entry", 366 "c": "entry", 367 "exit": "c", 368 } 369 370 CheckFunc(fun.f) 371 verifyDominators(t, fun, dominators, doms) 372 verifyDominators(t, fun, dominatorsSimple, doms) 373 } 374 375 func TestInfiniteLoop(t *testing.T) { 376 c := testConfig(t) 377 // note lack of an exit block 378 fun := Fun(c, "entry", 379 Bloc("entry", 380 Valu("mem", OpInitMem, TypeMem, 0, nil), 381 Valu("p", OpConstBool, TypeBool, 1, nil), 382 Goto("a")), 383 Bloc("a", 384 Goto("b")), 385 Bloc("b", 386 Goto("a"))) 387 388 CheckFunc(fun.f) 389 doms := map[string]string{"a": "entry", 390 "b": "a"} 391 verifyDominators(t, fun, dominators, doms) 392 } 393 394 func TestDomTricky(t *testing.T) { 395 doms := map[string]string{ 396 "4": "1", 397 "2": "4", 398 "5": "4", 399 "11": "4", 400 "15": "4", // the incorrect answer is "5" 401 "10": "15", 402 "19": "15", 403 } 404 405 if4 := [2]string{"2", "5"} 406 if5 := [2]string{"15", "11"} 407 if15 := [2]string{"19", "10"} 408 409 for i := 0; i < 8; i++ { 410 a := 1 & i 411 b := 1 & i >> 1 412 c := 1 & i >> 2 413 414 fun := Fun(testConfig(t), "1", 415 Bloc("1", 416 Valu("mem", OpInitMem, TypeMem, 0, nil), 417 Valu("p", OpConstBool, TypeBool, 1, nil), 418 Goto("4")), 419 Bloc("2", 420 Goto("11")), 421 Bloc("4", 422 If("p", if4[a], if4[1-a])), // 2, 5 423 Bloc("5", 424 If("p", if5[b], if5[1-b])), //15, 11 425 Bloc("10", 426 Exit("mem")), 427 Bloc("11", 428 Goto("15")), 429 Bloc("15", 430 If("p", if15[c], if15[1-c])), //19, 10 431 Bloc("19", 432 Goto("10"))) 433 CheckFunc(fun.f) 434 verifyDominators(t, fun, dominators, doms) 435 verifyDominators(t, fun, dominatorsSimple, doms) 436 } 437 } 438 439 // generateDominatorMap uses dominatorsSimple to obtain a 440 // reference dominator tree for testing faster algorithms. 441 func generateDominatorMap(fut fun) map[string]string { 442 blockNames := map[*Block]string{} 443 for n, b := range fut.blocks { 444 blockNames[b] = n 445 } 446 referenceDom := dominatorsSimple(fut.f) 447 doms := make(map[string]string) 448 for _, b := range fut.f.Blocks { 449 if d := referenceDom[b.ID]; d != nil { 450 doms[blockNames[b]] = blockNames[d] 451 } 452 } 453 return doms 454 } 455 456 func TestDominatorsPostTricky(t *testing.T) { 457 c := testConfig(t) 458 fun := Fun(c, "b1", 459 Bloc("b1", 460 Valu("mem", OpInitMem, TypeMem, 0, nil), 461 Valu("p", OpConstBool, TypeBool, 1, nil), 462 If("p", "b3", "b2")), 463 Bloc("b3", 464 If("p", "b5", "b6")), 465 Bloc("b5", 466 Goto("b7")), 467 Bloc("b7", 468 If("p", "b8", "b11")), 469 Bloc("b8", 470 Goto("b13")), 471 Bloc("b13", 472 If("p", "b14", "b15")), 473 Bloc("b14", 474 Goto("b10")), 475 Bloc("b15", 476 Goto("b16")), 477 Bloc("b16", 478 Goto("b9")), 479 Bloc("b9", 480 Goto("b7")), 481 Bloc("b11", 482 Goto("b12")), 483 Bloc("b12", 484 If("p", "b10", "b8")), 485 Bloc("b10", 486 Goto("b6")), 487 Bloc("b6", 488 Goto("b17")), 489 Bloc("b17", 490 Goto("b18")), 491 Bloc("b18", 492 If("p", "b22", "b19")), 493 Bloc("b22", 494 Goto("b23")), 495 Bloc("b23", 496 If("p", "b21", "b19")), 497 Bloc("b19", 498 If("p", "b24", "b25")), 499 Bloc("b24", 500 Goto("b26")), 501 Bloc("b26", 502 Goto("b25")), 503 Bloc("b25", 504 If("p", "b27", "b29")), 505 Bloc("b27", 506 Goto("b30")), 507 Bloc("b30", 508 Goto("b28")), 509 Bloc("b29", 510 Goto("b31")), 511 Bloc("b31", 512 Goto("b28")), 513 Bloc("b28", 514 If("p", "b32", "b33")), 515 Bloc("b32", 516 Goto("b21")), 517 Bloc("b21", 518 Goto("b47")), 519 Bloc("b47", 520 If("p", "b45", "b46")), 521 Bloc("b45", 522 Goto("b48")), 523 Bloc("b48", 524 Goto("b49")), 525 Bloc("b49", 526 If("p", "b50", "b51")), 527 Bloc("b50", 528 Goto("b52")), 529 Bloc("b52", 530 Goto("b53")), 531 Bloc("b53", 532 Goto("b51")), 533 Bloc("b51", 534 Goto("b54")), 535 Bloc("b54", 536 Goto("b46")), 537 Bloc("b46", 538 Exit("mem")), 539 Bloc("b33", 540 Goto("b34")), 541 Bloc("b34", 542 Goto("b37")), 543 Bloc("b37", 544 If("p", "b35", "b36")), 545 Bloc("b35", 546 Goto("b38")), 547 Bloc("b38", 548 Goto("b39")), 549 Bloc("b39", 550 If("p", "b40", "b41")), 551 Bloc("b40", 552 Goto("b42")), 553 Bloc("b42", 554 Goto("b43")), 555 Bloc("b43", 556 Goto("b41")), 557 Bloc("b41", 558 Goto("b44")), 559 Bloc("b44", 560 Goto("b36")), 561 Bloc("b36", 562 Goto("b20")), 563 Bloc("b20", 564 Goto("b18")), 565 Bloc("b2", 566 Goto("b4")), 567 Bloc("b4", 568 Exit("mem"))) 569 CheckFunc(fun.f) 570 doms := generateDominatorMap(fun) 571 verifyDominators(t, fun, dominators, doms) 572 } 573