/* -*- 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/. */

#ifndef jit_IonBuilder_h
#define jit_IonBuilder_h

// This file declares the data structures for building a MIRGraph from a
// JSScript.

#include "mozilla/LinkedList.h"

#include "jit/BaselineInspector.h"
#include "jit/BytecodeAnalysis.h"
#include "jit/IonAnalysis.h"
#include "jit/IonOptimizationLevels.h"
#include "jit/MIR.h"
#include "jit/MIRGenerator.h"
#include "jit/MIRGraph.h"
#include "jit/OptimizationTracking.h"

namespace js {
namespace jit {

class CodeGenerator;
class CallInfo;
class BaselineFrameInspector;

enum class InlinableNative : uint16_t;

// Records information about a baseline frame for compilation that is stable
// when later used off thread.
BaselineFrameInspector*
NewBaselineFrameInspector(TempAllocator* temp, BaselineFrame* frame, CompileInfo* info);

class IonBuilder
  : public MIRGenerator,
    public mozilla::LinkedListElement<IonBuilder>
{
    enum ControlStatus {
        ControlStatus_Error,
        ControlStatus_Abort,
        ControlStatus_Ended,        // There is no continuation/join point.
        ControlStatus_Joined,       // Created a join node.
        ControlStatus_Jumped,       // Parsing another branch at the same level.
        ControlStatus_None          // No control flow.
    };

    struct DeferredEdge : public TempObject
    {
        MBasicBlock* block;
        DeferredEdge* next;

        DeferredEdge(MBasicBlock* block, DeferredEdge* next)
          : block(block), next(next)
        { }
    };

    struct ControlFlowInfo {
        // Entry in the cfgStack.
        uint32_t cfgEntry;

        // Label that continues go to.
        jsbytecode* continuepc;

        ControlFlowInfo(uint32_t cfgEntry, jsbytecode* continuepc)
          : cfgEntry(cfgEntry),
            continuepc(continuepc)
        { }
    };

    // To avoid recursion, the bytecode analyzer uses a stack where each entry
    // is a small state machine. As we encounter branches or jumps in the
    // bytecode, we push information about the edges on the stack so that the
    // CFG can be built in a tree-like fashion.
    struct CFGState {
        enum State {
            IF_TRUE,            // if() { }, no else.
            IF_TRUE_EMPTY_ELSE, // if() { }, empty else
            IF_ELSE_TRUE,       // if() { X } else { }
            IF_ELSE_FALSE,      // if() { } else { X }
            DO_WHILE_LOOP_BODY, // do { x } while ()
            DO_WHILE_LOOP_COND, // do { } while (x)
            WHILE_LOOP_COND,    // while (x) { }
            WHILE_LOOP_BODY,    // while () { x }
            FOR_LOOP_COND,      // for (; x;) { }
            FOR_LOOP_BODY,      // for (; ;) { x }
            FOR_LOOP_UPDATE,    // for (; ; x) { }
            TABLE_SWITCH,       // switch() { x }
            COND_SWITCH_CASE,   // switch() { case X: ... }
            COND_SWITCH_BODY,   // switch() { case ...: X }
            AND_OR,             // && x, || x
            LABEL,              // label: x
            TRY                 // try { x } catch(e) { }
        };

        State state;            // Current state of this control structure.
        jsbytecode* stopAt;     // Bytecode at which to stop the processing loop.

        // For if structures, this contains branch information.
        union {
            struct {
                MBasicBlock* ifFalse;
                jsbytecode* falseEnd;
                MBasicBlock* ifTrue;    // Set when the end of the true path is reached.
                MTest* test;
            } branch;
            struct {
                // Common entry point.
                MBasicBlock* entry;

                // Whether OSR is being performed for this loop.
                bool osr;

                // Position of where the loop body starts and ends.
                jsbytecode* bodyStart;
                jsbytecode* bodyEnd;

                // pc immediately after the loop exits.
                jsbytecode* exitpc;

                // pc for 'continue' jumps.
                jsbytecode* continuepc;

                // Common exit point. Created lazily, so it may be nullptr.
                MBasicBlock* successor;

                // Deferred break and continue targets.
                DeferredEdge* breaks;
                DeferredEdge* continues;

                // Initial state, in case loop processing is restarted.
                State initialState;
                jsbytecode* initialPc;
                jsbytecode* initialStopAt;
                jsbytecode* loopHead;

                // For-loops only.
                jsbytecode* condpc;
                jsbytecode* updatepc;
                jsbytecode* updateEnd;
            } loop;
            struct {
                // pc immediately after the switch.
                jsbytecode* exitpc;

                // Deferred break and continue targets.
                DeferredEdge* breaks;

                // MIR instruction
                MTableSwitch* ins;

                // The number of current successor that get mapped into a block.
                uint32_t currentBlock;

            } tableswitch;
            struct {
                // Vector of body blocks to process after the cases.
                FixedList<MBasicBlock*>* bodies;

                // When processing case statements, this counter points at the
                // last uninitialized body.  When processing bodies, this
                // counter targets the next body to process.
                uint32_t currentIdx;

                // Remember the block index of the default case.
                jsbytecode* defaultTarget;
                uint32_t defaultIdx;

                // Block immediately after the switch.
                jsbytecode* exitpc;
                DeferredEdge* breaks;
            } condswitch;
            struct {
                DeferredEdge* breaks;
            } label;
            struct {
                MBasicBlock* successor;
            } try_;
        };

        inline bool isLoop() const {
            switch (state) {
              case DO_WHILE_LOOP_COND:
              case DO_WHILE_LOOP_BODY:
              case WHILE_LOOP_COND:
              case WHILE_LOOP_BODY:
              case FOR_LOOP_COND:
              case FOR_LOOP_BODY:
              case FOR_LOOP_UPDATE:
                return true;
              default:
                return false;
            }
        }

        static CFGState If(jsbytecode* join, MTest* test);
        static CFGState IfElse(jsbytecode* trueEnd, jsbytecode* falseEnd, MTest* test);
        static CFGState AndOr(jsbytecode* join, MBasicBlock* lhs);
        static CFGState TableSwitch(jsbytecode* exitpc, MTableSwitch* ins);
        static CFGState CondSwitch(IonBuilder* builder, jsbytecode* exitpc, jsbytecode* defaultTarget);
        static CFGState Label(jsbytecode* exitpc);
        static CFGState Try(jsbytecode* exitpc, MBasicBlock* successor);
    };

    static int CmpSuccessors(const void* a, const void* b);

  public:
    IonBuilder(JSContext* analysisContext, CompileCompartment* comp,
               const JitCompileOptions& options, TempAllocator* temp,
               MIRGraph* graph, CompilerConstraintList* constraints,
               BaselineInspector* inspector, CompileInfo* info,
               const OptimizationInfo* optimizationInfo, BaselineFrameInspector* baselineFrame,
               size_t inliningDepth = 0, uint32_t loopDepth = 0);

    // Callers of build() and buildInline() should always check whether the
    // call overrecursed, if false is returned.  Overrecursion is not
    // signaled as OOM and will not in general be caught by OOM paths.
    MOZ_MUST_USE bool build();
    MOZ_MUST_USE bool buildInline(IonBuilder* callerBuilder, MResumePoint* callerResumePoint,
                                  CallInfo& callInfo);

  private:
    MOZ_MUST_USE bool traverseBytecode();
    ControlStatus snoopControlFlow(JSOp op);
    MOZ_MUST_USE bool processIterators();
    MOZ_MUST_USE bool inspectOpcode(JSOp op);
    uint32_t readIndex(jsbytecode* pc);
    JSAtom* readAtom(jsbytecode* pc);
    bool abort(const char* message, ...) MOZ_FORMAT_PRINTF(2, 3);
    void trackActionableAbort(const char* message);
    void spew(const char* message);

    JSFunction* getSingleCallTarget(TemporaryTypeSet* calleeTypes);
    MOZ_MUST_USE bool getPolyCallTargets(TemporaryTypeSet* calleeTypes, bool constructing,
                                         ObjectVector& targets, uint32_t maxTargets);

    void popCfgStack();
    DeferredEdge* filterDeadDeferredEdges(DeferredEdge* edge);
    MOZ_MUST_USE bool processDeferredContinues(CFGState& state);
    ControlStatus processControlEnd();
    ControlStatus processCfgStack();
    ControlStatus processCfgEntry(CFGState& state);
    ControlStatus processIfEnd(CFGState& state);
    ControlStatus processIfElseTrueEnd(CFGState& state);
    ControlStatus processIfElseFalseEnd(CFGState& state);
    ControlStatus processDoWhileBodyEnd(CFGState& state);
    ControlStatus processDoWhileCondEnd(CFGState& state);
    ControlStatus processWhileCondEnd(CFGState& state);
    ControlStatus processWhileBodyEnd(CFGState& state);
    ControlStatus processForCondEnd(CFGState& state);
    ControlStatus processForBodyEnd(CFGState& state);
    ControlStatus processForUpdateEnd(CFGState& state);
    ControlStatus processNextTableSwitchCase(CFGState& state);
    ControlStatus processCondSwitchCase(CFGState& state);
    ControlStatus processCondSwitchBody(CFGState& state);
    ControlStatus processSwitchBreak(JSOp op);
    ControlStatus processSwitchEnd(DeferredEdge* breaks, jsbytecode* exitpc);
    ControlStatus processAndOrEnd(CFGState& state);
    ControlStatus processLabelEnd(CFGState& state);
    ControlStatus processTryEnd(CFGState& state);
    ControlStatus processReturn(JSOp op);
    ControlStatus processThrow();
    ControlStatus processContinue(JSOp op);
    ControlStatus processBreak(JSOp op, jssrcnote* sn);
    ControlStatus maybeLoop(JSOp op, jssrcnote* sn);
    MOZ_MUST_USE bool pushLoop(CFGState::State state, jsbytecode* stopAt, MBasicBlock* entry,
                               bool osr, jsbytecode* loopHead, jsbytecode* initialPc,
                               jsbytecode* bodyStart, jsbytecode* bodyEnd,
                               jsbytecode* exitpc, jsbytecode* continuepc);
    MOZ_MUST_USE bool analyzeNewLoopTypes(MBasicBlock* entry, jsbytecode* start, jsbytecode* end);

    MBasicBlock* addBlock(MBasicBlock* block, uint32_t loopDepth);
    MBasicBlock* newBlock(MBasicBlock* predecessor, jsbytecode* pc);
    MBasicBlock* newBlock(MBasicBlock* predecessor, jsbytecode* pc, uint32_t loopDepth);
    MBasicBlock* newBlock(MBasicBlock* predecessor, jsbytecode* pc, MResumePoint* priorResumePoint);
    MBasicBlock* newBlockPopN(MBasicBlock* predecessor, jsbytecode* pc, uint32_t popped);
    MBasicBlock* newBlockAfter(MBasicBlock* at, MBasicBlock* predecessor, jsbytecode* pc);
    MBasicBlock* newOsrPreheader(MBasicBlock* header, jsbytecode* loopEntry,
                                 jsbytecode* beforeLoopEntry);
    MBasicBlock* newPendingLoopHeader(MBasicBlock* predecessor, jsbytecode* pc, bool osr, bool canOsr,
                                      unsigned stackPhiCount);
    MBasicBlock* newBlock(jsbytecode* pc) {
        return newBlock(nullptr, pc);
    }
    MBasicBlock* newBlockAfter(MBasicBlock* at, jsbytecode* pc) {
        return newBlockAfter(at, nullptr, pc);
    }

    // We want to make sure that our MTest instructions all check whether the
    // thing being tested might emulate undefined.  So we funnel their creation
    // through this method, to make sure that happens.  We don't want to just do
    // the check in MTest::New, because that can run on background compilation
    // threads, and we're not sure it's safe to touch that part of the typeset
    // from a background thread.
    MTest* newTest(MDefinition* ins, MBasicBlock* ifTrue, MBasicBlock* ifFalse);

    // Given a list of pending breaks, creates a new block and inserts a Goto
    // linking each break to the new block.
    MBasicBlock* createBreakCatchBlock(DeferredEdge* edge, jsbytecode* pc);

    // Finishes loops that do not actually loop, containing only breaks and
    // returns or a do while loop with a condition that is constant false.
    ControlStatus processBrokenLoop(CFGState& state);

    // Computes loop phis, places them in all successors of a loop, then
    // handles any pending breaks.
    ControlStatus finishLoop(CFGState& state, MBasicBlock* successor);

    // Incorporates a type/typeSet into an OSR value for a loop, after the loop
    // body has been processed.
    MOZ_MUST_USE bool addOsrValueTypeBarrier(uint32_t slot, MInstruction** def,
                                             MIRType type, TemporaryTypeSet* typeSet);
    MOZ_MUST_USE bool maybeAddOsrTypeBarriers();

    // Restarts processing of a loop if the type information at its header was
    // incomplete.
    ControlStatus restartLoop(const CFGState& state);

    void assertValidLoopHeadOp(jsbytecode* pc);

    ControlStatus forLoop(JSOp op, jssrcnote* sn);
    ControlStatus whileOrForInLoop(jssrcnote* sn);
    ControlStatus doWhileLoop(JSOp op, jssrcnote* sn);
    ControlStatus tableSwitch(JSOp op, jssrcnote* sn);
    ControlStatus condSwitch(JSOp op, jssrcnote* sn);

    // Please see the Big Honkin' Comment about how resume points work in
    // IonBuilder.cpp, near the definition for this function.
    MOZ_MUST_USE bool resume(MInstruction* ins, jsbytecode* pc, MResumePoint::Mode mode);
    MOZ_MUST_USE bool resumeAt(MInstruction* ins, jsbytecode* pc);
    MOZ_MUST_USE bool resumeAfter(MInstruction* ins);
    MOZ_MUST_USE bool maybeInsertResume();

    void insertRecompileCheck();

    MOZ_MUST_USE bool initParameters();
    void initLocals();
    void rewriteParameter(uint32_t slotIdx, MDefinition* param, int32_t argIndex);
    MOZ_MUST_USE bool rewriteParameters();
    MOZ_MUST_USE bool initEnvironmentChain(MDefinition* callee = nullptr);
    MOZ_MUST_USE bool initArgumentsObject();
    void pushConstant(const Value& v);

    MConstant* constant(const Value& v);
    MConstant* constantInt(int32_t i);
    MInstruction* initializedLength(MDefinition* obj, MDefinition* elements,
                                    JSValueType unboxedType);
    MInstruction* setInitializedLength(MDefinition* obj, JSValueType unboxedType, size_t count);

    // Improve the type information at tests
    MOZ_MUST_USE bool improveTypesAtTest(MDefinition* ins, bool trueBranch, MTest* test);
    MOZ_MUST_USE bool improveTypesAtCompare(MCompare* ins, bool trueBranch, MTest* test);
    MOZ_MUST_USE bool improveTypesAtNullOrUndefinedCompare(MCompare* ins, bool trueBranch,
                                                           MTest* test);
    MOZ_MUST_USE bool improveTypesAtTypeOfCompare(MCompare* ins, bool trueBranch, MTest* test);

    // Used to detect triangular structure at test.
    MOZ_MUST_USE bool detectAndOrStructure(MPhi* ins, bool* branchIsTrue);
    MOZ_MUST_USE bool replaceTypeSet(MDefinition* subject, TemporaryTypeSet* type, MTest* test);

    // Add a guard which ensure that the set of type which goes through this
    // generated code correspond to the observed types for the bytecode.
    MDefinition* addTypeBarrier(MDefinition* def, TemporaryTypeSet* observed,
                                BarrierKind kind, MTypeBarrier** pbarrier = nullptr);
    MOZ_MUST_USE bool pushTypeBarrier(MDefinition* def, TemporaryTypeSet* observed,
                                      BarrierKind kind);

    // As pushTypeBarrier, but will compute the needBarrier boolean itself based
    // on observed and the JSFunction that we're planning to call. The
    // JSFunction must be a DOM method or getter.
    MOZ_MUST_USE bool pushDOMTypeBarrier(MInstruction* ins, TemporaryTypeSet* observed,
                                         JSFunction* func);

    // If definiteType is not known or def already has the right type, just
    // returns def.  Otherwise, returns an MInstruction that has that definite
    // type, infallibly unboxing ins as needed.  The new instruction will be
    // added to |current| in this case.
    MDefinition* ensureDefiniteType(MDefinition* def, MIRType definiteType);

    // Creates a MDefinition based on the given def improved with type as TypeSet.
    MDefinition* ensureDefiniteTypeSet(MDefinition* def, TemporaryTypeSet* types);

    void maybeMarkEmpty(MDefinition* ins);

    JSObject* getSingletonPrototype(JSFunction* target);

    MDefinition* createThisScripted(MDefinition* callee, MDefinition* newTarget);
    MDefinition* createThisScriptedSingleton(JSFunction* target, MDefinition* callee);
    MDefinition* createThisScriptedBaseline(MDefinition* callee);
    MDefinition* createThis(JSFunction* target, MDefinition* callee, MDefinition* newTarget);
    MInstruction* createNamedLambdaObject(MDefinition* callee, MDefinition* envObj);
    MInstruction* createCallObject(MDefinition* callee, MDefinition* envObj);

    MDefinition* walkEnvironmentChain(unsigned hops);

    MInstruction* addConvertElementsToDoubles(MDefinition* elements);
    MDefinition* addMaybeCopyElementsForWrite(MDefinition* object, bool checkNative);
    MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length);
    MInstruction* addShapeGuard(MDefinition* obj, Shape* const shape, BailoutKind bailoutKind);
    MInstruction* addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind);
    MInstruction* addUnboxedExpandoGuard(MDefinition* obj, bool hasExpando, BailoutKind bailoutKind);
    MInstruction* addSharedTypedArrayGuard(MDefinition* obj);

