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
17namespace v8 {
18namespace internal {
19namespace compiler {
20
21static 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.
35class CFGBuilder {
36 public:
37  Scheduler* scheduler_;
38  Schedule* schedule_;
39  ZoneQueue<Node*> queue_;
40  NodeVector control_;
41
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.
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
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
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
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
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
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
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
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
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
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
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
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
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
221Scheduler::SchedulerData Scheduler::DefaultSchedulerData() {
222  SchedulerData def = {0, 0, false, false, kUnknown};
223  return def;
224}
225
226
227Scheduler::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
237Schedule* 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
261Scheduler::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
298void 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
307BasicBlock* 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
322void 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
354class ScheduleEarlyNodeVisitor : public NullNodeVisitor {
355 public:
356  explicit ScheduleEarlyNodeVisitor(Scheduler* scheduler)
357      : has_changed_rpo_constraints_(true),
358        scheduler_(scheduler),
359        schedule_(scheduler->schedule_) {}
360
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
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
409void 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
424class PrepareUsesVisitor : public NullNodeVisitor {
425 public:
426  explicit PrepareUsesVisitor(Scheduler* scheduler)
427      : scheduler_(scheduler), schedule_(scheduler->schedule_) {}
428
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
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
469void 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
478class ScheduleLateNodeVisitor : public NullNodeVisitor {
479 public:
480  explicit ScheduleLateNodeVisitor(Scheduler* scheduler)
481      : scheduler_(scheduler), schedule_(scheduler_->schedule_) {}
482
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:
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
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
596void 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
630bool 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
662void 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:
727static const int kBlockOnStack = -2;
728static const int kBlockVisited1 = -3;
729static const int kBlockVisited2 = -4;
730static const int kBlockUnvisited1 = -1;
731static const int kBlockUnvisited2 = kBlockVisited1;
732
733struct SpecialRPOStackFrame {
734  BasicBlock* block;
735  int index;
736};
737
738struct BlockList {
739  BasicBlock* block;
740  BlockList* next;
741
742  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
749  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
757struct LoopInfo {
758  BasicBlock* header;
759  ZoneList<BasicBlock*>* outgoing;
760  BitVector* members;
761  LoopInfo* prev;
762  BlockList* end;
763  BlockList* start;
764
765  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
772static 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.
785static 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
832static 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
863static 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).
932BasicBlockVector* 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