tor-browser

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

MIR.h (325080B)


      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_h
     13 #define jit_MIR_h
     14 
     15 #include "mozilla/Array.h"
     16 #include "mozilla/Attributes.h"
     17 #include "mozilla/EnumSet.h"
     18 #include "mozilla/HashFunctions.h"
     19 #ifdef JS_JITSPEW
     20 #  include "mozilla/Attributes.h"  // MOZ_STACK_CLASS
     21 #endif
     22 #include "mozilla/MacroForEach.h"
     23 #ifdef JS_JITSPEW
     24 #  include "mozilla/Sprintf.h"
     25 #  include "mozilla/Vector.h"
     26 #endif
     27 
     28 #include <algorithm>
     29 
     30 #include "NamespaceImports.h"
     31 
     32 #include "jit/AtomicOp.h"
     33 #include "jit/FixedList.h"
     34 #include "jit/InlineList.h"
     35 #include "jit/JitAllocPolicy.h"
     36 #include "jit/MacroAssembler.h"
     37 #include "jit/MIROpsGenerated.h"
     38 #include "jit/ShuffleAnalysis.h"
     39 #include "jit/TypeData.h"
     40 #include "jit/TypePolicy.h"
     41 #include "js/experimental/JitInfo.h"  // JSJit{Getter,Setter}Op, JSJitInfo
     42 #include "js/HeapAPI.h"
     43 #include "js/ScalarType.h"  // js::Scalar::Type
     44 #include "js/Value.h"
     45 #include "js/Vector.h"
     46 #include "vm/BigIntType.h"
     47 #include "vm/EnvironmentObject.h"
     48 #include "vm/FunctionFlags.h"  // js::FunctionFlags
     49 #include "vm/JSContext.h"
     50 #include "vm/RegExpObject.h"
     51 #include "vm/TypedArrayObject.h"
     52 #include "wasm/WasmJS.h"  // for WasmInstanceObject
     53 #include "wasm/WasmValType.h"
     54 
     55 namespace JS {
     56 struct ExpandoAndGeneration;
     57 }
     58 
     59 namespace js {
     60 
     61 namespace wasm {
     62 class FuncExport;
     63 extern uint32_t MIRTypeToABIResultSize(jit::MIRType);
     64 }  // namespace wasm
     65 
     66 class JS_PUBLIC_API GenericPrinter;
     67 class NativeIteratorListHead;
     68 class StringObject;
     69 
     70 enum class UnaryMathFunction : uint8_t;
     71 
     72 bool CurrentThreadIsIonCompiling();
     73 
     74 namespace jit {
     75 
     76 class CallInfo;
     77 class ShapeListSnapshot;
     78 class ShapeListWithOffsetsSnapshot;
     79 
     80 #ifdef JS_JITSPEW
     81 // Helper for debug printing.  Avoids creating a MIR.h <--> MIRGraph.h cycle.
     82 // Implementation of this needs to see inside `MBasicBlock`; that is possible
     83 // in MIR.cpp since it also includes MIRGraph.h, whereas this file does not.
     84 class MBasicBlock;
     85 uint32_t GetMBasicBlockId(const MBasicBlock* block);
     86 
     87 // Helper class for debug printing.  This class allows `::getExtras` methods
     88 // to add strings to be printed, on a per-MIR-node basis.  The strings are
     89 // copied into storage owned by this class when `::add` is called, so the
     90 // `::getExtras` methods do not need to be concerned about storage management.
     91 class MOZ_STACK_CLASS ExtrasCollector {
     92  mozilla::Vector<UniqueChars, 4> strings_;
     93 
     94 public:
     95  // Add `str` to the collection.  A copy, owned by this object, is made.  In
     96  // case of OOM the call has no effect.
     97  void add(const char* str) {
     98    UniqueChars dup = DuplicateString(str);
     99    if (dup) {
    100      (void)strings_.append(std::move(dup));
    101    }
    102  }
    103  size_t count() const { return strings_.length(); }
    104  UniqueChars get(size_t ix) { return std::move(strings_[ix]); }
    105 };
    106 #endif
    107 
    108 // Forward declarations of MIR types.
    109 #define FORWARD_DECLARE(op) class M##op;
    110 MIR_OPCODE_LIST(FORWARD_DECLARE)
    111 #undef FORWARD_DECLARE
    112 
    113 // MDefinition visitor which ignores non-overloaded visit functions.
    114 class MDefinitionVisitorDefaultNoop {
    115 public:
    116 #define VISIT_INS(op) \
    117  void visit##op(M##op*) {}
    118  MIR_OPCODE_LIST(VISIT_INS)
    119 #undef VISIT_INS
    120 };
    121 
    122 class BytecodeSite;
    123 class CompactBufferWriter;
    124 class Range;
    125 
    126 #define MIR_FLAG_LIST(_)                                                       \
    127  _(InWorklist)                                                                \
    128  _(EmittedAtUses)                                                             \
    129  _(Commutative)                                                               \
    130  _(Movable) /* Allow passes like LICM to move this instruction */             \
    131  _(Lowered) /* (Debug only) has a virtual register */                         \
    132  _(Guard)   /* Not removable if uses == 0 */                                  \
    133                                                                               \
    134  /* Flag an instruction to be considered as a Guard if the instructions       \
    135   * bails out on some inputs.                                                 \
    136   *                                                                           \
    137   * Some optimizations can replace an instruction, and leave its operands     \
    138   * unused. When the type information of the operand got used as a            \
    139   * predicate of the transformation, then we have to flag the operands as     \
    140   * GuardRangeBailouts.                                                       \
    141   *                                                                           \
    142   * This flag prevents further optimization of instructions, which            \
    143   * might remove the run-time checks (bailout conditions) used as a           \
    144   * predicate of the previous transformation.                                 \
    145   */                                                                          \
    146  _(GuardRangeBailouts)                                                        \
    147                                                                               \
    148  /* Some instructions have uses that aren't directly represented in the       \
    149   * graph, and need to be handled specially. As an example, this is used to   \
    150   * keep the flagged instruction in resume points, not substituting with an   \
    151   * UndefinedValue. This can be used by call inlining when a function         \
    152   * argument is not used by the inlined instructions. It is also used         \
    153   * to annotate instructions which were used in removed branches.             \
    154   */                                                                          \
    155  _(ImplicitlyUsed)                                                            \
    156                                                                               \
    157  /* The instruction has been marked dead for lazy removal from resume         \
    158   * points.                                                                   \
    159   */                                                                          \
    160  _(Unused)                                                                    \
    161                                                                               \
    162  /* Marks if the current instruction should go to the bailout paths instead   \
    163   * of producing code as part of the control flow.  This flag can only be set \
    164   * on instructions which are only used by ResumePoint or by other flagged    \
    165   * instructions.                                                             \
    166   */                                                                          \
    167  _(RecoveredOnBailout)                                                        \
    168                                                                               \
    169  /* Some instructions might represent an object, but the memory of these      \
    170   * objects might be incomplete if we have not recovered all the stores which \
    171   * were supposed to happen before. This flag is used to annotate             \
    172   * instructions which might return a pointer to a memory area which is not   \
    173   * yet fully initialized. This flag is used to ensure that stores are        \
    174   * executed before returning the value.                                      \
    175   */                                                                          \
    176  _(IncompleteObject)                                                          \
    177                                                                               \
    178  /* For WebAssembly, there are functions with multiple results.  Instead of   \
    179   * having the results defined by one call instruction, they are instead      \
    180   * captured in subsequent result capture instructions, because modelling     \
    181   * multi-value results in Ion is too complicated.  However since they        \
    182   * capture ambient live registers, it would be an error to move an unrelated \
    183   * instruction between the call and the result capture.  This flag is used   \
    184   * to prevent code motion from moving instructions in invalid ways.          \
    185   */                                                                          \
    186  _(CallResultCapture)                                                         \
    187                                                                               \
    188  /* The current instruction got discarded from the MIR Graph. This is useful  \
    189   * when we want to iterate over resume points and instructions, while        \
    190   * handling instructions which are discarded without reporting to the        \
    191   * iterator.                                                                 \
    192   */                                                                          \
    193  _(Discarded)
    194 
    195 class MDefinition;
    196 class MInstruction;
    197 class MBasicBlock;
    198 class MNode;
    199 class MUse;
    200 class MPhi;
    201 class MIRGraph;
    202 class MResumePoint;
    203 class MControlInstruction;
    204 
    205 // Represents a use of a node.
    206 class MUse : public TempObject, public InlineListNode<MUse> {
    207  // Grant access to setProducerUnchecked.
    208  friend class MDefinition;
    209  friend class MPhi;
    210 
    211  MDefinition* producer_;  // MDefinition that is being used.
    212  MNode* consumer_;        // The node that is using this operand.
    213 
    214  // Low-level unchecked edit method for replaceAllUsesWith and
    215  // MPhi::removeOperand. This doesn't update use lists!
    216  // replaceAllUsesWith and MPhi::removeOperand do that manually.
    217  void setProducerUnchecked(MDefinition* producer) {
    218    MOZ_ASSERT(consumer_);
    219    MOZ_ASSERT(producer_);
    220    MOZ_ASSERT(producer);
    221    producer_ = producer;
    222  }
    223 
    224 public:
    225  // Default constructor for use in vectors.
    226  MUse() : producer_(nullptr), consumer_(nullptr) {}
    227 
    228  // Move constructor for use in vectors. When an MUse is moved, it stays
    229  // in its containing use list.
    230  MUse(MUse&& other)
    231      : InlineListNode<MUse>(std::move(other)),
    232        producer_(other.producer_),
    233        consumer_(other.consumer_) {}
    234 
    235  // Construct an MUse initialized with |producer| and |consumer|.
    236  MUse(MDefinition* producer, MNode* consumer) {
    237    initUnchecked(producer, consumer);
    238  }
    239 
    240  // Set this use, which was previously clear.
    241  inline void init(MDefinition* producer, MNode* consumer);
    242  // Like init, but works even when the use contains uninitialized data.
    243  inline void initUnchecked(MDefinition* producer, MNode* consumer);
    244  // Like initUnchecked, but set the producer to nullptr.
    245  inline void initUncheckedWithoutProducer(MNode* consumer);
    246  // Set this use, which was not previously clear.
    247  inline void replaceProducer(MDefinition* producer);
    248  // Clear this use.
    249  inline void releaseProducer();
    250 
    251  MDefinition* producer() const {
    252    MOZ_ASSERT(producer_ != nullptr);
    253    return producer_;
    254  }
    255  bool hasProducer() const { return producer_ != nullptr; }
    256  MNode* consumer() const {
    257    MOZ_ASSERT(consumer_ != nullptr);
    258    return consumer_;
    259  }
    260 
    261 #ifdef DEBUG
    262  // Return the operand index of this MUse in its consumer. This is DEBUG-only
    263  // as normal code should instead call indexOf on the cast consumer directly,
    264  // to allow it to be devirtualized and inlined.
    265  size_t index() const;
    266 #endif
    267 };
    268 
    269 using MUseIterator = InlineList<MUse>::iterator;
    270 
    271 // A node is an entry in the MIR graph. It has two kinds:
    272 //   MInstruction: an instruction which appears in the IR stream.
    273 //   MResumePoint: a list of instructions that correspond to the state of the
    274 //                 interpreter/Baseline stack.
    275 //
    276 // Nodes can hold references to MDefinitions. Each MDefinition has a list of
    277 // nodes holding such a reference (its use chain).
    278 class MNode : public TempObject {
    279 protected:
    280  enum class Kind { Definition = 0, ResumePoint };
    281 
    282 private:
    283  static const uintptr_t KindMask = 0x1;
    284  uintptr_t blockAndKind_;
    285 
    286  Kind kind() const { return Kind(blockAndKind_ & KindMask); }
    287 
    288 protected:
    289  explicit MNode(const MNode& other) : blockAndKind_(other.blockAndKind_) {}
    290 
    291  MNode(MBasicBlock* block, Kind kind) { setBlockAndKind(block, kind); }
    292 
    293  void setBlockAndKind(MBasicBlock* block, Kind kind) {
    294    blockAndKind_ = uintptr_t(block) | uintptr_t(kind);
    295    MOZ_ASSERT(this->block() == block);
    296  }
    297 
    298  MBasicBlock* definitionBlock() const {
    299    MOZ_ASSERT(isDefinition());
    300    static_assert(unsigned(Kind::Definition) == 0,
    301                  "Code below relies on low bit being 0");
    302    return reinterpret_cast<MBasicBlock*>(blockAndKind_);
    303  }
    304  MBasicBlock* resumePointBlock() const {
    305    MOZ_ASSERT(isResumePoint());
    306    static_assert(unsigned(Kind::ResumePoint) == 1,
    307                  "Code below relies on low bit being 1");
    308    // Use a subtraction: if the caller does block()->foo, the compiler
    309    // will be able to fold it with the load.
    310    return reinterpret_cast<MBasicBlock*>(blockAndKind_ - 1);
    311  }
    312 
    313 public:
    314  // Returns the definition at a given operand.
    315  virtual MDefinition* getOperand(size_t index) const = 0;
    316  virtual size_t numOperands() const = 0;
    317  virtual size_t indexOf(const MUse* u) const = 0;
    318 
    319  bool isDefinition() const { return kind() == Kind::Definition; }
    320  bool isResumePoint() const { return kind() == Kind::ResumePoint; }
    321  MBasicBlock* block() const {
    322    return reinterpret_cast<MBasicBlock*>(blockAndKind_ & ~KindMask);
    323  }
    324  MBasicBlock* caller() const;
    325 
    326  // Sets an already set operand, updating use information. If you're looking
    327  // for setOperand, this is probably what you want.
    328  virtual void replaceOperand(size_t index, MDefinition* operand) = 0;
    329 
    330  // Resets the operand to an uninitialized state, breaking the link
    331  // with the previous operand's producer.
    332  void releaseOperand(size_t index) { getUseFor(index)->releaseProducer(); }
    333  bool hasOperand(size_t index) const {
    334    return getUseFor(index)->hasProducer();
    335  }
    336 
    337  inline MDefinition* toDefinition();
    338  inline MResumePoint* toResumePoint();
    339 
    340  [[nodiscard]] virtual bool writeRecoverData(
    341      CompactBufferWriter& writer) const;
    342 
    343 #ifdef JS_JITSPEW
    344  virtual void dump(GenericPrinter& out) const = 0;
    345  virtual void dump() const = 0;
    346 #endif
    347 
    348 protected:
    349  // Need visibility on getUseFor to avoid O(n^2) complexity.
    350  friend void AssertBasicGraphCoherency(MIRGraph& graph, bool force);
    351 
    352  // Gets the MUse corresponding to given operand.
    353  virtual MUse* getUseFor(size_t index) = 0;
    354  virtual const MUse* getUseFor(size_t index) const = 0;
    355 };
    356 
    357 class AliasSet {
    358 private:
    359  uint32_t flags_;
    360 
    361 public:
    362  enum Flag {
    363    None_ = 0,
    364    ObjectFields = 1 << 0,      // shape, class, slots, length etc.
    365    Element = 1 << 1,           // A Value member of obj->elements or
    366                                // a typed object.
    367    UnboxedElement = 1 << 2,    // An unboxed scalar or reference member of
    368                                // typed object.
    369    DynamicSlot = 1 << 3,       // A Value member of obj->slots.
    370    FixedSlot = 1 << 4,         // A Value member of obj->fixedSlots().
    371    DOMProperty = 1 << 5,       // A DOM property
    372    WasmInstanceData = 1 << 6,  // An asm.js/wasm private global var
    373    WasmHeap = 1 << 7,          // An asm.js/wasm heap load
    374    WasmHeapMeta = 1 << 8,      // The asm.js/wasm heap base pointer and
    375                                // bounds check limit, in Instance.
    376    ArrayBufferViewLengthOrOffset =
    377        1 << 9,                  // An array buffer view's length or byteOffset
    378    WasmGlobalCell = 1 << 10,    // A wasm global cell
    379    WasmTableElement = 1 << 11,  // An element of a wasm table
    380    WasmTableMeta = 1 << 12,     // A wasm table elements pointer and
    381                                 // length field, in instance data.
    382    WasmStackResult = 1 << 13,   // A stack result from the current function
    383 
    384    // JSContext's exception state. This is used on instructions like MThrow
    385    // or MNewArrayDynamicLength that throw exceptions (other than OOM) but have
    386    // no other side effect, to ensure that they get their own up-to-date resume
    387    // point. (This resume point will be used when constructing the Baseline
    388    // frame during exception bailouts.)
    389    ExceptionState = 1 << 14,
    390 
    391    // Used for instructions that load the privateSlot of DOM proxies and
    392    // the ExpandoAndGeneration.
    393    DOMProxyExpando = 1 << 15,
    394 
    395    // Hash table of a Map/Set/WeakMap/WeakSet object.
    396    MapOrSetHashTable = 1 << 16,
    397 
    398    // Internal state of the random number generator
    399    RNG = 1 << 17,
    400 
    401    // The pendingException slot on the wasm instance object.
    402    WasmPendingException = 1 << 18,
    403 
    404    // The fuzzilliHash slot
    405    FuzzilliHash = 1 << 19,
    406 
    407    // The WasmStructObject::inlineData_[..] storage area
    408    WasmStructInlineDataArea = 1 << 20,
    409 
    410    // The WasmStructObject::outlineData_ pointer only
    411    WasmStructOutlineDataPointer = 1 << 21,
    412 
    413    // The malloc'd block that WasmStructObject::outlineData_ points at
    414    WasmStructOutlineDataArea = 1 << 22,
    415 
    416    // The WasmArrayObject::numElements_ field
    417    WasmArrayNumElements = 1 << 23,
    418 
    419    // The WasmArrayObject::data_ pointer only
    420    WasmArrayDataPointer = 1 << 24,
    421 
    422    // The malloc'd block that WasmArrayObject::data_ points at
    423    WasmArrayDataArea = 1 << 25,
    424 
    425    // The generation counter associated with the global object
    426    GlobalGenerationCounter = 1 << 26,
    427 
    428    // The SharedArrayRawBuffer::length field.
    429    SharedArrayRawBufferLength = 1 << 27,
    430 
    431    Last = SharedArrayRawBufferLength,
    432 
    433    Any = Last | (Last - 1),
    434    NumCategories = 28,
    435 
    436    // Indicates load or store.
    437    Store_ = 1 << 31
    438  };
    439 
    440  static_assert((1 << NumCategories) - 1 == Any,
    441                "NumCategories must include all flags present in Any");
    442 
    443  explicit constexpr AliasSet(uint32_t flags) : flags_(flags) {}
    444 
    445 public:
    446  inline constexpr bool isNone() const { return flags_ == None_; }
    447  constexpr uint32_t flags() const { return flags_ & Any; }
    448  inline constexpr bool isStore() const { return !!(flags_ & Store_); }
    449  inline constexpr bool isLoad() const { return !isStore() && !isNone(); }
    450  inline constexpr AliasSet operator|(const AliasSet& other) const {
    451    return AliasSet(flags_ | other.flags_);
    452  }
    453  inline constexpr AliasSet operator&(const AliasSet& other) const {
    454    return AliasSet(flags_ & other.flags_);
    455  }
    456  inline constexpr AliasSet operator~() const { return AliasSet(~flags_); }
    457  static constexpr AliasSet None() { return AliasSet(None_); }
    458  static constexpr AliasSet Load(uint32_t flags) {
    459    MOZ_ASSERT(flags && !(flags & Store_));
    460    return AliasSet(flags);
    461  }
    462  static constexpr AliasSet Store(uint32_t flags) {
    463    MOZ_ASSERT(flags && !(flags & Store_));
    464    return AliasSet(flags | Store_);
    465  }
    466 };
    467 
    468 using MDefinitionVector = Vector<MDefinition*, 6, JitAllocPolicy>;
    469 using MInstructionVector = Vector<MInstruction*, 6, JitAllocPolicy>;
    470 
    471 // When a floating-point value is used by nodes which would prefer to
    472 // receive integer inputs, we may be able to help by computing our result
    473 // into an integer directly.
    474 //
    475 // A value can be truncated in 4 differents ways:
    476 //   1. Ignore Infinities (x / 0 --> 0).
    477 //   2. Ignore overflow (INT_MIN / -1 == (INT_MAX + 1) --> INT_MIN)
    478 //   3. Ignore negative zeros. (-0 --> 0)
    479 //   4. Ignore remainder. (3 / 4 --> 0)
    480 //
    481 // Indirect truncation is used to represent that we are interested in the
    482 // truncated result, but only if it can safely flow into operations which
    483 // are computed modulo 2^32, such as (2) and (3). Infinities are not safe,
    484 // as they would have absorbed other math operations. Remainders are not
    485 // safe, as fractions can be scaled up by multiplication.
    486 //
    487 // Division is a particularly interesting node here because it covers all 4
    488 // cases even when its own operands are integers.
    489 //
    490 // Note that these enum values are ordered from least value-modifying to
    491 // most value-modifying, and code relies on this ordering.
    492 enum class TruncateKind {
    493  // No correction.
    494  NoTruncate = 0,
    495  // An integer is desired, but we can't skip bailout checks.
    496  TruncateAfterBailouts = 1,
    497  // The value will be truncated after some arithmetic (see above).
    498  IndirectTruncate = 2,
    499  // Direct and infallible truncation to int32.
    500  Truncate = 3
    501 };
    502 
    503 // An MDefinition is an SSA name.
    504 class MDefinition : public MNode {
    505  friend class MBasicBlock;
    506 
    507 public:
    508  enum class Opcode : uint16_t {
    509 #define DEFINE_OPCODES(op) op,
    510    MIR_OPCODE_LIST(DEFINE_OPCODES)
    511 #undef DEFINE_OPCODES
    512  };
    513 
    514 private:
    515  InlineList<MUse> uses_;  // Use chain.
    516  uint32_t id_;            // Instruction ID, which after block re-ordering
    517                           // is sorted within a basic block.
    518  Opcode op_;              // Opcode.
    519  uint16_t flags_;         // Bit flags.
    520  Range* range_;           // Any computed range for this def.
    521  union {
    522    MDefinition*
    523        loadDependency_;  // Implicit dependency (store, call, etc.) of this
    524                          // instruction. Used by alias analysis, GVN and LICM.
    525    uint32_t virtualRegister_;  // Used by lowering to map definitions to
    526                                // virtual registers.
    527  };
    528 
    529  // Track bailouts by storing the current pc in MIR instruction. Also used
    530  // for profiling and keeping track of what the last known pc was.
    531  const BytecodeSite* trackedSite_;
    532 
    533  // For nodes of MIRType::WasmAnyRef, a type precisely describing the value of
    534  // the node. It is set by the "Track wasm ref types" pass in Ion, and enables
    535  // GVN and LICM to perform more advanced optimizations (such as allowing
    536  // instructions to move if the source values are non-null, or omitting casts
    537  // that are statically known to succeed or fail).
    538  wasm::MaybeRefType wasmRefType_;
    539 
    540  // If we generate a bailout path for this instruction, this is the
    541  // bailout kind that will be encoded in the snapshot. When we bail out,
    542  // FinishBailoutToBaseline may take action based on the bailout kind to
    543  // prevent bailout loops. (For example, if an instruction bails out after
    544  // being hoisted by LICM, we will disable LICM when recompiling the script.)
    545  BailoutKind bailoutKind_;
    546 
    547  MIRType resultType_;  // Representation of result type.
    548 
    549 private:
    550  enum Flag {
    551    None = 0,
    552 #define DEFINE_FLAG(flag) flag,
    553    MIR_FLAG_LIST(DEFINE_FLAG)
    554 #undef DEFINE_FLAG
    555        Total
    556  };
    557 
    558  bool hasFlags(uint32_t flags) const { return (flags_ & flags) == flags; }
    559  void removeFlags(uint32_t flags) { flags_ &= ~flags; }
    560  void setFlags(uint32_t flags) { flags_ |= flags; }
    561 
    562  // Calling isDefinition or isResumePoint on MDefinition is unnecessary.
    563  bool isDefinition() const = delete;
    564  bool isResumePoint() const = delete;
    565 
    566 protected:
    567  void setInstructionBlock(MBasicBlock* block, const BytecodeSite* site) {
    568    MOZ_ASSERT(isInstruction());
    569    setBlockAndKind(block, Kind::Definition);
    570    setTrackedSite(site);
    571  }
    572 
    573  void setPhiBlock(MBasicBlock* block) {
    574    MOZ_ASSERT(isPhi());
    575    setBlockAndKind(block, Kind::Definition);
    576  }
    577 
    578  static HashNumber addU32ToHash(HashNumber hash, uint32_t data) {
    579    return data + (hash << 6) + (hash << 16) - hash;
    580  }
    581 
    582  static HashNumber addU64ToHash(HashNumber hash, uint64_t data) {
    583    hash = addU32ToHash(hash, uint32_t(data));
    584    hash = addU32ToHash(hash, uint32_t(data >> 32));
    585    return hash;
    586  }
    587 
    588 public:
    589  explicit MDefinition(Opcode op)
    590      : MNode(nullptr, Kind::Definition),
    591        id_(0),
    592        op_(op),
    593        flags_(0),
    594        range_(nullptr),
    595        loadDependency_(nullptr),
    596        trackedSite_(nullptr),
    597        bailoutKind_(BailoutKind::Unknown),
    598        resultType_(MIRType::None) {}
    599 
    600  // Copying a definition leaves the list of uses empty.
    601  explicit MDefinition(const MDefinition& other)
    602      : MNode(other),
    603        id_(0),
    604        op_(other.op_),
    605        flags_(other.flags_),
    606        range_(other.range_),
    607        loadDependency_(other.loadDependency_),
    608        trackedSite_(other.trackedSite_),
    609        bailoutKind_(other.bailoutKind_),
    610        resultType_(other.resultType_) {}
    611 
    612  Opcode op() const { return op_; }
    613 
    614 #ifdef JS_JITSPEW
    615  const char* opName() const;
    616  void printName(GenericPrinter& out) const;
    617  static void PrintOpcodeName(GenericPrinter& out, Opcode op);
    618  virtual void printOpcode(GenericPrinter& out) const;
    619  void dump(GenericPrinter& out) const override;
    620  void dump() const override;
    621  void dumpLocation(GenericPrinter& out) const;
    622  void dumpLocation() const;
    623  // Dump any other stuff the node wants to have printed in `extras`.  The
    624  // added strings are copied, with the `ExtrasCollector` taking ownership of
    625  // the copies.
    626  virtual void getExtras(ExtrasCollector* extras) const {}
    627 #endif
    628 
    629  // Also for LICM. Test whether this definition is likely to be a call, which
    630  // would clobber all or many of the floating-point registers, such that
    631  // hoisting floating-point constants out of containing loops isn't likely to
    632  // be worthwhile.
    633  virtual bool possiblyCalls() const { return false; }
    634 
    635  MBasicBlock* block() const { return definitionBlock(); }
    636 
    637 private:
    638  void setTrackedSite(const BytecodeSite* site) {
    639    MOZ_ASSERT(site);
    640    trackedSite_ = site;
    641  }
    642 
    643 public:
    644  const BytecodeSite* trackedSite() const {
    645    MOZ_ASSERT(trackedSite_,
    646               "missing tracked bytecode site; node not assigned to a block?");
    647    return trackedSite_;
    648  }
    649 
    650  BailoutKind bailoutKind() const { return bailoutKind_; }
    651  void setBailoutKind(BailoutKind kind) { bailoutKind_ = kind; }
    652 
    653  // Return the range of this value, *before* any bailout checks. Contrast
    654  // this with the type() method, and the Range constructor which takes an
    655  // MDefinition*, which describe the value *after* any bailout checks.
    656  //
    657  // Warning: Range analysis is removing the bit-operations such as '| 0' at
    658  // the end of the transformations. Using this function to analyse any
    659  // operands after the truncate phase of the range analysis will lead to
    660  // errors. Instead, one should define the collectRangeInfoPreTrunc() to set
    661  // the right set of flags which are dependent on the range of the inputs.
    662  Range* range() const {
    663    MOZ_ASSERT(type() != MIRType::None);
    664    return range_;
    665  }
    666  void setRange(Range* range) {
    667    MOZ_ASSERT(type() != MIRType::None);
    668    range_ = range;
    669  }
    670 
    671  virtual HashNumber valueHash() const;
    672  virtual bool congruentTo(const MDefinition* ins) const { return false; }
    673  const MDefinition* skipObjectGuards() const;
    674 
    675  // Note that, for a call `congruentIfOperandsEqual(ins)` inside some class
    676  // MFoo, if `true` is returned then we are ensured that `ins` is also an
    677  // MFoo, so it is safe to do `ins->toMFoo()` without first checking whether
    678  // `ins->isMFoo()`.
    679  bool congruentIfOperandsEqual(const MDefinition* ins) const;
    680 
    681  virtual MDefinition* foldsTo(TempAllocator& alloc);
    682  virtual void analyzeEdgeCasesForward();
    683  virtual void analyzeEdgeCasesBackward();
    684 
    685  // |canTruncate| reports if this instruction supports truncation. If
    686  // |canTruncate| function returns true, then the |truncate| function is
    687  // called on the same instruction to mutate the instruction, such as updating
    688  // the return type, the range and the specialization of the instruction.
    689  virtual bool canTruncate() const;
    690  virtual void truncate(TruncateKind kind);
    691 
    692  // Determine what kind of truncate this node prefers for the operand at the
    693  // given index.
    694  virtual TruncateKind operandTruncateKind(size_t index) const;
    695 
    696  // Compute an absolute or symbolic range for the value of this node.
    697  virtual void computeRange(TempAllocator& alloc) {}
    698 
    699  // Collect information from the pre-truncated ranges.
    700  virtual void collectRangeInfoPreTrunc() {}
    701 
    702  uint32_t id() const {
    703    MOZ_ASSERT(block());
    704    return id_;
    705  }
    706  void setId(uint32_t id) { id_ = id; }
    707 
    708 #define FLAG_ACCESSOR(flag)                            \
    709  bool is##flag() const {                              \
    710    static_assert(Flag::Total <= sizeof(flags_) * 8,   \
    711                  "Flags should fit in flags_ field"); \
    712    return hasFlags(1 << flag);                        \
    713  }                                                    \
    714  void set##flag() {                                   \
    715    MOZ_ASSERT(!hasFlags(1 << flag));                  \
    716    setFlags(1 << flag);                               \
    717  }                                                    \
    718  void setNot##flag() {                                \
    719    MOZ_ASSERT(hasFlags(1 << flag));                   \
    720    removeFlags(1 << flag);                            \
    721  }                                                    \
    722  void set##flag##Unchecked() { setFlags(1 << flag); } \
    723  void setNot##flag##Unchecked() { removeFlags(1 << flag); }
    724 
    725  MIR_FLAG_LIST(FLAG_ACCESSOR)
    726 #undef FLAG_ACCESSOR
    727 
    728  bool hasAnyFlags() const { return flags_ != 0; }
    729 
    730  // Return the type of this value. This may be speculative, and enforced
    731  // dynamically with the use of bailout checks. If all the bailout checks
    732  // pass, the value will have this type.
    733  //
    734  // Unless this is an MUrsh that has bailouts disabled, which, as a special
    735  // case, may return a value in (INT32_MAX,UINT32_MAX] even when its type()
    736  // is MIRType::Int32.
    737  MIRType type() const { return resultType_; }
    738 
    739  // Default EnumSet serialization is based on the enum's underlying type, which
    740  // means uint8_t for MIRType. To store all possible MIRType values we need at
    741  // least uint32_t.
    742  using MIRTypeEnumSet = mozilla::EnumSet<MIRType, uint32_t>;
    743  static_assert(static_cast<size_t>(MIRType::Last) <
    744                sizeof(MIRTypeEnumSet::serializedType) * CHAR_BIT);
    745 
    746  // Get the wasm reference type stored on the node. Do NOT use in congruentTo,
    747  // as this value can change throughout the optimization process. See
    748  // ReplaceAllUsesWith in ValueNumbering.cpp.
    749  wasm::MaybeRefType wasmRefType() const { return wasmRefType_; }
    750 
    751  // Sets the wasm reference type stored on the node. Does not check if there
    752  // was already a type on the node, which may lead to bugs; consider using
    753  // `initWasmRefType` instead if it applies.
    754  void setWasmRefType(wasm::MaybeRefType refType) { wasmRefType_ = refType; }
    755 
    756  // Sets the wasm reference type stored on the node. To be used for nodes that
    757  // have a fixed ref type that is set up front, which is a common case. Must be
    758  // called only during the node constructor and never again afterward.
    759  void initWasmRefType(wasm::MaybeRefType refType) {
    760    MOZ_ASSERT(!wasmRefType_);
    761    setWasmRefType(refType);
    762  }
    763 
    764  // Compute the wasm reference type for this node. This method is called by
    765  // updateWasmRefType. By default it returns the ref type stored on the node,
    766  // which means it will return either Nothing or a value set by
    767  // initWasmRefType.
    768  virtual wasm::MaybeRefType computeWasmRefType() const { return wasmRefType_; }
    769 
    770  // Return true if the result type is a member of the given types.
    771  bool typeIsOneOf(MIRTypeEnumSet types) const {
    772    MOZ_ASSERT(!types.isEmpty());
    773    return types.contains(type());
    774  }
    775 
    776  // Float32 specialization operations (see big comment in IonAnalysis before
    777  // the Float32 specialization algorithm).
    778  virtual bool isFloat32Commutative() const { return false; }
    779  virtual bool canProduceFloat32() const { return false; }
    780  virtual bool canConsumeFloat32(MUse* use) const { return false; }
    781  virtual void trySpecializeFloat32(TempAllocator& alloc) {}
    782 #ifdef DEBUG
    783  // Used during the pass that checks that Float32 flow into valid MDefinitions
    784  virtual bool isConsistentFloat32Use(MUse* use) const {
    785    return type() == MIRType::Float32 || canConsumeFloat32(use);
    786  }
    787 #endif
    788 
    789  // Returns the beginning of this definition's use chain.
    790  MUseIterator usesBegin() const { return uses_.begin(); }
    791 
    792  // Returns the end of this definition's use chain.
    793  MUseIterator usesEnd() const { return uses_.end(); }
    794 
    795  bool canEmitAtUses() const { return !isEmittedAtUses(); }
    796 
    797  // Removes a use at the given position
    798  void removeUse(MUse* use) { uses_.remove(use); }
    799 
    800 #if defined(DEBUG) || defined(JS_JITSPEW)
    801  // Number of uses of this instruction. This function is only available
    802  // in DEBUG mode since it requires traversing the list. Most users should
    803  // use hasUses() or hasOneUse() instead.
    804  size_t useCount() const;
    805 
    806  // Number of uses of this instruction (only counting MDefinitions, ignoring
    807  // MResumePoints). This function is only available in DEBUG mode since it
    808  // requires traversing the list. Most users should use hasUses() or
    809  // hasOneUse() instead.
    810  size_t defUseCount() const;
    811 #endif
    812 
    813  // Test whether this MDefinition has exactly one use.
    814  bool hasOneUse() const;
    815 
    816  // Test whether this MDefinition has exactly one use.
    817  // (only counting MDefinitions, ignoring MResumePoints)
    818  bool hasOneDefUse() const;
    819 
    820  // Test whether this MDefinition has exactly one live use. (only counting
    821  // MDefinitions which are not recovered on bailout and ignoring MResumePoints)
    822  bool hasOneLiveDefUse() const;
    823 
    824  // Test whether this MDefinition has at least one use.
    825  // (only counting MDefinitions, ignoring MResumePoints)
    826  bool hasDefUses() const;
    827 
    828  // Test whether this MDefinition has at least one non-recovered use.
    829  // (only counting MDefinitions, ignoring MResumePoints)
    830  bool hasLiveDefUses() const;
    831 
    832  bool hasUses() const { return !uses_.empty(); }
    833 
    834  // If this MDefinition has a single use (ignoring MResumePoints), returns that
    835  // use's definition. Else returns nullptr.
    836  MDefinition* maybeSingleDefUse() const;
    837 
    838  // Returns the most recently added use (ignoring MResumePoints) for this
    839  // MDefinition. Returns nullptr if there are no uses. Note that this relies on
    840  // addUse adding new uses to the front of the list, and should only be called
    841  // during MIR building (before optimization passes make changes to the uses).
    842  MDefinition* maybeMostRecentlyAddedDefUse() const;
    843 
    844  void addUse(MUse* use) {
    845    MOZ_ASSERT(use->producer() == this);
    846    uses_.pushFront(use);
    847  }
    848  void addUseUnchecked(MUse* use) {
    849    MOZ_ASSERT(use->producer() == this);
    850    uses_.pushFrontUnchecked(use);
    851  }
    852  void replaceUse(MUse* old, MUse* now) {
    853    MOZ_ASSERT(now->producer() == this);
    854    uses_.replace(old, now);
    855  }
    856 
    857  // Replace the current instruction by a dominating instruction |dom| in all
    858  // uses of the current instruction.
    859  void replaceAllUsesWith(MDefinition* dom);
    860 
    861  // Like replaceAllUsesWith, but doesn't set ImplicitlyUsed on |this|'s
    862  // operands.
    863  void justReplaceAllUsesWith(MDefinition* dom);
    864 
    865  // Replace the current instruction by an optimized-out constant in all uses
    866  // of the current instruction. Note, that optimized-out constant should not
    867  // be observed, and thus they should not flow in any computation.
    868  [[nodiscard]] bool optimizeOutAllUses(TempAllocator& alloc);
    869 
    870  // Replace the current instruction by a dominating instruction |dom| in all
    871  // instruction, but keep the current instruction for resume point and
    872  // instruction which are recovered on bailouts.
    873  void replaceAllLiveUsesWith(MDefinition* dom);
    874 
    875  void setVirtualRegister(uint32_t vreg) {
    876    virtualRegister_ = vreg;
    877    setLoweredUnchecked();
    878  }
    879  uint32_t virtualRegister() const {
    880    MOZ_ASSERT(isLowered());
    881    return virtualRegister_;
    882  }
    883 
    884 public:
    885  // Opcode testing and casts.
    886  template <typename MIRType>
    887  bool is() const {
    888    return op() == MIRType::classOpcode;
    889  }
    890  template <typename MIRType>
    891  MIRType* to() {
    892    MOZ_ASSERT(this->is<MIRType>());
    893    return static_cast<MIRType*>(this);
    894  }
    895  template <typename MIRType>
    896  const MIRType* to() const {
    897    MOZ_ASSERT(this->is<MIRType>());
    898    return static_cast<const MIRType*>(this);
    899  }
    900 #define OPCODE_CASTS(opcode)                                \
    901  bool is##opcode() const { return this->is<M##opcode>(); } \
    902  M##opcode* to##opcode() { return this->to<M##opcode>(); } \
    903  const M##opcode* to##opcode() const { return this->to<M##opcode>(); }
    904  MIR_OPCODE_LIST(OPCODE_CASTS)
    905 #undef OPCODE_CASTS
    906 
    907  inline MConstant* maybeConstantValue();
    908 
    909  inline MInstruction* toInstruction();
    910  inline const MInstruction* toInstruction() const;
    911  bool isInstruction() const { return !isPhi(); }
    912 
    913  virtual bool isControlInstruction() const { return false; }
    914  inline MControlInstruction* toControlInstruction();
    915 
    916  void setResultType(MIRType type) { resultType_ = type; }
    917  virtual AliasSet getAliasSet() const {
    918    // Instructions are effectful by default.
    919    return AliasSet::Store(AliasSet::Any);
    920  }
    921 
    922 #ifdef DEBUG
    923  bool hasDefaultAliasSet() const {
    924    AliasSet set = getAliasSet();
    925    return set.isStore() && set.flags() == AliasSet::Flag::Any;
    926  }
    927 #endif
    928 
    929  MDefinition* dependency() const {
    930    MOZ_ASSERT_IF(getAliasSet().isStore(), !loadDependency_);
    931    return loadDependency_;
    932  }
    933  void setDependency(MDefinition* dependency) {
    934    MOZ_ASSERT(!getAliasSet().isStore());
    935    loadDependency_ = dependency;
    936  }
    937  bool isEffectful() const { return getAliasSet().isStore(); }
    938 
    939 #ifdef DEBUG
    940  bool needsResumePoint() const {
    941    // Return whether this instruction should have its own resume point.
    942    return isEffectful();
    943  }
    944 #endif
    945 
    946  enum class AliasType : uint32_t { NoAlias = 0, MayAlias = 1, MustAlias = 2 };
    947  virtual AliasType mightAlias(const MDefinition* store) const {
    948    // Return whether this load may depend on the specified store, given
    949    // that the alias sets intersect. This may be refined to exclude
    950    // possible aliasing in cases where alias set flags are too imprecise.
    951    if (!(getAliasSet().flags() & store->getAliasSet().flags())) {
    952      return AliasType::NoAlias;
    953    }
    954    MOZ_ASSERT(!isEffectful() && store->isEffectful());
    955    return AliasType::MayAlias;
    956  }
    957 
    958  virtual bool canRecoverOnBailout() const { return false; }
    959 };
    960 
    961 // An MUseDefIterator walks over uses in a definition, skipping any use that is
    962 // not a definition. Items from the use list must not be deleted during
    963 // iteration.
    964 class MUseDefIterator {
    965  const MDefinition* def_;
    966  MUseIterator current_;
    967 
    968  MUseIterator search(MUseIterator start) {
    969    MUseIterator i(start);
    970    for (; i != def_->usesEnd(); i++) {
    971      if (i->consumer()->isDefinition()) {
    972        return i;
    973      }
    974    }
    975    return def_->usesEnd();
    976  }
    977 
    978 public:
    979  explicit MUseDefIterator(const MDefinition* def)
    980      : def_(def), current_(search(def->usesBegin())) {}
    981 
    982  explicit operator bool() const { return current_ != def_->usesEnd(); }
    983  MUseDefIterator operator++() {
    984    MOZ_ASSERT(current_ != def_->usesEnd());
    985    ++current_;
    986    current_ = search(current_);
    987    return *this;
    988  }
    989  MUseDefIterator operator++(int) {
    990    MUseDefIterator old(*this);
    991    operator++();
    992    return old;
    993  }
    994  MUse* use() const { return *current_; }
    995  MDefinition* def() const { return current_->consumer()->toDefinition(); }
    996 };
    997 
    998 // Helper class to check that GC pointers embedded in MIR instructions are not
    999 // in the nursery. Off-thread compilation and nursery GCs can happen in
   1000 // parallel. Nursery pointers are handled with MNurseryObject and the
   1001 // nurseryObjects lists in WarpSnapshot and IonScript.
   1002 //
   1003 // These GC things are rooted through the WarpSnapshot. Compacting GCs cancel
   1004 // off-thread compilations.
   1005 template <typename T>
   1006 class CompilerGCPointer {
   1007  js::gc::Cell* ptr_;
   1008 
   1009 public:
   1010  explicit CompilerGCPointer(T ptr) : ptr_(ptr) {
   1011    MOZ_ASSERT_IF(ptr, !IsInsideNursery(ptr));
   1012    MOZ_ASSERT_IF(!CurrentThreadIsIonCompiling(), TlsContext.get()->suppressGC);
   1013  }
   1014 
   1015  operator T() const { return static_cast<T>(ptr_); }
   1016  T operator->() const { return static_cast<T>(ptr_); }
   1017 
   1018 private:
   1019  CompilerGCPointer() = delete;
   1020  CompilerGCPointer(const CompilerGCPointer<T>&) = delete;
   1021  CompilerGCPointer<T>& operator=(const CompilerGCPointer<T>&) = delete;
   1022 };
   1023 
   1024 using CompilerObject = CompilerGCPointer<JSObject*>;
   1025 using CompilerNativeObject = CompilerGCPointer<NativeObject*>;
   1026 using CompilerFunction = CompilerGCPointer<JSFunction*>;
   1027 using CompilerBaseScript = CompilerGCPointer<BaseScript*>;
   1028 using CompilerPropertyName = CompilerGCPointer<PropertyName*>;
   1029 using CompilerShape = CompilerGCPointer<Shape*>;
   1030 using CompilerGetterSetter = CompilerGCPointer<GetterSetter*>;
   1031 
   1032 // An instruction is an SSA name that is inserted into a basic block's IR
   1033 // stream.
   1034 class MInstruction : public MDefinition, public InlineListNode<MInstruction> {
   1035  MResumePoint* resumePoint_;
   1036 
   1037 protected:
   1038  // All MInstructions are using the "MFoo::New(alloc)" notation instead of
   1039  // the TempObject new operator. This code redefines the new operator as
   1040  // protected, and delegates to the TempObject new operator. Thus, the
   1041  // following code prevents calls to "new(alloc) MFoo" outside the MFoo
   1042  // members.
   1043  inline void* operator new(size_t nbytes,
   1044                            TempAllocator::Fallible view) noexcept(true) {
   1045    return TempObject::operator new(nbytes, view);
   1046  }
   1047  inline void* operator new(size_t nbytes, TempAllocator& alloc) {
   1048    return TempObject::operator new(nbytes, alloc);
   1049  }
   1050  template <class T>
   1051  inline void* operator new(size_t nbytes, T* pos) {
   1052    return TempObject::operator new(nbytes, pos);
   1053  }
   1054 
   1055 public:
   1056  explicit MInstruction(Opcode op) : MDefinition(op), resumePoint_(nullptr) {}
   1057 
   1058  // Copying an instruction leaves the resume point as empty.
   1059  explicit MInstruction(const MInstruction& other)
   1060      : MDefinition(other), resumePoint_(nullptr) {}
   1061 
   1062  // Convenient function used for replacing a load by the value of the store
   1063  // if the types are match, and boxing the value if they do not match.
   1064  MDefinition* foldsToStore(TempAllocator& alloc);
   1065 
   1066  void setResumePoint(MResumePoint* resumePoint);
   1067  void stealResumePoint(MInstruction* other);
   1068 
   1069  void moveResumePointAsEntry();
   1070  void clearResumePoint();
   1071  MResumePoint* resumePoint() const { return resumePoint_; }
   1072 
   1073  // For instructions which can be cloned with new inputs, with all other
   1074  // information being the same. clone() implementations do not need to worry
   1075  // about cloning generic MInstruction/MDefinition state like flags and
   1076  // resume points.
   1077  virtual bool canClone() const { return false; }
   1078  virtual MInstruction* clone(TempAllocator& alloc,
   1079                              const MDefinitionVector& inputs) const {
   1080    MOZ_CRASH();
   1081  }
   1082 
   1083  // Instructions needing to hook into type analysis should return a
   1084  // TypePolicy.
   1085  virtual const TypePolicy* typePolicy() = 0;
   1086  virtual MIRType typePolicySpecialization() = 0;
   1087 };
   1088 
   1089 // Note: GenerateOpcodeFiles.py generates MOpcodesGenerated.h based on the
   1090 // INSTRUCTION_HEADER* macros.
   1091 #define INSTRUCTION_HEADER_WITHOUT_TYPEPOLICY(opcode) \
   1092  static const Opcode classOpcode = Opcode::opcode;   \
   1093  using MThisOpcode = M##opcode;
   1094 
   1095 #define INSTRUCTION_HEADER(opcode)                 \
   1096  INSTRUCTION_HEADER_WITHOUT_TYPEPOLICY(opcode)    \
   1097  virtual const TypePolicy* typePolicy() override; \
   1098  virtual MIRType typePolicySpecialization() override;
   1099 
   1100 #define ALLOW_CLONE(typename)                                                \
   1101  bool canClone() const override { return true; }                            \
   1102  MInstruction* clone(TempAllocator& alloc, const MDefinitionVector& inputs) \
   1103      const override {                                                       \
   1104    MOZ_ASSERT(numOperands() == inputs.length());                            \
   1105    MInstruction* res = new (alloc) typename(*this);                         \
   1106    if (!res) {                                                              \
   1107      return nullptr;                                                        \
   1108    }                                                                        \
   1109    for (size_t i = 0; i < numOperands(); i++) {                             \
   1110      res->replaceOperand(i, inputs[i]);                                     \
   1111    }                                                                        \
   1112    return res;                                                              \
   1113  }
   1114 
   1115 // Adds MFoo::New functions which are mirroring the arguments of the
   1116 // constructors. Opcodes which are using this macro can be called with a
   1117 // TempAllocator, or the fallible version of the TempAllocator.
   1118 #define TRIVIAL_NEW_WRAPPERS                                               \
   1119  template <typename... Args>                                              \
   1120  static MThisOpcode* New(TempAllocator& alloc, Args&&... args) {          \
   1121    return new (alloc) MThisOpcode(std::forward<Args>(args)...);           \
   1122  }                                                                        \
   1123  template <typename... Args>                                              \
   1124  static MThisOpcode* New(TempAllocator::Fallible alloc, Args&&... args) { \
   1125    return new (alloc) MThisOpcode(std::forward<Args>(args)...);           \
   1126  }
   1127 
   1128 // These macros are used as a syntactic sugar for writting getOperand
   1129 // accessors. They are meant to be used in the body of MIR Instructions as
   1130 // follows:
   1131 //
   1132 //   public:
   1133 //     INSTRUCTION_HEADER(Foo)
   1134 //     NAMED_OPERANDS((0, lhs), (1, rhs))
   1135 //
   1136 // The above example defines 2 accessors, one named "lhs" accessing the first
   1137 // operand, and a one named "rhs" accessing the second operand.
   1138 #define NAMED_OPERAND_ACCESSOR(Index, Name) \
   1139  MDefinition* Name() const { return getOperand(Index); }
   1140 #define NAMED_OPERAND_ACCESSOR_APPLY(Args) NAMED_OPERAND_ACCESSOR Args
   1141 #define NAMED_OPERANDS(...) \
   1142  MOZ_FOR_EACH(NAMED_OPERAND_ACCESSOR_APPLY, (), (__VA_ARGS__))
   1143 
   1144 template <size_t Arity>
   1145 class MAryInstruction : public MInstruction {
   1146  MOZ_NO_UNIQUE_ADDRESS mozilla::Array<MUse, Arity> operands_;
   1147 
   1148 protected:
   1149  MUse* getUseFor(size_t index) final { return &operands_[index]; }
   1150  const MUse* getUseFor(size_t index) const final { return &operands_[index]; }
   1151  void initOperand(size_t index, MDefinition* operand) {
   1152    operands_[index].init(operand, this);
   1153  }
   1154 
   1155 public:
   1156  MDefinition* getOperand(size_t index) const final {
   1157    return operands_[index].producer();
   1158  }
   1159  size_t numOperands() const final { return Arity; }
   1160 #ifdef DEBUG
   1161  static const size_t staticNumOperands = Arity;
   1162 #endif
   1163  size_t indexOf(const MUse* u) const final {
   1164    MOZ_ASSERT(u >= &operands_[0]);
   1165    MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
   1166    return u - &operands_[0];
   1167  }
   1168  void replaceOperand(size_t index, MDefinition* operand) final {
   1169    operands_[index].replaceProducer(operand);
   1170  }
   1171 
   1172  explicit MAryInstruction(Opcode op) : MInstruction(op) {}
   1173 
   1174  explicit MAryInstruction(const MAryInstruction<Arity>& other)
   1175      : MInstruction(other) {
   1176    for (int i = 0; i < (int)Arity;
   1177         i++) {  // N.B. use |int| to avoid warnings when Arity == 0
   1178      operands_[i].init(other.operands_[i].producer(), this);
   1179    }
   1180  }
   1181 };
   1182 
   1183 class MNullaryInstruction : public MAryInstruction<0>,
   1184                            public NoTypePolicy::Data {
   1185 protected:
   1186  explicit MNullaryInstruction(Opcode op) : MAryInstruction(op) {}
   1187 
   1188  HashNumber valueHash() const override;
   1189 };
   1190 
   1191 class MUnaryInstruction : public MAryInstruction<1> {
   1192 protected:
   1193  MUnaryInstruction(Opcode op, MDefinition* ins) : MAryInstruction(op) {
   1194    initOperand(0, ins);
   1195  }
   1196 
   1197  HashNumber valueHash() const override;
   1198 
   1199 public:
   1200  NAMED_OPERANDS((0, input))
   1201 };
   1202 
   1203 class MBinaryInstruction : public MAryInstruction<2> {
   1204 protected:
   1205  MBinaryInstruction(Opcode op, MDefinition* left, MDefinition* right)
   1206      : MAryInstruction(op) {
   1207    initOperand(0, left);
   1208    initOperand(1, right);
   1209  }
   1210 
   1211 public:
   1212  NAMED_OPERANDS((0, lhs), (1, rhs))
   1213 
   1214 protected:
   1215  HashNumber valueHash() const override;
   1216 
   1217  bool binaryCongruentTo(const MDefinition* ins) const {
   1218    if (op() != ins->op()) {
   1219      return false;
   1220    }
   1221 
   1222    if (type() != ins->type()) {
   1223      return false;
   1224    }
   1225 
   1226    if (isEffectful() || ins->isEffectful()) {
   1227      return false;
   1228    }
   1229 
   1230    const MDefinition* left = getOperand(0);
   1231    const MDefinition* right = getOperand(1);
   1232    if (isCommutative() && left->id() > right->id()) {
   1233      std::swap(left, right);
   1234    }
   1235 
   1236    const MBinaryInstruction* bi = static_cast<const MBinaryInstruction*>(ins);
   1237    const MDefinition* insLeft = bi->getOperand(0);
   1238    const MDefinition* insRight = bi->getOperand(1);
   1239    if (bi->isCommutative() && insLeft->id() > insRight->id()) {
   1240      std::swap(insLeft, insRight);
   1241    }
   1242 
   1243    return left == insLeft && right == insRight;
   1244  }
   1245 
   1246 public:
   1247  // Return if the operands to this instruction are both unsigned.
   1248  static bool unsignedOperands(MDefinition* left, MDefinition* right);
   1249  bool unsignedOperands();
   1250 
   1251  // Replace any wrapping operands with the underlying int32 operands
   1252  // in case of unsigned operands.
   1253  void replaceWithUnsignedOperands();
   1254 };
   1255 
   1256 class MTernaryInstruction : public MAryInstruction<3> {
   1257 protected:
   1258  MTernaryInstruction(Opcode op, MDefinition* first, MDefinition* second,
   1259                      MDefinition* third)
   1260      : MAryInstruction(op) {
   1261    initOperand(0, first);
   1262    initOperand(1, second);
   1263    initOperand(2, third);
   1264  }
   1265 
   1266  HashNumber valueHash() const override;
   1267 };
   1268 
   1269 class MQuaternaryInstruction : public MAryInstruction<4> {
   1270 protected:
   1271  MQuaternaryInstruction(Opcode op, MDefinition* first, MDefinition* second,
   1272                         MDefinition* third, MDefinition* fourth)
   1273      : MAryInstruction(op) {
   1274    initOperand(0, first);
   1275    initOperand(1, second);
   1276    initOperand(2, third);
   1277    initOperand(3, fourth);
   1278  }
   1279 
   1280  HashNumber valueHash() const override;
   1281 };
   1282 
   1283 class MQuinaryInstruction : public MAryInstruction<5> {
   1284 protected:
   1285  MQuinaryInstruction(Opcode op, MDefinition* first, MDefinition* second,
   1286                      MDefinition* third, MDefinition* fourth,
   1287                      MDefinition* fifth)
   1288      : MAryInstruction(op) {
   1289    initOperand(0, first);
   1290    initOperand(1, second);
   1291    initOperand(2, third);
   1292    initOperand(3, fourth);
   1293    initOperand(4, fifth);
   1294  }
   1295 
   1296  HashNumber valueHash() const override;
   1297 };
   1298 
   1299 template <class T>
   1300 class MVariadicT : public T {
   1301  FixedList<MUse> operands_;
   1302 
   1303 protected:
   1304  explicit MVariadicT(typename T::Opcode op) : T(op) {}
   1305  [[nodiscard]] bool init(TempAllocator& alloc, size_t length) {
   1306    return operands_.init(alloc, length);
   1307  }
   1308  void initOperand(size_t index, MDefinition* operand) {
   1309    // FixedList doesn't initialize its elements, so do an unchecked init.
   1310    operands_[index].initUnchecked(operand, this);
   1311  }
   1312  MUse* getUseFor(size_t index) final { return &operands_[index]; }
   1313  const MUse* getUseFor(size_t index) const final { return &operands_[index]; }
   1314 
   1315  // The MWasmCallBase mixin performs initialization for it's subclasses.
   1316  friend class MWasmCallBase;
   1317 
   1318 public:
   1319  // Will assert if called before initialization.
   1320  MDefinition* getOperand(size_t index) const final {
   1321    return operands_[index].producer();
   1322  }
   1323  size_t numOperands() const final { return operands_.length(); }
   1324  size_t indexOf(const MUse* u) const final {
   1325    MOZ_ASSERT(u >= &operands_[0]);
   1326    MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
   1327    return u - &operands_[0];
   1328  }
   1329  void replaceOperand(size_t index, MDefinition* operand) final {
   1330    operands_[index].replaceProducer(operand);
   1331  }
   1332 };
   1333 
   1334 // An instruction with a variable number of operands. Note that the
   1335 // MFoo::New constructor for variadic instructions fallibly
   1336 // initializes the operands_ array and must be checked for OOM.
   1337 using MVariadicInstruction = MVariadicT<MInstruction>;
   1338 
   1339 // All barriered operations:
   1340 // - MCompareExchangeTypedArrayElement
   1341 // - MExchangeTypedArrayElement
   1342 // - MAtomicTypedArrayElementBinop
   1343 // - MGrowableSharedArrayBufferByteLength
   1344 //
   1345 // And operations which are optionally barriered:
   1346 // - MLoadUnboxedScalar
   1347 // - MStoreUnboxedScalar
   1348 // - MResizableTypedArrayLength
   1349 // - MResizableDataViewByteLength
   1350 //
   1351 // Must have the following attributes:
   1352 //
   1353 // - Not movable
   1354 // - Not removable
   1355 // - Not congruent with any other instruction
   1356 // - Effectful (they alias every TypedArray store)
   1357 //
   1358 // The intended effect of those constraints is to prevent all loads and stores
   1359 // preceding the barriered operation from being moved to after the barriered
   1360 // operation, and vice versa, and to prevent the barriered operation from being
   1361 // removed or hoisted.
   1362 
   1363 enum class MemoryBarrierRequirement : bool {
   1364  NotRequired,
   1365  Required,
   1366 };
   1367 
   1368 inline Synchronization SynchronizeLoad(
   1369    MemoryBarrierRequirement requiresBarrier) {
   1370  if (requiresBarrier == MemoryBarrierRequirement::Required) {
   1371    return Synchronization::Load();
   1372  }
   1373  return Synchronization::None();
   1374 }
   1375 
   1376 inline Synchronization SynchronizeStore(
   1377    MemoryBarrierRequirement requiresBarrier) {
   1378  if (requiresBarrier == MemoryBarrierRequirement::Required) {
   1379    return Synchronization::Store();
   1380  }
   1381  return Synchronization::None();
   1382 }
   1383 
   1384 MIR_OPCODE_CLASS_GENERATED
   1385 
   1386 // Truncation barrier. This is intended for protecting its input against
   1387 // follow-up truncation optimizations.
   1388 class MLimitedTruncate : public MUnaryInstruction,
   1389                         public ConvertToInt32Policy<0>::Data {
   1390  TruncateKind truncate_;
   1391  TruncateKind truncateLimit_;
   1392 
   1393  MLimitedTruncate(MDefinition* input, TruncateKind limit)
   1394      : MUnaryInstruction(classOpcode, input),
   1395        truncate_(TruncateKind::NoTruncate),
   1396        truncateLimit_(limit) {
   1397    setResultType(MIRType::Int32);
   1398    setMovable();
   1399  }
   1400 
   1401 public:
   1402  INSTRUCTION_HEADER(LimitedTruncate)
   1403  TRIVIAL_NEW_WRAPPERS
   1404 
   1405  AliasSet getAliasSet() const override { return AliasSet::None(); }
   1406 
   1407  void computeRange(TempAllocator& alloc) override;
   1408  bool canTruncate() const override;
   1409  void truncate(TruncateKind kind) override;
   1410  TruncateKind operandTruncateKind(size_t index) const override;
   1411  TruncateKind truncateKind() const { return truncate_; }
   1412  void setTruncateKind(TruncateKind kind) { truncate_ = kind; }
   1413 };
   1414 
   1415 // Truncation barrier. This is intended for protecting its input against
   1416 // follow-up truncation optimizations.
   1417 class MIntPtrLimitedTruncate : public MUnaryInstruction,
   1418                               public NoTypePolicy::Data {
   1419  explicit MIntPtrLimitedTruncate(MDefinition* input)
   1420      : MUnaryInstruction(classOpcode, input) {
   1421    MOZ_ASSERT(input->type() == MIRType::IntPtr);
   1422    setResultType(MIRType::IntPtr);
   1423    setMovable();
   1424  }
   1425 
   1426 public:
   1427  INSTRUCTION_HEADER(IntPtrLimitedTruncate)
   1428  TRIVIAL_NEW_WRAPPERS
   1429 
   1430  AliasSet getAliasSet() const override { return AliasSet::None(); }
   1431 };
   1432 
   1433 // Truncation barrier. This is intended for protecting its input against
   1434 // follow-up truncation optimizations.
   1435 class MInt64LimitedTruncate : public MUnaryInstruction,
   1436                              public NoTypePolicy::Data {
   1437  explicit MInt64LimitedTruncate(MDefinition* input)
   1438      : MUnaryInstruction(classOpcode, input) {
   1439    MOZ_ASSERT(input->type() == MIRType::Int64);
   1440    setResultType(MIRType::Int64);
   1441    setMovable();
   1442  }
   1443 
   1444 public:
   1445  INSTRUCTION_HEADER(Int64LimitedTruncate)
   1446  TRIVIAL_NEW_WRAPPERS
   1447 
   1448  AliasSet getAliasSet() const override { return AliasSet::None(); }
   1449 };
   1450 
   1451 // A constant js::Value.
   1452 class MConstant : public MNullaryInstruction {
   1453  struct Payload {
   1454    union {
   1455      bool b;
   1456      int32_t i32;
   1457      int64_t i64;
   1458      intptr_t iptr;
   1459      float f;
   1460      double d;
   1461      JSOffThreadAtom* str;
   1462      JS::Symbol* sym;
   1463      BigInt* bi;
   1464      JSObject* obj;
   1465      Shape* shape;
   1466      uint64_t asBits;
   1467    };
   1468    Payload() : asBits(0) {}
   1469  };
   1470 
   1471  Payload payload_;
   1472 
   1473  static_assert(sizeof(Payload) == sizeof(uint64_t),
   1474                "asBits must be big enough for all payload bits");
   1475 
   1476 #ifdef DEBUG
   1477  void assertInitializedPayload() const;
   1478 #else
   1479  void assertInitializedPayload() const {}
   1480 #endif
   1481 
   1482  explicit MConstant(MIRType type) : MNullaryInstruction(classOpcode) {
   1483    setResultType(type);
   1484    setMovable();
   1485  }
   1486  explicit MConstant(bool b) : MConstant(MIRType::Boolean) { payload_.b = b; }
   1487  explicit MConstant(double d) : MConstant(MIRType::Double) { payload_.d = d; }
   1488  explicit MConstant(float f) : MConstant(MIRType::Float32) { payload_.f = f; }
   1489  explicit MConstant(int32_t i) : MConstant(MIRType::Int32) {
   1490    payload_.i32 = i;
   1491  }
   1492  MConstant(MIRType type, int64_t i) : MConstant(type) {
   1493    MOZ_ASSERT(type == MIRType::Int64 || type == MIRType::IntPtr);
   1494    if (type == MIRType::Int64) {
   1495      payload_.i64 = i;
   1496    } else {
   1497      payload_.iptr = i;
   1498    }
   1499  }
   1500 
   1501  MConstant(TempAllocator& alloc, const Value& v);
   1502  explicit MConstant(JSObject* obj);
   1503  explicit MConstant(Shape* shape);
   1504 
   1505 public:
   1506  INSTRUCTION_HEADER(Constant)
   1507  static MConstant* New(TempAllocator& alloc, const Value& v);
   1508  static MConstant* New(TempAllocator::Fallible alloc, const Value& v);
   1509  static MConstant* NewBoolean(TempAllocator& alloc, bool b);
   1510  static MConstant* NewDouble(TempAllocator& alloc, double d);
   1511  static MConstant* NewFloat32(TempAllocator& alloc, double d);
   1512  static MConstant* NewInt32(TempAllocator& alloc, int32_t i);
   1513  static MConstant* NewInt64(TempAllocator& alloc, int64_t i);
   1514  static MConstant* NewIntPtr(TempAllocator& alloc, intptr_t i);
   1515  static MConstant* NewMagic(TempAllocator& alloc, JSWhyMagic m);
   1516  static MConstant* NewNull(TempAllocator& alloc);
   1517  static MConstant* NewObject(TempAllocator& alloc, JSObject* v);
   1518  static MConstant* NewShape(TempAllocator& alloc, Shape* s);
   1519  static MConstant* NewString(TempAllocator& alloc, JSString* s);
   1520  static MConstant* NewUndefined(TempAllocator& alloc);
   1521 
   1522  // Try to convert this constant to boolean, similar to js::ToBoolean.
   1523  // Returns false if the type is MIRType::Magic* or MIRType::Object.
   1524  [[nodiscard]] bool valueToBoolean(bool* res) const;
   1525 
   1526 #ifdef JS_JITSPEW
   1527  void printOpcode(GenericPrinter& out) const override;
   1528 #endif
   1529 
   1530  HashNumber valueHash() const override;
   1531  bool congruentTo(const MDefinition* ins) const override;
   1532 
   1533  AliasSet getAliasSet() const override { return AliasSet::None(); }
   1534 
   1535  void computeRange(TempAllocator& alloc) override;
   1536  bool canTruncate() const override;
   1537  void truncate(TruncateKind kind) override;
   1538 
   1539  bool canProduceFloat32() const override;
   1540 
   1541  ALLOW_CLONE(MConstant)
   1542 
   1543  bool equals(const MConstant* other) const {
   1544    assertInitializedPayload();
   1545    return type() == other->type() && payload_.asBits == other->payload_.asBits;
   1546  }
   1547 
   1548  bool toBoolean() const {
   1549    MOZ_ASSERT(type() == MIRType::Boolean);
   1550    return payload_.b;
   1551  }
   1552  int32_t toInt32() const {
   1553    MOZ_ASSERT(type() == MIRType::Int32);
   1554    return payload_.i32;
   1555  }
   1556  int64_t toInt64() const {
   1557    MOZ_ASSERT(type() == MIRType::Int64);
   1558    return payload_.i64;
   1559  }
   1560  intptr_t toIntPtr() const {
   1561    MOZ_ASSERT(type() == MIRType::IntPtr);
   1562    return payload_.iptr;
   1563  }
   1564  bool isInt32(int32_t i) const {
   1565    return type() == MIRType::Int32 && payload_.i32 == i;
   1566  }
   1567  bool isInt64(int64_t i) const {
   1568    return type() == MIRType::Int64 && payload_.i64 == i;
   1569  }
   1570  const double& toDouble() const {
   1571    MOZ_ASSERT(type() == MIRType::Double);
   1572    return payload_.d;
   1573  }
   1574  const float& toFloat32() const {
   1575    MOZ_ASSERT(type() == MIRType::Float32);
   1576    return payload_.f;
   1577  }
   1578  JSOffThreadAtom* toString() const {
   1579    MOZ_ASSERT(type() == MIRType::String);
   1580    return payload_.str;
   1581  }
   1582  JS::Symbol* toSymbol() const {
   1583    MOZ_ASSERT(type() == MIRType::Symbol);
   1584    return payload_.sym;
   1585  }
   1586  BigInt* toBigInt() const {
   1587    MOZ_ASSERT(type() == MIRType::BigInt);
   1588    return payload_.bi;
   1589  }
   1590  JSObject& toObject() const {
   1591    MOZ_ASSERT(type() == MIRType::Object);
   1592    return *payload_.obj;
   1593  }
   1594  JSObject* toObjectOrNull() const {
   1595    if (type() == MIRType::Object) {
   1596      return payload_.obj;
   1597    }
   1598    MOZ_ASSERT(type() == MIRType::Null);
   1599    return nullptr;
   1600  }
   1601  Shape* toShape() const {
   1602    MOZ_ASSERT(type() == MIRType::Shape);
   1603    return payload_.shape;
   1604  }
   1605 
   1606  bool isTypeRepresentableAsDouble() const {
   1607    return IsTypeRepresentableAsDouble(type());
   1608  }
   1609  double numberToDouble() const {
   1610    MOZ_ASSERT(isTypeRepresentableAsDouble());
   1611    if (type() == MIRType::Int32) {
   1612      return toInt32();
   1613    }
   1614    if (type() == MIRType::Double) {
   1615      return toDouble();
   1616    }
   1617    return toFloat32();
   1618  }
   1619 
   1620  // Convert this constant to a js::Value. Float32 constants will be stored
   1621  // as DoubleValue and NaNs are canonicalized. Callers must be careful: not
   1622  // all constants can be represented by js::Value (wasm supports int64).
   1623  Value toJSValue() const;
   1624 };
   1625 
   1626 inline HashNumber ConstantValueHash(MIRType type, uint64_t payload) {
   1627  // Build a 64-bit value holding both the payload and the type.
   1628  static const size_t TypeBits = 8;
   1629  static const size_t TypeShift = 64 - TypeBits;
   1630  MOZ_ASSERT(uintptr_t(type) <= (1 << TypeBits) - 1);
   1631  uint64_t bits = (uint64_t(type) << TypeShift) ^ payload;
   1632 
   1633  // Fold all 64 bits into the 32-bit result. It's tempting to just discard
   1634  // half of the bits, as this is just a hash, however there are many common
   1635  // patterns of values where only the low or the high bits vary, so
   1636  // discarding either side would lead to excessive hash collisions.
   1637  return (HashNumber)bits ^ (HashNumber)(bits >> 32);
   1638 }
   1639 
   1640 class MParameter : public MNullaryInstruction {
   1641  int32_t index_;
   1642 
   1643  explicit MParameter(int32_t index)
   1644      : MNullaryInstruction(classOpcode), index_(index) {
   1645    setResultType(MIRType::Value);
   1646  }
   1647 
   1648 public:
   1649  INSTRUCTION_HEADER(Parameter)
   1650  TRIVIAL_NEW_WRAPPERS
   1651 
   1652  static const int32_t THIS_SLOT = -1;
   1653  int32_t index() const { return index_; }
   1654 #ifdef JS_JITSPEW
   1655  void printOpcode(GenericPrinter& out) const override;
   1656 #endif
   1657  HashNumber valueHash() const override;
   1658  bool congruentTo(const MDefinition* ins) const override;
   1659 };
   1660 
   1661 class MControlInstruction : public MInstruction {
   1662 protected:
   1663  explicit MControlInstruction(Opcode op) : MInstruction(op) {}
   1664 
   1665 public:
   1666  virtual size_t numSuccessors() const = 0;
   1667  virtual MBasicBlock* getSuccessor(size_t i) const = 0;
   1668  virtual void replaceSuccessor(size_t i, MBasicBlock* successor) = 0;
   1669 
   1670  void initSuccessor(size_t i, MBasicBlock* successor) {
   1671    MOZ_ASSERT(!getSuccessor(i));
   1672    replaceSuccessor(i, successor);
   1673  }
   1674 
   1675  bool isControlInstruction() const override { return true; }
   1676 
   1677 #ifdef JS_JITSPEW
   1678  void printOpcode(GenericPrinter& out) const override;
   1679 #endif
   1680 };
   1681 
   1682 class MTableSwitch final : public MControlInstruction,
   1683                           public NoFloatPolicy<0>::Data {
   1684  // The successors of the tableswitch
   1685  // - First successor = the default case
   1686  // - Successors 2 and higher = the cases
   1687  Vector<MBasicBlock*, 0, JitAllocPolicy> successors_;
   1688  // Index into successors_ sorted on case index
   1689  Vector<size_t, 0, JitAllocPolicy> cases_;
   1690 
   1691  MUse operand_;
   1692  int32_t low_;
   1693  int32_t high_;
   1694 
   1695  void initOperand(size_t index, MDefinition* operand) {
   1696    MOZ_ASSERT(index == 0);
   1697    operand_.init(operand, this);
   1698  }
   1699 
   1700  MTableSwitch(TempAllocator& alloc, MDefinition* ins, int32_t low,
   1701               int32_t high)
   1702      : MControlInstruction(classOpcode),
   1703        successors_(alloc),
   1704        cases_(alloc),
   1705        low_(low),
   1706        high_(high) {
   1707    initOperand(0, ins);
   1708  }
   1709 
   1710 protected:
   1711  MUse* getUseFor(size_t index) override {
   1712    MOZ_ASSERT(index == 0);
   1713    return &operand_;
   1714  }
   1715 
   1716  const MUse* getUseFor(size_t index) const override {
   1717    MOZ_ASSERT(index == 0);
   1718    return &operand_;
   1719  }
   1720 
   1721 public:
   1722  INSTRUCTION_HEADER(TableSwitch)
   1723 
   1724  static MTableSwitch* New(TempAllocator& alloc, MDefinition* ins, int32_t low,
   1725                           int32_t high) {
   1726    return new (alloc) MTableSwitch(alloc, ins, low, high);
   1727  }
   1728 
   1729  size_t numSuccessors() const override { return successors_.length(); }
   1730 
   1731  [[nodiscard]] bool addSuccessor(MBasicBlock* successor, size_t* index) {
   1732    MOZ_ASSERT(successors_.length() < (size_t)(high_ - low_ + 2));
   1733    MOZ_ASSERT(!successors_.empty());
   1734    *index = successors_.length();
   1735    return successors_.append(successor);
   1736  }
   1737 
   1738  MBasicBlock* getSuccessor(size_t i) const override {
   1739    MOZ_ASSERT(i < numSuccessors());
   1740    return successors_[i];
   1741  }
   1742 
   1743  void replaceSuccessor(size_t i, MBasicBlock* successor) override {
   1744    MOZ_ASSERT(i < numSuccessors());
   1745    successors_[i] = successor;
   1746  }
   1747 
   1748  int32_t low() const { return low_; }
   1749 
   1750  int32_t high() const { return high_; }
   1751 
   1752  MBasicBlock* getDefault() const { return getSuccessor(0); }
   1753 
   1754  MBasicBlock* getCase(size_t i) const { return getSuccessor(cases_[i]); }
   1755 
   1756  [[nodiscard]] bool addDefault(MBasicBlock* block, size_t* index = nullptr) {
   1757    MOZ_ASSERT(successors_.empty());
   1758    if (index) {
   1759      *index = 0;
   1760    }
   1761    return successors_.append(block);
   1762  }
   1763 
   1764  [[nodiscard]] bool addCase(size_t successorIndex) {
   1765    return cases_.append(successorIndex);
   1766  }
   1767 
   1768  size_t numCases() const { return high() - low() + 1; }
   1769 
   1770  MDefinition* getOperand(size_t index) const override {
   1771    MOZ_ASSERT(index == 0);
   1772    return operand_.producer();
   1773  }
   1774 
   1775  size_t numOperands() const override { return 1; }
   1776 
   1777  size_t indexOf(const MUse* u) const final {
   1778    MOZ_ASSERT(u == getUseFor(0));
   1779    return 0;
   1780  }
   1781 
   1782  void replaceOperand(size_t index, MDefinition* operand) final {
   1783    MOZ_ASSERT(index == 0);
   1784    operand_.replaceProducer(operand);
   1785  }
   1786 
   1787  MDefinition* foldsTo(TempAllocator& alloc) override;
   1788 
   1789  // It does read memory in that it must read an entry from the jump table,
   1790  // but that's effectively data that is private to this MIR.  And it should
   1791  // certainly never be modified by any other MIR.  Hence it is effect-free
   1792  // from an alias-analysis standpoint.
   1793  AliasSet getAliasSet() const override { return AliasSet::None(); }
   1794 };
   1795 
   1796 template <size_t Arity, size_t Successors>
   1797 class MAryControlInstruction : public MControlInstruction {
   1798  MOZ_NO_UNIQUE_ADDRESS mozilla::Array<MUse, Arity> operands_;
   1799  MOZ_NO_UNIQUE_ADDRESS mozilla::Array<MBasicBlock*, Successors> successors_;
   1800 
   1801 protected:
   1802  explicit MAryControlInstruction(Opcode op) : MControlInstruction(op) {}
   1803  void setSuccessor(size_t index, MBasicBlock* successor) {
   1804    successors_[index] = successor;
   1805  }
   1806 
   1807  MUse* getUseFor(size_t index) final { return &operands_[index]; }
   1808  const MUse* getUseFor(size_t index) const final { return &operands_[index]; }
   1809  void initOperand(size_t index, MDefinition* operand) {
   1810    operands_[index].init(operand, this);
   1811  }
   1812 
   1813 public:
   1814  MDefinition* getOperand(size_t index) const final {
   1815    return operands_[index].producer();
   1816  }
   1817  size_t numOperands() const final { return Arity; }
   1818  size_t indexOf(const MUse* u) const final {
   1819    MOZ_ASSERT(u >= &operands_[0]);
   1820    MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
   1821    return u - &operands_[0];
   1822  }
   1823  void replaceOperand(size_t index, MDefinition* operand) final {
   1824    operands_[index].replaceProducer(operand);
   1825  }
   1826  size_t numSuccessors() const final { return Successors; }
   1827  MBasicBlock* getSuccessor(size_t i) const final { return successors_[i]; }
   1828  void replaceSuccessor(size_t i, MBasicBlock* succ) final {
   1829    successors_[i] = succ;
   1830  }
   1831 };
   1832 
   1833 template <size_t Successors>
   1834 class MVariadicControlInstruction : public MVariadicT<MControlInstruction> {
   1835  MOZ_NO_UNIQUE_ADDRESS mozilla::Array<MBasicBlock*, Successors> successors_;
   1836 
   1837 protected:
   1838  explicit MVariadicControlInstruction(Opcode op)
   1839      : MVariadicT<MControlInstruction>(op) {}
   1840  void setSuccessor(size_t index, MBasicBlock* successor) {
   1841    successors_[index] = successor;
   1842  }
   1843 
   1844 public:
   1845  size_t numSuccessors() const final { return Successors; }
   1846  MBasicBlock* getSuccessor(size_t i) const final { return successors_[i]; }
   1847  void replaceSuccessor(size_t i, MBasicBlock* succ) final {
   1848    successors_[i] = succ;
   1849  }
   1850 };
   1851 
   1852 // Jump to the start of another basic block.
   1853 class MGoto : public MAryControlInstruction<0, 1>, public NoTypePolicy::Data {
   1854  explicit MGoto(MBasicBlock* target) : MAryControlInstruction(classOpcode) {
   1855    setSuccessor(TargetIndex, target);
   1856  }
   1857 
   1858 public:
   1859  INSTRUCTION_HEADER(Goto)
   1860  static MGoto* New(TempAllocator& alloc, MBasicBlock* target);
   1861  static MGoto* New(TempAllocator::Fallible alloc, MBasicBlock* target);
   1862 
   1863  // Variant that may patch the target later.
   1864  static MGoto* New(TempAllocator& alloc);
   1865 
   1866  static constexpr size_t TargetIndex = 0;
   1867 
   1868  MBasicBlock* target() const { return getSuccessor(TargetIndex); }
   1869  void setTarget(MBasicBlock* target) { setSuccessor(TargetIndex, target); }
   1870 
   1871  AliasSet getAliasSet() const override { return AliasSet::None(); }
   1872 
   1873 #ifdef JS_JITSPEW
   1874  void getExtras(ExtrasCollector* extras) const override {
   1875    char buf[64];
   1876    SprintfLiteral(buf, "Block%u", GetMBasicBlockId(target()));
   1877    extras->add(buf);
   1878  }
   1879 #endif
   1880 
   1881  ALLOW_CLONE(MGoto)
   1882 };
   1883 
   1884 // Tests if the input instruction evaluates to true or false, and jumps to the
   1885 // start of a corresponding basic block.
   1886 class MTest : public MAryControlInstruction<1, 2>, public TestPolicy::Data {
   1887  // It is allowable to specify `trueBranch` or `falseBranch` as nullptr and
   1888  // patch it in later.
   1889  MTest(MDefinition* ins, MBasicBlock* trueBranch, MBasicBlock* falseBranch)
   1890      : MAryControlInstruction(classOpcode) {
   1891    initOperand(0, ins);
   1892    setSuccessor(TrueBranchIndex, trueBranch);
   1893    setSuccessor(FalseBranchIndex, falseBranch);
   1894  }
   1895 
   1896  TypeDataList observedTypes_;
   1897 
   1898 public:
   1899  INSTRUCTION_HEADER(Test)
   1900  TRIVIAL_NEW_WRAPPERS
   1901  NAMED_OPERANDS((0, input))
   1902 
   1903  const TypeDataList& observedTypes() const { return observedTypes_; }
   1904  void setObservedTypes(const TypeDataList& observed) {
   1905    observedTypes_ = observed;
   1906  }
   1907 
   1908  static constexpr size_t TrueBranchIndex = 0;
   1909  static constexpr size_t FalseBranchIndex = 1;
   1910 
   1911  MBasicBlock* ifTrue() const { return getSuccessor(TrueBranchIndex); }
   1912  MBasicBlock* ifFalse() const { return getSuccessor(FalseBranchIndex); }
   1913  void setIfTrue(MBasicBlock* target) { setSuccessor(TrueBranchIndex, target); }
   1914  void setIfFalse(MBasicBlock* target) {
   1915    setSuccessor(FalseBranchIndex, target);
   1916  }
   1917 
   1918  MBasicBlock* branchSuccessor(BranchDirection dir) const {
   1919    return (dir == TRUE_BRANCH) ? ifTrue() : ifFalse();
   1920  }
   1921 
   1922  AliasSet getAliasSet() const override { return AliasSet::None(); }
   1923 
   1924  MDefinition* foldsDoubleNegation(TempAllocator& alloc);
   1925  MDefinition* foldsConstant(TempAllocator& alloc);
   1926  MDefinition* foldsTypes(TempAllocator& alloc);
   1927  MDefinition* foldsNeedlessControlFlow(TempAllocator& alloc);
   1928  MDefinition* foldsRedundantTest(TempAllocator& alloc);
   1929  MDefinition* foldsTo(TempAllocator& alloc) override;
   1930 
   1931 #ifdef DEBUG
   1932  bool isConsistentFloat32Use(MUse* use) const override { return true; }
   1933 #endif
   1934 
   1935 #ifdef JS_JITSPEW
   1936  void getExtras(ExtrasCollector* extras) const override {
   1937    char buf[64];
   1938    SprintfLiteral(buf, "true->Block%u false->Block%u",
   1939                   GetMBasicBlockId(ifTrue()), GetMBasicBlockId(ifFalse()));
   1940    extras->add(buf);
   1941  }
   1942 #endif
   1943 
   1944  bool canClone() const override { return true; }
   1945  MInstruction* clone(TempAllocator& alloc,
   1946                      const MDefinitionVector& inputs) const override {
   1947    MInstruction* res = new (alloc) MTest(input(), ifTrue(), ifFalse());
   1948    if (!res) {
   1949      return nullptr;
   1950    }
   1951    for (size_t i = 0; i < numOperands(); i++) {
   1952      res->replaceOperand(i, inputs[i]);
   1953    }
   1954    return res;
   1955  }
   1956 };
   1957 
   1958 // Returns from this function to the previous caller.
   1959 class MReturn : public MAryControlInstruction<1, 0>,
   1960                public BoxInputsPolicy::Data {
   1961  explicit MReturn(MDefinition* ins) : MAryControlInstruction(classOpcode) {
   1962    initOperand(0, ins);
   1963  }
   1964 
   1965 public:
   1966  INSTRUCTION_HEADER(Return)
   1967  TRIVIAL_NEW_WRAPPERS
   1968  NAMED_OPERANDS((0, input))
   1969 
   1970  AliasSet getAliasSet() const override { return AliasSet::None(); }
   1971 };
   1972 
   1973 class MNewArray : public MUnaryInstruction, public NoTypePolicy::Data {
   1974 private:
   1975  // Number of elements to allocate for the array.
   1976  uint32_t length_;
   1977 
   1978  // Heap where the array should be allocated.
   1979  gc::Heap initialHeap_;
   1980 
   1981  bool vmCall_;
   1982 
   1983  MNewArray(uint32_t length, MConstant* templateConst, gc::Heap initialHeap,
   1984            bool vmCall = false);
   1985 
   1986 public:
   1987  INSTRUCTION_HEADER(NewArray)
   1988  TRIVIAL_NEW_WRAPPERS
   1989 
   1990  static MNewArray* NewVM(TempAllocator& alloc, uint32_t length,
   1991                          MConstant* templateConst, gc::Heap initialHeap) {
   1992    return new (alloc) MNewArray(length, templateConst, initialHeap, true);
   1993  }
   1994 
   1995  uint32_t length() const { return length_; }
   1996 
   1997  JSObject* templateObject() const {
   1998    return getOperand(0)->toConstant()->toObjectOrNull();
   1999  }
   2000 
   2001  gc::Heap initialHeap() const { return initialHeap_; }
   2002 
   2003  bool isVMCall() const { return vmCall_; }
   2004 
   2005  // NewArray is marked as non-effectful because all our allocations are
   2006  // either lazy when we are using "new Array(length)" or bounded by the
   2007  // script or the stack size when we are using "new Array(...)" or "[...]"
   2008  // notations.  So we might have to allocate the array twice if we bail
   2009  // during the computation of the first element of the square braket
   2010  // notation.
   2011  virtual AliasSet getAliasSet() const override { return AliasSet::None(); }
   2012 
   2013  [[nodiscard]] bool writeRecoverData(
   2014      CompactBufferWriter& writer) const override;
   2015  bool canRecoverOnBailout() const override {
   2016    // The template object can safely be used in the recover instruction
   2017    // because it can never be mutated by any other function execution.
   2018    return templateObject() != nullptr;
   2019  }
   2020 };
   2021 
   2022 class MNewTypedArray : public MUnaryInstruction, public NoTypePolicy::Data {
   2023  gc::Heap initialHeap_;
   2024 
   2025  MNewTypedArray(MConstant* templateConst, gc::Heap initialHeap)
   2026      : MUnaryInstruction(classOpcode, templateConst),
   2027        initialHeap_(initialHeap) {
   2028    setResultType(MIRType::Object);
   2029  }
   2030 
   2031 public:
   2032  INSTRUCTION_HEADER(NewTypedArray)
   2033  TRIVIAL_NEW_WRAPPERS
   2034 
   2035  auto* templateObject() const {
   2036    auto* cst = getOperand(0)->toConstant();
   2037    return &cst->toObject().as<FixedLengthTypedArrayObject>();
   2038  }
   2039 
   2040  gc::Heap initialHeap() const { return initialHeap_; }
   2041 
   2042  virtual AliasSet getAliasSet() const override { return AliasSet::None(); }
   2043 
   2044  [[nodiscard]] bool writeRecoverData(
   2045      CompactBufferWriter& writer) const override;
   2046  bool canRecoverOnBailout() const override { return true; }
   2047 };
   2048 
   2049 class MNewObject : public MUnaryInstruction, public NoTypePolicy::Data {
   2050 public:
   2051  enum Mode { ObjectLiteral, ObjectCreate };
   2052 
   2053 private:
   2054  gc::Heap initialHeap_;
   2055  Mode mode_;
   2056  bool vmCall_;
   2057 
   2058  MNewObject(MConstant* templateConst, gc::Heap initialHeap, Mode mode,
   2059             bool vmCall = false)
   2060      : MUnaryInstruction(classOpcode, templateConst),
   2061        initialHeap_(initialHeap),
   2062        mode_(mode),
   2063        vmCall_(vmCall) {
   2064    if (mode == ObjectLiteral) {
   2065      MOZ_ASSERT(!templateObject());
   2066    } else {
   2067      MOZ_ASSERT(templateObject());
   2068    }
   2069    setResultType(MIRType::Object);
   2070 
   2071    // The constant is kept separated in a MConstant, this way we can safely
   2072    // mark it during GC if we recover the object allocation.  Otherwise, by
   2073    // making it emittedAtUses, we do not produce register allocations for
   2074    // it and inline its content inside the code produced by the
   2075    // CodeGenerator.
   2076    if (templateConst->toConstant()->type() == MIRType::Object) {
   2077      templateConst->setEmittedAtUses();
   2078    }
   2079  }
   2080 
   2081 public:
   2082  INSTRUCTION_HEADER(NewObject)
   2083  TRIVIAL_NEW_WRAPPERS
   2084 
   2085  static MNewObject* NewVM(TempAllocator& alloc, MConstant* templateConst,
   2086                           gc::Heap initialHeap, Mode mode) {
   2087    return new (alloc) MNewObject(templateConst, initialHeap, mode, true);
   2088  }
   2089 
   2090  Mode mode() const { return mode_; }
   2091 
   2092  JSObject* templateObject() const {
   2093    return getOperand(0)->toConstant()->toObjectOrNull();
   2094  }
   2095 
   2096  gc::Heap initialHeap() const { return initialHeap_; }
   2097 
   2098  bool isVMCall() const { return vmCall_; }
   2099 
   2100  [[nodiscard]] bool writeRecoverData(
   2101      CompactBufferWriter& writer) const override;
   2102  bool canRecoverOnBailout() const override {
   2103    // The template object can safely be used in the recover instruction
   2104    // because it can never be mutated by any other function execution.
   2105    return templateObject() != nullptr;
   2106  }
   2107 };
   2108 
   2109 class MNewPlainObject : public MUnaryInstruction, public NoTypePolicy::Data {
   2110 private:
   2111  uint32_t numFixedSlots_;
   2112  uint32_t numDynamicSlots_;
   2113  gc::AllocKind allocKind_;
   2114  gc::Heap initialHeap_;
   2115 
   2116  MNewPlainObject(MConstant* shapeConst, uint32_t numFixedSlots,
   2117                  uint32_t numDynamicSlots, gc::AllocKind allocKind,
   2118                  gc::Heap initialHeap)
   2119      : MUnaryInstruction(classOpcode, shapeConst),
   2120        numFixedSlots_(numFixedSlots),
   2121        numDynamicSlots_(numDynamicSlots),
   2122        allocKind_(allocKind),
   2123        initialHeap_(initialHeap) {
   2124    setResultType(MIRType::Object);
   2125 
   2126    // The shape constant is kept separated in a MConstant. This way we can
   2127    // safely mark it during GC if we recover the object allocation. Otherwise,
   2128    // by making it emittedAtUses, we do not produce register allocations for it
   2129    // and inline its content inside the code produced by the CodeGenerator.
   2130    MOZ_ASSERT(shapeConst->toConstant()->type() == MIRType::Shape);
   2131    shapeConst->setEmittedAtUses();
   2132  }
   2133 
   2134 public:
   2135  INSTRUCTION_HEADER(NewPlainObject)
   2136  TRIVIAL_NEW_WRAPPERS
   2137 
   2138  const Shape* shape() const { return getOperand(0)->toConstant()->toShape(); }
   2139 
   2140  uint32_t numFixedSlots() const { return numFixedSlots_; }
   2141  uint32_t numDynamicSlots() const { return numDynamicSlots_; }
   2142  gc::AllocKind allocKind() const { return allocKind_; }
   2143  gc::Heap initialHeap() const { return initialHeap_; }
   2144 
   2145  [[nodiscard]] bool writeRecoverData(
   2146      CompactBufferWriter& writer) const override;
   2147  bool canRecoverOnBailout() const override { return true; }
   2148 
   2149  AliasSet getAliasSet() const override { return AliasSet::None(); }
   2150 };
   2151 
   2152 class MNewArrayObject : public MUnaryInstruction, public NoTypePolicy::Data {
   2153 private:
   2154  uint32_t length_;
   2155  gc::Heap initialHeap_;
   2156 
   2157  MNewArrayObject(TempAllocator& alloc, MConstant* shapeConst, uint32_t length,
   2158                  gc::Heap initialHeap)
   2159      : MUnaryInstruction(classOpcode, shapeConst),
   2160        length_(length),
   2161        initialHeap_(initialHeap) {
   2162    setResultType(MIRType::Object);
   2163    MOZ_ASSERT(shapeConst->toConstant()->type() == MIRType::Shape);
   2164    shapeConst->setEmittedAtUses();
   2165  }
   2166 
   2167 public:
   2168  INSTRUCTION_HEADER(NewArrayObject)
   2169  TRIVIAL_NEW_WRAPPERS
   2170 
   2171  static MNewArrayObject* New(TempAllocator& alloc, MConstant* shapeConst,
   2172                              uint32_t length, gc::Heap initialHeap) {
   2173    return new (alloc) MNewArrayObject(alloc, shapeConst, length, initialHeap);
   2174  }
   2175 
   2176  const Shape* shape() const { return getOperand(0)->toConstant()->toShape(); }
   2177 
   2178  // See MNewArray::getAliasSet comment.
   2179  AliasSet getAliasSet() const override { return AliasSet::None(); }
   2180 
   2181  uint32_t length() const { return length_; }
   2182  gc::Heap initialHeap() const { return initialHeap_; }
   2183 
   2184  [[nodiscard]] bool writeRecoverData(
   2185      CompactBufferWriter& writer) const override;
   2186  bool canRecoverOnBailout() const override { return true; }
   2187 };
   2188 
   2189 class MNewIterator : public MUnaryInstruction, public NoTypePolicy::Data {
   2190 public:
   2191  enum Type {
   2192    ArrayIterator,
   2193    StringIterator,
   2194    RegExpStringIterator,
   2195  };
   2196 
   2197 private:
   2198  Type type_;
   2199 
   2200  MNewIterator(MConstant* templateConst, Type type)
   2201      : MUnaryInstruction(classOpcode, templateConst), type_(type) {
   2202    setResultType(MIRType::Object);
   2203    templateConst->setEmittedAtUses();
   2204  }
   2205 
   2206 public:
   2207  INSTRUCTION_HEADER(NewIterator)
   2208  TRIVIAL_NEW_WRAPPERS
   2209 
   2210  Type type() const { return type_; }
   2211 
   2212  JSObject* templateObject() {
   2213    return getOperand(0)->toConstant()->toObjectOrNull();
   2214  }
   2215 
   2216  AliasSet getAliasSet() const override { return AliasSet::None(); }
   2217 
   2218  [[nodiscard]] bool writeRecoverData(
   2219      CompactBufferWriter& writer) const override;
   2220  bool canRecoverOnBailout() const override { return true; }
   2221 };
   2222 
   2223 // Represent the content of all slots of an object.  This instruction is not
   2224 // lowered and is not used to generate code.
   2225 class MObjectState : public MVariadicInstruction,
   2226                     public NoFloatPolicyAfter<1>::Data {
   2227 private:
   2228  uint32_t numSlots_;
   2229  uint32_t numFixedSlots_;
   2230 
   2231  explicit MObjectState(JSObject* templateObject);
   2232  explicit MObjectState(const Shape* shape);
   2233  explicit MObjectState(MObjectState* state);
   2234 
   2235  [[nodiscard]] bool init(TempAllocator& alloc, MDefinition* obj);
   2236 
   2237  void initSlot(uint32_t slot, MDefinition* def) { initOperand(slot + 1, def); }
   2238 
   2239 public:
   2240  INSTRUCTION_HEADER(ObjectState)
   2241  NAMED_OPERANDS((0, object))
   2242 
   2243  // Return the template object of any object creation which can be recovered
   2244  // on bailout.
   2245  static JSObject* templateObjectOf(MDefinition* obj);
   2246 
   2247  static MObjectState* New(TempAllocator& alloc, MDefinition* obj);
   2248  static MObjectState* Copy(TempAllocator& alloc, MObjectState* state);
   2249 
   2250  // As we might do read of uninitialized properties, we have to copy the
   2251  // initial values from the template object.
   2252  void initFromTemplateObject(TempAllocator& alloc, MDefinition* undefinedVal);
   2253 
   2254  size_t numFixedSlots() const { return numFixedSlots_; }
   2255  size_t numSlots() const { return numSlots_; }
   2256 
   2257  MDefinition* getSlot(uint32_t slot) const { return getOperand(slot + 1); }
   2258  void setSlot(uint32_t slot, MDefinition* def) {
   2259    replaceOperand(slot + 1, def);
   2260  }
   2261 
   2262  bool hasFixedSlot(uint32_t slot) const {
   2263    return slot < numSlots() && slot < numFixedSlots();
   2264  }
   2265  MDefinition* getFixedSlot(uint32_t slot) const {
   2266    MOZ_ASSERT(slot < numFixedSlots());
   2267    return getSlot(slot);
   2268  }
   2269  void setFixedSlot(uint32_t slot, MDefinition* def) {
   2270    MOZ_ASSERT(slot < numFixedSlots());
   2271    setSlot(slot, def);
   2272  }
   2273 
   2274  bool hasDynamicSlot(uint32_t slot) const {
   2275    return numFixedSlots() < numSlots() && slot < numSlots() - numFixedSlots();
   2276  }
   2277  MDefinition* getDynamicSlot(uint32_t slot) const {
   2278    return getSlot(slot + numFixedSlots());
   2279  }
   2280  void setDynamicSlot(uint32_t slot, MDefinition* def) {
   2281    setSlot(slot + numFixedSlots(), def);
   2282  }
   2283 
   2284  [[nodiscard]] bool writeRecoverData(
   2285      CompactBufferWriter& writer) const override;
   2286  bool canRecoverOnBailout() const override { return true; }
   2287 };
   2288 
   2289 // Represent the contents of all elements of an array.  This instruction is not
   2290 // lowered and is not used to generate code.
   2291 class MArrayState : public MVariadicInstruction,
   2292                    public NoFloatPolicyAfter<2>::Data {
   2293 private:
   2294  uint32_t numElements_;
   2295 
   2296  explicit MArrayState(MDefinition* arr);
   2297 
   2298  [[nodiscard]] bool init(TempAllocator& alloc, MDefinition* obj,
   2299                          MDefinition* len);
   2300 
   2301  void initElement(uint32_t index, MDefinition* def) {
   2302    initOperand(index + 2, def);
   2303  }
   2304 
   2305 public:
   2306  INSTRUCTION_HEADER(ArrayState)
   2307  NAMED_OPERANDS((0, array), (1, initializedLength))
   2308 
   2309  static MArrayState* New(TempAllocator& alloc, MDefinition* arr,
   2310                          MDefinition* initLength);
   2311  static MArrayState* Copy(TempAllocator& alloc, MArrayState* state);
   2312 
   2313  void initFromTemplateObject(TempAllocator& alloc, MDefinition* undefinedVal);
   2314 
   2315  void setInitializedLength(MDefinition* def) { replaceOperand(1, def); }
   2316 
   2317  size_t numElements() const { return numElements_; }
   2318 
   2319  MDefinition* getElement(uint32_t index) const {
   2320    return getOperand(index + 2);
   2321  }
   2322  void setElement(uint32_t index, MDefinition* def) {
   2323    replaceOperand(index + 2, def);
   2324  }
   2325 
   2326  [[nodiscard]] bool writeRecoverData(
   2327      CompactBufferWriter& writer) const override;
   2328  bool canRecoverOnBailout() const override { return true; }
   2329 };
   2330 
   2331 // WrappedFunction stores information about a function that can safely be used
   2332 // off-thread. In particular, a function's flags can be modified on the main
   2333 // thread as functions are relazified and delazified, so we must be careful not
   2334 // to access these flags off-thread.
   2335 class WrappedFunction : public TempObject {
   2336  // If this is a native function without a JitEntry, the JSFunction*.
   2337  CompilerFunction nativeFun_;
   2338  uint16_t nargs_;
   2339  js::FunctionFlags flags_;
   2340 
   2341 public:
   2342  WrappedFunction(JSFunction* nativeFun, uint16_t nargs, FunctionFlags flags);
   2343 
   2344  // Note: When adding new accessors be sure to add consistency asserts
   2345  // to the constructor.
   2346 
   2347  size_t nargs() const { return nargs_; }
   2348 
   2349  bool isNativeWithoutJitEntry() const {
   2350    return flags_.isNativeWithoutJitEntry();
   2351  }
   2352  bool hasJitEntry() const { return flags_.hasJitEntry(); }
   2353  bool isConstructor() const { return flags_.isConstructor(); }
   2354  bool isClassConstructor() const { return flags_.isClassConstructor(); }
   2355 
   2356  // These fields never change, they can be accessed off-main thread.
   2357  JSNative native() const {
   2358    MOZ_ASSERT(isNativeWithoutJitEntry());
   2359    return nativeFun_->nativeUnchecked();
   2360  }
   2361  bool hasJitInfo() const {
   2362    return flags_.canHaveJitInfo() && nativeFun_->jitInfoUnchecked();
   2363  }
   2364  const JSJitInfo* jitInfo() const {
   2365    MOZ_ASSERT(hasJitInfo());
   2366    return nativeFun_->jitInfoUnchecked();
   2367  }
   2368 
   2369  JSFunction* rawNativeJSFunction() const { return nativeFun_; }
   2370 };
   2371 
   2372 enum class DOMObjectKind : uint8_t { Proxy, Native };
   2373 
   2374 class MCallBase : public MVariadicInstruction, public CallPolicy::Data {
   2375 protected:
   2376  // The callee, this, and the actual arguments are all operands of MCall.
   2377  static const size_t CalleeOperandIndex = 0;
   2378  static const size_t NumNonArgumentOperands = 1;
   2379 
   2380  explicit MCallBase(Opcode op) : MVariadicInstruction(op) {}
   2381 
   2382 public:
   2383  void initCallee(MDefinition* func) { initOperand(CalleeOperandIndex, func); }
   2384  MDefinition* getCallee() const { return getOperand(CalleeOperandIndex); }
   2385 
   2386  void replaceCallee(MInstruction* newfunc) {
   2387    replaceOperand(CalleeOperandIndex, newfunc);
   2388  }
   2389 
   2390  void addArg(size_t argnum, MDefinition* arg);
   2391 
   2392  MDefinition* getArg(uint32_t index) const {
   2393    return getOperand(NumNonArgumentOperands + index);
   2394  }
   2395 
   2396  // The number of stack arguments is the max between the number of formal
   2397  // arguments and the number of actual arguments. The number of stack
   2398  // argument includes the |undefined| padding added in case of underflow.
   2399  // Includes |this|.
   2400  uint32_t numStackArgs() const {
   2401    return numOperands() - NumNonArgumentOperands;
   2402  }
   2403  uint32_t paddedNumStackArgs() const {
   2404    if (JitStackValueAlignment > 1) {
   2405      return AlignBytes(numStackArgs(), JitStackValueAlignment);
   2406    }
   2407    return numStackArgs();
   2408  }
   2409 
   2410  static size_t IndexOfThis() { return NumNonArgumentOperands; }
   2411  static size_t IndexOfArgument(size_t index) {
   2412    return NumNonArgumentOperands + index + 1;  // +1 to skip |this|.
   2413  }
   2414  static size_t IndexOfStackArg(size_t index) {
   2415    return NumNonArgumentOperands + index;
   2416  }
   2417 };
   2418 
   2419 class MCall : public MCallBase {
   2420 protected:
   2421  // Monomorphic cache for MCalls with a single JSFunction target.
   2422  WrappedFunction* target_;
   2423 
   2424  // Original value of argc from the bytecode.
   2425  uint32_t numActualArgs_;
   2426 
   2427  // True if the call is for JSOp::New or JSOp::SuperCall.
   2428  bool construct_ : 1;
   2429 
   2430  // True if the caller does not use the return value.
   2431  bool ignoresReturnValue_ : 1;
   2432 
   2433  bool needsClassCheck_ : 1;
   2434  bool maybeCrossRealm_ : 1;
   2435  bool needsThisCheck_ : 1;
   2436 
   2437  MCall(WrappedFunction* target, uint32_t numActualArgs, bool construct,
   2438        bool ignoresReturnValue)
   2439      : MCallBase(classOpcode),
   2440        target_(target),
   2441        numActualArgs_(numActualArgs),
   2442        construct_(construct),
   2443        ignoresReturnValue_(ignoresReturnValue),
   2444        needsClassCheck_(true),
   2445        maybeCrossRealm_(true),
   2446        needsThisCheck_(false) {
   2447    setResultType(MIRType::Value);
   2448  }
   2449 
   2450 public:
   2451  INSTRUCTION_HEADER(Call)
   2452  static MCall* New(TempAllocator& alloc, WrappedFunction* target,
   2453                    size_t maxArgc, size_t numActualArgs, bool construct,
   2454                    bool ignoresReturnValue, bool isDOMCall,
   2455                    mozilla::Maybe<DOMObjectKind> objectKind,
   2456                    mozilla::Maybe<gc::Heap> initialHeap);
   2457 
   2458  bool needsClassCheck() const { return needsClassCheck_; }
   2459  void disableClassCheck() { needsClassCheck_ = false; }
   2460 
   2461  bool maybeCrossRealm() const { return maybeCrossRealm_; }
   2462  void setNotCrossRealm() { maybeCrossRealm_ = false; }
   2463 
   2464  bool needsThisCheck() const { return needsThisCheck_; }
   2465  void setNeedsThisCheck() {
   2466    MOZ_ASSERT(construct_);
   2467    needsThisCheck_ = true;
   2468  }
   2469 
   2470  // For monomorphic callsites.
   2471  WrappedFunction* getSingleTarget() const { return target_; }
   2472 
   2473  bool isConstructing() const { return construct_; }
   2474 
   2475  bool ignoresReturnValue() const { return ignoresReturnValue_; }
   2476 
   2477  // Does not include |this|.
   2478  uint32_t numActualArgs() const { return numActualArgs_; }
   2479 
   2480  bool possiblyCalls() const override { return true; }
   2481 
   2482  virtual bool isCallDOMNative() const { return false; }
   2483 
   2484  // A method that can be called to tell the MCall to figure out whether it's
   2485  // movable or not.  This can't be done in the constructor, because it
   2486  // depends on the arguments to the call, and those aren't passed to the
   2487  // constructor but are set up later via addArg.
   2488  virtual void computeMovable() {}
   2489 };
   2490 
   2491 class MCallDOMNative : public MCall {
   2492  // A helper class for MCalls for DOM natives.  Note that this is NOT
   2493  // actually a separate MIR op from MCall, because all sorts of places use
   2494  // isCall() to check for calls and all we really want is to overload a few
   2495  // virtual things from MCall.
   2496 
   2497  DOMObjectKind objectKind_;
   2498 
   2499  // Allow wrapper pre-tenuring
   2500  gc::Heap initialHeap_ = gc::Heap::Default;
   2501 
   2502  MCallDOMNative(WrappedFunction* target, uint32_t numActualArgs,
   2503                 DOMObjectKind objectKind, gc::Heap initialHeap)
   2504      : MCall(target, numActualArgs, false, false),
   2505        objectKind_(objectKind),
   2506        initialHeap_(initialHeap) {
   2507    MOZ_ASSERT(getJitInfo()->type() != JSJitInfo::InlinableNative);
   2508 
   2509    // If our jitinfo is not marked eliminatable, that means that our C++
   2510    // implementation is fallible or that it never wants to be eliminated or
   2511    // that we have no hope of ever doing the sort of argument analysis that
   2512    // would allow us to detemine that we're side-effect-free.  In the
   2513    // latter case we wouldn't get DCEd no matter what, but for the former
   2514    // two cases we have to explicitly say that we can't be DCEd.
   2515    if (!getJitInfo()->isEliminatable) {
   2516      setGuard();
   2517    }
   2518  }
   2519 
   2520  friend MCall* MCall::New(TempAllocator& alloc, WrappedFunction* target,
   2521                           size_t maxArgc, size_t numActualArgs, bool construct,
   2522                           bool ignoresReturnValue, bool isDOMCall,
   2523                           mozilla::Maybe<DOMObjectKind> objectKind,
   2524                           mozilla::Maybe<gc::Heap> initalHeap);
   2525 
   2526  const JSJitInfo* getJitInfo() const;
   2527 
   2528 public:
   2529  DOMObjectKind objectKind() const { return objectKind_; }
   2530 
   2531  virtual AliasSet getAliasSet() const override;
   2532 
   2533  virtual bool congruentTo(const MDefinition* ins) const override;
   2534 
   2535  virtual bool isCallDOMNative() const override { return true; }
   2536 
   2537  virtual void computeMovable() override;
   2538 
   2539  gc::Heap initialHeap() { return initialHeap_; }
   2540 };
   2541 
   2542 // Used to invoke a JSClass call/construct hook.
   2543 class MCallClassHook : public MCallBase {
   2544  const JSNative target_;
   2545  bool constructing_ : 1;
   2546  bool ignoresReturnValue_ : 1;
   2547 
   2548  MCallClassHook(JSNative target, bool constructing)
   2549      : MCallBase(classOpcode),
   2550        target_(target),
   2551        constructing_(constructing),
   2552        ignoresReturnValue_(false) {
   2553    setResultType(MIRType::Value);
   2554  }
   2555 
   2556 public:
   2557  INSTRUCTION_HEADER(CallClassHook)
   2558  static MCallClassHook* New(TempAllocator& alloc, JSNative target,
   2559                             uint32_t argc, bool constructing);
   2560 
   2561  JSNative target() const { return target_; }
   2562  bool isConstructing() const { return constructing_; }
   2563 
   2564  uint32_t numActualArgs() const {
   2565    uint32_t thisAndNewTarget = 1 + constructing_;
   2566    MOZ_ASSERT(numStackArgs() >= thisAndNewTarget);
   2567    return numStackArgs() - thisAndNewTarget;
   2568  }
   2569 
   2570  bool maybeCrossRealm() const { return true; }
   2571 
   2572  bool ignoresReturnValue() const { return ignoresReturnValue_; }
   2573  void setIgnoresReturnValue() { ignoresReturnValue_ = true; }
   2574 
   2575  bool possiblyCalls() const override { return true; }
   2576 };
   2577 
   2578 // fun.apply(self, arguments)
   2579 class MApplyArgs : public MTernaryInstruction,
   2580                   public MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>,
   2581                                    BoxPolicy<2>>::Data {
   2582  // Single target from CacheIR, or nullptr
   2583  WrappedFunction* target_;
   2584  // Number of extra initial formals to skip.
   2585  uint32_t numExtraFormals_;
   2586  bool maybeCrossRealm_ = true;
   2587  bool ignoresReturnValue_ = false;
   2588 
   2589  MApplyArgs(WrappedFunction* target, MDefinition* fun, MDefinition* argc,
   2590             MDefinition* self, uint32_t numExtraFormals = 0)
   2591      : MTernaryInstruction(classOpcode, fun, argc, self),
   2592        target_(target),
   2593        numExtraFormals_(numExtraFormals) {
   2594    MOZ_ASSERT(argc->type() == MIRType::Int32);
   2595    setResultType(MIRType::Value);
   2596  }
   2597 
   2598 public:
   2599  INSTRUCTION_HEADER(ApplyArgs)
   2600  TRIVIAL_NEW_WRAPPERS
   2601  NAMED_OPERANDS((0, getFunction), (1, getArgc), (2, getThis))
   2602 
   2603  WrappedFunction* getSingleTarget() const { return target_; }
   2604 
   2605  uint32_t numExtraFormals() const { return numExtraFormals_; }
   2606 
   2607  bool maybeCrossRealm() const { return maybeCrossRealm_; }
   2608  void setNotCrossRealm() { maybeCrossRealm_ = false; }
   2609 
   2610  bool ignoresReturnValue() const { return ignoresReturnValue_; }
   2611  void setIgnoresReturnValue() { ignoresReturnValue_ = true; }
   2612 
   2613  bool isConstructing() const { return false; }
   2614 
   2615  bool possiblyCalls() const override { return true; }
   2616 };
   2617 
   2618 class MApplyArgsObj
   2619    : public MTernaryInstruction,
   2620      public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>, BoxPolicy<2>>::Data {
   2621  WrappedFunction* target_;
   2622  bool maybeCrossRealm_ = true;
   2623  bool ignoresReturnValue_ = false;
   2624 
   2625  MApplyArgsObj(WrappedFunction* target, MDefinition* fun, MDefinition* argsObj,
   2626                MDefinition* thisArg)
   2627      : MTernaryInstruction(classOpcode, fun, argsObj, thisArg),
   2628        target_(target) {
   2629    MOZ_ASSERT(argsObj->type() == MIRType::Object);
   2630    setResultType(MIRType::Value);
   2631  }
   2632 
   2633 public:
   2634  INSTRUCTION_HEADER(ApplyArgsObj)
   2635  TRIVIAL_NEW_WRAPPERS
   2636  NAMED_OPERANDS((0, getFunction), (1, getArgsObj), (2, getThis))
   2637 
   2638  WrappedFunction* getSingleTarget() const { return target_; }
   2639 
   2640  bool maybeCrossRealm() const { return maybeCrossRealm_; }
   2641  void setNotCrossRealm() { maybeCrossRealm_ = false; }
   2642 
   2643  bool ignoresReturnValue() const { return ignoresReturnValue_; }
   2644  void setIgnoresReturnValue() { ignoresReturnValue_ = true; }
   2645 
   2646  bool isConstructing() const { return false; }
   2647 
   2648  bool possiblyCalls() const override { return true; }
   2649 };
   2650 
   2651 // fun.apply(fn, array)
   2652 class MApplyArray : public MTernaryInstruction,
   2653                    public MixPolicy<ObjectPolicy<0>, BoxPolicy<2>>::Data {
   2654  // Single target from CacheIR, or nullptr
   2655  WrappedFunction* target_;
   2656  bool maybeCrossRealm_ = true;
   2657  bool ignoresReturnValue_ = false;
   2658 
   2659  MApplyArray(WrappedFunction* target, MDefinition* fun, MDefinition* elements,
   2660              MDefinition* self)
   2661      : MTernaryInstruction(classOpcode, fun, elements, self), target_(target) {
   2662    MOZ_ASSERT(elements->type() == MIRType::Elements);
   2663    setResultType(MIRType::Value);
   2664  }
   2665 
   2666 public:
   2667  INSTRUCTION_HEADER(ApplyArray)
   2668  TRIVIAL_NEW_WRAPPERS
   2669  NAMED_OPERANDS((0, getFunction), (1, getElements), (2, getThis))
   2670 
   2671  WrappedFunction* getSingleTarget() const { return target_; }
   2672 
   2673  bool maybeCrossRealm() const { return maybeCrossRealm_; }
   2674  void setNotCrossRealm() { maybeCrossRealm_ = false; }
   2675 
   2676  bool ignoresReturnValue() const { return ignoresReturnValue_; }
   2677  void setIgnoresReturnValue() { ignoresReturnValue_ = true; }
   2678 
   2679  bool isConstructing() const { return false; }
   2680 
   2681  bool possiblyCalls() const override { return true; }
   2682 };
   2683 
   2684 // |new F(...arguments)| and |super(...arguments)|.
   2685 class MConstructArgs : public MQuaternaryInstruction,
   2686                       public MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>,
   2687                                        BoxPolicy<2>, ObjectPolicy<3>>::Data {
   2688  // Single target from CacheIR, or nullptr
   2689  WrappedFunction* target_;
   2690  // Number of extra initial formals to skip.
   2691  uint32_t numExtraFormals_;
   2692  bool maybeCrossRealm_ = true;
   2693 
   2694  MConstructArgs(WrappedFunction* target, MDefinition* fun, MDefinition* argc,
   2695                 MDefinition* thisValue, MDefinition* newTarget,
   2696                 uint32_t numExtraFormals = 0)
   2697      : MQuaternaryInstruction(classOpcode, fun, argc, thisValue, newTarget),
   2698        target_(target),
   2699        numExtraFormals_(numExtraFormals) {
   2700    MOZ_ASSERT(argc->type() == MIRType::Int32);
   2701    setResultType(MIRType::Value);
   2702  }
   2703 
   2704 public:
   2705  INSTRUCTION_HEADER(ConstructArgs)
   2706  TRIVIAL_NEW_WRAPPERS
   2707  NAMED_OPERANDS((0, getFunction), (1, getArgc), (2, getThis),
   2708                 (3, getNewTarget))
   2709 
   2710  WrappedFunction* getSingleTarget() const { return target_; }
   2711 
   2712  uint32_t numExtraFormals() const { return numExtraFormals_; }
   2713 
   2714  bool maybeCrossRealm() const { return maybeCrossRealm_; }
   2715  void setNotCrossRealm() { maybeCrossRealm_ = false; }
   2716 
   2717  bool ignoresReturnValue() const { return false; }
   2718  bool isConstructing() const { return true; }
   2719 
   2720  bool possiblyCalls() const override { return true; }
   2721 };
   2722 
   2723 // |new F(...args)| and |super(...args)|.
   2724 class MConstructArray
   2725    : public MQuaternaryInstruction,
   2726      public MixPolicy<ObjectPolicy<0>, BoxPolicy<2>, ObjectPolicy<3>>::Data {
   2727  // Single target from CacheIR, or nullptr
   2728  WrappedFunction* target_;
   2729  bool maybeCrossRealm_ = true;
   2730  bool needsThisCheck_ = false;
   2731 
   2732  MConstructArray(WrappedFunction* target, MDefinition* fun,
   2733                  MDefinition* elements, MDefinition* thisValue,
   2734                  MDefinition* newTarget)
   2735      : MQuaternaryInstruction(classOpcode, fun, elements, thisValue,
   2736                               newTarget),
   2737        target_(target) {
   2738    MOZ_ASSERT(elements->type() == MIRType::Elements);
   2739    setResultType(MIRType::Value);
   2740  }
   2741 
   2742 public:
   2743  INSTRUCTION_HEADER(ConstructArray)
   2744  TRIVIAL_NEW_WRAPPERS
   2745  NAMED_OPERANDS((0, getFunction), (1, getElements), (2, getThis),
   2746                 (3, getNewTarget))
   2747 
   2748  WrappedFunction* getSingleTarget() const { return target_; }
   2749 
   2750  bool maybeCrossRealm() const { return maybeCrossRealm_; }
   2751  void setNotCrossRealm() { maybeCrossRealm_ = false; }
   2752 
   2753  bool needsThisCheck() const { return needsThisCheck_; }
   2754  void setNeedsThisCheck() { needsThisCheck_ = true; }
   2755 
   2756  bool ignoresReturnValue() const { return false; }
   2757  bool isConstructing() const { return true; }
   2758 
   2759  bool possiblyCalls() const override { return true; }
   2760 };
   2761 
   2762 class MBail : public MNullaryInstruction {
   2763  explicit MBail(BailoutKind kind) : MNullaryInstruction(classOpcode) {
   2764    setBailoutKind(kind);
   2765    setGuard();
   2766  }
   2767 
   2768 public:
   2769  INSTRUCTION_HEADER(Bail)
   2770 
   2771  static MBail* New(TempAllocator& alloc, BailoutKind kind) {
   2772    return new (alloc) MBail(kind);
   2773  }
   2774  static MBail* New(TempAllocator& alloc) {
   2775    return new (alloc) MBail(BailoutKind::Inevitable);
   2776  }
   2777 
   2778  AliasSet getAliasSet() const override { return AliasSet::None(); }
   2779 };
   2780 
   2781 class MUnreachable : public MAryControlInstruction<0, 0>,
   2782                     public NoTypePolicy::Data {
   2783  MUnreachable() : MAryControlInstruction(classOpcode) {}
   2784 
   2785 public:
   2786  INSTRUCTION_HEADER(Unreachable)
   2787  TRIVIAL_NEW_WRAPPERS
   2788 
   2789  AliasSet getAliasSet() const override { return AliasSet::None(); }
   2790 };
   2791 
   2792 class MAssertRecoveredOnBailout : public MUnaryInstruction,
   2793                                  public NoTypePolicy::Data {
   2794  bool mustBeRecovered_;
   2795 
   2796  MAssertRecoveredOnBailout(MDefinition* ins, bool mustBeRecovered)
   2797      : MUnaryInstruction(classOpcode, ins), mustBeRecovered_(mustBeRecovered) {
   2798    setResultType(MIRType::Value);
   2799    setRecoveredOnBailout();
   2800    setGuard();
   2801  }
   2802 
   2803 public:
   2804  INSTRUCTION_HEADER(AssertRecoveredOnBailout)
   2805  TRIVIAL_NEW_WRAPPERS
   2806 
   2807  // Needed to assert that float32 instructions are correctly recovered.
   2808  bool canConsumeFloat32(MUse* use) const override { return true; }
   2809 
   2810  [[nodiscard]] bool writeRecoverData(
   2811      CompactBufferWriter& writer) const override;
   2812  bool canRecoverOnBailout() const override { return true; }
   2813 };
   2814 
   2815 class MAssertFloat32 : public MUnaryInstruction, public NoTypePolicy::Data {
   2816  bool mustBeFloat32_;
   2817 
   2818  MAssertFloat32(MDefinition* value, bool mustBeFloat32)
   2819      : MUnaryInstruction(classOpcode, value), mustBeFloat32_(mustBeFloat32) {}
   2820 
   2821 public:
   2822  INSTRUCTION_HEADER(AssertFloat32)
   2823  TRIVIAL_NEW_WRAPPERS
   2824 
   2825  bool canConsumeFloat32(MUse* use) const override { return true; }
   2826 
   2827  bool mustBeFloat32() const { return mustBeFloat32_; }
   2828 };
   2829 
   2830 class MCompare : public MBinaryInstruction, public ComparePolicy::Data {
   2831 public:
   2832  enum CompareType {
   2833 
   2834    // Anything compared to Undefined
   2835    Compare_Undefined,
   2836 
   2837    // Anything compared to Null
   2838    Compare_Null,
   2839 
   2840    // Int32   compared to Int32
   2841    // Boolean compared to Boolean
   2842    Compare_Int32,
   2843 
   2844    // Int32 compared as unsigneds
   2845    Compare_UInt32,
   2846 
   2847    // Int64 compared to Int64.
   2848    Compare_Int64,
   2849 
   2850    // Int64 compared as unsigneds.
   2851    Compare_UInt64,
   2852 
   2853    // IntPtr compared to IntPtr.
   2854    Compare_IntPtr,
   2855 
   2856    // IntPtr compared as unsigneds.
   2857    Compare_UIntPtr,
   2858 
   2859    // Double compared to Double
   2860    Compare_Double,
   2861 
   2862    // Float compared to Float
   2863    Compare_Float32,
   2864 
   2865    // String compared to String
   2866    Compare_String,
   2867 
   2868    // Symbol compared to Symbol
   2869    Compare_Symbol,
   2870 
   2871    // Object compared to Object
   2872    Compare_Object,
   2873 
   2874    // BigInt compared to BigInt
   2875    Compare_BigInt,
   2876 
   2877    // BigInt compared to Int32
   2878    Compare_BigInt_Int32,
   2879 
   2880    // BigInt compared to Double
   2881    Compare_BigInt_Double,
   2882 
   2883    // BigInt compared to String
   2884    Compare_BigInt_String,
   2885 
   2886    // Wasm Ref/AnyRef/NullRef compared to Ref/AnyRef/NullRef
   2887    Compare_WasmAnyRef,
   2888  };
   2889 
   2890 private:
   2891  CompareType compareType_;
   2892  JSOp jsop_;
   2893  bool operandsAreNeverNaN_;
   2894 
   2895  // When a floating-point comparison is converted to an integer comparison
   2896  // (when range analysis proves it safe), we need to convert the operands
   2897  // to integer as well.
   2898  bool truncateOperands_;
   2899 
   2900  MCompare(MDefinition* left, MDefinition* right, JSOp jsop,
   2901           CompareType compareType)
   2902      : MBinaryInstruction(classOpcode, left, right),
   2903        compareType_(compareType),
   2904        jsop_(jsop),
   2905        operandsAreNeverNaN_(false),
   2906        truncateOperands_(false) {
   2907    setResultType(MIRType::Boolean);
   2908    setMovable();
   2909  }
   2910 
   2911 public:
   2912  INSTRUCTION_HEADER(Compare)
   2913  TRIVIAL_NEW_WRAPPERS
   2914 
   2915  static MCompare* NewWasm(TempAllocator& alloc, MDefinition* left,
   2916                           MDefinition* right, JSOp jsop,
   2917                           CompareType compareType) {
   2918    MOZ_ASSERT(compareType == Compare_Int32 || compareType == Compare_UInt32 ||
   2919               compareType == Compare_Int64 || compareType == Compare_UInt64 ||
   2920               compareType == Compare_Double ||
   2921               compareType == Compare_Float32 ||
   2922               compareType == Compare_WasmAnyRef);
   2923    auto* ins = MCompare::New(alloc, left, right, jsop, compareType);
   2924    ins->setResultType(MIRType::Int32);
   2925    return ins;
   2926  }
   2927 
   2928  [[nodiscard]] bool tryFold(bool* result);
   2929  [[nodiscard]] bool evaluateConstantOperands(TempAllocator& alloc,
   2930                                              bool* result);
   2931  MDefinition* foldsTo(TempAllocator& alloc) override;
   2932 
   2933  CompareType compareType() const { return compareType_; }
   2934  bool isInt32Comparison() const { return compareType() == Compare_Int32; }
   2935  bool isDoubleComparison() const { return compareType() == Compare_Double; }
   2936  bool isFloat32Comparison() const { return compareType() == Compare_Float32; }
   2937  bool isNumericComparison() const {
   2938    return isInt32Comparison() || isDoubleComparison() || isFloat32Comparison();
   2939  }
   2940 
   2941  JSOp jsop() const { return jsop_; }
   2942  bool operandsAreNeverNaN() const { return operandsAreNeverNaN_; }
   2943  AliasSet getAliasSet() const override { return AliasSet::None(); }
   2944 
   2945 #ifdef JS_JITSPEW
   2946  void printOpcode(GenericPrinter& out) const override;
   2947 #endif
   2948  void collectRangeInfoPreTrunc() override;
   2949 
   2950  void trySpecializeFloat32(TempAllocator& alloc) override;
   2951  bool isFloat32Commutative() const override { return true; }
   2952  bool canTruncate() const override;
   2953  void truncate(TruncateKind kind) override;
   2954  TruncateKind operandTruncateKind(size_t index) const override;
   2955 
   2956 #ifdef DEBUG
   2957  bool isConsistentFloat32Use(MUse* use) const override {
   2958    // Both sides of the compare can be Float32
   2959    return compareType_ == Compare_Float32;
   2960  }
   2961 #endif
   2962 
   2963  ALLOW_CLONE(MCompare)
   2964 
   2965 private:
   2966  [[nodiscard]] bool tryFoldEqualOperands(bool* result);
   2967  [[nodiscard]] bool tryFoldTypeOf(bool* result);
   2968  [[nodiscard]] MDefinition* tryFoldTypeOf(TempAllocator& alloc);
   2969  [[nodiscard]] MDefinition* tryFoldCharCompare(TempAllocator& alloc);
   2970  [[nodiscard]] MDefinition* tryFoldStringCompare(TempAllocator& alloc);
   2971  [[nodiscard]] MDefinition* tryFoldStringSubstring(TempAllocator& alloc);
   2972  [[nodiscard]] MDefinition* tryFoldStringIndexOf(TempAllocator& alloc);
   2973  [[nodiscard]] MDefinition* tryFoldBigInt64(TempAllocator& alloc);
   2974  [[nodiscard]] MDefinition* tryFoldBigIntPtr(TempAllocator& alloc);
   2975  [[nodiscard]] MDefinition* tryFoldBigInt(TempAllocator& alloc);
   2976 
   2977 public:
   2978  bool congruentTo(const MDefinition* ins) const override {
   2979    if (!binaryCongruentTo(ins)) {
   2980      return false;
   2981    }
   2982    return compareType() == ins->toCompare()->compareType() &&
   2983           jsop() == ins->toCompare()->jsop();
   2984  }
   2985 
   2986  [[nodiscard]] bool writeRecoverData(
   2987      CompactBufferWriter& writer) const override;
   2988  bool canRecoverOnBailout() const override {
   2989    switch (compareType_) {
   2990      case Compare_Undefined:
   2991      case Compare_Null:
   2992      case Compare_Int32:
   2993      case Compare_UInt32:
   2994      case Compare_Double:
   2995      case Compare_Float32:
   2996      case Compare_String:
   2997      case Compare_Symbol:
   2998      case Compare_Object:
   2999      case Compare_BigInt:
   3000      case Compare_BigInt_Int32:
   3001      case Compare_BigInt_Double:
   3002      case Compare_BigInt_String:
   3003        return true;
   3004 
   3005      case Compare_Int64:
   3006      case Compare_UInt64:
   3007      case Compare_IntPtr:
   3008      case Compare_UIntPtr:
   3009      case Compare_WasmAnyRef:
   3010        return false;
   3011    }
   3012    MOZ_CRASH("unexpected compare type");
   3013  }
   3014 
   3015 #ifdef JS_JITSPEW
   3016  void getExtras(ExtrasCollector* extras) const override {
   3017    const char* ty = nullptr;
   3018    switch (compareType_) {
   3019      case Compare_Undefined:
   3020        ty = "Undefined";
   3021        break;
   3022      case Compare_Null:
   3023        ty = "Null";
   3024        break;
   3025      case Compare_Int32:
   3026        ty = "Int32";
   3027        break;
   3028      case Compare_UInt32:
   3029        ty = "UInt32";
   3030        break;
   3031      case Compare_Int64:
   3032        ty = "Int64";
   3033        break;
   3034      case Compare_UInt64:
   3035        ty = "UInt64";
   3036        break;
   3037      case Compare_IntPtr:
   3038        ty = "IntPtr";
   3039        break;
   3040      case Compare_UIntPtr:
   3041        ty = "UIntPtr";
   3042        break;
   3043      case Compare_Double:
   3044        ty = "Double";
   3045        break;
   3046      case Compare_Float32:
   3047        ty = "Float32";
   3048        break;
   3049      case Compare_String:
   3050        ty = "String";
   3051        break;
   3052      case Compare_Symbol:
   3053        ty = "Symbol";
   3054        break;
   3055      case Compare_Object:
   3056        ty = "Object";
   3057        break;
   3058      case Compare_BigInt:
   3059        ty = "BigInt";
   3060        break;
   3061      case Compare_BigInt_Int32:
   3062        ty = "BigInt_Int32";
   3063        break;
   3064      case Compare_BigInt_Double:
   3065        ty = "BigInt_Double";
   3066        break;
   3067      case Compare_BigInt_String:
   3068        ty = "BigInt_String";
   3069        break;
   3070      case Compare_WasmAnyRef:
   3071        ty = "WasmAnyRef";
   3072        break;
   3073      default:
   3074        ty = "!!unknown!!";
   3075        break;
   3076    };
   3077    char buf[64];
   3078    SprintfLiteral(buf, "ty=%s jsop=%s", ty, CodeName(jsop()));
   3079    extras->add(buf);
   3080  }
   3081 #endif
   3082 };
   3083 
   3084 // Takes a typed value and returns an untyped value.
   3085 class MBox : public MUnaryInstruction, public NoTypePolicy::Data {
   3086  explicit MBox(MDefinition* ins) : MUnaryInstruction(classOpcode, ins) {
   3087    // Cannot box a box.
   3088    MOZ_ASSERT(ins->type() != MIRType::Value);
   3089 
   3090    setResultType(MIRType::Value);
   3091    setMovable();
   3092  }
   3093 
   3094 public:
   3095  INSTRUCTION_HEADER(Box)
   3096  TRIVIAL_NEW_WRAPPERS
   3097 
   3098  bool congruentTo(const MDefinition* ins) const override {
   3099    return congruentIfOperandsEqual(ins);
   3100  }
   3101 
   3102  MDefinition* foldsTo(TempAllocator& alloc) override;
   3103 
   3104  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3105 
   3106  ALLOW_CLONE(MBox)
   3107 };
   3108 
   3109 // Note: the op may have been inverted during lowering (to put constants in a
   3110 // position where they can be immediates), so it is important to use the
   3111 // lir->jsop() instead of the mir->jsop() when it is present.
   3112 static inline Assembler::Condition JSOpToCondition(
   3113    MCompare::CompareType compareType, JSOp op) {
   3114  bool isSigned = (compareType != MCompare::Compare_UInt32 &&
   3115                   compareType != MCompare::Compare_UInt64 &&
   3116                   compareType != MCompare::Compare_UIntPtr);
   3117  return JSOpToCondition(op, isSigned);
   3118 }
   3119 
   3120 // Takes a typed value and checks if it is a certain type. If so, the payload
   3121 // is unpacked and returned as that type. Otherwise, it is considered a
   3122 // deoptimization.
   3123 class MUnbox final : public MUnaryInstruction, public BoxInputsPolicy::Data {
   3124 public:
   3125  enum Mode {
   3126    Fallible,    // Check the type, and deoptimize if unexpected.
   3127    Infallible,  // Type guard is not necessary.
   3128  };
   3129 
   3130 private:
   3131  Mode mode_;
   3132 
   3133  MUnbox(MDefinition* ins, MIRType type, Mode mode)
   3134      : MUnaryInstruction(classOpcode, ins), mode_(mode) {
   3135    // Only allow unboxing a non MIRType::Value when input and output types
   3136    // don't match. This is often used to force a bailout. Boxing happens
   3137    // during type analysis.
   3138    MOZ_ASSERT_IF(ins->type() != MIRType::Value, type != ins->type());
   3139 
   3140    MOZ_ASSERT(type == MIRType::Boolean || type == MIRType::Int32 ||
   3141               type == MIRType::Double || type == MIRType::String ||
   3142               type == MIRType::Symbol || type == MIRType::BigInt ||
   3143               type == MIRType::Object);
   3144 
   3145    setResultType(type);
   3146    setMovable();
   3147 
   3148    if (mode_ == Fallible) {
   3149      setGuard();
   3150    }
   3151  }
   3152 
   3153 public:
   3154  INSTRUCTION_HEADER(Unbox)
   3155  TRIVIAL_NEW_WRAPPERS
   3156 
   3157  Mode mode() const { return mode_; }
   3158  bool fallible() const { return mode() != Infallible; }
   3159  bool congruentTo(const MDefinition* ins) const override {
   3160    if (!ins->isUnbox() || ins->toUnbox()->mode() != mode()) {
   3161      return false;
   3162    }
   3163    return congruentIfOperandsEqual(ins);
   3164  }
   3165 
   3166  MDefinition* foldsTo(TempAllocator& alloc) override;
   3167 
   3168  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3169 #ifdef JS_JITSPEW
   3170  void printOpcode(GenericPrinter& out) const override;
   3171 #endif
   3172 
   3173  ALLOW_CLONE(MUnbox)
   3174 };
   3175 
   3176 class MAssertRange : public MUnaryInstruction, public NoTypePolicy::Data {
   3177  // This is the range checked by the assertion. Don't confuse this with the
   3178  // range_ member or the range() accessor. Since MAssertRange doesn't return
   3179  // a value, it doesn't use those.
   3180  const Range* assertedRange_;
   3181 
   3182  MAssertRange(MDefinition* ins, const Range* assertedRange)
   3183      : MUnaryInstruction(classOpcode, ins), assertedRange_(assertedRange) {
   3184    setGuard();
   3185    setResultType(MIRType::None);
   3186  }
   3187 
   3188 public:
   3189  INSTRUCTION_HEADER(AssertRange)
   3190  TRIVIAL_NEW_WRAPPERS
   3191 
   3192  const Range* assertedRange() const { return assertedRange_; }
   3193 
   3194  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3195 
   3196 #ifdef JS_JITSPEW
   3197  void printOpcode(GenericPrinter& out) const override;
   3198 #endif
   3199 };
   3200 
   3201 class MAssertClass : public MUnaryInstruction, public NoTypePolicy::Data {
   3202  const JSClass* class_;
   3203 
   3204  MAssertClass(MDefinition* obj, const JSClass* clasp)
   3205      : MUnaryInstruction(classOpcode, obj), class_(clasp) {
   3206    MOZ_ASSERT(obj->type() == MIRType::Object);
   3207 
   3208    setGuard();
   3209    setResultType(MIRType::None);
   3210  }
   3211 
   3212 public:
   3213  INSTRUCTION_HEADER(AssertClass)
   3214  TRIVIAL_NEW_WRAPPERS
   3215 
   3216  const JSClass* getClass() const { return class_; }
   3217  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3218 };
   3219 
   3220 class MAssertShape : public MUnaryInstruction, public NoTypePolicy::Data {
   3221  CompilerShape shape_;
   3222 
   3223  MAssertShape(MDefinition* obj, Shape* shape)
   3224      : MUnaryInstruction(classOpcode, obj), shape_(shape) {
   3225    MOZ_ASSERT(obj->type() == MIRType::Object);
   3226 
   3227    setGuard();
   3228    setResultType(MIRType::None);
   3229  }
   3230 
   3231 public:
   3232  INSTRUCTION_HEADER(AssertShape)
   3233  TRIVIAL_NEW_WRAPPERS
   3234 
   3235  const Shape* shape() const { return shape_; }
   3236  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3237 };
   3238 
   3239 // Eager initialization of arguments object.
   3240 class MCreateArgumentsObject : public MUnaryInstruction,
   3241                               public ObjectPolicy<0>::Data {
   3242  CompilerGCPointer<ArgumentsObject*> templateObj_;
   3243 
   3244  MCreateArgumentsObject(MDefinition* callObj, ArgumentsObject* templateObj)
   3245      : MUnaryInstruction(classOpcode, callObj), templateObj_(templateObj) {
   3246    setResultType(MIRType::Object);
   3247  }
   3248 
   3249 public:
   3250  INSTRUCTION_HEADER(CreateArgumentsObject)
   3251  TRIVIAL_NEW_WRAPPERS
   3252  NAMED_OPERANDS((0, getCallObject))
   3253 
   3254  ArgumentsObject* templateObject() const { return templateObj_; }
   3255 
   3256  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3257 
   3258  bool possiblyCalls() const override { return true; }
   3259 
   3260  [[nodiscard]] bool writeRecoverData(
   3261      CompactBufferWriter& writer) const override;
   3262  bool canRecoverOnBailout() const override { return true; }
   3263 };
   3264 
   3265 // Eager initialization of arguments object for inlined function
   3266 class MCreateInlinedArgumentsObject : public MVariadicInstruction,
   3267                                      public NoFloatPolicyAfter<0>::Data {
   3268  CompilerGCPointer<ArgumentsObject*> templateObj_;
   3269 
   3270  explicit MCreateInlinedArgumentsObject(ArgumentsObject* templateObj)
   3271      : MVariadicInstruction(classOpcode), templateObj_(templateObj) {
   3272    setResultType(MIRType::Object);
   3273  }
   3274 
   3275  static const size_t NumNonArgumentOperands = 2;
   3276 
   3277 public:
   3278  INSTRUCTION_HEADER(CreateInlinedArgumentsObject)
   3279  static MCreateInlinedArgumentsObject* New(TempAllocator& alloc,
   3280                                            MDefinition* callObj,
   3281                                            MDefinition* callee,
   3282                                            MDefinitionVector& args,
   3283                                            ArgumentsObject* templateObj);
   3284  NAMED_OPERANDS((0, getCallObject), (1, getCallee))
   3285 
   3286  ArgumentsObject* templateObject() const { return templateObj_; }
   3287 
   3288  MDefinition* getArg(uint32_t idx) const {
   3289    return getOperand(idx + NumNonArgumentOperands);
   3290  }
   3291  uint32_t numActuals() const { return numOperands() - NumNonArgumentOperands; }
   3292 
   3293  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3294 
   3295  bool possiblyCalls() const override { return true; }
   3296 
   3297  [[nodiscard]] bool writeRecoverData(
   3298      CompactBufferWriter& writer) const override;
   3299  bool canRecoverOnBailout() const override { return true; }
   3300 };
   3301 
   3302 class MGetInlinedArgument
   3303    : public MVariadicInstruction,
   3304      public MixPolicy<UnboxedInt32Policy<0>, NoFloatPolicyAfter<1>>::Data {
   3305  MGetInlinedArgument() : MVariadicInstruction(classOpcode) {
   3306    setResultType(MIRType::Value);
   3307  }
   3308 
   3309  static const size_t NumNonArgumentOperands = 1;
   3310 
   3311 public:
   3312  INSTRUCTION_HEADER(GetInlinedArgument)
   3313  static MGetInlinedArgument* New(TempAllocator& alloc, MDefinition* index,
   3314                                  MCreateInlinedArgumentsObject* args);
   3315  static MGetInlinedArgument* New(TempAllocator& alloc, MDefinition* index,
   3316                                  const CallInfo& callInfo);
   3317  NAMED_OPERANDS((0, index))
   3318 
   3319  MDefinition* getArg(uint32_t idx) const {
   3320    return getOperand(idx + NumNonArgumentOperands);
   3321  }
   3322  uint32_t numActuals() const { return numOperands() - NumNonArgumentOperands; }
   3323 
   3324  bool congruentTo(const MDefinition* ins) const override {
   3325    return congruentIfOperandsEqual(ins);
   3326  }
   3327  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3328 
   3329  MDefinition* foldsTo(TempAllocator& alloc) override;
   3330 };
   3331 
   3332 class MGetInlinedArgumentHole
   3333    : public MVariadicInstruction,
   3334      public MixPolicy<UnboxedInt32Policy<0>, NoFloatPolicyAfter<1>>::Data {
   3335  MGetInlinedArgumentHole() : MVariadicInstruction(classOpcode) {
   3336    setGuard();
   3337    setResultType(MIRType::Value);
   3338  }
   3339 
   3340  static const size_t NumNonArgumentOperands = 1;
   3341 
   3342 public:
   3343  INSTRUCTION_HEADER(GetInlinedArgumentHole)
   3344  static MGetInlinedArgumentHole* New(TempAllocator& alloc, MDefinition* index,
   3345                                      MCreateInlinedArgumentsObject* args);
   3346  NAMED_OPERANDS((0, index))
   3347 
   3348  MDefinition* getArg(uint32_t idx) const {
   3349    return getOperand(idx + NumNonArgumentOperands);
   3350  }
   3351  uint32_t numActuals() const { return numOperands() - NumNonArgumentOperands; }
   3352 
   3353  bool congruentTo(const MDefinition* ins) const override {
   3354    return congruentIfOperandsEqual(ins);
   3355  }
   3356  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3357 
   3358  MDefinition* foldsTo(TempAllocator& alloc) override;
   3359 };
   3360 
   3361 class MInlineArgumentsSlice
   3362    : public MVariadicInstruction,
   3363      public MixPolicy<UnboxedInt32Policy<0>, UnboxedInt32Policy<1>,
   3364                       NoFloatPolicyAfter<2>>::Data {
   3365  JSObject* templateObj_;
   3366  gc::Heap initialHeap_;
   3367 
   3368  MInlineArgumentsSlice(JSObject* templateObj, gc::Heap initialHeap)
   3369      : MVariadicInstruction(classOpcode),
   3370        templateObj_(templateObj),
   3371        initialHeap_(initialHeap) {
   3372    setResultType(MIRType::Object);
   3373  }
   3374 
   3375  static const size_t NumNonArgumentOperands = 2;
   3376 
   3377 public:
   3378  INSTRUCTION_HEADER(InlineArgumentsSlice)
   3379  static MInlineArgumentsSlice* New(TempAllocator& alloc, MDefinition* begin,
   3380                                    MDefinition* count,
   3381                                    MCreateInlinedArgumentsObject* args,
   3382                                    JSObject* templateObj,
   3383                                    gc::Heap initialHeap);
   3384  NAMED_OPERANDS((0, begin), (1, count))
   3385 
   3386  JSObject* templateObj() const { return templateObj_; }
   3387  gc::Heap initialHeap() const { return initialHeap_; }
   3388 
   3389  MDefinition* getArg(uint32_t idx) const {
   3390    return getOperand(idx + NumNonArgumentOperands);
   3391  }
   3392  uint32_t numActuals() const { return numOperands() - NumNonArgumentOperands; }
   3393 
   3394  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3395 
   3396  bool possiblyCalls() const override { return true; }
   3397 };
   3398 
   3399 // Allocates a new BoundFunctionObject and calls
   3400 // BoundFunctionObject::functionBindImpl. This instruction can have arbitrary
   3401 // side-effects because the GetProperty calls for length/name can call into JS.
   3402 class MBindFunction
   3403    : public MVariadicInstruction,
   3404      public MixPolicy<ObjectPolicy<0>, NoFloatPolicyAfter<1>>::Data {
   3405  CompilerGCPointer<JSObject*> templateObj_;
   3406 
   3407  explicit MBindFunction(JSObject* templateObj)
   3408      : MVariadicInstruction(classOpcode), templateObj_(templateObj) {
   3409    setResultType(MIRType::Object);
   3410  }
   3411 
   3412  // The target object is operand 0.
   3413  static const size_t NumNonArgumentOperands = 1;
   3414 
   3415 public:
   3416  INSTRUCTION_HEADER(BindFunction)
   3417  static MBindFunction* New(TempAllocator& alloc, MDefinition* target,
   3418                            uint32_t argc, JSObject* templateObj);
   3419  NAMED_OPERANDS((0, target))
   3420 
   3421  JSObject* templateObject() const { return templateObj_; }
   3422 
   3423  MDefinition* getArg(uint32_t idx) const {
   3424    return getOperand(idx + NumNonArgumentOperands);
   3425  }
   3426  void initArg(size_t i, MDefinition* arg) {
   3427    initOperand(NumNonArgumentOperands + i, arg);
   3428  }
   3429  uint32_t numStackArgs() const {
   3430    return numOperands() - NumNonArgumentOperands;
   3431  }
   3432 
   3433  bool possiblyCalls() const override { return true; }
   3434 };
   3435 
   3436 class MToFPInstruction : public MUnaryInstruction, public ToDoublePolicy::Data {
   3437 protected:
   3438  MToFPInstruction(Opcode op, MDefinition* def, MIRType resultType)
   3439      : MUnaryInstruction(op, def) {
   3440    setResultType(resultType);
   3441    setMovable();
   3442 
   3443    // Guard unless the conversion is known to be non-effectful & non-throwing.
   3444    if (!def->typeIsOneOf({MIRType::Undefined, MIRType::Null, MIRType::Boolean,
   3445                           MIRType::Int32, MIRType::Double, MIRType::Float32,
   3446                           MIRType::String})) {
   3447      setGuard();
   3448    }
   3449  }
   3450 };
   3451 
   3452 // Converts a primitive (either typed or untyped) to a double. If the input is
   3453 // not primitive at runtime, a bailout occurs.
   3454 class MToDouble : public MToFPInstruction {
   3455 private:
   3456  TruncateKind implicitTruncate_ = TruncateKind::NoTruncate;
   3457 
   3458  explicit MToDouble(MDefinition* def)
   3459      : MToFPInstruction(classOpcode, def, MIRType::Double) {}
   3460 
   3461 public:
   3462  INSTRUCTION_HEADER(ToDouble)
   3463  TRIVIAL_NEW_WRAPPERS
   3464 
   3465  MDefinition* foldsTo(TempAllocator& alloc) override;
   3466  bool congruentTo(const MDefinition* ins) const override {
   3467    return congruentIfOperandsEqual(ins);
   3468  }
   3469  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3470 
   3471  void computeRange(TempAllocator& alloc) override;
   3472  bool canTruncate() const override;
   3473  void truncate(TruncateKind kind) override;
   3474  TruncateKind operandTruncateKind(size_t index) const override;
   3475 
   3476 #ifdef DEBUG
   3477  bool isConsistentFloat32Use(MUse* use) const override { return true; }
   3478 #endif
   3479 
   3480  TruncateKind truncateKind() const { return implicitTruncate_; }
   3481  void setTruncateKind(TruncateKind kind) {
   3482    implicitTruncate_ = std::max(implicitTruncate_, kind);
   3483  }
   3484 
   3485  [[nodiscard]] bool writeRecoverData(
   3486      CompactBufferWriter& writer) const override;
   3487  bool canRecoverOnBailout() const override {
   3488    if (input()->type() == MIRType::Value) {
   3489      return false;
   3490    }
   3491    if (input()->type() == MIRType::Symbol) {
   3492      return false;
   3493    }
   3494    if (input()->type() == MIRType::BigInt) {
   3495      return false;
   3496    }
   3497 
   3498    return true;
   3499  }
   3500 
   3501  ALLOW_CLONE(MToDouble)
   3502 };
   3503 
   3504 // Converts a primitive (either typed or untyped) to a float32. If the input is
   3505 // not primitive at runtime, a bailout occurs.
   3506 class MToFloat32 : public MToFPInstruction {
   3507  bool mustPreserveNaN_ = false;
   3508 
   3509  explicit MToFloat32(MDefinition* def)
   3510      : MToFPInstruction(classOpcode, def, MIRType::Float32) {}
   3511 
   3512  explicit MToFloat32(MDefinition* def, bool mustPreserveNaN)
   3513      : MToFloat32(def) {
   3514    mustPreserveNaN_ = mustPreserveNaN;
   3515  }
   3516 
   3517 public:
   3518  INSTRUCTION_HEADER(ToFloat32)
   3519  TRIVIAL_NEW_WRAPPERS
   3520 
   3521  virtual MDefinition* foldsTo(TempAllocator& alloc) override;
   3522  bool congruentTo(const MDefinition* ins) const override {
   3523    if (!congruentIfOperandsEqual(ins)) {
   3524      return false;
   3525    }
   3526    return ins->toToFloat32()->mustPreserveNaN_ == mustPreserveNaN_;
   3527  }
   3528  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3529 
   3530  void computeRange(TempAllocator& alloc) override;
   3531 
   3532  bool canConsumeFloat32(MUse* use) const override { return true; }
   3533  bool canProduceFloat32() const override { return true; }
   3534 
   3535  [[nodiscard]] bool writeRecoverData(
   3536      CompactBufferWriter& writer) const override;
   3537  bool canRecoverOnBailout() const override { return true; }
   3538 
   3539  ALLOW_CLONE(MToFloat32)
   3540 };
   3541 
   3542 // Converts a primitive (either typed or untyped) to a float16. If the input is
   3543 // not primitive at runtime, a bailout occurs.
   3544 class MToFloat16 : public MToFPInstruction {
   3545  explicit MToFloat16(MDefinition* def)
   3546      : MToFPInstruction(classOpcode, def, MIRType::Float32) {}
   3547 
   3548 public:
   3549  INSTRUCTION_HEADER(ToFloat16)
   3550  TRIVIAL_NEW_WRAPPERS
   3551 
   3552  virtual MDefinition* foldsTo(TempAllocator& alloc) override;
   3553  bool congruentTo(const MDefinition* ins) const override {
   3554    return congruentIfOperandsEqual(ins);
   3555  }
   3556  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3557 
   3558  // This instruction can produce but NOT consume float32.
   3559  bool canProduceFloat32() const override { return true; }
   3560 
   3561 #ifdef DEBUG
   3562  // Float16 inputs are typed as float32, but this instruction can NOT consume
   3563  // float32.
   3564  bool isConsistentFloat32Use(MUse* use) const override { return true; }
   3565 #endif
   3566 
   3567  [[nodiscard]] bool writeRecoverData(
   3568      CompactBufferWriter& writer) const override;
   3569  bool canRecoverOnBailout() const override { return true; }
   3570 
   3571  ALLOW_CLONE(MToFloat16)
   3572 };
   3573 
   3574 class MWrapInt64ToInt32 : public MUnaryInstruction, public NoTypePolicy::Data {
   3575  bool bottomHalf_;
   3576 
   3577  explicit MWrapInt64ToInt32(MDefinition* def, bool bottomHalf = true)
   3578      : MUnaryInstruction(classOpcode, def), bottomHalf_(bottomHalf) {
   3579    setResultType(MIRType::Int32);
   3580    setMovable();
   3581  }
   3582 
   3583 public:
   3584  INSTRUCTION_HEADER(WrapInt64ToInt32)
   3585  TRIVIAL_NEW_WRAPPERS
   3586 
   3587  MDefinition* foldsTo(TempAllocator& alloc) override;
   3588  bool congruentTo(const MDefinition* ins) const override {
   3589    if (!ins->isWrapInt64ToInt32()) {
   3590      return false;
   3591    }
   3592    if (ins->toWrapInt64ToInt32()->bottomHalf() != bottomHalf()) {
   3593      return false;
   3594    }
   3595    return congruentIfOperandsEqual(ins);
   3596  }
   3597  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3598 
   3599  bool bottomHalf() const { return bottomHalf_; }
   3600 
   3601  ALLOW_CLONE(MWrapInt64ToInt32)
   3602 };
   3603 
   3604 class MExtendInt32ToInt64 : public MUnaryInstruction,
   3605                            public NoTypePolicy::Data {
   3606  bool isUnsigned_;
   3607 
   3608  MExtendInt32ToInt64(MDefinition* def, bool isUnsigned)
   3609      : MUnaryInstruction(classOpcode, def), isUnsigned_(isUnsigned) {
   3610    setResultType(MIRType::Int64);
   3611    setMovable();
   3612  }
   3613 
   3614 public:
   3615  INSTRUCTION_HEADER(ExtendInt32ToInt64)
   3616  TRIVIAL_NEW_WRAPPERS
   3617 
   3618  bool isUnsigned() const { return isUnsigned_; }
   3619 
   3620  MDefinition* foldsTo(TempAllocator& alloc) override;
   3621  bool congruentTo(const MDefinition* ins) const override {
   3622    if (!ins->isExtendInt32ToInt64()) {
   3623      return false;
   3624    }
   3625    if (ins->toExtendInt32ToInt64()->isUnsigned_ != isUnsigned_) {
   3626      return false;
   3627    }
   3628    return congruentIfOperandsEqual(ins);
   3629  }
   3630  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3631 
   3632  ALLOW_CLONE(MExtendInt32ToInt64)
   3633 };
   3634 
   3635 // Converts an int32 value to intptr by sign-extending it.
   3636 class MInt32ToIntPtr : public MUnaryInstruction,
   3637                       public UnboxedInt32Policy<0>::Data {
   3638  bool canBeNegative_ = true;
   3639 
   3640  explicit MInt32ToIntPtr(MDefinition* def)
   3641      : MUnaryInstruction(classOpcode, def) {
   3642    setResultType(MIRType::IntPtr);
   3643    setMovable();
   3644  }
   3645 
   3646 public:
   3647  INSTRUCTION_HEADER(Int32ToIntPtr)
   3648  TRIVIAL_NEW_WRAPPERS
   3649 
   3650  bool canBeNegative() const { return canBeNegative_; }
   3651  void setCanNotBeNegative() { canBeNegative_ = false; }
   3652 
   3653  void computeRange(TempAllocator& alloc) override;
   3654  void collectRangeInfoPreTrunc() override;
   3655 
   3656  MDefinition* foldsTo(TempAllocator& alloc) override;
   3657 
   3658  bool congruentTo(const MDefinition* ins) const override {
   3659    return congruentIfOperandsEqual(ins);
   3660  }
   3661  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3662 };
   3663 
   3664 // Converts an IntPtr value >= 0 to Int32. Bails out if the value > INT32_MAX.
   3665 class MNonNegativeIntPtrToInt32 : public MUnaryInstruction,
   3666                                  public NoTypePolicy::Data {
   3667  explicit MNonNegativeIntPtrToInt32(MDefinition* def)
   3668      : MUnaryInstruction(classOpcode, def) {
   3669    MOZ_ASSERT(def->type() == MIRType::IntPtr);
   3670    setResultType(MIRType::Int32);
   3671    setMovable();
   3672  }
   3673 
   3674 public:
   3675  INSTRUCTION_HEADER(NonNegativeIntPtrToInt32)
   3676  TRIVIAL_NEW_WRAPPERS
   3677 
   3678  void computeRange(TempAllocator& alloc) override;
   3679 
   3680  bool congruentTo(const MDefinition* ins) const override {
   3681    return congruentIfOperandsEqual(ins);
   3682  }
   3683  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3684 };
   3685 
   3686 // Converts an IntPtr value to Double.
   3687 class MIntPtrToDouble : public MUnaryInstruction, public NoTypePolicy::Data {
   3688  explicit MIntPtrToDouble(MDefinition* def)
   3689      : MUnaryInstruction(classOpcode, def) {
   3690    MOZ_ASSERT(def->type() == MIRType::IntPtr);
   3691    setResultType(MIRType::Double);
   3692    setMovable();
   3693  }
   3694 
   3695 public:
   3696  INSTRUCTION_HEADER(IntPtrToDouble)
   3697  TRIVIAL_NEW_WRAPPERS
   3698 
   3699  bool congruentTo(const MDefinition* ins) const override {
   3700    return congruentIfOperandsEqual(ins);
   3701  }
   3702  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3703 };
   3704 
   3705 // Subtracts (byteSize - 1) from the input value. Bails out if the result is
   3706 // negative. This is used to implement bounds checks for DataView accesses.
   3707 class MAdjustDataViewLength : public MUnaryInstruction,
   3708                              public NoTypePolicy::Data {
   3709  const uint32_t byteSize_;
   3710 
   3711  MAdjustDataViewLength(MDefinition* input, uint32_t byteSize)
   3712      : MUnaryInstruction(classOpcode, input), byteSize_(byteSize) {
   3713    MOZ_ASSERT(input->type() == MIRType::IntPtr);
   3714    MOZ_ASSERT(byteSize > 1);
   3715    setResultType(MIRType::IntPtr);
   3716    setMovable();
   3717    setGuard();
   3718  }
   3719 
   3720 public:
   3721  INSTRUCTION_HEADER(AdjustDataViewLength)
   3722  TRIVIAL_NEW_WRAPPERS
   3723 
   3724  uint32_t byteSize() const { return byteSize_; }
   3725 
   3726  bool congruentTo(const MDefinition* ins) const override {
   3727    if (!ins->isAdjustDataViewLength()) {
   3728      return false;
   3729    }
   3730    if (ins->toAdjustDataViewLength()->byteSize() != byteSize()) {
   3731      return false;
   3732    }
   3733    return congruentIfOperandsEqual(ins);
   3734  }
   3735  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3736 };
   3737 
   3738 class MInt64ToFloatingPoint : public MUnaryInstruction,
   3739                              public NoTypePolicy::Data {
   3740  bool isUnsigned_;
   3741  wasm::BytecodeOffset bytecodeOffset_;
   3742 
   3743  MInt64ToFloatingPoint(MDefinition* def, MIRType type,
   3744                        wasm::BytecodeOffset bytecodeOffset, bool isUnsigned)
   3745      : MUnaryInstruction(classOpcode, def),
   3746        isUnsigned_(isUnsigned),
   3747        bytecodeOffset_(bytecodeOffset) {
   3748    MOZ_ASSERT(IsFloatingPointType(type));
   3749    setResultType(type);
   3750    setMovable();
   3751  }
   3752 
   3753 public:
   3754  INSTRUCTION_HEADER(Int64ToFloatingPoint)
   3755  TRIVIAL_NEW_WRAPPERS
   3756 
   3757  bool isUnsigned() const { return isUnsigned_; }
   3758  wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
   3759 
   3760  bool congruentTo(const MDefinition* ins) const override {
   3761    if (!ins->isInt64ToFloatingPoint()) {
   3762      return false;
   3763    }
   3764    if (ins->toInt64ToFloatingPoint()->isUnsigned_ != isUnsigned_) {
   3765      return false;
   3766    }
   3767    return congruentIfOperandsEqual(ins);
   3768  }
   3769  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3770 };
   3771 
   3772 // It used only for arm now because on arm we need to call builtin to convert
   3773 // i64 to float.
   3774 class MBuiltinInt64ToFloatingPoint : public MAryInstruction<2>,
   3775                                     public NoTypePolicy::Data {
   3776  bool isUnsigned_;
   3777  wasm::BytecodeOffset bytecodeOffset_;
   3778 
   3779  MBuiltinInt64ToFloatingPoint(MDefinition* def, MDefinition* instance,
   3780                               MIRType type,
   3781                               wasm::BytecodeOffset bytecodeOffset,
   3782                               bool isUnsigned)
   3783      : MAryInstruction(classOpcode),
   3784        isUnsigned_(isUnsigned),
   3785        bytecodeOffset_(bytecodeOffset) {
   3786    MOZ_ASSERT(IsFloatingPointType(type));
   3787    initOperand(0, def);
   3788    initOperand(1, instance);
   3789    setResultType(type);
   3790    setMovable();
   3791  }
   3792 
   3793 public:
   3794  INSTRUCTION_HEADER(BuiltinInt64ToFloatingPoint)
   3795  NAMED_OPERANDS((0, input), (1, instance));
   3796  TRIVIAL_NEW_WRAPPERS
   3797 
   3798  bool isUnsigned() const { return isUnsigned_; }
   3799  wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
   3800 
   3801  bool congruentTo(const MDefinition* ins) const override {
   3802    if (!ins->isBuiltinInt64ToFloatingPoint()) {
   3803      return false;
   3804    }
   3805    if (ins->toBuiltinInt64ToFloatingPoint()->isUnsigned_ != isUnsigned_) {
   3806      return false;
   3807    }
   3808    return congruentIfOperandsEqual(ins);
   3809  }
   3810  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3811 };
   3812 
   3813 // Applies ECMA's ToNumber on a primitive (either typed or untyped) and expects
   3814 // the result to be precisely representable as an Int32, otherwise bails.
   3815 //
   3816 // If the input is not primitive at runtime, a bailout occurs. If the input
   3817 // cannot be converted to an int32 without loss (i.e. 5.5 or undefined) then a
   3818 // bailout occurs.
   3819 class MToNumberInt32 : public MUnaryInstruction, public ToInt32Policy::Data {
   3820  bool needsNegativeZeroCheck_;
   3821  IntConversionInputKind conversion_;
   3822 
   3823  explicit MToNumberInt32(MDefinition* def, IntConversionInputKind conversion =
   3824                                                IntConversionInputKind::Any)
   3825      : MUnaryInstruction(classOpcode, def),
   3826        needsNegativeZeroCheck_(true),
   3827        conversion_(conversion) {
   3828    setResultType(MIRType::Int32);
   3829    setMovable();
   3830 
   3831    // Guard unless the conversion is known to be non-effectful & non-throwing.
   3832    if (!def->typeIsOneOf({MIRType::Undefined, MIRType::Null, MIRType::Boolean,
   3833                           MIRType::Int32, MIRType::Double, MIRType::Float32,
   3834                           MIRType::String})) {
   3835      setGuard();
   3836    }
   3837  }
   3838 
   3839 public:
   3840  INSTRUCTION_HEADER(ToNumberInt32)
   3841  TRIVIAL_NEW_WRAPPERS
   3842 
   3843  MDefinition* foldsTo(TempAllocator& alloc) override;
   3844 
   3845  // this only has backwards information flow.
   3846  void analyzeEdgeCasesBackward() override;
   3847 
   3848  bool needsNegativeZeroCheck() const { return needsNegativeZeroCheck_; }
   3849  void setNeedsNegativeZeroCheck(bool needsCheck) {
   3850    needsNegativeZeroCheck_ = needsCheck;
   3851  }
   3852 
   3853  IntConversionInputKind conversion() const { return conversion_; }
   3854 
   3855  bool congruentTo(const MDefinition* ins) const override {
   3856    if (!ins->isToNumberInt32() ||
   3857        ins->toToNumberInt32()->conversion() != conversion()) {
   3858      return false;
   3859    }
   3860    return congruentIfOperandsEqual(ins);
   3861  }
   3862 
   3863  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3864  void computeRange(TempAllocator& alloc) override;
   3865  void collectRangeInfoPreTrunc() override;
   3866 
   3867 #ifdef DEBUG
   3868  bool isConsistentFloat32Use(MUse* use) const override { return true; }
   3869 #endif
   3870 
   3871  ALLOW_CLONE(MToNumberInt32)
   3872 };
   3873 
   3874 // Converts a value or typed input to a truncated int32, for use with bitwise
   3875 // operations. This is an infallible ValueToECMAInt32.
   3876 class MTruncateToInt32 : public MUnaryInstruction, public ToInt32Policy::Data {
   3877  wasm::TrapSiteDesc trapSiteDesc_;
   3878 
   3879  explicit MTruncateToInt32(
   3880      MDefinition* def, wasm::TrapSiteDesc trapSiteDesc = wasm::TrapSiteDesc())
   3881      : MUnaryInstruction(classOpcode, def), trapSiteDesc_(trapSiteDesc) {
   3882    setResultType(MIRType::Int32);
   3883    setMovable();
   3884 
   3885    // Guard unless the conversion is known to be non-effectful & non-throwing.
   3886    if (mightHaveSideEffects(def)) {
   3887      setGuard();
   3888    }
   3889  }
   3890 
   3891 public:
   3892  INSTRUCTION_HEADER(TruncateToInt32)
   3893  TRIVIAL_NEW_WRAPPERS
   3894 
   3895  static bool mightHaveSideEffects(MDefinition* def) {
   3896    return !def->typeIsOneOf({MIRType::Undefined, MIRType::Null,
   3897                              MIRType::Boolean, MIRType::Int32, MIRType::Double,
   3898                              MIRType::Float32, MIRType::String});
   3899  }
   3900 
   3901  MDefinition* foldsTo(TempAllocator& alloc) override;
   3902 
   3903  bool congruentTo(const MDefinition* ins) const override {
   3904    return congruentIfOperandsEqual(ins);
   3905  }
   3906  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3907 
   3908  void computeRange(TempAllocator& alloc) override;
   3909  TruncateKind operandTruncateKind(size_t index) const override;
   3910 #ifdef DEBUG
   3911  bool isConsistentFloat32Use(MUse* use) const override { return true; }
   3912 #endif
   3913 
   3914  [[nodiscard]] bool writeRecoverData(
   3915      CompactBufferWriter& writer) const override;
   3916  bool canRecoverOnBailout() const override {
   3917    return input()->type() < MIRType::Symbol;
   3918  }
   3919 
   3920  const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; }
   3921 
   3922  ALLOW_CLONE(MTruncateToInt32)
   3923 };
   3924 
   3925 // Converts a primitive (either typed or untyped) to a BigInt. If the input is
   3926 // not primitive at runtime, a bailout occurs.
   3927 class MToBigInt : public MUnaryInstruction, public ToBigIntPolicy::Data {
   3928 private:
   3929  explicit MToBigInt(MDefinition* def) : MUnaryInstruction(classOpcode, def) {
   3930    setResultType(MIRType::BigInt);
   3931    setMovable();
   3932 
   3933    // Guard unless the conversion is known to be non-effectful & non-throwing.
   3934    if (!def->typeIsOneOf({MIRType::Boolean, MIRType::BigInt})) {
   3935      setGuard();
   3936    }
   3937  }
   3938 
   3939 public:
   3940  INSTRUCTION_HEADER(ToBigInt)
   3941  TRIVIAL_NEW_WRAPPERS
   3942 
   3943  bool congruentTo(const MDefinition* ins) const override {
   3944    return congruentIfOperandsEqual(ins);
   3945  }
   3946  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3947 
   3948  ALLOW_CLONE(MToBigInt)
   3949 };
   3950 
   3951 // Takes a Value or typed input and returns a suitable Int64 using the
   3952 // ToBigInt algorithm, possibly calling out to the VM for string, etc inputs.
   3953 class MToInt64 : public MUnaryInstruction, public ToInt64Policy::Data {
   3954  explicit MToInt64(MDefinition* def) : MUnaryInstruction(classOpcode, def) {
   3955    setResultType(MIRType::Int64);
   3956    setMovable();
   3957 
   3958    // Guard unless the conversion is known to be non-effectful & non-throwing.
   3959    if (!def->typeIsOneOf(
   3960            {MIRType::Boolean, MIRType::BigInt, MIRType::Int64})) {
   3961      setGuard();
   3962    }
   3963  }
   3964 
   3965 public:
   3966  INSTRUCTION_HEADER(ToInt64)
   3967  TRIVIAL_NEW_WRAPPERS
   3968 
   3969  bool congruentTo(const MDefinition* ins) const override {
   3970    return congruentIfOperandsEqual(ins);
   3971  }
   3972 
   3973  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3974 
   3975  MDefinition* foldsTo(TempAllocator& alloc) override;
   3976 
   3977  ALLOW_CLONE(MToInt64)
   3978 };
   3979 
   3980 // Takes a BigInt pointer and returns its toInt64 value.
   3981 class MTruncateBigIntToInt64 : public MUnaryInstruction,
   3982                               public NoTypePolicy::Data {
   3983  explicit MTruncateBigIntToInt64(MDefinition* def)
   3984      : MUnaryInstruction(classOpcode, def) {
   3985    MOZ_ASSERT(def->type() == MIRType::BigInt);
   3986    setResultType(MIRType::Int64);
   3987    setMovable();
   3988  }
   3989 
   3990 public:
   3991  INSTRUCTION_HEADER(TruncateBigIntToInt64)
   3992  TRIVIAL_NEW_WRAPPERS
   3993 
   3994  bool congruentTo(const MDefinition* ins) const override {
   3995    return congruentIfOperandsEqual(ins);
   3996  }
   3997 
   3998  AliasSet getAliasSet() const override { return AliasSet::None(); }
   3999 
   4000  MDefinition* foldsTo(TempAllocator& alloc) override;
   4001 
   4002  ALLOW_CLONE(MTruncateBigIntToInt64)
   4003 };
   4004 
   4005 // Takes an Int64 and returns a fresh BigInt pointer.
   4006 class MInt64ToBigInt : public MUnaryInstruction, public NoTypePolicy::Data {
   4007  bool isSigned_;
   4008 
   4009  MInt64ToBigInt(MDefinition* def, bool isSigned)
   4010      : MUnaryInstruction(classOpcode, def), isSigned_(isSigned) {
   4011    MOZ_ASSERT(def->type() == MIRType::Int64);
   4012    setResultType(MIRType::BigInt);
   4013    setMovable();
   4014  }
   4015 
   4016 public:
   4017  INSTRUCTION_HEADER(Int64ToBigInt)
   4018  TRIVIAL_NEW_WRAPPERS
   4019 
   4020  bool congruentTo(const MDefinition* ins) const override {
   4021    return congruentIfOperandsEqual(ins) &&
   4022           ins->toInt64ToBigInt()->isSigned() == isSigned();
   4023  }
   4024 
   4025  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4026 
   4027  bool isSigned() const { return isSigned_; }
   4028 
   4029  [[nodiscard]] bool writeRecoverData(
   4030      CompactBufferWriter& writer) const override;
   4031  bool canRecoverOnBailout() const override { return true; }
   4032 
   4033  ALLOW_CLONE(MInt64ToBigInt)
   4034 };
   4035 
   4036 // Takes an Int64 and returns a IntPtr.
   4037 class MInt64ToIntPtr : public MUnaryInstruction, public NoTypePolicy::Data {
   4038  bool isSigned_;
   4039 
   4040  MInt64ToIntPtr(MDefinition* def, bool isSigned)
   4041      : MUnaryInstruction(classOpcode, def), isSigned_(isSigned) {
   4042    MOZ_ASSERT(def->type() == MIRType::Int64);
   4043    setResultType(MIRType::IntPtr);
   4044    setMovable();
   4045  }
   4046 
   4047 public:
   4048  INSTRUCTION_HEADER(Int64ToIntPtr)
   4049  TRIVIAL_NEW_WRAPPERS
   4050 
   4051  bool congruentTo(const MDefinition* ins) const override {
   4052    return congruentIfOperandsEqual(ins) &&
   4053           ins->toInt64ToIntPtr()->isSigned() == isSigned();
   4054  }
   4055 
   4056  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4057 
   4058  bool isSigned() const { return isSigned_; }
   4059 
   4060  ALLOW_CLONE(MInt64ToIntPtr)
   4061 };
   4062 
   4063 // Takes a IntPtr and returns an Int64.
   4064 class MIntPtrToInt64 : public MUnaryInstruction, public NoTypePolicy::Data {
   4065  explicit MIntPtrToInt64(MDefinition* def)
   4066      : MUnaryInstruction(classOpcode, def) {
   4067    MOZ_ASSERT(def->type() == MIRType::IntPtr);
   4068    setResultType(MIRType::Int64);
   4069    setMovable();
   4070  }
   4071 
   4072 public:
   4073  INSTRUCTION_HEADER(IntPtrToInt64)
   4074  TRIVIAL_NEW_WRAPPERS
   4075 
   4076  bool congruentTo(const MDefinition* ins) const override {
   4077    return congruentIfOperandsEqual(ins);
   4078  }
   4079 
   4080  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4081 
   4082  ALLOW_CLONE(MIntPtrToInt64)
   4083 };
   4084 
   4085 // Converts any type to a string
   4086 class MToString : public MUnaryInstruction, public ToStringPolicy::Data {
   4087 public:
   4088  // MToString has two modes for handling of object/symbol arguments: if the
   4089  // to-string conversion happens as part of another opcode, we have to bail out
   4090  // to Baseline. If the conversion is for a stand-alone JSOp we can support
   4091  // side-effects.
   4092  enum class SideEffectHandling { Bailout, Supported };
   4093 
   4094 private:
   4095  SideEffectHandling sideEffects_;
   4096  bool mightHaveSideEffects_ = false;
   4097 
   4098  MToString(MDefinition* def, SideEffectHandling sideEffects)
   4099      : MUnaryInstruction(classOpcode, def), sideEffects_(sideEffects) {
   4100    setResultType(MIRType::String);
   4101 
   4102    if (!def->typeIsOneOf({MIRType::Undefined, MIRType::Null, MIRType::Boolean,
   4103                           MIRType::Int32, MIRType::Double, MIRType::Float32,
   4104                           MIRType::String, MIRType::BigInt})) {
   4105      mightHaveSideEffects_ = true;
   4106    }
   4107 
   4108    // If this instruction is not effectful, mark it as movable and set the
   4109    // Guard flag if needed. If the operation is effectful it won't be
   4110    // optimized anyway so there's no need to set any flags.
   4111    if (!isEffectful()) {
   4112      setMovable();
   4113      // Objects might override toString; Symbol throws. We bailout in those
   4114      // cases and run side-effects in baseline instead.
   4115      if (mightHaveSideEffects_) {
   4116        setGuard();
   4117      }
   4118    }
   4119  }
   4120 
   4121 public:
   4122  INSTRUCTION_HEADER(ToString)
   4123  TRIVIAL_NEW_WRAPPERS
   4124 
   4125  MDefinition* foldsTo(TempAllocator& alloc) override;
   4126 
   4127  bool congruentTo(const MDefinition* ins) const override {
   4128    if (!ins->isToString()) {
   4129      return false;
   4130    }
   4131    if (sideEffects_ != ins->toToString()->sideEffects_) {
   4132      return false;
   4133    }
   4134    return congruentIfOperandsEqual(ins);
   4135  }
   4136 
   4137  AliasSet getAliasSet() const override {
   4138    if (supportSideEffects() && mightHaveSideEffects_) {
   4139      return AliasSet::Store(AliasSet::Any);
   4140    }
   4141    return AliasSet::None();
   4142  }
   4143 
   4144  bool mightHaveSideEffects() const { return mightHaveSideEffects_; }
   4145 
   4146  bool supportSideEffects() const {
   4147    return sideEffects_ == SideEffectHandling::Supported;
   4148  }
   4149 
   4150  bool needsSnapshot() const {
   4151    return sideEffects_ == SideEffectHandling::Bailout && mightHaveSideEffects_;
   4152  }
   4153 
   4154  ALLOW_CLONE(MToString)
   4155 };
   4156 
   4157 class MBitNot : public MUnaryInstruction, public BitwisePolicy::Data {
   4158  MBitNot(MDefinition* input, MIRType type)
   4159      : MUnaryInstruction(classOpcode, input) {
   4160    MOZ_ASSERT(type == MIRType::Int32 || type == MIRType::Int64);
   4161    setResultType(type);
   4162    setMovable();
   4163  }
   4164 
   4165 public:
   4166  INSTRUCTION_HEADER(BitNot)
   4167  TRIVIAL_NEW_WRAPPERS
   4168 
   4169  MDefinition* foldsTo(TempAllocator& alloc) override;
   4170 
   4171  bool congruentTo(const MDefinition* ins) const override {
   4172    return congruentIfOperandsEqual(ins);
   4173  }
   4174  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4175  void computeRange(TempAllocator& alloc) override;
   4176 
   4177  [[nodiscard]] bool writeRecoverData(
   4178      CompactBufferWriter& writer) const override;
   4179  bool canRecoverOnBailout() const override { return type() != MIRType::Int64; }
   4180 
   4181  ALLOW_CLONE(MBitNot)
   4182 };
   4183 
   4184 class MTypeOf : public MUnaryInstruction,
   4185                public BoxExceptPolicy<0, MIRType::Object>::Data {
   4186  explicit MTypeOf(MDefinition* def) : MUnaryInstruction(classOpcode, def) {
   4187    setResultType(MIRType::Int32);
   4188    setMovable();
   4189  }
   4190  TypeDataList observed_;
   4191 
   4192 public:
   4193  INSTRUCTION_HEADER(TypeOf)
   4194  TRIVIAL_NEW_WRAPPERS
   4195 
   4196  void setObservedTypes(const TypeDataList& observed) { observed_ = observed; }
   4197  bool hasObservedTypes() const { return observed_.count() > 0; }
   4198  const TypeDataList& observedTypes() const { return observed_; }
   4199 
   4200  MDefinition* foldsTo(TempAllocator& alloc) override;
   4201 
   4202  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4203 
   4204  bool congruentTo(const MDefinition* ins) const override {
   4205    return congruentIfOperandsEqual(ins);
   4206  }
   4207 
   4208  [[nodiscard]] bool writeRecoverData(
   4209      CompactBufferWriter& writer) const override;
   4210  bool canRecoverOnBailout() const override { return true; }
   4211 };
   4212 
   4213 class MTypeOfIs : public MUnaryInstruction, public NoTypePolicy::Data {
   4214  JSOp jsop_;
   4215  JSType jstype_;
   4216 
   4217  MTypeOfIs(MDefinition* def, JSOp jsop, JSType jstype)
   4218      : MUnaryInstruction(classOpcode, def), jsop_(jsop), jstype_(jstype) {
   4219    MOZ_ASSERT(def->type() == MIRType::Object || def->type() == MIRType::Value);
   4220 
   4221    setResultType(MIRType::Boolean);
   4222    setMovable();
   4223  }
   4224 
   4225 public:
   4226  INSTRUCTION_HEADER(TypeOfIs)
   4227  TRIVIAL_NEW_WRAPPERS
   4228 
   4229  JSOp jsop() const { return jsop_; }
   4230  JSType jstype() const { return jstype_; }
   4231 
   4232  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4233 
   4234  bool congruentTo(const MDefinition* ins) const override {
   4235    if (!congruentIfOperandsEqual(ins)) {
   4236      return false;
   4237    }
   4238    return jsop() == ins->toTypeOfIs()->jsop() &&
   4239           jstype() == ins->toTypeOfIs()->jstype();
   4240  }
   4241 
   4242 #ifdef JS_JITSPEW
   4243  void printOpcode(GenericPrinter& out) const override;
   4244 #endif
   4245 };
   4246 
   4247 class MBinaryBitwiseInstruction : public MBinaryInstruction,
   4248                                  public BitwisePolicy::Data {
   4249 protected:
   4250  MBinaryBitwiseInstruction(Opcode op, MDefinition* left, MDefinition* right,
   4251                            MIRType type)
   4252      : MBinaryInstruction(op, left, right),
   4253        maskMatchesLeftRange(false),
   4254        maskMatchesRightRange(false) {
   4255    MOZ_ASSERT(IsIntType(type) || (isUrsh() && type == MIRType::Double));
   4256    setResultType(type);
   4257    setMovable();
   4258  }
   4259 
   4260  bool maskMatchesLeftRange;
   4261  bool maskMatchesRightRange;
   4262 
   4263 public:
   4264  MDefinition* foldsTo(TempAllocator& alloc) override;
   4265  MDefinition* foldUnnecessaryBitop();
   4266  virtual MDefinition* foldIfZero(size_t operand) = 0;
   4267  virtual MDefinition* foldIfNegOne(size_t operand) = 0;
   4268  virtual MDefinition* foldIfEqual() = 0;
   4269  virtual MDefinition* foldIfAllBitsSet(size_t operand) = 0;
   4270  void collectRangeInfoPreTrunc() override;
   4271 
   4272  bool congruentTo(const MDefinition* ins) const override {
   4273    return binaryCongruentTo(ins);
   4274  }
   4275  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4276 
   4277  TruncateKind operandTruncateKind(size_t index) const override;
   4278 };
   4279 
   4280 class MBitAnd : public MBinaryBitwiseInstruction {
   4281  MBitAnd(MDefinition* left, MDefinition* right, MIRType type)
   4282      : MBinaryBitwiseInstruction(classOpcode, left, right, type) {
   4283    setCommutative();
   4284  }
   4285 
   4286 public:
   4287  INSTRUCTION_HEADER(BitAnd)
   4288  TRIVIAL_NEW_WRAPPERS
   4289 
   4290  MDefinition* foldIfZero(size_t operand) override {
   4291    return getOperand(operand);  // 0 & x => 0;
   4292  }
   4293  MDefinition* foldIfNegOne(size_t operand) override {
   4294    return getOperand(1 - operand);  // x & -1 => x
   4295  }
   4296  MDefinition* foldIfEqual() override {
   4297    return getOperand(0);  // x & x => x;
   4298  }
   4299  MDefinition* foldIfAllBitsSet(size_t operand) override {
   4300    // e.g. for uint16: x & 0xffff => x;
   4301    return getOperand(1 - operand);
   4302  }
   4303  void computeRange(TempAllocator& alloc) override;
   4304 
   4305  [[nodiscard]] bool writeRecoverData(
   4306      CompactBufferWriter& writer) const override;
   4307  bool canRecoverOnBailout() const override { return type() != MIRType::Int64; }
   4308 
   4309  ALLOW_CLONE(MBitAnd)
   4310 };
   4311 
   4312 class MBitOr : public MBinaryBitwiseInstruction {
   4313  MBitOr(MDefinition* left, MDefinition* right, MIRType type)
   4314      : MBinaryBitwiseInstruction(classOpcode, left, right, type) {
   4315    setCommutative();
   4316  }
   4317 
   4318 public:
   4319  INSTRUCTION_HEADER(BitOr)
   4320  TRIVIAL_NEW_WRAPPERS
   4321 
   4322  MDefinition* foldIfZero(size_t operand) override {
   4323    return getOperand(1 -
   4324                      operand);  // 0 | x => x, so if ith is 0, return (1-i)th
   4325  }
   4326  MDefinition* foldIfNegOne(size_t operand) override {
   4327    return getOperand(operand);  // x | -1 => -1
   4328  }
   4329  MDefinition* foldIfEqual() override {
   4330    return getOperand(0);  // x | x => x
   4331  }
   4332  MDefinition* foldIfAllBitsSet(size_t operand) override { return this; }
   4333  void computeRange(TempAllocator& alloc) override;
   4334  [[nodiscard]] bool writeRecoverData(
   4335      CompactBufferWriter& writer) const override;
   4336  bool canRecoverOnBailout() const override { return type() != MIRType::Int64; }
   4337 
   4338  ALLOW_CLONE(MBitOr)
   4339 };
   4340 
   4341 class MBitXor : public MBinaryBitwiseInstruction {
   4342  MBitXor(MDefinition* left, MDefinition* right, MIRType type)
   4343      : MBinaryBitwiseInstruction(classOpcode, left, right, type) {
   4344    setCommutative();
   4345  }
   4346 
   4347 public:
   4348  INSTRUCTION_HEADER(BitXor)
   4349  TRIVIAL_NEW_WRAPPERS
   4350 
   4351  MDefinition* foldIfZero(size_t operand) override {
   4352    return getOperand(1 - operand);  // 0 ^ x => x
   4353  }
   4354  MDefinition* foldIfNegOne(size_t operand) override { return this; }
   4355  MDefinition* foldIfEqual() override { return this; }
   4356  MDefinition* foldIfAllBitsSet(size_t operand) override { return this; }
   4357  void computeRange(TempAllocator& alloc) override;
   4358 
   4359  [[nodiscard]] bool writeRecoverData(
   4360      CompactBufferWriter& writer) const override;
   4361  bool canRecoverOnBailout() const override { return type() != MIRType::Int64; }
   4362 
   4363  ALLOW_CLONE(MBitXor)
   4364 };
   4365 
   4366 class MShiftInstruction : public MBinaryBitwiseInstruction {
   4367 protected:
   4368  MShiftInstruction(Opcode op, MDefinition* left, MDefinition* right,
   4369                    MIRType type)
   4370      : MBinaryBitwiseInstruction(op, left, right, type) {}
   4371 
   4372 public:
   4373  MDefinition* foldIfNegOne(size_t operand) override { return this; }
   4374  MDefinition* foldIfEqual() override { return this; }
   4375  MDefinition* foldIfAllBitsSet(size_t operand) override { return this; }
   4376 };
   4377 
   4378 class MLsh : public MShiftInstruction {
   4379  MLsh(MDefinition* left, MDefinition* right, MIRType type)
   4380      : MShiftInstruction(classOpcode, left, right, type) {}
   4381 
   4382 public:
   4383  INSTRUCTION_HEADER(Lsh)
   4384  TRIVIAL_NEW_WRAPPERS
   4385 
   4386  MDefinition* foldIfZero(size_t operand) override {
   4387    // 0 << x => 0
   4388    // x << 0 => x
   4389    return getOperand(0);
   4390  }
   4391 
   4392  void computeRange(TempAllocator& alloc) override;
   4393  [[nodiscard]] bool writeRecoverData(
   4394      CompactBufferWriter& writer) const override;
   4395  bool canRecoverOnBailout() const override {
   4396    return IsTypeRepresentableAsDouble(type());
   4397  }
   4398 
   4399  ALLOW_CLONE(MLsh)
   4400 };
   4401 
   4402 class MRsh : public MShiftInstruction {
   4403  MRsh(MDefinition* left, MDefinition* right, MIRType type)
   4404      : MShiftInstruction(classOpcode, left, right, type) {}
   4405 
   4406 public:
   4407  INSTRUCTION_HEADER(Rsh)
   4408  TRIVIAL_NEW_WRAPPERS
   4409 
   4410  MDefinition* foldIfZero(size_t operand) override {
   4411    // 0 >> x => 0
   4412    // x >> 0 => x
   4413    return getOperand(0);
   4414  }
   4415  void computeRange(TempAllocator& alloc) override;
   4416 
   4417  [[nodiscard]] bool writeRecoverData(
   4418      CompactBufferWriter& writer) const override;
   4419  bool canRecoverOnBailout() const override {
   4420    return IsTypeRepresentableAsDouble(type());
   4421  }
   4422 
   4423  MDefinition* foldsTo(TempAllocator& alloc) override;
   4424 
   4425  ALLOW_CLONE(MRsh)
   4426 };
   4427 
   4428 class MUrsh : public MShiftInstruction {
   4429  bool bailoutsDisabled_;
   4430 
   4431  MUrsh(MDefinition* left, MDefinition* right, MIRType type)
   4432      : MShiftInstruction(classOpcode, left, right, type),
   4433        bailoutsDisabled_(false) {}
   4434 
   4435 public:
   4436  INSTRUCTION_HEADER(Ursh)
   4437  TRIVIAL_NEW_WRAPPERS
   4438 
   4439  static MUrsh* NewWasm(TempAllocator& alloc, MDefinition* left,
   4440                        MDefinition* right, MIRType type);
   4441 
   4442  MDefinition* foldIfZero(size_t operand) override {
   4443    // 0 >>> x => 0
   4444    if (operand == 0) {
   4445      return getOperand(0);
   4446    }
   4447 
   4448    return this;
   4449  }
   4450 
   4451  bool bailoutsDisabled() const { return bailoutsDisabled_; }
   4452 
   4453  bool fallible() const;
   4454 
   4455  void computeRange(TempAllocator& alloc) override;
   4456  void collectRangeInfoPreTrunc() override;
   4457 
   4458  [[nodiscard]] bool writeRecoverData(
   4459      CompactBufferWriter& writer) const override;
   4460  bool canRecoverOnBailout() const override {
   4461    return IsTypeRepresentableAsDouble(type());
   4462  }
   4463 
   4464  ALLOW_CLONE(MUrsh)
   4465 };
   4466 
   4467 class MSignExtendInt32 : public MUnaryInstruction, public NoTypePolicy::Data {
   4468 public:
   4469  enum Mode { Byte, Half };
   4470 
   4471 private:
   4472  Mode mode_;
   4473 
   4474  MSignExtendInt32(MDefinition* op, Mode mode)
   4475      : MUnaryInstruction(classOpcode, op), mode_(mode) {
   4476    MOZ_ASSERT(op->type() == MIRType::Int32);
   4477    setResultType(MIRType::Int32);
   4478    setMovable();
   4479  }
   4480 
   4481 public:
   4482  INSTRUCTION_HEADER(SignExtendInt32)
   4483  TRIVIAL_NEW_WRAPPERS
   4484 
   4485  Mode mode() const { return mode_; }
   4486 
   4487  MDefinition* foldsTo(TempAllocator& alloc) override;
   4488  bool congruentTo(const MDefinition* ins) const override {
   4489    if (!congruentIfOperandsEqual(ins)) {
   4490      return false;
   4491    }
   4492    return ins->toSignExtendInt32()->mode_ == mode_;
   4493  }
   4494  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4495 
   4496  [[nodiscard]] bool writeRecoverData(
   4497      CompactBufferWriter& writer) const override;
   4498  bool canRecoverOnBailout() const override { return true; }
   4499 
   4500  ALLOW_CLONE(MSignExtendInt32)
   4501 };
   4502 
   4503 class MSignExtendInt64 : public MUnaryInstruction, public NoTypePolicy::Data {
   4504 public:
   4505  enum Mode { Byte, Half, Word };
   4506 
   4507 private:
   4508  Mode mode_;
   4509 
   4510  MSignExtendInt64(MDefinition* op, Mode mode)
   4511      : MUnaryInstruction(classOpcode, op), mode_(mode) {
   4512    MOZ_ASSERT(op->type() == MIRType::Int64);
   4513    setResultType(MIRType::Int64);
   4514    setMovable();
   4515  }
   4516 
   4517 public:
   4518  INSTRUCTION_HEADER(SignExtendInt64)
   4519  TRIVIAL_NEW_WRAPPERS
   4520 
   4521  Mode mode() const { return mode_; }
   4522 
   4523  MDefinition* foldsTo(TempAllocator& alloc) override;
   4524  bool congruentTo(const MDefinition* ins) const override {
   4525    if (!congruentIfOperandsEqual(ins)) {
   4526      return false;
   4527    }
   4528    return ins->toSignExtendInt64()->mode_ == mode_;
   4529  }
   4530  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4531 
   4532  ALLOW_CLONE(MSignExtendInt64)
   4533 };
   4534 
   4535 class MSignExtendIntPtr : public MUnaryInstruction, public NoTypePolicy::Data {
   4536 public:
   4537  enum Mode { Byte, Half, Word };
   4538 
   4539 private:
   4540  Mode mode_;
   4541 
   4542  MSignExtendIntPtr(MDefinition* op, Mode mode)
   4543      : MUnaryInstruction(classOpcode, op), mode_(mode) {
   4544    MOZ_ASSERT(op->type() == MIRType::IntPtr);
   4545    setResultType(MIRType::IntPtr);
   4546    setMovable();
   4547  }
   4548 
   4549 public:
   4550  INSTRUCTION_HEADER(SignExtendIntPtr)
   4551  TRIVIAL_NEW_WRAPPERS
   4552 
   4553  Mode mode() const { return mode_; }
   4554 
   4555  MDefinition* foldsTo(TempAllocator& alloc) override;
   4556  bool congruentTo(const MDefinition* ins) const override {
   4557    if (!congruentIfOperandsEqual(ins)) {
   4558      return false;
   4559    }
   4560    return ins->toSignExtendIntPtr()->mode_ == mode_;
   4561  }
   4562  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4563 
   4564  ALLOW_CLONE(MSignExtendIntPtr)
   4565 };
   4566 
   4567 class MBinaryArithInstruction : public MBinaryInstruction,
   4568                                public ArithPolicy::Data {
   4569  // Implicit truncate flag is set by the truncate backward range analysis
   4570  // optimization phase, and by wasm pre-processing. It is used in
   4571  // NeedNegativeZeroCheck to check if the result of a multiplication needs to
   4572  // produce -0 double value, and for avoiding overflow checks.
   4573 
   4574  // This optimization happens when the multiplication cannot be truncated
   4575  // even if all uses are truncating its result, such as when the range
   4576  // analysis detect a precision loss in the multiplication.
   4577  TruncateKind implicitTruncate_;
   4578 
   4579  // Whether we must preserve NaN semantics, and in particular not fold
   4580  // (x op id) or (id op x) to x, or replace a division by a multiply of the
   4581  // exact reciprocal.
   4582  bool mustPreserveNaN_;
   4583 
   4584 protected:
   4585  MBinaryArithInstruction(Opcode op, MDefinition* left, MDefinition* right,
   4586                          MIRType type)
   4587      : MBinaryInstruction(op, left, right),
   4588        implicitTruncate_(TruncateKind::NoTruncate),
   4589        mustPreserveNaN_(false) {
   4590    MOZ_ASSERT(IsNumberType(type));
   4591    setResultType(type);
   4592    setMovable();
   4593  }
   4594 
   4595 public:
   4596  void setMustPreserveNaN(bool b) { mustPreserveNaN_ = b; }
   4597  bool mustPreserveNaN() const { return mustPreserveNaN_; }
   4598 
   4599  MDefinition* foldsTo(TempAllocator& alloc) override;
   4600 #ifdef JS_JITSPEW
   4601  void printOpcode(GenericPrinter& out) const override;
   4602 #endif
   4603 
   4604  virtual double getIdentity() const = 0;
   4605 
   4606  void setSpecialization(MIRType type) {
   4607    MOZ_ASSERT(IsNumberType(type));
   4608    setResultType(type);
   4609  }
   4610 
   4611  virtual void trySpecializeFloat32(TempAllocator& alloc) override;
   4612 
   4613  bool congruentTo(const MDefinition* ins) const override {
   4614    if (!binaryCongruentTo(ins)) {
   4615      return false;
   4616    }
   4617    const auto* other = static_cast<const MBinaryArithInstruction*>(ins);
   4618    return other->mustPreserveNaN_ == mustPreserveNaN_;
   4619  }
   4620  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4621 
   4622  bool isTruncated() const {
   4623    return implicitTruncate_ == TruncateKind::Truncate;
   4624  }
   4625  TruncateKind truncateKind() const { return implicitTruncate_; }
   4626  void setTruncateKind(TruncateKind kind) {
   4627    implicitTruncate_ = std::max(implicitTruncate_, kind);
   4628  }
   4629 };
   4630 
   4631 class MMinMax : public MBinaryInstruction, public ArithPolicy::Data {
   4632  bool isMax_;
   4633 
   4634  MMinMax(MDefinition* left, MDefinition* right, MIRType type, bool isMax)
   4635      : MBinaryInstruction(classOpcode, left, right), isMax_(isMax) {
   4636    MOZ_ASSERT(IsNumberType(type));
   4637    setResultType(type);
   4638    setMovable();
   4639    setCommutative();
   4640  }
   4641 
   4642 public:
   4643  INSTRUCTION_HEADER(MinMax)
   4644  TRIVIAL_NEW_WRAPPERS
   4645 
   4646  template <typename... Args>
   4647  static MMinMax* NewMin(Args&&... args) {
   4648    return New(std::forward<Args>(args)..., /* isMax= */ false);
   4649  }
   4650 
   4651  template <typename... Args>
   4652  static MMinMax* NewMax(Args&&... args) {
   4653    return New(std::forward<Args>(args)..., /* isMax= */ true);
   4654  }
   4655 
   4656  static MMinMax* NewWasm(TempAllocator& alloc, MDefinition* left,
   4657                          MDefinition* right, MIRType type, bool isMax) {
   4658    return New(alloc, left, right, type, isMax);
   4659  }
   4660 
   4661  bool isMax() const { return isMax_; }
   4662 
   4663  bool congruentTo(const MDefinition* ins) const override {
   4664    if (!congruentIfOperandsEqual(ins)) {
   4665      return false;
   4666    }
   4667    const MMinMax* other = ins->toMinMax();
   4668    return other->isMax() == isMax();
   4669  }
   4670 
   4671  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4672  MDefinition* foldsTo(TempAllocator& alloc) override;
   4673  void computeRange(TempAllocator& alloc) override;
   4674  [[nodiscard]] bool writeRecoverData(
   4675      CompactBufferWriter& writer) const override;
   4676  bool canRecoverOnBailout() const override {
   4677    return IsTypeRepresentableAsDouble(type());
   4678  }
   4679 
   4680  bool isFloat32Commutative() const override { return true; }
   4681  void trySpecializeFloat32(TempAllocator& alloc) override;
   4682 
   4683 #ifdef JS_JITSPEW
   4684  void printOpcode(GenericPrinter& out) const override;
   4685 #endif
   4686 
   4687  ALLOW_CLONE(MMinMax)
   4688 };
   4689 
   4690 class MMinMaxArray : public MUnaryInstruction, public SingleObjectPolicy::Data {
   4691  bool isMax_;
   4692 
   4693  MMinMaxArray(MDefinition* array, MIRType type, bool isMax)
   4694      : MUnaryInstruction(classOpcode, array), isMax_(isMax) {
   4695    MOZ_ASSERT(type == MIRType::Int32 || type == MIRType::Double);
   4696    setResultType(type);
   4697 
   4698    // We can't DCE this, even if the result is unused, in case one of the
   4699    // elements of the array is an object with a `valueOf` function that
   4700    // must be called.
   4701    setGuard();
   4702  }
   4703 
   4704 public:
   4705  INSTRUCTION_HEADER(MinMaxArray)
   4706  TRIVIAL_NEW_WRAPPERS
   4707  NAMED_OPERANDS((0, array))
   4708 
   4709  bool isMax() const { return isMax_; }
   4710 
   4711  bool congruentTo(const MDefinition* ins) const override {
   4712    if (!ins->isMinMaxArray() || ins->toMinMaxArray()->isMax() != isMax()) {
   4713      return false;
   4714    }
   4715    return congruentIfOperandsEqual(ins);
   4716  }
   4717 
   4718  AliasSet getAliasSet() const override {
   4719    return AliasSet::Load(AliasSet::ObjectFields | AliasSet::Element);
   4720  }
   4721 
   4722 #ifdef JS_JITSPEW
   4723  void printOpcode(GenericPrinter& out) const override;
   4724 #endif
   4725 };
   4726 
   4727 class MAbs : public MUnaryInstruction, public ArithPolicy::Data {
   4728  bool implicitTruncate_;
   4729 
   4730  MAbs(MDefinition* num, MIRType type)
   4731      : MUnaryInstruction(classOpcode, num), implicitTruncate_(false) {
   4732    MOZ_ASSERT(IsNumberType(type));
   4733    setResultType(type);
   4734    setMovable();
   4735  }
   4736 
   4737 public:
   4738  INSTRUCTION_HEADER(Abs)
   4739  TRIVIAL_NEW_WRAPPERS
   4740 
   4741  static MAbs* NewWasm(TempAllocator& alloc, MDefinition* num, MIRType type) {
   4742    auto* ins = new (alloc) MAbs(num, type);
   4743    if (type == MIRType::Int32) {
   4744      ins->implicitTruncate_ = true;
   4745    }
   4746    return ins;
   4747  }
   4748 
   4749  bool congruentTo(const MDefinition* ins) const override {
   4750    return congruentIfOperandsEqual(ins);
   4751  }
   4752  bool fallible() const;
   4753 
   4754  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4755  void computeRange(TempAllocator& alloc) override;
   4756  bool isFloat32Commutative() const override { return true; }
   4757  void trySpecializeFloat32(TempAllocator& alloc) override;
   4758 
   4759  [[nodiscard]] bool writeRecoverData(
   4760      CompactBufferWriter& writer) const override;
   4761  bool canRecoverOnBailout() const override { return true; }
   4762 
   4763  ALLOW_CLONE(MAbs)
   4764 };
   4765 
   4766 class MClz : public MUnaryInstruction, public BitwisePolicy::Data {
   4767  bool operandIsNeverZero_;
   4768 
   4769  explicit MClz(MDefinition* num, MIRType type)
   4770      : MUnaryInstruction(classOpcode, num), operandIsNeverZero_(false) {
   4771    MOZ_ASSERT(IsIntType(type));
   4772    MOZ_ASSERT(IsNumberType(num->type()));
   4773    setResultType(type);
   4774    setMovable();
   4775  }
   4776 
   4777 public:
   4778  INSTRUCTION_HEADER(Clz)
   4779  TRIVIAL_NEW_WRAPPERS
   4780  NAMED_OPERANDS((0, num))
   4781 
   4782  bool congruentTo(const MDefinition* ins) const override {
   4783    return congruentIfOperandsEqual(ins);
   4784  }
   4785 
   4786  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4787 
   4788  bool operandIsNeverZero() const { return operandIsNeverZero_; }
   4789 
   4790  MDefinition* foldsTo(TempAllocator& alloc) override;
   4791  void computeRange(TempAllocator& alloc) override;
   4792  void collectRangeInfoPreTrunc() override;
   4793 
   4794  ALLOW_CLONE(MClz)
   4795 };
   4796 
   4797 class MCtz : public MUnaryInstruction, public BitwisePolicy::Data {
   4798  bool operandIsNeverZero_;
   4799 
   4800  explicit MCtz(MDefinition* num, MIRType type)
   4801      : MUnaryInstruction(classOpcode, num), operandIsNeverZero_(false) {
   4802    MOZ_ASSERT(IsIntType(type));
   4803    MOZ_ASSERT(IsNumberType(num->type()));
   4804    setResultType(type);
   4805    setMovable();
   4806  }
   4807 
   4808 public:
   4809  INSTRUCTION_HEADER(Ctz)
   4810  TRIVIAL_NEW_WRAPPERS
   4811  NAMED_OPERANDS((0, num))
   4812 
   4813  bool congruentTo(const MDefinition* ins) const override {
   4814    return congruentIfOperandsEqual(ins);
   4815  }
   4816 
   4817  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4818 
   4819  bool operandIsNeverZero() const { return operandIsNeverZero_; }
   4820 
   4821  MDefinition* foldsTo(TempAllocator& alloc) override;
   4822  void computeRange(TempAllocator& alloc) override;
   4823  void collectRangeInfoPreTrunc() override;
   4824 
   4825  ALLOW_CLONE(MCtz)
   4826 };
   4827 
   4828 class MPopcnt : public MUnaryInstruction, public BitwisePolicy::Data {
   4829  explicit MPopcnt(MDefinition* num, MIRType type)
   4830      : MUnaryInstruction(classOpcode, num) {
   4831    MOZ_ASSERT(IsNumberType(num->type()));
   4832    MOZ_ASSERT(IsIntType(type));
   4833    setResultType(type);
   4834    setMovable();
   4835  }
   4836 
   4837 public:
   4838  INSTRUCTION_HEADER(Popcnt)
   4839  TRIVIAL_NEW_WRAPPERS
   4840  NAMED_OPERANDS((0, num))
   4841 
   4842  bool congruentTo(const MDefinition* ins) const override {
   4843    return congruentIfOperandsEqual(ins);
   4844  }
   4845 
   4846  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4847 
   4848  MDefinition* foldsTo(TempAllocator& alloc) override;
   4849  void computeRange(TempAllocator& alloc) override;
   4850 
   4851  ALLOW_CLONE(MPopcnt)
   4852 };
   4853 
   4854 // Inline implementation of Math.sqrt().
   4855 class MSqrt : public MUnaryInstruction, public FloatingPointPolicy<0>::Data {
   4856  MSqrt(MDefinition* num, MIRType type) : MUnaryInstruction(classOpcode, num) {
   4857    setResultType(type);
   4858    specialization_ = type;
   4859    setMovable();
   4860  }
   4861 
   4862 public:
   4863  INSTRUCTION_HEADER(Sqrt)
   4864  TRIVIAL_NEW_WRAPPERS
   4865 
   4866  bool congruentTo(const MDefinition* ins) const override {
   4867    return congruentIfOperandsEqual(ins);
   4868  }
   4869 
   4870  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4871  void computeRange(TempAllocator& alloc) override;
   4872 
   4873  bool isFloat32Commutative() const override { return true; }
   4874  void trySpecializeFloat32(TempAllocator& alloc) override;
   4875 
   4876  [[nodiscard]] bool writeRecoverData(
   4877      CompactBufferWriter& writer) const override;
   4878  bool canRecoverOnBailout() const override { return true; }
   4879 
   4880  ALLOW_CLONE(MSqrt)
   4881 };
   4882 
   4883 class MCopySign : public MBinaryInstruction, public NoTypePolicy::Data {
   4884  MCopySign(MDefinition* lhs, MDefinition* rhs, MIRType type)
   4885      : MBinaryInstruction(classOpcode, lhs, rhs) {
   4886    setResultType(type);
   4887    setMovable();
   4888  }
   4889 
   4890 public:
   4891  INSTRUCTION_HEADER(CopySign)
   4892  TRIVIAL_NEW_WRAPPERS
   4893 
   4894  bool congruentTo(const MDefinition* ins) const override {
   4895    return congruentIfOperandsEqual(ins);
   4896  }
   4897  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4898 
   4899  ALLOW_CLONE(MCopySign)
   4900 };
   4901 
   4902 // Inline implementation of Math.hypot().
   4903 class MHypot : public MVariadicInstruction, public AllDoublePolicy::Data {
   4904  MHypot() : MVariadicInstruction(classOpcode) {
   4905    setResultType(MIRType::Double);
   4906    setMovable();
   4907  }
   4908 
   4909 public:
   4910  INSTRUCTION_HEADER(Hypot)
   4911  static MHypot* New(TempAllocator& alloc, const MDefinitionVector& vector);
   4912 
   4913  bool congruentTo(const MDefinition* ins) const override {
   4914    return congruentIfOperandsEqual(ins);
   4915  }
   4916 
   4917  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4918 
   4919  bool possiblyCalls() const override { return true; }
   4920 
   4921  [[nodiscard]] bool writeRecoverData(
   4922      CompactBufferWriter& writer) const override;
   4923  bool canRecoverOnBailout() const override { return true; }
   4924 
   4925  bool canClone() const override { return true; }
   4926 
   4927  MInstruction* clone(TempAllocator& alloc,
   4928                      const MDefinitionVector& inputs) const override {
   4929    return MHypot::New(alloc, inputs);
   4930  }
   4931 };
   4932 
   4933 // Inline implementation of Math.pow().
   4934 //
   4935 // Supports the following three specializations:
   4936 //
   4937 // 1. MPow(FloatingPoint, FloatingPoint) -> Double
   4938 //   - The most general mode, calls js::ecmaPow.
   4939 //   - Never performs a bailout.
   4940 // 2. MPow(FloatingPoint, Int32) -> Double
   4941 //   - Optimization to call js::powi instead of js::ecmaPow.
   4942 //   - Never performs a bailout.
   4943 // 3. MPow(Int32, Int32) -> Int32
   4944 //   - Performs the complete exponentiation operation in assembly code.
   4945 //   - Bails out if the result doesn't fit in Int32.
   4946 class MPow : public MBinaryInstruction, public PowPolicy::Data {
   4947  // If true, the result is guaranteed to never be negative zero, as long as the
   4948  // power is a positive number.
   4949  bool canBeNegativeZero_;
   4950 
   4951  MPow(MDefinition* input, MDefinition* power, MIRType specialization)
   4952      : MBinaryInstruction(classOpcode, input, power) {
   4953    MOZ_ASSERT(specialization == MIRType::Int32 ||
   4954               specialization == MIRType::Double);
   4955    setResultType(specialization);
   4956    setMovable();
   4957 
   4958    // The result can't be negative zero if the base is an Int32 value.
   4959    canBeNegativeZero_ = input->type() != MIRType::Int32;
   4960  }
   4961 
   4962  // Helpers for `foldsTo`
   4963  MDefinition* foldsConstant(TempAllocator& alloc);
   4964  MDefinition* foldsConstantPower(TempAllocator& alloc);
   4965 
   4966  bool canBeNegativeZero() const { return canBeNegativeZero_; }
   4967 
   4968 public:
   4969  INSTRUCTION_HEADER(Pow)
   4970  TRIVIAL_NEW_WRAPPERS
   4971 
   4972  MDefinition* input() const { return lhs(); }
   4973  MDefinition* power() const { return rhs(); }
   4974 
   4975  bool congruentTo(const MDefinition* ins) const override {
   4976    return congruentIfOperandsEqual(ins);
   4977  }
   4978  AliasSet getAliasSet() const override { return AliasSet::None(); }
   4979  bool possiblyCalls() const override { return type() != MIRType::Int32; }
   4980  [[nodiscard]] bool writeRecoverData(
   4981      CompactBufferWriter& writer) const override;
   4982  bool canRecoverOnBailout() const override { return true; }
   4983 
   4984  MDefinition* foldsTo(TempAllocator& alloc) override;
   4985 
   4986  ALLOW_CLONE(MPow)
   4987 };
   4988 
   4989 // Inline implementation of Math.pow(x, 0.5), which subtly differs from
   4990 // Math.sqrt(x).
   4991 class MPowHalf : public MUnaryInstruction, public DoublePolicy<0>::Data {
   4992  bool operandIsNeverNegativeInfinity_;
   4993  bool operandIsNeverNegativeZero_;
   4994  bool operandIsNeverNaN_;
   4995 
   4996  explicit MPowHalf(MDefinition* input)
   4997      : MUnaryInstruction(classOpcode, input),
   4998        operandIsNeverNegativeInfinity_(false),
   4999        operandIsNeverNegativeZero_(false),
   5000        operandIsNeverNaN_(false) {
   5001    setResultType(MIRType::Double);
   5002    setMovable();
   5003  }
   5004 
   5005 public:
   5006  INSTRUCTION_HEADER(PowHalf)
   5007  TRIVIAL_NEW_WRAPPERS
   5008 
   5009  bool congruentTo(const MDefinition* ins) const override {
   5010    return congruentIfOperandsEqual(ins);
   5011  }
   5012  bool operandIsNeverNegativeInfinity() const {
   5013    return operandIsNeverNegativeInfinity_;
   5014  }
   5015  bool operandIsNeverNegativeZero() const {
   5016    return operandIsNeverNegativeZero_;
   5017  }
   5018  bool operandIsNeverNaN() const { return operandIsNeverNaN_; }
   5019  AliasSet getAliasSet() const override { return AliasSet::None(); }
   5020  void collectRangeInfoPreTrunc() override;
   5021  [[nodiscard]] bool writeRecoverData(
   5022      CompactBufferWriter& writer) const override;
   5023  bool canRecoverOnBailout() const override { return true; }
   5024 
   5025  ALLOW_CLONE(MPowHalf)
   5026 };
   5027 
   5028 class MSign : public MUnaryInstruction, public SignPolicy::Data {
   5029 private:
   5030  MSign(MDefinition* input, MIRType resultType)
   5031      : MUnaryInstruction(classOpcode, input) {
   5032    MOZ_ASSERT(IsNumberType(input->type()));
   5033    MOZ_ASSERT(resultType == MIRType::Int32 || resultType == MIRType::Double);
   5034    specialization_ = input->type();
   5035    setResultType(resultType);
   5036    setMovable();
   5037  }
   5038 
   5039 public:
   5040  INSTRUCTION_HEADER(Sign)
   5041  TRIVIAL_NEW_WRAPPERS
   5042 
   5043  bool congruentTo(const MDefinition* ins) const override {
   5044    return congruentIfOperandsEqual(ins);
   5045  }
   5046 
   5047  AliasSet getAliasSet() const override { return AliasSet::None(); }
   5048 
   5049  MDefinition* foldsTo(TempAllocator& alloc) override;
   5050 
   5051  void computeRange(TempAllocator& alloc) override;
   5052  [[nodiscard]] bool writeRecoverData(
   5053      CompactBufferWriter& writer) const override;
   5054  bool canRecoverOnBailout() const override { return true; }
   5055 
   5056  ALLOW_CLONE(MSign)
   5057 };
   5058 
   5059 class MMathFunction : public MUnaryInstruction,
   5060                      public FloatingPointPolicy<0>::Data {
   5061  UnaryMathFunction function_;
   5062 
   5063  // A nullptr cache means this function will neither access nor update the
   5064  // cache.
   5065  MMathFunction(MDefinition* input, UnaryMathFunction function)
   5066      : MUnaryInstruction(classOpcode, input), function_(function) {
   5067    setResultType(MIRType::Double);
   5068    specialization_ = MIRType::Double;
   5069    setMovable();
   5070  }
   5071 
   5072 public:
   5073  INSTRUCTION_HEADER(MathFunction)
   5074  TRIVIAL_NEW_WRAPPERS
   5075 
   5076  UnaryMathFunction function() const { return function_; }
   5077 
   5078  bool congruentTo(const MDefinition* ins) const override {
   5079    if (!ins->isMathFunction()) {
   5080      return false;
   5081    }
   5082    if (ins->toMathFunction()->function() != function()) {
   5083      return false;
   5084    }
   5085    return congruentIfOperandsEqual(ins);
   5086  }
   5087 
   5088  AliasSet getAliasSet() const override { return AliasSet::None(); }
   5089 
   5090  bool possiblyCalls() const override { return true; }
   5091 
   5092  MDefinition* foldsTo(TempAllocator& alloc) override;
   5093 
   5094 #ifdef JS_JITSPEW
   5095  void printOpcode(GenericPrinter& out) const override;
   5096 #endif
   5097 
   5098  static const char* FunctionName(UnaryMathFunction function);
   5099 
   5100  bool isFloat32Commutative() const override;
   5101  void trySpecializeFloat32(TempAllocator& alloc) override;
   5102 
   5103  void computeRange(TempAllocator& alloc) override;
   5104 
   5105  [[nodiscard]] bool writeRecoverData(
   5106      CompactBufferWriter& writer) const override;
   5107  bool canRecoverOnBailout() const override { return true; }
   5108 
   5109  ALLOW_CLONE(MMathFunction)
   5110 };
   5111 
   5112 class MAdd : public MBinaryArithInstruction {
   5113  MAdd(MDefinition* left, MDefinition* right, MIRType type)
   5114      : MBinaryArithInstruction(classOpcode, left, right, type) {
   5115    setCommutative();
   5116  }
   5117 
   5118  MAdd(MDefinition* left, MDefinition* right, TruncateKind truncateKind)
   5119      : MAdd(left, right, MIRType::Int32) {
   5120    setTruncateKind(truncateKind);
   5121  }
   5122 
   5123 public:
   5124  INSTRUCTION_HEADER(Add)
   5125  TRIVIAL_NEW_WRAPPERS
   5126 
   5127  static MAdd* NewWasm(TempAllocator& alloc, MDefinition* left,
   5128                       MDefinition* right, MIRType type) {
   5129    auto* ret = new (alloc) MAdd(left, right, type);
   5130    if (type == MIRType::Int32) {
   5131      ret->setTruncateKind(TruncateKind::Truncate);
   5132    }
   5133    return ret;
   5134  }
   5135 
   5136  bool isFloat32Commutative() const override { return true; }
   5137 
   5138  double getIdentity() const override { return 0; }
   5139 
   5140  bool fallible() const;
   5141  void computeRange(TempAllocator& alloc) override;
   5142  bool canTruncate() const override;
   5143  void truncate(TruncateKind kind) override;
   5144  TruncateKind operandTruncateKind(size_t index) const override;
   5145 
   5146  [[nodiscard]] bool writeRecoverData(
   5147      CompactBufferWriter& writer) const override;
   5148  bool canRecoverOnBailout() const override {
   5149    return IsTypeRepresentableAsDouble(type());
   5150  }
   5151 
   5152  ALLOW_CLONE(MAdd)
   5153 };
   5154 
   5155 class MSub : public MBinaryArithInstruction {
   5156  MSub(MDefinition* left, MDefinition* right, MIRType type)
   5157      : MBinaryArithInstruction(classOpcode, left, right, type) {}
   5158 
   5159 public:
   5160  INSTRUCTION_HEADER(Sub)
   5161  TRIVIAL_NEW_WRAPPERS
   5162 
   5163  static MSub* NewWasm(TempAllocator& alloc, MDefinition* left,
   5164                       MDefinition* right, MIRType type, bool mustPreserveNaN) {
   5165    auto* ret = new (alloc) MSub(left, right, type);
   5166    ret->setMustPreserveNaN(mustPreserveNaN);
   5167    if (type == MIRType::Int32) {
   5168      ret->setTruncateKind(TruncateKind::Truncate);
   5169    }
   5170    return ret;
   5171  }
   5172 
   5173  MDefinition* foldsTo(TempAllocator& alloc) override;
   5174 
   5175  double getIdentity() const override { return 0; }
   5176 
   5177  bool isFloat32Commutative() const override { return true; }
   5178 
   5179  bool fallible() const;
   5180  void computeRange(TempAllocator& alloc) override;
   5181  bool canTruncate() const override;
   5182  void truncate(TruncateKind kind) override;
   5183  TruncateKind operandTruncateKind(size_t index) const override;
   5184 
   5185  [[nodiscard]] bool writeRecoverData(
   5186      CompactBufferWriter& writer) const override;
   5187  bool canRecoverOnBailout() const override {
   5188    return IsTypeRepresentableAsDouble(type());
   5189  }
   5190 
   5191  ALLOW_CLONE(MSub)
   5192 };
   5193 
   5194 class MMul : public MBinaryArithInstruction {
   5195 public:
   5196  enum Mode { Normal, Integer };
   5197 
   5198 private:
   5199  // Annotation the result could be a negative zero
   5200  // and we need to guard this during execution.
   5201  bool canBeNegativeZero_;
   5202 
   5203  Mode mode_;
   5204 
   5205  MMul(MDefinition* left, MDefinition* right, MIRType type, Mode mode)
   5206      : MBinaryArithInstruction(classOpcode, left, right, type),
   5207        canBeNegativeZero_(true),
   5208        mode_(mode) {
   5209    setCommutative();
   5210    if (mode == Integer) {
   5211      // This implements the required behavior for Math.imul, which
   5212      // can never fail and always truncates its output to int32.
   5213      canBeNegativeZero_ = false;
   5214      setTruncateKind(TruncateKind::Truncate);
   5215    }
   5216    MOZ_ASSERT_IF(mode != Integer, mode == Normal);
   5217  }
   5218 
   5219 public:
   5220  INSTRUCTION_HEADER(Mul)
   5221 
   5222  static MMul* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
   5223                   MIRType type, Mode mode = Normal) {
   5224    return new (alloc) MMul(left, right, type, mode);
   5225  }
   5226  static MMul* NewWasm(TempAllocator& alloc, MDefinition* left,
   5227                       MDefinition* right, MIRType type, Mode mode,
   5228                       bool mustPreserveNaN) {
   5229    auto* ret = new (alloc) MMul(left, right, type, mode);
   5230    ret->setMustPreserveNaN(mustPreserveNaN);
   5231    return ret;
   5232  }
   5233 
   5234  MDefinition* foldsTo(TempAllocator& alloc) override;
   5235  void analyzeEdgeCasesForward() override;
   5236  void analyzeEdgeCasesBackward() override;
   5237  void collectRangeInfoPreTrunc() override;
   5238 
   5239  double getIdentity() const override { return 1; }
   5240 
   5241  bool congruentTo(const MDefinition* ins) const override {
   5242    if (!ins->isMul()) {
   5243      return false;
   5244    }
   5245 
   5246    const MMul* mul = ins->toMul();
   5247    if (canBeNegativeZero_ != mul->canBeNegativeZero()) {
   5248      return false;
   5249    }
   5250 
   5251    if (mode_ != mul->mode()) {
   5252      return false;
   5253    }
   5254 
   5255    if (mustPreserveNaN() != mul->mustPreserveNaN()) {
   5256      return false;
   5257    }
   5258 
   5259    return binaryCongruentTo(ins);
   5260  }
   5261 
   5262  bool canOverflow() const;
   5263 
   5264  bool canBeNegativeZero() const { return canBeNegativeZero_; }
   5265  void setCanBeNegativeZero(bool negativeZero) {
   5266    canBeNegativeZero_ = negativeZero;
   5267  }
   5268 
   5269  bool fallible() const { return canBeNegativeZero_ || canOverflow(); }
   5270 
   5271  bool isFloat32Commutative() const override { return true; }
   5272 
   5273  void computeRange(TempAllocator& alloc) override;
   5274  bool canTruncate() const override;
   5275  void truncate(TruncateKind kind) override;
   5276  TruncateKind operandTruncateKind(size_t index) const override;
   5277 
   5278  Mode mode() const { return mode_; }
   5279 
   5280  [[nodiscard]] bool writeRecoverData(
   5281      CompactBufferWriter& writer) const override;
   5282  bool canRecoverOnBailout() const override {
   5283    return IsTypeRepresentableAsDouble(type());
   5284  }
   5285 
   5286  ALLOW_CLONE(MMul)
   5287 };
   5288 
   5289 class MDiv : public MBinaryArithInstruction {
   5290  bool canBeNegativeZero_;
   5291  bool canBeNegativeOverflow_;
   5292  bool canBeDivideByZero_;
   5293  bool canBeNegativeDividend_;
   5294  bool unsigned_;  // If false, signedness will be derived from operands
   5295  bool trapOnError_;
   5296  wasm::TrapSiteDesc trapSiteDesc_;
   5297 
   5298  MDiv(MDefinition* left, MDefinition* right, MIRType type)
   5299      : MBinaryArithInstruction(classOpcode, left, right, type),
   5300        canBeNegativeZero_(true),
   5301        canBeNegativeOverflow_(true),
   5302        canBeDivideByZero_(true),
   5303        canBeNegativeDividend_(true),
   5304        unsigned_(false),
   5305        trapOnError_(false) {}
   5306 
   5307 public:
   5308  INSTRUCTION_HEADER(Div)
   5309 
   5310  static MDiv* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
   5311                   MIRType type) {
   5312    return new (alloc) MDiv(left, right, type);
   5313  }
   5314  static MDiv* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
   5315                   MIRType type, bool unsignd, bool trapOnError = false,
   5316                   wasm::TrapSiteDesc trapSiteDesc = wasm::TrapSiteDesc(),
   5317                   bool mustPreserveNaN = false) {
   5318    auto* div = new (alloc) MDiv(left, right, type);
   5319    div->unsigned_ = unsignd;
   5320    div->trapOnError_ = trapOnError;
   5321    div->trapSiteDesc_ = trapSiteDesc;
   5322    if (trapOnError) {
   5323      div->setGuard();  // not removable because of possible side-effects.
   5324      div->setNotMovable();
   5325    }
   5326    div->setMustPreserveNaN(mustPreserveNaN);
   5327    if (type == MIRType::Int32) {
   5328      div->setTruncateKind(TruncateKind::Truncate);
   5329    }
   5330    return div;
   5331  }
   5332 
   5333  MDefinition* foldsTo(TempAllocator& alloc) override;
   5334  void analyzeEdgeCasesForward() override;
   5335  void analyzeEdgeCasesBackward() override;
   5336 
   5337  double getIdentity() const override { MOZ_CRASH("not used"); }
   5338 
   5339  bool canBeNegativeZero() const {
   5340    // This flag is only valid for integer division.
   5341    MOZ_ASSERT(type() == MIRType::Int32);
   5342    return canBeNegativeZero_;
   5343  }
   5344  void setCanBeNegativeZero(bool negativeZero) {
   5345    canBeNegativeZero_ = negativeZero;
   5346  }
   5347 
   5348  bool canBeNegativeOverflow() const { return canBeNegativeOverflow_; }
   5349 
   5350  bool canBeDivideByZero() const { return canBeDivideByZero_; }
   5351 
   5352  bool canBeNegativeDividend() const {
   5353    // "Dividend" is an ambiguous concept for unsigned truncated
   5354    // division, because of the truncation procedure:
   5355    // ((x>>>0)/2)|0, for example, gets transformed in
   5356    // MDiv::truncate into a node with lhs representing x (not
   5357    // x>>>0) and rhs representing the constant 2; in other words,
   5358    // the MIR node corresponds to "cast operands to unsigned and
   5359    // divide" operation. In this case, is the dividend x or is it
   5360    // x>>>0? In order to resolve such ambiguities, we disallow
   5361    // the usage of this method for unsigned division.
   5362    MOZ_ASSERT(!unsigned_);
   5363    return canBeNegativeDividend_;
   5364  }
   5365 
   5366  bool isUnsigned() const { return unsigned_; }
   5367 
   5368  bool isTruncatedIndirectly() const {
   5369    return truncateKind() >= TruncateKind::IndirectTruncate;
   5370  }
   5371 
   5372  bool canTruncateInfinities() const { return isTruncated(); }
   5373  bool canTruncateRemainder() const { return isTruncated(); }
   5374  bool canTruncateOverflow() const {
   5375    return isTruncated() || isTruncatedIndirectly();
   5376  }
   5377  bool canTruncateNegativeZero() const {
   5378    return isTruncated() || isTruncatedIndirectly();
   5379  }
   5380 
   5381  bool trapOnError() const { return trapOnError_; }
   5382  const wasm::TrapSiteDesc& trapSiteDesc() const {
   5383    MOZ_ASSERT(trapSiteDesc_.isValid());
   5384    return trapSiteDesc_;
   5385  }
   5386 
   5387  bool isFloat32Commutative() const override { return true; }
   5388 
   5389  void computeRange(TempAllocator& alloc) override;
   5390  bool fallible() const;
   5391  bool canTruncate() const override;
   5392  void truncate(TruncateKind kind) override;
   5393  void collectRangeInfoPreTrunc() override;
   5394  TruncateKind operandTruncateKind(size_t index) const override;
   5395 
   5396  [[nodiscard]] bool writeRecoverData(
   5397      CompactBufferWriter& writer) const override;
   5398  bool canRecoverOnBailout() const override { return type() != MIRType::Int64; }
   5399 
   5400  bool congruentTo(const MDefinition* ins) const override {
   5401    if (!MBinaryArithInstruction::congruentTo(ins)) {
   5402      return false;
   5403    }
   5404    const MDiv* other = ins->toDiv();
   5405    MOZ_ASSERT(other->trapOnError() == trapOnError_);
   5406    return unsigned_ == other->isUnsigned();
   5407  }
   5408 
   5409  ALLOW_CLONE(MDiv)
   5410 };
   5411 
   5412 class MMod : public MBinaryArithInstruction {
   5413  bool unsigned_;  // If false, signedness will be derived from operands
   5414  bool canBeNegativeDividend_;
   5415  bool canBePowerOfTwoDivisor_;
   5416  bool canBeDivideByZero_;
   5417  bool trapOnError_;
   5418  wasm::TrapSiteDesc trapSiteDesc_;
   5419 
   5420  MMod(MDefinition* left, MDefinition* right, MIRType type)
   5421      : MBinaryArithInstruction(classOpcode, left, right, type),
   5422        unsigned_(false),
   5423        canBeNegativeDividend_(true),
   5424        canBePowerOfTwoDivisor_(true),
   5425        canBeDivideByZero_(true),
   5426        trapOnError_(false) {}
   5427 
   5428 public:
   5429  INSTRUCTION_HEADER(Mod)
   5430 
   5431  static MMod* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
   5432                   MIRType type) {
   5433    return new (alloc) MMod(left, right, type);
   5434  }
   5435  static MMod* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
   5436                   MIRType type, bool unsignd, bool trapOnError = false,
   5437                   wasm::TrapSiteDesc trapSiteDesc = wasm::TrapSiteDesc()) {
   5438    auto* mod = new (alloc) MMod(left, right, type);
   5439    mod->unsigned_ = unsignd;
   5440    mod->trapOnError_ = trapOnError;
   5441    mod->trapSiteDesc_ = trapSiteDesc;
   5442    if (trapOnError) {
   5443      mod->setGuard();  // not removable because of possible side-effects.
   5444      mod->setNotMovable();
   5445    }
   5446    if (type == MIRType::Int32) {
   5447      mod->setTruncateKind(TruncateKind::Truncate);
   5448    }
   5449    return mod;
   5450  }
   5451 
   5452  MDefinition* foldsTo(TempAllocator& alloc) override;
   5453 
   5454  double getIdentity() const override { MOZ_CRASH("not used"); }
   5455 
   5456  bool canBeNegativeDividend() const {
   5457    MOZ_ASSERT(type() == MIRType::Int32 || type() == MIRType::Int64);
   5458    MOZ_ASSERT(!unsigned_);
   5459    return canBeNegativeDividend_;
   5460  }
   5461 
   5462  bool canBeDivideByZero() const {
   5463    MOZ_ASSERT(type() == MIRType::Int32 || type() == MIRType::Int64);
   5464    return canBeDivideByZero_;
   5465  }
   5466 
   5467  bool canBePowerOfTwoDivisor() const {
   5468    MOZ_ASSERT(type() == MIRType::Int32);
   5469    return canBePowerOfTwoDivisor_;
   5470  }
   5471 
   5472  void analyzeEdgeCasesForward() override;
   5473 
   5474  bool isUnsigned() const { return unsigned_; }
   5475 
   5476  bool trapOnError() const { return trapOnError_; }
   5477  const wasm::TrapSiteDesc& trapSiteDesc() const {
   5478    MOZ_ASSERT(trapSiteDesc_.isValid());
   5479    return trapSiteDesc_;
   5480  }
   5481 
   5482  [[nodiscard]] bool writeRecoverData(
   5483      CompactBufferWriter& writer) const override;
   5484  bool canRecoverOnBailout() const override { return type() != MIRType::Int64; }
   5485 
   5486  bool fallible() const;
   5487 
   5488  void computeRange(TempAllocator& alloc) override;
   5489  bool canTruncate() const override;
   5490  void truncate(TruncateKind kind) override;
   5491  void collectRangeInfoPreTrunc() override;
   5492  TruncateKind operandTruncateKind(size_t index) const override;
   5493 
   5494  bool congruentTo(const MDefinition* ins) const override {
   5495    return MBinaryArithInstruction::congruentTo(ins) &&
   5496           unsigned_ == ins->toMod()->isUnsigned();
   5497  }
   5498 
   5499  bool possiblyCalls() const override { return type() == MIRType::Double; }
   5500 
   5501  ALLOW_CLONE(MMod)
   5502 };
   5503 
   5504 class MBigIntBinaryArithInstruction : public MBinaryInstruction,
   5505                                      public BigIntArithPolicy::Data {
   5506 protected:
   5507  MBigIntBinaryArithInstruction(Opcode op, MDefinition* left,
   5508                                MDefinition* right)
   5509      : MBinaryInstruction(op, left, right) {
   5510    setResultType(MIRType::BigInt);
   5511    setMovable();
   5512  }
   5513 
   5514 public:
   5515  bool congruentTo(const MDefinition* ins) const override {
   5516    return binaryCongruentTo(ins);
   5517  }
   5518 
   5519  AliasSet getAliasSet() const override { return AliasSet::None(); }
   5520 };
   5521 
   5522 class MBigIntAdd : public MBigIntBinaryArithInstruction {
   5523  MBigIntAdd(MDefinition* left, MDefinition* right)
   5524      : MBigIntBinaryArithInstruction(classOpcode, left, right) {
   5525    setCommutative();
   5526 
   5527    // Don't guard this instruction even though adding two BigInts can throw
   5528    // JSMSG_BIGINT_TOO_LARGE. This matches the behavior when adding too large
   5529    // strings in MConcat.
   5530  }
   5531 
   5532 public:
   5533  INSTRUCTION_HEADER(BigIntAdd)
   5534  TRIVIAL_NEW_WRAPPERS
   5535 
   5536  [[nodiscard]] bool writeRecoverData(
   5537      CompactBufferWriter& writer) const override;
   5538  bool canRecoverOnBailout() const override { return true; }
   5539 
   5540  ALLOW_CLONE(MBigIntAdd)
   5541 };
   5542 
   5543 class MBigIntSub : public MBigIntBinaryArithInstruction {
   5544  MBigIntSub(MDefinition* left, MDefinition* right)
   5545      : MBigIntBinaryArithInstruction(classOpcode, left, right) {
   5546    // See MBigIntAdd for why we don't guard this instruction.
   5547  }
   5548 
   5549 public:
   5550  INSTRUCTION_HEADER(BigIntSub)
   5551  TRIVIAL_NEW_WRAPPERS
   5552 
   5553  [[nodiscard]] bool writeRecoverData(
   5554      CompactBufferWriter& writer) const override;
   5555  bool canRecoverOnBailout() const override { return true; }
   5556 
   5557  ALLOW_CLONE(MBigIntSub)
   5558 };
   5559 
   5560 class MBigIntMul : public MBigIntBinaryArithInstruction {
   5561  MBigIntMul(MDefinition* left, MDefinition* right)
   5562      : MBigIntBinaryArithInstruction(classOpcode, left, right) {
   5563    setCommutative();
   5564 
   5565    // See MBigIntAdd for why we don't guard this instruction.
   5566  }
   5567 
   5568 public:
   5569  INSTRUCTION_HEADER(BigIntMul)
   5570  TRIVIAL_NEW_WRAPPERS
   5571 
   5572  [[nodiscard]] bool writeRecoverData(
   5573      CompactBufferWriter& writer) const override;
   5574  bool canRecoverOnBailout() const override { return true; }
   5575 
   5576  ALLOW_CLONE(MBigIntMul)
   5577 };
   5578 
   5579 class MBigIntDiv : public MBigIntBinaryArithInstruction {
   5580  bool canBeDivideByZero_;
   5581 
   5582  MBigIntDiv(MDefinition* left, MDefinition* right)
   5583      : MBigIntBinaryArithInstruction(classOpcode, left, right) {
   5584    MOZ_ASSERT(right->type() == MIRType::BigInt);
   5585    canBeDivideByZero_ =
   5586        !right->isConstant() || right->toConstant()->toBigInt()->isZero();
   5587 
   5588    // Throws when the divisor is zero.
   5589    if (canBeDivideByZero_) {
   5590      setGuard();
   5591      setNotMovable();
   5592    }
   5593  }
   5594 
   5595 public:
   5596  INSTRUCTION_HEADER(BigIntDiv)
   5597  TRIVIAL_NEW_WRAPPERS
   5598 
   5599  bool canBeDivideByZero() const { return canBeDivideByZero_; }
   5600 
   5601  AliasSet getAliasSet() const override {
   5602    if (canBeDivideByZero()) {
   5603      return AliasSet::Store(AliasSet::ExceptionState);
   5604    }
   5605    return AliasSet::None();
   5606  }
   5607 
   5608  [[nodiscard]] bool writeRecoverData(
   5609      CompactBufferWriter& writer) const override;
   5610  bool canRecoverOnBailout() const override { return !canBeDivideByZero(); }
   5611 
   5612  ALLOW_CLONE(MBigIntDiv)
   5613 };
   5614 
   5615 class MBigIntMod : public MBigIntBinaryArithInstruction {
   5616  bool canBeDivideByZero_;
   5617 
   5618  MBigIntMod(MDefinition* left, MDefinition* right)
   5619      : MBigIntBinaryArithInstruction(classOpcode, left, right) {
   5620    MOZ_ASSERT(right->type() == MIRType::BigInt);
   5621    canBeDivideByZero_ =
   5622        !right->isConstant() || right->toConstant()->toBigInt()->isZero();
   5623 
   5624    // Throws when the divisor is zero.
   5625    if (canBeDivideByZero_) {
   5626      setGuard();
   5627      setNotMovable();
   5628    }
   5629  }
   5630 
   5631 public:
   5632  INSTRUCTION_HEADER(BigIntMod)
   5633  TRIVIAL_NEW_WRAPPERS
   5634 
   5635  bool canBeDivideByZero() const { return canBeDivideByZero_; }
   5636 
   5637  AliasSet getAliasSet() const override {
   5638    if (canBeDivideByZero()) {
   5639      return AliasSet::Store(AliasSet::ExceptionState);
   5640    }
   5641    return AliasSet::None();
   5642  }
   5643 
   5644  [[nodiscard]] bool writeRecoverData(
   5645      CompactBufferWriter& writer) const override;
   5646  bool canRecoverOnBailout() const override { return !canBeDivideByZero(); }
   5647 
   5648  ALLOW_CLONE(MBigIntMod)
   5649 };
   5650 
   5651 class MBigIntPow : public MBigIntBinaryArithInstruction {
   5652  bool canBeNegativeExponent_;
   5653 
   5654  MBigIntPow(MDefinition* left, MDefinition* right)
   5655      : MBigIntBinaryArithInstruction(classOpcode, left, right) {
   5656    MOZ_ASSERT(right->type() == MIRType::BigInt);
   5657    canBeNegativeExponent_ =
   5658        !right->isConstant() || right->toConstant()->toBigInt()->isNegative();
   5659 
   5660    // Throws when the exponent is negative.
   5661    if (canBeNegativeExponent_) {
   5662      setGuard();
   5663      setNotMovable();
   5664    }
   5665  }
   5666 
   5667 public:
   5668  INSTRUCTION_HEADER(BigIntPow)
   5669  TRIVIAL_NEW_WRAPPERS
   5670 
   5671  bool canBeNegativeExponent() const { return canBeNegativeExponent_; }
   5672 
   5673  AliasSet getAliasSet() const override {
   5674    if (canBeNegativeExponent()) {
   5675      return AliasSet::Store(AliasSet::ExceptionState);
   5676    }
   5677    return AliasSet::None();
   5678  }
   5679 
   5680  MDefinition* foldsTo(TempAllocator& alloc) override;
   5681 
   5682  [[nodiscard]] bool writeRecoverData(
   5683      CompactBufferWriter& writer) const override;
   5684  bool canRecoverOnBailout() const override { return !canBeNegativeExponent(); }
   5685 
   5686  ALLOW_CLONE(MBigIntPow)
   5687 };
   5688 
   5689 class MBigIntBitAnd : public MBigIntBinaryArithInstruction {
   5690  MBigIntBitAnd(MDefinition* left, MDefinition* right)
   5691      : MBigIntBinaryArithInstruction(classOpcode, left, right) {
   5692    setCommutative();
   5693 
   5694    // We don't need to guard this instruction because it can only fail on OOM.
   5695  }
   5696 
   5697 public:
   5698  INSTRUCTION_HEADER(BigIntBitAnd)
   5699  TRIVIAL_NEW_WRAPPERS
   5700 
   5701  [[nodiscard]] bool writeRecoverData(
   5702      CompactBufferWriter& writer) const override;
   5703  bool canRecoverOnBailout() const override { return true; }
   5704 
   5705  ALLOW_CLONE(MBigIntBitAnd)
   5706 };
   5707 
   5708 class MBigIntBitOr : public MBigIntBinaryArithInstruction {
   5709  MBigIntBitOr(MDefinition* left, MDefinition* right)
   5710      : MBigIntBinaryArithInstruction(classOpcode, left, right) {
   5711    setCommutative();
   5712 
   5713    // We don't need to guard this instruction because it can only fail on OOM.
   5714  }
   5715 
   5716 public:
   5717  INSTRUCTION_HEADER(BigIntBitOr)
   5718  TRIVIAL_NEW_WRAPPERS
   5719 
   5720  [[nodiscard]] bool writeRecoverData(
   5721      CompactBufferWriter& writer) const override;
   5722  bool canRecoverOnBailout() const override { return true; }
   5723 
   5724  ALLOW_CLONE(MBigIntBitOr)
   5725 };
   5726 
   5727 class MBigIntBitXor : public MBigIntBinaryArithInstruction {
   5728  MBigIntBitXor(MDefinition* left, MDefinition* right)
   5729      : MBigIntBinaryArithInstruction(classOpcode, left, right) {
   5730    setCommutative();
   5731 
   5732    // We don't need to guard this instruction because it can only fail on OOM.
   5733  }
   5734 
   5735 public:
   5736  INSTRUCTION_HEADER(BigIntBitXor)
   5737  TRIVIAL_NEW_WRAPPERS
   5738 
   5739  [[nodiscard]] bool writeRecoverData(
   5740      CompactBufferWriter& writer) const override;
   5741  bool canRecoverOnBailout() const override { return true; }
   5742 
   5743  ALLOW_CLONE(MBigIntBitXor)
   5744 };
   5745 
   5746 class MBigIntLsh : public MBigIntBinaryArithInstruction {
   5747  MBigIntLsh(MDefinition* left, MDefinition* right)
   5748      : MBigIntBinaryArithInstruction(classOpcode, left, right) {
   5749    // See MBigIntAdd for why we don't guard this instruction.
   5750  }
   5751 
   5752 public:
   5753  INSTRUCTION_HEADER(BigIntLsh)
   5754  TRIVIAL_NEW_WRAPPERS
   5755 
   5756  [[nodiscard]] bool writeRecoverData(
   5757      CompactBufferWriter& writer) const override;
   5758  bool canRecoverOnBailout() const override { return true; }
   5759 
   5760  ALLOW_CLONE(MBigIntLsh)
   5761 };
   5762 
   5763 class MBigIntRsh : public MBigIntBinaryArithInstruction {
   5764  MBigIntRsh(MDefinition* left, MDefinition* right)
   5765      : MBigIntBinaryArithInstruction(classOpcode, left, right) {
   5766    // See MBigIntAdd for why we don't guard this instruction.
   5767  }
   5768 
   5769 public:
   5770  INSTRUCTION_HEADER(BigIntRsh)
   5771  TRIVIAL_NEW_WRAPPERS
   5772 
   5773  [[nodiscard]] bool writeRecoverData(
   5774      CompactBufferWriter& writer) const override;
   5775  bool canRecoverOnBailout() const override { return true; }
   5776 
   5777  ALLOW_CLONE(MBigIntRsh)
   5778 };
   5779 
   5780 class MBigIntUnaryArithInstruction : public MUnaryInstruction,
   5781                                     public BigIntArithPolicy::Data {
   5782 protected:
   5783  MBigIntUnaryArithInstruction(Opcode op, MDefinition* input)
   5784      : MUnaryInstruction(op, input) {
   5785    setResultType(MIRType::BigInt);
   5786    setMovable();
   5787  }
   5788 
   5789 public:
   5790  bool congruentTo(const MDefinition* ins) const override {
   5791    return congruentIfOperandsEqual(ins);
   5792  }
   5793 
   5794  AliasSet getAliasSet() const override { return AliasSet::None(); }
   5795 };
   5796 
   5797 class MBigIntIncrement : public MBigIntUnaryArithInstruction {
   5798  explicit MBigIntIncrement(MDefinition* input)
   5799      : MBigIntUnaryArithInstruction(classOpcode, input) {
   5800    // See MBigIntAdd for why we don't guard this instruction.
   5801  }
   5802 
   5803 public:
   5804  INSTRUCTION_HEADER(BigIntIncrement)
   5805  TRIVIAL_NEW_WRAPPERS
   5806 
   5807  [[nodiscard]] bool writeRecoverData(
   5808      CompactBufferWriter& writer) const override;
   5809  bool canRecoverOnBailout() const override { return true; }
   5810 
   5811  ALLOW_CLONE(MBigIntIncrement)
   5812 };
   5813 
   5814 class MBigIntDecrement : public MBigIntUnaryArithInstruction {
   5815  explicit MBigIntDecrement(MDefinition* input)
   5816      : MBigIntUnaryArithInstruction(classOpcode, input) {
   5817    // See MBigIntAdd for why we don't guard this instruction.
   5818  }
   5819 
   5820 public:
   5821  INSTRUCTION_HEADER(BigIntDecrement)
   5822  TRIVIAL_NEW_WRAPPERS
   5823 
   5824  [[nodiscard]] bool writeRecoverData(
   5825      CompactBufferWriter& writer) const override;
   5826  bool canRecoverOnBailout() const override { return true; }
   5827 
   5828  ALLOW_CLONE(MBigIntDecrement)
   5829 };
   5830 
   5831 class MBigIntNegate : public MBigIntUnaryArithInstruction {
   5832  explicit MBigIntNegate(MDefinition* input)
   5833      : MBigIntUnaryArithInstruction(classOpcode, input) {
   5834    // We don't need to guard this instruction because it can only fail on OOM.
   5835  }
   5836 
   5837 public:
   5838  INSTRUCTION_HEADER(BigIntNegate)
   5839  TRIVIAL_NEW_WRAPPERS
   5840 
   5841  [[nodiscard]] bool writeRecoverData(
   5842      CompactBufferWriter& writer) const override;
   5843  bool canRecoverOnBailout() const override { return true; }
   5844 
   5845  ALLOW_CLONE(MBigIntNegate)
   5846 };
   5847 
   5848 class MBigIntBitNot : public MBigIntUnaryArithInstruction {
   5849  explicit MBigIntBitNot(MDefinition* input)
   5850      : MBigIntUnaryArithInstruction(classOpcode, input) {
   5851    // See MBigIntAdd for why we don't guard this instruction.
   5852  }
   5853 
   5854 public:
   5855  INSTRUCTION_HEADER(BigIntBitNot)
   5856  TRIVIAL_NEW_WRAPPERS
   5857 
   5858  [[nodiscard]] bool writeRecoverData(
   5859      CompactBufferWriter& writer) const override;
   5860  bool canRecoverOnBailout() const override { return true; }
   5861 
   5862  ALLOW_CLONE(MBigIntBitNot)
   5863 };
   5864 
   5865 class MBigIntPtrBinaryArithInstruction : public MBinaryInstruction,
   5866                                         public NoTypePolicy::Data {
   5867 protected:
   5868  MBigIntPtrBinaryArithInstruction(Opcode op, MDefinition* left,
   5869                                   MDefinition* right)
   5870      : MBinaryInstruction(op, left, right) {
   5871    MOZ_ASSERT(left->type() == MIRType::IntPtr);
   5872    MOZ_ASSERT(right->type() == MIRType::IntPtr);
   5873    setResultType(MIRType::IntPtr);
   5874    setMovable();
   5875  }
   5876 
   5877  static bool isMaybeZero(MDefinition* ins);
   5878  static bool isMaybeNegative(MDefinition* ins);
   5879 
   5880 public:
   5881  MDefinition* foldsTo(TempAllocator& alloc) override;
   5882 
   5883  bool congruentTo(const MDefinition* ins) const override {
   5884    return binaryCongruentTo(ins);
   5885  }
   5886 
   5887  AliasSet getAliasSet() const override { return AliasSet::None(); }
   5888 };
   5889 
   5890 class MBigIntPtrAdd : public MBigIntPtrBinaryArithInstruction {
   5891  MBigIntPtrAdd(MDefinition* left, MDefinition* right)
   5892      : MBigIntPtrBinaryArithInstruction(classOpcode, left, right) {
   5893    setCommutative();
   5894  }
   5895 
   5896 public:
   5897  INSTRUCTION_HEADER(BigIntPtrAdd)
   5898  TRIVIAL_NEW_WRAPPERS
   5899 
   5900  [[nodiscard]] bool writeRecoverData(
   5901      CompactBufferWriter& writer) const override;
   5902  bool canRecoverOnBailout() const override { return true; }
   5903 
   5904  ALLOW_CLONE(MBigIntPtrAdd)
   5905 };
   5906 
   5907 class MBigIntPtrSub : public MBigIntPtrBinaryArithInstruction {
   5908  MBigIntPtrSub(MDefinition* left, MDefinition* right)
   5909      : MBigIntPtrBinaryArithInstruction(classOpcode, left, right) {}
   5910 
   5911 public:
   5912  INSTRUCTION_HEADER(BigIntPtrSub)
   5913  TRIVIAL_NEW_WRAPPERS
   5914 
   5915  [[nodiscard]] bool writeRecoverData(
   5916      CompactBufferWriter& writer) const override;
   5917  bool canRecoverOnBailout() const override { return true; }
   5918 
   5919  ALLOW_CLONE(MBigIntPtrSub)
   5920 };
   5921 
   5922 class MBigIntPtrMul : public MBigIntPtrBinaryArithInstruction {
   5923  MBigIntPtrMul(MDefinition* left, MDefinition* right)
   5924      : MBigIntPtrBinaryArithInstruction(classOpcode, left, right) {
   5925    setCommutative();
   5926  }
   5927 
   5928 public:
   5929  INSTRUCTION_HEADER(BigIntPtrMul)
   5930  TRIVIAL_NEW_WRAPPERS
   5931 
   5932  [[nodiscard]] bool writeRecoverData(
   5933      CompactBufferWriter& writer) const override;
   5934  bool canRecoverOnBailout() const override { return true; }
   5935 
   5936  ALLOW_CLONE(MBigIntPtrMul)
   5937 };
   5938 
   5939 class MBigIntPtrDiv : public MBigIntPtrBinaryArithInstruction {
   5940  bool canBeDivideByZero_;
   5941 
   5942  MBigIntPtrDiv(MDefinition* left, MDefinition* right)
   5943      : MBigIntPtrBinaryArithInstruction(classOpcode, left, right) {
   5944    canBeDivideByZero_ = isMaybeZero(right);
   5945 
   5946    // Bails when the divisor is zero.
   5947    if (canBeDivideByZero_) {
   5948      setGuard();
   5949    }
   5950  }
   5951 
   5952 public:
   5953  INSTRUCTION_HEADER(BigIntPtrDiv)
   5954  TRIVIAL_NEW_WRAPPERS
   5955 
   5956  bool canBeDivideByZero() const { return canBeDivideByZero_; }
   5957 
   5958  [[nodiscard]] bool writeRecoverData(
   5959      CompactBufferWriter& writer) const override;
   5960  bool canRecoverOnBailout() const override { return true; }
   5961 
   5962  ALLOW_CLONE(MBigIntPtrDiv)
   5963 };
   5964 
   5965 class MBigIntPtrMod : public MBigIntPtrBinaryArithInstruction {
   5966  bool canBeDivideByZero_;
   5967 
   5968  MBigIntPtrMod(MDefinition* left, MDefinition* right)
   5969      : MBigIntPtrBinaryArithInstruction(classOpcode, left, right) {
   5970    canBeDivideByZero_ = isMaybeZero(right);
   5971 
   5972    // Bails when the divisor is zero.
   5973    if (canBeDivideByZero_) {
   5974      setGuard();
   5975    }
   5976  }
   5977 
   5978 public:
   5979  INSTRUCTION_HEADER(BigIntPtrMod)
   5980  TRIVIAL_NEW_WRAPPERS
   5981 
   5982  bool canBeDivideByZero() const { return canBeDivideByZero_; }
   5983 
   5984  [[nodiscard]] bool writeRecoverData(
   5985      CompactBufferWriter& writer) const override;
   5986  bool canRecoverOnBailout() const override { return true; }
   5987 
   5988  ALLOW_CLONE(MBigIntPtrMod)
   5989 };
   5990 
   5991 class MBigIntPtrPow : public MBigIntPtrBinaryArithInstruction {
   5992  bool canBeNegativeExponent_;
   5993 
   5994  MBigIntPtrPow(MDefinition* left, MDefinition* right)
   5995      : MBigIntPtrBinaryArithInstruction(classOpcode, left, right) {
   5996    canBeNegativeExponent_ = isMaybeNegative(right);
   5997 
   5998    // Bails when the exponent is negative.
   5999    if (canBeNegativeExponent_) {
   6000      setGuard();
   6001    }
   6002  }
   6003 
   6004 public:
   6005  INSTRUCTION_HEADER(BigIntPtrPow)
   6006  TRIVIAL_NEW_WRAPPERS
   6007 
   6008  bool canBeNegativeExponent() const { return canBeNegativeExponent_; }
   6009 
   6010  MDefinition* foldsTo(TempAllocator& alloc) override;
   6011 
   6012  [[nodiscard]] bool writeRecoverData(
   6013      CompactBufferWriter& writer) const override;
   6014  bool canRecoverOnBailout() const override { return true; }
   6015 
   6016  ALLOW_CLONE(MBigIntPtrPow)
   6017 };
   6018 
   6019 class MBigIntPtrBinaryBitwiseInstruction : public MBinaryInstruction,
   6020                                           public NoTypePolicy::Data {
   6021 protected:
   6022  MBigIntPtrBinaryBitwiseInstruction(Opcode op, MDefinition* left,
   6023                                     MDefinition* right)
   6024      : MBinaryInstruction(op, left, right) {
   6025    MOZ_ASSERT(left->type() == MIRType::IntPtr);
   6026    MOZ_ASSERT(right->type() == MIRType::IntPtr);
   6027    setResultType(MIRType::IntPtr);
   6028    setMovable();
   6029  }
   6030 
   6031 public:
   6032  MDefinition* foldsTo(TempAllocator& alloc) override;
   6033 
   6034  bool congruentTo(const MDefinition* ins) const override {
   6035    return binaryCongruentTo(ins);
   6036  }
   6037 
   6038  AliasSet getAliasSet() const override { return AliasSet::None(); }
   6039 };
   6040 
   6041 class MBigIntPtrBitAnd : public MBigIntPtrBinaryBitwiseInstruction {
   6042  MBigIntPtrBitAnd(MDefinition* left, MDefinition* right)
   6043      : MBigIntPtrBinaryBitwiseInstruction(classOpcode, left, right) {
   6044    setCommutative();
   6045  }
   6046 
   6047 public:
   6048  INSTRUCTION_HEADER(BigIntPtrBitAnd)
   6049  TRIVIAL_NEW_WRAPPERS
   6050 
   6051  [[nodiscard]] bool writeRecoverData(
   6052      CompactBufferWriter& writer) const override;
   6053  bool canRecoverOnBailout() const override { return true; }
   6054 
   6055  ALLOW_CLONE(MBigIntPtrBitAnd)
   6056 };
   6057 
   6058 class MBigIntPtrBitOr : public MBigIntPtrBinaryBitwiseInstruction {
   6059  MBigIntPtrBitOr(MDefinition* left, MDefinition* right)
   6060      : MBigIntPtrBinaryBitwiseInstruction(classOpcode, left, right) {
   6061    setCommutative();
   6062  }
   6063 
   6064 public:
   6065  INSTRUCTION_HEADER(BigIntPtrBitOr)
   6066  TRIVIAL_NEW_WRAPPERS
   6067 
   6068  [[nodiscard]] bool writeRecoverData(
   6069      CompactBufferWriter& writer) const override;
   6070  bool canRecoverOnBailout() const override { return true; }
   6071 
   6072  ALLOW_CLONE(MBigIntPtrBitOr)
   6073 };
   6074 
   6075 class MBigIntPtrBitXor : public MBigIntPtrBinaryBitwiseInstruction {
   6076  MBigIntPtrBitXor(MDefinition* left, MDefinition* right)
   6077      : MBigIntPtrBinaryBitwiseInstruction(classOpcode, left, right) {
   6078    setCommutative();
   6079  }
   6080 
   6081 public:
   6082  INSTRUCTION_HEADER(BigIntPtrBitXor)
   6083  TRIVIAL_NEW_WRAPPERS
   6084 
   6085  [[nodiscard]] bool writeRecoverData(
   6086      CompactBufferWriter& writer) const override;
   6087  bool canRecoverOnBailout() const override { return true; }
   6088 
   6089  ALLOW_CLONE(MBigIntPtrBitXor)
   6090 };
   6091 
   6092 class MBigIntPtrLsh : public MBigIntPtrBinaryBitwiseInstruction {
   6093  MBigIntPtrLsh(MDefinition* left, MDefinition* right)
   6094      : MBigIntPtrBinaryBitwiseInstruction(classOpcode, left, right) {}
   6095 
   6096 public:
   6097  INSTRUCTION_HEADER(BigIntPtrLsh)
   6098  TRIVIAL_NEW_WRAPPERS
   6099 
   6100  bool fallible() const {
   6101    return !rhs()->isConstant() || rhs()->toConstant()->toIntPtr() > 0;
   6102  }
   6103 
   6104  [[nodiscard]] bool writeRecoverData(
   6105      CompactBufferWriter& writer) const override;
   6106  bool canRecoverOnBailout() const override { return true; }
   6107 
   6108  ALLOW_CLONE(MBigIntPtrLsh)
   6109 };
   6110 
   6111 class MBigIntPtrRsh : public MBigIntPtrBinaryBitwiseInstruction {
   6112  MBigIntPtrRsh(MDefinition* left, MDefinition* right)
   6113      : MBigIntPtrBinaryBitwiseInstruction(classOpcode, left, right) {}
   6114 
   6115 public:
   6116  INSTRUCTION_HEADER(BigIntPtrRsh)
   6117  TRIVIAL_NEW_WRAPPERS
   6118 
   6119  bool fallible() const {
   6120    return !rhs()->isConstant() || rhs()->toConstant()->toIntPtr() < 0;
   6121  }
   6122 
   6123  [[nodiscard]] bool writeRecoverData(
   6124      CompactBufferWriter& writer) const override;
   6125  bool canRecoverOnBailout() const override { return true; }
   6126 
   6127  ALLOW_CLONE(MBigIntPtrRsh)
   6128 };
   6129 
   6130 class MBigIntPtrBitNot : public MUnaryInstruction, public NoTypePolicy::Data {
   6131  explicit MBigIntPtrBitNot(MDefinition* input)
   6132      : MUnaryInstruction(classOpcode, input) {
   6133    MOZ_ASSERT(input->type() == MIRType::IntPtr);
   6134    setResultType(MIRType::IntPtr);
   6135    setMovable();
   6136  }
   6137 
   6138 public:
   6139  INSTRUCTION_HEADER(BigIntPtrBitNot)
   6140  TRIVIAL_NEW_WRAPPERS
   6141 
   6142  MDefinition* foldsTo(TempAllocator& alloc) override;
   6143 
   6144  bool congruentTo(const MDefinition* ins) const override {
   6145    return congruentIfOperandsEqual(ins);
   6146  }
   6147  AliasSet getAliasSet() const override { return AliasSet::None(); }
   6148 
   6149  [[nodiscard]] bool writeRecoverData(
   6150      CompactBufferWriter& writer) const override;
   6151  bool canRecoverOnBailout() const override { return true; }
   6152 
   6153  ALLOW_CLONE(MBigIntPtrBitNot)
   6154 };
   6155 
   6156 class MConcat : public MBinaryInstruction,
   6157                public MixPolicy<ConvertToStringPolicy<0>,
   6158                                 ConvertToStringPolicy<1>>::Data {
   6159  MConcat(MDefinition* left, MDefinition* right)
   6160      : MBinaryInstruction(classOpcode, left, right) {
   6161    // At least one input should be definitely string
   6162    MOZ_ASSERT(left->type() == MIRType::String ||
   6163               right->type() == MIRType::String);
   6164 
   6165    setMovable();
   6166    setResultType(MIRType::String);
   6167  }
   6168 
   6169 public:
   6170  INSTRUCTION_HEADER(Concat)
   6171  TRIVIAL_NEW_WRAPPERS
   6172 
   6173  MDefinition* foldsTo(TempAllocator& alloc) override;
   6174  bool congruentTo(const MDefinition* ins) const override {
   6175    return congruentIfOperandsEqual(ins);
   6176  }
   6177  AliasSet getAliasSet() const override { return AliasSet::None(); }
   6178 
   6179  [[nodiscard]] bool writeRecoverData(
   6180      CompactBufferWriter& writer) const override;
   6181  bool canRecoverOnBailout() const override { return true; }
   6182 
   6183  ALLOW_CLONE(MConcat)
   6184 };
   6185 
   6186 class MStringConvertCase : public MUnaryInstruction,
   6187                           public StringPolicy<0>::Data {
   6188 public:
   6189  enum Mode { LowerCase, UpperCase };
   6190 
   6191 private:
   6192  Mode mode_;
   6193 
   6194  MStringConvertCase(MDefinition* string, Mode mode)
   6195      : MUnaryInstruction(classOpcode, string), mode_(mode) {
   6196    setResultType(MIRType::String);
   6197    setMovable();
   6198  }
   6199 
   6200 public:
   6201  INSTRUCTION_HEADER(StringConvertCase)
   6202  TRIVIAL_NEW_WRAPPERS
   6203  NAMED_OPERANDS((0, string))
   6204 
   6205  MDefinition* foldsTo(TempAllocator& alloc) override;
   6206  bool congruentTo(const MDefinition* ins) const override {
   6207    return congruentIfOperandsEqual(ins) &&
   6208           ins->toStringConvertCase()->mode() == mode();
   6209  }
   6210  AliasSet getAliasSet() const override { return AliasSet::None(); }
   6211  bool possiblyCalls() const override { return true; }
   6212  Mode mode() const { return mode_; }
   6213 };
   6214 
   6215 class MCharCodeConvertCase : public MUnaryInstruction,
   6216                             public UnboxedInt32Policy<0>::Data {
   6217 public:
   6218  enum Mode { LowerCase, UpperCase };
   6219 
   6220 private:
   6221  Mode mode_;
   6222 
   6223  MCharCodeConvertCase(MDefinition* code, Mode mode)
   6224      : MUnaryInstruction(classOpcode, code), mode_(mode) {
   6225    setResultType(MIRType::String);
   6226    setMovable();
   6227  }
   6228 
   6229 public:
   6230  INSTRUCTION_HEADER(CharCodeConvertCase)
   6231  TRIVIAL_NEW_WRAPPERS
   6232  NAMED_OPERANDS((0, code))
   6233 
   6234  bool congruentTo(const MDefinition* ins) const override {
   6235    return congruentIfOperandsEqual(ins) &&
   6236           ins->toCharCodeConvertCase()->mode() == mode();
   6237  }
   6238  AliasSet getAliasSet() const override { return AliasSet::None(); }
   6239  Mode mode() const { return mode_; }
   6240 };
   6241 
   6242 // This is a 3 state flag used by FlagPhiInputsAsImplicitlyUsed to record and
   6243 // propagate the information about the consumers of a Phi instruction. This is
   6244 // then used to set ImplicitlyUsed flags on the inputs of such Phi instructions.
   6245 enum class PhiUsage : uint8_t { Unknown, Unused, Used };
   6246 
   6247 using PhiVector = Vector<MPhi*, 4, JitAllocPolicy>;
   6248 
   6249 class MPhi final : public MDefinition,
   6250                   public InlineListNode<MPhi>,
   6251                   public NoTypePolicy::Data {
   6252  using InputVector = js::Vector<MUse, 2, JitAllocPolicy>;
   6253  InputVector inputs_;
   6254 
   6255  // Important: if you add fields to this class, be sure to update its ::clone
   6256  // method accordingly.
   6257  TruncateKind truncateKind_;
   6258  bool triedToSpecialize_;
   6259  bool isIterator_;
   6260  bool canProduceFloat32_;
   6261  bool canConsumeFloat32_;
   6262  // Record the state of the data flow before any mutation made to the control
   6263  // flow, such that removing branches is properly accounted for.
   6264  PhiUsage usageAnalysis_;
   6265 
   6266 protected:
   6267  MUse* getUseFor(size_t index) override {
   6268    MOZ_ASSERT(index < numOperands());
   6269    return &inputs_[index];
   6270  }
   6271  const MUse* getUseFor(size_t index) const override { return &inputs_[index]; }
   6272 
   6273 public:
   6274  INSTRUCTION_HEADER_WITHOUT_TYPEPOLICY(Phi)
   6275  virtual const TypePolicy* typePolicy();
   6276  virtual MIRType typePolicySpecialization();
   6277 
   6278  MPhi(TempAllocator& alloc, MIRType resultType)
   6279      : MDefinition(classOpcode),
   6280        inputs_(alloc),
   6281        truncateKind_(TruncateKind::NoTruncate),
   6282        triedToSpecialize_(false),
   6283        isIterator_(false),
   6284        canProduceFloat32_(false),
   6285        canConsumeFloat32_(false),
   6286        usageAnalysis_(PhiUsage::Unknown) {
   6287    setResultType(resultType);
   6288  }
   6289 
   6290  static MPhi* New(TempAllocator& alloc, MIRType resultType = MIRType::Value) {
   6291    return new (alloc) MPhi(alloc, resultType);
   6292  }
   6293  static MPhi* New(TempAllocator::Fallible alloc,
   6294                   MIRType resultType = MIRType::Value) {
   6295    return new (alloc) MPhi(alloc.alloc, resultType);
   6296  }
   6297 
   6298  MPhi* clone(TempAllocator& alloc, const MDefinitionVector& inputs) const {
   6299    MOZ_ASSERT(inputs.length() == inputs_.length());
   6300    MPhi* phi = MPhi::New(alloc);
   6301    if (!phi || !phi->reserveLength(inputs.length())) {
   6302      return nullptr;
   6303    }
   6304    for (const auto& inp : inputs) {
   6305      phi->addInput(inp);
   6306    }
   6307    phi->truncateKind_ = truncateKind_;
   6308    phi->triedToSpecialize_ = triedToSpecialize_;
   6309    phi->isIterator_ = isIterator_;
   6310    phi->canProduceFloat32_ = canProduceFloat32_;
   6311    phi->canConsumeFloat32_ = canConsumeFloat32_;
   6312    phi->usageAnalysis_ = usageAnalysis_;
   6313    phi->setResultType(type());
   6314    return phi;
   6315  }
   6316 
   6317  void removeOperand(size_t index);
   6318  void removeAllOperands();
   6319 
   6320  MDefinition* getOperand(size_t index) const override {
   6321    return inputs_[index].producer();
   6322  }
   6323  size_t numOperands() const override { return inputs_.length(); }
   6324  size_t indexOf(const MUse* u) const final {
   6325    MOZ_ASSERT(u >= &inputs_[0]);
   6326    MOZ_ASSERT(u <= &inputs_[numOperands() - 1]);
   6327    return u - &inputs_[0];
   6328  }
   6329  void replaceOperand(size_t index, MDefinition* operand) final {
   6330    inputs_[index].replaceProducer(operand);
   6331  }
   6332  bool triedToSpecialize() const { return triedToSpecialize_; }
   6333  void specialize(MIRType type) {
   6334    triedToSpecialize_ = true;
   6335    setResultType(type);
   6336  }
   6337 
   6338 #ifdef DEBUG
   6339  // Assert that this is a phi in a loop header with a unique predecessor and
   6340  // a unique backedge.
   6341  void assertLoopPhi() const;
   6342 #else
   6343  void assertLoopPhi() const {}
   6344 #endif
   6345 
   6346  // Assuming this phi is in a loop header with a unique loop entry, return
   6347  // the phi operand along the loop entry.
   6348  MDefinition* getLoopPredecessorOperand() const;
   6349 
   6350  // Assuming this phi is in a loop header with a unique loop entry, return
   6351  // the phi operand along the loop backedge.
   6352  MDefinition* getLoopBackedgeOperand() const;
   6353 
   6354  // Whether this phi's type already includes information for def.
   6355  bool typeIncludes(MDefinition* def);
   6356 
   6357  // Mark all phis in |iterators|, and the phis they flow into, as having
   6358  // implicit uses.
   6359  [[nodiscard]] static bool markIteratorPhis(const PhiVector& iterators);
   6360 
   6361  // Initializes the operands vector to the given capacity,
   6362  // permitting use of addInput() instead of addInputSlow().
   6363  [[nodiscard]] bool reserveLength(size_t length) {
   6364    return inputs_.reserve(length);
   6365  }
   6366 
   6367  // Use only if capacity has been reserved by reserveLength
   6368  void addInput(MDefinition* ins) {
   6369    MOZ_ASSERT_IF(type() != MIRType::Value, ins->type() == type());
   6370    inputs_.infallibleEmplaceBack(ins, this);
   6371  }
   6372 
   6373  // Append a new input to the input vector.  May fail.
   6374  [[nodiscard]] bool addInputFallible(MDefinition* ins) {
   6375    MOZ_ASSERT_IF(type() != MIRType::Value, ins->type() == type());
   6376    return inputs_.emplaceBack(ins, this);
   6377  }
   6378 
   6379  // Appends a new input to the input vector. May perform reallocation.
   6380  // Prefer reserveLength() and addInput() instead, where possible.
   6381  [[nodiscard]] bool addInputSlow(MDefinition* ins) {
   6382    MOZ_ASSERT_IF(type() != MIRType::Value, ins->type() == type());
   6383    return inputs_.emplaceBack(ins, this);
   6384  }
   6385 
   6386  // Appends a new input to the input vector. Infallible because
   6387  // we know the inputs fits in the vector's inline storage.
   6388  void addInlineInput(MDefinition* ins) {
   6389    MOZ_ASSERT(inputs_.length() < InputVector::InlineLength);
   6390    MOZ_ALWAYS_TRUE(addInputSlow(ins));
   6391  }
   6392 
   6393  MDefinition* foldsTo(TempAllocator& alloc) override;
   6394  MDefinition* foldsTernary(TempAllocator& alloc);
   6395 
   6396  bool congruentTo(const MDefinition* ins) const override;
   6397 
   6398  // Mark this phi-node as having replaced all uses of |other|, as during GVN.
   6399  // For use when GVN eliminates phis which are not equivalent to one another.
   6400  void updateForReplacement(MPhi* other);
   6401 
   6402  bool isIterator() const { return isIterator_; }
   6403  void setIterator() { isIterator_ = true; }
   6404 
   6405  AliasSet getAliasSet() const override { return AliasSet::None(); }
   6406  void computeRange(TempAllocator& alloc) override;
   6407 
   6408  MDefinition* operandIfRedundant();
   6409 
   6410  bool canProduceFloat32() const override { return canProduceFloat32_; }
   6411 
   6412  void setCanProduceFloat32(bool can) { canProduceFloat32_ = can; }
   6413 
   6414  bool canConsumeFloat32(MUse* use) const override {
   6415    return canConsumeFloat32_;
   6416  }
   6417 
   6418  void setCanConsumeFloat32(bool can) { canConsumeFloat32_ = can; }
   6419 
   6420  TruncateKind operandTruncateKind(size_t index) const override;
   6421  bool canTruncate() const override;
   6422  void truncate(TruncateKind kind) override;
   6423 
   6424  PhiUsage getUsageAnalysis() const { return usageAnalysis_; }
   6425  void setUsageAnalysis(PhiUsage pu) {
   6426    MOZ_ASSERT(usageAnalysis_ == PhiUsage::Unknown);
   6427    usageAnalysis_ = pu;
   6428    MOZ_ASSERT(usageAnalysis_ != PhiUsage::Unknown);
   6429  }
   6430 
   6431  wasm::MaybeRefType computeWasmRefType() const override {
   6432    if (numOperands() == 0) {
   6433      return wasm::MaybeRefType();
   6434    }
   6435    wasm::MaybeRefType firstRefType = getOperand(0)->wasmRefType();
   6436    if (firstRefType.isNothing()) {
   6437      return wasm::MaybeRefType();
   6438    }
   6439 
   6440    wasm::RefType topTypeOfOperands = firstRefType.value();
   6441    for (size_t i = 1; i < numOperands(); i++) {
   6442      MDefinition* op = getOperand(i);
   6443      wasm::MaybeRefType opType = op->wasmRefType();
   6444      if (opType.isNothing()) {
   6445        return wasm::MaybeRefType();
   6446      }
   6447      topTypeOfOperands =
   6448          wasm::RefType::leastUpperBound(topTypeOfOperands, opType.value());
   6449    }
   6450    return wasm::MaybeRefType(topTypeOfOperands);
   6451  }
   6452 };
   6453 
   6454 // The goal of a Beta node is to split a def at a conditionally taken
   6455 // branch, so that uses dominated by it have a different name.
   6456 class MBeta : public MUnaryInstruction, public NoTypePolicy::Data {
   6457 private:
   6458  // This is the range induced by a comparison and branch in a preceding
   6459  // block. Note that this does not reflect any range constraints from
   6460  // the input value itself, so this value may differ from the range()
   6461  // range after it is computed.
   6462  const Range* comparison_;
   6463 
   6464  MBeta(MDefinition* val, const Range* comp)
   6465      : MUnaryInstruction(classOpcode, val), comparison_(comp) {
   6466    setResultType(val->type());
   6467  }
   6468 
   6469 public:
   6470  INSTRUCTION_HEADER(Beta)
   6471  TRIVIAL_NEW_WRAPPERS
   6472 
   6473 #ifdef JS_JITSPEW
   6474  void printOpcode(GenericPrinter& out) const override;
   6475 #endif
   6476 
   6477  AliasSet getAliasSet() const override { return AliasSet::None(); }
   6478 
   6479  void computeRange(TempAllocator& alloc) override;
   6480 };
   6481 
   6482 // If input evaluates to false (i.e. it's NaN, 0 or -0), 0 is returned, else the
   6483 // input is returned
   6484 class MNaNToZero : public MUnaryInstruction, public DoublePolicy<0>::Data {
   6485  bool operandIsNeverNaN_;
   6486  bool operandIsNeverNegativeZero_;
   6487 
   6488  explicit MNaNToZero(MDefinition* input)
   6489      : MUnaryInstruction(classOpcode, input),
   6490        operandIsNeverNaN_(false),
   6491        operandIsNeverNegativeZero_(false) {
   6492    setResultType(MIRType::Double);
   6493    setMovable();
   6494  }
   6495 
   6496 public:
   6497  INSTRUCTION_HEADER(NaNToZero)
   6498  TRIVIAL_NEW_WRAPPERS
   6499 
   6500  bool operandIsNeverNaN() const { return operandIsNeverNaN_; }
   6501 
   6502  bool operandIsNeverNegativeZero() const {
   6503    return operandIsNeverNegativeZero_;
   6504  }
   6505 
   6506  void collectRangeInfoPreTrunc() override;
   6507 
   6508  AliasSet getAliasSet() const override { return AliasSet::None(); }
   6509 
   6510  void computeRange(TempAllocator& alloc) override;
   6511 
   6512  bool writeRecoverData(CompactBufferWriter& writer) const override;
   6513  bool canRecoverOnBailout() const override { return true; }
   6514 
   6515  ALLOW_CLONE(MNaNToZero)
   6516 };
   6517 
   6518 // MIR representation of a Value on the OSR BaselineFrame.
   6519 // The Value is indexed off of OsrFrameReg.
   6520 class MOsrValue : public MUnaryInstruction, public NoTypePolicy::Data {
   6521 private:
   6522  ptrdiff_t frameOffset_;
   6523 
   6524  MOsrValue(MOsrEntry* entry, ptrdiff_t frameOffset)
   6525      : MUnaryInstruction(classOpcode, entry), frameOffset_(frameOffset) {
   6526    setResultType(MIRType::Value);
   6527  }
   6528 
   6529 public:
   6530  INSTRUCTION_HEADER(OsrValue)
   6531  TRIVIAL_NEW_WRAPPERS
   6532 
   6533  ptrdiff_t frameOffset() const { return frameOffset_; }
   6534 
   6535  MOsrEntry* entry() { return getOperand(0)->toOsrEntry(); }
   6536 
   6537  AliasSet getAliasSet() const override { return AliasSet::None(); }
   6538 };
   6539 
   6540 // MIR representation of a JSObject scope chain pointer on the OSR
   6541 // BaselineFrame. The pointer is indexed off of OsrFrameReg.
   6542 class MOsrEnvironmentChain : public MUnaryInstruction,
   6543                             public NoTypePolicy::Data {
   6544 private:
   6545  explicit MOsrEnvironmentChain(MOsrEntry* entry)
   6546      : MUnaryInstruction(classOpcode, entry) {
   6547    setResultType(MIRType::Object);
   6548  }
   6549 
   6550 public:
   6551  INSTRUCTION_HEADER(OsrEnvironmentChain)
   6552  TRIVIAL_NEW_WRAPPERS
   6553 
   6554  MOsrEntry* entry() { return getOperand(0)->toOsrEntry(); }
   6555 };
   6556 
   6557 // MIR representation of a JSObject ArgumentsObject pointer on the OSR
   6558 // BaselineFrame. The pointer is indexed off of OsrFrameReg.
   6559 class MOsrArgumentsObject : public MUnaryInstruction,
   6560                            public NoTypePolicy::Data {
   6561 private:
   6562  explicit MOsrArgumentsObject(MOsrEntry* entry)
   6563      : MUnaryInstruction(classOpcode, entry) {
   6564    setResultType(MIRType::Object);
   6565  }
   6566 
   6567 public:
   6568  INSTRUCTION_HEADER(OsrArgumentsObject)
   6569  TRIVIAL_NEW_WRAPPERS
   6570 
   6571  MOsrEntry* entry() { return getOperand(0)->toOsrEntry(); }
   6572 };
   6573 
   6574 // MIR representation of the return value on the OSR BaselineFrame.
   6575 // The Value is indexed off of OsrFrameReg.
   6576 class MOsrReturnValue : public MUnaryInstruction, public NoTypePolicy::Data {
   6577 private:
   6578  explicit MOsrReturnValue(MOsrEntry* entry)
   6579      : MUnaryInstruction(classOpcode, entry) {
   6580    setResultType(MIRType::Value);
   6581  }
   6582 
   6583 public:
   6584  INSTRUCTION_HEADER(OsrReturnValue)
   6585  TRIVIAL_NEW_WRAPPERS
   6586 
   6587  MOsrEntry* entry() { return getOperand(0)->toOsrEntry(); }
   6588 };
   6589 
   6590 class MBinaryCache : public MBinaryInstruction,
   6591                     public MixPolicy<BoxPolicy<0>, BoxPolicy<1>>::Data {
   6592  explicit MBinaryCache(MDefinition* left, MDefinition* right, MIRType resType)
   6593      : MBinaryInstruction(classOpcode, left, right) {
   6594    setResultType(resType);
   6595  }
   6596 
   6597 public:
   6598  INSTRUCTION_HEADER(BinaryCache)
   6599  TRIVIAL_NEW_WRAPPERS
   6600 };
   6601 
   6602 // Checks if a value is JS_UNINITIALIZED_LEXICAL, bailout out if so, leaving
   6603 // it to baseline to throw at the correct pc.
   6604 class MLexicalCheck : public MUnaryInstruction, public BoxPolicy<0>::Data {
   6605  explicit MLexicalCheck(MDefinition* input)
   6606      : MUnaryInstruction(classOpcode, input) {
   6607    setResultType(MIRType::Value);
   6608    setMovable();
   6609    setGuard();
   6610 
   6611    // If this instruction bails out, we will set a flag to prevent
   6612    // lexical checks in this script from being moved.
   6613    setBailoutKind(BailoutKind::UninitializedLexical);
   6614  }
   6615 
   6616 public:
   6617  INSTRUCTION_HEADER(LexicalCheck)
   6618  TRIVIAL_NEW_WRAPPERS
   6619 
   6620  AliasSet getAliasSet() const override { return AliasSet::None(); }
   6621 
   6622  bool congruentTo(const MDefinition* ins) const override {
   6623    return congruentIfOperandsEqual(ins);
   6624  }
   6625 };
   6626 
   6627 // Unconditionally throw a known error number.
   6628 class MThrowMsg : public MNullaryInstruction {
   6629  const ThrowMsgKind throwMsgKind_;
   6630 
   6631  explicit MThrowMsg(ThrowMsgKind throwMsgKind)
   6632      : MNullaryInstruction(classOpcode), throwMsgKind_(throwMsgKind) {
   6633    setGuard();
   6634    setResultType(MIRType::None);
   6635  }
   6636 
   6637 public:
   6638  INSTRUCTION_HEADER(ThrowMsg)
   6639  TRIVIAL_NEW_WRAPPERS
   6640 
   6641  ThrowMsgKind throwMsgKind() const { return throwMsgKind_; }
   6642 
   6643  AliasSet getAliasSet() const override {
   6644    return AliasSet::Store(AliasSet::ExceptionState);
   6645  }
   6646 };
   6647 
   6648 class MGetFirstDollarIndex : public MUnaryInstruction,
   6649                             public StringPolicy<0>::Data {
   6650  explicit MGetFirstDollarIndex(MDefinition* str)
   6651      : MUnaryInstruction(classOpcode, str) {
   6652    setResultType(MIRType::Int32);
   6653 
   6654    // Codegen assumes string length > 0. Don't allow LICM to move this
   6655    // before the .length > 1 check in RegExpReplace in RegExp.js.
   6656    MOZ_ASSERT(!isMovable());
   6657  }
   6658 
   6659 public:
   6660  INSTRUCTION_HEADER(GetFirstDollarIndex)
   6661  TRIVIAL_NEW_WRAPPERS
   6662  NAMED_OPERANDS((0, str))
   6663 
   6664  AliasSet getAliasSet() const override { return AliasSet::None(); }
   6665 
   6666  MDefinition* foldsTo(TempAllocator& alloc) override;
   6667 };
   6668 
   6669 class MStringReplace : public MTernaryInstruction,
   6670                       public MixPolicy<StringPolicy<0>, StringPolicy<1>,
   6671                                        StringPolicy<2>>::Data {
   6672 private:
   6673  bool isFlatReplacement_;
   6674 
   6675  MStringReplace(MDefinition* string, MDefinition* pattern,
   6676                 MDefinition* replacement)
   6677      : MTernaryInstruction(classOpcode, string, pattern, replacement),
   6678        isFlatReplacement_(false) {
   6679    setMovable();
   6680    setResultType(MIRType::String);
   6681  }
   6682 
   6683 public:
   6684  INSTRUCTION_HEADER(StringReplace)
   6685  TRIVIAL_NEW_WRAPPERS
   6686  NAMED_OPERANDS((0, string), (1, pattern), (2, replacement))
   6687 
   6688  void setFlatReplacement() {
   6689    MOZ_ASSERT(!isFlatReplacement_);
   6690    isFlatReplacement_ = true;
   6691  }
   6692 
   6693  bool isFlatReplacement() const { return isFlatReplacement_; }
   6694 
   6695  bool congruentTo(const MDefinition* ins) const override {
   6696    if (!ins->isStringReplace()) {
   6697      return false;
   6698    }
   6699    if (isFlatReplacement_ != ins->toStringReplace()->isFlatReplacement()) {
   6700      return false;
   6701    }
   6702    return congruentIfOperandsEqual(ins);
   6703  }
   6704 
   6705  AliasSet getAliasSet() const override { return AliasSet::None(); }
   6706 
   6707  [[nodiscard]] bool writeRecoverData(
   6708      CompactBufferWriter& writer) const override;
   6709  bool canRecoverOnBailout() const override {
   6710    if (isFlatReplacement_) {
   6711      MOZ_ASSERT(!pattern()->isRegExp());
   6712      return true;
   6713    }
   6714    return false;
   6715  }
   6716 
   6717  bool possiblyCalls() const override { return true; }
   6718 };
   6719 
   6720 class MLambda : public MBinaryInstruction, public SingleObjectPolicy::Data {
   6721  MLambda(MDefinition* envChain, MConstant* cst, gc::Heap initialHeap)
   6722      : MBinaryInstruction(classOpcode, envChain, cst),
   6723        initialHeap_(initialHeap) {
   6724    setResultType(MIRType::Object);
   6725  }
   6726 
   6727  // Heap where the lambda should be allocated.
   6728  gc::Heap initialHeap_;
   6729 
   6730 public:
   6731  INSTRUCTION_HEADER(Lambda)
   6732  TRIVIAL_NEW_WRAPPERS
   6733  NAMED_OPERANDS((0, environmentChain))
   6734 
   6735  MConstant* functionOperand() const { return getOperand(1)->toConstant(); }
   6736  JSFunction* templateFunction() const {
   6737    return &functionOperand()->toObject().as<JSFunction>();
   6738  }
   6739 
   6740  gc::Heap initialHeap() const { return initialHeap_; }
   6741 
   6742  [[nodiscard]] bool writeRecoverData(
   6743      CompactBufferWriter& writer) const override;
   6744  bool canRecoverOnBailout() const override { return true; }
   6745 };
   6746 
   6747 class MFunctionWithProto : public MTernaryInstruction,
   6748                           public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>,
   6749                                            ObjectPolicy<2>>::Data {
   6750  CompilerFunction fun_;
   6751 
   6752  MFunctionWithProto(MDefinition* envChain, MDefinition* prototype,
   6753                     MConstant* cst)
   6754      : MTernaryInstruction(classOpcode, envChain, prototype, cst),
   6755        fun_(&cst->toObject().as<JSFunction>()) {
   6756    setResultType(MIRType::Object);
   6757  }
   6758 
   6759 public:
   6760  INSTRUCTION_HEADER(FunctionWithProto)
   6761  TRIVIAL_NEW_WRAPPERS
   6762  NAMED_OPERANDS((0, environmentChain), (1, prototype))
   6763 
   6764  MConstant* functionOperand() const { return getOperand(2)->toConstant(); }
   6765  JSFunction* function() const { return fun_; }
   6766  [[nodiscard]] bool writeRecoverData(
   6767      CompactBufferWriter& writer) const override;
   6768  bool canRecoverOnBailout() const override { return true; }
   6769 
   6770  bool possiblyCalls() const override { return true; }
   6771 };
   6772 
   6773 class MGetNextEntryForIterator
   6774    : public MBinaryInstruction,
   6775      public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>>::Data {
   6776 public:
   6777  enum Mode { Map, Set };
   6778 
   6779 private:
   6780  Mode mode_;
   6781 
   6782  explicit MGetNextEntryForIterator(MDefinition* iter, MDefinition* result,
   6783                                    Mode mode)
   6784      : MBinaryInstruction(classOpcode, iter, result), mode_(mode) {
   6785    setResultType(MIRType::Boolean);
   6786  }
   6787 
   6788 public:
   6789  INSTRUCTION_HEADER(GetNextEntryForIterator)
   6790  TRIVIAL_NEW_WRAPPERS
   6791  NAMED_OPERANDS((0, iter), (1, result))
   6792 
   6793  Mode mode() const { return mode_; }
   6794 };
   6795 
   6796 // Convert a Double into an IntPtr value for accessing a TypedArray or DataView
   6797 // element. If the input is non-finite, not an integer, negative, or outside the
   6798 // IntPtr range, either bails out or produces a value which is known to trigger
   6799 // an out-of-bounds access (this depends on the supportOOB flag).
   6800 class MGuardNumberToIntPtrIndex : public MUnaryInstruction,
   6801                                  public DoublePolicy<0>::Data {
   6802  // If true, produce an out-of-bounds index for non-IntPtr doubles instead of
   6803  // bailing out.
   6804  const bool supportOOB_;
   6805 
   6806  MGuardNumberToIntPtrIndex(MDefinition* def, bool supportOOB)
   6807      : MUnaryInstruction(classOpcode, def), supportOOB_(supportOOB) {
   6808    MOZ_ASSERT(IsNumberType(def->type()));
   6809    setResultType(MIRType::IntPtr);
   6810    setMovable();
   6811    if (!supportOOB) {
   6812      setGuard();
   6813    }
   6814  }
   6815 
   6816 public:
   6817  INSTRUCTION_HEADER(GuardNumberToIntPtrIndex)
   6818  TRIVIAL_NEW_WRAPPERS
   6819 
   6820  bool supportOOB() const { return supportOOB_; }
   6821 
   6822  MDefinition* foldsTo(TempAllocator& alloc) override;
   6823 
   6824  bool congruentTo(const MDefinition* ins) const override {
   6825    if (!ins->isGuardNumberToIntPtrIndex()) {
   6826      return false;
   6827    }
   6828    if (ins->toGuardNumberToIntPtrIndex()->supportOOB() != supportOOB()) {
   6829      return false;
   6830    }
   6831    return congruentIfOperandsEqual(ins);
   6832  }
   6833 
   6834  AliasSet getAliasSet() const override { return AliasSet::None(); }
   6835 
   6836  ALLOW_CLONE(MGuardNumberToIntPtrIndex)
   6837 };
   6838 
   6839 // Perform !-operation
   6840 class MNot : public MUnaryInstruction, public TestPolicy::Data {
   6841  bool operandIsNeverNaN_;
   6842  TypeDataList observedTypes_;
   6843 
   6844  explicit MNot(MDefinition* input)
   6845      : MUnaryInstruction(classOpcode, input), operandIsNeverNaN_(false) {
   6846    setResultType(MIRType::Boolean);
   6847    setMovable();
   6848  }
   6849 
   6850 public:
   6851  static MNot* NewInt32(TempAllocator& alloc, MDefinition* input) {
   6852    MOZ_ASSERT(input->type() == MIRType::Int32 ||
   6853               input->type() == MIRType::Int64);
   6854    auto* ins = new (alloc) MNot(input);
   6855    ins->setResultType(MIRType::Int32);
   6856    return ins;
   6857  }
   6858 
   6859  INSTRUCTION_HEADER(Not)
   6860  TRIVIAL_NEW_WRAPPERS
   6861 
   6862  void setObservedTypes(const TypeDataList& observed) {
   6863    observedTypes_ = observed;
   6864  }
   6865  const TypeDataList& observedTypes() const { return observedTypes_; }
   6866 
   6867  MDefinition* foldsTo(TempAllocator& alloc) override;
   6868 
   6869  bool operandIsNeverNaN() const { return operandIsNeverNaN_; }
   6870 
   6871  virtual AliasSet getAliasSet() const override { return AliasSet::None(); }
   6872  void collectRangeInfoPreTrunc() override;
   6873 
   6874  void trySpecializeFloat32(TempAllocator& alloc) override;
   6875  bool isFloat32Commutative() const override { return true; }
   6876 #ifdef DEBUG
   6877  bool isConsistentFloat32Use(MUse* use) const override { return true; }
   6878 #endif
   6879  bool congruentTo(const MDefinition* ins) const override {
   6880    return congruentIfOperandsEqual(ins);
   6881  }
   6882  [[nodiscard]] bool writeRecoverData(
   6883      CompactBufferWriter& writer) const override;
   6884  bool canRecoverOnBailout() const override {
   6885    return type() == MIRType::Boolean;
   6886  }
   6887 
   6888  ALLOW_CLONE(MNot)
   6889 };
   6890 
   6891 // Bailout if index + minimum < 0 or index + maximum >= length. The length used
   6892 // in a bounds check must not be negative, or the wrong result may be computed
   6893 // (unsigned comparisons may be used).
   6894 class MBoundsCheck
   6895    : public MBinaryInstruction,
   6896      public MixPolicy<Int32OrIntPtrPolicy<0>, Int32OrIntPtrPolicy<1>>::Data {
   6897  // Range over which to perform the bounds check, may be modified by GVN.
   6898  int32_t minimum_;
   6899  int32_t maximum_;
   6900  bool fallible_;
   6901 
   6902  MBoundsCheck(MDefinition* index, MDefinition* length)
   6903      : MBinaryInstruction(classOpcode, index, length),
   6904        minimum_(0),
   6905        maximum_(0),
   6906        fallible_(true) {
   6907    setGuard();
   6908    setMovable();
   6909    MOZ_ASSERT(index->type() == MIRType::Int32 ||
   6910               index->type() == MIRType::IntPtr);
   6911    MOZ_ASSERT(index->type() == length->type());
   6912 
   6913    // Returns the checked index.
   6914    setResultType(index->type());
   6915  }
   6916 
   6917 public:
   6918  INSTRUCTION_HEADER(BoundsCheck)
   6919  TRIVIAL_NEW_WRAPPERS
   6920  NAMED_OPERANDS((0, index), (1, length))
   6921 
   6922  int32_t minimum() const { return minimum_; }
   6923  void setMinimum(int32_t n) {
   6924    MOZ_ASSERT(fallible_);
   6925    minimum_ = n;
   6926  }
   6927  int32_t maximum() const { return maximum_; }
   6928  void setMaximum(int32_t n) {
   6929    MOZ_ASSERT(fallible_);
   6930    maximum_ = n;
   6931  }
   6932  MDefinition* foldsTo(TempAllocator& alloc) override;
   6933  bool congruentTo(const MDefinition* ins) const override {
   6934    if (!ins->isBoundsCheck()) {
   6935      return false;
   6936    }
   6937    const MBoundsCheck* other = ins->toBoundsCheck();
   6938    if (minimum() != other->minimum() || maximum() != other->maximum()) {
   6939      return false;
   6940    }
   6941    if (fallible() != other->fallible()) {
   6942      return false;
   6943    }
   6944    return congruentIfOperandsEqual(other);
   6945  }
   6946  virtual AliasSet getAliasSet() const override { return AliasSet::None(); }
   6947  void computeRange(TempAllocator& alloc) override;
   6948  bool fallible() const { return fallible_; }
   6949  void collectRangeInfoPreTrunc() override;
   6950 
   6951  ALLOW_CLONE(MBoundsCheck)
   6952 };
   6953 
   6954 // Bailout if index < minimum.
   6955 class MBoundsCheckLower : public MUnaryInstruction,
   6956                          public UnboxedInt32Policy<0>::Data {
   6957  int32_t minimum_;
   6958  bool fallible_;
   6959 
   6960  explicit MBoundsCheckLower(MDefinition* index)
   6961      : MUnaryInstruction(classOpcode, index), minimum_(0), fallible_(true) {
   6962    setGuard();
   6963    setMovable();
   6964    MOZ_ASSERT(index->type() == MIRType::Int32);
   6965  }
   6966 
   6967 public:
   6968  INSTRUCTION_HEADER(BoundsCheckLower)
   6969  TRIVIAL_NEW_WRAPPERS
   6970  NAMED_OPERANDS((0, index))
   6971 
   6972  int32_t minimum() const { return minimum_; }
   6973  void setMinimum(int32_t n) { minimum_ = n; }
   6974  AliasSet getAliasSet() const override { return AliasSet::None(); }
   6975  bool fallible() const { return fallible_; }
   6976  void collectRangeInfoPreTrunc() override;
   6977 };
   6978 
   6979 class MSpectreMaskIndex
   6980    : public MBinaryInstruction,
   6981      public MixPolicy<Int32OrIntPtrPolicy<0>, Int32OrIntPtrPolicy<1>>::Data {
   6982  MSpectreMaskIndex(MDefinition* index, MDefinition* length)
   6983      : MBinaryInstruction(classOpcode, index, length) {
   6984    // Note: this instruction does not need setGuard(): if there are no uses
   6985    // it's fine for DCE to eliminate this instruction.
   6986    setMovable();
   6987    MOZ_ASSERT(index->type() == MIRType::Int32 ||
   6988               index->type() == MIRType::IntPtr);
   6989    MOZ_ASSERT(index->type() == length->type());
   6990 
   6991    // Returns the masked index.
   6992    setResultType(index->type());
   6993  }
   6994 
   6995 public:
   6996  INSTRUCTION_HEADER(SpectreMaskIndex)
   6997  TRIVIAL_NEW_WRAPPERS
   6998  NAMED_OPERANDS((0, index), (1, length))
   6999 
   7000  bool congruentTo(const MDefinition* ins) const override {
   7001    return congruentIfOperandsEqual(ins);
   7002  }
   7003  virtual AliasSet getAliasSet() const override { return AliasSet::None(); }
   7004  void computeRange(TempAllocator& alloc) override;
   7005 
   7006  ALLOW_CLONE(MSpectreMaskIndex)
   7007 };
   7008 
   7009 // Load a value from a dense array's element vector. Bails out if the element is
   7010 // a hole.
   7011 class MLoadElement : public MBinaryInstruction, public NoTypePolicy::Data {
   7012  bool needsHoleCheck_;
   7013 
   7014  MLoadElement(MDefinition* elements, MDefinition* index, bool needsHoleCheck)
   7015      : MBinaryInstruction(classOpcode, elements, index),
   7016        needsHoleCheck_(needsHoleCheck) {
   7017    if (needsHoleCheck) {
   7018      // Uses may be optimized away based on this instruction's result
   7019      // type. This means it's invalid to DCE this instruction, as we
   7020      // have to invalidate when we read a hole.
   7021      setGuard();
   7022    }
   7023    setResultType(MIRType::Value);
   7024    setMovable();
   7025    MOZ_ASSERT(elements->type() == MIRType::Elements);
   7026    MOZ_ASSERT(index->type() == MIRType::Int32);
   7027  }
   7028 
   7029 public:
   7030  INSTRUCTION_HEADER(LoadElement)
   7031  TRIVIAL_NEW_WRAPPERS
   7032  NAMED_OPERANDS((0, elements), (1, index))
   7033 
   7034  bool needsHoleCheck() const { return needsHoleCheck_; }
   7035 
   7036  bool congruentTo(const MDefinition* ins) const override {
   7037    return congruentIfOperandsEqual(ins);
   7038  }
   7039  AliasType mightAlias(const MDefinition* store) const override;
   7040  MDefinition* foldsTo(TempAllocator& alloc) override;
   7041  AliasSet getAliasSet() const override {
   7042    return AliasSet::Load(AliasSet::Element);
   7043  }
   7044 
   7045  ALLOW_CLONE(MLoadElement)
   7046 };
   7047 
   7048 class MLoadElementAndUnbox : public MBinaryInstruction,
   7049                             public NoTypePolicy::Data {
   7050  MUnbox::Mode mode_;
   7051 
   7052  MLoadElementAndUnbox(MDefinition* elements, MDefinition* index,
   7053                       MUnbox::Mode mode, MIRType type)
   7054      : MBinaryInstruction(classOpcode, elements, index), mode_(mode) {
   7055    setResultType(type);
   7056    setMovable();
   7057    if (mode_ == MUnbox::Fallible) {
   7058      setGuard();
   7059    }
   7060  }
   7061 
   7062 public:
   7063  INSTRUCTION_HEADER(LoadElementAndUnbox)
   7064  TRIVIAL_NEW_WRAPPERS
   7065  NAMED_OPERANDS((0, elements), (1, index))
   7066 
   7067  MUnbox::Mode mode() const { return mode_; }
   7068  bool fallible() const { return mode_ != MUnbox::Infallible; }
   7069 
   7070  bool congruentTo(const MDefinition* ins) const override {
   7071    if (!ins->isLoadElementAndUnbox() ||
   7072        mode() != ins->toLoadElementAndUnbox()->mode()) {
   7073      return false;
   7074    }
   7075    return congruentIfOperandsEqual(ins);
   7076  }
   7077 
   7078  AliasSet getAliasSet() const override {
   7079    return AliasSet::Load(AliasSet::Element);
   7080  }
   7081 
   7082  ALLOW_CLONE(MLoadElementAndUnbox);
   7083 };
   7084 
   7085 // Load a value from the elements vector of a native object. If the index is
   7086 // out-of-bounds, or the indexed slot has a hole, undefined is returned instead.
   7087 class MLoadElementHole : public MTernaryInstruction, public NoTypePolicy::Data {
   7088  bool needsNegativeIntCheck_ = true;
   7089 
   7090  MLoadElementHole(MDefinition* elements, MDefinition* index,
   7091                   MDefinition* initLength)
   7092      : MTernaryInstruction(classOpcode, elements, index, initLength) {
   7093    setResultType(MIRType::Value);
   7094    setMovable();
   7095 
   7096    // Set the guard flag to make sure we bail when we see a negative
   7097    // index. We can clear this flag (and needsNegativeIntCheck_) in
   7098    // collectRangeInfoPreTrunc.
   7099    setGuard();
   7100 
   7101    MOZ_ASSERT(elements->type() == MIRType::Elements);
   7102    MOZ_ASSERT(index->type() == MIRType::Int32);
   7103    MOZ_ASSERT(initLength->type() == MIRType::Int32);
   7104  }
   7105 
   7106 public:
   7107  INSTRUCTION_HEADER(LoadElementHole)
   7108  TRIVIAL_NEW_WRAPPERS
   7109  NAMED_OPERANDS((0, elements), (1, index), (2, initLength))
   7110 
   7111  bool needsNegativeIntCheck() const { return needsNegativeIntCheck_; }
   7112  bool congruentTo(const MDefinition* ins) const override {
   7113    if (!ins->isLoadElementHole()) {
   7114      return false;
   7115    }
   7116    const MLoadElementHole* other = ins->toLoadElementHole();
   7117    if (needsNegativeIntCheck() != other->needsNegativeIntCheck()) {
   7118      return false;
   7119    }
   7120    return congruentIfOperandsEqual(other);
   7121  }
   7122  AliasSet getAliasSet() const override {
   7123    return AliasSet::Load(AliasSet::Element);
   7124  }
   7125  void collectRangeInfoPreTrunc() override;
   7126 
   7127  ALLOW_CLONE(MLoadElementHole)
   7128 };
   7129 
   7130 // Store a value to a dense array slots vector.
   7131 class MStoreElement : public MTernaryInstruction,
   7132                      public NoFloatPolicy<2>::Data {
   7133  bool needsHoleCheck_;
   7134  bool needsBarrier_;
   7135 
   7136  MStoreElement(MDefinition* elements, MDefinition* index, MDefinition* value,
   7137                bool needsHoleCheck, bool needsBarrier)
   7138      : MTernaryInstruction(classOpcode, elements, index, value) {
   7139    needsHoleCheck_ = needsHoleCheck;
   7140    needsBarrier_ = needsBarrier;
   7141    MOZ_ASSERT(elements->type() == MIRType::Elements);
   7142    MOZ_ASSERT(index->type() == MIRType::Int32);
   7143    MOZ_ASSERT(value->type() != MIRType::MagicHole);
   7144  }
   7145 
   7146 public:
   7147  INSTRUCTION_HEADER(StoreElement)
   7148  TRIVIAL_NEW_WRAPPERS
   7149  NAMED_OPERANDS((0, elements), (1, index), (2, value))
   7150 
   7151  static MStoreElement* NewUnbarriered(TempAllocator& alloc,
   7152                                       MDefinition* elements,
   7153                                       MDefinition* index, MDefinition* value,
   7154                                       bool needsHoleCheck) {
   7155    return new (alloc)
   7156        MStoreElement(elements, index, value, needsHoleCheck, false);
   7157  }
   7158 
   7159  static MStoreElement* NewBarriered(TempAllocator& alloc,
   7160                                     MDefinition* elements, MDefinition* index,
   7161                                     MDefinition* value, bool needsHoleCheck) {
   7162    return new (alloc)
   7163        MStoreElement(elements, index, value, needsHoleCheck, true);
   7164  }
   7165 
   7166  AliasSet getAliasSet() const override {
   7167    return AliasSet::Store(AliasSet::Element);
   7168  }
   7169  bool needsBarrier() const { return needsBarrier_; }
   7170  bool needsHoleCheck() const { return needsHoleCheck_; }
   7171  bool fallible() const { return needsHoleCheck(); }
   7172 
   7173  ALLOW_CLONE(MStoreElement)
   7174 };
   7175 
   7176 // Stores MagicValue(JS_ELEMENTS_HOLE) and marks the elements as non-packed.
   7177 class MStoreHoleValueElement : public MBinaryInstruction,
   7178                               public NoTypePolicy::Data {
   7179  MStoreHoleValueElement(MDefinition* elements, MDefinition* index)
   7180      : MBinaryInstruction(classOpcode, elements, index) {
   7181    MOZ_ASSERT(elements->type() == MIRType::Elements);
   7182    MOZ_ASSERT(index->type() == MIRType::Int32);
   7183  }
   7184 
   7185 public:
   7186  INSTRUCTION_HEADER(StoreHoleValueElement)
   7187  TRIVIAL_NEW_WRAPPERS
   7188  NAMED_OPERANDS((0, elements), (1, index))
   7189 
   7190  AliasSet getAliasSet() const override {
   7191    return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
   7192  }
   7193 
   7194  ALLOW_CLONE(MStoreHoleValueElement)
   7195 };
   7196 
   7197 // Like MStoreElement, but also supports index == initialized length. The
   7198 // downside is that we cannot hoist the elements vector and bounds check, since
   7199 // this instruction may update the (initialized) length and reallocate the
   7200 // elements vector.
   7201 class MStoreElementHole
   7202    : public MQuaternaryInstruction,
   7203      public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3>>::Data {
   7204  MStoreElementHole(MDefinition* object, MDefinition* elements,
   7205                    MDefinition* index, MDefinition* value)
   7206      : MQuaternaryInstruction(classOpcode, object, elements, index, value) {
   7207    MOZ_ASSERT(elements->type() == MIRType::Elements);
   7208    MOZ_ASSERT(index->type() == MIRType::Int32);
   7209    MOZ_ASSERT(value->type() != MIRType::MagicHole);
   7210  }
   7211 
   7212 public:
   7213  INSTRUCTION_HEADER(StoreElementHole)
   7214  TRIVIAL_NEW_WRAPPERS
   7215  NAMED_OPERANDS((0, object), (1, elements), (2, index), (3, value))
   7216 
   7217  AliasSet getAliasSet() const override {
   7218    // StoreElementHole can update the initialized length, the array length
   7219    // or reallocate obj->elements.
   7220    return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element);
   7221  }
   7222 
   7223  ALLOW_CLONE(MStoreElementHole)
   7224 };
   7225 
   7226 // Array.prototype.pop or Array.prototype.shift on a dense array.
   7227 class MArrayPopShift : public MUnaryInstruction,
   7228                       public SingleObjectPolicy::Data {
   7229 public:
   7230  enum Mode { Pop, Shift };
   7231 
   7232 private:
   7233  Mode mode_;
   7234 
   7235  MArrayPopShift(MDefinition* object, Mode mode)
   7236      : MUnaryInstruction(classOpcode, object), mode_(mode) {
   7237    setResultType(MIRType::Value);
   7238  }
   7239 
   7240 public:
   7241  INSTRUCTION_HEADER(ArrayPopShift)
   7242  TRIVIAL_NEW_WRAPPERS
   7243  NAMED_OPERANDS((0, object))
   7244 
   7245  bool mode() const { return mode_; }
   7246  AliasSet getAliasSet() const override {
   7247    return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element);
   7248  }
   7249 
   7250  ALLOW_CLONE(MArrayPopShift)
   7251 };
   7252 
   7253 // Load an unboxed scalar value from an array buffer view or other object.
   7254 class MLoadUnboxedScalar : public MBinaryInstruction,
   7255                           public NoTypePolicy::Data {
   7256  Scalar::Type storageType_;
   7257  MemoryBarrierRequirement requiresBarrier_;
   7258 
   7259  MLoadUnboxedScalar(MDefinition* elements, MDefinition* index,
   7260                     Scalar::Type storageType,
   7261                     MemoryBarrierRequirement requiresBarrier =
   7262                         MemoryBarrierRequirement::NotRequired)
   7263      : MBinaryInstruction(classOpcode, elements, index),
   7264        storageType_(storageType),
   7265        requiresBarrier_(requiresBarrier) {
   7266    setResultType(MIRType::Value);
   7267    if (requiresBarrier_ == MemoryBarrierRequirement::Required) {
   7268      setGuard();  // Not removable or movable
   7269    } else {
   7270      setMovable();
   7271    }
   7272    MOZ_ASSERT(elements->type() == MIRType::Elements);
   7273    MOZ_ASSERT(index->type() == MIRType::IntPtr);
   7274    MOZ_ASSERT(storageType >= 0 && storageType < Scalar::MaxTypedArrayViewType);
   7275  }
   7276 
   7277 public:
   7278  INSTRUCTION_HEADER(LoadUnboxedScalar)
   7279  TRIVIAL_NEW_WRAPPERS
   7280  NAMED_OPERANDS((0, elements), (1, index))
   7281 
   7282  Scalar::Type storageType() const { return storageType_; }
   7283  bool fallible() const {
   7284    // Bailout if the result does not fit in an int32.
   7285    return storageType_ == Scalar::Uint32 && type() == MIRType::Int32;
   7286  }
   7287  auto requiresMemoryBarrier() const { return requiresBarrier_; }
   7288  AliasSet getAliasSet() const override {
   7289    // When a barrier is needed make the instruction effectful by
   7290    // giving it a "store" effect.
   7291    if (requiresBarrier_ == MemoryBarrierRequirement::Required) {
   7292      return AliasSet::Store(AliasSet::UnboxedElement);
   7293    }
   7294    return AliasSet::Load(AliasSet::UnboxedElement);
   7295  }
   7296 
   7297  bool congruentTo(const MDefinition* ins) const override {
   7298    if (requiresBarrier_ == MemoryBarrierRequirement::Required) {
   7299      return false;
   7300    }
   7301    if (!ins->isLoadUnboxedScalar()) {
   7302      return false;
   7303    }
   7304    const MLoadUnboxedScalar* other = ins->toLoadUnboxedScalar();
   7305    if (storageType_ != other->storageType_) {
   7306      return false;
   7307    }
   7308    return congruentIfOperandsEqual(other);
   7309  }
   7310 
   7311 #ifdef JS_JITSPEW
   7312  void printOpcode(GenericPrinter& out) const override;
   7313 #endif
   7314 
   7315  void computeRange(TempAllocator& alloc) override;
   7316 
   7317  bool canProduceFloat32() const override {
   7318    return storageType_ == Scalar::Float32 || storageType_ == Scalar::Float16;
   7319  }
   7320 
   7321  ALLOW_CLONE(MLoadUnboxedScalar)
   7322 };
   7323 
   7324 // Load an unboxed scalar value from a dataview object.
   7325 class MLoadDataViewElement : public MTernaryInstruction,
   7326                             public NoTypePolicy::Data {
   7327  Scalar::Type storageType_;
   7328 
   7329  MLoadDataViewElement(MDefinition* elements, MDefinition* index,
   7330                       MDefinition* littleEndian, Scalar::Type storageType)
   7331      : MTernaryInstruction(classOpcode, elements, index, littleEndian),
   7332        storageType_(storageType) {
   7333    setResultType(MIRType::Value);
   7334    setMovable();
   7335    MOZ_ASSERT(elements->type() == MIRType::Elements);
   7336    MOZ_ASSERT(index->type() == MIRType::IntPtr);
   7337    MOZ_ASSERT(littleEndian->type() == MIRType::Boolean);
   7338    MOZ_ASSERT(storageType >= 0 && storageType < Scalar::MaxTypedArrayViewType);
   7339    MOZ_ASSERT(Scalar::byteSize(storageType) > 1);
   7340  }
   7341 
   7342 public:
   7343  INSTRUCTION_HEADER(LoadDataViewElement)
   7344  TRIVIAL_NEW_WRAPPERS
   7345  NAMED_OPERANDS((0, elements), (1, index), (2, littleEndian))
   7346 
   7347  Scalar::Type storageType() const { return storageType_; }
   7348  bool fallible() const {
   7349    // Bailout if the result does not fit in an int32.
   7350    return storageType_ == Scalar::Uint32 && type() == MIRType::Int32;
   7351  }
   7352  AliasSet getAliasSet() const override {
   7353    return AliasSet::Load(AliasSet::UnboxedElement);
   7354  }
   7355 
   7356  bool congruentTo(const MDefinition* ins) const override {
   7357    if (!ins->isLoadDataViewElement()) {
   7358      return false;
   7359    }
   7360    const MLoadDataViewElement* other = ins->toLoadDataViewElement();
   7361    if (storageType_ != other->storageType_) {
   7362      return false;
   7363    }
   7364    return congruentIfOperandsEqual(other);
   7365  }
   7366 
   7367 #ifdef JS_JITSPEW
   7368  void printOpcode(GenericPrinter& out) const override;
   7369 #endif
   7370 
   7371  void computeRange(TempAllocator& alloc) override;
   7372 
   7373  bool canProduceFloat32() const override {
   7374    return storageType_ == Scalar::Float32 || storageType_ == Scalar::Float16;
   7375  }
   7376 
   7377  ALLOW_CLONE(MLoadDataViewElement)
   7378 };
   7379 
   7380 // Load a value from a typed array. Out-of-bounds accesses are handled in-line.
   7381 class MLoadTypedArrayElementHole : public MTernaryInstruction,
   7382                                   public NoTypePolicy::Data {
   7383  Scalar::Type arrayType_;
   7384  bool forceDouble_;
   7385 
   7386  MLoadTypedArrayElementHole(MDefinition* elements, MDefinition* index,
   7387                             MDefinition* length, Scalar::Type arrayType,
   7388                             bool forceDouble)
   7389      : MTernaryInstruction(classOpcode, elements, index, length),
   7390        arrayType_(arrayType),
   7391        forceDouble_(forceDouble) {
   7392    setResultType(MIRType::Value);
   7393    setMovable();
   7394    MOZ_ASSERT(elements->type() == MIRType::Elements);
   7395    MOZ_ASSERT(index->type() == MIRType::IntPtr);
   7396    MOZ_ASSERT(length->type() == MIRType::IntPtr);
   7397    MOZ_ASSERT(arrayType >= 0 && arrayType < Scalar::MaxTypedArrayViewType);
   7398  }
   7399 
   7400 public:
   7401  INSTRUCTION_HEADER(LoadTypedArrayElementHole)
   7402  TRIVIAL_NEW_WRAPPERS
   7403  NAMED_OPERANDS((0, elements), (1, index), (2, length))
   7404 
   7405  Scalar::Type arrayType() const { return arrayType_; }
   7406  bool forceDouble() const { return forceDouble_; }
   7407  bool fallible() const {
   7408    return arrayType_ == Scalar::Uint32 && !forceDouble_;
   7409  }
   7410  bool congruentTo(const MDefinition* ins) const override {
   7411    if (!ins->isLoadTypedArrayElementHole()) {
   7412      return false;
   7413    }
   7414    const MLoadTypedArrayElementHole* other =
   7415        ins->toLoadTypedArrayElementHole();
   7416    if (arrayType() != other->arrayType()) {
   7417      return false;
   7418    }
   7419    if (forceDouble() != other->forceDouble()) {
   7420      return false;
   7421    }
   7422    return congruentIfOperandsEqual(other);
   7423  }
   7424  AliasSet getAliasSet() const override {
   7425    return AliasSet::Load(AliasSet::UnboxedElement);
   7426  }
   7427  bool canProduceFloat32() const override {
   7428    return arrayType_ == Scalar::Float32 || arrayType_ == Scalar::Float16;
   7429  }
   7430 
   7431  ALLOW_CLONE(MLoadTypedArrayElementHole)
   7432 };
   7433 
   7434 // Base class for MIR ops that write unboxed scalar values.
   7435 class StoreUnboxedScalarBase {
   7436  Scalar::Type writeType_;
   7437 
   7438 protected:
   7439  explicit StoreUnboxedScalarBase(Scalar::Type writeType)
   7440      : writeType_(writeType) {
   7441    MOZ_ASSERT(isIntegerWrite() || isFloatWrite() || isBigIntWrite());
   7442  }
   7443 
   7444 public:
   7445  Scalar::Type writeType() const { return writeType_; }
   7446  bool isByteWrite() const {
   7447    return writeType_ == Scalar::Int8 || writeType_ == Scalar::Uint8 ||
   7448           writeType_ == Scalar::Uint8Clamped;
   7449  }
   7450  bool isIntegerWrite() const {
   7451    return isByteWrite() || writeType_ == Scalar::Int16 ||
   7452           writeType_ == Scalar::Uint16 || writeType_ == Scalar::Int32 ||
   7453           writeType_ == Scalar::Uint32;
   7454  }
   7455  bool isFloatWrite() const {
   7456    return writeType_ == Scalar::Float16 || writeType_ == Scalar::Float32 ||
   7457           writeType_ == Scalar::Float64;
   7458  }
   7459  bool isBigIntWrite() const { return Scalar::isBigIntType(writeType_); }
   7460 };
   7461 
   7462 // Store an unboxed scalar value to an array buffer view or other object.
   7463 class MStoreUnboxedScalar : public MTernaryInstruction,
   7464                            public StoreUnboxedScalarBase,
   7465                            public StoreUnboxedScalarPolicy::Data {
   7466  MemoryBarrierRequirement requiresBarrier_;
   7467 
   7468  MStoreUnboxedScalar(MDefinition* elements, MDefinition* index,
   7469                      MDefinition* value, Scalar::Type storageType,
   7470                      MemoryBarrierRequirement requiresBarrier =
   7471                          MemoryBarrierRequirement::NotRequired)
   7472      : MTernaryInstruction(classOpcode, elements, index, value),
   7473        StoreUnboxedScalarBase(storageType),
   7474        requiresBarrier_(requiresBarrier) {
   7475    if (requiresBarrier_ == MemoryBarrierRequirement::Required) {
   7476      setGuard();  // Not removable or movable
   7477    }
   7478    MOZ_ASSERT(elements->type() == MIRType::Elements);
   7479    MOZ_ASSERT(index->type() == MIRType::IntPtr);
   7480    MOZ_ASSERT(storageType >= 0 && storageType < Scalar::MaxTypedArrayViewType);
   7481  }
   7482 
   7483 public:
   7484  INSTRUCTION_HEADER(StoreUnboxedScalar)
   7485  TRIVIAL_NEW_WRAPPERS
   7486  NAMED_OPERANDS((0, elements), (1, index), (2, value))
   7487 
   7488  AliasSet getAliasSet() const override {
   7489    return AliasSet::Store(AliasSet::UnboxedElement);
   7490  }
   7491  auto requiresMemoryBarrier() const { return requiresBarrier_; }
   7492  TruncateKind operandTruncateKind(size_t index) const override;
   7493 
   7494  bool canConsumeFloat32(MUse* use) const override {
   7495    return use == getUseFor(2) && writeType() == Scalar::Float32;
   7496  }
   7497 
   7498 #ifdef DEBUG
   7499  // Float16 inputs are typed as float32, but this instruction can NOT consume
   7500  // float32 when its write-type is float16.
   7501  bool isConsistentFloat32Use(MUse* use) const override {
   7502    return use == getUseFor(2) &&
   7503           (writeType() == Scalar::Float32 || writeType() == Scalar::Float16);
   7504  }
   7505 #endif
   7506 
   7507  ALLOW_CLONE(MStoreUnboxedScalar)
   7508 };
   7509 
   7510 // Store an unboxed scalar value to a dataview object.
   7511 class MStoreDataViewElement : public MQuaternaryInstruction,
   7512                              public StoreUnboxedScalarBase,
   7513                              public StoreDataViewElementPolicy::Data {
   7514  MStoreDataViewElement(MDefinition* elements, MDefinition* index,
   7515                        MDefinition* value, MDefinition* littleEndian,
   7516                        Scalar::Type storageType)
   7517      : MQuaternaryInstruction(classOpcode, elements, index, value,
   7518                               littleEndian),
   7519        StoreUnboxedScalarBase(storageType) {
   7520    MOZ_ASSERT(elements->type() == MIRType::Elements);
   7521    MOZ_ASSERT(index->type() == MIRType::IntPtr);
   7522    MOZ_ASSERT(storageType >= 0 && storageType < Scalar::MaxTypedArrayViewType);
   7523    MOZ_ASSERT(Scalar::byteSize(storageType) > 1);
   7524  }
   7525 
   7526 public:
   7527  INSTRUCTION_HEADER(StoreDataViewElement)
   7528  TRIVIAL_NEW_WRAPPERS
   7529  NAMED_OPERANDS((0, elements), (1, index), (2, value), (3, littleEndian))
   7530 
   7531  AliasSet getAliasSet() const override {
   7532    return AliasSet::Store(AliasSet::UnboxedElement);
   7533  }
   7534  TruncateKind operandTruncateKind(size_t index) const override;
   7535 
   7536  bool canConsumeFloat32(MUse* use) const override {
   7537    return use == getUseFor(2) && writeType() == Scalar::Float32;
   7538  }
   7539 
   7540 #ifdef DEBUG
   7541  // Float16 inputs are typed as float32, but this instruction can NOT consume
   7542  // float32 when its write-type is float16.
   7543  bool isConsistentFloat32Use(MUse* use) const override {
   7544    return use == getUseFor(2) &&
   7545           (writeType() == Scalar::Float32 || writeType() == Scalar::Float16);
   7546  }
   7547 #endif
   7548 
   7549  ALLOW_CLONE(MStoreDataViewElement)
   7550 };
   7551 
   7552 class MStoreTypedArrayElementHole : public MQuaternaryInstruction,
   7553                                    public StoreUnboxedScalarBase,
   7554                                    public StoreTypedArrayHolePolicy::Data {
   7555  MStoreTypedArrayElementHole(MDefinition* elements, MDefinition* length,
   7556                              MDefinition* index, MDefinition* value,
   7557                              Scalar::Type arrayType)
   7558      : MQuaternaryInstruction(classOpcode, elements, length, index, value),
   7559        StoreUnboxedScalarBase(arrayType) {
   7560    MOZ_ASSERT(elements->type() == MIRType::Elements);
   7561    MOZ_ASSERT(length->type() == MIRType::IntPtr);
   7562    MOZ_ASSERT(index->type() == MIRType::IntPtr);
   7563    MOZ_ASSERT(arrayType >= 0 && arrayType < Scalar::MaxTypedArrayViewType);
   7564  }
   7565 
   7566 public:
   7567  INSTRUCTION_HEADER(StoreTypedArrayElementHole)
   7568  TRIVIAL_NEW_WRAPPERS
   7569  NAMED_OPERANDS((0, elements), (1, length), (2, index), (3, value))
   7570 
   7571  Scalar::Type arrayType() const { return writeType(); }
   7572  AliasSet getAliasSet() const override {
   7573    return AliasSet::Store(AliasSet::UnboxedElement);
   7574  }
   7575  TruncateKind operandTruncateKind(size_t index) const override;
   7576 
   7577  bool canConsumeFloat32(MUse* use) const override {
   7578    return use == getUseFor(3) && arrayType() == Scalar::Float32;
   7579  }
   7580 
   7581 #ifdef DEBUG
   7582  // Float16 inputs are typed as float32, but this instruction can NOT consume
   7583  // float32 when its array-type is float16.
   7584  bool isConsistentFloat32Use(MUse* use) const override {
   7585    return use == getUseFor(3) &&
   7586           (arrayType() == Scalar::Float32 || arrayType() == Scalar::Float16);
   7587  }
   7588 #endif
   7589 
   7590  ALLOW_CLONE(MStoreTypedArrayElementHole)
   7591 };
   7592 
   7593 class MTypedArrayFill : public MQuaternaryInstruction,
   7594                        public StoreUnboxedScalarBase,
   7595                        public TypedArrayFillPolicy::Data {
   7596  MTypedArrayFill(MDefinition* object, MDefinition* value, MDefinition* start,
   7597                  MDefinition* end, Scalar::Type arrayType)
   7598      : MQuaternaryInstruction(classOpcode, object, value, start, end),
   7599        StoreUnboxedScalarBase(arrayType) {
   7600    MOZ_ASSERT(object->type() == MIRType::Object);
   7601    MOZ_ASSERT(start->type() == MIRType::IntPtr);
   7602    MOZ_ASSERT(end->type() == MIRType::IntPtr);
   7603    MOZ_ASSERT(arrayType >= 0 && arrayType < Scalar::MaxTypedArrayViewType);
   7604  }
   7605 
   7606 public:
   7607  INSTRUCTION_HEADER(TypedArrayFill)
   7608  TRIVIAL_NEW_WRAPPERS
   7609  NAMED_OPERANDS((0, object), (1, value), (2, start), (3, end))
   7610 
   7611  Scalar::Type elementType() const { return writeType(); }
   7612  AliasSet getAliasSet() const override {
   7613    // Loads typed array length and elements.
   7614    constexpr auto load =
   7615        AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset |
   7616                       AliasSet::ObjectFields | AliasSet::UnboxedElement);
   7617 
   7618    // Stores into typed array elements.
   7619    constexpr auto store = AliasSet::Store(AliasSet::UnboxedElement);
   7620 
   7621    return load | store;
   7622  }
   7623 
   7624  TruncateKind operandTruncateKind(size_t index) const override;
   7625 
   7626  bool canConsumeFloat32(MUse* use) const override {
   7627    return use == getUseFor(1) && elementType() == Scalar::Float32;
   7628  }
   7629 
   7630 #ifdef DEBUG
   7631  // Float16 inputs are typed as float32, but this instruction can NOT consume
   7632  // float32 when its array-type is float16.
   7633  bool isConsistentFloat32Use(MUse* use) const override {
   7634    return use == getUseFor(1) && (elementType() == Scalar::Float32 ||
   7635                                   elementType() == Scalar::Float16);
   7636  }
   7637 #endif
   7638 
   7639  bool possiblyCalls() const override { return true; }
   7640 
   7641  ALLOW_CLONE(MTypedArrayFill)
   7642 };
   7643 
   7644 // Inlined TypedArray.prototype.subarray
   7645 class MTypedArraySubarray : public MTernaryInstruction,
   7646                            public MixPolicy<ObjectPolicy<0>, IntPtrPolicy<1>,
   7647                                             IntPtrPolicy<2>>::Data {
   7648  CompilerGCPointer<JSObject*> templateObject_;
   7649  gc::Heap initialHeap_;
   7650  bool scalarReplaced_ = false;
   7651 
   7652  MTypedArraySubarray(MDefinition* object, MDefinition* start,
   7653                      MDefinition* length, JSObject* templateObject,
   7654                      gc::Heap initialHeap)
   7655      : MTernaryInstruction(classOpcode, object, start, length),
   7656        templateObject_(templateObject),
   7657        initialHeap_(initialHeap) {
   7658    setResultType(MIRType::Object);
   7659  }
   7660 
   7661 public:
   7662  INSTRUCTION_HEADER(TypedArraySubarray)
   7663  TRIVIAL_NEW_WRAPPERS
   7664  NAMED_OPERANDS((0, object), (1, start), (2, length))
   7665 
   7666  JSObject* templateObject() const { return templateObject_; }
   7667  gc::Heap initialHeap() const { return initialHeap_; }
   7668 
   7669  bool isScalarReplaced() const { return scalarReplaced_; }
   7670  void setScalarReplaced() { scalarReplaced_ = true; }
   7671 
   7672  AliasSet getAliasSet() const override {
   7673    if (scalarReplaced_) {
   7674      return AliasSet::None();
   7675    }
   7676    return AliasSet::Store(AliasSet::ObjectFields);
   7677  }
   7678 
   7679  bool possiblyCalls() const override { return true; }
   7680 
   7681  [[nodiscard]] bool writeRecoverData(
   7682      CompactBufferWriter& writer) const override;
   7683  bool canRecoverOnBailout() const override { return scalarReplaced_; }
   7684 };
   7685 
   7686 // Compute a 3-component "effective address":
   7687 //   base + index * scale + displacement
   7688 class MEffectiveAddress3 : public MBinaryInstruction,
   7689                           public NoTypePolicy::Data {
   7690  MEffectiveAddress3(MDefinition* base, MDefinition* index, Scale scale,
   7691                     int32_t displacement)
   7692      : MBinaryInstruction(classOpcode, base, index),
   7693        scale_(scale),
   7694        displacement_(displacement) {
   7695    MOZ_ASSERT(base->type() == MIRType::Int32);
   7696    MOZ_ASSERT(index->type() == MIRType::Int32);
   7697    setMovable();
   7698    setResultType(MIRType::Int32);
   7699  }
   7700 
   7701  Scale scale_;
   7702  int32_t displacement_;
   7703 
   7704 public:
   7705  INSTRUCTION_HEADER(EffectiveAddress3)
   7706  TRIVIAL_NEW_WRAPPERS
   7707 
   7708  MDefinition* base() const { return lhs(); }
   7709  MDefinition* index() const { return rhs(); }
   7710  Scale scale() const { return scale_; }
   7711  int32_t displacement() const { return displacement_; }
   7712 
   7713  AliasSet getAliasSet() const override { return AliasSet::None(); }
   7714 
   7715 #ifdef JS_JITSPEW
   7716  void getExtras(ExtrasCollector* extras) const override {
   7717    char buf[64];
   7718    SprintfLiteral(buf, "(disp=%d, scale=%s)", int(displacement_),
   7719                   StringFromScale(scale_));
   7720    extras->add(buf);
   7721  }
   7722 #endif
   7723 
   7724  ALLOW_CLONE(MEffectiveAddress3)
   7725 };
   7726 
   7727 // Compute a 2-component "effective address":
   7728 //   index * scale + displacement
   7729 class MEffectiveAddress2 : public MUnaryInstruction, public NoTypePolicy::Data {
   7730  MEffectiveAddress2(MDefinition* index, Scale scale, int32_t displacement)
   7731      : MUnaryInstruction(classOpcode, index),
   7732        scale_(scale),
   7733        displacement_(displacement) {
   7734    MOZ_ASSERT(index->type() == MIRType::Int32);
   7735    setMovable();
   7736    setResultType(MIRType::Int32);
   7737  }
   7738 
   7739  Scale scale_;
   7740  int32_t displacement_;
   7741 
   7742 public:
   7743  INSTRUCTION_HEADER(EffectiveAddress2)
   7744  TRIVIAL_NEW_WRAPPERS
   7745 
   7746  MDefinition* index() const { return input(); }
   7747  Scale scale() const { return scale_; }
   7748  int32_t displacement() const { return displacement_; }
   7749 
   7750  AliasSet getAliasSet() const override { return AliasSet::None(); }
   7751 
   7752 #ifdef JS_JITSPEW
   7753  void getExtras(ExtrasCollector* extras) const override {
   7754    char buf[64];
   7755    SprintfLiteral(buf, "(disp=%d, scale=%s)", int(displacement_),
   7756                   StringFromScale(scale_));
   7757    extras->add(buf);
   7758  }
   7759 #endif
   7760 
   7761  ALLOW_CLONE(MEffectiveAddress2)
   7762 };
   7763 
   7764 // Clamp input to range [0, 255] for Uint8ClampedArray.
   7765 class MClampToUint8 : public MUnaryInstruction, public ClampPolicy::Data {
   7766  explicit MClampToUint8(MDefinition* input)
   7767      : MUnaryInstruction(classOpcode, input) {
   7768    setResultType(MIRType::Int32);
   7769    setMovable();
   7770  }
   7771 
   7772 public:
   7773  INSTRUCTION_HEADER(ClampToUint8)
   7774  TRIVIAL_NEW_WRAPPERS
   7775 
   7776  MDefinition* foldsTo(TempAllocator& alloc) override;
   7777 
   7778  bool congruentTo(const MDefinition* ins) const override {
   7779    return congruentIfOperandsEqual(ins);
   7780  }
   7781  AliasSet getAliasSet() const override { return AliasSet::None(); }
   7782  void computeRange(TempAllocator& alloc) override;
   7783 
   7784  ALLOW_CLONE(MClampToUint8)
   7785 };
   7786 
   7787 class MLoadFixedSlot : public MUnaryInstruction,
   7788                       public SingleObjectPolicy::Data {
   7789  size_t slot_;
   7790  bool usedAsPropertyKey_ = false;
   7791 
   7792 protected:
   7793  MLoadFixedSlot(MDefinition* obj, size_t slot)
   7794      : MUnaryInstruction(classOpcode, obj), slot_(slot) {
   7795    setResultType(MIRType::Value);
   7796    setMovable();
   7797  }
   7798 
   7799 public:
   7800  INSTRUCTION_HEADER(LoadFixedSlot)
   7801  TRIVIAL_NEW_WRAPPERS
   7802  NAMED_OPERANDS((0, object))
   7803 
   7804  size_t slot() const { return slot_; }
   7805 
   7806  HashNumber valueHash() const override;
   7807 
   7808  bool congruentTo(const MDefinition* ins) const override {
   7809    if (!ins->isLoadFixedSlot()) {
   7810      return false;
   7811    }
   7812    if (slot() != ins->toLoadFixedSlot()->slot()) {
   7813      return false;
   7814    }
   7815    return congruentIfOperandsEqual(ins);
   7816  }
   7817 
   7818  MDefinition* foldsTo(TempAllocator& alloc) override;
   7819 
   7820  AliasSet getAliasSet() const override {
   7821    return AliasSet::Load(AliasSet::FixedSlot);
   7822  }
   7823 
   7824  AliasType mightAlias(const MDefinition* store) const override;
   7825 
   7826 #ifdef JS_JITSPEW
   7827  void printOpcode(GenericPrinter& out) const override;
   7828 #endif
   7829 
   7830  void setUsedAsPropertyKey() { usedAsPropertyKey_ = true; }
   7831  bool usedAsPropertyKey() const { return usedAsPropertyKey_; }
   7832 
   7833  ALLOW_CLONE(MLoadFixedSlot)
   7834 };
   7835 
   7836 class MLoadFixedSlotAndUnbox : public MUnaryInstruction,
   7837                               public SingleObjectPolicy::Data {
   7838  size_t slot_;
   7839  MUnbox::Mode mode_;
   7840  bool usedAsPropertyKey_;
   7841 
   7842  MLoadFixedSlotAndUnbox(MDefinition* obj, size_t slot, MUnbox::Mode mode,
   7843                         MIRType type, bool usedAsPropertyKey = false)
   7844      : MUnaryInstruction(classOpcode, obj),
   7845        slot_(slot),
   7846        mode_(mode),
   7847        usedAsPropertyKey_(usedAsPropertyKey) {
   7848    setResultType(type);
   7849    setMovable();
   7850    if (mode_ == MUnbox::Fallible) {
   7851      setGuard();
   7852    }
   7853  }
   7854 
   7855 public:
   7856  INSTRUCTION_HEADER(LoadFixedSlotAndUnbox)
   7857  TRIVIAL_NEW_WRAPPERS
   7858  NAMED_OPERANDS((0, object))
   7859 
   7860  size_t slot() const { return slot_; }
   7861  MUnbox::Mode mode() const { return mode_; }
   7862  bool fallible() const { return mode_ != MUnbox::Infallible; }
   7863  bool congruentTo(const MDefinition* ins) const override {
   7864    if (!ins->isLoadFixedSlotAndUnbox() ||
   7865        slot() != ins->toLoadFixedSlotAndUnbox()->slot() ||
   7866        mode() != ins->toLoadFixedSlotAndUnbox()->mode()) {
   7867      return false;
   7868    }
   7869    return congruentIfOperandsEqual(ins);
   7870  }
   7871 
   7872  MDefinition* foldsTo(TempAllocator& alloc) override;
   7873 
   7874  AliasSet getAliasSet() const override {
   7875    return AliasSet::Load(AliasSet::FixedSlot);
   7876  }
   7877 
   7878  AliasType mightAlias(const MDefinition* store) const override;
   7879 
   7880 #ifdef JS_JITSPEW
   7881  void printOpcode(GenericPrinter& out) const override;
   7882 #endif
   7883 
   7884  bool usedAsPropertyKey() const { return usedAsPropertyKey_; }
   7885 
   7886  ALLOW_CLONE(MLoadFixedSlotAndUnbox);
   7887 };
   7888 
   7889 class MLoadDynamicSlotAndUnbox : public MUnaryInstruction,
   7890                                 public NoTypePolicy::Data {
   7891  size_t slot_;
   7892  MUnbox::Mode mode_;
   7893  bool usedAsPropertyKey_ = false;
   7894 
   7895  MLoadDynamicSlotAndUnbox(MDefinition* slots, size_t slot, MUnbox::Mode mode,
   7896                           MIRType type, bool usedAsPropertyKey = false)
   7897      : MUnaryInstruction(classOpcode, slots),
   7898        slot_(slot),
   7899        mode_(mode),
   7900        usedAsPropertyKey_(usedAsPropertyKey) {
   7901    setResultType(type);
   7902    setMovable();
   7903    if (mode_ == MUnbox::Fallible) {
   7904      setGuard();
   7905    }
   7906  }
   7907 
   7908 public:
   7909  INSTRUCTION_HEADER(LoadDynamicSlotAndUnbox)
   7910  TRIVIAL_NEW_WRAPPERS
   7911  NAMED_OPERANDS((0, slots))
   7912 
   7913  size_t slot() const { return slot_; }
   7914  MUnbox::Mode mode() const { return mode_; }
   7915  bool fallible() const { return mode_ != MUnbox::Infallible; }
   7916 
   7917  bool congruentTo(const MDefinition* ins) const override {
   7918    if (!ins->isLoadDynamicSlotAndUnbox() ||
   7919        slot() != ins->toLoadDynamicSlotAndUnbox()->slot() ||
   7920        mode() != ins->toLoadDynamicSlotAndUnbox()->mode()) {
   7921      return false;
   7922    }
   7923    return congruentIfOperandsEqual(ins);
   7924  }
   7925 
   7926  AliasSet getAliasSet() const override {
   7927    return AliasSet::Load(AliasSet::DynamicSlot);
   7928  }
   7929 
   7930 #ifdef JS_JITSPEW
   7931  void printOpcode(GenericPrinter& out) const override;
   7932 #endif
   7933 
   7934  bool usedAsPropertyKey() const { return usedAsPropertyKey_; }
   7935 
   7936  ALLOW_CLONE(MLoadDynamicSlotAndUnbox);
   7937 };
   7938 
   7939 class MStoreFixedSlot
   7940    : public MBinaryInstruction,
   7941      public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1>>::Data {
   7942  bool needsBarrier_;
   7943  size_t slot_;
   7944 
   7945  MStoreFixedSlot(MDefinition* obj, MDefinition* rval, size_t slot,
   7946                  bool barrier)
   7947      : MBinaryInstruction(classOpcode, obj, rval),
   7948        needsBarrier_(barrier),
   7949        slot_(slot) {}
   7950 
   7951 public:
   7952  INSTRUCTION_HEADER(StoreFixedSlot)
   7953  NAMED_OPERANDS((0, object), (1, value))
   7954 
   7955  static MStoreFixedSlot* NewUnbarriered(TempAllocator& alloc, MDefinition* obj,
   7956                                         size_t slot, MDefinition* rval) {
   7957    return new (alloc) MStoreFixedSlot(obj, rval, slot, false);
   7958  }
   7959  static MStoreFixedSlot* NewBarriered(TempAllocator& alloc, MDefinition* obj,
   7960                                       size_t slot, MDefinition* rval) {
   7961    return new (alloc) MStoreFixedSlot(obj, rval, slot, true);
   7962  }
   7963 
   7964  size_t slot() const { return slot_; }
   7965 
   7966  AliasSet getAliasSet() const override {
   7967    return AliasSet::Store(AliasSet::FixedSlot);
   7968  }
   7969  bool needsBarrier() const { return needsBarrier_; }
   7970  void setNeedsBarrier(bool needsBarrier = true) {
   7971    needsBarrier_ = needsBarrier;
   7972  }
   7973 
   7974 #ifdef JS_JITSPEW
   7975  void printOpcode(GenericPrinter& out) const override;
   7976 #endif
   7977 
   7978  ALLOW_CLONE(MStoreFixedSlot)
   7979 };
   7980 
   7981 class MStoreFixedSlotFromOffset
   7982    : public MTernaryInstruction,
   7983      public MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>,
   7984                       NoFloatPolicy<2>>::Data {
   7985  bool needsBarrier_;
   7986 
   7987  MStoreFixedSlotFromOffset(MDefinition* obj, MDefinition* offset,
   7988                            MDefinition* rval, bool barrier)
   7989      : MTernaryInstruction(classOpcode, obj, offset, rval),
   7990        needsBarrier_(barrier) {
   7991    MOZ_ASSERT(obj->type() == MIRType::Object);
   7992  }
   7993 
   7994 public:
   7995  INSTRUCTION_HEADER(StoreFixedSlotFromOffset)
   7996  NAMED_OPERANDS((0, object), (1, offset), (2, value))
   7997 
   7998  static MStoreFixedSlotFromOffset* NewBarriered(TempAllocator& alloc,
   7999                                                 MDefinition* obj,
   8000                                                 MDefinition* offset,
   8001                                                 MDefinition* rval) {
   8002    return new (alloc) MStoreFixedSlotFromOffset(obj, offset, rval, true);
   8003  }
   8004 
   8005  AliasSet getAliasSet() const override {
   8006    return AliasSet::Store(AliasSet::FixedSlot);
   8007  }
   8008  bool needsBarrier() const { return needsBarrier_; }
   8009  void setNeedsBarrier(bool needsBarrier = true) {
   8010    needsBarrier_ = needsBarrier;
   8011  }
   8012 
   8013  ALLOW_CLONE(MStoreFixedSlotFromOffset)
   8014 };
   8015 
   8016 class MGetPropertyCache : public MBinaryInstruction,
   8017                          public MixPolicy<BoxExceptPolicy<0, MIRType::Object>,
   8018                                           CacheIdPolicy<1>>::Data {
   8019  MGetPropertyCache(MDefinition* obj, MDefinition* id)
   8020      : MBinaryInstruction(classOpcode, obj, id) {
   8021    setResultType(MIRType::Value);
   8022  }
   8023 
   8024 public:
   8025  INSTRUCTION_HEADER(GetPropertyCache)
   8026  TRIVIAL_NEW_WRAPPERS
   8027  NAMED_OPERANDS((0, value), (1, idval))
   8028 };
   8029 
   8030 class MGetPropSuperCache
   8031    : public MTernaryInstruction,
   8032      public MixPolicy<ObjectPolicy<0>, BoxExceptPolicy<1, MIRType::Object>,
   8033                       CacheIdPolicy<2>>::Data {
   8034  MGetPropSuperCache(MDefinition* obj, MDefinition* receiver, MDefinition* id)
   8035      : MTernaryInstruction(classOpcode, obj, receiver, id) {
   8036    setResultType(MIRType::Value);
   8037    setGuard();
   8038  }
   8039 
   8040 public:
   8041  INSTRUCTION_HEADER(GetPropSuperCache)
   8042  TRIVIAL_NEW_WRAPPERS
   8043  NAMED_OPERANDS((0, object), (1, receiver), (2, idval))
   8044 };
   8045 
   8046 // Guard the object's proto is |expected|.
   8047 class MGuardProto : public MBinaryInstruction, public SingleObjectPolicy::Data {
   8048  MGuardProto(MDefinition* obj, MDefinition* expected)
   8049      : MBinaryInstruction(classOpcode, obj, expected) {
   8050    MOZ_ASSERT(expected->isConstant() || expected->isNurseryObject());
   8051    setGuard();
   8052    setMovable();
   8053    setResultType(MIRType::Object);
   8054  }
   8055 
   8056 public:
   8057  INSTRUCTION_HEADER(GuardProto)
   8058  TRIVIAL_NEW_WRAPPERS
   8059  NAMED_OPERANDS((0, object), (1, expected))
   8060 
   8061  bool congruentTo(const MDefinition* ins) const override {
   8062    return congruentIfOperandsEqual(ins);
   8063  }
   8064 
   8065  AliasSet getAliasSet() const override {
   8066    return AliasSet::Load(AliasSet::ObjectFields);
   8067  }
   8068  AliasType mightAlias(const MDefinition* def) const override {
   8069    // These instructions never modify the [[Prototype]].
   8070    if (def->isAddAndStoreSlot() || def->isAllocateAndStoreSlot()) {
   8071      return AliasType::NoAlias;
   8072    }
   8073    return AliasType::MayAlias;
   8074  }
   8075 };
   8076 
   8077 // Guard the object has no proto.
   8078 class MGuardNullProto : public MUnaryInstruction,
   8079                        public SingleObjectPolicy::Data {
   8080  explicit MGuardNullProto(MDefinition* obj)
   8081      : MUnaryInstruction(classOpcode, obj) {
   8082    setGuard();
   8083    setMovable();
   8084    setResultType(MIRType::Object);
   8085  }
   8086 
   8087 public:
   8088  INSTRUCTION_HEADER(GuardNullProto)
   8089  TRIVIAL_NEW_WRAPPERS
   8090  NAMED_OPERANDS((0, object))
   8091 
   8092  bool congruentTo(const MDefinition* ins) const override {
   8093    return congruentIfOperandsEqual(ins);
   8094  }
   8095  AliasSet getAliasSet() const override {
   8096    return AliasSet::Load(AliasSet::ObjectFields);
   8097  }
   8098  AliasType mightAlias(const MDefinition* def) const override {
   8099    // These instructions never modify the [[Prototype]].
   8100    if (def->isAddAndStoreSlot() || def->isAllocateAndStoreSlot()) {
   8101      return AliasType::NoAlias;
   8102    }
   8103    return AliasType::MayAlias;
   8104  }
   8105 };
   8106 
   8107 // Guard on a specific Value.
   8108 class MGuardValue : public MUnaryInstruction, public BoxInputsPolicy::Data {
   8109  ValueOrNurseryValueIndex expected_;
   8110 
   8111  MGuardValue(MDefinition* val, ValueOrNurseryValueIndex expected)
   8112      : MUnaryInstruction(classOpcode, val), expected_(expected) {
   8113    setGuard();
   8114    setMovable();
   8115    setResultType(MIRType::Value);
   8116  }
   8117  MGuardValue(MDefinition* val, Value expected)
   8118      : MGuardValue(val, ValueOrNurseryValueIndex::fromValue(expected)) {}
   8119 
   8120 public:
   8121  INSTRUCTION_HEADER(GuardValue)
   8122  TRIVIAL_NEW_WRAPPERS
   8123  NAMED_OPERANDS((0, value))
   8124 
   8125  ValueOrNurseryValueIndex expected() const { return expected_; }
   8126 
   8127  bool congruentTo(const MDefinition* ins) const override {
   8128    if (!ins->isGuardValue()) {
   8129      return false;
   8130    }
   8131    if (expected() != ins->toGuardValue()->expected()) {
   8132      return false;
   8133    }
   8134    return congruentIfOperandsEqual(ins);
   8135  }
   8136  MDefinition* foldsTo(TempAllocator& alloc) override;
   8137  AliasSet getAliasSet() const override { return AliasSet::None(); }
   8138 };
   8139 
   8140 // Guard on function flags
   8141 class MGuardFunctionFlags : public MUnaryInstruction,
   8142                            public SingleObjectPolicy::Data {
   8143  // At least one of the expected flags must be set, but not necessarily all
   8144  // expected flags.
   8145  uint16_t expectedFlags_;
   8146 
   8147  // None of the unexpected flags must be set.
   8148  uint16_t unexpectedFlags_;
   8149 
   8150  explicit MGuardFunctionFlags(MDefinition* fun, uint16_t expectedFlags,
   8151                               uint16_t unexpectedFlags)
   8152      : MUnaryInstruction(classOpcode, fun),
   8153        expectedFlags_(expectedFlags),
   8154        unexpectedFlags_(unexpectedFlags) {
   8155    MOZ_ASSERT((expectedFlags & unexpectedFlags) == 0,
   8156               "Can't guard inconsistent flags");
   8157    MOZ_ASSERT((expectedFlags | unexpectedFlags) != 0,
   8158               "Can't guard zero flags");
   8159    setGuard();
   8160    setMovable();
   8161    setResultType(MIRType::Object);
   8162  }
   8163 
   8164 public:
   8165  INSTRUCTION_HEADER(GuardFunctionFlags)
   8166  TRIVIAL_NEW_WRAPPERS
   8167  NAMED_OPERANDS((0, function))
   8168 
   8169  uint16_t expectedFlags() const { return expectedFlags_; };
   8170  uint16_t unexpectedFlags() const { return unexpectedFlags_; };
   8171 
   8172  bool congruentTo(const MDefinition* ins) const override {
   8173    if (!ins->isGuardFunctionFlags()) {
   8174      return false;
   8175    }
   8176    if (expectedFlags() != ins->toGuardFunctionFlags()->expectedFlags()) {
   8177      return false;
   8178    }
   8179    if (unexpectedFlags() != ins->toGuardFunctionFlags()->unexpectedFlags()) {
   8180      return false;
   8181    }
   8182    return congruentIfOperandsEqual(ins);
   8183  }
   8184  AliasSet getAliasSet() const override {
   8185    return AliasSet::Load(AliasSet::ObjectFields);
   8186  }
   8187 };
   8188 
   8189 // Guard on an object's identity, inclusively or exclusively.
   8190 class MGuardObjectIdentity : public MBinaryInstruction,
   8191                             public SingleObjectPolicy::Data {
   8192  bool bailOnEquality_;
   8193 
   8194  MGuardObjectIdentity(MDefinition* obj, MDefinition* expected,
   8195                       bool bailOnEquality)
   8196      : MBinaryInstruction(classOpcode, obj, expected),
   8197        bailOnEquality_(bailOnEquality) {
   8198    setGuard();
   8199    setMovable();
   8200    setResultType(MIRType::Object);
   8201  }
   8202 
   8203 public:
   8204  INSTRUCTION_HEADER(GuardObjectIdentity)
   8205  TRIVIAL_NEW_WRAPPERS
   8206  NAMED_OPERANDS((0, object), (1, expected))
   8207 
   8208  bool bailOnEquality() const { return bailOnEquality_; }
   8209  MDefinition* foldsTo(TempAllocator& alloc) override;
   8210  bool congruentTo(const MDefinition* ins) const override {
   8211    if (!ins->isGuardObjectIdentity()) {
   8212      return false;
   8213    }
   8214    if (bailOnEquality() != ins->toGuardObjectIdentity()->bailOnEquality()) {
   8215      return false;
   8216    }
   8217    return congruentIfOperandsEqual(ins);
   8218  }
   8219  AliasSet getAliasSet() const override { return AliasSet::None(); }
   8220 };
   8221 
   8222 // Guard on a specific JSFunction. Used instead of MGuardObjectIdentity,
   8223 // so we can store some metadata related to the expected function.
   8224 class MGuardSpecificFunction : public MBinaryInstruction,
   8225                               public SingleObjectPolicy::Data {
   8226  uint16_t nargs_;
   8227  FunctionFlags flags_;
   8228 
   8229  MGuardSpecificFunction(MDefinition* obj, MDefinition* expected,
   8230                         uint16_t nargs, FunctionFlags flags)
   8231      : MBinaryInstruction(classOpcode, obj, expected),
   8232        nargs_(nargs),
   8233        flags_(flags) {
   8234    MOZ_ASSERT(expected->isConstant() || expected->isNurseryObject());
   8235    setGuard();
   8236    setMovable();
   8237    setResultType(MIRType::Object);
   8238  }
   8239 
   8240 public:
   8241  INSTRUCTION_HEADER(GuardSpecificFunction)
   8242  TRIVIAL_NEW_WRAPPERS
   8243  NAMED_OPERANDS((0, function), (1, expected))
   8244 
   8245  uint16_t nargs() const { return nargs_; }
   8246  FunctionFlags flags() const { return flags_; }
   8247 
   8248  MDefinition* foldsTo(TempAllocator& alloc) override;
   8249  bool congruentTo(const MDefinition* ins) const override {
   8250    if (!ins->isGuardSpecificFunction()) {
   8251      return false;
   8252    }
   8253 
   8254    auto* other = ins->toGuardSpecificFunction();
   8255    if (nargs() != other->nargs() ||
   8256        flags().toRaw() != other->flags().toRaw()) {
   8257      return false;
   8258    }
   8259    return congruentIfOperandsEqual(other);
   8260  }
   8261  AliasSet getAliasSet() const override { return AliasSet::None(); }
   8262 };
   8263 
   8264 class MGuardSpecificSymbol : public MUnaryInstruction,
   8265                             public SymbolPolicy<0>::Data {
   8266  CompilerGCPointer<JS::Symbol*> expected_;
   8267 
   8268  MGuardSpecificSymbol(MDefinition* symbol, JS::Symbol* expected)
   8269      : MUnaryInstruction(classOpcode, symbol), expected_(expected) {
   8270    setGuard();
   8271    setMovable();
   8272    setResultType(MIRType::Symbol);
   8273  }
   8274 
   8275 public:
   8276  INSTRUCTION_HEADER(GuardSpecificSymbol)
   8277  TRIVIAL_NEW_WRAPPERS
   8278  NAMED_OPERANDS((0, symbol))
   8279 
   8280  JS::Symbol* expected() const { return expected_; }
   8281 
   8282  bool congruentTo(const MDefinition* ins) const override {
   8283    if (!ins->isGuardSpecificSymbol()) {
   8284      return false;
   8285    }
   8286    if (expected() != ins->toGuardSpecificSymbol()->expected()) {
   8287      return false;
   8288    }
   8289    return congruentIfOperandsEqual(ins);
   8290  }
   8291  MDefinition* foldsTo(TempAllocator& alloc) override;
   8292  AliasSet getAliasSet() const override { return AliasSet::None(); }
   8293 };
   8294 
   8295 class MGuardTagNotEqual
   8296    : public MBinaryInstruction,
   8297      public MixPolicy<UnboxedInt32Policy<0>, UnboxedInt32Policy<1>>::Data {
   8298  MGuardTagNotEqual(MDefinition* left, MDefinition* right)
   8299      : MBinaryInstruction(classOpcode, left, right) {
   8300    setGuard();
   8301    setMovable();
   8302    setCommutative();
   8303  }
   8304 
   8305 public:
   8306  INSTRUCTION_HEADER(GuardTagNotEqual)
   8307  TRIVIAL_NEW_WRAPPERS
   8308 
   8309  AliasSet getAliasSet() const override { return AliasSet::None(); }
   8310 
   8311  bool congruentTo(const MDefinition* ins) const override {
   8312    return binaryCongruentTo(ins);
   8313  }
   8314 };
   8315 
   8316 // Load from vp[slot] (slots that are not inline in an object).
   8317 class MLoadDynamicSlot : public MUnaryInstruction, public NoTypePolicy::Data {
   8318  uint32_t slot_;
   8319  bool usedAsPropertyKey_ = false;
   8320 
   8321  MLoadDynamicSlot(MDefinition* slots, uint32_t slot)
   8322      : MUnaryInstruction(classOpcode, slots), slot_(slot) {
   8323    setResultType(MIRType::Value);
   8324    setMovable();
   8325    MOZ_ASSERT(slots->type() == MIRType::Slots);
   8326  }
   8327 
   8328 public:
   8329  INSTRUCTION_HEADER(LoadDynamicSlot)
   8330  TRIVIAL_NEW_WRAPPERS
   8331  NAMED_OPERANDS((0, slots))
   8332 
   8333  uint32_t slot() const { return slot_; }
   8334 
   8335  HashNumber valueHash() const override;
   8336  bool congruentTo(const MDefinition* ins) const override {
   8337    if (!ins->isLoadDynamicSlot()) {
   8338      return false;
   8339    }
   8340    if (slot() != ins->toLoadDynamicSlot()->slot()) {
   8341      return false;
   8342    }
   8343    return congruentIfOperandsEqual(ins);
   8344  }
   8345 
   8346  MDefinition* foldsTo(TempAllocator& alloc) override;
   8347 
   8348  AliasSet getAliasSet() const override {
   8349    MOZ_ASSERT(slots()->type() == MIRType::Slots);
   8350    return AliasSet::Load(AliasSet::DynamicSlot);
   8351  }
   8352  AliasType mightAlias(const MDefinition* store) const override;
   8353 
   8354 #ifdef JS_JITSPEW
   8355  void printOpcode(GenericPrinter& out) const override;
   8356 #endif
   8357 
   8358  void setUsedAsPropertyKey() { usedAsPropertyKey_ = true; }
   8359  bool usedAsPropertyKey() const { return usedAsPropertyKey_; }
   8360 
   8361  ALLOW_CLONE(MLoadDynamicSlot)
   8362 };
   8363 
   8364 class MAddAndStoreSlot
   8365    : public MBinaryInstruction,
   8366      public MixPolicy<SingleObjectPolicy, BoxPolicy<1>>::Data {
   8367 public:
   8368  enum class Kind {
   8369    FixedSlot,
   8370    DynamicSlot,
   8371  };
   8372 
   8373 private:
   8374  Kind kind_;
   8375  uint32_t slotOffset_;
   8376  CompilerShape shape_;
   8377  bool preserveWrapper_;
   8378 
   8379  MAddAndStoreSlot(MDefinition* obj, MDefinition* value, Kind kind,
   8380                   uint32_t slotOffset, Shape* shape, bool preserveWrapper)
   8381      : MBinaryInstruction(classOpcode, obj, value),
   8382        kind_(kind),
   8383        slotOffset_(slotOffset),
   8384        shape_(shape),
   8385        preserveWrapper_(preserveWrapper) {}
   8386 
   8387 public:
   8388  INSTRUCTION_HEADER(AddAndStoreSlot)
   8389  TRIVIAL_NEW_WRAPPERS
   8390  NAMED_OPERANDS((0, object), (1, value))
   8391 
   8392  Kind kind() const { return kind_; }
   8393  uint32_t slotOffset() const { return slotOffset_; }
   8394  Shape* shape() const { return shape_; }
   8395  bool preserveWrapper() const { return preserveWrapper_; }
   8396 
   8397  bool possiblyCalls() const override { return preserveWrapper_; }
   8398 
   8399  AliasSet getAliasSet() const override {
   8400    return AliasSet::Store(AliasSet::ObjectFields |
   8401                           (kind() == Kind::FixedSlot ? AliasSet::FixedSlot
   8402                                                      : AliasSet::DynamicSlot));
   8403  }
   8404 };
   8405 
   8406 // Store to vp[slot] (slots that are not inline in an object).
   8407 class MStoreDynamicSlot : public MBinaryInstruction,
   8408                          public NoFloatPolicy<1>::Data {
   8409  uint32_t slot_;
   8410  bool needsBarrier_;
   8411 
   8412  MStoreDynamicSlot(MDefinition* slots, uint32_t slot, MDefinition* value,
   8413                    bool barrier)
   8414      : MBinaryInstruction(classOpcode, slots, value),
   8415        slot_(slot),
   8416        needsBarrier_(barrier) {
   8417    MOZ_ASSERT(slots->type() == MIRType::Slots);
   8418  }
   8419 
   8420 public:
   8421  INSTRUCTION_HEADER(StoreDynamicSlot)
   8422  NAMED_OPERANDS((0, slots), (1, value))
   8423 
   8424  static MStoreDynamicSlot* NewUnbarriered(TempAllocator& alloc,
   8425                                           MDefinition* slots, uint32_t slot,
   8426                                           MDefinition* value) {
   8427    return new (alloc) MStoreDynamicSlot(slots, slot, value, false);
   8428  }
   8429  static MStoreDynamicSlot* NewBarriered(TempAllocator& alloc,
   8430                                         MDefinition* slots, uint32_t slot,
   8431                                         MDefinition* value) {
   8432    return new (alloc) MStoreDynamicSlot(slots, slot, value, true);
   8433  }
   8434 
   8435  uint32_t slot() const { return slot_; }
   8436  bool needsBarrier() const { return needsBarrier_; }
   8437  AliasSet getAliasSet() const override {
   8438    return AliasSet::Store(AliasSet::DynamicSlot);
   8439  }
   8440 
   8441 #ifdef JS_JITSPEW
   8442  void printOpcode(GenericPrinter& out) const override;
   8443 #endif
   8444 
   8445  ALLOW_CLONE(MStoreDynamicSlot)
   8446 };
   8447 
   8448 class MStoreDynamicSlotFromOffset
   8449    : public MTernaryInstruction,
   8450      public MixPolicy<UnboxedInt32Policy<1>, NoFloatPolicy<2>>::Data {
   8451  MStoreDynamicSlotFromOffset(MDefinition* slots, MDefinition* offset,
   8452                              MDefinition* rval, bool barrier)
   8453      : MTernaryInstruction(classOpcode, slots, offset, rval) {
   8454    MOZ_ASSERT(slots->type() == MIRType::Slots);
   8455  }
   8456 
   8457 public:
   8458  INSTRUCTION_HEADER(StoreDynamicSlotFromOffset)
   8459  NAMED_OPERANDS((0, slots), (1, offset), (2, value))
   8460 
   8461  static MStoreDynamicSlotFromOffset* New(TempAllocator& alloc,
   8462                                          MDefinition* slots,
   8463                                          MDefinition* offset,
   8464                                          MDefinition* rval) {
   8465    return new (alloc) MStoreDynamicSlotFromOffset(slots, offset, rval, true);
   8466  }
   8467 
   8468  AliasSet getAliasSet() const override {
   8469    return AliasSet::Store(AliasSet::DynamicSlot);
   8470  }
   8471 
   8472  ALLOW_CLONE(MStoreDynamicSlotFromOffset)
   8473 };
   8474 
   8475 class MSetPropertyCache : public MTernaryInstruction,
   8476                          public MixPolicy<SingleObjectPolicy, CacheIdPolicy<1>,
   8477                                           NoFloatPolicy<2>>::Data {
   8478  bool strict_ : 1;
   8479 
   8480  MSetPropertyCache(MDefinition* obj, MDefinition* id, MDefinition* value,
   8481                    bool strict)
   8482      : MTernaryInstruction(classOpcode, obj, id, value), strict_(strict) {}
   8483 
   8484 public:
   8485  INSTRUCTION_HEADER(SetPropertyCache)
   8486  TRIVIAL_NEW_WRAPPERS
   8487  NAMED_OPERANDS((0, object), (1, idval), (2, value))
   8488 
   8489  bool strict() const { return strict_; }
   8490 };
   8491 
   8492 class MMegamorphicSetElement : public MTernaryInstruction,
   8493                               public MegamorphicSetElementPolicy::Data {
   8494  bool strict_;
   8495 
   8496  MMegamorphicSetElement(MDefinition* object, MDefinition* index,
   8497                         MDefinition* value, bool strict)
   8498      : MTernaryInstruction(classOpcode, object, index, value),
   8499        strict_(strict) {}
   8500 
   8501 public:
   8502  INSTRUCTION_HEADER(MegamorphicSetElement)
   8503  TRIVIAL_NEW_WRAPPERS
   8504  NAMED_OPERANDS((0, object), (1, index), (2, value))
   8505 
   8506  bool strict() const { return strict_; }
   8507 
   8508  bool possiblyCalls() const override { return true; }
   8509 };
   8510 
   8511 class MSetDOMProperty : public MBinaryInstruction,
   8512                        public MixPolicy<ObjectPolicy<0>, BoxPolicy<1>>::Data {
   8513  const JSJitSetterOp func_;
   8514  Realm* setterRealm_;
   8515  DOMObjectKind objectKind_;
   8516 
   8517  MSetDOMProperty(const JSJitSetterOp func, DOMObjectKind objectKind,
   8518                  Realm* setterRealm, MDefinition* obj, MDefinition* val)
   8519      : MBinaryInstruction(classOpcode, obj, val),
   8520        func_(func),
   8521        setterRealm_(setterRealm),
   8522        objectKind_(objectKind) {}
   8523 
   8524 public:
   8525  INSTRUCTION_HEADER(SetDOMProperty)
   8526  TRIVIAL_NEW_WRAPPERS
   8527  NAMED_OPERANDS((0, object), (1, value))
   8528 
   8529  JSJitSetterOp fun() const { return func_; }
   8530  Realm* setterRealm() const { return setterRealm_; }
   8531  DOMObjectKind objectKind() const { return objectKind_; }
   8532 
   8533  bool possiblyCalls() const override { return true; }
   8534 };
   8535 
   8536 class MGetDOMPropertyBase : public MVariadicInstruction,
   8537                            public ObjectPolicy<0>::Data {
   8538  const JSJitInfo* info_;
   8539 
   8540 protected:
   8541  MGetDOMPropertyBase(Opcode op, const JSJitInfo* jitinfo)
   8542      : MVariadicInstruction(op), info_(jitinfo) {
   8543    MOZ_ASSERT(jitinfo);
   8544    MOZ_ASSERT(jitinfo->type() == JSJitInfo::Getter);
   8545 
   8546    // We are movable iff the jitinfo says we can be.
   8547    if (isDomMovable()) {
   8548      MOZ_ASSERT(jitinfo->aliasSet() != JSJitInfo::AliasEverything);
   8549      setMovable();
   8550    } else {
   8551      // If we're not movable, that means we shouldn't be DCEd either,
   8552      // because we might throw an exception when called, and getting rid
   8553      // of that is observable.
   8554      setGuard();
   8555    }
   8556 
   8557    setResultType(MIRType::Value);
   8558  }
   8559 
   8560  const JSJitInfo* info() const { return info_; }
   8561 
   8562  [[nodiscard]] bool init(TempAllocator& alloc, MDefinition* obj,
   8563                          MDefinition* guard, MDefinition* globalGuard) {
   8564    MOZ_ASSERT(obj);
   8565    // guard can be null.
   8566    // globalGuard can be null.
   8567    size_t operandCount = 1;
   8568    if (guard) {
   8569      ++operandCount;
   8570    }
   8571    if (globalGuard) {
   8572      ++operandCount;
   8573    }
   8574    if (!MVariadicInstruction::init(alloc, operandCount)) {
   8575      return false;
   8576    }
   8577    initOperand(0, obj);
   8578 
   8579    size_t operandIndex = 1;
   8580    // Pin the guard, if we have one as an operand if we want to hoist later.
   8581    if (guard) {
   8582      initOperand(operandIndex++, guard);
   8583    }
   8584 
   8585    // And the same for the global guard, if we have one.
   8586    if (globalGuard) {
   8587      initOperand(operandIndex, globalGuard);
   8588    }
   8589 
   8590    return true;
   8591  }
   8592 
   8593 public:
   8594  NAMED_OPERANDS((0, object))
   8595 
   8596  JSJitGetterOp fun() const { return info_->getter; }
   8597  bool isInfallible() const { return info_->isInfallible; }
   8598  bool isDomMovable() const { return info_->isMovable; }
   8599  JSJitInfo::AliasSet domAliasSet() const { return info_->aliasSet(); }
   8600  size_t domMemberSlotIndex() const {
   8601    MOZ_ASSERT(info_->isAlwaysInSlot || info_->isLazilyCachedInSlot);
   8602    return info_->slotIndex;
   8603  }
   8604  bool valueMayBeInSlot() const { return info_->isLazilyCachedInSlot; }
   8605 
   8606  bool baseCongruentTo(const MGetDOMPropertyBase* ins) const {
   8607    if (!isDomMovable()) {
   8608      return false;
   8609    }
   8610 
   8611    // Checking the jitinfo is the same as checking the constant function
   8612    if (!(info() == ins->info())) {
   8613      return false;
   8614    }
   8615 
   8616    return congruentIfOperandsEqual(ins);
   8617  }
   8618 
   8619  AliasSet getAliasSet() const override {
   8620    JSJitInfo::AliasSet aliasSet = domAliasSet();
   8621    if (aliasSet == JSJitInfo::AliasNone) {
   8622      return AliasSet::None();
   8623    }
   8624    if (aliasSet == JSJitInfo::AliasDOMSets) {
   8625      return AliasSet::Load(AliasSet::DOMProperty);
   8626    }
   8627    MOZ_ASSERT(aliasSet == JSJitInfo::AliasEverything);
   8628    return AliasSet::Store(AliasSet::Any);
   8629  }
   8630 };
   8631 
   8632 class MGetDOMProperty : public MGetDOMPropertyBase {
   8633  Realm* getterRealm_;
   8634  DOMObjectKind objectKind_;
   8635 
   8636  MGetDOMProperty(const JSJitInfo* jitinfo, DOMObjectKind objectKind,
   8637                  Realm* getterRealm)
   8638      : MGetDOMPropertyBase(classOpcode, jitinfo),
   8639        getterRealm_(getterRealm),
   8640        objectKind_(objectKind) {}
   8641 
   8642 public:
   8643  INSTRUCTION_HEADER(GetDOMProperty)
   8644 
   8645  static MGetDOMProperty* New(TempAllocator& alloc, const JSJitInfo* info,
   8646                              DOMObjectKind objectKind, Realm* getterRealm,
   8647                              MDefinition* obj, MDefinition* guard,
   8648                              MDefinition* globalGuard) {
   8649    auto* res = new (alloc) MGetDOMProperty(info, objectKind, getterRealm);
   8650    if (!res || !res->init(alloc, obj, guard, globalGuard)) {
   8651      return nullptr;
   8652    }
   8653    return res;
   8654  }
   8655 
   8656  Realm* getterRealm() const { return getterRealm_; }
   8657  DOMObjectKind objectKind() const { return objectKind_; }
   8658 
   8659  bool congruentTo(const MDefinition* ins) const override {
   8660    if (!ins->isGetDOMProperty()) {
   8661      return false;
   8662    }
   8663 
   8664    if (ins->toGetDOMProperty()->getterRealm() != getterRealm()) {
   8665      return false;
   8666    }
   8667 
   8668    return baseCongruentTo(ins->toGetDOMProperty());
   8669  }
   8670 
   8671  bool possiblyCalls() const override { return true; }
   8672 };
   8673 
   8674 class MGetDOMMember : public MGetDOMPropertyBase {
   8675  explicit MGetDOMMember(const JSJitInfo* jitinfo)
   8676      : MGetDOMPropertyBase(classOpcode, jitinfo) {
   8677    setResultType(MIRTypeFromValueType(jitinfo->returnType()));
   8678  }
   8679 
   8680 public:
   8681  INSTRUCTION_HEADER(GetDOMMember)
   8682 
   8683  static MGetDOMMember* New(TempAllocator& alloc, const JSJitInfo* info,
   8684                            MDefinition* obj, MDefinition* guard,
   8685                            MDefinition* globalGuard) {
   8686    auto* res = new (alloc) MGetDOMMember(info);
   8687    if (!res || !res->init(alloc, obj, guard, globalGuard)) {
   8688      return nullptr;
   8689    }
   8690    return res;
   8691  }
   8692 
   8693  bool possiblyCalls() const override { return false; }
   8694 
   8695  bool congruentTo(const MDefinition* ins) const override {
   8696    if (!ins->isGetDOMMember()) {
   8697      return false;
   8698    }
   8699 
   8700    return baseCongruentTo(ins->toGetDOMMember());
   8701  }
   8702 };
   8703 
   8704 class MLoadDOMExpandoValueGuardGeneration : public MUnaryInstruction,
   8705                                            public SingleObjectPolicy::Data {
   8706  JS::ExpandoAndGeneration* expandoAndGeneration_;
   8707  uint64_t generation_;
   8708 
   8709  MLoadDOMExpandoValueGuardGeneration(
   8710      MDefinition* proxy, JS::ExpandoAndGeneration* expandoAndGeneration,
   8711      uint64_t generation)
   8712      : MUnaryInstruction(classOpcode, proxy),
   8713        expandoAndGeneration_(expandoAndGeneration),
   8714        generation_(generation) {
   8715    setGuard();
   8716    setMovable();
   8717    setResultType(MIRType::Value);
   8718  }
   8719 
   8720 public:
   8721  INSTRUCTION_HEADER(LoadDOMExpandoValueGuardGeneration)
   8722  TRIVIAL_NEW_WRAPPERS
   8723  NAMED_OPERANDS((0, proxy))
   8724 
   8725  JS::ExpandoAndGeneration* expandoAndGeneration() const {
   8726    return expandoAndGeneration_;
   8727  }
   8728  uint64_t generation() const { return generation_; }
   8729 
   8730  bool congruentTo(const MDefinition* ins) const override {
   8731    if (!ins->isLoadDOMExpandoValueGuardGeneration()) {
   8732      return false;
   8733    }
   8734    const auto* other = ins->toLoadDOMExpandoValueGuardGeneration();
   8735    if (expandoAndGeneration() != other->expandoAndGeneration() ||
   8736        generation() != other->generation()) {
   8737      return false;
   8738    }
   8739    return congruentIfOperandsEqual(ins);
   8740  }
   8741  AliasSet getAliasSet() const override {
   8742    return AliasSet::Load(AliasSet::DOMProxyExpando);
   8743  }
   8744 };
   8745 
   8746 // Inlined assembly for Math.floor(double | float32) -> int32.
   8747 class MFloor : public MUnaryInstruction, public FloatingPointPolicy<0>::Data {
   8748  explicit MFloor(MDefinition* num) : MUnaryInstruction(classOpcode, num) {
   8749    setResultType(MIRType::Int32);
   8750    specialization_ = MIRType::Double;
   8751    setMovable();
   8752  }
   8753 
   8754 public:
   8755  INSTRUCTION_HEADER(Floor)
   8756  TRIVIAL_NEW_WRAPPERS
   8757 
   8758  AliasSet getAliasSet() const override { return AliasSet::None(); }
   8759  bool isFloat32Commutative() const override { return true; }
   8760  void trySpecializeFloat32(TempAllocator& alloc) override;
   8761 #ifdef DEBUG
   8762  bool isConsistentFloat32Use(MUse* use) const override { return true; }
   8763 #endif
   8764  bool congruentTo(const MDefinition* ins) const override {
   8765    return congruentIfOperandsEqual(ins);
   8766  }
   8767  void computeRange(TempAllocator& alloc) override;
   8768  [[nodiscard]] bool writeRecoverData(
   8769      CompactBufferWriter& writer) const override;
   8770  bool canRecoverOnBailout() const override { return true; }
   8771 
   8772  ALLOW_CLONE(MFloor)
   8773 };
   8774 
   8775 // Inlined assembly version for Math.ceil(double | float32) -> int32.
   8776 class MCeil : public MUnaryInstruction, public FloatingPointPolicy<0>::Data {
   8777  explicit MCeil(MDefinition* num) : MUnaryInstruction(classOpcode, num) {
   8778    setResultType(MIRType::Int32);
   8779    specialization_ = MIRType::Double;
   8780    setMovable();
   8781  }
   8782 
   8783 public:
   8784  INSTRUCTION_HEADER(Ceil)
   8785  TRIVIAL_NEW_WRAPPERS
   8786 
   8787  AliasSet getAliasSet() const override { return AliasSet::None(); }
   8788  bool isFloat32Commutative() const override { return true; }
   8789  void trySpecializeFloat32(TempAllocator& alloc) override;
   8790 #ifdef DEBUG
   8791  bool isConsistentFloat32Use(MUse* use) const override { return true; }
   8792 #endif
   8793  bool congruentTo(const MDefinition* ins) const override {
   8794    return congruentIfOperandsEqual(ins);
   8795  }
   8796  void computeRange(TempAllocator& alloc) override;
   8797  [[nodiscard]] bool writeRecoverData(
   8798      CompactBufferWriter& writer) const override;
   8799  bool canRecoverOnBailout() const override { return true; }
   8800 
   8801  ALLOW_CLONE(MCeil)
   8802 };
   8803 
   8804 // Inlined version of Math.round(double | float32) -> int32.
   8805 class MRound : public MUnaryInstruction, public FloatingPointPolicy<0>::Data {
   8806  explicit MRound(MDefinition* num) : MUnaryInstruction(classOpcode, num) {
   8807    setResultType(MIRType::Int32);
   8808    specialization_ = MIRType::Double;
   8809    setMovable();
   8810  }
   8811 
   8812 public:
   8813  INSTRUCTION_HEADER(Round)
   8814  TRIVIAL_NEW_WRAPPERS
   8815 
   8816  AliasSet getAliasSet() const override { return AliasSet::None(); }
   8817 
   8818  bool isFloat32Commutative() const override { return true; }
   8819  void trySpecializeFloat32(TempAllocator& alloc) override;
   8820 #ifdef DEBUG
   8821  bool isConsistentFloat32Use(MUse* use) const override { return true; }
   8822 #endif
   8823  bool congruentTo(const MDefinition* ins) const override {
   8824    return congruentIfOperandsEqual(ins);
   8825  }
   8826 
   8827  [[nodiscard]] bool writeRecoverData(
   8828      CompactBufferWriter& writer) const override;
   8829  bool canRecoverOnBailout() const override { return true; }
   8830 
   8831  ALLOW_CLONE(MRound)
   8832 };
   8833 
   8834 // Inlined version of Math.trunc(double | float32) -> int32.
   8835 class MTrunc : public MUnaryInstruction, public FloatingPointPolicy<0>::Data {
   8836  explicit MTrunc(MDefinition* num) : MUnaryInstruction(classOpcode, num) {
   8837    setResultType(MIRType::Int32);
   8838    specialization_ = MIRType::Double;
   8839    setMovable();
   8840  }
   8841 
   8842 public:
   8843  INSTRUCTION_HEADER(Trunc)
   8844  TRIVIAL_NEW_WRAPPERS
   8845 
   8846  AliasSet getAliasSet() const override { return AliasSet::None(); }
   8847 
   8848  bool isFloat32Commutative() const override { return true; }
   8849  void trySpecializeFloat32(TempAllocator& alloc) override;
   8850 #ifdef DEBUG
   8851  bool isConsistentFloat32Use(MUse* use) const override { return true; }
   8852 #endif
   8853  bool congruentTo(const MDefinition* ins) const override {
   8854    return congruentIfOperandsEqual(ins);
   8855  }
   8856 
   8857  [[nodiscard]] bool writeRecoverData(
   8858      CompactBufferWriter& writer) const override;
   8859  bool canRecoverOnBailout() const override { return true; }
   8860 
   8861  ALLOW_CLONE(MTrunc)
   8862 };
   8863 
   8864 // NearbyInt rounds the floating-point input to the nearest integer, according
   8865 // to the RoundingMode.
   8866 class MNearbyInt : public MUnaryInstruction,
   8867                   public FloatingPointPolicy<0>::Data {
   8868  RoundingMode roundingMode_;
   8869 
   8870  explicit MNearbyInt(MDefinition* num, MIRType resultType,
   8871                      RoundingMode roundingMode)
   8872      : MUnaryInstruction(classOpcode, num), roundingMode_(roundingMode) {
   8873    MOZ_ASSERT(HasAssemblerSupport(roundingMode));
   8874 
   8875    MOZ_ASSERT(IsFloatingPointType(resultType));
   8876    setResultType(resultType);
   8877    specialization_ = resultType;
   8878 
   8879    setMovable();
   8880  }
   8881 
   8882 public:
   8883  INSTRUCTION_HEADER(NearbyInt)
   8884  TRIVIAL_NEW_WRAPPERS
   8885 
   8886  static bool HasAssemblerSupport(RoundingMode mode) {
   8887    return Assembler::HasRoundInstruction(mode);
   8888  }
   8889 
   8890  RoundingMode roundingMode() const { return roundingMode_; }
   8891 
   8892  AliasSet getAliasSet() const override { return AliasSet::None(); }
   8893 
   8894  bool isFloat32Commutative() const override { return true; }
   8895  void trySpecializeFloat32(TempAllocator& alloc) override;
   8896 #ifdef DEBUG
   8897  bool isConsistentFloat32Use(MUse* use) const override { return true; }
   8898 #endif
   8899 
   8900  bool congruentTo(const MDefinition* ins) const override {
   8901    return congruentIfOperandsEqual(ins) &&
   8902           ins->toNearbyInt()->roundingMode() == roundingMode_;
   8903  }
   8904 
   8905 #ifdef JS_JITSPEW
   8906  void printOpcode(GenericPrinter& out) const override;
   8907 #endif
   8908 
   8909  [[nodiscard]] bool writeRecoverData(
   8910      CompactBufferWriter& writer) const override;
   8911 
   8912  bool canRecoverOnBailout() const override {
   8913    switch (roundingMode_) {
   8914      case RoundingMode::Up:
   8915      case RoundingMode::Down:
   8916      case RoundingMode::TowardsZero:
   8917        return true;
   8918      default:
   8919        return false;
   8920    }
   8921  }
   8922 
   8923  ALLOW_CLONE(MNearbyInt)
   8924 };
   8925 
   8926 // Inlined version of Math.round(double) -> double.
   8927 class MRoundToDouble : public MUnaryInstruction,
   8928                       public FloatingPointPolicy<0>::Data {
   8929  explicit MRoundToDouble(MDefinition* num)
   8930      : MUnaryInstruction(classOpcode, num) {
   8931    MOZ_ASSERT(HasAssemblerSupport());
   8932 
   8933    setResultType(MIRType::Double);
   8934    specialization_ = MIRType::Double;
   8935 
   8936    setMovable();
   8937  }
   8938 
   8939 public:
   8940  INSTRUCTION_HEADER(RoundToDouble)
   8941  TRIVIAL_NEW_WRAPPERS
   8942 
   8943  static bool HasAssemblerSupport() {
   8944    // Requires Ceil support in the assembler.
   8945    return Assembler::HasRoundInstruction(RoundingMode::Up);
   8946  }
   8947 
   8948  AliasSet getAliasSet() const override { return AliasSet::None(); }
   8949 
   8950  bool isFloat32Commutative() const override { return true; }
   8951  void trySpecializeFloat32(TempAllocator& alloc) override;
   8952 #ifdef DEBUG
   8953  bool isConsistentFloat32Use(MUse* use) const override { return true; }
   8954 #endif
   8955 
   8956  bool congruentTo(const MDefinition* ins) const override {
   8957    return congruentIfOperandsEqual(ins);
   8958  }
   8959 
   8960  [[nodiscard]] bool writeRecoverData(
   8961      CompactBufferWriter& writer) const override;
   8962 
   8963  bool canRecoverOnBailout() const override { return true; }
   8964 
   8965  ALLOW_CLONE(MRoundToDouble)
   8966 };
   8967 
   8968 class MGetIteratorCache : public MUnaryInstruction,
   8969                          public BoxExceptPolicy<0, MIRType::Object>::Data {
   8970  explicit MGetIteratorCache(MDefinition* val)
   8971      : MUnaryInstruction(classOpcode, val) {
   8972    setResultType(MIRType::Object);
   8973  }
   8974 
   8975 public:
   8976  INSTRUCTION_HEADER(GetIteratorCache)
   8977  TRIVIAL_NEW_WRAPPERS
   8978  NAMED_OPERANDS((0, value))
   8979 };
   8980 
   8981 // Implementation for 'in' operator using instruction cache
   8982 class MInCache : public MBinaryInstruction,
   8983                 public MixPolicy<CacheIdPolicy<0>, ObjectPolicy<1>>::Data {
   8984  MInCache(MDefinition* key, MDefinition* obj)
   8985      : MBinaryInstruction(classOpcode, key, obj) {
   8986    setResultType(MIRType::Boolean);
   8987  }
   8988 
   8989 public:
   8990  INSTRUCTION_HEADER(InCache)
   8991  TRIVIAL_NEW_WRAPPERS
   8992  NAMED_OPERANDS((0, key), (1, object))
   8993 };
   8994 
   8995 // Test whether the index is in the array bounds or a hole.
   8996 class MInArray : public MTernaryInstruction, public NoTypePolicy::Data {
   8997  bool needsNegativeIntCheck_ = true;
   8998 
   8999  MInArray(MDefinition* elements, MDefinition* index, MDefinition* initLength)
   9000      : MTernaryInstruction(classOpcode, elements, index, initLength) {
   9001    setResultType(MIRType::Boolean);
   9002    setMovable();
   9003 
   9004    // Set the guard flag to make sure we bail when we see a negative index.
   9005    // We can clear this flag (and needsNegativeIntCheck_) in
   9006    // collectRangeInfoPreTrunc.
   9007    setGuard();
   9008 
   9009    MOZ_ASSERT(elements->type() == MIRType::Elements);
   9010    MOZ_ASSERT(index->type() == MIRType::Int32);
   9011    MOZ_ASSERT(initLength->type() == MIRType::Int32);
   9012  }
   9013 
   9014 public:
   9015  INSTRUCTION_HEADER(InArray)
   9016  TRIVIAL_NEW_WRAPPERS
   9017  NAMED_OPERANDS((0, elements), (1, index), (2, initLength))
   9018 
   9019  bool needsNegativeIntCheck() const { return needsNegativeIntCheck_; }
   9020  void collectRangeInfoPreTrunc() override;
   9021  AliasSet getAliasSet() const override {
   9022    return AliasSet::Load(AliasSet::Element);
   9023  }
   9024  bool congruentTo(const MDefinition* ins) const override {
   9025    if (!ins->isInArray()) {
   9026      return false;
   9027    }
   9028    const MInArray* other = ins->toInArray();
   9029    if (needsNegativeIntCheck() != other->needsNegativeIntCheck()) {
   9030      return false;
   9031    }
   9032    return congruentIfOperandsEqual(other);
   9033  }
   9034 };
   9035 
   9036 // Bail when the element is a hole.
   9037 class MGuardElementNotHole : public MBinaryInstruction,
   9038                             public NoTypePolicy::Data {
   9039  MGuardElementNotHole(MDefinition* elements, MDefinition* index)
   9040      : MBinaryInstruction(classOpcode, elements, index) {
   9041    setMovable();
   9042    setGuard();
   9043    MOZ_ASSERT(elements->type() == MIRType::Elements);
   9044    MOZ_ASSERT(index->type() == MIRType::Int32);
   9045  }
   9046 
   9047 public:
   9048  INSTRUCTION_HEADER(GuardElementNotHole)
   9049  TRIVIAL_NEW_WRAPPERS
   9050  NAMED_OPERANDS((0, elements), (1, index))
   9051 
   9052  AliasSet getAliasSet() const override {
   9053    return AliasSet::Load(AliasSet::Element);
   9054  }
   9055  bool congruentTo(const MDefinition* ins) const override {
   9056    return congruentIfOperandsEqual(ins);
   9057  }
   9058 };
   9059 
   9060 class MCheckPrivateFieldCache
   9061    : public MBinaryInstruction,
   9062      public MixPolicy<BoxExceptPolicy<0, MIRType::Object>,
   9063                       CacheIdPolicy<1>>::Data {
   9064  MCheckPrivateFieldCache(MDefinition* obj, MDefinition* id)
   9065      : MBinaryInstruction(classOpcode, obj, id) {
   9066    setResultType(MIRType::Boolean);
   9067  }
   9068 
   9069 public:
   9070  INSTRUCTION_HEADER(CheckPrivateFieldCache)
   9071  TRIVIAL_NEW_WRAPPERS
   9072  NAMED_OPERANDS((0, value), (1, idval))
   9073 };
   9074 
   9075 class MHasOwnCache : public MBinaryInstruction,
   9076                     public MixPolicy<BoxExceptPolicy<0, MIRType::Object>,
   9077                                      CacheIdPolicy<1>>::Data {
   9078  MHasOwnCache(MDefinition* obj, MDefinition* id)
   9079      : MBinaryInstruction(classOpcode, obj, id) {
   9080    setResultType(MIRType::Boolean);
   9081  }
   9082 
   9083 public:
   9084  INSTRUCTION_HEADER(HasOwnCache)
   9085  TRIVIAL_NEW_WRAPPERS
   9086  NAMED_OPERANDS((0, value), (1, idval))
   9087 };
   9088 
   9089 // Implementation for instanceof operator with specific rhs.
   9090 class MInstanceOf : public MBinaryInstruction,
   9091                    public MixPolicy<BoxExceptPolicy<0, MIRType::Object>,
   9092                                     ObjectPolicy<1>>::Data {
   9093  MInstanceOf(MDefinition* obj, MDefinition* proto)
   9094      : MBinaryInstruction(classOpcode, obj, proto) {
   9095    setResultType(MIRType::Boolean);
   9096  }
   9097 
   9098 public:
   9099  INSTRUCTION_HEADER(InstanceOf)
   9100  TRIVIAL_NEW_WRAPPERS
   9101 };
   9102 
   9103 // Given a value being written to another object, update the generational store
   9104 // buffer if the value is in the nursery and object is in the tenured heap.
   9105 class MPostWriteBarrier : public MBinaryInstruction,
   9106                          public ObjectPolicy<0>::Data {
   9107  MPostWriteBarrier(MDefinition* obj, MDefinition* value)
   9108      : MBinaryInstruction(classOpcode, obj, value) {
   9109    setGuard();
   9110  }
   9111 
   9112 public:
   9113  INSTRUCTION_HEADER(PostWriteBarrier)
   9114  TRIVIAL_NEW_WRAPPERS
   9115  NAMED_OPERANDS((0, object), (1, value))
   9116 
   9117  AliasSet getAliasSet() const override { return AliasSet::None(); }
   9118 
   9119 #ifdef DEBUG
   9120  bool isConsistentFloat32Use(MUse* use) const override {
   9121    // During lowering, values that neither have object nor value MIR type
   9122    // are ignored, thus Float32 can show up at this point without any issue.
   9123    return use == getUseFor(1);
   9124  }
   9125 #endif
   9126 
   9127  ALLOW_CLONE(MPostWriteBarrier)
   9128 };
   9129 
   9130 // Given a value being written to another object's elements at the specified
   9131 // index, update the generational store buffer if the value is in the nursery
   9132 // and object is in the tenured heap.
   9133 class MPostWriteElementBarrier
   9134    : public MTernaryInstruction,
   9135      public MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<2>>::Data {
   9136  MPostWriteElementBarrier(MDefinition* obj, MDefinition* value,
   9137                           MDefinition* index)
   9138      : MTernaryInstruction(classOpcode, obj, value, index) {
   9139    setGuard();
   9140  }
   9141 
   9142 public:
   9143  INSTRUCTION_HEADER(PostWriteElementBarrier)
   9144  TRIVIAL_NEW_WRAPPERS
   9145  NAMED_OPERANDS((0, object), (1, value), (2, index))
   9146 
   9147  AliasSet getAliasSet() const override { return AliasSet::None(); }
   9148 
   9149 #ifdef DEBUG
   9150  bool isConsistentFloat32Use(MUse* use) const override {
   9151    // During lowering, values that neither have object nor value MIR type
   9152    // are ignored, thus Float32 can show up at this point without any issue.
   9153    return use == getUseFor(1);
   9154  }
   9155 #endif
   9156 
   9157  ALLOW_CLONE(MPostWriteElementBarrier)
   9158 };
   9159 
   9160 class MNewCallObject : public MUnaryInstruction,
   9161                       public SingleObjectPolicy::Data {
   9162  gc::Heap initialHeap_;
   9163 
   9164 public:
   9165  INSTRUCTION_HEADER(NewCallObject)
   9166  TRIVIAL_NEW_WRAPPERS
   9167 
   9168  explicit MNewCallObject(MConstant* templateObj, gc::Heap initialHeap)
   9169      : MUnaryInstruction(classOpcode, templateObj), initialHeap_(initialHeap) {
   9170    setResultType(MIRType::Object);
   9171  }
   9172 
   9173  CallObject* templateObject() const {
   9174    return &getOperand(0)->toConstant()->toObject().as<CallObject>();
   9175  }
   9176  gc::Heap initialHeap() const { return initialHeap_; }
   9177 
   9178  AliasSet getAliasSet() const override { return AliasSet::None(); }
   9179 
   9180  [[nodiscard]] bool writeRecoverData(
   9181      CompactBufferWriter& writer) const override;
   9182  bool canRecoverOnBailout() const override { return true; }
   9183 };
   9184 
   9185 class MNewStringObject : public MUnaryInstruction,
   9186                         public ConvertToStringPolicy<0>::Data {
   9187  CompilerObject templateObj_;
   9188 
   9189  MNewStringObject(MDefinition* input, JSObject* templateObj)
   9190      : MUnaryInstruction(classOpcode, input), templateObj_(templateObj) {
   9191    setResultType(MIRType::Object);
   9192  }
   9193 
   9194 public:
   9195  INSTRUCTION_HEADER(NewStringObject)
   9196  TRIVIAL_NEW_WRAPPERS
   9197 
   9198  StringObject* templateObj() const;
   9199 };
   9200 
   9201 // This is an alias for MLoadFixedSlot.
   9202 class MEnclosingEnvironment : public MLoadFixedSlot {
   9203  explicit MEnclosingEnvironment(MDefinition* obj)
   9204      : MLoadFixedSlot(obj, EnvironmentObject::enclosingEnvironmentSlot()) {
   9205    setResultType(MIRType::Object);
   9206  }
   9207 
   9208 public:
   9209  static MEnclosingEnvironment* New(TempAllocator& alloc, MDefinition* obj) {
   9210    return new (alloc) MEnclosingEnvironment(obj);
   9211  }
   9212 
   9213  AliasSet getAliasSet() const override {
   9214    // EnvironmentObject reserved slots are immutable.
   9215    return AliasSet::None();
   9216  }
   9217 };
   9218 
   9219 // This is an element of a spaghetti stack which is used to represent the memory
   9220 // context which has to be restored in case of a bailout.
   9221 struct MStoreToRecover : public TempObject,
   9222                         public InlineSpaghettiStackNode<MStoreToRecover> {
   9223  MDefinition* operand;
   9224 
   9225  explicit MStoreToRecover(MDefinition* operand) : operand(operand) {}
   9226 };
   9227 
   9228 using MStoresToRecoverList = InlineSpaghettiStack<MStoreToRecover>;
   9229 
   9230 // A resume point contains the information needed to reconstruct the Baseline
   9231 // Interpreter state from a position in Warp JIT code. A resume point is a
   9232 // mapping of stack slots to MDefinitions.
   9233 //
   9234 // We capture stack state at critical points:
   9235 //   * (1) At the beginning of every basic block.
   9236 //   * (2) After every effectful operation.
   9237 //
   9238 // As long as these two properties are maintained, instructions can be moved,
   9239 // hoisted, or, eliminated without problems, and ops without side effects do not
   9240 // need to worry about capturing state at precisely the right point in time.
   9241 //
   9242 // Effectful instructions, of course, need to capture state after completion,
   9243 // where the interpreter will not attempt to repeat the operation. For this,
   9244 // ResumeAfter must be used. The state is attached directly to the effectful
   9245 // instruction to ensure that no intermediate instructions could be injected
   9246 // in between by a future analysis pass.
   9247 //
   9248 // During LIR construction, if an instruction can bail back to the interpreter,
   9249 // we create an LSnapshot, which uses the last known resume point to request
   9250 // register/stack assignments for every live value.
   9251 class MResumePoint final : public MNode
   9252 #ifdef DEBUG
   9253    ,
   9254                           public InlineForwardListNode<MResumePoint>
   9255 #endif
   9256 {
   9257 private:
   9258  friend class MBasicBlock;
   9259  friend void AssertBasicGraphCoherency(MIRGraph& graph, bool force);
   9260 
   9261  // List of stack slots needed to reconstruct the BaselineFrame.
   9262  FixedList<MUse> operands_;
   9263 
   9264  // List of stores needed to reconstruct the content of objects which are
   9265  // emulated by EmulateStateOf variants.
   9266  MStoresToRecoverList stores_;
   9267 
   9268  jsbytecode* pc_;
   9269  MInstruction* instruction_;
   9270  ResumeMode mode_;
   9271  bool isDiscarded_ = false;
   9272 
   9273  MResumePoint(MBasicBlock* block, jsbytecode* pc, ResumeMode mode);
   9274  void inherit(MBasicBlock* state);
   9275 
   9276  // Calling isDefinition or isResumePoint on MResumePoint is unnecessary.
   9277  bool isDefinition() const = delete;
   9278  bool isResumePoint() const = delete;
   9279 
   9280  void setBlock(MBasicBlock* block) {
   9281    setBlockAndKind(block, Kind::ResumePoint);
   9282  }
   9283 
   9284 protected:
   9285  // Initializes operands_ to an empty array of a fixed length.
   9286  // The array may then be filled in by inherit().
   9287  [[nodiscard]] bool init(TempAllocator& alloc);
   9288 
   9289  void clearOperand(size_t index) {
   9290    // FixedList doesn't initialize its elements, so do an unchecked init.
   9291    operands_[index].initUncheckedWithoutProducer(this);
   9292  }
   9293 
   9294  MUse* getUseFor(size_t index) override { return &operands_[index]; }
   9295  const MUse* getUseFor(size_t index) const override {
   9296    return &operands_[index];
   9297  }
   9298 
   9299 public:
   9300  static MResumePoint* New(TempAllocator& alloc, MBasicBlock* block,
   9301                           jsbytecode* pc, ResumeMode mode);
   9302 
   9303  MBasicBlock* block() const { return resumePointBlock(); }
   9304 
   9305  size_t numAllocatedOperands() const { return operands_.length(); }
   9306  uint32_t stackDepth() const { return numAllocatedOperands(); }
   9307  size_t numOperands() const override { return numAllocatedOperands(); }
   9308  size_t indexOf(const MUse* u) const final {
   9309    MOZ_ASSERT(u >= &operands_[0]);
   9310    MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
   9311    return u - &operands_[0];
   9312  }
   9313  void initOperand(size_t index, MDefinition* operand) {
   9314    // FixedList doesn't initialize its elements, so do an unchecked init.
   9315    operands_[index].initUnchecked(operand, this);
   9316  }
   9317  void replaceOperand(size_t index, MDefinition* operand) final {
   9318    operands_[index].replaceProducer(operand);
   9319  }
   9320 
   9321  bool isObservableOperand(MUse* u) const;
   9322  bool isObservableOperand(size_t index) const;
   9323  bool isRecoverableOperand(MUse* u) const;
   9324 
   9325  MDefinition* getOperand(size_t index) const override {
   9326    return operands_[index].producer();
   9327  }
   9328  jsbytecode* pc() const { return pc_; }
   9329  MResumePoint* caller() const;
   9330  uint32_t frameCount() const {
   9331    uint32_t count = 1;
   9332    for (MResumePoint* it = caller(); it; it = it->caller()) {
   9333      count++;
   9334    }
   9335    return count;
   9336  }
   9337  MInstruction* instruction() { return instruction_; }
   9338  void setInstruction(MInstruction* ins) {
   9339    MOZ_ASSERT(!instruction_);
   9340    instruction_ = ins;
   9341  }
   9342  void resetInstruction() {
   9343    MOZ_ASSERT(instruction_);
   9344    instruction_ = nullptr;
   9345  }
   9346  ResumeMode mode() const { return mode_; }
   9347 
   9348  void releaseUses() {
   9349    for (size_t i = 0, e = numOperands(); i < e; i++) {
   9350      if (operands_[i].hasProducer()) {
   9351        operands_[i].releaseProducer();
   9352      }
   9353    }
   9354  }
   9355 
   9356  [[nodiscard]] bool writeRecoverData(
   9357      CompactBufferWriter& writer) const override;
   9358 
   9359  // Register a store instruction on the current resume point. This
   9360  // instruction would be recovered when we are bailing out. The |cache|
   9361  // argument can be any resume point, it is used to share memory if we are
   9362  // doing the same modification.
   9363  void addStore(TempAllocator& alloc, MDefinition* store,
   9364                const MResumePoint* cache = nullptr);
   9365 
   9366  MStoresToRecoverList::iterator storesBegin() const { return stores_.begin(); }
   9367  MStoresToRecoverList::iterator storesEnd() const { return stores_.end(); }
   9368  bool storesEmpty() const { return stores_.empty(); }
   9369 
   9370  void setDiscarded() { isDiscarded_ = true; }
   9371  bool isDiscarded() const { return isDiscarded_; }
   9372 
   9373 #ifdef JS_JITSPEW
   9374  virtual void dump(GenericPrinter& out) const override;
   9375  virtual void dump() const override;
   9376 #endif
   9377 };
   9378 
   9379 class MIsCallable : public MUnaryInstruction,
   9380                    public BoxExceptPolicy<0, MIRType::Object>::Data {
   9381  explicit MIsCallable(MDefinition* object)
   9382      : MUnaryInstruction(classOpcode, object) {
   9383    setResultType(MIRType::Boolean);
   9384    setMovable();
   9385  }
   9386 
   9387 public:
   9388  INSTRUCTION_HEADER(IsCallable)
   9389  TRIVIAL_NEW_WRAPPERS
   9390  NAMED_OPERANDS((0, object))
   9391 
   9392  bool congruentTo(const MDefinition* ins) const override {
   9393    return congruentIfOperandsEqual(ins);
   9394  }
   9395 
   9396  MDefinition* foldsTo(TempAllocator& alloc) override;
   9397  AliasSet getAliasSet() const override { return AliasSet::None(); }
   9398 };
   9399 
   9400 class MHasClass : public MUnaryInstruction, public SingleObjectPolicy::Data {
   9401  const JSClass* class_;
   9402 
   9403  MHasClass(MDefinition* object, const JSClass* clasp)
   9404      : MUnaryInstruction(classOpcode, object), class_(clasp) {
   9405    MOZ_ASSERT(object->type() == MIRType::Object);
   9406    setResultType(MIRType::Boolean);
   9407    setMovable();
   9408  }
   9409 
   9410 public:
   9411  INSTRUCTION_HEADER(HasClass)
   9412  TRIVIAL_NEW_WRAPPERS
   9413  NAMED_OPERANDS((0, object))
   9414 
   9415  const JSClass* getClass() const { return class_; }
   9416 
   9417  MDefinition* foldsTo(TempAllocator& alloc) override;
   9418  AliasSet getAliasSet() const override { return AliasSet::None(); }
   9419  bool congruentTo(const MDefinition* ins) const override {
   9420    if (!ins->isHasClass()) {
   9421      return false;
   9422    }
   9423    if (getClass() != ins->toHasClass()->getClass()) {
   9424      return false;
   9425    }
   9426    return congruentIfOperandsEqual(ins);
   9427  }
   9428 };
   9429 
   9430 class MGuardToClass : public MUnaryInstruction,
   9431                      public SingleObjectPolicy::Data {
   9432  const JSClass* class_;
   9433 
   9434  MGuardToClass(MDefinition* object, const JSClass* clasp)
   9435      : MUnaryInstruction(classOpcode, object), class_(clasp) {
   9436    MOZ_ASSERT(object->type() == MIRType::Object);
   9437    MOZ_ASSERT(!clasp->isJSFunction(), "Use MGuardToFunction instead");
   9438    setResultType(MIRType::Object);
   9439    setMovable();
   9440 
   9441    // We will bail out if the class type is incorrect, so we need to ensure we
   9442    // don't eliminate this instruction
   9443    setGuard();
   9444  }
   9445 
   9446 public:
   9447  INSTRUCTION_HEADER(GuardToClass)
   9448  TRIVIAL_NEW_WRAPPERS
   9449  NAMED_OPERANDS((0, object))
   9450 
   9451  const JSClass* getClass() const { return class_; }
   9452  bool isArgumentsObjectClass() const {
   9453    return class_ == &MappedArgumentsObject::class_ ||
   9454           class_ == &UnmappedArgumentsObject::class_;
   9455  }
   9456 
   9457  MDefinition* foldsTo(TempAllocator& alloc) override;
   9458  AliasSet getAliasSet() const override { return AliasSet::None(); }
   9459  bool congruentTo(const MDefinition* ins) const override {
   9460    if (!ins->isGuardToClass()) {
   9461      return false;
   9462    }
   9463    if (getClass() != ins->toGuardToClass()->getClass()) {
   9464      return false;
   9465    }
   9466    return congruentIfOperandsEqual(ins);
   9467  }
   9468 };
   9469 
   9470 class MGuardToFunction : public MUnaryInstruction,
   9471                         public SingleObjectPolicy::Data {
   9472  explicit MGuardToFunction(MDefinition* object)
   9473      : MUnaryInstruction(classOpcode, object) {
   9474    MOZ_ASSERT(object->type() == MIRType::Object);
   9475    setResultType(MIRType::Object);
   9476    setMovable();
   9477 
   9478    // We will bail out if the class type is incorrect, so we need to ensure we
   9479    // don't eliminate this instruction
   9480    setGuard();
   9481  }
   9482 
   9483 public:
   9484  INSTRUCTION_HEADER(GuardToFunction)
   9485  TRIVIAL_NEW_WRAPPERS
   9486  NAMED_OPERANDS((0, object))
   9487 
   9488  MDefinition* foldsTo(TempAllocator& alloc) override;
   9489  AliasSet getAliasSet() const override { return AliasSet::None(); }
   9490  bool congruentTo(const MDefinition* ins) const override {
   9491    if (!ins->isGuardToFunction()) {
   9492      return false;
   9493    }
   9494    return congruentIfOperandsEqual(ins);
   9495  }
   9496 };
   9497 
   9498 // Note: we might call a proxy trap, so this instruction is effectful.
   9499 class MIsArray : public MUnaryInstruction,
   9500                 public BoxExceptPolicy<0, MIRType::Object>::Data {
   9501  explicit MIsArray(MDefinition* value)
   9502      : MUnaryInstruction(classOpcode, value) {
   9503    setResultType(MIRType::Boolean);
   9504  }
   9505 
   9506 public:
   9507  INSTRUCTION_HEADER(IsArray)
   9508  TRIVIAL_NEW_WRAPPERS
   9509  NAMED_OPERANDS((0, value))
   9510 
   9511  MDefinition* foldsTo(TempAllocator& alloc) override;
   9512 };
   9513 
   9514 class MIsTypedArray : public MUnaryInstruction,
   9515                      public SingleObjectPolicy::Data {
   9516  bool possiblyWrapped_;
   9517 
   9518  explicit MIsTypedArray(MDefinition* value, bool possiblyWrapped)
   9519      : MUnaryInstruction(classOpcode, value),
   9520        possiblyWrapped_(possiblyWrapped) {
   9521    setResultType(MIRType::Boolean);
   9522 
   9523    if (possiblyWrapped) {
   9524      // Proxy checks may throw, so we're neither removable nor movable.
   9525      setGuard();
   9526    } else {
   9527      setMovable();
   9528    }
   9529  }
   9530 
   9531 public:
   9532  INSTRUCTION_HEADER(IsTypedArray)
   9533  TRIVIAL_NEW_WRAPPERS
   9534  NAMED_OPERANDS((0, value))
   9535 
   9536  bool isPossiblyWrapped() const { return possiblyWrapped_; }
   9537  AliasSet getAliasSet() const override {
   9538    if (isPossiblyWrapped()) {
   9539      return AliasSet::Store(AliasSet::Any);
   9540    }
   9541    return AliasSet::None();
   9542  }
   9543 };
   9544 
   9545 // Allocate the generator object for a frame.
   9546 class MGenerator : public MTernaryInstruction,
   9547                   public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>>::Data {
   9548  explicit MGenerator(MDefinition* callee, MDefinition* environmentChain,
   9549                      MDefinition* argsObject)
   9550      : MTernaryInstruction(classOpcode, callee, environmentChain, argsObject) {
   9551    setResultType(MIRType::Object);
   9552  };
   9553 
   9554 public:
   9555  INSTRUCTION_HEADER(Generator)
   9556  TRIVIAL_NEW_WRAPPERS
   9557  NAMED_OPERANDS((0, callee), (1, environmentChain), (2, argsObject))
   9558 };
   9559 
   9560 class MMaybeExtractAwaitValue : public MBinaryInstruction,
   9561                                public BoxPolicy<0>::Data {
   9562  explicit MMaybeExtractAwaitValue(MDefinition* value, MDefinition* canSkip)
   9563      : MBinaryInstruction(classOpcode, value, canSkip) {
   9564    setResultType(MIRType::Value);
   9565  }
   9566 
   9567 public:
   9568  INSTRUCTION_HEADER(MaybeExtractAwaitValue)
   9569  TRIVIAL_NEW_WRAPPERS
   9570  NAMED_OPERANDS((0, value), (1, canSkip))
   9571 };
   9572 
   9573 class MAtomicIsLockFree : public MUnaryInstruction,
   9574                          public ConvertToInt32Policy<0>::Data {
   9575  explicit MAtomicIsLockFree(MDefinition* value)
   9576      : MUnaryInstruction(classOpcode, value) {
   9577    setResultType(MIRType::Boolean);
   9578    setMovable();
   9579  }
   9580 
   9581 public:
   9582  INSTRUCTION_HEADER(AtomicIsLockFree)
   9583  TRIVIAL_NEW_WRAPPERS
   9584 
   9585  MDefinition* foldsTo(TempAllocator& alloc) override;
   9586 
   9587  AliasSet getAliasSet() const override { return AliasSet::None(); }
   9588 
   9589  bool congruentTo(const MDefinition* ins) const override {
   9590    return congruentIfOperandsEqual(ins);
   9591  }
   9592 
   9593  [[nodiscard]] bool writeRecoverData(
   9594      CompactBufferWriter& writer) const override;
   9595  bool canRecoverOnBailout() const override { return true; }
   9596 
   9597  ALLOW_CLONE(MAtomicIsLockFree)
   9598 };
   9599 
   9600 class MCompareExchangeTypedArrayElement
   9601    : public MQuaternaryInstruction,
   9602      public MixPolicy<TruncateToInt32OrToInt64Policy<2>,
   9603                       TruncateToInt32OrToInt64Policy<3>>::Data {
   9604  Scalar::Type arrayType_;
   9605 
   9606  explicit MCompareExchangeTypedArrayElement(MDefinition* elements,
   9607                                             MDefinition* index,
   9608                                             Scalar::Type arrayType,
   9609                                             MDefinition* oldval,
   9610                                             MDefinition* newval)
   9611      : MQuaternaryInstruction(classOpcode, elements, index, oldval, newval),
   9612        arrayType_(arrayType) {
   9613    MOZ_ASSERT(elements->type() == MIRType::Elements);
   9614    MOZ_ASSERT(index->type() == MIRType::IntPtr);
   9615    setGuard();  // Not removable
   9616  }
   9617 
   9618 public:
   9619  INSTRUCTION_HEADER(CompareExchangeTypedArrayElement)
   9620  TRIVIAL_NEW_WRAPPERS
   9621  NAMED_OPERANDS((0, elements), (1, index), (2, oldval), (3, newval))
   9622 
   9623  bool isByteArray() const {
   9624    return (arrayType_ == Scalar::Int8 || arrayType_ == Scalar::Uint8);
   9625  }
   9626  Scalar::Type arrayType() const { return arrayType_; }
   9627  AliasSet getAliasSet() const override {
   9628    return AliasSet::Store(AliasSet::UnboxedElement);
   9629  }
   9630 };
   9631 
   9632 class MAtomicExchangeTypedArrayElement
   9633    : public MTernaryInstruction,
   9634      public TruncateToInt32OrToInt64Policy<2>::Data {
   9635  Scalar::Type arrayType_;
   9636 
   9637  MAtomicExchangeTypedArrayElement(MDefinition* elements, MDefinition* index,
   9638                                   MDefinition* value, Scalar::Type arrayType)
   9639      : MTernaryInstruction(classOpcode, elements, index, value),
   9640        arrayType_(arrayType) {
   9641    MOZ_ASSERT(elements->type() == MIRType::Elements);
   9642    MOZ_ASSERT(index->type() == MIRType::IntPtr);
   9643    MOZ_ASSERT(arrayType <= Scalar::Uint32 || Scalar::isBigIntType(arrayType));
   9644    setGuard();  // Not removable
   9645  }
   9646 
   9647 public:
   9648  INSTRUCTION_HEADER(AtomicExchangeTypedArrayElement)
   9649  TRIVIAL_NEW_WRAPPERS
   9650  NAMED_OPERANDS((0, elements), (1, index), (2, value))
   9651 
   9652  bool isByteArray() const {
   9653    return (arrayType_ == Scalar::Int8 || arrayType_ == Scalar::Uint8);
   9654  }
   9655  Scalar::Type arrayType() const { return arrayType_; }
   9656  AliasSet getAliasSet() const override {
   9657    return AliasSet::Store(AliasSet::UnboxedElement);
   9658  }
   9659 };
   9660 
   9661 class MAtomicTypedArrayElementBinop
   9662    : public MTernaryInstruction,
   9663      public TruncateToInt32OrToInt64Policy<2>::Data {
   9664 private:
   9665  AtomicOp op_;
   9666  Scalar::Type arrayType_;
   9667  bool forEffect_;
   9668 
   9669  explicit MAtomicTypedArrayElementBinop(AtomicOp op, MDefinition* elements,
   9670                                         MDefinition* index,
   9671                                         Scalar::Type arrayType,
   9672                                         MDefinition* value, bool forEffect)
   9673      : MTernaryInstruction(classOpcode, elements, index, value),
   9674        op_(op),
   9675        arrayType_(arrayType),
   9676        forEffect_(forEffect) {
   9677    MOZ_ASSERT(elements->type() == MIRType::Elements);
   9678    MOZ_ASSERT(index->type() == MIRType::IntPtr);
   9679    MOZ_ASSERT(arrayType <= Scalar::Uint32 || Scalar::isBigIntType(arrayType));
   9680    setGuard();  // Not removable
   9681  }
   9682 
   9683 public:
   9684  INSTRUCTION_HEADER(AtomicTypedArrayElementBinop)
   9685  TRIVIAL_NEW_WRAPPERS
   9686  NAMED_OPERANDS((0, elements), (1, index), (2, value))
   9687 
   9688  bool isByteArray() const {
   9689    return (arrayType_ == Scalar::Int8 || arrayType_ == Scalar::Uint8);
   9690  }
   9691  AtomicOp operation() const { return op_; }
   9692  Scalar::Type arrayType() const { return arrayType_; }
   9693  bool isForEffect() const { return forEffect_; }
   9694  AliasSet getAliasSet() const override {
   9695    return AliasSet::Store(AliasSet::UnboxedElement);
   9696  }
   9697 };
   9698 
   9699 class MDebugger : public MNullaryInstruction {
   9700  MDebugger() : MNullaryInstruction(classOpcode) {
   9701    setBailoutKind(BailoutKind::Debugger);
   9702  }
   9703 
   9704 public:
   9705  INSTRUCTION_HEADER(Debugger)
   9706  TRIVIAL_NEW_WRAPPERS
   9707 };
   9708 
   9709 // Used to load the prototype of an object known to have
   9710 // a static prototype.
   9711 class MObjectStaticProto : public MUnaryInstruction,
   9712                           public SingleObjectPolicy::Data {
   9713  explicit MObjectStaticProto(MDefinition* object)
   9714      : MUnaryInstruction(classOpcode, object) {
   9715    setResultType(MIRType::Object);
   9716    setMovable();
   9717  }
   9718 
   9719 public:
   9720  INSTRUCTION_HEADER(ObjectStaticProto)
   9721  TRIVIAL_NEW_WRAPPERS
   9722  NAMED_OPERANDS((0, object))
   9723 
   9724  bool congruentTo(const MDefinition* ins) const override {
   9725    return congruentIfOperandsEqual(ins);
   9726  }
   9727 
   9728  AliasSet getAliasSet() const override {
   9729    return AliasSet::Load(AliasSet::ObjectFields);
   9730  }
   9731  AliasType mightAlias(const MDefinition* def) const override {
   9732    // These instructions never modify the [[Prototype]].
   9733    if (def->isAddAndStoreSlot() || def->isAllocateAndStoreSlot() ||
   9734        def->isStoreElementHole() || def->isArrayPush()) {
   9735      return AliasType::NoAlias;
   9736    }
   9737    return AliasType::MayAlias;
   9738  }
   9739 };
   9740 
   9741 class MConstantProto : public MUnaryInstruction,
   9742                       public SingleObjectPolicy::Data {
   9743  // NOTE: we're not going to actually use the underlying receiver object for
   9744  // anything. This is just here for giving extra information to MGuardShape
   9745  // to MGuardShape::mightAlias. Accordingly, we don't take it as an operand,
   9746  // but instead just keep a pointer to it. This means we need to ensure it's
   9747  // not discarded before we try to access it. If this is discarded, we
   9748  // basically just become an MConstant for the object's proto, which is fine.
   9749  const MDefinition* receiverObject_;
   9750 
   9751  explicit MConstantProto(MDefinition* protoObject,
   9752                          const MDefinition* receiverObject)
   9753      : MUnaryInstruction(classOpcode, protoObject),
   9754        receiverObject_(receiverObject) {
   9755    MOZ_ASSERT(protoObject->isConstant());
   9756    setResultType(MIRType::Object);
   9757    setMovable();
   9758  }
   9759 
   9760  ALLOW_CLONE(MConstantProto)
   9761 
   9762 public:
   9763  INSTRUCTION_HEADER(ConstantProto)
   9764  TRIVIAL_NEW_WRAPPERS
   9765  NAMED_OPERANDS((0, protoObject))
   9766 
   9767  HashNumber valueHash() const override;
   9768 
   9769  bool congruentTo(const MDefinition* ins) const override {
   9770    if (this == ins) {
   9771      return true;
   9772    }
   9773    const MDefinition* receiverObject = getReceiverObject();
   9774    return congruentIfOperandsEqual(ins) && receiverObject &&
   9775           receiverObject == ins->toConstantProto()->getReceiverObject();
   9776  }
   9777 
   9778  AliasSet getAliasSet() const override { return AliasSet::None(); }
   9779 
   9780  const MDefinition* getReceiverObject() const {
   9781    if (receiverObject_->isDiscarded()) {
   9782      return nullptr;
   9783    }
   9784    return receiverObject_;
   9785  }
   9786 };
   9787 
   9788 class MObjectToIterator : public MUnaryInstruction,
   9789                          public ObjectPolicy<0>::Data {
   9790  NativeIteratorListHead* enumeratorsAddr_;
   9791  bool wantsIndices_ = false;
   9792  bool skipRegistration_ = false;
   9793 
   9794  explicit MObjectToIterator(MDefinition* object,
   9795                             NativeIteratorListHead* enumeratorsAddr)
   9796      : MUnaryInstruction(classOpcode, object),
   9797        enumeratorsAddr_(enumeratorsAddr) {
   9798    setResultType(MIRType::Object);
   9799  }
   9800 
   9801 public:
   9802  NativeIteratorListHead* enumeratorsAddr() const { return enumeratorsAddr_; }
   9803  INSTRUCTION_HEADER(ObjectToIterator)
   9804  TRIVIAL_NEW_WRAPPERS
   9805  NAMED_OPERANDS((0, object))
   9806 
   9807  AliasSet getAliasSet() const override {
   9808    return skipRegistration_
   9809               ? AliasSet::Load(AliasSet::ObjectFields | AliasSet::Element)
   9810               : AliasSet::Store(AliasSet::Any);
   9811  }
   9812 
   9813  bool wantsIndices() const { return wantsIndices_; }
   9814  void setWantsIndices(bool value) { wantsIndices_ = value; }
   9815 
   9816  bool skipRegistration() const { return skipRegistration_; }
   9817  void setSkipRegistration(bool value) { skipRegistration_ = value; }
   9818 };
   9819 
   9820 class MPostIntPtrConversion : public MUnaryInstruction,
   9821                              public NoTypePolicy::Data {
   9822  explicit MPostIntPtrConversion(MDefinition* input)
   9823      : MUnaryInstruction(classOpcode, input) {
   9824    // Passes through the input.
   9825    setResultType(input->type());
   9826 
   9827    // Note: Must be non-movable so we can attach a resume point.
   9828  }
   9829 
   9830 public:
   9831  INSTRUCTION_HEADER(PostIntPtrConversion)
   9832  TRIVIAL_NEW_WRAPPERS
   9833 
   9834  AliasSet getAliasSet() const override { return AliasSet::None(); }
   9835 };
   9836 
   9837 class MCanonicalizeNaN : public MUnaryInstruction, public NoTypePolicy::Data {
   9838  explicit MCanonicalizeNaN(MDefinition* input)
   9839      : MUnaryInstruction(classOpcode, input) {
   9840    MOZ_ASSERT(IsFloatingPointType(input->type()));
   9841    setResultType(input->type());
   9842    setMovable();
   9843  }
   9844 
   9845 public:
   9846  INSTRUCTION_HEADER(CanonicalizeNaN)
   9847  TRIVIAL_NEW_WRAPPERS
   9848 
   9849  AliasSet getAliasSet() const override { return AliasSet::None(); }
   9850 
   9851  bool congruentTo(const MDefinition* ins) const override {
   9852    return congruentIfOperandsEqual(ins);
   9853  }
   9854 
   9855  bool canProduceFloat32() const override { return type() == MIRType::Float32; }
   9856 
   9857  ALLOW_CLONE(MCanonicalizeNaN)
   9858 };
   9859 
   9860 class MRotate : public MBinaryInstruction, public NoTypePolicy::Data {
   9861  bool isLeftRotate_;
   9862 
   9863  MRotate(MDefinition* input, MDefinition* count, MIRType type,
   9864          bool isLeftRotate)
   9865      : MBinaryInstruction(classOpcode, input, count),
   9866        isLeftRotate_(isLeftRotate) {
   9867    setMovable();
   9868    setResultType(type);
   9869    // Prevent reordering.  Although there's no problem eliding call result
   9870    // definitions, there's also no need, as they cause no codegen.
   9871    setGuard();
   9872  }
   9873 
   9874 public:
   9875  INSTRUCTION_HEADER(Rotate)
   9876  TRIVIAL_NEW_WRAPPERS
   9877  NAMED_OPERANDS((0, input), (1, count))
   9878 
   9879  AliasSet getAliasSet() const override { return AliasSet::None(); }
   9880  bool congruentTo(const MDefinition* ins) const override {
   9881    return congruentIfOperandsEqual(ins) &&
   9882           ins->toRotate()->isLeftRotate() == isLeftRotate_;
   9883  }
   9884 
   9885  bool isLeftRotate() const { return isLeftRotate_; }
   9886 
   9887  ALLOW_CLONE(MRotate)
   9888 };
   9889 
   9890 class MReinterpretCast : public MUnaryInstruction, public NoTypePolicy::Data {
   9891  MReinterpretCast(MDefinition* val, MIRType toType)
   9892      : MUnaryInstruction(classOpcode, val) {
   9893    switch (val->type()) {
   9894      case MIRType::Int32:
   9895        MOZ_ASSERT(toType == MIRType::Float32);
   9896        break;
   9897      case MIRType::Float32:
   9898        MOZ_ASSERT(toType == MIRType::Int32);
   9899        break;
   9900      case MIRType::Double:
   9901        MOZ_ASSERT(toType == MIRType::Int64);
   9902        break;
   9903      case MIRType::Int64:
   9904        MOZ_ASSERT(toType == MIRType::Double);
   9905        break;
   9906      default:
   9907        MOZ_CRASH("unexpected reinterpret conversion");
   9908    }
   9909    setMovable();
   9910    setResultType(toType);
   9911  }
   9912 
   9913 public:
   9914  INSTRUCTION_HEADER(ReinterpretCast)
   9915  TRIVIAL_NEW_WRAPPERS
   9916 
   9917  AliasSet getAliasSet() const override { return AliasSet::None(); }
   9918  bool congruentTo(const MDefinition* ins) const override {
   9919    // No need to check type() here, because congruentIfOperandsEqual will
   9920    // check it.
   9921    return congruentIfOperandsEqual(ins);
   9922  }
   9923 
   9924  ALLOW_CLONE(MReinterpretCast)
   9925 };
   9926 
   9927 // Used by MIR building to represent the bytecode result of an operation for
   9928 // which an MBail was generated, to balance the basic block's MDefinition stack.
   9929 class MUnreachableResult : public MNullaryInstruction {
   9930  explicit MUnreachableResult(MIRType type) : MNullaryInstruction(classOpcode) {
   9931    MOZ_ASSERT(type != MIRType::None);
   9932    setResultType(type);
   9933  }
   9934 
   9935 public:
   9936  INSTRUCTION_HEADER(UnreachableResult)
   9937  TRIVIAL_NEW_WRAPPERS
   9938 
   9939  bool congruentTo(const MDefinition* ins) const override {
   9940    return congruentIfOperandsEqual(ins);
   9941  }
   9942  AliasSet getAliasSet() const override { return AliasSet::None(); }
   9943 };
   9944 
   9945 #ifdef FUZZING_JS_FUZZILLI
   9946 class MFuzzilliHash : public MUnaryInstruction, public NoTypePolicy::Data {
   9947  explicit MFuzzilliHash(MDefinition* obj)
   9948      : MUnaryInstruction(classOpcode, obj) {
   9949    setResultType(MIRType::Int32);
   9950    setMovable();
   9951  }
   9952 
   9953 public:
   9954  INSTRUCTION_HEADER(FuzzilliHash);
   9955  TRIVIAL_NEW_WRAPPERS
   9956  ALLOW_CLONE(MFuzzilliHash)
   9957 
   9958 #  ifdef DEBUG
   9959  bool isConsistentFloat32Use(MUse* use) const override { return true; }
   9960 #  endif
   9961 
   9962  AliasSet getAliasSet() const override {
   9963    MDefinition* obj = getOperand(0);
   9964    if (obj->type() == MIRType::Object || obj->type() == MIRType::Value) {
   9965      return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
   9966                            AliasSet::DynamicSlot | AliasSet::Element |
   9967                            AliasSet::UnboxedElement);
   9968    }
   9969    return AliasSet::None();
   9970  }
   9971 };
   9972 
   9973 class MFuzzilliHashStore : public MUnaryInstruction, public NoTypePolicy::Data {
   9974  explicit MFuzzilliHashStore(MDefinition* obj)
   9975      : MUnaryInstruction(classOpcode, obj) {
   9976    MOZ_ASSERT(obj->type() == MIRType::Int32);
   9977 
   9978    setResultType(MIRType::None);
   9979  }
   9980 
   9981 public:
   9982  INSTRUCTION_HEADER(FuzzilliHashStore);
   9983  TRIVIAL_NEW_WRAPPERS
   9984  ALLOW_CLONE(MFuzzilliHashStore)
   9985 
   9986  // this is a store and hence effectful, however no other load can
   9987  // alias with the store
   9988  AliasSet getAliasSet() const override {
   9989    return AliasSet::Store(AliasSet::FuzzilliHash);
   9990  }
   9991 };
   9992 #endif
   9993 
   9994 void MUse::init(MDefinition* producer, MNode* consumer) {
   9995  MOZ_ASSERT(!consumer_, "Initializing MUse that already has a consumer");
   9996  MOZ_ASSERT(!producer_, "Initializing MUse that already has a producer");
   9997  initUnchecked(producer, consumer);
   9998 }
   9999 
  10000 void MUse::initUnchecked(MDefinition* producer, MNode* consumer) {
  10001  MOZ_ASSERT(consumer, "Initializing to null consumer");
  10002  consumer_ = consumer;
  10003  producer_ = producer;
  10004  producer_->addUseUnchecked(this);
  10005 }
  10006 
  10007 void MUse::initUncheckedWithoutProducer(MNode* consumer) {
  10008  MOZ_ASSERT(consumer, "Initializing to null consumer");
  10009  consumer_ = consumer;
  10010  producer_ = nullptr;
  10011 }
  10012 
  10013 void MUse::replaceProducer(MDefinition* producer) {
  10014  MOZ_ASSERT(consumer_, "Resetting MUse without a consumer");
  10015  producer_->removeUse(this);
  10016  producer_ = producer;
  10017  producer_->addUse(this);
  10018 }
  10019 
  10020 void MUse::releaseProducer() {
  10021  MOZ_ASSERT(consumer_, "Clearing MUse without a consumer");
  10022  producer_->removeUse(this);
  10023  producer_ = nullptr;
  10024 }
  10025 
  10026 // Implement cast functions now that the compiler can see the inheritance.
  10027 
  10028 MDefinition* MNode::toDefinition() {
  10029  MOZ_ASSERT(isDefinition());
  10030  return (MDefinition*)this;
  10031 }
  10032 
  10033 MResumePoint* MNode::toResumePoint() {
  10034  MOZ_ASSERT(isResumePoint());
  10035  return (MResumePoint*)this;
  10036 }
  10037 
  10038 MInstruction* MDefinition::toInstruction() {
  10039  MOZ_ASSERT(!isPhi());
  10040  return (MInstruction*)this;
  10041 }
  10042 
  10043 const MInstruction* MDefinition::toInstruction() const {
  10044  MOZ_ASSERT(!isPhi());
  10045  return (const MInstruction*)this;
  10046 }
  10047 
  10048 MControlInstruction* MDefinition::toControlInstruction() {
  10049  MOZ_ASSERT(isControlInstruction());
  10050  return (MControlInstruction*)this;
  10051 }
  10052 
  10053 MConstant* MDefinition::maybeConstantValue() {
  10054  MDefinition* op = this;
  10055  if (op->isBox()) {
  10056    op = op->toBox()->input();
  10057  }
  10058  if (op->isConstant()) {
  10059    return op->toConstant();
  10060  }
  10061  return nullptr;
  10062 }
  10063 
  10064 }  // namespace jit
  10065 }  // namespace js
  10066 
  10067 #endif /* jit_MIR_h */