Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

deps: backport v8 commits useful for snapshot integration #33300

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion common.gypi
Expand Up @@ -36,7 +36,7 @@

# Reset this number to 0 on major V8 upgrades.
# Increment by one for each non-official patch applied to deps/v8.
'v8_embedder_string': '-node.14',
'v8_embedder_string': '-node.17',

##### V8 defaults for Node.js #####

Expand Down
9 changes: 9 additions & 0 deletions deps/v8/include/v8.h
Expand Up @@ -9788,6 +9788,11 @@ class V8_EXPORT V8 {

/**
* Helper class to create a snapshot data blob.
*
* The Isolate used by a SnapshotCreator is owned by it, and will be entered
* and exited by the constructor and destructor, respectively; The destructor
* will also destroy the Isolate. Experimental language features, including
* those available by default, are not available while creating a snapshot.
*/
class V8_EXPORT SnapshotCreator {
public:
Expand Down Expand Up @@ -9816,6 +9821,10 @@ class V8_EXPORT SnapshotCreator {
SnapshotCreator(const intptr_t* external_references = nullptr,
StartupData* existing_blob = nullptr);

/**
* Destroy the snapshot creator, and exit and dispose of the Isolate
* associated with it.
*/
~SnapshotCreator();

/**
Expand Down
6 changes: 5 additions & 1 deletion deps/v8/src/heap/factory.cc
Expand Up @@ -2761,8 +2761,12 @@ Handle<JSGlobalProxy> Factory::NewUninitializedJSGlobalProxy(int size) {
map->set_is_access_check_needed(true);
map->set_may_have_interesting_symbols(true);
LOG(isolate(), MapDetails(*map));
return Handle<JSGlobalProxy>::cast(
Handle<JSGlobalProxy> proxy = Handle<JSGlobalProxy>::cast(
NewJSObjectFromMap(map, AllocationType::kYoung));
// Create identity hash early in case there is any JS collection containing
// a global proxy key and needs to be rehashed after deserialization.
proxy->GetOrCreateIdentityHash(isolate());
return proxy;
}

void Factory::ReinitializeJSGlobalProxy(Handle<JSGlobalProxy> object,
Expand Down
2 changes: 1 addition & 1 deletion deps/v8/src/objects/heap-object.h
Expand Up @@ -191,7 +191,7 @@ class HeapObject : public Object {
bool CanBeRehashed() const;

// Rehash the object based on the layout inferred from its map.
void RehashBasedOnMap(ReadOnlyRoots root);
void RehashBasedOnMap(Isolate* isolate);

// Layout description.
#define HEAP_OBJECT_FIELDS(V) \
Expand Down
2 changes: 2 additions & 0 deletions deps/v8/src/objects/js-collection.h
Expand Up @@ -30,6 +30,7 @@ class JSSet : public TorqueGeneratedJSSet<JSSet, JSCollection> {
public:
static void Initialize(Handle<JSSet> set, Isolate* isolate);
static void Clear(Isolate* isolate, Handle<JSSet> set);
void Rehash(Isolate* isolate);

// Dispatched behavior.
DECL_PRINTER(JSSet)
Expand All @@ -56,6 +57,7 @@ class JSMap : public TorqueGeneratedJSMap<JSMap, JSCollection> {
public:
static void Initialize(Handle<JSMap> map, Isolate* isolate);
static void Clear(Isolate* isolate, Handle<JSMap> map);
void Rehash(Isolate* isolate);

// Dispatched behavior.
DECL_PRINTER(JSMap)
Expand Down
38 changes: 34 additions & 4 deletions deps/v8/src/objects/objects.cc
Expand Up @@ -2305,9 +2305,8 @@ bool HeapObject::NeedsRehashing() const {
case TRANSITION_ARRAY_TYPE:
return TransitionArray::cast(*this).number_of_entries() > 1;
case ORDERED_HASH_MAP_TYPE:
return OrderedHashMap::cast(*this).NumberOfElements() > 0;
case ORDERED_HASH_SET_TYPE:
return OrderedHashSet::cast(*this).NumberOfElements() > 0;
return false; // We'll rehash from the JSMap or JSSet referencing them.
case NAME_DICTIONARY_TYPE:
case GLOBAL_DICTIONARY_TYPE:
case NUMBER_DICTIONARY_TYPE:
Expand All @@ -2317,6 +2316,8 @@ bool HeapObject::NeedsRehashing() const {
case SMALL_ORDERED_HASH_MAP_TYPE:
case SMALL_ORDERED_HASH_SET_TYPE:
case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
case JS_MAP_TYPE:
case JS_SET_TYPE:
return true;
default:
return false;
Expand All @@ -2326,10 +2327,13 @@ bool HeapObject::NeedsRehashing() const {
bool HeapObject::CanBeRehashed() const {
DCHECK(NeedsRehashing());
switch (map().instance_type()) {
case JS_MAP_TYPE:
case JS_SET_TYPE:
return true;
case ORDERED_HASH_MAP_TYPE:
case ORDERED_HASH_SET_TYPE:
UNREACHABLE(); // We'll rehash from the JSMap or JSSet referencing them.
case ORDERED_NAME_DICTIONARY_TYPE:
// TODO(yangguo): actually support rehashing OrderedHash{Map,Set}.
return false;
case NAME_DICTIONARY_TYPE:
case GLOBAL_DICTIONARY_TYPE:
Expand All @@ -2353,7 +2357,8 @@ bool HeapObject::CanBeRehashed() const {
return false;
}

void HeapObject::RehashBasedOnMap(ReadOnlyRoots roots) {
void HeapObject::RehashBasedOnMap(Isolate* isolate) {
ReadOnlyRoots roots = ReadOnlyRoots(isolate);
switch (map().instance_type()) {
case HASH_TABLE_TYPE:
UNREACHABLE();
Expand Down Expand Up @@ -2385,6 +2390,17 @@ void HeapObject::RehashBasedOnMap(ReadOnlyRoots roots) {
case SMALL_ORDERED_HASH_SET_TYPE:
DCHECK_EQ(0, SmallOrderedHashSet::cast(*this).NumberOfElements());
break;
case ORDERED_HASH_MAP_TYPE:
case ORDERED_HASH_SET_TYPE:
UNREACHABLE(); // We'll rehash from the JSMap or JSSet referencing them.
case JS_MAP_TYPE: {
JSMap::cast(*this).Rehash(isolate);
break;
}
case JS_SET_TYPE: {
JSSet::cast(*this).Rehash(isolate);
break;
}
case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
DCHECK_EQ(0, SmallOrderedNameDictionary::cast(*this).NumberOfElements());
break;
Expand Down Expand Up @@ -7864,6 +7880,13 @@ void JSSet::Clear(Isolate* isolate, Handle<JSSet> set) {
set->set_table(*table);
}

void JSSet::Rehash(Isolate* isolate) {
Handle<OrderedHashSet> table_handle(OrderedHashSet::cast(table()), isolate);
Handle<OrderedHashSet> new_table =
OrderedHashSet::Rehash(isolate, table_handle).ToHandleChecked();
set_table(*new_table);
}

void JSMap::Initialize(Handle<JSMap> map, Isolate* isolate) {
Handle<OrderedHashMap> table = isolate->factory()->NewOrderedHashMap();
map->set_table(*table);
Expand All @@ -7875,6 +7898,13 @@ void JSMap::Clear(Isolate* isolate, Handle<JSMap> map) {
map->set_table(*table);
}

void JSMap::Rehash(Isolate* isolate) {
Handle<OrderedHashMap> table_handle(OrderedHashMap::cast(table()), isolate);
Handle<OrderedHashMap> new_table =
OrderedHashMap::Rehash(isolate, table_handle).ToHandleChecked();
set_table(*new_table);
}

void JSWeakCollection::Initialize(Handle<JSWeakCollection> weak_collection,
Isolate* isolate) {
Handle<EphemeronHashTable> table = EphemeronHashTable::New(isolate, 0);
Expand Down
21 changes: 21 additions & 0 deletions deps/v8/src/objects/ordered-hash-table.cc
Expand Up @@ -194,6 +194,13 @@ HeapObject OrderedHashMap::GetEmpty(ReadOnlyRoots ro_roots) {
return ro_roots.empty_ordered_hash_map();
}

template <class Derived, int entrysize>
MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::Rehash(
Isolate* isolate, Handle<Derived> table) {
return OrderedHashTable<Derived, entrysize>::Rehash(isolate, table,
table->Capacity());
}

template <class Derived, int entrysize>
MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::Rehash(
Isolate* isolate, Handle<Derived> table, int new_capacity) {
Expand Down Expand Up @@ -250,6 +257,20 @@ MaybeHandle<OrderedHashSet> OrderedHashSet::Rehash(Isolate* isolate,
new_capacity);
}

MaybeHandle<OrderedHashSet> OrderedHashSet::Rehash(
Isolate* isolate, Handle<OrderedHashSet> table) {
return OrderedHashTable<
OrderedHashSet, OrderedHashSet::kEntrySizeWithoutChain>::Rehash(isolate,
table);
}

MaybeHandle<OrderedHashMap> OrderedHashMap::Rehash(
Isolate* isolate, Handle<OrderedHashMap> table) {
return OrderedHashTable<
OrderedHashMap, OrderedHashMap::kEntrySizeWithoutChain>::Rehash(isolate,
table);
}

MaybeHandle<OrderedHashMap> OrderedHashMap::Rehash(Isolate* isolate,
Handle<OrderedHashMap> table,
int new_capacity) {
Expand Down
7 changes: 7 additions & 0 deletions deps/v8/src/objects/ordered-hash-table.h
Expand Up @@ -138,6 +138,7 @@ class OrderedHashTable : public FixedArray {

// The extra +1 is for linking the bucket chains together.
static const int kEntrySize = entrysize + 1;
static const int kEntrySizeWithoutChain = entrysize;
static const int kChainOffset = entrysize;

static const int kNotFound = -1;
Expand Down Expand Up @@ -200,6 +201,8 @@ class OrderedHashTable : public FixedArray {
static MaybeHandle<Derived> Allocate(
Isolate* isolate, int capacity,
AllocationType allocation = AllocationType::kYoung);

static MaybeHandle<Derived> Rehash(Isolate* isolate, Handle<Derived> table);
static MaybeHandle<Derived> Rehash(Isolate* isolate, Handle<Derived> table,
int new_capacity);

Expand Down Expand Up @@ -244,6 +247,8 @@ class V8_EXPORT_PRIVATE OrderedHashSet
static MaybeHandle<OrderedHashSet> Rehash(Isolate* isolate,
Handle<OrderedHashSet> table,
int new_capacity);
static MaybeHandle<OrderedHashSet> Rehash(Isolate* isolate,
Handle<OrderedHashSet> table);
static MaybeHandle<OrderedHashSet> Allocate(
Isolate* isolate, int capacity,
AllocationType allocation = AllocationType::kYoung);
Expand Down Expand Up @@ -273,6 +278,8 @@ class V8_EXPORT_PRIVATE OrderedHashMap
static MaybeHandle<OrderedHashMap> Rehash(Isolate* isolate,
Handle<OrderedHashMap> table,
int new_capacity);
static MaybeHandle<OrderedHashMap> Rehash(Isolate* isolate,
Handle<OrderedHashMap> table);
Object ValueAt(int entry);

// This takes and returns raw Address values containing tagged Object
Expand Down
16 changes: 14 additions & 2 deletions deps/v8/src/snapshot/deserializer.cc
Expand Up @@ -70,7 +70,7 @@ void Deserializer::Initialize(Isolate* isolate) {
void Deserializer::Rehash() {
DCHECK(can_rehash() || deserializing_user_code());
for (HeapObject item : to_rehash_) {
item.RehashBasedOnMap(ReadOnlyRoots(isolate_));
item.RehashBasedOnMap(isolate_);
}
}

Expand Down Expand Up @@ -130,6 +130,14 @@ void Deserializer::DeserializeDeferredObjects() {
}
}
}

// When the deserialization of maps are deferred, they will be created
// as filler maps, and we postpone the post processing until the maps
// are also deserialized.
for (const auto& pair : fillers_to_post_process_) {
DCHECK(!pair.first.IsFiller());
PostProcessNewObject(pair.first, pair.second);
}
}

void Deserializer::LogNewObjectEvents() {
Expand Down Expand Up @@ -201,7 +209,11 @@ HeapObject Deserializer::PostProcessNewObject(HeapObject obj,
DisallowHeapAllocation no_gc;

if ((FLAG_rehash_snapshot && can_rehash_) || deserializing_user_code()) {
if (obj.IsString()) {
if (obj.IsFiller()) {
DCHECK_EQ(fillers_to_post_process_.find(obj),
fillers_to_post_process_.end());
fillers_to_post_process_.insert({obj, space});
} else if (obj.IsString()) {
// Uninitialize hash field as we need to recompute the hash.
String string = String::cast(obj);
string.set_hash_field(String::kEmptyHashField);
Expand Down
6 changes: 6 additions & 0 deletions deps/v8/src/snapshot/deserializer.h
Expand Up @@ -107,6 +107,7 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
}

std::shared_ptr<BackingStore> backing_store(size_t i) {
DCHECK_LT(i, backing_stores_.size());
return backing_stores_[i];
}

Expand Down Expand Up @@ -193,6 +194,11 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
// TODO(6593): generalize rehashing, and remove this flag.
bool can_rehash_;
std::vector<HeapObject> to_rehash_;
// Store the objects whose maps are deferred and thus initialized as filler
// maps during deserialization, so that they can be processed later when the
// maps become available.
std::unordered_map<HeapObject, SnapshotSpace, Object::Hasher>
fillers_to_post_process_;

#ifdef DEBUG
uint32_t num_api_references_;
Expand Down
2 changes: 1 addition & 1 deletion deps/v8/src/snapshot/object-deserializer.cc
Expand Up @@ -48,9 +48,9 @@ MaybeHandle<HeapObject> ObjectDeserializer::Deserialize(Isolate* isolate) {
LinkAllocationSites();
LogNewMapEvents();
result = handle(HeapObject::cast(root), isolate);
Rehash();
allocator()->RegisterDeserializedObjectsForBlackAllocation();
}
Rehash();
CommitPostProcessedObjects();
return scope.CloseAndEscape(result);
}
Expand Down
2 changes: 1 addition & 1 deletion deps/v8/src/snapshot/partial-deserializer.cc
Expand Up @@ -57,12 +57,12 @@ MaybeHandle<Object> PartialDeserializer::Deserialize(
// new code, which also has to be flushed from instruction cache.
CHECK_EQ(start_address, code_space->top());

if (FLAG_rehash_snapshot && can_rehash()) Rehash();
LogNewMapEvents();

result = handle(root, isolate);
}

if (FLAG_rehash_snapshot && can_rehash()) Rehash();
SetupOffHeapArrayBufferBackingStores();

return result;
Expand Down
12 changes: 10 additions & 2 deletions deps/v8/src/snapshot/serializer-common.cc
Expand Up @@ -76,7 +76,8 @@ ExternalReferenceEncoder::Value ExternalReferenceEncoder::Encode(
if (maybe_index.IsNothing()) {
void* addr = reinterpret_cast<void*>(address);
v8::base::OS::PrintError("Unknown external reference %p.\n", addr);
v8::base::OS::PrintError("%s", ExternalReferenceTable::ResolveSymbol(addr));
v8::base::OS::PrintError("%s\n",
ExternalReferenceTable::ResolveSymbol(addr));
v8::base::OS::Abort();
}
Value result(maybe_index.FromJust());
Expand Down Expand Up @@ -125,7 +126,14 @@ void SerializerDeserializer::Iterate(Isolate* isolate, RootVisitor* visitor) {
}

bool SerializerDeserializer::CanBeDeferred(HeapObject o) {
return !o.IsString() && !o.IsScript() && !o.IsJSTypedArray();
// ArrayBuffer instances are serialized by first re-assigning a index
// to the backing store field, then serializing the object, and then
// storing the actual backing store address again (and the same for the
// ArrayBufferExtension). If serialization of the object itself is deferred,
// the real backing store address is written into the snapshot, which cannot
// be processed when deserializing.
return !o.IsString() && !o.IsScript() && !o.IsJSTypedArray() &&
!o.IsJSArrayBuffer();
}

void SerializerDeserializer::RestoreExternalReferenceRedirectors(
Expand Down
1 change: 1 addition & 0 deletions deps/v8/src/snapshot/startup-serializer.cc
Expand Up @@ -193,6 +193,7 @@ void SerializedHandleChecker::VisitRootPointers(Root root,
PrintF("%s handle not serialized: ",
root == Root::kGlobalHandles ? "global" : "eternal");
(*p).Print();
PrintF("\n");
ok_ = false;
}
}
Expand Down
17 changes: 12 additions & 5 deletions deps/v8/test/cctest/test-serialize.cc
Expand Up @@ -3778,7 +3778,7 @@ UNINITIALIZED_TEST(SnapshotCreatorIncludeGlobalProxy) {
FreeCurrentEmbeddedBlob();
}

UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) {
UNINITIALIZED_TEST(ReinitializeHashSeedJSCollectionRehashable) {
DisableAlwaysOpt();
i::FLAG_rehash_snapshot = true;
i::FLAG_hash_seed = 42;
Expand All @@ -3796,13 +3796,18 @@ UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) {
CompileRun(
"var m = new Map();"
"m.set('a', 1);"
"m.set('b', 2);");
"m.set('b', 2);"
"var s = new Set();"
"s.add(1);"
"s.add(globalThis);");
ExpectInt32("m.get('b')", 2);
ExpectTrue("s.has(1)");
ExpectTrue("s.has(globalThis)");
creator.SetDefaultContext(context);
}
blob =
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
CHECK(!blob.CanBeRehashed());
CHECK(blob.CanBeRehashed());
}

ReadOnlyHeap::ClearSharedHeapForTest();
Expand All @@ -3812,15 +3817,17 @@ UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) {
create_params.snapshot_blob = &blob;
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
// Check that no rehashing has been performed.
CHECK_EQ(static_cast<uint64_t>(42),
// Check that rehashing has been performed.
CHECK_EQ(static_cast<uint64_t>(1337),
HashSeed(reinterpret_cast<i::Isolate*>(isolate)));
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
CHECK(!context.IsEmpty());
v8::Context::Scope context_scope(context);
ExpectInt32("m.get('b')", 2);
ExpectTrue("s.has(1)");
ExpectTrue("s.has(globalThis)");
}
isolate->Dispose();
delete[] blob.data;
Expand Down