| 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/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 |
| OLD | NEW |