Commit 3c422d1c authored by Jakob Gruber's avatar Jakob Gruber Committed by Commit Bot

[snapshot] Clear reconstructable data prior to d8 stress_snapshot run

The serializer currently cannot handle a heap state containing
arbitrary compiled Code objects. As a quick fix for the
--stress-snapshot d8 flag, we clear compiled data from the isolate
prior to the serialize-deserialize-verify pass.

With this change, mjsunit tests pass on x64.

The %SerializeDeserializeNow() runtime function would require more
work, since it is not possible to mutate the heap to this extent while
still preserving a runnable host context and isolate. We will need
another solution there.

Drive-by: Skip the stress_snapshot variant except for the mjsunit
suite.

Tbr: machenbach@chromium.org
Bug: v8:10493,v8:10416
Change-Id: Ie110da8b51613fcd69c7f391d3cf8589d6b04dd8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2182429Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67585}
parent e7e10aa7
...@@ -774,79 +774,11 @@ StartupData SnapshotCreator::CreateBlob( ...@@ -774,79 +774,11 @@ StartupData SnapshotCreator::CreateBlob(
isolate->heap()->CompactWeakArrayLists(internal::AllocationType::kOld); isolate->heap()->CompactWeakArrayLists(internal::AllocationType::kOld);
} }
if (function_code_handling == FunctionCodeHandling::kClear) { i::Snapshot::ClearReconstructableDataForSerialization(
// Clear out re-compilable data from all shared function infos. Any isolate, function_code_handling == FunctionCodeHandling::kClear);
// JSFunctions using these SFIs will have their code pointers reset by the
// context serializer.
//
// We have to iterate the heap and collect handles to each clearable SFI,
// before we disable allocation, since we have to allocate UncompiledDatas
// to be able to recompile them.
//
// Compiled irregexp code is also flushed by collecting and clearing any
// seen JSRegExp objects.
i::HandleScope scope(isolate);
std::vector<i::Handle<i::SharedFunctionInfo>> sfis_to_clear;
{ // Heap allocation is disallowed within this scope.
i::HeapObjectIterator heap_iterator(isolate->heap());
for (i::HeapObject current_obj = heap_iterator.Next();
!current_obj.is_null(); current_obj = heap_iterator.Next()) {
if (current_obj.IsSharedFunctionInfo()) {
i::SharedFunctionInfo shared =
i::SharedFunctionInfo::cast(current_obj);
if (shared.CanDiscardCompiled()) {
sfis_to_clear.emplace_back(shared, isolate);
}
} else if (current_obj.IsJSRegExp()) {
i::JSRegExp regexp = i::JSRegExp::cast(current_obj);
if (regexp.HasCompiledCode()) {
regexp.DiscardCompiledCodeForSerialization();
}
}
}
}
// Must happen after heap iteration since SFI::DiscardCompiled may allocate.
for (i::Handle<i::SharedFunctionInfo> shared : sfis_to_clear) {
i::SharedFunctionInfo::DiscardCompiled(isolate, shared);
}
}
i::DisallowHeapAllocation no_gc_from_here_on; i::DisallowHeapAllocation no_gc_from_here_on;
i::HeapObjectIterator heap_iterator(isolate->heap());
for (i::HeapObject current_obj = heap_iterator.Next(); !current_obj.is_null();
current_obj = heap_iterator.Next()) {
if (current_obj.IsJSFunction()) {
i::JSFunction fun = i::JSFunction::cast(current_obj);
// Complete in-object slack tracking for all functions.
fun.CompleteInobjectSlackTrackingIfActive();
// Also, clear out feedback vectors, or any optimized code.
// Note that checking for fun.IsOptimized() || fun.IsInterpreted() is not
// sufficient because the function can have a feedback vector even if it
// is not compiled (e.g. when the bytecode was flushed). On the other
// hand, only checking for the feedback vector is not sufficient because
// there can be multiple functions sharing the same feedback vector. So we
// need all these checks.
if (fun.IsOptimized() || fun.IsInterpreted() ||
!fun.raw_feedback_cell().value().IsUndefined()) {
fun.raw_feedback_cell().set_value(
i::ReadOnlyRoots(isolate).undefined_value());
fun.set_code(isolate->builtins()->builtin(i::Builtins::kCompileLazy));
}
#ifdef DEBUG
if (function_code_handling == FunctionCodeHandling::kClear) {
DCHECK(fun.shared().HasWasmExportedFunctionData() ||
fun.shared().HasBuiltinId() || fun.shared().IsApiFunction() ||
fun.shared().HasUncompiledDataWithoutPreparseData());
}
#endif // DEBUG
}
}
// Create a vector with all contexts and clear associated Persistent fields. // Create a vector with all contexts and clear associated Persistent fields.
// Note these contexts may be dead after calling Clear(), but will not be // Note these contexts may be dead after calling Clear(), but will not be
// collected until serialization completes and the DisallowHeapAllocation // collected until serialization completes and the DisallowHeapAllocation
......
...@@ -3046,9 +3046,12 @@ int Shell::RunMain(Isolate* isolate, bool last_run) { ...@@ -3046,9 +3046,12 @@ int Shell::RunMain(Isolate* isolate, bool last_run) {
DisposeModuleEmbedderData(context); DisposeModuleEmbedderData(context);
} }
WriteLcovData(isolate, options.lcov_file); WriteLcovData(isolate, options.lcov_file);
if (options.stress_snapshot) { if (last_run && options.stress_snapshot) {
static constexpr bool kClearRecompilableData = true;
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::Handle<i::Context> i_context = Utils::OpenHandle(*context); i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
i::Snapshot::ClearReconstructableDataForSerialization(
i_isolate, kClearRecompilableData);
i::Snapshot::SerializeDeserializeAndVerifyForTesting(i_isolate, i::Snapshot::SerializeDeserializeAndVerifyForTesting(i_isolate,
i_context); i_context);
} }
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "src/execution/isolate-inl.h" #include "src/execution/isolate-inl.h"
#include "src/init/bootstrapper.h" #include "src/init/bootstrapper.h"
#include "src/logging/counters.h" #include "src/logging/counters.h"
#include "src/objects/js-regexp-inl.h"
#include "src/snapshot/context-deserializer.h" #include "src/snapshot/context-deserializer.h"
#include "src/snapshot/context-serializer.h" #include "src/snapshot/context-serializer.h"
#include "src/snapshot/read-only-deserializer.h" #include "src/snapshot/read-only-deserializer.h"
...@@ -188,6 +189,79 @@ MaybeHandle<Context> Snapshot::NewContextFromSnapshot( ...@@ -188,6 +189,79 @@ MaybeHandle<Context> Snapshot::NewContextFromSnapshot(
return result; return result;
} }
// static
void Snapshot::ClearReconstructableDataForSerialization(
Isolate* isolate, bool clear_recompilable_data) {
// Clear SFIs and JSRegExps.
if (clear_recompilable_data) {
HandleScope scope(isolate);
std::vector<i::Handle<i::SharedFunctionInfo>> sfis_to_clear;
{ // Heap allocation is disallowed within this scope.
i::HeapObjectIterator it(isolate->heap());
for (i::HeapObject o = it.Next(); !o.is_null(); o = it.Next()) {
if (o.IsSharedFunctionInfo()) {
i::SharedFunctionInfo shared = i::SharedFunctionInfo::cast(o);
if (shared.script().IsScript() &&
Script::cast(shared.script()).type() == Script::TYPE_EXTENSION) {
continue; // Don't clear extensions, they cannot be recompiled.
}
if (shared.CanDiscardCompiled()) {
sfis_to_clear.emplace_back(shared, isolate);
}
} else if (o.IsJSRegExp()) {
i::JSRegExp regexp = i::JSRegExp::cast(o);
if (regexp.HasCompiledCode()) {
regexp.DiscardCompiledCodeForSerialization();
}
}
}
}
// Must happen after heap iteration since SFI::DiscardCompiled may allocate.
for (i::Handle<i::SharedFunctionInfo> shared : sfis_to_clear) {
i::SharedFunctionInfo::DiscardCompiled(isolate, shared);
}
}
// Clear JSFunctions.
i::HeapObjectIterator it(isolate->heap());
for (i::HeapObject o = it.Next(); !o.is_null(); o = it.Next()) {
if (!o.IsJSFunction()) continue;
i::JSFunction fun = i::JSFunction::cast(o);
fun.CompleteInobjectSlackTrackingIfActive();
i::SharedFunctionInfo shared = fun.shared();
if (shared.script().IsScript() &&
Script::cast(shared.script()).type() == Script::TYPE_EXTENSION) {
continue; // Don't clear extensions, they cannot be recompiled.
}
// Also, clear out feedback vectors, or any optimized code.
// Note that checking for fun.IsOptimized() || fun.IsInterpreted() is
// not sufficient because the function can have a feedback vector even
// if it is not compiled (e.g. when the bytecode was flushed). On the
// other hand, only checking for the feedback vector is not sufficient
// because there can be multiple functions sharing the same feedback
// vector. So we need all these checks.
if (fun.IsOptimized() || fun.IsInterpreted() ||
!fun.raw_feedback_cell().value().IsUndefined()) {
fun.raw_feedback_cell().set_value(
i::ReadOnlyRoots(isolate).undefined_value());
fun.set_code(isolate->builtins()->builtin(i::Builtins::kCompileLazy));
}
#ifdef DEBUG
if (clear_recompilable_data) {
DCHECK(fun.shared().HasWasmExportedFunctionData() ||
fun.shared().HasBuiltinId() || fun.shared().IsApiFunction() ||
fun.shared().HasUncompiledDataWithoutPreparseData());
}
#endif // DEBUG
}
}
// static // static
void Snapshot::SerializeDeserializeAndVerifyForTesting( void Snapshot::SerializeDeserializeAndVerifyForTesting(
Isolate* isolate, Handle<Context> default_context) { Isolate* isolate, Handle<Context> default_context) {
......
...@@ -41,6 +41,13 @@ class Snapshot : public AllStatic { ...@@ -41,6 +41,13 @@ class Snapshot : public AllStatic {
V8_EXPORT_PRIVATE static constexpr SerializerFlags kDefaultSerializerFlags = V8_EXPORT_PRIVATE static constexpr SerializerFlags kDefaultSerializerFlags =
{}; {};
// In preparation for serialization, clear data from the given isolate's heap
// that 1. can be reconstructed and 2. is not suitable for serialization. The
// `clear_recompilable_data` flag controls whether compiled objects are
// cleared from shared function infos and regexp objects.
V8_EXPORT_PRIVATE static void ClearReconstructableDataForSerialization(
Isolate* isolate, bool clear_recompilable_data);
// Serializes the given isolate and contexts. Each context may have an // Serializes the given isolate and contexts. Each context may have an
// associated callback to serialize internal fields. The default context must // associated callback to serialize internal fields. The default context must
// be passed at index 0. // be passed at index 0.
......
...@@ -82,4 +82,9 @@ ...@@ -82,4 +82,9 @@
'octane/typescript': [SKIP], 'octane/typescript': [SKIP],
}], # 'predictable' }], # 'predictable'
################################################################################
['variant == stress_snapshot', {
'*': [SKIP], # only relevant for mjsunit tests.
}],
] ]
...@@ -631,4 +631,9 @@ ...@@ -631,4 +631,9 @@
'test-regexp/UnicodePropertyEscapeCodeSize': [SKIP], 'test-regexp/UnicodePropertyEscapeCodeSize': [SKIP],
}], # no_i18n == True }], # no_i18n == True
################################################################################
['variant == stress_snapshot', {
'*': [SKIP], # only relevant for mjsunit tests.
}],
] ]
...@@ -162,4 +162,9 @@ ...@@ -162,4 +162,9 @@
'regress/regress-crbug-1032042': [SKIP], 'regress/regress-crbug-1032042': [SKIP],
}], # 'isolates' }], # 'isolates'
################################################################################
['variant == stress_snapshot', {
'*': [SKIP], # only relevant for mjsunit tests.
}],
] ]
...@@ -2,4 +2,11 @@ ...@@ -2,4 +2,11 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
[] [
################################################################################
['variant == stress_snapshot', {
'*': [SKIP], # only relevant for mjsunit tests.
}],
]
...@@ -13,4 +13,9 @@ ...@@ -13,4 +13,9 @@
'wasm_compile/*': [SKIP], 'wasm_compile/*': [SKIP],
}], # lite_mode or variant == jitless }], # lite_mode or variant == jitless
################################################################################
['variant == stress_snapshot', {
'*': [SKIP], # only relevant for mjsunit tests.
}],
] ]
...@@ -86,4 +86,9 @@ ...@@ -86,4 +86,9 @@
'debugger/wasm-*': [SKIP], 'debugger/wasm-*': [SKIP],
}], # 'arch == s390 or arch == s390x' }], # 'arch == s390 or arch == s390x'
################################################################################
['variant == stress_snapshot', {
'*': [SKIP], # only relevant for mjsunit tests.
}],
] ]
...@@ -66,4 +66,9 @@ ...@@ -66,4 +66,9 @@
'regress-7770': [SKIP], 'regress-7770': [SKIP],
}], # 'system == android' }], # 'system == android'
################################################################################
['variant == stress_snapshot', {
'*': [SKIP], # only relevant for mjsunit tests.
}],
] ]
...@@ -64,4 +64,9 @@ ...@@ -64,4 +64,9 @@
'asm-*': [SKIP], 'asm-*': [SKIP],
}], # lite_mode or variant == jitless }], # lite_mode or variant == jitless
################################################################################
['variant == stress_snapshot', {
'*': [SKIP], # only relevant for mjsunit tests.
}],
] ]
...@@ -1192,12 +1192,16 @@ ...@@ -1192,12 +1192,16 @@
}], # variant == assert_types }], # variant == assert_types
############################################################################## ##############################################################################
['variant == stress_snapshot', { ['variant == stress_snapshot and arch != x64', {
# TODO(jgruber): Remove this wildcard line ASAP. # Deserialization fails due to read-only snapshot checksum verification.
# https://crbug.com/v8/10491
'*': [SKIP], '*': [SKIP],
}],
['variant == stress_snapshot and arch == x64', {
# Crashes the serializer due to recursion. # Crashes the serializer due to recursion.
'deep-recursion': [SKIP], 'deep-recursion': [SKIP],
'string-replace-gc': [SKIP],
# Check failed: !field_type.NowStable() || field_type.NowContains(value). # Check failed: !field_type.NowStable() || field_type.NowContains(value).
'eval': [SKIP], 'eval': [SKIP],
'regress/regress-737588': [SKIP], 'regress/regress-737588': [SKIP],
......
...@@ -1063,4 +1063,9 @@ ...@@ -1063,4 +1063,9 @@
'*': [SKIP], '*': [SKIP],
}], # variant == no_wasm_traps }], # variant == no_wasm_traps
################################################################################
['variant == stress_snapshot', {
'*': [SKIP], # only relevant for mjsunit tests.
}],
] ]
...@@ -775,4 +775,9 @@ ...@@ -775,4 +775,9 @@
'intl402/DateTimeFormat/prototype/resolvedOptions/basic': [SKIP], 'intl402/DateTimeFormat/prototype/resolvedOptions/basic': [SKIP],
}], # system == windows }], # system == windows
################################################################################
['variant == stress_snapshot', {
'*': [SKIP], # only relevant for mjsunit tests.
}],
] ]
...@@ -48,4 +48,9 @@ ...@@ -48,4 +48,9 @@
'DecompressionOptimizerTest.*': [SKIP], 'DecompressionOptimizerTest.*': [SKIP],
}], # not pointer_compression }], # not pointer_compression
################################################################################
['variant == stress_snapshot', {
'*': [SKIP], # only relevant for mjsunit tests.
}],
] ]
...@@ -3,9 +3,15 @@ ...@@ -3,9 +3,15 @@
# found in the LICENSE file. # found in the LICENSE file.
[ [
['lite_mode or variant == jitless', { ['lite_mode or variant == jitless', {
# TODO(v8:7777): Re-enable once wasm is supported in jitless mode. # TODO(v8:7777): Re-enable once wasm is supported in jitless mode.
'*': [SKIP], '*': [SKIP],
}], # lite_mode or variant == jitless }], # lite_mode or variant == jitless
################################################################################
['variant == stress_snapshot', {
'*': [SKIP], # only relevant for mjsunit tests.
}],
] ]
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
# found in the LICENSE file. # found in the LICENSE file.
[ [
[ALWAYS, { [ALWAYS, {
# These are slow, and not useful to run for the proposals: # These are slow, and not useful to run for the proposals:
'proposals/reference-types/limits': [SKIP], 'proposals/reference-types/limits': [SKIP],
...@@ -34,4 +35,9 @@ ...@@ -34,4 +35,9 @@
'*': [SKIP], '*': [SKIP],
}], # lite_mode or variant == jitless }], # lite_mode or variant == jitless
################################################################################
['variant == stress_snapshot', {
'*': [SKIP], # only relevant for mjsunit tests.
}],
] ]
...@@ -99,5 +99,9 @@ ...@@ -99,5 +99,9 @@
'*': [SKIP], '*': [SKIP],
}], # lite_mode or variant == jitless }], # lite_mode or variant == jitless
################################################################################
['variant == stress_snapshot', {
'*': [SKIP], # only relevant for mjsunit tests.
}],
] ]
...@@ -136,5 +136,9 @@ ...@@ -136,5 +136,9 @@
'fast/js/string-capitalization': [FAIL], 'fast/js/string-capitalization': [FAIL],
}], # variant == no_wasm_traps }], # variant == no_wasm_traps
############################################################################## ################################################################################
['variant == stress_snapshot', {
'*': [SKIP], # only relevant for mjsunit tests.
}],
] ]
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