summaryrefslogtreecommitdiffstats
path: root/js/src/jit/JitHints.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/JitHints.h')
-rw-r--r--js/src/jit/JitHints.h182
1 files changed, 182 insertions, 0 deletions
diff --git a/js/src/jit/JitHints.h b/js/src/jit/JitHints.h
new file mode 100644
index 0000000000..c2f1f1129d
--- /dev/null
+++ b/js/src/jit/JitHints.h
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef jit_JitHints_h
+#define jit_JitHints_h
+
+#include "mozilla/BloomFilter.h"
+#include "mozilla/HashTable.h"
+#include "mozilla/LinkedList.h"
+#include "jit/JitOptions.h"
+#include "vm/BytecodeLocation.h"
+#include "vm/JSScript.h"
+
+namespace js::jit {
+
+/*
+ *
+ * [SMDOC] JitHintsMap
+ *
+ * The Jit hints map is an in process cache used to collect Baseline and Ion
+ * JIT hints to try and skip as much of the warmup as possible and jump
+ * straight into those tiers. Whenever a script enters one of these tiers
+ * a hint is recorded in this cache using the script's filename+sourceStart
+ * value, and if we ever encounter this script again later, e.g. during a
+ * navigation, then we try to eagerly compile it into baseline and ion
+ * based on its previous execution history.
+ */
+
+class JitHintsMap {
+ // ScriptKey is a hash on the filename+sourceStart.
+ using ScriptKey = HashNumber;
+ ScriptKey getScriptKey(JSScript* script) const;
+
+ /* Ion Hints
+ * -------------------------------------------------------------------------
+ * This implementation uses a combination of a HashMap and PriorityQueue
+ * to store a threshold value for each script that has been Ion compiled.
+ * The PriorityQueue is used to track the least recently used entries so
+ * that the cache does not exceed |IonHintMaxEntries| entries.
+ *
+ * After a script has entered Ion the first time, an eager threshold hint
+ * value is set using the warmup counter of when the last IC stub was
+ * attached, if available. This minimizes the risk that the script will
+ * bailout. If that script is bailout invalidated, the threshold value
+ * is incremented by |InvalidationThresholdIncrement| up to a maximum value of
+ * |JitOptions.normalIonWarmUpThreshold|.
+ *
+ * Each IonHint object also contains a list of bytecode offsets for locations
+ * of monomorphic inline calls that is used as a hint for future compilations.
+ *
+ */
+ class IonHint : public mozilla::LinkedListElement<IonHint> {
+ ScriptKey key_ = 0;
+
+ // We use a value of 0 to indicate that the script has not entered Ion
+ // yet, but has been monomorphically inlined and Ion compiled into
+ // another script and contains bytecode offsets of a nested call.
+ uint32_t threshold_ = 0;
+
+ // List of bytecode offsets that have been successfully inlined with
+ // a state of monomorphic inline.
+ Vector<uint32_t, 0, SystemAllocPolicy> monomorphicInlineOffsets;
+
+ public:
+ explicit IonHint(ScriptKey key) { key_ = key; }
+
+ void initThreshold(uint32_t lastStubCounter) {
+ threshold_ = IonHintEagerThresholdValue(lastStubCounter);
+ }
+
+ uint32_t threshold() { return threshold_; }
+
+ void incThreshold(uint32_t inc) {
+ uint32_t newThreshold = threshold() + inc;
+ threshold_ = (newThreshold > JitOptions.normalIonWarmUpThreshold)
+ ? JitOptions.normalIonWarmUpThreshold
+ : newThreshold;
+ }
+
+ bool hasSpaceForMonomorphicInlineEntry() {
+ return monomorphicInlineOffsets.length() < MonomorphicInlineMaxEntries;
+ }
+
+ bool hasMonomorphicInlineOffset(uint32_t offset) {
+ for (uint32_t iterOffset : monomorphicInlineOffsets) {
+ if (iterOffset == offset) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool addMonomorphicInlineOffset(uint32_t newOffset) {
+ MOZ_ASSERT(hasSpaceForMonomorphicInlineEntry());
+
+ if (hasMonomorphicInlineOffset(newOffset)) {
+ return true;
+ }
+ return monomorphicInlineOffsets.append(newOffset);
+ }
+
+ ScriptKey key() {
+ MOZ_ASSERT(key_ != 0, "Should have valid key.");
+ return key_;
+ }
+ };
+
+ using ScriptToHintMap =
+ HashMap<ScriptKey, IonHint*, js::DefaultHasher<ScriptKey>,
+ js::SystemAllocPolicy>;
+ using IonHintPriorityQueue = mozilla::LinkedList<IonHint>;
+
+ static constexpr uint32_t InvalidationThresholdIncrement = 500;
+ static constexpr uint32_t IonHintMaxEntries = 5000;
+ static constexpr uint32_t MonomorphicInlineMaxEntries = 16;
+
+ static uint32_t IonHintEagerThresholdValue(uint32_t lastStubCounter) {
+ // Use the counter when the last IC stub was attached but add 10
+ // for some wiggle room and to safeguard against cases where the
+ // lastStubCounter is 0.
+ uint32_t eagerThreshold = lastStubCounter + 10;
+
+ // Do not exceed the default Ion threshold value set in the options.
+ return std::min(eagerThreshold, JitOptions.normalIonWarmUpThreshold);
+ }
+
+ ScriptToHintMap ionHintMap_;
+ IonHintPriorityQueue ionHintQueue_;
+
+ /* Baseline Hints
+ * --------------------------------------------------------------------------
+ * This implementation uses a BitBloomFilter to track whether or not a script
+ * has been baseline compiled before in the same process. This can occur
+ * frequently during navigations.
+ *
+ * The bloom filter allows us to have very efficient storage and lookup costs,
+ * at the expense of occasional false positives. Using a bloom filter also
+ * allows us to have many more entries at minimal memory and allocation cost.
+ * The number of entries added to the bloom filter is monitored in order to
+ * try and keep the false positivity rate below 1%. If the entry count
+ * exceeds MaxEntries_, which indicates the false positivity rate may exceed
+ * 1.5%, then the filter is completely cleared to reset the cache.
+ */
+ static constexpr uint32_t EagerBaselineCacheSize_ = 16;
+ mozilla::BitBloomFilter<EagerBaselineCacheSize_, ScriptKey> baselineHintMap_;
+
+ /*
+ * MaxEntries_ is the approximate entry count for which the
+ * false positivity rate will exceed p=0.015 using k=2 and m=2**CacheSize.
+ * Formula is as follows:
+ * MaxEntries_ = floor(m / (-k / ln(1-exp(ln(p) / k))))
+ */
+ static constexpr uint32_t MaxEntries_ = 4281;
+ static_assert(EagerBaselineCacheSize_ == 16 && MaxEntries_ == 4281,
+ "MaxEntries should be recalculated for given CacheSize.");
+
+ uint32_t baselineEntryCount_ = 0;
+ void incrementBaselineEntryCount();
+
+ void updateAsRecentlyUsed(IonHint* hint);
+ IonHint* addIonHint(ScriptKey key, ScriptToHintMap::AddPtr& p);
+
+ public:
+ ~JitHintsMap();
+
+ void setEagerBaselineHint(JSScript* script);
+ bool mightHaveEagerBaselineHint(JSScript* script) const;
+
+ bool recordIonCompilation(JSScript* script);
+ bool getIonThresholdHint(JSScript* script, uint32_t& thresholdOut);
+
+ bool addMonomorphicInlineLocation(JSScript* script, BytecodeLocation loc);
+ bool hasMonomorphicInlineHintAtOffset(JSScript* script, uint32_t offset);
+
+ void recordInvalidation(JSScript* script);
+};
+
+} // namespace js::jit
+#endif /* jit_JitHints_h */