diff options
Diffstat (limited to 'js/src/jit/IonCompileTask.cpp')
-rw-r--r-- | js/src/jit/IonCompileTask.cpp | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/js/src/jit/IonCompileTask.cpp b/js/src/jit/IonCompileTask.cpp new file mode 100644 index 0000000000..0e612f308b --- /dev/null +++ b/js/src/jit/IonCompileTask.cpp @@ -0,0 +1,213 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/IonCompileTask.h" + +#include "jit/CodeGenerator.h" +#include "jit/Ion.h" +#include "jit/JitRuntime.h" +#include "jit/JitScript.h" +#include "jit/WarpSnapshot.h" +#include "vm/HelperThreadState.h" +#include "vm/JSScript.h" + +#include "vm/JSScript-inl.h" +#include "vm/Realm-inl.h" + +using namespace js; +using namespace js::jit; + +void IonCompileTask::runHelperThreadTask(AutoLockHelperThreadState& locked) { + // The build is taken by this thread. Unfreeze the LifoAlloc to allow + // mutations. + alloc().lifoAlloc()->setReadWrite(); + + { + AutoUnlockHelperThreadState unlock(locked); + runTask(); + } + + FinishOffThreadIonCompile(this, locked); + + JSRuntime* rt = script()->runtimeFromAnyThread(); + + // Ping the main thread so that the compiled code can be incorporated at the + // next interrupt callback. + // + // This must happen before the current task is reset. DestroyContext + // cancels in progress Ion compilations before destroying its target + // context, and after we reset the current task we are no longer considered + // to be Ion compiling. + rt->mainContextFromAnyThread()->requestInterrupt( + InterruptReason::AttachIonCompilations); +} + +void IonCompileTask::runTask() { + // This is the entry point when ion compiles are run offthread. + + jit::JitContext jctx(mirGen_.realm->runtime()); + setBackgroundCodegen(jit::CompileBackEnd(&mirGen_, snapshot_)); +} + +void IonCompileTask::trace(JSTracer* trc) { + if (!mirGen_.runtime->runtimeMatches(trc->runtime())) { + return; + } + + snapshot_->trace(trc); +} + +IonCompileTask::IonCompileTask(JSContext* cx, MIRGenerator& mirGen, + WarpSnapshot* snapshot) + : mirGen_(mirGen), + snapshot_(snapshot), + isExecuting_(cx->isExecutingRef()) {} + +size_t IonCompileTask::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) { + // See js::jit::FreeIonCompileTask. + // The IonCompileTask and most of its contents live in the LifoAlloc we point + // to. + + size_t result = alloc().lifoAlloc()->sizeOfIncludingThis(mallocSizeOf); + + if (backgroundCodegen_) { + result += mallocSizeOf(backgroundCodegen_); + } + + return result; +} + +static inline bool TooManyUnlinkedTasks(JSRuntime* rt) { + static const size_t MaxUnlinkedTasks = 100; + return rt->jitRuntime()->ionLazyLinkListSize() > MaxUnlinkedTasks; +} + +static void MoveFinishedTasksToLazyLinkList( + JSRuntime* rt, const AutoLockHelperThreadState& lock) { + // Incorporate any off thread compilations for the runtime which have + // finished, failed or have been cancelled. + + GlobalHelperThreadState::IonCompileTaskVector& finished = + HelperThreadState().ionFinishedList(lock); + + for (size_t i = 0; i < finished.length(); i++) { + // Find a finished task for the runtime. + IonCompileTask* task = finished[i]; + if (task->script()->runtimeFromAnyThread() != rt) { + continue; + } + + HelperThreadState().remove(finished, &i); + rt->jitRuntime()->numFinishedOffThreadTasksRef(lock)--; + + JSScript* script = task->script(); + MOZ_ASSERT(script->hasBaselineScript()); + script->baselineScript()->setPendingIonCompileTask(rt, script, task); + rt->jitRuntime()->ionLazyLinkListAdd(rt, task); + } +} + +static void EagerlyLinkExcessTasks(JSContext* cx, + AutoLockHelperThreadState& lock) { + JSRuntime* rt = cx->runtime(); + MOZ_ASSERT(TooManyUnlinkedTasks(rt)); + + do { + jit::IonCompileTask* task = rt->jitRuntime()->ionLazyLinkList(rt).getLast(); + RootedScript script(cx, task->script()); + + AutoUnlockHelperThreadState unlock(lock); + AutoRealm ar(cx, script); + jit::LinkIonScript(cx, script); + } while (TooManyUnlinkedTasks(rt)); +} + +void jit::AttachFinishedCompilations(JSContext* cx) { + JSRuntime* rt = cx->runtime(); + MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt)); + + if (!rt->jitRuntime() || !rt->jitRuntime()->numFinishedOffThreadTasks()) { + return; + } + + AutoLockHelperThreadState lock; + + while (true) { + MoveFinishedTasksToLazyLinkList(rt, lock); + + if (!TooManyUnlinkedTasks(rt)) { + break; + } + + EagerlyLinkExcessTasks(cx, lock); + + // Linking releases the lock so we must now check the finished list + // again. + } + + MOZ_ASSERT(!rt->jitRuntime()->numFinishedOffThreadTasks()); +} + +static void FreeIonCompileTask(IonCompileTask* task) { + // The task is allocated into its LifoAlloc, so destroying that will + // destroy the task and all other data accumulated during compilation, + // except any final codegen (which includes an assembler and needs to be + // explicitly destroyed). + js_delete(task->backgroundCodegen()); + js_delete(task->alloc().lifoAlloc()); +} + +void jit::FreeIonCompileTasks(const IonFreeCompileTasks& tasks) { + MOZ_ASSERT(!tasks.empty()); + for (auto* task : tasks) { + FreeIonCompileTask(task); + } +} + +void IonFreeTask::runHelperThreadTask(AutoLockHelperThreadState& locked) { + { + AutoUnlockHelperThreadState unlock(locked); + jit::FreeIonCompileTasks(compileTasks()); + } + + js_delete(this); +} + +void jit::FinishOffThreadTask(JSRuntime* runtime, + AutoStartIonFreeTask& freeTask, + IonCompileTask* task) { + MOZ_ASSERT(runtime); + MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime)); + + JSScript* script = task->script(); + + // Clean the references to the pending IonCompileTask, if we just finished it. + if (script->baselineScript()->hasPendingIonCompileTask() && + script->baselineScript()->pendingIonCompileTask() == task) { + script->baselineScript()->removePendingIonCompileTask(runtime, script); + } + + // If the task is still in one of the helper thread lists, then remove it. + if (task->isInList()) { + runtime->jitRuntime()->ionLazyLinkListRemove(runtime, task); + } + + // Clean up if compilation did not succeed. + if (script->isIonCompilingOffThread()) { + script->jitScript()->clearIsIonCompilingOffThread(script); + + const AbortReasonOr<Ok>& status = task->mirGen().getOffThreadStatus(); + if (status.isErr() && status.inspectErr() == AbortReason::Disable) { + script->disableIon(); + } + } + + // Try to free the Ion LifoAlloc off-thread. Free on the main thread if this + // OOMs. + if (!freeTask.addIonCompileToFreeTaskBatch(task)) { + FreeIonCompileTask(task); + } +} |