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 */