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 21namespace v8 { 22namespace internal { 23namespace compiler { 24 25#define TRACE(...) \ 26 do { \ 27 if (FLAG_trace_turbo_scheduler) PrintF(__VA_ARGS__); \ 28 } while (false) 29 30Scheduler::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 48Schedule* 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 77Scheduler::SchedulerData Scheduler::DefaultSchedulerData() { 78 SchedulerData def = {schedule_->start(), 0, kUnknown}; 79 return def; 80} 81 82 83Scheduler::SchedulerData* Scheduler::GetData(Node* node) { 84 return &node_data_[node->id()]; 85} 86 87Scheduler::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 117Scheduler::Placement Scheduler::GetPlacement(Node* node) { 118 return GetData(node)->placement_; 119} 120 121bool Scheduler::IsLive(Node* node) { return GetPlacement(node) != kUnknown; } 122 123void 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 179base::Optional<int> Scheduler::GetCoupledControlEdge(Node* node) { 180 if (GetPlacement(node) == kCoupled) { 181 return NodeProperties::FirstControlIndex(node); 182 } 183 return {}; 184} 185 186void 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 205void 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. 237class CFGBuilder : public ZoneObject { 238 public: 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. 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}. 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 313 void FixNode(BasicBlock* block, Node* node) { 314 schedule_->AddNode(block, node); 315 scheduler_->UpdatePlacement(node, Scheduler::kFixed); 316 } 317 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 613 bool IsFinalMerge(Node* node) { 614 return (node->opcode() == IrOpcode::kMerge && 615 node == scheduler_->graph_->end()->InputAt(0)); 616 } 617 618 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 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 642void 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). 675class SpecialRPONumberer : public ZoneObject { 676 public: 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. 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. 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. 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. 716 void PrintAndVerifySpecialRPO() { 717#if DEBUG 718 if (FLAG_trace_turbo_scheduler) PrintRPO(); 719 VerifySpecialRPO(); 720#endif 721 } 722 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 731 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 756 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 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 774 BasicBlock* PushFront(BasicBlock* head, BasicBlock* block) { 775 block->set_rpo_next(head); 776 return block; 777 } 778 779 static int GetLoopNumber(BasicBlock* block) { return block->loop_number(); } 780 static void SetLoopNumber(BasicBlock* block, int loop_number) { 781 return block->set_loop_number(loop_number); 782 } 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). 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. 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. 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 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 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 1156BasicBlockVector* 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 1165void 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 1173BasicBlock* 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 1182BasicBlock* 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 1262void 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 1298void 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 1306void Scheduler::GenerateDominatorTree() { 1307 TRACE("--- IMMEDIATE BLOCK DOMINATORS -----------------------------\n"); 1308 GenerateDominatorTree(schedule_); 1309} 1310 1311// ----------------------------------------------------------------------------- 1312// Phase 3: Prepare use counts for nodes. 1313 1314 1315class PrepareUsesVisitor { 1316 public: 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 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: 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 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 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 1387void 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 1401class ScheduleEarlyNodeVisitor { 1402 public: 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. 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. 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. 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 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 1488void 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 1514class ScheduleLateNodeVisitor { 1515 public: 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. 1524 void Run(NodeVector* roots) { 1525 for (Node* const root : *roots) { 1526 ProcessQueue(root); 1527 } 1528 } 1529 1530 private: 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. 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 1607 bool IsMarked(BasicBlock* block) const { 1608 DCHECK_LT(block->id().ToSize(), marked_.size()); 1609 return marked_[block->id().ToSize()]; 1610 } 1611 1612 void Mark(BasicBlock* block) { marked_[block->id().ToSize()] = true; } 1613 1614 // Mark {block} and push its non-marked predecessor on the marking queue. 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 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 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 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 1746 BasicBlock* FindPredecessorBlock(Node* node) { 1747 return scheduler_->control_flow_builder_->FindPredecessorBlock(node); 1748 } 1749 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 1790 void ScheduleFloatingControl(BasicBlock* block, Node* node) { 1791 scheduler_->FuseFloatingControl(block, node); 1792 } 1793 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 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 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 1859void 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 1879void 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 1903void 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 1954void 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