Commit d21b37d3 authored by Seth Brenith's avatar Seth Brenith Committed by V8 LUCI CQ

Revert several changes that caused performance regressions

This change reverts the following:

400b2cc2 Don't rescue old top-level SharedFunctionInfos
Reviewed on https://chromium-review.googlesource.com/c/v8/v8/+/3657472

16a7150b Reland "Disable recompilation of existing Scripts from
           Isolate compilation cache"
Reviewed on https://chromium-review.googlesource.com/c/v8/v8/+/3655011

2df4d58a Fix rehashing of script compilation cache
Reviewed on https://chromium-review.googlesource.com/c/v8/v8/+/3654413

c8848cf4 Refactor CompilationSubCache
Reviewed on https://chromium-review.googlesource.com/c/v8/v8/+/3629603

25072178 Improve Script reuse in isolate compilation cache, part 1
Reviewed on https://chromium-review.googlesource.com/c/v8/v8/+/3597106

Bug: v8:12808, chromium:1325566, chromium:1325567, chromium:1325601, chromium:1328671, chromium:1328672, chromium:1328678, chromium:1328811, chromium:1328810
Change-Id: I1d318dc172e5214166d3b15f19903186f4fe6024
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3664023Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80744}
parent a6cdc3a3
This diff is collapsed.
......@@ -18,49 +18,78 @@ class Handle;
class RootVisitor;
struct ScriptDetails;
// The compilation cache consists of several sub-caches: one each for evals and
// scripts, which use this class as a base class, and a separate generational
// sub-cache for RegExps. Since the same source code string has different
// compiled code for scripts and evals, we use separate sub-caches for different
// compilation modes, to avoid retrieving the wrong result.
class CompilationCacheEvalOrScript {
// The compilation cache consists of several generational sub-caches which uses
// this class as a base class. A sub-cache contains a compilation cache tables
// for each generation of the sub-cache. Since the same source code string has
// different compiled code for scripts and evals, we use separate sub-caches
// for different compilation modes, to avoid retrieving the wrong result.
class CompilationSubCache {
public:
explicit CompilationCacheEvalOrScript(Isolate* isolate) : isolate_(isolate) {}
CompilationSubCache(Isolate* isolate, int generations)
: isolate_(isolate), generations_(generations) {
DCHECK_LE(generations, kMaxGenerations);
}
static constexpr int kFirstGeneration = 0;
static constexpr int kMaxGenerations = 2;
// Allocates the table if it didn't yet exist.
Handle<CompilationCacheTable> GetTable();
// Get the compilation cache tables for a specific generation.
Handle<CompilationCacheTable> GetTable(int generation);
// Accessors for first generation.
Handle<CompilationCacheTable> GetFirstTable() {
return GetTable(kFirstGeneration);
}
void SetFirstTable(Handle<CompilationCacheTable> value) {
DCHECK_LT(kFirstGeneration, generations_);
tables_[kFirstGeneration] = *value;
}
// Age the sub-cache by evicting the oldest generation and creating a new
// young generation.
virtual void Age() = 0;
// GC support.
void Iterate(RootVisitor* v);
// Clears this sub-cache evicting all its content.
// Clear this sub-cache evicting all its content.
void Clear();
// Removes given shared function info from sub-cache.
// Remove given shared function info from sub-cache.
void Remove(Handle<SharedFunctionInfo> function_info);
// Number of generations in this sub-cache.
int generations() const { return generations_; }
protected:
Isolate* isolate() const { return isolate_; }
// Ageing occurs either by removing the oldest generation, or with
// custom logic implemented in CompilationCacheTable::Age.
static void AgeByGeneration(CompilationSubCache* c);
static void AgeCustom(CompilationSubCache* c);
private:
Isolate* const isolate_;
Object table_;
const int generations_;
Object tables_[kMaxGenerations]; // One for each generation.
DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheEvalOrScript);
DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationSubCache);
};
// Sub-cache for scripts.
class CompilationCacheScript : public CompilationCacheEvalOrScript {
class CompilationCacheScript : public CompilationSubCache {
public:
explicit CompilationCacheScript(Isolate* isolate)
: CompilationCacheEvalOrScript(isolate) {}
explicit CompilationCacheScript(Isolate* isolate);
using LookupResult = CompilationCacheScriptLookupResult;
LookupResult Lookup(Handle<String> source,
const ScriptDetails& script_details);
MaybeHandle<SharedFunctionInfo> Lookup(Handle<String> source,
const ScriptDetails& script_details,
LanguageMode language_mode);
void Put(Handle<String> source, Handle<SharedFunctionInfo> function_info);
void Put(Handle<String> source, LanguageMode language_mode,
Handle<SharedFunctionInfo> function_info);
void Age();
void Age() override;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheScript);
......@@ -78,10 +107,10 @@ class CompilationCacheScript : public CompilationCacheEvalOrScript {
// More specifically these are the CompileString, DebugEvaluate and
// DebugEvaluateGlobal runtime functions.
// 4. The start position of the calling scope.
class CompilationCacheEval : public CompilationCacheEvalOrScript {
class CompilationCacheEval : public CompilationSubCache {
public:
explicit CompilationCacheEval(Isolate* isolate)
: CompilationCacheEvalOrScript(isolate) {}
: CompilationSubCache(isolate, 1) {}
InfoCellPair Lookup(Handle<String> source,
Handle<SharedFunctionInfo> outer_info,
......@@ -93,45 +122,26 @@ class CompilationCacheEval : public CompilationCacheEvalOrScript {
Handle<Context> native_context, Handle<FeedbackCell> feedback_cell,
int position);
void Age();
void Age() override;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheEval);
};
// Sub-cache for regular expressions.
class CompilationCacheRegExp {
class CompilationCacheRegExp : public CompilationSubCache {
public:
CompilationCacheRegExp(Isolate* isolate) : isolate_(isolate) {}
CompilationCacheRegExp(Isolate* isolate, int generations)
: CompilationSubCache(isolate, generations) {}
MaybeHandle<FixedArray> Lookup(Handle<String> source, JSRegExp::Flags flags);
void Put(Handle<String> source, JSRegExp::Flags flags,
Handle<FixedArray> data);
// The number of generations for the RegExp sub cache.
static const int kGenerations = 2;
// Gets the compilation cache tables for a specific generation. Allocates the
// table if it does not yet exist.
Handle<CompilationCacheTable> GetTable(int generation);
// Ages the sub-cache by evicting the oldest generation and creating a new
// young generation.
void Age();
// GC support.
void Iterate(RootVisitor* v);
// Clears this sub-cache evicting all its content.
void Clear();
void Age() override;
private:
Isolate* isolate() const { return isolate_; }
Isolate* const isolate_;
Object tables_[kGenerations]; // One for each generation.
DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheRegExp);
};
......@@ -144,10 +154,10 @@ class V8_EXPORT_PRIVATE CompilationCache {
CompilationCache(const CompilationCache&) = delete;
CompilationCache& operator=(const CompilationCache&) = delete;
// Finds the Script and root SharedFunctionInfo for a script source string.
// Returns empty handles if the cache doesn't contain a script for the given
// source string with the right origin.
CompilationCacheScript::LookupResult LookupScript(
// Finds the script shared function info for a source
// string. Returns an empty handle if the cache doesn't contain a
// script for the given source string with the right origin.
MaybeHandle<SharedFunctionInfo> LookupScript(
Handle<String> source, const ScriptDetails& script_details,
LanguageMode language_mode);
......@@ -164,7 +174,7 @@ class V8_EXPORT_PRIVATE CompilationCache {
MaybeHandle<FixedArray> LookupRegExp(Handle<String> source,
JSRegExp::Flags flags);
// Associate the source string to the shared function
// Associate the (source, kind) pair to the shared function
// info. This may overwrite an existing mapping.
void PutScript(Handle<String> source, LanguageMode language_mode,
Handle<SharedFunctionInfo> function_info);
......@@ -214,11 +224,6 @@ class V8_EXPORT_PRIVATE CompilationCache {
bool IsEnabledScriptAndEval() const {
return FLAG_compilation_cache && enabled_script_and_eval_;
}
bool IsEnabledScript(LanguageMode language_mode) {
// Tests can change FLAG_use_strict at runtime. The compilation cache only
// contains scripts which were compiled with the default language mode.
return IsEnabledScriptAndEval() && language_mode == LanguageMode::kSloppy;
}
Isolate* isolate() const { return isolate_; }
......@@ -229,6 +234,9 @@ class V8_EXPORT_PRIVATE CompilationCache {
CompilationCacheEval eval_contextual_;
CompilationCacheRegExp reg_exp_;
static constexpr int kSubCacheCount = 4;
CompilationSubCache* subcaches_[kSubCacheCount];
// Current enable state of the compilation cache for scripts and eval.
bool enabled_script_and_eval_;
......
......@@ -1430,19 +1430,6 @@ Handle<SharedFunctionInfo> CreateTopLevelSharedFunctionInfo(
parse_info->literal(), script, true);
}
Handle<SharedFunctionInfo> GetOrCreateTopLevelSharedFunctionInfo(
ParseInfo* parse_info, Handle<Script> script, Isolate* isolate) {
EnsureSharedFunctionInfosArrayOnScript(script, parse_info, isolate);
MaybeHandle<SharedFunctionInfo> maybe_shared =
Script::FindSharedFunctionInfo(script, isolate, parse_info->literal());
if (Handle<SharedFunctionInfo> shared; maybe_shared.ToHandle(&shared)) {
DCHECK_EQ(shared->function_literal_id(),
parse_info->literal()->function_literal_id());
return shared;
}
return CreateTopLevelSharedFunctionInfo(parse_info, script, isolate);
}
MaybeHandle<SharedFunctionInfo> CompileToplevel(
ParseInfo* parse_info, Handle<Script> script,
MaybeHandle<ScopeInfo> maybe_outer_scope_info, Isolate* isolate,
......@@ -1476,7 +1463,7 @@ MaybeHandle<SharedFunctionInfo> CompileToplevel(
// Create the SharedFunctionInfo and add it to the script's list.
Handle<SharedFunctionInfo> shared_info =
GetOrCreateTopLevelSharedFunctionInfo(parse_info, script, isolate);
CreateTopLevelSharedFunctionInfo(parse_info, script, isolate);
FinalizeUnoptimizedCompilationDataList
finalize_unoptimized_compilation_data_list;
......@@ -2819,22 +2806,17 @@ Handle<Script> NewScript(
}
MaybeHandle<SharedFunctionInfo> CompileScriptOnMainThread(
UnoptimizedCompileFlags flags, Handle<String> source,
const UnoptimizedCompileFlags flags, Handle<String> source,
const ScriptDetails& script_details, NativesFlag natives,
v8::Extension* extension, Isolate* isolate,
MaybeHandle<Script> maybe_script, IsCompiledScope* is_compiled_scope) {
Handle<Script> script;
if (maybe_script.ToHandle(&script)) {
flags.set_script_id(script->id());
}
IsCompiledScope* is_compiled_scope) {
UnoptimizedCompileState compile_state;
ReusableUnoptimizedCompileState reusable_state(isolate);
ParseInfo parse_info(isolate, flags, &compile_state, &reusable_state);
parse_info.set_extension(extension);
if (script.is_null()) {
script = NewScript(isolate, &parse_info, source, script_details, natives);
}
Handle<Script> script =
NewScript(isolate, &parse_info, source, script_details, natives);
DCHECK_IMPLIES(parse_info.flags().collect_type_profile(),
script->IsUserJavaScript());
DCHECK_EQ(parse_info.flags().is_repl_mode(), script->is_repl_mode());
......@@ -2914,10 +2896,8 @@ bool CompilationExceptionIsRangeError(Isolate* isolate, Handle<Object> obj) {
MaybeHandle<SharedFunctionInfo> CompileScriptOnBothBackgroundAndMainThread(
Handle<String> source, const ScriptDetails& script_details,
MaybeHandle<Script> maybe_script, Isolate* isolate,
IsCompiledScope* is_compiled_scope) {
Isolate* isolate, IsCompiledScope* is_compiled_scope) {
// Start a background thread compiling the script.
// TODO(v8:12808): Use maybe_script for the background compilation.
StressBackgroundCompileThread background_compile_thread(
isolate, source,
script_details.origin_options.IsModule() ? ScriptType::kModule
......@@ -2940,7 +2920,7 @@ MaybeHandle<SharedFunctionInfo> CompileScriptOnBothBackgroundAndMainThread(
flags_copy.set_script_id(Script::kTemporaryScriptId);
main_thread_maybe_result = CompileScriptOnMainThread(
flags_copy, source, script_details, NOT_NATIVES_CODE, nullptr, isolate,
MaybeHandle<Script>(), &inner_is_compiled_scope);
&inner_is_compiled_scope);
if (main_thread_maybe_result.is_null()) {
// Assume all range errors are stack overflows.
main_thread_had_stack_overflow = CompilationExceptionIsRangeError(
......@@ -3031,7 +3011,6 @@ MaybeHandle<SharedFunctionInfo> GetSharedFunctionInfoForScriptImpl(
const bool use_compilation_cache =
extension == nullptr && script_details.repl_mode == REPLMode::kNo;
MaybeHandle<SharedFunctionInfo> maybe_result;
MaybeHandle<Script> maybe_script;
IsCompiledScope is_compiled_scope;
if (use_compilation_cache) {
bool can_consume_code_cache =
......@@ -3041,13 +3020,8 @@ MaybeHandle<SharedFunctionInfo> GetSharedFunctionInfoForScriptImpl(
}
// First check per-isolate compilation cache.
CompilationCacheScript::LookupResult lookup_result =
maybe_result =
compilation_cache->LookupScript(source, script_details, language_mode);
if (FLAG_isolate_script_cache_recompilation) {
maybe_script = lookup_result.script();
}
maybe_result = lookup_result.toplevel_sfi();
is_compiled_scope = lookup_result.is_compiled_scope();
if (!maybe_result.is_null()) {
compile_timer.set_hit_isolate_cache();
} else if (can_consume_code_cache) {
......@@ -3058,10 +3032,6 @@ MaybeHandle<SharedFunctionInfo> GetSharedFunctionInfoForScriptImpl(
RCS_SCOPE(isolate, RuntimeCallCounterId::kCompileDeserialize);
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
"V8.CompileDeserialize");
// TODO(v8:12808): If a Script was found in the compilation cache, then
// both of the code paths below (Finish and Deserialize) should make use
// of that Script to avoid duplicating the Script itself or any
// preexisting SharedFunctionInfos.
if (deserialize_task) {
// If there's a cache consume task, finish it.
maybe_result = deserialize_task->Finish(isolate, source,
......@@ -3096,7 +3066,7 @@ MaybeHandle<SharedFunctionInfo> GetSharedFunctionInfoForScriptImpl(
// If the --stress-background-compile flag is set, do the actual
// compilation on a background thread, and wait for its result.
maybe_result = CompileScriptOnBothBackgroundAndMainThread(
source, script_details, maybe_script, isolate, &is_compiled_scope);
source, script_details, isolate, &is_compiled_scope);
} else {
UnoptimizedCompileFlags flags =
UnoptimizedCompileFlags::ForToplevelCompile(
......@@ -3108,9 +3078,9 @@ MaybeHandle<SharedFunctionInfo> GetSharedFunctionInfoForScriptImpl(
flags.set_is_eager(compile_options == ScriptCompiler::kEagerCompile);
maybe_result = CompileScriptOnMainThread(
flags, source, script_details, natives, extension, isolate,
maybe_script, &is_compiled_scope);
maybe_result =
CompileScriptOnMainThread(flags, source, script_details, natives,
extension, isolate, &is_compiled_scope);
}
// Add the result to the isolate cache.
......@@ -3283,18 +3253,8 @@ Compiler::GetSharedFunctionInfoForStreamedScript(
{
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
"V8.StreamingFinalization.CheckCache");
CompilationCacheScript::LookupResult lookup_result =
compilation_cache->LookupScript(source, script_details,
task->flags().outer_language_mode());
// TODO(v8:12808): Determine what to do if we finish streaming and find that
// another copy of the Script already exists but has no root
// SharedFunctionInfo or has an uncompiled SharedFunctionInfo. For now, we
// just ignore it and create a new Script.
if (!lookup_result.toplevel_sfi().is_null()) {
maybe_result = lookup_result.toplevel_sfi();
}
maybe_result = compilation_cache->LookupScript(
source, script_details, task->flags().outer_language_mode());
if (!maybe_result.is_null()) {
compile_timer.set_hit_isolate_cache();
}
......@@ -3315,8 +3275,6 @@ Compiler::GetSharedFunctionInfoForStreamedScript(
// Add compiled code to the isolate cache.
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
"V8.StreamingFinalization.AddToCache");
DCHECK_EQ(task->flags().outer_language_mode(),
construct_language_mode(FLAG_use_strict));
compilation_cache->PutScript(source, task->flags().outer_language_mode(),
result);
}
......
......@@ -3887,12 +3887,6 @@ class BigIntPlatform : public bigint::Platform {
private:
Isolate* isolate_;
};
void MarkCompactPrologue(v8::Isolate* v8_isolate, v8::GCType gc_type,
v8::GCCallbackFlags flags, void* data) {
Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
isolate->compilation_cache()->MarkCompactPrologue();
}
} // namespace
VirtualMemoryCage* Isolate::GetPtrComprCodeCageForTesting() {
......@@ -4250,9 +4244,6 @@ bool Isolate::Init(SnapshotData* startup_snapshot_data,
}
#endif
heap()->AddGCPrologueCallback(MarkCompactPrologue, kGCTypeMarkSweepCompact,
nullptr);
initialized_ = true;
return true;
......
......@@ -2055,6 +2055,11 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
static void SetIsolateThreadLocals(Isolate* isolate,
PerIsolateThreadData* data);
void MarkCompactPrologue(bool is_compacting,
ThreadLocalTop* archived_thread_data);
void MarkCompactEpilogue(bool is_compacting,
ThreadLocalTop* archived_thread_data);
void FillCache();
// Propagate pending exception message to the v8::TryCatch.
......
......@@ -965,9 +965,6 @@ DEFINE_BOOL(turbo_collect_feedback_in_generic_lowering, true,
"enable experimental feedback collection in generic lowering.")
DEFINE_BOOL(isolate_script_cache_ageing, true,
"enable ageing of the isolate script cache.")
DEFINE_BOOL(isolate_script_cache_recompilation, false,
"enable recompiling an existing Script from the Isolate cache when "
"there is not a compiled top-level SharedFunctionInfo")
DEFINE_FLOAT(script_delay, 0, "busy wait [ms] on every Script::Run")
DEFINE_FLOAT(script_delay_once, 0, "busy wait [ms] on the first Script::Run")
......
......@@ -2673,6 +2673,8 @@ void Heap::MarkCompactPrologue() {
RegExpResultsCache::Clear(string_split_cache());
RegExpResultsCache::Clear(regexp_multiple_cache());
isolate_->compilation_cache()->MarkCompactPrologue();
FlushNumberStringCache();
}
......
......@@ -281,16 +281,13 @@ namespace internal {
// lines) rather than one macro (of length about 80 lines) to work around
// this problem. Please avoid using recursive macros of this length when
// possible.
#define STATS_COUNTER_LIST_1(SC) \
/* Global Handle Count*/ \
SC(global_handles, V8.GlobalHandles) \
SC(alive_after_last_gc, V8.AliveAfterLastGC) \
SC(compilation_cache_hits, V8.CompilationCacheHits) \
SC(compilation_cache_misses, V8.CompilationCacheMisses) \
/* Number of times the cache contained a reusable Script but not \
the root SharedFunctionInfo */ \
SC(compilation_cache_partial_hits, V8.CompilationCachePartialHits) \
SC(objs_since_last_young, V8.ObjsSinceLastYoung) \
#define STATS_COUNTER_LIST_1(SC) \
/* Global Handle Count*/ \
SC(global_handles, V8.GlobalHandles) \
SC(alive_after_last_gc, V8.AliveAfterLastGC) \
SC(compilation_cache_hits, V8.CompilationCacheHits) \
SC(compilation_cache_misses, V8.CompilationCacheMisses) \
SC(objs_since_last_young, V8.ObjsSinceLastYoung) \
SC(objs_since_last_full, V8.ObjsSinceLastFull)
#define STATS_COUNTER_LIST_2(SC) \
......
......@@ -26,77 +26,14 @@ CompilationCacheTable::CompilationCacheTable(Address ptr)
NEVER_READ_ONLY_SPACE_IMPL(CompilationCacheTable)
CAST_ACCESSOR(CompilationCacheTable)
Object CompilationCacheTable::PrimaryValueAt(InternalIndex entry) {
return get(EntryToIndex(entry) + 1);
}
void CompilationCacheTable::SetPrimaryValueAt(InternalIndex entry, Object value,
WriteBarrierMode mode) {
set(EntryToIndex(entry) + 1, value, mode);
}
Object CompilationCacheTable::EvalFeedbackValueAt(InternalIndex entry) {
static_assert(CompilationCacheShape::kEntrySize == 3);
return get(EntryToIndex(entry) + 2);
}
void CompilationCacheTable::SetEvalFeedbackValueAt(InternalIndex entry,
Object value,
WriteBarrierMode mode) {
set(EntryToIndex(entry) + 2, value, mode);
}
// The key in a script cache is a WeakFixedArray containing a weak pointer to
// the Script. The corresponding value can be either the root SharedFunctionInfo
// or undefined. The purpose of storing the root SharedFunctionInfo as the value
// is to keep it alive, not to save a lookup on the Script. A newly added entry
// always contains the root SharedFunctionInfo. After the root
// SharedFunctionInfo has aged sufficiently, it is replaced with undefined. In
// this way, all strong references to large objects are dropped, but there is
// still a way to get the Script if it happens to still be alive.
class ScriptCacheKey : public HashTableKey {
public:
enum Index {
kHash,
kWeakScript,
kEnd,
};
explicit ScriptCacheKey(Handle<String> source);
bool IsMatch(Object other) override;
Handle<Object> AsHandle(Isolate* isolate, Handle<SharedFunctionInfo> shared);
static base::Optional<String> SourceFromObject(Object obj) {
DisallowGarbageCollection no_gc;
DCHECK(obj.IsWeakFixedArray());
WeakFixedArray array = WeakFixedArray::cast(obj);
DCHECK_EQ(array.length(), kEnd);
MaybeObject maybe_script = array.Get(kWeakScript);
if (HeapObject script; maybe_script.GetHeapObjectIfWeak(&script)) {
PrimitiveHeapObject source_or_undefined = Script::cast(script).source();
// Scripts stored in the script cache should always have a source string.
return String::cast(source_or_undefined);
}
DCHECK(maybe_script.IsCleared());
return {};
}
private:
Handle<String> source_;
};
uint32_t CompilationCacheShape::RegExpHash(String string, Smi flags) {
return string.EnsureHash() + flags.value();
}
uint32_t CompilationCacheShape::EvalHash(String source,
SharedFunctionInfo shared,
LanguageMode language_mode,
int position) {
uint32_t CompilationCacheShape::StringSharedHash(String source,
SharedFunctionInfo shared,
LanguageMode language_mode,
int position) {
uint32_t hash = source.EnsureHash();
if (shared.HasSourceCode()) {
// Instead of using the SharedFunctionInfo pointer in the hash
......@@ -113,6 +50,14 @@ uint32_t CompilationCacheShape::EvalHash(String source,
return hash;
}
uint32_t CompilationCacheShape::StringSharedHash(String source,
LanguageMode language_mode) {
uint32_t hash = source.EnsureHash();
static_assert(LanguageModeSize == 2);
if (is_strict(language_mode)) hash ^= 0x8000;
return hash;
}
uint32_t CompilationCacheShape::HashForObject(ReadOnlyRoots roots,
Object object) {
// Eval: The key field contains the hash as a Number.
......@@ -123,24 +68,7 @@ uint32_t CompilationCacheShape::HashForObject(ReadOnlyRoots roots,
return SharedFunctionInfo::cast(object).Hash();
}
// Script.
if (object.IsWeakFixedArray()) {
uint32_t result = static_cast<uint32_t>(Smi::ToInt(
WeakFixedArray::cast(object).Get(ScriptCacheKey::kHash).ToSmi()));
#ifdef DEBUG
base::Optional<String> script_key =
ScriptCacheKey::SourceFromObject(object);
if (script_key) {
uint32_t source_hash;
if (script_key->TryGetHash(&source_hash)) {
DCHECK_EQ(result, source_hash);
}
}
#endif
return result;
}
// Eval: See EvalCacheKey::ToHandle for the encoding.
// Script: See StringSharedKey::ToHandle for the encoding.
FixedArray val = FixedArray::cast(object);
if (val.map() == roots.fixed_cow_array_map()) {
DCHECK_EQ(4, val.length());
......@@ -149,10 +77,14 @@ uint32_t CompilationCacheShape::HashForObject(ReadOnlyRoots roots,
DCHECK(is_valid_language_mode(language_unchecked));
LanguageMode language_mode = static_cast<LanguageMode>(language_unchecked);
int position = Smi::ToInt(val.get(3));
Object shared = val.get(0);
DCHECK(shared.IsSharedFunctionInfo());
return EvalHash(source, SharedFunctionInfo::cast(shared), language_mode,
position);
Object shared_or_smi = val.get(0);
if (shared_or_smi.IsSmi()) {
DCHECK_EQ(position, kNoSourcePosition);
return StringSharedHash(source, language_mode);
} else {
return StringSharedHash(source, SharedFunctionInfo::cast(shared_or_smi),
language_mode, position);
}
}
// RegExp: The key field (and the value field) contains the
......
This diff is collapsed.
......@@ -29,8 +29,13 @@ class CompilationCacheShape : public BaseShape<HashTableKey*> {
static inline uint32_t RegExpHash(String string, Smi flags);
static inline uint32_t EvalHash(String source, SharedFunctionInfo shared,
LanguageMode language_mode, int position);
static inline uint32_t StringSharedHash(String source,
SharedFunctionInfo shared,
LanguageMode language_mode,
int position);
static inline uint32_t StringSharedHash(String source,
LanguageMode language_mode);
static inline uint32_t HashForObject(ReadOnlyRoots roots, Object object);
......@@ -74,34 +79,6 @@ class InfoCellPair {
FeedbackCell feedback_cell_;
};
// A lookup result from the compilation cache for scripts. There are three
// possible states:
//
// 1. Cache miss: script and toplevel_sfi are both null.
// 2. Cache hit: script and toplevel_sfi are both non-null. toplevel_sfi is
// guaranteed to be compiled, and to stay compiled while this lookup result
// instance is alive.
// 3. Partial cache hit: script is non-null, but toplevel_sfi is null. The
// script may contain an uncompiled toplevel SharedFunctionInfo.
class CompilationCacheScriptLookupResult {
public:
MaybeHandle<Script> script() const { return script_; }
MaybeHandle<SharedFunctionInfo> toplevel_sfi() const { return toplevel_sfi_; }
IsCompiledScope is_compiled_scope() const { return is_compiled_scope_; }
using RawObjects = std::pair<Script, SharedFunctionInfo>;
RawObjects GetRawObjects() const;
static CompilationCacheScriptLookupResult FromRawObjects(RawObjects raw,
Isolate* isolate);
private:
MaybeHandle<Script> script_;
MaybeHandle<SharedFunctionInfo> toplevel_sfi_;
IsCompiledScope is_compiled_scope_;
};
EXTERN_DECLARE_HASH_TABLE(CompilationCacheTable, CompilationCacheShape)
class CompilationCacheTable
......@@ -109,18 +86,14 @@ class CompilationCacheTable
public:
NEVER_READ_ONLY_SPACE
// The 'script' cache contains SharedFunctionInfos. Once a root
// SharedFunctionInfo has become old enough that its bytecode is flushed, the
// entry is still present and can be used to get the Script. For consistency,
// this function always returns the Script. Callers should check whether there
// is a root SharedFunctionInfo in the script and whether it is already
// compiled, and choose what to do accordingly.
static CompilationCacheScriptLookupResult LookupScript(
// The 'script' cache contains SharedFunctionInfos.
static MaybeHandle<SharedFunctionInfo> LookupScript(
Handle<CompilationCacheTable> table, Handle<String> src,
Isolate* isolate);
LanguageMode language_mode, Isolate* isolate);
static Handle<CompilationCacheTable> PutScript(
Handle<CompilationCacheTable> cache, Handle<String> src,
Handle<SharedFunctionInfo> value, Isolate* isolate);
LanguageMode language_mode, Handle<SharedFunctionInfo> value,
Isolate* isolate);
// Eval code only gets cached after a second probe for the
// code object. To do so, on first "put" only a hash identifying the
......@@ -151,24 +124,12 @@ class CompilationCacheTable
JSRegExp::Flags flags, Handle<FixedArray> value);
void Remove(Object value);
void RemoveEntry(InternalIndex entry);
inline Object PrimaryValueAt(InternalIndex entry);
inline void SetPrimaryValueAt(InternalIndex entry, Object value,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
inline Object EvalFeedbackValueAt(InternalIndex entry);
inline void SetEvalFeedbackValueAt(
InternalIndex entry, Object value,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
// The initial placeholder insertion of the eval cache survives this many GCs.
static constexpr int kHashGenerations = 10;
void Age(Isolate* isolate);
DECL_CAST(CompilationCacheTable)
private:
static Handle<CompilationCacheTable> EnsureScriptTableCapacity(
Isolate* isolate, Handle<CompilationCacheTable> cache);
void RemoveEntry(int entry_index);
OBJECT_CONSTRUCTORS(CompilationCacheTable,
HashTable<CompilationCacheTable, CompilationCacheShape>);
......
......@@ -238,12 +238,6 @@ Object HashTable<Derived, Shape>::KeyAt(PtrComprCageBase cage_base,
return get(cage_base, EntryToIndex(entry) + kEntryKeyIndex, tag);
}
template <typename Derived, typename Shape>
void HashTable<Derived, Shape>::SetKeyAt(InternalIndex entry, Object value,
WriteBarrierMode mode) {
set_key(EntryToIndex(entry), value, mode);
}
template <typename Derived, typename Shape>
void HashTable<Derived, Shape>::set_key(int index, Object value) {
DCHECK(!IsEphemeronHashTable());
......
......@@ -165,9 +165,6 @@ class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) HashTable
inline Object KeyAt(PtrComprCageBase cage_base, InternalIndex entry,
RelaxedLoadTag tag);
inline void SetKeyAt(InternalIndex entry, Object value,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
static const int kElementsStartIndex = kPrefixStartIndex + Shape::kPrefixSize;
static const int kEntrySize = Shape::kEntrySize;
static_assert(kEntrySize > 0);
......
This diff is collapsed.
......@@ -23855,25 +23855,23 @@ TEST(StreamingWithHarmonyScopes) {
delete[] full_source;
}
namespace {
void StreamingWithIsolateScriptCache(bool run_gc) {
i::FLAG_expose_gc = true;
// Regression test for crbug.com/v8/12668. Verifies that after a streamed script
// is inserted into the isolate script cache, a non-streamed script with
// identical origin can reuse that data.
TEST(StreamingWithIsolateScriptCache) {
const char* chunks[] = {"'use strict'; (function test() { return 13; })",
nullptr};
const char* full_source = chunks[0];
v8::Isolate* isolate = CcTest::isolate();
auto i_isolate = reinterpret_cast<i::Isolate*>(isolate);
v8::HandleScope scope(isolate);
v8::ScriptOrigin origin(isolate, v8_str("http://foo.com"), 0, 0, false, -1,
v8::Local<v8::Value>(), false, false, false);
v8::Local<Value> first_function_untyped;
i::Handle<i::JSFunction> first_function;
i::Handle<i::JSFunction> second_function;
// Run the script using streaming.
{
LocalContext env;
v8::EscapableHandleScope inner_scope(isolate);
v8::ScriptCompiler::StreamedSource source(
std::make_unique<TestSourceStream>(chunks),
......@@ -23888,38 +23886,13 @@ void StreamingWithIsolateScriptCache(bool run_gc) {
origin)
.ToLocalChecked();
v8::Local<Value> result(script->Run(env.local()).ToLocalChecked());
first_function_untyped = inner_scope.Escape(result);
if (run_gc) {
// Age the top-level bytecode for the script to encourage the Isolate
// script cache to evict it. However, there are still active Handles
// referring to functions in that script, so the script itself should stay
// alive and reachable via the Isolate script cache.
i::Handle<i::JSFunction> script_function =
i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(*script));
i::Handle<i::BytecodeArray> script_bytecode(
script_function->shared().GetBytecodeArray(i_isolate), i_isolate);
for (int i = 0; i < 5; ++i) {
script_bytecode->MakeOlder();
}
}
first_function =
i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(*result));
}
first_function = i::Handle<i::JSFunction>::cast(
v8::Utils::OpenHandle(*first_function_untyped));
// Run the same script in another Context without streaming.
{
LocalContext env;
if (run_gc) {
// Perform garbage collection, which should remove the top-level
// SharedFunctionInfo from the Isolate script cache. However, the
// corresponding Script is still reachable and therefore still present in
// the Isolate script cache.
CompileRun("gc();");
}
v8::ScriptCompiler::Source script_source(v8_str(full_source), origin);
Local<Script> script =
v8::ScriptCompiler::Compile(env.local(), &script_source)
......@@ -23933,32 +23906,6 @@ void StreamingWithIsolateScriptCache(bool run_gc) {
// SharedFunctionInfo instance due to the isolate script cache.
CHECK_EQ(first_function->shared(), second_function->shared());
}
} // namespace
// Regression test for crbug.com/v8/12668. Verifies that after a streamed script
// is inserted into the isolate script cache, a non-streamed script with
// identical origin can reuse that data.
TEST(StreamingWithIsolateScriptCache) {
StreamingWithIsolateScriptCache(false);
}
// Variant of the above test which evicts the root SharedFunctionInfo from the
// Isolate script cache but still reuses the same Script.
TEST(StreamingWithIsolateScriptCacheClearingRootSFI) {
// TODO(v8:12808): Remove this check once background compilation is capable of
// reusing an existing Script.
if (v8::internal::FLAG_stress_background_compile) {
return;
}
// If the compiler is configured to not recompile a flushed root SFI, then
// this test is invalid.
if (!v8::internal::FLAG_isolate_script_cache_recompilation) {
return;
}
StreamingWithIsolateScriptCache(true);
}
TEST(CodeCache) {
v8::Isolate::CreateParams create_params;
......@@ -1820,9 +1820,10 @@ TEST(CodeSerializerPromotedToCompilationCache) {
ScriptDetails script_details(src);
script_details.host_defined_options =
default_script_details.host_defined_options;
auto lookup_result = isolate->compilation_cache()->LookupScript(
src, script_details, LanguageMode::kSloppy);
CHECK_EQ(*lookup_result.toplevel_sfi().ToHandleChecked(), *copy);
MaybeHandle<SharedFunctionInfo> shared =
isolate->compilation_cache()->LookupScript(src, script_details,
LanguageMode::kSloppy);
CHECK_EQ(*shared.ToHandleChecked(), *copy);
}
{
......@@ -1836,9 +1837,10 @@ TEST(CodeSerializerPromotedToCompilationCache) {
default_host_defined_option_1_string);
host_defined_options->set(1, *host_defined_option_1);
script_details.host_defined_options = host_defined_options;
auto lookup_result = isolate->compilation_cache()->LookupScript(
src, script_details, LanguageMode::kSloppy);
CHECK_EQ(*lookup_result.toplevel_sfi().ToHandleChecked(), *copy);
MaybeHandle<SharedFunctionInfo> shared =
isolate->compilation_cache()->LookupScript(src, script_details,
LanguageMode::kSloppy);
CHECK_EQ(*shared.ToHandleChecked(), *copy);
}
{
......@@ -1847,48 +1849,49 @@ TEST(CodeSerializerPromotedToCompilationCache) {
isolate->factory()->NewStringFromAsciiChecked(source));
script_details.host_defined_options =
default_script_details.host_defined_options;
auto lookup_result = isolate->compilation_cache()->LookupScript(
src, script_details, LanguageMode::kSloppy);
CHECK_EQ(*lookup_result.toplevel_sfi().ToHandleChecked(), *copy);
MaybeHandle<SharedFunctionInfo> shared =
isolate->compilation_cache()->LookupScript(src, script_details,
LanguageMode::kSloppy);
CHECK_EQ(*shared.ToHandleChecked(), *copy);
}
{
// Lookup with different name string should fail:
ScriptDetails script_details(
isolate->factory()->NewStringFromAsciiChecked("other"));
auto lookup_result = isolate->compilation_cache()->LookupScript(
src, script_details, LanguageMode::kSloppy);
CHECK(lookup_result.script().is_null() &&
lookup_result.toplevel_sfi().is_null());
MaybeHandle<SharedFunctionInfo> shared =
isolate->compilation_cache()->LookupScript(src, script_details,
LanguageMode::kSloppy);
CHECK(shared.is_null());
}
{
// Lookup with different position should fail:
ScriptDetails script_details(src);
script_details.line_offset = 0xFF;
auto lookup_result = isolate->compilation_cache()->LookupScript(
src, script_details, LanguageMode::kSloppy);
CHECK(lookup_result.script().is_null() &&
lookup_result.toplevel_sfi().is_null());
MaybeHandle<SharedFunctionInfo> shared =
isolate->compilation_cache()->LookupScript(src, script_details,
LanguageMode::kSloppy);
CHECK(shared.is_null());
}
{
// Lookup with different position should fail:
ScriptDetails script_details(src);
script_details.column_offset = 0xFF;
auto lookup_result = isolate->compilation_cache()->LookupScript(
src, script_details, LanguageMode::kSloppy);
CHECK(lookup_result.script().is_null() &&
lookup_result.toplevel_sfi().is_null());
MaybeHandle<SharedFunctionInfo> shared =
isolate->compilation_cache()->LookupScript(src, script_details,
LanguageMode::kSloppy);
CHECK(shared.is_null());
}
{
// Lookup with different language mode should fail:
ScriptDetails script_details(src);
auto lookup_result = isolate->compilation_cache()->LookupScript(
src, script_details, LanguageMode::kStrict);
CHECK(lookup_result.script().is_null() &&
lookup_result.toplevel_sfi().is_null());
MaybeHandle<SharedFunctionInfo> shared =
isolate->compilation_cache()->LookupScript(src, script_details,
LanguageMode::kStrict);
CHECK(shared.is_null());
}
{
......@@ -1896,20 +1899,20 @@ TEST(CodeSerializerPromotedToCompilationCache) {
ScriptOriginOptions origin_options(false, true);
CHECK_NE(ScriptOriginOptions().Flags(), origin_options.Flags());
ScriptDetails script_details(src, origin_options);
auto lookup_result = isolate->compilation_cache()->LookupScript(
src, script_details, LanguageMode::kSloppy);
CHECK(lookup_result.script().is_null() &&
lookup_result.toplevel_sfi().is_null());
MaybeHandle<SharedFunctionInfo> shared =
isolate->compilation_cache()->LookupScript(src, script_details,
LanguageMode::kSloppy);
CHECK(shared.is_null());
}
{
// Lookup with different host_defined_options should fail:
ScriptDetails script_details(src);
script_details.host_defined_options = isolate->factory()->NewFixedArray(5);
auto lookup_result = isolate->compilation_cache()->LookupScript(
src, script_details, LanguageMode::kSloppy);
CHECK(lookup_result.script().is_null() &&
lookup_result.toplevel_sfi().is_null());
MaybeHandle<SharedFunctionInfo> shared =
isolate->compilation_cache()->LookupScript(src, script_details,
LanguageMode::kSloppy);
CHECK(shared.is_null());
}
delete cache;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment