LCOV - code coverage report
Current view: top level - src/compiler - js-call-reducer.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 334 342 97.7 %
Date: 2017-04-26 Functions: 19 25 76.0 %

          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

Generated by: LCOV version 1.10