| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 #include "vm/flow_graph_inliner.h" | 5 #include "vm/flow_graph_inliner.h" |
| 6 | 6 |
| 7 #include "vm/compiler.h" | 7 #include "vm/compiler.h" |
| 8 #include "vm/flags.h" | 8 #include "vm/flags.h" |
| 9 #include "vm/flow_graph.h" | 9 #include "vm/flow_graph.h" |
| 10 #include "vm/flow_graph_builder.h" | 10 #include "vm/flow_graph_builder.h" |
| (...skipping 18 matching lines...) Expand all Loading... |
| 29 DEFINE_FLAG(int, inlining_in_loop_size_threshold, 80, | 29 DEFINE_FLAG(int, inlining_in_loop_size_threshold, 80, |
| 30 "Inline functions in loops that have threshold or fewer instructions"); | 30 "Inline functions in loops that have threshold or fewer instructions"); |
| 31 DEFINE_FLAG(int, inlining_callee_call_sites_threshold, 1, | 31 DEFINE_FLAG(int, inlining_callee_call_sites_threshold, 1, |
| 32 "Always inline functions containing threshold or fewer calls."); | 32 "Always inline functions containing threshold or fewer calls."); |
| 33 DEFINE_FLAG(int, inlining_constant_arguments_count, 1, | 33 DEFINE_FLAG(int, inlining_constant_arguments_count, 1, |
| 34 "Inline function calls with sufficient constant arguments " | 34 "Inline function calls with sufficient constant arguments " |
| 35 "and up to the increased threshold on instructions"); | 35 "and up to the increased threshold on instructions"); |
| 36 DEFINE_FLAG(int, inlining_constant_arguments_size_threshold, 60, | 36 DEFINE_FLAG(int, inlining_constant_arguments_size_threshold, 60, |
| 37 "Inline function calls with sufficient constant arguments " | 37 "Inline function calls with sufficient constant arguments " |
| 38 "and up to the increased threshold on instructions"); | 38 "and up to the increased threshold on instructions"); |
| 39 DEFINE_FLAG(int, inlining_hotness, 10, |
| 40 "Inline only hotter calls, in percents (0 .. 100); " |
| 41 "default 10%: calls above-equal 10% of max-count are inlined."); |
| 39 | 42 |
| 40 DECLARE_FLAG(bool, print_flow_graph); | 43 DECLARE_FLAG(bool, print_flow_graph); |
| 41 DECLARE_FLAG(int, deoptimization_counter_threshold); | 44 DECLARE_FLAG(int, deoptimization_counter_threshold); |
| 42 DECLARE_FLAG(bool, verify_compiler); | 45 DECLARE_FLAG(bool, verify_compiler); |
| 43 DECLARE_FLAG(bool, compiler_stats); | 46 DECLARE_FLAG(bool, compiler_stats); |
| 44 | 47 |
| 45 #define TRACE_INLINING(statement) \ | 48 #define TRACE_INLINING(statement) \ |
| 46 do { \ | 49 do { \ |
| 47 if (FLAG_trace_inlining) statement; \ | 50 if (FLAG_trace_inlining) statement; \ |
| 48 } while (false) | 51 } while (false) |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 178 // A collection of call sites to consider for inlining. | 181 // A collection of call sites to consider for inlining. |
| 179 class CallSites : public FlowGraphVisitor { | 182 class CallSites : public FlowGraphVisitor { |
| 180 public: | 183 public: |
| 181 explicit CallSites(FlowGraph* flow_graph) | 184 explicit CallSites(FlowGraph* flow_graph) |
| 182 : FlowGraphVisitor(flow_graph->postorder()), // We don't use this order. | 185 : FlowGraphVisitor(flow_graph->postorder()), // We don't use this order. |
| 183 static_calls_(), | 186 static_calls_(), |
| 184 closure_calls_(), | 187 closure_calls_(), |
| 185 instance_calls_(), | 188 instance_calls_(), |
| 186 skip_static_call_deopt_ids_() { } | 189 skip_static_call_deopt_ids_() { } |
| 187 | 190 |
| 188 GrowableArray<StaticCallInstr*>* static_calls() { | 191 const GrowableArray<StaticCallInstr*>& static_calls() const { |
| 189 return &static_calls_; | 192 return static_calls_; |
| 190 } | 193 } |
| 191 | 194 |
| 192 GrowableArray<ClosureCallInstr*>* closure_calls() { | 195 const GrowableArray<ClosureCallInstr*>& closure_calls() const { |
| 193 return &closure_calls_; | 196 return closure_calls_; |
| 194 } | 197 } |
| 195 | 198 |
| 196 GrowableArray<PolymorphicInstanceCallInstr*>* instance_calls() { | 199 struct InstanceCallInfo { |
| 197 return &instance_calls_; | 200 PolymorphicInstanceCallInstr* call; |
| 201 double ratio; |
| 202 explicit InstanceCallInfo(PolymorphicInstanceCallInstr* call_arg) |
| 203 : call(call_arg), ratio(0.0) {} |
| 204 }; |
| 205 |
| 206 const GrowableArray<InstanceCallInfo>& instance_calls() const { |
| 207 return instance_calls_; |
| 198 } | 208 } |
| 199 | 209 |
| 200 bool HasCalls() const { | 210 bool HasCalls() const { |
| 201 return !(static_calls_.is_empty() && | 211 return !(static_calls_.is_empty() && |
| 202 closure_calls_.is_empty() && | 212 closure_calls_.is_empty() && |
| 203 instance_calls_.is_empty()); | 213 instance_calls_.is_empty()); |
| 204 } | 214 } |
| 205 | 215 |
| 206 void Clear() { | 216 void Clear() { |
| 207 static_calls_.Clear(); | 217 static_calls_.Clear(); |
| 208 closure_calls_.Clear(); | 218 closure_calls_.Clear(); |
| 209 instance_calls_.Clear(); | 219 instance_calls_.Clear(); |
| 210 skip_static_call_deopt_ids_.Clear(); | 220 skip_static_call_deopt_ids_.Clear(); |
| 211 } | 221 } |
| 212 | 222 |
| 213 void FindCallSites(FlowGraph* graph) { | 223 void FindCallSites(FlowGraph* graph) { |
| 214 ASSERT(graph != NULL); | 224 ASSERT(graph != NULL); |
| 215 const Function& function = graph->parsed_function().function(); | 225 const Function& function = graph->parsed_function().function(); |
| 216 ASSERT(function.HasCode()); | 226 ASSERT(function.HasCode()); |
| 217 const Code& code = Code::Handle(function.unoptimized_code()); | 227 const Code& code = Code::Handle(function.unoptimized_code()); |
| 228 |
| 218 skip_static_call_deopt_ids_.Clear(); | 229 skip_static_call_deopt_ids_.Clear(); |
| 219 code.ExtractUncalledStaticCallDeoptIds(&skip_static_call_deopt_ids_); | 230 code.ExtractUncalledStaticCallDeoptIds(&skip_static_call_deopt_ids_); |
| 231 |
| 232 const intptr_t instance_call_start_ix = instance_calls_.length(); |
| 220 for (BlockIterator block_it = graph->postorder_iterator(); | 233 for (BlockIterator block_it = graph->postorder_iterator(); |
| 221 !block_it.Done(); | 234 !block_it.Done(); |
| 222 block_it.Advance()) { | 235 block_it.Advance()) { |
| 223 for (ForwardInstructionIterator it(block_it.Current()); | 236 for (ForwardInstructionIterator it(block_it.Current()); |
| 224 !it.Done(); | 237 !it.Done(); |
| 225 it.Advance()) { | 238 it.Advance()) { |
| 226 it.Current()->Accept(this); | 239 it.Current()->Accept(this); |
| 227 } | 240 } |
| 228 } | 241 } |
| 242 // Compute instance call site ratio. |
| 243 const intptr_t num_instance_calls = |
| 244 instance_calls_.length() - instance_call_start_ix; |
| 245 intptr_t max_count = 0; |
| 246 GrowableArray<intptr_t> call_counts(num_instance_calls); |
| 247 for (intptr_t i = 0; i < num_instance_calls; ++i) { |
| 248 const intptr_t aggregate_count = |
| 249 instance_calls_[i + instance_call_start_ix]. |
| 250 call->ic_data().AggregateCount(); |
| 251 call_counts.Add(aggregate_count); |
| 252 if (aggregate_count > max_count) max_count = aggregate_count; |
| 253 } |
| 254 |
| 255 |
| 256 for (intptr_t i = 0; i < num_instance_calls; ++i) { |
| 257 const double ratio = static_cast<double>(call_counts[i]) / max_count; |
| 258 instance_calls_[i + instance_call_start_ix].ratio = ratio; |
| 259 } |
| 229 } | 260 } |
| 230 | 261 |
| 231 void VisitClosureCall(ClosureCallInstr* call) { | 262 void VisitClosureCall(ClosureCallInstr* call) { |
| 232 closure_calls_.Add(call); | 263 closure_calls_.Add(call); |
| 233 } | 264 } |
| 234 | 265 |
| 235 void VisitPolymorphicInstanceCall(PolymorphicInstanceCallInstr* call) { | 266 void VisitPolymorphicInstanceCall(PolymorphicInstanceCallInstr* call) { |
| 236 instance_calls_.Add(call); | 267 instance_calls_.Add(InstanceCallInfo(call)); |
| 237 } | 268 } |
| 238 | 269 |
| 239 void VisitStaticCall(StaticCallInstr* call) { | 270 void VisitStaticCall(StaticCallInstr* call) { |
| 240 if (!call->function().IsInlineable()) return; | 271 if (!call->function().IsInlineable()) return; |
| 241 const intptr_t call_deopt_id = call->deopt_id(); | 272 const intptr_t call_deopt_id = call->deopt_id(); |
| 242 for (intptr_t i = 0; i < skip_static_call_deopt_ids_.length(); i++) { | 273 for (intptr_t i = 0; i < skip_static_call_deopt_ids_.length(); i++) { |
| 243 if (call_deopt_id == skip_static_call_deopt_ids_[i]) { | 274 if (call_deopt_id == skip_static_call_deopt_ids_[i]) { |
| 244 // Do not inline this call. | 275 // Do not inline this call. |
| 245 return; | 276 return; |
| 246 } | 277 } |
| 247 } | 278 } |
| 248 static_calls_.Add(call); | 279 static_calls_.Add(call); |
| 249 } | 280 } |
| 250 | 281 |
| 251 private: | 282 private: |
| 252 GrowableArray<StaticCallInstr*> static_calls_; | 283 GrowableArray<StaticCallInstr*> static_calls_; |
| 253 GrowableArray<ClosureCallInstr*> closure_calls_; | 284 GrowableArray<ClosureCallInstr*> closure_calls_; |
| 254 GrowableArray<PolymorphicInstanceCallInstr*> instance_calls_; | 285 GrowableArray<InstanceCallInfo> instance_calls_; |
| 255 GrowableArray<intptr_t> skip_static_call_deopt_ids_; | 286 GrowableArray<intptr_t> skip_static_call_deopt_ids_; |
| 256 | 287 |
| 257 DISALLOW_COPY_AND_ASSIGN(CallSites); | 288 DISALLOW_COPY_AND_ASSIGN(CallSites); |
| 258 }; | 289 }; |
| 259 | 290 |
| 260 | 291 |
| 261 class CallSiteInliner : public ValueObject { | 292 class CallSiteInliner : public ValueObject { |
| 262 public: | 293 public: |
| 263 explicit CallSiteInliner(FlowGraph* flow_graph) | 294 explicit CallSiteInliner(FlowGraph* flow_graph) |
| 264 : caller_graph_(flow_graph), | 295 : caller_graph_(flow_graph), |
| (...skipping 358 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 623 } | 654 } |
| 624 *in_cache = false; | 655 *in_cache = false; |
| 625 ParsedFunction* parsed_function = new ParsedFunction(function); | 656 ParsedFunction* parsed_function = new ParsedFunction(function); |
| 626 Parser::ParseFunction(parsed_function); | 657 Parser::ParseFunction(parsed_function); |
| 627 parsed_function->AllocateVariables(); | 658 parsed_function->AllocateVariables(); |
| 628 return parsed_function; | 659 return parsed_function; |
| 629 } | 660 } |
| 630 | 661 |
| 631 void InlineStaticCalls() { | 662 void InlineStaticCalls() { |
| 632 const GrowableArray<StaticCallInstr*>& calls = | 663 const GrowableArray<StaticCallInstr*>& calls = |
| 633 *inlining_call_sites_->static_calls(); | 664 inlining_call_sites_->static_calls(); |
| 634 TRACE_INLINING(OS::Print(" Static Calls (%d)\n", calls.length())); | 665 TRACE_INLINING(OS::Print(" Static Calls (%d)\n", calls.length())); |
| 635 for (intptr_t i = 0; i < calls.length(); ++i) { | 666 for (intptr_t i = 0; i < calls.length(); ++i) { |
| 636 StaticCallInstr* call = calls[i]; | 667 StaticCallInstr* call = calls[i]; |
| 637 GrowableArray<Value*> arguments(call->ArgumentCount()); | 668 GrowableArray<Value*> arguments(call->ArgumentCount()); |
| 638 for (int i = 0; i < call->ArgumentCount(); ++i) { | 669 for (int i = 0; i < call->ArgumentCount(); ++i) { |
| 639 arguments.Add(call->ArgumentAt(i)->value()); | 670 arguments.Add(call->ArgumentAt(i)->value()); |
| 640 } | 671 } |
| 641 TryInlining(call->function(), call->argument_names(), &arguments, call); | 672 TryInlining(call->function(), call->argument_names(), &arguments, call); |
| 642 } | 673 } |
| 643 } | 674 } |
| 644 | 675 |
| 645 void InlineClosureCalls() { | 676 void InlineClosureCalls() { |
| 646 const GrowableArray<ClosureCallInstr*>& calls = | 677 const GrowableArray<ClosureCallInstr*>& calls = |
| 647 *inlining_call_sites_->closure_calls(); | 678 inlining_call_sites_->closure_calls(); |
| 648 TRACE_INLINING(OS::Print(" Closure Calls (%d)\n", calls.length())); | 679 TRACE_INLINING(OS::Print(" Closure Calls (%d)\n", calls.length())); |
| 649 for (intptr_t i = 0; i < calls.length(); ++i) { | 680 for (intptr_t i = 0; i < calls.length(); ++i) { |
| 650 ClosureCallInstr* call = calls[i]; | 681 ClosureCallInstr* call = calls[i]; |
| 651 // Find the closure of the callee. | 682 // Find the closure of the callee. |
| 652 ASSERT(call->ArgumentCount() > 0); | 683 ASSERT(call->ArgumentCount() > 0); |
| 653 const CreateClosureInstr* closure = | 684 const CreateClosureInstr* closure = |
| 654 call->ArgumentAt(0)->value()->definition()->AsCreateClosure(); | 685 call->ArgumentAt(0)->value()->definition()->AsCreateClosure(); |
| 655 if (closure == NULL) { | 686 if (closure == NULL) { |
| 656 TRACE_INLINING(OS::Print(" Bailout: non-closure operator\n")); | 687 TRACE_INLINING(OS::Print(" Bailout: non-closure operator\n")); |
| 657 continue; | 688 continue; |
| 658 } | 689 } |
| 659 GrowableArray<Value*> arguments(call->ArgumentCount()); | 690 GrowableArray<Value*> arguments(call->ArgumentCount()); |
| 660 for (int i = 0; i < call->ArgumentCount(); ++i) { | 691 for (int i = 0; i < call->ArgumentCount(); ++i) { |
| 661 arguments.Add(call->ArgumentAt(i)->value()); | 692 arguments.Add(call->ArgumentAt(i)->value()); |
| 662 } | 693 } |
| 663 TryInlining(closure->function(), | 694 TryInlining(closure->function(), |
| 664 call->argument_names(), | 695 call->argument_names(), |
| 665 &arguments, | 696 &arguments, |
| 666 call); | 697 call); |
| 667 } | 698 } |
| 668 } | 699 } |
| 669 | 700 |
| 670 void InlineInstanceCalls() { | 701 void InlineInstanceCalls() { |
| 671 const GrowableArray<PolymorphicInstanceCallInstr*>& calls = | 702 const GrowableArray<CallSites::InstanceCallInfo>& call_info = |
| 672 *inlining_call_sites_->instance_calls(); | 703 inlining_call_sites_->instance_calls(); |
| 673 TRACE_INLINING(OS::Print(" Polymorphic Instance Calls (%d)\n", | 704 TRACE_INLINING(OS::Print(" Polymorphic Instance Calls (%d)\n", |
| 674 calls.length())); | 705 call_info.length())); |
| 675 for (intptr_t i = 0; i < calls.length(); ++i) { | 706 for (intptr_t i = 0; i < call_info.length(); ++i) { |
| 676 PolymorphicInstanceCallInstr* instr = calls[i]; | 707 PolymorphicInstanceCallInstr* instr = call_info[i].call; |
| 677 const ICData& ic_data = instr->ic_data(); | 708 const ICData& ic_data = instr->ic_data(); |
| 678 const Function& target = Function::ZoneHandle(ic_data.GetTargetAt(0)); | 709 const Function& target = Function::ZoneHandle(ic_data.GetTargetAt(0)); |
| 679 if (instr->with_checks()) { | 710 if (instr->with_checks()) { |
| 680 TRACE_INLINING(OS::Print( | 711 TRACE_INLINING(OS::Print( |
| 681 " => %s (deopt count %d)\n Bailout: %"Pd" checks\n", | 712 " => %s (deopt count %d)\n Bailout: %"Pd" checks\n", |
| 682 target.ToCString(), | 713 target.ToCString(), |
| 683 target.deoptimization_counter(), | 714 target.deoptimization_counter(), |
| 684 ic_data.NumberOfChecks())); | 715 ic_data.NumberOfChecks())); |
| 716 continue; |
| 717 } |
| 718 if ((call_info[i].ratio * 100) < FLAG_inlining_hotness) { |
| 719 TRACE_INLINING(OS::Print( |
| 720 " => %s (deopt count %d)\n Bailout: cold %f\n", |
| 721 target.ToCString(), |
| 722 target.deoptimization_counter(), |
| 723 call_info[i].ratio)); |
| 685 continue; | 724 continue; |
| 686 } | 725 } |
| 687 GrowableArray<Value*> arguments(instr->ArgumentCount()); | 726 GrowableArray<Value*> arguments(instr->ArgumentCount()); |
| 688 for (int i = 0; i < instr->ArgumentCount(); ++i) { | 727 for (int arg_i = 0; arg_i < instr->ArgumentCount(); ++arg_i) { |
| 689 arguments.Add(instr->ArgumentAt(i)->value()); | 728 arguments.Add(instr->ArgumentAt(arg_i)->value()); |
| 690 } | 729 } |
| 691 TryInlining(target, | 730 TryInlining(target, |
| 692 instr->instance_call()->argument_names(), | 731 instr->instance_call()->argument_names(), |
| 693 &arguments, | 732 &arguments, |
| 694 instr); | 733 instr); |
| 695 } | 734 } |
| 696 } | 735 } |
| 697 | 736 |
| 698 void AdjustForOptionalParameters(const ParsedFunction& parsed_function, | 737 void AdjustForOptionalParameters(const ParsedFunction& parsed_function, |
| 699 const Array& argument_names, | 738 const Array& argument_names, |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 843 OS::Print("After Inlining of %s\n", flow_graph_-> | 882 OS::Print("After Inlining of %s\n", flow_graph_-> |
| 844 parsed_function().function().ToFullyQualifiedCString()); | 883 parsed_function().function().ToFullyQualifiedCString()); |
| 845 FlowGraphPrinter printer(*flow_graph_); | 884 FlowGraphPrinter printer(*flow_graph_); |
| 846 printer.PrintBlocks(); | 885 printer.PrintBlocks(); |
| 847 } | 886 } |
| 848 } | 887 } |
| 849 } | 888 } |
| 850 } | 889 } |
| 851 | 890 |
| 852 } // namespace dart | 891 } // namespace dart |
| OLD | NEW |