| Index: src/compiler/scheduler.cc
|
| diff --git a/src/compiler/scheduler.cc b/src/compiler/scheduler.cc
|
| index bdca6b4f2bfa9bf99ecbf3575b2b31bd13d7b9d1..9f194cb32713c61dde6c34c4fe45c04ce64364be 100644
|
| --- a/src/compiler/scheduler.cc
|
| +++ b/src/compiler/scheduler.cc
|
| @@ -2,6 +2,9 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| +#include <deque>
|
| +#include <queue>
|
| +
|
| #include "src/compiler/scheduler.h"
|
|
|
| #include "src/compiler/graph.h"
|
| @@ -27,15 +30,63 @@ static inline void Trace(const char* msg, ...) {
|
|
|
| // 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 graph backwards from end, following control and data edges.
|
| -class CFGBuilder : public NullNodeVisitor {
|
| +// Visits the control edges of the graph backwards from end 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) {}
|
| +
|
| + // Run the control flow graph construction algorithm by walking the graph
|
| + // backwards from end through control edges, building and connecting the
|
| + // basic blocks for control nodes.
|
| + void Run() {
|
| + Graph* graph = scheduler_->graph_;
|
| + FixNode(schedule_->start(), graph->start());
|
| + Queue(graph->end());
|
| +
|
| + while (!queue_.empty()) { // Breadth-first backwards traversal.
|
| + Node* node = queue_.front();
|
| + queue_.pop();
|
| + int max = NodeProperties::PastControlIndex(node);
|
| + for (int i = NodeProperties::FirstControlIndex(node); i < max; i++) {
|
| + Queue(node->InputAt(i));
|
| + }
|
| + }
|
|
|
| - explicit CFGBuilder(Schedule* schedule) : schedule_(schedule) {}
|
| + for (NodeVector::iterator i = control_.begin(); i != control_.end(); ++i) {
|
| + ConnectBlocks(*i); // Connect block to its predecessor/successors.
|
| + }
|
| +
|
| + FixNode(schedule_->end(), graph->end());
|
| + }
|
| +
|
| + void FixNode(BasicBlock* block, Node* node) {
|
| + schedule_->AddNode(block, node);
|
| + scheduler_->GetData(node)->is_connected_control_ = true;
|
| + scheduler_->GetData(node)->placement_ = Scheduler::kFixed;
|
| + }
|
|
|
| - // Create the blocks for the schedule in pre-order.
|
| - void PreEdge(Node* from, int index, Node* node) {
|
| + void Queue(Node* node) {
|
| + // Mark the connected control nodes as they queued.
|
| + Scheduler::SchedulerData* data = scheduler_->GetData(node);
|
| + if (!data->is_connected_control_) {
|
| + BuildBlocks(node);
|
| + queue_.push(node);
|
| + control_.push_back(node);
|
| + data->is_connected_control_ = true;
|
| + }
|
| + }
|
| +
|
| + void BuildBlocks(Node* node) {
|
| switch (node->opcode()) {
|
| case IrOpcode::kLoop:
|
| case IrOpcode::kMerge:
|
| @@ -55,38 +106,40 @@ class CFGBuilder : public NullNodeVisitor {
|
| }
|
| }
|
|
|
| - // Connect the blocks after nodes have been visited in post-order.
|
| - GenericGraphVisit::Control Post(Node* node) {
|
| + void ConnectBlocks(Node* node) {
|
| switch (node->opcode()) {
|
| case IrOpcode::kLoop:
|
| case IrOpcode::kMerge:
|
| ConnectMerge(node);
|
| break;
|
| case IrOpcode::kBranch:
|
| + scheduler_->schedule_root_nodes_.push_back(node);
|
| ConnectBranch(node);
|
| break;
|
| case IrOpcode::kDeoptimize:
|
| + scheduler_->schedule_root_nodes_.push_back(node);
|
| ConnectDeoptimize(node);
|
| case IrOpcode::kCall:
|
| if (OperatorProperties::CanLazilyDeoptimize(node->op())) {
|
| + scheduler_->schedule_root_nodes_.push_back(node);
|
| ConnectCall(node);
|
| }
|
| break;
|
| case IrOpcode::kReturn:
|
| + scheduler_->schedule_root_nodes_.push_back(node);
|
| ConnectReturn(node);
|
| break;
|
| default:
|
| break;
|
| }
|
| - return GenericGraphVisit::CONTINUE;
|
| }
|
|
|
| void BuildBlockForNode(Node* node) {
|
| if (schedule_->block(node) == NULL) {
|
| BasicBlock* block = schedule_->NewBasicBlock();
|
| - Trace("Create block B%d for node %d (%s)\n", block->id(), node->id(),
|
| - IrOpcode::Mnemonic(node->opcode()));
|
| - schedule_->AddNode(block, node);
|
| + Trace("Create block B%d for #%d:%s\n", block->id(), node->id(),
|
| + node->op()->mnemonic());
|
| + FixNode(block, node);
|
| }
|
| }
|
|
|
| @@ -193,11 +246,11 @@ class CFGBuilder : public NullNodeVisitor {
|
| void TraceConnect(Node* node, BasicBlock* block, BasicBlock* succ) {
|
| DCHECK_NE(NULL, block);
|
| if (succ == NULL) {
|
| - Trace("node %d (%s) in block %d -> end\n", node->id(),
|
| - IrOpcode::Mnemonic(node->opcode()), block->id());
|
| + Trace("Connect #%d:%s, B%d -> end\n", node->id(), node->op()->mnemonic(),
|
| + block->id());
|
| } else {
|
| - Trace("node %d (%s) in block %d -> block %d\n", node->id(),
|
| - IrOpcode::Mnemonic(node->opcode()), block->id(), succ->id());
|
| + Trace("Connect #%d:%s, B%d -> B%d\n", node->id(), node->op()->mnemonic(),
|
| + block->id(), succ->id());
|
| }
|
| }
|
| };
|
| @@ -207,74 +260,80 @@ Scheduler::Scheduler(Zone* zone, Graph* graph, Schedule* schedule)
|
| : zone_(zone),
|
| graph_(graph),
|
| schedule_(schedule),
|
| - unscheduled_uses_(zone),
|
| scheduled_nodes_(zone),
|
| schedule_root_nodes_(zone),
|
| - schedule_early_rpo_index_(zone) {}
|
| + node_data_(graph_->NodeCount(),
|
| + SchedulerData{0, 0, false, false, kUnknown}, zone),
|
| + has_floating_control_(false) {}
|
|
|
|
|
| Schedule* Scheduler::ComputeSchedule(Graph* graph) {
|
| - Zone tmp_zone(graph->zone()->isolate());
|
| - Schedule* schedule = new (graph->zone()) Schedule(graph->zone());
|
| -
|
| - Scheduler::ComputeCFG(graph, schedule);
|
| + Schedule* schedule;
|
| + bool had_floating_control = false;
|
| + do {
|
| + Zone tmp_zone(graph->zone()->isolate());
|
| + schedule = new (graph->zone()) Schedule(graph->zone());
|
| + Scheduler scheduler(&tmp_zone, graph, schedule);
|
|
|
| - Scheduler scheduler(&tmp_zone, graph, schedule);
|
| + scheduler.BuildCFG();
|
|
|
| - scheduler.PrepareAuxiliaryNodeData();
|
| + Scheduler::ComputeSpecialRPO(schedule);
|
| + scheduler.GenerateImmediateDominatorTree();
|
|
|
| - scheduler.PrepareAuxiliaryBlockData();
|
| + scheduler.PrepareUses();
|
| + scheduler.ScheduleEarly();
|
| + scheduler.ScheduleLate();
|
|
|
| - Scheduler::ComputeSpecialRPO(schedule);
|
| - scheduler.GenerateImmediateDominatorTree();
|
| -
|
| - scheduler.PrepareUses();
|
| - scheduler.ScheduleEarly();
|
| - scheduler.ScheduleLate();
|
| + had_floating_control = scheduler.ConnectFloatingControl();
|
| + } while (had_floating_control);
|
|
|
| return schedule;
|
| }
|
|
|
|
|
| -bool Scheduler::IsBasicBlockBegin(Node* node) {
|
| - return OperatorProperties::IsBasicBlockBegin(node->op());
|
| -}
|
| -
|
| -
|
| -bool Scheduler::HasFixedSchedulePosition(Node* node) {
|
| - IrOpcode::Value opcode = node->opcode();
|
| - return (IrOpcode::IsControlOpcode(opcode)) ||
|
| - opcode == IrOpcode::kParameter || opcode == IrOpcode::kEffectPhi ||
|
| - opcode == IrOpcode::kPhi;
|
| -}
|
| -
|
| -
|
| -bool Scheduler::IsScheduleRoot(Node* node) {
|
| - IrOpcode::Value opcode = node->opcode();
|
| - return opcode == IrOpcode::kEnd || opcode == IrOpcode::kEffectPhi ||
|
| - opcode == IrOpcode::kPhi;
|
| +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::ComputeCFG(Graph* graph, Schedule* schedule) {
|
| - CFGBuilder cfg_builder(schedule);
|
| +void Scheduler::BuildCFG() {
|
| Trace("---------------- CREATING CFG ------------------\n");
|
| - schedule->AddNode(schedule->start(), graph->start());
|
| - graph->VisitNodeInputsFromEnd(&cfg_builder);
|
| - schedule->AddNode(schedule->end(), graph->end());
|
| -}
|
| -
|
| -
|
| -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(zone));
|
| - schedule_->immediate_dominator_.resize(schedule_->BasicBlockCount(), NULL);
|
| + CFGBuilder cfg_builder(zone_, this);
|
| + cfg_builder.Run();
|
| + // Initialize per-block data.
|
| + scheduled_nodes_.resize(schedule_->BasicBlockCount(), NodeVector(zone_));
|
| }
|
|
|
|
|
| @@ -284,9 +343,9 @@ BasicBlock* Scheduler::GetCommonDominator(BasicBlock* b1, BasicBlock* b2) {
|
| int b2_rpo = GetRPONumber(b2);
|
| DCHECK(b1_rpo != b2_rpo);
|
| if (b1_rpo < b2_rpo) {
|
| - b2 = schedule_->immediate_dominator_[b2->id()];
|
| + b2 = b2->dominator_;
|
| } else {
|
| - b1 = schedule_->immediate_dominator_[b1->id()];
|
| + b1 = b1->dominator_;
|
| }
|
| }
|
| return b1;
|
| @@ -318,7 +377,7 @@ void Scheduler::GenerateImmediateDominatorTree() {
|
| }
|
| ++current_pred;
|
| }
|
| - schedule_->immediate_dominator_[current_rpo->id()] = dominator;
|
| + current_rpo->dominator_ = dominator;
|
| Trace("Block %d's idom is %d\n", current_rpo->id(), dominator->id());
|
| }
|
| }
|
| @@ -333,39 +392,39 @@ class ScheduleEarlyNodeVisitor : public NullNodeVisitor {
|
| 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 (scheduler_->HasFixedSchedulePosition(node)) {
|
| + if (scheduler_->GetPlacement(node) == Scheduler::kFixed) {
|
| BasicBlock* block = schedule_->block(node);
|
| DCHECK(block != NULL);
|
| max_rpo = block->rpo_number_;
|
| - if (scheduler_->schedule_early_rpo_index_[id] != max_rpo) {
|
| + if (scheduler_->GetData(node)->minimum_rpo_ != max_rpo) {
|
| has_changed_rpo_constraints_ = true;
|
| }
|
| - scheduler_->schedule_early_rpo_index_[id] = max_rpo;
|
| - Trace("Node %d pre-scheduled early at rpo limit %d\n", id, max_rpo);
|
| + scheduler_->GetData(node)->minimum_rpo_ = max_rpo;
|
| + Trace("Preschedule #%d:%s minimum_rpo = %d\n", node->id(),
|
| + node->op()->mnemonic(), 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 (!scheduler_->HasFixedSchedulePosition(node)) {
|
| + if (scheduler_->GetPlacement(node) != Scheduler::kFixed) {
|
| for (InputIter i = node->inputs().begin(); i != node->inputs().end();
|
| ++i) {
|
| - int control_rpo = scheduler_->schedule_early_rpo_index_[(*i)->id()];
|
| + int control_rpo = scheduler_->GetData(*i)->minimum_rpo_;
|
| if (control_rpo > max_rpo) {
|
| max_rpo = control_rpo;
|
| }
|
| }
|
| - if (scheduler_->schedule_early_rpo_index_[id] != max_rpo) {
|
| + if (scheduler_->GetData(node)->minimum_rpo_ != max_rpo) {
|
| has_changed_rpo_constraints_ = true;
|
| }
|
| - scheduler_->schedule_early_rpo_index_[id] = max_rpo;
|
| - Trace("Node %d post-scheduled early at rpo limit %d\n", id, max_rpo);
|
| + scheduler_->GetData(node)->minimum_rpo_ = max_rpo;
|
| + Trace("Postschedule #%d:%s minimum_rpo = %d\n", node->id(),
|
| + node->op()->mnemonic(), max_rpo);
|
| }
|
| return GenericGraphVisit::CONTINUE;
|
| }
|
| @@ -401,24 +460,21 @@ class PrepareUsesVisitor : public NullNodeVisitor {
|
| : 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) &&
|
| - scheduler_->HasFixedSchedulePosition(node)) {
|
| - Trace("Fixed position node %d is unscheduled, scheduling now\n",
|
| - node->id());
|
| - IrOpcode::Value opcode = node->opcode();
|
| - BasicBlock* block =
|
| - opcode == IrOpcode::kParameter
|
| - ? schedule_->start()
|
| - : schedule_->block(NodeProperties::GetControlInput(node));
|
| - DCHECK(block != NULL);
|
| - schedule_->AddNode(block, node);
|
| - }
|
| -
|
| - if (scheduler_->IsScheduleRoot(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;
|
| @@ -429,10 +485,11 @@ class PrepareUsesVisitor : public NullNodeVisitor {
|
| // for all of its inputs. The same criterion will be used in ScheduleLate
|
| // for decrementing use counts.
|
| if (!schedule_->IsScheduled(from)) {
|
| - DCHECK(!scheduler_->HasFixedSchedulePosition(from));
|
| - ++scheduler_->unscheduled_uses_[to->id()];
|
| - Trace("Incrementing uses of node %d from %d to %d\n", to->id(),
|
| - from->id(), scheduler_->unscheduled_uses_[to->id()]);
|
| + 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_);
|
| }
|
| }
|
|
|
| @@ -461,13 +518,14 @@ class ScheduleLateNodeVisitor : public NullNodeVisitor {
|
| if (schedule_->IsScheduled(node)) {
|
| return GenericGraphVisit::CONTINUE;
|
| }
|
| - DCHECK(!scheduler_->HasFixedSchedulePosition(node));
|
| + Scheduler::SchedulerData* data = scheduler_->GetData(node);
|
| + DCHECK_EQ(Scheduler::kSchedulable, data->placement_);
|
|
|
| // 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;
|
| - Trace("Testing for schedule eligibility for node %d -> %s\n", node->id(),
|
| - eligible ? "true" : "false");
|
| + bool eligible = data->unscheduled_count_ == 0;
|
| + Trace("Testing for schedule eligibility for #%d:%s = %s\n", node->id(),
|
| + node->op()->mnemonic(), eligible ? "true" : "false");
|
| if (!eligible) return GenericGraphVisit::DEFER;
|
|
|
| // Determine the dominating block for all of the uses of this node. It is
|
| @@ -483,29 +541,30 @@ class ScheduleLateNodeVisitor : public NullNodeVisitor {
|
| }
|
| DCHECK(block != NULL);
|
|
|
| - int min_rpo = scheduler_->schedule_early_rpo_index_[node->id()];
|
| + int min_rpo = data->minimum_rpo_;
|
| Trace(
|
| - "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);
|
| + "Schedule late conservative for #%d:%s is B%d at loop depth %d, "
|
| + "minimum_rpo = %d\n",
|
| + node->id(), node->op()->mnemonic(), 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
|
| + // into enclosing 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;
|
| - Trace("Hoisting node %d to block %d\n", node->id(), block->id());
|
| + Trace(" hoisting #%d:%s to block %d\n", node->id(),
|
| + node->op()->mnemonic(), 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);
|
| + BasicBlock* pre_header = hoist_block->dominator_;
|
| DCHECK(pre_header == NULL ||
|
| *hoist_block->predecessors().begin() == pre_header);
|
| Trace(
|
| - "Try hoist to pre-header block %d of loop header block %d,"
|
| - " depth would be %d\n",
|
| + " hoist to pre-header B%d of loop header B%d, depth would be %d\n",
|
| pre_header->id(), hoist_block->id(), pre_header->loop_depth_);
|
| hoist_block = pre_header;
|
| }
|
| @@ -520,19 +579,23 @@ class ScheduleLateNodeVisitor : public NullNodeVisitor {
|
| BasicBlock* GetBlockForUse(Node::Edge edge) {
|
| Node* use = edge.from();
|
| IrOpcode::Value opcode = use->opcode();
|
| - // If the use is a phi, forward through the phi to the basic block
|
| - // corresponding to the phi's input.
|
| if (opcode == IrOpcode::kPhi || opcode == IrOpcode::kEffectPhi) {
|
| + // If the use is from a fixed (i.e. non-floating) phi, use the block
|
| + // of the corresponding control input to the merge.
|
| int index = edge.index();
|
| - Trace("Use %d is input %d to a phi\n", use->id(), index);
|
| - use = NodeProperties::GetControlInput(use, 0);
|
| - opcode = use->opcode();
|
| - DCHECK(opcode == IrOpcode::kMerge || opcode == IrOpcode::kLoop);
|
| - use = NodeProperties::GetControlInput(use, index);
|
| + if (scheduler_->GetPlacement(use) == Scheduler::kFixed) {
|
| + Trace(" input@%d into a fixed phi #%d:%s\n", index, use->id(),
|
| + use->op()->mnemonic());
|
| + Node* merge = NodeProperties::GetControlInput(use, 0);
|
| + opcode = merge->opcode();
|
| + DCHECK(opcode == IrOpcode::kMerge || opcode == IrOpcode::kLoop);
|
| + use = NodeProperties::GetControlInput(merge, index);
|
| + }
|
| }
|
| BasicBlock* result = schedule_->block(use);
|
| if (result == NULL) return NULL;
|
| - Trace("Must dominate use %d in block %d\n", use->id(), result->id());
|
| + Trace(" must dominate use #%d:%s in B%d\n", use->id(),
|
| + use->op()->mnemonic(), result->id());
|
| return result;
|
| }
|
|
|
| @@ -541,16 +604,18 @@ class ScheduleLateNodeVisitor : public NullNodeVisitor {
|
| scheduler_->scheduled_nodes_[block->id()].push_back(node);
|
|
|
| // Reduce the use count of the node's inputs to potentially make them
|
| - // scheduable.
|
| + // schedulable.
|
| for (InputIter i = node->inputs().begin(); i != node->inputs().end(); ++i) {
|
| - DCHECK(scheduler_->unscheduled_uses_[(*i)->id()] > 0);
|
| - --scheduler_->unscheduled_uses_[(*i)->id()];
|
| + Scheduler::SchedulerData* data = scheduler_->GetData(*i);
|
| + DCHECK(data->unscheduled_count_ > 0);
|
| + --data->unscheduled_count_;
|
| if (FLAG_trace_turbo_scheduler) {
|
| - Trace("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) {
|
| - Trace("node %d is now eligible for scheduling\n", (*i)->id());
|
| + Trace(" Use count for #%d:%s (used by #%d:%s)-- = %d\n", (*i)->id(),
|
| + (*i)->op()->mnemonic(), i.edge().from()->id(),
|
| + i.edge().from()->op()->mnemonic(), data->unscheduled_count_);
|
| + if (data->unscheduled_count_ == 0) {
|
| + Trace(" newly eligible #%d:%s\n", (*i)->id(),
|
| + (*i)->op()->mnemonic());
|
| }
|
| }
|
| }
|
| @@ -563,6 +628,14 @@ class ScheduleLateNodeVisitor : public NullNodeVisitor {
|
|
|
| void Scheduler::ScheduleLate() {
|
| Trace("------------------- SCHEDULE LATE -----------------\n");
|
| + if (FLAG_trace_turbo_scheduler) {
|
| + Trace("roots: ");
|
| + for (NodeVectorIter i = schedule_root_nodes_.begin();
|
| + i != schedule_root_nodes_.end(); ++i) {
|
| + Trace("#%d:%s ", (*i)->id(), (*i)->op()->mnemonic());
|
| + }
|
| + Trace("\n");
|
| + }
|
|
|
| // Schedule: Places nodes in dominator block of all their uses.
|
| ScheduleLateNodeVisitor schedule_late_visitor(this);
|
| @@ -587,6 +660,96 @@ 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);
|
| + for (int j = static_cast<int>(block->nodes_.size()) - 1; j >= 0; j--) {
|
| + Node* node = block->nodes_[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());
|
| + ConnectFloatingControlSubgraph(block, node);
|
| + }
|
| + }
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +
|
| +void Scheduler::ConnectFloatingControlSubgraph(BasicBlock* block, Node* end) {
|
| + Node* block_start = block->nodes_[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;
|
| + }
|
| + }
|
| + }
|
| +
|
| + 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());
|
| +}
|
| +
|
| +
|
| // Numbering for BasicBlockData.rpo_number_ for this block traversal:
|
| static const int kBlockOnStack = -2;
|
| static const int kBlockVisited1 = -3;
|
| @@ -956,8 +1119,8 @@ BasicBlockVector* Scheduler::ComputeSpecialRPO(Schedule* schedule) {
|
| current->loop_end_ = end == NULL ? static_cast<int>(final_order->size())
|
| : end->block->rpo_number_;
|
| current_header = current_loop->header;
|
| - Trace("Block %d is a loop header, increment loop depth to %d\n",
|
| - current->id(), loop_depth);
|
| + Trace("B%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_) {
|
| @@ -969,9 +1132,13 @@ BasicBlockVector* Scheduler::ComputeSpecialRPO(Schedule* schedule) {
|
| }
|
| }
|
| current->loop_depth_ = loop_depth;
|
| - Trace("Block %d's loop header is block %d, loop depth %d\n", current->id(),
|
| - current->loop_header_ == NULL ? -1 : current->loop_header_->id(),
|
| - current->loop_depth_);
|
| + if (current->loop_header_ == NULL) {
|
| + Trace("B%d is not in a loop (depth == %d)\n", current->id(),
|
| + current->loop_depth_);
|
| + } else {
|
| + Trace("B%d has loop header B%d, (depth == %d)\n", current->id(),
|
| + current->loop_header_->id(), current->loop_depth_);
|
| + }
|
| }
|
|
|
| #if DEBUG
|
|
|