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

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

Issue 10857016: Refactored FlowGraphBuilder into a separate FlowGraph representation. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Revision based on Kevin's review. Created 8 years, 4 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_allocator.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 #include "vm/flow_graph.h"
6
7 #include "vm/bit_vector.h"
8 #include "vm/flow_graph_builder.h"
9 #include "vm/intermediate_language.h"
10 #include "vm/longjump.h"
11
12 namespace dart {
13
14 FlowGraph::FlowGraph(const FlowGraphBuilder& builder,
15 GraphEntryInstr* graph_entry)
16 : parent_(),
17 assigned_vars_(),
18 current_ssa_temp_index_(0),
19 parsed_function_(builder.parsed_function()),
20 copied_parameter_count_(builder.copied_parameter_count()),
21 non_copied_parameter_count_(builder.non_copied_parameter_count()),
22 stack_local_count_(builder.stack_local_count()),
23 graph_entry_(graph_entry),
24 preorder_(),
25 postorder_(),
26 reverse_postorder_() {
27 DiscoverBlocks();
28 }
29
30
31 void FlowGraph::DiscoverBlocks() {
32 // Initialize state.
33 preorder_.TruncateTo(0);
34 postorder_.TruncateTo(0);
35 reverse_postorder_.TruncateTo(0);
36 parent_.TruncateTo(0);
37 assigned_vars_.TruncateTo(0);
38 // Perform a depth-first traversal of the graph to build preorder and
39 // postorder block orders.
40 graph_entry_->DiscoverBlocks(NULL, // Entry block predecessor.
41 &preorder_,
42 &postorder_,
43 &parent_,
44 &assigned_vars_,
45 variable_count(),
46 non_copied_parameter_count());
47 // Number blocks in reverse postorder.
48 intptr_t block_count = postorder_.length();
49 for (intptr_t i = 0; i < block_count; ++i) {
50 postorder_[i]->set_block_id(block_count - i - 1);
51 reverse_postorder_.Add(postorder_[block_count - i - 1]);
52 }
53 // Link instructions backwards for optimized compilation.
54 // TODO(zerny): The builder should do this at construction time.
55 for (intptr_t i = 0; i < block_count; ++i) {
56 BlockEntryInstr* entry = postorder_[i];
57 Instruction* previous = entry;
58 for (ForwardInstructionIterator it(entry); !it.Done(); it.Advance()) {
59 Instruction* current = it.Current();
60 current->set_previous(previous);
61 previous = current;
62 }
63 }
64 }
65
66
67 void FlowGraph::ComputeSSA() {
68 GrowableArray<BitVector*> dominance_frontier;
69 ComputeDominators(&preorder_, &parent_, &dominance_frontier);
70 InsertPhis(preorder_, assigned_vars_, dominance_frontier);
71 GrowableArray<PhiInstr*> live_phis;
72 // Rename uses to reference inserted phis where appropriate.
73 // Collect phis that reach a non-environment use.
74 Rename(&live_phis);
75 // Propagate alive mark transitively from alive phis.
76 MarkLivePhis(&live_phis);
77 }
78
79
80 // Compute immediate dominators and the dominance frontier for each basic
81 // block. As a side effect of the algorithm, sets the immediate dominator
82 // of each basic block.
83 //
84 // preorder: an input list of basic block entries in preorder. The
85 // algorithm relies on the block ordering.
86 //
87 // parent: an input parameter encoding a depth-first spanning tree of
88 // the control flow graph. The array maps the preorder block
89 // number of a block to the preorder block number of its spanning
90 // tree parent.
91 //
92 // dominance_frontier: an output parameter encoding the dominance frontier.
93 // The array maps the preorder block number of a block to the set of
94 // (preorder block numbers of) blocks in the dominance frontier.
95 void FlowGraph::ComputeDominators(
96 GrowableArray<BlockEntryInstr*>* preorder,
97 GrowableArray<intptr_t>* parent,
98 GrowableArray<BitVector*>* dominance_frontier) {
99 // Use the SEMI-NCA algorithm to compute dominators. This is a two-pass
100 // version of the Lengauer-Tarjan algorithm (LT is normally three passes)
101 // that eliminates a pass by using nearest-common ancestor (NCA) to
102 // compute immediate dominators from semidominators. It also removes a
103 // level of indirection in the link-eval forest data structure.
104 //
105 // The algorithm is described in Georgiadis, Tarjan, and Werneck's
106 // "Finding Dominators in Practice".
107 // See http://www.cs.princeton.edu/~rwerneck/dominators/ .
108
109 // All arrays are maps between preorder basic-block numbers.
110 intptr_t size = parent->length();
111 GrowableArray<intptr_t> idom(size); // Immediate dominator.
112 GrowableArray<intptr_t> semi(size); // Semidominator.
113 GrowableArray<intptr_t> label(size); // Label for link-eval forest.
114
115 // 1. First pass: compute semidominators as in Lengauer-Tarjan.
116 // Semidominators are computed from a depth-first spanning tree and are an
117 // approximation of immediate dominators.
118
119 // Use a link-eval data structure with path compression. Implement path
120 // compression in place by mutating the parent array. Each block has a
121 // label, which is the minimum block number on the compressed path.
122
123 // Initialize idom, semi, and label used by SEMI-NCA. Initialize the
124 // dominance frontier output array.
125 for (intptr_t i = 0; i < size; ++i) {
126 idom.Add((*parent)[i]);
127 semi.Add(i);
128 label.Add(i);
129 dominance_frontier->Add(new BitVector(size));
130 }
131
132 // Loop over the blocks in reverse preorder (not including the graph
133 // entry).
134 for (intptr_t block_index = size - 1; block_index >= 1; --block_index) {
135 // Loop over the predecessors.
136 BlockEntryInstr* block = (*preorder)[block_index];
137 for (intptr_t i = 0, count = block->PredecessorCount(); i < count; ++i) {
138 BlockEntryInstr* pred = block->PredecessorAt(i);
139 ASSERT(pred != NULL);
140
141 // Look for the semidominator by ascending the semidominator path
142 // starting from pred.
143 intptr_t pred_index = pred->preorder_number();
144 intptr_t best = pred_index;
145 if (pred_index > block_index) {
146 CompressPath(block_index, pred_index, parent, &label);
147 best = label[pred_index];
148 }
149
150 // Update the semidominator if we've found a better one.
151 semi[block_index] = Utils::Minimum(semi[block_index], semi[best]);
152 }
153
154 // Now use label for the semidominator.
155 label[block_index] = semi[block_index];
156 }
157
158 // 2. Compute the immediate dominators as the nearest common ancestor of
159 // spanning tree parent and semidominator, for all blocks except the entry.
160 for (intptr_t block_index = 1; block_index < size; ++block_index) {
161 intptr_t dom_index = idom[block_index];
162 while (dom_index > semi[block_index]) {
163 dom_index = idom[dom_index];
164 }
165 idom[block_index] = dom_index;
166 (*preorder)[block_index]->set_dominator((*preorder)[dom_index]);
167 (*preorder)[dom_index]->AddDominatedBlock((*preorder)[block_index]);
168 }
169
170 // 3. Now compute the dominance frontier for all blocks. This is
171 // algorithm in "A Simple, Fast Dominance Algorithm" (Figure 5), which is
172 // attributed to a paper by Ferrante et al. There is no bookkeeping
173 // required to avoid adding a block twice to the same block's dominance
174 // frontier because we use a set to represent the dominance frontier.
175 for (intptr_t block_index = 0; block_index < size; ++block_index) {
176 BlockEntryInstr* block = (*preorder)[block_index];
177 intptr_t count = block->PredecessorCount();
178 if (count <= 1) continue;
179 for (intptr_t i = 0; i < count; ++i) {
180 BlockEntryInstr* runner = block->PredecessorAt(i);
181 while (runner != block->dominator()) {
182 (*dominance_frontier)[runner->preorder_number()]->Add(block_index);
183 runner = runner->dominator();
184 }
185 }
186 }
187 }
188
189
190 void FlowGraph::CompressPath(intptr_t start_index,
191 intptr_t current_index,
192 GrowableArray<intptr_t>* parent,
193 GrowableArray<intptr_t>* label) {
194 intptr_t next_index = (*parent)[current_index];
195 if (next_index > start_index) {
196 CompressPath(start_index, next_index, parent, label);
197 (*label)[current_index] =
198 Utils::Minimum((*label)[current_index], (*label)[next_index]);
199 (*parent)[current_index] = (*parent)[next_index];
200 }
201 }
202
203
204 void FlowGraph::InsertPhis(
205 const GrowableArray<BlockEntryInstr*>& preorder,
206 const GrowableArray<BitVector*>& assigned_vars,
207 const GrowableArray<BitVector*>& dom_frontier) {
208 const intptr_t block_count = preorder.length();
209 // Map preorder block number to the highest variable index that has a phi
210 // in that block. Use it to avoid inserting multiple phis for the same
211 // variable.
212 GrowableArray<intptr_t> has_already(block_count);
213 // Map preorder block number to the highest variable index for which the
214 // block went on the worklist. Use it to avoid adding the same block to
215 // the worklist more than once for the same variable.
216 GrowableArray<intptr_t> work(block_count);
217
218 // Initialize has_already and work.
219 for (intptr_t block_index = 0; block_index < block_count; ++block_index) {
220 has_already.Add(-1);
221 work.Add(-1);
222 }
223
224 // Insert phis for each variable in turn.
225 GrowableArray<BlockEntryInstr*> worklist;
226 for (intptr_t var_index = 0; var_index < variable_count(); ++var_index) {
227 // Add to the worklist each block containing an assignment.
228 for (intptr_t block_index = 0; block_index < block_count; ++block_index) {
229 if (assigned_vars[block_index]->Contains(var_index)) {
230 work[block_index] = var_index;
231 worklist.Add(preorder[block_index]);
232 }
233 }
234
235 while (!worklist.is_empty()) {
236 BlockEntryInstr* current = worklist.Last();
237 worklist.RemoveLast();
238 // Ensure a phi for each block in the dominance frontier of current.
239 for (BitVector::Iterator it(dom_frontier[current->preorder_number()]);
240 !it.Done();
241 it.Advance()) {
242 int index = it.Current();
243 if (has_already[index] < var_index) {
244 BlockEntryInstr* block = preorder[index];
245 ASSERT(block->IsJoinEntry());
246 block->AsJoinEntry()->InsertPhi(var_index, variable_count());
247 has_already[index] = var_index;
248 if (work[index] < var_index) {
249 work[index] = var_index;
250 worklist.Add(block);
251 }
252 }
253 }
254 }
255 }
256 }
257
258
259 void FlowGraph::Rename(GrowableArray<PhiInstr*>* live_phis) {
260 // TODO(fschneider): Support catch-entry.
261 if (graph_entry_->SuccessorCount() > 1) {
262 Bailout("Catch-entry support in SSA.");
263 }
264
265 // Initialize start environment.
266 GrowableArray<Value*> start_env(variable_count());
267 for (intptr_t i = 0; i < parameter_count(); ++i) {
268 ParameterInstr* param = new ParameterInstr(i);
269 param->set_ssa_temp_index(alloc_ssa_temp_index()); // New SSA temp.
270 start_env.Add(new UseVal(param));
271 }
272
273 // All locals are initialized with #null.
274 Value* null_value = new ConstantVal(Object::ZoneHandle());
275 while (start_env.length() < variable_count()) {
276 start_env.Add(null_value);
277 }
278 graph_entry_->set_start_env(
279 new Environment(start_env, non_copied_parameter_count_));
280
281 BlockEntryInstr* normal_entry = graph_entry_->SuccessorAt(0);
282 ASSERT(normal_entry != NULL); // Must have entry.
283 GrowableArray<Value*> env(variable_count());
284 env.AddArray(start_env);
285 RenameRecursive(normal_entry, &env, live_phis);
286 }
287
288
289 // Helper to a copy a value iff it is a UseVal.
290 static Value* CopyValue(Value* value) {
291 return value->IsUse()
292 ? new UseVal(value->AsUse()->definition())
293 : value;
294 }
295
296
297 void FlowGraph::RenameRecursive(BlockEntryInstr* block_entry,
298 GrowableArray<Value*>* env,
299 GrowableArray<PhiInstr*>* live_phis) {
300 // 1. Process phis first.
301 if (block_entry->IsJoinEntry()) {
302 JoinEntryInstr* join = block_entry->AsJoinEntry();
303 if (join->phis() != NULL) {
304 for (intptr_t i = 0; i < join->phis()->length(); ++i) {
305 PhiInstr* phi = (*join->phis())[i];
306 if (phi != NULL) {
307 (*env)[i] = new UseVal(phi);
308 phi->set_ssa_temp_index(alloc_ssa_temp_index()); // New SSA temp.
309 }
310 }
311 }
312 }
313
314 // 2. Process normal instructions.
315 for (ForwardInstructionIterator it(block_entry); !it.Done(); it.Advance()) {
316 Instruction* current = it.Current();
317 // Attach current environment to the instruction. First, each instruction
318 // gets a full copy of the environment. Later we optimize this by
319 // eliminating unnecessary environments.
320 current->set_env(new Environment(*env, non_copied_parameter_count_));
321
322 // 2a. Handle uses:
323 // Update expression stack environment for each use.
324 // For each use of a LoadLocal or StoreLocal: Replace it with the value
325 // from the environment.
326 for (intptr_t i = current->InputCount() - 1; i >= 0; --i) {
327 Value* v = current->InputAt(i);
328 if (!v->IsUse()) continue;
329 // Update expression stack.
330 ASSERT(env->length() > variable_count());
331
332 Value* input_value = env->Last();
333 ASSERT(input_value->IsUse());
334 env->RemoveLast();
335
336 BindInstr* as_bind = v->AsUse()->definition()->AsBind();
337 if ((as_bind != NULL) &&
338 (as_bind->computation()->IsLoadLocal() ||
339 as_bind->computation()->IsStoreLocal())) {
340 // Assert exactly one use.
341 ASSERT(as_bind->use_list() == v);
342 ASSERT(as_bind->use_list()->next_use() == NULL);
343 // Remove the use, its defintion and copy the environment value.
344 v->RemoveFromUseList();
345 as_bind->RemoveFromGraph();
346 current->SetInputAt(i, CopyValue(input_value));
347 }
348 }
349
350 // Drop pushed arguments for calls.
351 for (intptr_t j = 0; j < current->ArgumentCount(); j++) {
352 env->RemoveLast();
353 }
354
355 // 2b. Handle LoadLocal and StoreLocal.
356 // For each LoadLocal: Remove it from the graph.
357 // For each StoreLocal: Remove it from the graph and update the environment.
358 BindInstr* bind = current->AsBind();
359 if (bind != NULL) {
360 LoadLocalComp* load = bind->computation()->AsLoadLocal();
361 StoreLocalComp* store = bind->computation()->AsStoreLocal();
362 if ((load != NULL) || (store != NULL)) {
363 intptr_t index;
364 if (store != NULL) {
365 index = store->local().BitIndexIn(non_copied_parameter_count_);
366 // Update renaming environment.
367 (*env)[index] = store->value();
368 } else {
369 // The graph construction ensures we do not have an unused LoadLocal
370 // computation.
371 ASSERT(bind->is_used());
372 index = load->local().BitIndexIn(non_copied_parameter_count_);
373
374 Value* value = (*env)[index];
375 if (value->IsUse()) {
376 PhiInstr* phi = value->AsUse()->definition()->AsPhi();
377 if ((phi != NULL) && !phi->is_alive()) {
378 phi->mark_alive();
379 live_phis->Add(phi);
380 }
381 }
382 }
383 // Update expression stack or remove from graph.
384 if (bind->is_used()) {
385 // Assert exactly one use.
386 ASSERT(bind->use_list() != NULL);
387 ASSERT(bind->use_list()->next_use() == NULL);
388 env->Add(CopyValue((*env)[index]));
389 // We remove load/store instructions when we find their use in 2a.
390 } else {
391 it.RemoveCurrentFromGraph();
392 }
393 } else {
394 // Not a load or store.
395 if (bind->is_used()) {
396 // Assign fresh SSA temporary and update expression stack.
397 bind->set_ssa_temp_index(alloc_ssa_temp_index());
398 env->Add(new UseVal(bind));
399 }
400 }
401 }
402
403 // 2c. Handle pushed argument.
404 PushArgumentInstr* push = current->AsPushArgument();
405 if (push != NULL) {
406 env->Add(new UseVal(push));
407 }
408 }
409
410 // 3. Process dominated blocks.
411 for (intptr_t i = 0; i < block_entry->dominated_blocks().length(); ++i) {
412 BlockEntryInstr* block = block_entry->dominated_blocks()[i];
413 GrowableArray<Value*> new_env(env->length());
414 new_env.AddArray(*env);
415 RenameRecursive(block, &new_env, live_phis);
416 }
417
418 // 4. Process successor block. We have edge-split form, so that only blocks
419 // with one successor can have a join block as successor.
420 if ((block_entry->last_instruction()->SuccessorCount() == 1) &&
421 block_entry->last_instruction()->SuccessorAt(0)->IsJoinEntry()) {
422 JoinEntryInstr* successor =
423 block_entry->last_instruction()->SuccessorAt(0)->AsJoinEntry();
424 intptr_t pred_index = successor->IndexOfPredecessor(block_entry);
425 ASSERT(pred_index >= 0);
426 if (successor->phis() != NULL) {
427 for (intptr_t i = 0; i < successor->phis()->length(); ++i) {
428 PhiInstr* phi = (*successor->phis())[i];
429 if (phi != NULL) {
430 // Rename input operand and make a copy if it is a UseVal.
431 phi->SetInputAt(pred_index, CopyValue((*env)[i]));
432 }
433 }
434 }
435 }
436 }
437
438
439 void FlowGraph::MarkLivePhis(GrowableArray<PhiInstr*>* live_phis) {
440 while (!live_phis->is_empty()) {
441 PhiInstr* phi = live_phis->Last();
442 live_phis->RemoveLast();
443 for (intptr_t i = 0; i < phi->InputCount(); i++) {
444 Value* val = phi->InputAt(i);
445 if (!val->IsUse()) continue;
446 PhiInstr* used_phi = val->AsUse()->definition()->AsPhi();
447 if ((used_phi != NULL) && !used_phi->is_alive()) {
448 used_phi->mark_alive();
449 live_phis->Add(used_phi);
450 }
451 }
452 }
453 }
454
455
456 void FlowGraph::Bailout(const char* reason) const {
457 const char* kFormat = "FlowGraph Bailout: %s %s";
458 const char* function_name = parsed_function_.function().ToCString();
459 intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1;
460 char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
461 OS::SNPrint(chars, len, kFormat, function_name, reason);
462 const Error& error = Error::Handle(
463 LanguageError::New(String::Handle(String::New(chars))));
464 Isolate::Current()->long_jump_base()->Jump(1, error);
465 }
466
467
468 } // namespace dart
OLDNEW
« no previous file with comments | « runtime/vm/flow_graph.h ('k') | runtime/vm/flow_graph_allocator.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698