    MInstruction*
    addGuardReceiverPolymorphic(MDefinition* obj, const BaselineInspector::ReceiverVector& receivers);

    MDefinition* convertShiftToMaskForStaticTypedArray(MDefinition* id,
                                                       Scalar::Type viewType);

    bool invalidatedIdempotentCache();

    bool hasStaticEnvironmentObject(EnvironmentCoordinate ec, JSObject** pcall);
    MOZ_MUST_USE bool loadSlot(MDefinition* obj, size_t slot, size_t nfixed, MIRType rvalType,
                               BarrierKind barrier, TemporaryTypeSet* types);
    MOZ_MUST_USE bool loadSlot(MDefinition* obj, Shape* shape, MIRType rvalType,
                               BarrierKind barrier, TemporaryTypeSet* types);
    MOZ_MUST_USE bool storeSlot(MDefinition* obj, size_t slot, size_t nfixed, MDefinition* value,
                                bool needsBarrier, MIRType slotType = MIRType::None);
    MOZ_MUST_USE bool storeSlot(MDefinition* obj, Shape* shape, MDefinition* value,
                                bool needsBarrier, MIRType slotType = MIRType::None);
    bool shouldAbortOnPreliminaryGroups(MDefinition *obj);

    MDefinition* tryInnerizeWindow(MDefinition* obj);
    MDefinition* maybeUnboxForPropertyAccess(MDefinition* def);

