Commit c13981cd authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[ignition] Collect JSBoundFunction feedback on Construct/ConstructWithSpread.

This addresses two TODOs in Ignition where the Construct and the
ConstructWithSpread bytecodes didn't collect JSBoundFunction
new.target feedback. This is fairly trivial to add now with the
existing machinery and the TurboFan side of this was already fixed
before, so we can leverage the new feedback.

Bug: v8:5267, v8:7109
Change-Id: Iae257836716c14f05f5d301326cbe8b2acaeb38b
Reviewed-on: https://chromium-review.googlesource.com/793048Reviewed-by: 's avatarMythri Alle <mythria@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49712}
parent 278981d7
......@@ -842,24 +842,50 @@ Node* InterpreterAssembler::Construct(Node* target, Node* context,
BIND(&initialize);
{
// Check if {new_target} is a JSFunction in the current native context.
Label create_allocation_site(this), create_weak_cell(this);
Comment("check if function in same native context");
GotoIf(TaggedIsSmi(new_target), &mark_megamorphic);
// TODO(bmeurer): Add support for arbitrary constructors here, and
// check via GetFunctionRealm (see src/objects.cc).
GotoIfNot(IsJSFunction(new_target), &mark_megamorphic);
Node* new_target_context =
LoadObjectField(new_target, JSFunction::kContextOffset);
Node* new_target_native_context = LoadNativeContext(new_target_context);
GotoIfNot(
WordEqual(LoadNativeContext(context), new_target_native_context),
&mark_megamorphic);
// Check if the {new_target} is a JSFunction or JSBoundFunction
// in the current native context.
VARIABLE(var_current, MachineRepresentation::kTagged, new_target);
Label loop(this, &var_current), done_loop(this);
Goto(&loop);
BIND(&loop);
{
Label if_boundfunction(this), if_function(this);
Node* current = var_current.value();
CSA_ASSERT(this, TaggedIsNotSmi(current));
Node* current_instance_type = LoadInstanceType(current);
GotoIf(InstanceTypeEqual(current_instance_type, JS_BOUND_FUNCTION_TYPE),
&if_boundfunction);
Branch(InstanceTypeEqual(current_instance_type, JS_FUNCTION_TYPE),
&if_function, &mark_megamorphic);
BIND(&if_function);
{
// Check that the JSFunction {current} is in the current native
// context.
Node* current_context =
LoadObjectField(current, JSFunction::kContextOffset);
Node* current_native_context = LoadNativeContext(current_context);
Branch(WordEqual(LoadNativeContext(context), current_native_context),
&done_loop, &mark_megamorphic);
}
BIND(&if_boundfunction);
{
// Continue with the [[BoundTargetFunction]] of {current}.
var_current.Bind(LoadObjectField(
current, JSBoundFunction::kBoundTargetFunctionOffset));
Goto(&loop);
}
}
BIND(&done_loop);
// Create an AllocationSite if {target} and {new_target} refer
// to the current native context's Array constructor.
Label create_allocation_site(this), create_weak_cell(this);
GotoIfNot(WordEqual(target, new_target), &create_weak_cell);
Node* array_function = LoadContextElement(new_target_native_context,
Node* array_function = LoadContextElement(LoadNativeContext(context),
Context::ARRAY_FUNCTION_INDEX);
Branch(WordEqual(target, array_function), &create_allocation_site,
&create_weak_cell);
......@@ -980,19 +1006,44 @@ Node* InterpreterAssembler::ConstructWithSpread(Node* target, Node* context,
BIND(&initialize);
{
// Check if {new_target} is a JSFunction in the current native
// context.
Comment("check if function in same native context");
GotoIf(TaggedIsSmi(new_target), &mark_megamorphic);
// TODO(bmeurer): Add support for arbitrary constructors here, and
// check via GetFunctionRealm (see src/objects.cc).
GotoIfNot(IsJSFunction(new_target), &mark_megamorphic);
Node* target_context =
LoadObjectField(new_target, JSFunction::kContextOffset);
Node* target_native_context = LoadNativeContext(target_context);
GotoIfNot(WordEqual(LoadNativeContext(context), target_native_context),
&mark_megamorphic);
// Check if the {new_target} is a JSFunction or JSBoundFunction
// in the current native context.
VARIABLE(var_current, MachineRepresentation::kTagged, new_target);
Label loop(this, &var_current), done_loop(this);
Goto(&loop);
BIND(&loop);
{
Label if_boundfunction(this), if_function(this);
Node* current = var_current.value();
CSA_ASSERT(this, TaggedIsNotSmi(current));
Node* current_instance_type = LoadInstanceType(current);
GotoIf(InstanceTypeEqual(current_instance_type, JS_BOUND_FUNCTION_TYPE),
&if_boundfunction);
Branch(InstanceTypeEqual(current_instance_type, JS_FUNCTION_TYPE),
&if_function, &mark_megamorphic);
BIND(&if_function);
{
// Check that the JSFunction {current} is in the current native
// context.
Node* current_context =
LoadObjectField(current, JSFunction::kContextOffset);
Node* current_native_context = LoadNativeContext(current_context);
Branch(WordEqual(LoadNativeContext(context), current_native_context),
&done_loop, &mark_megamorphic);
}
BIND(&if_boundfunction);
{
// Continue with the [[BoundTargetFunction]] of {current}.
var_current.Bind(LoadObjectField(
current, JSBoundFunction::kBoundTargetFunctionOffset));
Goto(&loop);
}
}
BIND(&done_loop);
CreateWeakCellInFeedbackVector(feedback_vector, slot_id, new_target);
ReportFeedbackUpdate(feedback_vector, slot_id,
"ConstructWithSpread:Initialize");
......
......@@ -182,3 +182,105 @@
assertEquals(2, foo(3).y);
assertEquals(3, foo(3).z);
})();
(function() {
const A = class A {};
const B = A.bind();
function foo(B) {
return new B;
}
assertInstanceof(foo(B), A);
assertInstanceof(foo(B), A);
%OptimizeFunctionOnNextCall(foo);
assertInstanceof(foo(B), A);
})();
(function() {
const A = class A {
constructor(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
};
const B = A.bind(null, 1, 2);
function foo(B, z) {
return new B(z);
}
assertEquals(1, foo(B, 3).x);
assertEquals(2, foo(B, 3).y);
assertEquals(3, foo(B, 3).z);
%OptimizeFunctionOnNextCall(foo);
assertEquals(1, foo(B, 3).x);
assertEquals(2, foo(B, 3).y);
assertEquals(3, foo(B, 3).z);
})();
(function() {
const A = class A {
constructor(value) {
this.value = value;
}
};
const C = class C extends A {
constructor() { super(1); }
};
const B = C.__proto__ = A.bind(null, 1);
assertInstanceof(new C(), A);
assertInstanceof(new C(), B);
assertInstanceof(new C(), C);
assertEquals(1, new C().value);
%OptimizeFunctionOnNextCall(C);
assertInstanceof(new C(), A);
assertInstanceof(new C(), B);
assertInstanceof(new C(), C);
assertEquals(1, new C().value);
})();
(function() {
const A = class A {};
const B = A.bind();
function bar(B, ...args) {
return new B(...args);
}
function foo(B) {
return bar(B)
}
assertInstanceof(foo(B), A);
assertInstanceof(foo(B), A);
%OptimizeFunctionOnNextCall(foo);
assertInstanceof(foo(B), A);
})();
(function() {
const A = class A {
constructor(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
};
const B = A.bind(null, 1, 2);
function bar(B, ...args) {
return new B(...args);
}
function foo(B, z) {
return bar(B, z);
}
assertEquals(1, foo(B, 3).x);
assertEquals(2, foo(B, 3).y);
assertEquals(3, foo(B, 3).z);
%OptimizeFunctionOnNextCall(foo);
assertEquals(1, foo(B, 3).x);
assertEquals(2, foo(B, 3).y);
assertEquals(3, foo(B, 3).z);
})();
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