OLD | NEW |
---|---|
1 // Copyright 2015 the V8 project authors. All rights reserved. | 1 // Copyright 2015 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/compiler/js-call-reducer.h" | 5 #include "src/compiler/js-call-reducer.h" |
6 | 6 |
7 #include "src/code-factory.h" | 7 #include "src/code-factory.h" |
8 #include "src/code-stubs.h" | 8 #include "src/code-stubs.h" |
9 #include "src/compilation-dependencies.h" | 9 #include "src/compilation-dependencies.h" |
10 #include "src/compiler/access-builder.h" | 10 #include "src/compiler/access-builder.h" |
(...skipping 22 matching lines...) Expand all Loading... | |
33 case IrOpcode::kJSCallWithArrayLike: | 33 case IrOpcode::kJSCallWithArrayLike: |
34 return ReduceJSCallWithArrayLike(node); | 34 return ReduceJSCallWithArrayLike(node); |
35 case IrOpcode::kJSCallWithSpread: | 35 case IrOpcode::kJSCallWithSpread: |
36 return ReduceJSCallWithSpread(node); | 36 return ReduceJSCallWithSpread(node); |
37 default: | 37 default: |
38 break; | 38 break; |
39 } | 39 } |
40 return NoChange(); | 40 return NoChange(); |
41 } | 41 } |
42 | 42 |
43 void JSCallReducer::Finalize() { | |
petermarshall
2017/06/29 13:25:24
I'm trying to figure out how we guarantee there is
Benedikt Meurer
2017/06/30 04:38:08
Finalize can be called multiple times, but only if
| |
44 // TODO(turbofan): This is not the best solution; ideally we would be able | |
45 // to teach the GraphReducer about arbitrary dependencies between different | |
46 // nodes, even if they don't show up in the use list of the other node. | |
47 std::set<Node*> const waitlist = std::move(waitlist_); | |
petermarshall
2017/06/29 13:25:24
I don't think std::move does anything useful here,
Benedikt Meurer
2017/06/30 04:38:08
It is the important bit, as it clears the waitlist
| |
48 for (Node* node : waitlist) { | |
49 if (!node->IsDead()) { | |
50 Reduction const reduction = Reduce(node); | |
51 if (reduction.Changed()) { | |
52 Node* replacement = reduction.replacement(); | |
53 if (replacement != node) { | |
54 Replace(node, replacement); | |
55 } | |
56 } | |
57 } | |
58 } | |
59 } | |
43 | 60 |
44 // ES6 section 22.1.1 The Array Constructor | 61 // ES6 section 22.1.1 The Array Constructor |
45 Reduction JSCallReducer::ReduceArrayConstructor(Node* node) { | 62 Reduction JSCallReducer::ReduceArrayConstructor(Node* node) { |
46 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); | 63 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
47 Node* target = NodeProperties::GetValueInput(node, 0); | 64 Node* target = NodeProperties::GetValueInput(node, 0); |
48 CallParameters const& p = CallParametersOf(node->op()); | 65 CallParameters const& p = CallParametersOf(node->op()); |
49 | 66 |
50 // Check if we have an allocation site from the CallIC. | 67 // Check if we have an allocation site from the CallIC. |
51 Handle<AllocationSite> site; | 68 Handle<AllocationSite> site; |
52 if (p.feedback().IsValid()) { | 69 if (p.feedback().IsValid()) { |
(...skipping 626 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
679 jsgraph()->HeapConstant(stub.GetCode())); | 696 jsgraph()->HeapConstant(stub.GetCode())); |
680 node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(data)); | 697 node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(data)); |
681 node->InsertInput(graph()->zone(), 3, holder); | 698 node->InsertInput(graph()->zone(), 3, holder); |
682 node->InsertInput(graph()->zone(), 4, | 699 node->InsertInput(graph()->zone(), 4, |
683 jsgraph()->ExternalConstant(function_reference)); | 700 jsgraph()->ExternalConstant(function_reference)); |
684 node->ReplaceInput(5, receiver); | 701 node->ReplaceInput(5, receiver); |
685 NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); | 702 NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); |
686 return Changed(node); | 703 return Changed(node); |
687 } | 704 } |
688 | 705 |
706 namespace { | |
707 | |
708 // Check whether elements aren't mutated; we play it extremely safe here by | |
709 // explicitly checking that {node} only used by {LoadField} or {LoadElement}. | |
petermarshall
2017/06/29 13:25:24
Nit: ...{node} *is* only...
Benedikt Meurer
2017/06/30 07:21:36
Done.
| |
710 bool IsSafeArgumentsElements(Node* node) { | |
711 for (Edge const edge : node->use_edges()) { | |
712 if (!NodeProperties::IsValueEdge(edge)) continue; | |
713 if (edge.from()->opcode() != IrOpcode::kLoadField && | |
714 edge.from()->opcode() != IrOpcode::kLoadElement) { | |
715 return false; | |
716 } | |
717 } | |
718 return true; | |
719 } | |
720 | |
721 } // namespace | |
722 | |
689 Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread( | 723 Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread( |
690 Node* node, int arity, CallFrequency const& frequency) { | 724 Node* node, int arity, CallFrequency const& frequency) { |
691 DCHECK(node->opcode() == IrOpcode::kJSCallWithArrayLike || | 725 DCHECK(node->opcode() == IrOpcode::kJSCallWithArrayLike || |
692 node->opcode() == IrOpcode::kJSCallWithSpread || | 726 node->opcode() == IrOpcode::kJSCallWithSpread || |
693 node->opcode() == IrOpcode::kJSConstructWithArrayLike || | 727 node->opcode() == IrOpcode::kJSConstructWithArrayLike || |
694 node->opcode() == IrOpcode::kJSConstructWithSpread); | 728 node->opcode() == IrOpcode::kJSConstructWithSpread); |
695 | 729 |
696 // In case of a call/construct with spread, we need to | 730 // In case of a call/construct with spread, we need to |
697 // ensure that it's safe to avoid the actual iteration. | 731 // ensure that it's safe to avoid the actual iteration. |
698 if ((node->opcode() == IrOpcode::kJSCallWithSpread || | 732 if ((node->opcode() == IrOpcode::kJSCallWithSpread || |
699 node->opcode() == IrOpcode::kJSConstructWithSpread) && | 733 node->opcode() == IrOpcode::kJSConstructWithSpread) && |
700 !isolate()->initial_array_iterator_prototype_map()->is_stable()) { | 734 !isolate()->initial_array_iterator_prototype_map()->is_stable()) { |
701 return NoChange(); | 735 return NoChange(); |
702 } | 736 } |
703 | 737 |
704 // Check if {arguments_list} is an arguments object, and {node} is the only | 738 // Check if {arguments_list} is an arguments object, and {node} is the only |
705 // value user of {arguments_list} (except for value uses in frame states). | 739 // value user of {arguments_list} (except for value uses in frame states). |
706 Node* arguments_list = NodeProperties::GetValueInput(node, arity); | 740 Node* arguments_list = NodeProperties::GetValueInput(node, arity); |
707 if (arguments_list->opcode() != IrOpcode::kJSCreateArguments) | 741 if (arguments_list->opcode() != IrOpcode::kJSCreateArguments) { |
708 return NoChange(); | 742 return NoChange(); |
743 } | |
709 for (Edge edge : arguments_list->use_edges()) { | 744 for (Edge edge : arguments_list->use_edges()) { |
745 if (!NodeProperties::IsValueEdge(edge)) continue; | |
710 Node* const user = edge.from(); | 746 Node* const user = edge.from(); |
711 if (user == node) continue; | 747 switch (user->opcode()) { |
712 // Ignore uses as frame state's locals or parameters. | 748 case IrOpcode::kCheckMaps: |
713 if (user->opcode() == IrOpcode::kStateValues) continue; | 749 case IrOpcode::kFrameState: |
714 // Ignore uses as frame state's accumulator. | 750 case IrOpcode::kStateValues: |
715 if (user->opcode() == IrOpcode::kFrameState && | 751 case IrOpcode::kReferenceEqual: |
716 user->InputAt(2) == arguments_list) { | 752 case IrOpcode::kReturn: |
717 continue; | 753 // Ignore safe uses that definitely don't mess with the arguments. |
754 continue; | |
755 case IrOpcode::kLoadField: { | |
756 DCHECK_EQ(arguments_list, user->InputAt(0)); | |
757 FieldAccess const& access = FieldAccessOf(user->op()); | |
758 if (access.offset == JSArray::kLengthOffset) { | |
petermarshall
2017/06/29 13:25:24
If this is checking for LoadField on an arguments
Benedikt Meurer
2017/06/30 04:39:37
Rest parameters are JSArray instances.
petermarshall
2017/06/30 07:14:10
OK
| |
759 // Ignore uses for arguments#length. | |
760 STATIC_ASSERT(JSArray::kLengthOffset == | |
761 JSArgumentsObject::kLengthOffset); | |
762 continue; | |
763 } else if (access.offset == JSObject::kElementsOffset) { | |
764 // Ignore safe uses for arguments#elements. | |
765 if (IsSafeArgumentsElements(user)) continue; | |
766 } | |
767 break; | |
768 } | |
769 case IrOpcode::kJSCallWithArrayLike: | |
770 // Ignore uses as argumentsList input to calls with array like. | |
771 if (user->InputAt(2) == arguments_list) continue; | |
772 break; | |
773 case IrOpcode::kJSConstructWithArrayLike: | |
774 // Ignore uses as argumentsList input to calls with array like. | |
775 if (user->InputAt(1) == arguments_list) continue; | |
776 break; | |
777 case IrOpcode::kJSCallWithSpread: { | |
778 // Ignore uses as spread input to calls with spread. | |
779 SpreadWithArityParameter p = SpreadWithArityParameterOf(user->op()); | |
780 int const arity = static_cast<int>(p.arity() - 1); | |
781 if (user->InputAt(arity) == arguments_list) continue; | |
782 break; | |
783 } | |
784 case IrOpcode::kJSConstructWithSpread: { | |
785 // Ignore uses as spread input to construct with spread. | |
786 SpreadWithArityParameter p = SpreadWithArityParameterOf(user->op()); | |
787 int const arity = static_cast<int>(p.arity() - 2); | |
788 if (user->InputAt(arity) == arguments_list) continue; | |
789 break; | |
790 } | |
791 default: | |
792 break; | |
718 } | 793 } |
719 if (!NodeProperties::IsValueEdge(edge)) continue; | 794 // We cannot currently reduce the {node} to something better than what |
795 // it already is, but we might be able to do something about the {node} | |
796 // later, so put it on the waitlist and try again during finalization. | |
797 waitlist_.insert(node); | |
720 return NoChange(); | 798 return NoChange(); |
721 } | 799 } |
722 | 800 |
723 // Get to the actual frame state from which to extract the arguments; | 801 // Get to the actual frame state from which to extract the arguments; |
724 // we can only optimize this in case the {node} was already inlined into | 802 // we can only optimize this in case the {node} was already inlined into |
725 // some other function (and same for the {arguments_list}). | 803 // some other function (and same for the {arguments_list}). |
726 CreateArgumentsType const type = CreateArgumentsTypeOf(arguments_list->op()); | 804 CreateArgumentsType const type = CreateArgumentsTypeOf(arguments_list->op()); |
727 Node* frame_state = NodeProperties::GetFrameStateInput(arguments_list); | 805 Node* frame_state = NodeProperties::GetFrameStateInput(arguments_list); |
728 FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state); | 806 FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state); |
729 int start_index = 0; | 807 int start_index = 0; |
(...skipping 456 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1186 return jsgraph()->javascript(); | 1264 return jsgraph()->javascript(); |
1187 } | 1265 } |
1188 | 1266 |
1189 SimplifiedOperatorBuilder* JSCallReducer::simplified() const { | 1267 SimplifiedOperatorBuilder* JSCallReducer::simplified() const { |
1190 return jsgraph()->simplified(); | 1268 return jsgraph()->simplified(); |
1191 } | 1269 } |
1192 | 1270 |
1193 } // namespace compiler | 1271 } // namespace compiler |
1194 } // namespace internal | 1272 } // namespace internal |
1195 } // namespace v8 | 1273 } // namespace v8 |
OLD | NEW |