Bug 991720 part 2 - Add a spaghetti stack of stores on resume points. r=h4writer
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Fri, 19 Dec 2014 15:28:31 +0100
changeset 220606 69c510e5fea23d19e1ba5112a4f2927171672e68
parent 220605 bc4542ef1182a65698d7d9a8a037e084f298ad1d
child 220607 f5497ebe2735a639bdd1c135260e9c55338c7015
push id10503
push userryanvm@gmail.com
push dateFri, 19 Dec 2014 20:13:42 +0000
treeherderfx-team@98ee95ac6be5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersh4writer
bugs991720
milestone37.0a1
Bug 991720 part 2 - Add a spaghetti stack of stores on resume points. r=h4writer
js/src/jit/IonBuilder.cpp
js/src/jit/LIR.cpp
js/src/jit/LoopUnroller.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/MIRGraph.cpp
js/src/jit/MIRGraph.h
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -446,17 +446,17 @@ IonBuilder::analyzeNewLoopTypes(MBasicBl
     for (size_t i = 0; i < loopHeaders_.length(); i++) {
         if (loopHeaders_[i].pc == start) {
             MBasicBlock *oldEntry = loopHeaders_[i].header;
 
             // If this block has been discarded, its resume points will have
             // already discarded their operands.
             if (!oldEntry->isDead()) {
                 MResumePoint *oldEntryRp = oldEntry->entryResumePoint();
-                size_t stackDepth = oldEntryRp->numOperands();
+                size_t stackDepth = oldEntryRp->stackDepth();
                 for (size_t slot = 0; slot < stackDepth; slot++) {
                     MDefinition *oldDef = oldEntryRp->getOperand(slot);
                     if (!oldDef->isPhi()) {
                         MOZ_ASSERT(oldDef->block()->id() < oldEntry->id());
                         MOZ_ASSERT(oldDef == entry->getSlot(slot));
                         continue;
                     }
                     MPhi *oldPhi = oldDef->toPhi();
@@ -896,21 +896,21 @@ IonBuilder::buildInline(IonBuilder *call
     if (!initScopeChain(callInfo.fun()))
         return false;
 
     JitSpew(JitSpew_Inlining, "Initializing %u local slots; fixed lexicals begin at %u",
             info().nlocals(), info().fixedLexicalBegin());
 
     initLocals();
 
-    JitSpew(JitSpew_Inlining, "Inline entry block MResumePoint %p, %u operands",
-            (void *) current->entryResumePoint(), current->entryResumePoint()->numOperands());
+    JitSpew(JitSpew_Inlining, "Inline entry block MResumePoint %p, %u stack slots",
+            (void *) current->entryResumePoint(), current->entryResumePoint()->stackDepth());
 
     // +2 for the scope chain and |this|, maybe another +1 for arguments object slot.
-    MOZ_ASSERT(current->entryResumePoint()->numOperands() == info().totalSlots());
+    MOZ_ASSERT(current->entryResumePoint()->stackDepth() == info().totalSlots());
 
     if (script_->argumentsHasVarBinding()) {
         lazyArguments_ = MConstant::New(alloc(), MagicValue(JS_OPTIMIZED_ARGUMENTS));
         current->add(lazyArguments_);
     }
 
     insertRecompileCheck();
 
@@ -1203,17 +1203,17 @@ IonBuilder::maybeAddOsrTypeBarriers()
     }
 
     MBasicBlock *preheader = osrBlock->getSuccessor(0);
     MBasicBlock *header = preheader->getSuccessor(0);
     static const size_t OSR_PHI_POSITION = 1;
     MOZ_ASSERT(preheader->getPredecessor(OSR_PHI_POSITION) == osrBlock);
 
     MResumePoint *headerRp = header->entryResumePoint();
-    size_t stackDepth = headerRp->numOperands();
+    size_t stackDepth = headerRp->stackDepth();
     MOZ_ASSERT(stackDepth == osrBlock->stackDepth());
     for (uint32_t slot = info().startArgSlot(); slot < stackDepth; slot++) {
         // Aliased slots are never accessed, since they need to go through
         // the callobject. The typebarriers are added there and can be
         // discarded here.
         if (info().isSlotAliasedAtOsr(slot))
             continue;
 
@@ -4905,17 +4905,17 @@ IonBuilder::inlineTypeObjectFallback(Cal
         return false;
 
     // Capture stack prior to the call operation. This captures the function.
     MResumePoint *preCallResumePoint =
         MResumePoint::New(alloc(), dispatchBlock, pc, callerResumePoint_, MResumePoint::ResumeAt);
     if (!preCallResumePoint)
         return false;
 
-    DebugOnly<size_t> preCallFuncIndex = preCallResumePoint->numOperands() - callInfo.numFormals();
+    DebugOnly<size_t> preCallFuncIndex = preCallResumePoint->stackDepth() - callInfo.numFormals();
     MOZ_ASSERT(preCallResumePoint->getOperand(preCallFuncIndex) == fallbackInfo.fun());
 
     // In the dispatch block, replace the function's slot entry with Undefined.
     MConstant *undefined = MConstant::New(alloc(), UndefinedValue());
     dispatchBlock->add(undefined);
     dispatchBlock->rewriteAtDepth(-int(callInfo.numFormals()), undefined);
 
     // Construct a block that does nothing but remove formals from the stack.
@@ -5070,17 +5070,17 @@ IonBuilder::inlineCalls(CallInfo &callIn
             return false;
 
         // Create a function MConstant to use in the entry ResumePoint.
         MConstant *funcDef = MConstant::New(alloc(), ObjectValue(*target), constraints());
         funcDef->setImplicitlyUsedUnchecked();
         dispatchBlock->add(funcDef);
 
         // Use the MConstant in the inline resume point and on stack.
-        int funIndex = inlineBlock->entryResumePoint()->numOperands() - callInfo.numFormals();
+        int funIndex = inlineBlock->entryResumePoint()->stackDepth() - callInfo.numFormals();
         inlineBlock->entryResumePoint()->replaceOperand(funIndex, funcDef);
         inlineBlock->rewriteSlot(funIndex, funcDef);
 
         // Create a new CallInfo to track modified state within the inline block.
         CallInfo inlineInfo(alloc(), callInfo.constructing());
         if (!inlineInfo.init(callInfo))
             return false;
         inlineInfo.popFormals(inlineBlock);
--- a/js/src/jit/LIR.cpp
+++ b/js/src/jit/LIR.cpp
@@ -218,24 +218,31 @@ LRecoverInfo::appendOperands(MNode *ins)
     return true;
 }
 
 bool
 LRecoverInfo::appendDefinition(MDefinition *def)
 {
     MOZ_ASSERT(def->isRecoveredOnBailout());
     def->setInWorklist();
+
     if (!appendOperands(def))
         return false;
     return instructions_.append(def);
 }
 
 bool
 LRecoverInfo::appendResumePoint(MResumePoint *rp)
 {
+    // Stores should be recovered first.
+    for (auto iter(rp->storesBegin()), end(rp->storesEnd()); iter != end; ++iter) {
+        if (!appendDefinition(iter->operand))
+            return false;
+    }
+
     if (rp->caller() && !appendResumePoint(rp->caller()))
         return false;
 
     if (!appendOperands(rp))
         return false;
 
     return instructions_.append(rp);
 }
--- a/js/src/jit/LoopUnroller.cpp
+++ b/js/src/jit/LoopUnroller.cpp
@@ -96,24 +96,24 @@ LoopUnroller::makeReplacementInstruction
         clone->setResumePoint(rp);
     }
 }
 
 MResumePoint *
 LoopUnroller::makeReplacementResumePoint(MBasicBlock *block, MResumePoint *rp)
 {
     MDefinitionVector inputs(alloc);
-    for (size_t i = 0; i < rp->stackDepth(); i++) {
+    for (size_t i = 0; i < rp->numOperands(); i++) {
         MDefinition *old = rp->getOperand(i);
         MDefinition *replacement = old->isUnused() ? old : getReplacementDefinition(old);
         if (!inputs.append(replacement))
             CrashAtUnhandlableOOM("LoopUnroller::makeReplacementResumePoint");
     }
 
-    MResumePoint *clone = MResumePoint::New(alloc, block, rp->pc(), rp->caller(), rp->mode(), inputs);
+    MResumePoint *clone = MResumePoint::New(alloc, block, rp, inputs);
     if (!clone)
         CrashAtUnhandlableOOM("LoopUnroller::makeReplacementResumePoint");
 
     return clone;
 }
 
 void
 LoopUnroller::go(LoopIterationBound *bound)
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2717,68 +2717,99 @@ MResumePoint::New(TempAllocator &alloc, 
     MResumePoint *resume = new(alloc) MResumePoint(block, pc, parent, mode);
     if (!resume->init(alloc))
         return nullptr;
     resume->inherit(block);
     return resume;
 }
 
 MResumePoint *
-MResumePoint::New(TempAllocator &alloc, MBasicBlock *block, jsbytecode *pc, MResumePoint *parent,
-                  Mode mode, const MDefinitionVector &operands)
-{
-    MResumePoint *resume = new(alloc) MResumePoint(block, pc, parent, mode);
-
-    if (!resume->operands_.init(alloc, operands.length()))
+MResumePoint::New(TempAllocator &alloc, MBasicBlock *block, MResumePoint *model,
+                  const MDefinitionVector &operands)
+{
+    MResumePoint *resume = new(alloc) MResumePoint(block, model->pc(), model->caller(), model->mode());
+
+    // Allocate the same number of operands as the original resume point, and
+    // copy operands from the operands vector and not the not from the current
+    // block stack.
+    if (!resume->operands_.init(alloc, model->numAllocatedOperands()))
         return nullptr;
+
+    // Copy the operands.
     for (size_t i = 0; i < operands.length(); i++)
         resume->initOperand(i, operands[i]);
 
     return resume;
 }
 
 MResumePoint *
 MResumePoint::Copy(TempAllocator &alloc, MResumePoint *src)
 {
     MResumePoint *resume = new(alloc) MResumePoint(src->block(), src->pc(),
                                                    src->caller(), src->mode());
     // Copy the operands from the original resume point, and not from the
     // current block stack.
-    if (!resume->operands_.init(alloc, src->stackDepth()))
+    if (!resume->operands_.init(alloc, src->numAllocatedOperands()))
         return nullptr;
-    for (size_t i = 0; i < resume->stackDepth(); i++)
+
+    // Copy the operands.
+    for (size_t i = 0; i < resume->numOperands(); i++)
         resume->initOperand(i, src->getOperand(i));
     return resume;
 }
 
 MResumePoint::MResumePoint(MBasicBlock *block, jsbytecode *pc, MResumePoint *caller,
                            Mode mode)
   : MNode(block),
     pc_(pc),
     caller_(caller),
     instruction_(nullptr),
     mode_(mode)
 {
     block->addResumePoint(this);
 }
 
-bool MResumePoint::init(TempAllocator &alloc)
+bool
+MResumePoint::init(TempAllocator &alloc)
 {
     return operands_.init(alloc, block()->stackDepth());
 }
 
 void
 MResumePoint::inherit(MBasicBlock *block)
 {
     // FixedList doesn't initialize its elements, so do unchecked inits.
     for (size_t i = 0; i < stackDepth(); i++)
         initOperand(i, block->getSlot(i));
 }
 
 void
+MResumePoint::addStore(TempAllocator &alloc, MDefinition *store, const MResumePoint *cache)
+{
+    MOZ_ASSERT(block()->outerResumePoint() != this);
+    MOZ_ASSERT_IF(cache, !cache->stores_.empty());
+
+    if (cache && cache->stores_.begin()->operand == store) {
+        // If the last resume point had the same side-effect stack, then we can
+        // reuse the current side effect without cloning it. This is a simple
+        // way to share common context by making a spaghetti stack.
+        if (++cache->stores_.begin() == stores_.begin()) {
+            stores_.copy(cache->stores_);
+            return;
+        }
+    }
+
+    // Ensure that the store would not be deleted by DCE.
+    MOZ_ASSERT(store->isEffectful());
+
+    MStoreToRecover *top = new(alloc) MStoreToRecover(store);
+    stores_.push(top);
+}
+
+void
 MResumePoint::dump(FILE *fp) const
 {
     fprintf(fp, "resumepoint mode=");
 
     switch (mode()) {
       case MResumePoint::ResumeAt:
         fprintf(fp, "At");
         break;
@@ -3415,16 +3446,17 @@ MObjectState::MObjectState(MDefinition *
     numFixedSlots_ = templateObject->numFixedSlots();
 }
 
 bool
 MObjectState::init(TempAllocator &alloc, MDefinition *obj)
 {
     if (!MVariadicInstruction::init(alloc, numSlots() + 1))
         return false;
+    // +1, for the Object.
     initOperand(0, obj);
     return true;
 }
 
 MObjectState *
 MObjectState::New(TempAllocator &alloc, MDefinition *obj, MDefinition *undefinedVal)
 {
     MObjectState *res = new(alloc) MObjectState(obj);
@@ -3442,16 +3474,61 @@ MObjectState::Copy(TempAllocator &alloc,
     MObjectState *res = new(alloc) MObjectState(obj);
     if (!res || !res->init(alloc, obj))
         return nullptr;
     for (size_t i = 0; i < res->numSlots(); i++)
         res->initSlot(i, state->getSlot(i));
     return res;
 }
 
+MArrayState::MArrayState(MDefinition *arr)
+{
+    // This instruction is only used as a summary for bailout paths.
+    setResultType(MIRType_Object);
+    setRecoveredOnBailout();
+    numElements_ = arr->toNewArray()->count();
+}
+
+bool
+MArrayState::init(TempAllocator &alloc, MDefinition *obj, MDefinition *len)
+{
+    if (!MVariadicInstruction::init(alloc, numElements() + 2))
+        return false;
+    // +1, for the Array object.
+    initOperand(0, obj);
+    // +1, for the length value of the array.
+    initOperand(1, len);
+    return true;
+}
+
+MArrayState *
+MArrayState::New(TempAllocator &alloc, MDefinition *arr, MDefinition *undefinedVal,
+                 MDefinition *initLength)
+{
+    MArrayState *res = new(alloc) MArrayState(arr);
+    if (!res || !res->init(alloc, arr, initLength))
+        return nullptr;
+    for (size_t i = 0; i < res->numElements(); i++)
+        res->initElement(i, undefinedVal);
+    return res;
+}
+
+MArrayState *
+MArrayState::Copy(TempAllocator &alloc, MArrayState *state)
+{
+    MDefinition *arr = state->array();
+    MDefinition *len = state->initializedLength();
+    MArrayState *res = new(alloc) MArrayState(arr);
+    if (!res || !res->init(alloc, arr, len))
+        return nullptr;
+    for (size_t i = 0; i < res->numElements(); i++)
+        res->initElement(i, state->getElement(i));
+    return res;
+}
+
 bool
 MNewArray::shouldUseVM() const
 {
     MOZ_ASSERT(count() < NativeObject::NELEMENTS_LIMIT);
 
     size_t arraySlots =
         gc::GetGCKindSlots(templateObject()->asTenured().getAllocKind()) - ObjectElements::VALUES_PER_HEADER;
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -2994,61 +2994,30 @@ class MObjectState
 // lowered and is not used to generate code.
 class MArrayState
   : public MVariadicInstruction,
     public NoFloatPolicyAfter<2>::Data
 {
   private:
     uint32_t numElements_;
 
-    explicit MArrayState(MDefinition *arr)
-    {
-        // This instruction is only used as a summary for bailout paths.
-        setResultType(MIRType_Object);
-        setRecoveredOnBailout();
-        numElements_ = arr->toNewArray()->count();
-    }
-
-    bool init(TempAllocator &alloc, MDefinition *obj, MDefinition *len) {
-        if (!MVariadicInstruction::init(alloc, numElements() + 2))
-            return false;
-        initOperand(0, obj);
-        initOperand(1, len);
-        return true;
-    }
+    explicit MArrayState(MDefinition *arr);
+
+    bool init(TempAllocator &alloc, MDefinition *obj, MDefinition *len);
 
     void initElement(uint32_t index, MDefinition *def) {
         initOperand(index + 2, def);
     }
 
   public:
     INSTRUCTION_HEADER(ArrayState)
 
     static MArrayState *New(TempAllocator &alloc, MDefinition *arr, MDefinition *undefinedVal,
-                            MDefinition *initLength)
-    {
-        MArrayState *res = new(alloc) MArrayState(arr);
-        if (!res || !res->init(alloc, arr, initLength))
-            return nullptr;
-        for (size_t i = 0; i < res->numElements(); i++)
-            res->initElement(i, undefinedVal);
-        return res;
-    }
-
-    static MArrayState *Copy(TempAllocator &alloc, MArrayState *state)
-    {
-        MDefinition *arr = state->array();
-        MDefinition *len = state->initializedLength();
-        MArrayState *res = new(alloc) MArrayState(arr);
-        if (!res || !res->init(alloc, arr, len))
-            return nullptr;
-        for (size_t i = 0; i < res->numElements(); i++)
-            res->initElement(i, state->getElement(i));
-        return res;
-    }
+                            MDefinition *initLength);
+    static MArrayState *Copy(TempAllocator &alloc, MArrayState *state);
 
     MDefinition *array() const {
         return getOperand(0);
     }
 
     MDefinition *initializedLength() const {
         return getOperand(1);
     }
@@ -11690,16 +11659,29 @@ class MNewDenseArrayPar : public MBinary
         return true;
     }
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
+// This is an element of a spaghetti stack which is used to represent the memory
+// context which has to be restored in case of a bailout.
+struct MStoreToRecover : public TempObject, public InlineSpaghettiStackNode<MStoreToRecover>
+{
+    MDefinition *operand;
+
+    explicit MStoreToRecover(MDefinition *operand)
+      : operand(operand)
+    { }
+};
+
+typedef InlineSpaghettiStack<MStoreToRecover> MStoresToRecoverList;
+
 // A resume point contains the information needed to reconstruct the Baseline
 // state from a position in the JIT. See the big comment near resumeAfter() in
 // IonBuilder.cpp.
 class MResumePoint MOZ_FINAL :
   public MNode
 #ifdef DEBUG
   , public InlineForwardListNode<MResumePoint>
 #endif
@@ -11710,17 +11692,24 @@ class MResumePoint MOZ_FINAL :
         ResumeAfter, // Resume after the current instruction
         Outer        // State before inlining.
     };
 
   private:
     friend class MBasicBlock;
     friend void AssertBasicGraphCoherency(MIRGraph &graph);
 
+    // List of stack slots needed to reconstruct the frame corresponding to the
+    // function which is compiled by IonBuilder.
     FixedList<MUse> operands_;
+
+    // List of stores needed to reconstruct the content of objects which are
+    // emulated by EmulateStateOf variants.
+    MStoresToRecoverList stores_;
+
     jsbytecode *pc_;
     MResumePoint *caller_;
     MInstruction *instruction_;
     Mode mode_;
 
     MResumePoint(MBasicBlock *block, jsbytecode *pc, MResumePoint *parent, Mode mode);
     void inherit(MBasicBlock *state);
 
@@ -11739,26 +11728,31 @@ class MResumePoint MOZ_FINAL :
     }
     const MUse *getUseFor(size_t index) const {
         return &operands_[index];
     }
 
   public:
     static MResumePoint *New(TempAllocator &alloc, MBasicBlock *block, jsbytecode *pc,
                              MResumePoint *parent, Mode mode);
-    static MResumePoint *New(TempAllocator &alloc, MBasicBlock *block, jsbytecode *pc,
-                             MResumePoint *parent, Mode mode,
+    static MResumePoint *New(TempAllocator &alloc, MBasicBlock *block, MResumePoint *model,
                              const MDefinitionVector &operands);
     static MResumePoint *Copy(TempAllocator &alloc, MResumePoint *src);
 
     MNode::Kind kind() const {
         return MNode::ResumePoint;
     }
+    size_t numAllocatedOperands() const {
+        return operands_.length();
+    }
+    uint32_t stackDepth() const {
+        return numAllocatedOperands();
+    }
     size_t numOperands() const {
-        return operands_.length();
+        return numAllocatedOperands();
     }
     size_t indexOf(const MUse *u) const MOZ_FINAL MOZ_OVERRIDE {
         MOZ_ASSERT(u >= &operands_[0]);
         MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
         return u - &operands_[0];
     }
     void initOperand(size_t index, MDefinition *operand) {
         // FixedList doesn't initialize its elements, so do an unchecked init.
@@ -11773,19 +11767,16 @@ class MResumePoint MOZ_FINAL :
     bool isRecoverableOperand(MUse *u) const;
 
     MDefinition *getOperand(size_t index) const {
         return operands_[index].producer();
     }
     jsbytecode *pc() const {
         return pc_;
     }
-    uint32_t stackDepth() const {
-        return operands_.length();
-    }
     MResumePoint *caller() const {
         return caller_;
     }
     void setCaller(MResumePoint *caller) {
         caller_ = caller;
     }
     uint32_t frameCount() const {
         uint32_t count = 1;
@@ -11809,24 +11800,37 @@ class MResumePoint MOZ_FINAL :
         MOZ_ASSERT(instruction_);
         instruction_ = nullptr;
     }
     Mode mode() const {
         return mode_;
     }
 
     void releaseUses() {
-        for (size_t i = 0; i < operands_.length(); i++) {
+        for (size_t i = 0, e = numOperands(); i < e; i++) {
             if (operands_[i].hasProducer())
                 operands_[i].releaseProducer();
         }
     }
 
     bool writeRecoverData(CompactBufferWriter &writer) const;
 
+    // Register a store instruction on the current resume point. This
+    // instruction would be recovered when we are bailing out. The |cache|
+    // argument can be any resume point, it is used to share memory if we are
+    // doing the same modification.
+    void addStore(TempAllocator &alloc, MDefinition *store, const MResumePoint *cache = nullptr);
+
+    MStoresToRecoverList::iterator storesBegin() const {
+        return stores_.begin();
+    }
+    MStoresToRecoverList::iterator storesEnd() const {
+        return stores_.end();
+    }
+
     virtual void dump(FILE *fp) const;
     virtual void dump() const;
 };
 
 class MIsCallable
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -434,17 +434,18 @@ MBasicBlock::inherit(TempAllocator &allo
 
     MOZ_ASSERT(info_.nslots() >= stackPosition_);
     MOZ_ASSERT(!entryResumePoint_);
 
     // Propagate the caller resume point from the inherited block.
     MResumePoint *callerResumePoint = pred ? pred->callerResumePoint() : nullptr;
 
     // Create a resume point using our initial stack state.
-    entryResumePoint_ = new(alloc) MResumePoint(this, pc(), callerResumePoint, MResumePoint::ResumeAt);
+    entryResumePoint_ = new(alloc) MResumePoint(this, pc(), callerResumePoint,
+                                                MResumePoint::ResumeAt);
     if (!entryResumePoint_->init(alloc))
         return false;
 
     if (pred) {
         if (!predecessors_.append(pred))
             return false;
 
         if (kind_ == PENDING_LOOP_HEADER) {
@@ -491,17 +492,17 @@ MBasicBlock::inherit(TempAllocator &allo
 
     return true;
 }
 
 bool
 MBasicBlock::inheritResumePoint(MBasicBlock *pred)
 {
     // Copy slots from the resume point.
-    stackPosition_ = entryResumePoint_->numOperands();
+    stackPosition_ = entryResumePoint_->stackDepth();
     for (uint32_t i = 0; i < stackPosition_; i++)
         slots_[i] = entryResumePoint_->getOperand(i);
 
     MOZ_ASSERT(info_.nslots() >= stackPosition_);
     MOZ_ASSERT(kind_ != PENDING_LOOP_HEADER);
     MOZ_ASSERT(pred != nullptr);
 
     if (!predecessors_.append(pred))
@@ -1399,17 +1400,17 @@ MBasicBlock::removePredecessor(MBasicBlo
     // operands have been removed.
     removePredecessorWithoutPhiOperands(pred, predIndex);
 }
 
 void
 MBasicBlock::inheritPhis(MBasicBlock *header)
 {
     MResumePoint *headerRp = header->entryResumePoint();
-    size_t stackDepth = headerRp->numOperands();
+    size_t stackDepth = headerRp->stackDepth();
     for (size_t slot = 0; slot < stackDepth; slot++) {
         MDefinition *exitDef = getSlot(slot);
         MDefinition *loopDef = headerRp->getOperand(slot);
         if (loopDef->block() != header) {
             MOZ_ASSERT(loopDef->block()->id() < header->id());
             MOZ_ASSERT(loopDef == exitDef);
             continue;
         }
@@ -1432,17 +1433,17 @@ MBasicBlock::inheritPhis(MBasicBlock *he
 }
 
 bool
 MBasicBlock::inheritPhisFromBackedge(MBasicBlock *backedge, bool *hadTypeChange)
 {
     // We must be a pending loop header
     MOZ_ASSERT(kind_ == PENDING_LOOP_HEADER);
 
-    size_t stackDepth = entryResumePoint()->numOperands();
+    size_t stackDepth = entryResumePoint()->stackDepth();
     for (size_t slot = 0; slot < stackDepth; slot++) {
         // Get the value stack-slot of the back edge.
         MDefinition *exitDef = backedge->getSlot(slot);
 
         // Get the value of the loop header.
         MDefinition *loopDef = entryResumePoint()->getOperand(slot);
         if (loopDef->block() != this) {
             // If we are finishing a pending loop header, then we need to ensure
--- a/js/src/jit/MIRGraph.h
+++ b/js/src/jit/MIRGraph.h
@@ -548,17 +548,17 @@ class MBasicBlock : public TempObject, p
     }
     MResumePoint *callerResumePoint() {
         return entryResumePoint() ? entryResumePoint()->caller() : nullptr;
     }
     void setCallerResumePoint(MResumePoint *caller) {
         entryResumePoint()->setCaller(caller);
     }
     size_t numEntrySlots() const {
-        return entryResumePoint()->numOperands();
+        return entryResumePoint()->stackDepth();
     }
     MDefinition *getEntrySlot(size_t i) const {
         MOZ_ASSERT(i < numEntrySlots());
         return entryResumePoint()->getOperand(i);
     }
 
     LBlock *lir() const {
         return lir_;