| 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 13 matching lines...) Expand all Loading... |
| 24 namespace internal { | 24 namespace internal { |
| 25 namespace compiler { | 25 namespace compiler { |
| 26 | 26 |
| 27 #define TRACE(...) \ | 27 #define TRACE(...) \ |
| 28 do { \ | 28 do { \ |
| 29 if (FLAG_trace_turbo_inlining) PrintF(__VA_ARGS__); \ | 29 if (FLAG_trace_turbo_inlining) PrintF(__VA_ARGS__); \ |
| 30 } while (false) | 30 } while (false) |
| 31 | 31 |
| 32 | 32 |
| 33 // Provides convenience accessors for the common layout of nodes having either | 33 // Provides convenience accessors for the common layout of nodes having either |
| 34 // the {JSCallFunction} or the {JSConstruct} operator. | 34 // the {JSCall} or the {JSConstruct} operator. |
| 35 class JSCallAccessor { | 35 class JSCallAccessor { |
| 36 public: | 36 public: |
| 37 explicit JSCallAccessor(Node* call) : call_(call) { | 37 explicit JSCallAccessor(Node* call) : call_(call) { |
| 38 DCHECK(call->opcode() == IrOpcode::kJSCallFunction || | 38 DCHECK(call->opcode() == IrOpcode::kJSCall || |
| 39 call->opcode() == IrOpcode::kJSConstruct); | 39 call->opcode() == IrOpcode::kJSConstruct); |
| 40 } | 40 } |
| 41 | 41 |
| 42 Node* target() { | 42 Node* target() { |
| 43 // Both, {JSCallFunction} and {JSConstruct}, have same layout here. | 43 // Both, {JSCall} and {JSConstruct}, have same layout here. |
| 44 return call_->InputAt(0); | 44 return call_->InputAt(0); |
| 45 } | 45 } |
| 46 | 46 |
| 47 Node* receiver() { | 47 Node* receiver() { |
| 48 DCHECK_EQ(IrOpcode::kJSCallFunction, call_->opcode()); | 48 DCHECK_EQ(IrOpcode::kJSCall, call_->opcode()); |
| 49 return call_->InputAt(1); | 49 return call_->InputAt(1); |
| 50 } | 50 } |
| 51 | 51 |
| 52 Node* new_target() { | 52 Node* new_target() { |
| 53 DCHECK_EQ(IrOpcode::kJSConstruct, call_->opcode()); | 53 DCHECK_EQ(IrOpcode::kJSConstruct, call_->opcode()); |
| 54 return call_->InputAt(formal_arguments() + 1); | 54 return call_->InputAt(formal_arguments() + 1); |
| 55 } | 55 } |
| 56 | 56 |
| 57 Node* frame_state() { | 57 Node* frame_state() { |
| 58 // Both, {JSCallFunction} and {JSConstruct}, have frame state. | 58 // Both, {JSCall} and {JSConstruct}, have frame state. |
| 59 return NodeProperties::GetFrameStateInput(call_); | 59 return NodeProperties::GetFrameStateInput(call_); |
| 60 } | 60 } |
| 61 | 61 |
| 62 int formal_arguments() { | 62 int formal_arguments() { |
| 63 // Both, {JSCallFunction} and {JSConstruct}, have two extra inputs: | 63 // Both, {JSCall} and {JSConstruct}, have two extra inputs: |
| 64 // - JSConstruct: Includes target function and new target. | 64 // - JSConstruct: Includes target function and new target. |
| 65 // - JSCallFunction: Includes target function and receiver. | 65 // - JSCall: Includes target function and receiver. |
| 66 return call_->op()->ValueInputCount() - 2; | 66 return call_->op()->ValueInputCount() - 2; |
| 67 } | 67 } |
| 68 | 68 |
| 69 float frequency() const { | 69 float frequency() const { |
| 70 return (call_->opcode() == IrOpcode::kJSCallFunction) | 70 return (call_->opcode() == IrOpcode::kJSCall) |
| 71 ? CallFunctionParametersOf(call_->op()).frequency() | 71 ? CallParametersOf(call_->op()).frequency() |
| 72 : ConstructParametersOf(call_->op()).frequency(); | 72 : ConstructParametersOf(call_->op()).frequency(); |
| 73 } | 73 } |
| 74 | 74 |
| 75 private: | 75 private: |
| 76 Node* call_; | 76 Node* call_; |
| 77 }; | 77 }; |
| 78 | 78 |
| 79 Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context, | 79 Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context, |
| 80 Node* frame_state, Node* start, Node* end, | 80 Node* frame_state, Node* start, Node* end, |
| 81 Node* exception_target, | 81 Node* exception_target, |
| (...skipping 264 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 346 } | 346 } |
| 347 | 347 |
| 348 } // namespace | 348 } // namespace |
| 349 | 349 |
| 350 | 350 |
| 351 Reduction JSInliner::Reduce(Node* node) { | 351 Reduction JSInliner::Reduce(Node* node) { |
| 352 if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange(); | 352 if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange(); |
| 353 | 353 |
| 354 // This reducer can handle both normal function calls as well a constructor | 354 // This reducer can handle both normal function calls as well a constructor |
| 355 // calls whenever the target is a constant function object, as follows: | 355 // calls whenever the target is a constant function object, as follows: |
| 356 // - JSCallFunction(target:constant, receiver, args...) | 356 // - JSCall(target:constant, receiver, args...) |
| 357 // - JSConstruct(target:constant, args..., new.target) | 357 // - JSConstruct(target:constant, args..., new.target) |
| 358 HeapObjectMatcher match(node->InputAt(0)); | 358 HeapObjectMatcher match(node->InputAt(0)); |
| 359 if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange(); | 359 if (!match.HasValue() || !match.Value()->IsJSFunction()) return NoChange(); |
| 360 Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value()); | 360 Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value()); |
| 361 | 361 |
| 362 return ReduceJSCall(node, function); | 362 return ReduceJSCall(node, function); |
| 363 } | 363 } |
| 364 | 364 |
| 365 Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) { | 365 Reduction JSInliner::ReduceJSCall(Node* node, Handle<JSFunction> function) { |
| 366 DCHECK(IrOpcode::IsInlineeOpcode(node->opcode())); | 366 DCHECK(IrOpcode::IsInlineeOpcode(node->opcode())); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 387 if (node->opcode() == IrOpcode::kJSConstruct && | 387 if (node->opcode() == IrOpcode::kJSConstruct && |
| 388 IsNonConstructible(shared_info)) { | 388 IsNonConstructible(shared_info)) { |
| 389 TRACE("Not inlining %s into %s because constructor is not constructable.\n", | 389 TRACE("Not inlining %s into %s because constructor is not constructable.\n", |
| 390 shared_info->DebugName()->ToCString().get(), | 390 shared_info->DebugName()->ToCString().get(), |
| 391 info_->shared_info()->DebugName()->ToCString().get()); | 391 info_->shared_info()->DebugName()->ToCString().get()); |
| 392 return NoChange(); | 392 return NoChange(); |
| 393 } | 393 } |
| 394 | 394 |
| 395 // Class constructors are callable, but [[Call]] will raise an exception. | 395 // Class constructors are callable, but [[Call]] will raise an exception. |
| 396 // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ). | 396 // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ). |
| 397 if (node->opcode() == IrOpcode::kJSCallFunction && | 397 if (node->opcode() == IrOpcode::kJSCall && |
| 398 IsClassConstructor(shared_info->kind())) { | 398 IsClassConstructor(shared_info->kind())) { |
| 399 TRACE("Not inlining %s into %s because callee is a class constructor.\n", | 399 TRACE("Not inlining %s into %s because callee is a class constructor.\n", |
| 400 shared_info->DebugName()->ToCString().get(), | 400 shared_info->DebugName()->ToCString().get(), |
| 401 info_->shared_info()->DebugName()->ToCString().get()); | 401 info_->shared_info()->DebugName()->ToCString().get()); |
| 402 return NoChange(); | 402 return NoChange(); |
| 403 } | 403 } |
| 404 | 404 |
| 405 // Function contains break points. | 405 // Function contains break points. |
| 406 if (shared_info->HasDebugInfo()) { | 406 if (shared_info->HasDebugInfo()) { |
| 407 TRACE("Not inlining %s into %s because callee may contain break points\n", | 407 TRACE("Not inlining %s into %s because callee may contain break points\n", |
| (...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 573 graph()->NewNode(common()->Select(MachineRepresentation::kTagged), | 573 graph()->NewNode(common()->Select(MachineRepresentation::kTagged), |
| 574 check, node, create); | 574 check, node, create); |
| 575 NodeProperties::ReplaceUses(node, select, node, node, node); | 575 NodeProperties::ReplaceUses(node, select, node, node, node); |
| 576 // Fix-up inputs that have been mangled by the {ReplaceUses} call above. | 576 // Fix-up inputs that have been mangled by the {ReplaceUses} call above. |
| 577 NodeProperties::ReplaceValueInput(select, node, 1); // Fix-up input. | 577 NodeProperties::ReplaceValueInput(select, node, 1); // Fix-up input. |
| 578 NodeProperties::ReplaceValueInput(check, node, 0); // Fix-up input. | 578 NodeProperties::ReplaceValueInput(check, node, 0); // Fix-up input. |
| 579 receiver = create; // The implicit receiver. | 579 receiver = create; // The implicit receiver. |
| 580 } | 580 } |
| 581 | 581 |
| 582 // Swizzle the inputs of the {JSConstruct} node to look like inputs to a | 582 // Swizzle the inputs of the {JSConstruct} node to look like inputs to a |
| 583 // normal {JSCallFunction} node so that the rest of the inlining machinery | 583 // normal {JSCall} node so that the rest of the inlining machinery |
| 584 // behaves as if we were dealing with a regular function invocation. | 584 // behaves as if we were dealing with a regular function invocation. |
| 585 new_target = call.new_target(); // Retrieve new target value input. | 585 new_target = call.new_target(); // Retrieve new target value input. |
| 586 node->RemoveInput(call.formal_arguments() + 1); // Drop new target. | 586 node->RemoveInput(call.formal_arguments() + 1); // Drop new target. |
| 587 node->InsertInput(graph()->zone(), 1, receiver); | 587 node->InsertInput(graph()->zone(), 1, receiver); |
| 588 | 588 |
| 589 // Insert a construct stub frame into the chain of frame states. This will | 589 // Insert a construct stub frame into the chain of frame states. This will |
| 590 // reconstruct the proper frame when deoptimizing within the constructor. | 590 // reconstruct the proper frame when deoptimizing within the constructor. |
| 591 frame_state = CreateArtificialFrameState( | 591 frame_state = CreateArtificialFrameState( |
| 592 node, frame_state, call.formal_arguments(), | 592 node, frame_state, call.formal_arguments(), |
| 593 FrameStateType::kConstructStub, info.shared_info()); | 593 FrameStateType::kConstructStub, info.shared_info()); |
| 594 } | 594 } |
| 595 | 595 |
| 596 // The inlinee specializes to the context from the JSFunction object. | 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 | 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 | 598 // runtime in case we only know the SharedFunctionInfo once we have dynamic |
| 599 // type feedback in the compiler. | 599 // type feedback in the compiler. |
| 600 Node* context = jsgraph()->Constant(handle(function->context())); | 600 Node* context = jsgraph()->Constant(handle(function->context())); |
| 601 | 601 |
| 602 // Insert a JSConvertReceiver node for sloppy callees. Note that the context | 602 // Insert a JSConvertReceiver node for sloppy callees. Note that the context |
| 603 // passed into this node has to be the callees context (loaded above). Note | 603 // passed into this node has to be the callees context (loaded above). Note |
| 604 // that the frame state passed to the JSConvertReceiver must be the frame | 604 // that the frame state passed to the JSConvertReceiver must be the frame |
| 605 // state _before_ the call; it is not necessary to fiddle with the receiver | 605 // state _before_ the call; it is not necessary to fiddle with the receiver |
| 606 // in that frame state tho, as the conversion of the receiver can be repeated | 606 // in that frame state tho, as the conversion of the receiver can be repeated |
| 607 // any number of times, it's not observable. | 607 // any number of times, it's not observable. |
| 608 if (node->opcode() == IrOpcode::kJSCallFunction && | 608 if (node->opcode() == IrOpcode::kJSCall && |
| 609 is_sloppy(shared_info->language_mode()) && !shared_info->native()) { | 609 is_sloppy(shared_info->language_mode()) && !shared_info->native()) { |
| 610 Node* effect = NodeProperties::GetEffectInput(node); | 610 Node* effect = NodeProperties::GetEffectInput(node); |
| 611 if (NeedsConvertReceiver(call.receiver(), effect)) { | 611 if (NeedsConvertReceiver(call.receiver(), effect)) { |
| 612 const CallFunctionParameters& p = CallFunctionParametersOf(node->op()); | 612 const CallParameters& p = CallParametersOf(node->op()); |
| 613 Node* frame_state_before = NodeProperties::FindFrameStateBefore(node); | 613 Node* frame_state_before = NodeProperties::FindFrameStateBefore(node); |
| 614 Node* convert = effect = graph()->NewNode( | 614 Node* convert = effect = graph()->NewNode( |
| 615 javascript()->ConvertReceiver(p.convert_mode()), call.receiver(), | 615 javascript()->ConvertReceiver(p.convert_mode()), call.receiver(), |
| 616 context, frame_state_before, effect, start); | 616 context, frame_state_before, effect, start); |
| 617 NodeProperties::ReplaceValueInput(node, convert, 1); | 617 NodeProperties::ReplaceValueInput(node, convert, 1); |
| 618 NodeProperties::ReplaceEffectInput(node, effect); | 618 NodeProperties::ReplaceEffectInput(node, effect); |
| 619 } | 619 } |
| 620 } | 620 } |
| 621 | 621 |
| 622 // If we are inlining a JS call at tail position then we have to pop current | 622 // If we are inlining a JS call at tail position then we have to pop current |
| 623 // frame state and its potential arguments adaptor frame state in order to | 623 // frame state and its potential arguments adaptor frame state in order to |
| 624 // make the call stack be consistent with non-inlining case. | 624 // make the call stack be consistent with non-inlining case. |
| 625 // After that we add a tail caller frame state which lets deoptimizer handle | 625 // After that we add a tail caller frame state which lets deoptimizer handle |
| 626 // the case when the outermost function inlines a tail call (it should remove | 626 // the case when the outermost function inlines a tail call (it should remove |
| 627 // potential arguments adaptor frame that belongs to outermost function when | 627 // potential arguments adaptor frame that belongs to outermost function when |
| 628 // deopt happens). | 628 // deopt happens). |
| 629 if (node->opcode() == IrOpcode::kJSCallFunction) { | 629 if (node->opcode() == IrOpcode::kJSCall) { |
| 630 const CallFunctionParameters& p = CallFunctionParametersOf(node->op()); | 630 const CallParameters& p = CallParametersOf(node->op()); |
| 631 if (p.tail_call_mode() == TailCallMode::kAllow) { | 631 if (p.tail_call_mode() == TailCallMode::kAllow) { |
| 632 frame_state = CreateTailCallerFrameState(node, frame_state); | 632 frame_state = CreateTailCallerFrameState(node, frame_state); |
| 633 } | 633 } |
| 634 } | 634 } |
| 635 | 635 |
| 636 // Insert argument adaptor frame if required. The callees formal parameter | 636 // Insert argument adaptor frame if required. The callees formal parameter |
| 637 // count (i.e. value outputs of start node minus target, receiver, new target, | 637 // count (i.e. value outputs of start node minus target, receiver, new target, |
| 638 // arguments count and context) have to match the number of arguments passed | 638 // arguments count and context) have to match the number of arguments passed |
| 639 // to the call. | 639 // to the call. |
| 640 int parameter_count = shared_info->internal_formal_parameter_count(); | 640 int parameter_count = shared_info->internal_formal_parameter_count(); |
| (...skipping 16 matching lines...) Expand all Loading... |
| 657 | 657 |
| 658 CommonOperatorBuilder* JSInliner::common() const { return jsgraph()->common(); } | 658 CommonOperatorBuilder* JSInliner::common() const { return jsgraph()->common(); } |
| 659 | 659 |
| 660 SimplifiedOperatorBuilder* JSInliner::simplified() const { | 660 SimplifiedOperatorBuilder* JSInliner::simplified() const { |
| 661 return jsgraph()->simplified(); | 661 return jsgraph()->simplified(); |
| 662 } | 662 } |
| 663 | 663 |
| 664 } // namespace compiler | 664 } // namespace compiler |
| 665 } // namespace internal | 665 } // namespace internal |
| 666 } // namespace v8 | 666 } // namespace v8 |
| OLD | NEW |