summaryrefslogtreecommitdiffstats
path: root/js/src/vm/StencilCache.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/StencilCache.h')
-rw-r--r--js/src/vm/StencilCache.h197
1 files changed, 197 insertions, 0 deletions
diff --git a/js/src/vm/StencilCache.h b/js/src/vm/StencilCache.h
new file mode 100644
index 0000000000..bc9f50e21b
--- /dev/null
+++ b/js/src/vm/StencilCache.h
@@ -0,0 +1,197 @@
+/* -*- 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 vm_StencilCache_h
+#define vm_StencilCache_h
+
+#include "mozilla/Atomics.h" // mozilla::Atomic
+#include "mozilla/HashFunctions.h" // mozilla::HashGeneric
+#include "mozilla/RefPtr.h" // mozilla::RefPtr
+
+#include "js/HashTable.h" // js::HashTable
+
+#include "threading/ExclusiveData.h" // js::ExclusiveData
+
+#include "vm/JSScript.h" // js::ScriptSource
+#include "vm/SharedStencil.h" // js::SourceExtent
+
+struct JS_PUBLIC_API JSContext; // vm/JSContext.h
+
+namespace js {
+
+namespace frontend {
+struct CompilationStencil; // frontend/CompilationStencil.h
+struct ExtensibleCompilationStencil; // frontend/CompilationStencil.h
+} /* namespace frontend */
+
+// Note, the key is a RefPtr<ScriptSource>, but neither the PointerHasher nor
+// DefaultHasher seems to handle these correctly.
+struct SourceCachePolicy {
+ using Lookup = const ScriptSource*;
+
+ static HashNumber hash(const Lookup& l) { return mozilla::HashGeneric(l); }
+ static bool match(const Lookup& entry, const Lookup& l) { return entry == l; }
+};
+
+// Immutable information to identify a unique function, as well as the
+// compilation context which will result in a unique Stencil once compiled.
+struct StencilContext {
+ // This pointer is used to isolate a single source. Note the uniqueness of the
+ // ReadOnlyCompileOptions is implied by the fact that we allocate a new
+ // ScriptSource for each CompilationInput, which are initialized with a set of
+ // CompileOptions.
+ RefPtr<ScriptSource> source;
+
+ SourceExtent::FunctionKey funKey;
+
+ StencilContext(RefPtr<ScriptSource>& source, SourceExtent extent)
+ : source(source), funKey(extent.toFunctionKey()) {}
+};
+
+struct StencilCachePolicy {
+ using Lookup = StencilContext;
+
+ static HashNumber hash(const Lookup& l) {
+ const ScriptSource* raw = l.source;
+ return mozilla::HashGeneric(raw, l.funKey);
+ }
+ static bool match(const Lookup& entry, const Lookup& l) {
+ return entry.source == l.source && entry.funKey == l.funKey;
+ }
+};
+
+// Cache stencils which are parsed from the same source, and with identical
+// compilation options.
+//
+// This cache does not check the principals, as the source should not be
+// aliased across different principals.
+//
+// The content provided by this cache is computed by delazification tasks. The
+// delazification task needs contextual information to generate Stencils, which
+// are then registered in this cache.
+//
+// To reclaim memory from this cache, the producers should be shutdown and the
+// cache should be cleared. As the delazification process needs contextual
+// information to generate the Stencils, it is impossible to resume without
+// either keeping memory or recomputing from the beginning.
+//
+// Therefore, this cache cannot be cleared without disabling it at the same
+// time. Disabling this cache ends all threads which attempts to add new
+// stencils.
+//
+// The cache can be in multiple states:
+//
+// - Cache disabled:
+//
+// The cache can be disabled when running in a steady state for some time,
+// or after reaching a shrinking GC, which reclaims the memory from this
+// cache.
+//
+// This state is expected for the steady state of web pages, once the
+// pages are loaded for a while and that we do not expect a huge load of
+// delazification.
+//
+// - Cache enabled, Source is not cached:
+//
+// This is a rare case which can happen either when testing the
+// StencilCache, or after some dynamic load of JavaScript code in a page
+// which already reached as steady state. In which case, we want to prevent
+// locking for any functions which are not part of the newly loaded source.
+//
+// This might also happen for eval-ed or inline JavaScript, which is
+// parsed on the main thread, and also delazified on the main thread.
+//
+// - Cache enabled, Source is cached:
+//
+// This case is expected to be frequent for any new document. A new
+// document will register many sources for parsing off-thread, and
+// triggering off-thread delazification.
+//
+// All newly parse sources will be registered here until a steady state is
+// reached, or a shrinking GC is called.
+class StencilCache {
+ using SourceSet =
+ js::HashSet<RefPtr<ScriptSource>, SourceCachePolicy, SystemAllocPolicy>;
+ using StencilMap =
+ js::HashMap<StencilContext, RefPtr<frontend::CompilationStencil>,
+ StencilCachePolicy, SystemAllocPolicy>;
+
+ struct CacheData {
+ // Sources which are recorded in this cache.
+ SourceSet watched;
+ // Stencils of functions which are recorded in this cache.
+ StencilMap functions;
+ };
+
+ // Map a function to its CompilationStencil.
+ ExclusiveData<CacheData> cache;
+
+ // This flag is mostly read, and changes rarely. We use this Atomic to avoid
+ // locking a Mutex when the cache is disabled.
+ //
+ // The cache can be disabled when running in a steady state for some time, or
+ // after reaching a shrinking GC, which reclaims the memory from this cache
+ // and indirectly ends the threads which are producing stencils for this
+ // cache.
+ mozilla::Atomic<bool, mozilla::ReleaseAcquire> enabled;
+
+ public:
+ StencilCache();
+
+ // An access key is returned when checking whether the cache is enabled. It
+ // should be used in an if statement and provided to all follow-up functions
+ // if it is true-ish.
+ using AccessKey = ExclusiveData<CacheData>::NullableGuard;
+
+ // Not all stencils should be cached, we use the ScriptSource pointer to
+ // identify whether we should look further or not in the cache.
+ AccessKey isSourceCached(ScriptSource* src);
+
+ // Register a source in the cache in order to cache any stencil associated
+ // with this source in the future. To stop caching, the function
+ // clearAndDisable can be used.
+ //
+ // Note: This function should be called once per source. Which is usualy after
+ // creating it.
+ [[nodiscard]] bool startCaching(RefPtr<ScriptSource>&& src);
+
+ // Checks if the cache contains a specific stencil and returns a pointer to
+ // it if it does. Otherwise, returns nullptr.
+ frontend::CompilationStencil* lookup(AccessKey& guard,
+ const StencilContext& key);
+
+ // Adds a newly compiled stencil to the cache. The cache should not contain
+ // any entry for this function before calling this function.
+ [[nodiscard]] bool putNew(AccessKey& guard, const StencilContext& key,
+ frontend::CompilationStencil* value);
+
+ // Prevent any further stencil from being cached, and clear and reclaim the
+ // memory of all stencil held by the cache.
+ //
+ // WARNING: This function should not be called within a scope checking for
+ // isSourceCached, as this would cause a dead-lock.
+ void clearAndDisable();
+};
+
+// This cache is used to store the result of delazification compilations which
+// might be happening off-thread. The main-thread will concurrently read the
+// content of this cache to avoid delazification, or fallback on running the
+// delazification on the main-thread.
+//
+// Main-thread results are not stored in the DelazificationCache as there is no
+// other consumer.
+class DelazificationCache : public StencilCache {
+ static DelazificationCache singleton;
+
+ public:
+ DelazificationCache() = default;
+
+ static DelazificationCache& getSingleton() { return singleton; }
+};
+
+} /* namespace js */
+
+#endif /* vm_StencilCache_h */