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