Commit 22014de0 authored by Joyee Cheung's avatar Joyee Cheung Committed by Commit Bot

Reland "[snapshot] rehash JSMap and JSSet during deserialization"

This is a reland of 8374feed.

Fixed rehashing of global proxy keys by creating its identity hash
early, before the deserialization of the context snapshot.

Original change's description:
> [snapshot] rehash JSMap and JSSet during deserialization
>
> To rehash JSMap and JSSet, we simply replace the backing store
> with a new one created with the new hash.
>
> Bug: v8:9187
> Change-Id: I90c25b18b33b7bc2b6ffe1b89fe17aa5f978b517
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2143983
> Commit-Queue: Joyee Cheung <joyee@igalia.com>
> Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> Reviewed-by: Camillo Bruni <cbruni@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#67663}

Bug: v8:9187, v8:10523
Change-Id: I7a0319b1d10ff07644de902fec43e7c2b1dd8da9
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2212085Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/master@{#67999}
parent 5df2f65d
...@@ -2819,8 +2819,12 @@ Handle<JSGlobalProxy> Factory::NewUninitializedJSGlobalProxy(int size) { ...@@ -2819,8 +2819,12 @@ Handle<JSGlobalProxy> Factory::NewUninitializedJSGlobalProxy(int size) {
map->set_is_access_check_needed(true); map->set_is_access_check_needed(true);
map->set_may_have_interesting_symbols(true); map->set_may_have_interesting_symbols(true);
LOG(isolate(), MapDetails(*map)); LOG(isolate(), MapDetails(*map));
return Handle<JSGlobalProxy>::cast( Handle<JSGlobalProxy> proxy = Handle<JSGlobalProxy>::cast(
NewJSObjectFromMap(map, AllocationType::kYoung)); 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, void Factory::ReinitializeJSGlobalProxy(Handle<JSGlobalProxy> object,
......
...@@ -30,6 +30,7 @@ class JSSet : public TorqueGeneratedJSSet<JSSet, JSCollection> { ...@@ -30,6 +30,7 @@ class JSSet : public TorqueGeneratedJSSet<JSSet, JSCollection> {
public: public:
static void Initialize(Handle<JSSet> set, Isolate* isolate); static void Initialize(Handle<JSSet> set, Isolate* isolate);
static void Clear(Isolate* isolate, Handle<JSSet> set); static void Clear(Isolate* isolate, Handle<JSSet> set);
void Rehash(Isolate* isolate);
// Dispatched behavior. // Dispatched behavior.
DECL_PRINTER(JSSet) DECL_PRINTER(JSSet)
...@@ -56,6 +57,7 @@ class JSMap : public TorqueGeneratedJSMap<JSMap, JSCollection> { ...@@ -56,6 +57,7 @@ class JSMap : public TorqueGeneratedJSMap<JSMap, JSCollection> {
public: public:
static void Initialize(Handle<JSMap> map, Isolate* isolate); static void Initialize(Handle<JSMap> map, Isolate* isolate);
static void Clear(Isolate* isolate, Handle<JSMap> map); static void Clear(Isolate* isolate, Handle<JSMap> map);
void Rehash(Isolate* isolate);
// Dispatched behavior. // Dispatched behavior.
DECL_PRINTER(JSMap) DECL_PRINTER(JSMap)
......
...@@ -2306,9 +2306,8 @@ bool HeapObject::NeedsRehashing() const { ...@@ -2306,9 +2306,8 @@ bool HeapObject::NeedsRehashing() const {
case TRANSITION_ARRAY_TYPE: case TRANSITION_ARRAY_TYPE:
return TransitionArray::cast(*this).number_of_entries() > 1; return TransitionArray::cast(*this).number_of_entries() > 1;
case ORDERED_HASH_MAP_TYPE: case ORDERED_HASH_MAP_TYPE:
return OrderedHashMap::cast(*this).NumberOfElements() > 0;
case ORDERED_HASH_SET_TYPE: 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 NAME_DICTIONARY_TYPE:
case GLOBAL_DICTIONARY_TYPE: case GLOBAL_DICTIONARY_TYPE:
case NUMBER_DICTIONARY_TYPE: case NUMBER_DICTIONARY_TYPE:
...@@ -2318,6 +2317,8 @@ bool HeapObject::NeedsRehashing() const { ...@@ -2318,6 +2317,8 @@ bool HeapObject::NeedsRehashing() const {
case SMALL_ORDERED_HASH_MAP_TYPE: case SMALL_ORDERED_HASH_MAP_TYPE:
case SMALL_ORDERED_HASH_SET_TYPE: case SMALL_ORDERED_HASH_SET_TYPE:
case SMALL_ORDERED_NAME_DICTIONARY_TYPE: case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
case JS_MAP_TYPE:
case JS_SET_TYPE:
return true; return true;
default: default:
return false; return false;
...@@ -2327,10 +2328,13 @@ bool HeapObject::NeedsRehashing() const { ...@@ -2327,10 +2328,13 @@ bool HeapObject::NeedsRehashing() const {
bool HeapObject::CanBeRehashed() const { bool HeapObject::CanBeRehashed() const {
DCHECK(NeedsRehashing()); DCHECK(NeedsRehashing());
switch (map().instance_type()) { switch (map().instance_type()) {
case JS_MAP_TYPE:
case JS_SET_TYPE:
return true;
case ORDERED_HASH_MAP_TYPE: case ORDERED_HASH_MAP_TYPE:
case ORDERED_HASH_SET_TYPE: case ORDERED_HASH_SET_TYPE:
UNREACHABLE(); // We'll rehash from the JSMap or JSSet referencing them.
case ORDERED_NAME_DICTIONARY_TYPE: case ORDERED_NAME_DICTIONARY_TYPE:
// TODO(yangguo): actually support rehashing OrderedHash{Map,Set}.
return false; return false;
case NAME_DICTIONARY_TYPE: case NAME_DICTIONARY_TYPE:
case GLOBAL_DICTIONARY_TYPE: case GLOBAL_DICTIONARY_TYPE:
...@@ -2387,6 +2391,19 @@ void HeapObject::RehashBasedOnMap(LocalIsolateWrapper isolate) { ...@@ -2387,6 +2391,19 @@ void HeapObject::RehashBasedOnMap(LocalIsolateWrapper isolate) {
case SMALL_ORDERED_HASH_SET_TYPE: case SMALL_ORDERED_HASH_SET_TYPE:
DCHECK_EQ(0, SmallOrderedHashSet::cast(*this).NumberOfElements()); DCHECK_EQ(0, SmallOrderedHashSet::cast(*this).NumberOfElements());
break; 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: {
DCHECK(isolate.is_main_thread());
JSMap::cast(*this).Rehash(isolate.main_thread());
break;
}
case JS_SET_TYPE: {
DCHECK(isolate.is_main_thread());
JSSet::cast(*this).Rehash(isolate.main_thread());
break;
}
case SMALL_ORDERED_NAME_DICTIONARY_TYPE: case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
DCHECK_EQ(0, SmallOrderedNameDictionary::cast(*this).NumberOfElements()); DCHECK_EQ(0, SmallOrderedNameDictionary::cast(*this).NumberOfElements());
break; break;
...@@ -7863,6 +7880,13 @@ void JSSet::Clear(Isolate* isolate, Handle<JSSet> set) { ...@@ -7863,6 +7880,13 @@ void JSSet::Clear(Isolate* isolate, Handle<JSSet> set) {
set->set_table(*table); 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) { void JSMap::Initialize(Handle<JSMap> map, Isolate* isolate) {
Handle<OrderedHashMap> table = isolate->factory()->NewOrderedHashMap(); Handle<OrderedHashMap> table = isolate->factory()->NewOrderedHashMap();
map->set_table(*table); map->set_table(*table);
...@@ -7874,6 +7898,13 @@ void JSMap::Clear(Isolate* isolate, Handle<JSMap> map) { ...@@ -7874,6 +7898,13 @@ void JSMap::Clear(Isolate* isolate, Handle<JSMap> map) {
map->set_table(*table); 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, void JSWeakCollection::Initialize(Handle<JSWeakCollection> weak_collection,
Isolate* isolate) { Isolate* isolate) {
Handle<EphemeronHashTable> table = EphemeronHashTable::New(isolate, 0); Handle<EphemeronHashTable> table = EphemeronHashTable::New(isolate, 0);
......
...@@ -194,6 +194,13 @@ HeapObject OrderedHashMap::GetEmpty(ReadOnlyRoots ro_roots) { ...@@ -194,6 +194,13 @@ HeapObject OrderedHashMap::GetEmpty(ReadOnlyRoots ro_roots) {
return ro_roots.empty_ordered_hash_map(); 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> template <class Derived, int entrysize>
MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::Rehash( MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::Rehash(
Isolate* isolate, Handle<Derived> table, int new_capacity) { Isolate* isolate, Handle<Derived> table, int new_capacity) {
...@@ -250,6 +257,20 @@ MaybeHandle<OrderedHashSet> OrderedHashSet::Rehash(Isolate* isolate, ...@@ -250,6 +257,20 @@ MaybeHandle<OrderedHashSet> OrderedHashSet::Rehash(Isolate* isolate,
new_capacity); 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, MaybeHandle<OrderedHashMap> OrderedHashMap::Rehash(Isolate* isolate,
Handle<OrderedHashMap> table, Handle<OrderedHashMap> table,
int new_capacity) { int new_capacity) {
......
...@@ -138,6 +138,7 @@ class OrderedHashTable : public FixedArray { ...@@ -138,6 +138,7 @@ class OrderedHashTable : public FixedArray {
// The extra +1 is for linking the bucket chains together. // The extra +1 is for linking the bucket chains together.
static const int kEntrySize = entrysize + 1; static const int kEntrySize = entrysize + 1;
static const int kEntrySizeWithoutChain = entrysize;
static const int kChainOffset = entrysize; static const int kChainOffset = entrysize;
static const int kNotFound = -1; static const int kNotFound = -1;
...@@ -200,6 +201,8 @@ class OrderedHashTable : public FixedArray { ...@@ -200,6 +201,8 @@ class OrderedHashTable : public FixedArray {
static MaybeHandle<Derived> Allocate( static MaybeHandle<Derived> Allocate(
Isolate* isolate, int capacity, Isolate* isolate, int capacity,
AllocationType allocation = AllocationType::kYoung); AllocationType allocation = AllocationType::kYoung);
static MaybeHandle<Derived> Rehash(Isolate* isolate, Handle<Derived> table);
static MaybeHandle<Derived> Rehash(Isolate* isolate, Handle<Derived> table, static MaybeHandle<Derived> Rehash(Isolate* isolate, Handle<Derived> table,
int new_capacity); int new_capacity);
...@@ -244,6 +247,8 @@ class V8_EXPORT_PRIVATE OrderedHashSet ...@@ -244,6 +247,8 @@ class V8_EXPORT_PRIVATE OrderedHashSet
static MaybeHandle<OrderedHashSet> Rehash(Isolate* isolate, static MaybeHandle<OrderedHashSet> Rehash(Isolate* isolate,
Handle<OrderedHashSet> table, Handle<OrderedHashSet> table,
int new_capacity); int new_capacity);
static MaybeHandle<OrderedHashSet> Rehash(Isolate* isolate,
Handle<OrderedHashSet> table);
static MaybeHandle<OrderedHashSet> Allocate( static MaybeHandle<OrderedHashSet> Allocate(
Isolate* isolate, int capacity, Isolate* isolate, int capacity,
AllocationType allocation = AllocationType::kYoung); AllocationType allocation = AllocationType::kYoung);
...@@ -273,6 +278,8 @@ class V8_EXPORT_PRIVATE OrderedHashMap ...@@ -273,6 +278,8 @@ class V8_EXPORT_PRIVATE OrderedHashMap
static MaybeHandle<OrderedHashMap> Rehash(Isolate* isolate, static MaybeHandle<OrderedHashMap> Rehash(Isolate* isolate,
Handle<OrderedHashMap> table, Handle<OrderedHashMap> table,
int new_capacity); int new_capacity);
static MaybeHandle<OrderedHashMap> Rehash(Isolate* isolate,
Handle<OrderedHashMap> table);
Object ValueAt(int entry); Object ValueAt(int entry);
// This takes and returns raw Address values containing tagged Object // This takes and returns raw Address values containing tagged Object
......
...@@ -59,12 +59,12 @@ MaybeHandle<Object> ContextDeserializer::Deserialize( ...@@ -59,12 +59,12 @@ MaybeHandle<Object> ContextDeserializer::Deserialize(
// new code, which also has to be flushed from instruction cache. // new code, which also has to be flushed from instruction cache.
CHECK_EQ(start_address, code_space->top()); CHECK_EQ(start_address, code_space->top());
if (FLAG_rehash_snapshot && can_rehash()) Rehash();
LogNewMapEvents(); LogNewMapEvents();
result = handle(root, isolate); result = handle(root, isolate);
} }
if (FLAG_rehash_snapshot && can_rehash()) Rehash();
SetupOffHeapArrayBufferBackingStores(); SetupOffHeapArrayBufferBackingStores();
return result; return result;
......
...@@ -147,6 +147,14 @@ void Deserializer::DeserializeDeferredObjects() { ...@@ -147,6 +147,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::LogNewMapEvents() { void Deserializer::LogNewMapEvents() {
...@@ -208,7 +216,11 @@ HeapObject Deserializer::PostProcessNewObject(HeapObject obj, ...@@ -208,7 +216,11 @@ HeapObject Deserializer::PostProcessNewObject(HeapObject obj,
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
if ((FLAG_rehash_snapshot && can_rehash_) || deserializing_user_code()) { 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. // Uninitialize hash field as we need to recompute the hash.
String string = String::cast(obj); String string = String::cast(obj);
string.set_hash_field(String::kEmptyHashField); string.set_hash_field(String::kEmptyHashField);
......
...@@ -204,6 +204,11 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer { ...@@ -204,6 +204,11 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
// TODO(6593): generalize rehashing, and remove this flag. // TODO(6593): generalize rehashing, and remove this flag.
bool can_rehash_; bool can_rehash_;
std::vector<HeapObject> to_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 #ifdef DEBUG
uint32_t num_api_references_; uint32_t num_api_references_;
......
...@@ -67,11 +67,12 @@ MaybeHandle<HeapObject> ObjectDeserializer::Deserialize( ...@@ -67,11 +67,12 @@ MaybeHandle<HeapObject> ObjectDeserializer::Deserialize(
LogNewMapEvents(); LogNewMapEvents();
} }
result = handle(HeapObject::cast(root), local_isolate); result = handle(HeapObject::cast(root), local_isolate);
Rehash();
if (is_main_thread()) { if (is_main_thread()) {
allocator()->RegisterDeserializedObjectsForBlackAllocation(); allocator()->RegisterDeserializedObjectsForBlackAllocation();
} }
} }
Rehash();
CommitPostProcessedObjects(); CommitPostProcessedObjects();
return scope.CloseAndEscape(result); return scope.CloseAndEscape(result);
} }
......
...@@ -3755,7 +3755,7 @@ UNINITIALIZED_TEST(SnapshotCreatorIncludeGlobalProxy) { ...@@ -3755,7 +3755,7 @@ UNINITIALIZED_TEST(SnapshotCreatorIncludeGlobalProxy) {
FreeCurrentEmbeddedBlob(); FreeCurrentEmbeddedBlob();
} }
UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) { UNINITIALIZED_TEST(ReinitializeHashSeedJSCollectionRehashable) {
DisableAlwaysOpt(); DisableAlwaysOpt();
i::FLAG_rehash_snapshot = true; i::FLAG_rehash_snapshot = true;
i::FLAG_hash_seed = 42; i::FLAG_hash_seed = 42;
...@@ -3773,13 +3773,18 @@ UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) { ...@@ -3773,13 +3773,18 @@ UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) {
CompileRun( CompileRun(
"var m = new Map();" "var m = new Map();"
"m.set('a', 1);" "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); ExpectInt32("m.get('b')", 2);
ExpectTrue("s.has(1)");
ExpectTrue("s.has(globalThis)");
creator.SetDefaultContext(context); creator.SetDefaultContext(context);
} }
blob = blob =
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear); creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
CHECK(!blob.CanBeRehashed()); CHECK(blob.CanBeRehashed());
} }
i::FLAG_hash_seed = 1337; i::FLAG_hash_seed = 1337;
...@@ -3788,8 +3793,8 @@ UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) { ...@@ -3788,8 +3793,8 @@ UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) {
create_params.snapshot_blob = &blob; create_params.snapshot_blob = &blob;
v8::Isolate* isolate = v8::Isolate::New(create_params); v8::Isolate* isolate = v8::Isolate::New(create_params);
{ {
// Check that no rehashing has been performed. // Check that rehashing has been performed.
CHECK_EQ(static_cast<uint64_t>(42), CHECK_EQ(static_cast<uint64_t>(1337),
HashSeed(reinterpret_cast<i::Isolate*>(isolate))); HashSeed(reinterpret_cast<i::Isolate*>(isolate)));
v8::Isolate::Scope isolate_scope(isolate); v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
...@@ -3797,6 +3802,8 @@ UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) { ...@@ -3797,6 +3802,8 @@ UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) {
CHECK(!context.IsEmpty()); CHECK(!context.IsEmpty());
v8::Context::Scope context_scope(context); v8::Context::Scope context_scope(context);
ExpectInt32("m.get('b')", 2); ExpectInt32("m.get('b')", 2);
ExpectTrue("s.has(1)");
ExpectTrue("s.has(globalThis)");
} }
isolate->Dispose(); isolate->Dispose();
delete[] blob.data; delete[] blob.data;
......
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