js-inlining.cc 30.7 KB
Newer Older
1 2 3 4
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
#include "src/compiler/js-inlining.h"

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 16
#include "src/compiler/js-operator.h"
#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 24 25 26

namespace v8 {
namespace internal {
namespace compiler {

27 28 29 30 31
#define TRACE(...)                                      \
  do {                                                  \
    if (FLAG_trace_turbo_inlining) PrintF(__VA_ARGS__); \
  } while (false)

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 45
    return call_->InputAt(0);
  }
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 67 68
    return call_->op()->ValueInputCount() - 2;
  }

69
  float frequency() const {
70 71
    return (call_->opcode() == IrOpcode::kJSCall)
               ? 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 81 82
                                Node* frame_state, Node* start, Node* end,
                                Node* exception_target,
                                const NodeVector& uncaught_subcalls) {
83
  // The scheduler is smart enough to place our code; we just ensure {control}
84 85
  // becomes the control input of the start of the inlinee, and {effect} becomes
  // the effect input of the start of the inlinee.
86
  Node* control = NodeProperties::GetControlInput(call);
87
  Node* effect = NodeProperties::GetEffectInput(call);
88

89 90
  int const inlinee_new_target_index =
      static_cast<int>(start->op()->ValueOutputCount()) - 3;
91 92
  int const inlinee_arity_index =
      static_cast<int>(start->op()->ValueOutputCount()) - 2;
93 94 95
  int const inlinee_context_index =
      static_cast<int>(start->op()->ValueOutputCount()) - 1;

96 97
  // {inliner_inputs} counts JSFunction, receiver, arguments, but not
  // 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()) {
danno's avatar
danno committed
101
    Node* use = edge.from();
102 103
    switch (use->opcode()) {
      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 108
          // There is an input from the call, and the index is a value
          // projection but not the context, so rewire the input.
109
          Replace(use, call->InputAt(index));
110 111 112
        } else if (index == inlinee_new_target_index) {
          // The projection is requesting the new target value.
          Replace(use, new_target);
113 114
        } else if (index == inlinee_arity_index) {
          // 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 124 125
        }
        break;
      }
      default:
danno's avatar
danno committed
126
        if (NodeProperties::IsEffectEdge(edge)) {
127
          edge.UpdateTo(effect);
danno's avatar
danno committed
128 129
        } else if (NodeProperties::IsControlEdge(edge)) {
          edge.UpdateTo(control);
130 131
        } else if (NodeProperties::IsFrameStateEdge(edge)) {
          edge.UpdateTo(frame_state);
132 133 134 135 136 137 138
        } else {
          UNREACHABLE();
        }
        break;
    }
  }

139 140 141 142 143
  if (exception_target != nullptr) {
    // Link uncaught calls in the inlinee to {exception_target}
    int subcall_count = static_cast<int>(uncaught_subcalls.size());
    if (subcall_count > 0) {
      TRACE(
144 145
          "Inlinee contains %d calls without local exception handler; "
          "linking to surrounding exception handler\n",
146 147 148 149
          subcall_count);
    }
    NodeVector on_exception_nodes(local_zone_);
    for (Node* subcall : uncaught_subcalls) {
150 151 152
      Node* on_success = graph()->NewNode(common()->IfSuccess(), subcall);
      NodeProperties::ReplaceUses(subcall, subcall, subcall, on_success);
      NodeProperties::ReplaceControlInput(on_success, subcall);
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
      Node* on_exception =
          graph()->NewNode(common()->IfException(), subcall, subcall);
      on_exception_nodes.push_back(on_exception);
    }

    DCHECK_EQ(subcall_count, static_cast<int>(on_exception_nodes.size()));
    if (subcall_count > 0) {
      Node* control_output =
          graph()->NewNode(common()->Merge(subcall_count), subcall_count,
                           &on_exception_nodes.front());
      NodeVector values_effects(local_zone_);
      values_effects = on_exception_nodes;
      values_effects.push_back(control_output);
      Node* value_output = graph()->NewNode(
          common()->Phi(MachineRepresentation::kTagged, subcall_count),
          subcall_count + 1, &values_effects.front());
      Node* effect_output =
          graph()->NewNode(common()->EffectPhi(subcall_count),
                           subcall_count + 1, &values_effects.front());
      ReplaceWithValue(exception_target, value_output, effect_output,
                       control_output);
    } else {
      ReplaceWithValue(exception_target, exception_target, exception_target,
                       jsgraph()->Dead());
    }
  }

180 181 182 183 184 185
  NodeVector values(local_zone_);
  NodeVector effects(local_zone_);
  NodeVector controls(local_zone_);
  for (Node* const input : end->inputs()) {
    switch (input->opcode()) {
      case IrOpcode::kReturn:
186
        values.push_back(NodeProperties::GetValueInput(input, 1));
187 188 189
        effects.push_back(NodeProperties::GetEffectInput(input));
        controls.push_back(NodeProperties::GetControlInput(input));
        break;
190 191 192
      case IrOpcode::kDeoptimize:
      case IrOpcode::kTerminate:
      case IrOpcode::kThrow:
193 194
        NodeProperties::MergeControlToEnd(graph(), common(), input);
        Revisit(graph()->end());
195
        break;
196 197 198
      default:
        UNREACHABLE();
        break;
199 200
    }
  }
201 202
  DCHECK_EQ(values.size(), effects.size());
  DCHECK_EQ(values.size(), controls.size());
203 204 205 206 207

  // Depending on whether the inlinee produces a value, we either replace value
  // uses with said value or kill value uses if no value can be returned.
  if (values.size() > 0) {
    int const input_count = static_cast<int>(controls.size());
208 209
    Node* control_output = graph()->NewNode(common()->Merge(input_count),
                                            input_count, &controls.front());
210 211
    values.push_back(control_output);
    effects.push_back(control_output);
212 213
    Node* value_output = graph()->NewNode(
        common()->Phi(MachineRepresentation::kTagged, input_count),
214
        static_cast<int>(values.size()), &values.front());
215 216 217
    Node* effect_output =
        graph()->NewNode(common()->EffectPhi(input_count),
                         static_cast<int>(effects.size()), &effects.front());
218 219 220
    ReplaceWithValue(call, value_output, effect_output, control_output);
    return Changed(value_output);
  } else {
221
    ReplaceWithValue(call, call, call, jsgraph()->Dead());
222 223
    return Changed(call);
  }
224 225
}

226 227
Node* JSInliner::CreateArtificialFrameState(Node* node, Node* outer_frame_state,
                                            int parameter_count,
228
                                            BailoutId bailout_id,
229 230
                                            FrameStateType frame_state_type,
                                            Handle<SharedFunctionInfo> shared) {
231
  const FrameStateFunctionInfo* state_info =
232 233
      common()->CreateFrameStateFunctionInfo(frame_state_type,
                                             parameter_count + 1, 0, shared);
234

235
  const Operator* op = common()->FrameState(
236
      bailout_id, OutputFrameStateCombine::Ignore(), state_info);
237
  const Operator* op0 = common()->StateValues(0, SparseInputMask::Dense());
238
  Node* node0 = graph()->NewNode(op0);
239
  NodeVector params(local_zone_);
240 241
  for (int parameter = 0; parameter < parameter_count + 1; ++parameter) {
    params.push_back(node->InputAt(1 + parameter));
242
  }
243 244
  const Operator* op_param = common()->StateValues(
      static_cast<int>(params.size()), SparseInputMask::Dense());
245
  Node* params_node = graph()->NewNode(
246
      op_param, static_cast<int>(params.size()), &params.front());
247 248 249
  return graph()->NewNode(op, params_node, node0, node0,
                          jsgraph()->UndefinedConstant(), node->InputAt(0),
                          outer_frame_state);
250 251
}

252 253
Node* JSInliner::CreateTailCallerFrameState(Node* node, Node* frame_state) {
  FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
254 255
  Handle<SharedFunctionInfo> shared;
  frame_info.shared_info().ToHandle(&shared);
256 257 258 259 260

  Node* function = frame_state->InputAt(kFrameStateFunctionInput);

  // If we are inlining a tail call drop caller's frame state and an
  // arguments adaptor if it exists.
261
  frame_state = NodeProperties::GetFrameStateInput(frame_state);
262
  if (frame_state->opcode() == IrOpcode::kFrameState) {
263 264
    FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
    if (frame_info.type() == FrameStateType::kArgumentsAdaptor) {
265
      frame_state = NodeProperties::GetFrameStateInput(frame_state);
266 267 268 269
    }
  }

  const FrameStateFunctionInfo* state_info =
270
      common()->CreateFrameStateFunctionInfo(
271 272
          FrameStateType::kTailCallerFunction, 0, 0, shared);

273
  const Operator* op = common()->FrameState(
274
      BailoutId(-1), OutputFrameStateCombine::Ignore(), state_info);
275
  const Operator* op0 = common()->StateValues(0, SparseInputMask::Dense());
276 277 278 279
  Node* node0 = graph()->NewNode(op0);
  return graph()->NewNode(op, node0, node0, node0,
                          jsgraph()->UndefinedConstant(), function,
                          frame_state);
280
}
281

282 283
namespace {

284 285 286 287 288 289 290 291 292 293 294 295
// TODO(bmeurer): Unify this with the witness helper functions in the
// js-builtin-reducer.cc once we have a better understanding of the
// map tracking we want to do, and eventually changed the CheckMaps
// operator to carry map constants on the operator instead of inputs.
// I.e. if the CheckMaps has some kind of SmallMapSet as operator
// parameter, then this could be changed to call a generic
//
//   SmallMapSet NodeProperties::CollectMapWitness(receiver, effect)
//
// function, which either returns the map set from the CheckMaps or
// a singleton set from a StoreField.
bool NeedsConvertReceiver(Node* receiver, Node* effect) {
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
  // Check if the {receiver} is already a JSReceiver.
  switch (receiver->opcode()) {
    case IrOpcode::kJSConstruct:
    case IrOpcode::kJSConstructWithSpread:
    case IrOpcode::kJSCreate:
    case IrOpcode::kJSCreateArguments:
    case IrOpcode::kJSCreateArray:
    case IrOpcode::kJSCreateClosure:
    case IrOpcode::kJSCreateIterResultObject:
    case IrOpcode::kJSCreateKeyValueArray:
    case IrOpcode::kJSCreateLiteralArray:
    case IrOpcode::kJSCreateLiteralObject:
    case IrOpcode::kJSCreateLiteralRegExp:
    case IrOpcode::kJSConvertReceiver:
    case IrOpcode::kJSGetSuperConstructor:
311
    case IrOpcode::kJSToObject: {
312 313
      return false;
    }
314 315 316 317 318 319
    default: {
      ZoneHandleSet<Map> maps;
      if (NodeProperties::InferReceiverMaps(receiver, effect, &maps)) {
        // Check if all {maps} are actually JSReceiver maps.
        for (size_t i = 0; i < maps.size(); ++i) {
          if (!maps[i]->IsJSReceiverMap()) return true;
320
        }
321
        return false;
322
      }
323
      return true;
324 325 326 327
    }
  }
}

328
// TODO(mstarzinger,verwaest): Move this predicate onto SharedFunctionInfo?
329 330 331 332
bool NeedsImplicitReceiver(Handle<SharedFunctionInfo> shared_info) {
  DisallowHeapAllocation no_gc;
  Isolate* const isolate = shared_info->GetIsolate();
  Code* const construct_stub = shared_info->construct_stub();
333 334 335 336
  return construct_stub != *isolate->builtins()->JSBuiltinsConstructStub() &&
         construct_stub !=
             *isolate->builtins()->JSBuiltinsConstructStubForDerived() &&
         construct_stub != *isolate->builtins()->JSConstructStubApi();
337 338 339 340 341 342 343
}

bool IsNonConstructible(Handle<SharedFunctionInfo> shared_info) {
  DisallowHeapAllocation no_gc;
  Isolate* const isolate = shared_info->GetIsolate();
  Code* const construct_stub = shared_info->construct_stub();
  return construct_stub == *isolate->builtins()->ConstructedNonConstructable();
344 345 346 347
}

}  // namespace

348 349 350 351 352 353 354
// Determines whether the call target of the given call {node} is statically
// known and can be used as an inlining candidate. The {SharedFunctionInfo} of
// the call target is provided (the exact closure might be unknown).
bool JSInliner::DetermineCallTarget(
    Node* node, Handle<SharedFunctionInfo>& shared_info_out) {
  DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
  HeapObjectMatcher match(node->InputAt(0));
355

356 357
  // This reducer can handle both normal function calls as well a constructor
  // calls whenever the target is a constant function object, as follows:
358
  //  - JSCall(target:constant, receiver, args...)
359
  //  - JSConstruct(target:constant, args..., new.target)
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
  if (match.HasValue() && match.Value()->IsJSFunction()) {
    Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value());

    // Disallow cross native-context inlining for now. This means that all parts
    // of the resulting code will operate on the same global object. This also
    // prevents cross context leaks, where we could inline functions from a
    // different context and hold on to that context (and closure) from the code
    // object.
    // TODO(turbofan): We might want to revisit this restriction later when we
    // have a need for this, and we know how to model different native contexts
    // in the same graph in a compositional way.
    if (function->context()->native_context() !=
        info_->context()->native_context()) {
      return false;
    }

    shared_info_out = handle(function->shared());
    return true;
  }

  // This reducer can also handle calls where the target is statically known to
  // be the result of a closure instantiation operation, as follows:
  //  - JSCall(JSCreateClosure[shared](context), receiver, args...)
  //  - JSConstruct(JSCreateClosure[shared](context), args..., new.target)
  if (match.IsJSCreateClosure()) {
    CreateClosureParameters const& p = CreateClosureParametersOf(match.op());

    // Disallow inlining in case the instantiation site was never run and hence
    // the vector cell does not contain a valid feedback vector for the call
    // target.
    // TODO(turbofan): We might consider to eagerly create the feedback vector
    // in such a case (in {DetermineCallContext} below) eventually.
392
    FeedbackSlot slot = p.feedback().slot();
393
    Handle<Cell> cell(Cell::cast(p.feedback().vector()->Get(slot)));
394
    if (!cell->value()->IsFeedbackVector()) return false;
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409

    shared_info_out = p.shared_info();
    return true;
  }

  return false;
}

// Determines statically known information about the call target (assuming that
// the call target is known according to {DetermineCallTarget} above). The
// following static information is provided:
//  - context         : The context (as SSA value) bound by the call target.
//  - feedback_vector : The target is guaranteed to use this feedback vector.
void JSInliner::DetermineCallContext(
    Node* node, Node*& context_out,
410
    Handle<FeedbackVector>& feedback_vector_out) {
411
  DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
412
  HeapObjectMatcher match(node->InputAt(0));
413

414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
  if (match.HasValue() && match.Value()->IsJSFunction()) {
    Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value());

    // If the target function was never invoked, its literals array might not
    // contain a feedback vector. We ensure at this point that it is created.
    JSFunction::EnsureLiterals(function);

    // The inlinee specializes to the context from the JSFunction object.
    context_out = jsgraph()->Constant(handle(function->context()));
    feedback_vector_out = handle(function->feedback_vector());
    return;
  }

  if (match.IsJSCreateClosure()) {
    CreateClosureParameters const& p = CreateClosureParametersOf(match.op());

    // Load the feedback vector of the target by looking up its vector cell at
    // the instantiation site (we only decide to inline if it's populated).
432
    FeedbackSlot slot = p.feedback().slot();
433
    Handle<Cell> cell(Cell::cast(p.feedback().vector()->Get(slot)));
434
    DCHECK(cell->value()->IsFeedbackVector());
435 436 437

    // The inlinee uses the locally provided context at instantiation.
    context_out = NodeProperties::GetContextInput(match.node());
438
    feedback_vector_out = handle(FeedbackVector::cast(cell->value()));
439 440 441 442 443
    return;
  }

  // Must succeed.
  UNREACHABLE();
444 445
}

446 447 448 449 450 451
Reduction JSInliner::Reduce(Node* node) {
  if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange();
  return ReduceJSCall(node);
}

Reduction JSInliner::ReduceJSCall(Node* node) {
452
  DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
453
  Handle<SharedFunctionInfo> shared_info;
454
  JSCallAccessor call(node);
455 456 457

  // Determine the call target.
  if (!DetermineCallTarget(node, shared_info)) return NoChange();
458

459 460
  // Inlining is only supported in the bytecode pipeline.
  if (!info_->is_optimizing_from_bytecode()) {
461
    TRACE("Not inlining %s into %s due to use of the deprecated pipeline\n",
462 463 464 465 466
          shared_info->DebugName()->ToCString().get(),
          info_->shared_info()->DebugName()->ToCString().get());
    return NoChange();
  }

467
  // Function must be inlineable.
468
  if (!shared_info->IsInlineable()) {
469
    TRACE("Not inlining %s into %s because callee is not inlineable\n",
470
          shared_info->DebugName()->ToCString().get(),
471 472 473 474
          info_->shared_info()->DebugName()->ToCString().get());
    return NoChange();
  }

475
  // Constructor must be constructable.
476
  if (node->opcode() == IrOpcode::kJSConstruct &&
477
      IsNonConstructible(shared_info)) {
478
    TRACE("Not inlining %s into %s because constructor is not constructable.\n",
479
          shared_info->DebugName()->ToCString().get(),
480 481
          info_->shared_info()->DebugName()->ToCString().get());
    return NoChange();
482 483 484 485 486 487 488 489 490 491 492 493
  }

  // TODO(706642): Don't inline derived class constructors for now, as the
  // inlining logic doesn't deal properly with derived class constructors
  // that return a primitive, i.e. it's not in sync with what the Parser
  // and the JSConstructSub does.
  if (node->opcode() == IrOpcode::kJSConstruct &&
      IsDerivedConstructor(shared_info->kind())) {
    TRACE("Not inlining %s into %s because constructor is derived.\n",
          shared_info->DebugName()->ToCString().get(),
          info_->shared_info()->DebugName()->ToCString().get());
    return NoChange();
494 495
  }

496 497
  // Class constructors are callable, but [[Call]] will raise an exception.
  // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ).
498
  if (node->opcode() == IrOpcode::kJSCall &&
499
      IsClassConstructor(shared_info->kind())) {
500
    TRACE("Not inlining %s into %s because callee is a class constructor.\n",
501
          shared_info->DebugName()->ToCString().get(),
502 503 504 505
          info_->shared_info()->DebugName()->ToCString().get());
    return NoChange();
  }

506
  // Function contains break points.
507
  if (shared_info->HasDebugInfo()) {
508
    TRACE("Not inlining %s into %s because callee may contain break points\n",
509
          shared_info->DebugName()->ToCString().get(),
510 511 512 513
          info_->shared_info()->DebugName()->ToCString().get());
    return NoChange();
  }

514 515 516
  // TODO(turbofan): TranslatedState::GetAdaptedArguments() currently relies on
  // not inlining recursive functions. We might want to relax that at some
  // point.
517
  for (Node* frame_state = call.frame_state();
518 519
       frame_state->opcode() == IrOpcode::kFrameState;
       frame_state = frame_state->InputAt(kFrameStateOuterStateInput)) {
520 521 522 523
    FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
    Handle<SharedFunctionInfo> frame_shared_info;
    if (frame_info.shared_info().ToHandle(&frame_shared_info) &&
        *frame_shared_info == *shared_info) {
524
      TRACE("Not inlining %s into %s because call is recursive\n",
525
            shared_info->DebugName()->ToCString().get(),
526 527 528 529 530
            info_->shared_info()->DebugName()->ToCString().get());
      return NoChange();
    }
  }

531 532
  // Calls surrounded by a local try-block are only inlined if the appropriate
  // flag is active. We also discover the {IfException} projection this way.
533
  Node* exception_target = nullptr;
534 535 536 537 538 539 540 541 542
  if (NodeProperties::IsExceptionalCall(node, &exception_target) &&
      !FLAG_inline_into_try) {
    TRACE(
        "Try block surrounds #%d:%s and --no-inline-into-try active, so not "
        "inlining %s into %s.\n",
        exception_target->id(), exception_target->op()->mnemonic(),
        shared_info->DebugName()->ToCString().get(),
        info_->shared_info()->DebugName()->ToCString().get());
    return NoChange();
543 544
  }

545
  ParseInfo parse_info(shared_info);
546
  CompilationInfo info(parse_info.zone(), &parse_info,
547
                       shared_info->GetIsolate(), Handle<JSFunction>::null());
548
  if (info_->is_deoptimization_enabled()) info.MarkAsDeoptimizationEnabled();
549
  info.MarkAsOptimizeFromBytecode();
550

551
  if (!Compiler::EnsureBytecode(&info)) {
552 553 554
    TRACE("Not inlining %s into %s because bytecode generation failed\n",
          shared_info->DebugName()->ToCString().get(),
          info_->shared_info()->DebugName()->ToCString().get());
555 556 557
    if (info_->isolate()->has_pending_exception()) {
      info_->isolate()->clear_pending_exception();
    }
558 559 560
    return NoChange();
  }

561 562 563
  // Remember that we inlined this function. This needs to be called right
  // after we ensure deoptimization support so that the code flusher
  // does not remove the code with the deoptimization support.
564 565
  int inlining_id = info_->AddInlinedFunction(
      shared_info, source_positions_->GetSourcePosition(node));
566 567 568 569

  // ----------------------------------------------------------------
  // After this point, we've made a decision to inline this function.
  // We shall not bailout from inlining if we got here.
570

571 572 573
  TRACE("Inlining %s into %s%s\n", shared_info->DebugName()->ToCString().get(),
        info_->shared_info()->DebugName()->ToCString().get(),
        (exception_target != nullptr) ? " (inside try-block)" : "");
574

575 576
  // Determine the targets feedback vector and its context.
  Node* context;
577
  Handle<FeedbackVector> feedback_vector;
578
  DetermineCallContext(node, context, feedback_vector);
579

580 581 582
  // Create the subgraph for the inlinee.
  Node* start;
  Node* end;
583
  {
584 585
    // Run the BytecodeGraphBuilder to create the subgraph.
    Graph::SubgraphScope scope(graph());
586 587 588 589
    JSTypeHintLowering::Flags flags = JSTypeHintLowering::kNoFlags;
    if (info_->is_bailout_on_uninitialized()) {
      flags |= JSTypeHintLowering::kBailoutOnUninitialized;
    }
590
    BytecodeGraphBuilder graph_builder(
591
        parse_info.zone(), shared_info, feedback_vector, BailoutId::None(),
592
        jsgraph(), call.frequency(), source_positions_, inlining_id, flags);
593
    graph_builder.CreateGraph(false);
594

595 596 597 598 599
    // Extract the inlinee start/end nodes.
    start = graph()->start();
    end = graph()->end();
  }

600 601 602
  // If we are inlining into a surrounding exception handler, we collect all
  // potentially throwing nodes within the inlinee that are not handled locally
  // by the inlinee itself. They are later wired into the surrounding handler.
603
  NodeVector uncaught_subcalls(local_zone_);
604 605 606 607
  if (exception_target != nullptr) {
    // Find all uncaught 'calls' in the inlinee.
    AllNodes inlined_nodes(local_zone_, end, graph());
    for (Node* subnode : inlined_nodes.reachable) {
608 609 610 611
      // Every possibly throwing node should get {IfSuccess} and {IfException}
      // projections, unless there already is local exception handling.
      if (subnode->op()->HasProperty(Operator::kNoThrow)) continue;
      if (!NodeProperties::IsExceptionalCall(subnode)) {
612 613 614 615 616 617
        DCHECK_EQ(2, subnode->op()->ControlOutputCount());
        uncaught_subcalls.push_back(subnode);
      }
    }
  }

618
  Node* frame_state = call.frame_state();
619
  Node* new_target = jsgraph()->UndefinedConstant();
620

621 622
  // Inline {JSConstruct} requires some additional magic.
  if (node->opcode() == IrOpcode::kJSConstruct) {
623 624 625 626 627 628 629
    // Swizzle the inputs of the {JSConstruct} node to look like inputs to a
    // normal {JSCall} node so that the rest of the inlining machinery
    // behaves as if we were dealing with a regular function invocation.
    new_target = call.new_target();  // Retrieve new target value input.
    node->RemoveInput(call.formal_arguments() + 1);  // Drop new target.
    node->InsertInput(graph()->zone(), 1, new_target);

630 631 632 633
    // Insert nodes around the call that model the behavior required for a
    // constructor dispatch (allocate implicit receiver and check return value).
    // This models the behavior usually accomplished by our {JSConstructStub}.
    // Note that the context has to be the callers context (input to call node).
634 635 636 637
    // Also note that by splitting off the {JSCreate} piece of the constructor
    // call, we create an observable deoptimization point after the receiver
    // instantiation but before the invocation (i.e. inside {JSConstructStub}
    // where execution continues at {construct_stub_create_deopt_pc_offset}).
638
    Node* receiver = jsgraph()->TheHoleConstant();  // Implicit receiver.
639 640
    if (NeedsImplicitReceiver(shared_info)) {
      Node* effect = NodeProperties::GetEffectInput(node);
641
      Node* control = NodeProperties::GetControlInput(node);
642
      Node* context = NodeProperties::GetContextInput(node);
643 644 645 646 647 648 649
      Node* frame_state_inside = CreateArtificialFrameState(
          node, frame_state, call.formal_arguments(),
          BailoutId::ConstructStubCreate(), FrameStateType::kConstructStub,
          info.shared_info());
      Node* create =
          graph()->NewNode(javascript()->Create(), call.target(), new_target,
                           context, frame_state_inside, effect, control);
650 651
      uncaught_subcalls.push_back(create);  // Adds {IfSuccess} & {IfException}.
      NodeProperties::ReplaceControlInput(node, create);
652 653
      NodeProperties::ReplaceEffectInput(node, create);
      // Insert a check of the return value to determine whether the return
654
      // value or the implicit receiver should be selected as a result of the
655 656
      // call.
      Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), node);
657 658 659
      Node* select =
          graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
                           check, node, create);
660
      NodeProperties::ReplaceUses(node, select, node, node, node);
661 662 663
      // Fix-up inputs that have been mangled by the {ReplaceUses} call above.
      NodeProperties::ReplaceValueInput(select, node, 1);  // Fix-up input.
      NodeProperties::ReplaceValueInput(check, node, 0);   // Fix-up input.
664 665
      receiver = create;  // The implicit receiver.
    }
666
    node->ReplaceInput(1, receiver);
667

668 669 670 671
    // Insert a construct stub frame into the chain of frame states. This will
    // reconstruct the proper frame when deoptimizing within the constructor.
    frame_state = CreateArtificialFrameState(
        node, frame_state, call.formal_arguments(),
672 673
        BailoutId::ConstructStubInvoke(), FrameStateType::kConstructStub,
        info.shared_info());
674 675
  }

676
  // Insert a JSConvertReceiver node for sloppy callees. Note that the context
677
  // passed into this node has to be the callees context (loaded above).
678
  if (node->opcode() == IrOpcode::kJSCall &&
679
      is_sloppy(shared_info->language_mode()) && !shared_info->native()) {
680
    Node* effect = NodeProperties::GetEffectInput(node);
681
    if (NeedsConvertReceiver(call.receiver(), effect)) {
682
      const CallParameters& p = CallParametersOf(node->op());
683 684 685
      Node* convert = effect =
          graph()->NewNode(javascript()->ConvertReceiver(p.convert_mode()),
                           call.receiver(), context, effect, start);
686 687 688
      NodeProperties::ReplaceValueInput(node, convert, 1);
      NodeProperties::ReplaceEffectInput(node, effect);
    }
689 690
  }

691 692 693 694 695 696 697
  // If we are inlining a JS call at tail position then we have to pop current
  // frame state and its potential arguments adaptor frame state in order to
  // make the call stack be consistent with non-inlining case.
  // After that we add a tail caller frame state which lets deoptimizer handle
  // the case when the outermost function inlines a tail call (it should remove
  // potential arguments adaptor frame that belongs to outermost function when
  // deopt happens).
698 699
  if (node->opcode() == IrOpcode::kJSCall) {
    const CallParameters& p = CallParametersOf(node->op());
700 701 702 703 704
    if (p.tail_call_mode() == TailCallMode::kAllow) {
      frame_state = CreateTailCallerFrameState(node, frame_state);
    }
  }

705
  // Insert argument adaptor frame if required. The callees formal parameter
706 707 708
  // count (i.e. value outputs of start node minus target, receiver, new target,
  // arguments count and context) have to match the number of arguments passed
  // to the call.
709
  int parameter_count = shared_info->internal_formal_parameter_count();
710
  DCHECK_EQ(parameter_count, start->op()->ValueOutputCount() - 5);
711
  if (call.formal_arguments() != parameter_count) {
712
    frame_state = CreateArtificialFrameState(
713
        node, frame_state, call.formal_arguments(), BailoutId::None(),
714
        FrameStateType::kArgumentsAdaptor, shared_info);
715 716
  }

717 718
  return InlineCall(node, new_target, context, frame_state, start, end,
                    exception_target, uncaught_subcalls);
719
}
720

721 722
Graph* JSInliner::graph() const { return jsgraph()->graph(); }

723 724 725 726 727 728
JSOperatorBuilder* JSInliner::javascript() const {
  return jsgraph()->javascript();
}

CommonOperatorBuilder* JSInliner::common() const { return jsgraph()->common(); }

729 730 731 732
SimplifiedOperatorBuilder* JSInliner::simplified() const {
  return jsgraph()->simplified();
}

733 734 735
}  // namespace compiler
}  // namespace internal
}  // namespace v8