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: {
......
...@@ -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,
......
// 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));
})();
This diff is collapsed.
...@@ -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