• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "src/compiler/scheduler.h"
6 
7 #include <iomanip>
8 
9 #include "src/base/iterator.h"
10 #include "src/builtins/profile-data-reader.h"
11 #include "src/codegen/tick-counter.h"
12 #include "src/compiler/common-operator.h"
13 #include "src/compiler/control-equivalence.h"
14 #include "src/compiler/graph.h"
15 #include "src/compiler/node-marker.h"
16 #include "src/compiler/node-properties.h"
17 #include "src/compiler/node.h"
18 #include "src/utils/bit-vector.h"
19 #include "src/zone/zone-containers.h"
20 
21 namespace v8 {
22 namespace internal {
23 namespace compiler {
24 
25 #define TRACE(...)                                       \
26   do {                                                   \
27     if (FLAG_trace_turbo_scheduler) PrintF(__VA_ARGS__); \
28   } while (false)
29 
Scheduler(Zone * zone,Graph * graph,Schedule * schedule,Flags flags,size_t node_count_hint,TickCounter * tick_counter,const ProfileDataFromFile * profile_data)30 Scheduler::Scheduler(Zone* zone, Graph* graph, Schedule* schedule, Flags flags,
31                      size_t node_count_hint, TickCounter* tick_counter,
32                      const ProfileDataFromFile* profile_data)
33     : zone_(zone),
34       graph_(graph),
35       schedule_(schedule),
36       flags_(flags),
37       scheduled_nodes_(zone),
38       schedule_root_nodes_(zone),
39       schedule_queue_(zone),
40       node_data_(zone),
41       tick_counter_(tick_counter),
42       profile_data_(profile_data),
43       common_dominator_cache_(zone) {
44   node_data_.reserve(node_count_hint);
45   node_data_.resize(graph->NodeCount(), DefaultSchedulerData());
46 }
47 
ComputeSchedule(Zone * zone,Graph * graph,Flags flags,TickCounter * tick_counter,const ProfileDataFromFile * profile_data)48 Schedule* Scheduler::ComputeSchedule(Zone* zone, Graph* graph, Flags flags,
49                                      TickCounter* tick_counter,
50                                      const ProfileDataFromFile* profile_data) {
51   Zone* schedule_zone =
52       (flags & Scheduler::kTempSchedule) ? zone : graph->zone();
53 
54   // Reserve 10% more space for nodes if node splitting is enabled to try to
55   // avoid resizing the vector since that would triple its zone memory usage.
56   float node_hint_multiplier = (flags & Scheduler::kSplitNodes) ? 1.1 : 1;
57   size_t node_count_hint = node_hint_multiplier * graph->NodeCount();
58 
59   Schedule* schedule =
60       schedule_zone->New<Schedule>(schedule_zone, node_count_hint);
61   Scheduler scheduler(zone, graph, schedule, flags, node_count_hint,
62                       tick_counter, profile_data);
63 
64   scheduler.BuildCFG();
65   scheduler.ComputeSpecialRPONumbering();
66   scheduler.GenerateDominatorTree();
67 
68   scheduler.PrepareUses();
69   scheduler.ScheduleEarly();
70   scheduler.ScheduleLate();
71 
72   scheduler.SealFinalSchedule();
73 
74   return schedule;
75 }
76 
DefaultSchedulerData()77 Scheduler::SchedulerData Scheduler::DefaultSchedulerData() {
78   SchedulerData def = {schedule_->start(), 0, kUnknown};
79   return def;
80 }
81 
82 
GetData(Node * node)83 Scheduler::SchedulerData* Scheduler::GetData(Node* node) {
84   return &node_data_[node->id()];
85 }
86 
InitializePlacement(Node * node)87 Scheduler::Placement Scheduler::InitializePlacement(Node* node) {
88   SchedulerData* data = GetData(node);
89   if (data->placement_ == kFixed) {
90     // Nothing to do for control nodes that have been already fixed in
91     // the schedule.
92     return data->placement_;
93   }
94   DCHECK_EQ(kUnknown, data->placement_);
95   switch (node->opcode()) {
96     case IrOpcode::kParameter:
97     case IrOpcode::kOsrValue:
98       // Parameters and OSR values are always fixed to the start block.
99       data->placement_ = kFixed;
100       break;
101     case IrOpcode::kPhi:
102     case IrOpcode::kEffectPhi: {
103       // Phis and effect phis are fixed if their control inputs are, whereas
104       // otherwise they are coupled to a floating control node.
105       Placement p = GetPlacement(NodeProperties::GetControlInput(node));
106       data->placement_ = (p == kFixed ? kFixed : kCoupled);
107       break;
108     }
109     default:
110       // Control nodes that were not control-reachable from end may float.
111       data->placement_ = kSchedulable;
112       break;
113   }
114   return data->placement_;
115 }
116 
GetPlacement(Node * node)117 Scheduler::Placement Scheduler::GetPlacement(Node* node) {
118   return GetData(node)->placement_;
119 }
120 
IsLive(Node * node)121 bool Scheduler::IsLive(Node* node) { return GetPlacement(node) != kUnknown; }
122 
UpdatePlacement(Node * node,Placement placement)123 void Scheduler::UpdatePlacement(Node* node, Placement placement) {
124   SchedulerData* data = GetData(node);
125   if (data->placement_ == kUnknown) {
126     // We only update control nodes from {kUnknown} to {kFixed}.  Ideally, we
127     // should check that {node} is a control node (including exceptional calls),
128     // but that is expensive.
129     DCHECK_EQ(Scheduler::kFixed, placement);
130     data->placement_ = placement;
131     return;
132   }
133 
134   switch (node->opcode()) {
135     case IrOpcode::kParameter:
136       // Parameters are fixed once and for all.
137       UNREACHABLE();
138     case IrOpcode::kPhi:
139     case IrOpcode::kEffectPhi: {
140       // Phis and effect phis are coupled to their respective blocks.
141       DCHECK_EQ(Scheduler::kCoupled, data->placement_);
142       DCHECK_EQ(Scheduler::kFixed, placement);
143       Node* control = NodeProperties::GetControlInput(node);
144       BasicBlock* block = schedule_->block(control);
145       schedule_->AddNode(block, node);
146       break;
147     }
148 #define DEFINE_CONTROL_CASE(V) case IrOpcode::k##V:
149       CONTROL_OP_LIST(DEFINE_CONTROL_CASE)
150 #undef DEFINE_CONTROL_CASE
151       {
152         // Control nodes force coupled uses to be placed.
153         for (auto use : node->uses()) {
154           if (GetPlacement(use) == Scheduler::kCoupled) {
155             DCHECK_EQ(node, NodeProperties::GetControlInput(use));
156             UpdatePlacement(use, placement);
157           }
158       }
159       break;
160     }
161     default:
162       DCHECK_EQ(Scheduler::kSchedulable, data->placement_);
163       DCHECK_EQ(Scheduler::kScheduled, placement);
164       break;
165   }
166   // Reduce the use count of the node's inputs to potentially make them
167   // schedulable. If all the uses of a node have been scheduled, then the node
168   // itself can be scheduled.
169   base::Optional<int> coupled_control_edge = GetCoupledControlEdge(node);
170   for (Edge const edge : node->input_edges()) {
171     DCHECK_EQ(node, edge.from());
172     if (edge.index() != coupled_control_edge) {
173       DecrementUnscheduledUseCount(edge.to(), node);
174     }
175   }
176   data->placement_ = placement;
177 }
178 
GetCoupledControlEdge(Node * node)179 base::Optional<int> Scheduler::GetCoupledControlEdge(Node* node) {
180   if (GetPlacement(node) == kCoupled) {
181     return NodeProperties::FirstControlIndex(node);
182   }
183   return {};
184 }
185 
IncrementUnscheduledUseCount(Node * node,Node * from)186 void Scheduler::IncrementUnscheduledUseCount(Node* node, Node* from) {
187   // Tracking use counts for fixed nodes is useless.
188   if (GetPlacement(node) == kFixed) return;
189 
190   // Use count for coupled nodes is summed up on their control.
191   if (GetPlacement(node) == kCoupled) {
192     node = NodeProperties::GetControlInput(node);
193     DCHECK_NE(GetPlacement(node), Placement::kFixed);
194     DCHECK_NE(GetPlacement(node), Placement::kCoupled);
195   }
196 
197   ++(GetData(node)->unscheduled_count_);
198   if (FLAG_trace_turbo_scheduler) {
199     TRACE("  Use count of #%d:%s (used by #%d:%s)++ = %d\n", node->id(),
200           node->op()->mnemonic(), from->id(), from->op()->mnemonic(),
201           GetData(node)->unscheduled_count_);
202   }
203 }
204 
DecrementUnscheduledUseCount(Node * node,Node * from)205 void Scheduler::DecrementUnscheduledUseCount(Node* node, Node* from) {
206   // Tracking use counts for fixed nodes is useless.
207   if (GetPlacement(node) == kFixed) return;
208 
209   // Use count for coupled nodes is summed up on their control.
210   if (GetPlacement(node) == kCoupled) {
211     node = NodeProperties::GetControlInput(node);
212     DCHECK_NE(GetPlacement(node), Placement::kFixed);
213     DCHECK_NE(GetPlacement(node), Placement::kCoupled);
214   }
215 
216   DCHECK_LT(0, GetData(node)->unscheduled_count_);
217   --(GetData(node)->unscheduled_count_);
218   if (FLAG_trace_turbo_scheduler) {
219     TRACE("  Use count of #%d:%s (used by #%d:%s)-- = %d\n", node->id(),
220           node->op()->mnemonic(), from->id(), from->op()->mnemonic(),
221           GetData(node)->unscheduled_count_);
222   }
223   if (GetData(node)->unscheduled_count_ == 0) {
224     TRACE("    newly eligible #%d:%s\n", node->id(), node->op()->mnemonic());
225     schedule_queue_.push(node);
226   }
227 }
228 
229 // -----------------------------------------------------------------------------
230 // Phase 1: Build control-flow graph.
231 
232 
233 // Internal class to build a control flow graph (i.e the basic blocks and edges
234 // between them within a Schedule) from the node graph. Visits control edges of
235 // the graph backwards from an end node in order to find the connected control
236 // subgraph, needed for scheduling.
237 class CFGBuilder : public ZoneObject {
238  public:
CFGBuilder(Zone * zone,Scheduler * scheduler)239   CFGBuilder(Zone* zone, Scheduler* scheduler)
240       : zone_(zone),
241         scheduler_(scheduler),
242         schedule_(scheduler->schedule_),
243         queued_(scheduler->graph_, 2),
244         queue_(zone),
245         control_(zone),
246         component_entry_(nullptr),
247         component_start_(nullptr),
248         component_end_(nullptr) {}
249 
250   // Run the control flow graph construction algorithm by walking the graph
251   // backwards from end through control edges, building and connecting the
252   // basic blocks for control nodes.
Run()253   void Run() {
254     ResetDataStructures();
255     Queue(scheduler_->graph_->end());
256 
257     while (!queue_.empty()) {  // Breadth-first backwards traversal.
258       scheduler_->tick_counter_->TickAndMaybeEnterSafepoint();
259       Node* node = queue_.front();
260       queue_.pop();
261       int max = NodeProperties::PastControlIndex(node);
262       for (int i = NodeProperties::FirstControlIndex(node); i < max; i++) {
263         Queue(node->InputAt(i));
264       }
265     }
266 
267     for (NodeVector::iterator i = control_.begin(); i != control_.end(); ++i) {
268       ConnectBlocks(*i);  // Connect block to its predecessor/successors.
269     }
270   }
271 
272   // Run the control flow graph construction for a minimal control-connected
273   // component ending in {exit} and merge that component into an existing
274   // control flow graph at the bottom of {block}.
Run(BasicBlock * block,Node * exit)275   void Run(BasicBlock* block, Node* exit) {
276     ResetDataStructures();
277     Queue(exit);
278 
279     component_entry_ = nullptr;
280     component_start_ = block;
281     component_end_ = schedule_->block(exit);
282     scheduler_->equivalence_->Run(exit);
283     while (!queue_.empty()) {  // Breadth-first backwards traversal.
284       scheduler_->tick_counter_->TickAndMaybeEnterSafepoint();
285       Node* node = queue_.front();
286       queue_.pop();
287 
288       // Use control dependence equivalence to find a canonical single-entry
289       // single-exit region that makes up a minimal component to be scheduled.
290       if (IsSingleEntrySingleExitRegion(node, exit)) {
291         TRACE("Found SESE at #%d:%s\n", node->id(), node->op()->mnemonic());
292         DCHECK(!component_entry_);
293         component_entry_ = node;
294         continue;
295       }
296 
297       int max = NodeProperties::PastControlIndex(node);
298       for (int i = NodeProperties::FirstControlIndex(node); i < max; i++) {
299         Queue(node->InputAt(i));
300       }
301     }
302     DCHECK(component_entry_);
303 
304     for (NodeVector::iterator i = control_.begin(); i != control_.end(); ++i) {
305       ConnectBlocks(*i);  // Connect block to its predecessor/successors.
306     }
307   }
308 
309  private:
310   friend class ScheduleLateNodeVisitor;
311   friend class Scheduler;
312 
FixNode(BasicBlock * block,Node * node)313   void FixNode(BasicBlock* block, Node* node) {
314     schedule_->AddNode(block, node);
315     scheduler_->UpdatePlacement(node, Scheduler::kFixed);
316   }
317 
Queue(Node * node)318   void Queue(Node* node) {
319     // Mark the connected control nodes as they are queued.
320     if (!queued_.Get(node)) {
321       BuildBlocks(node);
322       queue_.push(node);
323       queued_.Set(node, true);
324       control_.push_back(node);
325     }
326   }
327 
BuildBlocks(Node * node)328   void BuildBlocks(Node* node) {
329     switch (node->opcode()) {
330       case IrOpcode::kEnd:
331         FixNode(schedule_->end(), node);
332         break;
333       case IrOpcode::kStart:
334         FixNode(schedule_->start(), node);
335         break;
336       case IrOpcode::kLoop:
337       case IrOpcode::kMerge:
338         BuildBlockForNode(node);
339         break;
340       case IrOpcode::kTerminate: {
341         // Put Terminate in the loop to which it refers.
342         Node* loop = NodeProperties::GetControlInput(node);
343         BasicBlock* block = BuildBlockForNode(loop);
344         FixNode(block, node);
345         break;
346       }
347       case IrOpcode::kBranch:
348       case IrOpcode::kSwitch:
349         BuildBlocksForSuccessors(node);
350         break;
351 #define BUILD_BLOCK_JS_CASE(Name, ...) case IrOpcode::k##Name:
352         JS_OP_LIST(BUILD_BLOCK_JS_CASE)
353 // JS opcodes are just like calls => fall through.
354 #undef BUILD_BLOCK_JS_CASE
355       case IrOpcode::kCall:
356         if (NodeProperties::IsExceptionalCall(node)) {
357           BuildBlocksForSuccessors(node);
358         }
359         break;
360       default:
361         break;
362     }
363   }
364 
ConnectBlocks(Node * node)365   void ConnectBlocks(Node* node) {
366     switch (node->opcode()) {
367       case IrOpcode::kLoop:
368       case IrOpcode::kMerge:
369         ConnectMerge(node);
370         break;
371       case IrOpcode::kBranch:
372         scheduler_->UpdatePlacement(node, Scheduler::kFixed);
373         ConnectBranch(node);
374         break;
375       case IrOpcode::kSwitch:
376         scheduler_->UpdatePlacement(node, Scheduler::kFixed);
377         ConnectSwitch(node);
378         break;
379       case IrOpcode::kDeoptimize:
380         scheduler_->UpdatePlacement(node, Scheduler::kFixed);
381         ConnectDeoptimize(node);
382         break;
383       case IrOpcode::kTailCall:
384         scheduler_->UpdatePlacement(node, Scheduler::kFixed);
385         ConnectTailCall(node);
386         break;
387       case IrOpcode::kReturn:
388         scheduler_->UpdatePlacement(node, Scheduler::kFixed);
389         ConnectReturn(node);
390         break;
391       case IrOpcode::kThrow:
392         scheduler_->UpdatePlacement(node, Scheduler::kFixed);
393         ConnectThrow(node);
394         break;
395 #define CONNECT_BLOCK_JS_CASE(Name, ...) case IrOpcode::k##Name:
396         JS_OP_LIST(CONNECT_BLOCK_JS_CASE)
397 // JS opcodes are just like calls => fall through.
398 #undef CONNECT_BLOCK_JS_CASE
399       case IrOpcode::kCall:
400         if (NodeProperties::IsExceptionalCall(node)) {
401           scheduler_->UpdatePlacement(node, Scheduler::kFixed);
402           ConnectCall(node);
403         }
404         break;
405       default:
406         break;
407     }
408   }
409 
BuildBlockForNode(Node * node)410   BasicBlock* BuildBlockForNode(Node* node) {
411     BasicBlock* block = schedule_->block(node);
412     if (block == nullptr) {
413       block = schedule_->NewBasicBlock();
414       TRACE("Create block id:%d for #%d:%s\n", block->id().ToInt(), node->id(),
415             node->op()->mnemonic());
416       FixNode(block, node);
417     }
418     return block;
419   }
420 
BuildBlocksForSuccessors(Node * node)421   void BuildBlocksForSuccessors(Node* node) {
422     size_t const successor_cnt = node->op()->ControlOutputCount();
423     Node** successors = zone_->NewArray<Node*>(successor_cnt);
424     NodeProperties::CollectControlProjections(node, successors, successor_cnt);
425     for (size_t index = 0; index < successor_cnt; ++index) {
426       BuildBlockForNode(successors[index]);
427     }
428   }
429 
CollectSuccessorBlocks(Node * node,BasicBlock ** successor_blocks,size_t successor_cnt)430   void CollectSuccessorBlocks(Node* node, BasicBlock** successor_blocks,
431                               size_t successor_cnt) {
432     Node** successors = reinterpret_cast<Node**>(successor_blocks);
433     NodeProperties::CollectControlProjections(node, successors, successor_cnt);
434     for (size_t index = 0; index < successor_cnt; ++index) {
435       successor_blocks[index] = schedule_->block(successors[index]);
436     }
437   }
438 
FindPredecessorBlock(Node * node)439   BasicBlock* FindPredecessorBlock(Node* node) {
440     BasicBlock* predecessor_block = nullptr;
441     while (true) {
442       predecessor_block = schedule_->block(node);
443       if (predecessor_block != nullptr) break;
444       node = NodeProperties::GetControlInput(node);
445     }
446     return predecessor_block;
447   }
448 
ConnectCall(Node * call)449   void ConnectCall(Node* call) {
450     BasicBlock* successor_blocks[2];
451     CollectSuccessorBlocks(call, successor_blocks, arraysize(successor_blocks));
452 
453     // Consider the exception continuation to be deferred.
454     successor_blocks[1]->set_deferred(true);
455 
456     Node* call_control = NodeProperties::GetControlInput(call);
457     BasicBlock* call_block = FindPredecessorBlock(call_control);
458     TraceConnect(call, call_block, successor_blocks[0]);
459     TraceConnect(call, call_block, successor_blocks[1]);
460     schedule_->AddCall(call_block, call, successor_blocks[0],
461                        successor_blocks[1]);
462   }
463 
ConnectBranch(Node * branch)464   void ConnectBranch(Node* branch) {
465     BasicBlock* successor_blocks[2];
466     CollectSuccessorBlocks(branch, successor_blocks,
467                            arraysize(successor_blocks));
468 
469     BranchHint hint_from_profile = BranchHint::kNone;
470     if (const ProfileDataFromFile* profile_data = scheduler_->profile_data()) {
471       double block_zero_count =
472           profile_data->GetCounter(successor_blocks[0]->id().ToSize());
473       double block_one_count =
474           profile_data->GetCounter(successor_blocks[1]->id().ToSize());
475       // If a branch is visited a non-trivial number of times and substantially
476       // more often than its alternative, then mark it as likely.
477       constexpr double kMinimumCount = 100000;
478       constexpr double kThresholdRatio = 4000;
479       if (block_zero_count > kMinimumCount &&
480           block_zero_count / kThresholdRatio > block_one_count) {
481         hint_from_profile = BranchHint::kTrue;
482       } else if (block_one_count > kMinimumCount &&
483                  block_one_count / kThresholdRatio > block_zero_count) {
484         hint_from_profile = BranchHint::kFalse;
485       }
486     }
487 
488     // Consider branch hints.
489     switch (hint_from_profile) {
490       case BranchHint::kNone:
491         switch (BranchHintOf(branch->op())) {
492           case BranchHint::kNone:
493             break;
494           case BranchHint::kTrue:
495             successor_blocks[1]->set_deferred(true);
496             break;
497           case BranchHint::kFalse:
498             successor_blocks[0]->set_deferred(true);
499             break;
500         }
501         break;
502       case BranchHint::kTrue:
503         successor_blocks[1]->set_deferred(true);
504         break;
505       case BranchHint::kFalse:
506         successor_blocks[0]->set_deferred(true);
507         break;
508     }
509 
510     if (hint_from_profile != BranchHint::kNone &&
511         BranchHintOf(branch->op()) != BranchHint::kNone &&
512         hint_from_profile != BranchHintOf(branch->op())) {
513       PrintF("Warning: profiling data overrode manual branch hint.\n");
514     }
515 
516     if (branch == component_entry_) {
517       TraceConnect(branch, component_start_, successor_blocks[0]);
518       TraceConnect(branch, component_start_, successor_blocks[1]);
519       schedule_->InsertBranch(component_start_, component_end_, branch,
520                               successor_blocks[0], successor_blocks[1]);
521     } else {
522       Node* branch_control = NodeProperties::GetControlInput(branch);
523       BasicBlock* branch_block = FindPredecessorBlock(branch_control);
524       TraceConnect(branch, branch_block, successor_blocks[0]);
525       TraceConnect(branch, branch_block, successor_blocks[1]);
526       schedule_->AddBranch(branch_block, branch, successor_blocks[0],
527                            successor_blocks[1]);
528     }
529   }
530 
ConnectSwitch(Node * sw)531   void ConnectSwitch(Node* sw) {
532     size_t const successor_count = sw->op()->ControlOutputCount();
533     BasicBlock** successor_blocks =
534         zone_->NewArray<BasicBlock*>(successor_count);
535     CollectSuccessorBlocks(sw, successor_blocks, successor_count);
536 
537     if (sw == component_entry_) {
538       for (size_t index = 0; index < successor_count; ++index) {
539         TraceConnect(sw, component_start_, successor_blocks[index]);
540       }
541       schedule_->InsertSwitch(component_start_, component_end_, sw,
542                               successor_blocks, successor_count);
543     } else {
544       Node* switch_control = NodeProperties::GetControlInput(sw);
545       BasicBlock* switch_block = FindPredecessorBlock(switch_control);
546       for (size_t index = 0; index < successor_count; ++index) {
547         TraceConnect(sw, switch_block, successor_blocks[index]);
548       }
549       schedule_->AddSwitch(switch_block, sw, successor_blocks, successor_count);
550     }
551     for (size_t index = 0; index < successor_count; ++index) {
552       if (BranchHintOf(successor_blocks[index]->front()->op()) ==
553           BranchHint::kFalse) {
554         successor_blocks[index]->set_deferred(true);
555       }
556     }
557   }
558 
ConnectMerge(Node * merge)559   void ConnectMerge(Node* merge) {
560     // Don't connect the special merge at the end to its predecessors.
561     if (IsFinalMerge(merge)) return;
562 
563     BasicBlock* block = schedule_->block(merge);
564     DCHECK_NOT_NULL(block);
565     // For all of the merge's control inputs, add a goto at the end to the
566     // merge's basic block.
567     for (Node* const input : merge->inputs()) {
568       BasicBlock* predecessor_block = FindPredecessorBlock(input);
569       TraceConnect(merge, predecessor_block, block);
570       schedule_->AddGoto(predecessor_block, block);
571     }
572   }
573 
ConnectTailCall(Node * call)574   void ConnectTailCall(Node* call) {
575     Node* call_control = NodeProperties::GetControlInput(call);
576     BasicBlock* call_block = FindPredecessorBlock(call_control);
577     TraceConnect(call, call_block, nullptr);
578     schedule_->AddTailCall(call_block, call);
579   }
580 
ConnectReturn(Node * ret)581   void ConnectReturn(Node* ret) {
582     Node* return_control = NodeProperties::GetControlInput(ret);
583     BasicBlock* return_block = FindPredecessorBlock(return_control);
584     TraceConnect(ret, return_block, nullptr);
585     schedule_->AddReturn(return_block, ret);
586   }
587 
ConnectDeoptimize(Node * deopt)588   void ConnectDeoptimize(Node* deopt) {
589     Node* deoptimize_control = NodeProperties::GetControlInput(deopt);
590     BasicBlock* deoptimize_block = FindPredecessorBlock(deoptimize_control);
591     TraceConnect(deopt, deoptimize_block, nullptr);
592     schedule_->AddDeoptimize(deoptimize_block, deopt);
593   }
594 
ConnectThrow(Node * thr)595   void ConnectThrow(Node* thr) {
596     Node* throw_control = NodeProperties::GetControlInput(thr);
597     BasicBlock* throw_block = FindPredecessorBlock(throw_control);
598     TraceConnect(thr, throw_block, nullptr);
599     schedule_->AddThrow(throw_block, thr);
600   }
601 
TraceConnect(Node * node,BasicBlock * block,BasicBlock * succ)602   void TraceConnect(Node* node, BasicBlock* block, BasicBlock* succ) {
603     DCHECK_NOT_NULL(block);
604     if (succ == nullptr) {
605       TRACE("Connect #%d:%s, id:%d -> end\n", node->id(),
606             node->op()->mnemonic(), block->id().ToInt());
607     } else {
608       TRACE("Connect #%d:%s, id:%d -> id:%d\n", node->id(),
609             node->op()->mnemonic(), block->id().ToInt(), succ->id().ToInt());
610     }
611   }
612 
IsFinalMerge(Node * node)613   bool IsFinalMerge(Node* node) {
614     return (node->opcode() == IrOpcode::kMerge &&
615             node == scheduler_->graph_->end()->InputAt(0));
616   }
617 
IsSingleEntrySingleExitRegion(Node * entry,Node * exit) const618   bool IsSingleEntrySingleExitRegion(Node* entry, Node* exit) const {
619     size_t entry_class = scheduler_->equivalence_->ClassOf(entry);
620     size_t exit_class = scheduler_->equivalence_->ClassOf(exit);
621     return entry != exit && entry_class == exit_class;
622   }
623 
ResetDataStructures()624   void ResetDataStructures() {
625     control_.clear();
626     DCHECK(queue_.empty());
627     DCHECK(control_.empty());
628   }
629 
630   Zone* zone_;
631   Scheduler* scheduler_;
632   Schedule* schedule_;
633   NodeMarker<bool> queued_;      // Mark indicating whether node is queued.
634   ZoneQueue<Node*> queue_;       // Queue used for breadth-first traversal.
635   NodeVector control_;           // List of encountered control nodes.
636   Node* component_entry_;        // Component single-entry node.
637   BasicBlock* component_start_;  // Component single-entry block.
638   BasicBlock* component_end_;    // Component single-exit block.
639 };
640 
641 
BuildCFG()642 void Scheduler::BuildCFG() {
643   TRACE("--- CREATING CFG -------------------------------------------\n");
644 
645   // Instantiate a new control equivalence algorithm for the graph.
646   equivalence_ = zone_->New<ControlEquivalence>(zone_, graph_);
647 
648   // Build a control-flow graph for the main control-connected component that
649   // is being spanned by the graph's start and end nodes.
650   control_flow_builder_ = zone_->New<CFGBuilder>(zone_, this);
651   control_flow_builder_->Run();
652 
653   // Initialize per-block data.
654   // Reserve an extra 10% to avoid resizing vector when fusing floating control.
655   scheduled_nodes_.reserve(schedule_->BasicBlockCount() * 1.1);
656   scheduled_nodes_.resize(schedule_->BasicBlockCount());
657 }
658 
659 
660 // -----------------------------------------------------------------------------
661 // Phase 2: Compute special RPO and dominator tree.
662 
663 
664 // Compute the special reverse-post-order block ordering, which is essentially
665 // a RPO of the graph where loop bodies are contiguous. Properties:
666 // 1. If block A is a predecessor of B, then A appears before B in the order,
667 //    unless B is a loop header and A is in the loop headed at B
668 //    (i.e. A -> B is a backedge).
669 // => If block A dominates block B, then A appears before B in the order.
670 // => If block A is a loop header, A appears before all blocks in the loop
671 //    headed at A.
672 // 2. All loops are contiguous in the order (i.e. no intervening blocks that
673 //    do not belong to the loop.)
674 // Note a simple RPO traversal satisfies (1) but not (2).
675 class SpecialRPONumberer : public ZoneObject {
676  public:
SpecialRPONumberer(Zone * zone,Schedule * schedule)677   SpecialRPONumberer(Zone* zone, Schedule* schedule)
678       : zone_(zone),
679         schedule_(schedule),
680         order_(nullptr),
681         beyond_end_(nullptr),
682         loops_(zone),
683         backedges_(zone),
684         stack_(zone),
685         previous_block_count_(0),
686         empty_(0, zone) {}
687 
688   // Computes the special reverse-post-order for the main control flow graph,
689   // that is for the graph spanned between the schedule's start and end blocks.
ComputeSpecialRPO()690   void ComputeSpecialRPO() {
691     DCHECK_EQ(0, schedule_->end()->SuccessorCount());
692     DCHECK(!order_);  // Main order does not exist yet.
693     ComputeAndInsertSpecialRPO(schedule_->start(), schedule_->end());
694   }
695 
696   // Computes the special reverse-post-order for a partial control flow graph,
697   // that is for the graph spanned between the given {entry} and {end} blocks,
698   // then updates the existing ordering with this new information.
UpdateSpecialRPO(BasicBlock * entry,BasicBlock * end)699   void UpdateSpecialRPO(BasicBlock* entry, BasicBlock* end) {
700     DCHECK(order_);  // Main order to be updated is present.
701     ComputeAndInsertSpecialRPO(entry, end);
702   }
703 
704   // Serialize the previously computed order as a special reverse-post-order
705   // numbering for basic blocks into the final schedule.
SerializeRPOIntoSchedule()706   void SerializeRPOIntoSchedule() {
707     int32_t number = 0;
708     for (BasicBlock* b = order_; b != nullptr; b = b->rpo_next()) {
709       b->set_rpo_number(number++);
710       schedule_->rpo_order()->push_back(b);
711     }
712     BeyondEndSentinel()->set_rpo_number(number);
713   }
714 
715   // Print and verify the special reverse-post-order.
PrintAndVerifySpecialRPO()716   void PrintAndVerifySpecialRPO() {
717 #if DEBUG
718     if (FLAG_trace_turbo_scheduler) PrintRPO();
719     VerifySpecialRPO();
720 #endif
721   }
722 
GetOutgoingBlocks(BasicBlock * block)723   const ZoneVector<BasicBlock*>& GetOutgoingBlocks(BasicBlock* block) {
724     if (HasLoopNumber(block)) {
725       LoopInfo const& loop = loops_[GetLoopNumber(block)];
726       if (loop.outgoing) return *loop.outgoing;
727     }
728     return empty_;
729   }
730 
HasLoopBlocks() const731   bool HasLoopBlocks() const { return loops_.size() != 0; }
732 
733  private:
734   using Backedge = std::pair<BasicBlock*, size_t>;
735 
736   // Numbering for BasicBlock::rpo_number for this block traversal:
737   static const int kBlockOnStack = -2;
738   static const int kBlockVisited1 = -3;
739   static const int kBlockVisited2 = -4;
740   static const int kBlockUnvisited1 = -1;
741   static const int kBlockUnvisited2 = kBlockVisited1;
742 
743   struct SpecialRPOStackFrame {
744     BasicBlock* block;
745     size_t index;
746   };
747 
748   struct LoopInfo {
749     BasicBlock* header;
750     ZoneVector<BasicBlock*>* outgoing;
751     BitVector* members;
752     LoopInfo* prev;
753     BasicBlock* end;
754     BasicBlock* start;
755 
AddOutgoingv8::internal::compiler::SpecialRPONumberer::LoopInfo756     void AddOutgoing(Zone* zone, BasicBlock* block) {
757       if (outgoing == nullptr) {
758         outgoing = zone->New<ZoneVector<BasicBlock*>>(zone);
759       }
760       outgoing->push_back(block);
761     }
762   };
763 
Push(int depth,BasicBlock * child,int unvisited)764   int Push(int depth, BasicBlock* child, int unvisited) {
765     if (child->rpo_number() == unvisited) {
766       stack_[depth].block = child;
767       stack_[depth].index = 0;
768       child->set_rpo_number(kBlockOnStack);
769       return depth + 1;
770     }
771     return depth;
772   }
773 
PushFront(BasicBlock * head,BasicBlock * block)774   BasicBlock* PushFront(BasicBlock* head, BasicBlock* block) {
775     block->set_rpo_next(head);
776     return block;
777   }
778 
GetLoopNumber(BasicBlock * block)779   static int GetLoopNumber(BasicBlock* block) { return block->loop_number(); }
SetLoopNumber(BasicBlock * block,int loop_number)780   static void SetLoopNumber(BasicBlock* block, int loop_number) {
781     return block->set_loop_number(loop_number);
782   }
HasLoopNumber(BasicBlock * block)783   static bool HasLoopNumber(BasicBlock* block) {
784     return block->loop_number() >= 0;
785   }
786 
787   // We only need this special sentinel because some tests use the schedule's
788   // end block in actual control flow (e.g. with end having successors).
BeyondEndSentinel()789   BasicBlock* BeyondEndSentinel() {
790     if (beyond_end_ == nullptr) {
791       BasicBlock::Id id = BasicBlock::Id::FromInt(-1);
792       beyond_end_ = schedule_->zone()->New<BasicBlock>(schedule_->zone(), id);
793     }
794     return beyond_end_;
795   }
796 
797   // Compute special RPO for the control flow graph between {entry} and {end},
798   // mutating any existing order so that the result is still valid.
ComputeAndInsertSpecialRPO(BasicBlock * entry,BasicBlock * end)799   void ComputeAndInsertSpecialRPO(BasicBlock* entry, BasicBlock* end) {
800     // RPO should not have been serialized for this schedule yet.
801     CHECK_EQ(kBlockUnvisited1, schedule_->start()->loop_number());
802     CHECK_EQ(kBlockUnvisited1, schedule_->start()->rpo_number());
803     CHECK_EQ(0, static_cast<int>(schedule_->rpo_order()->size()));
804 
805     // Find correct insertion point within existing order.
806     BasicBlock* insertion_point = entry->rpo_next();
807     BasicBlock* order = insertion_point;
808 
809     // Perform an iterative RPO traversal using an explicit stack,
810     // recording backedges that form cycles. O(|B|).
811     DCHECK_LT(previous_block_count_, schedule_->BasicBlockCount());
812     stack_.resize(schedule_->BasicBlockCount() - previous_block_count_);
813     previous_block_count_ = schedule_->BasicBlockCount();
814     int stack_depth = Push(0, entry, kBlockUnvisited1);
815     int num_loops = static_cast<int>(loops_.size());
816 
817     while (stack_depth > 0) {
818       int current = stack_depth - 1;
819       SpecialRPOStackFrame* frame = &stack_[current];
820 
821       if (frame->block != end &&
822           frame->index < frame->block->SuccessorCount()) {
823         // Process the next successor.
824         BasicBlock* succ = frame->block->SuccessorAt(frame->index++);
825         if (succ->rpo_number() == kBlockVisited1) continue;
826         if (succ->rpo_number() == kBlockOnStack) {
827           // The successor is on the stack, so this is a backedge (cycle).
828           backedges_.push_back(Backedge(frame->block, frame->index - 1));
829           if (!HasLoopNumber(succ)) {
830             // Assign a new loop number to the header if it doesn't have one.
831             SetLoopNumber(succ, num_loops++);
832           }
833         } else {
834           // Push the successor onto the stack.
835           DCHECK_EQ(kBlockUnvisited1, succ->rpo_number());
836           stack_depth = Push(stack_depth, succ, kBlockUnvisited1);
837         }
838       } else {
839         // Finished with all successors; pop the stack and add the block.
840         order = PushFront(order, frame->block);
841         frame->block->set_rpo_number(kBlockVisited1);
842         stack_depth--;
843       }
844     }
845 
846     // If no loops were encountered, then the order we computed was correct.
847     if (num_loops > static_cast<int>(loops_.size())) {
848       // Otherwise, compute the loop information from the backedges in order
849       // to perform a traversal that groups loop bodies together.
850       ComputeLoopInfo(&stack_, num_loops, &backedges_);
851 
852       // Initialize the "loop stack". Note the entry could be a loop header.
853       LoopInfo* loop =
854           HasLoopNumber(entry) ? &loops_[GetLoopNumber(entry)] : nullptr;
855       order = insertion_point;
856 
857       // Perform an iterative post-order traversal, visiting loop bodies before
858       // edges that lead out of loops. Visits each block once, but linking loop
859       // sections together is linear in the loop size, so overall is
860       // O(|B| + max(loop_depth) * max(|loop|))
861       stack_depth = Push(0, entry, kBlockUnvisited2);
862       while (stack_depth > 0) {
863         SpecialRPOStackFrame* frame = &stack_[stack_depth - 1];
864         BasicBlock* block = frame->block;
865         BasicBlock* succ = nullptr;
866 
867         if (block != end && frame->index < block->SuccessorCount()) {
868           // Process the next normal successor.
869           succ = block->SuccessorAt(frame->index++);
870         } else if (HasLoopNumber(block)) {
871           // Process additional outgoing edges from the loop header.
872           if (block->rpo_number() == kBlockOnStack) {
873             // Finish the loop body the first time the header is left on the
874             // stack.
875             DCHECK(loop != nullptr && loop->header == block);
876             loop->start = PushFront(order, block);
877             order = loop->end;
878             block->set_rpo_number(kBlockVisited2);
879             // Pop the loop stack and continue visiting outgoing edges within
880             // the context of the outer loop, if any.
881             loop = loop->prev;
882             // We leave the loop header on the stack; the rest of this iteration
883             // and later iterations will go through its outgoing edges list.
884           }
885 
886           // Use the next outgoing edge if there are any.
887           size_t outgoing_index = frame->index - block->SuccessorCount();
888           LoopInfo* info = &loops_[GetLoopNumber(block)];
889           DCHECK(loop != info);
890           if (block != entry && info->outgoing != nullptr &&
891               outgoing_index < info->outgoing->size()) {
892             succ = info->outgoing->at(outgoing_index);
893             frame->index++;
894           }
895         }
896 
897         if (succ != nullptr) {
898           // Process the next successor.
899           if (succ->rpo_number() == kBlockOnStack) continue;
900           if (succ->rpo_number() == kBlockVisited2) continue;
901           DCHECK_EQ(kBlockUnvisited2, succ->rpo_number());
902           if (loop != nullptr && !loop->members->Contains(succ->id().ToInt())) {
903             // The successor is not in the current loop or any nested loop.
904             // Add it to the outgoing edges of this loop and visit it later.
905             loop->AddOutgoing(zone_, succ);
906           } else {
907             // Push the successor onto the stack.
908             stack_depth = Push(stack_depth, succ, kBlockUnvisited2);
909             if (HasLoopNumber(succ)) {
910               // Push the inner loop onto the loop stack.
911               DCHECK(GetLoopNumber(succ) < num_loops);
912               LoopInfo* next = &loops_[GetLoopNumber(succ)];
913               next->end = order;
914               next->prev = loop;
915               loop = next;
916             }
917           }
918         } else {
919           // Finished with all successors of the current block.
920           if (HasLoopNumber(block)) {
921             // If we are going to pop a loop header, then add its entire body.
922             LoopInfo* info = &loops_[GetLoopNumber(block)];
923             for (BasicBlock* b = info->start; true; b = b->rpo_next()) {
924               if (b->rpo_next() == info->end) {
925                 b->set_rpo_next(order);
926                 info->end = order;
927                 break;
928               }
929             }
930             order = info->start;
931           } else {
932             // Pop a single node off the stack and add it to the order.
933             order = PushFront(order, block);
934             block->set_rpo_number(kBlockVisited2);
935           }
936           stack_depth--;
937         }
938       }
939     }
940 
941     // Publish new order the first time.
942     if (order_ == nullptr) order_ = order;
943 
944     // Compute the correct loop headers and set the correct loop ends.
945     LoopInfo* current_loop = nullptr;
946     BasicBlock* current_header = entry->loop_header();
947     int32_t loop_depth = entry->loop_depth();
948     if (entry->IsLoopHeader()) --loop_depth;  // Entry might be a loop header.
949     for (BasicBlock* b = order; b != insertion_point; b = b->rpo_next()) {
950       BasicBlock* current = b;
951 
952       // Reset BasicBlock::rpo_number again.
953       current->set_rpo_number(kBlockUnvisited1);
954 
955       // Finish the previous loop(s) if we just exited them.
956       while (current_header != nullptr &&
957              current == current_header->loop_end()) {
958         DCHECK(current_header->IsLoopHeader());
959         DCHECK_NOT_NULL(current_loop);
960         current_loop = current_loop->prev;
961         current_header =
962             current_loop == nullptr ? nullptr : current_loop->header;
963         --loop_depth;
964       }
965       current->set_loop_header(current_header);
966 
967       // Push a new loop onto the stack if this loop is a loop header.
968       if (HasLoopNumber(current)) {
969         ++loop_depth;
970         current_loop = &loops_[GetLoopNumber(current)];
971         BasicBlock* loop_end = current_loop->end;
972         current->set_loop_end(loop_end == nullptr ? BeyondEndSentinel()
973                                                   : loop_end);
974         current_header = current_loop->header;
975         TRACE("id:%d is a loop header, increment loop depth to %d\n",
976               current->id().ToInt(), loop_depth);
977       }
978 
979       current->set_loop_depth(loop_depth);
980 
981       if (current->loop_header() == nullptr) {
982         TRACE("id:%d is not in a loop (depth == %d)\n", current->id().ToInt(),
983               current->loop_depth());
984       } else {
985         TRACE("id:%d has loop header id:%d, (depth == %d)\n",
986               current->id().ToInt(), current->loop_header()->id().ToInt(),
987               current->loop_depth());
988       }
989     }
990   }
991 
992   // Computes loop membership from the backedges of the control flow graph.
ComputeLoopInfo(ZoneVector<SpecialRPOStackFrame> * queue,size_t num_loops,ZoneVector<Backedge> * backedges)993   void ComputeLoopInfo(ZoneVector<SpecialRPOStackFrame>* queue,
994                        size_t num_loops, ZoneVector<Backedge>* backedges) {
995     // Extend existing loop membership vectors.
996     for (LoopInfo& loop : loops_) {
997       loop.members->Resize(static_cast<int>(schedule_->BasicBlockCount()),
998                            zone_);
999     }
1000 
1001     // Extend loop information vector.
1002     loops_.resize(num_loops, LoopInfo());
1003 
1004     // Compute loop membership starting from backedges.
1005     // O(max(loop_depth) * max(|loop|)
1006     for (size_t i = 0; i < backedges->size(); i++) {
1007       BasicBlock* member = backedges->at(i).first;
1008       BasicBlock* header = member->SuccessorAt(backedges->at(i).second);
1009       size_t loop_num = GetLoopNumber(header);
1010       if (loops_[loop_num].header == nullptr) {
1011         loops_[loop_num].header = header;
1012         loops_[loop_num].members = zone_->New<BitVector>(
1013             static_cast<int>(schedule_->BasicBlockCount()), zone_);
1014       }
1015 
1016       int queue_length = 0;
1017       if (member != header) {
1018         // As long as the header doesn't have a backedge to itself,
1019         // Push the member onto the queue and process its predecessors.
1020         if (!loops_[loop_num].members->Contains(member->id().ToInt())) {
1021           loops_[loop_num].members->Add(member->id().ToInt());
1022         }
1023         (*queue)[queue_length++].block = member;
1024       }
1025 
1026       // Propagate loop membership backwards. All predecessors of M up to the
1027       // loop header H are members of the loop too. O(|blocks between M and H|).
1028       while (queue_length > 0) {
1029         BasicBlock* block = (*queue)[--queue_length].block;
1030         for (size_t j = 0; j < block->PredecessorCount(); j++) {
1031           BasicBlock* pred = block->PredecessorAt(j);
1032           if (pred != header) {
1033             if (!loops_[loop_num].members->Contains(pred->id().ToInt())) {
1034               loops_[loop_num].members->Add(pred->id().ToInt());
1035               (*queue)[queue_length++].block = pred;
1036             }
1037           }
1038         }
1039       }
1040     }
1041   }
1042 
1043 #if DEBUG
PrintRPO()1044   void PrintRPO() {
1045     StdoutStream os;
1046     os << "RPO with " << loops_.size() << " loops";
1047     if (loops_.size() > 0) {
1048       os << " (";
1049       for (size_t i = 0; i < loops_.size(); i++) {
1050         if (i > 0) os << " ";
1051         os << "id:" << loops_[i].header->id();
1052       }
1053       os << ")";
1054     }
1055     os << ":\n";
1056 
1057     for (BasicBlock* block = order_; block != nullptr;
1058          block = block->rpo_next()) {
1059       os << std::setw(5) << "B" << block->rpo_number() << ":";
1060       for (size_t i = 0; i < loops_.size(); i++) {
1061         bool range = loops_[i].header->LoopContains(block);
1062         bool membership = loops_[i].header != block && range;
1063         os << (membership ? " |" : "  ");
1064         os << (range ? "x" : " ");
1065       }
1066       os << "  id:" << block->id() << ": ";
1067       if (block->loop_end() != nullptr) {
1068         os << " range: [B" << block->rpo_number() << ", B"
1069            << block->loop_end()->rpo_number() << ")";
1070       }
1071       if (block->loop_header() != nullptr) {
1072         os << " header: id:" << block->loop_header()->id();
1073       }
1074       if (block->loop_depth() > 0) {
1075         os << " depth: " << block->loop_depth();
1076       }
1077       os << "\n";
1078     }
1079   }
1080 
VerifySpecialRPO()1081   void VerifySpecialRPO() {
1082     BasicBlockVector* order = schedule_->rpo_order();
1083     DCHECK_LT(0, order->size());
1084     DCHECK_EQ(0, (*order)[0]->id().ToInt());  // entry should be first.
1085 
1086     for (size_t i = 0; i < loops_.size(); i++) {
1087       LoopInfo* loop = &loops_[i];
1088       BasicBlock* header = loop->header;
1089       BasicBlock* end = header->loop_end();
1090 
1091       DCHECK_NOT_NULL(header);
1092       DCHECK_LE(0, header->rpo_number());
1093       DCHECK_LT(header->rpo_number(), order->size());
1094       DCHECK_NOT_NULL(end);
1095       DCHECK_LE(end->rpo_number(), order->size());
1096       DCHECK_GT(end->rpo_number(), header->rpo_number());
1097       DCHECK_NE(header->loop_header(), header);
1098 
1099       // Verify the start ... end list relationship.
1100       int links = 0;
1101       BasicBlock* block = loop->start;
1102       DCHECK_EQ(header, block);
1103       bool end_found;
1104       while (true) {
1105         if (block == nullptr || block == loop->end) {
1106           end_found = (loop->end == block);
1107           break;
1108         }
1109         // The list should be in same order as the final result.
1110         DCHECK(block->rpo_number() == links + header->rpo_number());
1111         links++;
1112         block = block->rpo_next();
1113         DCHECK_LT(links, static_cast<int>(2 * order->size()));  // cycle?
1114       }
1115       DCHECK_LT(0, links);
1116       DCHECK_EQ(links, end->rpo_number() - header->rpo_number());
1117       DCHECK(end_found);
1118 
1119       // Check loop depth of the header.
1120       int loop_depth = 0;
1121       for (LoopInfo* outer = loop; outer != nullptr; outer = outer->prev) {
1122         loop_depth++;
1123       }
1124       DCHECK_EQ(loop_depth, header->loop_depth());
1125 
1126       // Check the contiguousness of loops.
1127       int count = 0;
1128       for (int j = 0; j < static_cast<int>(order->size()); j++) {
1129         block = order->at(j);
1130         DCHECK_EQ(block->rpo_number(), j);
1131         if (j < header->rpo_number() || j >= end->rpo_number()) {
1132           DCHECK(!header->LoopContains(block));
1133         } else {
1134           DCHECK(header->LoopContains(block));
1135           DCHECK_GE(block->loop_depth(), loop_depth);
1136           count++;
1137         }
1138       }
1139       DCHECK_EQ(links, count);
1140     }
1141   }
1142 #endif  // DEBUG
1143 
1144   Zone* zone_;
1145   Schedule* schedule_;
1146   BasicBlock* order_;
1147   BasicBlock* beyond_end_;
1148   ZoneVector<LoopInfo> loops_;
1149   ZoneVector<Backedge> backedges_;
1150   ZoneVector<SpecialRPOStackFrame> stack_;
1151   size_t previous_block_count_;
1152   ZoneVector<BasicBlock*> const empty_;
1153 };
1154 
1155 
ComputeSpecialRPO(Zone * zone,Schedule * schedule)1156 BasicBlockVector* Scheduler::ComputeSpecialRPO(Zone* zone, Schedule* schedule) {
1157   SpecialRPONumberer numberer(zone, schedule);
1158   numberer.ComputeSpecialRPO();
1159   numberer.SerializeRPOIntoSchedule();
1160   numberer.PrintAndVerifySpecialRPO();
1161   return schedule->rpo_order();
1162 }
1163 
1164 
ComputeSpecialRPONumbering()1165 void Scheduler::ComputeSpecialRPONumbering() {
1166   TRACE("--- COMPUTING SPECIAL RPO ----------------------------------\n");
1167 
1168   // Compute the special reverse-post-order for basic blocks.
1169   special_rpo_ = zone_->New<SpecialRPONumberer>(zone_, schedule_);
1170   special_rpo_->ComputeSpecialRPO();
1171 }
1172 
GetCommonDominatorIfCached(BasicBlock * b1,BasicBlock * b2)1173 BasicBlock* Scheduler::GetCommonDominatorIfCached(BasicBlock* b1,
1174                                                   BasicBlock* b2) {
1175   auto entry1 = common_dominator_cache_.find(b1->id().ToInt());
1176   if (entry1 == common_dominator_cache_.end()) return nullptr;
1177   auto entry2 = entry1->second->find(b2->id().ToInt());
1178   if (entry2 == entry1->second->end()) return nullptr;
1179   return entry2->second;
1180 }
1181 
GetCommonDominator(BasicBlock * b1,BasicBlock * b2)1182 BasicBlock* Scheduler::GetCommonDominator(BasicBlock* b1, BasicBlock* b2) {
1183   // A very common fast case:
1184   if (b1 == b2) return b1;
1185   // Try to find the common dominator by walking, if there is a chance of
1186   // finding it quickly.
1187   constexpr int kCacheGranularity = 63;
1188   STATIC_ASSERT((kCacheGranularity & (kCacheGranularity + 1)) == 0);
1189   int depth_difference = b1->dominator_depth() - b2->dominator_depth();
1190   if (depth_difference > -kCacheGranularity &&
1191       depth_difference < kCacheGranularity) {
1192     for (int i = 0; i < kCacheGranularity; i++) {
1193       if (b1->dominator_depth() < b2->dominator_depth()) {
1194         b2 = b2->dominator();
1195       } else {
1196         b1 = b1->dominator();
1197       }
1198       if (b1 == b2) return b1;
1199     }
1200     // We might fall out of the loop here if the dominator tree has several
1201     // deep "parallel" subtrees.
1202   }
1203   // If it'd be a long walk, take the bus instead (i.e. use the cache).
1204   // To keep memory consumption low, there'll be a bus stop every 64 blocks.
1205   // First, walk to the nearest bus stop.
1206   if (b1->dominator_depth() < b2->dominator_depth()) std::swap(b1, b2);
1207   while ((b1->dominator_depth() & kCacheGranularity) != 0) {
1208     if (V8_LIKELY(b1->dominator_depth() > b2->dominator_depth())) {
1209       b1 = b1->dominator();
1210     } else {
1211       b2 = b2->dominator();
1212     }
1213     if (b1 == b2) return b1;
1214   }
1215   // Then, walk from bus stop to bus stop until we either find a bus (i.e. an
1216   // existing cache entry) or the result. Make a list of any empty bus stops
1217   // we'd like to populate for next time.
1218   constexpr int kMaxNewCacheEntries = 2 * 50;  // Must be even.
1219   // This array stores a flattened list of pairs, e.g. if after finding the
1220   // {result}, we want to cache [(B11, B12) -> result, (B21, B22) -> result],
1221   // then we store [11, 12, 21, 22] here.
1222   int new_cache_entries[kMaxNewCacheEntries];
1223   // Next free slot in {new_cache_entries}.
1224   int new_cache_entries_cursor = 0;
1225   while (b1 != b2) {
1226     if ((b1->dominator_depth() & kCacheGranularity) == 0) {
1227       BasicBlock* maybe_cache_hit = GetCommonDominatorIfCached(b1, b2);
1228       if (maybe_cache_hit != nullptr) {
1229         b1 = b2 = maybe_cache_hit;
1230         break;
1231       } else if (new_cache_entries_cursor < kMaxNewCacheEntries) {
1232         new_cache_entries[new_cache_entries_cursor++] = b1->id().ToInt();
1233         new_cache_entries[new_cache_entries_cursor++] = b2->id().ToInt();
1234       }
1235     }
1236     if (V8_LIKELY(b1->dominator_depth() > b2->dominator_depth())) {
1237       b1 = b1->dominator();
1238     } else {
1239       b2 = b2->dominator();
1240     }
1241   }
1242   // Lastly, create new cache entries we noted down earlier.
1243   BasicBlock* result = b1;
1244   for (int i = 0; i < new_cache_entries_cursor;) {
1245     int id1 = new_cache_entries[i++];
1246     int id2 = new_cache_entries[i++];
1247     ZoneMap<int, BasicBlock*>* mapping;
1248     auto entry = common_dominator_cache_.find(id1);
1249     if (entry == common_dominator_cache_.end()) {
1250       mapping = zone_->New<ZoneMap<int, BasicBlock*>>(zone_);
1251       common_dominator_cache_[id1] = mapping;
1252     } else {
1253       mapping = entry->second;
1254     }
1255     // If there was an existing entry, we would have found it earlier.
1256     DCHECK_EQ(mapping->find(id2), mapping->end());
1257     mapping->insert({id2, result});
1258   }
1259   return result;
1260 }
1261 
PropagateImmediateDominators(BasicBlock * block)1262 void Scheduler::PropagateImmediateDominators(BasicBlock* block) {
1263   for (/*nop*/; block != nullptr; block = block->rpo_next()) {
1264     auto pred = block->predecessors().begin();
1265     auto end = block->predecessors().end();
1266     DCHECK(pred != end);  // All blocks except start have predecessors.
1267     BasicBlock* dominator = *pred;
1268     bool deferred = dominator->deferred();
1269     // For multiple predecessors, walk up the dominator tree until a common
1270     // dominator is found. Visitation order guarantees that all predecessors
1271     // except for backwards edges have been visited.
1272     // We use a one-element cache for previously-seen dominators. This gets
1273     // hit a lot for functions that have long chains of diamonds, and in
1274     // those cases turns quadratic into linear complexity.
1275     BasicBlock* cache = nullptr;
1276     for (++pred; pred != end; ++pred) {
1277       // Don't examine backwards edges.
1278       if ((*pred)->dominator_depth() < 0) continue;
1279       if ((*pred)->dominator_depth() > 3 &&
1280           ((*pred)->dominator()->dominator() == cache ||
1281            (*pred)->dominator()->dominator()->dominator() == cache)) {
1282         // Nothing to do, the last iteration covered this case.
1283         DCHECK_EQ(dominator, BasicBlock::GetCommonDominator(dominator, *pred));
1284       } else {
1285         dominator = BasicBlock::GetCommonDominator(dominator, *pred);
1286       }
1287       cache = (*pred)->dominator();
1288       deferred = deferred & (*pred)->deferred();
1289     }
1290     block->set_dominator(dominator);
1291     block->set_dominator_depth(dominator->dominator_depth() + 1);
1292     block->set_deferred(deferred | block->deferred());
1293     TRACE("Block id:%d's idom is id:%d, depth = %d\n", block->id().ToInt(),
1294           dominator->id().ToInt(), block->dominator_depth());
1295   }
1296 }
1297 
GenerateDominatorTree(Schedule * schedule)1298 void Scheduler::GenerateDominatorTree(Schedule* schedule) {
1299   // Seed start block to be the first dominator.
1300   schedule->start()->set_dominator_depth(0);
1301 
1302   // Build the block dominator tree resulting from the above seed.
1303   PropagateImmediateDominators(schedule->start()->rpo_next());
1304 }
1305 
GenerateDominatorTree()1306 void Scheduler::GenerateDominatorTree() {
1307   TRACE("--- IMMEDIATE BLOCK DOMINATORS -----------------------------\n");
1308   GenerateDominatorTree(schedule_);
1309 }
1310 
1311 // -----------------------------------------------------------------------------
1312 // Phase 3: Prepare use counts for nodes.
1313 
1314 
1315 class PrepareUsesVisitor {
1316  public:
PrepareUsesVisitor(Scheduler * scheduler,Graph * graph,Zone * zone)1317   explicit PrepareUsesVisitor(Scheduler* scheduler, Graph* graph, Zone* zone)
1318       : scheduler_(scheduler),
1319         schedule_(scheduler->schedule_),
1320         graph_(graph),
1321         visited_(graph_->NodeCount(), false, zone),
1322         stack_(zone) {}
1323 
Run()1324   void Run() {
1325     InitializePlacement(graph_->end());
1326     while (!stack_.empty()) {
1327       Node* node = stack_.top();
1328       stack_.pop();
1329       VisitInputs(node);
1330     }
1331   }
1332 
1333  private:
InitializePlacement(Node * node)1334   void InitializePlacement(Node* node) {
1335     TRACE("Pre #%d:%s\n", node->id(), node->op()->mnemonic());
1336     DCHECK(!Visited(node));
1337     if (scheduler_->InitializePlacement(node) == Scheduler::kFixed) {
1338       // Fixed nodes are always roots for schedule late.
1339       scheduler_->schedule_root_nodes_.push_back(node);
1340       if (!schedule_->IsScheduled(node)) {
1341         // Make sure root nodes are scheduled in their respective blocks.
1342         TRACE("Scheduling fixed position node #%d:%s\n", node->id(),
1343               node->op()->mnemonic());
1344         IrOpcode::Value opcode = node->opcode();
1345         BasicBlock* block =
1346             opcode == IrOpcode::kParameter
1347                 ? schedule_->start()
1348                 : schedule_->block(NodeProperties::GetControlInput(node));
1349         DCHECK_NOT_NULL(block);
1350         schedule_->AddNode(block, node);
1351       }
1352     }
1353     stack_.push(node);
1354     visited_[node->id()] = true;
1355   }
1356 
VisitInputs(Node * node)1357   void VisitInputs(Node* node) {
1358     DCHECK_NE(scheduler_->GetPlacement(node), Scheduler::kUnknown);
1359     bool is_scheduled = schedule_->IsScheduled(node);
1360     base::Optional<int> coupled_control_edge =
1361         scheduler_->GetCoupledControlEdge(node);
1362     for (auto edge : node->input_edges()) {
1363       Node* to = edge.to();
1364       DCHECK_EQ(node, edge.from());
1365       if (!Visited(to)) {
1366         InitializePlacement(to);
1367       }
1368       TRACE("PostEdge #%d:%s->#%d:%s\n", node->id(), node->op()->mnemonic(),
1369             to->id(), to->op()->mnemonic());
1370       DCHECK_NE(scheduler_->GetPlacement(to), Scheduler::kUnknown);
1371       if (!is_scheduled && edge.index() != coupled_control_edge) {
1372         scheduler_->IncrementUnscheduledUseCount(to, node);
1373       }
1374     }
1375   }
1376 
Visited(Node * node)1377   bool Visited(Node* node) { return visited_[node->id()]; }
1378 
1379   Scheduler* scheduler_;
1380   Schedule* schedule_;
1381   Graph* graph_;
1382   BoolVector visited_;
1383   ZoneStack<Node*> stack_;
1384 };
1385 
1386 
PrepareUses()1387 void Scheduler::PrepareUses() {
1388   TRACE("--- PREPARE USES -------------------------------------------\n");
1389 
1390   // Count the uses of every node, which is used to ensure that all of a
1391   // node's uses are scheduled before the node itself.
1392   PrepareUsesVisitor prepare_uses(this, graph_, zone_);
1393   prepare_uses.Run();
1394 }
1395 
1396 
1397 // -----------------------------------------------------------------------------
1398 // Phase 4: Schedule nodes early.
1399 
1400 
1401 class ScheduleEarlyNodeVisitor {
1402  public:
ScheduleEarlyNodeVisitor(Zone * zone,Scheduler * scheduler)1403   ScheduleEarlyNodeVisitor(Zone* zone, Scheduler* scheduler)
1404       : scheduler_(scheduler), schedule_(scheduler->schedule_), queue_(zone) {}
1405 
1406   // Run the schedule early algorithm on a set of fixed root nodes.
Run(NodeVector * roots)1407   void Run(NodeVector* roots) {
1408     for (Node* const root : *roots) {
1409       queue_.push(root);
1410     }
1411 
1412     while (!queue_.empty()) {
1413       scheduler_->tick_counter_->TickAndMaybeEnterSafepoint();
1414       VisitNode(queue_.front());
1415       queue_.pop();
1416     }
1417   }
1418 
1419  private:
1420   // Visits one node from the queue and propagates its current schedule early
1421   // position to all uses. This in turn might push more nodes onto the queue.
VisitNode(Node * node)1422   void VisitNode(Node* node) {
1423     Scheduler::SchedulerData* data = scheduler_->GetData(node);
1424 
1425     // Fixed nodes already know their schedule early position.
1426     if (scheduler_->GetPlacement(node) == Scheduler::kFixed) {
1427       data->minimum_block_ = schedule_->block(node);
1428       TRACE("Fixing #%d:%s minimum_block = id:%d, dominator_depth = %d\n",
1429             node->id(), node->op()->mnemonic(),
1430             data->minimum_block_->id().ToInt(),
1431             data->minimum_block_->dominator_depth());
1432     }
1433 
1434     // No need to propagate unconstrained schedule early positions.
1435     if (data->minimum_block_ == schedule_->start()) return;
1436 
1437     // Propagate schedule early position.
1438     DCHECK_NOT_NULL(data->minimum_block_);
1439     for (auto use : node->uses()) {
1440       if (scheduler_->IsLive(use)) {
1441         PropagateMinimumPositionToNode(data->minimum_block_, use);
1442       }
1443     }
1444   }
1445 
1446   // Propagates {block} as another minimum position into the given {node}. This
1447   // has the net effect of computing the minimum dominator block of {node} that
1448   // still post-dominates all inputs to {node} when the queue is processed.
PropagateMinimumPositionToNode(BasicBlock * block,Node * node)1449   void PropagateMinimumPositionToNode(BasicBlock* block, Node* node) {
1450     Scheduler::SchedulerData* data = scheduler_->GetData(node);
1451 
1452     // No need to propagate to fixed node, it's guaranteed to be a root.
1453     if (scheduler_->GetPlacement(node) == Scheduler::kFixed) return;
1454 
1455     // Coupled nodes influence schedule early position of their control.
1456     if (scheduler_->GetPlacement(node) == Scheduler::kCoupled) {
1457       Node* control = NodeProperties::GetControlInput(node);
1458       PropagateMinimumPositionToNode(block, control);
1459     }
1460 
1461     // Propagate new position if it is deeper down the dominator tree than the
1462     // current. Note that all inputs need to have minimum block position inside
1463     // the dominator chain of {node}'s minimum block position.
1464     DCHECK(InsideSameDominatorChain(block, data->minimum_block_));
1465     if (block->dominator_depth() > data->minimum_block_->dominator_depth()) {
1466       data->minimum_block_ = block;
1467       queue_.push(node);
1468       TRACE("Propagating #%d:%s minimum_block = id:%d, dominator_depth = %d\n",
1469             node->id(), node->op()->mnemonic(),
1470             data->minimum_block_->id().ToInt(),
1471             data->minimum_block_->dominator_depth());
1472     }
1473   }
1474 
1475 #if DEBUG
InsideSameDominatorChain(BasicBlock * b1,BasicBlock * b2)1476   bool InsideSameDominatorChain(BasicBlock* b1, BasicBlock* b2) {
1477     BasicBlock* dominator = BasicBlock::GetCommonDominator(b1, b2);
1478     return dominator == b1 || dominator == b2;
1479   }
1480 #endif
1481 
1482   Scheduler* scheduler_;
1483   Schedule* schedule_;
1484   ZoneQueue<Node*> queue_;
1485 };
1486 
1487 
ScheduleEarly()1488 void Scheduler::ScheduleEarly() {
1489   if (!special_rpo_->HasLoopBlocks()) {
1490     TRACE("--- NO LOOPS SO SKIPPING SCHEDULE EARLY --------------------\n");
1491     return;
1492   }
1493 
1494   TRACE("--- SCHEDULE EARLY -----------------------------------------\n");
1495   if (FLAG_trace_turbo_scheduler) {
1496     TRACE("roots: ");
1497     for (Node* node : schedule_root_nodes_) {
1498       TRACE("#%d:%s ", node->id(), node->op()->mnemonic());
1499     }
1500     TRACE("\n");
1501   }
1502 
1503   // Compute the minimum block for each node thereby determining the earliest
1504   // position each node could be placed within a valid schedule.
1505   ScheduleEarlyNodeVisitor schedule_early_visitor(zone_, this);
1506   schedule_early_visitor.Run(&schedule_root_nodes_);
1507 }
1508 
1509 
1510 // -----------------------------------------------------------------------------
1511 // Phase 5: Schedule nodes late.
1512 
1513 
1514 class ScheduleLateNodeVisitor {
1515  public:
ScheduleLateNodeVisitor(Zone * zone,Scheduler * scheduler)1516   ScheduleLateNodeVisitor(Zone* zone, Scheduler* scheduler)
1517       : zone_(zone),
1518         scheduler_(scheduler),
1519         schedule_(scheduler_->schedule_),
1520         marked_(scheduler->zone_),
1521         marking_queue_(scheduler->zone_) {}
1522 
1523   // Run the schedule late algorithm on a set of fixed root nodes.
Run(NodeVector * roots)1524   void Run(NodeVector* roots) {
1525     for (Node* const root : *roots) {
1526       ProcessQueue(root);
1527     }
1528   }
1529 
1530  private:
ProcessQueue(Node * root)1531   void ProcessQueue(Node* root) {
1532     ZoneQueue<Node*>* queue = &(scheduler_->schedule_queue_);
1533     for (Node* node : root->inputs()) {
1534       // Don't schedule coupled nodes on their own.
1535       if (scheduler_->GetPlacement(node) == Scheduler::kCoupled) {
1536         node = NodeProperties::GetControlInput(node);
1537       }
1538 
1539       // Test schedulability condition by looking at unscheduled use count.
1540       if (scheduler_->GetData(node)->unscheduled_count_ != 0) continue;
1541 
1542       queue->push(node);
1543       do {
1544         scheduler_->tick_counter_->TickAndMaybeEnterSafepoint();
1545         Node* const n = queue->front();
1546         queue->pop();
1547         VisitNode(n);
1548       } while (!queue->empty());
1549     }
1550   }
1551 
1552   // Visits one node from the queue of schedulable nodes and determines its
1553   // schedule late position. Also hoists nodes out of loops to find a more
1554   // optimal scheduling position.
VisitNode(Node * node)1555   void VisitNode(Node* node) {
1556     DCHECK_EQ(0, scheduler_->GetData(node)->unscheduled_count_);
1557 
1558     // Don't schedule nodes that are already scheduled.
1559     if (schedule_->IsScheduled(node)) return;
1560     DCHECK_EQ(Scheduler::kSchedulable, scheduler_->GetPlacement(node));
1561 
1562     // Determine the dominating block for all of the uses of this node. It is
1563     // the latest block that this node can be scheduled in.
1564     TRACE("Scheduling #%d:%s\n", node->id(), node->op()->mnemonic());
1565     BasicBlock* block = GetCommonDominatorOfUses(node);
1566     DCHECK_NOT_NULL(block);
1567 
1568     // The schedule early block dominates the schedule late block.
1569     BasicBlock* min_block = scheduler_->GetData(node)->minimum_block_;
1570     DCHECK_EQ(min_block, BasicBlock::GetCommonDominator(block, min_block));
1571 
1572     TRACE(
1573         "Schedule late of #%d:%s is id:%d at loop depth %d, minimum = id:%d\n",
1574         node->id(), node->op()->mnemonic(), block->id().ToInt(),
1575         block->loop_depth(), min_block->id().ToInt());
1576 
1577     // Hoist nodes out of loops if possible. Nodes can be hoisted iteratively
1578     // into enclosing loop pre-headers until they would precede their schedule
1579     // early position.
1580     BasicBlock* hoist_block = GetHoistBlock(block);
1581     if (hoist_block &&
1582         hoist_block->dominator_depth() >= min_block->dominator_depth()) {
1583       DCHECK(scheduler_->special_rpo_->HasLoopBlocks());
1584       do {
1585         TRACE("  hoisting #%d:%s to block id:%d\n", node->id(),
1586               node->op()->mnemonic(), hoist_block->id().ToInt());
1587         DCHECK_LT(hoist_block->loop_depth(), block->loop_depth());
1588         block = hoist_block;
1589         hoist_block = GetHoistBlock(hoist_block);
1590       } while (hoist_block &&
1591                hoist_block->dominator_depth() >= min_block->dominator_depth());
1592     } else if (scheduler_->flags_ & Scheduler::kSplitNodes) {
1593       // Split the {node} if beneficial and return the new {block} for it.
1594       block = SplitNode(block, node);
1595     }
1596 
1597     // Schedule the node or a floating control structure.
1598     if (IrOpcode::IsMergeOpcode(node->opcode())) {
1599       ScheduleFloatingControl(block, node);
1600     } else if (node->opcode() == IrOpcode::kFinishRegion) {
1601       ScheduleRegion(block, node);
1602     } else {
1603       ScheduleNode(block, node);
1604     }
1605   }
1606 
IsMarked(BasicBlock * block) const1607   bool IsMarked(BasicBlock* block) const {
1608     DCHECK_LT(block->id().ToSize(), marked_.size());
1609     return marked_[block->id().ToSize()];
1610   }
1611 
Mark(BasicBlock * block)1612   void Mark(BasicBlock* block) { marked_[block->id().ToSize()] = true; }
1613 
1614   // Mark {block} and push its non-marked predecessor on the marking queue.
MarkBlock(BasicBlock * block)1615   void MarkBlock(BasicBlock* block) {
1616     DCHECK_LT(block->id().ToSize(), marked_.size());
1617     Mark(block);
1618     for (BasicBlock* pred_block : block->predecessors()) {
1619       if (IsMarked(pred_block)) continue;
1620       marking_queue_.push_back(pred_block);
1621     }
1622   }
1623 
SplitNode(BasicBlock * block,Node * node)1624   BasicBlock* SplitNode(BasicBlock* block, Node* node) {
1625     // For now, we limit splitting to pure nodes.
1626     if (!node->op()->HasProperty(Operator::kPure)) return block;
1627     // TODO(titzer): fix the special case of splitting of projections.
1628     if (node->opcode() == IrOpcode::kProjection) return block;
1629 
1630     // The {block} is common dominator of all uses of {node}, so we cannot
1631     // split anything unless the {block} has at least two successors.
1632     DCHECK_EQ(block, GetCommonDominatorOfUses(node));
1633     if (block->SuccessorCount() < 2) return block;
1634 
1635     // Clear marking bits.
1636     DCHECK(marking_queue_.empty());
1637     std::fill(marked_.begin(), marked_.end(), false);
1638     marked_.resize(schedule_->BasicBlockCount() + 1, false);
1639 
1640     // Check if the {node} has uses in {block}.
1641     for (Edge edge : node->use_edges()) {
1642       if (!scheduler_->IsLive(edge.from())) continue;
1643       BasicBlock* use_block = GetBlockForUse(edge);
1644       if (use_block == nullptr || IsMarked(use_block)) continue;
1645       if (use_block == block) {
1646         TRACE("  not splitting #%d:%s, it is used in id:%d\n", node->id(),
1647               node->op()->mnemonic(), block->id().ToInt());
1648         marking_queue_.clear();
1649         return block;
1650       }
1651       MarkBlock(use_block);
1652     }
1653 
1654     // Compute transitive marking closure; a block is marked if all its
1655     // successors are marked.
1656     do {
1657       BasicBlock* top_block = marking_queue_.front();
1658       marking_queue_.pop_front();
1659       if (IsMarked(top_block)) continue;
1660       bool marked = true;
1661       for (BasicBlock* successor : top_block->successors()) {
1662         if (!IsMarked(successor)) {
1663           marked = false;
1664           break;
1665         }
1666       }
1667       if (marked) MarkBlock(top_block);
1668     } while (!marking_queue_.empty());
1669 
1670     // If the (common dominator) {block} is marked, we know that all paths from
1671     // {block} to the end contain at least one use of {node}, and hence there's
1672     // no point in splitting the {node} in this case.
1673     if (IsMarked(block)) {
1674       TRACE("  not splitting #%d:%s, its common dominator id:%d is perfect\n",
1675             node->id(), node->op()->mnemonic(), block->id().ToInt());
1676       return block;
1677     }
1678 
1679     // Split {node} for uses according to the previously computed marking
1680     // closure. Every marking partition has a unique dominator, which get's a
1681     // copy of the {node} with the exception of the first partition, which get's
1682     // the {node} itself.
1683     ZoneMap<BasicBlock*, Node*> dominators(scheduler_->zone_);
1684     for (Edge edge : node->use_edges()) {
1685       if (!scheduler_->IsLive(edge.from())) continue;
1686       BasicBlock* use_block = GetBlockForUse(edge);
1687       if (use_block == nullptr) continue;
1688       while (IsMarked(use_block->dominator())) {
1689         use_block = use_block->dominator();
1690       }
1691       auto& use_node = dominators[use_block];
1692       if (use_node == nullptr) {
1693         if (dominators.size() == 1u) {
1694           // Place the {node} at {use_block}.
1695           block = use_block;
1696           use_node = node;
1697           TRACE("  pushing #%d:%s down to id:%d\n", node->id(),
1698                 node->op()->mnemonic(), block->id().ToInt());
1699         } else {
1700           // Place a copy of {node} at {use_block}.
1701           use_node = CloneNode(node);
1702           TRACE("  cloning #%d:%s for id:%d\n", use_node->id(),
1703                 use_node->op()->mnemonic(), use_block->id().ToInt());
1704           scheduler_->schedule_queue_.push(use_node);
1705         }
1706       }
1707       edge.UpdateTo(use_node);
1708     }
1709     return block;
1710   }
1711 
GetHoistBlock(BasicBlock * block)1712   BasicBlock* GetHoistBlock(BasicBlock* block) {
1713     if (!scheduler_->special_rpo_->HasLoopBlocks()) return nullptr;
1714     if (block->IsLoopHeader()) return block->dominator();
1715     // We have to check to make sure that the {block} dominates all
1716     // of the outgoing blocks.  If it doesn't, then there is a path
1717     // out of the loop which does not execute this {block}, so we
1718     // can't hoist operations from this {block} out of the loop, as
1719     // that would introduce additional computations.
1720     if (BasicBlock* header_block = block->loop_header()) {
1721       for (BasicBlock* outgoing_block :
1722            scheduler_->special_rpo_->GetOutgoingBlocks(header_block)) {
1723         if (scheduler_->GetCommonDominator(block, outgoing_block) != block) {
1724           return nullptr;
1725         }
1726       }
1727       return header_block->dominator();
1728     }
1729     return nullptr;
1730   }
1731 
GetCommonDominatorOfUses(Node * node)1732   BasicBlock* GetCommonDominatorOfUses(Node* node) {
1733     BasicBlock* block = nullptr;
1734     for (Edge edge : node->use_edges()) {
1735       if (!scheduler_->IsLive(edge.from())) continue;
1736       BasicBlock* use_block = GetBlockForUse(edge);
1737       block = block == nullptr
1738                   ? use_block
1739                   : use_block == nullptr
1740                         ? block
1741                         : scheduler_->GetCommonDominator(block, use_block);
1742     }
1743     return block;
1744   }
1745 
FindPredecessorBlock(Node * node)1746   BasicBlock* FindPredecessorBlock(Node* node) {
1747     return scheduler_->control_flow_builder_->FindPredecessorBlock(node);
1748   }
1749 
GetBlockForUse(Edge edge)1750   BasicBlock* GetBlockForUse(Edge edge) {
1751     // TODO(titzer): ignore uses from dead nodes (not visited in PrepareUses()).
1752     // Dead uses only occur if the graph is not trimmed before scheduling.
1753     Node* use = edge.from();
1754     if (IrOpcode::IsPhiOpcode(use->opcode())) {
1755       // If the use is from a coupled (i.e. floating) phi, compute the common
1756       // dominator of its uses. This will not recurse more than one level.
1757       if (scheduler_->GetPlacement(use) == Scheduler::kCoupled) {
1758         TRACE("  inspecting uses of coupled #%d:%s\n", use->id(),
1759               use->op()->mnemonic());
1760         // TODO(titzer): reenable once above TODO is addressed.
1761         //        DCHECK_EQ(edge.to(), NodeProperties::GetControlInput(use));
1762         return GetCommonDominatorOfUses(use);
1763       }
1764       // If the use is from a fixed (i.e. non-floating) phi, we use the
1765       // predecessor block of the corresponding control input to the merge.
1766       if (scheduler_->GetPlacement(use) == Scheduler::kFixed) {
1767         TRACE("  input@%d into a fixed phi #%d:%s\n", edge.index(), use->id(),
1768               use->op()->mnemonic());
1769         Node* merge = NodeProperties::GetControlInput(use, 0);
1770         DCHECK(IrOpcode::IsMergeOpcode(merge->opcode()));
1771         Node* input = NodeProperties::GetControlInput(merge, edge.index());
1772         return FindPredecessorBlock(input);
1773       }
1774     } else if (IrOpcode::IsMergeOpcode(use->opcode())) {
1775       // If the use is from a fixed (i.e. non-floating) merge, we use the
1776       // predecessor block of the current input to the merge.
1777       if (scheduler_->GetPlacement(use) == Scheduler::kFixed) {
1778         TRACE("  input@%d into a fixed merge #%d:%s\n", edge.index(), use->id(),
1779               use->op()->mnemonic());
1780         return FindPredecessorBlock(edge.to());
1781       }
1782     }
1783     BasicBlock* result = schedule_->block(use);
1784     if (result == nullptr) return nullptr;
1785     TRACE("  must dominate use #%d:%s in id:%d\n", use->id(),
1786           use->op()->mnemonic(), result->id().ToInt());
1787     return result;
1788   }
1789 
ScheduleFloatingControl(BasicBlock * block,Node * node)1790   void ScheduleFloatingControl(BasicBlock* block, Node* node) {
1791     scheduler_->FuseFloatingControl(block, node);
1792   }
1793 
ScheduleRegion(BasicBlock * block,Node * region_end)1794   void ScheduleRegion(BasicBlock* block, Node* region_end) {
1795     // We only allow regions of instructions connected into a linear
1796     // effect chain. The only value allowed to be produced by a node
1797     // in the chain must be the value consumed by the FinishRegion node.
1798 
1799     // We schedule back to front; we first schedule FinishRegion.
1800     CHECK_EQ(IrOpcode::kFinishRegion, region_end->opcode());
1801     ScheduleNode(block, region_end);
1802 
1803     // Schedule the chain.
1804     Node* node = NodeProperties::GetEffectInput(region_end);
1805     while (node->opcode() != IrOpcode::kBeginRegion) {
1806       DCHECK_EQ(0, scheduler_->GetData(node)->unscheduled_count_);
1807       DCHECK_EQ(1, node->op()->EffectInputCount());
1808       DCHECK_EQ(1, node->op()->EffectOutputCount());
1809       DCHECK_EQ(0, node->op()->ControlOutputCount());
1810       // The value output (if there is any) must be consumed
1811       // by the EndRegion node.
1812       DCHECK(node->op()->ValueOutputCount() == 0 ||
1813              node == region_end->InputAt(0));
1814       ScheduleNode(block, node);
1815       node = NodeProperties::GetEffectInput(node);
1816     }
1817     // Schedule the BeginRegion node.
1818     DCHECK_EQ(0, scheduler_->GetData(node)->unscheduled_count_);
1819     ScheduleNode(block, node);
1820   }
1821 
ScheduleNode(BasicBlock * block,Node * node)1822   void ScheduleNode(BasicBlock* block, Node* node) {
1823     schedule_->PlanNode(block, node);
1824     size_t block_id = block->id().ToSize();
1825     if (!scheduler_->scheduled_nodes_[block_id]) {
1826       scheduler_->scheduled_nodes_[block_id] = zone_->New<NodeVector>(zone_);
1827     }
1828     scheduler_->scheduled_nodes_[block_id]->push_back(node);
1829     scheduler_->UpdatePlacement(node, Scheduler::kScheduled);
1830   }
1831 
CloneNode(Node * node)1832   Node* CloneNode(Node* node) {
1833     int const input_count = node->InputCount();
1834     base::Optional<int> coupled_control_edge =
1835         scheduler_->GetCoupledControlEdge(node);
1836     for (int index = 0; index < input_count; ++index) {
1837       if (index != coupled_control_edge) {
1838         Node* const input = node->InputAt(index);
1839         scheduler_->IncrementUnscheduledUseCount(input, node);
1840       }
1841     }
1842     Node* const copy = scheduler_->graph_->CloneNode(node);
1843     TRACE(("clone #%d:%s -> #%d\n"), node->id(), node->op()->mnemonic(),
1844           copy->id());
1845     scheduler_->node_data_.resize(copy->id() + 1,
1846                                   scheduler_->DefaultSchedulerData());
1847     scheduler_->node_data_[copy->id()] = scheduler_->node_data_[node->id()];
1848     return copy;
1849   }
1850 
1851   Zone* zone_;
1852   Scheduler* scheduler_;
1853   Schedule* schedule_;
1854   BoolVector marked_;
1855   ZoneDeque<BasicBlock*> marking_queue_;
1856 };
1857 
1858 
ScheduleLate()1859 void Scheduler::ScheduleLate() {
1860   TRACE("--- SCHEDULE LATE ------------------------------------------\n");
1861   if (FLAG_trace_turbo_scheduler) {
1862     TRACE("roots: ");
1863     for (Node* node : schedule_root_nodes_) {
1864       TRACE("#%d:%s ", node->id(), node->op()->mnemonic());
1865     }
1866     TRACE("\n");
1867   }
1868 
1869   // Schedule: Places nodes in dominator block of all their uses.
1870   ScheduleLateNodeVisitor schedule_late_visitor(zone_, this);
1871   schedule_late_visitor.Run(&schedule_root_nodes_);
1872 }
1873 
1874 
1875 // -----------------------------------------------------------------------------
1876 // Phase 6: Seal the final schedule.
1877 
1878 
SealFinalSchedule()1879 void Scheduler::SealFinalSchedule() {
1880   TRACE("--- SEAL FINAL SCHEDULE ------------------------------------\n");
1881 
1882   // Serialize the assembly order and reverse-post-order numbering.
1883   special_rpo_->SerializeRPOIntoSchedule();
1884   special_rpo_->PrintAndVerifySpecialRPO();
1885 
1886   // Add collected nodes for basic blocks to their blocks in the right order.
1887   int block_num = 0;
1888   for (NodeVector* nodes : scheduled_nodes_) {
1889     BasicBlock::Id id = BasicBlock::Id::FromInt(block_num++);
1890     BasicBlock* block = schedule_->GetBlockById(id);
1891     if (nodes) {
1892       for (Node* node : base::Reversed(*nodes)) {
1893         schedule_->AddNode(block, node);
1894       }
1895     }
1896   }
1897 }
1898 
1899 
1900 // -----------------------------------------------------------------------------
1901 
1902 
FuseFloatingControl(BasicBlock * block,Node * node)1903 void Scheduler::FuseFloatingControl(BasicBlock* block, Node* node) {
1904   TRACE("--- FUSE FLOATING CONTROL ----------------------------------\n");
1905   if (FLAG_trace_turbo_scheduler) {
1906     StdoutStream{} << "Schedule before control flow fusion:\n" << *schedule_;
1907   }
1908 
1909   // Iterate on phase 1: Build control-flow graph.
1910   control_flow_builder_->Run(block, node);
1911 
1912   // Iterate on phase 2: Compute special RPO and dominator tree.
1913   special_rpo_->UpdateSpecialRPO(block, schedule_->block(node));
1914   // TODO(turbofan): Currently "iterate on" means "re-run". Fix that.
1915   for (BasicBlock* b = block->rpo_next(); b != nullptr; b = b->rpo_next()) {
1916     b->set_dominator_depth(-1);
1917     b->set_dominator(nullptr);
1918   }
1919   PropagateImmediateDominators(block->rpo_next());
1920 
1921   // Iterate on phase 4: Schedule nodes early.
1922   // TODO(turbofan): The following loop gathering the propagation roots is a
1923   // temporary solution and should be merged into the rest of the scheduler as
1924   // soon as the approach settled for all floating loops.
1925   NodeVector propagation_roots(control_flow_builder_->control_);
1926   for (Node* control : control_flow_builder_->control_) {
1927     for (Node* use : control->uses()) {
1928       if (NodeProperties::IsPhi(use) && IsLive(use)) {
1929         propagation_roots.push_back(use);
1930       }
1931     }
1932   }
1933   if (FLAG_trace_turbo_scheduler) {
1934     TRACE("propagation roots: ");
1935     for (Node* r : propagation_roots) {
1936       TRACE("#%d:%s ", r->id(), r->op()->mnemonic());
1937     }
1938     TRACE("\n");
1939   }
1940   ScheduleEarlyNodeVisitor schedule_early_visitor(zone_, this);
1941   schedule_early_visitor.Run(&propagation_roots);
1942 
1943   // Move previously planned nodes.
1944   // TODO(turbofan): Improve that by supporting bulk moves.
1945   scheduled_nodes_.resize(schedule_->BasicBlockCount());
1946   MovePlannedNodes(block, schedule_->block(node));
1947 
1948   if (FLAG_trace_turbo_scheduler) {
1949     StdoutStream{} << "Schedule after control flow fusion:\n" << *schedule_;
1950   }
1951 }
1952 
1953 
MovePlannedNodes(BasicBlock * from,BasicBlock * to)1954 void Scheduler::MovePlannedNodes(BasicBlock* from, BasicBlock* to) {
1955   TRACE("Move planned nodes from id:%d to id:%d\n", from->id().ToInt(),
1956         to->id().ToInt());
1957   NodeVector* from_nodes = scheduled_nodes_[from->id().ToSize()];
1958   NodeVector* to_nodes = scheduled_nodes_[to->id().ToSize()];
1959   if (!from_nodes) return;
1960 
1961   for (Node* const node : *from_nodes) {
1962     schedule_->SetBlockForNode(to, node);
1963   }
1964   if (to_nodes) {
1965     to_nodes->insert(to_nodes->end(), from_nodes->begin(), from_nodes->end());
1966     from_nodes->clear();
1967   } else {
1968     std::swap(scheduled_nodes_[from->id().ToSize()],
1969               scheduled_nodes_[to->id().ToSize()]);
1970   }
1971 }
1972 
1973 #undef TRACE
1974 
1975 }  // namespace compiler
1976 }  // namespace internal
1977 }  // namespace v8
1978