Skip to content

Commit e2b306c

Browse files
hashseedaddaleax
authored andcommitted
deps: backport rehash strings after deserialization
Original commit messages: v8/v8@a2ab135 [snapshot] Rehash strings after deserialization. See https://goo.gl/6aN8xA Bug: v8:6593 Change-Id: Ic8b0b57195d01d41591397d5d45de3f0f3ebc3d9 Reviewed-on: https://chromium-review.googlesource.com/574527 Reviewed-by: Camillo Bruni <[email protected]> Reviewed-by: Jakob Gruber <[email protected]> Reviewed-by: Ulan Degenbaev <[email protected]> Commit-Queue: Yang Guo <[email protected]> Cr-Commit-Position: refs/heads/master@{#46732} v8/v8@182caaf Do not track transitions for built-in objects. Objects created during bootstrapping do not need a transition tree except for elements kind transitions. Bug: v8:6596 Change-Id: I237b8b2792f201336e1c9731c815095dd06bc182 Reviewed-on: https://chromium-review.googlesource.com/571750 Reviewed-by: Igor Sheludko <[email protected]> Commit-Queue: Yang Guo <[email protected]> Cr-Commit-Position: refs/heads/master@{#46693} Fixes: #14171 Refs: #14345 Backport-PR-URL: #14574 Backport-Reviewed-By: Anna Henningsen <[email protected]> Backport-Reviewed-By: Refael Ackermann <[email protected]> PR-URL: #14004 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Franziska Hinkelmann <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
1 parent 997204a commit e2b306c

21 files changed

+321
-32
lines changed

deps/v8/src/api.cc

+11-2
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,9 @@ StartupData SnapshotCreator::CreateBlob(
660660
isolate->heap()->SetSerializedGlobalProxySizes(*global_proxy_sizes);
661661
}
662662

