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

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, 3 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)
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
732 // Helper to possibly reindex a phi after splitting a block.
Kevin Millikin (Google) 2012/09/24 14:45:51 The comment needs to say that this happens while s
zerny-google 2012/09/25 10:51:32 Done.
733 static void ReindexPhiAfterSplit(BlockEntryInstr* caller_block,
734 BlockEntryInstr* return_block) {
735 if (caller_block->last_instruction()->SuccessorCount() != 1 ||
736 !caller_block->last_instruction()->SuccessorAt(0)->IsJoinEntry()) {
737 return;
738 }
739 JoinEntryInstr* join =
740 caller_block->last_instruction()->SuccessorAt(0)->AsJoinEntry();
Kevin Millikin (Google) 2012/09/24 14:45:51 SuccessorCount, SuccessorAt, IsJoinEntry, AsJoinEn
zerny-google 2012/09/25 10:51:32 A lot nicer.
741 intptr_t pred_index = join->IndexOfPredecessor(caller_block);
742 intptr_t pred_count = join->PredecessorCount();
743 ASSERT(pred_index >= 0);
744 ASSERT(pred_index < pred_count);
745 ASSERT(caller_block->block_id() < return_block->block_id());
746 // If the predecessor index is the last index there is nothing to update.
747 if (join->phis() == NULL || pred_index + 1 == pred_count) return;
Kevin Millikin (Google) 2012/09/24 14:45:51 We like to parenthesize these: if ((join->phis()
zerny-google 2012/09/25 10:51:32 Done.
748 intptr_t new_block_id = return_block->block_id();
749 for (intptr_t i = 0; i < join->phis()->length(); ++i) {
750 PhiInstr* phi = (*join->phis())[i];
751 if (phi == NULL) continue;
752 ASSERT(pred_count == phi->InputCount());
753 Value* pred_use = phi->InputAt(pred_index);
754 intptr_t curr_index = pred_index;
755 while (++curr_index < pred_count) {
Kevin Millikin (Google) 2012/09/24 14:45:51 I'm not a fan of side effects in expressions. Thi
zerny-google 2012/09/25 10:51:32 There are two issues with this restructuring. If t
756 if (new_block_id < join->PredecessorAt(curr_index)->block_id()) break;
757 Value* use = phi->InputAt(curr_index);
758 phi->SetInputAt(curr_index - 1, use);
759 use->set_use_index(curr_index - 1);
760 }
761 phi->SetInputAt(curr_index - 1, pred_use);
762 pred_use->set_use_index(curr_index - 1);
763 }
764 }
765
730 // Helper to get the block-entry of an instruction. 766 // Helper to get the block-entry of an instruction.
731 static BlockEntryInstr* GetBlockEntry(Instruction* instr) { 767 static BlockEntryInstr* GetBlockEntry(Instruction* instr) {
Kevin Millikin (Google) 2012/09/24 14:45:51 We have Instruction::GetBlock now, it can replace
zerny-google 2012/09/25 10:51:32 Done.
732 while (!instr->IsBlockEntry()) instr = instr->previous(); 768 while (!instr->IsBlockEntry()) instr = instr->previous();
733 return instr->AsBlockEntry(); 769 return instr->AsBlockEntry();
734 } 770 }
735 771
736 772
737 // Helper to link two instructions in the graph. 773 // Helper to link two instructions in the graph.
738 static void Link(Instruction* prev, Instruction* next) { 774 static void Link(Instruction* prev, Instruction* next) {
739 ASSERT(prev != next); 775 ASSERT(prev != next);
740 prev->set_next(next); 776 prev->set_next(next);
741 next->set_previous(prev); 777 next->set_previous(prev);
742 } 778 }
743 779
744 780
781 // Triple containing a return exit, its value, and its containing block.
782 class Exit : public ZoneAllocated {
Kevin Millikin (Google) 2012/09/24 14:45:51 This is just a struct.
zerny-google 2012/09/25 10:51:32 Done.
783 public:
784 ReturnInstr* exit;
785 Value* value;
786 BlockEntryInstr* block;
787 explicit Exit(ReturnInstr* exit)
788 : exit(exit),
789 value(exit->value()),
790 block(GetBlockEntry(exit)) { }
791 };
792
793
745 // Inline a flow graph at a call site. 794 // Inline a flow graph at a call site.
746 // 795 //
747 // Assumes the callee graph was computed by BuildGraph with an inlining context 796 // Assumes the callee graph was computed by BuildGraph with an inlining context
748 // and transformed to SSA with ComputeSSA with a correct virtual register 797 // and transformed to SSA with ComputeSSA with a correct virtual register
749 // number, and that the use lists have been correctly computed. 798 // number, and that the use lists have been correctly computed.
750 // 799 //
751 // After inlining the caller graph will correctly have adjusted the pre/post 800 // After inlining the caller graph will correctly have adjusted the pre/post
752 // orders, the dominator tree and the use lists. 801 // orders, the dominator tree and the use lists.
753 void FlowGraph::InlineCall(Definition* call, FlowGraph* callee_graph) { 802 void FlowGraph::InlineCall(Definition* call, FlowGraph* callee_graph) {
803 ASSERT(call->previous() != NULL);
804 ASSERT(call->next() != NULL);
754 ASSERT(callee_graph->exits() != NULL); 805 ASSERT(callee_graph->exits() != NULL);
755 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1); 806 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1);
807 ASSERT(callee_graph->max_block_id() > max_block_id());
756 ASSERT(callee_graph->max_virtual_register_number() > 808 ASSERT(callee_graph->max_virtual_register_number() >
757 max_virtual_register_number()); 809 max_virtual_register_number());
758 810
759 // TODO(zerny): Implement support for callee graphs with control flow. 811 // Adjust the max block id to the max block id of the callee graph.
760 ASSERT(callee_graph->preorder().length() == 2); 812 max_block_id_ = callee_graph->max_block_id();
761 813
762 // Adjust the SSA temp index by the callee graph's index. 814 // Adjust the SSA temp index by the callee graph's index.
763 current_ssa_temp_index_ = callee_graph->max_virtual_register_number(); 815 current_ssa_temp_index_ = callee_graph->max_virtual_register_number();
764 816
765 BlockEntryInstr* caller_entry = GetBlockEntry(call); 817 BlockEntryInstr* caller_entry = GetBlockEntry(call);
766 TargetEntryInstr* callee_entry = callee_graph->graph_entry()->normal_entry(); 818 TargetEntryInstr* callee_entry = callee_graph->graph_entry()->normal_entry();
767 ZoneGrowableArray<ReturnInstr*>* callee_exits = callee_graph->exits(); 819 ZoneGrowableArray<ReturnInstr*>* callee_exits = callee_graph->exits();
768 820
769 // 0. Attach the outer environment on each instruction in the callee graph. 821 // 0. Attach the outer environment on each instruction in the callee graph.
770 for (ForwardInstructionIterator it(callee_entry); !it.Done(); it.Advance()) { 822 for (intptr_t i = 1; i < callee_graph->preorder().length(); ++i) {
Kevin Millikin (Google) 2012/09/24 14:45:51 You can use one of the BlockIterators here --- exp
zerny-google 2012/09/25 10:51:32 Done.
771 Instruction* instr = it.Current(); 823 for (ForwardInstructionIterator it(callee_graph->preorder()[i]);
772 if (instr->CanDeoptimize()) call->env()->DeepCopyToOuter(instr); 824 !it.Done();
825 it.Advance()) {
826 Instruction* instr = it.Current();
827 if (instr->CanDeoptimize()) call->env()->DeepCopyToOuter(instr);
828 }
773 } 829 }
774 830
775 // 1. Insert the callee graph into the caller graph. 831 // 1. Insert the callee graph into the caller graph.
776 if (callee_exits->is_empty()) { 832 if (callee_exits->is_empty()) {
777 // If no normal exits exist, inline and truncate the block after inlining. 833 // TODO(zerny): Add support for non-local exits, such as throw.
778 Link(call->previous(), callee_entry->next()); 834 UNREACHABLE();
779 caller_entry->set_last_instruction(callee_entry->last_instruction());
780 } else if (callee_exits->length() == 1) { 835 } else if (callee_exits->length() == 1) {
781 ReturnInstr* exit = (*callee_exits)[0]; 836 ReturnInstr* exit = (*callee_exits)[0];
782 // TODO(zerny): Support one exit graph containing control flow. 837 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. 838 // For just one exit, replace the uses and remove the call from the graph.
785 call->ReplaceUsesWith(exit->value()->definition()); 839 call->ReplaceUsesWith(exit->value()->definition());
786 Link(call->previous(), callee_entry->next()); 840 Link(call->previous(), callee_entry->next());
787 Link(exit->previous(), call->next()); 841 Link(exit->previous(), call->next());
842 // In case of control flow, locally update the dominator tree.
843 if (callee_graph->preorder().length() > 2) {
844 BlockEntryInstr* exit_block = GetBlockEntry(exit);
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 ReindexPhiAfterSplit(caller_entry, exit_block);
849 // The callee return now dominates blocks dominated by the caller entry.
Kevin Millikin (Google) 2012/09/24 14:45:51 These are immediate dominators, right? Maybe the
zerny-google 2012/09/25 10:51:32 Done.
850 ASSERT(exit_block->dominated_blocks().is_empty());
851 for (intptr_t i = 0; i < caller_entry->dominated_blocks().length(); ++i) {
852 BlockEntryInstr* block = caller_entry->dominated_blocks()[i];
853 block->set_dominator(exit_block);
854 exit_block->AddDominatedBlock(block);
855 }
856 // The caller entry now dominates blocks dominated by the callee entry.
Kevin Millikin (Google) 2012/09/24 14:45:51 Whatever you come up with above, this is exactly p
zerny-google 2012/09/25 10:51:32 Done.
857 caller_entry->ClearDominatedBlocks();
858 for (intptr_t i = 0; i < callee_entry->dominated_blocks().length(); ++i) {
859 BlockEntryInstr* block = callee_entry->dominated_blocks()[i];
860 block->set_dominator(caller_entry);
861 caller_entry->AddDominatedBlock(block);
862 }
863 // Recompute the block orders.
864 DiscoverBlocks();
865 }
788 } else { 866 } else {
789 // TODO(zerny): Support multiple exits. 867 // Insertion sort the list of exits.
790 UNREACHABLE(); 868 GrowableArray<Exit*> exits(callee_exits->length());
Kevin Millikin (Google) 2012/09/24 14:45:51 This is pretty complicated with the sorting and ex
zerny-google 2012/09/25 10:51:32 Well, it will take a larger change to get a hold o
869 for (intptr_t i = 0; i < callee_exits->length(); ++i) {
870 Exit* exit = new Exit((*callee_exits)[i]);
871 intptr_t block_id = exit->block->block_id();
872 intptr_t index = 0;
873 while ((index < exits.length()) &&
874 (exits[index]->block->block_id() < block_id)) {
875 ++index;
876 }
877 exits.InsertAt(index, exit);
878 }
879 // Create a join of the returns.
880 JoinEntryInstr* join =
881 new JoinEntryInstr(++max_block_id_, CatchClauseNode::kInvalidTryIndex);
882 for (intptr_t i = 0; i < exits.length(); ++i) {
883 exits[i]->exit->previous()->Goto(join);
884 join->predecessors_.Add(exits[i]->block);
885 }
886 // Environment count: length before call - argument count (+ return)
887 intptr_t env_count = call->env()->Length() - call->ArgumentCount();
Kevin Millikin (Google) 2012/09/24 14:45:51 We should just avoid this for the relatively commo
zerny-google 2012/09/25 10:51:32 Done for the case where both the input and environ
888 // Add a phi of the return values.
889 join->InsertPhi(env_count, env_count + 1);
890 PhiInstr* phi = join->phis()->Last();
891 phi->set_ssa_temp_index(alloc_ssa_temp_index());
892 phi->mark_alive();
893 for (intptr_t i = 0; i < exits.length(); ++i) {
894 Value* use = exits[i]->value;
895 phi->SetInputAt(i, use);
896 use->set_instruction(phi);
897 use->set_use_index(i);
898 }
899 // Replace uses of call with phi and remove call from the graph.
900 call->ReplaceUsesWith(phi);
901 Link(call->previous(), callee_entry->next());
902 Link(join, call->next());
903 // The caller block is split and the new block id is that of the join
904 // block. If the caller block had outgoing edges, reorder the phis so they
905 // are still ordered by block id.
906 ReindexPhiAfterSplit(caller_entry, join);
907 // Adjust pre/post orders and update the dominator tree.
908 DiscoverBlocks();
909 GrowableArray<BitVector*> dominance_frontier;
910 ComputeDominators(&dominance_frontier);
Kevin Millikin (Google) 2012/09/24 14:45:51 Let's think of a way to avoid this.
zerny-google 2012/09/25 10:51:32 Will do so as a separate CL.
791 } 911 }
792
793 // TODO(zerny): Adjust pre/post orders.
794 // TODO(zerny): Update dominator tree.
795 } 912 }
796 913
797 914
798 } // namespace dart 915 } // 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