IonCompileTask.cpp (7725B)
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/Ion.h" 11 #include "jit/JitRuntime.h" 12 #include "jit/JitScript.h" 13 #include "jit/WarpSnapshot.h" 14 #include "vm/HelperThreadState.h" 15 #include "vm/JSScript.h" 16 17 #include "vm/JSScript-inl.h" 18 #include "vm/Realm-inl.h" 19 20 using namespace js; 21 using namespace js::jit; 22 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::AttachOffThreadCompilations); 46 } 47 48 void IonCompileTask::runTask() { 49 // This is the entry point when ion compiles are run offthread. 50 51 jit::JitContext jctx(mirGen_.realm->runtime()); 52 setBackgroundCodegen(jit::CompileBackEnd(&mirGen_, snapshot_)); 53 } 54 55 void IonCompileTask::trace(JSTracer* trc) { 56 if (!mirGen_.runtime->runtimeMatches(trc->runtime())) { 57 return; 58 } 59 60 snapshot_->trace(trc); 61 } 62 63 IonCompileTask::IonCompileTask(JSContext* cx, MIRGenerator& mirGen, 64 WarpSnapshot* snapshot) 65 : mirGen_(mirGen), 66 snapshot_(snapshot), 67 isExecuting_(cx->isExecutingRef()) {} 68 69 size_t IonCompileTask::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) { 70 // See js::jit::FreeIonCompileTask. 71 // The IonCompileTask and most of its contents live in the LifoAlloc we point 72 // to. 73 74 size_t result = alloc().lifoAlloc()->sizeOfIncludingThis(mallocSizeOf); 75 76 if (backgroundCodegen_) { 77 result += mallocSizeOf(backgroundCodegen_); 78 } 79 80 return result; 81 } 82 83 static inline bool TooManyUnlinkedTasks(JSRuntime* rt) { 84 static const size_t MaxUnlinkedTasks = 100; 85 return rt->jitRuntime()->ionLazyLinkListSize() > MaxUnlinkedTasks; 86 } 87 88 static void MoveFinishedTasksToLazyLinkList( 89 JSRuntime* rt, const AutoLockHelperThreadState& lock) { 90 // Incorporate any off thread compilations for the runtime which have 91 // finished, failed or have been cancelled. 92 93 GlobalHelperThreadState::IonCompileTaskVector& finished = 94 HelperThreadState().ionFinishedList(lock); 95 96 for (size_t i = 0; i < finished.length(); i++) { 97 // Find a finished task for the runtime. 98 IonCompileTask* task = finished[i]; 99 if (task->script()->runtimeFromAnyThread() != rt) { 100 continue; 101 } 102 103 HelperThreadState().remove(finished, &i); 104 rt->jitRuntime()->numFinishedOffThreadTasksRef(lock)--; 105 106 JSScript* script = task->script(); 107 MOZ_ASSERT(script->hasBaselineScript()); 108 script->baselineScript()->setPendingIonCompileTask(rt, script, task); 109 rt->jitRuntime()->ionLazyLinkListAdd(rt, task); 110 } 111 } 112 113 static void EagerlyLinkExcessTasks(JSContext* cx, 114 AutoLockHelperThreadState& lock) { 115 JSRuntime* rt = cx->runtime(); 116 MOZ_ASSERT(TooManyUnlinkedTasks(rt)); 117 118 do { 119 jit::IonCompileTask* task = rt->jitRuntime()->ionLazyLinkList(rt).getLast(); 120 RootedScript script(cx, task->script()); 121 122 AutoUnlockHelperThreadState unlock(lock); 123 AutoRealm ar(cx, script); 124 jit::LinkIonScript(cx, script); 125 } while (TooManyUnlinkedTasks(rt)); 126 } 127 128 void jit::AttachFinishedCompilations(JSContext* cx) { 129 JSRuntime* rt = cx->runtime(); 130 MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt)); 131 132 if (!rt->jitRuntime() || !rt->jitRuntime()->numFinishedOffThreadTasks()) { 133 return; 134 } 135 136 AutoLockHelperThreadState lock; 137 138 while (true) { 139 AttachFinishedBaselineCompilations(cx, lock); 140 141 MoveFinishedTasksToLazyLinkList(rt, lock); 142 143 if (!TooManyUnlinkedTasks(rt)) { 144 break; 145 } 146 147 EagerlyLinkExcessTasks(cx, lock); 148 149 // Linking releases the lock so we must now check the finished list 150 // again. 151 } 152 153 MOZ_ASSERT(!rt->jitRuntime()->numFinishedOffThreadTasks()); 154 } 155 156 static UniquePtr<LifoAlloc> FreeIonCompileTask(IonCompileTask* task) { 157 // To correctly free compilation dependencies, which may have virtual 158 // destructors we need to explicitly empty the MIRGenerator's list here. 159 task->mirGen().tracker.reset(); 160 161 // The task is allocated into its LifoAlloc, so destroying that will 162 // destroy the task and all other data accumulated during compilation, 163 // except any final codegen (which includes an assembler and needs to be 164 // explicitly destroyed). 165 js_delete(task->backgroundCodegen()); 166 167 // Return the LifoAlloc as UniquePtr. Callers can either reuse the LifoAlloc 168 // or ignore the return value. 169 return UniquePtr<LifoAlloc>(task->alloc().lifoAlloc()); 170 } 171 172 void jit::FreeIonCompileTasks(const IonFreeCompileTasks& tasks) { 173 MOZ_ASSERT(!tasks.empty()); 174 for (auto* task : tasks) { 175 FreeIonCompileTask(task); 176 } 177 } 178 179 UniquePtr<LifoAlloc> jit::FreeIonCompileTaskAndReuseLifoAlloc( 180 IonCompileTask* task) { 181 UniquePtr<LifoAlloc> lifoAlloc = FreeIonCompileTask(task); 182 183 // We have to call the TempAllocator's destructor first, because releaseAll 184 // can only be called if the LifoAlloc's mark-count is 0. Note that the 185 // TempAllocator is allocated in the LifoAlloc too. 186 // 187 // The LifoAllocScope's destructor calls freeAllIfHugeAndUnused and this will 188 // free all LifoAlloc memory immediately if the LifoAlloc is huge. That's not 189 // what we want here, so we rely on the caller ensuring !isHuge(). 190 MOZ_ASSERT(!lifoAlloc->isHuge()); 191 TempAllocator* tempAlloc = &task->alloc(); 192 tempAlloc->~TempAllocator(); 193 lifoAlloc->releaseAll(); 194 return lifoAlloc; 195 } 196 197 void IonFreeTask::runHelperThreadTask(AutoLockHelperThreadState& locked) { 198 { 199 AutoUnlockHelperThreadState unlock(locked); 200 jit::FreeIonCompileTasks(compileTasks()); 201 } 202 203 js_delete(this); 204 } 205 206 void jit::FinishOffThreadTask(JSRuntime* runtime, 207 AutoStartIonFreeTask& freeTask, 208 IonCompileTask* task) { 209 MOZ_ASSERT(runtime); 210 MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime)); 211 212 JSScript* script = task->script(); 213 214 // Clean the references to the pending IonCompileTask, if we just finished it. 215 if (script->baselineScript()->hasPendingIonCompileTask() && 216 script->baselineScript()->pendingIonCompileTask() == task) { 217 script->baselineScript()->removePendingIonCompileTask(runtime, script); 218 } 219 220 // If the task is still in one of the helper thread lists, then remove it. 221 if (task->isInList()) { 222 runtime->jitRuntime()->ionLazyLinkListRemove(runtime, task); 223 } 224 225 // Clean up if compilation did not succeed. 226 if (script->isIonCompilingOffThread()) { 227 script->jitScript()->clearIsIonCompilingOffThread(script); 228 229 const AbortReasonOr<Ok>& status = task->mirGen().getOffThreadStatus(); 230 if (status.isErr() && status.inspectErr() == AbortReason::Disable) { 231 script->disableIon(); 232 } 233 } 234 235 // Try to free the Ion LifoAlloc off-thread. Free on the main thread if this 236 // OOMs. 237 if (!freeTask.addIonCompileToFreeTaskBatch(task)) { 238 FreeIonCompileTask(task); 239 } 240 }