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..67e699961a9dcb20b6f9dffa9fb0a39ed3a83e90 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,85 @@ 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); |
+ |
+ // Do check to make sure we can actually avoid iteration. |
+ if (!isolate()->initial_array_iterator_prototype_map()->is_stable()) { |
+ return NoChange(); |
+ } |
+ |
+ 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 array iterator protector. |
+ dependencies()->AssumePropertyCell(factory()->array_iterator_protector()); |
+ } |
+ |
+ 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(); |
} |