github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/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 TestDominatorsPostTricky(t *testing.T) {
   458  	c := testConfig(t)
   459  	fun := c.Fun("b1",
   460  		Bloc("b1",
   461  			Valu("mem", OpInitMem, TypeMem, 0, nil),
   462  			Valu("p", OpConstBool, TypeBool, 1, nil),
   463  			If("p", "b3", "b2")),
   464  		Bloc("b3",
   465  			If("p", "b5", "b6")),
   466  		Bloc("b5",
   467  			Goto("b7")),
   468  		Bloc("b7",
   469  			If("p", "b8", "b11")),
   470  		Bloc("b8",
   471  			Goto("b13")),
   472  		Bloc("b13",
   473  			If("p", "b14", "b15")),
   474  		Bloc("b14",
   475  			Goto("b10")),
   476  		Bloc("b15",
   477  			Goto("b16")),
   478  		Bloc("b16",
   479  			Goto("b9")),
   480  		Bloc("b9",
   481  			Goto("b7")),
   482  		Bloc("b11",
   483  			Goto("b12")),
   484  		Bloc("b12",
   485  			If("p", "b10", "b8")),
   486  		Bloc("b10",
   487  			Goto("b6")),
   488  		Bloc("b6",
   489  			Goto("b17")),
   490  		Bloc("b17",
   491  			Goto("b18")),
   492  		Bloc("b18",
   493  			If("p", "b22", "b19")),
   494  		Bloc("b22",
   495  			Goto("b23")),
   496  		Bloc("b23",
   497  			If("p", "b21", "b19")),
   498  		Bloc("b19",
   499  			If("p", "b24", "b25")),
   500  		Bloc("b24",
   501  			Goto("b26")),
   502  		Bloc("b26",
   503  			Goto("b25")),
   504  		Bloc("b25",
   505  			If("p", "b27", "b29")),
   506  		Bloc("b27",
   507  			Goto("b30")),
   508  		Bloc("b30",
   509  			Goto("b28")),
   510  		Bloc("b29",
   511  			Goto("b31")),
   512  		Bloc("b31",
   513  			Goto("b28")),
   514  		Bloc("b28",
   515  			If("p", "b32", "b33")),
   516  		Bloc("b32",
   517  			Goto("b21")),
   518  		Bloc("b21",
   519  			Goto("b47")),
   520  		Bloc("b47",
   521  			If("p", "b45", "b46")),
   522  		Bloc("b45",
   523  			Goto("b48")),
   524  		Bloc("b48",
   525  			Goto("b49")),
   526  		Bloc("b49",
   527  			If("p", "b50", "b51")),
   528  		Bloc("b50",
   529  			Goto("b52")),
   530  		Bloc("b52",
   531  			Goto("b53")),
   532  		Bloc("b53",
   533  			Goto("b51")),
   534  		Bloc("b51",
   535  			Goto("b54")),
   536  		Bloc("b54",
   537  			Goto("b46")),
   538  		Bloc("b46",
   539  			Exit("mem")),
   540  		Bloc("b33",
   541  			Goto("b34")),
   542  		Bloc("b34",
   543  			Goto("b37")),
   544  		Bloc("b37",
   545  			If("p", "b35", "b36")),
   546  		Bloc("b35",
   547  			Goto("b38")),
   548  		Bloc("b38",
   549  			Goto("b39")),
   550  		Bloc("b39",
   551  			If("p", "b40", "b41")),
   552  		Bloc("b40",
   553  			Goto("b42")),
   554  		Bloc("b42",
   555  			Goto("b43")),
   556  		Bloc("b43",
   557  			Goto("b41")),
   558  		Bloc("b41",
   559  			Goto("b44")),
   560  		Bloc("b44",
   561  			Goto("b36")),
   562  		Bloc("b36",
   563  			Goto("b20")),
   564  		Bloc("b20",
   565  			Goto("b18")),
   566  		Bloc("b2",
   567  			Goto("b4")),
   568  		Bloc("b4",
   569  			Exit("mem")))
   570  	CheckFunc(fun.f)
   571  	doms := generateDominatorMap(fun)
   572  	verifyDominators(t, fun, dominators, doms)
   573  }