js/src/jit/MIR.h
author Nicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Thu, 07 Sep 2017 13:01:13 +0000
changeset 379553 8b1881ead0b6
parent 376309 e634da7e61a9
child 379935 a3a57ed979fa
permissions -rw-r--r--
Bug 966743 - Inline Array.prototype.push with more than one argument. r=jandem

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * Everything needed to build actual MIR instructions: the actual opcodes and
 * instructions, the instruction interface, and use chains.
 */

#ifndef jit_MIR_h
#define jit_MIR_h

#include "mozilla/Alignment.h"
#include "mozilla/Array.h"
#include "mozilla/Attributes.h"
#include "mozilla/MacroForEach.h"

#include "builtin/SIMD.h"
#include "jit/AtomicOp.h"
#include "jit/BaselineIC.h"
#include "jit/FixedList.h"
#include "jit/InlineList.h"
#include "jit/JitAllocPolicy.h"
#include "jit/MacroAssembler.h"
#include "jit/MOpcodes.h"
#include "jit/TypedObjectPrediction.h"
#include "jit/TypePolicy.h"
#include "js/HeapAPI.h"
#include "vm/ArrayObject.h"
#include "vm/EnvironmentObject.h"
#include "vm/RegExpObject.h"
#include "vm/SharedMem.h"
#include "vm/TypedArrayObject.h"
#include "vm/UnboxedObject.h"

namespace js {

class StringObject;

namespace jit {

class BaselineInspector;
class Range;

template <typename T>
struct ResultWithOOM {
    T value;
    bool oom;

    static ResultWithOOM<T> ok(T val) {
        return { val, false };
    }
    static ResultWithOOM<T> fail() {
        return { T(), true };
    }
};

static inline
MIRType MIRTypeFromValue(const js::Value& vp)
{
    if (vp.isDouble())
        return MIRType::Double;
    if (vp.isMagic()) {
        switch (vp.whyMagic()) {
          case JS_OPTIMIZED_ARGUMENTS:
            return MIRType::MagicOptimizedArguments;
          case JS_OPTIMIZED_OUT:
            return MIRType::MagicOptimizedOut;
          case JS_ELEMENTS_HOLE:
            return MIRType::MagicHole;
          case JS_IS_CONSTRUCTING:
            return MIRType::MagicIsConstructing;
          case JS_UNINITIALIZED_LEXICAL:
            return MIRType::MagicUninitializedLexical;
          default:
            MOZ_ASSERT_UNREACHABLE("Unexpected magic constant");
        }
    }
    return MIRTypeFromValueType(vp.extractNonDoubleType());
}

// If simdType is one of the SIMD types suported by Ion, set mirType to the
// corresponding MIRType, and return true.
//
// If simdType is not suported by Ion, return false.
static inline MOZ_MUST_USE
bool MaybeSimdTypeToMIRType(SimdType type, MIRType* mirType)
{
    switch (type) {
      case SimdType::Uint32x4:
      case SimdType::Int32x4:     *mirType = MIRType::Int32x4;   return true;
      case SimdType::Uint16x8:
      case SimdType::Int16x8:     *mirType = MIRType::Int16x8;   return true;
      case SimdType::Uint8x16:
      case SimdType::Int8x16:     *mirType = MIRType::Int8x16;   return true;
      case SimdType::Float32x4:   *mirType = MIRType::Float32x4; return true;
      case SimdType::Bool32x4:    *mirType = MIRType::Bool32x4;  return true;
      case SimdType::Bool16x8:    *mirType = MIRType::Bool16x8;  return true;
      case SimdType::Bool8x16:    *mirType = MIRType::Bool8x16;  return true;
      default:                    return false;
    }
}

// Convert a SimdType to the corresponding MIRType, or crash.
//
// Note that this is not an injective mapping: SimdType has signed and unsigned
// integer types that map to the same MIRType.
static inline
MIRType SimdTypeToMIRType(SimdType type)
{
    MIRType ret = MIRType::None;
    JS_ALWAYS_TRUE(MaybeSimdTypeToMIRType(type, &ret));
    return ret;
}

static inline
SimdType MIRTypeToSimdType(MIRType type)
{
    switch (type) {
      case MIRType::Int32x4:   return SimdType::Int32x4;
      case MIRType::Int16x8:   return SimdType::Int16x8;
      case MIRType::Int8x16:   return SimdType::Int8x16;
      case MIRType::Float32x4: return SimdType::Float32x4;
      case MIRType::Bool32x4:  return SimdType::Bool32x4;
      case MIRType::Bool16x8:  return SimdType::Bool16x8;
      case MIRType::Bool8x16:  return SimdType::Bool8x16;
      default:                break;
    }
    MOZ_CRASH("unhandled MIRType");
}

// Get the boolean MIRType with the same shape as type.
static inline
MIRType MIRTypeToBooleanSimdType(MIRType type)
{
    return SimdTypeToMIRType(GetBooleanSimdType(MIRTypeToSimdType(type)));
}

#define MIR_FLAG_LIST(_)                                                        \
    _(InWorklist)                                                               \
    _(EmittedAtUses)                                                            \
    _(Commutative)                                                              \
    _(Movable)       /* Allow passes like LICM to move this instruction */      \
    _(Lowered)       /* (Debug only) has a virtual register */                  \
    _(Guard)         /* Not removable if uses == 0 */                           \
                                                                                \
    /* Flag an instruction to be considered as a Guard if the instructions
     * bails out on some inputs.
     *
     * Some optimizations can replace an instruction, and leave its operands
     * unused. When the type information of the operand got used as a
     * predicate of the transformation, then we have to flag the operands as
     * GuardRangeBailouts.
     *
     * This flag prevents further optimization of instructions, which
     * might remove the run-time checks (bailout conditions) used as a
     * predicate of the previous transformation.
     */                                                                         \
    _(GuardRangeBailouts)                                                       \
                                                                                \
    /* Keep the flagged instruction in resume points and do not substitute this
     * instruction by an UndefinedValue. This might be used by call inlining
     * when a function argument is not used by the inlined instructions.
     */                                                                         \
    _(ImplicitlyUsed)                                                           \
                                                                                \
    /* The instruction has been marked dead for lazy removal from resume
     * points.
     */                                                                         \
    _(Unused)                                                                   \
                                                                                \
    /* When a branch is removed, the uses of multiple instructions are removed.
     * The removal of branches is based on hypotheses.  These hypotheses might
     * fail, in which case we need to bailout from the current code.
     *
     * When we implement a destructive optimization, we need to consider the
     * failing cases, and consider the fact that we might resume the execution
     * into a branch which was removed from the compiler.  As such, a
     * destructive optimization need to take into acount removed branches.
     *
     * In order to let destructive optimizations know about removed branches, we
     * have to annotate instructions with the UseRemoved flag.  This flag
     * annotates instruction which were used in removed branches.
     */                                                                         \
    _(UseRemoved)                                                               \
                                                                                \
    /* Marks if the current instruction should go to the bailout paths instead
     * of producing code as part of the control flow.  This flag can only be set
     * on instructions which are only used by ResumePoint or by other flagged
     * instructions.
     */                                                                         \
    _(RecoveredOnBailout)                                                       \
                                                                                \
    /* Some instructions might represent an object, but the memory of these
     * objects might be incomplete if we have not recovered all the stores which
     * were supposed to happen before. This flag is used to annotate
     * instructions which might return a pointer to a memory area which is not
     * yet fully initialized. This flag is used to ensure that stores are
     * executed before returning the value.
     */                                                                         \
    _(IncompleteObject)                                                         \
                                                                                \
    /* The current instruction got discarded from the MIR Graph. This is useful
     * when we want to iterate over resume points and instructions, while
     * handling instructions which are discarded without reporting to the
     * iterator.
     */                                                                         \
    _(Discarded)

class MDefinition;
class MInstruction;
class MBasicBlock;
class MNode;
class MUse;
class MPhi;
class MIRGraph;
class MResumePoint;
class MControlInstruction;

// Represents a use of a node.
class MUse : public TempObject, public InlineListNode<MUse>
{
    // Grant access to setProducerUnchecked.
    friend class MDefinition;
    friend class MPhi;

    MDefinition* producer_; // MDefinition that is being used.
    MNode* consumer_;       // The node that is using this operand.

    // Low-level unchecked edit method for replaceAllUsesWith and
    // MPhi::removeOperand. This doesn't update use lists!
    // replaceAllUsesWith and MPhi::removeOperand do that manually.
    void setProducerUnchecked(MDefinition* producer) {
        MOZ_ASSERT(consumer_);
        MOZ_ASSERT(producer_);
        MOZ_ASSERT(producer);
        producer_ = producer;
    }

  public:
    // Default constructor for use in vectors.
    MUse()
      : producer_(nullptr), consumer_(nullptr)
    { }

    // Move constructor for use in vectors. When an MUse is moved, it stays
    // in its containing use list.
    MUse(MUse&& other)
      : InlineListNode<MUse>(mozilla::Move(other)),
        producer_(other.producer_), consumer_(other.consumer_)
    { }

    // Construct an MUse initialized with |producer| and |consumer|.
    MUse(MDefinition* producer, MNode* consumer)
    {
        initUnchecked(producer, consumer);
    }

    // Set this use, which was previously clear.
    inline void init(MDefinition* producer, MNode* consumer);
    // Like init, but works even when the use contains uninitialized data.
    inline void initUnchecked(MDefinition* producer, MNode* consumer);
    // Like initUnchecked, but set the producer to nullptr.
    inline void initUncheckedWithoutProducer(MNode* consumer);
    // Set this use, which was not previously clear.
    inline void replaceProducer(MDefinition* producer);
    // Clear this use.
    inline void releaseProducer();

    MDefinition* producer() const {
        MOZ_ASSERT(producer_ != nullptr);
        return producer_;
    }
    bool hasProducer() const {
        return producer_ != nullptr;
    }
    MNode* consumer() const {
        MOZ_ASSERT(consumer_ != nullptr);
        return consumer_;
    }

#ifdef DEBUG
    // Return the operand index of this MUse in its consumer. This is DEBUG-only
    // as normal code should instead to call indexOf on the casted consumer
    // directly, to allow it to be devirtualized and inlined.
    size_t index() const;
#endif
};

typedef InlineList<MUse>::iterator MUseIterator;

// A node is an entry in the MIR graph. It has two kinds:
//   MInstruction: an instruction which appears in the IR stream.
//   MResumePoint: a list of instructions that correspond to the state of the
//                 interpreter/Baseline stack.
//
// Nodes can hold references to MDefinitions. Each MDefinition has a list of
// nodes holding such a reference (its use chain).
class MNode : public TempObject
{
  protected:
    enum class Kind {
        Definition = 0,
        ResumePoint
    };

  private:
    static const uintptr_t KindMask = 0x1;
    uintptr_t blockAndKind_;

    Kind kind() const {
        return Kind(blockAndKind_ & KindMask);
    }

  protected:
    explicit MNode(const MNode& other)
      : blockAndKind_(other.blockAndKind_)
    { }

    MNode(MBasicBlock* block, Kind kind)
    {
        setBlockAndKind(block, kind);
    }

    void setBlockAndKind(MBasicBlock* block, Kind kind) {
        blockAndKind_ = uintptr_t(block) | uintptr_t(kind);
        MOZ_ASSERT(this->block() == block);
    }

    MBasicBlock* definitionBlock() const {
        MOZ_ASSERT(isDefinition());
        static_assert(unsigned(Kind::Definition) == 0, "Code below relies on low bit being 0");
        return reinterpret_cast<MBasicBlock*>(blockAndKind_);
    }
    MBasicBlock* resumePointBlock() const {
        MOZ_ASSERT(isResumePoint());
        static_assert(unsigned(Kind::ResumePoint) == 1, "Code below relies on low bit being 1");
        // Use a subtraction: if the caller does block()->foo, the compiler
        // will be able to fold it with the load.
        return reinterpret_cast<MBasicBlock*>(blockAndKind_ - 1);
    }

  public:
    // Returns the definition at a given operand.
    virtual MDefinition* getOperand(size_t index) const = 0;
    virtual size_t numOperands() const = 0;
    virtual size_t indexOf(const MUse* u) const = 0;

    bool isDefinition() const {
        return kind() == Kind::Definition;
    }
    bool isResumePoint() const {
        return kind() == Kind::ResumePoint;
    }
    MBasicBlock* block() const {
        return reinterpret_cast<MBasicBlock*>(blockAndKind_ & ~KindMask);
    }
    MBasicBlock* caller() const;

    // Sets an already set operand, updating use information. If you're looking
    // for setOperand, this is probably what you want.
    virtual void replaceOperand(size_t index, MDefinition* operand) = 0;

    // Resets the operand to an uninitialized state, breaking the link
    // with the previous operand's producer.
    void releaseOperand(size_t index) {
        getUseFor(index)->releaseProducer();
    }
    bool hasOperand(size_t index) const {
        return getUseFor(index)->hasProducer();
    }

    inline MDefinition* toDefinition();
    inline MResumePoint* toResumePoint();

    virtual MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const;

    virtual void dump(GenericPrinter& out) const = 0;
    virtual void dump() const = 0;

  protected:
    // Need visibility on getUseFor to avoid O(n^2) complexity.
    friend void AssertBasicGraphCoherency(MIRGraph& graph, bool force);

    // Gets the MUse corresponding to given operand.
    virtual MUse* getUseFor(size_t index) = 0;
    virtual const MUse* getUseFor(size_t index) const = 0;
};

class AliasSet {
  private:
    uint32_t flags_;

  public:
    enum Flag {
        None_             = 0,
        ObjectFields      = 1 << 0, // shape, class, slots, length etc.
        Element           = 1 << 1, // A Value member of obj->elements or
                                    // a typed object.
        UnboxedElement    = 1 << 2, // An unboxed scalar or reference member of
                                    // a typed array, typed object, or unboxed
                                    // object.
        DynamicSlot       = 1 << 3, // A Value member of obj->slots.
        FixedSlot         = 1 << 4, // A Value member of obj->fixedSlots().
        DOMProperty       = 1 << 5, // A DOM property
        FrameArgument     = 1 << 6, // An argument kept on the stack frame
        WasmGlobalVar     = 1 << 7, // An asm.js/wasm global var
        WasmHeap          = 1 << 8, // An asm.js/wasm heap load
        WasmHeapMeta      = 1 << 9, // The asm.js/wasm heap base pointer and
                                    // bounds check limit, in Tls.
        TypedArrayLength  = 1 << 10,// A typed array's length
        Last              = TypedArrayLength,
        Any               = Last | (Last - 1),

        NumCategories     = 11,

        // Indicates load or store.
        Store_            = 1 << 31
    };

    static_assert((1 << NumCategories) - 1 == Any,
                  "NumCategories must include all flags present in Any");

    explicit AliasSet(uint32_t flags)
      : flags_(flags)
    {
    }

  public:
    static const char* Name(size_t flag);

    inline bool isNone() const {
        return flags_ == None_;
    }
    uint32_t flags() const {
        return flags_ & Any;
    }
    inline bool isStore() const {
        return !!(flags_ & Store_);
    }
    inline bool isLoad() const {
        return !isStore() && !isNone();
    }
    inline AliasSet operator |(const AliasSet& other) const {
        return AliasSet(flags_ | other.flags_);
    }
    inline AliasSet operator&(const AliasSet& other) const {
        return AliasSet(flags_ & other.flags_);
    }
    static AliasSet None() {
        return AliasSet(None_);
    }
    static AliasSet Load(uint32_t flags) {
        MOZ_ASSERT(flags && !(flags & Store_));
        return AliasSet(flags);
    }
    static AliasSet Store(uint32_t flags) {
        MOZ_ASSERT(flags && !(flags & Store_));
        return AliasSet(flags | Store_);
    }
    static uint32_t BoxedOrUnboxedElements(JSValueType type) {
        return (type == JSVAL_TYPE_MAGIC) ? Element : UnboxedElement;
    }
};

typedef Vector<MDefinition*, 6, JitAllocPolicy> MDefinitionVector;
typedef Vector<MInstruction*, 6, JitAllocPolicy> MInstructionVector;
typedef Vector<MDefinition*, 1, JitAllocPolicy> MStoreVector;

class StoreDependency : public TempObject
{
    MStoreVector all_;

  public:
    explicit StoreDependency(TempAllocator& alloc)
      : all_(alloc)
    { }

    MOZ_MUST_USE bool init(MDefinitionVector& all) {
        if (!all_.appendAll(all))
            return false;
        return true;
    }

    MStoreVector& get() {
        return all_;
    }
};

// An MDefinition is an SSA name.
class MDefinition : public MNode
{
    friend class MBasicBlock;

  public:
    enum class Opcode : uint16_t {
#   define DEFINE_OPCODES(op) op,
        MIR_OPCODE_LIST(DEFINE_OPCODES)
#   undef DEFINE_OPCODES
    };

  private:
    InlineList<MUse> uses_;        // Use chain.
    uint32_t id_;                  // Instruction ID, which after block re-ordering
                                   // is sorted within a basic block.
    Opcode op_;                    // Opcode.
    uint16_t flags_;               // Bit flags.
    Range* range_;                 // Any computed range for this def.
    MIRType resultType_;           // Representation of result type.
    TemporaryTypeSet* resultTypeSet_; // Optional refinement of the result type.
    union {
        MDefinition* loadDependency_;      // Implicit dependency (store, call, etc.) of this
        StoreDependency* storeDependency_; // instruction. Used by alias analysis, GVN and LICM.
        uint32_t virtualRegister_; // Used by lowering to map definitions to virtual registers.
    };

    // Track bailouts by storing the current pc in MIR instruction. Also used
    // for profiling and keeping track of what the last known pc was.
    const BytecodeSite* trackedSite_;

  private:
    enum Flag {
        None = 0,
#   define DEFINE_FLAG(flag) flag,
        MIR_FLAG_LIST(DEFINE_FLAG)
#   undef DEFINE_FLAG
        Total
    };

    bool hasFlags(uint32_t flags) const {
        return (flags_ & flags) == flags;
    }
    void removeFlags(uint32_t flags) {
        flags_ &= ~flags;
    }
    void setFlags(uint32_t flags) {
        flags_ |= flags;
    }

    // Calling isDefinition or isResumePoint on MDefinition is unnecessary.
    bool isDefinition() const = delete;
    bool isResumePoint() const = delete;

  protected:
    void setBlock(MBasicBlock* block) {
        setBlockAndKind(block, Kind::Definition);
    }

    static HashNumber addU32ToHash(HashNumber hash, uint32_t data) {
        return data + (hash << 6) + (hash << 16) - hash;
    }

  public:
    explicit MDefinition(Opcode op)
      : MNode(nullptr, Kind::Definition),
        id_(0),
        op_(op),
        flags_(0),
        range_(nullptr),
        resultType_(MIRType::None),
        resultTypeSet_(nullptr),
        loadDependency_(nullptr),
        trackedSite_(nullptr)
    { }

    // Copying a definition leaves the list of uses and the block empty.
    explicit MDefinition(const MDefinition& other)
      : MNode(other),
        id_(0),
        op_(other.op_),
        flags_(other.flags_),
        range_(other.range_),
        resultType_(other.resultType_),
        resultTypeSet_(other.resultTypeSet_),
        loadDependency_(other.loadDependency_),
        trackedSite_(other.trackedSite_)
    { }

    Opcode op() const { return op_; }

    virtual const char* opName() const = 0;
    virtual void accept(MDefinitionVisitor* visitor) = 0;

    void printName(GenericPrinter& out) const;
    static void PrintOpcodeName(GenericPrinter& out, Opcode op);
    virtual void printOpcode(GenericPrinter& out) const;
    void dump(GenericPrinter& out) const override;
    void dump() const override;
    void dumpLocation(GenericPrinter& out) const;
    void dumpLocation() const;

    // For LICM.
    virtual bool neverHoist() const { return false; }

    // Also for LICM. Test whether this definition is likely to be a call, which
    // would clobber all or many of the floating-point registers, such that
    // hoisting floating-point constants out of containing loops isn't likely to
    // be worthwhile.
    virtual bool possiblyCalls() const { return false; }

    MBasicBlock* block() const {
        return definitionBlock();
    }

    void setTrackedSite(const BytecodeSite* site) {
        MOZ_ASSERT(site);
        trackedSite_ = site;
    }
    const BytecodeSite* trackedSite() const {
        return trackedSite_;
    }
    jsbytecode* trackedPc() const {
        return trackedSite_ ? trackedSite_->pc() : nullptr;
    }
    InlineScriptTree* trackedTree() const {
        return trackedSite_ ? trackedSite_->tree() : nullptr;
    }
    TrackedOptimizations* trackedOptimizations() const {
        return trackedSite_ && trackedSite_->hasOptimizations()
               ? trackedSite_->optimizations()
               : nullptr;
    }

    JSScript* profilerLeaveScript() const {
        return trackedTree()->outermostCaller()->script();
    }

    jsbytecode* profilerLeavePc() const {
        // If this is in a top-level function, use the pc directly.
        if (trackedTree()->isOutermostCaller())
            return trackedPc();

        // Walk up the InlineScriptTree chain to find the top-most callPC
        InlineScriptTree* curTree = trackedTree();
        InlineScriptTree* callerTree = curTree->caller();
        while (!callerTree->isOutermostCaller()) {
            curTree = callerTree;
            callerTree = curTree->caller();
        }

        // Return the callPc of the topmost inlined script.
        return curTree->callerPc();
    }

    // Return the range of this value, *before* any bailout checks. Contrast
    // this with the type() method, and the Range constructor which takes an
    // MDefinition*, which describe the value *after* any bailout checks.
    //
    // Warning: Range analysis is removing the bit-operations such as '| 0' at
    // the end of the transformations. Using this function to analyse any
    // operands after the truncate phase of the range analysis will lead to
    // errors. Instead, one should define the collectRangeInfoPreTrunc() to set
    // the right set of flags which are dependent on the range of the inputs.
    Range* range() const {
        MOZ_ASSERT(type() != MIRType::None);
        return range_;
    }
    void setRange(Range* range) {
        MOZ_ASSERT(type() != MIRType::None);
        range_ = range;
    }

    virtual HashNumber valueHash() const;
    virtual bool congruentTo(const MDefinition* ins) const {
        return false;
    }
    bool congruentIfOperandsEqual(const MDefinition* ins) const;
    virtual MDefinition* foldsTo(TempAllocator& alloc);
    virtual void analyzeEdgeCasesForward();
    virtual void analyzeEdgeCasesBackward();

