[super] Use a feedback-collecting builtin in JSGenericLowering
Bug: v8:9237 Change-Id: I1e308a5a325b01fcdc9236f13080653f3be6d328 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2527095 Commit-Queue: Marja Hölttä <marja@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Cr-Commit-Position: refs/heads/master@{#71155}
This commit is contained in:
parent
f823ac7117
commit
cdcf3c3134
@ -318,12 +318,30 @@ void JSGenericLowering::LowerJSLoadNamed(Node* node) {
|
||||
}
|
||||
|
||||
void JSGenericLowering::LowerJSLoadNamedFromSuper(Node* node) {
|
||||
// TODO(marja, v8:9237): Call a builtin which collects feedback.
|
||||
JSLoadNamedFromSuperNode n(node);
|
||||
NamedAccess const& p = n.Parameters();
|
||||
node->RemoveInput(2); // Feedback vector
|
||||
Node* effect = NodeProperties::GetEffectInput(node);
|
||||
Node* control = NodeProperties::GetControlInput(node);
|
||||
// Node inputs: receiver, home object, FeedbackVector.
|
||||
// LoadSuperIC expects: receiver, lookup start object, name, slot,
|
||||
// FeedbackVector.
|
||||
Node* home_object_map = effect = graph()->NewNode(
|
||||
jsgraph()->simplified()->LoadField(AccessBuilder::ForMap()),
|
||||
n.home_object(), effect, control);
|
||||
Node* home_object_proto = effect = graph()->NewNode(
|
||||
jsgraph()->simplified()->LoadField(AccessBuilder::ForMapPrototype()),
|
||||
home_object_map, effect, control);
|
||||
n->ReplaceInput(n.HomeObjectIndex(), home_object_proto);
|
||||
NodeProperties::ReplaceEffectInput(node, effect);
|
||||
STATIC_ASSERT(n.FeedbackVectorIndex() == 2);
|
||||
// If the code below will be used for the invalid feedback case, it needs to
|
||||
// be double-checked that the FeedbackVector parameter will be the
|
||||
// UndefinedConstant.
|
||||
DCHECK(p.feedback().IsValid());
|
||||
node->InsertInput(zone(), 2, jsgraph()->HeapConstant(p.name()));
|
||||
ReplaceWithRuntimeCall(node, Runtime::kLoadFromSuper);
|
||||
node->InsertInput(zone(), 3,
|
||||
jsgraph()->TaggedIndexConstant(p.feedback().index()));
|
||||
ReplaceWithBuiltinCall(node, Builtins::kLoadSuperIC);
|
||||
}
|
||||
|
||||
void JSGenericLowering::LowerJSLoadGlobal(Node* node) {
|
||||
|
@ -1414,9 +1414,6 @@ class JSLoadNamedFromSuperNode final : public JSNodeWrapperBase {
|
||||
|
||||
const NamedAccess& Parameters() const { return NamedAccessOf(node()->op()); }
|
||||
|
||||
// TODO(marja, v8:9237): A more intuitive order would be (home_object,
|
||||
// receiver, feedback_vector). The order can be changed once we no longer
|
||||
// delegate to Runtime_LoadFromSuper.
|
||||
#define INPUTS(V) \
|
||||
V(Receiver, receiver, 0, Object) \
|
||||
V(HomeObject, home_object, 1, Object) \
|
||||
|
@ -7,7 +7,8 @@
|
||||
|
||||
// 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.
|
||||
// deoptimized won't hold. In addition, TurboProp doesn't inline, so all
|
||||
// tests that rely on inlining need to be in this file.
|
||||
|
||||
(function TestPropertyIsConstant() {
|
||||
// Test for a case where the property is a constant found in the lookup start
|
||||
@ -49,3 +50,131 @@
|
||||
// value).
|
||||
assertFalse(isOptimized(C.prototype.foo));
|
||||
})();
|
||||
|
||||
(function TestSuperpropertyAccessInlined() {
|
||||
class A {}
|
||||
A.prototype.bar = "wrong value: A.prototype.bar";
|
||||
|
||||
class B extends A {}
|
||||
B.prototype.bar = "correct value";
|
||||
|
||||
class C extends B {}
|
||||
|
||||
let inline_this_is_being_interpreted;
|
||||
class D extends C {
|
||||
inline_this() {
|
||||
inline_this_is_being_interpreted = %IsBeingInterpreted();
|
||||
return super.bar;
|
||||
}
|
||||
foo() { return this.inline_this(); }
|
||||
}
|
||||
%PrepareFunctionForOptimization(D.prototype.inline_this);
|
||||
%PrepareFunctionForOptimization(D.prototype.foo);
|
||||
D.prototype.bar = "wrong value: D.prototype.bar";
|
||||
|
||||
const o = new D();
|
||||
|
||||
// Fill in the feedback.
|
||||
let r = o.foo();
|
||||
assertEquals("correct value", r);
|
||||
|
||||
%OptimizeFunctionOnNextCall(D.prototype.foo);
|
||||
|
||||
// Test the optimized function.
|
||||
r = o.foo();
|
||||
assertEquals("correct value", r);
|
||||
|
||||
// Assert that the function was not deoptimized.
|
||||
assertOptimized(D.prototype.foo);
|
||||
|
||||
// Assert that inline_this really got inlined.
|
||||
assertFalse(inline_this_is_being_interpreted);
|
||||
})();
|
||||
|
||||
(function TestMegamorphicInlined() {
|
||||
class A {}
|
||||
A.prototype.bar = "wrong value: A.prototype.bar";
|
||||
|
||||
class B extends A {}
|
||||
B.prototype.bar = "correct value";
|
||||
|
||||
class C extends B {}
|
||||
|
||||
let inline_this_is_being_interpreted;
|
||||
class D extends C {
|
||||
inline_this() {
|
||||
inline_this_is_being_interpreted = %IsBeingInterpreted();
|
||||
return super.bar;
|
||||
}
|
||||
foo() { return this.inline_this(); }
|
||||
}
|
||||
%PrepareFunctionForOptimization(D.prototype.inline_this);
|
||||
%PrepareFunctionForOptimization(D.prototype.foo);
|
||||
D.prototype.bar = "wrong value: D.prototype.bar";
|
||||
|
||||
const o = new D();
|
||||
|
||||
// Create objects which will act as the "home object's prototype" later.
|
||||
const prototypes = [{"a": 0}, {"b": 0}, {"c": 0}, {"d": 0}, {"e": 0},
|
||||
{"f": 0}, {"g": 0}, {"e": 0}];
|
||||
for (p of prototypes) {
|
||||
p.__proto__ = B.prototype;
|
||||
}
|
||||
|
||||
// Fill in the feedback (megamorphic).
|
||||
for (p of prototypes) {
|
||||
D.prototype.__proto__ = p;
|
||||
const r = o.foo();
|
||||
assertEquals("correct value", r);
|
||||
}
|
||||
|
||||
%OptimizeFunctionOnNextCall(D.prototype.foo);
|
||||
|
||||
// Test the optimized function - don't change the home object's proto any
|
||||
// more.
|
||||
let r = o.foo();
|
||||
assertEquals("correct value", r);
|
||||
|
||||
// Assert that the function was not deoptimized.
|
||||
assertOptimized(D.prototype.foo);
|
||||
|
||||
// Assert that inline_this really got inlined.
|
||||
assertFalse(inline_this_is_being_interpreted);
|
||||
})();
|
||||
|
||||
(function TestHomeObjectProtoIsGlobalThisGetterPropertyInlined() {
|
||||
class A {}
|
||||
|
||||
let inline_this_is_being_interpreted;
|
||||
class B extends A {
|
||||
inline_this() {
|
||||
inline_this_is_being_interpreted = %IsBeingInterpreted();
|
||||
return super.bar;
|
||||
}
|
||||
foo() { return this.inline_this(); }
|
||||
}
|
||||
B.prototype.__proto__ = globalThis;
|
||||
Object.defineProperty(globalThis, "bar",
|
||||
{get: function() { return this.this_value; }});
|
||||
%PrepareFunctionForOptimization(B.prototype.inline_this);
|
||||
%PrepareFunctionForOptimization(B.prototype.foo);
|
||||
|
||||
let o = new B();
|
||||
o.this_value = "correct value";
|
||||
|
||||
// Fill in the feedback.
|
||||
let r = o.foo();
|
||||
assertEquals("correct value", r);
|
||||
|
||||
%OptimizeFunctionOnNextCall(B.prototype.foo);
|
||||
|
||||
// Test the optimized function.
|
||||
r = o.foo();
|
||||
assertEquals("correct value", r);
|
||||
|
||||
// Assert that the function was not deoptimized.
|
||||
assertOptimized(B.prototype.foo);
|
||||
|
||||
// Assert that inline_this really got inlined.
|
||||
assertFalse(inline_this_is_being_interpreted);
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user