tor-browser

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

JitZone.h (12178B)


      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 #ifndef jit_JitZone_h
      8 #define jit_JitZone_h
      9 
     10 #include "mozilla/Assertions.h"
     11 #include "mozilla/EnumeratedArray.h"
     12 #include "mozilla/HashFunctions.h"
     13 #include "mozilla/LinkedList.h"
     14 #include "mozilla/Maybe.h"
     15 #include "mozilla/MemoryReporting.h"
     16 
     17 #include <stddef.h>
     18 #include <stdint.h>
     19 #include <utility>
     20 
     21 #include "gc/Barrier.h"
     22 #include "gc/Marking.h"
     23 #include "jit/CacheIRAOT.h"
     24 #include "jit/ExecutableAllocator.h"
     25 #include "jit/ICStubSpace.h"
     26 #include "jit/Invalidation.h"
     27 #include "jit/JitScript.h"
     28 #include "js/AllocPolicy.h"
     29 #include "js/GCHashTable.h"
     30 #include "js/HashTable.h"
     31 #include "js/TracingAPI.h"
     32 #include "js/TypeDecls.h"
     33 #include "js/UniquePtr.h"
     34 #include "js/Utility.h"
     35 #include "threading/ProtectedData.h"
     36 
     37 namespace JS {
     38 struct CodeSizes;
     39 }
     40 
     41 namespace js {
     42 namespace jit {
     43 
     44 enum class CacheKind : uint8_t;
     45 class CacheIRStubInfo;
     46 class JitCode;
     47 class JitScript;
     48 
     49 enum class ICStubEngine : uint8_t {
     50  // Baseline IC, see BaselineIC.h.
     51  Baseline = 0,
     52 
     53  // Ion IC, see IonIC.h.
     54  IonIC
     55 };
     56 
     57 struct CacheIRStubKey : public DefaultHasher<CacheIRStubKey> {
     58  struct Lookup {
     59    CacheKind kind;
     60    ICStubEngine engine;
     61    const uint8_t* code;
     62    uint32_t length;
     63 
     64    Lookup(CacheKind kind, ICStubEngine engine, const uint8_t* code,
     65           uint32_t length)
     66        : kind(kind), engine(engine), code(code), length(length) {}
     67  };
     68 
     69  static HashNumber hash(const Lookup& l);
     70  static bool match(const CacheIRStubKey& entry, const Lookup& l);
     71 
     72  UniquePtr<CacheIRStubInfo, JS::FreePolicy> stubInfo;
     73 
     74  explicit CacheIRStubKey(CacheIRStubInfo* info) : stubInfo(info) {}
     75  CacheIRStubKey(CacheIRStubKey&& other)
     76      : stubInfo(std::move(other.stubInfo)) {}
     77 
     78  void operator=(CacheIRStubKey&& other) {
     79    stubInfo = std::move(other.stubInfo);
     80  }
     81 };
     82 
     83 struct BaselineCacheIRStubCodeMapGCPolicy {
     84  static bool traceWeak(JSTracer* trc, CacheIRStubKey*,
     85                        WeakHeapPtr<JitCode*>* value) {
     86    return TraceWeakEdge(trc, value, "traceWeak");
     87  }
     88 };
     89 
     90 enum JitScriptFilter : bool { SkipDyingScripts, IncludeDyingScripts };
     91 
     92 class JitZone {
     93 public:
     94  enum class StubKind : uint32_t {
     95    StringConcat = 0,
     96    RegExpMatcher,
     97    RegExpSearcher,
     98    RegExpExecMatch,
     99    RegExpExecTest,
    100    Count
    101  };
    102  template <typename Code>
    103  using Stubs =
    104      mozilla::EnumeratedArray<StubKind, Code, size_t(StubKind::Count)>;
    105 
    106 private:
    107  // Allocated space for CacheIR stubs.
    108  ICStubSpace stubSpace_;
    109 
    110  // Set of CacheIRStubInfo instances used by Ion stubs in this Zone.
    111  using IonCacheIRStubInfoSet =
    112      HashSet<CacheIRStubKey, CacheIRStubKey, SystemAllocPolicy>;
    113  IonCacheIRStubInfoSet ionCacheIRStubInfoSet_;
    114 
    115  // Map CacheIRStubKey to shared JitCode objects.
    116  using BaselineCacheIRStubCodeMap =
    117      GCHashMap<CacheIRStubKey, WeakHeapPtr<JitCode*>, CacheIRStubKey,
    118                SystemAllocPolicy, BaselineCacheIRStubCodeMapGCPolicy>;
    119  BaselineCacheIRStubCodeMap baselineCacheIRStubCodes_;
    120 
    121  // Executable allocator for all code except wasm code.
    122  MainThreadData<ExecutableAllocator> execAlloc_;
    123 
    124  // HashMap that maps scripts to compilations inlining those scripts.
    125  using InlinedScriptMap =
    126      GCHashMap<WeakHeapPtr<BaseScript*>, IonScriptKeyVector,
    127                StableCellHasher<WeakHeapPtr<BaseScript*>>, SystemAllocPolicy>;
    128  InlinedScriptMap inlinedCompilations_;
    129 
    130  mozilla::LinkedList<JitScript> jitScripts_;
    131 
    132  // The following two fields are a pair of associated scripts. If they are
    133  // non-null, we have bailed out from MGuardMultipleShapes. The inner and outer
    134  // scripts are either the same script (when no inlining happened) or else the
    135  // inner script was inlined into the outer script.
    136  //
    137  // This is used to distinguish a bailout from MGuardShapeList vs
    138  // MGuardMultipleShapes, and for monomorphic inlining we need to track the
    139  // outer script that inlined the inner script.
    140  WeakHeapPtr<JSScript*> lastStubFoldingBailoutInner_;
    141  WeakHeapPtr<JSScript*> lastStubFoldingBailoutOuter_;
    142 
    143  // The JitZone stores stubs to concatenate strings inline and perform RegExp
    144  // calls inline. These bake in zone specific pointers and can't be stored in
    145  // JitRuntime. They also are dependent on the value of 'initialStringHeap' and
    146  // must be flushed when its value changes.
    147  //
    148  // These are weak pointers. Ion compilations store strong references to stubs
    149  // they depend on in WarpSnapshot.
    150  Stubs<WeakHeapPtr<JitCode*>> stubs_;
    151 
    152  mozilla::Maybe<IonCompilationId> currentCompilationId_;
    153  bool keepJitScripts_ = false;
    154 
    155  // Whether AOT IC loading failed due to OOM; if so, disable
    156  // enforcing-AOT checks.
    157  bool incompleteAOTICs_ = false;
    158 
    159  gc::Heap initialStringHeap = gc::Heap::Tenured;
    160 
    161  JitCode* generateStringConcatStub(JSContext* cx);
    162  JitCode* generateRegExpMatcherStub(JSContext* cx);
    163  JitCode* generateRegExpSearcherStub(JSContext* cx);
    164  JitCode* generateRegExpExecMatchStub(JSContext* cx);
    165  JitCode* generateRegExpExecTestStub(JSContext* cx);
    166 
    167 public:
    168  explicit JitZone(JSContext* cx, bool zoneHasNurseryStrings) {
    169    setStringsCanBeInNursery(zoneHasNurseryStrings);
    170 #ifdef ENABLE_JS_AOT_ICS
    171    js::jit::FillAOTICs(cx, this);
    172 #endif
    173  }
    174  ~JitZone() {
    175    MOZ_ASSERT(jitScripts_.isEmpty());
    176    MOZ_ASSERT(!keepJitScripts_);
    177  }
    178 
    179  void traceWeak(JSTracer* trc, Zone* zone);
    180 
    181  void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
    182                              JS::CodeSizes* code, size_t* jitZone,
    183                              size_t* cacheIRStubs) const;
    184 
    185  ICStubSpace* stubSpace() { return &stubSpace_; }
    186 
    187  JitCode* getBaselineCacheIRStubCode(const CacheIRStubKey::Lookup& key,
    188                                      CacheIRStubInfo** stubInfo) {
    189    auto p = baselineCacheIRStubCodes_.lookup(key);
    190    if (p) {
    191      *stubInfo = p->key().stubInfo.get();
    192      return p->value();
    193    }
    194    *stubInfo = nullptr;
    195    return nullptr;
    196  }
    197  [[nodiscard]] bool putBaselineCacheIRStubCode(
    198      const CacheIRStubKey::Lookup& lookup, CacheIRStubKey& key,
    199      JitCode* stubCode) {
    200    auto p = baselineCacheIRStubCodes_.lookupForAdd(lookup);
    201    MOZ_ASSERT(!p);
    202    return baselineCacheIRStubCodes_.add(p, std::move(key), stubCode);
    203  }
    204 
    205  CacheIRStubInfo* getIonCacheIRStubInfo(const CacheIRStubKey::Lookup& key) {
    206    IonCacheIRStubInfoSet::Ptr p = ionCacheIRStubInfoSet_.lookup(key);
    207    return p ? p->stubInfo.get() : nullptr;
    208  }
    209  [[nodiscard]] bool putIonCacheIRStubInfo(const CacheIRStubKey::Lookup& lookup,
    210                                           CacheIRStubKey& key) {
    211    IonCacheIRStubInfoSet::AddPtr p =
    212        ionCacheIRStubInfoSet_.lookupForAdd(lookup);
    213    MOZ_ASSERT(!p);
    214    return ionCacheIRStubInfoSet_.add(p, std::move(key));
    215  }
    216  void purgeIonCacheIRStubInfo() { ionCacheIRStubInfoSet_.clearAndCompact(); }
    217 
    218  ExecutableAllocator& execAlloc() { return execAlloc_.ref(); }
    219  const ExecutableAllocator& execAlloc() const { return execAlloc_.ref(); }
    220 
    221  [[nodiscard]] bool addInlinedCompilation(const IonScriptKey& ionScriptKey,
    222                                           JSScript* inlined);
    223 
    224  IonScriptKeyVector* maybeInlinedCompilations(JSScript* inlined) {
    225    auto p = inlinedCompilations_.lookup(inlined);
    226    return p ? &p->value() : nullptr;
    227  }
    228 
    229  void removeInlinedCompilations(JSScript* inlined) {
    230    inlinedCompilations_.remove(inlined);
    231  }
    232 
    233  void noteStubFoldingBailout(JSScript* inner, JSScript* outer) {
    234    lastStubFoldingBailoutInner_ = inner;
    235    lastStubFoldingBailoutOuter_ = outer;
    236  }
    237  bool hasStubFoldingBailoutData(JSScript* inner) const {
    238    return lastStubFoldingBailoutInner_ &&
    239           lastStubFoldingBailoutInner_.get() == inner &&
    240           lastStubFoldingBailoutOuter_;
    241  }
    242  JSScript* stubFoldingBailoutOuter() const {
    243    MOZ_ASSERT(lastStubFoldingBailoutInner_);
    244    return lastStubFoldingBailoutOuter_.get();
    245  }
    246  void clearStubFoldingBailoutData() {
    247    lastStubFoldingBailoutInner_ = nullptr;
    248    lastStubFoldingBailoutOuter_ = nullptr;
    249  }
    250 
    251  void registerJitScript(JitScript* script) { jitScripts_.insertBack(script); }
    252 
    253  // Iterate over all JitScripts in this zone calling |f| on each, allowing |f|
    254  // to remove the script. The template parameter |filter| controls whether to
    255  // include dying JitScripts during GC sweeping. Be careful when using this not
    256  // to let GC things reachable from the JitScript escape - they may be gray.
    257  template <JitScriptFilter filter = SkipDyingScripts, typename F>
    258  void forEachJitScript(F&& f) {
    259    JitScript* script = jitScripts_.getFirst();
    260    while (script) {
    261      JitScript* next = script->getNext();
    262      if (filter == IncludeDyingScripts ||
    263          !gc::IsAboutToBeFinalizedUnbarriered(script->owningScript())) {
    264        f(script);
    265      }
    266      script = next;
    267    }
    268  }
    269 
    270  // Like forEachJitScript above, but abort if |f| returns false.
    271  template <JitScriptFilter filter = SkipDyingScripts, typename F>
    272  bool forEachJitScriptFallible(F&& f) {
    273    JitScript* script = jitScripts_.getFirst();
    274    while (script) {
    275      JitScript* next = script->getNext();
    276      if (filter == IncludeDyingScripts ||
    277          !gc::IsAboutToBeFinalizedUnbarriered(script->owningScript())) {
    278        if (!f(script)) {
    279          return false;
    280        }
    281      }
    282      script = next;
    283    }
    284    return true;
    285  }
    286 
    287  bool keepJitScripts() const { return keepJitScripts_; }
    288  void setKeepJitScripts(bool keep) { keepJitScripts_ = keep; }
    289 
    290  mozilla::Maybe<IonCompilationId> currentCompilationId() const {
    291    return currentCompilationId_;
    292  }
    293  mozilla::Maybe<IonCompilationId>& currentCompilationIdRef() {
    294    return currentCompilationId_;
    295  }
    296 
    297  void setIncompleteAOTICs() { incompleteAOTICs_ = true; }
    298  bool isIncompleteAOTICs() const { return incompleteAOTICs_; }
    299 
    300  void traceWeak(JSTracer* trc, JS::Realm* realm);
    301 
    302  void discardStubs() {
    303    for (WeakHeapPtr<JitCode*>& stubRef : stubs_) {
    304      stubRef = nullptr;
    305    }
    306  }
    307 
    308  bool hasStubs() const {
    309    for (const WeakHeapPtr<JitCode*>& stubRef : stubs_) {
    310      if (stubRef) {
    311        return true;
    312      }
    313    }
    314    return false;
    315  }
    316 
    317  void setStringsCanBeInNursery(bool allow) {
    318    MOZ_ASSERT(!hasStubs());
    319    initialStringHeap = allow ? gc::Heap::Default : gc::Heap::Tenured;
    320  }
    321 
    322  [[nodiscard]] JitCode* ensureStubExists(JSContext* cx, StubKind kind) {
    323    if (JitCode* code = stubs_[kind]) {
    324      return code;
    325    }
    326    switch (kind) {
    327      case StubKind::StringConcat:
    328        stubs_[kind] = generateStringConcatStub(cx);
    329        break;
    330      case StubKind::RegExpMatcher:
    331        stubs_[kind] = generateRegExpMatcherStub(cx);
    332        break;
    333      case StubKind::RegExpSearcher:
    334        stubs_[kind] = generateRegExpSearcherStub(cx);
    335        break;
    336      case StubKind::RegExpExecMatch:
    337        stubs_[kind] = generateRegExpExecMatchStub(cx);
    338        break;
    339      case StubKind::RegExpExecTest:
    340        stubs_[kind] = generateRegExpExecTestStub(cx);
    341        break;
    342      case StubKind::Count:
    343        MOZ_CRASH("Invalid kind");
    344    }
    345    return stubs_[kind];
    346  }
    347 
    348  static constexpr size_t offsetOfStringConcatStub() {
    349    return offsetof(JitZone, stubs_) +
    350           size_t(StubKind::StringConcat) * sizeof(uintptr_t);
    351  }
    352  static constexpr size_t offsetOfRegExpMatcherStub() {
    353    return offsetof(JitZone, stubs_) +
    354           size_t(StubKind::RegExpMatcher) * sizeof(uintptr_t);
    355  }
    356  static constexpr size_t offsetOfRegExpSearcherStub() {
    357    return offsetof(JitZone, stubs_) +
    358           size_t(StubKind::RegExpSearcher) * sizeof(uintptr_t);
    359  }
    360  static constexpr size_t offsetOfRegExpExecMatchStub() {
    361    return offsetof(JitZone, stubs_) +
    362           size_t(StubKind::RegExpExecMatch) * sizeof(uintptr_t);
    363  }
    364  static constexpr size_t offsetOfRegExpExecTestStub() {
    365    return offsetof(JitZone, stubs_) +
    366           size_t(StubKind::RegExpExecTest) * sizeof(uintptr_t);
    367  }
    368 };
    369 
    370 }  // namespace jit
    371 }  // namespace js
    372 
    373 #endif /* jit_JitZone_h */