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 }