Chromium Code Reviews| 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" |
| 11 #include "vm/flow_graph_optimizer.h" | 11 #include "vm/flow_graph_optimizer.h" |
| 12 #include "vm/il_printer.h" | 12 #include "vm/il_printer.h" |
| 13 #include "vm/intrinsifier.h" | 13 #include "vm/intrinsifier.h" |
| 14 #include "vm/longjump.h" | 14 #include "vm/longjump.h" |
| 15 #include "vm/object.h" | 15 #include "vm/object.h" |
| 16 #include "vm/object_store.h" | 16 #include "vm/object_store.h" |
| 17 #include "vm/timer.h" | 17 #include "vm/timer.h" |
| 18 | 18 |
| 19 namespace dart { | 19 namespace dart { |
| 20 | 20 |
| 21 DEFINE_FLAG(bool, trace_inlining, false, "Trace inlining"); | 21 DEFINE_FLAG(bool, trace_inlining, false, "Trace inlining"); |
| 22 DEFINE_FLAG(charp, inlining_filter, NULL, "Inline only in named function"); | 22 DEFINE_FLAG(charp, inlining_filter, NULL, "Inline only in named function"); |
| 23 DEFINE_FLAG(int, inlining_size_threshold, 50, | 23 |
| 24 "Inline only functions with up to threshold instructions (default 50)"); | 24 // Flags for inlining heuristics. |
| 25 // TODO(srdjan): set to 3 once crash in apidoc.dart is resolved. | |
| 26 DEFINE_FLAG(int, inlining_depth_threshold, 3, | 25 DEFINE_FLAG(int, inlining_depth_threshold, 3, |
| 27 "Inline recursively up to threshold depth (default 3)"); | 26 "Inline function calls up to threshold nesting depth"); |
| 27 DEFINE_FLAG(int, inlining_size_threshold, 20, | |
| 28 "Always inline functions that have threshold or fewer instructions"); | |
| 29 DEFINE_FLAG(int, inlining_in_loop_size_threshold, 60, | |
| 30 "Inline functions in loops that have threshold or fewer instructions"); | |
| 31 DEFINE_FLAG(int, inlining_callee_call_sites_threshold, 1, | |
| 32 "Always inline functions containing threshold or fewer calls."); | |
| 33 DEFINE_FLAG(int, inlining_constant_arguments_count, 1, | |
| 34 "Inline function calls with sufficient constant arguments " | |
| 35 "and up to the increased threshold on instructions"); | |
| 36 DEFINE_FLAG(int, inlining_constant_arguments_size_threshold, 40, | |
| 37 "Inline function calls with sufficient constant arguments " | |
| 38 "and up to the increased threshold on instructions"); | |
|
srdjan
2012/10/25 20:38:50
Indent 4 characters (continuation of a 'statement
zerny-google
2012/10/29 16:44:25
Done.
| |
| 39 | |
| 28 DECLARE_FLAG(bool, print_flow_graph); | 40 DECLARE_FLAG(bool, print_flow_graph); |
| 29 DECLARE_FLAG(int, deoptimization_counter_threshold); | 41 DECLARE_FLAG(int, deoptimization_counter_threshold); |
| 30 DECLARE_FLAG(bool, verify_compiler); | 42 DECLARE_FLAG(bool, verify_compiler); |
| 31 DECLARE_FLAG(bool, compiler_stats); | 43 DECLARE_FLAG(bool, compiler_stats); |
| 32 DECLARE_FLAG(bool, reject_named_argument_as_positional); | 44 DECLARE_FLAG(bool, reject_named_argument_as_positional); |
| 33 | 45 |
| 34 #define TRACE_INLINING(statement) \ | 46 #define TRACE_INLINING(statement) \ |
| 35 do { \ | 47 do { \ |
| 36 if (FLAG_trace_inlining) statement; \ | 48 if (FLAG_trace_inlining) statement; \ |
| 37 } while (false) | 49 } while (false) |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 121 // Pair of an argument name and its value. | 133 // Pair of an argument name and its value. |
| 122 struct NamedArgument : ValueObject { | 134 struct NamedArgument : ValueObject { |
| 123 public: | 135 public: |
| 124 String* name; | 136 String* name; |
| 125 Value* value; | 137 Value* value; |
| 126 NamedArgument(String* name, Value* value) | 138 NamedArgument(String* name, Value* value) |
| 127 : name(name), value(value) { } | 139 : name(name), value(value) { } |
| 128 }; | 140 }; |
| 129 | 141 |
| 130 | 142 |
| 143 // Helper to collect information about a callee graph. | |
|
srdjan
2012/10/25 20:38:50
about inlineable callee graphs
zerny-google
2012/10/29 16:44:25
Done.
| |
| 144 class GraphInfoCollector : public ValueObject { | |
| 145 public: | |
| 146 explicit GraphInfoCollector() | |
|
srdjan
2012/10/25 20:38:50
remove explicit
zerny-google
2012/10/29 16:44:25
Done.
| |
| 147 : call_site_count_(0), | |
| 148 instruction_count_(0) { } | |
| 149 | |
| 150 void Collect(const FlowGraph& graph) { | |
| 151 call_site_count_ = 0; | |
| 152 instruction_count_ = 0; | |
| 153 for (BlockIterator block_it = graph.postorder_iterator(); | |
| 154 !block_it.Done(); | |
| 155 block_it.Advance()) { | |
| 156 for (ForwardInstructionIterator it(block_it.Current()); | |
| 157 !it.Done(); | |
| 158 it.Advance()) { | |
| 159 ++instruction_count_; | |
| 160 if (it.Current()->IsStaticCall() || | |
| 161 it.Current()->IsClosureCall() || | |
| 162 it.Current()->IsPolymorphicInstanceCall()) { | |
| 163 ++call_site_count_; | |
| 164 } | |
| 165 } | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 intptr_t call_site_count() const { return call_site_count_; } | |
| 170 intptr_t instruction_count() const { return instruction_count_; } | |
| 171 | |
| 172 private: | |
| 173 intptr_t call_site_count_; | |
| 174 intptr_t instruction_count_; | |
| 175 }; | |
| 176 | |
| 177 | |
| 131 // A collection of call sites to consider for inlining. | 178 // A collection of call sites to consider for inlining. |
| 132 class CallSites : public FlowGraphVisitor { | 179 class CallSites : public FlowGraphVisitor { |
| 133 public: | 180 public: |
| 134 explicit CallSites(FlowGraph* flow_graph) | 181 explicit CallSites(FlowGraph* flow_graph) |
| 135 : FlowGraphVisitor(flow_graph->postorder()), // We don't use this order. | 182 : FlowGraphVisitor(flow_graph->postorder()), // We don't use this order. |
| 136 static_calls_(), | 183 static_calls_(), |
| 137 closure_calls_(), | 184 closure_calls_(), |
| 138 instance_calls_() { } | 185 instance_calls_() { } |
| 139 | 186 |
| 140 GrowableArray<StaticCallInstr*>* static_calls() { | 187 GrowableArray<StaticCallInstr*>* static_calls() { |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 200 : caller_graph_(flow_graph), | 247 : caller_graph_(flow_graph), |
| 201 next_ssa_temp_index_(flow_graph->max_virtual_register_number()), | 248 next_ssa_temp_index_(flow_graph->max_virtual_register_number()), |
| 202 inlined_(false), | 249 inlined_(false), |
| 203 initial_size_(flow_graph->InstructionCount()), | 250 initial_size_(flow_graph->InstructionCount()), |
| 204 inlined_size_(0), | 251 inlined_size_(0), |
| 205 inlining_depth_(1), | 252 inlining_depth_(1), |
| 206 collected_call_sites_(NULL), | 253 collected_call_sites_(NULL), |
| 207 inlining_call_sites_(NULL), | 254 inlining_call_sites_(NULL), |
| 208 function_cache_() { } | 255 function_cache_() { } |
| 209 | 256 |
| 257 // Inlining heuristics based on Cooper et al. 2008. | |
| 258 bool ShouldWeInline(intptr_t loop_depth, | |
| 259 intptr_t size, | |
|
srdjan
2012/10/25 20:38:50
Size of what? Please name better
zerny-google
2012/10/29 16:44:25
s/size/instr_count
| |
| 260 intptr_t call_sites, | |
|
srdjan
2012/10/25 20:38:50
num_call_sites?
zerny-google
2012/10/29 16:44:25
s/call_sites/call_site_count
| |
| 261 intptr_t constant_args) { | |
| 262 if (size <= FLAG_inlining_size_threshold) { | |
| 263 return true; | |
| 264 } | |
| 265 if (call_sites <= FLAG_inlining_callee_call_sites_threshold) { | |
| 266 return true; | |
| 267 } | |
| 268 if ((loop_depth > 0) && (size <= FLAG_inlining_in_loop_size_threshold)) { | |
| 269 return true; | |
| 270 } | |
| 271 if ((constant_args >= FLAG_inlining_constant_arguments_count) && | |
| 272 (size <= FLAG_inlining_constant_arguments_size_threshold)) { | |
| 273 return true; | |
| 274 } | |
| 275 return false; | |
| 276 } | |
| 277 | |
| 210 void InlineCalls() { | 278 void InlineCalls() { |
| 211 // If inlining depth is less then one abort. | 279 // If inlining depth is less then one abort. |
| 212 if (FLAG_inlining_depth_threshold < 1) return; | 280 if (FLAG_inlining_depth_threshold < 1) return; |
| 213 // Create two call site collections to swap between. | 281 // Create two call site collections to swap between. |
| 214 CallSites sites1(caller_graph_); | 282 CallSites sites1(caller_graph_); |
| 215 CallSites sites2(caller_graph_); | 283 CallSites sites2(caller_graph_); |
| 216 CallSites* call_sites_temp = NULL; | 284 CallSites* call_sites_temp = NULL; |
| 217 collected_call_sites_ = &sites1; | 285 collected_call_sites_ = &sites1; |
| 218 inlining_call_sites_ = &sites2; | 286 inlining_call_sites_ = &sites2; |
| 219 // Collect initial call sites. | 287 // Collect initial call sites. |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 281 } | 349 } |
| 282 | 350 |
| 283 // Abort if we are running legacy support for optional parameters. | 351 // Abort if we are running legacy support for optional parameters. |
| 284 if (!FLAG_reject_named_argument_as_positional && | 352 if (!FLAG_reject_named_argument_as_positional && |
| 285 function.HasOptionalPositionalParameters() && | 353 function.HasOptionalPositionalParameters() && |
| 286 (!argument_names.IsNull() && (argument_names.Length() > 0))) { | 354 (!argument_names.IsNull() && (argument_names.Length() > 0))) { |
| 287 function.set_is_inlinable(false); | 355 function.set_is_inlinable(false); |
| 288 TRACE_INLINING(OS::Print( | 356 TRACE_INLINING(OS::Print( |
| 289 " Bailout: named optional positional parameter\n")); | 357 " Bailout: named optional positional parameter\n")); |
| 290 return false; | 358 return false; |
| 291 } | 359 } |
|
srdjan
2012/10/25 20:38:50
Could you use a modified inlining_size_threshold v
Kevin Millikin (Google)
2012/10/26 12:07:22
We could definitely do that. In fact, we could ca
zerny-google
2012/10/29 16:44:25
The is_inlinable bit approximated this (line 479),
| |
| 292 | 360 |
| 293 Isolate* isolate = Isolate::Current(); | 361 Isolate* isolate = Isolate::Current(); |
| 294 // Save and clear IC data. | 362 // Save and clear IC data. |
| 295 const Array& prev_ic_data = Array::Handle(isolate->ic_data_array()); | 363 const Array& prev_ic_data = Array::Handle(isolate->ic_data_array()); |
| 296 isolate->set_ic_data_array(Array::null()); | 364 isolate->set_ic_data_array(Array::null()); |
| 297 // Save and clear deopt id. | 365 // Save and clear deopt id. |
| 298 const intptr_t prev_deopt_id = isolate->deopt_id(); | 366 const intptr_t prev_deopt_id = isolate->deopt_id(); |
| 299 isolate->set_deopt_id(0); | 367 isolate->set_deopt_id(0); |
| 300 // Install bailout jump. | 368 // Install bailout jump. |
| 301 LongJump* base = isolate->long_jump_base(); | 369 LongJump* base = isolate->long_jump_base(); |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 313 } | 381 } |
| 314 | 382 |
| 315 // Load IC data for the callee. | 383 // Load IC data for the callee. |
| 316 if (function.HasCode()) { | 384 if (function.HasCode()) { |
| 317 const Code& unoptimized_code = | 385 const Code& unoptimized_code = |
| 318 Code::Handle(function.unoptimized_code()); | 386 Code::Handle(function.unoptimized_code()); |
| 319 isolate->set_ic_data_array(unoptimized_code.ExtractTypeFeedbackArray()); | 387 isolate->set_ic_data_array(unoptimized_code.ExtractTypeFeedbackArray()); |
| 320 } | 388 } |
| 321 | 389 |
| 322 // Build the callee graph. | 390 // Build the callee graph. |
| 391 const intptr_t loop_depth = call->GetBlock()->loop_depth(); | |
| 323 FlowGraphBuilder builder(*parsed_function); | 392 FlowGraphBuilder builder(*parsed_function); |
| 324 builder.SetInitialBlockId(caller_graph_->max_block_id()); | 393 builder.SetInitialBlockId(caller_graph_->max_block_id()); |
| 325 FlowGraph* callee_graph; | 394 FlowGraph* callee_graph; |
| 326 { | 395 { |
| 327 TimerScope timer(FLAG_compiler_stats, | 396 TimerScope timer(FLAG_compiler_stats, |
| 328 &CompilerStats::graphinliner_build_timer, | 397 &CompilerStats::graphinliner_build_timer, |
| 329 isolate); | 398 isolate); |
| 330 callee_graph = builder.BuildGraph(FlowGraphBuilder::kValueContext); | 399 callee_graph = |
| 400 builder.BuildGraph(FlowGraphBuilder::kValueContext, loop_depth); | |
| 331 } | 401 } |
| 332 | 402 |
| 333 // The parameter stubs are a copy of the actual arguments providing | 403 // The parameter stubs are a copy of the actual arguments providing |
| 334 // concrete information about the values, for example constant values, | 404 // concrete information about the values, for example constant values, |
| 335 // without linking between the caller and callee graphs. | 405 // without linking between the caller and callee graphs. |
| 336 // TODO(zerny): Put more information in the stubs, eg, type information. | 406 // TODO(zerny): Put more information in the stubs, eg, type information. |
| 337 GrowableArray<Definition*> param_stubs(function.NumParameters()); | 407 GrowableArray<Definition*> param_stubs(function.NumParameters()); |
| 338 | 408 |
| 339 // Create a parameter stub for each fixed positional parameter. | 409 // Create a parameter stub for each fixed positional parameter. |
| 340 for (intptr_t i = 0; i < function.num_fixed_parameters(); ++i) { | 410 for (intptr_t i = 0; i < function.num_fixed_parameters(); ++i) { |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 385 callee_graph->ComputeUseLists(); | 455 callee_graph->ComputeUseLists(); |
| 386 } | 456 } |
| 387 | 457 |
| 388 if (FLAG_trace_inlining && FLAG_print_flow_graph) { | 458 if (FLAG_trace_inlining && FLAG_print_flow_graph) { |
| 389 OS::Print("Callee graph for inlining %s\n", | 459 OS::Print("Callee graph for inlining %s\n", |
| 390 function.ToFullyQualifiedCString()); | 460 function.ToFullyQualifiedCString()); |
| 391 FlowGraphPrinter printer(*callee_graph); | 461 FlowGraphPrinter printer(*callee_graph); |
| 392 printer.PrintBlocks(); | 462 printer.PrintBlocks(); |
| 393 } | 463 } |
| 394 | 464 |
| 395 // If result is more than size threshold then abort. | 465 // Collect information about the call site and caller graph. |
| 396 // TODO(zerny): Do this after CP and dead code elimination. | 466 // TODO(zerny): Do this after CP and dead code elimination. |
| 397 intptr_t size = callee_graph->InstructionCount(); | 467 intptr_t constants_count = 0; |
| 398 if (size > FLAG_inlining_size_threshold) { | 468 for (intptr_t i = 0; i < param_stubs.length(); ++i) { |
| 399 function.set_is_inlinable(false); | 469 if (param_stubs[i]->IsConstant()) ++constants_count; |
| 470 } | |
| 471 GraphInfoCollector info; | |
| 472 info.Collect(*callee_graph); | |
| 473 const intptr_t size = info.instruction_count(); | |
| 474 // Use heuristics do decide if this call should be inlined. | |
| 475 if (!ShouldWeInline(loop_depth, | |
| 476 size, | |
| 477 info.call_site_count(), | |
| 478 constants_count)) { | |
| 479 // If size is larger than all thresholds, don't consider it again. | |
| 480 if ((size > FLAG_inlining_size_threshold) && | |
| 481 (size > FLAG_inlining_in_loop_size_threshold) && | |
| 482 (size > FLAG_inlining_callee_call_sites_threshold) && | |
| 483 (size > FLAG_inlining_constant_arguments_size_threshold)) { | |
| 484 function.set_is_inlinable(false); | |
| 485 } | |
| 400 isolate->set_long_jump_base(base); | 486 isolate->set_long_jump_base(base); |
| 401 isolate->set_deopt_id(prev_deopt_id); | 487 isolate->set_deopt_id(prev_deopt_id); |
| 402 isolate->set_ic_data_array(prev_ic_data.raw()); | 488 isolate->set_ic_data_array(prev_ic_data.raw()); |
| 403 TRACE_INLINING(OS::Print(" Bailout: graph size %"Pd"\n", size)); | 489 TRACE_INLINING(OS::Print(" Bailout: heuristics with " |
| 490 "loop depth: %"Pd", " | |
| 491 "code size: %"Pd", " | |
| 492 "call sites: %"Pd", " | |
| 493 "const args: %"Pd"\n", | |
| 494 loop_depth, | |
| 495 size, | |
| 496 info.call_site_count(), | |
| 497 constants_count)); | |
| 404 return false; | 498 return false; |
| 405 } | 499 } |
| 406 | 500 |
| 407 // If depth is less or equal to threshold recursively add call sites. | 501 // If depth is less or equal to threshold recursively add call sites. |
| 408 if (inlining_depth_ < FLAG_inlining_depth_threshold) { | 502 if (inlining_depth_ < FLAG_inlining_depth_threshold) { |
| 409 collected_call_sites_->FindCallSites(callee_graph); | 503 collected_call_sites_->FindCallSites(callee_graph); |
| 410 } | 504 } |
| 411 | 505 |
| 412 { | 506 { |
| 413 TimerScope timer(FLAG_compiler_stats, | 507 TimerScope timer(FLAG_compiler_stats, |
| (...skipping 282 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 696 OS::Print("After Inlining of %s\n", flow_graph_-> | 790 OS::Print("After Inlining of %s\n", flow_graph_-> |
| 697 parsed_function().function().ToFullyQualifiedCString()); | 791 parsed_function().function().ToFullyQualifiedCString()); |
| 698 FlowGraphPrinter printer(*flow_graph_); | 792 FlowGraphPrinter printer(*flow_graph_); |
| 699 printer.PrintBlocks(); | 793 printer.PrintBlocks(); |
| 700 } | 794 } |
| 701 } | 795 } |
| 702 } | 796 } |
| 703 } | 797 } |
| 704 | 798 |
| 705 } // namespace dart | 799 } // namespace dart |
| OLD | NEW |