Bug 1414228 - Allocate type monitor fallback stubs lazily instead of allocating them in BaselineCompiler. r=tcampbell
authorJan de Mooij <jdemooij@mozilla.com>
Mon, 06 Nov 2017 14:10:23 +0100
changeset 443567 21fe8fa6b8e6f873442ae21db2fe06ffe1fd0d33
parent 443566 4d757e71f90e93b2700c9c9999c96dbfe94b59b7
child 443568 7c79d729d27e09f09a2e614d27897e4976959822
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcampbell
bugs1414228
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1414228 - Allocate type monitor fallback stubs lazily instead of allocating them in BaselineCompiler. r=tcampbell
js/src/jit/BaselineBailouts.cpp
js/src/jit/BaselineCacheIRCompiler.cpp
js/src/jit/BaselineDebugModeOSR.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineIC.h
js/src/jit/BaselineJIT.cpp
js/src/jit/Ion.cpp
js/src/jit/SharedIC.cpp
js/src/jit/SharedIC.h
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1022,17 +1022,17 @@ InitFromBailout(JSContext* cx, HandleScr
             pc = GetNextNonLoopEntryPc(pc);
             fasterPc = GetNextNonLoopEntryPc(GetNextNonLoopEntryPc(fasterPc));
             if (fasterPc == pc)
                 break;
         }
         op = JSOp(*pc);
     }
 
-    uint32_t pcOff = script->pcToOffset(pc);
+    const uint32_t pcOff = script->pcToOffset(pc);
     BaselineScript* baselineScript = script->baselineScript();
 
 #ifdef DEBUG
     uint32_t expectedDepth;
     bool reachablePC;
     if (!ReconstructStackDepth(cx, script, resumeAfter ? GetNextPc(pc) : pc, &expectedDepth, &reachablePC))
         return false;
 
