Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(154)

Side by Side Diff: runtime/vm/flow_graph.cc

Issue 10979078: Revert several inlining related changes. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « runtime/vm/flow_graph.h ('k') | runtime/vm/flow_graph_builder.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 16
17 FlowGraph::FlowGraph(const FlowGraphBuilder& builder, 17 FlowGraph::FlowGraph(const FlowGraphBuilder& builder,
18 GraphEntryInstr* graph_entry, 18 GraphEntryInstr* graph_entry)
19 intptr_t max_block_id)
20 : parent_(), 19 : parent_(),
21 assigned_vars_(), 20 assigned_vars_(),
22 current_ssa_temp_index_(0), 21 current_ssa_temp_index_(0),
23 max_block_id_(max_block_id),
24 parsed_function_(builder.parsed_function()), 22 parsed_function_(builder.parsed_function()),
25 num_copied_params_(builder.num_copied_params()), 23 num_copied_params_(builder.num_copied_params()),
26 num_non_copied_params_(builder.num_non_copied_params()), 24 num_non_copied_params_(builder.num_non_copied_params()),
27 num_stack_locals_(builder.num_stack_locals()), 25 num_stack_locals_(builder.num_stack_locals()),
28 graph_entry_(graph_entry), 26 graph_entry_(graph_entry),
29 preorder_(), 27 preorder_(),
30 postorder_(), 28 postorder_(),
31 reverse_postorder_(), 29 reverse_postorder_(),
32 exits_(NULL) { 30 exits_(NULL) {
33 DiscoverBlocks(); 31 DiscoverBlocks();
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
128 return true; // Return true so we can ASSERT the reset code. 126 return true; // Return true so we can ASSERT the reset code.
129 } 127 }
130 128
131 129
132 static void ValidateUseListsInInstruction(Instruction* instr) { 130 static void ValidateUseListsInInstruction(Instruction* instr) {
133 ASSERT(instr != NULL); 131 ASSERT(instr != NULL);
134 ASSERT(!instr->IsJoinEntry()); 132 ASSERT(!instr->IsJoinEntry());
135 for (intptr_t i = 0; i < instr->InputCount(); ++i) { 133 for (intptr_t i = 0; i < instr->InputCount(); ++i) {
136 Value* use = instr->InputAt(i); 134 Value* use = instr->InputAt(i);
137 ASSERT(use->use_index() == i); 135 ASSERT(use->use_index() == i);
138 // TODO(zerny): Make this a slow assert. 136 ASSERT(1 == MembershipCount(use, use->definition()->input_use_list()));
139 // ASSERT(1 == MembershipCount(use, use->definition()->input_use_list()));
140 } 137 }
141 if (instr->env() != NULL) { 138 if (instr->env() != NULL) {
142 intptr_t use_index = 0; 139 intptr_t use_index = 0;
143 for (Environment::DeepIterator it(instr->env()); !it.Done(); it.Advance()) { 140 for (Environment::DeepIterator it(instr->env()); !it.Done(); it.Advance()) {
144 Value* use = it.CurrentValue(); 141 Value* use = it.CurrentValue();
145 ASSERT(use->use_index() == use_index++); 142 ASSERT(use->use_index() == use_index++);
146 // TODO(zerny): Make this a slow assert. 143 ASSERT(1 == MembershipCount(use, use->definition()->env_use_list()));
147 // ASSERT(1 == MembershipCount(use, use->definition()->env_use_list()));
148 } 144 }
149 } 145 }
150 Definition* defn = instr->AsDefinition(); 146 Definition* defn = instr->AsDefinition();
151 if (defn != NULL) { 147 if (defn != NULL) {
152 for (Value* use = defn->input_use_list(); 148 for (Value* use = defn->input_use_list();
153 use != NULL; 149 use != NULL;
154 use = use->next_use()) { 150 use = use->next_use()) {
155 ASSERT(defn == use->definition()); 151 ASSERT(defn == use->definition());
156 ASSERT(use == use->instruction()->InputAt(use->use_index())); 152 ASSERT(use == use->instruction()->InputAt(use->use_index()));
157 } 153 }
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
200 } 196 }
201 197
202 198
203 static void RecordInputUses(Instruction* instr) { 199 static void RecordInputUses(Instruction* instr) {
204 ASSERT(instr != NULL); 200 ASSERT(instr != NULL);
205 for (intptr_t i = 0; i < instr->InputCount(); ++i) { 201 for (intptr_t i = 0; i < instr->InputCount(); ++i) {
206 Value* use = instr->InputAt(i); 202 Value* use = instr->InputAt(i);
207 ASSERT(use->instruction() == NULL); 203 ASSERT(use->instruction() == NULL);
208 ASSERT(use->use_index() == -1); 204 ASSERT(use->use_index() == -1);
209 ASSERT(use->next_use() == NULL); 205 ASSERT(use->next_use() == NULL);
210 // TODO(zerny): Make this a slow assert. 206 DEBUG_ASSERT(0 == MembershipCount(use,
211 // DEBUG_ASSERT(0 == MembershipCount(use, 207 use->definition()->input_use_list()));
212 // use->definition()->input_use_list()));
213 use->set_instruction(instr); 208 use->set_instruction(instr);
214 use->set_use_index(i); 209 use->set_use_index(i);
215 use->AddToInputUseList(); 210 use->AddToInputUseList();
216 } 211 }
217 } 212 }
218 213
219 214
220 static void RecordEnvUses(Instruction* instr) { 215 static void RecordEnvUses(Instruction* instr) {
221 ASSERT(instr != NULL); 216 ASSERT(instr != NULL);
222 if (instr->env() == NULL) return; 217 if (instr->env() == NULL) return;
223 intptr_t use_index = 0; 218 intptr_t use_index = 0;
224 for (Environment::DeepIterator it(instr->env()); !it.Done(); it.Advance()) { 219 for (Environment::DeepIterator it(instr->env()); !it.Done(); it.Advance()) {
225 Value* use = it.CurrentValue(); 220 Value* use = it.CurrentValue();
226 ASSERT(use->instruction() == NULL); 221 ASSERT(use->instruction() == NULL);
227 ASSERT(use->use_index() == -1); 222 ASSERT(use->use_index() == -1);
228 ASSERT(use->next_use() == NULL); 223 ASSERT(use->next_use() == NULL);
229 // TODO(zerny): Make this a slow assert. 224 DEBUG_ASSERT(0 == MembershipCount(use, use->definition()->env_use_list()));
230 // DEBUG_ASSERT(0 ==
231 // MembershipCount(use, use->definition()->env_use_list()));
232 use->set_instruction(instr); 225 use->set_instruction(instr);
233 use->set_use_index(use_index++); 226 use->set_use_index(use_index++);
234 use->AddToEnvUseList(); 227 use->AddToEnvUseList();
235 } 228 }
236 } 229 }
237 230
238 231
239 static void ComputeUseListsRecursive(BlockEntryInstr* block) { 232 static void ComputeUseListsRecursive(BlockEntryInstr* block) {
240 // Clear phi definitions. 233 // Clear phi definitions.
241 JoinEntryInstr* join = block->AsJoinEntry(); 234 JoinEntryInstr* join = block->AsJoinEntry();
(...skipping 289 matching lines...) Expand 10 before | Expand all | Expand 10 after
531 } 524 }
532 } 525 }
533 } 526 }
534 527
535 // 2. Process normal instructions. 528 // 2. Process normal instructions.
536 for (ForwardInstructionIterator it(block_entry); !it.Done(); it.Advance()) { 529 for (ForwardInstructionIterator it(block_entry); !it.Done(); it.Advance()) {
537 Instruction* current = it.Current(); 530 Instruction* current = it.Current();
538 // Attach current environment to the instruction. First, each instruction 531 // Attach current environment to the instruction. First, each instruction
539 // gets a full copy of the environment. Later we optimize this by 532 // gets a full copy of the environment. Later we optimize this by
540 // eliminating unnecessary environments. 533 // eliminating unnecessary environments.
541 // TODO(zerny): Avoid creating unnecessary environments. Note that some
542 // optimizations need deoptimization info for non-deoptable instructions,
543 // eg, LICM on GOTOs.
544 current->set_env(Environment::From(*env, 534 current->set_env(Environment::From(*env,
545 num_non_copied_params_, 535 num_non_copied_params_,
546 parsed_function_.function())); 536 parsed_function_.function()));
547 if (current->CanDeoptimize()) { 537 if (current->CanDeoptimize()) {
548 current->env()->set_deopt_id(current->deopt_id()); 538 current->env()->set_deopt_id(current->deopt_id());
549 } 539 }
550 540
551 // 2a. Handle uses: 541 // 2a. Handle uses:
552 // Update expression stack environment for each use. 542 // Update expression stack environment for each use.
553 // For each use of a LoadLocal or StoreLocal: Replace it with the value 543 // For each use of a LoadLocal or StoreLocal: Replace it with the value
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after
730 const char* function_name = parsed_function_.function().ToCString(); 720 const char* function_name = parsed_function_.function().ToCString();
731 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;
732 char* chars = Isolate::Current()->current_zone()->Alloc<char>(len); 722 char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
733 OS::SNPrint(chars, len, kFormat, function_name, reason); 723 OS::SNPrint(chars, len, kFormat, function_name, reason);
734 const Error& error = Error::Handle( 724 const Error& error = Error::Handle(
735 LanguageError::New(String::Handle(String::New(chars)))); 725 LanguageError::New(String::Handle(String::New(chars))));
736 Isolate::Current()->long_jump_base()->Jump(1, error); 726 Isolate::Current()->long_jump_base()->Jump(1, error);
737 } 727 }
738 728
739 729
740 // Helper to reorder phis after splitting a block. The last instruction(s) of 730 // Helper to get the block-entry of an instruction.
741 // the split block will now have a larger block id than any previously known 731 static BlockEntryInstr* GetBlockEntry(Instruction* instr) {
742 // blocks. If the last instruction jumps to a join, we must reorder phi inputs 732 while (!instr->IsBlockEntry()) instr = instr->previous();
743 // according to the block order, ie, we move this predecessor to the end. 733 return instr->AsBlockEntry();
744 static void ReorderPhis(BlockEntryInstr* block) {
745 GotoInstr* jump = block->last_instruction()->AsGoto();
746 if (jump == NULL) return;
747 JoinEntryInstr* join = jump->successor();
748 intptr_t pred_index = join->IndexOfPredecessor(block);
749 intptr_t pred_count = join->PredecessorCount();
750 ASSERT(pred_index >= 0);
751 ASSERT(pred_index < pred_count);
752 // If the predecessor index is the last index there is nothing to update.
753 if ((join->phis() == NULL) || (pred_index + 1 == pred_count)) return;
754 // Otherwise, move the predecessor use to the end in each phi.
755 for (intptr_t i = 0; i < join->phis()->length(); ++i) {
756 PhiInstr* phi = (*join->phis())[i];
757 if (phi == NULL) continue;
758 ASSERT(pred_count == phi->InputCount());
759 // Save the predecessor use.
760 Value* pred_use = phi->InputAt(pred_index);
761 // Move each of the following uses back by one.
762 ASSERT(pred_index < pred_count - 1); // Will move at least one index.
763 for (intptr_t i = pred_index; i < pred_count - 1; ++i) {
764 Value* use = phi->InputAt(i + 1);
765 phi->SetInputAt(i, use);
766 use->set_use_index(i);
767 }
768 // Write the predecessor use at the end.
769 phi->SetInputAt(pred_count - 1, pred_use);
770 pred_use->set_use_index(pred_count - 1);
771 }
772 } 734 }
773 735
774 736
775 // Helper to link two instructions in the graph. 737 // Helper to link two instructions in the graph.
776 static void Link(Instruction* prev, Instruction* next) { 738 static void Link(Instruction* prev, Instruction* next) {
777 ASSERT(prev != next); 739 ASSERT(prev != next);
778 prev->set_next(next); 740 prev->set_next(next);
779 next->set_previous(prev); 741 next->set_previous(prev);
780 } 742 }
781 743
782 744
783 // Helper to sort a list of blocks.
784 static int LowestBlockIdFirst(BlockEntryInstr* const* a,
785 BlockEntryInstr* const* b) {
786 return (*a)->block_id() - (*b)->block_id();
787 }
788
789
790 // Inline a flow graph at a call site. 745 // Inline a flow graph at a call site.
791 // 746 //
792 // Assumes the callee graph was computed by BuildGraph with an inlining context 747 // Assumes the callee graph was computed by BuildGraph with an inlining context
793 // and transformed to SSA with ComputeSSA with a correct virtual register 748 // and transformed to SSA with ComputeSSA with a correct virtual register
794 // number, and that the use lists have been correctly computed. 749 // number, and that the use lists have been correctly computed.
795 // 750 //
796 // After inlining the caller graph will correctly have adjusted the pre/post 751 // After inlining the caller graph will correctly have adjusted the pre/post
797 // orders, the dominator tree and the use lists. 752 // orders, the dominator tree and the use lists.
798 void FlowGraph::InlineCall(Definition* call, FlowGraph* callee_graph) { 753 void FlowGraph::InlineCall(Definition* call, FlowGraph* callee_graph) {
799 ASSERT(call->previous() != NULL);
800 ASSERT(call->next() != NULL);
801 ASSERT(callee_graph->exits() != NULL); 754 ASSERT(callee_graph->exits() != NULL);
802 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1); 755 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1);
803 ASSERT(callee_graph->max_block_id() > max_block_id());
804 ASSERT(callee_graph->max_virtual_register_number() > 756 ASSERT(callee_graph->max_virtual_register_number() >
805 max_virtual_register_number()); 757 max_virtual_register_number());
806 758
807 // Adjust the max block id to the max block id of the callee graph. 759 // TODO(zerny): Implement support for callee graphs with control flow.
808 max_block_id_ = callee_graph->max_block_id(); 760 ASSERT(callee_graph->preorder().length() == 2);
809 761
810 // Adjust the SSA temp index by the callee graph's index. 762 // Adjust the SSA temp index by the callee graph's index.
811 current_ssa_temp_index_ = callee_graph->max_virtual_register_number(); 763 current_ssa_temp_index_ = callee_graph->max_virtual_register_number();
812 764
813 BlockEntryInstr* caller_entry = call->GetBlock(); 765 BlockEntryInstr* caller_entry = GetBlockEntry(call);
814 TargetEntryInstr* callee_entry = callee_graph->graph_entry()->normal_entry(); 766 TargetEntryInstr* callee_entry = callee_graph->graph_entry()->normal_entry();
815 ZoneGrowableArray<ReturnInstr*>* callee_exits = callee_graph->exits(); 767 ZoneGrowableArray<ReturnInstr*>* callee_exits = callee_graph->exits();
816 768
817 // Attach the outer environment on each instruction in the callee graph. 769 // 0. Attach the outer environment on each instruction in the callee graph.
818 for (BlockIterator block_it = callee_graph->postorder_iterator(); 770 for (ForwardInstructionIterator it(callee_entry); !it.Done(); it.Advance()) {
819 !block_it.Done(); 771 Instruction* instr = it.Current();
820 block_it.Advance()) { 772 if (instr->CanDeoptimize()) call->env()->DeepCopyToOuter(instr);
821 for (ForwardInstructionIterator it(block_it.Current());
822 !it.Done();
823 it.Advance()) {
824 Instruction* instr = it.Current();
825 // TODO(zerny): Avoid creating unnecessary environments. Note that some
826 // optimizations need deoptimization info for non-deoptable instructions,
827 // eg, LICM on GOTOs.
828 if (instr->env() != NULL) call->env()->DeepCopyToOuter(instr);
829 }
830 } 773 }
831 774
832 // Insert the callee graph into the caller graph. 775 // 1. Insert the callee graph into the caller graph.
833 if (callee_exits->is_empty()) { 776 if (callee_exits->is_empty()) {
834 // TODO(zerny): Add support for non-local exits, such as throw. 777 // If no normal exits exist, inline and truncate the block after inlining.
835 UNREACHABLE(); 778 Link(call->previous(), callee_entry->next());
779 caller_entry->set_last_instruction(callee_entry->last_instruction());
836 } else if (callee_exits->length() == 1) { 780 } else if (callee_exits->length() == 1) {
837 ReturnInstr* exit = (*callee_exits)[0]; 781 ReturnInstr* exit = (*callee_exits)[0];
838 ASSERT(exit->previous() != NULL); 782 // TODO(zerny): Support one exit graph containing control flow.
783 ASSERT(callee_entry == GetBlockEntry(exit));
839 // For just one exit, replace the uses and remove the call from the graph. 784 // For just one exit, replace the uses and remove the call from the graph.
840 call->ReplaceUsesWith(exit->value()->definition()); 785 call->ReplaceUsesWith(exit->value()->definition());
841 Link(call->previous(), callee_entry->next()); 786 Link(call->previous(), callee_entry->next());
842 Link(exit->previous(), call->next()); 787 Link(exit->previous(), call->next());
843 // In case of control flow, locally update the dominator tree.
844 if (callee_graph->preorder().length() > 2) {
845 // The caller block is split and the new block id is that of the exit
846 // block. If the caller block had outgoing edges, reorder the phis so they
847 // are still ordered by block id.
848 ReorderPhis(caller_entry);
849 // The callee return is now the immediate dominator of blocks whose
850 // immediate dominator was the caller entry.
851 BlockEntryInstr* exit_block = exit->GetBlock();
852 ASSERT(exit_block->dominated_blocks().is_empty());
853 for (intptr_t i = 0; i < caller_entry->dominated_blocks().length(); ++i) {
854 BlockEntryInstr* block = caller_entry->dominated_blocks()[i];
855 block->set_dominator(exit_block);
856 exit_block->AddDominatedBlock(block);
857 }
858 // The caller entry is now the immediate dominator of blocks whose
859 // immediate dominator was the callee entry.
860 caller_entry->ClearDominatedBlocks();
861 for (intptr_t i = 0; i < callee_entry->dominated_blocks().length(); ++i) {
862 BlockEntryInstr* block = callee_entry->dominated_blocks()[i];
863 block->set_dominator(caller_entry);
864 caller_entry->AddDominatedBlock(block);
865 }
866 // Recompute the block orders.
867 DiscoverBlocks();
868 }
869 } else { 788 } else {
870 // Sort the list of exits by block id. 789 // TODO(zerny): Support multiple exits.
871 GrowableArray<BlockEntryInstr*> exits(callee_exits->length()); 790 UNREACHABLE();
872 for (intptr_t i = 0; i < callee_exits->length(); ++i) {
873 exits.Add((*callee_exits)[i]->GetBlock());
874 }
875 exits.Sort(LowestBlockIdFirst);
876 // Create a join of the returns.
877 JoinEntryInstr* join =
878 new JoinEntryInstr(++max_block_id_, CatchClauseNode::kInvalidTryIndex);
879 for (intptr_t i = 0; i < exits.length(); ++i) {
880 ReturnInstr* exit_instr = exits[i]->last_instruction()->AsReturn();
881 ASSERT(exit_instr != NULL);
882 exit_instr->previous()->Goto(join);
883 // Directly add the predecessors of the join in ascending block id order.
884 join->predecessors_.Add(exits[i]);
885 }
886 // If the call has uses, create a phi of the returns.
887 if ((call->input_use_list() != NULL) ||
888 (call->env_use_list() != NULL)) {
889 // Environment count: length before call - argument count (+ return)
890 intptr_t env_count = call->env()->Length() - call->ArgumentCount();
891 // Add a phi of the return values.
892 join->InsertPhi(env_count, env_count + 1);
893 PhiInstr* phi = join->phis()->Last();
894 phi->set_ssa_temp_index(alloc_ssa_temp_index());
895 phi->mark_alive();
896 for (intptr_t i = 0; i < exits.length(); ++i) {
897 ReturnInstr* exit_instr = exits[i]->last_instruction()->AsReturn();
898 ASSERT(exit_instr != NULL);
899 Value* use = exit_instr->value();
900 phi->SetInputAt(i, use);
901 use->set_instruction(phi);
902 use->set_use_index(i);
903 }
904 // Replace uses of the call with the phi.
905 call->ReplaceUsesWith(phi);
906 }
907 // Remove the call from the graph.
908 Link(call->previous(), callee_entry->next());
909 Link(join, call->next());
910 // The caller block is split and the new block id is that of the join
911 // block. If the caller block had outgoing edges, reorder the phis so they
912 // are still ordered by block id.
913 ReorderPhis(caller_entry);
914 // Adjust pre/post orders and update the dominator tree.
915 DiscoverBlocks();
916 // TODO(zerny): Compute the dominator frontier locally.
917 GrowableArray<BitVector*> dominance_frontier;
918 ComputeDominators(&dominance_frontier);
919 } 791 }
792
793 // TODO(zerny): Adjust pre/post orders.
794 // TODO(zerny): Update dominator tree.
920 } 795 }
921 796
922 797
923 intptr_t FlowGraph::InstructionCount() const {
924 intptr_t size = 0;
925 // Iterate each block, skipping the graph entry.
926 for (intptr_t i = 1; i < preorder_.length(); ++i) {
927 for (ForwardInstructionIterator it(preorder_[i]);
928 !it.Done();
929 it.Advance()) {
930 ++size;
931 }
932 }
933 return size;
934 }
935
936
937 } // namespace dart 798 } // namespace dart
OLDNEW
« no previous file with comments | « runtime/vm/flow_graph.h ('k') | runtime/vm/flow_graph_builder.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698