From 2f42dd8c6ad5c8626a0e16702f346986ed6d6dcb Mon Sep 17 00:00:00 2001 From: Mythri A Date: Mon, 27 Jul 2020 15:34:21 +0100 Subject: [PATCH] [turboprop] Optimize minimorphic accesses using dynamic map checks 1. Adds a flag to specify if minimorphic accesses should be optimized using dynamic map checks operators. This flag is disabled by default. 2. Builds the PropertyAccessInfo from handlers instead of reading it from maps for minimorphic accesses 3. Uses DynamicMapChecks operator to lower the minimorphic accesses. Bug: v8:10582, v8:9684 Change-Id: I0b7b26b876f9ad12d6fc38788137b66ee6455aeb Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2241524 Reviewed-by: Igor Sheludko Reviewed-by: Georg Neis Reviewed-by: Sathya Gunasekaran Reviewed-by: Michael Stanton Reviewed-by: Tobias Tebbi Commit-Queue: Mythri Alle Cr-Commit-Position: refs/heads/master@{#69112} --- src/compiler/access-info.cc | 37 +++++++ src/compiler/access-info.h | 33 +++++++ src/compiler/js-heap-broker.cc | 93 ++++++++++++++++-- src/compiler/js-heap-broker.h | 7 ++ .../js-native-context-specialization.cc | 36 +++++++ .../js-native-context-specialization.h | 4 + src/compiler/processed-feedback.h | 22 +++++ src/compiler/property-access-builder.cc | 97 +++++++++++++------ src/compiler/property-access-builder.h | 11 +++ .../serializer-for-background-compilation.cc | 15 +++ src/flags/flag-definitions.h | 3 + src/objects/feedback-vector.cc | 48 +++++++-- src/objects/feedback-vector.h | 13 +++ 13 files changed, 373 insertions(+), 46 deletions(-) diff --git a/src/compiler/access-info.cc b/src/compiler/access-info.cc index c12a0c4115..4fb5ebd69c 100644 --- a/src/compiler/access-info.cc +++ b/src/compiler/access-info.cc @@ -126,6 +126,20 @@ PropertyAccessInfo PropertyAccessInfo::StringLength(Zone* zone, {{receiver_map}, zone}); } +// static +MinimorphicLoadPropertyAccessInfo MinimorphicLoadPropertyAccessInfo::DataField( + int offset, bool is_inobject, Representation field_representation, + Type field_type) { + return MinimorphicLoadPropertyAccessInfo(kDataField, offset, is_inobject, + field_representation, field_type); +} + +// static +MinimorphicLoadPropertyAccessInfo MinimorphicLoadPropertyAccessInfo::Invalid() { + return MinimorphicLoadPropertyAccessInfo( + kInvalid, -1, false, Representation::None(), Type::None()); +} + PropertyAccessInfo::PropertyAccessInfo(Zone* zone) : kind_(kInvalid), receiver_maps_(zone), @@ -175,6 +189,15 @@ PropertyAccessInfo::PropertyAccessInfo( field_owner_map.address() == transition_map.address()); } +MinimorphicLoadPropertyAccessInfo::MinimorphicLoadPropertyAccessInfo( + Kind kind, int offset, bool is_inobject, + Representation field_representation, Type field_type) + : kind_(kind), + is_inobject_(is_inobject), + offset_(offset), + field_representation_(field_representation), + field_type_(field_type) {} + bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that, AccessMode access_mode, Zone* zone) { if (this->kind_ != that->kind_) return false; @@ -480,6 +503,20 @@ PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo( holder); } +MinimorphicLoadPropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo( + MinimorphicLoadPropertyAccessFeedback const& feedback) const { + DCHECK(feedback.handler()->IsSmi()); + int handler = Smi::cast(*feedback.handler()).value(); + bool is_inobject = LoadHandler::IsInobjectBits::decode(handler); + bool is_double = LoadHandler::IsDoubleBits::decode(handler); + int offset = LoadHandler::FieldIndexBits::decode(handler) * kTaggedSize; + Representation field_rep = + is_double ? Representation::Double() : Representation::Tagged(); + Type field_type = is_double ? Type::Number() : Type::Any(); + return MinimorphicLoadPropertyAccessInfo::DataField(offset, is_inobject, + field_rep, field_type); +} + PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo( Handle map, Handle name, AccessMode access_mode) const { CHECK(name->IsUniqueName()); diff --git a/src/compiler/access-info.h b/src/compiler/access-info.h index 59101e2cc9..65ea6a5376 100644 --- a/src/compiler/access-info.h +++ b/src/compiler/access-info.h @@ -28,6 +28,7 @@ class CompilationDependencies; class CompilationDependency; class ElementAccessFeedback; class JSHeapBroker; +class MinimorphicLoadPropertyAccessFeedback; class TypeCache; struct ConstFieldInfo; @@ -158,6 +159,35 @@ class PropertyAccessInfo final { MaybeHandle field_map_; }; +// This class encapsulates information required to generate load properties +// by only using the information from handlers. This information is used with +// dynamic map checks. +class MinimorphicLoadPropertyAccessInfo final { + public: + enum Kind { kInvalid, kDataField }; + static MinimorphicLoadPropertyAccessInfo DataField( + int offset, bool is_inobject, Representation field_representation, + Type field_type); + static MinimorphicLoadPropertyAccessInfo Invalid(); + + bool IsInvalid() const { return kind_ == kInvalid; } + bool IsDataField() const { return kind_ == kDataField; } + int offset() const { return offset_; } + int is_inobject() const { return is_inobject_; } + Type field_type() const { return field_type_; } + Representation field_representation() const { return field_representation_; } + + private: + MinimorphicLoadPropertyAccessInfo(Kind kind, int offset, bool is_inobject, + Representation field_representation, + Type field_type); + + Kind kind_; + bool is_inobject_; + int offset_; + Representation field_representation_; + Type field_type_; +}; // Factory class for {ElementAccessInfo}s and {PropertyAccessInfo}s. class AccessInfoFactory final { @@ -175,6 +205,9 @@ class AccessInfoFactory final { Handle name, AccessMode access_mode) const; + MinimorphicLoadPropertyAccessInfo ComputePropertyAccessInfo( + MinimorphicLoadPropertyAccessFeedback const& feedback) const; + // Convenience wrapper around {ComputePropertyAccessInfo} for multiple maps. void ComputePropertyAccessInfos( MapHandles const& maps, Handle name, AccessMode access_mode, diff --git a/src/compiler/js-heap-broker.cc b/src/compiler/js-heap-broker.cc index b172eca97d..930a1998c7 100644 --- a/src/compiler/js-heap-broker.cc +++ b/src/compiler/js-heap-broker.cc @@ -2422,6 +2422,7 @@ JSHeapBroker::JSHeapBroker( feedback_(zone()), bytecode_analyses_(zone()), property_access_infos_(zone()), + minimorphic_property_access_infos_(zone()), typed_array_string_tags_(zone()), serialized_functions_(zone()) { // Note that this initialization of {refs_} with the minimal initial capacity @@ -4673,6 +4674,16 @@ bool ElementAccessFeedback::HasOnlyStringMaps(JSHeapBroker* broker) const { return true; } +MinimorphicLoadPropertyAccessFeedback::MinimorphicLoadPropertyAccessFeedback( + NameRef const& name, FeedbackSlotKind slot_kind, bool is_monomorphic, + Handle handler) + : ProcessedFeedback(kMinimorphicPropertyAccess, slot_kind), + name_(name), + is_monomorphic_(is_monomorphic), + handler_(handler) { + DCHECK(IsLoadICKind(slot_kind)); +} + NamedAccessFeedback::NamedAccessFeedback(NameRef const& name, ZoneVector> const& maps, FeedbackSlotKind slot_kind) @@ -4740,6 +4751,34 @@ void FilterRelevantReceiverMaps(Isolate* isolate, MapHandles* maps) { // Remove everything between the last valid map and the end of the vector. maps->erase(out, end); } + +MaybeObjectHandle TryGetMinimorphicHandler( + std::vector const& maps_and_handlers, + FeedbackSlotKind kind) { + if (!FLAG_dynamic_map_checks || !IsLoadICKind(kind)) + return MaybeObjectHandle(); + + MaybeObjectHandle initial_handler; + for (MapAndHandler map_and_handler : maps_and_handlers) { + auto map = map_and_handler.first; + MaybeObjectHandle handler = map_and_handler.second; + if (handler.is_null()) return MaybeObjectHandle(); + DCHECK(!handler->IsCleared()); + // TODO(mythria): extend this to DataHandlers too + if (!handler.object()->IsSmi()) return MaybeObjectHandle(); + if (LoadHandler::GetHandlerKind(handler.object()->ToSmi()) != + LoadHandler::Kind::kField) { + return MaybeObjectHandle(); + } + CHECK(!map->IsJSGlobalProxyMap()); + if (initial_handler.is_null()) { + initial_handler = handler; + } else if (!handler.is_identical_to(initial_handler)) { + return MaybeObjectHandle(); + } + } + return initial_handler; +} } // namespace bool JSHeapBroker::CanUseFeedback(const FeedbackNexus& nexus) const { @@ -4760,18 +4799,29 @@ ProcessedFeedback const& JSHeapBroker::ReadFeedbackForPropertyAccess( FeedbackSlotKind kind = nexus.kind(); if (!CanUseFeedback(nexus)) return NewInsufficientFeedback(kind); + std::vector maps_and_handlers; + nexus.ExtractMapsAndFeedback(&maps_and_handlers); + + base::Optional name = + static_name.has_value() ? static_name : GetNameFeedback(nexus); + MaybeObjectHandle handler = TryGetMinimorphicHandler(maps_and_handlers, kind); + if (!handler.is_null()) { + return *zone()->New( + *name, kind, nexus.ic_state() == MONOMORPHIC, handler.object()); + } + MapHandles maps; - nexus.ExtractMaps(&maps); + for (auto const& entry : maps_and_handlers) { + maps.push_back(entry.first); + } FilterRelevantReceiverMaps(isolate(), &maps); - // If no maps were found for a non-megamorphic access, then our maps died and - // we should soft-deopt. + // If no maps were found for a non-megamorphic access, then our maps died + // and we should soft-deopt. if (maps.empty() && nexus.ic_state() != MEGAMORPHIC) { return NewInsufficientFeedback(kind); } - base::Optional name = - static_name.has_value() ? static_name : GetNameFeedback(nexus); if (name.has_value()) { // We rely on this invariant in JSGenericLowering. DCHECK_IMPLIES(maps.empty(), nexus.ic_state() == MEGAMORPHIC); @@ -4806,8 +4856,8 @@ ProcessedFeedback const& JSHeapBroker::ReadFeedbackForGlobalAccess( isolate()); if (feedback_value->IsSmi()) { - // The wanted name belongs to a script-scope variable and the feedback tells - // us where to find its value. + // The wanted name belongs to a script-scope variable and the feedback + // tells us where to find its value. int number = feedback_value->Number(); int const script_context_index = FeedbackNexus::ContextIndexBits::decode(number); @@ -5224,6 +5274,29 @@ PropertyAccessInfo JSHeapBroker::GetPropertyAccessInfo( return access_info; } +MinimorphicLoadPropertyAccessInfo JSHeapBroker::GetPropertyAccessInfo( + MinimorphicLoadPropertyAccessFeedback const& feedback, + FeedbackSource const& source, SerializationPolicy policy) { + auto it = minimorphic_property_access_infos_.find(source.index()); + if (it != minimorphic_property_access_infos_.end()) return it->second; + + if (policy == SerializationPolicy::kAssumeSerialized) { + TRACE_BROKER_MISSING( + this, "MinimorphicLoadPropertyAccessInfo for slot " << source.index()); + return MinimorphicLoadPropertyAccessInfo::Invalid(); + } + + AccessInfoFactory factory(this, nullptr, zone()); + MinimorphicLoadPropertyAccessInfo access_info = + factory.ComputePropertyAccessInfo(feedback); + if (is_concurrent_inlining_) { + TRACE(this, + "Storing MinimorphicLoadPropertyAccessInfo for " << source.index()); + minimorphic_property_access_infos_.insert({source.index(), access_info}); + } + return access_info; +} + BinaryOperationFeedback const& ProcessedFeedback::AsBinaryOperation() const { CHECK_EQ(kBinaryOperation, kind()); return *static_cast(this); @@ -5264,6 +5337,12 @@ NamedAccessFeedback const& ProcessedFeedback::AsNamedAccess() const { return *static_cast(this); } +MinimorphicLoadPropertyAccessFeedback const& +ProcessedFeedback::AsMinimorphicPropertyAccess() const { + CHECK_EQ(kMinimorphicPropertyAccess, kind()); + return *static_cast(this); +} + LiteralFeedback const& ProcessedFeedback::AsLiteral() const { CHECK_EQ(kLiteral, kind()); return *static_cast(this); diff --git a/src/compiler/js-heap-broker.h b/src/compiler/js-heap-broker.h index 495d69dc21..30bb17311f 100644 --- a/src/compiler/js-heap-broker.h +++ b/src/compiler/js-heap-broker.h @@ -199,6 +199,11 @@ class V8_EXPORT_PRIVATE JSHeapBroker { CompilationDependencies* dependencies = nullptr, SerializationPolicy policy = SerializationPolicy::kAssumeSerialized); + MinimorphicLoadPropertyAccessInfo GetPropertyAccessInfo( + MinimorphicLoadPropertyAccessFeedback const& feedback, + FeedbackSource const& source, + SerializationPolicy policy = SerializationPolicy::kAssumeSerialized); + StringRef GetTypedArrayStringTag(ElementsKind kind); bool ShouldBeSerializedForCompilation(const SharedFunctionInfoRef& shared, @@ -288,6 +293,8 @@ class V8_EXPORT_PRIVATE JSHeapBroker { ZoneUnorderedMap property_access_infos_; + ZoneUnorderedMap + minimorphic_property_access_infos_; ZoneVector typed_array_string_tags_; diff --git a/src/compiler/js-native-context-specialization.cc b/src/compiler/js-native-context-specialization.cc index ecbeb5eff4..d8eda2261f 100644 --- a/src/compiler/js-native-context-specialization.cc +++ b/src/compiler/js-native-context-specialization.cc @@ -1050,6 +1050,37 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) { } } +Reduction JSNativeContextSpecialization::ReduceMinimorphicPropertyAccess( + Node* node, Node* value, + MinimorphicLoadPropertyAccessFeedback const& feedback, + FeedbackSource const& source) { + Node* receiver = NodeProperties::GetValueInput(node, 0); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + + MinimorphicLoadPropertyAccessInfo access_info = + broker()->GetPropertyAccessInfo( + feedback, source, + should_disallow_heap_access() + ? SerializationPolicy::kAssumeSerialized + : SerializationPolicy::kSerializeIfNeeded); + if (access_info.IsInvalid()) return NoChange(); + + PropertyAccessBuilder access_builder(jsgraph(), broker(), nullptr); + effect = graph()->NewNode( + simplified()->DynamicCheckMaps( + feedback.handler(), source, + feedback.is_monomorphic() + ? DynamicCheckMapsParameters::ICState::kMonomorphic + : DynamicCheckMapsParameters::ICState::kPolymorphic), + receiver, effect, control); + value = access_builder.BuildMinimorphicLoadDataField( + feedback.name(), access_info, receiver, &effect, &control); + + ReplaceWithValue(node, value, effect, control); + return Replace(value); +} + Reduction JSNativeContextSpecialization::ReduceNamedAccess( Node* node, Node* value, NamedAccessFeedback const& feedback, AccessMode access_mode, Node* key) { @@ -1892,6 +1923,11 @@ Reduction JSNativeContextSpecialization::ReducePropertyAccess( case ProcessedFeedback::kNamedAccess: return ReduceNamedAccess(node, value, feedback.AsNamedAccess(), access_mode, key); + case ProcessedFeedback::kMinimorphicPropertyAccess: + DCHECK_EQ(access_mode, AccessMode::kLoad); + DCHECK_NULL(key); + return ReduceMinimorphicPropertyAccess( + node, value, feedback.AsMinimorphicPropertyAccess(), source); case ProcessedFeedback::kElementAccess: DCHECK_EQ(feedback.AsElementAccess().keyed_mode().access_mode(), access_mode); diff --git a/src/compiler/js-native-context-specialization.h b/src/compiler/js-native-context-specialization.h index dda7cfc818..81587870da 100644 --- a/src/compiler/js-native-context-specialization.h +++ b/src/compiler/js-native-context-specialization.h @@ -102,6 +102,10 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final Reduction ReduceNamedAccess(Node* node, Node* value, NamedAccessFeedback const& processed, AccessMode access_mode, Node* key = nullptr); + Reduction ReduceMinimorphicPropertyAccess( + Node* node, Node* value, + MinimorphicLoadPropertyAccessFeedback const& feedback, + FeedbackSource const& source); Reduction ReduceGlobalAccess(Node* node, Node* receiver, Node* value, NameRef const& name, AccessMode access_mode, Node* key = nullptr); diff --git a/src/compiler/processed-feedback.h b/src/compiler/processed-feedback.h index 1d1ee538d8..b1ad377db6 100644 --- a/src/compiler/processed-feedback.h +++ b/src/compiler/processed-feedback.h @@ -5,6 +5,7 @@ #ifndef V8_COMPILER_PROCESSED_FEEDBACK_H_ #define V8_COMPILER_PROCESSED_FEEDBACK_H_ +#include "src/compiler/feedback-source.h" #include "src/compiler/heap-refs.h" namespace v8 { @@ -19,6 +20,7 @@ class ForInFeedback; class GlobalAccessFeedback; class InstanceOfFeedback; class LiteralFeedback; +class MinimorphicLoadPropertyAccessFeedback; class NamedAccessFeedback; class RegExpLiteralFeedback; class TemplateObjectFeedback; @@ -35,6 +37,7 @@ class ProcessedFeedback : public ZoneObject { kGlobalAccess, kInstanceOf, kLiteral, + kMinimorphicPropertyAccess, kNamedAccess, kRegExpLiteral, kTemplateObject, @@ -52,6 +55,8 @@ class ProcessedFeedback : public ZoneObject { GlobalAccessFeedback const& AsGlobalAccess() const; InstanceOfFeedback const& AsInstanceOf() const; NamedAccessFeedback const& AsNamedAccess() const; + MinimorphicLoadPropertyAccessFeedback const& AsMinimorphicPropertyAccess() + const; LiteralFeedback const& AsLiteral() const; RegExpLiteralFeedback const& AsRegExpLiteral() const; TemplateObjectFeedback const& AsTemplateObject() const; @@ -168,6 +173,23 @@ class NamedAccessFeedback : public ProcessedFeedback { ZoneVector> const maps_; }; +class MinimorphicLoadPropertyAccessFeedback : public ProcessedFeedback { + public: + MinimorphicLoadPropertyAccessFeedback(NameRef const& name, + FeedbackSlotKind slot_kind, + bool is_monomorphic, + Handle handler); + + NameRef const& name() const { return name_; } + bool is_monomorphic() const { return is_monomorphic_; } + Handle handler() const { return handler_; } + + private: + NameRef const name_; + bool is_monomorphic_; + Handle handler_; +}; + class CallFeedback : public ProcessedFeedback { public: CallFeedback(base::Optional target, float frequency, diff --git a/src/compiler/property-access-builder.cc b/src/compiler/property-access-builder.cc index cf3d86712b..dc25326735 100644 --- a/src/compiler/property-access-builder.cc +++ b/src/compiler/property-access-builder.cc @@ -183,6 +183,63 @@ Node* PropertyAccessBuilder::TryBuildLoadConstantDataField( return jsgraph()->Constant(*value); } +Node* PropertyAccessBuilder::BuildLoadDataField(NameRef const& name, + Node* holder, + FieldAccess& field_access, + bool is_inobject, Node** effect, + Node** control) { + Node* storage = holder; + if (!is_inobject) { + storage = *effect = graph()->NewNode( + simplified()->LoadField( + AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer()), + storage, *effect, *control); + } + if (field_access.machine_type.representation() == + MachineRepresentation::kFloat64) { + bool const is_heapnumber = !is_inobject || !FLAG_unbox_double_fields; + if (is_heapnumber) { + FieldAccess const storage_access = {kTaggedBase, + field_access.offset, + name.object(), + MaybeHandle(), + Type::OtherInternal(), + MachineType::TaggedPointer(), + kPointerWriteBarrier, + LoadSensitivity::kCritical, + field_access.const_field_info}; + storage = *effect = graph()->NewNode( + simplified()->LoadField(storage_access), storage, *effect, *control); + field_access.offset = HeapNumber::kValueOffset; + field_access.name = MaybeHandle(); + } + } + Node* value = *effect = graph()->NewNode( + simplified()->LoadField(field_access), storage, *effect, *control); + return value; +} + +Node* PropertyAccessBuilder::BuildMinimorphicLoadDataField( + NameRef const& name, MinimorphicLoadPropertyAccessInfo const& access_info, + Node* receiver, Node** effect, Node** control) { + DCHECK_NULL(dependencies()); + MachineRepresentation const field_representation = + ConvertRepresentation(access_info.field_representation()); + + FieldAccess field_access = { + kTaggedBase, + access_info.offset(), + name.object(), + MaybeHandle(), + access_info.field_type(), + MachineType::TypeForRepresentation(field_representation), + kFullWriteBarrier, + LoadSensitivity::kCritical, + ConstFieldInfo::None()}; + return BuildLoadDataField(name, receiver, field_access, + access_info.is_inobject(), effect, control); +} + Node* PropertyAccessBuilder::BuildLoadDataField( NameRef const& name, PropertyAccessInfo const& access_info, Node* receiver, Node** effect, Node** control) { @@ -192,46 +249,22 @@ Node* PropertyAccessBuilder::BuildLoadDataField( return value; } - FieldIndex const field_index = access_info.field_index(); - Type const field_type = access_info.field_type(); MachineRepresentation const field_representation = ConvertRepresentation(access_info.field_representation()); Node* storage = ResolveHolder(access_info, receiver); - if (!field_index.is_inobject()) { - storage = *effect = graph()->NewNode( - simplified()->LoadField( - AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer()), - storage, *effect, *control); - } + FieldAccess field_access = { kTaggedBase, - field_index.offset(), + access_info.field_index().offset(), name.object(), MaybeHandle(), - field_type, + access_info.field_type(), MachineType::TypeForRepresentation(field_representation), kFullWriteBarrier, LoadSensitivity::kCritical, access_info.GetConstFieldInfo()}; - if (field_representation == MachineRepresentation::kFloat64) { - if (!field_index.is_inobject() || !FLAG_unbox_double_fields) { - FieldAccess const storage_access = {kTaggedBase, - field_index.offset(), - name.object(), - MaybeHandle(), - Type::OtherInternal(), - MachineType::TaggedPointer(), - kPointerWriteBarrier, - LoadSensitivity::kCritical, - access_info.GetConstFieldInfo()}; - storage = *effect = graph()->NewNode( - simplified()->LoadField(storage_access), storage, *effect, *control); - field_access.offset = HeapNumber::kValueOffset; - field_access.name = MaybeHandle(); - } - } else if (field_representation == MachineRepresentation::kTaggedPointer || - field_representation == - MachineRepresentation::kCompressedPointer) { + if (field_representation == MachineRepresentation::kTaggedPointer || + field_representation == MachineRepresentation::kCompressedPointer) { // Remember the map of the field value, if its map is stable. This is // used by the LoadElimination to eliminate map checks on the result. Handle field_map; @@ -243,9 +276,9 @@ Node* PropertyAccessBuilder::BuildLoadDataField( } } } - Node* value = *effect = graph()->NewNode( - simplified()->LoadField(field_access), storage, *effect, *control); - return value; + return BuildLoadDataField(name, storage, field_access, + access_info.field_index().is_inobject(), effect, + control); } } // namespace compiler diff --git a/src/compiler/property-access-builder.h b/src/compiler/property-access-builder.h index b69e937786..05436c2635 100644 --- a/src/compiler/property-access-builder.h +++ b/src/compiler/property-access-builder.h @@ -25,6 +25,7 @@ class JSGraph; class JSHeapBroker; class PropertyAccessInfo; class SimplifiedOperatorBuilder; +struct FieldAccess; class PropertyAccessBuilder { public: @@ -62,6 +63,12 @@ class PropertyAccessBuilder { PropertyAccessInfo const& access_info, Node* receiver, Node** effect, Node** control); + // Builds the load for data-field access for minimorphic loads that use + // dynamic map checks. These cannot depend on any information from the maps. + Node* BuildMinimorphicLoadDataField( + NameRef const& name, MinimorphicLoadPropertyAccessInfo const& access_info, + Node* receiver, Node** effect, Node** control); + static MachineRepresentation ConvertRepresentation( Representation representation); @@ -81,6 +88,10 @@ class PropertyAccessBuilder { // {access_info}. Node* ResolveHolder(PropertyAccessInfo const& access_info, Node* receiver); + Node* BuildLoadDataField(NameRef const& name, Node* holder, + FieldAccess& field_access, bool is_inobject, + Node** effect, Node** control); + JSGraph* jsgraph_; JSHeapBroker* broker_; CompilationDependencies* dependencies_; diff --git a/src/compiler/serializer-for-background-compilation.cc b/src/compiler/serializer-for-background-compilation.cc index c800fac489..c8013f0212 100644 --- a/src/compiler/serializer-for-background-compilation.cc +++ b/src/compiler/serializer-for-background-compilation.cc @@ -448,6 +448,9 @@ class SerializerForBackgroundCompilation { void ProcessElementAccess(Hints const& receiver, Hints const& key, ElementAccessFeedback const& feedback, AccessMode access_mode); + void ProcessMinimorphicPropertyAccess( + MinimorphicLoadPropertyAccessFeedback const& feedback, + FeedbackSource const& source); void ProcessModuleVariableAccess( interpreter::BytecodeArrayIterator* iterator); @@ -3052,6 +3055,13 @@ SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess( return access_info; } +void SerializerForBackgroundCompilation::ProcessMinimorphicPropertyAccess( + MinimorphicLoadPropertyAccessFeedback const& feedback, + FeedbackSource const& source) { + broker()->GetPropertyAccessInfo(feedback, source, + SerializationPolicy::kSerializeIfNeeded); +} + void SerializerForBackgroundCompilation::VisitLdaKeyedProperty( BytecodeArrayIterator* iterator) { Hints const& key = environment()->accumulator_hints(); @@ -3109,6 +3119,11 @@ void SerializerForBackgroundCompilation::ProcessNamedPropertyAccess( ProcessNamedAccess(receiver, feedback.AsNamedAccess(), access_mode, &new_accumulator_hints); break; + case ProcessedFeedback::kMinimorphicPropertyAccess: + DCHECK(name.equals(feedback.AsMinimorphicPropertyAccess().name())); + ProcessMinimorphicPropertyAccess(feedback.AsMinimorphicPropertyAccess(), + source); + break; case ProcessedFeedback::kInsufficient: break; default: diff --git a/src/flags/flag-definitions.h b/src/flags/flag-definitions.h index 24f5e66946..70d063e396 100644 --- a/src/flags/flag-definitions.h +++ b/src/flags/flag-definitions.h @@ -670,6 +670,9 @@ DEFINE_BOOL( DEFINE_BOOL(turbo_fast_api_calls, false, "enable fast API calls from TurboFan") DEFINE_INT(reuse_opt_code_count, 0, "don't discard optimized code for the specified number of deopts.") +DEFINE_BOOL(dynamic_map_checks, false, + "use dynamic map checks when generating code for property accesses " + "if all handlers in an IC are the same") // Native context independent (NCI) code. DEFINE_BOOL(turbo_nci, false, diff --git a/src/objects/feedback-vector.cc b/src/objects/feedback-vector.cc index 2505ea3a29..1fede3f5be 100644 --- a/src/objects/feedback-vector.cc +++ b/src/objects/feedback-vector.cc @@ -1005,8 +1005,8 @@ int FeedbackNexus::ExtractMaps(MapHandles* maps) const { return 0; } -int FeedbackNexus::ExtractMapsAndHandlers( - std::vector, MaybeObjectHandle>>* maps_and_handlers, +int FeedbackNexus::ExtractMapsAndFeedbackImpl( + std::vector* maps_and_feedback, bool try_update_deprecated) const { DCHECK(IsLoadICKind(kind()) || IsStoreICKind(kind()) | IsKeyedLoadICKind(kind()) || @@ -1032,19 +1032,18 @@ int FeedbackNexus::ExtractMapsAndHandlers( } const int increment = 2; HeapObject heap_object; - maps_and_handlers->reserve(array.length() / increment); + maps_and_feedback->reserve(array.length() / increment); for (int i = 0; i < array.length(); i += increment) { DCHECK(array.Get(i)->IsWeakOrCleared()); if (array.Get(i)->GetHeapObjectIfWeak(&heap_object)) { MaybeObject handler = array.Get(i + 1); if (!handler->IsCleared()) { - DCHECK(IC::IsHandler(handler)); Handle map(Map::cast(heap_object), isolate); if (try_update_deprecated && !Map::TryUpdate(isolate, map).ToHandle(&map)) { continue; } - maps_and_handlers->push_back( + maps_and_feedback->push_back( MapAndHandler(map, handle(handler, isolate))); found++; } @@ -1054,13 +1053,12 @@ int FeedbackNexus::ExtractMapsAndHandlers( } else if (feedback->GetHeapObjectIfWeak(&heap_object)) { MaybeObject handler = GetFeedbackExtra(); if (!handler->IsCleared()) { - DCHECK(IC::IsHandler(handler)); Handle map = handle(Map::cast(heap_object), isolate); if (try_update_deprecated && !Map::TryUpdate(isolate, map).ToHandle(&map)) { return 0; } - maps_and_handlers->push_back( + maps_and_feedback->push_back( MapAndHandler(map, handle(handler, isolate))); return 1; } @@ -1069,6 +1067,42 @@ int FeedbackNexus::ExtractMapsAndHandlers( return 0; } +int FeedbackNexus::ExtractMapsAndFeedback( + std::vector* maps_and_feedback, + bool try_update_deprecated) const { + DCHECK(IsLoadICKind(kind()) || + IsStoreICKind(kind()) | IsKeyedLoadICKind(kind()) || + IsKeyedStoreICKind(kind()) || IsStoreOwnICKind(kind()) || + IsStoreInArrayLiteralICKind(kind()) || IsKeyedHasICKind(kind()) || + IsStoreDataPropertyInLiteralKind(kind())); + int num_maps = + ExtractMapsAndFeedbackImpl(maps_and_feedback, try_update_deprecated); +#ifdef DEBUG + for (auto const& entry : *maps_and_feedback) { + DCHECK(IC::IsHandler(*entry.second) || + IsStoreDataPropertyInLiteralKind(kind())); + } +#endif + return num_maps; +} + +int FeedbackNexus::ExtractMapsAndHandlers( + std::vector* maps_and_handlers, + bool try_update_deprecated) const { + DCHECK(IsLoadICKind(kind()) || + IsStoreICKind(kind()) | IsKeyedLoadICKind(kind()) || + IsKeyedStoreICKind(kind()) || IsStoreOwnICKind(kind()) || + IsStoreInArrayLiteralICKind(kind()) || IsKeyedHasICKind(kind())); + int num_maps = + ExtractMapsAndFeedbackImpl(maps_and_handlers, try_update_deprecated); +#ifdef DEBUG + for (auto const& entry : *maps_and_handlers) { + DCHECK(IC::IsHandler(*entry.second)); + } +#endif + return num_maps; +} + MaybeObjectHandle FeedbackNexus::FindHandlerForMap(Handle map) const { DCHECK(IsLoadICKind(kind()) || IsStoreICKind(kind()) || IsKeyedLoadICKind(kind()) || IsKeyedStoreICKind(kind()) || diff --git a/src/objects/feedback-vector.h b/src/objects/feedback-vector.h index 0c9b144b7b..afc7484bae 100644 --- a/src/objects/feedback-vector.h +++ b/src/objects/feedback-vector.h @@ -62,6 +62,7 @@ enum class FeedbackSlotKind { }; using MapAndHandler = std::pair, MaybeObjectHandle>; +using MapAndFeedback = std::pair, MaybeObjectHandle>; inline bool IsCallICKind(FeedbackSlotKind kind) { return kind == FeedbackSlotKind::kCall; @@ -656,9 +657,18 @@ class V8_EXPORT_PRIVATE FeedbackNexus final { Map GetFirstMap() const; int ExtractMaps(MapHandles* maps) const; + // Used to obtain maps and the associated handlers stored in the feedback + // vector. This should be called when we expect only a handler to be sotred in + // the extra feedback. This is used by ICs when updting the handlers. int ExtractMapsAndHandlers(std::vector* maps_and_handlers, bool try_update_deprecated = false) const; MaybeObjectHandle FindHandlerForMap(Handle map) const; + // Used to obtain maps and the associated feedback stored in the feedback + // vector. The returned feedback need not be always a handler. It could be a + // name in the case of StoreDataInPropertyLiteral. This is used by TurboFan to + // get all the feedback stored in the vector. + int ExtractMapsAndFeedback(std::vector* maps_and_feedback, + bool try_update_deprecated = false) const; bool IsCleared() const { InlineCacheState state = ic_state(); @@ -768,6 +778,9 @@ class V8_EXPORT_PRIVATE FeedbackNexus final { FeedbackVector vector_; FeedbackSlot slot_; FeedbackSlotKind kind_; + + int ExtractMapsAndFeedbackImpl(std::vector* maps_and_feedback, + bool try_update_deprecated = false) const; }; inline BinaryOperationHint BinaryOperationHintFromFeedback(int type_feedback);