    // When a floating-point value is used by nodes which would prefer to
    // recieve integer inputs, we may be able to help by computing our result
    // into an integer directly.
    //
    // A value can be truncated in 4 differents ways:
    //   1. Ignore Infinities (x / 0 --> 0).
    //   2. Ignore overflow (INT_MIN / -1 == (INT_MAX + 1) --> INT_MIN)
    //   3. Ignore negative zeros. (-0 --> 0)
    //   4. Ignore remainder. (3 / 4 --> 0)
    //
    // Indirect truncation is used to represent that we are interested in the
    // truncated result, but only if it can safely flow into operations which
    // are computed modulo 2^32, such as (2) and (3). Infinities are not safe,
    // as they would have absorbed other math operations. Remainders are not
    // safe, as fractions can be scaled up by multiplication.
    //
    // Division is a particularly interesting node here because it covers all 4
    // cases even when its own operands are integers.
    //
    // Note that these enum values are ordered from least value-modifying to
    // most value-modifying, and code relies on this ordering.
    enum TruncateKind {
        // No correction.
        NoTruncate = 0,
        // An integer is desired, but we can't skip bailout checks.
        TruncateAfterBailouts = 1,
        // The value will be truncated after some arithmetic (see above).
        IndirectTruncate = 2,
        // Direct and infallible truncation to int32.
        Truncate = 3
    };

    static const char * TruncateKindString(TruncateKind kind) {
        switch(kind) {
          case NoTruncate:
            return "NoTruncate";
          case TruncateAfterBailouts:
            return "TruncateAfterBailouts";
          case IndirectTruncate:
            return "IndirectTruncate";
          case Truncate:
            return "Truncate";
          default:
            MOZ_CRASH("Unknown truncate kind.");
        }
    }

    // |needTruncation| records the truncation kind of the results, such that it
    // can be used to truncate the operands of this instruction.  If
    // |needTruncation| function returns true, then the |truncate| function is
    // called on the same instruction to mutate the instruction, such as
    // updating the return type, the range and the specialization of the
    // instruction.
    virtual bool needTruncation(TruncateKind kind);
    virtual void truncate();

    // Determine what kind of truncate this node prefers for the operand at the
    // given index.
    virtual TruncateKind operandTruncateKind(size_t index) const;

    // Compute an absolute or symbolic range for the value of this node.
    virtual void computeRange(TempAllocator& alloc) {
    }

    // Collect information from the pre-truncated ranges.
    virtual void collectRangeInfoPreTrunc() {
    }

    uint32_t id() const {
        MOZ_ASSERT(block());
        return id_;
    }
    void setId(uint32_t id) {
        id_ = id;
    }

#define FLAG_ACCESSOR(flag) \
    bool is##flag() const {\
        static_assert(Flag::Total <= sizeof(flags_) * 8, "Flags should fit in flags_ field");\
        return hasFlags(1 << flag);\
    }\
    void set##flag() {\
        MOZ_ASSERT(!hasFlags(1 << flag));\
        setFlags(1 << flag);\
    }\
    void setNot##flag() {\
        MOZ_ASSERT(hasFlags(1 << flag));\
        removeFlags(1 << flag);\
    }\
    void set##flag##Unchecked() {\
        setFlags(1 << flag);\
    } \
    void setNot##flag##Unchecked() {\
        removeFlags(1 << flag);\
    }

    MIR_FLAG_LIST(FLAG_ACCESSOR)
#undef FLAG_ACCESSOR

    // Return the type of this value. This may be speculative, and enforced
    // dynamically with the use of bailout checks. If all the bailout checks
    // pass, the value will have this type.
    //
    // Unless this is an MUrsh that has bailouts disabled, which, as a special
    // case, may return a value in (INT32_MAX,UINT32_MAX] even when its type()
    // is MIRType::Int32.
    MIRType type() const {
        return resultType_;
    }

    TemporaryTypeSet* resultTypeSet() const {
        return resultTypeSet_;
    }
    bool emptyResultTypeSet() const;

    bool mightBeType(MIRType type) const {
        MOZ_ASSERT(type != MIRType::Value);
        MOZ_ASSERT(type != MIRType::ObjectOrNull);

        if (type == this->type())
            return true;

        if (this->type() == MIRType::ObjectOrNull)
            return type == MIRType::Object || type == MIRType::Null;

        if (this->type() == MIRType::Value)
            return !resultTypeSet() || resultTypeSet()->mightBeMIRType(type);

        return false;
    }

    bool mightBeMagicType() const;

    bool maybeEmulatesUndefined(CompilerConstraintList* constraints);

    // Float32 specialization operations (see big comment in IonAnalysis before the Float32
    // specialization algorithm).
    virtual bool isFloat32Commutative() const { return false; }
    virtual bool canProduceFloat32() const { return false; }
    virtual bool canConsumeFloat32(MUse* use) const { return false; }
    virtual void trySpecializeFloat32(TempAllocator& alloc) {}
#ifdef DEBUG
    // Used during the pass that checks that Float32 flow into valid MDefinitions
    virtual bool isConsistentFloat32Use(MUse* use) const {
        return type() == MIRType::Float32 || canConsumeFloat32(use);
    }
#endif

    // Returns the beginning of this definition's use chain.
    MUseIterator usesBegin() const {
        return uses_.begin();
    }

    // Returns the end of this definition's use chain.
    MUseIterator usesEnd() const {
        return uses_.end();
    }

    bool canEmitAtUses() const {
        return !isEmittedAtUses();
    }

    // Removes a use at the given position
    void removeUse(MUse* use) {
        uses_.remove(use);
    }

#if defined(DEBUG) || defined(JS_JITSPEW)
    // Number of uses of this instruction. This function is only available
    // in DEBUG mode since it requires traversing the list. Most users should
    // use hasUses() or hasOneUse() instead.
    size_t useCount() const;

    // Number of uses of this instruction (only counting MDefinitions, ignoring
    // MResumePoints). This function is only available in DEBUG mode since it
    // requires traversing the list. Most users should use hasUses() or
    // hasOneUse() instead.
    size_t defUseCount() const;
#endif

    // Test whether this MDefinition has exactly one use.
    bool hasOneUse() const;

    // Test whether this MDefinition has exactly one use.
    // (only counting MDefinitions, ignoring MResumePoints)
    bool hasOneDefUse() const;

    // Test whether this MDefinition has at least one use.
    // (only counting MDefinitions, ignoring MResumePoints)
    bool hasDefUses() const;

    // Test whether this MDefinition has at least one non-recovered use.
    // (only counting MDefinitions, ignoring MResumePoints)
    bool hasLiveDefUses() const;

    bool hasUses() const {
        return !uses_.empty();
    }

    void addUse(MUse* use) {
        MOZ_ASSERT(use->producer() == this);
        uses_.pushFront(use);
    }
    void addUseUnchecked(MUse* use) {
        MOZ_ASSERT(use->producer() == this);
        uses_.pushFrontUnchecked(use);
    }
    void replaceUse(MUse* old, MUse* now) {
        MOZ_ASSERT(now->producer() == this);
        uses_.replace(old, now);
    }

    // Replace the current instruction by a dominating instruction |dom| in all
    // uses of the current instruction.
    void replaceAllUsesWith(MDefinition* dom);

    // Like replaceAllUsesWith, but doesn't set UseRemoved on |this|'s operands.
    void justReplaceAllUsesWith(MDefinition* dom);

    // Like justReplaceAllUsesWith, but doesn't replace its own use to the
    // dominating instruction (which would introduce a circular dependency).
    void justReplaceAllUsesWithExcept(MDefinition* dom);

    // Replace the current instruction by an optimized-out constant in all uses
    // of the current instruction. Note, that optimized-out constant should not
    // be observed, and thus they should not flow in any computation.
    MOZ_MUST_USE bool optimizeOutAllUses(TempAllocator& alloc);

    // Replace the current instruction by a dominating instruction |dom| in all
    // instruction, but keep the current instruction for resume point and
    // instruction which are recovered on bailouts.
    void replaceAllLiveUsesWith(MDefinition* dom);

    // Mark this instruction as having replaced all uses of ins, as during GVN,
    // returning false if the replacement should not be performed. For use when
    // GVN eliminates instructions which are not equivalent to one another.
    virtual MOZ_MUST_USE bool updateForReplacement(MDefinition* ins) {
        return true;
    }

    void setVirtualRegister(uint32_t vreg) {
        virtualRegister_ = vreg;
        setLoweredUnchecked();
    }
    uint32_t virtualRegister() const {
        MOZ_ASSERT(isLowered());
        return virtualRegister_;
    }

  public:
    // Opcode testing and casts.
    template<typename MIRType> bool is() const {
        return op() == MIRType::classOpcode;
    }
    template<typename MIRType> MIRType* to() {
        MOZ_ASSERT(this->is<MIRType>());
        return static_cast<MIRType*>(this);
    }
    template<typename MIRType> const MIRType* to() const {
        MOZ_ASSERT(this->is<MIRType>());
        return static_cast<const MIRType*>(this);
    }
#   define OPCODE_CASTS(opcode)           \
    bool is##opcode() const {             \
        return this->is<M##opcode>();     \
    }                                     \
    M##opcode* to##opcode() {             \
        return this->to<M##opcode>();     \
    }                                     \
    const M##opcode* to##opcode() const { \
        return this->to<M##opcode>();     \
    }
    MIR_OPCODE_LIST(OPCODE_CASTS)
#   undef OPCODE_CASTS

    inline MConstant* maybeConstantValue();

    inline MInstruction* toInstruction();
    inline const MInstruction* toInstruction() const;
    bool isInstruction() const {
        return !isPhi();
    }

    virtual bool isControlInstruction() const {
        return false;
    }
    inline MControlInstruction* toControlInstruction();

    void setResultType(MIRType type) {
        resultType_ = type;
    }
    void setResultTypeSet(TemporaryTypeSet* types) {
        resultTypeSet_ = types;
    }
    virtual AliasSet getAliasSet() const {
        // Instructions are effectful by default.
        return AliasSet::Store(AliasSet::Any);
    }

    MDefinition* dependency() const {
        if (getAliasSet().isStore())
            return nullptr;
        return loadDependency_;
    }
    void setDependency(MDefinition* dependency) {
        MOZ_ASSERT(!getAliasSet().isStore());
        loadDependency_ = dependency;
    }
    void setStoreDependency(StoreDependency* dependency) {
        MOZ_ASSERT(getAliasSet().isStore());
        storeDependency_ = dependency;
    }
    StoreDependency* storeDependency() {
        MOZ_ASSERT_IF(!getAliasSet().isStore(), !storeDependency_);
        return storeDependency_;
    }
    bool isEffectful() const {
        return getAliasSet().isStore();
    }

#ifdef DEBUG
    virtual bool needsResumePoint() const {
        // Return whether this instruction should have its own resume point.
        return isEffectful();
    }
#endif

    enum class AliasType : uint32_t {
        NoAlias = 0,
        MayAlias = 1,
        MustAlias = 2
    };
    virtual AliasType mightAlias(const MDefinition* store) const {
        // Return whether this load may depend on the specified store, given
        // that the alias sets intersect. This may be refined to exclude
        // possible aliasing in cases where alias set flags are too imprecise.
        if (!(getAliasSet().flags() & store->getAliasSet().flags()))
            return AliasType::NoAlias;
        MOZ_ASSERT(!isEffectful() && store->isEffectful());
        return AliasType::MayAlias;
    }

    virtual bool canRecoverOnBailout() const {
        return false;
    }
};

// An MUseDefIterator walks over uses in a definition, skipping any use that is
// not a definition. Items from the use list must not be deleted during
// iteration.
class MUseDefIterator
{
    const MDefinition* def_;
    MUseIterator current_;

    MUseIterator search(MUseIterator start) {
        MUseIterator i(start);
        for (; i != def_->usesEnd(); i++) {
            if (i->consumer()->isDefinition())
                return i;
        }
        return def_->usesEnd();
    }

  public:
    explicit MUseDefIterator(const MDefinition* def)
      : def_(def),
        current_(search(def->usesBegin()))
    { }

    explicit operator bool() const {
        return current_ != def_->usesEnd();
    }
    MUseDefIterator operator ++() {
        MOZ_ASSERT(current_ != def_->usesEnd());
        ++current_;
        current_ = search(current_);
        return *this;
    }
    MUseDefIterator operator ++(int) {
        MUseDefIterator old(*this);
        operator++();
        return old;
    }
    MUse* use() const {
        return *current_;
    }
    MDefinition* def() const {
        return current_->consumer()->toDefinition();
    }
};

#ifdef DEBUG
bool
IonCompilationCanUseNurseryPointers();
#endif

// Helper class to check that GC pointers embedded in MIR instructions are in
// in the nursery only when the store buffer has been marked as needing to
// cancel all ion compilations. Otherwise, off-thread Ion compilation and
// nursery GCs can happen in parallel, so it's invalid to store pointers to
// nursery things. There's no need to root these pointers, as GC is suppressed
// during compilation and off-thread compilations are canceled on major GCs.
template <typename T>
class CompilerGCPointer
{
    js::gc::Cell* ptr_;

  public:
    explicit CompilerGCPointer(T ptr)
      : ptr_(ptr)
    {
        MOZ_ASSERT_IF(IsInsideNursery(ptr), IonCompilationCanUseNurseryPointers());
        MOZ_ASSERT_IF(!CurrentThreadIsIonCompiling(), TlsContext.get()->suppressGC);
    }

    operator T() const { return static_cast<T>(ptr_); }
    T operator->() const { return static_cast<T>(ptr_); }

  private:
    CompilerGCPointer() = delete;
    CompilerGCPointer(const CompilerGCPointer<T>&) = delete;
    CompilerGCPointer<T>& operator=(const CompilerGCPointer<T>&) = delete;
};

typedef CompilerGCPointer<JSObject*> CompilerObject;
typedef CompilerGCPointer<NativeObject*> CompilerNativeObject;
typedef CompilerGCPointer<JSFunction*> CompilerFunction;
typedef CompilerGCPointer<JSScript*> CompilerScript;
typedef CompilerGCPointer<PropertyName*> CompilerPropertyName;
typedef CompilerGCPointer<Shape*> CompilerShape;
typedef CompilerGCPointer<ObjectGroup*> CompilerObjectGroup;

class MRootList : public TempObject
{
  public:
    using RootVector = Vector<void*, 0, JitAllocPolicy>;

  private:
    mozilla::EnumeratedArray<JS::RootKind, JS::RootKind::Limit, mozilla::Maybe<RootVector>> roots_;

    MRootList(const MRootList&) = delete;
    void operator=(const MRootList&) = delete;

  public:
    explicit MRootList(TempAllocator& alloc);

    void trace(JSTracer* trc);

    template <typename T>
    MOZ_MUST_USE bool append(T ptr) {
        if (ptr)
            return roots_[JS::MapTypeToRootKind<T>::kind]->append(ptr);
        return true;
    }

    template <typename T>
    MOZ_MUST_USE bool append(const CompilerGCPointer<T>& ptr) {
        return append(static_cast<T>(ptr));
    }
    MOZ_MUST_USE bool append(const ReceiverGuard& guard) {
        return append(guard.group) && append(guard.shape);
    }
};

// An instruction is an SSA name that is inserted into a basic block's IR
// stream.
class MInstruction
  : public MDefinition,
    public InlineListNode<MInstruction>
{
    MResumePoint* resumePoint_;

  protected:
    // All MInstructions are using the "MFoo::New(alloc)" notation instead of
    // the TempObject new operator. This code redefines the new operator as
    // protected, and delegates to the TempObject new operator. Thus, the
    // following code prevents calls to "new(alloc) MFoo" outside the MFoo
    // members.
    inline void* operator new(size_t nbytes, TempAllocator::Fallible view) throw() {
        return TempObject::operator new(nbytes, view);
    }
    inline void* operator new(size_t nbytes, TempAllocator& alloc) {
        return TempObject::operator new(nbytes, alloc);
    }
    template <class T>
    inline void* operator new(size_t nbytes, T* pos) {
        return TempObject::operator new(nbytes, pos);
    }

  public:
    explicit MInstruction(Opcode op)
      : MDefinition(op),
        resumePoint_(nullptr)
    { }

    // Copying an instruction leaves the block and resume point as empty.
    explicit MInstruction(const MInstruction& other)
      : MDefinition(other),
        resumePoint_(nullptr)
    { }

    // Convenient function used for replacing a load by the value of the store
    // if the types are match, and boxing the value if they do not match.
    MDefinition* foldsToStore(TempAllocator& alloc);

    void setResumePoint(MResumePoint* resumePoint);

    // Used to transfer the resume point to the rewritten instruction.
    void stealResumePoint(MInstruction* ins);
    void moveResumePointAsEntry();
    void clearResumePoint();
    MResumePoint* resumePoint() const {
        return resumePoint_;
    }

    // For instructions which can be cloned with new inputs, with all other
    // information being the same. clone() implementations do not need to worry
    // about cloning generic MInstruction/MDefinition state like flags and
    // resume points.
    virtual bool canClone() const {
        return false;
    }
    virtual MInstruction* clone(TempAllocator& alloc, const MDefinitionVector& inputs) const {
        MOZ_CRASH();
    }

    // MIR instructions containing GC pointers should override this to append
    // these pointers to the root list.
    virtual bool appendRoots(MRootList& roots) const {
        return true;
    }

    // Instructions needing to hook into type analysis should return a
    // TypePolicy.
    virtual TypePolicy* typePolicy() = 0;
    virtual MIRType typePolicySpecialization() = 0;
};

#define INSTRUCTION_HEADER_WITHOUT_TYPEPOLICY(opcode)                       \
    static const Opcode classOpcode = Opcode::opcode;                       \
    using MThisOpcode = M##opcode;                                          \
    const char* opName() const override {                                   \
        return #opcode;                                                     \
    }                                                                       \
    void accept(MDefinitionVisitor* visitor) override {                     \
        visitor->visit##opcode(this);                                       \
    }

#define INSTRUCTION_HEADER(opcode)                                          \
    INSTRUCTION_HEADER_WITHOUT_TYPEPOLICY(opcode)                           \
    virtual TypePolicy* typePolicy() override;                              \
    virtual MIRType typePolicySpecialization() override;

#define ALLOW_CLONE(typename)                                               \
    bool canClone() const override {                                        \
        return true;                                                        \
    }                                                                       \
    MInstruction* clone(TempAllocator& alloc,                               \
                        const MDefinitionVector& inputs) const override {   \
        MInstruction* res = new(alloc) typename(*this);                     \
        for (size_t i = 0; i < numOperands(); i++)                          \
            res->replaceOperand(i, inputs[i]);                              \
        return res;                                                         \
    }

// Adds MFoo::New functions which are mirroring the arguments of the
// constructors. Opcodes which are using this macro can be called with a
// TempAllocator, or the fallible version of the TempAllocator.
#define TRIVIAL_NEW_WRAPPERS                                                \
    template <typename... Args>                                             \
    static MThisOpcode* New(TempAllocator& alloc, Args&&... args) {         \
        return new(alloc) MThisOpcode(mozilla::Forward<Args>(args)...);     \
    }                                                                       \
    template <typename... Args>                                             \
    static MThisOpcode* New(TempAllocator::Fallible alloc, Args&&... args)  \
    {                                                                       \
        return new(alloc) MThisOpcode(mozilla::Forward<Args>(args)...);     \
    }

#define TRIVIAL_NEW_WRAPPERS_WITH_ALLOC                                     \
    template <typename... Args>                                             \
    static MThisOpcode* New(TempAllocator& alloc, Args&&... args) {         \
        return new(alloc) MThisOpcode(alloc, mozilla::Forward<Args>(args)...); \
    }                                                                       \
    template <typename... Args>                                             \
    static MThisOpcode* New(TempAllocator::Fallible alloc, Args&&... args)  \
    {                                                                       \
        return new(alloc) MThisOpcode(alloc, mozilla::Forward<Args>(args)...); \
    }

// These macros are used as a syntactic sugar for writting getOperand
// accessors. They are meant to be used in the body of MIR Instructions as
// follows:
//
//   public:
//     INSTRUCTION_HEADER(Foo)
//     NAMED_OPERANDS((0, lhs), (1, rhs))
//
// The above example defines 2 accessors, one named "lhs" accessing the first
// operand, and a one named "rhs" accessing the second operand.
#define NAMED_OPERAND_ACCESSOR(Index, Name)                                 \
    MDefinition* Name() const {                                             \
        return getOperand(Index);                                           \
    }
#define NAMED_OPERAND_ACCESSOR_APPLY(Args)                                  \
    NAMED_OPERAND_ACCESSOR Args
#define NAMED_OPERANDS(...)                                                 \
    MOZ_FOR_EACH(NAMED_OPERAND_ACCESSOR_APPLY, (), (__VA_ARGS__))

template <size_t Arity>
class MAryInstruction : public MInstruction
{
    mozilla::Array<MUse, Arity> operands_;

  protected:
    MUse* getUseFor(size_t index) final override {
        return &operands_[index];
    }
    const MUse* getUseFor(size_t index) const final override {
        return &operands_[index];
    }
    void initOperand(size_t index, MDefinition* operand) {
        operands_[index].init(operand, this);
    }

  public:
    MDefinition* getOperand(size_t index) const final override {
        return operands_[index].producer();
    }
    size_t numOperands() const final override {
        return Arity;
    }
#ifdef DEBUG
    static const size_t staticNumOperands = Arity;
#endif
    size_t indexOf(const MUse* u) const final override {
        MOZ_ASSERT(u >= &operands_[0]);
        MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
        return u - &operands_[0];
    }
    void replaceOperand(size_t index, MDefinition* operand) final override {
        operands_[index].replaceProducer(operand);
    }

    explicit MAryInstruction(Opcode op)
      : MInstruction(op)
    { }

    explicit MAryInstruction(const MAryInstruction<Arity>& other)
      : MInstruction(other)
    {
        for (int i = 0; i < (int) Arity; i++) // N.B. use |int| to avoid warnings when Arity == 0
            operands_[i].init(other.operands_[i].producer(), this);
    }
};

class MNullaryInstruction
  : public MAryInstruction<0>,
    public NoTypePolicy::Data
{
  protected:
    explicit MNullaryInstruction(Opcode op)
      : MAryInstruction(op)
    { }

    HashNumber valueHash() const override;
};

class MUnaryInstruction : public MAryInstruction<1>
{
  protected:
    MUnaryInstruction(Opcode op, MDefinition* ins)
      : MAryInstruction(op)
    {
        initOperand(0, ins);
    }

    HashNumber valueHash() const override;

  public:
    NAMED_OPERANDS((0, input))
};

class MBinaryInstruction : public MAryInstruction<2>
{
  protected:
    MBinaryInstruction(Opcode op, MDefinition* left, MDefinition* right)
      : MAryInstruction(op)
    {
        initOperand(0, left);
        initOperand(1, right);
    }

  public:
    NAMED_OPERANDS((0, lhs), (1, rhs))
    void swapOperands() {
        MDefinition* temp = getOperand(0);
        replaceOperand(0, getOperand(1));
        replaceOperand(1, temp);
    }

  protected:
    HashNumber valueHash() const override;

    bool binaryCongruentTo(const MDefinition* ins) const
    {
        if (op() != ins->op())
            return false;

        if (type() != ins->type())
            return false;

        if (isEffectful() || ins->isEffectful())
            return false;

        const MDefinition* left = getOperand(0);
        const MDefinition* right = getOperand(1);
        const MDefinition* tmp;

        if (isCommutative() && left->id() > right->id()) {
            tmp = right;
            right = left;
            left = tmp;
        }

        const MBinaryInstruction* bi = static_cast<const MBinaryInstruction*>(ins);
        const MDefinition* insLeft = bi->getOperand(0);
        const MDefinition* insRight = bi->getOperand(1);
        if (isCommutative() && insLeft->id() > insRight->id()) {
            tmp = insRight;
            insRight = insLeft;
            insLeft = tmp;
        }

        return left == insLeft &&
               right == insRight;
    }

  public:
    // Return if the operands to this instruction are both unsigned.
    static bool unsignedOperands(MDefinition* left, MDefinition* right);
    bool unsignedOperands();

    // Replace any wrapping operands with the underlying int32 operands
    // in case of unsigned operands.
    void replaceWithUnsignedOperands();
};

class MTernaryInstruction : public MAryInstruction<3>
{
  protected:
    MTernaryInstruction(Opcode op, MDefinition* first, MDefinition* second, MDefinition* third)
      : MAryInstruction(op)
    {
        initOperand(0, first);
        initOperand(1, second);
        initOperand(2, third);
    }

    HashNumber valueHash() const override;
};

class MQuaternaryInstruction : public MAryInstruction<4>
{
  protected:
    MQuaternaryInstruction(Opcode op,
                           MDefinition* first, MDefinition* second,
                           MDefinition* third, MDefinition* fourth)
      : MAryInstruction(op)
    {
        initOperand(0, first);
        initOperand(1, second);
        initOperand(2, third);
        initOperand(3, fourth);
    }

    HashNumber valueHash() const override;
};

template <class T>
class MVariadicT : public T
{
    FixedList<MUse> operands_;

  protected:
    explicit MVariadicT(typename T::Opcode op)
      : T(op)
    { }
    MOZ_MUST_USE bool init(TempAllocator& alloc, size_t length) {
        return operands_.init(alloc, length);
    }
    void initOperand(size_t index, MDefinition* operand) {
        // FixedList doesn't initialize its elements, so do an unchecked init.
        operands_[index].initUnchecked(operand, this);
    }
    MUse* getUseFor(size_t index) final override {
        return &operands_[index];
    }
    const MUse* getUseFor(size_t index) const final override {
        return &operands_[index];
    }

  public:
    // Will assert if called before initialization.
    MDefinition* getOperand(size_t index) const final override {
        return operands_[index].producer();
    }
    size_t numOperands() const final override {
        return operands_.length();
    }
    size_t indexOf(const MUse* u) const final override {
        MOZ_ASSERT(u >= &operands_[0]);
        MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
        return u - &operands_[0];
    }
    void replaceOperand(size_t index, MDefinition* operand) final override {
        operands_[index].replaceProducer(operand);
    }
};

typedef MVariadicT<MInstruction> MVariadicInstruction;

// Generates an LSnapshot without further effect.
class MStart : public MNullaryInstruction
{
    MStart()
      : MNullaryInstruction(classOpcode)
    { }

  public:
    INSTRUCTION_HEADER(Start)
    TRIVIAL_NEW_WRAPPERS
};

// Instruction marking on entrypoint for on-stack replacement.
// OSR may occur at loop headers (at JSOP_TRACE).
// There is at most one MOsrEntry per MIRGraph.
class MOsrEntry : public MNullaryInstruction
{
  protected:
    MOsrEntry()
      : MNullaryInstruction(classOpcode)
    {
        setResultType(MIRType::Pointer);
    }

  public:
    INSTRUCTION_HEADER(OsrEntry)
    TRIVIAL_NEW_WRAPPERS
};

// No-op instruction. This cannot be moved or eliminated, and is intended for
// anchoring resume points at arbitrary points in a block.
class MNop : public MNullaryInstruction
{
  protected:
    MNop()
      : MNullaryInstruction(classOpcode)
    { }

  public:
    INSTRUCTION_HEADER(Nop)
    TRIVIAL_NEW_WRAPPERS

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    ALLOW_CLONE(MNop)
};

// Truncation barrier. This is intended for protecting its input against
// follow-up truncation optimizations.
class MLimitedTruncate
  : public MUnaryInstruction,
    public ConvertToInt32Policy<0>::Data
{
  public:
    TruncateKind truncate_;
    TruncateKind truncateLimit_;

  protected:
    MLimitedTruncate(MDefinition* input, TruncateKind limit)
      : MUnaryInstruction(classOpcode, input),
        truncate_(NoTruncate),
        truncateLimit_(limit)
    {
        setResultType(MIRType::Int32);
        setResultTypeSet(input->resultTypeSet());
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(LimitedTruncate)
    TRIVIAL_NEW_WRAPPERS

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    void computeRange(TempAllocator& alloc) override;
    bool needTruncation(TruncateKind kind) override;
    TruncateKind operandTruncateKind(size_t index) const override;
    TruncateKind truncateKind() const {
        return truncate_;
    }
    void setTruncateKind(TruncateKind kind) {
        truncate_ = kind;
    }
};

// A constant js::Value.
class MConstant : public MNullaryInstruction
{
    struct Payload {
        union {
            bool b;
            int32_t i32;
            int64_t i64;
            float f;
            double d;
            JSString* str;
            JS::Symbol* sym;
            JSObject* obj;
            uint64_t asBits;
        };
        Payload() : asBits(0) {}
    };

    Payload payload_;

    static_assert(sizeof(Payload) == sizeof(uint64_t),
                  "asBits must be big enough for all payload bits");

#ifdef DEBUG
    void assertInitializedPayload() const;
#else
    void assertInitializedPayload() const {}
#endif

  protected:
    MConstant(TempAllocator& alloc, const Value& v, CompilerConstraintList* constraints);
    explicit MConstant(JSObject* obj);
    explicit MConstant(float f);
    explicit MConstant(int64_t i);

  public:
    INSTRUCTION_HEADER(Constant)
    static MConstant* New(TempAllocator& alloc, const Value& v,
                          CompilerConstraintList* constraints = nullptr);
    static MConstant* New(TempAllocator::Fallible alloc, const Value& v,
                          CompilerConstraintList* constraints = nullptr);
    static MConstant* New(TempAllocator& alloc, const Value& v, MIRType type);
    static MConstant* NewFloat32(TempAllocator& alloc, double d);
    static MConstant* NewInt64(TempAllocator& alloc, int64_t i);
    static MConstant* NewConstraintlessObject(TempAllocator& alloc, JSObject* v);
    static MConstant* Copy(TempAllocator& alloc, MConstant* src) {
        return new(alloc) MConstant(*src);
    }

    // Try to convert this constant to boolean, similar to js::ToBoolean.
    // Returns false if the type is MIRType::Magic*.
    bool MOZ_MUST_USE valueToBoolean(bool* res) const;

    // Like valueToBoolean, but returns the result directly instead of using
    // an outparam. Should not be used if this constant might be a magic value.
    bool valueToBooleanInfallible() const {
        bool res;
        MOZ_ALWAYS_TRUE(valueToBoolean(&res));
        return res;
    }

    void printOpcode(GenericPrinter& out) const override;

    HashNumber valueHash() const override;
    bool congruentTo(const MDefinition* ins) const override;

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    MOZ_MUST_USE bool updateForReplacement(MDefinition* def) override {
        MConstant* c = def->toConstant();
        // During constant folding, we don't want to replace a float32
        // value by a double value.
        if (type() == MIRType::Float32)
            return c->type() == MIRType::Float32;
        if (type() == MIRType::Double)
            return c->type() != MIRType::Float32;
        return true;
    }

    void computeRange(TempAllocator& alloc) override;
    bool needTruncation(TruncateKind kind) override;
    void truncate() override;

    bool canProduceFloat32() const override;

    ALLOW_CLONE(MConstant)

    bool equals(const MConstant* other) const {
        assertInitializedPayload();
        return type() == other->type() && payload_.asBits == other->payload_.asBits;
    }

    bool toBoolean() const {
        MOZ_ASSERT(type() == MIRType::Boolean);
        return payload_.b;
    }
    int32_t toInt32() const {
        MOZ_ASSERT(type() == MIRType::Int32);
        return payload_.i32;
    }
    int64_t toInt64() const {
        MOZ_ASSERT(type() == MIRType::Int64);
        return payload_.i64;
    }
    bool isInt32(int32_t i) const {
        return type() == MIRType::Int32 && payload_.i32 == i;
    }
    const double& toDouble() const {
        MOZ_ASSERT(type() == MIRType::Double);
        return payload_.d;
    }
    const float& toFloat32() const {
        MOZ_ASSERT(type() == MIRType::Float32);
        return payload_.f;
    }
    JSString* toString() const {
        MOZ_ASSERT(type() == MIRType::String);
        return payload_.str;
    }
    JS::Symbol* toSymbol() const {
        MOZ_ASSERT(type() == MIRType::Symbol);
        return payload_.sym;
    }
    JSObject& toObject() const {
        MOZ_ASSERT(type() == MIRType::Object);
        return *payload_.obj;
    }
    JSObject* toObjectOrNull() const {
        if (type() == MIRType::Object)
            return payload_.obj;
        MOZ_ASSERT(type() == MIRType::Null);
        return nullptr;
    }

    bool isTypeRepresentableAsDouble() const {
        return IsTypeRepresentableAsDouble(type());
    }
    double numberToDouble() const {
        MOZ_ASSERT(isTypeRepresentableAsDouble());
        if (type() == MIRType::Int32)
            return toInt32();
        if (type() == MIRType::Double)
            return toDouble();
        return toFloat32();
    }

    // Convert this constant to a js::Value. Float32 constants will be stored
    // as DoubleValue and NaNs are canonicalized. Callers must be careful: not
    // all constants can be represented by js::Value (wasm supports int64).
    Value toJSValue() const;

    bool appendRoots(MRootList& roots) const override;
};

// Floating-point value as created by wasm. Just a constant value, used to
// effectively inhibite all the MIR optimizations. This uses the same LIR nodes
// as a MConstant of the same type would.
class MWasmFloatConstant : public MNullaryInstruction
{
    union {
        float f32_;
        double f64_;
        uint64_t bits_;
    } u;

    explicit MWasmFloatConstant(MIRType type)
      : MNullaryInstruction(classOpcode)
    {
        u.bits_ = 0;
        setResultType(type);
    }

  public:
    INSTRUCTION_HEADER(WasmFloatConstant)

    static MWasmFloatConstant* NewDouble(TempAllocator& alloc, double d) {
        auto* ret = new(alloc) MWasmFloatConstant(MIRType::Double);
        ret->u.f64_ = d;
        return ret;
    }

    static MWasmFloatConstant* NewFloat32(TempAllocator& alloc, float f) {
        auto* ret = new(alloc) MWasmFloatConstant(MIRType::Float32);
        ret->u.f32_ = f;
        return ret;
    }

    HashNumber valueHash() const override;
    bool congruentTo(const MDefinition* ins) const override;
    AliasSet getAliasSet() const override { return AliasSet::None(); }

    const double& toDouble() const {
        MOZ_ASSERT(type() == MIRType::Double);
        return u.f64_;
    }
    const float& toFloat32() const {
        MOZ_ASSERT(type() == MIRType::Float32);
        return u.f32_;
    }
};

// Generic constructor of SIMD valuesX4.
class MSimdValueX4
  : public MQuaternaryInstruction,
    public Mix4Policy<SimdScalarPolicy<0>, SimdScalarPolicy<1>,
                      SimdScalarPolicy<2>, SimdScalarPolicy<3> >::Data
{
  protected:
    MSimdValueX4(MIRType type, MDefinition* x, MDefinition* y, MDefinition* z, MDefinition* w)
      : MQuaternaryInstruction(classOpcode, x, y, z, w)
    {
        MOZ_ASSERT(IsSimdType(type));
        MOZ_ASSERT(SimdTypeToLength(type) == 4);

        setMovable();
        setResultType(type);
    }

  public:
    INSTRUCTION_HEADER(SimdValueX4)
    TRIVIAL_NEW_WRAPPERS

    bool canConsumeFloat32(MUse* use) const override {
        return SimdTypeToLaneType(type()) == MIRType::Float32;
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins);
    }

    MDefinition* foldsTo(TempAllocator& alloc) override;

    ALLOW_CLONE(MSimdValueX4)
};

// Generic constructor of SIMD values with identical lanes.
class MSimdSplat
  : public MUnaryInstruction,
    public SimdScalarPolicy<0>::Data
{
  protected:
    MSimdSplat(MDefinition* v, MIRType type)
      : MUnaryInstruction(classOpcode, v)
    {
        MOZ_ASSERT(IsSimdType(type));
        setMovable();
        setResultType(type);
    }

  public:
    INSTRUCTION_HEADER(SimdSplat)
    TRIVIAL_NEW_WRAPPERS

    bool canConsumeFloat32(MUse* use) const override {
        return SimdTypeToLaneType(type()) == MIRType::Float32;
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins);
    }

    MDefinition* foldsTo(TempAllocator& alloc) override;

    ALLOW_CLONE(MSimdSplat)
};

// A constant SIMD value.
class MSimdConstant
  : public MNullaryInstruction
{
    SimdConstant value_;

  protected:
    MSimdConstant(const SimdConstant& v, MIRType type)
      : MNullaryInstruction(classOpcode),
        value_(v)
    {
        MOZ_ASSERT(IsSimdType(type));
        setMovable();
        setResultType(type);
    }

  public:
    INSTRUCTION_HEADER(SimdConstant)
    TRIVIAL_NEW_WRAPPERS

    bool congruentTo(const MDefinition* ins) const override {
        if (!ins->isSimdConstant())
            return false;
        // Bool32x4 and Int32x4 share the same underlying SimdConstant representation.
        if (type() != ins->type())
            return false;
        return value() == ins->toSimdConstant()->value();
    }

    const SimdConstant& value() const {
        return value_;
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    ALLOW_CLONE(MSimdConstant)
};

// Converts all lanes of a given vector into the type of another vector
class MSimdConvert
  : public MUnaryInstruction,
    public SimdPolicy<0>::Data
{
    // When either fromType or toType is an integer vector, should it be treated
    // as signed or unsigned. Note that we don't support int-int conversions -
    // use MSimdReinterpretCast for that.
    SimdSign sign_;
    wasm::BytecodeOffset bytecodeOffset_;

    MSimdConvert(MDefinition* obj, MIRType toType, SimdSign sign,
                 wasm::BytecodeOffset bytecodeOffset)
      : MUnaryInstruction(classOpcode, obj), sign_(sign), bytecodeOffset_(bytecodeOffset)
    {
        MIRType fromType = obj->type();
        MOZ_ASSERT(IsSimdType(fromType));
        MOZ_ASSERT(IsSimdType(toType));
        // All conversions are int <-> float, so signedness is required.
        MOZ_ASSERT(sign != SimdSign::NotApplicable);

        setResultType(toType);
        specialization_ = fromType; // expects fromType as input

        setMovable();
        if (IsFloatingPointSimdType(fromType) && IsIntegerSimdType(toType)) {
            // Does the extra range check => do not remove
            setGuard();
        }
    }

    static MSimdConvert* New(TempAllocator& alloc, MDefinition* obj, MIRType toType, SimdSign sign,
                             wasm::BytecodeOffset bytecodeOffset)
    {
        return new (alloc) MSimdConvert(obj, toType, sign, bytecodeOffset);
    }

  public:
    INSTRUCTION_HEADER(SimdConvert)

    // Create a MSimdConvert instruction and add it to the basic block.
    // Possibly create and add an equivalent sequence of instructions instead if
    // the current target doesn't support the requested conversion directly.
    // Return the inserted MInstruction that computes the converted value.
    static MInstruction* AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* obj,
                                      MIRType toType, SimdSign sign,
                                      wasm::BytecodeOffset bytecodeOffset = wasm::BytecodeOffset());

    SimdSign signedness() const {
        return sign_;
    }
    wasm::BytecodeOffset bytecodeOffset() const {
        return bytecodeOffset_;
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
    bool congruentTo(const MDefinition* ins) const override {
        if (!congruentIfOperandsEqual(ins))
            return false;
        const MSimdConvert* other = ins->toSimdConvert();
        return sign_ == other->sign_;
    }
    ALLOW_CLONE(MSimdConvert)
};

// Casts bits of a vector input to another SIMD type (doesn't generate code).
class MSimdReinterpretCast
  : public MUnaryInstruction,
    public SimdPolicy<0>::Data
{
    MSimdReinterpretCast(MDefinition* obj, MIRType toType)
      : MUnaryInstruction(classOpcode, obj)
    {
        MIRType fromType = obj->type();
        MOZ_ASSERT(IsSimdType(fromType));
        MOZ_ASSERT(IsSimdType(toType));
        setMovable();
        setResultType(toType);
        specialization_ = fromType; // expects fromType as input
    }

  public:
    INSTRUCTION_HEADER(SimdReinterpretCast)
    TRIVIAL_NEW_WRAPPERS

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins);
    }
    ALLOW_CLONE(MSimdReinterpretCast)
};

// Extracts a lane element from a given vector type, given by its lane symbol.
//
// For integer SIMD types, a SimdSign must be provided so the lane value can be
// converted to a scalar correctly.
class MSimdExtractElement
  : public MUnaryInstruction,
    public SimdPolicy<0>::Data
{
  protected:
    unsigned lane_;
    SimdSign sign_;

    MSimdExtractElement(MDefinition* obj, MIRType laneType, unsigned lane, SimdSign sign)
      : MUnaryInstruction(classOpcode, obj), lane_(lane), sign_(sign)
    {
        MIRType vecType = obj->type();
        MOZ_ASSERT(IsSimdType(vecType));
        MOZ_ASSERT(lane < SimdTypeToLength(vecType));
        MOZ_ASSERT(!IsSimdType(laneType));
        MOZ_ASSERT((sign != SimdSign::NotApplicable) == IsIntegerSimdType(vecType),
                   "Signedness must be specified for integer SIMD extractLanes");
        // The resulting type should match the lane type.
        // Allow extracting boolean lanes directly into an Int32 (for wasm).
        // Allow extracting Uint32 lanes into a double.
        //
        // We also allow extracting Uint32 lanes into a MIRType::Int32. This is
        // equivalent to extracting the Uint32 lane to a double and then
        // applying MTruncateToInt32, but it bypasses the conversion to/from
        // double.
        MOZ_ASSERT(SimdTypeToLaneType(vecType) == laneType ||
                   (IsBooleanSimdType(vecType) && laneType == MIRType::Int32) ||
                   (vecType == MIRType::Int32x4 && laneType == MIRType::Double &&
                    sign == SimdSign::Unsigned));

        setMovable();
        specialization_ = vecType;
        setResultType(laneType);
    }

  public:
    INSTRUCTION_HEADER(SimdExtractElement)
    TRIVIAL_NEW_WRAPPERS

    unsigned lane() const {
        return lane_;
    }

    SimdSign signedness() const {
        return sign_;
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
    bool congruentTo(const MDefinition* ins) const override {
        if (!ins->isSimdExtractElement())
            return false;
        const MSimdExtractElement* other = ins->toSimdExtractElement();
        if (other->lane_ != lane_ || other->sign_ != sign_)
            return false;
        return congruentIfOperandsEqual(other);
    }
    ALLOW_CLONE(MSimdExtractElement)
};

// Replaces the datum in the given lane by a scalar value of the same type.
class MSimdInsertElement
  : public MBinaryInstruction,
    public MixPolicy< SimdSameAsReturnedTypePolicy<0>, SimdScalarPolicy<1> >::Data
{
  private:
    unsigned lane_;

    MSimdInsertElement(MDefinition* vec, MDefinition* val, unsigned lane)
      : MBinaryInstruction(classOpcode, vec, val), lane_(lane)
    {
        MIRType type = vec->type();
        MOZ_ASSERT(IsSimdType(type));
        MOZ_ASSERT(lane < SimdTypeToLength(type));
        setMovable();
        setResultType(type);
    }

  public:
    INSTRUCTION_HEADER(SimdInsertElement)
    TRIVIAL_NEW_WRAPPERS
    NAMED_OPERANDS((0, vector), (1, value))

    unsigned lane() const {
        return lane_;
    }

    bool canConsumeFloat32(MUse* use) const override {
        return use == getUseFor(1) && SimdTypeToLaneType(type()) == MIRType::Float32;
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    bool congruentTo(const MDefinition* ins) const override {
        return binaryCongruentTo(ins) && lane_ == ins->toSimdInsertElement()->lane();
    }

    void printOpcode(GenericPrinter& out) const override;

    ALLOW_CLONE(MSimdInsertElement)
};

// Returns true if all lanes are true.
class MSimdAllTrue
  : public MUnaryInstruction,
    public SimdPolicy<0>::Data
{
  protected:
    explicit MSimdAllTrue(MDefinition* obj, MIRType result)
      : MUnaryInstruction(classOpcode, obj)
    {
        MIRType simdType = obj->type();
        MOZ_ASSERT(IsBooleanSimdType(simdType));
        MOZ_ASSERT(result == MIRType::Boolean || result == MIRType::Int32);
        setResultType(result);
        specialization_ = simdType;
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(SimdAllTrue)
    TRIVIAL_NEW_WRAPPERS

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins);
    }
    ALLOW_CLONE(MSimdAllTrue)
};

// Returns true if any lane is true.
class MSimdAnyTrue
  : public MUnaryInstruction,
    public SimdPolicy<0>::Data
{
  protected:
    explicit MSimdAnyTrue(MDefinition* obj, MIRType result)
      : MUnaryInstruction(classOpcode, obj)
    {
        MIRType simdType = obj->type();
        MOZ_ASSERT(IsBooleanSimdType(simdType));
        MOZ_ASSERT(result == MIRType::Boolean || result == MIRType::Int32);
        setResultType(result);
        specialization_ = simdType;
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(SimdAnyTrue)
    TRIVIAL_NEW_WRAPPERS

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins);
    }

    ALLOW_CLONE(MSimdAnyTrue)
};

// Base for the MSimdSwizzle and MSimdShuffle classes.
class MSimdShuffleBase
{
  protected:
    // As of now, there are at most 16 lanes. For each lane, we need to know
    // which input we choose and which of the lanes we choose.
    mozilla::Array<uint8_t, 16> lane_;
    uint32_t arity_;

    MSimdShuffleBase(const uint8_t lanes[], MIRType type)
    {
        arity_ = SimdTypeToLength(type);
        for (unsigned i = 0; i < arity_; i++)
            lane_[i] = lanes[i];
    }

    bool sameLanes(const MSimdShuffleBase* other) const {
        return arity_ == other->arity_ &&
               memcmp(&lane_[0], &other->lane_[0], arity_) == 0;
    }

