Chromium Code Reviews| Index: src/compiler/js-call-reducer.cc |
| diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc |
| index 8dd1ce79aa298bbe6e0761edfa9bb097fbe41ed1..b308724448071208bdea8e1e319147e7328257a6 100644 |
| --- a/src/compiler/js-call-reducer.cc |
| +++ b/src/compiler/js-call-reducer.cc |
| @@ -6,6 +6,7 @@ |
| #include "src/code-factory.h" |
| #include "src/code-stubs.h" |
| +#include "src/compilation-dependencies.h" |
| #include "src/compiler/js-graph.h" |
| #include "src/compiler/linkage.h" |
| #include "src/compiler/node-matchers.h" |
| @@ -21,6 +22,8 @@ Reduction JSCallReducer::Reduce(Node* node) { |
| switch (node->opcode()) { |
| case IrOpcode::kJSCallConstruct: |
| return ReduceJSCallConstruct(node); |
| + case IrOpcode::kJSCallConstructWithSpread: |
| + return ReduceJSCallConstructWithSpread(node); |
| case IrOpcode::kJSCallFunction: |
| return ReduceJSCallFunction(node); |
| default: |
| @@ -691,10 +694,84 @@ Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) { |
| return NoChange(); |
| } |
| +Reduction JSCallReducer::ReduceJSCallConstructWithSpread(Node* node) { |
| + DCHECK_EQ(IrOpcode::kJSCallConstructWithSpread, node->opcode()); |
| + CallConstructWithSpreadParameters const& p = |
| + CallConstructWithSpreadParametersOf(node->op()); |
| + DCHECK_LE(3u, p.arity()); |
| + int arity = static_cast<int>(p.arity() - 2); |
| + |
| + Node* spread = NodeProperties::GetValueInput(node, arity); |
| + |
| + // Check if spread is an arguments object, and {node} is the only value user |
| + // 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; |
| + 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()); |
| + 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; |
| + 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(); |
| + } else if (type == CreateArgumentsType::kRestParameter) { |
| + Handle<SharedFunctionInfo> shared; |
| + if (!state_info.shared_info().ToHandle(&shared)) return NoChange(); |
| + start_index = shared->internal_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 string length overflow protector. |
|
Benedikt Meurer
2017/01/30 09:14:16
Typo: string length overflow :-)
petermarshall
2017/01/30 09:43:57
Done
|
| + dependencies()->AssumePropertyCell(factory()->array_iterator_protector()); |
| + } |
| + |
| + // Do checks to make sure we can actually avoid iteration. |
| + if (!isolate()->initial_array_iterator_prototype_map()->is_stable()) { |
|
Benedikt Meurer
2017/01/30 09:14:16
This should be done earlier, before you add depend
petermarshall
2017/01/30 09:43:58
Done
|
| + return NoChange(); |
| + } |
| + dependencies()->AssumeMapStable( |
| + isolate()->initial_array_iterator_prototype_map()); |
| + |
| + // Remove the spread input from the {node}. |
| + node->RemoveInput(arity--); |
| + |
| + // 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) { |
| + node->InsertInput(graph()->zone(), static_cast<int>(++arity), |
| + parameters->InputAt(i)); |
| + } |
| + |
| + NodeProperties::ChangeOp( |
| + node, javascript()->CallConstruct(arity + 2, 7, VectorSlotPair())); |
| + |
| + return Changed(node); |
| +} |
| + |
| Graph* JSCallReducer::graph() const { return jsgraph()->graph(); } |
| Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); } |
| +Factory* JSCallReducer::factory() const { return isolate()->factory(); } |
| + |
| CommonOperatorBuilder* JSCallReducer::common() const { |
| return jsgraph()->common(); |
| } |