diff --git a/src/code-stub-assembler.cc b/src/code-stub-assembler.cc index 2e73c89cb63f..323c05d17e73 100644 --- a/src/code-stub-assembler.cc +++ b/src/code-stub-assembler.cc @@ -5635,8 +5635,7 @@ void CodeStubAssembler::HandleLoadICHandlerCase( Bind(&try_proto_handler); { - GotoIf(WordEqual(LoadMap(handler), LoadRoot(Heap::kCodeMapRootIndex)), - &call_handler); + GotoIf(IsCodeMap(LoadMap(handler)), &call_handler); HandleLoadICProtoHandler(p, handler, &var_holder, &var_smi_handler, &if_smi_handler, miss); } @@ -6086,11 +6085,26 @@ void CodeStubAssembler::KeyedLoadICGeneric(const LoadICParameters* p) { } } -void CodeStubAssembler::HandleStoreFieldAndReturn( - Node* handler_word, Node* holder, Representation representation, - Node* value, bool transition_to_field, Label* miss) { +void CodeStubAssembler::HandleStoreFieldAndReturn(Node* handler_word, + Node* holder, + Representation representation, + Node* value, Node* transition, + Label* miss) { + bool transition_to_field = transition != nullptr; Node* prepared_value = PrepareValueForWrite(value, representation, miss); + if (transition_to_field) { + Label storage_extended(this); + GotoUnless(IsSetWord(handler_word), + &storage_extended); + Comment("[ Extend storage"); + ExtendPropertiesBackingStore(holder); + Comment("] Extend storage"); + Goto(&storage_extended); + + Bind(&storage_extended); + } + Node* offset = DecodeWord(handler_word); Label if_inobject(this), if_out_of_object(this); Branch(IsSetWord(handler_word), &if_inobject, @@ -6100,6 +6114,9 @@ void CodeStubAssembler::HandleStoreFieldAndReturn( { StoreNamedField(holder, offset, true, representation, prepared_value, transition_to_field); + if (transition_to_field) { + StoreObjectField(holder, JSObject::kMapOffset, transition); + } Return(value); } @@ -6107,15 +6124,33 @@ void CodeStubAssembler::HandleStoreFieldAndReturn( { StoreNamedField(holder, offset, false, representation, prepared_value, transition_to_field); + if (transition_to_field) { + StoreObjectField(holder, JSObject::kMapOffset, transition); + } Return(value); } } void CodeStubAssembler::HandleStoreICSmiHandlerCase(Node* handler_word, Node* holder, Node* value, - bool transition_to_field, + Node* transition, Label* miss) { - Comment(transition_to_field ? "transitioning field store" : "field store"); + Comment(transition ? "transitioning field store" : "field store"); + +#ifdef DEBUG + Node* handler_kind = DecodeWord(handler_word); + if (transition) { + CSA_ASSERT( + this, + WordOr(WordEqual(handler_kind, + IntPtrConstant(StoreHandler::kTransitionToField)), + WordEqual(handler_kind, + IntPtrConstant(StoreHandler::kTransitionToConstant)))); + } else { + CSA_ASSERT(this, WordEqual(handler_kind, + IntPtrConstant(StoreHandler::kStoreField))); + } +#endif Node* field_representation = DecodeWord(handler_word); @@ -6138,14 +6173,14 @@ void CodeStubAssembler::HandleStoreICSmiHandlerCase(Node* handler_word, { Comment("store tagged field"); HandleStoreFieldAndReturn(handler_word, holder, Representation::Tagged(), - value, transition_to_field, miss); + value, transition, miss); } Bind(&if_double_field); { Comment("store double field"); HandleStoreFieldAndReturn(handler_word, holder, Representation::Double(), - value, transition_to_field, miss); + value, transition, miss); } Bind(&if_heap_object_field); @@ -6156,7 +6191,8 @@ void CodeStubAssembler::HandleStoreICSmiHandlerCase(Node* handler_word, PrepareValueForWrite(value, Representation::HeapObject(), miss); Node* value_index_in_descriptor = DecodeWord(handler_word); - Node* descriptors = LoadMapDescriptors(LoadMap(holder)); + Node* descriptors = + LoadMapDescriptors(transition ? transition : LoadMap(holder)); Node* maybe_field_type = LoadFixedArrayElement( descriptors, value_index_in_descriptor, 0, INTPTR_PARAMETERS); Label do_store(this); @@ -6168,22 +6204,23 @@ void CodeStubAssembler::HandleStoreICSmiHandlerCase(Node* handler_word, } Bind(&do_store); HandleStoreFieldAndReturn(handler_word, holder, Representation::Tagged(), - prepared_value, transition_to_field, miss); + prepared_value, transition, miss); } Bind(&if_smi_field); { Comment("store smi field"); HandleStoreFieldAndReturn(handler_word, holder, Representation::Smi(), - value, transition_to_field, miss); + value, transition, miss); } } void CodeStubAssembler::HandleStoreICHandlerCase(const StoreICParameters* p, Node* handler, Label* miss) { - Label if_smi_handler(this), call_handler(this); + Label if_smi_handler(this); + Label try_proto_handler(this), call_handler(this); - Branch(TaggedIsSmi(handler), &if_smi_handler, &call_handler); + Branch(TaggedIsSmi(handler), &if_smi_handler, &try_proto_handler); // |handler| is a Smi, encoding what to do. See SmiHandler methods // for the encoding format. @@ -6192,8 +6229,14 @@ void CodeStubAssembler::HandleStoreICHandlerCase(const StoreICParameters* p, Node* holder = p->receiver; Node* handler_word = SmiUntag(handler); - // Handle non-transitioning stores. - HandleStoreICSmiHandlerCase(handler_word, holder, p->value, false, miss); + // Handle non-transitioning field stores. + HandleStoreICSmiHandlerCase(handler_word, holder, p->value, nullptr, miss); + } + + Bind(&try_proto_handler); + { + GotoIf(IsCodeMap(LoadMap(handler)), &call_handler); + HandleStoreICProtoHandler(p, handler, miss); } // |handler| is a heap object. Must be code, call it. @@ -6205,6 +6248,99 @@ void CodeStubAssembler::HandleStoreICHandlerCase(const StoreICParameters* p, } } +void CodeStubAssembler::HandleStoreICProtoHandler(const StoreICParameters* p, + Node* handler, Label* miss) { + // IC dispatchers rely on these assumptions to be held. + STATIC_ASSERT(FixedArray::kLengthOffset == + StoreHandler::kTransitionCellOffset); + DCHECK_EQ(FixedArray::OffsetOfElementAt(StoreHandler::kSmiHandlerIndex), + StoreHandler::kSmiHandlerOffset); + DCHECK_EQ(FixedArray::OffsetOfElementAt(StoreHandler::kValidityCellIndex), + StoreHandler::kValidityCellOffset); + + // Both FixedArray and Tuple3 handlers have validity cell at the same offset. + Label validity_cell_check_done(this); + Node* validity_cell = + LoadObjectField(handler, StoreHandler::kValidityCellOffset); + GotoIf(WordEqual(validity_cell, IntPtrConstant(0)), + &validity_cell_check_done); + Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset); + GotoIf(WordNotEqual(cell_value, + SmiConstant(Smi::FromInt(Map::kPrototypeChainValid))), + miss); + Goto(&validity_cell_check_done); + + Bind(&validity_cell_check_done); + Node* smi_handler = LoadObjectField(handler, StoreHandler::kSmiHandlerOffset); + CSA_ASSERT(this, TaggedIsSmi(smi_handler)); + + Node* maybe_transition_cell = + LoadObjectField(handler, StoreHandler::kTransitionCellOffset); + Label array_handler(this), tuple_handler(this); + Branch(TaggedIsSmi(maybe_transition_cell), &array_handler, &tuple_handler); + + Variable var_transition(this, MachineRepresentation::kTagged); + Label if_transition(this), if_transition_to_constant(this); + Bind(&tuple_handler); + { + Node* transition = LoadWeakCellValue(maybe_transition_cell, miss); + var_transition.Bind(transition); + Goto(&if_transition); + } + + Bind(&array_handler); + { + Node* length = SmiUntag(maybe_transition_cell); + BuildFastLoop(MachineType::PointerRepresentation(), + IntPtrConstant(StoreHandler::kFirstPrototypeIndex), length, + [this, p, handler, miss](CodeStubAssembler*, Node* current) { + Node* prototype_cell = LoadFixedArrayElement( + handler, current, 0, INTPTR_PARAMETERS); + CheckPrototype(prototype_cell, p->name, miss); + }, + 1, IndexAdvanceMode::kPost); + + Node* maybe_transition_cell = LoadFixedArrayElement( + handler, IntPtrConstant(StoreHandler::kTransitionCellIndex), 0, + INTPTR_PARAMETERS); + Node* transition = LoadWeakCellValue(maybe_transition_cell, miss); + var_transition.Bind(transition); + Goto(&if_transition); + } + + Bind(&if_transition); + { + Node* holder = p->receiver; + Node* transition = var_transition.value(); + Node* handler_word = SmiUntag(smi_handler); + + GotoIf(IsSetWord32(LoadMapBitField3(transition)), miss); + + Node* handler_kind = DecodeWord(handler_word); + GotoIf(WordEqual(handler_kind, + IntPtrConstant(StoreHandler::kTransitionToConstant)), + &if_transition_to_constant); + + // Handle transitioning field stores. + HandleStoreICSmiHandlerCase(handler_word, holder, p->value, transition, + miss); + + Bind(&if_transition_to_constant); + { + // Check that constant matches value. + Node* value_index_in_descriptor = + DecodeWord(handler_word); + Node* descriptors = LoadMapDescriptors(transition); + Node* constant = LoadFixedArrayElement( + descriptors, value_index_in_descriptor, 0, INTPTR_PARAMETERS); + GotoIf(WordNotEqual(p->value, constant), miss); + + StoreObjectField(p->receiver, JSObject::kMapOffset, transition); + Return(p->value); + } + } +} + void CodeStubAssembler::StoreIC(const StoreICParameters* p) { Variable var_handler(this, MachineRepresentation::kTagged); // TODO(ishell): defer blocks when it works. diff --git a/src/code-stub-assembler.h b/src/code-stub-assembler.h index 1ed5615c4205..709c415e10d3 100644 --- a/src/code-stub-assembler.h +++ b/src/code-stub-assembler.h @@ -24,6 +24,7 @@ enum class IterationKind { kKeys, kValues, kEntries }; #define HEAP_CONSTANT_LIST(V) \ V(BooleanMap, BooleanMap) \ + V(CodeMap, CodeMap) \ V(empty_string, EmptyString) \ V(EmptyFixedArray, EmptyFixedArray) \ V(FalseValue, False) \ @@ -1152,16 +1153,23 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { void NameDictionaryNegativeLookup(compiler::Node* object, compiler::Node* name, Label* miss); + // If |transition| is nullptr then the normal field store is generated or + // transitioning store otherwise. void HandleStoreFieldAndReturn(compiler::Node* handler_word, compiler::Node* holder, Representation representation, compiler::Node* value, - bool transition_to_field, Label* miss); + compiler::Node* transition, Label* miss); + // If |transition| is nullptr then the normal field store is generated or + // transitioning store otherwise. void HandleStoreICSmiHandlerCase(compiler::Node* handler_word, compiler::Node* holder, compiler::Node* value, - bool transition_to_field, Label* miss); + compiler::Node* transition, Label* miss); + + void HandleStoreICProtoHandler(const StoreICParameters* p, + compiler::Node* handler, Label* miss); compiler::Node* TryToIntptr(compiler::Node* key, Label* miss); void EmitFastElementsBoundsCheck(compiler::Node* object, diff --git a/src/counters.h b/src/counters.h index 06181c4a72e2..91ad1edf15cc 100644 --- a/src/counters.h +++ b/src/counters.h @@ -800,6 +800,7 @@ class RuntimeCallTimer { V(StoreIC_StoreNormal) \ V(StoreIC_StoreScriptContextFieldStub) \ V(StoreIC_StoreTransition) \ + V(StoreIC_StoreTransitionDH) \ V(StoreIC_StoreViaSetter) class RuntimeCallStats { diff --git a/src/ic/handler-configuration-inl.h b/src/ic/handler-configuration-inl.h index ca67257abe02..505d67cf42ba 100644 --- a/src/ic/handler-configuration-inl.h +++ b/src/ic/handler-configuration-inl.h @@ -79,9 +79,10 @@ Handle LoadHandler::LoadElement(Isolate* isolate, return handle(Smi::FromInt(config), isolate); } -Handle StoreHandler::StoreField(Isolate* isolate, int descriptor, - FieldIndex field_index, - Representation representation) { +Handle StoreHandler::StoreField(Isolate* isolate, Kind kind, + int descriptor, FieldIndex field_index, + Representation representation, + bool extend_storage) { StoreHandler::FieldRepresentation field_rep; switch (representation.kind()) { case Representation::kSmi: @@ -102,7 +103,11 @@ Handle StoreHandler::StoreField(Isolate* isolate, int descriptor, } int value_index = DescriptorArray::ToValueIndex(descriptor); - int config = StoreHandler::KindBits::encode(StoreHandler::kForFields) | + DCHECK(kind == kStoreField || kind == kTransitionToField); + DCHECK_IMPLIES(kind == kStoreField, !extend_storage); + + int config = StoreHandler::KindBits::encode(kind) | + StoreHandler::ExtendStorageBits::encode(extend_storage) | StoreHandler::IsInobjectBits::encode(field_index.is_inobject()) | StoreHandler::FieldRepresentationBits::encode(field_rep) | StoreHandler::DescriptorValueIndexBits::encode(value_index) | @@ -110,6 +115,30 @@ Handle StoreHandler::StoreField(Isolate* isolate, int descriptor, return handle(Smi::FromInt(config), isolate); } +Handle StoreHandler::StoreField(Isolate* isolate, int descriptor, + FieldIndex field_index, + Representation representation) { + return StoreField(isolate, kStoreField, descriptor, field_index, + representation, false); +} + +Handle StoreHandler::TransitionToField(Isolate* isolate, int descriptor, + FieldIndex field_index, + Representation representation, + bool extend_storage) { + return StoreField(isolate, kTransitionToField, descriptor, field_index, + representation, extend_storage); +} + +Handle StoreHandler::TransitionToConstant(Isolate* isolate, + int descriptor) { + int value_index = DescriptorArray::ToValueIndex(descriptor); + int config = + StoreHandler::KindBits::encode(StoreHandler::kTransitionToConstant) | + StoreHandler::DescriptorValueIndexBits::encode(value_index); + return handle(Smi::FromInt(config), isolate); +} + } // namespace internal } // namespace v8 diff --git a/src/ic/handler-configuration.h b/src/ic/handler-configuration.h index b4887e33bac3..a5291736dc0e 100644 --- a/src/ic/handler-configuration.h +++ b/src/ic/handler-configuration.h @@ -37,6 +37,7 @@ class LoadHandler { class IsAccessorInfoBits : public BitField {}; + // Index of a value entry in the descriptor array. // +2 here is because each descriptor entry occupies 3 slots in array. class DescriptorValueIndexBits : public BitField {}; + enum Kind { + kStoreElement, + kStoreField, + kTransitionToField, + kTransitionToConstant + }; + class KindBits : public BitField {}; enum FieldRepresentation { kSmi, kDouble, kHeapObject, kTagged }; + // Applicable to kStoreField, kTransitionToField and kTransitionToConstant + // kinds. + + // Index of a value entry in the descriptor array. + // +2 here is because each descriptor entry occupies 3 slots in array. + class DescriptorValueIndexBits + : public BitField {}; // - // Encoding when KindBits contains kForFields. + // Encoding when KindBits contains kTransitionToConstant. // - class IsInobjectBits : public BitField {}; + + // Make sure we don't overflow the smi. + STATIC_ASSERT(DescriptorValueIndexBits::kNext <= kSmiValueSize); + + // + // Encoding when KindBits contains kStoreField or kTransitionToField. + // + class ExtendStorageBits + : public BitField {}; + class IsInobjectBits : public BitField {}; class FieldRepresentationBits : public BitField {}; - // +2 here is because each descriptor entry occupies 3 slots in array. - class DescriptorValueIndexBits - : public BitField {}; // +1 here is to cover all possible JSObject header sizes. class FieldOffsetBits - : public BitField {}; // Make sure we don't overflow the smi. STATIC_ASSERT(FieldOffsetBits::kNext <= kSmiValueSize); + // The layout of an Tuple3 handler representing a transitioning store + // when prototype chain checks do not include non-existing lookups or access + // checks. + static const int kTransitionCellOffset = Tuple3::kValue1Offset; + static const int kSmiHandlerOffset = Tuple3::kValue2Offset; + static const int kValidityCellOffset = Tuple3::kValue3Offset; + + // The layout of an array handler representing a transitioning store + // when prototype chain checks include non-existing lookups and access checks. + static const int kSmiHandlerIndex = 0; + static const int kValidityCellIndex = 1; + static const int kTransitionCellIndex = 2; + static const int kFirstPrototypeIndex = 3; + // Creates a Smi-handler for storing a field to fast object. static inline Handle StoreField(Isolate* isolate, int descriptor, FieldIndex field_index, Representation representation); + + // Creates a Smi-handler for transitioning store to a field. + static inline Handle TransitionToField(Isolate* isolate, + int descriptor, + FieldIndex field_index, + Representation representation, + bool extend_storage); + + // Creates a Smi-handler for transitioning store to a constant field (in this + // case the only thing that needs to be done is an update of a map). + static inline Handle TransitionToConstant(Isolate* isolate, + int descriptor); + + private: + static inline Handle StoreField(Isolate* isolate, Kind kind, + int descriptor, + FieldIndex field_index, + Representation representation, + bool extend_storage); }; } // namespace internal diff --git a/src/ic/ic.cc b/src/ic/ic.cc index 91430790265c..bcd98f156ce7 100644 --- a/src/ic/ic.cc +++ b/src/ic/ic.cc @@ -852,10 +852,10 @@ Handle LoadIC::SimpleFieldLoad(FieldIndex index) { namespace { -template +template int InitPrototypeChecks(Isolate* isolate, Handle receiver_map, Handle holder, Handle array, - Handle name) { + Handle name, int first_index) { DCHECK(holder.is_null() || holder->HasFastProperties()); // The following kinds of receiver maps require custom handler compilation. @@ -898,8 +898,7 @@ int InitPrototypeChecks(Isolate* isolate, Handle receiver_map, global, name, PropertyCellType::kInvalidated); DCHECK(cell->value()->IsTheHole(isolate)); Handle weak_cell = isolate->factory()->NewWeakCell(cell); - array->set(LoadHandler::kFirstPrototypeIndex + checks_count, - *weak_cell); + array->set(first_index + checks_count, *weak_cell); } checks_count++; @@ -910,8 +909,7 @@ int InitPrototypeChecks(Isolate* isolate, Handle receiver_map, current->property_dictionary()->FindEntry(name)); Handle weak_cell = Map::GetOrCreatePrototypeWeakCell(current, isolate); - array->set(LoadHandler::kFirstPrototypeIndex + checks_count, - *weak_cell); + array->set(first_index + checks_count, *weak_cell); } checks_count++; } @@ -919,19 +917,25 @@ int InitPrototypeChecks(Isolate* isolate, Handle receiver_map, return checks_count; } -} // namespace - -int LoadIC::GetPrototypeCheckCount(Handle receiver_map, - Handle holder) { - return InitPrototypeChecks(isolate(), receiver_map, holder, - Handle(), Handle()); +// Returns 0 if the validity cell check is enough to ensure that the +// 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 +// checks otherwise. +int GetPrototypeCheckCount(Isolate* isolate, Handle receiver_map, + Handle holder) { + return InitPrototypeChecks(isolate, receiver_map, holder, + Handle(), Handle(), 0); } +} // namespace + Handle LoadIC::LoadFromPrototype(Handle receiver_map, Handle holder, Handle name, Handle smi_handler) { - int checks_count = GetPrototypeCheckCount(receiver_map, holder); + int checks_count = GetPrototypeCheckCount(isolate(), receiver_map, holder); DCHECK_LE(0, checks_count); DCHECK(!receiver_map->IsJSGlobalObjectMap()); @@ -961,15 +965,15 @@ Handle LoadIC::LoadFromPrototype(Handle receiver_map, handler_array->set(LoadHandler::kSmiHandlerIndex, *smi_handler); handler_array->set(LoadHandler::kValidityCellIndex, *validity_cell); handler_array->set(LoadHandler::kHolderCellIndex, *holder_cell); - InitPrototypeChecks(isolate(), receiver_map, holder, handler_array, - name); + InitPrototypeChecks(isolate(), receiver_map, holder, handler_array, name, + LoadHandler::kFirstPrototypeIndex); return handler_array; } Handle LoadIC::LoadNonExistent(Handle receiver_map, Handle name) { Handle holder; // null handle - int checks_count = GetPrototypeCheckCount(receiver_map, holder); + int checks_count = GetPrototypeCheckCount(isolate(), receiver_map, holder); DCHECK_LE(0, checks_count); DCHECK(!receiver_map->IsJSGlobalObjectMap()); @@ -1003,8 +1007,8 @@ Handle LoadIC::LoadNonExistent(Handle receiver_map, handler_array->set(LoadHandler::kSmiHandlerIndex, *smi_handler); handler_array->set(LoadHandler::kValidityCellIndex, *validity_cell); handler_array->set(LoadHandler::kHolderCellIndex, *factory->null_value()); - InitPrototypeChecks(isolate(), receiver_map, holder, handler_array, - name); + InitPrototypeChecks(isolate(), receiver_map, holder, handler_array, name, + LoadHandler::kFirstPrototypeIndex); return handler_array; } @@ -1861,6 +1865,62 @@ void StoreIC::UpdateCaches(LookupIterator* lookup, Handle value, TRACE_IC("StoreIC", lookup->name()); } +Handle StoreIC::StoreTransition(Handle receiver_map, + Handle holder, + Handle transition, + Handle name) { + int descriptor = transition->LastAdded(); + Handle descriptors(transition->instance_descriptors()); + PropertyDetails details = descriptors->GetDetails(descriptor); + Representation representation = details.representation(); + DCHECK(!representation.IsNone()); + + // Declarative handlers don't support access checks. + DCHECK(!transition->is_access_check_needed()); + + Handle smi_handler; + if (details.type() == DATA_CONSTANT) { + smi_handler = StoreHandler::TransitionToConstant(isolate(), descriptor); + + } else { + DCHECK_EQ(DATA, details.type()); + bool extend_storage = + Map::cast(transition->GetBackPointer())->unused_property_fields() == 0; + + FieldIndex index = FieldIndex::ForDescriptor(*transition, descriptor); + smi_handler = StoreHandler::TransitionToField( + isolate(), descriptor, index, representation, extend_storage); + } + + int checks_count = GetPrototypeCheckCount(isolate(), receiver_map, holder); + DCHECK_LE(0, checks_count); + DCHECK(!receiver_map->IsJSGlobalObjectMap()); + + Handle 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()); + } + + Handle transition_cell = Map::WeakCellForMap(transition); + + Factory* factory = isolate()->factory(); + if (checks_count == 0) { + return factory->NewTuple3(transition_cell, smi_handler, validity_cell); + } + Handle handler_array(factory->NewFixedArray( + StoreHandler::kFirstPrototypeIndex + checks_count, TENURED)); + handler_array->set(StoreHandler::kSmiHandlerIndex, *smi_handler); + handler_array->set(StoreHandler::kValidityCellIndex, *validity_cell); + handler_array->set(StoreHandler::kTransitionCellIndex, *transition_cell); + InitPrototypeChecks(isolate(), receiver_map, holder, handler_array, name, + StoreHandler::kFirstPrototypeIndex); + return handler_array; +} static Handle PropertyCellStoreHandler( Isolate* isolate, Handle receiver, Handle holder, @@ -1897,8 +1957,13 @@ Handle StoreIC::GetMapIndependentHandler(LookupIterator* lookup) { TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub); return slow_stub(); } - DCHECK(lookup->IsCacheableTransition()); + if (FLAG_tf_store_ic_stub) { + Handle transition = lookup->transition_map(); + TRACE_HANDLER_STATS(isolate(), StoreIC_StoreTransitionDH); + return StoreTransition(receiver_map(), holder, transition, + lookup->name()); + } break; // Custom-compiled handler. } @@ -2038,6 +2103,7 @@ Handle StoreIC::CompileHandler(LookupIterator* lookup, cell->set_value(isolate()->heap()->the_hole_value()); return code; } + DCHECK(!FLAG_tf_store_ic_stub); Handle transition = lookup->transition_map(); // Currently not handled by CompileStoreTransition. DCHECK(holder->HasFastProperties()); diff --git a/src/ic/ic.h b/src/ic/ic.h index e7312e2888b1..9e69cc85d08b 100644 --- a/src/ic/ic.h +++ b/src/ic/ic.h @@ -314,14 +314,6 @@ class LoadIC : public IC { // Creates a data handler that represents a load of a field by given index. Handle SimpleFieldLoad(FieldIndex index); - // Returns 0 if the validity cell check is enough to ensure that the - // 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 - // checks otherwise. - int GetPrototypeCheckCount(Handle receiver_map, Handle holder); - // Creates a data handler that represents a prototype chain check followed // by given Smi-handler that encoded a load from the holder. // Can be used only if GetPrototypeCheckCount() returns non negative value. @@ -425,6 +417,10 @@ class StoreIC : public IC { CacheHolderFlag cache_holder) override; private: + Handle StoreTransition(Handle receiver_map, + Handle holder, + Handle transition, Handle name); + friend class IC; };