Skip to content

Commit 9cbd134

Browse files
verwaestV8 LUCI CQ
authored andcommitted
[compiler] Reuse ScopeInfos from previous compiles
- We assign unique IDs to scopes and scope infos mainly based on their source location, and match newly parsed scopes on existing scope infos when we allocate scope infos for scopes. Existing scope infos are found by taking them out from existing inner SFIs from the SFI table of the script. - When we streaming compile scripts, we use the ConstantPoolPointerForwarder to merge in scope infos. - We add an "eval state" bool to scope infos that we flip for scopes eval'd in outer scopes. That way we can check the bit to see whether we're looking at scope infos still belonging to the current script, or possibly to a parent script that called eval. (In case of nested eval it's likely possible that the in-between eval either has a context or doesn't. It doesn't really matter, we look at the actual outer context on eval and flip that flag. If we need a scope info, that scope info will have the inverted bit of whatever the outer context had, independent of its origin). Change-Id: Ib75b63e46599c8d5fe014275bffb538de21b4ffc Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/5630974 Commit-Queue: Toon Verwaest <[email protected]> Reviewed-by: Leszek Swirski <[email protected]> Cr-Commit-Position: refs/heads/main@{#94966}
1 parent 0c26903 commit 9cbd134

16 files changed

Lines changed: 386 additions & 134 deletions

File tree

src/ast/scopes.cc

Lines changed: 104 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ void DeclarationScope::SetDefaults() {
329329
is_skipped_function_ = false;
330330
preparse_data_builder_ = nullptr;
331331
class_scope_has_private_brand_ = false;
332+
eval_state_ = false;
332333
#ifdef DEBUG
333334
DeclarationScope* outer_declaration_scope =
334335
outer_scope_ ? outer_scope_->GetDeclarationScope() : nullptr;
@@ -353,6 +354,7 @@ void Scope::SetDefaults() {
353354

354355
calls_eval_ = false;
355356
sloppy_eval_can_extend_vars_ = false;
357+
eval_state_ = false;
356358
scope_nonlinear_ = false;
357359
is_hidden_ = false;
358360
is_debug_evaluate_scope_ = false;
@@ -556,6 +558,16 @@ const DeclarationScope* Scope::AsDeclarationScope() const {
556558
return static_cast<const DeclarationScope*>(this);
557559
}
558560

561+
void DeclarationScope::set_eval_state() {
562+
DCHECK(is_eval_scope());
563+
if (outer_scope_->scope_info_.is_null()) {
564+
CHECK(outer_scope_->is_script_scope());
565+
eval_state_ = true;
566+
} else {
567+
eval_state_ = !outer_scope_->scope_info()->EvalState();
568+
}
569+
}
570+
559571
ModuleScope* Scope::AsModuleScope() {
560572
SBXCHECK(is_module_scope());
561573
return static_cast<ModuleScope*>(this);
@@ -2611,6 +2623,23 @@ void ModuleScope::AllocateModuleVariables() {
26112623
}
26122624
}
26132625

2626+
// Needs to be kept in sync with ScopeInfo::UniqueIdInScript.
2627+
int Scope::UniqueIdInScript() const {
2628+
// Script scopes start "before" the script to avoid clashing with a scope that
2629+
// starts on character 0.
2630+
if (is_script_scope() || scope_type() == EVAL_SCOPE ||
2631+
scope_type() == MODULE_SCOPE) {
2632+
return -1;
2633+
}
2634+
if (is_declaration_scope()) {
2635+
// Default constructors have the same start position as their parent class
2636+
// scope. Use the next char position to distinguish this scope.
2637+
return start_position() +
2638+
IsDefaultConstructor(AsDeclarationScope()->function_kind());
2639+
}
2640+
return start_position();
2641+
}
2642+
26142643
void Scope::AllocateVariablesRecursively() {
26152644
this->ForEach([](Scope* scope) -> Iteration {
26162645
DCHECK(!scope->already_resolved_);
@@ -2662,33 +2691,59 @@ void Scope::AllocateVariablesRecursively() {
26622691
}
26632692

26642693
template <typename IsolateT>
2665-
void Scope::AllocateScopeInfosRecursively(IsolateT* isolate,
2666-
MaybeHandle<ScopeInfo> outer_scope) {
2694+
void Scope::AllocateScopeInfosRecursively(
2695+
IsolateT* isolate, MaybeHandle<ScopeInfo> outer_scope,
2696+
std::unordered_map<int, Handle<ScopeInfo>>& scope_infos_to_reuse) {
26672697
DCHECK(scope_info_.is_null());
26682698
MaybeHandle<ScopeInfo> next_outer_scope = outer_scope;
26692699

2670-
if (NeedsScopeInfo()) {
2700+
auto it = scope_infos_to_reuse.find(UniqueIdInScript());
2701+
if (it != scope_infos_to_reuse.end()) {
2702+
scope_info_ = it->second;
2703+
CHECK(NeedsContext());
2704+
// The ScopeInfo chain mirrors the context chain, so we only link to the
2705+
// next outer scope that needs a context.
2706+
next_outer_scope = scope_info_;
2707+
DCHECK(!scope_info_.is_null());
2708+
CHECK_EQ(scope_info_->scope_type(), scope_type_);
2709+
CHECK_EQ(scope_info_->ContextLength(), num_heap_slots_);
2710+
#ifdef DEBUG
2711+
// Consume the scope info.
2712+
it->second = {};
2713+
#endif
2714+
} else if (NeedsScopeInfo()) {
2715+
#ifdef DEBUG
2716+
// Mark this ID as being used.
2717+
scope_infos_to_reuse[UniqueIdInScript()] = {};
2718+
#endif
26712719
scope_info_ = ScopeInfo::Create(isolate, zone(), this, outer_scope);
2672-
// The ScopeInfo chain should mirror the context chain, so we only link to
2673-
// the next outer scope that needs a context.
2720+
DCHECK_EQ(UniqueIdInScript(), scope_info_->UniqueIdInScript());
2721+
// The ScopeInfo chain mirrors the context chain, so we only link to the
2722+
// next outer scope that needs a context.
26742723
if (NeedsContext()) next_outer_scope = scope_info_;
26752724
}
26762725

26772726
// Allocate ScopeInfos for inner scopes.
26782727
for (Scope* scope = inner_scope_; scope != nullptr; scope = scope->sibling_) {
2728+
DCHECK_GT(scope->UniqueIdInScript(), UniqueIdInScript());
2729+
DCHECK_IMPLIES(scope->sibling_, scope->sibling_->UniqueIdInScript() !=
2730+
scope->UniqueIdInScript());
26792731
if (!scope->is_function_scope() ||
26802732
scope->AsDeclarationScope()->ShouldEagerCompile()) {
2681-
scope->AllocateScopeInfosRecursively(isolate, next_outer_scope);
2733+
scope->AllocateScopeInfosRecursively(isolate, next_outer_scope,
2734+
scope_infos_to_reuse);
26822735
}
26832736
}
26842737
}
26852738

26862739
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) void Scope::
2687-
AllocateScopeInfosRecursively<Isolate>(Isolate* isolate,
2688-
MaybeHandle<ScopeInfo> outer_scope);
2740+
AllocateScopeInfosRecursively<Isolate>(
2741+
Isolate* isolate, MaybeHandle<ScopeInfo> outer_scope,
2742+
std::unordered_map<int, Handle<ScopeInfo>>& scope_infos_to_reuse);
26892743
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) void Scope::
26902744
AllocateScopeInfosRecursively<LocalIsolate>(
2691-
LocalIsolate* isolate, MaybeHandle<ScopeInfo> outer_scope);
2745+
LocalIsolate* isolate, MaybeHandle<ScopeInfo> outer_scope,
2746+
std::unordered_map<int, Handle<ScopeInfo>>& scope_infos_to_reuse);
26922747

26932748
void DeclarationScope::RecalcPrivateNameContextChain() {
26942749
// The outermost scope in a class heritage expression is marked to skip the
@@ -2733,7 +2788,9 @@ void DeclarationScope::RecordNeedsPrivateNameContextChainRecalc() {
27332788

27342789
// static
27352790
template <typename IsolateT>
2736-
void DeclarationScope::AllocateScopeInfos(ParseInfo* info, IsolateT* isolate) {
2791+
void DeclarationScope::AllocateScopeInfos(ParseInfo* info,
2792+
Handle<Script> script,
2793+
IsolateT* isolate) {
27372794
DeclarationScope* scope = info->literal()->scope();
27382795

27392796
// No one else should have allocated a scope info for this scope yet.
@@ -2748,7 +2805,41 @@ void DeclarationScope::AllocateScopeInfos(ParseInfo* info, IsolateT* isolate) {
27482805
if (scope->needs_private_name_context_chain_recalc()) {
27492806
scope->RecalcPrivateNameContextChain();
27502807
}
2751-
scope->AllocateScopeInfosRecursively(isolate, outer_scope);
2808+
2809+
Tagged<WeakFixedArray> infos = script->shared_function_infos();
2810+
std::unordered_map<int, Handle<ScopeInfo>> scope_infos_to_reuse;
2811+
if (infos->length() != 0) {
2812+
// Look at all the existing inner functions (they are numbered id+1 until
2813+
// max_id+1) to reattach their outer scope infos to corresponding scopes.
2814+
for (int i = info->literal()->function_literal_id() + 1;
2815+
i < info->max_function_literal_id() + 1; ++i) {
2816+
Tagged<MaybeObject> maybe_sfi = infos->get(i);
2817+
if (maybe_sfi.IsWeak()) {
2818+
Tagged<SharedFunctionInfo> sfi =
2819+
Cast<SharedFunctionInfo>(maybe_sfi.GetHeapObjectAssumeWeak());
2820+
// Reuse outer scope infos. Don't look at sfi->scope_info() because that
2821+
// might be empty if the sfi isn't compiled yet.
2822+
if (!sfi->HasOuterScopeInfo()) continue;
2823+
Tagged<ScopeInfo> scope_info = sfi->GetOuterScopeInfo();
2824+
while (true) {
2825+
if (scope_info->EvalState() != scope->eval_state()) break;
2826+
if (scope_info->StartPosition() < scope->start_position()) break;
2827+
int id = scope_info->UniqueIdInScript();
2828+
auto it = scope_infos_to_reuse.find(id);
2829+
if (it != scope_infos_to_reuse.end()) {
2830+
CHECK_EQ(*it->second, scope_info);
2831+
break;
2832+
}
2833+
scope_infos_to_reuse[id] = handle(scope_info, isolate);
2834+
if (!scope_info->HasOuterScopeInfo()) break;
2835+
scope_info = scope_info->OuterScopeInfo();
2836+
}
2837+
}
2838+
}
2839+
}
2840+
2841+
scope->AllocateScopeInfosRecursively(isolate, outer_scope,
2842+
scope_infos_to_reuse);
27522843

27532844
// The debugger expects all shared function infos to contain a scope info.
27542845
// Since the top-most scope will end up in a shared function info, make sure
@@ -2767,9 +2858,9 @@ void DeclarationScope::AllocateScopeInfos(ParseInfo* info, IsolateT* isolate) {
27672858
}
27682859

27692860
template V8_EXPORT_PRIVATE void DeclarationScope::AllocateScopeInfos(
2770-
ParseInfo* info, Isolate* isolate);
2861+
ParseInfo* info, Handle<Script> script, Isolate* isolate);
27712862
template V8_EXPORT_PRIVATE void DeclarationScope::AllocateScopeInfos(
2772-
ParseInfo* info, LocalIsolate* isolate);
2863+
ParseInfo* info, Handle<Script> script, LocalIsolate* isolate);
27732864

27742865
int Scope::ContextLocalCount() const {
27752866
if (num_heap_slots() == 0) return 0;

src/ast/scopes.h

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
101101
}
102102
#endif
103103

104+
// An ID that uniquely identifies this scope within the script. Inner scopes
105+
// have a higher ID than their outer scopes. ScopeInfo created from a scope
106+
// has the same ID as the scope.
107+
int UniqueIdInScript() const;
108+
104109
DeclarationScope* AsDeclarationScope();
105110
const DeclarationScope* AsDeclarationScope() const;
106111
ModuleScope* AsModuleScope();
@@ -381,6 +386,7 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
381386
bool has_await_using_declaration() const {
382387
return has_await_using_declaration_;
383388
}
389+
bool eval_state() const { return eval_state_; }
384390

385391
#if V8_ENABLE_WEBASSEMBLY
386392
bool IsAsmModule() const;
@@ -719,11 +725,9 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
719725
void AllocateVariablesRecursively();
720726

721727
template <typename IsolateT>
722-
void AllocateScopeInfosRecursively(IsolateT* isolate,
723-
MaybeHandle<ScopeInfo> outer_scope);
724-
725-
void AllocateDebuggerScopeInfos(Isolate* isolate,
726-
MaybeHandle<ScopeInfo> outer_scope);
728+
void AllocateScopeInfosRecursively(
729+
IsolateT* isolate, MaybeHandle<ScopeInfo> outer_scope,
730+
std::unordered_map<int, Handle<ScopeInfo>>& scope_infos_to_reuse);
727731

728732
// Construct a scope based on the scope info.
729733
Scope(Zone* zone, ScopeType type, AstValueFactory* ast_value_factory,
@@ -737,6 +741,7 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
737741
inner_scope->sibling_ = inner_scope_;
738742
inner_scope_ = inner_scope;
739743
inner_scope->outer_scope_ = this;
744+
inner_scope->eval_state_ = eval_state_;
740745
}
741746

742747
void SetDefaults();
@@ -823,9 +828,7 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
823828

824829
bool must_use_preparsed_scope_data_ : 1;
825830

826-
// True if this is a script scope that originated from
827-
// DebugEvaluate::GlobalREPL().
828-
bool is_repl_mode_scope_ : 1;
831+
bool eval_state_ : 1;
829832

830833
// True if this is a deserialized scope which caches its lookups on another
831834
// Scope's variable map. This will be true for every scope above the first
@@ -886,6 +889,8 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
886889
return is_function_scope() && IsArrowFunction(function_kind_);
887890
}
888891

892+
void set_eval_state();
893+
889894
// Inform the scope and outer scopes that the corresponding code contains an
890895
// eval call.
891896
void RecordDeclarationScopeEvalCall() {
@@ -1161,6 +1166,7 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
11611166
// Does nothing if ScopeInfo is already allocated.
11621167
template <typename IsolateT>
11631168
V8_EXPORT_PRIVATE static void AllocateScopeInfos(ParseInfo* info,
1169+
Handle<Script> script,
11641170
IsolateT* isolate);
11651171

11661172
// Determine if we can use lazy compilation for this scope.

0 commit comments

Comments
 (0)