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 10 matching lines...) Expand all Loading... |
21 namespace compiler { | 21 namespace compiler { |
22 | 22 |
23 Reduction JSCallReducer::Reduce(Node* node) { | 23 Reduction JSCallReducer::Reduce(Node* node) { |
24 switch (node->opcode()) { | 24 switch (node->opcode()) { |
25 case IrOpcode::kJSConstruct: | 25 case IrOpcode::kJSConstruct: |
26 return ReduceJSConstruct(node); | 26 return ReduceJSConstruct(node); |
27 case IrOpcode::kJSConstructWithSpread: | 27 case IrOpcode::kJSConstructWithSpread: |
28 return ReduceJSConstructWithSpread(node); | 28 return ReduceJSConstructWithSpread(node); |
29 case IrOpcode::kJSCall: | 29 case IrOpcode::kJSCall: |
30 return ReduceJSCall(node); | 30 return ReduceJSCall(node); |
| 31 case IrOpcode::kJSCallWithArrayLike: |
| 32 return ReduceJSCallWithArrayLike(node); |
31 case IrOpcode::kJSCallWithSpread: | 33 case IrOpcode::kJSCallWithSpread: |
32 return ReduceJSCallWithSpread(node); | 34 return ReduceJSCallWithSpread(node); |
33 default: | 35 default: |
34 break; | 36 break; |
35 } | 37 } |
36 return NoChange(); | 38 return NoChange(); |
37 } | 39 } |
38 | 40 |
39 | 41 |
40 // ES6 section 22.1.1 The Array Constructor | 42 // ES6 section 22.1.1 The Array Constructor |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
88 | 90 |
89 // Turn the {node} into a {JSToNumber} call. | 91 // Turn the {node} into a {JSToNumber} call. |
90 DCHECK_LE(2u, p.arity()); | 92 DCHECK_LE(2u, p.arity()); |
91 Node* value = (p.arity() == 2) ? jsgraph()->ZeroConstant() | 93 Node* value = (p.arity() == 2) ? jsgraph()->ZeroConstant() |
92 : NodeProperties::GetValueInput(node, 2); | 94 : NodeProperties::GetValueInput(node, 2); |
93 NodeProperties::ReplaceValueInputs(node, value); | 95 NodeProperties::ReplaceValueInputs(node, value); |
94 NodeProperties::ChangeOp(node, javascript()->ToNumber()); | 96 NodeProperties::ChangeOp(node, javascript()->ToNumber()); |
95 return Changed(node); | 97 return Changed(node); |
96 } | 98 } |
97 | 99 |
| 100 namespace { |
| 101 |
| 102 bool CanBeNullOrUndefined(Node* node) { |
| 103 switch (node->opcode()) { |
| 104 case IrOpcode::kJSCreate: |
| 105 case IrOpcode::kJSCreateArguments: |
| 106 case IrOpcode::kJSCreateArray: |
| 107 case IrOpcode::kJSCreateClosure: |
| 108 case IrOpcode::kJSCreateIterResultObject: |
| 109 case IrOpcode::kJSCreateKeyValueArray: |
| 110 case IrOpcode::kJSCreateLiteralArray: |
| 111 case IrOpcode::kJSCreateLiteralObject: |
| 112 case IrOpcode::kJSCreateLiteralRegExp: |
| 113 case IrOpcode::kJSConstruct: |
| 114 case IrOpcode::kJSConstructForwardVarargs: |
| 115 case IrOpcode::kJSConstructWithSpread: |
| 116 case IrOpcode::kJSConvertReceiver: |
| 117 case IrOpcode::kJSToBoolean: |
| 118 case IrOpcode::kJSToInteger: |
| 119 case IrOpcode::kJSToLength: |
| 120 case IrOpcode::kJSToName: |
| 121 case IrOpcode::kJSToNumber: |
| 122 case IrOpcode::kJSToObject: |
| 123 case IrOpcode::kJSToString: |
| 124 case IrOpcode::kJSToPrimitiveToString: |
| 125 return false; |
| 126 case IrOpcode::kHeapConstant: { |
| 127 Handle<HeapObject> value = HeapObjectMatcher(node).Value(); |
| 128 Isolate* const isolate = value->GetIsolate(); |
| 129 return value->IsNull(isolate) || value->IsUndefined(isolate); |
| 130 } |
| 131 default: |
| 132 return true; |
| 133 } |
| 134 } |
| 135 |
| 136 } // namespace |
98 | 137 |
99 // ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray ) | 138 // ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray ) |
100 Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) { | 139 Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) { |
101 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); | 140 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
102 Node* target = NodeProperties::GetValueInput(node, 0); | |
103 CallParameters const& p = CallParametersOf(node->op()); | 141 CallParameters const& p = CallParametersOf(node->op()); |
104 // Tail calls to Function.prototype.apply are not properly supported | 142 // Tail calls to Function.prototype.apply are not properly supported |
105 // down the pipeline, so we disable this optimization completely for | 143 // down the pipeline, so we disable this optimization completely for |
106 // tail calls (for now). | 144 // tail calls (for now). |
107 if (p.tail_call_mode() == TailCallMode::kAllow) return NoChange(); | 145 if (p.tail_call_mode() == TailCallMode::kAllow) return NoChange(); |
108 Handle<JSFunction> apply = | |
109 Handle<JSFunction>::cast(HeapObjectMatcher(target).Value()); | |
110 size_t arity = p.arity(); | 146 size_t arity = p.arity(); |
111 DCHECK_LE(2u, arity); | 147 DCHECK_LE(2u, arity); |
112 ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny; | 148 ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny; |
113 if (arity == 2) { | 149 if (arity == 2) { |
114 // Neither thisArg nor argArray was provided. | 150 // Neither thisArg nor argArray was provided. |
115 convert_mode = ConvertReceiverMode::kNullOrUndefined; | 151 convert_mode = ConvertReceiverMode::kNullOrUndefined; |
116 node->ReplaceInput(0, node->InputAt(1)); | 152 node->ReplaceInput(0, node->InputAt(1)); |
117 node->ReplaceInput(1, jsgraph()->UndefinedConstant()); | 153 node->ReplaceInput(1, jsgraph()->UndefinedConstant()); |
118 } else if (arity == 3) { | 154 } else if (arity == 3) { |
119 // The argArray was not provided, just remove the {target}. | 155 // The argArray was not provided, just remove the {target}. |
120 node->RemoveInput(0); | 156 node->RemoveInput(0); |
121 --arity; | 157 --arity; |
122 } else if (arity == 4) { | 158 } else { |
123 // Check if argArray is an arguments object, and {node} is the only value | 159 Node* target = NodeProperties::GetValueInput(node, 1); |
124 // user of argArray (except for value uses in frame states). | 160 Node* this_argument = NodeProperties::GetValueInput(node, 2); |
125 Node* arg_array = NodeProperties::GetValueInput(node, 3); | 161 Node* arguments_list = NodeProperties::GetValueInput(node, 3); |
126 if (arg_array->opcode() != IrOpcode::kJSCreateArguments) return NoChange(); | 162 Node* context = NodeProperties::GetContextInput(node); |
127 for (Edge edge : arg_array->use_edges()) { | 163 Node* frame_state = NodeProperties::GetFrameStateInput(node); |
128 Node* const user = edge.from(); | 164 Node* effect = NodeProperties::GetEffectInput(node); |
129 if (user == node) continue; | 165 Node* control = NodeProperties::GetControlInput(node); |
130 // Ignore uses as frame state's locals or parameters. | 166 |
131 if (user->opcode() == IrOpcode::kStateValues) continue; | 167 // If {arguments_list} cannot be null or undefined, we don't need |
132 // Ignore uses as frame state's accumulator. | 168 // to expand this {node} to control-flow. |
133 if (user->opcode() == IrOpcode::kFrameState && | 169 if (!CanBeNullOrUndefined(arguments_list)) { |
134 user->InputAt(2) == arg_array) { | 170 // Massage the value inputs appropriately. |
135 continue; | 171 node->ReplaceInput(0, target); |
| 172 node->ReplaceInput(1, this_argument); |
| 173 node->ReplaceInput(2, arguments_list); |
| 174 while (arity-- > 3) node->RemoveInput(3); |
| 175 |
| 176 // Morph the {node} to a {JSCallWithArrayLike}. |
| 177 NodeProperties::ChangeOp(node, |
| 178 javascript()->CallWithArrayLike(p.frequency())); |
| 179 Reduction const reduction = ReduceJSCallWithArrayLike(node); |
| 180 return reduction.Changed() ? reduction : Changed(node); |
| 181 } else { |
| 182 // Check whether {arguments_list} is null. |
| 183 Node* check_null = |
| 184 graph()->NewNode(simplified()->ReferenceEqual(), arguments_list, |
| 185 jsgraph()->NullConstant()); |
| 186 control = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
| 187 check_null, control); |
| 188 Node* if_null = graph()->NewNode(common()->IfTrue(), control); |
| 189 control = graph()->NewNode(common()->IfFalse(), control); |
| 190 |
| 191 // Check whether {arguments_list} is undefined. |
| 192 Node* check_undefined = |
| 193 graph()->NewNode(simplified()->ReferenceEqual(), arguments_list, |
| 194 jsgraph()->UndefinedConstant()); |
| 195 control = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
| 196 check_undefined, control); |
| 197 Node* if_undefined = graph()->NewNode(common()->IfTrue(), control); |
| 198 control = graph()->NewNode(common()->IfFalse(), control); |
| 199 |
| 200 // Lower to {JSCallWithArrayLike} if {arguments_list} is neither null |
| 201 // nor undefined. |
| 202 Node* effect0 = effect; |
| 203 Node* control0 = control; |
| 204 Node* value0 = effect0 = control0 = graph()->NewNode( |
| 205 javascript()->CallWithArrayLike(p.frequency()), target, this_argument, |
| 206 arguments_list, context, frame_state, effect0, control0); |
| 207 |
| 208 // Lower to {JSCall} if {arguments_list} is either null or undefined. |
| 209 Node* effect1 = effect; |
| 210 Node* control1 = |
| 211 graph()->NewNode(common()->Merge(2), if_null, if_undefined); |
| 212 Node* value1 = effect1 = control1 = |
| 213 graph()->NewNode(javascript()->Call(2), target, this_argument, |
| 214 context, frame_state, effect1, control1); |
| 215 |
| 216 // Rewire potential exception edges. |
| 217 Node* if_exception = nullptr; |
| 218 if (NodeProperties::IsExceptionalCall(node, &if_exception)) { |
| 219 // Create appropriate {IfException} and {IfSuccess} nodes. |
| 220 Node* if_exception0 = |
| 221 graph()->NewNode(common()->IfException(), control0, effect0); |
| 222 control0 = graph()->NewNode(common()->IfSuccess(), control0); |
| 223 Node* if_exception1 = |
| 224 graph()->NewNode(common()->IfException(), control1, effect1); |
| 225 control1 = graph()->NewNode(common()->IfSuccess(), control1); |
| 226 |
| 227 // Join the exception edges. |
| 228 Node* merge = |
| 229 graph()->NewNode(common()->Merge(2), if_exception0, if_exception1); |
| 230 Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception0, |
| 231 if_exception1, merge); |
| 232 Node* phi = |
| 233 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
| 234 if_exception0, if_exception1, merge); |
| 235 ReplaceWithValue(if_exception, phi, ephi, merge); |
136 } | 236 } |
137 if (!NodeProperties::IsValueEdge(edge)) continue; | 237 |
138 return NoChange(); | 238 // Join control paths. |
| 239 control = graph()->NewNode(common()->Merge(2), control0, control1); |
| 240 effect = |
| 241 graph()->NewNode(common()->EffectPhi(2), effect0, effect1, control); |
| 242 Node* value = |
| 243 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
| 244 value0, value1, control); |
| 245 ReplaceWithValue(node, value, effect, control); |
| 246 return Replace(value); |
139 } | 247 } |
140 // Check if the arguments can be handled in the fast case (i.e. we don't | |
141 // have aliased sloppy arguments), and compute the {start_index} for | |
142 // rest parameters. | |
143 CreateArgumentsType const type = CreateArgumentsTypeOf(arg_array->op()); | |
144 Node* frame_state = NodeProperties::GetFrameStateInput(arg_array); | |
145 FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state); | |
146 int start_index = 0; | |
147 // Determine the formal parameter count; | |
148 Handle<SharedFunctionInfo> shared; | |
149 if (!state_info.shared_info().ToHandle(&shared)) return NoChange(); | |
150 int formal_parameter_count = shared->internal_formal_parameter_count(); | |
151 if (type == CreateArgumentsType::kMappedArguments) { | |
152 // Mapped arguments (sloppy mode) that are aliased can only be handled | |
153 // here if there's no side-effect between the {node} and the {arg_array}. | |
154 // TODO(turbofan): Further relax this constraint. | |
155 if (formal_parameter_count != 0) { | |
156 Node* effect = NodeProperties::GetEffectInput(node); | |
157 while (effect != arg_array) { | |
158 if (effect->op()->EffectInputCount() != 1 || | |
159 !(effect->op()->properties() & Operator::kNoWrite)) { | |
160 return NoChange(); | |
161 } | |
162 effect = NodeProperties::GetEffectInput(effect); | |
163 } | |
164 } | |
165 } else if (type == CreateArgumentsType::kRestParameter) { | |
166 start_index = formal_parameter_count; | |
167 } | |
168 // Check if are applying to inlined arguments or to the arguments of | |
169 // the outermost function. | |
170 Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput); | |
171 if (outer_state->opcode() != IrOpcode::kFrameState) { | |
172 // Reduce {node} to a JSCallForwardVarargs operation, which just | |
173 // re-pushes the incoming arguments and calls the {target}. | |
174 node->RemoveInput(0); // Function.prototype.apply | |
175 node->RemoveInput(2); // arguments | |
176 NodeProperties::ChangeOp(node, javascript()->CallForwardVarargs( | |
177 2, start_index, p.tail_call_mode())); | |
178 return Changed(node); | |
179 } | |
180 // Get to the actual frame state from which to extract the arguments; | |
181 // we can only optimize this in case the {node} was already inlined into | |
182 // some other function (and same for the {arg_array}). | |
183 FrameStateInfo outer_info = OpParameter<FrameStateInfo>(outer_state); | |
184 if (outer_info.type() == FrameStateType::kArgumentsAdaptor) { | |
185 // Need to take the parameters from the arguments adaptor. | |
186 frame_state = outer_state; | |
187 } | |
188 // Remove the argArray input from the {node}. | |
189 node->RemoveInput(static_cast<int>(--arity)); | |
190 // Add the actual parameters to the {node}, skipping the receiver, | |
191 // starting from {start_index}. | |
192 Node* const parameters = frame_state->InputAt(kFrameStateParametersInput); | |
193 for (int i = start_index + 1; i < parameters->InputCount(); ++i) { | |
194 node->InsertInput(graph()->zone(), static_cast<int>(arity), | |
195 parameters->InputAt(i)); | |
196 ++arity; | |
197 } | |
198 // Drop the {target} from the {node}. | |
199 node->RemoveInput(0); | |
200 --arity; | |
201 } else { | |
202 return NoChange(); | |
203 } | 248 } |
204 // Change {node} to the new {JSCall} operator. | 249 // Change {node} to the new {JSCall} operator. |
205 NodeProperties::ChangeOp( | 250 NodeProperties::ChangeOp( |
206 node, | 251 node, |
207 javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode, | 252 javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode, |
208 p.tail_call_mode())); | 253 p.tail_call_mode())); |
209 // Change context of {node} to the Function.prototype.apply context, | |
210 // to ensure any exception is thrown in the correct context. | |
211 NodeProperties::ReplaceContextInput( | |
212 node, jsgraph()->HeapConstant(handle(apply->context(), isolate()))); | |
213 // Try to further reduce the JSCall {node}. | 254 // Try to further reduce the JSCall {node}. |
214 Reduction const reduction = ReduceJSCall(node); | 255 Reduction const reduction = ReduceJSCall(node); |
215 return reduction.Changed() ? reduction : Changed(node); | 256 return reduction.Changed() ? reduction : Changed(node); |
216 } | 257 } |
217 | 258 |
218 | 259 |
219 // ES6 section 19.2.3.3 Function.prototype.call (thisArg, ...args) | 260 // ES6 section 19.2.3.3 Function.prototype.call (thisArg, ...args) |
220 Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) { | 261 Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) { |
221 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); | 262 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
222 CallParameters const& p = CallParametersOf(node->op()); | 263 CallParameters const& p = CallParametersOf(node->op()); |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
367 // JSHasInPrototypeChain operator immediately aborts and yields false. | 408 // JSHasInPrototypeChain operator immediately aborts and yields false. |
368 NodeProperties::ReplaceValueInput(node, value, 0); | 409 NodeProperties::ReplaceValueInput(node, value, 0); |
369 NodeProperties::ReplaceValueInput(node, receiver, 1); | 410 NodeProperties::ReplaceValueInput(node, receiver, 1); |
370 for (int i = node->op()->ValueInputCount(); i-- > 2;) { | 411 for (int i = node->op()->ValueInputCount(); i-- > 2;) { |
371 node->RemoveInput(i); | 412 node->RemoveInput(i); |
372 } | 413 } |
373 NodeProperties::ChangeOp(node, javascript()->HasInPrototypeChain()); | 414 NodeProperties::ChangeOp(node, javascript()->HasInPrototypeChain()); |
374 return Changed(node); | 415 return Changed(node); |
375 } | 416 } |
376 | 417 |
| 418 // ES6 section 26.1.1 Reflect.apply ( target, thisArgument, argumentsList ) |
| 419 Reduction JSCallReducer::ReduceReflectApply(Node* node) { |
| 420 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
| 421 CallParameters const& p = CallParametersOf(node->op()); |
| 422 int arity = static_cast<int>(p.arity() - 2); |
| 423 DCHECK_LE(0, arity); |
| 424 // Massage value inputs appropriately. |
| 425 node->RemoveInput(0); |
| 426 node->RemoveInput(0); |
| 427 while (arity < 3) { |
| 428 node->InsertInput(graph()->zone(), arity++, jsgraph()->UndefinedConstant()); |
| 429 } |
| 430 while (arity-- > 3) { |
| 431 node->RemoveInput(arity); |
| 432 } |
| 433 NodeProperties::ChangeOp(node, |
| 434 javascript()->CallWithArrayLike(p.frequency())); |
| 435 Reduction const reduction = ReduceJSCallWithArrayLike(node); |
| 436 return reduction.Changed() ? reduction : Changed(node); |
| 437 } |
| 438 |
377 // ES6 section 26.1.7 Reflect.getPrototypeOf ( target ) | 439 // ES6 section 26.1.7 Reflect.getPrototypeOf ( target ) |
378 Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) { | 440 Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) { |
379 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); | 441 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
380 Node* target = (node->op()->ValueInputCount() >= 3) | 442 Node* target = (node->op()->ValueInputCount() >= 3) |
381 ? NodeProperties::GetValueInput(node, 2) | 443 ? NodeProperties::GetValueInput(node, 2) |
382 : jsgraph()->UndefinedConstant(); | 444 : jsgraph()->UndefinedConstant(); |
383 return ReduceObjectGetPrototype(node, target); | 445 return ReduceObjectGetPrototype(node, target); |
384 } | 446 } |
385 | 447 |
386 Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function, | 448 Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function, |
(...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
591 jsgraph()->HeapConstant(stub.GetCode())); | 653 jsgraph()->HeapConstant(stub.GetCode())); |
592 node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(data)); | 654 node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(data)); |
593 node->InsertInput(graph()->zone(), 3, holder); | 655 node->InsertInput(graph()->zone(), 3, holder); |
594 node->InsertInput(graph()->zone(), 4, | 656 node->InsertInput(graph()->zone(), 4, |
595 jsgraph()->ExternalConstant(function_reference)); | 657 jsgraph()->ExternalConstant(function_reference)); |
596 node->ReplaceInput(5, receiver); | 658 node->ReplaceInput(5, receiver); |
597 NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); | 659 NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); |
598 return Changed(node); | 660 return Changed(node); |
599 } | 661 } |
600 | 662 |
601 Reduction JSCallReducer::ReduceSpreadCall(Node* node, int arity) { | 663 Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread( |
602 DCHECK(node->opcode() == IrOpcode::kJSCallWithSpread || | 664 Node* node, int arity, CallFrequency const& frequency) { |
| 665 DCHECK(node->opcode() == IrOpcode::kJSCallWithArrayLike || |
| 666 node->opcode() == IrOpcode::kJSCallWithSpread || |
603 node->opcode() == IrOpcode::kJSConstructWithSpread); | 667 node->opcode() == IrOpcode::kJSConstructWithSpread); |
604 | 668 |
605 // Do check to make sure we can actually avoid iteration. | 669 // In case of a call/construct with spread, we need to |
606 if (!isolate()->initial_array_iterator_prototype_map()->is_stable()) { | 670 // ensure that it's safe to avoid the actual iteration. |
| 671 if ((node->opcode() == IrOpcode::kJSCallWithSpread || |
| 672 node->opcode() == IrOpcode::kJSConstructWithSpread) && |
| 673 !isolate()->initial_array_iterator_prototype_map()->is_stable()) { |
607 return NoChange(); | 674 return NoChange(); |
608 } | 675 } |
609 | 676 |
610 Node* spread = NodeProperties::GetValueInput(node, arity); | 677 // Check if {arguments_list} is an arguments object, and {node} is the only |
611 | 678 // value user of {arguments_list} (except for value uses in frame states). |
612 // Check if spread is an arguments object, and {node} is the only value user | 679 Node* arguments_list = NodeProperties::GetValueInput(node, arity); |
613 // of spread (except for value uses in frame states). | 680 if (arguments_list->opcode() != IrOpcode::kJSCreateArguments) |
614 if (spread->opcode() != IrOpcode::kJSCreateArguments) return NoChange(); | 681 return NoChange(); |
615 for (Edge edge : spread->use_edges()) { | 682 for (Edge edge : arguments_list->use_edges()) { |
616 Node* const user = edge.from(); | 683 Node* const user = edge.from(); |
617 if (user == node) continue; | 684 if (user == node) continue; |
618 // Ignore uses as frame state's locals or parameters. | 685 // Ignore uses as frame state's locals or parameters. |
619 if (user->opcode() == IrOpcode::kStateValues) continue; | 686 if (user->opcode() == IrOpcode::kStateValues) continue; |
620 // Ignore uses as frame state's accumulator. | 687 // Ignore uses as frame state's accumulator. |
621 if (user->opcode() == IrOpcode::kFrameState && user->InputAt(2) == spread) { | 688 if (user->opcode() == IrOpcode::kFrameState && |
| 689 user->InputAt(2) == arguments_list) { |
622 continue; | 690 continue; |
623 } | 691 } |
624 if (!NodeProperties::IsValueEdge(edge)) continue; | 692 if (!NodeProperties::IsValueEdge(edge)) continue; |
625 return NoChange(); | 693 return NoChange(); |
626 } | 694 } |
627 | 695 |
628 // Get to the actual frame state from which to extract the arguments; | 696 // Get to the actual frame state from which to extract the arguments; |
629 // we can only optimize this in case the {node} was already inlined into | 697 // we can only optimize this in case the {node} was already inlined into |
630 // some other function (and same for the {spread}). | 698 // some other function (and same for the {arguments_list}). |
631 CreateArgumentsType const type = CreateArgumentsTypeOf(spread->op()); | 699 CreateArgumentsType const type = CreateArgumentsTypeOf(arguments_list->op()); |
632 Node* frame_state = NodeProperties::GetFrameStateInput(spread); | 700 Node* frame_state = NodeProperties::GetFrameStateInput(arguments_list); |
633 FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state); | 701 FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state); |
634 int start_index = 0; | 702 int start_index = 0; |
635 // Determine the formal parameter count; | 703 // Determine the formal parameter count; |
636 Handle<SharedFunctionInfo> shared; | 704 Handle<SharedFunctionInfo> shared; |
637 if (!state_info.shared_info().ToHandle(&shared)) return NoChange(); | 705 if (!state_info.shared_info().ToHandle(&shared)) return NoChange(); |
638 int formal_parameter_count = shared->internal_formal_parameter_count(); | 706 int formal_parameter_count = shared->internal_formal_parameter_count(); |
639 if (type == CreateArgumentsType::kMappedArguments) { | 707 if (type == CreateArgumentsType::kMappedArguments) { |
640 // Mapped arguments (sloppy mode) that are aliased can only be handled | 708 // Mapped arguments (sloppy mode) that are aliased can only be handled |
641 // here if there's no side-effect between the {node} and the {arg_array}. | 709 // here if there's no side-effect between the {node} and the {arg_array}. |
642 // TODO(turbofan): Further relax this constraint. | 710 // TODO(turbofan): Further relax this constraint. |
643 if (formal_parameter_count != 0) { | 711 if (formal_parameter_count != 0) { |
644 Node* effect = NodeProperties::GetEffectInput(node); | 712 Node* effect = NodeProperties::GetEffectInput(node); |
645 while (effect != spread) { | 713 while (effect != arguments_list) { |
646 if (effect->op()->EffectInputCount() != 1 || | 714 if (effect->op()->EffectInputCount() != 1 || |
647 !(effect->op()->properties() & Operator::kNoWrite)) { | 715 !(effect->op()->properties() & Operator::kNoWrite)) { |
648 return NoChange(); | 716 return NoChange(); |
649 } | 717 } |
650 effect = NodeProperties::GetEffectInput(effect); | 718 effect = NodeProperties::GetEffectInput(effect); |
651 } | 719 } |
652 } | 720 } |
653 } else if (type == CreateArgumentsType::kRestParameter) { | 721 } else if (type == CreateArgumentsType::kRestParameter) { |
654 start_index = formal_parameter_count; | 722 start_index = formal_parameter_count; |
655 | 723 |
656 // Only check the array iterator protector when we have a rest object. | 724 // For spread calls/constructs with rest parameters we need to ensure that |
657 if (!isolate()->IsArrayIteratorLookupChainIntact()) return NoChange(); | 725 // the array iterator protector is intact, which guards that the rest |
| 726 // parameter iteration is not observable. |
| 727 if (node->opcode() == IrOpcode::kJSCallWithSpread || |
| 728 node->opcode() == IrOpcode::kJSConstructWithSpread) { |
| 729 if (!isolate()->IsArrayIteratorLookupChainIntact()) return NoChange(); |
| 730 dependencies()->AssumePropertyCell(factory()->array_iterator_protector()); |
| 731 } |
658 } | 732 } |
659 | 733 |
660 // Install appropriate code dependencies. | 734 // For call/construct with spread, we need to also install a code |
661 dependencies()->AssumeMapStable( | 735 // dependency on the initial %ArrayIteratorPrototype% map here to |
662 isolate()->initial_array_iterator_prototype_map()); | 736 // ensure that no one messes with the next method. |
663 if (type == CreateArgumentsType::kRestParameter) { | 737 if (node->opcode() == IrOpcode::kJSCallWithSpread || |
664 dependencies()->AssumePropertyCell(factory()->array_iterator_protector()); | 738 node->opcode() == IrOpcode::kJSConstructWithSpread) { |
| 739 dependencies()->AssumeMapStable( |
| 740 isolate()->initial_array_iterator_prototype_map()); |
665 } | 741 } |
666 // Remove the spread input from the {node}. | 742 |
| 743 // Remove the {arguments_list} input from the {node}. |
667 node->RemoveInput(arity--); | 744 node->RemoveInput(arity--); |
668 // Check if are spreading to inlined arguments or to the arguments of | 745 // Check if are spreading to inlined arguments or to the arguments of |
669 // the outermost function. | 746 // the outermost function. |
670 Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput); | 747 Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput); |
671 if (outer_state->opcode() != IrOpcode::kFrameState) { | 748 if (outer_state->opcode() != IrOpcode::kFrameState) { |
672 Operator const* op = | 749 Operator const* op = |
673 (node->opcode() == IrOpcode::kJSCallWithSpread) | 750 (node->opcode() == IrOpcode::kJSCallWithArrayLike || |
| 751 node->opcode() == IrOpcode::kJSCallWithSpread) |
674 ? javascript()->CallForwardVarargs(arity + 1, start_index, | 752 ? javascript()->CallForwardVarargs(arity + 1, start_index, |
675 TailCallMode::kDisallow) | 753 TailCallMode::kDisallow) |
676 : javascript()->ConstructForwardVarargs(arity + 2, start_index); | 754 : javascript()->ConstructForwardVarargs(arity + 2, start_index); |
677 NodeProperties::ChangeOp(node, op); | 755 NodeProperties::ChangeOp(node, op); |
678 return Changed(node); | 756 return Changed(node); |
679 } | 757 } |
680 // Get to the actual frame state from which to extract the arguments; | 758 // Get to the actual frame state from which to extract the arguments; |
681 // we can only optimize this in case the {node} was already inlined into | 759 // we can only optimize this in case the {node} was already inlined into |
682 // some other function (and same for the {arg_array}). | 760 // some other function (and same for the {arg_array}). |
683 FrameStateInfo outer_info = OpParameter<FrameStateInfo>(outer_state); | 761 FrameStateInfo outer_info = OpParameter<FrameStateInfo>(outer_state); |
684 if (outer_info.type() == FrameStateType::kArgumentsAdaptor) { | 762 if (outer_info.type() == FrameStateType::kArgumentsAdaptor) { |
685 // Need to take the parameters from the arguments adaptor. | 763 // Need to take the parameters from the arguments adaptor. |
686 frame_state = outer_state; | 764 frame_state = outer_state; |
687 } | 765 } |
688 // Add the actual parameters to the {node}, skipping the receiver. | 766 // Add the actual parameters to the {node}, skipping the receiver. |
689 Node* const parameters = frame_state->InputAt(kFrameStateParametersInput); | 767 Node* const parameters = frame_state->InputAt(kFrameStateParametersInput); |
690 for (int i = start_index + 1; i < parameters->InputCount(); ++i) { | 768 for (int i = start_index + 1; i < parameters->InputCount(); ++i) { |
691 node->InsertInput(graph()->zone(), static_cast<int>(++arity), | 769 node->InsertInput(graph()->zone(), static_cast<int>(++arity), |
692 parameters->InputAt(i)); | 770 parameters->InputAt(i)); |
693 } | 771 } |
694 | 772 |
695 // TODO(turbofan): Collect call counts on spread call/construct and thread it | 773 if (node->opcode() == IrOpcode::kJSCallWithArrayLike || |
696 // through here. | 774 node->opcode() == IrOpcode::kJSCallWithSpread) { |
697 if (node->opcode() == IrOpcode::kJSCallWithSpread) { | 775 NodeProperties::ChangeOp(node, javascript()->Call(arity + 1, frequency)); |
698 NodeProperties::ChangeOp(node, javascript()->Call(arity + 1)); | 776 Reduction const reduction = ReduceJSCall(node); |
699 Reduction const r = ReduceJSCall(node); | 777 return reduction.Changed() ? reduction : Changed(node); |
700 return r.Changed() ? r : Changed(node); | |
701 } else { | 778 } else { |
702 NodeProperties::ChangeOp(node, javascript()->Construct(arity + 2)); | 779 NodeProperties::ChangeOp(node, |
703 Reduction const r = ReduceJSConstruct(node); | 780 javascript()->Construct(arity + 2, frequency)); |
704 return r.Changed() ? r : Changed(node); | 781 Reduction const reduction = ReduceJSConstruct(node); |
| 782 return reduction.Changed() ? reduction : Changed(node); |
705 } | 783 } |
706 } | 784 } |
707 | 785 |
708 namespace { | 786 namespace { |
709 | 787 |
710 bool ShouldUseCallICFeedback(Node* node) { | 788 bool ShouldUseCallICFeedback(Node* node) { |
711 HeapObjectMatcher m(node); | 789 HeapObjectMatcher m(node); |
712 if (m.HasValue() || m.IsJSCreateClosure()) { | 790 if (m.HasValue() || m.IsJSCreateClosure()) { |
713 // Don't use CallIC feedback when we know the function | 791 // Don't use CallIC feedback when we know the function |
714 // being called, i.e. either know the closure itself or | 792 // being called, i.e. either know the closure itself or |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
768 case Builtins::kFunctionPrototypeHasInstance: | 846 case Builtins::kFunctionPrototypeHasInstance: |
769 return ReduceFunctionPrototypeHasInstance(node); | 847 return ReduceFunctionPrototypeHasInstance(node); |
770 case Builtins::kNumberConstructor: | 848 case Builtins::kNumberConstructor: |
771 return ReduceNumberConstructor(node); | 849 return ReduceNumberConstructor(node); |
772 case Builtins::kObjectGetPrototypeOf: | 850 case Builtins::kObjectGetPrototypeOf: |
773 return ReduceObjectGetPrototypeOf(node); | 851 return ReduceObjectGetPrototypeOf(node); |
774 case Builtins::kObjectPrototypeGetProto: | 852 case Builtins::kObjectPrototypeGetProto: |
775 return ReduceObjectPrototypeGetProto(node); | 853 return ReduceObjectPrototypeGetProto(node); |
776 case Builtins::kObjectPrototypeIsPrototypeOf: | 854 case Builtins::kObjectPrototypeIsPrototypeOf: |
777 return ReduceObjectPrototypeIsPrototypeOf(node); | 855 return ReduceObjectPrototypeIsPrototypeOf(node); |
| 856 case Builtins::kReflectApply: |
| 857 return ReduceReflectApply(node); |
778 case Builtins::kReflectGetPrototypeOf: | 858 case Builtins::kReflectGetPrototypeOf: |
779 return ReduceReflectGetPrototypeOf(node); | 859 return ReduceReflectGetPrototypeOf(node); |
780 case Builtins::kArrayForEach: | 860 case Builtins::kArrayForEach: |
781 return ReduceArrayForEach(function, node); | 861 return ReduceArrayForEach(function, node); |
782 case Builtins::kReturnReceiver: | 862 case Builtins::kReturnReceiver: |
783 return ReduceReturnReceiver(node); | 863 return ReduceReturnReceiver(node); |
784 default: | 864 default: |
785 break; | 865 break; |
786 } | 866 } |
787 | 867 |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
898 NodeProperties::ReplaceEffectInput(node, effect); | 978 NodeProperties::ReplaceEffectInput(node, effect); |
899 | 979 |
900 // Try to further reduce the JSCall {node}. | 980 // Try to further reduce the JSCall {node}. |
901 Reduction const reduction = ReduceJSCall(node); | 981 Reduction const reduction = ReduceJSCall(node); |
902 return reduction.Changed() ? reduction : Changed(node); | 982 return reduction.Changed() ? reduction : Changed(node); |
903 } | 983 } |
904 } | 984 } |
905 return NoChange(); | 985 return NoChange(); |
906 } | 986 } |
907 | 987 |
| 988 Reduction JSCallReducer::ReduceJSCallWithArrayLike(Node* node) { |
| 989 DCHECK_EQ(IrOpcode::kJSCallWithArrayLike, node->opcode()); |
| 990 CallFrequency frequency = CallFrequencyOf(node->op()); |
| 991 return ReduceCallOrConstructWithArrayLikeOrSpread(node, 2, frequency); |
| 992 } |
| 993 |
908 Reduction JSCallReducer::ReduceJSCallWithSpread(Node* node) { | 994 Reduction JSCallReducer::ReduceJSCallWithSpread(Node* node) { |
909 DCHECK_EQ(IrOpcode::kJSCallWithSpread, node->opcode()); | 995 DCHECK_EQ(IrOpcode::kJSCallWithSpread, node->opcode()); |
910 SpreadWithArityParameter const& p = SpreadWithArityParameterOf(node->op()); | 996 SpreadWithArityParameter const& p = SpreadWithArityParameterOf(node->op()); |
911 DCHECK_LE(3u, p.arity()); | 997 DCHECK_LE(3u, p.arity()); |
912 int arity = static_cast<int>(p.arity() - 1); | 998 int arity = static_cast<int>(p.arity() - 1); |
913 | 999 |
914 return ReduceSpreadCall(node, arity); | 1000 // TODO(turbofan): Collect call counts on spread call/construct and thread it |
| 1001 // through here. |
| 1002 CallFrequency frequency; |
| 1003 return ReduceCallOrConstructWithArrayLikeOrSpread(node, arity, frequency); |
915 } | 1004 } |
916 | 1005 |
917 Reduction JSCallReducer::ReduceJSConstruct(Node* node) { | 1006 Reduction JSCallReducer::ReduceJSConstruct(Node* node) { |
918 DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode()); | 1007 DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode()); |
919 ConstructParameters const& p = ConstructParametersOf(node->op()); | 1008 ConstructParameters const& p = ConstructParametersOf(node->op()); |
920 DCHECK_LE(2u, p.arity()); | 1009 DCHECK_LE(2u, p.arity()); |
921 int const arity = static_cast<int>(p.arity() - 2); | 1010 int const arity = static_cast<int>(p.arity() - 2); |
922 Node* target = NodeProperties::GetValueInput(node, 0); | 1011 Node* target = NodeProperties::GetValueInput(node, 0); |
923 Node* new_target = NodeProperties::GetValueInput(node, arity + 1); | 1012 Node* new_target = NodeProperties::GetValueInput(node, arity + 1); |
924 Node* effect = NodeProperties::GetEffectInput(node); | 1013 Node* effect = NodeProperties::GetEffectInput(node); |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1028 | 1117 |
1029 return NoChange(); | 1118 return NoChange(); |
1030 } | 1119 } |
1031 | 1120 |
1032 Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) { | 1121 Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) { |
1033 DCHECK_EQ(IrOpcode::kJSConstructWithSpread, node->opcode()); | 1122 DCHECK_EQ(IrOpcode::kJSConstructWithSpread, node->opcode()); |
1034 SpreadWithArityParameter const& p = SpreadWithArityParameterOf(node->op()); | 1123 SpreadWithArityParameter const& p = SpreadWithArityParameterOf(node->op()); |
1035 DCHECK_LE(3u, p.arity()); | 1124 DCHECK_LE(3u, p.arity()); |
1036 int arity = static_cast<int>(p.arity() - 2); | 1125 int arity = static_cast<int>(p.arity() - 2); |
1037 | 1126 |
1038 return ReduceSpreadCall(node, arity); | 1127 // TODO(turbofan): Collect call counts on spread call/construct and thread it |
| 1128 // through here. |
| 1129 CallFrequency frequency; |
| 1130 return ReduceCallOrConstructWithArrayLikeOrSpread(node, arity, frequency); |
1039 } | 1131 } |
1040 | 1132 |
1041 Reduction JSCallReducer::ReduceReturnReceiver(Node* node) { | 1133 Reduction JSCallReducer::ReduceReturnReceiver(Node* node) { |
1042 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); | 1134 DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
1043 Node* receiver = NodeProperties::GetValueInput(node, 1); | 1135 Node* receiver = NodeProperties::GetValueInput(node, 1); |
1044 ReplaceWithValue(node, receiver); | 1136 ReplaceWithValue(node, receiver); |
1045 return Replace(receiver); | 1137 return Replace(receiver); |
1046 } | 1138 } |
1047 | 1139 |
1048 Graph* JSCallReducer::graph() const { return jsgraph()->graph(); } | 1140 Graph* JSCallReducer::graph() const { return jsgraph()->graph(); } |
(...skipping 15 matching lines...) Expand all Loading... |
1064 return jsgraph()->javascript(); | 1156 return jsgraph()->javascript(); |
1065 } | 1157 } |
1066 | 1158 |
1067 SimplifiedOperatorBuilder* JSCallReducer::simplified() const { | 1159 SimplifiedOperatorBuilder* JSCallReducer::simplified() const { |
1068 return jsgraph()->simplified(); | 1160 return jsgraph()->simplified(); |
1069 } | 1161 } |
1070 | 1162 |
1071 } // namespace compiler | 1163 } // namespace compiler |
1072 } // namespace internal | 1164 } // namespace internal |
1073 } // namespace v8 | 1165 } // namespace v8 |
OLD | NEW |