OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "src/compiler/scheduler.h" |
| 6 |
| 7 #include "src/compiler/graph.h" |
| 8 #include "src/compiler/graph-inl.h" |
| 9 #include "src/compiler/node.h" |
| 10 #include "src/compiler/node-properties.h" |
| 11 #include "src/compiler/node-properties-inl.h" |
| 12 #include "src/data-flow.h" |
| 13 |
| 14 namespace v8 { |
| 15 namespace internal { |
| 16 namespace compiler { |
| 17 |
| 18 Scheduler::Scheduler(Zone* zone) |
| 19 : zone_(zone), |
| 20 graph_(NULL), |
| 21 schedule_(NULL), |
| 22 branches_(NodeVector::allocator_type(zone)), |
| 23 calls_(NodeVector::allocator_type(zone)), |
| 24 deopts_(NodeVector::allocator_type(zone)), |
| 25 returns_(NodeVector::allocator_type(zone)), |
| 26 loops_and_merges_(NodeVector::allocator_type(zone)), |
| 27 node_block_placement_(BasicBlockVector::allocator_type(zone)), |
| 28 unscheduled_uses_(IntVector::allocator_type(zone)), |
| 29 scheduled_nodes_(NodeVectorVector::allocator_type(zone)), |
| 30 schedule_root_nodes_(NodeVector::allocator_type(zone)), |
| 31 schedule_early_rpo_index_(IntVector::allocator_type(zone)) {} |
| 32 |
| 33 |
| 34 Scheduler::Scheduler(Zone* zone, Graph* graph, Schedule* schedule) |
| 35 : zone_(zone), |
| 36 graph_(graph), |
| 37 schedule_(schedule), |
| 38 branches_(NodeVector::allocator_type(zone)), |
| 39 calls_(NodeVector::allocator_type(zone)), |
| 40 deopts_(NodeVector::allocator_type(zone)), |
| 41 returns_(NodeVector::allocator_type(zone)), |
| 42 loops_and_merges_(NodeVector::allocator_type(zone)), |
| 43 node_block_placement_(BasicBlockVector::allocator_type(zone)), |
| 44 unscheduled_uses_(IntVector::allocator_type(zone)), |
| 45 scheduled_nodes_(NodeVectorVector::allocator_type(zone)), |
| 46 schedule_root_nodes_(NodeVector::allocator_type(zone)), |
| 47 schedule_early_rpo_index_(IntVector::allocator_type(zone)) {} |
| 48 |
| 49 |
| 50 Schedule* Scheduler::NewSchedule(Graph* graph) { |
| 51 graph_ = graph; |
| 52 schedule_ = new(zone_) Schedule(zone_); |
| 53 |
| 54 schedule_->AddNode(schedule_->end(), graph_->end()); |
| 55 |
| 56 PrepareAuxiliaryNodeData(); |
| 57 |
| 58 // Create basic blocks for each block and merge node in the graph. |
| 59 CreateBlocks(); |
| 60 |
| 61 // Wire the basic blocks together. |
| 62 WireBlocks(); |
| 63 |
| 64 PrepareAuxiliaryBlockData(); |
| 65 |
| 66 ComputeSpecialRPO(); |
| 67 GenerateImmediateDominatorTree(); |
| 68 |
| 69 PrepareUses(); |
| 70 ScheduleEarly(); |
| 71 ScheduleLate(); |
| 72 |
| 73 return schedule_; |
| 74 } |
| 75 |
| 76 |
| 77 class CreateBlockVisitor : public NullNodeVisitor { |
| 78 public: |
| 79 explicit CreateBlockVisitor(Scheduler* scheduler) : scheduler_(scheduler) {} |
| 80 |
| 81 GenericGraphVisit::Control Post(Node* node) { |
| 82 Schedule* schedule = scheduler_->schedule_; |
| 83 switch (node->opcode()) { |
| 84 case IrOpcode::kIfTrue: |
| 85 case IrOpcode::kIfFalse: |
| 86 case IrOpcode::kContinuation: |
| 87 case IrOpcode::kLazyDeoptimization: { |
| 88 BasicBlock* block = schedule->NewBasicBlock(); |
| 89 schedule->AddNode(block, node); |
| 90 break; |
| 91 } |
| 92 case IrOpcode::kLoop: |
| 93 case IrOpcode::kMerge: { |
| 94 BasicBlock* block = schedule->NewBasicBlock(); |
| 95 schedule->AddNode(block, node); |
| 96 scheduler_->loops_and_merges_.push_back(node); |
| 97 break; |
| 98 } |
| 99 case IrOpcode::kBranch: { |
| 100 scheduler_->branches_.push_back(node); |
| 101 break; |
| 102 } |
| 103 case IrOpcode::kDeoptimize: { |
| 104 scheduler_->deopts_.push_back(node); |
| 105 break; |
| 106 } |
| 107 case IrOpcode::kCall: { |
| 108 if (NodeProperties::CanLazilyDeoptimize(node)) { |
| 109 scheduler_->calls_.push_back(node); |
| 110 } |
| 111 break; |
| 112 } |
| 113 case IrOpcode::kReturn: |
| 114 scheduler_->returns_.push_back(node); |
| 115 break; |
| 116 default: |
| 117 break; |
| 118 } |
| 119 |
| 120 return GenericGraphVisit::CONTINUE; |
| 121 } |
| 122 |
| 123 private: |
| 124 Scheduler* scheduler_; |
| 125 }; |
| 126 |
| 127 |
| 128 void Scheduler::CreateBlocks() { |
| 129 CreateBlockVisitor create_blocks(this); |
| 130 if (FLAG_trace_turbo_scheduler) { |
| 131 PrintF("---------------- CREATING BLOCKS ------------------\n"); |
| 132 } |
| 133 schedule_->AddNode(schedule_->entry(), graph_->start()); |
| 134 graph_->VisitNodeInputsFromEnd(&create_blocks); |
| 135 } |
| 136 |
| 137 |
| 138 void Scheduler::WireBlocks() { |
| 139 if (FLAG_trace_turbo_scheduler) { |
| 140 PrintF("----------------- WIRING BLOCKS -------------------\n"); |
| 141 } |
| 142 AddSuccessorsForBranches(); |
| 143 AddSuccessorsForReturns(); |
| 144 AddSuccessorsForCalls(); |
| 145 AddSuccessorsForDeopts(); |
| 146 AddPredecessorsForLoopsAndMerges(); |
| 147 // TODO(danno): Handle Throw, et al. |
| 148 } |
| 149 |
| 150 |
| 151 void Scheduler::PrepareAuxiliaryNodeData() { |
| 152 unscheduled_uses_.resize(graph_->NodeCount(), 0); |
| 153 schedule_early_rpo_index_.resize(graph_->NodeCount(), 0); |
| 154 } |
| 155 |
| 156 |
| 157 void Scheduler::PrepareAuxiliaryBlockData() { |
| 158 Zone* zone = schedule_->zone(); |
| 159 scheduled_nodes_.resize(schedule_->BasicBlockCount(), |
| 160 NodeVector(NodeVector::allocator_type(zone))); |
| 161 schedule_->immediate_dominator_.resize(schedule_->BasicBlockCount(), NULL); |
| 162 } |
| 163 |
| 164 |
| 165 void Scheduler::AddPredecessorsForLoopsAndMerges() { |
| 166 for (NodeVectorIter i = loops_and_merges_.begin(); |
| 167 i != loops_and_merges_.end(); ++i) { |
| 168 Node* merge_or_loop = *i; |
| 169 BasicBlock* block = schedule_->block(merge_or_loop); |
| 170 ASSERT(block != NULL); |
| 171 // For all of the merge's control inputs, add a goto at the end to the |
| 172 // merge's basic block. |
| 173 for (InputIter j = (*i)->inputs().begin(); j != (*i)->inputs().end(); ++j) { |
| 174 if (NodeProperties::IsBasicBlockBegin(*i)) { |
| 175 BasicBlock* predecessor_block = schedule_->block(*j); |
| 176 if ((*j)->opcode() != IrOpcode::kReturn && |
| 177 (*j)->opcode() != IrOpcode::kDeoptimize) { |
| 178 ASSERT(predecessor_block != NULL); |
| 179 if (FLAG_trace_turbo_scheduler) { |
| 180 IrOpcode::Value opcode = (*i)->opcode(); |
| 181 PrintF("node %d (%s) in block %d -> block %d\n", |
| 182 (*i)->id(), |
| 183 IrOpcode::Mnemonic(opcode), |
| 184 predecessor_block->id(), |
| 185 block->id()); |
| 186 } |
| 187 schedule_->AddGoto(predecessor_block, block); |
| 188 } |
| 189 } |
| 190 } |
| 191 } |
| 192 } |
| 193 |
| 194 |
| 195 void Scheduler::AddSuccessorsForCalls() { |
| 196 for (NodeVectorIter i = calls_.begin(); i != calls_.end(); ++i) { |
| 197 Node* call = *i; |
| 198 ASSERT(call->opcode() == IrOpcode::kCall); |
| 199 ASSERT(NodeProperties::CanLazilyDeoptimize(call)); |
| 200 |
| 201 Node* lazy_deopt_node = NULL; |
| 202 Node* cont_node = NULL; |
| 203 // Find the continuation and lazy-deopt nodes among the uses. |
| 204 for (UseIter use_iter = call->uses().begin(); |
| 205 use_iter != call->uses().end(); ++use_iter) { |
| 206 switch ((*use_iter)->opcode()) { |
| 207 case IrOpcode::kContinuation: { |
| 208 ASSERT(cont_node == NULL); |
| 209 cont_node = *use_iter; |
| 210 break; |
| 211 } |
| 212 case IrOpcode::kLazyDeoptimization: { |
| 213 ASSERT(lazy_deopt_node == NULL); |
| 214 lazy_deopt_node = *use_iter; |
| 215 break; |
| 216 } |
| 217 default: |
| 218 break; |
| 219 } |
| 220 } |
| 221 ASSERT(lazy_deopt_node != NULL); |
| 222 ASSERT(cont_node != NULL); |
| 223 BasicBlock* cont_successor_block = schedule_->block(cont_node); |
| 224 BasicBlock* deopt_successor_block = schedule_->block(lazy_deopt_node); |
| 225 Node* call_block_node = NodeProperties::GetControlInput(call); |
| 226 BasicBlock* call_block = schedule_->block(call_block_node); |
| 227 if (FLAG_trace_turbo_scheduler) { |
| 228 IrOpcode::Value opcode = call->opcode(); |
| 229 PrintF("node %d (%s) in block %d -> block %d\n", call->id(), |
| 230 IrOpcode::Mnemonic(opcode), call_block->id(), |
| 231 cont_successor_block->id()); |
| 232 PrintF("node %d (%s) in block %d -> block %d\n", call->id(), |
| 233 IrOpcode::Mnemonic(opcode), call_block->id(), |
| 234 deopt_successor_block->id()); |
| 235 } |
| 236 schedule_->AddCall(call_block, call, cont_successor_block, |
| 237 deopt_successor_block); |
| 238 } |
| 239 } |
| 240 |
| 241 |
| 242 void Scheduler::AddSuccessorsForDeopts() { |
| 243 for (NodeVectorIter i = deopts_.begin(); i != deopts_.end(); ++i) { |
| 244 Node* deopt_block_node = NodeProperties::GetControlInput(*i); |
| 245 BasicBlock* deopt_block = schedule_->block(deopt_block_node); |
| 246 ASSERT(deopt_block != NULL); |
| 247 if (FLAG_trace_turbo_scheduler) { |
| 248 IrOpcode::Value opcode = (*i)->opcode(); |
| 249 PrintF("node %d (%s) in block %d -> end\n", (*i)->id(), |
| 250 IrOpcode::Mnemonic(opcode), deopt_block->id()); |
| 251 } |
| 252 schedule_->AddDeoptimize(deopt_block, *i); |
| 253 } |
| 254 } |
| 255 |
| 256 |
| 257 void Scheduler::AddSuccessorsForBranches() { |
| 258 for (NodeVectorIter i = branches_.begin(); i != branches_.end(); ++i) { |
| 259 Node* branch = *i; |
| 260 ASSERT(branch->opcode() == IrOpcode::kBranch); |
| 261 Node* branch_block_node = NodeProperties::GetControlInput(branch); |
| 262 BasicBlock* branch_block = schedule_->block(branch_block_node); |
| 263 ASSERT(branch_block != NULL); |
| 264 UseIter use_iter = branch->uses().begin(); |
| 265 Node* first_successor = *use_iter; |
| 266 ++use_iter; |
| 267 ASSERT(use_iter != branch->uses().end()); |
| 268 Node* second_successor = *use_iter; |
| 269 ASSERT(++use_iter == branch->uses().end()); |
| 270 Node* true_successor_node = first_successor->opcode() == IrOpcode::kIfTrue |
| 271 ? first_successor : second_successor; |
| 272 Node* false_successor_node = first_successor->opcode() == IrOpcode::kIfTrue |
| 273 ? second_successor : first_successor; |
| 274 ASSERT(true_successor_node->opcode() == IrOpcode::kIfTrue); |
| 275 ASSERT(false_successor_node->opcode() == IrOpcode::kIfFalse); |
| 276 BasicBlock* true_successor_block = schedule_->block(true_successor_node); |
| 277 BasicBlock* false_successor_block = schedule_->block(false_successor_node); |
| 278 ASSERT(true_successor_block != NULL); |
| 279 ASSERT(false_successor_block != NULL); |
| 280 if (FLAG_trace_turbo_scheduler) { |
| 281 IrOpcode::Value opcode = branch->opcode(); |
| 282 PrintF("node %d (%s) in block %d -> block %d\n", |
| 283 branch->id(), |
| 284 IrOpcode::Mnemonic(opcode), |
| 285 branch_block->id(), |
| 286 true_successor_block->id()); |
| 287 PrintF("node %d (%s) in block %d -> block %d\n", |
| 288 branch->id(), |
| 289 IrOpcode::Mnemonic(opcode), |
| 290 branch_block->id(), |
| 291 false_successor_block->id()); |
| 292 } |
| 293 schedule_->AddBranch(branch_block, branch, true_successor_block, |
| 294 false_successor_block); |
| 295 } |
| 296 } |
| 297 |
| 298 |
| 299 void Scheduler::AddSuccessorsForReturns() { |
| 300 for (NodeVectorIter i = returns_.begin(); i != returns_.end(); ++i) { |
| 301 Node* return_block_node = NodeProperties::GetControlInput(*i); |
| 302 BasicBlock* return_block = schedule_->block(return_block_node); |
| 303 ASSERT(return_block != NULL); |
| 304 if (FLAG_trace_turbo_scheduler) { |
| 305 IrOpcode::Value opcode = (*i)->opcode(); |
| 306 PrintF("node %d (%s) in block %d -> end\n", |
| 307 (*i)->id(), |
| 308 IrOpcode::Mnemonic(opcode), |
| 309 return_block->id()); |
| 310 } |
| 311 schedule_->AddReturn(return_block, *i); |
| 312 } |
| 313 } |
| 314 |
| 315 |
| 316 BasicBlock* Scheduler::GetCommonDominator(BasicBlock* b1, |
| 317 BasicBlock* b2) { |
| 318 while (b1 != b2) { |
| 319 int b1_rpo = GetRPONumber(b1); |
| 320 int b2_rpo = GetRPONumber(b2); |
| 321 ASSERT(b1_rpo != b2_rpo); |
| 322 if (b1_rpo < b2_rpo) { |
| 323 b2 = schedule_->immediate_dominator_[b2->id()]; |
| 324 } else { |
| 325 b1 = schedule_->immediate_dominator_[b1->id()]; |
| 326 } |
| 327 } |
| 328 return b1; |
| 329 } |
| 330 |
| 331 |
| 332 void Scheduler::GenerateImmediateDominatorTree() { |
| 333 // Build the dominator graph. TODO(danno): consider using Lengauer & Tarjan's |
| 334 // if this becomes really slow. |
| 335 if (FLAG_trace_turbo_scheduler) { |
| 336 PrintF("------------ IMMEDIATE BLOCK DOMINATORS -----------\n"); |
| 337 } |
| 338 for (size_t i = 0; i < schedule_->rpo_order_.size(); i++) { |
| 339 BasicBlock* current_rpo = schedule_->rpo_order_[i]; |
| 340 if (current_rpo != schedule_->entry()) { |
| 341 BasicBlock::Predecessors::iterator current_pred = |
| 342 current_rpo->predecessors().begin(); |
| 343 BasicBlock::Predecessors::iterator end = |
| 344 current_rpo->predecessors().end(); |
| 345 ASSERT(current_pred != end); |
| 346 BasicBlock* dominator = *current_pred; |
| 347 ++current_pred; |
| 348 // For multiple predecessors, walk up the rpo ordering until a common |
| 349 // dominator is found. |
| 350 int current_rpo_pos = GetRPONumber(current_rpo); |
| 351 while (current_pred != end) { |
| 352 // Don't examine backwards edges |
| 353 BasicBlock* pred = *current_pred; |
| 354 if (GetRPONumber(pred) < current_rpo_pos) { |
| 355 dominator = GetCommonDominator(dominator, *current_pred); |
| 356 } |
| 357 ++current_pred; |
| 358 } |
| 359 schedule_->immediate_dominator_[current_rpo->id()] = dominator; |
| 360 if (FLAG_trace_turbo_scheduler) { |
| 361 PrintF("Block %d's idom is %d\n", current_rpo->id(), dominator->id()); |
| 362 } |
| 363 } |
| 364 } |
| 365 } |
| 366 |
| 367 |
| 368 class ScheduleEarlyNodeVisitor : public NullNodeVisitor { |
| 369 public: |
| 370 explicit ScheduleEarlyNodeVisitor(Scheduler* scheduler) |
| 371 : has_changed_rpo_constraints_(true), |
| 372 scheduler_(scheduler), schedule_(scheduler->schedule_) {} |
| 373 |
| 374 GenericGraphVisit::Control Pre(Node* node) { |
| 375 int id = node->id(); |
| 376 int max_rpo = 0; |
| 377 // Fixed nodes already know their schedule early position. |
| 378 if (IsFixedNode(node)) { |
| 379 BasicBlock* block = schedule_->block(node); |
| 380 ASSERT(block != NULL); |
| 381 max_rpo = block->rpo_number_; |
| 382 if (scheduler_->schedule_early_rpo_index_[id] != max_rpo) { |
| 383 has_changed_rpo_constraints_ = true; |
| 384 } |
| 385 scheduler_->schedule_early_rpo_index_[id] = max_rpo; |
| 386 if (FLAG_trace_turbo_scheduler) { |
| 387 PrintF("Node %d pre-scheduled early at rpo limit %d\n", id, max_rpo); |
| 388 } |
| 389 } |
| 390 return GenericGraphVisit::CONTINUE; |
| 391 } |
| 392 |
| 393 GenericGraphVisit::Control Post(Node* node) { |
| 394 int id = node->id(); |
| 395 int max_rpo = 0; |
| 396 // Otherwise, the minimum rpo for the node is the max of all of the inputs. |
| 397 if (!IsFixedNode(node)) { |
| 398 ASSERT(!NodeProperties::IsBasicBlockBegin(node)); |
| 399 for (InputIter i = node->inputs().begin(); |
| 400 i != node->inputs().end(); ++i) { |
| 401 int control_rpo = scheduler_->schedule_early_rpo_index_[(*i)->id()]; |
| 402 if (control_rpo > max_rpo) { |
| 403 max_rpo = control_rpo; |
| 404 } |
| 405 } |
| 406 if (scheduler_->schedule_early_rpo_index_[id] != max_rpo) { |
| 407 has_changed_rpo_constraints_ = true; |
| 408 } |
| 409 scheduler_->schedule_early_rpo_index_[id] = max_rpo; |
| 410 if (FLAG_trace_turbo_scheduler) { |
| 411 PrintF("Node %d post-scheduled early at rpo limit %d\n", id, max_rpo); |
| 412 } |
| 413 } |
| 414 return GenericGraphVisit::CONTINUE; |
| 415 } |
| 416 |
| 417 static bool IsFixedNode(Node* node) { |
| 418 return NodeProperties::HasFixedSchedulePosition(node) || |
| 419 !NodeProperties::CanBeScheduled(node); |
| 420 } |
| 421 |
| 422 // TODO(mstarzinger): Dirty hack to unblock others, schedule early should be |
| 423 // rewritten to use a pre-order traversal from the start instead. |
| 424 bool has_changed_rpo_constraints_; |
| 425 |
| 426 private: |
| 427 Scheduler* scheduler_; |
| 428 Schedule* schedule_; |
| 429 }; |
| 430 |
| 431 |
| 432 void Scheduler::ScheduleEarly() { |
| 433 if (FLAG_trace_turbo_scheduler) { |
| 434 PrintF("------------------- SCHEDULE EARLY ----------------\n"); |
| 435 } |
| 436 |
| 437 int fixpoint_count = 0; |
| 438 ScheduleEarlyNodeVisitor visitor(this); |
| 439 while (visitor.has_changed_rpo_constraints_) { |
| 440 visitor.has_changed_rpo_constraints_ = false; |
| 441 graph_->VisitNodeInputsFromEnd(&visitor); |
| 442 fixpoint_count++; |
| 443 } |
| 444 |
| 445 if (FLAG_trace_turbo_scheduler) { |
| 446 PrintF("It took %d iterations to determine fixpoint\n", fixpoint_count); |
| 447 } |
| 448 } |
| 449 |
| 450 |
| 451 class PrepareUsesVisitor : public NullNodeVisitor { |
| 452 public: |
| 453 explicit PrepareUsesVisitor(Scheduler* scheduler) |
| 454 : scheduler_(scheduler), schedule_(scheduler->schedule_) {} |
| 455 |
| 456 GenericGraphVisit::Control Pre(Node* node) { |
| 457 // Some nodes must be scheduled explicitly to ensure they are in exactly the |
| 458 // right place; it's a convenient place during the preparation of use counts |
| 459 // to schedule them. |
| 460 if (!schedule_->IsScheduled(node) && |
| 461 NodeProperties::HasFixedSchedulePosition(node)) { |
| 462 if (FLAG_trace_turbo_scheduler) { |
| 463 PrintF("Fixed position node %d is unscheduled, scheduling now\n", |
| 464 node->id()); |
| 465 } |
| 466 IrOpcode::Value opcode = node->opcode(); |
| 467 BasicBlock* block = opcode == IrOpcode::kParameter |
| 468 ? schedule_->entry() |
| 469 : schedule_->block(NodeProperties::GetControlInput(node)); |
| 470 ASSERT(block != NULL); |
| 471 schedule_->AddNode(block, node); |
| 472 } |
| 473 |
| 474 if (NodeProperties::IsScheduleRoot(node)) { |
| 475 scheduler_->schedule_root_nodes_.push_back(node); |
| 476 } |
| 477 |
| 478 return GenericGraphVisit::CONTINUE; |
| 479 } |
| 480 |
| 481 void PostEdge(Node* from, int index, Node* to) { |
| 482 // If the edge is from an unscheduled node, then tally it in the use count |
| 483 // for all of its inputs. The same criterion will be used in ScheduleLate |
| 484 // for decrementing use counts. |
| 485 if (!schedule_->IsScheduled(from) && |
| 486 NodeProperties::CanBeScheduled(from)) { |
| 487 ASSERT(!NodeProperties::HasFixedSchedulePosition(from)); |
| 488 ++scheduler_->unscheduled_uses_[to->id()]; |
| 489 if (FLAG_trace_turbo_scheduler) { |
| 490 PrintF("Incrementing uses of node %d from %d to %d\n", to->id(), |
| 491 from->id(), |
| 492 scheduler_->unscheduled_uses_[to->id()]); |
| 493 } |
| 494 } |
| 495 } |
| 496 |
| 497 private: |
| 498 Scheduler* scheduler_; |
| 499 Schedule* schedule_; |
| 500 }; |
| 501 |
| 502 |
| 503 void Scheduler::PrepareUses() { |
| 504 if (FLAG_trace_turbo_scheduler) { |
| 505 PrintF("------------------- PREPARE USES ------------------\n"); |
| 506 } |
| 507 // Count the uses of every node, it will be used to ensure that all of a |
| 508 // node's uses are scheduled before the node itself. |
| 509 PrepareUsesVisitor prepare_uses(this); |
| 510 graph_->VisitNodeInputsFromEnd(&prepare_uses); |
| 511 } |
| 512 |
| 513 |
| 514 class ScheduleLateNodeVisitor : public NullNodeVisitor { |
| 515 public: |
| 516 explicit ScheduleLateNodeVisitor(Scheduler* scheduler) |
| 517 : scheduler_(scheduler), schedule_(scheduler_->schedule_) {} |
| 518 |
| 519 GenericGraphVisit::Control Pre(Node* node) { |
| 520 // Don't schedule nodes that cannot be scheduled or are already scheduled. |
| 521 if (!NodeProperties::CanBeScheduled(node) || schedule_->IsScheduled(node)) { |
| 522 return GenericGraphVisit::CONTINUE; |
| 523 } |
| 524 ASSERT(!NodeProperties::HasFixedSchedulePosition(node)); |
| 525 |
| 526 // If all the uses of a node have been scheduled, then the node itself can |
| 527 // be scheduled. |
| 528 bool eligible = scheduler_->unscheduled_uses_[node->id()] == 0; |
| 529 if (FLAG_trace_turbo_scheduler) { |
| 530 PrintF("Testing for schedule eligibility for node %d -> %s\n", |
| 531 node->id(), eligible ? "true" : "false"); |
| 532 } |
| 533 if (!eligible) return GenericGraphVisit::DEFER; |
| 534 |
| 535 // Determine the dominating block for all of the uses of this node. It is |
| 536 // the latest block that this node can be scheduled in. |
| 537 BasicBlock* block = NULL; |
| 538 for (Node::Uses::iterator i = node->uses().begin(); |
| 539 i != node->uses().end(); ++i) { |
| 540 BasicBlock* use_block = GetBlockForUse(i.edge()); |
| 541 block = block == NULL ? use_block |
| 542 : use_block == NULL ? block |
| 543 : scheduler_->GetCommonDominator(block, use_block); |
| 544 } |
| 545 ASSERT(block != NULL); |
| 546 |
| 547 int min_rpo = scheduler_->schedule_early_rpo_index_[node->id()]; |
| 548 if (FLAG_trace_turbo_scheduler) { |
| 549 PrintF("Schedule late conservative for node %d is block %d at " |
| 550 "loop depth %d, min rpo = %d\n", node->id(), block->id(), |
| 551 block->loop_depth_, min_rpo); |
| 552 } |
| 553 // Hoist nodes out of loops if possible. Nodes can be hoisted iteratively |
| 554 // into enlcosing loop pre-headers until they would preceed their |
| 555 // ScheduleEarly position. |
| 556 BasicBlock* hoist_block = block; |
| 557 while (hoist_block != NULL && hoist_block->rpo_number_ >= min_rpo) { |
| 558 if (hoist_block->loop_depth_ < block->loop_depth_) { |
| 559 block = hoist_block; |
| 560 if (FLAG_trace_turbo_scheduler) { |
| 561 PrintF("Hoisting node %d to block %d\n", node->id(), block->id()); |
| 562 } |
| 563 } |
| 564 // Try to hoist to the pre-header of the loop header. |
| 565 hoist_block = hoist_block->loop_header(); |
| 566 if (hoist_block != NULL) { |
| 567 BasicBlock* pre_header = schedule_->dominator(hoist_block); |
| 568 ASSERT(pre_header == NULL || |
| 569 *hoist_block->predecessors().begin() == pre_header); |
| 570 if (FLAG_trace_turbo_scheduler) { |
| 571 PrintF("Try hoist to pre-header block %d of loop header block %d," |
| 572 " depth would be %d\n", pre_header->id(), hoist_block->id(), |
| 573 pre_header->loop_depth_); |
| 574 } |
| 575 hoist_block = pre_header; |
| 576 } |
| 577 } |
| 578 |
| 579 ScheduleNode(block, node); |
| 580 |
| 581 return GenericGraphVisit::CONTINUE; |
| 582 } |
| 583 |
| 584 private: |
| 585 BasicBlock* GetBlockForUse(Node::Edge edge) { |
| 586 Node* use = edge.from(); |
| 587 IrOpcode::Value opcode = use->opcode(); |
| 588 // If the use is a phi, forward through the the phi to the basic block |
| 589 // corresponding to the phi's input. |
| 590 if (opcode == IrOpcode::kPhi || opcode == IrOpcode::kEffectPhi) { |
| 591 int index = edge.index(); |
| 592 if (FLAG_trace_turbo_scheduler) { |
| 593 PrintF("Use %d is input %d to a phi\n", use->id(), index); |
| 594 } |
| 595 use = NodeProperties::GetControlInput(use, 0); |
| 596 opcode = use->opcode(); |
| 597 ASSERT(opcode == IrOpcode::kMerge || opcode == IrOpcode::kLoop); |
| 598 use = NodeProperties::GetControlInput(use, index); |
| 599 } |
| 600 BasicBlock* result = schedule_->block(use); |
| 601 if (result == NULL) return NULL; |
| 602 if (FLAG_trace_turbo_scheduler) { |
| 603 PrintF("Must dominate use %d in block %d\n", use->id(), result->id()); |
| 604 } |
| 605 return result; |
| 606 } |
| 607 |
| 608 bool IsNodeEligible(Node* node) { |
| 609 bool eligible = scheduler_->unscheduled_uses_[node->id()] == 0; |
| 610 return eligible; |
| 611 } |
| 612 |
| 613 void ScheduleNode(BasicBlock* block, Node* node) { |
| 614 schedule_->PlanNode(block, node); |
| 615 scheduler_->scheduled_nodes_[block->id()].push_back(node); |
| 616 |
| 617 // Reduce the use count of the node's inputs to potentially make them |
| 618 // scheduable. |
| 619 for (InputIter i = node->inputs().begin(); i != node->inputs().end(); ++i) { |
| 620 ASSERT(scheduler_->unscheduled_uses_[(*i)->id()] > 0); |
| 621 --scheduler_->unscheduled_uses_[(*i)->id()]; |
| 622 if (FLAG_trace_turbo_scheduler) { |
| 623 PrintF("Decrementing use count for node %d from node %d (now %d)\n", |
| 624 (*i)->id(), i.edge().from()->id(), |
| 625 scheduler_->unscheduled_uses_[(*i)->id()]); |
| 626 if (scheduler_->unscheduled_uses_[(*i)->id()] == 0) { |
| 627 PrintF("node %d is now eligible for scheduling\n", (*i)->id()); |
| 628 } |
| 629 } |
| 630 } |
| 631 } |
| 632 |
| 633 Scheduler* scheduler_; |
| 634 Schedule* schedule_; |
| 635 }; |
| 636 |
| 637 |
| 638 void Scheduler::ScheduleLate() { |
| 639 if (FLAG_trace_turbo_scheduler) { |
| 640 PrintF("------------------- SCHEDULE LATE -----------------\n"); |
| 641 } |
| 642 |
| 643 // Schedule: Places nodes in dominator block of all their uses. |
| 644 ScheduleLateNodeVisitor schedule_late_visitor(this); |
| 645 |
| 646 for (NodeVectorIter i = schedule_root_nodes_.begin(); |
| 647 i != schedule_root_nodes_.end(); ++i) { |
| 648 GenericGraphVisit::Visit<ScheduleLateNodeVisitor, |
| 649 NodeInputIterationTraits<Node> >(graph_, *i, &schedule_late_visitor); |
| 650 } |
| 651 |
| 652 // Add collected nodes for basic blocks to their blocks in the right order. |
| 653 int block_num = 0; |
| 654 for (NodeVectorVectorIter i = scheduled_nodes_.begin(); |
| 655 i != scheduled_nodes_.end(); ++i) { |
| 656 for (NodeVectorRIter j = i->rbegin(); j != i->rend(); ++j) { |
| 657 schedule_->AddNode(schedule_->all_blocks_.at(block_num), *j); |
| 658 } |
| 659 block_num++; |
| 660 } |
| 661 } |
| 662 |
| 663 |
| 664 // Numbering for BasicBlockData.rpo_number_ for this block traversal: |
| 665 static const int kBlockOnStack = -2; |
| 666 static const int kBlockVisited1 = -3; |
| 667 static const int kBlockVisited2 = -4; |
| 668 static const int kBlockUnvisited1 = -1; |
| 669 static const int kBlockUnvisited2 = kBlockVisited1; |
| 670 |
| 671 struct SpecialRPOStackFrame { |
| 672 BasicBlock* block; |
| 673 int index; |
| 674 }; |
| 675 |
| 676 struct BlockList { |
| 677 BasicBlock* block; |
| 678 BlockList* next; |
| 679 |
| 680 BlockList* Add(Zone* zone, BasicBlock* b) { |
| 681 BlockList* list = static_cast<BlockList*>(zone->New(sizeof(BlockList))); |
| 682 list->block = b; |
| 683 list->next = this; |
| 684 return list; |
| 685 } |
| 686 |
| 687 void Serialize(BasicBlockVector* final_order) { |
| 688 for (BlockList* l = this; l != NULL; l = l->next) { |
| 689 l->block->rpo_number_ = static_cast<int>(final_order->size()); |
| 690 final_order->push_back(l->block); |
| 691 } |
| 692 } |
| 693 }; |
| 694 |
| 695 struct LoopInfo { |
| 696 BasicBlock* header; |
| 697 ZoneList<BasicBlock*>* outgoing; |
| 698 BitVector* members; |
| 699 LoopInfo* prev; |
| 700 BlockList* end; |
| 701 BlockList* start; |
| 702 |
| 703 void AddOutgoing(Zone* zone, BasicBlock* block) { |
| 704 if (outgoing == NULL) outgoing = new(zone) ZoneList<BasicBlock*>(2, zone); |
| 705 outgoing->Add(block, zone); |
| 706 } |
| 707 }; |
| 708 |
| 709 |
| 710 static int Push(SpecialRPOStackFrame* stack, int depth, BasicBlock* child, |
| 711 int unvisited) { |
| 712 if (child->rpo_number_ == unvisited) { |
| 713 stack[depth].block = child; |
| 714 stack[depth].index = 0; |
| 715 child->rpo_number_ = kBlockOnStack; |
| 716 return depth + 1; |
| 717 } |
| 718 return depth; |
| 719 } |
| 720 |
| 721 |
| 722 // Computes loop membership from the backedges of the control flow graph. |
| 723 static LoopInfo* ComputeLoopInfo( |
| 724 Zone* zone, SpecialRPOStackFrame* queue, int num_loops, int num_blocks, |
| 725 ZoneList<std::pair<BasicBlock*, int> >* backedges) { |
| 726 |
| 727 LoopInfo* loops = zone->NewArray<LoopInfo>(num_loops); |
| 728 memset(loops, 0, num_loops * sizeof(LoopInfo)); |
| 729 |
| 730 // Compute loop membership starting from backedges. |
| 731 // O(max(loop_depth) * max(|loop|) |
| 732 for (int i = 0; i < backedges->length(); i++) { |
| 733 BasicBlock* member = backedges->at(i).first; |
| 734 BasicBlock* header = member->SuccessorAt(backedges->at(i).second); |
| 735 int loop_num = header->loop_end_; |
| 736 if (loops[loop_num].header == NULL) { |
| 737 loops[loop_num].header = header; |
| 738 loops[loop_num].members = new(zone) BitVector(num_blocks, zone); |
| 739 } |
| 740 |
| 741 int queue_length = 0; |
| 742 if (member != header) { |
| 743 // As long as the header doesn't have a backedge to itself, |
| 744 // Push the member onto the queue and process its predecessors. |
| 745 if (!loops[loop_num].members->Contains(member->id())) { |
| 746 loops[loop_num].members->Add(member->id()); |
| 747 } |
| 748 queue[queue_length++].block = member; |
| 749 } |
| 750 |
| 751 // Propagate loop membership backwards. All predecessors of M up to the |
| 752 // loop header H are members of the loop too. O(|blocks between M and H|). |
| 753 while (queue_length > 0) { |
| 754 BasicBlock* block = queue[--queue_length].block; |
| 755 for (int i = 0; i < block->PredecessorCount(); i++) { |
| 756 BasicBlock* pred = block->PredecessorAt(i); |
| 757 if (pred != header) { |
| 758 if (!loops[loop_num].members->Contains(pred->id())) { |
| 759 loops[loop_num].members->Add(pred->id()); |
| 760 queue[queue_length++].block = pred; |
| 761 } |
| 762 } |
| 763 } |
| 764 } |
| 765 } |
| 766 return loops; |
| 767 } |
| 768 |
| 769 |
| 770 #if DEBUG |
| 771 static void PrintRPO( |
| 772 int num_loops, LoopInfo* loops, BasicBlockVector* order) { |
| 773 PrintF("-- RPO with %d loops ", num_loops); |
| 774 if (num_loops > 0) { |
| 775 PrintF("("); |
| 776 for (int i = 0; i < num_loops; i++) { |
| 777 if (i > 0) PrintF(" "); |
| 778 PrintF("B%d", loops[i].header->id()); |
| 779 } |
| 780 PrintF(") "); |
| 781 } |
| 782 PrintF("-- \n"); |
| 783 |
| 784 for (int i = 0; i < static_cast<int>(order->size()); i++) { |
| 785 BasicBlock* block = (*order)[i]; |
| 786 int bid = block->id(); |
| 787 PrintF("%5d:", i); |
| 788 for (int i = 0; i < num_loops; i++) { |
| 789 bool membership = loops[i].members->Contains(bid); |
| 790 bool range = loops[i].header->LoopContains(block); |
| 791 PrintF(membership ? " |" : " "); |
| 792 PrintF(range ? "x" : " "); |
| 793 } |
| 794 PrintF(" B%d: ", bid); |
| 795 if (block->loop_end_ >= 0) { |
| 796 PrintF(" range: [%d, %d)", block->rpo_number_, block->loop_end_); |
| 797 } |
| 798 PrintF("\n"); |
| 799 } |
| 800 } |
| 801 |
| 802 |
| 803 static void VerifySpecialRPO( |
| 804 int num_loops, LoopInfo* loops, BasicBlockVector* order) { |
| 805 ASSERT(order->size() > 0); |
| 806 ASSERT((*order)[0]->id() == 0); // entry should be first. |
| 807 |
| 808 for (int i = 0; i < num_loops; i++) { |
| 809 LoopInfo* loop = &loops[i]; |
| 810 BasicBlock* header = loop->header; |
| 811 |
| 812 ASSERT(header != NULL); |
| 813 ASSERT(header->rpo_number_ >= 0); |
| 814 ASSERT(header->rpo_number_ < static_cast<int>(order->size())); |
| 815 ASSERT(header->loop_end_ >= 0); |
| 816 ASSERT(header->loop_end_ <= static_cast<int>(order->size())); |
| 817 ASSERT(header->loop_end_ > header->rpo_number_); |
| 818 |
| 819 // Verify the start ... end list relationship. |
| 820 int links = 0; |
| 821 BlockList* l = loop->start; |
| 822 ASSERT(l != NULL && l->block == header); |
| 823 bool end_found; |
| 824 while (true) { |
| 825 if (l == NULL || l == loop->end) { |
| 826 end_found = (loop->end == l); |
| 827 break; |
| 828 } |
| 829 // The list should be in same order as the final result. |
| 830 ASSERT(l->block->rpo_number_ == links + loop->header->rpo_number_); |
| 831 links++; |
| 832 l = l->next; |
| 833 ASSERT(links < static_cast<int>(2 * order->size())); // cycle? |
| 834 } |
| 835 ASSERT(links > 0); |
| 836 ASSERT(links == (header->loop_end_ - header->rpo_number_)); |
| 837 ASSERT(end_found); |
| 838 |
| 839 // Check the contiguousness of loops. |
| 840 int count = 0; |
| 841 for (int j = 0; j < static_cast<int>(order->size()); j++) { |
| 842 BasicBlock* block = order->at(j); |
| 843 ASSERT(block->rpo_number_ == j); |
| 844 if (j < header->rpo_number_ || j >= header->loop_end_) { |
| 845 ASSERT(!loop->members->Contains(block->id())); |
| 846 } else { |
| 847 if (block == header) { |
| 848 ASSERT(!loop->members->Contains(block->id())); |
| 849 } else { |
| 850 ASSERT(loop->members->Contains(block->id())); |
| 851 } |
| 852 count++; |
| 853 } |
| 854 } |
| 855 ASSERT(links == count); |
| 856 } |
| 857 } |
| 858 #endif // DEBUG |
| 859 |
| 860 |
| 861 // Compute the special reverse-post-order block ordering, which is essentially |
| 862 // a RPO of the graph where loop bodies are contiguous. Properties: |
| 863 // 1. If block A is a predecessor of B, then A appears before B in the order, |
| 864 // unless B is a loop header and A is in the loop headed at B |
| 865 // (i.e. A -> B is a backedge). |
| 866 // => If block A dominates block B, then A appears before B in the order. |
| 867 // => If block A is a loop header, A appears before all blocks in the loop |
| 868 // headed at A. |
| 869 // 2. All loops are contiguous in the order (i.e. no intervening blocks that |
| 870 // do not belong to the loop.) |
| 871 // Note a simple RPO traversal satisfies (1) but not (3). |
| 872 BasicBlockVector* Scheduler::ComputeSpecialRPO() { |
| 873 if (FLAG_trace_turbo_scheduler) { |
| 874 PrintF("------------- COMPUTING SPECIAL RPO ---------------\n"); |
| 875 } |
| 876 // RPO should not have been computed for this schedule yet. |
| 877 CHECK_EQ(kBlockUnvisited1, schedule_->entry()->rpo_number_); |
| 878 CHECK_EQ(0, schedule_->rpo_order_.size()); |
| 879 |
| 880 // Perform an iterative RPO traversal using an explicit stack, |
| 881 // recording backedges that form cycles. O(|B|). |
| 882 ZoneList<std::pair<BasicBlock*, int> > backedges(1, zone_); |
| 883 SpecialRPOStackFrame* stack = |
| 884 zone_->NewArray<SpecialRPOStackFrame>(schedule_->BasicBlockCount()); |
| 885 BasicBlock* entry = schedule_->entry(); |
| 886 BlockList* order = NULL; |
| 887 int stack_depth = Push(stack, 0, entry, kBlockUnvisited1); |
| 888 int num_loops = 0; |
| 889 |
| 890 while (stack_depth > 0) { |
| 891 int current = stack_depth - 1; |
| 892 SpecialRPOStackFrame* frame = stack + current; |
| 893 |
| 894 if (frame->index < frame->block->SuccessorCount()) { |
| 895 // Process the next successor. |
| 896 BasicBlock* succ = frame->block->SuccessorAt(frame->index++); |
| 897 if (succ->rpo_number_ == kBlockVisited1) continue; |
| 898 if (succ->rpo_number_ == kBlockOnStack) { |
| 899 // The successor is on the stack, so this is a backedge (cycle). |
| 900 backedges.Add(std::pair<BasicBlock*, int>(frame->block, |
| 901 frame->index - 1), zone_); |
| 902 if (succ->loop_end_ < 0) { |
| 903 // Assign a new loop number to the header if it doesn't have one. |
| 904 succ->loop_end_ = num_loops++; |
| 905 } |
| 906 } else { |
| 907 // Push the successor onto the stack. |
| 908 ASSERT(succ->rpo_number_ == kBlockUnvisited1); |
| 909 stack_depth = Push(stack, stack_depth, succ, kBlockUnvisited1); |
| 910 } |
| 911 } else { |
| 912 // Finished with all successors; pop the stack and add the block. |
| 913 order = order->Add(zone_, frame->block); |
| 914 frame->block->rpo_number_ = kBlockVisited1; |
| 915 stack_depth--; |
| 916 } |
| 917 } |
| 918 |
| 919 // If no loops were encountered, then the order we computed was correct. |
| 920 LoopInfo* loops = NULL; |
| 921 if (num_loops != 0) { |
| 922 // Otherwise, compute the loop information from the backedges in order |
| 923 // to perform a traversal that groups loop bodies together. |
| 924 loops = ComputeLoopInfo(zone_, stack, num_loops, |
| 925 schedule_->BasicBlockCount(), &backedges); |
| 926 |
| 927 // Initialize the "loop stack". Note the entry could be a loop header. |
| 928 LoopInfo* loop = entry->IsLoopHeader() ? &loops[entry->loop_end_] : NULL; |
| 929 order = NULL; |
| 930 |
| 931 // Perform an iterative post-order traversal, visiting loop bodies before |
| 932 // edges that lead out of loops. Visits each block once, but linking loop |
| 933 // sections together is linear in the loop size, so overall is |
| 934 // O(|B| + max(loop_depth) * max(|loop|)) |
| 935 stack_depth = Push(stack, 0, entry, kBlockUnvisited2); |
| 936 while (stack_depth > 0) { |
| 937 SpecialRPOStackFrame* frame = stack + (stack_depth - 1); |
| 938 BasicBlock* block = frame->block; |
| 939 BasicBlock* succ = NULL; |
| 940 |
| 941 if (frame->index < block->SuccessorCount()) { |
| 942 // Process the next normal successor. |
| 943 succ = block->SuccessorAt(frame->index++); |
| 944 } else if (block->IsLoopHeader()) { |
| 945 // Process additional outgoing edges from the loop header. |
| 946 if (block->rpo_number_ == kBlockOnStack) { |
| 947 // Finish the loop body the first time the header is left on the |
| 948 // stack. |
| 949 ASSERT(loop != NULL && loop->header == block); |
| 950 loop->start = order->Add(zone_, block); |
| 951 order = loop->end; |
| 952 block->rpo_number_ = kBlockVisited2; |
| 953 // Pop the loop stack and continue visiting outgoing edges within the |
| 954 // the context of the outer loop, if any. |
| 955 loop = loop->prev; |
| 956 // We leave the loop header on the stack; the rest of this iteration |
| 957 // and later iterations will go through its outgoing edges list. |
| 958 } |
| 959 |
| 960 // Use the next outgoing edge if there are any. |
| 961 int outgoing_index = frame->index - block->SuccessorCount(); |
| 962 LoopInfo* info = &loops[block->loop_end_]; |
| 963 ASSERT(loop != info); |
| 964 if (info->outgoing != NULL && outgoing_index < |
| 965 info->outgoing->length()) { |
| 966 succ = info->outgoing->at(outgoing_index); |
| 967 frame->index++; |
| 968 } |
| 969 } |
| 970 |
| 971 if (succ != NULL) { |
| 972 // Process the next successor. |
| 973 if (succ->rpo_number_ == kBlockOnStack) continue; |
| 974 if (succ->rpo_number_ == kBlockVisited2) continue; |
| 975 ASSERT(succ->rpo_number_ == kBlockUnvisited2); |
| 976 if (loop != NULL && !loop->members->Contains(succ->id())) { |
| 977 // The successor is not in the current loop or any nested loop. |
| 978 // Add it to the outgoing edges of this loop and visit it later. |
| 979 loop->AddOutgoing(zone_, succ); |
| 980 } else { |
| 981 // Push the successor onto the stack. |
| 982 stack_depth = Push(stack, stack_depth, succ, kBlockUnvisited2); |
| 983 if (succ->IsLoopHeader()) { |
| 984 // Push the inner loop onto the loop stack. |
| 985 ASSERT(succ->loop_end_ >= 0 && succ->loop_end_ < num_loops); |
| 986 LoopInfo* next = &loops[succ->loop_end_]; |
| 987 next->end = order; |
| 988 next->prev = loop; |
| 989 loop = next; |
| 990 } |
| 991 } |
| 992 } else { |
| 993 // Finished with all successors of the current block. |
| 994 if (block->IsLoopHeader()) { |
| 995 // If we are going to pop a loop header, then add its entire body. |
| 996 LoopInfo* info = &loops[block->loop_end_]; |
| 997 for (BlockList* l = info->start; true; l = l->next) { |
| 998 if (l->next == info->end) { |
| 999 l->next = order; |
| 1000 info->end = order; |
| 1001 break; |
| 1002 } |
| 1003 } |
| 1004 order = info->start; |
| 1005 } else { |
| 1006 // Pop a single node off the stack and add it to the order. |
| 1007 order = order->Add(zone_, block); |
| 1008 block->rpo_number_ = kBlockVisited2; |
| 1009 } |
| 1010 stack_depth--; |
| 1011 } |
| 1012 } |
| 1013 } |
| 1014 |
| 1015 // Construct the final order from the list. |
| 1016 BasicBlockVector* final_order = &schedule_->rpo_order_; |
| 1017 order->Serialize(final_order); |
| 1018 |
| 1019 // Compute the correct loop header for every block and set the correct loop |
| 1020 // ends. |
| 1021 LoopInfo* current_loop = NULL; |
| 1022 BasicBlock* current_header = NULL; |
| 1023 int loop_depth = 0; |
| 1024 for (BasicBlockVectorIter i = final_order->begin(); |
| 1025 i != final_order->end(); ++i) { |
| 1026 BasicBlock* current = *i; |
| 1027 current->loop_header_ = current_header; |
| 1028 if (current->IsLoopHeader()) { |
| 1029 loop_depth++; |
| 1030 current_loop = &loops[current->loop_end_]; |
| 1031 BlockList* end = current_loop->end; |
| 1032 current->loop_end_ = end == NULL ? |
| 1033 static_cast<int>(final_order->size()) : end->block->rpo_number_; |
| 1034 current_header = current_loop->header; |
| 1035 if (FLAG_trace_turbo_scheduler) { |
| 1036 PrintF("Block %d is a loop header, increment loop depth to %d\n", |
| 1037 current->id(), loop_depth); |
| 1038 } |
| 1039 } else { |
| 1040 while (current_header != NULL && |
| 1041 current->rpo_number_ >= current_header->loop_end_) { |
| 1042 ASSERT(current_header->IsLoopHeader()); |
| 1043 ASSERT(current_loop != NULL); |
| 1044 current_loop = current_loop->prev; |
| 1045 current_header = current_loop == NULL ? NULL : current_loop->header; |
| 1046 --loop_depth; |
| 1047 } |
| 1048 } |
| 1049 current->loop_depth_ = loop_depth; |
| 1050 if (FLAG_trace_turbo_scheduler) { |
| 1051 if (current->loop_header_ == NULL) { |
| 1052 PrintF("Block %d's loop header is NULL, loop depth %d\n", current->id(), |
| 1053 current->loop_depth_); |
| 1054 } else { |
| 1055 PrintF("Block %d's loop header is block %d, loop depth %d\n", |
| 1056 current->id(), |
| 1057 current->loop_header_->id(), |
| 1058 current->loop_depth_); |
| 1059 } |
| 1060 } |
| 1061 } |
| 1062 |
| 1063 #if DEBUG |
| 1064 if (FLAG_trace_turbo_scheduler) PrintRPO(num_loops, loops, final_order); |
| 1065 VerifySpecialRPO(num_loops, loops, final_order); |
| 1066 #endif |
| 1067 return final_order; |
| 1068 } |
| 1069 |
| 1070 } } } // namespace v8::internal::compiler |
OLD | NEW |