WasmBCRegDefs.h (23690B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * 4 * Copyright 2016 Mozilla Foundation 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 // This is an INTERNAL header for Wasm baseline compiler: definitions of 20 // registers and the register allocator. 21 22 #ifndef wasm_wasm_baseline_regdefs_h 23 #define wasm_wasm_baseline_regdefs_h 24 25 #include "wasm/WasmBCDefs.h" 26 27 namespace js { 28 namespace wasm { 29 30 struct BaseCompiler; 31 32 using namespace js::jit; 33 34 ////////////////////////////////////////////////////////////////////////////// 35 // 36 // Scratch register configuration. 37 38 #if defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_WASM32) 39 # define RABALDR_SCRATCH_I32 40 # define RABALDR_SCRATCH_F32 41 # define RABALDR_SCRATCH_F64 42 43 static constexpr Register RabaldrScratchI32 = Register::Invalid(); 44 static constexpr FloatRegister RabaldrScratchF32 = InvalidFloatReg; 45 static constexpr FloatRegister RabaldrScratchF64 = InvalidFloatReg; 46 #endif 47 48 #ifdef JS_CODEGEN_ARM64 49 # define RABALDR_SCRATCH_I32 50 # define RABALDR_SCRATCH_F32 51 # define RABALDR_SCRATCH_F64 52 # define RABALDR_SCRATCH_V128 53 # define RABALDR_SCRATCH_F32_ALIASES_F64 54 55 static constexpr Register RabaldrScratchI32{Registers::x15}; 56 57 // Note, the float scratch regs cannot be registers that are used for parameter 58 // passing in any ABI we use. Argregs tend to be low-numbered; register 30 59 // should be safe. 60 61 static constexpr FloatRegister RabaldrScratchF32{FloatRegisters::s30, 62 FloatRegisters::Single}; 63 static constexpr FloatRegister RabaldrScratchF64{FloatRegisters::d30, 64 FloatRegisters::Double}; 65 # ifdef ENABLE_WASM_SIMD 66 static constexpr FloatRegister RabaldrScratchV128{FloatRegisters::d30, 67 FloatRegisters::Simd128}; 68 # endif 69 70 static_assert(RabaldrScratchF32 != ScratchFloat32Reg_, "Too busy"); 71 static_assert(RabaldrScratchF64 != ScratchDoubleReg_, "Too busy"); 72 # ifdef ENABLE_WASM_SIMD 73 static_assert(RabaldrScratchV128 != ScratchSimd128Reg, "Too busy"); 74 # endif 75 #endif 76 77 #ifdef JS_CODEGEN_X86 78 // The selection of EBX here steps gingerly around: the need for EDX 79 // to be allocatable for multiply/divide; ECX to be allocatable for 80 // shift/rotate; EAX (= ReturnReg) to be allocatable as the result 81 // register; EBX not being one of the WasmTableCall registers; and 82 // needing a temp register for load/store that has a single-byte 83 // persona. 84 // 85 // The compiler assumes that RabaldrScratchI32 has a single-byte 86 // persona. Code for 8-byte atomic operations assumes that 87 // RabaldrScratchI32 is in fact ebx. 88 89 # define RABALDR_SCRATCH_I32 90 static constexpr Register RabaldrScratchI32 = ebx; 91 #endif 92 93 #ifdef JS_CODEGEN_ARM 94 // We use our own scratch register, because the macro assembler uses 95 // the regular scratch register(s) pretty liberally. We could 96 // work around that in several cases but the mess does not seem 97 // worth it yet. CallTempReg2 seems safe. 98 99 # define RABALDR_SCRATCH_I32 100 static constexpr Register RabaldrScratchI32 = CallTempReg2; 101 #endif 102 103 #ifdef JS_CODEGEN_MIPS64 104 # define RABALDR_SCRATCH_I32 105 static constexpr Register RabaldrScratchI32 = CallTempReg2; 106 #endif 107 108 #ifdef JS_CODEGEN_LOONG64 109 // We use our own scratch register, because the macro assembler uses 110 // the regular scratch register(s) pretty liberally. We could 111 // work around that in several cases but the mess does not seem 112 // worth it yet. CallTempReg2 seems safe. 113 114 # define RABALDR_SCRATCH_I32 115 static constexpr Register RabaldrScratchI32 = CallTempReg2; 116 #endif 117 118 #ifdef JS_CODEGEN_RISCV64 119 # define RABALDR_SCRATCH_I32 120 static constexpr Register RabaldrScratchI32 = CallTempReg2; 121 #endif 122 123 #ifdef RABALDR_SCRATCH_F32_ALIASES_F64 124 # if !defined(RABALDR_SCRATCH_F32) || !defined(RABALDR_SCRATCH_F64) 125 # error "Bad configuration" 126 # endif 127 #endif 128 129 ////////////////////////////////////////////////////////////////////////////// 130 // 131 // ... 132 133 template <MIRType t> 134 struct RegTypeOf { 135 #ifdef ENABLE_WASM_SIMD 136 static_assert(t == MIRType::Float32 || t == MIRType::Double || 137 t == MIRType::Simd128, 138 "Float mask type"); 139 #else 140 static_assert(t == MIRType::Float32 || t == MIRType::Double, 141 "Float mask type"); 142 #endif 143 }; 144 145 template <> 146 struct RegTypeOf<MIRType::Float32> { 147 static constexpr RegTypeName value = RegTypeName::Float32; 148 }; 149 template <> 150 struct RegTypeOf<MIRType::Double> { 151 static constexpr RegTypeName value = RegTypeName::Float64; 152 }; 153 #ifdef ENABLE_WASM_SIMD 154 template <> 155 struct RegTypeOf<MIRType::Simd128> { 156 static constexpr RegTypeName value = RegTypeName::Vector128; 157 }; 158 #endif 159 160 ////////////////////////////////////////////////////////////////////////////// 161 // 162 // Strongly typed register wrappers. 163 164 // The strongly typed register wrappers are especially useful to distinguish 165 // float registers from double registers, but they also clearly distinguish 166 // 32-bit registers from 64-bit register pairs on 32-bit systems. 167 168 struct RegI32 : public Register { 169 RegI32() : Register(Register::Invalid()) {} 170 explicit RegI32(Register reg) : Register(reg) { 171 MOZ_ASSERT(reg != Invalid()); 172 } 173 bool isInvalid() const { return *this == Invalid(); } 174 bool isValid() const { return !isInvalid(); } 175 static RegI32 Invalid() { return RegI32(); } 176 }; 177 178 struct RegI64 : public Register64 { 179 RegI64() : Register64(Register64::Invalid()) {} 180 explicit RegI64(Register64 reg) : Register64(reg) { 181 MOZ_ASSERT(reg != Invalid()); 182 } 183 bool isInvalid() const { return *this == Invalid(); } 184 bool isValid() const { return !isInvalid(); } 185 static RegI64 Invalid() { return RegI64(); } 186 }; 187 188 // RegRef is for GC-pointers, for non GC-pointers use RegPtr 189 struct RegRef : public Register { 190 RegRef() : Register(Register::Invalid()) {} 191 explicit RegRef(Register reg) : Register(reg) { 192 MOZ_ASSERT(reg != Invalid()); 193 } 194 bool isInvalid() const { return *this == Invalid(); } 195 bool isValid() const { return !isInvalid(); } 196 static RegRef Invalid() { return RegRef(); } 197 }; 198 199 // RegPtr is for non GC-pointers, for GC-pointers use RegRef 200 struct RegPtr : public Register { 201 RegPtr() : Register(Register::Invalid()) {} 202 explicit RegPtr(Register reg) : Register(reg) { 203 MOZ_ASSERT(reg != Invalid()); 204 } 205 bool isInvalid() const { return *this == Invalid(); } 206 bool isValid() const { return !isInvalid(); } 207 static RegPtr Invalid() { return RegPtr(); } 208 }; 209 210 struct RegF32 : public FloatRegister { 211 RegF32() {} 212 explicit RegF32(FloatRegister reg) : FloatRegister(reg) { 213 MOZ_ASSERT(isSingle()); 214 } 215 bool isValid() const { return !isInvalid(); } 216 static RegF32 Invalid() { return RegF32(); } 217 }; 218 219 struct RegF64 : public FloatRegister { 220 RegF64() {} 221 explicit RegF64(FloatRegister reg) : FloatRegister(reg) { 222 MOZ_ASSERT(isDouble()); 223 } 224 bool isValid() const { return !isInvalid(); } 225 static RegF64 Invalid() { return RegF64(); } 226 }; 227 228 #ifdef ENABLE_WASM_SIMD 229 struct RegV128 : public FloatRegister { 230 RegV128() {} 231 explicit RegV128(FloatRegister reg) : FloatRegister(reg) { 232 MOZ_ASSERT(isSimd128()); 233 } 234 bool isValid() const { return !isInvalid(); } 235 static RegV128 Invalid() { return RegV128(); } 236 }; 237 #endif 238 239 struct AnyReg { 240 union { 241 RegI32 i32_; 242 RegI64 i64_; 243 RegRef ref_; 244 RegF32 f32_; 245 RegF64 f64_; 246 #ifdef ENABLE_WASM_SIMD 247 RegV128 v128_; 248 #endif 249 }; 250 251 enum { 252 I32, 253 I64, 254 REF, 255 F32, 256 F64, 257 #ifdef ENABLE_WASM_SIMD 258 V128 259 #endif 260 } tag; 261 262 explicit AnyReg(RegI32 r) { 263 tag = I32; 264 i32_ = r; 265 } 266 explicit AnyReg(RegI64 r) { 267 tag = I64; 268 i64_ = r; 269 } 270 explicit AnyReg(RegF32 r) { 271 tag = F32; 272 f32_ = r; 273 } 274 explicit AnyReg(RegF64 r) { 275 tag = F64; 276 f64_ = r; 277 } 278 #ifdef ENABLE_WASM_SIMD 279 explicit AnyReg(RegV128 r) { 280 tag = V128; 281 v128_ = r; 282 } 283 #endif 284 explicit AnyReg(RegRef r) { 285 tag = REF; 286 ref_ = r; 287 } 288 289 RegI32 i32() const { 290 MOZ_ASSERT(tag == I32); 291 return i32_; 292 } 293 RegI64 i64() const { 294 MOZ_ASSERT(tag == I64); 295 return i64_; 296 } 297 RegF32 f32() const { 298 MOZ_ASSERT(tag == F32); 299 return f32_; 300 } 301 RegF64 f64() const { 302 MOZ_ASSERT(tag == F64); 303 return f64_; 304 } 305 #ifdef ENABLE_WASM_SIMD 306 RegV128 v128() const { 307 MOZ_ASSERT(tag == V128); 308 return v128_; 309 } 310 #endif 311 RegRef ref() const { 312 MOZ_ASSERT(tag == REF); 313 return ref_; 314 } 315 316 AnyRegister any() const { 317 switch (tag) { 318 case F32: 319 return AnyRegister(f32_); 320 case F64: 321 return AnyRegister(f64_); 322 #ifdef ENABLE_WASM_SIMD 323 case V128: 324 return AnyRegister(v128_); 325 #endif 326 case I32: 327 return AnyRegister(i32_); 328 case I64: 329 #ifdef JS_PUNBOX64 330 return AnyRegister(i64_.reg); 331 #else 332 // The compiler is written so that this is never needed: any() is 333 // called on arbitrary registers for asm.js but asm.js does not have 334 // 64-bit ints. For wasm, any() is called on arbitrary registers 335 // only on 64-bit platforms. 336 MOZ_CRASH("AnyReg::any() on 32-bit platform"); 337 #endif 338 case REF: 339 MOZ_CRASH("AnyReg::any() not implemented for ref types"); 340 default: 341 MOZ_CRASH(); 342 } 343 // Work around GCC 5 analysis/warning bug. 344 MOZ_CRASH("AnyReg::any(): impossible case"); 345 } 346 }; 347 348 ////////////////////////////////////////////////////////////////////////////// 349 // 350 // Platform-specific registers. 351 // 352 // All platforms must define struct SpecificRegs. All 32-bit platforms must 353 // have an abiReturnRegI64 member in that struct. 354 355 #if defined(JS_CODEGEN_X64) 356 struct SpecificRegs { 357 RegI32 eax, ecx, edx, edi, esi; 358 RegI64 rax, rcx, rdx; 359 360 SpecificRegs() 361 : eax(RegI32(js::jit::eax)), 362 ecx(RegI32(js::jit::ecx)), 363 edx(RegI32(js::jit::edx)), 364 edi(RegI32(js::jit::edi)), 365 esi(RegI32(js::jit::esi)), 366 rax(RegI64(Register64(js::jit::rax))), 367 rcx(RegI64(Register64(js::jit::rcx))), 368 rdx(RegI64(Register64(js::jit::rdx))) {} 369 }; 370 #elif defined(JS_CODEGEN_X86) 371 struct SpecificRegs { 372 RegI32 eax, ecx, edx, edi, esi; 373 RegI64 ecx_ebx, edx_eax, abiReturnRegI64; 374 375 SpecificRegs() 376 : eax(RegI32(js::jit::eax)), 377 ecx(RegI32(js::jit::ecx)), 378 edx(RegI32(js::jit::edx)), 379 edi(RegI32(js::jit::edi)), 380 esi(RegI32(js::jit::esi)), 381 ecx_ebx(RegI64(Register64(js::jit::ecx, js::jit::ebx))), 382 edx_eax(RegI64(Register64(js::jit::edx, js::jit::eax))), 383 abiReturnRegI64(edx_eax) {} 384 }; 385 #elif defined(JS_CODEGEN_ARM) 386 struct SpecificRegs { 387 RegI64 abiReturnRegI64; 388 389 SpecificRegs() : abiReturnRegI64(ReturnReg64) {} 390 }; 391 #elif defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS64) || \ 392 defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) 393 struct SpecificRegs { 394 // Required by gcc. 395 SpecificRegs() {} 396 }; 397 #else 398 struct SpecificRegs { 399 # ifndef JS_64BIT 400 RegI64 abiReturnRegI64; 401 # endif 402 403 SpecificRegs() { MOZ_CRASH("BaseCompiler porting interface: SpecificRegs"); } 404 }; 405 #endif 406 407 ////////////////////////////////////////////////////////////////////////////// 408 // 409 // Register allocator. 410 411 class BaseRegAlloc { 412 // Notes on float register allocation. 413 // 414 // The general rule in SpiderMonkey is that float registers can alias double 415 // registers, but there are predicates to handle exceptions to that rule: 416 // hasUnaliasedDouble() and hasMultiAlias(). The way aliasing actually 417 // works is platform dependent and exposed through the aliased(n, &r) 418 // predicate, etc. 419 // 420 // - hasUnaliasedDouble(): on ARM VFPv3-D32 there are double registers that 421 // cannot be treated as float. 422 // - hasMultiAlias(): on ARM and MIPS a double register aliases two float 423 // registers. 424 // 425 // On some platforms (x86, x64, ARM64) but not all (ARM) 426 // ScratchFloat32Register is the same as ScratchDoubleRegister. 427 // 428 // It's a basic invariant of the AllocatableRegisterSet that it deals 429 // properly with aliasing of registers: if s0 or s1 are allocated then d0 is 430 // not allocatable; if s0 and s1 are freed individually then d0 becomes 431 // allocatable. 432 433 BaseCompiler* bc; 434 AllocatableGeneralRegisterSet availGPR; 435 AllocatableFloatRegisterSet availFPU; 436 #ifdef DEBUG 437 // The registers available after removing ScratchReg, HeapReg, etc. 438 AllocatableGeneralRegisterSet allGPR; 439 AllocatableFloatRegisterSet allFPU; 440 uint32_t scratchTaken; 441 #endif 442 #ifdef JS_CODEGEN_X86 443 AllocatableGeneralRegisterSet singleByteRegs; 444 #endif 445 446 bool hasGPR() { return !availGPR.empty(); } 447 448 bool hasGPR64() { 449 #ifdef JS_PUNBOX64 450 return !availGPR.empty(); 451 #else 452 if (availGPR.empty()) { 453 return false; 454 } 455 Register r = allocGPR(); 456 bool available = !availGPR.empty(); 457 freeGPR(r); 458 return available; 459 #endif 460 } 461 462 template <MIRType t> 463 bool hasFPU() { 464 return availFPU.hasAny<RegTypeOf<t>::value>(); 465 } 466 467 bool isAvailableGPR(Register r) { return availGPR.has(r); } 468 469 bool isAvailableFPU(FloatRegister r) { return availFPU.has(r); } 470 471 void allocGPR(Register r) { 472 MOZ_ASSERT(isAvailableGPR(r)); 473 availGPR.take(r); 474 } 475 476 Register allocGPR() { 477 MOZ_ASSERT(hasGPR()); 478 return availGPR.takeAny(); 479 } 480 481 void allocInt64(Register64 r) { 482 #ifdef JS_PUNBOX64 483 allocGPR(r.reg); 484 #else 485 allocGPR(r.low); 486 allocGPR(r.high); 487 #endif 488 } 489 490 Register64 allocInt64() { 491 MOZ_ASSERT(hasGPR64()); 492 #ifdef JS_PUNBOX64 493 return Register64(availGPR.takeAny()); 494 #else 495 Register high = availGPR.takeAny(); 496 Register low = availGPR.takeAny(); 497 return Register64(high, low); 498 #endif 499 } 500 501 #ifdef JS_CODEGEN_ARM 502 // r12 is normally the ScratchRegister and r13 is always the stack pointer, 503 // so the highest possible pair has r10 as the even-numbered register. 504 505 static constexpr uint32_t PAIR_LIMIT = 10; 506 507 bool hasGPRPair() { 508 for (uint32_t i = 0; i <= PAIR_LIMIT; i += 2) { 509 if (isAvailableGPR(Register::FromCode(i)) && 510 isAvailableGPR(Register::FromCode(i + 1))) { 511 return true; 512 } 513 } 514 return false; 515 } 516 517 void allocGPRPair(Register* low, Register* high) { 518 MOZ_ASSERT(hasGPRPair()); 519 for (uint32_t i = 0; i <= PAIR_LIMIT; i += 2) { 520 if (isAvailableGPR(Register::FromCode(i)) && 521 isAvailableGPR(Register::FromCode(i + 1))) { 522 *low = Register::FromCode(i); 523 *high = Register::FromCode(i + 1); 524 allocGPR(*low); 525 allocGPR(*high); 526 return; 527 } 528 } 529 MOZ_CRASH("No pair"); 530 } 531 #endif 532 533 void allocFPU(FloatRegister r) { 534 MOZ_ASSERT(isAvailableFPU(r)); 535 availFPU.take(r); 536 } 537 538 template <MIRType t> 539 FloatRegister allocFPU() { 540 return availFPU.takeAny<RegTypeOf<t>::value>(); 541 } 542 543 void freeGPR(Register r) { availGPR.add(r); } 544 545 void freeInt64(Register64 r) { 546 #ifdef JS_PUNBOX64 547 freeGPR(r.reg); 548 #else 549 freeGPR(r.low); 550 freeGPR(r.high); 551 #endif 552 } 553 554 void freeFPU(FloatRegister r) { availFPU.add(r); } 555 556 public: 557 explicit BaseRegAlloc() 558 : bc(nullptr), 559 availGPR(GeneralRegisterSet::All()), 560 availFPU(FloatRegisterSet::All()) 561 #ifdef DEBUG 562 , 563 scratchTaken(0) 564 #endif 565 #ifdef JS_CODEGEN_X86 566 , 567 singleByteRegs(GeneralRegisterSet(Registers::SingleByteRegs)) 568 #endif 569 { 570 RegisterAllocator::takeWasmRegisters(availGPR); 571 572 #ifdef RABALDR_PIN_INSTANCE 573 // If the InstanceReg is pinned then it is never available for 574 // allocation. 575 availGPR.take(InstanceReg); 576 #endif 577 578 // Allocate any private scratch registers. 579 #if defined(RABALDR_SCRATCH_I32) 580 if (RabaldrScratchI32 != RegI32::Invalid()) { 581 availGPR.take(RabaldrScratchI32); 582 } 583 #endif 584 585 #ifdef RABALDR_SCRATCH_F32_ALIASES_F64 586 static_assert(RabaldrScratchF32 != InvalidFloatReg, "Float reg definition"); 587 static_assert(RabaldrScratchF64 != InvalidFloatReg, "Float reg definition"); 588 #endif 589 590 #if defined(RABALDR_SCRATCH_F32) && !defined(RABALDR_SCRATCH_F32_ALIASES_F64) 591 if (RabaldrScratchF32 != RegF32::Invalid()) { 592 availFPU.take(RabaldrScratchF32); 593 } 594 #endif 595 596 #if defined(RABALDR_SCRATCH_F64) 597 # ifdef RABALDR_SCRATCH_F32_ALIASES_F64 598 MOZ_ASSERT(availFPU.has(RabaldrScratchF32)); 599 # endif 600 if (RabaldrScratchF64 != RegF64::Invalid()) { 601 availFPU.take(RabaldrScratchF64); 602 } 603 # ifdef RABALDR_SCRATCH_F32_ALIASES_F64 604 MOZ_ASSERT(!availFPU.has(RabaldrScratchF32)); 605 # endif 606 #endif 607 608 #ifdef DEBUG 609 allGPR = availGPR; 610 allFPU = availFPU; 611 #endif 612 } 613 614 void init(BaseCompiler* bc) { this->bc = bc; } 615 616 enum class ScratchKind { I32 = 1, F32 = 2, F64 = 4, V128 = 8 }; 617 618 #ifdef DEBUG 619 bool isScratchRegisterTaken(ScratchKind s) const { 620 return (scratchTaken & uint32_t(s)) != 0; 621 } 622 623 void setScratchRegisterTaken(ScratchKind s, bool state) { 624 if (state) { 625 scratchTaken |= uint32_t(s); 626 } else { 627 scratchTaken &= ~uint32_t(s); 628 } 629 } 630 #endif 631 632 #ifdef JS_CODEGEN_X86 633 bool isSingleByteI32(Register r) { return singleByteRegs.has(r); } 634 #endif 635 636 bool isAvailableI32(RegI32 r) { return isAvailableGPR(r); } 637 638 bool isAvailableI64(RegI64 r) { 639 #ifdef JS_PUNBOX64 640 return isAvailableGPR(r.reg); 641 #else 642 return isAvailableGPR(r.low) && isAvailableGPR(r.high); 643 #endif 644 } 645 646 bool isAvailableRef(RegRef r) { return isAvailableGPR(r); } 647 648 bool isAvailablePtr(RegPtr r) { return isAvailableGPR(r); } 649 650 bool isAvailableF32(RegF32 r) { return isAvailableFPU(r); } 651 652 bool isAvailableF64(RegF64 r) { return isAvailableFPU(r); } 653 654 #ifdef ENABLE_WASM_SIMD 655 bool isAvailableV128(RegV128 r) { return isAvailableFPU(r); } 656 #endif 657 658 [[nodiscard]] inline RegI32 needI32(); 659 inline void needI32(RegI32 specific); 660 661 [[nodiscard]] inline RegI64 needI64(); 662 inline void needI64(RegI64 specific); 663 664 [[nodiscard]] inline RegRef needRef(); 665 inline void needRef(RegRef specific); 666 667 [[nodiscard]] inline RegPtr needPtr(); 668 inline void needPtr(RegPtr specific); 669 670 [[nodiscard]] inline RegF32 needF32(); 671 inline void needF32(RegF32 specific); 672 673 [[nodiscard]] inline RegF64 needF64(); 674 inline void needF64(RegF64 specific); 675 676 #ifdef ENABLE_WASM_SIMD 677 [[nodiscard]] inline RegV128 needV128(); 678 inline void needV128(RegV128 specific); 679 #endif 680 681 inline void freeI32(RegI32 r); 682 inline void freeI64(RegI64 r); 683 inline void freeRef(RegRef r); 684 inline void freePtr(RegPtr r); 685 inline void freeF64(RegF64 r); 686 inline void freeF32(RegF32 r); 687 #ifdef ENABLE_WASM_SIMD 688 inline void freeV128(RegV128 r); 689 #endif 690 691 // Use when you need a register for a short time but explicitly want to avoid 692 // a full sync(). 693 [[nodiscard]] inline RegPtr needTempPtr(RegPtr fallback, bool* saved); 694 inline void freeTempPtr(RegPtr r, bool saved); 695 696 #ifdef JS_CODEGEN_ARM 697 [[nodiscard]] inline RegI64 needI64Pair(); 698 #endif 699 700 #ifdef DEBUG 701 friend class LeakCheck; 702 703 class MOZ_RAII LeakCheck { 704 private: 705 const BaseRegAlloc& ra; 706 AllocatableGeneralRegisterSet knownGPR_; 707 AllocatableFloatRegisterSet knownFPU_; 708 709 public: 710 explicit LeakCheck(const BaseRegAlloc& ra) : ra(ra) { 711 knownGPR_ = ra.availGPR; 712 knownFPU_ = ra.availFPU; 713 } 714 715 ~LeakCheck() { 716 MOZ_ASSERT(knownGPR_.bits() == ra.allGPR.bits()); 717 MOZ_ASSERT(knownFPU_.bits() == ra.allFPU.bits()); 718 } 719 720 void addKnownI32(RegI32 r) { knownGPR_.add(r); } 721 722 void addKnownI64(RegI64 r) { 723 # ifdef JS_PUNBOX64 724 knownGPR_.add(r.reg); 725 # else 726 knownGPR_.add(r.high); 727 knownGPR_.add(r.low); 728 # endif 729 } 730 731 void addKnownF32(RegF32 r) { knownFPU_.add(r); } 732 733 void addKnownF64(RegF64 r) { knownFPU_.add(r); } 734 735 # ifdef ENABLE_WASM_SIMD 736 void addKnownV128(RegV128 r) { knownFPU_.add(r); } 737 # endif 738 739 void addKnownRef(RegRef r) { knownGPR_.add(r); } 740 }; 741 #endif 742 }; 743 744 // Scratch register abstractions. 745 // 746 // We define our own scratch registers when the platform doesn't provide what we 747 // need. A notable use case is that we will need a private scratch register 748 // when the platform masm uses its scratch register very frequently (eg, ARM). 749 750 class BaseScratchRegister { 751 #ifdef DEBUG 752 BaseRegAlloc& ra; 753 BaseRegAlloc::ScratchKind kind_; 754 755 public: 756 explicit BaseScratchRegister(BaseRegAlloc& ra, BaseRegAlloc::ScratchKind kind) 757 : ra(ra), kind_(kind) { 758 MOZ_ASSERT(!ra.isScratchRegisterTaken(kind_)); 759 ra.setScratchRegisterTaken(kind_, true); 760 } 761 ~BaseScratchRegister() { 762 MOZ_ASSERT(ra.isScratchRegisterTaken(kind_)); 763 ra.setScratchRegisterTaken(kind_, false); 764 } 765 #else 766 public: 767 explicit BaseScratchRegister(BaseRegAlloc& ra, 768 BaseRegAlloc::ScratchKind kind) {} 769 #endif 770 }; 771 772 #ifdef ENABLE_WASM_SIMD 773 # ifdef RABALDR_SCRATCH_V128 774 class ScratchV128 : public BaseScratchRegister { 775 public: 776 explicit ScratchV128(BaseRegAlloc& ra) 777 : BaseScratchRegister(ra, BaseRegAlloc::ScratchKind::V128) {} 778 operator RegV128() const { return RegV128(RabaldrScratchV128); } 779 }; 780 # else 781 class ScratchV128 : public ScratchSimd128Scope { 782 public: 783 explicit ScratchV128(MacroAssembler& m) : ScratchSimd128Scope(m) {} 784 operator RegV128() const { return RegV128(FloatRegister(*this)); } 785 }; 786 # endif 787 #endif 788 789 #ifdef RABALDR_SCRATCH_F64 790 class ScratchF64 : public BaseScratchRegister { 791 public: 792 explicit ScratchF64(BaseRegAlloc& ra) 793 : BaseScratchRegister(ra, BaseRegAlloc::ScratchKind::F64) {} 794 operator RegF64() const { return RegF64(RabaldrScratchF64); } 795 }; 796 #else 797 class ScratchF64 : public ScratchDoubleScope { 798 public: 799 explicit ScratchF64(MacroAssembler& m) : ScratchDoubleScope(m) {} 800 operator RegF64() const { return RegF64(FloatRegister(*this)); } 801 }; 802 #endif 803 804 #ifdef RABALDR_SCRATCH_F32 805 class ScratchF32 : public BaseScratchRegister { 806 public: 807 explicit ScratchF32(BaseRegAlloc& ra) 808 : BaseScratchRegister(ra, BaseRegAlloc::ScratchKind::F32) {} 809 operator RegF32() const { return RegF32(RabaldrScratchF32); } 810 }; 811 #else 812 class ScratchF32 : public ScratchFloat32Scope { 813 public: 814 explicit ScratchF32(MacroAssembler& m) : ScratchFloat32Scope(m) {} 815 operator RegF32() const { return RegF32(FloatRegister(*this)); } 816 }; 817 #endif 818 819 #ifdef RABALDR_SCRATCH_I32 820 template <class RegType> 821 class ScratchGPR : public BaseScratchRegister { 822 public: 823 explicit ScratchGPR(BaseRegAlloc& ra) 824 : BaseScratchRegister(ra, BaseRegAlloc::ScratchKind::I32) {} 825 operator RegType() const { return RegType(RabaldrScratchI32); } 826 }; 827 #else 828 template <class RegType> 829 class ScratchGPR : public ScratchRegisterScope { 830 public: 831 explicit ScratchGPR(MacroAssembler& m) : ScratchRegisterScope(m) {} 832 operator RegType() const { return RegType(Register(*this)); } 833 }; 834 #endif 835 836 using ScratchI32 = ScratchGPR<RegI32>; 837 using ScratchPtr = ScratchGPR<RegPtr>; 838 using ScratchRef = ScratchGPR<RegRef>; 839 840 #if defined(JS_CODEGEN_X86) 841 // ScratchEBX is a mnemonic device: For some atomic ops we really need EBX, 842 // no other register will do. And we would normally have to allocate that 843 // register using ScratchI32 since normally the scratch register is EBX. 844 // But the whole point of ScratchI32 is to hide that relationship. By using 845 // the ScratchEBX alias, we document that at that point we require the 846 // scratch register to be EBX. 847 using ScratchEBX = ScratchI32; 848 849 // ScratchI8 is a mnemonic device: For some ops we need a register with a 850 // byte subregister. 851 using ScratchI8 = ScratchI32; 852 #endif 853 854 } // namespace wasm 855 } // namespace js 856 857 #endif // wasm_wasm_baseline_regdefs_h