Improve comments and readability of scheduler.
R=jarin@chromium.org Review URL: https://codereview.chromium.org/642803003 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24526 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
6490b9a656
commit
2d29390448
@ -28,6 +28,102 @@ static inline void Trace(const char* msg, ...) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Scheduler::Scheduler(Zone* zone, Graph* graph, Schedule* schedule)
|
||||||
|
: zone_(zone),
|
||||||
|
graph_(graph),
|
||||||
|
schedule_(schedule),
|
||||||
|
scheduled_nodes_(zone),
|
||||||
|
schedule_root_nodes_(zone),
|
||||||
|
node_data_(graph_->NodeCount(), DefaultSchedulerData(), zone),
|
||||||
|
has_floating_control_(false) {}
|
||||||
|
|
||||||
|
|
||||||
|
Schedule* Scheduler::ComputeSchedule(Graph* graph) {
|
||||||
|
Schedule* schedule;
|
||||||
|
bool had_floating_control = false;
|
||||||
|
do {
|
||||||
|
Zone tmp_zone(graph->zone()->isolate());
|
||||||
|
schedule = new (graph->zone())
|
||||||
|
Schedule(graph->zone(), static_cast<size_t>(graph->NodeCount()));
|
||||||
|
Scheduler scheduler(&tmp_zone, graph, schedule);
|
||||||
|
|
||||||
|
scheduler.BuildCFG();
|
||||||
|
Scheduler::ComputeSpecialRPO(schedule);
|
||||||
|
scheduler.GenerateImmediateDominatorTree();
|
||||||
|
|
||||||
|
scheduler.PrepareUses();
|
||||||
|
scheduler.ScheduleEarly();
|
||||||
|
scheduler.ScheduleLate();
|
||||||
|
|
||||||
|
had_floating_control = scheduler.ConnectFloatingControl();
|
||||||
|
} while (had_floating_control);
|
||||||
|
|
||||||
|
return schedule;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Scheduler::SchedulerData Scheduler::DefaultSchedulerData() {
|
||||||
|
SchedulerData def = {0, -1, false, false, kUnknown};
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Scheduler::Placement Scheduler::GetPlacement(Node* node) {
|
||||||
|
SchedulerData* data = GetData(node);
|
||||||
|
if (data->placement_ == kUnknown) { // Compute placement, once, on demand.
|
||||||
|
switch (node->opcode()) {
|
||||||
|
case IrOpcode::kParameter:
|
||||||
|
// Parameters are always fixed to the start node.
|
||||||
|
data->placement_ = kFixed;
|
||||||
|
break;
|
||||||
|
case IrOpcode::kPhi:
|
||||||
|
case IrOpcode::kEffectPhi: {
|
||||||
|
// Phis and effect phis are fixed if their control inputs are.
|
||||||
|
data->placement_ = GetPlacement(NodeProperties::GetControlInput(node));
|
||||||
|
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 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;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
data->placement_ = kSchedulable;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data->placement_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BasicBlock* Scheduler::GetCommonDominator(BasicBlock* b1, BasicBlock* b2) {
|
||||||
|
while (b1 != b2) {
|
||||||
|
int b1_rpo = GetRPONumber(b1);
|
||||||
|
int b2_rpo = GetRPONumber(b2);
|
||||||
|
DCHECK(b1_rpo != b2_rpo);
|
||||||
|
if (b1_rpo < b2_rpo) {
|
||||||
|
b2 = b2->dominator();
|
||||||
|
} else {
|
||||||
|
b1 = b1->dominator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Phase 1: Build control-flow graph and dominator tree.
|
||||||
|
|
||||||
|
|
||||||
// Internal class to build a control flow graph (i.e the basic blocks and edges
|
// Internal class to build a control flow graph (i.e the basic blocks and edges
|
||||||
// between them within a Schedule) from the node graph.
|
// between them within a Schedule) from the node graph.
|
||||||
// Visits the control edges of the graph backwards from end in order to find
|
// Visits the control edges of the graph backwards from end in order to find
|
||||||
@ -218,84 +314,6 @@ class CFGBuilder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Scheduler::SchedulerData Scheduler::DefaultSchedulerData() {
|
|
||||||
SchedulerData def = {0, -1, false, false, kUnknown};
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Scheduler::Scheduler(Zone* zone, Graph* graph, Schedule* schedule)
|
|
||||||
: zone_(zone),
|
|
||||||
graph_(graph),
|
|
||||||
schedule_(schedule),
|
|
||||||
scheduled_nodes_(zone),
|
|
||||||
schedule_root_nodes_(zone),
|
|
||||||
node_data_(graph_->NodeCount(), DefaultSchedulerData(), zone),
|
|
||||||
has_floating_control_(false) {}
|
|
||||||
|
|
||||||
|
|
||||||
Schedule* Scheduler::ComputeSchedule(Graph* graph) {
|
|
||||||
Schedule* schedule;
|
|
||||||
bool had_floating_control = false;
|
|
||||||
do {
|
|
||||||
Zone tmp_zone(graph->zone()->isolate());
|
|
||||||
schedule = new (graph->zone())
|
|
||||||
Schedule(graph->zone(), static_cast<size_t>(graph->NodeCount()));
|
|
||||||
Scheduler scheduler(&tmp_zone, graph, schedule);
|
|
||||||
|
|
||||||
scheduler.BuildCFG();
|
|
||||||
|
|
||||||
Scheduler::ComputeSpecialRPO(schedule);
|
|
||||||
scheduler.GenerateImmediateDominatorTree();
|
|
||||||
|
|
||||||
scheduler.PrepareUses();
|
|
||||||
scheduler.ScheduleEarly();
|
|
||||||
scheduler.ScheduleLate();
|
|
||||||
|
|
||||||
had_floating_control = scheduler.ConnectFloatingControl();
|
|
||||||
} while (had_floating_control);
|
|
||||||
|
|
||||||
return schedule;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Scheduler::Placement Scheduler::GetPlacement(Node* node) {
|
|
||||||
SchedulerData* data = GetData(node);
|
|
||||||
if (data->placement_ == kUnknown) { // Compute placement, once, on demand.
|
|
||||||
switch (node->opcode()) {
|
|
||||||
case IrOpcode::kParameter:
|
|
||||||
// Parameters are always fixed to the start node.
|
|
||||||
data->placement_ = kFixed;
|
|
||||||
break;
|
|
||||||
case IrOpcode::kPhi:
|
|
||||||
case IrOpcode::kEffectPhi: {
|
|
||||||
// Phis and effect phis are fixed if their control inputs are.
|
|
||||||
data->placement_ = GetPlacement(NodeProperties::GetControlInput(node));
|
|
||||||
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 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;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
data->placement_ = kSchedulable;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return data->placement_;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Scheduler::BuildCFG() {
|
void Scheduler::BuildCFG() {
|
||||||
Trace("---------------- CREATING CFG ------------------\n");
|
Trace("---------------- CREATING CFG ------------------\n");
|
||||||
CFGBuilder cfg_builder(zone_, this);
|
CFGBuilder cfg_builder(zone_, this);
|
||||||
@ -305,21 +323,6 @@ void Scheduler::BuildCFG() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BasicBlock* Scheduler::GetCommonDominator(BasicBlock* b1, BasicBlock* b2) {
|
|
||||||
while (b1 != b2) {
|
|
||||||
int b1_rpo = GetRPONumber(b1);
|
|
||||||
int b2_rpo = GetRPONumber(b2);
|
|
||||||
DCHECK(b1_rpo != b2_rpo);
|
|
||||||
if (b1_rpo < b2_rpo) {
|
|
||||||
b2 = b2->dominator();
|
|
||||||
} else {
|
|
||||||
b1 = b1->dominator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Scheduler::GenerateImmediateDominatorTree() {
|
void Scheduler::GenerateImmediateDominatorTree() {
|
||||||
// Build the dominator graph. TODO(danno): consider using Lengauer & Tarjan's
|
// Build the dominator graph. TODO(danno): consider using Lengauer & Tarjan's
|
||||||
// if this becomes really slow.
|
// if this becomes really slow.
|
||||||
@ -352,6 +355,69 @@ void Scheduler::GenerateImmediateDominatorTree() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Phase 2: Prepare use counts for nodes.
|
||||||
|
|
||||||
|
|
||||||
|
class PrepareUsesVisitor : public NullNodeVisitor {
|
||||||
|
public:
|
||||||
|
explicit PrepareUsesVisitor(Scheduler* scheduler)
|
||||||
|
: scheduler_(scheduler), schedule_(scheduler->schedule_) {}
|
||||||
|
|
||||||
|
GenericGraphVisit::Control Pre(Node* node) {
|
||||||
|
if (scheduler_->GetPlacement(node) == Scheduler::kFixed) {
|
||||||
|
// Fixed nodes are always roots for schedule late.
|
||||||
|
scheduler_->schedule_root_nodes_.push_back(node);
|
||||||
|
if (!schedule_->IsScheduled(node)) {
|
||||||
|
// Make sure root nodes are scheduled in their respective blocks.
|
||||||
|
Trace(" Scheduling fixed position node #%d:%s\n", node->id(),
|
||||||
|
node->op()->mnemonic());
|
||||||
|
IrOpcode::Value opcode = node->opcode();
|
||||||
|
BasicBlock* block =
|
||||||
|
opcode == IrOpcode::kParameter
|
||||||
|
? schedule_->start()
|
||||||
|
: schedule_->block(NodeProperties::GetControlInput(node));
|
||||||
|
DCHECK(block != NULL);
|
||||||
|
schedule_->AddNode(block, 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)) {
|
||||||
|
DCHECK_NE(Scheduler::kFixed, scheduler_->GetPlacement(from));
|
||||||
|
++(scheduler_->GetData(to)->unscheduled_count_);
|
||||||
|
Trace(" Use count of #%d:%s (used by #%d:%s)++ = %d\n", to->id(),
|
||||||
|
to->op()->mnemonic(), from->id(), from->op()->mnemonic(),
|
||||||
|
scheduler_->GetData(to)->unscheduled_count_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Scheduler* scheduler_;
|
||||||
|
Schedule* schedule_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void Scheduler::PrepareUses() {
|
||||||
|
Trace("------------------- 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Phase 3: Schedule nodes early.
|
||||||
|
|
||||||
|
|
||||||
class ScheduleEarlyNodeVisitor : public NullNodeVisitor {
|
class ScheduleEarlyNodeVisitor : public NullNodeVisitor {
|
||||||
public:
|
public:
|
||||||
explicit ScheduleEarlyNodeVisitor(Scheduler* scheduler)
|
explicit ScheduleEarlyNodeVisitor(Scheduler* scheduler)
|
||||||
@ -429,59 +495,8 @@ void Scheduler::ScheduleEarly() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PrepareUsesVisitor : public NullNodeVisitor {
|
// -----------------------------------------------------------------------------
|
||||||
public:
|
// Phase 4: Schedule nodes late.
|
||||||
explicit PrepareUsesVisitor(Scheduler* scheduler)
|
|
||||||
: scheduler_(scheduler), schedule_(scheduler->schedule_) {}
|
|
||||||
|
|
||||||
GenericGraphVisit::Control Pre(Node* node) {
|
|
||||||
if (scheduler_->GetPlacement(node) == Scheduler::kFixed) {
|
|
||||||
// Fixed nodes are always roots for schedule late.
|
|
||||||
scheduler_->schedule_root_nodes_.push_back(node);
|
|
||||||
if (!schedule_->IsScheduled(node)) {
|
|
||||||
// Make sure root nodes are scheduled in their respective blocks.
|
|
||||||
Trace(" Scheduling fixed position node #%d:%s\n", node->id(),
|
|
||||||
node->op()->mnemonic());
|
|
||||||
IrOpcode::Value opcode = node->opcode();
|
|
||||||
BasicBlock* block =
|
|
||||||
opcode == IrOpcode::kParameter
|
|
||||||
? schedule_->start()
|
|
||||||
: schedule_->block(NodeProperties::GetControlInput(node));
|
|
||||||
DCHECK(block != NULL);
|
|
||||||
schedule_->AddNode(block, 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)) {
|
|
||||||
DCHECK_NE(Scheduler::kFixed, scheduler_->GetPlacement(from));
|
|
||||||
++(scheduler_->GetData(to)->unscheduled_count_);
|
|
||||||
Trace(" Use count of #%d:%s (used by #%d:%s)++ = %d\n", to->id(),
|
|
||||||
to->op()->mnemonic(), from->id(), from->op()->mnemonic(),
|
|
||||||
scheduler_->GetData(to)->unscheduled_count_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Scheduler* scheduler_;
|
|
||||||
Schedule* schedule_;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void Scheduler::PrepareUses() {
|
|
||||||
Trace("------------------- 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 {
|
class ScheduleLateNodeVisitor : public NullNodeVisitor {
|
||||||
@ -637,6 +652,9 @@ void Scheduler::ScheduleLate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
bool Scheduler::ConnectFloatingControl() {
|
bool Scheduler::ConnectFloatingControl() {
|
||||||
if (!has_floating_control_) return false;
|
if (!has_floating_control_) return false;
|
||||||
|
|
||||||
@ -1137,6 +1155,7 @@ BasicBlockVector* Scheduler::ComputeSpecialRPO(Schedule* schedule) {
|
|||||||
#endif
|
#endif
|
||||||
return final_order;
|
return final_order;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
} // namespace compiler
|
||||||
} // namespace v8::internal::compiler
|
} // namespace internal
|
||||||
|
} // namespace v8
|
||||||
|
@ -19,17 +19,13 @@ namespace compiler {
|
|||||||
// ordering the basic blocks in the special RPO order.
|
// ordering the basic blocks in the special RPO order.
|
||||||
class Scheduler {
|
class Scheduler {
|
||||||
public:
|
public:
|
||||||
// The complete scheduling algorithm.
|
// The complete scheduling algorithm. Creates a new schedule and places all
|
||||||
// Create a new schedule and place all nodes from the graph into it.
|
// nodes from the graph into it.
|
||||||
static Schedule* ComputeSchedule(Graph* graph);
|
static Schedule* ComputeSchedule(Graph* graph);
|
||||||
|
|
||||||
// Compute the RPO of blocks in an existing schedule.
|
// Compute the RPO of blocks in an existing schedule.
|
||||||
static BasicBlockVector* ComputeSpecialRPO(Schedule* schedule);
|
static BasicBlockVector* ComputeSpecialRPO(Schedule* schedule);
|
||||||
|
|
||||||
// (Exposed for testing only)
|
|
||||||
// Build and connect the CFG for a node graph, but don't schedule nodes.
|
|
||||||
static void ComputeCFG(Graph* graph, Schedule* schedule);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum Placement { kUnknown, kSchedulable, kFixed };
|
enum Placement { kUnknown, kSchedulable, kFixed };
|
||||||
|
|
||||||
@ -61,8 +57,6 @@ class Scheduler {
|
|||||||
return &node_data_[node->id()];
|
return &node_data_[node->id()];
|
||||||
}
|
}
|
||||||
|
|
||||||
void BuildCFG();
|
|
||||||
|
|
||||||
Placement GetPlacement(Node* node);
|
Placement GetPlacement(Node* node);
|
||||||
|
|
||||||
int GetRPONumber(BasicBlock* block) {
|
int GetRPONumber(BasicBlock* block) {
|
||||||
@ -73,26 +67,31 @@ class Scheduler {
|
|||||||
return block->rpo_number();
|
return block->rpo_number();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenerateImmediateDominatorTree();
|
|
||||||
BasicBlock* GetCommonDominator(BasicBlock* b1, BasicBlock* b2);
|
BasicBlock* GetCommonDominator(BasicBlock* b1, BasicBlock* b2);
|
||||||
|
|
||||||
|
// Phase 1: Build control-flow graph and dominator tree.
|
||||||
friend class CFGBuilder;
|
friend class CFGBuilder;
|
||||||
|
void BuildCFG();
|
||||||
|
void GenerateImmediateDominatorTree();
|
||||||
|
|
||||||
friend class ScheduleEarlyNodeVisitor;
|
// Phase 2: Prepare use counts for nodes.
|
||||||
void ScheduleEarly();
|
|
||||||
|
|
||||||
friend class PrepareUsesVisitor;
|
friend class PrepareUsesVisitor;
|
||||||
void PrepareUses();
|
void PrepareUses();
|
||||||
|
|
||||||
|
// Phase 3: Schedule nodes early.
|
||||||
|
friend class ScheduleEarlyNodeVisitor;
|
||||||
|
void ScheduleEarly();
|
||||||
|
|
||||||
|
// Phase 4: Schedule nodes late.
|
||||||
friend class ScheduleLateNodeVisitor;
|
friend class ScheduleLateNodeVisitor;
|
||||||
void ScheduleLate();
|
void ScheduleLate();
|
||||||
|
|
||||||
bool ConnectFloatingControl();
|
bool ConnectFloatingControl();
|
||||||
|
|
||||||
void ConnectFloatingControlSubgraph(BasicBlock* block, Node* node);
|
void ConnectFloatingControlSubgraph(BasicBlock* block, Node* node);
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
} // namespace compiler
|
||||||
} // namespace v8::internal::compiler
|
} // namespace internal
|
||||||
|
} // namespace v8
|
||||||
|
|
||||||
#endif // V8_COMPILER_SCHEDULER_H_
|
#endif // V8_COMPILER_SCHEDULER_H_
|
||||||
|
Loading…
Reference in New Issue
Block a user