Skip to content

Commit 9baf286

Browse files
committed
[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
1 parent 679945f commit 9baf286

10 files changed

Lines changed: 96 additions & 14 deletions

src/objects/heap-object.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ class HeapObject : public Object {
191191
bool CanBeRehashed() const;
192192

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

196196
// Layout description.
197197
#define HEAP_OBJECT_FIELDS(V) \

src/objects/js-collection.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class JSSet : public TorqueGeneratedJSSet<JSSet, JSCollection> {
3030
public:
3131
static void Initialize(Handle<JSSet> set, Isolate* isolate);
3232
static void Clear(Isolate* isolate, Handle<JSSet> set);
33+
void Rehash(Isolate* isolate);
3334

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

6062
// Dispatched behavior.
6163
DECL_PRINTER(JSMap)

src/objects/objects.cc

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2305,9 +2305,8 @@ bool HeapObject::NeedsRehashing() const {
23052305
case TRANSITION_ARRAY_TYPE:
23062306
return TransitionArray::cast(*this).number_of_entries() > 1;
23072307
case ORDERED_HASH_MAP_TYPE:
2308-
return OrderedHashMap::cast(*this).NumberOfElements() > 0;
23092308
case ORDERED_HASH_SET_TYPE:
2310-
return OrderedHashSet::cast(*this).NumberOfElements() > 0;
2309+
return false; // We'll rehash from the JSMap or JSSet referencing them.
23112310
case NAME_DICTIONARY_TYPE:
23122311
case GLOBAL_DICTIONARY_TYPE:
23132312
case NUMBER_DICTIONARY_TYPE:
@@ -2317,6 +2316,8 @@ bool HeapObject::NeedsRehashing() const {
23172316
case SMALL_ORDERED_HASH_MAP_TYPE:
23182317
case SMALL_ORDERED_HASH_SET_TYPE:
23192318
case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
2319+
case JS_MAP_TYPE:
2320+
case JS_SET_TYPE:
23202321
return true;
23212322
default:
23222323
return false;
@@ -2326,10 +2327,13 @@ bool HeapObject::NeedsRehashing() const {
23262327
bool HeapObject::CanBeRehashed() const {
23272328
DCHECK(NeedsRehashing());
23282329
switch (map().instance_type()) {
2330+
case JS_MAP_TYPE:
2331+
case JS_SET_TYPE:
2332+
return true;
23292333
case ORDERED_HASH_MAP_TYPE:
23302334
case ORDERED_HASH_SET_TYPE:
2335+
UNREACHABLE(); // We'll rehash from the JSMap or JSSet referencing them.
23312336
case ORDERED_NAME_DICTIONARY_TYPE:
2332-
// TODO(yangguo): actually support rehashing OrderedHash{Map,Set}.
23332337
return false;
23342338
case NAME_DICTIONARY_TYPE:
23352339
case GLOBAL_DICTIONARY_TYPE:
@@ -2353,7 +2357,8 @@ bool HeapObject::CanBeRehashed() const {
23532357
return false;
23542358
}
23552359

2356-
void HeapObject::RehashBasedOnMap(ReadOnlyRoots roots) {
2360+
void HeapObject::RehashBasedOnMap(Isolate* isolate) {
2361+
ReadOnlyRoots roots = ReadOnlyRoots(isolate);
23572362
switch (map().instance_type()) {
23582363
case HASH_TABLE_TYPE:
23592364
UNREACHABLE();
@@ -2385,6 +2390,17 @@ void HeapObject::RehashBasedOnMap(ReadOnlyRoots roots) {
23852390
case SMALL_ORDERED_HASH_SET_TYPE:
23862391
DCHECK_EQ(0, SmallOrderedHashSet::cast(*this).NumberOfElements());
23872392
break;
2393+
case ORDERED_HASH_MAP_TYPE:
2394+
case ORDERED_HASH_SET_TYPE:
2395+
UNREACHABLE(); // We'll rehash from the JSMap or JSSet referencing them.
2396+
case JS_MAP_TYPE: {
2397+
JSMap::cast(*this).Rehash(isolate);
2398+
break;
2399+
}
2400+
case JS_SET_TYPE: {
2401+
JSSet::cast(*this).Rehash(isolate);
2402+
break;
2403+
}
23882404
case SMALL_ORDERED_NAME_DICTIONARY_TYPE:
23892405
DCHECK_EQ(0, SmallOrderedNameDictionary::cast(*this).NumberOfElements());
23902406
break;
@@ -7852,6 +7868,13 @@ void JSSet::Clear(Isolate* isolate, Handle<JSSet> set) {
78527868
set->set_table(*table);
78537869
}
78547870

7871+
void JSSet::Rehash(Isolate* isolate) {
7872+
Handle<OrderedHashSet> table_handle(OrderedHashSet::cast(table()), isolate);
7873+
Handle<OrderedHashSet> new_table =
7874+
OrderedHashSet::Rehash(isolate, table_handle).ToHandleChecked();
7875+
set_table(*new_table);
7876+
}
7877+
78557878
void JSMap::Initialize(Handle<JSMap> map, Isolate* isolate) {
78567879
Handle<OrderedHashMap> table = isolate->factory()->NewOrderedHashMap();
78577880
map->set_table(*table);
@@ -7863,6 +7886,13 @@ void JSMap::Clear(Isolate* isolate, Handle<JSMap> map) {
78637886
map->set_table(*table);
78647887
}
78657888

7889+
void JSMap::Rehash(Isolate* isolate) {
7890+
Handle<OrderedHashMap> table_handle(OrderedHashMap::cast(table()), isolate);
7891+
Handle<OrderedHashMap> new_table =
7892+
OrderedHashMap::Rehash(isolate, table_handle).ToHandleChecked();
7893+
set_table(*new_table);
7894+
}
7895+
78667896
void JSWeakCollection::Initialize(Handle<JSWeakCollection> weak_collection,
78677897
Isolate* isolate) {
78687898
Handle<EphemeronHashTable> table = EphemeronHashTable::New(isolate, 0);

src/objects/ordered-hash-table.cc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,13 @@ HeapObject OrderedHashMap::GetEmpty(ReadOnlyRoots ro_roots) {
194194
return ro_roots.empty_ordered_hash_map();
195195
}
196196

197+
template <class Derived, int entrysize>
198+
MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::Rehash(
199+
Isolate* isolate, Handle<Derived> table) {
200+
return OrderedHashTable<Derived, entrysize>::Rehash(isolate, table,
201+
table->Capacity());
202+
}
203+
197204
template <class Derived, int entrysize>
198205
MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::Rehash(
199206
Isolate* isolate, Handle<Derived> table, int new_capacity) {
@@ -250,6 +257,20 @@ MaybeHandle<OrderedHashSet> OrderedHashSet::Rehash(Isolate* isolate,
250257
new_capacity);
251258
}
252259

260+
MaybeHandle<OrderedHashSet> OrderedHashSet::Rehash(
261+
Isolate* isolate, Handle<OrderedHashSet> table) {
262+
return OrderedHashTable<
263+
OrderedHashSet, OrderedHashSet::kEntrySizeWithoutChain>::Rehash(isolate,
264+
table);
265+
}
266+
267+
MaybeHandle<OrderedHashMap> OrderedHashMap::Rehash(
268+
Isolate* isolate, Handle<OrderedHashMap> table) {
269+
return OrderedHashTable<
270+
OrderedHashMap, OrderedHashMap::kEntrySizeWithoutChain>::Rehash(isolate,
271+
table);
272+
}
273+
253274
MaybeHandle<OrderedHashMap> OrderedHashMap::Rehash(Isolate* isolate,
254275
Handle<OrderedHashMap> table,
255276
int new_capacity) {

src/objects/ordered-hash-table.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ class OrderedHashTable : public FixedArray {
138138

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

143144
static const int kNotFound = -1;
@@ -200,6 +201,8 @@ class OrderedHashTable : public FixedArray {
200201
static MaybeHandle<Derived> Allocate(
201202
Isolate* isolate, int capacity,
202203
AllocationType allocation = AllocationType::kYoung);
204+
205+
static MaybeHandle<Derived> Rehash(Isolate* isolate, Handle<Derived> table);
203206
static MaybeHandle<Derived> Rehash(Isolate* isolate, Handle<Derived> table,
204207
int new_capacity);
205208

@@ -244,6 +247,8 @@ class V8_EXPORT_PRIVATE OrderedHashSet
244247
static MaybeHandle<OrderedHashSet> Rehash(Isolate* isolate,
245248
Handle<OrderedHashSet> table,
246249
int new_capacity);
250+
static MaybeHandle<OrderedHashSet> Rehash(Isolate* isolate,
251+
Handle<OrderedHashSet> table);
247252
static MaybeHandle<OrderedHashSet> Allocate(
248253
Isolate* isolate, int capacity,
249254
AllocationType allocation = AllocationType::kYoung);
@@ -273,6 +278,8 @@ class V8_EXPORT_PRIVATE OrderedHashMap
273278
static MaybeHandle<OrderedHashMap> Rehash(Isolate* isolate,
274279
Handle<OrderedHashMap> table,
275280
int new_capacity);
281+
static MaybeHandle<OrderedHashMap> Rehash(Isolate* isolate,
282+
Handle<OrderedHashMap> table);
276283
Object ValueAt(int entry);
277284

278285
// This takes and returns raw Address values containing tagged Object

src/snapshot/context-deserializer.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ MaybeHandle<Object> ContextDeserializer::Deserialize(
5656
// new code, which also has to be flushed from instruction cache.
5757
CHECK_EQ(start_address, code_space->top());
5858

59-
if (FLAG_rehash_snapshot && can_rehash()) Rehash();
6059
LogNewMapEvents();
6160

6261
result = handle(root, isolate);
6362
}
6463

64+
if (FLAG_rehash_snapshot && can_rehash()) Rehash();
6565
SetupOffHeapArrayBufferBackingStores();
6666

6767
return result;

src/snapshot/deserializer.cc

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ void Deserializer::Initialize(Isolate* isolate) {
8282
void Deserializer::Rehash() {
8383
DCHECK(can_rehash() || deserializing_user_code());
8484
for (HeapObject item : to_rehash_) {
85-
item.RehashBasedOnMap(ReadOnlyRoots(isolate_));
85+
item.RehashBasedOnMap(isolate_);
8686
}
8787
}
8888

@@ -142,6 +142,14 @@ void Deserializer::DeserializeDeferredObjects() {
142142
}
143143
}
144144
}
145+
146+
// When the deserialization of maps are deferred, they will be created
147+
// as filler maps, and we postpone the post processing until the maps
148+
// are also deserialized.
149+
for (const auto& pair : fillers_to_post_process_) {
150+
DCHECK(!pair.first.IsFiller());
151+
PostProcessNewObject(pair.first, pair.second);
152+
}
145153
}
146154

147155
void Deserializer::LogNewObjectEvents() {
@@ -213,7 +221,11 @@ HeapObject Deserializer::PostProcessNewObject(HeapObject obj,
213221
DisallowHeapAllocation no_gc;
214222

215223
if ((FLAG_rehash_snapshot && can_rehash_) || deserializing_user_code()) {
216-
if (obj.IsString()) {
224+
if (obj.IsFiller()) {
225+
DCHECK_EQ(fillers_to_post_process_.find(obj),
226+
fillers_to_post_process_.end());
227+
fillers_to_post_process_.insert({obj, space});
228+
} else if (obj.IsString()) {
217229
// Uninitialize hash field as we need to recompute the hash.
218230
String string = String::cast(obj);
219231
string.set_hash_field(String::kEmptyHashField);

src/snapshot/deserializer.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,11 @@ class V8_EXPORT_PRIVATE Deserializer : public SerializerDeserializer {
197197
// TODO(6593): generalize rehashing, and remove this flag.
198198
bool can_rehash_;
199199
std::vector<HeapObject> to_rehash_;
200+
// Store the objects whose maps are deferred and thus initialized as filler
201+
// maps during deserialization, so that they can be processed later when the
202+
// maps become available.
203+
std::unordered_map<HeapObject, SnapshotSpace, Object::Hasher>
204+
fillers_to_post_process_;
200205

201206
#ifdef DEBUG
202207
uint32_t num_api_references_;

src/snapshot/object-deserializer.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,10 @@ MaybeHandle<HeapObject> ObjectDeserializer::Deserialize(Isolate* isolate) {
4747
LinkAllocationSites();
4848
LogNewMapEvents();
4949
result = handle(HeapObject::cast(root), isolate);
50-
Rehash();
5150
allocator()->RegisterDeserializedObjectsForBlackAllocation();
5251
}
52+
53+
Rehash();
5354
CommitPostProcessedObjects();
5455
return scope.CloseAndEscape(result);
5556
}

test/cctest/test-serialize.cc

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3752,7 +3752,7 @@ UNINITIALIZED_TEST(SnapshotCreatorIncludeGlobalProxy) {
37523752
FreeCurrentEmbeddedBlob();
37533753
}
37543754

3755-
UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) {
3755+
UNINITIALIZED_TEST(ReinitializeHashSeedJSCollectionRehashable) {
37563756
DisableAlwaysOpt();
37573757
i::FLAG_rehash_snapshot = true;
37583758
i::FLAG_hash_seed = 42;
@@ -3770,13 +3770,16 @@ UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) {
37703770
CompileRun(
37713771
"var m = new Map();"
37723772
"m.set('a', 1);"
3773-
"m.set('b', 2);");
3773+
"m.set('b', 2);"
3774+
"var s = new Set();"
3775+
"s.add(1)");
37743776
ExpectInt32("m.get('b')", 2);
3777+
ExpectTrue("s.has(1)");
37753778
creator.SetDefaultContext(context);
37763779
}
37773780
blob =
37783781
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
3779-
CHECK(!blob.CanBeRehashed());
3782+
CHECK(blob.CanBeRehashed());
37803783
}
37813784

37823785
i::FLAG_hash_seed = 1337;
@@ -3785,15 +3788,16 @@ UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) {
37853788
create_params.snapshot_blob = &blob;
37863789
v8::Isolate* isolate = v8::Isolate::New(create_params);
37873790
{
3788-
// Check that no rehashing has been performed.
3789-
CHECK_EQ(static_cast<uint64_t>(42),
3791+
// Check that rehashing has been performed.
3792+
CHECK_EQ(static_cast<uint64_t>(1337),
37903793
HashSeed(reinterpret_cast<i::Isolate*>(isolate)));
37913794
v8::Isolate::Scope isolate_scope(isolate);
37923795
v8::HandleScope handle_scope(isolate);
37933796
v8::Local<v8::Context> context = v8::Context::New(isolate);
37943797
CHECK(!context.IsEmpty());
37953798
v8::Context::Scope context_scope(context);
37963799
ExpectInt32("m.get('b')", 2);
3800+
ExpectTrue("s.has(1)");
37973801
}
37983802
isolate->Dispose();
37993803
delete[] blob.data;

0 commit comments

Comments
 (0)