From afc49f47254cda5a2355c4ed2afbbf6b6f8e1e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Fl=C3=BCckiger?= Date: Tue, 13 Dec 2022 14:37:08 +0000 Subject: [PATCH] [static-roots] Support serializing read-only heap as a memory dump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build mode for serializing the read only heap as a memory dump in the startup snapshot. This makes compressed pointers of root objects statically known at mksnapshot time. This CL also adds a feature to mksnapshot to dump the static addresses to a C++ header file. This will allow us to use these addresses in the future. The mode is disabled for now since we need some build infrastructure first to conveniently re-generate the table when the layout changes. Bug: v8:13466 Change-Id: I975b15bd89fedf713fb7d12b4929935ece78139d Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4056181 Commit-Queue: Olivier Flückiger Reviewed-by: Michael Lippautz Reviewed-by: Igor Sheludko Reviewed-by: Toon Verwaest Cr-Commit-Position: refs/heads/main@{#84815} --- BUILD.bazel | 3 + BUILD.gn | 3 + src/common/globals.h | 11 ++++ src/execution/isolate.cc | 41 +++++++++++++ src/execution/isolate.h | 2 + src/flags/flag-definitions.h | 2 + src/heap/memory-allocator.cc | 31 +++++----- src/heap/memory-allocator.h | 12 +++- src/heap/read-only-spaces.cc | 42 +++++++++++++ src/heap/read-only-spaces.h | 4 +- src/heap/setup-heap-internal.cc | 21 ++++++- src/roots/roots.cc | 16 +++++ src/roots/roots.h | 4 ++ src/roots/static-roots.h | 20 +++++++ src/snapshot/code-serializer.cc | 24 +------- src/snapshot/code-serializer.h | 3 - src/snapshot/deserializer.cc | 10 +++- src/snapshot/mksnapshot.cc | 7 ++- src/snapshot/read-only-deserializer.cc | 54 +++++++++++++---- src/snapshot/read-only-deserializer.h | 2 + src/snapshot/read-only-serializer.cc | 82 +++++++++++++++++--------- src/snapshot/roots-serializer.h | 2 + src/snapshot/serializer.cc | 23 ++++++++ src/snapshot/serializer.h | 4 ++ src/snapshot/static-roots-gen.cc | 58 ++++++++++++++++++ src/snapshot/static-roots-gen.h | 21 +++++++ src/utils/allocation.cc | 2 +- 27 files changed, 414 insertions(+), 90 deletions(-) create mode 100644 src/roots/static-roots.h create mode 100644 src/snapshot/static-roots-gen.cc create mode 100644 src/snapshot/static-roots-gen.h diff --git a/BUILD.bazel b/BUILD.bazel index 0627af95ba..b1ca4e93d6 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -2036,6 +2036,7 @@ filegroup( "src/roots/roots-inl.h", "src/roots/roots.cc", "src/roots/roots.h", + "src/roots/static-roots.h", "src/runtime/runtime-array.cc", "src/runtime/runtime-atomics.cc", "src/runtime/runtime-bigint.cc", @@ -3234,6 +3235,8 @@ filegroup( "src/snapshot/embedded/platform-embedded-file-writer-mac.h", "src/snapshot/embedded/platform-embedded-file-writer-win.cc", "src/snapshot/embedded/platform-embedded-file-writer-win.h", + "src/snapshot/static-roots-gen.cc", + "src/snapshot/static-roots-gen.h", "src/snapshot/mksnapshot.cc", "src/snapshot/snapshot-empty.cc", ], diff --git a/BUILD.gn b/BUILD.gn index 1d9a331f1c..e09bf65323 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -3508,6 +3508,7 @@ v8_header_set("v8_internal_headers") { "src/regexp/special-case.h", "src/roots/roots-inl.h", "src/roots/roots.h", + "src/roots/static-roots.h", "src/runtime/runtime-utils.h", "src/runtime/runtime.h", "src/sandbox/bounded-size-inl.h", @@ -6227,6 +6228,8 @@ if (current_toolchain == v8_snapshot_toolchain) { "src/snapshot/embedded/platform-embedded-file-writer-win.h", "src/snapshot/mksnapshot.cc", "src/snapshot/snapshot-empty.cc", + "src/snapshot/static-roots-gen.cc", + "src/snapshot/static-roots-gen.h", ] if (v8_control_flow_integrity) { diff --git a/src/common/globals.h b/src/common/globals.h index d488d01b6d..f552c8cb24 100644 --- a/src/common/globals.h +++ b/src/common/globals.h @@ -122,6 +122,17 @@ namespace internal { #define V8_CAN_CREATE_SHARED_HEAP_BOOL false #endif +// Disabling WASM or INTL invalidates the contents of static-roots.h +#if defined(V8_SHARED_RO_HEAP) && defined(V8_COMPRESS_POINTERS) && \ + defined(V8_COMPRESS_POINTERS_IN_SHARED_CAGE) && V8_ENABLE_WEBASSEMBLY && \ + defined(V8_INTL_SUPPORT) +// TODO(olivf, v8:13466): Add build infra to conveniently re-regenerate the +// static roots table and then enable it. +#define V8_STATIC_ROOTS_BOOL false +#else +#define V8_STATIC_ROOTS_BOOL false +#endif + #ifdef V8_ENABLE_SANDBOX #define V8_ENABLE_SANDBOX_BOOL true #else diff --git a/src/execution/isolate.cc b/src/execution/isolate.cc index 1870e0295c..82da1764d3 100644 --- a/src/execution/isolate.cc +++ b/src/execution/isolate.cc @@ -34,6 +34,7 @@ #include "src/codegen/compilation-cache.h" #include "src/codegen/flush-instruction-cache.h" #include "src/common/assert-scope.h" +#include "src/common/globals.h" #include "src/common/ptr-compr-inl.h" #include "src/compiler-dispatcher/lazy-compile-dispatcher.h" #include "src/compiler-dispatcher/optimizing-compile-dispatcher.h" @@ -99,6 +100,7 @@ #include "src/profiler/heap-profiler.h" #include "src/profiler/tracing-cpu-profiler.h" #include "src/regexp/regexp-stack.h" +#include "src/roots/static-roots.h" #include "src/snapshot/embedded/embedded-data-inl.h" #include "src/snapshot/embedded/embedded-file-writer-interface.h" #include "src/snapshot/read-only-deserializer.h" @@ -423,6 +425,20 @@ size_t Isolate::HashIsolateForEmbeddedBlob() { static constexpr size_t kSeed = 0; size_t hash = kSeed; + // Hash static entries of the roots table. + hash = base::hash_combine(hash, V8_STATIC_ROOTS_BOOL); +#if V8_STATIC_ROOTS_BOOL + hash = base::hash_combine(hash, + static_cast(RootIndex::kReadOnlyRootsCount)); + RootIndex i = RootIndex::kFirstReadOnlyRoot; + for (auto ptr : StaticReadOnlyRootsPointerTable) { + hash = base::hash_combine(ptr, hash); + hash = base::hash_combine(std::hash{}(roots_table().name(i)), + hash); + ++i; + } +#endif // V8_STATIC_ROOTS_BOOL + // Hash data sections of builtin code objects. for (Builtin builtin = Builtins::kFirst; builtin <= Builtins::kLast; ++builtin) { @@ -4136,6 +4152,30 @@ VirtualMemoryCage* Isolate::GetPtrComprCodeCageForTesting() { return V8_EXTERNAL_CODE_SPACE_BOOL ? heap_.code_range() : GetPtrComprCage(); } +// If this check fails mksnapshot needs to be built without static roots and +// then called with --static-roots to re-regenerate the static-roots.h file. +void Isolate::VerifyStaticRoots() { +#if V8_STATIC_ROOTS_BOOL + auto& roots = roots_table(); + CHECK_EQ(static_cast(RootIndex::kReadOnlyRootsCount), + StaticReadOnlyRootsPointerTable.size()); + RootIndex idx = RootIndex::kFirstReadOnlyRoot; + ReadOnlyPage* first_page = read_only_heap()->read_only_space()->pages()[0]; + for (Tagged_t cmp_ptr : StaticReadOnlyRootsPointerTable) { + Address the_root = roots[idx]; + Address ptr = + V8HeapCompressionScheme::DecompressTaggedPointer(cage_base(), cmp_ptr); + CHECK_EQ(the_root, ptr); + // All roots must fit on first page, since only this page is guaranteed to + // have a stable offset from the cage base. If this ever changes we need + // to load more pages with predictable offset at + // ReadOnlySpace::InitFromMemoryDump. + CHECK(first_page->Contains(the_root)); + ++idx; + } +#endif // V8_STATIC_ROOTS_BOOL +} + bool Isolate::Init(SnapshotData* startup_snapshot_data, SnapshotData* read_only_snapshot_data, SnapshotData* shared_heap_snapshot_data, bool can_rehash) { @@ -4482,6 +4522,7 @@ bool Isolate::Init(SnapshotData* startup_snapshot_data, can_rehash); startup_deserializer.DeserializeIntoIsolate(); } + if (DEBUG_BOOL) VerifyStaticRoots(); load_stub_cache_->Initialize(); store_stub_cache_->Initialize(); interpreter_->Initialize(); diff --git a/src/execution/isolate.h b/src/execution/isolate.h index bcb275a91a..a32f999fe5 100644 --- a/src/execution/isolate.h +++ b/src/execution/isolate.h @@ -2040,6 +2040,8 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { Object LocalsBlockListCacheGet(Handle scope_info); private: + void VerifyStaticRoots(); + explicit Isolate(std::unique_ptr isolate_allocator, bool is_shared); ~Isolate(); diff --git a/src/flags/flag-definitions.h b/src/flags/flag-definitions.h index 981af0b483..350540516a 100644 --- a/src/flags/flag-definitions.h +++ b/src/flags/flag-definitions.h @@ -2001,6 +2001,8 @@ DEFINE_STRING(embedded_src, nullptr, DEFINE_STRING( embedded_variant, nullptr, "Label to disambiguate symbols in embedded data file. (mksnapshot only)") +DEFINE_STRING(static_roots, nullptr, + "Path for writing static-roots.h. (mksnapshot only)") DEFINE_STRING(startup_src, nullptr, "Write V8 startup as C++ src. (mksnapshot only)") DEFINE_STRING(startup_blob, nullptr, diff --git a/src/heap/memory-allocator.cc b/src/heap/memory-allocator.cc index c17f7d1490..e1aa2ca851 100644 --- a/src/heap/memory-allocator.cc +++ b/src/heap/memory-allocator.cc @@ -345,26 +345,28 @@ size_t MemoryAllocator::ComputeChunkSize(size_t area_size, } base::Optional -MemoryAllocator::AllocateUninitializedChunk(BaseSpace* space, size_t area_size, - Executability executable, - PageSize page_size) { -#ifdef V8_COMPRESS_POINTERS +MemoryAllocator::AllocateUninitializedChunkAt(BaseSpace* space, + size_t area_size, + Executability executable, + Address hint, + PageSize page_size) { +#ifndef V8_COMPRESS_POINTERS // When pointer compression is enabled, spaces are expected to be at a // predictable address (see mkgrokdump) so we don't supply a hint and rely on // the deterministic behaviour of the BoundedPageAllocator. - void* address_hint = nullptr; -#else - void* address_hint = AlignedAddress(isolate_->heap()->GetRandomMmapAddr(), - MemoryChunk::kAlignment); + if (hint == kNullAddress) { + hint = reinterpret_cast
(AlignedAddress( + isolate_->heap()->GetRandomMmapAddr(), MemoryChunk::kAlignment)); + } #endif VirtualMemory reservation; size_t chunk_size = ComputeChunkSize(area_size, executable); DCHECK_EQ(chunk_size % GetCommitPageSize(), 0); - Address base = - AllocateAlignedMemory(chunk_size, area_size, MemoryChunk::kAlignment, - executable, address_hint, &reservation); + Address base = AllocateAlignedMemory( + chunk_size, area_size, MemoryChunk::kAlignment, executable, + reinterpret_cast(hint), &reservation); if (base == kNullAddress) return {}; size_ += reservation.size(); @@ -587,12 +589,13 @@ Page* MemoryAllocator::AllocatePage(MemoryAllocator::AllocationMode alloc_mode, return page; } -ReadOnlyPage* MemoryAllocator::AllocateReadOnlyPage(ReadOnlySpace* space) { +ReadOnlyPage* MemoryAllocator::AllocateReadOnlyPage(ReadOnlySpace* space, + Address hint) { DCHECK_EQ(space->identity(), RO_SPACE); size_t size = MemoryChunkLayout::AllocatableMemoryInMemoryChunk(RO_SPACE); base::Optional chunk_info = - AllocateUninitializedChunk(space, size, NOT_EXECUTABLE, - PageSize::kRegular); + AllocateUninitializedChunkAt(space, size, NOT_EXECUTABLE, hint, + PageSize::kRegular); if (!chunk_info) return nullptr; return new (chunk_info->start) ReadOnlyPage( isolate_->heap(), space, chunk_info->size, chunk_info->area_start, diff --git a/src/heap/memory-allocator.h b/src/heap/memory-allocator.h index ed6e4c82fa..8147e70547 100644 --- a/src/heap/memory-allocator.h +++ b/src/heap/memory-allocator.h @@ -194,7 +194,8 @@ class MemoryAllocator { size_t object_size, Executability executable); - ReadOnlyPage* AllocateReadOnlyPage(ReadOnlySpace* space); + ReadOnlyPage* AllocateReadOnlyPage(ReadOnlySpace* space, + Address hint = kNullAddress); std::unique_ptr<::v8::PageAllocator::SharedMemoryMapping> RemapSharedPage( ::v8::PageAllocator::SharedMemory* shared_memory, Address new_address); @@ -293,7 +294,14 @@ class MemoryAllocator { // the unintialized memory region. V8_WARN_UNUSED_RESULT base::Optional AllocateUninitializedChunk(BaseSpace* space, size_t area_size, - Executability executable, PageSize page_size); + Executability executable, PageSize page_size) { + return AllocateUninitializedChunkAt(space, area_size, executable, + kNullAddress, page_size); + } + V8_WARN_UNUSED_RESULT base::Optional + AllocateUninitializedChunkAt(BaseSpace* space, size_t area_size, + Executability executable, Address hint, + PageSize page_size); // Internal raw allocation method that allocates an aligned MemoryChunk and // sets the right memory permissions. diff --git a/src/heap/read-only-spaces.cc b/src/heap/read-only-spaces.cc index 4d079cdb0a..9d264cf686 100644 --- a/src/heap/read-only-spaces.cc +++ b/src/heap/read-only-spaces.cc @@ -21,6 +21,7 @@ #include "src/heap/read-only-heap.h" #include "src/objects/objects-inl.h" #include "src/snapshot/snapshot-data.h" +#include "src/snapshot/snapshot-source-sink.h" #include "src/snapshot/snapshot-utils.h" namespace v8 { @@ -754,5 +755,46 @@ SharedReadOnlySpace::SharedReadOnlySpace(Heap* heap, pages_ = artifacts->pages(); } +void ReadOnlySpace::InitFromMemoryDump(Isolate* isolate, + SnapshotByteSource* in) { + size_t num_pages = in->GetInt(); + auto cage = isolate->GetPtrComprCage(); + + CHECK_LT(num_pages, 10); + + auto first_page = cage->base() + in->GetInt(); + + for (size_t i = 0; i < num_pages; ++i) { + int size = in->GetInt(); + ReadOnlyPage* chunk; + if (i == 0) { + chunk = + heap()->memory_allocator()->AllocateReadOnlyPage(this, first_page); + // If this fails we probably allocated r/o space too late. + CHECK_EQ(reinterpret_cast(first_page), chunk); + } else { + chunk = heap()->memory_allocator()->AllocateReadOnlyPage(this); + } + + capacity_ += AreaSize(); + + AccountCommitted(chunk->size()); + CHECK_NOT_NULL(chunk); + + CHECK_LE(chunk->area_start() + size, chunk->area_end()); + in->CopyRaw(reinterpret_cast(chunk->area_start()), size); + chunk->IncreaseAllocatedBytes(size); + chunk->high_water_mark_ = (chunk->area_start() - chunk->address()) + size; + + DCHECK_NE(chunk->allocated_bytes(), 0); + accounting_stats_.IncreaseCapacity(chunk->area_size()); + accounting_stats_.IncreaseAllocatedBytes(chunk->allocated_bytes(), chunk); + pages_.push_back(chunk); + + top_ = chunk->area_start() + size; + limit_ = chunk->area_end(); + } +} + } // namespace internal } // namespace v8 diff --git a/src/heap/read-only-spaces.h b/src/heap/read-only-spaces.h index 36754c66c7..f6c60398ec 100644 --- a/src/heap/read-only-spaces.h +++ b/src/heap/read-only-spaces.h @@ -23,7 +23,7 @@ namespace internal { class MemoryAllocator; class ReadOnlyHeap; -class SnapshotData; +class SnapshotByteSource; class ReadOnlyPage : public BasicMemoryChunk { public: @@ -235,6 +235,8 @@ class ReadOnlySpace : public BaseSpace { Address FirstPageAddress() const { return pages_.front()->address(); } + void InitFromMemoryDump(Isolate* isolate, SnapshotByteSource* source); + protected: friend class SingleCopyReadOnlyArtifacts; diff --git a/src/heap/setup-heap-internal.cc b/src/heap/setup-heap-internal.cc index ffea67d96b..bd1dd03015 100644 --- a/src/heap/setup-heap-internal.cc +++ b/src/heap/setup-heap-internal.cc @@ -728,6 +728,20 @@ void Heap::CreateInitialReadOnlyObjects() { Factory* factory = isolate()->factory(); ReadOnlyRoots roots(this); + // For static roots we need the r/o space to have identical layout on all + // compile targets. Varying objects are padded to their biggest size. + auto StaticRootsEnsureAllocatedSize = [&](HeapObject obj, int required) { +#ifdef V8_STATIC_ROOTS_BOOL + if (required == obj.Size()) return; + CHECK_LT(obj.Size(), required); + int filler_size = required - obj.Size(); + auto filler = factory->NewFillerObject(filler_size, + AllocationAlignment::kTaggedAligned, + AllocationType::kReadOnly); + CHECK_EQ(filler->address() + filler->Size(), obj.address() + required); +#endif + }; + // The -0 value must be set before NewNumber works. set_minus_zero_value( *factory->NewHeapNumber(-0.0)); @@ -939,6 +953,8 @@ void Heap::CreateInitialReadOnlyObjects() { Handle empty_swiss_property_dictionary = factory->CreateCanonicalEmptySwissNameDictionary(); set_empty_swiss_property_dictionary(*empty_swiss_property_dictionary); + StaticRootsEnsureAllocatedSize(*empty_swiss_property_dictionary, + 8 * kTaggedSize); // Allocate the empty FeedbackMetadata. Handle empty_feedback_metadata = @@ -959,8 +975,9 @@ void Heap::CreateInitialReadOnlyObjects() { set_native_scope_info(*native_scope_info); // Canonical off-heap trampoline data - set_off_heap_trampoline_relocation_info( - *Builtins::GenerateOffHeapTrampolineRelocInfo(isolate_)); + auto reloc_info = Builtins::GenerateOffHeapTrampolineRelocInfo(isolate_); + set_off_heap_trampoline_relocation_info(*reloc_info); + StaticRootsEnsureAllocatedSize(*reloc_info, 4 * kTaggedSize); if (V8_EXTERNAL_CODE_SPACE_BOOL) { // These roots will not be used. diff --git a/src/roots/roots.cc b/src/roots/roots.cc index 9330759948..f280498a0d 100644 --- a/src/roots/roots.cc +++ b/src/roots/roots.cc @@ -4,9 +4,11 @@ #include "src/roots/roots.h" +#include "src/common/globals.h" #include "src/objects/elements-kind.h" #include "src/objects/objects-inl.h" #include "src/objects/visitors.h" +#include "src/roots/static-roots.h" namespace v8 { namespace internal { @@ -68,5 +70,19 @@ Handle ReadOnlyRoots::FindHeapNumber(double value) { return Handle(); } +void ReadOnlyRoots::InitFromStaticRootsTable(Address cage_base) { + CHECK(V8_STATIC_ROOTS_BOOL); +#if V8_STATIC_ROOTS_BOOL + RootIndex pos = RootIndex::kFirstReadOnlyRoot; + for (auto element : StaticReadOnlyRootsPointerTable) { + auto ptr = + V8HeapCompressionScheme::DecompressTaggedPointer(cage_base, element); + *GetLocation(pos) = ptr; + ++pos; + } + DCHECK_EQ(static_cast(pos) - 1, RootIndex::kLastReadOnlyRoot); +#endif // V8_STATIC_ROOTS_BOOL +} + } // namespace internal } // namespace v8 diff --git a/src/roots/roots.h b/src/roots/roots.h index 88ab36e68d..7edbc9f673 100644 --- a/src/roots/roots.h +++ b/src/roots/roots.h @@ -643,6 +643,10 @@ class ReadOnlyRoots { // heap verification. void Iterate(RootVisitor* visitor); + // Uncompress pointers in the static roots table and store them into the + // actual roots table. + void InitFromStaticRootsTable(Address cage_base); + private: V8_INLINE Address first_name_for_protector() const; V8_INLINE Address last_name_for_protector() const; diff --git a/src/roots/static-roots.h b/src/roots/static-roots.h new file mode 100644 index 0000000000..eb4aebd879 --- /dev/null +++ b/src/roots/static-roots.h @@ -0,0 +1,20 @@ +// Copyright 2022 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_ROOTS_STATIC_ROOTS_H_ +#define V8_ROOTS_STATIC_ROOTS_H_ + +#include "src/common/globals.h" +#if V8_STATIC_ROOTS_BOOL + +namespace v8 { +namespace internal { + +// TODO(olivf, v8:13466): Enable and add static roots +constexpr static std::array StaticReadOnlyRootsPointerTable = {}; + +} // namespace internal +} // namespace v8 +#endif // V8_STATIC_ROOTS_BOOL +#endif // V8_ROOTS_STATIC_ROOTS_H_ diff --git a/src/snapshot/code-serializer.cc b/src/snapshot/code-serializer.cc index c866bc38e9..f2fb83d7da 100644 --- a/src/snapshot/code-serializer.cc +++ b/src/snapshot/code-serializer.cc @@ -108,28 +108,6 @@ AlignedCachedData* CodeSerializer::SerializeSharedFunctionInfo( return data.GetScriptData(); } -bool CodeSerializer::SerializeReadOnlyObject( - HeapObject obj, const DisallowGarbageCollection& no_gc) { - if (!ReadOnlyHeap::Contains(obj)) return false; - - // For objects on the read-only heap, never serialize the object, but instead - // create a back reference that encodes the page number as the chunk_index and - // the offset within the page as the chunk_offset. - Address address = obj.address(); - BasicMemoryChunk* chunk = BasicMemoryChunk::FromAddress(address); - uint32_t chunk_index = 0; - ReadOnlySpace* const read_only_space = isolate()->heap()->read_only_space(); - for (ReadOnlyPage* page : read_only_space->pages()) { - if (chunk == page) break; - ++chunk_index; - } - uint32_t chunk_offset = static_cast(chunk->Offset(address)); - sink_.Put(kReadOnlyHeapRef, "ReadOnlyHeapRef"); - sink_.PutInt(chunk_index, "ReadOnlyHeapRefChunkIndex"); - sink_.PutInt(chunk_offset, "ReadOnlyHeapRefChunkOffset"); - return true; -} - void CodeSerializer::SerializeObjectImpl(Handle obj) { ReadOnlyRoots roots(isolate()); InstanceType instance_type; @@ -139,7 +117,7 @@ void CodeSerializer::SerializeObjectImpl(Handle obj) { if (SerializeHotObject(raw)) return; if (SerializeRoot(raw)) return; if (SerializeBackReference(raw)) return; - if (SerializeReadOnlyObject(raw, no_gc)) return; + if (SerializeReadOnlyObjectReference(raw, &sink_)) return; instance_type = raw.map().instance_type(); CHECK(!InstanceTypeChecker::IsCode(instance_type)); diff --git a/src/snapshot/code-serializer.h b/src/snapshot/code-serializer.h index 5ce6ab43ca..a2fe3f182f 100644 --- a/src/snapshot/code-serializer.h +++ b/src/snapshot/code-serializer.h @@ -111,9 +111,6 @@ class CodeSerializer : public Serializer { private: void SerializeObjectImpl(Handle o) override; - bool SerializeReadOnlyObject(HeapObject obj, - const DisallowGarbageCollection& no_gc); - DISALLOW_GARBAGE_COLLECTION(no_gc_) uint32_t source_hash_; }; diff --git a/src/snapshot/deserializer.cc b/src/snapshot/deserializer.cc index c8861f971e..2312ae38b2 100644 --- a/src/snapshot/deserializer.cc +++ b/src/snapshot/deserializer.cc @@ -911,6 +911,8 @@ int Deserializer::ReadSingleBytecodeData(byte data, // object. case CASE_RANGE_ALL_SPACES(kNewObject): { SnapshotSpace space = NewObject::Decode(data); + DCHECK_IMPLIES(V8_STATIC_ROOTS_BOOL, + space != SnapshotSpace::kReadOnlyHeap); // Save the reference type before recursing down into reading the object. HeapObjectReferenceType ref_type = GetAndResetNextReferenceType(); Handle heap_object = ReadObject(space); @@ -925,9 +927,12 @@ int Deserializer::ReadSingleBytecodeData(byte data, } // Reference an object in the read-only heap. This should be used when an - // object is read-only, but is not a root. + // object is read-only, but is not a root. Except with static roots we + // always use this reference to refer to read only objects since they are + // created by loading a memory dump of r/o space. case kReadOnlyHeapRef: { - DCHECK(isolate()->heap()->deserialization_complete()); + DCHECK(isolate()->heap()->deserialization_complete() || + V8_STATIC_ROOTS_BOOL); uint32_t chunk_index = source_.GetInt(); uint32_t chunk_offset = source_.GetInt(); @@ -964,6 +969,7 @@ int Deserializer::ReadSingleBytecodeData(byte data, // Find an object in the read-only object cache and write a pointer to it // to the current object. case kReadOnlyObjectCache: { + DCHECK(!V8_STATIC_ROOTS_BOOL); int cache_index = source_.GetInt(); // TODO(leszeks): Could we use the address of the cached_read_only_object // entry as a Handle backing? diff --git a/src/snapshot/mksnapshot.cc b/src/snapshot/mksnapshot.cc index dd67969694..ef606e87e1 100644 --- a/src/snapshot/mksnapshot.cc +++ b/src/snapshot/mksnapshot.cc @@ -18,6 +18,7 @@ #include "src/flags/flags.h" #include "src/snapshot/embedded/embedded-file-writer.h" #include "src/snapshot/snapshot.h" +#include "src/snapshot/static-roots-gen.h" namespace { @@ -230,7 +231,7 @@ int main(int argc, char** argv) { std::string usage = "Usage: " + std::string(argv[0]) + " [--startup-src=file]" + " [--startup-blob=file]" + " [--embedded-src=file]" + " [--embedded-variant=label]" + - " [--target-arch=arch]" + + " [--static-roots=file]" + " [--target-arch=arch]" + " [--target-os=os] [extras]\n\n"; int result = i::FlagList::SetFlagsFromCommandLine( &argc, argv, true, HelpOptions(HelpOptions::kExit, usage.c_str())); @@ -289,6 +290,10 @@ int main(int argc, char** argv) { // is still alive (we called DisableEmbeddedBlobRefcounting above). // That's fine as far as the embedded file writer is concerned. WriteEmbeddedFile(&embedded_writer); + + if (i::v8_flags.static_roots) { + i::StaticRootsTableGen::write(i_isolate, i::v8_flags.static_roots); + } } if (warmup_script) { diff --git a/src/snapshot/read-only-deserializer.cc b/src/snapshot/read-only-deserializer.cc index 067dab0320..e417253936 100644 --- a/src/snapshot/read-only-deserializer.cc +++ b/src/snapshot/read-only-deserializer.cc @@ -5,10 +5,12 @@ #include "src/snapshot/read-only-deserializer.h" #include "src/api/api.h" +#include "src/common/globals.h" #include "src/execution/v8threads.h" #include "src/heap/heap-inl.h" // crbug.com/v8/8499 #include "src/heap/read-only-heap.h" #include "src/objects/slots.h" +#include "src/roots/static-roots.h" namespace v8 { namespace internal { @@ -31,20 +33,25 @@ void ReadOnlyDeserializer::DeserializeIntoIsolate() { { ReadOnlyRoots roots(isolate()); + if (V8_STATIC_ROOTS_BOOL) { + ro_heap->read_only_space()->InitFromMemoryDump(isolate(), source()); + roots.InitFromStaticRootsTable(isolate()->cage_base()); + ro_heap->read_only_space()->RepairFreeSpacesAfterDeserialization(); + } else { + roots.Iterate(this); - roots.Iterate(this); - ro_heap->read_only_space()->RepairFreeSpacesAfterDeserialization(); - - // Deserialize the Read-only Object Cache. - for (;;) { - Object* object = ro_heap->ExtendReadOnlyObjectCache(); - // During deserialization, the visitor populates the read-only object - // cache and eventually terminates the cache with undefined. - VisitRootPointer(Root::kReadOnlyObjectCache, nullptr, - FullObjectSlot(object)); - if (object->IsUndefined(roots)) break; + // Deserialize the Read-only Object Cache. + for (;;) { + Object* object = ro_heap->ExtendReadOnlyObjectCache(); + // During deserialization, the visitor populates the read-only object + // cache and eventually terminates the cache with undefined. + VisitRootPointer(Root::kReadOnlyObjectCache, nullptr, + FullObjectSlot(object)); + if (object->IsUndefined(roots)) break; + } + DeserializeDeferredObjects(); } - DeserializeDeferredObjects(); + #ifdef DEBUG roots.VerifyNameForProtectors(); #endif @@ -53,6 +60,29 @@ void ReadOnlyDeserializer::DeserializeIntoIsolate() { if (should_rehash()) { isolate()->heap()->InitializeHashSeed(); + RehashReadOnly(); + } +} + +void ReadOnlyDeserializer::RehashReadOnly() { + DCHECK(should_rehash()); + if (V8_STATIC_ROOTS_BOOL) { + // Since we are not deserializing individual objects we need to scan the + // heap and search for the ones that need rehashing. + ReadOnlyHeapObjectIterator iterator(isolate()->read_only_heap()); + PtrComprCageBase cage_base(isolate()); + for (HeapObject object = iterator.Next(); !object.is_null(); + object = iterator.Next()) { + auto instance_type = object.map(cage_base).instance_type(); + if (InstanceTypeChecker::IsInternalizedString(instance_type)) { + auto str = String::cast(object); + str.set_raw_hash_field(Name::kEmptyHashField); + str.EnsureHash(); + } else if (object.NeedsRehashing(instance_type)) { + object.RehashBasedOnMap(isolate()); + } + } + } else { Rehash(); } } diff --git a/src/snapshot/read-only-deserializer.h b/src/snapshot/read-only-deserializer.h index 05b4379169..a38a0e4627 100644 --- a/src/snapshot/read-only-deserializer.h +++ b/src/snapshot/read-only-deserializer.h @@ -22,6 +22,8 @@ class ReadOnlyDeserializer final : public Deserializer { // Deserialize the snapshot into an empty heap. void DeserializeIntoIsolate(); + + void RehashReadOnly(); }; } // namespace internal diff --git a/src/snapshot/read-only-serializer.cc b/src/snapshot/read-only-serializer.cc index b05a0c0870..122c66d691 100644 --- a/src/snapshot/read-only-serializer.cc +++ b/src/snapshot/read-only-serializer.cc @@ -33,6 +33,7 @@ ReadOnlySerializer::~ReadOnlySerializer() { void ReadOnlySerializer::SerializeObjectImpl(Handle obj) { CHECK(ReadOnlyHeap::Contains(*obj)); CHECK_IMPLIES(obj->IsString(), obj->IsInternalizedString()); + DCHECK(!V8_STATIC_ROOTS_BOOL); // There should be no references to the not_mapped_symbol except for the entry // in the root table, so don't try to serialize a reference and rely on the @@ -73,36 +74,56 @@ void ReadOnlySerializer::SerializeReadOnlyRoots() { CHECK_IMPLIES(!allow_active_isolate_for_testing(), isolate()->handle_scope_implementer()->blocks()->empty()); - ReadOnlyRoots(isolate()).Iterate(this); - - if (reconstruct_read_only_and_shared_object_caches_for_testing()) { - ReconstructReadOnlyObjectCacheForTesting(); + if (!V8_STATIC_ROOTS_BOOL) { + ReadOnlyRoots(isolate()).Iterate(this); + if (reconstruct_read_only_and_shared_object_caches_for_testing()) { + ReconstructReadOnlyObjectCacheForTesting(); + } } } void ReadOnlySerializer::FinalizeSerialization() { - // This comes right after serialization of the other snapshots, where we - // add entries to the read-only object cache. Add one entry with 'undefined' - // to terminate the read-only object cache. - Object undefined = ReadOnlyRoots(isolate()).undefined_value(); - VisitRootPointer(Root::kReadOnlyObjectCache, nullptr, - FullObjectSlot(&undefined)); - SerializeDeferredObjects(); - Pad(); + if (V8_STATIC_ROOTS_BOOL) { + DCHECK(object_cache_empty()); + DCHECK(deferred_objects_empty()); + DCHECK_EQ(sink_.Position(), 0); + + auto space = isolate()->read_only_heap()->read_only_space(); + size_t num_pages = space->pages().size(); + sink_.PutInt(num_pages, "num pages"); + Tagged_t pos = V8HeapCompressionScheme::CompressTagged( + reinterpret_cast
(space->pages()[0])); + sink_.PutInt(pos, "first page offset"); + for (auto p : space->pages()) { + size_t page_size = p->area_size(); + sink_.PutInt(page_size, "page size"); + sink_.PutRaw(reinterpret_cast(p->area_start()), + static_cast(p->area_size()), "page"); + } + } else { + // This comes right after serialization of the other snapshots, where we + // add entries to the read-only object cache. Add one entry with 'undefined' + // to terminate the read-only object cache. + Object undefined = ReadOnlyRoots(isolate()).undefined_value(); + VisitRootPointer(Root::kReadOnlyObjectCache, nullptr, + FullObjectSlot(&undefined)); + SerializeDeferredObjects(); + Pad(); #ifdef DEBUG - // Check that every object on read-only heap is reachable (and was - // serialized). - ReadOnlyHeapObjectIterator iterator(isolate()->read_only_heap()); - for (HeapObject object = iterator.Next(); !object.is_null(); - object = iterator.Next()) { - if (IsNotMappedSymbol(object)) { - CHECK(did_serialize_not_mapped_symbol_); - } else { - CHECK_NOT_NULL(serialized_objects_.Find(object)); + // Check that every object on read-only heap is reachable (and was + // serialized). + ReadOnlyHeapObjectIterator iterator(isolate()->read_only_heap()); + for (HeapObject object = iterator.Next(); !object.is_null(); + object = iterator.Next()) { + if (IsNotMappedSymbol(object)) { + CHECK(did_serialize_not_mapped_symbol_); + } else { + CHECK_NOT_NULL(serialized_objects_.Find(object)); + } } +#endif // DEBUG } -#endif } bool ReadOnlySerializer::MustBeDeferred(HeapObject object) { @@ -122,13 +143,16 @@ bool ReadOnlySerializer::SerializeUsingReadOnlyObjectCache( SnapshotByteSink* sink, Handle obj) { if (!ReadOnlyHeap::Contains(*obj)) return false; - // Get the cache index and serialize it into the read-only snapshot if - // necessary. - int cache_index = SerializeInObjectCache(obj); - - // Writing out the cache entry into the calling serializer's sink. - sink->Put(kReadOnlyObjectCache, "ReadOnlyObjectCache"); - sink->PutInt(cache_index, "read_only_object_cache_index"); + if (V8_STATIC_ROOTS_BOOL) { + SerializeReadOnlyObjectReference(*obj, sink); + } else { + // Get the cache index and serialize it into the read-only snapshot if + // necessary. + int cache_index = SerializeInObjectCache(obj); + // Writing out the cache entry into the calling serializer's sink. + sink->Put(kReadOnlyObjectCache, "ReadOnlyObjectCache"); + sink->PutInt(cache_index, "read_only_object_cache_index"); + } return true; } diff --git a/src/snapshot/roots-serializer.h b/src/snapshot/roots-serializer.h index 7a699a7645..739d8df903 100644 --- a/src/snapshot/roots-serializer.h +++ b/src/snapshot/roots-serializer.h @@ -46,6 +46,8 @@ class RootsSerializer : public Serializer { // Serializes |object| if not previously seen and returns its cache index. int SerializeInObjectCache(Handle object); + bool object_cache_empty() { return object_cache_index_map_.size() == 0; } + private: void VisitRootPointers(Root root, const char* description, FullObjectSlot start, FullObjectSlot end) override; diff --git a/src/snapshot/serializer.cc b/src/snapshot/serializer.cc index 021dc5ea0b..3e459b5217 100644 --- a/src/snapshot/serializer.cc +++ b/src/snapshot/serializer.cc @@ -1370,5 +1370,28 @@ Handle ObjectCacheIndexMap::Values(Isolate* isolate) { return externals; } +bool Serializer::SerializeReadOnlyObjectReference(HeapObject obj, + SnapshotByteSink* sink) { + if (!ReadOnlyHeap::Contains(obj)) return false; + + // For objects on the read-only heap, never serialize the object, but instead + // create a back reference that encodes the page number as the chunk_index and + // the offset within the page as the chunk_offset. + Address address = obj.address(); + BasicMemoryChunk* chunk = BasicMemoryChunk::FromAddress(address); + uint32_t chunk_index = 0; + ReadOnlySpace* const read_only_space = isolate()->heap()->read_only_space(); + DCHECK(!read_only_space->writable()); + for (ReadOnlyPage* page : read_only_space->pages()) { + if (chunk == page) break; + ++chunk_index; + } + uint32_t chunk_offset = static_cast(chunk->Offset(address)); + sink->Put(kReadOnlyHeapRef, "ReadOnlyHeapRef"); + sink->PutInt(chunk_index, "ReadOnlyHeapRefChunkIndex"); + sink->PutInt(chunk_offset, "ReadOnlyHeapRefChunkOffset"); + return true; +} + } // namespace internal } // namespace v8 diff --git a/src/snapshot/serializer.h b/src/snapshot/serializer.h index edd3dfea31..8c599c2216 100644 --- a/src/snapshot/serializer.h +++ b/src/snapshot/serializer.h @@ -266,6 +266,8 @@ class Serializer : public SerializerDeserializer { return external_reference_encoder_.TryEncode(addr); } + bool SerializeReadOnlyObjectReference(HeapObject obj, SnapshotByteSink* sink); + // GetInt reads 4 bytes at once, requiring padding at the end. // Use padding_offset to specify the space you want to use after padding. void Pad(int padding_offset = 0); @@ -317,6 +319,8 @@ class Serializer : public SerializerDeserializer { Snapshot::kReconstructReadOnlyAndSharedObjectCachesForTesting) != 0; } + bool deferred_objects_empty() { return deferred_objects_.size() == 0; } + private: // A circular queue of hot objects. This is added to in the same order as in // Deserializer::HotObjectsList, but this stores the objects as an array of diff --git a/src/snapshot/static-roots-gen.cc b/src/snapshot/static-roots-gen.cc new file mode 100644 index 0000000000..f84c3cf2f4 --- /dev/null +++ b/src/snapshot/static-roots-gen.cc @@ -0,0 +1,58 @@ +// Copyright 2018 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/snapshot/static-roots-gen.h" + +#include + +#include "src/common/ptr-compr-inl.h" +#include "src/execution/isolate.h" +#include "src/roots/roots-inl.h" +#include "src/roots/roots.h" + +namespace v8 { +namespace internal { + +void StaticRootsTableGen::write(Isolate* isolate, const char* file) { + CHECK(file); + static_assert(static_cast(RootIndex::kFirstReadOnlyRoot) == 0); + + std::ofstream out(file); + const auto roots = isolate->roots_table(); + const auto size = static_cast(RootIndex::kReadOnlyRootsCount); + + out << "// Copyright 2022 the V8 project authors. All rights reserved.\n" + << "// Use of this source code is governed by a BSD-style license " + "that can be\n" + << "// found in the LICENSE file.\n" + << "\n" + << "#ifndef V8_ROOTS_STATIC_ROOTS_H_\n" + << "#define V8_ROOTS_STATIC_ROOTS_H_\n" + << "\n" + << "#include \"src/common/globals.h\"\n" + << "#if V8_STATIC_ROOTS_BOOL\n" + << "\n" + << "namespace v8 {\n" + << "namespace internal {\n" + << "\n" + << "constexpr static std::array StaticReadOnlyRootsPointerTable = {\n"; + auto pos = RootIndex::kFirstReadOnlyRoot; + for (; pos <= RootIndex::kLastReadOnlyRoot; ++pos) { + auto el = roots[pos]; + auto n = roots.name(pos); + el = V8HeapCompressionScheme::CompressTagged(el); + out << " " << reinterpret_cast(el) << ", // " << n << "\n"; + } + CHECK_EQ(static_cast(pos), size); + out << "};\n" + << "\n" + << "} // namespace internal\n" + << "} // namespace v8\n" + << "#endif // V8_STATIC_ROOTS_BOOL\n" + << "#endif // V8_ROOTS_STATIC_ROOTS_H_\n"; +} + +} // namespace internal +} // namespace v8 diff --git a/src/snapshot/static-roots-gen.h b/src/snapshot/static-roots-gen.h new file mode 100644 index 0000000000..2422df75d0 --- /dev/null +++ b/src/snapshot/static-roots-gen.h @@ -0,0 +1,21 @@ +// Copyright 2022 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_SNAPSHOT_STATIC_ROOTS_GEN_H_ +#define V8_SNAPSHOT_STATIC_ROOTS_GEN_H_ + +namespace v8 { +namespace internal { + +class Isolate; + +class StaticRootsTableGen { + public: + static void write(Isolate* isolate, const char* file); +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_SNAPSHOT_STATIC_ROOTS_GEN_H_ diff --git a/src/utils/allocation.cc b/src/utils/allocation.cc index ba81aee001..553c2c53a6 100644 --- a/src/utils/allocation.cc +++ b/src/utils/allocation.cc @@ -166,7 +166,7 @@ void* AllocatePages(v8::PageAllocator* page_allocator, void* hint, size_t size, DCHECK_NOT_NULL(page_allocator); DCHECK_EQ(hint, AlignedAddress(hint, alignment)); DCHECK(IsAligned(size, page_allocator->AllocatePageSize())); - if (v8_flags.randomize_all_allocations) { + if (!hint && v8_flags.randomize_all_allocations) { hint = AlignedAddress(page_allocator->GetRandomMmapAddr(), alignment); } void* result = nullptr;