tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }