github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/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 := testConfig(b)
   164  	fun := c.Fun("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 := c.Fun("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 := c.Fun("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 := c.Fun("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 := c.Fun("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 := c.Fun("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 := c.Fun("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 := c.Fun("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  		cfg := testConfig(t)
   415  		fun := cfg.Fun("1",
   416  			Bloc("1",
   417  				Valu("mem", OpInitMem, TypeMem, 0, nil),
   418  				Valu("p", OpConstBool, TypeBool, 1, nil),
   419  				Goto("4")),
   420  			Bloc("2",
   421  				Goto("11")),
   422  			Bloc("4",
   423  				If("p", if4[a], if4[1-a])), // 2, 5
   424  			Bloc("5",
   425  				If("p", if5[b], if5[1-b])), //15, 11
   426  			Bloc("10",
   427  				Exit("mem")),
   428  			Bloc("11",
   429  				Goto("15")),
   430  			Bloc("15",
   431  				If("p", if15[c], if15[1-c])), //19, 10
   432  			Bloc("19",
   433  				Goto("10")))
   434  		CheckFunc(fun.f)
   435  		verifyDominators(t, fun, dominators, doms)
   436  		verifyDominators(t, fun, dominatorsSimple, doms)
   437  	}
   438  }
   439  
   440  // generateDominatorMap uses dominatorsSimple to obtain a
   441  // reference dominator tree for testing faster algorithms.
   442  func generateDominatorMap(fut fun) map[string]string {
   443  	blockNames := map[*Block]string{}
   444  	for n, b := range fut.blocks {
   445  		blockNames[b] = n
   446  	}
   447  	referenceDom := dominatorsSimple(fut.f)
   448  	doms := make(map[string]string)
   449  	for _, b := range fut.f.Blocks {
   450  		if d := referenceDom[b.ID]; d != nil {
   451  			doms[blockNames[b]] = blockNames[d]
   452  		}
   453  	}
   454  	return doms
   455  }
   456  
   457  func TestDominatorsPostTrickyA(t *testing.T) {
   458  	testDominatorsPostTricky(t, "b8", "b11", "b10", "b8", "b14", "b15")
   459  }
   460  
   461  func TestDominatorsPostTrickyB(t *testing.T) {
   462  	testDominatorsPostTricky(t, "b11", "b8", "b10", "b8", "b14", "b15")
   463  }
   464  
   465  func TestDominatorsPostTrickyC(t *testing.T) {
   466  	testDominatorsPostTricky(t, "b8", "b11", "b8", "b10", "b14", "b15")
   467  }
   468  
   469  func TestDominatorsPostTrickyD(t *testing.T) {
   470  	testDominatorsPostTricky(t, "b11", "b8", "b8", "b10", "b14", "b15")
   471  }
   472  
   473  func TestDominatorsPostTrickyE(t *testing.T) {
   474  	testDominatorsPostTricky(t, "b8", "b11", "b10", "b8", "b15", "b14")
   475  }
   476  
   477  func TestDominatorsPostTrickyF(t *testing.T) {
   478  	testDominatorsPostTricky(t, "b11", "b8", "b10", "b8", "b15", "b14")
   479  }
   480  
   481  func TestDominatorsPostTrickyG(t *testing.T) {
   482  	testDominatorsPostTricky(t, "b8", "b11", "b8", "b10", "b15", "b14")
   483  }
   484  
   485  func TestDominatorsPostTrickyH(t *testing.T) {
   486  	testDominatorsPostTricky(t, "b11", "b8", "b8", "b10", "b15", "b14")
   487  }
   488  
   489  func testDominatorsPostTricky(t *testing.T, b7then, b7else, b12then, b12else, b13then, b13else string) {
   490  	c := testConfig(t)
   491  	fun := c.Fun("b1",
   492  		Bloc("b1",
   493  			Valu("mem", OpInitMem, TypeMem, 0, nil),
   494  			Valu("p", OpConstBool, TypeBool, 1, nil),
   495  			If("p", "b3", "b2")),
   496  		Bloc("b3",
   497  			If("p", "b5", "b6")),
   498  		Bloc("b5",
   499  			Goto("b7")),
   500  		Bloc("b7",
   501  			If("p", b7then, b7else)),
   502  		Bloc("b8",
   503  			Goto("b13")),
   504  		Bloc("b13",
   505  			If("p", b13then, b13else)),
   506  		Bloc("b14",
   507  			Goto("b10")),
   508  		Bloc("b15",
   509  			Goto("b16")),
   510  		Bloc("b16",
   511  			Goto("b9")),
   512  		Bloc("b9",
   513  			Goto("b7")),
   514  		Bloc("b11",
   515  			Goto("b12")),
   516  		Bloc("b12",
   517  			If("p", b12then, b12else)),
   518  		Bloc("b10",
   519  			Goto("b6")),
   520  		Bloc("b6",
   521  			Goto("b17")),
   522  		Bloc("b17",
   523  			Goto("b18")),
   524  		Bloc("b18",
   525  			If("p", "b22", "b19")),
   526  		Bloc("b22",
   527  			Goto("b23")),
   528  		Bloc("b23",
   529  			If("p", "b21", "b19")),
   530  		Bloc("b19",
   531  			If("p", "b24", "b25")),
   532  		Bloc("b24",
   533  			Goto("b26")),
   534  		Bloc("b26",
   535  			Goto("b25")),
   536  		Bloc("b25",
   537  			If("p", "b27", "b29")),
   538  		Bloc("b27",
   539  			Goto("b30")),
   540  		Bloc("b30",
   541  			Goto("b28")),
   542  		Bloc("b29",
   543  			Goto("b31")),
   544  		Bloc("b31",
   545  			Goto("b28")),
   546  		Bloc("b28",
   547  			If("p", "b32", "b33")),
   548  		Bloc("b32",
   549  			Goto("b21")),
   550  		Bloc("b21",
   551  			Goto("b47")),
   552  		Bloc("b47",
   553  			If("p", "b45", "b46")),
   554  		Bloc("b45",
   555  			Goto("b48")),
   556  		Bloc("b48",
   557  			Goto("b49")),
   558  		Bloc("b49",
   559  			If("p", "b50", "b51")),
   560  		Bloc("b50",
   561  			Goto("b52")),
   562  		Bloc("b52",
   563  			Goto("b53")),
   564  		Bloc("b53",
   565  			Goto("b51")),
   566  		Bloc("b51",
   567  			Goto("b54")),
   568  		Bloc("b54",
   569  			Goto("b46")),
   570  		Bloc("b46",
   571  			Exit("mem")),
   572  		Bloc("b33",
   573  			Goto("b34")),
   574  		Bloc("b34",
   575  			Goto("b37")),
   576  		Bloc("b37",
   577  			If("p", "b35", "b36")),
   578  		Bloc("b35",
   579  			Goto("b38")),
   580  		Bloc("b38",
   581  			Goto("b39")),
   582  		Bloc("b39",
   583  			If("p", "b40", "b41")),
   584  		Bloc("b40",
   585  			Goto("b42")),
   586  		Bloc("b42",
   587  			Goto("b43")),
   588  		Bloc("b43",
   589  			Goto("b41")),
   590  		Bloc("b41",
   591  			Goto("b44")),
   592  		Bloc("b44",
   593  			Goto("b36")),
   594  		Bloc("b36",
   595  			Goto("b20")),
   596  		Bloc("b20",
   597  			Goto("b18")),
   598  		Bloc("b2",
   599  			Goto("b4")),
   600  		Bloc("b4",
   601  			Exit("mem")))
   602  	CheckFunc(fun.f)
   603  	doms := generateDominatorMap(fun)
   604  	verifyDominators(t, fun, dominators, doms)
   605  }