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