tor-browser

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

MIR-wasm.h (110769B)


      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 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /*
      8 * Everything needed to build actual MIR instructions: the actual opcodes and
      9 * instructions, the instruction interface, and use chains.
     10 */
     11 
     12 #ifndef jit_MIR_wasm_h
     13 #define jit_MIR_wasm_h
     14 
     15 #include "mozilla/HashFunctions.h"
     16 #include "mozilla/Vector.h"
     17 #ifdef JS_JITSPEW
     18 #  include "mozilla/Sprintf.h"
     19 #endif
     20 
     21 #include <algorithm>
     22 
     23 #include "jit/MIR.h"
     24 #include "util/DifferentialTesting.h"
     25 #include "wasm/WasmGcObject.h"
     26 
     27 namespace js {
     28 
     29 class WasmInstanceObject;
     30 
     31 namespace wasm {
     32 class FuncExport;
     33 extern uint32_t MIRTypeToABIResultSize(jit::MIRType);
     34 }  // namespace wasm
     35 
     36 namespace jit {
     37 
     38 class MWasmNullConstant : public MNullaryInstruction {
     39  mozilla::Maybe<wasm::RefTypeHierarchy> hierarchy_;
     40 
     41  explicit MWasmNullConstant(wasm::MaybeRefType type)
     42      : MNullaryInstruction(classOpcode),
     43        hierarchy_(type.isSome() ? mozilla::Some(type.value().hierarchy())
     44                                 : mozilla::Nothing()) {
     45    setResultType(MIRType::WasmAnyRef);
     46    setMovable();
     47    if (type.isSome()) {
     48      initWasmRefType(wasm::MaybeRefType(type.value().bottomType()));
     49    }
     50  }
     51 
     52 public:
     53  INSTRUCTION_HEADER(WasmNullConstant)
     54  TRIVIAL_NEW_WRAPPERS
     55 
     56  mozilla::Maybe<wasm::RefTypeHierarchy> hierarchy() const {
     57    return hierarchy_;
     58  }
     59 
     60  HashNumber valueHash() const override;
     61  bool congruentTo(const MDefinition* ins) const override {
     62    return ins->isWasmNullConstant() &&
     63           hierarchy() == ins->toWasmNullConstant()->hierarchy();
     64  }
     65  AliasSet getAliasSet() const override { return AliasSet::None(); }
     66 
     67  ALLOW_CLONE(MWasmNullConstant)
     68 };
     69 
     70 // Floating-point value as created by wasm. Just a constant value, used to
     71 // effectively inhibit all the MIR optimizations. This uses the same LIR nodes
     72 // as a MConstant of the same type would.
     73 class MWasmFloatConstant : public MNullaryInstruction {
     74  union {
     75    float f32_;
     76    double f64_;
     77 #ifdef ENABLE_WASM_SIMD
     78    int8_t s128_[16];
     79    uint64_t bits_[2];
     80 #else
     81    uint64_t bits_[1];
     82 #endif
     83  } u;
     84 
     85  explicit MWasmFloatConstant(MIRType type) : MNullaryInstruction(classOpcode) {
     86    u.bits_[0] = 0;
     87 #ifdef ENABLE_WASM_SIMD
     88    u.bits_[1] = 0;
     89 #endif
     90    setResultType(type);
     91  }
     92 
     93 public:
     94  INSTRUCTION_HEADER(WasmFloatConstant)
     95 
     96  static MWasmFloatConstant* NewDouble(TempAllocator& alloc, double d) {
     97    auto* ret = new (alloc) MWasmFloatConstant(MIRType::Double);
     98    ret->u.f64_ = d;
     99    return ret;
    100  }
    101 
    102  static MWasmFloatConstant* NewFloat32(TempAllocator& alloc, float f) {
    103    auto* ret = new (alloc) MWasmFloatConstant(MIRType::Float32);
    104    ret->u.f32_ = f;
    105    return ret;
    106  }
    107 
    108 #ifdef ENABLE_WASM_SIMD
    109  static MWasmFloatConstant* NewSimd128(TempAllocator& alloc,
    110                                        const SimdConstant& s) {
    111    auto* ret = new (alloc) MWasmFloatConstant(MIRType::Simd128);
    112    memcpy(ret->u.s128_, s.bytes(), 16);
    113    return ret;
    114  }
    115 #endif
    116 
    117  HashNumber valueHash() const override;
    118  bool congruentTo(const MDefinition* ins) const override;
    119  AliasSet getAliasSet() const override { return AliasSet::None(); }
    120 
    121  const double& toDouble() const {
    122    MOZ_ASSERT(type() == MIRType::Double);
    123    return u.f64_;
    124  }
    125  const float& toFloat32() const {
    126    MOZ_ASSERT(type() == MIRType::Float32);
    127    return u.f32_;
    128  }
    129 #ifdef ENABLE_WASM_SIMD
    130  const SimdConstant toSimd128() const {
    131    MOZ_ASSERT(type() == MIRType::Simd128);
    132    return SimdConstant::CreateX16(u.s128_);
    133  }
    134 #endif
    135 #ifdef JS_JITSPEW
    136  void getExtras(ExtrasCollector* extras) const override {
    137    char buf[64];
    138    switch (type()) {
    139      case MIRType::Float32:
    140        SprintfLiteral(buf, "f32{%e}", (double)u.f32_);
    141        break;
    142      case MIRType::Double:
    143        SprintfLiteral(buf, "f64{%e}", u.f64_);
    144        break;
    145 #  ifdef ENABLE_WASM_SIMD
    146      case MIRType::Simd128:
    147        SprintfLiteral(buf, "v128{[1]=%016llx:[0]=%016llx}",
    148                       (unsigned long long int)u.bits_[1],
    149                       (unsigned long long int)u.bits_[0]);
    150        break;
    151 #  endif
    152      default:
    153        SprintfLiteral(buf, "!!getExtras: missing case!!");
    154        break;
    155    }
    156    extras->add(buf);
    157  }
    158 #endif
    159 
    160  ALLOW_CLONE(MWasmFloatConstant)
    161 };
    162 
    163 // Converts a uint32 to a float32 (coming from wasm).
    164 class MWasmUnsignedToFloat32 : public MUnaryInstruction,
    165                               public NoTypePolicy::Data {
    166  explicit MWasmUnsignedToFloat32(MDefinition* def)
    167      : MUnaryInstruction(classOpcode, def) {
    168    setResultType(MIRType::Float32);
    169    setMovable();
    170  }
    171 
    172 public:
    173  INSTRUCTION_HEADER(WasmUnsignedToFloat32)
    174  TRIVIAL_NEW_WRAPPERS
    175 
    176  MDefinition* foldsTo(TempAllocator& alloc) override;
    177  bool congruentTo(const MDefinition* ins) const override {
    178    return congruentIfOperandsEqual(ins);
    179  }
    180  AliasSet getAliasSet() const override { return AliasSet::None(); }
    181 
    182  bool canProduceFloat32() const override { return true; }
    183 
    184  ALLOW_CLONE(MWasmUnsignedToFloat32)
    185 };
    186 
    187 class MWasmNewI31Ref : public MUnaryInstruction, public NoTypePolicy::Data {
    188  explicit MWasmNewI31Ref(MDefinition* input)
    189      : MUnaryInstruction(classOpcode, input) {
    190    MOZ_ASSERT(input->type() == MIRType::Int32);
    191    setResultType(MIRType::WasmAnyRef);
    192    setMovable();
    193    initWasmRefType(wasm::MaybeRefType(wasm::RefType::i31().asNonNullable()));
    194  }
    195 
    196 public:
    197  INSTRUCTION_HEADER(WasmNewI31Ref)
    198  TRIVIAL_NEW_WRAPPERS
    199 
    200  bool congruentTo(const MDefinition* ins) const override {
    201    return congruentIfOperandsEqual(ins);
    202  }
    203 
    204  AliasSet getAliasSet() const override { return AliasSet::None(); }
    205 };
    206 
    207 // The same as MWasmTruncateToInt64 but with the Instance dependency.
    208 // It used only for arm now because on arm we need to call builtin to truncate
    209 // to i64.
    210 class MWasmBuiltinTruncateToInt64 : public MAryInstruction<2>,
    211                                    public NoTypePolicy::Data {
    212  TruncFlags flags_;
    213  wasm::TrapSiteDesc trapSiteDesc_;
    214 
    215  MWasmBuiltinTruncateToInt64(MDefinition* def, MDefinition* instance,
    216                              TruncFlags flags,
    217                              const wasm::TrapSiteDesc& trapSiteDesc)
    218      : MAryInstruction(classOpcode),
    219        flags_(flags),
    220        trapSiteDesc_(trapSiteDesc) {
    221    initOperand(0, def);
    222    initOperand(1, instance);
    223 
    224    setResultType(MIRType::Int64);
    225    setGuard();  // neither removable nor movable because of possible
    226                 // side-effects.
    227  }
    228 
    229 public:
    230  INSTRUCTION_HEADER(WasmBuiltinTruncateToInt64)
    231  NAMED_OPERANDS((0, input), (1, instance));
    232  TRIVIAL_NEW_WRAPPERS
    233 
    234  bool isUnsigned() const { return flags_ & TRUNC_UNSIGNED; }
    235  bool isSaturating() const { return flags_ & TRUNC_SATURATING; }
    236  TruncFlags flags() const { return flags_; }
    237  const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; }
    238 
    239  bool congruentTo(const MDefinition* ins) const override {
    240    return congruentIfOperandsEqual(ins) &&
    241           ins->toWasmBuiltinTruncateToInt64()->flags() == flags_;
    242  }
    243  AliasSet getAliasSet() const override { return AliasSet::None(); }
    244 };
    245 
    246 class MWasmTruncateToInt64 : public MUnaryInstruction,
    247                             public NoTypePolicy::Data {
    248  TruncFlags flags_;
    249  wasm::TrapSiteDesc trapSiteDesc_;
    250 
    251  MWasmTruncateToInt64(MDefinition* def, TruncFlags flags,
    252                       const wasm::TrapSiteDesc& trapSiteDesc)
    253      : MUnaryInstruction(classOpcode, def),
    254        flags_(flags),
    255        trapSiteDesc_(trapSiteDesc) {
    256    setResultType(MIRType::Int64);
    257    setGuard();  // neither removable nor movable because of possible
    258                 // side-effects.
    259  }
    260 
    261 public:
    262  INSTRUCTION_HEADER(WasmTruncateToInt64)
    263  TRIVIAL_NEW_WRAPPERS
    264 
    265  bool isUnsigned() const { return flags_ & TRUNC_UNSIGNED; }
    266  bool isSaturating() const { return flags_ & TRUNC_SATURATING; }
    267  TruncFlags flags() const { return flags_; }
    268  const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; }
    269 
    270  bool congruentTo(const MDefinition* ins) const override {
    271    return congruentIfOperandsEqual(ins) &&
    272           ins->toWasmTruncateToInt64()->flags() == flags_;
    273  }
    274  AliasSet getAliasSet() const override { return AliasSet::None(); }
    275 };
    276 
    277 // Truncate a value to an int32, with wasm semantics: this will trap when the
    278 // value is out of range.
    279 class MWasmTruncateToInt32 : public MUnaryInstruction,
    280                             public NoTypePolicy::Data {
    281  TruncFlags flags_;
    282  wasm::TrapSiteDesc trapSiteDesc_;
    283 
    284  explicit MWasmTruncateToInt32(MDefinition* def, TruncFlags flags,
    285                                const wasm::TrapSiteDesc& trapSiteDesc)
    286      : MUnaryInstruction(classOpcode, def),
    287        flags_(flags),
    288        trapSiteDesc_(trapSiteDesc) {
    289    setResultType(MIRType::Int32);
    290    setGuard();  // neither removable nor movable because of possible
    291                 // side-effects.
    292  }
    293 
    294 public:
    295  INSTRUCTION_HEADER(WasmTruncateToInt32)
    296  TRIVIAL_NEW_WRAPPERS
    297 
    298  bool isUnsigned() const { return flags_ & TRUNC_UNSIGNED; }
    299  bool isSaturating() const { return flags_ & TRUNC_SATURATING; }
    300  TruncFlags flags() const { return flags_; }
    301  const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; }
    302 
    303  MDefinition* foldsTo(TempAllocator& alloc) override;
    304 
    305  bool congruentTo(const MDefinition* ins) const override {
    306    return congruentIfOperandsEqual(ins) &&
    307           ins->toWasmTruncateToInt32()->flags() == flags_;
    308  }
    309 
    310  AliasSet getAliasSet() const override { return AliasSet::None(); }
    311 };
    312 
    313 // It is like MTruncateToInt32 but with instance dependency.
    314 class MWasmBuiltinTruncateToInt32 : public MAryInstruction<2>,
    315                                    public ToInt32Policy::Data {
    316  wasm::TrapSiteDesc trapSiteDesc_;
    317 
    318  MWasmBuiltinTruncateToInt32(
    319      MDefinition* def, MDefinition* instance,
    320      wasm::TrapSiteDesc trapSiteDesc = wasm::TrapSiteDesc())
    321      : MAryInstruction(classOpcode), trapSiteDesc_(trapSiteDesc) {
    322    initOperand(0, def);
    323    initOperand(1, instance);
    324    setResultType(MIRType::Int32);
    325    setMovable();
    326 
    327    // Guard unless the conversion is known to be non-effectful & non-throwing.
    328    if (MTruncateToInt32::mightHaveSideEffects(def)) {
    329      setGuard();
    330    }
    331  }
    332 
    333 public:
    334  INSTRUCTION_HEADER(WasmBuiltinTruncateToInt32)
    335  NAMED_OPERANDS((0, input), (1, instance))
    336  TRIVIAL_NEW_WRAPPERS
    337 
    338  bool congruentTo(const MDefinition* ins) const override {
    339    return congruentIfOperandsEqual(ins);
    340  }
    341  AliasSet getAliasSet() const override { return AliasSet::None(); }
    342 
    343  const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; }
    344 
    345  ALLOW_CLONE(MWasmBuiltinTruncateToInt32)
    346 };
    347 
    348 class MWasmBuiltinDivI64 : public MAryInstruction<3>, public ArithPolicy::Data {
    349  bool canBeNegativeZero_;
    350  bool canBeNegativeOverflow_;
    351  bool canBeDivideByZero_;
    352  bool canBeNegativeDividend_;
    353  bool unsigned_;  // If false, signedness will be derived from operands
    354  bool trapOnError_;
    355  wasm::TrapSiteDesc trapSiteDesc_;
    356 
    357  MWasmBuiltinDivI64(MDefinition* left, MDefinition* right,
    358                     MDefinition* instance)
    359      : MAryInstruction(classOpcode),
    360        canBeNegativeZero_(true),
    361        canBeNegativeOverflow_(true),
    362        canBeDivideByZero_(true),
    363        canBeNegativeDividend_(true),
    364        unsigned_(false),
    365        trapOnError_(false) {
    366    initOperand(0, left);
    367    initOperand(1, right);
    368    initOperand(2, instance);
    369 
    370    setResultType(MIRType::Int64);
    371    setMovable();
    372  }
    373 
    374 public:
    375  INSTRUCTION_HEADER(WasmBuiltinDivI64)
    376 
    377  NAMED_OPERANDS((0, lhs), (1, rhs), (2, instance))
    378 
    379  static MWasmBuiltinDivI64* New(
    380      TempAllocator& alloc, MDefinition* left, MDefinition* right,
    381      MDefinition* instance, bool unsignd, bool trapOnError = false,
    382      wasm::TrapSiteDesc trapSiteDesc = wasm::TrapSiteDesc()) {
    383    auto* wasm64Div = new (alloc) MWasmBuiltinDivI64(left, right, instance);
    384    wasm64Div->unsigned_ = unsignd;
    385    wasm64Div->trapOnError_ = trapOnError;
    386    wasm64Div->trapSiteDesc_ = trapSiteDesc;
    387    if (trapOnError) {
    388      wasm64Div->setGuard();  // not removable because of possible side-effects.
    389      wasm64Div->setNotMovable();
    390    }
    391    return wasm64Div;
    392  }
    393 
    394  bool canBeNegativeZero() const { return canBeNegativeZero_; }
    395  void setCanBeNegativeZero(bool negativeZero) {
    396    canBeNegativeZero_ = negativeZero;
    397  }
    398 
    399  bool canBeNegativeOverflow() const { return canBeNegativeOverflow_; }
    400 
    401  bool canBeDivideByZero() const { return canBeDivideByZero_; }
    402 
    403  bool canBeNegativeDividend() const {
    404    // "Dividend" is an ambiguous concept for unsigned truncated
    405    // division, because of the truncation procedure:
    406    // ((x>>>0)/2)|0, for example, gets transformed in
    407    // MWasmDiv::truncate into a node with lhs representing x (not
    408    // x>>>0) and rhs representing the constant 2; in other words,
    409    // the MIR node corresponds to "cast operands to unsigned and
    410    // divide" operation. In this case, is the dividend x or is it
    411    // x>>>0? In order to resolve such ambiguities, we disallow
    412    // the usage of this method for unsigned division.
    413    MOZ_ASSERT(!unsigned_);
    414    return canBeNegativeDividend_;
    415  }
    416 
    417  bool isUnsigned() const { return unsigned_; }
    418 
    419  bool trapOnError() const { return trapOnError_; }
    420  const wasm::TrapSiteDesc& trapSiteDesc() const {
    421    MOZ_ASSERT(trapSiteDesc_.isValid());
    422    return trapSiteDesc_;
    423  }
    424 
    425  ALLOW_CLONE(MWasmBuiltinDivI64)
    426 };
    427 
    428 class MWasmBuiltinModD : public MAryInstruction<3>, public ArithPolicy::Data {
    429  wasm::BytecodeOffset bytecodeOffset_;
    430 
    431  MWasmBuiltinModD(MDefinition* left, MDefinition* right, MDefinition* instance,
    432                   MIRType type)
    433      : MAryInstruction(classOpcode) {
    434    initOperand(0, left);
    435    initOperand(1, right);
    436    initOperand(2, instance);
    437 
    438    setResultType(type);
    439    setMovable();
    440  }
    441 
    442 public:
    443  INSTRUCTION_HEADER(WasmBuiltinModD)
    444  NAMED_OPERANDS((0, lhs), (1, rhs), (2, instance))
    445 
    446  static MWasmBuiltinModD* New(
    447      TempAllocator& alloc, MDefinition* left, MDefinition* right,
    448      MDefinition* instance, MIRType type,
    449      wasm::BytecodeOffset bytecodeOffset = wasm::BytecodeOffset()) {
    450    auto* wasmBuiltinModD =
    451        new (alloc) MWasmBuiltinModD(left, right, instance, type);
    452    wasmBuiltinModD->bytecodeOffset_ = bytecodeOffset;
    453    return wasmBuiltinModD;
    454  }
    455 
    456  wasm::BytecodeOffset bytecodeOffset() const {
    457    MOZ_ASSERT(bytecodeOffset_.isValid());
    458    return bytecodeOffset_;
    459  }
    460 
    461  ALLOW_CLONE(MWasmBuiltinModD)
    462 };
    463 
    464 class MWasmBuiltinModI64 : public MAryInstruction<3>, public ArithPolicy::Data {
    465  bool unsigned_;  // If false, signedness will be derived from operands
    466  bool canBeNegativeDividend_;
    467  bool canBeDivideByZero_;
    468  bool trapOnError_;
    469  wasm::TrapSiteDesc trapSiteDesc_;
    470 
    471  MWasmBuiltinModI64(MDefinition* left, MDefinition* right,
    472                     MDefinition* instance)
    473      : MAryInstruction(classOpcode),
    474        unsigned_(false),
    475        canBeNegativeDividend_(true),
    476        canBeDivideByZero_(true),
    477        trapOnError_(false) {
    478    initOperand(0, left);
    479    initOperand(1, right);
    480    initOperand(2, instance);
    481 
    482    setResultType(MIRType::Int64);
    483    setMovable();
    484  }
    485 
    486 public:
    487  INSTRUCTION_HEADER(WasmBuiltinModI64)
    488 
    489  NAMED_OPERANDS((0, lhs), (1, rhs), (2, instance))
    490 
    491  static MWasmBuiltinModI64* New(
    492      TempAllocator& alloc, MDefinition* left, MDefinition* right,
    493      MDefinition* instance, bool unsignd, bool trapOnError = false,
    494      wasm::TrapSiteDesc trapSiteDesc = wasm::TrapSiteDesc()) {
    495    auto* mod = new (alloc) MWasmBuiltinModI64(left, right, instance);
    496    mod->unsigned_ = unsignd;
    497    mod->trapOnError_ = trapOnError;
    498    mod->trapSiteDesc_ = trapSiteDesc;
    499    if (trapOnError) {
    500      mod->setGuard();  // not removable because of possible side-effects.
    501      mod->setNotMovable();
    502    }
    503    return mod;
    504  }
    505 
    506  bool canBeNegativeDividend() const {
    507    MOZ_ASSERT(!unsigned_);
    508    return canBeNegativeDividend_;
    509  }
    510 
    511  bool canBeDivideByZero() const { return canBeDivideByZero_; }
    512 
    513  bool isUnsigned() const { return unsigned_; }
    514 
    515  bool trapOnError() const { return trapOnError_; }
    516  const wasm::TrapSiteDesc& trapSiteDesc() const {
    517    MOZ_ASSERT(trapSiteDesc_.isValid());
    518    return trapSiteDesc_;
    519  }
    520 
    521  ALLOW_CLONE(MWasmBuiltinModI64)
    522 };
    523 
    524 // Check whether we need to fire the interrupt handler (in wasm code).
    525 class MWasmInterruptCheck : public MUnaryInstruction,
    526                            public NoTypePolicy::Data {
    527  wasm::TrapSiteDesc trapSiteDesc_;
    528 
    529  MWasmInterruptCheck(MDefinition* instance,
    530                      const wasm::TrapSiteDesc& trapSiteDesc)
    531      : MUnaryInstruction(classOpcode, instance), trapSiteDesc_(trapSiteDesc) {
    532    setGuard();
    533  }
    534 
    535 public:
    536  INSTRUCTION_HEADER(WasmInterruptCheck)
    537  TRIVIAL_NEW_WRAPPERS
    538  NAMED_OPERANDS((0, instance))
    539 
    540  AliasSet getAliasSet() const override { return AliasSet::None(); }
    541  const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; }
    542 
    543  ALLOW_CLONE(MWasmInterruptCheck)
    544 };
    545 
    546 // Directly jumps to the indicated trap, leaving Wasm code and reporting a
    547 // runtime error.
    548 
    549 class MWasmTrap : public MAryControlInstruction<0, 0>,
    550                  public NoTypePolicy::Data {
    551  wasm::Trap trap_;
    552  wasm::TrapSiteDesc trapSiteDesc_;
    553 
    554  explicit MWasmTrap(wasm::Trap trap, const wasm::TrapSiteDesc& trapSiteDesc)
    555      : MAryControlInstruction(classOpcode),
    556        trap_(trap),
    557        trapSiteDesc_(trapSiteDesc) {}
    558 
    559 public:
    560  INSTRUCTION_HEADER(WasmTrap)
    561  TRIVIAL_NEW_WRAPPERS
    562 
    563  AliasSet getAliasSet() const override { return AliasSet::None(); }
    564 
    565  wasm::Trap trap() const { return trap_; }
    566  const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; }
    567 };
    568 
    569 // Flips the input's sign bit, independently of the rest of the number's
    570 // payload. Note this is different from multiplying by minus-one, which has
    571 // side-effects for e.g. NaNs.
    572 class MWasmNeg : public MUnaryInstruction, public NoTypePolicy::Data {
    573  MWasmNeg(MDefinition* op, MIRType type) : MUnaryInstruction(classOpcode, op) {
    574    setResultType(type);
    575    setMovable();
    576  }
    577 
    578 public:
    579  INSTRUCTION_HEADER(WasmNeg)
    580  TRIVIAL_NEW_WRAPPERS
    581  AliasSet getAliasSet() const override { return AliasSet::None(); }
    582  ALLOW_CLONE(MWasmNeg)
    583 };
    584 
    585 // Machine-level bitwise AND/OR/XOR, avoiding all JS-level complexity embodied
    586 // in MBinaryBitwiseInstruction.
    587 class MWasmBinaryBitwise : public MBinaryInstruction,
    588                           public NoTypePolicy::Data {
    589 public:
    590  enum class SubOpcode { And, Or, Xor };
    591 
    592 protected:
    593  MWasmBinaryBitwise(MDefinition* left, MDefinition* right, MIRType type,
    594                     SubOpcode subOpcode)
    595      : MBinaryInstruction(classOpcode, left, right), subOpcode_(subOpcode) {
    596    MOZ_ASSERT(type == MIRType::Int32 || type == MIRType::Int64);
    597    setResultType(type);
    598    setMovable();
    599    setCommutative();
    600  }
    601 
    602 public:
    603  INSTRUCTION_HEADER(WasmBinaryBitwise)
    604  TRIVIAL_NEW_WRAPPERS
    605 
    606  SubOpcode subOpcode() const { return subOpcode_; }
    607  MDefinition* foldsTo(TempAllocator& alloc) override;
    608 
    609  bool congruentTo(const MDefinition* ins) const override {
    610    return ins->isWasmBinaryBitwise() &&
    611           ins->toWasmBinaryBitwise()->subOpcode() == subOpcode() &&
    612           binaryCongruentTo(ins);
    613  }
    614 
    615  AliasSet getAliasSet() const override { return AliasSet::None(); }
    616 
    617 #ifdef JS_JITSPEW
    618  void getExtras(ExtrasCollector* extras) const override {
    619    const char* what = "!!unknown!!";
    620    switch (subOpcode()) {
    621      case SubOpcode::And:
    622        what = "And";
    623        break;
    624      case SubOpcode::Or:
    625        what = "Or";
    626        break;
    627      case SubOpcode::Xor:
    628        what = "Xor";
    629        break;
    630    }
    631    extras->add(what);
    632  }
    633 #endif
    634 
    635 private:
    636  SubOpcode subOpcode_;
    637 
    638  ALLOW_CLONE(MWasmBinaryBitwise)
    639 };
    640 
    641 class MWasmLoadInstance : public MUnaryInstruction, public NoTypePolicy::Data {
    642  uint32_t offset_;
    643  AliasSet aliases_;
    644 
    645  explicit MWasmLoadInstance(MDefinition* instance, uint32_t offset,
    646                             MIRType type, AliasSet aliases)
    647      : MUnaryInstruction(classOpcode, instance),
    648        offset_(offset),
    649        aliases_(aliases) {
    650    // Different instance data have different alias classes and only those
    651    // classes are allowed.
    652    MOZ_ASSERT(
    653        aliases_.flags() == AliasSet::Load(AliasSet::WasmHeapMeta).flags() ||
    654        aliases_.flags() == AliasSet::Load(AliasSet::WasmTableMeta).flags() ||
    655        aliases_.flags() ==
    656            AliasSet::Load(AliasSet::WasmPendingException).flags() ||
    657        aliases_.flags() == AliasSet::None().flags());
    658 
    659    // The only types supported at the moment.
    660    MOZ_ASSERT(type == MIRType::Pointer || type == MIRType::Int32 ||
    661               type == MIRType::Int64 || type == MIRType::WasmAnyRef);
    662 
    663    setMovable();
    664    setResultType(type);
    665  }
    666 
    667 public:
    668  INSTRUCTION_HEADER(WasmLoadInstance)
    669  TRIVIAL_NEW_WRAPPERS
    670  NAMED_OPERANDS((0, instance))
    671 
    672  uint32_t offset() const { return offset_; }
    673 
    674  bool congruentTo(const MDefinition* ins) const override {
    675    return op() == ins->op() &&
    676           offset() == ins->toWasmLoadInstance()->offset() &&
    677           type() == ins->type();
    678  }
    679 
    680  HashNumber valueHash() const override {
    681    HashNumber hash = MUnaryInstruction::valueHash();
    682    hash = addU32ToHash(hash, offset());
    683    return hash;
    684  }
    685 
    686  AliasSet getAliasSet() const override { return aliases_; }
    687 };
    688 
    689 class MWasmStoreInstance : public MBinaryInstruction,
    690                           public NoTypePolicy::Data {
    691  uint32_t offset_;
    692  AliasSet aliases_;
    693 
    694  explicit MWasmStoreInstance(MDefinition* instance, MDefinition* value,
    695                              uint32_t offset, MIRType type, AliasSet aliases)
    696      : MBinaryInstruction(classOpcode, instance, value),
    697        offset_(offset),
    698        aliases_(aliases) {
    699    // Different instance data have different alias classes and only those
    700    // classes are allowed.
    701    MOZ_ASSERT(aliases_.flags() ==
    702               AliasSet::Store(AliasSet::WasmPendingException).flags());
    703 
    704    // The only types supported at the moment.
    705    MOZ_ASSERT(type == MIRType::Pointer || type == MIRType::Int32 ||
    706               type == MIRType::Int64 || type == MIRType::WasmAnyRef);
    707  }
    708 
    709 public:
    710  INSTRUCTION_HEADER(WasmStoreInstance)
    711  TRIVIAL_NEW_WRAPPERS
    712  NAMED_OPERANDS((0, instance), (1, value))
    713 
    714  uint32_t offset() const { return offset_; }
    715 
    716  AliasSet getAliasSet() const override { return aliases_; }
    717 };
    718 
    719 class MWasmHeapReg : public MNullaryInstruction {
    720  AliasSet aliases_;
    721 
    722  explicit MWasmHeapReg(AliasSet aliases)
    723      : MNullaryInstruction(classOpcode), aliases_(aliases) {
    724    setMovable();
    725    setResultType(MIRType::Pointer);
    726  }
    727 
    728 public:
    729  INSTRUCTION_HEADER(WasmHeapReg)
    730  TRIVIAL_NEW_WRAPPERS
    731 
    732  bool congruentTo(const MDefinition* ins) const override {
    733    return ins->isWasmHeapReg();
    734  }
    735 
    736  AliasSet getAliasSet() const override { return aliases_; }
    737 };
    738 
    739 // For memory32, bounds check nodes are of type Int32 on 32-bit systems for both
    740 // wasm and asm.js code, as well as on 64-bit systems for asm.js code and for
    741 // wasm code that is known to have a bounds check limit that fits into 32 bits.
    742 // They are of type Int64 only on 64-bit systems for wasm code with 4GB heaps.
    743 // There is no way for nodes of both types to be present in the same function.
    744 // Should this change, then BCE must be updated to take type into account.
    745 //
    746 // For memory64, bounds check nodes are always of type Int64.
    747 
    748 class MWasmBoundsCheck : public MBinaryInstruction, public NoTypePolicy::Data {
    749 public:
    750  enum Target {
    751    // If using the following options, `targetIndex` must be specified.
    752    Memory,
    753    Table,
    754    // Everything else. Currently used for arrays in the GC proposal. If using
    755    // this, targetIndex should not be used.
    756    Other,
    757  };
    758 
    759 private:
    760  wasm::TrapSiteDesc trapSiteDesc_;
    761  Target target_;
    762  uint32_t targetIndex_;
    763 
    764  explicit MWasmBoundsCheck(MDefinition* index, MDefinition* boundsCheckLimit,
    765                            const wasm::TrapSiteDesc& trapSiteDesc,
    766                            Target target, uint32_t targetIndex = UINT32_MAX)
    767      : MBinaryInstruction(classOpcode, index, boundsCheckLimit),
    768        trapSiteDesc_(trapSiteDesc),
    769        target_(target),
    770        targetIndex_(targetIndex) {
    771    MOZ_ASSERT(index->type() == boundsCheckLimit->type());
    772    MOZ_ASSERT_IF(target == Memory || target == Table,
    773                  targetIndex != UINT32_MAX);
    774    MOZ_ASSERT_IF(target == Other, targetIndex == UINT32_MAX);
    775 
    776    // Bounds check is effectful: it throws for OOB.
    777    setGuard();
    778 
    779    if (JitOptions.spectreIndexMasking) {
    780      setResultType(index->type());
    781    }
    782  }
    783 
    784 public:
    785  INSTRUCTION_HEADER(WasmBoundsCheck)
    786  TRIVIAL_NEW_WRAPPERS
    787  NAMED_OPERANDS((0, index), (1, boundsCheckLimit))
    788 
    789  AliasSet getAliasSet() const override { return AliasSet::None(); }
    790 
    791  const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; }
    792  Target target() const { return target_; }
    793  uint32_t targetIndex() const { return targetIndex_; }
    794 
    795  bool isRedundant() const { return !isGuard(); }
    796  void setRedundant() { setNotGuard(); }
    797 
    798  bool congruentTo(const MDefinition* ins) const override {
    799    return congruentIfOperandsEqual(ins) &&
    800           ins->toWasmBoundsCheck()->target() == target() &&
    801           ins->toWasmBoundsCheck()->targetIndex() == targetIndex();
    802  }
    803 
    804  ALLOW_CLONE(MWasmBoundsCheck)
    805 };
    806 
    807 class MWasmAddOffset : public MUnaryInstruction, public NoTypePolicy::Data {
    808  uint64_t offset_;
    809  wasm::TrapSiteDesc trapSiteDesc_;
    810 
    811  MWasmAddOffset(MDefinition* base, uint64_t offset,
    812                 const wasm::TrapSiteDesc& trapSiteDesc)
    813      : MUnaryInstruction(classOpcode, base),
    814        offset_(offset),
    815        trapSiteDesc_(trapSiteDesc) {
    816    setGuard();
    817    MOZ_ASSERT(base->type() == MIRType::Int32 ||
    818               base->type() == MIRType::Int64);
    819    setResultType(base->type());
    820  }
    821 
    822 public:
    823  INSTRUCTION_HEADER(WasmAddOffset)
    824  TRIVIAL_NEW_WRAPPERS
    825  NAMED_OPERANDS((0, base))
    826 
    827  MDefinition* foldsTo(TempAllocator& alloc) override;
    828 
    829  AliasSet getAliasSet() const override { return AliasSet::None(); }
    830 
    831  uint64_t offset() const { return offset_; }
    832  const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; }
    833 };
    834 
    835 class MWasmAlignmentCheck : public MUnaryInstruction,
    836                            public NoTypePolicy::Data {
    837  uint32_t byteSize_;
    838  wasm::TrapSiteDesc trapSiteDesc_;
    839 
    840  explicit MWasmAlignmentCheck(MDefinition* index, uint32_t byteSize,
    841                               const wasm::TrapSiteDesc& trapSiteDesc)
    842      : MUnaryInstruction(classOpcode, index),
    843        byteSize_(byteSize),
    844        trapSiteDesc_(trapSiteDesc) {
    845    MOZ_ASSERT(mozilla::IsPowerOfTwo(byteSize));
    846    // Alignment check is effectful: it throws for unaligned.
    847    setGuard();
    848  }
    849 
    850 public:
    851  INSTRUCTION_HEADER(WasmAlignmentCheck)
    852  TRIVIAL_NEW_WRAPPERS
    853  NAMED_OPERANDS((0, index))
    854 
    855  bool congruentTo(const MDefinition* ins) const override;
    856 
    857  AliasSet getAliasSet() const override { return AliasSet::None(); }
    858 
    859  uint32_t byteSize() const { return byteSize_; }
    860 
    861  const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; }
    862 
    863  ALLOW_CLONE(MWasmAlignmentCheck)
    864 };
    865 
    866 class MWasmLoad
    867    : public MVariadicInstruction,  // memoryBase is nullptr on some platforms
    868      public NoTypePolicy::Data {
    869  wasm::MemoryAccessDesc access_;
    870 
    871  explicit MWasmLoad(const wasm::MemoryAccessDesc& access, MIRType resultType)
    872      : MVariadicInstruction(classOpcode), access_(access) {
    873    setGuard();
    874    setResultType(resultType);
    875  }
    876 
    877 public:
    878  INSTRUCTION_HEADER(WasmLoad)
    879  NAMED_OPERANDS((0, base), (1, memoryBase));
    880 
    881  static MWasmLoad* New(TempAllocator& alloc, MDefinition* memoryBase,
    882                        MDefinition* base, const wasm::MemoryAccessDesc& access,
    883                        MIRType resultType) {
    884    MWasmLoad* load = new (alloc) MWasmLoad(access, resultType);
    885    if (!load->init(alloc, 1 + !!memoryBase)) {
    886      return nullptr;
    887    }
    888 
    889    load->initOperand(0, base);
    890    if (memoryBase) {
    891      load->initOperand(1, memoryBase);
    892    }
    893 
    894    return load;
    895  }
    896 
    897  const wasm::MemoryAccessDesc& access() const { return access_; }
    898 
    899  AliasSet getAliasSet() const override {
    900    // When a barrier is needed, make the instruction effectful by giving
    901    // it a "store" effect.
    902    if (access_.isAtomic()) {
    903      return AliasSet::Store(AliasSet::WasmHeap);
    904    }
    905    return AliasSet::Load(AliasSet::WasmHeap);
    906  }
    907 
    908  bool hasMemoryBase() const { return numOperands() > 1; }
    909 
    910 #ifdef JS_JITSPEW
    911  void getExtras(ExtrasCollector* extras) const override {
    912    char buf[64];
    913    SprintfLiteral(buf, "(offs=%lld)", (long long int)access().offset64());
    914    extras->add(buf);
    915  }
    916 #endif
    917 
    918  // Unfortunately we cannot use ALLOW_CLONE here, due to the variable number
    919  // of operands.
    920  bool canClone() const override { return true; }
    921  MInstruction* clone(TempAllocator& alloc,
    922                      const MDefinitionVector& inputs) const override {
    923    MInstruction* res =
    924        MWasmLoad::New(alloc, hasMemoryBase() ? memoryBase() : nullptr, base(),
    925                       access(), type());
    926    if (!res) {
    927      return nullptr;
    928    }
    929    for (size_t i = 0; i < numOperands(); i++) {
    930      res->replaceOperand(i, inputs[i]);
    931    }
    932    return res;
    933  }
    934 };
    935 
    936 class MWasmStore : public MVariadicInstruction, public NoTypePolicy::Data {
    937  wasm::MemoryAccessDesc access_;
    938 
    939  explicit MWasmStore(const wasm::MemoryAccessDesc& access)
    940      : MVariadicInstruction(classOpcode), access_(access) {
    941    setGuard();
    942  }
    943 
    944 public:
    945  INSTRUCTION_HEADER(WasmStore)
    946  NAMED_OPERANDS((0, base), (1, value), (2, memoryBase))
    947 
    948  static MWasmStore* New(TempAllocator& alloc, MDefinition* memoryBase,
    949                         MDefinition* base,
    950                         const wasm::MemoryAccessDesc& access,
    951                         MDefinition* value) {
    952    MWasmStore* store = new (alloc) MWasmStore(access);
    953    if (!store->init(alloc, 2 + !!memoryBase)) {
    954      return nullptr;
    955    }
    956 
    957    store->initOperand(0, base);
    958    store->initOperand(1, value);
    959    if (memoryBase) {
    960      store->initOperand(2, memoryBase);
    961    }
    962 
    963    return store;
    964  }
    965 
    966  const wasm::MemoryAccessDesc& access() const { return access_; }
    967 
    968  AliasSet getAliasSet() const override {
    969    return AliasSet::Store(AliasSet::WasmHeap);
    970  }
    971 
    972  bool hasMemoryBase() const { return numOperands() > 2; }
    973 
    974 #ifdef JS_JITSPEW
    975  void getExtras(ExtrasCollector* extras) const override {
    976    char buf[64];
    977    SprintfLiteral(buf, "(offs=%lld)", (long long int)access().offset64());
    978    extras->add(buf);
    979  }
    980 #endif
    981 
    982  bool canClone() const override { return true; }
    983  MInstruction* clone(TempAllocator& alloc,
    984                      const MDefinitionVector& inputs) const override {
    985    MInstruction* res =
    986        MWasmStore::New(alloc, hasMemoryBase() ? memoryBase() : nullptr, base(),
    987                        access(), value());
    988    if (!res) {
    989      return nullptr;
    990    }
    991    for (size_t i = 0; i < numOperands(); i++) {
    992      res->replaceOperand(i, inputs[i]);
    993    }
    994    return res;
    995  }
    996 };
    997 
    998 class MAsmJSMemoryAccess {
    999  Scalar::Type accessType_;
   1000  bool needsBoundsCheck_;
   1001 
   1002 public:
   1003  explicit MAsmJSMemoryAccess(Scalar::Type accessType)
   1004      : accessType_(accessType), needsBoundsCheck_(true) {
   1005    MOZ_ASSERT(accessType != Scalar::Uint8Clamped);
   1006  }
   1007 
   1008  Scalar::Type accessType() const { return accessType_; }
   1009  unsigned byteSize() const { return TypedArrayElemSize(accessType()); }
   1010  bool needsBoundsCheck() const { return needsBoundsCheck_; }
   1011 
   1012  wasm::MemoryAccessDesc access() const {
   1013    return wasm::MemoryAccessDesc(0, accessType_, Scalar::byteSize(accessType_),
   1014                                  0, wasm::TrapSiteDesc(), false);
   1015  }
   1016 
   1017  void removeBoundsCheck() { needsBoundsCheck_ = false; }
   1018 };
   1019 
   1020 class MAsmJSLoadHeap
   1021    : public MVariadicInstruction,  // 1 plus optional memoryBase and
   1022                                    // boundsCheckLimit
   1023      public MAsmJSMemoryAccess,
   1024      public NoTypePolicy::Data {
   1025  uint32_t memoryBaseIndex_;
   1026 
   1027  explicit MAsmJSLoadHeap(uint32_t memoryBaseIndex, Scalar::Type accessType)
   1028      : MVariadicInstruction(classOpcode),
   1029        MAsmJSMemoryAccess(accessType),
   1030        memoryBaseIndex_(memoryBaseIndex) {
   1031    setResultType(ScalarTypeToMIRType(accessType));
   1032  }
   1033 
   1034 public:
   1035  INSTRUCTION_HEADER(AsmJSLoadHeap)
   1036  NAMED_OPERANDS((0, base), (1, boundsCheckLimit))
   1037 
   1038  static MAsmJSLoadHeap* New(TempAllocator& alloc, MDefinition* memoryBase,
   1039                             MDefinition* base, MDefinition* boundsCheckLimit,
   1040                             Scalar::Type accessType) {
   1041    uint32_t nextIndex = 2;
   1042    uint32_t memoryBaseIndex = memoryBase ? nextIndex++ : UINT32_MAX;
   1043 
   1044    MAsmJSLoadHeap* load =
   1045        new (alloc) MAsmJSLoadHeap(memoryBaseIndex, accessType);
   1046    if (!load->init(alloc, nextIndex)) {
   1047      return nullptr;
   1048    }
   1049 
   1050    load->initOperand(0, base);
   1051    load->initOperand(1, boundsCheckLimit);
   1052    if (memoryBase) {
   1053      load->initOperand(memoryBaseIndex, memoryBase);
   1054    }
   1055 
   1056    return load;
   1057  }
   1058 
   1059  bool hasMemoryBase() const { return memoryBaseIndex_ != UINT32_MAX; }
   1060  MDefinition* memoryBase() const {
   1061    MOZ_ASSERT(hasMemoryBase());
   1062    return getOperand(memoryBaseIndex_);
   1063  }
   1064 
   1065  bool congruentTo(const MDefinition* ins) const override;
   1066  AliasSet getAliasSet() const override {
   1067    return AliasSet::Load(AliasSet::WasmHeap);
   1068  }
   1069  AliasType mightAlias(const MDefinition* def) const override;
   1070 };
   1071 
   1072 class MAsmJSStoreHeap
   1073    : public MVariadicInstruction,  // 2 plus optional memoryBase and
   1074                                    // boundsCheckLimit
   1075      public MAsmJSMemoryAccess,
   1076      public NoTypePolicy::Data {
   1077  uint32_t memoryBaseIndex_;
   1078 
   1079  explicit MAsmJSStoreHeap(uint32_t memoryBaseIndex, Scalar::Type accessType)
   1080      : MVariadicInstruction(classOpcode),
   1081        MAsmJSMemoryAccess(accessType),
   1082        memoryBaseIndex_(memoryBaseIndex) {}
   1083 
   1084 public:
   1085  INSTRUCTION_HEADER(AsmJSStoreHeap)
   1086  NAMED_OPERANDS((0, base), (1, value), (2, boundsCheckLimit))
   1087 
   1088  static MAsmJSStoreHeap* New(TempAllocator& alloc, MDefinition* memoryBase,
   1089                              MDefinition* base, MDefinition* boundsCheckLimit,
   1090                              Scalar::Type accessType, MDefinition* v) {
   1091    uint32_t nextIndex = 3;
   1092    uint32_t memoryBaseIndex = memoryBase ? nextIndex++ : UINT32_MAX;
   1093 
   1094    MAsmJSStoreHeap* store =
   1095        new (alloc) MAsmJSStoreHeap(memoryBaseIndex, accessType);
   1096    if (!store->init(alloc, nextIndex)) {
   1097      return nullptr;
   1098    }
   1099 
   1100    store->initOperand(0, base);
   1101    store->initOperand(1, v);
   1102    store->initOperand(2, boundsCheckLimit);
   1103    if (memoryBase) {
   1104      store->initOperand(memoryBaseIndex, memoryBase);
   1105    }
   1106 
   1107    return store;
   1108  }
   1109 
   1110  bool hasMemoryBase() const { return memoryBaseIndex_ != UINT32_MAX; }
   1111  MDefinition* memoryBase() const {
   1112    MOZ_ASSERT(hasMemoryBase());
   1113    return getOperand(memoryBaseIndex_);
   1114  }
   1115 
   1116  AliasSet getAliasSet() const override {
   1117    return AliasSet::Store(AliasSet::WasmHeap);
   1118  }
   1119 };
   1120 
   1121 class MWasmCompareExchangeHeap : public MVariadicInstruction,
   1122                                 public NoTypePolicy::Data {
   1123  wasm::MemoryAccessDesc access_;
   1124  wasm::BytecodeOffset bytecodeOffset_;
   1125 
   1126  explicit MWasmCompareExchangeHeap(const wasm::MemoryAccessDesc& access,
   1127                                    wasm::BytecodeOffset bytecodeOffset)
   1128      : MVariadicInstruction(classOpcode),
   1129        access_(access),
   1130        bytecodeOffset_(bytecodeOffset) {
   1131    setGuard();  // Not removable
   1132    setResultType(ScalarTypeToMIRType(access.type()));
   1133  }
   1134 
   1135 public:
   1136  INSTRUCTION_HEADER(WasmCompareExchangeHeap)
   1137  NAMED_OPERANDS((0, base), (1, oldValue), (2, newValue), (3, instance),
   1138                 (4, memoryBase))
   1139 
   1140  static MWasmCompareExchangeHeap* New(TempAllocator& alloc,
   1141                                       wasm::BytecodeOffset bytecodeOffset,
   1142                                       MDefinition* memoryBase,
   1143                                       MDefinition* base,
   1144                                       const wasm::MemoryAccessDesc& access,
   1145                                       MDefinition* oldv, MDefinition* newv,
   1146                                       MDefinition* instance) {
   1147    MWasmCompareExchangeHeap* cas =
   1148        new (alloc) MWasmCompareExchangeHeap(access, bytecodeOffset);
   1149    if (!cas->init(alloc, 4 + !!memoryBase)) {
   1150      return nullptr;
   1151    }
   1152    cas->initOperand(0, base);
   1153    cas->initOperand(1, oldv);
   1154    cas->initOperand(2, newv);
   1155    cas->initOperand(3, instance);
   1156    if (memoryBase) {
   1157      cas->initOperand(4, memoryBase);
   1158    }
   1159    return cas;
   1160  }
   1161 
   1162  const wasm::MemoryAccessDesc& access() const { return access_; }
   1163  wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
   1164 
   1165  AliasSet getAliasSet() const override {
   1166    return AliasSet::Store(AliasSet::WasmHeap);
   1167  }
   1168 
   1169  bool hasMemoryBase() const { return numOperands() > 4; }
   1170 };
   1171 
   1172 class MWasmAtomicExchangeHeap : public MVariadicInstruction,
   1173                                public NoTypePolicy::Data {
   1174  wasm::MemoryAccessDesc access_;
   1175  wasm::BytecodeOffset bytecodeOffset_;
   1176 
   1177  explicit MWasmAtomicExchangeHeap(const wasm::MemoryAccessDesc& access,
   1178                                   wasm::BytecodeOffset bytecodeOffset)
   1179      : MVariadicInstruction(classOpcode),
   1180        access_(access),
   1181        bytecodeOffset_(bytecodeOffset) {
   1182    setGuard();  // Not removable
   1183    setResultType(ScalarTypeToMIRType(access.type()));
   1184  }
   1185 
   1186 public:
   1187  INSTRUCTION_HEADER(WasmAtomicExchangeHeap)
   1188  NAMED_OPERANDS((0, base), (1, value), (2, instance), (3, memoryBase))
   1189 
   1190  static MWasmAtomicExchangeHeap* New(TempAllocator& alloc,
   1191                                      wasm::BytecodeOffset bytecodeOffset,
   1192                                      MDefinition* memoryBase,
   1193                                      MDefinition* base,
   1194                                      const wasm::MemoryAccessDesc& access,
   1195                                      MDefinition* value,
   1196                                      MDefinition* instance) {
   1197    MWasmAtomicExchangeHeap* xchg =
   1198        new (alloc) MWasmAtomicExchangeHeap(access, bytecodeOffset);
   1199    if (!xchg->init(alloc, 3 + !!memoryBase)) {
   1200      return nullptr;
   1201    }
   1202 
   1203    xchg->initOperand(0, base);
   1204    xchg->initOperand(1, value);
   1205    xchg->initOperand(2, instance);
   1206    if (memoryBase) {
   1207      xchg->initOperand(3, memoryBase);
   1208    }
   1209 
   1210    return xchg;
   1211  }
   1212 
   1213  const wasm::MemoryAccessDesc& access() const { return access_; }
   1214  wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
   1215 
   1216  AliasSet getAliasSet() const override {
   1217    return AliasSet::Store(AliasSet::WasmHeap);
   1218  }
   1219 
   1220  bool hasMemoryBase() const { return numOperands() > 3; }
   1221 };
   1222 
   1223 class MWasmAtomicBinopHeap : public MVariadicInstruction,
   1224                             public NoTypePolicy::Data {
   1225  AtomicOp op_;
   1226  wasm::MemoryAccessDesc access_;
   1227  wasm::BytecodeOffset bytecodeOffset_;
   1228 
   1229  explicit MWasmAtomicBinopHeap(AtomicOp op,
   1230                                const wasm::MemoryAccessDesc& access,
   1231                                wasm::BytecodeOffset bytecodeOffset)
   1232      : MVariadicInstruction(classOpcode),
   1233        op_(op),
   1234        access_(access),
   1235        bytecodeOffset_(bytecodeOffset) {
   1236    setGuard();  // Not removable
   1237    setResultType(ScalarTypeToMIRType(access.type()));
   1238  }
   1239 
   1240 public:
   1241  INSTRUCTION_HEADER(WasmAtomicBinopHeap)
   1242  NAMED_OPERANDS((0, base), (1, value), (2, instance), (3, memoryBase))
   1243 
   1244  static MWasmAtomicBinopHeap* New(TempAllocator& alloc,
   1245                                   wasm::BytecodeOffset bytecodeOffset,
   1246                                   AtomicOp op, MDefinition* memoryBase,
   1247                                   MDefinition* base,
   1248                                   const wasm::MemoryAccessDesc& access,
   1249                                   MDefinition* v, MDefinition* instance) {
   1250    MWasmAtomicBinopHeap* binop =
   1251        new (alloc) MWasmAtomicBinopHeap(op, access, bytecodeOffset);
   1252    if (!binop->init(alloc, 3 + !!memoryBase)) {
   1253      return nullptr;
   1254    }
   1255 
   1256    binop->initOperand(0, base);
   1257    binop->initOperand(1, v);
   1258    binop->initOperand(2, instance);
   1259    if (memoryBase) {
   1260      binop->initOperand(3, memoryBase);
   1261    }
   1262 
   1263    return binop;
   1264  }
   1265 
   1266  AtomicOp operation() const { return op_; }
   1267  const wasm::MemoryAccessDesc& access() const { return access_; }
   1268  wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
   1269 
   1270  AliasSet getAliasSet() const override {
   1271    return AliasSet::Store(AliasSet::WasmHeap);
   1272  }
   1273 
   1274  bool hasMemoryBase() const { return numOperands() > 3; }
   1275 };
   1276 
   1277 class MWasmLoadInstanceDataField : public MUnaryInstruction,
   1278                                   public NoTypePolicy::Data {
   1279  unsigned instanceDataOffset_;
   1280  bool isConstant_;
   1281 
   1282  MWasmLoadInstanceDataField(
   1283      MIRType type, unsigned instanceDataOffset, bool isConstant,
   1284      MDefinition* instance,
   1285      wasm::MaybeRefType maybeRefType = wasm::MaybeRefType())
   1286      : MUnaryInstruction(classOpcode, instance),
   1287        instanceDataOffset_(instanceDataOffset),
   1288        isConstant_(isConstant) {
   1289    MOZ_ASSERT(IsNumberType(type) || type == MIRType::Simd128 ||
   1290               type == MIRType::Pointer || type == MIRType::WasmAnyRef);
   1291    setResultType(type);
   1292    setMovable();
   1293    initWasmRefType(maybeRefType);
   1294  }
   1295 
   1296 public:
   1297  INSTRUCTION_HEADER(WasmLoadInstanceDataField)
   1298  TRIVIAL_NEW_WRAPPERS
   1299  NAMED_OPERANDS((0, instance))
   1300 
   1301  unsigned instanceDataOffset() const { return instanceDataOffset_; }
   1302 
   1303  HashNumber valueHash() const override;
   1304  bool congruentTo(const MDefinition* ins) const override;
   1305  MDefinition* foldsTo(TempAllocator& alloc) override;
   1306 
   1307  AliasSet getAliasSet() const override {
   1308    return isConstant_ ? AliasSet::None()
   1309                       : AliasSet::Load(AliasSet::WasmInstanceData);
   1310  }
   1311 
   1312  AliasType mightAlias(const MDefinition* def) const override;
   1313 
   1314 #ifdef JS_JITSPEW
   1315  void getExtras(ExtrasCollector* extras) const override {
   1316    char buf[96];
   1317    SprintfLiteral(buf, "(offs=%lld, isConst=%s)",
   1318                   (long long int)instanceDataOffset_,
   1319                   isConstant_ ? "true" : "false");
   1320    extras->add(buf);
   1321  }
   1322 #endif
   1323 
   1324  ALLOW_CLONE(MWasmLoadInstanceDataField)
   1325 };
   1326 
   1327 class MWasmLoadGlobalCell : public MUnaryInstruction,
   1328                            public NoTypePolicy::Data {
   1329  MWasmLoadGlobalCell(MIRType type, MDefinition* cellPtr,
   1330                      wasm::ValType globalType)
   1331      : MUnaryInstruction(classOpcode, cellPtr) {
   1332    setResultType(type);
   1333    setMovable();
   1334    initWasmRefType(globalType.toMaybeRefType());
   1335  }
   1336 
   1337 public:
   1338  INSTRUCTION_HEADER(WasmLoadGlobalCell)
   1339  TRIVIAL_NEW_WRAPPERS
   1340  NAMED_OPERANDS((0, cellPtr))
   1341 
   1342  // The default valueHash is good enough, because there are no non-operand
   1343  // fields.
   1344  bool congruentTo(const MDefinition* ins) const override;
   1345 
   1346  AliasSet getAliasSet() const override {
   1347    return AliasSet::Load(AliasSet::WasmGlobalCell);
   1348  }
   1349 
   1350  AliasType mightAlias(const MDefinition* def) const override;
   1351 
   1352  ALLOW_CLONE(MWasmLoadGlobalCell)
   1353 };
   1354 
   1355 class MWasmLoadTableElement : public MBinaryInstruction,
   1356                              public NoTypePolicy::Data {
   1357  MWasmLoadTableElement(MDefinition* elements, MDefinition* index,
   1358                        wasm::RefType refType)
   1359      : MBinaryInstruction(classOpcode, elements, index) {
   1360    setResultType(MIRType::WasmAnyRef);
   1361    setMovable();
   1362    initWasmRefType(wasm::MaybeRefType(refType));
   1363  }
   1364 
   1365 public:
   1366  INSTRUCTION_HEADER(WasmLoadTableElement)
   1367  TRIVIAL_NEW_WRAPPERS
   1368  NAMED_OPERANDS((0, elements))
   1369  NAMED_OPERANDS((1, index))
   1370 
   1371  AliasSet getAliasSet() const override {
   1372    return AliasSet::Load(AliasSet::WasmTableElement);
   1373  }
   1374 };
   1375 
   1376 class MWasmStoreInstanceDataField : public MBinaryInstruction,
   1377                                    public NoTypePolicy::Data {
   1378  MWasmStoreInstanceDataField(unsigned instanceDataOffset, MDefinition* value,
   1379                              MDefinition* instance)
   1380      : MBinaryInstruction(classOpcode, value, instance),
   1381        instanceDataOffset_(instanceDataOffset) {}
   1382 
   1383  unsigned instanceDataOffset_;
   1384 
   1385 public:
   1386  INSTRUCTION_HEADER(WasmStoreInstanceDataField)
   1387  TRIVIAL_NEW_WRAPPERS
   1388  NAMED_OPERANDS((0, value), (1, instance))
   1389 
   1390  unsigned instanceDataOffset() const { return instanceDataOffset_; }
   1391 
   1392  AliasSet getAliasSet() const override {
   1393    return AliasSet::Store(AliasSet::WasmInstanceData);
   1394  }
   1395 };
   1396 
   1397 class MWasmStoreGlobalCell : public MBinaryInstruction,
   1398                             public NoTypePolicy::Data {
   1399  MWasmStoreGlobalCell(MDefinition* value, MDefinition* cellPtr)
   1400      : MBinaryInstruction(classOpcode, value, cellPtr) {}
   1401 
   1402 public:
   1403  INSTRUCTION_HEADER(WasmStoreGlobalCell)
   1404  TRIVIAL_NEW_WRAPPERS
   1405  NAMED_OPERANDS((0, value), (1, cellPtr))
   1406 
   1407  AliasSet getAliasSet() const override {
   1408    return AliasSet::Store(AliasSet::WasmGlobalCell);
   1409  }
   1410 
   1411  ALLOW_CLONE(MWasmStoreGlobalCell)
   1412 };
   1413 
   1414 class MWasmStoreStackResult : public MBinaryInstruction,
   1415                              public NoTypePolicy::Data {
   1416  MWasmStoreStackResult(MDefinition* stackResultArea, uint32_t offset,
   1417                        MDefinition* value)
   1418      : MBinaryInstruction(classOpcode, stackResultArea, value),
   1419        offset_(offset) {}
   1420 
   1421  uint32_t offset_;
   1422 
   1423 public:
   1424  INSTRUCTION_HEADER(WasmStoreStackResult)
   1425  TRIVIAL_NEW_WRAPPERS
   1426  NAMED_OPERANDS((0, stackResultArea), (1, value))
   1427 
   1428  uint32_t offset() const { return offset_; }
   1429 
   1430  AliasSet getAliasSet() const override {
   1431    return AliasSet::Store(AliasSet::WasmStackResult);
   1432  }
   1433 };
   1434 
   1435 // Represents a known-good derived pointer into an object or memory region (in
   1436 // the most general sense) that will not move while the derived pointer is live.
   1437 // The `offset` *must* be a valid offset into the object represented by `base`;
   1438 // hence overflow in the address calculation will never be an issue.  `offset`
   1439 // must be representable as a 31-bit unsigned integer.
   1440 //
   1441 // DO NOT use this with a base value of any JS-heap-resident object type.
   1442 // Such a value would need to be adjusted during GC, yet we have no mechanism
   1443 // to do that.  See bug 1810090.
   1444 
   1445 class MWasmDerivedPointer : public MUnaryInstruction,
   1446                            public NoTypePolicy::Data {
   1447  MWasmDerivedPointer(MDefinition* base, size_t offset)
   1448      : MUnaryInstruction(classOpcode, base), offset_(uint32_t(offset)) {
   1449    MOZ_ASSERT(offset <= INT32_MAX);
   1450    // Do not change this to allow `base` to be a GC-heap allocated type.
   1451    MOZ_ASSERT(base->type() == MIRType::Pointer ||
   1452               base->type() == TargetWordMIRType());
   1453    setResultType(MIRType::Pointer);
   1454    setMovable();
   1455  }
   1456 
   1457  uint32_t offset_;
   1458 
   1459 public:
   1460  INSTRUCTION_HEADER(WasmDerivedPointer)
   1461  TRIVIAL_NEW_WRAPPERS
   1462  NAMED_OPERANDS((0, base))
   1463 
   1464  uint32_t offset() const { return offset_; }
   1465 
   1466  AliasSet getAliasSet() const override { return AliasSet::None(); }
   1467 
   1468  HashNumber valueHash() const override {
   1469    HashNumber hash = MUnaryInstruction::valueHash();
   1470    hash = addU32ToHash(hash, offset());
   1471    return hash;
   1472  }
   1473  bool congruentTo(const MDefinition* ins) const override {
   1474    return congruentIfOperandsEqual(ins) &&
   1475           ins->toWasmDerivedPointer()->offset() == offset();
   1476  }
   1477 
   1478 #ifdef JS_JITSPEW
   1479  void getExtras(ExtrasCollector* extras) const override {
   1480    char buf[64];
   1481    SprintfLiteral(buf, "(offs=%lld)", (long long int)offset_);
   1482    extras->add(buf);
   1483  }
   1484 #endif
   1485 
   1486  ALLOW_CLONE(MWasmDerivedPointer)
   1487 };
   1488 
   1489 // As with MWasmDerivedPointer, DO NOT use this with a base value of any
   1490 // JS-heap-resident object type.
   1491 class MWasmDerivedIndexPointer : public MBinaryInstruction,
   1492                                 public NoTypePolicy::Data {
   1493  MWasmDerivedIndexPointer(MDefinition* base, MDefinition* index, Scale scale)
   1494      : MBinaryInstruction(classOpcode, base, index), scale_(scale) {
   1495    // Do not change this to allow `base` to be a GC-heap allocated type.
   1496    MOZ_ASSERT(base->type() == MIRType::Pointer);
   1497    setResultType(MIRType::Pointer);
   1498    setMovable();
   1499  }
   1500 
   1501  Scale scale_;
   1502 
   1503 public:
   1504  INSTRUCTION_HEADER(WasmDerivedIndexPointer)
   1505  TRIVIAL_NEW_WRAPPERS
   1506  NAMED_OPERANDS((0, base))
   1507  NAMED_OPERANDS((1, index))
   1508 
   1509  Scale scale() const { return scale_; }
   1510 
   1511  AliasSet getAliasSet() const override { return AliasSet::None(); }
   1512 
   1513  bool congruentTo(const MDefinition* ins) const override {
   1514    return congruentIfOperandsEqual(ins) &&
   1515           ins->toWasmDerivedIndexPointer()->scale() == scale();
   1516  }
   1517 
   1518  ALLOW_CLONE(MWasmDerivedIndexPointer)
   1519 };
   1520 
   1521 // Whether to perform a pre-write barrier for a wasm store reference.
   1522 enum class WasmPreBarrierKind : uint8_t { None, Normal };
   1523 
   1524 // Whether to perform a post-write barrier for a wasm store reference.
   1525 enum class WasmPostBarrierKind : uint8_t { None, Edge, WholeCell };
   1526 
   1527 // Stores a reference to an address. This performs a pre-barrier on the address,
   1528 // but not a post-barrier. A post-barrier must be performed separately, if it's
   1529 // required.  The accessed location is `valueBase + valueOffset`.  The latter
   1530 // must be be representable as a 31-bit unsigned integer.
   1531 
   1532 class MWasmStoreRef : public MAryInstruction<3>, public NoTypePolicy::Data {
   1533  uint32_t offset_;
   1534  AliasSet::Flag aliasSet_;
   1535  WasmPreBarrierKind preBarrierKind_;
   1536 
   1537  MWasmStoreRef(MDefinition* instance, MDefinition* valueBase,
   1538                size_t valueOffset, MDefinition* value, AliasSet::Flag aliasSet,
   1539                WasmPreBarrierKind preBarrierKind)
   1540      : MAryInstruction<3>(classOpcode),
   1541        offset_(uint32_t(valueOffset)),
   1542        aliasSet_(aliasSet),
   1543        preBarrierKind_(preBarrierKind) {
   1544    MOZ_ASSERT(valueOffset <= INT32_MAX);
   1545    MOZ_ASSERT(valueBase->type() == MIRType::Pointer ||
   1546               valueBase->type() == MIRType::StackResults);
   1547    MOZ_ASSERT(value->type() == MIRType::WasmAnyRef);
   1548    initOperand(0, instance);
   1549    initOperand(1, valueBase);
   1550    initOperand(2, value);
   1551  }
   1552 
   1553 public:
   1554  INSTRUCTION_HEADER(WasmStoreRef)
   1555  TRIVIAL_NEW_WRAPPERS
   1556  NAMED_OPERANDS((0, instance), (1, valueBase), (2, value))
   1557 
   1558  uint32_t offset() const { return offset_; }
   1559  AliasSet getAliasSet() const override { return AliasSet::Store(aliasSet_); }
   1560  WasmPreBarrierKind preBarrierKind() const { return preBarrierKind_; }
   1561 
   1562 #ifdef JS_JITSPEW
   1563  void getExtras(ExtrasCollector* extras) const override {
   1564    char buf[64];
   1565    SprintfLiteral(buf, "(offs=%lld)", (long long int)offset_);
   1566    extras->add(buf);
   1567  }
   1568 #endif
   1569 };
   1570 
   1571 // Given a value being written to another object, update the generational store
   1572 // buffer if the value is in the nursery and object is in the tenured heap.
   1573 class MWasmPostWriteBarrierWholeCell : public MTernaryInstruction,
   1574                                       public NoTypePolicy::Data {
   1575  MWasmPostWriteBarrierWholeCell(MDefinition* instance, MDefinition* object,
   1576                                 MDefinition* value)
   1577      : MTernaryInstruction(classOpcode, instance, object, value) {
   1578    MOZ_ASSERT(object->type() == MIRType::WasmAnyRef);
   1579    MOZ_ASSERT(value->type() == MIRType::WasmAnyRef);
   1580    setGuard();
   1581  }
   1582 
   1583 public:
   1584  INSTRUCTION_HEADER(WasmPostWriteBarrierWholeCell)
   1585  TRIVIAL_NEW_WRAPPERS
   1586  NAMED_OPERANDS((0, instance), (1, object), (2, value))
   1587 
   1588  AliasSet getAliasSet() const override { return AliasSet::None(); }
   1589 
   1590  ALLOW_CLONE(MWasmPostWriteBarrierWholeCell)
   1591 };
   1592 
   1593 // Given a value being written to another object, update the generational store
   1594 // buffer if the value is in the nursery and object is in the tenured heap.
   1595 class MWasmPostWriteBarrierEdgeAtIndex : public MAryInstruction<5>,
   1596                                         public NoTypePolicy::Data {
   1597  uint32_t elemSize_;
   1598 
   1599  MWasmPostWriteBarrierEdgeAtIndex(MDefinition* instance, MDefinition* object,
   1600                                   MDefinition* valueBase, MDefinition* index,
   1601                                   uint32_t scale, MDefinition* value)
   1602      : MAryInstruction<5>(classOpcode), elemSize_(scale) {
   1603    MOZ_ASSERT(object->type() == MIRType::WasmAnyRef);
   1604    MOZ_ASSERT(value->type() == MIRType::WasmAnyRef);
   1605    initOperand(0, instance);
   1606    initOperand(1, object);
   1607    initOperand(2, valueBase);
   1608    initOperand(3, index);
   1609    initOperand(4, value);
   1610    setGuard();
   1611  }
   1612 
   1613 public:
   1614  INSTRUCTION_HEADER(WasmPostWriteBarrierEdgeAtIndex)
   1615  TRIVIAL_NEW_WRAPPERS
   1616  NAMED_OPERANDS((0, instance), (1, object), (2, valueBase), (3, index),
   1617                 (4, value))
   1618 
   1619  AliasSet getAliasSet() const override { return AliasSet::None(); }
   1620  uint32_t elemSize() const { return elemSize_; }
   1621 
   1622  ALLOW_CLONE(MWasmPostWriteBarrierEdgeAtIndex)
   1623 };
   1624 
   1625 class MWasmParameter : public MNullaryInstruction {
   1626  ABIArg abi_;
   1627 
   1628  MWasmParameter(ABIArg abi, MIRType mirType,
   1629                 wasm::MaybeRefType refType = wasm::MaybeRefType())
   1630      : MNullaryInstruction(classOpcode), abi_(abi) {
   1631    setResultType(mirType);
   1632    initWasmRefType(refType);
   1633  }
   1634 
   1635 public:
   1636  INSTRUCTION_HEADER(WasmParameter)
   1637  TRIVIAL_NEW_WRAPPERS
   1638  // MWasmParameter has no getAliasSet routine.  Hence it acquires the default
   1639  // aliases-everything setting.  This doesn't matter in practice because these
   1640  // nodes only appear at the start of the function's entry block, and in any
   1641  // case they are not marked as movable.
   1642 
   1643  ABIArg abi() const { return abi_; }
   1644 };
   1645 
   1646 class MWasmReturn : public MAryControlInstruction<2, 0>,
   1647                    public NoTypePolicy::Data {
   1648  MWasmReturn(MDefinition* ins, MDefinition* instance)
   1649      : MAryControlInstruction(classOpcode) {
   1650    initOperand(0, ins);
   1651    initOperand(1, instance);
   1652  }
   1653 
   1654 public:
   1655  INSTRUCTION_HEADER(WasmReturn)
   1656  TRIVIAL_NEW_WRAPPERS
   1657 
   1658  AliasSet getAliasSet() const override { return AliasSet::None(); }
   1659 };
   1660 
   1661 class MWasmReturnVoid : public MAryControlInstruction<1, 0>,
   1662                        public NoTypePolicy::Data {
   1663  explicit MWasmReturnVoid(MDefinition* instance)
   1664      : MAryControlInstruction(classOpcode) {
   1665    initOperand(0, instance);
   1666  }
   1667 
   1668 public:
   1669  INSTRUCTION_HEADER(WasmReturnVoid)
   1670  TRIVIAL_NEW_WRAPPERS
   1671 
   1672  AliasSet getAliasSet() const override { return AliasSet::None(); }
   1673 };
   1674 
   1675 class MWasmStackArg : public MUnaryInstruction, public NoTypePolicy::Data {
   1676  MWasmStackArg(uint32_t spOffset, MDefinition* ins)
   1677      : MUnaryInstruction(classOpcode, ins), spOffset_(spOffset) {}
   1678 
   1679  uint32_t spOffset_;
   1680 
   1681 public:
   1682  INSTRUCTION_HEADER(WasmStackArg)
   1683  TRIVIAL_NEW_WRAPPERS
   1684  NAMED_OPERANDS((0, arg))
   1685 
   1686  uint32_t spOffset() const { return spOffset_; }
   1687  void incrementOffset(uint32_t inc) { spOffset_ += inc; }
   1688  AliasSet getAliasSet() const override {
   1689    return AliasSet::Store(AliasSet::Flag::Any);
   1690  }
   1691 
   1692  ALLOW_CLONE(MWasmStackArg)
   1693 };
   1694 
   1695 template <typename Location>
   1696 class MWasmResultBase : public MNullaryInstruction {
   1697  Location loc_;
   1698 
   1699 protected:
   1700  MWasmResultBase(Opcode op, MIRType type, Location loc)
   1701      : MNullaryInstruction(op), loc_(loc) {
   1702    setResultType(type);
   1703    setCallResultCapture();
   1704  }
   1705 
   1706 public:
   1707  Location loc() { return loc_; }
   1708 };
   1709 
   1710 class MWasmRegisterResult : public MWasmResultBase<Register> {
   1711  MWasmRegisterResult(MIRType type, Register reg,
   1712                      wasm::MaybeRefType maybeRefType = wasm::MaybeRefType())
   1713      : MWasmResultBase(classOpcode, type, reg) {
   1714    initWasmRefType(maybeRefType);
   1715  }
   1716 
   1717 public:
   1718  INSTRUCTION_HEADER(WasmRegisterResult)
   1719  TRIVIAL_NEW_WRAPPERS
   1720  AliasSet getAliasSet() const override { return AliasSet::None(); }
   1721 };
   1722 
   1723 class MWasmFloatRegisterResult : public MWasmResultBase<FloatRegister> {
   1724  MWasmFloatRegisterResult(MIRType type, FloatRegister reg)
   1725      : MWasmResultBase(classOpcode, type, reg) {}
   1726 
   1727 public:
   1728  INSTRUCTION_HEADER(WasmFloatRegisterResult)
   1729  TRIVIAL_NEW_WRAPPERS
   1730  AliasSet getAliasSet() const override { return AliasSet::None(); }
   1731 };
   1732 
   1733 class MWasmSystemFloatRegisterResult : public MWasmResultBase<FloatRegister> {
   1734  MWasmSystemFloatRegisterResult(MIRType type, FloatRegister reg, bool hardFP)
   1735      : MWasmResultBase(classOpcode, type, reg), hardFP_(hardFP) {}
   1736 
   1737  bool hardFP_;
   1738 
   1739 public:
   1740  INSTRUCTION_HEADER(WasmSystemFloatRegisterResult)
   1741  TRIVIAL_NEW_WRAPPERS
   1742  AliasSet getAliasSet() const override { return AliasSet::None(); }
   1743 
   1744  bool hardFP() const { return hardFP_; }
   1745 };
   1746 
   1747 class MWasmRegister64Result : public MWasmResultBase<Register64> {
   1748  explicit MWasmRegister64Result(Register64 reg)
   1749      : MWasmResultBase(classOpcode, MIRType::Int64, reg) {}
   1750 
   1751 public:
   1752  INSTRUCTION_HEADER(WasmRegister64Result)
   1753  TRIVIAL_NEW_WRAPPERS
   1754  AliasSet getAliasSet() const override { return AliasSet::None(); }
   1755 };
   1756 
   1757 class MWasmStackResultArea : public MNullaryInstruction {
   1758 public:
   1759  class StackResult {
   1760    // Offset in bytes from lowest address of stack result area.
   1761    uint32_t offset_;
   1762    MIRType type_;
   1763 
   1764   public:
   1765    StackResult() : type_(MIRType::Undefined) {}
   1766    StackResult(uint32_t offset, MIRType type) : offset_(offset), type_(type) {}
   1767 
   1768    bool initialized() const { return type_ != MIRType::Undefined; }
   1769    uint32_t offset() const {
   1770      MOZ_ASSERT(initialized());
   1771      return offset_;
   1772    }
   1773    MIRType type() const {
   1774      MOZ_ASSERT(initialized());
   1775      return type_;
   1776    }
   1777    uint32_t endOffset() const {
   1778      return offset() + wasm::MIRTypeToABIResultSize(type());
   1779    }
   1780  };
   1781 
   1782 private:
   1783  FixedList<StackResult> results_;
   1784  uint32_t base_;
   1785 
   1786  explicit MWasmStackResultArea()
   1787      : MNullaryInstruction(classOpcode), base_(UINT32_MAX) {
   1788    setResultType(MIRType::StackResults);
   1789  }
   1790 
   1791  void assertInitialized() const {
   1792    MOZ_ASSERT(results_.length() != 0);
   1793 #ifdef DEBUG
   1794    for (size_t i = 0; i < results_.length(); i++) {
   1795      MOZ_ASSERT(results_[i].initialized());
   1796    }
   1797 #endif
   1798  }
   1799 
   1800  bool baseInitialized() const { return base_ != UINT32_MAX; }
   1801 
   1802 public:
   1803  INSTRUCTION_HEADER(WasmStackResultArea)
   1804  TRIVIAL_NEW_WRAPPERS
   1805 
   1806  [[nodiscard]] bool init(TempAllocator& alloc, size_t stackResultCount) {
   1807    MOZ_ASSERT(results_.length() == 0);
   1808    MOZ_ASSERT(stackResultCount > 0);
   1809    if (!results_.init(alloc, stackResultCount)) {
   1810      return false;
   1811    }
   1812    for (size_t n = 0; n < stackResultCount; n++) {
   1813      results_[n] = StackResult();
   1814    }
   1815    return true;
   1816  }
   1817 
   1818  size_t resultCount() const { return results_.length(); }
   1819  const StackResult& result(size_t n) const {
   1820    MOZ_ASSERT(results_[n].initialized());
   1821    return results_[n];
   1822  }
   1823  void initResult(size_t n, const StackResult& loc) {
   1824    MOZ_ASSERT(!results_[n].initialized());
   1825    MOZ_ASSERT((n == 0) == (loc.offset() == 0));
   1826    MOZ_ASSERT_IF(n > 0, loc.offset() >= result(n - 1).endOffset());
   1827    results_[n] = loc;
   1828  }
   1829 
   1830  uint32_t byteSize() const {
   1831    assertInitialized();
   1832    return result(resultCount() - 1).endOffset();
   1833  }
   1834 
   1835  // Stack index indicating base of stack area.
   1836  uint32_t base() const {
   1837    MOZ_ASSERT(baseInitialized());
   1838    return base_;
   1839  }
   1840  void setBase(uint32_t base) {
   1841    MOZ_ASSERT(!baseInitialized());
   1842    base_ = base;
   1843    MOZ_ASSERT(baseInitialized());
   1844  }
   1845 };
   1846 
   1847 class MWasmStackResult : public MUnaryInstruction, public NoTypePolicy::Data {
   1848  uint32_t resultIdx_;
   1849 
   1850  MWasmStackResult(MWasmStackResultArea* resultArea, size_t idx)
   1851      : MUnaryInstruction(classOpcode, resultArea), resultIdx_(idx) {
   1852    setResultType(result().type());
   1853    setCallResultCapture();
   1854  }
   1855 
   1856 public:
   1857  INSTRUCTION_HEADER(WasmStackResult)
   1858  TRIVIAL_NEW_WRAPPERS
   1859  NAMED_OPERANDS((0, resultArea))
   1860 
   1861  const MWasmStackResultArea::StackResult& result() const {
   1862    return resultArea()->toWasmStackResultArea()->result(resultIdx_);
   1863  }
   1864 };
   1865 
   1866 // Mixin class for wasm calls that may or may not be catchable.
   1867 class MWasmCallBase {
   1868 public:
   1869  struct Arg {
   1870    AnyRegister reg;
   1871    MDefinition* def;
   1872    Arg(AnyRegister reg, MDefinition* def) : reg(reg), def(def) {}
   1873  };
   1874  using Args = Vector<Arg, 8, SystemAllocPolicy>;
   1875 
   1876 protected:
   1877  wasm::CallSiteDesc desc_;
   1878  wasm::CalleeDesc callee_;
   1879  wasm::FailureMode builtinMethodFailureMode_;
   1880  wasm::Trap builtinMethodFailureTrap_;
   1881  FixedList<AnyRegister> argRegs_;
   1882  uint32_t stackArgAreaSizeUnaligned_;
   1883  ABIArg instanceArg_;
   1884  bool inTry_;
   1885  size_t tryNoteIndex_;
   1886 
   1887  MWasmCallBase(const wasm::CallSiteDesc& desc, const wasm::CalleeDesc& callee,
   1888                uint32_t stackArgAreaSizeUnaligned, bool inTry,
   1889                size_t tryNoteIndex)
   1890      : desc_(desc),
   1891        callee_(callee),
   1892        builtinMethodFailureMode_(wasm::FailureMode::Infallible),
   1893        stackArgAreaSizeUnaligned_(stackArgAreaSizeUnaligned),
   1894        inTry_(inTry),
   1895        tryNoteIndex_(tryNoteIndex) {}
   1896 
   1897  template <class MVariadicT>
   1898  [[nodiscard]] bool initWithArgs(TempAllocator& alloc, MVariadicT* ins,
   1899                                  const Args& args,
   1900                                  MDefinition* tableAddressOrRef) {
   1901    if (!argRegs_.init(alloc, args.length())) {
   1902      return false;
   1903    }
   1904    for (size_t i = 0; i < argRegs_.length(); i++) {
   1905      argRegs_[i] = args[i].reg;
   1906    }
   1907 
   1908    if (!ins->init(alloc, argRegs_.length() + (tableAddressOrRef ? 1 : 0))) {
   1909      return false;
   1910    }
   1911    // FixedList doesn't initialize its elements, so do an unchecked init.
   1912    for (size_t i = 0; i < argRegs_.length(); i++) {
   1913      ins->initOperand(i, args[i].def);
   1914    }
   1915    if (tableAddressOrRef) {
   1916      ins->initOperand(argRegs_.length(), tableAddressOrRef);
   1917    }
   1918    return true;
   1919  }
   1920 
   1921 public:
   1922  static bool IsWasmCall(MDefinition* def) {
   1923    return def->isWasmCallCatchable() || def->isWasmCallUncatchable() ||
   1924           def->isWasmReturnCall();
   1925  }
   1926 
   1927  size_t numArgs() const { return argRegs_.length(); }
   1928  AnyRegister registerForArg(size_t index) const {
   1929    MOZ_ASSERT(index < numArgs());
   1930    return argRegs_[index];
   1931  }
   1932  const wasm::CallSiteDesc& desc() const { return desc_; }
   1933  const wasm::CalleeDesc& callee() const { return callee_; }
   1934  wasm::FailureMode builtinMethodFailureMode() const {
   1935    MOZ_ASSERT(callee_.which() == wasm::CalleeDesc::BuiltinInstanceMethod);
   1936    return builtinMethodFailureMode_;
   1937  }
   1938  wasm::Trap builtinMethodFailureTrap() const {
   1939    MOZ_ASSERT(callee_.which() == wasm::CalleeDesc::BuiltinInstanceMethod);
   1940    return builtinMethodFailureTrap_;
   1941  }
   1942  uint32_t stackArgAreaSizeUnaligned() const {
   1943    return stackArgAreaSizeUnaligned_;
   1944  }
   1945 
   1946  const ABIArg& instanceArg() const { return instanceArg_; }
   1947 
   1948  bool inTry() const { return inTry_; }
   1949  size_t tryNoteIndex() const { return tryNoteIndex_; }
   1950 
   1951  static AliasSet wasmCallAliasSet() {
   1952    // This is ok because:
   1953    // - numElements is immutable
   1954    // - the GC will rewrite any array data pointers on move
   1955    AliasSet exclude = AliasSet(AliasSet::WasmArrayNumElements) |
   1956                       AliasSet(AliasSet::WasmArrayDataPointer);
   1957    return AliasSet::Store(AliasSet::Any) & ~exclude;
   1958  }
   1959 };
   1960 
   1961 // A wasm call that is catchable. This instruction is a control instruction,
   1962 // and terminates the block it is on. A normal return will proceed in a the
   1963 // fallthrough block. An exceptional return will unwind into the landing pad
   1964 // block for this call. The landing pad block must begin with an
   1965 // MWasmCallLandingPrePad.
   1966 class MWasmCallCatchable final : public MVariadicControlInstruction<2>,
   1967                                 public MWasmCallBase,
   1968                                 public NoTypePolicy::Data {
   1969  MWasmCallCatchable(const wasm::CallSiteDesc& desc,
   1970                     const wasm::CalleeDesc& callee,
   1971                     uint32_t stackArgAreaSizeUnaligned, size_t tryNoteIndex)
   1972      : MVariadicControlInstruction(classOpcode),
   1973        MWasmCallBase(desc, callee, stackArgAreaSizeUnaligned, true,
   1974                      tryNoteIndex) {}
   1975 
   1976 public:
   1977  INSTRUCTION_HEADER(WasmCallCatchable)
   1978 
   1979  static MWasmCallCatchable* New(
   1980      TempAllocator& alloc, const wasm::CallSiteDesc& desc,
   1981      const wasm::CalleeDesc& callee, const Args& args,
   1982      uint32_t stackArgAreaSizeUnaligned, uint32_t tryNoteIndex,
   1983      MBasicBlock* fallthroughBlock, MBasicBlock* prePadBlock,
   1984      MDefinition* tableAddressOrRef = nullptr);
   1985 
   1986  static MWasmCallCatchable* NewBuiltinInstanceMethodCall(
   1987      TempAllocator& alloc, const wasm::CallSiteDesc& desc,
   1988      const wasm::SymbolicAddress builtin, wasm::FailureMode failureMode,
   1989      wasm::Trap failureTrap, const ABIArg& instanceArg, const Args& args,
   1990      uint32_t stackArgAreaSizeUnaligned, uint32_t tryNoteIndex,
   1991      MBasicBlock* fallthroughBlock, MBasicBlock* prePadBlock);
   1992 
   1993  bool possiblyCalls() const override { return true; }
   1994  AliasSet getAliasSet() const override { return wasmCallAliasSet(); }
   1995 
   1996  static const size_t FallthroughBranchIndex = 0;
   1997  static const size_t PrePadBranchIndex = 1;
   1998 };
   1999 
   2000 // A wasm call that is not catchable. This instruction is not a control
   2001 // instruction, and therefore is not a block terminator.
   2002 class MWasmCallUncatchable final : public MVariadicInstruction,
   2003                                   public MWasmCallBase,
   2004                                   public NoTypePolicy::Data {
   2005  MWasmCallUncatchable(const wasm::CallSiteDesc& desc,
   2006                       const wasm::CalleeDesc& callee,
   2007                       uint32_t stackArgAreaSizeUnaligned)
   2008      : MVariadicInstruction(classOpcode),
   2009        MWasmCallBase(desc, callee, stackArgAreaSizeUnaligned, false, 0) {}
   2010 
   2011 public:
   2012  INSTRUCTION_HEADER(WasmCallUncatchable)
   2013 
   2014  static MWasmCallUncatchable* New(TempAllocator& alloc,
   2015                                   const wasm::CallSiteDesc& desc,
   2016                                   const wasm::CalleeDesc& callee,
   2017                                   const Args& args,
   2018                                   uint32_t stackArgAreaSizeUnaligned,
   2019                                   MDefinition* tableAddressOrRef = nullptr);
   2020 
   2021  static MWasmCallUncatchable* NewBuiltinInstanceMethodCall(
   2022      TempAllocator& alloc, const wasm::CallSiteDesc& desc,
   2023      const wasm::SymbolicAddress builtin, wasm::FailureMode failureMode,
   2024      wasm::Trap failureTrap, const ABIArg& instanceArg, const Args& args,
   2025      uint32_t stackArgAreaSizeUnaligned);
   2026 
   2027  bool possiblyCalls() const override { return true; }
   2028  AliasSet getAliasSet() const override { return wasmCallAliasSet(); }
   2029 };
   2030 
   2031 class MWasmReturnCall final : public MVariadicControlInstruction<0>,
   2032                              public MWasmCallBase,
   2033                              public NoTypePolicy::Data {
   2034  MWasmReturnCall(const wasm::CallSiteDesc& desc,
   2035                  const wasm::CalleeDesc& callee,
   2036                  uint32_t stackArgAreaSizeUnaligned)
   2037      : MVariadicControlInstruction(classOpcode),
   2038        MWasmCallBase(desc, callee, stackArgAreaSizeUnaligned, false, 0) {}
   2039 
   2040 public:
   2041  INSTRUCTION_HEADER(WasmReturnCall)
   2042 
   2043  static MWasmReturnCall* New(TempAllocator& alloc,
   2044                              const wasm::CallSiteDesc& desc,
   2045                              const wasm::CalleeDesc& callee, const Args& args,
   2046                              uint32_t stackArgAreaSizeUnaligned,
   2047                              MDefinition* tableAddressOrRef = nullptr);
   2048 
   2049  bool possiblyCalls() const override { return true; }
   2050 };
   2051 
   2052 // A marker instruction for a block which is the landing pad for a catchable
   2053 // wasm call. This instruction does not emit any code, only filling in
   2054 // metadata. This instruction must be the first instruction added to the
   2055 // landing pad block.
   2056 class MWasmCallLandingPrePad : public MNullaryInstruction {
   2057  // The block of the call that may unwind to this landing pad.
   2058  MBasicBlock* callBlock_;
   2059  // The index of the try note to initialize a landing pad for.
   2060  size_t tryNoteIndex_;
   2061 
   2062  explicit MWasmCallLandingPrePad(MBasicBlock* callBlock, size_t tryNoteIndex)
   2063      : MNullaryInstruction(classOpcode),
   2064        callBlock_(callBlock),
   2065        tryNoteIndex_(tryNoteIndex) {
   2066    setGuard();
   2067  }
   2068 
   2069 public:
   2070  INSTRUCTION_HEADER(WasmCallLandingPrePad)
   2071  TRIVIAL_NEW_WRAPPERS
   2072 
   2073  AliasSet getAliasSet() const override { return AliasSet::None(); }
   2074 
   2075  size_t tryNoteIndex() { return tryNoteIndex_; }
   2076  MBasicBlock* callBlock() { return callBlock_; }
   2077 };
   2078 
   2079 class MWasmSelect : public MTernaryInstruction, public NoTypePolicy::Data {
   2080  MWasmSelect(MDefinition* trueExpr, MDefinition* falseExpr,
   2081              MDefinition* condExpr)
   2082      : MTernaryInstruction(classOpcode, trueExpr, falseExpr, condExpr) {
   2083    MOZ_ASSERT(condExpr->type() == MIRType::Int32);
   2084    MOZ_ASSERT(trueExpr->type() == falseExpr->type());
   2085    setResultType(trueExpr->type());
   2086    setMovable();
   2087  }
   2088 
   2089 public:
   2090  INSTRUCTION_HEADER(WasmSelect)
   2091  TRIVIAL_NEW_WRAPPERS
   2092  NAMED_OPERANDS((0, trueExpr), (1, falseExpr), (2, condExpr))
   2093 
   2094  AliasSet getAliasSet() const override { return AliasSet::None(); }
   2095 
   2096  bool congruentTo(const MDefinition* ins) const override {
   2097    return congruentIfOperandsEqual(ins);
   2098  }
   2099 
   2100  wasm::MaybeRefType computeWasmRefType() const override {
   2101    return wasm::MaybeRefType::leastUpperBound(trueExpr()->wasmRefType(),
   2102                                               falseExpr()->wasmRefType());
   2103  }
   2104 
   2105  MDefinition* foldsTo(TempAllocator& alloc) override;
   2106 
   2107  ALLOW_CLONE(MWasmSelect)
   2108 };
   2109 
   2110 // Wasm SIMD.
   2111 //
   2112 // See comment in WasmIonCompile.cpp for a justification for these nodes.
   2113 
   2114 // (v128, v128, v128) -> v128 effect-free operation.
   2115 class MWasmTernarySimd128 : public MTernaryInstruction,
   2116                            public NoTypePolicy::Data {
   2117  wasm::SimdOp simdOp_;
   2118 
   2119  MWasmTernarySimd128(MDefinition* v0, MDefinition* v1, MDefinition* v2,
   2120                      wasm::SimdOp simdOp)
   2121      : MTernaryInstruction(classOpcode, v0, v1, v2), simdOp_(simdOp) {
   2122    setMovable();
   2123    setResultType(MIRType::Simd128);
   2124  }
   2125 
   2126 public:
   2127  INSTRUCTION_HEADER(WasmTernarySimd128)
   2128  TRIVIAL_NEW_WRAPPERS
   2129  NAMED_OPERANDS((0, v0), (1, v1), (2, v2))
   2130 
   2131  AliasSet getAliasSet() const override { return AliasSet::None(); }
   2132  bool congruentTo(const MDefinition* ins) const override {
   2133    return congruentIfOperandsEqual(ins) &&
   2134           simdOp() == ins->toWasmTernarySimd128()->simdOp();
   2135  }
   2136 #ifdef ENABLE_WASM_SIMD
   2137  MDefinition* foldsTo(TempAllocator& alloc) override;
   2138 
   2139  // If the control mask of a bitselect allows the operation to be specialized
   2140  // as a shuffle and it is profitable to specialize it on this platform, return
   2141  // true and the appropriate shuffle mask.
   2142  bool specializeBitselectConstantMaskAsShuffle(int8_t shuffle[16]);
   2143  // Checks if more relaxed version of lane select can be used. It returns true
   2144  // if a bit mask input expected to be all 0s or 1s for entire 8-bit lanes,
   2145  // false otherwise.
   2146  bool canRelaxBitselect();
   2147 #endif
   2148 
   2149  wasm::SimdOp simdOp() const { return simdOp_; }
   2150 
   2151  ALLOW_CLONE(MWasmTernarySimd128)
   2152 };
   2153 
   2154 // (v128, v128) -> v128 effect-free operations.
   2155 class MWasmBinarySimd128 : public MBinaryInstruction,
   2156                           public NoTypePolicy::Data {
   2157  wasm::SimdOp simdOp_;
   2158 
   2159  MWasmBinarySimd128(MDefinition* lhs, MDefinition* rhs, bool commutative,
   2160                     wasm::SimdOp simdOp)
   2161      : MBinaryInstruction(classOpcode, lhs, rhs), simdOp_(simdOp) {
   2162    setMovable();
   2163    setResultType(MIRType::Simd128);
   2164    if (commutative) {
   2165      setCommutative();
   2166    }
   2167  }
   2168 
   2169 public:
   2170  INSTRUCTION_HEADER(WasmBinarySimd128)
   2171  TRIVIAL_NEW_WRAPPERS
   2172 
   2173  AliasSet getAliasSet() const override { return AliasSet::None(); }
   2174  bool congruentTo(const MDefinition* ins) const override {
   2175    return congruentIfOperandsEqual(ins) &&
   2176           ins->toWasmBinarySimd128()->simdOp() == simdOp_;
   2177  }
   2178 #ifdef ENABLE_WASM_SIMD
   2179  MDefinition* foldsTo(TempAllocator& alloc) override;
   2180 
   2181  // Checks if pmaddubsw operation is supported.
   2182  bool canPmaddubsw();
   2183 #endif
   2184 
   2185  wasm::SimdOp simdOp() const { return simdOp_; }
   2186 
   2187  // Platform-dependent specialization.
   2188  bool specializeForConstantRhs();
   2189 
   2190  ALLOW_CLONE(MWasmBinarySimd128)
   2191 };
   2192 
   2193 // (v128, const) -> v128 effect-free operations.
   2194 class MWasmBinarySimd128WithConstant : public MUnaryInstruction,
   2195                                       public NoTypePolicy::Data {
   2196  SimdConstant rhs_;
   2197  wasm::SimdOp simdOp_;
   2198 
   2199  MWasmBinarySimd128WithConstant(MDefinition* lhs, const SimdConstant& rhs,
   2200                                 wasm::SimdOp simdOp)
   2201      : MUnaryInstruction(classOpcode, lhs), rhs_(rhs), simdOp_(simdOp) {
   2202    setMovable();
   2203    setResultType(MIRType::Simd128);
   2204  }
   2205 
   2206 public:
   2207  INSTRUCTION_HEADER(WasmBinarySimd128WithConstant)
   2208  TRIVIAL_NEW_WRAPPERS
   2209 
   2210  AliasSet getAliasSet() const override { return AliasSet::None(); }
   2211  bool congruentTo(const MDefinition* ins) const override {
   2212    return congruentIfOperandsEqual(ins) &&
   2213           ins->toWasmBinarySimd128WithConstant()->simdOp() == simdOp_ &&
   2214           rhs_.bitwiseEqual(ins->toWasmBinarySimd128WithConstant()->rhs());
   2215  }
   2216 
   2217  wasm::SimdOp simdOp() const { return simdOp_; }
   2218  MDefinition* lhs() const { return input(); }
   2219  const SimdConstant& rhs() const { return rhs_; }
   2220 
   2221  ALLOW_CLONE(MWasmBinarySimd128WithConstant)
   2222 };
   2223 
   2224 // (v128, scalar, imm) -> v128 effect-free operations.
   2225 class MWasmReplaceLaneSimd128 : public MBinaryInstruction,
   2226                                public NoTypePolicy::Data {
   2227  uint32_t laneIndex_;
   2228  wasm::SimdOp simdOp_;
   2229 
   2230  MWasmReplaceLaneSimd128(MDefinition* lhs, MDefinition* rhs,
   2231                          uint32_t laneIndex, wasm::SimdOp simdOp)
   2232      : MBinaryInstruction(classOpcode, lhs, rhs),
   2233        laneIndex_(laneIndex),
   2234        simdOp_(simdOp) {
   2235    setMovable();
   2236    setResultType(MIRType::Simd128);
   2237  }
   2238 
   2239 public:
   2240  INSTRUCTION_HEADER(WasmReplaceLaneSimd128)
   2241  TRIVIAL_NEW_WRAPPERS
   2242 
   2243  AliasSet getAliasSet() const override { return AliasSet::None(); }
   2244  bool congruentTo(const MDefinition* ins) const override {
   2245    return congruentIfOperandsEqual(ins) &&
   2246           ins->toWasmReplaceLaneSimd128()->simdOp() == simdOp_ &&
   2247           ins->toWasmReplaceLaneSimd128()->laneIndex() == laneIndex_;
   2248  }
   2249 
   2250  uint32_t laneIndex() const { return laneIndex_; }
   2251  wasm::SimdOp simdOp() const { return simdOp_; }
   2252 
   2253  ALLOW_CLONE(MWasmReplaceLaneSimd128)
   2254 };
   2255 
   2256 // (scalar) -> v128 effect-free operations.
   2257 class MWasmScalarToSimd128 : public MUnaryInstruction,
   2258                             public NoTypePolicy::Data {
   2259  wasm::SimdOp simdOp_;
   2260 
   2261  MWasmScalarToSimd128(MDefinition* src, wasm::SimdOp simdOp)
   2262      : MUnaryInstruction(classOpcode, src), simdOp_(simdOp) {
   2263    setMovable();
   2264    setResultType(MIRType::Simd128);
   2265  }
   2266 
   2267 public:
   2268  INSTRUCTION_HEADER(WasmScalarToSimd128)
   2269  TRIVIAL_NEW_WRAPPERS
   2270 
   2271  AliasSet getAliasSet() const override { return AliasSet::None(); }
   2272  bool congruentTo(const MDefinition* ins) const override {
   2273    return congruentIfOperandsEqual(ins) &&
   2274           ins->toWasmScalarToSimd128()->simdOp() == simdOp_;
   2275  }
   2276 #ifdef ENABLE_WASM_SIMD
   2277  MDefinition* foldsTo(TempAllocator& alloc) override;
   2278 #endif
   2279 
   2280  wasm::SimdOp simdOp() const { return simdOp_; }
   2281 
   2282  ALLOW_CLONE(MWasmScalarToSimd128)
   2283 };
   2284 
   2285 // (v128, imm) -> scalar effect-free operations.
   2286 class MWasmReduceSimd128 : public MUnaryInstruction, public NoTypePolicy::Data {
   2287  wasm::SimdOp simdOp_;
   2288  uint32_t imm_;
   2289 
   2290  MWasmReduceSimd128(MDefinition* src, wasm::SimdOp simdOp, MIRType outType,
   2291                     uint32_t imm)
   2292      : MUnaryInstruction(classOpcode, src), simdOp_(simdOp), imm_(imm) {
   2293    setMovable();
   2294    setResultType(outType);
   2295  }
   2296 
   2297 public:
   2298  INSTRUCTION_HEADER(WasmReduceSimd128)
   2299  TRIVIAL_NEW_WRAPPERS
   2300 
   2301  AliasSet getAliasSet() const override { return AliasSet::None(); }
   2302  bool congruentTo(const MDefinition* ins) const override {
   2303    return congruentIfOperandsEqual(ins) &&
   2304           ins->toWasmReduceSimd128()->simdOp() == simdOp_ &&
   2305           ins->toWasmReduceSimd128()->imm() == imm_;
   2306  }
   2307 #ifdef ENABLE_WASM_SIMD
   2308  MDefinition* foldsTo(TempAllocator& alloc) override;
   2309 #endif
   2310 
   2311  uint32_t imm() const { return imm_; }
   2312  wasm::SimdOp simdOp() const { return simdOp_; }
   2313 
   2314  ALLOW_CLONE(MWasmReduceSimd128)
   2315 };
   2316 
   2317 class MWasmLoadLaneSimd128
   2318    : public MVariadicInstruction,  // memoryBase is nullptr on some platforms
   2319      public NoTypePolicy::Data {
   2320  wasm::MemoryAccessDesc access_;
   2321  uint32_t laneSize_;
   2322  uint32_t laneIndex_;
   2323  uint32_t memoryBaseIndex_;
   2324 
   2325  MWasmLoadLaneSimd128(const wasm::MemoryAccessDesc& access, uint32_t laneSize,
   2326                       uint32_t laneIndex, uint32_t memoryBaseIndex)
   2327      : MVariadicInstruction(classOpcode),
   2328        access_(access),
   2329        laneSize_(laneSize),
   2330        laneIndex_(laneIndex),
   2331        memoryBaseIndex_(memoryBaseIndex) {
   2332    MOZ_ASSERT(!access_.isAtomic());
   2333    setGuard();
   2334    setResultType(MIRType::Simd128);
   2335  }
   2336 
   2337 public:
   2338  INSTRUCTION_HEADER(WasmLoadLaneSimd128)
   2339  NAMED_OPERANDS((0, base), (1, value));
   2340 
   2341  static MWasmLoadLaneSimd128* New(TempAllocator& alloc,
   2342                                   MDefinition* memoryBase, MDefinition* base,
   2343                                   const wasm::MemoryAccessDesc& access,
   2344                                   uint32_t laneSize, uint32_t laneIndex,
   2345                                   MDefinition* value) {
   2346    uint32_t nextIndex = 2;
   2347    uint32_t memoryBaseIndex = memoryBase ? nextIndex++ : UINT32_MAX;
   2348 
   2349    MWasmLoadLaneSimd128* load = new (alloc)
   2350        MWasmLoadLaneSimd128(access, laneSize, laneIndex, memoryBaseIndex);
   2351    if (!load->init(alloc, nextIndex)) {
   2352      return nullptr;
   2353    }
   2354 
   2355    load->initOperand(0, base);
   2356    load->initOperand(1, value);
   2357    if (memoryBase) {
   2358      load->initOperand(memoryBaseIndex, memoryBase);
   2359    }
   2360 
   2361    return load;
   2362  }
   2363 
   2364  const wasm::MemoryAccessDesc& access() const { return access_; }
   2365  uint32_t laneSize() const { return laneSize_; }
   2366  uint32_t laneIndex() const { return laneIndex_; }
   2367  bool hasMemoryBase() const { return memoryBaseIndex_ != UINT32_MAX; }
   2368  MDefinition* memoryBase() const {
   2369    MOZ_ASSERT(hasMemoryBase());
   2370    return getOperand(memoryBaseIndex_);
   2371  }
   2372 
   2373  AliasSet getAliasSet() const override {
   2374    return AliasSet::Load(AliasSet::WasmHeap);
   2375  }
   2376 };
   2377 
   2378 class MWasmStoreLaneSimd128 : public MVariadicInstruction,
   2379                              public NoTypePolicy::Data {
   2380  wasm::MemoryAccessDesc access_;
   2381  uint32_t laneSize_;
   2382  uint32_t laneIndex_;
   2383  uint32_t memoryBaseIndex_;
   2384 
   2385  explicit MWasmStoreLaneSimd128(const wasm::MemoryAccessDesc& access,
   2386                                 uint32_t laneSize, uint32_t laneIndex,
   2387                                 uint32_t memoryBaseIndex)
   2388      : MVariadicInstruction(classOpcode),
   2389        access_(access),
   2390        laneSize_(laneSize),
   2391        laneIndex_(laneIndex),
   2392        memoryBaseIndex_(memoryBaseIndex) {
   2393    MOZ_ASSERT(!access_.isAtomic());
   2394    setGuard();
   2395    setResultType(MIRType::Simd128);
   2396  }
   2397 
   2398 public:
   2399  INSTRUCTION_HEADER(WasmStoreLaneSimd128)
   2400  NAMED_OPERANDS((0, base), (1, value))
   2401 
   2402  static MWasmStoreLaneSimd128* New(TempAllocator& alloc,
   2403                                    MDefinition* memoryBase, MDefinition* base,
   2404                                    const wasm::MemoryAccessDesc& access,
   2405                                    uint32_t laneSize, uint32_t laneIndex,
   2406                                    MDefinition* value) {
   2407    uint32_t nextIndex = 2;
   2408    uint32_t memoryBaseIndex = memoryBase ? nextIndex++ : UINT32_MAX;
   2409 
   2410    MWasmStoreLaneSimd128* store = new (alloc)
   2411        MWasmStoreLaneSimd128(access, laneSize, laneIndex, memoryBaseIndex);
   2412    if (!store->init(alloc, nextIndex)) {
   2413      return nullptr;
   2414    }
   2415 
   2416    store->initOperand(0, base);
   2417    store->initOperand(1, value);
   2418    if (memoryBase) {
   2419      store->initOperand(memoryBaseIndex, memoryBase);
   2420    }
   2421 
   2422    return store;
   2423  }
   2424 
   2425  const wasm::MemoryAccessDesc& access() const { return access_; }
   2426  uint32_t laneSize() const { return laneSize_; }
   2427  uint32_t laneIndex() const { return laneIndex_; }
   2428  bool hasMemoryBase() const { return memoryBaseIndex_ != UINT32_MAX; }
   2429  MDefinition* memoryBase() const {
   2430    MOZ_ASSERT(hasMemoryBase());
   2431    return getOperand(memoryBaseIndex_);
   2432  }
   2433 
   2434  AliasSet getAliasSet() const override {
   2435    return AliasSet::Store(AliasSet::WasmHeap);
   2436  }
   2437 };
   2438 
   2439 // End Wasm SIMD
   2440 
   2441 class MIonToWasmCall final : public MVariadicInstruction,
   2442                             public NoTypePolicy::Data {
   2443  CompilerGCPointer<WasmInstanceObject*> instanceObj_;
   2444  const wasm::FuncExport& funcExport_;
   2445 
   2446  MIonToWasmCall(WasmInstanceObject* instanceObj, MIRType resultType,
   2447                 const wasm::FuncExport& funcExport)
   2448      : MVariadicInstruction(classOpcode),
   2449        instanceObj_(instanceObj),
   2450        funcExport_(funcExport) {
   2451    setResultType(resultType);
   2452  }
   2453 
   2454 public:
   2455  INSTRUCTION_HEADER(IonToWasmCall);
   2456 
   2457  static MIonToWasmCall* New(TempAllocator& alloc,
   2458                             WasmInstanceObject* instanceObj,
   2459                             const wasm::FuncExport& funcExport);
   2460 
   2461  void initArg(size_t i, MDefinition* arg) { initOperand(i, arg); }
   2462 
   2463  WasmInstanceObject* instanceObject() const { return instanceObj_; }
   2464  wasm::Instance* instance() const { return &instanceObj_->instance(); }
   2465  const wasm::FuncExport& funcExport() const { return funcExport_; }
   2466  bool possiblyCalls() const override { return true; }
   2467 #ifdef DEBUG
   2468  bool isConsistentFloat32Use(MUse* use) const override;
   2469 #endif
   2470 };
   2471 
   2472 // For accesses to wasm object fields, we need to be able to describe 8- and
   2473 // 16-bit accesses.  But MIRType can't represent those.  Hence these two
   2474 // supplemental enums, used for reading and writing fields respectively.
   2475 
   2476 // Indicates how to widen an 8- or 16-bit value (when it is read from memory).
   2477 enum class MWideningOp : uint8_t { None, FromU16, FromS16, FromU8, FromS8 };
   2478 
   2479 #ifdef JS_JITSPEW
   2480 static inline const char* StringFromMWideningOp(MWideningOp op) {
   2481  switch (op) {
   2482    case MWideningOp::None:
   2483      return "None";
   2484    case MWideningOp::FromU16:
   2485      return "FromU16";
   2486    case MWideningOp::FromS16:
   2487      return "FromS16";
   2488    case MWideningOp::FromU8:
   2489      return "FromU8";
   2490    case MWideningOp::FromS8:
   2491      return "FromS8";
   2492    default:
   2493      break;
   2494  }
   2495  MOZ_CRASH("Unknown MWideningOp");
   2496 }
   2497 #endif
   2498 
   2499 // Indicates how to narrow a 32-bit value (when it is written to memory).  The
   2500 // operation is a simple truncate.
   2501 enum class MNarrowingOp : uint8_t { None, To16, To8 };
   2502 
   2503 #ifdef JS_JITSPEW
   2504 static inline const char* StringFromMNarrowingOp(MNarrowingOp op) {
   2505  switch (op) {
   2506    case MNarrowingOp::None:
   2507      return "None";
   2508    case MNarrowingOp::To16:
   2509      return "To16";
   2510    case MNarrowingOp::To8:
   2511      return "To8";
   2512    default:
   2513      break;
   2514  }
   2515  MOZ_CRASH("Unknown MNarrowingOp");
   2516 }
   2517 #endif
   2518 
   2519 // Loads a value from a location, denoted as a fixed offset from a base
   2520 // pointer. This field may be any value type, including references. No
   2521 // barriers are performed.
   2522 //
   2523 // This instruction can extend the lifetime of an optional `keepAlive`
   2524 // parameter to match the lifetime of this instruction. This is necessary if
   2525 // the base pointer is owned by some GC'ed object, which means that the GC
   2526 // object must have the same lifetime as all uses of it's owned pointers.
   2527 // No code to access the keepAlive value is generated.
   2528 //
   2529 // `offset` must be representable as a 31-bit unsigned integer.
   2530 //
   2531 // An optional structFieldIndex can be given for struct accesses and used in
   2532 // scalar replacement.
   2533 class MWasmLoadField : public MBinaryInstruction, public NoTypePolicy::Data {
   2534  uint32_t offset_;
   2535  mozilla::Maybe<uint32_t> structFieldIndex_;
   2536  MWideningOp wideningOp_;
   2537  AliasSet aliases_;
   2538  wasm::MaybeTrapSiteDesc maybeTrap_;
   2539  mozilla::Maybe<wasm::RefTypeHierarchy> hierarchy_;
   2540 
   2541  MWasmLoadField(MDefinition* base, MDefinition* keepAlive, size_t offset,
   2542                 mozilla::Maybe<uint32_t> structFieldIndex, MIRType type,
   2543                 MWideningOp wideningOp, AliasSet aliases,
   2544                 wasm::MaybeTrapSiteDesc maybeTrap = mozilla::Nothing(),
   2545                 wasm::MaybeRefType maybeRefType = wasm::MaybeRefType())
   2546      : MBinaryInstruction(classOpcode, base, keepAlive ? keepAlive : base),
   2547        offset_(uint32_t(offset)),
   2548        structFieldIndex_(structFieldIndex),
   2549        wideningOp_(wideningOp),
   2550        aliases_(aliases),
   2551        maybeTrap_(std::move(maybeTrap)),
   2552        hierarchy_(maybeRefType.hierarchy()) {
   2553    MOZ_ASSERT(offset <= INT32_MAX);
   2554    // "if you want to widen the value when it is loaded, the destination type
   2555    // must be Int32".
   2556    MOZ_ASSERT_IF(wideningOp != MWideningOp::None, type == MIRType::Int32);
   2557    // Check the alias set is one of the expected kinds.
   2558    MOZ_ASSERT(
   2559        aliases.flags() ==
   2560            AliasSet::Load(AliasSet::WasmStructOutlineDataPointer).flags() ||
   2561        aliases.flags() ==
   2562            AliasSet::Load(AliasSet::WasmStructInlineDataArea).flags() ||
   2563        aliases.flags() ==
   2564            AliasSet::Load(AliasSet::WasmStructOutlineDataArea).flags() ||
   2565        aliases.flags() ==
   2566            AliasSet::Load(AliasSet::WasmArrayNumElements).flags() ||
   2567        aliases.flags() ==
   2568            AliasSet::Load(AliasSet::WasmArrayDataPointer).flags() ||
   2569        aliases.flags() ==
   2570            AliasSet::Load(AliasSet::WasmArrayDataArea).flags() ||
   2571        aliases.flags() == AliasSet::Load(AliasSet::Any).flags());
   2572    // Check the alias set is consistent with the result type.
   2573    MOZ_ASSERT(
   2574        (aliases.flags() ==
   2575         AliasSet::Load(AliasSet::WasmStructOutlineDataPointer).flags()) ==
   2576        (type == MIRType::WasmStructData));
   2577    MOZ_ASSERT((aliases.flags() ==
   2578                AliasSet::Load(AliasSet::WasmArrayDataPointer).flags()) ==
   2579               (type == MIRType::WasmArrayData));
   2580    setResultType(type);
   2581    if (maybeTrap_) {
   2582      // This is safe, but see bug 1992059 for associated details.
   2583      setGuard();
   2584    } else {
   2585      setMovable();
   2586    }
   2587    initWasmRefType(maybeRefType);
   2588  }
   2589 
   2590 public:
   2591  INSTRUCTION_HEADER(WasmLoadField)
   2592  TRIVIAL_NEW_WRAPPERS
   2593  NAMED_OPERANDS((0, base), (1, keepAlive))
   2594 
   2595  uint32_t offset() const { return offset_; }
   2596  mozilla::Maybe<uint32_t> structFieldIndex() const {
   2597    return structFieldIndex_;
   2598  }
   2599  MWideningOp wideningOp() const { return wideningOp_; }
   2600  AliasSet getAliasSet() const override { return aliases_; }
   2601  wasm::MaybeTrapSiteDesc maybeTrap() const { return maybeTrap_; }
   2602  mozilla::Maybe<wasm::RefTypeHierarchy> hierarchy() const {
   2603    return hierarchy_;
   2604  }
   2605 
   2606  bool congruentTo(const MDefinition* ins) const override {
   2607    if (!ins->isWasmLoadField()) {
   2608      return false;
   2609    }
   2610    const MWasmLoadField* other = ins->toWasmLoadField();
   2611    return congruentIfOperandsEqual(other) && offset() == other->offset() &&
   2612           structFieldIndex() == other->structFieldIndex() &&
   2613           wideningOp() == other->wideningOp() &&
   2614           getAliasSet().flags() == other->getAliasSet().flags() &&
   2615           hierarchy() == other->hierarchy();
   2616  }
   2617 
   2618  virtual AliasType mightAlias(const MDefinition* ins) const override;
   2619 
   2620 #ifdef JS_JITSPEW
   2621  void getExtras(ExtrasCollector* extras) const override {
   2622    char buf[96];
   2623    SprintfLiteral(buf, "(offs=%lld, wideningOp=%s)", (long long int)offset_,
   2624                   StringFromMWideningOp(wideningOp_));
   2625    extras->add(buf);
   2626  }
   2627 #endif
   2628 
   2629  ALLOW_CLONE(MWasmLoadField)
   2630 };
   2631 
   2632 // Loads a value from base pointer, given an index and element size. This field
   2633 // may be any value type, including references. No barriers are performed.
   2634 //
   2635 // The element size is implicitly defined by MIRType and MWideningOp. For
   2636 // example, MIRType::Float32 indicates an element size of 32 bits, and
   2637 // MIRType::Int32 and MWideningOp::FromU16 together indicate an element size of
   2638 // 16 bits.
   2639 //
   2640 // This instruction takes an optional second object `keepAlive` that must be
   2641 // kept alive, as described for MWasmLoadField above.
   2642 class MWasmLoadElement : public MTernaryInstruction, public NoTypePolicy::Data {
   2643  MWideningOp wideningOp_;
   2644  Scale scale_;
   2645  AliasSet aliases_;
   2646  wasm::MaybeTrapSiteDesc maybeTrap_;
   2647 
   2648  MWasmLoadElement(MDefinition* base, MDefinition* keepAlive,
   2649                   MDefinition* index, MIRType type, MWideningOp wideningOp,
   2650                   Scale scale, AliasSet aliases,
   2651                   wasm::MaybeTrapSiteDesc maybeTrap = mozilla::Nothing(),
   2652                   wasm::MaybeRefType maybeRefType = wasm::MaybeRefType())
   2653      : MTernaryInstruction(classOpcode, base, index,
   2654                            keepAlive ? keepAlive : base),
   2655        wideningOp_(wideningOp),
   2656        scale_(scale),
   2657        aliases_(aliases),
   2658        maybeTrap_(std::move(maybeTrap)) {
   2659    MOZ_ASSERT(base->type() == MIRType::WasmArrayData);
   2660    MOZ_ASSERT(aliases.flags() ==
   2661                   AliasSet::Load(AliasSet::WasmArrayDataArea).flags() ||
   2662               aliases.flags() == AliasSet::Load(AliasSet::Any).flags());
   2663    setResultType(type);
   2664    if (maybeTrap_) {
   2665      setGuard();
   2666    }
   2667    initWasmRefType(maybeRefType);
   2668  }
   2669 
   2670 public:
   2671  INSTRUCTION_HEADER(WasmLoadElement)
   2672  TRIVIAL_NEW_WRAPPERS
   2673  NAMED_OPERANDS((0, base), (1, index), (2, keepAlive))
   2674 
   2675  MWideningOp wideningOp() const { return wideningOp_; }
   2676  Scale scale() const { return scale_; }
   2677  AliasSet getAliasSet() const override { return aliases_; }
   2678  wasm::MaybeTrapSiteDesc maybeTrap() const { return maybeTrap_; }
   2679 
   2680 #ifdef JS_JITSPEW
   2681  void getExtras(ExtrasCollector* extras) const override {
   2682    char buf[96];
   2683    SprintfLiteral(buf, "(wideningOp=%s, scale=%s)",
   2684                   StringFromMWideningOp(wideningOp_), StringFromScale(scale_));
   2685    extras->add(buf);
   2686  }
   2687 #endif
   2688 
   2689  ALLOW_CLONE(MWasmLoadElement)
   2690 };
   2691 
   2692 // Stores a non-reference value to anlocation, denoted as a fixed offset from
   2693 // a base pointer, which (it is assumed) is within a wasm object.  This field
   2694 // may be any value type, _excluding_ references.  References _must_ use the
   2695 // 'Ref' variant of this instruction.  The offset must be representable as a
   2696 // 31-bit unsigned integer.
   2697 //
   2698 // This instruction takes a second object `keepAlive` that must be kept alive,
   2699 // as described for MWasmLoadField above.
   2700 class MWasmStoreField : public MTernaryInstruction, public NoTypePolicy::Data {
   2701  uint32_t offset_;
   2702  mozilla::Maybe<uint32_t> structFieldIndex_;
   2703  MNarrowingOp narrowingOp_;
   2704  AliasSet aliases_;
   2705  wasm::MaybeTrapSiteDesc maybeTrap_;
   2706 
   2707  MWasmStoreField(MDefinition* base, MDefinition* keepAlive, size_t offset,
   2708                  mozilla::Maybe<uint32_t> structFieldIndex, MDefinition* value,
   2709                  MNarrowingOp narrowingOp, AliasSet aliases,
   2710                  wasm::MaybeTrapSiteDesc maybeTrap = mozilla::Nothing())
   2711      : MTernaryInstruction(classOpcode, base, value,
   2712                            keepAlive ? keepAlive : base),
   2713        offset_(uint32_t(offset)),
   2714        structFieldIndex_(structFieldIndex),
   2715        narrowingOp_(narrowingOp),
   2716        aliases_(aliases),
   2717        maybeTrap_(std::move(maybeTrap)) {
   2718    MOZ_ASSERT(offset <= INT32_MAX);
   2719    MOZ_ASSERT(value->type() != MIRType::WasmAnyRef);
   2720    // "if you want to narrow the value when it is stored, the source type
   2721    // must be Int32".
   2722    MOZ_ASSERT_IF(narrowingOp != MNarrowingOp::None,
   2723                  value->type() == MIRType::Int32);
   2724    MOZ_ASSERT(
   2725        aliases.flags() ==
   2726            AliasSet::Store(AliasSet::WasmStructInlineDataArea).flags() ||
   2727        aliases.flags() ==
   2728            AliasSet::Store(AliasSet::WasmStructOutlineDataArea).flags() ||
   2729        aliases.flags() ==
   2730            AliasSet::Store(AliasSet::WasmArrayDataArea).flags() ||
   2731        aliases.flags() == AliasSet::Store(AliasSet::Any).flags());
   2732    if (maybeTrap_) {
   2733      setGuard();
   2734    }
   2735  }
   2736 
   2737 public:
   2738  INSTRUCTION_HEADER(WasmStoreField)
   2739  TRIVIAL_NEW_WRAPPERS
   2740  NAMED_OPERANDS((0, base), (1, value), (2, keepAlive))
   2741 
   2742  uint32_t offset() const { return offset_; }
   2743  mozilla::Maybe<uint32_t> structFieldIndex() const {
   2744    return structFieldIndex_;
   2745  }
   2746  MNarrowingOp narrowingOp() const { return narrowingOp_; }
   2747  AliasSet getAliasSet() const override { return aliases_; }
   2748  wasm::MaybeTrapSiteDesc maybeTrap() const { return maybeTrap_; }
   2749 
   2750 #ifdef JS_JITSPEW
   2751  void getExtras(ExtrasCollector* extras) const override {
   2752    char buf[96];
   2753    SprintfLiteral(buf, "(offs=%lld, narrowingOp=%s)", (long long int)offset_,
   2754                   StringFromMNarrowingOp(narrowingOp_));
   2755    extras->add(buf);
   2756  }
   2757 #endif
   2758 
   2759  ALLOW_CLONE(MWasmStoreField)
   2760 };
   2761 
   2762 // Stores a reference value to a location, denoted as a fixed offset from a
   2763 // base pointer, which (it is assumed) is within a wasm object.  This
   2764 // instruction emits a pre-barrier.  A post barrier _must_ be performed
   2765 // separately.  The offset must be representable as a 31-bit unsigned integer.
   2766 //
   2767 // This instruction takes a second object `keepAlive` that must be kept alive,
   2768 // as described for MWasmLoadField above.
   2769 class MWasmStoreFieldRef : public MAryInstruction<4>,
   2770                           public NoTypePolicy::Data {
   2771  uint32_t offset_;
   2772  mozilla::Maybe<uint32_t> structFieldIndex_;
   2773  AliasSet aliases_;
   2774  wasm::MaybeTrapSiteDesc maybeTrap_;
   2775  WasmPreBarrierKind preBarrierKind_;
   2776 
   2777  MWasmStoreFieldRef(MDefinition* instance, MDefinition* base,
   2778                     MDefinition* keepAlive, size_t offset,
   2779                     mozilla::Maybe<uint32_t> structFieldIndex,
   2780                     MDefinition* value, AliasSet aliases,
   2781                     wasm::MaybeTrapSiteDesc maybeTrap,
   2782                     WasmPreBarrierKind preBarrierKind)
   2783      : MAryInstruction<4>(classOpcode),
   2784        offset_(uint32_t(offset)),
   2785        structFieldIndex_(structFieldIndex),
   2786        aliases_(aliases),
   2787        maybeTrap_(std::move(maybeTrap)),
   2788        preBarrierKind_(preBarrierKind) {
   2789    MOZ_ASSERT(base->type() == TargetWordMIRType() ||
   2790               base->type() == MIRType::Pointer ||
   2791               base->type() == MIRType::WasmAnyRef ||
   2792               base->type() == MIRType::WasmStructData ||
   2793               base->type() == MIRType::WasmArrayData);
   2794    MOZ_ASSERT(offset <= INT32_MAX);
   2795    MOZ_ASSERT(value->type() == MIRType::WasmAnyRef);
   2796    MOZ_ASSERT(
   2797        aliases.flags() ==
   2798            AliasSet::Store(AliasSet::WasmStructInlineDataArea).flags() ||
   2799        aliases.flags() ==
   2800            AliasSet::Store(AliasSet::WasmStructOutlineDataArea).flags() ||
   2801        aliases.flags() ==
   2802            AliasSet::Store(AliasSet::WasmArrayDataArea).flags() ||
   2803        aliases.flags() == AliasSet::Store(AliasSet::Any).flags());
   2804    initOperand(0, instance);
   2805    initOperand(1, base);
   2806    initOperand(2, value);
   2807    initOperand(3, keepAlive ? keepAlive : base);
   2808    if (maybeTrap_) {
   2809      setGuard();
   2810    }
   2811  }
   2812 
   2813 public:
   2814  INSTRUCTION_HEADER(WasmStoreFieldRef)
   2815  TRIVIAL_NEW_WRAPPERS
   2816  NAMED_OPERANDS((0, instance), (1, base), (2, value), (3, keepAlive))
   2817 
   2818  uint32_t offset() const { return offset_; }
   2819  mozilla::Maybe<uint32_t> structFieldIndex() const {
   2820    return structFieldIndex_;
   2821  }
   2822  AliasSet getAliasSet() const override { return aliases_; }
   2823  wasm::MaybeTrapSiteDesc maybeTrap() const { return maybeTrap_; }
   2824  WasmPreBarrierKind preBarrierKind() const { return preBarrierKind_; }
   2825 
   2826 #ifdef JS_JITSPEW
   2827  void getExtras(ExtrasCollector* extras) const override {
   2828    char buf[64];
   2829    SprintfLiteral(buf, "(offs=%lld)", (long long int)offset_);
   2830    extras->add(buf);
   2831  }
   2832 #endif
   2833 
   2834  ALLOW_CLONE(MWasmStoreFieldRef)
   2835 };
   2836 
   2837 // Stores a non-reference value to a base pointer, given an index and element
   2838 // size. This field may be any value type, excluding references. References MUST
   2839 // use the 'Ref' variant of this instruction.
   2840 //
   2841 // The element size is implicitly defined by MIRType and MNarrowingOp. For
   2842 // example, MIRType::Float32 indicates an element size of 32 bits, and
   2843 // MIRType::Int32 and MNarrowingOp::To16 together indicate an element size of 16
   2844 // bits.
   2845 //
   2846 // This instruction takes a second object `keepAlive` that must be kept alive,
   2847 // as described for MWasmLoadField above.
   2848 class MWasmStoreElement : public MQuaternaryInstruction,
   2849                          public NoTypePolicy::Data {
   2850  MNarrowingOp narrowingOp_;
   2851  Scale scale_;
   2852  AliasSet aliases_;
   2853  wasm::MaybeTrapSiteDesc maybeTrap_;
   2854 
   2855  MWasmStoreElement(MDefinition* base, MDefinition* index, MDefinition* value,
   2856                    MDefinition* keepAlive, MNarrowingOp narrowingOp,
   2857                    Scale scale, AliasSet aliases,
   2858                    wasm::MaybeTrapSiteDesc maybeTrap = mozilla::Nothing())
   2859      : MQuaternaryInstruction(classOpcode, base, index, value,
   2860                               keepAlive ? keepAlive : base),
   2861        narrowingOp_(narrowingOp),
   2862        scale_(scale),
   2863        aliases_(aliases),
   2864        maybeTrap_(std::move(maybeTrap)) {
   2865    MOZ_ASSERT(base->type() == MIRType::WasmArrayData);
   2866    MOZ_ASSERT(value->type() != MIRType::WasmAnyRef);
   2867    // "if you want to narrow the value when it is stored, the source type
   2868    // must be Int32".
   2869    MOZ_ASSERT_IF(narrowingOp != MNarrowingOp::None,
   2870                  value->type() == MIRType::Int32);
   2871    MOZ_ASSERT(aliases.flags() ==
   2872                   AliasSet::Store(AliasSet::WasmArrayDataArea).flags() ||
   2873               aliases.flags() == AliasSet::Store(AliasSet::Any).flags());
   2874    if (maybeTrap_) {
   2875      setGuard();
   2876    }
   2877  }
   2878 
   2879 public:
   2880  INSTRUCTION_HEADER(WasmStoreElement)
   2881  TRIVIAL_NEW_WRAPPERS
   2882  NAMED_OPERANDS((0, base), (1, index), (2, value), (3, keepAlive))
   2883 
   2884  MNarrowingOp narrowingOp() const { return narrowingOp_; }
   2885  Scale scale() const { return scale_; }
   2886  AliasSet getAliasSet() const override { return aliases_; }
   2887  wasm::MaybeTrapSiteDesc maybeTrap() const { return maybeTrap_; }
   2888 
   2889 #ifdef JS_JITSPEW
   2890  void getExtras(ExtrasCollector* extras) const override {
   2891    char buf[96];
   2892    SprintfLiteral(buf, "(narrowingOp=%s, scale=%s)",
   2893                   StringFromMNarrowingOp(narrowingOp_),
   2894                   StringFromScale(scale_));
   2895    extras->add(buf);
   2896  }
   2897 #endif
   2898 
   2899  ALLOW_CLONE(MWasmStoreElement)
   2900 };
   2901 
   2902 // Stores a reference value to a base pointer, given an index and element size.
   2903 // This instruction emits a pre-barrier. A post barrier MUST be performed
   2904 // separately.
   2905 //
   2906 // The element size is implicitly defined by MIRType and MNarrowingOp, as
   2907 // described for MWasmStoreElement above.
   2908 //
   2909 // This instruction takes a second object `ka` that must be kept alive, as
   2910 // described for MWasmLoadField above.
   2911 class MWasmStoreElementRef : public MAryInstruction<5>,
   2912                             public NoTypePolicy::Data {
   2913  AliasSet aliases_;
   2914  wasm::MaybeTrapSiteDesc maybeTrap_;
   2915  WasmPreBarrierKind preBarrierKind_;
   2916 
   2917  MWasmStoreElementRef(MDefinition* instance, MDefinition* base,
   2918                       MDefinition* index, MDefinition* value,
   2919                       MDefinition* keepAlive, AliasSet aliases,
   2920                       wasm::MaybeTrapSiteDesc maybeTrap,
   2921                       WasmPreBarrierKind preBarrierKind)
   2922      : MAryInstruction<5>(classOpcode),
   2923        aliases_(aliases),
   2924        maybeTrap_(std::move(maybeTrap)),
   2925        preBarrierKind_(preBarrierKind) {
   2926    MOZ_ASSERT(base->type() == MIRType::WasmArrayData);
   2927    MOZ_ASSERT(value->type() == MIRType::WasmAnyRef);
   2928    MOZ_ASSERT(aliases.flags() ==
   2929                   AliasSet::Store(AliasSet::WasmArrayDataArea).flags() ||
   2930               aliases.flags() == AliasSet::Store(AliasSet::Any).flags());
   2931    initOperand(0, instance);
   2932    initOperand(1, base);
   2933    initOperand(2, index);
   2934    initOperand(3, value);
   2935    initOperand(4, keepAlive ? keepAlive : base);
   2936    if (maybeTrap_) {
   2937      setGuard();
   2938    }
   2939  }
   2940 
   2941 public:
   2942  INSTRUCTION_HEADER(WasmStoreElementRef)
   2943  TRIVIAL_NEW_WRAPPERS
   2944  NAMED_OPERANDS((0, instance), (1, base), (2, index), (3, value),
   2945                 (4, keepAlive))
   2946 
   2947  AliasSet getAliasSet() const override { return aliases_; }
   2948  wasm::MaybeTrapSiteDesc maybeTrap() const { return maybeTrap_; }
   2949  WasmPreBarrierKind preBarrierKind() const { return preBarrierKind_; }
   2950 
   2951  ALLOW_CLONE(MWasmStoreElementRef)
   2952 };
   2953 
   2954 class MWasmRefAsNonNull : public MUnaryInstruction, public NoTypePolicy::Data {
   2955  wasm::TrapSiteDesc trapSiteDesc_;
   2956 
   2957  MWasmRefAsNonNull(MDefinition* ref, const wasm::TrapSiteDesc& trapSiteDesc)
   2958      : MUnaryInstruction(classOpcode, ref), trapSiteDesc_(trapSiteDesc) {
   2959    setResultType(MIRType::WasmAnyRef);
   2960    setGuard();
   2961  }
   2962 
   2963 public:
   2964  INSTRUCTION_HEADER(WasmRefAsNonNull)
   2965  TRIVIAL_NEW_WRAPPERS
   2966  NAMED_OPERANDS((0, ref))
   2967 
   2968  const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; }
   2969 
   2970  bool congruentTo(const MDefinition* ins) const override {
   2971    return congruentIfOperandsEqual(ins);
   2972  }
   2973 
   2974  wasm::MaybeRefType computeWasmRefType() const override {
   2975    if (ref()->wasmRefType().isNothing()) {
   2976      return wasm::MaybeRefType();
   2977    }
   2978    return wasm::MaybeRefType(ref()->wasmRefType().value().asNonNullable());
   2979  }
   2980 
   2981  MDefinition* foldsTo(TempAllocator& alloc) override;
   2982  AliasSet getAliasSet() const override { return AliasSet::None(); }
   2983 
   2984  ALLOW_CLONE(MWasmRefAsNonNull)
   2985 };
   2986 
   2987 // Tests if the wasm ref `ref` is a subtype of `destType` and returns the
   2988 // boolean representing the result.
   2989 class MWasmRefTestAbstract : public MUnaryInstruction,
   2990                             public NoTypePolicy::Data {
   2991  wasm::RefType destType_;
   2992 
   2993  MWasmRefTestAbstract(MDefinition* ref, wasm::RefType destType)
   2994      : MUnaryInstruction(classOpcode, ref), destType_(destType) {
   2995    MOZ_ASSERT(!destType.isTypeRef());
   2996    setResultType(MIRType::Int32);
   2997    setMovable();
   2998  }
   2999 
   3000 public:
   3001  INSTRUCTION_HEADER(WasmRefTestAbstract)
   3002  TRIVIAL_NEW_WRAPPERS
   3003  NAMED_OPERANDS((0, ref))
   3004 
   3005  wasm::RefType destType() const { return destType_; };
   3006 
   3007  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3008 
   3009  bool congruentTo(const MDefinition* ins) const override {
   3010    return congruentIfOperandsEqual(ins) &&
   3011           destType() == ins->toWasmRefTestAbstract()->destType();
   3012  }
   3013 
   3014  HashNumber valueHash() const override {
   3015    HashNumber hn = MUnaryInstruction::valueHash();
   3016    hn = addU64ToHash(hn, destType().packed().bits());
   3017    return hn;
   3018  }
   3019 
   3020  MDefinition* foldsTo(TempAllocator& alloc) override;
   3021 
   3022  ALLOW_CLONE(MWasmRefTestAbstract)
   3023 };
   3024 
   3025 // Tests if the wasm ref `ref` is a subtype of `superSTV` and returns the
   3026 // boolean representing the result.
   3027 //
   3028 // The actual super type definition must be known at compile time, so that the
   3029 // subtyping depth of super type depth can be used.
   3030 class MWasmRefTestConcrete : public MBinaryInstruction,
   3031                             public NoTypePolicy::Data {
   3032  wasm::RefType destType_;
   3033 
   3034  MWasmRefTestConcrete(MDefinition* ref, MDefinition* superSTV,
   3035                       wasm::RefType destType)
   3036      : MBinaryInstruction(classOpcode, ref, superSTV), destType_(destType) {
   3037    MOZ_ASSERT(destType.isTypeRef());
   3038    setResultType(MIRType::Int32);
   3039    setMovable();
   3040  }
   3041 
   3042 public:
   3043  INSTRUCTION_HEADER(WasmRefTestConcrete)
   3044  TRIVIAL_NEW_WRAPPERS
   3045  NAMED_OPERANDS((0, ref), (1, superSTV))
   3046 
   3047  wasm::RefType destType() const { return destType_; };
   3048 
   3049  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3050 
   3051  bool congruentTo(const MDefinition* ins) const override {
   3052    return congruentIfOperandsEqual(ins) &&
   3053           destType() == ins->toWasmRefTestConcrete()->destType();
   3054  }
   3055 
   3056  HashNumber valueHash() const override {
   3057    HashNumber hn = MBinaryInstruction::valueHash();
   3058    hn = addU64ToHash(hn, destType().packed().bits());
   3059    return hn;
   3060  }
   3061 
   3062  MDefinition* foldsTo(TempAllocator& alloc) override;
   3063 
   3064  ALLOW_CLONE(MWasmRefTestConcrete)
   3065 };
   3066 
   3067 // Tests if the wasm ref `ref` is a subtype of `destType` and if so returns the
   3068 // ref, otherwise it does a wasm trap.
   3069 class MWasmRefCastAbstract : public MUnaryInstruction,
   3070                             public NoTypePolicy::Data {
   3071  wasm::RefType destType_;
   3072  wasm::TrapSiteDesc trapSiteDesc_;
   3073 
   3074  MWasmRefCastAbstract(MDefinition* ref, wasm::RefType destType,
   3075                       wasm::TrapSiteDesc&& trapSiteDesc)
   3076      : MUnaryInstruction(classOpcode, ref),
   3077        destType_(destType),
   3078        trapSiteDesc_(std::move(trapSiteDesc)) {
   3079    MOZ_ASSERT(!destType.isTypeRef());
   3080    setResultType(MIRType::WasmAnyRef);
   3081    // This may trap, which requires this to be a guard.
   3082    setGuard();
   3083    initWasmRefType(wasm::MaybeRefType(destType));
   3084  }
   3085 
   3086 public:
   3087  INSTRUCTION_HEADER(WasmRefCastAbstract)
   3088  TRIVIAL_NEW_WRAPPERS
   3089  NAMED_OPERANDS((0, ref))
   3090  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3091 
   3092  wasm::RefType destType() const { return destType_; };
   3093  const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; }
   3094 
   3095  MDefinition* foldsTo(TempAllocator& alloc) override;
   3096 };
   3097 
   3098 // Tests if the wasm ref `ref` is a subtype of `superSTV`, if so return the
   3099 // ref, otherwise do a wasm trap.
   3100 //
   3101 // The actual super type definition must be known at compile time, so that the
   3102 // subtyping depth of super type depth can be used.
   3103 class MWasmRefCastConcrete : public MBinaryInstruction,
   3104                             public NoTypePolicy::Data {
   3105  wasm::RefType destType_;
   3106  wasm::TrapSiteDesc trapSiteDesc_;
   3107 
   3108  MWasmRefCastConcrete(MDefinition* ref, MDefinition* superSTV,
   3109                       wasm::RefType destType,
   3110                       wasm::TrapSiteDesc&& trapSiteDesc)
   3111      : MBinaryInstruction(classOpcode, ref, superSTV),
   3112        destType_(destType),
   3113        trapSiteDesc_(std::move(trapSiteDesc)) {
   3114    MOZ_ASSERT(destType.isTypeRef());
   3115    setResultType(MIRType::WasmAnyRef);
   3116    // This may trap, which requires this to be a guard.
   3117    setGuard();
   3118    initWasmRefType(wasm::MaybeRefType(destType));
   3119  }
   3120 
   3121 public:
   3122  INSTRUCTION_HEADER(WasmRefCastConcrete)
   3123  TRIVIAL_NEW_WRAPPERS
   3124  NAMED_OPERANDS((0, ref), (1, superSTV))
   3125  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3126 
   3127  wasm::RefType destType() const { return destType_; };
   3128  const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; }
   3129 
   3130  MDefinition* foldsTo(TempAllocator& alloc) override;
   3131 };
   3132 
   3133 class MWasmRefConvertAnyExtern : public MUnaryInstruction,
   3134                                 public NoTypePolicy::Data {
   3135  wasm::RefType::Kind destTypeKind_;
   3136 
   3137  MWasmRefConvertAnyExtern(MDefinition* ref, wasm::RefType::Kind destTypeKind)
   3138      : MUnaryInstruction(classOpcode, ref), destTypeKind_(destTypeKind) {
   3139    MOZ_ASSERT(destTypeKind_ == wasm::RefType::Kind::Any ||
   3140               destTypeKind_ == wasm::RefType::Kind::Extern);
   3141    setResultType(MIRType::WasmAnyRef);
   3142    setMovable();
   3143  }
   3144 
   3145 public:
   3146  INSTRUCTION_HEADER(WasmRefConvertAnyExtern)
   3147  TRIVIAL_NEW_WRAPPERS
   3148  NAMED_OPERANDS((0, ref))
   3149 
   3150  wasm::RefType::Kind destTypeKind() const { return destTypeKind_; }
   3151 
   3152  wasm::MaybeRefType computeWasmRefType() const override {
   3153    bool nullable = true;
   3154    if (ref()->wasmRefType().isSome()) {
   3155      nullable = ref()->wasmRefType().value().isNullable();
   3156    };
   3157    return wasm::MaybeRefType(wasm::RefType::fromKind(destTypeKind_, nullable));
   3158  }
   3159 
   3160  bool congruentTo(const MDefinition* ins) const override {
   3161    return congruentIfOperandsEqual(ins) &&
   3162           destTypeKind() == ins->toWasmRefConvertAnyExtern()->destTypeKind();
   3163  }
   3164 
   3165  HashNumber valueHash() const override {
   3166    HashNumber hn = MUnaryInstruction::valueHash();
   3167    hn = addU32ToHash(hn, destTypeKind());
   3168    return hn;
   3169  }
   3170 
   3171  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3172 };
   3173 
   3174 // Represents the contents of all fields of a wasm struct.
   3175 // This class will be used for scalar replacement of wasm structs.
   3176 class MWasmStructState : public TempObject {
   3177 private:
   3178  MDefinition* wasmStruct_;
   3179  // Represents the fields of this struct.
   3180  Vector<MDefinition*, 0, JitAllocPolicy> fields_;
   3181 
   3182  explicit MWasmStructState(TempAllocator& alloc, MDefinition* structObject)
   3183      : wasmStruct_(structObject), fields_(alloc) {}
   3184 
   3185 public:
   3186  static MWasmStructState* New(TempAllocator& alloc, MDefinition* structObject);
   3187  static MWasmStructState* Copy(TempAllocator& alloc, MWasmStructState* state);
   3188 
   3189  // Init the fields_ vector.
   3190  [[nodiscard]] bool init();
   3191 
   3192  size_t numFields() const { return fields_.length(); }
   3193  MDefinition* wasmStruct() const { return wasmStruct_; }
   3194 
   3195  // Get the field value based on the position of the field in the struct.
   3196  MDefinition* getField(uint32_t index) const { return fields_[index]; }
   3197  // Set the field offset based on the position of the field in the struct.
   3198  void setField(uint32_t index, MDefinition* def) { fields_[index] = def; }
   3199 };
   3200 
   3201 class MWasmNewStructObject : public MBinaryInstruction,
   3202                             public NoTypePolicy::Data {
   3203 private:
   3204  const wasm::TypeDef* typeDef_;
   3205  bool zeroFields_;
   3206  wasm::TrapSiteDesc trapSiteDesc_;
   3207 
   3208  MWasmNewStructObject(MDefinition* instance, MDefinition* allocSite,
   3209                       const wasm::TypeDef* typeDef, bool zeroFields,
   3210                       const wasm::TrapSiteDesc& trapSiteDesc)
   3211      : MBinaryInstruction(classOpcode, instance, allocSite),
   3212        typeDef_(typeDef),
   3213        zeroFields_(zeroFields),
   3214        trapSiteDesc_(trapSiteDesc) {
   3215    MOZ_ASSERT(typeDef->isStructType());
   3216    setResultType(MIRType::WasmAnyRef);
   3217    initWasmRefType(
   3218        wasm::MaybeRefType(wasm::RefType::fromTypeDef(typeDef_, false)));
   3219  }
   3220 
   3221 public:
   3222  INSTRUCTION_HEADER(WasmNewStructObject)
   3223  TRIVIAL_NEW_WRAPPERS
   3224  NAMED_OPERANDS((0, instance), (1, allocSite))
   3225 
   3226  AliasSet getAliasSet() const override {
   3227    if (js::SupportDifferentialTesting()) {
   3228      // Consider allocations effectful for differential testing.
   3229      return MDefinition::getAliasSet();
   3230    }
   3231    return AliasSet::None();
   3232  }
   3233  const wasm::TypeDef& typeDef() { return *typeDef_; }
   3234  const wasm::StructType& structType() const { return typeDef_->structType(); }
   3235  bool isOutline() const { return typeDef_->structType().hasOOL(); }
   3236  bool zeroFields() const { return zeroFields_; }
   3237  const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; }
   3238  gc::AllocKind allocKind() const { return typeDef_->structType().allocKind_; }
   3239 };
   3240 
   3241 class MWasmNewArrayObject : public MTernaryInstruction,
   3242                            public NoTypePolicy::Data {
   3243 private:
   3244  const wasm::TypeDef* typeDef_;
   3245  bool zeroFields_;
   3246  wasm::TrapSiteDesc trapSiteDesc_;
   3247 
   3248  MWasmNewArrayObject(MDefinition* instance, MDefinition* numElements,
   3249                      MDefinition* allocSite, const wasm::TypeDef* typeDef,
   3250                      bool zeroFields, const wasm::TrapSiteDesc& trapSiteDesc)
   3251      : MTernaryInstruction(classOpcode, instance, numElements, allocSite),
   3252        typeDef_(typeDef),
   3253        zeroFields_(zeroFields),
   3254        trapSiteDesc_(trapSiteDesc) {
   3255    MOZ_ASSERT(typeDef->isArrayType());
   3256    setResultType(MIRType::WasmAnyRef);
   3257    initWasmRefType(
   3258        wasm::MaybeRefType(wasm::RefType::fromTypeDef(typeDef_, false)));
   3259  }
   3260 
   3261 public:
   3262  INSTRUCTION_HEADER(WasmNewArrayObject)
   3263  TRIVIAL_NEW_WRAPPERS
   3264  NAMED_OPERANDS((0, instance), (1, numElements), (2, allocSite))
   3265 
   3266  AliasSet getAliasSet() const override {
   3267    if (js::SupportDifferentialTesting()) {
   3268      // Consider allocations effectful for differential testing.
   3269      return MDefinition::getAliasSet();
   3270    }
   3271    return AliasSet::None();
   3272  }
   3273  const wasm::TypeDef& typeDef() { return *typeDef_; }
   3274  const wasm::ArrayType& arrayType() const { return typeDef_->arrayType(); }
   3275  uint32_t elemSize() const {
   3276    return typeDef_->arrayType().elementType().size();
   3277  }
   3278  bool zeroFields() const { return zeroFields_; }
   3279  const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; }
   3280 };
   3281 
   3282 #undef INSTRUCTION_HEADER
   3283 
   3284 #ifdef ENABLE_WASM_SIMD
   3285 MWasmShuffleSimd128* BuildWasmShuffleSimd128(TempAllocator& alloc,
   3286                                             const int8_t* control,
   3287                                             MDefinition* lhs,
   3288                                             MDefinition* rhs);
   3289 #endif  // ENABLE_WASM_SIMD
   3290 
   3291 }  // namespace jit
   3292 }  // namespace js
   3293 
   3294 #endif /* jit_MIR_wasm_h */