  public:
    unsigned numLanes() const {
        return arity_;
    }

    unsigned lane(unsigned i) const {
        MOZ_ASSERT(i < arity_);
        return lane_[i];
    }

    bool lanesMatch(uint32_t x, uint32_t y, uint32_t z, uint32_t w) const {
        return arity_ == 4 && lane(0) == x && lane(1) == y && lane(2) == z &&
               lane(3) == w;
    }
};

// Applies a swizzle operation to the input, putting the input lanes as
// indicated in the output register's lanes. This implements the SIMD.js
// "swizzle" function, that takes one vector and an array of lane indexes.
class MSimdSwizzle
  : public MUnaryInstruction,
    public MSimdShuffleBase,
    public NoTypePolicy::Data
{
  protected:
    MSimdSwizzle(MDefinition* obj, const uint8_t lanes[])
      : MUnaryInstruction(classOpcode, obj), MSimdShuffleBase(lanes, obj->type())
    {
        for (unsigned i = 0; i < arity_; i++)
            MOZ_ASSERT(lane(i) < arity_);
        setResultType(obj->type());
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(SimdSwizzle)
    TRIVIAL_NEW_WRAPPERS

    bool congruentTo(const MDefinition* ins) const override {
        if (!ins->isSimdSwizzle())
            return false;
        const MSimdSwizzle* other = ins->toSimdSwizzle();
        return sameLanes(other) && congruentIfOperandsEqual(other);
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    MDefinition* foldsTo(TempAllocator& alloc) override;

    ALLOW_CLONE(MSimdSwizzle)
};

// A "general shuffle" is a swizzle or a shuffle with non-constant lane
// indices.  This is the one that Ion inlines and it can be folded into a
// MSimdSwizzle/MSimdShuffle if lane indices are constant.  Performance of
// general swizzle/shuffle does not really matter, as we expect to get
// constant indices most of the time.
class MSimdGeneralShuffle :
    public MVariadicInstruction,
    public SimdShufflePolicy::Data
{
    unsigned numVectors_;
    unsigned numLanes_;

  protected:
    MSimdGeneralShuffle(unsigned numVectors, unsigned numLanes, MIRType type)
      : MVariadicInstruction(classOpcode), numVectors_(numVectors), numLanes_(numLanes)
    {
        MOZ_ASSERT(IsSimdType(type));
        MOZ_ASSERT(SimdTypeToLength(type) == numLanes_);

        setResultType(type);
        specialization_ = type;
        setGuard(); // throws if lane index is out of bounds
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(SimdGeneralShuffle);
    TRIVIAL_NEW_WRAPPERS

    MOZ_MUST_USE bool init(TempAllocator& alloc) {
        return MVariadicInstruction::init(alloc, numVectors_ + numLanes_);
    }
    void setVector(unsigned i, MDefinition* vec) {
        MOZ_ASSERT(i < numVectors_);
        initOperand(i, vec);
    }
    void setLane(unsigned i, MDefinition* laneIndex) {
        MOZ_ASSERT(i < numLanes_);
        initOperand(numVectors_ + i, laneIndex);
    }

    unsigned numVectors() const {
        return numVectors_;
    }
    unsigned numLanes() const {
        return numLanes_;
    }
    MDefinition* vector(unsigned i) const {
        MOZ_ASSERT(i < numVectors_);
        return getOperand(i);
    }
    MDefinition* lane(unsigned i) const {
        MOZ_ASSERT(i < numLanes_);
        return getOperand(numVectors_ + i);
    }

    bool congruentTo(const MDefinition* ins) const override {
        if (!ins->isSimdGeneralShuffle())
            return false;
        const MSimdGeneralShuffle* other = ins->toSimdGeneralShuffle();
        return numVectors_ == other->numVectors() &&
               numLanes_ == other->numLanes() &&
               congruentIfOperandsEqual(other);
    }

    MDefinition* foldsTo(TempAllocator& alloc) override;

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
};

// Applies a shuffle operation to the inputs. The lane indexes select a source
// lane from the concatenation of the two input vectors.
class MSimdShuffle
  : public MBinaryInstruction,
    public MSimdShuffleBase,
    public NoTypePolicy::Data
{
    MSimdShuffle(MDefinition* lhs, MDefinition* rhs, const uint8_t lanes[])
      : MBinaryInstruction(classOpcode, lhs, rhs), MSimdShuffleBase(lanes, lhs->type())
    {
        MOZ_ASSERT(IsSimdType(lhs->type()));
        MOZ_ASSERT(IsSimdType(rhs->type()));
        MOZ_ASSERT(lhs->type() == rhs->type());
        for (unsigned i = 0; i < arity_; i++)
            MOZ_ASSERT(lane(i) < 2 * arity_);
        setResultType(lhs->type());
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(SimdShuffle)

    static MInstruction* New(TempAllocator& alloc, MDefinition* lhs, MDefinition* rhs,
                             const uint8_t lanes[])
    {
        unsigned arity = SimdTypeToLength(lhs->type());

        // Swap operands so that new lanes come from LHS in majority.
        // In the balanced case, swap operands if needs be, in order to be able
        // to do only one vshufps on x86.
        unsigned lanesFromLHS = 0;
        for (unsigned i = 0; i < arity; i++) {
            if (lanes[i] < arity)
                lanesFromLHS++;
        }

        if (lanesFromLHS < arity / 2 ||
            (arity == 4 && lanesFromLHS == 2 && lanes[0] >= 4 && lanes[1] >= 4)) {
            mozilla::Array<uint8_t, 16> newLanes;
            for (unsigned i = 0; i < arity; i++)
                newLanes[i] = (lanes[i] + arity) % (2 * arity);
            return New(alloc, rhs, lhs, &newLanes[0]);
        }

        // If all lanes come from the same vector, just use swizzle instead.
        if (lanesFromLHS == arity)
            return MSimdSwizzle::New(alloc, lhs, lanes);

        return new(alloc) MSimdShuffle(lhs, rhs, lanes);
    }

    bool congruentTo(const MDefinition* ins) const override {
        if (!ins->isSimdShuffle())
            return false;
        const MSimdShuffle* other = ins->toSimdShuffle();
        return sameLanes(other) && binaryCongruentTo(other);
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    ALLOW_CLONE(MSimdShuffle)
};

class MSimdUnaryArith
  : public MUnaryInstruction,
    public SimdSameAsReturnedTypePolicy<0>::Data
{
  public:
    enum Operation {
#define OP_LIST_(OP) OP,
        FOREACH_FLOAT_SIMD_UNOP(OP_LIST_)
        neg,
        not_
#undef OP_LIST_
    };

    static const char* OperationName(Operation op) {
        switch (op) {
          case abs:                         return "abs";
          case neg:                         return "neg";
          case not_:                        return "not";
          case reciprocalApproximation:     return "reciprocalApproximation";
          case reciprocalSqrtApproximation: return "reciprocalSqrtApproximation";
          case sqrt:                        return "sqrt";
        }
        MOZ_CRASH("unexpected operation");
    }

  private:
    Operation operation_;

    MSimdUnaryArith(MDefinition* def, Operation op)
      : MUnaryInstruction(classOpcode, def), operation_(op)
    {
        MIRType type = def->type();
        MOZ_ASSERT(IsSimdType(type));
        MOZ_ASSERT_IF(IsIntegerSimdType(type), op == neg || op == not_);
        setResultType(type);
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(SimdUnaryArith)
    TRIVIAL_NEW_WRAPPERS

    Operation operation() const { return operation_; }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins) && ins->toSimdUnaryArith()->operation() == operation();
    }

    void printOpcode(GenericPrinter& out) const override;

    ALLOW_CLONE(MSimdUnaryArith);
};

// Compares each value of a SIMD vector to each corresponding lane's value of
// another SIMD vector, and returns a boolean vector containing the results of
// the comparison: all bits are set to 1 if the comparison is true, 0 otherwise.
// When comparing integer vectors, a SimdSign must be provided to request signed
// or unsigned comparison.
class MSimdBinaryComp
  : public MBinaryInstruction,
    public SimdAllPolicy::Data
{
  public:
    enum Operation {
#define NAME_(x) x,
        FOREACH_COMP_SIMD_OP(NAME_)
#undef NAME_
    };

    static const char* OperationName(Operation op) {
        switch (op) {
#define NAME_(x) case x: return #x;
        FOREACH_COMP_SIMD_OP(NAME_)
#undef NAME_
        }
        MOZ_CRASH("unexpected operation");
    }

  private:
    Operation operation_;
    SimdSign sign_;

    MSimdBinaryComp(MDefinition* left, MDefinition* right, Operation op, SimdSign sign)
      : MBinaryInstruction(classOpcode, left, right), operation_(op), sign_(sign)
    {
        MOZ_ASSERT(left->type() == right->type());
        MIRType opType = left->type();
        MOZ_ASSERT(IsSimdType(opType));
        MOZ_ASSERT((sign != SimdSign::NotApplicable) == IsIntegerSimdType(opType),
                   "Signedness must be specified for integer SIMD compares");
        setResultType(MIRTypeToBooleanSimdType(opType));
        specialization_ = opType;
        setMovable();
        if (op == equal || op == notEqual)
            setCommutative();
    }

    static MSimdBinaryComp* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
                                Operation op, SimdSign sign)
    {
        return new (alloc) MSimdBinaryComp(left, right, op, sign);
    }

  public:
    INSTRUCTION_HEADER(SimdBinaryComp)

    // Create a MSimdBinaryComp or an equivalent sequence of instructions
    // supported by the current target.
    // Add all instructions to the basic block |addTo|.
    static MInstruction* AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left,
                                      MDefinition* right, Operation op, SimdSign sign);

    AliasSet getAliasSet() const override
    {
        return AliasSet::None();
    }

    Operation operation() const { return operation_; }
    SimdSign signedness() const { return sign_; }
    MIRType specialization() const { return specialization_; }

    // Swap the operands and reverse the comparison predicate.
    void reverse() {
        switch (operation()) {
          case greaterThan:        operation_ = lessThan; break;
          case greaterThanOrEqual: operation_ = lessThanOrEqual; break;
          case lessThan:           operation_ = greaterThan; break;
          case lessThanOrEqual:    operation_ = greaterThanOrEqual; break;
          case equal:
          case notEqual:
            break;
          default: MOZ_CRASH("Unexpected compare operation");
        }
        swapOperands();
    }

    bool congruentTo(const MDefinition* ins) const override {
        if (!binaryCongruentTo(ins))
            return false;
        const MSimdBinaryComp* other = ins->toSimdBinaryComp();
        return specialization_ == other->specialization() &&
               operation_ == other->operation() &&
               sign_ == other->signedness();
    }

    void printOpcode(GenericPrinter& out) const override;

    ALLOW_CLONE(MSimdBinaryComp)
};

class MSimdBinaryArith
  : public MBinaryInstruction,
    public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdSameAsReturnedTypePolicy<1> >::Data
{
  public:
    enum Operation {
#define OP_LIST_(OP) Op_##OP,
        FOREACH_NUMERIC_SIMD_BINOP(OP_LIST_)
        FOREACH_FLOAT_SIMD_BINOP(OP_LIST_)
#undef OP_LIST_
    };

    static const char* OperationName(Operation op) {
        switch (op) {
#define OP_CASE_LIST_(OP) case Op_##OP: return #OP;
          FOREACH_NUMERIC_SIMD_BINOP(OP_CASE_LIST_)
          FOREACH_FLOAT_SIMD_BINOP(OP_CASE_LIST_)
#undef OP_CASE_LIST_
        }
        MOZ_CRASH("unexpected operation");
    }

  private:
    Operation operation_;

    MSimdBinaryArith(MDefinition* left, MDefinition* right, Operation op)
      : MBinaryInstruction(classOpcode, left, right), operation_(op)
    {
        MOZ_ASSERT(left->type() == right->type());
        MIRType type = left->type();
        MOZ_ASSERT(IsSimdType(type));
        MOZ_ASSERT_IF(IsIntegerSimdType(type), op == Op_add || op == Op_sub || op == Op_mul);
        setResultType(type);
        setMovable();
        if (op == Op_add || op == Op_mul || op == Op_min || op == Op_max)
            setCommutative();
    }

    static MSimdBinaryArith* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
                                 Operation op)
    {
        return new (alloc) MSimdBinaryArith(left, right, op);
    }

  public:
    INSTRUCTION_HEADER(SimdBinaryArith)

    // Create an MSimdBinaryArith instruction and add it to the basic block. Possibly
    // create and add an equivalent sequence of instructions instead if the
    // current target doesn't support the requested shift operation directly.
    static MInstruction* AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left,
                                      MDefinition* right, Operation op);

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    Operation operation() const { return operation_; }

    bool congruentTo(const MDefinition* ins) const override {
        if (!binaryCongruentTo(ins))
            return false;
        return operation_ == ins->toSimdBinaryArith()->operation();
    }

    void printOpcode(GenericPrinter& out) const override;

    ALLOW_CLONE(MSimdBinaryArith)
};

class MSimdBinarySaturating
  : public MBinaryInstruction,
    public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdSameAsReturnedTypePolicy<1>>::Data
{
  public:
    enum Operation
    {
        add,
        sub,
    };

    static const char* OperationName(Operation op)
    {
        switch (op) {
          case add:
            return "add";
          case sub:
            return "sub";
        }
        MOZ_CRASH("unexpected operation");
    }

  private:
    Operation operation_;
    SimdSign sign_;

    MSimdBinarySaturating(MDefinition* left, MDefinition* right, Operation op, SimdSign sign)
      : MBinaryInstruction(classOpcode, left, right)
      , operation_(op)
      , sign_(sign)
    {
        MOZ_ASSERT(left->type() == right->type());
        MIRType type = left->type();
        MOZ_ASSERT(type == MIRType::Int8x16 || type == MIRType::Int16x8);
        setResultType(type);
        setMovable();
        if (op == add)
            setCommutative();
    }

  public:
    INSTRUCTION_HEADER(SimdBinarySaturating)
    TRIVIAL_NEW_WRAPPERS

    AliasSet getAliasSet() const override { return AliasSet::None(); }

    Operation operation() const { return operation_; }
    SimdSign signedness() const { return sign_; }

    bool congruentTo(const MDefinition* ins) const override
    {
        if (!binaryCongruentTo(ins))
            return false;
        return operation_ == ins->toSimdBinarySaturating()->operation() &&
               sign_ == ins->toSimdBinarySaturating()->signedness();
    }

    void printOpcode(GenericPrinter& out) const override;

    ALLOW_CLONE(MSimdBinarySaturating)
};

class MSimdBinaryBitwise
  : public MBinaryInstruction,
    public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdSameAsReturnedTypePolicy<1> >::Data
{
  public:
    enum Operation {
        and_,
        or_,
        xor_
    };

    static const char* OperationName(Operation op) {
        switch (op) {
          case and_: return "and";
          case or_:  return "or";
          case xor_: return "xor";
        }
        MOZ_CRASH("unexpected operation");
    }

  private:
    Operation operation_;

    MSimdBinaryBitwise(MDefinition* left, MDefinition* right, Operation op)
      : MBinaryInstruction(classOpcode, left, right), operation_(op)
    {
        MOZ_ASSERT(left->type() == right->type());
        MIRType type = left->type();
        MOZ_ASSERT(IsSimdType(type));
        setResultType(type);
        setMovable();
        setCommutative();
    }

  public:
    INSTRUCTION_HEADER(SimdBinaryBitwise)
    TRIVIAL_NEW_WRAPPERS

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    Operation operation() const { return operation_; }

    bool congruentTo(const MDefinition* ins) const override {
        if (!binaryCongruentTo(ins))
            return false;
        return operation_ == ins->toSimdBinaryBitwise()->operation();
    }

    void printOpcode(GenericPrinter& out) const override;

    ALLOW_CLONE(MSimdBinaryBitwise)
};

class MSimdShift
  : public MBinaryInstruction,
    public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdScalarPolicy<1> >::Data
{
  public:
    enum Operation {
        lsh,
        rsh,
        ursh
    };

  private:
    Operation operation_;

    MSimdShift(MDefinition* left, MDefinition* right, Operation op)
      : MBinaryInstruction(classOpcode, left, right), operation_(op)
    {
        MIRType type = left->type();
        MOZ_ASSERT(IsIntegerSimdType(type));
        setResultType(type);
        setMovable();
    }

    static MSimdShift* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
                           Operation op)
    {
        return new (alloc) MSimdShift(left, right, op);
    }

  public:
    INSTRUCTION_HEADER(SimdShift)

    // Create an MSimdShift instruction and add it to the basic block. Possibly
    // create and add an equivalent sequence of instructions instead if the
    // current target doesn't support the requested shift operation directly.
    // Return the inserted MInstruction that computes the shifted value.
    static MInstruction* AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left,
                                      MDefinition* right, Operation op);

    // Get the relevant right shift operation given the signedness of a type.
    static Operation rshForSign(SimdSign sign) {
        return sign == SimdSign::Unsigned ? ursh : rsh;
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    Operation operation() const { return operation_; }

    static const char* OperationName(Operation op) {
        switch (op) {
          case lsh:  return "lsh";
          case rsh:  return "rsh-arithmetic";
          case ursh: return "rsh-logical";
        }
        MOZ_CRASH("unexpected operation");
    }

    void printOpcode(GenericPrinter& out) const override;

    bool congruentTo(const MDefinition* ins) const override {
        if (!binaryCongruentTo(ins))
            return false;
        return operation_ == ins->toSimdShift()->operation();
    }

    ALLOW_CLONE(MSimdShift)
};

class MSimdSelect
  : public MTernaryInstruction,
    public SimdSelectPolicy::Data
{
    MSimdSelect(MDefinition* mask, MDefinition* lhs, MDefinition* rhs)
      : MTernaryInstruction(classOpcode, mask, lhs, rhs)
    {
        MOZ_ASSERT(IsBooleanSimdType(mask->type()));
        MOZ_ASSERT(lhs->type() == lhs->type());
        MIRType type = lhs->type();
        MOZ_ASSERT(IsSimdType(type));
        setResultType(type);
        specialization_ = type;
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(SimdSelect)
    TRIVIAL_NEW_WRAPPERS
    NAMED_OPERANDS((0, mask))

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins);
    }

    ALLOW_CLONE(MSimdSelect)
};

// Deep clone a constant JSObject.
class MCloneLiteral
  : public MUnaryInstruction,
    public ObjectPolicy<0>::Data
{
  protected:
    explicit MCloneLiteral(MDefinition* obj)
      : MUnaryInstruction(classOpcode, obj)
    {
        setResultType(MIRType::Object);
    }

  public:
    INSTRUCTION_HEADER(CloneLiteral)
    TRIVIAL_NEW_WRAPPERS
};

class MParameter : public MNullaryInstruction
{
    int32_t index_;

    MParameter(int32_t index, TemporaryTypeSet* types)
      : MNullaryInstruction(classOpcode),
        index_(index)
    {
        setResultType(MIRType::Value);
        setResultTypeSet(types);
    }

  public:
    INSTRUCTION_HEADER(Parameter)
    TRIVIAL_NEW_WRAPPERS

    static const int32_t THIS_SLOT = -1;
    int32_t index() const {
        return index_;
    }
    void printOpcode(GenericPrinter& out) const override;

    HashNumber valueHash() const override;
    bool congruentTo(const MDefinition* ins) const override;
};

class MCallee : public MNullaryInstruction
{
  public:
    MCallee()
      : MNullaryInstruction(classOpcode)
    {
        setResultType(MIRType::Object);
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(Callee)
    TRIVIAL_NEW_WRAPPERS

    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins);
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
};

class MIsConstructing : public MNullaryInstruction
{
  public:
    MIsConstructing()
      : MNullaryInstruction(classOpcode)
    {
        setResultType(MIRType::Boolean);
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(IsConstructing)
    TRIVIAL_NEW_WRAPPERS

    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins);
    }
    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
};

class MControlInstruction : public MInstruction
{
  protected:
    explicit MControlInstruction(Opcode op)
      : MInstruction(op)
    { }

  public:
    virtual size_t numSuccessors() const = 0;
    virtual MBasicBlock* getSuccessor(size_t i) const = 0;
    virtual void replaceSuccessor(size_t i, MBasicBlock* successor) = 0;

    bool isControlInstruction() const override {
        return true;
    }

    void printOpcode(GenericPrinter& out) const override;
};

class MTableSwitch final
  : public MControlInstruction,
    public NoFloatPolicy<0>::Data
{
    // The successors of the tableswitch
    // - First successor = the default case
    // - Successors 2 and higher = the cases
    Vector<MBasicBlock*, 0, JitAllocPolicy> successors_;
    // Index into successors_ sorted on case index
    Vector<size_t, 0, JitAllocPolicy> cases_;

    MUse operand_;
    int32_t low_;
    int32_t high_;

    void initOperand(size_t index, MDefinition* operand) {
        MOZ_ASSERT(index == 0);
        operand_.init(operand, this);
    }

    MTableSwitch(TempAllocator& alloc, MDefinition* ins,
                 int32_t low, int32_t high)
      : MControlInstruction(classOpcode),
        successors_(alloc),
        cases_(alloc),
        low_(low),
        high_(high)
    {
        initOperand(0, ins);
    }

  protected:
    MUse* getUseFor(size_t index) override {
        MOZ_ASSERT(index == 0);
        return &operand_;
    }

    const MUse* getUseFor(size_t index) const override {
        MOZ_ASSERT(index == 0);
        return &operand_;
    }

  public:
    INSTRUCTION_HEADER(TableSwitch)
    static MTableSwitch* New(TempAllocator& alloc, MDefinition* ins, int32_t low, int32_t high);

    size_t numSuccessors() const override {
        return successors_.length();
    }

    MOZ_MUST_USE bool addSuccessor(MBasicBlock* successor, size_t* index) {
        MOZ_ASSERT(successors_.length() < (size_t)(high_ - low_ + 2));
        MOZ_ASSERT(!successors_.empty());
        *index = successors_.length();
        return successors_.append(successor);
    }

    MBasicBlock* getSuccessor(size_t i) const override {
        MOZ_ASSERT(i < numSuccessors());
        return successors_[i];
    }

    void replaceSuccessor(size_t i, MBasicBlock* successor) override {
        MOZ_ASSERT(i < numSuccessors());
        successors_[i] = successor;
    }

    int32_t low() const {
        return low_;
    }

    int32_t high() const {
        return high_;
    }

    MBasicBlock* getDefault() const {
        return getSuccessor(0);
    }

    MBasicBlock* getCase(size_t i) const {
        return getSuccessor(cases_[i]);
    }

    MOZ_MUST_USE bool addDefault(MBasicBlock* block, size_t* index = nullptr) {
        MOZ_ASSERT(successors_.empty());
        if (index)
            *index = 0;
        return successors_.append(block);
    }

    MOZ_MUST_USE bool addCase(size_t successorIndex) {
        return cases_.append(successorIndex);
    }

    size_t numCases() const {
        return high() - low() + 1;
    }

    MDefinition* getOperand(size_t index) const override {
        MOZ_ASSERT(index == 0);
        return operand_.producer();
    }

    size_t numOperands() const override {
        return 1;
    }

    size_t indexOf(const MUse* u) const final override {
        MOZ_ASSERT(u == getUseFor(0));
        return 0;
    }

    void replaceOperand(size_t index, MDefinition* operand) final override {
        MOZ_ASSERT(index == 0);
        operand_.replaceProducer(operand);
    }

    MDefinition* foldsTo(TempAllocator& alloc) override;
};

template <size_t Arity, size_t Successors>
class MAryControlInstruction : public MControlInstruction
{
    mozilla::Array<MUse, Arity> operands_;
    mozilla::Array<MBasicBlock*, Successors> successors_;

  protected:
    explicit MAryControlInstruction(Opcode op)
      : MControlInstruction(op)
    { }
    void setSuccessor(size_t index, MBasicBlock* successor) {
        successors_[index] = successor;
    }

    MUse* getUseFor(size_t index) final override {
        return &operands_[index];
    }
    const MUse* getUseFor(size_t index) const final override {
        return &operands_[index];
    }
    void initOperand(size_t index, MDefinition* operand) {
        operands_[index].init(operand, this);
    }

  public:
    MDefinition* getOperand(size_t index) const final override {
        return operands_[index].producer();
    }
    size_t numOperands() const final override {
        return Arity;
    }
    size_t indexOf(const MUse* u) const final override {
        MOZ_ASSERT(u >= &operands_[0]);
        MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
        return u - &operands_[0];
    }
    void replaceOperand(size_t index, MDefinition* operand) final override {
        operands_[index].replaceProducer(operand);
    }
    size_t numSuccessors() const final override {
        return Successors;
    }
    MBasicBlock* getSuccessor(size_t i) const final override {
        return successors_[i];
    }
    void replaceSuccessor(size_t i, MBasicBlock* succ) final override {
        successors_[i] = succ;
    }
};

// Jump to the start of another basic block.
class MGoto
  : public MAryControlInstruction<0, 1>,
    public NoTypePolicy::Data
{
    explicit MGoto(MBasicBlock* target)
      : MAryControlInstruction(classOpcode)
    {
        setSuccessor(0, target);
    }

  public:
    INSTRUCTION_HEADER(Goto)
    static MGoto* New(TempAllocator& alloc, MBasicBlock* target);
    static MGoto* New(TempAllocator::Fallible alloc, MBasicBlock* target);

    // Variant that may patch the target later.
    static MGoto* New(TempAllocator& alloc);

    static const size_t TargetIndex = 0;

    MBasicBlock* target() {
        return getSuccessor(0);
    }
    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
};

enum BranchDirection {
    FALSE_BRANCH,
    TRUE_BRANCH
};

static inline BranchDirection
NegateBranchDirection(BranchDirection dir)
{
    return (dir == FALSE_BRANCH) ? TRUE_BRANCH : FALSE_BRANCH;
}

// Tests if the input instruction evaluates to true or false, and jumps to the
// start of a corresponding basic block.
class MTest
  : public MAryControlInstruction<1, 2>,
    public TestPolicy::Data
{
    bool operandMightEmulateUndefined_;

    MTest(MDefinition* ins, MBasicBlock* trueBranch, MBasicBlock* falseBranch)
      : MAryControlInstruction(classOpcode),
        operandMightEmulateUndefined_(true)
    {
        initOperand(0, ins);
        setSuccessor(0, trueBranch);
        setSuccessor(1, falseBranch);
    }

    // Variant which may patch the ifTrue branch later.
    MTest(MDefinition* ins, MBasicBlock* falseBranch)
      : MTest(ins, nullptr, falseBranch)
    {}

  public:
    INSTRUCTION_HEADER(Test)
    TRIVIAL_NEW_WRAPPERS
    NAMED_OPERANDS((0, input))

    static const size_t TrueBranchIndex = 0;

    MBasicBlock* ifTrue() const {
        return getSuccessor(0);
    }
    MBasicBlock* ifFalse() const {
        return getSuccessor(1);
    }
    MBasicBlock* branchSuccessor(BranchDirection dir) const {
        return (dir == TRUE_BRANCH) ? ifTrue() : ifFalse();
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    // We cache whether our operand might emulate undefined, but we don't want
    // to do that from New() or the constructor, since those can be called on
    // background threads.  So make callers explicitly call it if they want us
    // to check whether the operand might do this.  If this method is never
    // called, we'll assume our operand can emulate undefined.
    void cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints);
    MDefinition* foldsDoubleNegation(TempAllocator& alloc);
    MDefinition* foldsConstant(TempAllocator& alloc);
    MDefinition* foldsTypes(TempAllocator& alloc);
    MDefinition* foldsNeedlessControlFlow(TempAllocator& alloc);
    MDefinition* foldsTo(TempAllocator& alloc) override;
    void filtersUndefinedOrNull(bool trueBranch, MDefinition** subject, bool* filtersUndefined,
                                bool* filtersNull);

    void markNoOperandEmulatesUndefined() {
        operandMightEmulateUndefined_ = false;
    }
    bool operandMightEmulateUndefined() const {
        return operandMightEmulateUndefined_;
    }
#ifdef DEBUG
    bool isConsistentFloat32Use(MUse* use) const override {
        return true;
    }
#endif
};

// Equivalent to MTest(true, successor, fake), except without the foldsTo
// method. This allows IonBuilder to insert fake CFG edges to magically protect
// control flow for try-catch blocks.
class MGotoWithFake
  : public MAryControlInstruction<0, 2>,
    public NoTypePolicy::Data
{
    MGotoWithFake(MBasicBlock* successor, MBasicBlock* fake)
      : MAryControlInstruction(classOpcode)
    {
        setSuccessor(0, successor);
        setSuccessor(1, fake);
    }

  public:
    INSTRUCTION_HEADER(GotoWithFake)
    TRIVIAL_NEW_WRAPPERS

    MBasicBlock* target() const {
        return getSuccessor(0);
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
};

// Returns from this function to the previous caller.
class MReturn
  : public MAryControlInstruction<1, 0>,
    public BoxInputsPolicy::Data
{
    explicit MReturn(MDefinition* ins)
      : MAryControlInstruction(classOpcode)
    {
        initOperand(0, ins);
    }

  public:
    INSTRUCTION_HEADER(Return)
    TRIVIAL_NEW_WRAPPERS
    NAMED_OPERANDS((0, input))

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
};

class MThrow
  : public MAryControlInstruction<1, 0>,
    public BoxInputsPolicy::Data
{
    explicit MThrow(MDefinition* ins)
      : MAryControlInstruction(classOpcode)
    {
        initOperand(0, ins);
    }

  public:
    INSTRUCTION_HEADER(Throw)
    TRIVIAL_NEW_WRAPPERS

    virtual AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
    bool possiblyCalls() const override {
        return true;
    }
};

// Fabricate a type set containing only the type of the specified object.
TemporaryTypeSet*
MakeSingletonTypeSet(TempAllocator& alloc, CompilerConstraintList* constraints, JSObject* obj);

TemporaryTypeSet*
MakeSingletonTypeSet(TempAllocator& alloc, CompilerConstraintList* constraints, ObjectGroup* obj);

MOZ_MUST_USE bool
MergeTypes(TempAllocator& alloc, MIRType* ptype, TemporaryTypeSet** ptypeSet,
           MIRType newType, TemporaryTypeSet* newTypeSet);

bool
TypeSetIncludes(TypeSet* types, MIRType input, TypeSet* inputTypes);

bool
EqualTypes(MIRType type1, TemporaryTypeSet* typeset1,
           MIRType type2, TemporaryTypeSet* typeset2);

bool
CanStoreUnboxedType(TempAllocator& alloc,
                    JSValueType unboxedType, MIRType input, TypeSet* inputTypes);

class MNewArray
  : public MUnaryInstruction,
    public NoTypePolicy::Data
{
  private:
    // Number of elements to allocate for the array.
    uint32_t length_;

    // Heap where the array should be allocated.
    gc::InitialHeap initialHeap_;

    // Whether values written to this array should be converted to double first.
    bool convertDoubleElements_;

    jsbytecode* pc_;

    bool vmCall_;

    MNewArray(TempAllocator& alloc, CompilerConstraintList* constraints, uint32_t length,
              MConstant* templateConst, gc::InitialHeap initialHeap, jsbytecode* pc,
              bool vmCall = false);

  public:
    INSTRUCTION_HEADER(NewArray)
    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC

    static MNewArray* NewVM(TempAllocator& alloc, CompilerConstraintList* constraints,
                            uint32_t length, MConstant* templateConst,
                            gc::InitialHeap initialHeap, jsbytecode* pc)
    {
        return new(alloc) MNewArray(alloc, constraints, length, templateConst, initialHeap, pc,
                                    true);
    }

    uint32_t length() const {
        return length_;
    }

    JSObject* templateObject() const {
        return getOperand(0)->toConstant()->toObjectOrNull();
    }

    gc::InitialHeap initialHeap() const {
        return initialHeap_;
    }

    jsbytecode* pc() const {
        return pc_;
    }

    bool isVMCall() const {
        return vmCall_;
    }

    bool convertDoubleElements() const {
        return convertDoubleElements_;
    }

    // NewArray is marked as non-effectful because all our allocations are
    // either lazy when we are using "new Array(length)" or bounded by the
    // script or the stack size when we are using "new Array(...)" or "[...]"
    // notations.  So we might have to allocate the array twice if we bail
    // during the computation of the first element of the square braket
    // notation.
    virtual AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        // The template object can safely be used in the recover instruction
        // because it can never be mutated by any other function execution.
        return templateObject() != nullptr;
    }
};

class MNewArrayCopyOnWrite : public MNullaryInstruction
{
    CompilerGCPointer<ArrayObject*> templateObject_;
    gc::InitialHeap initialHeap_;

    MNewArrayCopyOnWrite(TempAllocator& alloc, CompilerConstraintList* constraints,
                         ArrayObject* templateObject, gc::InitialHeap initialHeap)
      : MNullaryInstruction(classOpcode),
        templateObject_(templateObject),
        initialHeap_(initialHeap)
    {
        MOZ_ASSERT(!templateObject->isSingleton());
        setResultType(MIRType::Object);
        setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
    }

  public:
    INSTRUCTION_HEADER(NewArrayCopyOnWrite)
    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC

    ArrayObject* templateObject() const {
        return templateObject_;
    }

    gc::InitialHeap initialHeap() const {
        return initialHeap_;
    }

    virtual AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    bool appendRoots(MRootList& roots) const override {
        return roots.append(templateObject_);
    }
};

class MNewArrayDynamicLength
  : public MUnaryInstruction,
    public IntPolicy<0>::Data
{
    CompilerObject templateObject_;
    gc::InitialHeap initialHeap_;

    MNewArrayDynamicLength(TempAllocator& alloc, CompilerConstraintList* constraints,
                           JSObject* templateObject, gc::InitialHeap initialHeap,
                           MDefinition* length)
      : MUnaryInstruction(classOpcode, length),
        templateObject_(templateObject),
        initialHeap_(initialHeap)
    {
        setGuard(); // Need to throw if length is negative.
        setResultType(MIRType::Object);
        if (!templateObject->isSingleton())
            setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
    }

  public:
    INSTRUCTION_HEADER(NewArrayDynamicLength)
    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
    NAMED_OPERANDS((0, length))

    JSObject* templateObject() const {
        return templateObject_;
    }
    gc::InitialHeap initialHeap() const {
        return initialHeap_;
    }

    virtual AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    bool appendRoots(MRootList& roots) const override {
        return roots.append(templateObject_);
    }
};

class MNewTypedArray
  : public MUnaryInstruction,
    public NoTypePolicy::Data
{
    gc::InitialHeap initialHeap_;

    MNewTypedArray(TempAllocator& alloc, CompilerConstraintList* constraints,
                   MConstant* templateConst, gc::InitialHeap initialHeap)
      : MUnaryInstruction(classOpcode, templateConst),
        initialHeap_(initialHeap)
    {
        MOZ_ASSERT(!templateObject()->isSingleton());
        setResultType(MIRType::Object);
        setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject()));
    }

  public:
    INSTRUCTION_HEADER(NewTypedArray)
    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC

    TypedArrayObject* templateObject() const {
        return &getOperand(0)->toConstant()->toObject().as<TypedArrayObject>();
    }

    gc::InitialHeap initialHeap() const {
        return initialHeap_;
    }

    virtual AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        return true;
    }
};

class MNewTypedArrayDynamicLength
  : public MUnaryInstruction,
    public IntPolicy<0>::Data
{
    CompilerObject templateObject_;
    gc::InitialHeap initialHeap_;

    MNewTypedArrayDynamicLength(TempAllocator& alloc, CompilerConstraintList* constraints,
                                JSObject* templateObject, gc::InitialHeap initialHeap,
                                MDefinition* length)
      : MUnaryInstruction(classOpcode, length),
        templateObject_(templateObject),
        initialHeap_(initialHeap)
    {
        setGuard(); // Need to throw if length is negative.
        setResultType(MIRType::Object);
        if (!templateObject->isSingleton())
            setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
    }

  public:
    INSTRUCTION_HEADER(NewTypedArrayDynamicLength)
    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC

    MDefinition* length() const {
        return getOperand(0);
    }
    JSObject* templateObject() const {
        return templateObject_;
    }
    gc::InitialHeap initialHeap() const {
        return initialHeap_;
    }

    virtual AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    bool appendRoots(MRootList& roots) const override {
        return roots.append(templateObject_);
    }
};

class MNewObject
  : public MUnaryInstruction,
    public NoTypePolicy::Data
{
  public:
    enum Mode { ObjectLiteral, ObjectCreate };

  private:
    gc::InitialHeap initialHeap_;
    Mode mode_;
    bool vmCall_;

    MNewObject(TempAllocator& alloc, CompilerConstraintList* constraints, MConstant* templateConst,
               gc::InitialHeap initialHeap, Mode mode, bool vmCall = false)
      : MUnaryInstruction(classOpcode, templateConst),
        initialHeap_(initialHeap),
        mode_(mode),
        vmCall_(vmCall)
    {
        MOZ_ASSERT_IF(mode != ObjectLiteral, templateObject());
        setResultType(MIRType::Object);

        if (JSObject* obj = templateObject())
            setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, obj));

        // The constant is kept separated in a MConstant, this way we can safely
        // mark it during GC if we recover the object allocation.  Otherwise, by
        // making it emittedAtUses, we do not produce register allocations for
        // it and inline its content inside the code produced by the
        // CodeGenerator.
        if (templateConst->toConstant()->type() == MIRType::Object)
            templateConst->setEmittedAtUses();
    }

  public:
    INSTRUCTION_HEADER(NewObject)
    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC

    static MNewObject* NewVM(TempAllocator& alloc, CompilerConstraintList* constraints,
                             MConstant* templateConst, gc::InitialHeap initialHeap,
                             Mode mode)
    {
        return new(alloc) MNewObject(alloc, constraints, templateConst, initialHeap, mode, true);
    }

    Mode mode() const {
        return mode_;
    }

    JSObject* templateObject() const {
        return getOperand(0)->toConstant()->toObjectOrNull();
    }

    gc::InitialHeap initialHeap() const {
        return initialHeap_;
    }

    bool isVMCall() const {
        return vmCall_;
    }

    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        // The template object can safely be used in the recover instruction
        // because it can never be mutated by any other function execution.
        return templateObject() != nullptr;
    }
};


class MNewIterator
  : public MUnaryInstruction,
    public NoTypePolicy::Data
{
  public:
    enum Type {
        ArrayIterator,
        StringIterator,
    };

private:
    Type type_;

    MNewIterator(TempAllocator& alloc, CompilerConstraintList* constraints,
                 MConstant* templateConst, Type type)
      : MUnaryInstruction(classOpcode, templateConst),
        type_(type)
    {
        setResultType(MIRType::Object);
        setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject()));
        templateConst->setEmittedAtUses();
    }

  public:
    INSTRUCTION_HEADER(NewIterator)
    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC

    Type type() const {
        return type_;
    }

    JSObject* templateObject() {
        return getOperand(0)->toConstant()->toObjectOrNull();
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        return true;
    }
};


class MNewTypedObject : public MNullaryInstruction
{
    CompilerGCPointer<InlineTypedObject*> templateObject_;
    gc::InitialHeap initialHeap_;

    MNewTypedObject(TempAllocator& alloc, CompilerConstraintList* constraints,
                    InlineTypedObject* templateObject,
                    gc::InitialHeap initialHeap)
      : MNullaryInstruction(classOpcode),
        templateObject_(templateObject),
        initialHeap_(initialHeap)
    {
        setResultType(MIRType::Object);
        setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
    }

  public:
    INSTRUCTION_HEADER(NewTypedObject)
    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC

    InlineTypedObject* templateObject() const {
        return templateObject_;
    }

    gc::InitialHeap initialHeap() const {
        return initialHeap_;
    }

    virtual AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    bool appendRoots(MRootList& roots) const override {
        return roots.append(templateObject_);
    }
};

class MTypedObjectDescr
  : public MUnaryInstruction,
    public SingleObjectPolicy::Data
{
  private:
    explicit MTypedObjectDescr(MDefinition* object)
      : MUnaryInstruction(classOpcode, object)
    {
        setResultType(MIRType::Object);
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(TypedObjectDescr)
    TRIVIAL_NEW_WRAPPERS
    NAMED_OPERANDS((0, object))

    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins);
    }
    AliasSet getAliasSet() const override {
        return AliasSet::Load(AliasSet::ObjectFields);
    }
};

// Generic way for constructing a SIMD object in IonMonkey, this instruction
// takes as argument a SIMD instruction and returns a new SIMD object which
// corresponds to the MIRType of its operand.
class MSimdBox
  : public MUnaryInstruction,
    public NoTypePolicy::Data
{
  protected:
    CompilerGCPointer<InlineTypedObject*> templateObject_;
    SimdType simdType_;
    gc::InitialHeap initialHeap_;

    MSimdBox(TempAllocator& alloc,
             CompilerConstraintList* constraints,
             MDefinition* op,
             InlineTypedObject* templateObject,
             SimdType simdType,
             gc::InitialHeap initialHeap)
      : MUnaryInstruction(classOpcode, op),
        templateObject_(templateObject),
        simdType_(simdType),
        initialHeap_(initialHeap)
    {
        MOZ_ASSERT(IsSimdType(op->type()));
        setMovable();
        setResultType(MIRType::Object);
        if (constraints)
            setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
    }

  public:
    INSTRUCTION_HEADER(SimdBox)
    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC

    InlineTypedObject* templateObject() const {
        return templateObject_;
    }

    SimdType simdType() const {
        return simdType_;
    }

    gc::InitialHeap initialHeap() const {
        return initialHeap_;
    }

    bool congruentTo(const MDefinition* ins) const override {
        if (!congruentIfOperandsEqual(ins))
            return false;
        const MSimdBox* box = ins->toSimdBox();
        if (box->simdType() != simdType())
            return false;
        MOZ_ASSERT(box->templateObject() == templateObject());
        if (box->initialHeap() != initialHeap())
            return false;
        return true;
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    void printOpcode(GenericPrinter& out) const override;
    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        return true;
    }

    bool appendRoots(MRootList& roots) const override {
        return roots.append(templateObject_);
    }
};

class MSimdUnbox
  : public MUnaryInstruction,
    public SingleObjectPolicy::Data
{
  protected:
    SimdType simdType_;

    MSimdUnbox(MDefinition* op, SimdType simdType)
      : MUnaryInstruction(classOpcode, op),
        simdType_(simdType)
    {
        MIRType type = SimdTypeToMIRType(simdType);
        MOZ_ASSERT(IsSimdType(type));
        setGuard();
        setMovable();
        setResultType(type);
    }

  public:
    INSTRUCTION_HEADER(SimdUnbox)
    TRIVIAL_NEW_WRAPPERS
    ALLOW_CLONE(MSimdUnbox)

    SimdType simdType() const { return simdType_; }

    MDefinition* foldsTo(TempAllocator& alloc) override;
    bool congruentTo(const MDefinition* ins) const override {
        if (!congruentIfOperandsEqual(ins))
            return false;
        return ins->toSimdUnbox()->simdType() == simdType();
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    void printOpcode(GenericPrinter& out) const override;
};

// Creates a new derived type object. At runtime, this is just a call
// to `BinaryBlock::createDerived()`. That is, the MIR itself does not
// compile to particularly optimized code. However, using a distinct
// MIR for creating derived type objects allows the compiler to
// optimize ephemeral typed objects as would be created for a
// reference like `a.b.c` -- here, the `a.b` will create an ephemeral
// derived type object that aliases the memory of `a` itself. The
// specific nature of `a.b` is revealed by using
// `MNewDerivedTypedObject` rather than `MGetProperty` or what have
// you. Moreover, the compiler knows that there are no side-effects,
// so `MNewDerivedTypedObject` instructions can be reordered or pruned
// as dead code.
class MNewDerivedTypedObject
  : public MTernaryInstruction,
    public Mix3Policy<ObjectPolicy<0>,
                      ObjectPolicy<1>,
                      IntPolicy<2> >::Data
{
  private:
    TypedObjectPrediction prediction_;

    MNewDerivedTypedObject(TypedObjectPrediction prediction,
                           MDefinition* type,
                           MDefinition* owner,
                           MDefinition* offset)
      : MTernaryInstruction(classOpcode, type, owner, offset),
        prediction_(prediction)
    {
        setMovable();
        setResultType(MIRType::Object);
    }

  public:
    INSTRUCTION_HEADER(NewDerivedTypedObject)
    TRIVIAL_NEW_WRAPPERS
    NAMED_OPERANDS((0, type), (1, owner), (2, offset))

    TypedObjectPrediction prediction() const {
        return prediction_;
    }

    virtual AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        return true;
    }
};

// This vector is used when the recovered object is kept unboxed. We map the
// offset of each property to the index of the corresponding operands in the
// object state.
struct OperandIndexMap : public TempObject
{
    // The number of properties is limited by scalar replacement. Thus we cannot
    // have any large number of properties.
    FixedList<uint8_t> map;

    MOZ_MUST_USE bool init(TempAllocator& alloc, JSObject* templateObject);
};

