github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/ssa/regalloc_test.go (about) 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 ssa 6 7 import ( 8 "testing" 9 10 "github.com/go-asm/go/cmd/compile/types" 11 ) 12 13 func TestLiveControlOps(t *testing.T) { 14 c := testConfig(t) 15 f := c.Fun("entry", 16 Bloc("entry", 17 Valu("mem", OpInitMem, types.TypeMem, 0, nil), 18 Valu("x", OpAMD64MOVLconst, c.config.Types.Int8, 1, nil), 19 Valu("y", OpAMD64MOVLconst, c.config.Types.Int8, 2, nil), 20 Valu("a", OpAMD64TESTB, types.TypeFlags, 0, nil, "x", "y"), 21 Valu("b", OpAMD64TESTB, types.TypeFlags, 0, nil, "y", "x"), 22 Eq("a", "if", "exit"), 23 ), 24 Bloc("if", 25 Eq("b", "plain", "exit"), 26 ), 27 Bloc("plain", 28 Goto("exit"), 29 ), 30 Bloc("exit", 31 Exit("mem"), 32 ), 33 ) 34 flagalloc(f.f) 35 regalloc(f.f) 36 checkFunc(f.f) 37 } 38 39 // Test to make sure G register is never reloaded from spill (spill of G is okay) 40 // See #25504 41 func TestNoGetgLoadReg(t *testing.T) { 42 /* 43 Original: 44 func fff3(i int) *g { 45 gee := getg() 46 if i == 0 { 47 fff() 48 } 49 return gee // here 50 } 51 */ 52 c := testConfigARM64(t) 53 f := c.Fun("b1", 54 Bloc("b1", 55 Valu("v1", OpInitMem, types.TypeMem, 0, nil), 56 Valu("v6", OpArg, c.config.Types.Int64, 0, c.Temp(c.config.Types.Int64)), 57 Valu("v8", OpGetG, c.config.Types.Int64.PtrTo(), 0, nil, "v1"), 58 Valu("v11", OpARM64CMPconst, types.TypeFlags, 0, nil, "v6"), 59 Eq("v11", "b2", "b4"), 60 ), 61 Bloc("b4", 62 Goto("b3"), 63 ), 64 Bloc("b3", 65 Valu("v14", OpPhi, types.TypeMem, 0, nil, "v1", "v12"), 66 Valu("sb", OpSB, c.config.Types.Uintptr, 0, nil), 67 Valu("v16", OpARM64MOVDstore, types.TypeMem, 0, nil, "v8", "sb", "v14"), 68 Exit("v16"), 69 ), 70 Bloc("b2", 71 Valu("v12", OpARM64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "v1"), 72 Goto("b3"), 73 ), 74 ) 75 regalloc(f.f) 76 checkFunc(f.f) 77 // Double-check that we never restore to the G register. Regalloc should catch it, but check again anyway. 78 r := f.f.RegAlloc 79 for _, b := range f.blocks { 80 for _, v := range b.Values { 81 if v.Op == OpLoadReg && r[v.ID].String() == "g" { 82 t.Errorf("Saw OpLoadReg targeting g register: %s", v.LongString()) 83 } 84 } 85 } 86 } 87 88 // Test to make sure we don't push spills into loops. 89 // See issue #19595. 90 func TestSpillWithLoop(t *testing.T) { 91 c := testConfig(t) 92 f := c.Fun("entry", 93 Bloc("entry", 94 Valu("mem", OpInitMem, types.TypeMem, 0, nil), 95 Valu("ptr", OpArg, c.config.Types.Int64.PtrTo(), 0, c.Temp(c.config.Types.Int64)), 96 Valu("cond", OpArg, c.config.Types.Bool, 0, c.Temp(c.config.Types.Bool)), 97 Valu("ld", OpAMD64MOVQload, c.config.Types.Int64, 0, nil, "ptr", "mem"), // this value needs a spill 98 Goto("loop"), 99 ), 100 Bloc("loop", 101 Valu("memphi", OpPhi, types.TypeMem, 0, nil, "mem", "call"), 102 Valu("call", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "memphi"), 103 Valu("test", OpAMD64CMPBconst, types.TypeFlags, 0, nil, "cond"), 104 Eq("test", "next", "exit"), 105 ), 106 Bloc("next", 107 Goto("loop"), 108 ), 109 Bloc("exit", 110 Valu("store", OpAMD64MOVQstore, types.TypeMem, 0, nil, "ptr", "ld", "call"), 111 Exit("store"), 112 ), 113 ) 114 regalloc(f.f) 115 checkFunc(f.f) 116 for _, v := range f.blocks["loop"].Values { 117 if v.Op == OpStoreReg { 118 t.Errorf("spill inside loop %s", v.LongString()) 119 } 120 } 121 } 122 123 func TestSpillMove1(t *testing.T) { 124 c := testConfig(t) 125 f := c.Fun("entry", 126 Bloc("entry", 127 Valu("mem", OpInitMem, types.TypeMem, 0, nil), 128 Valu("x", OpArg, c.config.Types.Int64, 0, c.Temp(c.config.Types.Int64)), 129 Valu("p", OpArg, c.config.Types.Int64.PtrTo(), 0, c.Temp(c.config.Types.Int64.PtrTo())), 130 Valu("a", OpAMD64TESTQ, types.TypeFlags, 0, nil, "x", "x"), 131 Goto("loop1"), 132 ), 133 Bloc("loop1", 134 Valu("y", OpAMD64MULQ, c.config.Types.Int64, 0, nil, "x", "x"), 135 Eq("a", "loop2", "exit1"), 136 ), 137 Bloc("loop2", 138 Eq("a", "loop1", "exit2"), 139 ), 140 Bloc("exit1", 141 // store before call, y is available in a register 142 Valu("mem2", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem"), 143 Valu("mem3", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem2"), 144 Exit("mem3"), 145 ), 146 Bloc("exit2", 147 // store after call, y must be loaded from a spill location 148 Valu("mem4", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem"), 149 Valu("mem5", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem4"), 150 Exit("mem5"), 151 ), 152 ) 153 flagalloc(f.f) 154 regalloc(f.f) 155 checkFunc(f.f) 156 // Spill should be moved to exit2. 157 if numSpills(f.blocks["loop1"]) != 0 { 158 t.Errorf("spill present from loop1") 159 } 160 if numSpills(f.blocks["loop2"]) != 0 { 161 t.Errorf("spill present in loop2") 162 } 163 if numSpills(f.blocks["exit1"]) != 0 { 164 t.Errorf("spill present in exit1") 165 } 166 if numSpills(f.blocks["exit2"]) != 1 { 167 t.Errorf("spill missing in exit2") 168 } 169 170 } 171 172 func TestSpillMove2(t *testing.T) { 173 c := testConfig(t) 174 f := c.Fun("entry", 175 Bloc("entry", 176 Valu("mem", OpInitMem, types.TypeMem, 0, nil), 177 Valu("x", OpArg, c.config.Types.Int64, 0, c.Temp(c.config.Types.Int64)), 178 Valu("p", OpArg, c.config.Types.Int64.PtrTo(), 0, c.Temp(c.config.Types.Int64.PtrTo())), 179 Valu("a", OpAMD64TESTQ, types.TypeFlags, 0, nil, "x", "x"), 180 Goto("loop1"), 181 ), 182 Bloc("loop1", 183 Valu("y", OpAMD64MULQ, c.config.Types.Int64, 0, nil, "x", "x"), 184 Eq("a", "loop2", "exit1"), 185 ), 186 Bloc("loop2", 187 Eq("a", "loop1", "exit2"), 188 ), 189 Bloc("exit1", 190 // store after call, y must be loaded from a spill location 191 Valu("mem2", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem"), 192 Valu("mem3", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem2"), 193 Exit("mem3"), 194 ), 195 Bloc("exit2", 196 // store after call, y must be loaded from a spill location 197 Valu("mem4", OpAMD64CALLstatic, types.TypeMem, 0, AuxCallLSym("_"), "mem"), 198 Valu("mem5", OpAMD64MOVQstore, types.TypeMem, 0, nil, "p", "y", "mem4"), 199 Exit("mem5"), 200 ), 201 ) 202 flagalloc(f.f) 203 regalloc(f.f) 204 checkFunc(f.f) 205 // There should be a spill in loop1, and nowhere else. 206 // TODO: resurrect moving spills out of loops? We could put spills at the start of both exit1 and exit2. 207 if numSpills(f.blocks["loop1"]) != 1 { 208 t.Errorf("spill missing from loop1") 209 } 210 if numSpills(f.blocks["loop2"]) != 0 { 211 t.Errorf("spill present in loop2") 212 } 213 if numSpills(f.blocks["exit1"]) != 0 { 214 t.Errorf("spill present in exit1") 215 } 216 if numSpills(f.blocks["exit2"]) != 0 { 217 t.Errorf("spill present in exit2") 218 } 219 220 } 221 222 func numSpills(b *Block) int { 223 n := 0 224 for _, v := range b.Values { 225 if v.Op == OpStoreReg { 226 n++ 227 } 228 } 229 return n 230 }