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.
4 
5 #include <deque>
6 #include <queue>
7 
8 #include "src/compiler/scheduler.h"
9 
10 #include "src/compiler/graph.h"
11 #include "src/compiler/graph-inl.h"
12 #include "src/compiler/node.h"
13 #include "src/compiler/node-properties.h"
14 #include "src/compiler/node-properties-inl.h"
15 #include "src/data-flow.h"
16 
17 namespace v8 {
18 namespace internal {
19 namespace compiler {
20 
Trace(const char * msg,...)21 static inline void Trace(const char* msg, ...) {
22   if (FLAG_trace_turbo_scheduler) {
23     va_list arguments;
24     va_start(arguments, msg);
25     base::OS::VPrint(msg, arguments);
26     va_end(arguments);
27   }
28 }
29 
30 
31 // Internal class to build a control flow graph (i.e the basic blocks and edges
32 // between them within a Schedule) from the node graph.
33 // Visits the control edges of the graph backwards from end in order to find
34 // the connected control subgraph, needed for scheduling.
35 class CFGBuilder {
36  public:
37   Scheduler* scheduler_;
38   Schedule* schedule_;
39   ZoneQueue<Node*> queue_;
40   NodeVector control_;
41 
CFGBuilder(Zone * zone,Scheduler * scheduler)42   CFGBuilder(Zone* zone, Scheduler* scheduler)
43       : scheduler_(scheduler),
44         schedule_(scheduler->schedule_),
45         queue_(zone),
46         control_(zone) {}
47 
48   // Run the control flow graph construction algorithm by walking the graph
49   // backwards from end through control edges, building and connecting the
50   // basic blocks for control nodes.
Run()51   void Run() {
52     Graph* graph = scheduler_->graph_;
53     FixNode(schedule_->start(), graph->start());
54     Queue(graph->end());
55 
56     while (!queue_.empty()) {  // Breadth-first backwards traversal.
57       Node* node = queue_.front();
58       queue_.pop();
59       int max = NodeProperties::PastControlIndex(node);
60       for (int i = NodeProperties::FirstControlIndex(node); i < max; i++) {
61         Queue(node->InputAt(i));
62       }
63     }
64 
65     for (NodeVector::iterator i = control_.begin(); i != control_.end(); ++i) {
66       ConnectBlocks(*i);  // Connect block to its predecessor/successors.
67     }
68 
69     FixNode(schedule_->end(), graph->end());
70   }
71 
FixNode(BasicBlock * block,Node * node)72   void FixNode(BasicBlock* block, Node* node) {
73     schedule_->AddNode(block, node);
74     scheduler_->GetData(node)->is_connected_control_ = true;
75     scheduler_->GetData(node)->placement_ = Scheduler::kFixed;
76   }
77 
Queue(Node * node)78   void Queue(Node* node) {
79     // Mark the connected control nodes as they queued.
80     Scheduler::SchedulerData* data = scheduler_->GetData(node);
81     if (!data->is_connected_control_) {
82       BuildBlocks(node);
83       queue_.push(node);
84       control_.push_back(node);
85       data->is_connected_control_ = true;
86     }
87   }
88 
BuildBlocks(Node * node)89   void BuildBlocks(Node* node) {
90     switch (node->opcode()) {
91       case IrOpcode::kLoop:
92       case IrOpcode::kMerge:
93         BuildBlockForNode(node);
94         break;
95       case IrOpcode::kBranch:
96         BuildBlocksForSuccessors(node, IrOpcode::kIfTrue, IrOpcode::kIfFalse);
97         break;
98       default:
99         break;
100     }
101   }
102 
ConnectBlocks(Node * node)103   void ConnectBlocks(Node* node) {
104     switch (node->opcode()) {
105       case IrOpcode::kLoop:
106       case IrOpcode::kMerge:
107         ConnectMerge(node);
108         break;
109       case IrOpcode::kBranch:
110         scheduler_->schedule_root_nodes_.push_back(node);
111         ConnectBranch(node);
112         break;
113       case IrOpcode::kReturn:
114         scheduler_->schedule_root_nodes_.push_back(node);
115         ConnectReturn(node);
116         break;
117       default:
118         break;
119     }
120   }
121 
BuildBlockForNode(Node * node)122   void BuildBlockForNode(Node* node) {
123     if (schedule_->block(node) == NULL) {
124       BasicBlock* block = schedule_->NewBasicBlock();
125       Trace("Create block B%d for #%d:%s\n", block->id(), node->id(),
126             node->op()->mnemonic());
127       FixNode(block, node);
128     }
129   }
130 
BuildBlocksForSuccessors(Node * node,IrOpcode::Value a,IrOpcode::Value b)131   void BuildBlocksForSuccessors(Node* node, IrOpcode::Value a,
132                                 IrOpcode::Value b) {
133     Node* successors[2];
134     CollectSuccessorProjections(node, successors, a, b);
135     BuildBlockForNode(successors[0]);
136     BuildBlockForNode(successors[1]);
137   }
138 
139   // Collect the branch-related projections from a node, such as IfTrue,
140   // IfFalse.
141   // TODO(titzer): consider moving this to node.h
CollectSuccessorProjections(Node * node,Node ** buffer,IrOpcode::Value true_opcode,IrOpcode::Value false_opcode)142   void CollectSuccessorProjections(Node* node, Node** buffer,
143                                    IrOpcode::Value true_opcode,
144                                    IrOpcode::Value false_opcode) {
145     buffer[0] = NULL;
146     buffer[1] = NULL;
147     for (UseIter i = node->uses().begin(); i != node->uses().end(); ++i) {
148       if ((*i)->opcode() == true_opcode) {
149         DCHECK_EQ(NULL, buffer[0]);
150         buffer[0] = *i;
151       }
152       if ((*i)->opcode() == false_opcode) {
153         DCHECK_EQ(NULL, buffer[1]);
154         buffer[1] = *i;
155       }
156     }
157     DCHECK_NE(NULL, buffer[0]);
158     DCHECK_NE(NULL, buffer[1]);
159   }
160 
CollectSuccessorBlocks(Node * node,BasicBlock ** buffer,IrOpcode::Value true_opcode,IrOpcode::Value false_opcode)161   void CollectSuccessorBlocks(Node* node, BasicBlock** buffer,
162                               IrOpcode::Value true_opcode,
163                               IrOpcode::Value false_opcode) {
164     Node* successors[2];
165     CollectSuccessorProjections(node, successors, true_opcode, false_opcode);
166     buffer[0] = schedule_->block(successors[0]);
167     buffer[1] = schedule_->block(successors[1]);
168   }
169 
ConnectBranch(Node * branch)170   void ConnectBranch(Node* branch) {
171     Node* branch_block_node = NodeProperties::GetControlInput(branch);
172     BasicBlock* branch_block = schedule_->block(branch_block_node);
173     DCHECK(branch_block != NULL);
174 
175     BasicBlock* successor_blocks[2];
176     CollectSuccessorBlocks(branch, successor_blocks, IrOpcode::kIfTrue,
177                            IrOpcode::kIfFalse);
178 
179     TraceConnect(branch, branch_block, successor_blocks[0]);
180     TraceConnect(branch, branch_block, successor_blocks[1]);
181 
182     schedule_->AddBranch(branch_block, branch, successor_blocks[0],
183                          successor_blocks[1]);
184   }
185 
ConnectMerge(Node * merge)186   void ConnectMerge(Node* merge) {
187     BasicBlock* block = schedule_->block(merge);
188     DCHECK(block != NULL);
189     // For all of the merge's control inputs, add a goto at the end to the
190     // merge's basic block.
191     for (InputIter j = merge->inputs().begin(); j != merge->inputs().end();
192          ++j) {
193       BasicBlock* predecessor_block = schedule_->block(*j);
194       if ((*j)->opcode() != IrOpcode::kReturn) {
195         TraceConnect(merge, predecessor_block, block);
196         schedule_->AddGoto(predecessor_block, block);
197       }
198     }
199   }
200 
ConnectReturn(Node * ret)201   void ConnectReturn(Node* ret) {
202     Node* return_block_node = NodeProperties::GetControlInput(ret);
203     BasicBlock* return_block = schedule_->block(return_block_node);
204     TraceConnect(ret, return_block, NULL);
205     schedule_->AddReturn(return_block, ret);
206   }
207 
TraceConnect(Node * node,BasicBlock * block,BasicBlock * succ)208   void TraceConnect(Node* node, BasicBlock* block, BasicBlock* succ) {
209     DCHECK_NE(NULL, block);
210     if (succ == NULL) {
211       Trace("Connect #%d:%s, B%d -> end\n", node->id(), node->op()->mnemonic(),
212             block->id());
213     } else {
214       Trace("Connect #%d:%s, B%d -> B%d\n", node->id(), node->op()->mnemonic(),
215             block->id(), succ->id());
216     }
217   }
218 };
219 
220 
DefaultSchedulerData()221 Scheduler::SchedulerData Scheduler::DefaultSchedulerData() {
222   SchedulerData def = {0, 0, false, false, kUnknown};
223   return def;
224 }
225 
226 
Scheduler(Zone * zone,Graph * graph,Schedule * schedule)227 Scheduler::Scheduler(Zone* zone, Graph* graph, Schedule* schedule)
228     : zone_(zone),
229       graph_(graph),
230       schedule_(schedule),
231       scheduled_nodes_(zone),
232       schedule_root_nodes_(zone),
233       node_data_(graph_->NodeCount(), DefaultSchedulerData(), zone),
234       has_floating_control_(false) {}
235 
236 
ComputeSchedule(Graph * graph)237 Schedule* Scheduler::ComputeSchedule(Graph* graph) {
238   Schedule* schedule;
239   bool had_floating_control = false;
240   do {
241     Zone tmp_zone(graph->zone()->isolate());
242     schedule = new (graph->zone()) Schedule(graph->zone());
243     Scheduler scheduler(&tmp_zone, graph, schedule);
244 
245     scheduler.BuildCFG();
246 
247     Scheduler::ComputeSpecialRPO(schedule);
248     scheduler.GenerateImmediateDominatorTree();
249 
250     scheduler.PrepareUses();
251     scheduler.ScheduleEarly();
252     scheduler.ScheduleLate();
253 
254     had_floating_control = scheduler.ConnectFloatingControl();
255   } while (had_floating_control);
256 
257   return schedule;
258 }
259 
260 
GetPlacement(Node * node)261 Scheduler::Placement Scheduler::GetPlacement(Node* node) {
262   SchedulerData* data = GetData(node);
263   if (data->placement_ == kUnknown) {  // Compute placement, once, on demand.
264     switch (node->opcode()) {
265       case IrOpcode::kParameter:
266         // Parameters are always fixed to the start node.
267         data->placement_ = kFixed;
268         break;
269       case IrOpcode::kPhi:
270       case IrOpcode::kEffectPhi: {
271         // Phis and effect phis are fixed if their control inputs are.
272         data->placement_ = GetPlacement(NodeProperties::GetControlInput(node));
273         break;
274       }
275 #define DEFINE_FLOATING_CONTROL_CASE(V) case IrOpcode::k##V:
276         CONTROL_OP_LIST(DEFINE_FLOATING_CONTROL_CASE)
277 #undef DEFINE_FLOATING_CONTROL_CASE
278         {
279           // Control nodes that were not control-reachable from end may float.
280           data->placement_ = kSchedulable;
281           if (!data->is_connected_control_) {
282             data->is_floating_control_ = true;
283             has_floating_control_ = true;
284             Trace("Floating control found: #%d:%s\n", node->id(),
285                   node->op()->mnemonic());
286           }
287           break;
288         }
289       default:
290         data->placement_ = kSchedulable;
291         break;
292     }
293   }
294   return data->placement_;
295 }
296 
297 
BuildCFG()298 void Scheduler::BuildCFG() {
299   Trace("---------------- CREATING CFG ------------------\n");
300   CFGBuilder cfg_builder(zone_, this);
301   cfg_builder.Run();
302   // Initialize per-block data.
303   scheduled_nodes_.resize(schedule_->BasicBlockCount(), NodeVector(zone_));
304 }
305 
306 
GetCommonDominator(BasicBlock * b1,BasicBlock * b2)307 BasicBlock* Scheduler::GetCommonDominator(BasicBlock* b1, BasicBlock* b2) {
308   while (b1 != b2) {
309     int b1_rpo = GetRPONumber(b1);
310     int b2_rpo = GetRPONumber(b2);
311     DCHECK(b1_rpo != b2_rpo);
312     if (b1_rpo < b2_rpo) {
313       b2 = b2->dominator_;
314     } else {
315       b1 = b1->dominator_;
316     }
317   }
318   return b1;
319 }
320 
321 
GenerateImmediateDominatorTree()322 void Scheduler::GenerateImmediateDominatorTree() {
323   // Build the dominator graph.  TODO(danno): consider using Lengauer & Tarjan's
324   // if this becomes really slow.
325   Trace("------------ IMMEDIATE BLOCK DOMINATORS -----------\n");
326   for (size_t i = 0; i < schedule_->rpo_order_.size(); i++) {
327     BasicBlock* current_rpo = schedule_->rpo_order_[i];
328     if (current_rpo != schedule_->start()) {
329       BasicBlock::Predecessors::iterator current_pred =
330           current_rpo->predecessors().begin();
331       BasicBlock::Predecessors::iterator end =
332           current_rpo->predecessors().end();
333       DCHECK(current_pred != end);
334       BasicBlock* dominator = *current_pred;
335       ++current_pred;
336       // For multiple predecessors, walk up the rpo ordering until a common
337       // dominator is found.
338       int current_rpo_pos = GetRPONumber(current_rpo);
339       while (current_pred != end) {
340         // Don't examine backwards edges
341         BasicBlock* pred = *current_pred;
342         if (GetRPONumber(pred) < current_rpo_pos) {
343           dominator = GetCommonDominator(dominator, *current_pred);
344         }
345         ++current_pred;
346       }
347       current_rpo->dominator_ = dominator;
348       Trace("Block %d's idom is %d\n", current_rpo->id(), dominator->id());
349     }
350   }
351 }
352 
353 
354 class ScheduleEarlyNodeVisitor : public NullNodeVisitor {
355  public:
ScheduleEarlyNodeVisitor(Scheduler * scheduler)356   explicit ScheduleEarlyNodeVisitor(Scheduler* scheduler)
357       : has_changed_rpo_constraints_(true),
358         scheduler_(scheduler),
359         schedule_(scheduler->schedule_) {}
360 
Pre(Node * node)361   GenericGraphVisit::Control Pre(Node* node) {
362     int max_rpo = 0;
363     // Fixed nodes already know their schedule early position.
364     if (scheduler_->GetPlacement(node) == Scheduler::kFixed) {
365       BasicBlock* block = schedule_->block(node);
366       DCHECK(block != NULL);
367       max_rpo = block->rpo_number_;
368       if (scheduler_->GetData(node)->minimum_rpo_ != max_rpo) {
369         has_changed_rpo_constraints_ = true;
370       }
371       scheduler_->GetData(node)->minimum_rpo_ = max_rpo;
372       Trace("Preschedule #%d:%s minimum_rpo = %d\n", node->id(),
373             node->op()->mnemonic(), max_rpo);
374     }
375     return GenericGraphVisit::CONTINUE;
376   }
377 
Post(Node * node)378   GenericGraphVisit::Control Post(Node* node) {
379     int max_rpo = 0;
380     // Otherwise, the minimum rpo for the node is the max of all of the inputs.
381     if (scheduler_->GetPlacement(node) != Scheduler::kFixed) {
382       for (InputIter i = node->inputs().begin(); i != node->inputs().end();
383            ++i) {
384         int control_rpo = scheduler_->GetData(*i)->minimum_rpo_;
385         if (control_rpo > max_rpo) {
386           max_rpo = control_rpo;
387         }
388       }
389       if (scheduler_->GetData(node)->minimum_rpo_ != max_rpo) {
390         has_changed_rpo_constraints_ = true;
391       }
392       scheduler_->GetData(node)->minimum_rpo_ = max_rpo;
393       Trace("Postschedule #%d:%s minimum_rpo = %d\n", node->id(),
394             node->op()->mnemonic(), max_rpo);
395     }
396     return GenericGraphVisit::CONTINUE;
397   }
398 
399   // TODO(mstarzinger): Dirty hack to unblock others, schedule early should be
400   // rewritten to use a pre-order traversal from the start instead.
401   bool has_changed_rpo_constraints_;
402 
403  private:
404   Scheduler* scheduler_;
405   Schedule* schedule_;
406 };
407 
408 
ScheduleEarly()409 void Scheduler::ScheduleEarly() {
410   Trace("------------------- SCHEDULE EARLY ----------------\n");
411 
412   int fixpoint_count = 0;
413   ScheduleEarlyNodeVisitor visitor(this);
414   while (visitor.has_changed_rpo_constraints_) {
415     visitor.has_changed_rpo_constraints_ = false;
416     graph_->VisitNodeInputsFromEnd(&visitor);
417     fixpoint_count++;
418   }
419 
420   Trace("It took %d iterations to determine fixpoint\n", fixpoint_count);
421 }
422 
423 
424 class PrepareUsesVisitor : public NullNodeVisitor {
425  public:
PrepareUsesVisitor(Scheduler * scheduler)426   explicit PrepareUsesVisitor(Scheduler* scheduler)
427       : scheduler_(scheduler), schedule_(scheduler->schedule_) {}
428 
Pre(Node * node)429   GenericGraphVisit::Control Pre(Node* node) {
430     if (scheduler_->GetPlacement(node) == Scheduler::kFixed) {
431       // Fixed nodes are always roots for schedule late.
432       scheduler_->schedule_root_nodes_.push_back(node);
433       if (!schedule_->IsScheduled(node)) {
434         // Make sure root nodes are scheduled in their respective blocks.
435         Trace("  Scheduling fixed position node #%d:%s\n", node->id(),
436               node->op()->mnemonic());
437         IrOpcode::Value opcode = node->opcode();
438         BasicBlock* block =
439             opcode == IrOpcode::kParameter
440                 ? schedule_->start()
441                 : schedule_->block(NodeProperties::GetControlInput(node));
442         DCHECK(block != NULL);
443         schedule_->AddNode(block, node);
444       }
445     }
446 
447     return GenericGraphVisit::CONTINUE;
448   }
449 
PostEdge(Node * from,int index,Node * to)450   void PostEdge(Node* from, int index, Node* to) {
451     // If the edge is from an unscheduled node, then tally it in the use count
452     // for all of its inputs. The same criterion will be used in ScheduleLate
453     // for decrementing use counts.
454     if (!schedule_->IsScheduled(from)) {
455       DCHECK_NE(Scheduler::kFixed, scheduler_->GetPlacement(from));
456       ++(scheduler_->GetData(to)->unscheduled_count_);
457       Trace("  Use count of #%d:%s (used by #%d:%s)++ = %d\n", to->id(),
458             to->op()->mnemonic(), from->id(), from->op()->mnemonic(),
459             scheduler_->GetData(to)->unscheduled_count_);
460     }
461   }
462 
463  private:
464   Scheduler* scheduler_;
465   Schedule* schedule_;
466 };
467 
468 
PrepareUses()469 void Scheduler::PrepareUses() {
470   Trace("------------------- PREPARE USES ------------------\n");
471   // Count the uses of every node, it will be used to ensure that all of a
472   // node's uses are scheduled before the node itself.
473   PrepareUsesVisitor prepare_uses(this);
474   graph_->VisitNodeInputsFromEnd(&prepare_uses);
475 }
476 
477 
478 class ScheduleLateNodeVisitor : public NullNodeVisitor {
479  public:
ScheduleLateNodeVisitor(Scheduler * scheduler)480   explicit ScheduleLateNodeVisitor(Scheduler* scheduler)
481       : scheduler_(scheduler), schedule_(scheduler_->schedule_) {}
482 
Pre(Node * node)483   GenericGraphVisit::Control Pre(Node* node) {
484     // Don't schedule nodes that are already scheduled.
485     if (schedule_->IsScheduled(node)) {
486       return GenericGraphVisit::CONTINUE;
487     }
488     Scheduler::SchedulerData* data = scheduler_->GetData(node);
489     DCHECK_EQ(Scheduler::kSchedulable, data->placement_);
490 
491     // If all the uses of a node have been scheduled, then the node itself can
492     // be scheduled.
493     bool eligible = data->unscheduled_count_ == 0;
494     Trace("Testing for schedule eligibility for #%d:%s = %s\n", node->id(),
495           node->op()->mnemonic(), eligible ? "true" : "false");
496     if (!eligible) return GenericGraphVisit::DEFER;
497 
498     // Determine the dominating block for all of the uses of this node. It is
499     // the latest block that this node can be scheduled in.
500     BasicBlock* block = NULL;
501     for (Node::Uses::iterator i = node->uses().begin(); i != node->uses().end();
502          ++i) {
503       BasicBlock* use_block = GetBlockForUse(i.edge());
504       block = block == NULL ? use_block : use_block == NULL
505                                               ? block
506                                               : scheduler_->GetCommonDominator(
507                                                     block, use_block);
508     }
509     DCHECK(block != NULL);
510 
511     int min_rpo = data->minimum_rpo_;
512     Trace(
513         "Schedule late conservative for #%d:%s is B%d at loop depth %d, "
514         "minimum_rpo = %d\n",
515         node->id(), node->op()->mnemonic(), block->id(), block->loop_depth_,
516         min_rpo);
517     // Hoist nodes out of loops if possible. Nodes can be hoisted iteratively
518     // into enclosing loop pre-headers until they would preceed their
519     // ScheduleEarly position.
520     BasicBlock* hoist_block = block;
521     while (hoist_block != NULL && hoist_block->rpo_number_ >= min_rpo) {
522       if (hoist_block->loop_depth_ < block->loop_depth_) {
523         block = hoist_block;
524         Trace("  hoisting #%d:%s to block %d\n", node->id(),
525               node->op()->mnemonic(), block->id());
526       }
527       // Try to hoist to the pre-header of the loop header.
528       hoist_block = hoist_block->loop_header();
529       if (hoist_block != NULL) {
530         BasicBlock* pre_header = hoist_block->dominator_;
531         DCHECK(pre_header == NULL ||
532                *hoist_block->predecessors().begin() == pre_header);
533         Trace(
534             "  hoist to pre-header B%d of loop header B%d, depth would be %d\n",
535             pre_header->id(), hoist_block->id(), pre_header->loop_depth_);
536         hoist_block = pre_header;
537       }
538     }
539 
540     ScheduleNode(block, node);
541 
542     return GenericGraphVisit::CONTINUE;
543   }
544 
545  private:
GetBlockForUse(Node::Edge edge)546   BasicBlock* GetBlockForUse(Node::Edge edge) {
547     Node* use = edge.from();
548     IrOpcode::Value opcode = use->opcode();
549     if (opcode == IrOpcode::kPhi || opcode == IrOpcode::kEffectPhi) {
550       // If the use is from a fixed (i.e. non-floating) phi, use the block
551       // of the corresponding control input to the merge.
552       int index = edge.index();
553       if (scheduler_->GetPlacement(use) == Scheduler::kFixed) {
554         Trace("  input@%d into a fixed phi #%d:%s\n", index, use->id(),
555               use->op()->mnemonic());
556         Node* merge = NodeProperties::GetControlInput(use, 0);
557         opcode = merge->opcode();
558         DCHECK(opcode == IrOpcode::kMerge || opcode == IrOpcode::kLoop);
559         use = NodeProperties::GetControlInput(merge, index);
560       }
561     }
562     BasicBlock* result = schedule_->block(use);
563     if (result == NULL) return NULL;
564     Trace("  must dominate use #%d:%s in B%d\n", use->id(),
565           use->op()->mnemonic(), result->id());
566     return result;
567   }
568 
ScheduleNode(BasicBlock * block,Node * node)569   void ScheduleNode(BasicBlock* block, Node* node) {
570     schedule_->PlanNode(block, node);
571     scheduler_->scheduled_nodes_[block->id()].push_back(node);
572 
573     // Reduce the use count of the node's inputs to potentially make them
574     // schedulable.
575     for (InputIter i = node->inputs().begin(); i != node->inputs().end(); ++i) {
576       Scheduler::SchedulerData* data = scheduler_->GetData(*i);
577       DCHECK(data->unscheduled_count_ > 0);
578       --data->unscheduled_count_;
579       if (FLAG_trace_turbo_scheduler) {
580         Trace("  Use count for #%d:%s (used by #%d:%s)-- = %d\n", (*i)->id(),
581               (*i)->op()->mnemonic(), i.edge().from()->id(),
582               i.edge().from()->op()->mnemonic(), data->unscheduled_count_);
583         if (data->unscheduled_count_ == 0) {
584           Trace("  newly eligible #%d:%s\n", (*i)->id(),
585                 (*i)->op()->mnemonic());
586         }
587       }
588     }
589   }
590 
591   Scheduler* scheduler_;
592   Schedule* schedule_;
593 };
594 
595 
ScheduleLate()596 void Scheduler::ScheduleLate() {
597   Trace("------------------- SCHEDULE LATE -----------------\n");
598   if (FLAG_trace_turbo_scheduler) {
599     Trace("roots: ");
600     for (NodeVectorIter i = schedule_root_nodes_.begin();
601          i != schedule_root_nodes_.end(); ++i) {
602       Trace("#%d:%s ", (*i)->id(), (*i)->op()->mnemonic());
603     }
604     Trace("\n");
605   }
606 
607   // Schedule: Places nodes in dominator block of all their uses.
608   ScheduleLateNodeVisitor schedule_late_visitor(this);
609 
610   {
611     Zone zone(zone_->isolate());
612     GenericGraphVisit::Visit<ScheduleLateNodeVisitor,
613                              NodeInputIterationTraits<Node> >(
614         graph_, &zone, schedule_root_nodes_.begin(), schedule_root_nodes_.end(),
615         &schedule_late_visitor);
616   }
617 
618   // Add collected nodes for basic blocks to their blocks in the right order.
619   int block_num = 0;
620   for (NodeVectorVectorIter i = scheduled_nodes_.begin();
621        i != scheduled_nodes_.end(); ++i) {
622     for (NodeVectorRIter j = i->rbegin(); j != i->rend(); ++j) {
623       schedule_->AddNode(schedule_->all_blocks_.at(block_num), *j);
624     }
625     block_num++;
626   }
627 }
628 
629 
ConnectFloatingControl()630 bool Scheduler::ConnectFloatingControl() {
631   if (!has_floating_control_) return false;
632 
633   Trace("Connecting floating control...\n");
634 
635   // Process blocks and instructions backwards to find and connect floating
636   // control nodes into the control graph according to the block they were
637   // scheduled into.
638   int max = static_cast<int>(schedule_->rpo_order()->size());
639   for (int i = max - 1; i >= 0; i--) {
640     BasicBlock* block = schedule_->rpo_order()->at(i);
641     // TODO(titzer): we place at most one floating control structure per
642     // basic block because scheduling currently can interleave phis from
643     // one subgraph with the merges from another subgraph.
644     bool one_placed = false;
645     for (int j = static_cast<int>(block->nodes_.size()) - 1; j >= 0; j--) {
646       Node* node = block->nodes_[j];
647       SchedulerData* data = GetData(node);
648       if (data->is_floating_control_ && !data->is_connected_control_ &&
649           !one_placed) {
650         Trace("  Floating control #%d:%s was scheduled in B%d\n", node->id(),
651               node->op()->mnemonic(), block->id());
652         ConnectFloatingControlSubgraph(block, node);
653         one_placed = true;
654       }
655     }
656   }
657 
658   return true;
659 }
660 
661 
ConnectFloatingControlSubgraph(BasicBlock * block,Node * end)662 void Scheduler::ConnectFloatingControlSubgraph(BasicBlock* block, Node* end) {
663   Node* block_start = block->nodes_[0];
664   DCHECK(IrOpcode::IsControlOpcode(block_start->opcode()));
665   // Find the current "control successor" of the node that starts the block
666   // by searching the control uses for a control input edge from a connected
667   // control node.
668   Node* control_succ = NULL;
669   for (UseIter i = block_start->uses().begin(); i != block_start->uses().end();
670        ++i) {
671     Node::Edge edge = i.edge();
672     if (NodeProperties::IsControlEdge(edge) &&
673         GetData(edge.from())->is_connected_control_) {
674       DCHECK_EQ(NULL, control_succ);
675       control_succ = edge.from();
676       control_succ->ReplaceInput(edge.index(), end);
677     }
678   }
679   DCHECK_NE(NULL, control_succ);
680   Trace("  Inserting floating control end %d:%s between %d:%s -> %d:%s\n",
681         end->id(), end->op()->mnemonic(), control_succ->id(),
682         control_succ->op()->mnemonic(), block_start->id(),
683         block_start->op()->mnemonic());
684 
685   // Find the "start" node of the control subgraph, which should be the
686   // unique node that is itself floating control but has a control input that
687   // is not floating.
688   Node* start = NULL;
689   ZoneQueue<Node*> queue(zone_);
690   queue.push(end);
691   GetData(end)->is_connected_control_ = true;
692   while (!queue.empty()) {
693     Node* node = queue.front();
694     queue.pop();
695     Trace("  Search #%d:%s for control subgraph start\n", node->id(),
696           node->op()->mnemonic());
697     int max = NodeProperties::PastControlIndex(node);
698     for (int i = NodeProperties::FirstControlIndex(node); i < max; i++) {
699       Node* input = node->InputAt(i);
700       SchedulerData* data = GetData(input);
701       if (data->is_floating_control_) {
702         // {input} is floating control.
703         if (!data->is_connected_control_) {
704           // First time seeing {input} during this traversal, queue it.
705           queue.push(input);
706           data->is_connected_control_ = true;
707         }
708       } else {
709         // Otherwise, {node} is the start node, because it is floating control
710         // but is connected to {input} that is not floating control.
711         DCHECK_EQ(NULL, start);  // There can be only one.
712         start = node;
713       }
714     }
715   }
716 
717   DCHECK_NE(NULL, start);
718   start->ReplaceInput(NodeProperties::FirstControlIndex(start), block_start);
719 
720   Trace("  Connecting floating control start %d:%s to %d:%s\n", start->id(),
721         start->op()->mnemonic(), block_start->id(),
722         block_start->op()->mnemonic());
723 }
724 
725 
726 // Numbering for BasicBlockData.rpo_number_ for this block traversal:
727 static const int kBlockOnStack = -2;
728 static const int kBlockVisited1 = -3;
729 static const int kBlockVisited2 = -4;
730 static const int kBlockUnvisited1 = -1;
731 static const int kBlockUnvisited2 = kBlockVisited1;
732 
733 struct SpecialRPOStackFrame {
734   BasicBlock* block;
735   int index;
736 };
737 
738 struct BlockList {
739   BasicBlock* block;
740   BlockList* next;
741 
Addv8::internal::compiler::BlockList742   BlockList* Add(Zone* zone, BasicBlock* b) {
743     BlockList* list = static_cast<BlockList*>(zone->New(sizeof(BlockList)));
744     list->block = b;
745     list->next = this;
746     return list;
747   }
748 
Serializev8::internal::compiler::BlockList749   void Serialize(BasicBlockVector* final_order) {
750     for (BlockList* l = this; l != NULL; l = l->next) {
751       l->block->rpo_number_ = static_cast<int>(final_order->size());
752       final_order->push_back(l->block);
753     }
754   }
755 };
756 
757 struct LoopInfo {
758   BasicBlock* header;
759   ZoneList<BasicBlock*>* outgoing;
760   BitVector* members;
761   LoopInfo* prev;
762   BlockList* end;
763   BlockList* start;
764 
AddOutgoingv8::internal::compiler::LoopInfo765   void AddOutgoing(Zone* zone, BasicBlock* block) {
766     if (outgoing == NULL) outgoing = new (zone) ZoneList<BasicBlock*>(2, zone);
767     outgoing->Add(block, zone);
768   }
769 };
770 
771 
Push(SpecialRPOStackFrame * stack,int depth,BasicBlock * child,int unvisited)772 static int Push(SpecialRPOStackFrame* stack, int depth, BasicBlock* child,
773                 int unvisited) {
774   if (child->rpo_number_ == unvisited) {
775     stack[depth].block = child;
776     stack[depth].index = 0;
777     child->rpo_number_ = kBlockOnStack;
778     return depth + 1;
779   }
780   return depth;
781 }
782 
783 
784 // Computes loop membership from the backedges of the control flow graph.
ComputeLoopInfo(Zone * zone,SpecialRPOStackFrame * queue,int num_loops,int num_blocks,ZoneList<std::pair<BasicBlock *,int>> * backedges)785 static LoopInfo* ComputeLoopInfo(
786     Zone* zone, SpecialRPOStackFrame* queue, int num_loops, int num_blocks,
787     ZoneList<std::pair<BasicBlock*, int> >* backedges) {
788   LoopInfo* loops = zone->NewArray<LoopInfo>(num_loops);
789   memset(loops, 0, num_loops * sizeof(LoopInfo));
790 
791   // Compute loop membership starting from backedges.
792   // O(max(loop_depth) * max(|loop|)
793   for (int i = 0; i < backedges->length(); i++) {
794     BasicBlock* member = backedges->at(i).first;
795     BasicBlock* header = member->SuccessorAt(backedges->at(i).second);
796     int loop_num = header->loop_end_;
797     if (loops[loop_num].header == NULL) {
798       loops[loop_num].header = header;
799       loops[loop_num].members = new (zone) BitVector(num_blocks, zone);
800     }
801 
802     int queue_length = 0;
803     if (member != header) {
804       // As long as the header doesn't have a backedge to itself,
805       // Push the member onto the queue and process its predecessors.
806       if (!loops[loop_num].members->Contains(member->id())) {
807         loops[loop_num].members->Add(member->id());
808       }
809       queue[queue_length++].block = member;
810     }
811 
812     // Propagate loop membership backwards. All predecessors of M up to the
813     // loop header H are members of the loop too. O(|blocks between M and H|).
814     while (queue_length > 0) {
815       BasicBlock* block = queue[--queue_length].block;
816       for (int i = 0; i < block->PredecessorCount(); i++) {
817         BasicBlock* pred = block->PredecessorAt(i);
818         if (pred != header) {
819           if (!loops[loop_num].members->Contains(pred->id())) {
820             loops[loop_num].members->Add(pred->id());
821             queue[queue_length++].block = pred;
822           }
823         }
824       }
825     }
826   }
827   return loops;
828 }
829 
830 
831 #if DEBUG
PrintRPO(int num_loops,LoopInfo * loops,BasicBlockVector * order)832 static void PrintRPO(int num_loops, LoopInfo* loops, BasicBlockVector* order) {
833   PrintF("-- RPO with %d loops ", num_loops);
834   if (num_loops > 0) {
835     PrintF("(");
836     for (int i = 0; i < num_loops; i++) {
837       if (i > 0) PrintF(" ");
838       PrintF("B%d", loops[i].header->id());
839     }
840     PrintF(") ");
841   }
842   PrintF("-- \n");
843 
844   for (int i = 0; i < static_cast<int>(order->size()); i++) {
845     BasicBlock* block = (*order)[i];
846     int bid = block->id();
847     PrintF("%5d:", i);
848     for (int i = 0; i < num_loops; i++) {
849       bool membership = loops[i].members->Contains(bid);
850       bool range = loops[i].header->LoopContains(block);
851       PrintF(membership ? " |" : "  ");
852       PrintF(range ? "x" : " ");
853     }
854     PrintF("  B%d: ", bid);
855     if (block->loop_end_ >= 0) {
856       PrintF(" range: [%d, %d)", block->rpo_number_, block->loop_end_);
857     }
858     PrintF("\n");
859   }
860 }
861 
862 
VerifySpecialRPO(int num_loops,LoopInfo * loops,BasicBlockVector * order)863 static void VerifySpecialRPO(int num_loops, LoopInfo* loops,
864                              BasicBlockVector* order) {
865   DCHECK(order->size() > 0);
866   DCHECK((*order)[0]->id() == 0);  // entry should be first.
867 
868   for (int i = 0; i < num_loops; i++) {
869     LoopInfo* loop = &loops[i];
870     BasicBlock* header = loop->header;
871 
872     DCHECK(header != NULL);
873     DCHECK(header->rpo_number_ >= 0);
874     DCHECK(header->rpo_number_ < static_cast<int>(order->size()));
875     DCHECK(header->loop_end_ >= 0);
876     DCHECK(header->loop_end_ <= static_cast<int>(order->size()));
877     DCHECK(header->loop_end_ > header->rpo_number_);
878 
879     // Verify the start ... end list relationship.
880     int links = 0;
881     BlockList* l = loop->start;
882     DCHECK(l != NULL && l->block == header);
883     bool end_found;
884     while (true) {
885       if (l == NULL || l == loop->end) {
886         end_found = (loop->end == l);
887         break;
888       }
889       // The list should be in same order as the final result.
890       DCHECK(l->block->rpo_number_ == links + loop->header->rpo_number_);
891       links++;
892       l = l->next;
893       DCHECK(links < static_cast<int>(2 * order->size()));  // cycle?
894     }
895     DCHECK(links > 0);
896     DCHECK(links == (header->loop_end_ - header->rpo_number_));
897     DCHECK(end_found);
898 
899     // Check the contiguousness of loops.
900     int count = 0;
901     for (int j = 0; j < static_cast<int>(order->size()); j++) {
902       BasicBlock* block = order->at(j);
903       DCHECK(block->rpo_number_ == j);
904       if (j < header->rpo_number_ || j >= header->loop_end_) {
905         DCHECK(!loop->members->Contains(block->id()));
906       } else {
907         if (block == header) {
908           DCHECK(!loop->members->Contains(block->id()));
909         } else {
910           DCHECK(loop->members->Contains(block->id()));
911         }
912         count++;
913       }
914     }
915     DCHECK(links == count);
916   }
917 }
918 #endif  // DEBUG
919 
920 
921 // Compute the special reverse-post-order block ordering, which is essentially
922 // a RPO of the graph where loop bodies are contiguous. Properties:
923 // 1. If block A is a predecessor of B, then A appears before B in the order,
924 //    unless B is a loop header and A is in the loop headed at B
925 //    (i.e. A -> B is a backedge).
926 // => If block A dominates block B, then A appears before B in the order.
927 // => If block A is a loop header, A appears before all blocks in the loop
928 //    headed at A.
929 // 2. All loops are contiguous in the order (i.e. no intervening blocks that
930 //    do not belong to the loop.)
931 // Note a simple RPO traversal satisfies (1) but not (3).
ComputeSpecialRPO(Schedule * schedule)932 BasicBlockVector* Scheduler::ComputeSpecialRPO(Schedule* schedule) {
933   Zone tmp_zone(schedule->zone()->isolate());
934   Zone* zone = &tmp_zone;
935   Trace("------------- COMPUTING SPECIAL RPO ---------------\n");
936   // RPO should not have been computed for this schedule yet.
937   CHECK_EQ(kBlockUnvisited1, schedule->start()->rpo_number_);
938   CHECK_EQ(0, static_cast<int>(schedule->rpo_order_.size()));
939 
940   // Perform an iterative RPO traversal using an explicit stack,
941   // recording backedges that form cycles. O(|B|).
942   ZoneList<std::pair<BasicBlock*, int> > backedges(1, zone);
943   SpecialRPOStackFrame* stack =
944       zone->NewArray<SpecialRPOStackFrame>(schedule->BasicBlockCount());
945   BasicBlock* entry = schedule->start();
946   BlockList* order = NULL;
947   int stack_depth = Push(stack, 0, entry, kBlockUnvisited1);
948   int num_loops = 0;
949 
950   while (stack_depth > 0) {
951     int current = stack_depth - 1;
952     SpecialRPOStackFrame* frame = stack + current;
953 
954     if (frame->index < frame->block->SuccessorCount()) {
955       // Process the next successor.
956       BasicBlock* succ = frame->block->SuccessorAt(frame->index++);
957       if (succ->rpo_number_ == kBlockVisited1) continue;
958       if (succ->rpo_number_ == kBlockOnStack) {
959         // The successor is on the stack, so this is a backedge (cycle).
960         backedges.Add(
961             std::pair<BasicBlock*, int>(frame->block, frame->index - 1), zone);
962         if (succ->loop_end_ < 0) {
963           // Assign a new loop number to the header if it doesn't have one.
964           succ->loop_end_ = num_loops++;
965         }
966       } else {
967         // Push the successor onto the stack.
968         DCHECK(succ->rpo_number_ == kBlockUnvisited1);
969         stack_depth = Push(stack, stack_depth, succ, kBlockUnvisited1);
970       }
971     } else {
972       // Finished with all successors; pop the stack and add the block.
973       order = order->Add(zone, frame->block);
974       frame->block->rpo_number_ = kBlockVisited1;
975       stack_depth--;
976     }
977   }
978 
979   // If no loops were encountered, then the order we computed was correct.
980   LoopInfo* loops = NULL;
981   if (num_loops != 0) {
982     // Otherwise, compute the loop information from the backedges in order
983     // to perform a traversal that groups loop bodies together.
984     loops = ComputeLoopInfo(zone, stack, num_loops, schedule->BasicBlockCount(),
985                             &backedges);
986 
987     // Initialize the "loop stack". Note the entry could be a loop header.
988     LoopInfo* loop = entry->IsLoopHeader() ? &loops[entry->loop_end_] : NULL;
989     order = NULL;
990 
991     // Perform an iterative post-order traversal, visiting loop bodies before
992     // edges that lead out of loops. Visits each block once, but linking loop
993     // sections together is linear in the loop size, so overall is
994     // O(|B| + max(loop_depth) * max(|loop|))
995     stack_depth = Push(stack, 0, entry, kBlockUnvisited2);
996     while (stack_depth > 0) {
997       SpecialRPOStackFrame* frame = stack + (stack_depth - 1);
998       BasicBlock* block = frame->block;
999       BasicBlock* succ = NULL;
1000 
1001       if (frame->index < block->SuccessorCount()) {
1002         // Process the next normal successor.
1003         succ = block->SuccessorAt(frame->index++);
1004       } else if (block->IsLoopHeader()) {
1005         // Process additional outgoing edges from the loop header.
1006         if (block->rpo_number_ == kBlockOnStack) {
1007           // Finish the loop body the first time the header is left on the
1008           // stack.
1009           DCHECK(loop != NULL && loop->header == block);
1010           loop->start = order->Add(zone, block);
1011           order = loop->end;
1012           block->rpo_number_ = kBlockVisited2;
1013           // Pop the loop stack and continue visiting outgoing edges within the
1014           // the context of the outer loop, if any.
1015           loop = loop->prev;
1016           // We leave the loop header on the stack; the rest of this iteration
1017           // and later iterations will go through its outgoing edges list.
1018         }
1019 
1020         // Use the next outgoing edge if there are any.
1021         int outgoing_index = frame->index - block->SuccessorCount();
1022         LoopInfo* info = &loops[block->loop_end_];
1023         DCHECK(loop != info);
1024         if (info->outgoing != NULL &&
1025             outgoing_index < info->outgoing->length()) {
1026           succ = info->outgoing->at(outgoing_index);
1027           frame->index++;
1028         }
1029       }
1030 
1031       if (succ != NULL) {
1032         // Process the next successor.
1033         if (succ->rpo_number_ == kBlockOnStack) continue;
1034         if (succ->rpo_number_ == kBlockVisited2) continue;
1035         DCHECK(succ->rpo_number_ == kBlockUnvisited2);
1036         if (loop != NULL && !loop->members->Contains(succ->id())) {
1037           // The successor is not in the current loop or any nested loop.
1038           // Add it to the outgoing edges of this loop and visit it later.
1039           loop->AddOutgoing(zone, succ);
1040         } else {
1041           // Push the successor onto the stack.
1042           stack_depth = Push(stack, stack_depth, succ, kBlockUnvisited2);
1043           if (succ->IsLoopHeader()) {
1044             // Push the inner loop onto the loop stack.
1045             DCHECK(succ->loop_end_ >= 0 && succ->loop_end_ < num_loops);
1046             LoopInfo* next = &loops[succ->loop_end_];
1047             next->end = order;
1048             next->prev = loop;
1049             loop = next;
1050           }
1051         }
1052       } else {
1053         // Finished with all successors of the current block.
1054         if (block->IsLoopHeader()) {
1055           // If we are going to pop a loop header, then add its entire body.
1056           LoopInfo* info = &loops[block->loop_end_];
1057           for (BlockList* l = info->start; true; l = l->next) {
1058             if (l->next == info->end) {
1059               l->next = order;
1060               info->end = order;
1061               break;
1062             }
1063           }
1064           order = info->start;
1065         } else {
1066           // Pop a single node off the stack and add it to the order.
1067           order = order->Add(zone, block);
1068           block->rpo_number_ = kBlockVisited2;
1069         }
1070         stack_depth--;
1071       }
1072     }
1073   }
1074 
1075   // Construct the final order from the list.
1076   BasicBlockVector* final_order = &schedule->rpo_order_;
1077   order->Serialize(final_order);
1078 
1079   // Compute the correct loop header for every block and set the correct loop
1080   // ends.
1081   LoopInfo* current_loop = NULL;
1082   BasicBlock* current_header = NULL;
1083   int loop_depth = 0;
1084   for (BasicBlockVectorIter i = final_order->begin(); i != final_order->end();
1085        ++i) {
1086     BasicBlock* current = *i;
1087     current->loop_header_ = current_header;
1088     if (current->IsLoopHeader()) {
1089       loop_depth++;
1090       current_loop = &loops[current->loop_end_];
1091       BlockList* end = current_loop->end;
1092       current->loop_end_ = end == NULL ? static_cast<int>(final_order->size())
1093                                        : end->block->rpo_number_;
1094       current_header = current_loop->header;
1095       Trace("B%d is a loop header, increment loop depth to %d\n", current->id(),
1096             loop_depth);
1097     } else {
1098       while (current_header != NULL &&
1099              current->rpo_number_ >= current_header->loop_end_) {
1100         DCHECK(current_header->IsLoopHeader());
1101         DCHECK(current_loop != NULL);
1102         current_loop = current_loop->prev;
1103         current_header = current_loop == NULL ? NULL : current_loop->header;
1104         --loop_depth;
1105       }
1106     }
1107     current->loop_depth_ = loop_depth;
1108     if (current->loop_header_ == NULL) {
1109       Trace("B%d is not in a loop (depth == %d)\n", current->id(),
1110             current->loop_depth_);
1111     } else {
1112       Trace("B%d has loop header B%d, (depth == %d)\n", current->id(),
1113             current->loop_header_->id(), current->loop_depth_);
1114     }
1115   }
1116 
1117 #if DEBUG
1118   if (FLAG_trace_turbo_scheduler) PrintRPO(num_loops, loops, final_order);
1119   VerifySpecialRPO(num_loops, loops, final_order);
1120 #endif
1121   return final_order;
1122 }
1123 }
1124 }
1125 }  // namespace v8::internal::compiler
1126