Commit 61e2f270 authored by peterwmwong's avatar peterwmwong Committed by Commit Bot

[turbofan] Array.prototype.findIndex inlining.

Support inlining Array.prototype.findIndex in Turbofan.
Depending on array size, quick benchmarks show a >2x
improvement: https://github.com/peterwmwong/v8-perf/blob/master/array-find-findIndex-tf/README.md

Bug: chromium:791045, v8:1956, v8:7165
Change-Id: I250554885f924c97b0072e09ee289713df5cbe63
Reviewed-on: https://chromium-review.googlesource.com/824382
Commit-Queue: Peter Wong <peter.wm.wong@gmail.com>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarMichael Stanton <mvstanton@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50133}
parent bb26027e
This diff is collapsed.
......@@ -317,11 +317,17 @@ namespace internal {
TFJ(ArrayFindLoopLazyDeoptContinuation, 5, kCallbackFn, kThisArg, kInitialK, \
kLength, kResult) \
TFJ(ArrayFindLoopAfterCallbackLazyDeoptContinuation, 6, kCallbackFn, \
kThisArg, kInitialK, kLength, kElement, kResult) \
kThisArg, kInitialK, kLength, kFoundValue, kIsFound) \
TFJ(ArrayPrototypeFind, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-array.prototype.findIndex */ \
TFS(ArrayFindIndexLoopContinuation, kReceiver, kCallbackFn, kThisArg, \
kArray, kObject, kInitialK, kLength, kTo) \
TFJ(ArrayFindIndexLoopEagerDeoptContinuation, 4, kCallbackFn, kThisArg, \
kInitialK, kLength) \
TFJ(ArrayFindIndexLoopLazyDeoptContinuation, 5, kCallbackFn, kThisArg, \
kInitialK, kLength, kResult) \
TFJ(ArrayFindIndexLoopAfterCallbackLazyDeoptContinuation, 6, kCallbackFn, \
kThisArg, kInitialK, kLength, kFoundValue, kIsFound) \
TFJ(ArrayPrototypeFindIndex, \
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-array.prototype.keys */ \
......
......@@ -172,9 +172,12 @@ Callable Builtins::CallableFor(Isolate* isolate, Name name) {
#undef CASE_OTHER
case kArrayFilterLoopEagerDeoptContinuation:
case kArrayFilterLoopLazyDeoptContinuation:
case kArrayFindIndexLoopAfterCallbackLazyDeoptContinuation:
case kArrayFindIndexLoopEagerDeoptContinuation:
case kArrayFindIndexLoopLazyDeoptContinuation:
case kArrayFindLoopAfterCallbackLazyDeoptContinuation:
case kArrayFindLoopEagerDeoptContinuation:
case kArrayFindLoopLazyDeoptContinuation:
case kArrayFindLoopAfterCallbackLazyDeoptContinuation:
case kArrayForEach:
case kArrayForEachLoopEagerDeoptContinuation:
case kArrayForEachLoopLazyDeoptContinuation:
......@@ -220,6 +223,12 @@ bool Builtins::IsLazy(int index) {
case kArrayFindLoopLazyDeoptContinuation: // https://crbug.com/v8/6786.
// https://crbug.com/v8/6786.
case kArrayFindLoopAfterCallbackLazyDeoptContinuation:
// https://crbug.com/v8/6786.
case kArrayFindIndexLoopEagerDeoptContinuation:
// https://crbug.com/v8/6786.
case kArrayFindIndexLoopLazyDeoptContinuation:
// https://crbug.com/v8/6786.
case kArrayFindIndexLoopAfterCallbackLazyDeoptContinuation:
case kArrayForEachLoopEagerDeoptContinuation: // https://crbug.com/v8/6786.
case kArrayForEachLoopLazyDeoptContinuation: // https://crbug.com/v8/6786.
case kArrayMapLoopEagerDeoptContinuation: // https://crbug.com/v8/6786.
......
......@@ -1392,7 +1392,8 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function,
return Replace(a);
}
Reduction JSCallReducer::ReduceArrayFind(Handle<JSFunction> function,
Reduction JSCallReducer::ReduceArrayFind(ArrayFindVariant variant,
Handle<JSFunction> function,
Node* node) {
if (!FLAG_turbo_inline_array_builtins) return NoChange();
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
......@@ -1401,6 +1402,24 @@ Reduction JSCallReducer::ReduceArrayFind(Handle<JSFunction> function,
return NoChange();
}
Builtins::Name eager_continuation_builtin;
Builtins::Name lazy_continuation_builtin;
Builtins::Name after_callback_lazy_continuation_builtin;
if (variant == ArrayFindVariant::kFind) {
eager_continuation_builtin = Builtins::kArrayFindLoopEagerDeoptContinuation;
lazy_continuation_builtin = Builtins::kArrayFindLoopLazyDeoptContinuation;
after_callback_lazy_continuation_builtin =
Builtins::kArrayFindLoopAfterCallbackLazyDeoptContinuation;
} else {
DCHECK_EQ(ArrayFindVariant::kFindIndex, variant);
eager_continuation_builtin =
Builtins::kArrayFindIndexLoopEagerDeoptContinuation;
lazy_continuation_builtin =
Builtins::kArrayFindIndexLoopLazyDeoptContinuation;
after_callback_lazy_continuation_builtin =
Builtins::kArrayFindIndexLoopAfterCallbackLazyDeoptContinuation;
}
Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
......@@ -1455,9 +1474,9 @@ Reduction JSCallReducer::ReduceArrayFind(Handle<JSFunction> function,
Node* check_throw = nullptr;
{
Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
jsgraph(), function, Builtins::kArrayFindLoopLazyDeoptContinuation,
node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
outer_frame_state, ContinuationFrameStateMode::LAZY);
jsgraph(), function, lazy_continuation_builtin, node->InputAt(0),
context, &checkpoint_params[0], stack_parameters, outer_frame_state,
ContinuationFrameStateMode::LAZY);
WireInCallbackIsCallableCheck(fncallback, context, frame_state, effect,
&control, &check_fail, &check_throw);
}
......@@ -1486,9 +1505,9 @@ Reduction JSCallReducer::ReduceArrayFind(Handle<JSFunction> function,
// Check the map hasn't changed during the iteration.
{
Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
jsgraph(), function, Builtins::kArrayFindLoopEagerDeoptContinuation,
node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
outer_frame_state, ContinuationFrameStateMode::EAGER);
jsgraph(), function, eager_continuation_builtin, node->InputAt(0),
context, &checkpoint_params[0], stack_parameters, outer_frame_state,
ContinuationFrameStateMode::EAGER);
effect =
graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
......@@ -1516,17 +1535,20 @@ Reduction JSCallReducer::ReduceArrayFind(Handle<JSFunction> function,
jsgraph()->UndefinedConstant(), element);
}
Node* if_found_return_value =
(variant == ArrayFindVariant::kFind) ? element : k;
// Call the callback.
Node* callback_value = nullptr;
{
std::vector<Node*> call_checkpoint_params(
{receiver, fncallback, this_arg, next_k, original_length, element});
std::vector<Node*> call_checkpoint_params({receiver, fncallback, this_arg,
next_k, original_length,
if_found_return_value});
const int call_stack_parameters =
static_cast<int>(call_checkpoint_params.size());
Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
jsgraph(), function,
Builtins::kArrayFindLoopAfterCallbackLazyDeoptContinuation,
jsgraph(), function, after_callback_lazy_continuation_builtin,
node->InputAt(0), context, &call_checkpoint_params[0],
call_stack_parameters, outer_frame_state,
ContinuationFrameStateMode::LAZY);
......@@ -1561,9 +1583,13 @@ Reduction JSCallReducer::ReduceArrayFind(Handle<JSFunction> function,
control = graph()->NewNode(common()->Merge(2), if_found, if_false);
effect =
graph()->NewNode(common()->EffectPhi(2), efound_branch, eloop, control);
Node* if_not_found_value = (variant == ArrayFindVariant::kFind)
? jsgraph()->UndefinedConstant()
: jsgraph()->MinusOneConstant();
Node* return_value =
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
element, jsgraph()->UndefinedConstant(), control);
if_found_return_value, if_not_found_value, control);
// Wire up the branch for the case when IsCallable fails for the callback.
// Since {check_throw} is an unconditional throw, it's impossible to
......@@ -2128,7 +2154,9 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
case Builtins::kArrayFilter:
return ReduceArrayFilter(function, node);
case Builtins::kArrayPrototypeFind:
return ReduceArrayFind(function, node);
return ReduceArrayFind(ArrayFindVariant::kFind, function, node);
case Builtins::kArrayPrototypeFindIndex:
return ReduceArrayFind(ArrayFindVariant::kFindIndex, function, node);
case Builtins::kReturnReceiver:
return ReduceReturnReceiver(node);
default:
......
......@@ -74,7 +74,9 @@ class JSCallReducer final : public AdvancedReducer {
Reduction ReduceArrayForEach(Handle<JSFunction> function, Node* node);
Reduction ReduceArrayMap(Handle<JSFunction> function, Node* node);
Reduction ReduceArrayFilter(Handle<JSFunction> function, Node* node);
Reduction ReduceArrayFind(Handle<JSFunction> function, Node* node);
enum class ArrayFindVariant : uint8_t { kFind, kFindIndex };
Reduction ReduceArrayFind(ArrayFindVariant variant,
Handle<JSFunction> function, Node* node);
Reduction ReduceCallOrConstructWithArrayLikeOrSpread(
Node* node, int arity, CallFrequency const& frequency,
VectorSlotPair const& feedback);
......
......@@ -72,6 +72,25 @@
assertEquals(100, lazyChanger());
})();
// Lazy deopt from a callback that will always return false and no element is
// found. Verifies the lazy-after-callback continuation builtin.
(() => {
const a = [1, 2, 3, 4, 5];
function lazyChanger(deopt) {
return a.find((v, i) => {
if (i === 3 && deopt) {
%DeoptimizeNow();
}
return false;
});
}
assertEquals(undefined, lazyChanger());
lazyChanger();
%OptimizeFunctionOnNextCall(lazyChanger);
assertEquals(undefined, lazyChanger(true));
assertEquals(undefined, lazyChanger());
})();
// Lazy deopt from a callback that changes the input array. Deopt in a callback
// execution that returns false.
(() => {
......@@ -408,3 +427,16 @@
%OptimizeFunctionOnNextCall(withHoles);
assertArrayEquals([1.5, 2.5, undefined, 3.5, 4.5], withHoles());
})();
// Handle callback is not callable.
(() => {
const a = [1, 2, 3, 4, 5];
function notCallable() {
return a.find(undefined);
}
assertThrows(notCallable, TypeError);
try { notCallable(); } catch(e) { }
%OptimizeFunctionOnNextCall(notCallable);
assertThrows(notCallable, TypeError);
})();
This diff is collapsed.
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