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

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

Powered by Google App Engine
This is Rietveld 408576698