diff --git a/src/compiler/js-generic-lowering.cc b/src/compiler/js-generic-lowering.cc index 0b38bd538d..35a4376979 100644 --- a/src/compiler/js-generic-lowering.cc +++ b/src/compiler/js-generic-lowering.cc @@ -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) { diff --git a/src/compiler/js-operator.h b/src/compiler/js-operator.h index 7e61bf3760..518eff7fc0 100644 --- a/src/compiler/js-operator.h +++ b/src/compiler/js-operator.h @@ -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) \ diff --git a/test/mjsunit/es6/super-ic-opt-no-turboprop.js b/test/mjsunit/es6/super-ic-opt-no-turboprop.js index 8653ffbe3d..365d01f32b 100644 --- a/test/mjsunit/es6/super-ic-opt-no-turboprop.js +++ b/test/mjsunit/es6/super-ic-opt-no-turboprop.js @@ -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); +})();