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 }