From d3616887ffe09d00b48a82f1fbb1754fadcf73d5 Mon Sep 17 00:00:00 2001 From: Jakob Kummerow Date: Tue, 11 Aug 2015 11:59:52 +0200 Subject: [PATCH] Version 4.5.103.22 (cherry-pick) Merged 1e65e201894791c3bf8473728021f0e548a0543b Merged 6ea0d55dfb9306200b86e057ac74528321ca709b Fasterify JSObject::UnregisterPrototypeUser Fasterify ICSlotCache BUG=chromium:517406,chromium:517406,chromium:517778 LOG=N R=hablich@chromium.org Review URL: https://codereview.chromium.org/1282143003 . Cr-Commit-Position: refs/branch-heads/4.5@{#24} Cr-Branched-From: 7f211533faba9dd85708b1394186c7fe99b88392-refs/heads/4.5.103@{#1} Cr-Branched-From: 4b38c15817033ccd9a65efbb3d038ae2423293c2-refs/heads/master@{#29527} --- include/v8-version.h | 2 +- src/ast-numbering.cc | 2 +- src/ast.cc | 13 +-- src/ast.h | 28 +++-- src/factory.cc | 1 + src/hashmap.h | 8 +- src/objects-inl.h | 1 + src/objects-printer.cc | 1 + src/objects.cc | 236 ++++++++++++++++++++------------------ src/objects.h | 40 +++++-- src/snapshot/serialize.cc | 21 +++- test/cctest/test-heap.cc | 2 +- 12 files changed, 203 insertions(+), 152 deletions(-) diff --git a/include/v8-version.h b/include/v8-version.h index 3a236612236..c4333c7160a 100644 --- a/include/v8-version.h +++ b/include/v8-version.h @@ -11,7 +11,7 @@ #define V8_MAJOR_VERSION 4 #define V8_MINOR_VERSION 5 #define V8_BUILD_NUMBER 103 -#define V8_PATCH_LEVEL 21 +#define V8_PATCH_LEVEL 22 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/src/ast-numbering.cc b/src/ast-numbering.cc index ab8a6b281c4..151cc8abc10 100644 --- a/src/ast-numbering.cc +++ b/src/ast-numbering.cc @@ -18,7 +18,7 @@ class AstNumberingVisitor final : public AstVisitor { : AstVisitor(), next_id_(BailoutId::FirstUsable().ToInt()), properties_(zone), - ic_slot_cache_(4), + ic_slot_cache_(zone), dont_optimize_reason_(kNoReason) { InitializeAstVisitor(isolate, zone); } diff --git a/src/ast.cc b/src/ast.cc index 8ce5e7e29c9..e52504a86fb 100644 --- a/src/ast.cc +++ b/src/ast.cc @@ -96,7 +96,7 @@ void VariableProxy::SetFirstFeedbackICSlot(FeedbackVectorICSlot slot, ICSlotCache* cache) { variable_feedback_slot_ = slot; if (var()->IsUnallocatedOrGlobalSlot()) { - cache->Add(VariableICSlotPair(var(), slot)); + cache->Put(var(), slot); } } @@ -107,12 +107,11 @@ FeedbackVectorRequirements VariableProxy::ComputeFeedbackRequirements( // VariableProxies that point to the same Variable within a function can // make their loads from the same IC slot. if (var()->IsUnallocatedOrGlobalSlot()) { - for (int i = 0; i < cache->length(); i++) { - VariableICSlotPair& pair = cache->at(i); - if (pair.variable() == var()) { - variable_feedback_slot_ = pair.slot(); - return FeedbackVectorRequirements(0, 0); - } + ZoneHashMap::Entry* entry = cache->Get(var()); + if (entry != NULL) { + variable_feedback_slot_ = FeedbackVectorICSlot( + static_cast(reinterpret_cast(entry->value))); + return FeedbackVectorRequirements(0, 0); } } return FeedbackVectorRequirements(0, 1); diff --git a/src/ast.h b/src/ast.h index e5842c292e5..115c59ff80e 100644 --- a/src/ast.h +++ b/src/ast.h @@ -154,25 +154,29 @@ class FeedbackVectorRequirements { }; -class VariableICSlotPair final { +class ICSlotCache { public: - VariableICSlotPair(Variable* variable, FeedbackVectorICSlot slot) - : variable_(variable), slot_(slot) {} - VariableICSlotPair() - : variable_(NULL), slot_(FeedbackVectorICSlot::Invalid()) {} + explicit ICSlotCache(Zone* zone) + : zone_(zone), + hash_map_(HashMap::PointersMatch, ZoneHashMap::kDefaultHashMapCapacity, + ZoneAllocationPolicy(zone)) {} - Variable* variable() const { return variable_; } - FeedbackVectorICSlot slot() const { return slot_; } + void Put(Variable* variable, FeedbackVectorICSlot slot) { + ZoneHashMap::Entry* entry = hash_map_.LookupOrInsert( + variable, ComputePointerHash(variable), ZoneAllocationPolicy(zone_)); + entry->value = reinterpret_cast(slot.ToInt()); + } + + ZoneHashMap::Entry* Get(Variable* variable) const { + return hash_map_.Lookup(variable, ComputePointerHash(variable)); + } private: - Variable* variable_; - FeedbackVectorICSlot slot_; + Zone* zone_; + ZoneHashMap hash_map_; }; -typedef List ICSlotCache; - - class AstProperties final BASE_EMBEDDED { public: class Flags : public EnumSet {}; diff --git a/src/factory.cc b/src/factory.cc index 2b79a5bf8f2..f0967c72000 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -55,6 +55,7 @@ Handle Factory::NewPrototypeInfo() { Handle result = Handle::cast(NewStruct(PROTOTYPE_INFO_TYPE)); result->set_prototype_users(WeakFixedArray::Empty()); + result->set_registry_slot(PrototypeInfo::UNREGISTERED); result->set_validity_cell(Smi::FromInt(0)); result->set_constructor_name(Smi::FromInt(0)); return result; diff --git a/src/hashmap.h b/src/hashmap.h index 688fc2cf369..ee3797fe594 100644 --- a/src/hashmap.h +++ b/src/hashmap.h @@ -43,7 +43,7 @@ class TemplateHashMapImpl { // If an entry with matching key is found, returns that entry. // Otherwise, NULL is returned. - Entry* Lookup(void* key, uint32_t hash); + Entry* Lookup(void* key, uint32_t hash) const; // If an entry with matching key is found, returns that entry. // If no matching entry is found, a new entry is inserted with @@ -90,7 +90,7 @@ class TemplateHashMapImpl { uint32_t occupancy_; Entry* map_end() const { return map_ + capacity_; } - Entry* Probe(void* key, uint32_t hash); + Entry* Probe(void* key, uint32_t hash) const; void Initialize(uint32_t capacity, AllocationPolicy allocator); void Resize(AllocationPolicy allocator); }; @@ -113,7 +113,7 @@ TemplateHashMapImpl::~TemplateHashMapImpl() { template typename TemplateHashMapImpl::Entry* -TemplateHashMapImpl::Lookup(void* key, uint32_t hash) { +TemplateHashMapImpl::Lookup(void* key, uint32_t hash) const { Entry* p = Probe(key, hash); return p->key != NULL ? p : NULL; } @@ -242,7 +242,7 @@ typename TemplateHashMapImpl::Entry* template typename TemplateHashMapImpl::Entry* -TemplateHashMapImpl::Probe(void* key, uint32_t hash) { +TemplateHashMapImpl::Probe(void* key, uint32_t hash) const { DCHECK(key != NULL); DCHECK(base::bits::IsPowerOfTwo32(capacity_)); diff --git a/src/objects-inl.h b/src/objects-inl.h index 0e43e1d3a0f..fbc2c4ee76b 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -5066,6 +5066,7 @@ ACCESSORS(ExecutableAccessorInfo, data, Object, kDataOffset) ACCESSORS(Box, value, Object, kValueOffset) ACCESSORS(PrototypeInfo, prototype_users, Object, kPrototypeUsersOffset) +SMI_ACCESSORS(PrototypeInfo, registry_slot, kRegistrySlotOffset) ACCESSORS(PrototypeInfo, validity_cell, Object, kValidityCellOffset) ACCESSORS(PrototypeInfo, constructor_name, Object, kConstructorNameOffset) diff --git a/src/objects-printer.cc b/src/objects-printer.cc index 1dcc3d53948..b514a9443f2 100644 --- a/src/objects-printer.cc +++ b/src/objects-printer.cc @@ -849,6 +849,7 @@ void Box::BoxPrint(std::ostream& os) { // NOLINT void PrototypeInfo::PrototypeInfoPrint(std::ostream& os) { // NOLINT HeapObject::PrintHeader(os, "PrototypeInfo"); os << "\n - prototype users: " << Brief(prototype_users()); + os << "\n - registry slot: " << registry_slot(); os << "\n - validity cell: " << Brief(validity_cell()); os << "\n - constructor name: " << Brief(constructor_name()); os << "\n"; diff --git a/src/objects.cc b/src/objects.cc index e5aafeacb43..2b042fda8e6 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -1760,6 +1760,33 @@ bool Map::InstancesNeedRewriting(Map* target, int target_number_of_fields, } +static void UpdatePrototypeUserRegistration(Handle old_map, + Handle new_map, + Isolate* isolate) { + if (!FLAG_track_prototype_users) return; + if (!old_map->is_prototype_map()) return; + DCHECK(new_map->is_prototype_map()); + bool was_registered = JSObject::UnregisterPrototypeUser(old_map, isolate); + new_map->set_prototype_info(old_map->prototype_info()); + old_map->set_prototype_info(Smi::FromInt(0)); + if (FLAG_trace_prototype_users) { + PrintF("Moving prototype_info %p from map %p to map %p.\n", + reinterpret_cast(new_map->prototype_info()), + reinterpret_cast(*old_map), + reinterpret_cast(*new_map)); + } + if (was_registered) { + if (new_map->prototype_info()->IsPrototypeInfo()) { + // The new map isn't registered with its prototype yet; reflect this fact + // in the PrototypeInfo it just inherited from the old map. + PrototypeInfo::cast(new_map->prototype_info()) + ->set_registry_slot(PrototypeInfo::UNREGISTERED); + } + JSObject::LazyRegisterPrototypeUser(new_map, isolate); + } +} + + void JSObject::MigrateToMap(Handle object, Handle new_map, int expected_additional_properties) { if (object->map() == *new_map) return; @@ -1773,16 +1800,7 @@ void JSObject::MigrateToMap(Handle object, Handle new_map, // when a map on a prototype chain is registered with its prototype, then // all prototypes further up the chain are also registered with their // respective prototypes. - Object* maybe_old_prototype = old_map->prototype(); - if (FLAG_track_prototype_users && old_map->is_prototype_map() && - maybe_old_prototype->IsJSObject()) { - Handle old_prototype(JSObject::cast(maybe_old_prototype)); - bool was_registered = - JSObject::UnregisterPrototypeUser(old_prototype, old_map); - if (was_registered) { - JSObject::LazyRegisterPrototypeUser(new_map, new_map->GetIsolate()); - } - } + UpdatePrototypeUserRegistration(old_map, new_map, new_map->GetIsolate()); if (object->HasFastProperties()) { if (!new_map->is_dictionary_map()) { @@ -1815,20 +1833,7 @@ void JSObject::MigrateToMap(Handle object, Handle new_map, // state now: the new map might have a new elements_kind, but the object's // elements pointer hasn't been updated yet. Callers will fix this, but in // the meantime, (indirectly) calling JSObjectVerify() must be avoided. - DisallowHeapAllocation no_object_verification; - - if (old_map->is_prototype_map() && FLAG_track_prototype_users) { - DCHECK(new_map->is_prototype_map()); - DCHECK(object->map() == *new_map); - new_map->set_prototype_info(old_map->prototype_info()); - old_map->set_prototype_info(Smi::FromInt(0)); - if (FLAG_trace_prototype_users) { - PrintF("Moving prototype_info %p from map %p to map %p.\n", - reinterpret_cast(new_map->prototype_info()), - reinterpret_cast(*old_map), - reinterpret_cast(*new_map)); - } - } + // When adding code here, add a DisallowHeapAllocation too. } @@ -4654,27 +4659,7 @@ void JSObject::MigrateSlowToFast(Handle object, Handle new_map = Map::CopyDropDescriptors(old_map); new_map->set_dictionary_map(false); - if (old_map->is_prototype_map() && FLAG_track_prototype_users) { - DCHECK(new_map->is_prototype_map()); - - Object* maybe_old_prototype = old_map->prototype(); - if (maybe_old_prototype->IsJSObject()) { - Handle old_prototype(JSObject::cast(maybe_old_prototype)); - bool was_registered = - JSObject::UnregisterPrototypeUser(old_prototype, old_map); - if (was_registered) { - JSObject::LazyRegisterPrototypeUser(new_map, isolate); - } - } - new_map->set_prototype_info(old_map->prototype_info()); - old_map->set_prototype_info(Smi::FromInt(0)); - if (FLAG_trace_prototype_users) { - PrintF("Moving prototype_info %p from map %p to map %p.\n", - reinterpret_cast(new_map->prototype_info()), - reinterpret_cast(*old_map), - reinterpret_cast(*new_map)); - } - } + UpdatePrototypeUserRegistration(old_map, new_map, isolate); #if TRACE_MAPS if (FLAG_trace_maps) { @@ -8068,59 +8053,46 @@ void WeakFixedArray::Set(Handle array, int index, // static -Handle WeakFixedArray::Add( - Handle maybe_array, Handle value, - SearchForDuplicates search_for_duplicates, bool* was_present) { +Handle WeakFixedArray::Add(Handle maybe_array, + Handle value, + int* assigned_index) { Handle array = (maybe_array.is_null() || !maybe_array->IsWeakFixedArray()) ? Allocate(value->GetIsolate(), 1, Handle::null()) : Handle::cast(maybe_array); - if (was_present != NULL) *was_present = false; - if (search_for_duplicates == kAddIfNotFound) { - for (int i = 0; i < array->Length(); ++i) { - if (array->Get(i) == *value) { - if (was_present != NULL) *was_present = true; - return array; - } - } -#if 0 // Enable this if you want to check your search_for_duplicates flags. - } else { - for (int i = 0; i < array->Length(); ++i) { - DCHECK_NE(*value, array->Get(i)); - } -#endif - } - // Try to store the new entry if there's room. Optimize for consecutive // accesses. int first_index = array->last_used_index(); - if (array->Length() > 0) { + int length = array->Length(); + if (length > 0) { for (int i = first_index;;) { if (array->IsEmptySlot((i))) { WeakFixedArray::Set(array, i, value); + if (assigned_index != NULL) *assigned_index = i; return array; } if (FLAG_trace_weak_arrays) { PrintF("[WeakFixedArray: searching for free slot]\n"); } - i = (i + 1) % array->Length(); + i = (i + 1) % length; if (i == first_index) break; } } // No usable slot found, grow the array. - int new_length = - array->Length() == 0 ? 1 : array->Length() + (array->Length() >> 1) + 4; + int new_length = length == 0 ? 1 : length + (length >> 1) + 4; Handle new_array = Allocate(array->GetIsolate(), new_length, array); if (FLAG_trace_weak_arrays) { PrintF("[WeakFixedArray: growing to size %d ]\n", new_length); } - WeakFixedArray::Set(new_array, array->Length(), value); + WeakFixedArray::Set(new_array, length, value); + if (assigned_index != NULL) *assigned_index = length; return new_array; } +template void WeakFixedArray::Compact() { FixedArray* array = FixedArray::cast(this); int new_length = kFirstIndex; @@ -8128,6 +8100,9 @@ void WeakFixedArray::Compact() { Object* element = array->get(i); if (element->IsSmi()) continue; if (WeakCell::cast(element)->cleared()) continue; + Object* value = WeakCell::cast(element)->value(); + CompactionCallback::Callback(value, i - kFirstIndex, + new_length - kFirstIndex); array->set(new_length++, element); } array->Shrink(new_length); @@ -8135,6 +8110,23 @@ void WeakFixedArray::Compact() { } +void JSObject::PrototypeRegistryCompactionCallback::Callback(Object* value, + int old_index, + int new_index) { + DCHECK(value->IsMap() && Map::cast(value)->is_prototype_map()); + Map* map = Map::cast(value); + DCHECK(map->prototype_info()->IsPrototypeInfo()); + PrototypeInfo* proto_info = PrototypeInfo::cast(map->prototype_info()); + DCHECK_EQ(old_index, proto_info->registry_slot()); + proto_info->set_registry_slot(new_index); +} + + +template void WeakFixedArray::Compact(); +template void +WeakFixedArray::Compact(); + + bool WeakFixedArray::Remove(Handle value) { if (Length() == 0) return false; // Optimize for the most recently added element to be removed again. @@ -8142,8 +8134,7 @@ bool WeakFixedArray::Remove(Handle value) { for (int i = first_index;;) { if (Get(i) == *value) { Clear(i); - // Users of WeakFixedArray should make sure that there are no duplicates, - // they can use Add(..., kAddIfNotFound) if necessary. + // Users of WeakFixedArray should make sure that there are no duplicates. return true; } i = (i + 1) % Length(); @@ -9861,67 +9852,74 @@ void JSObject::LazyRegisterPrototypeUser(Handle user, Isolate* isolate) { DCHECK(user->is_prototype_map()); Handle current_user = user; + Handle current_user_info = + Map::GetOrCreatePrototypeInfo(user, isolate); for (PrototypeIterator iter(user); !iter.IsAtEnd(); iter.Advance()) { + // Walk up the prototype chain as far as links haven't been registered yet. + if (current_user_info->registry_slot() != PrototypeInfo::UNREGISTERED) { + break; + } Handle maybe_proto = PrototypeIterator::GetCurrent(iter); if (maybe_proto->IsJSGlobalProxy()) continue; // Proxies on the prototype chain are not supported. if (maybe_proto->IsJSProxy()) return; Handle proto = Handle::cast(maybe_proto); - bool just_registered = - RegisterPrototypeUserIfNotRegistered(proto, current_user, isolate); - // Walk up the prototype chain as far as links haven't been registered yet. - if (!just_registered) break; - current_user = handle(proto->map(), isolate); - } -} - + Handle proto_info = + Map::GetOrCreatePrototypeInfo(proto, isolate); + Handle maybe_registry(proto_info->prototype_users(), isolate); + int slot = 0; + Handle new_array = + WeakFixedArray::Add(maybe_registry, current_user, &slot); + current_user_info->set_registry_slot(slot); + if (!maybe_registry.is_identical_to(new_array)) { + proto_info->set_prototype_users(*new_array); + } + if (FLAG_trace_prototype_users) { + PrintF("Registering %p as a user of prototype %p (map=%p).\n", + reinterpret_cast(*current_user), + reinterpret_cast(*proto), + reinterpret_cast(proto->map())); + } -// Returns true if the user was not yet registered. -// static -bool JSObject::RegisterPrototypeUserIfNotRegistered(Handle prototype, - Handle user, - Isolate* isolate) { - Handle proto_info = - Map::GetOrCreatePrototypeInfo(prototype, isolate); - Handle maybe_registry(proto_info->prototype_users(), isolate); - bool was_present = false; - Handle new_array = WeakFixedArray::Add( - maybe_registry, user, WeakFixedArray::kAddIfNotFound, &was_present); - if (!maybe_registry.is_identical_to(new_array)) { - proto_info->set_prototype_users(*new_array); - } - if (FLAG_trace_prototype_users && !was_present) { - PrintF("Registering %p as a user of prototype %p (map=%p).\n", - reinterpret_cast(*user), reinterpret_cast(*prototype), - reinterpret_cast(prototype->map())); + current_user = handle(proto->map(), isolate); + current_user_info = proto_info; } - return !was_present; } // Can be called regardless of whether |user| was actually registered with // |prototype|. Returns true when there was a registration. // static -bool JSObject::UnregisterPrototypeUser(Handle prototype, - Handle user) { - Isolate* isolate = prototype->GetIsolate(); +bool JSObject::UnregisterPrototypeUser(Handle user, Isolate* isolate) { + DCHECK(user->is_prototype_map()); + // If it doesn't have a PrototypeInfo, it was never registered. + if (!user->prototype_info()->IsPrototypeInfo()) return false; + // If it doesn't have a prototype, it can't be registered. + if (!user->prototype()->IsJSObject()) return false; + Handle prototype(JSObject::cast(user->prototype()), isolate); + Handle user_info = + Map::GetOrCreatePrototypeInfo(user, isolate); + int slot = user_info->registry_slot(); + if (slot == PrototypeInfo::UNREGISTERED) return false; if (prototype->IsJSGlobalProxy()) { PrototypeIterator iter(isolate, prototype); prototype = Handle::cast(PrototypeIterator::GetCurrent(iter)); } DCHECK(prototype->map()->is_prototype_map()); Object* maybe_proto_info = prototype->map()->prototype_info(); - if (!maybe_proto_info->IsPrototypeInfo()) return false; + // User knows its registry slot, prototype info and user registry must exist. + DCHECK(maybe_proto_info->IsPrototypeInfo()); Handle proto_info(PrototypeInfo::cast(maybe_proto_info), isolate); Object* maybe_registry = proto_info->prototype_users(); - if (!maybe_registry->IsWeakFixedArray()) return false; - bool result = WeakFixedArray::cast(maybe_registry)->Remove(user); - if (FLAG_trace_prototype_users && result) { + DCHECK(maybe_registry->IsWeakFixedArray()); + DCHECK(WeakFixedArray::cast(maybe_registry)->Get(slot) == *user); + WeakFixedArray::cast(maybe_registry)->Clear(slot); + if (FLAG_trace_prototype_users) { PrintF("Unregistering %p as a user of prototype %p.\n", reinterpret_cast(*user), reinterpret_cast(*prototype)); } - return result; + return true; } @@ -9982,6 +9980,19 @@ Handle Map::GetOrCreatePrototypeInfo(Handle prototype, } +// static +Handle Map::GetOrCreatePrototypeInfo(Handle prototype_map, + Isolate* isolate) { + Object* maybe_proto_info = prototype_map->prototype_info(); + if (maybe_proto_info->IsPrototypeInfo()) { + return handle(PrototypeInfo::cast(maybe_proto_info), isolate); + } + Handle proto_info = isolate->factory()->NewPrototypeInfo(); + prototype_map->set_prototype_info(*proto_info); + return proto_info; +} + + // static Handle Map::GetOrCreatePrototypeChainValidityCell(Handle map, Isolate* isolate) { @@ -10473,13 +10484,14 @@ void SharedFunctionInfo::SetScript(Handle shared, Handle