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

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

Issue 10967007: Inlining functions with control flow. (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
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)
19 : parent_(), 20 : parent_(),
20 assigned_vars_(), 21 assigned_vars_(),
21 current_ssa_temp_index_(0), 22 current_ssa_temp_index_(0),
23 max_block_id_(max_block_id),
22 parsed_function_(builder.parsed_function()), 24 parsed_function_(builder.parsed_function()),
23 num_copied_params_(builder.num_copied_params()), 25 num_copied_params_(builder.num_copied_params()),
24 num_non_copied_params_(builder.num_non_copied_params()), 26 num_non_copied_params_(builder.num_non_copied_params()),
25 num_stack_locals_(builder.num_stack_locals()), 27 num_stack_locals_(builder.num_stack_locals()),
26 graph_entry_(graph_entry), 28 graph_entry_(graph_entry),
27 preorder_(), 29 preorder_(),
28 postorder_(), 30 postorder_(),
29 reverse_postorder_(), 31 reverse_postorder_(),
30 exits_(NULL) { 32 exits_(NULL) {
31 DiscoverBlocks(); 33 DiscoverBlocks();
(...skipping 688 matching lines...) Expand 10 before | Expand all | Expand 10 after
720 const char* function_name = parsed_function_.function().ToCString(); 722 const char* function_name = parsed_function_.function().ToCString();
721 intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1; 723 intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1;
722 char* chars = Isolate::Current()->current_zone()->Alloc<char>(len); 724 char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
723 OS::SNPrint(chars, len, kFormat, function_name, reason); 725 OS::SNPrint(chars, len, kFormat, function_name, reason);
724 const Error& error = Error::Handle( 726 const Error& error = Error::Handle(
725 LanguageError::New(String::Handle(String::New(chars)))); 727 LanguageError::New(String::Handle(String::New(chars))));
726 Isolate::Current()->long_jump_base()->Jump(1, error); 728 Isolate::Current()->long_jump_base()->Jump(1, error);
727 } 729 }
728 730
729 731
730 // Helper to get the block-entry of an instruction. 732 // Helper to reorder phis after splitting a block. The last instruction(s) of
731 static BlockEntryInstr* GetBlockEntry(Instruction* instr) { 733 // the split block will now have a larger block id than any previously known
732 while (!instr->IsBlockEntry()) instr = instr->previous(); 734 // blocks. If the last instruction jumps to a join, we must reorder phi inputs
733 return instr->AsBlockEntry(); 735 // according to the block order, ie, we move this predecessor to the end.
736 static void ReorderPhis(BlockEntryInstr* block) {
737 GotoInstr* jump = block->last_instruction()->AsGoto();
738 if (jump == NULL) return;
739 JoinEntryInstr* join = jump->successor();
740 intptr_t pred_index = join->IndexOfPredecessor(block);
741 intptr_t pred_count = join->PredecessorCount();
742 ASSERT(pred_index >= 0);
743 ASSERT(pred_index < pred_count);
744 // If the predecessor index is the last index there is nothing to update.
745 if ((join->phis() == NULL) || (pred_index + 1 == pred_count)) return;
746 // Otherwise, move the predecessor use to the end in each phi.
747 for (intptr_t i = 0; i < join->phis()->length(); ++i) {
748 PhiInstr* phi = (*join->phis())[i];
749 if (phi == NULL) continue;
750 ASSERT(pred_count == phi->InputCount());
751 // Save the predecessor use.
752 Value* pred_use = phi->InputAt(pred_index);
753 // Move each of the following uses back by one.
754 ASSERT(pred_index < pred_count - 1); // Will move at least one index.
755 for (intptr_t i = pred_index; i < pred_count - 1; ++i) {
756 Value* use = phi->InputAt(i + 1);
757 phi->SetInputAt(i, use);
758 use->set_use_index(i);
759 }
760 // Write the predecessor use at the end.
761 phi->SetInputAt(pred_count - 1, pred_use);
762 pred_use->set_use_index(pred_count - 1);
763 }
734 } 764 }
735 765
736 766
737 // Helper to link two instructions in the graph. 767 // Helper to link two instructions in the graph.
738 static void Link(Instruction* prev, Instruction* next) { 768 static void Link(Instruction* prev, Instruction* next) {
739 ASSERT(prev != next); 769 ASSERT(prev != next);
740 prev->set_next(next); 770 prev->set_next(next);
741 next->set_previous(prev); 771 next->set_previous(prev);
742 } 772 }
743 773
744 774
775 // Helper to sort a list of blocks.
776 static int LowestBlockIdFirst(BlockEntryInstr* const* a,
777 BlockEntryInstr* const* b) {
778 return (*a)->block_id() - (*b)->block_id();
779 }
780
781
745 // Inline a flow graph at a call site. 782 // Inline a flow graph at a call site.
746 // 783 //
747 // Assumes the callee graph was computed by BuildGraph with an inlining context 784 // Assumes the callee graph was computed by BuildGraph with an inlining context
748 // and transformed to SSA with ComputeSSA with a correct virtual register 785 // and transformed to SSA with ComputeSSA with a correct virtual register
749 // number, and that the use lists have been correctly computed. 786 // number, and that the use lists have been correctly computed.
750 // 787 //
751 // After inlining the caller graph will correctly have adjusted the pre/post 788 // After inlining the caller graph will correctly have adjusted the pre/post
752 // orders, the dominator tree and the use lists. 789 // orders, the dominator tree and the use lists.
753 void FlowGraph::InlineCall(Definition* call, FlowGraph* callee_graph) { 790 void FlowGraph::InlineCall(Definition* call, FlowGraph* callee_graph) {
791 ASSERT(call->previous() != NULL);
792 ASSERT(call->next() != NULL);
754 ASSERT(callee_graph->exits() != NULL); 793 ASSERT(callee_graph->exits() != NULL);
755 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1); 794 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1);
795 ASSERT(callee_graph->max_block_id() > max_block_id());
756 ASSERT(callee_graph->max_virtual_register_number() > 796 ASSERT(callee_graph->max_virtual_register_number() >
757 max_virtual_register_number()); 797 max_virtual_register_number());
758 798
759 // TODO(zerny): Implement support for callee graphs with control flow. 799 // Adjust the max block id to the max block id of the callee graph.
760 ASSERT(callee_graph->preorder().length() == 2); 800 max_block_id_ = callee_graph->max_block_id();
761 801
762 // Adjust the SSA temp index by the callee graph's index. 802 // Adjust the SSA temp index by the callee graph's index.
763 current_ssa_temp_index_ = callee_graph->max_virtual_register_number(); 803 current_ssa_temp_index_ = callee_graph->max_virtual_register_number();
764 804
765 BlockEntryInstr* caller_entry = GetBlockEntry(call); 805 BlockEntryInstr* caller_entry = call->GetBlock();
766 TargetEntryInstr* callee_entry = callee_graph->graph_entry()->normal_entry(); 806 TargetEntryInstr* callee_entry = callee_graph->graph_entry()->normal_entry();
767 ZoneGrowableArray<ReturnInstr*>* callee_exits = callee_graph->exits(); 807 ZoneGrowableArray<ReturnInstr*>* callee_exits = callee_graph->exits();
768 808
769 // 0. Attach the outer environment on each instruction in the callee graph. 809 // Attach the outer environment on each instruction in the callee graph.
770 for (ForwardInstructionIterator it(callee_entry); !it.Done(); it.Advance()) { 810 for (BlockIterator block_it = callee_graph->postorder_iterator();
771 Instruction* instr = it.Current(); 811 !block_it.Done();
772 if (instr->CanDeoptimize()) call->env()->DeepCopyToOuter(instr); 812 block_it.Advance()) {
813 for (ForwardInstructionIterator it(block_it.Current());
814 !it.Done();
815 it.Advance()) {
816 Instruction* instr = it.Current();
817 if (instr->CanDeoptimize()) call->env()->DeepCopyToOuter(instr);
818 }
773 } 819 }
774 820
775 // 1. Insert the callee graph into the caller graph. 821 // Insert the callee graph into the caller graph.
776 if (callee_exits->is_empty()) { 822 if (callee_exits->is_empty()) {
777 // If no normal exits exist, inline and truncate the block after inlining. 823 // TODO(zerny): Add support for non-local exits, such as throw.
778 Link(call->previous(), callee_entry->next()); 824 UNREACHABLE();
779 caller_entry->set_last_instruction(callee_entry->last_instruction());
780 } else if (callee_exits->length() == 1) { 825 } else if (callee_exits->length() == 1) {
781 ReturnInstr* exit = (*callee_exits)[0]; 826 ReturnInstr* exit = (*callee_exits)[0];
782 // TODO(zerny): Support one exit graph containing control flow. 827 ASSERT(exit->previous() != NULL);
783 ASSERT(callee_entry == GetBlockEntry(exit));
784 // For just one exit, replace the uses and remove the call from the graph. 828 // For just one exit, replace the uses and remove the call from the graph.
785 call->ReplaceUsesWith(exit->value()->definition()); 829 call->ReplaceUsesWith(exit->value()->definition());
786 Link(call->previous(), callee_entry->next()); 830 Link(call->previous(), callee_entry->next());
787 Link(exit->previous(), call->next()); 831 Link(exit->previous(), call->next());
832 // In case of control flow, locally update the dominator tree.
833 if (callee_graph->preorder().length() > 2) {
834 // The caller block is split and the new block id is that of the exit
835 // block. If the caller block had outgoing edges, reorder the phis so they
836 // are still ordered by block id.
837 ReorderPhis(caller_entry);
838 // The callee return is now the immediate dominator of blocks whose
839 // immediate dominator was the caller entry.
840 BlockEntryInstr* exit_block = exit->GetBlock();
841 ASSERT(exit_block->dominated_blocks().is_empty());
842 for (intptr_t i = 0; i < caller_entry->dominated_blocks().length(); ++i) {
843 BlockEntryInstr* block = caller_entry->dominated_blocks()[i];
844 block->set_dominator(exit_block);
845 exit_block->AddDominatedBlock(block);
846 }
847 // The caller entry is now the immediate dominator of blocks whose
848 // immediate dominator was the callee entry.
849 caller_entry->ClearDominatedBlocks();
850 for (intptr_t i = 0; i < callee_entry->dominated_blocks().length(); ++i) {
851 BlockEntryInstr* block = callee_entry->dominated_blocks()[i];
852 block->set_dominator(caller_entry);
853 caller_entry->AddDominatedBlock(block);
854 }
855 // Recompute the block orders.
856 DiscoverBlocks();
857 }
788 } else { 858 } else {
789 // TODO(zerny): Support multiple exits. 859 // Sort the list of exits by block id.
790 UNREACHABLE(); 860 GrowableArray<BlockEntryInstr*> exits(callee_exits->length());
861 for (intptr_t i = 0; i < callee_exits->length(); ++i) {
862 exits.Add((*callee_exits)[i]->GetBlock());
863 }
864 exits.Sort(LowestBlockIdFirst);
865 // Create a join of the returns.
866 JoinEntryInstr* join =
867 new JoinEntryInstr(++max_block_id_, CatchClauseNode::kInvalidTryIndex);
868 for (intptr_t i = 0; i < exits.length(); ++i) {
869 ReturnInstr* exit_instr = exits[i]->last_instruction()->AsReturn();
870 ASSERT(exit_instr != NULL);
871 exit_instr->previous()->Goto(join);
872 // Directly add the predecessors of the join in ascending block id order.
873 join->predecessors_.Add(exits[i]);
874 }
875 // If the call has uses, create a phi of the returns.
876 if ((call->input_use_list() != NULL) ||
877 (call->env_use_list() != NULL)) {
878 // Environment count: length before call - argument count (+ return)
879 intptr_t env_count = call->env()->Length() - call->ArgumentCount();
880 // Add a phi of the return values.
881 join->InsertPhi(env_count, env_count + 1);
882 PhiInstr* phi = join->phis()->Last();
883 phi->set_ssa_temp_index(alloc_ssa_temp_index());
884 phi->mark_alive();
885 for (intptr_t i = 0; i < exits.length(); ++i) {
886 ReturnInstr* exit_instr = exits[i]->last_instruction()->AsReturn();
887 ASSERT(exit_instr != NULL);
888 Value* use = exit_instr->value();
889 phi->SetInputAt(i, use);
890 use->set_instruction(phi);
891 use->set_use_index(i);
892 }
893 // Replace uses of the call with the phi.
894 call->ReplaceUsesWith(phi);
895 }
896 // Remove the call from the graph.
897 Link(call->previous(), callee_entry->next());
898 Link(join, call->next());
899 // The caller block is split and the new block id is that of the join
900 // block. If the caller block had outgoing edges, reorder the phis so they
901 // are still ordered by block id.
902 ReorderPhis(caller_entry);
903 // Adjust pre/post orders and update the dominator tree.
904 DiscoverBlocks();
905 // TODO(zerny): Compute the dominator frontier locally.
906 GrowableArray<BitVector*> dominance_frontier;
907 ComputeDominators(&dominance_frontier);
791 } 908 }
792
793 // TODO(zerny): Adjust pre/post orders.
794 // TODO(zerny): Update dominator tree.
795 } 909 }
796 910
797 911
798 } // namespace dart 912 } // namespace dart
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698