| 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 740 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 751 const char* function_name = parsed_function_.function().ToCString(); | 751 const char* function_name = parsed_function_.function().ToCString(); |
| 752 intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1; | 752 intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1; |
| 753 char* chars = Isolate::Current()->current_zone()->Alloc<char>(len); | 753 char* chars = Isolate::Current()->current_zone()->Alloc<char>(len); |
| 754 OS::SNPrint(chars, len, kFormat, function_name, reason); | 754 OS::SNPrint(chars, len, kFormat, function_name, reason); |
| 755 const Error& error = Error::Handle( | 755 const Error& error = Error::Handle( |
| 756 LanguageError::New(String::Handle(String::New(chars)))); | 756 LanguageError::New(String::Handle(String::New(chars)))); |
| 757 Isolate::Current()->long_jump_base()->Jump(1, error); | 757 Isolate::Current()->long_jump_base()->Jump(1, error); |
| 758 } | 758 } |
| 759 | 759 |
| 760 | 760 |
| 761 // Helper to replace a predecessor block. For each successor of 'old_block', the | |
| 762 // predecessors will be reordered to preserve block-order sorting of the | |
| 763 // predecessors as well as the phis if the successor is a join. | |
| 764 void FlowGraph::ReplacePredecessor(BlockEntryInstr* old_block, | |
| 765 BlockEntryInstr* new_block) { | |
| 766 // Set the last instruction of the new block to that of the old block. | |
| 767 Instruction* last = old_block->last_instruction(); | |
| 768 new_block->set_last_instruction(last); | |
| 769 // For each successor, update the predecessors. | |
| 770 for (intptr_t sidx = 0; sidx < last->SuccessorCount(); ++sidx) { | |
| 771 // If the successor is a target, update its predecessor. | |
| 772 TargetEntryInstr* target = last->SuccessorAt(sidx)->AsTargetEntry(); | |
| 773 if (target != NULL) { | |
| 774 target->predecessor_ = new_block; | |
| 775 continue; | |
| 776 } | |
| 777 // If the successor is a join, update each predecessor and the phis. | |
| 778 JoinEntryInstr* join = last->SuccessorAt(sidx)->AsJoinEntry(); | |
| 779 ASSERT(join != NULL); | |
| 780 // Find the old predecessor index. | |
| 781 intptr_t old_index = join->IndexOfPredecessor(old_block); | |
| 782 intptr_t pred_count = join->PredecessorCount(); | |
| 783 ASSERT(old_index >= 0); | |
| 784 ASSERT(old_index < pred_count); | |
| 785 // Find the new predecessor index while reordering the predecessors. | |
| 786 intptr_t new_id = new_block->block_id(); | |
| 787 intptr_t new_index = old_index; | |
| 788 if (old_block->block_id() < new_id) { | |
| 789 // Search upwards, bubbling down intermediate predecessors. | |
| 790 for (; new_index < pred_count - 1; ++new_index) { | |
| 791 if (join->predecessors_[new_index + 1]->block_id() > new_id) break; | |
| 792 join->predecessors_[new_index] = join->predecessors_[new_index + 1]; | |
| 793 } | |
| 794 } else { | |
| 795 // Search downwards, bubbling up intermediate predecessors. | |
| 796 for (; new_index > 0; --new_index) { | |
| 797 if (join->predecessors_[new_index - 1]->block_id() < new_id) break; | |
| 798 join->predecessors_[new_index] = join->predecessors_[new_index - 1]; | |
| 799 } | |
| 800 } | |
| 801 join->predecessors_[new_index] = new_block; | |
| 802 // If the new and old predecessor index match there is nothing to update. | |
| 803 if ((join->phis() == NULL) || (old_index == new_index)) return; | |
| 804 // Otherwise, reorder the predecessor uses in each phi. | |
| 805 for (intptr_t i = 0; i < join->phis()->length(); ++i) { | |
| 806 PhiInstr* phi = (*join->phis())[i]; | |
| 807 if (phi == NULL) continue; | |
| 808 ASSERT(pred_count == phi->InputCount()); | |
| 809 // Save the predecessor use. | |
| 810 Value* pred_use = phi->InputAt(old_index); | |
| 811 // Move uses between old and new. | |
| 812 intptr_t step = (old_index < new_index) ? 1 : -1; | |
| 813 for (intptr_t use_idx = old_index; | |
| 814 use_idx != new_index; | |
| 815 use_idx += step) { | |
| 816 Value* use = phi->InputAt(use_idx + step); | |
| 817 phi->SetInputAt(use_idx, use); | |
| 818 use->set_use_index(use_idx); | |
| 819 } | |
| 820 // Write the predecessor use. | |
| 821 phi->SetInputAt(new_index, pred_use); | |
| 822 pred_use->set_use_index(new_index); | |
| 823 } | |
| 824 } | |
| 825 } | |
| 826 | |
| 827 | |
| 828 // Inline a flow graph at a call site. | |
| 829 // | |
| 830 // Assumes the callee graph was computed by BuildGraph with an inlining context | |
| 831 // and transformed to SSA with ComputeSSA with a correct virtual register | |
| 832 // number, and that the use lists have been correctly computed. | |
| 833 // | |
| 834 // After inlining the caller graph will correctly have adjusted the pre/post | |
| 835 // orders, the dominator tree and the use lists. | |
| 836 void FlowGraph::InlineCall(Definition* call, | |
| 837 FlowGraph* callee_graph, | |
| 838 ValueInliningContext* inlining_context) { | |
| 839 ASSERT(call->previous() != NULL); | |
| 840 ASSERT(call->next() != NULL); | |
| 841 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1); | |
| 842 ASSERT(callee_graph->max_block_id() > max_block_id()); | |
| 843 ASSERT(callee_graph->max_virtual_register_number() > | |
| 844 max_virtual_register_number()); | |
| 845 | |
| 846 // Adjust the max block id to the max block id of the callee graph. | |
| 847 max_block_id_ = callee_graph->max_block_id(); | |
| 848 | |
| 849 // Adjust the SSA temp index by the callee graph's index. | |
| 850 current_ssa_temp_index_ = callee_graph->max_virtual_register_number(); | |
| 851 | |
| 852 BlockEntryInstr* caller_entry = call->GetBlock(); | |
| 853 TargetEntryInstr* callee_entry = callee_graph->graph_entry()->normal_entry(); | |
| 854 | |
| 855 // Attach the outer environment on each instruction in the callee graph. | |
| 856 for (BlockIterator block_it = callee_graph->postorder_iterator(); | |
| 857 !block_it.Done(); | |
| 858 block_it.Advance()) { | |
| 859 for (ForwardInstructionIterator it(block_it.Current()); | |
| 860 !it.Done(); | |
| 861 it.Advance()) { | |
| 862 Instruction* instr = it.Current(); | |
| 863 // TODO(zerny): Avoid creating unnecessary environments. Note that some | |
| 864 // optimizations need deoptimization info for non-deoptable instructions, | |
| 865 // eg, LICM on GOTOs. | |
| 866 if (instr->env() != NULL) call->env()->DeepCopyToOuter(instr); | |
| 867 } | |
| 868 } | |
| 869 | |
| 870 // Insert the callee graph into the caller graph. First sort the list of | |
| 871 // exits by block id (recording block entries as a side effect). | |
| 872 inlining_context->SortExits(); | |
| 873 if (inlining_context->NumExits() == 0) { | |
| 874 // TODO(zerny): Add support for non-local exits, such as throw. | |
| 875 UNREACHABLE(); | |
| 876 } else if (inlining_context->NumExits() == 1) { | |
| 877 // For just one exit, replace the uses and remove the call from the graph. | |
| 878 call->ReplaceUsesWith(inlining_context->ValueAt(0)->definition()); | |
| 879 call->previous()->LinkTo(callee_entry->next()); | |
| 880 inlining_context->LastInstructionAt(0)->LinkTo(call->next()); | |
| 881 // In case of control flow, locally update the predecessors, phis and | |
| 882 // dominator tree. | |
| 883 // TODO(zerny): should we leave the dominator tree since we recompute it | |
| 884 // after a full inlining pass? | |
| 885 if (callee_graph->preorder().length() > 2) { | |
| 886 BlockEntryInstr* exit_block = inlining_context->ExitBlockAt(0); | |
| 887 // Pictorially, the graph structure is: | |
| 888 // | |
| 889 // Bc : caller_entry Bi : callee_entry | |
| 890 // before_call inlined_head | |
| 891 // call ... other blocks ... | |
| 892 // after_call Be : exit_block | |
| 893 // inlined_foot | |
| 894 // And becomes: | |
| 895 // | |
| 896 // Bc : caller_entry | |
| 897 // before_call | |
| 898 // inlined_head | |
| 899 // ... other blocks ... | |
| 900 // Be : exit_block | |
| 901 // inlined_foot | |
| 902 // after_call | |
| 903 // | |
| 904 // For 'after_call', caller entry (Bc) is replaced by callee exit (Be). | |
| 905 ReplacePredecessor(caller_entry, exit_block); | |
| 906 // For 'inlined_head', callee entry (Bi) is replaced by caller entry (Bc). | |
| 907 ReplacePredecessor(callee_entry, caller_entry); | |
| 908 // The callee exit is now the immediate dominator of blocks whose | |
| 909 // immediate dominator was the caller entry. | |
| 910 ASSERT(exit_block->dominated_blocks().is_empty()); | |
| 911 for (intptr_t i = 0; i < caller_entry->dominated_blocks().length(); ++i) { | |
| 912 BlockEntryInstr* block = caller_entry->dominated_blocks()[i]; | |
| 913 block->set_dominator(exit_block); | |
| 914 exit_block->AddDominatedBlock(block); | |
| 915 } | |
| 916 // The caller entry is now the immediate dominator of blocks whose | |
| 917 // immediate dominator was the callee entry. | |
| 918 caller_entry->ClearDominatedBlocks(); | |
| 919 for (intptr_t i = 0; i < callee_entry->dominated_blocks().length(); ++i) { | |
| 920 BlockEntryInstr* block = callee_entry->dominated_blocks()[i]; | |
| 921 block->set_dominator(caller_entry); | |
| 922 caller_entry->AddDominatedBlock(block); | |
| 923 } | |
| 924 } | |
| 925 } else { | |
| 926 // Create a join of the returns. | |
| 927 JoinEntryInstr* join = | |
| 928 new JoinEntryInstr(++max_block_id_, CatchClauseNode::kInvalidTryIndex); | |
| 929 intptr_t count = inlining_context->NumExits(); | |
| 930 for (intptr_t i = 0; i < count; ++i) { | |
| 931 inlining_context->LastInstructionAt(i)->Goto(join); | |
| 932 // Directly add the predecessors of the join in ascending block id order. | |
| 933 join->predecessors_.Add(inlining_context->ExitBlockAt(i)); | |
| 934 } | |
| 935 // If the call has uses, create a phi of the returns. | |
| 936 if (call->HasUses()) { | |
| 937 // Environment count: length before call - argument count (+ return) | |
| 938 intptr_t env_count = call->env()->Length() - call->ArgumentCount(); | |
| 939 // Add a phi of the return values. | |
| 940 join->InsertPhi(env_count, env_count + 1); | |
| 941 PhiInstr* phi = join->phis()->Last(); | |
| 942 phi->set_ssa_temp_index(alloc_ssa_temp_index()); | |
| 943 phi->mark_alive(); | |
| 944 for (intptr_t i = 0; i < count; ++i) { | |
| 945 Value* value = inlining_context->ValueAt(i); | |
| 946 phi->SetInputAt(i, value); | |
| 947 value->set_instruction(phi); | |
| 948 value->set_use_index(i); | |
| 949 } | |
| 950 // Replace uses of the call with the phi. | |
| 951 call->ReplaceUsesWith(phi); | |
| 952 } | |
| 953 // Remove the call from the graph. | |
| 954 call->previous()->LinkTo(callee_entry->next()); | |
| 955 join->LinkTo(call->next()); | |
| 956 // Replace the blocks after splitting (see comment in the len=1 case above). | |
| 957 ReplacePredecessor(caller_entry, join); | |
| 958 ReplacePredecessor(callee_entry, caller_entry); | |
| 959 // Update the last instruction pointers on each exit block to the new goto. | |
| 960 for (intptr_t i = 0; i < count; ++i) { | |
| 961 inlining_context->ExitBlockAt(i)->set_last_instruction( | |
| 962 inlining_context->LastInstructionAt(i)->next()); | |
| 963 } | |
| 964 // Mark that the dominator tree is invalid. | |
| 965 // TODO(zerny): Compute the dominator frontier locally. | |
| 966 invalid_dominator_tree_ = true; | |
| 967 } | |
| 968 } | |
| 969 | |
| 970 | |
| 971 void FlowGraph::RepairGraphAfterInlining() { | 761 void FlowGraph::RepairGraphAfterInlining() { |
| 972 DiscoverBlocks(); | 762 DiscoverBlocks(); |
| 973 if (invalid_dominator_tree_) { | 763 if (invalid_dominator_tree_) { |
| 974 GrowableArray<BitVector*> dominance_frontier; | 764 GrowableArray<BitVector*> dominance_frontier; |
| 975 ComputeDominators(&dominance_frontier); | 765 ComputeDominators(&dominance_frontier); |
| 976 } | 766 } |
| 977 } | 767 } |
| 978 | 768 |
| 979 | 769 |
| 980 intptr_t FlowGraph::InstructionCount() const { | 770 intptr_t FlowGraph::InstructionCount() const { |
| 981 intptr_t size = 0; | 771 intptr_t size = 0; |
| 982 // Iterate each block, skipping the graph entry. | 772 // Iterate each block, skipping the graph entry. |
| 983 for (intptr_t i = 1; i < preorder_.length(); ++i) { | 773 for (intptr_t i = 1; i < preorder_.length(); ++i) { |
| 984 for (ForwardInstructionIterator it(preorder_[i]); | 774 for (ForwardInstructionIterator it(preorder_[i]); |
| 985 !it.Done(); | 775 !it.Done(); |
| 986 it.Advance()) { | 776 it.Advance()) { |
| 987 ++size; | 777 ++size; |
| 988 } | 778 } |
| 989 } | 779 } |
| 990 return size; | 780 return size; |
| 991 } | 781 } |
| 992 | 782 |
| 993 | 783 |
| 994 } // namespace dart | 784 } // namespace dart |
| OLD | NEW |