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