| Index: src/compiler/js-call-reducer.cc
|
| diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc
|
| index edb3d7149f7fcab2410ae1d815f7ce66c5ebc8a6..a7121c2b8d440b53ecaa524d1b9ab2f8422bdfbf 100644
|
| --- a/src/compiler/js-call-reducer.cc
|
| +++ b/src/compiler/js-call-reducer.cc
|
| @@ -7,6 +7,7 @@
|
| #include "src/code-factory.h"
|
| #include "src/code-stubs.h"
|
| #include "src/compilation-dependencies.h"
|
| +#include "src/compiler/access-builder.h"
|
| #include "src/compiler/js-graph.h"
|
| #include "src/compiler/linkage.h"
|
| #include "src/compiler/node-matchers.h"
|
| @@ -343,6 +344,196 @@ Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) {
|
| return ReduceObjectGetPrototype(node, target);
|
| }
|
|
|
| +std::pair<Node*, Node*>
|
| +JSCallReducer::CreateJavaScriptBuiltinContinuationFrameState(
|
| + Handle<SharedFunctionInfo> shared, Builtins::Name name, Node* target,
|
| + Node* context, Node** stack_parameters, int stack_parameter_count,
|
| + Node* effect, Node* control, Node* outer_frame_state, CheckpointMode mode) {
|
| + BailoutId bailout_id = Builtins::GetContinuationBailoutId(name);
|
| + Callable callable = Builtins::CallableFor(isolate(), name);
|
| +
|
| + // Lazy deopt points where the frame state is assocated with a call get an
|
| + // additional parameter for the return result from the call that's added by
|
| + // the deoptimizer and not explicitly specified in the frame state. Check that
|
| + // there is not a mismatch between the number of frame state parameters and
|
| + // the stack parameters required by the builtin taking this into account.
|
| + DCHECK_EQ(
|
| + Builtins::GetStackParameterCount(isolate(), name) + 1, // add receiver
|
| + stack_parameter_count + (mode == CREATE_CHECKPOINT ? 0 : 1));
|
| +
|
| + NodeVector params(local_zone_);
|
| + // target
|
| + params.push_back(target);
|
| + // new target
|
| + params.push_back(jsgraph()->UndefinedConstant());
|
| + // argc, remove receiver and add in return value for lazy deopts from calls
|
| + params.push_back(jsgraph()->Constant(stack_parameter_count -
|
| + (mode == CREATE_CHECKPOINT ? 1 : 0)));
|
| + // context
|
| + params.push_back(context);
|
| +
|
| + for (int i = 0; i < stack_parameter_count; ++i) {
|
| + params.push_back(stack_parameters[i]);
|
| + }
|
| +
|
| + const Operator* op_param = common()->StateValues(
|
| + static_cast<int>(params.size()), SparseInputMask::Dense());
|
| + Node* params_node = graph()->NewNode(
|
| + op_param, static_cast<int>(params.size()), ¶ms.front());
|
| +
|
| + // Add the context to the total parameter count, it is implicit in the
|
| + // descriptor.
|
| + const int total_parameter_count =
|
| + stack_parameter_count +
|
| + callable.descriptor().GetRegisterParameterCount() + 1;
|
| + const FrameStateFunctionInfo* state_info =
|
| + common()->CreateFrameStateFunctionInfo(
|
| + FrameStateType::kBuiltinContinuation, total_parameter_count, 0,
|
| + shared);
|
| + const Operator* op = common()->FrameState(
|
| + bailout_id, OutputFrameStateCombine::Ignore(), state_info);
|
| + const Operator* op0 = common()->StateValues(0, SparseInputMask::Dense());
|
| + Node* node0 = graph()->NewNode(op0);
|
| +
|
| + Node* frame_state = graph()->NewNode(op, params_node, node0, node0,
|
| + jsgraph()->UndefinedConstant(), target,
|
| + outer_frame_state);
|
| +
|
| + return std::make_pair(
|
| + frame_state, mode == CREATE_CHECKPOINT
|
| + ? graph()->NewNode(common()->Checkpoint(), frame_state,
|
| + effect, control)
|
| + : effect);
|
| +}
|
| +
|
| +Reduction JSCallReducer::ReduceArrayForEach(Handle<SharedFunctionInfo> shared,
|
| + Node* node) {
|
| + 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);
|
| +
|
| + // 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;
|
| + if (!NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps)) {
|
| + return NoChange();
|
| + }
|
| + if (receiver_maps.size() != 1) return NoChange();
|
| + Handle<Map> receiver_map(receiver_maps[0]);
|
| + ElementsKind kind = receiver_map->elements_kind();
|
| + // TODO(danno): Handle holey Smi and Object fast elements kinds and double
|
| + // packed.
|
| + if (!IsFastPackedElementsKind(kind) || IsFastDoubleElementsKind(kind)) {
|
| + return NoChange();
|
| + }
|
| +
|
| + Node* k = jsgraph()->Constant(0);
|
| +
|
| + Node* original_length = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForJSArrayLength(FAST_ELEMENTS)),
|
| + receiver, effect, control);
|
| +
|
| + Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
|
| + Node* eloop = effect =
|
| + graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
|
| + Node* vloop = k = graph()->NewNode(
|
| + common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop);
|
| +
|
| + control = loop;
|
| + effect = eloop;
|
| +
|
| + 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;
|
| +
|
| + NodeVector checkpoint_params(local_zone_);
|
| + checkpoint_params.push_back(receiver);
|
| + checkpoint_params.push_back(fncallback);
|
| + checkpoint_params.push_back(this_arg);
|
| + checkpoint_params.push_back(k);
|
| + checkpoint_params.push_back(original_length);
|
| + const int stack_parameters = static_cast<int>(checkpoint_params.size());
|
| +
|
| + Node* frame_state;
|
| + std::tie(frame_state, effect) = CreateJavaScriptBuiltinContinuationFrameState(
|
| + shared, Builtins::kArrayForEachLoopEagerDeoptContinuation,
|
| + node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
|
| + effect, control, outer_frame_state, CREATE_CHECKPOINT);
|
| +
|
| + // Make sure the map hasn't changed during the iteration
|
| + Node* orig_map = jsgraph()->HeapConstant(receiver_map);
|
| + Node* array_map = effect =
|
| + graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
|
| + receiver, effect, control);
|
| + Node* check_map =
|
| + graph()->NewNode(simplified()->ReferenceEqual(), array_map, orig_map);
|
| + effect =
|
| + graph()->NewNode(simplified()->CheckIf(), check_map, effect, control);
|
| +
|
| + // Make sure that the access is still in bounds, since the callback could have
|
| + // changed the array's size.
|
| + Node* length = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForJSArrayLength(FAST_ELEMENTS)),
|
| + receiver, effect, control);
|
| + k = effect =
|
| + graph()->NewNode(simplified()->CheckBounds(), k, length, effect, control);
|
| + USE(length);
|
| +
|
| + // Reload the elements pointer before calling the callback, since the previous
|
| + // callback might have resized the array causing the elements buffer to be
|
| + // re-allocated.
|
| + Node* elements = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
|
| + effect, control);
|
| +
|
| + Node* element = graph()->NewNode(
|
| + simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()),
|
| + elements, k, effect, control);
|
| +
|
| + Node* next_k =
|
| + graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->Constant(1));
|
| + checkpoint_params[3] = next_k;
|
| + std::tie(frame_state, effect) = CreateJavaScriptBuiltinContinuationFrameState(
|
| + shared, Builtins::kArrayForEachLoopLazyDeoptContinuation,
|
| + node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
|
| + effect, control, outer_frame_state, DONT_CREATE_CHECKPOINT);
|
| +
|
| + Node* call_back = control = effect =
|
| + graph()->NewNode(javascript()->Call(5, 1), fncallback, this_arg, element,
|
| + k, receiver, context, frame_state, effect, control);
|
| + USE(call_back);
|
| + USE(this_arg);
|
| +
|
| + k = next_k;
|
| +
|
| + loop->ReplaceInput(1, control);
|
| + vloop->ReplaceInput(1, k);
|
| + eloop->ReplaceInput(1, effect);
|
| +
|
| + control = if_false;
|
| + effect = eloop;
|
| +
|
| + NodeProperties::ReplaceUses(node, jsgraph()->UndefinedConstant(), effect,
|
| + control);
|
| +
|
| + node->TrimInputCount(0);
|
| + NodeProperties::ChangeOp(node, common()->Dead());
|
| + return Changed(node);
|
| +}
|
| +
|
| Reduction JSCallReducer::ReduceCallApiFunction(
|
| Node* node, Handle<FunctionTemplateInfo> function_template_info) {
|
| DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
|
| @@ -575,6 +766,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
|
| return ReduceObjectPrototypeGetProto(node);
|
| case Builtins::kReflectGetPrototypeOf:
|
| return ReduceReflectGetPrototypeOf(node);
|
| + case Builtins::kArrayForEach:
|
| + return ReduceArrayForEach(shared, node);
|
| default:
|
| break;
|
| }
|
|
|