    // jsop_getprop() helpers.
    MOZ_MUST_USE bool checkIsDefinitelyOptimizedArguments(MDefinition* obj, bool* isOptimizedArgs);
    MOZ_MUST_USE bool getPropTryInferredConstant(bool* emitted, MDefinition* obj,
                                                 PropertyName* name, TemporaryTypeSet* types);
    MOZ_MUST_USE bool getPropTryArgumentsLength(bool* emitted, MDefinition* obj);
    MOZ_MUST_USE bool getPropTryArgumentsCallee(bool* emitted, MDefinition* obj,
                                                PropertyName* name);
    MOZ_MUST_USE bool getPropTryConstant(bool* emitted, MDefinition* obj, jsid id,
                                         TemporaryTypeSet* types);
    MOZ_MUST_USE bool getPropTryNotDefined(bool* emitted, MDefinition* obj, jsid id,
                                           TemporaryTypeSet* types);
    MOZ_MUST_USE bool getPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName* name,
                                             BarrierKind barrier, TemporaryTypeSet* types);
    MOZ_MUST_USE bool getPropTryModuleNamespace(bool* emitted, MDefinition* obj, PropertyName* name,
                                                BarrierKind barrier, TemporaryTypeSet* types);
    MOZ_MUST_USE bool getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* name,
                                        BarrierKind barrier, TemporaryTypeSet* types);
    MOZ_MUST_USE bool getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName* name,
                                             TemporaryTypeSet* types);
    MOZ_MUST_USE bool getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name,
                                             BarrierKind barrier, TemporaryTypeSet* types);
    MOZ_MUST_USE bool getPropTryTypedObject(bool* emitted, MDefinition* obj, PropertyName* name);
    MOZ_MUST_USE bool getPropTryScalarPropOfTypedObject(bool* emitted, MDefinition* typedObj,
                                                        int32_t fieldOffset,
                                                        TypedObjectPrediction fieldTypeReprs);
    MOZ_MUST_USE bool getPropTryReferencePropOfTypedObject(bool* emitted, MDefinition* typedObj,
                                                           int32_t fieldOffset,
                                                           TypedObjectPrediction fieldPrediction,
                                                           PropertyName* name);
    MOZ_MUST_USE bool getPropTryComplexPropOfTypedObject(bool* emitted, MDefinition* typedObj,
                                                         int32_t fieldOffset,
                                                         TypedObjectPrediction fieldTypeReprs,
                                                         size_t fieldIndex);
    MOZ_MUST_USE bool getPropTryInnerize(bool* emitted, MDefinition* obj, PropertyName* name,
                                         TemporaryTypeSet* types);
    MOZ_MUST_USE bool getPropTryCache(bool* emitted, MDefinition* obj, PropertyName* name,
                                      BarrierKind barrier, TemporaryTypeSet* types);
    MOZ_MUST_USE bool getPropTrySharedStub(bool* emitted, MDefinition* obj,
                                           TemporaryTypeSet* types);

    // jsop_setprop() helpers.
    MOZ_MUST_USE bool setPropTryCommonSetter(bool* emitted, MDefinition* obj,
                                             PropertyName* name, MDefinition* value);
    MOZ_MUST_USE bool setPropTryCommonDOMSetter(bool* emitted, MDefinition* obj,
                                                MDefinition* value, JSFunction* setter,
                                                TemporaryTypeSet* objTypes);
    MOZ_MUST_USE bool setPropTryDefiniteSlot(bool* emitted, MDefinition* obj,
                                             PropertyName* name, MDefinition* value,
                                             bool barrier, TemporaryTypeSet* objTypes);
    MOZ_MUST_USE bool setPropTryUnboxed(bool* emitted, MDefinition* obj,
                                        PropertyName* name, MDefinition* value,
                                        bool barrier, TemporaryTypeSet* objTypes);
    MOZ_MUST_USE bool setPropTryInlineAccess(bool* emitted, MDefinition* obj,
                                             PropertyName* name, MDefinition* value,
                                             bool barrier, TemporaryTypeSet* objTypes);
    MOZ_MUST_USE bool setPropTryTypedObject(bool* emitted, MDefinition* obj,
                                            PropertyName* name, MDefinition* value);
    MOZ_MUST_USE bool setPropTryReferencePropOfTypedObject(bool* emitted, MDefinition* obj,
                                                           int32_t fieldOffset, MDefinition* value,
                                                           TypedObjectPrediction fieldPrediction,
                                                           PropertyName* name);
    MOZ_MUST_USE bool setPropTryScalarPropOfTypedObject(bool* emitted,
                                                        MDefinition* obj,
                                                        int32_t fieldOffset,
                                                        MDefinition* value,
                                                        TypedObjectPrediction fieldTypeReprs);
    MOZ_MUST_USE bool setPropTryCache(bool* emitted, MDefinition* obj,
                                      PropertyName* name, MDefinition* value,
                                      bool barrier, TemporaryTypeSet* objTypes);

    // jsop_binary_arith helpers.
    MBinaryArithInstruction* binaryArithInstruction(JSOp op, MDefinition* left, MDefinition* right);
    MOZ_MUST_USE bool binaryArithTryConcat(bool* emitted, JSOp op, MDefinition* left,
                                           MDefinition* right);
    MOZ_MUST_USE bool binaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* left,
                                                MDefinition* right);
    MOZ_MUST_USE bool binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
                                                                   MDefinition* left,
                                                                   MDefinition* right);
    MOZ_MUST_USE bool arithTrySharedStub(bool* emitted, JSOp op, MDefinition* left,
                                         MDefinition* right);

    // jsop_bitnot helpers.
    MOZ_MUST_USE bool bitnotTrySpecialized(bool* emitted, MDefinition* input);

    // jsop_pow helpers.
    MOZ_MUST_USE bool powTrySpecialized(bool* emitted, MDefinition* base, MDefinition* power,
                                        MIRType outputType);

    // jsop_compare helpers.
    MOZ_MUST_USE bool compareTrySpecialized(bool* emitted, JSOp op, MDefinition* left,
                                            MDefinition* right);
    MOZ_MUST_USE bool compareTryBitwise(bool* emitted, JSOp op, MDefinition* left,
                                        MDefinition* right);
    MOZ_MUST_USE bool compareTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
                                                               MDefinition* left,
                                                               MDefinition* right);
    MOZ_MUST_USE bool compareTrySharedStub(bool* emitted, JSOp op, MDefinition* left,
                                           MDefinition* right);

    // jsop_newarray helpers.
    MOZ_MUST_USE bool newArrayTrySharedStub(bool* emitted);
    MOZ_MUST_USE bool newArrayTryTemplateObject(bool* emitted, JSObject* templateObject,
                                                uint32_t length);
    MOZ_MUST_USE bool newArrayTryVM(bool* emitted, JSObject* templateObject, uint32_t length);

    // jsop_newobject helpers.
    MOZ_MUST_USE bool newObjectTrySharedStub(bool* emitted);
    MOZ_MUST_USE bool newObjectTryTemplateObject(bool* emitted, JSObject* templateObject);
    MOZ_MUST_USE bool newObjectTryVM(bool* emitted, JSObject* templateObject);

    // jsop_in helpers.
    MOZ_MUST_USE bool inTryDense(bool* emitted, MDefinition* obj, MDefinition* id);
    MOZ_MUST_USE bool inTryFold(bool* emitted, MDefinition* obj, MDefinition* id);

    // binary data lookup helpers.
    TypedObjectPrediction typedObjectPrediction(MDefinition* typedObj);
    TypedObjectPrediction typedObjectPrediction(TemporaryTypeSet* types);
    MOZ_MUST_USE bool typedObjectHasField(MDefinition* typedObj,
                                          PropertyName* name,
                                          size_t* fieldOffset,
                                          TypedObjectPrediction* fieldTypeReprs,
                                          size_t* fieldIndex);
    MDefinition* loadTypedObjectType(MDefinition* value);
    void loadTypedObjectData(MDefinition* typedObj,
                             MDefinition** owner,
                             LinearSum* ownerOffset);
    void loadTypedObjectElements(MDefinition* typedObj,
                                 const LinearSum& byteOffset,
                                 uint32_t scale,
                                 MDefinition** ownerElements,
                                 MDefinition** ownerScaledOffset,
                                 int32_t* ownerByteAdjustment);
    MDefinition* typeObjectForElementFromArrayStructType(MDefinition* typedObj);
    MDefinition* typeObjectForFieldFromStructType(MDefinition* type,
                                                  size_t fieldIndex);
    MOZ_MUST_USE bool storeReferenceTypedObjectValue(MDefinition* typedObj,
                                                     const LinearSum& byteOffset,
                                                     ReferenceTypeDescr::Type type,
                                                     MDefinition* value,
                                                     PropertyName* name);
    MOZ_MUST_USE bool storeScalarTypedObjectValue(MDefinition* typedObj,
                                                  const LinearSum& byteOffset,
                                                  ScalarTypeDescr::Type type,
                                                  MDefinition* value);
    MOZ_MUST_USE bool checkTypedObjectIndexInBounds(uint32_t elemSize,
                                                    MDefinition* obj,
                                                    MDefinition* index,
                                                    TypedObjectPrediction objTypeDescrs,
                                                    LinearSum* indexAsByteOffset);
    MOZ_MUST_USE bool pushDerivedTypedObject(bool* emitted,
                                             MDefinition* obj,
                                             const LinearSum& byteOffset,
                                             TypedObjectPrediction derivedTypeDescrs,
                                             MDefinition* derivedTypeObj);
    MOZ_MUST_USE bool pushScalarLoadFromTypedObject(MDefinition* obj,
                                                    const LinearSum& byteoffset,
                                                    ScalarTypeDescr::Type type);
    MOZ_MUST_USE bool pushReferenceLoadFromTypedObject(MDefinition* typedObj,
                                                       const LinearSum& byteOffset,
                                                       ReferenceTypeDescr::Type type,
                                                       PropertyName* name);
    JSObject* getStaticTypedArrayObject(MDefinition* obj, MDefinition* index);

    // jsop_setelem() helpers.
    MOZ_MUST_USE bool setElemTryTypedArray(bool* emitted, MDefinition* object,
                                           MDefinition* index, MDefinition* value);
    MOZ_MUST_USE bool setElemTryTypedObject(bool* emitted, MDefinition* obj,
                                            MDefinition* index, MDefinition* value);
    MOZ_MUST_USE bool setElemTryTypedStatic(bool* emitted, MDefinition* object,
                                            MDefinition* index, MDefinition* value);
    MOZ_MUST_USE bool setElemTryDense(bool* emitted, MDefinition* object,
                                      MDefinition* index, MDefinition* value, bool writeHole);
    MOZ_MUST_USE bool setElemTryArguments(bool* emitted, MDefinition* object,
                                          MDefinition* index, MDefinition* value);
    MOZ_MUST_USE bool setElemTryCache(bool* emitted, MDefinition* object,
                                      MDefinition* index, MDefinition* value);
    MOZ_MUST_USE bool setElemTryReferenceElemOfTypedObject(bool* emitted,
                                                           MDefinition* obj,
                                                           MDefinition* index,
                                                           TypedObjectPrediction objPrediction,
                                                           MDefinition* value,
                                                           TypedObjectPrediction elemPrediction);
    MOZ_MUST_USE bool setElemTryScalarElemOfTypedObject(bool* emitted,
                                                        MDefinition* obj,
                                                        MDefinition* index,
                                                        TypedObjectPrediction objTypeReprs,
                                                        MDefinition* value,
                                                        TypedObjectPrediction elemTypeReprs,
                                                        uint32_t elemSize);
    MOZ_MUST_USE bool initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value,
                                             JSValueType unboxedType,
                                             bool addResumePointAndIncrementInitializedLength);

    // jsop_getelem() helpers.
    MOZ_MUST_USE bool getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index);
    MOZ_MUST_USE bool getElemTryGetProp(bool* emitted, MDefinition* obj, MDefinition* index);
    MOZ_MUST_USE bool getElemTryTypedStatic(bool* emitted, MDefinition* obj, MDefinition* index);
    MOZ_MUST_USE bool getElemTryTypedArray(bool* emitted, MDefinition* obj, MDefinition* index);
    MOZ_MUST_USE bool getElemTryTypedObject(bool* emitted, MDefinition* obj, MDefinition* index);
    MOZ_MUST_USE bool getElemTryString(bool* emitted, MDefinition* obj, MDefinition* index);
    MOZ_MUST_USE bool getElemTryArguments(bool* emitted, MDefinition* obj, MDefinition* index);
    MOZ_MUST_USE bool getElemTryArgumentsInlined(bool* emitted, MDefinition* obj,
                                                 MDefinition* index);
    MOZ_MUST_USE bool getElemTryCache(bool* emitted, MDefinition* obj, MDefinition* index);
    MOZ_MUST_USE bool getElemTryScalarElemOfTypedObject(bool* emitted,
                                                        MDefinition* obj,
                                                        MDefinition* index,
                                                        TypedObjectPrediction objTypeReprs,
                                                        TypedObjectPrediction elemTypeReprs,
                                                        uint32_t elemSize);
    MOZ_MUST_USE bool getElemTryReferenceElemOfTypedObject(bool* emitted,
                                                           MDefinition* obj,
                                                           MDefinition* index,
                                                           TypedObjectPrediction objPrediction,
                                                           TypedObjectPrediction elemPrediction);
    MOZ_MUST_USE bool getElemTryComplexElemOfTypedObject(bool* emitted,
                                                         MDefinition* obj,
                                                         MDefinition* index,
                                                         TypedObjectPrediction objTypeReprs,
                                                         TypedObjectPrediction elemTypeReprs,
                                                         uint32_t elemSize);
    TemporaryTypeSet* computeHeapType(const TemporaryTypeSet* objTypes, const jsid id);

    enum BoundsChecking { DoBoundsCheck, SkipBoundsCheck };

    MInstruction* addArrayBufferByteLength(MDefinition* obj);

    // Add instructions to compute a typed array's length and data.  Also
    // optionally convert |*index| into a bounds-checked definition, if
    // requested.
    //
    // If you only need the array's length, use addTypedArrayLength below.
    void addTypedArrayLengthAndData(MDefinition* obj,
                                    BoundsChecking checking,
                                    MDefinition** index,
                                    MInstruction** length, MInstruction** elements);

    // Add an instruction to compute a typed array's length to the current
    // block.  If you also need the typed array's data, use the above method
    // instead.
    MInstruction* addTypedArrayLength(MDefinition* obj) {
        MInstruction* length;
        addTypedArrayLengthAndData(obj, SkipBoundsCheck, nullptr, &length, nullptr);
        return length;
    }

    MOZ_MUST_USE bool improveThisTypesForCall();

    MDefinition* getCallee();
    MDefinition* getAliasedVar(EnvironmentCoordinate ec);
    MDefinition* addLexicalCheck(MDefinition* input);

    MDefinition* convertToBoolean(MDefinition* input);

    MOZ_MUST_USE bool tryFoldInstanceOf(MDefinition* lhs, JSObject* protoObject);
    MOZ_MUST_USE bool hasOnProtoChain(TypeSet::ObjectKey* key, JSObject* protoObject,
                                      bool* hasOnProto);

    MOZ_MUST_USE bool jsop_add(MDefinition* left, MDefinition* right);
    MOZ_MUST_USE bool jsop_bitnot();
    MOZ_MUST_USE bool jsop_bitop(JSOp op);
    MOZ_MUST_USE bool jsop_binary_arith(JSOp op);
    MOZ_MUST_USE bool jsop_binary_arith(JSOp op, MDefinition* left, MDefinition* right);
    MOZ_MUST_USE bool jsop_pow();
    MOZ_MUST_USE bool jsop_pos();
    MOZ_MUST_USE bool jsop_neg();
    MOZ_MUST_USE bool jsop_tostring();
    MOZ_MUST_USE bool jsop_setarg(uint32_t arg);
    MOZ_MUST_USE bool jsop_defvar(uint32_t index);
    MOZ_MUST_USE bool jsop_deflexical(uint32_t index);
    MOZ_MUST_USE bool jsop_deffun(uint32_t index);
    MOZ_MUST_USE bool jsop_notearg();
    MOZ_MUST_USE bool jsop_throwsetconst();
    MOZ_MUST_USE bool jsop_checklexical();
    MOZ_MUST_USE bool jsop_checkaliasedlexical(EnvironmentCoordinate ec);
    MOZ_MUST_USE bool jsop_funcall(uint32_t argc);
    MOZ_MUST_USE bool jsop_funapply(uint32_t argc);
    MOZ_MUST_USE bool jsop_funapplyarguments(uint32_t argc);
    MOZ_MUST_USE bool jsop_funapplyarray(uint32_t argc);
    MOZ_MUST_USE bool jsop_call(uint32_t argc, bool constructing);
    MOZ_MUST_USE bool jsop_eval(uint32_t argc);
    MOZ_MUST_USE bool jsop_ifeq(JSOp op);
    MOZ_MUST_USE bool jsop_try();
    MOZ_MUST_USE bool jsop_label();
    MOZ_MUST_USE bool jsop_condswitch();
    MOZ_MUST_USE bool jsop_andor(JSOp op);
    MOZ_MUST_USE bool jsop_dup2();
    MOZ_MUST_USE bool jsop_loophead(jsbytecode* pc);
    MOZ_MUST_USE bool jsop_compare(JSOp op);
    MOZ_MUST_USE bool jsop_compare(JSOp op, MDefinition* left, MDefinition* right);
    MOZ_MUST_USE bool getStaticName(JSObject* staticObject, PropertyName* name, bool* psucceeded,
                                    MDefinition* lexicalCheck = nullptr);
    MOZ_MUST_USE bool loadStaticSlot(JSObject* staticObject, BarrierKind barrier,
                                     TemporaryTypeSet* types, uint32_t slot);
    MOZ_MUST_USE bool setStaticName(JSObject* staticObject, PropertyName* name);
    MOZ_MUST_USE bool jsop_getgname(PropertyName* name);
    MOZ_MUST_USE bool jsop_getname(PropertyName* name);
    MOZ_MUST_USE bool jsop_intrinsic(PropertyName* name);
    MOZ_MUST_USE bool jsop_getimport(PropertyName* name);
    MOZ_MUST_USE bool jsop_bindname(PropertyName* name);
    MOZ_MUST_USE bool jsop_bindvar();
    MOZ_MUST_USE bool jsop_getelem();
    MOZ_MUST_USE bool jsop_getelem_dense(MDefinition* obj, MDefinition* index,
                                         JSValueType unboxedType);
    MOZ_MUST_USE bool jsop_getelem_typed(MDefinition* obj, MDefinition* index,
                                         ScalarTypeDescr::Type arrayType);
    MOZ_MUST_USE bool jsop_setelem();
    MOZ_MUST_USE bool jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
                                         MDefinition* object, MDefinition* index,
                                         MDefinition* value, JSValueType unboxedType,
                                         bool writeHole, bool* emitted);
    MOZ_MUST_USE bool jsop_setelem_typed(ScalarTypeDescr::Type arrayType,
                                         MDefinition* object, MDefinition* index,
                                         MDefinition* value);
    MOZ_MUST_USE bool jsop_length();
    MOZ_MUST_USE bool jsop_length_fastPath();
    MOZ_MUST_USE bool jsop_arguments();
    MOZ_MUST_USE bool jsop_arguments_getelem();
    MOZ_MUST_USE bool jsop_runonce();
    MOZ_MUST_USE bool jsop_rest();
    MOZ_MUST_USE bool jsop_not();
    MOZ_MUST_USE bool jsop_getprop(PropertyName* name);
    MOZ_MUST_USE bool jsop_setprop(PropertyName* name);
    MOZ_MUST_USE bool jsop_delprop(PropertyName* name);
    MOZ_MUST_USE bool jsop_delelem();
    MOZ_MUST_USE bool jsop_newarray(uint32_t length);
    MOZ_MUST_USE bool jsop_newarray(JSObject* templateObject, uint32_t length);
    MOZ_MUST_USE bool jsop_newarray_copyonwrite();
    MOZ_MUST_USE bool jsop_newobject();
    MOZ_MUST_USE bool jsop_initelem();
    MOZ_MUST_USE bool jsop_initelem_array();
    MOZ_MUST_USE bool jsop_initelem_getter_setter();
    MOZ_MUST_USE bool jsop_mutateproto();
    MOZ_MUST_USE bool jsop_initprop(PropertyName* name);
    MOZ_MUST_USE bool jsop_initprop_getter_setter(PropertyName* name);
    MOZ_MUST_USE bool jsop_regexp(RegExpObject* reobj);
    MOZ_MUST_USE bool jsop_object(JSObject* obj);
    MOZ_MUST_USE bool jsop_lambda(JSFunction* fun);
    MOZ_MUST_USE bool jsop_lambda_arrow(JSFunction* fun);
    MOZ_MUST_USE bool jsop_functionthis();
    MOZ_MUST_USE bool jsop_globalthis();
    MOZ_MUST_USE bool jsop_typeof();
    MOZ_MUST_USE bool jsop_toasync();
    MOZ_MUST_USE bool jsop_toid();
    MOZ_MUST_USE bool jsop_iter(uint8_t flags);
    MOZ_MUST_USE bool jsop_itermore();
    MOZ_MUST_USE bool jsop_isnoiter();
    MOZ_MUST_USE bool jsop_iterend();
    MOZ_MUST_USE bool jsop_in();
    MOZ_MUST_USE bool jsop_instanceof();
    MOZ_MUST_USE bool jsop_getaliasedvar(EnvironmentCoordinate ec);
    MOZ_MUST_USE bool jsop_setaliasedvar(EnvironmentCoordinate ec);
    MOZ_MUST_USE bool jsop_debugger();
    MOZ_MUST_USE bool jsop_newtarget();
    MOZ_MUST_USE bool jsop_checkisobj(uint8_t kind);
    MOZ_MUST_USE bool jsop_checkobjcoercible();
    MOZ_MUST_USE bool jsop_pushcallobj();

    /* Inlining. */

    enum InliningStatus
    {
        InliningStatus_Error,
        InliningStatus_NotInlined,
        InliningStatus_WarmUpCountTooLow,
        InliningStatus_Inlined
    };

    enum InliningDecision
    {
        InliningDecision_Error,
        InliningDecision_Inline,
        InliningDecision_DontInline,
        InliningDecision_WarmUpCountTooLow
    };

    static InliningDecision DontInline(JSScript* targetScript, const char* reason);

    // Helper function for canInlineTarget
    bool hasCommonInliningPath(const JSScript* scriptToInline);

    // Oracles.
    InliningDecision canInlineTarget(JSFunction* target, CallInfo& callInfo);
    InliningDecision makeInliningDecision(JSObject* target, CallInfo& callInfo);
    MOZ_MUST_USE bool selectInliningTargets(const ObjectVector& targets, CallInfo& callInfo,
                                            BoolVector& choiceSet, uint32_t* numInlineable);

    // Native inlining helpers.
    // The typeset for the return value of our function.  These are
    // the types it's been observed returning in the past.
    TemporaryTypeSet* getInlineReturnTypeSet();
    // The known MIR type of getInlineReturnTypeSet.
    MIRType getInlineReturnType();

    // Array natives.
    InliningStatus inlineArray(CallInfo& callInfo);
    InliningStatus inlineArrayIsArray(CallInfo& callInfo);
    InliningStatus inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode);
    InliningStatus inlineArrayPush(CallInfo& callInfo);
    InliningStatus inlineArraySlice(CallInfo& callInfo);
    InliningStatus inlineArrayJoin(CallInfo& callInfo);
    InliningStatus inlineArraySplice(CallInfo& callInfo);

    // Math natives.
    InliningStatus inlineMathAbs(CallInfo& callInfo);
    InliningStatus inlineMathFloor(CallInfo& callInfo);
    InliningStatus inlineMathCeil(CallInfo& callInfo);
    InliningStatus inlineMathClz32(CallInfo& callInfo);
    InliningStatus inlineMathRound(CallInfo& callInfo);
    InliningStatus inlineMathSqrt(CallInfo& callInfo);
    InliningStatus inlineMathAtan2(CallInfo& callInfo);
    InliningStatus inlineMathHypot(CallInfo& callInfo);
    InliningStatus inlineMathMinMax(CallInfo& callInfo, bool max);
    InliningStatus inlineMathPow(CallInfo& callInfo);
    InliningStatus inlineMathRandom(CallInfo& callInfo);
    InliningStatus inlineMathImul(CallInfo& callInfo);
    InliningStatus inlineMathFRound(CallInfo& callInfo);
    InliningStatus inlineMathFunction(CallInfo& callInfo, MMathFunction::Function function);

    // String natives.
    InliningStatus inlineStringObject(CallInfo& callInfo);
    InliningStatus inlineStrCharCodeAt(CallInfo& callInfo);
    InliningStatus inlineConstantCharCodeAt(CallInfo& callInfo);
    InliningStatus inlineStrFromCharCode(CallInfo& callInfo);
    InliningStatus inlineStrFromCodePoint(CallInfo& callInfo);
    InliningStatus inlineStrCharAt(CallInfo& callInfo);

    // String intrinsics.
    InliningStatus inlineStringReplaceString(CallInfo& callInfo);
    InliningStatus inlineConstantStringSplitString(CallInfo& callInfo);
    InliningStatus inlineStringSplitString(CallInfo& callInfo);

    // RegExp intrinsics.
    InliningStatus inlineRegExpMatcher(CallInfo& callInfo);
    InliningStatus inlineRegExpSearcher(CallInfo& callInfo);
    InliningStatus inlineRegExpTester(CallInfo& callInfo);
    InliningStatus inlineIsRegExpObject(CallInfo& callInfo);
    InliningStatus inlineRegExpPrototypeOptimizable(CallInfo& callInfo);
    InliningStatus inlineRegExpInstanceOptimizable(CallInfo& callInfo);
    InliningStatus inlineGetFirstDollarIndex(CallInfo& callInfo);

    // Object natives and intrinsics.
    InliningStatus inlineObjectCreate(CallInfo& callInfo);
    InliningStatus inlineDefineDataProperty(CallInfo& callInfo);

    // Atomics natives.
    InliningStatus inlineAtomicsCompareExchange(CallInfo& callInfo);
    InliningStatus inlineAtomicsExchange(CallInfo& callInfo);
    InliningStatus inlineAtomicsLoad(CallInfo& callInfo);
    InliningStatus inlineAtomicsStore(CallInfo& callInfo);
    InliningStatus inlineAtomicsBinop(CallInfo& callInfo, InlinableNative target);
    InliningStatus inlineAtomicsIsLockFree(CallInfo& callInfo);

    // Slot intrinsics.
    InliningStatus inlineUnsafeSetReservedSlot(CallInfo& callInfo);
    InliningStatus inlineUnsafeGetReservedSlot(CallInfo& callInfo,
                                               MIRType knownValueType);

    // Map and Set intrinsics.
    InliningStatus inlineGetNextEntryForIterator(CallInfo& callInfo,
                                                 MGetNextEntryForIterator::Mode mode);

    // ArrayBuffer intrinsics.
    InliningStatus inlineArrayBufferByteLength(CallInfo& callInfo);
    InliningStatus inlinePossiblyWrappedArrayBufferByteLength(CallInfo& callInfo);

    // TypedArray intrinsics.
    enum WrappingBehavior { AllowWrappedTypedArrays, RejectWrappedTypedArrays };
    InliningStatus inlineTypedArray(CallInfo& callInfo, Native native);
    InliningStatus inlineIsTypedArrayHelper(CallInfo& callInfo, WrappingBehavior wrappingBehavior);
    InliningStatus inlineIsTypedArray(CallInfo& callInfo);
    InliningStatus inlineIsPossiblyWrappedTypedArray(CallInfo& callInfo);
    InliningStatus inlineTypedArrayLength(CallInfo& callInfo);
    InliningStatus inlinePossiblyWrappedTypedArrayLength(CallInfo& callInfo);
    InliningStatus inlineSetDisjointTypedElements(CallInfo& callInfo);

    // TypedObject intrinsics and natives.
    InliningStatus inlineObjectIsTypeDescr(CallInfo& callInfo);
    InliningStatus inlineSetTypedObjectOffset(CallInfo& callInfo);
    InliningStatus inlineConstructTypedObject(CallInfo& callInfo, TypeDescr* target);

    // SIMD intrinsics and natives.
    InliningStatus inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* target);

    // SIMD helpers.
    bool canInlineSimd(CallInfo& callInfo, JSNative native, unsigned numArgs,
                       InlineTypedObject** templateObj);
    MDefinition* unboxSimd(MDefinition* ins, SimdType type);
    IonBuilder::InliningStatus boxSimd(CallInfo& callInfo, MDefinition* ins,
                                       InlineTypedObject* templateObj);
    MDefinition* convertToBooleanSimdLane(MDefinition* scalar);

    InliningStatus inlineSimd(CallInfo& callInfo, JSFunction* target, SimdType type);

    InliningStatus inlineSimdBinaryArith(CallInfo& callInfo, JSNative native,
                                         MSimdBinaryArith::Operation op, SimdType type);
    InliningStatus inlineSimdBinaryBitwise(CallInfo& callInfo, JSNative native,
                                           MSimdBinaryBitwise::Operation op, SimdType type);
    InliningStatus inlineSimdBinarySaturating(CallInfo& callInfo, JSNative native,
                                              MSimdBinarySaturating::Operation op, SimdType type);
    InliningStatus inlineSimdShift(CallInfo& callInfo, JSNative native, MSimdShift::Operation op,
                                   SimdType type);
    InliningStatus inlineSimdComp(CallInfo& callInfo, JSNative native,
                                  MSimdBinaryComp::Operation op, SimdType type);
    InliningStatus inlineSimdUnary(CallInfo& callInfo, JSNative native,
                                   MSimdUnaryArith::Operation op, SimdType type);
    InliningStatus inlineSimdExtractLane(CallInfo& callInfo, JSNative native, SimdType type);
    InliningStatus inlineSimdReplaceLane(CallInfo& callInfo, JSNative native, SimdType type);
    InliningStatus inlineSimdSplat(CallInfo& callInfo, JSNative native, SimdType type);
    InliningStatus inlineSimdShuffle(CallInfo& callInfo, JSNative native, SimdType type,
                                     unsigned numVectors);
    InliningStatus inlineSimdCheck(CallInfo& callInfo, JSNative native, SimdType type);
    InliningStatus inlineSimdConvert(CallInfo& callInfo, JSNative native, bool isCast,
                                     SimdType from, SimdType to);
    InliningStatus inlineSimdSelect(CallInfo& callInfo, JSNative native, SimdType type);

    MOZ_MUST_USE bool prepareForSimdLoadStore(CallInfo& callInfo, Scalar::Type simdType,
                                              MInstruction** elements, MDefinition** index,
                                              Scalar::Type* arrayType);
    InliningStatus inlineSimdLoad(CallInfo& callInfo, JSNative native, SimdType type,
                                  unsigned numElems);
    InliningStatus inlineSimdStore(CallInfo& callInfo, JSNative native, SimdType type,
                                   unsigned numElems);

    InliningStatus inlineSimdAnyAllTrue(CallInfo& callInfo, bool IsAllTrue, JSNative native,
                                        SimdType type);

    // Utility intrinsics.
    InliningStatus inlineIsCallable(CallInfo& callInfo);
    InliningStatus inlineIsConstructor(CallInfo& callInfo);
    InliningStatus inlineIsObject(CallInfo& callInfo);
    InliningStatus inlineToObject(CallInfo& callInfo);
    InliningStatus inlineIsWrappedArrayConstructor(CallInfo& callInfo);
    InliningStatus inlineToInteger(CallInfo& callInfo);
    InliningStatus inlineToString(CallInfo& callInfo);
    InliningStatus inlineDump(CallInfo& callInfo);
    InliningStatus inlineHasClass(CallInfo& callInfo, const Class* clasp,
                                  const Class* clasp2 = nullptr,
                                  const Class* clasp3 = nullptr,
                                  const Class* clasp4 = nullptr);
    InliningStatus inlineIsConstructing(CallInfo& callInfo);
    InliningStatus inlineSubstringKernel(CallInfo& callInfo);
    InliningStatus inlineObjectHasPrototype(CallInfo& callInfo);

    // Testing functions.
    InliningStatus inlineBailout(CallInfo& callInfo);
    InliningStatus inlineAssertFloat32(CallInfo& callInfo);
    InliningStatus inlineAssertRecoveredOnBailout(CallInfo& callInfo);

    // Bind function.
    InliningStatus inlineBoundFunction(CallInfo& callInfo, JSFunction* target);

    // Main inlining functions
    InliningStatus inlineNativeCall(CallInfo& callInfo, JSFunction* target);
    InliningStatus inlineNativeGetter(CallInfo& callInfo, JSFunction* target);
    InliningStatus inlineNonFunctionCall(CallInfo& callInfo, JSObject* target);
    InliningStatus inlineScriptedCall(CallInfo& callInfo, JSFunction* target);
    InliningStatus inlineSingleCall(CallInfo& callInfo, JSObject* target);

    // Call functions
    InliningStatus inlineCallsite(const ObjectVector& targets, CallInfo& callInfo);
    MOZ_MUST_USE bool inlineCalls(CallInfo& callInfo, const ObjectVector& targets,
                                  BoolVector& choiceSet, MGetPropertyCache* maybeCache);

    // Inlining helpers.
    MOZ_MUST_USE bool inlineGenericFallback(JSFunction* target, CallInfo& callInfo,
                                            MBasicBlock* dispatchBlock);
    MOZ_MUST_USE bool inlineObjectGroupFallback(CallInfo& callInfo, MBasicBlock* dispatchBlock,
                                                MObjectGroupDispatch* dispatch,
                                                MGetPropertyCache* cache,
                                                MBasicBlock** fallbackTarget);

    enum AtomicCheckResult {
        DontCheckAtomicResult,
        DoCheckAtomicResult
    };

    MOZ_MUST_USE bool atomicsMeetsPreconditions(CallInfo& callInfo, Scalar::Type* arrayElementType,
                                                bool* requiresDynamicCheck,
                                                AtomicCheckResult checkResult=DoCheckAtomicResult);
    void atomicsCheckBounds(CallInfo& callInfo, MInstruction** elements, MDefinition** index);

    MOZ_MUST_USE bool testNeedsArgumentCheck(JSFunction* target, CallInfo& callInfo);

    MCall* makeCallHelper(JSFunction* target, CallInfo& callInfo);
    MOZ_MUST_USE bool makeCall(JSFunction* target, CallInfo& callInfo);

    MDefinition* patchInlinedReturn(CallInfo& callInfo, MBasicBlock* exit, MBasicBlock* bottom);
    MDefinition* patchInlinedReturns(CallInfo& callInfo, MIRGraphReturns& returns,
                                     MBasicBlock* bottom);
    MDefinition* specializeInlinedReturn(MDefinition* rdef, MBasicBlock* exit);

    MOZ_MUST_USE bool objectsHaveCommonPrototype(TemporaryTypeSet* types, PropertyName* name,
                                                 bool isGetter, JSObject* foundProto,
                                                 bool* guardGlobal);
    void freezePropertiesForCommonPrototype(TemporaryTypeSet* types, PropertyName* name,
                                            JSObject* foundProto, bool allowEmptyTypesForGlobal = false);
    /*
     * Callers must pass a non-null globalGuard if they pass a non-null globalShape.
     */
    MOZ_MUST_USE bool testCommonGetterSetter(TemporaryTypeSet* types, PropertyName* name,
                                             bool isGetter, JSObject* foundProto,
                                             Shape* lastProperty, JSFunction* getterOrSetter,
                                             MDefinition** guard, Shape* globalShape = nullptr,
                                             MDefinition** globalGuard = nullptr);
    MOZ_MUST_USE bool testShouldDOMCall(TypeSet* inTypes,
                                        JSFunction* func, JSJitInfo::OpType opType);

    MDefinition*
    addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape,
                                  const BaselineInspector::ReceiverVector& receivers,
                                  const BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
                                  bool isOwnProperty);

    MOZ_MUST_USE bool annotateGetPropertyCache(MDefinition* obj, PropertyName* name,
                                               MGetPropertyCache* getPropCache,
                                               TemporaryTypeSet* objTypes,
                                               TemporaryTypeSet* pushedTypes);

    MGetPropertyCache* getInlineableGetPropertyCache(CallInfo& callInfo);

    JSObject* testGlobalLexicalBinding(PropertyName* name);

    JSObject* testSingletonProperty(JSObject* obj, jsid id);
    JSObject* testSingletonPropertyTypes(MDefinition* obj, jsid id);

    ResultWithOOM<bool> testNotDefinedProperty(MDefinition* obj, jsid id);

    uint32_t getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed);
    MDefinition* convertUnboxedObjects(MDefinition* obj);
    MDefinition* convertUnboxedObjects(MDefinition* obj,
                                       const BaselineInspector::ObjectGroupVector& list);
    uint32_t getUnboxedOffset(TemporaryTypeSet* types, PropertyName* name,
                              JSValueType* punboxedType);
    MInstruction* loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
                                      BarrierKind barrier, TemporaryTypeSet* types);
    MInstruction* loadUnboxedValue(MDefinition* elements, size_t elementsOffset,
                                   MDefinition* scaledOffset, JSValueType unboxedType,
                                   BarrierKind barrier, TemporaryTypeSet* types);
    MInstruction* storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
                                       MDefinition* value);
    MInstruction* storeUnboxedValue(MDefinition* obj,
                                    MDefinition* elements, int32_t elementsOffset,
                                    MDefinition* scaledOffset, JSValueType unboxedType,
                                    MDefinition* value, bool preBarrier = true);
    MOZ_MUST_USE bool checkPreliminaryGroups(MDefinition *obj);
    MOZ_MUST_USE bool freezePropTypeSets(TemporaryTypeSet* types,
                                         JSObject* foundProto, PropertyName* name);
    bool canInlinePropertyOpShapes(const BaselineInspector::ReceiverVector& receivers);

    TemporaryTypeSet* bytecodeTypes(jsbytecode* pc);

    // Use one of the below methods for updating the current block, rather than
    // updating |current| directly. setCurrent() should only be used in cases
    // where the block cannot have phis whose type needs to be computed.

    MOZ_MUST_USE bool setCurrentAndSpecializePhis(MBasicBlock* block) {
        if (block) {
            if (!block->specializePhis(alloc()))
                return false;
        }
        setCurrent(block);
        return true;
    }

    void setCurrent(MBasicBlock* block) {
        current = block;
    }

    // A builder is inextricably tied to a particular script.
    JSScript* script_;

    // script->hasIonScript() at the start of the compilation. Used to avoid
    // calling hasIonScript() from background compilation threads.
    bool scriptHasIonScript_;

    // If off thread compilation is successful, the final code generator is
    // attached here. Code has been generated, but not linked (there is not yet
    // an IonScript). This is heap allocated, and must be explicitly destroyed,
    // performed by FinishOffThreadBuilder().
    CodeGenerator* backgroundCodegen_;

    // Some aborts are actionable (e.g., using an unsupported bytecode). When
    // optimization tracking is enabled, the location and message of the abort
    // are recorded here so they may be propagated to the script's
    // corresponding JitcodeGlobalEntry::BaselineEntry.
    JSScript* actionableAbortScript_;
    jsbytecode* actionableAbortPc_;
    const char* actionableAbortMessage_;

    MRootList* rootList_;

  public:
    void setRootList(MRootList& rootList) {
        MOZ_ASSERT(!rootList_);
        rootList_ = &rootList;
    }
    void clearForBackEnd();
    JSObject* checkNurseryObject(JSObject* obj);

    JSScript* script() const { return script_; }
    bool scriptHasIonScript() const { return scriptHasIonScript_; }

    CodeGenerator* backgroundCodegen() const { return backgroundCodegen_; }
    void setBackgroundCodegen(CodeGenerator* codegen) { backgroundCodegen_ = codegen; }

    CompilerConstraintList* constraints() {
        return constraints_;
    }

    bool isInlineBuilder() const {
        return callerBuilder_ != nullptr;
    }

    const JSAtomState& names() { return compartment->runtime()->names(); }

    bool hadActionableAbort() const {
        MOZ_ASSERT(!actionableAbortScript_ ||
                   (actionableAbortPc_ && actionableAbortMessage_));
        return actionableAbortScript_ != nullptr;
    }

    TraceLoggerThread *traceLogger() {
        // Currently ionbuilder only runs on the main thread.
        return TraceLoggerForMainThread(compartment->runtime()->mainThread()->runtimeFromMainThread());
    }

    void actionableAbortLocationAndMessage(JSScript** abortScript, jsbytecode** abortPc,
                                           const char** abortMessage)
    {
        MOZ_ASSERT(hadActionableAbort());
        *abortScript = actionableAbortScript_;
        *abortPc = actionableAbortPc_;
        *abortMessage = actionableAbortMessage_;
    }

    void trace(JSTracer* trc);

  private:
    MOZ_MUST_USE bool init();

    JSContext* analysisContext;
    BaselineFrameInspector* baselineFrame_;

    // Constraints for recording dependencies on type information.
    CompilerConstraintList* constraints_;

    // Basic analysis information about the script.
    BytecodeAnalysis analysis_;
    BytecodeAnalysis& analysis() {
        return analysis_;
    }

    TemporaryTypeSet* thisTypes;
    TemporaryTypeSet* argTypes;
    TemporaryTypeSet* typeArray;
    uint32_t typeArrayHint;
    uint32_t* bytecodeTypeMap;

    GSNCache gsn;
    EnvironmentCoordinateNameCache envCoordinateNameCache;

    jsbytecode* pc;
    MBasicBlock* current;
    uint32_t loopDepth_;

    Vector<BytecodeSite*, 0, JitAllocPolicy> trackedOptimizationSites_;

    BytecodeSite* bytecodeSite(jsbytecode* pc) {
        MOZ_ASSERT(info().inlineScriptTree()->script()->containsPC(pc));
        // See comment in maybeTrackedOptimizationSite.
        if (isOptimizationTrackingEnabled()) {
            if (BytecodeSite* site = maybeTrackedOptimizationSite(pc))
                return site;
        }
        return new(alloc()) BytecodeSite(info().inlineScriptTree(), pc);
    }

    BytecodeSite* maybeTrackedOptimizationSite(jsbytecode* pc);

    MDefinition* lexicalCheck_;

    void setLexicalCheck(MDefinition* lexical) {
        MOZ_ASSERT(!lexicalCheck_);
        lexicalCheck_ = lexical;
    }
    MDefinition* takeLexicalCheck() {
        MDefinition* lexical = lexicalCheck_;
        lexicalCheck_ = nullptr;
        return lexical;
    }

    /* Information used for inline-call builders. */
    MResumePoint* callerResumePoint_;
    jsbytecode* callerPC() {
        return callerResumePoint_ ? callerResumePoint_->pc() : nullptr;
    }
    IonBuilder* callerBuilder_;

    IonBuilder* outermostBuilder();

    struct LoopHeader {
        jsbytecode* pc;
        MBasicBlock* header;

        LoopHeader(jsbytecode* pc, MBasicBlock* header)
          : pc(pc), header(header)
        {}
    };

    Vector<CFGState, 8, JitAllocPolicy> cfgStack_;
    Vector<ControlFlowInfo, 4, JitAllocPolicy> loops_;
    Vector<ControlFlowInfo, 0, JitAllocPolicy> switches_;
    Vector<ControlFlowInfo, 2, JitAllocPolicy> labels_;
    Vector<MInstruction*, 2, JitAllocPolicy> iterators_;
    Vector<LoopHeader, 0, JitAllocPolicy> loopHeaders_;
    BaselineInspector* inspector;

    size_t inliningDepth_;

    // Total bytecode length of all inlined scripts. Only tracked for the
    // outermost builder.
    size_t inlinedBytecodeLength_;

    // Cutoff to disable compilation if excessive time is spent reanalyzing
    // loop bodies to compute a fixpoint of the types for loop variables.
    static const size_t MAX_LOOP_RESTARTS = 40;
    size_t numLoopRestarts_;

    // True if script->failedBoundsCheck is set for the current script or
    // an outer script.
    bool failedBoundsCheck_;

    // True if script->failedShapeGuard is set for the current script or
    // an outer script.
    bool failedShapeGuard_;

    // True if script->failedLexicalCheck_ is set for the current script or
    // an outer script.
    bool failedLexicalCheck_;

    // Has an iterator other than 'for in'.
    bool nonStringIteration_;

    // If this script can use a lazy arguments object, it will be pre-created
    // here.
    MInstruction* lazyArguments_;

    // If this is an inline builder, the call info for the builder.
    const CallInfo* inlineCallInfo_;

    // When compiling a call with multiple targets, we are first creating a
    // MGetPropertyCache.  This MGetPropertyCache is following the bytecode, and
    // is used to recover the JSFunction.  In some cases, the Type of the object
    // which own the property is enough for dispatching to the right function.
    // In such cases we do not have read the property, except when the type
    // object is unknown.
    //
    // As an optimization, we can dispatch a call based on the object group,
    // without doing the MGetPropertyCache.  This is what is achieved by
    // |IonBuilder::inlineCalls|.  As we might not know all the functions, we
    // are adding a fallback path, where this MGetPropertyCache would be moved
    // into.
    //
    // In order to build the fallback path, we have to capture a resume point
    // ahead, for the potential fallback path.  This resume point is captured
    // while building MGetPropertyCache.  It is capturing the state of Baseline
    // before the execution of the MGetPropertyCache, such as we can safely do
    // it in the fallback path.
    //
    // This field is used to discard the resume point if it is not used for
    // building a fallback path.

    // Discard the prior resume point while setting a new MGetPropertyCache.
    void replaceMaybeFallbackFunctionGetter(MGetPropertyCache* cache);

    // Discard the MGetPropertyCache if it is handled by WrapMGetPropertyCache.
    void keepFallbackFunctionGetter(MGetPropertyCache* cache) {
        if (cache == maybeFallbackFunctionGetter_)
            maybeFallbackFunctionGetter_ = nullptr;
    }

    MGetPropertyCache* maybeFallbackFunctionGetter_;

    // Used in tracking outcomes of optimization strategies for devtools.
    void startTrackingOptimizations();

    // The track* methods below are called often. Do not combine them with the
    // unchecked variants, despite the unchecked variants having no other
    // callers.
    void trackTypeInfo(JS::TrackedTypeSite site, MIRType mirType,
                       TemporaryTypeSet* typeSet)
    {
        if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
            trackTypeInfoUnchecked(site, mirType, typeSet);
    }
    void trackTypeInfo(JS::TrackedTypeSite site, JSObject* obj) {
        if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
            trackTypeInfoUnchecked(site, obj);
    }
    void trackTypeInfo(CallInfo& callInfo) {
        if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
            trackTypeInfoUnchecked(callInfo);
    }
    void trackOptimizationAttempt(JS::TrackedStrategy strategy) {
        if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
            trackOptimizationAttemptUnchecked(strategy);
    }
    void amendOptimizationAttempt(uint32_t index) {
        if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
            amendOptimizationAttemptUnchecked(index);
    }
    void trackOptimizationOutcome(JS::TrackedOutcome outcome) {
        if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
            trackOptimizationOutcomeUnchecked(outcome);
    }
    void trackOptimizationSuccess() {
        if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
            trackOptimizationSuccessUnchecked();
    }
    void trackInlineSuccess(InliningStatus status = InliningStatus_Inlined) {
        if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
            trackInlineSuccessUnchecked(status);
    }

    bool forceInlineCaches() {
        return MOZ_UNLIKELY(JitOptions.forceInlineCaches);
    }

    // Out-of-line variants that don't check if optimization tracking is
    // enabled.
    void trackTypeInfoUnchecked(JS::TrackedTypeSite site, MIRType mirType,
                                TemporaryTypeSet* typeSet);
    void trackTypeInfoUnchecked(JS::TrackedTypeSite site, JSObject* obj);
    void trackTypeInfoUnchecked(CallInfo& callInfo);
    void trackOptimizationAttemptUnchecked(JS::TrackedStrategy strategy);
    void amendOptimizationAttemptUnchecked(uint32_t index);
    void trackOptimizationOutcomeUnchecked(JS::TrackedOutcome outcome);
    void trackOptimizationSuccessUnchecked();
    void trackInlineSuccessUnchecked(InliningStatus status);
};

