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