Source file src/cmd/compile/internal/ssacompile/deadstore_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 ssacompile
     6  
     7  import (
     8  	"fmt"
     9  	"sort"
    10  	"testing"
    11  
    12  	"cmd/compile/internal/ssa/ssaop"
    13  	"cmd/compile/internal/types"
    14  	"cmd/internal/src"
    15  )
    16  
    17  func TestDeadStore(t *testing.T) {
    18  	c := testConfig(t)
    19  	ptrType := c.config.Types.BytePtr
    20  	t.Logf("PTRTYPE %v", ptrType)
    21  	fun := c.Fun("entry",
    22  		Bloc("entry",
    23  			Valu("start", ssaop.OpInitMem, types.TypeMem, 0, nil),
    24  			Valu("sb", ssaop.OpSB, c.config.Types.Uintptr, 0, nil),
    25  			Valu("v", ssaop.OpConstBool, c.config.Types.Bool, 1, nil),
    26  			Valu("addr1", ssaop.OpAddr, ptrType, 0, nil, "sb"),
    27  			Valu("addr2", ssaop.OpAddr, ptrType, 0, nil, "sb"),
    28  			Valu("addr3", ssaop.OpAddr, ptrType, 0, nil, "sb"),
    29  			Valu("zero1", ssaop.OpZero, types.TypeMem, 1, c.config.Types.Bool, "addr3", "start"),
    30  			Valu("store1", ssaop.OpStore, types.TypeMem, 0, c.config.Types.Bool, "addr1", "v", "zero1"),
    31  			Valu("store2", ssaop.OpStore, types.TypeMem, 0, c.config.Types.Bool, "addr2", "v", "store1"),
    32  			Valu("store3", ssaop.OpStore, types.TypeMem, 0, c.config.Types.Bool, "addr1", "v", "store2"),
    33  			Valu("store4", ssaop.OpStore, types.TypeMem, 0, c.config.Types.Bool, "addr3", "v", "store3"),
    34  			Goto("exit")),
    35  		Bloc("exit",
    36  			Exit("store3")))
    37  
    38  	CheckFunc(fun.f)
    39  	dse(fun.f)
    40  	CheckFunc(fun.f)
    41  
    42  	v1 := fun.values["store1"]
    43  	if v1.Op != ssaop.OpCopy {
    44  		t.Errorf("dead store not removed")
    45  	}
    46  
    47  	v2 := fun.values["zero1"]
    48  	if v2.Op != ssaop.OpCopy {
    49  		t.Errorf("dead store (zero) not removed")
    50  	}
    51  }
    52  
    53  func TestDeadStorePhi(t *testing.T) {
    54  	// make sure we don't get into an infinite loop with phi values.
    55  	c := testConfig(t)
    56  	ptrType := c.config.Types.BytePtr
    57  	fun := c.Fun("entry",
    58  		Bloc("entry",
    59  			Valu("start", ssaop.OpInitMem, types.TypeMem, 0, nil),
    60  			Valu("sb", ssaop.OpSB, c.config.Types.Uintptr, 0, nil),
    61  			Valu("v", ssaop.OpConstBool, c.config.Types.Bool, 1, nil),
    62  			Valu("addr", ssaop.OpAddr, ptrType, 0, nil, "sb"),
    63  			Goto("loop")),
    64  		Bloc("loop",
    65  			Valu("phi", ssaop.OpPhi, types.TypeMem, 0, nil, "start", "store"),
    66  			Valu("store", ssaop.OpStore, types.TypeMem, 0, c.config.Types.Bool, "addr", "v", "phi"),
    67  			If("v", "loop", "exit")),
    68  		Bloc("exit",
    69  			Exit("store")))
    70  
    71  	CheckFunc(fun.f)
    72  	dse(fun.f)
    73  	CheckFunc(fun.f)
    74  }
    75  
    76  func TestDeadStoreTypes(t *testing.T) {
    77  	// Make sure a narrow store can't shadow a wider one. We test an even
    78  	// stronger restriction, that one store can't shadow another unless the
    79  	// types of the address fields are identical (where identicalness is
    80  	// decided by the CSE pass).
    81  	c := testConfig(t)
    82  	t1 := c.config.Types.UInt64.PtrTo()
    83  	t2 := c.config.Types.UInt32.PtrTo()
    84  	fun := c.Fun("entry",
    85  		Bloc("entry",
    86  			Valu("start", ssaop.OpInitMem, types.TypeMem, 0, nil),
    87  			Valu("sb", ssaop.OpSB, c.config.Types.Uintptr, 0, nil),
    88  			Valu("v", ssaop.OpConstBool, c.config.Types.Bool, 1, nil),
    89  			Valu("addr1", ssaop.OpAddr, t1, 0, nil, "sb"),
    90  			Valu("addr2", ssaop.OpAddr, t2, 0, nil, "sb"),
    91  			Valu("store1", ssaop.OpStore, types.TypeMem, 0, c.config.Types.Bool, "addr1", "v", "start"),
    92  			Valu("store2", ssaop.OpStore, types.TypeMem, 0, c.config.Types.Bool, "addr2", "v", "store1"),
    93  			Goto("exit")),
    94  		Bloc("exit",
    95  			Exit("store2")))
    96  
    97  	CheckFunc(fun.f)
    98  	cse(fun.f)
    99  	dse(fun.f)
   100  	CheckFunc(fun.f)
   101  
   102  	v := fun.values["store1"]
   103  	if v.Op == ssaop.OpCopy {
   104  		t.Errorf("store %s incorrectly removed", v)
   105  	}
   106  }
   107  
   108  func TestDeadStoreUnsafe(t *testing.T) {
   109  	// Make sure a narrow store can't shadow a wider one. The test above
   110  	// covers the case of two different types, but unsafe pointer casting
   111  	// can get to a point where the size is changed but type unchanged.
   112  	c := testConfig(t)
   113  	ptrType := c.config.Types.UInt64.PtrTo()
   114  	fun := c.Fun("entry",
   115  		Bloc("entry",
   116  			Valu("start", ssaop.OpInitMem, types.TypeMem, 0, nil),
   117  			Valu("sb", ssaop.OpSB, c.config.Types.Uintptr, 0, nil),
   118  			Valu("v", ssaop.OpConstBool, c.config.Types.Bool, 1, nil),
   119  			Valu("addr1", ssaop.OpAddr, ptrType, 0, nil, "sb"),
   120  			Valu("store1", ssaop.OpStore, types.TypeMem, 0, c.config.Types.Int64, "addr1", "v", "start"), // store 8 bytes
   121  			Valu("store2", ssaop.OpStore, types.TypeMem, 0, c.config.Types.Bool, "addr1", "v", "store1"), // store 1 byte
   122  			Goto("exit")),
   123  		Bloc("exit",
   124  			Exit("store2")))
   125  
   126  	CheckFunc(fun.f)
   127  	cse(fun.f)
   128  	dse(fun.f)
   129  	CheckFunc(fun.f)
   130  
   131  	v := fun.values["store1"]
   132  	if v.Op == ssaop.OpCopy {
   133  		t.Errorf("store %s incorrectly removed", v)
   134  	}
   135  }
   136  
   137  func TestDeadStoreSmallStructInit(t *testing.T) {
   138  	c := testConfig(t)
   139  	ptrType := c.config.Types.BytePtr
   140  	typ := types.NewStruct([]*types.Field{
   141  		types.NewField(src.NoXPos, &types.Sym{Name: "A"}, c.config.Types.Int),
   142  		types.NewField(src.NoXPos, &types.Sym{Name: "B"}, c.config.Types.Int),
   143  	})
   144  	name := c.Temp(typ)
   145  	fun := c.Fun("entry",
   146  		Bloc("entry",
   147  			Valu("start", ssaop.OpInitMem, types.TypeMem, 0, nil),
   148  			Valu("sp", ssaop.OpSP, c.config.Types.Uintptr, 0, nil),
   149  			Valu("zero", ssaop.OpConst64, c.config.Types.Int, 0, nil),
   150  			Valu("v6", ssaop.OpLocalAddr, ptrType, 0, name, "sp", "start"),
   151  			Valu("v3", ssaop.OpOffPtr, ptrType, 8, nil, "v6"),
   152  			Valu("v22", ssaop.OpOffPtr, ptrType, 0, nil, "v6"),
   153  			Valu("zerostore1", ssaop.OpStore, types.TypeMem, 0, c.config.Types.Int, "v22", "zero", "start"),
   154  			Valu("zerostore2", ssaop.OpStore, types.TypeMem, 0, c.config.Types.Int, "v3", "zero", "zerostore1"),
   155  			Valu("v8", ssaop.OpLocalAddr, ptrType, 0, name, "sp", "zerostore2"),
   156  			Valu("v23", ssaop.OpOffPtr, ptrType, 8, nil, "v8"),
   157  			Valu("v25", ssaop.OpOffPtr, ptrType, 0, nil, "v8"),
   158  			Valu("zerostore3", ssaop.OpStore, types.TypeMem, 0, c.config.Types.Int, "v25", "zero", "zerostore2"),
   159  			Valu("zerostore4", ssaop.OpStore, types.TypeMem, 0, c.config.Types.Int, "v23", "zero", "zerostore3"),
   160  			Goto("exit")),
   161  		Bloc("exit",
   162  			Exit("zerostore4")))
   163  
   164  	fun.f.Name = "smallstructinit"
   165  	CheckFunc(fun.f)
   166  	cse(fun.f)
   167  	dse(fun.f)
   168  	CheckFunc(fun.f)
   169  
   170  	v1 := fun.values["zerostore1"]
   171  	if v1.Op != ssaop.OpCopy {
   172  		t.Errorf("dead store not removed")
   173  	}
   174  	v2 := fun.values["zerostore2"]
   175  	if v2.Op != ssaop.OpCopy {
   176  		t.Errorf("dead store not removed")
   177  	}
   178  }
   179  
   180  func TestDeadStoreArrayGap(t *testing.T) {
   181  	c := testConfig(t)
   182  	ptr := c.config.Types.BytePtr
   183  	i64 := c.config.Types.Int64
   184  
   185  	typ := types.NewArray(i64, 5)
   186  	tmp := c.Temp(typ)
   187  
   188  	fun := c.Fun("entry",
   189  		Bloc("entry",
   190  			Valu("start", ssaop.OpInitMem, types.TypeMem, 0, nil),
   191  			Valu("sp", ssaop.OpSP, c.config.Types.Uintptr, 0, nil),
   192  
   193  			Valu("base", ssaop.OpLocalAddr, ptr, 0, tmp, "sp", "start"),
   194  
   195  			Valu("p0", ssaop.OpOffPtr, ptr, 0, nil, "base"),
   196  			Valu("p1", ssaop.OpOffPtr, ptr, 8, nil, "base"),
   197  			Valu("p2", ssaop.OpOffPtr, ptr, 16, nil, "base"),
   198  			Valu("p3", ssaop.OpOffPtr, ptr, 24, nil, "base"),
   199  			Valu("p4", ssaop.OpOffPtr, ptr, 32, nil, "base"),
   200  
   201  			Valu("one", ssaop.OpConst64, i64, 1, nil),
   202  			Valu("seven", ssaop.OpConst64, i64, 7, nil),
   203  			Valu("zero", ssaop.OpConst64, i64, 0, nil),
   204  
   205  			Valu("mem0", ssaop.OpZero, types.TypeMem, 40, typ, "base", "start"),
   206  
   207  			Valu("s0", ssaop.OpStore, types.TypeMem, 0, i64, "p0", "one", "mem0"),
   208  			Valu("s1", ssaop.OpStore, types.TypeMem, 0, i64, "p1", "seven", "s0"),
   209  			Valu("s2", ssaop.OpStore, types.TypeMem, 0, i64, "p3", "one", "s1"),
   210  			Valu("s3", ssaop.OpStore, types.TypeMem, 0, i64, "p4", "one", "s2"),
   211  			Valu("s4", ssaop.OpStore, types.TypeMem, 0, i64, "p2", "zero", "s3"),
   212  
   213  			Goto("exit")),
   214  		Bloc("exit",
   215  			Exit("s4")))
   216  
   217  	CheckFunc(fun.f)
   218  	dse(fun.f)
   219  	CheckFunc(fun.f)
   220  
   221  	if op := fun.values["mem0"].Op; op != ssaop.OpCopy {
   222  		t.Fatalf("dead Zero not removed: got %s, want OpCopy", op)
   223  	}
   224  }
   225  
   226  func TestShadowRanges(t *testing.T) {
   227  	t.Run("simple insert & contains", func(t *testing.T) {
   228  		var sr shadowRanges
   229  		sr.add(10, 20)
   230  
   231  		wantRanges(t, sr.ranges, [][2]uint16{{10, 20}})
   232  		if !sr.contains(12, 18) || !sr.contains(10, 20) {
   233  			t.Fatalf("contains failed after simple add")
   234  		}
   235  		if sr.contains(9, 11) || sr.contains(11, 21) {
   236  			t.Fatalf("contains erroneously true for non-contained range")
   237  		}
   238  	})
   239  
   240  	t.Run("merge overlapping", func(t *testing.T) {
   241  		var sr shadowRanges
   242  		sr.add(10, 20)
   243  		sr.add(15, 25)
   244  
   245  		wantRanges(t, sr.ranges, [][2]uint16{{10, 25}})
   246  		if !sr.contains(13, 24) {
   247  			t.Fatalf("contains should be true after merge")
   248  		}
   249  	})
   250  
   251  	t.Run("merge touching boundary", func(t *testing.T) {
   252  		var sr shadowRanges
   253  		sr.add(100, 150)
   254  		// touches at 150 - should coalesce
   255  		sr.add(150, 180)
   256  
   257  		wantRanges(t, sr.ranges, [][2]uint16{{100, 180}})
   258  	})
   259  
   260  	t.Run("union across several ranges", func(t *testing.T) {
   261  		var sr shadowRanges
   262  		sr.add(10, 20)
   263  		sr.add(30, 40)
   264  		// bridges second, not first
   265  		sr.add(25, 35)
   266  
   267  		wantRanges(t, sr.ranges, [][2]uint16{{10, 20}, {25, 40}})
   268  
   269  		// envelops everything
   270  		sr.add(5, 50)
   271  		wantRanges(t, sr.ranges, [][2]uint16{{5, 50}})
   272  	})
   273  
   274  	t.Run("disjoint intervals stay separate", func(t *testing.T) {
   275  		var sr shadowRanges
   276  		sr.add(10, 20)
   277  		sr.add(22, 30)
   278  
   279  		wantRanges(t, sr.ranges, [][2]uint16{{10, 20}, {22, 30}})
   280  		// spans both
   281  		if sr.contains(15, 25) {
   282  			t.Fatalf("contains across two disjoint ranges should be false")
   283  		}
   284  	})
   285  
   286  	t.Run("large uint16 offsets still work", func(t *testing.T) {
   287  		var sr shadowRanges
   288  		sr.add(40000, 45000)
   289  
   290  		if !sr.contains(42000, 43000) {
   291  			t.Fatalf("contains failed for large uint16 values")
   292  		}
   293  	})
   294  
   295  	t.Run("out-of-bounds inserts ignored", func(t *testing.T) {
   296  		var sr shadowRanges
   297  		sr.add(10, 20)
   298  		sr.add(-5, 5)
   299  		sr.add(70000, 70010)
   300  
   301  		wantRanges(t, sr.ranges, [][2]uint16{{10, 20}})
   302  	})
   303  }
   304  
   305  // canonicalise order for comparisons
   306  func sortRanges(r []shadowRange) {
   307  	sort.Slice(r, func(i, j int) bool { return r[i].lo < r[j].lo })
   308  }
   309  
   310  // compare actual slice with expected pairs
   311  func wantRanges(t *testing.T, got []shadowRange, want [][2]uint16) {
   312  	t.Helper()
   313  	sortRanges(got)
   314  
   315  	if len(got) != len(want) {
   316  		t.Fatalf("len(ranges)=%d, want %d (got=%v)", len(got), len(want), got)
   317  	}
   318  
   319  	for i, w := range want {
   320  		if got[i].lo != w[0] || got[i].hi != w[1] {
   321  			t.Fatalf("range %d = [%d,%d], want [%d,%d] (full=%v)",
   322  				i, got[i].lo, got[i].hi, w[0], w[1], got)
   323  		}
   324  	}
   325  }
   326  
   327  func BenchmarkDeadStore(b *testing.B) {
   328  	cfg := testConfig(b)
   329  	ptr := cfg.config.Types.BytePtr
   330  
   331  	f := cfg.Fun("entry",
   332  		Bloc("entry",
   333  			Valu("start", ssaop.OpInitMem, types.TypeMem, 0, nil),
   334  			Valu("sb", ssaop.OpSB, cfg.config.Types.Uintptr, 0, nil),
   335  			Valu("v", ssaop.OpConstBool, cfg.config.Types.Bool, 1, nil),
   336  			Valu("a1", ssaop.OpAddr, ptr, 0, nil, "sb"),
   337  			Valu("a2", ssaop.OpAddr, ptr, 0, nil, "sb"),
   338  			Valu("a3", ssaop.OpAddr, ptr, 0, nil, "sb"),
   339  			Valu("z1", ssaop.OpZero, types.TypeMem, 1, cfg.config.Types.Bool, "a3", "start"),
   340  			Valu("s1", ssaop.OpStore, types.TypeMem, 0, cfg.config.Types.Bool, "a1", "v", "z1"),
   341  			Valu("s2", ssaop.OpStore, types.TypeMem, 0, cfg.config.Types.Bool, "a2", "v", "s1"),
   342  			Valu("s3", ssaop.OpStore, types.TypeMem, 0, cfg.config.Types.Bool, "a1", "v", "s2"),
   343  			Valu("s4", ssaop.OpStore, types.TypeMem, 0, cfg.config.Types.Bool, "a3", "v", "s3"),
   344  			Goto("exit")),
   345  		Bloc("exit",
   346  			Exit("s3")))
   347  
   348  	runBench(b, func() {
   349  		dse(f.f)
   350  	})
   351  }
   352  
   353  func BenchmarkDeadStorePhi(b *testing.B) {
   354  	cfg := testConfig(b)
   355  	ptr := cfg.config.Types.BytePtr
   356  
   357  	f := cfg.Fun("entry",
   358  		Bloc("entry",
   359  			Valu("start", ssaop.OpInitMem, types.TypeMem, 0, nil),
   360  			Valu("sb", ssaop.OpSB, cfg.config.Types.Uintptr, 0, nil),
   361  			Valu("v", ssaop.OpConstBool, cfg.config.Types.Bool, 1, nil),
   362  			Valu("addr", ssaop.OpAddr, ptr, 0, nil, "sb"),
   363  			Goto("loop")),
   364  		Bloc("loop",
   365  			Valu("phi", ssaop.OpPhi, types.TypeMem, 0, nil, "start", "store"),
   366  			Valu("store", ssaop.OpStore, types.TypeMem, 0, cfg.config.Types.Bool, "addr", "v", "phi"),
   367  			If("v", "loop", "exit")),
   368  		Bloc("exit",
   369  			Exit("store")))
   370  
   371  	runBench(b, func() {
   372  		dse(f.f)
   373  	})
   374  }
   375  
   376  func BenchmarkDeadStoreTypes(b *testing.B) {
   377  	cfg := testConfig(b)
   378  
   379  	t1 := cfg.config.Types.UInt64.PtrTo()
   380  	t2 := cfg.config.Types.UInt32.PtrTo()
   381  
   382  	f := cfg.Fun("entry",
   383  		Bloc("entry",
   384  			Valu("start", ssaop.OpInitMem, types.TypeMem, 0, nil),
   385  			Valu("sb", ssaop.OpSB, cfg.config.Types.Uintptr, 0, nil),
   386  			Valu("v", ssaop.OpConstBool, cfg.config.Types.Bool, 1, nil),
   387  			Valu("a1", ssaop.OpAddr, t1, 0, nil, "sb"),
   388  			Valu("a2", ssaop.OpAddr, t2, 0, nil, "sb"),
   389  			Valu("s1", ssaop.OpStore, types.TypeMem, 0, cfg.config.Types.Bool, "a1", "v", "start"),
   390  			Valu("s2", ssaop.OpStore, types.TypeMem, 0, cfg.config.Types.Bool, "a2", "v", "s1"),
   391  			Goto("exit")),
   392  		Bloc("exit",
   393  			Exit("s2")))
   394  	cse(f.f)
   395  
   396  	runBench(b, func() {
   397  		dse(f.f)
   398  	})
   399  }
   400  
   401  func BenchmarkDeadStoreUnsafe(b *testing.B) {
   402  	cfg := testConfig(b)
   403  	ptr := cfg.config.Types.UInt64.PtrTo()
   404  	f := cfg.Fun("entry",
   405  		Bloc("entry",
   406  			Valu("start", ssaop.OpInitMem, types.TypeMem, 0, nil),
   407  			Valu("sb", ssaop.OpSB, cfg.config.Types.Uintptr, 0, nil),
   408  			Valu("v", ssaop.OpConstBool, cfg.config.Types.Bool, 1, nil),
   409  			Valu("a1", ssaop.OpAddr, ptr, 0, nil, "sb"),
   410  			Valu("s1", ssaop.OpStore, types.TypeMem, 0, cfg.config.Types.Int64, "a1", "v", "start"),
   411  			Valu("s2", ssaop.OpStore, types.TypeMem, 0, cfg.config.Types.Bool, "a1", "v", "s1"),
   412  			Goto("exit")),
   413  		Bloc("exit",
   414  			Exit("s2")))
   415  	cse(f.f)
   416  	runBench(b, func() {
   417  		dse(f.f)
   418  	})
   419  }
   420  
   421  func BenchmarkDeadStoreSmallStructInit(b *testing.B) {
   422  	cfg := testConfig(b)
   423  	ptr := cfg.config.Types.BytePtr
   424  
   425  	typ := types.NewStruct([]*types.Field{
   426  		types.NewField(src.NoXPos, &types.Sym{Name: "A"}, cfg.config.Types.Int),
   427  		types.NewField(src.NoXPos, &types.Sym{Name: "B"}, cfg.config.Types.Int),
   428  	})
   429  	tmp := cfg.Temp(typ)
   430  
   431  	f := cfg.Fun("entry",
   432  		Bloc("entry",
   433  			Valu("start", ssaop.OpInitMem, types.TypeMem, 0, nil),
   434  			Valu("sp", ssaop.OpSP, cfg.config.Types.Uintptr, 0, nil),
   435  			Valu("zero", ssaop.OpConst64, cfg.config.Types.Int, 0, nil),
   436  
   437  			Valu("v6", ssaop.OpLocalAddr, ptr, 0, tmp, "sp", "start"),
   438  			Valu("v3", ssaop.OpOffPtr, ptr, 8, nil, "v6"),
   439  			Valu("v22", ssaop.OpOffPtr, ptr, 0, nil, "v6"),
   440  			Valu("s1", ssaop.OpStore, types.TypeMem, 0, cfg.config.Types.Int, "v22", "zero", "start"),
   441  			Valu("s2", ssaop.OpStore, types.TypeMem, 0, cfg.config.Types.Int, "v3", "zero", "s1"),
   442  
   443  			Valu("v8", ssaop.OpLocalAddr, ptr, 0, tmp, "sp", "s2"),
   444  			Valu("v23", ssaop.OpOffPtr, ptr, 8, nil, "v8"),
   445  			Valu("v25", ssaop.OpOffPtr, ptr, 0, nil, "v8"),
   446  			Valu("s3", ssaop.OpStore, types.TypeMem, 0, cfg.config.Types.Int, "v25", "zero", "s2"),
   447  			Valu("s4", ssaop.OpStore, types.TypeMem, 0, cfg.config.Types.Int, "v23", "zero", "s3"),
   448  			Goto("exit")),
   449  		Bloc("exit",
   450  			Exit("s4")))
   451  	cse(f.f)
   452  
   453  	runBench(b, func() {
   454  		dse(f.f)
   455  	})
   456  }
   457  
   458  func BenchmarkDeadStoreLargeBlock(b *testing.B) {
   459  	// create a very large block with many shadowed stores
   460  	const (
   461  		addrCount = 128
   462  		// first 7 are dead
   463  		storesPerAddr = 8
   464  	)
   465  	cfg := testConfig(b)
   466  	ptrType := cfg.config.Types.BytePtr
   467  	boolType := cfg.config.Types.Bool
   468  
   469  	items := []interface{}{
   470  		Valu("start", ssaop.OpInitMem, types.TypeMem, 0, nil),
   471  		Valu("sb", ssaop.OpSB, cfg.config.Types.Uintptr, 0, nil),
   472  		Valu("v", ssaop.OpConstBool, boolType, 1, nil),
   473  	}
   474  
   475  	for i := 0; i < addrCount; i++ {
   476  		items = append(items,
   477  			Valu(fmt.Sprintf("addr%d", i), ssaop.OpAddr, ptrType, 0, nil, "sb"),
   478  		)
   479  	}
   480  
   481  	prev := "start"
   482  	for round := 0; round < storesPerAddr; round++ {
   483  		for i := 0; i < addrCount; i++ {
   484  			store := fmt.Sprintf("s_%03d_%d", i, round)
   485  			addr := fmt.Sprintf("addr%d", i)
   486  			items = append(items,
   487  				Valu(store, ssaop.OpStore, types.TypeMem, 0, boolType, addr, "v", prev),
   488  			)
   489  			prev = store
   490  		}
   491  	}
   492  
   493  	items = append(items, Goto("exit"))
   494  	entryBlk := Bloc("entry", items...)
   495  	exitBlk := Bloc("exit", Exit(prev))
   496  
   497  	f := cfg.Fun("stress", entryBlk, exitBlk)
   498  
   499  	runBench(b, func() {
   500  		dse(f.f)
   501  	})
   502  }
   503  
   504  func runBench(b *testing.B, build func()) {
   505  	b.ReportAllocs()
   506  	b.ResetTimer()
   507  	for i := 0; i < b.N; i++ {
   508  		build()
   509  	}
   510  }
   511  

View as plain text