[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});
}
// 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> map, Handle<Name> name, AccessMode access_mode) const {
CHECK(name->IsUniqueName());

View File

@ -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<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.
class AccessInfoFactory final {
@ -175,6 +205,9 @@ class AccessInfoFactory final {
Handle<Name> 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> name, AccessMode access_mode,

View File

@ -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<Object> handler)
: ProcessedFeedback(kMinimorphicPropertyAccess, slot_kind),
name_(name),
is_monomorphic_(is_monomorphic),
handler_(handler) {
DCHECK(IsLoadICKind(slot_kind));
}
NamedAccessFeedback::NamedAccessFeedback(NameRef const& name,
ZoneVector<Handle<Map>> 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<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
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<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;
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<NameRef> 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<BinaryOperationFeedback const*>(this);
@ -5264,6 +5337,12 @@ NamedAccessFeedback const& ProcessedFeedback::AsNamedAccess() const {
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 {
CHECK_EQ(kLiteral, kind());
return *static_cast<LiteralFeedback const*>(this);

View File

@ -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<PropertyAccessTarget, PropertyAccessInfo,
PropertyAccessTarget::Hash, PropertyAccessTarget::Equal>
property_access_infos_;
ZoneUnorderedMap<int, MinimorphicLoadPropertyAccessInfo>
minimorphic_property_access_infos_;
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(
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);

View File

@ -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);

View File

@ -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<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 {
public:
CallFeedback(base::Optional<HeapObjectRef> target, float frequency,

View File

@ -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<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(
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<Map>(),
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<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) {
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<Map> 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

View File

@ -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_;

View File

@ -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:

View File

@ -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,

View File

@ -1005,8 +1005,8 @@ int FeedbackNexus::ExtractMaps(MapHandles* maps) const {
return 0;
}
int FeedbackNexus::ExtractMapsAndHandlers(
std::vector<std::pair<Handle<Map>, MaybeObjectHandle>>* maps_and_handlers,
int FeedbackNexus::ExtractMapsAndFeedbackImpl(
std::vector<MapAndFeedback>* 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(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> 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<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 {
DCHECK(IsLoadICKind(kind()) || IsStoreICKind(kind()) ||
IsKeyedLoadICKind(kind()) || IsKeyedStoreICKind(kind()) ||

View File

@ -62,6 +62,7 @@ enum class FeedbackSlotKind {
};
using MapAndHandler = std::pair<Handle<Map>, MaybeObjectHandle>;
using MapAndFeedback = std::pair<Handle<Map>, 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<MapAndHandler>* maps_and_handlers,
bool try_update_deprecated = false) 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 {
InlineCacheState state = ic_state();
@ -768,6 +778,9 @@ class V8_EXPORT_PRIVATE FeedbackNexus final {
FeedbackVector vector_;
FeedbackSlot slot_;
FeedbackSlotKind kind_;
int ExtractMapsAndFeedbackImpl(std::vector<MapAndFeedback>* maps_and_feedback,
bool try_update_deprecated = false) const;
};
inline BinaryOperationHint BinaryOperationHintFromFeedback(int type_feedback);