github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/engine/wazevo/backend/isa/arm64/lower_mem_test.go (about) 1 package arm64 2 3 import ( 4 "fmt" 5 "math" 6 "strings" 7 "testing" 8 9 "github.com/tetratelabs/wazero/internal/engine/wazevo/backend" 10 "github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc" 11 "github.com/tetratelabs/wazero/internal/engine/wazevo/ssa" 12 "github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi" 13 "github.com/tetratelabs/wazero/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 tc := tc 457 t.Run(tc.name, func(t *testing.T) { 458 ctx, b, m := newSetupWithMockContext() 459 ptr, verify := tc.setup(ctx, b, m) 460 actual32sQ, actual64sQ, actualOffset := m.collectAddends(ptr) 461 require.Equal(t, tc.exp32s, actual32sQ.Data) 462 require.Equal(t, tc.exp64s, actual64sQ.Data) 463 require.Equal(t, tc.offset, actualOffset) 464 verify(t) 465 }) 466 } 467 } 468 469 func TestMachine_addConstToReg64(t *testing.T) { 470 const nextVRegID = 100 471 t.Run("positive imm12", func(t *testing.T) { 472 c := int64(0xaaa) 473 ctx, _, m := newSetupWithMockContext() 474 ctx.vRegCounter = nextVRegID - 1 475 m.addConstToReg64(regalloc.FromRealReg(x15, regalloc.RegTypeInt), c) 476 require.Equal(t, `add x100?, x15, #0xaaa`, formatEmittedInstructionsInCurrentBlock(m)) 477 }) 478 t.Run("negative imm12", func(t *testing.T) { 479 c := int64(-0xaaa) 480 ctx, _, m := newSetupWithMockContext() 481 ctx.vRegCounter = nextVRegID - 1 482 m.addConstToReg64(regalloc.FromRealReg(x15, regalloc.RegTypeInt), c) 483 require.Equal(t, `sub x100?, x15, #0xaaa`, formatEmittedInstructionsInCurrentBlock(m)) 484 }) 485 t.Run("non imm12", func(t *testing.T) { 486 c := int64(1<<32 | 1) 487 ctx, _, m := newSetupWithMockContext() 488 ctx.vRegCounter = nextVRegID - 1 489 m.addConstToReg64(regalloc.FromRealReg(x15, regalloc.RegTypeInt), c) 490 require.Equal(t, `movz x101?, #0x1, lsl 0 491 movk x101?, #0x1, lsl 32 492 add x100?, x15, x101?`, formatEmittedInstructionsInCurrentBlock(m)) 493 }) 494 } 495 496 func TestMachine_addReg64ToReg64(t *testing.T) { 497 const nextVRegID = 100 498 for _, tc := range []struct { 499 exp string 500 rn, rm regalloc.VReg 501 }{ 502 { 503 exp: "add x100?, x0, x1", 504 rn: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 505 rm: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 506 }, 507 { 508 exp: "add x100?, x10, x12", 509 rn: regalloc.FromRealReg(x10, regalloc.RegTypeInt), 510 rm: regalloc.FromRealReg(x12, regalloc.RegTypeInt), 511 }, 512 } { 513 tc := tc 514 t.Run(tc.exp, func(t *testing.T) { 515 ctx, _, m := newSetupWithMockContext() 516 ctx.vRegCounter = nextVRegID - 1 517 rd := m.addReg64ToReg64(tc.rn, tc.rm) 518 require.Equal(t, tc.exp, formatEmittedInstructionsInCurrentBlock(m)) 519 require.Equal(t, rd, regalloc.VReg(nextVRegID).SetRegType(regalloc.RegTypeInt)) 520 }) 521 } 522 } 523 524 func TestMachine_addRegToReg64Ext(t *testing.T) { 525 const nextVRegID = 100 526 for _, tc := range []struct { 527 exp string 528 rn, rm regalloc.VReg 529 ext extendOp 530 }{ 531 { 532 exp: "add x100?, x0, w1 UXTW", 533 rn: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 534 rm: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 535 ext: extendOpUXTW, 536 }, 537 { 538 exp: "add x100?, x0, w1 SXTW", 539 rn: regalloc.FromRealReg(x0, regalloc.RegTypeInt), 540 rm: regalloc.FromRealReg(x1, regalloc.RegTypeInt), 541 ext: extendOpSXTW, 542 }, 543 } { 544 tc := tc 545 t.Run(tc.exp, func(t *testing.T) { 546 ctx, _, m := newSetupWithMockContext() 547 ctx.vRegCounter = nextVRegID - 1 548 rd := m.addRegToReg64Ext(tc.rn, tc.rm, tc.ext) 549 require.Equal(t, tc.exp, formatEmittedInstructionsInCurrentBlock(m)) 550 require.Equal(t, rd, regalloc.VReg(nextVRegID).SetRegType(regalloc.RegTypeInt)) 551 }) 552 } 553 } 554 555 func TestMachine_lowerToAddressModeFromAddends(t *testing.T) { 556 x1, x2, x3 := regalloc.FromRealReg(x1, regalloc.RegTypeInt), regalloc.FromRealReg(x2, regalloc.RegTypeInt), regalloc.FromRealReg(x3, regalloc.RegTypeInt) 557 x4, x5, x6 := regalloc.FromRealReg(x4, regalloc.RegTypeInt), regalloc.FromRealReg(x5, regalloc.RegTypeInt), regalloc.FromRealReg(x6, regalloc.RegTypeInt) 558 559 nextVReg, nextNextVeg := regalloc.VReg(100).SetRegType(regalloc.RegTypeInt), regalloc.VReg(101).SetRegType(regalloc.RegTypeInt) 560 for _, tc := range []struct { 561 name string 562 a32s []addend32 563 a64s []regalloc.VReg 564 dstSizeInBits byte 565 offset int64 566 exp addressMode 567 insts []string 568 }{ 569 { 570 name: "only offset", 571 offset: 4095, 572 insts: []string{"orr x100?, xzr, #0xfff"}, 573 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 0}, 574 }, 575 { 576 name: "only offset", 577 offset: 4095 << 12, 578 insts: []string{"orr x100?, xzr, #0xfff000"}, 579 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 0}, 580 }, 581 { 582 name: "one a64 with imm12", 583 a64s: []regalloc.VReg{x1}, 584 offset: 4095, 585 dstSizeInBits: 8, 586 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: x1, imm: 4095}, 587 }, 588 { 589 name: "one a64 with imm12", 590 a64s: []regalloc.VReg{x1}, 591 offset: 4095 * 2, 592 dstSizeInBits: 16, 593 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: x1, imm: 4095 * 2}, 594 }, 595 { 596 name: "one a64 with imm12", 597 a64s: []regalloc.VReg{x1}, 598 offset: 4095 * 4, 599 dstSizeInBits: 32, 600 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: x1, imm: 4095 * 4}, 601 }, 602 { 603 name: "one a64 with imm12", 604 a64s: []regalloc.VReg{x1}, 605 offset: 4095 * 8, 606 dstSizeInBits: 64, 607 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: x1, imm: 4095 * 8}, 608 }, 609 { 610 name: "one a64 with imm9", 611 a64s: []regalloc.VReg{x1}, 612 dstSizeInBits: 64, 613 exp: addressMode{kind: addressModeKindRegSignedImm9, rn: x1, imm: 0}, 614 }, 615 { 616 name: "one a64 with imm9", 617 a64s: []regalloc.VReg{x1}, 618 offset: -256, 619 dstSizeInBits: 64, 620 exp: addressMode{kind: addressModeKindRegSignedImm9, rn: x1, imm: -256}, 621 }, 622 { 623 name: "one a64 with offset not fitting", 624 a64s: []regalloc.VReg{x1}, 625 offset: 1 << 16, 626 dstSizeInBits: 64, 627 insts: []string{"add x100?, x1, #0x10000"}, 628 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 0}, 629 }, 630 { 631 name: "two a64 with imm12", 632 a64s: []regalloc.VReg{x1, x2}, 633 offset: 4095, 634 dstSizeInBits: 8, 635 insts: []string{"add x100?, x1, x2"}, 636 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 4095}, 637 }, 638 { 639 name: "two a64 with imm12", 640 a64s: []regalloc.VReg{x1, x2}, 641 offset: 4095 * 2, 642 dstSizeInBits: 16, 643 insts: []string{"add x100?, x1, x2"}, 644 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 4095 * 2}, 645 }, 646 { 647 name: "two a64 with imm12", 648 a64s: []regalloc.VReg{x1, x2}, 649 offset: 4095 * 4, 650 dstSizeInBits: 32, 651 insts: []string{"add x100?, x1, x2"}, 652 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 4095 * 4}, 653 }, 654 { 655 name: "two a64 with imm12", 656 a64s: []regalloc.VReg{x1, x2}, 657 offset: 4095 * 8, 658 dstSizeInBits: 64, 659 insts: []string{"add x100?, x1, x2"}, 660 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 4095 * 8}, 661 }, 662 { 663 name: "two a64 with imm9", 664 a64s: []regalloc.VReg{x1, x2}, 665 dstSizeInBits: 64, 666 insts: []string{"add x100?, x1, x2"}, 667 exp: addressMode{kind: addressModeKindRegSignedImm9, rn: nextVReg, imm: 0}, 668 }, 669 { 670 name: "two a64 with imm9", 671 a64s: []regalloc.VReg{x1, x2}, 672 offset: -256, 673 dstSizeInBits: 64, 674 insts: []string{"add x100?, x1, x2"}, 675 exp: addressMode{kind: addressModeKindRegSignedImm9, rn: nextVReg, imm: -256}, 676 }, 677 { 678 name: "two a64 with offset not fitting", 679 a64s: []regalloc.VReg{x1, x2}, 680 offset: 1 << 16, 681 dstSizeInBits: 64, 682 insts: []string{"add x100?, x1, #0x10000"}, 683 exp: addressMode{kind: addressModeKindRegReg, rn: nextVReg, rm: x2, extOp: extendOpUXTX}, 684 }, 685 { 686 name: "three a64 with imm12", 687 a64s: []regalloc.VReg{x1, x2, x3}, 688 offset: 4095 * 2, 689 dstSizeInBits: 16, 690 insts: []string{ 691 "add x100?, x1, x2", 692 "add x101?, x100?, x3", 693 }, 694 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextNextVeg, imm: 4095 * 2}, 695 }, 696 { 697 name: "three a64 with imm12", 698 a64s: []regalloc.VReg{x1, x2, x3}, 699 offset: 4095 * 4, 700 dstSizeInBits: 32, 701 insts: []string{ 702 "add x100?, x1, x2", 703 "add x101?, x100?, x3", 704 }, 705 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextNextVeg, imm: 4095 * 4}, 706 }, 707 { 708 name: "three a64 with imm12", 709 a64s: []regalloc.VReg{x1, x2, x3}, 710 offset: 4095 * 8, 711 dstSizeInBits: 64, 712 insts: []string{ 713 "add x100?, x1, x2", 714 "add x101?, x100?, x3", 715 }, 716 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextNextVeg, imm: 4095 * 8}, 717 }, 718 { 719 name: "three a64 with imm9", 720 a64s: []regalloc.VReg{x1, x2, x3}, 721 dstSizeInBits: 64, 722 insts: []string{ 723 "add x100?, x1, x2", 724 "add x101?, x100?, x3", 725 }, 726 exp: addressMode{kind: addressModeKindRegSignedImm9, rn: nextNextVeg, imm: 0}, 727 }, 728 { 729 name: "three a64 with imm9", 730 a64s: []regalloc.VReg{x1, x2, x3}, 731 offset: -256, 732 dstSizeInBits: 64, 733 insts: []string{ 734 "add x100?, x1, x2", 735 "add x101?, x100?, x3", 736 }, 737 exp: addressMode{kind: addressModeKindRegSignedImm9, rn: nextNextVeg, imm: -256}, 738 }, 739 { 740 name: "three a64 with offset not fitting", 741 a64s: []regalloc.VReg{x1, x2, x3}, 742 offset: 1 << 16, 743 dstSizeInBits: 64, 744 insts: []string{ 745 "add x100?, x1, #0x10000", 746 "add x101?, x100?, x3", 747 }, 748 exp: addressMode{kind: addressModeKindRegReg, rn: nextNextVeg, rm: x2, extOp: extendOpUXTX}, 749 }, 750 { 751 name: "three a32/a64 with offset", 752 a64s: []regalloc.VReg{x1, x2, x3}, 753 a32s: []addend32{{r: x4, ext: extendOpSXTW}, {r: x5, ext: extendOpUXTW}, {r: x6, ext: extendOpSXTW}}, 754 offset: 1 << 16, 755 dstSizeInBits: 64, 756 insts: []string{ 757 "add x100?, x1, #0x10000", 758 "add x101?, x100?, x2", 759 "add x102?, x101?, x3", 760 "add x103?, x102?, w5 UXTW", 761 "add x104?, x103?, w6 SXTW", 762 }, 763 exp: addressMode{ 764 kind: addressModeKindRegExtended, 765 rn: regalloc.VReg(104).SetRegType(regalloc.RegTypeInt), 766 rm: x4, extOp: extendOpSXTW, 767 }, 768 }, 769 { 770 name: "one a32 with offset", 771 a32s: []addend32{{r: x1, ext: extendOpSXTW}}, 772 offset: 1 << 16, 773 insts: []string{ 774 "sxtw x100?, w1", 775 "add x101?, x100?, #0x10000", 776 }, 777 exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextNextVeg, imm: 0}, 778 }, 779 { 780 name: "two a32s with offset", 781 a32s: []addend32{{r: x1, ext: extendOpSXTW}, {r: x2, ext: extendOpUXTW}}, 782 offset: 1 << 16, 783 insts: []string{ 784 "sxtw x100?, w1", 785 "add x101?, x100?, #0x10000", 786 }, 787 exp: addressMode{ 788 kind: addressModeKindRegExtended, 789 rn: regalloc.VReg(101).SetRegType(regalloc.RegTypeInt), 790 rm: x2, 791 imm: 0, 792 extOp: extendOpUXTW, 793 }, 794 }, 795 } { 796 t.Run(tc.name, func(t *testing.T) { 797 ctx, _, m := newSetupWithMockContext() 798 ctx.vRegCounter = int(nextVReg.ID()) - 1 799 800 var a32s wazevoapi.Queue[addend32] 801 var a64s wazevoapi.Queue[regalloc.VReg] 802 for _, a32 := range tc.a32s { 803 a32s.Enqueue(a32) 804 } 805 for _, a64 := range tc.a64s { 806 a64s.Enqueue(a64) 807 } 808 actual := m.lowerToAddressModeFromAddends(&a32s, &a64s, tc.dstSizeInBits, tc.offset) 809 require.Equal(t, strings.Join(tc.insts, "\n"), formatEmittedInstructionsInCurrentBlock(m)) 810 require.Equal(t, tc.exp, actual, actual.format(tc.dstSizeInBits)) 811 }) 812 } 813 } 814 815 func Test_extLoadSizeSign(t *testing.T) { 816 for _, tc := range []struct { 817 op ssa.Opcode 818 expSize byte 819 signed bool 820 }{ 821 {op: ssa.OpcodeUload8, expSize: 8, signed: false}, 822 {op: ssa.OpcodeUload16, expSize: 16, signed: false}, 823 {op: ssa.OpcodeUload32, expSize: 32, signed: false}, 824 {op: ssa.OpcodeSload8, expSize: 8, signed: true}, 825 {op: ssa.OpcodeSload16, expSize: 16, signed: true}, 826 {op: ssa.OpcodeSload32, expSize: 32, signed: true}, 827 } { 828 size, signed := extLoadSignSize(tc.op) 829 require.Equal(t, tc.expSize, size) 830 require.Equal(t, tc.signed, signed) 831 } 832 }