Fasterify JSObject::UnregisterPrototypeUser

When a (prototype) map registers as a user of its own prototype, it now remembers the index in that prototype's registry where it is listed.
This remembered index is used on un-registration to find the right slot to clear without walking the entire registry.
Compaction of the registry must update all entries' remembered indices.

BUG=chromium:517778,chromium:517406
LOG=n
R=yangguo@chromium.org

Review URL: https://codereview.chromium.org/1276353004

Cr-Commit-Position: refs/heads/master@{#30079}
This commit is contained in:
jkummerow 2015-08-08 15:56:15 -07:00 committed by Commit bot
parent 6ea0d55dfb
commit 1e65e20189
7 changed files with 175 additions and 127 deletions

View File

@ -55,6 +55,7 @@ Handle<PrototypeInfo> Factory::NewPrototypeInfo() {
Handle<PrototypeInfo> result =
Handle<PrototypeInfo>::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;

View File

@ -4963,6 +4963,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)

View File

@ -967,6 +967,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";

View File

@ -1895,6 +1895,33 @@ bool Map::InstancesNeedRewriting(Map* target, int target_number_of_fields,
}
static void UpdatePrototypeUserRegistration(Handle<Map> old_map,
Handle<Map> 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<void*>(new_map->prototype_info()),
reinterpret_cast<void*>(*old_map),
reinterpret_cast<void*>(*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<JSObject> object, Handle<Map> new_map,
int expected_additional_properties) {
if (object->map() == *new_map) return;
@ -1908,16 +1935,7 @@ void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> 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<JSObject> 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()) {
@ -1950,20 +1968,7 @@ void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> 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<void*>(new_map->prototype_info()),
reinterpret_cast<void*>(*old_map),
reinterpret_cast<void*>(*new_map));
}
}
// When adding code here, add a DisallowHeapAllocation too.
}
@ -4769,27 +4774,7 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object,
Handle<Map> 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<JSObject> 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<void*>(new_map->prototype_info()),
reinterpret_cast<void*>(*old_map),
reinterpret_cast<void*>(*new_map));
}
}
UpdatePrototypeUserRegistration(old_map, new_map, isolate);
#if TRACE_MAPS
if (FLAG_trace_maps) {
@ -7938,59 +7923,46 @@ void WeakFixedArray::Set(Handle<WeakFixedArray> array, int index,
// static
Handle<WeakFixedArray> WeakFixedArray::Add(
Handle<Object> maybe_array, Handle<HeapObject> value,
SearchForDuplicates search_for_duplicates, bool* was_present) {
Handle<WeakFixedArray> WeakFixedArray::Add(Handle<Object> maybe_array,
Handle<HeapObject> value,
int* assigned_index) {
Handle<WeakFixedArray> array =
(maybe_array.is_null() || !maybe_array->IsWeakFixedArray())
? Allocate(value->GetIsolate(), 1, Handle<WeakFixedArray>::null())
: Handle<WeakFixedArray>::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<WeakFixedArray> 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 <class CompactionCallback>
void WeakFixedArray::Compact() {
FixedArray* array = FixedArray::cast(this);
int new_length = kFirstIndex;
@ -7998,6 +7970,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);
@ -8005,6 +7980,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<WeakFixedArray::NullCallback>();
template void
WeakFixedArray::Compact<JSObject::PrototypeRegistryCompactionCallback>();
bool WeakFixedArray::Remove(Handle<HeapObject> value) {
if (Length() == 0) return false;
// Optimize for the most recently added element to be removed again.
@ -8012,8 +8004,7 @@ bool WeakFixedArray::Remove(Handle<HeapObject> 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();
@ -9758,67 +9749,74 @@ void JSObject::LazyRegisterPrototypeUser(Handle<Map> user, Isolate* isolate) {
DCHECK(user->is_prototype_map());
Handle<Map> current_user = user;
Handle<PrototypeInfo> 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<Object> maybe_proto = PrototypeIterator::GetCurrent(iter);
if (maybe_proto->IsJSGlobalProxy()) continue;
// Proxies on the prototype chain are not supported.
if (maybe_proto->IsJSProxy()) return;
Handle<JSObject> proto = Handle<JSObject>::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;
Handle<PrototypeInfo> proto_info =
Map::GetOrCreatePrototypeInfo(proto, isolate);
Handle<Object> maybe_registry(proto_info->prototype_users(), isolate);
int slot = 0;
Handle<WeakFixedArray> 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<void*>(*current_user),
reinterpret_cast<void*>(*proto),
reinterpret_cast<void*>(proto->map()));
}
current_user = handle(proto->map(), isolate);
current_user_info = proto_info;
}
}
// Returns true if the user was not yet registered.
// static
bool JSObject::RegisterPrototypeUserIfNotRegistered(Handle<JSObject> prototype,
Handle<HeapObject> user,
Isolate* isolate) {
Handle<PrototypeInfo> proto_info =
Map::GetOrCreatePrototypeInfo(prototype, isolate);
Handle<Object> maybe_registry(proto_info->prototype_users(), isolate);
bool was_present = false;
Handle<WeakFixedArray> 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<void*>(*user), reinterpret_cast<void*>(*prototype),
reinterpret_cast<void*>(prototype->map()));
}
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<JSObject> prototype,
Handle<HeapObject> user) {
Isolate* isolate = prototype->GetIsolate();
bool JSObject::UnregisterPrototypeUser(Handle<Map> 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<JSObject> prototype(JSObject::cast(user->prototype()), isolate);
Handle<PrototypeInfo> 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<JSObject>::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<PrototypeInfo> 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<void*>(*user), reinterpret_cast<void*>(*prototype));
}
return result;
return true;
}
@ -9879,6 +9877,19 @@ Handle<PrototypeInfo> Map::GetOrCreatePrototypeInfo(Handle<JSObject> prototype,
}
// static
Handle<PrototypeInfo> Map::GetOrCreatePrototypeInfo(Handle<Map> 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<PrototypeInfo> proto_info = isolate->factory()->NewPrototypeInfo();
prototype_map->set_prototype_info(*proto_info);
return proto_info;
}
// static
Handle<Cell> Map::GetOrCreatePrototypeChainValidityCell(Handle<Map> map,
Isolate* isolate) {
@ -10370,13 +10381,14 @@ void SharedFunctionInfo::SetScript(Handle<SharedFunctionInfo> shared,
Handle<Script> script = Handle<Script>::cast(script_object);
Handle<Object> list(script->shared_function_infos(), shared->GetIsolate());
#ifdef DEBUG
bool found = false;
list = WeakFixedArray::Add(list, shared, WeakFixedArray::kAddIfNotFound,
&found);
CHECK(!found);
#else
list = WeakFixedArray::Add(list, shared, WeakFixedArray::kAlwaysAdd);
if (list->IsWeakFixedArray()) {
Handle<WeakFixedArray> array = Handle<WeakFixedArray>::cast(list);
for (int i = 0; i < array->Length(); ++i) {
DCHECK(array->Get(i) != *shared);
}
}
#endif // DEBUG
list = WeakFixedArray::Add(list, shared);
script->set_shared_function_infos(*list);
}
// Finally set new script.

View File

@ -1906,13 +1906,15 @@ class JSObject: public JSReceiver {
PrototypeOptimizationMode mode);
static void ReoptimizeIfPrototype(Handle<JSObject> object);
static void LazyRegisterPrototypeUser(Handle<Map> user, Isolate* isolate);
static bool RegisterPrototypeUserIfNotRegistered(Handle<JSObject> prototype,
Handle<HeapObject> user,
Isolate* isolate);
static bool UnregisterPrototypeUser(Handle<JSObject> prototype,
Handle<HeapObject> user);
static bool UnregisterPrototypeUser(Handle<Map> user, Isolate* isolate);
static void InvalidatePrototypeChains(Map* map);
// Alternative implementation of WeakFixedArray::NullCallback.
class PrototypeRegistryCompactionCallback {
public:
static void Callback(Object* value, int old_index, int new_index);
};
// Retrieve interceptors.
InterceptorInfo* GetNamedInterceptor();
InterceptorInfo* GetIndexedInterceptor();
@ -2531,17 +2533,22 @@ class FixedDoubleArray: public FixedArrayBase {
class WeakFixedArray : public FixedArray {
public:
enum SearchForDuplicates { kAlwaysAdd, kAddIfNotFound };
// If |maybe_array| is not a WeakFixedArray, a fresh one will be allocated.
static Handle<WeakFixedArray> Add(
Handle<Object> maybe_array, Handle<HeapObject> value,
SearchForDuplicates search_for_duplicates = kAlwaysAdd,
bool* was_present = NULL);
// This function does not check if the value exists already, callers must
// ensure this themselves if necessary.
static Handle<WeakFixedArray> Add(Handle<Object> maybe_array,
Handle<HeapObject> value,
int* assigned_index = NULL);
// Returns true if an entry was found and removed.
bool Remove(Handle<HeapObject> value);
class NullCallback {
public:
static void Callback(Object* value, int old_index, int new_index) {}
};
template <class CompactionCallback>
void Compact();
inline Object* Get(int index) const;
@ -5467,6 +5474,8 @@ class Map: public HeapObject {
// the given prototype's map).
static Handle<PrototypeInfo> GetOrCreatePrototypeInfo(
Handle<JSObject> prototype, Isolate* isolate);
static Handle<PrototypeInfo> GetOrCreatePrototypeInfo(
Handle<Map> prototype_map, Isolate* isolate);
// [prototype chain validity cell]: Associated with a prototype object,
// stored in that object's map's PrototypeInfo, indicates that prototype
@ -6036,9 +6045,15 @@ class Box : public Struct {
// Container for metadata stored on each prototype map.
class PrototypeInfo : public Struct {
public:
static const int UNREGISTERED = -1;
// [prototype_users]: WeakFixedArray containing maps using this prototype,
// or Smi(0) if uninitialized.
DECL_ACCESSORS(prototype_users, Object)
// [registry_slot]: Slot in prototype's user registry where this user
// is stored. Returns UNREGISTERED if this prototype has not been registered.
inline int registry_slot() const;
inline void set_registry_slot(int slot);
// [validity_cell]: Cell containing the validity bit for prototype chains
// going through this object, or Smi(0) if uninitialized.
DECL_ACCESSORS(validity_cell, Object)
@ -6052,7 +6067,8 @@ class PrototypeInfo : public Struct {
DECLARE_VERIFIER(PrototypeInfo)
static const int kPrototypeUsersOffset = HeapObject::kHeaderSize;
static const int kValidityCellOffset = kPrototypeUsersOffset + kPointerSize;
static const int kRegistrySlotOffset = kPrototypeUsersOffset + kPointerSize;
static const int kValidityCellOffset = kRegistrySlotOffset + kPointerSize;
static const int kConstructorNameOffset = kValidityCellOffset + kPointerSize;
static const int kSize = kConstructorNameOffset + kPointerSize;

View File

@ -1926,7 +1926,23 @@ void Serializer::ObjectSerializer::Serialize() {
if (object_->IsPrototypeInfo()) {
Object* prototype_users = PrototypeInfo::cast(object_)->prototype_users();
if (prototype_users->IsWeakFixedArray()) {
WeakFixedArray::cast(prototype_users)->Compact();
WeakFixedArray* array = WeakFixedArray::cast(prototype_users);
array->Compact<JSObject::PrototypeRegistryCompactionCallback>();
}
}
// Compaction of a prototype users list can require the registered users
// to update their remembered slots. That doesn't work if those users
// have already been serialized themselves. So if this object is a
// registered user, compact its prototype's user list now.
if (object_->IsMap()) {
Map* map = Map::cast(object_);
if (map->is_prototype_map() && map->prototype_info()->IsPrototypeInfo() &&
PrototypeInfo::cast(map->prototype_info())->registry_slot() !=
PrototypeInfo::UNREGISTERED) {
JSObject* proto = JSObject::cast(map->prototype());
PrototypeInfo* info = PrototypeInfo::cast(proto->map()->prototype_info());
WeakFixedArray* array = WeakFixedArray::cast(info->prototype_users());
array->Compact<JSObject::PrototypeRegistryCompactionCallback>();
}
}
@ -1936,7 +1952,8 @@ void Serializer::ObjectSerializer::Serialize() {
Script::cast(object_)->set_line_ends(undefined);
Object* shared_list = Script::cast(object_)->shared_function_infos();
if (shared_list->IsWeakFixedArray()) {
WeakFixedArray::cast(shared_list)->Compact();
WeakFixedArray::cast(shared_list)
->Compact<WeakFixedArray::NullCallback>();
}
}

View File

@ -5954,7 +5954,7 @@ TEST(WeakFixedArray) {
Handle<HeapNumber> number = CcTest::i_isolate()->factory()->NewHeapNumber(1);
Handle<WeakFixedArray> array = WeakFixedArray::Add(Handle<Object>(), number);
array->Remove(number);
array->Compact();
array->Compact<WeakFixedArray::NullCallback>();
WeakFixedArray::Add(array, number);
}