Bug 1541404 part 19 - Add BaselineInterpreter class and use it in various places. r=tcampbell
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 03 May 2019 07:42:31 +0000
changeset 531255 f52499501a149497888857d4dc23ec5adca2c352
parent 531254 de1a60c3fbf2078321aa69300a3ee0c277ea59bc
child 531256 3cc662da1cd8d2a81d91912844247b51ce949e69
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcampbell
bugs1541404
milestone68.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 1541404 part 19 - Add BaselineInterpreter class and use it in various places. r=tcampbell I considered adding BaselineInterpreter.{h,cpp} files but there are shared helper functions so this might get awkward. Maybe once the rest of the code is in we can experiment with changes in this area. Differential Revision: https://phabricator.services.mozilla.com/D29158
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineJIT.cpp
js/src/jit/BaselineJIT.h
js/src/jit/Ion.cpp
js/src/jit/Jit.cpp
js/src/jit/JitFrames.cpp
js/src/jit/JitRealm.h
js/src/vm/JSScript.cpp
js/src/vm/Runtime.cpp
js/src/vm/TypeInference.cpp
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -5655,17 +5655,19 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   }
 
   masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
   return emitReturn();
 }
 
 template <>
 void BaselineCompilerCodeGen::emitJumpToInterpretOpLabel() {
-  MOZ_CRASH("NYI: Interpreter emitJumpToInterpretOpLabel");
+  TrampolinePtr code =
+      cx->runtime()->jitRuntime()->baselineInterpreter().interpretOpAddr();
+  masm.jump(code);
 }
 
 template <>
 void BaselineInterpreterCodeGen::emitJumpToInterpretOpLabel() {
   masm.jump(handler.interpretOpLabel());
 }
 
 template <typename Handler>
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -146,17 +146,19 @@ JitExecStatus jit::EnterBaselineAtBranch
 
     // Skip debug breakpoint/trap handler, the interpreter already handled it
     // for the current op.
     if (fp->isDebuggee()) {
       MOZ_RELEASE_ASSERT(baseline->hasDebugInstrumentation());
       data.jitcode += MacroAssembler::ToggledCallSize(data.jitcode);
     }
   } else {
-    MOZ_CRASH("NYI: Interpreter executeOp code");
+    const BaselineInterpreter& interp =
+        cx->runtime()->jitRuntime()->baselineInterpreter();
+    data.jitcode = interp.interpretOpAddr().value;
   }
 
   // Note: keep this in sync with SetEnterJitData.
 
   data.osrFrame = fp;
   data.osrNumStackValues =
       fp->script()->nfixed() + cx->interpreterRegs().stackDepth();
 
@@ -1075,40 +1077,84 @@ void BaselineScript::toggleTraceLoggerEn
   }
 
 #  if DEBUG
   traceLoggerEngineEnabled_ = enable;
 #  endif
 }
 #endif
 
+static void ToggleProfilerInstrumentation(JitCode* code,
+                                          uint32_t profilerEnterToggleOffset,
+                                          uint32_t profilerExitToggleOffset,
+                                          bool enable) {
+  CodeLocationLabel enterToggleLocation(code,
+                                        CodeOffset(profilerEnterToggleOffset));
+  CodeLocationLabel exitToggleLocation(code,
+                                       CodeOffset(profilerExitToggleOffset));
+  if (enable) {
+    Assembler::ToggleToCmp(enterToggleLocation);
+    Assembler::ToggleToCmp(exitToggleLocation);
+  } else {
+    Assembler::ToggleToJmp(enterToggleLocation);
+    Assembler::ToggleToJmp(exitToggleLocation);
+  }
+}
+
 void BaselineScript::toggleProfilerInstrumentation(bool enable) {
   if (enable == isProfilerInstrumentationOn()) {
     return;
   }
 
   JitSpew(JitSpew_BaselineIC, "  toggling profiling %s for BaselineScript %p",
           enable ? "on" : "off", this);
 
-  // Toggle the jump
-  CodeLocationLabel enterToggleLocation(method_,
-                                        CodeOffset(profilerEnterToggleOffset_));
-  CodeLocationLabel exitToggleLocation(method_,
-                                       CodeOffset(profilerExitToggleOffset_));
+  ToggleProfilerInstrumentation(method_, profilerEnterToggleOffset_,
+                                profilerExitToggleOffset_, enable);
+
   if (enable) {
-    Assembler::ToggleToCmp(enterToggleLocation);
-    Assembler::ToggleToCmp(exitToggleLocation);
     flags_ |= uint32_t(PROFILER_INSTRUMENTATION_ON);
   } else {
-    Assembler::ToggleToJmp(enterToggleLocation);
-    Assembler::ToggleToJmp(exitToggleLocation);
     flags_ &= ~uint32_t(PROFILER_INSTRUMENTATION_ON);
   }
 }
 
