github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/compile/internal/ssa/fuse_test.go (about)

     1  package ssa
     2  
     3  import (
     4  	"github.com/gagliardetto/golang-go/cmd/compile/internal/types"
     5  	"fmt"
     6  	"strconv"
     7  	"testing"
     8  )
     9  
    10  func TestFuseEliminatesOneBranch(t *testing.T) {
    11  	c := testConfig(t)
    12  	ptrType := c.config.Types.BytePtr
    13  	fun := c.Fun("entry",
    14  		Bloc("entry",
    15  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    16  			Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil),
    17  			Goto("checkPtr")),
    18  		Bloc("checkPtr",
    19  			Valu("ptr1", OpLoad, ptrType, 0, nil, "sb", "mem"),
    20  			Valu("nilptr", OpConstNil, ptrType, 0, nil),
    21  			Valu("bool1", OpNeqPtr, c.config.Types.Bool, 0, nil, "ptr1", "nilptr"),
    22  			If("bool1", "then", "exit")),
    23  		Bloc("then",
    24  			Goto("exit")),
    25  		Bloc("exit",
    26  			Exit("mem")))
    27  
    28  	CheckFunc(fun.f)
    29  	fuseAll(fun.f)
    30  
    31  	for _, b := range fun.f.Blocks {
    32  		if b == fun.blocks["then"] && b.Kind != BlockInvalid {
    33  			t.Errorf("then was not eliminated, but should have")
    34  		}
    35  	}
    36  }
    37  
    38  func TestFuseEliminatesBothBranches(t *testing.T) {
    39  	c := testConfig(t)
    40  	ptrType := c.config.Types.BytePtr
    41  	fun := c.Fun("entry",
    42  		Bloc("entry",
    43  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    44  			Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil),
    45  			Goto("checkPtr")),
    46  		Bloc("checkPtr",
    47  			Valu("ptr1", OpLoad, ptrType, 0, nil, "sb", "mem"),
    48  			Valu("nilptr", OpConstNil, ptrType, 0, nil),
    49  			Valu("bool1", OpNeqPtr, c.config.Types.Bool, 0, nil, "ptr1", "nilptr"),
    50  			If("bool1", "then", "else")),
    51  		Bloc("then",
    52  			Goto("exit")),
    53  		Bloc("else",
    54  			Goto("exit")),
    55  		Bloc("exit",
    56  			Exit("mem")))
    57  
    58  	CheckFunc(fun.f)
    59  	fuseAll(fun.f)
    60  
    61  	for _, b := range fun.f.Blocks {
    62  		if b == fun.blocks["then"] && b.Kind != BlockInvalid {
    63  			t.Errorf("then was not eliminated, but should have")
    64  		}
    65  		if b == fun.blocks["else"] && b.Kind != BlockInvalid {
    66  			t.Errorf("else was not eliminated, but should have")
    67  		}
    68  	}
    69  }
    70  
    71  func TestFuseHandlesPhis(t *testing.T) {
    72  	c := testConfig(t)
    73  	ptrType := c.config.Types.BytePtr
    74  	fun := c.Fun("entry",
    75  		Bloc("entry",
    76  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
    77  			Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil),
    78  			Goto("checkPtr")),
    79  		Bloc("checkPtr",
    80  			Valu("ptr1", OpLoad, ptrType, 0, nil, "sb", "mem"),
    81  			Valu("nilptr", OpConstNil, ptrType, 0, nil),
    82  			Valu("bool1", OpNeqPtr, c.config.Types.Bool, 0, nil, "ptr1", "nilptr"),
    83  			If("bool1", "then", "else")),
    84  		Bloc("then",
    85  			Goto("exit")),
    86  		Bloc("else",
    87  			Goto("exit")),
    88  		Bloc("exit",
    89  			Valu("phi", OpPhi, ptrType, 0, nil, "ptr1", "ptr1"),
    90  			Exit("mem")))
    91  
    92  	CheckFunc(fun.f)
    93  	fuseAll(fun.f)
    94  
    95  	for _, b := range fun.f.Blocks {
    96  		if b == fun.blocks["then"] && b.Kind != BlockInvalid {
    97  			t.Errorf("then was not eliminated, but should have")
    98  		}
    99  		if b == fun.blocks["else"] && b.Kind != BlockInvalid {
   100  			t.Errorf("else was not eliminated, but should have")
   101  		}
   102  	}
   103  }
   104  
   105  func TestFuseEliminatesEmptyBlocks(t *testing.T) {
   106  	c := testConfig(t)
   107  	fun := c.Fun("entry",
   108  		Bloc("entry",
   109  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   110  			Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil),
   111  			Goto("z0")),
   112  		Bloc("z1",
   113  			Goto("z2")),
   114  		Bloc("z3",
   115  			Goto("exit")),
   116  		Bloc("z2",
   117  			Goto("z3")),
   118  		Bloc("z0",
   119  			Goto("z1")),
   120  		Bloc("exit",
   121  			Exit("mem"),
   122  		))
   123  
   124  	CheckFunc(fun.f)
   125  	fuseAll(fun.f)
   126  
   127  	for k, b := range fun.blocks {
   128  		if k[:1] == "z" && b.Kind != BlockInvalid {
   129  			t.Errorf("%s was not eliminated, but should have", k)
   130  		}
   131  	}
   132  }
   133  
   134  func TestFuseSideEffects(t *testing.T) {
   135  	// Test that we don't fuse branches that have side effects but
   136  	// have no use (e.g. followed by infinite loop).
   137  	// See issue #36005.
   138  	c := testConfig(t)
   139  	fun := c.Fun("entry",
   140  		Bloc("entry",
   141  			Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   142  			Valu("b", OpArg, c.config.Types.Bool, 0, nil),
   143  			If("b", "then", "else")),
   144  		Bloc("then",
   145  			Valu("call1", OpStaticCall, types.TypeMem, 0, nil, "mem"),
   146  			Goto("empty")),
   147  		Bloc("else",
   148  			Valu("call2", OpStaticCall, types.TypeMem, 0, nil, "mem"),
   149  			Goto("empty")),
   150  		Bloc("empty",
   151  			Goto("loop")),
   152  		Bloc("loop",
   153  			Goto("loop")))
   154  
   155  	CheckFunc(fun.f)
   156  	fuseAll(fun.f)
   157  
   158  	for _, b := range fun.f.Blocks {
   159  		if b == fun.blocks["then"] && b.Kind == BlockInvalid {
   160  			t.Errorf("then is eliminated, but should not")
   161  		}
   162  		if b == fun.blocks["else"] && b.Kind == BlockInvalid {
   163  			t.Errorf("else is eliminated, but should not")
   164  		}
   165  	}
   166  }
   167  
   168  func BenchmarkFuse(b *testing.B) {
   169  	for _, n := range [...]int{1, 10, 100, 1000, 10000} {
   170  		b.Run(strconv.Itoa(n), func(b *testing.B) {
   171  			c := testConfig(b)
   172  
   173  			blocks := make([]bloc, 0, 2*n+3)
   174  			blocks = append(blocks,
   175  				Bloc("entry",
   176  					Valu("mem", OpInitMem, types.TypeMem, 0, nil),
   177  					Valu("cond", OpArg, c.config.Types.Bool, 0, nil),
   178  					Valu("x", OpArg, c.config.Types.Int64, 0, nil),
   179  					Goto("exit")))
   180  
   181  			phiArgs := make([]string, 0, 2*n)
   182  			for i := 0; i < n; i++ {
   183  				cname := fmt.Sprintf("c%d", i)
   184  				blocks = append(blocks,
   185  					Bloc(fmt.Sprintf("b%d", i), If("cond", cname, "merge")),
   186  					Bloc(cname, Goto("merge")))
   187  				phiArgs = append(phiArgs, "x", "x")
   188  			}
   189  			blocks = append(blocks,
   190  				Bloc("merge",
   191  					Valu("phi", OpPhi, types.TypeMem, 0, nil, phiArgs...),
   192  					Goto("exit")),
   193  				Bloc("exit",
   194  					Exit("mem")))
   195  
   196  			b.ResetTimer()
   197  			for i := 0; i < b.N; i++ {
   198  				fun := c.Fun("entry", blocks...)
   199  				fuseAll(fun.f)
   200  			}
   201  		})
   202  	}
   203  }