| 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 inline_only_recognized_methods = |
| 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 (!inline_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 // Method not inlined because inlining too deep and method |
| 306 // not recognized. |
| 307 if (FLAG_print_inlining_tree) { |
| 308 const Function* caller = &graph->parsed_function().function(); |
| 309 const Function* target = |
| 310 &Function::ZoneHandle( |
| 311 instance_call->ic_data().GetTargetAt(0)); |
| 312 inlined_info->Add(InlinedInfo( |
| 313 caller, target, depth, instance_call, "Too deep")); |
| 314 } |
| 281 } | 315 } |
| 282 continue; | 316 } else if (current->IsStaticCall()) { |
| 283 } | 317 StaticCallInstr* static_call = current->AsStaticCall(); |
| 284 // Collect all call sites (!only_recognized_methods). | 318 if (!inline_only_recognized_methods || |
| 285 ClosureCallInstr* closure_call = current->AsClosureCall(); | 319 static_call->function().is_recognized()) { |
| 286 if (closure_call != NULL) { | 320 static_calls_.Add(StaticCallInfo(static_call, graph)); |
| 287 closure_calls_.Add(ClosureCallInfo(closure_call, graph)); | 321 } else { |
| 288 continue; | 322 // Method not inlined because inlining too deep and method |
| 289 } | 323 // not recognized. |
| 290 StaticCallInstr* static_call = current->AsStaticCall(); | 324 if (FLAG_print_inlining_tree) { |
| 291 if (static_call != NULL) { | 325 const Function* caller = &graph->parsed_function().function(); |
| 292 static_calls_.Add(StaticCallInfo(static_call, graph)); | 326 const Function* target = &static_call->function(); |
| 293 continue; | 327 inlined_info->Add(InlinedInfo( |
| 294 } | 328 caller, target, depth, static_call, "Too deep")); |
| 295 PolymorphicInstanceCallInstr* instance_call = | 329 } |
| 296 current->AsPolymorphicInstanceCall(); | 330 } |
| 297 if (instance_call != NULL) { | 331 } else if (current->IsClosureCall()) { |
| 298 instance_calls_.Add(InstanceCallInfo(instance_call, graph)); | 332 if (!inline_only_recognized_methods) { |
| 299 continue; | 333 ClosureCallInstr* closure_call = current->AsClosureCall(); |
| 334 closure_calls_.Add(ClosureCallInfo(closure_call, graph)); |
| 335 } |
| 300 } | 336 } |
| 301 } | 337 } |
| 302 } | 338 } |
| 303 ComputeCallSiteRatio(static_call_start_ix, instance_call_start_ix); | 339 ComputeCallSiteRatio(static_call_start_ix, instance_call_start_ix); |
| 304 } | 340 } |
| 305 | 341 |
| 306 private: | 342 private: |
| 307 GrowableArray<StaticCallInfo> static_calls_; | 343 GrowableArray<StaticCallInfo> static_calls_; |
| 308 GrowableArray<ClosureCallInfo> closure_calls_; | 344 GrowableArray<ClosureCallInfo> closure_calls_; |
| 309 GrowableArray<InstanceCallInfo> instance_calls_; | 345 GrowableArray<InstanceCallInfo> instance_calls_; |
| 310 | 346 |
| 311 DISALLOW_COPY_AND_ASSIGN(CallSites); | 347 DISALLOW_COPY_AND_ASSIGN(CallSites); |
| 312 }; | 348 }; |
| 313 | 349 |
| 314 | 350 |
| 315 struct InlinedCallData { | 351 struct InlinedCallData { |
| 316 InlinedCallData(Definition* call, | 352 InlinedCallData(Definition* call, |
| 317 GrowableArray<Value*>* arguments, | 353 GrowableArray<Value*>* arguments, |
| 318 const Function& caller) | 354 const Function& caller) |
| 319 : call(call), | 355 : call(call), |
| 320 arguments(arguments), | 356 arguments(arguments), |
| 321 callee_graph(NULL), | 357 callee_graph(NULL), |
| 322 parameter_stubs(NULL), | 358 parameter_stubs(NULL), |
| 323 exit_collector(NULL), | 359 exit_collector(NULL), |
| 324 caller_(caller) { } | 360 caller(caller) { } |
| 325 | 361 |
| 326 Definition* call; | 362 Definition* call; |
| 327 GrowableArray<Value*>* arguments; | 363 GrowableArray<Value*>* arguments; |
| 328 FlowGraph* callee_graph; | 364 FlowGraph* callee_graph; |
| 329 ZoneGrowableArray<Definition*>* parameter_stubs; | 365 ZoneGrowableArray<Definition*>* parameter_stubs; |
| 330 InlineExitCollector* exit_collector; | 366 InlineExitCollector* exit_collector; |
| 331 const Function& caller_; | 367 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 }; | 368 }; |
| 350 | 369 |
| 351 | 370 |
| 352 class CallSiteInliner; | 371 class CallSiteInliner; |
| 353 | 372 |
| 354 class PolymorphicInliner : public ValueObject { | 373 class PolymorphicInliner : public ValueObject { |
| 355 public: | 374 public: |
| 356 PolymorphicInliner(CallSiteInliner* owner, | 375 PolymorphicInliner(CallSiteInliner* owner, |
| 357 PolymorphicInstanceCallInstr* call, | 376 PolymorphicInstanceCallInstr* call, |
| 358 const Function& caller_function); | 377 const Function& caller_function); |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 430 FLAG_deoptimization_counter_inlining_threshold) { | 449 FLAG_deoptimization_counter_inlining_threshold) { |
| 431 return; | 450 return; |
| 432 } | 451 } |
| 433 // Create two call site collections to swap between. | 452 // Create two call site collections to swap between. |
| 434 CallSites sites1(caller_graph_); | 453 CallSites sites1(caller_graph_); |
| 435 CallSites sites2(caller_graph_); | 454 CallSites sites2(caller_graph_); |
| 436 CallSites* call_sites_temp = NULL; | 455 CallSites* call_sites_temp = NULL; |
| 437 collected_call_sites_ = &sites1; | 456 collected_call_sites_ = &sites1; |
| 438 inlining_call_sites_ = &sites2; | 457 inlining_call_sites_ = &sites2; |
| 439 // Collect initial call sites. | 458 // Collect initial call sites. |
| 440 collected_call_sites_->FindCallSites(caller_graph_, inlining_depth_); | 459 collected_call_sites_->FindCallSites(caller_graph_, |
| 460 inlining_depth_, |
| 461 &inlined_info_); |
| 441 while (collected_call_sites_->HasCalls()) { | 462 while (collected_call_sites_->HasCalls()) { |
| 442 TRACE_INLINING(OS::Print(" Depth %" Pd " ----------\n", | 463 TRACE_INLINING(OS::Print(" Depth %" Pd " ----------\n", |
| 443 inlining_depth_)); | 464 inlining_depth_)); |
| 444 // Swap collected and inlining arrays and clear the new collecting array. | 465 // Swap collected and inlining arrays and clear the new collecting array. |
| 445 call_sites_temp = collected_call_sites_; | 466 call_sites_temp = collected_call_sites_; |
| 446 collected_call_sites_ = inlining_call_sites_; | 467 collected_call_sites_ = inlining_call_sites_; |
| 447 inlining_call_sites_ = call_sites_temp; | 468 inlining_call_sites_ = call_sites_temp; |
| 448 collected_call_sites_->Clear(); | 469 collected_call_sites_->Clear(); |
| 449 // Inline call sites at the current depth. | 470 // Inline call sites at the current depth. |
| 450 InlineStaticCalls(); | 471 InlineStaticCalls(); |
| (...skipping 17 matching lines...) Expand all Loading... |
| 468 const Array& argument_names, | 489 const Array& argument_names, |
| 469 InlinedCallData* call_data) { | 490 InlinedCallData* call_data) { |
| 470 TRACE_INLINING(OS::Print(" => %s (deopt count %d)\n", | 491 TRACE_INLINING(OS::Print(" => %s (deopt count %d)\n", |
| 471 function.ToCString(), | 492 function.ToCString(), |
| 472 function.deoptimization_counter())); | 493 function.deoptimization_counter())); |
| 473 | 494 |
| 474 // TODO(fschneider): Enable inlining inside try-blocks. | 495 // TODO(fschneider): Enable inlining inside try-blocks. |
| 475 if (call_data->call->GetBlock()->try_index() != | 496 if (call_data->call->GetBlock()->try_index() != |
| 476 CatchClauseNode::kInvalidTryIndex) { | 497 CatchClauseNode::kInvalidTryIndex) { |
| 477 TRACE_INLINING(OS::Print(" Bailout: inside try-block\n")); | 498 TRACE_INLINING(OS::Print(" Bailout: inside try-block\n")); |
| 499 if (FLAG_print_inlining_tree) { |
| 500 inlined_info_.Add(InlinedInfo( |
| 501 &call_data->caller, &function, inlining_depth_, call_data->call, |
| 502 "Inside try-block")); |
| 503 } |
| 478 return false; | 504 return false; |
| 479 } | 505 } |
| 480 | 506 |
| 481 // Make a handle for the unoptimized code so that it is not disconnected | 507 // Make a handle for the unoptimized code so that it is not disconnected |
| 482 // from the function while we are trying to inline it. | 508 // from the function while we are trying to inline it. |
| 483 const Code& unoptimized_code = Code::Handle(function.unoptimized_code()); | 509 const Code& unoptimized_code = Code::Handle(function.unoptimized_code()); |
| 484 // Abort if the inlinable bit on the function is low. | 510 // Abort if the inlinable bit on the function is low. |
| 485 if (!function.IsInlineable()) { | 511 if (!function.IsInlineable()) { |
| 486 TRACE_INLINING(OS::Print(" Bailout: not inlinable\n")); | 512 TRACE_INLINING(OS::Print(" Bailout: not inlinable\n")); |
| 513 if (FLAG_print_inlining_tree) { |
| 514 inlined_info_.Add(InlinedInfo( |
| 515 &call_data->caller, &function, inlining_depth_, call_data->call, |
| 516 "Not inlinable")); |
| 517 } |
| 487 return false; | 518 return false; |
| 488 } | 519 } |
| 489 | 520 |
| 490 // Abort if this function has deoptimized too much. | 521 // Abort if this function has deoptimized too much. |
| 491 if (function.deoptimization_counter() >= | 522 if (function.deoptimization_counter() >= |
| 492 FLAG_deoptimization_counter_threshold) { | 523 FLAG_deoptimization_counter_threshold) { |
| 493 function.set_is_inlinable(false); | 524 function.set_is_inlinable(false); |
| 494 TRACE_INLINING(OS::Print(" Bailout: deoptimization threshold\n")); | 525 TRACE_INLINING(OS::Print(" Bailout: deoptimization threshold\n")); |
| 526 if (FLAG_print_inlining_tree) { |
| 527 inlined_info_.Add(InlinedInfo( |
| 528 &call_data->caller, &function, inlining_depth_, call_data->call, |
| 529 "Deoptimization threshold exceeded")); |
| 530 } |
| 495 return false; | 531 return false; |
| 496 } | 532 } |
| 497 | 533 |
| 498 GrowableArray<Value*>* arguments = call_data->arguments; | 534 GrowableArray<Value*>* arguments = call_data->arguments; |
| 499 const intptr_t constant_arguments = CountConstants(*arguments); | 535 const intptr_t constant_arguments = CountConstants(*arguments); |
| 500 if (!ShouldWeInline(function, | 536 if (!ShouldWeInline(function, |
| 501 function.optimized_instruction_count(), | 537 function.optimized_instruction_count(), |
| 502 function.optimized_call_site_count(), | 538 function.optimized_call_site_count(), |
| 503 constant_arguments)) { | 539 constant_arguments)) { |
| 504 TRACE_INLINING(OS::Print(" Bailout: early heuristics with " | 540 TRACE_INLINING(OS::Print(" Bailout: early heuristics with " |
| 505 "code size: %" Pd ", " | 541 "code size: %" Pd ", " |
| 506 "call sites: %" Pd ", " | 542 "call sites: %" Pd ", " |
| 507 "const args: %" Pd "\n", | 543 "const args: %" Pd "\n", |
| 508 function.optimized_instruction_count(), | 544 function.optimized_instruction_count(), |
| 509 function.optimized_call_site_count(), | 545 function.optimized_call_site_count(), |
| 510 constant_arguments)); | 546 constant_arguments)); |
| 547 if (FLAG_print_inlining_tree) { |
| 548 inlined_info_.Add(InlinedInfo( |
| 549 &call_data->caller, &function, inlining_depth_, call_data->call, |
| 550 "Early heuristic")); |
| 551 } |
| 511 return false; | 552 return false; |
| 512 } | 553 } |
| 513 | 554 |
| 514 // Abort if this is a recursive occurrence. | 555 // Abort if this is a recursive occurrence. |
| 515 Definition* call = call_data->call; | 556 Definition* call = call_data->call; |
| 516 if (!FLAG_inline_recursive && IsCallRecursive(unoptimized_code, call)) { | 557 if (!FLAG_inline_recursive && IsCallRecursive(unoptimized_code, call)) { |
| 517 function.set_is_inlinable(false); | 558 function.set_is_inlinable(false); |
| 518 TRACE_INLINING(OS::Print(" Bailout: recursive function\n")); | 559 TRACE_INLINING(OS::Print(" Bailout: recursive function\n")); |
| 519 return false; | 560 return false; |
| 520 } | 561 } |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 627 FlowGraphPrinter printer(*callee_graph); | 668 FlowGraphPrinter printer(*callee_graph); |
| 628 printer.PrintBlocks(); | 669 printer.PrintBlocks(); |
| 629 } | 670 } |
| 630 | 671 |
| 631 // Collect information about the call site and caller graph. | 672 // Collect information about the call site and caller graph. |
| 632 // TODO(zerny): Do this after CP and dead code elimination. | 673 // TODO(zerny): Do this after CP and dead code elimination. |
| 633 intptr_t constants_count = 0; | 674 intptr_t constants_count = 0; |
| 634 for (intptr_t i = 0; i < param_stubs->length(); ++i) { | 675 for (intptr_t i = 0; i < param_stubs->length(); ++i) { |
| 635 if ((*param_stubs)[i]->IsConstant()) ++constants_count; | 676 if ((*param_stubs)[i]->IsConstant()) ++constants_count; |
| 636 } | 677 } |
| 637 GraphInfoCollector info; | 678 |
| 638 info.Collect(*callee_graph); | 679 FlowGraphInliner::CollectGraphInfo(callee_graph); |
| 639 const intptr_t size = info.instruction_count(); | 680 const intptr_t size = function.optimized_instruction_count(); |
| 640 const intptr_t call_site_count = info.call_site_count(); | 681 const intptr_t call_site_count = function.optimized_call_site_count(); |
| 641 | 682 |
| 642 function.set_optimized_instruction_count(size); | 683 function.set_optimized_instruction_count(size); |
| 643 function.set_optimized_call_site_count(call_site_count); | 684 function.set_optimized_call_site_count(call_site_count); |
| 644 | 685 |
| 645 // Use heuristics do decide if this call should be inlined. | 686 // Use heuristics do decide if this call should be inlined. |
| 646 if (!ShouldWeInline(function, size, call_site_count, constants_count)) { | 687 if (!ShouldWeInline(function, size, call_site_count, constants_count)) { |
| 647 // If size is larger than all thresholds, don't consider it again. | 688 // If size is larger than all thresholds, don't consider it again. |
| 648 if ((size > FLAG_inlining_size_threshold) && | 689 if ((size > FLAG_inlining_size_threshold) && |
| 649 (call_site_count > FLAG_inlining_callee_call_sites_threshold) && | 690 (call_site_count > FLAG_inlining_callee_call_sites_threshold) && |
| 650 (size > FLAG_inlining_constant_arguments_size_threshold)) { | 691 (size > FLAG_inlining_constant_arguments_size_threshold)) { |
| 651 function.set_is_inlinable(false); | 692 function.set_is_inlinable(false); |
| 652 } | 693 } |
| 653 isolate->set_deopt_id(prev_deopt_id); | 694 isolate->set_deopt_id(prev_deopt_id); |
| 654 TRACE_INLINING(OS::Print(" Bailout: heuristics with " | 695 TRACE_INLINING(OS::Print(" Bailout: heuristics with " |
| 655 "code size: %" Pd ", " | 696 "code size: %" Pd ", " |
| 656 "call sites: %" Pd ", " | 697 "call sites: %" Pd ", " |
| 657 "const args: %" Pd "\n", | 698 "const args: %" Pd "\n", |
| 658 size, | 699 size, |
| 659 call_site_count, | 700 call_site_count, |
| 660 constants_count)); | 701 constants_count)); |
| 702 if (FLAG_print_inlining_tree) { |
| 703 inlined_info_.Add(InlinedInfo( |
| 704 &call_data->caller, &function, inlining_depth_, call_data->call, |
| 705 "Heuristic fail")); |
| 706 } |
| 661 return false; | 707 return false; |
| 662 } | 708 } |
| 663 | 709 |
| 664 collected_call_sites_->FindCallSites(callee_graph, inlining_depth_); | 710 collected_call_sites_->FindCallSites(callee_graph, |
| 711 inlining_depth_, |
| 712 &inlined_info_); |
| 665 | 713 |
| 666 // Add the function to the cache. | 714 // Add the function to the cache. |
| 667 if (!in_cache) { | 715 if (!in_cache) { |
| 668 function_cache_.Add(parsed_function); | 716 function_cache_.Add(parsed_function); |
| 669 } | 717 } |
| 670 | 718 |
| 671 // Build succeeded so we restore the bailout jump. | 719 // Build succeeded so we restore the bailout jump. |
| 672 inlined_ = true; | 720 inlined_ = true; |
| 673 inlined_size_ += size; | 721 inlined_size_ += size; |
| 674 isolate->set_deopt_id(prev_deopt_id); | 722 isolate->set_deopt_id(prev_deopt_id); |
| 675 | 723 |
| 676 call_data->callee_graph = callee_graph; | 724 call_data->callee_graph = callee_graph; |
| 677 call_data->parameter_stubs = param_stubs; | 725 call_data->parameter_stubs = param_stubs; |
| 678 call_data->exit_collector = exit_collector; | 726 call_data->exit_collector = exit_collector; |
| 679 | 727 |
| 680 // When inlined, we add the guarded fields of the callee to the caller's | 728 // When inlined, we add the guarded fields of the callee to the caller's |
| 681 // list of guarded fields. | 729 // list of guarded fields. |
| 682 for (intptr_t i = 0; i < callee_graph->guarded_fields()->length(); ++i) { | 730 for (intptr_t i = 0; i < callee_graph->guarded_fields()->length(); ++i) { |
| 683 FlowGraph::AddToGuardedFields(caller_graph_->guarded_fields(), | 731 FlowGraph::AddToGuardedFields(caller_graph_->guarded_fields(), |
| 684 (*callee_graph->guarded_fields())[i]); | 732 (*callee_graph->guarded_fields())[i]); |
| 685 } | 733 } |
| 686 | 734 |
| 687 // We allocate a ZoneHandle for the unoptimized code so that it cannot be | 735 // We allocate a ZoneHandle for the unoptimized code so that it cannot be |
| 688 // disconnected from its function during the rest of compilation. | 736 // disconnected from its function during the rest of compilation. |
| 689 Code::ZoneHandle(unoptimized_code.raw()); | 737 Code::ZoneHandle(unoptimized_code.raw()); |
| 690 TRACE_INLINING(OS::Print(" Success\n")); | 738 TRACE_INLINING(OS::Print(" Success\n")); |
| 691 if (FLAG_print_inline_tree) { | 739 if (FLAG_print_inlining_tree) { |
| 692 inlined_info_.Add( | 740 inlined_info_.Add( |
| 693 InlinedInfo(&call_data->caller_, &function, inlining_depth_, call)); | 741 InlinedInfo(&call_data->caller, &function, inlining_depth_, call)); |
| 694 } | 742 } |
| 695 return true; | 743 return true; |
| 696 } else { | 744 } else { |
| 697 Error& error = Error::Handle(); | 745 Error& error = Error::Handle(); |
| 698 error = isolate->object_store()->sticky_error(); | 746 error = isolate->object_store()->sticky_error(); |
| 699 isolate->object_store()->clear_sticky_error(); | 747 isolate->object_store()->clear_sticky_error(); |
| 700 isolate->set_deopt_id(prev_deopt_id); | 748 isolate->set_deopt_id(prev_deopt_id); |
| 701 TRACE_INLINING(OS::Print(" Bailout: %s\n", error.ToErrorCString())); | 749 TRACE_INLINING(OS::Print(" Bailout: %s\n", error.ToErrorCString())); |
| 702 return false; | 750 return false; |
| 703 } | 751 } |
| 704 } | 752 } |
| 705 | 753 |
| 706 void PrintInlinedInfo(const Function& top) { | 754 void PrintInlinedInfo(const Function& top) { |
| 707 OS::Print("Inlining into: %s\n", top.ToFullyQualifiedCString()); | 755 if (inlined_info_.length() > 0) { |
| 708 PrintInlinedInfoFor(top, 1); | 756 OS::Print("Inlining into: '%s' growth: %f (%"Pd" -> %"Pd")\n", |
| 757 top.ToFullyQualifiedCString(), |
| 758 GrowthFactor(), |
| 759 initial_size_, |
| 760 inlined_size_); |
| 761 PrintInlinedInfoFor(top, 1); |
| 762 } |
| 709 } | 763 } |
| 710 | 764 |
| 711 private: | 765 private: |
| 712 friend class PolymorphicInliner; | 766 friend class PolymorphicInliner; |
| 713 | 767 |
| 714 void PrintInlinedInfoFor(const Function& caller, intptr_t depth) { | 768 void PrintInlinedInfoFor(const Function& caller, intptr_t depth) { |
| 769 // Print those that were inlined. |
| 715 for (intptr_t i = 0; i < inlined_info_.length(); i++) { | 770 for (intptr_t i = 0; i < inlined_info_.length(); i++) { |
| 716 const InlinedInfo& info = inlined_info_[i]; | 771 const InlinedInfo& info = inlined_info_[i]; |
| 772 if (info.bailout_reason != NULL) continue; |
| 717 if ((info.inlined_depth == depth) && | 773 if ((info.inlined_depth == depth) && |
| 718 (info.caller->raw() == caller.raw())) { | 774 (info.caller->raw() == caller.raw())) { |
| 719 for (int t = 0; t < depth; t++) { | 775 for (int t = 0; t < depth; t++) { |
| 720 OS::Print(" "); | 776 OS::Print(" "); |
| 721 } | 777 } |
| 722 OS::Print("%" Pd " %s\n", | 778 OS::Print("%" Pd " %s\n", |
| 723 info.call_instr->GetDeoptId(), | 779 info.call_instr->GetDeoptId(), |
| 724 info.inlined->ToQualifiedCString()); | 780 info.inlined->ToQualifiedCString()); |
| 725 PrintInlinedInfoFor(*info.inlined, depth + 1); | 781 PrintInlinedInfoFor(*info.inlined, depth + 1); |
| 726 } | 782 } |
| 727 } | 783 } |
| 784 // Print those that were not inlined. |
| 785 for (intptr_t i = 0; i < inlined_info_.length(); i++) { |
| 786 const InlinedInfo& info = inlined_info_[i]; |
| 787 if (info.bailout_reason == NULL) continue; |
| 788 if ((info.inlined_depth == depth) && |
| 789 (info.caller->raw() == caller.raw())) { |
| 790 for (int t = 0; t < depth; t++) { |
| 791 OS::Print(" "); |
| 792 } |
| 793 OS::Print("NO %" Pd " %s - %s\n", |
| 794 info.call_instr->GetDeoptId(), |
| 795 info.inlined->ToQualifiedCString(), |
| 796 info.bailout_reason); |
| 797 } |
| 798 } |
| 728 } | 799 } |
| 729 | 800 |
| 730 void InlineCall(InlinedCallData* call_data) { | 801 void InlineCall(InlinedCallData* call_data) { |
| 731 TimerScope timer(FLAG_compiler_stats, | 802 TimerScope timer(FLAG_compiler_stats, |
| 732 &CompilerStats::graphinliner_subst_timer, | 803 &CompilerStats::graphinliner_subst_timer, |
| 733 Isolate::Current()); | 804 Isolate::Current()); |
| 734 | 805 |
| 735 // For closure calls: Store context value. | 806 // For closure calls: Store context value. |
| 736 FlowGraph* callee_graph = call_data->callee_graph; | 807 FlowGraph* callee_graph = call_data->callee_graph; |
| 737 TargetEntryInstr* callee_entry = | 808 TargetEntryInstr* callee_entry = |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 836 } | 907 } |
| 837 } | 908 } |
| 838 const Function& target = call->function(); | 909 const Function& target = call->function(); |
| 839 if (!FlowGraphInliner::AlwaysInline(target) && | 910 if (!FlowGraphInliner::AlwaysInline(target) && |
| 840 (call_info[call_idx].ratio * 100) < FLAG_inlining_hotness) { | 911 (call_info[call_idx].ratio * 100) < FLAG_inlining_hotness) { |
| 841 TRACE_INLINING(OS::Print( | 912 TRACE_INLINING(OS::Print( |
| 842 " => %s (deopt count %d)\n Bailout: cold %f\n", | 913 " => %s (deopt count %d)\n Bailout: cold %f\n", |
| 843 target.ToCString(), | 914 target.ToCString(), |
| 844 target.deoptimization_counter(), | 915 target.deoptimization_counter(), |
| 845 call_info[call_idx].ratio)); | 916 call_info[call_idx].ratio)); |
| 917 if (FLAG_print_inlining_tree) { |
| 918 inlined_info_.Add(InlinedInfo( |
| 919 call_info[call_idx].caller, |
| 920 &call->function(), |
| 921 inlining_depth_, |
| 922 call, |
| 923 "Too cold")); |
| 924 } |
| 846 continue; | 925 continue; |
| 847 } | 926 } |
| 848 GrowableArray<Value*> arguments(call->ArgumentCount()); | 927 GrowableArray<Value*> arguments(call->ArgumentCount()); |
| 849 for (int i = 0; i < call->ArgumentCount(); ++i) { | 928 for (int i = 0; i < call->ArgumentCount(); ++i) { |
| 850 arguments.Add(call->PushArgumentAt(i)->value()); | 929 arguments.Add(call->PushArgumentAt(i)->value()); |
| 851 } | 930 } |
| 852 InlinedCallData call_data(call, &arguments, *call_info[call_idx].caller); | 931 InlinedCallData call_data(call, &arguments, *call_info[call_idx].caller); |
| 853 if (TryInlining(call->function(), call->argument_names(), &call_data)) { | 932 if (TryInlining(call->function(), call->argument_names(), &call_data)) { |
| 854 InlineCall(&call_data); | 933 InlineCall(&call_data); |
| 855 } | 934 } |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 905 | 984 |
| 906 const ICData& ic_data = call->ic_data(); | 985 const ICData& ic_data = call->ic_data(); |
| 907 const Function& target = Function::ZoneHandle(ic_data.GetTargetAt(0)); | 986 const Function& target = Function::ZoneHandle(ic_data.GetTargetAt(0)); |
| 908 if (!FlowGraphInliner::AlwaysInline(target) && | 987 if (!FlowGraphInliner::AlwaysInline(target) && |
| 909 (call_info[call_idx].ratio * 100) < FLAG_inlining_hotness) { | 988 (call_info[call_idx].ratio * 100) < FLAG_inlining_hotness) { |
| 910 TRACE_INLINING(OS::Print( | 989 TRACE_INLINING(OS::Print( |
| 911 " => %s (deopt count %d)\n Bailout: cold %f\n", | 990 " => %s (deopt count %d)\n Bailout: cold %f\n", |
| 912 target.ToCString(), | 991 target.ToCString(), |
| 913 target.deoptimization_counter(), | 992 target.deoptimization_counter(), |
| 914 call_info[call_idx].ratio)); | 993 call_info[call_idx].ratio)); |
| 994 if (FLAG_print_inlining_tree) { |
| 995 inlined_info_.Add(InlinedInfo( |
| 996 call_info[call_idx].caller, |
| 997 &target, |
| 998 inlining_depth_, |
| 999 call, |
| 1000 "Too cold")); |
| 1001 } |
| 915 continue; | 1002 continue; |
| 916 } | 1003 } |
| 917 GrowableArray<Value*> arguments(call->ArgumentCount()); | 1004 GrowableArray<Value*> arguments(call->ArgumentCount()); |
| 918 for (int arg_i = 0; arg_i < call->ArgumentCount(); ++arg_i) { | 1005 for (int arg_i = 0; arg_i < call->ArgumentCount(); ++arg_i) { |
| 919 arguments.Add(call->PushArgumentAt(arg_i)->value()); | 1006 arguments.Add(call->PushArgumentAt(arg_i)->value()); |
| 920 } | 1007 } |
| 921 InlinedCallData call_data(call, &arguments, *call_info[call_idx].caller); | 1008 InlinedCallData call_data(call, &arguments, *call_info[call_idx].caller); |
| 922 if (TryInlining(target, | 1009 if (TryInlining(target, |
| 923 call->instance_call()->argument_names(), | 1010 call->instance_call()->argument_names(), |
| 924 &call_data)) { | 1011 &call_data)) { |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1014 } else { | 1101 } else { |
| 1015 param_stubs->Add( | 1102 param_stubs->Add( |
| 1016 GetDefaultValue(i - fixed_param_count, parsed_function)); | 1103 GetDefaultValue(i - fixed_param_count, parsed_function)); |
| 1017 } | 1104 } |
| 1018 } | 1105 } |
| 1019 return argument_names_count == match_count; | 1106 return argument_names_count == match_count; |
| 1020 } | 1107 } |
| 1021 | 1108 |
| 1022 FlowGraph* caller_graph_; | 1109 FlowGraph* caller_graph_; |
| 1023 bool inlined_; | 1110 bool inlined_; |
| 1024 intptr_t initial_size_; | 1111 const intptr_t initial_size_; |
| 1025 intptr_t inlined_size_; | 1112 intptr_t inlined_size_; |
| 1026 intptr_t inlining_depth_; | 1113 intptr_t inlining_depth_; |
| 1027 CallSites* collected_call_sites_; | 1114 CallSites* collected_call_sites_; |
| 1028 CallSites* inlining_call_sites_; | 1115 CallSites* inlining_call_sites_; |
| 1029 GrowableArray<ParsedFunction*> function_cache_; | 1116 GrowableArray<ParsedFunction*> function_cache_; |
| 1030 GrowableArray<InlinedInfo> inlined_info_; | 1117 GrowableArray<InlinedInfo> inlined_info_; |
| 1031 | 1118 |
| 1032 DISALLOW_COPY_AND_ASSIGN(CallSiteInliner); | 1119 DISALLOW_COPY_AND_ASSIGN(CallSiteInliner); |
| 1033 }; | 1120 }; |
| 1034 | 1121 |
| (...skipping 454 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1489 TargetEntryInstr* entry = BuildDecisionGraph(); | 1576 TargetEntryInstr* entry = BuildDecisionGraph(); |
| 1490 exit_collector_->ReplaceCall(entry); | 1577 exit_collector_->ReplaceCall(entry); |
| 1491 } | 1578 } |
| 1492 | 1579 |
| 1493 | 1580 |
| 1494 static uint16_t ClampUint16(intptr_t v) { | 1581 static uint16_t ClampUint16(intptr_t v) { |
| 1495 return (v > 0xFFFF) ? 0xFFFF : static_cast<uint16_t>(v); | 1582 return (v > 0xFFFF) ? 0xFFFF : static_cast<uint16_t>(v); |
| 1496 } | 1583 } |
| 1497 | 1584 |
| 1498 | 1585 |
| 1499 void FlowGraphInliner::CollectGraphInfo(FlowGraph* flow_graph) { | 1586 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(); | 1587 const Function& function = flow_graph->parsed_function().function(); |
| 1503 function.set_optimized_instruction_count( | 1588 if (force || (function.optimized_instruction_count() == 0)) { |
| 1504 ClampUint16(info.instruction_count())); | 1589 GraphInfoCollector info; |
| 1505 function.set_optimized_call_site_count(ClampUint16(info.call_site_count())); | 1590 info.Collect(*flow_graph); |
| 1591 |
| 1592 function.set_optimized_instruction_count( |
| 1593 ClampUint16(info.instruction_count())); |
| 1594 function.set_optimized_call_site_count(ClampUint16(info.call_site_count())); |
| 1595 } |
| 1506 } | 1596 } |
| 1507 | 1597 |
| 1508 | 1598 |
| 1509 bool FlowGraphInliner::AlwaysInline(const Function& function) { | 1599 bool FlowGraphInliner::AlwaysInline(const Function& function) { |
| 1510 if (function.IsImplicitGetterFunction() || function.IsGetterFunction() || | 1600 if (function.IsImplicitGetterFunction() || function.IsGetterFunction() || |
| 1511 function.IsImplicitSetterFunction() || function.IsSetterFunction()) { | 1601 function.IsImplicitSetterFunction() || function.IsSetterFunction()) { |
| 1512 const intptr_t count = function.optimized_instruction_count(); | 1602 const intptr_t count = function.optimized_instruction_count(); |
| 1513 if ((count != 0) && (count < FLAG_inline_getters_setters_smaller_than)) { | 1603 if ((count != 0) && (count < FLAG_inline_getters_setters_smaller_than)) { |
| 1514 return true; | 1604 return true; |
| 1515 } | 1605 } |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1534 if (FLAG_trace_inlining && | 1624 if (FLAG_trace_inlining && |
| 1535 (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized)) { | 1625 (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized)) { |
| 1536 OS::Print("Before Inlining of %s\n", flow_graph_-> | 1626 OS::Print("Before Inlining of %s\n", flow_graph_-> |
| 1537 parsed_function().function().ToFullyQualifiedCString()); | 1627 parsed_function().function().ToFullyQualifiedCString()); |
| 1538 FlowGraphPrinter printer(*flow_graph_); | 1628 FlowGraphPrinter printer(*flow_graph_); |
| 1539 printer.PrintBlocks(); | 1629 printer.PrintBlocks(); |
| 1540 } | 1630 } |
| 1541 | 1631 |
| 1542 CallSiteInliner inliner(flow_graph_); | 1632 CallSiteInliner inliner(flow_graph_); |
| 1543 inliner.InlineCalls(); | 1633 inliner.InlineCalls(); |
| 1544 if (FLAG_print_inline_tree) { | 1634 if (FLAG_print_inlining_tree) { |
| 1545 inliner.PrintInlinedInfo(top); | 1635 inliner.PrintInlinedInfo(top); |
| 1546 } | 1636 } |
| 1547 | 1637 |
| 1548 if (inliner.inlined()) { | 1638 if (inliner.inlined()) { |
| 1549 flow_graph_->DiscoverBlocks(); | 1639 flow_graph_->DiscoverBlocks(); |
| 1550 if (FLAG_trace_inlining) { | 1640 if (FLAG_trace_inlining) { |
| 1551 OS::Print("Inlining growth factor: %f\n", inliner.GrowthFactor()); | 1641 OS::Print("Inlining growth factor: %f\n", inliner.GrowthFactor()); |
| 1552 if (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized) { | 1642 if (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized) { |
| 1553 OS::Print("After Inlining of %s\n", flow_graph_-> | 1643 OS::Print("After Inlining of %s\n", flow_graph_-> |
| 1554 parsed_function().function().ToFullyQualifiedCString()); | 1644 parsed_function().function().ToFullyQualifiedCString()); |
| 1555 FlowGraphPrinter printer(*flow_graph_); | 1645 FlowGraphPrinter printer(*flow_graph_); |
| 1556 printer.PrintBlocks(); | 1646 printer.PrintBlocks(); |
| 1557 } | 1647 } |
| 1558 } | 1648 } |
| 1559 } | 1649 } |
| 1560 } | 1650 } |
| 1561 | 1651 |
| 1562 } // namespace dart | 1652 } // namespace dart |
| OLD | NEW |