| OLD | NEW |
| 1 // Copyright 2014 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/compiler/js-inlining.h" | 5 #include "src/compiler/js-inlining.h" |
| 6 | 6 |
| 7 #include "src/ast/ast.h" | 7 #include "src/ast/ast.h" |
| 8 #include "src/compilation-info.h" | 8 #include "src/compilation-info.h" |
| 9 #include "src/compiler.h" | 9 #include "src/compiler.h" |
| 10 #include "src/compiler/all-nodes.h" | 10 #include "src/compiler/all-nodes.h" |
| (...skipping 329 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 340 | 340 |
| 341 bool IsNonConstructible(Handle<SharedFunctionInfo> shared_info) { | 341 bool IsNonConstructible(Handle<SharedFunctionInfo> shared_info) { |
| 342 DisallowHeapAllocation no_gc; | 342 DisallowHeapAllocation no_gc; |
| 343 Isolate* const isolate = shared_info->GetIsolate(); | 343 Isolate* const isolate = shared_info->GetIsolate(); |
| 344 Code* const construct_stub = shared_info->construct_stub(); | 344 Code* const construct_stub = shared_info->construct_stub(); |
| 345 return construct_stub == *isolate->builtins()->ConstructedNonConstructable(); | 345 return construct_stub == *isolate->builtins()->ConstructedNonConstructable(); |
| 346 } | 346 } |
| 347 | 347 |
| 348 } // namespace | 348 } // namespace |
| 349 | 349 |
| 350 | 350 // Determines whether the call target of the given call {node} is statically |
| 351 Reduction JSInliner::Reduce(Node* node) { | 351 // known and can be used as an inlining candidate. The {SharedFunctionInfo} of |
| 352 if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange(); | 352 // the call target is provided (the exact closure might be unknown). |
| 353 bool JSInliner::DetermineCallTarget( |
| 354 Node* node, Handle<SharedFunctionInfo>& shared_info_out) { |
| 355 DCHECK(IrOpcode::IsInlineeOpcode(node->opcode())); |
| 356 HeapObjectMatcher match(node->InputAt(0)); |
| 353 | 357 |
| 354 // This reducer can handle both normal function calls as well a constructor | 358 // This reducer can handle both normal function calls as well a constructor |
| 355 // calls whenever the target is a constant function object, as follows: | 359 // calls whenever the target is a constant function object, as follows: |
| 356 // - JSCall(target:constant, receiver, args...) | 360 // - JSCall(target:constant, receiver, args...) |
| 357 // - JSConstruct(target:constant, args..., new.target) | 361 // - JSConstruct(target:constant, args..., new.target) |
| 358 HeapObjectMatcher match(node->InputAt(0)); | 362 if (match.HasValue() && match.Value()->IsJSFunction()) { |
| 359 if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange(); | 363 Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value()); |
| 360 Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value()); | |
| 361 | 364 |
| 362 return ReduceJSCall(node, function); | 365 // Disallow cross native-context inlining for now. This means that all parts |
| 366 // of the resulting code will operate on the same global object. This also |
| 367 // prevents cross context leaks, where we could inline functions from a |
| 368 // different context and hold on to that context (and closure) from the code |
| 369 // object. |
| 370 // TODO(turbofan): We might want to revisit this restriction later when we |
| 371 // have a need for this, and we know how to model different native contexts |
| 372 // in the same graph in a compositional way. |
| 373 if (function->context()->native_context() != |
| 374 info_->context()->native_context()) { |
| 375 return false; |
| 376 } |
| 377 |
| 378 shared_info_out = handle(function->shared()); |
| 379 return true; |
| 380 } |
| 381 |
| 382 // This reducer can also handle calls where the target is statically known to |
| 383 // be the result of a closure instantiation operation, as follows: |
| 384 // - JSCall(JSCreateClosure[shared](context), receiver, args...) |
| 385 // - JSConstruct(JSCreateClosure[shared](context), args..., new.target) |
| 386 if (match.IsJSCreateClosure()) { |
| 387 CreateClosureParameters const& p = CreateClosureParametersOf(match.op()); |
| 388 |
| 389 // Disallow inlining in case the instantiation site was never run and hence |
| 390 // the vector cell does not contain a valid feedback vector for the call |
| 391 // target. |
| 392 // TODO(turbofan): We might consider to eagerly create the feedback vector |
| 393 // in such a case (in {DetermineCallContext} below) eventually. |
| 394 FeedbackVectorSlot slot = p.feedback().slot(); |
| 395 Handle<Cell> cell(Cell::cast(p.feedback().vector()->Get(slot))); |
| 396 if (!cell->value()->IsTypeFeedbackVector()) return false; |
| 397 |
| 398 shared_info_out = p.shared_info(); |
| 399 return true; |
| 400 } |
| 401 |
| 402 return false; |
| 363 } | 403 } |
| 364 | 404 |
| 365 Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) { | 405 // Determines statically known information about the call target (assuming that |
| 406 // the call target is known according to {DetermineCallTarget} above). The |
| 407 // following static information is provided: |
| 408 // - context : The context (as SSA value) bound by the call target. |
| 409 // - feedback_vector : The target is guaranteed to use this feedback vector. |
| 410 void JSInliner::DetermineCallContext( |
| 411 Node* node, Node*& context_out, |
| 412 Handle<TypeFeedbackVector>& feedback_vector_out) { |
| 366 DCHECK(IrOpcode::IsInlineeOpcode(node->opcode())); | 413 DCHECK(IrOpcode::IsInlineeOpcode(node->opcode())); |
| 414 HeapObjectMatcher match(node->InputAt(0)); |
| 415 |
| 416 if (match.HasValue() && match.Value()->IsJSFunction()) { |
| 417 Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value()); |
| 418 |
| 419 // If the target function was never invoked, its literals array might not |
| 420 // contain a feedback vector. We ensure at this point that it is created. |
| 421 JSFunction::EnsureLiterals(function); |
| 422 |
| 423 // The inlinee specializes to the context from the JSFunction object. |
| 424 context_out = jsgraph()->Constant(handle(function->context())); |
| 425 feedback_vector_out = handle(function->feedback_vector()); |
| 426 return; |
| 427 } |
| 428 |
| 429 if (match.IsJSCreateClosure()) { |
| 430 CreateClosureParameters const& p = CreateClosureParametersOf(match.op()); |
| 431 |
| 432 // Load the feedback vector of the target by looking up its vector cell at |
| 433 // the instantiation site (we only decide to inline if it's populated). |
| 434 FeedbackVectorSlot slot = p.feedback().slot(); |
| 435 Handle<Cell> cell(Cell::cast(p.feedback().vector()->Get(slot))); |
| 436 DCHECK(cell->value()->IsTypeFeedbackVector()); |
| 437 |
| 438 // The inlinee uses the locally provided context at instantiation. |
| 439 context_out = NodeProperties::GetContextInput(match.node()); |
| 440 feedback_vector_out = handle(TypeFeedbackVector::cast(cell->value())); |
| 441 return; |
| 442 } |
| 443 |
| 444 // Must succeed. |
| 445 UNREACHABLE(); |
| 446 } |
| 447 |
| 448 Reduction JSInliner::Reduce(Node* node) { |
| 449 if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange(); |
| 450 return ReduceJSCall(node); |
| 451 } |
| 452 |
| 453 Reduction JSInliner::ReduceJSCall(Node* node) { |
| 454 DCHECK(IrOpcode::IsInlineeOpcode(node->opcode())); |
| 455 Handle<SharedFunctionInfo> shared_info; |
| 367 JSCallAccessor call(node); | 456 JSCallAccessor call(node); |
| 368 Handle<SharedFunctionInfo> shared_info(function->shared()); | 457 |
| 458 // Determine the call target. |
| 459 if (!DetermineCallTarget(node, shared_info)) return NoChange(); |
| 369 | 460 |
| 370 // Inlining is only supported in the bytecode pipeline. | 461 // Inlining is only supported in the bytecode pipeline. |
| 371 if (!info_->is_optimizing_from_bytecode()) { | 462 if (!info_->is_optimizing_from_bytecode()) { |
| 372 TRACE("Inlining %s into %s is not supported in the deprecated pipeline\n", | 463 TRACE("Not inlining %s into %s due to use of the deprecated pipeline\n", |
| 373 shared_info->DebugName()->ToCString().get(), | 464 shared_info->DebugName()->ToCString().get(), |
| 374 info_->shared_info()->DebugName()->ToCString().get()); | 465 info_->shared_info()->DebugName()->ToCString().get()); |
| 375 return NoChange(); | 466 return NoChange(); |
| 376 } | 467 } |
| 377 | 468 |
| 378 // Function must be inlineable. | 469 // Function must be inlineable. |
| 379 if (!shared_info->IsInlineable()) { | 470 if (!shared_info->IsInlineable()) { |
| 380 TRACE("Not inlining %s into %s because callee is not inlineable\n", | 471 TRACE("Not inlining %s into %s because callee is not inlineable\n", |
| 381 shared_info->DebugName()->ToCString().get(), | 472 shared_info->DebugName()->ToCString().get(), |
| 382 info_->shared_info()->DebugName()->ToCString().get()); | 473 info_->shared_info()->DebugName()->ToCString().get()); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 403 } | 494 } |
| 404 | 495 |
| 405 // Function contains break points. | 496 // Function contains break points. |
| 406 if (shared_info->HasDebugInfo()) { | 497 if (shared_info->HasDebugInfo()) { |
| 407 TRACE("Not inlining %s into %s because callee may contain break points\n", | 498 TRACE("Not inlining %s into %s because callee may contain break points\n", |
| 408 shared_info->DebugName()->ToCString().get(), | 499 shared_info->DebugName()->ToCString().get(), |
| 409 info_->shared_info()->DebugName()->ToCString().get()); | 500 info_->shared_info()->DebugName()->ToCString().get()); |
| 410 return NoChange(); | 501 return NoChange(); |
| 411 } | 502 } |
| 412 | 503 |
| 413 // Disallow cross native-context inlining for now. This means that all parts | |
| 414 // of the resulting code will operate on the same global object. | |
| 415 // This also prevents cross context leaks for asm.js code, where we could | |
| 416 // inline functions from a different context and hold on to that context (and | |
| 417 // closure) from the code object. | |
| 418 // TODO(turbofan): We might want to revisit this restriction later when we | |
| 419 // have a need for this, and we know how to model different native contexts | |
| 420 // in the same graph in a compositional way. | |
| 421 if (function->context()->native_context() != | |
| 422 info_->context()->native_context()) { | |
| 423 TRACE("Not inlining %s into %s because of different native contexts\n", | |
| 424 shared_info->DebugName()->ToCString().get(), | |
| 425 info_->shared_info()->DebugName()->ToCString().get()); | |
| 426 return NoChange(); | |
| 427 } | |
| 428 | |
| 429 // TODO(turbofan): TranslatedState::GetAdaptedArguments() currently relies on | 504 // TODO(turbofan): TranslatedState::GetAdaptedArguments() currently relies on |
| 430 // not inlining recursive functions. We might want to relax that at some | 505 // not inlining recursive functions. We might want to relax that at some |
| 431 // point. | 506 // point. |
| 432 for (Node* frame_state = call.frame_state(); | 507 for (Node* frame_state = call.frame_state(); |
| 433 frame_state->opcode() == IrOpcode::kFrameState; | 508 frame_state->opcode() == IrOpcode::kFrameState; |
| 434 frame_state = frame_state->InputAt(kFrameStateOuterStateInput)) { | 509 frame_state = frame_state->InputAt(kFrameStateOuterStateInput)) { |
| 435 FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state); | 510 FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state); |
| 436 Handle<SharedFunctionInfo> frame_shared_info; | 511 Handle<SharedFunctionInfo> frame_shared_info; |
| 437 if (frame_info.shared_info().ToHandle(&frame_shared_info) && | 512 if (frame_info.shared_info().ToHandle(&frame_shared_info) && |
| 438 *frame_shared_info == *shared_info) { | 513 *frame_shared_info == *shared_info) { |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 497 shared_info, source_positions_->GetSourcePosition(node)); | 572 shared_info, source_positions_->GetSourcePosition(node)); |
| 498 | 573 |
| 499 // ---------------------------------------------------------------- | 574 // ---------------------------------------------------------------- |
| 500 // After this point, we've made a decision to inline this function. | 575 // After this point, we've made a decision to inline this function. |
| 501 // We shall not bailout from inlining if we got here. | 576 // We shall not bailout from inlining if we got here. |
| 502 | 577 |
| 503 TRACE("Inlining %s into %s\n", | 578 TRACE("Inlining %s into %s\n", |
| 504 shared_info->DebugName()->ToCString().get(), | 579 shared_info->DebugName()->ToCString().get(), |
| 505 info_->shared_info()->DebugName()->ToCString().get()); | 580 info_->shared_info()->DebugName()->ToCString().get()); |
| 506 | 581 |
| 507 // If function was lazily compiled, its literals array may not yet be set up. | 582 // Determine the targets feedback vector and its context. |
| 508 JSFunction::EnsureLiterals(function); | 583 Node* context; |
| 584 Handle<TypeFeedbackVector> feedback_vector; |
| 585 DetermineCallContext(node, context, feedback_vector); |
| 509 | 586 |
| 510 // Create the subgraph for the inlinee. | 587 // Create the subgraph for the inlinee. |
| 511 Node* start; | 588 Node* start; |
| 512 Node* end; | 589 Node* end; |
| 513 { | 590 { |
| 514 // Run the BytecodeGraphBuilder to create the subgraph. | 591 // Run the BytecodeGraphBuilder to create the subgraph. |
| 515 Graph::SubgraphScope scope(graph()); | 592 Graph::SubgraphScope scope(graph()); |
| 516 BytecodeGraphBuilder graph_builder( | 593 BytecodeGraphBuilder graph_builder( |
| 517 &zone, shared_info, handle(function->feedback_vector()), | 594 &zone, shared_info, feedback_vector, BailoutId::None(), jsgraph(), |
| 518 BailoutId::None(), jsgraph(), call.frequency(), source_positions_, | 595 call.frequency(), source_positions_, inlining_id); |
| 519 inlining_id); | |
| 520 graph_builder.CreateGraph(false); | 596 graph_builder.CreateGraph(false); |
| 521 | 597 |
| 522 // Extract the inlinee start/end nodes. | 598 // Extract the inlinee start/end nodes. |
| 523 start = graph()->start(); | 599 start = graph()->start(); |
| 524 end = graph()->end(); | 600 end = graph()->end(); |
| 525 } | 601 } |
| 526 | 602 |
| 527 if (exception_target != nullptr) { | 603 if (exception_target != nullptr) { |
| 528 // Find all uncaught 'calls' in the inlinee. | 604 // Find all uncaught 'calls' in the inlinee. |
| 529 AllNodes inlined_nodes(local_zone_, end, graph()); | 605 AllNodes inlined_nodes(local_zone_, end, graph()); |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 586 node->RemoveInput(call.formal_arguments() + 1); // Drop new target. | 662 node->RemoveInput(call.formal_arguments() + 1); // Drop new target. |
| 587 node->InsertInput(graph()->zone(), 1, receiver); | 663 node->InsertInput(graph()->zone(), 1, receiver); |
| 588 | 664 |
| 589 // Insert a construct stub frame into the chain of frame states. This will | 665 // Insert a construct stub frame into the chain of frame states. This will |
| 590 // reconstruct the proper frame when deoptimizing within the constructor. | 666 // reconstruct the proper frame when deoptimizing within the constructor. |
| 591 frame_state = CreateArtificialFrameState( | 667 frame_state = CreateArtificialFrameState( |
| 592 node, frame_state, call.formal_arguments(), | 668 node, frame_state, call.formal_arguments(), |
| 593 FrameStateType::kConstructStub, info.shared_info()); | 669 FrameStateType::kConstructStub, info.shared_info()); |
| 594 } | 670 } |
| 595 | 671 |
| 596 // The inlinee specializes to the context from the JSFunction object. | |
| 597 // TODO(turbofan): We might want to load the context from the JSFunction at | |
| 598 // runtime in case we only know the SharedFunctionInfo once we have dynamic | |
| 599 // type feedback in the compiler. | |
| 600 Node* context = jsgraph()->Constant(handle(function->context())); | |
| 601 | |
| 602 // Insert a JSConvertReceiver node for sloppy callees. Note that the context | 672 // Insert a JSConvertReceiver node for sloppy callees. Note that the context |
| 603 // passed into this node has to be the callees context (loaded above). | 673 // passed into this node has to be the callees context (loaded above). |
| 604 if (node->opcode() == IrOpcode::kJSCall && | 674 if (node->opcode() == IrOpcode::kJSCall && |
| 605 is_sloppy(shared_info->language_mode()) && !shared_info->native()) { | 675 is_sloppy(shared_info->language_mode()) && !shared_info->native()) { |
| 606 Node* effect = NodeProperties::GetEffectInput(node); | 676 Node* effect = NodeProperties::GetEffectInput(node); |
| 607 if (NeedsConvertReceiver(call.receiver(), effect)) { | 677 if (NeedsConvertReceiver(call.receiver(), effect)) { |
| 608 const CallParameters& p = CallParametersOf(node->op()); | 678 const CallParameters& p = CallParametersOf(node->op()); |
| 609 Node* convert = effect = | 679 Node* convert = effect = |
| 610 graph()->NewNode(javascript()->ConvertReceiver(p.convert_mode()), | 680 graph()->NewNode(javascript()->ConvertReceiver(p.convert_mode()), |
| 611 call.receiver(), context, effect, start); | 681 call.receiver(), context, effect, start); |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 652 | 722 |
| 653 CommonOperatorBuilder* JSInliner::common() const { return jsgraph()->common(); } | 723 CommonOperatorBuilder* JSInliner::common() const { return jsgraph()->common(); } |
| 654 | 724 |
| 655 SimplifiedOperatorBuilder* JSInliner::simplified() const { | 725 SimplifiedOperatorBuilder* JSInliner::simplified() const { |
| 656 return jsgraph()->simplified(); | 726 return jsgraph()->simplified(); |
| 657 } | 727 } |
| 658 | 728 |
| 659 } // namespace compiler | 729 } // namespace compiler |
| 660 } // namespace internal | 730 } // namespace internal |
| 661 } // namespace v8 | 731 } // namespace v8 |
| OLD | NEW |