Line data Source code
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
3 : // found in the LICENSE file.
4 :
5 : #include "src/compiler/js-call-reducer.h"
6 :
7 : #include "src/code-factory.h"
8 : #include "src/code-stubs.h"
9 : #include "src/compilation-dependencies.h"
10 : #include "src/compiler/js-graph.h"
11 : #include "src/compiler/linkage.h"
12 : #include "src/compiler/node-matchers.h"
13 : #include "src/compiler/simplified-operator.h"
14 : #include "src/feedback-vector-inl.h"
15 : #include "src/ic/call-optimization.h"
16 : #include "src/objects-inl.h"
17 :
18 : namespace v8 {
19 : namespace internal {
20 : namespace compiler {
21 :
22 31337080 : Reduction JSCallReducer::Reduce(Node* node) {
23 31337080 : switch (node->opcode()) {
24 : case IrOpcode::kJSConstruct:
25 33918 : return ReduceJSConstruct(node);
26 : case IrOpcode::kJSConstructWithSpread:
27 247 : return ReduceJSConstructWithSpread(node);
28 : case IrOpcode::kJSCall:
29 705472 : return ReduceJSCall(node);
30 : case IrOpcode::kJSCallWithSpread:
31 296 : return ReduceJSCallWithSpread(node);
32 : default:
33 : break;
34 : }
35 : return NoChange();
36 : }
37 :
38 :
39 : // ES6 section 22.1.1 The Array Constructor
40 496 : Reduction JSCallReducer::ReduceArrayConstructor(Node* node) {
41 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
42 248 : Node* target = NodeProperties::GetValueInput(node, 0);
43 496 : CallParameters const& p = CallParametersOf(node->op());
44 :
45 : // Check if we have an allocation site from the CallIC.
46 : Handle<AllocationSite> site;
47 248 : if (p.feedback().IsValid()) {
48 : CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
49 248 : Handle<Object> feedback(nexus.GetFeedback(), isolate());
50 248 : if (feedback->IsAllocationSite()) {
51 179 : site = Handle<AllocationSite>::cast(feedback);
52 : }
53 : }
54 :
55 : // Turn the {node} into a {JSCreateArray} call.
56 : DCHECK_LE(2u, p.arity());
57 248 : size_t const arity = p.arity() - 2;
58 248 : NodeProperties::ReplaceValueInput(node, target, 0);
59 248 : NodeProperties::ReplaceValueInput(node, target, 1);
60 : // TODO(bmeurer): We might need to propagate the tail call mode to
61 : // the JSCreateArray operator, because an Array call in tail call
62 : // position must always properly consume the parent stack frame.
63 248 : NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site));
64 248 : return Changed(node);
65 : }
66 :
67 : // ES6 section 19.3.1.1 Boolean ( value )
68 12 : Reduction JSCallReducer::ReduceBooleanConstructor(Node* node) {
69 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
70 12 : CallParameters const& p = CallParametersOf(node->op());
71 :
72 : // Replace the {node} with a proper {JSToBoolean} operator.
73 : DCHECK_LE(2u, p.arity());
74 : Node* value = (p.arity() == 2) ? jsgraph()->UndefinedConstant()
75 12 : : NodeProperties::GetValueInput(node, 2);
76 12 : Node* context = NodeProperties::GetContextInput(node);
77 : value = graph()->NewNode(javascript()->ToBoolean(ToBooleanHint::kAny), value,
78 24 : context);
79 12 : ReplaceWithValue(node, value);
80 12 : return Replace(value);
81 : }
82 :
83 : // ES6 section 20.1.1 The Number Constructor
84 543 : Reduction JSCallReducer::ReduceNumberConstructor(Node* node) {
85 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
86 542 : CallParameters const& p = CallParametersOf(node->op());
87 :
88 : // Turn the {node} into a {JSToNumber} call.
89 : DCHECK_LE(2u, p.arity());
90 : Node* value = (p.arity() == 2) ? jsgraph()->ZeroConstant()
91 543 : : NodeProperties::GetValueInput(node, 2);
92 542 : NodeProperties::ReplaceValueInputs(node, value);
93 542 : NodeProperties::ChangeOp(node, javascript()->ToNumber());
94 542 : return Changed(node);
95 : }
96 :
97 :
98 : // ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray )
99 1563 : Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
100 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
101 728 : Node* target = NodeProperties::GetValueInput(node, 0);
102 1143 : CallParameters const& p = CallParametersOf(node->op());
103 : // Tail calls to Function.prototype.apply are not properly supported
104 : // down the pipeline, so we disable this optimization completely for
105 : // tail calls (for now).
106 728 : if (p.tail_call_mode() == TailCallMode::kAllow) return NoChange();
107 : Handle<JSFunction> apply =
108 : Handle<JSFunction>::cast(HeapObjectMatcher(target).Value());
109 : size_t arity = p.arity();
110 : DCHECK_LE(2u, arity);
111 : ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny;
112 536 : if (arity == 2) {
113 : // Neither thisArg nor argArray was provided.
114 : convert_mode = ConvertReceiverMode::kNullOrUndefined;
115 7 : node->ReplaceInput(0, node->InputAt(1));
116 7 : node->ReplaceInput(1, jsgraph()->UndefinedConstant());
117 529 : } else if (arity == 3) {
118 : // The argArray was not provided, just remove the {target}.
119 0 : node->RemoveInput(0);
120 0 : --arity;
121 529 : } else if (arity == 4) {
122 : // Check if argArray is an arguments object, and {node} is the only value
123 : // user of argArray (except for value uses in frame states).
124 843 : Node* arg_array = NodeProperties::GetValueInput(node, 3);
125 489 : if (arg_array->opcode() != IrOpcode::kJSCreateArguments) return NoChange();
126 5228 : for (Edge edge : arg_array->use_edges()) {
127 4874 : if (edge.from()->opcode() == IrOpcode::kStateValues) continue;
128 836 : if (!NodeProperties::IsValueEdge(edge)) continue;
129 456 : if (edge.from() == node) continue;
130 86 : return NoChange();
131 : }
132 : // Check if the arguments can be handled in the fast case (i.e. we don't
133 : // have aliased sloppy arguments), and compute the {start_index} for
134 : // rest parameters.
135 354 : CreateArgumentsType const type = CreateArgumentsTypeOf(arg_array->op());
136 354 : Node* frame_state = NodeProperties::GetFrameStateInput(arg_array);
137 354 : FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state);
138 : int start_index = 0;
139 : // Determine the formal parameter count;
140 : Handle<SharedFunctionInfo> shared;
141 354 : if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
142 : int formal_parameter_count = shared->internal_formal_parameter_count();
143 354 : if (type == CreateArgumentsType::kMappedArguments) {
144 : // Mapped arguments (sloppy mode) that are aliased can only be handled
145 : // here if there's no side-effect between the {node} and the {arg_array}.
146 : // TODO(turbofan): Further relax this constraint.
147 259 : if (formal_parameter_count != 0) {
148 386 : Node* effect = NodeProperties::GetEffectInput(node);
149 418 : while (effect != arg_array) {
150 608 : if (effect->op()->EffectInputCount() != 1 ||
151 : !(effect->op()->properties() & Operator::kNoWrite)) {
152 : return NoChange();
153 : }
154 262 : effect = NodeProperties::GetEffectInput(effect);
155 : }
156 : }
157 95 : } else if (type == CreateArgumentsType::kRestParameter) {
158 : start_index = formal_parameter_count;
159 : }
160 : // Check if are applying to inlined arguments or to the arguments of
161 : // the outermost function.
162 308 : Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput);
163 308 : if (outer_state->opcode() != IrOpcode::kFrameState) {
164 : // Reduce {node} to a JSCallForwardVarargs operation, which just
165 : // re-pushes the incoming arguments and calls the {target}.
166 215 : node->RemoveInput(0); // Function.prototype.apply
167 215 : node->RemoveInput(2); // arguments
168 : NodeProperties::ChangeOp(node, javascript()->CallForwardVarargs(
169 430 : start_index, p.tail_call_mode()));
170 : return Changed(node);
171 : }
172 : // Get to the actual frame state from which to extract the arguments;
173 : // we can only optimize this in case the {node} was already inlined into
174 : // some other function (and same for the {arg_array}).
175 93 : FrameStateInfo outer_info = OpParameter<FrameStateInfo>(outer_state);
176 93 : if (outer_info.type() == FrameStateType::kArgumentsAdaptor) {
177 : // Need to take the parameters from the arguments adaptor.
178 : frame_state = outer_state;
179 : }
180 : // Remove the argArray input from the {node}.
181 93 : node->RemoveInput(static_cast<int>(--arity));
182 : // Add the actual parameters to the {node}, skipping the receiver,
183 : // starting from {start_index}.
184 : Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
185 482 : for (int i = start_index + 1; i < parameters->InputCount(); ++i) {
186 : node->InsertInput(graph()->zone(), static_cast<int>(arity),
187 296 : parameters->InputAt(i));
188 148 : ++arity;
189 : }
190 : // Drop the {target} from the {node}.
191 93 : node->RemoveInput(0);
192 93 : --arity;
193 : } else {
194 : return NoChange();
195 : }
196 : // Change {node} to the new {JSCall} operator.
197 : NodeProperties::ChangeOp(
198 : node,
199 : javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode,
200 200 : p.tail_call_mode()));
201 : // Change context of {node} to the Function.prototype.apply context,
202 : // to ensure any exception is thrown in the correct context.
203 : NodeProperties::ReplaceContextInput(
204 100 : node, jsgraph()->HeapConstant(handle(apply->context(), isolate())));
205 : // Try to further reduce the JSCall {node}.
206 100 : Reduction const reduction = ReduceJSCall(node);
207 100 : return reduction.Changed() ? reduction : Changed(node);
208 : }
209 :
210 :
211 : // ES6 section 19.2.3.3 Function.prototype.call (thisArg, ...args)
212 2685 : Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) {
213 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
214 5320 : CallParameters const& p = CallParametersOf(node->op());
215 : Handle<JSFunction> call = Handle<JSFunction>::cast(
216 1330 : HeapObjectMatcher(NodeProperties::GetValueInput(node, 0)).Value());
217 : // Change context of {node} to the Function.prototype.call context,
218 : // to ensure any exception is thrown in the correct context.
219 : NodeProperties::ReplaceContextInput(
220 1330 : node, jsgraph()->HeapConstant(handle(call->context(), isolate())));
221 : // Remove the target from {node} and use the receiver as target instead, and
222 : // the thisArg becomes the new target. If thisArg was not provided, insert
223 : // undefined instead.
224 : size_t arity = p.arity();
225 : DCHECK_LE(2u, arity);
226 : ConvertReceiverMode convert_mode;
227 1330 : if (arity == 2) {
228 : // The thisArg was not provided, use undefined as receiver.
229 : convert_mode = ConvertReceiverMode::kNullOrUndefined;
230 25 : node->ReplaceInput(0, node->InputAt(1));
231 25 : node->ReplaceInput(1, jsgraph()->UndefinedConstant());
232 : } else {
233 : // Just remove the target, which is the first value input.
234 : convert_mode = ConvertReceiverMode::kAny;
235 1305 : node->RemoveInput(0);
236 1305 : --arity;
237 : }
238 : NodeProperties::ChangeOp(
239 : node,
240 : javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode,
241 2660 : p.tail_call_mode()));
242 : // Try to further reduce the JSCall {node}.
243 1330 : Reduction const reduction = ReduceJSCall(node);
244 1330 : return reduction.Changed() ? reduction : Changed(node);
245 : }
246 :
247 : // ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] (V)
248 2128 : Reduction JSCallReducer::ReduceFunctionPrototypeHasInstance(Node* node) {
249 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
250 1064 : Node* receiver = NodeProperties::GetValueInput(node, 1);
251 1064 : Node* object = (node->op()->ValueInputCount() >= 3)
252 : ? NodeProperties::GetValueInput(node, 2)
253 1064 : : jsgraph()->UndefinedConstant();
254 1064 : Node* context = NodeProperties::GetContextInput(node);
255 1064 : Node* frame_state = NodeProperties::GetFrameStateInput(node);
256 1064 : Node* effect = NodeProperties::GetEffectInput(node);
257 1064 : Node* control = NodeProperties::GetControlInput(node);
258 :
259 : // TODO(turbofan): If JSOrdinaryToInstance raises an exception, the
260 : // stack trace doesn't contain the @@hasInstance call; we have the
261 : // corresponding bug in the baseline case. Some massaging of the frame
262 : // state would be necessary here.
263 :
264 : // Morph this {node} into a JSOrdinaryHasInstance node.
265 1064 : node->ReplaceInput(0, receiver);
266 1064 : node->ReplaceInput(1, object);
267 1064 : node->ReplaceInput(2, context);
268 1064 : node->ReplaceInput(3, frame_state);
269 1064 : node->ReplaceInput(4, effect);
270 1064 : node->ReplaceInput(5, control);
271 1064 : node->TrimInputCount(6);
272 1064 : NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance());
273 1064 : return Changed(node);
274 : }
275 :
276 126 : Reduction JSCallReducer::ReduceObjectGetPrototype(Node* node, Node* object) {
277 85 : Node* effect = NodeProperties::GetEffectInput(node);
278 :
279 : // Try to determine the {object} map.
280 : ZoneHandleSet<Map> object_maps;
281 : NodeProperties::InferReceiverMapsResult result =
282 85 : NodeProperties::InferReceiverMaps(object, effect, &object_maps);
283 85 : if (result != NodeProperties::kNoReceiverMaps) {
284 : Handle<Map> candidate_map(
285 34 : object_maps[0]->GetPrototypeChainRootMap(isolate()));
286 : Handle<Object> candidate_prototype(candidate_map->prototype(), isolate());
287 :
288 : // We cannot deal with primitives here.
289 34 : if (candidate_map->IsPrimitiveMap()) return NoChange();
290 :
291 : // Check if we can constant-fold the {candidate_prototype}.
292 88 : for (size_t i = 0; i < object_maps.size(); ++i) {
293 : Handle<Map> const object_map(
294 34 : object_maps[i]->GetPrototypeChainRootMap(isolate()));
295 68 : if (object_map->IsSpecialReceiverMap() ||
296 68 : object_map->has_hidden_prototype() ||
297 : object_map->prototype() != *candidate_prototype) {
298 : // We exclude special receivers, like JSProxy or API objects that
299 : // might require access checks here; we also don't want to deal
300 : // with hidden prototypes at this point.
301 : return NoChange();
302 : }
303 55 : if (result == NodeProperties::kUnreliableReceiverMaps &&
304 : !object_map->is_stable()) {
305 : return NoChange();
306 : }
307 : }
308 27 : if (result == NodeProperties::kUnreliableReceiverMaps) {
309 42 : for (size_t i = 0; i < object_maps.size(); ++i) {
310 14 : dependencies()->AssumeMapStable(object_maps[i]);
311 : }
312 : }
313 27 : Node* value = jsgraph()->Constant(candidate_prototype);
314 27 : ReplaceWithValue(node, value);
315 : return Replace(value);
316 : }
317 :
318 : return NoChange();
319 : }
320 :
321 : // ES6 section 19.1.2.11 Object.getPrototypeOf ( O )
322 65 : Reduction JSCallReducer::ReduceObjectGetPrototypeOf(Node* node) {
323 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
324 65 : Node* object = (node->op()->ValueInputCount() >= 3)
325 : ? NodeProperties::GetValueInput(node, 2)
326 65 : : jsgraph()->UndefinedConstant();
327 65 : return ReduceObjectGetPrototype(node, object);
328 : }
329 :
330 : // ES6 section B.2.2.1.1 get Object.prototype.__proto__
331 13 : Reduction JSCallReducer::ReduceObjectPrototypeGetProto(Node* node) {
332 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
333 13 : Node* receiver = NodeProperties::GetValueInput(node, 1);
334 13 : return ReduceObjectGetPrototype(node, receiver);
335 : }
336 :
337 : // ES6 section 26.1.7 Reflect.getPrototypeOf ( target )
338 7 : Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) {
339 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
340 7 : Node* target = (node->op()->ValueInputCount() >= 3)
341 : ? NodeProperties::GetValueInput(node, 2)
342 7 : : jsgraph()->UndefinedConstant();
343 7 : return ReduceObjectGetPrototype(node, target);
344 : }
345 :
346 9170 : Reduction JSCallReducer::ReduceCallApiFunction(
347 53219 : Node* node, Handle<FunctionTemplateInfo> function_template_info) {
348 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
349 9170 : CallParameters const& p = CallParametersOf(node->op());
350 9170 : int const argc = static_cast<int>(p.arity()) - 2;
351 : Node* receiver = (p.convert_mode() == ConvertReceiverMode::kNullOrUndefined)
352 25614 : ? jsgraph()->HeapConstant(global_proxy())
353 18340 : : NodeProperties::GetValueInput(node, 1);
354 9170 : Node* effect = NodeProperties::GetEffectInput(node);
355 :
356 : // CallApiCallbackStub expects the target in a register, so we count it out,
357 : // and counts the receiver as an implicit argument, so we count the receiver
358 : // out too.
359 9170 : if (argc > CallApiCallbackStub::kArgMax) return NoChange();
360 :
361 : // Infer the {receiver} maps, and check if we can inline the API function
362 : // callback based on those.
363 : ZoneHandleSet<Map> receiver_maps;
364 : NodeProperties::InferReceiverMapsResult result =
365 9168 : NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
366 9168 : if (result == NodeProperties::kNoReceiverMaps) return NoChange();
367 27006 : for (size_t i = 0; i < receiver_maps.size(); ++i) {
368 : Handle<Map> receiver_map = receiver_maps[i];
369 27006 : if (!receiver_map->IsJSObjectMap() ||
370 0 : (!function_template_info->accept_any_receiver() &&
371 : receiver_map->is_access_check_needed())) {
372 : return NoChange();
373 : }
374 : // In case of unreliable {receiver} information, the {receiver_maps}
375 : // must all be stable in order to consume the information.
376 9002 : if (result == NodeProperties::kUnreliableReceiverMaps) {
377 8834 : if (!receiver_map->is_stable()) return NoChange();
378 : }
379 : }
380 :
381 : // See if we can constant-fold the compatible receiver checks.
382 9002 : CallOptimization call_optimization(function_template_info);
383 9002 : if (!call_optimization.is_simple_api_call()) return NoChange();
384 : CallOptimization::HolderLookup lookup;
385 : Handle<JSObject> api_holder =
386 9000 : call_optimization.LookupHolderOfExpectedType(receiver_maps[0], &lookup);
387 9000 : if (lookup == CallOptimization::kHolderNotFound) return NoChange();
388 8997 : for (size_t i = 1; i < receiver_maps.size(); ++i) {
389 : CallOptimization::HolderLookup lookupi;
390 : Handle<JSObject> holder = call_optimization.LookupHolderOfExpectedType(
391 0 : receiver_maps[i], &lookupi);
392 0 : if (lookup != lookupi) return NoChange();
393 0 : if (!api_holder.is_identical_to(holder)) return NoChange();
394 : }
395 :
396 : // Install stability dependencies for unreliable {receiver_maps}.
397 8997 : if (result == NodeProperties::kUnreliableReceiverMaps) {
398 26487 : for (size_t i = 0; i < receiver_maps.size(); ++i) {
399 8829 : dependencies()->AssumeMapStable(receiver_maps[i]);
400 : }
401 : }
402 :
403 : // CallApiCallbackStub's register arguments: code, target, call data, holder,
404 : // function address.
405 : // TODO(turbofan): Consider introducing a JSCallApiCallback operator for
406 : // this and lower it during JSGenericLowering, and unify this with the
407 : // JSNativeContextSpecialization::InlineApiCall method a bit.
408 : Handle<CallHandlerInfo> call_handler_info(
409 : CallHandlerInfo::cast(function_template_info->call_code()), isolate());
410 : Handle<Object> data(call_handler_info->data(), isolate());
411 : CallApiCallbackStub stub(isolate(), argc, false);
412 : CallInterfaceDescriptor cid = stub.GetCallInterfaceDescriptor();
413 : CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor(
414 : isolate(), graph()->zone(), cid,
415 8997 : cid.GetStackParameterCount() + argc + 1 /* implicit receiver */,
416 : CallDescriptor::kNeedsFrameState, Operator::kNoProperties,
417 26991 : MachineType::AnyTagged(), 1);
418 : ApiFunction api_function(v8::ToCData<Address>(call_handler_info->callback()));
419 8997 : Node* holder = lookup == CallOptimization::kHolderFound
420 9004 : ? jsgraph()->HeapConstant(api_holder)
421 17994 : : receiver;
422 : ExternalReference function_reference(
423 8997 : &api_function, ExternalReference::DIRECT_API_CALL, isolate());
424 : node->InsertInput(graph()->zone(), 0,
425 26991 : jsgraph()->HeapConstant(stub.GetCode()));
426 17994 : node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(data));
427 8997 : node->InsertInput(graph()->zone(), 3, holder);
428 : node->InsertInput(graph()->zone(), 4,
429 17994 : jsgraph()->ExternalConstant(function_reference));
430 8997 : node->ReplaceInput(5, receiver);
431 8997 : NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
432 : return Changed(node);
433 : }
434 :
435 709 : Reduction JSCallReducer::ReduceSpreadCall(Node* node, int arity) {
436 : DCHECK(node->opcode() == IrOpcode::kJSCallWithSpread ||
437 : node->opcode() == IrOpcode::kJSConstructWithSpread);
438 :
439 : // Do check to make sure we can actually avoid iteration.
440 1086 : if (!isolate()->initial_array_iterator_prototype_map()->is_stable()) {
441 : return NoChange();
442 : }
443 :
444 873 : Node* spread = NodeProperties::GetValueInput(node, arity);
445 :
446 : // Check if spread is an arguments object, and {node} is the only value user
447 : // of spread (except for value uses in frame states).
448 543 : if (spread->opcode() != IrOpcode::kJSCreateArguments) return NoChange();
449 3680 : for (Edge edge : spread->use_edges()) {
450 3350 : if (edge.from()->opcode() == IrOpcode::kStateValues) continue;
451 712 : if (!NodeProperties::IsValueEdge(edge)) continue;
452 382 : if (edge.from() == node) continue;
453 38 : return NoChange();
454 : }
455 :
456 : // Get to the actual frame state from which to extract the arguments;
457 : // we can only optimize this in case the {node} was already inlined into
458 : // some other function (and same for the {spread}).
459 330 : CreateArgumentsType type = CreateArgumentsTypeOf(spread->op());
460 330 : Node* frame_state = NodeProperties::GetFrameStateInput(spread);
461 330 : Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput);
462 330 : if (outer_state->opcode() != IrOpcode::kFrameState) return NoChange();
463 77 : FrameStateInfo outer_info = OpParameter<FrameStateInfo>(outer_state);
464 77 : if (outer_info.type() == FrameStateType::kArgumentsAdaptor) {
465 : // Need to take the parameters from the arguments adaptor.
466 : frame_state = outer_state;
467 : }
468 77 : FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state);
469 : int start_index = 0;
470 77 : if (type == CreateArgumentsType::kMappedArguments) {
471 : // Mapped arguments (sloppy mode) cannot be handled if they are aliased.
472 : Handle<SharedFunctionInfo> shared;
473 21 : if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
474 21 : if (shared->internal_formal_parameter_count() != 0) return NoChange();
475 56 : } else if (type == CreateArgumentsType::kRestParameter) {
476 : Handle<SharedFunctionInfo> shared;
477 35 : if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
478 : start_index = shared->internal_formal_parameter_count();
479 :
480 : // Only check the array iterator protector when we have a rest object.
481 35 : if (!isolate()->IsArrayIteratorLookupChainIntact()) return NoChange();
482 : // Add a code dependency on the array iterator protector.
483 : dependencies()->AssumePropertyCell(factory()->array_iterator_protector());
484 : }
485 :
486 : dependencies()->AssumeMapStable(
487 134 : isolate()->initial_array_iterator_prototype_map());
488 :
489 67 : node->RemoveInput(arity--);
490 :
491 : // Add the actual parameters to the {node}, skipping the receiver.
492 : Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
493 388 : for (int i = start_index + 1; i < state_info.parameter_count(); ++i) {
494 : node->InsertInput(graph()->zone(), static_cast<int>(++arity),
495 254 : parameters->InputAt(i));
496 : }
497 :
498 67 : if (node->opcode() == IrOpcode::kJSCallWithSpread) {
499 : NodeProperties::ChangeOp(
500 134 : node, javascript()->Call(arity + 1, 7, VectorSlotPair()));
501 : } else {
502 : NodeProperties::ChangeOp(
503 0 : node, javascript()->Construct(arity + 2, 7, VectorSlotPair()));
504 : }
505 : return Changed(node);
506 : }
507 :
508 : namespace {
509 :
510 27220 : bool ShouldUseCallICFeedback(Node* node) {
511 : HeapObjectMatcher m(node);
512 27220 : if (m.HasValue() || m.IsJSCreateClosure()) {
513 : // Don't use CallIC feedback when we know the function
514 : // being called, i.e. either know the closure itself or
515 : // at least the SharedFunctionInfo.
516 : return false;
517 23611 : } else if (m.IsPhi()) {
518 : // Protect against endless loops here.
519 2418 : Node* control = NodeProperties::GetControlInput(node);
520 2418 : if (control->opcode() == IrOpcode::kLoop) return false;
521 : // Check if {node} is a Phi of nodes which shouldn't
522 : // use CallIC feedback (not looking through loops).
523 2392 : int const value_input_count = m.node()->op()->ValueInputCount();
524 4564 : for (int n = 0; n < value_input_count; ++n) {
525 4564 : if (ShouldUseCallICFeedback(node->InputAt(n))) return true;
526 : }
527 : return false;
528 : }
529 : return true;
530 : }
531 :
532 : } // namespace
533 :
534 2178729 : Reduction JSCallReducer::ReduceJSCall(Node* node) {
535 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
536 1085282 : CallParameters const& p = CallParametersOf(node->op());
537 727895 : Node* target = NodeProperties::GetValueInput(node, 0);
538 727895 : Node* control = NodeProperties::GetControlInput(node);
539 727895 : Node* effect = NodeProperties::GetEffectInput(node);
540 :
541 : // Try to specialize JSCall {node}s with constant {target}s.
542 : HeapObjectMatcher m(target);
543 727895 : if (m.HasValue()) {
544 323322 : if (m.Value()->IsJSFunction()) {
545 : Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
546 : Handle<SharedFunctionInfo> shared(function->shared(), isolate());
547 :
548 : // Raise a TypeError if the {target} is a "classConstructor".
549 322948 : if (IsClassConstructor(shared->kind())) {
550 8 : NodeProperties::ReplaceValueInputs(node, target);
551 : NodeProperties::ChangeOp(
552 : node, javascript()->CallRuntime(
553 8 : Runtime::kThrowConstructorNonCallableError, 1));
554 : return Changed(node);
555 : }
556 :
557 : // Don't inline cross native context.
558 322940 : if (function->native_context() != *native_context()) return NoChange();
559 :
560 : // Check for known builtin functions.
561 322922 : switch (shared->code()->builtin_index()) {
562 : case Builtins::kBooleanConstructor:
563 12 : return ReduceBooleanConstructor(node);
564 : case Builtins::kFunctionPrototypeApply:
565 728 : return ReduceFunctionPrototypeApply(node);
566 : case Builtins::kFunctionPrototypeCall:
567 1330 : return ReduceFunctionPrototypeCall(node);
568 : case Builtins::kFunctionPrototypeHasInstance:
569 1064 : return ReduceFunctionPrototypeHasInstance(node);
570 : case Builtins::kNumberConstructor:
571 542 : return ReduceNumberConstructor(node);
572 : case Builtins::kObjectGetPrototypeOf:
573 65 : return ReduceObjectGetPrototypeOf(node);
574 : case Builtins::kObjectPrototypeGetProto:
575 13 : return ReduceObjectPrototypeGetProto(node);
576 : case Builtins::kReflectGetPrototypeOf:
577 7 : return ReduceReflectGetPrototypeOf(node);
578 : default:
579 : break;
580 : }
581 :
582 : // Check for the Array constructor.
583 319161 : if (*function == function->native_context()->array_function()) {
584 218 : return ReduceArrayConstructor(node);
585 : }
586 :
587 637886 : if (!FLAG_runtime_stats && shared->IsApiFunction()) {
588 : Handle<FunctionTemplateInfo> function_template_info(
589 : FunctionTemplateInfo::cast(shared->function_data()), isolate());
590 9170 : return ReduceCallApiFunction(node, function_template_info);
591 : }
592 374 : } else if (m.Value()->IsJSBoundFunction()) {
593 : Handle<JSBoundFunction> function =
594 : Handle<JSBoundFunction>::cast(m.Value());
595 : Handle<JSReceiver> bound_target_function(
596 : function->bound_target_function(), isolate());
597 : Handle<Object> bound_this(function->bound_this(), isolate());
598 : Handle<FixedArray> bound_arguments(function->bound_arguments(),
599 : isolate());
600 636 : CallParameters const& p = CallParametersOf(node->op());
601 : ConvertReceiverMode const convert_mode =
602 : (bound_this->IsNullOrUndefined(isolate()))
603 : ? ConvertReceiverMode::kNullOrUndefined
604 159 : : ConvertReceiverMode::kNotNullOrUndefined;
605 : size_t arity = p.arity();
606 : DCHECK_LE(2u, arity);
607 : // Patch {node} to use [[BoundTargetFunction]] and [[BoundThis]].
608 : NodeProperties::ReplaceValueInput(
609 159 : node, jsgraph()->Constant(bound_target_function), 0);
610 : NodeProperties::ReplaceValueInput(node, jsgraph()->Constant(bound_this),
611 159 : 1);
612 : // Insert the [[BoundArguments]] for {node}.
613 320 : for (int i = 0; i < bound_arguments->length(); ++i) {
614 : node->InsertInput(
615 : graph()->zone(), i + 2,
616 2 : jsgraph()->Constant(handle(bound_arguments->get(i), isolate())));
617 1 : arity++;
618 : }
619 : NodeProperties::ChangeOp(
620 : node,
621 : javascript()->Call(arity, p.frequency(), VectorSlotPair(),
622 318 : convert_mode, p.tail_call_mode()));
623 : // Try to further reduce the JSCall {node}.
624 159 : Reduction const reduction = ReduceJSCall(node);
625 159 : return reduction.Changed() ? reduction : Changed(node);
626 : }
627 :
628 : // Don't mess with other {node}s that have a constant {target}.
629 : // TODO(bmeurer): Also support proxies here.
630 : return NoChange();
631 : }
632 :
633 : // Extract feedback from the {node} using the CallICNexus.
634 404573 : if (!p.feedback().IsValid()) return NoChange();
635 : CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
636 389896 : if (nexus.IsUninitialized()) {
637 : // TODO(turbofan): Tail-calling to a CallIC stub is not supported.
638 357387 : if (p.tail_call_mode() == TailCallMode::kAllow) return NoChange();
639 :
640 : // Insert a CallIC here to collect feedback for uninitialized calls.
641 357373 : int const arg_count = static_cast<int>(p.arity() - 2);
642 357373 : Callable callable = CodeFactory::CallIC(isolate(), p.convert_mode());
643 : CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
644 : CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
645 : isolate(), graph()->zone(), callable.descriptor(), arg_count + 1,
646 1429492 : flags);
647 357373 : Node* stub_code = jsgraph()->HeapConstant(callable.code());
648 357373 : Node* stub_arity = jsgraph()->Constant(arg_count);
649 : Node* slot_index =
650 357373 : jsgraph()->Constant(FeedbackVector::GetIndex(p.feedback().slot()));
651 357373 : Node* feedback_vector = jsgraph()->HeapConstant(p.feedback().vector());
652 357373 : node->InsertInput(graph()->zone(), 0, stub_code);
653 357373 : node->InsertInput(graph()->zone(), 2, stub_arity);
654 357373 : node->InsertInput(graph()->zone(), 3, slot_index);
655 357373 : node->InsertInput(graph()->zone(), 4, feedback_vector);
656 357373 : NodeProperties::ChangeOp(node, common()->Call(desc));
657 : return Changed(node);
658 : }
659 :
660 32509 : Handle<Object> feedback(nexus.GetFeedback(), isolate());
661 32509 : if (feedback->IsAllocationSite()) {
662 : // Retrieve the Array function from the {node}.
663 : Node* array_function = jsgraph()->HeapConstant(
664 30 : handle(native_context()->array_function(), isolate()));
665 :
666 : // Check that the {target} is still the {array_function}.
667 : Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
668 30 : array_function);
669 30 : effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control);
670 :
671 : // Turn the {node} into a {JSCreateArray} call.
672 30 : NodeProperties::ReplaceValueInput(node, array_function, 0);
673 30 : NodeProperties::ReplaceEffectInput(node, effect);
674 30 : return ReduceArrayConstructor(node);
675 32479 : } else if (feedback->IsWeakCell()) {
676 : // Check if we want to use CallIC feedback here.
677 22293 : if (!ShouldUseCallICFeedback(target)) return NoChange();
678 :
679 : Handle<WeakCell> cell = Handle<WeakCell>::cast(feedback);
680 20858 : if (cell->value()->IsJSFunction()) {
681 : Node* target_function =
682 20834 : jsgraph()->Constant(handle(cell->value(), isolate()));
683 :
684 : // Check that the {target} is still the {target_function}.
685 : Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
686 20834 : target_function);
687 : effect =
688 20834 : graph()->NewNode(simplified()->CheckIf(), check, effect, control);
689 :
690 : // Specialize the JSCall node to the {target_function}.
691 20834 : NodeProperties::ReplaceValueInput(node, target_function, 0);
692 20834 : NodeProperties::ReplaceEffectInput(node, effect);
693 :
694 : // Try to further reduce the JSCall {node}.
695 20834 : Reduction const reduction = ReduceJSCall(node);
696 20834 : return reduction.Changed() ? reduction : Changed(node);
697 : }
698 : }
699 : return NoChange();
700 : }
701 :
702 296 : Reduction JSCallReducer::ReduceJSCallWithSpread(Node* node) {
703 : DCHECK_EQ(IrOpcode::kJSCallWithSpread, node->opcode());
704 296 : SpreadWithArityParameter const& p = SpreadWithArityParameterOf(node->op());
705 : DCHECK_LE(3u, p.arity());
706 296 : int arity = static_cast<int>(p.arity() - 1);
707 :
708 296 : return ReduceSpreadCall(node, arity);
709 : }
710 :
711 34609 : Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
712 : DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode());
713 34253 : ConstructParameters const& p = ConstructParametersOf(node->op());
714 : DCHECK_LE(2u, p.arity());
715 34253 : int const arity = static_cast<int>(p.arity() - 2);
716 34253 : Node* target = NodeProperties::GetValueInput(node, 0);
717 34253 : Node* new_target = NodeProperties::GetValueInput(node, arity + 1);
718 34253 : Node* effect = NodeProperties::GetEffectInput(node);
719 34253 : Node* control = NodeProperties::GetControlInput(node);
720 :
721 : // Try to specialize JSConstruct {node}s with constant {target}s.
722 : HeapObjectMatcher m(target);
723 34253 : if (m.HasValue()) {
724 26260 : if (m.Value()->IsJSFunction()) {
725 : Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
726 :
727 : // Raise a TypeError if the {target} is not a constructor.
728 26249 : if (!function->IsConstructor()) {
729 33 : NodeProperties::ReplaceValueInputs(node, target);
730 : NodeProperties::ChangeOp(
731 : node, javascript()->CallRuntime(
732 33 : Runtime::kThrowConstructedNonConstructable));
733 : return Changed(node);
734 : }
735 :
736 : // Don't inline cross native context.
737 26216 : if (function->native_context() != *native_context()) return NoChange();
738 :
739 : // Check for the ArrayConstructor.
740 26214 : if (*function == function->native_context()->array_function()) {
741 : // Check if we have an allocation site.
742 : Handle<AllocationSite> site;
743 3606 : if (p.feedback().IsValid()) {
744 : CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
745 3606 : Handle<Object> feedback(nexus.GetFeedback(), isolate());
746 3606 : if (feedback->IsAllocationSite()) {
747 500 : site = Handle<AllocationSite>::cast(feedback);
748 : }
749 : }
750 :
751 : // Turn the {node} into a {JSCreateArray} call.
752 7034 : for (int i = arity; i > 0; --i) {
753 : NodeProperties::ReplaceValueInput(
754 3428 : node, NodeProperties::GetValueInput(node, i), i + 1);
755 : }
756 3606 : NodeProperties::ReplaceValueInput(node, new_target, 1);
757 7212 : NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site));
758 : return Changed(node);
759 : }
760 : }
761 :
762 : // Don't mess with other {node}s that have a constant {target}.
763 : // TODO(bmeurer): Also support optimizing bound functions and proxies here.
764 : return NoChange();
765 : }
766 :
767 7993 : if (!p.feedback().IsValid()) return NoChange();
768 : CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
769 7993 : Handle<Object> feedback(nexus.GetFeedback(), isolate());
770 7993 : if (feedback->IsAllocationSite()) {
771 : // The feedback is an AllocationSite, which means we have called the
772 : // Array function and collected transition (and pretenuring) feedback
773 : // for the resulting arrays. This has to be kept in sync with the
774 : // implementation of the CallConstructStub.
775 21 : Handle<AllocationSite> site = Handle<AllocationSite>::cast(feedback);
776 :
777 : // Retrieve the Array function from the {node}.
778 : Node* array_function = jsgraph()->HeapConstant(
779 21 : handle(native_context()->array_function(), isolate()));
780 :
781 : // Check that the {target} is still the {array_function}.
782 : Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
783 21 : array_function);
784 21 : effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control);
785 :
786 : // Turn the {node} into a {JSCreateArray} call.
787 21 : NodeProperties::ReplaceEffectInput(node, effect);
788 42 : for (int i = arity; i > 0; --i) {
789 : NodeProperties::ReplaceValueInput(
790 21 : node, NodeProperties::GetValueInput(node, i), i + 1);
791 : }
792 21 : NodeProperties::ReplaceValueInput(node, new_target, 1);
793 42 : NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site));
794 : return Changed(node);
795 7972 : } else if (feedback->IsWeakCell()) {
796 : // Check if we want to use CallIC feedback here.
797 363 : if (!ShouldUseCallICFeedback(target)) return NoChange();
798 :
799 : Handle<WeakCell> cell = Handle<WeakCell>::cast(feedback);
800 335 : if (cell->value()->IsJSFunction()) {
801 : Node* target_function =
802 335 : jsgraph()->Constant(handle(cell->value(), isolate()));
803 :
804 : // Check that the {target} is still the {target_function}.
805 : Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
806 335 : target_function);
807 : effect =
808 335 : graph()->NewNode(simplified()->CheckIf(), check, effect, control);
809 :
810 : // Specialize the JSConstruct node to the {target_function}.
811 335 : NodeProperties::ReplaceValueInput(node, target_function, 0);
812 335 : NodeProperties::ReplaceEffectInput(node, effect);
813 335 : if (target == new_target) {
814 335 : NodeProperties::ReplaceValueInput(node, target_function, arity + 1);
815 : }
816 :
817 : // Try to further reduce the JSConstruct {node}.
818 335 : Reduction const reduction = ReduceJSConstruct(node);
819 335 : return reduction.Changed() ? reduction : Changed(node);
820 : }
821 : }
822 :
823 : return NoChange();
824 : }
825 :
826 247 : Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) {
827 : DCHECK_EQ(IrOpcode::kJSConstructWithSpread, node->opcode());
828 247 : SpreadWithArityParameter const& p = SpreadWithArityParameterOf(node->op());
829 : DCHECK_LE(3u, p.arity());
830 247 : int arity = static_cast<int>(p.arity() - 2);
831 :
832 247 : return ReduceSpreadCall(node, arity);
833 : }
834 :
835 1874578 : Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }
836 :
837 1168461 : Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); }
838 :
839 0 : Factory* JSCallReducer::factory() const { return isolate()->factory(); }
840 :
841 8222 : Handle<JSGlobalProxy> JSCallReducer::global_proxy() const {
842 8222 : return handle(JSGlobalProxy::cast(native_context()->global_proxy()),
843 8222 : isolate());
844 : }
845 :
846 366370 : CommonOperatorBuilder* JSCallReducer::common() const {
847 366370 : return jsgraph()->common();
848 : }
849 :
850 7405 : JSOperatorBuilder* JSCallReducer::javascript() const {
851 7405 : return jsgraph()->javascript();
852 : }
853 :
854 42440 : SimplifiedOperatorBuilder* JSCallReducer::simplified() const {
855 42440 : return jsgraph()->simplified();
856 : }
857 :
858 : } // namespace compiler
859 : } // namespace internal
860 : } // namespace v8
|