Index: src/compiler/scheduler.cc |
diff --git a/src/compiler/scheduler.cc b/src/compiler/scheduler.cc |
index d1da320bd0d48dd23e299a2de1ff52a75f207d96..d9e67ee20a875e24f75d8f6972bc646a863e4ca7 100644 |
--- a/src/compiler/scheduler.cc |
+++ b/src/compiler/scheduler.cc |
@@ -35,29 +35,22 @@ Scheduler::Scheduler(Zone* zone, Graph* graph, Schedule* schedule) |
scheduled_nodes_(zone), |
schedule_root_nodes_(zone), |
schedule_queue_(zone), |
- node_data_(graph_->NodeCount(), DefaultSchedulerData(), zone), |
- has_floating_control_(false) {} |
+ node_data_(graph_->NodeCount(), DefaultSchedulerData(), zone) {} |
Schedule* Scheduler::ComputeSchedule(ZonePool* zone_pool, Graph* graph) { |
- Schedule* schedule; |
- bool had_floating_control = false; |
- do { |
- ZonePool::Scope zone_scope(zone_pool); |
- schedule = new (graph->zone()) |
- Schedule(graph->zone(), static_cast<size_t>(graph->NodeCount())); |
- Scheduler scheduler(zone_scope.zone(), graph, schedule); |
- |
- scheduler.BuildCFG(); |
- scheduler.ComputeSpecialRPONumbering(); |
- scheduler.GenerateImmediateDominatorTree(); |
+ ZonePool::Scope zone_scope(zone_pool); |
+ Schedule* schedule = new (graph->zone()) |
+ Schedule(graph->zone(), static_cast<size_t>(graph->NodeCount())); |
+ Scheduler scheduler(zone_scope.zone(), graph, schedule); |
- scheduler.PrepareUses(); |
- scheduler.ScheduleEarly(); |
- scheduler.ScheduleLate(); |
+ scheduler.BuildCFG(); |
+ scheduler.ComputeSpecialRPONumbering(); |
+ scheduler.GenerateImmediateDominatorTree(); |
- had_floating_control = scheduler.ConnectFloatingControl(); |
- } while (had_floating_control); |
+ scheduler.PrepareUses(); |
+ scheduler.ScheduleEarly(); |
+ scheduler.ScheduleLate(); |
return schedule; |
} |
@@ -92,19 +85,18 @@ Scheduler::Placement Scheduler::GetPlacement(Node* node) { |
break; |
} |
#define DEFINE_FLOATING_CONTROL_CASE(V) case IrOpcode::k##V: |
- CONTROL_OP_LIST(DEFINE_FLOATING_CONTROL_CASE) |
+ CONTROL_OP_LIST(DEFINE_FLOATING_CONTROL_CASE) |
#undef DEFINE_FLOATING_CONTROL_CASE |
- { |
- // Control nodes that were not control-reachable from end may float. |
- data->placement_ = kSchedulable; |
- if (!data->is_connected_control_) { |
- data->is_floating_control_ = true; |
- has_floating_control_ = true; |
- Trace("Floating control found: #%d:%s\n", node->id(), |
- node->op()->mnemonic()); |
- } |
- break; |
+ { |
+ // Control nodes that were not control-reachable from end may float. |
+ data->placement_ = kSchedulable; |
+ if (!data->is_connected_control_) { |
+ data->is_floating_control_ = true; |
+ Trace("Floating control found: #%d:%s\n", node->id(), |
+ node->op()->mnemonic()); |
} |
+ break; |
+ } |
default: |
data->placement_ = kSchedulable; |
break; |
@@ -114,6 +106,60 @@ Scheduler::Placement Scheduler::GetPlacement(Node* node) { |
} |
+void Scheduler::UpdatePlacement(Node* node, Placement placement) { |
+ SchedulerData* data = GetData(node); |
+ if (data->placement_ != kUnknown) { // Trap on mutation, not initialization. |
+ switch (node->opcode()) { |
+ case IrOpcode::kParameter: |
+ // Parameters are fixed once and for all. |
+ UNREACHABLE(); |
+ break; |
+ case IrOpcode::kPhi: |
+ case IrOpcode::kEffectPhi: { |
+ // Phis and effect phis are coupled to their respective blocks. |
+ DCHECK_EQ(Scheduler::kCoupled, data->placement_); |
+ DCHECK_EQ(Scheduler::kFixed, placement); |
+ Node* control = NodeProperties::GetControlInput(node); |
+ BasicBlock* block = schedule_->block(control); |
+ schedule_->AddNode(block, node); |
+ // TODO(mstarzinger): Cheap hack to make sure unscheduled use count of |
+ // control does not drop below zero. This might cause the control to be |
+ // queued for scheduling more than once, which makes this ugly! |
+ ++(GetData(control)->unscheduled_count_); |
+ break; |
+ } |
+#define DEFINE_FLOATING_CONTROL_CASE(V) case IrOpcode::k##V: |
+ CONTROL_OP_LIST(DEFINE_FLOATING_CONTROL_CASE) |
+#undef DEFINE_FLOATING_CONTROL_CASE |
+ { |
+ // Control nodes force coupled uses to be placed. |
+ Node::Uses uses = node->uses(); |
+ for (Node::Uses::iterator i = uses.begin(); i != uses.end(); ++i) { |
+ if (GetPlacement(*i) == Scheduler::kCoupled) { |
+ DCHECK_EQ(node, NodeProperties::GetControlInput(*i)); |
+ UpdatePlacement(*i, placement); |
+ } |
+ } |
+ break; |
+ } |
+ default: |
+ DCHECK_EQ(Scheduler::kSchedulable, data->placement_); |
+ DCHECK_EQ(Scheduler::kScheduled, placement); |
+ break; |
+ } |
+ // Reduce the use count of the node's inputs to potentially make them |
+ // schedulable. If all the uses of a node have been scheduled, then the node |
+ // itself can be scheduled. |
+ for (InputIter i = node->inputs().begin(); i != node->inputs().end(); ++i) { |
+ // TODO(mstarzinger): Another cheap hack for use counts. |
+ if (GetData(*i)->placement_ == kFixed) continue; |
+ DecrementUnscheduledUseCount(*i, i.edge().from()); |
+ } |
+ } |
+ data->placement_ = placement; |
+} |
+ |
+ |
void Scheduler::IncrementUnscheduledUseCount(Node* node, Node* from) { |
if (GetPlacement(node) == kCoupled) { |
// Use count for coupled nodes is summed up on their control. |
@@ -177,21 +223,19 @@ BasicBlock* Scheduler::GetCommonDominator(BasicBlock* b1, BasicBlock* b2) { |
// Internal class to build a control flow graph (i.e the basic blocks and edges |
-// between them within a Schedule) from the node graph. |
-// Visits the control edges of the graph backwards from end in order to find |
-// the connected control subgraph, needed for scheduling. |
+// between them within a Schedule) from the node graph. Visits control edges of |
+// the graph backwards from an end node in order to find the connected control |
+// subgraph, needed for scheduling. |
class CFGBuilder { |
public: |
- Scheduler* scheduler_; |
- Schedule* schedule_; |
- ZoneQueue<Node*> queue_; |
- NodeVector control_; |
- |
CFGBuilder(Zone* zone, Scheduler* scheduler) |
: scheduler_(scheduler), |
schedule_(scheduler->schedule_), |
queue_(zone), |
- control_(zone) {} |
+ control_(zone), |
+ component_head_(NULL), |
+ component_start_(NULL), |
+ component_end_(NULL) {} |
// Run the control flow graph construction algorithm by walking the graph |
// backwards from end through control edges, building and connecting the |
@@ -217,10 +261,40 @@ class CFGBuilder { |
FixNode(schedule_->end(), graph->end()); |
} |
+ // Run the control flow graph construction for a minimal control-connected |
+ // component ending in {node} and merge that component into an existing |
+ // control flow graph at the bottom of {block}. |
+ void Run(BasicBlock* block, Node* node) { |
+ Queue(node); |
+ |
+ component_start_ = block; |
+ component_end_ = schedule_->block(node); |
+ while (!queue_.empty()) { // Breadth-first backwards traversal. |
+ Node* node = queue_.front(); |
+ queue_.pop(); |
+ bool is_dom = true; |
+ int max = NodeProperties::PastControlIndex(node); |
+ for (int i = NodeProperties::FirstControlIndex(node); i < max; i++) { |
+ is_dom = is_dom && |
+ !scheduler_->GetData(node->InputAt(i))->is_floating_control_; |
+ Queue(node->InputAt(i)); |
+ } |
+ // TODO(mstarzinger): This is a hacky way to find component dominator. |
+ if (is_dom) component_head_ = node; |
+ } |
+ DCHECK_NOT_NULL(component_head_); |
+ |
+ for (NodeVector::iterator i = control_.begin(); i != control_.end(); ++i) { |
+ scheduler_->GetData(*i)->is_floating_control_ = false; |
+ ConnectBlocks(*i); // Connect block to its predecessor/successors. |
+ } |
+ } |
+ |
+ private: |
void FixNode(BasicBlock* block, Node* node) { |
schedule_->AddNode(block, node); |
scheduler_->GetData(node)->is_connected_control_ = true; |
- scheduler_->GetData(node)->placement_ = Scheduler::kFixed; |
+ scheduler_->UpdatePlacement(node, Scheduler::kFixed); |
} |
void Queue(Node* node) { |
@@ -257,11 +331,11 @@ class CFGBuilder { |
ConnectMerge(node); |
break; |
case IrOpcode::kBranch: |
- scheduler_->schedule_root_nodes_.push_back(node); |
+ scheduler_->UpdatePlacement(node, Scheduler::kFixed); |
ConnectBranch(node); |
break; |
case IrOpcode::kReturn: |
- scheduler_->schedule_root_nodes_.push_back(node); |
+ scheduler_->UpdatePlacement(node, Scheduler::kFixed); |
ConnectReturn(node); |
break; |
default: |
@@ -318,17 +392,10 @@ class CFGBuilder { |
} |
void ConnectBranch(Node* branch) { |
- Node* branch_block_node = NodeProperties::GetControlInput(branch); |
- BasicBlock* branch_block = schedule_->block(branch_block_node); |
- DCHECK(branch_block != NULL); |
- |
BasicBlock* successor_blocks[2]; |
CollectSuccessorBlocks(branch, successor_blocks, IrOpcode::kIfTrue, |
IrOpcode::kIfFalse); |
- TraceConnect(branch, branch_block, successor_blocks[0]); |
- TraceConnect(branch, branch_block, successor_blocks[1]); |
- |
// Consider branch hints. |
// TODO(turbofan): Propagate the deferred flag to all blocks dominated by |
// this IfTrue/IfFalse later. |
@@ -343,8 +410,21 @@ class CFGBuilder { |
break; |
} |
- schedule_->AddBranch(branch_block, branch, successor_blocks[0], |
- successor_blocks[1]); |
+ if (branch == component_head_) { |
+ TraceConnect(branch, component_start_, successor_blocks[0]); |
+ TraceConnect(branch, component_start_, successor_blocks[1]); |
+ schedule_->InsertBranch(component_start_, component_end_, branch, |
+ successor_blocks[0], successor_blocks[1]); |
+ } else { |
+ Node* branch_block_node = NodeProperties::GetControlInput(branch); |
+ BasicBlock* branch_block = schedule_->block(branch_block_node); |
+ DCHECK(branch_block != NULL); |
+ |
+ TraceConnect(branch, branch_block, successor_blocks[0]); |
+ TraceConnect(branch, branch_block, successor_blocks[1]); |
+ schedule_->AddBranch(branch_block, branch, successor_blocks[0], |
+ successor_blocks[1]); |
+ } |
} |
void ConnectMerge(Node* merge) { |
@@ -385,13 +465,25 @@ class CFGBuilder { |
return (node->opcode() == IrOpcode::kMerge && |
node == scheduler_->graph_->end()->InputAt(0)); |
} |
+ |
+ Scheduler* scheduler_; |
+ Schedule* schedule_; |
+ ZoneQueue<Node*> queue_; |
+ NodeVector control_; |
+ Node* component_head_; |
+ BasicBlock* component_start_; |
+ BasicBlock* component_end_; |
}; |
void Scheduler::BuildCFG() { |
Trace("--- CREATING CFG -------------------------------------------\n"); |
+ |
+ // Build a control-flow graph for the main control-connected component that |
+ // is being spanned by the graph's start and end nodes. |
CFGBuilder cfg_builder(zone_, this); |
cfg_builder.Run(); |
+ |
// Initialize per-block data. |
scheduled_nodes_.resize(schedule_->BasicBlockCount(), NodeVector(zone_)); |
} |
@@ -867,7 +959,7 @@ void Scheduler::GenerateImmediateDominatorTree() { |
DCHECK(current_pred != end); |
BasicBlock* dominator = *current_pred; |
++current_pred; |
- // For multiple predecessors, walk up the rpo ordering until a common |
+ // 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) { |
@@ -1091,6 +1183,7 @@ class ScheduleLateNodeVisitor { |
// Determine the dominating block for all of the uses of this node. It is |
// the latest block that this node can be scheduled in. |
+ Trace("Scheduling #%d:%s\n", node->id(), node->op()->mnemonic()); |
BasicBlock* block = GetCommonDominatorOfUses(node); |
DCHECK_NOT_NULL(block); |
@@ -1111,7 +1204,12 @@ class ScheduleLateNodeVisitor { |
hoist_block = GetPreHeader(hoist_block); |
} |
- ScheduleNode(block, node); |
+ // Schedule the node or a floating control structure. |
+ if (NodeProperties::IsControl(node)) { |
+ ScheduleFloatingControl(block, node); |
+ } else { |
+ ScheduleNode(block, node); |
+ } |
} |
BasicBlock* GetPreHeader(BasicBlock* block) { |
@@ -1144,7 +1242,7 @@ class ScheduleLateNodeVisitor { |
// If the use is from a coupled (i.e. floating) phi, compute the common |
// dominator of its uses. This will not recurse more than one level. |
if (scheduler_->GetPlacement(use) == Scheduler::kCoupled) { |
- Trace(" inspecting uses of coupled phi #%d:%s\n", use->id(), |
+ Trace(" inspecting uses of coupled #%d:%s\n", use->id(), |
use->op()->mnemonic()); |
DCHECK_EQ(edge.to(), NodeProperties::GetControlInput(use)); |
return GetCommonDominatorOfUses(use); |
@@ -1167,16 +1265,15 @@ class ScheduleLateNodeVisitor { |
return result; |
} |
+ void ScheduleFloatingControl(BasicBlock* block, Node* node) { |
+ DCHECK(scheduler_->GetData(node)->is_floating_control_); |
+ scheduler_->FuseFloatingControl(block, node); |
+ } |
+ |
void ScheduleNode(BasicBlock* block, Node* node) { |
schedule_->PlanNode(block, node); |
scheduler_->scheduled_nodes_[block->id().ToSize()].push_back(node); |
- |
- // Reduce the use count of the node's inputs to potentially make them |
- // schedulable. If all the uses of a node have been scheduled, then the node |
- // itself can be scheduled. |
- for (InputIter i = node->inputs().begin(); i != node->inputs().end(); ++i) { |
- scheduler_->DecrementUnscheduledUseCount(*i, i.edge().from()); |
- } |
+ scheduler_->UpdatePlacement(node, Scheduler::kScheduled); |
} |
Scheduler* scheduler_; |
@@ -1214,97 +1311,53 @@ void Scheduler::ScheduleLate() { |
// ----------------------------------------------------------------------------- |
-bool Scheduler::ConnectFloatingControl() { |
- if (!has_floating_control_) return false; |
- |
- Trace("Connecting floating control...\n"); |
- |
- // Process blocks and instructions backwards to find and connect floating |
- // control nodes into the control graph according to the block they were |
- // scheduled into. |
- int max = static_cast<int>(schedule_->rpo_order()->size()); |
- for (int i = max - 1; i >= 0; i--) { |
- BasicBlock* block = schedule_->rpo_order()->at(i); |
- // TODO(titzer): we place at most one floating control structure per |
- // basic block because scheduling currently can interleave phis from |
- // one subgraph with the merges from another subgraph. |
- for (size_t j = 0; j < block->NodeCount(); j++) { |
- Node* node = block->NodeAt(block->NodeCount() - 1 - j); |
- SchedulerData* data = GetData(node); |
- if (data->is_floating_control_ && !data->is_connected_control_) { |
- Trace(" Floating control #%d:%s was scheduled in B%d\n", node->id(), |
- node->op()->mnemonic(), block->id().ToInt()); |
- ConnectFloatingControlSubgraph(block, node); |
- break; |
- } |
- } |
+void Scheduler::FuseFloatingControl(BasicBlock* block, Node* node) { |
+ Trace("--- FUSE FLOATING CONTROL ----------------------------------\n"); |
+ if (FLAG_trace_turbo_scheduler) { |
+ OFStream os(stdout); |
+ os << "Schedule before control flow fusion:\n" << *schedule_; |
} |
- return true; |
-} |
+ // Iterate on phase 1: Build control-flow graph. |
+ CFGBuilder cfg_builder(zone_, this); |
+ cfg_builder.Run(block, node); |
+ |
+ // Iterate on phase 2: Compute special RPO and dominator tree. |
+ // TODO(mstarzinger): Currently "iterate on" means "re-run". Fix that. |
+ BasicBlockVector* rpo = schedule_->rpo_order(); |
+ for (BasicBlockVectorIter i = rpo->begin(); i != rpo->end(); ++i) { |
+ BasicBlock* block = *i; |
+ block->set_rpo_number(-1); |
+ block->set_loop_header(NULL); |
+ block->set_loop_depth(0); |
+ block->set_loop_end(-1); |
+ } |
+ schedule_->rpo_order()->clear(); |
+ SpecialRPONumberer numberer(zone_, schedule_); |
+ numberer.ComputeSpecialRPO(); |
+ GenerateImmediateDominatorTree(); |
+ scheduled_nodes_.resize(schedule_->BasicBlockCount(), NodeVector(zone_)); |
+ // Move previously planned nodes. |
+ // TODO(mstarzinger): Improve that by supporting bulk moves. |
+ MovePlannedNodes(block, schedule_->block(node)); |
-void Scheduler::ConnectFloatingControlSubgraph(BasicBlock* block, Node* end) { |
- Node* block_start = block->NodeAt(0); |
- DCHECK(IrOpcode::IsControlOpcode(block_start->opcode())); |
- // Find the current "control successor" of the node that starts the block |
- // by searching the control uses for a control input edge from a connected |
- // control node. |
- Node* control_succ = NULL; |
- for (UseIter i = block_start->uses().begin(); i != block_start->uses().end(); |
- ++i) { |
- Node::Edge edge = i.edge(); |
- if (NodeProperties::IsControlEdge(edge) && |
- GetData(edge.from())->is_connected_control_) { |
- DCHECK_EQ(NULL, control_succ); |
- control_succ = edge.from(); |
- control_succ->ReplaceInput(edge.index(), end); |
- } |
- } |
- DCHECK_NE(NULL, control_succ); |
- Trace(" Inserting floating control end %d:%s between %d:%s -> %d:%s\n", |
- end->id(), end->op()->mnemonic(), control_succ->id(), |
- control_succ->op()->mnemonic(), block_start->id(), |
- block_start->op()->mnemonic()); |
- |
- // Find the "start" node of the control subgraph, which should be the |
- // unique node that is itself floating control but has a control input that |
- // is not floating. |
- Node* start = NULL; |
- ZoneQueue<Node*> queue(zone_); |
- queue.push(end); |
- GetData(end)->is_connected_control_ = true; |
- while (!queue.empty()) { |
- Node* node = queue.front(); |
- queue.pop(); |
- Trace(" Search #%d:%s for control subgraph start\n", node->id(), |
- node->op()->mnemonic()); |
- int max = NodeProperties::PastControlIndex(node); |
- for (int i = NodeProperties::FirstControlIndex(node); i < max; i++) { |
- Node* input = node->InputAt(i); |
- SchedulerData* data = GetData(input); |
- if (data->is_floating_control_) { |
- // {input} is floating control. |
- if (!data->is_connected_control_) { |
- // First time seeing {input} during this traversal, queue it. |
- queue.push(input); |
- data->is_connected_control_ = true; |
- } |
- } else { |
- // Otherwise, {node} is the start node, because it is floating control |
- // but is connected to {input} that is not floating control. |
- DCHECK_EQ(NULL, start); // There can be only one. |
- start = node; |
- } |
- } |
+ if (FLAG_trace_turbo_scheduler) { |
+ OFStream os(stdout); |
+ os << "Schedule after control flow fusion:" << *schedule_; |
} |
+} |
- DCHECK_NE(NULL, start); |
- start->ReplaceInput(NodeProperties::FirstControlIndex(start), block_start); |
- Trace(" Connecting floating control start %d:%s to %d:%s\n", start->id(), |
- start->op()->mnemonic(), block_start->id(), |
- block_start->op()->mnemonic()); |
+void Scheduler::MovePlannedNodes(BasicBlock* from, BasicBlock* to) { |
+ Trace("Move planned nodes from B%d to B%d\n", from->id().ToInt(), |
+ to->id().ToInt()); |
+ NodeVector* nodes = &(scheduled_nodes_[from->id().ToSize()]); |
+ for (NodeVectorIter i = nodes->begin(); i != nodes->end(); ++i) { |
+ schedule_->SetBlockForNode(to, *i); |
+ scheduled_nodes_[to->id().ToSize()].push_back(*i); |
+ } |
+ nodes->clear(); |
} |
} // namespace compiler |