| 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), | |
| 34 invalid_dominator_tree_(true) { | 33 invalid_dominator_tree_(true) { |
| 35 DiscoverBlocks(); | 34 DiscoverBlocks(); |
| 36 } | 35 } |
| 37 | 36 |
| 38 | 37 |
| 39 ConstantInstr* FlowGraph::AddConstantToInitialDefinitions( | 38 ConstantInstr* FlowGraph::AddConstantToInitialDefinitions( |
| 40 const Object& object) { | 39 const Object& object) { |
| 41 // Check if the constant is already in the pool. | 40 // Check if the constant is already in the pool. |
| 42 for (intptr_t i = 0; i < graph_entry_->initial_definitions()->length(); ++i) { | 41 for (intptr_t i = 0; i < graph_entry_->initial_definitions()->length(); ++i) { |
| 43 ConstantInstr* constant = | 42 ConstantInstr* constant = |
| (...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 202 ValidateUseListsInInstruction(it.Current()); | 201 ValidateUseListsInInstruction(it.Current()); |
| 203 } | 202 } |
| 204 } | 203 } |
| 205 return true; // Return true so we can ASSERT validation. | 204 return true; // Return true so we can ASSERT validation. |
| 206 } | 205 } |
| 207 #endif // DEBUG | 206 #endif // DEBUG |
| 208 | 207 |
| 209 | 208 |
| 210 static void ClearUseLists(Definition* defn) { | 209 static void ClearUseLists(Definition* defn) { |
| 211 ASSERT(defn != NULL); | 210 ASSERT(defn != NULL); |
| 212 ASSERT(defn->input_use_list() == NULL); | 211 ASSERT(!defn->HasUses()); |
| 213 ASSERT(defn->env_use_list() == NULL); | |
| 214 defn->set_input_use_list(NULL); | 212 defn->set_input_use_list(NULL); |
| 215 defn->set_env_use_list(NULL); | 213 defn->set_env_use_list(NULL); |
| 216 } | 214 } |
| 217 | 215 |
| 218 | 216 |
| 219 static void RecordInputUses(Instruction* instr) { | 217 static void RecordInputUses(Instruction* instr) { |
| 220 ASSERT(instr != NULL); | 218 ASSERT(instr != NULL); |
| 221 for (intptr_t i = 0; i < instr->InputCount(); ++i) { | 219 for (intptr_t i = 0; i < instr->InputCount(); ++i) { |
| 222 Value* use = instr->InputAt(i); | 220 Value* use = instr->InputAt(i); |
| 223 ASSERT(use->instruction() == NULL); | 221 ASSERT(use->instruction() == NULL); |
| (...skipping 596 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 820 use->set_use_index(use_idx); | 818 use->set_use_index(use_idx); |
| 821 } | 819 } |
| 822 // Write the predecessor use. | 820 // Write the predecessor use. |
| 823 phi->SetInputAt(new_index, pred_use); | 821 phi->SetInputAt(new_index, pred_use); |
| 824 pred_use->set_use_index(new_index); | 822 pred_use->set_use_index(new_index); |
| 825 } | 823 } |
| 826 } | 824 } |
| 827 } | 825 } |
| 828 | 826 |
| 829 | 827 |
| 830 // Helper to sort a list of blocks. | |
| 831 static int LowestBlockIdFirst(BlockEntryInstr* const* a, | |
| 832 BlockEntryInstr* const* b) { | |
| 833 return (*a)->block_id() - (*b)->block_id(); | |
| 834 } | |
| 835 | |
| 836 | |
| 837 // Inline a flow graph at a call site. | 828 // Inline a flow graph at a call site. |
| 838 // | 829 // |
| 839 // Assumes the callee graph was computed by BuildGraph with an inlining context | 830 // Assumes the callee graph was computed by BuildGraph with an inlining context |
| 840 // and transformed to SSA with ComputeSSA with a correct virtual register | 831 // and transformed to SSA with ComputeSSA with a correct virtual register |
| 841 // number, and that the use lists have been correctly computed. | 832 // number, and that the use lists have been correctly computed. |
| 842 // | 833 // |
| 843 // After inlining the caller graph will correctly have adjusted the pre/post | 834 // After inlining the caller graph will correctly have adjusted the pre/post |
| 844 // orders, the dominator tree and the use lists. | 835 // orders, the dominator tree and the use lists. |
| 845 void FlowGraph::InlineCall(Definition* call, FlowGraph* callee_graph) { | 836 void FlowGraph::InlineCall(Definition* call, |
| 837 FlowGraph* callee_graph, |
| 838 ValueInliningContext* inlining_context) { |
| 846 ASSERT(call->previous() != NULL); | 839 ASSERT(call->previous() != NULL); |
| 847 ASSERT(call->next() != NULL); | 840 ASSERT(call->next() != NULL); |
| 848 ASSERT(callee_graph->exits() != NULL); | |
| 849 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1); | 841 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1); |
| 850 ASSERT(callee_graph->max_block_id() > max_block_id()); | 842 ASSERT(callee_graph->max_block_id() > max_block_id()); |
| 851 ASSERT(callee_graph->max_virtual_register_number() > | 843 ASSERT(callee_graph->max_virtual_register_number() > |
| 852 max_virtual_register_number()); | 844 max_virtual_register_number()); |
| 853 | 845 |
| 854 // Adjust the max block id to the max block id of the callee graph. | 846 // Adjust the max block id to the max block id of the callee graph. |
| 855 max_block_id_ = callee_graph->max_block_id(); | 847 max_block_id_ = callee_graph->max_block_id(); |
| 856 | 848 |
| 857 // Adjust the SSA temp index by the callee graph's index. | 849 // Adjust the SSA temp index by the callee graph's index. |
| 858 current_ssa_temp_index_ = callee_graph->max_virtual_register_number(); | 850 current_ssa_temp_index_ = callee_graph->max_virtual_register_number(); |
| 859 | 851 |
| 860 BlockEntryInstr* caller_entry = call->GetBlock(); | 852 BlockEntryInstr* caller_entry = call->GetBlock(); |
| 861 TargetEntryInstr* callee_entry = callee_graph->graph_entry()->normal_entry(); | 853 TargetEntryInstr* callee_entry = callee_graph->graph_entry()->normal_entry(); |
| 862 ZoneGrowableArray<ReturnInstr*>* callee_exits = callee_graph->exits(); | |
| 863 | 854 |
| 864 // Attach the outer environment on each instruction in the callee graph. | 855 // Attach the outer environment on each instruction in the callee graph. |
| 865 for (BlockIterator block_it = callee_graph->postorder_iterator(); | 856 for (BlockIterator block_it = callee_graph->postorder_iterator(); |
| 866 !block_it.Done(); | 857 !block_it.Done(); |
| 867 block_it.Advance()) { | 858 block_it.Advance()) { |
| 868 for (ForwardInstructionIterator it(block_it.Current()); | 859 for (ForwardInstructionIterator it(block_it.Current()); |
| 869 !it.Done(); | 860 !it.Done(); |
| 870 it.Advance()) { | 861 it.Advance()) { |
| 871 Instruction* instr = it.Current(); | 862 Instruction* instr = it.Current(); |
| 872 // TODO(zerny): Avoid creating unnecessary environments. Note that some | 863 // TODO(zerny): Avoid creating unnecessary environments. Note that some |
| 873 // optimizations need deoptimization info for non-deoptable instructions, | 864 // optimizations need deoptimization info for non-deoptable instructions, |
| 874 // eg, LICM on GOTOs. | 865 // eg, LICM on GOTOs. |
| 875 if (instr->env() != NULL) call->env()->DeepCopyToOuter(instr); | 866 if (instr->env() != NULL) call->env()->DeepCopyToOuter(instr); |
| 876 } | 867 } |
| 877 } | 868 } |
| 878 | 869 |
| 879 // Insert the callee graph into the caller graph. | 870 // Insert the callee graph into the caller graph. First sort the list of |
| 880 if (callee_exits->is_empty()) { | 871 // exits by block id (recording block entries as a side effect). |
| 872 inlining_context->SortExits(); |
| 873 if (inlining_context->NumExits() == 0) { |
| 881 // TODO(zerny): Add support for non-local exits, such as throw. | 874 // TODO(zerny): Add support for non-local exits, such as throw. |
| 882 UNREACHABLE(); | 875 UNREACHABLE(); |
| 883 } else if (callee_exits->length() == 1) { | 876 } else if (inlining_context->NumExits() == 1) { |
| 884 ReturnInstr* exit = (*callee_exits)[0]; | |
| 885 ASSERT(exit->previous() != NULL); | |
| 886 // For just one exit, replace the uses and remove the call from the graph. | 877 // For just one exit, replace the uses and remove the call from the graph. |
| 887 call->ReplaceUsesWith(exit->value()->definition()); | 878 call->ReplaceUsesWith(inlining_context->ValueAt(0)->definition()); |
| 888 call->previous()->LinkTo(callee_entry->next()); | 879 call->previous()->LinkTo(callee_entry->next()); |
| 889 exit->previous()->LinkTo(call->next()); | 880 inlining_context->LastInstructionAt(0)->LinkTo(call->next()); |
| 890 // In case of control flow, locally update the predecessors, phis and | 881 // In case of control flow, locally update the predecessors, phis and |
| 891 // dominator tree. | 882 // dominator tree. |
| 892 // TODO(zerny): should we leave the dominator tree since we recompute it | 883 // TODO(zerny): should we leave the dominator tree since we recompute it |
| 893 // after a full inlining pass? | 884 // after a full inlining pass? |
| 894 if (callee_graph->preorder().length() > 2) { | 885 if (callee_graph->preorder().length() > 2) { |
| 895 BlockEntryInstr* exit_block = exit->GetBlock(); | 886 BlockEntryInstr* exit_block = inlining_context->ExitBlockAt(0); |
| 896 // Pictorially, the graph structure is: | 887 // Pictorially, the graph structure is: |
| 897 // | 888 // |
| 898 // Bc : caller_entry Bi : callee_entry | 889 // Bc : caller_entry Bi : callee_entry |
| 899 // before_call inlined_head | 890 // before_call inlined_head |
| 900 // call ... other blocks ... | 891 // call ... other blocks ... |
| 901 // after_call Be : exit_block | 892 // after_call Be : exit_block |
| 902 // inlined_foot | 893 // inlined_foot |
| 903 // And becomes: | 894 // And becomes: |
| 904 // | 895 // |
| 905 // Bc : caller_entry | 896 // Bc : caller_entry |
| (...skipping 19 matching lines...) Expand all Loading... |
| 925 // The caller entry is now the immediate dominator of blocks whose | 916 // The caller entry is now the immediate dominator of blocks whose |
| 926 // immediate dominator was the callee entry. | 917 // immediate dominator was the callee entry. |
| 927 caller_entry->ClearDominatedBlocks(); | 918 caller_entry->ClearDominatedBlocks(); |
| 928 for (intptr_t i = 0; i < callee_entry->dominated_blocks().length(); ++i) { | 919 for (intptr_t i = 0; i < callee_entry->dominated_blocks().length(); ++i) { |
| 929 BlockEntryInstr* block = callee_entry->dominated_blocks()[i]; | 920 BlockEntryInstr* block = callee_entry->dominated_blocks()[i]; |
| 930 block->set_dominator(caller_entry); | 921 block->set_dominator(caller_entry); |
| 931 caller_entry->AddDominatedBlock(block); | 922 caller_entry->AddDominatedBlock(block); |
| 932 } | 923 } |
| 933 } | 924 } |
| 934 } else { | 925 } else { |
| 935 // Sort the list of exits by block id. | |
| 936 GrowableArray<BlockEntryInstr*> exits(callee_exits->length()); | |
| 937 for (intptr_t i = 0; i < callee_exits->length(); ++i) { | |
| 938 exits.Add((*callee_exits)[i]->GetBlock()); | |
| 939 } | |
| 940 exits.Sort(LowestBlockIdFirst); | |
| 941 // Create a join of the returns. | 926 // Create a join of the returns. |
| 942 JoinEntryInstr* join = | 927 JoinEntryInstr* join = |
| 943 new JoinEntryInstr(++max_block_id_, | 928 new JoinEntryInstr(++max_block_id_, |
| 944 CatchClauseNode::kInvalidTryIndex, | 929 CatchClauseNode::kInvalidTryIndex, |
| 945 caller_entry->loop_depth()); | 930 caller_entry->loop_depth()); |
| 946 for (intptr_t i = 0; i < exits.length(); ++i) { | 931 intptr_t count = inlining_context->NumExits(); |
| 947 ReturnInstr* exit_instr = exits[i]->last_instruction()->AsReturn(); | 932 for (intptr_t i = 0; i < count; ++i) { |
| 948 ASSERT(exit_instr != NULL); | 933 inlining_context->LastInstructionAt(i)->Goto(join); |
| 949 exit_instr->previous()->Goto(join); | |
| 950 // Directly add the predecessors of the join in ascending block id order. | 934 // Directly add the predecessors of the join in ascending block id order. |
| 951 join->predecessors_.Add(exits[i]); | 935 join->predecessors_.Add(inlining_context->ExitBlockAt(i)); |
| 952 } | 936 } |
| 953 // If the call has uses, create a phi of the returns. | 937 // If the call has uses, create a phi of the returns. |
| 954 if ((call->input_use_list() != NULL) || | 938 if (call->HasUses()) { |
| 955 (call->env_use_list() != NULL)) { | |
| 956 // Environment count: length before call - argument count (+ return) | 939 // Environment count: length before call - argument count (+ return) |
| 957 intptr_t env_count = call->env()->Length() - call->ArgumentCount(); | 940 intptr_t env_count = call->env()->Length() - call->ArgumentCount(); |
| 958 // Add a phi of the return values. | 941 // Add a phi of the return values. |
| 959 join->InsertPhi(env_count, env_count + 1); | 942 join->InsertPhi(env_count, env_count + 1); |
| 960 PhiInstr* phi = join->phis()->Last(); | 943 PhiInstr* phi = join->phis()->Last(); |
| 961 phi->set_ssa_temp_index(alloc_ssa_temp_index()); | 944 phi->set_ssa_temp_index(alloc_ssa_temp_index()); |
| 962 phi->mark_alive(); | 945 phi->mark_alive(); |
| 963 for (intptr_t i = 0; i < exits.length(); ++i) { | 946 for (intptr_t i = 0; i < count; ++i) { |
| 964 ReturnInstr* exit_instr = exits[i]->last_instruction()->AsReturn(); | 947 Value* value = inlining_context->ValueAt(i); |
| 965 ASSERT(exit_instr != NULL); | 948 phi->SetInputAt(i, value); |
| 966 Value* use = exit_instr->value(); | 949 value->set_instruction(phi); |
| 967 phi->SetInputAt(i, use); | 950 value->set_use_index(i); |
| 968 use->set_instruction(phi); | |
| 969 use->set_use_index(i); | |
| 970 } | 951 } |
| 971 // Replace uses of the call with the phi. | 952 // Replace uses of the call with the phi. |
| 972 call->ReplaceUsesWith(phi); | 953 call->ReplaceUsesWith(phi); |
| 973 } | 954 } |
| 974 // Remove the call from the graph. | 955 // Remove the call from the graph. |
| 975 call->previous()->LinkTo(callee_entry->next()); | 956 call->previous()->LinkTo(callee_entry->next()); |
| 976 join->LinkTo(call->next()); | 957 join->LinkTo(call->next()); |
| 977 // Replace the blocks after splitting (see comment in the len=1 case above). | 958 // Replace the blocks after splitting (see comment in the len=1 case above). |
| 978 ReplacePredecessor(caller_entry, join); | 959 ReplacePredecessor(caller_entry, join); |
| 979 ReplacePredecessor(callee_entry, caller_entry); | 960 ReplacePredecessor(callee_entry, caller_entry); |
| 980 // Update the last instruction pointers on each exit (ie, to the new goto). | 961 // Update the last instruction pointers on each exit block to the new goto. |
| 981 for (intptr_t i = 0; i < exits.length(); ++i) { | 962 for (intptr_t i = 0; i < count; ++i) { |
| 982 exits[i]->set_last_instruction( | 963 inlining_context->ExitBlockAt(i)->set_last_instruction( |
| 983 exits[i]->last_instruction()->previous()->next()); | 964 inlining_context->LastInstructionAt(i)->next()); |
| 984 } | 965 } |
| 985 // Mark that the dominator tree is invalid. | 966 // Mark that the dominator tree is invalid. |
| 986 // TODO(zerny): Compute the dominator frontier locally. | 967 // TODO(zerny): Compute the dominator frontier locally. |
| 987 invalid_dominator_tree_ = true; | 968 invalid_dominator_tree_ = true; |
| 988 } | 969 } |
| 989 } | 970 } |
| 990 | 971 |
| 991 | 972 |
| 992 void FlowGraph::RepairGraphAfterInlining() { | 973 void FlowGraph::RepairGraphAfterInlining() { |
| 993 DiscoverBlocks(); | 974 DiscoverBlocks(); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 1006 !it.Done(); | 987 !it.Done(); |
| 1007 it.Advance()) { | 988 it.Advance()) { |
| 1008 ++size; | 989 ++size; |
| 1009 } | 990 } |
| 1010 } | 991 } |
| 1011 return size; | 992 return size; |
| 1012 } | 993 } |
| 1013 | 994 |
| 1014 | 995 |
| 1015 } // namespace dart | 996 } // namespace dart |
| OLD | NEW |