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

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

Issue 14067002: Incrementally recompute dominators when inlining. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 8 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_builder.h ('k') | runtime/vm/flow_graph_inliner.cc » ('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_builder.h" 5 #include "vm/flow_graph_builder.h"
6 6
7 #include "lib/invocation_mirror.h" 7 #include "lib/invocation_mirror.h"
8 #include "vm/ast_printer.h" 8 #include "vm/ast_printer.h"
9 #include "vm/code_descriptors.h" 9 #include "vm/code_descriptors.h"
10 #include "vm/dart_entry.h" 10 #include "vm/dart_entry.h"
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
55 last_used_try_index_(CatchClauseNode::kInvalidTryIndex), 55 last_used_try_index_(CatchClauseNode::kInvalidTryIndex),
56 try_index_(CatchClauseNode::kInvalidTryIndex), 56 try_index_(CatchClauseNode::kInvalidTryIndex),
57 graph_entry_(NULL) { } 57 graph_entry_(NULL) { }
58 58
59 59
60 void FlowGraphBuilder::AddCatchEntry(CatchBlockEntryInstr* entry) { 60 void FlowGraphBuilder::AddCatchEntry(CatchBlockEntryInstr* entry) {
61 graph_entry_->AddCatchEntry(entry); 61 graph_entry_->AddCatchEntry(entry);
62 } 62 }
63 63
64 64
65 void InliningContext::PrepareGraphs(FlowGraph* caller_graph, 65 void InliningContext::PrepareGraphs(FlowGraph* callee_graph) {
66 Definition* call,
67 FlowGraph* callee_graph) {
68 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1); 66 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1);
69 ASSERT(callee_graph->max_block_id() > caller_graph->max_block_id()); 67 ASSERT(callee_graph->max_block_id() > caller_graph_->max_block_id());
70 ASSERT(callee_graph->max_virtual_register_number() > 68 ASSERT(callee_graph->max_virtual_register_number() >
71 caller_graph->max_virtual_register_number()); 69 caller_graph_->max_virtual_register_number());
72 70
73 // Adjust the caller's maximum block id and current SSA temp index. 71 // Adjust the caller's maximum block id and current SSA temp index.
74 caller_graph->set_max_block_id(callee_graph->max_block_id()); 72 caller_graph_->set_max_block_id(callee_graph->max_block_id());
75 caller_graph->set_current_ssa_temp_index( 73 caller_graph_->set_current_ssa_temp_index(
76 callee_graph->max_virtual_register_number()); 74 callee_graph->max_virtual_register_number());
77 75
78 // Attach the outer environment on each instruction in the callee graph. 76 // Attach the outer environment on each instruction in the callee graph.
79 for (BlockIterator block_it = callee_graph->postorder_iterator(); 77 for (BlockIterator block_it = callee_graph->postorder_iterator();
80 !block_it.Done(); 78 !block_it.Done();
81 block_it.Advance()) { 79 block_it.Advance()) {
82 for (ForwardInstructionIterator it(block_it.Current()); 80 for (ForwardInstructionIterator it(block_it.Current());
83 !it.Done(); 81 !it.Done();
84 it.Advance()) { 82 it.Advance()) {
85 Instruction* instr = it.Current(); 83 Instruction* instr = it.Current();
86 // TODO(zerny): Avoid creating unnecessary environments. Note that some 84 // TODO(zerny): Avoid creating unnecessary environments. Note that some
87 // optimizations need deoptimization info for non-deoptable instructions, 85 // optimizations need deoptimization info for non-deoptable instructions,
88 // eg, LICM on GOTOs. 86 // eg, LICM on GOTOs.
89 if (instr->env() != NULL) call->env()->DeepCopyToOuter(instr); 87 if (instr->env() != NULL) call_->env()->DeepCopyToOuter(instr);
90 } 88 }
91 } 89 }
92 } 90 }
93 91
94 92
95 void InliningContext::AddExit(ReturnInstr* exit) { 93 void InliningContext::AddExit(ReturnInstr* exit) {
96 Data data = { NULL, exit }; 94 Data data = { NULL, exit };
97 exits_.Add(data); 95 exits_.Add(data);
98 } 96 }
99 97
100 98
101 int InliningContext::LowestBlockIdFirst(const Data* a, const Data* b) { 99 int InliningContext::LowestBlockIdFirst(const Data* a, const Data* b) {
102 return (a->exit_block->block_id() - b->exit_block->block_id()); 100 return (a->exit_block->block_id() - b->exit_block->block_id());
103 } 101 }
104 102
105 103
106 void InliningContext::SortExits() { 104 void InliningContext::SortExits() {
107 // Assign block entries here because we did not necessarily know them when 105 // Assign block entries here because we did not necessarily know them when
108 // the return exit was added to the array. 106 // the return exit was added to the array.
109 for (int i = 0; i < exits_.length(); ++i) { 107 for (int i = 0; i < exits_.length(); ++i) {
110 exits_[i].exit_block = exits_[i].exit_return->GetBlock(); 108 exits_[i].exit_block = exits_[i].exit_return->GetBlock();
111 } 109 }
112 exits_.Sort(LowestBlockIdFirst); 110 exits_.Sort(LowestBlockIdFirst);
113 } 111 }
114 112
115 113
116 void InliningContext::ReplaceCall(FlowGraph* caller_graph, 114 Definition* InliningContext::JoinReturns(BlockEntryInstr** exit_block,
117 Definition* call, 115 Instruction** last_instruction) {
118 FlowGraph* callee_graph) { 116 // First sort the list of exits by block id (caching return instruction
119 ASSERT(call->previous() != NULL); 117 // block entries as a side effect).
120 ASSERT(call->next() != NULL);
121 PrepareGraphs(caller_graph, call, callee_graph);
122
123 BlockEntryInstr* caller_entry = call->GetBlock();
124 TargetEntryInstr* callee_entry = callee_graph->graph_entry()->normal_entry();
125
126 // Insert the callee graph into the caller graph. First sort the list of
127 // exits by block id (recording block entries as a side effect).
128 SortExits(); 118 SortExits();
129 intptr_t num_exits = exits_.length(); 119 intptr_t num_exits = exits_.length();
130 if (num_exits == 0) { 120 if (num_exits == 0) {
131 // TODO(zerny): Add support for non-local exits, such as throw. 121 // TODO(zerny): Add support for non-local exits, such as throw.
132 UNREACHABLE(); 122 UNREACHABLE();
123 return NULL;
133 } else if (num_exits == 1) { 124 } else if (num_exits == 1) {
134 // For just one exit, replace the uses and remove the call from the graph.
135 call->ReplaceUsesWith(ValueAt(0)->definition());
136 ValueAt(0)->RemoveFromUseList(); 125 ValueAt(0)->RemoveFromUseList();
137 call->previous()->LinkTo(callee_entry->next()); 126 *exit_block = ExitBlockAt(0);
138 LastInstructionAt(0)->LinkTo(call->next()); 127 *last_instruction = LastInstructionAt(0);
139 // In case of control flow, locally update the predecessors, phis and 128 return call_->HasUses() ? ValueAt(0)->definition() : NULL;
140 // dominator tree. 129 } else {
141 // TODO(zerny): should we leave the dominator tree since we recompute it 130 // Create a join of the returns.
142 // after a full inlining pass? 131 intptr_t join_id = caller_graph_->max_block_id() + 1;
143 if (callee_graph->preorder().length() > 2) { 132 caller_graph_->set_max_block_id(join_id);
144 BlockEntryInstr* exit_block = ExitBlockAt(0); 133 JoinEntryInstr* join =
145 // Pictorially, the graph structure is: 134 new JoinEntryInstr(join_id, CatchClauseNode::kInvalidTryIndex);
146 // 135
147 // Bc : caller_entry Bi : callee_entry 136 // The dominator set of the join is the intersection of the dominator
148 // before_call inlined_head 137 // sets of all the predecessors. If we keep the dominator sets ordered
149 // call ... other blocks ... 138 // by height in the dominator tree, we can also get the immediate
150 // after_call Be : exit_block 139 // dominator of the join node from the intersection.
151 // inlined_foot 140 //
152 // And becomes: 141 // block_dominators is the dominator set for each block, ordered from
153 // 142 // the immediate dominator to the root of the dominator tree. This is
154 // Bc : caller_entry 143 // the order we collect them in (adding at the end).
155 // before_call 144 //
156 // inlined_head 145 // join_dominators is the join's dominators ordered from the root of the
157 // ... other blocks ... 146 // dominator tree to the immediate dominator. This order supports
158 // Be : exit_block 147 // removing during intersection by truncating the list.
159 // inlined_foot 148 GrowableArray<BlockEntryInstr*> block_dominators;
160 // after_call 149 GrowableArray<BlockEntryInstr*> join_dominators;
161 // 150 for (intptr_t i = 0; i < num_exits; ++i) {
162 // For 'after_call', caller entry (Bc) is replaced by callee exit (Be). 151 // Add the control-flow edge.
163 caller_entry->ReplaceAsPredecessorWith(exit_block); 152 LastInstructionAt(i)->Goto(join);
164 // For 'inlined_head', callee entry (Bi) is replaced by caller entry (Bc). 153 ExitBlockAt(i)->set_last_instruction(LastInstructionAt(i)->next());
165 callee_entry->ReplaceAsPredecessorWith(caller_entry); 154 join->predecessors_.Add(ExitBlockAt(i));
166 // The callee exit is now the immediate dominator of blocks whose 155
167 // immediate dominator was the caller entry. 156 // Collect the block's dominators.
168 ASSERT(exit_block->dominated_blocks().is_empty()); 157 block_dominators.Clear();
169 for (intptr_t i = 0; i < caller_entry->dominated_blocks().length(); ++i) { 158 BlockEntryInstr* dominator = ExitBlockAt(i)->dominator();
170 BlockEntryInstr* block = caller_entry->dominated_blocks()[i]; 159 while (dominator != NULL) {
171 block->set_dominator(exit_block); 160 block_dominators.Add(dominator);
172 exit_block->AddDominatedBlock(block); 161 dominator = dominator->dominator();
173 } 162 }
174 // The caller entry is now the immediate dominator of blocks whose 163
175 // immediate dominator was the callee entry. 164 if (i == 0) {
176 caller_entry->ClearDominatedBlocks(); 165 // The initial dominator set is the first predecessor's dominator
177 for (intptr_t i = 0; i < callee_entry->dominated_blocks().length(); ++i) { 166 // set. Reverse it.
178 BlockEntryInstr* block = callee_entry->dominated_blocks()[i]; 167 for (intptr_t j = block_dominators.length() - 1; j >= 0; --j) {
179 block->set_dominator(caller_entry); 168 join_dominators.Add(block_dominators[j]);
180 caller_entry->AddDominatedBlock(block); 169 }
170 } else {
171 // Intersect the block's dominators with the join's dominators so far.
172 intptr_t last = block_dominators.length() - 1;
173 for (intptr_t j = 0; j < join_dominators.length(); ++j) {
174 intptr_t k = last - j; // Corresponding index in block_dominators.
175 if ((k < 0) || (join_dominators[j] != block_dominators[k])) {
176 // We either exhausted the dominators for this block before
177 // exhausting the current intersection, or else we found a block
178 // on the path from the root of the tree that is not in common.
179 ASSERT(j >= 1);
Florian Schneider 2013/04/11 11:30:18 I think you can even assert that there are always
180 join_dominators.TruncateTo(j - 1);
181 break;
182 }
183 }
181 } 184 }
182 } 185 }
183 } else { 186 // The immediate dominator of the join is the last one in the ordered
184 // Create a join of the returns. 187 // intersection.
185 intptr_t join_id = caller_graph->max_block_id() + 1; 188 join->set_dominator(join_dominators.Last());
186 caller_graph->set_max_block_id(join_id); 189 join_dominators.Last()->AddDominatedBlock(join);
187 JoinEntryInstr* join = 190 *exit_block = join;
188 new JoinEntryInstr(join_id, CatchClauseNode::kInvalidTryIndex); 191 *last_instruction = join;
189 for (intptr_t i = 0; i < num_exits; ++i) { 192
190 LastInstructionAt(i)->Goto(join);
191 // Directly add the predecessors of the join in ascending block id order.
192 join->predecessors_.Add(ExitBlockAt(i));
193 }
194 // If the call has uses, create a phi of the returns. 193 // If the call has uses, create a phi of the returns.
195 if (call->HasUses()) { 194 if (call_->HasUses()) {
196 // Add a phi of the return values. 195 // Add a phi of the return values.
197 PhiInstr* phi = new PhiInstr(join, num_exits); 196 PhiInstr* phi = new PhiInstr(join, num_exits);
198 phi->set_ssa_temp_index(caller_graph->alloc_ssa_temp_index()); 197 phi->set_ssa_temp_index(caller_graph_->alloc_ssa_temp_index());
199 phi->mark_alive(); 198 phi->mark_alive();
200 for (intptr_t i = 0; i < num_exits; ++i) { 199 for (intptr_t i = 0; i < num_exits; ++i) {
201 phi->SetInputAt(i, ValueAt(i)); 200 phi->SetInputAt(i, ValueAt(i));
202 } 201 }
203 join->InsertPhi(phi); 202 join->InsertPhi(phi);
204 // Replace uses of the call with the phi. 203 return phi;
205 call->ReplaceUsesWith(phi);
206 } else { 204 } else {
207 // In the case that the result is unused, remove the return value uses 205 // In the case that the result is unused, remove the return value uses
208 // from their definition's use list. 206 // from their definition's use list.
209 for (intptr_t i = 0; i < num_exits; ++i) { 207 for (intptr_t i = 0; i < num_exits; ++i) {
210 ValueAt(i)->RemoveFromUseList(); 208 ValueAt(i)->RemoveFromUseList();
211 } 209 }
210 return NULL;
212 } 211 }
213 // Remove the call from the graph.
214 call->previous()->LinkTo(callee_entry->next());
215 join->LinkTo(call->next());
216 // Replace the blocks after splitting (see comment in the len=1 case above).
217 caller_entry->ReplaceAsPredecessorWith(join);
218 callee_entry->ReplaceAsPredecessorWith(caller_entry);
219 // Update the last instruction pointers on each exit block to the new goto.
220 for (intptr_t i = 0; i < num_exits; ++i) {
221 ExitBlockAt(i)->set_last_instruction(LastInstructionAt(i)->next());
222 }
223 // Mark that the dominator tree is invalid.
224 // TODO(zerny): Compute the dominator frontier locally.
225 caller_graph->InvalidateDominatorTree();
226 } 212 }
227 } 213 }
228 214
215
216 void InliningContext::ReplaceCall(FlowGraph* callee_graph) {
217 ASSERT(call_->previous() != NULL);
218 ASSERT(call_->next() != NULL);
219 PrepareGraphs(callee_graph);
220
221 BlockEntryInstr* call_block = call_->GetBlock();
222 TargetEntryInstr* callee_entry = callee_graph->graph_entry()->normal_entry();
223
224 // Insert the callee graph into the caller graph.
225 BlockEntryInstr* callee_exit = NULL;
226 Instruction* callee_last_instruction = NULL;
227 Definition* callee_result = JoinReturns(&callee_exit,
228 &callee_last_instruction);
229 if (callee_result != NULL) {
230 call_->ReplaceUsesWith(callee_result);
231 }
232 if (callee_last_instruction == callee_entry) {
233 // There are no instructions in the inlined function (e.g., it might be
234 // a return of a parameter or a return of a constant defined in the
235 // initial definitions).
236 call_->previous()->LinkTo(call_->next());
237 } else {
238 call_->previous()->LinkTo(callee_entry->next());
239 callee_last_instruction->LinkTo(call_->next());
240 }
241 if (callee_exit != callee_entry) {
242 // In case of control flow, locally update the predecessors, phis and
243 // dominator tree.
244 //
245 // Pictorially, the graph structure is:
246 //
247 // Bc : call_block Bi : callee_entry
248 // before_call inlined_head
249 // call ... other blocks ...
250 // after_call Be : callee_exit
251 // inlined_foot
252 // And becomes:
253 //
254 // Bc : call_block
255 // before_call
256 // inlined_head
257 // ... other blocks ...
258 // Be : callee_exit
259 // inlined_foot
260 // after_call
261 //
262 // For successors of 'after_call', the call block (Bc) is replaced as a
263 // predecessor by the callee exit (Be).
264 call_block->ReplaceAsPredecessorWith(callee_exit);
265 // For successors of 'inlined_head', the callee entry (Bi) is replaced
266 // as a predecessor by the call block (Bc).
267 callee_entry->ReplaceAsPredecessorWith(call_block);
268
269 // The callee exit is now the immediate dominator of blocks whose
270 // immediate dominator was the call block.
271 ASSERT(callee_exit->dominated_blocks().is_empty());
272 for (intptr_t i = 0; i < call_block->dominated_blocks().length(); ++i) {
273 BlockEntryInstr* block = call_block->dominated_blocks()[i];
274 block->set_dominator(callee_exit);
275 callee_exit->AddDominatedBlock(block);
276 }
277 // The call block is now the immediate dominator of blocks whose
278 // immediate dominator was the callee entry.
279 call_block->ClearDominatedBlocks();
280 for (intptr_t i = 0; i < callee_entry->dominated_blocks().length(); ++i) {
281 BlockEntryInstr* block = callee_entry->dominated_blocks()[i];
282 block->set_dominator(call_block);
283 call_block->AddDominatedBlock(block);
284 }
285 }
286 }
287
229 288
230 void EffectGraphVisitor::Append(const EffectGraphVisitor& other_fragment) { 289 void EffectGraphVisitor::Append(const EffectGraphVisitor& other_fragment) {
231 ASSERT(is_open()); 290 ASSERT(is_open());
232 if (other_fragment.is_empty()) return; 291 if (other_fragment.is_empty()) return;
233 if (is_empty()) { 292 if (is_empty()) {
234 entry_ = other_fragment.entry(); 293 entry_ = other_fragment.entry();
235 exit_ = other_fragment.exit(); 294 exit_ = other_fragment.exit();
236 } else { 295 } else {
237 exit()->LinkTo(other_fragment.entry()); 296 exit()->LinkTo(other_fragment.entry());
238 exit_ = other_fragment.exit(); 297 exit_ = other_fragment.exit();
(...skipping 3105 matching lines...) Expand 10 before | Expand all | Expand 10 after
3344 intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1; 3403 intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1;
3345 char* chars = Isolate::Current()->current_zone()->Alloc<char>(len); 3404 char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
3346 OS::SNPrint(chars, len, kFormat, function_name, reason); 3405 OS::SNPrint(chars, len, kFormat, function_name, reason);
3347 const Error& error = Error::Handle( 3406 const Error& error = Error::Handle(
3348 LanguageError::New(String::Handle(String::New(chars)))); 3407 LanguageError::New(String::Handle(String::New(chars))));
3349 Isolate::Current()->long_jump_base()->Jump(1, error); 3408 Isolate::Current()->long_jump_base()->Jump(1, error);
3350 } 3409 }
3351 3410
3352 3411
3353 } // namespace dart 3412 } // namespace dart
OLDNEW
« no previous file with comments | « runtime/vm/flow_graph_builder.h ('k') | runtime/vm/flow_graph_inliner.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698