Commit 9377226a authored by ishell's avatar ishell Committed by Commit bot

[ic] Data handlers for loads of non-existent properties.

BUG=v8:5561

Review-Url: https://codereview.chromium.org/2471613006
Cr-Commit-Position: refs/heads/master@{#40735}
parent a902ef88
...@@ -5632,13 +5632,18 @@ void CodeStubAssembler::HandleLoadICProtoHandler( ...@@ -5632,13 +5632,18 @@ void CodeStubAssembler::HandleLoadICProtoHandler(
LoadHandler::kValidityCellOffset); LoadHandler::kValidityCellOffset);
// Both FixedArray and Tuple3 handlers have validity cell at the same offset. // Both FixedArray and Tuple3 handlers have validity cell at the same offset.
Label validity_cell_check_done(this);
Node* validity_cell = Node* validity_cell =
LoadObjectField(handler, LoadHandler::kValidityCellOffset); LoadObjectField(handler, LoadHandler::kValidityCellOffset);
GotoIf(WordEqual(validity_cell, IntPtrConstant(0)),
&validity_cell_check_done);
Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset); Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset);
GotoIf(WordNotEqual(cell_value, GotoIf(WordNotEqual(cell_value,
SmiConstant(Smi::FromInt(Map::kPrototypeChainValid))), SmiConstant(Smi::FromInt(Map::kPrototypeChainValid))),
miss); miss);
Goto(&validity_cell_check_done);
Bind(&validity_cell_check_done);
Node* smi_handler = LoadObjectField(handler, LoadHandler::kSmiHandlerOffset); Node* smi_handler = LoadObjectField(handler, LoadHandler::kSmiHandlerOffset);
CSA_ASSERT(TaggedIsSmi(smi_handler)); CSA_ASSERT(TaggedIsSmi(smi_handler));
...@@ -5660,6 +5665,12 @@ void CodeStubAssembler::HandleLoadICProtoHandler( ...@@ -5660,6 +5665,12 @@ void CodeStubAssembler::HandleLoadICProtoHandler(
Bind(&tuple_handler); Bind(&tuple_handler);
{ {
Label load_existent(this);
GotoIf(WordNotEqual(maybe_holder_cell, NullConstant()), &load_existent);
// This is a handler for a load of a non-existent value.
Return(UndefinedConstant());
Bind(&load_existent);
Node* holder = LoadWeakCellValue(maybe_holder_cell); Node* holder = LoadWeakCellValue(maybe_holder_cell);
// The |holder| is guaranteed to be alive at this point since we passed // The |holder| is guaranteed to be alive at this point since we passed
// both the receiver map check and the validity cell check. // both the receiver map check and the validity cell check.
...@@ -5682,10 +5693,16 @@ void CodeStubAssembler::HandleLoadICProtoHandler( ...@@ -5682,10 +5693,16 @@ void CodeStubAssembler::HandleLoadICProtoHandler(
}, },
1, IndexAdvanceMode::kPost); 1, IndexAdvanceMode::kPost);
Node* holder_cell = LoadFixedArrayElement( Node* maybe_holder_cell = LoadFixedArrayElement(
handler, IntPtrConstant(LoadHandler::kHolderCellIndex), 0, handler, IntPtrConstant(LoadHandler::kHolderCellIndex), 0,
INTPTR_PARAMETERS); INTPTR_PARAMETERS);
Node* holder = LoadWeakCellValue(holder_cell); Label load_existent(this);
GotoIf(WordNotEqual(maybe_holder_cell, NullConstant()), &load_existent);
// This is a handler for a load of a non-existent value.
Return(UndefinedConstant());
Bind(&load_existent);
Node* holder = LoadWeakCellValue(maybe_holder_cell);
// The |holder| is guaranteed to be alive at this point since we passed // The |holder| is guaranteed to be alive at this point since we passed
// the receiver map check, the validity cell check and the prototype chain // the receiver map check, the validity cell check and the prototype chain
// check. // check.
......
...@@ -764,6 +764,7 @@ class RuntimeCallTimer { ...@@ -764,6 +764,7 @@ class RuntimeCallTimer {
V(LoadIC_LoadFieldStub) \ V(LoadIC_LoadFieldStub) \
V(LoadIC_LoadGlobal) \ V(LoadIC_LoadGlobal) \
V(LoadIC_LoadInterceptor) \ V(LoadIC_LoadInterceptor) \
V(LoadIC_LoadNonexistentDH) \
V(LoadIC_LoadNonexistent) \ V(LoadIC_LoadNonexistent) \
V(LoadIC_LoadNormal) \ V(LoadIC_LoadNormal) \
V(LoadIC_LoadScriptContextFieldStub) \ V(LoadIC_LoadScriptContextFieldStub) \
......
...@@ -40,6 +40,14 @@ Handle<Object> LoadHandler::EnableNegativeLookupOnReceiver( ...@@ -40,6 +40,14 @@ Handle<Object> LoadHandler::EnableNegativeLookupOnReceiver(
return handle(Smi::FromInt(config), isolate); return handle(Smi::FromInt(config), isolate);
} }
Handle<Object> LoadHandler::LoadNonExistent(
Isolate* isolate, bool do_negative_lookup_on_receiver) {
int config =
KindBits::encode(kForNonExistent) |
DoNegativeLookupOnReceiverBits::encode(do_negative_lookup_on_receiver);
return handle(Smi::FromInt(config), isolate);
}
Handle<Object> LoadHandler::LoadElement(Isolate* isolate, Handle<Object> LoadHandler::LoadElement(Isolate* isolate,
ElementsKind elements_kind, ElementsKind elements_kind,
bool convert_hole_to_undefined, bool convert_hole_to_undefined,
......
...@@ -16,12 +16,12 @@ namespace internal { ...@@ -16,12 +16,12 @@ namespace internal {
// A set of bit fields representing Smi handlers for loads. // A set of bit fields representing Smi handlers for loads.
class LoadHandler { class LoadHandler {
public: public:
enum Kind { kForElements, kForFields, kForConstants }; enum Kind { kForElements, kForFields, kForConstants, kForNonExistent };
class KindBits : public BitField<Kind, 0, 2> {}; class KindBits : public BitField<Kind, 0, 2> {};
// Defines whether negative lookup check should be done on receiver object. // Defines whether negative lookup check should be done on receiver object.
// Applicable to kForFields and kForConstants kinds only when loading value // Applicable to kForFields, kForConstants and kForNonExistent kinds only when
// from prototype chain. Ignored when loading from holder. // loading value from prototype chain. Ignored when loading from holder.
class DoNegativeLookupOnReceiverBits class DoNegativeLookupOnReceiverBits
: public BitField<bool, KindBits::kNext, 1> {}; : public BitField<bool, KindBits::kNext, 1> {};
...@@ -86,6 +86,11 @@ class LoadHandler { ...@@ -86,6 +86,11 @@ class LoadHandler {
static inline Handle<Object> EnableNegativeLookupOnReceiver( static inline Handle<Object> EnableNegativeLookupOnReceiver(
Isolate* isolate, Handle<Object> smi_handler); Isolate* isolate, Handle<Object> smi_handler);
// Creates a Smi-handler for loading a non-existent property. Works only as
// a part of prototype chain check.
static inline Handle<Object> LoadNonExistent(
Isolate* isolate, bool do_negative_lookup_on_receiver);
// Creates a Smi-handler for loading an element. // Creates a Smi-handler for loading an element.
static inline Handle<Object> LoadElement(Isolate* isolate, static inline Handle<Object> LoadElement(Isolate* isolate,
ElementsKind elements_kind, ElementsKind elements_kind,
......
...@@ -859,7 +859,7 @@ template <bool fill_array> ...@@ -859,7 +859,7 @@ template <bool fill_array>
int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map, int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map,
Handle<JSObject> holder, Handle<FixedArray> array, Handle<JSObject> holder, Handle<FixedArray> array,
Handle<Name> name) { Handle<Name> name) {
DCHECK(holder->HasFastProperties()); DCHECK(holder.is_null() || holder->HasFastProperties());
// The following kinds of receiver maps require custom handler compilation. // The following kinds of receiver maps require custom handler compilation.
if (receiver_map->IsJSGlobalObjectMap()) { if (receiver_map->IsJSGlobalObjectMap()) {
...@@ -872,11 +872,11 @@ int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map, ...@@ -872,11 +872,11 @@ int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map,
HandleScope scope(isolate); HandleScope scope(isolate);
int checks_count = 0; int checks_count = 0;
// Switch to custom compiled handler if the prototype chain contains global // Create/count entries for each global or dictionary prototype appeared in
// or dictionary objects. // the prototype chain contains from receiver till holder.
for (PrototypeIterator iter(receiver_map); !iter.IsAtEnd(); iter.Advance()) { for (PrototypeIterator iter(receiver_map); !iter.IsAtEnd(); iter.Advance()) {
Handle<JSObject> current = PrototypeIterator::GetCurrent<JSObject>(iter); Handle<JSObject> current = PrototypeIterator::GetCurrent<JSObject>(iter);
if (*current == *holder) break; if (holder.is_identical_to(current)) break;
Handle<Map> current_map(current->map(), isolate); Handle<Map> current_map(current->map(), isolate);
if (current_map->IsJSGlobalObjectMap()) { if (current_map->IsJSGlobalObjectMap()) {
...@@ -950,6 +950,41 @@ Handle<Object> LoadIC::SimpleLoadFromPrototype(Handle<Map> receiver_map, ...@@ -950,6 +950,41 @@ Handle<Object> LoadIC::SimpleLoadFromPrototype(Handle<Map> receiver_map,
return handler_array; return handler_array;
} }
Handle<Object> LoadIC::SimpleLoadNonExistent(Handle<Map> receiver_map,
Handle<Name> name) {
Handle<JSObject> holder; // null handle
int checks_count = GetPrototypeCheckCount(receiver_map, holder);
DCHECK_LE(0, checks_count);
DCHECK(!receiver_map->IsJSGlobalObjectMap());
Handle<Object> smi_handler = LoadHandler::LoadNonExistent(
isolate(), receiver_map->is_dictionary_map());
Handle<Object> validity_cell =
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
if (validity_cell.is_null()) {
// This must be a case when receiver's prototype is null.
DCHECK_EQ(*isolate()->factory()->null_value(),
receiver_map->GetPrototypeChainRootMap(isolate())->prototype());
DCHECK_EQ(0, checks_count);
validity_cell = handle(Smi::FromInt(0), isolate());
}
Factory* factory = isolate()->factory();
if (checks_count == 0) {
return factory->NewTuple3(factory->null_value(), smi_handler,
validity_cell);
}
Handle<FixedArray> handler_array(factory->NewFixedArray(
LoadHandler::kFirstPrototypeIndex + checks_count, TENURED));
handler_array->set(LoadHandler::kSmiHandlerIndex, *smi_handler);
handler_array->set(LoadHandler::kValidityCellIndex, *validity_cell);
handler_array->set(LoadHandler::kHolderCellIndex, *factory->null_value());
InitPrototypeChecks<true>(isolate(), receiver_map, holder, handler_array,
name);
return handler_array;
}
bool IsCompatibleReceiver(LookupIterator* lookup, Handle<Map> receiver_map) { bool IsCompatibleReceiver(LookupIterator* lookup, Handle<Map> receiver_map) {
DCHECK(lookup->state() == LookupIterator::ACCESSOR); DCHECK(lookup->state() == LookupIterator::ACCESSOR);
Isolate* isolate = lookup->isolate(); Isolate* isolate = lookup->isolate();
...@@ -1004,7 +1039,10 @@ void LoadIC::UpdateCaches(LookupIterator* lookup) { ...@@ -1004,7 +1039,10 @@ void LoadIC::UpdateCaches(LookupIterator* lookup) {
lookup->state() == LookupIterator::ACCESS_CHECK) { lookup->state() == LookupIterator::ACCESS_CHECK) {
code = slow_stub(); code = slow_stub();
} else if (!lookup->IsFound()) { } else if (!lookup->IsFound()) {
if (kind() == Code::LOAD_IC || kind() == Code::LOAD_GLOBAL_IC) { if (kind() == Code::LOAD_IC) {
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNonexistentDH);
code = SimpleLoadNonExistent(receiver_map(), lookup->name());
} else if (kind() == Code::LOAD_GLOBAL_IC) {
code = NamedLoadHandlerCompiler::ComputeLoadNonexistent(lookup->name(), code = NamedLoadHandlerCompiler::ComputeLoadNonexistent(lookup->name(),
receiver_map()); receiver_map());
// TODO(jkummerow/verwaest): Introduce a builtin that handles this case. // TODO(jkummerow/verwaest): Introduce a builtin that handles this case.
......
...@@ -311,22 +311,28 @@ class LoadIC : public IC { ...@@ -311,22 +311,28 @@ class LoadIC : public IC {
CacheHolderFlag cache_holder) override; CacheHolderFlag cache_holder) override;
private: private:
// Creates a data handler that represents a load of a field by given index.
Handle<Object> SimpleFieldLoad(FieldIndex index); Handle<Object> SimpleFieldLoad(FieldIndex index);
// Returns 0 if the validity cell check is enough to ensure that the // Returns 0 if the validity cell check is enough to ensure that the
// prototype chain from |receiver_map| till |holder| did not change. // prototype chain from |receiver_map| till |holder| did not change.
// If the |holder| is an empty handle then the full prototype chain is
// checked.
// Returns -1 if the handler has to be compiled or the number of prototype // Returns -1 if the handler has to be compiled or the number of prototype
// checks otherwise. // checks otherwise.
int GetPrototypeCheckCount(Handle<Map> receiver_map, Handle<JSObject> holder); int GetPrototypeCheckCount(Handle<Map> receiver_map, Handle<JSObject> holder);
// Creates a data handler that represents a prototype chain check followed // Creates a data handler that represents a prototype chain check followed
// by given Smi-handler that encoded a load from the holder. // by given Smi-handler that encoded a load from the holder.
// Can be used only if IsPrototypeValidityCellCheckEnough() predicate is true. // Can be used only if GetPrototypeCheckCount() returns non negative value.
Handle<Object> SimpleLoadFromPrototype(Handle<Map> receiver_map, Handle<Object> SimpleLoadFromPrototype(Handle<Map> receiver_map,
Handle<JSObject> holder, Handle<JSObject> holder,
Handle<Name> name, Handle<Name> name,
Handle<Object> smi_handler); Handle<Object> smi_handler);
// Creates a data handler that represents a load of a non-existent property.
Handle<Object> SimpleLoadNonExistent(Handle<Map> receiver_map,
Handle<Name> name);
friend class IC; friend class IC;
}; };
......
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