github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/ssa/branchelim_test.go (about)

     1  // Copyright 2017 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  	"testing"
     9  
    10  	"github.com/go-asm/go/cmd/compile/types"
    11  )
    12  
    13  // Test that a trivial 'if' is eliminated
    14  func TestBranchElimIf(t *testing.T) {
    15  	var testData = []struct {
    16  		arch    string
    17  		intType string
    18  		ok      bool
    19  	}{
    20  		{"arm64", "int32", true},
    21  		{"amd64", "int32", true},
    22  		{"amd64", "int8", false},
    23  	}
    24  
    25  	for _, data := range testData {
    26  		t.Run(data.arch+"/"+data.intType, func(t *testing.T) {
    27  			c := testConfigArch(t, data.arch)
    28  			boolType := c.config.Types.Bool
    29  			var intType *types.Type
    30  			switch data.intType {
    31  			case "int32":
    32  				intType = c.config.Types.Int32
    33  			case "int8":
    34  				intType = c.config.Types.Int8
    35  			default:
    36  				t.Fatal("invalid integer type:", data.intType)
    37  			}
    38  			fun := c.Fun("entry",
    39  				Bloc("entry",
    40  					Valu("start", OpInitMem, types.TypeMem, 0, nil),
    41  					Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil),
    42  					Valu("const1", OpConst32, intType, 1, nil),
    43  					Valu("const2", OpConst32, intType, 2, nil),
    44  					Valu("addr", OpAddr, boolType.PtrTo(), 0, nil, "sb"),
    45  					Valu("cond", OpLoad, boolType, 0, nil, "addr", "start"),
    46  					If("cond", "b2", "b3")),
    47  				Bloc("b2",
    48  					Goto("b3")),
    49  				Bloc("b3",
    50  					Valu("phi", OpPhi, intType, 0, nil, "const1", "const2"),
    51  					Valu("retstore", OpStore, types.TypeMem, 0, nil, "phi", "sb", "start"),
    52  					Exit("retstore")))
    53  
    54  			CheckFunc(fun.f)
    55  			branchelim(fun.f)
    56  			CheckFunc(fun.f)
    57  			Deadcode(fun.f)
    58  			CheckFunc(fun.f)
    59  
    60  			if data.ok {
    61  
    62  				if len(fun.f.Blocks) != 1 {
    63  					t.Fatalf("expected 1 block after branchelim and deadcode; found %d", len(fun.f.Blocks))
    64  				}
    65  				if fun.values["phi"].Op != OpCondSelect {
    66  					t.Fatalf("expected phi op to be CondSelect; found op %s", fun.values["phi"].Op)
    67  				}
    68  				if fun.values["phi"].Args[2] != fun.values["cond"] {
    69  					t.Errorf("expected CondSelect condition to be %s; found %s", fun.values["cond"], fun.values["phi"].Args[2])
    70  				}
    71  				if fun.blocks["entry"].Kind != BlockExit {
    72  					t.Errorf("expected entry to be BlockExit; found kind %s", fun.blocks["entry"].Kind.String())
    73  				}
    74  			} else {
    75  				if len(fun.f.Blocks) != 3 {
    76  					t.Fatalf("expected 3 block after branchelim and deadcode; found %d", len(fun.f.Blocks))
    77  				}
    78  			}
    79  		})
    80  	}
    81  }
    82  
    83  // Test that a trivial if/else is eliminated
    84  func TestBranchElimIfElse(t *testing.T) {
    85  	for _, arch := range []string{"arm64", "amd64"} {
    86  		t.Run(arch, func(t *testing.T) {
    87  			c := testConfigArch(t, arch)
    88  			boolType := c.config.Types.Bool
    89  			intType := c.config.Types.Int32
    90  			fun := c.Fun("entry",
    91  				Bloc("entry",
    92  					Valu("start", OpInitMem, types.TypeMem, 0, nil),
    93  					Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil),
    94  					Valu("const1", OpConst32, intType, 1, nil),
    95  					Valu("const2", OpConst32, intType, 2, nil),
    96  					Valu("addr", OpAddr, boolType.PtrTo(), 0, nil, "sb"),
    97  					Valu("cond", OpLoad, boolType, 0, nil, "addr", "start"),
    98  					If("cond", "b2", "b3")),
    99  				Bloc("b2",
   100  					Goto("b4")),
   101  				Bloc("b3",
   102  					Goto("b4")),
   103  				Bloc("b4",
   104  					Valu("phi", OpPhi, intType, 0, nil, "const1", "const2"),
   105  					Valu("retstore", OpStore, types.TypeMem, 0, nil, "phi", "sb", "start"),
   106  					Exit("retstore")))
   107  
   108  			CheckFunc(fun.f)
   109  			branchelim(fun.f)
   110  			CheckFunc(fun.f)
   111  			Deadcode(fun.f)
   112  			CheckFunc(fun.f)
   113  
   114  			if len(fun.f.Blocks) != 1 {
   115  				t.Fatalf("expected 1 block after branchelim; found %d", len(fun.f.Blocks))
   116  			}
   117  			if fun.values["phi"].Op != OpCondSelect {
   118  				t.Fatalf("expected phi op to be CondSelect; found op %s", fun.values["phi"].Op)
   119  			}
   120  			if fun.values["phi"].Args[2] != fun.values["cond"] {
   121  				t.Errorf("expected CondSelect condition to be %s; found %s", fun.values["cond"], fun.values["phi"].Args[2])
   122  			}
   123  			if fun.blocks["entry"].Kind != BlockExit {
   124  				t.Errorf("expected entry to be BlockExit; found kind %s", fun.blocks["entry"].Kind.String())
   125  			}
   126  		})
   127  	}
   128  }
   129  
   130  // Test that an if/else CFG that loops back
   131  // into itself does *not* get eliminated.
   132  func TestNoBranchElimLoop(t *testing.T) {
   133  	for _, arch := range []string{"arm64", "amd64"} {
   134  		t.Run(arch, func(t *testing.T) {
   135  			c := testConfigArch(t, arch)
   136  			boolType := c.config.Types.Bool
   137  			intType := c.config.Types.Int32
   138  
   139  			// The control flow here is totally bogus,
   140  			// but a dead cycle seems like the only plausible
   141  			// way to arrive at a diamond CFG that is also a loop.
   142  			fun := c.Fun("entry",
   143  				Bloc("entry",
   144  					Valu("start", OpInitMem, types.TypeMem, 0, nil),
   145  					Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil),
   146  					Valu("const2", OpConst32, intType, 2, nil),
   147  					Valu("const3", OpConst32, intType, 3, nil),
   148  					Goto("b5")),
   149  				Bloc("b2",
   150  					Valu("addr", OpAddr, boolType.PtrTo(), 0, nil, "sb"),
   151  					Valu("cond", OpLoad, boolType, 0, nil, "addr", "start"),
   152  					Valu("phi", OpPhi, intType, 0, nil, "const2", "const3"),
   153  					If("cond", "b3", "b4")),
   154  				Bloc("b3",
   155  					Goto("b2")),
   156  				Bloc("b4",
   157  					Goto("b2")),
   158  				Bloc("b5",
   159  					Exit("start")))
   160  
   161  			CheckFunc(fun.f)
   162  			branchelim(fun.f)
   163  			CheckFunc(fun.f)
   164  
   165  			if len(fun.f.Blocks) != 5 {
   166  				t.Errorf("expected 5 block after branchelim; found %d", len(fun.f.Blocks))
   167  			}
   168  			if fun.values["phi"].Op != OpPhi {
   169  				t.Errorf("expected phi op to be CondSelect; found op %s", fun.values["phi"].Op)
   170  			}
   171  		})
   172  	}
   173  }