github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/obj/x86/asm_test.go (about) 1 // Copyright 2018 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 x86 6 7 import ( 8 "os" 9 "path/filepath" 10 "regexp" 11 "testing" 12 13 "github.com/go-asm/go/cmd/obj" 14 "github.com/go-asm/go/cmd/objabi" 15 "github.com/go-asm/go/testenv" 16 ) 17 18 type oclassTest struct { 19 arg *obj.Addr 20 want int // Expected oclass return value for a given arg 21 } 22 23 // Filled inside init, because it's easier to do with helper functions. 24 var ( 25 oclassTestsAMD64 []*oclassTest 26 oclassTests386 []*oclassTest 27 ) 28 29 func init() { 30 // Required for tests that access any of 31 // opindex/ycover/reg/regrex global tables. 32 var ctxt obj.Link 33 instinit(&ctxt) 34 35 regAddr := func(reg int16) *obj.Addr { 36 return &obj.Addr{Type: obj.TYPE_REG, Reg: reg} 37 } 38 immAddr := func(v int64) *obj.Addr { 39 return &obj.Addr{Type: obj.TYPE_CONST, Offset: v} 40 } 41 regListAddr := func(regFrom, regTo int16) *obj.Addr { 42 return &obj.Addr{Type: obj.TYPE_REGLIST, Offset: EncodeRegisterRange(regFrom, regTo)} 43 } 44 memAddr := func(base, index int16) *obj.Addr { 45 return &obj.Addr{Type: obj.TYPE_MEM, Reg: base, Index: index} 46 } 47 48 // TODO(quasilyte): oclass doesn't return Yxxx for X/Y regs with 49 // ID higher than 7. We don't encode such instructions, but this 50 // behavior seems inconsistent. It should probably either 51 // never check for arch or do it in all cases. 52 53 oclassTestsCommon := []*oclassTest{ 54 {&obj.Addr{Type: obj.TYPE_NONE}, Ynone}, 55 {&obj.Addr{Type: obj.TYPE_BRANCH}, Ybr}, 56 {&obj.Addr{Type: obj.TYPE_TEXTSIZE}, Ytextsize}, 57 58 {&obj.Addr{Type: obj.TYPE_INDIR, Name: obj.NAME_EXTERN}, Yindir}, 59 {&obj.Addr{Type: obj.TYPE_INDIR, Name: obj.NAME_GOTREF}, Yindir}, 60 61 {&obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_AUTO}, Yiauto}, 62 {&obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_PARAM}, Yiauto}, 63 {&obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_EXTERN}, Yiauto}, 64 {&obj.Addr{Type: obj.TYPE_ADDR, Sym: &obj.LSym{Name: "runtime.duff"}}, Yi32}, 65 {&obj.Addr{Type: obj.TYPE_ADDR, Offset: 4}, Yu7}, 66 {&obj.Addr{Type: obj.TYPE_ADDR, Offset: 255}, Yu8}, 67 68 {immAddr(0), Yi0}, 69 {immAddr(1), Yi1}, 70 {immAddr(2), Yu2}, 71 {immAddr(3), Yu2}, 72 {immAddr(4), Yu7}, 73 {immAddr(86), Yu7}, 74 {immAddr(127), Yu7}, 75 {immAddr(128), Yu8}, 76 {immAddr(200), Yu8}, 77 {immAddr(255), Yu8}, 78 {immAddr(-1), Yi8}, 79 {immAddr(-100), Yi8}, 80 {immAddr(-128), Yi8}, 81 82 {regAddr(REG_AL), Yal}, 83 {regAddr(REG_AX), Yax}, 84 {regAddr(REG_DL), Yrb}, 85 {regAddr(REG_DH), Yrb}, 86 {regAddr(REG_BH), Yrb}, 87 {regAddr(REG_CL), Ycl}, 88 {regAddr(REG_CX), Ycx}, 89 {regAddr(REG_DX), Yrx}, 90 {regAddr(REG_BX), Yrx}, 91 {regAddr(REG_F0), Yf0}, 92 {regAddr(REG_F3), Yrf}, 93 {regAddr(REG_F7), Yrf}, 94 {regAddr(REG_M0), Ymr}, 95 {regAddr(REG_M3), Ymr}, 96 {regAddr(REG_M7), Ymr}, 97 {regAddr(REG_X0), Yxr0}, 98 {regAddr(REG_X6), Yxr}, 99 {regAddr(REG_X13), Yxr}, 100 {regAddr(REG_X20), YxrEvex}, 101 {regAddr(REG_X31), YxrEvex}, 102 {regAddr(REG_Y0), Yyr}, 103 {regAddr(REG_Y6), Yyr}, 104 {regAddr(REG_Y13), Yyr}, 105 {regAddr(REG_Y20), YyrEvex}, 106 {regAddr(REG_Y31), YyrEvex}, 107 {regAddr(REG_Z0), Yzr}, 108 {regAddr(REG_Z6), Yzr}, 109 {regAddr(REG_K0), Yk0}, 110 {regAddr(REG_K5), Yknot0}, 111 {regAddr(REG_K7), Yknot0}, 112 {regAddr(REG_CS), Ycs}, 113 {regAddr(REG_SS), Yss}, 114 {regAddr(REG_DS), Yds}, 115 {regAddr(REG_ES), Yes}, 116 {regAddr(REG_FS), Yfs}, 117 {regAddr(REG_GS), Ygs}, 118 {regAddr(REG_TLS), Ytls}, 119 {regAddr(REG_GDTR), Ygdtr}, 120 {regAddr(REG_IDTR), Yidtr}, 121 {regAddr(REG_LDTR), Yldtr}, 122 {regAddr(REG_MSW), Ymsw}, 123 {regAddr(REG_TASK), Ytask}, 124 {regAddr(REG_CR0), Ycr0}, 125 {regAddr(REG_CR5), Ycr5}, 126 {regAddr(REG_CR8), Ycr8}, 127 {regAddr(REG_DR0), Ydr0}, 128 {regAddr(REG_DR5), Ydr5}, 129 {regAddr(REG_DR7), Ydr7}, 130 {regAddr(REG_TR0), Ytr0}, 131 {regAddr(REG_TR5), Ytr5}, 132 {regAddr(REG_TR7), Ytr7}, 133 134 {regListAddr(REG_X0, REG_X3), YxrEvexMulti4}, 135 {regListAddr(REG_X4, REG_X7), YxrEvexMulti4}, 136 {regListAddr(REG_Y0, REG_Y3), YyrEvexMulti4}, 137 {regListAddr(REG_Y4, REG_Y7), YyrEvexMulti4}, 138 {regListAddr(REG_Z0, REG_Z3), YzrMulti4}, 139 {regListAddr(REG_Z4, REG_Z7), YzrMulti4}, 140 141 {memAddr(REG_AL, REG_NONE), Ym}, 142 {memAddr(REG_AL, REG_SI), Ym}, 143 {memAddr(REG_SI, REG_CX), Ym}, 144 {memAddr(REG_DI, REG_X0), Yxvm}, 145 {memAddr(REG_DI, REG_X7), Yxvm}, 146 {memAddr(REG_DI, REG_Y0), Yyvm}, 147 {memAddr(REG_DI, REG_Y7), Yyvm}, 148 {memAddr(REG_DI, REG_Z0), Yzvm}, 149 {memAddr(REG_DI, REG_Z7), Yzvm}, 150 } 151 152 oclassTestsAMD64 = []*oclassTest{ 153 {immAddr(-200), Ys32}, 154 {immAddr(500), Ys32}, 155 {immAddr(0x7FFFFFFF), Ys32}, 156 {immAddr(0x7FFFFFFF + 1), Yi32}, 157 {immAddr(0xFFFFFFFF), Yi32}, 158 {immAddr(0xFFFFFFFF + 1), Yi64}, 159 160 {regAddr(REG_BPB), Yrb}, 161 {regAddr(REG_SIB), Yrb}, 162 {regAddr(REG_DIB), Yrb}, 163 {regAddr(REG_R8B), Yrb}, 164 {regAddr(REG_R12B), Yrb}, 165 {regAddr(REG_R8), Yrl}, 166 {regAddr(REG_R13), Yrl}, 167 {regAddr(REG_R15), Yrl}, 168 {regAddr(REG_SP), Yrl}, 169 {regAddr(REG_SI), Yrl}, 170 {regAddr(REG_DI), Yrl}, 171 {regAddr(REG_Z13), Yzr}, 172 {regAddr(REG_Z20), Yzr}, 173 {regAddr(REG_Z31), Yzr}, 174 175 {regListAddr(REG_X10, REG_X13), YxrEvexMulti4}, 176 {regListAddr(REG_X24, REG_X27), YxrEvexMulti4}, 177 {regListAddr(REG_Y10, REG_Y13), YyrEvexMulti4}, 178 {regListAddr(REG_Y24, REG_Y27), YyrEvexMulti4}, 179 {regListAddr(REG_Z10, REG_Z13), YzrMulti4}, 180 {regListAddr(REG_Z24, REG_Z27), YzrMulti4}, 181 182 {memAddr(REG_DI, REG_X20), YxvmEvex}, 183 {memAddr(REG_DI, REG_X27), YxvmEvex}, 184 {memAddr(REG_DI, REG_Y20), YyvmEvex}, 185 {memAddr(REG_DI, REG_Y27), YyvmEvex}, 186 {memAddr(REG_DI, REG_Z20), Yzvm}, 187 {memAddr(REG_DI, REG_Z27), Yzvm}, 188 } 189 190 oclassTests386 = []*oclassTest{ 191 {&obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_EXTERN, Sym: &obj.LSym{}}, Yi32}, 192 193 {immAddr(-200), Yi32}, 194 195 {regAddr(REG_SP), Yrl32}, 196 {regAddr(REG_SI), Yrl32}, 197 {regAddr(REG_DI), Yrl32}, 198 } 199 200 // Add tests that are arch-independent for all sets. 201 oclassTestsAMD64 = append(oclassTestsAMD64, oclassTestsCommon...) 202 oclassTests386 = append(oclassTests386, oclassTestsCommon...) 203 } 204 205 func TestOclass(t *testing.T) { 206 runTest := func(t *testing.T, ctxt *obj.Link, tests []*oclassTest) { 207 var p obj.Prog 208 for _, test := range tests { 209 have := oclass(ctxt, &p, test.arg) 210 if have != test.want { 211 t.Errorf("oclass(%q):\nhave: %d\nwant: %d", 212 obj.Dconv(&p, test.arg), have, test.want) 213 } 214 } 215 } 216 217 // TODO(quasilyte): test edge cases for Hsolaris, etc? 218 219 t.Run("linux/AMD64", func(t *testing.T) { 220 ctxtAMD64 := obj.Linknew(&Linkamd64) 221 ctxtAMD64.Headtype = objabi.Hlinux // See #32028 222 runTest(t, ctxtAMD64, oclassTestsAMD64) 223 }) 224 225 t.Run("linux/386", func(t *testing.T) { 226 ctxt386 := obj.Linknew(&Link386) 227 ctxt386.Headtype = objabi.Hlinux // See #32028 228 runTest(t, ctxt386, oclassTests386) 229 }) 230 } 231 232 func TestRegisterListEncDec(t *testing.T) { 233 tests := []struct { 234 printed string 235 reg0 int16 236 reg1 int16 237 }{ 238 {"[R10-R13]", REG_R10, REG_R13}, 239 {"[X0-AX]", REG_X0, REG_AX}, 240 241 {"[X0-X3]", REG_X0, REG_X3}, 242 {"[X21-X24]", REG_X21, REG_X24}, 243 244 {"[Y0-Y3]", REG_Y0, REG_Y3}, 245 {"[Y21-Y24]", REG_Y21, REG_Y24}, 246 247 {"[Z0-Z3]", REG_Z0, REG_Z3}, 248 {"[Z21-Z24]", REG_Z21, REG_Z24}, 249 } 250 251 for _, test := range tests { 252 enc := EncodeRegisterRange(test.reg0, test.reg1) 253 reg0, reg1 := decodeRegisterRange(enc) 254 255 if int16(reg0) != test.reg0 { 256 t.Errorf("%s reg0 mismatch: have %d, want %d", 257 test.printed, reg0, test.reg0) 258 } 259 if int16(reg1) != test.reg1 { 260 t.Errorf("%s reg1 mismatch: have %d, want %d", 261 test.printed, reg1, test.reg1) 262 } 263 wantPrinted := test.printed 264 if rlconv(enc) != wantPrinted { 265 t.Errorf("%s string mismatch: have %s, want %s", 266 test.printed, rlconv(enc), wantPrinted) 267 } 268 } 269 } 270 271 func TestRegIndex(t *testing.T) { 272 tests := []struct { 273 regFrom int 274 regTo int 275 }{ 276 {REG_AL, REG_R15B}, 277 {REG_AX, REG_R15}, 278 {REG_M0, REG_M7}, 279 {REG_K0, REG_K7}, 280 {REG_X0, REG_X31}, 281 {REG_Y0, REG_Y31}, 282 {REG_Z0, REG_Z31}, 283 } 284 285 for _, test := range tests { 286 for index, reg := 0, test.regFrom; reg <= test.regTo; index, reg = index+1, reg+1 { 287 have := regIndex(int16(reg)) 288 want := index 289 if have != want { 290 regName := rconv(int(reg)) 291 t.Errorf("regIndex(%s):\nhave: %d\nwant: %d", 292 regName, have, want) 293 } 294 } 295 } 296 } 297 298 // TestPCALIGN verifies the correctness of the PCALIGN by checking if the 299 // code can be aligned to the alignment value. 300 func TestPCALIGN(t *testing.T) { 301 testenv.MustHaveGoBuild(t) 302 dir := t.TempDir() 303 tmpfile := filepath.Join(dir, "test.s") 304 tmpout := filepath.Join(dir, "test.o") 305 306 var testCases = []struct { 307 name string 308 code string 309 out string 310 }{ 311 { 312 name: "8-byte alignment", 313 code: "TEXT ·foo(SB),$0-0\nMOVQ $0, AX\nPCALIGN $8\nMOVQ $1, BX\nRET\n", 314 out: `0x0008\s00008\s\(.*\)\tMOVQ\t\$1,\sBX`, 315 }, 316 { 317 name: "16-byte alignment", 318 code: "TEXT ·foo(SB),$0-0\nMOVQ $0, AX\nPCALIGN $16\nMOVQ $2, CX\nRET\n", 319 out: `0x0010\s00016\s\(.*\)\tMOVQ\t\$2,\sCX`, 320 }, 321 } 322 323 for _, test := range testCases { 324 if err := os.WriteFile(tmpfile, []byte(test.code), 0644); err != nil { 325 t.Fatal(err) 326 } 327 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-S", "-o", tmpout, tmpfile) 328 cmd.Env = append(os.Environ(), "GOARCH=amd64", "GOOS=linux") 329 out, err := cmd.CombinedOutput() 330 if err != nil { 331 t.Errorf("The %s build failed: %v, output: %s", test.name, err, out) 332 continue 333 } 334 335 matched, err := regexp.MatchString(test.out, string(out)) 336 if err != nil { 337 t.Fatal(err) 338 } 339 if !matched { 340 t.Errorf("The %s testing failed!\ninput: %s\noutput: %s\n", test.name, test.code, out) 341 } 342 } 343 }