Bug 1292590 - Trace script pointers in off thread compilation tasks r=terrence a=abillings
authorJon Coppeard <jcoppeard@mozilla.com>
Wed, 07 Sep 2016 11:30:32 +0100
changeset 312986 488c4ea38e16888e9ab439f5ef0f258252597848
parent 312970 b294f305f8d1115fdcfdbf66eb4cb55393e5af04
child 312987 cef1721594bf04fb708e6fb1f5a4d80722443b02
push id20479
push userkwierso@gmail.com
push dateThu, 08 Sep 2016 01:08:46 +0000
treeherderfx-team@fb7c6b034329 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence, abillings
bugs1292590
milestone51.0a1
Bug 1292590 - Trace script pointers in off thread compilation tasks r=terrence a=abillings
js/src/gc/RootMarking.cpp
js/src/jit-test/tests/debug/Memory-takeCensus-11.js
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/vm/HelperThreads.cpp
js/src/vm/HelperThreads.h
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -374,16 +374,19 @@ js::gc::GCRuntime::traceRuntimeCommon(JS
     // Trace all compartment roots, but not the compartment itself; it is
     // marked via the parent pointer if traceRoots actually traces anything.
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
         c->traceRoots(trc, traceOrMark);
 
     // Trace SPS.
     rt->spsProfiler.trace(trc);
 
+    // Trace helper thread roots.
+    HelperThreadState().trace(trc);
+
     // Trace the embedding's black and gray roots.
     if (!rt->isHeapMinorCollecting()) {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_EMBEDDING);
 
         /*
          * The embedding can register additional roots here.
          *
          * We don't need to trace these in a minor GC because all pointers into
--- a/js/src/jit-test/tests/debug/Memory-takeCensus-11.js
+++ b/js/src/jit-test/tests/debug/Memory-takeCensus-11.js
@@ -1,12 +1,13 @@
 // Check byte counts produced by takeCensus.
 
 const g = newGlobal();
 g.eval("setLazyParsingDisabled(true)");
+g.eval("setJitCompilerOption('ion.warmup.trigger', 1000)");
 
 const dbg = new Debugger(g);
 
 g.evaluate("function one() {}", { fileName: "one.js" });
 g.evaluate(`function two1() {}
             function two2() {}`,
            { fileName: "two.js" });
 g.evaluate(`function three1() {}
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -14611,8 +14611,19 @@ IonBuilder::convertToBoolean(MDefinition
     // Convert to bool with the '!!' idiom
     MNot* resultInverted = MNot::New(alloc(), input, constraints());
     current->add(resultInverted);
     MNot* result = MNot::New(alloc(), resultInverted, constraints());
     current->add(result);
 
     return result;
 }
+
+void
+IonBuilder::trace(JSTracer* trc)
+{
+    if (script_->zoneFromAnyThread()->runtimeFromAnyThread() != trc->runtime())
+        return;
+
+    TraceManuallyBarrieredEdge(trc, &script_, "IonBuiler::script_");
+    if (actionableAbortScript_)
+        TraceManuallyBarrieredEdge(trc, &actionableAbortScript_, "IonBuiler::actionableAbortScript_");
+}
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -1149,16 +1149,18 @@ class IonBuilder
                                            const char** abortMessage)
     {
         MOZ_ASSERT(hadActionableAbort());
         *abortScript = actionableAbortScript_;
         *abortPc = actionableAbortPc_;
         *abortMessage = actionableAbortMessage_;
     }
 
+    void trace(JSTracer* trc);
+
   private:
     MOZ_MUST_USE bool init();
 
     JSContext* analysisContext;
     BaselineFrameInspector* baselineFrame_;
 
     // Constraints for recording dependencies on type information.
     CompilerConstraintList* constraints_;
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -205,19 +205,19 @@ static const JSClass parseTaskGlobalClas
     &parseTaskGlobalClassOps
 };
 
 ParseTask::ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
                      JSContext* initCx, const char16_t* chars, size_t length,
                      JS::OffThreadCompileCallback callback, void* callbackData)
   : kind(kind), cx(cx), options(initCx), chars(chars), length(length),
     alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
-    exclusiveContextGlobal(initCx, exclusiveContextGlobal),
+    exclusiveContextGlobal(exclusiveContextGlobal),
     callback(callback), callbackData(callbackData),
-    script(initCx), sourceObject(initCx),
+    script(nullptr), sourceObject(nullptr),
     errors(cx), overRecursed(false), outOfMemory(false)
 {
 }
 
 bool
 ParseTask::init(JSContext* cx, const ReadOnlyCompileOptions& options)
 {
     if (!this->options.copy(cx, options))
@@ -249,48 +249,60 @@ ParseTask::~ParseTask()
 {
     // ParseTask takes over ownership of its input exclusive context.
     js_delete(cx);
 
     for (size_t i = 0; i < errors.length(); i++)
         js_delete(errors[i]);
 }
 
+void
+ParseTask::trace(JSTracer* trc)
+{
+    if (exclusiveContextGlobal->zoneFromAnyThread()->runtimeFromAnyThread() != trc->runtime())
+        return;
+
+    TraceManuallyBarrieredEdge(trc, &exclusiveContextGlobal, "ParseTask::exclusiveContextGlobal");
+    if (script)
+        TraceManuallyBarrieredEdge(trc, &script, "ParseTask::script");
+    if (sourceObject)
+        TraceManuallyBarrieredEdge(trc, &sourceObject, "ParseTask::sourceObject");
+}
+
 ScriptParseTask::ScriptParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
                                  JSContext* initCx, const char16_t* chars, size_t length,
                                  JS::OffThreadCompileCallback callback, void* callbackData)
   : ParseTask(ParseTaskKind::Script, cx, exclusiveContextGlobal, initCx, chars, length, callback,
               callbackData)
 {
 }
 
 void
 ScriptParseTask::parse()
 {
     SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
     script = frontend::CompileGlobalScript(cx, alloc, ScopeKind::Global,
                                            options, srcBuf,
                                            /* extraSct = */ nullptr,
-                                           /* sourceObjectOut = */ sourceObject.address());
+                                           /* sourceObjectOut = */ &sourceObject);
 }
 
 ModuleParseTask::ModuleParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
                                  JSContext* initCx, const char16_t* chars, size_t length,
                                  JS::OffThreadCompileCallback callback, void* callbackData)
   : ParseTask(ParseTaskKind::Module, cx, exclusiveContextGlobal, initCx, chars, length, callback,
               callbackData)
 {
 }
 
 void
 ModuleParseTask::parse()
 {
     SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
-    ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc,
-                                                   sourceObject.address());
+    ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject);
     if (module)
         script = module->script();
 }
 
 void
 js::CancelOffThreadParses(JSRuntime* rt)
 {
     AutoLockHelperThreadState lock;
@@ -1669,16 +1681,32 @@ GlobalHelperThreadState::compressionTask
         SourceCompressionTask* task = thread.compressionTask();
         if (task && task->source() == ss)
             return task;
     }
     return nullptr;
 }
 
 void
+GlobalHelperThreadState::trace(JSTracer* trc)
+{
+    AutoLockHelperThreadState lock;
+    for (auto builder : ionWorklist(lock))
+        builder->trace(trc);
+    for (auto builder : ionFinishedList(lock))
+        builder->trace(trc);
+    for (auto parseTask : parseWorklist_)
+        parseTask->trace(trc);
+    for (auto parseTask : parseFinishedList_)
+        parseTask->trace(trc);
+    for (auto parseTask : parseWaitingOnGC_)
+        parseTask->trace(trc);
+}
+
+void
 HelperThread::handleGCHelperWorkload(AutoLockHelperThreadState& locked)
 {
     MOZ_ASSERT(HelperThreadState().canStartGCHelperTask(locked));
     MOZ_ASSERT(idle());
 
     currentTask.emplace(HelperThreadState().gcHelperWorklist(locked).popCopy());
     GCHelperState* task = gcHelperTask();
 
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -225,16 +225,18 @@ class GlobalHelperThreadState
 
     JSScript* finishParseTask(JSContext* cx, ParseTaskKind kind, void* token);
     void cancelParseTask(JSContext* cx, ParseTaskKind kind, void* token);
 
     void mergeParseTaskCompartment(JSContext* cx, ParseTask* parseTask,
                                    Handle<GlobalObject*> global,
                                    JSCompartment* dest);
 
+    void trace(JSTracer* trc);
+
   private:
     /*
      * Number of wasm jobs that encountered failure for the active module.
      * Their parent is logically the main thread, and this number serves for harvesting.
      */
     uint32_t numWasmFailedJobs;
 
   public:
@@ -485,29 +487,29 @@ struct ParseTask
     ParseTaskKind kind;
     ExclusiveContext* cx;
     OwningCompileOptions options;
     const char16_t* chars;
     size_t length;
     LifoAlloc alloc;
 
     // Rooted pointer to the global object used by 'cx'.
-    PersistentRootedObject exclusiveContextGlobal;
+    JSObject* exclusiveContextGlobal;
 
     // Callback invoked off the main thread when the parse finishes.
     JS::OffThreadCompileCallback callback;
     void* callbackData;
 
     // Holds the final script between the invocation of the callback and the
     // point where FinishOffThreadScript is called, which will destroy the
     // ParseTask.
-    PersistentRootedScript script;
+    JSScript* script;
 
     // Holds the ScriptSourceObject generated for the script compilation.
-    PersistentRooted<ScriptSourceObject*> sourceObject;
+    ScriptSourceObject* sourceObject;
 
     // Any errors or warnings produced during compilation. These are reported
     // when finishing the script.
     Vector<frontend::CompileError*> errors;
     bool overRecursed;
     bool outOfMemory;
 
     ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
@@ -519,16 +521,18 @@ struct ParseTask
     virtual void parse() = 0;
     bool finish(JSContext* cx);
 
     bool runtimeMatches(JSRuntime* rt) {
         return exclusiveContextGlobal->runtimeFromAnyThread() == rt;
     }
 
     virtual ~ParseTask();
+
+    void trace(JSTracer* trc);
 };
 
 struct ScriptParseTask : public ParseTask
 {
     ScriptParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
                     JSContext* initCx, const char16_t* chars, size_t length,
                     JS::OffThreadCompileCallback callback, void* callbackData);
     void parse() override;