summaryrefslogtreecommitdiff
path: root/deps/v8/src/compiler/js-call-reducer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/compiler/js-call-reducer.cc')
-rw-r--r--deps/v8/src/compiler/js-call-reducer.cc2109
1 files changed, 1967 insertions, 142 deletions
diff --git a/deps/v8/src/compiler/js-call-reducer.cc b/deps/v8/src/compiler/js-call-reducer.cc
index c595b360d5..1f8e7a2cef 100644
--- a/deps/v8/src/compiler/js-call-reducer.cc
+++ b/deps/v8/src/compiler/js-call-reducer.cc
@@ -5,6 +5,7 @@
#include "src/compiler/js-call-reducer.h"
#include "src/api.h"
+#include "src/builtins/builtins-utils.h"
#include "src/code-factory.h"
#include "src/code-stubs.h"
#include "src/compilation-dependencies.h"
@@ -17,6 +18,7 @@
#include "src/feedback-vector-inl.h"
#include "src/ic/call-optimization.h"
#include "src/objects-inl.h"
+#include "src/vector-slot-pair.h"
namespace v8 {
namespace internal {
@@ -90,20 +92,6 @@ Reduction JSCallReducer::ReduceBooleanConstructor(Node* node) {
return Replace(value);
}
-// ES6 section 20.1.1 The Number Constructor
-Reduction JSCallReducer::ReduceNumberConstructor(Node* node) {
- DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
- CallParameters const& p = CallParametersOf(node->op());
-
- // Turn the {node} into a {JSToNumber} call.
- DCHECK_LE(2u, p.arity());
- Node* value = (p.arity() == 2) ? jsgraph()->ZeroConstant()
- : NodeProperties::GetValueInput(node, 2);
- NodeProperties::ReplaceValueInputs(node, value);
- NodeProperties::ChangeOp(node, javascript()->ToNumber());
- return Changed(node);
-}
-
// ES section #sec-object-constructor
Reduction JSCallReducer::ReduceObjectConstructor(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
@@ -549,7 +537,7 @@ Reduction JSCallReducer::ReduceObjectPrototypeHasOwnProperty(Node* node) {
Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
receiver_map, cache_type);
effect = graph()->NewNode(
- simplified()->CheckIf(DeoptimizeReason::kNoReason), check, effect,
+ simplified()->CheckIf(DeoptimizeReason::kWrongMap), check, effect,
control);
}
Node* value = jsgraph()->TrueConstant();
@@ -804,15 +792,37 @@ bool CanInlineArrayIteratingBuiltin(Handle<Map> receiver_map) {
isolate->IsAnyInitialArrayPrototype(receiver_prototype);
}
+Node* JSCallReducer::WireInLoopStart(Node* k, Node** control, Node** effect) {
+ Node* loop = *control =
+ graph()->NewNode(common()->Loop(2), *control, *control);
+ Node* eloop = *effect =
+ graph()->NewNode(common()->EffectPhi(2), *effect, *effect, loop);
+ Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
+ NodeProperties::MergeControlToEnd(graph(), common(), terminate);
+ return graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), k,
+ k, loop);
+}
+
+void JSCallReducer::WireInLoopEnd(Node* loop, Node* eloop, Node* vloop, Node* k,
+ Node* control, Node* effect) {
+ loop->ReplaceInput(1, control);
+ vloop->ReplaceInput(1, k);
+ eloop->ReplaceInput(1, effect);
+}
+
Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
Node* node) {
if (!FLAG_turbo_inline_array_builtins) return NoChange();
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ CallParameters const& p = CallParametersOf(node->op());
+ if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
+ return NoChange();
+ }
+
Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* context = NodeProperties::GetContextInput(node);
- CallParameters const& p = CallParametersOf(node->op());
// Try to determine the {receiver} map.
Node* receiver = NodeProperties::GetValueInput(node, 1);
@@ -825,10 +835,193 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
ZoneHandleSet<Map> receiver_maps;
NodeProperties::InferReceiverMapsResult result =
NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
- if (result != NodeProperties::kReliableReceiverMaps) {
+ if (result == NodeProperties::kNoReceiverMaps) return NoChange();
+
+ // By ensuring that {kind} is object or double, we can be polymorphic
+ // on different elements kinds.
+ ElementsKind kind = receiver_maps[0]->elements_kind();
+ if (IsSmiElementsKind(kind)) {
+ kind = FastSmiToObjectElementsKind(kind);
+ }
+ for (Handle<Map> receiver_map : receiver_maps) {
+ ElementsKind next_kind = receiver_map->elements_kind();
+ if (!CanInlineArrayIteratingBuiltin(receiver_map)) {
+ return NoChange();
+ }
+ if (!IsFastElementsKind(next_kind)) {
+ return NoChange();
+ }
+ if (IsDoubleElementsKind(kind) != IsDoubleElementsKind(next_kind)) {
+ return NoChange();
+ }
+ if (IsHoleyElementsKind(next_kind)) {
+ kind = GetHoleyElementsKind(kind);
+ }
+ }
+
+ // Install code dependencies on the {receiver} prototype maps and the
+ // global array protector cell.
+ dependencies()->AssumePropertyCell(factory()->no_elements_protector());
+
+ // If we have unreliable maps, we need a map check.
+ if (result == NodeProperties::kUnreliableReceiverMaps) {
+ effect =
+ graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
+ receiver_maps, p.feedback()),
+ receiver, effect, control);
+ }
+
+ Node* k = jsgraph()->ZeroConstant();
+
+ Node* original_length = effect = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
+ effect, control);
+
+ std::vector<Node*> checkpoint_params(
+ {receiver, fncallback, this_arg, k, original_length});
+ const int stack_parameters = static_cast<int>(checkpoint_params.size());
+
+ // Check whether the given callback function is callable. Note that this has
+ // to happen outside the loop to make sure we also throw on empty arrays.
+ Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
+ jsgraph(), function, Builtins::kArrayForEachLoopLazyDeoptContinuation,
+ node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
+ outer_frame_state, ContinuationFrameStateMode::LAZY);
+ Node* check_fail = nullptr;
+ Node* check_throw = nullptr;
+ WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, effect,
+ &control, &check_fail, &check_throw);
+
+ // Start the loop.
+ Node* vloop = k = WireInLoopStart(k, &control, &effect);
+ Node *loop = control, *eloop = effect;
+ checkpoint_params[3] = k;
+
+ Node* continue_test =
+ graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
+ Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
+ continue_test, control);
+
+ Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
+ Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
+ control = if_true;
+
+ Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
+ jsgraph(), function, Builtins::kArrayForEachLoopEagerDeoptContinuation,
+ node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
+ outer_frame_state, ContinuationFrameStateMode::EAGER);
+
+ effect =
+ graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
+
+ // Make sure the map hasn't changed during the iteration
+ effect =
+ graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
+ receiver_maps, p.feedback()),
+ receiver, effect, control);
+
+ Node* element =
+ SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());
+
+ Node* next_k =
+ graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());
+ checkpoint_params[3] = next_k;
+
+ Node* hole_true = nullptr;
+ Node* hole_false = nullptr;
+ Node* effect_true = effect;
+
+ if (IsHoleyElementsKind(kind)) {
+ // Holey elements kind require a hole check and skipping of the element in
+ // the case of a hole.
+ Node* check;
+ if (IsDoubleElementsKind(kind)) {
+ check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
+ } else {
+ check = graph()->NewNode(simplified()->ReferenceEqual(), element,
+ jsgraph()->TheHoleConstant());
+ }
+ Node* branch =
+ graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
+ hole_true = graph()->NewNode(common()->IfTrue(), branch);
+ hole_false = graph()->NewNode(common()->IfFalse(), branch);
+ control = hole_false;
+
+ // The contract is that we don't leak "the hole" into "user JavaScript",
+ // so we must rename the {element} here to explicitly exclude "the hole"
+ // from the type of {element}.
+ element = effect = graph()->NewNode(
+ common()->TypeGuard(Type::NonInternal()), element, effect, control);
+ }
+
+ frame_state = CreateJavaScriptBuiltinContinuationFrameState(
+ jsgraph(), function, Builtins::kArrayForEachLoopLazyDeoptContinuation,
+ node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
+ outer_frame_state, ContinuationFrameStateMode::LAZY);
+
+ control = effect = graph()->NewNode(
+ javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
+ receiver, context, frame_state, effect, control);
+
+ // Rewire potential exception edges.
+ Node* on_exception = nullptr;
+ if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
+ RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
+ &check_fail, &control);
+ }
+
+ if (IsHoleyElementsKind(kind)) {
+ Node* after_call_control = control;
+ Node* after_call_effect = effect;
+ control = hole_true;
+ effect = effect_true;
+
+ control = graph()->NewNode(common()->Merge(2), control, after_call_control);
+ effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect,
+ control);
+ }
+
+ WireInLoopEnd(loop, eloop, vloop, next_k, control, effect);
+
+ control = if_false;
+ effect = eloop;
+
+ // Wire up the branch for the case when IsCallable fails for the callback.
+ // Since {check_throw} is an unconditional throw, it's impossible to
+ // return a successful completion. Therefore, we simply connect the successful
+ // completion to the graph end.
+ Node* throw_node =
+ graph()->NewNode(common()->Throw(), check_throw, check_fail);
+ NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
+
+ ReplaceWithValue(node, jsgraph()->UndefinedConstant(), effect, control);
+ return Replace(jsgraph()->UndefinedConstant());
+}
+
+Reduction JSCallReducer::ReduceArrayReduce(Handle<JSFunction> function,
+ Node* node) {
+ if (!FLAG_turbo_inline_array_builtins) return NoChange();
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+
+ Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
+ Node* effect = NodeProperties::GetEffectInput(node);
+ Node* control = NodeProperties::GetControlInput(node);
+ Node* context = NodeProperties::GetContextInput(node);
+ CallParameters const& p = CallParametersOf(node->op());
+ if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
return NoChange();
}
- if (receiver_maps.size() == 0) return NoChange();
+
+ // Try to determine the {receiver} map.
+ Node* receiver = NodeProperties::GetValueInput(node, 1);
+ Node* fncallback = node->op()->ValueInputCount() > 2
+ ? NodeProperties::GetValueInput(node, 2)
+ : jsgraph()->UndefinedConstant();
+
+ ZoneHandleSet<Map> receiver_maps;
+ NodeProperties::InferReceiverMapsResult result =
+ NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
+ if (result == NodeProperties::kNoReceiverMaps) return NoChange();
ElementsKind kind = IsDoubleElementsKind(receiver_maps[0]->elements_kind())
? PACKED_DOUBLE_ELEMENTS
@@ -838,8 +1031,7 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
if (!CanInlineArrayIteratingBuiltin(receiver_map)) {
return NoChange();
}
- if (!IsFastElementsKind(next_kind) ||
- (IsDoubleElementsKind(next_kind) && IsHoleyElementsKind(next_kind))) {
+ if (!IsFastElementsKind(next_kind) || IsHoleyElementsKind(next_kind)) {
return NoChange();
}
if (IsDoubleElementsKind(kind) != IsDoubleElementsKind(next_kind)) {
@@ -854,36 +1046,73 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
// global array protector cell.
dependencies()->AssumePropertyCell(factory()->no_elements_protector());
- Node* k = jsgraph()->ZeroConstant();
+ // If we have unreliable maps, we need a map check.
+ if (result == NodeProperties::kUnreliableReceiverMaps) {
+ effect =
+ graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
+ receiver_maps, p.feedback()),
+ receiver, effect, control);
+ }
Node* original_length = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayLength(PACKED_ELEMENTS)),
receiver, effect, control);
- std::vector<Node*> checkpoint_params(
- {receiver, fncallback, this_arg, k, original_length});
+ Node* k = jsgraph()->ZeroConstant();
+
+ std::vector<Node*> checkpoint_params({receiver, fncallback, k,
+ original_length,
+ jsgraph()->UndefinedConstant()});
const int stack_parameters = static_cast<int>(checkpoint_params.size());
// Check whether the given callback function is callable. Note that this has
// to happen outside the loop to make sure we also throw on empty arrays.
Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
- jsgraph(), function, Builtins::kArrayForEachLoopLazyDeoptContinuation,
- node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
+ jsgraph(), function, Builtins::kArrayReduceLoopLazyDeoptContinuation,
+ node->InputAt(0), context, &checkpoint_params[0], stack_parameters - 1,
outer_frame_state, ContinuationFrameStateMode::LAZY);
Node* check_fail = nullptr;
Node* check_throw = nullptr;
WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, effect,
&control, &check_fail, &check_throw);
+ // Set initial accumulator value
+ Node* cur = jsgraph()->TheHoleConstant();
+
+ Node* initial_element_check_fail = nullptr;
+ Node* initial_element_check_throw = nullptr;
+ if (node->op()->ValueInputCount() > 3) {
+ cur = NodeProperties::GetValueInput(node, 3);
+ } else {
+ Node* check =
+ graph()->NewNode(simplified()->NumberEqual(), original_length, k);
+ Node* check_branch =
+ graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
+ initial_element_check_fail =
+ graph()->NewNode(common()->IfTrue(), check_branch);
+ initial_element_check_throw = graph()->NewNode(
+ javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
+ jsgraph()->Constant(MessageTemplate::kReduceNoInitial), fncallback,
+ context, check_frame_state, effect, initial_element_check_fail);
+ control = graph()->NewNode(common()->IfFalse(), check_branch);
+
+ cur = SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());
+ k = graph()->NewNode(simplified()->NumberAdd(), k,
+ jsgraph()->OneConstant());
+ }
+
// Start the loop.
Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
Node* eloop = effect =
graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
NodeProperties::MergeControlToEnd(graph(), common(), terminate);
- Node* vloop = k = graph()->NewNode(
+ Node* kloop = k = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop);
- checkpoint_params[3] = k;
+ Node* curloop = cur = graph()->NewNode(
+ common()->Phi(MachineRepresentation::kTagged, 2), cur, cur, loop);
+ checkpoint_params[2] = k;
+ checkpoint_params[4] = curloop;
control = loop;
effect = eloop;
@@ -898,7 +1127,7 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
control = if_true;
Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
- jsgraph(), function, Builtins::kArrayForEachLoopEagerDeoptContinuation,
+ jsgraph(), function, Builtins::kArrayReduceLoopEagerDeoptContinuation,
node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
outer_frame_state, ContinuationFrameStateMode::EAGER);
@@ -910,11 +1139,12 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
effect, control);
- Node* element = SafeLoadElement(kind, receiver, control, &effect, &k);
+ Node* element =
+ SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());
Node* next_k =
- graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->Constant(1));
- checkpoint_params[3] = next_k;
+ graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());
+ checkpoint_params[2] = next_k;
Node* hole_true = nullptr;
Node* hole_false = nullptr;
@@ -934,18 +1164,19 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
// The contract is that we don't leak "the hole" into "user JavaScript",
// so we must rename the {element} here to explicitly exclude "the hole"
// from the type of {element}.
- element = graph()->NewNode(common()->TypeGuard(Type::NonInternal()),
- element, control);
+ element = effect = graph()->NewNode(
+ common()->TypeGuard(Type::NonInternal()), element, effect, control);
}
frame_state = CreateJavaScriptBuiltinContinuationFrameState(
- jsgraph(), function, Builtins::kArrayForEachLoopLazyDeoptContinuation,
- node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
+ jsgraph(), function, Builtins::kArrayReduceLoopLazyDeoptContinuation,
+ node->InputAt(0), context, &checkpoint_params[0], stack_parameters - 1,
outer_frame_state, ContinuationFrameStateMode::LAZY);
- control = effect = graph()->NewNode(
- javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
- receiver, context, frame_state, effect, control);
+ Node* next_cur = control = effect =
+ graph()->NewNode(javascript()->Call(6, p.frequency()), fncallback,
+ jsgraph()->UndefinedConstant(), cur, element, k,
+ receiver, context, frame_state, effect, control);
// Rewire potential exception edges.
Node* on_exception = nullptr;
@@ -963,12 +1194,17 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
control = graph()->NewNode(common()->Merge(2), control, after_call_control);
effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect,
control);
+ next_cur =
+ graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), cur,
+ next_cur, control);
}
k = next_k;
+ cur = next_cur;
loop->ReplaceInput(1, control);
- vloop->ReplaceInput(1, k);
+ kloop->ReplaceInput(1, k);
+ curloop->ReplaceInput(1, cur);
eloop->ReplaceInput(1, effect);
control = if_false;
@@ -982,19 +1218,271 @@ Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
graph()->NewNode(common()->Throw(), check_throw, check_fail);
NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
- ReplaceWithValue(node, jsgraph()->UndefinedConstant(), effect, control);
- return Replace(jsgraph()->UndefinedConstant());
-}
+ if (node->op()->ValueInputCount() <= 3) {
+ // Wire up the branch for the case when an array is empty.
+ // Since {check_throw} is an unconditional throw, it's impossible to
+ // return a successful completion. Therefore, we simply connect the
+ // successful completion to the graph end.
+ Node* throw_node =
+ graph()->NewNode(common()->Throw(), initial_element_check_throw,
+ initial_element_check_fail);
+ NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
+ }
+
+ ReplaceWithValue(node, curloop, effect, control);
+ return Replace(curloop);
+} // namespace compiler
+
+Reduction JSCallReducer::ReduceArrayReduceRight(Handle<JSFunction> function,
+ Node* node) {
+ if (!FLAG_turbo_inline_array_builtins) return NoChange();
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+
+ Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
+ Node* effect = NodeProperties::GetEffectInput(node);
+ Node* control = NodeProperties::GetControlInput(node);
+ Node* context = NodeProperties::GetContextInput(node);
+ CallParameters const& p = CallParametersOf(node->op());
+ if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
+ return NoChange();
+ }
+
+ // Try to determine the {receiver} map.
+ Node* receiver = NodeProperties::GetValueInput(node, 1);
+ Node* fncallback = node->op()->ValueInputCount() > 2
+ ? NodeProperties::GetValueInput(node, 2)
+ : jsgraph()->UndefinedConstant();
+
+ ZoneHandleSet<Map> receiver_maps;
+ NodeProperties::InferReceiverMapsResult result =
+ NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
+ if (result == NodeProperties::kNoReceiverMaps) return NoChange();
+
+ ElementsKind kind = IsDoubleElementsKind(receiver_maps[0]->elements_kind())
+ ? PACKED_DOUBLE_ELEMENTS
+ : PACKED_ELEMENTS;
+ for (Handle<Map> receiver_map : receiver_maps) {
+ ElementsKind next_kind = receiver_map->elements_kind();
+ if (!CanInlineArrayIteratingBuiltin(receiver_map)) {
+ return NoChange();
+ }
+ if (!IsFastElementsKind(next_kind) || IsHoleyElementsKind(next_kind)) {
+ return NoChange();
+ }
+ if (IsDoubleElementsKind(kind) != IsDoubleElementsKind(next_kind)) {
+ return NoChange();
+ }
+ if (IsHoleyElementsKind(next_kind)) {
+ kind = HOLEY_ELEMENTS;
+ }
+ }
+
+ // Install code dependencies on the {receiver} prototype maps and the
+ // global array protector cell.
+ dependencies()->AssumePropertyCell(factory()->no_elements_protector());
+
+ // If we have unreliable maps, we need a map check.
+ if (result == NodeProperties::kUnreliableReceiverMaps) {
+ effect =
+ graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
+ receiver_maps, p.feedback()),
+ receiver, effect, control);
+ }
+
+ Node* original_length = effect = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForJSArrayLength(PACKED_ELEMENTS)),
+ receiver, effect, control);
+
+ Node* k = graph()->NewNode(simplified()->NumberSubtract(), original_length,
+ jsgraph()->OneConstant());
+
+ std::vector<Node*> checkpoint_params({receiver, fncallback, k,
+ original_length,
+ jsgraph()->UndefinedConstant()});
+ const int stack_parameters = static_cast<int>(checkpoint_params.size());
+
+ // Check whether the given callback function is callable. Note that this has
+ // to happen outside the loop to make sure we also throw on empty arrays.
+ Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
+ jsgraph(), function, Builtins::kArrayReduceRightLoopLazyDeoptContinuation,
+ node->InputAt(0), context, &checkpoint_params[0], stack_parameters - 1,
+ outer_frame_state, ContinuationFrameStateMode::LAZY);
+ Node* check_fail = nullptr;
+ Node* check_throw = nullptr;
+ WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, effect,
+ &control, &check_fail, &check_throw);
+
+ // Set initial accumulator value
+ Node* cur = nullptr;
+
+ Node* initial_element_check_fail = nullptr;
+ Node* initial_element_check_throw = nullptr;
+ if (node->op()->ValueInputCount() > 3) {
+ cur = NodeProperties::GetValueInput(node, 3);
+ } else {
+ Node* check = graph()->NewNode(simplified()->NumberEqual(), original_length,
+ jsgraph()->SmiConstant(0));
+ Node* check_branch =
+ graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
+ initial_element_check_fail =
+ graph()->NewNode(common()->IfTrue(), check_branch);
+ initial_element_check_throw = graph()->NewNode(
+ javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
+ jsgraph()->Constant(MessageTemplate::kReduceNoInitial), fncallback,
+ context, check_frame_state, effect, initial_element_check_fail);
+ control = graph()->NewNode(common()->IfFalse(), check_branch);
+
+ cur = SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());
+ k = graph()->NewNode(simplified()->NumberSubtract(), k,
+ jsgraph()->OneConstant());
+ }
+
+ // Start the loop.
+ Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
+ Node* eloop = effect =
+ graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
+ Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
+ NodeProperties::MergeControlToEnd(graph(), common(), terminate);
+ Node* kloop = k = graph()->NewNode(
+ common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop);
+ Node* curloop = cur = graph()->NewNode(
+ common()->Phi(MachineRepresentation::kTagged, 2), cur, cur, loop);
+ checkpoint_params[2] = k;
+ checkpoint_params[4] = curloop;
+
+ control = loop;
+ effect = eloop;
+
+ Node* continue_test = graph()->NewNode(simplified()->NumberLessThanOrEqual(),
+ jsgraph()->ZeroConstant(), k);
+ Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
+ continue_test, control);
+
+ Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
+ Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
+ control = if_true;
+
+ Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
+ jsgraph(), function,
+ Builtins::kArrayReduceRightLoopEagerDeoptContinuation, node->InputAt(0),
+ context, &checkpoint_params[0], stack_parameters, outer_frame_state,
+ ContinuationFrameStateMode::EAGER);
+
+ effect =
+ graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
+
+ // Make sure the map hasn't changed during the iteration
+ effect = graph()->NewNode(
+ simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
+ effect, control);
+
+ Node* element =
+ SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());
+
+ Node* next_k = graph()->NewNode(simplified()->NumberSubtract(), k,
+ jsgraph()->OneConstant());
+ checkpoint_params[2] = next_k;
+
+ Node* hole_true = nullptr;
+ Node* hole_false = nullptr;
+ Node* effect_true = effect;
+
+ if (IsHoleyElementsKind(kind)) {
+ // Holey elements kind require a hole check and skipping of the element in
+ // the case of a hole.
+ Node* check = graph()->NewNode(simplified()->ReferenceEqual(), element,
+ jsgraph()->TheHoleConstant());
+ Node* branch =
+ graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
+ hole_true = graph()->NewNode(common()->IfTrue(), branch);
+ hole_false = graph()->NewNode(common()->IfFalse(), branch);
+ control = hole_false;
+
+ // The contract is that we don't leak "the hole" into "user JavaScript",
+ // so we must rename the {element} here to explicitly exclude "the hole"
+ // from the type of {element}.
+ element = effect = graph()->NewNode(
+ common()->TypeGuard(Type::NonInternal()), element, effect, control);
+ }
+
+ frame_state = CreateJavaScriptBuiltinContinuationFrameState(
+ jsgraph(), function, Builtins::kArrayReduceRightLoopLazyDeoptContinuation,
+ node->InputAt(0), context, &checkpoint_params[0], stack_parameters - 1,
+ outer_frame_state, ContinuationFrameStateMode::LAZY);
+
+ Node* next_cur = control = effect =
+ graph()->NewNode(javascript()->Call(6, p.frequency()), fncallback,
+ jsgraph()->UndefinedConstant(), cur, element, k,
+ receiver, context, frame_state, effect, control);
+
+ // Rewire potential exception edges.
+ Node* on_exception = nullptr;
+ if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
+ RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
+ &check_fail, &control);
+ }
+
+ if (IsHoleyElementsKind(kind)) {
+ Node* after_call_control = control;
+ Node* after_call_effect = effect;
+ control = hole_true;
+ effect = effect_true;
+
+ control = graph()->NewNode(common()->Merge(2), control, after_call_control);
+ effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect,
+ control);
+ next_cur =
+ graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), cur,
+ next_cur, control);
+ }
+
+ k = next_k;
+ cur = next_cur;
+
+ loop->ReplaceInput(1, control);
+ kloop->ReplaceInput(1, k);
+ curloop->ReplaceInput(1, cur);
+ eloop->ReplaceInput(1, effect);
+
+ control = if_false;
+ effect = eloop;
+
+ // Wire up the branch for the case when IsCallable fails for the callback.
+ // Since {check_throw} is an unconditional throw, it's impossible to
+ // return a successful completion. Therefore, we simply connect the successful
+ // completion to the graph end.
+ Node* throw_node =
+ graph()->NewNode(common()->Throw(), check_throw, check_fail);
+ NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
+
+ if (node->op()->ValueInputCount() <= 3) {
+ // Wire up the branch for the case when an array is empty.
+ // Since {check_throw} is an unconditional throw, it's impossible to
+ // return a successful completion. Therefore, we simply connect the
+ // successful completion to the graph end.
+ Node* throw_node =
+ graph()->NewNode(common()->Throw(), initial_element_check_throw,
+ initial_element_check_fail);
+ NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
+ }
+
+ ReplaceWithValue(node, curloop, effect, control);
+ return Replace(curloop);
+} // namespace compiler
Reduction JSCallReducer::ReduceArrayMap(Handle<JSFunction> function,
Node* node) {
if (!FLAG_turbo_inline_array_builtins) return NoChange();
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ CallParameters const& p = CallParametersOf(node->op());
+ if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
+ return NoChange();
+ }
+
Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* context = NodeProperties::GetContextInput(node);
- CallParameters const& p = CallParametersOf(node->op());
// Try to determine the {receiver} map.
Node* receiver = NodeProperties::GetValueInput(node, 1);
@@ -1007,31 +1495,18 @@ Reduction JSCallReducer::ReduceArrayMap(Handle<JSFunction> function,
ZoneHandleSet<Map> receiver_maps;
NodeProperties::InferReceiverMapsResult result =
NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
- if (result != NodeProperties::kReliableReceiverMaps) {
- return NoChange();
- }
+ if (result == NodeProperties::kNoReceiverMaps) return NoChange();
// Ensure that any changes to the Array species constructor cause deopt.
if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange();
- if (receiver_maps.size() == 0) return NoChange();
-
const ElementsKind kind = receiver_maps[0]->elements_kind();
- // TODO(danno): Handle holey elements kinds.
- if (!IsFastPackedElementsKind(kind)) {
- return NoChange();
- }
-
for (Handle<Map> receiver_map : receiver_maps) {
- if (!CanInlineArrayIteratingBuiltin(receiver_map)) {
- return NoChange();
- }
+ if (!CanInlineArrayIteratingBuiltin(receiver_map)) return NoChange();
// We can handle different maps, as long as their elements kind are the
// same.
- if (receiver_map->elements_kind() != kind) {
- return NoChange();
- }
+ if (receiver_map->elements_kind() != kind) return NoChange();
}
dependencies()->AssumePropertyCell(factory()->species_protector());
@@ -1045,10 +1520,13 @@ Reduction JSCallReducer::ReduceArrayMap(Handle<JSFunction> function,
Node* k = jsgraph()->ZeroConstant();
- // Make sure the map hasn't changed before we construct the output array.
- effect = graph()->NewNode(
- simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
- effect, control);
+ // If we have unreliable maps, we need a map check.
+ if (result == NodeProperties::kUnreliableReceiverMaps) {
+ effect =
+ graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
+ receiver_maps, p.feedback()),
+ receiver, effect, control);
+ }
Node* original_length = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
@@ -1078,18 +1556,10 @@ Reduction JSCallReducer::ReduceArrayMap(Handle<JSFunction> function,
&control, &check_fail, &check_throw);
// Start the loop.
- Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
- Node* eloop = effect =
- graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
- Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
- NodeProperties::MergeControlToEnd(graph(), common(), terminate);
- Node* vloop = k = graph()->NewNode(
- common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop);
+ Node* vloop = k = WireInLoopStart(k, &control, &effect);
+ Node *loop = control, *eloop = effect;
checkpoint_params[4] = k;
- control = loop;
- effect = eloop;
-
Node* continue_test =
graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
@@ -1108,15 +1578,44 @@ Reduction JSCallReducer::ReduceArrayMap(Handle<JSFunction> function,
graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
// Make sure the map hasn't changed during the iteration
- effect = graph()->NewNode(
- simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
- effect, control);
+ effect =
+ graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
+ receiver_maps, p.feedback()),
+ receiver, effect, control);
- Node* element = SafeLoadElement(kind, receiver, control, &effect, &k);
+ Node* element =
+ SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());
Node* next_k =
graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());
+ Node* hole_true = nullptr;
+ Node* hole_false = nullptr;
+ Node* effect_true = effect;
+
+ if (IsHoleyElementsKind(kind)) {
+ // Holey elements kind require a hole check and skipping of the element in
+ // the case of a hole.
+ Node* check;
+ if (IsDoubleElementsKind(kind)) {
+ check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
+ } else {
+ check = graph()->NewNode(simplified()->ReferenceEqual(), element,
+ jsgraph()->TheHoleConstant());
+ }
+ Node* branch =
+ graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
+ hole_true = graph()->NewNode(common()->IfTrue(), branch);
+ hole_false = graph()->NewNode(common()->IfFalse(), branch);
+ control = hole_false;
+
+ // The contract is that we don't leak "the hole" into "user JavaScript",
+ // so we must rename the {element} here to explicitly exclude "the hole"
+ // from the type of {element}.
+ element = effect = graph()->NewNode(
+ common()->TypeGuard(Type::NonInternal()), element, effect, control);
+ }
+
// This frame state is dealt with by hand in
// ArrayMapLoopLazyDeoptContinuation.
frame_state = CreateJavaScriptBuiltinContinuationFrameState(
@@ -1143,11 +1642,19 @@ Reduction JSCallReducer::ReduceArrayMap(Handle<JSFunction> function,
simplified()->TransitionAndStoreElement(double_map, fast_map), a, k,
callback_value, effect, control);
- k = next_k;
+ if (IsHoleyElementsKind(kind)) {
+ Node* after_call_and_store_control = control;
+ Node* after_call_and_store_effect = effect;
+ control = hole_true;
+ effect = effect_true;
- loop->ReplaceInput(1, control);
- vloop->ReplaceInput(1, k);
- eloop->ReplaceInput(1, effect);
+ control = graph()->NewNode(common()->Merge(2), control,
+ after_call_and_store_control);
+ effect = graph()->NewNode(common()->EffectPhi(2), effect,
+ after_call_and_store_effect, control);
+ }
+
+ WireInLoopEnd(loop, eloop, vloop, next_k, control, effect);
control = if_false;
effect = eloop;
@@ -1168,11 +1675,15 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function,
Node* node) {
if (!FLAG_turbo_inline_array_builtins) return NoChange();
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ CallParameters const& p = CallParametersOf(node->op());
+ if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
+ return NoChange();
+ }
+
Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* context = NodeProperties::GetContextInput(node);
- CallParameters const& p = CallParametersOf(node->op());
// Try to determine the {receiver} map.
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* fncallback = node->op()->ValueInputCount() > 2
@@ -1184,21 +1695,14 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function,
ZoneHandleSet<Map> receiver_maps;
NodeProperties::InferReceiverMapsResult result =
NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
- if (result != NodeProperties::kReliableReceiverMaps) {
- return NoChange();
- }
+ if (result == NodeProperties::kNoReceiverMaps) return NoChange();
// And ensure that any changes to the Array species constructor cause deopt.
if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange();
- if (receiver_maps.size() == 0) return NoChange();
-
const ElementsKind kind = receiver_maps[0]->elements_kind();
-
- // TODO(danno): Handle holey elements kinds.
- if (!IsFastPackedElementsKind(kind)) {
- return NoChange();
- }
+ // The output array is packed (filter doesn't visit holes).
+ const ElementsKind packed_kind = GetPackedElementsKind(kind);
for (Handle<Map> receiver_map : receiver_maps) {
if (!CanInlineArrayIteratingBuiltin(receiver_map)) {
@@ -1206,23 +1710,24 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function,
}
// We can handle different maps, as long as their elements kind are the
// same.
- if (receiver_map->elements_kind() != kind) {
- return NoChange();
- }
+ if (receiver_map->elements_kind() != kind) return NoChange();
}
dependencies()->AssumePropertyCell(factory()->species_protector());
Handle<Map> initial_map(
- Map::cast(native_context()->GetInitialJSArrayMap(kind)));
+ Map::cast(native_context()->GetInitialJSArrayMap(packed_kind)));
Node* k = jsgraph()->ZeroConstant();
Node* to = jsgraph()->ZeroConstant();
- // Make sure the map hasn't changed before we construct the output array.
- effect = graph()->NewNode(
- simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
- effect, control);
+ // If we have unreliable maps, we need a map check.
+ if (result == NodeProperties::kUnreliableReceiverMaps) {
+ effect =
+ graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
+ receiver_maps, p.feedback()),
+ receiver, effect, control);
+ }
Node* a; // Construct the output array.
{
@@ -1232,7 +1737,8 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function,
Node* empty_fixed_array = jsgraph()->EmptyFixedArrayConstant();
ab.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), empty_fixed_array);
ab.Store(AccessBuilder::ForJSObjectElements(), empty_fixed_array);
- ab.Store(AccessBuilder::ForJSArrayLength(kind), jsgraph()->ZeroConstant());
+ ab.Store(AccessBuilder::ForJSArrayLength(packed_kind),
+ jsgraph()->ZeroConstant());
for (int i = 0; i < initial_map->GetInObjectProperties(); ++i) {
ab.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i),
jsgraph()->UndefinedConstant());
@@ -1268,19 +1774,11 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function,
}
// Start the loop.
- Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
- Node* eloop = effect =
- graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
- Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
- NodeProperties::MergeControlToEnd(graph(), common(), terminate);
- Node* vloop = k = graph()->NewNode(
- common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop);
+ Node* vloop = k = WireInLoopStart(k, &control, &effect);
+ Node *loop = control, *eloop = effect;
Node* v_to_loop = to = graph()->NewNode(
common()->Phi(MachineRepresentation::kTaggedSigned, 2), to, to, loop);
- control = loop;
- effect = eloop;
-
Node* continue_test =
graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
@@ -1305,15 +1803,45 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function,
}
// Make sure the map hasn't changed during the iteration.
- effect = graph()->NewNode(
- simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
- effect, control);
+ effect =
+ graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
+ receiver_maps, p.feedback()),
+ receiver, effect, control);
- Node* element = SafeLoadElement(kind, receiver, control, &effect, &k);
+ Node* element =
+ SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());
Node* next_k =
graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());
+ Node* hole_true = nullptr;
+ Node* hole_false = nullptr;
+ Node* effect_true = effect;
+ Node* hole_true_vto = to;
+
+ if (IsHoleyElementsKind(kind)) {
+ // Holey elements kind require a hole check and skipping of the element in
+ // the case of a hole.
+ Node* check;
+ if (IsDoubleElementsKind(kind)) {
+ check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
+ } else {
+ check = graph()->NewNode(simplified()->ReferenceEqual(), element,
+ jsgraph()->TheHoleConstant());
+ }
+ Node* branch =
+ graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
+ hole_true = graph()->NewNode(common()->IfTrue(), branch);
+ hole_false = graph()->NewNode(common()->IfFalse(), branch);
+ control = hole_false;
+
+ // The contract is that we don't leak "the hole" into "user JavaScript",
+ // so we must rename the {element} here to explicitly exclude "the hole"
+ // from the type of {element}.
+ element = effect = graph()->NewNode(
+ common()->TypeGuard(Type::NonInternal()), element, effect, control);
+ }
+
Node* callback_value = nullptr;
{
// This frame state is dealt with by hand in
@@ -1363,14 +1891,25 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function,
// We have to coerce callback_value to boolean, and only store the element in
// a if it's true. The checkpoint above protects against the case that
// growing {a} fails.
- to = DoFilterPostCallbackWork(kind, &control, &effect, a, to, element,
+ to = DoFilterPostCallbackWork(packed_kind, &control, &effect, a, to, element,
callback_value);
- k = next_k;
- loop->ReplaceInput(1, control);
- vloop->ReplaceInput(1, k);
+ if (IsHoleyElementsKind(kind)) {
+ Node* after_call_control = control;
+ Node* after_call_effect = effect;
+ control = hole_true;
+ effect = effect_true;
+
+ control = graph()->NewNode(common()->Merge(2), control, after_call_control);
+ effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect,
+ control);
+ to =
+ graph()->NewNode(common()->Phi(MachineRepresentation::kTaggedSigned, 2),
+ hole_true_vto, to, control);
+ }
+
+ WireInLoopEnd(loop, eloop, vloop, next_k, control, effect);
v_to_loop->ReplaceInput(1, to);
- eloop->ReplaceInput(1, effect);
control = if_false;
effect = eloop;
@@ -1387,6 +1926,216 @@ Reduction JSCallReducer::ReduceArrayFilter(Handle<JSFunction> function,
return Replace(a);
}
+Reduction JSCallReducer::ReduceArrayFind(ArrayFindVariant variant,
+ Handle<JSFunction> function,
+ Node* node) {
+ if (!FLAG_turbo_inline_array_builtins) return NoChange();
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ CallParameters const& p = CallParametersOf(node->op());
+ if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
+ 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);
+ Node* context = NodeProperties::GetContextInput(node);
+
+ // Try to determine the {receiver} map.
+ Node* receiver = NodeProperties::GetValueInput(node, 1);
+ Node* fncallback = node->op()->ValueInputCount() > 2
+ ? NodeProperties::GetValueInput(node, 2)
+ : jsgraph()->UndefinedConstant();
+ Node* this_arg = node->op()->ValueInputCount() > 3
+ ? NodeProperties::GetValueInput(node, 3)
+ : jsgraph()->UndefinedConstant();
+ ZoneHandleSet<Map> receiver_maps;
+ NodeProperties::InferReceiverMapsResult result =
+ NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
+ if (result == NodeProperties::kNoReceiverMaps) return NoChange();
+
+ const ElementsKind kind = receiver_maps[0]->elements_kind();
+
+ // TODO(pwong): Handle holey double elements kinds.
+ if (IsDoubleElementsKind(kind) && IsHoleyElementsKind(kind)) {
+ return NoChange();
+ }
+
+ for (Handle<Map> receiver_map : receiver_maps) {
+ if (!CanInlineArrayIteratingBuiltin(receiver_map)) return NoChange();
+ // We can handle different maps, as long as their elements kind are the
+ // same.
+ if (receiver_map->elements_kind() != kind) return NoChange();
+ }
+
+ // Install code dependencies on the {receiver} prototype maps and the
+ // global array protector cell.
+ dependencies()->AssumePropertyCell(factory()->no_elements_protector());
+
+ // If we have unreliable maps, we need a map check.
+ if (result == NodeProperties::kUnreliableReceiverMaps) {
+ effect =
+ graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
+ receiver_maps, p.feedback()),
+ receiver, effect, control);
+ }
+
+ Node* k = jsgraph()->ZeroConstant();
+
+ Node* original_length = effect = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
+ effect, control);
+
+ std::vector<Node*> checkpoint_params(
+ {receiver, fncallback, this_arg, k, original_length});
+ const int stack_parameters = static_cast<int>(checkpoint_params.size());
+
+ // Check whether the given callback function is callable. Note that this has
+ // to happen outside the loop to make sure we also throw on empty arrays.
+ Node* check_fail = nullptr;
+ Node* check_throw = nullptr;
+ {
+ Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
+ 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);
+ }
+
+ // Start the loop.
+ Node* vloop = k = WireInLoopStart(k, &control, &effect);
+ Node *loop = control, *eloop = effect;
+ checkpoint_params[3] = k;
+
+ // Check if we've iterated past the last element of the array.
+ Node* if_false = nullptr;
+ {
+ Node* continue_test =
+ graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
+ Node* continue_branch = graph()->NewNode(
+ common()->Branch(BranchHint::kTrue), continue_test, control);
+ control = graph()->NewNode(common()->IfTrue(), continue_branch);
+ if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
+ }
+
+ // Check the map hasn't changed during the iteration.
+ {
+ Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
+ 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);
+
+ effect =
+ graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
+ receiver_maps, p.feedback()),
+ receiver, effect, control);
+ }
+
+ // Load k-th element from receiver.
+ Node* element =
+ SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());
+
+ // Increment k for the next iteration.
+ Node* next_k = checkpoint_params[3] =
+ graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());
+
+ // Replace holes with undefined.
+ if (IsHoleyElementsKind(kind)) {
+ element = graph()->NewNode(
+ common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
+ graph()->NewNode(simplified()->ReferenceEqual(), element,
+ jsgraph()->TheHoleConstant()),
+ 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,
+ if_found_return_value});
+ const int call_stack_parameters =
+ static_cast<int>(call_checkpoint_params.size());
+
+ Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
+ jsgraph(), function, after_callback_lazy_continuation_builtin,
+ node->InputAt(0), context, &call_checkpoint_params[0],
+ call_stack_parameters, outer_frame_state,
+ ContinuationFrameStateMode::LAZY);
+
+ callback_value = control = effect = graph()->NewNode(
+ javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
+ receiver, context, frame_state, effect, control);
+ }
+
+ // Rewire potential exception edges.
+ Node* on_exception = nullptr;
+ if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
+ RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
+ &check_fail, &control);
+ }
+
+ // Check whether the given callback function returned a truthy value.
+ Node* boolean_result =
+ graph()->NewNode(simplified()->ToBoolean(), callback_value);
+ Node* efound_branch = effect;
+ Node* found_branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
+ boolean_result, control);
+ Node* if_found = graph()->NewNode(common()->IfTrue(), found_branch);
+ Node* if_notfound = graph()->NewNode(common()->IfFalse(), found_branch);
+ control = if_notfound;
+
+ // Close the loop.
+ WireInLoopEnd(loop, eloop, vloop, next_k, control, effect);
+
+ 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),
+ 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
+ // return a successful completion. Therefore, we simply connect the successful
+ // completion to the graph end.
+ Node* throw_node =
+ graph()->NewNode(common()->Throw(), check_throw, check_fail);
+ NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
+
+ ReplaceWithValue(node, return_value, effect, control);
+ return Replace(return_value);
+}
+
Node* JSCallReducer::DoFilterPostCallbackWork(ElementsKind kind, Node** control,
Node** effect, Node* a, Node* to,
Node* element,
@@ -1411,8 +2160,8 @@ Node* JSCallReducer::DoFilterPostCallbackWork(ElementsKind kind, Node** control,
// We know that {to} is in Unsigned31 range here, being smaller than
// {original_length} at all times.
- Node* checked_to =
- graph()->NewNode(common()->TypeGuard(Type::Unsigned31()), to, if_true);
+ Node* checked_to = etrue = graph()->NewNode(
+ common()->TypeGuard(Type::Unsigned31()), to, etrue, if_true);
Node* elements_length = etrue = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), elements,
etrue, if_true);
@@ -1420,9 +2169,9 @@ Node* JSCallReducer::DoFilterPostCallbackWork(ElementsKind kind, Node** control,
GrowFastElementsMode mode =
IsDoubleElementsKind(kind) ? GrowFastElementsMode::kDoubleElements
: GrowFastElementsMode::kSmiOrObjectElements;
- elements = etrue =
- graph()->NewNode(simplified()->MaybeGrowFastElements(mode), a, elements,
- checked_to, elements_length, etrue, if_true);
+ elements = etrue = graph()->NewNode(
+ simplified()->MaybeGrowFastElements(mode, VectorSlotPair()), a,
+ elements, checked_to, elements_length, etrue, if_true);
// Update the length of {a}.
Node* new_length_a = graph()->NewNode(simplified()->NumberAdd(), checked_to,
@@ -1489,14 +2238,15 @@ void JSCallReducer::RewirePostCallbackExceptionEdges(Node* check_throw,
}
Node* JSCallReducer::SafeLoadElement(ElementsKind kind, Node* receiver,
- Node* control, Node** effect, Node** k) {
+ Node* control, Node** effect, Node** k,
+ const VectorSlotPair& feedback) {
// Make sure that the access is still in bounds, since the callback could have
// changed the array's size.
Node* length = *effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
*effect, control);
- *k = *effect = graph()->NewNode(simplified()->CheckBounds(), *k, length,
- *effect, control);
+ *k = *effect = graph()->NewNode(simplified()->CheckBounds(feedback), *k,
+ length, *effect, control);
// Reload the elements pointer before calling the callback, since the previous
// callback might have resized the array causing the elements buffer to be
@@ -1514,6 +2264,455 @@ Node* JSCallReducer::SafeLoadElement(ElementsKind kind, Node* receiver,
return element;
}
+Reduction JSCallReducer::ReduceArrayEvery(Handle<JSFunction> function,
+ Node* node) {
+ if (!FLAG_turbo_inline_array_builtins) return NoChange();
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ CallParameters const& p = CallParametersOf(node->op());
+ if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
+ return NoChange();
+ }
+
+ Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
+ Node* effect = NodeProperties::GetEffectInput(node);
+ Node* control = NodeProperties::GetControlInput(node);
+ Node* context = NodeProperties::GetContextInput(node);
+ // Try to determine the {receiver} map.
+ Node* receiver = NodeProperties::GetValueInput(node, 1);
+ Node* fncallback = node->op()->ValueInputCount() > 2
+ ? NodeProperties::GetValueInput(node, 2)
+ : jsgraph()->UndefinedConstant();
+ Node* this_arg = node->op()->ValueInputCount() > 3
+ ? NodeProperties::GetValueInput(node, 3)
+ : jsgraph()->UndefinedConstant();
+ ZoneHandleSet<Map> receiver_maps;
+ NodeProperties::InferReceiverMapsResult result =
+ NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
+ if (result == NodeProperties::kNoReceiverMaps) return NoChange();
+
+ // And ensure that any changes to the Array species constructor cause deopt.
+ if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange();
+
+ const ElementsKind kind = receiver_maps[0]->elements_kind();
+
+ for (Handle<Map> receiver_map : receiver_maps) {
+ if (!CanInlineArrayIteratingBuiltin(receiver_map)) return NoChange();
+ // We can handle different maps, as long as their elements kind are the
+ // same.
+ if (receiver_map->elements_kind() != kind) return NoChange();
+ }
+
+ dependencies()->AssumePropertyCell(factory()->species_protector());
+
+ // If we have unreliable maps, we need a map check.
+ if (result == NodeProperties::kUnreliableReceiverMaps) {
+ effect =
+ graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
+ receiver_maps, p.feedback()),
+ receiver, effect, control);
+ }
+
+ Node* k = jsgraph()->ZeroConstant();
+
+ // Make sure the map hasn't changed before we construct the output array.
+ effect = graph()->NewNode(
+ simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
+ effect, control);
+
+ Node* original_length = effect = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
+ effect, control);
+
+ // Check whether the given callback function is callable. Note that this has
+ // to happen outside the loop to make sure we also throw on empty arrays.
+ Node* check_fail = nullptr;
+ Node* check_throw = nullptr;
+ {
+ // This frame state doesn't ever call the deopt continuation, it's only
+ // necessary to specifiy a continuation in order to handle the exceptional
+ // case.
+ std::vector<Node*> checkpoint_params(
+ {receiver, fncallback, this_arg, k, original_length});
+ const int stack_parameters = static_cast<int>(checkpoint_params.size());
+
+ Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
+ jsgraph(), function, Builtins::kArrayEveryLoopLazyDeoptContinuation,
+ node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
+ outer_frame_state, ContinuationFrameStateMode::LAZY);
+ WireInCallbackIsCallableCheck(fncallback, context, check_frame_state,
+ effect, &control, &check_fail, &check_throw);
+ }
+
+ // Start the loop.
+ Node* vloop = k = WireInLoopStart(k, &control, &effect);
+ Node *loop = control, *eloop = effect;
+
+ Node* continue_test =
+ graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
+ Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
+ continue_test, control);
+
+ Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
+ Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
+ control = if_true;
+
+ {
+ std::vector<Node*> checkpoint_params(
+ {receiver, fncallback, this_arg, k, original_length});
+ const int stack_parameters = static_cast<int>(checkpoint_params.size());
+
+ Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
+ jsgraph(), function, Builtins::kArrayEveryLoopEagerDeoptContinuation,
+ node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
+ outer_frame_state, ContinuationFrameStateMode::EAGER);
+
+ effect =
+ graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
+ }
+
+ // Make sure the map hasn't changed during the iteration.
+ effect =
+ graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
+ receiver_maps, p.feedback()),
+ receiver, effect, control);
+
+ Node* element =
+ SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());
+
+ Node* next_k =
+ graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());
+
+ Node* hole_true = nullptr;
+ Node* hole_false = nullptr;
+ Node* effect_true = effect;
+
+ if (IsHoleyElementsKind(kind)) {
+ // Holey elements kind require a hole check and skipping of the element in
+ // the case of a hole.
+ Node* check;
+ if (IsDoubleElementsKind(kind)) {
+ check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
+ } else {
+ check = graph()->NewNode(simplified()->ReferenceEqual(), element,
+ jsgraph()->TheHoleConstant());
+ }
+ Node* branch =
+ graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
+ hole_true = graph()->NewNode(common()->IfTrue(), branch);
+ hole_false = graph()->NewNode(common()->IfFalse(), branch);
+ control = hole_false;
+
+ // The contract is that we don't leak "the hole" into "user JavaScript",
+ // so we must rename the {element} here to explicitly exclude "the hole"
+ // from the type of {element}.
+ element = effect = graph()->NewNode(
+ common()->TypeGuard(Type::NonInternal()), element, effect, control);
+ }
+
+ Node* callback_value = nullptr;
+ {
+ // This frame state is dealt with by hand in
+ // Builtins::kArrayEveryLoopLazyDeoptContinuation.
+ std::vector<Node*> checkpoint_params(
+ {receiver, fncallback, this_arg, k, original_length});
+ const int stack_parameters = static_cast<int>(checkpoint_params.size());
+
+ Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
+ jsgraph(), function, Builtins::kArrayEveryLoopLazyDeoptContinuation,
+ node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
+ outer_frame_state, ContinuationFrameStateMode::LAZY);
+
+ callback_value = control = effect = graph()->NewNode(
+ javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
+ receiver, context, frame_state, effect, control);
+ }
+
+ // Rewire potential exception edges.
+ Node* on_exception = nullptr;
+ if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
+ RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
+ &check_fail, &control);
+ }
+
+ // We have to coerce callback_value to boolean.
+ Node* if_false_callback;
+ Node* efalse_callback;
+ {
+ Node* boolean_result =
+ graph()->NewNode(simplified()->ToBoolean(), callback_value);
+ Node* check_boolean_result =
+ graph()->NewNode(simplified()->ReferenceEqual(), boolean_result,
+ jsgraph()->TrueConstant());
+ Node* boolean_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
+ check_boolean_result, control);
+ if_false_callback = graph()->NewNode(common()->IfFalse(), boolean_branch);
+ efalse_callback = effect;
+
+ // Nothing to do in the true case.
+ control = graph()->NewNode(common()->IfTrue(), boolean_branch);
+ }
+
+ if (IsHoleyElementsKind(kind)) {
+ Node* after_call_control = control;
+ Node* after_call_effect = effect;
+ control = hole_true;
+ effect = effect_true;
+
+ control = graph()->NewNode(common()->Merge(2), control, after_call_control);
+ effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect,
+ control);
+ }
+
+ WireInLoopEnd(loop, eloop, vloop, next_k, control, effect);
+
+ control = graph()->NewNode(common()->Merge(2), if_false, if_false_callback);
+ effect =
+ graph()->NewNode(common()->EffectPhi(2), eloop, efalse_callback, control);
+ Node* return_value = graph()->NewNode(
+ common()->Phi(MachineRepresentation::kTagged, 2),
+ jsgraph()->TrueConstant(), jsgraph()->FalseConstant(), 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
+ // return a successful completion. Therefore, we simply connect the successful
+ // completion to the graph end.
+ Node* throw_node =
+ graph()->NewNode(common()->Throw(), check_throw, check_fail);
+ NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
+
+ ReplaceWithValue(node, return_value, effect, control);
+ return Replace(return_value);
+}
+
+Reduction JSCallReducer::ReduceArraySome(Handle<JSFunction> function,
+ Node* node) {
+ if (!FLAG_turbo_inline_array_builtins) return NoChange();
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ CallParameters const& p = CallParametersOf(node->op());
+ if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
+ return NoChange();
+ }
+
+ Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
+ Node* effect = NodeProperties::GetEffectInput(node);
+ Node* control = NodeProperties::GetControlInput(node);
+ Node* context = NodeProperties::GetContextInput(node);
+ // Try to determine the {receiver} map.
+ Node* receiver = NodeProperties::GetValueInput(node, 1);
+ Node* fncallback = node->op()->ValueInputCount() > 2
+ ? NodeProperties::GetValueInput(node, 2)
+ : jsgraph()->UndefinedConstant();
+ Node* this_arg = node->op()->ValueInputCount() > 3
+ ? NodeProperties::GetValueInput(node, 3)
+ : jsgraph()->UndefinedConstant();
+ ZoneHandleSet<Map> receiver_maps;
+ NodeProperties::InferReceiverMapsResult result =
+ NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
+ if (result == NodeProperties::kNoReceiverMaps) return NoChange();
+
+ // And ensure that any changes to the Array species constructor cause deopt.
+ if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange();
+
+ if (receiver_maps.size() == 0) return NoChange();
+
+ const ElementsKind kind = receiver_maps[0]->elements_kind();
+
+ // TODO(pwong): Handle holey double elements kinds.
+ if (IsDoubleElementsKind(kind) && IsHoleyElementsKind(kind)) {
+ return NoChange();
+ }
+
+ for (Handle<Map> receiver_map : receiver_maps) {
+ if (!CanInlineArrayIteratingBuiltin(receiver_map)) return NoChange();
+ // We can handle different maps, as long as their elements kind are the
+ // same.
+ if (receiver_map->elements_kind() != kind) return NoChange();
+ }
+
+ dependencies()->AssumePropertyCell(factory()->species_protector());
+
+ Node* k = jsgraph()->ZeroConstant();
+
+ // If we have unreliable maps, we need a map check.
+ if (result == NodeProperties::kUnreliableReceiverMaps) {
+ effect =
+ graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
+ receiver_maps, p.feedback()),
+ receiver, effect, control);
+ }
+
+ // Make sure the map hasn't changed before we construct the output array.
+ effect = graph()->NewNode(
+ simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
+ effect, control);
+
+ Node* original_length = effect = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
+ effect, control);
+
+ // Check whether the given callback function is callable. Note that this has
+ // to happen outside the loop to make sure we also throw on empty arrays.
+ Node* check_fail = nullptr;
+ Node* check_throw = nullptr;
+ {
+ // This frame state doesn't ever call the deopt continuation, it's only
+ // necessary to specifiy a continuation in order to handle the exceptional
+ // case.
+ std::vector<Node*> checkpoint_params(
+ {receiver, fncallback, this_arg, k, original_length});
+ const int stack_parameters = static_cast<int>(checkpoint_params.size());
+
+ Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
+ jsgraph(), function, Builtins::kArraySomeLoopLazyDeoptContinuation,
+ node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
+ outer_frame_state, ContinuationFrameStateMode::LAZY);
+ WireInCallbackIsCallableCheck(fncallback, context, check_frame_state,
+ effect, &control, &check_fail, &check_throw);
+ }
+
+ // Start the loop.
+ Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
+ Node* eloop = effect =
+ graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
+ Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
+ NodeProperties::MergeControlToEnd(graph(), common(), terminate);
+ Node* vloop = k = graph()->NewNode(
+ common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop);
+
+ Node* continue_test =
+ graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
+ Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
+ continue_test, control);
+
+ Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
+ Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
+ control = if_true;
+
+ {
+ std::vector<Node*> checkpoint_params(
+ {receiver, fncallback, this_arg, k, original_length});
+ const int stack_parameters = static_cast<int>(checkpoint_params.size());
+
+ Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
+ jsgraph(), function, Builtins::kArraySomeLoopEagerDeoptContinuation,
+ node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
+ outer_frame_state, ContinuationFrameStateMode::EAGER);
+
+ effect =
+ graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
+ }
+
+ // Make sure the map hasn't changed during the iteration.
+ effect =
+ graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
+ receiver_maps, p.feedback()),
+ receiver, effect, control);
+
+ Node* element =
+ SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());
+
+ Node* next_k =
+ graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());
+
+ Node* hole_true = nullptr;
+ Node* hole_false = nullptr;
+ Node* effect_true = effect;
+
+ if (IsHoleyElementsKind(kind)) {
+ // Holey elements kind require a hole check and skipping of the element in
+ // the case of a hole.
+ Node* check = graph()->NewNode(simplified()->ReferenceEqual(), element,
+ jsgraph()->TheHoleConstant());
+ Node* branch =
+ graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
+ hole_true = graph()->NewNode(common()->IfTrue(), branch);
+ hole_false = graph()->NewNode(common()->IfFalse(), branch);
+ control = hole_false;
+
+ // The contract is that we don't leak "the hole" into "user JavaScript",
+ // so we must rename the {element} here to explicitly exclude "the hole"
+ // from the type of {element}.
+ element = effect = graph()->NewNode(
+ common()->TypeGuard(Type::NonInternal()), element, effect, control);
+ }
+
+ Node* callback_value = nullptr;
+ {
+ // This frame state is dealt with by hand in
+ // Builtins::kArrayEveryLoopLazyDeoptContinuation.
+ std::vector<Node*> checkpoint_params(
+ {receiver, fncallback, this_arg, k, original_length});
+ const int stack_parameters = static_cast<int>(checkpoint_params.size());
+
+ Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
+ jsgraph(), function, Builtins::kArraySomeLoopLazyDeoptContinuation,
+ node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
+ outer_frame_state, ContinuationFrameStateMode::LAZY);
+
+ callback_value = control = effect = graph()->NewNode(
+ javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
+ receiver, context, frame_state, effect, control);
+ }
+
+ // Rewire potential exception edges.
+ Node* on_exception = nullptr;
+ if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
+ RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
+ &check_fail, &control);
+ }
+
+ // We have to coerce callback_value to boolean.
+ Node* if_true_callback;
+ Node* etrue_callback;
+ {
+ Node* boolean_result =
+ graph()->NewNode(simplified()->ToBoolean(), callback_value);
+ Node* check_boolean_result =
+ graph()->NewNode(simplified()->ReferenceEqual(), boolean_result,
+ jsgraph()->TrueConstant());
+ Node* boolean_branch = graph()->NewNode(
+ common()->Branch(BranchHint::kFalse), check_boolean_result, control);
+ if_true_callback = graph()->NewNode(common()->IfTrue(), boolean_branch);
+ etrue_callback = effect;
+
+ // Nothing to do in the false case.
+ control = graph()->NewNode(common()->IfFalse(), boolean_branch);
+ }
+
+ if (IsHoleyElementsKind(kind)) {
+ Node* after_call_control = control;
+ Node* after_call_effect = effect;
+ control = hole_true;
+ effect = effect_true;
+
+ control = graph()->NewNode(common()->Merge(2), control, after_call_control);
+ effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect,
+ control);
+ }
+
+ loop->ReplaceInput(1, control);
+ vloop->ReplaceInput(1, next_k);
+ eloop->ReplaceInput(1, effect);
+
+ control = graph()->NewNode(common()->Merge(2), if_false, if_true_callback);
+ effect =
+ graph()->NewNode(common()->EffectPhi(2), eloop, etrue_callback, control);
+ Node* return_value = graph()->NewNode(
+ common()->Phi(MachineRepresentation::kTagged, 2),
+ jsgraph()->FalseConstant(), jsgraph()->TrueConstant(), 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
+ // return a successful completion. Therefore, we simply connect the successful
+ // completion to the graph end.
+ Node* throw_node =
+ graph()->NewNode(common()->Throw(), check_throw, check_fail);
+ NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
+
+ ReplaceWithValue(node, return_value, effect, control);
+ return Replace(return_value);
+}
+
Reduction JSCallReducer::ReduceCallApiFunction(Node* node,
Handle<JSFunction> function) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
@@ -1911,8 +3110,6 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
return ReduceFunctionPrototypeCall(node);
case Builtins::kFunctionPrototypeHasInstance:
return ReduceFunctionPrototypeHasInstance(node);
- case Builtins::kNumberConstructor:
- return ReduceNumberConstructor(node);
case Builtins::kObjectConstructor:
return ReduceObjectConstructor(node);
case Builtins::kObjectGetPrototypeOf:
@@ -1941,8 +3138,30 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
return ReduceArrayMap(function, node);
case Builtins::kArrayFilter:
return ReduceArrayFilter(function, node);
+ case Builtins::kArrayReduce:
+ return ReduceArrayReduce(function, node);
+ case Builtins::kArrayReduceRight:
+ return ReduceArrayReduceRight(function, node);
+ case Builtins::kArrayPrototypeFind:
+ return ReduceArrayFind(ArrayFindVariant::kFind, function, node);
+ case Builtins::kArrayPrototypeFindIndex:
+ return ReduceArrayFind(ArrayFindVariant::kFindIndex, function, node);
+ case Builtins::kArrayEvery:
+ return ReduceArrayEvery(function, node);
+ case Builtins::kArrayPrototypePush:
+ return ReduceArrayPrototypePush(node);
+ case Builtins::kArrayPrototypePop:
+ return ReduceArrayPrototypePop(node);
+ case Builtins::kArrayPrototypeShift:
+ return ReduceArrayPrototypeShift(node);
case Builtins::kReturnReceiver:
return ReduceReturnReceiver(node);
+ case Builtins::kStringPrototypeIndexOf:
+ return ReduceStringPrototypeIndexOf(function, node);
+ case Builtins::kStringPrototypeCharAt:
+ return ReduceStringPrototypeCharAt(node);
+ case Builtins::kStringPrototypeCharCodeAt:
+ return ReduceStringPrototypeCharCodeAt(node);
default:
break;
}
@@ -2046,9 +3265,9 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
// Check that the {target} is still the {target_function}.
Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
target_function);
- effect =
- graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kNoReason),
- check, effect, control);
+ effect = graph()->NewNode(
+ simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check,
+ effect, control);
// Specialize the JSCall node to the {target_function}.
NodeProperties::ReplaceValueInput(node, target_function, 0);
@@ -2119,9 +3338,9 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
// Check that the {target} is still the {array_function}.
Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
array_function);
- effect =
- graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kNoReason),
- check, effect, control);
+ effect = graph()->NewNode(
+ simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check,
+ effect, control);
// Turn the {node} into a {JSCreateArray} call.
NodeProperties::ReplaceEffectInput(node, effect);
@@ -2142,9 +3361,9 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
// Check that the {new_target} is still the {new_target_feedback}.
Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
new_target, new_target_feedback);
- effect =
- graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kNoReason),
- check, effect, control);
+ effect = graph()->NewNode(
+ simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check,
+ effect, control);
// Specialize the JSConstruct node to the {new_target_feedback}.
NodeProperties::ReplaceValueInput(node, new_target_feedback, arity + 1);
@@ -2297,6 +3516,47 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
return NoChange();
}
+// ES6 String.prototype.indexOf(searchString [, position])
+// #sec-string.prototype.indexof
+Reduction JSCallReducer::ReduceStringPrototypeIndexOf(
+ Handle<JSFunction> function, Node* node) {
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ CallParameters const& p = CallParametersOf(node->op());
+ if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
+ return NoChange();
+ }
+
+ Node* effect = NodeProperties::GetEffectInput(node);
+ Node* control = NodeProperties::GetControlInput(node);
+ if (node->op()->ValueInputCount() >= 3) {
+ Node* receiver = NodeProperties::GetValueInput(node, 1);
+ Node* new_receiver = effect = graph()->NewNode(
+ simplified()->CheckString(p.feedback()), receiver, effect, control);
+
+ Node* search_string = NodeProperties::GetValueInput(node, 2);
+ Node* new_search_string = effect =
+ graph()->NewNode(simplified()->CheckString(p.feedback()), search_string,
+ effect, control);
+
+ Node* new_position = jsgraph()->ZeroConstant();
+ if (node->op()->ValueInputCount() >= 4) {
+ Node* position = NodeProperties::GetValueInput(node, 3);
+ new_position = effect = graph()->NewNode(
+ simplified()->CheckSmi(p.feedback()), position, effect, control);
+ }
+
+ NodeProperties::ReplaceEffectInput(node, effect);
+ RelaxEffectsAndControls(node);
+ node->ReplaceInput(0, new_receiver);
+ node->ReplaceInput(1, new_search_string);
+ node->ReplaceInput(2, new_position);
+ node->TrimInputCount(3);
+ NodeProperties::ChangeOp(node, simplified()->StringIndexOf());
+ return Changed(node);
+ }
+ return NoChange();
+}
+
Reduction JSCallReducer::ReduceJSConstructWithArrayLike(Node* node) {
DCHECK_EQ(IrOpcode::kJSConstructWithArrayLike, node->opcode());
CallFrequency frequency = CallFrequencyOf(node->op());
@@ -2328,9 +3588,9 @@ Reduction JSCallReducer::ReduceSoftDeoptimize(Node* node,
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* frame_state = NodeProperties::FindFrameStateBefore(node);
- Node* deoptimize =
- graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kSoft, reason),
- frame_state, effect, control);
+ Node* deoptimize = graph()->NewNode(
+ common()->Deoptimize(DeoptimizeKind::kSoft, reason, VectorSlotPair()),
+ frame_state, effect, control);
// TODO(bmeurer): This should be on the AdvancedReducer somehow.
NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
Revisit(graph()->end());
@@ -2339,6 +3599,571 @@ Reduction JSCallReducer::ReduceSoftDeoptimize(Node* node,
return Changed(node);
}
+namespace {
+
+// TODO(turbofan): This was copied from Crankshaft, might be too restrictive.
+bool IsReadOnlyLengthDescriptor(Handle<Map> jsarray_map) {
+ DCHECK(!jsarray_map->is_dictionary_map());
+ Isolate* isolate = jsarray_map->GetIsolate();
+ Handle<Name> length_string = isolate->factory()->length_string();
+ DescriptorArray* descriptors = jsarray_map->instance_descriptors();
+ int number =
+ descriptors->SearchWithCache(isolate, *length_string, *jsarray_map);
+ DCHECK_NE(DescriptorArray::kNotFound, number);
+ return descriptors->GetDetails(number).IsReadOnly();
+}
+
+// TODO(turbofan): This was copied from Crankshaft, might be too restrictive.
+bool CanInlineArrayResizeOperation(Handle<Map> receiver_map) {
+ Isolate* const isolate = receiver_map->GetIsolate();
+ if (!receiver_map->prototype()->IsJSArray()) return false;
+ Handle<JSArray> receiver_prototype(JSArray::cast(receiver_map->prototype()),
+ isolate);
+ return receiver_map->instance_type() == JS_ARRAY_TYPE &&
+ IsFastElementsKind(receiver_map->elements_kind()) &&
+ !receiver_map->is_dictionary_map() && receiver_map->is_extensible() &&
+ isolate->IsAnyInitialArrayPrototype(receiver_prototype) &&
+ !IsReadOnlyLengthDescriptor(receiver_map);
+}
+
+} // namespace
+
+// ES6 section 22.1.3.18 Array.prototype.push ( )
+Reduction JSCallReducer::ReduceArrayPrototypePush(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ CallParameters const& p = CallParametersOf(node->op());
+ if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
+ return NoChange();
+ }
+
+ if (!isolate()->IsNoElementsProtectorIntact()) return NoChange();
+
+ int const num_values = node->op()->ValueInputCount() - 2;
+ Node* receiver = NodeProperties::GetValueInput(node, 1);
+ Node* effect = NodeProperties::GetEffectInput(node);
+ Node* control = NodeProperties::GetControlInput(node);
+
+ // Try to determine the {receiver} map(s).
+ ZoneHandleSet<Map> receiver_maps;
+ NodeProperties::InferReceiverMapsResult result =
+ NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
+ if (result == NodeProperties::kNoReceiverMaps) return NoChange();
+ DCHECK_NE(0, receiver_maps.size());
+
+ ElementsKind kind = receiver_maps[0]->elements_kind();
+
+ for (Handle<Map> receiver_map : receiver_maps) {
+ if (!CanInlineArrayResizeOperation(receiver_map)) return NoChange();
+ if (!UnionElementsKindUptoPackedness(&kind, receiver_map->elements_kind()))
+ return NoChange();
+ }
+
+ // Install code dependencies on the {receiver} global array protector cell.
+ dependencies()->AssumePropertyCell(factory()->no_elements_protector());
+
+ // If the {receiver_maps} information is not reliable, we need
+ // to check that the {receiver} still has one of these maps.
+ if (result == NodeProperties::kUnreliableReceiverMaps) {
+ effect =
+ graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
+ receiver_maps, p.feedback()),
+ receiver, effect, control);
+ }
+
+ // Collect the value inputs to push.
+ std::vector<Node*> values(num_values);
+ for (int i = 0; i < num_values; ++i) {
+ values[i] = NodeProperties::GetValueInput(node, 2 + i);
+ }
+
+ for (auto& value : values) {
+ if (IsSmiElementsKind(kind)) {
+ value = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()),
+ value, effect, control);
+ } else if (IsDoubleElementsKind(kind)) {
+ value = effect = graph()->NewNode(simplified()->CheckNumber(p.feedback()),
+ value, effect, control);
+ // Make sure we do not store signaling NaNs into double arrays.
+ value = graph()->NewNode(simplified()->NumberSilenceNaN(), value);
+ }
+ }
+
+ // Load the "length" property of the {receiver}.
+ Node* length = effect = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
+ effect, control);
+ Node* value = length;
+
+ // Check if we have any {values} to push.
+ if (num_values > 0) {
+ // Compute the resulting "length" of the {receiver}.
+ Node* new_length = value = graph()->NewNode(
+ simplified()->NumberAdd(), length, jsgraph()->Constant(num_values));
+
+ // Load the elements backing store of the {receiver}.
+ Node* elements = effect = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
+ effect, control);
+ Node* elements_length = effect = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), elements,
+ effect, control);
+
+ GrowFastElementsMode mode =
+ IsDoubleElementsKind(kind) ? GrowFastElementsMode::kDoubleElements
+ : GrowFastElementsMode::kSmiOrObjectElements;
+ elements = effect = graph()->NewNode(
+ simplified()->MaybeGrowFastElements(mode, p.feedback()), receiver,
+ elements,
+ graph()->NewNode(simplified()->NumberAdd(), length,
+ jsgraph()->Constant(num_values - 1)),
+ elements_length, effect, control);
+
+ // Update the JSArray::length field. Since this is observable,
+ // there must be no other check after this.
+ effect = graph()->NewNode(
+ simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)),
+ receiver, new_length, effect, control);
+
+ // Append the {values} to the {elements}.
+ for (int i = 0; i < num_values; ++i) {
+ Node* value = values[i];
+ Node* index = graph()->NewNode(simplified()->NumberAdd(), length,
+ jsgraph()->Constant(i));
+ effect = graph()->NewNode(
+ simplified()->StoreElement(AccessBuilder::ForFixedArrayElement(kind)),
+ elements, index, value, effect, control);
+ }
+ }
+
+ ReplaceWithValue(node, value, effect, control);
+ return Replace(value);
+}
+
+// ES6 section 22.1.3.17 Array.prototype.pop ( )
+Reduction JSCallReducer::ReduceArrayPrototypePop(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ CallParameters const& p = CallParametersOf(node->op());
+ if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
+ return NoChange();
+ }
+
+ if (!isolate()->IsNoElementsProtectorIntact()) return NoChange();
+
+ Node* receiver = NodeProperties::GetValueInput(node, 1);
+ Node* effect = NodeProperties::GetEffectInput(node);
+ Node* control = NodeProperties::GetControlInput(node);
+
+ ZoneHandleSet<Map> receiver_maps;
+ NodeProperties::InferReceiverMapsResult result =
+ NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
+ if (result == NodeProperties::kNoReceiverMaps) return NoChange();
+ DCHECK_NE(0, receiver_maps.size());
+
+ ElementsKind kind = receiver_maps[0]->elements_kind();
+ for (Handle<Map> receiver_map : receiver_maps) {
+ if (!CanInlineArrayResizeOperation(receiver_map)) return NoChange();
+ // TODO(turbofan): Extend this to also handle fast holey double elements
+ // once we got the hole NaN mess sorted out in TurboFan/V8.
+ if (receiver_map->elements_kind() == HOLEY_DOUBLE_ELEMENTS)
+ return NoChange();
+ if (!UnionElementsKindUptoPackedness(&kind, receiver_map->elements_kind()))
+ return NoChange();
+ }
+
+ // Install code dependencies on the {receiver} global array protector cell.
+ dependencies()->AssumePropertyCell(factory()->no_elements_protector());
+
+ // If the {receiver_maps} information is not reliable, we need
+ // to check that the {receiver} still has one of these maps.
+ if (result == NodeProperties::kUnreliableReceiverMaps) {
+ effect =
+ graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
+ receiver_maps, p.feedback()),
+ receiver, effect, control);
+ }
+
+ // Load the "length" property of the {receiver}.
+ Node* length = effect = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
+ effect, control);
+
+ // Check if the {receiver} has any elements.
+ Node* check = graph()->NewNode(simplified()->NumberEqual(), length,
+ jsgraph()->ZeroConstant());
+ Node* branch =
+ graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
+
+ Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
+ Node* etrue = effect;
+ Node* vtrue = jsgraph()->UndefinedConstant();
+
+ Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
+ Node* efalse = effect;
+ Node* vfalse;
+ {
+ // TODO(tebbi): We should trim the backing store if the capacity is too
+ // big, as implemented in elements.cc:ElementsAccessorBase::SetLengthImpl.
+
+ // Load the elements backing store from the {receiver}.
+ Node* elements = efalse = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
+ efalse, if_false);
+
+ // Ensure that we aren't popping from a copy-on-write backing store.
+ if (IsSmiOrObjectElementsKind(kind)) {
+ elements = efalse =
+ graph()->NewNode(simplified()->EnsureWritableFastElements(), receiver,
+ elements, efalse, if_false);
+ }
+
+ // Compute the new {length}.
+ length = graph()->NewNode(simplified()->NumberSubtract(), length,
+ jsgraph()->OneConstant());
+
+ // Store the new {length} to the {receiver}.
+ efalse = graph()->NewNode(
+ simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)),
+ receiver, length, efalse, if_false);
+
+ // Load the last entry from the {elements}.
+ vfalse = efalse = graph()->NewNode(
+ simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(kind)),
+ elements, length, efalse, if_false);
+
+ // Store a hole to the element we just removed from the {receiver}.
+ efalse = graph()->NewNode(
+ simplified()->StoreElement(
+ AccessBuilder::ForFixedArrayElement(GetHoleyElementsKind(kind))),
+ elements, length, jsgraph()->TheHoleConstant(), efalse, if_false);
+ }
+
+ control = graph()->NewNode(common()->Merge(2), if_true, if_false);
+ effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
+ Node* value = graph()->NewNode(
+ common()->Phi(MachineRepresentation::kTagged, 2), vtrue, vfalse, control);
+
+ // Convert the hole to undefined. Do this last, so that we can optimize
+ // conversion operator via some smart strength reduction in many cases.
+ if (IsHoleyElementsKind(kind)) {
+ value =
+ graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), value);
+ }
+
+ ReplaceWithValue(node, value, effect, control);
+ return Replace(value);
+}
+
+// ES6 section 22.1.3.22 Array.prototype.shift ( )
+Reduction JSCallReducer::ReduceArrayPrototypeShift(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ CallParameters const& p = CallParametersOf(node->op());
+ if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
+ return NoChange();
+ }
+
+ if (!isolate()->IsNoElementsProtectorIntact()) return NoChange();
+ Node* target = NodeProperties::GetValueInput(node, 0);
+ Node* receiver = NodeProperties::GetValueInput(node, 1);
+ Node* context = NodeProperties::GetContextInput(node);
+ Node* frame_state = NodeProperties::GetFrameStateInput(node);
+ Node* effect = NodeProperties::GetEffectInput(node);
+ Node* control = NodeProperties::GetControlInput(node);
+
+ ZoneHandleSet<Map> receiver_maps;
+ NodeProperties::InferReceiverMapsResult result =
+ NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
+ if (result == NodeProperties::kNoReceiverMaps) return NoChange();
+ DCHECK_NE(0, receiver_maps.size());
+
+ ElementsKind kind = receiver_maps[0]->elements_kind();
+ for (Handle<Map> receiver_map : receiver_maps) {
+ if (!CanInlineArrayResizeOperation(receiver_map)) return NoChange();
+ // TODO(turbofan): Extend this to also handle fast holey double elements
+ // once we got the hole NaN mess sorted out in TurboFan/V8.
+ if (receiver_map->elements_kind() == HOLEY_DOUBLE_ELEMENTS)
+ return NoChange();
+ if (!UnionElementsKindUptoPackedness(&kind, receiver_map->elements_kind()))
+ return NoChange();
+ }
+
+ // Install code dependencies on the {receiver} global array protector cell.
+ dependencies()->AssumePropertyCell(factory()->no_elements_protector());
+
+ // If the {receiver_maps} information is not reliable, we need
+ // to check that the {receiver} still has one of these maps.
+ if (result == NodeProperties::kUnreliableReceiverMaps) {
+ effect =
+ graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
+ receiver_maps, p.feedback()),
+ receiver, effect, control);
+ }
+
+ // Load length of the {receiver}.
+ Node* length = effect = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
+ effect, control);
+
+ // Return undefined if {receiver} has no elements.
+ Node* check0 = graph()->NewNode(simplified()->NumberEqual(), length,
+ jsgraph()->ZeroConstant());
+ Node* branch0 =
+ graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control);
+
+ Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
+ Node* etrue0 = effect;
+ Node* vtrue0 = jsgraph()->UndefinedConstant();
+
+ Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
+ Node* efalse0 = effect;
+ Node* vfalse0;
+ {
+ // Check if we should take the fast-path.
+ Node* check1 =
+ graph()->NewNode(simplified()->NumberLessThanOrEqual(), length,
+ jsgraph()->Constant(JSArray::kMaxCopyElements));
+ Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
+ check1, if_false0);
+
+ Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
+ Node* etrue1 = efalse0;
+ Node* vtrue1;
+ {
+ Node* elements = etrue1 = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
+ receiver, etrue1, if_true1);
+
+ // Load the first element here, which we return below.
+ vtrue1 = etrue1 = graph()->NewNode(
+ simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(kind)),
+ elements, jsgraph()->ZeroConstant(), etrue1, if_true1);
+
+ // Ensure that we aren't shifting a copy-on-write backing store.
+ if (IsSmiOrObjectElementsKind(kind)) {
+ elements = etrue1 =
+ graph()->NewNode(simplified()->EnsureWritableFastElements(),
+ receiver, elements, etrue1, if_true1);
+ }
+
+ // Shift the remaining {elements} by one towards the start.
+ Node* loop = graph()->NewNode(common()->Loop(2), if_true1, if_true1);
+ Node* eloop =
+ graph()->NewNode(common()->EffectPhi(2), etrue1, etrue1, loop);
+ Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
+ NodeProperties::MergeControlToEnd(graph(), common(), terminate);
+ Node* index = graph()->NewNode(
+ common()->Phi(MachineRepresentation::kTagged, 2),
+ jsgraph()->OneConstant(),
+ jsgraph()->Constant(JSArray::kMaxCopyElements - 1), loop);
+
+ {
+ Node* check2 =
+ graph()->NewNode(simplified()->NumberLessThan(), index, length);
+ Node* branch2 = graph()->NewNode(common()->Branch(), check2, loop);
+
+ if_true1 = graph()->NewNode(common()->IfFalse(), branch2);
+ etrue1 = eloop;
+
+ Node* control = graph()->NewNode(common()->IfTrue(), branch2);
+ Node* effect = etrue1;
+
+ ElementAccess const access = AccessBuilder::ForFixedArrayElement(kind);
+ Node* value = effect =
+ graph()->NewNode(simplified()->LoadElement(access), elements, index,
+ effect, control);
+ effect =
+ graph()->NewNode(simplified()->StoreElement(access), elements,
+ graph()->NewNode(simplified()->NumberSubtract(),
+ index, jsgraph()->OneConstant()),
+ value, effect, control);
+
+ loop->ReplaceInput(1, control);
+ eloop->ReplaceInput(1, effect);
+ index->ReplaceInput(1,
+ graph()->NewNode(simplified()->NumberAdd(), index,
+ jsgraph()->OneConstant()));
+ }
+
+ // Compute the new {length}.
+ length = graph()->NewNode(simplified()->NumberSubtract(), length,
+ jsgraph()->OneConstant());
+
+ // Store the new {length} to the {receiver}.
+ etrue1 = graph()->NewNode(
+ simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)),
+ receiver, length, etrue1, if_true1);
+
+ // Store a hole to the element we just removed from the {receiver}.
+ etrue1 = graph()->NewNode(
+ simplified()->StoreElement(
+ AccessBuilder::ForFixedArrayElement(GetHoleyElementsKind(kind))),
+ elements, length, jsgraph()->TheHoleConstant(), etrue1, if_true1);
+ }
+
+ Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
+ Node* efalse1 = efalse0;
+ Node* vfalse1;
+ {
+ // Call the generic C++ implementation.
+ const int builtin_index = Builtins::kArrayShift;
+ CallDescriptor const* const desc = Linkage::GetCEntryStubCallDescriptor(
+ graph()->zone(), 1, BuiltinArguments::kNumExtraArgsWithReceiver,
+ Builtins::name(builtin_index), node->op()->properties(),
+ CallDescriptor::kNeedsFrameState);
+ Node* stub_code =
+ jsgraph()->CEntryStubConstant(1, kDontSaveFPRegs, kArgvOnStack, true);
+ Address builtin_entry = Builtins::CppEntryOf(builtin_index);
+ Node* entry = jsgraph()->ExternalConstant(
+ ExternalReference(builtin_entry, isolate()));
+ Node* argc =
+ jsgraph()->Constant(BuiltinArguments::kNumExtraArgsWithReceiver);
+ if_false1 = efalse1 = vfalse1 =
+ graph()->NewNode(common()->Call(desc), stub_code, receiver,
+ jsgraph()->PaddingConstant(), argc, target,
+ jsgraph()->UndefinedConstant(), entry, argc, context,
+ frame_state, efalse1, if_false1);
+ }
+
+ if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
+ efalse0 =
+ graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_false0);
+ vfalse0 = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
+ vtrue1, vfalse1, if_false0);
+ }
+
+ control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
+ effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control);
+ Node* value =
+ graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
+ vtrue0, vfalse0, control);
+
+ // Convert the hole to undefined. Do this last, so that we can optimize
+ // conversion operator via some smart strength reduction in many cases.
+ if (IsHoleyElementsKind(kind)) {
+ value =
+ graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), value);
+ }
+
+ ReplaceWithValue(node, value, effect, control);
+ return Replace(value);
+}
+
+// ES6 section 21.1.3.1 String.prototype.charAt ( pos )
+Reduction JSCallReducer::ReduceStringPrototypeCharAt(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ CallParameters const& p = CallParametersOf(node->op());
+ if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
+ return NoChange();
+ }
+
+ Node* receiver = NodeProperties::GetValueInput(node, 1);
+ Node* index = jsgraph()->ZeroConstant();
+ Node* effect = NodeProperties::GetEffectInput(node);
+ Node* control = NodeProperties::GetControlInput(node);
+
+ receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
+ receiver, effect, control);
+ if (node->op()->ValueInputCount() >= 3) {
+ index = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()),
+ NodeProperties::GetValueInput(node, 2),
+ effect, control);
+ // Map -0 and NaN to 0 (as per ToInteger), and the values in
+ // the [-2^31,-1] range to the [2^31,2^32-1] range, which will
+ // be considered out-of-bounds as well, because of the maximal
+ // String length limit in V8.
+ STATIC_ASSERT(String::kMaxLength <= kMaxInt);
+ index = graph()->NewNode(simplified()->NumberToUint32(), index);
+ }
+
+ // Determine the {receiver} length.
+ Node* receiver_length =
+ graph()->NewNode(simplified()->StringLength(), receiver);
+
+ // Check if {index} is less than {receiver} length.
+ Node* check =
+ graph()->NewNode(simplified()->NumberLessThan(), index, receiver_length);
+ Node* branch =
+ graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
+
+ // Return the character from the {receiver} as single character string.
+ Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
+
+ Node* masked_index = graph()->NewNode(simplified()->MaskIndexWithBound(),
+ index, receiver_length);
+
+ Node* vtrue = graph()->NewNode(simplified()->StringCharAt(), receiver,
+ masked_index, if_true);
+
+ // Return the empty string otherwise.
+ Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
+ Node* vfalse = jsgraph()->EmptyStringConstant();
+
+ control = graph()->NewNode(common()->Merge(2), if_true, if_false);
+ Node* value = graph()->NewNode(
+ common()->Phi(MachineRepresentation::kTagged, 2), vtrue, vfalse, control);
+
+ ReplaceWithValue(node, value, effect, control);
+ return Replace(value);
+}
+
+// ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos )
+Reduction JSCallReducer::ReduceStringPrototypeCharCodeAt(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ CallParameters const& p = CallParametersOf(node->op());
+ if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
+ return NoChange();
+ }
+
+ Node* receiver = NodeProperties::GetValueInput(node, 1);
+ Node* index = jsgraph()->ZeroConstant();
+ Node* effect = NodeProperties::GetEffectInput(node);
+ Node* control = NodeProperties::GetControlInput(node);
+
+ receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
+ receiver, effect, control);
+ if (node->op()->ValueInputCount() >= 3) {
+ index = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()),
+ NodeProperties::GetValueInput(node, 2),
+ effect, control);
+
+ // Map -0 and NaN to 0 (as per ToInteger), and the values in
+ // the [-2^31,-1] range to the [2^31,2^32-1] range, which will
+ // be considered out-of-bounds as well, because of the maximal
+ // String length limit in V8.
+ STATIC_ASSERT(String::kMaxLength <= kMaxInt);
+ index = graph()->NewNode(simplified()->NumberToUint32(), index);
+ }
+
+ // Determine the {receiver} length.
+ Node* receiver_length =
+ graph()->NewNode(simplified()->StringLength(), receiver);
+
+ // Check if {index} is less than {receiver} length.
+ Node* check =
+ graph()->NewNode(simplified()->NumberLessThan(), index, receiver_length);
+ Node* branch =
+ graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
+
+ // Load the character from the {receiver}.
+ Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
+
+ Node* masked_index = graph()->NewNode(simplified()->MaskIndexWithBound(),
+ index, receiver_length);
+
+ Node* vtrue = graph()->NewNode(simplified()->StringCharCodeAt(), receiver,
+ masked_index, if_true);
+
+ // Return NaN otherwise.
+ Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
+ Node* vfalse = jsgraph()->NaNConstant();
+
+ control = graph()->NewNode(common()->Merge(2), if_true, if_false);
+ Node* value = graph()->NewNode(
+ common()->Phi(MachineRepresentation::kTagged, 2), vtrue, vfalse, control);
+
+ ReplaceWithValue(node, value, effect, control);
+ return Replace(value);
+}
+
Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }
Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); }