Source file src/cmd/compile/internal/ssa/dom_test.go

     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 (
     8  	"cmd/compile/internal/types"
     9  	"testing"
    10  )
    11  
    12  func BenchmarkDominatorsLinear(b *testing.B)     { benchmarkDominators(b, 10000, genLinear) }
    13  func BenchmarkDominatorsFwdBack(b *testing.B)    { benchmarkDominators(b, 10000, genFwdBack) }
    14  func BenchmarkDominatorsManyPred(b *testing.B)   { benchmarkDominators(b, 10000, genManyPred) }
    15  func BenchmarkDominatorsMaxPred(b *testing.B)    { benchmarkDominators(b, 10000, genMaxPred) }
    16  func BenchmarkDominatorsMaxPredVal(b *testing.B) { benchmarkDominators(b, 10000, genMaxPredValue) }
    17  
    18  type blockGen func(size int) []bloc
    19  
    20  // genLinear creates an array of blocks that succeed one another
    21  // b_n -> [b_n+1].
    22  func genLinear(size int) []bloc {
    23  	var blocs []bloc
    24  	blocs = append(blocs,
    25  		Bloc("entry",
    26  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    27  			Goto(blockn(0)),
    28  		),
    29  	)
    30  	for i := 0; i < size; i++ {
    31  		blocs = append(blocs, Bloc(blockn(i),
    32  			Goto(blockn(i+1))))
    33  	}
    34  
    35  	blocs = append(blocs,
    36  		Bloc(blockn(size), Goto("exit")),
    37  		Bloc("exit", Exit("mem")),
    38  	)
    39  
    40  	return blocs
    41  }
    42  
    43  // genLinear creates an array of blocks that alternate between
    44  // b_n -> [b_n+1], b_n -> [b_n+1, b_n-1] , b_n -> [b_n+1, b_n+2]
    45  func genFwdBack(size int) []bloc {
    46  	var blocs []bloc
    47  	blocs = append(blocs,
    48  		Bloc("entry",
    49  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    50  			Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil),
    51  			Goto(blockn(0)),
    52  		),
    53  	)
    54  	for i := 0; i < size; i++ {
    55  		switch i % 2 {
    56  		case 0:
    57  			blocs = append(blocs, Bloc(blockn(i),
    58  				If("p", blockn(i+1), blockn(i+2))))
    59  		case 1:
    60  			blocs = append(blocs, Bloc(blockn(i),
    61  				If("p", blockn(i+1), blockn(i-1))))
    62  		}
    63  	}
    64  
    65  	blocs = append(blocs,
    66  		Bloc(blockn(size), Goto("exit")),
    67  		Bloc("exit", Exit("mem")),
    68  	)
    69  
    70  	return blocs
    71  }
    72  
    73  // genManyPred creates an array of blocks where 1/3rd have a successor of the
    74  // first block, 1/3rd the last block, and the remaining third are plain.
    75  func genManyPred(size int) []bloc {
    76  	var blocs []bloc
    77  	blocs = append(blocs,
    78  		Bloc("entry",
    79  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    80  			Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil),
    81  			Goto(blockn(0)),
    82  		),
    83  	)
    84  
    85  	// We want predecessor lists to be long, so 2/3rds of the blocks have a
    86  	// successor of the first or last block.
    87  	for i := 0; i < size; i++ {
    88  		switch i % 3 {
    89  		case 0:
    90  			blocs = append(blocs, Bloc(blockn(i),
    91  				Valu("a", OpConstBool, types.Types[types.TBOOL], 1, nil),
    92  				Goto(blockn(i+1))))
    93  		case 1:
    94  			blocs = append(blocs, Bloc(blockn(i),
    95  				Valu("a", OpConstBool, types.Types[types.TBOOL], 1, nil),
    96  				If("p", blockn(i+1), blockn(0))))
    97  		case 2:
    98  			blocs = append(blocs, Bloc(blockn(i),
    99  				Valu("a", OpConstBool, types.Types[types.TBOOL], 1, nil),
   100  				If("p", blockn(i+1), blockn(size))))
   101  		}
   102  	}
   103  
   104  	blocs = append(blocs,
   105  		Bloc(blockn(size), Goto("exit")),
   106  		Bloc("exit", Exit("mem")),
   107  	)
   108  
   109  	return blocs
   110  }
   111  
   112  // genMaxPred maximizes the size of the 'exit' predecessor list.
   113  func genMaxPred(size int) []bloc {
   114  	var blocs []bloc
   115  	blocs = append(blocs,
   116  		Bloc("entry",
   117  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   118  			Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil),
   119  			Goto(blockn(0)),
   120  		),
   121  	)
   122  
   123  	for i := 0; i < size; i++ {
   124  		blocs = append(blocs, Bloc(blockn(i),
   125  			If("p", blockn(i+1), "exit")))
   126  	}
   127  
   128  	blocs = append(blocs,
   129  		Bloc(blockn(size), Goto("exit")),
   130  		Bloc("exit", Exit("mem")),
   131  	)
   132  
   133  	return blocs
   134  }
   135  
   136  // genMaxPredValue is identical to genMaxPred but contains an
   137  // additional value.
   138  func genMaxPredValue(size int) []bloc {
   139  	var blocs []bloc
   140  	blocs = append(blocs,
   141  		Bloc("entry",
   142  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   143  			Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil),
   144  			Goto(blockn(0)),
   145  		),
   146  	)
   147  
   148  	for i := 0; i < size; i++ {
   149  		blocs = append(blocs, Bloc(blockn(i),
   150  			Valu("a", OpConstBool, types.Types[types.TBOOL], 1, nil),
   151  			If("p", blockn(i+1), "exit")))
   152  	}
   153  
   154  	blocs = append(blocs,
   155  		Bloc(blockn(size), Goto("exit")),
   156  		Bloc("exit", Exit("mem")),
   157  	)
   158  
   159  	return blocs
   160  }
   161  
   162  // sink for benchmark
   163  var domBenchRes []*Block
   164  
   165  func benchmarkDominators(b *testing.B, size int, bg blockGen) {
   166  	c := testConfig(b)
   167  	fun := c.Fun("entry", bg(size)...)
   168  
   169  	CheckFunc(fun.f)
   170  	b.SetBytes(int64(size))
   171  	b.ResetTimer()
   172  	for i := 0; i < b.N; i++ {
   173  		domBenchRes = dominators(fun.f)
   174  	}
   175  }
   176  
   177  type domFunc func(f *Func) []*Block
   178  
   179  // verifyDominators verifies that the dominators of fut (function under test)
   180  // as determined by domFn, match the map node->dominator
   181  func verifyDominators(t *testing.T, fut fun, domFn domFunc, doms map[string]string) {
   182  	blockNames := map[*Block]string{}
   183  	for n, b := range fut.blocks {
   184  		blockNames[b] = n
   185  	}
   186  
   187  	calcDom := domFn(fut.f)
   188  
   189  	for n, d := range doms {
   190  		nblk, ok := fut.blocks[n]
   191  		if !ok {
   192  			t.Errorf("invalid block name %s", n)
   193  		}
   194  		dblk, ok := fut.blocks[d]
   195  		if !ok {
   196  			t.Errorf("invalid block name %s", d)
   197  		}
   198  
   199  		domNode := calcDom[nblk.ID]
   200  		switch {
   201  		case calcDom[nblk.ID] == dblk:
   202  			calcDom[nblk.ID] = nil
   203  			continue
   204  		case calcDom[nblk.ID] != dblk:
   205  			t.Errorf("expected %s as dominator of %s, found %s", d, n, blockNames[domNode])
   206  		default:
   207  			t.Fatal("unexpected dominator condition")
   208  		}
   209  	}
   210  
   211  	for id, d := range calcDom {
   212  		// If nil, we've already verified it
   213  		if d == nil {
   214  			continue
   215  		}
   216  		for _, b := range fut.blocks {
   217  			if int(b.ID) == id {
   218  				t.Errorf("unexpected dominator of %s for %s", blockNames[d], blockNames[b])
   219  			}
   220  		}
   221  	}
   222  
   223  }
   224  
   225  func TestDominatorsSingleBlock(t *testing.T) {
   226  	c := testConfig(t)
   227  	fun := c.Fun("entry",
   228  		Bloc("entry",
   229  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   230  			Exit("mem")))
   231  
   232  	doms := map[string]string{}
   233  
   234  	CheckFunc(fun.f)
   235  	verifyDominators(t, fun, dominators, doms)
   236  	verifyDominators(t, fun, dominatorsSimple, doms)
   237  
   238  }
   239  
   240  func TestDominatorsSimple(t *testing.T) {
   241  	c := testConfig(t)
   242  	fun := c.Fun("entry",
   243  		Bloc("entry",
   244  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   245  			Goto("a")),
   246  		Bloc("a",
   247  			Goto("b")),
   248  		Bloc("b",
   249  			Goto("c")),
   250  		Bloc("c",
   251  			Goto("exit")),
   252  		Bloc("exit",
   253  			Exit("mem")))
   254  
   255  	doms := map[string]string{
   256  		"a":    "entry",
   257  		"b":    "a",
   258  		"c":    "b",
   259  		"exit": "c",
   260  	}
   261  
   262  	CheckFunc(fun.f)
   263  	verifyDominators(t, fun, dominators, doms)
   264  	verifyDominators(t, fun, dominatorsSimple, doms)
   265  
   266  }
   267  
   268  func TestDominatorsMultPredFwd(t *testing.T) {
   269  	c := testConfig(t)
   270  	fun := c.Fun("entry",
   271  		Bloc("entry",
   272  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   273  			Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil),
   274  			If("p", "a", "c")),
   275  		Bloc("a",
   276  			If("p", "b", "c")),
   277  		Bloc("b",
   278  			Goto("c")),
   279  		Bloc("c",
   280  			Goto("exit")),
   281  		Bloc("exit",
   282  			Exit("mem")))
   283  
   284  	doms := map[string]string{
   285  		"a":    "entry",
   286  		"b":    "a",
   287  		"c":    "entry",
   288  		"exit": "c",
   289  	}
   290  
   291  	CheckFunc(fun.f)
   292  	verifyDominators(t, fun, dominators, doms)
   293  	verifyDominators(t, fun, dominatorsSimple, doms)
   294  }
   295  
   296  func TestDominatorsDeadCode(t *testing.T) {
   297  	c := testConfig(t)
   298  	fun := c.Fun("entry",
   299  		Bloc("entry",
   300  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   301  			Valu("p", OpConstBool, types.Types[types.TBOOL], 0, nil),
   302  			If("p", "b3", "b5")),
   303  		Bloc("b2", Exit("mem")),
   304  		Bloc("b3", Goto("b2")),
   305  		Bloc("b4", Goto("b2")),
   306  		Bloc("b5", Goto("b2")))
   307  
   308  	doms := map[string]string{
   309  		"b2": "entry",
   310  		"b3": "entry",
   311  		"b5": "entry",
   312  	}
   313  
   314  	CheckFunc(fun.f)
   315  	verifyDominators(t, fun, dominators, doms)
   316  	verifyDominators(t, fun, dominatorsSimple, doms)
   317  }
   318  
   319  func TestDominatorsMultPredRev(t *testing.T) {
   320  	c := testConfig(t)
   321  	fun := c.Fun("entry",
   322  		Bloc("entry",
   323  			Goto("first")),
   324  		Bloc("first",
   325  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   326  			Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil),
   327  			Goto("a")),
   328  		Bloc("a",
   329  			If("p", "b", "first")),
   330  		Bloc("b",
   331  			Goto("c")),
   332  		Bloc("c",
   333  			If("p", "exit", "b")),
   334  		Bloc("exit",
   335  			Exit("mem")))
   336  
   337  	doms := map[string]string{
   338  		"first": "entry",
   339  		"a":     "first",
   340  		"b":     "a",
   341  		"c":     "b",
   342  		"exit":  "c",
   343  	}
   344  
   345  	CheckFunc(fun.f)
   346  	verifyDominators(t, fun, dominators, doms)
   347  	verifyDominators(t, fun, dominatorsSimple, doms)
   348  }
   349  
   350  func TestDominatorsMultPred(t *testing.T) {
   351  	c := testConfig(t)
   352  	fun := c.Fun("entry",
   353  		Bloc("entry",
   354  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   355  			Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil),
   356  			If("p", "a", "c")),
   357  		Bloc("a",
   358  			If("p", "b", "c")),
   359  		Bloc("b",
   360  			Goto("c")),
   361  		Bloc("c",
   362  			If("p", "b", "exit")),
   363  		Bloc("exit",
   364  			Exit("mem")))
   365  
   366  	doms := map[string]string{
   367  		"a":    "entry",
   368  		"b":    "entry",
   369  		"c":    "entry",
   370  		"exit": "c",
   371  	}
   372  
   373  	CheckFunc(fun.f)
   374  	verifyDominators(t, fun, dominators, doms)
   375  	verifyDominators(t, fun, dominatorsSimple, doms)
   376  }
   377  
   378  func TestInfiniteLoop(t *testing.T) {
   379  	c := testConfig(t)
   380  	// note lack of an exit block
   381  	fun := c.Fun("entry",
   382  		Bloc("entry",
   383  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   384  			Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil),
   385  			Goto("a")),
   386  		Bloc("a",
   387  			Goto("b")),
   388  		Bloc("b",
   389  			Goto("a")))
   390  
   391  	CheckFunc(fun.f)
   392  	doms := map[string]string{"a": "entry",
   393  		"b": "a"}
   394  	verifyDominators(t, fun, dominators, doms)
   395  }
   396  
   397  func TestDomTricky(t *testing.T) {
   398  	doms := map[string]string{
   399  		"4":  "1",
   400  		"2":  "4",
   401  		"5":  "4",
   402  		"11": "4",
   403  		"15": "4", // the incorrect answer is "5"
   404  		"10": "15",
   405  		"19": "15",
   406  	}
   407  
   408  	if4 := [2]string{"2", "5"}
   409  	if5 := [2]string{"15", "11"}
   410  	if15 := [2]string{"19", "10"}
   411  
   412  	for i := 0; i < 8; i++ {
   413  		a := 1 & i
   414  		b := 1 & i >> 1
   415  		c := 1 & i >> 2
   416  
   417  		cfg := testConfig(t)
   418  		fun := cfg.Fun("1",
   419  			Bloc("1",
   420  				Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   421  				Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil),
   422  				Goto("4")),
   423  			Bloc("2",
   424  				Goto("11")),
   425  			Bloc("4",
   426  				If("p", if4[a], if4[1-a])), // 2, 5
   427  			Bloc("5",
   428  				If("p", if5[b], if5[1-b])), //15, 11
   429  			Bloc("10",
   430  				Exit("mem")),
   431  			Bloc("11",
   432  				Goto("15")),
   433  			Bloc("15",
   434  				If("p", if15[c], if15[1-c])), //19, 10
   435  			Bloc("19",
   436  				Goto("10")))
   437  		CheckFunc(fun.f)
   438  		verifyDominators(t, fun, dominators, doms)
   439  		verifyDominators(t, fun, dominatorsSimple, doms)
   440  	}
   441  }
   442  
   443  // generateDominatorMap uses dominatorsSimple to obtain a
   444  // reference dominator tree for testing faster algorithms.
   445  func generateDominatorMap(fut fun) map[string]string {
   446  	blockNames := map[*Block]string{}
   447  	for n, b := range fut.blocks {
   448  		blockNames[b] = n
   449  	}
   450  	referenceDom := dominatorsSimple(fut.f)
   451  	doms := make(map[string]string)
   452  	for _, b := range fut.f.Blocks {
   453  		if d := referenceDom[b.ID]; d != nil {
   454  			doms[blockNames[b]] = blockNames[d]
   455  		}
   456  	}
   457  	return doms
   458  }
   459  
   460  func TestDominatorsPostTrickyA(t *testing.T) {
   461  	testDominatorsPostTricky(t, "b8", "b11", "b10", "b8", "b14", "b15")
   462  }
   463  
   464  func TestDominatorsPostTrickyB(t *testing.T) {
   465  	testDominatorsPostTricky(t, "b11", "b8", "b10", "b8", "b14", "b15")
   466  }
   467  
   468  func TestDominatorsPostTrickyC(t *testing.T) {
   469  	testDominatorsPostTricky(t, "b8", "b11", "b8", "b10", "b14", "b15")
   470  }
   471  
   472  func TestDominatorsPostTrickyD(t *testing.T) {
   473  	testDominatorsPostTricky(t, "b11", "b8", "b8", "b10", "b14", "b15")
   474  }
   475  
   476  func TestDominatorsPostTrickyE(t *testing.T) {
   477  	testDominatorsPostTricky(t, "b8", "b11", "b10", "b8", "b15", "b14")
   478  }
   479  
   480  func TestDominatorsPostTrickyF(t *testing.T) {
   481  	testDominatorsPostTricky(t, "b11", "b8", "b10", "b8", "b15", "b14")
   482  }
   483  
   484  func TestDominatorsPostTrickyG(t *testing.T) {
   485  	testDominatorsPostTricky(t, "b8", "b11", "b8", "b10", "b15", "b14")
   486  }
   487  
   488  func TestDominatorsPostTrickyH(t *testing.T) {
   489  	testDominatorsPostTricky(t, "b11", "b8", "b8", "b10", "b15", "b14")
   490  }
   491  
   492  func testDominatorsPostTricky(t *testing.T, b7then, b7else, b12then, b12else, b13then, b13else string) {
   493  	c := testConfig(t)
   494  	fun := c.Fun("b1",
   495  		Bloc("b1",
   496  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   497  			Valu("p", OpConstBool, types.Types[types.TBOOL], 1, nil),
   498  			If("p", "b3", "b2")),
   499  		Bloc("b3",
   500  			If("p", "b5", "b6")),
   501  		Bloc("b5",
   502  			Goto("b7")),
   503  		Bloc("b7",
   504  			If("p", b7then, b7else)),
   505  		Bloc("b8",
   506  			Goto("b13")),
   507  		Bloc("b13",
   508  			If("p", b13then, b13else)),
   509  		Bloc("b14",
   510  			Goto("b10")),
   511  		Bloc("b15",
   512  			Goto("b16")),
   513  		Bloc("b16",
   514  			Goto("b9")),
   515  		Bloc("b9",
   516  			Goto("b7")),
   517  		Bloc("b11",
   518  			Goto("b12")),
   519  		Bloc("b12",
   520  			If("p", b12then, b12else)),
   521  		Bloc("b10",
   522  			Goto("b6")),
   523  		Bloc("b6",
   524  			Goto("b17")),
   525  		Bloc("b17",
   526  			Goto("b18")),
   527  		Bloc("b18",
   528  			If("p", "b22", "b19")),
   529  		Bloc("b22",
   530  			Goto("b23")),
   531  		Bloc("b23",
   532  			If("p", "b21", "b19")),
   533  		Bloc("b19",
   534  			If("p", "b24", "b25")),
   535  		Bloc("b24",
   536  			Goto("b26")),
   537  		Bloc("b26",
   538  			Goto("b25")),
   539  		Bloc("b25",
   540  			If("p", "b27", "b29")),
   541  		Bloc("b27",
   542  			Goto("b30")),
   543  		Bloc("b30",
   544  			Goto("b28")),
   545  		Bloc("b29",
   546  			Goto("b31")),
   547  		Bloc("b31",
   548  			Goto("b28")),
   549  		Bloc("b28",
   550  			If("p", "b32", "b33")),
   551  		Bloc("b32",
   552  			Goto("b21")),
   553  		Bloc("b21",
   554  			Goto("b47")),
   555  		Bloc("b47",
   556  			If("p", "b45", "b46")),
   557  		Bloc("b45",
   558  			Goto("b48")),
   559  		Bloc("b48",
   560  			Goto("b49")),
   561  		Bloc("b49",
   562  			If("p", "b50", "b51")),
   563  		Bloc("b50",
   564  			Goto("b52")),
   565  		Bloc("b52",
   566  			Goto("b53")),
   567  		Bloc("b53",
   568  			Goto("b51")),
   569  		Bloc("b51",
   570  			Goto("b54")),
   571  		Bloc("b54",
   572  			Goto("b46")),
   573  		Bloc("b46",
   574  			Exit("mem")),
   575  		Bloc("b33",
   576  			Goto("b34")),
   577  		Bloc("b34",
   578  			Goto("b37")),
   579  		Bloc("b37",
   580  			If("p", "b35", "b36")),
   581  		Bloc("b35",
   582  			Goto("b38")),
   583  		Bloc("b38",
   584  			Goto("b39")),
   585  		Bloc("b39",
   586  			If("p", "b40", "b41")),
   587  		Bloc("b40",
   588  			Goto("b42")),
   589  		Bloc("b42",
   590  			Goto("b43")),
   591  		Bloc("b43",
   592  			Goto("b41")),
   593  		Bloc("b41",
   594  			Goto("b44")),
   595  		Bloc("b44",
   596  			Goto("b36")),
   597  		Bloc("b36",
   598  			Goto("b20")),
   599  		Bloc("b20",
   600  			Goto("b18")),
   601  		Bloc("b2",
   602  			Goto("b4")),
   603  		Bloc("b4",
   604  			Exit("mem")))
   605  	CheckFunc(fun.f)
   606  	doms := generateDominatorMap(fun)
   607  	verifyDominators(t, fun, dominators, doms)
   608  }
   609  

View as plain text