@@ -1102,18 +1102,23 @@ InitFromBailout(JSContext* cx, HandleScr
         builder.setResumePC(pc);
         builder.setResumeFramePtr(prevFramePtr);
 
         if (enterMonitorChain) {
             BaselineICEntry& icEntry = baselineScript->icEntryFromPCOffset(pcOff);
             ICFallbackStub* fallbackStub = icEntry.firstStub()->getChainFallback();
             MOZ_ASSERT(fallbackStub->isMonitoredFallback());
             JitSpew(JitSpew_BaselineBailouts, "      [TYPE-MONITOR CHAIN]");
-            ICMonitoredFallbackStub* monFallbackStub = fallbackStub->toMonitoredFallbackStub();
-            ICStub* firstMonStub = monFallbackStub->fallbackMonitorStub()->firstMonitorStub();
+
+            ICTypeMonitor_Fallback* typeMonitorFallback =
+                fallbackStub->toMonitoredFallbackStub()->getFallbackMonitorStub(cx, script);
+            if (!typeMonitorFallback)
+                return false;
+
+            ICStub* firstMonStub = typeMonitorFallback->firstMonitorStub();
 
             // To enter a monitoring chain, we load the top stack value into R0
             JitSpew(JitSpew_BaselineBailouts, "      Popping top stack value into R0.");
             builder.popValueInto(PCMappingSlotInfo::SlotInR0);
             frameSize -= sizeof(Value);
 
             if (JSOp(*pc) == JSOP_GETELEM_SUPER) {
                 // Push a fake value so that the stack stays balanced.
@@ -1382,16 +1387,24 @@ InitFromBailout(JSContext* cx, HandleScr
     if (!builder.writePtr(CalleeToToken(calleeFun, pushedNewTarget), "CalleeToken"))
         return false;
     nextCallee.set(calleeFun);
 
     // Push BaselineStub frame descriptor
     if (!builder.writeWord(baselineStubFrameDescr, "Descriptor"))
         return false;
 
+    // Ensure we have a TypeMonitor fallback stub so we don't crash in JIT code
+    // when we try to enter it. See callers of offsetOfFallbackMonitorStub.
+    if (CodeSpec[*pc].format & JOF_TYPESET) {
+        ICFallbackStub* fallbackStub = icEntry.firstStub()->getChainFallback();
+        if (!fallbackStub->toMonitoredFallbackStub()->getFallbackMonitorStub(cx, script))
+            return false;
+    }
+
     // Push return address into ICCall_Scripted stub, immediately after the call.
     void* baselineCallReturnAddr = GetStubReturnAddress(cx, pc);
     MOZ_ASSERT(baselineCallReturnAddr);
     if (!builder.writePtr(baselineCallReturnAddr, "ReturnAddr"))
         return false;
     MOZ_ASSERT(builder.framePushed() % JitStackAlignment == 0);
 
     // If actualArgc >= fun->nargs, then we are done.  Otherwise, we need to push on
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -2257,18 +2257,23 @@ js::jit::AttachBaselineCacheIRStub(JSCon
       case BaselineCacheIRStubKind::Regular: {
         auto newStub = new(newStubMem) ICCacheIR_Regular(code, stubInfo);
         writer.copyStubData(newStub->stubDataStart());
         stub->addNewStub(newStub);
         *attached = true;
         return newStub;
       }
       case BaselineCacheIRStubKind::Monitored: {
-        ICStub* monitorStub =
-            stub->toMonitoredFallbackStub()->fallbackMonitorStub()->firstMonitorStub();
+        ICTypeMonitor_Fallback* typeMonitorFallback =
+            stub->toMonitoredFallbackStub()->getFallbackMonitorStub(cx, outerScript);
+        if (!typeMonitorFallback) {
+            cx->recoverFromOutOfMemory();
+            return nullptr;
+        }
+        ICStub* monitorStub = typeMonitorFallback->firstMonitorStub();
         auto newStub = new(newStubMem) ICCacheIR_Monitored(code, monitorStub, stubInfo);
         writer.copyStubData(newStub->stubDataStart());
         stub->addNewStub(newStub);
         *attached = true;
         return newStub;
       }
       case BaselineCacheIRStubKind::Updated: {
         auto newStub = new(newStubMem) ICCacheIR_Updated(code, stubInfo);
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -752,17 +752,20 @@ CloneOldBaselineStub(JSContext* cx, Debu
         }
     }
 
     // Some stubs are monitored, get the first stub in the monitor chain from
     // the new fallback stub if so.
     ICStub* firstMonitorStub;
     if (fallbackStub->isMonitoredFallback()) {
         ICMonitoredFallbackStub* monitored = fallbackStub->toMonitoredFallbackStub();
-        firstMonitorStub = monitored->fallbackMonitorStub()->firstMonitorStub();
+        ICTypeMonitor_Fallback* fallback = monitored->getFallbackMonitorStub(cx, entry.script);
+        if (!fallback)
+            return false;
+        firstMonitorStub = fallback->firstMonitorStub();
     } else {
         firstMonitorStub = nullptr;
     }
     ICStubSpace* stubSpace = ICStubCompiler::StubSpaceForStub(oldStub->makesGCCalls(), entry.script,
                                                               ICStubCompiler::Engine::Baseline);
 
     // Clone the existing stub into the recompiled IC.
     //
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1836,34 +1836,35 @@ ICSetProp_Fallback::Compiler::postGenera
 }
 
 //
 // Call_Fallback
 //
 
 static bool
 TryAttachFunApplyStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsbytecode* pc,
-                      HandleValue thisv, uint32_t argc, Value* argv, bool* attached)
+                      HandleValue thisv, uint32_t argc, Value* argv,
+                      ICTypeMonitor_Fallback* typeMonitorFallback, bool* attached)
 {
     if (argc != 2)
         return true;
 
     if (!thisv.isObject() || !thisv.toObject().is<JSFunction>())
         return true;
     RootedFunction target(cx, &thisv.toObject().as<JSFunction>());
 
     bool isScripted = target->hasJITCode();
 
     // right now, only handle situation where second argument is |arguments|
     if (argv[1].isMagic(JS_OPTIMIZED_ARGUMENTS) && !script->needsArgsObj()) {
         if (isScripted && !stub->hasStub(ICStub::Call_ScriptedApplyArguments)) {
             JitSpew(JitSpew_BaselineIC, "  Generating Call_ScriptedApplyArguments stub");
 
             ICCall_ScriptedApplyArguments::Compiler compiler(
-                cx, stub->fallbackMonitorStub()->firstMonitorStub(), script->pcToOffset(pc));
+                cx, typeMonitorFallback->firstMonitorStub(), script->pcToOffset(pc));
             ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
             if (!newStub)
                 return false;
 
             stub->addNewStub(newStub);
             *attached = true;
             return true;
         }
@@ -1871,49 +1872,50 @@ TryAttachFunApplyStub(JSContext* cx, ICC
         // TODO: handle FUNAPPLY for native targets.
     }
 
     if (argv[1].isObject() && argv[1].toObject().is<ArrayObject>()) {
         if (isScripted && !stub->hasStub(ICStub::Call_ScriptedApplyArray)) {
             JitSpew(JitSpew_BaselineIC, "  Generating Call_ScriptedApplyArray stub");
 
             ICCall_ScriptedApplyArray::Compiler compiler(
-                cx, stub->fallbackMonitorStub()->firstMonitorStub(), script->pcToOffset(pc));
+                cx, typeMonitorFallback->firstMonitorStub(), script->pcToOffset(pc));
             ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
             if (!newStub)
                 return false;
 
             stub->addNewStub(newStub);
             *attached = true;
             return true;
         }
     }
     return true;
 }
 
 static bool
 TryAttachFunCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsbytecode* pc,
-                     HandleValue thisv, bool* attached)
+                     HandleValue thisv, ICTypeMonitor_Fallback* typeMonitorFallback,
+                     bool* attached)
 {
     // Try to attach a stub for Function.prototype.call with scripted |this|.
 
     *attached = false;
     if (!thisv.isObject() || !thisv.toObject().is<JSFunction>())
         return true;
     RootedFunction target(cx, &thisv.toObject().as<JSFunction>());
 
     // Attach a stub if the script can be Baseline-compiled. We do this also
     // if the script is not yet compiled to avoid attaching a CallNative stub
     // that handles everything, even after the callee becomes hot.
     if (target->hasScript() && target->nonLazyScript()->canBaselineCompile() &&
         !stub->hasStub(ICStub::Call_ScriptedFunCall))
     {
         JitSpew(JitSpew_BaselineIC, "  Generating Call_ScriptedFunCall stub");
 
-        ICCall_ScriptedFunCall::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+        ICCall_ScriptedFunCall::Compiler compiler(cx, typeMonitorFallback->firstMonitorStub(),
                                                   script->pcToOffset(pc));
         ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
         *attached = true;
         stub->addNewStub(newStub);
         return true;
@@ -2150,31 +2152,35 @@ TryAttachCallStub(JSContext* cx, ICCall_
     if (stub->numOptimizedStubs() == 0 && IsOptimizableConstStringSplit(callee, argc, vp + 2))
         return true;
 
     stub->unlinkStubsWithKind(cx, ICStub::Call_ConstStringSplit);
 
     if (!callee.isObject())
         return true;
 
+    ICTypeMonitor_Fallback* typeMonitorFallback = stub->getFallbackMonitorStub(cx, script);
+    if (!typeMonitorFallback)
+        return false;
+
     RootedObject obj(cx, &callee.toObject());
     if (!obj->is<JSFunction>()) {
         // Try to attach a stub for a call/construct hook on the object.
         // Ignore proxies, which are special cased by callHook/constructHook.
         if (obj->is<ProxyObject>())
             return true;
         if (JSNative hook = constructing ? obj->constructHook() : obj->callHook()) {
             if (op != JSOP_FUNAPPLY && !isSpread && !createSingleton) {
                 RootedObject templateObject(cx);
                 CallArgs args = CallArgsFromVp(argc, vp);
                 if (!GetTemplateObjectForClassHook(cx, hook, args, &templateObject))
                     return false;
 
                 JitSpew(JitSpew_BaselineIC, "  Generating Call_ClassHook stub");
-                ICCall_ClassHook::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+                ICCall_ClassHook::Compiler compiler(cx, typeMonitorFallback->firstMonitorStub(),
                                                     obj->getClass(), hook, templateObject,
                                                     script->pcToOffset(pc), constructing);
                 ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
                 if (!newStub)
                     return false;
 
                 stub->addNewStub(newStub);
                 *handled = true;
@@ -2212,17 +2218,17 @@ TryAttachCallStub(JSContext* cx, ICCall_
             JitSpew(JitSpew_BaselineIC, "  Chain already has generalized scripted call stub!");
             return true;
         }
 
         if (stub->scriptedStubCount() >= ICCall_Fallback::MAX_SCRIPTED_STUBS) {
             // Create a Call_AnyScripted stub.
             JitSpew(JitSpew_BaselineIC, "  Generating Call_AnyScripted stub (cons=%s, spread=%s)",
                     constructing ? "yes" : "no", isSpread ? "yes" : "no");
-            ICCallScriptedCompiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+            ICCallScriptedCompiler compiler(cx, typeMonitorFallback->firstMonitorStub(),
                                             constructing, isSpread, script->pcToOffset(pc));
             ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
             if (!newStub)
                 return false;
 
             // Before adding new stub, unlink all previous Call_Scripted.
             stub->unlinkStubsWithKind(cx, ICStub::Call_Scripted);
 
@@ -2281,17 +2287,17 @@ TryAttachCallStub(JSContext* cx, ICCall_
             if (thisObject->is<PlainObject>() || thisObject->is<UnboxedPlainObject>())
                 templateObject = thisObject;
         }
 
         JitSpew(JitSpew_BaselineIC,
                 "  Generating Call_Scripted stub (fun=%p, %s:%zu, cons=%s, spread=%s)",
                 fun.get(), fun->nonLazyScript()->filename(), fun->nonLazyScript()->lineno(),
                 constructing ? "yes" : "no", isSpread ? "yes" : "no");
-        ICCallScriptedCompiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+        ICCallScriptedCompiler compiler(cx, typeMonitorFallback->firstMonitorStub(),
                                         fun, templateObject,
                                         constructing, isSpread, script->pcToOffset(pc));
         ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
         stub->addNewStub(newStub);
         *handled = true;
@@ -2299,26 +2305,28 @@ TryAttachCallStub(JSContext* cx, ICCall_
     }
 
     if (fun->isNative() && (!constructing || (constructing && fun->isConstructor()))) {
         // Generalized native call stubs are not here yet!
         MOZ_ASSERT(!stub->nativeStubsAreGeneralized());
 
         // Check for JSOP_FUNAPPLY
         if (op == JSOP_FUNAPPLY) {
-            if (fun->native() == fun_apply)
-                return TryAttachFunApplyStub(cx, stub, script, pc, thisv, argc, vp + 2, handled);
+            if (fun->native() == fun_apply) {
+                return TryAttachFunApplyStub(cx, stub, script, pc, thisv, argc, vp + 2,
+                                             typeMonitorFallback, handled);
+            }
 
             // Don't try to attach a "regular" optimized call stubs for FUNAPPLY ops,
             // since MagicArguments may escape through them.
             return true;
         }
 
         if (op == JSOP_FUNCALL && fun->native() == fun_call) {
-            if (!TryAttachFunCallStub(cx, stub, script, pc, thisv, handled))
+            if (!TryAttachFunCallStub(cx, stub, script, pc, thisv, typeMonitorFallback, handled))
                 return false;
             if (*handled)
                 return true;
         }
 
         if (stub->nativeStubCount() >= ICCall_Fallback::MAX_NATIVE_STUBS) {
             JitSpew(JitSpew_BaselineIC,
                     "  Too many Call_Native stubs. TODO: add Call_AnyNative!");
@@ -2357,17 +2365,17 @@ TryAttachCallStub(JSContext* cx, ICCall_
         bool ignoresReturnValue = false;
         if (op == JSOP_CALL_IGNORES_RV && fun->isNative()) {
             const JSJitInfo* jitInfo = fun->jitInfo();
             ignoresReturnValue = jitInfo && jitInfo->type() == JSJitInfo::IgnoresReturnValueNative;
         }
 
         JitSpew(JitSpew_BaselineIC, "  Generating Call_Native stub (fun=%p, cons=%s, spread=%s)",
                 fun.get(), constructing ? "yes" : "no", isSpread ? "yes" : "no");
-        ICCall_Native::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+        ICCall_Native::Compiler compiler(cx, typeMonitorFallback->firstMonitorStub(),
                                          fun, templateObject, constructing, ignoresReturnValue,
                                          isSpread, script->pcToOffset(pc));
         ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
         stub->addNewStub(newStub);
         *handled = true;
@@ -2425,17 +2433,21 @@ TryAttachConstStringSplit(JSContext* cx,
     for (uint32_t i = 0; i < initLength; i++) {
         JSAtom* str = js::AtomizeString(cx, arrObj->getDenseElement(i).toString());
         if (!str)
             return false;
 
         arrObj->setDenseElementWithType(cx, i, StringValue(str));
     }
 
-    ICCall_ConstStringSplit::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+    ICTypeMonitor_Fallback* typeMonitorFallback = stub->getFallbackMonitorStub(cx, script);
+    if (!typeMonitorFallback)
+        return false;
+
+    ICCall_ConstStringSplit::Compiler compiler(cx, typeMonitorFallback->firstMonitorStub(),
                                                script->pcToOffset(pc), str, sep, arrObj);
     ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
     if (!newStub)
         return false;
 
     stub->addNewStub(newStub);
     *attached = true;
     return true;
@@ -3022,17 +3034,19 @@ ICCall_Fallback::Compiler::generateStubC
         masm.assumeUnreachable("Failed to return object in constructing call.");
 #endif
         masm.bind(&skipThisReplace);
     }
 
     // At this point, ICStubReg points to the ICCall_Fallback stub, which is NOT
     // a MonitoredStub, but rather a MonitoredFallbackStub.  To use EmitEnterTypeMonitorIC,
     // first load the ICTypeMonitor_Fallback stub into ICStubReg.  Then, use
-    // EmitEnterTypeMonitorIC with a custom struct offset.
+    // EmitEnterTypeMonitorIC with a custom struct offset. Note that we must
+    // have a non-null fallbackMonitorStub here because InitFromBailout
+    // delazifies.
     masm.loadPtr(Address(ICStubReg, ICMonitoredFallbackStub::offsetOfFallbackMonitorStub()),
                  ICStubReg);
     EmitEnterTypeMonitorIC(masm, ICTypeMonitor_Fallback::offsetOfFirstMonitorStub());
 
     return true;
 }
 
 void
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -426,22 +426,17 @@ class ICGetElem_Fallback : public ICMoni
 
       public:
         explicit Compiler(JSContext* cx, bool hasReceiver = false)
           : ICStubCompiler(cx, ICStub::GetElem_Fallback, Engine::Baseline),
             hasReceiver_(hasReceiver)
         { }
 
         ICStub* getStub(ICStubSpace* space) {
-            ICGetElem_Fallback* stub = newStub<ICGetElem_Fallback>(space, getStubCode());
-            if (!stub)
-                return nullptr;
-            if (!stub->initMonitoringChain(cx, space))
-                return nullptr;
-            return stub;
+            return newStub<ICGetElem_Fallback>(space, getStubCode());
         }
     };
 };
 
 // SetElem
 //      JSOP_SETELEM
 //      JSOP_INITELEM
 
@@ -557,20 +552,17 @@ class ICGetName_Fallback : public ICMoni
         MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
 
       public:
         explicit Compiler(JSContext* cx)
           : ICStubCompiler(cx, ICStub::GetName_Fallback, Engine::Baseline)
         { }
 
         ICStub* getStub(ICStubSpace* space) {
-            ICGetName_Fallback* stub = newStub<ICGetName_Fallback>(space, getStubCode());
-            if (!stub || !stub->initMonitoringChain(cx, space))
-                return nullptr;
-            return stub;
+            return newStub<ICGetName_Fallback>(space, getStubCode());
         }
     };
 };
 
 // BindName
 //      JSOP_BINDNAME
 class ICBindName_Fallback : public ICFallbackStub
 {
@@ -612,21 +604,17 @@ class ICGetIntrinsic_Fallback : public I
         MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
 
       public:
         explicit Compiler(JSContext* cx)
           : ICStubCompiler(cx, ICStub::GetIntrinsic_Fallback, Engine::Baseline)
         { }
 
         ICStub* getStub(ICStubSpace* space) {
-            ICGetIntrinsic_Fallback* stub =
-                newStub<ICGetIntrinsic_Fallback>(space, getStubCode());
-            if (!stub || !stub->initMonitoringChain(cx, space))
-                return nullptr;
-            return stub;
+            return newStub<ICGetIntrinsic_Fallback>(space, getStubCode());
         }
     };
 };
 
 // Stub that loads the constant result of a GETINTRINSIC operation.
 class ICGetIntrinsic_Constant : public ICStub
 {
     friend class ICStubSpace;
@@ -794,20 +782,17 @@ class ICCall_Fallback : public ICMonitor
       public:
         Compiler(JSContext* cx, bool isConstructing, bool isSpread)
           : ICCallStubCompiler(cx, ICStub::Call_Fallback),
             isConstructing_(isConstructing),
             isSpread_(isSpread)
         { }
 
         ICStub* getStub(ICStubSpace* space) {
-            ICCall_Fallback* stub = newStub<ICCall_Fallback>(space, getStubCode());
-            if (!stub || !stub->initMonitoringChain(cx, space))
-                return nullptr;
-            return stub;
+            return newStub<ICCall_Fallback>(space, getStubCode());
         }
     };
 };
 
 class ICCall_Scripted : public ICMonitoredStub
 {
     friend class ICStubSpace;
   public:
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -1074,18 +1074,19 @@ BaselineScript::purgeOptimizedStubs(Zone
                 prev = stub;
                 stub = stub->next();
             }
 
             if (lastStub->isMonitoredFallback()) {
                 // Monitor stubs can't make calls, so are always in the
                 // optimized stub space.
                 ICTypeMonitor_Fallback* lastMonStub =
-                    lastStub->toMonitoredFallbackStub()->fallbackMonitorStub();
-                lastMonStub->resetMonitorStubChain(zone);
+                    lastStub->toMonitoredFallbackStub()->maybeFallbackMonitorStub();
+                if (lastMonStub)
+                    lastMonStub->resetMonitorStubChain(zone);
             }
         } else if (lastStub->isTypeMonitor_Fallback()) {
             lastStub->toTypeMonitor_Fallback()->resetMonitorStubChain(zone);
         } else {
             MOZ_ASSERT(lastStub->isTableSwitch());
         }
     }
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1250,24 +1250,18 @@ IonScript::purgeOptimizedStubs(Zone* zon
                 }
 
                 prev = stub;
                 stub = stub->next();
             }
 
             lastStub->toFallbackStub()->setInvalid();
 
-            if (lastStub->isMonitoredFallback()) {
-                // Monitor stubs can't make calls, so are always in the
-                // optimized stub space.
-                ICTypeMonitor_Fallback* lastMonStub =
-                    lastStub->toMonitoredFallbackStub()->fallbackMonitorStub();
-                lastMonStub->resetMonitorStubChain(zone);
-                lastMonStub->setInvalid();
-            }
+            MOZ_ASSERT(!lastStub->isMonitoredFallback(),
+                       "None of the shared stubs used in Ion are monitored");
         } else if (lastStub->isTypeMonitor_Fallback()) {
             lastStub->toTypeMonitor_Fallback()->resetMonitorStubChain(zone);
             lastStub->toTypeMonitor_Fallback()->setInvalid();
         } else {
             MOZ_ASSERT(lastStub->isTableSwitch());
         }
     }
 
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -221,20 +221,26 @@ ICStub::trace(JSTracer* trc)
 {
     traceCode(trc, "shared-stub-jitcode");
 
     // If the stub is a monitored fallback stub, then trace the monitor ICs hanging
     // off of that stub.  We don't need to worry about the regular monitored stubs,
     // because the regular monitored stubs will always have a monitored fallback stub
     // that references the same stub chain.
     if (isMonitoredFallback()) {
-        ICTypeMonitor_Fallback* lastMonStub = toMonitoredFallbackStub()->fallbackMonitorStub();
-        for (ICStubConstIterator iter(lastMonStub->firstMonitorStub()); !iter.atEnd(); iter++) {
-            MOZ_ASSERT_IF(iter->next() == nullptr, *iter == lastMonStub);
-            iter->trace(trc);
+        ICTypeMonitor_Fallback* lastMonStub =
+            toMonitoredFallbackStub()->maybeFallbackMonitorStub();
+        if (lastMonStub) {
+            for (ICStubConstIterator iter(lastMonStub->firstMonitorStub());
+                 !iter.atEnd();
+                 iter++)
+            {
+                MOZ_ASSERT_IF(iter->next() == nullptr, *iter == lastMonStub);
+                iter->trace(trc);
+            }
         }
     }
 
     if (isUpdated()) {
         for (ICStubConstIterator iter(toUpdatedStub()->firstUpdateStub()); !iter.atEnd(); iter++) {
             MOZ_ASSERT_IF(iter->next() == nullptr, iter->isTypeUpdate_Fallback());
             iter->trace(trc);
         }
@@ -361,17 +367,19 @@ ICFallbackStub::unlinkStub(Zone* zone, I
         stub->trace(zone->barrierTracer());
     }
 
     if (stub->makesGCCalls() && stub->isMonitored()) {
         // This stub can make calls so we can return to it if it's on the stack.
         // We just have to reset its firstMonitorStub_ field to avoid a stale
         // pointer when purgeOptimizedStubs destroys all optimized monitor
         // stubs (unlinked stubs won't be updated).
-        ICTypeMonitor_Fallback* monitorFallback = toMonitoredFallbackStub()->fallbackMonitorStub();
+        ICTypeMonitor_Fallback* monitorFallback =
+            toMonitoredFallbackStub()->maybeFallbackMonitorStub();
+        MOZ_ASSERT(monitorFallback);
         stub->toMonitoredStub()->resetFirstMonitorStub(monitorFallback);
     }
 
 #ifdef DEBUG
     // Poison stub code to ensure we don't call this stub again. However, if
     // this stub can make calls, a pointer to it may be stored in a stub frame
     // on the stack, so we can't touch the stubCode_ or GC will crash when
     // tracing this pointer.
@@ -452,33 +460,37 @@ ICMonitoredStub::ICMonitoredStub(Kind ki
     // If the first monitored stub is a ICTypeMonitor_Fallback stub, then
     // double check that _its_ firstMonitorStub is the same as this one.
     MOZ_ASSERT_IF(firstMonitorStub_->isTypeMonitor_Fallback(),
                   firstMonitorStub_->toTypeMonitor_Fallback()->firstMonitorStub() ==
                      firstMonitorStub_);
 }
 
 bool
-ICMonitoredFallbackStub::initMonitoringChain(JSContext* cx, ICStubSpace* space)
+ICMonitoredFallbackStub::initMonitoringChain(JSContext* cx, JSScript* script)
 {
     MOZ_ASSERT(fallbackMonitorStub_ == nullptr);
 
     ICTypeMonitor_Fallback::Compiler compiler(cx, this);
+    ICStubSpace* space = script->baselineScript()->fallbackStubSpace();
     ICTypeMonitor_Fallback* stub = compiler.getStub(space);
     if (!stub)
         return false;
     fallbackMonitorStub_ = stub;
     return true;
 }
 
 bool
 ICMonitoredFallbackStub::addMonitorStubForValue(JSContext* cx, BaselineFrame* frame,
                                                 StackTypeSet* types, HandleValue val)
 {
-    return fallbackMonitorStub_->addMonitorStubForValue(cx, frame, types, val);
+    ICTypeMonitor_Fallback* typeMonitorFallback = getFallbackMonitorStub(cx, frame->script());
+    if (!typeMonitorFallback)
+        return false;
+    return typeMonitorFallback->addMonitorStubForValue(cx, frame, types, val);
 }
 
 bool
 ICUpdatedStub::initUpdatingChain(JSContext* cx, ICStubSpace* space)
 {
     MOZ_ASSERT(firstUpdateStub_ == nullptr);
 
     ICTypeUpdate_Fallback::Compiler compiler(cx);
@@ -2157,17 +2169,18 @@ ICGetProp_Fallback::Compiler::generateSt
     // will point here.
     assumeStubFrame(masm);
     bailoutReturnOffset_.bind(masm.currentOffset());
 
     leaveStubFrame(masm, true);
 
     // When we get here, ICStubReg contains the ICGetProp_Fallback stub,
     // which we can't use to enter the TypeMonitor IC, because it's a MonitoredFallbackStub
-    // instead of a MonitoredStub. So, we cheat.
+    // instead of a MonitoredStub. So, we cheat. Note that we must have a
+    // non-null fallbackMonitorStub here because InitFromBailout delazifies.
     masm.loadPtr(Address(ICStubReg, ICMonitoredFallbackStub::offsetOfFallbackMonitorStub()),
                  ICStubReg);
     EmitEnterTypeMonitorIC(masm, ICTypeMonitor_Fallback::offsetOfFirstMonitorStub());
 
     return true;
 }
 
 void
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -1196,29 +1196,36 @@ class SharedStubInfo
         return icEntry_;
     }
 };
 
 // Monitored fallback stubs - as the name implies.
 class ICMonitoredFallbackStub : public ICFallbackStub
 {
   protected:
-    // Pointer to the fallback monitor stub.
+    // Pointer to the fallback monitor stub. Created lazily by
+    // getFallbackMonitorStub if needed.
     ICTypeMonitor_Fallback* fallbackMonitorStub_;
 
     ICMonitoredFallbackStub(Kind kind, JitCode* stubCode)
       : ICFallbackStub(kind, ICStub::MonitoredFallback, stubCode),
         fallbackMonitorStub_(nullptr) {}
 
   public:
-    MOZ_MUST_USE bool initMonitoringChain(JSContext* cx, ICStubSpace* space);
+    MOZ_MUST_USE bool initMonitoringChain(JSContext* cx, JSScript* script);
     MOZ_MUST_USE bool addMonitorStubForValue(JSContext* cx, BaselineFrame* frame,
                                              StackTypeSet* types, HandleValue val);
 
-    inline ICTypeMonitor_Fallback* fallbackMonitorStub() const {
+    ICTypeMonitor_Fallback* maybeFallbackMonitorStub() const {
+        return fallbackMonitorStub_;
+    }
+    ICTypeMonitor_Fallback* getFallbackMonitorStub(JSContext* cx, JSScript* script) {
+        if (!fallbackMonitorStub_ && !initMonitoringChain(cx, script))
+            return nullptr;
+        MOZ_ASSERT(fallbackMonitorStub_);
         return fallbackMonitorStub_;
     }
 
     static inline size_t offsetOfFallbackMonitorStub() {
         return offsetof(ICMonitoredFallbackStub, fallbackMonitorStub_);
     }
 };
 
@@ -2356,20 +2363,17 @@ class ICGetProp_Fallback : public ICMoni
 
       public:
         explicit Compiler(JSContext* cx, Engine engine, bool hasReceiver = false)
           : ICStubCompiler(cx, ICStub::GetProp_Fallback, engine),
             hasReceiver_(hasReceiver)
         { }
 
         ICStub* getStub(ICStubSpace* space) {
-            ICGetProp_Fallback* stub = newStub<ICGetProp_Fallback>(space, getStubCode());
-            if (!stub || !stub->initMonitoringChain(cx, space))
-                return nullptr;
-            return stub;
+            return newStub<ICGetProp_Fallback>(space, getStubCode());
         }
     };
 };
 
 static inline uint32_t
 SimpleTypeDescrKey(SimpleTypeDescr* descr)
 {
     if (descr->is<ScalarTypeDescr>())