Bug 1524499 part 1 - Move active flag from BaselineScript to TypeScript. r=tcampbell
authorJan de Mooij <jdemooij@mozilla.com>
Wed, 06 Feb 2019 07:56:17 +0000
changeset 457405 fdc01631d226da0960703451ed2045853658304c
parent 457404 46ccb19887128bb67b48f1474a05803c09e61025
child 457406 936d2328fb864d10c086729bc3105d115430009b
push id111725
push usershindli@mozilla.com
push dateWed, 06 Feb 2019 16:42:39 +0000
treeherdermozilla-inbound@c3cb1fec89a7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcampbell
bugs1524499
milestone67.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 1524499 part 1 - Move active flag from BaselineScript to TypeScript. r=tcampbell Baseline interpreter frames will have a TypeScript/ICScript we want to keep around on GC. The JSScript::typesDontCheckGeneration method will be removed in the next patch. Differential Revision: https://phabricator.services.mozilla.com/D18548
js/src/gc/Zone.cpp
js/src/jit/BaselineJIT.cpp
js/src/jit/BaselineJIT.h
js/src/vm/Debugger.cpp
js/src/vm/JSScript.h
js/src/vm/TypeInference.cpp
js/src/vm/TypeInference.h
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -201,27 +201,28 @@ void Zone::discardJitCode(FreeOp* fop,
   if (!jitZone()) {
     return;
   }
 
   if (isPreservingCode()) {
     return;
   }
 
-  if (discardBaselineCode) {
+  if (discardBaselineCode || releaseTypes) {
 #ifdef DEBUG
-    /* Assert no baseline scripts are marked as active. */
+    // Assert no TypeScripts are marked as active.
     for (auto script = cellIter<JSScript>(); !script.done(); script.next()) {
-      MOZ_ASSERT_IF(script->hasBaselineScript(),
-                    !script->baselineScript()->active());
+      if (TypeScript* types = script->typesDontCheckGeneration()) {
+        MOZ_ASSERT(!types->active());
+      }
     }
 #endif
 
-    /* Mark baseline scripts on the stack as active. */
-    jit::MarkActiveBaselineScripts(this);
+    // Mark TypeScripts on the stack as active.
+    jit::MarkActiveTypeScripts(this);
   }
 
   /* Only mark OSI points if code is being discarded. */
   jit::InvalidateAll(fop, this);
 
   for (auto script = cellIter<JSScript>(); !script.done(); script.next()) {
     jit::FinishInvalidation(fop, script);
 
@@ -257,16 +258,21 @@ void Zone::discardJitCode(FreeOp* fop,
 
     // The optimizedStubSpace will be purged below so make sure ICScript
     // doesn't point into it. We do this after (potentially) releasing types
     // because TypeScript contains the ICScript* and there's no need to
     // purge stubs if we just destroyed the Typescript.
     if (discardBaselineCode && script->hasICScript()) {
       script->icScript()->purgeOptimizedStubs(script);
     }
+
+    // Finally, reset the active flag.
+    if (TypeScript* types = script->typesDontCheckGeneration()) {
+      types->resetActive();
+    }
   }
 
   /*
    * When scripts contains pointers to nursery things, the store buffer
    * can contain entries that point into the optimized stub space. Since
    * this method can be called outside the context of a GC, this situation
    * could result in us trying to mark invalid store buffer entries.
    *
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -1137,21 +1137,17 @@ void jit::JitSpewBaselineICStats(JSScrip
 }
 #endif
 
 void jit::FinishDiscardBaselineScript(FreeOp* fop, JSScript* script) {
   if (!script->hasBaselineScript()) {
     return;
   }
 
-  if (script->baselineScript()->active()) {
-    // Reset |active| flag so that we don't need a separate script
-    // iteration to unmark them.
-    script->baselineScript()->resetActive();
-
+  if (script->typesDontCheckGeneration()->active()) {
     // The baseline caches have been wiped out, so the script will need to
     // warm back up before it can be inlined during Ion compilation.
     script->baselineScript()->clearIonCompiledOrInlined();
     return;
   }
 
   BaselineScript* baseline = script->baselineScript();
   script->setBaselineScript(fop->runtime(), nullptr);
@@ -1211,52 +1207,52 @@ void jit::ToggleBaselineTraceLoggerEngin
         continue;
       }
       script->baselineScript()->toggleTraceLoggerEngine(enable);
     }
   }
 }
 #endif
 
-static void MarkActiveBaselineScripts(JSContext* cx,
-                                      const JitActivationIterator& activation) {
+static void MarkActiveTypeScripts(JSContext* cx,
+                                  const JitActivationIterator& activation) {
   for (OnlyJSJitFrameIter iter(activation); !iter.done(); ++iter) {
     const JSJitFrameIter& frame = iter.frame();
     switch (frame.type()) {
       case FrameType::BaselineJS:
-        frame.script()->baselineScript()->setActive();
+        frame.script()->typesDontCheckGeneration()->setActive();
         break;
       case FrameType::Exit:
         if (frame.exitFrame()->is<LazyLinkExitFrameLayout>()) {
           LazyLinkExitFrameLayout* ll =
               frame.exitFrame()->as<LazyLinkExitFrameLayout>();
-          ScriptFromCalleeToken(ll->jsFrame()->calleeToken())
-              ->baselineScript()
-              ->setActive();
+          JSScript* script =
+              ScriptFromCalleeToken(ll->jsFrame()->calleeToken());
+          script->typesDontCheckGeneration()->setActive();
         }
         break;
       case FrameType::Bailout:
       case FrameType::IonJS: {
-        // Keep the baseline script around, since bailouts from the ion
-        // jitcode might need to re-enter into the baseline jitcode.
-        frame.script()->baselineScript()->setActive();
+        // Keep the TypeScript and BaselineScript around, since bailouts from
+        // the ion jitcode need to re-enter into the Baseline code.
+        frame.script()->typesDontCheckGeneration()->setActive();
         for (InlineFrameIterator inlineIter(cx, &frame); inlineIter.more();
              ++inlineIter) {
-          inlineIter.script()->baselineScript()->setActive();
+          inlineIter.script()->typesDontCheckGeneration()->setActive();
         }
         break;
       }
       default:;
     }
   }
 }
 
-void jit::MarkActiveBaselineScripts(Zone* zone) {
+void jit::MarkActiveTypeScripts(Zone* zone) {
   if (zone->isAtomsZone()) {
     return;
   }
   JSContext* cx = TlsContext.get();
   for (JitActivationIterator iter(cx); !iter.done(); ++iter) {
     if (iter->compartment()->zone() == zone) {
-      MarkActiveBaselineScripts(cx, iter);
+      MarkActiveTypeScripts(cx, iter);
     }
   }
 }
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -247,21 +247,17 @@ struct BaselineScript final {
   bool traceLoggerScriptsEnabled_ = false;
   bool traceLoggerEngineEnabled_ = false;
 #  endif
   TraceLoggerEvent traceLoggerScriptEvent_ = {};
 #endif
 
  public:
   enum Flag {
-    // (1 << 0) is unused.
-
-    // Flag set when discarding JIT code, to indicate this script is
-    // on the stack and should not be discarded.
-    ACTIVE = 1 << 1,
+    // (1 << 0) and (1 << 1) are unused.
 
     // Flag set when the script contains any writes to its on-stack
     // (rather than call object stored) arguments.
     MODIFIES_ARGUMENTS = 1 << 2,
 
     // Flag set when compiled for use with Debugger. Handles various
     // Debugger hooks and compiles toggled calls for traps.
     HAS_DEBUG_INSTRUMENTATION = 1 << 3,
@@ -349,20 +345,16 @@ struct BaselineScript final {
     return offsetof(BaselineScript, method_);
   }
 
   void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
                               size_t* data) const {
     *data += mallocSizeOf(this);
   }
 
-  bool active() const { return flags_ & ACTIVE; }
-  void setActive() { flags_ |= ACTIVE; }
-  void resetActive() { flags_ &= ~ACTIVE; }
-
   void setModifiesArguments() { flags_ |= MODIFIES_ARGUMENTS; }
   bool modifiesArguments() { return flags_ & MODIFIES_ARGUMENTS; }
 
   void setHasDebugInstrumentation() { flags_ |= HAS_DEBUG_INSTRUMENTATION; }
   bool hasDebugInstrumentation() const {
     return flags_ & HAS_DEBUG_INSTRUMENTATION;
   }
 
@@ -627,19 +619,19 @@ struct BaselineBailoutInfo {
   BailoutKind bailoutKind;
 };
 
 MOZ_MUST_USE bool BailoutIonToBaseline(
     JSContext* cx, JitActivation* activation, const JSJitFrameIter& iter,
     bool invalidate, BaselineBailoutInfo** bailoutInfo,
     const ExceptionBailoutInfo* exceptionInfo);
 
-// Mark baseline scripts on the stack as active, so that they are not discarded
+// Mark TypeScripts on the stack as active, so that they are not discarded
 // during GC.
-void MarkActiveBaselineScripts(Zone* zone);
+void MarkActiveTypeScripts(Zone* zone);
 
 MethodStatus BaselineCompile(JSContext* cx, JSScript* script,
                              bool forceDebugInstrumentation = false);
 
 #ifdef JS_STRUCTURED_SPEW
 void JitSpewBaselineICStats(JSScript* script, const char* dumpReason);
 #endif
 
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2700,20 +2700,20 @@ class MOZ_RAII ExecutionObservableScript
   if (oldestEnabledFrame) {
     AutoRealm ar(cx, oldestEnabledFrame.environmentChain());
     DebugEnvironments::unsetPrevUpToDateUntil(cx, oldestEnabledFrame);
   }
 
   return true;
 }
 
-static inline void MarkBaselineScriptActiveIfObservable(
+static inline void MarkTypeScriptActiveIfObservable(
     JSScript* script, const Debugger::ExecutionObservableSet& obs) {
   if (obs.shouldRecompileOrInvalidate(script)) {
-    script->baselineScript()->setActive();
+    script->typesDontCheckGeneration()->setActive();
   }
 }
 
 static bool AppendAndInvalidateScript(JSContext* cx, Zone* zone,
                                       JSScript* script,
                                       Vector<JSScript*>& scripts) {
   // Enter the script's realm as addPendingRecompile attempts to
   // cancel off-thread compilations, whose books are kept on the
@@ -2767,36 +2767,37 @@ static bool UpdateExecutionObservability
     if (actIter->compartment()->zone() != zone) {
       continue;
     }
 
     for (OnlyJSJitFrameIter iter(actIter); !iter.done(); ++iter) {
       const JSJitFrameIter& frame = iter.frame();
       switch (frame.type()) {
         case FrameType::BaselineJS:
-          MarkBaselineScriptActiveIfObservable(frame.script(), obs);
+          MarkTypeScriptActiveIfObservable(frame.script(), obs);
           break;
         case FrameType::IonJS:
-          MarkBaselineScriptActiveIfObservable(frame.script(), obs);
+          MarkTypeScriptActiveIfObservable(frame.script(), obs);
           for (InlineFrameIterator inlineIter(cx, &frame); inlineIter.more();
                ++inlineIter) {
-            MarkBaselineScriptActiveIfObservable(inlineIter.script(), obs);
+            MarkTypeScriptActiveIfObservable(inlineIter.script(), obs);
           }
           break;
         default:;
       }
     }
   }
 
   // Iterate through the scripts again and finish discarding
   // BaselineScripts. This must be done as a separate phase as we can only
   // discard the BaselineScript on scripts that have no IonScript.
   for (size_t i = 0; i < scripts.length(); i++) {
     MOZ_ASSERT_IF(scripts[i]->isDebuggee(), observing);
     FinishDiscardBaselineScript(fop, scripts[i]);
+    scripts[i]->typesDontCheckGeneration()->resetActive();
   }
 
   // Iterate through all wasm instances to find ones that need to be updated.
   for (RealmsInZoneIter r(zone); !r.done(); r.next()) {
     for (wasm::Instance* instance : r->wasm.instances()) {
       if (!instance->debugEnabled()) {
         continue;
       }
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -2418,16 +2418,18 @@ class JSScript : public js::gc::TenuredC
   bool isTopLevel() { return code() && !functionNonDelazifying(); }
 
   /* Ensure the script has a TypeScript. */
   inline bool ensureHasTypes(JSContext* cx, js::AutoKeepTypeScripts&);
 
   inline js::TypeScript* types(const js::AutoSweepTypeScript& sweep);
   inline bool typesNeedsSweep() const;
 
+  js::TypeScript* typesDontCheckGeneration() { return types_; }
+
   void maybeReleaseTypes();
   void sweepTypes(const js::AutoSweepTypeScript& sweep);
 
   inline js::GlobalObject& global() const;
   inline bool hasGlobal(const js::GlobalObject* global) const;
   js::GlobalObject& uninlinedGlobal() const;
 
   uint32_t bodyScopeIndex() const { return bodyScopeIndex_; }
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -3591,17 +3591,18 @@ static size_t NumTypeSets(JSScript* scri
 
   return num;
 }
 
 TypeScript::TypeScript(JSScript* script, ICScriptPtr&& icScript,
                        uint32_t numTypeSets)
     : icScript_(std::move(icScript)),
       numTypeSets_(numTypeSets),
-      bytecodeTypeMapHint_(0) {
+      bytecodeTypeMapHint_(0),
+      active_(false) {
   StackTypeSet* array = typeArray();
   for (unsigned i = 0; i < numTypeSets; i++) {
     new (&array[i]) StackTypeSet();
   }
 
   FillBytecodeTypeMap(script, bytecodeTypeMap());
 }
 
@@ -4738,17 +4739,18 @@ void ObjectGroup::sweep(const AutoSweepO
   if (zone()->types.hadOOMSweepingTypes()) {
     // It's possible we OOM'd while copying freeze constraints, so they
     // need to be regenerated.
     clearFlag(MutableFlags::HasFreezeConstraints);
   }
 }
 
 void JSScript::maybeReleaseTypes() {
-  if (!types_ || zone()->types.keepTypeScripts || hasBaselineScript()) {
+  if (!types_ || zone()->types.keepTypeScripts || hasBaselineScript() ||
+      types_->active()) {
     return;
   }
 
   MOZ_ASSERT(!hasIonScript());
 
   types_->destroy(zone());
   types_ = nullptr;
 
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -215,16 +215,20 @@ class TypeScript {
 
   // Number of TypeSets in typeArray_.
   uint32_t numTypeSets_;
 
   // This field is used to avoid binary searches for the sought entry when
   // bytecode map queries are in linear order.
   uint32_t bytecodeTypeMapHint_;
 
+  // Flag set when discarding JIT code to indicate this script is on the stack
+  // and type information and JIT code should not be discarded.
+  bool active_;
+
   // Variable-size array. This is followed by the bytecode type map.
   StackTypeSet typeArray_[1];
 
  public:
   TypeScript(JSScript* script, ICScriptPtr&& icScript, uint32_t numTypeSets);
 
   RecompileInfoVector& inlinedCompilations() { return inlinedCompilations_; }
   MOZ_MUST_USE bool addInlinedCompilation(RecompileInfo info) {
@@ -233,16 +237,20 @@ class TypeScript {
     }
     return inlinedCompilations_.append(info);
   }
 
   uint32_t numTypeSets() const { return numTypeSets_; }
 
   uint32_t* bytecodeTypeMapHint() { return &bytecodeTypeMapHint_; }
 
+  bool active() const { return active_; }
+  void setActive() { active_ = true; }
+  void resetActive() { active_ = false; }
+
   jit::ICScript* icScript() const {
     MOZ_ASSERT(icScript_);
     return icScript_.get();
   }
 
   /* Array of type sets for variables and JOF_TYPESET ops. */
   StackTypeSet* typeArray() const {
     // Ensure typeArray_ is the last data member of TypeScript.