github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/engine/wazevo/backend/isa/arm64/lower_mem_test.go (about) 1 package arm64 2 3 import ( 4 "fmt" 5 "math" 6 "strconv" 7 "strings" 8 "testing" 9 10 "github.com/wasilibs/wazerox/internal/engine/wazevo/backend" 11 "github.com/wasilibs/wazerox/internal/engine/wazevo/backend/regalloc" 12 "github.com/wasilibs/wazerox/internal/engine/wazevo/ssa" 13 "github.com/wasilibs/wazerox/internal/testing/require" 14 ) 15 16 func TestAddressMode_format(t *testing.T) { 17 t.Run("addressModeKindRegScaledExtended", func(t *testing.T) { 18 require.Equal(t, 19 "[x1, w0, UXTW #0x1]", 20 addressMode{ 21 kind: addressModeKindRegScaledExtended, 22 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 23 rm: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 24 extOp: extendOpUXTW, 25 imm: 0, 26 }.format(16), 27 ) 28 require.Equal(t, 29 "[x1, w0, SXTW #0x1]", 30 addressMode{ 31 kind: addressModeKindRegScaledExtended, 32 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 33 rm: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 34 extOp: extendOpSXTW, 35 imm: 0, 36 }.format(16), 37 ) 38 require.Equal(t, 39 "[x1, w0, UXTW #0x2]", 40 addressMode{ 41 kind: addressModeKindRegScaledExtended, 42 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 43 rm: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 44 extOp: extendOpUXTW, 45 imm: 0, 46 }.format(32), 47 ) 48 require.Equal(t, 49 "[x1, w0, SXTW #0x2]", 50 addressMode{ 51 kind: addressModeKindRegScaledExtended, 52 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 53 rm: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 54 extOp: extendOpSXTW, 55 imm: 0, 56 }.format(32), 57 ) 58 require.Equal(t, 59 "[x1, w0, UXTW #0x3]", 60 addressMode{ 61 kind: addressModeKindRegScaledExtended, 62 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 63 rm: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 64 extOp: extendOpUXTW, 65 imm: 0, 66 }.format(64), 67 ) 68 require.Equal(t, 69 "[x1, w0, SXTW #0x3]", 70 addressMode{ 71 kind: addressModeKindRegScaledExtended, 72 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 73 rm: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 74 extOp: extendOpSXTW, 75 imm: 0, 76 }.format(64), 77 ) 78 }) 79 t.Run("addressModeKindRegScaled", func(t *testing.T) { 80 require.Equal(t, 81 "[x1, w0, lsl #0x1]", 82 addressMode{ 83 kind: addressModeKindRegScaled, 84 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 85 rm: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 86 extOp: extendOpUXTW, 87 imm: 0, 88 }.format(16), 89 ) 90 require.Equal(t, 91 "[x1, w0, lsl #0x1]", 92 addressMode{ 93 kind: addressModeKindRegScaled, 94 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 95 rm: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 96 extOp: extendOpSXTW, 97 imm: 0, 98 }.format(16), 99 ) 100 require.Equal(t, 101 "[x1, w0, lsl #0x2]", 102 addressMode{ 103 kind: addressModeKindRegScaled, 104 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 105 rm: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 106 extOp: extendOpUXTW, 107 imm: 0, 108 }.format(32), 109 ) 110 require.Equal(t, 111 "[x1, w0, lsl #0x2]", 112 addressMode{ 113 kind: addressModeKindRegScaled, 114 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 115 rm: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 116 extOp: extendOpSXTW, 117 imm: 0, 118 }.format(32), 119 ) 120 require.Equal(t, 121 "[x1, w0, lsl #0x3]", 122 addressMode{ 123 kind: addressModeKindRegScaled, 124 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 125 rm: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 126 extOp: extendOpUXTW, 127 imm: 0, 128 }.format(64), 129 ) 130 require.Equal(t, 131 "[x1, w0, lsl #0x3]", 132 addressMode{ 133 kind: addressModeKindRegScaled, 134 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 135 rm: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 136 extOp: extendOpSXTW, 137 imm: 0, 138 }.format(64), 139 ) 140 }) 141 t.Run("addressModeKindRegExtended", func(t *testing.T) { 142 require.Equal(t, 143 "[x1, w0, UXTW]", 144 addressMode{ 145 kind: addressModeKindRegExtended, 146 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 147 rm: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 148 extOp: extendOpUXTW, 149 imm: 0, 150 }.format(16), 151 ) 152 require.Equal(t, 153 "[x1, w0, SXTW]", 154 addressMode{ 155 kind: addressModeKindRegExtended, 156 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 157 rm: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 158 extOp: extendOpSXTW, 159 imm: 0, 160 }.format(64), 161 ) 162 }) 163 t.Run("addressModeKindRegReg", func(t *testing.T) { 164 require.Equal(t, 165 "[x1, w29]", 166 addressMode{ 167 kind: addressModeKindRegReg, 168 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 169 rm: regalloc.FromRealReg(x29, regalloc.RegTypeInt), 170 extOp: extendOpUXTW, // To indicate that the index reg is 32-bit. 171 imm: 0, 172 }.format(64), 173 ) 174 require.Equal(t, 175 "[x1, x29]", 176 addressMode{ 177 kind: addressModeKindRegReg, 178 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 179 rm: regalloc.FromRealReg(x29, regalloc.RegTypeInt), 180 extOp: extendOpUXTX, // To indicate that the index reg is 64-bit. 181 imm: 0, 182 }.format(64), 183 ) 184 }) 185 t.Run("addressModeKindRegSignedImm9", func(t *testing.T) { 186 require.Equal(t, 187 "[x1]", 188 addressMode{ 189 kind: addressModeKindRegSignedImm9, 190 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 191 imm: 0, 192 }.format(64), 193 ) 194 require.Equal(t, 195 "[x1, #-0x100]", 196 addressMode{ 197 kind: addressModeKindRegSignedImm9, 198 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 199 imm: math.MinInt8 << 1, 200 }.format(64), 201 ) 202 require.Equal(t, 203 "[x1, #0xff]", 204 addressMode{ 205 kind: addressModeKindRegSignedImm9, 206 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 207 imm: (math.MaxInt8 << 1) + 1, 208 }.format(64), 209 ) 210 }) 211 212 t.Run("addressModeKindRegUnsignedImm12", func(t *testing.T) { 213 require.Equal(t, 214 "[x1]", 215 addressMode{ 216 kind: addressModeKindRegUnsignedImm12, 217 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 218 imm: 0, 219 }.format(64), 220 ) 221 require.Equal(t, 222 "[x1, #0xfff]", 223 addressMode{ 224 kind: addressModeKindRegUnsignedImm12, 225 rn: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 226 imm: 4095, 227 }.format(64), 228 ) 229 }) 230 } 231 232 func Test_offsetFitsInAddressModeKindRegUnsignedImm12(t *testing.T) { 233 for _, tc := range []struct { 234 dstSizeInBits byte 235 offset int64 236 exp bool 237 }{ 238 {dstSizeInBits: 8, offset: -1, exp: false}, 239 {dstSizeInBits: 8, offset: 0, exp: false}, 240 {dstSizeInBits: 8, offset: 1, exp: true}, 241 {dstSizeInBits: 8, offset: 2, exp: true}, 242 {dstSizeInBits: 8, offset: 3, exp: true}, 243 {dstSizeInBits: 8, offset: 4095, exp: true}, 244 {dstSizeInBits: 8, offset: 4096, exp: false}, 245 {dstSizeInBits: 16, offset: -2, exp: false}, 246 {dstSizeInBits: 16, offset: -1, exp: false}, 247 {dstSizeInBits: 16, offset: 0, exp: false}, 248 {dstSizeInBits: 16, offset: 1, exp: false}, 249 {dstSizeInBits: 16, offset: 2, exp: true}, 250 {dstSizeInBits: 16, offset: 3, exp: false}, 251 {dstSizeInBits: 16, offset: 4095, exp: false}, 252 {dstSizeInBits: 16, offset: 4096, exp: true}, 253 {dstSizeInBits: 16, offset: 4095 * 2, exp: true}, 254 {dstSizeInBits: 16, offset: 4095*2 + 1, exp: false}, 255 {dstSizeInBits: 32, offset: -4, exp: false}, 256 {dstSizeInBits: 32, offset: -1, exp: false}, 257 {dstSizeInBits: 32, offset: 0, exp: false}, 258 {dstSizeInBits: 32, offset: 1, exp: false}, 259 {dstSizeInBits: 32, offset: 2, exp: false}, 260 {dstSizeInBits: 32, offset: 3, exp: false}, 261 {dstSizeInBits: 32, offset: 4, exp: true}, 262 {dstSizeInBits: 32, offset: 4095, exp: false}, 263 {dstSizeInBits: 32, offset: 4096, exp: true}, 264 {dstSizeInBits: 32, offset: 4095 * 4, exp: true}, 265 {dstSizeInBits: 32, offset: 4095*4 + 1, exp: false}, 266 {dstSizeInBits: 64, offset: -8, exp: false}, 267 {dstSizeInBits: 64, offset: -1, exp: false}, 268 {dstSizeInBits: 64, offset: 0, exp: false}, 269 {dstSizeInBits: 64, offset: 1, exp: false}, 270 {dstSizeInBits: 64, offset: 2, exp: false}, 271 {dstSizeInBits: 64, offset: 3, exp: false}, 272 {dstSizeInBits: 64, offset: 4, exp: false}, 273 {dstSizeInBits: 64, offset: 8, exp: true}, 274 {dstSizeInBits: 64, offset: 4095, exp: false}, 275 {dstSizeInBits: 64, offset: 4096, exp: true}, 276 {dstSizeInBits: 64, offset: 4095 * 8, exp: true}, 277 {dstSizeInBits: 64, offset: 4095*8 + 1, exp: false}, 278 } { 279 require.Equal( 280 t, tc.exp, 281 offsetFitsInAddressModeKindRegUnsignedImm12(tc.dstSizeInBits, tc.offset), 282 fmt.Sprintf("dstSizeInBits=%d, offset=%d", tc.dstSizeInBits, tc.offset), 283 ) 284 } 285 } 286 287 func Test_offsetFitsInAddressModeKindRegSignedImm9(t *testing.T) { 288 require.Equal(t, true, offsetFitsInAddressModeKindRegSignedImm9(0)) 289 require.Equal(t, false, offsetFitsInAddressModeKindRegSignedImm9(-257)) 290 require.Equal(t, true, offsetFitsInAddressModeKindRegSignedImm9(-256)) 291 require.Equal(t, true, offsetFitsInAddressModeKindRegSignedImm9(255)) 292 require.Equal(t, false, offsetFitsInAddressModeKindRegSignedImm9(256)) 293 } 294 295 func TestMachine_collectAddends(t *testing.T) { 296 v1000, v2000 := regalloc.VReg(1000).SetRegType(regalloc.RegTypeInt), regalloc.VReg(2000).SetRegType(regalloc.RegTypeInt) 297 addParam := func(ctx *mockCompiler, b ssa.Builder, typ ssa.Type) ssa.Value { 298 p := b.CurrentBlock().AddParam(b, typ) 299 ctx.definitions[p] = &backend.SSAValueDefinition{BlockParamValue: p, BlkParamVReg: v1000} 300 return p 301 } 302 insertI32Const := func(m *mockCompiler, b ssa.Builder, v uint32) *ssa.Instruction { 303 inst := b.AllocateInstruction() 304 inst.AsIconst32(v) 305 b.InsertInstruction(inst) 306 m.definitions[inst.Return()] = &backend.SSAValueDefinition{Instr: inst} 307 return inst 308 } 309 insertI64Const := func(m *mockCompiler, b ssa.Builder, v uint64) *ssa.Instruction { 310 inst := b.AllocateInstruction() 311 inst.AsIconst64(v) 312 b.InsertInstruction(inst) 313 m.definitions[inst.Return()] = &backend.SSAValueDefinition{Instr: inst} 314 return inst 315 } 316 insertIadd := func(m *mockCompiler, b ssa.Builder, lhs, rhs ssa.Value) *ssa.Instruction { 317 inst := b.AllocateInstruction() 318 inst.AsIadd(lhs, rhs) 319 b.InsertInstruction(inst) 320 m.definitions[inst.Return()] = &backend.SSAValueDefinition{Instr: inst} 321 return inst 322 } 323 insertExt := func(m *mockCompiler, b ssa.Builder, v ssa.Value, from, to byte, signed bool) *ssa.Instruction { 324 inst := b.AllocateInstruction() 325 if signed { 326 inst.AsSExtend(v, from, to) 327 } else { 328 inst.AsUExtend(v, from, to) 329 } 330 b.InsertInstruction(inst) 331 m.definitions[inst.Return()] = &backend.SSAValueDefinition{Instr: inst} 332 return inst 333 } 334 335 for _, tc := range []struct { 336 name string 337 setup func(*mockCompiler, ssa.Builder, *machine) (ptr ssa.Value, verify func(t *testing.T)) 338 exp32s []addend32 339 exp64s []regalloc.VReg 340 offset int64 341 }{ 342 { 343 name: "non merged", 344 setup: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, verify func(t *testing.T)) { 345 ptr = addParam(ctx, b, ssa.TypeI64) 346 return ptr, func(t *testing.T) {} 347 }, 348 exp64s: []regalloc.VReg{v1000}, 349 }, 350 { 351 name: "i32 constant folded", 352 setup: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, verify func(t *testing.T)) { 353 minus1 := int32(-1) 354 c1, c2, c3, c4 := insertI32Const(ctx, b, 1), insertI32Const(ctx, b, 2), insertI32Const(ctx, b, 3), insertI32Const(ctx, b, uint32(minus1)) 355 iadd1, iadd2 := insertIadd(ctx, b, c1.Return(), c2.Return()), insertIadd(ctx, b, c3.Return(), c4.Return()) 356 iadd3 := insertIadd(ctx, b, iadd1.Return(), iadd2.Return()) 357 return iadd3.Return(), func(t *testing.T) { 358 for _, instr := range []*ssa.Instruction{iadd1, iadd2, iadd3} { 359 require.True(t, instr.Lowered()) 360 } 361 } 362 }, 363 offset: 1 + 2 + 3 - 1, 364 }, 365 { 366 name: "i64 constant folded", 367 setup: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, verify func(t *testing.T)) { 368 minus1 := int32(-1) 369 c1, c2, c3, c4 := insertI64Const(ctx, b, 1), insertI64Const(ctx, b, 2), insertI64Const(ctx, b, 3), insertI64Const(ctx, b, uint64(minus1)) 370 iadd1, iadd2 := insertIadd(ctx, b, c1.Return(), c2.Return()), insertIadd(ctx, b, c3.Return(), c4.Return()) 371 iadd3 := insertIadd(ctx, b, iadd1.Return(), iadd2.Return()) 372 return iadd3.Return(), func(t *testing.T) { 373 for _, instr := range []*ssa.Instruction{iadd1, iadd2, iadd3} { 374 require.True(t, instr.Lowered()) 375 } 376 } 377 }, 378 offset: 1 + 2 + 3 - 1, 379 }, 380 { 381 name: "constant folded with one 32 value", 382 setup: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, verify func(t *testing.T)) { 383 param := addParam(ctx, b, ssa.TypeI32) 384 minus1 := int32(-1) 385 c1, c2, c3, c4 := insertI32Const(ctx, b, 1), insertI32Const(ctx, b, 2), insertI32Const(ctx, b, 3), insertI32Const(ctx, b, uint32(minus1)) 386 iadd1, iadd2 := insertIadd(ctx, b, c1.Return(), c2.Return()), insertIadd(ctx, b, c3.Return(), c4.Return()) 387 iadd3 := insertIadd(ctx, b, iadd1.Return(), iadd2.Return()) 388 iadd4 := insertIadd(ctx, b, param, iadd3.Return()) 389 390 return iadd4.Return(), func(t *testing.T) { 391 for _, instr := range []*ssa.Instruction{iadd1, iadd2, iadd3, iadd4} { 392 require.True(t, instr.Lowered()) 393 } 394 // Param must be zero-extended. 395 require.Equal(t, "uxtw x1?, w1000?", formatEmittedInstructionsInCurrentBlock(m)) 396 } 397 }, 398 exp64s: []regalloc.VReg{regalloc.VReg(1).SetRegType(regalloc.RegTypeInt)}, 399 offset: 1 + 2 + 3 - 1, 400 }, 401 { 402 name: "one 64 value + sign-extended (32->64) instr", 403 setup: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, verify func(t *testing.T)) { 404 param := addParam(ctx, b, ssa.TypeI64) 405 c1, c2 := insertI32Const(ctx, b, 1), insertI32Const(ctx, b, 2) 406 iadd1 := insertIadd(ctx, b, c1.Return(), c2.Return()) 407 ext := insertExt(ctx, b, iadd1.Return(), 32, 64, true) 408 ctx.vRegMap[ext.Arg()] = v2000 409 iadd4 := insertIadd(ctx, b, param, ext.Return()) 410 return iadd4.Return(), func(t *testing.T) { 411 for _, instr := range []*ssa.Instruction{ext, iadd4} { 412 require.True(t, instr.Lowered()) 413 } 414 } 415 }, 416 exp64s: []regalloc.VReg{v1000 /* == param */}, 417 exp32s: []addend32{{v2000, extendOpSXTW} /* sign-extended iadd1 */}, 418 }, 419 { 420 name: "one 64 value + sign-extended (32->64) const", 421 setup: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, verify func(t *testing.T)) { 422 param := addParam(ctx, b, ssa.TypeI64) 423 minus1 := int32(-1) 424 c1 := insertI32Const(ctx, b, uint32(minus1)) 425 ext := insertExt(ctx, b, c1.Return(), 32, 64, true) 426 ctx.vRegMap[ext.Arg()] = v2000 427 iadd4 := insertIadd(ctx, b, param, ext.Return()) 428 return iadd4.Return(), func(t *testing.T) { 429 for _, instr := range []*ssa.Instruction{ext, iadd4} { 430 require.True(t, instr.Lowered()) 431 } 432 } 433 }, 434 exp64s: []regalloc.VReg{v1000 /* == param */}, 435 offset: -1, 436 }, 437 { 438 name: "one 64 value + zero-extended (32->64) const", 439 setup: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, verify func(t *testing.T)) { 440 param := addParam(ctx, b, ssa.TypeI64) 441 minus1 := int32(-1) 442 c1 := insertI32Const(ctx, b, uint32(minus1)) 443 ext := insertExt(ctx, b, c1.Return(), 32, 64, false) 444 ctx.vRegMap[ext.Arg()] = v2000 445 iadd4 := insertIadd(ctx, b, param, ext.Return()) 446 return iadd4.Return(), func(t *testing.T) { 447 for _, instr := range []*ssa.Instruction{ext, iadd4} { 448 require.True(t, instr.Lowered()) 449 } 450 } 451 }, 452 exp64s: []regalloc.VReg{v1000 /* == param */}, 453 offset: math.MaxUint32, // zero-extended -1 454 }, 455 { 456 name: "one 64 value + redundant extension", 457 setup: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, verify func(t *testing.T)) { 458 param := addParam(ctx, b, ssa.TypeI64) 459 ext := insertExt(ctx, b, param, 64, 64, true) 460 ctx.vRegMap[ext.Arg()] = v2000 461 iadd4 := insertIadd(ctx, b, param, ext.Return()) 462 return iadd4.Return(), func(t *testing.T) { 463 for _, instr := range []*ssa.Instruction{ext, iadd4} { 464 require.True(t, instr.Lowered()) 465 } 466 } 467 }, 468 exp64s: []regalloc.VReg{v1000, v1000}, 469 }, 470 } { 471 tc := tc 472 t.Run(tc.name, func(t *testing.T) { 473 ctx, b, m := newSetupWithMockContext() 474 ptr, verify := tc.setup(ctx, b, m) 475 actual32sQ, actual64sQ, actualOffset := m.collectAddends(ptr) 476 require.Equal(t, tc.exp32s, actual32sQ.data) 477 require.Equal(t, tc.exp64s, actual64sQ.data) 478 require.Equal(t, tc.offset, actualOffset) 479 verify(t) 480 }) 481 } 482 } 483 484 func TestMachine_addConstToReg64(t *testing.T) { 485 const nextVRegID = 100 486 t.Run("positive imm12", func(t *testing.T) { 487 c := int64(0xaaa) 488 ctx, _, m := newSetupWithMockContext() 489 ctx.vRegCounter = nextVRegID - 1 490 m.addConstToReg64(regalloc.FromRealReg(x15, regalloc.RegTypeInt), c) 491 require.Equal(t, `add x100?, x15, #0xaaa`, formatEmittedInstructionsInCurrentBlock(m)) 492 }) 493 t.Run("positive imm12", func(t *testing.T) { 494 c := int64(-0xaaa) 495 ctx, _, m := newSetupWithMockContext() 496 ctx.vRegCounter = nextVRegID - 1 497 m.addConstToReg64(regalloc.FromRealReg(x15, regalloc.RegTypeInt), c) 498 require.Equal(t, `sub x100?, x15, #0xaaa`, formatEmittedInstructionsInCurrentBlock(m)) 499 }) 500 t.Run("non imm12", func(t *testing.T) { 501 c := int64(1<<32 | 1) 502 ctx, _, m := newSetupWithMockContext() 503 ctx.vRegCounter = nextVRegID - 1 504 m.addConstToReg64(regalloc.FromRealReg(x15, regalloc.RegTypeInt), c) 505 require.Equal(t, `movz x101?, #0x1, lsl 0 506 movk x101?, #0x1, lsl 32 507 add x100?, x15, x101?`, formatEmittedInstructionsInCurrentBlock(m)) 508 }) 509 } 510 511 func TestMachine_addReg64ToReg64(t *testing.T) { 512 const nextVRegID = 100 513 for _, tc := range []struct { 514 exp string 515 rn, rm regalloc.VReg 516 }{ 517 { 518 exp: "add x100?, x0, x1", 519 rn: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 520 rm: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 521 }, 522 { 523 exp: "add x100?, x10, x12", 524 rn: regalloc.FromRealReg(x10, regalloc.RegTypeInt), 525 rm: regalloc.FromRealReg(x12, regalloc.RegTypeInt), 526 }, 527 } { 528 tc := tc 529 t.Run(tc.exp, func(t *testing.T) { 530 ctx, _, m := newSetupWithMockContext() 531 ctx.vRegCounter = nextVRegID - 1 532 rd := m.addReg64ToReg64(tc.rn, tc.rm) 533 require.Equal(t, tc.exp, formatEmittedInstructionsInCurrentBlock(m)) 534 require.Equal(t, rd, regalloc.VReg(nextVRegID).SetRegType(regalloc.RegTypeInt)) 535 }) 536 } 537 } 538 539 func TestMachine_addRegToReg64Ext(t *testing.T) { 540 const nextVRegID = 100 541 for _, tc := range []struct { 542 exp string 543 rn, rm regalloc.VReg 544 ext extendOp 545 }{ 546 { 547 exp: "add x100?, x0, w1 UXTW", 548 rn: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 549 rm: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 550 ext: extendOpUXTW, 551 }, 552 { 553 exp: "add x100?, x0, w1 SXTW", 554 rn: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 555 rm: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 556 ext: extendOpSXTW, 557 }, 558 } { 559 tc := tc 560 t.Run(tc.exp, func(t *testing.T) { 561 ctx, _, m := newSetupWithMockContext() 562 ctx.vRegCounter = nextVRegID - 1 563 rd := m.addRegToReg64Ext(tc.rn, tc.rm, tc.ext) 564 require.Equal(t, tc.exp, formatEmittedInstructionsInCurrentBlock(m)) 565 require.Equal(t, rd, regalloc.VReg(nextVRegID).SetRegType(regalloc.RegTypeInt)) 566 }) 567 } 568 } 569 570 func TestMachine_lowerToAddressModeFromAddends(t *testing.T) { 571 x1, x2, x3 := regalloc.FromRealReg(x1, regalloc.RegTypeInt), regalloc.FromRealReg(x2, regalloc.RegTypeInt), regalloc.FromRealReg(x3, regalloc.RegTypeInt) 572 x4, x5, x6 := regalloc.FromRealReg(x4, regalloc.RegTypeInt), regalloc.FromRealReg(x5, regalloc.RegTypeInt), regalloc.FromRealReg(x6, regalloc.RegTypeInt) 573 574 nextVReg, nextNextVeg := regalloc.VReg(100).SetRegType(regalloc.RegTypeInt), regalloc.VReg(101).SetRegType(regalloc.RegTypeInt) 575 for _, tc := range []struct { 576 name string 577 a32s []addend32 578 a64s []regalloc.VReg 579 dstSizeInBits byte 580 offset int64 581 exp addressMode 582 insts []string 583 }{ 584 { 585 name: "only offset", 586 offset: 4095, 587 insts: []string{"orr x100?, xzr, #0xfff"}, 588 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 0}, 589 }, 590 { 591 name: "only offset", 592 offset: 4095 << 12, 593 insts: []string{"orr x100?, xzr, #0xfff000"}, 594 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 0}, 595 }, 596 { 597 name: "one a64 with imm12", 598 a64s: []regalloc.VReg{x1}, 599 offset: 4095, 600 dstSizeInBits: 8, 601 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: x1, imm: 4095}, 602 }, 603 { 604 name: "one a64 with imm12", 605 a64s: []regalloc.VReg{x1}, 606 offset: 4095 * 2, 607 dstSizeInBits: 16, 608 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: x1, imm: 4095 * 2}, 609 }, 610 { 611 name: "one a64 with imm12", 612 a64s: []regalloc.VReg{x1}, 613 offset: 4095 * 4, 614 dstSizeInBits: 32, 615 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: x1, imm: 4095 * 4}, 616 }, 617 { 618 name: "one a64 with imm12", 619 a64s: []regalloc.VReg{x1}, 620 offset: 4095 * 8, 621 dstSizeInBits: 64, 622 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: x1, imm: 4095 * 8}, 623 }, 624 { 625 name: "one a64 with imm9", 626 a64s: []regalloc.VReg{x1}, 627 dstSizeInBits: 64, 628 exp: addressMode{kind: addressModeKindRegSignedImm9, rn: x1, imm: 0}, 629 }, 630 { 631 name: "one a64 with imm9", 632 a64s: []regalloc.VReg{x1}, 633 offset: -256, 634 dstSizeInBits: 64, 635 exp: addressMode{kind: addressModeKindRegSignedImm9, rn: x1, imm: -256}, 636 }, 637 { 638 name: "one a64 with offset not fitting", 639 a64s: []regalloc.VReg{x1}, 640 offset: 1 << 16, 641 dstSizeInBits: 64, 642 insts: []string{"add x100?, x1, #0x10000"}, 643 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 0}, 644 }, 645 { 646 name: "two a64 with imm12", 647 a64s: []regalloc.VReg{x1, x2}, 648 offset: 4095, 649 dstSizeInBits: 8, 650 insts: []string{"add x100?, x1, x2"}, 651 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 4095}, 652 }, 653 { 654 name: "two a64 with imm12", 655 a64s: []regalloc.VReg{x1, x2}, 656 offset: 4095 * 2, 657 dstSizeInBits: 16, 658 insts: []string{"add x100?, x1, x2"}, 659 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 4095 * 2}, 660 }, 661 { 662 name: "two a64 with imm12", 663 a64s: []regalloc.VReg{x1, x2}, 664 offset: 4095 * 4, 665 dstSizeInBits: 32, 666 insts: []string{"add x100?, x1, x2"}, 667 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 4095 * 4}, 668 }, 669 { 670 name: "two a64 with imm12", 671 a64s: []regalloc.VReg{x1, x2}, 672 offset: 4095 * 8, 673 dstSizeInBits: 64, 674 insts: []string{"add x100?, x1, x2"}, 675 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 4095 * 8}, 676 }, 677 { 678 name: "two a64 with imm9", 679 a64s: []regalloc.VReg{x1, x2}, 680 dstSizeInBits: 64, 681 insts: []string{"add x100?, x1, x2"}, 682 exp: addressMode{kind: addressModeKindRegSignedImm9, rn: nextVReg, imm: 0}, 683 }, 684 { 685 name: "two a64 with imm9", 686 a64s: []regalloc.VReg{x1, x2}, 687 offset: -256, 688 dstSizeInBits: 64, 689 insts: []string{"add x100?, x1, x2"}, 690 exp: addressMode{kind: addressModeKindRegSignedImm9, rn: nextVReg, imm: -256}, 691 }, 692 { 693 name: "two a64 with offset not fitting", 694 a64s: []regalloc.VReg{x1, x2}, 695 offset: 1 << 16, 696 dstSizeInBits: 64, 697 insts: []string{"add x100?, x1, #0x10000"}, 698 exp: addressMode{kind: addressModeKindRegReg, rn: nextVReg, rm: x2, extOp: extendOpUXTX}, 699 }, 700 { 701 name: "three a64 with imm12", 702 a64s: []regalloc.VReg{x1, x2, x3}, 703 offset: 4095 * 2, 704 dstSizeInBits: 16, 705 insts: []string{ 706 "add x100?, x1, x2", 707 "add x101?, x100?, x3", 708 }, 709 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextNextVeg, imm: 4095 * 2}, 710 }, 711 { 712 name: "three a64 with imm12", 713 a64s: []regalloc.VReg{x1, x2, x3}, 714 offset: 4095 * 4, 715 dstSizeInBits: 32, 716 insts: []string{ 717 "add x100?, x1, x2", 718 "add x101?, x100?, x3", 719 }, 720 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextNextVeg, imm: 4095 * 4}, 721 }, 722 { 723 name: "three a64 with imm12", 724 a64s: []regalloc.VReg{x1, x2, x3}, 725 offset: 4095 * 8, 726 dstSizeInBits: 64, 727 insts: []string{ 728 "add x100?, x1, x2", 729 "add x101?, x100?, x3", 730 }, 731 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextNextVeg, imm: 4095 * 8}, 732 }, 733 { 734 name: "three a64 with imm9", 735 a64s: []regalloc.VReg{x1, x2, x3}, 736 dstSizeInBits: 64, 737 insts: []string{ 738 "add x100?, x1, x2", 739 "add x101?, x100?, x3", 740 }, 741 exp: addressMode{kind: addressModeKindRegSignedImm9, rn: nextNextVeg, imm: 0}, 742 }, 743 { 744 name: "three a64 with imm9", 745 a64s: []regalloc.VReg{x1, x2, x3}, 746 offset: -256, 747 dstSizeInBits: 64, 748 insts: []string{ 749 "add x100?, x1, x2", 750 "add x101?, x100?, x3", 751 }, 752 exp: addressMode{kind: addressModeKindRegSignedImm9, rn: nextNextVeg, imm: -256}, 753 }, 754 { 755 name: "three a64 with offset not fitting", 756 a64s: []regalloc.VReg{x1, x2, x3}, 757 offset: 1 << 16, 758 dstSizeInBits: 64, 759 insts: []string{ 760 "add x100?, x1, #0x10000", 761 "add x101?, x100?, x3", 762 }, 763 exp: addressMode{kind: addressModeKindRegReg, rn: nextNextVeg, rm: x2, extOp: extendOpUXTX}, 764 }, 765 { 766 name: "three a32/a64 with offset", 767 a64s: []regalloc.VReg{x1, x2, x3}, 768 a32s: []addend32{{r: x4, ext: extendOpSXTW}, {r: x5, ext: extendOpUXTW}, {r: x6, ext: extendOpSXTW}}, 769 offset: 1 << 16, 770 dstSizeInBits: 64, 771 insts: []string{ 772 "add x100?, x1, #0x10000", 773 "add x101?, x100?, x2", 774 "add x102?, x101?, x3", 775 "add x103?, x102?, w5 UXTW", 776 "add x104?, x103?, w6 SXTW", 777 }, 778 exp: addressMode{ 779 kind: addressModeKindRegExtended, 780 rn: regalloc.VReg(104).SetRegType(regalloc.RegTypeInt), 781 rm: x4, extOp: extendOpSXTW, 782 }, 783 }, 784 { 785 name: "one a32 with offset", 786 a32s: []addend32{{r: x1, ext: extendOpSXTW}}, 787 offset: 1 << 16, 788 insts: []string{ 789 "sxtw x100?, w1", 790 "add x101?, x100?, #0x10000", 791 }, 792 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextNextVeg, imm: 0}, 793 }, 794 { 795 name: "two a32s with offset", 796 a32s: []addend32{{r: x1, ext: extendOpSXTW}, {r: x2, ext: extendOpUXTW}}, 797 offset: 1 << 16, 798 insts: []string{ 799 "sxtw x100?, w1", 800 "add x101?, x100?, #0x10000", 801 }, 802 exp: addressMode{ 803 kind: addressModeKindRegExtended, 804 rn: regalloc.VReg(101).SetRegType(regalloc.RegTypeInt), 805 rm: x2, 806 imm: 0, 807 extOp: extendOpUXTW, 808 }, 809 }, 810 } { 811 t.Run(tc.name, func(t *testing.T) { 812 ctx, _, m := newSetupWithMockContext() 813 ctx.vRegCounter = int(nextVReg.ID()) - 1 814 815 var a32s queue[addend32] 816 var a64s queue[regalloc.VReg] 817 for _, a32 := range tc.a32s { 818 a32s.enqueue(a32) 819 } 820 for _, a64 := range tc.a64s { 821 a64s.enqueue(a64) 822 } 823 actual := m.lowerToAddressModeFromAddends(&a32s, &a64s, tc.dstSizeInBits, tc.offset) 824 require.Equal(t, strings.Join(tc.insts, "\n"), formatEmittedInstructionsInCurrentBlock(m)) 825 require.Equal(t, tc.exp, actual, actual.format(tc.dstSizeInBits)) 826 }) 827 } 828 } 829 830 func Test_extLoadSizeSign(t *testing.T) { 831 for _, tc := range []struct { 832 op ssa.Opcode 833 expSize byte 834 signed bool 835 }{ 836 {op: ssa.OpcodeUload8, expSize: 8, signed: false}, 837 {op: ssa.OpcodeUload16, expSize: 16, signed: false}, 838 {op: ssa.OpcodeUload32, expSize: 32, signed: false}, 839 {op: ssa.OpcodeSload8, expSize: 8, signed: true}, 840 {op: ssa.OpcodeSload16, expSize: 16, signed: true}, 841 {op: ssa.OpcodeSload32, expSize: 32, signed: true}, 842 } { 843 size, signed := extLoadSignSize(tc.op) 844 require.Equal(t, tc.expSize, size) 845 require.Equal(t, tc.signed, signed) 846 } 847 } 848 849 func Test_lowerLoadSplatFromAddressMode(t *testing.T) { 850 positiveTests := make(map[addressModeKind]bool) 851 nextVReg := regalloc.VReg(100).SetRegType(regalloc.RegTypeInt) 852 853 for _, tc := range []struct { 854 amode addressMode 855 expected string 856 expectPanic bool 857 }{ 858 { 859 amode: addressMode{kind: addressModeKindRegReg, rn: x0VReg, rm: x1VReg}, 860 expected: ` 861 add x100?, x0, x1 862 ld1r {x10.4s}, [x100?] 863 `, 864 }, 865 { 866 amode: addressMode{kind: addressModeKindRegUnsignedImm12, rn: x0VReg, imm: 15616}, 867 expected: ` 868 movz x101?, #0x3d00, lsl 0 869 add x100?, x0, x101? 870 ld1r {x10.4s}, [x100?] 871 `, 872 }, 873 { 874 amode: addressMode{kind: addressModeKindRegUnsignedImm12, rn: x15VReg, imm: 0}, 875 expected: ` 876 ld1r {x10.4s}, [x15] 877 `, 878 }, 879 { 880 amode: addressMode{kind: addressModeKindRegSignedImm9, rn: x0VReg, imm: 42}, 881 expected: ` 882 add x100?, x0, #0x2a 883 ld1r {x10.4s}, [x100?] 884 `, 885 }, 886 } { 887 tc := tc 888 t.Run("address mode "+strconv.Itoa(int(tc.amode.kind)), func(t *testing.T) { 889 ctx, _, m := newSetupWithMockContext() 890 ctx.vRegCounter = int(nextVReg.ID()) - 1 891 positiveTests[tc.amode.kind] = true 892 893 m.lowerLoadSplatFromAddressMode(operandNR(x10VReg), tc.amode, ssa.VecLaneI32x4) 894 require.Equal(t, tc.expected, "\n"+formatEmittedInstructionsInCurrentBlock(m)+"\n") 895 }) 896 } 897 898 // Must panic for all other addressModeKinds. 899 for k := 0; k <= int(addressModeKindResultStackSpace); k++ { 900 amk := addressModeKind(k) 901 if positiveTests[amk] { 902 continue 903 } 904 905 ctx, _, m := newSetupWithMockContext() 906 ctx.vRegCounter = int(nextVReg.ID()) - 1 907 908 t.Run("address mode "+strconv.Itoa(k), func(t *testing.T) { 909 err := require.CapturePanic(func() { 910 m.lowerLoadSplatFromAddressMode(operandNR(x10VReg), addressMode{kind: amk}, ssa.VecLaneI32x4) 911 }) 912 require.Contains(t, err.Error(), "unsupported address mode for LoadSplat") 913 }) 914 915 } 916 }