class CallInfo
{
    MDefinition* fun_;
    MDefinition* thisArg_;
    MDefinition* newTargetArg_;
    MDefinitionVector args_;

    bool constructing_;
    bool setter_;

  public:
    CallInfo(TempAllocator& alloc, bool constructing)
      : fun_(nullptr),
        thisArg_(nullptr),
        newTargetArg_(nullptr),
        args_(alloc),
        constructing_(constructing),
        setter_(false)
    { }

    MOZ_MUST_USE bool init(CallInfo& callInfo) {
        MOZ_ASSERT(constructing_ == callInfo.constructing());

        fun_ = callInfo.fun();
        thisArg_ = callInfo.thisArg();

        if (constructing())
            newTargetArg_ = callInfo.getNewTarget();

        if (!args_.appendAll(callInfo.argv()))
            return false;

        return true;
    }

    MOZ_MUST_USE bool init(MBasicBlock* current, uint32_t argc) {
        MOZ_ASSERT(args_.empty());

        // Get the arguments in the right order
        if (!args_.reserve(argc))
            return false;

        if (constructing())
            setNewTarget(current->pop());

        for (int32_t i = argc; i > 0; i--)
            args_.infallibleAppend(current->peek(-i));
        current->popn(argc);

        // Get |this| and |fun|
        setThis(current->pop());
        setFun(current->pop());

        return true;
    }

