tor-browser

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

InvalidatingFuse.cpp (4602B)


      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 "vm/InvalidatingFuse.h"
      8 
      9 #include "gc/PublicIterators.h"
     10 #include "jit/Invalidation.h"
     11 #include "jit/JitSpewer.h"
     12 #include "vm/JSContext.h"
     13 #include "vm/JSScript.h"
     14 #include "vm/Logging.h"
     15 
     16 #include "gc/StableCellHasher-inl.h"
     17 #include "vm/JSScript-inl.h"
     18 
     19 js::FuseDependentIonScriptSet::FuseDependentIonScriptSet(JSContext* cx,
     20                                                         InvalidatingFuse* fuse)
     21    : associatedFuse(fuse), ionScripts(cx->runtime()) {}
     22 
     23 bool js::InvalidatingRuntimeFuse::addFuseDependency(
     24    JSContext* cx, const jit::IonScriptKey& ionScript) {
     25  MOZ_ASSERT(ionScript.script()->zone() == cx->zone());
     26 
     27  auto* scriptSet =
     28      cx->zone()->fuseDependencies.getOrCreateDependentScriptSet(cx, this);
     29  if (!scriptSet) {
     30    return false;
     31  }
     32 
     33  return scriptSet->addScriptForFuse(this, ionScript);
     34 }
     35 
     36 // This method will pop the fuse and iterates over its dependencies
     37 // iff this fuse is still intact.
     38 // This avoids a costly iteration over dependencies in subsequent calls.
     39 void js::InvalidatingRuntimeFuse::popFuse(JSContext* cx) {
     40  if (!intact()) {
     41    return;
     42  }
     43 
     44  // Pop the fuse in the base class
     45  GuardFuse::popFuse(cx);
     46  JS_LOG(fuseInvalidation, Verbose, "Invalidating fuse popping: %s", name());
     47  // do invalidation.
     48  for (AllZonesIter z(cx->runtime()); !z.done(); z.next()) {
     49    // There's one dependent script set per fuse; just iterate over them all to
     50    // find the one we need (see comment on JS::Zone::fuseDependencies for
     51    // reasoning).
     52    for (auto& fd : z.get()->fuseDependencies) {
     53      fd.invalidateForFuse(cx, this);
     54    }
     55  }
     56 }
     57 
     58 void js::FuseDependentIonScriptSet::invalidateForFuse(JSContext* cx,
     59                                                      InvalidatingFuse* fuse) {
     60  if (associatedFuse != fuse) {
     61    return;
     62  }
     63  ionScripts.get().invalidateAndClear(cx, "fuse");
     64 }
     65 
     66 void js::jit::DependentIonScriptSet::invalidateAndClear(JSContext* cx,
     67                                                        const char* reason) {
     68  for (const auto& ionScriptKey : ionScripts_) {
     69    IonScript* ionScript = ionScriptKey.maybeIonScriptToInvalidate();
     70    if (ionScript) {
     71      JSScript* script = ionScriptKey.script();
     72      JitSpew(jit::JitSpew_IonInvalidate, "Invalidating ion script %p for %s",
     73              ionScript, reason);
     74      JS_LOG(fuseInvalidation, Debug,
     75             "Invalidating ion script %s:%d for reason %s", script->filename(),
     76             script->lineno(), reason);
     77    }
     78  }
     79  js::jit::Invalidate(cx, ionScripts_);
     80  ionScripts_.clearAndFree();
     81 }
     82 
     83 bool js::FuseDependentIonScriptSet::addScriptForFuse(
     84    InvalidatingFuse* fuse, const jit::IonScriptKey& ionScript) {
     85  MOZ_ASSERT(fuse == associatedFuse);
     86  return ionScripts.get().addToSet(ionScript);
     87 }
     88 
     89 js::FuseDependentIonScriptSet*
     90 js::DependentIonScriptGroup::getOrCreateDependentScriptSet(
     91    JSContext* cx, js::InvalidatingFuse* fuse) {
     92  for (auto& dss : dependencies) {
     93    if (dss.associatedFuse == fuse) {
     94      return &dss;
     95    }
     96  }
     97 
     98  if (!dependencies.emplaceBack(cx, fuse)) {
     99    return nullptr;
    100  }
    101 
    102  auto& dss = dependencies.back();
    103  MOZ_ASSERT(dss.associatedFuse == fuse);
    104  return &dss;
    105 }
    106 
    107 bool js::jit::DependentIonScriptSet::addToSet(const IonScriptKey& ionScript) {
    108  MOZ_ASSERT(lengthAfterLastCompaction_ <= ionScripts_.length());
    109 
    110  // If `ionScript` is already in the vector, it must be the last entry.
    111  if (!ionScripts_.empty() && ionScripts_.back() == ionScript) {
    112    return true;
    113  }
    114 
    115  // Assert `ionScript` is not in the vector. Limit this to the last 8 entries
    116  // to not slow down debug builds too much.
    117 #ifdef DEBUG
    118  size_t numToCheck = std::min<size_t>(ionScripts_.length(), 8);
    119  for (size_t i = 0; i < numToCheck; i++) {
    120    MOZ_ASSERT(ionScripts_[ionScripts_.length() - 1 - i] != ionScript);
    121  }
    122 #endif
    123 
    124  // Compact the vector if its size doubled since the last compaction. This
    125  // isn't required for correctness but it avoids keeping a lot of stale
    126  // entries around between GCs.
    127  if (ionScripts_.length() / 2 > lengthAfterLastCompaction_) {
    128    ionScripts_.eraseIf([](const IonScriptKey& ionScript) {
    129      return ionScript.maybeIonScriptToInvalidate() == nullptr;
    130    });
    131    lengthAfterLastCompaction_ = ionScripts_.length();
    132  }
    133 
    134  return ionScripts_.append(ionScript);
    135 }