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