github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/cmd/compile/internal/ssa/dom_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  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 TestPostDominators(t *testing.T) {
   376  	c := testConfig(t)
   377  	fun := Fun(c, "entry",
   378  		Bloc("entry",
   379  			Valu("mem", OpInitMem, TypeMem, 0, nil),
   380  			Valu("p", OpConstBool, TypeBool, 1, nil),
   381  			If("p", "a", "c")),
   382  		Bloc("a",
   383  			If("p", "b", "c")),
   384  		Bloc("b",
   385  			Goto("c")),
   386  		Bloc("c",
   387  			If("p", "b", "exit")),
   388  		Bloc("exit",
   389  			Exit("mem")))
   390  
   391  	doms := map[string]string{"entry": "c",
   392  		"a": "c",
   393  		"b": "c",
   394  		"c": "exit",
   395  	}
   396  
   397  	CheckFunc(fun.f)
   398  	verifyDominators(t, fun, postDominators, doms)
   399  }
   400  
   401  func TestInfiniteLoop(t *testing.T) {
   402  	c := testConfig(t)
   403  	// note lack of an exit block
   404  	fun := Fun(c, "entry",
   405  		Bloc("entry",
   406  			Valu("mem", OpInitMem, TypeMem, 0, nil),
   407  			Valu("p", OpConstBool, TypeBool, 1, nil),
   408  			Goto("a")),
   409  		Bloc("a",
   410  			Goto("b")),
   411  		Bloc("b",
   412  			Goto("a")))
   413  
   414  	CheckFunc(fun.f)
   415  	doms := map[string]string{"a": "entry",
   416  		"b": "a"}
   417  	verifyDominators(t, fun, dominators, doms)
   418  
   419  	// no exit block, so there are no post-dominators
   420  	postDoms := map[string]string{}
   421  	verifyDominators(t, fun, postDominators, postDoms)
   422  }