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" |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 23 current_ssa_temp_index_(0), | 23 current_ssa_temp_index_(0), |
| 24 max_block_id_(max_block_id), | 24 max_block_id_(max_block_id), |
| 25 parsed_function_(builder.parsed_function()), | 25 parsed_function_(builder.parsed_function()), |
| 26 num_copied_params_(builder.num_copied_params()), | 26 num_copied_params_(builder.num_copied_params()), |
| 27 num_non_copied_params_(builder.num_non_copied_params()), | 27 num_non_copied_params_(builder.num_non_copied_params()), |
| 28 num_stack_locals_(builder.num_stack_locals()), | 28 num_stack_locals_(builder.num_stack_locals()), |
| 29 graph_entry_(graph_entry), | 29 graph_entry_(graph_entry), |
| 30 preorder_(), | 30 preorder_(), |
| 31 postorder_(), | 31 postorder_(), |
| 32 reverse_postorder_(), | 32 reverse_postorder_(), |
| 33 exits_(NULL) { | 33 exits_(NULL), |
| 34 invalid_dominator_tree_(true) { | |
| 34 DiscoverBlocks(); | 35 DiscoverBlocks(); |
| 35 } | 36 } |
| 36 | 37 |
| 37 | 38 |
| 38 void FlowGraph::DiscoverBlocks() { | 39 void FlowGraph::DiscoverBlocks() { |
| 39 // Initialize state. | 40 // Initialize state. |
| 40 preorder_.Clear(); | 41 preorder_.Clear(); |
| 41 postorder_.Clear(); | 42 postorder_.Clear(); |
| 42 reverse_postorder_.Clear(); | 43 reverse_postorder_.Clear(); |
| 43 parent_.Clear(); | 44 parent_.Clear(); |
| (...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 297 | 298 |
| 298 // Compute immediate dominators and the dominance frontier for each basic | 299 // Compute immediate dominators and the dominance frontier for each basic |
| 299 // block. As a side effect of the algorithm, sets the immediate dominator | 300 // block. As a side effect of the algorithm, sets the immediate dominator |
| 300 // of each basic block. | 301 // of each basic block. |
| 301 // | 302 // |
| 302 // dominance_frontier: an output parameter encoding the dominance frontier. | 303 // dominance_frontier: an output parameter encoding the dominance frontier. |
| 303 // The array maps the preorder block number of a block to the set of | 304 // The array maps the preorder block number of a block to the set of |
| 304 // (preorder block numbers of) blocks in the dominance frontier. | 305 // (preorder block numbers of) blocks in the dominance frontier. |
| 305 void FlowGraph::ComputeDominators( | 306 void FlowGraph::ComputeDominators( |
| 306 GrowableArray<BitVector*>* dominance_frontier) { | 307 GrowableArray<BitVector*>* dominance_frontier) { |
| 308 invalid_dominator_tree_ = false; | |
| 307 // Use the SEMI-NCA algorithm to compute dominators. This is a two-pass | 309 // Use the SEMI-NCA algorithm to compute dominators. This is a two-pass |
| 308 // version of the Lengauer-Tarjan algorithm (LT is normally three passes) | 310 // version of the Lengauer-Tarjan algorithm (LT is normally three passes) |
| 309 // that eliminates a pass by using nearest-common ancestor (NCA) to | 311 // that eliminates a pass by using nearest-common ancestor (NCA) to |
| 310 // compute immediate dominators from semidominators. It also removes a | 312 // compute immediate dominators from semidominators. It also removes a |
| 311 // level of indirection in the link-eval forest data structure. | 313 // level of indirection in the link-eval forest data structure. |
| 312 // | 314 // |
| 313 // The algorithm is described in Georgiadis, Tarjan, and Werneck's | 315 // The algorithm is described in Georgiadis, Tarjan, and Werneck's |
| 314 // "Finding Dominators in Practice". | 316 // "Finding Dominators in Practice". |
| 315 // See http://www.cs.princeton.edu/~rwerneck/dominators/ . | 317 // See http://www.cs.princeton.edu/~rwerneck/dominators/ . |
| 316 | 318 |
| (...skipping 401 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 718 const char* function_name = parsed_function_.function().ToCString(); | 720 const char* function_name = parsed_function_.function().ToCString(); |
| 719 intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1; | 721 intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1; |
| 720 char* chars = Isolate::Current()->current_zone()->Alloc<char>(len); | 722 char* chars = Isolate::Current()->current_zone()->Alloc<char>(len); |
| 721 OS::SNPrint(chars, len, kFormat, function_name, reason); | 723 OS::SNPrint(chars, len, kFormat, function_name, reason); |
| 722 const Error& error = Error::Handle( | 724 const Error& error = Error::Handle( |
| 723 LanguageError::New(String::Handle(String::New(chars)))); | 725 LanguageError::New(String::Handle(String::New(chars)))); |
| 724 Isolate::Current()->long_jump_base()->Jump(1, error); | 726 Isolate::Current()->long_jump_base()->Jump(1, error); |
| 725 } | 727 } |
| 726 | 728 |
| 727 | 729 |
| 728 // Helper to reorder phis after splitting a block. The last instruction(s) of | 730 // Helper to reorder predecessors and phis after splitting a block. For each |
|
Kevin Millikin (Google)
2012/10/22 09:10:37
Comment needs some updating---it's no longer calle
zerny-google
2012/10/22 13:21:31
Done.
| |
| 729 // the split block will now have a larger block id than any previously known | 731 // successor of 'old_block', the predecessors will be reordered to preserve |
| 730 // blocks. If the last instruction jumps to a join, we must reorder phi inputs | 732 // block order sorting. If the successor is a join, also the phi inputs will be |
| 731 // according to the block order, ie, we move this predecessor to the end. | 733 // updated. |
| 732 static void ReorderPhis(BlockEntryInstr* block) { | 734 // Prerequisite: the last-instruction pointers must not have been updated yet. |
| 733 GotoInstr* jump = block->last_instruction()->AsGoto(); | 735 void FlowGraph::ReorderPredecessors(BlockEntryInstr* old_block, |
|
Kevin Millikin (Google)
2012/10/22 09:10:37
I find the naming and emphasis (on reordering) a b
zerny-google
2012/10/22 13:21:31
I renamed it to ReplaceBlock, but that is not enti
| |
| 736 BlockEntryInstr* new_block) { | |
| 737 // If the last instruction is a branch, update the predecessor of the targets. | |
| 738 BranchInstr* branch = old_block->last_instruction()->AsBranch(); | |
|
Kevin Millikin (Google)
2012/10/22 09:10:37
It doesn't make a huge difference, but this code c
zerny-google
2012/10/22 13:21:31
Done.
| |
| 739 if (branch != NULL) { | |
| 740 for (intptr_t i = 0; i < branch->SuccessorCount(); ++i) { | |
| 741 TargetEntryInstr* target = branch->SuccessorAt(i)->AsTargetEntry(); | |
| 742 ASSERT(target != NULL); | |
| 743 target->predecessor_ = new_block; | |
| 744 } | |
| 745 return; | |
| 746 } | |
| 747 // If the last instruction is a jump, update the join's predecessors and phis. | |
| 748 GotoInstr* jump = old_block->last_instruction()->AsGoto(); | |
| 734 if (jump == NULL) return; | 749 if (jump == NULL) return; |
| 750 // Find the old predecessor index | |
| 735 JoinEntryInstr* join = jump->successor(); | 751 JoinEntryInstr* join = jump->successor(); |
| 736 intptr_t pred_index = join->IndexOfPredecessor(block); | 752 intptr_t pred_index = join->IndexOfPredecessor(old_block); |
| 737 intptr_t pred_count = join->PredecessorCount(); | 753 intptr_t pred_count = join->PredecessorCount(); |
| 738 ASSERT(pred_index >= 0); | 754 ASSERT(pred_index >= 0); |
| 739 ASSERT(pred_index < pred_count); | 755 ASSERT(pred_index < pred_count); |
| 740 // If the predecessor index is the last index there is nothing to update. | 756 // Find the new predecessor index |
|
Kevin Millikin (Google)
2012/10/22 09:10:37
It seems like we could do the search and move in t
zerny-google
2012/10/22 13:21:31
Done.
| |
| 741 if ((join->phis() == NULL) || (pred_index + 1 == pred_count)) return; | 757 intptr_t new_block_id = new_block->block_id(); |
| 742 // Otherwise, move the predecessor use to the end in each phi. | 758 intptr_t new_pred_index = -1; |
|
Kevin Millikin (Google)
2012/10/22 09:10:37
Is it simpler to understand this if you shift the
zerny-google
2012/10/22 13:21:31
No longer an issue with the new "find predecessor
| |
| 759 while (new_pred_index < pred_count - 1) { | |
| 760 ++new_pred_index; | |
| 761 if (join->predecessors_[new_pred_index]->block_id() > new_block_id) break; | |
| 762 } | |
| 763 // Reorder the predecessors of the join. | |
| 764 intptr_t step = (new_pred_index > pred_index) ? 1 : -1; | |
| 765 intptr_t i = pred_index; | |
| 766 while (i != new_pred_index) { | |
|
zerny-google
2012/10/12 11:52:05
I'll rewrite this to a for loop again (still using
| |
| 767 join->predecessors_[i] = join->predecessors_[i + step]; | |
| 768 i += step; | |
| 769 } | |
| 770 join->predecessors_[new_pred_index] = new_block; | |
| 771 // If the new and old predecessor index match there is nothing to update. | |
| 772 if ((join->phis() == NULL) || (pred_index == new_pred_index)) return; | |
|
Kevin Millikin (Google)
2012/10/22 09:10:37
Can't you can skip the reordering of the predecess
zerny-google
2012/10/22 13:21:31
No, we still need to write the new_block at the ne
| |
| 773 // Otherwise, reorder the predecessor uses in each phi. | |
| 743 for (intptr_t i = 0; i < join->phis()->length(); ++i) { | 774 for (intptr_t i = 0; i < join->phis()->length(); ++i) { |
| 744 PhiInstr* phi = (*join->phis())[i]; | 775 PhiInstr* phi = (*join->phis())[i]; |
| 745 if (phi == NULL) continue; | 776 if (phi == NULL) continue; |
| 746 ASSERT(pred_count == phi->InputCount()); | 777 ASSERT(pred_count == phi->InputCount()); |
| 747 // Save the predecessor use. | 778 // Save the predecessor use. |
| 748 Value* pred_use = phi->InputAt(pred_index); | 779 Value* pred_use = phi->InputAt(pred_index); |
| 749 // Move each of the following uses back by one. | 780 // Move uses between old and new. |
| 750 ASSERT(pred_index < pred_count - 1); // Will move at least one index. | 781 intptr_t use_idx = pred_index; |
| 751 for (intptr_t i = pred_index; i < pred_count - 1; ++i) { | 782 while (use_idx != new_pred_index) { |
|
zerny-google
2012/10/12 11:52:05
I'll rewrite this to a for loop again (still using
| |
| 752 Value* use = phi->InputAt(i + 1); | 783 Value* use = phi->InputAt(use_idx + step); |
| 753 phi->SetInputAt(i, use); | 784 phi->SetInputAt(use_idx, use); |
| 754 use->set_use_index(i); | 785 use->set_use_index(use_idx); |
| 786 use_idx += step; | |
| 755 } | 787 } |
| 756 // Write the predecessor use at the end. | 788 // Write the predecessor use. |
| 757 phi->SetInputAt(pred_count - 1, pred_use); | 789 phi->SetInputAt(new_pred_index, pred_use); |
| 758 pred_use->set_use_index(pred_count - 1); | 790 pred_use->set_use_index(new_pred_index); |
| 759 } | 791 } |
| 760 } | 792 } |
| 761 | 793 |
| 762 | 794 |
| 763 // Helper to sort a list of blocks. | 795 // Helper to sort a list of blocks. |
| 764 static int LowestBlockIdFirst(BlockEntryInstr* const* a, | 796 static int LowestBlockIdFirst(BlockEntryInstr* const* a, |
| 765 BlockEntryInstr* const* b) { | 797 BlockEntryInstr* const* b) { |
| 766 return (*a)->block_id() - (*b)->block_id(); | 798 return (*a)->block_id() - (*b)->block_id(); |
| 767 } | 799 } |
| 768 | 800 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 813 if (callee_exits->is_empty()) { | 845 if (callee_exits->is_empty()) { |
| 814 // TODO(zerny): Add support for non-local exits, such as throw. | 846 // TODO(zerny): Add support for non-local exits, such as throw. |
| 815 UNREACHABLE(); | 847 UNREACHABLE(); |
| 816 } else if (callee_exits->length() == 1) { | 848 } else if (callee_exits->length() == 1) { |
| 817 ReturnInstr* exit = (*callee_exits)[0]; | 849 ReturnInstr* exit = (*callee_exits)[0]; |
| 818 ASSERT(exit->previous() != NULL); | 850 ASSERT(exit->previous() != NULL); |
| 819 // For just one exit, replace the uses and remove the call from the graph. | 851 // For just one exit, replace the uses and remove the call from the graph. |
| 820 call->ReplaceUsesWith(exit->value()->definition()); | 852 call->ReplaceUsesWith(exit->value()->definition()); |
| 821 call->previous()->LinkTo(callee_entry->next()); | 853 call->previous()->LinkTo(callee_entry->next()); |
| 822 exit->previous()->LinkTo(call->next()); | 854 exit->previous()->LinkTo(call->next()); |
| 823 // In case of control flow, locally update the dominator tree. | 855 // In case of control flow, locally update the predecessors, phis and |
| 856 // dominator tree. | |
| 857 // TODO(zerny): should we leave the dominator tree since we recompute it | |
| 858 // after a full inlining pass. | |
| 824 if (callee_graph->preorder().length() > 2) { | 859 if (callee_graph->preorder().length() > 2) { |
| 825 // The caller block is split and the new block id is that of the exit | 860 BlockEntryInstr* exit_block = exit->GetBlock(); |
| 826 // block. If the caller block had outgoing edges, reorder the phis so they | 861 // The caller block is split and the predecessors might need reordering. |
|
Kevin Millikin (Google)
2012/10/22 09:10:37
'the predecessors' is unclear. "The caller block
zerny-google
2012/10/22 13:21:31
Done.
| |
| 827 // are still ordered by block id. | 862 // The caller entry becomes the block of the callee entry block. |
|
Kevin Millikin (Google)
2012/10/22 09:10:37
I don't think I understand this comment at all :)
zerny-google
2012/10/22 13:21:31
Done.
| |
| 828 ReorderPhis(caller_entry); | 863 ReorderPredecessors(callee_entry, caller_entry); |
| 864 // The callee exit block becomes the block of the caller entry block. | |
| 865 ReorderPredecessors(caller_entry, exit_block); | |
| 829 // The callee return is now the immediate dominator of blocks whose | 866 // The callee return is now the immediate dominator of blocks whose |
| 830 // immediate dominator was the caller entry. | 867 // immediate dominator was the caller entry. |
| 831 BlockEntryInstr* exit_block = exit->GetBlock(); | |
| 832 ASSERT(exit_block->dominated_blocks().is_empty()); | 868 ASSERT(exit_block->dominated_blocks().is_empty()); |
| 833 for (intptr_t i = 0; i < caller_entry->dominated_blocks().length(); ++i) { | 869 for (intptr_t i = 0; i < caller_entry->dominated_blocks().length(); ++i) { |
| 834 BlockEntryInstr* block = caller_entry->dominated_blocks()[i]; | 870 BlockEntryInstr* block = caller_entry->dominated_blocks()[i]; |
| 835 block->set_dominator(exit_block); | 871 block->set_dominator(exit_block); |
| 836 exit_block->AddDominatedBlock(block); | 872 exit_block->AddDominatedBlock(block); |
| 837 } | 873 } |
| 838 // The caller entry is now the immediate dominator of blocks whose | 874 // The caller entry is now the immediate dominator of blocks whose |
| 839 // immediate dominator was the callee entry. | 875 // immediate dominator was the callee entry. |
| 840 caller_entry->ClearDominatedBlocks(); | 876 caller_entry->ClearDominatedBlocks(); |
| 841 for (intptr_t i = 0; i < callee_entry->dominated_blocks().length(); ++i) { | 877 for (intptr_t i = 0; i < callee_entry->dominated_blocks().length(); ++i) { |
| 842 BlockEntryInstr* block = callee_entry->dominated_blocks()[i]; | 878 BlockEntryInstr* block = callee_entry->dominated_blocks()[i]; |
| 843 block->set_dominator(caller_entry); | 879 block->set_dominator(caller_entry); |
| 844 caller_entry->AddDominatedBlock(block); | 880 caller_entry->AddDominatedBlock(block); |
| 845 } | 881 } |
| 846 // Recompute the block orders. | 882 // Update the last-instruction pointers. |
| 847 DiscoverBlocks(); | 883 exit_block->set_last_instruction(caller_entry->last_instruction()); |
|
Kevin Millikin (Google)
2012/10/22 09:10:37
It seems like this could also be moved into Reorde
zerny-google
2012/10/22 13:21:31
Done.
| |
| 884 caller_entry->set_last_instruction(callee_entry->last_instruction()); | |
| 848 } | 885 } |
| 849 } else { | 886 } else { |
| 850 // Sort the list of exits by block id. | 887 // Sort the list of exits by block id. |
| 851 GrowableArray<BlockEntryInstr*> exits(callee_exits->length()); | 888 GrowableArray<BlockEntryInstr*> exits(callee_exits->length()); |
| 852 for (intptr_t i = 0; i < callee_exits->length(); ++i) { | 889 for (intptr_t i = 0; i < callee_exits->length(); ++i) { |
| 853 exits.Add((*callee_exits)[i]->GetBlock()); | 890 exits.Add((*callee_exits)[i]->GetBlock()); |
| 854 } | 891 } |
| 855 exits.Sort(LowestBlockIdFirst); | 892 exits.Sort(LowestBlockIdFirst); |
| 856 // Create a join of the returns. | 893 // Create a join of the returns. |
| 857 JoinEntryInstr* join = | 894 JoinEntryInstr* join = |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 877 ReturnInstr* exit_instr = exits[i]->last_instruction()->AsReturn(); | 914 ReturnInstr* exit_instr = exits[i]->last_instruction()->AsReturn(); |
| 878 ASSERT(exit_instr != NULL); | 915 ASSERT(exit_instr != NULL); |
| 879 Value* use = exit_instr->value(); | 916 Value* use = exit_instr->value(); |
| 880 phi->SetInputAt(i, use); | 917 phi->SetInputAt(i, use); |
| 881 use->set_instruction(phi); | 918 use->set_instruction(phi); |
| 882 use->set_use_index(i); | 919 use->set_use_index(i); |
| 883 } | 920 } |
| 884 // Replace uses of the call with the phi. | 921 // Replace uses of the call with the phi. |
| 885 call->ReplaceUsesWith(phi); | 922 call->ReplaceUsesWith(phi); |
| 886 } | 923 } |
| 887 // Remove the call from the graph. | 924 // Remove the call from the graph. |
| 888 call->previous()->LinkTo(callee_entry->next()); | 925 call->previous()->LinkTo(callee_entry->next()); |
| 889 join->LinkTo(call->next()); | 926 join->LinkTo(call->next()); |
| 890 // The caller block is split and the new block id is that of the join | 927 // The caller block is split and the predecessors might need reordering. |
| 891 // block. If the caller block had outgoing edges, reorder the phis so they | 928 // The caller entry becomes the block of the callee entry block. |
| 892 // are still ordered by block id. | 929 ReorderPredecessors(callee_entry, caller_entry); |
| 893 ReorderPhis(caller_entry); | 930 // The join block becomes the block of the caller entry block. |
| 894 // Adjust pre/post orders and update the dominator tree. | 931 ReorderPredecessors(caller_entry, join); |
| 895 DiscoverBlocks(); | 932 // Update the last instruction pointers. |
| 933 join->set_last_instruction(caller_entry->last_instruction()); | |
| 934 caller_entry->set_last_instruction(callee_entry->last_instruction()); | |
| 935 for (intptr_t i = 0; i < exits.length(); ++i) { | |
| 936 exits[i]->set_last_instruction( | |
| 937 exits[i]->last_instruction()->previous()->next()); | |
| 938 } | |
| 939 // Mark that the dominator tree is invalid. | |
| 896 // TODO(zerny): Compute the dominator frontier locally. | 940 // TODO(zerny): Compute the dominator frontier locally. |
| 941 invalid_dominator_tree_ = true; | |
| 942 } | |
| 943 } | |
| 944 | |
| 945 | |
| 946 void FlowGraph::RepairGraphAfterInlining() { | |
| 947 DiscoverBlocks(); | |
| 948 if (invalid_dominator_tree_) { | |
| 897 GrowableArray<BitVector*> dominance_frontier; | 949 GrowableArray<BitVector*> dominance_frontier; |
| 898 ComputeDominators(&dominance_frontier); | 950 ComputeDominators(&dominance_frontier); |
| 899 } | 951 } |
| 900 } | 952 } |
| 901 | 953 |
| 902 | 954 |
| 903 intptr_t FlowGraph::InstructionCount() const { | 955 intptr_t FlowGraph::InstructionCount() const { |
| 904 intptr_t size = 0; | 956 intptr_t size = 0; |
| 905 // Iterate each block, skipping the graph entry. | 957 // Iterate each block, skipping the graph entry. |
| 906 for (intptr_t i = 1; i < preorder_.length(); ++i) { | 958 for (intptr_t i = 1; i < preorder_.length(); ++i) { |
| 907 for (ForwardInstructionIterator it(preorder_[i]); | 959 for (ForwardInstructionIterator it(preorder_[i]); |
| 908 !it.Done(); | 960 !it.Done(); |
| 909 it.Advance()) { | 961 it.Advance()) { |
| 910 ++size; | 962 ++size; |
| 911 } | 963 } |
| 912 } | 964 } |
| 913 return size; | 965 return size; |
| 914 } | 966 } |
| 915 | 967 |
| 916 | 968 |
| 917 } // namespace dart | 969 } // namespace dart |
| OLD | NEW |