Commit 388a80ba authored by Shu-yu Guo's avatar Shu-yu Guo Committed by V8 LUCI CQ

[snapshots] Add a new snapshot for shared heap objects

This CL adds a new snapshot to hold objects that are in the shared heap
or may need to be in the shared heap depending on runtime flags.
Currently this is to support --shared-string-table, which puts all
in-place-internalizable strings, internalized strings, and the
string table into the shared heap.

The shared heap snapshot is never deserialized into client Isolates.
This means when V8 is started without a shared Isolate, the shared heap
snapshot is deserialized into all Isolates.

Bug: v8:12007
Change-Id: I7eeab73080cda2e8250a5a49747f25b2440a349d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3173905
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77309}
parent e1981ff5
......@@ -1899,6 +1899,10 @@ filegroup(
"src/snapshot/snapshot-utils.h",
"src/snapshot/snapshot.cc",
"src/snapshot/snapshot.h",
"src/snapshot/shared-heap-deserializer.cc",
"src/snapshot/shared-heap-deserializer.h",
"src/snapshot/shared-heap-serializer.cc",
"src/snapshot/shared-heap-serializer.h",
"src/snapshot/startup-deserializer.cc",
"src/snapshot/startup-deserializer.h",
"src/snapshot/startup-serializer.cc",
......
......@@ -3328,6 +3328,8 @@ v8_header_set("v8_internal_headers") {
"src/snapshot/roots-serializer.h",
"src/snapshot/serializer-deserializer.h",
"src/snapshot/serializer.h",
"src/snapshot/shared-heap-deserializer.h",
"src/snapshot/shared-heap-serializer.h",
"src/snapshot/snapshot-compression.h",
"src/snapshot/snapshot-data.h",
"src/snapshot/snapshot-source-sink.h",
......@@ -4335,6 +4337,8 @@ v8_source_set("v8_base_without_compiler") {
"src/snapshot/roots-serializer.cc",
"src/snapshot/serializer-deserializer.cc",
"src/snapshot/serializer.cc",
"src/snapshot/shared-heap-deserializer.cc",
"src/snapshot/shared-heap-serializer.cc",
"src/snapshot/snapshot-compression.cc",
"src/snapshot/snapshot-data.cc",
"src/snapshot/snapshot-source-sink.cc",
......
......@@ -90,6 +90,7 @@
#include "src/snapshot/embedded/embedded-data.h"
#include "src/snapshot/embedded/embedded-file-writer-interface.h"
#include "src/snapshot/read-only-deserializer.h"
#include "src/snapshot/shared-heap-deserializer.h"
#include "src/snapshot/startup-deserializer.h"
#include "src/strings/string-builder-inl.h"
#include "src/strings/string-stream.h"
......@@ -3529,14 +3530,19 @@ void Isolate::TearDownEmbeddedBlob() {
}
}
bool Isolate::InitWithoutSnapshot() { return Init(nullptr, nullptr, false); }
bool Isolate::InitWithoutSnapshot() {
return Init(nullptr, nullptr, nullptr, false);
}
bool Isolate::InitWithSnapshot(SnapshotData* startup_snapshot_data,
SnapshotData* read_only_snapshot_data,
SnapshotData* shared_heap_snapshot_data,
bool can_rehash) {
DCHECK_NOT_NULL(startup_snapshot_data);
DCHECK_NOT_NULL(read_only_snapshot_data);
return Init(startup_snapshot_data, read_only_snapshot_data, can_rehash);
DCHECK_NOT_NULL(shared_heap_snapshot_data);
return Init(startup_snapshot_data, read_only_snapshot_data,
shared_heap_snapshot_data, can_rehash);
}
static std::string AddressToString(uintptr_t address) {
......@@ -3601,11 +3607,13 @@ class BigIntPlatform : public bigint::Platform {
} // namespace
bool Isolate::Init(SnapshotData* startup_snapshot_data,
SnapshotData* read_only_snapshot_data, bool can_rehash) {
SnapshotData* read_only_snapshot_data,
SnapshotData* shared_heap_snapshot_data, bool can_rehash) {
TRACE_ISOLATE(init);
const bool create_heap_objects = (read_only_snapshot_data == nullptr);
// We either have both or neither.
// We either have all or none.
DCHECK_EQ(create_heap_objects, startup_snapshot_data == nullptr);
DCHECK_EQ(create_heap_objects, shared_heap_snapshot_data == nullptr);
base::ElapsedTimer timer;
if (create_heap_objects && FLAG_profile_deserialization) timer.Start();
......@@ -3724,8 +3732,9 @@ bool Isolate::Init(SnapshotData* startup_snapshot_data,
}
if (create_heap_objects) {
// Terminate the startup object cache so we can iterate.
// Terminate the startup and shared heap object caches so we can iterate.
startup_object_cache_.push_back(ReadOnlyRoots(this).undefined_value());
shared_heap_object_cache_.push_back(ReadOnlyRoots(this).undefined_value());
}
InitializeThreadLocal();
......@@ -3790,6 +3799,10 @@ bool Isolate::Init(SnapshotData* startup_snapshot_data,
heap_.read_only_space()->ClearStringPaddingIfNeeded();
read_only_heap_->OnCreateHeapObjectsComplete(this);
} else {
SharedHeapDeserializer shared_heap_deserializer(
this, shared_heap_snapshot_data, can_rehash);
shared_heap_deserializer.DeserializeIntoIsolate();
StartupDeserializer startup_deserializer(this, startup_snapshot_data,
can_rehash);
startup_deserializer.DeserializeIntoIsolate();
......
......@@ -614,7 +614,9 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
bool InitWithoutSnapshot();
bool InitWithSnapshot(SnapshotData* startup_snapshot_data,
SnapshotData* read_only_snapshot_data, bool can_rehash);
SnapshotData* read_only_snapshot_data,
SnapshotData* shared_heap_snapshot_data,
bool can_rehash);
// True if at least one thread Enter'ed this isolate.
bool IsInUse() { return entry_stack_ != nullptr; }
......@@ -1570,6 +1572,14 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
std::vector<Object>* startup_object_cache() { return &startup_object_cache_; }
// When there is a shared space (i.e. when this is a client Isolate), the
// shared heap object cache holds objects in shared among Isolates. Otherwise
// this object cache is per-Isolate like the startup object cache.
std::vector<Object>* shared_heap_object_cache() {
if (shared_isolate()) return shared_isolate()->shared_heap_object_cache();
return &shared_heap_object_cache_;
}
bool IsGeneratingEmbeddedBuiltins() const {
return builtins_constants_table_builder() != nullptr;
}
......@@ -1837,7 +1847,8 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
~Isolate();
bool Init(SnapshotData* startup_snapshot_data,
SnapshotData* read_only_snapshot_data, bool can_rehash);
SnapshotData* read_only_snapshot_data,
SnapshotData* shared_heap_snapshot_data, bool can_rehash);
void CheckIsolateLayout();
......@@ -2175,6 +2186,12 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
std::vector<Object> startup_object_cache_;
// When sharing data among Isolates (e.g. FLAG_shared_string_table), only the
// shared Isolate populates this and client Isolates reference that copy.
//
// Otherwise this is populated for all Isolates.
std::vector<Object> shared_heap_object_cache_;
// Used during builtins compilation to build the builtins constants table,
// which is stored on the root list prior to serialization.
BuiltinsConstantsTableBuilder* builtins_constants_table_builder_ = nullptr;
......
......@@ -938,7 +938,7 @@ MaybeHandle<Map> FactoryBase<Impl>::GetInPlaceInternalizedStringMap(
return read_only_roots()
.external_one_byte_internalized_string_map_handle();
default:
return MaybeHandle<Map>(); // No match found.
return MaybeHandle<Map>();
}
}
......
......@@ -4896,10 +4896,21 @@ void Heap::IterateRoots(RootVisitor* v, base::EnumSet<SkipRoot> options) {
}
v->Synchronize(VisitorSynchronization::kStrongRoots);
// Iterate over the startup object cache unless serializing or
// deserializing.
SerializerDeserializer::Iterate(isolate_, v);
// Iterate over the startup and shared heap object caches unless
// serializing or deserializing.
SerializerDeserializer::IterateStartupObjectCache(isolate_, v);
v->Synchronize(VisitorSynchronization::kStartupObjectCache);
// When shared_isolate() is null, isolate_ is either an unshared (instead of
// a client) Isolate or the shared Isolate. In both cases isolate_ owns its
// shared heap object cache and should iterate it.
//
// When shared_isolate() is not null, isolate_ is a client Isolate, does not
// own its shared heap object cache, and should not iterate it.
if (isolate_->shared_isolate() == nullptr) {
SerializerDeserializer::IterateSharedHeapObjectCache(isolate_, v);
v->Synchronize(VisitorSynchronization::kSharedHeapObjectCache);
}
}
if (!options.contains(SkipRoot::kWeak)) {
......
......@@ -1262,6 +1262,13 @@ SubStringRange::iterator SubStringRange::end() {
return SubStringRange::iterator(string_, first_ + length_, no_gc_);
}
// static
bool String::IsInPlaceInternalizable(Isolate* isolate, String string) {
return !isolate->factory()
->GetInPlaceInternalizedStringMap(string.map())
.is_null();
}
} // namespace internal
} // namespace v8
......
......@@ -552,6 +552,11 @@ class String : public TorqueGeneratedString<String, Name> {
Handle<String> string,
bool include_ending_line);
// Returns true if string can be internalized without copying. In such cases
// the string is inserted into the string table and its map is changed to an
// internalized equivalent.
static inline bool IsInPlaceInternalizable(Isolate* isolate, String string);
private:
friend class Name;
friend class StringTableInsertionKey;
......
......@@ -37,6 +37,7 @@ class CodeDataContainer;
V(kCodeFlusher, "(Code flusher)") \
V(kStartupObjectCache, "(Startup object cache)") \
V(kReadOnlyObjectCache, "(Read-only object cache)") \
V(kSharedHeapObjectCache, "(Shareable object cache)") \
V(kWeakCollections, "(Weak collections)") \
V(kWrapperTracing, "(Wrapper tracing)") \
V(kWriteBarrier, "(Write barrier)") \
......
......@@ -142,6 +142,10 @@ void ContextSerializer::SerializeObjectImpl(Handle<HeapObject> obj) {
return;
}
if (startup_serializer_->SerializeUsingSharedHeapObjectCache(&sink_, obj)) {
return;
}
if (ShouldBeInTheStartupObjectCache(*obj)) {
startup_serializer_->SerializeUsingStartupObjectCache(&sink_, obj);
return;
......@@ -152,7 +156,7 @@ void ContextSerializer::SerializeObjectImpl(Handle<HeapObject> obj) {
// If this is not the case you may have to add something to the root array.
DCHECK(!startup_serializer_->ReferenceMapContains(obj));
// All the internalized strings that the context snapshot needs should be
// either in the root table or in the startup object cache.
// either in the root table or in the shared heap object cache.
DCHECK(!obj->IsInternalizedString());
// Function and object templates are not context specific.
DCHECK(!obj->IsTemplateInfo());
......@@ -204,6 +208,12 @@ bool ContextSerializer::ShouldBeInTheStartupObjectCache(HeapObject o) {
o.map() == ReadOnlyRoots(isolate()).fixed_cow_array_map();
}
bool ContextSerializer::ShouldBeInTheSharedObjectCache(HeapObject o) {
// FLAG_shared_string_table may be true during deserialization, so put
// internalized strings into the shared object snapshot.
return o.IsInternalizedString();
}
namespace {
bool DataIsEmpty(const StartupData& data) { return data.raw_size == 0; }
} // anonymous namespace
......
......@@ -32,6 +32,7 @@ class V8_EXPORT_PRIVATE ContextSerializer : public Serializer {
private:
void SerializeObjectImpl(Handle<HeapObject> o) override;
bool ShouldBeInTheStartupObjectCache(HeapObject o);
bool ShouldBeInTheSharedObjectCache(HeapObject o);
bool SerializeJSObjectWithEmbedderFields(Handle<HeapObject> obj);
void CheckRehashability(HeapObject obj);
......
......@@ -36,6 +36,7 @@
#include "src/snapshot/embedded/embedded-data.h"
#include "src/snapshot/references.h"
#include "src/snapshot/serializer-deserializer.h"
#include "src/snapshot/shared-heap-serializer.h"
#include "src/snapshot/snapshot-data.h"
#include "src/snapshot/snapshot.h"
#include "src/tracing/trace-event.h"
......@@ -600,16 +601,13 @@ Handle<HeapObject> Deserializer<IsolateT>::ReadObject(SnapshotSpace space) {
AllocationType allocation = SpaceToAllocation(space);
// When sharing a string table, all in-place internalizable strings except
// internalized strings are allocated in the shared heap. Internalized strings
// are allocated in the local heap as an optimization, because they need to be
// looked up in the shared string table to get the canonical copy anyway, and
// the shared allocation is needless synchronization on the concurrent
// allocator.
// When sharing a string table, all in-place internalizable and internalized
// strings internalized strings are allocated in the shared heap.
//
// TODO(12007): When shipping, add a new SharedOld SnapshotSpace.
if (FLAG_shared_string_table &&
!isolate()->factory()->GetInPlaceInternalizedStringMap(*map).is_null() &&
(!InstanceTypeChecker::IsInternalizedString(map->instance_type()) ||
deserializing_user_code())) {
(!isolate()->factory()->GetInPlaceInternalizedStringMap(*map).is_null() ||
InstanceTypeChecker::IsInternalizedString(map->instance_type()))) {
allocation = isolate()
->factory()
->RefineAllocationTypeForInPlaceInternalizableString(
......@@ -969,6 +967,19 @@ int Deserializer<IsolateT>::ReadSingleBytecodeData(byte data,
return slot_accessor.Write(heap_object, GetAndResetNextReferenceType());
}
// Find an object in the shared heap object cache and write a pointer to it
// to the current object.
case kSharedHeapObjectCache: {
int cache_index = source_.GetInt();
// TODO(leszeks): Could we use the address of the
// shared_heap_object_cache entry as a Handle backing?
HeapObject heap_object = HeapObject::cast(
main_thread_isolate()->shared_heap_object_cache()->at(cache_index));
DCHECK(SharedHeapSerializer::ShouldBeInSharedOldSpace(
main_thread_isolate(), heap_object));
return slot_accessor.Write(heap_object, GetAndResetNextReferenceType());
}
// Deserialize a new meta-map and write a pointer to it to the current
// object.
case kNewMetaMap: {
......
......@@ -10,24 +10,37 @@
namespace v8 {
namespace internal {
// The startup object cache is terminated by undefined. We visit the context
// snapshot...
// - during deserialization to populate it.
// - during normal GC to keep its content alive.
// - not during serialization. The context serializer adds to it explicitly.
namespace {
DISABLE_CFI_PERF
void SerializerDeserializer::Iterate(Isolate* isolate, RootVisitor* visitor) {
std::vector<Object>* cache = isolate->startup_object_cache();
void IterateObjectCache(Isolate* isolate, std::vector<Object>* cache,
Root root_id, RootVisitor* visitor) {
for (size_t i = 0;; ++i) {
// Extend the array ready to get a value when deserializing.
if (cache->size() <= i) cache->push_back(Smi::zero());
// During deserialization, the visitor populates the startup object cache
// and eventually terminates the cache with undefined.
visitor->VisitRootPointer(Root::kStartupObjectCache, nullptr,
FullObjectSlot(&cache->at(i)));
// During deserialization, the visitor populates the object cache and
// eventually terminates the cache with undefined.
visitor->VisitRootPointer(root_id, nullptr, FullObjectSlot(&cache->at(i)));
if (cache->at(i).IsUndefined(isolate)) break;
}
}
} // namespace
// The startup and shared heap object caches are terminated by undefined. We
// visit these caches...
// - during deserialization to populate it.
// - during normal GC to keep its content alive.
// - not during serialization. The context serializer adds to it explicitly.
void SerializerDeserializer::IterateStartupObjectCache(Isolate* isolate,
RootVisitor* visitor) {
IterateObjectCache(isolate, isolate->startup_object_cache(),
Root::kStartupObjectCache, visitor);
}
void SerializerDeserializer::IterateSharedHeapObjectCache(
Isolate* isolate, RootVisitor* visitor) {
IterateObjectCache(isolate, isolate->shared_heap_object_cache(),
Root::kSharedHeapObjectCache, visitor);
}
bool SerializerDeserializer::CanBeDeferred(HeapObject o) {
// 1. Maps cannot be deferred as objects are expected to have a valid map
......
......@@ -20,7 +20,10 @@ class Isolate;
// both.
class SerializerDeserializer : public RootVisitor {
public:
static void Iterate(Isolate* isolate, RootVisitor* visitor);
static void IterateStartupObjectCache(Isolate* isolate, RootVisitor* visitor);
static void IterateSharedHeapObjectCache(Isolate* isolate,
RootVisitor* visitor);
protected:
static bool CanBeDeferred(HeapObject o);
......@@ -32,8 +35,8 @@ class SerializerDeserializer : public RootVisitor {
// clang-format off
#define UNUSED_SERIALIZER_BYTE_CODES(V) \
/* Free range 0x1c..0x1f */ \
V(0x1c) V(0x1d) V(0x1e) V(0x1f) \
/* Free range 0x1d..0x1f */ \
V(0x1d) V(0x1e) V(0x1f) \
/* Free range 0x20..0x2f */ \
V(0x20) V(0x21) V(0x22) V(0x23) V(0x24) V(0x25) V(0x26) V(0x27) \
V(0x28) V(0x29) V(0x2a) V(0x2b) V(0x2c) V(0x2d) V(0x2e) V(0x2f) \
......@@ -80,7 +83,7 @@ class SerializerDeserializer : public RootVisitor {
enum Bytecode : byte {
//
// ---------- byte code range 0x00..0x1b ----------
// ---------- byte code range 0x00..0x1c ----------
//
// 0x00..0x03 Allocate new object, in specified space.
......@@ -97,6 +100,8 @@ class SerializerDeserializer : public RootVisitor {
kAttachedReference,
// Object in the read-only object cache.
kReadOnlyObjectCache,
// Object in the shared heap object cache.
kSharedHeapObjectCache,
// Do nothing, used for padding.
kNop,
// A tag emitted at strategic points in the snapshot to delineate sections.
......
// Copyright 2021 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/shared-heap-deserializer.h"
#include "src/heap/heap-inl.h"
#include "src/snapshot/shared-heap-serializer.h"
namespace v8 {
namespace internal {
void SharedHeapDeserializer::DeserializeIntoIsolate() {
// Don't deserialize into client Isolates. If there are client Isolates, the
// shared heap object cache should already be populated.
DCHECK_IMPLIES(isolate()->shared_isolate() != nullptr,
!isolate()->shared_heap_object_cache()->empty());
if (isolate()->shared_isolate() != nullptr) return;
DCHECK(isolate()->shared_heap_object_cache()->empty());
HandleScope scope(isolate());
IterateSharedHeapObjectCache(isolate(), this);
DeserializeStringTable();
DeserializeDeferredObjects();
if (FLAG_rehash_snapshot && can_rehash()) {
// Hash seed was initialized in ReadOnlyDeserializer.
Rehash();
}
}
void SharedHeapDeserializer::DeserializeStringTable() {
// See SharedHeapSerializer::SerializeStringTable.
DCHECK(isolate()->OwnsStringTable());
// Get the string table size.
int string_table_size = source()->GetInt();
// Add each string to the Isolate's string table.
// TODO(leszeks): Consider pre-sizing the string table.
for (int i = 0; i < string_table_size; ++i) {
Handle<String> string = Handle<String>::cast(ReadObject());
StringTableInsertionKey key(
isolate(), string,
DeserializingUserCodeOption::kNotDeserializingUserCode);
Handle<String> result =
isolate()->string_table()->LookupKey(isolate(), &key);
// Since this is startup, there should be no duplicate entries in the
// string table, and the lookup should unconditionally add the given
// string.
DCHECK_EQ(*result, *string);
USE(result);
}
DCHECK_EQ(string_table_size, isolate()->string_table()->NumberOfElements());
}
} // namespace internal
} // namespace v8
// Copyright 2021 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_SHARED_HEAP_DESERIALIZER_H_
#define V8_SNAPSHOT_SHARED_HEAP_DESERIALIZER_H_
#include "src/snapshot/deserializer.h"
#include "src/snapshot/snapshot-data.h"
#include "src/snapshot/snapshot.h"
namespace v8 {
namespace internal {
// Initializes objects in the shared isolate that are not already included in
// the startup snapshot.
class SharedHeapDeserializer final : public Deserializer<Isolate> {
public:
explicit SharedHeapDeserializer(Isolate* isolate,
const SnapshotData* shared_heap_data,
bool can_rehash)
: Deserializer(isolate, shared_heap_data->Payload(),
shared_heap_data->GetMagicNumber(), false, can_rehash) {}
// Depending on runtime flags, deserialize shared heap objects into the
// Isolate.
void DeserializeIntoIsolate();
private:
void DeserializeStringTable();
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_SHARED_HEAP_DESERIALIZER_H_
// Copyright 2021 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/shared-heap-serializer.h"
#include "src/heap/heap-inl.h"
#include "src/heap/read-only-heap.h"
#include "src/objects/objects-inl.h"
#include "src/snapshot/read-only-serializer.h"
namespace v8 {
namespace internal {
// static
bool SharedHeapSerializer::ShouldBeInSharedOldSpace(Isolate* isolate,
HeapObject obj) {
if (obj.IsString()) {
return obj.IsInternalizedString() ||
String::IsInPlaceInternalizable(isolate, String::cast(obj));
}
return false;
}
SharedHeapSerializer::SharedHeapSerializer(
Isolate* isolate, Snapshot::SerializerFlags flags,
ReadOnlySerializer* read_only_serializer)
: RootsSerializer(isolate, flags, RootIndex::kFirstStrongRoot),
read_only_serializer_(read_only_serializer)
#ifdef DEBUG
,
serialized_objects_(isolate->heap())
#endif
{
}
SharedHeapSerializer::~SharedHeapSerializer() {
OutputStatistics("SharedHeapSerializer");
}
void SharedHeapSerializer::FinalizeSerialization() {
// This is called after serialization of the startup and context snapshots
// which entries are added to the shared heap object cache. Terminate the
// cache with an undefined.
Object undefined = ReadOnlyRoots(isolate()).undefined_value();
VisitRootPointer(Root::kSharedHeapObjectCache, nullptr,
FullObjectSlot(&undefined));
// When FLAG_shared_string_table is true, all internalized and
// internalizable-in-place strings are in the shared heap.
SerializeStringTable(isolate()->string_table());
SerializeDeferredObjects();
Pad();
#ifdef DEBUG
// During snapshotting there is no shared heap.
CHECK(!isolate()->is_shared());
CHECK_NULL(isolate()->shared_isolate());
// Check that all serialized object are in shared heap and not RO. RO objects
// should be in the RO snapshot.
IdentityMap<int, base::DefaultAllocationPolicy>::IteratableScope it_scope(
&serialized_objects_);
for (auto it = it_scope.begin(); it != it_scope.end(); ++it) {
HeapObject obj = HeapObject::cast(it.key());
CHECK(ShouldBeInSharedOldSpace(isolate(), obj));
CHECK(!ReadOnlyHeap::Contains(obj));
}
#endif
}
bool SharedHeapSerializer::SerializeUsingReadOnlyObjectCache(
SnapshotByteSink* sink, Handle<HeapObject> obj) {
return read_only_serializer_->SerializeUsingReadOnlyObjectCache(sink, obj);
}
bool SharedHeapSerializer::SerializeUsingSharedHeapObjectCache(
SnapshotByteSink* sink, Handle<HeapObject> obj) {
if (!ShouldBeInSharedOldSpace(isolate(), *obj)) return false;
int cache_index = SerializeInObjectCache(obj);
sink->Put(kSharedHeapObjectCache, "SharedHeapObjectCache");
sink->PutInt(cache_index, "shared_heap_object_cache_index");
return true;
}
void SharedHeapSerializer::SerializeStringTable(StringTable* string_table) {
// A StringTable is serialized as:
//
// N : int
// string 1
// string 2
// ...
// string N
//
// Notably, the hashmap structure, including empty and deleted elements, is
// not serialized.
sink_.PutInt(string_table->NumberOfElements(),
"String table number of elements");
// Custom RootVisitor which walks the string table, but only serializes the
// string entries. This is an inline class to be able to access the non-public
// SerializeObject method.
class SharedHeapSerializerStringTableVisitor : public RootVisitor {
public:
explicit SharedHeapSerializerStringTableVisitor(
SharedHeapSerializer* serializer)
: serializer_(serializer) {}
void VisitRootPointers(Root root, const char* description,
FullObjectSlot start, FullObjectSlot end) override {
UNREACHABLE();
}
void VisitRootPointers(Root root, const char* description,
OffHeapObjectSlot start,
OffHeapObjectSlot end) override {
DCHECK_EQ(root, Root::kStringTable);
Isolate* isolate = serializer_->isolate();
for (OffHeapObjectSlot current = start; current < end; ++current) {
Object obj = current.load(isolate);
if (obj.IsHeapObject()) {
DCHECK(obj.IsInternalizedString());
serializer_->SerializeObject(handle(HeapObject::cast(obj), isolate));
}
}
}
private:
SharedHeapSerializer* serializer_;
};
SharedHeapSerializerStringTableVisitor string_table_visitor(this);
isolate()->string_table()->IterateElements(&string_table_visitor);
}
void SharedHeapSerializer::SerializeObjectImpl(Handle<HeapObject> obj) {
// Objects in the shared heap cannot depend on per-Isolate roots but can
// depend on RO roots since sharing objects requires sharing the RO space.
DCHECK(ShouldBeInSharedOldSpace(isolate(), *obj) ||
ReadOnlyHeap::Contains(*obj));
if (SerializeHotObject(obj)) return;
if (IsRootAndHasBeenSerialized(*obj) && SerializeRoot(obj)) return;
if (SerializeUsingReadOnlyObjectCache(&sink_, obj)) return;
if (SerializeBackReference(obj)) return;
CheckRehashability(*obj);
DCHECK(!ReadOnlyHeap::Contains(*obj));
ObjectSerializer object_serializer(this, obj, &sink_);
object_serializer.Serialize();
#ifdef DEBUG
CHECK_NULL(serialized_objects_.Find(obj));
// There's no "IdentitySet", so use an IdentityMap with a value that is
// later ignored.
serialized_objects_.Insert(obj, 0);
#endif
}
} // namespace internal
} // namespace v8
// Copyright 2021 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_SHARED_HEAP_SERIALIZER_H_
#define V8_SNAPSHOT_SHARED_HEAP_SERIALIZER_H_
#include "src/snapshot/roots-serializer.h"
namespace v8 {
namespace internal {
class HeapObject;
class ReadOnlySerializer;
// SharedHeapSerializer serializes objects that should be in the shared heap in
// the shared Isolate during startup. Currently the shared heap is only in use
// behind flags (e.g. --shared-string-table). When it is not in use, its
// contents are deserialized into each Isolate.
class V8_EXPORT_PRIVATE SharedHeapSerializer : public RootsSerializer {
public:
SharedHeapSerializer(Isolate* isolate, Snapshot::SerializerFlags flags,
ReadOnlySerializer* read_only_serializer);
~SharedHeapSerializer() override;
SharedHeapSerializer(const SharedHeapSerializer&) = delete;
SharedHeapSerializer& operator=(const SharedHeapSerializer&) = delete;
// Terminate the shared heap object cache with an undefined value and
// serialize the string table..
void FinalizeSerialization();
// If |obj| can be serialized in the read-only snapshot then add it to the
// read-only object cache if not already present and emit a
// ReadOnlyObjectCache bytecode into |sink|. Returns whether this was
// successful.
bool SerializeUsingReadOnlyObjectCache(SnapshotByteSink* sink,
Handle<HeapObject> obj);
// If |obj| can be serialized in the shared heap snapshot then add it to the
// shared heap object cache if not already present and emit a
// SharedHeapObjectCache bytecode into |sink|. Returns whether this was
// successful.
bool SerializeUsingSharedHeapObjectCache(SnapshotByteSink* sink,
Handle<HeapObject> obj);
static bool ShouldBeInSharedOldSpace(Isolate* isolate, HeapObject obj);
private:
void SerializeStringTable(StringTable* string_table);
void SerializeObjectImpl(Handle<HeapObject> obj) override;
ReadOnlySerializer* read_only_serializer_;
#ifdef DEBUG
IdentityMap<int, base::DefaultAllocationPolicy> serialized_objects_;
#endif
};
} // namespace internal
} // namespace v8
#endif // V8_SNAPSHOT_SHARED_HEAP_SERIALIZER_H_
......@@ -18,6 +18,8 @@
#include "src/snapshot/context-serializer.h"
#include "src/snapshot/read-only-deserializer.h"
#include "src/snapshot/read-only-serializer.h"
#include "src/snapshot/shared-heap-deserializer.h"
#include "src/snapshot/shared-heap-serializer.h"
#include "src/snapshot/snapshot-utils.h"
#include "src/snapshot/startup-deserializer.h"
#include "src/snapshot/startup-serializer.h"
......@@ -38,6 +40,7 @@ class SnapshotImpl : public AllStatic {
static v8::StartupData CreateSnapshotBlob(
const SnapshotData* startup_snapshot_in,
const SnapshotData* read_only_snapshot_in,
const SnapshotData* shared_heap_snapshot_in,
const std::vector<SnapshotData*>& context_snapshots_in,
bool can_be_rehashed);
......@@ -48,6 +51,8 @@ class SnapshotImpl : public AllStatic {
const v8::StartupData* data);
static base::Vector<const byte> ExtractReadOnlyData(
const v8::StartupData* data);
static base::Vector<const byte> ExtractSharedHeapData(
const v8::StartupData* data);
static base::Vector<const byte> ExtractContextData(
const v8::StartupData* data, uint32_t index);
......@@ -68,12 +73,14 @@ class SnapshotImpl : public AllStatic {
// [2] checksum
// [3] (128 bytes) version string
// [4] offset to readonly
// [5] offset to context 0
// [6] offset to context 1
// [5] offset to shared heap
// [6] offset to context 0
// [7] offset to context 1
// ...
// ... offset to context N - 1
// ... startup snapshot data
// ... read-only snapshot data
// ... shared heap snapshot data
// ... context 0 snapshot data
// ... context 1 snapshot data
......@@ -86,8 +93,10 @@ class SnapshotImpl : public AllStatic {
static const uint32_t kVersionStringLength = 64;
static const uint32_t kReadOnlyOffsetOffset =
kVersionStringOffset + kVersionStringLength;
static const uint32_t kFirstContextOffsetOffset =
static const uint32_t kSharedHeapOffsetOffset =
kReadOnlyOffsetOffset + kUInt32Size;
static const uint32_t kFirstContextOffsetOffset =
kSharedHeapOffsetOffset + kUInt32Size;
static base::Vector<const byte> ChecksummedContent(
const v8::StartupData* data) {
......@@ -161,13 +170,16 @@ bool Snapshot::Initialize(Isolate* isolate) {
SnapshotImpl::ExtractStartupData(blob);
base::Vector<const byte> read_only_data =
SnapshotImpl::ExtractReadOnlyData(blob);
base::Vector<const byte> shared_heap_data =
SnapshotImpl::ExtractSharedHeapData(blob);
SnapshotData startup_snapshot_data(MaybeDecompress(startup_data));
SnapshotData read_only_snapshot_data(MaybeDecompress(read_only_data));
SnapshotData shared_heap_snapshot_data(MaybeDecompress(shared_heap_data));
bool success = isolate->InitWithSnapshot(&startup_snapshot_data,
&read_only_snapshot_data,
ExtractRehashability(blob));
bool success = isolate->InitWithSnapshot(
&startup_snapshot_data, &read_only_snapshot_data,
&shared_heap_snapshot_data, ExtractRehashability(blob));
if (FLAG_profile_deserialization) {
double ms = timer.Elapsed().InMillisecondsF();
int bytes = startup_data.length();
......@@ -355,7 +367,11 @@ v8::StartupData Snapshot::Create(
ReadOnlySerializer read_only_serializer(isolate, flags);
read_only_serializer.SerializeReadOnlyRoots();
StartupSerializer startup_serializer(isolate, flags, &read_only_serializer);
SharedHeapSerializer shared_heap_serializer(isolate, flags,
&read_only_serializer);
StartupSerializer startup_serializer(isolate, flags, &read_only_serializer,
&shared_heap_serializer);
startup_serializer.SerializeStrongReferences(no_gc);
// Serialize each context with a new serializer.
......@@ -384,12 +400,16 @@ v8::StartupData Snapshot::Create(
startup_serializer.CheckNoDirtyFinalizationRegistries();
shared_heap_serializer.FinalizeSerialization();
can_be_rehashed = can_be_rehashed && shared_heap_serializer.can_be_rehashed();
read_only_serializer.FinalizeSerialization();
can_be_rehashed = can_be_rehashed && read_only_serializer.can_be_rehashed();
if (FLAG_serialization_statistics) {
// These prints should match the regexp in test/memory/Memory.json
DCHECK_NE(read_only_serializer.TotalAllocationSize(), 0);
DCHECK_NE(shared_heap_serializer.TotalAllocationSize(), 0);
DCHECK_NE(startup_serializer.TotalAllocationSize(), 0);
PrintF("Deserialization will allocate:\n");
PrintF("%10d bytes per isolate\n",
......@@ -403,9 +423,10 @@ v8::StartupData Snapshot::Create(
}
SnapshotData read_only_snapshot(&read_only_serializer);
SnapshotData shared_heap_snapshot(&shared_heap_serializer);
SnapshotData startup_snapshot(&startup_serializer);
v8::StartupData result =
SnapshotImpl::CreateSnapshotBlob(&startup_snapshot, &read_only_snapshot,
v8::StartupData result = SnapshotImpl::CreateSnapshotBlob(
&startup_snapshot, &read_only_snapshot, &shared_heap_snapshot,
context_snapshots, can_be_rehashed);
for (const SnapshotData* ptr : context_snapshots) delete ptr;
......@@ -426,20 +447,25 @@ v8::StartupData Snapshot::Create(Isolate* isolate, Context default_context,
v8::StartupData SnapshotImpl::CreateSnapshotBlob(
const SnapshotData* startup_snapshot_in,
const SnapshotData* read_only_snapshot_in,
const SnapshotData* shared_heap_snapshot_in,
const std::vector<SnapshotData*>& context_snapshots_in,
bool can_be_rehashed) {
// Have these separate from snapshot_in for compression, since we need to
// access the compressed data as well as the uncompressed reservations.
const SnapshotData* startup_snapshot;
const SnapshotData* read_only_snapshot;
const SnapshotData* shared_heap_snapshot;
const std::vector<SnapshotData*>* context_snapshots;
#ifdef V8_SNAPSHOT_COMPRESSION
SnapshotData startup_compressed(
SnapshotCompression::Compress(startup_snapshot_in));
SnapshotData read_only_compressed(
SnapshotCompression::Compress(read_only_snapshot_in));
SnapshotData shared_heap_compressed(
SnapshotCompression::Compress(shared_heap_snapshot_in));
startup_snapshot = &startup_compressed;
read_only_snapshot = &read_only_compressed;
shared_heap_snapshot = &shared_heap_compressed;
std::vector<SnapshotData> context_snapshots_compressed;
context_snapshots_compressed.reserve(context_snapshots_in.size());
std::vector<SnapshotData*> context_snapshots_compressed_ptrs;
......@@ -453,6 +479,7 @@ v8::StartupData SnapshotImpl::CreateSnapshotBlob(
#else
startup_snapshot = startup_snapshot_in;
read_only_snapshot = read_only_snapshot_in;
shared_heap_snapshot = shared_heap_snapshot_in;
context_snapshots = &context_snapshots_in;
#endif
......@@ -462,6 +489,8 @@ v8::StartupData SnapshotImpl::CreateSnapshotBlob(
uint32_t total_length = startup_snapshot_offset;
total_length += static_cast<uint32_t>(startup_snapshot->RawData().length());
total_length += static_cast<uint32_t>(read_only_snapshot->RawData().length());
total_length +=
static_cast<uint32_t>(shared_heap_snapshot->RawData().length());
for (const auto context_snapshot : *context_snapshots) {
total_length += static_cast<uint32_t>(context_snapshot->RawData().length());
}
......@@ -508,6 +537,19 @@ v8::StartupData SnapshotImpl::CreateSnapshotBlob(
}
payload_offset += payload_length;
// Shared heap.
SnapshotImpl::SetHeaderValue(data, SnapshotImpl::kSharedHeapOffsetOffset,
payload_offset);
payload_length = shared_heap_snapshot->RawData().length();
CopyBytes(
data + payload_offset,
reinterpret_cast<const char*>(shared_heap_snapshot->RawData().begin()),
payload_length);
if (FLAG_serialization_statistics) {
PrintF("%10d bytes for shared heap\n", payload_length);
}
payload_offset += payload_length;
// Context snapshots (context-specific data).
for (uint32_t i = 0; i < num_contexts; i++) {
SnapshotImpl::SetHeaderValue(
......@@ -600,6 +642,14 @@ base::Vector<const byte> SnapshotImpl::ExtractReadOnlyData(
DCHECK(Snapshot::SnapshotIsValid(data));
return ExtractData(data, GetHeaderValue(data, kReadOnlyOffsetOffset),
GetHeaderValue(data, kSharedHeapOffsetOffset));
}
base::Vector<const byte> SnapshotImpl::ExtractSharedHeapData(
const v8::StartupData* data) {
DCHECK(Snapshot::SnapshotIsValid(data));
return ExtractData(data, GetHeaderValue(data, kSharedHeapOffsetOffset),
GetHeaderValue(data, ContextSnapshotOffsetOffset(0)));
}
......
......@@ -31,8 +31,7 @@ void StartupDeserializer::DeserializeIntoIsolate() {
isolate()->heap()->IterateRoots(
this,
base::EnumSet<SkipRoot>{SkipRoot::kUnserializable, SkipRoot::kWeak});
Iterate(isolate(), this);
DeserializeStringTable();
IterateStartupObjectCache(isolate(), this);
isolate()->heap()->IterateWeakRoots(
this, base::EnumSet<SkipRoot>{SkipRoot::kUnserializable});
......@@ -70,44 +69,11 @@ void StartupDeserializer::DeserializeIntoIsolate() {
WeakenDescriptorArrays();
if (FLAG_rehash_snapshot && can_rehash()) {
// Hash seed was initalized in ReadOnlyDeserializer.
// Hash seed was initialized in ReadOnlyDeserializer.
Rehash();
}
}
void StartupDeserializer::DeserializeStringTable() {
// See StartupSerializer::SerializeStringTable.
// Get the string table size.
int string_table_size = source()->GetInt();
// Add each string to the Isolate's string table.
// TODO(leszeks): Consider pre-sizing the string table.
for (int i = 0; i < string_table_size; ++i) {
Handle<String> string = Handle<String>::cast(ReadObject());
StringTableInsertionKey key(
isolate(), string,
DeserializingUserCodeOption::kNotDeserializingUserCode);
Handle<String> result =
isolate()->string_table()->LookupKey(isolate(), &key);
if (isolate()->OwnsStringTable()) {
// When not sharing the string table, since this is startup, there should
// be no duplicate entries in the string table, and the lookup should
// unconditionally add the given string.
DCHECK_EQ(*result, *string);
USE(result);
} else if (*result != *string) {
DCHECK(!string->InSharedHeap());
DCHECK(result->InSharedHeap());
string->MakeThin(isolate(), *result);
string.PatchValue(*result);
}
}
DCHECK_EQ(string_table_size, isolate()->string_table()->NumberOfElements());
}
void StartupDeserializer::LogNewMapEvents() {
if (FLAG_log_maps) LOG(isolate(), LogAllMaps());
}
......
......@@ -25,7 +25,6 @@ class StartupDeserializer final : public Deserializer<Isolate> {
void DeserializeIntoIsolate();
private:
void DeserializeStringTable();
void FlushICache();
void LogNewMapEvents();
};
......
......@@ -15,6 +15,7 @@
#include "src/objects/objects-inl.h"
#include "src/objects/slots.h"
#include "src/snapshot/read-only-serializer.h"
#include "src/snapshot/shared-heap-serializer.h"
namespace v8 {
namespace internal {
......@@ -62,11 +63,13 @@ class V8_NODISCARD SanitizeIsolateScope final {
} // namespace
StartupSerializer::StartupSerializer(Isolate* isolate,
Snapshot::SerializerFlags flags,
ReadOnlySerializer* read_only_serializer)
StartupSerializer::StartupSerializer(
Isolate* isolate, Snapshot::SerializerFlags flags,
ReadOnlySerializer* read_only_serializer,
SharedHeapSerializer* shared_heap_serializer)
: RootsSerializer(isolate, flags, RootIndex::kFirstStrongRoot),
read_only_serializer_(read_only_serializer),
shared_heap_serializer_(shared_heap_serializer),
accessor_infos_(isolate->heap()),
call_handler_infos_(isolate->heap()) {
InitializeCodeAddressMap();
......@@ -146,6 +149,7 @@ void StartupSerializer::SerializeObjectImpl(Handle<HeapObject> obj) {
if (SerializeHotObject(obj)) return;
if (IsRootAndHasBeenSerialized(*obj) && SerializeRoot(obj)) return;
if (SerializeUsingReadOnlyObjectCache(&sink_, obj)) return;
if (SerializeUsingSharedHeapObjectCache(&sink_, obj)) return;
if (SerializeBackReference(obj)) return;
bool use_simulator = false;
......@@ -196,64 +200,12 @@ void StartupSerializer::SerializeWeakReferencesAndDeferred() {
VisitRootPointer(Root::kStartupObjectCache, nullptr,
FullObjectSlot(&undefined));
SerializeStringTable(isolate()->string_table());
isolate()->heap()->IterateWeakRoots(
this, base::EnumSet<SkipRoot>{SkipRoot::kUnserializable});
SerializeDeferredObjects();
Pad();
}
void StartupSerializer::SerializeStringTable(StringTable* string_table) {
// A StringTable is serialized as:
//
// N : int
// string 1
// string 2
// ...
// string N
//
// Notably, the hashmap structure, including empty and deleted elements, is
// not serialized.
sink_.PutInt(isolate()->string_table()->NumberOfElements(),
"String table number of elements");
// Custom RootVisitor which walks the string table, but only serializes the
// string entries. This is an inline class to be able to access the non-public
// SerializeObject method.
class StartupSerializerStringTableVisitor : public RootVisitor {
public:
explicit StartupSerializerStringTableVisitor(StartupSerializer* serializer)
: serializer_(serializer) {}
void VisitRootPointers(Root root, const char* description,
FullObjectSlot start, FullObjectSlot end) override {
UNREACHABLE();
}
void VisitRootPointers(Root root, const char* description,
OffHeapObjectSlot start,
OffHeapObjectSlot end) override {
DCHECK_EQ(root, Root::kStringTable);
Isolate* isolate = serializer_->isolate();
for (OffHeapObjectSlot current = start; current < end; ++current) {
Object obj = current.load(isolate);
if (obj.IsHeapObject()) {
DCHECK(obj.IsInternalizedString());
serializer_->SerializeObject(handle(HeapObject::cast(obj), isolate));
}
}
}
private:
StartupSerializer* serializer_;
};
StartupSerializerStringTableVisitor string_table_visitor(this);
isolate()->string_table()->IterateElements(&string_table_visitor);
}
void StartupSerializer::SerializeStrongReferences(
const DisallowGarbageCollection& no_gc) {
Isolate* isolate = this->isolate();
......@@ -285,6 +237,12 @@ bool StartupSerializer::SerializeUsingReadOnlyObjectCache(
return read_only_serializer_->SerializeUsingReadOnlyObjectCache(sink, obj);
}
bool StartupSerializer::SerializeUsingSharedHeapObjectCache(
SnapshotByteSink* sink, Handle<HeapObject> obj) {
return shared_heap_serializer_->SerializeUsingSharedHeapObjectCache(sink,
obj);
}
void StartupSerializer::SerializeUsingStartupObjectCache(
SnapshotByteSink* sink, Handle<HeapObject> obj) {
int cache_index = SerializeInObjectCache(obj);
......
......@@ -16,11 +16,13 @@ namespace internal {
class HeapObject;
class SnapshotByteSink;
class ReadOnlySerializer;
class SharedHeapSerializer;
class V8_EXPORT_PRIVATE StartupSerializer : public RootsSerializer {
public:
StartupSerializer(Isolate* isolate, Snapshot::SerializerFlags flags,
ReadOnlySerializer* read_only_serializer);
ReadOnlySerializer* read_only_serializer,
SharedHeapSerializer* shared_heap_serializer);
~StartupSerializer() override;
StartupSerializer(const StartupSerializer&) = delete;
StartupSerializer& operator=(const StartupSerializer&) = delete;
......@@ -40,6 +42,13 @@ class V8_EXPORT_PRIVATE StartupSerializer : public RootsSerializer {
bool SerializeUsingReadOnlyObjectCache(SnapshotByteSink* sink,
Handle<HeapObject> obj);
// If |obj| can be serialized in the shared heap snapshot then add it to the
// shareable object cache if not already present and emits a
// SharedHeapObjectCache bytecode into |sink|. Returns whether this was
// successful.
bool SerializeUsingSharedHeapObjectCache(SnapshotByteSink* sink,
Handle<HeapObject> obj);
// Adds |obj| to the startup object object cache if not already present and
// emits a StartupObjectCache bytecode into |sink|.
void SerializeUsingStartupObjectCache(SnapshotByteSink* sink,
......@@ -51,9 +60,9 @@ class V8_EXPORT_PRIVATE StartupSerializer : public RootsSerializer {
private:
void SerializeObjectImpl(Handle<HeapObject> o) override;
void SerializeStringTable(StringTable* string_table);
ReadOnlySerializer* read_only_serializer_;
ReadOnlySerializer* const read_only_serializer_;
SharedHeapSerializer* const shared_heap_serializer_;
GlobalHandleVector<AccessorInfo> accessor_infos_;
GlobalHandleVector<CallHandlerInfo> call_handler_infos_;
};
......
......@@ -164,7 +164,7 @@ UNINITIALIZED_TEST(SharedCollectionWithoutClients) {
Isolate::Delete(shared_isolate);
}
void AllocateInSharedSpace(Isolate* shared_isolate) {
void AllocateInSharedHeap(Isolate* shared_isolate) {
SetupClientIsolateAndRunCallback(
shared_isolate,
[](v8::Isolate* client_isolate, Isolate* i_client_isolate) {
......@@ -198,7 +198,7 @@ UNINITIALIZED_TEST(SharedCollectionWithOneClient) {
create_params.array_buffer_allocator = allocator.get();
Isolate* shared_isolate = Isolate::NewShared(create_params);
AllocateInSharedSpace(shared_isolate);
AllocateInSharedHeap(shared_isolate);
Isolate::Delete(shared_isolate);
}
......
......@@ -57,6 +57,8 @@
#include "src/snapshot/context-serializer.h"
#include "src/snapshot/read-only-deserializer.h"
#include "src/snapshot/read-only-serializer.h"
#include "src/snapshot/shared-heap-deserializer.h"
#include "src/snapshot/shared-heap-serializer.h"
#include "src/snapshot/snapshot-compression.h"
#include "src/snapshot/snapshot.h"
#include "src/snapshot/startup-deserializer.h"
......@@ -81,10 +83,12 @@ void DisableAlwaysOpt() {
struct StartupBlobs {
base::Vector<const byte> startup;
base::Vector<const byte> read_only;
base::Vector<const byte> shared_space;
void Dispose() {
startup.Dispose();
read_only.Dispose();
shared_space.Dispose();
}
};
......@@ -94,24 +98,30 @@ class TestSerializer {
static v8::Isolate* NewIsolateInitialized() {
const bool kEnableSerializer = true;
const bool kGenerateHeap = true;
const bool kIsShared = false;
DisableEmbeddedBlobRefcounting();
v8::Isolate* v8_isolate = NewIsolate(kEnableSerializer, kGenerateHeap);
v8::Isolate* v8_isolate =
NewIsolate(kEnableSerializer, kGenerateHeap, kIsShared);
v8::Isolate::Scope isolate_scope(v8_isolate);
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
isolate->Init(nullptr, nullptr, false);
isolate->Init(nullptr, nullptr, nullptr, false);
return v8_isolate;
}
static v8::Isolate* NewIsolateFromBlob(const StartupBlobs& blobs) {
SnapshotData startup_snapshot(blobs.startup);
SnapshotData read_only_snapshot(blobs.read_only);
const bool kEnableSerializer = false;
const bool kGenerateHeap = false;
v8::Isolate* v8_isolate = NewIsolate(kEnableSerializer, kGenerateHeap);
v8::Isolate::Scope isolate_scope(v8_isolate);
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
isolate->Init(&startup_snapshot, &read_only_snapshot, false);
return v8_isolate;
const bool kIsShared = false;
return NewIsolateFromBlob(blobs, kIsShared, nullptr);
}
static v8::Isolate* NewSharedIsolateFromBlob(const StartupBlobs& blobs) {
const bool kIsShared = true;
return NewIsolateFromBlob(blobs, kIsShared, nullptr);
}
static v8::Isolate* NewClientIsolateFromBlob(const StartupBlobs& blobs,
v8::Isolate* shared_isolate) {
const bool kIsShared = false;
return NewIsolateFromBlob(blobs, kIsShared, shared_isolate);
}
// Wraps v8::Isolate::New, but with a test isolate under the hood.
......@@ -120,15 +130,18 @@ class TestSerializer {
static v8::Isolate* NewIsolate(const v8::Isolate::CreateParams& params) {
const bool kEnableSerializer = false;
const bool kGenerateHeap = params.snapshot_blob == nullptr;
v8::Isolate* v8_isolate = NewIsolate(kEnableSerializer, kGenerateHeap);
const bool kIsShared = false;
v8::Isolate* v8_isolate =
NewIsolate(kEnableSerializer, kGenerateHeap, kIsShared);
v8::Isolate::Initialize(v8_isolate, params);
return v8_isolate;
}
private:
// Creates an Isolate instance configured for testing.
static v8::Isolate* NewIsolate(bool with_serializer, bool generate_heap) {
i::Isolate* isolate = i::Isolate::New();
static v8::Isolate* NewIsolate(bool with_serializer, bool generate_heap,
bool is_shared) {
i::Isolate* isolate = i::Isolate::Allocate(is_shared);
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
if (with_serializer) isolate->enable_serializer();
......@@ -137,6 +150,29 @@ class TestSerializer {
return v8_isolate;
}
static v8::Isolate* NewIsolateFromBlob(const StartupBlobs& blobs,
bool is_shared,
v8::Isolate* shared_isolate) {
SnapshotData startup_snapshot(blobs.startup);
SnapshotData read_only_snapshot(blobs.read_only);
SnapshotData shared_space_snapshot(blobs.shared_space);
const bool kEnableSerializer = false;
const bool kGenerateHeap = false;
if (is_shared) CHECK_NULL(shared_isolate);
v8::Isolate* v8_isolate =
NewIsolate(kEnableSerializer, kGenerateHeap, is_shared);
v8::Isolate::Scope isolate_scope(v8_isolate);
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
if (shared_isolate) {
CHECK(!is_shared);
isolate->AttachToSharedIsolate(
reinterpret_cast<Isolate*>(shared_isolate));
}
isolate->Init(&startup_snapshot, &read_only_snapshot,
&shared_space_snapshot, false);
return v8_isolate;
}
};
static base::Vector<const byte> WritePayload(
......@@ -182,16 +218,24 @@ static StartupBlobs Serialize(v8::Isolate* isolate) {
Snapshot::kDefaultSerializerFlags);
read_only_serializer.SerializeReadOnlyRoots();
StartupSerializer ser(internal_isolate, Snapshot::kDefaultSerializerFlags,
SharedHeapSerializer shared_space_serializer(
internal_isolate, Snapshot::kDefaultSerializerFlags,
&read_only_serializer);
StartupSerializer ser(internal_isolate, Snapshot::kDefaultSerializerFlags,
&read_only_serializer, &shared_space_serializer);
ser.SerializeStrongReferences(no_gc);
ser.SerializeWeakReferencesAndDeferred();
shared_space_serializer.FinalizeSerialization();
read_only_serializer.FinalizeSerialization();
SnapshotData startup_snapshot(&ser);
SnapshotData read_only_snapshot(&read_only_serializer);
SnapshotData shared_space_snapshot(&shared_space_serializer);
return {WritePayload(startup_snapshot.RawData()),
WritePayload(read_only_snapshot.RawData())};
WritePayload(read_only_snapshot.RawData()),
WritePayload(shared_space_snapshot.RawData())};
}
base::Vector<const char> ConstructSource(base::Vector<const char> head,
......@@ -327,6 +371,7 @@ UNINITIALIZED_TEST(StartupSerializerTwiceRunScript) {
static void SerializeContext(base::Vector<const byte>* startup_blob_out,
base::Vector<const byte>* read_only_blob_out,
base::Vector<const byte>* shared_space_blob_out,
base::Vector<const byte>* context_blob_out) {
v8::Isolate* v8_isolate = TestSerializer::NewIsolateInitialized();
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
......@@ -367,9 +412,13 @@ static void SerializeContext(base::Vector<const byte>* startup_blob_out,
Snapshot::kDefaultSerializerFlags);
read_only_serializer.SerializeReadOnlyRoots();
SharedHeapSerializer shared_space_serializer(
isolate, Snapshot::kDefaultSerializerFlags, &read_only_serializer);
SnapshotByteSink startup_sink;
StartupSerializer startup_serializer(
isolate, Snapshot::kDefaultSerializerFlags, &read_only_serializer);
isolate, Snapshot::kDefaultSerializerFlags, &read_only_serializer,
&shared_space_serializer);
startup_serializer.SerializeStrongReferences(no_gc);
SnapshotByteSink context_sink;
......@@ -380,15 +429,18 @@ static void SerializeContext(base::Vector<const byte>* startup_blob_out,
startup_serializer.SerializeWeakReferencesAndDeferred();
shared_space_serializer.FinalizeSerialization();
read_only_serializer.FinalizeSerialization();
SnapshotData read_only_snapshot(&read_only_serializer);
SnapshotData shared_space_snapshot(&shared_space_serializer);
SnapshotData startup_snapshot(&startup_serializer);
SnapshotData context_snapshot(&context_serializer);
*context_blob_out = WritePayload(context_snapshot.RawData());
*startup_blob_out = WritePayload(startup_snapshot.RawData());
*read_only_blob_out = WritePayload(read_only_snapshot.RawData());
*shared_space_blob_out = WritePayload(shared_space_snapshot.RawData());
}
v8_isolate->Dispose();
}
......@@ -397,8 +449,10 @@ UNINITIALIZED_TEST(SnapshotCompression) {
DisableAlwaysOpt();
base::Vector<const byte> startup_blob;
base::Vector<const byte> read_only_blob;
base::Vector<const byte> shared_space_blob;
base::Vector<const byte> context_blob;
SerializeContext(&startup_blob, &read_only_blob, &context_blob);
SerializeContext(&startup_blob, &read_only_blob, &shared_space_blob,
&context_blob);
SnapshotData original_snapshot_data(context_blob);
SnapshotData compressed =
i::SnapshotCompression::Compress(&original_snapshot_data);
......@@ -408,6 +462,7 @@ UNINITIALIZED_TEST(SnapshotCompression) {
startup_blob.Dispose();
read_only_blob.Dispose();
shared_space_blob.Dispose();
context_blob.Dispose();
}
......@@ -415,10 +470,12 @@ UNINITIALIZED_TEST(ContextSerializerContext) {
DisableAlwaysOpt();
base::Vector<const byte> startup_blob;
base::Vector<const byte> read_only_blob;
base::Vector<const byte> shared_space_blob;
base::Vector<const byte> context_blob;
SerializeContext(&startup_blob, &read_only_blob, &context_blob);
SerializeContext(&startup_blob, &read_only_blob, &shared_space_blob,
&context_blob);
StartupBlobs blobs = {startup_blob, read_only_blob};
StartupBlobs blobs = {startup_blob, read_only_blob, shared_space_blob};
v8::Isolate* v8_isolate = TestSerializer::NewIsolateFromBlob(blobs);
CHECK(v8_isolate);
{
......@@ -457,8 +514,10 @@ UNINITIALIZED_TEST(ContextSerializerContext) {
FreeCurrentEmbeddedBlob();
}
static void SerializeCustomContext(base::Vector<const byte>* startup_blob_out,
static void SerializeCustomContext(
base::Vector<const byte>* startup_blob_out,
base::Vector<const byte>* read_only_blob_out,
base::Vector<const byte>* shared_space_blob_out,
base::Vector<const byte>* context_blob_out) {
v8::Isolate* v8_isolate = TestSerializer::NewIsolateInitialized();
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
......@@ -521,9 +580,13 @@ static void SerializeCustomContext(base::Vector<const byte>* startup_blob_out,
Snapshot::kDefaultSerializerFlags);
read_only_serializer.SerializeReadOnlyRoots();
SharedHeapSerializer shared_space_serializer(
isolate, Snapshot::kDefaultSerializerFlags, &read_only_serializer);
SnapshotByteSink startup_sink;
StartupSerializer startup_serializer(
isolate, Snapshot::kDefaultSerializerFlags, &read_only_serializer);
isolate, Snapshot::kDefaultSerializerFlags, &read_only_serializer,
&shared_space_serializer);
startup_serializer.SerializeStrongReferences(no_gc);
SnapshotByteSink context_sink;
......@@ -534,15 +597,18 @@ static void SerializeCustomContext(base::Vector<const byte>* startup_blob_out,
startup_serializer.SerializeWeakReferencesAndDeferred();
shared_space_serializer.FinalizeSerialization();
read_only_serializer.FinalizeSerialization();
SnapshotData read_only_snapshot(&read_only_serializer);
SnapshotData shared_space_snapshot(&shared_space_serializer);
SnapshotData startup_snapshot(&startup_serializer);
SnapshotData context_snapshot(&context_serializer);
*context_blob_out = WritePayload(context_snapshot.RawData());
*startup_blob_out = WritePayload(startup_snapshot.RawData());
*read_only_blob_out = WritePayload(read_only_snapshot.RawData());
*shared_space_blob_out = WritePayload(shared_space_snapshot.RawData());
}
v8_isolate->Dispose();
}
......@@ -551,10 +617,12 @@ UNINITIALIZED_TEST(ContextSerializerCustomContext) {
DisableAlwaysOpt();
base::Vector<const byte> startup_blob;
base::Vector<const byte> read_only_blob;
base::Vector<const byte> shared_space_blob;
base::Vector<const byte> context_blob;
SerializeCustomContext(&startup_blob, &read_only_blob, &context_blob);
SerializeCustomContext(&startup_blob, &read_only_blob, &shared_space_blob,
&context_blob);
StartupBlobs blobs = {startup_blob, read_only_blob};
StartupBlobs blobs = {startup_blob, read_only_blob, shared_space_blob};
v8::Isolate* v8_isolate = TestSerializer::NewIsolateFromBlob(blobs);
CHECK(v8_isolate);
{
......@@ -4277,5 +4345,51 @@ UNINITIALIZED_TEST(NoStackFrameCacheSerialization) {
delete[] blob.data;
}
namespace {
void CheckObjectsAreInSharedHeap(Isolate* isolate) {
HeapObjectIterator iterator(isolate->heap());
DisallowGarbageCollection no_gc;
for (HeapObject obj = iterator.Next(); !obj.is_null();
obj = iterator.Next()) {
if (SharedHeapSerializer::ShouldBeInSharedOldSpace(isolate, obj)) {
CHECK(obj.InSharedHeap());
}
}
}
} // namespace
UNINITIALIZED_TEST(SharedStrings) {
// Test that deserializing with --shared-string-table deserializes into the
// shared Isolate.
if (!ReadOnlyHeap::IsReadOnlySpaceShared()) return;
if (!COMPRESS_POINTERS_IN_SHARED_CAGE_BOOL) return;
v8::Isolate* isolate_to_serialize = TestSerializer::NewIsolateInitialized();
StartupBlobs blobs = Serialize(isolate_to_serialize);
isolate_to_serialize->Dispose();
FLAG_shared_string_table = true;
v8::Isolate* shared_isolate = TestSerializer::NewSharedIsolateFromBlob(blobs);
v8::Isolate* isolate1 =
TestSerializer::NewClientIsolateFromBlob(blobs, shared_isolate);
v8::Isolate* isolate2 =
TestSerializer::NewClientIsolateFromBlob(blobs, shared_isolate);
Isolate* i_isolate1 = reinterpret_cast<Isolate*>(isolate1);
Isolate* i_isolate2 = reinterpret_cast<Isolate*>(isolate2);
CHECK_EQ(i_isolate1->string_table(), i_isolate2->string_table());
CheckObjectsAreInSharedHeap(i_isolate1);
CheckObjectsAreInSharedHeap(i_isolate2);
isolate1->Dispose();
isolate2->Dispose();
Isolate::Delete(reinterpret_cast<Isolate*>(shared_isolate));
blobs.Dispose();
FreeCurrentEmbeddedBlob();
}
} // namespace internal
} // namespace v8
......@@ -452,62 +452,62 @@ KNOWN_OBJECTS = {
("read_only_space", 0x034b5): "EmptyFunctionScopeInfo",
("read_only_space", 0x034d9): "NativeScopeInfo",
("read_only_space", 0x034f1): "HashSeed",
("old_space", 0x02119): "ArgumentsIteratorAccessor",
("old_space", 0x0215d): "ArrayLengthAccessor",
("old_space", 0x021a1): "BoundFunctionLengthAccessor",
("old_space", 0x021e5): "BoundFunctionNameAccessor",
("old_space", 0x02229): "ErrorStackAccessor",
("old_space", 0x0226d): "FunctionArgumentsAccessor",
("old_space", 0x022b1): "FunctionCallerAccessor",
("old_space", 0x022f5): "FunctionNameAccessor",
("old_space", 0x02339): "FunctionLengthAccessor",
("old_space", 0x0237d): "FunctionPrototypeAccessor",
("old_space", 0x023c1): "StringLengthAccessor",
("old_space", 0x02405): "InvalidPrototypeValidityCell",
("old_space", 0x0240d): "EmptyScript",
("old_space", 0x0244d): "ManyClosuresCell",
("old_space", 0x02459): "ArrayConstructorProtector",
("old_space", 0x0246d): "NoElementsProtector",
("old_space", 0x02481): "MegaDOMProtector",
("old_space", 0x02495): "IsConcatSpreadableProtector",
("old_space", 0x024a9): "ArraySpeciesProtector",
("old_space", 0x024bd): "TypedArraySpeciesProtector",
("old_space", 0x024d1): "PromiseSpeciesProtector",
("old_space", 0x024e5): "RegExpSpeciesProtector",
("old_space", 0x024f9): "StringLengthProtector",
("old_space", 0x0250d): "ArrayIteratorProtector",
("old_space", 0x02521): "ArrayBufferDetachingProtector",
("old_space", 0x02535): "PromiseHookProtector",
("old_space", 0x02549): "PromiseResolveProtector",
("old_space", 0x0255d): "MapIteratorProtector",
("old_space", 0x02571): "PromiseThenProtector",
("old_space", 0x02585): "SetIteratorProtector",
("old_space", 0x02599): "StringIteratorProtector",
("old_space", 0x025ad): "SingleCharacterStringCache",
("old_space", 0x029b5): "StringSplitCache",
("old_space", 0x02dbd): "RegExpMultipleCache",
("old_space", 0x031c5): "BuiltinsConstantsTable",
("old_space", 0x035ed): "AsyncFunctionAwaitRejectSharedFun",
("old_space", 0x03611): "AsyncFunctionAwaitResolveSharedFun",
("old_space", 0x03635): "AsyncGeneratorAwaitRejectSharedFun",
("old_space", 0x03659): "AsyncGeneratorAwaitResolveSharedFun",
("old_space", 0x0367d): "AsyncGeneratorYieldResolveSharedFun",
("old_space", 0x036a1): "AsyncGeneratorReturnResolveSharedFun",
("old_space", 0x036c5): "AsyncGeneratorReturnClosedRejectSharedFun",
("old_space", 0x036e9): "AsyncGeneratorReturnClosedResolveSharedFun",
("old_space", 0x0370d): "AsyncIteratorValueUnwrapSharedFun",
("old_space", 0x03731): "PromiseAllResolveElementSharedFun",
("old_space", 0x03755): "PromiseAllSettledResolveElementSharedFun",
("old_space", 0x03779): "PromiseAllSettledRejectElementSharedFun",
("old_space", 0x0379d): "PromiseAnyRejectElementSharedFun",
("old_space", 0x037c1): "PromiseCapabilityDefaultRejectSharedFun",
("old_space", 0x037e5): "PromiseCapabilityDefaultResolveSharedFun",
("old_space", 0x03809): "PromiseCatchFinallySharedFun",
("old_space", 0x0382d): "PromiseGetCapabilitiesExecutorSharedFun",
("old_space", 0x03851): "PromiseThenFinallySharedFun",
("old_space", 0x03875): "PromiseThrowerFinallySharedFun",
("old_space", 0x03899): "PromiseValueThunkFinallySharedFun",
("old_space", 0x038bd): "ProxyRevokeSharedFun",
("old_space", 0x04aa1): "ArgumentsIteratorAccessor",
("old_space", 0x04ae5): "ArrayLengthAccessor",
("old_space", 0x04b29): "BoundFunctionLengthAccessor",
("old_space", 0x04b6d): "BoundFunctionNameAccessor",
("old_space", 0x04bb1): "ErrorStackAccessor",
("old_space", 0x04bf5): "FunctionArgumentsAccessor",
("old_space", 0x04c39): "FunctionCallerAccessor",
("old_space", 0x04c7d): "FunctionNameAccessor",
("old_space", 0x04cc1): "FunctionLengthAccessor",
("old_space", 0x04d05): "FunctionPrototypeAccessor",
("old_space", 0x04d49): "StringLengthAccessor",
("old_space", 0x04d8d): "InvalidPrototypeValidityCell",
("old_space", 0x04d95): "EmptyScript",
("old_space", 0x04dd5): "ManyClosuresCell",
("old_space", 0x04de1): "ArrayConstructorProtector",
("old_space", 0x04df5): "NoElementsProtector",
("old_space", 0x04e09): "MegaDOMProtector",
("old_space", 0x04e1d): "IsConcatSpreadableProtector",
("old_space", 0x04e31): "ArraySpeciesProtector",
("old_space", 0x04e45): "TypedArraySpeciesProtector",
("old_space", 0x04e59): "PromiseSpeciesProtector",
("old_space", 0x04e6d): "RegExpSpeciesProtector",
("old_space", 0x04e81): "StringLengthProtector",
("old_space", 0x04e95): "ArrayIteratorProtector",
("old_space", 0x04ea9): "ArrayBufferDetachingProtector",
("old_space", 0x04ebd): "PromiseHookProtector",
("old_space", 0x04ed1): "PromiseResolveProtector",
("old_space", 0x04ee5): "MapIteratorProtector",
("old_space", 0x04ef9): "PromiseThenProtector",
("old_space", 0x04f0d): "SetIteratorProtector",
("old_space", 0x04f21): "StringIteratorProtector",
("old_space", 0x04f35): "SingleCharacterStringCache",
("old_space", 0x0533d): "StringSplitCache",
("old_space", 0x05745): "RegExpMultipleCache",
("old_space", 0x05b4d): "BuiltinsConstantsTable",
("old_space", 0x05f75): "AsyncFunctionAwaitRejectSharedFun",
("old_space", 0x05f99): "AsyncFunctionAwaitResolveSharedFun",
("old_space", 0x05fbd): "AsyncGeneratorAwaitRejectSharedFun",
("old_space", 0x05fe1): "AsyncGeneratorAwaitResolveSharedFun",
("old_space", 0x06005): "AsyncGeneratorYieldResolveSharedFun",
("old_space", 0x06029): "AsyncGeneratorReturnResolveSharedFun",
("old_space", 0x0604d): "AsyncGeneratorReturnClosedRejectSharedFun",
("old_space", 0x06071): "AsyncGeneratorReturnClosedResolveSharedFun",
("old_space", 0x06095): "AsyncIteratorValueUnwrapSharedFun",
("old_space", 0x060b9): "PromiseAllResolveElementSharedFun",
("old_space", 0x060dd): "PromiseAllSettledResolveElementSharedFun",
("old_space", 0x06101): "PromiseAllSettledRejectElementSharedFun",
("old_space", 0x06125): "PromiseAnyRejectElementSharedFun",
("old_space", 0x06149): "PromiseCapabilityDefaultRejectSharedFun",
("old_space", 0x0616d): "PromiseCapabilityDefaultResolveSharedFun",
("old_space", 0x06191): "PromiseCatchFinallySharedFun",
("old_space", 0x061b5): "PromiseGetCapabilitiesExecutorSharedFun",
("old_space", 0x061d9): "PromiseThenFinallySharedFun",
("old_space", 0x061fd): "PromiseThrowerFinallySharedFun",
("old_space", 0x06221): "PromiseValueThunkFinallySharedFun",
("old_space", 0x06245): "ProxyRevokeSharedFun",
}
# Lower 32 bits of first page addresses for various heap spaces.
......
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