Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/flow_graph.h" | 5 #include "vm/flow_graph.h" |
| 6 | 6 |
| 7 #include "vm/bit_vector.h" | 7 #include "vm/bit_vector.h" |
| 8 #include "vm/flow_graph_builder.h" | 8 #include "vm/flow_graph_builder.h" |
| 9 #include "vm/intermediate_language.h" | 9 #include "vm/intermediate_language.h" |
| 10 #include "vm/longjump.h" | 10 #include "vm/longjump.h" |
| 11 #include "vm/growable_array.h" | 11 #include "vm/growable_array.h" |
| 12 | 12 |
| 13 namespace dart { | 13 namespace dart { |
| 14 | 14 |
| 15 DECLARE_FLAG(bool, trace_optimization); | 15 DECLARE_FLAG(bool, trace_optimization); |
| 16 DECLARE_FLAG(bool, verify_compiler); | 16 DECLARE_FLAG(bool, verify_compiler); |
| 17 | 17 |
| 18 #if defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_MIPS) | |
| 19 DEFINE_FLAG(bool, optimize_try_catch, false, "Optimization of try-catch"); | |
| 20 #else | |
| 21 DEFINE_FLAG(bool, optimize_try_catch, true, "Optimization of try-catch"); | |
| 22 #endif | |
| 23 | |
| 18 FlowGraph::FlowGraph(const FlowGraphBuilder& builder, | 24 FlowGraph::FlowGraph(const FlowGraphBuilder& builder, |
| 19 GraphEntryInstr* graph_entry, | 25 GraphEntryInstr* graph_entry, |
| 20 intptr_t max_block_id) | 26 intptr_t max_block_id) |
| 21 : parent_(), | 27 : parent_(), |
| 22 current_ssa_temp_index_(0), | 28 current_ssa_temp_index_(0), |
| 23 max_block_id_(max_block_id), | 29 max_block_id_(max_block_id), |
| 24 parsed_function_(builder.parsed_function()), | 30 parsed_function_(builder.parsed_function()), |
| 25 num_copied_params_(builder.num_copied_params()), | 31 num_copied_params_(builder.num_copied_params()), |
| 26 num_non_copied_params_(builder.num_non_copied_params()), | 32 num_non_copied_params_(builder.num_non_copied_params()), |
| 27 num_stack_locals_(builder.num_stack_locals()), | 33 num_stack_locals_(builder.num_stack_locals()), |
| (...skipping 292 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 320 // have new assigned values flowing out of that block. | 326 // have new assigned values flowing out of that block. |
| 321 const GrowableArray<BitVector*>& ComputeAssignedVars() { | 327 const GrowableArray<BitVector*>& ComputeAssignedVars() { |
| 322 // We can't directly return kill_ because it uses postorder numbering while | 328 // We can't directly return kill_ because it uses postorder numbering while |
| 323 // SSA construction uses preorder numbering internally. | 329 // SSA construction uses preorder numbering internally. |
| 324 // We have to permute postorder into preorder. | 330 // We have to permute postorder into preorder. |
| 325 assigned_vars_.Clear(); | 331 assigned_vars_.Clear(); |
| 326 | 332 |
| 327 const intptr_t block_count = flow_graph_->preorder().length(); | 333 const intptr_t block_count = flow_graph_->preorder().length(); |
| 328 for (intptr_t i = 0; i < block_count; i++) { | 334 for (intptr_t i = 0; i < block_count; i++) { |
| 329 BlockEntryInstr* block = flow_graph_->preorder()[i]; | 335 BlockEntryInstr* block = flow_graph_->preorder()[i]; |
| 336 // All locals are assigned inside try-catch. | |
|
Kevin Millikin (Google)
2013/05/08 11:42:00
Comment needs to say why. I don't understand why
Florian Schneider
2013/05/08 17:10:55
Done.
| |
| 330 BitVector* kill = GetKillSet(block); | 337 BitVector* kill = GetKillSet(block); |
| 331 kill->Intersect(GetLiveOutSet(block)); | 338 if (block->try_index() != CatchClauseNode::kInvalidTryIndex) { |
| 339 kill->SetAll(); | |
| 340 } else { | |
| 341 kill->Intersect(GetLiveOutSet(block)); | |
| 342 } | |
| 332 assigned_vars_.Add(kill); | 343 assigned_vars_.Add(kill); |
| 333 } | 344 } |
| 334 | 345 |
| 335 return assigned_vars_; | 346 return assigned_vars_; |
| 336 } | 347 } |
| 337 | 348 |
| 338 // Returns true if the value set by the given store reaches any load from the | 349 // Returns true if the value set by the given store reaches any load from the |
| 339 // same local variable. | 350 // same local variable. |
| 340 bool IsStoreAlive(BlockEntryInstr* block, StoreLocalInstr* store) { | 351 bool IsStoreAlive(BlockEntryInstr* block, StoreLocalInstr* store) { |
| 341 if (store->is_dead()) { | 352 if (store->is_dead()) { |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 370 const intptr_t block_count = postorder_.length(); | 381 const intptr_t block_count = postorder_.length(); |
| 371 | 382 |
| 372 BitVector* last_loads = new BitVector(variable_count_); | 383 BitVector* last_loads = new BitVector(variable_count_); |
| 373 for (intptr_t i = 0; i < block_count; i++) { | 384 for (intptr_t i = 0; i < block_count; i++) { |
| 374 BlockEntryInstr* block = postorder_[i]; | 385 BlockEntryInstr* block = postorder_[i]; |
| 375 | 386 |
| 376 BitVector* kill = kill_[i]; | 387 BitVector* kill = kill_[i]; |
| 377 BitVector* live_in = live_in_[i]; | 388 BitVector* live_in = live_in_[i]; |
| 378 last_loads->Clear(); | 389 last_loads->Clear(); |
| 379 | 390 |
| 391 // Inside try-catch all locals are live. | |
|
Kevin Millikin (Google)
2013/05/08 11:42:00
Again, I don't understand why. This is just for a
Florian Schneider
2013/05/08 17:10:55
Done.
| |
| 392 if (block->try_index() != CatchClauseNode::kInvalidTryIndex) { | |
| 393 live_in->SetAll(); | |
| 394 continue; | |
| 395 } | |
| 396 | |
| 380 // Iterate backwards starting at the last instruction. | 397 // Iterate backwards starting at the last instruction. |
| 381 for (BackwardInstructionIterator it(block); !it.Done(); it.Advance()) { | 398 for (BackwardInstructionIterator it(block); !it.Done(); it.Advance()) { |
| 382 Instruction* current = it.Current(); | 399 Instruction* current = it.Current(); |
| 383 | 400 |
| 384 LoadLocalInstr* load = current->AsLoadLocal(); | 401 LoadLocalInstr* load = current->AsLoadLocal(); |
| 385 if (load != NULL) { | 402 if (load != NULL) { |
| 386 const intptr_t index = load->local().BitIndexIn(num_non_copied_params_); | 403 const intptr_t index = load->local().BitIndexIn(num_non_copied_params_); |
| 387 live_in->Add(index); | 404 live_in->Add(index); |
| 388 if (!last_loads->Contains(index)) { | 405 if (!last_loads->Contains(index) && !load->local().always_live()) { |
| 389 last_loads->Add(index); | 406 last_loads->Add(index); |
| 390 load->mark_last(); | 407 load->mark_last(); |
| 391 } | 408 } |
| 392 continue; | 409 continue; |
| 393 } | 410 } |
| 394 | 411 |
| 395 StoreLocalInstr* store = current->AsStoreLocal(); | 412 StoreLocalInstr* store = current->AsStoreLocal(); |
| 396 if (store != NULL) { | 413 if (store != NULL && !store->local().always_live()) { |
| 397 const intptr_t index = | 414 const intptr_t index = |
| 398 store->local().BitIndexIn(num_non_copied_params_); | 415 store->local().BitIndexIn(num_non_copied_params_); |
| 399 if (kill->Contains(index)) { | 416 if (kill->Contains(index)) { |
| 400 if (!live_in->Contains(index)) { | 417 if (!live_in->Contains(index)) { |
| 401 store->mark_dead(); | 418 store->mark_dead(); |
| 402 } | 419 } |
| 403 } else { | 420 } else { |
| 404 if (!live_in->Contains(index)) { | 421 if (!live_in->Contains(index)) { |
| 405 store->mark_last(); | 422 store->mark_last(); |
| 406 } | 423 } |
| 407 kill->Add(index); | 424 kill->Add(index); |
| 408 } | 425 } |
| 409 live_in->Remove(index); | 426 if (block->try_index() == CatchClauseNode::kInvalidTryIndex) { |
|
Kevin Millikin (Google)
2013/05/08 11:42:00
This comparison is always true. The loop is not e
Florian Schneider
2013/05/08 17:10:55
Done.
| |
| 427 live_in->Remove(index); | |
| 428 } | |
| 410 continue; | 429 continue; |
| 411 } | 430 } |
| 412 } | 431 } |
| 413 } | 432 } |
| 414 } | 433 } |
| 415 | 434 |
| 416 | 435 |
| 417 void FlowGraph::ComputeSSA( | 436 void FlowGraph::ComputeSSA( |
| 418 intptr_t next_virtual_register_number, | 437 intptr_t next_virtual_register_number, |
| 419 ZoneGrowableArray<Definition*>* inlining_parameters) { | 438 ZoneGrowableArray<Definition*>* inlining_parameters) { |
| (...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 609 } | 628 } |
| 610 } | 629 } |
| 611 } | 630 } |
| 612 } | 631 } |
| 613 } | 632 } |
| 614 | 633 |
| 615 | 634 |
| 616 void FlowGraph::Rename(GrowableArray<PhiInstr*>* live_phis, | 635 void FlowGraph::Rename(GrowableArray<PhiInstr*>* live_phis, |
| 617 VariableLivenessAnalysis* variable_liveness, | 636 VariableLivenessAnalysis* variable_liveness, |
| 618 ZoneGrowableArray<Definition*>* inlining_parameters) { | 637 ZoneGrowableArray<Definition*>* inlining_parameters) { |
| 619 // TODO(fschneider): Support catch-entry. | 638 if (!FLAG_optimize_try_catch && (graph_entry_->SuccessorCount() > 1)) { |
| 620 if (graph_entry_->SuccessorCount() > 1) { | |
| 621 Bailout("Catch-entry support in SSA."); | 639 Bailout("Catch-entry support in SSA."); |
| 622 } | 640 } |
| 623 | 641 |
| 624 // Initial renaming environment. | 642 // Initial renaming environment. |
| 625 GrowableArray<Definition*> env(variable_count()); | 643 GrowableArray<Definition*> env(variable_count()); |
| 626 | 644 |
| 627 // Add global constants to the initial definitions. | 645 // Add global constants to the initial definitions. |
| 628 constant_null_ = | 646 constant_null_ = |
| 629 AddConstantToInitialDefinitions(Object::ZoneHandle()); | 647 AddConstantToInitialDefinitions(Object::ZoneHandle()); |
| 630 | 648 |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 646 AddToInitialDefinitions(param); | 664 AddToInitialDefinitions(param); |
| 647 env.Add(param); | 665 env.Add(param); |
| 648 } | 666 } |
| 649 } | 667 } |
| 650 | 668 |
| 651 // Initialize all locals with #null in the renaming environment. | 669 // Initialize all locals with #null in the renaming environment. |
| 652 for (intptr_t i = parameter_count(); i < variable_count(); ++i) { | 670 for (intptr_t i = parameter_count(); i < variable_count(); ++i) { |
| 653 env.Add(constant_null()); | 671 env.Add(constant_null()); |
| 654 } | 672 } |
| 655 | 673 |
| 656 BlockEntryInstr* normal_entry = graph_entry_->SuccessorAt(0); | 674 if (graph_entry_->SuccessorCount() > 1) { |
| 657 ASSERT(normal_entry != NULL); // Must have entry. | 675 // Functions with try-catch have a fixed area of stack slots reserved |
| 658 RenameRecursive(normal_entry, &env, live_phis, variable_liveness); | 676 // so that all local variables are stored at a known location when |
| 677 // on entry to the catch. | |
| 678 graph_entry_->set_fixed_slot_count( | |
| 679 num_stack_locals() + num_copied_params()); | |
| 680 } | |
| 681 RenameRecursive(graph_entry_, &env, live_phis, variable_liveness); | |
| 659 } | 682 } |
| 660 | 683 |
| 661 | 684 |
| 662 void FlowGraph::AttachEnvironment(Instruction* instr, | 685 void FlowGraph::AttachEnvironment(Instruction* instr, |
| 663 GrowableArray<Definition*>* env) { | 686 GrowableArray<Definition*>* env) { |
| 664 Environment* deopt_env = | 687 Environment* deopt_env = |
| 665 Environment::From(*env, | 688 Environment::From(*env, |
| 666 num_non_copied_params_, | 689 num_non_copied_params_, |
| 667 parsed_function_.function()); | 690 parsed_function_.function()); |
| 668 instr->SetEnvironment(deopt_env); | 691 instr->SetEnvironment(deopt_env); |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 682 VariableLivenessAnalysis* variable_liveness) { | 705 VariableLivenessAnalysis* variable_liveness) { |
| 683 // 1. Process phis first. | 706 // 1. Process phis first. |
| 684 if (block_entry->IsJoinEntry()) { | 707 if (block_entry->IsJoinEntry()) { |
| 685 JoinEntryInstr* join = block_entry->AsJoinEntry(); | 708 JoinEntryInstr* join = block_entry->AsJoinEntry(); |
| 686 if (join->phis() != NULL) { | 709 if (join->phis() != NULL) { |
| 687 for (intptr_t i = 0; i < join->phis()->length(); ++i) { | 710 for (intptr_t i = 0; i < join->phis()->length(); ++i) { |
| 688 PhiInstr* phi = (*join->phis())[i]; | 711 PhiInstr* phi = (*join->phis())[i]; |
| 689 if (phi != NULL) { | 712 if (phi != NULL) { |
| 690 (*env)[i] = phi; | 713 (*env)[i] = phi; |
| 691 phi->set_ssa_temp_index(alloc_ssa_temp_index()); // New SSA temp. | 714 phi->set_ssa_temp_index(alloc_ssa_temp_index()); // New SSA temp. |
| 715 if (block_entry->try_index() != CatchClauseNode::kInvalidTryIndex) { | |
|
Kevin Millikin (Google)
2013/05/08 11:42:00
This comparison is repeated a lot in this change.
Florian Schneider
2013/05/08 17:10:55
Done.
| |
| 716 phi->mark_alive(); | |
| 717 live_phis->Add(phi); | |
| 718 } | |
| 692 } | 719 } |
| 693 } | 720 } |
| 694 } | 721 } |
| 722 } else if (block_entry->IsCatchBlockEntry()) { | |
| 723 // Add real definitions for all locals and parameters. The location of the | |
| 724 // parameters and locals is the same as at function entry. | |
|
Kevin Millikin (Google)
2013/05/08 11:42:00
I don't think this comment is quite correct. The
Florian Schneider
2013/05/08 17:10:55
Done.
| |
| 725 for (intptr_t i = 0; i < env->length(); ++i) { | |
| 726 ParameterInstr* param = new ParameterInstr(i, block_entry); | |
| 727 param->set_ssa_temp_index(alloc_ssa_temp_index()); // New SSA temp. | |
| 728 (*env)[i] = param; | |
| 729 block_entry->AsCatchBlockEntry()->initial_definitions()->Add(param); | |
| 730 } | |
| 695 } | 731 } |
| 696 | 732 |
| 697 // Attach environment to the block entry. | 733 // Attach environment to the block entry. |
| 698 AttachEnvironment(block_entry, env); | 734 AttachEnvironment(block_entry, env); |
| 699 | 735 |
| 700 // 2. Process normal instructions. | 736 // 2. Process normal instructions. |
| 701 | 737 |
| 702 for (ForwardInstructionIterator it(block_entry); !it.Done(); it.Advance()) { | 738 for (ForwardInstructionIterator it(block_entry); !it.Done(); it.Advance()) { |
| 703 Instruction* current = it.Current(); | 739 Instruction* current = it.Current(); |
| 704 | 740 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 743 LoadLocalInstr* load = definition->AsLoadLocal(); | 779 LoadLocalInstr* load = definition->AsLoadLocal(); |
| 744 StoreLocalInstr* store = definition->AsStoreLocal(); | 780 StoreLocalInstr* store = definition->AsStoreLocal(); |
| 745 if ((load != NULL) || (store != NULL)) { | 781 if ((load != NULL) || (store != NULL)) { |
| 746 intptr_t index; | 782 intptr_t index; |
| 747 Definition* result; | 783 Definition* result; |
| 748 if (store != NULL) { | 784 if (store != NULL) { |
| 749 // Update renaming environment. | 785 // Update renaming environment. |
| 750 index = store->local().BitIndexIn(num_non_copied_params_); | 786 index = store->local().BitIndexIn(num_non_copied_params_); |
| 751 result = store->value()->definition(); | 787 result = store->value()->definition(); |
| 752 | 788 |
| 753 if (variable_liveness->IsStoreAlive(block_entry, store)) { | 789 if (variable_liveness->IsStoreAlive(block_entry, store) || |
| 790 store->local().always_live()) { | |
|
Kevin Millikin (Google)
2013/05/08 11:42:00
Need to know why, also below.
These are for the t
Florian Schneider
2013/05/08 17:10:55
Done. Not needed anymore.
| |
| 754 (*env)[index] = result; | 791 (*env)[index] = result; |
| 755 } else { | 792 } else { |
| 756 (*env)[index] = constant_null(); | 793 (*env)[index] = constant_null(); |
| 757 } | 794 } |
| 758 } else { | 795 } else { |
| 759 // The graph construction ensures we do not have an unused LoadLocal | 796 // The graph construction ensures we do not have an unused LoadLocal |
| 760 // computation. | 797 // computation. |
| 761 ASSERT(definition->is_used()); | 798 ASSERT(definition->is_used()); |
| 762 index = load->local().BitIndexIn(num_non_copied_params_); | 799 index = load->local().BitIndexIn(num_non_copied_params_); |
| 763 result = (*env)[index]; | 800 result = (*env)[index]; |
| 764 | 801 |
| 765 PhiInstr* phi = result->AsPhi(); | 802 PhiInstr* phi = result->AsPhi(); |
| 766 if ((phi != NULL) && !phi->is_alive()) { | 803 if ((phi != NULL) && !phi->is_alive()) { |
| 767 phi->mark_alive(); | 804 phi->mark_alive(); |
| 768 live_phis->Add(phi); | 805 live_phis->Add(phi); |
| 769 } | 806 } |
| 770 | 807 |
| 771 if (variable_liveness->IsLastLoad(block_entry, load)) { | 808 if (variable_liveness->IsLastLoad(block_entry, load) && |
| 809 !load->local().always_live()) { | |
|
Kevin Millikin (Google)
2013/05/08 11:42:00
Is this because there are implicit loads from the
Florian Schneider
2013/05/08 17:10:55
Done. Not needed anymore.
| |
| 772 (*env)[index] = constant_null(); | 810 (*env)[index] = constant_null(); |
| 773 } | 811 } |
| 774 } | 812 } |
| 775 // Update expression stack or remove from graph. | 813 // Update expression stack or remove from graph. |
| 776 if (definition->is_used()) { | 814 if (definition->is_used()) { |
| 777 env->Add(result); | 815 env->Add(result); |
| 778 // We remove load/store instructions when we find their use in 2a. | 816 // We remove load/store instructions when we find their use in 2a. |
| 779 } else { | 817 } else { |
| 780 it.RemoveCurrentFromGraph(); | 818 it.RemoveCurrentFromGraph(); |
| 781 } | 819 } |
| (...skipping 270 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1052 } | 1090 } |
| 1053 | 1091 |
| 1054 | 1092 |
| 1055 bool BlockEffects::IsSideEffectFreePath(BlockEntryInstr* from, | 1093 bool BlockEffects::IsSideEffectFreePath(BlockEntryInstr* from, |
| 1056 BlockEntryInstr* to) const { | 1094 BlockEntryInstr* to) const { |
| 1057 return available_at_[to->postorder_number()]->Contains( | 1095 return available_at_[to->postorder_number()]->Contains( |
| 1058 from->postorder_number()); | 1096 from->postorder_number()); |
| 1059 } | 1097 } |
| 1060 | 1098 |
| 1061 } // namespace dart | 1099 } // namespace dart |
| OLD | NEW |