tor-browser

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

JitHints.h (6623B)


      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_JitHints_h
      8 #define jit_JitHints_h
      9 
     10 #include "mozilla/BloomFilter.h"
     11 #include "mozilla/HashTable.h"
     12 #include "mozilla/LinkedList.h"
     13 #include "jit/JitOptions.h"
     14 #include "vm/BytecodeLocation.h"
     15 #include "vm/JSScript.h"
     16 
     17 namespace js::jit {
     18 
     19 /*
     20 *
     21 * [SMDOC] JitHintsMap
     22 *
     23 * The Jit hints map is an in process cache used to collect Baseline and Ion
     24 * JIT hints to try and skip as much of the warmup as possible and jump
     25 * straight into those tiers.  Whenever a script enters one of these tiers
     26 * a hint is recorded in this cache using the script's filename+sourceStart
     27 * value, and if we ever encounter this script again later, e.g. during a
     28 * navigation, then we try to eagerly compile it into baseline and ion
     29 * based on its previous execution history.
     30 */
     31 
     32 class JitHintsMap {
     33  // ScriptKey is a hash on the filename+sourceStart.
     34  using ScriptKey = HashNumber;
     35  ScriptKey getScriptKey(JSScript* script) const;
     36 
     37  /* Ion Hints
     38   * -------------------------------------------------------------------------
     39   * This implementation uses a combination of a HashMap and PriorityQueue
     40   * to store a threshold value for each script that has been Ion compiled.
     41   * The PriorityQueue is used to track the least recently used entries so
     42   * that the cache does not exceed |IonHintMaxEntries| entries.
     43   *
     44   * After a script has entered Ion the first time, an eager threshold hint
     45   * value is set using the warmup counter of when the last IC stub was
     46   * attached, if available. This minimizes the risk that the script will
     47   * bailout. If that script is bailout invalidated, the threshold value
     48   * is incremented by |InvalidationThresholdIncrement| up to a maximum value of
     49   * |JitOptions.normalIonWarmUpThreshold|.
     50   *
     51   * Each IonHint object also contains a list of bytecode offsets for locations
     52   * of monomorphic inline calls that is used as a hint for future compilations.
     53   *
     54   */
     55  class IonHint : public mozilla::LinkedListElement<IonHint> {
     56    ScriptKey key_ = 0;
     57 
     58    // We use a value of 0 to indicate that the script has not entered Ion
     59    // yet, but has been monomorphically inlined and Ion compiled into
     60    // another script and contains bytecode offsets of a nested call.
     61    uint32_t threshold_ = 0;
     62 
     63    // List of bytecode offsets that have been successfully inlined with
     64    // a state of monomorphic inline.
     65    Vector<uint32_t, 0, SystemAllocPolicy> monomorphicInlineOffsets;
     66 
     67   public:
     68    explicit IonHint(ScriptKey key) { key_ = key; }
     69 
     70    void initThreshold(uint32_t threshold) { threshold_ = threshold; }
     71 
     72    uint32_t threshold() { return threshold_; }
     73 
     74    void incThreshold(uint32_t inc) {
     75      uint32_t newThreshold = threshold() + inc;
     76      threshold_ = (newThreshold > JitOptions.normalIonWarmUpThreshold)
     77                       ? JitOptions.normalIonWarmUpThreshold
     78                       : newThreshold;
     79    }
     80 
     81    bool hasSpaceForMonomorphicInlineEntry() {
     82      return monomorphicInlineOffsets.length() < MonomorphicInlineMaxEntries;
     83    }
     84 
     85    bool hasMonomorphicInlineOffset(uint32_t offset) {
     86      for (uint32_t iterOffset : monomorphicInlineOffsets) {
     87        if (iterOffset == offset) {
     88          return true;
     89        }
     90      }
     91      return false;
     92    }
     93 
     94    bool addMonomorphicInlineOffset(uint32_t newOffset) {
     95      MOZ_ASSERT(hasSpaceForMonomorphicInlineEntry());
     96 
     97      if (hasMonomorphicInlineOffset(newOffset)) {
     98        return true;
     99      }
    100      return monomorphicInlineOffsets.append(newOffset);
    101    }
    102 
    103    ScriptKey key() {
    104      MOZ_ASSERT(key_ != 0, "Should have valid key.");
    105      return key_;
    106    }
    107  };
    108 
    109  using ScriptToHintMap =
    110      HashMap<ScriptKey, IonHint*, js::DefaultHasher<ScriptKey>,
    111              js::SystemAllocPolicy>;
    112  using IonHintPriorityQueue = mozilla::LinkedList<IonHint>;
    113 
    114  static constexpr uint32_t InvalidationThresholdIncrement = 500;
    115  static constexpr uint32_t IonHintMaxEntries = 5000;
    116  static constexpr uint32_t MonomorphicInlineMaxEntries = 16;
    117 
    118  static uint32_t IonHintEagerThresholdValue(uint32_t lastStubCounter,
    119                                             bool hasPretenuredAllocSites);
    120 
    121  ScriptToHintMap ionHintMap_;
    122  IonHintPriorityQueue ionHintQueue_;
    123 
    124  /* Baseline Hints
    125   * --------------------------------------------------------------------------
    126   * This implementation uses a BitBloomFilter to track whether or not a script
    127   * has been baseline compiled before in the same process.  This can occur
    128   * frequently during navigations.
    129   *
    130   * The bloom filter allows us to have very efficient storage and lookup costs,
    131   * at the expense of occasional false positives.  Using a bloom filter also
    132   * allows us to have many more entries at minimal memory and allocation cost.
    133   * The number of entries added to the bloom filter is monitored in order to
    134   * try and keep the false positivity rate below 1%.  If the entry count
    135   * exceeds MaxEntries_, which indicates the false positivity rate may exceed
    136   * 1.5%, then the filter is completely cleared to reset the cache.
    137   */
    138  static constexpr uint32_t EagerBaselineCacheSize_ = 16;
    139  mozilla::BitBloomFilter<EagerBaselineCacheSize_, ScriptKey> baselineHintMap_;
    140 
    141  /*
    142   * MaxEntries_ is the approximate entry count for which the
    143   * false positivity rate will exceed p=0.015 using k=2 and m=2**CacheSize.
    144   * Formula is as follows:
    145   * MaxEntries_ = floor(m / (-k / ln(1-exp(ln(p) / k))))
    146   */
    147  static constexpr uint32_t MaxEntries_ = 4281;
    148  static_assert(EagerBaselineCacheSize_ == 16 && MaxEntries_ == 4281,
    149                "MaxEntries should be recalculated for given CacheSize.");
    150 
    151  uint32_t baselineEntryCount_ = 0;
    152  void incrementBaselineEntryCount();
    153 
    154  void updateAsRecentlyUsed(IonHint* hint);
    155  IonHint* addIonHint(ScriptKey key, ScriptToHintMap::AddPtr& p);
    156 
    157 public:
    158  ~JitHintsMap();
    159 
    160  void setEagerBaselineHint(JSScript* script);
    161  bool mightHaveEagerBaselineHint(JSScript* script) const;
    162 
    163  bool recordIonCompilation(JSScript* script);
    164  bool getIonThresholdHint(JSScript* script, uint32_t& thresholdOut);
    165 
    166  bool addMonomorphicInlineLocation(JSScript* script, BytecodeLocation loc);
    167  bool hasMonomorphicInlineHintAtOffset(JSScript* script, uint32_t offset);
    168 
    169  void recordInvalidation(JSScript* script);
    170 };
    171 
    172 }  // namespace js::jit
    173 #endif /* jit_JitHints_h */