// Represent the content of all slots of an object.  This instruction is not
// lowered and is not used to generate code.
class MObjectState
  : public MVariadicInstruction,
    public NoFloatPolicyAfter<1>::Data
{
  private:
    uint32_t numSlots_;
    uint32_t numFixedSlots_;        // valid if isUnboxed() == false.
    OperandIndexMap* operandIndex_; // valid if isUnboxed() == true.

    bool isUnboxed() const {
        return operandIndex_ != nullptr;
    }

    MObjectState(JSObject *templateObject, OperandIndexMap* operandIndex);
    explicit MObjectState(MObjectState* state);

    MOZ_MUST_USE bool init(TempAllocator& alloc, MDefinition* obj);

    void initSlot(uint32_t slot, MDefinition* def) {
        initOperand(slot + 1, def);
    }

  public:
    INSTRUCTION_HEADER(ObjectState)
    NAMED_OPERANDS((0, object))

    // Return the template object of any object creation which can be recovered
    // on bailout.
    static JSObject* templateObjectOf(MDefinition* obj);

    static MObjectState* New(TempAllocator& alloc, MDefinition* obj);
    static MObjectState* Copy(TempAllocator& alloc, MObjectState* state);

    // As we might do read of uninitialized properties, we have to copy the
    // initial values from the template object.
    MOZ_MUST_USE bool initFromTemplateObject(TempAllocator& alloc, MDefinition* undefinedVal);

    size_t numFixedSlots() const {
        MOZ_ASSERT(!isUnboxed());
        return numFixedSlots_;
    }
    size_t numSlots() const {
        return numSlots_;
    }

    MDefinition* getSlot(uint32_t slot) const {
        return getOperand(slot + 1);
    }
    void setSlot(uint32_t slot, MDefinition* def) {
        replaceOperand(slot + 1, def);
    }

    bool hasFixedSlot(uint32_t slot) const {
        return slot < numSlots() && slot < numFixedSlots();
    }
    MDefinition* getFixedSlot(uint32_t slot) const {
        MOZ_ASSERT(slot < numFixedSlots());
        return getSlot(slot);
    }
    void setFixedSlot(uint32_t slot, MDefinition* def) {
        MOZ_ASSERT(slot < numFixedSlots());
        setSlot(slot, def);
    }

    bool hasDynamicSlot(uint32_t slot) const {
        return numFixedSlots() < numSlots() && slot < numSlots() - numFixedSlots();
    }
    MDefinition* getDynamicSlot(uint32_t slot) const {
        return getSlot(slot + numFixedSlots());
    }
    void setDynamicSlot(uint32_t slot, MDefinition* def) {
        setSlot(slot + numFixedSlots(), def);
    }

    // Interface reserved for unboxed objects.
    bool hasOffset(uint32_t offset) const {
        MOZ_ASSERT(isUnboxed());
        return offset < operandIndex_->map.length() && operandIndex_->map[offset] != 0;
    }
    MDefinition* getOffset(uint32_t offset) const {
        return getOperand(operandIndex_->map[offset]);
    }
    void setOffset(uint32_t offset, MDefinition* def) {
        replaceOperand(operandIndex_->map[offset], def);
    }

    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        return true;
    }
};

// Represent the contents of all elements of an array.  This instruction is not
// lowered and is not used to generate code.
class MArrayState
  : public MVariadicInstruction,
    public NoFloatPolicyAfter<2>::Data
{
  private:
    uint32_t numElements_;

    explicit MArrayState(MDefinition* arr);

    MOZ_MUST_USE bool init(TempAllocator& alloc, MDefinition* obj, MDefinition* len);

    void initElement(uint32_t index, MDefinition* def) {
        initOperand(index + 2, def);
    }

  public:
    INSTRUCTION_HEADER(ArrayState)
    NAMED_OPERANDS((0, array), (1, initializedLength))

    static MArrayState* New(TempAllocator& alloc, MDefinition* arr, MDefinition* undefinedVal,
                            MDefinition* initLength);
    static MArrayState* Copy(TempAllocator& alloc, MArrayState* state);

    void setInitializedLength(MDefinition* def) {
        replaceOperand(1, def);
    }


    size_t numElements() const {
        return numElements_;
    }

    MDefinition* getElement(uint32_t index) const {
        return getOperand(index + 2);
    }
    void setElement(uint32_t index, MDefinition* def) {
        replaceOperand(index + 2, def);
    }

    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        return true;
    }
};

// Hold the arguments of an inlined frame. At the moment this class is not
// recovered on bailout as it does not have an implementation and it should
// be inlined at all its uses.
class MArgumentState
  : public MVariadicInstruction,
    public NoFloatPolicyAfter<0>::Data
{
  private:
    explicit MArgumentState()
      : MVariadicInstruction(classOpcode)
    {
        setResultType(MIRType::Object);
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(ArgumentState)

    static MArgumentState* New(TempAllocator::Fallible view, const MDefinitionVector& args);
    static MArgumentState* Copy(TempAllocator& alloc, MArgumentState* state);

    size_t numElements() const {
        return numOperands();
    }

    MDefinition* getElement(uint32_t index) const {
        return getOperand(index);
    }

    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins);
    }
    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
};

// Setting __proto__ in an object literal.
class MMutateProto
  : public MBinaryInstruction,
    public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
{
  protected:
    MMutateProto(MDefinition* obj, MDefinition* value)
      : MBinaryInstruction(classOpcode, obj, value)
    {
        setResultType(MIRType::None);
    }

  public:
    INSTRUCTION_HEADER(MutateProto)
    TRIVIAL_NEW_WRAPPERS
    NAMED_OPERANDS((0, getObject), (1, getValue))

    bool possiblyCalls() const override {
        return true;
    }
};

class MInitPropGetterSetter
  : public MBinaryInstruction,
    public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
{
    CompilerPropertyName name_;

    MInitPropGetterSetter(MDefinition* obj, PropertyName* name, MDefinition* value)
      : MBinaryInstruction(classOpcode, obj, value),
        name_(name)
    { }

  public:
    INSTRUCTION_HEADER(InitPropGetterSetter)
    TRIVIAL_NEW_WRAPPERS
    NAMED_OPERANDS((0, object), (1, value))

    PropertyName* name() const {
        return name_;
    }

    bool appendRoots(MRootList& roots) const override {
        return roots.append(name_);
    }
};

class MInitElem
  : public MTernaryInstruction,
    public Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, BoxPolicy<2> >::Data
{
    MInitElem(MDefinition* obj, MDefinition* id, MDefinition* value)
      : MTernaryInstruction(classOpcode, obj, id, value)
    {
        setResultType(MIRType::None);
    }

  public:
    INSTRUCTION_HEADER(InitElem)
    TRIVIAL_NEW_WRAPPERS
    NAMED_OPERANDS((0, getObject), (1, getId), (2, getValue))

    bool possiblyCalls() const override {
        return true;
    }
};

class MInitElemGetterSetter
  : public MTernaryInstruction,
    public Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, ObjectPolicy<2> >::Data
{
    MInitElemGetterSetter(MDefinition* obj, MDefinition* id, MDefinition* value)
      : MTernaryInstruction(classOpcode, obj, id, value)
    { }

  public:
    INSTRUCTION_HEADER(InitElemGetterSetter)
    TRIVIAL_NEW_WRAPPERS
    NAMED_OPERANDS((0, object), (1, idValue), (2, value))

};

// WrappedFunction wraps a JSFunction so it can safely be used off-thread.
// In particular, a function's flags can be modified on the active thread as
// functions are relazified and delazified, so we must be careful not to access
// these flags off-thread.
class WrappedFunction : public TempObject
{
    CompilerFunction fun_;
    uint16_t nargs_;
    bool isNative_ : 1;
    bool isConstructor_ : 1;
    bool isClassConstructor_ : 1;
    bool isSelfHostedBuiltin_ : 1;

  public:
    explicit WrappedFunction(JSFunction* fun);
    size_t nargs() const { return nargs_; }
    bool isNative() const { return isNative_; }
    bool isConstructor() const { return isConstructor_; }
    bool isClassConstructor() const { return isClassConstructor_; }
    bool isSelfHostedBuiltin() const { return isSelfHostedBuiltin_; }

    // fun->native() and fun->jitInfo() can safely be called off-thread: these
    // fields never change.
    JSNative native() const { return fun_->native(); }
    const JSJitInfo* jitInfo() const { return fun_->jitInfo(); }

    JSFunction* rawJSFunction() const { return fun_; }

    bool appendRoots(MRootList& roots) const {
        return roots.append(fun_);
    }
};

class MCall
  : public MVariadicInstruction,
    public CallPolicy::Data
{
  private:
    // An MCall uses the MPrepareCall, MDefinition for the function, and
    // MPassArg instructions. They are stored in the same list.
    static const size_t FunctionOperandIndex   = 0;
    static const size_t NumNonArgumentOperands = 1;

  protected:
    // Monomorphic cache of single target from TI, or nullptr.
    WrappedFunction* target_;

    // Original value of argc from the bytecode.
    uint32_t numActualArgs_;

    // True if the call is for JSOP_NEW.
    bool construct_:1;

    // True if the caller does not use the return value.
    bool ignoresReturnValue_:1;

    bool needsArgCheck_:1;

    MCall(WrappedFunction* target, uint32_t numActualArgs, bool construct, bool ignoresReturnValue)
      : MVariadicInstruction(classOpcode),
        target_(target),
        numActualArgs_(numActualArgs),
        construct_(construct),
        ignoresReturnValue_(ignoresReturnValue),
        needsArgCheck_(true)
    {
        setResultType(MIRType::Value);
    }

  public:
    INSTRUCTION_HEADER(Call)
    static MCall* New(TempAllocator& alloc, JSFunction* target, size_t maxArgc, size_t numActualArgs,
                      bool construct, bool ignoresReturnValue, bool isDOMCall);

    void initFunction(MDefinition* func) {
        initOperand(FunctionOperandIndex, func);
    }

    bool needsArgCheck() const {
        return needsArgCheck_;
    }

    void disableArgCheck() {
        needsArgCheck_ = false;
    }
    MDefinition* getFunction() const {
        return getOperand(FunctionOperandIndex);
    }
    void replaceFunction(MInstruction* newfunc) {
        replaceOperand(FunctionOperandIndex, newfunc);
    }

    void addArg(size_t argnum, MDefinition* arg);

    MDefinition* getArg(uint32_t index) const {
        return getOperand(NumNonArgumentOperands + index);
    }

    static size_t IndexOfThis() {
        return NumNonArgumentOperands;
    }
    static size_t IndexOfArgument(size_t index) {
        return NumNonArgumentOperands + index + 1; // +1 to skip |this|.
    }
    static size_t IndexOfStackArg(size_t index) {
        return NumNonArgumentOperands + index;
    }

    // For TI-informed monomorphic callsites.
    WrappedFunction* getSingleTarget() const {
        return target_;
    }

    bool isConstructing() const {
        return construct_;
    }

    bool ignoresReturnValue() const {
        return ignoresReturnValue_;
    }

    // The number of stack arguments is the max between the number of formal
    // arguments and the number of actual arguments. The number of stack
    // argument includes the |undefined| padding added in case of underflow.
    // Includes |this|.
    uint32_t numStackArgs() const {
        return numOperands() - NumNonArgumentOperands;
    }

    // Does not include |this|.
    uint32_t numActualArgs() const {
        return numActualArgs_;
    }

    bool possiblyCalls() const override {
        return true;
    }

    virtual bool isCallDOMNative() const {
        return false;
    }

    // A method that can be called to tell the MCall to figure out whether it's
    // movable or not.  This can't be done in the constructor, because it
    // depends on the arguments to the call, and those aren't passed to the
    // constructor but are set up later via addArg.
    virtual void computeMovable() {
    }

    bool appendRoots(MRootList& roots) const override {
        if (target_)
            return target_->appendRoots(roots);
        return true;
    }
};

class MCallDOMNative : public MCall
{
    // A helper class for MCalls for DOM natives.  Note that this is NOT
    // actually a separate MIR op from MCall, because all sorts of places use
    // isCall() to check for calls and all we really want is to overload a few
    // virtual things from MCall.
  protected:
    MCallDOMNative(WrappedFunction* target, uint32_t numActualArgs)
        : MCall(target, numActualArgs, false, false)
    {
        MOZ_ASSERT(getJitInfo()->type() != JSJitInfo::InlinableNative);

        // If our jitinfo is not marked eliminatable, that means that our C++
        // implementation is fallible or that it never wants to be eliminated or
        // that we have no hope of ever doing the sort of argument analysis that
        // would allow us to detemine that we're side-effect-free.  In the
        // latter case we wouldn't get DCEd no matter what, but for the former
        // two cases we have to explicitly say that we can't be DCEd.
        if (!getJitInfo()->isEliminatable)
            setGuard();
    }

    friend MCall* MCall::New(TempAllocator& alloc, JSFunction* target, size_t maxArgc,
                             size_t numActualArgs, bool construct, bool ignoresReturnValue,
                             bool isDOMCall);

    const JSJitInfo* getJitInfo() const;
  public:
    virtual AliasSet getAliasSet() const override;

    virtual bool congruentTo(const MDefinition* ins) const override;

    virtual bool isCallDOMNative() const override {
        return true;
    }

    virtual void computeMovable() override;
};

// fun.apply(self, arguments)
class MApplyArgs
  : public MTernaryInstruction,
    public Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, BoxPolicy<2> >::Data
{
  protected:
    // Monomorphic cache of single target from TI, or nullptr.
    WrappedFunction* target_;

    MApplyArgs(WrappedFunction* target, MDefinition* fun, MDefinition* argc, MDefinition* self)
      : MTernaryInstruction(classOpcode, fun, argc, self),
        target_(target)
    {
        setResultType(MIRType::Value);
    }

  public:
    INSTRUCTION_HEADER(ApplyArgs)
    TRIVIAL_NEW_WRAPPERS
    NAMED_OPERANDS((0, getFunction), (1, getArgc), (2, getThis))

    // For TI-informed monomorphic callsites.
    WrappedFunction* getSingleTarget() const {
        return target_;
    }

    bool possiblyCalls() const override {
        return true;
    }

    bool appendRoots(MRootList& roots) const override {
        if (target_)
            return target_->appendRoots(roots);
        return true;
    }
};

// fun.apply(fn, array)
class MApplyArray
  : public MTernaryInstruction,
    public Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, BoxPolicy<2> >::Data
{
  protected:
    // Monomorphic cache of single target from TI, or nullptr.
    WrappedFunction* target_;

    MApplyArray(WrappedFunction* target, MDefinition* fun, MDefinition* elements, MDefinition* self)
      : MTernaryInstruction(classOpcode, fun, elements, self),
        target_(target)
    {
        setResultType(MIRType::Value);
    }

  public:
    INSTRUCTION_HEADER(ApplyArray)
    TRIVIAL_NEW_WRAPPERS
    NAMED_OPERANDS((0, getFunction), (1, getElements), (2, getThis))

    // For TI-informed monomorphic callsites.
    WrappedFunction* getSingleTarget() const {
        return target_;
    }

    bool possiblyCalls() const override {
        return true;
    }

    bool appendRoots(MRootList& roots) const override {
        if (target_)
            return target_->appendRoots(roots);
        return true;
    }
};

class MBail : public MNullaryInstruction
{
  protected:
    explicit MBail(BailoutKind kind)
      : MNullaryInstruction(classOpcode)
    {
        bailoutKind_ = kind;
        setGuard();
    }

  private:
    BailoutKind bailoutKind_;

  public:
    INSTRUCTION_HEADER(Bail)

    static MBail*
    New(TempAllocator& alloc, BailoutKind kind) {
        return new(alloc) MBail(kind);
    }
    static MBail*
    New(TempAllocator& alloc) {
        return new(alloc) MBail(Bailout_Inevitable);
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    BailoutKind bailoutKind() const {
        return bailoutKind_;
    }
};

class MUnreachable
  : public MAryControlInstruction<0, 0>,
    public NoTypePolicy::Data
{
    MUnreachable()
      : MAryControlInstruction(classOpcode)
    { }

  public:
    INSTRUCTION_HEADER(Unreachable)
    TRIVIAL_NEW_WRAPPERS

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
};

// This class serve as a way to force the encoding of a snapshot, even if there
// is no resume point using it.  This is useful to run MAssertRecoveredOnBailout
// assertions.
class MEncodeSnapshot : public MNullaryInstruction
{
  protected:
    MEncodeSnapshot()
      : MNullaryInstruction(classOpcode)
    {
        setGuard();
    }

  public:
    INSTRUCTION_HEADER(EncodeSnapshot)

    static MEncodeSnapshot*
    New(TempAllocator& alloc) {
        return new(alloc) MEncodeSnapshot();
    }
};

class MAssertRecoveredOnBailout
  : public MUnaryInstruction,
    public NoTypePolicy::Data
{
  protected:
    bool mustBeRecovered_;

    MAssertRecoveredOnBailout(MDefinition* ins, bool mustBeRecovered)
      : MUnaryInstruction(classOpcode, ins), mustBeRecovered_(mustBeRecovered)
    {
        setResultType(MIRType::Value);
        setRecoveredOnBailout();
        setGuard();
    }

  public:
    INSTRUCTION_HEADER(AssertRecoveredOnBailout)
    TRIVIAL_NEW_WRAPPERS

    // Needed to assert that float32 instructions are correctly recovered.
    bool canConsumeFloat32(MUse* use) const override { return true; }

    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        return true;
    }
};

class MAssertFloat32
  : public MUnaryInstruction,
    public NoTypePolicy::Data
{
  protected:
    bool mustBeFloat32_;

    MAssertFloat32(MDefinition* value, bool mustBeFloat32)
      : MUnaryInstruction(classOpcode, value), mustBeFloat32_(mustBeFloat32)
    {
    }

  public:
    INSTRUCTION_HEADER(AssertFloat32)
    TRIVIAL_NEW_WRAPPERS

    bool canConsumeFloat32(MUse* use) const override { return true; }

    bool mustBeFloat32() const { return mustBeFloat32_; }
};

class MGetDynamicName
  : public MBinaryInstruction,
    public MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<1> >::Data
{
  protected:
    MGetDynamicName(MDefinition* envChain, MDefinition* name)
      : MBinaryInstruction(classOpcode, envChain, name)
    {
        setResultType(MIRType::Value);
    }

  public:
    INSTRUCTION_HEADER(GetDynamicName)
    NAMED_OPERANDS((0, getEnvironmentChain), (1, getName))

    static MGetDynamicName*
    New(TempAllocator& alloc, MDefinition* envChain, MDefinition* name) {
        return new(alloc) MGetDynamicName(envChain, name);
    }

    bool possiblyCalls() const override {
        return true;
    }
};

class MCallDirectEval
  : public MTernaryInstruction,
    public Mix3Policy<ObjectPolicy<0>,
                      StringPolicy<1>,
                      BoxPolicy<2> >::Data
{
  protected:
    MCallDirectEval(MDefinition* envChain, MDefinition* string,
                    MDefinition* newTargetValue, jsbytecode* pc)
      : MTernaryInstruction(classOpcode, envChain, string, newTargetValue),
        pc_(pc)
    {
        setResultType(MIRType::Value);
    }

  public:
    INSTRUCTION_HEADER(CallDirectEval)
    NAMED_OPERANDS((0, getEnvironmentChain), (1, getString), (2, getNewTargetValue))

    static MCallDirectEval*
    New(TempAllocator& alloc, MDefinition* envChain, MDefinition* string,
        MDefinition* newTargetValue, jsbytecode* pc)
    {
        return new(alloc) MCallDirectEval(envChain, string, newTargetValue, pc);
    }

    jsbytecode* pc() const {
        return pc_;
    }

    bool possiblyCalls() const override {
        return true;
    }

  private:
    jsbytecode* pc_;
};

class MCompare
  : public MBinaryInstruction,
    public ComparePolicy::Data
{
  public:
    enum CompareType {

        // Anything compared to Undefined
        Compare_Undefined,

        // Anything compared to Null
        Compare_Null,

        // Undefined compared to Boolean
        // Null      compared to Boolean
        // Double    compared to Boolean
        // String    compared to Boolean
        // Symbol    compared to Boolean
        // Object    compared to Boolean
        // Value     compared to Boolean
        Compare_Boolean,

        // Int32   compared to Int32
        // Boolean compared to Boolean
        Compare_Int32,
        Compare_Int32MaybeCoerceBoth,
        Compare_Int32MaybeCoerceLHS,
        Compare_Int32MaybeCoerceRHS,

        // Int32 compared as unsigneds
        Compare_UInt32,

        // Int64 compared to Int64.
        Compare_Int64,

        // Int64 compared as unsigneds.
        Compare_UInt64,

        // Double compared to Double
        Compare_Double,

        Compare_DoubleMaybeCoerceLHS,
        Compare_DoubleMaybeCoerceRHS,

        // Float compared to Float
        Compare_Float32,

        // String compared to String
        Compare_String,

        // Symbol compared to Symbol
        Compare_Symbol,

        // Undefined compared to String
        // Null      compared to String
        // Boolean   compared to String
        // Int32     compared to String
        // Double    compared to String
        // Object    compared to String
        // Value     compared to String
        Compare_StrictString,

        // Object compared to Object
        Compare_Object,

        // Compare 2 values bitwise
        Compare_Bitwise,

        // All other possible compares
        Compare_Unknown
    };

  private:
    CompareType compareType_;
    JSOp jsop_;
    bool operandMightEmulateUndefined_;
    bool operandsAreNeverNaN_;

    // When a floating-point comparison is converted to an integer comparison
    // (when range analysis proves it safe), we need to convert the operands
    // to integer as well.
    bool truncateOperands_;

    MCompare(MDefinition* left, MDefinition* right, JSOp jsop)
      : MBinaryInstruction(classOpcode, left, right),
        compareType_(Compare_Unknown),
        jsop_(jsop),
        operandMightEmulateUndefined_(true),
        operandsAreNeverNaN_(false),
        truncateOperands_(false)
    {
        setResultType(MIRType::Boolean);
        setMovable();
    }

    MCompare(MDefinition* left, MDefinition* right, JSOp jsop, CompareType compareType)
      : MCompare(left, right, jsop)
    {
        MOZ_ASSERT(compareType == Compare_Int32 || compareType == Compare_UInt32 ||
                   compareType == Compare_Int64 || compareType == Compare_UInt64 ||
                   compareType == Compare_Double || compareType == Compare_Float32);
        compareType_ = compareType;
        operandMightEmulateUndefined_ = false;
        setResultType(MIRType::Int32);
    }

  public:
    INSTRUCTION_HEADER(Compare)
    TRIVIAL_NEW_WRAPPERS

    MOZ_MUST_USE bool tryFold(bool* result);
    MOZ_MUST_USE bool evaluateConstantOperands(TempAllocator& alloc, bool* result);
    MDefinition* foldsTo(TempAllocator& alloc) override;
    void filtersUndefinedOrNull(bool trueBranch, MDefinition** subject, bool* filtersUndefined,
                                bool* filtersNull);

    CompareType compareType() const {
        return compareType_;
    }
    bool isInt32Comparison() const {
        return compareType() == Compare_Int32 ||
               compareType() == Compare_Int32MaybeCoerceBoth ||
               compareType() == Compare_Int32MaybeCoerceLHS ||
               compareType() == Compare_Int32MaybeCoerceRHS;
    }
    bool isDoubleComparison() const {
        return compareType() == Compare_Double ||
               compareType() == Compare_DoubleMaybeCoerceLHS ||
               compareType() == Compare_DoubleMaybeCoerceRHS;
    }
    bool isFloat32Comparison() const {
        return compareType() == Compare_Float32;
    }
    bool isNumericComparison() const {
        return isInt32Comparison() ||
               isDoubleComparison() ||
               isFloat32Comparison();
    }
    void setCompareType(CompareType type) {
        compareType_ = type;
    }
    MIRType inputType();

    JSOp jsop() const {
        return jsop_;
    }
    void markNoOperandEmulatesUndefined() {
        operandMightEmulateUndefined_ = false;
    }
    bool operandMightEmulateUndefined() const {
        return operandMightEmulateUndefined_;
    }
    bool operandsAreNeverNaN() const {
        return operandsAreNeverNaN_;
    }
    AliasSet getAliasSet() const override {
        // Strict equality is never effectful.
        if (jsop_ == JSOP_STRICTEQ || jsop_ == JSOP_STRICTNE)
            return AliasSet::None();
        if (compareType_ == Compare_Unknown)
            return AliasSet::Store(AliasSet::Any);
        MOZ_ASSERT(compareType_ <= Compare_Bitwise);
        return AliasSet::None();
    }

    void printOpcode(GenericPrinter& out) const override;
    void collectRangeInfoPreTrunc() override;

    void trySpecializeFloat32(TempAllocator& alloc) override;
    bool isFloat32Commutative() const override { return true; }
    bool needTruncation(TruncateKind kind) override;
    void truncate() override;
    TruncateKind operandTruncateKind(size_t index) const override;

    static CompareType determineCompareType(JSOp op, MDefinition* left, MDefinition* right);
    void cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints);

# ifdef DEBUG
    bool isConsistentFloat32Use(MUse* use) const override {
        // Both sides of the compare can be Float32
        return compareType_ == Compare_Float32;
    }
# endif

    ALLOW_CLONE(MCompare)

  protected:
    MOZ_MUST_USE bool tryFoldEqualOperands(bool* result);
    MOZ_MUST_USE bool tryFoldTypeOf(bool* result);

    bool congruentTo(const MDefinition* ins) const override {
        if (!binaryCongruentTo(ins))
            return false;
        return compareType() == ins->toCompare()->compareType() &&
               jsop() == ins->toCompare()->jsop();
    }
};

// Takes a typed value and returns an untyped value.
class MBox
  : public MUnaryInstruction,
    public NoTypePolicy::Data
{
    MBox(TempAllocator& alloc, MDefinition* ins)
      : MUnaryInstruction(classOpcode, ins)
    {
        setResultType(MIRType::Value);
        if (ins->resultTypeSet()) {
            setResultTypeSet(ins->resultTypeSet());
        } else if (ins->type() != MIRType::Value) {
            TypeSet::Type ntype = ins->type() == MIRType::Object
                                  ? TypeSet::AnyObjectType()
                                  : TypeSet::PrimitiveType(ValueTypeFromMIRType(ins->type()));
            setResultTypeSet(alloc.lifoAlloc()->new_<TemporaryTypeSet>(alloc.lifoAlloc(), ntype));
        }
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(Box)
    static MBox* New(TempAllocator& alloc, MDefinition* ins)
    {
        // Cannot box a box.
        MOZ_ASSERT(ins->type() != MIRType::Value);

        return new(alloc) MBox(alloc, ins);
    }

    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins);
    }
    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    ALLOW_CLONE(MBox)
};

// Note: the op may have been inverted during lowering (to put constants in a
// position where they can be immediates), so it is important to use the
// lir->jsop() instead of the mir->jsop() when it is present.
static inline Assembler::Condition
JSOpToCondition(MCompare::CompareType compareType, JSOp op)
{
    bool isSigned = (compareType != MCompare::Compare_UInt32);
    return JSOpToCondition(op, isSigned);
}

// Takes a typed value and checks if it is a certain type. If so, the payload
// is unpacked and returned as that type. Otherwise, it is considered a
// deoptimization.
class MUnbox final : public MUnaryInstruction, public BoxInputsPolicy::Data
{
  public:
    enum Mode {
        Fallible,       // Check the type, and deoptimize if unexpected.
        Infallible,     // Type guard is not necessary.
        TypeBarrier     // Guard on the type, and act like a TypeBarrier on failure.
    };

  private:
    Mode mode_;
    BailoutKind bailoutKind_;

    MUnbox(MDefinition* ins, MIRType type, Mode mode, BailoutKind kind, TempAllocator& alloc)
      : MUnaryInstruction(classOpcode, ins),
        mode_(mode)
    {
        // Only allow unboxing a non MIRType::Value when input and output types
        // don't match. This is often used to force a bailout. Boxing happens
        // during type analysis.
        MOZ_ASSERT_IF(ins->type() != MIRType::Value, type != ins->type());

        MOZ_ASSERT(type == MIRType::Boolean ||
                   type == MIRType::Int32   ||
                   type == MIRType::Double  ||
                   type == MIRType::String  ||
                   type == MIRType::Symbol  ||
                   type == MIRType::Object);

        TemporaryTypeSet* resultSet = ins->resultTypeSet();
        if (resultSet && type == MIRType::Object)
            resultSet = resultSet->cloneObjectsOnly(alloc.lifoAlloc());

        setResultType(type);
        setResultTypeSet(resultSet);
        setMovable();

        if (mode_ == TypeBarrier || mode_ == Fallible)
            setGuard();

        bailoutKind_ = kind;
    }
  public:
    INSTRUCTION_HEADER(Unbox)
    static MUnbox* New(TempAllocator& alloc, MDefinition* ins, MIRType type, Mode mode)
    {
        // Unless we were given a specific BailoutKind, pick a default based on
        // the type we expect.
        BailoutKind kind;
        switch (type) {
          case MIRType::Boolean:
            kind = Bailout_NonBooleanInput;
            break;
          case MIRType::Int32:
            kind = Bailout_NonInt32Input;
            break;
          case MIRType::Double:
            kind = Bailout_NonNumericInput; // Int32s are fine too
            break;
          case MIRType::String:
            kind = Bailout_NonStringInput;
            break;
          case MIRType::Symbol:
            kind = Bailout_NonSymbolInput;
            break;
          case MIRType::Object:
            kind = Bailout_NonObjectInput;
            break;
          default:
            MOZ_CRASH("Given MIRType cannot be unboxed.");
        }

        return new(alloc) MUnbox(ins, type, mode, kind, alloc);
    }

    static MUnbox* New(TempAllocator& alloc, MDefinition* ins, MIRType type, Mode mode,
                       BailoutKind kind)
    {
        return new(alloc) MUnbox(ins, type, mode, kind, alloc);
    }

    Mode mode() const {
        return mode_;
    }
    BailoutKind bailoutKind() const {
        // If infallible, no bailout should be generated.
        MOZ_ASSERT(fallible());
        return bailoutKind_;
    }
    bool fallible() const {
        return mode() != Infallible;
    }
    bool congruentTo(const MDefinition* ins) const override {
        if (!ins->isUnbox() || ins->toUnbox()->mode() != mode())
            return false;
        return congruentIfOperandsEqual(ins);
    }

    MDefinition* foldsTo(TempAllocator& alloc) override;

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
    void printOpcode(GenericPrinter& out) const override;
    void makeInfallible() {
        // Should only be called if we're already Infallible or TypeBarrier
        MOZ_ASSERT(mode() != Fallible);
        mode_ = Infallible;
    }

    ALLOW_CLONE(MUnbox)
};

class MGuardObject
  : public MUnaryInstruction,
    public SingleObjectPolicy::Data
{
    explicit MGuardObject(MDefinition* ins)
      : MUnaryInstruction(classOpcode, ins)
    {
        setGuard();
        setMovable();
        setResultType(MIRType::Object);
        setResultTypeSet(ins->resultTypeSet());
    }

  public:
    INSTRUCTION_HEADER(GuardObject)
    TRIVIAL_NEW_WRAPPERS

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
};

class MGuardString
  : public MUnaryInstruction,
    public StringPolicy<0>::Data
{
    explicit MGuardString(MDefinition* ins)
      : MUnaryInstruction(classOpcode, ins)
    {
        setGuard();
        setMovable();
        setResultType(MIRType::String);
    }

  public:
    INSTRUCTION_HEADER(GuardString)
    TRIVIAL_NEW_WRAPPERS

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
};

class MPolyInlineGuard
  : public MUnaryInstruction,
    public SingleObjectPolicy::Data
{
    explicit MPolyInlineGuard(MDefinition* ins)
      : MUnaryInstruction(classOpcode, ins)
    {
        setGuard();
        setResultType(MIRType::Object);
        setResultTypeSet(ins->resultTypeSet());
    }

  public:
    INSTRUCTION_HEADER(PolyInlineGuard)
    TRIVIAL_NEW_WRAPPERS

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
};

class MAssertRange
  : public MUnaryInstruction,
    public NoTypePolicy::Data
{
    // This is the range checked by the assertion. Don't confuse this with the
    // range_ member or the range() accessor. Since MAssertRange doesn't return
    // a value, it doesn't use those.
    const Range* assertedRange_;

    MAssertRange(MDefinition* ins, const Range* assertedRange)
      : MUnaryInstruction(classOpcode, ins), assertedRange_(assertedRange)
    {
        setGuard();
        setResultType(MIRType::None);
    }

  public:
    INSTRUCTION_HEADER(AssertRange)
    TRIVIAL_NEW_WRAPPERS

    const Range* assertedRange() const {
        return assertedRange_;
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    void printOpcode(GenericPrinter& out) const override;
};

// Caller-side allocation of |this| for |new|:
// Given a templateobject, construct |this| for JSOP_NEW
class MCreateThisWithTemplate
  : public MUnaryInstruction,
    public NoTypePolicy::Data
{
    gc::InitialHeap initialHeap_;

    MCreateThisWithTemplate(TempAllocator& alloc, CompilerConstraintList* constraints,
                            MConstant* templateConst, gc::InitialHeap initialHeap)
      : MUnaryInstruction(classOpcode, templateConst),
        initialHeap_(initialHeap)
    {
        setResultType(MIRType::Object);
        setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject()));
    }

  public:
    INSTRUCTION_HEADER(CreateThisWithTemplate)
    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC

    // Template for |this|, provided by TI.
    JSObject* templateObject() const {
        return &getOperand(0)->toConstant()->toObject();
    }

    gc::InitialHeap initialHeap() const {
        return initialHeap_;
    }

    // Although creation of |this| modifies global state, it is safely repeatable.
    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override;
};

// Caller-side allocation of |this| for |new|:
// Given a prototype operand, construct |this| for JSOP_NEW.
class MCreateThisWithProto
  : public MTernaryInstruction,
    public Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, ObjectPolicy<2> >::Data
{
    MCreateThisWithProto(MDefinition* callee, MDefinition* newTarget, MDefinition* prototype)
      : MTernaryInstruction(classOpcode, callee, newTarget, prototype)
    {
        setResultType(MIRType::Object);
    }

  public:
    INSTRUCTION_HEADER(CreateThisWithProto)
    TRIVIAL_NEW_WRAPPERS
    NAMED_OPERANDS((0, getCallee), (1, getNewTarget), (2, getPrototype))

    // Although creation of |this| modifies global state, it is safely repeatable.
    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
    bool possiblyCalls() const override {
        return true;
    }
};

// Caller-side allocation of |this| for |new|:
// Constructs |this| when possible, else MagicValue(JS_IS_CONSTRUCTING).
class MCreateThis
  : public MBinaryInstruction,
    public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
{
    explicit MCreateThis(MDefinition* callee, MDefinition* newTarget)
      : MBinaryInstruction(classOpcode, callee, newTarget)
    {
        setResultType(MIRType::Value);
    }

  public:
    INSTRUCTION_HEADER(CreateThis)
    TRIVIAL_NEW_WRAPPERS
    NAMED_OPERANDS((0, getCallee), (1, getNewTarget))

    // Although creation of |this| modifies global state, it is safely repeatable.
    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
    bool possiblyCalls() const override {
        return true;
    }
};

// Eager initialization of arguments object.
class MCreateArgumentsObject
  : public MUnaryInstruction,
    public ObjectPolicy<0>::Data
{
    CompilerGCPointer<ArgumentsObject*> templateObj_;

    MCreateArgumentsObject(MDefinition* callObj, ArgumentsObject* templateObj)
      : MUnaryInstruction(classOpcode, callObj),
        templateObj_(templateObj)
    {
        setResultType(MIRType::Object);
        setGuard();
    }

  public:
    INSTRUCTION_HEADER(CreateArgumentsObject)
    TRIVIAL_NEW_WRAPPERS
    NAMED_OPERANDS((0, getCallObject))

    ArgumentsObject* templateObject() const {
        return templateObj_;
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    bool possiblyCalls() const override {
        return true;
    }

    bool appendRoots(MRootList& roots) const override {
        return roots.append(templateObj_);
    }
};

class MGetArgumentsObjectArg
  : public MUnaryInstruction,
    public ObjectPolicy<0>::Data
{
    size_t argno_;

    MGetArgumentsObjectArg(MDefinition* argsObject, size_t argno)
      : MUnaryInstruction(classOpcode, argsObject),
        argno_(argno)
    {
        setResultType(MIRType::Value);
    }

  public:
    INSTRUCTION_HEADER(GetArgumentsObjectArg)
    TRIVIAL_NEW_WRAPPERS
    NAMED_OPERANDS((0, getArgsObject))

    size_t argno() const {
        return argno_;
    }

    AliasSet getAliasSet() const override {
        return AliasSet::Load(AliasSet::Any);
    }
};

class MSetArgumentsObjectArg
  : public MBinaryInstruction,
    public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
{
    size_t argno_;

    MSetArgumentsObjectArg(MDefinition* argsObj, size_t argno, MDefinition* value)
      : MBinaryInstruction(classOpcode, argsObj, value),
        argno_(argno)
    {
    }

  public:
    INSTRUCTION_HEADER(SetArgumentsObjectArg)
    TRIVIAL_NEW_WRAPPERS
    NAMED_OPERANDS((0, getArgsObject), (1, getValue))

    size_t argno() const {
        return argno_;
    }

    AliasSet getAliasSet() const override {
        return AliasSet::Store(AliasSet::Any);
    }
};

class MRunOncePrologue
  : public MNullaryInstruction
{
  protected:
    MRunOncePrologue()
      : MNullaryInstruction(classOpcode)
    {
        setGuard();
    }

  public:
    INSTRUCTION_HEADER(RunOncePrologue)
    TRIVIAL_NEW_WRAPPERS

    bool possiblyCalls() const override {
        return true;
    }
};

// Given a MIRType::Value A and a MIRType::Object B:
// If the Value may be safely unboxed to an Object, return Object(A).
// Otherwise, return B.
// Used to implement return behavior for inlined constructors.
class MReturnFromCtor
  : public MBinaryInstruction,
    public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >::Data
{
    MReturnFromCtor(MDefinition* value, MDefinition* object)
      : MBinaryInstruction(classOpcode, value, object)
    {
        setResultType(MIRType::Object);
    }

  public:
    INSTRUCTION_HEADER(ReturnFromCtor)
    TRIVIAL_NEW_WRAPPERS
    NAMED_OPERANDS((0, getValue), (1, getObject))

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
};

class MToFPInstruction
  : public MUnaryInstruction,
    public ToDoublePolicy::Data
{
  public:
    // Types of values which can be converted.
    enum ConversionKind {
        NonStringPrimitives,
        NonNullNonStringPrimitives,
        NumbersOnly
    };

  private:
    ConversionKind conversion_;

  protected:
    MToFPInstruction(Opcode op, MDefinition* def, ConversionKind conversion = NonStringPrimitives)
      : MUnaryInstruction(op, def), conversion_(conversion)
    { }

  public:
    ConversionKind conversion() const {
        return conversion_;
    }
};

// Converts a primitive (either typed or untyped) to a double. If the input is
// not primitive at runtime, a bailout occurs.
class MToDouble
  : public MToFPInstruction
{
  private:
    TruncateKind implicitTruncate_;

    explicit MToDouble(MDefinition* def, ConversionKind conversion = NonStringPrimitives)
      : MToFPInstruction(classOpcode, def, conversion), implicitTruncate_(NoTruncate)
    {
        setResultType(MIRType::Double);
        setMovable();

        // An object might have "valueOf", which means it is effectful.
        // ToNumber(symbol) throws.
        if (def->mightBeType(MIRType::Object) || def->mightBeType(MIRType::Symbol))
            setGuard();
    }

  public:
    INSTRUCTION_HEADER(ToDouble)
    TRIVIAL_NEW_WRAPPERS

    MDefinition* foldsTo(TempAllocator& alloc) override;
    bool congruentTo(const MDefinition* ins) const override {
        if (!ins->isToDouble() || ins->toToDouble()->conversion() != conversion())
            return false;
        return congruentIfOperandsEqual(ins);
    }
    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    void computeRange(TempAllocator& alloc) override;
    bool needTruncation(TruncateKind kind) override;
    void truncate() override;
    TruncateKind operandTruncateKind(size_t index) const override;

#ifdef DEBUG
    bool isConsistentFloat32Use(MUse* use) const override { return true; }
#endif

    TruncateKind truncateKind() const {
        return implicitTruncate_;
    }
    void setTruncateKind(TruncateKind kind) {
        implicitTruncate_ = Max(implicitTruncate_, kind);
    }

    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        if (input()->type() == MIRType::Value)
            return false;
        if (input()->type() == MIRType::Symbol)
            return false;

        return true;
    }

    ALLOW_CLONE(MToDouble)
};

// Converts a primitive (either typed or untyped) to a float32. If the input is
// not primitive at runtime, a bailout occurs.
class MToFloat32
  : public MToFPInstruction
{
  protected:
    bool mustPreserveNaN_;

    explicit MToFloat32(MDefinition* def, ConversionKind conversion = NonStringPrimitives)
      : MToFPInstruction(classOpcode, def, conversion),
        mustPreserveNaN_(false)
    {
        setResultType(MIRType::Float32);
        setMovable();

        // An object might have "valueOf", which means it is effectful.
        // ToNumber(symbol) throws.
        if (def->mightBeType(MIRType::Object) || def->mightBeType(MIRType::Symbol))
            setGuard();
    }

    explicit MToFloat32(MDefinition* def, bool mustPreserveNaN)
      : MToFloat32(def)
    {
        mustPreserveNaN_ = mustPreserveNaN;
    }

  public:
    INSTRUCTION_HEADER(ToFloat32)
    TRIVIAL_NEW_WRAPPERS

    virtual MDefinition* foldsTo(TempAllocator& alloc) override;
    bool congruentTo(const MDefinition* ins) const override {
        if (!congruentIfOperandsEqual(ins))
            return false;
        auto* other = ins->toToFloat32();
        return other->conversion() == conversion() &&
               other->mustPreserveNaN_ == mustPreserveNaN_;
    }
    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    void computeRange(TempAllocator& alloc) override;

    bool canConsumeFloat32(MUse* use) const override { return true; }
    bool canProduceFloat32() const override { return true; }

    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        return true;
    }

    ALLOW_CLONE(MToFloat32)
};

// Converts a uint32 to a double (coming from wasm).
class MWasmUnsignedToDouble
  : public MUnaryInstruction,
    public NoTypePolicy::Data
{
    explicit MWasmUnsignedToDouble(MDefinition* def)
      : MUnaryInstruction(classOpcode, def)
    {
        setResultType(MIRType::Double);
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(WasmUnsignedToDouble)
    TRIVIAL_NEW_WRAPPERS

    MDefinition* foldsTo(TempAllocator& alloc) override;
    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins);
    }
    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
};

// Converts a uint32 to a float32 (coming from wasm).
class MWasmUnsignedToFloat32
  : public MUnaryInstruction,
    public NoTypePolicy::Data
{
    explicit MWasmUnsignedToFloat32(MDefinition* def)
      : MUnaryInstruction(classOpcode, def)
    {
        setResultType(MIRType::Float32);
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(WasmUnsignedToFloat32)
    TRIVIAL_NEW_WRAPPERS

    MDefinition* foldsTo(TempAllocator& alloc) override;
    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins);
    }
    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    bool canProduceFloat32() const override { return true; }
};

class MWrapInt64ToInt32
  : public MUnaryInstruction,
    public NoTypePolicy::Data
{
    bool bottomHalf_;

    explicit MWrapInt64ToInt32(MDefinition* def, bool bottomHalf = true)
      : MUnaryInstruction(classOpcode, def),
        bottomHalf_(bottomHalf)
    {
        setResultType(MIRType::Int32);
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(WrapInt64ToInt32)
    TRIVIAL_NEW_WRAPPERS

    MDefinition* foldsTo(TempAllocator& alloc) override;
    bool congruentTo(const MDefinition* ins) const override {
        if (!ins->isWrapInt64ToInt32())
            return false;
        if (ins->toWrapInt64ToInt32()->bottomHalf() != bottomHalf())
            return false;
        return congruentIfOperandsEqual(ins);
    }
    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    bool bottomHalf() const {
        return bottomHalf_;
    }
};

class MExtendInt32ToInt64
  : public MUnaryInstruction,
    public NoTypePolicy::Data
{
    bool isUnsigned_;

    MExtendInt32ToInt64(MDefinition* def, bool isUnsigned)
      : MUnaryInstruction(classOpcode, def),
        isUnsigned_(isUnsigned)
    {
        setResultType(MIRType::Int64);
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(ExtendInt32ToInt64)
    TRIVIAL_NEW_WRAPPERS

    bool isUnsigned() const { return isUnsigned_; }

    MDefinition* foldsTo(TempAllocator& alloc) override;
    bool congruentTo(const MDefinition* ins) const override {
        if (!ins->isExtendInt32ToInt64())
            return false;
        if (ins->toExtendInt32ToInt64()->isUnsigned_ != isUnsigned_)
            return false;
        return congruentIfOperandsEqual(ins);
    }
    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
};

class MWasmTruncateToInt64
  : public MUnaryInstruction,
    public NoTypePolicy::Data
{
    bool isUnsigned_;
    wasm::BytecodeOffset bytecodeOffset_;

    MWasmTruncateToInt64(MDefinition* def, bool isUnsigned, wasm::BytecodeOffset bytecodeOffset)
      : MUnaryInstruction(classOpcode, def),
        isUnsigned_(isUnsigned),
        bytecodeOffset_(bytecodeOffset)
    {
        setResultType(MIRType::Int64);
        setGuard(); // neither removable nor movable because of possible side-effects.
    }

  public:
    INSTRUCTION_HEADER(WasmTruncateToInt64)
    TRIVIAL_NEW_WRAPPERS

    bool isUnsigned() const { return isUnsigned_; }
    wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }

    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins) &&
               ins->toWasmTruncateToInt64()->isUnsigned() == isUnsigned_;
    }
    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
};

