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 {
TFH(LoadIC_Noninlined, LoadWithVector) \
TFH(LoadICTrampoline, Load) \
TFH(LoadICTrampoline_Megamorphic, Load) \
TFH(LoadSuperIC, LoadWithReceiverAndVector) \
TFH(KeyedLoadIC, LoadWithVector) \
TFH(KeyedLoadIC_Megamorphic, LoadWithVector) \
TFH(KeyedLoadICTrampoline, Load) \
......
......@@ -26,6 +26,7 @@ IC_BUILTIN(LoadIC_Noninlined)
IC_BUILTIN(LoadIC_NoFeedback)
IC_BUILTIN(LoadICTrampoline)
IC_BUILTIN(LoadICTrampoline_Megamorphic)
IC_BUILTIN(LoadSuperIC)
IC_BUILTIN(KeyedLoadIC)
IC_BUILTIN(KeyedLoadIC_Megamorphic)
IC_BUILTIN(KeyedLoadIC_PolymorphicName)
......
......@@ -52,6 +52,11 @@ const Register LoadDescriptor::SlotRegister() { return r0; }
const Register LoadWithVectorDescriptor::VectorRegister() { return r3; }
const Register
LoadWithReceiverAndVectorDescriptor::LookupStartObjectRegister() {
return r4;
}
const Register StoreDescriptor::ReceiverRegister() { return r1; }
const Register StoreDescriptor::NameRegister() { return r2; }
const Register StoreDescriptor::ValueRegister() { return r0; }
......
......@@ -52,6 +52,11 @@ const Register LoadDescriptor::SlotRegister() { return x0; }
const Register LoadWithVectorDescriptor::VectorRegister() { return x3; }
const Register
LoadWithReceiverAndVectorDescriptor::LookupStartObjectRegister() {
return x4;
}
const Register StoreDescriptor::ReceiverRegister() { return x1; }
const Register StoreDescriptor::NameRegister() { return x2; }
const Register StoreDescriptor::ValueRegister() { return x0; }
......
......@@ -55,6 +55,11 @@ const Register LoadDescriptor::SlotRegister() { return eax; }
const Register LoadWithVectorDescriptor::VectorRegister() { return no_reg; }
const Register
LoadWithReceiverAndVectorDescriptor::LookupStartObjectRegister() {
return edi;
}
const Register StoreDescriptor::ReceiverRegister() { return edx; }
const Register StoreDescriptor::NameRegister() { return ecx; }
const Register StoreDescriptor::ValueRegister() { return no_reg; }
......
......@@ -221,6 +221,16 @@ void LoadGlobalWithVectorDescriptor::InitializePlatformSpecific(
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(
CallInterfaceDescriptorData* data) {
Register registers[] = {NameRegister(), ValueRegister(), SlotRegister()};
......
......@@ -78,6 +78,7 @@ namespace internal {
V(LoadGlobalWithVector) \
V(LoadNoFeedback) \
V(LoadWithVector) \
V(LoadWithReceiverAndVector) \
V(NoContext) \
V(RecordWrite) \
V(ResumeGenerator) \
......@@ -821,6 +822,34 @@ class LoadWithVectorDescriptor : public LoadDescriptor {
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 {
public:
DEFINE_PARAMETERS(kName, kSlot, kVector)
......
......@@ -54,6 +54,11 @@ const Register LoadDescriptor::SlotRegister() { return rax; }
const Register LoadWithVectorDescriptor::VectorRegister() { return rbx; }
const Register
LoadWithReceiverAndVectorDescriptor::LookupStartObjectRegister() {
return rdi;
}
const Register StoreDescriptor::ReceiverRegister() { return rdx; }
const Register StoreDescriptor::NameRegister() { return rcx; }
const Register StoreDescriptor::ValueRegister() { return rax; }
......
......@@ -2358,7 +2358,7 @@ void AccessorAssembler::GenericPropertyLoad(
TNode<HeapObject> lookup_start_object, TNode<Map> lookup_start_object_map,
TNode<Int32T> lookup_start_object_instance_type, const LoadICParameters* p,
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);
Comment("key is unique name");
......@@ -2400,6 +2400,7 @@ void AccessorAssembler::GenericPropertyLoad(
}
if (use_stub_cache == kUseStubCache) {
DCHECK_EQ(lookup_start_object, p->receiver_and_lookup_start_object());
Label stub_cache(this);
BIND(&try_stub_cache);
// When there is no feedback vector don't use stub cache.
......@@ -2780,6 +2781,59 @@ void AccessorAssembler::LoadIC(const LoadICParameters* p) {
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,
TNode<Map> lookup_start_object_map,
TNode<HeapObject> feedback,
......@@ -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,
const LazyNode<TaggedIndex>& lazy_slot,
const LazyNode<Context>& lazy_context,
......@@ -3760,6 +3835,22 @@ void AccessorAssembler::GenerateLoadICTrampoline_Megamorphic() {
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() {
using Descriptor = LoadGlobalNoFeedbackDescriptor;
......
......@@ -32,6 +32,7 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler {
void GenerateLoadGlobalIC_NoFeedback();
void GenerateLoadICTrampoline();
void GenerateLoadICTrampoline_Megamorphic();
void GenerateLoadSuperIC();
void GenerateKeyedLoadIC();
void GenerateKeyedLoadIC_Megamorphic();
void GenerateKeyedLoadIC_PolymorphicName();
......@@ -250,18 +251,23 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler {
// LoadIC contains the full LoadIC logic, while LoadIC_Noninlined contains
// logic not inlined into Ignition bytecode handlers.
void LoadIC(const LoadICParameters* p);
// Can be used in the receiver != lookup_start_object case.
void LoadIC_Noninlined(const LoadICParameters* p,
TNode<Map> lookup_start_object_map,
TNode<HeapObject> feedback,
TVariable<MaybeObject>* var_handler, Label* if_handler,
Label* miss, ExitPoint* exit_point);
void LoadSuperIC(const LoadICParameters* p);
TNode<Object> LoadDescriptorValue(TNode<Map> map,
TNode<IntPtrT> descriptor_entry);
TNode<MaybeObject> LoadDescriptorValueOrFieldType(
TNode<Map> map, TNode<IntPtrT> descriptor_entry);
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,
TNode<Smi> smi_typeof_mode);
......
......@@ -383,9 +383,14 @@ void IC::ConfigureVectorState(
}
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;
if (receiver.is_null()) {
receiver = object;
}
// If the object is undefined or null it's illegal to try to get any
// of its properties; throw a TypeError in that case.
if (IsAnyHas() ? !object->IsJSReceiver()
......@@ -418,7 +423,8 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name,
update_receiver_map(object);
LookupIterator::Key key(isolate(), name);
LookupIterator it(isolate(), object, key);
LookupIterator it =
LookupIterator::LookupWithReceiver(isolate(), receiver, key, object);
// Named lookup in the object.
LookupForRead(&it, IsAnyHas());
......@@ -2338,6 +2344,21 @@ RUNTIME_FUNCTION(Runtime_LoadNoFeedbackIC_Miss) {
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) {
HandleScope scope(isolate);
DCHECK_EQ(4, args.length());
......@@ -2383,6 +2404,23 @@ RUNTIME_FUNCTION(Runtime_LoadGlobalIC_Slow) {
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) {
HandleScope scope(isolate);
DCHECK_EQ(4, args.length());
......
......@@ -184,9 +184,10 @@ class LoadIC : public IC {
return ShouldThrowReferenceError(kind());
}
V8_WARN_UNUSED_RESULT MaybeHandle<Object> Load(Handle<Object> object,
Handle<Name> name,
bool update_feedback = true);
// If receiver is empty, use object as the receiver.
V8_WARN_UNUSED_RESULT MaybeHandle<Object> Load(
Handle<Object> object, Handle<Name> name, bool update_feedback = true,
Handle<Object> receiver = Handle<Object>());
protected:
// Update the inline cache and the global stub cache based on the
......
......@@ -831,9 +831,9 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty(
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedPropertyFromSuper(
Register object, const AstRawString* name) {
Register object, const AstRawString* name, int feedback_slot) {
size_t name_index = GetConstantPoolEntry(name);
OutputLdaNamedPropertyFromSuper(object, name_index);
OutputLdaNamedPropertyFromSuper(object, name_index, feedback_slot);
return *this;
}
......
......@@ -137,7 +137,8 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final {
const AstRawString* name);
BytecodeArrayBuilder& LoadNamedPropertyFromSuper(Register object,
const AstRawString* name);
const AstRawString* name,
int feedback_slot);
// Keyed load property. The key should be in the accumulator.
BytecodeArrayBuilder& LoadKeyedProperty(Register object, int feedback_slot);
......
......@@ -860,6 +860,7 @@ class BytecodeGenerator::FeedbackSlotCache : public ZoneObject {
kStoreNamedStrict,
kStoreNamedSloppy,
kLoadProperty,
kLoadSuperProperty,
kLoadGlobalNotInsideTypeof,
kLoadGlobalInsideTypeof,
kClosureFeedbackCell
......@@ -877,6 +878,9 @@ class BytecodeGenerator::FeedbackSlotCache : public ZoneObject {
int 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 {
return GetImpl(slot_kind, 0, variable);
......@@ -888,6 +892,9 @@ class BytecodeGenerator::FeedbackSlotCache : public ZoneObject {
const AstRawString* name) const {
return GetImpl(slot_kind, variable_index, name);
}
int Get(SlotKind slot_kind, const AstRawString* name) const {
return GetImpl(slot_kind, 0, name);
}
private:
using Key = std::tuple<SlotKind, int, const void*>;
......@@ -4726,8 +4733,9 @@ void BytecodeGenerator::VisitNamedSuperPropertyLoad(Property* property,
builder()->StoreAccumulatorInRegister(receiver);
VisitForAccumulatorValue(super_property->home_object());
builder()->SetExpressionPosition(property);
builder()->LoadNamedPropertyFromSuper(
receiver, property->key()->AsLiteral()->AsRawPropertyName());
auto name = property->key()->AsLiteral()->AsRawPropertyName();
FeedbackSlot slot = GetCachedLoadSuperICSlot(name);
builder()->LoadNamedPropertyFromSuper(receiver, name, feedback_index(slot));
if (opt_receiver_out.is_valid()) {
builder()->MoveRegister(receiver, opt_receiver_out);
}
......@@ -6568,6 +6576,7 @@ FeedbackSlot BytecodeGenerator::GetCachedStoreGlobalICSlot(
FeedbackSlot BytecodeGenerator::GetCachedLoadICSlot(const Expression* expr,
const AstRawString* name) {
DCHECK(!expr->IsSuperPropertyReference());
if (!FLAG_ignition_share_named_property_feedback) {
return feedback_spec()->AddLoadICSlot();
}
......@@ -6588,6 +6597,23 @@ FeedbackSlot BytecodeGenerator::GetCachedLoadICSlot(const Expression* expr,
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,
const AstRawString* name) {
if (!FLAG_ignition_share_named_property_feedback) {
......
......@@ -422,6 +422,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
Variable* variable);
FeedbackSlot GetCachedLoadICSlot(const Expression* expr,
const AstRawString* name);
FeedbackSlot GetCachedLoadSuperICSlot(const AstRawString* name);
FeedbackSlot GetCachedStoreICSlot(const Expression* expr,
const AstRawString* name);
FeedbackSlot GetDummyCompareICSlot();
......
......@@ -101,7 +101,7 @@ namespace interpreter {
V(LdaNamedPropertyNoFeedback, AccumulatorUse::kWrite, OperandType::kReg, \
OperandType::kIdx) \
V(LdaNamedPropertyFromSuper, AccumulatorUse::kReadWrite, OperandType::kReg, \
OperandType::kIdx) \
OperandType::kIdx, OperandType::kIdx) \
V(LdaKeyedProperty, AccumulatorUse::kReadWrite, OperandType::kReg, \
OperandType::kIdx) \
\
......
......@@ -553,18 +553,23 @@ IGNITION_HANDLER(LdaNamedPropertyNoFeedback, InterpreterAssembler) {
Dispatch();
}
// LdaNamedPropertyFromSuper <receiver> <name_index>
// LdaNamedPropertyFromSuper <receiver> <name_index> <slot>
//
// Calls the LoadFromSuper runtime function for <receiver>, home object (in the
// accumulator) and the name at constant pool entry <name_index>.
// Calls the LoadSuperIC at FeedBackVector slot <slot> for <receiver>, home
// object's prototype (home object in the accumulator) and the name at constant
// pool entry <name_index>.
IGNITION_HANDLER(LdaNamedPropertyFromSuper, InterpreterAssembler) {
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<TaggedIndex> slot = BytecodeOperandIdxTaggedIndex(2);
TNode<HeapObject> feedback_vector = LoadFeedbackVector();
TNode<Context> context = GetContext();
TNode<Object> result = CallRuntime(Runtime::kLoadFromSuper, context, receiver,
home_object, name);
TNode<Object> result =
CallBuiltin(Builtins::kLoadSuperIC, context, receiver,
home_object_prototype, name, slot, feedback_vector);
SetAccumulator(result);
Dispatch();
}
......
......@@ -605,6 +605,8 @@ namespace internal {
F(LoadGlobalIC_Slow, 3, 1) \
F(LoadIC_Miss, 4, 1) \
F(LoadNoFeedbackIC_Miss, 4, 1) \
F(LoadWithReceiverIC_Miss, 5, 1) \
F(LoadWithReceiverNoFeedbackIC_Miss, 3, 1) \
F(LoadPropertyWithInterceptor, 5, 1) \
F(StoreCallbackProperty, 5, 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) {
// Emit load / store property operations.
builder.LoadNamedProperty(reg, name, load_slot.ToInt())
.LoadNamedPropertyNoFeedback(reg, name)
.LoadNamedPropertyFromSuper(reg, name)
.LoadNamedPropertyFromSuper(reg, name, load_slot.ToInt())
.LoadKeyedProperty(reg, keyed_load_slot.ToInt())
.StoreNamedProperty(reg, name, sloppy_store_slot.ToInt(),
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