| Index: src/compiler/js-call-reducer.cc
|
| diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc
|
| index dfade082168a1594e1211610fe455adbb10ce354..1e1d3a92ab01abc235b58dbec165db4ad3d83ecb 100644
|
| --- a/src/compiler/js-call-reducer.cc
|
| +++ b/src/compiler/js-call-reducer.cc
|
| @@ -124,7 +124,8 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
|
| Node* arg_array = NodeProperties::GetValueInput(node, 3);
|
| if (arg_array->opcode() != IrOpcode::kJSCreateArguments) return NoChange();
|
| for (Edge edge : arg_array->use_edges()) {
|
| - Node* user = edge.from();
|
| + Node* const user = edge.from();
|
| + if (user == node) continue;
|
| // Ignore uses as frame state's locals or parameters.
|
| if (user->opcode() == IrOpcode::kStateValues) continue;
|
| // Ignore uses as frame state's accumulator.
|
| @@ -133,7 +134,6 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
|
| continue;
|
| }
|
| if (!NodeProperties::IsValueEdge(edge)) continue;
|
| - if (edge.from() == node) continue;
|
| return NoChange();
|
| }
|
| // Check if the arguments can be handled in the fast case (i.e. we don't
|
| @@ -173,7 +173,7 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
|
| node->RemoveInput(0); // Function.prototype.apply
|
| node->RemoveInput(2); // arguments
|
| NodeProperties::ChangeOp(node, javascript()->CallForwardVarargs(
|
| - start_index, p.tail_call_mode()));
|
| + 2, start_index, p.tail_call_mode()));
|
| return Changed(node);
|
| }
|
| // Get to the actual frame state from which to extract the arguments;
|
| @@ -454,50 +454,81 @@ Reduction JSCallReducer::ReduceSpreadCall(Node* node, int arity) {
|
| // of spread (except for value uses in frame states).
|
| if (spread->opcode() != IrOpcode::kJSCreateArguments) return NoChange();
|
| for (Edge edge : spread->use_edges()) {
|
| - if (edge.from()->opcode() == IrOpcode::kStateValues) continue;
|
| + Node* const user = edge.from();
|
| + if (user == node) continue;
|
| + // Ignore uses as frame state's locals or parameters.
|
| + if (user->opcode() == IrOpcode::kStateValues) continue;
|
| + // Ignore uses as frame state's accumulator.
|
| + if (user->opcode() == IrOpcode::kFrameState && user->InputAt(2) == spread) {
|
| + continue;
|
| + }
|
| if (!NodeProperties::IsValueEdge(edge)) continue;
|
| - if (edge.from() == node) continue;
|
| return NoChange();
|
| }
|
|
|
| // Get to the actual frame state from which to extract the arguments;
|
| // we can only optimize this in case the {node} was already inlined into
|
| // some other function (and same for the {spread}).
|
| - CreateArgumentsType type = CreateArgumentsTypeOf(spread->op());
|
| + CreateArgumentsType const type = CreateArgumentsTypeOf(spread->op());
|
| Node* frame_state = NodeProperties::GetFrameStateInput(spread);
|
| - Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput);
|
| - if (outer_state->opcode() != IrOpcode::kFrameState) return NoChange();
|
| - FrameStateInfo outer_info = OpParameter<FrameStateInfo>(outer_state);
|
| - if (outer_info.type() == FrameStateType::kArgumentsAdaptor) {
|
| - // Need to take the parameters from the arguments adaptor.
|
| - frame_state = outer_state;
|
| - }
|
| FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state);
|
| int start_index = 0;
|
| + // Determine the formal parameter count;
|
| + Handle<SharedFunctionInfo> shared;
|
| + if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
|
| + int formal_parameter_count = shared->internal_formal_parameter_count();
|
| if (type == CreateArgumentsType::kMappedArguments) {
|
| - // Mapped arguments (sloppy mode) cannot be handled if they are aliased.
|
| - Handle<SharedFunctionInfo> shared;
|
| - if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
|
| - if (shared->internal_formal_parameter_count() != 0) return NoChange();
|
| + // Mapped arguments (sloppy mode) that are aliased can only be handled
|
| + // here if there's no side-effect between the {node} and the {arg_array}.
|
| + // TODO(turbofan): Further relax this constraint.
|
| + if (formal_parameter_count != 0) {
|
| + Node* effect = NodeProperties::GetEffectInput(node);
|
| + while (effect != spread) {
|
| + if (effect->op()->EffectInputCount() != 1 ||
|
| + !(effect->op()->properties() & Operator::kNoWrite)) {
|
| + return NoChange();
|
| + }
|
| + effect = NodeProperties::GetEffectInput(effect);
|
| + }
|
| + }
|
| } else if (type == CreateArgumentsType::kRestParameter) {
|
| - Handle<SharedFunctionInfo> shared;
|
| - if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
|
| - start_index = shared->internal_formal_parameter_count();
|
| + start_index = formal_parameter_count;
|
|
|
| // Only check the array iterator protector when we have a rest object.
|
| if (!isolate()->IsArrayIteratorLookupChainIntact()) return NoChange();
|
| - // Add a code dependency on the array iterator protector.
|
| - dependencies()->AssumePropertyCell(factory()->array_iterator_protector());
|
| }
|
|
|
| + // Install appropriate code dependencies.
|
| dependencies()->AssumeMapStable(
|
| isolate()->initial_array_iterator_prototype_map());
|
| -
|
| + if (type == CreateArgumentsType::kRestParameter) {
|
| + dependencies()->AssumePropertyCell(factory()->array_iterator_protector());
|
| + }
|
| + // Remove the spread input from the {node}.
|
| node->RemoveInput(arity--);
|
| -
|
| + // Check if are spreading to inlined arguments or to the arguments of
|
| + // the outermost function.
|
| + Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput);
|
| + if (outer_state->opcode() != IrOpcode::kFrameState) {
|
| + Operator const* op =
|
| + (node->opcode() == IrOpcode::kJSCallWithSpread)
|
| + ? javascript()->CallForwardVarargs(arity + 1, start_index,
|
| + TailCallMode::kDisallow)
|
| + : javascript()->ConstructForwardVarargs(arity + 2, start_index);
|
| + NodeProperties::ChangeOp(node, op);
|
| + return Changed(node);
|
| + }
|
| + // Get to the actual frame state from which to extract the arguments;
|
| + // we can only optimize this in case the {node} was already inlined into
|
| + // some other function (and same for the {arg_array}).
|
| + FrameStateInfo outer_info = OpParameter<FrameStateInfo>(outer_state);
|
| + if (outer_info.type() == FrameStateType::kArgumentsAdaptor) {
|
| + // Need to take the parameters from the arguments adaptor.
|
| + frame_state = outer_state;
|
| + }
|
| // Add the actual parameters to the {node}, skipping the receiver.
|
| Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
|
| - for (int i = start_index + 1; i < state_info.parameter_count(); ++i) {
|
| + for (int i = start_index + 1; i < parameters->InputCount(); ++i) {
|
| node->InsertInput(graph()->zone(), static_cast<int>(++arity),
|
| parameters->InputAt(i));
|
| }
|
|
|