Home | History | Annotate | Download | only in compiler
      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 
     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
     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
     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 )
     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)
    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)
    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 
    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 
    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__
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    786 Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }
    787 
    788 Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); }
    789 
    790 Factory* JSCallReducer::factory() const { return isolate()->factory(); }
    791 
    792 CommonOperatorBuilder* JSCallReducer::common() const {
    793   return jsgraph()->common();
    794 }
    795 
    796 JSOperatorBuilder* JSCallReducer::javascript() const {
    797   return jsgraph()->javascript();
    798 }
    799 
    800 SimplifiedOperatorBuilder* JSCallReducer::simplified() const {
    801   return jsgraph()->simplified();
    802 }
    803 
    804 }  // namespace compiler
    805 }  // namespace internal
    806 }  // namespace v8
    807