[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 <ishell@chromium.org>
Reviewed-by: Georg Neis <neis@chromium.org>
Reviewed-by: Sathya Gunasekaran  <gsathya@chromium.org>
Reviewed-by: Michael Stanton <mvstanton@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Commit-Queue: Mythri Alle <mythria@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69112}
This commit is contained in:
Mythri A 2020-07-27 15:34:21 +01:00 committed by Commit Bot
parent 406b99c4d4
commit 2f42dd8c6a
13 changed files with 373 additions and 46 deletions

View File

@ -126,6 +126,20 @@ PropertyAccessInfo PropertyAccessInfo::StringLength(Zone* zone,
{{receiver_map}, 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) PropertyAccessInfo::PropertyAccessInfo(Zone* zone)
: kind_(kInvalid), : kind_(kInvalid),
receiver_maps_(zone), receiver_maps_(zone),
@ -175,6 +189,15 @@ PropertyAccessInfo::PropertyAccessInfo(
field_owner_map.address() == transition_map.address()); 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, bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
AccessMode access_mode, Zone* zone) { AccessMode access_mode, Zone* zone) {
if (this->kind_ != that->kind_) return false; if (this->kind_ != that->kind_) return false;
@ -480,6 +503,20 @@ PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo(
holder); 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( PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
Handle<Map> map, Handle<Name> name, AccessMode access_mode) const { Handle<Map> map, Handle<Name> name, AccessMode access_mode) const {
CHECK(name->IsUniqueName()); CHECK(name->IsUniqueName());

View File

@ -28,6 +28,7 @@ class CompilationDependencies;
class CompilationDependency; class CompilationDependency;
class ElementAccessFeedback; class ElementAccessFeedback;
class JSHeapBroker; class JSHeapBroker;
class MinimorphicLoadPropertyAccessFeedback;
class TypeCache; class TypeCache;
struct ConstFieldInfo; struct ConstFieldInfo;
@ -158,6 +159,35 @@ class PropertyAccessInfo final {
MaybeHandle<Map> field_map_; MaybeHandle<Map> 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. // Factory class for {ElementAccessInfo}s and {PropertyAccessInfo}s.
class AccessInfoFactory final { class AccessInfoFactory final {
@ -175,6 +205,9 @@ class AccessInfoFactory final {
Handle<Name> name, Handle<Name> name,
AccessMode access_mode) const; AccessMode access_mode) const;
MinimorphicLoadPropertyAccessInfo ComputePropertyAccessInfo(
MinimorphicLoadPropertyAccessFeedback const& feedback) const;
// Convenience wrapper around {ComputePropertyAccessInfo} for multiple maps. // Convenience wrapper around {ComputePropertyAccessInfo} for multiple maps.
void ComputePropertyAccessInfos( void ComputePropertyAccessInfos(
MapHandles const& maps, Handle<Name> name, AccessMode access_mode, MapHandles const& maps, Handle<Name> name, AccessMode access_mode,

View File

@ -2422,6 +2422,7 @@ JSHeapBroker::JSHeapBroker(
feedback_(zone()), feedback_(zone()),
bytecode_analyses_(zone()), bytecode_analyses_(zone()),
property_access_infos_(zone()), property_access_infos_(zone()),
minimorphic_property_access_infos_(zone()),
typed_array_string_tags_(zone()), typed_array_string_tags_(zone()),
serialized_functions_(zone()) { serialized_functions_(zone()) {
// Note that this initialization of {refs_} with the minimal initial capacity // Note that this initialization of {refs_} with the minimal initial capacity
@ -4673,6 +4674,16 @@ bool ElementAccessFeedback::HasOnlyStringMaps(JSHeapBroker* broker) const {
return true; return true;
} }
MinimorphicLoadPropertyAccessFeedback::MinimorphicLoadPropertyAccessFeedback(
NameRef const& name, FeedbackSlotKind slot_kind, bool is_monomorphic,
Handle<Object> handler)
: ProcessedFeedback(kMinimorphicPropertyAccess, slot_kind),
name_(name),
is_monomorphic_(is_monomorphic),
handler_(handler) {
DCHECK(IsLoadICKind(slot_kind));
}
NamedAccessFeedback::NamedAccessFeedback(NameRef const& name, NamedAccessFeedback::NamedAccessFeedback(NameRef const& name,
ZoneVector<Handle<Map>> const& maps, ZoneVector<Handle<Map>> const& maps,
FeedbackSlotKind slot_kind) 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. // Remove everything between the last valid map and the end of the vector.
maps->erase(out, end); maps->erase(out, end);
} }
MaybeObjectHandle TryGetMinimorphicHandler(
std::vector<MapAndHandler> 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 } // namespace
bool JSHeapBroker::CanUseFeedback(const FeedbackNexus& nexus) const { bool JSHeapBroker::CanUseFeedback(const FeedbackNexus& nexus) const {
@ -4760,18 +4799,29 @@ ProcessedFeedback const& JSHeapBroker::ReadFeedbackForPropertyAccess(
FeedbackSlotKind kind = nexus.kind(); FeedbackSlotKind kind = nexus.kind();
if (!CanUseFeedback(nexus)) return NewInsufficientFeedback(kind); if (!CanUseFeedback(nexus)) return NewInsufficientFeedback(kind);
std::vector<MapAndHandler> maps_and_handlers;
nexus.ExtractMapsAndFeedback(&maps_and_handlers);
base::Optional<NameRef> name =
static_name.has_value() ? static_name : GetNameFeedback(nexus);
MaybeObjectHandle handler = TryGetMinimorphicHandler(maps_and_handlers, kind);
if (!handler.is_null()) {
return *zone()->New<MinimorphicLoadPropertyAccessFeedback>(
*name, kind, nexus.ic_state() == MONOMORPHIC, handler.object());
}
MapHandles maps; MapHandles maps;
nexus.ExtractMaps(&maps); for (auto const& entry : maps_and_handlers) {
maps.push_back(entry.first);
}
FilterRelevantReceiverMaps(isolate(), &maps); FilterRelevantReceiverMaps(isolate(), &maps);
// If no maps were found for a non-megamorphic access, then our maps died and // If no maps were found for a non-megamorphic access, then our maps died
// we should soft-deopt. // and we should soft-deopt.
if (maps.empty() && nexus.ic_state() != MEGAMORPHIC) { if (maps.empty() && nexus.ic_state() != MEGAMORPHIC) {
return NewInsufficientFeedback(kind); return NewInsufficientFeedback(kind);
} }
base::Optional<NameRef> name =
static_name.has_value() ? static_name : GetNameFeedback(nexus);
if (name.has_value()) { if (name.has_value()) {
// We rely on this invariant in JSGenericLowering. // We rely on this invariant in JSGenericLowering.
DCHECK_IMPLIES(maps.empty(), nexus.ic_state() == MEGAMORPHIC); DCHECK_IMPLIES(maps.empty(), nexus.ic_state() == MEGAMORPHIC);
@ -4806,8 +4856,8 @@ ProcessedFeedback const& JSHeapBroker::ReadFeedbackForGlobalAccess(
isolate()); isolate());
if (feedback_value->IsSmi()) { if (feedback_value->IsSmi()) {
// The wanted name belongs to a script-scope variable and the feedback tells // The wanted name belongs to a script-scope variable and the feedback
// us where to find its value. // tells us where to find its value.
int number = feedback_value->Number(); int number = feedback_value->Number();
int const script_context_index = int const script_context_index =
FeedbackNexus::ContextIndexBits::decode(number); FeedbackNexus::ContextIndexBits::decode(number);
@ -5224,6 +5274,29 @@ PropertyAccessInfo JSHeapBroker::GetPropertyAccessInfo(
return access_info; 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 { BinaryOperationFeedback const& ProcessedFeedback::AsBinaryOperation() const {
CHECK_EQ(kBinaryOperation, kind()); CHECK_EQ(kBinaryOperation, kind());
return *static_cast<BinaryOperationFeedback const*>(this); return *static_cast<BinaryOperationFeedback const*>(this);
@ -5264,6 +5337,12 @@ NamedAccessFeedback const& ProcessedFeedback::AsNamedAccess() const {
return *static_cast<NamedAccessFeedback const*>(this); return *static_cast<NamedAccessFeedback const*>(this);
} }
MinimorphicLoadPropertyAccessFeedback const&
ProcessedFeedback::AsMinimorphicPropertyAccess() const {
CHECK_EQ(kMinimorphicPropertyAccess, kind());
return *static_cast<MinimorphicLoadPropertyAccessFeedback const*>(this);
}
LiteralFeedback const& ProcessedFeedback::AsLiteral() const { LiteralFeedback const& ProcessedFeedback::AsLiteral() const {
CHECK_EQ(kLiteral, kind()); CHECK_EQ(kLiteral, kind());
return *static_cast<LiteralFeedback const*>(this); return *static_cast<LiteralFeedback const*>(this);

View File

@ -199,6 +199,11 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
CompilationDependencies* dependencies = nullptr, CompilationDependencies* dependencies = nullptr,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized); SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
MinimorphicLoadPropertyAccessInfo GetPropertyAccessInfo(
MinimorphicLoadPropertyAccessFeedback const& feedback,
FeedbackSource const& source,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
StringRef GetTypedArrayStringTag(ElementsKind kind); StringRef GetTypedArrayStringTag(ElementsKind kind);
bool ShouldBeSerializedForCompilation(const SharedFunctionInfoRef& shared, bool ShouldBeSerializedForCompilation(const SharedFunctionInfoRef& shared,
@ -288,6 +293,8 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
ZoneUnorderedMap<PropertyAccessTarget, PropertyAccessInfo, ZoneUnorderedMap<PropertyAccessTarget, PropertyAccessInfo,
PropertyAccessTarget::Hash, PropertyAccessTarget::Equal> PropertyAccessTarget::Hash, PropertyAccessTarget::Equal>
property_access_infos_; property_access_infos_;
ZoneUnorderedMap<int, MinimorphicLoadPropertyAccessInfo>
minimorphic_property_access_infos_;
ZoneVector<ObjectData*> typed_array_string_tags_; ZoneVector<ObjectData*> typed_array_string_tags_;

View File

@ -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( Reduction JSNativeContextSpecialization::ReduceNamedAccess(
Node* node, Node* value, NamedAccessFeedback const& feedback, Node* node, Node* value, NamedAccessFeedback const& feedback,
AccessMode access_mode, Node* key) { AccessMode access_mode, Node* key) {
@ -1892,6 +1923,11 @@ Reduction JSNativeContextSpecialization::ReducePropertyAccess(
case ProcessedFeedback::kNamedAccess: case ProcessedFeedback::kNamedAccess:
return ReduceNamedAccess(node, value, feedback.AsNamedAccess(), return ReduceNamedAccess(node, value, feedback.AsNamedAccess(),
access_mode, key); 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: case ProcessedFeedback::kElementAccess:
DCHECK_EQ(feedback.AsElementAccess().keyed_mode().access_mode(), DCHECK_EQ(feedback.AsElementAccess().keyed_mode().access_mode(),
access_mode); access_mode);

View File

@ -102,6 +102,10 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
Reduction ReduceNamedAccess(Node* node, Node* value, Reduction ReduceNamedAccess(Node* node, Node* value,
NamedAccessFeedback const& processed, NamedAccessFeedback const& processed,
AccessMode access_mode, Node* key = nullptr); 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, Reduction ReduceGlobalAccess(Node* node, Node* receiver, Node* value,
NameRef const& name, AccessMode access_mode, NameRef const& name, AccessMode access_mode,
Node* key = nullptr); Node* key = nullptr);

View File

@ -5,6 +5,7 @@
#ifndef V8_COMPILER_PROCESSED_FEEDBACK_H_ #ifndef V8_COMPILER_PROCESSED_FEEDBACK_H_
#define V8_COMPILER_PROCESSED_FEEDBACK_H_ #define V8_COMPILER_PROCESSED_FEEDBACK_H_
#include "src/compiler/feedback-source.h"
#include "src/compiler/heap-refs.h" #include "src/compiler/heap-refs.h"
namespace v8 { namespace v8 {
@ -19,6 +20,7 @@ class ForInFeedback;
class GlobalAccessFeedback; class GlobalAccessFeedback;
class InstanceOfFeedback; class InstanceOfFeedback;
class LiteralFeedback; class LiteralFeedback;
class MinimorphicLoadPropertyAccessFeedback;
class NamedAccessFeedback; class NamedAccessFeedback;
class RegExpLiteralFeedback; class RegExpLiteralFeedback;
class TemplateObjectFeedback; class TemplateObjectFeedback;
@ -35,6 +37,7 @@ class ProcessedFeedback : public ZoneObject {
kGlobalAccess, kGlobalAccess,
kInstanceOf, kInstanceOf,
kLiteral, kLiteral,
kMinimorphicPropertyAccess,
kNamedAccess, kNamedAccess,
kRegExpLiteral, kRegExpLiteral,
kTemplateObject, kTemplateObject,
@ -52,6 +55,8 @@ class ProcessedFeedback : public ZoneObject {
GlobalAccessFeedback const& AsGlobalAccess() const; GlobalAccessFeedback const& AsGlobalAccess() const;
InstanceOfFeedback const& AsInstanceOf() const; InstanceOfFeedback const& AsInstanceOf() const;
NamedAccessFeedback const& AsNamedAccess() const; NamedAccessFeedback const& AsNamedAccess() const;
MinimorphicLoadPropertyAccessFeedback const& AsMinimorphicPropertyAccess()
const;
LiteralFeedback const& AsLiteral() const; LiteralFeedback const& AsLiteral() const;
RegExpLiteralFeedback const& AsRegExpLiteral() const; RegExpLiteralFeedback const& AsRegExpLiteral() const;
TemplateObjectFeedback const& AsTemplateObject() const; TemplateObjectFeedback const& AsTemplateObject() const;
@ -168,6 +173,23 @@ class NamedAccessFeedback : public ProcessedFeedback {
ZoneVector<Handle<Map>> const maps_; ZoneVector<Handle<Map>> const maps_;
}; };
class MinimorphicLoadPropertyAccessFeedback : public ProcessedFeedback {
public:
MinimorphicLoadPropertyAccessFeedback(NameRef const& name,
FeedbackSlotKind slot_kind,
bool is_monomorphic,
Handle<Object> handler);
NameRef const& name() const { return name_; }
bool is_monomorphic() const { return is_monomorphic_; }
Handle<Object> handler() const { return handler_; }
private:
NameRef const name_;
bool is_monomorphic_;
Handle<Object> handler_;
};
class CallFeedback : public ProcessedFeedback { class CallFeedback : public ProcessedFeedback {
public: public:
CallFeedback(base::Optional<HeapObjectRef> target, float frequency, CallFeedback(base::Optional<HeapObjectRef> target, float frequency,

View File

@ -183,6 +183,63 @@ Node* PropertyAccessBuilder::TryBuildLoadConstantDataField(
return jsgraph()->Constant(*value); 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<Map>(),
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<Name>();
}
}
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<Map>(),
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( Node* PropertyAccessBuilder::BuildLoadDataField(
NameRef const& name, PropertyAccessInfo const& access_info, Node* receiver, NameRef const& name, PropertyAccessInfo const& access_info, Node* receiver,
Node** effect, Node** control) { Node** effect, Node** control) {
@ -192,46 +249,22 @@ Node* PropertyAccessBuilder::BuildLoadDataField(
return value; return value;
} }
FieldIndex const field_index = access_info.field_index();
Type const field_type = access_info.field_type();
MachineRepresentation const field_representation = MachineRepresentation const field_representation =
ConvertRepresentation(access_info.field_representation()); ConvertRepresentation(access_info.field_representation());
Node* storage = ResolveHolder(access_info, receiver); 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 = { FieldAccess field_access = {
kTaggedBase, kTaggedBase,
field_index.offset(), access_info.field_index().offset(),
name.object(), name.object(),
MaybeHandle<Map>(), MaybeHandle<Map>(),
field_type, access_info.field_type(),
MachineType::TypeForRepresentation(field_representation), MachineType::TypeForRepresentation(field_representation),
kFullWriteBarrier, kFullWriteBarrier,
LoadSensitivity::kCritical, LoadSensitivity::kCritical,
access_info.GetConstFieldInfo()}; access_info.GetConstFieldInfo()};
if (field_representation == MachineRepresentation::kFloat64) { if (field_representation == MachineRepresentation::kTaggedPointer ||
if (!field_index.is_inobject() || !FLAG_unbox_double_fields) { field_representation == MachineRepresentation::kCompressedPointer) {
FieldAccess const storage_access = {kTaggedBase,
field_index.offset(),
name.object(),
MaybeHandle<Map>(),
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<Name>();
}
} else if (field_representation == MachineRepresentation::kTaggedPointer ||
field_representation ==
MachineRepresentation::kCompressedPointer) {
// Remember the map of the field value, if its map is stable. This is // 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. // used by the LoadElimination to eliminate map checks on the result.
Handle<Map> field_map; Handle<Map> field_map;
@ -243,9 +276,9 @@ Node* PropertyAccessBuilder::BuildLoadDataField(
} }
} }
} }
Node* value = *effect = graph()->NewNode( return BuildLoadDataField(name, storage, field_access,
simplified()->LoadField(field_access), storage, *effect, *control); access_info.field_index().is_inobject(), effect,
return value; control);
} }
} // namespace compiler } // namespace compiler

View File

@ -25,6 +25,7 @@ class JSGraph;
class JSHeapBroker; class JSHeapBroker;
class PropertyAccessInfo; class PropertyAccessInfo;
class SimplifiedOperatorBuilder; class SimplifiedOperatorBuilder;
struct FieldAccess;
class PropertyAccessBuilder { class PropertyAccessBuilder {
public: public:
@ -62,6 +63,12 @@ class PropertyAccessBuilder {
PropertyAccessInfo const& access_info, PropertyAccessInfo const& access_info,
Node* receiver, Node** effect, Node** control); 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( static MachineRepresentation ConvertRepresentation(
Representation representation); Representation representation);
@ -81,6 +88,10 @@ class PropertyAccessBuilder {
// {access_info}. // {access_info}.
Node* ResolveHolder(PropertyAccessInfo const& access_info, Node* receiver); 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_; JSGraph* jsgraph_;
JSHeapBroker* broker_; JSHeapBroker* broker_;
CompilationDependencies* dependencies_; CompilationDependencies* dependencies_;

View File

@ -448,6 +448,9 @@ class SerializerForBackgroundCompilation {
void ProcessElementAccess(Hints const& receiver, Hints const& key, void ProcessElementAccess(Hints const& receiver, Hints const& key,
ElementAccessFeedback const& feedback, ElementAccessFeedback const& feedback,
AccessMode access_mode); AccessMode access_mode);
void ProcessMinimorphicPropertyAccess(
MinimorphicLoadPropertyAccessFeedback const& feedback,
FeedbackSource const& source);
void ProcessModuleVariableAccess( void ProcessModuleVariableAccess(
interpreter::BytecodeArrayIterator* iterator); interpreter::BytecodeArrayIterator* iterator);
@ -3052,6 +3055,13 @@ SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess(
return access_info; return access_info;
} }
void SerializerForBackgroundCompilation::ProcessMinimorphicPropertyAccess(
MinimorphicLoadPropertyAccessFeedback const& feedback,
FeedbackSource const& source) {
broker()->GetPropertyAccessInfo(feedback, source,
SerializationPolicy::kSerializeIfNeeded);
}
void SerializerForBackgroundCompilation::VisitLdaKeyedProperty( void SerializerForBackgroundCompilation::VisitLdaKeyedProperty(
BytecodeArrayIterator* iterator) { BytecodeArrayIterator* iterator) {
Hints const& key = environment()->accumulator_hints(); Hints const& key = environment()->accumulator_hints();
@ -3109,6 +3119,11 @@ void SerializerForBackgroundCompilation::ProcessNamedPropertyAccess(
ProcessNamedAccess(receiver, feedback.AsNamedAccess(), access_mode, ProcessNamedAccess(receiver, feedback.AsNamedAccess(), access_mode,
&new_accumulator_hints); &new_accumulator_hints);
break; break;
case ProcessedFeedback::kMinimorphicPropertyAccess:
DCHECK(name.equals(feedback.AsMinimorphicPropertyAccess().name()));
ProcessMinimorphicPropertyAccess(feedback.AsMinimorphicPropertyAccess(),
source);
break;
case ProcessedFeedback::kInsufficient: case ProcessedFeedback::kInsufficient:
break; break;
default: default:

View File

@ -670,6 +670,9 @@ DEFINE_BOOL(
DEFINE_BOOL(turbo_fast_api_calls, false, "enable fast API calls from TurboFan") DEFINE_BOOL(turbo_fast_api_calls, false, "enable fast API calls from TurboFan")
DEFINE_INT(reuse_opt_code_count, 0, DEFINE_INT(reuse_opt_code_count, 0,
"don't discard optimized code for the specified number of deopts.") "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. // Native context independent (NCI) code.
DEFINE_BOOL(turbo_nci, false, DEFINE_BOOL(turbo_nci, false,

View File

@ -1005,8 +1005,8 @@ int FeedbackNexus::ExtractMaps(MapHandles* maps) const {
return 0; return 0;
} }
int FeedbackNexus::ExtractMapsAndHandlers( int FeedbackNexus::ExtractMapsAndFeedbackImpl(
std::vector<std::pair<Handle<Map>, MaybeObjectHandle>>* maps_and_handlers, std::vector<MapAndFeedback>* maps_and_feedback,
bool try_update_deprecated) const { bool try_update_deprecated) const {
DCHECK(IsLoadICKind(kind()) || DCHECK(IsLoadICKind(kind()) ||
IsStoreICKind(kind()) | IsKeyedLoadICKind(kind()) || IsStoreICKind(kind()) | IsKeyedLoadICKind(kind()) ||
@ -1032,19 +1032,18 @@ int FeedbackNexus::ExtractMapsAndHandlers(
} }
const int increment = 2; const int increment = 2;
HeapObject heap_object; 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) { for (int i = 0; i < array.length(); i += increment) {
DCHECK(array.Get(i)->IsWeakOrCleared()); DCHECK(array.Get(i)->IsWeakOrCleared());
if (array.Get(i)->GetHeapObjectIfWeak(&heap_object)) { if (array.Get(i)->GetHeapObjectIfWeak(&heap_object)) {
MaybeObject handler = array.Get(i + 1); MaybeObject handler = array.Get(i + 1);
if (!handler->IsCleared()) { if (!handler->IsCleared()) {
DCHECK(IC::IsHandler(handler));
Handle<Map> map(Map::cast(heap_object), isolate); Handle<Map> map(Map::cast(heap_object), isolate);
if (try_update_deprecated && if (try_update_deprecated &&
!Map::TryUpdate(isolate, map).ToHandle(&map)) { !Map::TryUpdate(isolate, map).ToHandle(&map)) {
continue; continue;
} }
maps_and_handlers->push_back( maps_and_feedback->push_back(
MapAndHandler(map, handle(handler, isolate))); MapAndHandler(map, handle(handler, isolate)));
found++; found++;
} }
@ -1054,13 +1053,12 @@ int FeedbackNexus::ExtractMapsAndHandlers(
} else if (feedback->GetHeapObjectIfWeak(&heap_object)) { } else if (feedback->GetHeapObjectIfWeak(&heap_object)) {
MaybeObject handler = GetFeedbackExtra(); MaybeObject handler = GetFeedbackExtra();
if (!handler->IsCleared()) { if (!handler->IsCleared()) {
DCHECK(IC::IsHandler(handler));
Handle<Map> map = handle(Map::cast(heap_object), isolate); Handle<Map> map = handle(Map::cast(heap_object), isolate);
if (try_update_deprecated && if (try_update_deprecated &&
!Map::TryUpdate(isolate, map).ToHandle(&map)) { !Map::TryUpdate(isolate, map).ToHandle(&map)) {
return 0; return 0;
} }
maps_and_handlers->push_back( maps_and_feedback->push_back(
MapAndHandler(map, handle(handler, isolate))); MapAndHandler(map, handle(handler, isolate)));
return 1; return 1;
} }
@ -1069,6 +1067,42 @@ int FeedbackNexus::ExtractMapsAndHandlers(
return 0; return 0;
} }
int FeedbackNexus::ExtractMapsAndFeedback(
std::vector<MapAndFeedback>* 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<MapAndHandler>* 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> map) const { MaybeObjectHandle FeedbackNexus::FindHandlerForMap(Handle<Map> map) const {
DCHECK(IsLoadICKind(kind()) || IsStoreICKind(kind()) || DCHECK(IsLoadICKind(kind()) || IsStoreICKind(kind()) ||
IsKeyedLoadICKind(kind()) || IsKeyedStoreICKind(kind()) || IsKeyedLoadICKind(kind()) || IsKeyedStoreICKind(kind()) ||

View File

@ -62,6 +62,7 @@ enum class FeedbackSlotKind {
}; };
using MapAndHandler = std::pair<Handle<Map>, MaybeObjectHandle>; using MapAndHandler = std::pair<Handle<Map>, MaybeObjectHandle>;
using MapAndFeedback = std::pair<Handle<Map>, MaybeObjectHandle>;
inline bool IsCallICKind(FeedbackSlotKind kind) { inline bool IsCallICKind(FeedbackSlotKind kind) {
return kind == FeedbackSlotKind::kCall; return kind == FeedbackSlotKind::kCall;
@ -656,9 +657,18 @@ class V8_EXPORT_PRIVATE FeedbackNexus final {
Map GetFirstMap() const; Map GetFirstMap() const;
int ExtractMaps(MapHandles* maps) 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<MapAndHandler>* maps_and_handlers, int ExtractMapsAndHandlers(std::vector<MapAndHandler>* maps_and_handlers,
bool try_update_deprecated = false) const; bool try_update_deprecated = false) const;
MaybeObjectHandle FindHandlerForMap(Handle<Map> map) const; MaybeObjectHandle FindHandlerForMap(Handle<Map> 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<MapAndFeedback>* maps_and_feedback,
bool try_update_deprecated = false) const;
bool IsCleared() const { bool IsCleared() const {
InlineCacheState state = ic_state(); InlineCacheState state = ic_state();
@ -768,6 +778,9 @@ class V8_EXPORT_PRIVATE FeedbackNexus final {
FeedbackVector vector_; FeedbackVector vector_;
FeedbackSlot slot_; FeedbackSlot slot_;
FeedbackSlotKind kind_; FeedbackSlotKind kind_;
int ExtractMapsAndFeedbackImpl(std::vector<MapAndFeedback>* maps_and_feedback,
bool try_update_deprecated = false) const;
}; };
inline BinaryOperationHint BinaryOperationHintFromFeedback(int type_feedback); inline BinaryOperationHint BinaryOperationHintFromFeedback(int type_feedback);