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

[ic] Teach CallIC about JSBoundFunction.

This addresses the odd performance cliff, where the CallIC tracks known
JSFunction targets, but goes MEGAMORPHIC when it sees a JSBoundFunction
target. With this fix in place the micro-benchmark on the bug goes from

  arrowCall: 82 ms.
  boundCall: 234 ms.

to

  arrowCall: 81 ms.
  boundCall: 80 ms.

so Function#bind doesn't cause any additional overhead anymore.

Bug: v8:5267, v8:6962
Change-Id: Iaceaf89fd3e99e2afe2ae45e96a6813a3ef8b1d2
Reviewed-on: https://chromium-review.googlesource.com/727879
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48722}
parent 4c5c5bc0
......@@ -1985,7 +1985,7 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
if (!ShouldUseCallICFeedback(target)) return NoChange();
Handle<WeakCell> cell = Handle<WeakCell>::cast(feedback);
if (cell->value()->IsJSFunction()) {
if (cell->value()->IsCallable()) {
Node* target_function =
jsgraph()->Constant(handle(cell->value(), isolate()));
......
......@@ -641,15 +641,42 @@ void InterpreterAssembler::CollectCallFeedback(Node* target, Node* context,
// context.
Comment("check if function in same native context");
GotoIf(TaggedIsSmi(target), &mark_megamorphic);
// TODO(bmeurer): Add support for arbitrary callables here, and
// check via GetFunctionRealm (see src/objects.cc).
GotoIfNot(IsJSFunction(target), &mark_megamorphic);
Node* target_context =
LoadObjectField(target, JSFunction::kContextOffset);
Node* target_native_context = LoadNativeContext(target_context);
GotoIfNot(WordEqual(LoadNativeContext(context), target_native_context),
&mark_megamorphic);
// Check if the {target} is a JSFunction or JSBoundFunction
// in the current native context.
VARIABLE(var_target, MachineRepresentation::kTagged, target);
Label loop(this, &var_target), done_loop(this);
Goto(&loop);
BIND(&loop);
{
Label if_boundfunction(this), if_function(this);
Node* target = var_target.value();
CSA_ASSERT(this, TaggedIsNotSmi(target));
Node* target_instance_type = LoadInstanceType(target);
GotoIf(InstanceTypeEqual(target_instance_type, JS_BOUND_FUNCTION_TYPE),
&if_boundfunction);
Branch(InstanceTypeEqual(target_instance_type, JS_FUNCTION_TYPE),
&if_function, &mark_megamorphic);
BIND(&if_function);
{
// Check that the JSFunction {target} is in the current native
// context.
Node* target_context =
LoadObjectField(target, JSFunction::kContextOffset);
Node* target_native_context = LoadNativeContext(target_context);
Branch(WordEqual(LoadNativeContext(context), target_native_context),
&done_loop, &mark_megamorphic);
}
BIND(&if_boundfunction);
{
// Continue with the [[BoundTargetFunction]] of {target}.
var_target.Bind(LoadObjectField(
target, JSBoundFunction::kBoundTargetFunctionOffset));
Goto(&loop);
}
}
BIND(&done_loop);
CreateWeakCellInFeedbackVector(feedback_vector, SmiTag(slot_id), target);
// Reset profiler ticks.
StoreObjectFieldNoWriteBarrier(feedback_vector,
......
......@@ -99,3 +99,15 @@
assertEquals([1, 2, 3], foo([0, 1, 2]));
assertEquals([2, 3, 4], foo([1, 2, 3]));
})();
(function() {
const add = (x, y) => x + y;
const inc = add.bind(null, 1);
function foo(inc) { return inc(1); }
assertEquals(2, foo(inc));
assertEquals(2, foo(inc));
%OptimizeFunctionOnNextCall(foo);
assertEquals(2, foo(inc));
})();
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