Commit 3d40ec8d authored by Marja Hölttä's avatar Marja Hölttä Committed by Commit Bot

[super property speed] Add an IC for super property loads

Bug: v8:9237
Change-Id: I06d7e74ba0360334e6fa65c19f24548e220e4c69
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2349297
Commit-Queue: Marja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69735}
parent 7b8cce77
...@@ -536,6 +536,7 @@ namespace internal { ...@@ -536,6 +536,7 @@ namespace internal {
TFH(LoadIC_Noninlined, LoadWithVector) \ TFH(LoadIC_Noninlined, LoadWithVector) \
TFH(LoadICTrampoline, Load) \ TFH(LoadICTrampoline, Load) \
TFH(LoadICTrampoline_Megamorphic, Load) \ TFH(LoadICTrampoline_Megamorphic, Load) \
TFH(LoadSuperIC, LoadWithReceiverAndVector) \
TFH(KeyedLoadIC, LoadWithVector) \ TFH(KeyedLoadIC, LoadWithVector) \
TFH(KeyedLoadIC_Megamorphic, LoadWithVector) \ TFH(KeyedLoadIC_Megamorphic, LoadWithVector) \
TFH(KeyedLoadICTrampoline, Load) \ TFH(KeyedLoadICTrampoline, Load) \
......
...@@ -26,6 +26,7 @@ IC_BUILTIN(LoadIC_Noninlined) ...@@ -26,6 +26,7 @@ IC_BUILTIN(LoadIC_Noninlined)
IC_BUILTIN(LoadIC_NoFeedback) IC_BUILTIN(LoadIC_NoFeedback)
IC_BUILTIN(LoadICTrampoline) IC_BUILTIN(LoadICTrampoline)
IC_BUILTIN(LoadICTrampoline_Megamorphic) IC_BUILTIN(LoadICTrampoline_Megamorphic)
IC_BUILTIN(LoadSuperIC)
IC_BUILTIN(KeyedLoadIC) IC_BUILTIN(KeyedLoadIC)
IC_BUILTIN(KeyedLoadIC_Megamorphic) IC_BUILTIN(KeyedLoadIC_Megamorphic)
IC_BUILTIN(KeyedLoadIC_PolymorphicName) IC_BUILTIN(KeyedLoadIC_PolymorphicName)
......
...@@ -52,6 +52,11 @@ const Register LoadDescriptor::SlotRegister() { return r0; } ...@@ -52,6 +52,11 @@ const Register LoadDescriptor::SlotRegister() { return r0; }
const Register LoadWithVectorDescriptor::VectorRegister() { return r3; } const Register LoadWithVectorDescriptor::VectorRegister() { return r3; }
const Register
LoadWithReceiverAndVectorDescriptor::LookupStartObjectRegister() {
return r4;
}
const Register StoreDescriptor::ReceiverRegister() { return r1; } const Register StoreDescriptor::ReceiverRegister() { return r1; }
const Register StoreDescriptor::NameRegister() { return r2; } const Register StoreDescriptor::NameRegister() { return r2; }
const Register StoreDescriptor::ValueRegister() { return r0; } const Register StoreDescriptor::ValueRegister() { return r0; }
......
...@@ -52,6 +52,11 @@ const Register LoadDescriptor::SlotRegister() { return x0; } ...@@ -52,6 +52,11 @@ const Register LoadDescriptor::SlotRegister() { return x0; }
const Register LoadWithVectorDescriptor::VectorRegister() { return x3; } const Register LoadWithVectorDescriptor::VectorRegister() { return x3; }
const Register
LoadWithReceiverAndVectorDescriptor::LookupStartObjectRegister() {
return x4;
}
const Register StoreDescriptor::ReceiverRegister() { return x1; } const Register StoreDescriptor::ReceiverRegister() { return x1; }
const Register StoreDescriptor::NameRegister() { return x2; } const Register StoreDescriptor::NameRegister() { return x2; }
const Register StoreDescriptor::ValueRegister() { return x0; } const Register StoreDescriptor::ValueRegister() { return x0; }
......
...@@ -55,6 +55,11 @@ const Register LoadDescriptor::SlotRegister() { return eax; } ...@@ -55,6 +55,11 @@ const Register LoadDescriptor::SlotRegister() { return eax; }
const Register LoadWithVectorDescriptor::VectorRegister() { return no_reg; } const Register LoadWithVectorDescriptor::VectorRegister() { return no_reg; }
const Register
LoadWithReceiverAndVectorDescriptor::LookupStartObjectRegister() {
return edi;
}
const Register StoreDescriptor::ReceiverRegister() { return edx; } const Register StoreDescriptor::ReceiverRegister() { return edx; }
const Register StoreDescriptor::NameRegister() { return ecx; } const Register StoreDescriptor::NameRegister() { return ecx; }
const Register StoreDescriptor::ValueRegister() { return no_reg; } const Register StoreDescriptor::ValueRegister() { return no_reg; }
......
...@@ -221,6 +221,16 @@ void LoadGlobalWithVectorDescriptor::InitializePlatformSpecific( ...@@ -221,6 +221,16 @@ void LoadGlobalWithVectorDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers); data->InitializePlatformSpecific(arraysize(registers), registers);
} }
void LoadWithReceiverAndVectorDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
DCHECK(!AreAliased(ReceiverRegister(), LookupStartObjectRegister(),
NameRegister(), SlotRegister(), VectorRegister()));
Register registers[] = {ReceiverRegister(), LookupStartObjectRegister(),
NameRegister(), SlotRegister(), VectorRegister()};
int len = arraysize(registers) - kStackArgumentsCount;
data->InitializePlatformSpecific(len, registers);
}
void StoreGlobalDescriptor::InitializePlatformSpecific( void StoreGlobalDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
Register registers[] = {NameRegister(), ValueRegister(), SlotRegister()}; Register registers[] = {NameRegister(), ValueRegister(), SlotRegister()};
......
...@@ -78,6 +78,7 @@ namespace internal { ...@@ -78,6 +78,7 @@ namespace internal {
V(LoadGlobalWithVector) \ V(LoadGlobalWithVector) \
V(LoadNoFeedback) \ V(LoadNoFeedback) \
V(LoadWithVector) \ V(LoadWithVector) \
V(LoadWithReceiverAndVector) \
V(NoContext) \ V(NoContext) \
V(RecordWrite) \ V(RecordWrite) \
V(ResumeGenerator) \ V(ResumeGenerator) \
...@@ -821,6 +822,34 @@ class LoadWithVectorDescriptor : public LoadDescriptor { ...@@ -821,6 +822,34 @@ class LoadWithVectorDescriptor : public LoadDescriptor {
static const int kStackArgumentsCount = kPassLastArgsOnStack ? 1 : 0; static const int kStackArgumentsCount = kPassLastArgsOnStack ? 1 : 0;
}; };
// Like LoadWithVectorDescriptor, except we pass the receiver (the object which
// should be used as the receiver for accessor function calls) and the lookup
// start object separately.
class LoadWithReceiverAndVectorDescriptor : public LoadWithVectorDescriptor {
public:
// TODO(v8:9497): Revert the Machine type for kSlot to the
// TaggedSigned once Torque can emit better call descriptors
DEFINE_PARAMETERS(kReceiver, kLookupStartObject, kName, kSlot, kVector)
DEFINE_PARAMETER_TYPES(MachineType::AnyTagged(), // kReceiver
MachineType::AnyTagged(), // kLookupStartObject
MachineType::AnyTagged(), // kName
MachineType::AnyTagged(), // kSlot
MachineType::AnyTagged()) // kVector
DECLARE_DESCRIPTOR(LoadWithReceiverAndVectorDescriptor,
LoadWithVectorDescriptor)
static const Register LookupStartObjectRegister();
#if V8_TARGET_ARCH_IA32
static const bool kPassLastArgsOnStack = true;
#else
static const bool kPassLastArgsOnStack = false;
#endif
// Pass vector through the stack.
static const int kStackArgumentsCount = kPassLastArgsOnStack ? 1 : 0;
};
class LoadGlobalWithVectorDescriptor : public LoadGlobalDescriptor { class LoadGlobalWithVectorDescriptor : public LoadGlobalDescriptor {
public: public:
DEFINE_PARAMETERS(kName, kSlot, kVector) DEFINE_PARAMETERS(kName, kSlot, kVector)
......
...@@ -54,6 +54,11 @@ const Register LoadDescriptor::SlotRegister() { return rax; } ...@@ -54,6 +54,11 @@ const Register LoadDescriptor::SlotRegister() { return rax; }
const Register LoadWithVectorDescriptor::VectorRegister() { return rbx; } const Register LoadWithVectorDescriptor::VectorRegister() { return rbx; }
const Register
LoadWithReceiverAndVectorDescriptor::LookupStartObjectRegister() {
return rdi;
}
const Register StoreDescriptor::ReceiverRegister() { return rdx; } const Register StoreDescriptor::ReceiverRegister() { return rdx; }
const Register StoreDescriptor::NameRegister() { return rcx; } const Register StoreDescriptor::NameRegister() { return rcx; }
const Register StoreDescriptor::ValueRegister() { return rax; } const Register StoreDescriptor::ValueRegister() { return rax; }
......
...@@ -2358,7 +2358,7 @@ void AccessorAssembler::GenericPropertyLoad( ...@@ -2358,7 +2358,7 @@ void AccessorAssembler::GenericPropertyLoad(
TNode<HeapObject> lookup_start_object, TNode<Map> lookup_start_object_map, TNode<HeapObject> lookup_start_object, TNode<Map> lookup_start_object_map,
TNode<Int32T> lookup_start_object_instance_type, const LoadICParameters* p, TNode<Int32T> lookup_start_object_instance_type, const LoadICParameters* p,
Label* slow, UseStubCache use_stub_cache) { Label* slow, UseStubCache use_stub_cache) {
DCHECK_EQ(lookup_start_object, p->receiver_and_lookup_start_object()); DCHECK_EQ(lookup_start_object, p->lookup_start_object());
ExitPoint direct_exit(this); ExitPoint direct_exit(this);
Comment("key is unique name"); Comment("key is unique name");
...@@ -2400,6 +2400,7 @@ void AccessorAssembler::GenericPropertyLoad( ...@@ -2400,6 +2400,7 @@ void AccessorAssembler::GenericPropertyLoad(
} }
if (use_stub_cache == kUseStubCache) { if (use_stub_cache == kUseStubCache) {
DCHECK_EQ(lookup_start_object, p->receiver_and_lookup_start_object());
Label stub_cache(this); Label stub_cache(this);
BIND(&try_stub_cache); BIND(&try_stub_cache);
// When there is no feedback vector don't use stub cache. // When there is no feedback vector don't use stub cache.
...@@ -2780,6 +2781,59 @@ void AccessorAssembler::LoadIC(const LoadICParameters* p) { ...@@ -2780,6 +2781,59 @@ void AccessorAssembler::LoadIC(const LoadICParameters* p) {
p->name(), p->slot(), p->vector()); p->name(), p->slot(), p->vector());
} }
void AccessorAssembler::LoadSuperIC(const LoadICParameters* p) {
ExitPoint direct_exit(this);
TVARIABLE(MaybeObject, var_handler);
Label if_handler(this, &var_handler), no_feedback(this),
non_inlined(this, Label::kDeferred), try_polymorphic(this),
miss(this, Label::kDeferred);
GotoIf(IsUndefined(p->vector()), &no_feedback);
// The lookup start object cannot be a SMI, since it's the home object's
// prototype, and it's not possible to set SMIs as prototypes.
TNode<Map> lookup_start_object_map =
LoadReceiverMap(p->lookup_start_object());
GotoIf(IsDeprecatedMap(lookup_start_object_map), &miss);
TNode<MaybeObject> feedback =
TryMonomorphicCase(p->slot(), CAST(p->vector()), lookup_start_object_map,
&if_handler, &var_handler, &try_polymorphic);
BIND(&if_handler);
{
LazyLoadICParameters lazy_p(p);
HandleLoadICHandlerCase(&lazy_p, CAST(var_handler.value()), &miss,
&direct_exit);
}
BIND(&no_feedback);
{ LoadSuperIC_NoFeedback(p); }
BIND(&try_polymorphic);
TNode<HeapObject> strong_feedback = GetHeapObjectIfStrong(feedback, &miss);
{
Comment("LoadSuperIC_try_polymorphic");
GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &non_inlined);
HandlePolymorphicCase(lookup_start_object_map, CAST(strong_feedback),
&if_handler, &var_handler, &miss);
}
BIND(&non_inlined);
{
// LoadIC_Noninlined can be used here, since it handles the
// lookup_start_object != receiver case gracefully.
LoadIC_Noninlined(p, lookup_start_object_map, strong_feedback, &var_handler,
&if_handler, &miss, &direct_exit);
}
BIND(&miss);
direct_exit.ReturnCallRuntime(Runtime::kLoadWithReceiverIC_Miss, p->context(),
p->receiver(), p->lookup_start_object(),
p->name(), p->slot(), p->vector());
}
void AccessorAssembler::LoadIC_Noninlined(const LoadICParameters* p, void AccessorAssembler::LoadIC_Noninlined(const LoadICParameters* p,
TNode<Map> lookup_start_object_map, TNode<Map> lookup_start_object_map,
TNode<HeapObject> feedback, TNode<HeapObject> feedback,
...@@ -2837,6 +2891,27 @@ void AccessorAssembler::LoadIC_NoFeedback(const LoadICParameters* p, ...@@ -2837,6 +2891,27 @@ void AccessorAssembler::LoadIC_NoFeedback(const LoadICParameters* p,
} }
} }
void AccessorAssembler::LoadSuperIC_NoFeedback(const LoadICParameters* p) {
Label miss(this, Label::kDeferred);
TNode<Object> lookup_start_object = p->lookup_start_object();
// The lookup start object cannot be a SMI, since it's the home object's
// prototype, and it's not possible to set SMIs as prototypes.
TNode<Map> lookup_start_object_map = LoadMap(CAST(lookup_start_object));
GotoIf(IsDeprecatedMap(lookup_start_object_map), &miss);
TNode<Uint16T> instance_type = LoadMapInstanceType(lookup_start_object_map);
GenericPropertyLoad(CAST(lookup_start_object), lookup_start_object_map,
instance_type, p, &miss, kDontUseStubCache);
BIND(&miss);
{
TailCallRuntime(Runtime::kLoadWithReceiverNoFeedbackIC_Miss, p->context(),
p->receiver(), p->lookup_start_object(), p->name());
}
}
void AccessorAssembler::LoadGlobalIC(TNode<HeapObject> maybe_feedback_vector, void AccessorAssembler::LoadGlobalIC(TNode<HeapObject> maybe_feedback_vector,
const LazyNode<TaggedIndex>& lazy_slot, const LazyNode<TaggedIndex>& lazy_slot,
const LazyNode<Context>& lazy_context, const LazyNode<Context>& lazy_context,
...@@ -3760,6 +3835,22 @@ void AccessorAssembler::GenerateLoadICTrampoline_Megamorphic() { ...@@ -3760,6 +3835,22 @@ void AccessorAssembler::GenerateLoadICTrampoline_Megamorphic() {
vector); vector);
} }
void AccessorAssembler::GenerateLoadSuperIC() {
using Descriptor = LoadWithReceiverAndVectorDescriptor;
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
TNode<Object> lookup_start_object =
CAST(Parameter(Descriptor::kLookupStartObject));
TNode<Object> name = CAST(Parameter(Descriptor::kName));
TNode<TaggedIndex> slot = CAST(Parameter(Descriptor::kSlot));
TNode<HeapObject> vector = CAST(Parameter(Descriptor::kVector));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
LoadICParameters p(context, receiver, name, slot, vector,
lookup_start_object);
LoadSuperIC(&p);
}
void AccessorAssembler::GenerateLoadGlobalIC_NoFeedback() { void AccessorAssembler::GenerateLoadGlobalIC_NoFeedback() {
using Descriptor = LoadGlobalNoFeedbackDescriptor; using Descriptor = LoadGlobalNoFeedbackDescriptor;
......
...@@ -32,6 +32,7 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler { ...@@ -32,6 +32,7 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler {
void GenerateLoadGlobalIC_NoFeedback(); void GenerateLoadGlobalIC_NoFeedback();
void GenerateLoadICTrampoline(); void GenerateLoadICTrampoline();
void GenerateLoadICTrampoline_Megamorphic(); void GenerateLoadICTrampoline_Megamorphic();
void GenerateLoadSuperIC();
void GenerateKeyedLoadIC(); void GenerateKeyedLoadIC();
void GenerateKeyedLoadIC_Megamorphic(); void GenerateKeyedLoadIC_Megamorphic();
void GenerateKeyedLoadIC_PolymorphicName(); void GenerateKeyedLoadIC_PolymorphicName();
...@@ -250,18 +251,23 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler { ...@@ -250,18 +251,23 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler {
// LoadIC contains the full LoadIC logic, while LoadIC_Noninlined contains // LoadIC contains the full LoadIC logic, while LoadIC_Noninlined contains
// logic not inlined into Ignition bytecode handlers. // logic not inlined into Ignition bytecode handlers.
void LoadIC(const LoadICParameters* p); void LoadIC(const LoadICParameters* p);
// Can be used in the receiver != lookup_start_object case.
void LoadIC_Noninlined(const LoadICParameters* p, void LoadIC_Noninlined(const LoadICParameters* p,
TNode<Map> lookup_start_object_map, TNode<Map> lookup_start_object_map,
TNode<HeapObject> feedback, TNode<HeapObject> feedback,
TVariable<MaybeObject>* var_handler, Label* if_handler, TVariable<MaybeObject>* var_handler, Label* if_handler,
Label* miss, ExitPoint* exit_point); Label* miss, ExitPoint* exit_point);
void LoadSuperIC(const LoadICParameters* p);
TNode<Object> LoadDescriptorValue(TNode<Map> map, TNode<Object> LoadDescriptorValue(TNode<Map> map,
TNode<IntPtrT> descriptor_entry); TNode<IntPtrT> descriptor_entry);
TNode<MaybeObject> LoadDescriptorValueOrFieldType( TNode<MaybeObject> LoadDescriptorValueOrFieldType(
TNode<Map> map, TNode<IntPtrT> descriptor_entry); TNode<Map> map, TNode<IntPtrT> descriptor_entry);
void LoadIC_NoFeedback(const LoadICParameters* p, TNode<Smi> smi_typeof_mode); void LoadIC_NoFeedback(const LoadICParameters* p, TNode<Smi> smi_typeof_mode);
void LoadSuperIC_NoFeedback(const LoadICParameters* p);
void LoadGlobalIC_NoFeedback(TNode<Context> context, TNode<Object> name, void LoadGlobalIC_NoFeedback(TNode<Context> context, TNode<Object> name,
TNode<Smi> smi_typeof_mode); TNode<Smi> smi_typeof_mode);
......
...@@ -383,9 +383,14 @@ void IC::ConfigureVectorState( ...@@ -383,9 +383,14 @@ void IC::ConfigureVectorState(
} }
MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name, MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name,
bool update_feedback) { bool update_feedback,
Handle<Object> receiver) {
bool use_ic = (state() != NO_FEEDBACK) && FLAG_use_ic && update_feedback; bool use_ic = (state() != NO_FEEDBACK) && FLAG_use_ic && update_feedback;
if (receiver.is_null()) {
receiver = object;
}
// If the object is undefined or null it's illegal to try to get any // If the object is undefined or null it's illegal to try to get any
// of its properties; throw a TypeError in that case. // of its properties; throw a TypeError in that case.
if (IsAnyHas() ? !object->IsJSReceiver() if (IsAnyHas() ? !object->IsJSReceiver()
...@@ -418,7 +423,8 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name, ...@@ -418,7 +423,8 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name,
update_receiver_map(object); update_receiver_map(object);
LookupIterator::Key key(isolate(), name); LookupIterator::Key key(isolate(), name);
LookupIterator it(isolate(), object, key); LookupIterator it =
LookupIterator::LookupWithReceiver(isolate(), receiver, key, object);
// Named lookup in the object. // Named lookup in the object.
LookupForRead(&it, IsAnyHas()); LookupForRead(&it, IsAnyHas());
...@@ -2338,6 +2344,21 @@ RUNTIME_FUNCTION(Runtime_LoadNoFeedbackIC_Miss) { ...@@ -2338,6 +2344,21 @@ RUNTIME_FUNCTION(Runtime_LoadNoFeedbackIC_Miss) {
RETURN_RESULT_OR_FAILURE(isolate, ic.Load(receiver, key)); RETURN_RESULT_OR_FAILURE(isolate, ic.Load(receiver, key));
} }
RUNTIME_FUNCTION(Runtime_LoadWithReceiverNoFeedbackIC_Miss) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
// Runtime functions don't follow the IC's calling convention.
Handle<Object> receiver = args.at(0);
Handle<Object> object = args.at(1);
Handle<Name> key = args.at<Name>(2);
Handle<FeedbackVector> vector = Handle<FeedbackVector>();
FeedbackSlot vector_slot = FeedbackSlot::Invalid();
LoadIC ic(isolate, vector, vector_slot, FeedbackSlotKind::kLoadProperty);
ic.UpdateState(object, key);
RETURN_RESULT_OR_FAILURE(isolate, ic.Load(object, key, true, receiver));
}
RUNTIME_FUNCTION(Runtime_LoadGlobalIC_Miss) { RUNTIME_FUNCTION(Runtime_LoadGlobalIC_Miss) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(4, args.length()); DCHECK_EQ(4, args.length());
...@@ -2383,6 +2404,23 @@ RUNTIME_FUNCTION(Runtime_LoadGlobalIC_Slow) { ...@@ -2383,6 +2404,23 @@ RUNTIME_FUNCTION(Runtime_LoadGlobalIC_Slow) {
return *result; return *result;
} }
RUNTIME_FUNCTION(Runtime_LoadWithReceiverIC_Miss) {
HandleScope scope(isolate);
DCHECK_EQ(5, args.length());
// Runtime functions don't follow the IC's calling convention.
Handle<Object> receiver = args.at(0);
Handle<Object> object = args.at(1);
Handle<Name> key = args.at<Name>(2);
Handle<TaggedIndex> slot = args.at<TaggedIndex>(3);
Handle<FeedbackVector> vector = args.at<FeedbackVector>(4);
FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value());
DCHECK(IsLoadICKind(vector->GetKind(vector_slot)));
LoadIC ic(isolate, vector, vector_slot, FeedbackSlotKind::kLoadProperty);
ic.UpdateState(object, key);
RETURN_RESULT_OR_FAILURE(isolate, ic.Load(object, key, true, receiver));
}
RUNTIME_FUNCTION(Runtime_KeyedLoadIC_Miss) { RUNTIME_FUNCTION(Runtime_KeyedLoadIC_Miss) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(4, args.length()); DCHECK_EQ(4, args.length());
......
...@@ -184,9 +184,10 @@ class LoadIC : public IC { ...@@ -184,9 +184,10 @@ class LoadIC : public IC {
return ShouldThrowReferenceError(kind()); return ShouldThrowReferenceError(kind());
} }
V8_WARN_UNUSED_RESULT MaybeHandle<Object> Load(Handle<Object> object, // If receiver is empty, use object as the receiver.
Handle<Name> name, V8_WARN_UNUSED_RESULT MaybeHandle<Object> Load(
bool update_feedback = true); Handle<Object> object, Handle<Name> name, bool update_feedback = true,
Handle<Object> receiver = Handle<Object>());
protected: protected:
// Update the inline cache and the global stub cache based on the // Update the inline cache and the global stub cache based on the
......
...@@ -831,9 +831,9 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty( ...@@ -831,9 +831,9 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty(
} }
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedPropertyFromSuper( BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedPropertyFromSuper(
Register object, const AstRawString* name) { Register object, const AstRawString* name, int feedback_slot) {
size_t name_index = GetConstantPoolEntry(name); size_t name_index = GetConstantPoolEntry(name);
OutputLdaNamedPropertyFromSuper(object, name_index); OutputLdaNamedPropertyFromSuper(object, name_index, feedback_slot);
return *this; return *this;
} }
......
...@@ -137,7 +137,8 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final { ...@@ -137,7 +137,8 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final {
const AstRawString* name); const AstRawString* name);
BytecodeArrayBuilder& LoadNamedPropertyFromSuper(Register object, BytecodeArrayBuilder& LoadNamedPropertyFromSuper(Register object,
const AstRawString* name); const AstRawString* name,
int feedback_slot);
// Keyed load property. The key should be in the accumulator. // Keyed load property. The key should be in the accumulator.
BytecodeArrayBuilder& LoadKeyedProperty(Register object, int feedback_slot); BytecodeArrayBuilder& LoadKeyedProperty(Register object, int feedback_slot);
......
...@@ -860,6 +860,7 @@ class BytecodeGenerator::FeedbackSlotCache : public ZoneObject { ...@@ -860,6 +860,7 @@ class BytecodeGenerator::FeedbackSlotCache : public ZoneObject {
kStoreNamedStrict, kStoreNamedStrict,
kStoreNamedSloppy, kStoreNamedSloppy,
kLoadProperty, kLoadProperty,
kLoadSuperProperty,
kLoadGlobalNotInsideTypeof, kLoadGlobalNotInsideTypeof,
kLoadGlobalInsideTypeof, kLoadGlobalInsideTypeof,
kClosureFeedbackCell kClosureFeedbackCell
...@@ -877,6 +878,9 @@ class BytecodeGenerator::FeedbackSlotCache : public ZoneObject { ...@@ -877,6 +878,9 @@ class BytecodeGenerator::FeedbackSlotCache : public ZoneObject {
int slot_index) { int slot_index) {
PutImpl(slot_kind, variable_index, name, slot_index); PutImpl(slot_kind, variable_index, name, slot_index);
} }
void Put(SlotKind slot_kind, const AstRawString* name, int slot_index) {
PutImpl(slot_kind, 0, name, slot_index);
}
int Get(SlotKind slot_kind, Variable* variable) const { int Get(SlotKind slot_kind, Variable* variable) const {
return GetImpl(slot_kind, 0, variable); return GetImpl(slot_kind, 0, variable);
...@@ -888,6 +892,9 @@ class BytecodeGenerator::FeedbackSlotCache : public ZoneObject { ...@@ -888,6 +892,9 @@ class BytecodeGenerator::FeedbackSlotCache : public ZoneObject {
const AstRawString* name) const { const AstRawString* name) const {
return GetImpl(slot_kind, variable_index, name); return GetImpl(slot_kind, variable_index, name);
} }
int Get(SlotKind slot_kind, const AstRawString* name) const {
return GetImpl(slot_kind, 0, name);
}
private: private:
using Key = std::tuple<SlotKind, int, const void*>; using Key = std::tuple<SlotKind, int, const void*>;
...@@ -4726,8 +4733,9 @@ void BytecodeGenerator::VisitNamedSuperPropertyLoad(Property* property, ...@@ -4726,8 +4733,9 @@ void BytecodeGenerator::VisitNamedSuperPropertyLoad(Property* property,
builder()->StoreAccumulatorInRegister(receiver); builder()->StoreAccumulatorInRegister(receiver);
VisitForAccumulatorValue(super_property->home_object()); VisitForAccumulatorValue(super_property->home_object());
builder()->SetExpressionPosition(property); builder()->SetExpressionPosition(property);
builder()->LoadNamedPropertyFromSuper( auto name = property->key()->AsLiteral()->AsRawPropertyName();
receiver, property->key()->AsLiteral()->AsRawPropertyName()); FeedbackSlot slot = GetCachedLoadSuperICSlot(name);
builder()->LoadNamedPropertyFromSuper(receiver, name, feedback_index(slot));
if (opt_receiver_out.is_valid()) { if (opt_receiver_out.is_valid()) {
builder()->MoveRegister(receiver, opt_receiver_out); builder()->MoveRegister(receiver, opt_receiver_out);
} }
...@@ -6568,6 +6576,7 @@ FeedbackSlot BytecodeGenerator::GetCachedStoreGlobalICSlot( ...@@ -6568,6 +6576,7 @@ FeedbackSlot BytecodeGenerator::GetCachedStoreGlobalICSlot(
FeedbackSlot BytecodeGenerator::GetCachedLoadICSlot(const Expression* expr, FeedbackSlot BytecodeGenerator::GetCachedLoadICSlot(const Expression* expr,
const AstRawString* name) { const AstRawString* name) {
DCHECK(!expr->IsSuperPropertyReference());
if (!FLAG_ignition_share_named_property_feedback) { if (!FLAG_ignition_share_named_property_feedback) {
return feedback_spec()->AddLoadICSlot(); return feedback_spec()->AddLoadICSlot();
} }
...@@ -6588,6 +6597,23 @@ FeedbackSlot BytecodeGenerator::GetCachedLoadICSlot(const Expression* expr, ...@@ -6588,6 +6597,23 @@ FeedbackSlot BytecodeGenerator::GetCachedLoadICSlot(const Expression* expr,
return slot; return slot;
} }
FeedbackSlot BytecodeGenerator::GetCachedLoadSuperICSlot(
const AstRawString* name) {
if (!FLAG_ignition_share_named_property_feedback) {
return feedback_spec()->AddLoadICSlot();
}
FeedbackSlotCache::SlotKind slot_kind =
FeedbackSlotCache::SlotKind::kLoadSuperProperty;
FeedbackSlot slot(feedback_slot_cache()->Get(slot_kind, name));
if (!slot.IsInvalid()) {
return slot;
}
slot = feedback_spec()->AddLoadICSlot();
feedback_slot_cache()->Put(slot_kind, name, feedback_index(slot));
return slot;
}
FeedbackSlot BytecodeGenerator::GetCachedStoreICSlot(const Expression* expr, FeedbackSlot BytecodeGenerator::GetCachedStoreICSlot(const Expression* expr,
const AstRawString* name) { const AstRawString* name) {
if (!FLAG_ignition_share_named_property_feedback) { if (!FLAG_ignition_share_named_property_feedback) {
......
...@@ -422,6 +422,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> { ...@@ -422,6 +422,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
Variable* variable); Variable* variable);
FeedbackSlot GetCachedLoadICSlot(const Expression* expr, FeedbackSlot GetCachedLoadICSlot(const Expression* expr,
const AstRawString* name); const AstRawString* name);
FeedbackSlot GetCachedLoadSuperICSlot(const AstRawString* name);
FeedbackSlot GetCachedStoreICSlot(const Expression* expr, FeedbackSlot GetCachedStoreICSlot(const Expression* expr,
const AstRawString* name); const AstRawString* name);
FeedbackSlot GetDummyCompareICSlot(); FeedbackSlot GetDummyCompareICSlot();
......
...@@ -101,7 +101,7 @@ namespace interpreter { ...@@ -101,7 +101,7 @@ namespace interpreter {
V(LdaNamedPropertyNoFeedback, AccumulatorUse::kWrite, OperandType::kReg, \ V(LdaNamedPropertyNoFeedback, AccumulatorUse::kWrite, OperandType::kReg, \
OperandType::kIdx) \ OperandType::kIdx) \
V(LdaNamedPropertyFromSuper, AccumulatorUse::kReadWrite, OperandType::kReg, \ V(LdaNamedPropertyFromSuper, AccumulatorUse::kReadWrite, OperandType::kReg, \
OperandType::kIdx) \ OperandType::kIdx, OperandType::kIdx) \
V(LdaKeyedProperty, AccumulatorUse::kReadWrite, OperandType::kReg, \ V(LdaKeyedProperty, AccumulatorUse::kReadWrite, OperandType::kReg, \
OperandType::kIdx) \ OperandType::kIdx) \
\ \
......
...@@ -553,18 +553,23 @@ IGNITION_HANDLER(LdaNamedPropertyNoFeedback, InterpreterAssembler) { ...@@ -553,18 +553,23 @@ IGNITION_HANDLER(LdaNamedPropertyNoFeedback, InterpreterAssembler) {
Dispatch(); Dispatch();
} }
// LdaNamedPropertyFromSuper <receiver> <name_index> // LdaNamedPropertyFromSuper <receiver> <name_index> <slot>
// //
// Calls the LoadFromSuper runtime function for <receiver>, home object (in the // Calls the LoadSuperIC at FeedBackVector slot <slot> for <receiver>, home
// accumulator) and the name at constant pool entry <name_index>. // object's prototype (home object in the accumulator) and the name at constant
// pool entry <name_index>.
IGNITION_HANDLER(LdaNamedPropertyFromSuper, InterpreterAssembler) { IGNITION_HANDLER(LdaNamedPropertyFromSuper, InterpreterAssembler) {
TNode<Object> receiver = LoadRegisterAtOperandIndex(0); TNode<Object> receiver = LoadRegisterAtOperandIndex(0);
TNode<Object> home_object = GetAccumulator(); TNode<HeapObject> home_object = CAST(GetAccumulator());
TNode<Object> home_object_prototype = LoadMapPrototype(LoadMap(home_object));
TNode<Object> name = LoadConstantPoolEntryAtOperandIndex(1); TNode<Object> name = LoadConstantPoolEntryAtOperandIndex(1);
TNode<TaggedIndex> slot = BytecodeOperandIdxTaggedIndex(2);
TNode<HeapObject> feedback_vector = LoadFeedbackVector();
TNode<Context> context = GetContext(); TNode<Context> context = GetContext();
TNode<Object> result = CallRuntime(Runtime::kLoadFromSuper, context, receiver, TNode<Object> result =
home_object, name); CallBuiltin(Builtins::kLoadSuperIC, context, receiver,
home_object_prototype, name, slot, feedback_vector);
SetAccumulator(result); SetAccumulator(result);
Dispatch(); Dispatch();
} }
......
...@@ -605,6 +605,8 @@ namespace internal { ...@@ -605,6 +605,8 @@ namespace internal {
F(LoadGlobalIC_Slow, 3, 1) \ F(LoadGlobalIC_Slow, 3, 1) \
F(LoadIC_Miss, 4, 1) \ F(LoadIC_Miss, 4, 1) \
F(LoadNoFeedbackIC_Miss, 4, 1) \ F(LoadNoFeedbackIC_Miss, 4, 1) \
F(LoadWithReceiverIC_Miss, 5, 1) \
F(LoadWithReceiverNoFeedbackIC_Miss, 3, 1) \
F(LoadPropertyWithInterceptor, 5, 1) \ F(LoadPropertyWithInterceptor, 5, 1) \
F(StoreCallbackProperty, 5, 1) \ F(StoreCallbackProperty, 5, 1) \
F(StoreGlobalIC_Miss, 4, 1) \ F(StoreGlobalIC_Miss, 4, 1) \
......
// 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
(function TestHomeObjectPrototypeNull() {
class A {}
class B extends A {
bar() {
return super.y;
}
};
let o = new B();
B.prototype.__proto__ = null;
assertThrows(() => { o.bar(); });
})();
(function TestMonomorphic() {
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";
let o = new D();
o.bar = "wrong value: o.bar";
for (let i = 0; i < 100; ++i) {
const r = o.foo();
assertEquals("correct value", r);
}
})();
(function TestMonomorphicWithGetter() {
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;
}
}
D.prototype.bar = "wrong value: D.prototype.bar";
let o = new D();
o.bar = "wrong value: o.bar";
o.test_value = "correct value";
for (let i = 0; i < 1000; ++i) {
const r = o.foo();
assertEquals("correct value", r);
}
})();
(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; }
}
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 (let i = 0; i < prototypes.length; ++i) {
prototypes[i].__proto__ = B.prototype;
}
for (let i = 0; i < 1000; ++i) {
D.prototype.__proto__ = prototypes[i % prototypes.length];
const r = o.foo();
assertEquals("correct value", r);
}
})();
(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; }
}
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 (let i = 0; i < prototypes.length; ++i) {
prototypes[i].__proto__ = B.prototype;
}
for (let i = 0; i < 1000; ++i) {
D.prototype.__proto__ = prototypes[i % prototypes.length];
const r = o.foo();
assertEquals("correct 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; }
}
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 (let i = 0; i < prototypes.length; ++i) {
prototypes[i].__proto__ = B.prototype;
}
for (let i = 0; i < 1000; ++i) {
D.prototype.__proto__ = prototypes[i % prototypes.length];
const r = o.foo();
assertEquals("correct value", r);
}
})();
(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;}
}
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 (let i = 0; i < prototypes.length; ++i) {
prototypes[i].__proto__ = B.prototype;
}
for (let i = 0; i < 1000; ++i) {
D.prototype.__proto__ = prototypes[i % prototypes.length];
const r = o.foo();
assertEquals("correct value", r);
}
})();
(function TestHolderHandledCorrectlyAfterOptimization() {
class A {
m() { return "m return value"; }
get boom() { return this.m; }
}
class B extends A { f() { return super.boom() } }
%PrepareFunctionForOptimization(B.prototype.f);
const r1 = new B().f();
assertEquals("m return value", r1);
const r2 = new B().f();
assertEquals("m return value", r2);
})();
(function TestHolderHandledCorrectlyAfterOptimization2() {
class A {
m() { return "m return value"; }
get boom() { return this.m; }
}
class Middle1 extends A {}
class Middle2 extends Middle1 {}
class B extends Middle2 { f() { return super.boom() } }
%PrepareFunctionForOptimization(B.prototype.f);
const r1 = new B().f();
assertEquals("m return value", r1);
const r2 = new B().f();
assertEquals("m return value", r2);
})();
(function TestStubCacheConfusion() {
// Regression test for using the wrong stub from the stub cache.
class A {};
A.prototype.foo = "foo from A.prototype";
class B extends A {
m() { return super.foo; }
}
// Create objects which will act as receivers for the method call. All of
// them will have a different map.
class C0 extends B { foo = "foo from C0"; };
class C1 extends B { foo = "foo from C1"; };
class C2 extends B { foo = "foo from C2"; };
class C3 extends B { foo = "foo from C3"; };
class C4 extends B { foo = "foo from C4"; };
class C5 extends B { foo = "foo from C5"; };
class C6 extends B { foo = "foo from C6"; };
class C7 extends B { foo = "foo from C7"; };
class C8 extends B { foo = "foo from C8"; };
class C9 extends B { foo = "foo from C9"; };
let receivers = [
new C0(), new C1(), new C2(), new C3(), new C4(), new C5(), new C6(), new C7(),
new C8(), new C9()
];
// Fill the stub cache with handlers which access "foo" from the receivers.
function getfoo(o) {
return o.foo;
}
for (let i = 0; i < 1000; ++i) {
getfoo(receivers[i % receivers.length]);
}
// Create objects which will act as the "home object's prototype" later.
const prototypes = [{"a": "prop in prototypes[0]"},
{"b": "prop in prototypes[1]"},
{"c": "prop in prototypes[2]"},
{"d": "prop in prototypes[3]"},
{"e": "prop in prototypes[4]"},
{"f": "prop in prototypes[5]"},
{"g": "prop in prototypes[6]"},
{"h": "prop in prototypes[7]"},
{"i": "prop in prototypes[8]"},
{"j": "prop in prototypes[9]"}];
for (let i = 0; i < prototypes.length; ++i) {
prototypes[i].__proto__ = A.prototype;
}
for (let i = 0; i < 1000; ++i) {
// Make the property load for "super.foo" megamorphic in terms of the home
// object's prototype.
B.prototype.__proto__ = prototypes[i % prototypes.length];
const r = receivers[i % receivers.length].m();
// The bug was that we used the same handlers which were accessing "foo"
// from the receivers, instead of accessing "foo" from the home object's
// prototype.
assertEquals("foo from A.prototype", r);
}
})();
(function TestLengthConfusion() {
// Regression test for confusion between bound function length and array
// length.
class A {};
class B extends A {
m() {
return super.length;
}
}
// Create a "home object proto" object which is a bound function.
let home_object_proto = (function() {}).bind({});
// Force home_object_proto to dictionary mode.
for (let i = 0; i < 2000; ++i) {
home_object_proto["prop" + i] = "prop_value";
}
B.prototype.__proto__ = home_object_proto;
assertEquals(0, home_object_proto.length);
for (let i = 0; i < 1000; ++i) {
const r = B.prototype.m.call([1, 2]);
// The bug was that here we retrieved the length of the receiver array
// instead of the home object's __proto__.
assertEquals(home_object_proto.length, r);
}
})();
(function TestSuperInsideArrowFunction() {
class A {};
A.prototype.foo = "correct value";
class B extends A {
m() {
const bar = () => {
return super.foo;
}
return bar();
}
n() {
const bar = () => {
return super.foo;
}
return bar;
}
};
assertEquals(A.prototype.foo, (new B).m());
assertEquals(A.prototype.foo, (new B).n()());
})();
...@@ -137,7 +137,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { ...@@ -137,7 +137,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
// Emit load / store property operations. // Emit load / store property operations.
builder.LoadNamedProperty(reg, name, load_slot.ToInt()) builder.LoadNamedProperty(reg, name, load_slot.ToInt())
.LoadNamedPropertyNoFeedback(reg, name) .LoadNamedPropertyNoFeedback(reg, name)
.LoadNamedPropertyFromSuper(reg, name) .LoadNamedPropertyFromSuper(reg, name, load_slot.ToInt())
.LoadKeyedProperty(reg, keyed_load_slot.ToInt()) .LoadKeyedProperty(reg, keyed_load_slot.ToInt())
.StoreNamedProperty(reg, name, sloppy_store_slot.ToInt(), .StoreNamedProperty(reg, name, sloppy_store_slot.ToInt(),
LanguageMode::kSloppy) LanguageMode::kSloppy)
......
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