Snapshot: support rehashing property and element dictionaries.

This change
- adds new maps for elements, global, and named dictionaries.
- adds support to embed these dictionaries in the startup snapshot.
- adds support to embed these dictionaries in the code cache.
- refactors the rehashing logic.

R=ishell@chromium.org, jgruber@chromium.org, mstarzinger@chromium.org

Bug: v8:6593
Change-Id: I8d9a7ba7145f1af4e6e15301a4d5611f07c77f33
Reviewed-on: https://chromium-review.googlesource.com/753323
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Commit-Queue: Yang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49155}
This commit is contained in:
Yang Guo 2017-11-06 14:26:17 +01:00 committed by Commit Bot
parent 0afe2524c0
commit 3877bf6f4b
33 changed files with 338 additions and 177 deletions

View File

@ -2394,8 +2394,8 @@ Node* CodeStubAssembler::AllocateNameDictionaryWithCapacity(Node* capacity) {
Node* result = AllocateInNewSpace(store_size);
Comment("Initialize NameDictionary");
// Initialize FixedArray fields.
DCHECK(Heap::RootIsImmortalImmovable(Heap::kHashTableMapRootIndex));
StoreMapNoWriteBarrier(result, Heap::kHashTableMapRootIndex);
DCHECK(Heap::RootIsImmortalImmovable(Heap::kNameDictionaryMapRootIndex));
StoreMapNoWriteBarrier(result, Heap::kNameDictionaryMapRootIndex);
StoreObjectFieldNoWriteBarrier(result, FixedArray::kLengthOffset,
SmiFromWord(length));
// Initialized HashTable fields.

View File

@ -2262,7 +2262,7 @@ Reduction JSBuiltinReducer::ReduceObjectCreate(Node* node) {
Node* properties = jsgraph()->EmptyFixedArrayConstant();
if (instance_map->is_dictionary_map()) {
// Allocated an empty NameDictionary as backing store for the properties.
Handle<Map> map(isolate()->heap()->hash_table_map(), isolate());
Handle<Map> map(isolate()->heap()->name_dictionary_map(), isolate());
int capacity =
NameDictionary::ComputeCapacity(NameDictionary::kInitialCapacity);
DCHECK(base::bits::IsPowerOfTwo(capacity));

View File

@ -515,7 +515,9 @@ ScriptCompiler::CachedData* CompileForCachedData(
}
Isolate::CreateParams create_params;
create_params.array_buffer_allocator = Shell::array_buffer_allocator;
i::FLAG_hash_seed ^= 1337; // Use a different hash seed.
Isolate* temp_isolate = Isolate::New(create_params);
i::FLAG_hash_seed ^= 1337; // Restore old hash seed.
temp_isolate->SetHostImportModuleDynamicallyCallback(
Shell::HostImportModuleDynamically);
temp_isolate->SetHostInitializeImportMetaObjectCallback(

View File

@ -104,6 +104,9 @@ using v8::MemoryPressureLevel;
V(Map, fixed_double_array_map, FixedDoubleArrayMap) \
V(Map, mutable_heap_number_map, MutableHeapNumberMap) \
V(Map, ordered_hash_table_map, OrderedHashTableMap) \
V(Map, name_dictionary_map, NameDictionaryMap) \
V(Map, global_dictionary_map, GlobalDictionaryMap) \
V(Map, seeded_number_dictionary_map, SeededNumberDictionaryMap) \
V(Map, unseeded_number_dictionary_map, UnseededNumberDictionaryMap) \
V(Map, sloppy_arguments_elements_map, SloppyArgumentsElementsMap) \
V(Map, small_ordered_hash_map_map, SmallOrderedHashMapMap) \
@ -311,6 +314,7 @@ using v8::MemoryPressureLevel;
V(ForeignMap) \
V(FreeSpaceMap) \
V(FunctionContextMap) \
V(GlobalDictionaryMap) \
V(GlobalPropertyCellMap) \
V(HashTableMap) \
V(HeapNumberMap) \
@ -327,6 +331,7 @@ using v8::MemoryPressureLevel;
V(ModuleContextMap) \
V(ModuleInfoMap) \
V(MutableHeapNumberMap) \
V(NameDictionaryMap) \
V(NanValue) \
V(NativeContextMap) \
V(NoClosuresCellMap) \
@ -337,12 +342,13 @@ using v8::MemoryPressureLevel;
V(OptimizedOut) \
V(OrderedHashTableMap) \
V(PropertyArrayMap) \
V(SmallOrderedHashMapMap) \
V(SmallOrderedHashSetMap) \
V(ScopeInfoMap) \
V(ScriptContextMap) \
V(SeededNumberDictionaryMap) \
V(SharedFunctionInfoMap) \
V(SloppyArgumentsElementsMap) \
V(SmallOrderedHashMapMap) \
V(SmallOrderedHashSetMap) \
V(SpeciesProtector) \
V(StaleRegister) \
V(StringLengthProtector) \
@ -358,6 +364,7 @@ using v8::MemoryPressureLevel;
V(UndefinedValue) \
V(UninitializedMap) \
V(UninitializedValue) \
V(UnseededNumberDictionaryMap) \
V(WeakCellMap) \
V(WithContextMap) \
PRIVATE_SYMBOL_LIST(V)

View File

@ -288,6 +288,9 @@ bool Heap::CreateInitialMaps() {
ALLOCATE_VARSIZE_MAP(HASH_TABLE_TYPE, hash_table)
ALLOCATE_VARSIZE_MAP(HASH_TABLE_TYPE, ordered_hash_table)
ALLOCATE_VARSIZE_MAP(HASH_TABLE_TYPE, name_dictionary)
ALLOCATE_VARSIZE_MAP(HASH_TABLE_TYPE, global_dictionary)
ALLOCATE_VARSIZE_MAP(HASH_TABLE_TYPE, seeded_number_dictionary)
ALLOCATE_VARSIZE_MAP(HASH_TABLE_TYPE, unseeded_number_dictionary)
ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, function_context)

View File

@ -139,7 +139,7 @@ void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup(
// Check that the properties array is a dictionary.
__ ldr(map, FieldMemOperand(properties, HeapObject::kMapOffset));
Register tmp = properties;
__ LoadRoot(tmp, Heap::kHashTableMapRootIndex);
__ LoadRoot(tmp, Heap::kNameDictionaryMapRootIndex);
__ cmp(map, tmp);
__ b(ne, miss_label);

View File

@ -76,7 +76,7 @@ void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup(
FieldMemOperand(receiver, JSObject::kPropertiesOrHashOffset));
// Check that the properties array is a dictionary.
__ Ldr(map, FieldMemOperand(properties, HeapObject::kMapOffset));
__ JumpIfNotRoot(map, Heap::kHashTableMapRootIndex, miss_label);
__ JumpIfNotRoot(map, Heap::kNameDictionaryMapRootIndex, miss_label);
NameDictionaryLookupStub::GenerateNegativeLookup(
masm, miss_label, &done, receiver, properties, name, scratch1);

View File

@ -88,7 +88,7 @@ void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup(
// Check that the properties array is a dictionary.
__ cmp(FieldOperand(properties, HeapObject::kMapOffset),
Immediate(masm->isolate()->factory()->hash_table_map()));
Immediate(masm->isolate()->factory()->name_dictionary_map()));
__ j(not_equal, miss_label);
Label done;

View File

@ -131,7 +131,7 @@ void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup(
// Check that the properties array is a dictionary.
__ lw(map, FieldMemOperand(properties, HeapObject::kMapOffset));
Register tmp = properties;
__ LoadRoot(tmp, Heap::kHashTableMapRootIndex);
__ LoadRoot(tmp, Heap::kNameDictionaryMapRootIndex);
__ Branch(miss_label, ne, map, Operand(tmp));
// Restore the temporarily used register.

View File

@ -131,7 +131,7 @@ void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup(
// Check that the properties array is a dictionary.
__ Ld(map, FieldMemOperand(properties, HeapObject::kMapOffset));
Register tmp = properties;
__ LoadRoot(tmp, Heap::kHashTableMapRootIndex);
__ LoadRoot(tmp, Heap::kNameDictionaryMapRootIndex);
__ Branch(miss_label, ne, map, Operand(tmp));
// Restore the temporarily used register.

View File

@ -133,7 +133,7 @@ void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup(
// Check that the properties array is a dictionary.
__ LoadP(map, FieldMemOperand(properties, HeapObject::kMapOffset));
Register tmp = properties;
__ LoadRoot(tmp, Heap::kHashTableMapRootIndex);
__ LoadRoot(tmp, Heap::kNameDictionaryMapRootIndex);
__ cmp(map, tmp);
__ bne(miss_label);

View File

@ -129,7 +129,7 @@ void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup(
FieldMemOperand(receiver, JSObject::kPropertiesOrHashOffset));
// Check that the properties array is a dictionary.
__ LoadP(map, FieldMemOperand(properties, HeapObject::kMapOffset));
__ CompareRoot(map, Heap::kHashTableMapRootIndex);
__ CompareRoot(map, Heap::kNameDictionaryMapRootIndex);
__ bne(miss_label);
// Restore the temporarily used register.

View File

@ -74,7 +74,7 @@ void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup(
// Check that the properties array is a dictionary.
__ CompareRoot(FieldOperand(properties, HeapObject::kMapOffset),
Heap::kHashTableMapRootIndex);
Heap::kNameDictionaryMapRootIndex);
__ j(not_equal, miss_label);
Label done;

View File

@ -424,11 +424,17 @@ bool HeapObject::IsDictionary() const {
return IsHashTable() && this != GetHeap()->string_table();
}
bool Object::IsNameDictionary() const { return IsDictionary(); }
bool HeapObject::IsGlobalDictionary() const {
return map() == GetHeap()->global_dictionary_map();
}
bool Object::IsGlobalDictionary() const { return IsDictionary(); }
bool HeapObject::IsNameDictionary() const {
return map() == GetHeap()->name_dictionary_map();
}
bool Object::IsSeededNumberDictionary() const { return IsDictionary(); }
bool HeapObject::IsSeededNumberDictionary() const {
return map() == GetHeap()->seeded_number_dictionary_map();
}
bool HeapObject::IsUnseededNumberDictionary() const {
return map() == GetHeap()->unseeded_number_dictionary_map();
@ -1957,6 +1963,19 @@ AllocationAlignment HeapObject::RequiredAlignment() const {
return kWordAligned;
}
bool HeapObject::NeedsRehashing() const {
switch (map()->instance_type()) {
case HASH_TABLE_TYPE:
return !IsUnseededNumberDictionary();
case TRANSITION_ARRAY_TYPE:
return TransitionArray::cast(this)->number_of_entries() > 1;
case SMALL_ORDERED_HASH_MAP_TYPE:
case SMALL_ORDERED_HASH_SET_TYPE:
return true;
default:
return false;
}
}
void FixedArray::set(int index,
Object* value,
@ -4779,8 +4798,16 @@ Object* GlobalDictionaryShape::Unwrap(Object* object) {
return PropertyCell::cast(object)->name();
}
Map* GlobalDictionaryShape::GetMap(Isolate* isolate) {
return isolate->heap()->global_dictionary_map();
}
Name* NameDictionary::NameAt(int entry) { return Name::cast(KeyAt(entry)); }
Map* NameDictionaryShape::GetMap(Isolate* isolate) {
return isolate->heap()->name_dictionary_map();
}
PropertyCell* GlobalDictionary::CellAt(int entry) {
DCHECK(KeyAt(entry)->IsPropertyCell());
return PropertyCell::cast(KeyAt(entry));
@ -4841,6 +4868,9 @@ uint32_t SeededNumberDictionaryShape::HashForObject(Isolate* isolate,
isolate->heap()->HashSeed());
}
Map* SeededNumberDictionaryShape::GetMap(Isolate* isolate) {
return isolate->heap()->seeded_number_dictionary_map();
}
Handle<Object> NumberDictionaryShape::AsHandle(Isolate* isolate, uint32_t key) {
return isolate->factory()->NewNumberFromUint(key);

View File

@ -3630,6 +3630,55 @@ String* JSReceiver::class_name() {
return GetHeap()->Object_string();
}
bool HeapObject::CanBeRehashed() const {
DCHECK(NeedsRehashing());
switch (map()->instance_type()) {
case HASH_TABLE_TYPE:
return IsNameDictionary() || IsGlobalDictionary() ||
IsSeededNumberDictionary();
case TRANSITION_ARRAY_TYPE:
return true;
case SMALL_ORDERED_HASH_MAP_TYPE:
return SmallOrderedHashMap::cast(this)->NumberOfElements() == 0;
case SMALL_ORDERED_HASH_SET_TYPE:
return SmallOrderedHashMap::cast(this)->NumberOfElements() == 0;
default:
return false;
}
return false;
}
void HeapObject::RehashBasedOnMap() {
switch (map()->instance_type()) {
case HASH_TABLE_TYPE:
if (IsNameDictionary()) {
NameDictionary::cast(this)->Rehash();
} else if (IsSeededNumberDictionary()) {
SeededNumberDictionary::cast(this)->Rehash();
} else if (IsGlobalDictionary()) {
GlobalDictionary::cast(this)->Rehash();
} else {
// TODO(6593): Some hash tables cannot yet be rehashed based on the map,
// and are handled explicitly in StartupDeserializer::RehashHeap.
if (this == GetHeap()->empty_ordered_hash_table()) break;
if (this == GetHeap()->weak_object_to_code_table()) break;
if (this == GetHeap()->string_table()) break;
UNREACHABLE();
}
break;
case TRANSITION_ARRAY_TYPE:
TransitionArray::cast(this)->Sort();
break;
case SMALL_ORDERED_HASH_MAP_TYPE:
DCHECK_EQ(0, SmallOrderedHashMap::cast(this)->NumberOfElements());
break;
case SMALL_ORDERED_HASH_SET_TYPE:
DCHECK_EQ(0, SmallOrderedHashSet::cast(this)->NumberOfElements());
break;
default:
break;
}
}
// static
Handle<String> JSReceiver::GetConstructorName(Handle<JSReceiver> receiver) {
@ -6436,10 +6485,14 @@ int GetIdentityHashHelper(Isolate* isolate, JSReceiver* object) {
return PropertyArray::cast(properties)->Hash();
}
if (properties->IsDictionary()) {
if (properties->IsNameDictionary()) {
return NameDictionary::cast(properties)->Hash();
}
if (properties->IsGlobalDictionary()) {
return GlobalDictionary::cast(properties)->Hash();
}
#ifdef DEBUG
FixedArray* empty_fixed_array = isolate->heap()->empty_fixed_array();
FixedArray* empty_property_dictionary =

View File

@ -1013,6 +1013,7 @@ template <class C> inline bool Is(Object* obj);
V(FrameArray) \
V(FreeSpace) \
V(Function) \
V(GlobalDictionary) \
V(HandlerTable) \
V(HeapNumber) \
V(InternalizedString) \
@ -1056,6 +1057,7 @@ template <class C> inline bool Is(Object* obj);
V(ModuleInfo) \
V(MutableHeapNumber) \
V(Name) \
V(NameDictionary) \
V(NativeContext) \
V(NormalizedMapCache) \
V(ObjectHashSet) \
@ -1069,6 +1071,7 @@ template <class C> inline bool Is(Object* obj);
V(RegExpMatchInfo) \
V(ScopeInfo) \
V(ScriptContextTable) \
V(SeededNumberDictionary) \
V(SeqOneByteString) \
V(SeqString) \
V(SeqTwoByteString) \
@ -1182,9 +1185,6 @@ class Object {
// ES6, #sec-isarray. NOT to be confused with %_IsArray.
INLINE(MUST_USE_RESULT static Maybe<bool> IsArray(Handle<Object> object));
INLINE(bool IsNameDictionary() const);
INLINE(bool IsGlobalDictionary() const);
INLINE(bool IsSeededNumberDictionary() const);
INLINE(bool IsOrderedHashSet() const);
INLINE(bool IsOrderedHashMap() const);
INLINE(bool IsSmallOrderedHashTable() const);
@ -1816,6 +1816,19 @@ class HeapObject: public Object {
inline AllocationAlignment RequiredAlignment() const;
// Whether the object needs rehashing. That is the case if the object's
// content depends on FLAG_hash_seed. When the object is deserialized into
// a heap with a different hash seed, these objects need to adapt.
inline bool NeedsRehashing() const;
// Rehashing support is not implemented for all objects that need rehashing.
// With objects that need rehashing but cannot be rehashed, rehashing has to
// be disabled.
bool CanBeRehashed() const;
// Rehash the object based on the layout inferred from its map.
void RehashBasedOnMap();
// Layout description.
// First field in a heap object is map.
static const int kMapOffset = Object::kHeaderSize;

View File

@ -112,6 +112,7 @@ class NameDictionaryShape : public BaseDictionaryShape<Handle<Name>> {
static inline uint32_t Hash(Isolate* isolate, Handle<Name> key);
static inline uint32_t HashForObject(Isolate* isolate, Object* object);
static inline Handle<Object> AsHandle(Isolate* isolate, Handle<Name> key);
static inline Map* GetMap(Isolate* isolate);
static const int kPrefixSize = 2;
static const int kEntrySize = 3;
static const int kEntryValueIndex = 1;
@ -207,6 +208,7 @@ class GlobalDictionaryShape : public NameDictionaryShape {
static inline Object* Unwrap(Object* key);
static inline bool IsKey(Isolate* isolate, Object* k);
static inline bool IsLive(Isolate* isolate, Object* key);
static inline Map* GetMap(Isolate* isolate);
};
class GlobalDictionary
@ -235,6 +237,8 @@ class SeededNumberDictionaryShape : public NumberDictionaryShape {
static inline uint32_t Hash(Isolate* isolate, uint32_t key);
static inline uint32_t HashForObject(Isolate* isolate, Object* object);
static inline Map* GetMap(Isolate* isolate);
};
class UnseededNumberDictionaryShape : public NumberDictionaryShape {

View File

@ -123,8 +123,8 @@ void CodeSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
CHECK(!obj->IsMap());
// There should be no references to the global object embedded.
CHECK(!obj->IsJSGlobalProxy() && !obj->IsJSGlobalObject());
// There should be no hash table embedded. They would require rehashing.
CHECK(!obj->IsHashTable());
// Embedded FixedArrays that need rehashing must support rehashing.
CHECK_IMPLIES(obj->NeedsRehashing(), obj->CanBeRehashed());
// We expect no instantiated function objects or contexts.
CHECK(!obj->IsJSFunction() && !obj->IsContext());

View File

@ -6,6 +6,7 @@
#include "src/assembler-inl.h"
#include "src/isolate.h"
#include "src/objects/hash-table.h"
#include "src/objects/string.h"
#include "src/snapshot/builtin-deserializer-allocator.h"
#include "src/snapshot/natives.h"
@ -38,6 +39,18 @@ bool Deserializer<AllocatorT>::IsLazyDeserializationEnabled() const {
return FLAG_lazy_deserialization && !isolate()->serializer_enabled();
}
template <class AllocatorT>
void Deserializer<AllocatorT>::Rehash() {
DCHECK(can_rehash() || deserializing_user_code());
for (const auto& item : to_rehash_) item->RehashBasedOnMap();
for (const auto& address : allocator()->GetAllocatedMaps()) {
Map* map = Map::cast(HeapObject::FromAddress(address));
if (map->instance_descriptors()->number_of_descriptors() > 1) {
map->instance_descriptors()->Sort();
}
}
}
template <class AllocatorT>
Deserializer<AllocatorT>::~Deserializer() {
#ifdef DEBUG
@ -126,11 +139,19 @@ uint32_t StringTableInsertionKey::ComputeHashField(String* string) {
template <class AllocatorT>
HeapObject* Deserializer<AllocatorT>::PostProcessNewObject(HeapObject* obj,
int space) {
if ((FLAG_rehash_snapshot && can_rehash_) || deserializing_user_code()) {
if (obj->IsString()) {
// Uninitialize hash field as we need to recompute the hash.
String* string = String::cast(obj);
string->set_hash_field(String::kEmptyHashField);
} else if (obj->NeedsRehashing()) {
to_rehash_.push_back(obj);
}
}
if (deserializing_user_code()) {
if (obj->IsString()) {
String* string = String::cast(obj);
// Uninitialize hash field as the hash seed may have changed.
string->set_hash_field(String::kEmptyHashField);
if (string->IsInternalizedString()) {
// Canonicalize the internalized string. If it already exists in the
// string table, set it to forward to the existing one.
@ -203,20 +224,9 @@ HeapObject* Deserializer<AllocatorT>::PostProcessNewObject(HeapObject* obj,
if (fta->base_pointer() == nullptr) {
Smi* store_index = reinterpret_cast<Smi*>(fta->external_pointer());
void* backing_store = off_heap_backing_stores_[store_index->value()];
fta->set_external_pointer(backing_store);
}
}
if (FLAG_rehash_snapshot && can_rehash_ && !deserializing_user_code()) {
if (obj->IsString()) {
// Uninitialize hash field as we are going to reinitialize the hash seed.
String* string = String::cast(obj);
string->set_hash_field(String::kEmptyHashField);
} else if (obj->IsTransitionArray() &&
TransitionArray::cast(obj)->number_of_entries() > 1) {
transition_arrays_.push_back(TransitionArray::cast(obj));
}
}
// Check alignment.
DCHECK_EQ(0, Heap::GetFillToAlign(obj->address(), obj->RequiredAlignment()));
return obj;
@ -258,16 +268,6 @@ HeapObject* Deserializer<AllocatorT>::GetBackReferencedObject(int space) {
return obj;
}
template <class AllocatorT>
void Deserializer<AllocatorT>::SortMapDescriptors() {
for (const auto& address : allocator()->GetAllocatedMaps()) {
Map* map = Map::cast(HeapObject::FromAddress(address));
if (map->instance_descriptors()->number_of_descriptors() > 1) {
map->instance_descriptors()->Sort();
}
}
}
// This routine writes the new object into the pointer provided and then
// returns true if the new object was in young space and false otherwise.
// The reason for this strange interface is that otherwise the object is

View File

@ -88,9 +88,6 @@ class Deserializer : public SerializerDeserializer {
const std::vector<Handle<Script>>& new_scripts() const {
return new_scripts_;
}
const std::vector<TransitionArray*>& transition_arrays() const {
return transition_arrays_;
}
AllocatorT* allocator() { return &allocator_; }
bool deserializing_user_code() const { return deserializing_user_code_; }
@ -98,6 +95,8 @@ class Deserializer : public SerializerDeserializer {
bool IsLazyDeserializationEnabled() const;
void Rehash();
private:
void VisitRootPointers(Root root, Object** start, Object** end) override;
@ -147,7 +146,6 @@ class Deserializer : public SerializerDeserializer {
std::vector<CallHandlerInfo*> call_handler_infos_;
std::vector<Handle<String>> new_internalized_strings_;
std::vector<Handle<Script>> new_scripts_;
std::vector<TransitionArray*> transition_arrays_;
std::vector<byte*> off_heap_backing_stores_;
AllocatorT allocator_;
@ -155,6 +153,7 @@ class Deserializer : public SerializerDeserializer {
// TODO(6593): generalize rehashing, and remove this flag.
bool can_rehash_;
std::vector<HeapObject*> to_rehash_;
#ifdef DEBUG
uint32_t num_api_references_;

View File

@ -79,6 +79,7 @@ MaybeHandle<HeapObject> ObjectDeserializer::Deserialize(Isolate* isolate) {
DeserializeDeferredObjects();
FlushICacheForNewCodeObjectsAndRecordEmbeddedObjects();
result = Handle<HeapObject>(HeapObject::cast(root));
Rehash();
allocator()->RegisterDeserializedObjectsForBlackAllocation();
}
CommitPostProcessedObjects();

View File

@ -53,7 +53,7 @@ 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()) RehashContext(Context::cast(root));
if (FLAG_rehash_snapshot && can_rehash()) Rehash();
return Handle<Object>(root, isolate);
}
@ -84,13 +84,5 @@ void PartialDeserializer::DeserializeEmbedderFields(
delete[] data;
}
}
void PartialDeserializer::RehashContext(Context* context) {
DCHECK(can_rehash());
for (const auto& array : transition_arrays()) array->Sort();
context->global_object()->global_dictionary()->Rehash();
SortMapDescriptors();
}
} // namespace internal
} // namespace v8

View File

@ -33,9 +33,6 @@ class PartialDeserializer final : public Deserializer<> {
void DeserializeEmbedderFields(
v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer);
// Rehash after deserializing a context.
void RehashContext(Context* context);
};
} // namespace internal

View File

@ -17,7 +17,6 @@ PartialSerializer::PartialSerializer(
: Serializer(isolate),
startup_serializer_(startup_serializer),
serialize_embedder_fields_(callback),
rehashable_global_dictionary_(nullptr),
can_be_rehashed_(true) {
InitializeCodeAddressMap();
}
@ -42,8 +41,6 @@ void PartialSerializer::Serialize(Object** o, bool include_global_proxy) {
// Reset math random cache to get fresh random numbers.
context->set_math_random_index(Smi::kZero);
context->set_math_random_cache(isolate()->heap()->undefined_value());
DCHECK_NULL(rehashable_global_dictionary_);
rehashable_global_dictionary_ = context->global_object()->global_dictionary();
VisitRootPointer(Root::kPartialSnapshotCache, o);
SerializeDeferredObjects();
@ -104,7 +101,7 @@ void PartialSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
}
}
if (obj->IsHashTable()) CheckRehashability(obj);
CheckRehashability(obj);
// Object has not yet been serialized. Serialize it here.
ObjectSerializer serializer(this, obj, &sink_, how_to_code, where_to_point);
@ -155,17 +152,10 @@ void PartialSerializer::SerializeEmbedderFields() {
sink_.Put(kSynchronize, "Finished with embedder fields data");
}
void PartialSerializer::CheckRehashability(HeapObject* table) {
DCHECK(table->IsHashTable());
void PartialSerializer::CheckRehashability(HeapObject* obj) {
if (!can_be_rehashed_) return;
if (table->IsUnseededNumberDictionary()) return;
if (table->IsOrderedHashMap() &&
OrderedHashMap::cast(table)->NumberOfElements() == 0) {
return;
}
// We can only correctly rehash if the global dictionary is the only hash
// table that we deserialize.
if (table == rehashable_global_dictionary_) return;
if (!obj->NeedsRehashing()) return;
if (obj->CanBeRehashed()) return;
can_be_rehashed_ = false;
}

View File

@ -33,12 +33,11 @@ class PartialSerializer : public Serializer<> {
void SerializeEmbedderFields();
void CheckRehashability(HeapObject* table);
void CheckRehashability(HeapObject* obj);
StartupSerializer* startup_serializer_;
std::vector<JSObject*> embedder_field_holders_;
v8::SerializeEmbedderFieldsCallback serialize_embedder_fields_;
GlobalDictionary* rehashable_global_dictionary_;
// Indicates whether we only serialized hash tables that we can rehash.
// TODO(yangguo): generalize rehashing, and remove this flag.
bool can_be_rehashed_;

View File

@ -73,7 +73,7 @@ void StartupDeserializer::DeserializeInto(Isolate* isolate) {
// to display the builtin names.
PrintDisassembledCodeObjects();
if (FLAG_rehash_snapshot && can_rehash()) Rehash();
if (FLAG_rehash_snapshot && can_rehash()) RehashHeap();
}
void StartupDeserializer::FlushICacheForNewIsolate() {
@ -105,12 +105,12 @@ void StartupDeserializer::PrintDisassembledCodeObjects() {
#endif
}
void StartupDeserializer::Rehash() {
void StartupDeserializer::RehashHeap() {
DCHECK(FLAG_rehash_snapshot && can_rehash());
isolate()->heap()->InitializeHashSeed();
isolate()->heap()->string_table()->Rehash();
isolate()->heap()->weak_object_to_code_table()->Rehash();
SortMapDescriptors();
Rehash();
}
} // namespace internal

View File

@ -26,7 +26,7 @@ class StartupDeserializer final : public Deserializer<> {
void PrintDisassembledCodeObjects();
// Rehash after deserializing an isolate.
void Rehash();
void RehashHeap();
const BuiltinSnapshotData* builtin_data_;
};

View File

@ -81,7 +81,7 @@ void StartupSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
}
}
if (obj->IsHashTable()) CheckRehashability(obj);
CheckRehashability(obj);
// Object has not yet been serialized. Serialize it here.
ObjectSerializer object_serializer(this, obj, &sink_, how_to_code,
@ -182,17 +182,15 @@ bool StartupSerializer::RootShouldBeSkipped(int root_index) {
serializing_immortal_immovables_roots_;
}
void StartupSerializer::CheckRehashability(HeapObject* table) {
DCHECK(table->IsHashTable());
void StartupSerializer::CheckRehashability(HeapObject* obj) {
if (!can_be_rehashed_) return;
if (!obj->NeedsRehashing()) return;
if (obj->CanBeRehashed()) return;
// We can only correctly rehash if the four hash tables below are the only
// ones that we deserialize.
if (table->IsUnseededNumberDictionary()) return;
if (table == isolate()->heap()->empty_ordered_hash_table()) return;
if (table == isolate()->heap()->empty_slow_element_dictionary()) return;
if (table == isolate()->heap()->empty_property_dictionary()) return;
if (table == isolate()->heap()->weak_object_to_code_table()) return;
if (table == isolate()->heap()->string_table()) return;
if (obj == isolate()->heap()->empty_ordered_hash_table()) return;
if (obj == isolate()->heap()->weak_object_to_code_table()) return;
if (obj == isolate()->heap()->string_table()) return;
can_be_rehashed_ = false;
}

View File

@ -75,7 +75,7 @@ class StartupSerializer : public Serializer<> {
// roots. In the second pass, we serialize the rest.
bool RootShouldBeSkipped(int root_index);
void CheckRehashability(HeapObject* hashtable);
void CheckRehashability(HeapObject* obj);
const bool clear_function_code_;
bool serializing_immortal_immovables_roots_;

View File

@ -9,6 +9,9 @@
#include "src/ic/handler-configuration-inl.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
@ -31,12 +34,7 @@ TransitionArray* TransitionsAccessor::transitions() {
return TransitionArray::cast(raw_transitions_);
}
// static
TransitionArray* TransitionArray::cast(Object* object) {
DCHECK(object->IsTransitionArray());
return reinterpret_cast<TransitionArray*>(object);
}
CAST_ACCESSOR(TransitionArray)
bool TransitionArray::HasPrototypeTransitions() {
return get(kPrototypeTransitionsIndex) != Smi::kZero;
@ -215,4 +213,6 @@ void TransitionArray::SetNumberOfTransitions(int number_of_transitions) {
} // namespace internal
} // namespace v8
#include "src/objects/object-macros-undef.h"
#endif // V8_TRANSITIONS_INL_H_

View File

@ -12,6 +12,9 @@
#include "src/objects/map.h"
#include "src/objects/name.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
@ -205,7 +208,7 @@ class TransitionsAccessor {
// [3 + number of transitions * kTransitionSize]: start of slack
class TransitionArray : public FixedArray {
public:
inline static TransitionArray* cast(Object* object);
DECL_CAST(TransitionArray)
inline FixedArray* GetPrototypeTransitions();
inline Object** GetPrototypeTransitionsSlot();
@ -227,7 +230,7 @@ class TransitionArray : public FixedArray {
return GetKey(transition_number);
}
int GetSortedKeyIndex(int transition_number) { return transition_number; }
inline int number_of_entries() { return number_of_transitions(); }
inline int number_of_entries() const { return number_of_transitions(); }
#ifdef DEBUG
bool IsSortedNoDuplicates(int valid_entries = -1);
#endif
@ -326,7 +329,7 @@ class TransitionArray : public FixedArray {
int SearchDetails(int transition, PropertyKind kind,
PropertyAttributes attributes, int* out_insertion_index);
int number_of_transitions() {
int number_of_transitions() const {
if (length() < kFirstIndex) return 0;
return Smi::ToInt(get(kTransitionLengthIndex));
}
@ -362,8 +365,9 @@ class TransitionArray : public FixedArray {
DISALLOW_IMPLICIT_CONSTRUCTORS(TransitionArray);
};
} // namespace internal
} // namespace v8
#include "src/objects/object-macros-undef.h"
#endif // V8_TRANSITIONS_H_

View File

@ -2721,7 +2721,7 @@ TEST(SnapshotCreatorIncludeGlobalProxy) {
delete[] blob.data;
}
UNINITIALIZED_TEST(ReinitializeStringHashSeedNotRehashable) {
UNINITIALIZED_TEST(ReinitializeHashSeedNotRehashable) {
DisableAlwaysOpt();
i::FLAG_rehash_snapshot = true;
i::FLAG_hash_seed = 42;
@ -2734,13 +2734,12 @@ UNINITIALIZED_TEST(ReinitializeStringHashSeedNotRehashable) {
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
// Create dictionary mode object.
// Create an object with an ordered hash table.
CompileRun(
"var a = {};"
"a.b = 1;"
"a.c = 2;"
"delete a.b;");
ExpectInt32("a.c", 2);
"var m = new Map();"
"m.set('a', 1);"
"m.set('b', 2);");
ExpectInt32("m.get('b')", 2);
creator.SetDefaultContext(context);
}
blob =
@ -2760,7 +2759,74 @@ UNINITIALIZED_TEST(ReinitializeStringHashSeedNotRehashable) {
v8::Local<v8::Context> context = v8::Context::New(isolate);
CHECK(!context.IsEmpty());
v8::Context::Scope context_scope(context);
ExpectInt32("a.c", 2);
ExpectInt32("m.get('b')", 2);
}
isolate->Dispose();
delete[] blob.data;
}
UNINITIALIZED_TEST(ReinitializeHashSeedRehashable) {
DisableAlwaysOpt();
i::FLAG_rehash_snapshot = true;
i::FLAG_hash_seed = 42;
i::FLAG_allow_natives_syntax = true;
v8::StartupData blob;
{
v8::SnapshotCreator creator;
v8::Isolate* isolate = creator.GetIsolate();
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
// Create dictionary mode object.
CompileRun(
"var a = new Array(10000);"
"%NormalizeElements(a);"
"a[133] = 1;"
"a[177] = 2;"
"a[971] = 3;"
"a[7997] = 4;"
"a[2111] = 5;"
"var o = {};"
"%OptimizeObjectForAddingMultipleProperties(o, 3);"
"o.a = 1;"
"o.b = 2;"
"o.c = 3;");
i::Handle<i::Object> i_a = v8::Utils::OpenHandle(*CompileRun("a"));
i::Handle<i::Object> i_o = v8::Utils::OpenHandle(*CompileRun("o"));
CHECK(i_a->IsJSArray());
CHECK(i_a->IsJSObject());
CHECK(!i::Handle<i::JSArray>::cast(i_a)->HasFastElements());
CHECK(!i::Handle<i::JSObject>::cast(i_o)->HasFastProperties());
ExpectInt32("a[2111]", 5);
ExpectInt32("o.c", 3);
creator.SetDefaultContext(context);
}
blob =
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
}
i::FLAG_hash_seed = 1337;
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
create_params.snapshot_blob = &blob;
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
// Check that rehashing has been performed.
CHECK_EQ(1337, reinterpret_cast<i::Isolate*>(isolate)->heap()->HashSeed());
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);
i::Handle<i::Object> i_a = v8::Utils::OpenHandle(*CompileRun("a"));
i::Handle<i::Object> i_o = v8::Utils::OpenHandle(*CompileRun("o"));
CHECK(i_a->IsJSArray());
CHECK(i_a->IsJSObject());
CHECK(!i::Handle<i::JSArray>::cast(i_a)->HasFastElements());
CHECK(!i::Handle<i::JSObject>::cast(i_o)->HasFastProperties());
ExpectInt32("a[2111]", 5);
ExpectInt32("o.c", 3);
}
isolate->Dispose();
delete[] blob.data;

View File

@ -202,74 +202,77 @@ KNOWN_MAPS = {
0x02de1: (148, "FixedDoubleArrayMap"),
0x02e31: (134, "MutableHeapNumberMap"),
0x02e81: (172, "OrderedHashTableMap"),
0x02ed1: (171, "SloppyArgumentsElementsMap"),
0x02f21: (180, "SmallOrderedHashMapMap"),
0x02f71: (181, "SmallOrderedHashSetMap"),
0x02fc1: (189, "JSMessageObjectMap"),
0x03011: (137, "BytecodeArrayMap"),
0x03061: (171, "ModuleInfoMap"),
0x030b1: (177, "NoClosuresCellMap"),
0x03101: (177, "OneClosureCellMap"),
0x03151: (177, "ManyClosuresCellMap"),
0x031a1: (175, "PropertyArrayMap"),
0x031f1: (130, "BigIntMap"),
0x03241: (64, "StringMap"),
0x03291: (73, "ConsOneByteStringMap"),
0x032e1: (65, "ConsStringMap"),
0x03331: (77, "ThinOneByteStringMap"),
0x03381: (69, "ThinStringMap"),
0x033d1: (67, "SlicedStringMap"),
0x03421: (75, "SlicedOneByteStringMap"),
0x03471: (66, "ExternalStringMap"),
0x034c1: (82, "ExternalStringWithOneByteDataMap"),
0x03511: (74, "ExternalOneByteStringMap"),
0x03561: (98, "ShortExternalStringMap"),
0x035b1: (114, "ShortExternalStringWithOneByteDataMap"),
0x03601: (0, "InternalizedStringMap"),
0x03651: (2, "ExternalInternalizedStringMap"),
0x036a1: (18, "ExternalInternalizedStringWithOneByteDataMap"),
0x036f1: (10, "ExternalOneByteInternalizedStringMap"),
0x03741: (34, "ShortExternalInternalizedStringMap"),
0x03791: (50, "ShortExternalInternalizedStringWithOneByteDataMap"),
0x037e1: (42, "ShortExternalOneByteInternalizedStringMap"),
0x03831: (106, "ShortExternalOneByteStringMap"),
0x03881: (140, "FixedUint8ArrayMap"),
0x038d1: (139, "FixedInt8ArrayMap"),
0x03921: (142, "FixedUint16ArrayMap"),
0x03971: (141, "FixedInt16ArrayMap"),
0x039c1: (144, "FixedUint32ArrayMap"),
0x03a11: (143, "FixedInt32ArrayMap"),
0x03a61: (145, "FixedFloat32ArrayMap"),
0x03ab1: (146, "FixedFloat64ArrayMap"),
0x03b01: (147, "FixedUint8ClampedArrayMap"),
0x03b51: (158, "ScriptMap"),
0x03ba1: (182, "CodeDataContainerMap"),
0x03bf1: (173, "FeedbackVectorMap"),
0x03c41: (171, "DebugEvaluateContextMap"),
0x03c91: (171, "ScriptContextTableMap"),
0x03ce1: (172, "UnseededNumberDictionaryMap"),
0x03d31: (192, "ExternalMap"),
0x03d81: (106, "NativeSourceStringMap"),
0x03dd1: (165, "Tuple2Map"),
0x03e21: (153, "InterceptorInfoMap"),
0x03e71: (150, "AccessorInfoMap"),
0x03ec1: (151, "AccessorPairMap"),
0x03f11: (152, "AccessCheckInfoMap"),
0x03f61: (154, "FunctionTemplateInfoMap"),
0x03fb1: (155, "ObjectTemplateInfoMap"),
0x04001: (156, "AllocationSiteMap"),
0x04051: (157, "AllocationMementoMap"),
0x040a1: (159, "AliasedArgumentsEntryMap"),
0x040f1: (160, "PromiseResolveThenableJobInfoMap"),
0x04141: (161, "PromiseReactionJobInfoMap"),
0x04191: (162, "DebugInfoMap"),
0x041e1: (163, "StackFrameInfoMap"),
0x04231: (164, "PrototypeInfoMap"),
0x04281: (166, "Tuple3Map"),
0x042d1: (167, "ContextExtensionMap"),
0x04321: (168, "ModuleMap"),
0x04371: (169, "ModuleInfoEntryMap"),
0x043c1: (170, "AsyncGeneratorRequestMap"),
0x02ed1: (172, "NameDictionaryMap"),
0x02f21: (172, "GlobalDictionaryMap"),
0x02f71: (172, "SeededNumberDictionaryMap"),
0x02fc1: (172, "UnseededNumberDictionaryMap"),
0x03011: (171, "SloppyArgumentsElementsMap"),
0x03061: (180, "SmallOrderedHashMapMap"),
0x030b1: (181, "SmallOrderedHashSetMap"),
0x03101: (189, "JSMessageObjectMap"),
0x03151: (137, "BytecodeArrayMap"),
0x031a1: (171, "ModuleInfoMap"),
0x031f1: (177, "NoClosuresCellMap"),
0x03241: (177, "OneClosureCellMap"),
0x03291: (177, "ManyClosuresCellMap"),
0x032e1: (175, "PropertyArrayMap"),
0x03331: (130, "BigIntMap"),
0x03381: (64, "StringMap"),
0x033d1: (73, "ConsOneByteStringMap"),
0x03421: (65, "ConsStringMap"),
0x03471: (77, "ThinOneByteStringMap"),
0x034c1: (69, "ThinStringMap"),
0x03511: (67, "SlicedStringMap"),
0x03561: (75, "SlicedOneByteStringMap"),
0x035b1: (66, "ExternalStringMap"),
0x03601: (82, "ExternalStringWithOneByteDataMap"),
0x03651: (74, "ExternalOneByteStringMap"),
0x036a1: (98, "ShortExternalStringMap"),
0x036f1: (114, "ShortExternalStringWithOneByteDataMap"),
0x03741: (0, "InternalizedStringMap"),
0x03791: (2, "ExternalInternalizedStringMap"),
0x037e1: (18, "ExternalInternalizedStringWithOneByteDataMap"),
0x03831: (10, "ExternalOneByteInternalizedStringMap"),
0x03881: (34, "ShortExternalInternalizedStringMap"),
0x038d1: (50, "ShortExternalInternalizedStringWithOneByteDataMap"),
0x03921: (42, "ShortExternalOneByteInternalizedStringMap"),
0x03971: (106, "ShortExternalOneByteStringMap"),
0x039c1: (140, "FixedUint8ArrayMap"),
0x03a11: (139, "FixedInt8ArrayMap"),
0x03a61: (142, "FixedUint16ArrayMap"),
0x03ab1: (141, "FixedInt16ArrayMap"),
0x03b01: (144, "FixedUint32ArrayMap"),
0x03b51: (143, "FixedInt32ArrayMap"),
0x03ba1: (145, "FixedFloat32ArrayMap"),
0x03bf1: (146, "FixedFloat64ArrayMap"),
0x03c41: (147, "FixedUint8ClampedArrayMap"),
0x03c91: (158, "ScriptMap"),
0x03ce1: (182, "CodeDataContainerMap"),
0x03d31: (173, "FeedbackVectorMap"),
0x03d81: (171, "DebugEvaluateContextMap"),
0x03dd1: (171, "ScriptContextTableMap"),
0x03e21: (192, "ExternalMap"),
0x03e71: (106, "NativeSourceStringMap"),
0x03ec1: (165, "Tuple2Map"),
0x03f11: (153, "InterceptorInfoMap"),
0x03f61: (150, "AccessorInfoMap"),
0x03fb1: (151, "AccessorPairMap"),
0x04001: (152, "AccessCheckInfoMap"),
0x04051: (154, "FunctionTemplateInfoMap"),
0x040a1: (155, "ObjectTemplateInfoMap"),
0x040f1: (156, "AllocationSiteMap"),
0x04141: (157, "AllocationMementoMap"),
0x04191: (159, "AliasedArgumentsEntryMap"),
0x041e1: (160, "PromiseResolveThenableJobInfoMap"),
0x04231: (161, "PromiseReactionJobInfoMap"),
0x04281: (162, "DebugInfoMap"),
0x042d1: (163, "StackFrameInfoMap"),
0x04321: (164, "PrototypeInfoMap"),
0x04371: (166, "Tuple3Map"),
0x043c1: (167, "ContextExtensionMap"),
0x04411: (168, "ModuleMap"),
0x04461: (169, "ModuleInfoEntryMap"),
0x044b1: (170, "AsyncGeneratorRequestMap"),
}
# List of known V8 objects.