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

Unified Diff: src/compiler/scheduler.cc

Issue 426233002: Land the Fan (disabled) (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Review feedback, rebase and "git cl format" Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/compiler/scheduler.h ('k') | src/compiler/simplified-lowering.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « src/compiler/scheduler.h ('k') | src/compiler/simplified-lowering.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698