Index: src/compiler/scheduler.cc |
diff --git a/src/compiler/scheduler.cc b/src/compiler/scheduler.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ee8b2266ac7d6ed445bb9d0af0ff92af91f77b04 |
--- /dev/null |
+++ b/src/compiler/scheduler.cc |
@@ -0,0 +1,1065 @@ |
+// Copyright 2013 the V8 project authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "src/compiler/scheduler.h" |
+ |
+#include "src/compiler/graph.h" |
+#include "src/compiler/graph-inl.h" |
+#include "src/compiler/node.h" |
+#include "src/compiler/node-properties.h" |
+#include "src/compiler/node-properties-inl.h" |
+#include "src/data-flow.h" |
+ |
+namespace v8 { |
+namespace internal { |
+namespace compiler { |
+ |
+Scheduler::Scheduler(Zone* zone) |
+ : zone_(zone), |
+ graph_(NULL), |
+ schedule_(NULL), |
+ branches_(NodeVector::allocator_type(zone)), |
+ calls_(NodeVector::allocator_type(zone)), |
+ deopts_(NodeVector::allocator_type(zone)), |
+ returns_(NodeVector::allocator_type(zone)), |
+ loops_and_merges_(NodeVector::allocator_type(zone)), |
+ node_block_placement_(BasicBlockVector::allocator_type(zone)), |
+ unscheduled_uses_(IntVector::allocator_type(zone)), |
+ scheduled_nodes_(NodeVectorVector::allocator_type(zone)), |
+ schedule_root_nodes_(NodeVector::allocator_type(zone)), |
+ schedule_early_rpo_index_(IntVector::allocator_type(zone)) {} |
+ |
+ |
+Scheduler::Scheduler(Zone* zone, Graph* graph, Schedule* schedule) |
+ : zone_(zone), |
+ graph_(graph), |
+ schedule_(schedule), |
+ branches_(NodeVector::allocator_type(zone)), |
+ calls_(NodeVector::allocator_type(zone)), |
+ deopts_(NodeVector::allocator_type(zone)), |
+ returns_(NodeVector::allocator_type(zone)), |
+ loops_and_merges_(NodeVector::allocator_type(zone)), |
+ node_block_placement_(BasicBlockVector::allocator_type(zone)), |
+ unscheduled_uses_(IntVector::allocator_type(zone)), |
+ scheduled_nodes_(NodeVectorVector::allocator_type(zone)), |
+ schedule_root_nodes_(NodeVector::allocator_type(zone)), |
+ schedule_early_rpo_index_(IntVector::allocator_type(zone)) {} |
+ |
+ |
+Schedule* Scheduler::NewSchedule(Graph* graph) { |
+ graph_ = graph; |
+ schedule_ = new (zone_) Schedule(zone_); |
+ |
+ schedule_->AddNode(schedule_->end(), graph_->end()); |
+ |
+ PrepareAuxiliaryNodeData(); |
+ |
+ // Create basic blocks for each block and merge node in the graph. |
+ CreateBlocks(); |
+ |
+ // Wire the basic blocks together. |
+ WireBlocks(); |
+ |
+ PrepareAuxiliaryBlockData(); |
+ |
+ ComputeSpecialRPO(); |
+ GenerateImmediateDominatorTree(); |
+ |
+ PrepareUses(); |
+ ScheduleEarly(); |
+ ScheduleLate(); |
+ |
+ return schedule_; |
+} |
+ |
+ |
+class CreateBlockVisitor : public NullNodeVisitor { |
+ public: |
+ explicit CreateBlockVisitor(Scheduler* scheduler) : scheduler_(scheduler) {} |
+ |
+ GenericGraphVisit::Control Post(Node* node) { |
+ Schedule* schedule = scheduler_->schedule_; |
+ switch (node->opcode()) { |
+ case IrOpcode::kIfTrue: |
+ case IrOpcode::kIfFalse: |
+ case IrOpcode::kContinuation: |
+ case IrOpcode::kLazyDeoptimization: { |
+ BasicBlock* block = schedule->NewBasicBlock(); |
+ schedule->AddNode(block, node); |
+ break; |
+ } |
+ case IrOpcode::kLoop: |
+ case IrOpcode::kMerge: { |
+ BasicBlock* block = schedule->NewBasicBlock(); |
+ schedule->AddNode(block, node); |
+ scheduler_->loops_and_merges_.push_back(node); |
+ break; |
+ } |
+ case IrOpcode::kBranch: { |
+ scheduler_->branches_.push_back(node); |
+ break; |
+ } |
+ case IrOpcode::kDeoptimize: { |
+ scheduler_->deopts_.push_back(node); |
+ break; |
+ } |
+ case IrOpcode::kCall: { |
+ if (NodeProperties::CanLazilyDeoptimize(node)) { |
+ scheduler_->calls_.push_back(node); |
+ } |
+ break; |
+ } |
+ case IrOpcode::kReturn: |
+ scheduler_->returns_.push_back(node); |
+ break; |
+ default: |
+ break; |
+ } |
+ |
+ return GenericGraphVisit::CONTINUE; |
+ } |
+ |
+ private: |
+ Scheduler* scheduler_; |
+}; |
+ |
+ |
+void Scheduler::CreateBlocks() { |
+ CreateBlockVisitor create_blocks(this); |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF("---------------- CREATING BLOCKS ------------------\n"); |
+ } |
+ schedule_->AddNode(schedule_->entry(), graph_->start()); |
+ graph_->VisitNodeInputsFromEnd(&create_blocks); |
+} |
+ |
+ |
+void Scheduler::WireBlocks() { |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF("----------------- WIRING BLOCKS -------------------\n"); |
+ } |
+ AddSuccessorsForBranches(); |
+ AddSuccessorsForReturns(); |
+ AddSuccessorsForCalls(); |
+ AddSuccessorsForDeopts(); |
+ AddPredecessorsForLoopsAndMerges(); |
+ // TODO(danno): Handle Throw, et al. |
+} |
+ |
+ |
+void Scheduler::PrepareAuxiliaryNodeData() { |
+ unscheduled_uses_.resize(graph_->NodeCount(), 0); |
+ schedule_early_rpo_index_.resize(graph_->NodeCount(), 0); |
+} |
+ |
+ |
+void Scheduler::PrepareAuxiliaryBlockData() { |
+ Zone* zone = schedule_->zone(); |
+ scheduled_nodes_.resize(schedule_->BasicBlockCount(), |
+ NodeVector(NodeVector::allocator_type(zone))); |
+ schedule_->immediate_dominator_.resize(schedule_->BasicBlockCount(), NULL); |
+} |
+ |
+ |
+void Scheduler::AddPredecessorsForLoopsAndMerges() { |
+ for (NodeVectorIter i = loops_and_merges_.begin(); |
+ i != loops_and_merges_.end(); ++i) { |
+ Node* merge_or_loop = *i; |
+ BasicBlock* block = schedule_->block(merge_or_loop); |
+ ASSERT(block != NULL); |
+ // For all of the merge's control inputs, add a goto at the end to the |
+ // merge's basic block. |
+ for (InputIter j = (*i)->inputs().begin(); j != (*i)->inputs().end(); ++j) { |
+ if (NodeProperties::IsBasicBlockBegin(*i)) { |
+ BasicBlock* predecessor_block = schedule_->block(*j); |
+ if ((*j)->opcode() != IrOpcode::kReturn && |
+ (*j)->opcode() != IrOpcode::kDeoptimize) { |
+ ASSERT(predecessor_block != NULL); |
+ if (FLAG_trace_turbo_scheduler) { |
+ IrOpcode::Value opcode = (*i)->opcode(); |
+ PrintF("node %d (%s) in block %d -> block %d\n", (*i)->id(), |
+ IrOpcode::Mnemonic(opcode), predecessor_block->id(), |
+ block->id()); |
+ } |
+ schedule_->AddGoto(predecessor_block, block); |
+ } |
+ } |
+ } |
+ } |
+} |
+ |
+ |
+void Scheduler::AddSuccessorsForCalls() { |
+ for (NodeVectorIter i = calls_.begin(); i != calls_.end(); ++i) { |
+ Node* call = *i; |
+ ASSERT(call->opcode() == IrOpcode::kCall); |
+ ASSERT(NodeProperties::CanLazilyDeoptimize(call)); |
+ |
+ Node* lazy_deopt_node = NULL; |
+ Node* cont_node = NULL; |
+ // Find the continuation and lazy-deopt nodes among the uses. |
+ for (UseIter use_iter = call->uses().begin(); |
+ use_iter != call->uses().end(); ++use_iter) { |
+ switch ((*use_iter)->opcode()) { |
+ case IrOpcode::kContinuation: { |
+ ASSERT(cont_node == NULL); |
+ cont_node = *use_iter; |
+ break; |
+ } |
+ case IrOpcode::kLazyDeoptimization: { |
+ ASSERT(lazy_deopt_node == NULL); |
+ lazy_deopt_node = *use_iter; |
+ break; |
+ } |
+ default: |
+ break; |
+ } |
+ } |
+ ASSERT(lazy_deopt_node != NULL); |
+ ASSERT(cont_node != NULL); |
+ BasicBlock* cont_successor_block = schedule_->block(cont_node); |
+ BasicBlock* deopt_successor_block = schedule_->block(lazy_deopt_node); |
+ Node* call_block_node = NodeProperties::GetControlInput(call); |
+ BasicBlock* call_block = schedule_->block(call_block_node); |
+ if (FLAG_trace_turbo_scheduler) { |
+ IrOpcode::Value opcode = call->opcode(); |
+ PrintF("node %d (%s) in block %d -> block %d\n", call->id(), |
+ IrOpcode::Mnemonic(opcode), call_block->id(), |
+ cont_successor_block->id()); |
+ PrintF("node %d (%s) in block %d -> block %d\n", call->id(), |
+ IrOpcode::Mnemonic(opcode), call_block->id(), |
+ deopt_successor_block->id()); |
+ } |
+ schedule_->AddCall(call_block, call, cont_successor_block, |
+ deopt_successor_block); |
+ } |
+} |
+ |
+ |
+void Scheduler::AddSuccessorsForDeopts() { |
+ for (NodeVectorIter i = deopts_.begin(); i != deopts_.end(); ++i) { |
+ Node* deopt_block_node = NodeProperties::GetControlInput(*i); |
+ BasicBlock* deopt_block = schedule_->block(deopt_block_node); |
+ ASSERT(deopt_block != NULL); |
+ if (FLAG_trace_turbo_scheduler) { |
+ IrOpcode::Value opcode = (*i)->opcode(); |
+ PrintF("node %d (%s) in block %d -> end\n", (*i)->id(), |
+ IrOpcode::Mnemonic(opcode), deopt_block->id()); |
+ } |
+ schedule_->AddDeoptimize(deopt_block, *i); |
+ } |
+} |
+ |
+ |
+void Scheduler::AddSuccessorsForBranches() { |
+ for (NodeVectorIter i = branches_.begin(); i != branches_.end(); ++i) { |
+ Node* branch = *i; |
+ ASSERT(branch->opcode() == IrOpcode::kBranch); |
+ Node* branch_block_node = NodeProperties::GetControlInput(branch); |
+ BasicBlock* branch_block = schedule_->block(branch_block_node); |
+ ASSERT(branch_block != NULL); |
+ UseIter use_iter = branch->uses().begin(); |
+ Node* first_successor = *use_iter; |
+ ++use_iter; |
+ ASSERT(use_iter != branch->uses().end()); |
+ Node* second_successor = *use_iter; |
+ ASSERT(++use_iter == branch->uses().end()); |
+ Node* true_successor_node = first_successor->opcode() == IrOpcode::kIfTrue |
+ ? first_successor |
+ : second_successor; |
+ Node* false_successor_node = first_successor->opcode() == IrOpcode::kIfTrue |
+ ? second_successor |
+ : first_successor; |
+ ASSERT(true_successor_node->opcode() == IrOpcode::kIfTrue); |
+ ASSERT(false_successor_node->opcode() == IrOpcode::kIfFalse); |
+ BasicBlock* true_successor_block = schedule_->block(true_successor_node); |
+ BasicBlock* false_successor_block = schedule_->block(false_successor_node); |
+ ASSERT(true_successor_block != NULL); |
+ ASSERT(false_successor_block != NULL); |
+ if (FLAG_trace_turbo_scheduler) { |
+ IrOpcode::Value opcode = branch->opcode(); |
+ PrintF("node %d (%s) in block %d -> block %d\n", branch->id(), |
+ IrOpcode::Mnemonic(opcode), branch_block->id(), |
+ true_successor_block->id()); |
+ PrintF("node %d (%s) in block %d -> block %d\n", branch->id(), |
+ IrOpcode::Mnemonic(opcode), branch_block->id(), |
+ false_successor_block->id()); |
+ } |
+ schedule_->AddBranch(branch_block, branch, true_successor_block, |
+ false_successor_block); |
+ } |
+} |
+ |
+ |
+void Scheduler::AddSuccessorsForReturns() { |
+ for (NodeVectorIter i = returns_.begin(); i != returns_.end(); ++i) { |
+ Node* return_block_node = NodeProperties::GetControlInput(*i); |
+ BasicBlock* return_block = schedule_->block(return_block_node); |
+ ASSERT(return_block != NULL); |
+ if (FLAG_trace_turbo_scheduler) { |
+ IrOpcode::Value opcode = (*i)->opcode(); |
+ PrintF("node %d (%s) in block %d -> end\n", (*i)->id(), |
+ IrOpcode::Mnemonic(opcode), return_block->id()); |
+ } |
+ schedule_->AddReturn(return_block, *i); |
+ } |
+} |
+ |
+ |
+BasicBlock* Scheduler::GetCommonDominator(BasicBlock* b1, BasicBlock* b2) { |
+ while (b1 != b2) { |
+ int b1_rpo = GetRPONumber(b1); |
+ int b2_rpo = GetRPONumber(b2); |
+ ASSERT(b1_rpo != b2_rpo); |
+ if (b1_rpo < b2_rpo) { |
+ b2 = schedule_->immediate_dominator_[b2->id()]; |
+ } else { |
+ b1 = schedule_->immediate_dominator_[b1->id()]; |
+ } |
+ } |
+ return b1; |
+} |
+ |
+ |
+void Scheduler::GenerateImmediateDominatorTree() { |
+ // Build the dominator graph. TODO(danno): consider using Lengauer & Tarjan's |
+ // if this becomes really slow. |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF("------------ IMMEDIATE BLOCK DOMINATORS -----------\n"); |
+ } |
+ for (size_t i = 0; i < schedule_->rpo_order_.size(); i++) { |
+ BasicBlock* current_rpo = schedule_->rpo_order_[i]; |
+ if (current_rpo != schedule_->entry()) { |
+ BasicBlock::Predecessors::iterator current_pred = |
+ current_rpo->predecessors().begin(); |
+ BasicBlock::Predecessors::iterator end = |
+ current_rpo->predecessors().end(); |
+ ASSERT(current_pred != end); |
+ BasicBlock* dominator = *current_pred; |
+ ++current_pred; |
+ // For multiple predecessors, walk up the rpo ordering until a common |
+ // dominator is found. |
+ int current_rpo_pos = GetRPONumber(current_rpo); |
+ while (current_pred != end) { |
+ // Don't examine backwards edges |
+ BasicBlock* pred = *current_pred; |
+ if (GetRPONumber(pred) < current_rpo_pos) { |
+ dominator = GetCommonDominator(dominator, *current_pred); |
+ } |
+ ++current_pred; |
+ } |
+ schedule_->immediate_dominator_[current_rpo->id()] = dominator; |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF("Block %d's idom is %d\n", current_rpo->id(), dominator->id()); |
+ } |
+ } |
+ } |
+} |
+ |
+ |
+class ScheduleEarlyNodeVisitor : public NullNodeVisitor { |
+ public: |
+ explicit ScheduleEarlyNodeVisitor(Scheduler* scheduler) |
+ : has_changed_rpo_constraints_(true), |
+ scheduler_(scheduler), |
+ schedule_(scheduler->schedule_) {} |
+ |
+ GenericGraphVisit::Control Pre(Node* node) { |
+ int id = node->id(); |
+ int max_rpo = 0; |
+ // Fixed nodes already know their schedule early position. |
+ if (IsFixedNode(node)) { |
+ BasicBlock* block = schedule_->block(node); |
+ ASSERT(block != NULL); |
+ max_rpo = block->rpo_number_; |
+ if (scheduler_->schedule_early_rpo_index_[id] != max_rpo) { |
+ has_changed_rpo_constraints_ = true; |
+ } |
+ scheduler_->schedule_early_rpo_index_[id] = max_rpo; |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF("Node %d pre-scheduled early at rpo limit %d\n", id, max_rpo); |
+ } |
+ } |
+ return GenericGraphVisit::CONTINUE; |
+ } |
+ |
+ GenericGraphVisit::Control Post(Node* node) { |
+ int id = node->id(); |
+ int max_rpo = 0; |
+ // Otherwise, the minimum rpo for the node is the max of all of the inputs. |
+ if (!IsFixedNode(node)) { |
+ ASSERT(!NodeProperties::IsBasicBlockBegin(node)); |
+ for (InputIter i = node->inputs().begin(); i != node->inputs().end(); |
+ ++i) { |
+ int control_rpo = scheduler_->schedule_early_rpo_index_[(*i)->id()]; |
+ if (control_rpo > max_rpo) { |
+ max_rpo = control_rpo; |
+ } |
+ } |
+ if (scheduler_->schedule_early_rpo_index_[id] != max_rpo) { |
+ has_changed_rpo_constraints_ = true; |
+ } |
+ scheduler_->schedule_early_rpo_index_[id] = max_rpo; |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF("Node %d post-scheduled early at rpo limit %d\n", id, max_rpo); |
+ } |
+ } |
+ return GenericGraphVisit::CONTINUE; |
+ } |
+ |
+ static bool IsFixedNode(Node* node) { |
+ return NodeProperties::HasFixedSchedulePosition(node) || |
+ !NodeProperties::CanBeScheduled(node); |
+ } |
+ |
+ // TODO(mstarzinger): Dirty hack to unblock others, schedule early should be |
+ // rewritten to use a pre-order traversal from the start instead. |
+ bool has_changed_rpo_constraints_; |
+ |
+ private: |
+ Scheduler* scheduler_; |
+ Schedule* schedule_; |
+}; |
+ |
+ |
+void Scheduler::ScheduleEarly() { |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF("------------------- SCHEDULE EARLY ----------------\n"); |
+ } |
+ |
+ int fixpoint_count = 0; |
+ ScheduleEarlyNodeVisitor visitor(this); |
+ while (visitor.has_changed_rpo_constraints_) { |
+ visitor.has_changed_rpo_constraints_ = false; |
+ graph_->VisitNodeInputsFromEnd(&visitor); |
+ fixpoint_count++; |
+ } |
+ |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF("It took %d iterations to determine fixpoint\n", fixpoint_count); |
+ } |
+} |
+ |
+ |
+class PrepareUsesVisitor : public NullNodeVisitor { |
+ public: |
+ explicit PrepareUsesVisitor(Scheduler* scheduler) |
+ : scheduler_(scheduler), schedule_(scheduler->schedule_) {} |
+ |
+ GenericGraphVisit::Control Pre(Node* node) { |
+ // Some nodes must be scheduled explicitly to ensure they are in exactly the |
+ // right place; it's a convenient place during the preparation of use counts |
+ // to schedule them. |
+ if (!schedule_->IsScheduled(node) && |
+ NodeProperties::HasFixedSchedulePosition(node)) { |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF("Fixed position node %d is unscheduled, scheduling now\n", |
+ node->id()); |
+ } |
+ IrOpcode::Value opcode = node->opcode(); |
+ BasicBlock* block = |
+ opcode == IrOpcode::kParameter |
+ ? schedule_->entry() |
+ : schedule_->block(NodeProperties::GetControlInput(node)); |
+ ASSERT(block != NULL); |
+ schedule_->AddNode(block, node); |
+ } |
+ |
+ if (NodeProperties::IsScheduleRoot(node)) { |
+ scheduler_->schedule_root_nodes_.push_back(node); |
+ } |
+ |
+ return GenericGraphVisit::CONTINUE; |
+ } |
+ |
+ void PostEdge(Node* from, int index, Node* to) { |
+ // If the edge is from an unscheduled node, then tally it in the use count |
+ // for all of its inputs. The same criterion will be used in ScheduleLate |
+ // for decrementing use counts. |
+ if (!schedule_->IsScheduled(from) && NodeProperties::CanBeScheduled(from)) { |
+ ASSERT(!NodeProperties::HasFixedSchedulePosition(from)); |
+ ++scheduler_->unscheduled_uses_[to->id()]; |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF("Incrementing uses of node %d from %d to %d\n", to->id(), |
+ from->id(), scheduler_->unscheduled_uses_[to->id()]); |
+ } |
+ } |
+ } |
+ |
+ private: |
+ Scheduler* scheduler_; |
+ Schedule* schedule_; |
+}; |
+ |
+ |
+void Scheduler::PrepareUses() { |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF("------------------- PREPARE USES ------------------\n"); |
+ } |
+ // Count the uses of every node, it will be used to ensure that all of a |
+ // node's uses are scheduled before the node itself. |
+ PrepareUsesVisitor prepare_uses(this); |
+ graph_->VisitNodeInputsFromEnd(&prepare_uses); |
+} |
+ |
+ |
+class ScheduleLateNodeVisitor : public NullNodeVisitor { |
+ public: |
+ explicit ScheduleLateNodeVisitor(Scheduler* scheduler) |
+ : scheduler_(scheduler), schedule_(scheduler_->schedule_) {} |
+ |
+ GenericGraphVisit::Control Pre(Node* node) { |
+ // Don't schedule nodes that cannot be scheduled or are already scheduled. |
+ if (!NodeProperties::CanBeScheduled(node) || schedule_->IsScheduled(node)) { |
+ return GenericGraphVisit::CONTINUE; |
+ } |
+ ASSERT(!NodeProperties::HasFixedSchedulePosition(node)); |
+ |
+ // If all the uses of a node have been scheduled, then the node itself can |
+ // be scheduled. |
+ bool eligible = scheduler_->unscheduled_uses_[node->id()] == 0; |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF("Testing for schedule eligibility for node %d -> %s\n", node->id(), |
+ eligible ? "true" : "false"); |
+ } |
+ if (!eligible) return GenericGraphVisit::DEFER; |
+ |
+ // Determine the dominating block for all of the uses of this node. It is |
+ // the latest block that this node can be scheduled in. |
+ BasicBlock* block = NULL; |
+ for (Node::Uses::iterator i = node->uses().begin(); i != node->uses().end(); |
+ ++i) { |
+ BasicBlock* use_block = GetBlockForUse(i.edge()); |
+ block = block == NULL ? use_block : use_block == NULL |
+ ? block |
+ : scheduler_->GetCommonDominator( |
+ block, use_block); |
+ } |
+ ASSERT(block != NULL); |
+ |
+ int min_rpo = scheduler_->schedule_early_rpo_index_[node->id()]; |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF( |
+ "Schedule late conservative for node %d is block %d at " |
+ "loop depth %d, min rpo = %d\n", |
+ node->id(), block->id(), block->loop_depth_, min_rpo); |
+ } |
+ // Hoist nodes out of loops if possible. Nodes can be hoisted iteratively |
+ // into enlcosing loop pre-headers until they would preceed their |
+ // ScheduleEarly position. |
+ BasicBlock* hoist_block = block; |
+ while (hoist_block != NULL && hoist_block->rpo_number_ >= min_rpo) { |
+ if (hoist_block->loop_depth_ < block->loop_depth_) { |
+ block = hoist_block; |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF("Hoisting node %d to block %d\n", node->id(), block->id()); |
+ } |
+ } |
+ // Try to hoist to the pre-header of the loop header. |
+ hoist_block = hoist_block->loop_header(); |
+ if (hoist_block != NULL) { |
+ BasicBlock* pre_header = schedule_->dominator(hoist_block); |
+ ASSERT(pre_header == NULL || |
+ *hoist_block->predecessors().begin() == pre_header); |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF( |
+ "Try hoist to pre-header block %d of loop header block %d," |
+ " depth would be %d\n", |
+ pre_header->id(), hoist_block->id(), pre_header->loop_depth_); |
+ } |
+ hoist_block = pre_header; |
+ } |
+ } |
+ |
+ ScheduleNode(block, node); |
+ |
+ return GenericGraphVisit::CONTINUE; |
+ } |
+ |
+ private: |
+ BasicBlock* GetBlockForUse(Node::Edge edge) { |
+ Node* use = edge.from(); |
+ IrOpcode::Value opcode = use->opcode(); |
+ // If the use is a phi, forward through the the phi to the basic block |
+ // corresponding to the phi's input. |
+ if (opcode == IrOpcode::kPhi || opcode == IrOpcode::kEffectPhi) { |
+ int index = edge.index(); |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF("Use %d is input %d to a phi\n", use->id(), index); |
+ } |
+ use = NodeProperties::GetControlInput(use, 0); |
+ opcode = use->opcode(); |
+ ASSERT(opcode == IrOpcode::kMerge || opcode == IrOpcode::kLoop); |
+ use = NodeProperties::GetControlInput(use, index); |
+ } |
+ BasicBlock* result = schedule_->block(use); |
+ if (result == NULL) return NULL; |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF("Must dominate use %d in block %d\n", use->id(), result->id()); |
+ } |
+ return result; |
+ } |
+ |
+ bool IsNodeEligible(Node* node) { |
+ bool eligible = scheduler_->unscheduled_uses_[node->id()] == 0; |
+ return eligible; |
+ } |
+ |
+ void ScheduleNode(BasicBlock* block, Node* node) { |
+ schedule_->PlanNode(block, node); |
+ scheduler_->scheduled_nodes_[block->id()].push_back(node); |
+ |
+ // Reduce the use count of the node's inputs to potentially make them |
+ // scheduable. |
+ for (InputIter i = node->inputs().begin(); i != node->inputs().end(); ++i) { |
+ ASSERT(scheduler_->unscheduled_uses_[(*i)->id()] > 0); |
+ --scheduler_->unscheduled_uses_[(*i)->id()]; |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF("Decrementing use count for node %d from node %d (now %d)\n", |
+ (*i)->id(), i.edge().from()->id(), |
+ scheduler_->unscheduled_uses_[(*i)->id()]); |
+ if (scheduler_->unscheduled_uses_[(*i)->id()] == 0) { |
+ PrintF("node %d is now eligible for scheduling\n", (*i)->id()); |
+ } |
+ } |
+ } |
+ } |
+ |
+ Scheduler* scheduler_; |
+ Schedule* schedule_; |
+}; |
+ |
+ |
+void Scheduler::ScheduleLate() { |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF("------------------- SCHEDULE LATE -----------------\n"); |
+ } |
+ |
+ // Schedule: Places nodes in dominator block of all their uses. |
+ ScheduleLateNodeVisitor schedule_late_visitor(this); |
+ |
+ for (NodeVectorIter i = schedule_root_nodes_.begin(); |
+ i != schedule_root_nodes_.end(); ++i) { |
+ GenericGraphVisit::Visit<ScheduleLateNodeVisitor, |
+ NodeInputIterationTraits<Node> >( |
+ graph_, *i, &schedule_late_visitor); |
+ } |
+ |
+ // Add collected nodes for basic blocks to their blocks in the right order. |
+ int block_num = 0; |
+ for (NodeVectorVectorIter i = scheduled_nodes_.begin(); |
+ i != scheduled_nodes_.end(); ++i) { |
+ for (NodeVectorRIter j = i->rbegin(); j != i->rend(); ++j) { |
+ schedule_->AddNode(schedule_->all_blocks_.at(block_num), *j); |
+ } |
+ block_num++; |
+ } |
+} |
+ |
+ |
+// Numbering for BasicBlockData.rpo_number_ for this block traversal: |
+static const int kBlockOnStack = -2; |
+static const int kBlockVisited1 = -3; |
+static const int kBlockVisited2 = -4; |
+static const int kBlockUnvisited1 = -1; |
+static const int kBlockUnvisited2 = kBlockVisited1; |
+ |
+struct SpecialRPOStackFrame { |
+ BasicBlock* block; |
+ int index; |
+}; |
+ |
+struct BlockList { |
+ BasicBlock* block; |
+ BlockList* next; |
+ |
+ BlockList* Add(Zone* zone, BasicBlock* b) { |
+ BlockList* list = static_cast<BlockList*>(zone->New(sizeof(BlockList))); |
+ list->block = b; |
+ list->next = this; |
+ return list; |
+ } |
+ |
+ void Serialize(BasicBlockVector* final_order) { |
+ for (BlockList* l = this; l != NULL; l = l->next) { |
+ l->block->rpo_number_ = static_cast<int>(final_order->size()); |
+ final_order->push_back(l->block); |
+ } |
+ } |
+}; |
+ |
+struct LoopInfo { |
+ BasicBlock* header; |
+ ZoneList<BasicBlock*>* outgoing; |
+ BitVector* members; |
+ LoopInfo* prev; |
+ BlockList* end; |
+ BlockList* start; |
+ |
+ void AddOutgoing(Zone* zone, BasicBlock* block) { |
+ if (outgoing == NULL) outgoing = new (zone) ZoneList<BasicBlock*>(2, zone); |
+ outgoing->Add(block, zone); |
+ } |
+}; |
+ |
+ |
+static int Push(SpecialRPOStackFrame* stack, int depth, BasicBlock* child, |
+ int unvisited) { |
+ if (child->rpo_number_ == unvisited) { |
+ stack[depth].block = child; |
+ stack[depth].index = 0; |
+ child->rpo_number_ = kBlockOnStack; |
+ return depth + 1; |
+ } |
+ return depth; |
+} |
+ |
+ |
+// Computes loop membership from the backedges of the control flow graph. |
+static LoopInfo* ComputeLoopInfo( |
+ Zone* zone, SpecialRPOStackFrame* queue, int num_loops, int num_blocks, |
+ ZoneList<std::pair<BasicBlock*, int> >* backedges) { |
+ LoopInfo* loops = zone->NewArray<LoopInfo>(num_loops); |
+ memset(loops, 0, num_loops * sizeof(LoopInfo)); |
+ |
+ // Compute loop membership starting from backedges. |
+ // O(max(loop_depth) * max(|loop|) |
+ for (int i = 0; i < backedges->length(); i++) { |
+ BasicBlock* member = backedges->at(i).first; |
+ BasicBlock* header = member->SuccessorAt(backedges->at(i).second); |
+ int loop_num = header->loop_end_; |
+ if (loops[loop_num].header == NULL) { |
+ loops[loop_num].header = header; |
+ loops[loop_num].members = new (zone) BitVector(num_blocks, zone); |
+ } |
+ |
+ int queue_length = 0; |
+ if (member != header) { |
+ // As long as the header doesn't have a backedge to itself, |
+ // Push the member onto the queue and process its predecessors. |
+ if (!loops[loop_num].members->Contains(member->id())) { |
+ loops[loop_num].members->Add(member->id()); |
+ } |
+ queue[queue_length++].block = member; |
+ } |
+ |
+ // Propagate loop membership backwards. All predecessors of M up to the |
+ // loop header H are members of the loop too. O(|blocks between M and H|). |
+ while (queue_length > 0) { |
+ BasicBlock* block = queue[--queue_length].block; |
+ for (int i = 0; i < block->PredecessorCount(); i++) { |
+ BasicBlock* pred = block->PredecessorAt(i); |
+ if (pred != header) { |
+ if (!loops[loop_num].members->Contains(pred->id())) { |
+ loops[loop_num].members->Add(pred->id()); |
+ queue[queue_length++].block = pred; |
+ } |
+ } |
+ } |
+ } |
+ } |
+ return loops; |
+} |
+ |
+ |
+#if DEBUG |
+static void PrintRPO(int num_loops, LoopInfo* loops, BasicBlockVector* order) { |
+ PrintF("-- RPO with %d loops ", num_loops); |
+ if (num_loops > 0) { |
+ PrintF("("); |
+ for (int i = 0; i < num_loops; i++) { |
+ if (i > 0) PrintF(" "); |
+ PrintF("B%d", loops[i].header->id()); |
+ } |
+ PrintF(") "); |
+ } |
+ PrintF("-- \n"); |
+ |
+ for (int i = 0; i < static_cast<int>(order->size()); i++) { |
+ BasicBlock* block = (*order)[i]; |
+ int bid = block->id(); |
+ PrintF("%5d:", i); |
+ for (int i = 0; i < num_loops; i++) { |
+ bool membership = loops[i].members->Contains(bid); |
+ bool range = loops[i].header->LoopContains(block); |
+ PrintF(membership ? " |" : " "); |
+ PrintF(range ? "x" : " "); |
+ } |
+ PrintF(" B%d: ", bid); |
+ if (block->loop_end_ >= 0) { |
+ PrintF(" range: [%d, %d)", block->rpo_number_, block->loop_end_); |
+ } |
+ PrintF("\n"); |
+ } |
+} |
+ |
+ |
+static void VerifySpecialRPO(int num_loops, LoopInfo* loops, |
+ BasicBlockVector* order) { |
+ ASSERT(order->size() > 0); |
+ ASSERT((*order)[0]->id() == 0); // entry should be first. |
+ |
+ for (int i = 0; i < num_loops; i++) { |
+ LoopInfo* loop = &loops[i]; |
+ BasicBlock* header = loop->header; |
+ |
+ ASSERT(header != NULL); |
+ ASSERT(header->rpo_number_ >= 0); |
+ ASSERT(header->rpo_number_ < static_cast<int>(order->size())); |
+ ASSERT(header->loop_end_ >= 0); |
+ ASSERT(header->loop_end_ <= static_cast<int>(order->size())); |
+ ASSERT(header->loop_end_ > header->rpo_number_); |
+ |
+ // Verify the start ... end list relationship. |
+ int links = 0; |
+ BlockList* l = loop->start; |
+ ASSERT(l != NULL && l->block == header); |
+ bool end_found; |
+ while (true) { |
+ if (l == NULL || l == loop->end) { |
+ end_found = (loop->end == l); |
+ break; |
+ } |
+ // The list should be in same order as the final result. |
+ ASSERT(l->block->rpo_number_ == links + loop->header->rpo_number_); |
+ links++; |
+ l = l->next; |
+ ASSERT(links < static_cast<int>(2 * order->size())); // cycle? |
+ } |
+ ASSERT(links > 0); |
+ ASSERT(links == (header->loop_end_ - header->rpo_number_)); |
+ ASSERT(end_found); |
+ |
+ // Check the contiguousness of loops. |
+ int count = 0; |
+ for (int j = 0; j < static_cast<int>(order->size()); j++) { |
+ BasicBlock* block = order->at(j); |
+ ASSERT(block->rpo_number_ == j); |
+ if (j < header->rpo_number_ || j >= header->loop_end_) { |
+ ASSERT(!loop->members->Contains(block->id())); |
+ } else { |
+ if (block == header) { |
+ ASSERT(!loop->members->Contains(block->id())); |
+ } else { |
+ ASSERT(loop->members->Contains(block->id())); |
+ } |
+ count++; |
+ } |
+ } |
+ ASSERT(links == count); |
+ } |
+} |
+#endif // DEBUG |
+ |
+ |
+// Compute the special reverse-post-order block ordering, which is essentially |
+// a RPO of the graph where loop bodies are contiguous. Properties: |
+// 1. If block A is a predecessor of B, then A appears before B in the order, |
+// unless B is a loop header and A is in the loop headed at B |
+// (i.e. A -> B is a backedge). |
+// => If block A dominates block B, then A appears before B in the order. |
+// => If block A is a loop header, A appears before all blocks in the loop |
+// headed at A. |
+// 2. All loops are contiguous in the order (i.e. no intervening blocks that |
+// do not belong to the loop.) |
+// Note a simple RPO traversal satisfies (1) but not (3). |
+BasicBlockVector* Scheduler::ComputeSpecialRPO() { |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF("------------- COMPUTING SPECIAL RPO ---------------\n"); |
+ } |
+ // RPO should not have been computed for this schedule yet. |
+ CHECK_EQ(kBlockUnvisited1, schedule_->entry()->rpo_number_); |
+ CHECK_EQ(0, schedule_->rpo_order_.size()); |
+ |
+ // Perform an iterative RPO traversal using an explicit stack, |
+ // recording backedges that form cycles. O(|B|). |
+ ZoneList<std::pair<BasicBlock*, int> > backedges(1, zone_); |
+ SpecialRPOStackFrame* stack = |
+ zone_->NewArray<SpecialRPOStackFrame>(schedule_->BasicBlockCount()); |
+ BasicBlock* entry = schedule_->entry(); |
+ BlockList* order = NULL; |
+ int stack_depth = Push(stack, 0, entry, kBlockUnvisited1); |
+ int num_loops = 0; |
+ |
+ while (stack_depth > 0) { |
+ int current = stack_depth - 1; |
+ SpecialRPOStackFrame* frame = stack + current; |
+ |
+ if (frame->index < frame->block->SuccessorCount()) { |
+ // Process the next successor. |
+ BasicBlock* succ = frame->block->SuccessorAt(frame->index++); |
+ if (succ->rpo_number_ == kBlockVisited1) continue; |
+ if (succ->rpo_number_ == kBlockOnStack) { |
+ // The successor is on the stack, so this is a backedge (cycle). |
+ backedges.Add( |
+ std::pair<BasicBlock*, int>(frame->block, frame->index - 1), zone_); |
+ if (succ->loop_end_ < 0) { |
+ // Assign a new loop number to the header if it doesn't have one. |
+ succ->loop_end_ = num_loops++; |
+ } |
+ } else { |
+ // Push the successor onto the stack. |
+ ASSERT(succ->rpo_number_ == kBlockUnvisited1); |
+ stack_depth = Push(stack, stack_depth, succ, kBlockUnvisited1); |
+ } |
+ } else { |
+ // Finished with all successors; pop the stack and add the block. |
+ order = order->Add(zone_, frame->block); |
+ frame->block->rpo_number_ = kBlockVisited1; |
+ stack_depth--; |
+ } |
+ } |
+ |
+ // If no loops were encountered, then the order we computed was correct. |
+ LoopInfo* loops = NULL; |
+ if (num_loops != 0) { |
+ // Otherwise, compute the loop information from the backedges in order |
+ // to perform a traversal that groups loop bodies together. |
+ loops = ComputeLoopInfo(zone_, stack, num_loops, |
+ schedule_->BasicBlockCount(), &backedges); |
+ |
+ // Initialize the "loop stack". Note the entry could be a loop header. |
+ LoopInfo* loop = entry->IsLoopHeader() ? &loops[entry->loop_end_] : NULL; |
+ order = NULL; |
+ |
+ // Perform an iterative post-order traversal, visiting loop bodies before |
+ // edges that lead out of loops. Visits each block once, but linking loop |
+ // sections together is linear in the loop size, so overall is |
+ // O(|B| + max(loop_depth) * max(|loop|)) |
+ stack_depth = Push(stack, 0, entry, kBlockUnvisited2); |
+ while (stack_depth > 0) { |
+ SpecialRPOStackFrame* frame = stack + (stack_depth - 1); |
+ BasicBlock* block = frame->block; |
+ BasicBlock* succ = NULL; |
+ |
+ if (frame->index < block->SuccessorCount()) { |
+ // Process the next normal successor. |
+ succ = block->SuccessorAt(frame->index++); |
+ } else if (block->IsLoopHeader()) { |
+ // Process additional outgoing edges from the loop header. |
+ if (block->rpo_number_ == kBlockOnStack) { |
+ // Finish the loop body the first time the header is left on the |
+ // stack. |
+ ASSERT(loop != NULL && loop->header == block); |
+ loop->start = order->Add(zone_, block); |
+ order = loop->end; |
+ block->rpo_number_ = kBlockVisited2; |
+ // Pop the loop stack and continue visiting outgoing edges within the |
+ // the context of the outer loop, if any. |
+ loop = loop->prev; |
+ // We leave the loop header on the stack; the rest of this iteration |
+ // and later iterations will go through its outgoing edges list. |
+ } |
+ |
+ // Use the next outgoing edge if there are any. |
+ int outgoing_index = frame->index - block->SuccessorCount(); |
+ LoopInfo* info = &loops[block->loop_end_]; |
+ ASSERT(loop != info); |
+ if (info->outgoing != NULL && |
+ outgoing_index < info->outgoing->length()) { |
+ succ = info->outgoing->at(outgoing_index); |
+ frame->index++; |
+ } |
+ } |
+ |
+ if (succ != NULL) { |
+ // Process the next successor. |
+ if (succ->rpo_number_ == kBlockOnStack) continue; |
+ if (succ->rpo_number_ == kBlockVisited2) continue; |
+ ASSERT(succ->rpo_number_ == kBlockUnvisited2); |
+ if (loop != NULL && !loop->members->Contains(succ->id())) { |
+ // The successor is not in the current loop or any nested loop. |
+ // Add it to the outgoing edges of this loop and visit it later. |
+ loop->AddOutgoing(zone_, succ); |
+ } else { |
+ // Push the successor onto the stack. |
+ stack_depth = Push(stack, stack_depth, succ, kBlockUnvisited2); |
+ if (succ->IsLoopHeader()) { |
+ // Push the inner loop onto the loop stack. |
+ ASSERT(succ->loop_end_ >= 0 && succ->loop_end_ < num_loops); |
+ LoopInfo* next = &loops[succ->loop_end_]; |
+ next->end = order; |
+ next->prev = loop; |
+ loop = next; |
+ } |
+ } |
+ } else { |
+ // Finished with all successors of the current block. |
+ if (block->IsLoopHeader()) { |
+ // If we are going to pop a loop header, then add its entire body. |
+ LoopInfo* info = &loops[block->loop_end_]; |
+ for (BlockList* l = info->start; true; l = l->next) { |
+ if (l->next == info->end) { |
+ l->next = order; |
+ info->end = order; |
+ break; |
+ } |
+ } |
+ order = info->start; |
+ } else { |
+ // Pop a single node off the stack and add it to the order. |
+ order = order->Add(zone_, block); |
+ block->rpo_number_ = kBlockVisited2; |
+ } |
+ stack_depth--; |
+ } |
+ } |
+ } |
+ |
+ // Construct the final order from the list. |
+ BasicBlockVector* final_order = &schedule_->rpo_order_; |
+ order->Serialize(final_order); |
+ |
+ // Compute the correct loop header for every block and set the correct loop |
+ // ends. |
+ LoopInfo* current_loop = NULL; |
+ BasicBlock* current_header = NULL; |
+ int loop_depth = 0; |
+ for (BasicBlockVectorIter i = final_order->begin(); i != final_order->end(); |
+ ++i) { |
+ BasicBlock* current = *i; |
+ current->loop_header_ = current_header; |
+ if (current->IsLoopHeader()) { |
+ loop_depth++; |
+ current_loop = &loops[current->loop_end_]; |
+ BlockList* end = current_loop->end; |
+ current->loop_end_ = end == NULL ? static_cast<int>(final_order->size()) |
+ : end->block->rpo_number_; |
+ current_header = current_loop->header; |
+ if (FLAG_trace_turbo_scheduler) { |
+ PrintF("Block %d is a loop header, increment loop depth to %d\n", |
+ current->id(), loop_depth); |
+ } |
+ } else { |
+ while (current_header != NULL && |
+ current->rpo_number_ >= current_header->loop_end_) { |
+ ASSERT(current_header->IsLoopHeader()); |
+ ASSERT(current_loop != NULL); |
+ current_loop = current_loop->prev; |
+ current_header = current_loop == NULL ? NULL : current_loop->header; |
+ --loop_depth; |
+ } |
+ } |
+ current->loop_depth_ = loop_depth; |
+ if (FLAG_trace_turbo_scheduler) { |
+ if (current->loop_header_ == NULL) { |
+ PrintF("Block %d's loop header is NULL, loop depth %d\n", current->id(), |
+ current->loop_depth_); |
+ } else { |
+ PrintF("Block %d's loop header is block %d, loop depth %d\n", |
+ current->id(), current->loop_header_->id(), |
+ current->loop_depth_); |
+ } |
+ } |
+ } |
+ |
+#if DEBUG |
+ if (FLAG_trace_turbo_scheduler) PrintRPO(num_loops, loops, final_order); |
+ VerifySpecialRPO(num_loops, loops, final_order); |
+#endif |
+ return final_order; |
+} |
+} |
+} |
+} // namespace v8::internal::compiler |