Skip to content

Commit 67dd629

Browse files
verwaestV8 LUCI CQ
authored andcommitted
[compiler] Multiple fixes for reuse_scope_infos
- Also reuse the scope_info of the function itself. It might be in RO_SPACE in case the script is in the snapshot, and it would be a shame to recreate it. - Make sure we don't drop (or forget to pick up) scope infos for newly compiled sfis that already existed in the script - Make sure to reattach scope info chains wherever an outer scope info exists first. Due to code caches with spotty coverage we might see unexpected SFI/scope info combinations. This also adds a flag to run verification on scope info reuse after merging. Bug: 352673356 Change-Id: Iad702d2ab775cdcb72bdc472942601c340f4f4d2 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/5725954 Reviewed-by: Leszek Swirski <[email protected]> Commit-Queue: Toon Verwaest <[email protected]> Cr-Commit-Position: refs/heads/main@{#95154}
1 parent 4192b4d commit 67dd629

File tree

7 files changed

+170
-29
lines changed

7 files changed

+170
-29
lines changed

src/ast/scopes.cc

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2611,7 +2611,8 @@ void ModuleScope::AllocateModuleVariables() {
26112611
}
26122612
}
26132613

2614-
// Needs to be kept in sync with ScopeInfo::UniqueIdInScript.
2614+
// Needs to be kept in sync with ScopeInfo::UniqueIdInScript and
2615+
// SharedFunctionInfo::UniqueIdInScript.
26152616
int Scope::UniqueIdInScript() const {
26162617
// Script scopes start "before" the script to avoid clashing with a scope that
26172618
// starts on character 0.
@@ -2805,20 +2806,25 @@ void DeclarationScope::AllocateScopeInfos(ParseInfo* info,
28052806
Tagged<ScopeInfo> outer = sfi->HasOuterScopeInfo()
28062807
? sfi->GetOuterScopeInfo()
28072808
: Tagged<ScopeInfo>();
2808-
// Look at all the existing inner functions (they are numbered id+1 until
2809-
// max_id+1) to reattach their outer scope infos to corresponding scopes.
2810-
for (int i = info->literal()->function_literal_id() + 1;
2809+
// Look at all inner functions whether they have scope infos that we should
2810+
// reuse. Also look at the compiled function itself, and reuse its function
2811+
// scope info if it exists.
2812+
for (int i = info->literal()->function_literal_id();
28112813
i < info->max_info_id() + 1; ++i) {
28122814
Tagged<MaybeObject> maybe_info = infos->get(i);
28132815
if (maybe_info.IsWeak()) {
28142816
Tagged<Object> info = maybe_info.GetHeapObjectAssumeWeak();
28152817
Tagged<ScopeInfo> scope_info;
28162818
if (Is<SharedFunctionInfo>(info)) {
28172819
Tagged<SharedFunctionInfo> sfi = Cast<SharedFunctionInfo>(info);
2818-
// Reuse outer scope infos. Don't look at sfi->scope_info() because
2819-
// that might be empty if the sfi isn't compiled yet.
2820-
if (!sfi->HasOuterScopeInfo()) continue;
2821-
scope_info = sfi->GetOuterScopeInfo();
2820+
if (!sfi->scope_info()->IsEmpty() &&
2821+
sfi->scope_info()->HasContext()) {
2822+
scope_info = sfi->scope_info();
2823+
} else if (sfi->HasOuterScopeInfo()) {
2824+
scope_info = sfi->GetOuterScopeInfo();
2825+
} else {
2826+
continue;
2827+
}
28222828
} else {
28232829
scope_info = Cast<ScopeInfo>(info);
28242830
}

src/codegen/compiler.cc

Lines changed: 134 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2022,18 +2022,39 @@ class ConstantPoolPointerForwarder {
20222022
}
20232023

20242024
void RecordScopeInfos(Tagged<MaybeObject> maybe_old_info) {
2025+
RecordScopeInfos(maybe_old_info.GetHeapObjectAssumeWeak());
2026+
}
2027+
2028+
// Record all scope infos relevant for a shared function info or scope info
2029+
// (recorded for eval).
2030+
void RecordScopeInfos(Tagged<HeapObject> info) {
20252031
Tagged<ScopeInfo> scope_info;
2026-
Tagged<HeapObject> info = maybe_old_info.GetHeapObjectAssumeWeak();
20272032
if (Is<SharedFunctionInfo>(info)) {
20282033
Tagged<SharedFunctionInfo> old_sfi = Cast<SharedFunctionInfo>(info);
2029-
if (!old_sfi->HasOuterScopeInfo()) return;
2030-
scope_info = old_sfi->GetOuterScopeInfo();
2034+
// Also record context-having own scope infos for SFIs.
2035+
if (!old_sfi->scope_info()->IsEmpty() &&
2036+
old_sfi->scope_info()->HasContext()) {
2037+
scope_info = old_sfi->scope_info();
2038+
} else if (old_sfi->HasOuterScopeInfo()) {
2039+
scope_info = old_sfi->GetOuterScopeInfo();
2040+
} else {
2041+
return;
2042+
}
20312043
} else {
20322044
scope_info = Cast<ScopeInfo>(info);
20332045
}
2046+
20342047
while (true) {
2035-
if (scope_infos_to_update_.find(scope_info->UniqueIdInScript()) !=
2036-
scope_infos_to_update_.end()) {
2048+
auto it = scope_infos_to_update_.find(scope_info->UniqueIdInScript());
2049+
if (it != scope_infos_to_update_.end()) {
2050+
// Once we find an already recorded scope info, it need to match the one
2051+
// on the chain.
2052+
if (V8_UNLIKELY(*it->second != scope_info)) {
2053+
info->Print();
2054+
(*it->second)->Print();
2055+
scope_info->Print();
2056+
UNREACHABLE();
2057+
}
20372058
return;
20382059
}
20392060
scope_infos_to_update_[scope_info->UniqueIdInScript()] =
@@ -2062,17 +2083,51 @@ class ConstantPoolPointerForwarder {
20622083
!scope_infos_to_update_.empty();
20632084
}
20642085

2065-
void UpdateOuterScopeInfo(Tagged<SharedFunctionInfo> sfi) {
2086+
// Find an own scope info for the sfi based on the UniqueIdInScript that the
2087+
// own scope info would have. This works even if the SFI doesn't yet have a
2088+
// scope info attached by computing UniqueIdInScript from the SFI position.
2089+
//
2090+
// This should only directly be used for SFIs that already existed on the
2091+
// script. Their outer scope info will already be correct.
2092+
bool InstallOwnScopeInfo(Tagged<SharedFunctionInfo> sfi) {
2093+
auto it = scope_infos_to_update_.find(sfi->UniqueIdInScript());
2094+
if (it == scope_infos_to_update_.end()) return false;
2095+
sfi->SetScopeInfo(*it->second);
2096+
return true;
2097+
}
2098+
2099+
// Either replace the own scope info of the sfi, or the first outer scope info
2100+
// that was recorded.
2101+
//
2102+
// This has to be used for all newly created SFIs since their outer scope info
2103+
// also may need to be reattached.
2104+
void UpdateScopeInfo(Tagged<SharedFunctionInfo> sfi) {
2105+
// This should not be called on already existing SFIs. Their scope infos are
2106+
// already correct.
2107+
DCHECK_NE(MakeWeak(sfi),
2108+
old_script_->infos()->get(sfi->function_literal_id()));
2109+
if (InstallOwnScopeInfo(sfi)) return;
20662110
if (!sfi->HasOuterScopeInfo()) return;
2111+
2112+
Tagged<ScopeInfo> parent =
2113+
sfi->scope_info()->IsEmpty() ? Tagged<ScopeInfo>() : sfi->scope_info();
20672114
Tagged<ScopeInfo> outer_info = sfi->GetOuterScopeInfo();
2115+
20682116
auto it = scope_infos_to_update_.find(outer_info->UniqueIdInScript());
2069-
if (it == scope_infos_to_update_.end()) return;
2117+
while (it == scope_infos_to_update_.end()) {
2118+
if (!outer_info->HasOuterScopeInfo()) return;
2119+
parent = outer_info;
2120+
outer_info = outer_info->OuterScopeInfo();
2121+
it = scope_infos_to_update_.find(outer_info->UniqueIdInScript());
2122+
}
20702123
if (outer_info == *it->second) return;
2124+
20712125
VerifyScopeInfo(outer_info, *it->second);
2072-
if (sfi->is_compiled()) {
2073-
sfi->scope_info()->set_outer_scope_info(*it->second);
2074-
} else {
2126+
2127+
if (parent.is_null()) {
20752128
sfi->set_raw_outer_scope_info_or_feedback_metadata(*it->second);
2129+
} else {
2130+
parent->set_outer_scope_info(*it->second);
20762131
}
20772132
}
20782133

@@ -2254,6 +2309,16 @@ void BackgroundMergeTask::BeginMergeInBackground(
22542309
new_compiled_data_for_cached_sfis_.push_back(
22552310
{local_heap->NewPersistentHandle(old_sfi),
22562311
local_heap->NewPersistentHandle(new_sfi)});
2312+
// Pick up existing scope infos from the old sfi. The new sfi will be
2313+
// copied over the old sfi later. This will ensure that we'll keep
2314+
// using the old sfis. This will also allow us check later whether new
2315+
// scope infos have appeared that need to be reused.
2316+
if (!old_sfi->scope_info()->IsEmpty()) {
2317+
new_sfi->SetScopeInfo(old_sfi->scope_info());
2318+
} else if (old_sfi->HasOuterScopeInfo()) {
2319+
new_sfi->scope_info()->set_outer_scope_info(
2320+
old_sfi->GetOuterScopeInfo());
2321+
}
22572322
forwarder.AddBytecodeArray(new_sfi->GetBytecodeArray(isolate));
22582323
}
22592324
} else {
@@ -2278,13 +2343,23 @@ void BackgroundMergeTask::BeginMergeInBackground(
22782343

22792344
if (forwarder.HasAnythingToForward()) {
22802345
for (DirectHandle<SharedFunctionInfo> new_sfi : used_new_sfis_) {
2281-
forwarder.UpdateOuterScopeInfo(*new_sfi);
2346+
forwarder.UpdateScopeInfo(*new_sfi);
2347+
}
2348+
for (const auto& new_compiled_data : new_compiled_data_for_cached_sfis_) {
2349+
// It's possible that new_compiled_data.cached_sfi had
2350+
// scope_info()->IsEmpty() while an inner function has scope info if the
2351+
// cached_sfi was recreated when an outer function was recompiled. If so,
2352+
// new_compiled_data.new_sfi does not have a reused scope info yet, and
2353+
// we'll have found it when we visited the inner function. Try to pick it
2354+
// up here.
2355+
forwarder.InstallOwnScopeInfo(*new_compiled_data.new_sfi);
22822356
}
22832357
forwarder.IterateAndForwardPointers();
22842358
}
22852359
persistent_handles_ = local_heap->DetachPersistentHandles();
22862360
state_ = kPendingForegroundWork;
22872361
}
2362+
22882363
Handle<SharedFunctionInfo> BackgroundMergeTask::CompleteMergeInForeground(
22892364
Isolate* isolate, DirectHandle<Script> new_script) {
22902365
DCHECK_EQ(state_, kPendingForegroundWork);
@@ -2295,8 +2370,8 @@ Handle<SharedFunctionInfo> BackgroundMergeTask::CompleteMergeInForeground(
22952370
isolate, isolate->main_thread_local_heap(), old_script);
22962371

22972372
for (const auto& new_compiled_data : new_compiled_data_for_cached_sfis_) {
2298-
if (!new_compiled_data.cached_sfi->is_compiled() &&
2299-
new_compiled_data.new_sfi->is_compiled()) {
2373+
Tagged<SharedFunctionInfo> sfi = *new_compiled_data.cached_sfi;
2374+
if (!sfi->is_compiled() && new_compiled_data.new_sfi->is_compiled()) {
23002375
// Updating existing DebugInfos is not supported, but we don't expect
23012376
// uncompiled SharedFunctionInfos to contain DebugInfos.
23022377
DCHECK(!new_compiled_data.cached_sfi->HasDebugInfo(isolate));
@@ -2306,8 +2381,7 @@ Handle<SharedFunctionInfo> BackgroundMergeTask::CompleteMergeInForeground(
23062381
// cached_sfi to new_sfi, and then copy every field using CopyFrom.
23072382
new_compiled_data.new_sfi->set_script(
23082383
new_compiled_data.cached_sfi->script(kAcquireLoad), kReleaseStore);
2309-
new_compiled_data.cached_sfi->CopyFrom(*new_compiled_data.new_sfi,
2310-
isolate);
2384+
sfi->CopyFrom(*new_compiled_data.new_sfi, isolate);
23112385
}
23122386
}
23132387

@@ -2336,12 +2410,17 @@ Handle<SharedFunctionInfo> BackgroundMergeTask::CompleteMergeInForeground(
23362410
// pools is required.
23372411
if (forwarder.HasAnythingToForward()) {
23382412
for (DirectHandle<SharedFunctionInfo> new_sfi : used_new_sfis_) {
2339-
forwarder.UpdateOuterScopeInfo(*new_sfi);
2413+
forwarder.UpdateScopeInfo(*new_sfi);
23402414
if (new_sfi->HasBytecodeArray(isolate)) {
23412415
forwarder.AddBytecodeArray(new_sfi->GetBytecodeArray(isolate));
23422416
}
23432417
}
23442418
for (const auto& new_compiled_data : new_compiled_data_for_cached_sfis_) {
2419+
// It's possible that cached_sfi wasn't compiled, but an inner function
2420+
// existed that didn't exist when be background merged. In that case, pick
2421+
// up the relevant scope infos.
2422+
Tagged<SharedFunctionInfo> sfi = *new_compiled_data.cached_sfi;
2423+
forwarder.InstallOwnScopeInfo(sfi);
23452424
if (new_compiled_data.cached_sfi->HasBytecodeArray(isolate)) {
23462425
forwarder.AddBytecodeArray(
23472426
new_compiled_data.cached_sfi->GetBytecodeArray(isolate));
@@ -2364,6 +2443,45 @@ Handle<SharedFunctionInfo> BackgroundMergeTask::CompleteMergeInForeground(
23642443
SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate, result);
23652444
}
23662445

2446+
if (v8_flags.verify_code_merge) {
2447+
// Check that there aren't any duplicate scope infos. Every scope/context
2448+
// should correspond to at most one scope info.
2449+
std::unordered_map<int, Tagged<ScopeInfo>> scope_infos;
2450+
for (int i = 0; i < old_script->infos()->length(); i++) {
2451+
Tagged<ScopeInfo> scope_info;
2452+
if (!old_script->infos()->get(i).IsWeak()) continue;
2453+
Tagged<HeapObject> info =
2454+
old_script->infos()->get(i).GetHeapObjectAssumeWeak();
2455+
if (Is<SharedFunctionInfo>(info)) {
2456+
Tagged<SharedFunctionInfo> old_sfi = Cast<SharedFunctionInfo>(info);
2457+
if (!old_sfi->scope_info()->IsEmpty()) {
2458+
scope_info = old_sfi->scope_info();
2459+
} else if (old_sfi->HasOuterScopeInfo()) {
2460+
scope_info = old_sfi->GetOuterScopeInfo();
2461+
} else {
2462+
continue;
2463+
}
2464+
} else {
2465+
scope_info = Cast<ScopeInfo>(info);
2466+
}
2467+
while (true) {
2468+
auto it = scope_infos.find(scope_info->UniqueIdInScript());
2469+
if (it != scope_infos.end()) {
2470+
if (*it->second != scope_info) {
2471+
old_script->infos()->get(i).GetHeapObjectAssumeWeak()->Print();
2472+
(*it->second)->Print();
2473+
scope_info->Print();
2474+
UNREACHABLE();
2475+
}
2476+
break;
2477+
}
2478+
scope_infos[scope_info->UniqueIdInScript()] = scope_info;
2479+
if (!scope_info->HasOuterScopeInfo()) break;
2480+
scope_info = scope_info->OuterScopeInfo();
2481+
}
2482+
}
2483+
}
2484+
23672485
return handle_scope.CloseAndEscape(result);
23682486
}
23692487

src/flags/flag-definitions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2217,6 +2217,7 @@ DEFINE_BOOL(
22172217
merge_background_deserialized_script_with_compilation_cache, true,
22182218
"After deserializing code cache data on a background thread, merge it into "
22192219
"an existing Script if one is found in the Isolate compilation cache")
2220+
DEFINE_BOOL(verify_code_merge, false, "Verify scope infos after merge")
22202221
DEFINE_BOOL(
22212222
embedder_instance_types, false,
22222223
"enable type checks based on instance types provided by the embedder")

src/objects/scope-info.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,8 @@ int ScopeInfo::ContextLength() const {
726726
(function_name_context_slot ? 1 : 0);
727727
}
728728

729-
// Needs to be kept in sync with Scope::UniqueIdInScript.
729+
// Needs to be kept in sync with Scope::UniqueIdInScript and
730+
// SharedFunctionInfo::UniqueIdInScript.
730731
int ScopeInfo::UniqueIdInScript() const {
731732
// Script scopes start "before" the script to avoid clashing with a scope that
732733
// starts on character 0.

src/objects/shared-function-info-inl.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -613,11 +613,11 @@ DEF_GETTER(SharedFunctionInfo, outer_scope_info, Tagged<HeapObject>) {
613613

614614
bool SharedFunctionInfo::HasOuterScopeInfo() const {
615615
Tagged<ScopeInfo> outer_info;
616-
if (!is_compiled()) {
616+
Tagged<ScopeInfo> info = scope_info(kAcquireLoad);
617+
if (info->IsEmpty()) {
617618
if (!IsScopeInfo(outer_scope_info())) return false;
618619
outer_info = Cast<ScopeInfo>(outer_scope_info());
619620
} else {
620-
Tagged<ScopeInfo> info = scope_info(kAcquireLoad);
621621
if (!info->HasOuterScopeInfo()) return false;
622622
outer_info = info->OuterScopeInfo();
623623
}
@@ -626,15 +626,17 @@ bool SharedFunctionInfo::HasOuterScopeInfo() const {
626626

627627
Tagged<ScopeInfo> SharedFunctionInfo::GetOuterScopeInfo() const {
628628
DCHECK(HasOuterScopeInfo());
629-
if (!is_compiled()) return Cast<ScopeInfo>(outer_scope_info());
630-
return scope_info(kAcquireLoad)->OuterScopeInfo();
629+
Tagged<ScopeInfo> info = scope_info(kAcquireLoad);
630+
if (info->IsEmpty()) return Cast<ScopeInfo>(outer_scope_info());
631+
return info->OuterScopeInfo();
631632
}
632633

633634
void SharedFunctionInfo::set_outer_scope_info(Tagged<HeapObject> value,
634635
WriteBarrierMode mode) {
635636
DCHECK(!is_compiled());
636637
DCHECK(IsTheHole(raw_outer_scope_info_or_feedback_metadata()));
637638
DCHECK(IsScopeInfo(value) || IsTheHole(value));
639+
DCHECK(scope_info()->IsEmpty());
638640
set_raw_outer_scope_info_or_feedback_metadata(value, mode);
639641
}
640642

src/objects/shared-function-info.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,17 @@ bool SharedFunctionInfo::HasDebugInfo(Isolate* isolate) const {
265265
return isolate->debug()->HasDebugInfo(*this);
266266
}
267267

268+
// Needs to be kept in sync with Scope::UniqueIdInScript and
269+
// ScopeInfo::UniqueIdInScript.
270+
int SharedFunctionInfo::UniqueIdInScript() const {
271+
// Script scopes start "before" the script to avoid clashing with a scope that
272+
// starts on character 0.
273+
if (function_literal_id() == kFunctionLiteralIdTopLevel) return -1;
274+
// Default constructors have the same start position as their parent class
275+
// scope. Use the next char position to distinguish this scope.
276+
return StartPosition() + IsDefaultConstructor(kind());
277+
}
278+
268279
Tagged<DebugInfo> SharedFunctionInfo::GetDebugInfo(Isolate* isolate) const {
269280
return isolate->debug()->TryGetDebugInfo(*this).value();
270281
}

src/objects/shared-function-info.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,8 @@ class SharedFunctionInfo
597597

598598
inline FunctionKind kind() const;
599599

600+
int UniqueIdInScript() const;
601+
600602
// Defines the index in a native context of closure's map instantiated using
601603
// this shared function info.
602604
DECL_INT_ACCESSORS(function_map_index)

0 commit comments

Comments
 (0)