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