| 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 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 52 DECLARE_FLAG(bool, verify_compiler); | 52 DECLARE_FLAG(bool, verify_compiler); |
| 53 DECLARE_FLAG(bool, compiler_stats); | 53 DECLARE_FLAG(bool, compiler_stats); |
| 54 | 54 |
| 55 #define TRACE_INLINING(statement) \ | 55 #define TRACE_INLINING(statement) \ |
| 56 do { \ | 56 do { \ |
| 57 if (FLAG_trace_inlining) statement; \ | 57 if (FLAG_trace_inlining) statement; \ |
| 58 } while (false) | 58 } while (false) |
| 59 | 59 |
| 60 | 60 |
| 61 // Test if a call is recursive by looking in the deoptimization environment. | 61 // Test if a call is recursive by looking in the deoptimization environment. |
| 62 static bool IsCallRecursive(const Function& function, Definition* call) { | 62 static bool IsCallRecursive(const Code& code, Definition* call) { |
| 63 Environment* env = call->env(); | 63 Environment* env = call->env(); |
| 64 while (env != NULL) { | 64 while (env != NULL) { |
| 65 if (function.raw() == env->function().raw()) return true; | 65 if (code.raw() == env->code().raw()) { |
| 66 return true; |
| 67 } |
| 66 env = env->outer(); | 68 env = env->outer(); |
| 67 } | 69 } |
| 68 return false; | 70 return false; |
| 69 } | 71 } |
| 70 | 72 |
| 71 | 73 |
| 72 // Helper to create a parameter stub from an actual argument. | 74 // Helper to create a parameter stub from an actual argument. |
| 73 static Definition* CreateParameterStub(intptr_t i, | 75 static Definition* CreateParameterStub(intptr_t i, |
| 74 Value* argument, | 76 Value* argument, |
| 75 FlowGraph* graph) { | 77 FlowGraph* graph) { |
| (...skipping 351 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 427 function.ToCString(), | 429 function.ToCString(), |
| 428 function.deoptimization_counter())); | 430 function.deoptimization_counter())); |
| 429 | 431 |
| 430 // TODO(fschneider): Enable inlining inside try-blocks. | 432 // TODO(fschneider): Enable inlining inside try-blocks. |
| 431 if (call_data->call->GetBlock()->try_index() != | 433 if (call_data->call->GetBlock()->try_index() != |
| 432 CatchClauseNode::kInvalidTryIndex) { | 434 CatchClauseNode::kInvalidTryIndex) { |
| 433 TRACE_INLINING(OS::Print(" Bailout: inside try-block\n")); | 435 TRACE_INLINING(OS::Print(" Bailout: inside try-block\n")); |
| 434 return false; | 436 return false; |
| 435 } | 437 } |
| 436 | 438 |
| 439 // Make a handle for the unoptimized code so that it is not disconnected |
| 440 // from the function while we are trying to inline it. |
| 441 const Code& unoptimized_code = Code::Handle(function.unoptimized_code()); |
| 437 // Abort if the inlinable bit on the function is low. | 442 // Abort if the inlinable bit on the function is low. |
| 438 if (!function.IsInlineable()) { | 443 if (!function.IsInlineable()) { |
| 439 TRACE_INLINING(OS::Print(" Bailout: not inlinable\n")); | 444 TRACE_INLINING(OS::Print(" Bailout: not inlinable\n")); |
| 440 return false; | 445 return false; |
| 441 } | 446 } |
| 442 | 447 |
| 443 // Abort if this function has deoptimized too much. | 448 // Abort if this function has deoptimized too much. |
| 444 if (function.deoptimization_counter() >= | 449 if (function.deoptimization_counter() >= |
| 445 FLAG_deoptimization_counter_threshold) { | 450 FLAG_deoptimization_counter_threshold) { |
| 446 function.set_is_inlinable(false); | 451 function.set_is_inlinable(false); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 459 "call sites: %" Pd ", " | 464 "call sites: %" Pd ", " |
| 460 "const args: %" Pd "\n", | 465 "const args: %" Pd "\n", |
| 461 function.optimized_instruction_count(), | 466 function.optimized_instruction_count(), |
| 462 function.optimized_call_site_count(), | 467 function.optimized_call_site_count(), |
| 463 constant_arguments)); | 468 constant_arguments)); |
| 464 return false; | 469 return false; |
| 465 } | 470 } |
| 466 | 471 |
| 467 // Abort if this is a recursive occurrence. | 472 // Abort if this is a recursive occurrence. |
| 468 Definition* call = call_data->call; | 473 Definition* call = call_data->call; |
| 469 if (!FLAG_inline_recursive && IsCallRecursive(function, call)) { | 474 if (!FLAG_inline_recursive && IsCallRecursive(unoptimized_code, call)) { |
| 470 function.set_is_inlinable(false); | 475 function.set_is_inlinable(false); |
| 471 TRACE_INLINING(OS::Print(" Bailout: recursive function\n")); | 476 TRACE_INLINING(OS::Print(" Bailout: recursive function\n")); |
| 472 return false; | 477 return false; |
| 473 } | 478 } |
| 474 | 479 |
| 475 Isolate* isolate = Isolate::Current(); | 480 Isolate* isolate = Isolate::Current(); |
| 476 // Save and clear deopt id. | 481 // Save and clear deopt id. |
| 477 const intptr_t prev_deopt_id = isolate->deopt_id(); | 482 const intptr_t prev_deopt_id = isolate->deopt_id(); |
| 478 isolate->set_deopt_id(0); | 483 isolate->set_deopt_id(0); |
| 479 // Install bailout jump. | 484 // Install bailout jump. |
| 480 LongJump* base = isolate->long_jump_base(); | 485 LongJump* base = isolate->long_jump_base(); |
| 481 LongJump jump; | 486 LongJump jump; |
| 482 isolate->set_long_jump_base(&jump); | 487 isolate->set_long_jump_base(&jump); |
| 483 if (setjmp(*jump.Set()) == 0) { | 488 if (setjmp(*jump.Set()) == 0) { |
| 484 // Parse the callee function. | 489 // Parse the callee function. |
| 485 bool in_cache; | 490 bool in_cache; |
| 486 ParsedFunction* parsed_function; | 491 ParsedFunction* parsed_function; |
| 487 { | 492 { |
| 488 TimerScope timer(FLAG_compiler_stats, | 493 TimerScope timer(FLAG_compiler_stats, |
| 489 &CompilerStats::graphinliner_parse_timer, | 494 &CompilerStats::graphinliner_parse_timer, |
| 490 isolate); | 495 isolate); |
| 491 parsed_function = GetParsedFunction(function, &in_cache); | 496 parsed_function = GetParsedFunction(function, &in_cache); |
| 492 } | 497 } |
| 493 | 498 |
| 494 // Load IC data for the callee. | 499 // Load IC data for the callee. |
| 495 Array& ic_data_array = Array::Handle(); | 500 Array& ic_data_array = Array::Handle(); |
| 496 if (function.HasCode()) { | 501 |
| 497 const Code& unoptimized_code = | 502 // IsInlineable above checked HasCode. Creating a Handle for the code |
| 498 Code::Handle(function.unoptimized_code()); | 503 // should have kept GC from detaching, but let's assert just to make sure. |
| 499 ic_data_array = unoptimized_code.ExtractTypeFeedbackArray(); | 504 ASSERT(function.HasCode()); |
| 500 } | 505 ic_data_array = unoptimized_code.ExtractTypeFeedbackArray(); |
| 501 | 506 |
| 502 // Build the callee graph. | 507 // Build the callee graph. |
| 503 InlineExitCollector* exit_collector = | 508 InlineExitCollector* exit_collector = |
| 504 new InlineExitCollector(caller_graph_, call); | 509 new InlineExitCollector(caller_graph_, call); |
| 505 FlowGraphBuilder builder(parsed_function, | 510 FlowGraphBuilder builder(parsed_function, |
| 506 ic_data_array, | 511 ic_data_array, |
| 507 exit_collector, | 512 exit_collector, |
| 508 Isolate::kNoDeoptId); | 513 Isolate::kNoDeoptId); |
| 509 builder.SetInitialBlockId(caller_graph_->max_block_id()); | 514 builder.SetInitialBlockId(caller_graph_->max_block_id()); |
| 510 FlowGraph* callee_graph; | 515 FlowGraph* callee_graph; |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 613 "const args: %" Pd "\n", | 618 "const args: %" Pd "\n", |
| 614 size, | 619 size, |
| 615 call_site_count, | 620 call_site_count, |
| 616 constants_count)); | 621 constants_count)); |
| 617 return false; | 622 return false; |
| 618 } | 623 } |
| 619 | 624 |
| 620 collected_call_sites_->FindCallSites(callee_graph, inlining_depth_); | 625 collected_call_sites_->FindCallSites(callee_graph, inlining_depth_); |
| 621 | 626 |
| 622 // Add the function to the cache. | 627 // Add the function to the cache. |
| 623 if (!in_cache) function_cache_.Add(parsed_function); | 628 if (!in_cache) { |
| 629 function_cache_.Add(parsed_function); |
| 630 } |
| 624 | 631 |
| 625 // Build succeeded so we restore the bailout jump. | 632 // Build succeeded so we restore the bailout jump. |
| 626 inlined_ = true; | 633 inlined_ = true; |
| 627 inlined_size_ += size; | 634 inlined_size_ += size; |
| 628 isolate->set_long_jump_base(base); | 635 isolate->set_long_jump_base(base); |
| 629 isolate->set_deopt_id(prev_deopt_id); | 636 isolate->set_deopt_id(prev_deopt_id); |
| 630 | 637 |
| 631 call_data->callee_graph = callee_graph; | 638 call_data->callee_graph = callee_graph; |
| 632 call_data->parameter_stubs = param_stubs; | 639 call_data->parameter_stubs = param_stubs; |
| 633 call_data->exit_collector = exit_collector; | 640 call_data->exit_collector = exit_collector; |
| 634 | 641 |
| 635 // When inlined, we add the guarded fields of the callee to the caller's | 642 // When inlined, we add the guarded fields of the callee to the caller's |
| 636 // list of guarded fields. | 643 // list of guarded fields. |
| 637 for (intptr_t i = 0; i < callee_graph->guarded_fields()->length(); ++i) { | 644 for (intptr_t i = 0; i < callee_graph->guarded_fields()->length(); ++i) { |
| 638 FlowGraph::AddToGuardedFields(caller_graph_->guarded_fields(), | 645 FlowGraph::AddToGuardedFields(caller_graph_->guarded_fields(), |
| 639 (*callee_graph->guarded_fields())[i]); | 646 (*callee_graph->guarded_fields())[i]); |
| 640 } | 647 } |
| 641 | 648 |
| 649 // We allocate a ZoneHandle for the unoptimized code so that it cannot be |
| 650 // disconnected from its function during the rest of compilation. |
| 651 Code::ZoneHandle(unoptimized_code.raw()); |
| 642 TRACE_INLINING(OS::Print(" Success\n")); | 652 TRACE_INLINING(OS::Print(" Success\n")); |
| 643 return true; | 653 return true; |
| 644 } else { | 654 } else { |
| 645 Error& error = Error::Handle(); | 655 Error& error = Error::Handle(); |
| 646 error = isolate->object_store()->sticky_error(); | 656 error = isolate->object_store()->sticky_error(); |
| 647 isolate->object_store()->clear_sticky_error(); | 657 isolate->object_store()->clear_sticky_error(); |
| 648 isolate->set_long_jump_base(base); | 658 isolate->set_long_jump_base(base); |
| 649 isolate->set_deopt_id(prev_deopt_id); | 659 isolate->set_deopt_id(prev_deopt_id); |
| 650 TRACE_INLINING(OS::Print(" Bailout: %s\n", error.ToErrorCString())); | 660 TRACE_INLINING(OS::Print(" Bailout: %s\n", error.ToErrorCString())); |
| 651 return false; | 661 return false; |
| (...skipping 821 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1473 OS::Print("After Inlining of %s\n", flow_graph_-> | 1483 OS::Print("After Inlining of %s\n", flow_graph_-> |
| 1474 parsed_function().function().ToFullyQualifiedCString()); | 1484 parsed_function().function().ToFullyQualifiedCString()); |
| 1475 FlowGraphPrinter printer(*flow_graph_); | 1485 FlowGraphPrinter printer(*flow_graph_); |
| 1476 printer.PrintBlocks(); | 1486 printer.PrintBlocks(); |
| 1477 } | 1487 } |
| 1478 } | 1488 } |
| 1479 } | 1489 } |
| 1480 } | 1490 } |
| 1481 | 1491 |
| 1482 } // namespace dart | 1492 } // namespace dart |
| OLD | NEW |