Commit 2f3f5301 authored by Igor Sheludko's avatar Igor Sheludko Committed by Commit Bot

[ic] Rely on prototype validity cell in Load/StoreGlobalIC.

... instead of checking if the property cell is still empty when loading/storing
through JSGlobalObject prototype.

Also invalidate the validity cell when new global lexical variables appear in the
script.

Bug: v8:5561
Change-Id: Iaf122dffe76d57b32e2b69291dee079e772b271c
Reviewed-on: https://chromium-review.googlesource.com/819230Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50100}
parent e8c28a04
...@@ -682,13 +682,9 @@ Node* AccessorAssembler::EmitLoadICProtoArrayCheck(const LoadICParameters* p, ...@@ -682,13 +682,9 @@ Node* AccessorAssembler::EmitLoadICProtoArrayCheck(const LoadICParameters* p,
} }
BIND(&can_access); BIND(&can_access);
BuildFastLoop(var_start_index.value(), handler_length, // TODO(ishell): Use LoadHandler with data2 field instead of FixedArray
[=](Node* current) { // handlers.
Node* prototype_cell = CSA_ASSERT(this, WordEqual(var_start_index.value(), handler_length));
LoadFixedArrayElement(handler, current);
CheckPrototype(prototype_cell, p->name, miss);
},
1, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
Node* maybe_holder_cell = Node* maybe_holder_cell =
LoadFixedArrayElement(handler, LoadHandler::kDataIndex); LoadFixedArrayElement(handler, LoadHandler::kDataIndex);
...@@ -973,14 +969,11 @@ void AccessorAssembler::HandleStoreICProtoHandler( ...@@ -973,14 +969,11 @@ void AccessorAssembler::HandleStoreICProtoHandler(
} }
BIND(&can_access); BIND(&can_access);
// TODO(ishell): Use StoreHandler with data2 field instead of FixedArray
// handlers.
Node* length = SmiUntag(maybe_transition_cell); Node* length = SmiUntag(maybe_transition_cell);
BuildFastLoop(var_start_index.value(), length, CSA_ASSERT(this, WordEqual(var_start_index.value(), length));
[=](Node* current) { USE(length);
Node* prototype_cell =
LoadFixedArrayElement(handler, current);
CheckPrototype(prototype_cell, p->name, miss);
},
1, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
Node* maybe_transition_cell = Node* maybe_transition_cell =
LoadFixedArrayElement(handler, StoreHandler::kDataIndex); LoadFixedArrayElement(handler, StoreHandler::kDataIndex);
...@@ -1749,35 +1742,6 @@ void AccessorAssembler::EmitElementLoad( ...@@ -1749,35 +1742,6 @@ void AccessorAssembler::EmitElementLoad(
} }
} }
void AccessorAssembler::CheckPrototype(Node* prototype_cell, Node* name,
Label* miss) {
Node* maybe_prototype = LoadWeakCellValue(prototype_cell, miss);
Label done(this);
Label if_property_cell(this), if_dictionary_object(this);
// |maybe_prototype| is either a PropertyCell or a slow-mode prototype.
Branch(IsPropertyCell(maybe_prototype), &if_property_cell,
&if_dictionary_object);
BIND(&if_dictionary_object);
{
CSA_ASSERT(this, IsDictionaryMap(LoadMap(maybe_prototype)));
NameDictionaryNegativeLookup(maybe_prototype, name, miss);
Goto(&done);
}
BIND(&if_property_cell);
{
// Ensure the property cell still contains the hole.
Node* value = LoadObjectField(maybe_prototype, PropertyCell::kValueOffset);
GotoIfNot(IsTheHole(value), miss);
Goto(&done);
}
BIND(&done);
}
void AccessorAssembler::NameDictionaryNegativeLookup(Node* object, Node* name, void AccessorAssembler::NameDictionaryNegativeLookup(Node* object, Node* name,
Label* miss) { Label* miss) {
CSA_ASSERT(this, IsDictionaryMap(LoadMap(object))); CSA_ASSERT(this, IsDictionaryMap(LoadMap(object)));
...@@ -1833,11 +1797,18 @@ void AccessorAssembler::InvalidateValidityCellIfPrototype(Node* map, ...@@ -1833,11 +1797,18 @@ void AccessorAssembler::InvalidateValidityCellIfPrototype(Node* map,
&cont); &cont);
BIND(&is_prototype); BIND(&is_prototype);
Node* function = ExternalConstant( {
ExternalReference::invalidate_prototype_chains_function(isolate())); Node* maybe_prototype_info =
CallCFunction1(MachineType::AnyTagged(), MachineType::AnyTagged(), function, LoadObjectField(map, Map::kTransitionsOrPrototypeInfoOffset);
map); // If there's no prototype info then there's nothing to invalidate.
Goto(&cont); GotoIf(TaggedIsSmi(maybe_prototype_info), &cont);
Node* function = ExternalConstant(
ExternalReference::invalidate_prototype_chains_function(isolate()));
CallCFunction1(MachineType::AnyTagged(), MachineType::AnyTagged(), function,
map);
Goto(&cont);
}
BIND(&cont); BIND(&cont);
} }
......
...@@ -234,7 +234,6 @@ class AccessorAssembler : public CodeStubAssembler { ...@@ -234,7 +234,6 @@ class AccessorAssembler : public CodeStubAssembler {
Label* rebox_double, Variable* var_double_value, Label* rebox_double, Variable* var_double_value,
Label* unimplemented_elements_kind, Label* out_of_bounds, Label* unimplemented_elements_kind, Label* out_of_bounds,
Label* miss, ExitPoint* exit_point); Label* miss, ExitPoint* exit_point);
void CheckPrototype(Node* prototype_cell, Node* name, Label* miss);
void NameDictionaryNegativeLookup(Node* object, Node* name, Label* miss); void NameDictionaryNegativeLookup(Node* object, Node* name, Label* miss);
// Stub cache access helpers. // Stub cache access helpers.
......
...@@ -34,19 +34,6 @@ int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map, ...@@ -34,19 +34,6 @@ int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map,
array->set(first_index + checks_count, native_context->self_weak_cell()); array->set(first_index + checks_count, native_context->self_weak_cell());
} }
checks_count++; checks_count++;
} else if (receiver_map->IsJSGlobalObjectMap()) {
// If we are creating a handler for [Load/Store]GlobalIC then we need to
// check that the property did not appear in the global object.
if (fill_array) {
Handle<JSGlobalObject> global = isolate->global_object();
Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
global, name, PropertyCellType::kInvalidated);
DCHECK(cell->value()->IsTheHole(isolate));
Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(cell);
array->set(first_index + checks_count, *weak_cell);
}
checks_count++;
} }
return checks_count; return checks_count;
} }
......
...@@ -448,6 +448,10 @@ void Map::MapVerify() { ...@@ -448,6 +448,10 @@ void Map::MapVerify() {
CHECK_IMPLIES(IsJSObjectMap() && !CanHaveFastTransitionableElementsKind(), CHECK_IMPLIES(IsJSObjectMap() && !CanHaveFastTransitionableElementsKind(),
IsDictionaryElementsKind(elements_kind()) || IsDictionaryElementsKind(elements_kind()) ||
IsTerminalElementsKind(elements_kind())); IsTerminalElementsKind(elements_kind()));
if (is_prototype_map()) {
DCHECK(prototype_info() == Smi::kZero ||
prototype_info()->IsPrototypeInfo());
}
} }
......
...@@ -12478,15 +12478,19 @@ bool JSObject::UnregisterPrototypeUser(Handle<Map> user, Isolate* isolate) { ...@@ -12478,15 +12478,19 @@ bool JSObject::UnregisterPrototypeUser(Handle<Map> user, Isolate* isolate) {
return true; return true;
} }
namespace {
static void InvalidatePrototypeChainsInternal(Map* map) { // This function must be kept in sync with
// AccessorAssembler::InvalidateValidityCellIfPrototype() which does pre-checks
// before jumping here.
PrototypeInfo* InvalidateOnePrototypeValidityCellInternal(Map* map) {
DCHECK(map->is_prototype_map()); DCHECK(map->is_prototype_map());
if (FLAG_trace_prototype_users) { if (FLAG_trace_prototype_users) {
PrintF("Invalidating prototype map %p 's cell\n", PrintF("Invalidating prototype map %p 's cell\n",
reinterpret_cast<void*>(map)); reinterpret_cast<void*>(map));
} }
Object* maybe_proto_info = map->prototype_info(); Object* maybe_proto_info = map->prototype_info();
if (!maybe_proto_info->IsPrototypeInfo()) return; if (!maybe_proto_info->IsPrototypeInfo()) return nullptr;
PrototypeInfo* proto_info = PrototypeInfo::cast(maybe_proto_info); PrototypeInfo* proto_info = PrototypeInfo::cast(maybe_proto_info);
Object* maybe_cell = proto_info->validity_cell(); Object* maybe_cell = proto_info->validity_cell();
if (maybe_cell->IsCell()) { if (maybe_cell->IsCell()) {
...@@ -12494,6 +12498,12 @@ static void InvalidatePrototypeChainsInternal(Map* map) { ...@@ -12494,6 +12498,12 @@ static void InvalidatePrototypeChainsInternal(Map* map) {
Cell* cell = Cell::cast(maybe_cell); Cell* cell = Cell::cast(maybe_cell);
cell->set_value(Smi::FromInt(Map::kPrototypeChainInvalid)); cell->set_value(Smi::FromInt(Map::kPrototypeChainInvalid));
} }
return proto_info;
}
void InvalidatePrototypeChainsInternal(Map* map) {
PrototypeInfo* proto_info = InvalidateOnePrototypeValidityCellInternal(map);
if (proto_info == nullptr) return;
WeakFixedArray::Iterator iterator(proto_info->prototype_users()); WeakFixedArray::Iterator iterator(proto_info->prototype_users());
// For now, only maps register themselves as users. // For now, only maps register themselves as users.
...@@ -12504,6 +12514,7 @@ static void InvalidatePrototypeChainsInternal(Map* map) { ...@@ -12504,6 +12514,7 @@ static void InvalidatePrototypeChainsInternal(Map* map) {
} }
} }
} // namespace
// static // static
Map* JSObject::InvalidatePrototypeChains(Map* map) { Map* JSObject::InvalidatePrototypeChains(Map* map) {
...@@ -12512,6 +12523,18 @@ Map* JSObject::InvalidatePrototypeChains(Map* map) { ...@@ -12512,6 +12523,18 @@ Map* JSObject::InvalidatePrototypeChains(Map* map) {
return map; return map;
} }
// We also invalidate global objects validity cell when a new lexical
// environment variable is added. This is necessary to ensure that
// Load/StoreGlobalIC handlers that load/store from global object's prototype
// get properly invalidated.
// Note, that the normal Load/StoreICs that load/store through the global object
// in the prototype chain are not affected by appearance of a new lexical
// variable and therefore we don't propagate invalidation down.
// static
void JSObject::InvalidatePrototypeValidityCell(JSGlobalObject* global) {
DisallowHeapAllocation no_gc;
InvalidateOnePrototypeValidityCellInternal(global->map());
}
// static // static
Handle<PrototypeInfo> Map::GetOrCreatePrototypeInfo(Handle<JSObject> prototype, Handle<PrototypeInfo> Map::GetOrCreatePrototypeInfo(Handle<JSObject> prototype,
...@@ -16636,6 +16659,10 @@ MaybeHandle<JSTypedArray> JSTypedArray::SpeciesCreate( ...@@ -16636,6 +16659,10 @@ MaybeHandle<JSTypedArray> JSTypedArray::SpeciesCreate(
void JSGlobalObject::InvalidatePropertyCell(Handle<JSGlobalObject> global, void JSGlobalObject::InvalidatePropertyCell(Handle<JSGlobalObject> global,
Handle<Name> name) { Handle<Name> name) {
// Regardless of whether the property is there or not invalidate
// Load/StoreGlobalICs that load/store through global object's prototype.
JSObject::InvalidatePrototypeValidityCell(*global);
DCHECK(!global->HasFastProperties()); DCHECK(!global->HasFastProperties());
auto dictionary = handle(global->global_dictionary()); auto dictionary = handle(global->global_dictionary());
int entry = dictionary->FindEntry(name); int entry = dictionary->FindEntry(name);
......
...@@ -2355,6 +2355,7 @@ class JSObject: public JSReceiver { ...@@ -2355,6 +2355,7 @@ class JSObject: public JSReceiver {
Isolate* isolate); Isolate* isolate);
static bool UnregisterPrototypeUser(Handle<Map> user, Isolate* isolate); static bool UnregisterPrototypeUser(Handle<Map> user, Isolate* isolate);
static Map* InvalidatePrototypeChains(Map* map); static Map* InvalidatePrototypeChains(Map* map);
static void InvalidatePrototypeValidityCell(JSGlobalObject* global);
// Updates prototype chain tracking information when an object changes its // Updates prototype chain tracking information when an object changes its
// map from |old_map| to |new_map|. // map from |old_map| to |new_map|.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment