V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
js-inlining.cc
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/compiler.h"
9 #include "src/compiler/all-nodes.h"
10 #include "src/compiler/bytecode-graph-builder.h"
11 #include "src/compiler/common-operator.h"
12 #include "src/compiler/compiler-source-position-table.h"
13 #include "src/compiler/graph-reducer.h"
14 #include "src/compiler/js-operator.h"
15 #include "src/compiler/node-matchers.h"
16 #include "src/compiler/node-properties.h"
17 #include "src/compiler/operator-properties.h"
18 #include "src/compiler/simplified-operator.h"
19 #include "src/isolate-inl.h"
20 #include "src/optimized-compilation-info.h"
21 #include "src/parsing/parse-info.h"
22 
23 namespace v8 {
24 namespace internal {
25 namespace compiler {
26 
27 namespace {
28 // This is just to avoid some corner cases, especially since we allow recursive
29 // inlining.
30 static const int kMaxDepthForInlining = 50;
31 } // namespace
32 
33 #define TRACE(...) \
34  do { \
35  if (FLAG_trace_turbo_inlining) PrintF(__VA_ARGS__); \
36  } while (false)
37 
38 
39 // Provides convenience accessors for the common layout of nodes having either
40 // the {JSCall} or the {JSConstruct} operator.
42  public:
43  explicit JSCallAccessor(Node* call) : call_(call) {
44  DCHECK(call->opcode() == IrOpcode::kJSCall ||
45  call->opcode() == IrOpcode::kJSConstruct);
46  }
47 
48  Node* target() {
49  // Both, {JSCall} and {JSConstruct}, have same layout here.
50  return call_->InputAt(0);
51  }
52 
53  Node* receiver() {
54  DCHECK_EQ(IrOpcode::kJSCall, call_->opcode());
55  return call_->InputAt(1);
56  }
57 
58  Node* new_target() {
59  DCHECK_EQ(IrOpcode::kJSConstruct, call_->opcode());
60  return call_->InputAt(formal_arguments() + 1);
61  }
62 
63  Node* frame_state() {
64  // Both, {JSCall} and {JSConstruct}, have frame state.
65  return NodeProperties::GetFrameStateInput(call_);
66  }
67 
68  int formal_arguments() {
69  // Both, {JSCall} and {JSConstruct}, have two extra inputs:
70  // - JSConstruct: Includes target function and new target.
71  // - JSCall: Includes target function and receiver.
72  return call_->op()->ValueInputCount() - 2;
73  }
74 
75  CallFrequency frequency() const {
76  return (call_->opcode() == IrOpcode::kJSCall)
77  ? CallParametersOf(call_->op()).frequency()
78  : ConstructParametersOf(call_->op()).frequency();
79  }
80 
81  private:
82  Node* call_;
83 };
84 
85 Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context,
86  Node* frame_state, Node* start, Node* end,
87  Node* exception_target,
88  const NodeVector& uncaught_subcalls) {
89  // The scheduler is smart enough to place our code; we just ensure {control}
90  // becomes the control input of the start of the inlinee, and {effect} becomes
91  // the effect input of the start of the inlinee.
92  Node* control = NodeProperties::GetControlInput(call);
93  Node* effect = NodeProperties::GetEffectInput(call);
94 
95  int const inlinee_new_target_index =
96  static_cast<int>(start->op()->ValueOutputCount()) - 3;
97  int const inlinee_arity_index =
98  static_cast<int>(start->op()->ValueOutputCount()) - 2;
99  int const inlinee_context_index =
100  static_cast<int>(start->op()->ValueOutputCount()) - 1;
101 
102  // {inliner_inputs} counts JSFunction, receiver, arguments, but not
103  // new target value, argument count, context, effect or control.
104  int inliner_inputs = call->op()->ValueInputCount();
105  // Iterate over all uses of the start node.
106  for (Edge edge : start->use_edges()) {
107  Node* use = edge.from();
108  switch (use->opcode()) {
109  case IrOpcode::kParameter: {
110  int index = 1 + ParameterIndexOf(use->op());
111  DCHECK_LE(index, inlinee_context_index);
112  if (index < inliner_inputs && index < inlinee_new_target_index) {
113  // There is an input from the call, and the index is a value
114  // projection but not the context, so rewire the input.
115  Replace(use, call->InputAt(index));
116  } else if (index == inlinee_new_target_index) {
117  // The projection is requesting the new target value.
118  Replace(use, new_target);
119  } else if (index == inlinee_arity_index) {
120  // The projection is requesting the number of arguments.
121  Replace(use, jsgraph()->Constant(inliner_inputs - 2));
122  } else if (index == inlinee_context_index) {
123  // The projection is requesting the inlinee function context.
124  Replace(use, context);
125  } else {
126  // Call has fewer arguments than required, fill with undefined.
127  Replace(use, jsgraph()->UndefinedConstant());
128  }
129  break;
130  }
131  default:
132  if (NodeProperties::IsEffectEdge(edge)) {
133  edge.UpdateTo(effect);
134  } else if (NodeProperties::IsControlEdge(edge)) {
135  edge.UpdateTo(control);
136  } else if (NodeProperties::IsFrameStateEdge(edge)) {
137  edge.UpdateTo(frame_state);
138  } else {
139  UNREACHABLE();
140  }
141  break;
142  }
143  }
144 
145  if (exception_target != nullptr) {
146  // Link uncaught calls in the inlinee to {exception_target}
147  int subcall_count = static_cast<int>(uncaught_subcalls.size());
148  if (subcall_count > 0) {
149  TRACE(
150  "Inlinee contains %d calls without local exception handler; "
151  "linking to surrounding exception handler\n",
152  subcall_count);
153  }
154  NodeVector on_exception_nodes(local_zone_);
155  for (Node* subcall : uncaught_subcalls) {
156  Node* on_success = graph()->NewNode(common()->IfSuccess(), subcall);
157  NodeProperties::ReplaceUses(subcall, subcall, subcall, on_success);
158  NodeProperties::ReplaceControlInput(on_success, subcall);
159  Node* on_exception =
160  graph()->NewNode(common()->IfException(), subcall, subcall);
161  on_exception_nodes.push_back(on_exception);
162  }
163 
164  DCHECK_EQ(subcall_count, static_cast<int>(on_exception_nodes.size()));
165  if (subcall_count > 0) {
166  Node* control_output =
167  graph()->NewNode(common()->Merge(subcall_count), subcall_count,
168  &on_exception_nodes.front());
169  NodeVector values_effects(local_zone_);
170  values_effects = on_exception_nodes;
171  values_effects.push_back(control_output);
172  Node* value_output = graph()->NewNode(
173  common()->Phi(MachineRepresentation::kTagged, subcall_count),
174  subcall_count + 1, &values_effects.front());
175  Node* effect_output =
176  graph()->NewNode(common()->EffectPhi(subcall_count),
177  subcall_count + 1, &values_effects.front());
178  ReplaceWithValue(exception_target, value_output, effect_output,
179  control_output);
180  } else {
181  ReplaceWithValue(exception_target, exception_target, exception_target,
182  jsgraph()->Dead());
183  }
184  }
185 
186  NodeVector values(local_zone_);
187  NodeVector effects(local_zone_);
188  NodeVector controls(local_zone_);
189  for (Node* const input : end->inputs()) {
190  switch (input->opcode()) {
191  case IrOpcode::kReturn:
192  values.push_back(NodeProperties::GetValueInput(input, 1));
193  effects.push_back(NodeProperties::GetEffectInput(input));
194  controls.push_back(NodeProperties::GetControlInput(input));
195  break;
196  case IrOpcode::kDeoptimize:
197  case IrOpcode::kTerminate:
198  case IrOpcode::kThrow:
199  NodeProperties::MergeControlToEnd(graph(), common(), input);
200  Revisit(graph()->end());
201  break;
202  default:
203  UNREACHABLE();
204  break;
205  }
206  }
207  DCHECK_EQ(values.size(), effects.size());
208  DCHECK_EQ(values.size(), controls.size());
209 
210  // Depending on whether the inlinee produces a value, we either replace value
211  // uses with said value or kill value uses if no value can be returned.
212  if (values.size() > 0) {
213  int const input_count = static_cast<int>(controls.size());
214  Node* control_output = graph()->NewNode(common()->Merge(input_count),
215  input_count, &controls.front());
216  values.push_back(control_output);
217  effects.push_back(control_output);
218  Node* value_output = graph()->NewNode(
219  common()->Phi(MachineRepresentation::kTagged, input_count),
220  static_cast<int>(values.size()), &values.front());
221  Node* effect_output =
222  graph()->NewNode(common()->EffectPhi(input_count),
223  static_cast<int>(effects.size()), &effects.front());
224  ReplaceWithValue(call, value_output, effect_output, control_output);
225  return Changed(value_output);
226  } else {
227  ReplaceWithValue(call, jsgraph()->Dead(), jsgraph()->Dead(),
228  jsgraph()->Dead());
229  return Changed(call);
230  }
231 }
232 
233 Node* JSInliner::CreateArtificialFrameState(Node* node, Node* outer_frame_state,
234  int parameter_count,
235  BailoutId bailout_id,
236  FrameStateType frame_state_type,
237  Handle<SharedFunctionInfo> shared,
238  Node* context) {
239  const FrameStateFunctionInfo* state_info =
240  common()->CreateFrameStateFunctionInfo(frame_state_type,
241  parameter_count + 1, 0, shared);
242 
243  const Operator* op = common()->FrameState(
244  bailout_id, OutputFrameStateCombine::Ignore(), state_info);
245  const Operator* op0 = common()->StateValues(0, SparseInputMask::Dense());
246  Node* node0 = graph()->NewNode(op0);
247  NodeVector params(local_zone_);
248  for (int parameter = 0; parameter < parameter_count + 1; ++parameter) {
249  params.push_back(node->InputAt(1 + parameter));
250  }
251  const Operator* op_param = common()->StateValues(
252  static_cast<int>(params.size()), SparseInputMask::Dense());
253  Node* params_node = graph()->NewNode(
254  op_param, static_cast<int>(params.size()), &params.front());
255  if (!context) {
256  context = jsgraph()->UndefinedConstant();
257  }
258  return graph()->NewNode(op, params_node, node0, node0, context,
259  node->InputAt(0), outer_frame_state);
260 }
261 
262 namespace {
263 
264 // TODO(mstarzinger,verwaest): Move this predicate onto SharedFunctionInfo?
265 bool NeedsImplicitReceiver(Handle<SharedFunctionInfo> shared_info) {
266  DisallowHeapAllocation no_gc;
267  if (!shared_info->construct_as_builtin()) {
268  return !IsDerivedConstructor(shared_info->kind());
269  } else {
270  return false;
271  }
272 }
273 
274 } // namespace
275 
276 // Determines whether the call target of the given call {node} is statically
277 // known and can be used as an inlining candidate. The {SharedFunctionInfo} of
278 // the call target is provided (the exact closure might be unknown).
279 bool JSInliner::DetermineCallTarget(
280  Node* node, Handle<SharedFunctionInfo>& shared_info_out) {
281  DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
282  HeapObjectMatcher match(node->InputAt(0));
283 
284  // This reducer can handle both normal function calls as well a constructor
285  // calls whenever the target is a constant function object, as follows:
286  // - JSCall(target:constant, receiver, args...)
287  // - JSConstruct(target:constant, args..., new.target)
288  if (match.HasValue() && match.Value()->IsJSFunction()) {
289  Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value());
290 
291  // Disallow cross native-context inlining for now. This means that all parts
292  // of the resulting code will operate on the same global object. This also
293  // prevents cross context leaks, where we could inline functions from a
294  // different context and hold on to that context (and closure) from the code
295  // object.
296  // TODO(turbofan): We might want to revisit this restriction later when we
297  // have a need for this, and we know how to model different native contexts
298  // in the same graph in a compositional way.
299  if (function->native_context() != info_->native_context()) {
300  return false;
301  }
302 
303  shared_info_out = handle(function->shared(), isolate());
304  return true;
305  }
306 
307  // This reducer can also handle calls where the target is statically known to
308  // be the result of a closure instantiation operation, as follows:
309  // - JSCall(JSCreateClosure[shared](context), receiver, args...)
310  // - JSConstruct(JSCreateClosure[shared](context), args..., new.target)
311  if (match.IsJSCreateClosure()) {
312  CreateClosureParameters const& p = CreateClosureParametersOf(match.op());
313 
314  // Disallow inlining in case the instantiation site was never run and hence
315  // the vector cell does not contain a valid feedback vector for the call
316  // target.
317  // TODO(turbofan): We might consider to eagerly create the feedback vector
318  // in such a case (in {DetermineCallContext} below) eventually.
319  Handle<FeedbackCell> cell = p.feedback_cell();
320  if (!cell->value()->IsFeedbackVector()) return false;
321 
322  shared_info_out = p.shared_info();
323  return true;
324  }
325 
326  return false;
327 }
328 
329 // Determines statically known information about the call target (assuming that
330 // the call target is known according to {DetermineCallTarget} above). The
331 // following static information is provided:
332 // - context : The context (as SSA value) bound by the call target.
333 // - feedback_vector : The target is guaranteed to use this feedback vector.
334 void JSInliner::DetermineCallContext(
335  Node* node, Node*& context_out,
336  Handle<FeedbackVector>& feedback_vector_out) {
337  DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
338  HeapObjectMatcher match(node->InputAt(0));
339 
340  if (match.HasValue() && match.Value()->IsJSFunction()) {
341  Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value());
342 
343  // If the target function was never invoked, its feedback cell array might
344  // not contain a feedback vector. We ensure at this point that it's created.
345  JSFunction::EnsureFeedbackVector(function);
346 
347  // The inlinee specializes to the context from the JSFunction object.
348  context_out = jsgraph()->Constant(handle(function->context(), isolate()));
349  feedback_vector_out = handle(function->feedback_vector(), isolate());
350  return;
351  }
352 
353  if (match.IsJSCreateClosure()) {
354  CreateClosureParameters const& p = CreateClosureParametersOf(match.op());
355 
356  // Load the feedback vector of the target by looking up its vector cell at
357  // the instantiation site (we only decide to inline if it's populated).
358  Handle<FeedbackCell> cell = p.feedback_cell();
359  DCHECK(cell->value()->IsFeedbackVector());
360 
361  // The inlinee uses the locally provided context at instantiation.
362  context_out = NodeProperties::GetContextInput(match.node());
363  feedback_vector_out =
364  handle(FeedbackVector::cast(cell->value()), isolate());
365  return;
366  }
367 
368  // Must succeed.
369  UNREACHABLE();
370 }
371 
372 Reduction JSInliner::Reduce(Node* node) {
373  if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange();
374  return ReduceJSCall(node);
375 }
376 
377 Handle<Context> JSInliner::native_context() const {
378  return handle(info_->native_context(), isolate());
379 }
380 
381 Reduction JSInliner::ReduceJSCall(Node* node) {
382  DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
383  Handle<SharedFunctionInfo> shared_info;
384  JSCallAccessor call(node);
385 
386  // Determine the call target.
387  if (!DetermineCallTarget(node, shared_info)) return NoChange();
388 
389  // Function must be inlineable.
390  if (!shared_info->IsInlineable()) {
391  TRACE("Not inlining %s into %s because callee is not inlineable\n",
392  shared_info->DebugName()->ToCString().get(),
393  info_->shared_info()->DebugName()->ToCString().get());
394  return NoChange();
395  }
396 
397  // Constructor must be constructable.
398  if (node->opcode() == IrOpcode::kJSConstruct &&
399  !IsConstructable(shared_info->kind())) {
400  TRACE("Not inlining %s into %s because constructor is not constructable.\n",
401  shared_info->DebugName()->ToCString().get(),
402  info_->shared_info()->DebugName()->ToCString().get());
403  return NoChange();
404  }
405 
406  // Class constructors are callable, but [[Call]] will raise an exception.
407  // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ).
408  if (node->opcode() == IrOpcode::kJSCall &&
409  IsClassConstructor(shared_info->kind())) {
410  TRACE("Not inlining %s into %s because callee is a class constructor.\n",
411  shared_info->DebugName()->ToCString().get(),
412  info_->shared_info()->DebugName()->ToCString().get());
413  return NoChange();
414  }
415 
416  // Function contains break points.
417  if (shared_info->HasBreakInfo()) {
418  TRACE("Not inlining %s into %s because callee may contain break points\n",
419  shared_info->DebugName()->ToCString().get(),
420  info_->shared_info()->DebugName()->ToCString().get());
421  return NoChange();
422  }
423 
424  // To ensure inlining always terminates, we have an upper limit on inlining
425  // the nested calls.
426  int nesting_level = 0;
427  for (Node* frame_state = call.frame_state();
428  frame_state->opcode() == IrOpcode::kFrameState;
429  frame_state = frame_state->InputAt(kFrameStateOuterStateInput)) {
430  nesting_level++;
431  if (nesting_level > kMaxDepthForInlining) {
432  TRACE(
433  "Not inlining %s into %s because call has exceeded the maximum depth "
434  "for function inlining\n",
435  shared_info->DebugName()->ToCString().get(),
436  info_->shared_info()->DebugName()->ToCString().get());
437  return NoChange();
438  }
439  }
440 
441  // Calls surrounded by a local try-block are only inlined if the appropriate
442  // flag is active. We also discover the {IfException} projection this way.
443  Node* exception_target = nullptr;
444  if (NodeProperties::IsExceptionalCall(node, &exception_target) &&
445  !FLAG_inline_into_try) {
446  TRACE(
447  "Try block surrounds #%d:%s and --no-inline-into-try active, so not "
448  "inlining %s into %s.\n",
449  exception_target->id(), exception_target->op()->mnemonic(),
450  shared_info->DebugName()->ToCString().get(),
451  info_->shared_info()->DebugName()->ToCString().get());
452  return NoChange();
453  }
454 
455  if (!shared_info->is_compiled() &&
456  !Compiler::Compile(shared_info, Compiler::CLEAR_EXCEPTION)) {
457  TRACE("Not inlining %s into %s because bytecode generation failed\n",
458  shared_info->DebugName()->ToCString().get(),
459  info_->shared_info()->DebugName()->ToCString().get());
460  return NoChange();
461  }
462 
463  // ----------------------------------------------------------------
464  // After this point, we've made a decision to inline this function.
465  // We shall not bailout from inlining if we got here.
466 
467  TRACE("Inlining %s into %s%s\n", shared_info->DebugName()->ToCString().get(),
468  info_->shared_info()->DebugName()->ToCString().get(),
469  (exception_target != nullptr) ? " (inside try-block)" : "");
470 
471  // Get the bytecode array.
472  Handle<BytecodeArray> bytecode_array =
473  handle(shared_info->GetBytecodeArray(), isolate());
474 
475  // Determine the targets feedback vector and its context.
476  Node* context;
477  Handle<FeedbackVector> feedback_vector;
478  DetermineCallContext(node, context, feedback_vector);
479 
480  // Remember that we inlined this function.
481  int inlining_id = info_->AddInlinedFunction(
482  shared_info, bytecode_array, source_positions_->GetSourcePosition(node));
483 
484  // Create the subgraph for the inlinee.
485  Node* start;
486  Node* end;
487  {
488  // Run the BytecodeGraphBuilder to create the subgraph.
489  Graph::SubgraphScope scope(graph());
490  JSTypeHintLowering::Flags flags = JSTypeHintLowering::kNoFlags;
491  if (info_->is_bailout_on_uninitialized()) {
492  flags |= JSTypeHintLowering::kBailoutOnUninitialized;
493  }
494  CallFrequency frequency = call.frequency();
495  BytecodeGraphBuilder graph_builder(
496  zone(), bytecode_array, shared_info, feedback_vector, BailoutId::None(),
497  jsgraph(), frequency, source_positions_, native_context(), inlining_id,
498  flags, false, info_->is_analyze_environment_liveness());
499  graph_builder.CreateGraph();
500 
501  // Extract the inlinee start/end nodes.
502  start = graph()->start();
503  end = graph()->end();
504  }
505 
506  // If we are inlining into a surrounding exception handler, we collect all
507  // potentially throwing nodes within the inlinee that are not handled locally
508  // by the inlinee itself. They are later wired into the surrounding handler.
509  NodeVector uncaught_subcalls(local_zone_);
510  if (exception_target != nullptr) {
511  // Find all uncaught 'calls' in the inlinee.
512  AllNodes inlined_nodes(local_zone_, end, graph());
513  for (Node* subnode : inlined_nodes.reachable) {
514  // Every possibly throwing node should get {IfSuccess} and {IfException}
515  // projections, unless there already is local exception handling.
516  if (subnode->op()->HasProperty(Operator::kNoThrow)) continue;
517  if (!NodeProperties::IsExceptionalCall(subnode)) {
518  DCHECK_EQ(2, subnode->op()->ControlOutputCount());
519  uncaught_subcalls.push_back(subnode);
520  }
521  }
522  }
523 
524  Node* frame_state = call.frame_state();
525  Node* new_target = jsgraph()->UndefinedConstant();
526 
527  // Inline {JSConstruct} requires some additional magic.
528  if (node->opcode() == IrOpcode::kJSConstruct) {
529  // Swizzle the inputs of the {JSConstruct} node to look like inputs to a
530  // normal {JSCall} node so that the rest of the inlining machinery
531  // behaves as if we were dealing with a regular function invocation.
532  new_target = call.new_target(); // Retrieve new target value input.
533  node->RemoveInput(call.formal_arguments() + 1); // Drop new target.
534  node->InsertInput(graph()->zone(), 1, new_target);
535 
536  // Insert nodes around the call that model the behavior required for a
537  // constructor dispatch (allocate implicit receiver and check return value).
538  // This models the behavior usually accomplished by our {JSConstructStub}.
539  // Note that the context has to be the callers context (input to call node).
540  // Also note that by splitting off the {JSCreate} piece of the constructor
541  // call, we create an observable deoptimization point after the receiver
542  // instantiation but before the invocation (i.e. inside {JSConstructStub}
543  // where execution continues at {construct_stub_create_deopt_pc_offset}).
544  Node* receiver = jsgraph()->TheHoleConstant(); // Implicit receiver.
545  Node* context = NodeProperties::GetContextInput(node);
546  if (NeedsImplicitReceiver(shared_info)) {
547  Node* effect = NodeProperties::GetEffectInput(node);
548  Node* control = NodeProperties::GetControlInput(node);
549  Node* frame_state_inside = CreateArtificialFrameState(
550  node, frame_state, call.formal_arguments(),
551  BailoutId::ConstructStubCreate(), FrameStateType::kConstructStub,
552  shared_info, context);
553  Node* create =
554  graph()->NewNode(javascript()->Create(), call.target(), new_target,
555  context, frame_state_inside, effect, control);
556  uncaught_subcalls.push_back(create); // Adds {IfSuccess} & {IfException}.
557  NodeProperties::ReplaceControlInput(node, create);
558  NodeProperties::ReplaceEffectInput(node, create);
559  // Placeholder to hold {node}'s value dependencies while {node} is
560  // replaced.
561  Node* dummy = graph()->NewNode(common()->Dead());
562  NodeProperties::ReplaceUses(node, dummy, node, node, node);
563  Node* result;
564  // Insert a check of the return value to determine whether the return
565  // value or the implicit receiver should be selected as a result of the
566  // call.
567  Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), node);
568  result =
569  graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
570  check, node, create);
571  receiver = create; // The implicit receiver.
572  ReplaceWithValue(dummy, result);
573  } else if (IsDerivedConstructor(shared_info->kind())) {
574  Node* node_success =
575  NodeProperties::FindSuccessfulControlProjection(node);
576  Node* is_receiver =
577  graph()->NewNode(simplified()->ObjectIsReceiver(), node);
578  Node* branch_is_receiver =
579  graph()->NewNode(common()->Branch(), is_receiver, node_success);
580  Node* branch_is_receiver_true =
581  graph()->NewNode(common()->IfTrue(), branch_is_receiver);
582  Node* branch_is_receiver_false =
583  graph()->NewNode(common()->IfFalse(), branch_is_receiver);
584  branch_is_receiver_false =
585  graph()->NewNode(javascript()->CallRuntime(
586  Runtime::kThrowConstructorReturnedNonObject),
587  context, NodeProperties::GetFrameStateInput(node),
588  node, branch_is_receiver_false);
589  uncaught_subcalls.push_back(branch_is_receiver_false);
590  branch_is_receiver_false =
591  graph()->NewNode(common()->Throw(), branch_is_receiver_false,
592  branch_is_receiver_false);
593  NodeProperties::MergeControlToEnd(graph(), common(),
594  branch_is_receiver_false);
595 
596  ReplaceWithValue(node_success, node_success, node_success,
597  branch_is_receiver_true);
598  // Fix input destroyed by the above {ReplaceWithValue} call.
599  NodeProperties::ReplaceControlInput(branch_is_receiver, node_success, 0);
600  }
601  node->ReplaceInput(1, receiver);
602  // Insert a construct stub frame into the chain of frame states. This will
603  // reconstruct the proper frame when deoptimizing within the constructor.
604  frame_state = CreateArtificialFrameState(
605  node, frame_state, call.formal_arguments(),
606  BailoutId::ConstructStubInvoke(), FrameStateType::kConstructStub,
607  shared_info, context);
608  }
609 
610  // Insert a JSConvertReceiver node for sloppy callees. Note that the context
611  // passed into this node has to be the callees context (loaded above).
612  if (node->opcode() == IrOpcode::kJSCall &&
613  is_sloppy(shared_info->language_mode()) && !shared_info->native()) {
614  Node* effect = NodeProperties::GetEffectInput(node);
615  if (NodeProperties::CanBePrimitive(broker(), call.receiver(), effect)) {
616  CallParameters const& p = CallParametersOf(node->op());
617  Node* global_proxy = jsgraph()->HeapConstant(
618  handle(info_->native_context()->global_proxy(), isolate()));
619  Node* receiver = effect =
620  graph()->NewNode(simplified()->ConvertReceiver(p.convert_mode()),
621  call.receiver(), global_proxy, effect, start);
622  NodeProperties::ReplaceValueInput(node, receiver, 1);
623  NodeProperties::ReplaceEffectInput(node, effect);
624  }
625  }
626 
627  // Insert argument adaptor frame if required. The callees formal parameter
628  // count (i.e. value outputs of start node minus target, receiver, new target,
629  // arguments count and context) have to match the number of arguments passed
630  // to the call.
631  int parameter_count = shared_info->internal_formal_parameter_count();
632  DCHECK_EQ(parameter_count, start->op()->ValueOutputCount() - 5);
633  if (call.formal_arguments() != parameter_count) {
634  frame_state = CreateArtificialFrameState(
635  node, frame_state, call.formal_arguments(), BailoutId::None(),
636  FrameStateType::kArgumentsAdaptor, shared_info);
637  }
638 
639  return InlineCall(node, new_target, context, frame_state, start, end,
640  exception_target, uncaught_subcalls);
641 }
642 
643 Graph* JSInliner::graph() const { return jsgraph()->graph(); }
644 
645 JSOperatorBuilder* JSInliner::javascript() const {
646  return jsgraph()->javascript();
647 }
648 
649 CommonOperatorBuilder* JSInliner::common() const { return jsgraph()->common(); }
650 
651 SimplifiedOperatorBuilder* JSInliner::simplified() const {
652  return jsgraph()->simplified();
653 }
654 
655 #undef TRACE
656 
657 } // namespace compiler
658 } // namespace internal
659 } // namespace v8
Definition: libplatform.h:13