// Truncate a value to an int32, with wasm semantics: this will trap when the
// value is out of range.
class MWasmTruncateToInt32
  : public MUnaryInstruction,
    public NoTypePolicy::Data
{
    bool isUnsigned_;
    wasm::BytecodeOffset bytecodeOffset_;

    explicit MWasmTruncateToInt32(MDefinition* def, bool isUnsigned,
                                  wasm::BytecodeOffset bytecodeOffset)
      : MUnaryInstruction(classOpcode, def),
        isUnsigned_(isUnsigned), bytecodeOffset_(bytecodeOffset)
    {
        setResultType(MIRType::Int32);
        setGuard(); // neither removable nor movable because of possible side-effects.
    }

  public:
    INSTRUCTION_HEADER(WasmTruncateToInt32)
    TRIVIAL_NEW_WRAPPERS

    bool isUnsigned() const {
        return isUnsigned_;
    }
    wasm::BytecodeOffset bytecodeOffset() const {
        return bytecodeOffset_;
    }

    MDefinition* foldsTo(TempAllocator& alloc) override;

    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins) &&
               ins->toWasmTruncateToInt32()->isUnsigned() == isUnsigned_;
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
};

class MInt64ToFloatingPoint
  : public MUnaryInstruction,
    public NoTypePolicy::Data
{
    bool isUnsigned_;
    wasm::BytecodeOffset bytecodeOffset_;

    MInt64ToFloatingPoint(MDefinition* def, MIRType type, wasm::BytecodeOffset bytecodeOffset,
                          bool isUnsigned)
      : MUnaryInstruction(classOpcode, def),
        isUnsigned_(isUnsigned),
        bytecodeOffset_(bytecodeOffset)
    {
        MOZ_ASSERT(IsFloatingPointType(type));
        setResultType(type);
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(Int64ToFloatingPoint)
    TRIVIAL_NEW_WRAPPERS

    bool isUnsigned() const { return isUnsigned_; }
    wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }

    bool congruentTo(const MDefinition* ins) const override {
        if (!ins->isInt64ToFloatingPoint())
            return false;
        if (ins->toInt64ToFloatingPoint()->isUnsigned_ != isUnsigned_)
            return false;
        return congruentIfOperandsEqual(ins);
    }
    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
};

// Converts a primitive (either typed or untyped) to an int32. If the input is
// not primitive at runtime, a bailout occurs. If the input cannot be converted
// to an int32 without loss (i.e. "5.5" or undefined) then a bailout occurs.
class MToInt32
  : public MUnaryInstruction,
    public ToInt32Policy::Data
{
    bool canBeNegativeZero_;
    MacroAssembler::IntConversionInputKind conversion_;

    explicit MToInt32(MDefinition* def, MacroAssembler::IntConversionInputKind conversion =
                                            MacroAssembler::IntConversion_Any)
      : MUnaryInstruction(classOpcode, def),
        canBeNegativeZero_(true),
        conversion_(conversion)
    {
        setResultType(MIRType::Int32);
        setMovable();

        // An object might have "valueOf", which means it is effectful.
        // ToNumber(symbol) throws.
        if (def->mightBeType(MIRType::Object) || def->mightBeType(MIRType::Symbol))
            setGuard();
    }

  public:
    INSTRUCTION_HEADER(ToInt32)
    TRIVIAL_NEW_WRAPPERS

    MDefinition* foldsTo(TempAllocator& alloc) override;

    // this only has backwards information flow.
    void analyzeEdgeCasesBackward() override;

    bool canBeNegativeZero() const {
        return canBeNegativeZero_;
    }
    void setCanBeNegativeZero(bool negativeZero) {
        canBeNegativeZero_ = negativeZero;
    }

    MacroAssembler::IntConversionInputKind conversion() const {
        return conversion_;
    }

    bool congruentTo(const MDefinition* ins) const override {
        if (!ins->isToInt32() || ins->toToInt32()->conversion() != conversion())
            return false;
        return congruentIfOperandsEqual(ins);
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }
    void computeRange(TempAllocator& alloc) override;
    void collectRangeInfoPreTrunc() override;

#ifdef DEBUG
    bool isConsistentFloat32Use(MUse* use) const override { return true; }
#endif

    ALLOW_CLONE(MToInt32)
};

// Converts a value or typed input to a truncated int32, for use with bitwise
// operations. This is an infallible ValueToECMAInt32.
class MTruncateToInt32
  : public MUnaryInstruction,
    public ToInt32Policy::Data
{
    wasm::BytecodeOffset bytecodeOffset_;

    explicit MTruncateToInt32(MDefinition* def,
                              wasm::BytecodeOffset bytecodeOffset = wasm::BytecodeOffset())
      : MUnaryInstruction(classOpcode, def),
        bytecodeOffset_(bytecodeOffset)
    {
        setResultType(MIRType::Int32);
        setMovable();

        // An object might have "valueOf", which means it is effectful.
        // ToInt32(symbol) throws.
        if (def->mightBeType(MIRType::Object) || def->mightBeType(MIRType::Symbol))
            setGuard();
    }

  public:
    INSTRUCTION_HEADER(TruncateToInt32)
    TRIVIAL_NEW_WRAPPERS

    MDefinition* foldsTo(TempAllocator& alloc) override;

    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins);
    }
    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    void computeRange(TempAllocator& alloc) override;
    TruncateKind operandTruncateKind(size_t index) const override;
# ifdef DEBUG
    bool isConsistentFloat32Use(MUse* use) const override {
        return true;
    }
#endif

    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        return input()->type() < MIRType::Symbol;
    }

    wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }

    ALLOW_CLONE(MTruncateToInt32)
};

// Converts any type to a string
class MToString :
  public MUnaryInstruction,
  public ToStringPolicy::Data
{
    explicit MToString(MDefinition* def)
      : MUnaryInstruction(classOpcode, def)
    {
        setResultType(MIRType::String);
        setMovable();

        // Objects might override toString and Symbols throw. We bailout in
        // those cases and run side-effects in baseline instead.
        if (def->mightBeType(MIRType::Object) || def->mightBeType(MIRType::Symbol))
            setGuard();
    }

  public:
    INSTRUCTION_HEADER(ToString)
    TRIVIAL_NEW_WRAPPERS

    MDefinition* foldsTo(TempAllocator& alloc) override;

    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins);
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    bool fallible() const {
        return input()->mightBeType(MIRType::Object) ||
               input()->mightBeType(MIRType::Symbol);
    }

    ALLOW_CLONE(MToString)
};

// Converts any type to an object, throwing on null or undefined.
class MToObject :
  public MUnaryInstruction,
  public BoxInputsPolicy::Data
{
    explicit MToObject(MDefinition* def)
      : MUnaryInstruction(classOpcode, def)
    {
        setResultType(MIRType::Object);
        setGuard(); // Throws on null or undefined.
    }

  public:
    INSTRUCTION_HEADER(ToObject)
    TRIVIAL_NEW_WRAPPERS

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    ALLOW_CLONE(MToObject)
};

// Converts any type to an object or null value, throwing on undefined.
class MToObjectOrNull :
  public MUnaryInstruction,
  public BoxInputsPolicy::Data
{
    explicit MToObjectOrNull(MDefinition* def)
      : MUnaryInstruction(classOpcode, def)
    {
        setResultType(MIRType::ObjectOrNull);
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(ToObjectOrNull)
    TRIVIAL_NEW_WRAPPERS

    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins);
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    ALLOW_CLONE(MToObjectOrNull)
};

class MBitNot
  : public MUnaryInstruction,
    public BitwisePolicy::Data
{
  protected:
    explicit MBitNot(MDefinition* input)
      : MUnaryInstruction(classOpcode, input)
    {
        specialization_ = MIRType::None;
        setResultType(MIRType::Int32);
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(BitNot)
    TRIVIAL_NEW_WRAPPERS

    static MBitNot* NewInt32(TempAllocator& alloc, MDefinition* input);

    MDefinition* foldsTo(TempAllocator& alloc) override;
    void setSpecialization(MIRType type) {
        specialization_ = type;
        setResultType(type);
    }

    bool congruentTo(const MDefinition* ins) const override {
        return congruentIfOperandsEqual(ins);
    }
    AliasSet getAliasSet() const override {
        if (specialization_ == MIRType::None)
            return AliasSet::Store(AliasSet::Any);
        return AliasSet::None();
    }
    void computeRange(TempAllocator& alloc) override;

    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        return specialization_ != MIRType::None;
    }

    ALLOW_CLONE(MBitNot)
};

class MTypeOf
  : public MUnaryInstruction,
    public BoxInputsPolicy::Data
{
    MIRType inputType_;
    bool inputMaybeCallableOrEmulatesUndefined_;

    MTypeOf(MDefinition* def, MIRType inputType)
      : MUnaryInstruction(classOpcode, def), inputType_(inputType),
        inputMaybeCallableOrEmulatesUndefined_(true)
    {
        setResultType(MIRType::String);
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(TypeOf)
    TRIVIAL_NEW_WRAPPERS

    MIRType inputType() const {
        return inputType_;
    }

    MDefinition* foldsTo(TempAllocator& alloc) override;
    void cacheInputMaybeCallableOrEmulatesUndefined(CompilerConstraintList* constraints);

    bool inputMaybeCallableOrEmulatesUndefined() const {
        return inputMaybeCallableOrEmulatesUndefined_;
    }
    void markInputNotCallableOrEmulatesUndefined() {
        inputMaybeCallableOrEmulatesUndefined_ = false;
    }

    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    bool congruentTo(const MDefinition* ins) const override {
        if (!ins->isTypeOf())
            return false;
        if (inputType() != ins->toTypeOf()->inputType())
            return false;
        if (inputMaybeCallableOrEmulatesUndefined() !=
            ins->toTypeOf()->inputMaybeCallableOrEmulatesUndefined())
        {
            return false;
        }
        return congruentIfOperandsEqual(ins);
    }

    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        return true;
    }
};

class MToAsync
  : public MUnaryInstruction,
    public SingleObjectPolicy::Data
{
    explicit MToAsync(MDefinition* unwrapped)
      : MUnaryInstruction(classOpcode, unwrapped)
    {
        setResultType(MIRType::Object);
    }

  public:
    INSTRUCTION_HEADER(ToAsync)
    TRIVIAL_NEW_WRAPPERS
};

class MToAsyncGen
  : public MUnaryInstruction,
    public SingleObjectPolicy::Data
{
    explicit MToAsyncGen(MDefinition* unwrapped)
      : MUnaryInstruction(classOpcode, unwrapped)
    {
        setResultType(MIRType::Object);
    }

  public:
    INSTRUCTION_HEADER(ToAsyncGen)
    TRIVIAL_NEW_WRAPPERS
};

class MToAsyncIter
  : public MUnaryInstruction,
    public SingleObjectPolicy::Data
{
    explicit MToAsyncIter(MDefinition* unwrapped)
      : MUnaryInstruction(classOpcode, unwrapped)
    {
        setResultType(MIRType::Object);
    }

  public:
    INSTRUCTION_HEADER(ToAsyncIter)
    TRIVIAL_NEW_WRAPPERS
};

class MToId
  : public MUnaryInstruction,
    public BoxInputsPolicy::Data
{
    explicit MToId(MDefinition* index)
      : MUnaryInstruction(classOpcode, index)
    {
        setResultType(MIRType::Value);
    }

  public:
    INSTRUCTION_HEADER(ToId)
    TRIVIAL_NEW_WRAPPERS
};

class MBinaryBitwiseInstruction
  : public MBinaryInstruction,
    public BitwisePolicy::Data
{
  protected:
    MBinaryBitwiseInstruction(Opcode op, MDefinition* left, MDefinition* right, MIRType type)
      : MBinaryInstruction(op, left, right), maskMatchesLeftRange(false),
        maskMatchesRightRange(false)
    {
        MOZ_ASSERT(type == MIRType::Int32 || type == MIRType::Int64);
        setResultType(type);
        setMovable();
    }

    void specializeAs(MIRType type);
    bool maskMatchesLeftRange;
    bool maskMatchesRightRange;

  public:
    MDefinition* foldsTo(TempAllocator& alloc) override;
    MDefinition* foldUnnecessaryBitop();
    virtual MDefinition* foldIfZero(size_t operand) = 0;
    virtual MDefinition* foldIfNegOne(size_t operand) = 0;
    virtual MDefinition* foldIfEqual()  = 0;
    virtual MDefinition* foldIfAllBitsSet(size_t operand)  = 0;
    virtual void infer(BaselineInspector* inspector, jsbytecode* pc);
    void collectRangeInfoPreTrunc() override;

    void setInt32Specialization() {
        specialization_ = MIRType::Int32;
        setResultType(MIRType::Int32);
    }

    bool congruentTo(const MDefinition* ins) const override {
        return binaryCongruentTo(ins);
    }
    AliasSet getAliasSet() const override {
        if (specialization_ >= MIRType::Object)
            return AliasSet::Store(AliasSet::Any);
        return AliasSet::None();
    }

    TruncateKind operandTruncateKind(size_t index) const override;
};

class MBitAnd : public MBinaryBitwiseInstruction
{
    MBitAnd(MDefinition* left, MDefinition* right, MIRType type)
      : MBinaryBitwiseInstruction(classOpcode, left, right, type)
    { }

  public:
    INSTRUCTION_HEADER(BitAnd)
    static MBitAnd* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
    static MBitAnd* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type);

    MDefinition* foldIfZero(size_t operand) override {
        return getOperand(operand); // 0 & x => 0;
    }
    MDefinition* foldIfNegOne(size_t operand) override {
        return getOperand(1 - operand); // x & -1 => x
    }
    MDefinition* foldIfEqual() override {
        return getOperand(0); // x & x => x;
    }
    MDefinition* foldIfAllBitsSet(size_t operand) override {
        // e.g. for uint16: x & 0xffff => x;
        return getOperand(1 - operand);
    }
    void computeRange(TempAllocator& alloc) override;

    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        return specialization_ != MIRType::None;
    }

    ALLOW_CLONE(MBitAnd)
};

class MBitOr : public MBinaryBitwiseInstruction
{
    MBitOr(MDefinition* left, MDefinition* right, MIRType type)
      : MBinaryBitwiseInstruction(classOpcode, left, right, type)
    { }

  public:
    INSTRUCTION_HEADER(BitOr)
    static MBitOr* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
    static MBitOr* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type);

    MDefinition* foldIfZero(size_t operand) override {
        return getOperand(1 - operand); // 0 | x => x, so if ith is 0, return (1-i)th
    }
    MDefinition* foldIfNegOne(size_t operand) override {
        return getOperand(operand); // x | -1 => -1
    }
    MDefinition* foldIfEqual() override {
        return getOperand(0); // x | x => x
    }
    MDefinition* foldIfAllBitsSet(size_t operand) override {
        return this;
    }
    void computeRange(TempAllocator& alloc) override;
    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        return specialization_ != MIRType::None;
    }

    ALLOW_CLONE(MBitOr)
};

class MBitXor : public MBinaryBitwiseInstruction
{
    MBitXor(MDefinition* left, MDefinition* right, MIRType type)
      : MBinaryBitwiseInstruction(classOpcode, left, right, type)
    { }

  public:
    INSTRUCTION_HEADER(BitXor)
    static MBitXor* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
    static MBitXor* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type);

    MDefinition* foldIfZero(size_t operand) override {
        return getOperand(1 - operand); // 0 ^ x => x
    }
    MDefinition* foldIfNegOne(size_t operand) override {
        return this;
    }
    MDefinition* foldIfEqual() override {
        return this;
    }
    MDefinition* foldIfAllBitsSet(size_t operand) override {
        return this;
    }
    void computeRange(TempAllocator& alloc) override;

    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        return specialization_ < MIRType::Object;
    }

    ALLOW_CLONE(MBitXor)
};

class MShiftInstruction
  : public MBinaryBitwiseInstruction
{
  protected:
    MShiftInstruction(Opcode op, MDefinition* left, MDefinition* right, MIRType type)
      : MBinaryBitwiseInstruction(op, left, right, type)
    { }

  public:
    MDefinition* foldIfNegOne(size_t operand) override {
        return this;
    }
    MDefinition* foldIfEqual() override {
        return this;
    }
    MDefinition* foldIfAllBitsSet(size_t operand) override {
        return this;
    }
    virtual void infer(BaselineInspector* inspector, jsbytecode* pc) override;
};

class MLsh : public MShiftInstruction
{
    MLsh(MDefinition* left, MDefinition* right, MIRType type)
      : MShiftInstruction(classOpcode, left, right, type)
    { }

  public:
    INSTRUCTION_HEADER(Lsh)
    static MLsh* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
    static MLsh* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type);

    MDefinition* foldIfZero(size_t operand) override {
        // 0 << x => 0
        // x << 0 => x
        return getOperand(0);
    }

    void computeRange(TempAllocator& alloc) override;
    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        return specialization_ != MIRType::None;
    }

    ALLOW_CLONE(MLsh)
};

class MRsh : public MShiftInstruction
{
    MRsh(MDefinition* left, MDefinition* right, MIRType type)
      : MShiftInstruction(classOpcode, left, right, type)
    { }

  public:
    INSTRUCTION_HEADER(Rsh)
    static MRsh* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
    static MRsh* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type);

    MDefinition* foldIfZero(size_t operand) override {
        // 0 >> x => 0
        // x >> 0 => x
        return getOperand(0);
    }
    void computeRange(TempAllocator& alloc) override;

    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        return specialization_ < MIRType::Object;
    }

    MDefinition* foldsTo(TempAllocator& alloc) override;

    ALLOW_CLONE(MRsh)
};

class MUrsh : public MShiftInstruction
{
    bool bailoutsDisabled_;

    MUrsh(MDefinition* left, MDefinition* right, MIRType type)
      : MShiftInstruction(classOpcode, left, right, type),
        bailoutsDisabled_(false)
    { }

  public:
    INSTRUCTION_HEADER(Ursh)
    static MUrsh* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
    static MUrsh* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type);

    MDefinition* foldIfZero(size_t operand) override {
        // 0 >>> x => 0
        if (operand == 0)
            return getOperand(0);

        return this;
    }

    void infer(BaselineInspector* inspector, jsbytecode* pc) override;

    bool bailoutsDisabled() const {
        return bailoutsDisabled_;
    }

    bool fallible() const;

    void computeRange(TempAllocator& alloc) override;
    void collectRangeInfoPreTrunc() override;

    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        return specialization_ < MIRType::Object;
    }

    ALLOW_CLONE(MUrsh)
};

class MSignExtendInt32
  : public MUnaryInstruction,
    public NoTypePolicy::Data
{
  public:
    enum Mode {
        Byte,
        Half
    };

  private:
    Mode mode_;

    MSignExtendInt32(MDefinition* op, Mode mode)
      : MUnaryInstruction(classOpcode, op), mode_(mode)
    {
        setResultType(MIRType::Int32);
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(SignExtendInt32)
    TRIVIAL_NEW_WRAPPERS

    Mode mode() const { return mode_; }

    MDefinition* foldsTo(TempAllocator& alloc) override;
    bool congruentTo(const MDefinition* ins) const override {
        if (!congruentIfOperandsEqual(ins))
            return false;
        return ins->isSignExtendInt32() && ins->toSignExtendInt32()->mode_ == mode_;
    }
    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
    bool canRecoverOnBailout() const override {
        return true;
    }

    ALLOW_CLONE(MSignExtendInt32)
};

class MSignExtendInt64
  : public MUnaryInstruction,
    public NoTypePolicy::Data
{
  public:
    enum Mode {
        Byte,
        Half,
        Word
    };

  private:
    Mode mode_;

    MSignExtendInt64(MDefinition* op, Mode mode)
      : MUnaryInstruction(classOpcode, op), mode_(mode)
    {
        setResultType(MIRType::Int64);
        setMovable();
    }

  public:
    INSTRUCTION_HEADER(SignExtendInt64)
    TRIVIAL_NEW_WRAPPERS

    Mode mode() const { return mode_; }

    MDefinition* foldsTo(TempAllocator& alloc) override;
    bool congruentTo(const MDefinition* ins) const override {
        if (!congruentIfOperandsEqual(ins))
            return false;
        return ins->isSignExtendInt64() && ins->toSignExtendInt64()->mode_ == mode_;
    }
    AliasSet getAliasSet() const override {
        return AliasSet::None();
    }

    ALLOW_CLONE(MSignExtendInt64)
};

class MBinaryArithInstruction
  : public MBinaryInstruction,
    public ArithPolicy::Data
{
    // Implicit truncate flag is set by the truncate backward range analysis
    // optimization phase, and by wasm pre-processing. It is used in
    // NeedNegativeZeroCheck to check if the result of a multiplication needs to
    // produce -0 double value, and for avoiding overflow checks.

    // This optimization happens when the multiplication cannot be truncated
    // even if all uses are truncating its result, such as when the range
    // analysis detect a precision loss in the multiplication.
    TruncateKind implicitTruncate_;

    // Whether we must preserve NaN semantics, and in particular not fold
    // (x op id) or (id op x) to x, or replace a division by a multiply of the
    // exact reciprocal.
    bool mustPreserveNaN_;

  public:
    MBinaryArithInstruction(Opcode op, MDefinition* left, MDefinition* right)
      : MBinaryInstruction(op, left, right),
        implicitTruncate_(NoTruncate),
        mustPreserveNaN_(false)
    {
        specialization_ = MIRType::None;
        setMovable();
    }

    static MBinaryArithInstruction* New(TempAllocator& alloc, Opcode op,
                                        MDefinition* left, MDefinition* right);

    bool constantDoubleResult(TempAllocator& alloc);

    void setMustPreserveNaN(bool b) { mustPreserveNaN_ = b; }
    bool mustPreserveNaN() const { return mustPreserveNaN_; }

    MDefinition* foldsTo(TempAllocator& alloc) override;
    void printOpcode(GenericPrinter& out) const override;

    virtual double getIdentity() = 0;

    void setSpecialization(MIRType type) {
        specialization_ = type;
        setResultType(type);
    }
    void setInt32Specialization() {
        specialization_ = MIRType::Int32;
        setResultType(MIRType::Int32);
    }
    void setNumberSpecialization(TempAllocator& alloc, BaselineInspector* inspector, jsbytecode* pc);

    virtual void trySpecializeFloat32(TempAllocator& alloc) override;

    bool congruentTo(const MDefinition* ins) const override {
        if (!binaryCongruentTo(ins))
            return false;
        const auto* other = static_cast<const MBinaryArithInstruction*>(ins);
        return other->mustPreserveNaN_ == mustPreserveNaN_;
    }
    AliasSet getAliasSet() const override {
        if (specialization_ >= MIRType::Object)
            return AliasSet::Store(AliasSet::Any);
        return AliasSet