| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #if !defined(DART_PRECOMPILED_RUNTIME) | 5 #if !defined(DART_PRECOMPILED_RUNTIME) |
| 6 | 6 |
| 7 #include "vm/compiler/backend/inliner.h" | 7 #include "vm/compiler/backend/inliner.h" |
| 8 | 8 |
| 9 #include "vm/compiler/aot/aot_call_specializer.h" | 9 #include "vm/compiler/aot/aot_call_specializer.h" |
| 10 #include "vm/compiler/aot/precompiler.h" | 10 #include "vm/compiler/aot/precompiler.h" |
| (...skipping 438 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 449 | 449 |
| 450 class CallSiteInliner; | 450 class CallSiteInliner; |
| 451 | 451 |
| 452 class PolymorphicInliner : public ValueObject { | 452 class PolymorphicInliner : public ValueObject { |
| 453 public: | 453 public: |
| 454 PolymorphicInliner(CallSiteInliner* owner, | 454 PolymorphicInliner(CallSiteInliner* owner, |
| 455 PolymorphicInstanceCallInstr* call, | 455 PolymorphicInstanceCallInstr* call, |
| 456 const Function& caller_function, | 456 const Function& caller_function, |
| 457 intptr_t caller_inlining_id); | 457 intptr_t caller_inlining_id); |
| 458 | 458 |
| 459 void Inline(); | 459 bool Inline(); |
| 460 | 460 |
| 461 private: | 461 private: |
| 462 bool CheckInlinedDuplicate(const Function& target); | 462 bool CheckInlinedDuplicate(const Function& target); |
| 463 bool CheckNonInlinedDuplicate(const Function& target); | 463 bool CheckNonInlinedDuplicate(const Function& target); |
| 464 | 464 |
| 465 bool TryInliningPoly(const TargetInfo& target); | 465 bool TryInliningPoly(const TargetInfo& target); |
| 466 bool TryInlineRecognizedMethod(intptr_t receiver_cid, const Function& target); | 466 bool TryInlineRecognizedMethod(intptr_t receiver_cid, const Function& target); |
| 467 | 467 |
| 468 TargetEntryInstr* BuildDecisionGraph(); | 468 TargetEntryInstr* BuildDecisionGraph(); |
| 469 | 469 |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 613 inlined_info_() {} | 613 inlined_info_() {} |
| 614 | 614 |
| 615 FlowGraph* caller_graph() const { return caller_graph_; } | 615 FlowGraph* caller_graph() const { return caller_graph_; } |
| 616 | 616 |
| 617 Thread* thread() const { return caller_graph_->thread(); } | 617 Thread* thread() const { return caller_graph_->thread(); } |
| 618 Isolate* isolate() const { return caller_graph_->isolate(); } | 618 Isolate* isolate() const { return caller_graph_->isolate(); } |
| 619 Zone* zone() const { return caller_graph_->zone(); } | 619 Zone* zone() const { return caller_graph_->zone(); } |
| 620 | 620 |
| 621 bool trace_inlining() const { return inliner_->trace_inlining(); } | 621 bool trace_inlining() const { return inliner_->trace_inlining(); } |
| 622 | 622 |
| 623 int inlining_depth() { return inlining_depth_; } |
| 624 |
| 625 struct InliningDecision { |
| 626 InliningDecision(bool b, const char* r) : value(b), reason(r) {} |
| 627 bool value; |
| 628 const char* reason; |
| 629 static InliningDecision Yes(const char* reason) { |
| 630 return InliningDecision(true, reason); |
| 631 } |
| 632 static InliningDecision No(const char* reason) { |
| 633 return InliningDecision(false, reason); |
| 634 } |
| 635 }; |
| 636 |
| 623 // Inlining heuristics based on Cooper et al. 2008. | 637 // Inlining heuristics based on Cooper et al. 2008. |
| 624 bool ShouldWeInline(const Function& callee, | 638 InliningDecision ShouldWeInline(const Function& callee, |
| 625 intptr_t instr_count, | 639 intptr_t instr_count, |
| 626 intptr_t call_site_count, | 640 intptr_t call_site_count, |
| 627 intptr_t const_arg_count) { | 641 intptr_t const_arg_count) { |
| 628 if (inliner_->AlwaysInline(callee)) { | 642 if (inliner_->AlwaysInline(callee)) { |
| 629 return true; | 643 return InliningDecision::Yes("AlwaysInline"); |
| 630 } | 644 } |
| 631 if (inlined_size_ > FLAG_inlining_caller_size_threshold) { | 645 if (inlined_size_ > FLAG_inlining_caller_size_threshold) { |
| 632 // Prevent methods becoming humongous and thus slow to compile. | 646 // Prevent methods becoming humongous and thus slow to compile. |
| 633 return false; | 647 return InliningDecision::No("--inlining-caller-size-threshold"); |
| 634 } | 648 } |
| 635 if (const_arg_count > 0) { | 649 if (const_arg_count > 0) { |
| 636 if (instr_count > FLAG_inlining_constant_arguments_max_size_threshold) { | 650 if (instr_count > FLAG_inlining_constant_arguments_max_size_threshold) { |
| 637 return false; | 651 return InliningDecision( |
| 652 false, "--inlining-constant-arguments-max-size-threshold"); |
| 638 } | 653 } |
| 639 } else if (instr_count > FLAG_inlining_callee_size_threshold) { | 654 } else if (instr_count > FLAG_inlining_callee_size_threshold) { |
| 640 return false; | 655 return InliningDecision::No("--inlining-callee-size-threshold"); |
| 656 } |
| 657 int callee_inlining_depth = callee.inlining_depth(); |
| 658 if (callee_inlining_depth > 0 && callee_inlining_depth + inlining_depth_ > |
| 659 FLAG_inlining_depth_threshold) { |
| 660 return InliningDecision::No("--inlining-depth-threshold"); |
| 641 } | 661 } |
| 642 // 'instr_count' can be 0 if it was not computed yet. | 662 // 'instr_count' can be 0 if it was not computed yet. |
| 643 if ((instr_count != 0) && (instr_count <= FLAG_inlining_size_threshold)) { | 663 if ((instr_count != 0) && (instr_count <= FLAG_inlining_size_threshold)) { |
| 644 return true; | 664 return InliningDecision::Yes("--inlining-size-threshold"); |
| 645 } | 665 } |
| 646 if (call_site_count <= FLAG_inlining_callee_call_sites_threshold) { | 666 if (call_site_count <= FLAG_inlining_callee_call_sites_threshold) { |
| 647 return true; | 667 return InliningDecision::Yes("--inlining-callee-call-sites-threshold"); |
| 648 } | 668 } |
| 649 if ((const_arg_count >= FLAG_inlining_constant_arguments_count) && | 669 if ((const_arg_count >= FLAG_inlining_constant_arguments_count) && |
| 650 (instr_count <= FLAG_inlining_constant_arguments_min_size_threshold)) { | 670 (instr_count <= FLAG_inlining_constant_arguments_min_size_threshold)) { |
| 651 return true; | 671 return InliningDecision(true, |
| 672 "--inlining-constant-arguments-count and " |
| 673 "inlining-constant-arguments-min-size-threshold"); |
| 652 } | 674 } |
| 653 return false; | 675 return InliningDecision::No("default"); |
| 654 } | 676 } |
| 655 | 677 |
| 656 void InlineCalls() { | 678 void InlineCalls() { |
| 657 // If inlining depth is less than one abort. | 679 // If inlining depth is less than one abort. |
| 658 if (inlining_depth_threshold_ < 1) return; | 680 if (inlining_depth_threshold_ < 1) return; |
| 659 if (caller_graph_->function().deoptimization_counter() >= | 681 if (caller_graph_->function().deoptimization_counter() >= |
| 660 FLAG_deoptimization_counter_inlining_threshold) { | 682 FLAG_deoptimization_counter_inlining_threshold) { |
| 661 return; | 683 return; |
| 662 } | 684 } |
| 663 // Create two call site collections to swap between. | 685 // Create two call site collections to swap between. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 678 if (FLAG_print_inlining_tree) { | 700 if (FLAG_print_inlining_tree) { |
| 679 THR_Print("**Depth % " Pd " calls to inline %" Pd "\n", inlining_depth_, | 701 THR_Print("**Depth % " Pd " calls to inline %" Pd "\n", inlining_depth_, |
| 680 collected_call_sites_->NumCalls()); | 702 collected_call_sites_->NumCalls()); |
| 681 } | 703 } |
| 682 // Swap collected and inlining arrays and clear the new collecting array. | 704 // Swap collected and inlining arrays and clear the new collecting array. |
| 683 call_sites_temp = collected_call_sites_; | 705 call_sites_temp = collected_call_sites_; |
| 684 collected_call_sites_ = inlining_call_sites_; | 706 collected_call_sites_ = inlining_call_sites_; |
| 685 inlining_call_sites_ = call_sites_temp; | 707 inlining_call_sites_ = call_sites_temp; |
| 686 collected_call_sites_->Clear(); | 708 collected_call_sites_->Clear(); |
| 687 // Inline call sites at the current depth. | 709 // Inline call sites at the current depth. |
| 688 InlineInstanceCalls(); | 710 bool inlined_instance = InlineInstanceCalls(); |
| 689 InlineStaticCalls(); | 711 bool inlined_statics = InlineStaticCalls(); |
| 690 InlineClosureCalls(); | 712 bool inlined_closures = InlineClosureCalls(); |
| 691 // Increment the inlining depths. Checked before subsequent inlining. | 713 if (inlined_instance || inlined_statics || inlined_closures) { |
| 692 ++inlining_depth_; | 714 // Increment the inlining depths. Checked before subsequent inlining. |
| 693 if (inlined_recursive_call_) { | 715 ++inlining_depth_; |
| 694 ++inlining_recursion_depth_; | 716 if (inlined_recursive_call_) { |
| 695 inlined_recursive_call_ = false; | 717 ++inlining_recursion_depth_; |
| 718 inlined_recursive_call_ = false; |
| 719 } |
| 720 thread()->CheckForSafepoint(); |
| 696 } | 721 } |
| 697 thread()->CheckForSafepoint(); | |
| 698 } | 722 } |
| 699 | 723 |
| 700 collected_call_sites_ = NULL; | 724 collected_call_sites_ = NULL; |
| 701 inlining_call_sites_ = NULL; | 725 inlining_call_sites_ = NULL; |
| 702 } | 726 } |
| 703 | 727 |
| 704 bool inlined() const { return inlined_; } | 728 bool inlined() const { return inlined_; } |
| 705 | 729 |
| 706 double GrowthFactor() const { | 730 double GrowthFactor() const { |
| 707 return static_cast<double>(inlined_size_) / | 731 return static_cast<double>(inlined_size_) / |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 744 if (FLAG_precompiled_mode && function.is_intrinsic() && | 768 if (FLAG_precompiled_mode && function.is_intrinsic() && |
| 745 !inliner_->AlwaysInline(function)) { | 769 !inliner_->AlwaysInline(function)) { |
| 746 TRACE_INLINING(THR_Print(" Bailout: intrinisic\n")); | 770 TRACE_INLINING(THR_Print(" Bailout: intrinisic\n")); |
| 747 PRINT_INLINING_TREE("intrinsic", &call_data->caller, &function, | 771 PRINT_INLINING_TREE("intrinsic", &call_data->caller, &function, |
| 748 call_data->call); | 772 call_data->call); |
| 749 return false; | 773 return false; |
| 750 } | 774 } |
| 751 | 775 |
| 752 // Do not rely on function type feedback or presence of code to determine | 776 // Do not rely on function type feedback or presence of code to determine |
| 753 // if a function was compiled. | 777 // if a function was compiled. |
| 754 if (!FLAG_precompiled_mode && !function.was_compiled()) { | 778 if (!FLAG_precompiled_mode && !function.WasCompiled()) { |
| 755 TRACE_INLINING(THR_Print(" Bailout: not compiled yet\n")); | 779 TRACE_INLINING(THR_Print(" Bailout: not compiled yet\n")); |
| 756 PRINT_INLINING_TREE("Not compiled", &call_data->caller, &function, | 780 PRINT_INLINING_TREE("Not compiled", &call_data->caller, &function, |
| 757 call_data->call); | 781 call_data->call); |
| 758 return false; | 782 return false; |
| 759 } | 783 } |
| 760 | 784 |
| 761 if ((inliner_->precompiler_ != NULL) && | 785 if ((inliner_->precompiler_ != NULL) && |
| 762 inliner_->precompiler_->HasFeedback() && | 786 inliner_->precompiler_->HasFeedback() && |
| 763 (function.usage_counter() <= 0) && !inliner_->AlwaysInline(function)) { | 787 (function.usage_counter() <= 0) && !inliner_->AlwaysInline(function)) { |
| 764 TRACE_INLINING(THR_Print(" Bailout: not compiled yet\n")); | 788 TRACE_INLINING(THR_Print(" Bailout: not compiled yet\n")); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 788 | 812 |
| 789 const char* kNeverInlineAnnotation = "NeverInline"; | 813 const char* kNeverInlineAnnotation = "NeverInline"; |
| 790 if (FLAG_enable_inlining_annotations && | 814 if (FLAG_enable_inlining_annotations && |
| 791 HasAnnotation(function, kNeverInlineAnnotation)) { | 815 HasAnnotation(function, kNeverInlineAnnotation)) { |
| 792 TRACE_INLINING(THR_Print(" Bailout: NeverInline annotation\n")); | 816 TRACE_INLINING(THR_Print(" Bailout: NeverInline annotation\n")); |
| 793 return false; | 817 return false; |
| 794 } | 818 } |
| 795 | 819 |
| 796 GrowableArray<Value*>* arguments = call_data->arguments; | 820 GrowableArray<Value*>* arguments = call_data->arguments; |
| 797 const intptr_t constant_arguments = CountConstants(*arguments); | 821 const intptr_t constant_arguments = CountConstants(*arguments); |
| 798 if (!ShouldWeInline(function, function.optimized_instruction_count(), | 822 InliningDecision decision = ShouldWeInline( |
| 799 function.optimized_call_site_count(), | 823 function, function.optimized_instruction_count(), |
| 800 constant_arguments)) { | 824 function.optimized_call_site_count(), constant_arguments); |
| 825 if (!decision.value) { |
| 801 TRACE_INLINING( | 826 TRACE_INLINING( |
| 802 THR_Print(" Bailout: early heuristics with " | 827 THR_Print(" Bailout: early heuristics (%s) with " |
| 803 "code size: %" Pd ", " | 828 "code size: %" Pd ", " |
| 804 "call sites: %" Pd ", " | 829 "call sites: %" Pd ", " |
| 830 "inlining depth of callee: %d, " |
| 805 "const args: %" Pd "\n", | 831 "const args: %" Pd "\n", |
| 806 function.optimized_instruction_count(), | 832 decision.reason, function.optimized_instruction_count(), |
| 807 function.optimized_call_site_count(), constant_arguments)); | 833 function.optimized_call_site_count(), |
| 834 function.inlining_depth(), constant_arguments)); |
| 808 PRINT_INLINING_TREE("Early heuristic", &call_data->caller, &function, | 835 PRINT_INLINING_TREE("Early heuristic", &call_data->caller, &function, |
| 809 call_data->call); | 836 call_data->call); |
| 810 return false; | 837 return false; |
| 811 } | 838 } |
| 812 | 839 |
| 813 // Abort if this is a recursive occurrence. | 840 // Abort if this is a recursive occurrence. |
| 814 Definition* call = call_data->call; | 841 Definition* call = call_data->call; |
| 815 // Added 'volatile' works around a possible GCC 4.9 compiler bug. | 842 // Added 'volatile' works around a possible GCC 4.9 compiler bug. |
| 816 volatile bool is_recursive_call = IsCallRecursive(function, call); | 843 volatile bool is_recursive_call = IsCallRecursive(function, call); |
| 817 if (is_recursive_call && | 844 if (is_recursive_call && |
| (...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1038 // TODO(zerny): Do this after CP and dead code elimination. | 1065 // TODO(zerny): Do this after CP and dead code elimination. |
| 1039 intptr_t constants_count = 0; | 1066 intptr_t constants_count = 0; |
| 1040 for (intptr_t i = 0; i < param_stubs->length(); ++i) { | 1067 for (intptr_t i = 0; i < param_stubs->length(); ++i) { |
| 1041 if ((*param_stubs)[i]->IsConstant()) ++constants_count; | 1068 if ((*param_stubs)[i]->IsConstant()) ++constants_count; |
| 1042 } | 1069 } |
| 1043 | 1070 |
| 1044 FlowGraphInliner::CollectGraphInfo(callee_graph); | 1071 FlowGraphInliner::CollectGraphInfo(callee_graph); |
| 1045 const intptr_t size = function.optimized_instruction_count(); | 1072 const intptr_t size = function.optimized_instruction_count(); |
| 1046 const intptr_t call_site_count = function.optimized_call_site_count(); | 1073 const intptr_t call_site_count = function.optimized_call_site_count(); |
| 1047 | 1074 |
| 1048 function.set_optimized_instruction_count(size); | |
| 1049 function.set_optimized_call_site_count(call_site_count); | |
| 1050 | |
| 1051 // Use heuristics do decide if this call should be inlined. | 1075 // Use heuristics do decide if this call should be inlined. |
| 1052 if (!ShouldWeInline(function, size, call_site_count, constants_count)) { | 1076 InliningDecision decision = |
| 1077 ShouldWeInline(function, size, call_site_count, constants_count); |
| 1078 if (!decision.value) { |
| 1053 // If size is larger than all thresholds, don't consider it again. | 1079 // If size is larger than all thresholds, don't consider it again. |
| 1054 if ((size > FLAG_inlining_size_threshold) && | 1080 if ((size > FLAG_inlining_size_threshold) && |
| 1055 (call_site_count > FLAG_inlining_callee_call_sites_threshold) && | 1081 (call_site_count > FLAG_inlining_callee_call_sites_threshold) && |
| 1056 (size > FLAG_inlining_constant_arguments_min_size_threshold) && | 1082 (size > FLAG_inlining_constant_arguments_min_size_threshold) && |
| 1057 (size > FLAG_inlining_constant_arguments_max_size_threshold)) { | 1083 (size > FLAG_inlining_constant_arguments_max_size_threshold)) { |
| 1058 function.set_is_inlinable(false); | 1084 function.set_is_inlinable(false); |
| 1059 } | 1085 } |
| 1060 thread()->set_deopt_id(prev_deopt_id); | 1086 thread()->set_deopt_id(prev_deopt_id); |
| 1061 TRACE_INLINING( | 1087 TRACE_INLINING( |
| 1062 THR_Print(" Bailout: heuristics with " | 1088 THR_Print(" Bailout: heuristics (%s) with " |
| 1063 "code size: %" Pd ", " | 1089 "code size: %" Pd ", " |
| 1064 "call sites: %" Pd ", " | 1090 "call sites: %" Pd ", " |
| 1091 "inlining depth of callee: %d, " |
| 1065 "const args: %" Pd "\n", | 1092 "const args: %" Pd "\n", |
| 1066 size, call_site_count, constants_count)); | 1093 decision.reason, size, call_site_count, |
| 1094 function.inlining_depth(), constants_count)); |
| 1067 PRINT_INLINING_TREE("Heuristic fail", &call_data->caller, &function, | 1095 PRINT_INLINING_TREE("Heuristic fail", &call_data->caller, &function, |
| 1068 call_data->call); | 1096 call_data->call); |
| 1069 return false; | 1097 return false; |
| 1070 } | 1098 } |
| 1071 | 1099 |
| 1072 // Inline dispatcher methods regardless of the current depth. | 1100 // Inline dispatcher methods regardless of the current depth. |
| 1073 const intptr_t depth = | 1101 const intptr_t depth = |
| 1074 function.IsDispatcherOrImplicitAccessor() ? 0 : inlining_depth_; | 1102 function.IsDispatcherOrImplicitAccessor() ? 0 : inlining_depth_; |
| 1075 collected_call_sites_->FindCallSites(callee_graph, depth, | 1103 collected_call_sites_->FindCallSites(callee_graph, depth, |
| 1076 &inlined_info_); | 1104 &inlined_info_); |
| (...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1252 *in_cache = false; | 1280 *in_cache = false; |
| 1253 ParsedFunction* parsed_function = | 1281 ParsedFunction* parsed_function = |
| 1254 new (Z) ParsedFunction(thread(), function); | 1282 new (Z) ParsedFunction(thread(), function); |
| 1255 if (!UseKernelFrontEndFor(parsed_function)) { | 1283 if (!UseKernelFrontEndFor(parsed_function)) { |
| 1256 Parser::ParseFunction(parsed_function); | 1284 Parser::ParseFunction(parsed_function); |
| 1257 parsed_function->AllocateVariables(); | 1285 parsed_function->AllocateVariables(); |
| 1258 } | 1286 } |
| 1259 return parsed_function; | 1287 return parsed_function; |
| 1260 } | 1288 } |
| 1261 | 1289 |
| 1262 void InlineStaticCalls() { | 1290 bool InlineStaticCalls() { |
| 1291 bool inlined = false; |
| 1263 const GrowableArray<CallSites::StaticCallInfo>& call_info = | 1292 const GrowableArray<CallSites::StaticCallInfo>& call_info = |
| 1264 inlining_call_sites_->static_calls(); | 1293 inlining_call_sites_->static_calls(); |
| 1265 TRACE_INLINING(THR_Print(" Static Calls (%" Pd ")\n", call_info.length())); | 1294 TRACE_INLINING(THR_Print(" Static Calls (%" Pd ")\n", call_info.length())); |
| 1266 for (intptr_t call_idx = 0; call_idx < call_info.length(); ++call_idx) { | 1295 for (intptr_t call_idx = 0; call_idx < call_info.length(); ++call_idx) { |
| 1267 StaticCallInstr* call = call_info[call_idx].call; | 1296 StaticCallInstr* call = call_info[call_idx].call; |
| 1268 const Function& target = call->function(); | 1297 const Function& target = call->function(); |
| 1269 if (!inliner_->AlwaysInline(target) && | 1298 if (!inliner_->AlwaysInline(target) && |
| 1270 (call_info[call_idx].ratio * 100) < FLAG_inlining_hotness) { | 1299 (call_info[call_idx].ratio * 100) < FLAG_inlining_hotness) { |
| 1271 if (trace_inlining()) { | 1300 if (trace_inlining()) { |
| 1272 String& name = String::Handle(target.QualifiedUserVisibleName()); | 1301 String& name = String::Handle(target.QualifiedUserVisibleName()); |
| 1273 THR_Print(" => %s (deopt count %d)\n Bailout: cold %f\n", | 1302 THR_Print(" => %s (deopt count %d)\n Bailout: cold %f\n", |
| 1274 name.ToCString(), target.deoptimization_counter(), | 1303 name.ToCString(), target.deoptimization_counter(), |
| 1275 call_info[call_idx].ratio); | 1304 call_info[call_idx].ratio); |
| 1276 } | 1305 } |
| 1277 PRINT_INLINING_TREE("Too cold", &call_info[call_idx].caller(), | 1306 PRINT_INLINING_TREE("Too cold", &call_info[call_idx].caller(), |
| 1278 &call->function(), call); | 1307 &call->function(), call); |
| 1279 continue; | 1308 continue; |
| 1280 } | 1309 } |
| 1281 GrowableArray<Value*> arguments(call->ArgumentCount()); | 1310 GrowableArray<Value*> arguments(call->ArgumentCount()); |
| 1282 for (int i = 0; i < call->ArgumentCount(); ++i) { | 1311 for (int i = 0; i < call->ArgumentCount(); ++i) { |
| 1283 arguments.Add(call->PushArgumentAt(i)->value()); | 1312 arguments.Add(call->PushArgumentAt(i)->value()); |
| 1284 } | 1313 } |
| 1285 InlinedCallData call_data( | 1314 InlinedCallData call_data( |
| 1286 call, call->FirstArgIndex(), &arguments, call_info[call_idx].caller(), | 1315 call, call->FirstArgIndex(), &arguments, call_info[call_idx].caller(), |
| 1287 call_info[call_idx].caller_graph->inlining_id()); | 1316 call_info[call_idx].caller_graph->inlining_id()); |
| 1288 if (TryInlining(call->function(), call->argument_names(), &call_data)) { | 1317 if (TryInlining(call->function(), call->argument_names(), &call_data)) { |
| 1289 InlineCall(&call_data); | 1318 InlineCall(&call_data); |
| 1319 inlined = true; |
| 1290 } | 1320 } |
| 1291 } | 1321 } |
| 1322 return inlined; |
| 1292 } | 1323 } |
| 1293 | 1324 |
| 1294 void InlineClosureCalls() { | 1325 bool InlineClosureCalls() { |
| 1326 bool inlined = false; |
| 1295 const GrowableArray<CallSites::ClosureCallInfo>& call_info = | 1327 const GrowableArray<CallSites::ClosureCallInfo>& call_info = |
| 1296 inlining_call_sites_->closure_calls(); | 1328 inlining_call_sites_->closure_calls(); |
| 1297 TRACE_INLINING( | 1329 TRACE_INLINING( |
| 1298 THR_Print(" Closure Calls (%" Pd ")\n", call_info.length())); | 1330 THR_Print(" Closure Calls (%" Pd ")\n", call_info.length())); |
| 1299 for (intptr_t call_idx = 0; call_idx < call_info.length(); ++call_idx) { | 1331 for (intptr_t call_idx = 0; call_idx < call_info.length(); ++call_idx) { |
| 1300 ClosureCallInstr* call = call_info[call_idx].call; | 1332 ClosureCallInstr* call = call_info[call_idx].call; |
| 1301 // Find the closure of the callee. | 1333 // Find the closure of the callee. |
| 1302 ASSERT(call->ArgumentCount() > 0); | 1334 ASSERT(call->ArgumentCount() > 0); |
| 1303 Function& target = Function::ZoneHandle(); | 1335 Function& target = Function::ZoneHandle(); |
| 1304 AllocateObjectInstr* alloc = | 1336 AllocateObjectInstr* alloc = |
| (...skipping 21 matching lines...) Expand all Loading... |
| 1326 | 1358 |
| 1327 GrowableArray<Value*> arguments(call->ArgumentCount()); | 1359 GrowableArray<Value*> arguments(call->ArgumentCount()); |
| 1328 for (int i = 0; i < call->ArgumentCount(); ++i) { | 1360 for (int i = 0; i < call->ArgumentCount(); ++i) { |
| 1329 arguments.Add(call->PushArgumentAt(i)->value()); | 1361 arguments.Add(call->PushArgumentAt(i)->value()); |
| 1330 } | 1362 } |
| 1331 InlinedCallData call_data( | 1363 InlinedCallData call_data( |
| 1332 call, call->FirstArgIndex(), &arguments, call_info[call_idx].caller(), | 1364 call, call->FirstArgIndex(), &arguments, call_info[call_idx].caller(), |
| 1333 call_info[call_idx].caller_graph->inlining_id()); | 1365 call_info[call_idx].caller_graph->inlining_id()); |
| 1334 if (TryInlining(target, call->argument_names(), &call_data)) { | 1366 if (TryInlining(target, call->argument_names(), &call_data)) { |
| 1335 InlineCall(&call_data); | 1367 InlineCall(&call_data); |
| 1368 inlined = true; |
| 1336 } | 1369 } |
| 1337 } | 1370 } |
| 1371 return inlined; |
| 1338 } | 1372 } |
| 1339 | 1373 |
| 1340 void InlineInstanceCalls() { | 1374 bool InlineInstanceCalls() { |
| 1375 bool inlined = false; |
| 1341 const GrowableArray<CallSites::InstanceCallInfo>& call_info = | 1376 const GrowableArray<CallSites::InstanceCallInfo>& call_info = |
| 1342 inlining_call_sites_->instance_calls(); | 1377 inlining_call_sites_->instance_calls(); |
| 1343 TRACE_INLINING(THR_Print(" Polymorphic Instance Calls (%" Pd ")\n", | 1378 TRACE_INLINING(THR_Print(" Polymorphic Instance Calls (%" Pd ")\n", |
| 1344 call_info.length())); | 1379 call_info.length())); |
| 1345 for (intptr_t call_idx = 0; call_idx < call_info.length(); ++call_idx) { | 1380 for (intptr_t call_idx = 0; call_idx < call_info.length(); ++call_idx) { |
| 1346 PolymorphicInstanceCallInstr* call = call_info[call_idx].call; | 1381 PolymorphicInstanceCallInstr* call = call_info[call_idx].call; |
| 1347 // PolymorphicInliner introduces deoptimization paths. | 1382 // PolymorphicInliner introduces deoptimization paths. |
| 1348 if (!call->complete() && !FLAG_polymorphic_with_deopt) { | 1383 if (!call->complete() && !FLAG_polymorphic_with_deopt) { |
| 1349 TRACE_INLINING( | 1384 TRACE_INLINING( |
| 1350 THR_Print(" => %s\n Bailout: call with checks\n", | 1385 THR_Print(" => %s\n Bailout: call with checks\n", |
| 1351 call->instance_call()->function_name().ToCString())); | 1386 call->instance_call()->function_name().ToCString())); |
| 1352 continue; | 1387 continue; |
| 1353 } | 1388 } |
| 1354 const Function& cl = call_info[call_idx].caller(); | 1389 const Function& cl = call_info[call_idx].caller(); |
| 1355 intptr_t caller_inlining_id = | 1390 intptr_t caller_inlining_id = |
| 1356 call_info[call_idx].caller_graph->inlining_id(); | 1391 call_info[call_idx].caller_graph->inlining_id(); |
| 1357 PolymorphicInliner inliner(this, call, cl, caller_inlining_id); | 1392 PolymorphicInliner inliner(this, call, cl, caller_inlining_id); |
| 1358 inliner.Inline(); | 1393 if (inliner.Inline()) inlined = true; |
| 1359 } | 1394 } |
| 1395 return inlined; |
| 1360 } | 1396 } |
| 1361 | 1397 |
| 1362 bool AdjustForOptionalParameters(const ParsedFunction& parsed_function, | 1398 bool AdjustForOptionalParameters(const ParsedFunction& parsed_function, |
| 1363 intptr_t first_arg_index, | 1399 intptr_t first_arg_index, |
| 1364 const Array& argument_names, | 1400 const Array& argument_names, |
| 1365 GrowableArray<Value*>* arguments, | 1401 GrowableArray<Value*>* arguments, |
| 1366 ZoneGrowableArray<Definition*>* param_stubs, | 1402 ZoneGrowableArray<Definition*>* param_stubs, |
| 1367 FlowGraph* callee_graph) { | 1403 FlowGraph* callee_graph) { |
| 1368 const Function& function = parsed_function.function(); | 1404 const Function& function = parsed_function.function(); |
| 1369 // The language and this code does not support both optional positional | 1405 // The language and this code does not support both optional positional |
| (...skipping 534 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1904 int percent = total == 0 ? 0 : (100 * targets.TargetAt(idx)->count) / total; | 1940 int percent = total == 0 ? 0 : (100 * targets.TargetAt(idx)->count) / total; |
| 1905 THR_Print("%s cid %" Pd "-%" Pd ": %" Pd "/%" Pd " %d%% %s\n", | 1941 THR_Print("%s cid %" Pd "-%" Pd ": %" Pd "/%" Pd " %d%% %s\n", |
| 1906 name.ToCString(), targets[idx].cid_start, targets[idx].cid_end, | 1942 name.ToCString(), targets[idx].cid_start, targets[idx].cid_end, |
| 1907 targets.TargetAt(idx)->count, total, percent, message); | 1943 targets.TargetAt(idx)->count, total, percent, message); |
| 1908 } | 1944 } |
| 1909 | 1945 |
| 1910 bool PolymorphicInliner::trace_inlining() const { | 1946 bool PolymorphicInliner::trace_inlining() const { |
| 1911 return owner_->trace_inlining(); | 1947 return owner_->trace_inlining(); |
| 1912 } | 1948 } |
| 1913 | 1949 |
| 1914 void PolymorphicInliner::Inline() { | 1950 bool PolymorphicInliner::Inline() { |
| 1915 ASSERT(&variants_ == &call_->targets_); | 1951 ASSERT(&variants_ == &call_->targets_); |
| 1916 | 1952 |
| 1917 intptr_t total = call_->total_call_count(); | 1953 intptr_t total = call_->total_call_count(); |
| 1918 for (intptr_t var_idx = 0; var_idx < variants_.length(); ++var_idx) { | 1954 for (intptr_t var_idx = 0; var_idx < variants_.length(); ++var_idx) { |
| 1919 TargetInfo* info = variants_.TargetAt(var_idx); | 1955 TargetInfo* info = variants_.TargetAt(var_idx); |
| 1920 if (variants_.length() > FLAG_max_polymorphic_checks) { | 1956 if (variants_.length() > FLAG_max_polymorphic_checks) { |
| 1921 non_inlined_variants_->Add(info); | 1957 non_inlined_variants_->Add(info); |
| 1922 continue; | 1958 continue; |
| 1923 } | 1959 } |
| 1924 | 1960 |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1977 TRACE_INLINING(TracePolyInlining(variants_, var_idx, total, "inlined")); | 2013 TRACE_INLINING(TracePolyInlining(variants_, var_idx, total, "inlined")); |
| 1978 inlined_variants_.Add(&variants_[var_idx]); | 2014 inlined_variants_.Add(&variants_[var_idx]); |
| 1979 } else { | 2015 } else { |
| 1980 TRACE_INLINING( | 2016 TRACE_INLINING( |
| 1981 TracePolyInlining(variants_, var_idx, total, "not inlined")); | 2017 TracePolyInlining(variants_, var_idx, total, "not inlined")); |
| 1982 non_inlined_variants_->Add(&variants_[var_idx]); | 2018 non_inlined_variants_->Add(&variants_[var_idx]); |
| 1983 } | 2019 } |
| 1984 } | 2020 } |
| 1985 | 2021 |
| 1986 // If there are no inlined variants, leave the call in place. | 2022 // If there are no inlined variants, leave the call in place. |
| 1987 if (inlined_variants_.is_empty()) return; | 2023 if (inlined_variants_.is_empty()) return false; |
| 1988 | 2024 |
| 1989 // Now build a decision tree (a DAG because of shared inline variants) and | 2025 // Now build a decision tree (a DAG because of shared inline variants) and |
| 1990 // inline it at the call site. | 2026 // inline it at the call site. |
| 1991 TargetEntryInstr* entry = BuildDecisionGraph(); | 2027 TargetEntryInstr* entry = BuildDecisionGraph(); |
| 1992 exit_collector_->ReplaceCall(entry); | 2028 exit_collector_->ReplaceCall(entry); |
| 1993 } | 2029 return true; |
| 1994 | |
| 1995 static uint16_t ClampUint16(intptr_t v) { | |
| 1996 return (v > 0xFFFF) ? 0xFFFF : static_cast<uint16_t>(v); | |
| 1997 } | 2030 } |
| 1998 | 2031 |
| 1999 static bool ShouldTraceInlining(FlowGraph* flow_graph) { | 2032 static bool ShouldTraceInlining(FlowGraph* flow_graph) { |
| 2000 const Function& top = flow_graph->parsed_function().function(); | 2033 const Function& top = flow_graph->parsed_function().function(); |
| 2001 return FLAG_trace_inlining && FlowGraphPrinter::ShouldPrint(top); | 2034 return FLAG_trace_inlining && FlowGraphPrinter::ShouldPrint(top); |
| 2002 } | 2035 } |
| 2003 | 2036 |
| 2004 FlowGraphInliner::FlowGraphInliner( | 2037 FlowGraphInliner::FlowGraphInliner( |
| 2005 FlowGraph* flow_graph, | 2038 FlowGraph* flow_graph, |
| 2006 GrowableArray<const Function*>* inline_id_to_function, | 2039 GrowableArray<const Function*>* inline_id_to_function, |
| (...skipping 12 matching lines...) Expand all Loading... |
| 2019 precompiler_(precompiler) { | 2052 precompiler_(precompiler) { |
| 2020 ASSERT(!use_speculative_inlining || (inlining_black_list != NULL)); | 2053 ASSERT(!use_speculative_inlining || (inlining_black_list != NULL)); |
| 2021 } | 2054 } |
| 2022 | 2055 |
| 2023 void FlowGraphInliner::CollectGraphInfo(FlowGraph* flow_graph, bool force) { | 2056 void FlowGraphInliner::CollectGraphInfo(FlowGraph* flow_graph, bool force) { |
| 2024 const Function& function = flow_graph->function(); | 2057 const Function& function = flow_graph->function(); |
| 2025 if (force || (function.optimized_instruction_count() == 0)) { | 2058 if (force || (function.optimized_instruction_count() == 0)) { |
| 2026 GraphInfoCollector info; | 2059 GraphInfoCollector info; |
| 2027 info.Collect(*flow_graph); | 2060 info.Collect(*flow_graph); |
| 2028 | 2061 |
| 2029 function.set_optimized_instruction_count( | 2062 function.SetOptimizedInstructionCountClamped(info.instruction_count()); |
| 2030 ClampUint16(info.instruction_count())); | 2063 function.SetOptimizedCallSiteCountClamped(info.call_site_count()); |
| 2031 function.set_optimized_call_site_count(ClampUint16(info.call_site_count())); | |
| 2032 } | 2064 } |
| 2033 } | 2065 } |
| 2034 | 2066 |
| 2035 // TODO(srdjan): This is only needed when disassembling and/or profiling. | 2067 // TODO(srdjan): This is only needed when disassembling and/or profiling. |
| 2036 // Sets inlining id for all instructions of this flow-graph, as well for the | 2068 // Sets inlining id for all instructions of this flow-graph, as well for the |
| 2037 // FlowGraph itself. | 2069 // FlowGraph itself. |
| 2038 void FlowGraphInliner::SetInliningId(FlowGraph* flow_graph, | 2070 void FlowGraphInliner::SetInliningId(FlowGraph* flow_graph, |
| 2039 intptr_t inlining_id) { | 2071 intptr_t inlining_id) { |
| 2040 ASSERT(flow_graph->inlining_id() < 0); | 2072 ASSERT(flow_graph->inlining_id() < 0); |
| 2041 flow_graph->set_inlining_id(inlining_id); | 2073 flow_graph->set_inlining_id(inlining_id); |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2083 IsInlineableOperator(function) || | 2115 IsInlineableOperator(function) || |
| 2084 (function.kind() == RawFunction::kConstructor)) { | 2116 (function.kind() == RawFunction::kConstructor)) { |
| 2085 const intptr_t count = function.optimized_instruction_count(); | 2117 const intptr_t count = function.optimized_instruction_count(); |
| 2086 if ((count != 0) && (count < FLAG_inline_getters_setters_smaller_than)) { | 2118 if ((count != 0) && (count < FLAG_inline_getters_setters_smaller_than)) { |
| 2087 return true; | 2119 return true; |
| 2088 } | 2120 } |
| 2089 } | 2121 } |
| 2090 return MethodRecognizer::AlwaysInline(function); | 2122 return MethodRecognizer::AlwaysInline(function); |
| 2091 } | 2123 } |
| 2092 | 2124 |
| 2093 void FlowGraphInliner::Inline() { | 2125 int FlowGraphInliner::Inline() { |
| 2094 // Collect graph info and store it on the function. | 2126 // Collect graph info and store it on the function. |
| 2095 // We might later use it for an early bailout from the inlining. | 2127 // We might later use it for an early bailout from the inlining. |
| 2096 CollectGraphInfo(flow_graph_); | 2128 CollectGraphInfo(flow_graph_); |
| 2097 | 2129 |
| 2098 const Function& top = flow_graph_->function(); | 2130 const Function& top = flow_graph_->function(); |
| 2099 if ((FLAG_inlining_filter != NULL) && | 2131 if ((FLAG_inlining_filter != NULL) && |
| 2100 (strstr(top.ToFullyQualifiedCString(), FLAG_inlining_filter) == NULL)) { | 2132 (strstr(top.ToFullyQualifiedCString(), FLAG_inlining_filter) == NULL)) { |
| 2101 return; | 2133 return 0; |
| 2102 } | 2134 } |
| 2103 | 2135 |
| 2104 if (trace_inlining()) { | 2136 if (trace_inlining()) { |
| 2105 String& name = String::Handle(top.QualifiedUserVisibleName()); | 2137 String& name = String::Handle(top.QualifiedUserVisibleName()); |
| 2106 THR_Print("Inlining calls in %s\n", name.ToCString()); | 2138 THR_Print("Inlining calls in %s\n", name.ToCString()); |
| 2107 } | 2139 } |
| 2108 | 2140 |
| 2109 if (FLAG_support_il_printer && trace_inlining() && | 2141 if (FLAG_support_il_printer && trace_inlining() && |
| 2110 (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized)) { | 2142 (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized)) { |
| 2111 THR_Print("Before Inlining of %s\n", | 2143 THR_Print("Before Inlining of %s\n", |
| (...skipping 20 matching lines...) Expand all Loading... |
| 2132 THR_Print("Inlining growth factor: %f\n", inliner.GrowthFactor()); | 2164 THR_Print("Inlining growth factor: %f\n", inliner.GrowthFactor()); |
| 2133 if (FLAG_support_il_printer && | 2165 if (FLAG_support_il_printer && |
| 2134 (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized)) { | 2166 (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized)) { |
| 2135 THR_Print("After Inlining of %s\n", | 2167 THR_Print("After Inlining of %s\n", |
| 2136 flow_graph_->function().ToFullyQualifiedCString()); | 2168 flow_graph_->function().ToFullyQualifiedCString()); |
| 2137 FlowGraphPrinter printer(*flow_graph_); | 2169 FlowGraphPrinter printer(*flow_graph_); |
| 2138 printer.PrintBlocks(); | 2170 printer.PrintBlocks(); |
| 2139 } | 2171 } |
| 2140 } | 2172 } |
| 2141 } | 2173 } |
| 2174 return inliner.inlining_depth(); |
| 2142 } | 2175 } |
| 2143 | 2176 |
| 2144 intptr_t FlowGraphInliner::NextInlineId(const Function& function, | 2177 intptr_t FlowGraphInliner::NextInlineId(const Function& function, |
| 2145 TokenPosition tp, | 2178 TokenPosition tp, |
| 2146 intptr_t parent_id) { | 2179 intptr_t parent_id) { |
| 2147 const intptr_t id = inline_id_to_function_->length(); | 2180 const intptr_t id = inline_id_to_function_->length(); |
| 2148 // TODO(johnmccutchan): Do not allow IsNoSource once all nodes have proper | 2181 // TODO(johnmccutchan): Do not allow IsNoSource once all nodes have proper |
| 2149 // source positions. | 2182 // source positions. |
| 2150 ASSERT(tp.IsReal() || tp.IsSynthetic() || tp.IsNoSource()); | 2183 ASSERT(tp.IsReal() || tp.IsSynthetic() || tp.IsNoSource()); |
| 2151 inline_id_to_function_->Add(&function); | 2184 inline_id_to_function_->Add(&function); |
| (...skipping 1578 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3730 } | 3763 } |
| 3731 | 3764 |
| 3732 default: | 3765 default: |
| 3733 return false; | 3766 return false; |
| 3734 } | 3767 } |
| 3735 } | 3768 } |
| 3736 | 3769 |
| 3737 } // namespace dart | 3770 } // namespace dart |
| 3738 | 3771 |
| 3739 #endif // !defined(DART_PRECOMPILED_RUNTIME) | 3772 #endif // !defined(DART_PRECOMPILED_RUNTIME) |
| OLD | NEW |