LCOV - code coverage report
Current view: top level - src/compiler - js-inlining.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 213 225 94.7 %
Date: 2017-04-26 Functions: 10 15 66.7 %

          Line data    Source code
       1             : // Copyright 2014 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-inlining.h"
       6             : 
       7             : #include "src/ast/ast.h"
       8             : #include "src/compilation-info.h"
       9             : #include "src/compiler.h"
      10             : #include "src/compiler/all-nodes.h"
      11             : #include "src/compiler/bytecode-graph-builder.h"
      12             : #include "src/compiler/common-operator.h"
      13             : #include "src/compiler/compiler-source-position-table.h"
      14             : #include "src/compiler/graph-reducer.h"
      15             : #include "src/compiler/js-operator.h"
      16             : #include "src/compiler/node-matchers.h"
      17             : #include "src/compiler/node-properties.h"
      18             : #include "src/compiler/operator-properties.h"
      19             : #include "src/compiler/simplified-operator.h"
      20             : #include "src/isolate-inl.h"
      21             : #include "src/parsing/parse-info.h"
      22             : 
      23             : namespace v8 {
      24             : namespace internal {
      25             : namespace compiler {
      26             : 
      27             : #define TRACE(...)                                      \
      28             :   do {                                                  \
      29             :     if (FLAG_trace_turbo_inlining) PrintF(__VA_ARGS__); \
      30             :   } while (false)
      31             : 
      32             : 
      33             : // Provides convenience accessors for the common layout of nodes having either
      34             : // the {JSCall} or the {JSConstruct} operator.
      35             : class JSCallAccessor {
      36             :  public:
      37       45290 :   explicit JSCallAccessor(Node* call) : call_(call) {
      38             :     DCHECK(call->opcode() == IrOpcode::kJSCall ||
      39             :            call->opcode() == IrOpcode::kJSConstruct);
      40             :   }
      41             : 
      42             :   Node* target() {
      43             :     // Both, {JSCall} and {JSConstruct}, have same layout here.
      44         821 :     return call_->InputAt(0);
      45             :   }
      46             : 
      47             :   Node* receiver() {
      48             :     DCHECK_EQ(IrOpcode::kJSCall, call_->opcode());
      49       61064 :     return call_->InputAt(1);
      50             :   }
      51             : 
      52             :   Node* new_target() {
      53             :     DCHECK_EQ(IrOpcode::kJSConstruct, call_->opcode());
      54         821 :     return call_->InputAt(formal_arguments() + 1);
      55             :   }
      56             : 
      57             :   Node* frame_state() {
      58             :     // Both, {JSCall} and {JSConstruct}, have frame state.
      59       78598 :     return NodeProperties::GetFrameStateInput(call_);
      60             :   }
      61             : 
      62             :   int formal_arguments() {
      63             :     // Both, {JSCall} and {JSConstruct}, have two extra inputs:
      64             :     //  - JSConstruct: Includes target function and new target.
      65             :     //  - JSCall: Includes target function and receiver.
      66       80986 :     return call_->op()->ValueInputCount() - 2;
      67             :   }
      68             : 
      69       38030 :   float frequency() const {
      70       38030 :     return (call_->opcode() == IrOpcode::kJSCall)
      71       37209 :                ? CallParametersOf(call_->op()).frequency()
      72       38851 :                : ConstructParametersOf(call_->op()).frequency();
      73             :   }
      74             : 
      75             :  private:
      76             :   Node* call_;
      77             : };
      78             : 
      79       76060 : Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context,
      80       38030 :                                 Node* frame_state, Node* start, Node* end,
      81             :                                 Node* exception_target,
      82       12085 :                                 const NodeVector& uncaught_subcalls) {
      83             :   // The scheduler is smart enough to place our code; we just ensure {control}
      84             :   // becomes the control input of the start of the inlinee, and {effect} becomes
      85             :   // the effect input of the start of the inlinee.
      86       38030 :   Node* control = NodeProperties::GetControlInput(call);
      87       38030 :   Node* effect = NodeProperties::GetEffectInput(call);
      88             : 
      89             :   int const inlinee_new_target_index =
      90       76060 :       static_cast<int>(start->op()->ValueOutputCount()) - 3;
      91             :   int const inlinee_arity_index =
      92       38030 :       static_cast<int>(start->op()->ValueOutputCount()) - 2;
      93             :   int const inlinee_context_index =
      94       38030 :       static_cast<int>(start->op()->ValueOutputCount()) - 1;
      95             : 
      96             :   // {inliner_inputs} counts JSFunction, receiver, arguments, but not
      97             :   // new target value, argument count, context, effect or control.
      98       38030 :   int inliner_inputs = call->op()->ValueInputCount();
      99             :   // Iterate over all uses of the start node.
     100     1860968 :   for (Edge edge : start->use_edges()) {
     101      911469 :     Node* use = edge.from();
     102      911469 :     switch (use->opcode()) {
     103             :       case IrOpcode::kParameter: {
     104      221978 :         int index = 1 + ParameterIndexOf(use->op());
     105             :         DCHECK_LE(index, inlinee_context_index);
     106      221978 :         if (index < inliner_inputs && index < inlinee_new_target_index) {
     107             :           // There is an input from the call, and the index is a value
     108             :           // projection but not the context, so rewire the input.
     109      286117 :           Replace(use, call->InputAt(index));
     110       48142 :         } else if (index == inlinee_new_target_index) {
     111             :           // The projection is requesting the new target value.
     112             :           Replace(use, new_target);
     113       47913 :         } else if (index == inlinee_arity_index) {
     114             :           // The projection is requesting the number of arguments.
     115           0 :           Replace(use, jsgraph()->Constant(inliner_inputs - 2));
     116       47913 :         } else if (index == inlinee_context_index) {
     117             :           // The projection is requesting the inlinee function context.
     118             :           Replace(use, context);
     119             :         } else {
     120             :           // Call has fewer arguments than required, fill with undefined.
     121        9883 :           Replace(use, jsgraph()->UndefinedConstant());
     122             :         }
     123             :         break;
     124             :       }
     125             :       default:
     126      689491 :         if (NodeProperties::IsEffectEdge(edge)) {
     127       38449 :           edge.UpdateTo(effect);
     128      651042 :         } else if (NodeProperties::IsControlEdge(edge)) {
     129      131245 :           edge.UpdateTo(control);
     130      519797 :         } else if (NodeProperties::IsFrameStateEdge(edge)) {
     131      519797 :           edge.UpdateTo(frame_state);
     132             :         } else {
     133           0 :           UNREACHABLE();
     134             :         }
     135             :         break;
     136             :     }
     137             :   }
     138             : 
     139       38030 :   if (exception_target != nullptr) {
     140             :     // Link uncaught calls in the inlinee to {exception_target}
     141        6018 :     int subcall_count = static_cast<int>(uncaught_subcalls.size());
     142        3009 :     if (subcall_count > 0) {
     143        2964 :       TRACE(
     144             :           "Inlinee contains %d calls without local exception handler; "
     145             :           "linking to surrounding exception handler\n",
     146             :           subcall_count);
     147             :     }
     148        3009 :     NodeVector on_exception_nodes(local_zone_);
     149       20590 :     for (Node* subcall : uncaught_subcalls) {
     150       14572 :       Node* on_success = graph()->NewNode(common()->IfSuccess(), subcall);
     151       14572 :       NodeProperties::ReplaceUses(subcall, subcall, subcall, on_success);
     152       14572 :       NodeProperties::ReplaceControlInput(on_success, subcall);
     153             :       Node* on_exception =
     154       29144 :           graph()->NewNode(common()->IfException(), subcall, subcall);
     155       14572 :       on_exception_nodes.push_back(on_exception);
     156             :     }
     157             : 
     158             :     DCHECK_EQ(subcall_count, static_cast<int>(on_exception_nodes.size()));
     159        3009 :     if (subcall_count > 0) {
     160             :       Node* control_output =
     161             :           graph()->NewNode(common()->Merge(subcall_count), subcall_count,
     162        5928 :                            &on_exception_nodes.front());
     163        2964 :       NodeVector values_effects(local_zone_);
     164             :       values_effects = on_exception_nodes;
     165        2964 :       values_effects.push_back(control_output);
     166             :       Node* value_output = graph()->NewNode(
     167             :           common()->Phi(MachineRepresentation::kTagged, subcall_count),
     168        8892 :           subcall_count + 1, &values_effects.front());
     169             :       Node* effect_output =
     170             :           graph()->NewNode(common()->EffectPhi(subcall_count),
     171        5928 :                            subcall_count + 1, &values_effects.front());
     172             :       ReplaceWithValue(exception_target, value_output, effect_output,
     173        2964 :                        control_output);
     174             :     } else {
     175             :       ReplaceWithValue(exception_target, exception_target, exception_target,
     176          45 :                        jsgraph()->Dead());
     177             :     }
     178             :   }
     179             : 
     180       38030 :   NodeVector values(local_zone_);
     181             :   NodeVector effects(local_zone_);
     182             :   NodeVector controls(local_zone_);
     183      325880 :   for (Node* const input : end->inputs()) {
     184      143925 :     switch (input->opcode()) {
     185             :       case IrOpcode::kReturn:
     186      241650 :         values.push_back(NodeProperties::GetValueInput(input, 1));
     187      241650 :         effects.push_back(NodeProperties::GetEffectInput(input));
     188      241650 :         controls.push_back(NodeProperties::GetControlInput(input));
     189      120825 :         break;
     190             :       case IrOpcode::kDeoptimize:
     191             :       case IrOpcode::kTerminate:
     192             :       case IrOpcode::kThrow:
     193       23100 :         NodeProperties::MergeControlToEnd(graph(), common(), input);
     194       23100 :         Revisit(graph()->end());
     195             :         break;
     196             :       default:
     197           0 :         UNREACHABLE();
     198             :         break;
     199             :     }
     200             :   }
     201             :   DCHECK_EQ(values.size(), effects.size());
     202             :   DCHECK_EQ(values.size(), controls.size());
     203             : 
     204             :   // Depending on whether the inlinee produces a value, we either replace value
     205             :   // uses with said value or kill value uses if no value can be returned.
     206       76060 :   if (values.size() > 0) {
     207       74622 :     int const input_count = static_cast<int>(controls.size());
     208             :     Node* control_output = graph()->NewNode(common()->Merge(input_count),
     209       74622 :                                             input_count, &controls.front());
     210       37311 :     values.push_back(control_output);
     211       37311 :     effects.push_back(control_output);
     212             :     Node* value_output = graph()->NewNode(
     213             :         common()->Phi(MachineRepresentation::kTagged, input_count),
     214      149244 :         static_cast<int>(values.size()), &values.front());
     215             :     Node* effect_output =
     216             :         graph()->NewNode(common()->EffectPhi(input_count),
     217      149244 :                          static_cast<int>(effects.size()), &effects.front());
     218       37311 :     ReplaceWithValue(call, value_output, effect_output, control_output);
     219             :     return Changed(value_output);
     220             :   } else {
     221             :     ReplaceWithValue(call, jsgraph()->Dead(), jsgraph()->Dead(),
     222        2157 :                      jsgraph()->Dead());
     223             :     return Changed(call);
     224             :   }
     225             : }
     226             : 
     227       12531 : Node* JSInliner::CreateArtificialFrameState(Node* node, Node* outer_frame_state,
     228             :                                             int parameter_count,
     229             :                                             BailoutId bailout_id,
     230             :                                             FrameStateType frame_state_type,
     231       12531 :                                             Handle<SharedFunctionInfo> shared) {
     232             :   const FrameStateFunctionInfo* state_info =
     233             :       common()->CreateFrameStateFunctionInfo(frame_state_type,
     234       25062 :                                              parameter_count + 1, 0, shared);
     235             : 
     236             :   const Operator* op = common()->FrameState(
     237       12531 :       bailout_id, OutputFrameStateCombine::Ignore(), state_info);
     238       12531 :   const Operator* op0 = common()->StateValues(0, SparseInputMask::Dense());
     239             :   Node* node0 = graph()->NewNode(op0);
     240       12531 :   NodeVector params(local_zone_);
     241       60257 :   for (int parameter = 0; parameter < parameter_count + 1; ++parameter) {
     242      105585 :     params.push_back(node->InputAt(1 + parameter));
     243             :   }
     244             :   const Operator* op_param = common()->StateValues(
     245       37593 :       static_cast<int>(params.size()), SparseInputMask::Dense());
     246             :   Node* params_node = graph()->NewNode(
     247       37593 :       op_param, static_cast<int>(params.size()), &params.front());
     248             :   return graph()->NewNode(op, params_node, node0, node0,
     249             :                           jsgraph()->UndefinedConstant(), node->InputAt(0),
     250       25062 :                           outer_frame_state);
     251             : }
     252             : 
     253        1095 : Node* JSInliner::CreateTailCallerFrameState(Node* node, Node* frame_state) {
     254         365 :   FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
     255             :   Handle<SharedFunctionInfo> shared;
     256             :   frame_info.shared_info().ToHandle(&shared);
     257             : 
     258             :   Node* function = frame_state->InputAt(kFrameStateFunctionInput);
     259             : 
     260             :   // If we are inlining a tail call drop caller's frame state and an
     261             :   // arguments adaptor if it exists.
     262         365 :   frame_state = NodeProperties::GetFrameStateInput(frame_state);
     263         365 :   if (frame_state->opcode() == IrOpcode::kFrameState) {
     264         272 :     FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
     265         272 :     if (frame_info.type() == FrameStateType::kArgumentsAdaptor) {
     266         196 :       frame_state = NodeProperties::GetFrameStateInput(frame_state);
     267             :     }
     268             :   }
     269             : 
     270             :   const FrameStateFunctionInfo* state_info =
     271             :       common()->CreateFrameStateFunctionInfo(
     272         365 :           FrameStateType::kTailCallerFunction, 0, 0, shared);
     273             : 
     274             :   const Operator* op = common()->FrameState(
     275         365 :       BailoutId(-1), OutputFrameStateCombine::Ignore(), state_info);
     276         365 :   const Operator* op0 = common()->StateValues(0, SparseInputMask::Dense());
     277             :   Node* node0 = graph()->NewNode(op0);
     278             :   return graph()->NewNode(op, node0, node0, node0,
     279             :                           jsgraph()->UndefinedConstant(), function,
     280         730 :                           frame_state);
     281             : }
     282             : 
     283             : namespace {
     284             : 
     285             : // TODO(bmeurer): Unify this with the witness helper functions in the
     286             : // js-builtin-reducer.cc once we have a better understanding of the
     287             : // map tracking we want to do, and eventually changed the CheckMaps
     288             : // operator to carry map constants on the operator instead of inputs.
     289             : // I.e. if the CheckMaps has some kind of SmallMapSet as operator
     290             : // parameter, then this could be changed to call a generic
     291             : //
     292             : //   SmallMapSet NodeProperties::CollectMapWitness(receiver, effect)
     293             : //
     294             : // function, which either returns the map set from the CheckMaps or
     295             : // a singleton set from a StoreField.
     296       31067 : bool NeedsConvertReceiver(Node* receiver, Node* effect) {
     297             :   // Check if the {receiver} is already a JSReceiver.
     298       31067 :   switch (receiver->opcode()) {
     299             :     case IrOpcode::kJSConstruct:
     300             :     case IrOpcode::kJSConstructWithSpread:
     301             :     case IrOpcode::kJSCreate:
     302             :     case IrOpcode::kJSCreateArguments:
     303             :     case IrOpcode::kJSCreateArray:
     304             :     case IrOpcode::kJSCreateClosure:
     305             :     case IrOpcode::kJSCreateIterResultObject:
     306             :     case IrOpcode::kJSCreateKeyValueArray:
     307             :     case IrOpcode::kJSCreateLiteralArray:
     308             :     case IrOpcode::kJSCreateLiteralObject:
     309             :     case IrOpcode::kJSCreateLiteralRegExp:
     310             :     case IrOpcode::kJSConvertReceiver:
     311             :     case IrOpcode::kJSGetSuperConstructor:
     312             :     case IrOpcode::kJSToObject: {
     313             :       return false;
     314             :     }
     315             :     default: {
     316             :       // We don't really care about the exact maps here, just the instance
     317             :       // types, which don't change across potential side-effecting operations.
     318             :       ZoneHandleSet<Map> maps;
     319             :       NodeProperties::InferReceiverMapsResult result =
     320       30925 :           NodeProperties::InferReceiverMaps(receiver, effect, &maps);
     321       30925 :       if (result != NodeProperties::kNoReceiverMaps) {
     322             :         // Check if all {maps} are actually JSReceiver maps.
     323       32634 :         for (size_t i = 0; i < maps.size(); ++i) {
     324       30750 :           if (!maps[i]->IsJSReceiverMap()) return true;
     325             :         }
     326             :         return false;
     327             :       }
     328             :       return true;
     329             :     }
     330             :   }
     331             : }
     332             : 
     333             : // TODO(mstarzinger,verwaest): Move this predicate onto SharedFunctionInfo?
     334         821 : bool NeedsImplicitReceiver(Handle<SharedFunctionInfo> shared_info) {
     335             :   DisallowHeapAllocation no_gc;
     336             :   Isolate* const isolate = shared_info->GetIsolate();
     337             :   Code* const construct_stub = shared_info->construct_stub();
     338        2463 :   return construct_stub != *isolate->builtins()->JSBuiltinsConstructStub() &&
     339             :          construct_stub !=
     340        2463 :              *isolate->builtins()->JSBuiltinsConstructStubForDerived() &&
     341        2463 :          construct_stub != *isolate->builtins()->JSConstructStubApi();
     342             : }
     343             : 
     344         973 : bool IsNonConstructible(Handle<SharedFunctionInfo> shared_info) {
     345             :   DisallowHeapAllocation no_gc;
     346             :   Isolate* const isolate = shared_info->GetIsolate();
     347             :   Code* const construct_stub = shared_info->construct_stub();
     348        1946 :   return construct_stub == *isolate->builtins()->ConstructedNonConstructable();
     349             : }
     350             : 
     351             : }  // namespace
     352             : 
     353             : // Determines whether the call target of the given call {node} is statically
     354             : // known and can be used as an inlining candidate. The {SharedFunctionInfo} of
     355             : // the call target is provided (the exact closure might be unknown).
     356       45290 : bool JSInliner::DetermineCallTarget(
     357             :     Node* node, Handle<SharedFunctionInfo>& shared_info_out) {
     358             :   DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
     359             :   HeapObjectMatcher match(node->InputAt(0));
     360             : 
     361             :   // This reducer can handle both normal function calls as well a constructor
     362             :   // calls whenever the target is a constant function object, as follows:
     363             :   //  - JSCall(target:constant, receiver, args...)
     364             :   //  - JSConstruct(target:constant, args..., new.target)
     365       90126 :   if (match.HasValue() && match.Value()->IsJSFunction()) {
     366             :     Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value());
     367             : 
     368             :     // Disallow cross native-context inlining for now. This means that all parts
     369             :     // of the resulting code will operate on the same global object. This also
     370             :     // prevents cross context leaks, where we could inline functions from a
     371             :     // different context and hold on to that context (and closure) from the code
     372             :     // object.
     373             :     // TODO(turbofan): We might want to revisit this restriction later when we
     374             :     // have a need for this, and we know how to model different native contexts
     375             :     // in the same graph in a compositional way.
     376       44836 :     if (function->context()->native_context() !=
     377       44836 :         info_->context()->native_context()) {
     378             :       return false;
     379             :     }
     380             : 
     381       44836 :     shared_info_out = handle(function->shared());
     382       44836 :     return true;
     383             :   }
     384             : 
     385             :   // This reducer can also handle calls where the target is statically known to
     386             :   // be the result of a closure instantiation operation, as follows:
     387             :   //  - JSCall(JSCreateClosure[shared](context), receiver, args...)
     388             :   //  - JSConstruct(JSCreateClosure[shared](context), args..., new.target)
     389         454 :   if (match.IsJSCreateClosure()) {
     390         454 :     CreateClosureParameters const& p = CreateClosureParametersOf(match.op());
     391             : 
     392             :     // Disallow inlining in case the instantiation site was never run and hence
     393             :     // the vector cell does not contain a valid feedback vector for the call
     394             :     // target.
     395             :     // TODO(turbofan): We might consider to eagerly create the feedback vector
     396             :     // in such a case (in {DetermineCallContext} below) eventually.
     397             :     FeedbackSlot slot = p.feedback().slot();
     398             :     Handle<Cell> cell(Cell::cast(p.feedback().vector()->Get(slot)));
     399         454 :     if (!cell->value()->IsFeedbackVector()) return false;
     400             : 
     401         454 :     shared_info_out = p.shared_info();
     402         454 :     return true;
     403             :   }
     404             : 
     405             :   return false;
     406             : }
     407             : 
     408             : // Determines statically known information about the call target (assuming that
     409             : // the call target is known according to {DetermineCallTarget} above). The
     410             : // following static information is provided:
     411             : //  - context         : The context (as SSA value) bound by the call target.
     412             : //  - feedback_vector : The target is guaranteed to use this feedback vector.
     413       38030 : void JSInliner::DetermineCallContext(
     414             :     Node* node, Node*& context_out,
     415       37686 :     Handle<FeedbackVector>& feedback_vector_out) {
     416             :   DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
     417             :   HeapObjectMatcher match(node->InputAt(0));
     418             : 
     419       75716 :   if (match.HasValue() && match.Value()->IsJSFunction()) {
     420             :     Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value());
     421             : 
     422             :     // If the target function was never invoked, its literals array might not
     423             :     // contain a feedback vector. We ensure at this point that it is created.
     424       37686 :     JSFunction::EnsureLiterals(function);
     425             : 
     426             :     // The inlinee specializes to the context from the JSFunction object.
     427       37686 :     context_out = jsgraph()->Constant(handle(function->context()));
     428       37686 :     feedback_vector_out = handle(function->feedback_vector());
     429             :     return;
     430             :   }
     431             : 
     432         344 :   if (match.IsJSCreateClosure()) {
     433         344 :     CreateClosureParameters const& p = CreateClosureParametersOf(match.op());
     434             : 
     435             :     // Load the feedback vector of the target by looking up its vector cell at
     436             :     // the instantiation site (we only decide to inline if it's populated).
     437             :     FeedbackSlot slot = p.feedback().slot();
     438             :     Handle<Cell> cell(Cell::cast(p.feedback().vector()->Get(slot)));
     439             :     DCHECK(cell->value()->IsFeedbackVector());
     440             : 
     441             :     // The inlinee uses the locally provided context at instantiation.
     442         344 :     context_out = NodeProperties::GetContextInput(match.node());
     443         344 :     feedback_vector_out = handle(FeedbackVector::cast(cell->value()));
     444             :     return;
     445             :   }
     446             : 
     447             :   // Must succeed.
     448           0 :   UNREACHABLE();
     449             : }
     450             : 
     451           0 : Reduction JSInliner::Reduce(Node* node) {
     452           0 :   if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange();
     453           0 :   return ReduceJSCall(node);
     454             : }
     455             : 
     456      347688 : Reduction JSInliner::ReduceJSCall(Node* node) {
     457             :   DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
     458             :   Handle<SharedFunctionInfo> shared_info;
     459             :   JSCallAccessor call(node);
     460             : 
     461             :   // Determine the call target.
     462       45290 :   if (!DetermineCallTarget(node, shared_info)) return NoChange();
     463             : 
     464             :   // Inlining is only supported in the bytecode pipeline.
     465       92420 :   if (!info_->is_optimizing_from_bytecode()) {
     466           2 :     TRACE("Not inlining %s into %s due to use of the deprecated pipeline\n",
     467             :           shared_info->DebugName()->ToCString().get(),
     468             :           info_->shared_info()->DebugName()->ToCString().get());
     469             :     return NoChange();
     470             :   }
     471             : 
     472             :   // Function must be inlineable.
     473       45288 :   if (!shared_info->IsInlineable()) {
     474        4573 :     TRACE("Not inlining %s into %s because callee is not inlineable\n",
     475             :           shared_info->DebugName()->ToCString().get(),
     476             :           info_->shared_info()->DebugName()->ToCString().get());
     477             :     return NoChange();
     478             :   }
     479             : 
     480             :   // Constructor must be constructable.
     481       41688 :   if (node->opcode() == IrOpcode::kJSConstruct &&
     482         973 :       IsNonConstructible(shared_info)) {
     483           0 :     TRACE("Not inlining %s into %s because constructor is not constructable.\n",
     484             :           shared_info->DebugName()->ToCString().get(),
     485             :           info_->shared_info()->DebugName()->ToCString().get());
     486             :     return NoChange();
     487             :   }
     488             : 
     489             :   // TODO(6180): Don't inline class constructors for now, as the
     490             :   // inlining logic doesn't deal properly with class constructors
     491             :   // that return a primitive.
     492       40715 :   if (FLAG_harmony_restrict_constructor_return &&
     493       40715 :       node->opcode() == IrOpcode::kJSConstruct &&
     494             :       IsClassConstructor(shared_info->kind())) {
     495           0 :     TRACE(
     496             :         "Not inlining %s into %s because class constructor inlining is"
     497             :         "not supported.\n",
     498             :         shared_info->DebugName()->ToCString().get(),
     499             :         info_->shared_info()->DebugName()->ToCString().get());
     500             :     return NoChange();
     501             :   }
     502             : 
     503             :   // TODO(706642): Don't inline derived class constructors for now, as the
     504             :   // inlining logic doesn't deal properly with derived class constructors
     505             :   // that return a primitive, i.e. it's not in sync with what the Parser
     506             :   // and the JSConstructSub does.
     507       41688 :   if (node->opcode() == IrOpcode::kJSConstruct &&
     508             :       IsDerivedConstructor(shared_info->kind())) {
     509         147 :     TRACE("Not inlining %s into %s because constructor is derived.\n",
     510             :           shared_info->DebugName()->ToCString().get(),
     511             :           info_->shared_info()->DebugName()->ToCString().get());
     512             :     return NoChange();
     513             :   }
     514             : 
     515             :   // Class constructors are callable, but [[Call]] will raise an exception.
     516             :   // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ).
     517       80310 :   if (node->opcode() == IrOpcode::kJSCall &&
     518             :       IsClassConstructor(shared_info->kind())) {
     519           0 :     TRACE("Not inlining %s into %s because callee is a class constructor.\n",
     520             :           shared_info->DebugName()->ToCString().get(),
     521             :           info_->shared_info()->DebugName()->ToCString().get());
     522             :     return NoChange();
     523             :   }
     524             : 
     525             :   // Function contains break points.
     526       40568 :   if (shared_info->HasDebugInfo()) {
     527           0 :     TRACE("Not inlining %s into %s because callee may contain break points\n",
     528             :           shared_info->DebugName()->ToCString().get(),
     529             :           info_->shared_info()->DebugName()->ToCString().get());
     530             :     return NoChange();
     531             :   }
     532             : 
     533             :   // TODO(turbofan): TranslatedState::GetAdaptedArguments() currently relies on
     534             :   // not inlining recursive functions. We might want to relax that at some
     535             :   // point.
     536      273106 :   for (Node* frame_state = call.frame_state();
     537             :        frame_state->opcode() == IrOpcode::kFrameState;
     538             :        frame_state = frame_state->InputAt(kFrameStateOuterStateInput)) {
     539       76414 :     FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
     540             :     Handle<SharedFunctionInfo> frame_shared_info;
     541      152828 :     if (frame_info.shared_info().ToHandle(&frame_shared_info) &&
     542             :         *frame_shared_info == *shared_info) {
     543         713 :       TRACE("Not inlining %s into %s because call is recursive\n",
     544             :             shared_info->DebugName()->ToCString().get(),
     545             :             info_->shared_info()->DebugName()->ToCString().get());
     546             :       return NoChange();
     547             :     }
     548             :   }
     549             : 
     550             :   // Calls surrounded by a local try-block are only inlined if the appropriate
     551             :   // flag is active. We also discover the {IfException} projection this way.
     552       39855 :   Node* exception_target = nullptr;
     553       43420 :   if (NodeProperties::IsExceptionalCall(node, &exception_target) &&
     554        3565 :       !FLAG_inline_into_try) {
     555           0 :     TRACE(
     556             :         "Try block surrounds #%d:%s and --no-inline-into-try active, so not "
     557             :         "inlining %s into %s.\n",
     558             :         exception_target->id(), exception_target->op()->mnemonic(),
     559             :         shared_info->DebugName()->ToCString().get(),
     560             :         info_->shared_info()->DebugName()->ToCString().get());
     561             :     return NoChange();
     562             :   }
     563             : 
     564       39855 :   ParseInfo parse_info(shared_info);
     565             :   CompilationInfo info(parse_info.zone(), &parse_info,
     566       79710 :                        shared_info->GetIsolate(), Handle<JSFunction>::null());
     567       79710 :   if (info_->is_deoptimization_enabled()) info.MarkAsDeoptimizationEnabled();
     568             :   info.MarkAsOptimizeFromBytecode();
     569             : 
     570       39855 :   if (!Compiler::EnsureBytecode(&info)) {
     571        1825 :     TRACE("Not inlining %s into %s because bytecode generation failed\n",
     572             :           shared_info->DebugName()->ToCString().get(),
     573             :           info_->shared_info()->DebugName()->ToCString().get());
     574        3650 :     if (info_->isolate()->has_pending_exception()) {
     575          15 :       info_->isolate()->clear_pending_exception();
     576             :     }
     577             :     return NoChange();
     578             :   }
     579             : 
     580             :   // Remember that we inlined this function. This needs to be called right
     581             :   // after we ensure deoptimization support so that the code flusher
     582             :   // does not remove the code with the deoptimization support.
     583             :   int inlining_id = info_->AddInlinedFunction(
     584       38030 :       shared_info, source_positions_->GetSourcePosition(node));
     585             : 
     586             :   // ----------------------------------------------------------------
     587             :   // After this point, we've made a decision to inline this function.
     588             :   // We shall not bailout from inlining if we got here.
     589             : 
     590       38030 :   TRACE("Inlining %s into %s%s\n", shared_info->DebugName()->ToCString().get(),
     591             :         info_->shared_info()->DebugName()->ToCString().get(),
     592             :         (exception_target != nullptr) ? " (inside try-block)" : "");
     593             : 
     594             :   // Determine the targets feedback vector and its context.
     595             :   Node* context;
     596             :   Handle<FeedbackVector> feedback_vector;
     597       38030 :   DetermineCallContext(node, context, feedback_vector);
     598             : 
     599             :   // Create the subgraph for the inlinee.
     600             :   Node* start;
     601             :   Node* end;
     602             :   {
     603             :     // Run the BytecodeGraphBuilder to create the subgraph.
     604             :     Graph::SubgraphScope scope(graph());
     605             :     JSTypeHintLowering::Flags flags = JSTypeHintLowering::kNoFlags;
     606       76060 :     if (info_->is_bailout_on_uninitialized()) {
     607             :       flags |= JSTypeHintLowering::kBailoutOnUninitialized;
     608             :     }
     609             :     BytecodeGraphBuilder graph_builder(
     610             :         parse_info.zone(), shared_info, feedback_vector, BailoutId::None(),
     611       76060 :         jsgraph(), call.frequency(), source_positions_, inlining_id, flags);
     612       38030 :     graph_builder.CreateGraph(false);
     613             : 
     614             :     // Extract the inlinee start/end nodes.
     615       38030 :     start = graph()->start();
     616       38030 :     end = graph()->end();
     617             :   }
     618             : 
     619             :   // If we are inlining into a surrounding exception handler, we collect all
     620             :   // potentially throwing nodes within the inlinee that are not handled locally
     621             :   // by the inlinee itself. They are later wired into the surrounding handler.
     622       38030 :   NodeVector uncaught_subcalls(local_zone_);
     623       38030 :   if (exception_target != nullptr) {
     624             :     // Find all uncaught 'calls' in the inlinee.
     625        3009 :     AllNodes inlined_nodes(local_zone_, end, graph());
     626      224103 :     for (Node* subnode : inlined_nodes.reachable) {
     627             :       // Every possibly throwing node should get {IfSuccess} and {IfException}
     628             :       // projections, unless there already is local exception handling.
     629      436170 :       if (subnode->op()->HasProperty(Operator::kNoThrow)) continue;
     630       16623 :       if (!NodeProperties::IsExceptionalCall(subnode)) {
     631             :         DCHECK_EQ(2, subnode->op()->ControlOutputCount());
     632       14487 :         uncaught_subcalls.push_back(subnode);
     633             :       }
     634             :     }
     635             :   }
     636             : 
     637             :   Node* frame_state = call.frame_state();
     638       38030 :   Node* new_target = jsgraph()->UndefinedConstant();
     639             : 
     640             :   // Inline {JSConstruct} requires some additional magic.
     641       38030 :   if (node->opcode() == IrOpcode::kJSConstruct) {
     642             :     // Swizzle the inputs of the {JSConstruct} node to look like inputs to a
     643             :     // normal {JSCall} node so that the rest of the inlining machinery
     644             :     // behaves as if we were dealing with a regular function invocation.
     645             :     new_target = call.new_target();  // Retrieve new target value input.
     646         821 :     node->RemoveInput(call.formal_arguments() + 1);  // Drop new target.
     647         821 :     node->InsertInput(graph()->zone(), 1, new_target);
     648             : 
     649             :     // Insert nodes around the call that model the behavior required for a
     650             :     // constructor dispatch (allocate implicit receiver and check return value).
     651             :     // This models the behavior usually accomplished by our {JSConstructStub}.
     652             :     // Note that the context has to be the callers context (input to call node).
     653             :     // Also note that by splitting off the {JSCreate} piece of the constructor
     654             :     // call, we create an observable deoptimization point after the receiver
     655             :     // instantiation but before the invocation (i.e. inside {JSConstructStub}
     656             :     // where execution continues at {construct_stub_create_deopt_pc_offset}).
     657         821 :     Node* receiver = jsgraph()->TheHoleConstant();  // Implicit receiver.
     658         821 :     if (NeedsImplicitReceiver(shared_info)) {
     659         821 :       Node* effect = NodeProperties::GetEffectInput(node);
     660         821 :       Node* control = NodeProperties::GetControlInput(node);
     661         821 :       Node* context = NodeProperties::GetContextInput(node);
     662             :       Node* frame_state_inside = CreateArtificialFrameState(
     663             :           node, frame_state, call.formal_arguments(),
     664             :           BailoutId::ConstructStubCreate(), FrameStateType::kConstructStub,
     665        1642 :           info.shared_info());
     666             :       Node* create =
     667             :           graph()->NewNode(javascript()->Create(), call.target(), new_target,
     668        1642 :                            context, frame_state_inside, effect, control);
     669         821 :       uncaught_subcalls.push_back(create);  // Adds {IfSuccess} & {IfException}.
     670         821 :       NodeProperties::ReplaceControlInput(node, create);
     671         821 :       NodeProperties::ReplaceEffectInput(node, create);
     672             :       // Insert a check of the return value to determine whether the return
     673             :       // value or the implicit receiver should be selected as a result of the
     674             :       // call.
     675         821 :       Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), node);
     676             :       Node* select =
     677             :           graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
     678        1642 :                            check, node, create);
     679         821 :       NodeProperties::ReplaceUses(node, select, node, node, node);
     680             :       // Fix-up inputs that have been mangled by the {ReplaceUses} call above.
     681         821 :       NodeProperties::ReplaceValueInput(select, node, 1);  // Fix-up input.
     682         821 :       NodeProperties::ReplaceValueInput(check, node, 0);   // Fix-up input.
     683         821 :       receiver = create;  // The implicit receiver.
     684             :     }
     685         821 :     node->ReplaceInput(1, receiver);
     686             : 
     687             :     // Insert a construct stub frame into the chain of frame states. This will
     688             :     // reconstruct the proper frame when deoptimizing within the constructor.
     689             :     frame_state = CreateArtificialFrameState(
     690             :         node, frame_state, call.formal_arguments(),
     691             :         BailoutId::ConstructStubInvoke(), FrameStateType::kConstructStub,
     692        1642 :         info.shared_info());
     693             :   }
     694             : 
     695             :   // Insert a JSConvertReceiver node for sloppy callees. Note that the context
     696             :   // passed into this node has to be the callees context (loaded above).
     697       75239 :   if (node->opcode() == IrOpcode::kJSCall &&
     698       69097 :       is_sloppy(shared_info->language_mode()) && !shared_info->native()) {
     699       31067 :     Node* effect = NodeProperties::GetEffectInput(node);
     700       31067 :     if (NeedsConvertReceiver(call.receiver(), effect)) {
     701       59994 :       const CallParameters& p = CallParametersOf(node->op());
     702             :       Node* convert = effect =
     703             :           graph()->NewNode(javascript()->ConvertReceiver(p.convert_mode()),
     704       59994 :                            call.receiver(), context, effect, start);
     705       29997 :       NodeProperties::ReplaceValueInput(node, convert, 1);
     706       29997 :       NodeProperties::ReplaceEffectInput(node, effect);
     707             :     }
     708             :   }
     709             : 
     710             :   // If we are inlining a JS call at tail position then we have to pop current
     711             :   // frame state and its potential arguments adaptor frame state in order to
     712             :   // make the call stack be consistent with non-inlining case.
     713             :   // After that we add a tail caller frame state which lets deoptimizer handle
     714             :   // the case when the outermost function inlines a tail call (it should remove
     715             :   // potential arguments adaptor frame that belongs to outermost function when
     716             :   // deopt happens).
     717       38030 :   if (node->opcode() == IrOpcode::kJSCall) {
     718       37209 :     const CallParameters& p = CallParametersOf(node->op());
     719       37209 :     if (p.tail_call_mode() == TailCallMode::kAllow) {
     720         365 :       frame_state = CreateTailCallerFrameState(node, frame_state);
     721             :     }
     722             :   }
     723             : 
     724             :   // Insert argument adaptor frame if required. The callees formal parameter
     725             :   // count (i.e. value outputs of start node minus target, receiver, new target,
     726             :   // arguments count and context) have to match the number of arguments passed
     727             :   // to the call.
     728             :   int parameter_count = shared_info->internal_formal_parameter_count();
     729             :   DCHECK_EQ(parameter_count, start->op()->ValueOutputCount() - 5);
     730       38030 :   if (call.formal_arguments() != parameter_count) {
     731             :     frame_state = CreateArtificialFrameState(
     732             :         node, frame_state, call.formal_arguments(), BailoutId::None(),
     733       10889 :         FrameStateType::kArgumentsAdaptor, shared_info);
     734             :   }
     735             : 
     736             :   return InlineCall(node, new_target, context, frame_state, start, end,
     737       77885 :                     exception_target, uncaught_subcalls);
     738             : }
     739             : 
     740      346842 : Graph* JSInliner::graph() const { return jsgraph()->graph(); }
     741             : 
     742       30818 : JSOperatorBuilder* JSInliner::javascript() const {
     743       30818 :   return jsgraph()->javascript();
     744             : }
     745             : 
     746      225109 : CommonOperatorBuilder* JSInliner::common() const { return jsgraph()->common(); }
     747             : 
     748         821 : SimplifiedOperatorBuilder* JSInliner::simplified() const {
     749         821 :   return jsgraph()->simplified();
     750             : }
     751             : 
     752             : }  // namespace compiler
     753             : }  // namespace internal
     754             : }  // namespace v8

Generated by: LCOV version 1.10