diff --git a/src/api/api.cc b/src/api/api.cc index a776513abf..20186ca45b 100644 --- a/src/api/api.cc +++ b/src/api/api.cc @@ -774,79 +774,11 @@ StartupData SnapshotCreator::CreateBlob( isolate->heap()->CompactWeakArrayLists(internal::AllocationType::kOld); } - if (function_code_handling == FunctionCodeHandling::kClear) { - // Clear out re-compilable data from all shared function infos. Any - // 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> 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 shared : sfis_to_clear) { - i::SharedFunctionInfo::DiscardCompiled(isolate, shared); - } - } + i::Snapshot::ClearReconstructableDataForSerialization( + isolate, function_code_handling == FunctionCodeHandling::kClear); 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. // Note these contexts may be dead after calling Clear(), but will not be // collected until serialization completes and the DisallowHeapAllocation diff --git a/src/d8/d8.cc b/src/d8/d8.cc index 523dff99e3..caf202105c 100644 --- a/src/d8/d8.cc +++ b/src/d8/d8.cc @@ -3046,9 +3046,12 @@ int Shell::RunMain(Isolate* isolate, bool last_run) { DisposeModuleEmbedderData(context); } 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(isolate); i::Handle i_context = Utils::OpenHandle(*context); + i::Snapshot::ClearReconstructableDataForSerialization( + i_isolate, kClearRecompilableData); i::Snapshot::SerializeDeserializeAndVerifyForTesting(i_isolate, i_context); } diff --git a/src/snapshot/snapshot.cc b/src/snapshot/snapshot.cc index 43ff55abe0..6c129a846a 100644 --- a/src/snapshot/snapshot.cc +++ b/src/snapshot/snapshot.cc @@ -10,6 +10,7 @@ #include "src/execution/isolate-inl.h" #include "src/init/bootstrapper.h" #include "src/logging/counters.h" +#include "src/objects/js-regexp-inl.h" #include "src/snapshot/context-deserializer.h" #include "src/snapshot/context-serializer.h" #include "src/snapshot/read-only-deserializer.h" @@ -188,6 +189,79 @@ MaybeHandle Snapshot::NewContextFromSnapshot( 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> 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 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 void Snapshot::SerializeDeserializeAndVerifyForTesting( Isolate* isolate, Handle default_context) { diff --git a/src/snapshot/snapshot.h b/src/snapshot/snapshot.h index 2f49afb536..e0ea02681c 100644 --- a/src/snapshot/snapshot.h +++ b/src/snapshot/snapshot.h @@ -41,6 +41,13 @@ class Snapshot : public AllStatic { 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 // associated callback to serialize internal fields. The default context must // be passed at index 0. diff --git a/test/benchmarks/benchmarks.status b/test/benchmarks/benchmarks.status index 1ad433bb23..127375f26f 100644 --- a/test/benchmarks/benchmarks.status +++ b/test/benchmarks/benchmarks.status @@ -82,4 +82,9 @@ 'octane/typescript': [SKIP], }], # 'predictable' +################################################################################ +['variant == stress_snapshot', { + '*': [SKIP], # only relevant for mjsunit tests. +}], + ] diff --git a/test/cctest/cctest.status b/test/cctest/cctest.status index a45053d823..7b037c00a7 100644 --- a/test/cctest/cctest.status +++ b/test/cctest/cctest.status @@ -631,4 +631,9 @@ 'test-regexp/UnicodePropertyEscapeCodeSize': [SKIP], }], # no_i18n == True +################################################################################ +['variant == stress_snapshot', { + '*': [SKIP], # only relevant for mjsunit tests. +}], + ] diff --git a/test/debugger/debugger.status b/test/debugger/debugger.status index 67c1e9c191..71d0381196 100644 --- a/test/debugger/debugger.status +++ b/test/debugger/debugger.status @@ -162,4 +162,9 @@ 'regress/regress-crbug-1032042': [SKIP], }], # 'isolates' +################################################################################ +['variant == stress_snapshot', { + '*': [SKIP], # only relevant for mjsunit tests. +}], + ] diff --git a/test/debugging/debugging.status b/test/debugging/debugging.status index b5ebc84474..7cbaca0e9b 100644 --- a/test/debugging/debugging.status +++ b/test/debugging/debugging.status @@ -2,4 +2,11 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -[] +[ + +################################################################################ +['variant == stress_snapshot', { + '*': [SKIP], # only relevant for mjsunit tests. +}], + +] diff --git a/test/fuzzer/fuzzer.status b/test/fuzzer/fuzzer.status index 65e51c238b..0a08dfa2e9 100644 --- a/test/fuzzer/fuzzer.status +++ b/test/fuzzer/fuzzer.status @@ -13,4 +13,9 @@ 'wasm_compile/*': [SKIP], }], # lite_mode or variant == jitless +################################################################################ +['variant == stress_snapshot', { + '*': [SKIP], # only relevant for mjsunit tests. +}], + ] diff --git a/test/inspector/inspector.status b/test/inspector/inspector.status index 6eed35594c..858a76141d 100644 --- a/test/inspector/inspector.status +++ b/test/inspector/inspector.status @@ -86,4 +86,9 @@ 'debugger/wasm-*': [SKIP], }], # 'arch == s390 or arch == s390x' +################################################################################ +['variant == stress_snapshot', { + '*': [SKIP], # only relevant for mjsunit tests. +}], + ] diff --git a/test/intl/intl.status b/test/intl/intl.status index eeab557064..1d76ffb123 100644 --- a/test/intl/intl.status +++ b/test/intl/intl.status @@ -66,4 +66,9 @@ 'regress-7770': [SKIP], }], # 'system == android' +################################################################################ +['variant == stress_snapshot', { + '*': [SKIP], # only relevant for mjsunit tests. +}], + ] diff --git a/test/message/message.status b/test/message/message.status index 5e0cedd290..09a90fb4f1 100644 --- a/test/message/message.status +++ b/test/message/message.status @@ -64,4 +64,9 @@ 'asm-*': [SKIP], }], # lite_mode or variant == jitless +################################################################################ +['variant == stress_snapshot', { + '*': [SKIP], # only relevant for mjsunit tests. +}], + ] diff --git a/test/mjsunit/mjsunit.status b/test/mjsunit/mjsunit.status index e395e4fd9b..e5bb2aaf63 100644 --- a/test/mjsunit/mjsunit.status +++ b/test/mjsunit/mjsunit.status @@ -1192,12 +1192,16 @@ }], # variant == assert_types ############################################################################## -['variant == stress_snapshot', { - # TODO(jgruber): Remove this wildcard line ASAP. +['variant == stress_snapshot and arch != x64', { + # Deserialization fails due to read-only snapshot checksum verification. + # https://crbug.com/v8/10491 '*': [SKIP], +}], +['variant == stress_snapshot and arch == x64', { # Crashes the serializer due to recursion. 'deep-recursion': [SKIP], + 'string-replace-gc': [SKIP], # Check failed: !field_type.NowStable() || field_type.NowContains(value). 'eval': [SKIP], 'regress/regress-737588': [SKIP], diff --git a/test/mozilla/mozilla.status b/test/mozilla/mozilla.status index cb556331bd..37fb66884f 100644 --- a/test/mozilla/mozilla.status +++ b/test/mozilla/mozilla.status @@ -1063,4 +1063,9 @@ '*': [SKIP], }], # variant == no_wasm_traps +################################################################################ +['variant == stress_snapshot', { + '*': [SKIP], # only relevant for mjsunit tests. +}], + ] diff --git a/test/test262/test262.status b/test/test262/test262.status index f24337f8b5..2cf2de5107 100644 --- a/test/test262/test262.status +++ b/test/test262/test262.status @@ -775,4 +775,9 @@ 'intl402/DateTimeFormat/prototype/resolvedOptions/basic': [SKIP], }], # system == windows +################################################################################ +['variant == stress_snapshot', { + '*': [SKIP], # only relevant for mjsunit tests. +}], + ] diff --git a/test/unittests/unittests.status b/test/unittests/unittests.status index 8d5aac70d9..61bf5d1718 100644 --- a/test/unittests/unittests.status +++ b/test/unittests/unittests.status @@ -48,4 +48,9 @@ 'DecompressionOptimizerTest.*': [SKIP], }], # not pointer_compression +################################################################################ +['variant == stress_snapshot', { + '*': [SKIP], # only relevant for mjsunit tests. +}], + ] diff --git a/test/wasm-api-tests/wasm-api-tests.status b/test/wasm-api-tests/wasm-api-tests.status index a5d554103f..05488c1711 100644 --- a/test/wasm-api-tests/wasm-api-tests.status +++ b/test/wasm-api-tests/wasm-api-tests.status @@ -3,9 +3,15 @@ # found in the LICENSE file. [ + ['lite_mode or variant == jitless', { # TODO(v8:7777): Re-enable once wasm is supported in jitless mode. '*': [SKIP], }], # lite_mode or variant == jitless +################################################################################ +['variant == stress_snapshot', { + '*': [SKIP], # only relevant for mjsunit tests. +}], + ] diff --git a/test/wasm-js/wasm-js.status b/test/wasm-js/wasm-js.status index a8df481491..2dec35869a 100644 --- a/test/wasm-js/wasm-js.status +++ b/test/wasm-js/wasm-js.status @@ -3,6 +3,7 @@ # found in the LICENSE file. [ + [ALWAYS, { # These are slow, and not useful to run for the proposals: 'proposals/reference-types/limits': [SKIP], @@ -34,4 +35,9 @@ '*': [SKIP], }], # lite_mode or variant == jitless +################################################################################ +['variant == stress_snapshot', { + '*': [SKIP], # only relevant for mjsunit tests. +}], + ] diff --git a/test/wasm-spec-tests/wasm-spec-tests.status b/test/wasm-spec-tests/wasm-spec-tests.status index b8bffdcae8..d82c2f218f 100644 --- a/test/wasm-spec-tests/wasm-spec-tests.status +++ b/test/wasm-spec-tests/wasm-spec-tests.status @@ -99,5 +99,9 @@ '*': [SKIP], }], # lite_mode or variant == jitless +################################################################################ +['variant == stress_snapshot', { + '*': [SKIP], # only relevant for mjsunit tests. +}], ] diff --git a/test/webkit/webkit.status b/test/webkit/webkit.status index b2938c8921..162d843bda 100644 --- a/test/webkit/webkit.status +++ b/test/webkit/webkit.status @@ -136,5 +136,9 @@ 'fast/js/string-capitalization': [FAIL], }], # variant == no_wasm_traps -############################################################################## +################################################################################ +['variant == stress_snapshot', { + '*': [SKIP], # only relevant for mjsunit tests. +}], + ]