    void popFormals(MBasicBlock* current) {
        current->popn(numFormals());
    }

    void pushFormals(MBasicBlock* current) {
        current->push(fun());
        current->push(thisArg());

        for (uint32_t i = 0; i < argc(); i++)
            current->push(getArg(i));

        if (constructing())
            current->push(getNewTarget());
    }

    uint32_t argc() const {
        return args_.length();
    }
    uint32_t numFormals() const {
        return argc() + 2 + constructing();
    }

    MOZ_MUST_USE bool setArgs(const MDefinitionVector& args) {
        MOZ_ASSERT(args_.empty());
        return args_.appendAll(args);
    }

    MDefinitionVector& argv() {
        return args_;
    }

    const MDefinitionVector& argv() const {
        return args_;
    }

    MDefinition* getArg(uint32_t i) const {
        MOZ_ASSERT(i < argc());
        return args_[i];
    }

    MDefinition* getArgWithDefault(uint32_t i, MDefinition* defaultValue) const {
        if (i < argc())
            return args_[i];

        return defaultValue;
    }

    void setArg(uint32_t i, MDefinition* def) {
        MOZ_ASSERT(i < argc());
        args_[i] = def;
    }

    MDefinition* thisArg() const {
        MOZ_ASSERT(thisArg_);
        return thisArg_;
    }

    void setThis(MDefinition* thisArg) {
        thisArg_ = thisArg;
    }

    bool constructing() const {
        return constructing_;
    }

    void setNewTarget(MDefinition* newTarget) {
        MOZ_ASSERT(constructing());
        newTargetArg_ = newTarget;
    }
    MDefinition* getNewTarget() const {
        MOZ_ASSERT(newTargetArg_);
        return newTargetArg_;
    }

    bool isSetter() const {
        return setter_;
    }
    void markAsSetter() {
        setter_ = true;
    }

    MDefinition* fun() const {
        MOZ_ASSERT(fun_);
        return fun_;
    }

    void setFun(MDefinition* fun) {
        fun_ = fun;
    }

    void setImplicitlyUsedUnchecked() {
        fun_->setImplicitlyUsedUnchecked();
        thisArg_->setImplicitlyUsedUnchecked();
        if (newTargetArg_)
            newTargetArg_->setImplicitlyUsedUnchecked();
        for (uint32_t i = 0; i < argc(); i++)
            getArg(i)->setImplicitlyUsedUnchecked();
    }
};

bool NeedsPostBarrier(MDefinition* value);

} // namespace jit
} // namespace js

#endif /* jit_IonBuilder_h */