Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(203)

Side by Side Diff: src/compiler/scheduler.cc

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

Powered by Google App Engine
This is Rietveld 408576698