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 { ...@@ -317,11 +317,17 @@ namespace internal {
TFJ(ArrayFindLoopLazyDeoptContinuation, 5, kCallbackFn, kThisArg, kInitialK, \ TFJ(ArrayFindLoopLazyDeoptContinuation, 5, kCallbackFn, kThisArg, kInitialK, \
kLength, kResult) \ kLength, kResult) \
TFJ(ArrayFindLoopAfterCallbackLazyDeoptContinuation, 6, kCallbackFn, \ TFJ(ArrayFindLoopAfterCallbackLazyDeoptContinuation, 6, kCallbackFn, \
kThisArg, kInitialK, kLength, kElement, kResult) \ kThisArg, kInitialK, kLength, kFoundValue, kIsFound) \
TFJ(ArrayPrototypeFind, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \ TFJ(ArrayPrototypeFind, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-array.prototype.findIndex */ \ /* ES6 #sec-array.prototype.findIndex */ \
TFS(ArrayFindIndexLoopContinuation, kReceiver, kCallbackFn, kThisArg, \ TFS(ArrayFindIndexLoopContinuation, kReceiver, kCallbackFn, kThisArg, \
kArray, kObject, kInitialK, kLength, kTo) \ 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, \ TFJ(ArrayPrototypeFindIndex, \
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \ SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-array.prototype.keys */ \ /* ES6 #sec-array.prototype.keys */ \
......
...@@ -172,9 +172,12 @@ Callable Builtins::CallableFor(Isolate* isolate, Name name) { ...@@ -172,9 +172,12 @@ Callable Builtins::CallableFor(Isolate* isolate, Name name) {
#undef CASE_OTHER #undef CASE_OTHER
case kArrayFilterLoopEagerDeoptContinuation: case kArrayFilterLoopEagerDeoptContinuation:
case kArrayFilterLoopLazyDeoptContinuation: case kArrayFilterLoopLazyDeoptContinuation:
case kArrayFindIndexLoopAfterCallbackLazyDeoptContinuation:
case kArrayFindIndexLoopEagerDeoptContinuation:
case kArrayFindIndexLoopLazyDeoptContinuation:
case kArrayFindLoopAfterCallbackLazyDeoptContinuation:
case kArrayFindLoopEagerDeoptContinuation: case kArrayFindLoopEagerDeoptContinuation:
case kArrayFindLoopLazyDeoptContinuation: case kArrayFindLoopLazyDeoptContinuation:
case kArrayFindLoopAfterCallbackLazyDeoptContinuation:
case kArrayForEach: case kArrayForEach:
case kArrayForEachLoopEagerDeoptContinuation: case kArrayForEachLoopEagerDeoptContinuation:
case kArrayForEachLoopLazyDeoptContinuation: case kArrayForEachLoopLazyDeoptContinuation:
...@@ -220,6 +223,12 @@ bool Builtins::IsLazy(int index) { ...@@ -220,6 +223,12 @@ bool Builtins::IsLazy(int index) {
case kArrayFindLoopLazyDeoptContinuation: // https://crbug.com/v8/6786. case kArrayFindLoopLazyDeoptContinuation: // https://crbug.com/v8/6786.
// https://crbug.com/v8/6786. // https://crbug.com/v8/6786.
case kArrayFindLoopAfterCallbackLazyDeoptContinuation: 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 kArrayForEachLoopEagerDeoptContinuation: // https://crbug.com/v8/6786.
case kArrayForEachLoopLazyDeoptContinuation: // https://crbug.com/v8/6786. case kArrayForEachLoopLazyDeoptContinuation: // https://crbug.com/v8/6786.
case kArrayMapLoopEagerDeoptContinuation: // https://crbug.com/v8/6786. case kArrayMapLoopEagerDeoptContinuation: // https://crbug.com/v8/6786.
......
...@@ -1392,7 +1392,8 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function, ...@@ -1392,7 +1392,8 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function,
return Replace(a); return Replace(a);
} }
Reduction JSCallReducer::ReduceArrayFind(Handle<JSFunction> function, Reduction JSCallReducer::ReduceArrayFind(ArrayFindVariant variant,
Handle<JSFunction> function,
Node* node) { Node* node) {
if (!FLAG_turbo_inline_array_builtins) return NoChange(); if (!FLAG_turbo_inline_array_builtins) return NoChange();
DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
...@@ -1401,6 +1402,24 @@ Reduction JSCallReducer::ReduceArrayFind(Handle<JSFunction> function, ...@@ -1401,6 +1402,24 @@ Reduction JSCallReducer::ReduceArrayFind(Handle<JSFunction> function,
return NoChange(); 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* outer_frame_state = NodeProperties::GetFrameStateInput(node);
Node* effect = NodeProperties::GetEffectInput(node); Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node); Node* control = NodeProperties::GetControlInput(node);
...@@ -1455,9 +1474,9 @@ Reduction JSCallReducer::ReduceArrayFind(Handle<JSFunction> function, ...@@ -1455,9 +1474,9 @@ Reduction JSCallReducer::ReduceArrayFind(Handle<JSFunction> function,
Node* check_throw = nullptr; Node* check_throw = nullptr;
{ {
Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState( Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
jsgraph(), function, Builtins::kArrayFindLoopLazyDeoptContinuation, jsgraph(), function, lazy_continuation_builtin, node->InputAt(0),
node->InputAt(0), context, &checkpoint_params[0], stack_parameters, context, &checkpoint_params[0], stack_parameters, outer_frame_state,
outer_frame_state, ContinuationFrameStateMode::LAZY); ContinuationFrameStateMode::LAZY);
WireInCallbackIsCallableCheck(fncallback, context, frame_state, effect, WireInCallbackIsCallableCheck(fncallback, context, frame_state, effect,
&control, &check_fail, &check_throw); &control, &check_fail, &check_throw);
} }
...@@ -1486,9 +1505,9 @@ Reduction JSCallReducer::ReduceArrayFind(Handle<JSFunction> function, ...@@ -1486,9 +1505,9 @@ Reduction JSCallReducer::ReduceArrayFind(Handle<JSFunction> function,
// Check the map hasn't changed during the iteration. // Check the map hasn't changed during the iteration.
{ {
Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState( Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
jsgraph(), function, Builtins::kArrayFindLoopEagerDeoptContinuation, jsgraph(), function, eager_continuation_builtin, node->InputAt(0),
node->InputAt(0), context, &checkpoint_params[0], stack_parameters, context, &checkpoint_params[0], stack_parameters, outer_frame_state,
outer_frame_state, ContinuationFrameStateMode::EAGER); ContinuationFrameStateMode::EAGER);
effect = effect =
graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
...@@ -1516,17 +1535,20 @@ Reduction JSCallReducer::ReduceArrayFind(Handle<JSFunction> function, ...@@ -1516,17 +1535,20 @@ Reduction JSCallReducer::ReduceArrayFind(Handle<JSFunction> function,
jsgraph()->UndefinedConstant(), element); jsgraph()->UndefinedConstant(), element);
} }
Node* if_found_return_value =
(variant == ArrayFindVariant::kFind) ? element : k;
// Call the callback. // Call the callback.
Node* callback_value = nullptr; Node* callback_value = nullptr;
{ {
std::vector<Node*> call_checkpoint_params( std::vector<Node*> call_checkpoint_params({receiver, fncallback, this_arg,
{receiver, fncallback, this_arg, next_k, original_length, element}); next_k, original_length,
if_found_return_value});
const int call_stack_parameters = const int call_stack_parameters =
static_cast<int>(call_checkpoint_params.size()); static_cast<int>(call_checkpoint_params.size());
Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState( Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
jsgraph(), function, jsgraph(), function, after_callback_lazy_continuation_builtin,
Builtins::kArrayFindLoopAfterCallbackLazyDeoptContinuation,
node->InputAt(0), context, &call_checkpoint_params[0], node->InputAt(0), context, &call_checkpoint_params[0],
call_stack_parameters, outer_frame_state, call_stack_parameters, outer_frame_state,
ContinuationFrameStateMode::LAZY); ContinuationFrameStateMode::LAZY);
...@@ -1561,9 +1583,13 @@ Reduction JSCallReducer::ReduceArrayFind(Handle<JSFunction> function, ...@@ -1561,9 +1583,13 @@ Reduction JSCallReducer::ReduceArrayFind(Handle<JSFunction> function,
control = graph()->NewNode(common()->Merge(2), if_found, if_false); control = graph()->NewNode(common()->Merge(2), if_found, if_false);
effect = effect =
graph()->NewNode(common()->EffectPhi(2), efound_branch, eloop, control); graph()->NewNode(common()->EffectPhi(2), efound_branch, eloop, control);
Node* if_not_found_value = (variant == ArrayFindVariant::kFind)
? jsgraph()->UndefinedConstant()
: jsgraph()->MinusOneConstant();
Node* return_value = Node* return_value =
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), 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. // Wire up the branch for the case when IsCallable fails for the callback.
// Since {check_throw} is an unconditional throw, it's impossible to // Since {check_throw} is an unconditional throw, it's impossible to
...@@ -2128,7 +2154,9 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) { ...@@ -2128,7 +2154,9 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
case Builtins::kArrayFilter: case Builtins::kArrayFilter:
return ReduceArrayFilter(function, node); return ReduceArrayFilter(function, node);
case Builtins::kArrayPrototypeFind: 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: case Builtins::kReturnReceiver:
return ReduceReturnReceiver(node); return ReduceReturnReceiver(node);
default: default:
......
...@@ -74,7 +74,9 @@ class JSCallReducer final : public AdvancedReducer { ...@@ -74,7 +74,9 @@ class JSCallReducer final : public AdvancedReducer {
Reduction ReduceArrayForEach(Handle<JSFunction> function, Node* node); Reduction ReduceArrayForEach(Handle<JSFunction> function, Node* node);
Reduction ReduceArrayMap(Handle<JSFunction> function, Node* node); Reduction ReduceArrayMap(Handle<JSFunction> function, Node* node);
Reduction ReduceArrayFilter(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( Reduction ReduceCallOrConstructWithArrayLikeOrSpread(
Node* node, int arity, CallFrequency const& frequency, Node* node, int arity, CallFrequency const& frequency,
VectorSlotPair const& feedback); VectorSlotPair const& feedback);
......
...@@ -72,6 +72,25 @@ ...@@ -72,6 +72,25 @@
assertEquals(100, lazyChanger()); 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 // Lazy deopt from a callback that changes the input array. Deopt in a callback
// execution that returns false. // execution that returns false.
(() => { (() => {
...@@ -408,3 +427,16 @@ ...@@ -408,3 +427,16 @@
%OptimizeFunctionOnNextCall(withHoles); %OptimizeFunctionOnNextCall(withHoles);
assertArrayEquals([1.5, 2.5, undefined, 3.5, 4.5], 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