Commit 0be88a57 authored by Marja Hölttä's avatar Marja Hölttä Committed by Commit Bot

[super] Optimize super property access in JSNativeContextSpecialization

This is the second reland of
https://chromium-review.googlesource.com/c/v8/v8/+/2487122 , this time
without RuntimeCallStats in the tests.

Generalize the existing property lookup machinery
(JSNCS::ReduceNamedAccess) to handle the case where the
lookup_start_object and the receiver are different objects.

Design doc: https://docs.google.com/document/d/1b_wgtExmJDLb8206jpJol-g4vJAxPs1XjEx95hwRboI/edit#heading=h.xqthbgih7l2l

Bug: v8:9237
Change-Id: I782df6e032ff8191082b425e68d68b69cef0a560
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2527092
Auto-Submit: Marja Hölttä <marja@chromium.org>
Commit-Queue: Georg Neis <neis@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71077}
parent 981bb48d
...@@ -70,12 +70,13 @@ std::ostream& operator<<(std::ostream& os, AccessMode access_mode) { ...@@ -70,12 +70,13 @@ std::ostream& operator<<(std::ostream& os, AccessMode access_mode) {
UNREACHABLE(); UNREACHABLE();
} }
ElementAccessInfo::ElementAccessInfo(ZoneVector<Handle<Map>>&& receiver_maps, ElementAccessInfo::ElementAccessInfo(
ElementsKind elements_kind, Zone* zone) ZoneVector<Handle<Map>>&& lookup_start_object_maps,
ElementsKind elements_kind, Zone* zone)
: elements_kind_(elements_kind), : elements_kind_(elements_kind),
receiver_maps_(receiver_maps), lookup_start_object_maps_(lookup_start_object_maps),
transition_sources_(zone) { transition_sources_(zone) {
CHECK(!receiver_maps.empty()); CHECK(!lookup_start_object_maps.empty());
} }
// static // static
...@@ -158,27 +159,26 @@ MinimorphicLoadPropertyAccessInfo MinimorphicLoadPropertyAccessInfo::Invalid() { ...@@ -158,27 +159,26 @@ MinimorphicLoadPropertyAccessInfo MinimorphicLoadPropertyAccessInfo::Invalid() {
PropertyAccessInfo::PropertyAccessInfo(Zone* zone) PropertyAccessInfo::PropertyAccessInfo(Zone* zone)
: kind_(kInvalid), : kind_(kInvalid),
receiver_maps_(zone), lookup_start_object_maps_(zone),
unrecorded_dependencies_(zone), unrecorded_dependencies_(zone),
field_representation_(Representation::None()), field_representation_(Representation::None()),
field_type_(Type::None()) {} field_type_(Type::None()) {}
PropertyAccessInfo::PropertyAccessInfo(Zone* zone, Kind kind, PropertyAccessInfo::PropertyAccessInfo(
MaybeHandle<JSObject> holder, Zone* zone, Kind kind, MaybeHandle<JSObject> holder,
ZoneVector<Handle<Map>>&& receiver_maps) ZoneVector<Handle<Map>>&& lookup_start_object_maps)
: kind_(kind), : kind_(kind),
receiver_maps_(receiver_maps), lookup_start_object_maps_(lookup_start_object_maps),
unrecorded_dependencies_(zone), unrecorded_dependencies_(zone),
holder_(holder), holder_(holder),
field_representation_(Representation::None()), field_representation_(Representation::None()),
field_type_(Type::None()) {} field_type_(Type::None()) {}
PropertyAccessInfo::PropertyAccessInfo(Zone* zone, Kind kind, PropertyAccessInfo::PropertyAccessInfo(
MaybeHandle<JSObject> holder, Zone* zone, Kind kind, MaybeHandle<JSObject> holder,
Handle<Object> constant, Handle<Object> constant, ZoneVector<Handle<Map>>&& lookup_start_object_maps)
ZoneVector<Handle<Map>>&& receiver_maps)
: kind_(kind), : kind_(kind),
receiver_maps_(receiver_maps), lookup_start_object_maps_(lookup_start_object_maps),
unrecorded_dependencies_(zone), unrecorded_dependencies_(zone),
constant_(constant), constant_(constant),
holder_(holder), holder_(holder),
...@@ -189,10 +189,10 @@ PropertyAccessInfo::PropertyAccessInfo( ...@@ -189,10 +189,10 @@ PropertyAccessInfo::PropertyAccessInfo(
Kind kind, MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map, Kind kind, MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map,
FieldIndex field_index, Representation field_representation, FieldIndex field_index, Representation field_representation,
Type field_type, Handle<Map> field_owner_map, MaybeHandle<Map> field_map, Type field_type, Handle<Map> field_owner_map, MaybeHandle<Map> field_map,
ZoneVector<Handle<Map>>&& receiver_maps, ZoneVector<Handle<Map>>&& lookup_start_object_maps,
ZoneVector<CompilationDependency const*>&& unrecorded_dependencies) ZoneVector<CompilationDependency const*>&& unrecorded_dependencies)
: kind_(kind), : kind_(kind),
receiver_maps_(receiver_maps), lookup_start_object_maps_(lookup_start_object_maps),
unrecorded_dependencies_(std::move(unrecorded_dependencies)), unrecorded_dependencies_(std::move(unrecorded_dependencies)),
transition_map_(transition_map), transition_map_(transition_map),
holder_(holder), holder_(holder),
...@@ -265,9 +265,10 @@ bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that, ...@@ -265,9 +265,10 @@ bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
} }
this->field_type_ = this->field_type_ =
Type::Union(this->field_type_, that->field_type_, zone); Type::Union(this->field_type_, that->field_type_, zone);
this->receiver_maps_.insert(this->receiver_maps_.end(), this->lookup_start_object_maps_.insert(
that->receiver_maps_.begin(), this->lookup_start_object_maps_.end(),
that->receiver_maps_.end()); that->lookup_start_object_maps_.begin(),
that->lookup_start_object_maps_.end());
this->unrecorded_dependencies_.insert( this->unrecorded_dependencies_.insert(
this->unrecorded_dependencies_.end(), this->unrecorded_dependencies_.end(),
that->unrecorded_dependencies_.begin(), that->unrecorded_dependencies_.begin(),
...@@ -282,9 +283,10 @@ bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that, ...@@ -282,9 +283,10 @@ bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
if (this->constant_.address() == that->constant_.address()) { if (this->constant_.address() == that->constant_.address()) {
DCHECK(this->unrecorded_dependencies_.empty()); DCHECK(this->unrecorded_dependencies_.empty());
DCHECK(that->unrecorded_dependencies_.empty()); DCHECK(that->unrecorded_dependencies_.empty());
this->receiver_maps_.insert(this->receiver_maps_.end(), this->lookup_start_object_maps_.insert(
that->receiver_maps_.begin(), this->lookup_start_object_maps_.end(),
that->receiver_maps_.end()); that->lookup_start_object_maps_.begin(),
that->lookup_start_object_maps_.end());
return true; return true;
} }
return false; return false;
...@@ -294,9 +296,10 @@ bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that, ...@@ -294,9 +296,10 @@ bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
case kStringLength: { case kStringLength: {
DCHECK(this->unrecorded_dependencies_.empty()); DCHECK(this->unrecorded_dependencies_.empty());
DCHECK(that->unrecorded_dependencies_.empty()); DCHECK(that->unrecorded_dependencies_.empty());
this->receiver_maps_.insert(this->receiver_maps_.end(), this->lookup_start_object_maps_.insert(
that->receiver_maps_.begin(), this->lookup_start_object_maps_.end(),
that->receiver_maps_.end()); that->lookup_start_object_maps_.begin(),
that->lookup_start_object_maps_.end());
return true; return true;
} }
case kModuleExport: case kModuleExport:
......
...@@ -37,25 +37,25 @@ std::ostream& operator<<(std::ostream&, AccessMode); ...@@ -37,25 +37,25 @@ std::ostream& operator<<(std::ostream&, AccessMode);
// This class encapsulates all information required to access a certain element. // This class encapsulates all information required to access a certain element.
class ElementAccessInfo final { class ElementAccessInfo final {
public: public:
ElementAccessInfo(ZoneVector<Handle<Map>>&& receiver_maps, ElementAccessInfo(ZoneVector<Handle<Map>>&& lookup_start_object_maps,
ElementsKind elements_kind, Zone* zone); ElementsKind elements_kind, Zone* zone);
ElementsKind elements_kind() const { return elements_kind_; } ElementsKind elements_kind() const { return elements_kind_; }
ZoneVector<Handle<Map>> const& receiver_maps() const { ZoneVector<Handle<Map>> const& lookup_start_object_maps() const {
return receiver_maps_; return lookup_start_object_maps_;
} }
ZoneVector<Handle<Map>> const& transition_sources() const { ZoneVector<Handle<Map>> const& transition_sources() const {
return transition_sources_; return transition_sources_;
} }
void AddTransitionSource(Handle<Map> map) { void AddTransitionSource(Handle<Map> map) {
CHECK_EQ(receiver_maps_.size(), 1); CHECK_EQ(lookup_start_object_maps_.size(), 1);
transition_sources_.push_back(map); transition_sources_.push_back(map);
} }
private: private:
ElementsKind elements_kind_; ElementsKind elements_kind_;
ZoneVector<Handle<Map>> receiver_maps_; ZoneVector<Handle<Map>> lookup_start_object_maps_;
ZoneVector<Handle<Map>> transition_sources_; ZoneVector<Handle<Map>> transition_sources_;
}; };
...@@ -128,26 +128,26 @@ class PropertyAccessInfo final { ...@@ -128,26 +128,26 @@ class PropertyAccessInfo final {
Type field_type() const { return field_type_; } Type field_type() const { return field_type_; }
Representation field_representation() const { return field_representation_; } Representation field_representation() const { return field_representation_; }
MaybeHandle<Map> field_map() const { return field_map_; } MaybeHandle<Map> field_map() const { return field_map_; }
ZoneVector<Handle<Map>> const& receiver_maps() const { ZoneVector<Handle<Map>> const& lookup_start_object_maps() const {
return receiver_maps_; return lookup_start_object_maps_;
} }
private: private:
explicit PropertyAccessInfo(Zone* zone); explicit PropertyAccessInfo(Zone* zone);
PropertyAccessInfo(Zone* zone, Kind kind, MaybeHandle<JSObject> holder, PropertyAccessInfo(Zone* zone, Kind kind, MaybeHandle<JSObject> holder,
ZoneVector<Handle<Map>>&& receiver_maps); ZoneVector<Handle<Map>>&& lookup_start_object_maps);
PropertyAccessInfo(Zone* zone, Kind kind, MaybeHandle<JSObject> holder, PropertyAccessInfo(Zone* zone, Kind kind, MaybeHandle<JSObject> holder,
Handle<Object> constant, Handle<Object> constant,
ZoneVector<Handle<Map>>&& receiver_maps); ZoneVector<Handle<Map>>&& lookup_start_object_maps);
PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder, PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
MaybeHandle<Map> transition_map, FieldIndex field_index, MaybeHandle<Map> transition_map, FieldIndex field_index,
Representation field_representation, Type field_type, Representation field_representation, Type field_type,
Handle<Map> field_owner_map, MaybeHandle<Map> field_map, Handle<Map> field_owner_map, MaybeHandle<Map> field_map,
ZoneVector<Handle<Map>>&& receiver_maps, ZoneVector<Handle<Map>>&& lookup_start_object_maps,
ZoneVector<CompilationDependency const*>&& dependencies); ZoneVector<CompilationDependency const*>&& dependencies);
Kind kind_; Kind kind_;
ZoneVector<Handle<Map>> receiver_maps_; ZoneVector<Handle<Map>> lookup_start_object_maps_;
ZoneVector<CompilationDependency const*> unrecorded_dependencies_; ZoneVector<CompilationDependency const*> unrecorded_dependencies_;
Handle<Object> constant_; Handle<Object> constant_;
MaybeHandle<Map> transition_map_; MaybeHandle<Map> transition_map_;
......
...@@ -264,7 +264,7 @@ class BytecodeGraphBuilder { ...@@ -264,7 +264,7 @@ class BytecodeGraphBuilder {
const Operator* op, Node* receiver, FeedbackSlot load_slot, const Operator* op, Node* receiver, FeedbackSlot load_slot,
FeedbackSlot call_slot); FeedbackSlot call_slot);
JSTypeHintLowering::LoweringResult TryBuildSimplifiedLoadNamed( JSTypeHintLowering::LoweringResult TryBuildSimplifiedLoadNamed(
const Operator* op, Node* receiver, FeedbackSlot slot); const Operator* op, FeedbackSlot slot);
JSTypeHintLowering::LoweringResult TryBuildSimplifiedLoadKeyed( JSTypeHintLowering::LoweringResult TryBuildSimplifiedLoadKeyed(
const Operator* op, Node* receiver, Node* key, FeedbackSlot slot); const Operator* op, Node* receiver, Node* key, FeedbackSlot slot);
JSTypeHintLowering::LoweringResult TryBuildSimplifiedStoreNamed( JSTypeHintLowering::LoweringResult TryBuildSimplifiedStoreNamed(
...@@ -2019,7 +2019,7 @@ void BytecodeGraphBuilder::VisitLdaNamedProperty() { ...@@ -2019,7 +2019,7 @@ void BytecodeGraphBuilder::VisitLdaNamedProperty() {
const Operator* op = javascript()->LoadNamed(name.object(), feedback); const Operator* op = javascript()->LoadNamed(name.object(), feedback);
JSTypeHintLowering::LoweringResult lowering = JSTypeHintLowering::LoweringResult lowering =
TryBuildSimplifiedLoadNamed(op, object, feedback.slot); TryBuildSimplifiedLoadNamed(op, feedback.slot);
if (lowering.IsExit()) return; if (lowering.IsExit()) return;
Node* node = nullptr; Node* node = nullptr;
...@@ -2052,10 +2052,24 @@ void BytecodeGraphBuilder::VisitLdaNamedPropertyFromSuper() { ...@@ -2052,10 +2052,24 @@ void BytecodeGraphBuilder::VisitLdaNamedPropertyFromSuper() {
Node* home_object = environment()->LookupAccumulator(); Node* home_object = environment()->LookupAccumulator();
NameRef name(broker(), NameRef name(broker(),
bytecode_iterator().GetConstantForIndexOperand(1, isolate())); bytecode_iterator().GetConstantForIndexOperand(1, isolate()));
const Operator* op = javascript()->LoadNamedFromSuper(name.object());
// TODO(marja, v8:9237): Use lowering.
Node* node = NewNode(op, receiver, home_object); FeedbackSource feedback =
CreateFeedbackSource(bytecode_iterator().GetIndexOperand(2));
const Operator* op =
javascript()->LoadNamedFromSuper(name.object(), feedback);
JSTypeHintLowering::LoweringResult lowering =
TryBuildSimplifiedLoadNamed(op, feedback.slot);
if (lowering.IsExit()) return;
Node* node = nullptr;
if (lowering.IsSideEffectFree()) {
node = lowering.value();
} else {
DCHECK(!lowering.Changed());
DCHECK(IrOpcode::IsFeedbackCollectingOpcode(op->opcode()));
node = NewNode(op, receiver, home_object, feedback_vector_node());
}
environment()->BindAccumulator(node, Environment::kAttachFrameState); environment()->BindAccumulator(node, Environment::kAttachFrameState);
} }
...@@ -4222,14 +4236,12 @@ BytecodeGraphBuilder::TryBuildSimplifiedGetIterator(const Operator* op, ...@@ -4222,14 +4236,12 @@ BytecodeGraphBuilder::TryBuildSimplifiedGetIterator(const Operator* op,
JSTypeHintLowering::LoweringResult JSTypeHintLowering::LoweringResult
BytecodeGraphBuilder::TryBuildSimplifiedLoadNamed(const Operator* op, BytecodeGraphBuilder::TryBuildSimplifiedLoadNamed(const Operator* op,
Node* receiver,
FeedbackSlot slot) { FeedbackSlot slot) {
if (!CanApplyTypeHintLowering(op)) return NoChange(); if (!CanApplyTypeHintLowering(op)) return NoChange();
Node* effect = environment()->GetEffectDependency(); Node* effect = environment()->GetEffectDependency();
Node* control = environment()->GetControlDependency(); Node* control = environment()->GetControlDependency();
JSTypeHintLowering::LoweringResult early_reduction = JSTypeHintLowering::LoweringResult early_reduction =
type_hint_lowering().ReduceLoadNamedOperation(op, receiver, effect, type_hint_lowering().ReduceLoadNamedOperation(op, effect, control, slot);
control, slot);
ApplyEarlyReduction(early_reduction); ApplyEarlyReduction(early_reduction);
return early_reduction; return early_reduction;
} }
......
...@@ -7648,7 +7648,7 @@ Reduction JSCallReducer::ReduceRegExpPrototypeTest(Node* node) { ...@@ -7648,7 +7648,7 @@ Reduction JSCallReducer::ReduceRegExpPrototypeTest(Node* node) {
// Add proper dependencies on the {regexp}s [[Prototype]]s. // Add proper dependencies on the {regexp}s [[Prototype]]s.
dependencies()->DependOnStablePrototypeChains( dependencies()->DependOnStablePrototypeChains(
ai_exec.receiver_maps(), kStartAtPrototype, ai_exec.lookup_start_object_maps(), kStartAtPrototype,
JSObjectRef(broker(), holder)); JSObjectRef(broker(), holder));
} else { } else {
return inference.NoChange(); return inference.NoChange();
......
...@@ -318,8 +318,10 @@ void JSGenericLowering::LowerJSLoadNamed(Node* node) { ...@@ -318,8 +318,10 @@ void JSGenericLowering::LowerJSLoadNamed(Node* node) {
} }
void JSGenericLowering::LowerJSLoadNamedFromSuper(Node* node) { void JSGenericLowering::LowerJSLoadNamedFromSuper(Node* node) {
// TODO(marja, v8:9237): Call a builtin which collects feedback.
JSLoadNamedFromSuperNode n(node); JSLoadNamedFromSuperNode n(node);
NamedAccess const& p = n.Parameters(); NamedAccess const& p = n.Parameters();
node->RemoveInput(2); // Feedback vector
node->InsertInput(zone(), 2, jsgraph()->HeapConstant(p.name())); node->InsertInput(zone(), 2, jsgraph()->HeapConstant(p.name()));
ReplaceWithRuntimeCall(node, Runtime::kLoadFromSuper); ReplaceWithRuntimeCall(node, Runtime::kLoadFromSuper);
} }
......
...@@ -172,10 +172,12 @@ Reduction JSHeapCopyReducer::Reduce(Node* node) { ...@@ -172,10 +172,12 @@ Reduction JSHeapCopyReducer::Reduce(Node* node) {
break; break;
} }
case IrOpcode::kJSLoadNamedFromSuper: { case IrOpcode::kJSLoadNamedFromSuper: {
// TODO(marja, v8:9237): Process feedback once it's added to the byte
// code.
NamedAccess const& p = NamedAccessOf(node->op()); NamedAccess const& p = NamedAccessOf(node->op());
NameRef name(broker(), p.name()); NameRef name(broker(), p.name());
if (p.feedback().IsValid()) {
broker()->ProcessFeedbackForPropertyAccess(p.feedback(),
AccessMode::kLoad, name);
}
break; break;
} }
case IrOpcode::kJSStoreNamed: { case IrOpcode::kJSStoreNamed: {
......
...@@ -102,6 +102,8 @@ Reduction JSNativeContextSpecialization::Reduce(Node* node) { ...@@ -102,6 +102,8 @@ Reduction JSNativeContextSpecialization::Reduce(Node* node) {
return ReduceJSStoreGlobal(node); return ReduceJSStoreGlobal(node);
case IrOpcode::kJSLoadNamed: case IrOpcode::kJSLoadNamed:
return ReduceJSLoadNamed(node); return ReduceJSLoadNamed(node);
case IrOpcode::kJSLoadNamedFromSuper:
return ReduceJSLoadNamedFromSuper(node);
case IrOpcode::kJSStoreNamed: case IrOpcode::kJSStoreNamed:
return ReduceJSStoreNamed(node); return ReduceJSStoreNamed(node);
case IrOpcode::kJSHasProperty: case IrOpcode::kJSHasProperty:
...@@ -432,12 +434,12 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { ...@@ -432,12 +434,12 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
// takes over, but that requires the constructor to be callable. // takes over, but that requires the constructor to be callable.
if (!receiver_map.is_callable()) return NoChange(); if (!receiver_map.is_callable()) return NoChange();
dependencies()->DependOnStablePrototypeChains(access_info.receiver_maps(), dependencies()->DependOnStablePrototypeChains(
kStartAtPrototype); access_info.lookup_start_object_maps(), kStartAtPrototype);
// Monomorphic property access. // Monomorphic property access.
access_builder.BuildCheckMaps(constructor, &effect, control, access_builder.BuildCheckMaps(constructor, &effect, control,
access_info.receiver_maps()); access_info.lookup_start_object_maps());
// Lower to OrdinaryHasInstance(C, O). // Lower to OrdinaryHasInstance(C, O).
NodeProperties::ReplaceValueInput(node, constructor, 0); NodeProperties::ReplaceValueInput(node, constructor, 0);
...@@ -462,7 +464,7 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { ...@@ -462,7 +464,7 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
if (found_on_proto) { if (found_on_proto) {
dependencies()->DependOnStablePrototypeChains( dependencies()->DependOnStablePrototypeChains(
access_info.receiver_maps(), kStartAtPrototype, access_info.lookup_start_object_maps(), kStartAtPrototype,
JSObjectRef(broker(), holder)); JSObjectRef(broker(), holder));
} }
...@@ -472,7 +474,7 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { ...@@ -472,7 +474,7 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
// Monomorphic property access. // Monomorphic property access.
access_builder.BuildCheckMaps(constructor, &effect, control, access_builder.BuildCheckMaps(constructor, &effect, control,
access_info.receiver_maps()); access_info.lookup_start_object_maps());
// Create a nested frame state inside the current method's most-recent frame // Create a nested frame state inside the current method's most-recent frame
// state that will ensure that deopts that happen after this point will not // state that will ensure that deopts that happen after this point will not
...@@ -521,10 +523,9 @@ JSNativeContextSpecialization::InferHasInPrototypeChainResult ...@@ -521,10 +523,9 @@ JSNativeContextSpecialization::InferHasInPrototypeChainResult
JSNativeContextSpecialization::InferHasInPrototypeChain( JSNativeContextSpecialization::InferHasInPrototypeChain(
Node* receiver, Node* effect, HeapObjectRef const& prototype) { Node* receiver, Node* effect, HeapObjectRef const& prototype) {
ZoneHandleSet<Map> receiver_maps; ZoneHandleSet<Map> receiver_maps;
NodeProperties::InferReceiverMapsResult result = NodeProperties::InferMapsResult result = NodeProperties::InferMapsUnsafe(
NodeProperties::InferReceiverMapsUnsafe(broker(), receiver, effect, broker(), receiver, effect, &receiver_maps);
&receiver_maps); if (result == NodeProperties::kNoMaps) return kMayBeInPrototypeChain;
if (result == NodeProperties::kNoReceiverMaps) return kMayBeInPrototypeChain;
// Try to determine either that all of the {receiver_maps} have the given // Try to determine either that all of the {receiver_maps} have the given
// {prototype} in their chain, or that none do. If we can't tell, return // {prototype} in their chain, or that none do. If we can't tell, return
...@@ -533,7 +534,7 @@ JSNativeContextSpecialization::InferHasInPrototypeChain( ...@@ -533,7 +534,7 @@ JSNativeContextSpecialization::InferHasInPrototypeChain(
bool none = true; bool none = true;
for (size_t i = 0; i < receiver_maps.size(); ++i) { for (size_t i = 0; i < receiver_maps.size(); ++i) {
MapRef map(broker(), receiver_maps[i]); MapRef map(broker(), receiver_maps[i]);
if (result == NodeProperties::kUnreliableReceiverMaps && !map.is_stable()) { if (result == NodeProperties::kUnreliableMaps && !map.is_stable()) {
return kMayBeInPrototypeChain; return kMayBeInPrototypeChain;
} }
while (true) { while (true) {
...@@ -575,7 +576,7 @@ JSNativeContextSpecialization::InferHasInPrototypeChain( ...@@ -575,7 +576,7 @@ JSNativeContextSpecialization::InferHasInPrototypeChain(
if (!prototype.map().is_stable()) return kMayBeInPrototypeChain; if (!prototype.map().is_stable()) return kMayBeInPrototypeChain;
last_prototype = prototype.AsJSObject(); last_prototype = prototype.AsJSObject();
} }
WhereToStart start = result == NodeProperties::kUnreliableReceiverMaps WhereToStart start = result == NodeProperties::kUnreliableMaps
? kStartAtReceiver ? kStartAtReceiver
: kStartAtPrototype; : kStartAtPrototype;
dependencies()->DependOnStablePrototypeChains(receiver_maps, start, dependencies()->DependOnStablePrototypeChains(receiver_maps, start,
...@@ -749,8 +750,8 @@ Reduction JSNativeContextSpecialization::ReduceJSResolvePromise(Node* node) { ...@@ -749,8 +750,8 @@ Reduction JSNativeContextSpecialization::ReduceJSResolvePromise(Node* node) {
return inference.NoChange(); return inference.NoChange();
} }
dependencies()->DependOnStablePrototypeChains(access_info.receiver_maps(), dependencies()->DependOnStablePrototypeChains(
kStartAtPrototype); access_info.lookup_start_object_maps(), kStartAtPrototype);
// Simply fulfill the {promise} with the {resolution}. // Simply fulfill the {promise} with the {resolution}.
Node* value = effect = Node* value = effect =
...@@ -781,23 +782,30 @@ FieldAccess ForPropertyCellValue(MachineRepresentation representation, ...@@ -781,23 +782,30 @@ FieldAccess ForPropertyCellValue(MachineRepresentation representation,
} // namespace } // namespace
Reduction JSNativeContextSpecialization::ReduceGlobalAccess( Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
Node* node, Node* receiver, Node* value, NameRef const& name, Node* node, Node* lookup_start_object, Node* receiver, Node* value,
AccessMode access_mode, Node* key) { NameRef const& name, AccessMode access_mode, Node* key, Node* effect) {
base::Optional<PropertyCellRef> cell = base::Optional<PropertyCellRef> cell =
native_context().global_object().GetPropertyCell(name); native_context().global_object().GetPropertyCell(name);
return cell.has_value() ? ReduceGlobalAccess(node, receiver, value, name, return cell.has_value()
access_mode, key, *cell) ? ReduceGlobalAccess(node, lookup_start_object, receiver, value,
: NoChange(); name, access_mode, key, *cell, effect)
: NoChange();
} }
// TODO(neis): Try to merge this with ReduceNamedAccess by introducing a new // TODO(neis): Try to merge this with ReduceNamedAccess by introducing a new
// PropertyAccessInfo kind for global accesses and using the existing mechanism // PropertyAccessInfo kind for global accesses and using the existing mechanism
// for building loads/stores. // for building loads/stores.
// Note: The "receiver" parameter is only used for DCHECKS, but that's on
// purpose. This way we can assert the super property access cases won't hit the
// code which hasn't been modified to support super property access.
Reduction JSNativeContextSpecialization::ReduceGlobalAccess( Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
Node* node, Node* receiver, Node* value, NameRef const& name, Node* node, Node* lookup_start_object, Node* receiver, Node* value,
AccessMode access_mode, Node* key, PropertyCellRef const& property_cell) { NameRef const& name, AccessMode access_mode, Node* key,
Node* effect = NodeProperties::GetEffectInput(node); PropertyCellRef const& property_cell, Node* effect) {
Node* control = NodeProperties::GetControlInput(node); Node* control = NodeProperties::GetControlInput(node);
if (effect == nullptr) {
effect = NodeProperties::GetEffectInput(node);
}
ObjectRef property_cell_value = property_cell.value(); ObjectRef property_cell_value = property_cell.value();
if (property_cell_value.IsHeapObject() && if (property_cell_value.IsHeapObject() &&
...@@ -813,6 +821,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( ...@@ -813,6 +821,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
// We have additional constraints for stores. // We have additional constraints for stores.
if (access_mode == AccessMode::kStore) { if (access_mode == AccessMode::kStore) {
DCHECK_EQ(receiver, lookup_start_object);
if (property_details.IsReadOnly()) { if (property_details.IsReadOnly()) {
// Don't even bother trying to lower stores to read-only data properties. // Don't even bother trying to lower stores to read-only data properties.
return NoChange(); return NoChange();
...@@ -828,6 +837,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( ...@@ -828,6 +837,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
} }
} }
} else if (access_mode == AccessMode::kHas) { } else if (access_mode == AccessMode::kHas) {
DCHECK_EQ(receiver, lookup_start_object);
// has checks cannot follow the fast-path used by loads when these // has checks cannot follow the fast-path used by loads when these
// conditions hold. // conditions hold.
if ((property_details.IsConfigurable() || !property_details.IsReadOnly()) && if ((property_details.IsConfigurable() || !property_details.IsReadOnly()) &&
...@@ -841,16 +851,16 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( ...@@ -841,16 +851,16 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
effect = BuildCheckEqualsName(name, key, effect, control); effect = BuildCheckEqualsName(name, key, effect, control);
} }
// If we have a {receiver} to validate, we do so by checking that its map is // If we have a {lookup_start_object} to validate, we do so by checking that
// the (target) global proxy's map. This guarantees that in fact the receiver // its map is the (target) global proxy's map. This guarantees that in fact
// is the global proxy. // the lookup start object is the global proxy.
if (receiver != nullptr) { if (lookup_start_object != nullptr) {
effect = graph()->NewNode( effect = graph()->NewNode(
simplified()->CheckMaps( simplified()->CheckMaps(
CheckMapsFlag::kNone, CheckMapsFlag::kNone,
ZoneHandleSet<Map>( ZoneHandleSet<Map>(
HeapObjectRef(broker(), global_proxy()).map().object())), HeapObjectRef(broker(), global_proxy()).map().object())),
receiver, effect, control); lookup_start_object, effect, control);
} }
if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) { if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
...@@ -916,6 +926,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( ...@@ -916,6 +926,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
} }
} else { } else {
DCHECK_EQ(AccessMode::kStore, access_mode); DCHECK_EQ(AccessMode::kStore, access_mode);
DCHECK_EQ(receiver, lookup_start_object);
DCHECK(!property_details.IsReadOnly()); DCHECK(!property_details.IsReadOnly());
switch (property_details.cell_type()) { switch (property_details.cell_type()) {
case PropertyCellType::kUndefined: { case PropertyCellType::kUndefined: {
...@@ -1012,7 +1023,7 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) { ...@@ -1012,7 +1023,7 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) {
ReplaceWithValue(node, value, effect); ReplaceWithValue(node, value, effect);
return Replace(value); return Replace(value);
} else if (feedback.IsPropertyCell()) { } else if (feedback.IsPropertyCell()) {
return ReduceGlobalAccess(node, nullptr, nullptr, return ReduceGlobalAccess(node, nullptr, nullptr, nullptr,
NameRef(broker(), p.name()), AccessMode::kLoad, NameRef(broker(), p.name()), AccessMode::kLoad,
nullptr, feedback.property_cell()); nullptr, feedback.property_cell());
} else { } else {
...@@ -1043,9 +1054,9 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) { ...@@ -1043,9 +1054,9 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) {
ReplaceWithValue(node, value, effect, control); ReplaceWithValue(node, value, effect, control);
return Replace(value); return Replace(value);
} else if (feedback.IsPropertyCell()) { } else if (feedback.IsPropertyCell()) {
return ReduceGlobalAccess(node, nullptr, value, NameRef(broker(), p.name()), return ReduceGlobalAccess(node, nullptr, nullptr, value,
AccessMode::kStore, nullptr, NameRef(broker(), p.name()), AccessMode::kStore,
feedback.property_cell()); nullptr, feedback.property_cell());
} else { } else {
DCHECK(feedback.IsMegamorphic()); DCHECK(feedback.IsMegamorphic());
return NoChange(); return NoChange();
...@@ -1056,10 +1067,26 @@ Reduction JSNativeContextSpecialization::ReduceMinimorphicPropertyAccess( ...@@ -1056,10 +1067,26 @@ Reduction JSNativeContextSpecialization::ReduceMinimorphicPropertyAccess(
Node* node, Node* value, Node* node, Node* value,
MinimorphicLoadPropertyAccessFeedback const& feedback, MinimorphicLoadPropertyAccessFeedback const& feedback,
FeedbackSource const& source) { FeedbackSource const& source) {
Node* receiver = NodeProperties::GetValueInput(node, 0); DCHECK(node->opcode() == IrOpcode::kJSLoadNamed ||
node->opcode() == IrOpcode::kJSLoadProperty ||
node->opcode() == IrOpcode::kJSLoadNamedFromSuper);
STATIC_ASSERT(JSLoadNamedNode::ObjectIndex() == 0 &&
JSLoadPropertyNode::ObjectIndex() == 0);
Node* effect = NodeProperties::GetEffectInput(node); Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node); Node* control = NodeProperties::GetControlInput(node);
Node* lookup_start_object;
if (node->opcode() == IrOpcode::kJSLoadNamedFromSuper) {
DCHECK(FLAG_super_ic);
JSLoadNamedFromSuperNode n(node);
// Lookup start object is the __proto__ of the home object.
lookup_start_object = effect =
BuildLoadPrototypeFromObject(n.home_object(), effect, control);
} else {
lookup_start_object = NodeProperties::GetValueInput(node, 0);
}
MinimorphicLoadPropertyAccessInfo access_info = MinimorphicLoadPropertyAccessInfo access_info =
broker()->GetPropertyAccessInfo( broker()->GetPropertyAccessInfo(
feedback, source, feedback, source,
...@@ -1091,9 +1118,9 @@ Reduction JSNativeContextSpecialization::ReduceMinimorphicPropertyAccess( ...@@ -1091,9 +1118,9 @@ Reduction JSNativeContextSpecialization::ReduceMinimorphicPropertyAccess(
effect = graph()->NewNode( effect = graph()->NewNode(
simplified()->DynamicCheckMaps(flags, feedback.handler(), maps, source), simplified()->DynamicCheckMaps(flags, feedback.handler(), maps, source),
receiver, effect, control); lookup_start_object, effect, control);
value = access_builder.BuildMinimorphicLoadDataField( value = access_builder.BuildMinimorphicLoadDataField(
feedback.name(), access_info, receiver, &effect, &control); feedback.name(), access_info, lookup_start_object, &effect, &control);
ReplaceWithValue(node, value, effect, control); ReplaceWithValue(node, value, effect, control);
return Replace(value); return Replace(value);
...@@ -1108,7 +1135,8 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( ...@@ -1108,7 +1135,8 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
node->opcode() == IrOpcode::kJSStoreProperty || node->opcode() == IrOpcode::kJSStoreProperty ||
node->opcode() == IrOpcode::kJSStoreNamedOwn || node->opcode() == IrOpcode::kJSStoreNamedOwn ||
node->opcode() == IrOpcode::kJSStoreDataPropertyInLiteral || node->opcode() == IrOpcode::kJSStoreDataPropertyInLiteral ||
node->opcode() == IrOpcode::kJSHasProperty); node->opcode() == IrOpcode::kJSHasProperty ||
node->opcode() == IrOpcode::kJSLoadNamedFromSuper);
STATIC_ASSERT(JSLoadNamedNode::ObjectIndex() == 0 && STATIC_ASSERT(JSLoadNamedNode::ObjectIndex() == 0 &&
JSStoreNamedNode::ObjectIndex() == 0 && JSStoreNamedNode::ObjectIndex() == 0 &&
JSLoadPropertyNode::ObjectIndex() == 0 && JSLoadPropertyNode::ObjectIndex() == 0 &&
...@@ -1117,36 +1145,51 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( ...@@ -1117,36 +1145,51 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
JSStoreNamedNode::ObjectIndex() == 0 && JSStoreNamedNode::ObjectIndex() == 0 &&
JSStoreDataPropertyInLiteralNode::ObjectIndex() == 0 && JSStoreDataPropertyInLiteralNode::ObjectIndex() == 0 &&
JSHasPropertyNode::ObjectIndex() == 0); JSHasPropertyNode::ObjectIndex() == 0);
Node* receiver = NodeProperties::GetValueInput(node, 0); STATIC_ASSERT(JSLoadNamedFromSuperNode::ReceiverIndex() == 0);
Node* context = NodeProperties::GetContextInput(node); Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node); Node* frame_state = NodeProperties::GetFrameStateInput(node);
Node* effect = NodeProperties::GetEffectInput(node); Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node); Node* control = NodeProperties::GetControlInput(node);
// receiver = the object we pass to the accessor (if any) as the "this" value.
Node* receiver = NodeProperties::GetValueInput(node, 0);
// lookup_start_object = the object where we start looking for the property.
Node* lookup_start_object;
if (node->opcode() == IrOpcode::kJSLoadNamedFromSuper) {
DCHECK(FLAG_super_ic);
JSLoadNamedFromSuperNode n(node);
// Lookup start object is the __proto__ of the home object.
lookup_start_object = effect =
BuildLoadPrototypeFromObject(n.home_object(), effect, control);
} else {
lookup_start_object = receiver;
}
// Either infer maps from the graph or use the feedback. // Either infer maps from the graph or use the feedback.
ZoneVector<Handle<Map>> receiver_maps(zone()); ZoneVector<Handle<Map>> lookup_start_object_maps(zone());
if (!InferReceiverMaps(receiver, effect, &receiver_maps)) { if (!InferMaps(lookup_start_object, effect, &lookup_start_object_maps)) {
receiver_maps = feedback.maps(); lookup_start_object_maps = feedback.maps();
} }
RemoveImpossibleReceiverMaps(receiver, &receiver_maps); RemoveImpossibleMaps(lookup_start_object, &lookup_start_object_maps);
// Check if we have an access o.x or o.x=v where o is the target native // Check if we have an access o.x or o.x=v where o is the target native
// contexts' global proxy, and turn that into a direct access to the // contexts' global proxy, and turn that into a direct access to the
// corresponding global object instead. // corresponding global object instead.
if (receiver_maps.size() == 1) { if (lookup_start_object_maps.size() == 1) {
MapRef receiver_map(broker(), receiver_maps[0]); MapRef lookup_start_object_map(broker(), lookup_start_object_maps[0]);
if (receiver_map.equals( if (lookup_start_object_map.equals(
broker()->target_native_context().global_proxy_object().map()) && broker()->target_native_context().global_proxy_object().map()) &&
!broker()->target_native_context().global_object().IsDetached()) { !broker()->target_native_context().global_object().IsDetached()) {
return ReduceGlobalAccess(node, receiver, value, feedback.name(), return ReduceGlobalAccess(node, lookup_start_object, receiver, value,
access_mode, key); feedback.name(), access_mode, key, effect);
} }
} }
ZoneVector<PropertyAccessInfo> access_infos(zone()); ZoneVector<PropertyAccessInfo> access_infos(zone());
{ {
ZoneVector<PropertyAccessInfo> access_infos_for_feedback(zone()); ZoneVector<PropertyAccessInfo> access_infos_for_feedback(zone());
for (Handle<Map> map_handle : receiver_maps) { for (Handle<Map> map_handle : lookup_start_object_maps) {
MapRef map(broker(), map_handle); MapRef map(broker(), map_handle);
if (map.is_deprecated()) continue; if (map.is_deprecated()) continue;
PropertyAccessInfo access_info = broker()->GetPropertyAccessInfo( PropertyAccessInfo access_info = broker()->GetPropertyAccessInfo(
...@@ -1183,15 +1226,26 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( ...@@ -1183,15 +1226,26 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
// Check for the monomorphic cases. // Check for the monomorphic cases.
if (access_infos.size() == 1) { if (access_infos.size() == 1) {
PropertyAccessInfo access_info = access_infos.front(); PropertyAccessInfo access_info = access_infos.front();
// Try to build string check or number check if possible. if (receiver != lookup_start_object) {
// Otherwise build a map check. // Super property access. lookup_start_object is a JSReceiver or
if (!access_builder.TryBuildStringCheck(broker(), // null. It can't be a number, a string etc. So trying to build the
access_info.receiver_maps(), // checks in the "else if" branch doesn't make sense.
&receiver, &effect, control) && access_builder.BuildCheckMaps(lookup_start_object, &effect, control,
!access_builder.TryBuildNumberCheck(broker(), access_info.lookup_start_object_maps());
access_info.receiver_maps(),
&receiver, &effect, control)) { } else if (!access_builder.TryBuildStringCheck(
if (HasNumberMaps(broker(), access_info.receiver_maps())) { broker(), access_info.lookup_start_object_maps(), &receiver,
&effect, control) &&
!access_builder.TryBuildNumberCheck(
broker(), access_info.lookup_start_object_maps(), &receiver,
&effect, control)) {
// Try to build string check or number check if possible. Otherwise build
// a map check.
// TryBuildStringCheck and TryBuildNumberCheck don't update the receiver
// if they fail.
DCHECK_EQ(receiver, lookup_start_object);
if (HasNumberMaps(broker(), access_info.lookup_start_object_maps())) {
// We need to also let Smi {receiver}s through in this case, so // We need to also let Smi {receiver}s through in this case, so
// we construct a diamond, guarded by the Sminess of the {receiver} // we construct a diamond, guarded by the Sminess of the {receiver}
// and if {receiver} is not a Smi just emit a sequence of map checks. // and if {receiver} is not a Smi just emit a sequence of map checks.
...@@ -1205,7 +1259,7 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( ...@@ -1205,7 +1259,7 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
Node* efalse = effect; Node* efalse = effect;
{ {
access_builder.BuildCheckMaps(receiver, &efalse, if_false, access_builder.BuildCheckMaps(receiver, &efalse, if_false,
access_info.receiver_maps()); access_info.lookup_start_object_maps());
} }
control = graph()->NewNode(common()->Merge(2), if_true, if_false); control = graph()->NewNode(common()->Merge(2), if_true, if_false);
...@@ -1213,14 +1267,19 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( ...@@ -1213,14 +1267,19 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
} else { } else {
access_builder.BuildCheckMaps(receiver, &effect, control, access_builder.BuildCheckMaps(receiver, &effect, control,
access_info.receiver_maps()); access_info.lookup_start_object_maps());
} }
} else {
// At least one of TryBuildStringCheck & TryBuildNumberCheck succeeded
// and updated the receiver. Update lookup_start_object to match (they
// should be the same).
lookup_start_object = receiver;
} }
// Generate the actual property access. // Generate the actual property access.
ValueEffectControl continuation = BuildPropertyAccess( ValueEffectControl continuation = BuildPropertyAccess(
receiver, value, context, frame_state, effect, control, feedback.name(), lookup_start_object, receiver, value, context, frame_state, effect,
if_exceptions, access_info, access_mode); control, feedback.name(), if_exceptions, access_info, access_mode);
value = continuation.value(); value = continuation.value();
effect = continuation.effect(); effect = continuation.effect();
control = continuation.control(); control = continuation.control();
...@@ -1231,24 +1290,27 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( ...@@ -1231,24 +1290,27 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
ZoneVector<Node*> effects(zone()); ZoneVector<Node*> effects(zone());
ZoneVector<Node*> controls(zone()); ZoneVector<Node*> controls(zone());
// Check if {receiver} may be a number.
bool receiverissmi_possible = false;
for (PropertyAccessInfo const& access_info : access_infos) {
if (HasNumberMaps(broker(), access_info.receiver_maps())) {
receiverissmi_possible = true;
break;
}
}
// Handle the case that {receiver} may be a number.
Node* receiverissmi_control = nullptr; Node* receiverissmi_control = nullptr;
Node* receiverissmi_effect = effect; Node* receiverissmi_effect = effect;
if (receiverissmi_possible) {
Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); if (receiver == lookup_start_object) {
Node* branch = graph()->NewNode(common()->Branch(), check, control); // Check if {receiver} may be a number.
control = graph()->NewNode(common()->IfFalse(), branch); bool receiverissmi_possible = false;
receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch); for (PropertyAccessInfo const& access_info : access_infos) {
receiverissmi_effect = effect; if (HasNumberMaps(broker(), access_info.lookup_start_object_maps())) {
receiverissmi_possible = true;
break;
}
}
// Handle the case that {receiver} may be a number.
if (receiverissmi_possible) {
Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
Node* branch = graph()->NewNode(common()->Branch(), check, control);
control = graph()->NewNode(common()->IfFalse(), branch);
receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch);
receiverissmi_effect = effect;
}
} }
// Generate code for the various different property access patterns. // Generate code for the various different property access patterns.
...@@ -1256,24 +1318,25 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( ...@@ -1256,24 +1318,25 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
for (size_t j = 0; j < access_infos.size(); ++j) { for (size_t j = 0; j < access_infos.size(); ++j) {
PropertyAccessInfo const& access_info = access_infos[j]; PropertyAccessInfo const& access_info = access_infos[j];
Node* this_value = value; Node* this_value = value;
Node* this_lookup_start_object = lookup_start_object;
Node* this_receiver = receiver; Node* this_receiver = receiver;
Node* this_effect = effect; Node* this_effect = effect;
Node* this_control = fallthrough_control; Node* this_control = fallthrough_control;
// Perform map check on {receiver}. // Perform map check on {lookup_start_object}.
ZoneVector<Handle<Map>> const& receiver_maps = ZoneVector<Handle<Map>> const& lookup_start_object_maps =
access_info.receiver_maps(); access_info.lookup_start_object_maps();
{ {
// Whether to insert a dedicated MapGuard node into the // Whether to insert a dedicated MapGuard node into the
// effect to be able to learn from the control flow. // effect to be able to learn from the control flow.
bool insert_map_guard = true; bool insert_map_guard = true;
// Check maps for the {receiver}s. // Check maps for the {lookup_start_object}s.
if (j == access_infos.size() - 1) { if (j == access_infos.size() - 1) {
// Last map check on the fallthrough control path, do a // Last map check on the fallthrough control path, do a
// conditional eager deoptimization exit here. // conditional eager deoptimization exit here.
access_builder.BuildCheckMaps(receiver, &this_effect, this_control, access_builder.BuildCheckMaps(lookup_start_object, &this_effect,
receiver_maps); this_control, lookup_start_object_maps);
fallthrough_control = nullptr; fallthrough_control = nullptr;
// Don't insert a MapGuard in this case, as the CheckMaps // Don't insert a MapGuard in this case, as the CheckMaps
...@@ -1281,14 +1344,14 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( ...@@ -1281,14 +1344,14 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
// along the effect chain. // along the effect chain.
insert_map_guard = false; insert_map_guard = false;
} else { } else {
// Explicitly branch on the {receiver_maps}. // Explicitly branch on the {lookup_start_object_maps}.
ZoneHandleSet<Map> maps; ZoneHandleSet<Map> maps;
for (Handle<Map> map : receiver_maps) { for (Handle<Map> map : lookup_start_object_maps) {
maps.insert(map, graph()->zone()); maps.insert(map, graph()->zone());
} }
Node* check = this_effect = Node* check = this_effect =
graph()->NewNode(simplified()->CompareMaps(maps), receiver, graph()->NewNode(simplified()->CompareMaps(maps),
this_effect, this_control); lookup_start_object, this_effect, this_control);
Node* branch = Node* branch =
graph()->NewNode(common()->Branch(), check, this_control); graph()->NewNode(common()->Branch(), check, this_control);
fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
...@@ -1296,8 +1359,9 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( ...@@ -1296,8 +1359,9 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
} }
// The Number case requires special treatment to also deal with Smis. // The Number case requires special treatment to also deal with Smis.
if (HasNumberMaps(broker(), receiver_maps)) { if (HasNumberMaps(broker(), lookup_start_object_maps)) {
// Join this check with the "receiver is smi" check above. // Join this check with the "receiver is smi" check above.
DCHECK_EQ(receiver, lookup_start_object);
DCHECK_NOT_NULL(receiverissmi_effect); DCHECK_NOT_NULL(receiverissmi_effect);
DCHECK_NOT_NULL(receiverissmi_control); DCHECK_NOT_NULL(receiverissmi_control);
this_control = graph()->NewNode(common()->Merge(2), this_control, this_control = graph()->NewNode(common()->Merge(2), this_control,
...@@ -1306,7 +1370,7 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( ...@@ -1306,7 +1370,7 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
receiverissmi_effect, this_control); receiverissmi_effect, this_control);
receiverissmi_effect = receiverissmi_control = nullptr; receiverissmi_effect = receiverissmi_control = nullptr;
// The {receiver} can also be a Smi in this case, so // The {lookup_start_object} can also be a Smi in this case, so
// a MapGuard doesn't make sense for this at all. // a MapGuard doesn't make sense for this at all.
insert_map_guard = false; insert_map_guard = false;
} }
...@@ -1314,29 +1378,32 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( ...@@ -1314,29 +1378,32 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
// Introduce a MapGuard to learn from this on the effect chain. // Introduce a MapGuard to learn from this on the effect chain.
if (insert_map_guard) { if (insert_map_guard) {
ZoneHandleSet<Map> maps; ZoneHandleSet<Map> maps;
for (auto receiver_map : receiver_maps) { for (auto lookup_start_object_map : lookup_start_object_maps) {
maps.insert(receiver_map, graph()->zone()); maps.insert(lookup_start_object_map, graph()->zone());
} }
this_effect = graph()->NewNode(simplified()->MapGuard(maps), receiver, this_effect =
this_effect, this_control); graph()->NewNode(simplified()->MapGuard(maps),
lookup_start_object, this_effect, this_control);
} }
// If all {receiver_maps} are Strings we also need to rename the // If all {lookup_start_object_maps} are Strings we also need to rename
// {receiver} here to make sure that TurboFan knows that along this // the {lookup_start_object} here to make sure that TurboFan knows that
// path the {this_receiver} is a String. This is because we want // along this path the {this_lookup_start_object} is a String. This is
// strict checking of types, for example for StringLength operators. // because we want strict checking of types, for example for
if (HasOnlyStringMaps(broker(), receiver_maps)) { // StringLength operators.
this_receiver = this_effect = if (HasOnlyStringMaps(broker(), lookup_start_object_maps)) {
graph()->NewNode(common()->TypeGuard(Type::String()), receiver, DCHECK_EQ(receiver, lookup_start_object);
this_effect, this_control); this_lookup_start_object = this_receiver = this_effect =
graph()->NewNode(common()->TypeGuard(Type::String()),
lookup_start_object, this_effect, this_control);
} }
} }
// Generate the actual property access. // Generate the actual property access.
ValueEffectControl continuation = ValueEffectControl continuation = BuildPropertyAccess(
BuildPropertyAccess(this_receiver, this_value, context, frame_state, this_lookup_start_object, this_receiver, this_value, context,
this_effect, this_control, feedback.name(), frame_state, this_effect, this_control, feedback.name(),
if_exceptions, access_info, access_mode); if_exceptions, access_info, access_mode);
values.push_back(continuation.value()); values.push_back(continuation.value());
effects.push_back(continuation.effect()); effects.push_back(continuation.effect());
controls.push_back(continuation.control()); controls.push_back(continuation.control());
...@@ -1428,6 +1495,17 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { ...@@ -1428,6 +1495,17 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
FeedbackSource(p.feedback()), AccessMode::kLoad); FeedbackSource(p.feedback()), AccessMode::kLoad);
} }
Reduction JSNativeContextSpecialization::ReduceJSLoadNamedFromSuper(
Node* node) {
JSLoadNamedFromSuperNode n(node);
NamedAccess const& p = n.Parameters();
NameRef name(broker(), p.name());
if (!p.feedback().IsValid()) return NoChange();
return ReducePropertyAccess(node, nullptr, name, jsgraph()->Dead(),
FeedbackSource(p.feedback()), AccessMode::kLoad);
}
Reduction JSNativeContextSpecialization::ReduceJSGetIterator(Node* node) { Reduction JSNativeContextSpecialization::ReduceJSGetIterator(Node* node) {
JSGetIteratorNode n(node); JSGetIteratorNode n(node);
GetIteratorParameters const& p = n.Parameters(); GetIteratorParameters const& p = n.Parameters();
...@@ -1571,20 +1649,20 @@ base::Optional<JSTypedArrayRef> GetTypedArrayConstant(JSHeapBroker* broker, ...@@ -1571,20 +1649,20 @@ base::Optional<JSTypedArrayRef> GetTypedArrayConstant(JSHeapBroker* broker,
} }
} // namespace } // namespace
void JSNativeContextSpecialization::RemoveImpossibleReceiverMaps( void JSNativeContextSpecialization::RemoveImpossibleMaps(
Node* receiver, ZoneVector<Handle<Map>>* receiver_maps) const { Node* object, ZoneVector<Handle<Map>>* maps) const {
base::Optional<MapRef> root_map = InferReceiverRootMap(receiver); base::Optional<MapRef> root_map = InferRootMap(object);
if (root_map.has_value()) { if (root_map.has_value()) {
DCHECK(!root_map->is_abandoned_prototype_map()); DCHECK(!root_map->is_abandoned_prototype_map());
receiver_maps->erase( maps->erase(
std::remove_if(receiver_maps->begin(), receiver_maps->end(), std::remove_if(maps->begin(), maps->end(),
[root_map, this](Handle<Map> map) { [root_map, this](Handle<Map> map) {
MapRef map_ref(broker(), map); MapRef map_ref(broker(), map);
return map_ref.is_abandoned_prototype_map() || return map_ref.is_abandoned_prototype_map() ||
(map_ref.FindRootMap().has_value() && (map_ref.FindRootMap().has_value() &&
!map_ref.FindRootMap()->equals(*root_map)); !map_ref.FindRootMap()->equals(*root_map));
}), }),
receiver_maps->end()); maps->end());
} }
} }
...@@ -1598,9 +1676,9 @@ JSNativeContextSpecialization::TryRefineElementAccessFeedback( ...@@ -1598,9 +1676,9 @@ JSNativeContextSpecialization::TryRefineElementAccessFeedback(
if (!use_inference) return feedback; if (!use_inference) return feedback;
ZoneVector<Handle<Map>> inferred_maps(zone()); ZoneVector<Handle<Map>> inferred_maps(zone());
if (!InferReceiverMaps(receiver, effect, &inferred_maps)) return feedback; if (!InferMaps(receiver, effect, &inferred_maps)) return feedback;
RemoveImpossibleReceiverMaps(receiver, &inferred_maps); RemoveImpossibleMaps(receiver, &inferred_maps);
// TODO(neis): After Refine, the resulting feedback can still contain // TODO(neis): After Refine, the resulting feedback can still contain
// impossible maps when a target is kept only because more than one of its // impossible maps when a target is kept only because more than one of its
// sources was inferred. Think of a way to completely rule out impossible // sources was inferred. Think of a way to completely rule out impossible
...@@ -1667,7 +1745,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( ...@@ -1667,7 +1745,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
// the zone allocation of this vector. // the zone allocation of this vector.
ZoneVector<MapRef> prototype_maps(zone()); ZoneVector<MapRef> prototype_maps(zone());
for (ElementAccessInfo const& access_info : access_infos) { for (ElementAccessInfo const& access_info : access_infos) {
for (Handle<Map> map : access_info.receiver_maps()) { for (Handle<Map> map : access_info.lookup_start_object_maps()) {
MapRef receiver_map(broker(), map); MapRef receiver_map(broker(), map);
// If the {receiver_map} has a prototype and its elements backing // If the {receiver_map} has a prototype and its elements backing
// store is either holey, or we have a potentially growing store, // store is either holey, or we have a potentially growing store,
...@@ -1714,9 +1792,10 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( ...@@ -1714,9 +1792,10 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
ElementAccessInfo access_info = access_infos.front(); ElementAccessInfo access_info = access_infos.front();
// Perform possible elements kind transitions. // Perform possible elements kind transitions.
MapRef transition_target(broker(), access_info.receiver_maps().front()); MapRef transition_target(broker(),
access_info.lookup_start_object_maps().front());
for (auto source : access_info.transition_sources()) { for (auto source : access_info.transition_sources()) {
DCHECK_EQ(access_info.receiver_maps().size(), 1); DCHECK_EQ(access_info.lookup_start_object_maps().size(), 1);
MapRef transition_source(broker(), source); MapRef transition_source(broker(), source);
effect = graph()->NewNode( effect = graph()->NewNode(
simplified()->TransitionElementsKind(ElementsTransition( simplified()->TransitionElementsKind(ElementsTransition(
...@@ -1738,7 +1817,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( ...@@ -1738,7 +1817,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
// Perform map check on the {receiver}. // Perform map check on the {receiver}.
access_builder.BuildCheckMaps(receiver, &effect, control, access_builder.BuildCheckMaps(receiver, &effect, control,
access_info.receiver_maps()); access_info.lookup_start_object_maps());
// Access the actual element. // Access the actual element.
ValueEffectControl continuation = ValueEffectControl continuation =
...@@ -1765,10 +1844,11 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( ...@@ -1765,10 +1844,11 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
Node* this_control = fallthrough_control; Node* this_control = fallthrough_control;
// Perform possible elements kind transitions. // Perform possible elements kind transitions.
MapRef transition_target(broker(), access_info.receiver_maps().front()); MapRef transition_target(broker(),
access_info.lookup_start_object_maps().front());
for (auto source : access_info.transition_sources()) { for (auto source : access_info.transition_sources()) {
MapRef transition_source(broker(), source); MapRef transition_source(broker(), source);
DCHECK_EQ(access_info.receiver_maps().size(), 1); DCHECK_EQ(access_info.lookup_start_object_maps().size(), 1);
this_effect = graph()->NewNode( this_effect = graph()->NewNode(
simplified()->TransitionElementsKind(ElementsTransition( simplified()->TransitionElementsKind(ElementsTransition(
IsSimpleMapChangeTransition(transition_source.elements_kind(), IsSimpleMapChangeTransition(transition_source.elements_kind(),
...@@ -1781,7 +1861,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( ...@@ -1781,7 +1861,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
// Perform map check(s) on {receiver}. // Perform map check(s) on {receiver}.
ZoneVector<Handle<Map>> const& receiver_maps = ZoneVector<Handle<Map>> const& receiver_maps =
access_info.receiver_maps(); access_info.lookup_start_object_maps();
if (j == access_infos.size() - 1) { if (j == access_infos.size() - 1) {
// Last map check on the fallthrough control path, do a // Last map check on the fallthrough control path, do a
// conditional eager deoptimization exit here. // conditional eager deoptimization exit here.
...@@ -1928,7 +2008,8 @@ Reduction JSNativeContextSpecialization::ReducePropertyAccess( ...@@ -1928,7 +2008,8 @@ Reduction JSNativeContextSpecialization::ReducePropertyAccess(
node->opcode() == IrOpcode::kJSHasProperty || node->opcode() == IrOpcode::kJSHasProperty ||
node->opcode() == IrOpcode::kJSLoadNamed || node->opcode() == IrOpcode::kJSLoadNamed ||
node->opcode() == IrOpcode::kJSStoreNamed || node->opcode() == IrOpcode::kJSStoreNamed ||
node->opcode() == IrOpcode::kJSStoreNamedOwn); node->opcode() == IrOpcode::kJSStoreNamedOwn ||
node->opcode() == IrOpcode::kJSLoadNamedFromSuper);
DCHECK_GE(node->op()->ControlOutputCount(), 1); DCHECK_GE(node->op()->ControlOutputCount(), 1);
ProcessedFeedback const& feedback = ProcessedFeedback const& feedback =
...@@ -1949,6 +2030,7 @@ Reduction JSNativeContextSpecialization::ReducePropertyAccess( ...@@ -1949,6 +2030,7 @@ Reduction JSNativeContextSpecialization::ReducePropertyAccess(
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);
DCHECK_NE(node->opcode(), IrOpcode::kJSLoadNamedFromSuper);
return ReduceElementAccess(node, key, value, feedback.AsElementAccess()); return ReduceElementAccess(node, key, value, feedback.AsElementAccess());
default: default:
UNREACHABLE(); UNREACHABLE();
...@@ -2245,14 +2327,14 @@ Node* JSNativeContextSpecialization::InlineApiCall( ...@@ -2245,14 +2327,14 @@ Node* JSNativeContextSpecialization::InlineApiCall(
JSNativeContextSpecialization::ValueEffectControl JSNativeContextSpecialization::ValueEffectControl
JSNativeContextSpecialization::BuildPropertyLoad( JSNativeContextSpecialization::BuildPropertyLoad(
Node* receiver, Node* context, Node* frame_state, Node* effect, Node* lookup_start_object, Node* receiver, Node* context, Node* frame_state,
Node* control, NameRef const& name, ZoneVector<Node*>* if_exceptions, Node* effect, Node* control, NameRef const& name,
PropertyAccessInfo const& access_info) { ZoneVector<Node*>* if_exceptions, PropertyAccessInfo const& access_info) {
// Determine actual holder and perform prototype chain checks. // Determine actual holder and perform prototype chain checks.
Handle<JSObject> holder; Handle<JSObject> holder;
if (access_info.holder().ToHandle(&holder)) { if (access_info.holder().ToHandle(&holder)) {
dependencies()->DependOnStablePrototypeChains( dependencies()->DependOnStablePrototypeChains(
access_info.receiver_maps(), kStartAtPrototype, access_info.lookup_start_object_maps(), kStartAtPrototype,
JSObjectRef(broker(), holder)); JSObjectRef(broker(), holder));
} }
...@@ -2270,12 +2352,13 @@ JSNativeContextSpecialization::BuildPropertyLoad( ...@@ -2270,12 +2352,13 @@ JSNativeContextSpecialization::BuildPropertyLoad(
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForCellValue()), graph()->NewNode(simplified()->LoadField(AccessBuilder::ForCellValue()),
cell, effect, control); cell, effect, control);
} else if (access_info.IsStringLength()) { } else if (access_info.IsStringLength()) {
DCHECK_EQ(receiver, lookup_start_object);
value = graph()->NewNode(simplified()->StringLength(), receiver); value = graph()->NewNode(simplified()->StringLength(), receiver);
} else { } else {
DCHECK(access_info.IsDataField() || access_info.IsDataConstant()); DCHECK(access_info.IsDataField() || access_info.IsDataConstant());
PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies()); PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies());
value = access_builder.BuildLoadDataField(name, access_info, receiver, value = access_builder.BuildLoadDataField(
&effect, &control); name, access_info, lookup_start_object, &effect, &control);
} }
return ValueEffectControl(value, effect, control); return ValueEffectControl(value, effect, control);
...@@ -2288,7 +2371,7 @@ JSNativeContextSpecialization::BuildPropertyTest( ...@@ -2288,7 +2371,7 @@ JSNativeContextSpecialization::BuildPropertyTest(
Handle<JSObject> holder; Handle<JSObject> holder;
if (access_info.holder().ToHandle(&holder)) { if (access_info.holder().ToHandle(&holder)) {
dependencies()->DependOnStablePrototypeChains( dependencies()->DependOnStablePrototypeChains(
access_info.receiver_maps(), kStartAtPrototype, access_info.lookup_start_object_maps(), kStartAtPrototype,
JSObjectRef(broker(), holder)); JSObjectRef(broker(), holder));
} }
...@@ -2299,19 +2382,23 @@ JSNativeContextSpecialization::BuildPropertyTest( ...@@ -2299,19 +2382,23 @@ JSNativeContextSpecialization::BuildPropertyTest(
JSNativeContextSpecialization::ValueEffectControl JSNativeContextSpecialization::ValueEffectControl
JSNativeContextSpecialization::BuildPropertyAccess( JSNativeContextSpecialization::BuildPropertyAccess(
Node* receiver, Node* value, Node* context, Node* frame_state, Node* effect, Node* lookup_start_object, Node* receiver, Node* value, Node* context,
Node* control, NameRef const& name, ZoneVector<Node*>* if_exceptions, Node* frame_state, Node* effect, Node* control, NameRef const& name,
PropertyAccessInfo const& access_info, AccessMode access_mode) { ZoneVector<Node*>* if_exceptions, PropertyAccessInfo const& access_info,
AccessMode access_mode) {
switch (access_mode) { switch (access_mode) {
case AccessMode::kLoad: case AccessMode::kLoad:
return BuildPropertyLoad(receiver, context, frame_state, effect, control, return BuildPropertyLoad(lookup_start_object, receiver, context,
name, if_exceptions, access_info); frame_state, effect, control, name,
if_exceptions, access_info);
case AccessMode::kStore: case AccessMode::kStore:
case AccessMode::kStoreInLiteral: case AccessMode::kStoreInLiteral:
DCHECK_EQ(receiver, lookup_start_object);
return BuildPropertyStore(receiver, value, context, frame_state, effect, return BuildPropertyStore(receiver, value, context, frame_state, effect,
control, name, if_exceptions, access_info, control, name, if_exceptions, access_info,
access_mode); access_mode);
case AccessMode::kHas: case AccessMode::kHas:
DCHECK_EQ(receiver, lookup_start_object);
return BuildPropertyTest(effect, control, access_info); return BuildPropertyTest(effect, control, access_info);
} }
UNREACHABLE(); UNREACHABLE();
...@@ -2328,7 +2415,7 @@ JSNativeContextSpecialization::BuildPropertyStore( ...@@ -2328,7 +2415,7 @@ JSNativeContextSpecialization::BuildPropertyStore(
if (access_info.holder().ToHandle(&holder)) { if (access_info.holder().ToHandle(&holder)) {
DCHECK_NE(AccessMode::kStoreInLiteral, access_mode); DCHECK_NE(AccessMode::kStoreInLiteral, access_mode);
dependencies()->DependOnStablePrototypeChains( dependencies()->DependOnStablePrototypeChains(
access_info.receiver_maps(), kStartAtPrototype, access_info.lookup_start_object_maps(), kStartAtPrototype,
JSObjectRef(broker(), holder)); JSObjectRef(broker(), holder));
} }
...@@ -2591,7 +2678,8 @@ JSNativeContextSpecialization::BuildElementAccess( ...@@ -2591,7 +2678,8 @@ JSNativeContextSpecialization::BuildElementAccess(
// TODO(bmeurer): We currently specialize based on elements kind. We should // TODO(bmeurer): We currently specialize based on elements kind. We should
// also be able to properly support strings and other JSObjects here. // also be able to properly support strings and other JSObjects here.
ElementsKind elements_kind = access_info.elements_kind(); ElementsKind elements_kind = access_info.elements_kind();
ZoneVector<Handle<Map>> const& receiver_maps = access_info.receiver_maps(); ZoneVector<Handle<Map>> const& receiver_maps =
access_info.lookup_start_object_maps();
if (IsTypedArrayElementsKind(elements_kind)) { if (IsTypedArrayElementsKind(elements_kind)) {
Node* buffer_or_receiver = receiver; Node* buffer_or_receiver = receiver;
...@@ -3350,42 +3438,40 @@ bool JSNativeContextSpecialization::CanTreatHoleAsUndefined( ...@@ -3350,42 +3438,40 @@ bool JSNativeContextSpecialization::CanTreatHoleAsUndefined(
return dependencies()->DependOnNoElementsProtector(); return dependencies()->DependOnNoElementsProtector();
} }
bool JSNativeContextSpecialization::InferReceiverMaps( bool JSNativeContextSpecialization::InferMaps(
Node* receiver, Node* effect, Node* object, Node* effect, ZoneVector<Handle<Map>>* maps) const {
ZoneVector<Handle<Map>>* receiver_maps) const { ZoneHandleSet<Map> map_set;
ZoneHandleSet<Map> maps; NodeProperties::InferMapsResult result =
NodeProperties::InferReceiverMapsResult result = NodeProperties::InferMapsUnsafe(broker(), object, effect, &map_set);
NodeProperties::InferReceiverMapsUnsafe(broker(), receiver, effect, if (result == NodeProperties::kReliableMaps) {
&maps); for (size_t i = 0; i < map_set.size(); ++i) {
if (result == NodeProperties::kReliableReceiverMaps) { maps->push_back(map_set[i]);
for (size_t i = 0; i < maps.size(); ++i) {
receiver_maps->push_back(maps[i]);
} }
return true; return true;
} else if (result == NodeProperties::kUnreliableReceiverMaps) { } else if (result == NodeProperties::kUnreliableMaps) {
// For untrusted receiver maps, we can still use the information // For untrusted maps, we can still use the information
// if the maps are stable. // if the maps are stable.
for (size_t i = 0; i < maps.size(); ++i) { for (size_t i = 0; i < map_set.size(); ++i) {
MapRef map(broker(), maps[i]); MapRef map(broker(), map_set[i]);
if (!map.is_stable()) return false; if (!map.is_stable()) return false;
} }
for (size_t i = 0; i < maps.size(); ++i) { for (size_t i = 0; i < map_set.size(); ++i) {
receiver_maps->push_back(maps[i]); maps->push_back(map_set[i]);
} }
return true; return true;
} }
return false; return false;
} }
base::Optional<MapRef> JSNativeContextSpecialization::InferReceiverRootMap( base::Optional<MapRef> JSNativeContextSpecialization::InferRootMap(
Node* receiver) const { Node* object) const {
HeapObjectMatcher m(receiver); HeapObjectMatcher m(object);
if (m.HasResolvedValue()) { if (m.HasResolvedValue()) {
MapRef map = m.Ref(broker()).map(); MapRef map = m.Ref(broker()).map();
return map.FindRootMap(); return map.FindRootMap();
} else if (m.IsJSCreate()) { } else if (m.IsJSCreate()) {
base::Optional<MapRef> initial_map = base::Optional<MapRef> initial_map =
NodeProperties::GetJSCreateMap(broker(), receiver); NodeProperties::GetJSCreateMap(broker(), object);
if (initial_map.has_value()) { if (initial_map.has_value()) {
if (!initial_map->FindRootMap().has_value()) { if (!initial_map->FindRootMap().has_value()) {
return base::nullopt; return base::nullopt;
...@@ -3397,6 +3483,16 @@ base::Optional<MapRef> JSNativeContextSpecialization::InferReceiverRootMap( ...@@ -3397,6 +3483,16 @@ base::Optional<MapRef> JSNativeContextSpecialization::InferReceiverRootMap(
return base::nullopt; return base::nullopt;
} }
Node* JSNativeContextSpecialization::BuildLoadPrototypeFromObject(
Node* object, Node* effect, Node* control) {
Node* map = effect =
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), object,
effect, control);
return graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMapPrototype()), map, effect,
control);
}
Graph* JSNativeContextSpecialization::graph() const { Graph* JSNativeContextSpecialization::graph() const {
return jsgraph()->graph(); return jsgraph()->graph();
} }
......
...@@ -83,6 +83,7 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final ...@@ -83,6 +83,7 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
Reduction ReduceJSLoadGlobal(Node* node); Reduction ReduceJSLoadGlobal(Node* node);
Reduction ReduceJSStoreGlobal(Node* node); Reduction ReduceJSStoreGlobal(Node* node);
Reduction ReduceJSLoadNamed(Node* node); Reduction ReduceJSLoadNamed(Node* node);
Reduction ReduceJSLoadNamedFromSuper(Node* node);
Reduction ReduceJSGetIterator(Node* node); Reduction ReduceJSGetIterator(Node* node);
Reduction ReduceJSStoreNamed(Node* node); Reduction ReduceJSStoreNamed(Node* node);
Reduction ReduceJSHasProperty(Node* node); Reduction ReduceJSHasProperty(Node* node);
...@@ -94,7 +95,7 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final ...@@ -94,7 +95,7 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
Reduction ReduceJSToObject(Node* node); Reduction ReduceJSToObject(Node* node);
Reduction ReduceElementAccess(Node* node, Node* index, Node* value, Reduction ReduceElementAccess(Node* node, Node* index, Node* value,
ElementAccessFeedback const& processed); ElementAccessFeedback const& feedback);
// In the case of non-keyed (named) accesses, pass the name as {static_name} // In the case of non-keyed (named) accesses, pass the name as {static_name}
// and use {nullptr} for {key} (load/store modes are irrelevant). // and use {nullptr} for {key} (load/store modes are irrelevant).
Reduction ReducePropertyAccess(Node* node, Node* key, Reduction ReducePropertyAccess(Node* node, Node* key,
...@@ -102,18 +103,21 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final ...@@ -102,18 +103,21 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
Node* value, FeedbackSource const& source, Node* value, FeedbackSource const& source,
AccessMode access_mode); AccessMode access_mode);
Reduction ReduceNamedAccess(Node* node, Node* value, Reduction ReduceNamedAccess(Node* node, Node* value,
NamedAccessFeedback const& processed, NamedAccessFeedback const& feedback,
AccessMode access_mode, Node* key = nullptr); AccessMode access_mode, Node* key = nullptr);
Reduction ReduceMinimorphicPropertyAccess( Reduction ReduceMinimorphicPropertyAccess(
Node* node, Node* value, Node* node, Node* value,
MinimorphicLoadPropertyAccessFeedback const& feedback, MinimorphicLoadPropertyAccessFeedback const& feedback,
FeedbackSource const& source); FeedbackSource const& source);
Reduction ReduceGlobalAccess(Node* node, Node* receiver, Node* value, Reduction ReduceGlobalAccess(Node* node, Node* lookup_start_object,
NameRef const& name, AccessMode access_mode, Node* receiver, Node* value, NameRef const& name,
Node* key = nullptr); AccessMode access_mode, Node* key = nullptr,
Reduction ReduceGlobalAccess(Node* node, Node* receiver, Node* value, Node* effect = nullptr);
NameRef const& name, AccessMode access_mode, Reduction ReduceGlobalAccess(Node* node, Node* lookup_start_object,
Node* key, PropertyCellRef const& property_cell); Node* receiver, Node* value, NameRef const& name,
AccessMode access_mode, Node* key,
PropertyCellRef const& property_cell,
Node* effect = nullptr);
Reduction ReduceElementLoadFromHeapConstant(Node* node, Node* key, Reduction ReduceElementLoadFromHeapConstant(Node* node, Node* key,
AccessMode access_mode, AccessMode access_mode,
KeyedAccessLoadMode load_mode); KeyedAccessLoadMode load_mode);
...@@ -146,14 +150,13 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final ...@@ -146,14 +150,13 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
}; };
// Construct the appropriate subgraph for property access. // Construct the appropriate subgraph for property access.
ValueEffectControl BuildPropertyAccess(Node* receiver, Node* value, ValueEffectControl BuildPropertyAccess(
Node* context, Node* frame_state, Node* lookup_start_object, Node* receiver, Node* value, Node* context,
Node* effect, Node* control, Node* frame_state, Node* effect, Node* control, NameRef const& name,
NameRef const& name, ZoneVector<Node*>* if_exceptions, PropertyAccessInfo const& access_info,
ZoneVector<Node*>* if_exceptions, AccessMode access_mode);
PropertyAccessInfo const& access_info, ValueEffectControl BuildPropertyLoad(Node* lookup_start_object,
AccessMode access_mode); Node* receiver, Node* context,
ValueEffectControl BuildPropertyLoad(Node* receiver, Node* context,
Node* frame_state, Node* effect, Node* frame_state, Node* effect,
Node* control, NameRef const& name, Node* control, NameRef const& name,
ZoneVector<Node*>* if_exceptions, ZoneVector<Node*>* if_exceptions,
...@@ -212,20 +215,19 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final ...@@ -212,20 +215,19 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
// code dependencies and might use the array protector cell. // code dependencies and might use the array protector cell.
bool CanTreatHoleAsUndefined(ZoneVector<Handle<Map>> const& receiver_maps); bool CanTreatHoleAsUndefined(ZoneVector<Handle<Map>> const& receiver_maps);
void RemoveImpossibleReceiverMaps( void RemoveImpossibleMaps(Node* object, ZoneVector<Handle<Map>>* maps) const;
Node* receiver, ZoneVector<Handle<Map>>* receiver_maps) const;
ElementAccessFeedback const& TryRefineElementAccessFeedback( ElementAccessFeedback const& TryRefineElementAccessFeedback(
ElementAccessFeedback const& feedback, Node* receiver, ElementAccessFeedback const& feedback, Node* receiver,
Node* effect) const; Node* effect) const;
// Try to infer maps for the given {receiver} at the current {effect}. // Try to infer maps for the given {object} at the current {effect}.
bool InferReceiverMaps(Node* receiver, Node* effect, bool InferMaps(Node* object, Node* effect,
ZoneVector<Handle<Map>>* receiver_maps) const; ZoneVector<Handle<Map>>* maps) const;
// Try to infer a root map for the {receiver} independent of the current // Try to infer a root map for the {object} independent of the current program
// program location. // location.
base::Optional<MapRef> InferReceiverRootMap(Node* receiver) const; base::Optional<MapRef> InferRootMap(Node* object) const;
// Checks if we know at compile time that the {receiver} either definitely // Checks if we know at compile time that the {receiver} either definitely
// has the {prototype} in it's prototype chain, or the {receiver} definitely // has the {prototype} in it's prototype chain, or the {receiver} definitely
...@@ -238,6 +240,8 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final ...@@ -238,6 +240,8 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
InferHasInPrototypeChainResult InferHasInPrototypeChain( InferHasInPrototypeChainResult InferHasInPrototypeChain(
Node* receiver, Node* effect, HeapObjectRef const& prototype); Node* receiver, Node* effect, HeapObjectRef const& prototype);
Node* BuildLoadPrototypeFromObject(Node* object, Node* effect, Node* control);
Graph* graph() const; Graph* graph() const;
JSGraph* jsgraph() const { return jsgraph_; } JSGraph* jsgraph() const { return jsgraph_; }
......
...@@ -935,12 +935,13 @@ const Operator* JSOperatorBuilder::LoadNamed(Handle<Name> name, ...@@ -935,12 +935,13 @@ const Operator* JSOperatorBuilder::LoadNamed(Handle<Name> name,
access); // parameter access); // parameter
} }
const Operator* JSOperatorBuilder::LoadNamedFromSuper(Handle<Name> name) { const Operator* JSOperatorBuilder::LoadNamedFromSuper(
Handle<Name> name, const FeedbackSource& feedback) {
static constexpr int kReceiver = 1; static constexpr int kReceiver = 1;
static constexpr int kHomeObject = 1; static constexpr int kHomeObject = 1;
static constexpr int kArity = kReceiver + kHomeObject; static constexpr int kFeedbackVector = 1;
// TODO(marja, v8:9237): Use real feedback. static constexpr int kArity = kReceiver + kHomeObject + kFeedbackVector;
NamedAccess access(LanguageMode::kSloppy, name, FeedbackSource()); NamedAccess access(LanguageMode::kSloppy, name, feedback);
return zone()->New<Operator1<NamedAccess>>( // -- return zone()->New<Operator1<NamedAccess>>( // --
IrOpcode::kJSLoadNamedFromSuper, Operator::kNoProperties, // opcode IrOpcode::kJSLoadNamedFromSuper, Operator::kNoProperties, // opcode
"JSLoadNamedFromSuper", // name "JSLoadNamedFromSuper", // name
......
...@@ -937,7 +937,8 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final ...@@ -937,7 +937,8 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* LoadProperty(FeedbackSource const& feedback); const Operator* LoadProperty(FeedbackSource const& feedback);
const Operator* LoadNamed(Handle<Name> name, FeedbackSource const& feedback); const Operator* LoadNamed(Handle<Name> name, FeedbackSource const& feedback);
const Operator* LoadNamedFromSuper(Handle<Name> name); const Operator* LoadNamedFromSuper(Handle<Name> name,
FeedbackSource const& feedback);
const Operator* StoreProperty(LanguageMode language_mode, const Operator* StoreProperty(LanguageMode language_mode,
FeedbackSource const& feedback); FeedbackSource const& feedback);
...@@ -1413,9 +1414,13 @@ class JSLoadNamedFromSuperNode final : public JSNodeWrapperBase { ...@@ -1413,9 +1414,13 @@ class JSLoadNamedFromSuperNode final : public JSNodeWrapperBase {
const NamedAccess& Parameters() const { return NamedAccessOf(node()->op()); } const NamedAccess& Parameters() const { return NamedAccessOf(node()->op()); }
#define INPUTS(V) \ // TODO(marja, v8:9237): A more intuitive order would be (home_object,
V(Receiver, receiver, 0, Object) \ // receiver, feedback_vector). The order can be changed once we no longer
V(Object, home_object, 1, Object) // delegate to Runtime_LoadFromSuper.
#define INPUTS(V) \
V(Receiver, receiver, 0, Object) \
V(HomeObject, home_object, 1, Object) \
V(FeedbackVector, feedback_vector, 2, HeapObject)
INPUTS(DEFINE_INPUT_ACCESSORS) INPUTS(DEFINE_INPUT_ACCESSORS)
#undef INPUTS #undef INPUTS
}; };
......
...@@ -513,9 +513,9 @@ JSTypeHintLowering::ReduceGetIteratorOperation(const Operator* op, ...@@ -513,9 +513,9 @@ JSTypeHintLowering::ReduceGetIteratorOperation(const Operator* op,
} }
JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceLoadNamedOperation( JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceLoadNamedOperation(
const Operator* op, Node* receiver, Node* effect, Node* control, const Operator* op, Node* effect, Node* control, FeedbackSlot slot) const {
FeedbackSlot slot) const { DCHECK(op->opcode() == IrOpcode::kJSLoadNamed ||
DCHECK_EQ(IrOpcode::kJSLoadNamed, op->opcode()); op->opcode() == IrOpcode::kJSLoadNamedFromSuper);
if (Node* node = TryBuildSoftDeopt( if (Node* node = TryBuildSoftDeopt(
slot, effect, control, slot, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) { DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
......
...@@ -144,8 +144,8 @@ class JSTypeHintLowering { ...@@ -144,8 +144,8 @@ class JSTypeHintLowering {
FeedbackSlot call_slot) const; FeedbackSlot call_slot) const;
// Potential reduction of property access operations. // Potential reduction of property access operations.
LoweringResult ReduceLoadNamedOperation(const Operator* op, Node* obj, LoweringResult ReduceLoadNamedOperation(const Operator* op, Node* effect,
Node* effect, Node* control, Node* control,
FeedbackSlot slot) const; FeedbackSlot slot) const;
LoweringResult ReduceLoadKeyedOperation(const Operator* op, Node* obj, LoweringResult ReduceLoadKeyedOperation(const Operator* op, Node* obj,
Node* key, Node* effect, Node* key, Node* effect,
......
...@@ -19,12 +19,12 @@ MapInference::MapInference(JSHeapBroker* broker, Node* object, Node* effect) ...@@ -19,12 +19,12 @@ MapInference::MapInference(JSHeapBroker* broker, Node* object, Node* effect)
: broker_(broker), object_(object) { : broker_(broker), object_(object) {
ZoneHandleSet<Map> maps; ZoneHandleSet<Map> maps;
auto result = auto result =
NodeProperties::InferReceiverMapsUnsafe(broker_, object_, effect, &maps); NodeProperties::InferMapsUnsafe(broker_, object_, effect, &maps);
maps_.insert(maps_.end(), maps.begin(), maps.end()); maps_.insert(maps_.end(), maps.begin(), maps.end());
maps_state_ = (result == NodeProperties::kUnreliableReceiverMaps) maps_state_ = (result == NodeProperties::kUnreliableMaps)
? kUnreliableDontNeedGuard ? kUnreliableDontNeedGuard
: kReliableOrGuarded; : kReliableOrGuarded;
DCHECK_EQ(maps_.empty(), result == NodeProperties::kNoReceiverMaps); DCHECK_EQ(maps_.empty(), result == NodeProperties::kNoMaps);
} }
MapInference::~MapInference() { CHECK(Safe()); } MapInference::~MapInference() { CHECK(Safe()); }
......
...@@ -349,7 +349,7 @@ base::Optional<MapRef> NodeProperties::GetJSCreateMap(JSHeapBroker* broker, ...@@ -349,7 +349,7 @@ base::Optional<MapRef> NodeProperties::GetJSCreateMap(JSHeapBroker* broker,
} }
// static // static
NodeProperties::InferReceiverMapsResult NodeProperties::InferReceiverMapsUnsafe( NodeProperties::InferMapsResult NodeProperties::InferMapsUnsafe(
JSHeapBroker* broker, Node* receiver, Node* effect, JSHeapBroker* broker, Node* receiver, Node* effect,
ZoneHandleSet<Map>* maps_return) { ZoneHandleSet<Map>* maps_return) {
HeapObjectMatcher m(receiver); HeapObjectMatcher m(receiver);
...@@ -368,11 +368,11 @@ NodeProperties::InferReceiverMapsResult NodeProperties::InferReceiverMapsUnsafe( ...@@ -368,11 +368,11 @@ NodeProperties::InferReceiverMapsResult NodeProperties::InferReceiverMapsUnsafe(
// The {receiver_map} is only reliable when we install a stability // The {receiver_map} is only reliable when we install a stability
// code dependency. // code dependency.
*maps_return = ZoneHandleSet<Map>(receiver.map().object()); *maps_return = ZoneHandleSet<Map>(receiver.map().object());
return kUnreliableReceiverMaps; return kUnreliableMaps;
} }
} }
} }
InferReceiverMapsResult result = kReliableReceiverMaps; InferMapsResult result = kReliableMaps;
while (true) { while (true) {
switch (effect->opcode()) { switch (effect->opcode()) {
case IrOpcode::kMapGuard: { case IrOpcode::kMapGuard: {
...@@ -399,9 +399,9 @@ NodeProperties::InferReceiverMapsResult NodeProperties::InferReceiverMapsUnsafe( ...@@ -399,9 +399,9 @@ NodeProperties::InferReceiverMapsResult NodeProperties::InferReceiverMapsUnsafe(
return result; return result;
} }
// We reached the allocation of the {receiver}. // We reached the allocation of the {receiver}.
return kNoReceiverMaps; return kNoMaps;
} }
result = kUnreliableReceiverMaps; // JSCreate can have side-effect. result = kUnreliableMaps; // JSCreate can have side-effect.
break; break;
} }
case IrOpcode::kJSCreatePromise: { case IrOpcode::kJSCreatePromise: {
...@@ -430,7 +430,7 @@ NodeProperties::InferReceiverMapsResult NodeProperties::InferReceiverMapsUnsafe( ...@@ -430,7 +430,7 @@ NodeProperties::InferReceiverMapsResult NodeProperties::InferReceiverMapsUnsafe(
} }
// Without alias analysis we cannot tell whether this // Without alias analysis we cannot tell whether this
// StoreField[map] affects {receiver} or not. // StoreField[map] affects {receiver} or not.
result = kUnreliableReceiverMaps; result = kUnreliableMaps;
} }
break; break;
} }
...@@ -453,25 +453,25 @@ NodeProperties::InferReceiverMapsResult NodeProperties::InferReceiverMapsUnsafe( ...@@ -453,25 +453,25 @@ NodeProperties::InferReceiverMapsResult NodeProperties::InferReceiverMapsUnsafe(
if (control->opcode() != IrOpcode::kLoop) { if (control->opcode() != IrOpcode::kLoop) {
DCHECK(control->opcode() == IrOpcode::kDead || DCHECK(control->opcode() == IrOpcode::kDead ||
control->opcode() == IrOpcode::kMerge); control->opcode() == IrOpcode::kMerge);
return kNoReceiverMaps; return kNoMaps;
} }
// Continue search for receiver map outside the loop. Since operations // Continue search for receiver map outside the loop. Since operations
// inside the loop may change the map, the result is unreliable. // inside the loop may change the map, the result is unreliable.
effect = GetEffectInput(effect, 0); effect = GetEffectInput(effect, 0);
result = kUnreliableReceiverMaps; result = kUnreliableMaps;
continue; continue;
} }
default: { default: {
DCHECK_EQ(1, effect->op()->EffectOutputCount()); DCHECK_EQ(1, effect->op()->EffectOutputCount());
if (effect->op()->EffectInputCount() != 1) { if (effect->op()->EffectInputCount() != 1) {
// Didn't find any appropriate CheckMaps node. // Didn't find any appropriate CheckMaps node.
return kNoReceiverMaps; return kNoMaps;
} }
if (!effect->op()->HasProperty(Operator::kNoWrite)) { if (!effect->op()->HasProperty(Operator::kNoWrite)) {
// Without alias/escape analysis we cannot tell whether this // Without alias/escape analysis we cannot tell whether this
// {effect} affects {receiver} or not. // {effect} affects {receiver} or not.
result = kUnreliableReceiverMaps; result = kUnreliableMaps;
} }
break; break;
} }
...@@ -479,7 +479,7 @@ NodeProperties::InferReceiverMapsResult NodeProperties::InferReceiverMapsUnsafe( ...@@ -479,7 +479,7 @@ NodeProperties::InferReceiverMapsResult NodeProperties::InferReceiverMapsUnsafe(
// Stop walking the effect chain once we hit the definition of // Stop walking the effect chain once we hit the definition of
// the {receiver} along the {effect}s. // the {receiver} along the {effect}s.
if (IsSame(receiver, effect)) return kNoReceiverMaps; if (IsSame(receiver, effect)) return kNoMaps;
// Continue with the next {effect}. // Continue with the next {effect}.
DCHECK_EQ(1, effect->op()->EffectInputCount()); DCHECK_EQ(1, effect->op()->EffectInputCount());
......
...@@ -203,15 +203,15 @@ class V8_EXPORT_PRIVATE NodeProperties final { ...@@ -203,15 +203,15 @@ class V8_EXPORT_PRIVATE NodeProperties final {
// Walks up the {effect} chain to find a witness that provides map // Walks up the {effect} chain to find a witness that provides map
// information about the {receiver}. Can look through potentially // information about the {receiver}. Can look through potentially
// side effecting nodes. // side effecting nodes.
enum InferReceiverMapsResult { enum InferMapsResult {
kNoReceiverMaps, // No receiver maps inferred. kNoMaps, // No maps inferred.
kReliableReceiverMaps, // Receiver maps can be trusted. kReliableMaps, // Maps can be trusted.
kUnreliableReceiverMaps // Receiver maps might have changed (side-effect). kUnreliableMaps // Maps might have changed (side-effect).
}; };
// DO NOT USE InferReceiverMapsUnsafe IN NEW CODE. Use MapInference instead. // DO NOT USE InferMapsUnsafe IN NEW CODE. Use MapInference instead.
static InferReceiverMapsResult InferReceiverMapsUnsafe( static InferMapsResult InferMapsUnsafe(JSHeapBroker* broker, Node* object,
JSHeapBroker* broker, Node* receiver, Node* effect, Node* effect,
ZoneHandleSet<Map>* maps_return); ZoneHandleSet<Map>* maps);
// Return the initial map of the new-target if the allocation can be inlined. // Return the initial map of the new-target if the allocation can be inlined.
static base::Optional<MapRef> GetJSCreateMap(JSHeapBroker* broker, static base::Optional<MapRef> GetJSCreateMap(JSHeapBroker* broker,
......
...@@ -1120,6 +1120,7 @@ class V8_EXPORT_PRIVATE IrOpcode { ...@@ -1120,6 +1120,7 @@ class V8_EXPORT_PRIVATE IrOpcode {
case kJSInstanceOf: case kJSInstanceOf:
case kJSLoadGlobal: case kJSLoadGlobal:
case kJSLoadNamed: case kJSLoadNamed:
case kJSLoadNamedFromSuper:
case kJSLoadProperty: case kJSLoadProperty:
case kJSStoreDataPropertyInLiteral: case kJSStoreDataPropertyInLiteral:
case kJSStoreGlobal: case kJSStoreGlobal:
......
...@@ -82,30 +82,30 @@ bool PropertyAccessBuilder::TryBuildNumberCheck( ...@@ -82,30 +82,30 @@ bool PropertyAccessBuilder::TryBuildNumberCheck(
} }
void PropertyAccessBuilder::BuildCheckMaps( void PropertyAccessBuilder::BuildCheckMaps(
Node* receiver, Node** effect, Node* control, Node* object, Node** effect, Node* control,
ZoneVector<Handle<Map>> const& receiver_maps) { ZoneVector<Handle<Map>> const& maps) {
HeapObjectMatcher m(receiver); HeapObjectMatcher m(object);
if (m.HasResolvedValue()) { if (m.HasResolvedValue()) {
MapRef receiver_map = m.Ref(broker()).map(); MapRef object_map = m.Ref(broker()).map();
if (receiver_map.is_stable()) { if (object_map.is_stable()) {
for (Handle<Map> map : receiver_maps) { for (Handle<Map> map : maps) {
if (MapRef(broker(), map).equals(receiver_map)) { if (MapRef(broker(), map).equals(object_map)) {
dependencies()->DependOnStableMap(receiver_map); dependencies()->DependOnStableMap(object_map);
return; return;
} }
} }
} }
} }
ZoneHandleSet<Map> maps; ZoneHandleSet<Map> map_set;
CheckMapsFlags flags = CheckMapsFlag::kNone; CheckMapsFlags flags = CheckMapsFlag::kNone;
for (Handle<Map> map : receiver_maps) { for (Handle<Map> map : maps) {
MapRef receiver_map(broker(), map); MapRef object_map(broker(), map);
maps.insert(receiver_map.object(), graph()->zone()); map_set.insert(object_map.object(), graph()->zone());
if (receiver_map.is_migration_target()) { if (object_map.is_migration_target()) {
flags |= CheckMapsFlag::kTryMigrateInstance; flags |= CheckMapsFlag::kTryMigrateInstance;
} }
} }
*effect = graph()->NewNode(simplified()->CheckMaps(flags, maps), receiver, *effect = graph()->NewNode(simplified()->CheckMaps(flags, map_set), object,
*effect, control); *effect, control);
} }
...@@ -124,12 +124,12 @@ Node* PropertyAccessBuilder::BuildCheckValue(Node* receiver, Effect* effect, ...@@ -124,12 +124,12 @@ Node* PropertyAccessBuilder::BuildCheckValue(Node* receiver, Effect* effect,
} }
Node* PropertyAccessBuilder::ResolveHolder( Node* PropertyAccessBuilder::ResolveHolder(
PropertyAccessInfo const& access_info, Node* receiver) { PropertyAccessInfo const& access_info, Node* lookup_start_object) {
Handle<JSObject> holder; Handle<JSObject> holder;
if (access_info.holder().ToHandle(&holder)) { if (access_info.holder().ToHandle(&holder)) {
return jsgraph()->Constant(ObjectRef(broker(), holder)); return jsgraph()->Constant(ObjectRef(broker(), holder));
} }
return receiver; return lookup_start_object;
} }
MachineRepresentation PropertyAccessBuilder::ConvertRepresentation( MachineRepresentation PropertyAccessBuilder::ConvertRepresentation(
...@@ -150,25 +150,27 @@ MachineRepresentation PropertyAccessBuilder::ConvertRepresentation( ...@@ -150,25 +150,27 @@ MachineRepresentation PropertyAccessBuilder::ConvertRepresentation(
Node* PropertyAccessBuilder::TryBuildLoadConstantDataField( Node* PropertyAccessBuilder::TryBuildLoadConstantDataField(
NameRef const& name, PropertyAccessInfo const& access_info, NameRef const& name, PropertyAccessInfo const& access_info,
Node* receiver) { Node* lookup_start_object) {
if (!access_info.IsDataConstant()) return nullptr; if (!access_info.IsDataConstant()) return nullptr;
// First, determine if we have a constant holder to load from. // First, determine if we have a constant holder to load from.
Handle<JSObject> holder; Handle<JSObject> holder;
// If {access_info} has a holder, just use it. // If {access_info} has a holder, just use it.
if (!access_info.holder().ToHandle(&holder)) { if (!access_info.holder().ToHandle(&holder)) {
// Otherwise, try to match the {receiver} as a constant. // Otherwise, try to match the {lookup_start_object} as a constant.
HeapObjectMatcher m(receiver); HeapObjectMatcher m(lookup_start_object);
if (!m.HasResolvedValue() || !m.Ref(broker()).IsJSObject()) return nullptr; if (!m.HasResolvedValue() || !m.Ref(broker()).IsJSObject()) return nullptr;
// Let us make sure the actual map of the constant receiver is among // Let us make sure the actual map of the constant lookup_start_object is
// the maps in {access_info}. // among the maps in {access_info}.
MapRef receiver_map = m.Ref(broker()).map(); MapRef lookup_start_object_map = m.Ref(broker()).map();
if (std::find_if(access_info.receiver_maps().begin(), if (std::find_if(
access_info.receiver_maps().end(), [&](Handle<Map> map) { access_info.lookup_start_object_maps().begin(),
return MapRef(broker(), map).equals(receiver_map); access_info.lookup_start_object_maps().end(), [&](Handle<Map> map) {
}) == access_info.receiver_maps().end()) { return MapRef(broker(), map).equals(lookup_start_object_map);
// The map of the receiver is not in the feedback, let us bail out. }) == access_info.lookup_start_object_maps().end()) {
// The map of the lookup_start_object is not in the feedback, let us bail
// out.
return nullptr; return nullptr;
} }
holder = m.Ref(broker()).AsJSObject().object(); holder = m.Ref(broker()).AsJSObject().object();
...@@ -253,7 +255,7 @@ Node* PropertyAccessBuilder::BuildLoadDataField(NameRef const& name, ...@@ -253,7 +255,7 @@ Node* PropertyAccessBuilder::BuildLoadDataField(NameRef const& name,
Node* PropertyAccessBuilder::BuildMinimorphicLoadDataField( Node* PropertyAccessBuilder::BuildMinimorphicLoadDataField(
NameRef const& name, MinimorphicLoadPropertyAccessInfo const& access_info, NameRef const& name, MinimorphicLoadPropertyAccessInfo const& access_info,
Node* receiver, Node** effect, Node** control) { Node* lookup_start_object, Node** effect, Node** control) {
DCHECK_NULL(dependencies()); DCHECK_NULL(dependencies());
MachineRepresentation const field_representation = MachineRepresentation const field_representation =
ConvertRepresentation(access_info.field_representation()); ConvertRepresentation(access_info.field_representation());
...@@ -268,22 +270,22 @@ Node* PropertyAccessBuilder::BuildMinimorphicLoadDataField( ...@@ -268,22 +270,22 @@ Node* PropertyAccessBuilder::BuildMinimorphicLoadDataField(
kFullWriteBarrier, kFullWriteBarrier,
LoadSensitivity::kCritical, LoadSensitivity::kCritical,
ConstFieldInfo::None()}; ConstFieldInfo::None()};
return BuildLoadDataField(name, receiver, field_access, return BuildLoadDataField(name, lookup_start_object, field_access,
access_info.is_inobject(), effect, control); 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** effect, Node** control) { Node* lookup_start_object, Node** effect, Node** control) {
DCHECK(access_info.IsDataField() || access_info.IsDataConstant()); DCHECK(access_info.IsDataField() || access_info.IsDataConstant());
if (Node* value = if (Node* value = TryBuildLoadConstantDataField(name, access_info,
TryBuildLoadConstantDataField(name, access_info, receiver)) { lookup_start_object)) {
return value; return value;
} }
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, lookup_start_object);
FieldAccess field_access = { FieldAccess field_access = {
kTaggedBase, kTaggedBase,
......
...@@ -45,13 +45,13 @@ class PropertyAccessBuilder { ...@@ -45,13 +45,13 @@ class PropertyAccessBuilder {
// TODO(jgruber): Remove the untyped version once all uses are // TODO(jgruber): Remove the untyped version once all uses are
// updated. // updated.
void BuildCheckMaps(Node* receiver, Node** effect, Node* control, void BuildCheckMaps(Node* object, Node** effect, Node* control,
ZoneVector<Handle<Map>> const& receiver_maps); ZoneVector<Handle<Map>> const& maps);
void BuildCheckMaps(Node* receiver, Effect* effect, Control control, void BuildCheckMaps(Node* object, Effect* effect, Control control,
ZoneVector<Handle<Map>> const& receiver_maps) { ZoneVector<Handle<Map>> const& maps) {
Node* e = *effect; Node* e = *effect;
Node* c = control; Node* c = control;
BuildCheckMaps(receiver, &e, c, receiver_maps); BuildCheckMaps(object, &e, c, maps);
*effect = e; *effect = e;
} }
Node* BuildCheckValue(Node* receiver, Effect* effect, Control control, Node* BuildCheckValue(Node* receiver, Effect* effect, Control control,
...@@ -61,13 +61,14 @@ class PropertyAccessBuilder { ...@@ -61,13 +61,14 @@ class PropertyAccessBuilder {
// properties (without heap-object or map checks). // properties (without heap-object or map checks).
Node* BuildLoadDataField(NameRef const& name, Node* BuildLoadDataField(NameRef const& name,
PropertyAccessInfo const& access_info, PropertyAccessInfo const& access_info,
Node* receiver, Node** effect, Node** control); Node* lookup_start_object, Node** effect,
Node** control);
// Builds the load for data-field access for minimorphic loads that use // Builds the load for data-field access for minimorphic loads that use
// dynamic map checks. These cannot depend on any information from the maps. // dynamic map checks. These cannot depend on any information from the maps.
Node* BuildMinimorphicLoadDataField( Node* BuildMinimorphicLoadDataField(
NameRef const& name, MinimorphicLoadPropertyAccessInfo const& access_info, NameRef const& name, MinimorphicLoadPropertyAccessInfo const& access_info,
Node* receiver, Node** effect, Node** control); Node* lookup_start_object, Node** effect, Node** control);
static MachineRepresentation ConvertRepresentation( static MachineRepresentation ConvertRepresentation(
Representation representation); Representation representation);
...@@ -83,10 +84,11 @@ class PropertyAccessBuilder { ...@@ -83,10 +84,11 @@ class PropertyAccessBuilder {
Node* TryBuildLoadConstantDataField(NameRef const& name, Node* TryBuildLoadConstantDataField(NameRef const& name,
PropertyAccessInfo const& access_info, PropertyAccessInfo const& access_info,
Node* receiver); Node* lookup_start_object);
// Returns a node with the holder for the property access described by // Returns a node with the holder for the property access described by
// {access_info}. // {access_info}.
Node* ResolveHolder(PropertyAccessInfo const& access_info, Node* receiver); Node* ResolveHolder(PropertyAccessInfo const& access_info,
Node* lookup_start_object);
Node* BuildLoadDataField(NameRef const& name, Node* holder, Node* BuildLoadDataField(NameRef const& name, Node* holder,
FieldAccess& field_access, bool is_inobject, FieldAccess& field_access, bool is_inobject,
......
...@@ -460,8 +460,14 @@ class SerializerForBackgroundCompilation { ...@@ -460,8 +460,14 @@ class SerializerForBackgroundCompilation {
bool honor_bailout_on_uninitialized); bool honor_bailout_on_uninitialized);
void ProcessNamedPropertyAccess(Hints* receiver, NameRef const& name, void ProcessNamedPropertyAccess(Hints* receiver, NameRef const& name,
FeedbackSlot slot, AccessMode access_mode); FeedbackSlot slot, AccessMode access_mode);
void ProcessNamedSuperPropertyAccess(Hints* receiver, NameRef const& name,
FeedbackSlot slot,
AccessMode access_mode);
void ProcessNamedAccess(Hints* receiver, NamedAccessFeedback const& feedback, void ProcessNamedAccess(Hints* receiver, NamedAccessFeedback const& feedback,
AccessMode access_mode, Hints* result_hints); AccessMode access_mode, Hints* result_hints);
void ProcessNamedSuperAccess(Hints* receiver,
NamedAccessFeedback const& feedback,
AccessMode access_mode, Hints* result_hints);
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);
...@@ -495,7 +501,8 @@ class SerializerForBackgroundCompilation { ...@@ -495,7 +501,8 @@ class SerializerForBackgroundCompilation {
bool honor_bailout_on_uninitialized); bool honor_bailout_on_uninitialized);
PropertyAccessInfo ProcessMapForNamedPropertyAccess( PropertyAccessInfo ProcessMapForNamedPropertyAccess(
Hints* receiver, MapRef receiver_map, NameRef const& name, Hints* receiver, base::Optional<MapRef> receiver_map,
MapRef lookup_start_object_map, NameRef const& name,
AccessMode access_mode, base::Optional<JSObjectRef> concrete_receiver, AccessMode access_mode, base::Optional<JSObjectRef> concrete_receiver,
Hints* result_hints); Hints* result_hints);
...@@ -2155,7 +2162,7 @@ void SerializerForBackgroundCompilation::ProcessCallOrConstruct( ...@@ -2155,7 +2162,7 @@ void SerializerForBackgroundCompilation::ProcessCallOrConstruct(
arguments->insert(arguments->begin(), result_hints_from_new_target); arguments->insert(arguments->begin(), result_hints_from_new_target);
} }
// For JSNativeContextSpecialization::InferReceiverRootMap // For JSNativeContextSpecialization::InferRootMap
Hints new_accumulator_hints = result_hints_from_new_target.Copy(zone()); Hints new_accumulator_hints = result_hints_from_new_target.Copy(zone());
ProcessCallOrConstructRecursive(callee, new_target, *arguments, ProcessCallOrConstructRecursive(callee, new_target, *arguments,
...@@ -2963,18 +2970,20 @@ void SerializerForBackgroundCompilation::ProcessUnaryOrBinaryOperation( ...@@ -2963,18 +2970,20 @@ void SerializerForBackgroundCompilation::ProcessUnaryOrBinaryOperation(
PropertyAccessInfo PropertyAccessInfo
SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess( SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess(
Hints* receiver, MapRef receiver_map, NameRef const& name, Hints* receiver, base::Optional<MapRef> receiver_map,
AccessMode access_mode, base::Optional<JSObjectRef> concrete_receiver, MapRef lookup_start_object_map, NameRef const& name, AccessMode access_mode,
Hints* result_hints) { base::Optional<JSObjectRef> concrete_receiver, Hints* result_hints) {
// For JSNativeContextSpecialization::InferReceiverRootMap DCHECK_IMPLIES(concrete_receiver.has_value(), receiver_map.has_value());
receiver_map.SerializeRootMap();
// For JSNativeContextSpecialization::InferRootMap
lookup_start_object_map.SerializeRootMap();
// For JSNativeContextSpecialization::ReduceNamedAccess. // For JSNativeContextSpecialization::ReduceNamedAccess.
JSGlobalProxyRef global_proxy = JSGlobalProxyRef global_proxy =
broker()->target_native_context().global_proxy_object(); broker()->target_native_context().global_proxy_object();
JSGlobalObjectRef global_object = JSGlobalObjectRef global_object =
broker()->target_native_context().global_object(); broker()->target_native_context().global_object();
if (receiver_map.equals(global_proxy.map())) { if (lookup_start_object_map.equals(global_proxy.map())) {
base::Optional<PropertyCellRef> cell = global_object.GetPropertyCell( base::Optional<PropertyCellRef> cell = global_object.GetPropertyCell(
name, SerializationPolicy::kSerializeIfNeeded); name, SerializationPolicy::kSerializeIfNeeded);
if (access_mode == AccessMode::kLoad && cell.has_value()) { if (access_mode == AccessMode::kLoad && cell.has_value()) {
...@@ -2983,7 +2992,7 @@ SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess( ...@@ -2983,7 +2992,7 @@ SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess(
} }
PropertyAccessInfo access_info = broker()->GetPropertyAccessInfo( PropertyAccessInfo access_info = broker()->GetPropertyAccessInfo(
receiver_map, name, access_mode, dependencies(), lookup_start_object_map, name, access_mode, dependencies(),
SerializationPolicy::kSerializeIfNeeded); SerializationPolicy::kSerializeIfNeeded);
// For JSNativeContextSpecialization::InlinePropertySetterCall // For JSNativeContextSpecialization::InlinePropertySetterCall
...@@ -2992,25 +3001,27 @@ SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess( ...@@ -2992,25 +3001,27 @@ SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess(
if (access_info.constant()->IsJSFunction()) { if (access_info.constant()->IsJSFunction()) {
JSFunctionRef function(broker(), access_info.constant()); JSFunctionRef function(broker(), access_info.constant());
// For JSCallReducer and JSInlining(Heuristic). if (receiver_map.has_value()) {
HintsVector arguments({Hints::SingleMap(receiver_map.object(), zone())}, // For JSCallReducer and JSInlining(Heuristic).
zone()); HintsVector arguments(
// In the case of a setter any added result hints won't make sense, but {Hints::SingleMap(receiver_map->object(), zone())}, zone());
// they will be ignored anyways by Process*PropertyAccess due to the // In the case of a setter any added result hints won't make sense, but
// access mode not being kLoad. // they will be ignored anyways by Process*PropertyAccess due to the
ProcessCalleeForCallOrConstruct( // access mode not being kLoad.
function.object(), base::nullopt, arguments, ProcessCalleeForCallOrConstruct(
SpeculationMode::kDisallowSpeculation, kMissingArgumentsAreUndefined, function.object(), base::nullopt, arguments,
result_hints); SpeculationMode::kDisallowSpeculation,
kMissingArgumentsAreUndefined, result_hints);
// For JSCallReducer::ReduceCallApiFunction.
Handle<SharedFunctionInfo> sfi = function.shared().object(); // For JSCallReducer::ReduceCallApiFunction.
if (sfi->IsApiFunction()) { Handle<SharedFunctionInfo> sfi = function.shared().object();
FunctionTemplateInfoRef fti_ref( if (sfi->IsApiFunction()) {
broker(), handle(sfi->get_api_func_data(), broker()->isolate())); FunctionTemplateInfoRef fti_ref(
if (fti_ref.has_call_code()) { broker(), handle(sfi->get_api_func_data(), broker()->isolate()));
fti_ref.SerializeCallCode(); if (fti_ref.has_call_code()) {
ProcessReceiverMapForApiCall(fti_ref, receiver_map.object()); fti_ref.SerializeCallCode();
ProcessReceiverMapForApiCall(fti_ref, receiver_map->object());
}
} }
} }
} else if (access_info.constant()->IsJSBoundFunction()) { } else if (access_info.constant()->IsJSBoundFunction()) {
...@@ -3038,7 +3049,7 @@ SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess( ...@@ -3038,7 +3049,7 @@ SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess(
holder = JSObjectRef(broker(), prototype); holder = JSObjectRef(broker(), prototype);
} else { } else {
CHECK_IMPLIES(concrete_receiver.has_value(), CHECK_IMPLIES(concrete_receiver.has_value(),
concrete_receiver->map().equals(receiver_map)); concrete_receiver->map().equals(*receiver_map));
holder = concrete_receiver; holder = concrete_receiver;
} }
...@@ -3152,6 +3163,38 @@ void SerializerForBackgroundCompilation::ProcessNamedPropertyAccess( ...@@ -3152,6 +3163,38 @@ void SerializerForBackgroundCompilation::ProcessNamedPropertyAccess(
} }
} }
void SerializerForBackgroundCompilation::ProcessNamedSuperPropertyAccess(
Hints* receiver, NameRef const& name, FeedbackSlot slot,
AccessMode access_mode) {
if (slot.IsInvalid() || feedback_vector().is_null()) return;
FeedbackSource source(feedback_vector(), slot);
ProcessedFeedback const& feedback =
broker()->ProcessFeedbackForPropertyAccess(source, access_mode, name);
if (BailoutOnUninitialized(feedback)) return;
Hints new_accumulator_hints;
switch (feedback.kind()) {
case ProcessedFeedback::kNamedAccess:
DCHECK(name.equals(feedback.AsNamedAccess().name()));
ProcessNamedSuperAccess(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:
UNREACHABLE();
}
if (access_mode == AccessMode::kLoad) {
environment()->accumulator_hints() = new_accumulator_hints;
}
}
void SerializerForBackgroundCompilation::ProcessNamedAccess( void SerializerForBackgroundCompilation::ProcessNamedAccess(
Hints* receiver, NamedAccessFeedback const& feedback, Hints* receiver, NamedAccessFeedback const& feedback,
AccessMode access_mode, Hints* result_hints) { AccessMode access_mode, Hints* result_hints) {
...@@ -3165,17 +3208,18 @@ void SerializerForBackgroundCompilation::ProcessNamedAccess( ...@@ -3165,17 +3208,18 @@ void SerializerForBackgroundCompilation::ProcessNamedAccess(
for (Handle<Map> map : for (Handle<Map> map :
GetRelevantReceiverMaps(broker()->isolate(), receiver->maps())) { GetRelevantReceiverMaps(broker()->isolate(), receiver->maps())) {
MapRef map_ref(broker(), map); MapRef map_ref(broker(), map);
ProcessMapForNamedPropertyAccess(receiver, map_ref, feedback.name(), ProcessMapForNamedPropertyAccess(receiver, map_ref, map_ref,
access_mode, base::nullopt, result_hints); feedback.name(), access_mode,
base::nullopt, result_hints);
} }
for (Handle<Object> hint : receiver->constants()) { for (Handle<Object> hint : receiver->constants()) {
ObjectRef object(broker(), hint); ObjectRef object(broker(), hint);
if (access_mode == AccessMode::kLoad && object.IsJSObject()) { if (access_mode == AccessMode::kLoad && object.IsJSObject()) {
MapRef map_ref = object.AsJSObject().map(); MapRef map_ref = object.AsJSObject().map();
ProcessMapForNamedPropertyAccess(receiver, map_ref, feedback.name(), ProcessMapForNamedPropertyAccess(receiver, map_ref, map_ref,
access_mode, object.AsJSObject(), feedback.name(), access_mode,
result_hints); object.AsJSObject(), result_hints);
} }
// For JSNativeContextSpecialization::ReduceJSLoadNamed. // For JSNativeContextSpecialization::ReduceJSLoadNamed.
if (access_mode == AccessMode::kLoad && object.IsJSFunction() && if (access_mode == AccessMode::kLoad && object.IsJSFunction() &&
...@@ -3193,6 +3237,30 @@ void SerializerForBackgroundCompilation::ProcessNamedAccess( ...@@ -3193,6 +3237,30 @@ void SerializerForBackgroundCompilation::ProcessNamedAccess(
} }
} }
void SerializerForBackgroundCompilation::ProcessNamedSuperAccess(
Hints* receiver, NamedAccessFeedback const& feedback,
AccessMode access_mode, Hints* result_hints) {
MapHandles receiver_maps =
GetRelevantReceiverMaps(broker()->isolate(), receiver->maps());
for (Handle<Map> receiver_map : receiver_maps) {
MapRef receiver_map_ref(broker(), receiver_map);
for (Handle<Map> feedback_map : feedback.maps()) {
MapRef feedback_map_ref(broker(), feedback_map);
ProcessMapForNamedPropertyAccess(
receiver, receiver_map_ref, feedback_map_ref, feedback.name(),
access_mode, base::nullopt, result_hints);
}
}
if (receiver_maps.empty()) {
for (Handle<Map> feedback_map : feedback.maps()) {
MapRef feedback_map_ref(broker(), feedback_map);
ProcessMapForNamedPropertyAccess(
receiver, base::nullopt, feedback_map_ref, feedback.name(),
access_mode, base::nullopt, result_hints);
}
}
}
void SerializerForBackgroundCompilation::ProcessElementAccess( void SerializerForBackgroundCompilation::ProcessElementAccess(
Hints const& receiver, Hints const& key, Hints const& receiver, Hints const& key,
ElementAccessFeedback const& feedback, AccessMode access_mode) { ElementAccessFeedback const& feedback, AccessMode access_mode) {
...@@ -3217,7 +3285,7 @@ void SerializerForBackgroundCompilation::ProcessElementAccess( ...@@ -3217,7 +3285,7 @@ void SerializerForBackgroundCompilation::ProcessElementAccess(
for (Handle<Object> hint : receiver.constants()) { for (Handle<Object> hint : receiver.constants()) {
ObjectRef receiver_ref(broker(), hint); ObjectRef receiver_ref(broker(), hint);
// For JSNativeContextSpecialization::InferReceiverRootMap // For JSNativeContextSpecialization::InferRootMap
if (receiver_ref.IsHeapObject()) { if (receiver_ref.IsHeapObject()) {
receiver_ref.AsHeapObject().map().SerializeRootMap(); receiver_ref.AsHeapObject().map().SerializeRootMap();
} }
...@@ -3248,7 +3316,7 @@ void SerializerForBackgroundCompilation::ProcessElementAccess( ...@@ -3248,7 +3316,7 @@ void SerializerForBackgroundCompilation::ProcessElementAccess(
} }
} }
// For JSNativeContextSpecialization::InferReceiverRootMap // For JSNativeContextSpecialization::InferRootMap
for (Handle<Map> map : receiver.maps()) { for (Handle<Map> map : receiver.maps()) {
MapRef map_ref(broker(), map); MapRef map_ref(broker(), map);
map_ref.SerializeRootMap(); map_ref.SerializeRootMap();
...@@ -3266,9 +3334,11 @@ void SerializerForBackgroundCompilation::VisitLdaNamedProperty( ...@@ -3266,9 +3334,11 @@ void SerializerForBackgroundCompilation::VisitLdaNamedProperty(
void SerializerForBackgroundCompilation::VisitLdaNamedPropertyFromSuper( void SerializerForBackgroundCompilation::VisitLdaNamedPropertyFromSuper(
BytecodeArrayIterator* iterator) { BytecodeArrayIterator* iterator) {
NameRef(broker(), Hints* receiver = &register_hints(iterator->GetRegisterOperand(0));
iterator->GetConstantForIndexOperand(1, broker()->isolate())); NameRef name(broker(),
// TODO(marja, v8:9237): Process feedback once it's added to the byte code. iterator->GetConstantForIndexOperand(1, broker()->isolate()));
FeedbackSlot slot = iterator->GetSlotOperand(2);
ProcessNamedSuperPropertyAccess(receiver, name, slot, AccessMode::kLoad);
} }
// TODO(neis): Do feedback-independent serialization also for *NoFeedback // TODO(neis): Do feedback-independent serialization also for *NoFeedback
......
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --super-ic --opt
// Flags: --no-always-opt --no-stress-opt --turboprop
// Flags: --turboprop-dynamic-map-checks
// This file contains tests which require --dynamic-map-chekcs.
(function TestMinimorphicPropertyAccess() {
class A {}
A.prototype.bar = "wrong value: A.prototype.bar";
class B extends A {};
B.prototype.bar = "correct value";
class C extends B {
foo(should_bailout) {
const r = super.bar;
const did_bailout = (
%GetOptimizationStatus(C.prototype.foo) &
V8OptimizationStatus.kTopmostFrameIsTurboFanned) == 0;
assertEquals(should_bailout, did_bailout);
return r;
}
}
C.prototype.bar = "wrong value: C.prototype.bar";
%PrepareFunctionForOptimization(C.prototype.foo);
let o = new C();
o.bar = "wrong value: o.bar";
// Fill in the feedback.
let r = o.foo(true);
assertEquals("correct value", r);
%OptimizeFunctionOnNextCall(C.prototype.foo);
// Test the optimized function.
r = o.foo(false);
assertEquals("correct value", r);
})();
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --super-ic --opt
// Flags: --no-always-opt --no-stress-opt
// This file contains tests which are disabled for TurboProp. TurboProp deopts
// differently than TurboFan, so the assertions about when a function is
// deoptimized won't hold.
(function TestPropertyIsConstant() {
// Test for a case where the property is a constant found in the lookup start
// object.
class A {}
A.prototype.bar = "wrong value: A.prototype.bar";
class B extends A {};
B.prototype.bar = "correct value";
class C extends B {
foo() { return super.bar; }
}
C.prototype.bar = "wrong value: C.prototype.bar";
%PrepareFunctionForOptimization(C.prototype.foo);
let o = new C();
o.bar = "wrong value: o.bar";
// Fill in the feedback.
r = o.foo();
assertEquals("correct value", r);
%OptimizeFunctionOnNextCall(C.prototype.foo);
// Test the optimized function.
r = o.foo();
assertEquals("correct value", r);
// Assert that the function was not deoptimized.
assertOptimized(C.prototype.foo);
// Change the property value.
B.prototype.bar = "new value";
r = o.foo();
assertEquals("new value", r);
// Assert that the function was deoptimized (dependency to the constant
// value).
assertFalse(isOptimized(C.prototype.foo));
})();
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --super-ic --opt
// Flags: --no-always-opt --no-stress-opt
(function TestPropertyIsInTheHomeObjectsProto() {
// Test where the property is a constant found on home object's proto. This
// will generate a minimorphic property load.
class A {}
A.prototype.bar = "wrong value: A.prototype.bar";
class B extends A {};
B.prototype.bar = "correct value";
class C extends B {
foo() { return super.bar; }
}
C.prototype.bar = "wrong value: D.prototype.bar";
%PrepareFunctionForOptimization(C.prototype.foo);
let o = new C();
o.bar = "wrong value: o.bar";
// Fill in the feedback.
let r = o.foo();
assertEquals("correct value", r);
%OptimizeFunctionOnNextCall(C.prototype.foo);
// Test the optimized function.
r = o.foo();
assertEquals("correct value", r);
// Assert that the function was not deoptimized.
assertOptimized(C.prototype.foo);
// Change the property value.
B.prototype.bar = "new value";
r = o.foo();
assertEquals("new value", r);
})();
(function TestPropertyIsGetterInTheHomeObjectsProto() {
// Test where the property is a constant found on home object's proto. This
// will generate a minimorphic property load.
class A {}
A.prototype.bar = "wrong value: A.prototype.bar";
class B extends A {
get bar() { return this.this_value; }
}
class C extends B {
foo() { return super.bar; }
}
C.prototype.bar = "wrong value: D.prototype.bar";
%PrepareFunctionForOptimization(C.prototype.foo);
let o = new C();
o.bar = "wrong value: o.bar";
o.this_value = "correct value";
// Fill in the feedback.
let r = o.foo();
assertEquals("correct value", r);
%OptimizeFunctionOnNextCall(C.prototype.foo);
// Test the optimized function.
r = o.foo();
assertEquals("correct value", r);
// Assert that the function was not deoptimized.
assertOptimized(C.prototype.foo);
// Change the property value.
o.this_value = "new value";
r = o.foo();
assertEquals("new value", r);
})();
(function TestPropertyIsConstantInThePrototypeChain() {
// Test where the property is a constant found on the prototype chain of the
// lookup start object.
class A {}
A.prototype.bar = "wrong value: A.prototype.bar";
class B extends A {};
B.prototype.bar = "correct value";
class C extends B {};
class D extends C {
foo() { return super.bar; }
}
D.prototype.bar = "wrong value: D.prototype.bar";
%PrepareFunctionForOptimization(D.prototype.foo);
let o = new D();
o.bar = "wrong value: o.bar";
// Fill in the feedback.
let r = o.foo();
assertEquals("correct value", r);
%OptimizeFunctionOnNextCall(D.prototype.foo);
// Test the optimized function.
r = o.foo();
assertEquals("correct value", r);
// Assert that the function was not deoptimized.
assertOptimized(D.prototype.foo);
// Change the property value.
B.prototype.bar = "new value";
r = o.foo();
assertEquals("new value", r);
// Assert that the function was deoptimized (dependency to the constant
// value).
assertFalse(isOptimized(D.prototype.foo));
})();
(function TestPropertyIsNonConstantData() {
// Test for a case where the property is a non-constant data property found
// in the lookup start object.
class A {}
A.prototype.bar = "wrong value: A.prototype.bar";
class B extends A {};
B.prototype.bar = "initial value";
class C extends B {
foo() { return super.bar; }
}
C.prototype.bar = "wrong value: C.prototype.bar";
%PrepareFunctionForOptimization(C.prototype.foo);
let o = new C();
o.bar = "wrong value: o.bar";
// Make the property look like a non-constant.
B.prototype.bar = "correct value";
// Fill in the feedback.
let r = o.foo();
assertEquals("correct value", r);
%OptimizeFunctionOnNextCall(C.prototype.foo);
// Test the optimized function.
r = o.foo();
assertEquals("correct value", r);
// Assert that the function was not deoptimized.
assertOptimized(C.prototype.foo);
// Change the property value.
B.prototype.bar = "new value";
r = o.foo();
assertEquals("new value", r);
// Assert that the function was still not deoptimized (the value was not a
// constant to begin with).
assertOptimized(C.prototype.foo);
})();
(function TestPropertyIsGetter() {
class A {}
A.prototype.bar = "wrong value: A.prototype.bar";
class B extends A {
get bar() {
return this.test_value;
}
};
class C extends B {}
class D extends C {
foo() {
const b = super.bar;
return b;
}
}
%PrepareFunctionForOptimization(D.prototype.foo);
D.prototype.bar = "wrong value: D.prototype.bar";
let o = new D();
o.bar = "wrong value: o.bar";
o.test_value = "correct value";
// Fill in the feedback.
let r = o.foo();
assertEquals("correct value", r);
%OptimizeFunctionOnNextCall(D.prototype.foo);
// Test the optimized function.
r = o.foo();
assertEquals("correct value", r);
// Assert that the function was not deoptimized.
assertOptimized(D.prototype.foo);
})();
(function TestPropertyInsertedInTheMiddle() {
// Test for a case where the property is a constant found in the lookup start
// object.
class A {}
A.prototype.bar = "correct value";
class B extends A {};
class C extends B {
foo() { return super.bar; }
}
C.prototype.bar = "wrong value: C.prototype.bar";
%PrepareFunctionForOptimization(C.prototype.foo);
let o = new C();
o.bar = "wrong value: o.bar";
// Fill in the feedback.
let r = o.foo();
assertEquals("correct value", r);
%OptimizeFunctionOnNextCall(C.prototype.foo);
// Test the optimized function.
r = o.foo();
assertEquals("correct value", r);
// Assert that the function was not deoptimized.
assertOptimized(C.prototype.foo);
// Insert the property into the prototype chain between the lookup start
// object and the old holder.
B.prototype.bar = "new value";
r = o.foo();
assertEquals("new value", r);
// Assert that the function was deoptimized (holder changed).
assertFalse(isOptimized(C.prototype.foo));
})();
(function TestUnexpectedHomeObjectPrototypeDeoptimizes() {
class A {}
A.prototype.bar = "wrong value: A.prototype.bar";
class B extends A {}
B.prototype.bar = "correct value";
class C extends B {}
class D extends C {
foo() { return super.bar; }
}
%PrepareFunctionForOptimization(D.prototype.foo);
D.prototype.bar = "wrong value: D.prototype.bar";
const o = new D();
// Fill in the feedback.
let r = o.foo();
assertEquals("correct value", r);
%OptimizeFunctionOnNextCall(D.prototype.foo);
// Test the optimized function.
r = o.foo();
assertEquals("correct value", r);
// Assert that the function was not deoptimized.
assertOptimized(D.prototype.foo);
// Change the home object's prototype.
D.prototype.__proto__ = {"bar": "new value"};
r = o.foo();
assertEquals("new value", r);
// Assert that the function was deoptimized.
assertEquals(false, isOptimized(D.prototype.foo));
})();
(function TestUnexpectedReceiverDoesNotDeoptimize() {
class A {}
A.prototype.bar = "wrong value: A.prototype.bar";
class B extends A {};
B.prototype.bar = "correct value";
class C extends B {
foo() { return super.bar; }
}
C.prototype.bar = "wrong value: C.prototype.bar";
%PrepareFunctionForOptimization(C.prototype.foo);
let o = new C();
o.bar = "wrong value: o.bar";
// Fill in the feedback.
let r = o.foo();
assertEquals("correct value", r);
%OptimizeFunctionOnNextCall(C.prototype.foo);
o.foo();
assertOptimized(C.prototype.foo);
// Test the optimized function with an unexpected receiver.
r = C.prototype.foo.call({'lol': 5});
assertEquals("correct value", r);
// Assert that the function was not deoptimized.
assertOptimized(C.prototype.foo);
})();
(function TestPolymorphic() {
class A {}
A.prototype.bar = "wrong value: A.prototype.bar";
class B extends A {}
B.prototype.bar = "correct value";
class C extends B {}
class D extends C {
foo() { return super.bar; }
}
%PrepareFunctionForOptimization(D.prototype.foo);
D.prototype.bar = "wrong value: D.prototype.bar";
const o = new D();
// Create objects which will act as the "home object's prototype" later.
const prototypes = [{"a": 0}, {"b": 0}];
for (p of prototypes) {
p.__proto__ = B.prototype;
}
// Fill in the feedback (polymorphic).
for (p of prototypes) {
D.prototype.__proto__ = p;
const r = o.foo();
assertEquals("correct value", r);
}
%OptimizeFunctionOnNextCall(D.prototype.foo);
// Test the optimized function - don't change the home object's proto any
// more.
let r = o.foo();
assertEquals("correct value", r);
// Assert that the function was not deoptimized.
assertOptimized(D.prototype.foo);
})();
(function TestPolymorphicWithGetter() {
class A {}
A.prototype.bar = "wrong value: A.prototype.bar";
class B extends A {
get bar() {
return this.test_value;
}
}
class C extends B {}
class D extends C {
foo() { return super.bar; }
}
%PrepareFunctionForOptimization(D.prototype.foo);
D.prototype.bar = "wrong value: D.prototype.bar";
const o = new D();
o.test_value = "correct value";
// Create objects which will act as the "home object's prototype" later.
const prototypes = [{"a": 0}, {"b": 0}];
for (p of prototypes) {
p.__proto__ = B.prototype;
}
// Fill in the feedback.
for (p of prototypes) {
D.prototype.__proto__ = p;
const r = o.foo();
assertEquals("correct value", r);
}
%OptimizeFunctionOnNextCall(D.prototype.foo);
// Test the optimized function - don't change the home object's proto any
// more.
const r = o.foo();
assertEquals("correct value", r);
// Assert that the function was not deoptimized.
assertOptimized(D.prototype.foo);
})();
(function TestPolymorphicMixinDoesNotDeopt() {
function createClasses() {
class A {}
A.prototype.bar = "correct value";
class B extends A {
foo() { return super.bar; }
}
return B;
}
const b1 = createClasses();
%PrepareFunctionForOptimization(b1.prototype.foo);
const b2 = createClasses();
%PrepareFunctionForOptimization(b2.prototype.foo);
class c1 extends b1 {};
class c2 extends b2 {};
const objects = [new c1(), new c2()];
// Fill in the feedback.
for (o of objects) {
const r = o.foo();
assertEquals("correct value", r);
}
%OptimizeFunctionOnNextCall(b1.prototype.foo);
%OptimizeFunctionOnNextCall(b2.prototype.foo);
// Test the optimized function.
for (o of objects) {
const r = o.foo();
assertEquals("correct value", r);
}
assertOptimized(b1.prototype.foo);
assertOptimized(b2.prototype.foo);
})();
(function TestHomeObjectProtoIsGlobalThis() {
class A {}
class B extends A {
foo() { return super.bar; }
}
B.prototype.__proto__ = globalThis;
globalThis.bar = "correct value";
%PrepareFunctionForOptimization(B.prototype.foo);
let o = new B();
// Fill in the feedback.
let r = o.foo();
assertEquals("correct value", r);
%OptimizeFunctionOnNextCall(B.prototype.foo);
// Test the optimized function.
r = o.foo();
assertEquals("correct value", r);
// Assert that the function was not deoptimized.
assertOptimized(B.prototype.foo);
globalThis.bar = "new value";
r = o.foo();
assertEquals("new value", r);
})();
(function TestMegamorphic() {
class A {}
A.prototype.bar = "wrong value: A.prototype.bar";
class B extends A {}
B.prototype.bar = "correct value";
class C extends B {}
class D extends C {
foo() { return super.bar; }
}
%PrepareFunctionForOptimization(D.prototype.foo);
D.prototype.bar = "wrong value: D.prototype.bar";
const o = new D();
// Create objects which will act as the "home object's prototype" later.
const prototypes = [{"a": 0}, {"b": 0}, {"c": 0}, {"d": 0}, {"e": 0},
{"f": 0}, {"g": 0}, {"e": 0}];
for (p of prototypes) {
p.__proto__ = B.prototype;
}
// Fill in the feedback (megamorphic).
for (p of prototypes) {
D.prototype.__proto__ = p;
const r = o.foo();
assertEquals("correct value", r);
}
%OptimizeFunctionOnNextCall(D.prototype.foo);
// Test the optimized function - don't change the home object's proto any
// more.
let r = o.foo();
assertEquals("correct value", r);
// Assert that the function was not deoptimized.
assertOptimized(D.prototype.foo);
})();
(function TestMegamorphicWithGetter() {
class A {}
A.prototype.bar = "wrong value: A.prototype.bar";
class B extends A {
get bar() {
return this.test_value;
}
};
class C extends B {}
class D extends C {
foo() { return super.bar;}
}
%PrepareFunctionForOptimization(D.prototype.foo);
D.prototype.bar = "wrong value: D.prototype.bar";
const o = new D();
o.test_value = "correct value";
// Create objects which will act as the "home object's prototype" later.
const prototypes = [{"a": 0}, {"b": 0}, {"c": 0}, {"d": 0}, {"e": 0},
{"f": 0}, {"g": 0}, {"e": 0}];
for (p of prototypes) {
p.__proto__ = B.prototype;
}
// Fill in the feedback (megamorphic).
for (p of prototypes) {
D.prototype.__proto__ = p;
const r = o.foo();
assertEquals("correct value", r);
}
%OptimizeFunctionOnNextCall(D.prototype.foo);
// Test the optimized function - don't change the home object's proto any
// more.
const r = o.foo();
assertEquals("correct value", r);
})();
(function TestHomeObjectProtoIsGlobalThisGetterProperty() {
class A {}
class B extends A {
foo() { return super.bar; }
}
B.prototype.__proto__ = globalThis;
Object.defineProperty(globalThis, "bar", {get: function() { return this.this_value; }});
%PrepareFunctionForOptimization(B.prototype.foo);
let o = new B();
o.this_value = "correct value";
// Fill in the feedback.
let r = o.foo();
assertEquals("correct value", r);
%OptimizeFunctionOnNextCall(B.prototype.foo);
// Test the optimized function.
r = o.foo();
assertEquals("correct value", r);
// Assert that the function was not deoptimized.
assertOptimized(B.prototype.foo);
})();
(function TestHomeObjectProtoIsFunctionAndPropertyIsPrototype() {
// There are special optimizations for accessing Function.prototype. Test
// that super property access which ends up accessing it works.
class A {}
class B extends A {
foo() { return super.prototype; }
}
function f() {}
B.prototype.__proto__ = f;
%PrepareFunctionForOptimization(B.prototype.foo);
let o = new B();
// Fill in the feedback.
let r = o.foo();
assertEquals(f.prototype, r);
%OptimizeFunctionOnNextCall(B.prototype.foo);
// Test the optimized function.
r = o.foo();
assertEquals(f.prototype, r);
// Assert that the function was not deoptimized.
assertOptimized(B.prototype.foo);
})();
...@@ -1151,6 +1151,7 @@ ...@@ -1151,6 +1151,7 @@
'compiler/number-comparison-truncations': [SKIP], 'compiler/number-comparison-truncations': [SKIP],
'compiler/redundancy-elimination': [SKIP], 'compiler/redundancy-elimination': [SKIP],
'compiler/regress-9945-*': [SKIP], 'compiler/regress-9945-*': [SKIP],
'es6/super-ic-opt-no-turboprop': [SKIP],
# Static asserts for optimizations don't hold due to removed optimization # Static asserts for optimizations don't hold due to removed optimization
# phases. # phases.
...@@ -1439,6 +1440,8 @@ ...@@ -1439,6 +1440,8 @@
'compiler/serializer-feedback-propagation-1': [SKIP], 'compiler/serializer-feedback-propagation-1': [SKIP],
'compiler/serializer-feedback-propagation-2': [SKIP], 'compiler/serializer-feedback-propagation-2': [SKIP],
'compiler/serializer-transition-propagation': [SKIP], 'compiler/serializer-transition-propagation': [SKIP],
# crbug.com/v8/11110
'es6/super-ic-opt*': [SKIP],
}], # variant == nci or variant == nci_as_midtier }], # variant == nci or variant == nci_as_midtier
['((arch == mipsel or arch == mips64el or arch == mips or arch == mips64) and not simd_mips) or (arch in [ppc64, s390x])', { ['((arch == mipsel or arch == mips64el or arch == mips or arch == mips64) and not simd_mips) or (arch in [ppc64, s390x])', {
......
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --concurrent-inlining --no-use-ic --super-ic
class A {
bar() { }
}
class B extends A {
foo() {
return super.bar();
}
}
%PrepareFunctionForOptimization(B.prototype.foo);
new B().foo();
%OptimizeFunctionOnNextCall(B.prototype.foo);
new B().foo();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment