summaryrefslogtreecommitdiffstats
path: root/js/src/jit/IonCompileTask.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/IonCompileTask.cpp')
-rw-r--r--js/src/jit/IonCompileTask.cpp213
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);
+ }
+}