Index: src/compiler/js-call-reducer.cc |
diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc |
index 79fd6f6d3862d405ef90330c4526bcb957027fb4..b71c22723ceaf0e802c11cdb6a0e4ac11a1ef7b3 100644 |
--- a/src/compiler/js-call-reducer.cc |
+++ b/src/compiler/js-call-reducer.cc |
@@ -40,6 +40,23 @@ Reduction JSCallReducer::Reduce(Node* node) { |
return NoChange(); |
} |
+void JSCallReducer::Finalize() { |
+ // TODO(turbofan): This is not the best solution; ideally we would be able |
+ // to teach the GraphReducer about arbitrary dependencies between different |
+ // nodes, even if they don't show up in the use list of the other node. |
+ std::set<Node*> const waitlist = std::move(waitlist_); |
+ for (Node* node : waitlist) { |
+ if (!node->IsDead()) { |
+ Reduction const reduction = Reduce(node); |
+ if (reduction.Changed()) { |
+ Node* replacement = reduction.replacement(); |
+ if (replacement != node) { |
+ Replace(node, replacement); |
+ } |
+ } |
+ } |
+ } |
+} |
// ES6 section 22.1.1 The Array Constructor |
Reduction JSCallReducer::ReduceArrayConstructor(Node* node) { |
@@ -686,6 +703,23 @@ Reduction JSCallReducer::ReduceCallApiFunction( |
return Changed(node); |
} |
+namespace { |
+ |
+// Check whether elements aren't mutated; we play it extremely safe here by |
+// explicitly checking that {node} is only used by {LoadField} or {LoadElement}. |
+bool IsSafeArgumentsElements(Node* node) { |
+ for (Edge const edge : node->use_edges()) { |
+ if (!NodeProperties::IsValueEdge(edge)) continue; |
+ if (edge.from()->opcode() != IrOpcode::kLoadField && |
+ edge.from()->opcode() != IrOpcode::kLoadElement) { |
+ return false; |
+ } |
+ } |
+ return true; |
+} |
+ |
+} // namespace |
+ |
Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread( |
Node* node, int arity, CallFrequency const& frequency) { |
DCHECK(node->opcode() == IrOpcode::kJSCallWithArrayLike || |
@@ -704,19 +738,63 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread( |
// Check if {arguments_list} is an arguments object, and {node} is the only |
// value user of {arguments_list} (except for value uses in frame states). |
Node* arguments_list = NodeProperties::GetValueInput(node, arity); |
- if (arguments_list->opcode() != IrOpcode::kJSCreateArguments) |
+ if (arguments_list->opcode() != IrOpcode::kJSCreateArguments) { |
return NoChange(); |
+ } |
for (Edge edge : arguments_list->use_edges()) { |
+ if (!NodeProperties::IsValueEdge(edge)) 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) == arguments_list) { |
- continue; |
+ switch (user->opcode()) { |
+ case IrOpcode::kCheckMaps: |
+ case IrOpcode::kFrameState: |
+ case IrOpcode::kStateValues: |
+ case IrOpcode::kReferenceEqual: |
+ case IrOpcode::kReturn: |
+ // Ignore safe uses that definitely don't mess with the arguments. |
+ continue; |
+ case IrOpcode::kLoadField: { |
+ DCHECK_EQ(arguments_list, user->InputAt(0)); |
+ FieldAccess const& access = FieldAccessOf(user->op()); |
+ if (access.offset == JSArray::kLengthOffset) { |
+ // Ignore uses for arguments#length. |
+ STATIC_ASSERT(JSArray::kLengthOffset == |
+ JSArgumentsObject::kLengthOffset); |
+ continue; |
+ } else if (access.offset == JSObject::kElementsOffset) { |
+ // Ignore safe uses for arguments#elements. |
+ if (IsSafeArgumentsElements(user)) continue; |
+ } |
+ break; |
+ } |
+ case IrOpcode::kJSCallWithArrayLike: |
+ // Ignore uses as argumentsList input to calls with array like. |
+ if (user->InputAt(2) == arguments_list) continue; |
+ break; |
+ case IrOpcode::kJSConstructWithArrayLike: |
+ // Ignore uses as argumentsList input to calls with array like. |
+ if (user->InputAt(1) == arguments_list) continue; |
+ break; |
+ case IrOpcode::kJSCallWithSpread: { |
+ // Ignore uses as spread input to calls with spread. |
+ SpreadWithArityParameter p = SpreadWithArityParameterOf(user->op()); |
+ int const arity = static_cast<int>(p.arity() - 1); |
+ if (user->InputAt(arity) == arguments_list) continue; |
+ break; |
+ } |
+ case IrOpcode::kJSConstructWithSpread: { |
+ // Ignore uses as spread input to construct with spread. |
+ SpreadWithArityParameter p = SpreadWithArityParameterOf(user->op()); |
+ int const arity = static_cast<int>(p.arity() - 2); |
+ if (user->InputAt(arity) == arguments_list) continue; |
+ break; |
+ } |
+ default: |
+ break; |
} |
- if (!NodeProperties::IsValueEdge(edge)) continue; |
+ // We cannot currently reduce the {node} to something better than what |
+ // it already is, but we might be able to do something about the {node} |
+ // later, so put it on the waitlist and try again during finalization. |
+ waitlist_.insert(node); |
return NoChange(); |
} |