diff options
Diffstat (limited to 'src/cmd/compile/internal/ssa/dom_test.go')
-rw-r--r-- | src/cmd/compile/internal/ssa/dom_test.go | 608 |
1 files changed, 608 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/ssa/dom_test.go b/src/cmd/compile/internal/ssa/dom_test.go new file mode 100644 index 0000000..fa51718 --- /dev/null +++ b/src/cmd/compile/internal/ssa/dom_test.go @@ -0,0 +1,608 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssa + +import ( + "cmd/compile/internal/types" + "testing" +) + +func BenchmarkDominatorsLinear(b *testing.B) { benchmarkDominators(b, 10000, genLinear) } +func BenchmarkDominatorsFwdBack(b *testing.B) { benchmarkDominators(b, 10000, genFwdBack) } +func BenchmarkDominatorsManyPred(b *testing.B) { benchmarkDominators(b, 10000, genManyPred) } +func BenchmarkDominatorsMaxPred(b *testing.B) { benchmarkDominators(b, 10000, genMaxPred) } +func BenchmarkDominatorsMaxPredVal(b *testing.B) { benchmarkDominators(b, 10000, genMaxPredValue) } + +type blockGen func(size int) []bloc + +// genLinear creates an array of blocks that succeed one another +// b_n -> [b_n+1]. +func genLinear(size int) []bloc { + var blocs []bloc + blocs = append(blocs, + Bloc("entry", + Valu("mem", OpInitMem, types.TypeMem, 0, nil), + Goto(blockn(0)), + ), + ) + for i := 0; i < size; i++ { + blocs = append(blocs, Bloc(blockn(i), + Goto(blockn(i+1)))) + } + + blocs = append(blocs, + Bloc(blockn(size), Goto("exit")), + Bloc("exit", Exit("mem")), + ) + + return blocs +} + +// genLinear creates an array of blocks that alternate between +// b_n -> [b_n+1], b_n -> [b_n+1, b_n-1] , b_n -> [b_n+1, b_n+2] +func genFwdBack(size int) []bloc { + var blocs []bloc + blocs = append(blocs, + Bloc("entry", + Valu("mem", OpInitMem, types.TypeMem, 0, nil), + Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil), + Goto(blockn(0)), + ), + ) + for i := 0; i < size; i++ { + switch i % 2 { + case 0: + blocs = append(blocs, Bloc(blockn(i), + If("p", blockn(i+1), blockn(i+2)))) + case 1: + blocs = append(blocs, Bloc(blockn(i), + If("p", blockn(i+1), blockn(i-1)))) + } + } + + blocs = append(blocs, + Bloc(blockn(size), Goto("exit")), + Bloc("exit", Exit("mem")), + ) + + return blocs +} + +// genManyPred creates an array of blocks where 1/3rd have a successor of the +// first block, 1/3rd the last block, and the remaining third are plain. +func genManyPred(size int) []bloc { + var blocs []bloc + blocs = append(blocs, + Bloc("entry", + Valu("mem", OpInitMem, types.TypeMem, 0, nil), + Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil), + Goto(blockn(0)), + ), + ) + + // We want predecessor lists to be long, so 2/3rds of the blocks have a + // successor of the first or last block. + for i := 0; i < size; i++ { + switch i % 3 { + case 0: + blocs = append(blocs, Bloc(blockn(i), + Valu("a", OpConstBool, types.Types[types.TBOOL], 1, nil), + Goto(blockn(i+1)))) + case 1: + blocs = append(blocs, Bloc(blockn(i), + Valu("a", OpConstBool, types.Types[types.TBOOL], 1, nil), + If("p", blockn(i+1), blockn(0)))) + case 2: + blocs = append(blocs, Bloc(blockn(i), + Valu("a", OpConstBool, types.Types[types.TBOOL], 1, nil), + If("p", blockn(i+1), blockn(size)))) + } + } + + blocs = append(blocs, + Bloc(blockn(size), Goto("exit")), + Bloc("exit", Exit("mem")), + ) + + return blocs +} + +// genMaxPred maximizes the size of the 'exit' predecessor list. +func genMaxPred(size int) []bloc { + var blocs []bloc + blocs = append(blocs, + Bloc("entry", + Valu("mem", OpInitMem, types.TypeMem, 0, nil), + Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil), + Goto(blockn(0)), + ), + ) + + for i := 0; i < size; i++ { + blocs = append(blocs, Bloc(blockn(i), + If("p", blockn(i+1), "exit"))) + } + + blocs = append(blocs, + Bloc(blockn(size), Goto("exit")), + Bloc("exit", Exit("mem")), + ) + + return blocs +} + +// genMaxPredValue is identical to genMaxPred but contains an +// additional value. +func genMaxPredValue(size int) []bloc { + var blocs []bloc + blocs = append(blocs, + Bloc("entry", + Valu("mem", OpInitMem, types.TypeMem, 0, nil), + Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil), + Goto(blockn(0)), + ), + ) + + for i := 0; i < size; i++ { + blocs = append(blocs, Bloc(blockn(i), + Valu("a", OpConstBool, types.Types[types.TBOOL], 1, nil), + If("p", blockn(i+1), "exit"))) + } + + blocs = append(blocs, + Bloc(blockn(size), Goto("exit")), + Bloc("exit", Exit("mem")), + ) + + return blocs +} + +// sink for benchmark +var domBenchRes []*Block + +func benchmarkDominators(b *testing.B, size int, bg blockGen) { + c := testConfig(b) + fun := c.Fun("entry", bg(size)...) + + CheckFunc(fun.f) + b.SetBytes(int64(size)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + domBenchRes = dominators(fun.f) + } +} + +type domFunc func(f *Func) []*Block + +// verifyDominators verifies that the dominators of fut (function under test) +// as determined by domFn, match the map node->dominator +func verifyDominators(t *testing.T, fut fun, domFn domFunc, doms map[string]string) { + blockNames := map[*Block]string{} + for n, b := range fut.blocks { + blockNames[b] = n + } + + calcDom := domFn(fut.f) + + for n, d := range doms { + nblk, ok := fut.blocks[n] + if !ok { + t.Errorf("invalid block name %s", n) + } + dblk, ok := fut.blocks[d] + if !ok { + t.Errorf("invalid block name %s", d) + } + + domNode := calcDom[nblk.ID] + switch { + case calcDom[nblk.ID] == dblk: + calcDom[nblk.ID] = nil + continue + case calcDom[nblk.ID] != dblk: + t.Errorf("expected %s as dominator of %s, found %s", d, n, blockNames[domNode]) + default: + t.Fatal("unexpected dominator condition") + } + } + + for id, d := range calcDom { + // If nil, we've already verified it + if d == nil { + continue + } + for _, b := range fut.blocks { + if int(b.ID) == id { + t.Errorf("unexpected dominator of %s for %s", blockNames[d], blockNames[b]) + } + } + } + +} + +func TestDominatorsSingleBlock(t *testing.T) { + c := testConfig(t) + fun := c.Fun("entry", + Bloc("entry", + Valu("mem", OpInitMem, types.TypeMem, 0, nil), + Exit("mem"))) + + doms := map[string]string{} + + CheckFunc(fun.f) + verifyDominators(t, fun, dominators, doms) + verifyDominators(t, fun, dominatorsSimple, doms) + +} + +func TestDominatorsSimple(t *testing.T) { + c := testConfig(t) + fun := c.Fun("entry", + Bloc("entry", + Valu("mem", OpInitMem, types.TypeMem, 0, nil), + Goto("a")), + Bloc("a", + Goto("b")), + Bloc("b", + Goto("c")), + Bloc("c", + Goto("exit")), + Bloc("exit", + Exit("mem"))) + + doms := map[string]string{ + "a": "entry", + "b": "a", + "c": "b", + "exit": "c", + } + + CheckFunc(fun.f) + verifyDominators(t, fun, dominators, doms) + verifyDominators(t, fun, dominatorsSimple, doms) + +} + +func TestDominatorsMultPredFwd(t *testing.T) { + c := testConfig(t) + fun := c.Fun("entry", + Bloc("entry", + Valu("mem", OpInitMem, types.TypeMem, 0, nil), + Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil), + If("p", "a", "c")), + Bloc("a", + If("p", "b", "c")), + Bloc("b", + Goto("c")), + Bloc("c", + Goto("exit")), + Bloc("exit", + Exit("mem"))) + + doms := map[string]string{ + "a": "entry", + "b": "a", + "c": "entry", + "exit": "c", + } + + CheckFunc(fun.f) + verifyDominators(t, fun, dominators, doms) + verifyDominators(t, fun, dominatorsSimple, doms) +} + +func TestDominatorsDeadCode(t *testing.T) { + c := testConfig(t) + fun := c.Fun("entry", + Bloc("entry", + Valu("mem", OpInitMem, types.TypeMem, 0, nil), + Valu("p", OpConstBool, types.Types[types.TBOOL], 0, nil), + If("p", "b3", "b5")), + Bloc("b2", Exit("mem")), + Bloc("b3", Goto("b2")), + Bloc("b4", Goto("b2")), + Bloc("b5", Goto("b2"))) + + doms := map[string]string{ + "b2": "entry", + "b3": "entry", + "b5": "entry", + } + + CheckFunc(fun.f) + verifyDominators(t, fun, dominators, doms) + verifyDominators(t, fun, dominatorsSimple, doms) +} + +func TestDominatorsMultPredRev(t *testing.T) { + c := testConfig(t) + fun := c.Fun("entry", + Bloc("entry", + Goto("first")), + Bloc("first", + Valu("mem", OpInitMem, types.TypeMem, 0, nil), + Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil), + Goto("a")), + Bloc("a", + If("p", "b", "first")), + Bloc("b", + Goto("c")), + Bloc("c", + If("p", "exit", "b")), + Bloc("exit", + Exit("mem"))) + + doms := map[string]string{ + "first": "entry", + "a": "first", + "b": "a", + "c": "b", + "exit": "c", + } + + CheckFunc(fun.f) + verifyDominators(t, fun, dominators, doms) + verifyDominators(t, fun, dominatorsSimple, doms) +} + +func TestDominatorsMultPred(t *testing.T) { + c := testConfig(t) + fun := c.Fun("entry", + Bloc("entry", + Valu("mem", OpInitMem, types.TypeMem, 0, nil), + Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil), + If("p", "a", "c")), + Bloc("a", + If("p", "b", "c")), + Bloc("b", + Goto("c")), + Bloc("c", + If("p", "b", "exit")), + Bloc("exit", + Exit("mem"))) + + doms := map[string]string{ + "a": "entry", + "b": "entry", + "c": "entry", + "exit": "c", + } + + CheckFunc(fun.f) + verifyDominators(t, fun, dominators, doms) + verifyDominators(t, fun, dominatorsSimple, doms) +} + +func TestInfiniteLoop(t *testing.T) { + c := testConfig(t) + // note lack of an exit block + fun := c.Fun("entry", + Bloc("entry", + Valu("mem", OpInitMem, types.TypeMem, 0, nil), + Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil), + Goto("a")), + Bloc("a", + Goto("b")), + Bloc("b", + Goto("a"))) + + CheckFunc(fun.f) + doms := map[string]string{"a": "entry", + "b": "a"} + verifyDominators(t, fun, dominators, doms) +} + +func TestDomTricky(t *testing.T) { + doms := map[string]string{ + "4": "1", + "2": "4", + "5": "4", + "11": "4", + "15": "4", // the incorrect answer is "5" + "10": "15", + "19": "15", + } + + if4 := [2]string{"2", "5"} + if5 := [2]string{"15", "11"} + if15 := [2]string{"19", "10"} + + for i := 0; i < 8; i++ { + a := 1 & i + b := 1 & i >> 1 + c := 1 & i >> 2 + + cfg := testConfig(t) + fun := cfg.Fun("1", + Bloc("1", + Valu("mem", OpInitMem, types.TypeMem, 0, nil), + Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil), + Goto("4")), + Bloc("2", + Goto("11")), + Bloc("4", + If("p", if4[a], if4[1-a])), // 2, 5 + Bloc("5", + If("p", if5[b], if5[1-b])), //15, 11 + Bloc("10", + Exit("mem")), + Bloc("11", + Goto("15")), + Bloc("15", + If("p", if15[c], if15[1-c])), //19, 10 + Bloc("19", + Goto("10"))) + CheckFunc(fun.f) + verifyDominators(t, fun, dominators, doms) + verifyDominators(t, fun, dominatorsSimple, doms) + } +} + +// generateDominatorMap uses dominatorsSimple to obtain a +// reference dominator tree for testing faster algorithms. +func generateDominatorMap(fut fun) map[string]string { + blockNames := map[*Block]string{} + for n, b := range fut.blocks { + blockNames[b] = n + } + referenceDom := dominatorsSimple(fut.f) + doms := make(map[string]string) + for _, b := range fut.f.Blocks { + if d := referenceDom[b.ID]; d != nil { + doms[blockNames[b]] = blockNames[d] + } + } + return doms +} + +func TestDominatorsPostTrickyA(t *testing.T) { + testDominatorsPostTricky(t, "b8", "b11", "b10", "b8", "b14", "b15") +} + +func TestDominatorsPostTrickyB(t *testing.T) { + testDominatorsPostTricky(t, "b11", "b8", "b10", "b8", "b14", "b15") +} + +func TestDominatorsPostTrickyC(t *testing.T) { + testDominatorsPostTricky(t, "b8", "b11", "b8", "b10", "b14", "b15") +} + +func TestDominatorsPostTrickyD(t *testing.T) { + testDominatorsPostTricky(t, "b11", "b8", "b8", "b10", "b14", "b15") +} + +func TestDominatorsPostTrickyE(t *testing.T) { + testDominatorsPostTricky(t, "b8", "b11", "b10", "b8", "b15", "b14") +} + +func TestDominatorsPostTrickyF(t *testing.T) { + testDominatorsPostTricky(t, "b11", "b8", "b10", "b8", "b15", "b14") +} + +func TestDominatorsPostTrickyG(t *testing.T) { + testDominatorsPostTricky(t, "b8", "b11", "b8", "b10", "b15", "b14") +} + +func TestDominatorsPostTrickyH(t *testing.T) { + testDominatorsPostTricky(t, "b11", "b8", "b8", "b10", "b15", "b14") +} + +func testDominatorsPostTricky(t *testing.T, b7then, b7else, b12then, b12else, b13then, b13else string) { + c := testConfig(t) + fun := c.Fun("b1", + Bloc("b1", + Valu("mem", OpInitMem, types.TypeMem, 0, nil), + Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil), + If("p", "b3", "b2")), + Bloc("b3", + If("p", "b5", "b6")), + Bloc("b5", + Goto("b7")), + Bloc("b7", + If("p", b7then, b7else)), + Bloc("b8", + Goto("b13")), + Bloc("b13", + If("p", b13then, b13else)), + Bloc("b14", + Goto("b10")), + Bloc("b15", + Goto("b16")), + Bloc("b16", + Goto("b9")), + Bloc("b9", + Goto("b7")), + Bloc("b11", + Goto("b12")), + Bloc("b12", + If("p", b12then, b12else)), + Bloc("b10", + Goto("b6")), + Bloc("b6", + Goto("b17")), + Bloc("b17", + Goto("b18")), + Bloc("b18", + If("p", "b22", "b19")), + Bloc("b22", + Goto("b23")), + Bloc("b23", + If("p", "b21", "b19")), + Bloc("b19", + If("p", "b24", "b25")), + Bloc("b24", + Goto("b26")), + Bloc("b26", + Goto("b25")), + Bloc("b25", + If("p", "b27", "b29")), + Bloc("b27", + Goto("b30")), + Bloc("b30", + Goto("b28")), + Bloc("b29", + Goto("b31")), + Bloc("b31", + Goto("b28")), + Bloc("b28", + If("p", "b32", "b33")), + Bloc("b32", + Goto("b21")), + Bloc("b21", + Goto("b47")), + Bloc("b47", + If("p", "b45", "b46")), + Bloc("b45", + Goto("b48")), + Bloc("b48", + Goto("b49")), + Bloc("b49", + If("p", "b50", "b51")), + Bloc("b50", + Goto("b52")), + Bloc("b52", + Goto("b53")), + Bloc("b53", + Goto("b51")), + Bloc("b51", + Goto("b54")), + Bloc("b54", + Goto("b46")), + Bloc("b46", + Exit("mem")), + Bloc("b33", + Goto("b34")), + Bloc("b34", + Goto("b37")), + Bloc("b37", + If("p", "b35", "b36")), + Bloc("b35", + Goto("b38")), + Bloc("b38", + Goto("b39")), + Bloc("b39", + If("p", "b40", "b41")), + Bloc("b40", + Goto("b42")), + Bloc("b42", + Goto("b43")), + Bloc("b43", + Goto("b41")), + Bloc("b41", + Goto("b44")), + Bloc("b44", + Goto("b36")), + Bloc("b36", + Goto("b20")), + Bloc("b20", + Goto("b18")), + Bloc("b2", + Goto("b4")), + Bloc("b4", + Exit("mem"))) + CheckFunc(fun.f) + doms := generateDominatorMap(fun) + verifyDominators(t, fun, dominators, doms) +} |