tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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