+void BaselineInterpreter::toggleProfilerInstrumentation(bool enable) {
+  if (!JitOptions.baselineInterpreter) {
+    return;
+  }
+
+  AutoWritableJitCode awjc(code_);
+  ToggleProfilerInstrumentation(code_, profilerEnterToggleOffset_,
+                                profilerExitToggleOffset_, enable);
+}
+
+void BaselineInterpreter::toggleDebuggerInstrumentation(bool enable) {
+  if (!JitOptions.baselineInterpreter) {
+    return;
+  }
+
+  AutoWritableJitCode awjc(code_);
+
+  // Toggle prologue IsDebuggeeCheck code.
+  CodeLocationLabel debuggeeCheckLocation(code_,
+                                          CodeOffset(debuggeeCheckOffset_));
+  if (enable) {
+    Assembler::ToggleToCmp(debuggeeCheckLocation);
+  } else {
+    Assembler::ToggleToJmp(debuggeeCheckLocation);
+  }
+
+  // Toggle DebugTrapHandler calls.
+  for (uint32_t offset : debugTrapOffsets_) {
+    CodeLocationLabel trapLocation(code_, CodeOffset(offset));
+    Assembler::ToggleCall(trapLocation, enable);
+  }
+}
+
 void ICScript::purgeOptimizedStubs(JSScript* script) {
   MOZ_ASSERT(script->icScript() == this);
 
   Zone* zone = script->zone();
   if (zone->isGCSweeping() && IsAboutToBeFinalizedDuringSweep(*script)) {
     // We're sweeping and the script is dead. Don't purge optimized stubs
     // because (1) accessing CacheIRStubInfo pointers in ICStubs is invalid
     // because we may have swept them already when we started (incremental)
@@ -1272,16 +1318,18 @@ void jit::AddSizeOfBaselineData(JSScript
 }
 
 void jit::ToggleBaselineProfiling(JSRuntime* runtime, bool enable) {
   JitRuntime* jrt = runtime->jitRuntime();
   if (!jrt) {
     return;
   }
 
+  jrt->baselineInterpreter().toggleProfilerInstrumentation(enable);
+
   for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
     for (auto script = zone->cellIter<JSScript>(); !script.done();
          script.next()) {
       if (!script->hasBaselineScript()) {
         continue;
       }
       AutoWritableJitCode awjc(script->baselineScript()->method());
       script->baselineScript()->toggleProfilerInstrumentation(enable);
@@ -1360,8 +1408,31 @@ void jit::MarkActiveTypeScripts(Zone* zo
   }
   JSContext* cx = TlsContext.get();
   for (JitActivationIterator iter(cx); !iter.done(); ++iter) {
     if (iter->compartment()->zone() == zone) {
       MarkActiveTypeScripts(cx, iter);
     }
   }
 }
+
+void BaselineInterpreter::init(JitCode* code, uint32_t interpretOpOffset,
+                               uint32_t profilerEnterToggleOffset,
+                               uint32_t profilerExitToggleOffset,
+                               uint32_t debuggeeCheckOffset,
+                               DebugTrapOffsets&& debugTrapOffsets) {
+  code_ = code;
+  interpretOpOffset_ = interpretOpOffset;
+  profilerEnterToggleOffset_ = profilerEnterToggleOffset;
+  profilerExitToggleOffset_ = profilerExitToggleOffset;
+  debuggeeCheckOffset_ = debuggeeCheckOffset;
+  debugTrapOffsets_ = std::move(debugTrapOffsets);
+}
+
+bool jit::GenerateBaselineInterpreter(JSContext* cx,
+                                      BaselineInterpreter& interpreter) {
+  // Temporary JitOptions check to prevent crashes for now.
+  if (JitOptions.baselineInterpreter) {
+    MOZ_CRASH("NYI: GenerateBaselineInterpreter");
+  }
+
+  return true;
+}
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -646,16 +646,61 @@ MethodStatus BaselineCompile(JSContext* 
                              bool forceDebugInstrumentation = false);
 
 #ifdef JS_STRUCTURED_SPEW
 void JitSpewBaselineICStats(JSScript* script, const char* dumpReason);
 #endif
 
 static const unsigned BASELINE_MAX_ARGS_LENGTH = 20000;
 
+// Class storing the generated Baseline Interpreter code for the runtime.
+class BaselineInterpreter {
+  // The interpreter code.
+  JitCode* code_ = nullptr;
+
+  // Offset of the code to start interpreting a bytecode op.
+  uint32_t interpretOpOffset_ = 0;
+
+  // The offsets for the toggledJump instructions for profiler instrumentation.
+  uint32_t profilerEnterToggleOffset_ = 0;
+  uint32_t profilerExitToggleOffset_ = 0;
+
+  // The offset for the toggledJump instruction for the debugger's
+  // IsDebuggeeCheck code in the prologue.
+  uint32_t debuggeeCheckOffset_ = 0;
+
+  // Offsets of toggled calls to the DebugTrapHandler trampoline (for
+  // breakpoints and stepping).
+  using DebugTrapOffsets = js::Vector<uint32_t, 0, SystemAllocPolicy>;
+  DebugTrapOffsets debugTrapOffsets_;
+
+ public:
+  BaselineInterpreter() = default;
+
+  BaselineInterpreter(const BaselineInterpreter&) = delete;
+  void operator=(const BaselineInterpreter&) = delete;
+
+  void init(JitCode* code, uint32_t interpretOpOffset,
+            uint32_t profilerEnterToggleOffset,
+            uint32_t profilerExitToggleOffset, uint32_t debuggeeCheckOffset,
+            DebugTrapOffsets&& debugTrapOffsets);
+
+  uint8_t* codeRaw() const { return code_->raw(); }
+
+  TrampolinePtr interpretOpAddr() const {
+    return TrampolinePtr(codeRaw() + interpretOpOffset_);
+  }
+
+  void toggleProfilerInstrumentation(bool enable);
+  void toggleDebuggerInstrumentation(bool enable);
+};
+
+MOZ_MUST_USE bool GenerateBaselineInterpreter(JSContext* cx,
+                                              BaselineInterpreter& interpreter);
+
 }  // namespace jit
 }  // namespace js
 
 namespace JS {
 
 template <>
 struct DeletePolicy<js::jit::BaselineScript> {
   explicit DeletePolicy(JSRuntime* rt) : rt_(rt) {}
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -161,16 +161,17 @@ JitRuntime::JitRuntime()
       argumentsRectifierOffset_(0),
       argumentsRectifierReturnOffset_(0),
       invalidatorOffset_(0),
       lazyLinkStubOffset_(0),
       interpreterStubOffset_(0),
       doubleToInt32ValueStubOffset_(0),
       debugTrapHandler_(nullptr),
       baselineDebugModeOSRHandler_(nullptr),
+      baselineInterpreter_(),
       trampolineCode_(nullptr),
       jitcodeGlobalTable_(nullptr),
 #ifdef DEBUG
       ionBailAfter_(0),
 #endif
       numFinishedBuilders_(0),
       ionLazyLinkListSize_(0) {
 }
@@ -208,16 +209,20 @@ bool JitRuntime::initialize(JSContext* c
     return false;
   }
 
   jitcodeGlobalTable_ = cx->new_<JitcodeGlobalTable>();
   if (!jitcodeGlobalTable_) {
     return false;
   }
 
+  if (!GenerateBaselineInterpreter(cx, baselineInterpreter_)) {
+    return false;
+  }
+
   return true;
 }
 
 bool JitRuntime::generateTrampolines(JSContext* cx) {
   StackMacroAssembler masm;
 
   Label bailoutTail;
   JitSpew(JitSpew_Codegen, "# Emitting bailout tail stub");
--- a/js/src/jit/Jit.cpp
+++ b/js/src/jit/Jit.cpp
@@ -15,18 +15,21 @@
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 static EnterJitStatus JS_HAZ_JSNATIVE_CALLER EnterJit(JSContext* cx,
                                                       RunState& state,
                                                       uint8_t* code) {
-  MOZ_ASSERT(state.script()->hasBaselineScript());
+  // We don't want to call the interpreter stub here (because
+  // C++ -> interpreterStub -> C++ is slower than staying in C++).
   MOZ_ASSERT(code);
+  MOZ_ASSERT(code != cx->runtime()->jitRuntime()->interpreterStub().value);
+
   MOZ_ASSERT(IsBaselineEnabled(cx));
 
   if (!CheckRecursionLimit(cx)) {
     return EnterJitStatus::Error;
   }
 
 #ifdef DEBUG
   // Assert we don't GC before entering JIT code. A GC could discard JIT code
@@ -50,17 +53,21 @@ static EnterJitStatus JS_HAZ_JSNATIVE_CA
     numActualArgs = args.length();
 
     if (TooManyActualArguments(numActualArgs)) {
       // Too many arguments for Ion. Baseline supports more actual
       // arguments, so in that case force Baseline code.
       if (numActualArgs > BASELINE_MAX_ARGS_LENGTH) {
         return EnterJitStatus::NotEntered;
       }
-      code = script->baselineScript()->method()->raw();
+      if (script->hasBaselineScript()) {
+        code = script->baselineScript()->method()->raw();
+      } else {
+        code = cx->runtime()->jitRuntime()->baselineInterpreter().codeRaw();
+      }
     }
 
     constructing = state.asInvoke()->constructing();
     maxArgc = args.length() + 1;
     maxArgv = args.array() - 1;  // -1 to include |this|
     envChain = nullptr;
     calleeToken = CalleeToToken(&args.callee().as<JSFunction>(), constructing);
 
@@ -126,21 +133,26 @@ static EnterJitStatus JS_HAZ_JSNATIVE_CA
   return EnterJitStatus::Ok;
 }
 
 EnterJitStatus js::jit::MaybeEnterJit(JSContext* cx, RunState& state) {
   JSScript* script = state.script();
 
   uint8_t* code = script->jitCodeRaw();
   do {
-    // Make sure we have a BaselineScript: we don't want to call the
-    // interpreter stub here. Note that Baseline code contains warm-up
-    // checks in the prologue to Ion-compile if needed.
-    if (script->hasBaselineScript()) {
-      break;
+    // Make sure we can enter Baseline Interpreter or JIT code. Note that
+    // the prologue has warm-up checks to tier up if needed.
+    if (JitOptions.baselineInterpreter) {
+      if (script->types()) {
+        break;
+      }
+    } else {
+      if (script->hasBaselineScript()) {
+        break;
+      }
     }
 
     script->incWarmUpCounter();
 
     // Try to Ion-compile.
     if (jit::IsIonEnabled(cx)) {
       jit::MethodStatus status = jit::CanEnterIon(cx, state);
       if (status == jit::Method_Error) {
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -371,31 +371,45 @@ static bool ProcessTryNotesBaseline(JSCo
         SettleOnTryNote(cx, tn, frame, ei, rfe, pc);
 
         // Ion can compile try-catch, but bailing out to catch
         // exceptions is slow. Reset the warm-up counter so that if we
         // catch many exceptions we won't Ion-compile the script.
         script->resetWarmUpCounter();
 
         // Resume at the start of the catch block.
-        PCMappingSlotInfo slotInfo;
         rfe->kind = ResumeFromException::RESUME_CATCH;
-        rfe->target =
-            script->baselineScript()->nativeCodeForPC(script, *pc, &slotInfo);
-        MOZ_ASSERT(slotInfo.isStackSynced());
+        if (frame.baselineFrame()->runningInInterpreter()) {
+          const BaselineInterpreter& interp =
+              cx->runtime()->jitRuntime()->baselineInterpreter();
+          frame.baselineFrame()->setInterpreterPC(*pc);
+          rfe->target = interp.interpretOpAddr().value;
+        } else {
+          PCMappingSlotInfo slotInfo;
+          rfe->target =
+              script->baselineScript()->nativeCodeForPC(script, *pc, &slotInfo);
+          MOZ_ASSERT(slotInfo.isStackSynced());
+        }
         return true;
       }
 
       case JSTRY_FINALLY: {
-        PCMappingSlotInfo slotInfo;
         SettleOnTryNote(cx, tn, frame, ei, rfe, pc);
         rfe->kind = ResumeFromException::RESUME_FINALLY;
-        rfe->target =
-            script->baselineScript()->nativeCodeForPC(script, *pc, &slotInfo);
-        MOZ_ASSERT(slotInfo.isStackSynced());
+        if (frame.baselineFrame()->runningInInterpreter()) {
+          const BaselineInterpreter& interp =
+              cx->runtime()->jitRuntime()->baselineInterpreter();
+          frame.baselineFrame()->setInterpreterPC(*pc);
+          rfe->target = interp.interpretOpAddr().value;
+        } else {
+          PCMappingSlotInfo slotInfo;
+          rfe->target =
+              script->baselineScript()->nativeCodeForPC(script, *pc, &slotInfo);
+          MOZ_ASSERT(slotInfo.isStackSynced());
+        }
         // Drop the exception instead of leaking cross compartment data.
         if (!cx->getPendingException(
                 MutableHandleValue::fromMarkedLocation(&rfe->exception))) {
           rfe->exception = UndefinedValue();
         }
         cx->clearPendingException();
         return true;
       }
--- a/js/src/jit/JitRealm.h
+++ b/js/src/jit/JitRealm.h
@@ -11,16 +11,17 @@
 #include "mozilla/DebugOnly.h"
 #include "mozilla/EnumeratedArray.h"
 #include "mozilla/MemoryReporting.h"
 
 #include <utility>
 
 #include "builtin/TypedObject.h"
 #include "jit/BaselineICList.h"
+#include "jit/BaselineJIT.h"
 #include "jit/CompileInfo.h"
 #include "jit/ICStubSpace.h"
 #include "jit/IonCode.h"
 #include "jit/IonControlFlow.h"
 #include "jit/JitFrames.h"
 #include "jit/shared/Assembler-shared.h"
 #include "js/GCHashTable.h"
 #include "js/Value.h"
@@ -190,16 +191,19 @@ class JitRuntime {
 
   // Thunk used by the debugger for breakpoint and step mode.
   WriteOnceData<JitCode*> debugTrapHandler_;
 
   // Thunk used to fix up on-stack recompile of baseline scripts.
   WriteOnceData<JitCode*> baselineDebugModeOSRHandler_;
   WriteOnceData<void*> baselineDebugModeOSRHandlerNoFrameRegPopAddr_;
 
+  // BaselineInterpreter state.
+  BaselineInterpreter baselineInterpreter_;
+
   // Code for trampolines and VMFunction wrappers.
   WriteOnceData<JitCode*> trampolineCode_;
 
   // Map VMFunction addresses to the offset of the wrapper in
   // trampolineCode_.
   using VMWrapperMap = HashMap<const VMFunction*, uint32_t, VMFunction>;
   WriteOnceData<VMWrapperMap*> functionWrappers_;
 
@@ -321,16 +325,18 @@ class JitRuntime {
     MOZ_ASSERT(trampolineCode_);
     return trampolineCode(tailCallFunctionWrapperOffsets_[size_t(funId)]);
   }
 
   JitCode* debugTrapHandler(JSContext* cx);
   JitCode* getBaselineDebugModeOSRHandler(JSContext* cx);
   void* getBaselineDebugModeOSRHandlerAddress(JSContext* cx, bool popFrameReg);
 
+  BaselineInterpreter& baselineInterpreter() { return baselineInterpreter_; }
+
   TrampolinePtr getGenericBailoutHandler() const {
     return trampolineCode(bailoutHandlerOffset_);
   }
 
   TrampolinePtr getExceptionTail() const {
     return trampolineCode(exceptionTailOffset_);
   }
 
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -5426,16 +5426,19 @@ void JSScript::updateJitCodeRaw(JSRuntim
     jitCodeRaw_ = rt->jitRuntime()->lazyLinkStub().value;
     jitCodeSkipArgCheck_ = jitCodeRaw_;
   } else if (hasIonScript()) {
     jitCodeRaw_ = ion->method()->raw();
     jitCodeSkipArgCheck_ = jitCodeRaw_ + ion->getSkipArgCheckEntryOffset();
   } else if (hasBaselineScript()) {
     jitCodeRaw_ = baseline->method()->raw();
     jitCodeSkipArgCheck_ = jitCodeRaw_;
+  } else if (types() && js::jit::JitOptions.baselineInterpreter) {
+    jitCodeRaw_ = rt->jitRuntime()->baselineInterpreter().codeRaw();
+    jitCodeSkipArgCheck_ = jitCodeRaw_;
   } else {
     jitCodeRaw_ = rt->jitRuntime()->interpreterStub().value;
     jitCodeSkipArgCheck_ = jitCodeRaw_;
   }
   MOZ_ASSERT(jitCodeRaw_);
   MOZ_ASSERT(jitCodeSkipArgCheck_);
 }
 
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -765,23 +765,31 @@ void JSRuntime::clearUsedByHelperThread(
 
   JSContext* cx = mainContextFromOwnThread();
   if (gc.fullGCForAtomsRequested() && cx->canCollectAtoms()) {
     gc.triggerFullGCForAtoms(cx);
   }
 }
 
 void JSRuntime::incrementNumDebuggeeRealms() {
+  if (numDebuggeeRealms_ == 0) {
+    jitRuntime()->baselineInterpreter().toggleDebuggerInstrumentation(true);
+  }
+
   numDebuggeeRealms_++;
   MOZ_ASSERT(numDebuggeeRealms_ <= numRealms);
 }
 
 void JSRuntime::decrementNumDebuggeeRealms() {
   MOZ_ASSERT(numDebuggeeRealms_ > 0);
   numDebuggeeRealms_--;
+
+  if (numDebuggeeRealms_ == 0) {
+    jitRuntime()->baselineInterpreter().toggleDebuggerInstrumentation(false);
+  }
 }
 
 bool js::CurrentThreadCanAccessRuntime(const JSRuntime* rt) {
   return rt->mainContextFromAnyThread() == TlsContext.get();
 }
 
 bool js::CurrentThreadCanAccessZone(Zone* zone) {
   // Helper thread zones can only be used by their owning thread.
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -3550,16 +3550,20 @@ bool JSScript::makeTypes(JSContext* cx) 
   if (!typeScript) {
     return false;
   }
 
   prepareForDestruction.release();
 
   types_ = new (typeScript) TypeScript(this, std::move(icScript), numTypeSets);
 
+  // We have a TypeScript so we can set the script's jitCodeRaw_ pointer to the
+  // Baseline Interpreter code.
+  updateJitCodeRaw(cx->runtime());
+
 #ifdef DEBUG
   StackTypeSet* typeArray = typeScript->typeArrayDontCheckGeneration();
   for (unsigned i = 0; i < numBytecodeTypeSets(); i++) {
     InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u %p",
               InferSpewColor(&typeArray[i]), &typeArray[i],
               InferSpewColorReset(), i, this);
   }
   TypeSet* thisTypes = TypeScript::ThisTypes(this);
@@ -4580,16 +4584,17 @@ void JSScript::maybeReleaseTypes() {
       types_->active()) {
     return;
   }
 
   MOZ_ASSERT(!hasIonScript());
 
   types_->destroy(zone());
   types_ = nullptr;
+  updateJitCodeRaw(runtimeFromMainThread());
 }
 
 void TypeScript::destroy(Zone* zone) {
   icScript_->prepareForDestruction(zone);
 
   js_delete(this);
 }