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()), ¶ms.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