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