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