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 #include "vm/flow_graph_inliner.h" | 5 #include "vm/flow_graph_inliner.h" |
| 6 | 6 |
| 7 #include "vm/block_scheduler.h" | 7 #include "vm/block_scheduler.h" |
| 8 #include "vm/compiler.h" | 8 #include "vm/compiler.h" |
| 9 #include "vm/flags.h" | 9 #include "vm/flags.h" |
| 10 #include "vm/flow_graph.h" | 10 #include "vm/flow_graph.h" |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 40 "Inline function calls with sufficient constant arguments " | 40 "Inline function calls with sufficient constant arguments " |
| 41 "and up to the increased threshold on instructions"); | 41 "and up to the increased threshold on instructions"); |
| 42 DEFINE_FLAG(int, inlining_constant_arguments_size_threshold, 60, | 42 DEFINE_FLAG(int, inlining_constant_arguments_size_threshold, 60, |
| 43 "Inline function calls with sufficient constant arguments " | 43 "Inline function calls with sufficient constant arguments " |
| 44 "and up to the increased threshold on instructions"); | 44 "and up to the increased threshold on instructions"); |
| 45 DEFINE_FLAG(int, inlining_hotness, 10, | 45 DEFINE_FLAG(int, inlining_hotness, 10, |
| 46 "Inline only hotter calls, in percents (0 .. 100); " | 46 "Inline only hotter calls, in percents (0 .. 100); " |
| 47 "default 10%: calls above-equal 10% of max-count are inlined."); | 47 "default 10%: calls above-equal 10% of max-count are inlined."); |
| 48 DEFINE_FLAG(bool, inline_recursive, true, | 48 DEFINE_FLAG(bool, inline_recursive, true, |
| 49 "Inline recursive calls."); | 49 "Inline recursive calls."); |
| 50 DEFINE_FLAG(bool, print_inline_tree, false, "Print inlining tree"); | 50 DEFINE_FLAG(bool, print_inlining_tree, false, "Print inlining tree"); |
| 51 | 51 |
| 52 DECLARE_FLAG(bool, print_flow_graph); | 52 DECLARE_FLAG(bool, print_flow_graph); |
| 53 DECLARE_FLAG(bool, print_flow_graph_optimized); | 53 DECLARE_FLAG(bool, print_flow_graph_optimized); |
| 54 DECLARE_FLAG(int, deoptimization_counter_threshold); | 54 DECLARE_FLAG(int, deoptimization_counter_threshold); |
| 55 DECLARE_FLAG(bool, verify_compiler); | 55 DECLARE_FLAG(bool, verify_compiler); |
| 56 DECLARE_FLAG(bool, compiler_stats); | 56 DECLARE_FLAG(bool, compiler_stats); |
| 57 | 57 |
| 58 #define TRACE_INLINING(statement) \ | 58 #define TRACE_INLINING(statement) \ |
| 59 do { \ | 59 do { \ |
| 60 if (FLAG_trace_inlining) statement; \ | 60 if (FLAG_trace_inlining) statement; \ |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 129 continue; | 129 continue; |
| 130 } | 130 } |
| 131 if (current->IsPolymorphicInstanceCall()) { | 131 if (current->IsPolymorphicInstanceCall()) { |
| 132 PolymorphicInstanceCallInstr* call = | 132 PolymorphicInstanceCallInstr* call = |
| 133 current->AsPolymorphicInstanceCall(); | 133 current->AsPolymorphicInstanceCall(); |
| 134 // These checks make sure that the number of call-sites counted does | 134 // These checks make sure that the number of call-sites counted does |
| 135 // not change relative to the time when the current set of inlining | 135 // not change relative to the time when the current set of inlining |
| 136 // parameters was fixed. | 136 // parameters was fixed. |
| 137 // TODO(fschneider): Determine new heuristic parameters that avoid | 137 // TODO(fschneider): Determine new heuristic parameters that avoid |
| 138 // these checks entirely. | 138 // these checks entirely. |
| 139 if (!call->HasRecognizedTarget() && | 139 if (!call->HasSingleRecognizedTarget() && |
| 140 (call->instance_call()->token_kind() != Token::kEQ)) { | 140 (call->instance_call()->token_kind() != Token::kEQ)) { |
| 141 ++call_site_count_; | 141 ++call_site_count_; |
| 142 } | 142 } |
| 143 } | 143 } |
| 144 } | 144 } |
| 145 } | 145 } |
| 146 } | 146 } |
| 147 | 147 |
| 148 intptr_t call_site_count() const { return call_site_count_; } | 148 intptr_t call_site_count() const { return call_site_count_; } |
| 149 intptr_t instruction_count() const { return instruction_count_; } | 149 intptr_t instruction_count() const { return instruction_count_; } |
| 150 | 150 |
| 151 private: | 151 private: |
| 152 intptr_t call_site_count_; | 152 intptr_t call_site_count_; |
| 153 intptr_t instruction_count_; | 153 intptr_t instruction_count_; |
| 154 }; | 154 }; |
| 155 | 155 |
| 156 | 156 |
| 157 // Structure for collecting inline data needed to print inlining tree. | |
| 158 struct InlinedInfo { | |
| 159 const Function* caller; | |
| 160 const Function* inlined; | |
| 161 intptr_t inlined_depth; | |
| 162 const Definition* call_instr; | |
| 163 const char* bailout_reason; | |
| 164 InlinedInfo(const Function* caller_function, | |
| 165 const Function* inlined_function, | |
| 166 const intptr_t depth, | |
| 167 const Definition* call, | |
| 168 const char* reason = NULL) | |
| 169 : caller(caller_function), | |
| 170 inlined(inlined_function), | |
| 171 inlined_depth(depth), | |
| 172 call_instr(call), | |
| 173 bailout_reason(reason) {} | |
| 174 }; | |
| 175 | |
| 176 | |
| 157 // A collection of call sites to consider for inlining. | 177 // A collection of call sites to consider for inlining. |
| 158 class CallSites : public ValueObject { | 178 class CallSites : public ValueObject { |
| 159 public: | 179 public: |
| 160 explicit CallSites(FlowGraph* flow_graph) | 180 explicit CallSites(FlowGraph* flow_graph) |
| 161 : static_calls_(), | 181 : static_calls_(), |
| 162 closure_calls_(), | 182 closure_calls_(), |
| 163 instance_calls_() { } | 183 instance_calls_() { } |
| 164 | 184 |
| 165 struct InstanceCallInfo { | 185 struct InstanceCallInfo { |
| 166 PolymorphicInstanceCallInstr* call; | 186 PolymorphicInstanceCallInstr* call; |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 247 0.0 : static_cast<double>(instance_call_counts[i]) / max_count; | 267 0.0 : static_cast<double>(instance_call_counts[i]) / max_count; |
| 248 instance_calls_[i + instance_call_start_ix].ratio = ratio; | 268 instance_calls_[i + instance_call_start_ix].ratio = ratio; |
| 249 } | 269 } |
| 250 for (intptr_t i = 0; i < num_static_calls; ++i) { | 270 for (intptr_t i = 0; i < num_static_calls; ++i) { |
| 251 const double ratio = (max_count == 0) ? | 271 const double ratio = (max_count == 0) ? |
| 252 0.0 : static_cast<double>(static_call_counts[i]) / max_count; | 272 0.0 : static_cast<double>(static_call_counts[i]) / max_count; |
| 253 static_calls_[i + static_call_start_ix].ratio = ratio; | 273 static_calls_[i + static_call_start_ix].ratio = ratio; |
| 254 } | 274 } |
| 255 } | 275 } |
| 256 | 276 |
| 257 void FindCallSites(FlowGraph* graph, intptr_t depth) { | 277 void FindCallSites(FlowGraph* graph, |
| 278 intptr_t depth, | |
| 279 GrowableArray<InlinedInfo>* inlined_info) { | |
| 258 ASSERT(graph != NULL); | 280 ASSERT(graph != NULL); |
| 259 // If depth is less than the threshold recursively add call sites. | 281 |
| 260 if (depth > FLAG_inlining_depth_threshold) return; | 282 if (depth > FLAG_inlining_depth_threshold) return; |
| 261 | 283 |
| 262 // Recognized methods are not treated as normal calls. They don't have | 284 // Recognized methods are not treated as normal calls. They don't have |
| 263 // calls in themselves, so we keep adding those even when at the threshold. | 285 // calls in themselves, so we keep adding those even when at the threshold. |
| 264 const bool only_recognized_methods = | 286 const bool only_recognized_methods = |
|
Cutch
2014/03/26 20:26:37
Could this be renamed? It was confusing that you d
srdjan
2014/03/26 21:05:31
Renamed to inline_only_recognized_methods, added c
| |
| 265 (depth == FLAG_inlining_depth_threshold); | 287 (depth == FLAG_inlining_depth_threshold); |
| 266 | 288 |
| 267 const intptr_t instance_call_start_ix = instance_calls_.length(); | 289 const intptr_t instance_call_start_ix = instance_calls_.length(); |
| 268 const intptr_t static_call_start_ix = static_calls_.length(); | 290 const intptr_t static_call_start_ix = static_calls_.length(); |
| 269 for (BlockIterator block_it = graph->postorder_iterator(); | 291 for (BlockIterator block_it = graph->postorder_iterator(); |
| 270 !block_it.Done(); | 292 !block_it.Done(); |
| 271 block_it.Advance()) { | 293 block_it.Advance()) { |
| 272 for (ForwardInstructionIterator it(block_it.Current()); | 294 for (ForwardInstructionIterator it(block_it.Current()); |
| 273 !it.Done(); | 295 !it.Done(); |
| 274 it.Advance()) { | 296 it.Advance()) { |
| 275 Instruction* current = it.Current(); | 297 Instruction* current = it.Current(); |
| 276 if (only_recognized_methods) { | 298 if (current->IsPolymorphicInstanceCall()) { |
| 277 PolymorphicInstanceCallInstr* instance_call = | 299 PolymorphicInstanceCallInstr* instance_call = |
| 278 current->AsPolymorphicInstanceCall(); | 300 current->AsPolymorphicInstanceCall(); |
| 279 if ((instance_call != NULL) && instance_call->HasRecognizedTarget()) { | 301 if (!only_recognized_methods || |
| 302 instance_call->HasSingleRecognizedTarget()) { | |
| 280 instance_calls_.Add(InstanceCallInfo(instance_call, graph)); | 303 instance_calls_.Add(InstanceCallInfo(instance_call, graph)); |
| 304 } else { | |
| 305 if (FLAG_print_inlining_tree) { | |
| 306 const Function* caller = &graph->parsed_function().function(); | |
| 307 const Function* target = | |
| 308 &Function::ZoneHandle( | |
| 309 instance_call->ic_data().GetTargetAt(0)); | |
| 310 inlined_info->Add(InlinedInfo( | |
| 311 caller, target, depth, instance_call, "Too deep")); | |
| 312 } | |
| 281 } | 313 } |
| 282 continue; | 314 } else if (current->IsStaticCall()) { |
| 283 } | 315 StaticCallInstr* static_call = current->AsStaticCall(); |
| 284 // Collect all call sites (!only_recognized_methods). | 316 if (!only_recognized_methods || |
| 285 ClosureCallInstr* closure_call = current->AsClosureCall(); | 317 static_call->function().is_recognized()) { |
| 286 if (closure_call != NULL) { | 318 static_calls_.Add(StaticCallInfo(static_call, graph)); |
| 287 closure_calls_.Add(ClosureCallInfo(closure_call, graph)); | 319 } else { |
| 288 continue; | 320 if (FLAG_print_inlining_tree) { |
| 289 } | 321 const Function* caller = &graph->parsed_function().function(); |
| 290 StaticCallInstr* static_call = current->AsStaticCall(); | 322 const Function* target = &static_call->function(); |
| 291 if (static_call != NULL) { | 323 inlined_info->Add(InlinedInfo( |
| 292 static_calls_.Add(StaticCallInfo(static_call, graph)); | 324 caller, target, depth, static_call, "Too deep")); |
| 293 continue; | 325 } |
| 294 } | 326 } |
| 295 PolymorphicInstanceCallInstr* instance_call = | 327 } else if (current->IsClosureCall()) { |
| 296 current->AsPolymorphicInstanceCall(); | 328 if (!only_recognized_methods) { |
| 297 if (instance_call != NULL) { | 329 ClosureCallInstr* closure_call = current->AsClosureCall(); |
| 298 instance_calls_.Add(InstanceCallInfo(instance_call, graph)); | 330 closure_calls_.Add(ClosureCallInfo(closure_call, graph)); |
| 299 continue; | 331 } |
| 300 } | 332 } |
| 301 } | 333 } |
| 302 } | 334 } |
| 303 ComputeCallSiteRatio(static_call_start_ix, instance_call_start_ix); | 335 ComputeCallSiteRatio(static_call_start_ix, instance_call_start_ix); |
| 304 } | 336 } |
| 305 | 337 |
| 306 private: | 338 private: |
| 307 GrowableArray<StaticCallInfo> static_calls_; | 339 GrowableArray<StaticCallInfo> static_calls_; |
| 308 GrowableArray<ClosureCallInfo> closure_calls_; | 340 GrowableArray<ClosureCallInfo> closure_calls_; |
| 309 GrowableArray<InstanceCallInfo> instance_calls_; | 341 GrowableArray<InstanceCallInfo> instance_calls_; |
| 310 | 342 |
| 311 DISALLOW_COPY_AND_ASSIGN(CallSites); | 343 DISALLOW_COPY_AND_ASSIGN(CallSites); |
| 312 }; | 344 }; |
| 313 | 345 |
| 314 | 346 |
| 315 struct InlinedCallData { | 347 struct InlinedCallData { |
| 316 InlinedCallData(Definition* call, | 348 InlinedCallData(Definition* call, |
| 317 GrowableArray<Value*>* arguments, | 349 GrowableArray<Value*>* arguments, |
| 318 const Function& caller) | 350 const Function& caller) |
| 319 : call(call), | 351 : call(call), |
| 320 arguments(arguments), | 352 arguments(arguments), |
| 321 callee_graph(NULL), | 353 callee_graph(NULL), |
| 322 parameter_stubs(NULL), | 354 parameter_stubs(NULL), |
| 323 exit_collector(NULL), | 355 exit_collector(NULL), |
| 324 caller_(caller) { } | 356 caller(caller) { } |
| 325 | 357 |
| 326 Definition* call; | 358 Definition* call; |
| 327 GrowableArray<Value*>* arguments; | 359 GrowableArray<Value*>* arguments; |
| 328 FlowGraph* callee_graph; | 360 FlowGraph* callee_graph; |
| 329 ZoneGrowableArray<Definition*>* parameter_stubs; | 361 ZoneGrowableArray<Definition*>* parameter_stubs; |
| 330 InlineExitCollector* exit_collector; | 362 InlineExitCollector* exit_collector; |
| 331 const Function& caller_; | 363 const Function& caller; |
| 332 }; | |
| 333 | |
| 334 | |
| 335 // Structure for collecting inline data needed to print inlining tree. | |
| 336 struct InlinedInfo { | |
| 337 const Function* caller; | |
| 338 const Function* inlined; | |
| 339 intptr_t inlined_depth; | |
| 340 const Definition* call_instr; | |
| 341 InlinedInfo(const Function* caller_function, | |
| 342 const Function* inlined_function, | |
| 343 const intptr_t depth, | |
| 344 const Definition* call) | |
| 345 : caller(caller_function), | |
| 346 inlined(inlined_function), | |
| 347 inlined_depth(depth), | |
| 348 call_instr(call) {} | |
| 349 }; | 364 }; |
| 350 | 365 |
| 351 | 366 |
| 352 class CallSiteInliner; | 367 class CallSiteInliner; |
| 353 | 368 |
| 354 class PolymorphicInliner : public ValueObject { | 369 class PolymorphicInliner : public ValueObject { |
| 355 public: | 370 public: |
| 356 PolymorphicInliner(CallSiteInliner* owner, | 371 PolymorphicInliner(CallSiteInliner* owner, |
| 357 PolymorphicInstanceCallInstr* call, | 372 PolymorphicInstanceCallInstr* call, |
| 358 const Function& caller_function); | 373 const Function& caller_function); |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 430 FLAG_deoptimization_counter_inlining_threshold) { | 445 FLAG_deoptimization_counter_inlining_threshold) { |
| 431 return; | 446 return; |
| 432 } | 447 } |
| 433 // Create two call site collections to swap between. | 448 // Create two call site collections to swap between. |
| 434 CallSites sites1(caller_graph_); | 449 CallSites sites1(caller_graph_); |
| 435 CallSites sites2(caller_graph_); | 450 CallSites sites2(caller_graph_); |
| 436 CallSites* call_sites_temp = NULL; | 451 CallSites* call_sites_temp = NULL; |
| 437 collected_call_sites_ = &sites1; | 452 collected_call_sites_ = &sites1; |
| 438 inlining_call_sites_ = &sites2; | 453 inlining_call_sites_ = &sites2; |
| 439 // Collect initial call sites. | 454 // Collect initial call sites. |
| 440 collected_call_sites_->FindCallSites(caller_graph_, inlining_depth_); | 455 collected_call_sites_->FindCallSites(caller_graph_, |
| 456 inlining_depth_, | |
| 457 &inlined_info_); | |
| 441 while (collected_call_sites_->HasCalls()) { | 458 while (collected_call_sites_->HasCalls()) { |
| 442 TRACE_INLINING(OS::Print(" Depth %" Pd " ----------\n", | 459 TRACE_INLINING(OS::Print(" Depth %" Pd " ----------\n", |
| 443 inlining_depth_)); | 460 inlining_depth_)); |
| 444 // Swap collected and inlining arrays and clear the new collecting array. | 461 // Swap collected and inlining arrays and clear the new collecting array. |
| 445 call_sites_temp = collected_call_sites_; | 462 call_sites_temp = collected_call_sites_; |
| 446 collected_call_sites_ = inlining_call_sites_; | 463 collected_call_sites_ = inlining_call_sites_; |
| 447 inlining_call_sites_ = call_sites_temp; | 464 inlining_call_sites_ = call_sites_temp; |
| 448 collected_call_sites_->Clear(); | 465 collected_call_sites_->Clear(); |
| 449 // Inline call sites at the current depth. | 466 // Inline call sites at the current depth. |
| 450 InlineStaticCalls(); | 467 InlineStaticCalls(); |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 468 const Array& argument_names, | 485 const Array& argument_names, |
| 469 InlinedCallData* call_data) { | 486 InlinedCallData* call_data) { |
| 470 TRACE_INLINING(OS::Print(" => %s (deopt count %d)\n", | 487 TRACE_INLINING(OS::Print(" => %s (deopt count %d)\n", |
| 471 function.ToCString(), | 488 function.ToCString(), |
| 472 function.deoptimization_counter())); | 489 function.deoptimization_counter())); |
| 473 | 490 |
| 474 // TODO(fschneider): Enable inlining inside try-blocks. | 491 // TODO(fschneider): Enable inlining inside try-blocks. |
| 475 if (call_data->call->GetBlock()->try_index() != | 492 if (call_data->call->GetBlock()->try_index() != |
| 476 CatchClauseNode::kInvalidTryIndex) { | 493 CatchClauseNode::kInvalidTryIndex) { |
| 477 TRACE_INLINING(OS::Print(" Bailout: inside try-block\n")); | 494 TRACE_INLINING(OS::Print(" Bailout: inside try-block\n")); |
| 495 if (FLAG_print_inlining_tree) { | |
| 496 inlined_info_.Add(InlinedInfo( | |
| 497 &call_data->caller, &function, inlining_depth_, call_data->call, | |
| 498 "Inside try-block")); | |
| 499 } | |
| 478 return false; | 500 return false; |
| 479 } | 501 } |
| 480 | 502 |
| 481 // Make a handle for the unoptimized code so that it is not disconnected | 503 // Make a handle for the unoptimized code so that it is not disconnected |
| 482 // from the function while we are trying to inline it. | 504 // from the function while we are trying to inline it. |
| 483 const Code& unoptimized_code = Code::Handle(function.unoptimized_code()); | 505 const Code& unoptimized_code = Code::Handle(function.unoptimized_code()); |
| 484 // Abort if the inlinable bit on the function is low. | 506 // Abort if the inlinable bit on the function is low. |
| 485 if (!function.IsInlineable()) { | 507 if (!function.IsInlineable()) { |
| 486 TRACE_INLINING(OS::Print(" Bailout: not inlinable\n")); | 508 TRACE_INLINING(OS::Print(" Bailout: not inlinable\n")); |
| 509 if (FLAG_print_inlining_tree) { | |
| 510 inlined_info_.Add(InlinedInfo( | |
| 511 &call_data->caller, &function, inlining_depth_, call_data->call, | |
| 512 "Not inlinable")); | |
| 513 } | |
| 487 return false; | 514 return false; |
| 488 } | 515 } |
| 489 | 516 |
| 490 // Abort if this function has deoptimized too much. | 517 // Abort if this function has deoptimized too much. |
| 491 if (function.deoptimization_counter() >= | 518 if (function.deoptimization_counter() >= |
| 492 FLAG_deoptimization_counter_threshold) { | 519 FLAG_deoptimization_counter_threshold) { |
| 493 function.set_is_inlinable(false); | 520 function.set_is_inlinable(false); |
| 494 TRACE_INLINING(OS::Print(" Bailout: deoptimization threshold\n")); | 521 TRACE_INLINING(OS::Print(" Bailout: deoptimization threshold\n")); |
| 522 if (FLAG_print_inlining_tree) { | |
| 523 inlined_info_.Add(InlinedInfo( | |
| 524 &call_data->caller, &function, inlining_depth_, call_data->call, | |
| 525 "Deoptimization threshold exceeded")); | |
| 526 } | |
| 495 return false; | 527 return false; |
| 496 } | 528 } |
| 497 | 529 |
| 498 GrowableArray<Value*>* arguments = call_data->arguments; | 530 GrowableArray<Value*>* arguments = call_data->arguments; |
| 499 const intptr_t constant_arguments = CountConstants(*arguments); | 531 const intptr_t constant_arguments = CountConstants(*arguments); |
| 500 if (!ShouldWeInline(function, | 532 if (!ShouldWeInline(function, |
| 501 function.optimized_instruction_count(), | 533 function.optimized_instruction_count(), |
| 502 function.optimized_call_site_count(), | 534 function.optimized_call_site_count(), |
| 503 constant_arguments)) { | 535 constant_arguments)) { |
| 504 TRACE_INLINING(OS::Print(" Bailout: early heuristics with " | 536 TRACE_INLINING(OS::Print(" Bailout: early heuristics with " |
| 505 "code size: %" Pd ", " | 537 "code size: %" Pd ", " |
| 506 "call sites: %" Pd ", " | 538 "call sites: %" Pd ", " |
| 507 "const args: %" Pd "\n", | 539 "const args: %" Pd "\n", |
| 508 function.optimized_instruction_count(), | 540 function.optimized_instruction_count(), |
| 509 function.optimized_call_site_count(), | 541 function.optimized_call_site_count(), |
| 510 constant_arguments)); | 542 constant_arguments)); |
| 543 if (FLAG_print_inlining_tree) { | |
| 544 inlined_info_.Add(InlinedInfo( | |
| 545 &call_data->caller, &function, inlining_depth_, call_data->call, | |
| 546 "Early heuristic")); | |
| 547 } | |
| 511 return false; | 548 return false; |
| 512 } | 549 } |
| 513 | 550 |
| 514 // Abort if this is a recursive occurrence. | 551 // Abort if this is a recursive occurrence. |
| 515 Definition* call = call_data->call; | 552 Definition* call = call_data->call; |
| 516 if (!FLAG_inline_recursive && IsCallRecursive(unoptimized_code, call)) { | 553 if (!FLAG_inline_recursive && IsCallRecursive(unoptimized_code, call)) { |
| 517 function.set_is_inlinable(false); | 554 function.set_is_inlinable(false); |
| 518 TRACE_INLINING(OS::Print(" Bailout: recursive function\n")); | 555 TRACE_INLINING(OS::Print(" Bailout: recursive function\n")); |
| 519 return false; | 556 return false; |
| 520 } | 557 } |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 627 FlowGraphPrinter printer(*callee_graph); | 664 FlowGraphPrinter printer(*callee_graph); |
| 628 printer.PrintBlocks(); | 665 printer.PrintBlocks(); |
| 629 } | 666 } |
| 630 | 667 |
| 631 // Collect information about the call site and caller graph. | 668 // Collect information about the call site and caller graph. |
| 632 // TODO(zerny): Do this after CP and dead code elimination. | 669 // TODO(zerny): Do this after CP and dead code elimination. |
| 633 intptr_t constants_count = 0; | 670 intptr_t constants_count = 0; |
| 634 for (intptr_t i = 0; i < param_stubs->length(); ++i) { | 671 for (intptr_t i = 0; i < param_stubs->length(); ++i) { |
| 635 if ((*param_stubs)[i]->IsConstant()) ++constants_count; | 672 if ((*param_stubs)[i]->IsConstant()) ++constants_count; |
| 636 } | 673 } |
| 637 GraphInfoCollector info; | 674 |
| 638 info.Collect(*callee_graph); | 675 FlowGraphInliner::CollectGraphInfo(callee_graph); |
| 639 const intptr_t size = info.instruction_count(); | 676 const intptr_t size = function.optimized_instruction_count(); |
| 640 const intptr_t call_site_count = info.call_site_count(); | 677 const intptr_t call_site_count = function.optimized_call_site_count(); |
| 641 | 678 |
| 642 function.set_optimized_instruction_count(size); | 679 function.set_optimized_instruction_count(size); |
| 643 function.set_optimized_call_site_count(call_site_count); | 680 function.set_optimized_call_site_count(call_site_count); |
| 644 | 681 |
| 645 // Use heuristics do decide if this call should be inlined. | 682 // Use heuristics do decide if this call should be inlined. |
| 646 if (!ShouldWeInline(function, size, call_site_count, constants_count)) { | 683 if (!ShouldWeInline(function, size, call_site_count, constants_count)) { |
| 647 // If size is larger than all thresholds, don't consider it again. | 684 // If size is larger than all thresholds, don't consider it again. |
| 648 if ((size > FLAG_inlining_size_threshold) && | 685 if ((size > FLAG_inlining_size_threshold) && |
| 649 (call_site_count > FLAG_inlining_callee_call_sites_threshold) && | 686 (call_site_count > FLAG_inlining_callee_call_sites_threshold) && |
| 650 (size > FLAG_inlining_constant_arguments_size_threshold)) { | 687 (size > FLAG_inlining_constant_arguments_size_threshold)) { |
| 651 function.set_is_inlinable(false); | 688 function.set_is_inlinable(false); |
| 652 } | 689 } |
| 653 isolate->set_deopt_id(prev_deopt_id); | 690 isolate->set_deopt_id(prev_deopt_id); |
| 654 TRACE_INLINING(OS::Print(" Bailout: heuristics with " | 691 TRACE_INLINING(OS::Print(" Bailout: heuristics with " |
| 655 "code size: %" Pd ", " | 692 "code size: %" Pd ", " |
| 656 "call sites: %" Pd ", " | 693 "call sites: %" Pd ", " |
| 657 "const args: %" Pd "\n", | 694 "const args: %" Pd "\n", |
| 658 size, | 695 size, |
| 659 call_site_count, | 696 call_site_count, |
| 660 constants_count)); | 697 constants_count)); |
| 698 if (FLAG_print_inlining_tree) { | |
| 699 inlined_info_.Add(InlinedInfo( | |
| 700 &call_data->caller, &function, inlining_depth_, call_data->call, | |
| 701 "Heuristic fail")); | |
| 702 } | |
| 661 return false; | 703 return false; |
| 662 } | 704 } |
| 663 | 705 |
| 664 collected_call_sites_->FindCallSites(callee_graph, inlining_depth_); | 706 collected_call_sites_->FindCallSites(callee_graph, |
| 707 inlining_depth_, | |
| 708 &inlined_info_); | |
| 665 | 709 |
| 666 // Add the function to the cache. | 710 // Add the function to the cache. |
| 667 if (!in_cache) { | 711 if (!in_cache) { |
| 668 function_cache_.Add(parsed_function); | 712 function_cache_.Add(parsed_function); |
| 669 } | 713 } |
| 670 | 714 |
| 671 // Build succeeded so we restore the bailout jump. | 715 // Build succeeded so we restore the bailout jump. |
| 672 inlined_ = true; | 716 inlined_ = true; |
| 673 inlined_size_ += size; | 717 inlined_size_ += size; |
| 674 isolate->set_deopt_id(prev_deopt_id); | 718 isolate->set_deopt_id(prev_deopt_id); |
| 675 | 719 |
| 676 call_data->callee_graph = callee_graph; | 720 call_data->callee_graph = callee_graph; |
| 677 call_data->parameter_stubs = param_stubs; | 721 call_data->parameter_stubs = param_stubs; |
| 678 call_data->exit_collector = exit_collector; | 722 call_data->exit_collector = exit_collector; |
| 679 | 723 |
| 680 // When inlined, we add the guarded fields of the callee to the caller's | 724 // When inlined, we add the guarded fields of the callee to the caller's |
| 681 // list of guarded fields. | 725 // list of guarded fields. |
| 682 for (intptr_t i = 0; i < callee_graph->guarded_fields()->length(); ++i) { | 726 for (intptr_t i = 0; i < callee_graph->guarded_fields()->length(); ++i) { |
| 683 FlowGraph::AddToGuardedFields(caller_graph_->guarded_fields(), | 727 FlowGraph::AddToGuardedFields(caller_graph_->guarded_fields(), |
| 684 (*callee_graph->guarded_fields())[i]); | 728 (*callee_graph->guarded_fields())[i]); |
| 685 } | 729 } |
| 686 | 730 |
| 687 // We allocate a ZoneHandle for the unoptimized code so that it cannot be | 731 // We allocate a ZoneHandle for the unoptimized code so that it cannot be |
| 688 // disconnected from its function during the rest of compilation. | 732 // disconnected from its function during the rest of compilation. |
| 689 Code::ZoneHandle(unoptimized_code.raw()); | 733 Code::ZoneHandle(unoptimized_code.raw()); |
| 690 TRACE_INLINING(OS::Print(" Success\n")); | 734 TRACE_INLINING(OS::Print(" Success\n")); |
| 691 if (FLAG_print_inline_tree) { | 735 if (FLAG_print_inlining_tree) { |
| 692 inlined_info_.Add( | 736 inlined_info_.Add( |
| 693 InlinedInfo(&call_data->caller_, &function, inlining_depth_, call)); | 737 InlinedInfo(&call_data->caller, &function, inlining_depth_, call)); |
| 694 } | 738 } |
| 695 return true; | 739 return true; |
| 696 } else { | 740 } else { |
| 697 Error& error = Error::Handle(); | 741 Error& error = Error::Handle(); |
| 698 error = isolate->object_store()->sticky_error(); | 742 error = isolate->object_store()->sticky_error(); |
| 699 isolate->object_store()->clear_sticky_error(); | 743 isolate->object_store()->clear_sticky_error(); |
| 700 isolate->set_deopt_id(prev_deopt_id); | 744 isolate->set_deopt_id(prev_deopt_id); |
| 701 TRACE_INLINING(OS::Print(" Bailout: %s\n", error.ToErrorCString())); | 745 TRACE_INLINING(OS::Print(" Bailout: %s\n", error.ToErrorCString())); |
| 702 return false; | 746 return false; |
| 703 } | 747 } |
| 704 } | 748 } |
| 705 | 749 |
| 706 void PrintInlinedInfo(const Function& top) { | 750 void PrintInlinedInfo(const Function& top) { |
| 707 OS::Print("Inlining into: %s\n", top.ToFullyQualifiedCString()); | 751 if (inlined_info_.length() > 0) { |
| 708 PrintInlinedInfoFor(top, 1); | 752 OS::Print("Inlining into: '%s' growth: %f (%"Pd" -> %"Pd")\n", |
| 753 top.ToFullyQualifiedCString(), | |
| 754 GrowthFactor(), | |
| 755 initial_size_, | |
| 756 inlined_size_); | |
| 757 PrintInlinedInfoFor(top, 1); | |
| 758 } | |
| 709 } | 759 } |
| 710 | 760 |
| 711 private: | 761 private: |
| 712 friend class PolymorphicInliner; | 762 friend class PolymorphicInliner; |
| 713 | 763 |
| 714 void PrintInlinedInfoFor(const Function& caller, intptr_t depth) { | 764 void PrintInlinedInfoFor(const Function& caller, intptr_t depth) { |
| 765 // Print those that were inlined. | |
| 715 for (intptr_t i = 0; i < inlined_info_.length(); i++) { | 766 for (intptr_t i = 0; i < inlined_info_.length(); i++) { |
| 716 const InlinedInfo& info = inlined_info_[i]; | 767 const InlinedInfo& info = inlined_info_[i]; |
| 768 if (info.bailout_reason != NULL) continue; | |
| 717 if ((info.inlined_depth == depth) && | 769 if ((info.inlined_depth == depth) && |
| 718 (info.caller->raw() == caller.raw())) { | 770 (info.caller->raw() == caller.raw())) { |
| 719 for (int t = 0; t < depth; t++) { | 771 for (int t = 0; t < depth; t++) { |
| 720 OS::Print(" "); | 772 OS::Print(" "); |
| 721 } | 773 } |
| 722 OS::Print("%" Pd " %s\n", | 774 OS::Print("%" Pd " %s\n", |
| 723 info.call_instr->GetDeoptId(), | 775 info.call_instr->GetDeoptId(), |
| 724 info.inlined->ToQualifiedCString()); | 776 info.inlined->ToQualifiedCString()); |
| 725 PrintInlinedInfoFor(*info.inlined, depth + 1); | 777 PrintInlinedInfoFor(*info.inlined, depth + 1); |
| 726 } | 778 } |
| 727 } | 779 } |
| 780 // Print those that were not inlined. | |
| 781 for (intptr_t i = 0; i < inlined_info_.length(); i++) { | |
| 782 const InlinedInfo& info = inlined_info_[i]; | |
| 783 if (info.bailout_reason == NULL) continue; | |
| 784 if ((info.inlined_depth == depth) && | |
| 785 (info.caller->raw() == caller.raw())) { | |
| 786 for (int t = 0; t < depth; t++) { | |
| 787 OS::Print(" "); | |
| 788 } | |
| 789 OS::Print("NO %" Pd " %s - %s\n", | |
| 790 info.call_instr->GetDeoptId(), | |
| 791 info.inlined->ToQualifiedCString(), | |
| 792 info.bailout_reason); | |
| 793 } | |
| 794 } | |
| 728 } | 795 } |
| 729 | 796 |
| 730 void InlineCall(InlinedCallData* call_data) { | 797 void InlineCall(InlinedCallData* call_data) { |
| 731 TimerScope timer(FLAG_compiler_stats, | 798 TimerScope timer(FLAG_compiler_stats, |
| 732 &CompilerStats::graphinliner_subst_timer, | 799 &CompilerStats::graphinliner_subst_timer, |
| 733 Isolate::Current()); | 800 Isolate::Current()); |
| 734 | 801 |
| 735 // For closure calls: Store context value. | 802 // For closure calls: Store context value. |
| 736 FlowGraph* callee_graph = call_data->callee_graph; | 803 FlowGraph* callee_graph = call_data->callee_graph; |
| 737 TargetEntryInstr* callee_entry = | 804 TargetEntryInstr* callee_entry = |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 836 } | 903 } |
| 837 } | 904 } |
| 838 const Function& target = call->function(); | 905 const Function& target = call->function(); |
| 839 if (!FlowGraphInliner::AlwaysInline(target) && | 906 if (!FlowGraphInliner::AlwaysInline(target) && |
| 840 (call_info[call_idx].ratio * 100) < FLAG_inlining_hotness) { | 907 (call_info[call_idx].ratio * 100) < FLAG_inlining_hotness) { |
| 841 TRACE_INLINING(OS::Print( | 908 TRACE_INLINING(OS::Print( |
| 842 " => %s (deopt count %d)\n Bailout: cold %f\n", | 909 " => %s (deopt count %d)\n Bailout: cold %f\n", |
| 843 target.ToCString(), | 910 target.ToCString(), |
| 844 target.deoptimization_counter(), | 911 target.deoptimization_counter(), |
| 845 call_info[call_idx].ratio)); | 912 call_info[call_idx].ratio)); |
| 913 if (FLAG_print_inlining_tree) { | |
| 914 inlined_info_.Add(InlinedInfo( | |
| 915 call_info[call_idx].caller, | |
| 916 &call->function(), | |
| 917 inlining_depth_, | |
| 918 call, | |
| 919 "Too cold")); | |
| 920 } | |
| 846 continue; | 921 continue; |
| 847 } | 922 } |
| 848 GrowableArray<Value*> arguments(call->ArgumentCount()); | 923 GrowableArray<Value*> arguments(call->ArgumentCount()); |
| 849 for (int i = 0; i < call->ArgumentCount(); ++i) { | 924 for (int i = 0; i < call->ArgumentCount(); ++i) { |
| 850 arguments.Add(call->PushArgumentAt(i)->value()); | 925 arguments.Add(call->PushArgumentAt(i)->value()); |
| 851 } | 926 } |
| 852 InlinedCallData call_data(call, &arguments, *call_info[call_idx].caller); | 927 InlinedCallData call_data(call, &arguments, *call_info[call_idx].caller); |
| 853 if (TryInlining(call->function(), call->argument_names(), &call_data)) { | 928 if (TryInlining(call->function(), call->argument_names(), &call_data)) { |
| 854 InlineCall(&call_data); | 929 InlineCall(&call_data); |
| 855 } | 930 } |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 905 | 980 |
| 906 const ICData& ic_data = call->ic_data(); | 981 const ICData& ic_data = call->ic_data(); |
| 907 const Function& target = Function::ZoneHandle(ic_data.GetTargetAt(0)); | 982 const Function& target = Function::ZoneHandle(ic_data.GetTargetAt(0)); |
| 908 if (!FlowGraphInliner::AlwaysInline(target) && | 983 if (!FlowGraphInliner::AlwaysInline(target) && |
| 909 (call_info[call_idx].ratio * 100) < FLAG_inlining_hotness) { | 984 (call_info[call_idx].ratio * 100) < FLAG_inlining_hotness) { |
| 910 TRACE_INLINING(OS::Print( | 985 TRACE_INLINING(OS::Print( |
| 911 " => %s (deopt count %d)\n Bailout: cold %f\n", | 986 " => %s (deopt count %d)\n Bailout: cold %f\n", |
| 912 target.ToCString(), | 987 target.ToCString(), |
| 913 target.deoptimization_counter(), | 988 target.deoptimization_counter(), |
| 914 call_info[call_idx].ratio)); | 989 call_info[call_idx].ratio)); |
| 990 if (FLAG_print_inlining_tree) { | |
| 991 inlined_info_.Add(InlinedInfo( | |
| 992 call_info[call_idx].caller, | |
| 993 &target, | |
| 994 inlining_depth_, | |
| 995 call, | |
| 996 "Too cold")); | |
| 997 } | |
| 915 continue; | 998 continue; |
| 916 } | 999 } |
| 917 GrowableArray<Value*> arguments(call->ArgumentCount()); | 1000 GrowableArray<Value*> arguments(call->ArgumentCount()); |
| 918 for (int arg_i = 0; arg_i < call->ArgumentCount(); ++arg_i) { | 1001 for (int arg_i = 0; arg_i < call->ArgumentCount(); ++arg_i) { |
| 919 arguments.Add(call->PushArgumentAt(arg_i)->value()); | 1002 arguments.Add(call->PushArgumentAt(arg_i)->value()); |
| 920 } | 1003 } |
| 921 InlinedCallData call_data(call, &arguments, *call_info[call_idx].caller); | 1004 InlinedCallData call_data(call, &arguments, *call_info[call_idx].caller); |
| 922 if (TryInlining(target, | 1005 if (TryInlining(target, |
| 923 call->instance_call()->argument_names(), | 1006 call->instance_call()->argument_names(), |
| 924 &call_data)) { | 1007 &call_data)) { |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1014 } else { | 1097 } else { |
| 1015 param_stubs->Add( | 1098 param_stubs->Add( |
| 1016 GetDefaultValue(i - fixed_param_count, parsed_function)); | 1099 GetDefaultValue(i - fixed_param_count, parsed_function)); |
| 1017 } | 1100 } |
| 1018 } | 1101 } |
| 1019 return argument_names_count == match_count; | 1102 return argument_names_count == match_count; |
| 1020 } | 1103 } |
| 1021 | 1104 |
| 1022 FlowGraph* caller_graph_; | 1105 FlowGraph* caller_graph_; |
| 1023 bool inlined_; | 1106 bool inlined_; |
| 1024 intptr_t initial_size_; | 1107 const intptr_t initial_size_; |
| 1025 intptr_t inlined_size_; | 1108 intptr_t inlined_size_; |
| 1026 intptr_t inlining_depth_; | 1109 intptr_t inlining_depth_; |
| 1027 CallSites* collected_call_sites_; | 1110 CallSites* collected_call_sites_; |
| 1028 CallSites* inlining_call_sites_; | 1111 CallSites* inlining_call_sites_; |
| 1029 GrowableArray<ParsedFunction*> function_cache_; | 1112 GrowableArray<ParsedFunction*> function_cache_; |
| 1030 GrowableArray<InlinedInfo> inlined_info_; | 1113 GrowableArray<InlinedInfo> inlined_info_; |
| 1031 | 1114 |
| 1032 DISALLOW_COPY_AND_ASSIGN(CallSiteInliner); | 1115 DISALLOW_COPY_AND_ASSIGN(CallSiteInliner); |
| 1033 }; | 1116 }; |
| 1034 | 1117 |
| (...skipping 454 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1489 TargetEntryInstr* entry = BuildDecisionGraph(); | 1572 TargetEntryInstr* entry = BuildDecisionGraph(); |
| 1490 exit_collector_->ReplaceCall(entry); | 1573 exit_collector_->ReplaceCall(entry); |
| 1491 } | 1574 } |
| 1492 | 1575 |
| 1493 | 1576 |
| 1494 static uint16_t ClampUint16(intptr_t v) { | 1577 static uint16_t ClampUint16(intptr_t v) { |
| 1495 return (v > 0xFFFF) ? 0xFFFF : static_cast<uint16_t>(v); | 1578 return (v > 0xFFFF) ? 0xFFFF : static_cast<uint16_t>(v); |
| 1496 } | 1579 } |
| 1497 | 1580 |
| 1498 | 1581 |
| 1499 void FlowGraphInliner::CollectGraphInfo(FlowGraph* flow_graph) { | 1582 void FlowGraphInliner::CollectGraphInfo(FlowGraph* flow_graph, bool force) { |
| 1500 GraphInfoCollector info; | |
| 1501 info.Collect(*flow_graph); | |
| 1502 const Function& function = flow_graph->parsed_function().function(); | 1583 const Function& function = flow_graph->parsed_function().function(); |
| 1503 function.set_optimized_instruction_count( | 1584 if (force || (function.optimized_instruction_count() == 0)) { |
| 1504 ClampUint16(info.instruction_count())); | 1585 GraphInfoCollector info; |
| 1505 function.set_optimized_call_site_count(ClampUint16(info.call_site_count())); | 1586 info.Collect(*flow_graph); |
| 1587 | |
| 1588 function.set_optimized_instruction_count( | |
| 1589 ClampUint16(info.instruction_count())); | |
| 1590 function.set_optimized_call_site_count(ClampUint16(info.call_site_count())); | |
| 1591 } | |
| 1506 } | 1592 } |
| 1507 | 1593 |
| 1508 | 1594 |
| 1509 bool FlowGraphInliner::AlwaysInline(const Function& function) { | 1595 bool FlowGraphInliner::AlwaysInline(const Function& function) { |
| 1510 if (function.IsImplicitGetterFunction() || function.IsGetterFunction() || | 1596 if (function.IsImplicitGetterFunction() || function.IsGetterFunction() || |
| 1511 function.IsImplicitSetterFunction() || function.IsSetterFunction()) { | 1597 function.IsImplicitSetterFunction() || function.IsSetterFunction()) { |
| 1512 const intptr_t count = function.optimized_instruction_count(); | 1598 const intptr_t count = function.optimized_instruction_count(); |
| 1513 if ((count != 0) && (count < FLAG_inline_getters_setters_smaller_than)) { | 1599 if ((count != 0) && (count < FLAG_inline_getters_setters_smaller_than)) { |
| 1514 return true; | 1600 return true; |
| 1515 } | 1601 } |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 1534 if (FLAG_trace_inlining && | 1620 if (FLAG_trace_inlining && |
| 1535 (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized)) { | 1621 (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized)) { |
| 1536 OS::Print("Before Inlining of %s\n", flow_graph_-> | 1622 OS::Print("Before Inlining of %s\n", flow_graph_-> |
| 1537 parsed_function().function().ToFullyQualifiedCString()); | 1623 parsed_function().function().ToFullyQualifiedCString()); |
| 1538 FlowGraphPrinter printer(*flow_graph_); | 1624 FlowGraphPrinter printer(*flow_graph_); |
| 1539 printer.PrintBlocks(); | 1625 printer.PrintBlocks(); |
| 1540 } | 1626 } |
| 1541 | 1627 |
| 1542 CallSiteInliner inliner(flow_graph_); | 1628 CallSiteInliner inliner(flow_graph_); |
| 1543 inliner.InlineCalls(); | 1629 inliner.InlineCalls(); |
| 1544 if (FLAG_print_inline_tree) { | 1630 if (FLAG_print_inlining_tree) { |
| 1545 inliner.PrintInlinedInfo(top); | 1631 inliner.PrintInlinedInfo(top); |
| 1546 } | 1632 } |
| 1547 | 1633 |
| 1548 if (inliner.inlined()) { | 1634 if (inliner.inlined()) { |
| 1549 flow_graph_->DiscoverBlocks(); | 1635 flow_graph_->DiscoverBlocks(); |
| 1550 if (FLAG_trace_inlining) { | 1636 if (FLAG_trace_inlining) { |
| 1551 OS::Print("Inlining growth factor: %f\n", inliner.GrowthFactor()); | 1637 OS::Print("Inlining growth factor: %f\n", inliner.GrowthFactor()); |
| 1552 if (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized) { | 1638 if (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized) { |
| 1553 OS::Print("After Inlining of %s\n", flow_graph_-> | 1639 OS::Print("After Inlining of %s\n", flow_graph_-> |
| 1554 parsed_function().function().ToFullyQualifiedCString()); | 1640 parsed_function().function().ToFullyQualifiedCString()); |
| 1555 FlowGraphPrinter printer(*flow_graph_); | 1641 FlowGraphPrinter printer(*flow_graph_); |
| 1556 printer.PrintBlocks(); | 1642 printer.PrintBlocks(); |
| 1557 } | 1643 } |
| 1558 } | 1644 } |
| 1559 } | 1645 } |
| 1560 } | 1646 } |
| 1561 | 1647 |
| 1562 } // namespace dart | 1648 } // namespace dart |
| OLD | NEW |