75629d5f9a
When calling a known function from optimized code, where the number of actual arguments does not match the number of expected arguments, TurboFan has to call indirectly via the arguments adaptor trampoline, which creates an argument adaptor frame underneath the activation record for the callee. This is done so that the callee can still get to the actual arguments, using either 1. the arguments object, or 2. rest parameters (to get to superfluous arguments), or 3. the non-standard Function.arguments accessor (for sloppy mode functions), or 4. direct eval(), where we don't know whether there's a use of the arguments object hiding somewhere in the string. However going through the arguments adaptor trampoline is quite expensive usually, it seems to be responsible for over 60% of the call overhead in those cases. So this adds a fast path for the case of calling strict mode functions where we have an arguments mismatch, but where we are sure that the callee cannot observe the actual arguments. We use a bit on the SharedFunctionInfo to indicate that this is safe, which is controlled by hints from the Parser which knows whether the callee uses either arguments object or rest parameters. In those cases we use a direct call from optimized code, passing the expected arguments instead of the actual arguments. This improves the benchmark on the document below by around 60-65%, which is exactly the overhead of the arguments adaptor trampoline that we save in this case. This also adds a runtime flag --fast_calls_with_arguments_mismatches, which can be used to turn off the new behavior. This might be handy for checking the performance impact via Finch. Bug: v8:8895 Change-Id: Idea51dba7ee6cb989e86e0742eaf3516e5afe3c4 Cq-Include-Trybots: luci.chromium.try:linux-blink-rel Doc: http://bit.ly/v8-faster-calls-with-arguments-mismatch Reviewed-on: https://chromium-review.googlesource.com/c/1482735 Commit-Queue: Benedikt Meurer <bmeurer@chromium.org> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Cr-Commit-Position: refs/heads/master@{#59825}
145 lines
4.2 KiB
JavaScript
145 lines
4.2 KiB
JavaScript
// Copyright 2019 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// Flags: --allow-natives-syntax --noturbo-inlining
|
|
|
|
// Ensure that arguments in sloppy mode function works
|
|
// properly when called directly from optimized code.
|
|
(function() {
|
|
function g() { return arguments; }
|
|
function f() { return g(1, 2, 3); }
|
|
|
|
assertEquals(g(1, 2, 3), f());
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(f);
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(g);
|
|
assertEquals(g(1, 2, 3), f());
|
|
})();
|
|
|
|
// Ensure that arguments in strict mode function works
|
|
// properly when called directly from optimized code.
|
|
(function() {
|
|
"use strict";
|
|
function g() { return arguments; }
|
|
function f() { return g(1, 2, 3); }
|
|
|
|
assertEquals(g(1, 2, 3), f());
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(f);
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(g);
|
|
assertEquals(g(1, 2, 3), f());
|
|
})();
|
|
|
|
// Ensure that arguments in sloppy mode function works
|
|
// properly when called directly from optimized code,
|
|
// and the access to "arguments" is hidden inside eval().
|
|
(function() {
|
|
function g() { return eval("arguments"); }
|
|
function f() { return g(1, 2, 3); }
|
|
|
|
assertEquals(g(1, 2, 3), f());
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(f);
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(g);
|
|
assertEquals(g(1, 2, 3), f());
|
|
})();
|
|
|
|
// Ensure that arguments in strict mode function works
|
|
// properly when called directly from optimized code,
|
|
// and the access to "arguments" is hidden inside eval().
|
|
(function() {
|
|
"use strict";
|
|
function g() { return eval("arguments"); }
|
|
function f() { return g(1, 2, 3); }
|
|
|
|
assertEquals(g(1, 2, 3), f());
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(f);
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(g);
|
|
assertEquals(g(1, 2, 3), f());
|
|
})();
|
|
|
|
// Ensure that `Function.arguments` accessor does the
|
|
// right thing in sloppy mode functions called directly
|
|
// from optimized code.
|
|
(function() {
|
|
function h() { return g.arguments; }
|
|
function g() { return h(); }
|
|
function f() { return g(1, 2, 3); }
|
|
|
|
assertEquals(g(1, 2, 3), f());
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(f);
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(g);
|
|
assertEquals(g(1, 2, 3), f());
|
|
})();
|
|
(function() {
|
|
function h() { return g.arguments; }
|
|
function g() { return h(); }
|
|
function f() { "use strict"; return g(1, 2, 3); }
|
|
|
|
assertEquals(g(1, 2, 3), f());
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(f);
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(g);
|
|
assertEquals(g(1, 2, 3), f());
|
|
})();
|
|
(function() {
|
|
function h() { "use strict"; return g.arguments; }
|
|
function g() { return h(); }
|
|
function f() { return g(1, 2, 3); }
|
|
|
|
assertEquals(g(1, 2, 3), f());
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(f);
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(g);
|
|
assertEquals(g(1, 2, 3), f());
|
|
})();
|
|
(function() {
|
|
function h() { "use strict"; return g.arguments; }
|
|
function g() { return h(); }
|
|
function f() { "use strict"; return g(1, 2, 3); }
|
|
|
|
assertEquals(g(1, 2, 3), f());
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(f);
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(g);
|
|
assertEquals(g(1, 2, 3), f());
|
|
})();
|
|
|
|
// Ensure that `Function.arguments` works properly in
|
|
// combination with the `Function.caller` proper.
|
|
(function() {
|
|
function h() { return h.caller.arguments; }
|
|
function g() { return h(); }
|
|
function f() { return g(1, 2, 3); }
|
|
|
|
assertEquals(g(1, 2, 3), f());
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(f);
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(g);
|
|
assertEquals(g(1, 2, 3), f());
|
|
})();
|
|
(function() {
|
|
function h() { return h.caller.arguments; }
|
|
function g() { return h(); }
|
|
function f() { "use strict"; return g(1, 2, 3); }
|
|
|
|
assertEquals(g(1, 2, 3), f());
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(f);
|
|
assertEquals(g(1, 2, 3), f());
|
|
%OptimizeFunctionOnNextCall(g);
|
|
assertEquals(g(1, 2, 3), f());
|
|
})();
|