diff options
Diffstat (limited to 'src/cmd/compile/internal/ssa/dom_test.go')
| -rw-r--r-- | src/cmd/compile/internal/ssa/dom_test.go | 422 |
1 files changed, 422 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 0000000000..0328655b6a --- /dev/null +++ b/src/cmd/compile/internal/ssa/dom_test.go @@ -0,0 +1,422 @@ +// 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 "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, 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, TypeMem, 0, nil), + Valu("p", OpConstBool, TypeBool, 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 sucessor 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, TypeMem, 0, nil), + Valu("p", OpConstBool, TypeBool, 1, nil), + Goto(blockn(0)), + ), + ) + + // We want predecessor lists to be long, so 2/3rds of the blocks have a + // sucessor 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, TypeBool, 1, nil), + Goto(blockn(i+1)))) + case 1: + blocs = append(blocs, Bloc(blockn(i), + Valu("a", OpConstBool, TypeBool, 1, nil), + If("p", blockn(i+1), blockn(0)))) + case 2: + blocs = append(blocs, Bloc(blockn(i), + Valu("a", OpConstBool, TypeBool, 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, TypeMem, 0, nil), + Valu("p", OpConstBool, TypeBool, 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, TypeMem, 0, nil), + Valu("p", OpConstBool, TypeBool, 1, nil), + Goto(blockn(0)), + ), + ) + + for i := 0; i < size; i++ { + blocs = append(blocs, Bloc(blockn(i), + Valu("a", OpConstBool, TypeBool, 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 := NewConfig("amd64", DummyFrontend{b}, nil, true) + fun := Fun(c, "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 := Fun(c, "entry", + Bloc("entry", + Valu("mem", OpInitMem, 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 := Fun(c, "entry", + Bloc("entry", + Valu("mem", OpInitMem, 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 := Fun(c, "entry", + Bloc("entry", + Valu("mem", OpInitMem, TypeMem, 0, nil), + Valu("p", OpConstBool, TypeBool, 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 := Fun(c, "entry", + Bloc("entry", + Valu("mem", OpInitMem, TypeMem, 0, nil), + Valu("p", OpConstBool, TypeBool, 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 := Fun(c, "entry", + Bloc("entry", + Goto("first")), + Bloc("first", + Valu("mem", OpInitMem, TypeMem, 0, nil), + Valu("p", OpConstBool, TypeBool, 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 := Fun(c, "entry", + Bloc("entry", + Valu("mem", OpInitMem, TypeMem, 0, nil), + Valu("p", OpConstBool, TypeBool, 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 TestPostDominators(t *testing.T) { + c := testConfig(t) + fun := Fun(c, "entry", + Bloc("entry", + Valu("mem", OpInitMem, TypeMem, 0, nil), + Valu("p", OpConstBool, TypeBool, 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{"entry": "c", + "a": "c", + "b": "c", + "c": "exit", + } + + CheckFunc(fun.f) + verifyDominators(t, fun, postDominators, doms) +} + +func TestInfiniteLoop(t *testing.T) { + c := testConfig(t) + // note lack of an exit block + fun := Fun(c, "entry", + Bloc("entry", + Valu("mem", OpInitMem, TypeMem, 0, nil), + Valu("p", OpConstBool, TypeBool, 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) + + // no exit block, so there are no post-dominators + postDoms := map[string]string{} + verifyDominators(t, fun, postDominators, postDoms) +} |