663+
// We might rehash strings and re-sort descriptors. Clear the lookup cache.
664+
isolate->descriptor_lookup_cache()->Clear();
665+
663666
// If we don't do this then we end up with a stray root pointing at the
664667
// context even after we have disposed of the context.
665668
isolate->heap()->CollectAllAvailableGarbage(
@@ -701,22 +704,28 @@ StartupData SnapshotCreator::CreateBlob(
701704
// Serialize each context with a new partial serializer.
702705
i::List<i::SnapshotData*> context_snapshots(num_additional_contexts + 1);
703706

707+
// TODO(6593): generalize rehashing, and remove this flag.
708+
bool can_be_rehashed = true;
709+
704710
{
705711
// The default snapshot does not support embedder fields.
706712
i::PartialSerializer partial_serializer(
707713
isolate, &startup_serializer, v8::SerializeInternalFieldsCallback());
708714
partial_serializer.Serialize(&default_context, false);
715+
can_be_rehashed = can_be_rehashed && partial_serializer.can_be_rehashed();
709716
context_snapshots.Add(new i::SnapshotData(&partial_serializer));
710717
}
711718

712719
for (int i = 0; i < num_additional_contexts; i++) {
713720
i::PartialSerializer partial_serializer(
714721
isolate, &startup_serializer, data->embedder_fields_serializers_[i]);
715722
partial_serializer.Serialize(&contexts[i], true);
723+
can_be_rehashed = can_be_rehashed && partial_serializer.can_be_rehashed();
716724
context_snapshots.Add(new i::SnapshotData(&partial_serializer));
717725
}
718726

719727
startup_serializer.SerializeWeakReferencesAndDeferred();
728+
can_be_rehashed = can_be_rehashed && startup_serializer.can_be_rehashed();
720729

721730
#ifdef DEBUG
722731
if (i::FLAG_external_reference_stats) {
@@ -725,8 +734,8 @@ StartupData SnapshotCreator::CreateBlob(
725734
#endif // DEBUG
726735

727736
i::SnapshotData startup_snapshot(&startup_serializer);
728-
StartupData result =
729-
i::Snapshot::CreateSnapshotBlob(&startup_snapshot, &context_snapshots);
737+
StartupData result = i::Snapshot::CreateSnapshotBlob(
738+
&startup_snapshot, &context_snapshots, can_be_rehashed);
730739

731740
// Delete heap-allocated context snapshot instances.
732741
for (const auto& context_snapshot : context_snapshots) {

deps/v8/src/bootstrapper.cc

+6
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,8 @@ Handle<JSFunction> Genesis::GetThrowTypeErrorIntrinsic(
652652
DCHECK(false);
653653
}
654654

655+
JSObject::MigrateSlowToFast(function, 0, "Bootstrapping");
656+
655657
return function;
656658
}
657659

@@ -1397,6 +1399,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
13971399
sloppy_function_map_writable_prototype_->SetConstructor(*function_fun);
13981400
strict_function_map_writable_prototype_->SetConstructor(*function_fun);
13991401
class_function_map_->SetConstructor(*function_fun);
1402+
1403+
JSObject::MigrateSlowToFast(function_fun, 0, "Bootstrapping");
14001404
}
14011405

14021406
{
@@ -2221,6 +2225,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
22212225
info->set_length(1);
22222226
native_context()->set_promise_reject_shared_fun(*info);
22232227
}
2228+
2229+
JSObject::MigrateSlowToFast(promise_fun, 0, "Bootstrapping");
22242230
}
22252231

22262232
{ // -- R e g E x p

deps/v8/src/flag-definitions.h

+2
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,8 @@ DEFINE_BOOL(abort_on_stack_overflow, false,
10021002
DEFINE_BOOL(randomize_hashes, true,
10031003
"randomize hashes to avoid predictable hash collisions "
10041004
"(with snapshots this option cannot override the baked-in seed)")
1005+
DEFINE_BOOL(rehash_snapshot, true,
1006+
"rehash strings from the snapshot to override the baked-in seed")
10051007
DEFINE_INT(hash_seed, 0,
10061008
"Fixed seed to use to hash property keys (0 means random)"
10071009
"(with snapshots this option cannot override the baked-in seed)")

deps/v8/src/heap/heap.cc

+9-8
Original file line numberDiff line numberDiff line change
@@ -5679,14 +5679,7 @@ bool Heap::SetUp() {
56795679

56805680
// Set up the seed that is used to randomize the string hash function.
56815681
DCHECK(hash_seed() == 0);
5682-
if (FLAG_randomize_hashes) {
5683-
if (FLAG_hash_seed == 0) {
5684-
int rnd = isolate()->random_number_generator()->NextInt();
5685-
set_hash_seed(Smi::FromInt(rnd & Name::kHashBitMask));
5686-
} else {
5687-
set_hash_seed(Smi::FromInt(FLAG_hash_seed));
5688-
}
5689-
}
5682+
if (FLAG_randomize_hashes) InitializeHashSeed();
56905683

56915684
for (int i = 0; i < static_cast<int>(v8::Isolate::kUseCounterFeatureCount);
56925685
i++) {
@@ -5731,6 +5724,14 @@ bool Heap::SetUp() {
57315724
return true;
57325725
}
57335726

5727+
void Heap::InitializeHashSeed() {
5728+
if (FLAG_hash_seed == 0) {
5729+
int rnd = isolate()->random_number_generator()->NextInt();
5730+
set_hash_seed(Smi::FromInt(rnd & Name::kHashBitMask));
5731+
} else {
5732+
set_hash_seed(Smi::FromInt(FLAG_hash_seed));
5733+
}
5734+
}
57345735

57355736
bool Heap::CreateHeapObjects() {
57365737
// Create initial maps.

deps/v8/src/heap/heap.h

+3
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,9 @@ class Heap {
10001000
// without actually creating any objects.
10011001
bool SetUp();
10021002

1003+
// (Re-)Initialize hash seed from flag or RNG.
1004+
void InitializeHashSeed();
1005+
10031006
// Bootstraps the object heap with the core set of objects required to run.
10041007
// Returns whether it succeeded.
10051008
bool CreateHeapObjects();

deps/v8/src/js/array.js

+2
Original file line numberDiff line numberDiff line change
@@ -1282,6 +1282,8 @@ var unscopables = {
12821282
keys: true,
12831283
};
12841284

1285+
%ToFastProperties(unscopables);
1286+
12851287
%AddNamedProperty(GlobalArray.prototype, unscopablesSymbol, unscopables,
12861288
DONT_ENUM | READ_ONLY);
12871289

deps/v8/src/objects.cc

+10-1
Original file line numberDiff line numberDiff line change
@@ -8860,7 +8860,13 @@ void Map::TraceAllTransitions(Map* map) {
88608860

88618861
void Map::ConnectTransition(Handle<Map> parent, Handle<Map> child,
88628862
Handle<Name> name, SimpleTransitionFlag flag) {
8863-
if (!parent->GetBackPointer()->IsUndefined(parent->GetIsolate())) {
8863+
Isolate* isolate = parent->GetIsolate();
8864+
// Do not track transitions during bootstrap except for element transitions.
8865+
if (isolate->bootstrapper()->IsActive() &&
8866+
!name.is_identical_to(isolate->factory()->elements_transition_symbol())) {
8867+
return;
8868+
}
8869+
if (!parent->GetBackPointer()->IsUndefined(isolate)) {
88648870
parent->set_owns_descriptors(false);
88658871
} else {
88668872
// |parent| is initial map and it must keep the ownership, there must be no
@@ -16712,6 +16718,9 @@ template class Dictionary<UnseededNumberDictionary,
1671216718
UnseededNumberDictionaryShape,
1671316719
uint32_t>;
1671416720

16721+
template void
16722+
HashTable<GlobalDictionary, GlobalDictionaryShape, Handle<Name> >::Rehash(Handle<Name> key);
16723+
1671516724
template Handle<SeededNumberDictionary>
1671616725
Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::New(
1671716726
Isolate*, int at_least_space_for, PretenureFlag pretenure,

deps/v8/src/snapshot/deserializer.cc

+71
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ void Deserializer::Deserialize(Isolate* isolate) {
125125
LOG_CODE_EVENT(isolate_, LogCompiledFunctions());
126126

127127
isolate_->builtins()->MarkInitialized();
128+
if (FLAG_rehash_snapshot && can_rehash_) Rehash();
128129
}
129130

130131
MaybeHandle<Object> Deserializer::DeserializePartial(
@@ -155,6 +156,9 @@ MaybeHandle<Object> Deserializer::DeserializePartial(
155156
// changed and logging should be added to notify the profiler et al of the
156157
// new code, which also has to be flushed from instruction cache.
157158
CHECK_EQ(start_address, code_space->top());
159+
160+
if (FLAG_rehash_snapshot && can_rehash_) RehashContext(Context::cast(root));
161+
158162
return Handle<Object>(root, isolate);
159163
}
160164

@@ -181,6 +185,63 @@ MaybeHandle<HeapObject> Deserializer::DeserializeObject(Isolate* isolate) {
181185
}
182186
}
183187

188+
// We only really just need HashForObject here.
189+
class StringRehashKey : public HashTableKey {
190+
public:
191+
uint32_t HashForObject(Object* other) override {
192+
return String::cast(other)->Hash();
193+
}
194+
195+
static uint32_t StringHash(Object* obj) {
196+
UNREACHABLE();
197+
return String::cast(obj)->Hash();
198+
}
199+
200+
bool IsMatch(Object* string) override {
201+
UNREACHABLE();
202+
return false;
203+
}
204+
205+
uint32_t Hash() override {
206+
UNREACHABLE();
207+
return 0;
208+
}
209+
210+
Handle<Object> AsHandle(Isolate* isolate) override {
211+
UNREACHABLE();
212+
return isolate->factory()->empty_string();
213+
}
214+
};
215+
216+
void Deserializer::Rehash() {
217+
DCHECK(can_rehash_);
218+
isolate_->heap()->InitializeHashSeed();
219+
if (FLAG_profile_deserialization) {
220+
PrintF("Re-initializing hash seed to %x\n",
221+
isolate_->heap()->hash_seed()->value());
222+
}
223+
StringRehashKey string_rehash_key;
224+
isolate_->heap()->string_table()->Rehash(&string_rehash_key);
225+
SortMapDescriptors();
226+
}
227+
228+
void Deserializer::RehashContext(Context* context) {
229+
DCHECK(can_rehash_);
230+
for (const auto& array : transition_arrays_) array->Sort();
231+
Handle<Name> dummy = isolate_->factory()->empty_string();
232+
context->global_object()->global_dictionary()->Rehash(dummy);
233+
SortMapDescriptors();
234+
}
235+
236+
void Deserializer::SortMapDescriptors() {
237+
for (const auto& address : allocated_maps_) {
238+
Map* map = Map::cast(HeapObject::FromAddress(address));
239+
if (map->instance_descriptors()->number_of_descriptors() > 1) {
240+
map->instance_descriptors()->Sort();
241+
}
242+
}
243+
}
244+
184245
Deserializer::~Deserializer() {
185246
#ifdef DEBUG
186247
// Do not perform checks if we aborted deserialization.
@@ -371,6 +432,16 @@ HeapObject* Deserializer::PostProcessNewObject(HeapObject* obj, int space) {
371432
string->resource()));
372433
isolate_->heap()->RegisterExternalString(string);
373434
}
435+
if (FLAG_rehash_snapshot && can_rehash_ && !deserializing_user_code()) {
436+
if (obj->IsString()) {
437+
// Uninitialize hash field as we are going to reinitialize the hash seed.
438+
String* string = String::cast(obj);
439+
string->set_hash_field(String::kEmptyHashField);
440+
} else if (obj->IsTransitionArray() &&
441+
TransitionArray::cast(obj)->number_of_entries() > 1) {
442+
transition_arrays_.Add(TransitionArray::cast(obj));
443+
}
444+
}
374445
// Check alignment.
375446
DCHECK_EQ(0, Heap::GetFillToAlign(obj->address(), obj->RequiredAlignment()));
376447
return obj;

deps/v8/src/snapshot/deserializer.h

+17-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ class Deserializer : public SerializerDeserializer {
3939
external_reference_table_(NULL),
4040
deserialized_large_objects_(0),
4141
deserializing_user_code_(deserializing_user_code),
42-
next_alignment_(kWordAligned) {
42+
next_alignment_(kWordAligned),
43+
can_rehash_(false) {
4344
DecodeReservation(data->Reservations());
4445
}
4546

@@ -62,6 +63,8 @@ class Deserializer : public SerializerDeserializer {
6263
attached_objects_.Add(attached_object);
6364
}
6465

66+
void SetRehashability(bool v) { can_rehash_ = v; }
67+
6568
private:
6669
void VisitRootPointers(Root root, Object** start, Object** end) override;
6770

@@ -115,6 +118,15 @@ class Deserializer : public SerializerDeserializer {
115118
// snapshot by chunk index and offset.
116119
HeapObject* GetBackReferencedObject(int space);
117120

121+
// Rehash after deserializing an isolate.
122+
void Rehash();
123+
124+
// Rehash after deserializing a context.
125+
void RehashContext(Context* context);
126+
127+
// Sort descriptors of deserialized maps using new string hashes.
128+
void SortMapDescriptors();
129+
118130
// Cached current isolate.
119131
Isolate* isolate_;
120132

@@ -142,11 +154,15 @@ class Deserializer : public SerializerDeserializer {
142154
List<AccessorInfo*> accessor_infos_;
143155
List<Handle<String> > new_internalized_strings_;
144156
List<Handle<Script> > new_scripts_;
157+
List<TransitionArray*> transition_arrays_;
145158

146159
bool deserializing_user_code_;
147160

148161
AllocationAlignment next_alignment_;
149162

163+
// TODO(6593): generalize rehashing, and remove this flag.
164+
bool can_rehash_;
165+
150166
DISALLOW_COPY_AND_ASSIGN(Deserializer);
151167
};
152168

deps/v8/src/snapshot/partial-serializer.cc

+27-10
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ PartialSerializer::PartialSerializer(
1515
v8::SerializeEmbedderFieldsCallback callback)
1616
: Serializer(isolate),
1717
startup_serializer_(startup_serializer),
18-
serialize_embedder_fields_(callback) {
18+
serialize_embedder_fields_(callback),
19+
rehashable_global_dictionary_(nullptr),
20+
can_be_rehashed_(true) {
1921
InitializeCodeAddressMap();
2022
}
2123

@@ -24,22 +26,26 @@ PartialSerializer::~PartialSerializer() {
2426
}
2527

2628
void PartialSerializer::Serialize(Object** o, bool include_global_proxy) {
27-
if ((*o)->IsContext()) {
29+
if ((*o)->IsNativeContext()) {
2830
Context* context = Context::cast(*o);
2931
reference_map()->AddAttachedReference(context->global_proxy());
3032
// The bootstrap snapshot has a code-stub context. When serializing the
3133
// partial snapshot, it is chained into the weak context list on the isolate
3234
// and it's next context pointer may point to the code-stub context. Clear
3335
// it before serializing, it will get re-added to the context list
3436
// explicitly when it's loaded.
35-
if (context->IsNativeContext()) {
36-
context->set(Context::NEXT_CONTEXT_LINK,
37-
isolate_->heap()->undefined_value());
38-
DCHECK(!context->global_object()->IsUndefined(context->GetIsolate()));
39-
// Reset math random cache to get fresh random numbers.
40-
context->set_math_random_index(Smi::kZero);
41-
context->set_math_random_cache(isolate_->heap()->undefined_value());
42-
}
37+
context->set(Context::NEXT_CONTEXT_LINK,
38+
isolate_->heap()->undefined_value());
39+
DCHECK(!context->global_object()->IsUndefined(context->GetIsolate()));
40+
// Reset math random cache to get fresh random numbers.
41+
context->set_math_random_index(Smi::kZero);
42+
context->set_math_random_cache(isolate_->heap()->undefined_value());
43+
DCHECK_NULL(rehashable_global_dictionary_);
44+
rehashable_global_dictionary_ =
45+
context->global_object()->global_dictionary();
46+
} else {
47+
// We only do rehashing for native contexts.
48+
can_be_rehashed_ = false;
4349
}
4450
VisitRootPointer(Root::kPartialSnapshotCache, o);
4551
SerializeDeferredObjects();
@@ -104,6 +110,8 @@ void PartialSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
104110
}
105111
}
106112

113+
if (obj->IsHashTable()) CheckRehashability(obj);
114+
107115
// Object has not yet been serialized. Serialize it here.
108116
ObjectSerializer serializer(this, obj, &sink_, how_to_code, where_to_point);
109117
serializer.Serialize();
@@ -152,5 +160,14 @@ void PartialSerializer::SerializeEmbedderFields() {
152160
sink_.Put(kSynchronize, "Finished with embedder fields data");
153161
}
154162

163+
void PartialSerializer::CheckRehashability(HeapObject* table) {
164+
DCHECK(table->IsHashTable());
165+
if (!can_be_rehashed_) return;
166+
// We can only correctly rehash if the global dictionary is the only hash
167+
// table that we deserialize.
168+
if (table == rehashable_global_dictionary_) return;
169+
can_be_rehashed_ = false;
170+
}
171+
155172
} // namespace internal
156173
} // namespace v8

0 commit comments

Comments
 (0)