1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "src/compiler/scheduler.h"
9 #include "src/bit-vector.h"
10 #include "src/compiler/common-operator.h"
11 #include "src/compiler/control-equivalence.h"
12 #include "src/compiler/graph.h"
13 #include "src/compiler/node.h"
14 #include "src/compiler/node-marker.h"
15 #include "src/compiler/node-properties.h"
16 #include "src/zone-containers.h"
24 if (FLAG_trace_turbo_scheduler) PrintF(__VA_ARGS__); \
27 Scheduler::Scheduler(Zone* zone, Graph* graph, Schedule* schedule, Flags flags)
32 scheduled_nodes_(zone),
33 schedule_root_nodes_(zone),
34 schedule_queue_(zone),
35 node_data_(graph_->NodeCount(), DefaultSchedulerData(), zone) {}
38 Schedule* Scheduler::ComputeSchedule(Zone* zone, Graph* graph, Flags flags) {
39 Schedule* schedule = new (graph->zone())
40 Schedule(graph->zone(), static_cast<size_t>(graph->NodeCount()));
41 Scheduler scheduler(zone, graph, schedule, flags);
44 scheduler.ComputeSpecialRPONumbering();
45 scheduler.GenerateImmediateDominatorTree();
47 scheduler.PrepareUses();
48 scheduler.ScheduleEarly();
49 scheduler.ScheduleLate();
51 scheduler.SealFinalSchedule();
57 Scheduler::SchedulerData Scheduler::DefaultSchedulerData() {
58 SchedulerData def = {schedule_->start(), 0, kUnknown};
63 Scheduler::SchedulerData* Scheduler::GetData(Node* node) {
64 return &node_data_[node->id()];
68 Scheduler::Placement Scheduler::GetPlacement(Node* node) {
69 SchedulerData* data = GetData(node);
70 if (data->placement_ == kUnknown) { // Compute placement, once, on demand.
71 switch (node->opcode()) {
72 case IrOpcode::kParameter:
73 case IrOpcode::kOsrValue:
74 // Parameters and OSR values are always fixed to the start block.
75 data->placement_ = kFixed;
78 case IrOpcode::kEffectPhi: {
79 // Phis and effect phis are fixed if their control inputs are, whereas
80 // otherwise they are coupled to a floating control node.
81 Placement p = GetPlacement(NodeProperties::GetControlInput(node));
82 data->placement_ = (p == kFixed ? kFixed : kCoupled);
85 #define DEFINE_CONTROL_CASE(V) case IrOpcode::k##V:
86 CONTROL_OP_LIST(DEFINE_CONTROL_CASE)
87 #undef DEFINE_CONTROL_CASE
89 // Control nodes that were not control-reachable from end may float.
90 data->placement_ = kSchedulable;
94 data->placement_ = kSchedulable;
98 return data->placement_;
102 void Scheduler::UpdatePlacement(Node* node, Placement placement) {
103 SchedulerData* data = GetData(node);
104 if (data->placement_ != kUnknown) { // Trap on mutation, not initialization.
105 switch (node->opcode()) {
106 case IrOpcode::kParameter:
107 // Parameters are fixed once and for all.
111 case IrOpcode::kEffectPhi: {
112 // Phis and effect phis are coupled to their respective blocks.
113 DCHECK_EQ(Scheduler::kCoupled, data->placement_);
114 DCHECK_EQ(Scheduler::kFixed, placement);
115 Node* control = NodeProperties::GetControlInput(node);
116 BasicBlock* block = schedule_->block(control);
117 schedule_->AddNode(block, node);
120 #define DEFINE_CONTROL_CASE(V) case IrOpcode::k##V:
121 CONTROL_OP_LIST(DEFINE_CONTROL_CASE)
122 #undef DEFINE_CONTROL_CASE
124 // Control nodes force coupled uses to be placed.
125 for (auto use : node->uses()) {
126 if (GetPlacement(use) == Scheduler::kCoupled) {
127 DCHECK_EQ(node, NodeProperties::GetControlInput(use));
128 UpdatePlacement(use, placement);
134 DCHECK_EQ(Scheduler::kSchedulable, data->placement_);
135 DCHECK_EQ(Scheduler::kScheduled, placement);
138 // Reduce the use count of the node's inputs to potentially make them
139 // schedulable. If all the uses of a node have been scheduled, then the node
140 // itself can be scheduled.
141 for (Edge const edge : node->input_edges()) {
142 DecrementUnscheduledUseCount(edge.to(), edge.index(), edge.from());
145 data->placement_ = placement;
149 bool Scheduler::IsCoupledControlEdge(Node* node, int index) {
150 return GetPlacement(node) == kCoupled &&
151 NodeProperties::FirstControlIndex(node) == index;
155 void Scheduler::IncrementUnscheduledUseCount(Node* node, int index,
157 // Make sure that control edges from coupled nodes are not counted.
158 if (IsCoupledControlEdge(from, index)) return;
160 // Tracking use counts for fixed nodes is useless.
161 if (GetPlacement(node) == kFixed) return;
163 // Use count for coupled nodes is summed up on their control.
164 if (GetPlacement(node) == kCoupled) {
165 Node* control = NodeProperties::GetControlInput(node);
166 return IncrementUnscheduledUseCount(control, index, from);
169 ++(GetData(node)->unscheduled_count_);
170 if (FLAG_trace_turbo_scheduler) {
171 TRACE(" Use count of #%d:%s (used by #%d:%s)++ = %d\n", node->id(),
172 node->op()->mnemonic(), from->id(), from->op()->mnemonic(),
173 GetData(node)->unscheduled_count_);
178 void Scheduler::DecrementUnscheduledUseCount(Node* node, int index,
180 // Make sure that control edges from coupled nodes are not counted.
181 if (IsCoupledControlEdge(from, index)) return;
183 // Tracking use counts for fixed nodes is useless.
184 if (GetPlacement(node) == kFixed) return;
186 // Use count for coupled nodes is summed up on their control.
187 if (GetPlacement(node) == kCoupled) {
188 Node* control = NodeProperties::GetControlInput(node);
189 return DecrementUnscheduledUseCount(control, index, from);
192 DCHECK(GetData(node)->unscheduled_count_ > 0);
193 --(GetData(node)->unscheduled_count_);
194 if (FLAG_trace_turbo_scheduler) {
195 TRACE(" Use count of #%d:%s (used by #%d:%s)-- = %d\n", node->id(),
196 node->op()->mnemonic(), from->id(), from->op()->mnemonic(),
197 GetData(node)->unscheduled_count_);
199 if (GetData(node)->unscheduled_count_ == 0) {
200 TRACE(" newly eligible #%d:%s\n", node->id(), node->op()->mnemonic());
201 schedule_queue_.push(node);
206 // -----------------------------------------------------------------------------
207 // Phase 1: Build control-flow graph.
210 // Internal class to build a control flow graph (i.e the basic blocks and edges
211 // between them within a Schedule) from the node graph. Visits control edges of
212 // the graph backwards from an end node in order to find the connected control
213 // subgraph, needed for scheduling.
214 class CFGBuilder : public ZoneObject {
216 CFGBuilder(Zone* zone, Scheduler* scheduler)
218 scheduler_(scheduler),
219 schedule_(scheduler->schedule_),
220 queued_(scheduler->graph_, 2),
223 component_entry_(NULL),
224 component_start_(NULL),
225 component_end_(NULL) {}
227 // Run the control flow graph construction algorithm by walking the graph
228 // backwards from end through control edges, building and connecting the
229 // basic blocks for control nodes.
231 ResetDataStructures();
232 Queue(scheduler_->graph_->end());
234 while (!queue_.empty()) { // Breadth-first backwards traversal.
235 Node* node = queue_.front();
237 int max = NodeProperties::PastControlIndex(node);
238 for (int i = NodeProperties::FirstControlIndex(node); i < max; i++) {
239 Queue(node->InputAt(i));
243 for (NodeVector::iterator i = control_.begin(); i != control_.end(); ++i) {
244 ConnectBlocks(*i); // Connect block to its predecessor/successors.
248 // Run the control flow graph construction for a minimal control-connected
249 // component ending in {exit} and merge that component into an existing
250 // control flow graph at the bottom of {block}.
251 void Run(BasicBlock* block, Node* exit) {
252 ResetDataStructures();
255 component_entry_ = NULL;
256 component_start_ = block;
257 component_end_ = schedule_->block(exit);
258 scheduler_->equivalence_->Run(exit);
259 while (!queue_.empty()) { // Breadth-first backwards traversal.
260 Node* node = queue_.front();
263 // Use control dependence equivalence to find a canonical single-entry
264 // single-exit region that makes up a minimal component to be scheduled.
265 if (IsSingleEntrySingleExitRegion(node, exit)) {
266 TRACE("Found SESE at #%d:%s\n", node->id(), node->op()->mnemonic());
267 DCHECK(!component_entry_);
268 component_entry_ = node;
272 int max = NodeProperties::PastControlIndex(node);
273 for (int i = NodeProperties::FirstControlIndex(node); i < max; i++) {
274 Queue(node->InputAt(i));
277 DCHECK(component_entry_);
279 for (NodeVector::iterator i = control_.begin(); i != control_.end(); ++i) {
280 ConnectBlocks(*i); // Connect block to its predecessor/successors.
285 friend class ScheduleLateNodeVisitor;
286 friend class Scheduler;
288 void FixNode(BasicBlock* block, Node* node) {
289 schedule_->AddNode(block, node);
290 scheduler_->UpdatePlacement(node, Scheduler::kFixed);
293 void Queue(Node* node) {
294 // Mark the connected control nodes as they are queued.
295 if (!queued_.Get(node)) {
298 queued_.Set(node, true);
299 control_.push_back(node);
303 void BuildBlocks(Node* node) {
304 switch (node->opcode()) {
306 FixNode(schedule_->end(), node);
308 case IrOpcode::kStart:
309 FixNode(schedule_->start(), node);
311 case IrOpcode::kLoop:
312 case IrOpcode::kMerge:
313 BuildBlockForNode(node);
315 case IrOpcode::kBranch:
316 case IrOpcode::kSwitch:
317 BuildBlocksForSuccessors(node);
319 case IrOpcode::kCall:
320 if (IsExceptionalCall(node)) {
321 BuildBlocksForSuccessors(node);
329 void ConnectBlocks(Node* node) {
330 switch (node->opcode()) {
331 case IrOpcode::kLoop:
332 case IrOpcode::kMerge:
335 case IrOpcode::kBranch:
336 scheduler_->UpdatePlacement(node, Scheduler::kFixed);
339 case IrOpcode::kSwitch:
340 scheduler_->UpdatePlacement(node, Scheduler::kFixed);
343 case IrOpcode::kDeoptimize:
344 scheduler_->UpdatePlacement(node, Scheduler::kFixed);
345 ConnectDeoptimize(node);
347 case IrOpcode::kReturn:
348 scheduler_->UpdatePlacement(node, Scheduler::kFixed);
351 case IrOpcode::kThrow:
352 scheduler_->UpdatePlacement(node, Scheduler::kFixed);
355 case IrOpcode::kCall:
356 if (IsExceptionalCall(node)) {
357 scheduler_->UpdatePlacement(node, Scheduler::kFixed);
366 BasicBlock* BuildBlockForNode(Node* node) {
367 BasicBlock* block = schedule_->block(node);
369 block = schedule_->NewBasicBlock();
370 TRACE("Create block id:%d for #%d:%s\n", block->id().ToInt(), node->id(),
371 node->op()->mnemonic());
372 FixNode(block, node);
377 void BuildBlocksForSuccessors(Node* node) {
378 size_t const successor_cnt = node->op()->ControlOutputCount();
379 Node** successors = zone_->NewArray<Node*>(successor_cnt);
380 NodeProperties::CollectControlProjections(node, successors, successor_cnt);
381 for (size_t index = 0; index < successor_cnt; ++index) {
382 BuildBlockForNode(successors[index]);
386 void CollectSuccessorBlocks(Node* node, BasicBlock** successor_blocks,
387 size_t successor_cnt) {
388 Node** successors = reinterpret_cast<Node**>(successor_blocks);
389 NodeProperties::CollectControlProjections(node, successors, successor_cnt);
390 for (size_t index = 0; index < successor_cnt; ++index) {
391 successor_blocks[index] = schedule_->block(successors[index]);
395 BasicBlock* FindPredecessorBlock(Node* node) {
396 BasicBlock* predecessor_block = nullptr;
398 predecessor_block = schedule_->block(node);
399 if (predecessor_block != nullptr) break;
400 node = NodeProperties::GetControlInput(node);
402 return predecessor_block;
405 void ConnectCall(Node* call) {
406 BasicBlock* successor_blocks[2];
407 CollectSuccessorBlocks(call, successor_blocks, arraysize(successor_blocks));
409 // Consider the exception continuation to be deferred.
410 successor_blocks[1]->set_deferred(true);
412 Node* call_control = NodeProperties::GetControlInput(call);
413 BasicBlock* call_block = FindPredecessorBlock(call_control);
414 TraceConnect(call, call_block, successor_blocks[0]);
415 TraceConnect(call, call_block, successor_blocks[1]);
416 schedule_->AddCall(call_block, call, successor_blocks[0],
417 successor_blocks[1]);
420 void ConnectBranch(Node* branch) {
421 BasicBlock* successor_blocks[2];
422 CollectSuccessorBlocks(branch, successor_blocks,
423 arraysize(successor_blocks));
425 // Consider branch hints.
426 switch (BranchHintOf(branch->op())) {
427 case BranchHint::kNone:
429 case BranchHint::kTrue:
430 successor_blocks[1]->set_deferred(true);
432 case BranchHint::kFalse:
433 successor_blocks[0]->set_deferred(true);
437 if (branch == component_entry_) {
438 TraceConnect(branch, component_start_, successor_blocks[0]);
439 TraceConnect(branch, component_start_, successor_blocks[1]);
440 schedule_->InsertBranch(component_start_, component_end_, branch,
441 successor_blocks[0], successor_blocks[1]);
443 Node* branch_control = NodeProperties::GetControlInput(branch);
444 BasicBlock* branch_block = FindPredecessorBlock(branch_control);
445 TraceConnect(branch, branch_block, successor_blocks[0]);
446 TraceConnect(branch, branch_block, successor_blocks[1]);
447 schedule_->AddBranch(branch_block, branch, successor_blocks[0],
448 successor_blocks[1]);
452 void ConnectSwitch(Node* sw) {
453 size_t const successor_count = sw->op()->ControlOutputCount();
454 BasicBlock** successor_blocks =
455 zone_->NewArray<BasicBlock*>(successor_count);
456 CollectSuccessorBlocks(sw, successor_blocks, successor_count);
458 if (sw == component_entry_) {
459 for (size_t index = 0; index < successor_count; ++index) {
460 TraceConnect(sw, component_start_, successor_blocks[index]);
462 schedule_->InsertSwitch(component_start_, component_end_, sw,
463 successor_blocks, successor_count);
465 Node* switch_control = NodeProperties::GetControlInput(sw);
466 BasicBlock* switch_block = FindPredecessorBlock(switch_control);
467 for (size_t index = 0; index < successor_count; ++index) {
468 TraceConnect(sw, switch_block, successor_blocks[index]);
470 schedule_->AddSwitch(switch_block, sw, successor_blocks, successor_count);
474 void ConnectMerge(Node* merge) {
475 // Don't connect the special merge at the end to its predecessors.
476 if (IsFinalMerge(merge)) return;
478 BasicBlock* block = schedule_->block(merge);
479 DCHECK_NOT_NULL(block);
480 // For all of the merge's control inputs, add a goto at the end to the
481 // merge's basic block.
482 for (Node* const input : merge->inputs()) {
483 BasicBlock* predecessor_block = FindPredecessorBlock(input);
484 TraceConnect(merge, predecessor_block, block);
485 schedule_->AddGoto(predecessor_block, block);
489 void ConnectReturn(Node* ret) {
490 Node* return_control = NodeProperties::GetControlInput(ret);
491 BasicBlock* return_block = FindPredecessorBlock(return_control);
492 TraceConnect(ret, return_block, NULL);
493 schedule_->AddReturn(return_block, ret);
496 void ConnectDeoptimize(Node* deopt) {
497 Node* deoptimize_control = NodeProperties::GetControlInput(deopt);
498 BasicBlock* deoptimize_block = FindPredecessorBlock(deoptimize_control);
499 TraceConnect(deopt, deoptimize_block, NULL);
500 schedule_->AddDeoptimize(deoptimize_block, deopt);
503 void ConnectThrow(Node* thr) {
504 Node* throw_control = NodeProperties::GetControlInput(thr);
505 BasicBlock* throw_block = FindPredecessorBlock(throw_control);
506 TraceConnect(thr, throw_block, NULL);
507 schedule_->AddThrow(throw_block, thr);
510 void TraceConnect(Node* node, BasicBlock* block, BasicBlock* succ) {
511 DCHECK_NOT_NULL(block);
513 TRACE("Connect #%d:%s, id:%d -> end\n", node->id(),
514 node->op()->mnemonic(), block->id().ToInt());
516 TRACE("Connect #%d:%s, id:%d -> id:%d\n", node->id(),
517 node->op()->mnemonic(), block->id().ToInt(), succ->id().ToInt());
521 bool IsExceptionalCall(Node* node) {
522 for (Node* const use : node->uses()) {
523 if (use->opcode() == IrOpcode::kIfException) return true;
528 bool IsFinalMerge(Node* node) {
529 return (node->opcode() == IrOpcode::kMerge &&
530 node == scheduler_->graph_->end()->InputAt(0));
533 bool IsSingleEntrySingleExitRegion(Node* entry, Node* exit) const {
534 size_t entry_class = scheduler_->equivalence_->ClassOf(entry);
535 size_t exit_class = scheduler_->equivalence_->ClassOf(exit);
536 return entry != exit && entry_class == exit_class;
539 void ResetDataStructures() {
541 DCHECK(queue_.empty());
542 DCHECK(control_.empty());
546 Scheduler* scheduler_;
548 NodeMarker<bool> queued_; // Mark indicating whether node is queued.
549 ZoneQueue<Node*> queue_; // Queue used for breadth-first traversal.
550 NodeVector control_; // List of encountered control nodes.
551 Node* component_entry_; // Component single-entry node.
552 BasicBlock* component_start_; // Component single-entry block.
553 BasicBlock* component_end_; // Component single-exit block.
557 void Scheduler::BuildCFG() {
558 TRACE("--- CREATING CFG -------------------------------------------\n");
560 // Instantiate a new control equivalence algorithm for the graph.
561 equivalence_ = new (zone_) ControlEquivalence(zone_, graph_);
563 // Build a control-flow graph for the main control-connected component that
564 // is being spanned by the graph's start and end nodes.
565 control_flow_builder_ = new (zone_) CFGBuilder(zone_, this);
566 control_flow_builder_->Run();
568 // Initialize per-block data.
569 scheduled_nodes_.resize(schedule_->BasicBlockCount(), NodeVector(zone_));
573 // -----------------------------------------------------------------------------
574 // Phase 2: Compute special RPO and dominator tree.
577 // Compute the special reverse-post-order block ordering, which is essentially
578 // a RPO of the graph where loop bodies are contiguous. Properties:
579 // 1. If block A is a predecessor of B, then A appears before B in the order,
580 // unless B is a loop header and A is in the loop headed at B
581 // (i.e. A -> B is a backedge).
582 // => If block A dominates block B, then A appears before B in the order.
583 // => If block A is a loop header, A appears before all blocks in the loop
585 // 2. All loops are contiguous in the order (i.e. no intervening blocks that
586 // do not belong to the loop.)
587 // Note a simple RPO traversal satisfies (1) but not (2).
588 class SpecialRPONumberer : public ZoneObject {
590 SpecialRPONumberer(Zone* zone, Schedule* schedule)
598 previous_block_count_(0),
601 // Computes the special reverse-post-order for the main control flow graph,
602 // that is for the graph spanned between the schedule's start and end blocks.
603 void ComputeSpecialRPO() {
604 DCHECK(schedule_->end()->SuccessorCount() == 0);
605 DCHECK(!order_); // Main order does not exist yet.
606 ComputeAndInsertSpecialRPO(schedule_->start(), schedule_->end());
609 // Computes the special reverse-post-order for a partial control flow graph,
610 // that is for the graph spanned between the given {entry} and {end} blocks,
611 // then updates the existing ordering with this new information.
612 void UpdateSpecialRPO(BasicBlock* entry, BasicBlock* end) {
613 DCHECK(order_); // Main order to be updated is present.
614 ComputeAndInsertSpecialRPO(entry, end);
617 // Serialize the previously computed order as a special reverse-post-order
618 // numbering for basic blocks into the final schedule.
619 void SerializeRPOIntoSchedule() {
621 for (BasicBlock* b = order_; b != NULL; b = b->rpo_next()) {
622 b->set_rpo_number(number++);
623 schedule_->rpo_order()->push_back(b);
625 BeyondEndSentinel()->set_rpo_number(number);
628 // Print and verify the special reverse-post-order.
629 void PrintAndVerifySpecialRPO() {
631 if (FLAG_trace_turbo_scheduler) PrintRPO();
636 const ZoneList<BasicBlock*>& GetOutgoingBlocks(BasicBlock* block) {
637 if (HasLoopNumber(block)) {
638 LoopInfo const& loop = loops_[GetLoopNumber(block)];
639 if (loop.outgoing) return *loop.outgoing;
645 typedef std::pair<BasicBlock*, size_t> Backedge;
647 // Numbering for BasicBlock::rpo_number for this block traversal:
648 static const int kBlockOnStack = -2;
649 static const int kBlockVisited1 = -3;
650 static const int kBlockVisited2 = -4;
651 static const int kBlockUnvisited1 = -1;
652 static const int kBlockUnvisited2 = kBlockVisited1;
654 struct SpecialRPOStackFrame {
661 ZoneList<BasicBlock*>* outgoing;
667 void AddOutgoing(Zone* zone, BasicBlock* block) {
668 if (outgoing == NULL) {
669 outgoing = new (zone) ZoneList<BasicBlock*>(2, zone);
671 outgoing->Add(block, zone);
675 int Push(ZoneVector<SpecialRPOStackFrame>& stack, int depth,
676 BasicBlock* child, int unvisited) {
677 if (child->rpo_number() == unvisited) {
678 stack[depth].block = child;
679 stack[depth].index = 0;
680 child->set_rpo_number(kBlockOnStack);
686 BasicBlock* PushFront(BasicBlock* head, BasicBlock* block) {
687 block->set_rpo_next(head);
691 static int GetLoopNumber(BasicBlock* block) { return block->loop_number(); }
692 static void SetLoopNumber(BasicBlock* block, int loop_number) {
693 return block->set_loop_number(loop_number);
695 static bool HasLoopNumber(BasicBlock* block) {
696 return block->loop_number() >= 0;
699 // TODO(mstarzinger): We only need this special sentinel because some tests
700 // use the schedule's end block in actual control flow (e.g. with end having
701 // successors). Once this has been cleaned up we can use the end block here.
702 BasicBlock* BeyondEndSentinel() {
703 if (beyond_end_ == NULL) {
704 BasicBlock::Id id = BasicBlock::Id::FromInt(-1);
705 beyond_end_ = new (schedule_->zone()) BasicBlock(schedule_->zone(), id);
710 // Compute special RPO for the control flow graph between {entry} and {end},
711 // mutating any existing order so that the result is still valid.
712 void ComputeAndInsertSpecialRPO(BasicBlock* entry, BasicBlock* end) {
713 // RPO should not have been serialized for this schedule yet.
714 CHECK_EQ(kBlockUnvisited1, schedule_->start()->loop_number());
715 CHECK_EQ(kBlockUnvisited1, schedule_->start()->rpo_number());
716 CHECK_EQ(0, static_cast<int>(schedule_->rpo_order()->size()));
718 // Find correct insertion point within existing order.
719 BasicBlock* insertion_point = entry->rpo_next();
720 BasicBlock* order = insertion_point;
722 // Perform an iterative RPO traversal using an explicit stack,
723 // recording backedges that form cycles. O(|B|).
724 DCHECK_LT(previous_block_count_, schedule_->BasicBlockCount());
725 stack_.resize(schedule_->BasicBlockCount() - previous_block_count_);
726 previous_block_count_ = schedule_->BasicBlockCount();
727 int stack_depth = Push(stack_, 0, entry, kBlockUnvisited1);
728 int num_loops = static_cast<int>(loops_.size());
730 while (stack_depth > 0) {
731 int current = stack_depth - 1;
732 SpecialRPOStackFrame* frame = &stack_[current];
734 if (frame->block != end &&
735 frame->index < frame->block->SuccessorCount()) {
736 // Process the next successor.
737 BasicBlock* succ = frame->block->SuccessorAt(frame->index++);
738 if (succ->rpo_number() == kBlockVisited1) continue;
739 if (succ->rpo_number() == kBlockOnStack) {
740 // The successor is on the stack, so this is a backedge (cycle).
741 backedges_.push_back(Backedge(frame->block, frame->index - 1));
742 if (!HasLoopNumber(succ)) {
743 // Assign a new loop number to the header if it doesn't have one.
744 SetLoopNumber(succ, num_loops++);
747 // Push the successor onto the stack.
748 DCHECK(succ->rpo_number() == kBlockUnvisited1);
749 stack_depth = Push(stack_, stack_depth, succ, kBlockUnvisited1);
752 // Finished with all successors; pop the stack and add the block.
753 order = PushFront(order, frame->block);
754 frame->block->set_rpo_number(kBlockVisited1);
759 // If no loops were encountered, then the order we computed was correct.
760 if (num_loops > static_cast<int>(loops_.size())) {
761 // Otherwise, compute the loop information from the backedges in order
762 // to perform a traversal that groups loop bodies together.
763 ComputeLoopInfo(stack_, num_loops, &backedges_);
765 // Initialize the "loop stack". Note the entry could be a loop header.
767 HasLoopNumber(entry) ? &loops_[GetLoopNumber(entry)] : NULL;
768 order = insertion_point;
770 // Perform an iterative post-order traversal, visiting loop bodies before
771 // edges that lead out of loops. Visits each block once, but linking loop
772 // sections together is linear in the loop size, so overall is
773 // O(|B| + max(loop_depth) * max(|loop|))
774 stack_depth = Push(stack_, 0, entry, kBlockUnvisited2);
775 while (stack_depth > 0) {
776 SpecialRPOStackFrame* frame = &stack_[stack_depth - 1];
777 BasicBlock* block = frame->block;
778 BasicBlock* succ = NULL;
780 if (block != end && frame->index < block->SuccessorCount()) {
781 // Process the next normal successor.
782 succ = block->SuccessorAt(frame->index++);
783 } else if (HasLoopNumber(block)) {
784 // Process additional outgoing edges from the loop header.
785 if (block->rpo_number() == kBlockOnStack) {
786 // Finish the loop body the first time the header is left on the
788 DCHECK(loop != NULL && loop->header == block);
789 loop->start = PushFront(order, block);
791 block->set_rpo_number(kBlockVisited2);
792 // Pop the loop stack and continue visiting outgoing edges within
793 // the context of the outer loop, if any.
795 // We leave the loop header on the stack; the rest of this iteration
796 // and later iterations will go through its outgoing edges list.
799 // Use the next outgoing edge if there are any.
801 static_cast<int>(frame->index - block->SuccessorCount());
802 LoopInfo* info = &loops_[GetLoopNumber(block)];
803 DCHECK(loop != info);
804 if (block != entry && info->outgoing != NULL &&
805 outgoing_index < info->outgoing->length()) {
806 succ = info->outgoing->at(outgoing_index);
812 // Process the next successor.
813 if (succ->rpo_number() == kBlockOnStack) continue;
814 if (succ->rpo_number() == kBlockVisited2) continue;
815 DCHECK(succ->rpo_number() == kBlockUnvisited2);
816 if (loop != NULL && !loop->members->Contains(succ->id().ToInt())) {
817 // The successor is not in the current loop or any nested loop.
818 // Add it to the outgoing edges of this loop and visit it later.
819 loop->AddOutgoing(zone_, succ);
821 // Push the successor onto the stack.
822 stack_depth = Push(stack_, stack_depth, succ, kBlockUnvisited2);
823 if (HasLoopNumber(succ)) {
824 // Push the inner loop onto the loop stack.
825 DCHECK(GetLoopNumber(succ) < num_loops);
826 LoopInfo* next = &loops_[GetLoopNumber(succ)];
833 // Finished with all successors of the current block.
834 if (HasLoopNumber(block)) {
835 // If we are going to pop a loop header, then add its entire body.
836 LoopInfo* info = &loops_[GetLoopNumber(block)];
837 for (BasicBlock* b = info->start; true; b = b->rpo_next()) {
838 if (b->rpo_next() == info->end) {
839 b->set_rpo_next(order);
846 // Pop a single node off the stack and add it to the order.
847 order = PushFront(order, block);
848 block->set_rpo_number(kBlockVisited2);
855 // Publish new order the first time.
856 if (order_ == NULL) order_ = order;
858 // Compute the correct loop headers and set the correct loop ends.
859 LoopInfo* current_loop = NULL;
860 BasicBlock* current_header = entry->loop_header();
861 int32_t loop_depth = entry->loop_depth();
862 if (entry->IsLoopHeader()) --loop_depth; // Entry might be a loop header.
863 for (BasicBlock* b = order; b != insertion_point; b = b->rpo_next()) {
864 BasicBlock* current = b;
866 // Reset BasicBlock::rpo_number again.
867 current->set_rpo_number(kBlockUnvisited1);
869 // Finish the previous loop(s) if we just exited them.
870 while (current_header != NULL && current == current_header->loop_end()) {
871 DCHECK(current_header->IsLoopHeader());
872 DCHECK(current_loop != NULL);
873 current_loop = current_loop->prev;
874 current_header = current_loop == NULL ? NULL : current_loop->header;
877 current->set_loop_header(current_header);
879 // Push a new loop onto the stack if this loop is a loop header.
880 if (HasLoopNumber(current)) {
882 current_loop = &loops_[GetLoopNumber(current)];
883 BasicBlock* end = current_loop->end;
884 current->set_loop_end(end == NULL ? BeyondEndSentinel() : end);
885 current_header = current_loop->header;
886 TRACE("id:%d is a loop header, increment loop depth to %d\n",
887 current->id().ToInt(), loop_depth);
890 current->set_loop_depth(loop_depth);
892 if (current->loop_header() == NULL) {
893 TRACE("id:%d is not in a loop (depth == %d)\n", current->id().ToInt(),
894 current->loop_depth());
896 TRACE("id:%d has loop header id:%d, (depth == %d)\n",
897 current->id().ToInt(), current->loop_header()->id().ToInt(),
898 current->loop_depth());
903 // Computes loop membership from the backedges of the control flow graph.
904 void ComputeLoopInfo(ZoneVector<SpecialRPOStackFrame>& queue,
905 size_t num_loops, ZoneVector<Backedge>* backedges) {
906 // Extend existing loop membership vectors.
907 for (LoopInfo& loop : loops_) {
908 BitVector* new_members = new (zone_)
909 BitVector(static_cast<int>(schedule_->BasicBlockCount()), zone_);
910 new_members->CopyFrom(*loop.members);
911 loop.members = new_members;
914 // Extend loop information vector.
915 loops_.resize(num_loops, LoopInfo());
917 // Compute loop membership starting from backedges.
918 // O(max(loop_depth) * max(|loop|)
919 for (size_t i = 0; i < backedges->size(); i++) {
920 BasicBlock* member = backedges->at(i).first;
921 BasicBlock* header = member->SuccessorAt(backedges->at(i).second);
922 size_t loop_num = GetLoopNumber(header);
923 if (loops_[loop_num].header == NULL) {
924 loops_[loop_num].header = header;
925 loops_[loop_num].members = new (zone_)
926 BitVector(static_cast<int>(schedule_->BasicBlockCount()), zone_);
929 int queue_length = 0;
930 if (member != header) {
931 // As long as the header doesn't have a backedge to itself,
932 // Push the member onto the queue and process its predecessors.
933 if (!loops_[loop_num].members->Contains(member->id().ToInt())) {
934 loops_[loop_num].members->Add(member->id().ToInt());
936 queue[queue_length++].block = member;
939 // Propagate loop membership backwards. All predecessors of M up to the
940 // loop header H are members of the loop too. O(|blocks between M and H|).
941 while (queue_length > 0) {
942 BasicBlock* block = queue[--queue_length].block;
943 for (size_t i = 0; i < block->PredecessorCount(); i++) {
944 BasicBlock* pred = block->PredecessorAt(i);
945 if (pred != header) {
946 if (!loops_[loop_num].members->Contains(pred->id().ToInt())) {
947 loops_[loop_num].members->Add(pred->id().ToInt());
948 queue[queue_length++].block = pred;
959 os << "RPO with " << loops_.size() << " loops";
960 if (loops_.size() > 0) {
962 for (size_t i = 0; i < loops_.size(); i++) {
963 if (i > 0) os << " ";
964 os << "id:" << loops_[i].header->id();
970 for (BasicBlock* block = order_; block != NULL; block = block->rpo_next()) {
971 os << std::setw(5) << "B" << block->rpo_number() << ":";
972 for (size_t i = 0; i < loops_.size(); i++) {
973 bool range = loops_[i].header->LoopContains(block);
974 bool membership = loops_[i].header != block && range;
975 os << (membership ? " |" : " ");
976 os << (range ? "x" : " ");
978 os << " id:" << block->id() << ": ";
979 if (block->loop_end() != NULL) {
980 os << " range: [B" << block->rpo_number() << ", B"
981 << block->loop_end()->rpo_number() << ")";
983 if (block->loop_header() != NULL) {
984 os << " header: id:" << block->loop_header()->id();
986 if (block->loop_depth() > 0) {
987 os << " depth: " << block->loop_depth();
993 void VerifySpecialRPO() {
994 BasicBlockVector* order = schedule_->rpo_order();
995 DCHECK(order->size() > 0);
996 DCHECK((*order)[0]->id().ToInt() == 0); // entry should be first.
998 for (size_t i = 0; i < loops_.size(); i++) {
999 LoopInfo* loop = &loops_[i];
1000 BasicBlock* header = loop->header;
1001 BasicBlock* end = header->loop_end();
1003 DCHECK(header != NULL);
1004 DCHECK(header->rpo_number() >= 0);
1005 DCHECK(header->rpo_number() < static_cast<int>(order->size()));
1006 DCHECK(end != NULL);
1007 DCHECK(end->rpo_number() <= static_cast<int>(order->size()));
1008 DCHECK(end->rpo_number() > header->rpo_number());
1009 DCHECK(header->loop_header() != header);
1011 // Verify the start ... end list relationship.
1013 BasicBlock* block = loop->start;
1014 DCHECK_EQ(header, block);
1017 if (block == NULL || block == loop->end) {
1018 end_found = (loop->end == block);
1021 // The list should be in same order as the final result.
1022 DCHECK(block->rpo_number() == links + header->rpo_number());
1024 block = block->rpo_next();
1025 DCHECK_LT(links, static_cast<int>(2 * order->size())); // cycle?
1028 DCHECK(links == end->rpo_number() - header->rpo_number());
1031 // Check loop depth of the header.
1033 for (LoopInfo* outer = loop; outer != NULL; outer = outer->prev) {
1036 DCHECK_EQ(loop_depth, header->loop_depth());
1038 // Check the contiguousness of loops.
1040 for (int j = 0; j < static_cast<int>(order->size()); j++) {
1041 BasicBlock* block = order->at(j);
1042 DCHECK(block->rpo_number() == j);
1043 if (j < header->rpo_number() || j >= end->rpo_number()) {
1044 DCHECK(!header->LoopContains(block));
1046 DCHECK(header->LoopContains(block));
1047 DCHECK_GE(block->loop_depth(), loop_depth);
1051 DCHECK(links == count);
1057 Schedule* schedule_;
1059 BasicBlock* beyond_end_;
1060 ZoneVector<LoopInfo> loops_;
1061 ZoneVector<Backedge> backedges_;
1062 ZoneVector<SpecialRPOStackFrame> stack_;
1063 size_t previous_block_count_;
1064 ZoneList<BasicBlock*> const empty_;
1068 BasicBlockVector* Scheduler::ComputeSpecialRPO(Zone* zone, Schedule* schedule) {
1069 SpecialRPONumberer numberer(zone, schedule);
1070 numberer.ComputeSpecialRPO();
1071 numberer.SerializeRPOIntoSchedule();
1072 numberer.PrintAndVerifySpecialRPO();
1073 return schedule->rpo_order();
1077 void Scheduler::ComputeSpecialRPONumbering() {
1078 TRACE("--- COMPUTING SPECIAL RPO ----------------------------------\n");
1080 // Compute the special reverse-post-order for basic blocks.
1081 special_rpo_ = new (zone_) SpecialRPONumberer(zone_, schedule_);
1082 special_rpo_->ComputeSpecialRPO();
1086 void Scheduler::PropagateImmediateDominators(BasicBlock* block) {
1087 for (/*nop*/; block != NULL; block = block->rpo_next()) {
1088 auto pred = block->predecessors().begin();
1089 auto end = block->predecessors().end();
1090 DCHECK(pred != end); // All blocks except start have predecessors.
1091 BasicBlock* dominator = *pred;
1092 bool deferred = dominator->deferred();
1093 // For multiple predecessors, walk up the dominator tree until a common
1094 // dominator is found. Visitation order guarantees that all predecessors
1095 // except for backwards edges have been visited.
1096 for (++pred; pred != end; ++pred) {
1097 // Don't examine backwards edges.
1098 if ((*pred)->dominator_depth() < 0) continue;
1099 dominator = BasicBlock::GetCommonDominator(dominator, *pred);
1100 deferred = deferred & (*pred)->deferred();
1102 block->set_dominator(dominator);
1103 block->set_dominator_depth(dominator->dominator_depth() + 1);
1104 block->set_deferred(deferred | block->deferred());
1105 TRACE("Block id:%d's idom is id:%d, depth = %d\n", block->id().ToInt(),
1106 dominator->id().ToInt(), block->dominator_depth());
1111 void Scheduler::GenerateImmediateDominatorTree() {
1112 TRACE("--- IMMEDIATE BLOCK DOMINATORS -----------------------------\n");
1114 // Seed start block to be the first dominator.
1115 schedule_->start()->set_dominator_depth(0);
1117 // Build the block dominator tree resulting from the above seed.
1118 PropagateImmediateDominators(schedule_->start()->rpo_next());
1122 // -----------------------------------------------------------------------------
1123 // Phase 3: Prepare use counts for nodes.
1126 class PrepareUsesVisitor {
1128 explicit PrepareUsesVisitor(Scheduler* scheduler)
1129 : scheduler_(scheduler), schedule_(scheduler->schedule_) {}
1131 void Pre(Node* node) {
1132 if (scheduler_->GetPlacement(node) == Scheduler::kFixed) {
1133 // Fixed nodes are always roots for schedule late.
1134 scheduler_->schedule_root_nodes_.push_back(node);
1135 if (!schedule_->IsScheduled(node)) {
1136 // Make sure root nodes are scheduled in their respective blocks.
1137 TRACE("Scheduling fixed position node #%d:%s\n", node->id(),
1138 node->op()->mnemonic());
1139 IrOpcode::Value opcode = node->opcode();
1141 opcode == IrOpcode::kParameter
1142 ? schedule_->start()
1143 : schedule_->block(NodeProperties::GetControlInput(node));
1144 DCHECK(block != NULL);
1145 schedule_->AddNode(block, node);
1150 void PostEdge(Node* from, int index, Node* to) {
1151 // If the edge is from an unscheduled node, then tally it in the use count
1152 // for all of its inputs. The same criterion will be used in ScheduleLate
1153 // for decrementing use counts.
1154 if (!schedule_->IsScheduled(from)) {
1155 DCHECK_NE(Scheduler::kFixed, scheduler_->GetPlacement(from));
1156 scheduler_->IncrementUnscheduledUseCount(to, index, from);
1161 Scheduler* scheduler_;
1162 Schedule* schedule_;
1166 void Scheduler::PrepareUses() {
1167 TRACE("--- PREPARE USES -------------------------------------------\n");
1169 // Count the uses of every node, which is used to ensure that all of a
1170 // node's uses are scheduled before the node itself.
1171 PrepareUsesVisitor prepare_uses(this);
1173 // TODO(turbofan): simplify the careful pre/post ordering here.
1174 BoolVector visited(graph_->NodeCount(), false, zone_);
1175 ZoneStack<Node::InputEdges::iterator> stack(zone_);
1176 Node* node = graph_->end();
1177 prepare_uses.Pre(node);
1178 visited[node->id()] = true;
1179 stack.push(node->input_edges().begin());
1180 while (!stack.empty()) {
1181 Edge edge = *stack.top();
1182 Node* node = edge.to();
1183 if (visited[node->id()]) {
1184 prepare_uses.PostEdge(edge.from(), edge.index(), edge.to());
1185 if (++stack.top() == edge.from()->input_edges().end()) stack.pop();
1187 prepare_uses.Pre(node);
1188 visited[node->id()] = true;
1189 if (node->InputCount() > 0) stack.push(node->input_edges().begin());
1195 // -----------------------------------------------------------------------------
1196 // Phase 4: Schedule nodes early.
1199 class ScheduleEarlyNodeVisitor {
1201 ScheduleEarlyNodeVisitor(Zone* zone, Scheduler* scheduler)
1202 : scheduler_(scheduler), schedule_(scheduler->schedule_), queue_(zone) {}
1204 // Run the schedule early algorithm on a set of fixed root nodes.
1205 void Run(NodeVector* roots) {
1206 for (Node* const root : *roots) {
1208 while (!queue_.empty()) {
1209 VisitNode(queue_.front());
1216 // Visits one node from the queue and propagates its current schedule early
1217 // position to all uses. This in turn might push more nodes onto the queue.
1218 void VisitNode(Node* node) {
1219 Scheduler::SchedulerData* data = scheduler_->GetData(node);
1221 // Fixed nodes already know their schedule early position.
1222 if (scheduler_->GetPlacement(node) == Scheduler::kFixed) {
1223 data->minimum_block_ = schedule_->block(node);
1224 TRACE("Fixing #%d:%s minimum_block = id:%d, dominator_depth = %d\n",
1225 node->id(), node->op()->mnemonic(),
1226 data->minimum_block_->id().ToInt(),
1227 data->minimum_block_->dominator_depth());
1230 // No need to propagate unconstrained schedule early positions.
1231 if (data->minimum_block_ == schedule_->start()) return;
1233 // Propagate schedule early position.
1234 DCHECK(data->minimum_block_ != NULL);
1235 for (auto use : node->uses()) {
1236 PropagateMinimumPositionToNode(data->minimum_block_, use);
1240 // Propagates {block} as another minimum position into the given {node}. This
1241 // has the net effect of computing the minimum dominator block of {node} that
1242 // still post-dominates all inputs to {node} when the queue is processed.
1243 void PropagateMinimumPositionToNode(BasicBlock* block, Node* node) {
1244 Scheduler::SchedulerData* data = scheduler_->GetData(node);
1246 // No need to propagate to fixed node, it's guaranteed to be a root.
1247 if (scheduler_->GetPlacement(node) == Scheduler::kFixed) return;
1249 // Coupled nodes influence schedule early position of their control.
1250 if (scheduler_->GetPlacement(node) == Scheduler::kCoupled) {
1251 Node* control = NodeProperties::GetControlInput(node);
1252 PropagateMinimumPositionToNode(block, control);
1255 // Propagate new position if it is deeper down the dominator tree than the
1256 // current. Note that all inputs need to have minimum block position inside
1257 // the dominator chain of {node}'s minimum block position.
1258 DCHECK(InsideSameDominatorChain(block, data->minimum_block_));
1259 if (block->dominator_depth() > data->minimum_block_->dominator_depth()) {
1260 data->minimum_block_ = block;
1262 TRACE("Propagating #%d:%s minimum_block = id:%d, dominator_depth = %d\n",
1263 node->id(), node->op()->mnemonic(),
1264 data->minimum_block_->id().ToInt(),
1265 data->minimum_block_->dominator_depth());
1270 bool InsideSameDominatorChain(BasicBlock* b1, BasicBlock* b2) {
1271 BasicBlock* dominator = BasicBlock::GetCommonDominator(b1, b2);
1272 return dominator == b1 || dominator == b2;
1276 Scheduler* scheduler_;
1277 Schedule* schedule_;
1278 ZoneQueue<Node*> queue_;
1282 void Scheduler::ScheduleEarly() {
1283 TRACE("--- SCHEDULE EARLY -----------------------------------------\n");
1284 if (FLAG_trace_turbo_scheduler) {
1286 for (Node* node : schedule_root_nodes_) {
1287 TRACE("#%d:%s ", node->id(), node->op()->mnemonic());
1292 // Compute the minimum block for each node thereby determining the earliest
1293 // position each node could be placed within a valid schedule.
1294 ScheduleEarlyNodeVisitor schedule_early_visitor(zone_, this);
1295 schedule_early_visitor.Run(&schedule_root_nodes_);
1299 // -----------------------------------------------------------------------------
1300 // Phase 5: Schedule nodes late.
1303 class ScheduleLateNodeVisitor {
1305 ScheduleLateNodeVisitor(Zone* zone, Scheduler* scheduler)
1306 : scheduler_(scheduler),
1307 schedule_(scheduler_->schedule_),
1308 marked_(scheduler->zone_),
1309 marking_queue_(scheduler->zone_) {}
1311 // Run the schedule late algorithm on a set of fixed root nodes.
1312 void Run(NodeVector* roots) {
1313 for (Node* const root : *roots) {
1319 void ProcessQueue(Node* root) {
1320 ZoneQueue<Node*>* queue = &(scheduler_->schedule_queue_);
1321 for (Node* node : root->inputs()) {
1322 // Don't schedule coupled nodes on their own.
1323 if (scheduler_->GetPlacement(node) == Scheduler::kCoupled) {
1324 node = NodeProperties::GetControlInput(node);
1327 // Test schedulability condition by looking at unscheduled use count.
1328 if (scheduler_->GetData(node)->unscheduled_count_ != 0) continue;
1332 Node* const node = queue->front();
1335 } while (!queue->empty());
1339 // Visits one node from the queue of schedulable nodes and determines its
1340 // schedule late position. Also hoists nodes out of loops to find a more
1341 // optimal scheduling position.
1342 void VisitNode(Node* node) {
1343 DCHECK_EQ(0, scheduler_->GetData(node)->unscheduled_count_);
1345 // Don't schedule nodes that are already scheduled.
1346 if (schedule_->IsScheduled(node)) return;
1347 DCHECK_EQ(Scheduler::kSchedulable, scheduler_->GetPlacement(node));
1349 // Determine the dominating block for all of the uses of this node. It is
1350 // the latest block that this node can be scheduled in.
1351 TRACE("Scheduling #%d:%s\n", node->id(), node->op()->mnemonic());
1352 BasicBlock* block = GetCommonDominatorOfUses(node);
1353 DCHECK_NOT_NULL(block);
1355 // The schedule early block dominates the schedule late block.
1356 BasicBlock* min_block = scheduler_->GetData(node)->minimum_block_;
1357 DCHECK_EQ(min_block, BasicBlock::GetCommonDominator(block, min_block));
1359 "Schedule late of #%d:%s is id:%d at loop depth %d, minimum = id:%d\n",
1360 node->id(), node->op()->mnemonic(), block->id().ToInt(),
1361 block->loop_depth(), min_block->id().ToInt());
1363 // Hoist nodes out of loops if possible. Nodes can be hoisted iteratively
1364 // into enclosing loop pre-headers until they would preceed their schedule
1366 BasicBlock* hoist_block = GetHoistBlock(block);
1368 hoist_block->dominator_depth() >= min_block->dominator_depth()) {
1370 TRACE(" hoisting #%d:%s to block id:%d\n", node->id(),
1371 node->op()->mnemonic(), hoist_block->id().ToInt());
1372 DCHECK_LT(hoist_block->loop_depth(), block->loop_depth());
1373 block = hoist_block;
1374 hoist_block = GetHoistBlock(hoist_block);
1375 } while (hoist_block &&
1376 hoist_block->dominator_depth() >= min_block->dominator_depth());
1377 } else if (scheduler_->flags_ & Scheduler::kSplitNodes) {
1378 // Split the {node} if beneficial and return the new {block} for it.
1379 block = SplitNode(block, node);
1382 // Schedule the node or a floating control structure.
1383 if (IrOpcode::IsMergeOpcode(node->opcode())) {
1384 ScheduleFloatingControl(block, node);
1386 ScheduleNode(block, node);
1390 // Mark {block} and push its non-marked predecessor on the marking queue.
1391 void MarkBlock(BasicBlock* block) {
1392 DCHECK_LT(block->id().ToSize(), marked_.size());
1393 marked_[block->id().ToSize()] = true;
1394 for (BasicBlock* pred_block : block->predecessors()) {
1395 DCHECK_LT(pred_block->id().ToSize(), marked_.size());
1396 if (marked_[pred_block->id().ToSize()]) continue;
1397 marking_queue_.push_back(pred_block);
1401 BasicBlock* SplitNode(BasicBlock* block, Node* node) {
1402 // For now, we limit splitting to pure nodes.
1403 if (!node->op()->HasProperty(Operator::kPure)) return block;
1404 // TODO(titzer): fix the special case of splitting of projections.
1405 if (node->opcode() == IrOpcode::kProjection) return block;
1407 // The {block} is common dominator of all uses of {node}, so we cannot
1408 // split anything unless the {block} has at least two successors.
1409 DCHECK_EQ(block, GetCommonDominatorOfUses(node));
1410 if (block->SuccessorCount() < 2) return block;
1412 // Clear marking bits.
1413 DCHECK(marking_queue_.empty());
1414 std::fill(marked_.begin(), marked_.end(), false);
1415 marked_.resize(schedule_->BasicBlockCount() + 1, false);
1417 // Check if the {node} has uses in {block}.
1418 for (Edge edge : node->use_edges()) {
1419 BasicBlock* use_block = GetBlockForUse(edge);
1420 if (use_block == nullptr || marked_[use_block->id().ToSize()]) continue;
1421 if (use_block == block) {
1422 TRACE(" not splitting #%d:%s, it is used in id:%d\n", node->id(),
1423 node->op()->mnemonic(), block->id().ToInt());
1424 marking_queue_.clear();
1427 MarkBlock(use_block);
1430 // Compute transitive marking closure; a block is marked if all its
1431 // successors are marked.
1433 BasicBlock* top_block = marking_queue_.front();
1434 marking_queue_.pop_front();
1435 if (marked_[top_block->id().ToSize()]) continue;
1437 for (BasicBlock* successor : top_block->successors()) {
1438 if (!marked_[successor->id().ToSize()]) {
1443 if (marked) MarkBlock(top_block);
1444 } while (!marking_queue_.empty());
1446 // If the (common dominator) {block} is marked, we know that all paths from
1447 // {block} to the end contain at least one use of {node}, and hence there's
1448 // no point in splitting the {node} in this case.
1449 if (marked_[block->id().ToSize()]) {
1450 TRACE(" not splitting #%d:%s, its common dominator id:%d is perfect\n",
1451 node->id(), node->op()->mnemonic(), block->id().ToInt());
1455 // Split {node} for uses according to the previously computed marking
1456 // closure. Every marking partition has a unique dominator, which get's a
1457 // copy of the {node} with the exception of the first partition, which get's
1458 // the {node} itself.
1459 ZoneMap<BasicBlock*, Node*> dominators(scheduler_->zone_);
1460 for (Edge edge : node->use_edges()) {
1461 BasicBlock* use_block = GetBlockForUse(edge);
1462 if (use_block == nullptr) continue;
1463 while (marked_[use_block->dominator()->id().ToSize()]) {
1464 use_block = use_block->dominator();
1466 auto& use_node = dominators[use_block];
1467 if (use_node == nullptr) {
1468 if (dominators.size() == 1u) {
1469 // Place the {node} at {use_block}.
1472 TRACE(" pushing #%d:%s down to id:%d\n", node->id(),
1473 node->op()->mnemonic(), block->id().ToInt());
1475 // Place a copy of {node} at {use_block}.
1476 use_node = CloneNode(node);
1477 TRACE(" cloning #%d:%s for id:%d\n", use_node->id(),
1478 use_node->op()->mnemonic(), use_block->id().ToInt());
1479 scheduler_->schedule_queue_.push(use_node);
1482 edge.UpdateTo(use_node);
1487 BasicBlock* GetHoistBlock(BasicBlock* block) {
1488 if (block->IsLoopHeader()) return block->dominator();
1489 // We have to check to make sure that the {block} dominates all
1490 // of the outgoing blocks. If it doesn't, then there is a path
1491 // out of the loop which does not execute this {block}, so we
1492 // can't hoist operations from this {block} out of the loop, as
1493 // that would introduce additional computations.
1494 if (BasicBlock* header_block = block->loop_header()) {
1495 for (BasicBlock* outgoing_block :
1496 scheduler_->special_rpo_->GetOutgoingBlocks(header_block)) {
1497 if (BasicBlock::GetCommonDominator(block, outgoing_block) != block) {
1501 return header_block->dominator();
1506 BasicBlock* GetCommonDominatorOfUses(Node* node) {
1507 BasicBlock* block = nullptr;
1508 for (Edge edge : node->use_edges()) {
1509 BasicBlock* use_block = GetBlockForUse(edge);
1510 block = block == NULL ? use_block : use_block == NULL
1512 : BasicBlock::GetCommonDominator(
1518 BasicBlock* FindPredecessorBlock(Node* node) {
1519 return scheduler_->control_flow_builder_->FindPredecessorBlock(node);
1522 BasicBlock* GetBlockForUse(Edge edge) {
1523 Node* use = edge.from();
1524 if (IrOpcode::IsPhiOpcode(use->opcode())) {
1525 // If the use is from a coupled (i.e. floating) phi, compute the common
1526 // dominator of its uses. This will not recurse more than one level.
1527 if (scheduler_->GetPlacement(use) == Scheduler::kCoupled) {
1528 TRACE(" inspecting uses of coupled #%d:%s\n", use->id(),
1529 use->op()->mnemonic());
1530 DCHECK_EQ(edge.to(), NodeProperties::GetControlInput(use));
1531 return GetCommonDominatorOfUses(use);
1533 // If the use is from a fixed (i.e. non-floating) phi, we use the
1534 // predecessor block of the corresponding control input to the merge.
1535 if (scheduler_->GetPlacement(use) == Scheduler::kFixed) {
1536 TRACE(" input@%d into a fixed phi #%d:%s\n", edge.index(), use->id(),
1537 use->op()->mnemonic());
1538 Node* merge = NodeProperties::GetControlInput(use, 0);
1539 DCHECK(IrOpcode::IsMergeOpcode(merge->opcode()));
1540 Node* input = NodeProperties::GetControlInput(merge, edge.index());
1541 return FindPredecessorBlock(input);
1543 } else if (IrOpcode::IsMergeOpcode(use->opcode())) {
1544 // If the use is from a fixed (i.e. non-floating) merge, we use the
1545 // predecessor block of the current input to the merge.
1546 if (scheduler_->GetPlacement(use) == Scheduler::kFixed) {
1547 TRACE(" input@%d into a fixed merge #%d:%s\n", edge.index(), use->id(),
1548 use->op()->mnemonic());
1549 return FindPredecessorBlock(edge.to());
1552 BasicBlock* result = schedule_->block(use);
1553 if (result == NULL) return NULL;
1554 TRACE(" must dominate use #%d:%s in id:%d\n", use->id(),
1555 use->op()->mnemonic(), result->id().ToInt());
1559 void ScheduleFloatingControl(BasicBlock* block, Node* node) {
1560 scheduler_->FuseFloatingControl(block, node);
1563 void ScheduleNode(BasicBlock* block, Node* node) {
1564 schedule_->PlanNode(block, node);
1565 scheduler_->scheduled_nodes_[block->id().ToSize()].push_back(node);
1566 scheduler_->UpdatePlacement(node, Scheduler::kScheduled);
1569 Node* CloneNode(Node* node) {
1570 int const input_count = node->InputCount();
1571 Node** const inputs = scheduler_->zone_->NewArray<Node*>(input_count);
1572 for (int index = 0; index < input_count; ++index) {
1573 Node* const input = node->InputAt(index);
1574 scheduler_->IncrementUnscheduledUseCount(input, index, node);
1575 inputs[index] = input;
1577 Node* copy = scheduler_->graph_->NewNode(node->op(), input_count, inputs);
1578 TRACE(("clone #%d:%s -> #%d\n"), node->id(), node->op()->mnemonic(),
1580 scheduler_->node_data_.resize(copy->id() + 1,
1581 scheduler_->DefaultSchedulerData());
1582 scheduler_->node_data_[copy->id()] = scheduler_->node_data_[node->id()];
1586 Scheduler* scheduler_;
1587 Schedule* schedule_;
1589 ZoneDeque<BasicBlock*> marking_queue_;
1593 void Scheduler::ScheduleLate() {
1594 TRACE("--- SCHEDULE LATE ------------------------------------------\n");
1595 if (FLAG_trace_turbo_scheduler) {
1597 for (Node* node : schedule_root_nodes_) {
1598 TRACE("#%d:%s ", node->id(), node->op()->mnemonic());
1603 // Schedule: Places nodes in dominator block of all their uses.
1604 ScheduleLateNodeVisitor schedule_late_visitor(zone_, this);
1605 schedule_late_visitor.Run(&schedule_root_nodes_);
1609 // -----------------------------------------------------------------------------
1610 // Phase 6: Seal the final schedule.
1613 void Scheduler::SealFinalSchedule() {
1614 TRACE("--- SEAL FINAL SCHEDULE ------------------------------------\n");
1616 // Serialize the assembly order and reverse-post-order numbering.
1617 special_rpo_->SerializeRPOIntoSchedule();
1618 special_rpo_->PrintAndVerifySpecialRPO();
1620 // Add collected nodes for basic blocks to their blocks in the right order.
1622 for (NodeVector& nodes : scheduled_nodes_) {
1623 BasicBlock::Id id = BasicBlock::Id::FromInt(block_num++);
1624 BasicBlock* block = schedule_->GetBlockById(id);
1625 for (auto i = nodes.rbegin(); i != nodes.rend(); ++i) {
1626 schedule_->AddNode(block, *i);
1632 // -----------------------------------------------------------------------------
1635 void Scheduler::FuseFloatingControl(BasicBlock* block, Node* node) {
1636 TRACE("--- FUSE FLOATING CONTROL ----------------------------------\n");
1637 if (FLAG_trace_turbo_scheduler) {
1638 OFStream os(stdout);
1639 os << "Schedule before control flow fusion:\n" << *schedule_;
1642 // Iterate on phase 1: Build control-flow graph.
1643 control_flow_builder_->Run(block, node);
1645 // Iterate on phase 2: Compute special RPO and dominator tree.
1646 special_rpo_->UpdateSpecialRPO(block, schedule_->block(node));
1647 // TODO(mstarzinger): Currently "iterate on" means "re-run". Fix that.
1648 for (BasicBlock* b = block->rpo_next(); b != NULL; b = b->rpo_next()) {
1649 b->set_dominator_depth(-1);
1650 b->set_dominator(NULL);
1652 PropagateImmediateDominators(block->rpo_next());
1654 // Iterate on phase 4: Schedule nodes early.
1655 // TODO(mstarzinger): The following loop gathering the propagation roots is a
1656 // temporary solution and should be merged into the rest of the scheduler as
1657 // soon as the approach settled for all floating loops.
1658 NodeVector propagation_roots(control_flow_builder_->control_);
1659 for (Node* node : control_flow_builder_->control_) {
1660 for (Node* use : node->uses()) {
1661 if (NodeProperties::IsPhi(use)) propagation_roots.push_back(use);
1664 if (FLAG_trace_turbo_scheduler) {
1665 TRACE("propagation roots: ");
1666 for (Node* node : propagation_roots) {
1667 TRACE("#%d:%s ", node->id(), node->op()->mnemonic());
1671 ScheduleEarlyNodeVisitor schedule_early_visitor(zone_, this);
1672 schedule_early_visitor.Run(&propagation_roots);
1674 // Move previously planned nodes.
1675 // TODO(mstarzinger): Improve that by supporting bulk moves.
1676 scheduled_nodes_.resize(schedule_->BasicBlockCount(), NodeVector(zone_));
1677 MovePlannedNodes(block, schedule_->block(node));
1679 if (FLAG_trace_turbo_scheduler) {
1680 OFStream os(stdout);
1681 os << "Schedule after control flow fusion:\n" << *schedule_;
1686 void Scheduler::MovePlannedNodes(BasicBlock* from, BasicBlock* to) {
1687 TRACE("Move planned nodes from id:%d to id:%d\n", from->id().ToInt(),
1689 NodeVector* nodes = &(scheduled_nodes_[from->id().ToSize()]);
1690 for (Node* const node : *nodes) {
1691 schedule_->SetBlockForNode(to, node);
1692 scheduled_nodes_[to->id().ToSize()].push_back(node);
1697 } // namespace compiler
1698 } // namespace internal