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-numbering.h"
8 #include "src/ast/ast.h"
9 #include "src/compilation-info.h"
10 #include "src/compiler.h"
11 #include "src/compiler/all-nodes.h"
12 #include "src/compiler/ast-graph-builder.h"
13 #include "src/compiler/ast-loop-assignment-analyzer.h"
14 #include "src/compiler/bytecode-graph-builder.h"
15 #include "src/compiler/common-operator.h"
16 #include "src/compiler/graph-reducer.h"
17 #include "src/compiler/js-operator.h"
18 #include "src/compiler/node-matchers.h"
19 #include "src/compiler/node-properties.h"
20 #include "src/compiler/operator-properties.h"
21 #include "src/compiler/simplified-operator.h"
22 #include "src/compiler/type-hint-analyzer.h"
23 #include "src/isolate-inl.h"
24 #include "src/parsing/parse-info.h"
25 #include "src/parsing/rewriter.h"
26 
27 namespace v8 {
28 namespace internal {
29 namespace compiler {
30 
31 #define TRACE(...)                                      \
32   do {                                                  \
33     if (FLAG_trace_turbo_inlining) PrintF(__VA_ARGS__); \
34   } while (false)
35 
36 
37 // Provides convenience accessors for the common layout of nodes having either
38 // the {JSCallFunction} or the {JSCallConstruct} operator.
39 class JSCallAccessor {
40  public:
JSCallAccessor(Node * call)41   explicit JSCallAccessor(Node* call) : call_(call) {
42     DCHECK(call->opcode() == IrOpcode::kJSCallFunction ||
43            call->opcode() == IrOpcode::kJSCallConstruct);
44   }
45 
target()46   Node* target() {
47     // Both, {JSCallFunction} and {JSCallConstruct}, have same layout here.
48     return call_->InputAt(0);
49   }
50 
receiver()51   Node* receiver() {
52     DCHECK_EQ(IrOpcode::kJSCallFunction, call_->opcode());
53     return call_->InputAt(1);
54   }
55 
new_target()56   Node* new_target() {
57     DCHECK_EQ(IrOpcode::kJSCallConstruct, call_->opcode());
58     return call_->InputAt(formal_arguments() + 1);
59   }
60 
frame_state()61   Node* frame_state() {
62     // Both, {JSCallFunction} and {JSCallConstruct}, have frame state.
63     return NodeProperties::GetFrameStateInput(call_);
64   }
65 
formal_arguments()66   int formal_arguments() {
67     // Both, {JSCallFunction} and {JSCallConstruct}, have two extra inputs:
68     //  - JSCallConstruct: Includes target function and new target.
69     //  - JSCallFunction: Includes target function and receiver.
70     return call_->op()->ValueInputCount() - 2;
71   }
72 
frequency() const73   float frequency() const {
74     return (call_->opcode() == IrOpcode::kJSCallFunction)
75                ? CallFunctionParametersOf(call_->op()).frequency()
76                : CallConstructParametersOf(call_->op()).frequency();
77   }
78 
79  private:
80   Node* call_;
81 };
82 
InlineCall(Node * call,Node * new_target,Node * context,Node * frame_state,Node * start,Node * end,Node * exception_target,const NodeVector & uncaught_subcalls)83 Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context,
84                                 Node* frame_state, Node* start, Node* end,
85                                 Node* exception_target,
86                                 const NodeVector& uncaught_subcalls) {
87   // The scheduler is smart enough to place our code; we just ensure {control}
88   // becomes the control input of the start of the inlinee, and {effect} becomes
89   // the effect input of the start of the inlinee.
90   Node* control = NodeProperties::GetControlInput(call);
91   Node* effect = NodeProperties::GetEffectInput(call);
92 
93   int const inlinee_new_target_index =
94       static_cast<int>(start->op()->ValueOutputCount()) - 3;
95   int const inlinee_arity_index =
96       static_cast<int>(start->op()->ValueOutputCount()) - 2;
97   int const inlinee_context_index =
98       static_cast<int>(start->op()->ValueOutputCount()) - 1;
99 
100   // {inliner_inputs} counts JSFunction, receiver, arguments, but not
101   // new target value, argument count, context, effect or control.
102   int inliner_inputs = call->op()->ValueInputCount();
103   // Iterate over all uses of the start node.
104   for (Edge edge : start->use_edges()) {
105     Node* use = edge.from();
106     switch (use->opcode()) {
107       case IrOpcode::kParameter: {
108         int index = 1 + ParameterIndexOf(use->op());
109         DCHECK_LE(index, inlinee_context_index);
110         if (index < inliner_inputs && index < inlinee_new_target_index) {
111           // There is an input from the call, and the index is a value
112           // projection but not the context, so rewire the input.
113           Replace(use, call->InputAt(index));
114         } else if (index == inlinee_new_target_index) {
115           // The projection is requesting the new target value.
116           Replace(use, new_target);
117         } else if (index == inlinee_arity_index) {
118           // The projection is requesting the number of arguments.
119           Replace(use, jsgraph()->Constant(inliner_inputs - 2));
120         } else if (index == inlinee_context_index) {
121           // The projection is requesting the inlinee function context.
122           Replace(use, context);
123         } else {
124           // Call has fewer arguments than required, fill with undefined.
125           Replace(use, jsgraph()->UndefinedConstant());
126         }
127         break;
128       }
129       default:
130         if (NodeProperties::IsEffectEdge(edge)) {
131           edge.UpdateTo(effect);
132         } else if (NodeProperties::IsControlEdge(edge)) {
133           edge.UpdateTo(control);
134         } else if (NodeProperties::IsFrameStateEdge(edge)) {
135           edge.UpdateTo(frame_state);
136         } else {
137           UNREACHABLE();
138         }
139         break;
140     }
141   }
142 
143   if (exception_target != nullptr) {
144     // Link uncaught calls in the inlinee to {exception_target}
145     int subcall_count = static_cast<int>(uncaught_subcalls.size());
146     if (subcall_count > 0) {
147       TRACE(
148           "Inlinee contains %d calls without IfException; "
149           "linking to existing IfException\n",
150           subcall_count);
151     }
152     NodeVector on_exception_nodes(local_zone_);
153     for (Node* subcall : uncaught_subcalls) {
154       Node* on_exception =
155           graph()->NewNode(common()->IfException(), subcall, subcall);
156       on_exception_nodes.push_back(on_exception);
157     }
158 
159     DCHECK_EQ(subcall_count, static_cast<int>(on_exception_nodes.size()));
160     if (subcall_count > 0) {
161       Node* control_output =
162           graph()->NewNode(common()->Merge(subcall_count), subcall_count,
163                            &on_exception_nodes.front());
164       NodeVector values_effects(local_zone_);
165       values_effects = on_exception_nodes;
166       values_effects.push_back(control_output);
167       Node* value_output = graph()->NewNode(
168           common()->Phi(MachineRepresentation::kTagged, subcall_count),
169           subcall_count + 1, &values_effects.front());
170       Node* effect_output =
171           graph()->NewNode(common()->EffectPhi(subcall_count),
172                            subcall_count + 1, &values_effects.front());
173       ReplaceWithValue(exception_target, value_output, effect_output,
174                        control_output);
175     } else {
176       ReplaceWithValue(exception_target, exception_target, exception_target,
177                        jsgraph()->Dead());
178     }
179   }
180 
181   NodeVector values(local_zone_);
182   NodeVector effects(local_zone_);
183   NodeVector controls(local_zone_);
184   for (Node* const input : end->inputs()) {
185     switch (input->opcode()) {
186       case IrOpcode::kReturn:
187         values.push_back(NodeProperties::GetValueInput(input, 1));
188         effects.push_back(NodeProperties::GetEffectInput(input));
189         controls.push_back(NodeProperties::GetControlInput(input));
190         break;
191       case IrOpcode::kDeoptimize:
192       case IrOpcode::kTerminate:
193       case IrOpcode::kThrow:
194         NodeProperties::MergeControlToEnd(graph(), common(), input);
195         Revisit(graph()->end());
196         break;
197       default:
198         UNREACHABLE();
199         break;
200     }
201   }
202   DCHECK_EQ(values.size(), effects.size());
203   DCHECK_EQ(values.size(), controls.size());
204 
205   // Depending on whether the inlinee produces a value, we either replace value
206   // uses with said value or kill value uses if no value can be returned.
207   if (values.size() > 0) {
208     int const input_count = static_cast<int>(controls.size());
209     Node* control_output = graph()->NewNode(common()->Merge(input_count),
210                                             input_count, &controls.front());
211     values.push_back(control_output);
212     effects.push_back(control_output);
213     Node* value_output = graph()->NewNode(
214         common()->Phi(MachineRepresentation::kTagged, input_count),
215         static_cast<int>(values.size()), &values.front());
216     Node* effect_output =
217         graph()->NewNode(common()->EffectPhi(input_count),
218                          static_cast<int>(effects.size()), &effects.front());
219     ReplaceWithValue(call, value_output, effect_output, control_output);
220     return Changed(value_output);
221   } else {
222     ReplaceWithValue(call, call, call, jsgraph()->Dead());
223     return Changed(call);
224   }
225 }
226 
227 
CreateArtificialFrameState(Node * node,Node * outer_frame_state,int parameter_count,FrameStateType frame_state_type,Handle<SharedFunctionInfo> shared)228 Node* JSInliner::CreateArtificialFrameState(Node* node, Node* outer_frame_state,
229                                             int parameter_count,
230                                             FrameStateType frame_state_type,
231                                             Handle<SharedFunctionInfo> shared) {
232   const FrameStateFunctionInfo* state_info =
233       common()->CreateFrameStateFunctionInfo(frame_state_type,
234                                              parameter_count + 1, 0, shared);
235 
236   const Operator* op = common()->FrameState(
237       BailoutId(-1), OutputFrameStateCombine::Ignore(), state_info);
238   const Operator* op0 = common()->StateValues(0);
239   Node* node0 = graph()->NewNode(op0);
240   NodeVector params(local_zone_);
241   for (int parameter = 0; parameter < parameter_count + 1; ++parameter) {
242     params.push_back(node->InputAt(1 + parameter));
243   }
244   const Operator* op_param =
245       common()->StateValues(static_cast<int>(params.size()));
246   Node* params_node = graph()->NewNode(
247       op_param, static_cast<int>(params.size()), &params.front());
248   return graph()->NewNode(op, params_node, node0, node0,
249                           jsgraph()->UndefinedConstant(), node->InputAt(0),
250                           outer_frame_state);
251 }
252 
CreateTailCallerFrameState(Node * node,Node * frame_state)253 Node* JSInliner::CreateTailCallerFrameState(Node* node, Node* frame_state) {
254   FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
255   Handle<SharedFunctionInfo> shared;
256   frame_info.shared_info().ToHandle(&shared);
257 
258   Node* function = frame_state->InputAt(kFrameStateFunctionInput);
259 
260   // If we are inlining a tail call drop caller's frame state and an
261   // arguments adaptor if it exists.
262   frame_state = NodeProperties::GetFrameStateInput(frame_state);
263   if (frame_state->opcode() == IrOpcode::kFrameState) {
264     FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
265     if (frame_info.type() == FrameStateType::kArgumentsAdaptor) {
266       frame_state = NodeProperties::GetFrameStateInput(frame_state);
267     }
268   }
269 
270   const FrameStateFunctionInfo* state_info =
271       common()->CreateFrameStateFunctionInfo(
272           FrameStateType::kTailCallerFunction, 0, 0, shared);
273 
274   const Operator* op = common()->FrameState(
275       BailoutId(-1), OutputFrameStateCombine::Ignore(), state_info);
276   const Operator* op0 = common()->StateValues(0);
277   Node* node0 = graph()->NewNode(op0);
278   return graph()->NewNode(op, node0, node0, node0,
279                           jsgraph()->UndefinedConstant(), function,
280                           frame_state);
281 }
282 
283 namespace {
284 
285 // TODO(turbofan): Shall we move this to the NodeProperties? Or some (untyped)
286 // alias analyzer?
IsSame(Node * a,Node * b)287 bool IsSame(Node* a, Node* b) {
288   if (a == b) {
289     return true;
290   } else if (a->opcode() == IrOpcode::kCheckHeapObject) {
291     return IsSame(a->InputAt(0), b);
292   } else if (b->opcode() == IrOpcode::kCheckHeapObject) {
293     return IsSame(a, b->InputAt(0));
294   }
295   return false;
296 }
297 
298 // TODO(bmeurer): Unify this with the witness helper functions in the
299 // js-builtin-reducer.cc once we have a better understanding of the
300 // map tracking we want to do, and eventually changed the CheckMaps
301 // operator to carry map constants on the operator instead of inputs.
302 // I.e. if the CheckMaps has some kind of SmallMapSet as operator
303 // parameter, then this could be changed to call a generic
304 //
305 //   SmallMapSet NodeProperties::CollectMapWitness(receiver, effect)
306 //
307 // function, which either returns the map set from the CheckMaps or
308 // a singleton set from a StoreField.
NeedsConvertReceiver(Node * receiver,Node * effect)309 bool NeedsConvertReceiver(Node* receiver, Node* effect) {
310   for (Node* dominator = effect;;) {
311     if (dominator->opcode() == IrOpcode::kCheckMaps &&
312         IsSame(dominator->InputAt(0), receiver)) {
313       // Check if all maps have the given {instance_type}.
314       for (int i = 1; i < dominator->op()->ValueInputCount(); ++i) {
315         HeapObjectMatcher m(NodeProperties::GetValueInput(dominator, i));
316         if (!m.HasValue()) return true;
317         Handle<Map> const map = Handle<Map>::cast(m.Value());
318         if (!map->IsJSReceiverMap()) return true;
319       }
320       return false;
321     }
322     switch (dominator->opcode()) {
323       case IrOpcode::kStoreField: {
324         FieldAccess const& access = FieldAccessOf(dominator->op());
325         if (access.base_is_tagged == kTaggedBase &&
326             access.offset == HeapObject::kMapOffset) {
327           return true;
328         }
329         break;
330       }
331       case IrOpcode::kStoreElement:
332       case IrOpcode::kStoreTypedElement:
333         break;
334       default: {
335         DCHECK_EQ(1, dominator->op()->EffectOutputCount());
336         if (dominator->op()->EffectInputCount() != 1 ||
337             !dominator->op()->HasProperty(Operator::kNoWrite)) {
338           // Didn't find any appropriate CheckMaps node.
339           return true;
340         }
341         break;
342       }
343     }
344     dominator = NodeProperties::GetEffectInput(dominator);
345   }
346 }
347 
348 // TODO(mstarzinger,verwaest): Move this predicate onto SharedFunctionInfo?
NeedsImplicitReceiver(Handle<SharedFunctionInfo> shared_info)349 bool NeedsImplicitReceiver(Handle<SharedFunctionInfo> shared_info) {
350   DisallowHeapAllocation no_gc;
351   Isolate* const isolate = shared_info->GetIsolate();
352   Code* const construct_stub = shared_info->construct_stub();
353   return construct_stub != *isolate->builtins()->JSBuiltinsConstructStub() &&
354          construct_stub !=
355              *isolate->builtins()->JSBuiltinsConstructStubForDerived() &&
356          construct_stub != *isolate->builtins()->JSConstructStubApi();
357 }
358 
IsNonConstructible(Handle<SharedFunctionInfo> shared_info)359 bool IsNonConstructible(Handle<SharedFunctionInfo> shared_info) {
360   DisallowHeapAllocation no_gc;
361   Isolate* const isolate = shared_info->GetIsolate();
362   Code* const construct_stub = shared_info->construct_stub();
363   return construct_stub == *isolate->builtins()->ConstructedNonConstructable();
364 }
365 
366 }  // namespace
367 
368 
Reduce(Node * node)369 Reduction JSInliner::Reduce(Node* node) {
370   if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange();
371 
372   // This reducer can handle both normal function calls as well a constructor
373   // calls whenever the target is a constant function object, as follows:
374   //  - JSCallFunction(target:constant, receiver, args...)
375   //  - JSCallConstruct(target:constant, args..., new.target)
376   HeapObjectMatcher match(node->InputAt(0));
377   if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange();
378   Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value());
379 
380   return ReduceJSCall(node, function);
381 }
382 
ReduceJSCall(Node * node,Handle<JSFunction> function)383 Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) {
384   DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
385   JSCallAccessor call(node);
386   Handle<SharedFunctionInfo> shared_info(function->shared());
387 
388   // Function must be inlineable.
389   if (!shared_info->IsInlineable()) {
390     TRACE("Not inlining %s into %s because callee is not inlineable\n",
391           shared_info->DebugName()->ToCString().get(),
392           info_->shared_info()->DebugName()->ToCString().get());
393     return NoChange();
394   }
395 
396   // Constructor must be constructable.
397   if (node->opcode() == IrOpcode::kJSCallConstruct &&
398       IsNonConstructible(shared_info)) {
399     TRACE("Not inlining %s into %s because constructor is not constructable.\n",
400           shared_info->DebugName()->ToCString().get(),
401           info_->shared_info()->DebugName()->ToCString().get());
402     return NoChange();
403   }
404 
405   // Class constructors are callable, but [[Call]] will raise an exception.
406   // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ).
407   if (node->opcode() == IrOpcode::kJSCallFunction &&
408       IsClassConstructor(shared_info->kind())) {
409     TRACE("Not inlining %s into %s because callee is a class constructor.\n",
410           shared_info->DebugName()->ToCString().get(),
411           info_->shared_info()->DebugName()->ToCString().get());
412     return NoChange();
413   }
414 
415   // Function contains break points.
416   if (shared_info->HasDebugInfo()) {
417     TRACE("Not inlining %s into %s because callee may contain break points\n",
418           shared_info->DebugName()->ToCString().get(),
419           info_->shared_info()->DebugName()->ToCString().get());
420     return NoChange();
421   }
422 
423   // Disallow cross native-context inlining for now. This means that all parts
424   // of the resulting code will operate on the same global object.
425   // This also prevents cross context leaks for asm.js code, where we could
426   // inline functions from a different context and hold on to that context (and
427   // closure) from the code object.
428   // TODO(turbofan): We might want to revisit this restriction later when we
429   // have a need for this, and we know how to model different native contexts
430   // in the same graph in a compositional way.
431   if (function->context()->native_context() !=
432       info_->context()->native_context()) {
433     TRACE("Not inlining %s into %s because of different native contexts\n",
434           shared_info->DebugName()->ToCString().get(),
435           info_->shared_info()->DebugName()->ToCString().get());
436     return NoChange();
437   }
438 
439   // TODO(turbofan): TranslatedState::GetAdaptedArguments() currently relies on
440   // not inlining recursive functions. We might want to relax that at some
441   // point.
442   for (Node* frame_state = call.frame_state();
443        frame_state->opcode() == IrOpcode::kFrameState;
444        frame_state = frame_state->InputAt(kFrameStateOuterStateInput)) {
445     FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state);
446     Handle<SharedFunctionInfo> frame_shared_info;
447     if (frame_info.shared_info().ToHandle(&frame_shared_info) &&
448         *frame_shared_info == *shared_info) {
449       TRACE("Not inlining %s into %s because call is recursive\n",
450             shared_info->DebugName()->ToCString().get(),
451             info_->shared_info()->DebugName()->ToCString().get());
452       return NoChange();
453     }
454   }
455 
456   // Find the IfException node, if any.
457   Node* exception_target = nullptr;
458   for (Edge edge : node->use_edges()) {
459     if (NodeProperties::IsControlEdge(edge) &&
460         edge.from()->opcode() == IrOpcode::kIfException) {
461       DCHECK_NULL(exception_target);
462       exception_target = edge.from();
463     }
464   }
465 
466   NodeVector uncaught_subcalls(local_zone_);
467 
468   if (exception_target != nullptr) {
469     if (!FLAG_inline_into_try) {
470       TRACE(
471           "Try block surrounds #%d:%s and --no-inline-into-try active, so not "
472           "inlining %s into %s.\n",
473           exception_target->id(), exception_target->op()->mnemonic(),
474           shared_info->DebugName()->ToCString().get(),
475           info_->shared_info()->DebugName()->ToCString().get());
476       return NoChange();
477     } else {
478       TRACE(
479           "Inlining %s into %s regardless of surrounding try-block to catcher "
480           "#%d:%s\n",
481           shared_info->DebugName()->ToCString().get(),
482           info_->shared_info()->DebugName()->ToCString().get(),
483           exception_target->id(), exception_target->op()->mnemonic());
484     }
485   }
486 
487   Zone zone(info_->isolate()->allocator(), ZONE_NAME);
488   ParseInfo parse_info(&zone, shared_info);
489   CompilationInfo info(&parse_info, function);
490   if (info_->is_deoptimization_enabled()) info.MarkAsDeoptimizationEnabled();
491   if (info_->is_type_feedback_enabled()) info.MarkAsTypeFeedbackEnabled();
492   if (info_->is_optimizing_from_bytecode()) info.MarkAsOptimizeFromBytecode();
493 
494   if (info.is_optimizing_from_bytecode() && !Compiler::EnsureBytecode(&info)) {
495     TRACE("Not inlining %s into %s because bytecode generation failed\n",
496           shared_info->DebugName()->ToCString().get(),
497           info_->shared_info()->DebugName()->ToCString().get());
498     if (info_->isolate()->has_pending_exception()) {
499       info_->isolate()->clear_pending_exception();
500     }
501     return NoChange();
502   }
503 
504   if (!info.is_optimizing_from_bytecode() &&
505       !Compiler::ParseAndAnalyze(info.parse_info())) {
506     TRACE("Not inlining %s into %s because parsing failed\n",
507           shared_info->DebugName()->ToCString().get(),
508           info_->shared_info()->DebugName()->ToCString().get());
509     if (info_->isolate()->has_pending_exception()) {
510       info_->isolate()->clear_pending_exception();
511     }
512     return NoChange();
513   }
514 
515   if (!info.is_optimizing_from_bytecode() &&
516       !Compiler::EnsureDeoptimizationSupport(&info)) {
517     TRACE("Not inlining %s into %s because deoptimization support failed\n",
518           shared_info->DebugName()->ToCString().get(),
519           info_->shared_info()->DebugName()->ToCString().get());
520     return NoChange();
521   }
522 
523   // Remember that we inlined this function. This needs to be called right
524   // after we ensure deoptimization support so that the code flusher
525   // does not remove the code with the deoptimization support.
526   int inlining_id = info_->AddInlinedFunction(
527       shared_info, source_positions_->GetSourcePosition(node));
528 
529   // ----------------------------------------------------------------
530   // After this point, we've made a decision to inline this function.
531   // We shall not bailout from inlining if we got here.
532 
533   TRACE("Inlining %s into %s\n",
534         shared_info->DebugName()->ToCString().get(),
535         info_->shared_info()->DebugName()->ToCString().get());
536 
537   // If function was lazily compiled, its literals array may not yet be set up.
538   JSFunction::EnsureLiterals(function);
539 
540   // Create the subgraph for the inlinee.
541   Node* start;
542   Node* end;
543   if (info.is_optimizing_from_bytecode()) {
544     // Run the BytecodeGraphBuilder to create the subgraph.
545     Graph::SubgraphScope scope(graph());
546     BytecodeGraphBuilder graph_builder(&zone, &info, jsgraph(),
547                                        call.frequency(), source_positions_,
548                                        inlining_id);
549     graph_builder.CreateGraph(false);
550 
551     // Extract the inlinee start/end nodes.
552     start = graph()->start();
553     end = graph()->end();
554   } else {
555     // Run the loop assignment analyzer on the inlinee.
556     AstLoopAssignmentAnalyzer loop_assignment_analyzer(&zone, &info);
557     LoopAssignmentAnalysis* loop_assignment =
558         loop_assignment_analyzer.Analyze();
559 
560     // Run the type hint analyzer on the inlinee.
561     TypeHintAnalyzer type_hint_analyzer(&zone);
562     TypeHintAnalysis* type_hint_analysis =
563         type_hint_analyzer.Analyze(handle(shared_info->code(), info.isolate()));
564 
565     // Run the AstGraphBuilder to create the subgraph.
566     Graph::SubgraphScope scope(graph());
567     AstGraphBuilderWithPositions graph_builder(
568         &zone, &info, jsgraph(), call.frequency(), loop_assignment,
569         type_hint_analysis, source_positions_, inlining_id);
570     graph_builder.CreateGraph(false);
571 
572     // Extract the inlinee start/end nodes.
573     start = graph()->start();
574     end = graph()->end();
575   }
576 
577   if (exception_target != nullptr) {
578     // Find all uncaught 'calls' in the inlinee.
579     AllNodes inlined_nodes(local_zone_, end, graph());
580     for (Node* subnode : inlined_nodes.reachable) {
581       // Every possibly throwing node with an IfSuccess should get an
582       // IfException.
583       if (subnode->op()->HasProperty(Operator::kNoThrow)) {
584         continue;
585       }
586       bool hasIfException = false;
587       for (Node* use : subnode->uses()) {
588         if (use->opcode() == IrOpcode::kIfException) {
589           hasIfException = true;
590           break;
591         }
592       }
593       if (!hasIfException) {
594         DCHECK_EQ(2, subnode->op()->ControlOutputCount());
595         uncaught_subcalls.push_back(subnode);
596       }
597     }
598   }
599 
600   Node* frame_state = call.frame_state();
601   Node* new_target = jsgraph()->UndefinedConstant();
602 
603   // Inline {JSCallConstruct} requires some additional magic.
604   if (node->opcode() == IrOpcode::kJSCallConstruct) {
605     // Insert nodes around the call that model the behavior required for a
606     // constructor dispatch (allocate implicit receiver and check return value).
607     // This models the behavior usually accomplished by our {JSConstructStub}.
608     // Note that the context has to be the callers context (input to call node).
609     Node* receiver = jsgraph()->TheHoleConstant();  // Implicit receiver.
610     if (NeedsImplicitReceiver(shared_info)) {
611       Node* frame_state_before = NodeProperties::FindFrameStateBefore(node);
612       Node* effect = NodeProperties::GetEffectInput(node);
613       Node* context = NodeProperties::GetContextInput(node);
614       Node* create = graph()->NewNode(javascript()->Create(), call.target(),
615                                       call.new_target(), context,
616                                       frame_state_before, effect);
617       NodeProperties::ReplaceEffectInput(node, create);
618       // Insert a check of the return value to determine whether the return
619       // value or the implicit receiver should be selected as a result of the
620       // call.
621       Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), node);
622       Node* select =
623           graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
624                            check, node, create);
625       NodeProperties::ReplaceUses(node, select, node, node, node);
626       // Fix-up inputs that have been mangled by the {ReplaceUses} call above.
627       NodeProperties::ReplaceValueInput(select, node, 1);  // Fix-up input.
628       NodeProperties::ReplaceValueInput(check, node, 0);   // Fix-up input.
629       receiver = create;  // The implicit receiver.
630     }
631 
632     // Swizzle the inputs of the {JSCallConstruct} node to look like inputs to a
633     // normal {JSCallFunction} node so that the rest of the inlining machinery
634     // behaves as if we were dealing with a regular function invocation.
635     new_target = call.new_target();  // Retrieve new target value input.
636     node->RemoveInput(call.formal_arguments() + 1);  // Drop new target.
637     node->InsertInput(graph()->zone(), 1, receiver);
638 
639     // Insert a construct stub frame into the chain of frame states. This will
640     // reconstruct the proper frame when deoptimizing within the constructor.
641     frame_state = CreateArtificialFrameState(
642         node, frame_state, call.formal_arguments(),
643         FrameStateType::kConstructStub, info.shared_info());
644   }
645 
646   // The inlinee specializes to the context from the JSFunction object.
647   // TODO(turbofan): We might want to load the context from the JSFunction at
648   // runtime in case we only know the SharedFunctionInfo once we have dynamic
649   // type feedback in the compiler.
650   Node* context = jsgraph()->Constant(handle(function->context()));
651 
652   // Insert a JSConvertReceiver node for sloppy callees. Note that the context
653   // passed into this node has to be the callees context (loaded above). Note
654   // that the frame state passed to the JSConvertReceiver must be the frame
655   // state _before_ the call; it is not necessary to fiddle with the receiver
656   // in that frame state tho, as the conversion of the receiver can be repeated
657   // any number of times, it's not observable.
658   if (node->opcode() == IrOpcode::kJSCallFunction &&
659       is_sloppy(shared_info->language_mode()) && !shared_info->native()) {
660     Node* effect = NodeProperties::GetEffectInput(node);
661     if (NeedsConvertReceiver(call.receiver(), effect)) {
662       const CallFunctionParameters& p = CallFunctionParametersOf(node->op());
663       Node* frame_state_before = NodeProperties::FindFrameStateBefore(node);
664       Node* convert = effect = graph()->NewNode(
665           javascript()->ConvertReceiver(p.convert_mode()), call.receiver(),
666           context, frame_state_before, effect, start);
667       NodeProperties::ReplaceValueInput(node, convert, 1);
668       NodeProperties::ReplaceEffectInput(node, effect);
669     }
670   }
671 
672   // If we are inlining a JS call at tail position then we have to pop current
673   // frame state and its potential arguments adaptor frame state in order to
674   // make the call stack be consistent with non-inlining case.
675   // After that we add a tail caller frame state which lets deoptimizer handle
676   // the case when the outermost function inlines a tail call (it should remove
677   // potential arguments adaptor frame that belongs to outermost function when
678   // deopt happens).
679   if (node->opcode() == IrOpcode::kJSCallFunction) {
680     const CallFunctionParameters& p = CallFunctionParametersOf(node->op());
681     if (p.tail_call_mode() == TailCallMode::kAllow) {
682       frame_state = CreateTailCallerFrameState(node, frame_state);
683     }
684   }
685 
686   // Insert argument adaptor frame if required. The callees formal parameter
687   // count (i.e. value outputs of start node minus target, receiver, new target,
688   // arguments count and context) have to match the number of arguments passed
689   // to the call.
690   int parameter_count = shared_info->internal_formal_parameter_count();
691   DCHECK_EQ(parameter_count, start->op()->ValueOutputCount() - 5);
692   if (call.formal_arguments() != parameter_count) {
693     frame_state = CreateArtificialFrameState(
694         node, frame_state, call.formal_arguments(),
695         FrameStateType::kArgumentsAdaptor, shared_info);
696   }
697 
698   return InlineCall(node, new_target, context, frame_state, start, end,
699                     exception_target, uncaught_subcalls);
700 }
701 
graph() const702 Graph* JSInliner::graph() const { return jsgraph()->graph(); }
703 
javascript() const704 JSOperatorBuilder* JSInliner::javascript() const {
705   return jsgraph()->javascript();
706 }
707 
common() const708 CommonOperatorBuilder* JSInliner::common() const { return jsgraph()->common(); }
709 
simplified() const710 SimplifiedOperatorBuilder* JSInliner::simplified() const {
711   return jsgraph()->simplified();
712 }
713 
714 }  // namespace compiler
715 }  // namespace internal
716 }  // namespace v8
717