Refactor CompilationSubCache

CompilationSubCache has some complexity regarding generations of tables
which is only used by one subclass, CompilationCacheRegExp. This change
adjusts the class hierarchy so that classes only contain the necessary
member functions.

Bug: v8:12808
Change-Id: I4f4cf15bbf9b80c2de0c18aea82a0c238804759d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3629603
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Commit-Queue: Seth Brenith <seth.brenith@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#80506}
This commit is contained in:
Seth Brenith 2022-05-12 14:24:35 -07:00 committed by V8 LUCI CQ
parent b16d2a4e2f
commit c8848cf493
2 changed files with 102 additions and 132 deletions

View File

@ -18,9 +18,6 @@
namespace v8 {
namespace internal {
// The number of generations for each sub cache.
static const int kRegExpGenerations = 2;
// Initial size of each compilation cache table allocated.
static const int kInitialCacheSize = 64;
@ -29,17 +26,18 @@ CompilationCache::CompilationCache(Isolate* isolate)
script_(isolate),
eval_global_(isolate),
eval_contextual_(isolate),
reg_exp_(isolate, kRegExpGenerations),
enabled_script_and_eval_(true) {
CompilationSubCache* subcaches[kSubCacheCount] = {
&script_, &eval_global_, &eval_contextual_, &reg_exp_};
for (int i = 0; i < kSubCacheCount; ++i) {
subcaches_[i] = subcaches[i];
reg_exp_(isolate),
enabled_script_and_eval_(true) {}
Handle<CompilationCacheTable> CompilationCacheEvalOrScript::GetTable() {
if (table_.IsUndefined(isolate())) {
return CompilationCacheTable::New(isolate(), kInitialCacheSize);
}
return handle(CompilationCacheTable::cast(table_), isolate());
}
Handle<CompilationCacheTable> CompilationSubCache::GetTable(int generation) {
DCHECK_LT(generation, generations());
Handle<CompilationCacheTable> CompilationCacheRegExp::GetTable(int generation) {
DCHECK_LT(generation, kGenerations);
Handle<CompilationCacheTable> result;
if (tables_[generation].IsUndefined(isolate())) {
result = CompilationCacheTable::New(isolate(), kInitialCacheSize);
@ -52,31 +50,23 @@ Handle<CompilationCacheTable> CompilationSubCache::GetTable(int generation) {
return result;
}
// static
void CompilationSubCache::AgeByGeneration(CompilationSubCache* c) {
DCHECK_GT(c->generations(), 1);
void CompilationCacheRegExp::Age() {
STATIC_ASSERT(kGenerations > 1);
// Age the generations implicitly killing off the oldest.
for (int i = c->generations() - 1; i > 0; i--) {
c->tables_[i] = c->tables_[i - 1];
for (int i = kGenerations - 1; i > 0; i--) {
tables_[i] = tables_[i - 1];
}
// Set the first generation as unborn.
c->tables_[0] = ReadOnlyRoots(c->isolate()).undefined_value();
}
CompilationCacheTable CompilationSubCache::MaybeGetFirstTable() {
DCHECK_GE(generations(), 1);
if (tables_[0].IsUndefined(isolate())) return {};
return CompilationCacheTable::cast(tables_[0]);
tables_[0] = ReadOnlyRoots(isolate()).undefined_value();
}
void CompilationCacheScript::Age() {
DisallowGarbageCollection no_gc;
if (!FLAG_isolate_script_cache_ageing) return;
DCHECK_EQ(generations(), 1);
CompilationCacheTable table = MaybeGetFirstTable();
if (table.is_null()) return;
if (table_.IsUndefined(isolate())) return;
CompilationCacheTable table = CompilationCacheTable::cast(table_);
for (InternalIndex entry : table.IterateEntries()) {
Object key;
@ -97,9 +87,8 @@ void CompilationCacheScript::Age() {
void CompilationCacheEval::Age() {
DisallowGarbageCollection no_gc;
DCHECK_EQ(generations(), 1);
CompilationCacheTable table = MaybeGetFirstTable();
if (table.is_null()) return;
if (table_.IsUndefined(isolate())) return;
CompilationCacheTable table = CompilationCacheTable::cast(table_);
for (InternalIndex entry : table.IterateEntries()) {
Object key;
@ -134,35 +123,32 @@ void CompilationCacheEval::Age() {
}
}
void CompilationCacheRegExp::Age() { AgeByGeneration(this); }
void CompilationCacheEvalOrScript::Iterate(RootVisitor* v) {
v->VisitRootPointer(Root::kCompilationCache, nullptr,
FullObjectSlot(&table_));
}
void CompilationSubCache::Iterate(RootVisitor* v) {
void CompilationCacheRegExp::Iterate(RootVisitor* v) {
v->VisitRootPointers(Root::kCompilationCache, nullptr,
FullObjectSlot(&tables_[0]),
FullObjectSlot(&tables_[generations()]));
FullObjectSlot(&tables_[kGenerations]));
}
void CompilationSubCache::Clear() {
void CompilationCacheEvalOrScript::Clear() {
table_ = ReadOnlyRoots(isolate()).undefined_value();
}
void CompilationCacheRegExp::Clear() {
MemsetPointer(reinterpret_cast<Address*>(tables_),
ReadOnlyRoots(isolate()).undefined_value().ptr(),
generations());
ReadOnlyRoots(isolate()).undefined_value().ptr(), kGenerations);
}
void CompilationSubCache::Remove(Handle<SharedFunctionInfo> function_info) {
// Probe the script generation tables. Make sure not to leak handles
// into the caller's handle scope.
{
HandleScope scope(isolate());
for (int generation = 0; generation < generations(); generation++) {
Handle<CompilationCacheTable> table = GetTable(generation);
table->Remove(*function_info);
}
}
void CompilationCacheEvalOrScript::Remove(
Handle<SharedFunctionInfo> function_info) {
if (table_.IsUndefined(isolate())) return;
CompilationCacheTable::cast(table_).Remove(*function_info);
}
CompilationCacheScript::CompilationCacheScript(Isolate* isolate)
: CompilationSubCache(isolate, 1) {}
namespace {
// We only re-use a cached function for some script source code if the
@ -230,9 +216,7 @@ CompilationCacheScript::LookupResult CompilationCacheScript::Lookup(
// into the caller's handle scope.
{
HandleScope scope(isolate());
const int generation = 0;
DCHECK_EQ(generations(), 1);
Handle<CompilationCacheTable> table = GetTable(generation);
Handle<CompilationCacheTable> table = GetTable();
LookupResult probe =
CompilationCacheTable::LookupScript(table, source, isolate());
Handle<Script> script;
@ -269,9 +253,9 @@ CompilationCacheScript::LookupResult CompilationCacheScript::Lookup(
void CompilationCacheScript::Put(Handle<String> source,
Handle<SharedFunctionInfo> function_info) {
HandleScope scope(isolate());
Handle<CompilationCacheTable> table = GetFirstTable();
SetFirstTable(CompilationCacheTable::PutScript(table, source, function_info,
isolate()));
Handle<CompilationCacheTable> table = GetTable();
table_ = *CompilationCacheTable::PutScript(table, source, function_info,
isolate());
}
InfoCellPair CompilationCacheEval::Lookup(Handle<String> source,
@ -284,9 +268,7 @@ InfoCellPair CompilationCacheEval::Lookup(Handle<String> source,
// scope. Otherwise, we risk keeping old tables around even after
// having cleared the cache.
InfoCellPair result;
const int generation = 0;
DCHECK_EQ(generations(), 1);
Handle<CompilationCacheTable> table = GetTable(generation);
Handle<CompilationCacheTable> table = GetTable();
result = CompilationCacheTable::LookupEval(
table, source, outer_info, native_context, language_mode, position);
if (result.has_shared()) {
@ -304,11 +286,10 @@ void CompilationCacheEval::Put(Handle<String> source,
Handle<FeedbackCell> feedback_cell,
int position) {
HandleScope scope(isolate());
Handle<CompilationCacheTable> table = GetFirstTable();
table =
CompilationCacheTable::PutEval(table, source, outer_info, function_info,
native_context, feedback_cell, position);
SetFirstTable(table);
Handle<CompilationCacheTable> table = GetTable();
table_ =
*CompilationCacheTable::PutEval(table, source, outer_info, function_info,
native_context, feedback_cell, position);
}
MaybeHandle<FixedArray> CompilationCacheRegExp::Lookup(Handle<String> source,
@ -319,7 +300,7 @@ MaybeHandle<FixedArray> CompilationCacheRegExp::Lookup(Handle<String> source,
// having cleared the cache.
Handle<Object> result = isolate()->factory()->undefined_value();
int generation;
for (generation = 0; generation < generations(); generation++) {
for (generation = 0; generation < kGenerations; generation++) {
Handle<CompilationCacheTable> table = GetTable(generation);
result = table->LookupRegExp(source, flags);
if (result->IsFixedArray()) break;
@ -340,9 +321,9 @@ MaybeHandle<FixedArray> CompilationCacheRegExp::Lookup(Handle<String> source,
void CompilationCacheRegExp::Put(Handle<String> source, JSRegExp::Flags flags,
Handle<FixedArray> data) {
HandleScope scope(isolate());
Handle<CompilationCacheTable> table = GetFirstTable();
SetFirstTable(
CompilationCacheTable::PutRegExp(isolate(), table, source, flags, data));
Handle<CompilationCacheTable> table = GetTable(0);
tables_[0] =
*CompilationCacheTable::PutRegExp(isolate(), table, source, flags, data);
}
void CompilationCache::Remove(Handle<SharedFunctionInfo> function_info) {
@ -434,21 +415,24 @@ void CompilationCache::PutRegExp(Handle<String> source, JSRegExp::Flags flags,
}
void CompilationCache::Clear() {
for (int i = 0; i < kSubCacheCount; i++) {
subcaches_[i]->Clear();
}
script_.Clear();
eval_global_.Clear();
eval_contextual_.Clear();
reg_exp_.Clear();
}
void CompilationCache::Iterate(RootVisitor* v) {
for (int i = 0; i < kSubCacheCount; i++) {
subcaches_[i]->Iterate(v);
}
script_.Iterate(v);
eval_global_.Iterate(v);
eval_contextual_.Iterate(v);
reg_exp_.Iterate(v);
}
void CompilationCache::MarkCompactPrologue() {
for (int i = 0; i < kSubCacheCount; i++) {
subcaches_[i]->Age();
}
script_.Age();
eval_global_.Age();
eval_contextual_.Age();
reg_exp_.Age();
}
void CompilationCache::EnableScriptAndEval() {

View File

@ -18,71 +18,41 @@ class Handle;
class RootVisitor;
struct ScriptDetails;
// 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 {
// 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 {
public:
CompilationSubCache(Isolate* isolate, int generations)
: isolate_(isolate), generations_(generations) {
DCHECK_LE(generations, kMaxGenerations);
}
explicit CompilationCacheEvalOrScript(Isolate* isolate) : isolate_(isolate) {}
static constexpr int kFirstGeneration = 0;
static constexpr int kMaxGenerations = 2;
// 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;
// Allocates the table if it didn't yet exist.
Handle<CompilationCacheTable> GetTable();
// GC support.
void Iterate(RootVisitor* v);
// Clear this sub-cache evicting all its content.
// Clears this sub-cache evicting all its content.
void Clear();
// Remove given shared function info from sub-cache.
// Removes 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 subclass logic.
static void AgeByGeneration(CompilationSubCache* c);
// Returns null if there is no first table.
CompilationCacheTable MaybeGetFirstTable();
private:
Isolate* const isolate_;
const int generations_;
Object tables_[kMaxGenerations]; // One for each generation.
Object table_;
DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationSubCache);
DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheEvalOrScript);
};
// Sub-cache for scripts.
class CompilationCacheScript : public CompilationSubCache {
class CompilationCacheScript : public CompilationCacheEvalOrScript {
public:
explicit CompilationCacheScript(Isolate* isolate);
explicit CompilationCacheScript(Isolate* isolate)
: CompilationCacheEvalOrScript(isolate) {}
using LookupResult = CompilationCacheScriptLookupResult;
LookupResult Lookup(Handle<String> source,
@ -90,7 +60,7 @@ class CompilationCacheScript : public CompilationSubCache {
void Put(Handle<String> source, Handle<SharedFunctionInfo> function_info);
void Age() override;
void Age();
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheScript);
@ -108,10 +78,10 @@ class CompilationCacheScript : public CompilationSubCache {
// More specifically these are the CompileString, DebugEvaluate and
// DebugEvaluateGlobal runtime functions.
// 4. The start position of the calling scope.
class CompilationCacheEval : public CompilationSubCache {
class CompilationCacheEval : public CompilationCacheEvalOrScript {
public:
explicit CompilationCacheEval(Isolate* isolate)
: CompilationSubCache(isolate, 1) {}
: CompilationCacheEvalOrScript(isolate) {}
InfoCellPair Lookup(Handle<String> source,
Handle<SharedFunctionInfo> outer_info,
@ -123,26 +93,45 @@ class CompilationCacheEval : public CompilationSubCache {
Handle<Context> native_context, Handle<FeedbackCell> feedback_cell,
int position);
void Age() override;
void Age();
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheEval);
};
// Sub-cache for regular expressions.
class CompilationCacheRegExp : public CompilationSubCache {
class CompilationCacheRegExp {
public:
CompilationCacheRegExp(Isolate* isolate, int generations)
: CompilationSubCache(isolate, generations) {}
CompilationCacheRegExp(Isolate* isolate) : isolate_(isolate) {}
MaybeHandle<FixedArray> Lookup(Handle<String> source, JSRegExp::Flags flags);
void Put(Handle<String> source, JSRegExp::Flags flags,
Handle<FixedArray> data);
void Age() override;
// 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();
private:
Isolate* isolate() const { return isolate_; }
Isolate* const isolate_;
Object tables_[kGenerations]; // One for each generation.
DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheRegExp);
};
@ -240,9 +229,6 @@ 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_;