diff --git a/js/public/Utility.h b/js/public/Utility.h index 14e29dd41..a9d889df1 100644 --- a/js/public/Utility.h +++ b/js/public/Utility.h @@ -68,6 +68,7 @@ enum ThreadType { THREAD_TYPE_GCHELPER, // 6 THREAD_TYPE_GCPARALLEL, // 7 THREAD_TYPE_PROMISE_TASK, // 8 + THREAD_TYPE_ION_FREE, // 9 THREAD_TYPE_MAX // Used to check shell function arguments }; diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 01ea5b20b..077d891ff 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -474,6 +474,17 @@ JitCompartment::ensureIonStubsExist(JSContext* cx) return true; } +void +jit::FreeIonBuilder(IonBuilder* builder) +{ + // The builder is allocated into its LifoAlloc, so destroying that will + // destroy the builder and all other data accumulated during compilation, + // except any final codegen (which includes an assembler and needs to be + // explicitly destroyed). + js_delete(builder->backgroundCodegen()); + js_delete(builder->alloc().lifoAlloc()); +} + void jit::FinishOffThreadBuilder(JSRuntime* runtime, IonBuilder* builder, const AutoLockHelperThreadState& locked) @@ -505,12 +516,9 @@ jit::FinishOffThreadBuilder(JSRuntime* runtime, IonBuilder* builder, builder->script()->setIonScript(runtime, ion); } - // The builder is allocated into its LifoAlloc, so destroying that will - // destroy the builder and all other data accumulated during compilation, - // except any final codegen (which includes an assembler and needs to be - // explicitly destroyed). - js_delete(builder->backgroundCodegen()); - js_delete(builder->alloc().lifoAlloc()); + // Free Ion LifoAlloc off-thread. Free on the main thread if this OOMs. + if (!StartOffThreadIonFree(builder, locked)) + FreeIonBuilder(builder); } static bool diff --git a/js/src/jit/Ion.h b/js/src/jit/Ion.h index bff01c944..44c205864 100644 --- a/js/src/jit/Ion.h +++ b/js/src/jit/Ion.h @@ -171,6 +171,7 @@ CodeGenerator* CompileBackEnd(MIRGenerator* mir); void AttachFinishedCompilations(JSContext* cx); void FinishOffThreadBuilder(JSRuntime* runtime, IonBuilder* builder, const AutoLockHelperThreadState& lock); +void FreeIonBuilder(IonBuilder* builder); void LinkIonScript(JSContext* cx, HandleScript calleescript); uint8_t* LazyLinkTopActivation(JSContext* cx); diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp index a1cc7e75a..2ad09c716 100644 --- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -110,6 +110,18 @@ js::StartOffThreadIonCompile(JSContext* cx, jit::IonBuilder* builder) return true; } +bool +js::StartOffThreadIonFree(jit::IonBuilder* builder, const AutoLockHelperThreadState& lock) +{ + MOZ_ASSERT(CanUseExtraThreads()); + + if (!HelperThreadState().ionFreeList(lock).append(builder)) + return false; + + HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock); + return true; +} + /* * Move an IonBuilder for which compilation has either finished, failed, or * been cancelled into the global finished compilation list. All off thread @@ -784,6 +796,14 @@ void GlobalHelperThreadState::finish() { finishThreads(); + + // Make sure there are no Ion free tasks left. We check this here because, + // unlike the other tasks, we don't explicitly block on this when + // destroying a runtime. + AutoLockHelperThreadState lock; + auto& freeList = ionFreeList(lock); + while (!freeList.empty()) + jit::FreeIonBuilder(freeList.popCopy()); } void @@ -994,6 +1014,12 @@ GlobalHelperThreadState::canStartIonCompile(const AutoLockHelperThreadState& loc checkTaskThreadLimit(maxIonCompilationThreads()); } +bool +GlobalHelperThreadState::canStartIonFreeTask(const AutoLockHelperThreadState& lock) +{ + return !ionFreeList(lock).empty(); +} + jit::IonBuilder* GlobalHelperThreadState::highestPriorityPendingIonCompile(const AutoLockHelperThreadState& lock, bool remove /* = false */) @@ -1610,6 +1636,21 @@ CurrentHelperThread() return thread; } +void +HelperThread::handleIonFreeWorkload(AutoLockHelperThreadState& locked) +{ + MOZ_ASSERT(idle()); + MOZ_ASSERT(HelperThreadState().canStartIonFreeTask(locked)); + + auto& freeList = HelperThreadState().ionFreeList(locked); + + jit::IonBuilder* builder = freeList.popCopy(); + { + AutoUnlockHelperThreadState unlock(locked); + FreeIonBuilder(builder); + } +} + void js::PauseCurrentHelperThread() { @@ -1925,6 +1966,8 @@ HelperThread::threadLoop() task = js::oom::THREAD_TYPE_PARSE; else if (HelperThreadState().canStartCompressionTask(lock)) task = js::oom::THREAD_TYPE_COMPRESS; + else if (HelperThreadState().canStartIonFreeTask(lock)) + task = js::oom::THREAD_TYPE_ION_FREE; else task = js::oom::THREAD_TYPE_NONE; @@ -1957,6 +2000,9 @@ HelperThread::threadLoop() case js::oom::THREAD_TYPE_COMPRESS: handleCompressionWorkload(lock); break; + case js::oom::THREAD_TYPE_ION_FREE: + handleIonFreeWorkload(lock); + break; default: MOZ_CRASH("No task to perform"); } diff --git a/js/src/vm/HelperThreads.h b/js/src/vm/HelperThreads.h index 2c7d38769..ad7dcf3c5 100644 --- a/js/src/vm/HelperThreads.h +++ b/js/src/vm/HelperThreads.h @@ -84,7 +84,7 @@ class GlobalHelperThreadState // The lists below are all protected by |lock|. // Ion compilation worklist and finished jobs. - IonBuilderVector ionWorklist_, ionFinishedList_; + IonBuilderVector ionWorklist_, ionFinishedList_, ionFreeList_; // wasm worklist and finished jobs. wasm::CompileTaskPtrVector wasmWorklist_, wasmFinishedList_; @@ -165,6 +165,9 @@ class GlobalHelperThreadState IonBuilderVector& ionFinishedList(const AutoLockHelperThreadState&) { return ionFinishedList_; } + IonBuilderVector& ionFreeList(const AutoLockHelperThreadState&) { + return ionFreeList_; + } wasm::CompileTaskPtrVector& wasmWorklist(const AutoLockHelperThreadState&) { return wasmWorklist_; @@ -202,6 +205,7 @@ class GlobalHelperThreadState bool canStartWasmCompile(const AutoLockHelperThreadState& lock); bool canStartPromiseTask(const AutoLockHelperThreadState& lock); bool canStartIonCompile(const AutoLockHelperThreadState& lock); + bool canStartIonFreeTask(const AutoLockHelperThreadState& lock); bool canStartParseTask(const AutoLockHelperThreadState& lock); bool canStartCompressionTask(const AutoLockHelperThreadState& lock); bool canStartGCHelperTask(const AutoLockHelperThreadState& lock); @@ -382,6 +386,7 @@ struct HelperThread void handleWasmWorkload(AutoLockHelperThreadState& locked); void handlePromiseTaskWorkload(AutoLockHelperThreadState& locked); void handleIonWorkload(AutoLockHelperThreadState& locked); + void handleIonFreeWorkload(AutoLockHelperThreadState& locked); void handleParseWorkload(AutoLockHelperThreadState& locked, uintptr_t stackLimit); void handleCompressionWorkload(AutoLockHelperThreadState& locked); void handleGCHelperWorkload(AutoLockHelperThreadState& locked); @@ -439,6 +444,12 @@ StartPromiseTask(JSContext* cx, UniquePtr task); bool StartOffThreadIonCompile(JSContext* cx, jit::IonBuilder* builder); +/* + * Schedule deletion of Ion compilation data. + */ +bool +StartOffThreadIonFree(jit::IonBuilder* builder, const AutoLockHelperThreadState& lock); + struct AllCompilations {}; struct ZonesInState { JSRuntime* runtime; JS::Zone::GCState state; };