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/. */
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"
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.
33 // ScriptKey is a hash on the filename+sourceStart.
34 using ScriptKey
= HashNumber
;
35 ScriptKey
getScriptKey(JSScript
* script
) const;
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.
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|.
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.
55 class IonHint
: public mozilla::LinkedListElement
<IonHint
> {
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;
63 // List of bytecode offsets that have been successfully inlined with
64 // a state of monomorphic inline.
65 Vector
<uint32_t, 0, SystemAllocPolicy
> monomorphicInlineOffsets
;
68 explicit IonHint(ScriptKey key
) { key_
= key
; }
70 void initThreshold(uint32_t threshold
) { threshold_
= threshold
; }
72 uint32_t threshold() { return threshold_
; }
74 void incThreshold(uint32_t inc
) {
75 uint32_t newThreshold
= threshold() + inc
;
76 threshold_
= (newThreshold
> JitOptions
.normalIonWarmUpThreshold
)
77 ? JitOptions
.normalIonWarmUpThreshold
81 bool hasSpaceForMonomorphicInlineEntry() {
82 return monomorphicInlineOffsets
.length() < MonomorphicInlineMaxEntries
;
85 bool hasMonomorphicInlineOffset(uint32_t offset
) {
86 for (uint32_t iterOffset
: monomorphicInlineOffsets
) {
87 if (iterOffset
== offset
) {
94 bool addMonomorphicInlineOffset(uint32_t newOffset
) {
95 MOZ_ASSERT(hasSpaceForMonomorphicInlineEntry());
97 if (hasMonomorphicInlineOffset(newOffset
)) {
100 return monomorphicInlineOffsets
.append(newOffset
);
104 MOZ_ASSERT(key_
!= 0, "Should have valid key.");
109 using ScriptToHintMap
=
110 HashMap
<ScriptKey
, IonHint
*, js::DefaultHasher
<ScriptKey
>,
111 js::SystemAllocPolicy
>;
112 using IonHintPriorityQueue
= mozilla::LinkedList
<IonHint
>;
114 static constexpr uint32_t InvalidationThresholdIncrement
= 500;
115 static constexpr uint32_t IonHintMaxEntries
= 5000;
116 static constexpr uint32_t MonomorphicInlineMaxEntries
= 16;
118 static uint32_t IonHintEagerThresholdValue(uint32_t lastStubCounter
,
119 bool hasPretenuredAllocSites
);
121 ScriptToHintMap ionHintMap_
;
122 IonHintPriorityQueue ionHintQueue_
;
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.
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.
138 static constexpr uint32_t EagerBaselineCacheSize_
= 16;
139 mozilla::BitBloomFilter
<EagerBaselineCacheSize_
, ScriptKey
> baselineHintMap_
;
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))))
147 static constexpr uint32_t MaxEntries_
= 4281;
148 static_assert(EagerBaselineCacheSize_
== 16 && MaxEntries_
== 4281,
149 "MaxEntries should be recalculated for given CacheSize.");
151 uint32_t baselineEntryCount_
= 0;
152 void incrementBaselineEntryCount();
154 void updateAsRecentlyUsed(IonHint
* hint
);
155 IonHint
* addIonHint(ScriptKey key
, ScriptToHintMap::AddPtr
& p
);
160 void setEagerBaselineHint(JSScript
* script
);
161 bool mightHaveEagerBaselineHint(JSScript
* script
) const;
163 bool recordIonCompilation(JSScript
* script
);
164 bool getIonThresholdHint(JSScript
* script
, uint32_t& thresholdOut
);
166 bool addMonomorphicInlineLocation(JSScript
* script
, BytecodeLocation loc
);
167 bool hasMonomorphicInlineHintAtOffset(JSScript
* script
, uint32_t offset
);
169 void recordInvalidation(JSScript
* script
);
172 } // namespace js::jit
173 #endif /* jit_JitHints_h */