From 1d80223f3db99ad66887edaabefc3cf557a55de7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Mon, 5 Jun 2017 13:49:29 +0200 Subject: [PATCH] Bug 1370195 - Use fewer array allocations when invoking bound functions with many arguments. r=till --- js/src/builtin/Function.js | 98 +++++++++++++++++++++++++++++--------- 1 file changed, 76 insertions(+), 22 deletions(-) diff --git a/js/src/builtin/Function.js b/js/src/builtin/Function.js index 78f1fc29247f..e34b5bb3eb58 100644 --- a/js/src/builtin/Function.js +++ b/js/src/builtin/Function.js @@ -50,11 +50,20 @@ function FunctionBind(thisArg, ...boundArgs) { * construct helper functions. This avoids having to use rest parameters and * destructuring in the fast path. * + * Directly embedding the for-loop to combine bound and call arguments may + * inhibit inlining of the bound function, so we use a separate combiner + * function to perform this task. This combiner function is created lazily to + * ensure we only pay its construction cost when needed. + * * All bind_bindFunction{X} functions have the same signature to enable simple * reading out of closed-over state by debugging functions. */ function bind_bindFunction0(fun, thisArg, boundArgs) { return function bound() { + // Ensure we allocate a call-object slot for |boundArgs|, so the + // debugger can access this value. + if (false) void boundArgs; + var newTarget; if (_IsConstructing()) { newTarget = new.target; @@ -73,6 +82,9 @@ function bind_bindFunction0(fun, thisArg, boundArgs) { return constructContentFunction(fun, newTarget, SPREAD(arguments, 4)); case 5: return constructContentFunction(fun, newTarget, SPREAD(arguments, 5)); + default: + var args = FUN_APPLY(bind_mapArguments, null, arguments); + return bind_constructFunctionN(fun, newTarget, args); } } else { switch (arguments.length) { @@ -88,16 +100,21 @@ function bind_bindFunction0(fun, thisArg, boundArgs) { return callContentFunction(fun, thisArg, SPREAD(arguments, 4)); case 5: return callContentFunction(fun, thisArg, SPREAD(arguments, 5)); + default: + return FUN_APPLY(fun, thisArg, arguments); } } - var callArgs = FUN_APPLY(bind_mapArguments, null, arguments); - return bind_invokeFunctionN(fun, thisArg, newTarget, boundArgs, callArgs); }; } function bind_bindFunction1(fun, thisArg, boundArgs) { var bound1 = boundArgs[0]; + var combiner = null; return function bound() { + // Ensure we allocate a call-object slot for |boundArgs|, so the + // debugger can access this value. + if (false) void boundArgs; + var newTarget; if (_IsConstructing()) { newTarget = new.target; @@ -133,15 +150,34 @@ function bind_bindFunction1(fun, thisArg, boundArgs) { return callContentFunction(fun, thisArg, bound1, SPREAD(arguments, 5)); } } - var callArgs = FUN_APPLY(bind_mapArguments, null, arguments); - return bind_invokeFunctionN(fun, thisArg, newTarget, boundArgs, callArgs); + + if (combiner === null) { + combiner = function() { + var callArgsCount = arguments.length; + var args = std_Array(1 + callArgsCount); + _DefineDataProperty(args, 0, bound1); + for (var i = 0; i < callArgsCount; i++) + _DefineDataProperty(args, i + 1, arguments[i]); + return args; + }; + } + + var args = FUN_APPLY(combiner, null, arguments); + if (newTarget === undefined) + return bind_applyFunctionN(fun, thisArg, args); + return bind_constructFunctionN(fun, newTarget, args); }; } function bind_bindFunction2(fun, thisArg, boundArgs) { var bound1 = boundArgs[0]; var bound2 = boundArgs[1]; + var combiner = null; return function bound() { + // Ensure we allocate a call-object slot for |boundArgs|, so the + // debugger can access this value. + if (false) void boundArgs; + var newTarget; if (_IsConstructing()) { newTarget = new.target; @@ -177,13 +213,29 @@ function bind_bindFunction2(fun, thisArg, boundArgs) { return callContentFunction(fun, thisArg, bound1, bound2, SPREAD(arguments, 5)); } } - var callArgs = FUN_APPLY(bind_mapArguments, null, arguments); - return bind_invokeFunctionN(fun, thisArg, newTarget, boundArgs, callArgs); + + if (combiner === null) { + combiner = function() { + var callArgsCount = arguments.length; + var args = std_Array(2 + callArgsCount); + _DefineDataProperty(args, 0, bound1); + _DefineDataProperty(args, 1, bound2); + for (var i = 0; i < callArgsCount; i++) + _DefineDataProperty(args, i + 2, arguments[i]); + return args; + }; + } + + var args = FUN_APPLY(combiner, null, arguments); + if (newTarget === undefined) + return bind_applyFunctionN(fun, thisArg, args); + return bind_constructFunctionN(fun, newTarget, args); }; } function bind_bindFunctionN(fun, thisArg, boundArgs) { assert(boundArgs.length > 2, "Fast paths should be used for few-bound-args cases."); + var combiner = null; return function bound() { var newTarget; if (_IsConstructing()) { @@ -194,11 +246,26 @@ function bind_bindFunctionN(fun, thisArg, boundArgs) { if (arguments.length === 0) { if (newTarget !== undefined) return bind_constructFunctionN(fun, newTarget, boundArgs); - return bind_applyFunctionN(fun, thisArg, boundArgs); } - var callArgs = FUN_APPLY(bind_mapArguments, null, arguments); - return bind_invokeFunctionN(fun, thisArg, newTarget, boundArgs, callArgs); + + if (combiner === null) { + combiner = function() { + var boundArgsCount = boundArgs.length; + var callArgsCount = arguments.length; + var args = std_Array(boundArgsCount + callArgsCount); + for (var i = 0; i < boundArgsCount; i++) + _DefineDataProperty(args, i, boundArgs[i]); + for (var i = 0; i < callArgsCount; i++) + _DefineDataProperty(args, i + boundArgsCount, arguments[i]); + return args; + }; + } + + var args = FUN_APPLY(combiner, null, arguments); + if (newTarget !== undefined) + return bind_constructFunctionN(fun, newTarget, args); + return bind_applyFunctionN(fun, thisArg, args); }; } @@ -210,19 +277,6 @@ function bind_mapArguments() { return args; } -function bind_invokeFunctionN(fun, thisArg, newTarget, boundArgs, callArgs) { - var boundArgsCount = boundArgs.length; - var callArgsCount = callArgs.length; - var args = std_Array(boundArgsCount + callArgsCount); - for (var i = 0; i < boundArgsCount; i++) - _DefineDataProperty(args, i, boundArgs[i]); - for (var i = 0; i < callArgsCount; i++) - _DefineDataProperty(args, i + boundArgsCount, callArgs[i]); - if (newTarget !== undefined) - return bind_constructFunctionN(fun, newTarget, args); - return bind_applyFunctionN(fun, thisArg, args); -} - function bind_applyFunctionN(fun, thisArg, args) { switch (args.length) { case 0: