1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "jit/IonCompileTask.h"
8
9 #include "jit/CodeGenerator.h"
10 #include "jit/CompileInfo.h"
11 #include "jit/Ion.h"
12 #include "jit/JitRuntime.h"
13 #include "jit/JitScript.h"
14 #include "jit/WarpSnapshot.h"
15 #include "vm/HelperThreadState.h"
16 #include "vm/JSScript.h"
17
18 #include "vm/JSScript-inl.h"
19
20 using namespace js;
21 using namespace js::jit;
22
runHelperThreadTask(AutoLockHelperThreadState & locked)23 void IonCompileTask::runHelperThreadTask(AutoLockHelperThreadState& locked) {
24 // The build is taken by this thread. Unfreeze the LifoAlloc to allow
25 // mutations.
26 alloc().lifoAlloc()->setReadWrite();
27
28 {
29 AutoUnlockHelperThreadState unlock(locked);
30 runTask();
31 }
32
33 FinishOffThreadIonCompile(this, locked);
34
35 JSRuntime* rt = script()->runtimeFromAnyThread();
36
37 // Ping the main thread so that the compiled code can be incorporated at the
38 // next interrupt callback.
39 //
40 // This must happen before the current task is reset. DestroyContext
41 // cancels in progress Ion compilations before destroying its target
42 // context, and after we reset the current task we are no longer considered
43 // to be Ion compiling.
44 rt->mainContextFromAnyThread()->requestInterrupt(
45 InterruptReason::AttachIonCompilations);
46 }
47
runTask()48 void IonCompileTask::runTask() {
49 // This is the entry point when ion compiles are run offthread.
50 TraceLoggerThread* logger = TraceLoggerForCurrentThread();
51 TraceLoggerEvent event(TraceLogger_AnnotateScripts, script());
52 AutoTraceLog logScript(logger, event);
53 AutoTraceLog logCompile(logger, TraceLogger_IonCompilation);
54
55 jit::JitContext jctx(mirGen_.realm->runtime(), mirGen_.realm, &alloc());
56 setBackgroundCodegen(jit::CompileBackEnd(&mirGen_, snapshot_));
57 }
58
trace(JSTracer * trc)59 void IonCompileTask::trace(JSTracer* trc) {
60 if (!mirGen_.runtime->runtimeMatches(trc->runtime())) {
61 return;
62 }
63
64 snapshot_->trace(trc);
65 }
66
IonCompileTask(MIRGenerator & mirGen,WarpSnapshot * snapshot)67 IonCompileTask::IonCompileTask(MIRGenerator& mirGen, WarpSnapshot* snapshot)
68 : mirGen_(mirGen), snapshot_(snapshot) {}
69
sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)70 size_t IonCompileTask::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
71 // See js::jit::FreeIonCompileTask.
72 // The IonCompileTask and most of its contents live in the LifoAlloc we point
73 // to.
74
75 size_t result = alloc().lifoAlloc()->sizeOfIncludingThis(mallocSizeOf);
76
77 if (backgroundCodegen_) {
78 result += mallocSizeOf(backgroundCodegen_);
79 }
80
81 return result;
82 }
83
TooManyUnlinkedTasks(JSRuntime * rt)84 static inline bool TooManyUnlinkedTasks(JSRuntime* rt) {
85 static const size_t MaxUnlinkedTasks = 100;
86 return rt->jitRuntime()->ionLazyLinkListSize() > MaxUnlinkedTasks;
87 }
88
MoveFinishedTasksToLazyLinkList(JSRuntime * rt,const AutoLockHelperThreadState & lock)89 static void MoveFinishedTasksToLazyLinkList(
90 JSRuntime* rt, const AutoLockHelperThreadState& lock) {
91 // Incorporate any off thread compilations for the runtime which have
92 // finished, failed or have been cancelled.
93
94 GlobalHelperThreadState::IonCompileTaskVector& finished =
95 HelperThreadState().ionFinishedList(lock);
96
97 for (size_t i = 0; i < finished.length(); i++) {
98 // Find a finished task for the runtime.
99 IonCompileTask* task = finished[i];
100 if (task->script()->runtimeFromAnyThread() != rt) {
101 continue;
102 }
103
104 HelperThreadState().remove(finished, &i);
105 rt->jitRuntime()->numFinishedOffThreadTasksRef(lock)--;
106
107 JSScript* script = task->script();
108 MOZ_ASSERT(script->hasBaselineScript());
109 script->baselineScript()->setPendingIonCompileTask(rt, script, task);
110 rt->jitRuntime()->ionLazyLinkListAdd(rt, task);
111 }
112 }
113
EagerlyLinkExcessTasks(JSContext * cx,AutoLockHelperThreadState & lock)114 static void EagerlyLinkExcessTasks(JSContext* cx,
115 AutoLockHelperThreadState& lock) {
116 JSRuntime* rt = cx->runtime();
117 MOZ_ASSERT(TooManyUnlinkedTasks(rt));
118
119 do {
120 jit::IonCompileTask* task = rt->jitRuntime()->ionLazyLinkList(rt).getLast();
121 RootedScript script(cx, task->script());
122
123 AutoUnlockHelperThreadState unlock(lock);
124 AutoRealm ar(cx, script);
125 jit::LinkIonScript(cx, script);
126 } while (TooManyUnlinkedTasks(rt));
127 }
128
AttachFinishedCompilations(JSContext * cx)129 void jit::AttachFinishedCompilations(JSContext* cx) {
130 JSRuntime* rt = cx->runtime();
131 MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
132
133 if (!rt->jitRuntime() || !rt->jitRuntime()->numFinishedOffThreadTasks()) {
134 return;
135 }
136
137 AutoLockHelperThreadState lock;
138
139 while (true) {
140 MoveFinishedTasksToLazyLinkList(rt, lock);
141
142 if (!TooManyUnlinkedTasks(rt)) {
143 break;
144 }
145
146 EagerlyLinkExcessTasks(cx, lock);
147
148 // Linking releases the lock so we must now check the finished list
149 // again.
150 }
151
152 MOZ_ASSERT(!rt->jitRuntime()->numFinishedOffThreadTasks());
153 }
154
FreeIonCompileTask(IonCompileTask * task)155 void jit::FreeIonCompileTask(IonCompileTask* task) {
156 // The task is allocated into its LifoAlloc, so destroying that will
157 // destroy the task and all other data accumulated during compilation,
158 // except any final codegen (which includes an assembler and needs to be
159 // explicitly destroyed).
160 js_delete(task->backgroundCodegen());
161 js_delete(task->alloc().lifoAlloc());
162 }
163
runHelperThreadTask(AutoLockHelperThreadState & locked)164 void IonFreeTask::runHelperThreadTask(AutoLockHelperThreadState& locked) {
165 {
166 AutoUnlockHelperThreadState unlock(locked);
167 jit::FreeIonCompileTask(task_);
168 }
169
170 js_delete(this);
171 }
172
FinishOffThreadTask(JSRuntime * runtime,IonCompileTask * task,const AutoLockHelperThreadState & locked)173 void jit::FinishOffThreadTask(JSRuntime* runtime, IonCompileTask* task,
174 const AutoLockHelperThreadState& locked) {
175 MOZ_ASSERT(runtime);
176
177 JSScript* script = task->script();
178
179 // Clean the references to the pending IonCompileTask, if we just finished it.
180 if (script->baselineScript()->hasPendingIonCompileTask() &&
181 script->baselineScript()->pendingIonCompileTask() == task) {
182 script->baselineScript()->removePendingIonCompileTask(runtime, script);
183 }
184
185 // If the task is still in one of the helper thread lists, then remove it.
186 if (task->isInList()) {
187 runtime->jitRuntime()->ionLazyLinkListRemove(runtime, task);
188 }
189
190 // Clean up if compilation did not succeed.
191 if (script->isIonCompilingOffThread()) {
192 script->jitScript()->clearIsIonCompilingOffThread(script);
193
194 const AbortReasonOr<Ok>& status = task->mirGen().getOffThreadStatus();
195 if (status.isErr() && status.inspectErr() == AbortReason::Disable) {
196 script->disableIon();
197 }
198 }
199
200 // Free Ion LifoAlloc off-thread. Free on the main thread if this OOMs.
201 if (!StartOffThreadIonFree(task, locked)) {
202 FreeIonCompileTask(task);
203 }
204 }
205