Chromium Code Reviews| 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" |
| 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 Loading... | |
| 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 |
| OLD | NEW |