| OLD | NEW |
| 1 // Copyright 2013 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <deque> | 5 #include <deque> |
| 6 #include <queue> | 6 #include <queue> |
| 7 | 7 |
| 8 #include "src/compiler/scheduler.h" | 8 #include "src/compiler/scheduler.h" |
| 9 | 9 |
| 10 #include "src/compiler/graph.h" | 10 #include "src/compiler/graph.h" |
| (...skipping 17 matching lines...) Expand all Loading... |
| 28 } | 28 } |
| 29 | 29 |
| 30 | 30 |
| 31 Scheduler::Scheduler(Zone* zone, Graph* graph, Schedule* schedule) | 31 Scheduler::Scheduler(Zone* zone, Graph* graph, Schedule* schedule) |
| 32 : zone_(zone), | 32 : zone_(zone), |
| 33 graph_(graph), | 33 graph_(graph), |
| 34 schedule_(schedule), | 34 schedule_(schedule), |
| 35 scheduled_nodes_(zone), | 35 scheduled_nodes_(zone), |
| 36 schedule_root_nodes_(zone), | 36 schedule_root_nodes_(zone), |
| 37 schedule_queue_(zone), | 37 schedule_queue_(zone), |
| 38 node_data_(graph_->NodeCount(), DefaultSchedulerData(), zone), | 38 node_data_(graph_->NodeCount(), DefaultSchedulerData(), zone) {} |
| 39 has_floating_control_(false) {} | |
| 40 | 39 |
| 41 | 40 |
| 42 Schedule* Scheduler::ComputeSchedule(ZonePool* zone_pool, Graph* graph) { | 41 Schedule* Scheduler::ComputeSchedule(ZonePool* zone_pool, Graph* graph) { |
| 43 Schedule* schedule; | 42 ZonePool::Scope zone_scope(zone_pool); |
| 44 bool had_floating_control = false; | 43 Schedule* schedule = new (graph->zone()) |
| 45 do { | 44 Schedule(graph->zone(), static_cast<size_t>(graph->NodeCount())); |
| 46 ZonePool::Scope zone_scope(zone_pool); | 45 Scheduler scheduler(zone_scope.zone(), graph, schedule); |
| 47 schedule = new (graph->zone()) | |
| 48 Schedule(graph->zone(), static_cast<size_t>(graph->NodeCount())); | |
| 49 Scheduler scheduler(zone_scope.zone(), graph, schedule); | |
| 50 | 46 |
| 51 scheduler.BuildCFG(); | 47 scheduler.BuildCFG(); |
| 52 scheduler.ComputeSpecialRPONumbering(); | 48 scheduler.ComputeSpecialRPONumbering(); |
| 53 scheduler.GenerateImmediateDominatorTree(); | 49 scheduler.GenerateImmediateDominatorTree(); |
| 54 | 50 |
| 55 scheduler.PrepareUses(); | 51 scheduler.PrepareUses(); |
| 56 scheduler.ScheduleEarly(); | 52 scheduler.ScheduleEarly(); |
| 57 scheduler.ScheduleLate(); | 53 scheduler.ScheduleLate(); |
| 58 | |
| 59 had_floating_control = scheduler.ConnectFloatingControl(); | |
| 60 } while (had_floating_control); | |
| 61 | 54 |
| 62 return schedule; | 55 return schedule; |
| 63 } | 56 } |
| 64 | 57 |
| 65 | 58 |
| 66 Scheduler::SchedulerData Scheduler::DefaultSchedulerData() { | 59 Scheduler::SchedulerData Scheduler::DefaultSchedulerData() { |
| 67 SchedulerData def = {schedule_->start(), 0, false, false, kUnknown}; | 60 SchedulerData def = {schedule_->start(), 0, false, false, kUnknown}; |
| 68 return def; | 61 return def; |
| 69 } | 62 } |
| 70 | 63 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 85 break; | 78 break; |
| 86 case IrOpcode::kPhi: | 79 case IrOpcode::kPhi: |
| 87 case IrOpcode::kEffectPhi: { | 80 case IrOpcode::kEffectPhi: { |
| 88 // Phis and effect phis are fixed if their control inputs are, whereas | 81 // Phis and effect phis are fixed if their control inputs are, whereas |
| 89 // otherwise they are coupled to a floating control node. | 82 // otherwise they are coupled to a floating control node. |
| 90 Placement p = GetPlacement(NodeProperties::GetControlInput(node)); | 83 Placement p = GetPlacement(NodeProperties::GetControlInput(node)); |
| 91 data->placement_ = (p == kFixed ? kFixed : kCoupled); | 84 data->placement_ = (p == kFixed ? kFixed : kCoupled); |
| 92 break; | 85 break; |
| 93 } | 86 } |
| 94 #define DEFINE_FLOATING_CONTROL_CASE(V) case IrOpcode::k##V: | 87 #define DEFINE_FLOATING_CONTROL_CASE(V) case IrOpcode::k##V: |
| 95 CONTROL_OP_LIST(DEFINE_FLOATING_CONTROL_CASE) | 88 CONTROL_OP_LIST(DEFINE_FLOATING_CONTROL_CASE) |
| 96 #undef DEFINE_FLOATING_CONTROL_CASE | 89 #undef DEFINE_FLOATING_CONTROL_CASE |
| 97 { | 90 { |
| 98 // Control nodes that were not control-reachable from end may float. | 91 // Control nodes that were not control-reachable from end may float. |
| 99 data->placement_ = kSchedulable; | 92 data->placement_ = kSchedulable; |
| 100 if (!data->is_connected_control_) { | 93 if (!data->is_connected_control_) { |
| 101 data->is_floating_control_ = true; | 94 data->is_floating_control_ = true; |
| 102 has_floating_control_ = true; | 95 Trace("Floating control found: #%d:%s\n", node->id(), |
| 103 Trace("Floating control found: #%d:%s\n", node->id(), | 96 node->op()->mnemonic()); |
| 104 node->op()->mnemonic()); | |
| 105 } | |
| 106 break; | |
| 107 } | 97 } |
| 98 break; |
| 99 } |
| 108 default: | 100 default: |
| 109 data->placement_ = kSchedulable; | 101 data->placement_ = kSchedulable; |
| 110 break; | 102 break; |
| 111 } | 103 } |
| 112 } | 104 } |
| 113 return data->placement_; | 105 return data->placement_; |
| 114 } | 106 } |
| 115 | 107 |
| 116 | 108 |
| 109 void Scheduler::UpdatePlacement(Node* node, Placement placement) { |
| 110 SchedulerData* data = GetData(node); |
| 111 if (data->placement_ != kUnknown) { // Trap on mutation, not initialization. |
| 112 switch (node->opcode()) { |
| 113 case IrOpcode::kParameter: |
| 114 // Parameters are fixed once and for all. |
| 115 UNREACHABLE(); |
| 116 break; |
| 117 case IrOpcode::kPhi: |
| 118 case IrOpcode::kEffectPhi: { |
| 119 // Phis and effect phis are coupled to their respective blocks. |
| 120 DCHECK_EQ(Scheduler::kCoupled, data->placement_); |
| 121 DCHECK_EQ(Scheduler::kFixed, placement); |
| 122 Node* control = NodeProperties::GetControlInput(node); |
| 123 BasicBlock* block = schedule_->block(control); |
| 124 schedule_->AddNode(block, node); |
| 125 // TODO(mstarzinger): Cheap hack to make sure unscheduled use count of |
| 126 // control does not drop below zero. This might cause the control to be |
| 127 // queued for scheduling more than once, which makes this ugly! |
| 128 ++(GetData(control)->unscheduled_count_); |
| 129 break; |
| 130 } |
| 131 #define DEFINE_FLOATING_CONTROL_CASE(V) case IrOpcode::k##V: |
| 132 CONTROL_OP_LIST(DEFINE_FLOATING_CONTROL_CASE) |
| 133 #undef DEFINE_FLOATING_CONTROL_CASE |
| 134 { |
| 135 // Control nodes force coupled uses to be placed. |
| 136 Node::Uses uses = node->uses(); |
| 137 for (Node::Uses::iterator i = uses.begin(); i != uses.end(); ++i) { |
| 138 if (GetPlacement(*i) == Scheduler::kCoupled) { |
| 139 DCHECK_EQ(node, NodeProperties::GetControlInput(*i)); |
| 140 UpdatePlacement(*i, placement); |
| 141 } |
| 142 } |
| 143 break; |
| 144 } |
| 145 default: |
| 146 DCHECK_EQ(Scheduler::kSchedulable, data->placement_); |
| 147 DCHECK_EQ(Scheduler::kScheduled, placement); |
| 148 break; |
| 149 } |
| 150 // Reduce the use count of the node's inputs to potentially make them |
| 151 // schedulable. If all the uses of a node have been scheduled, then the node |
| 152 // itself can be scheduled. |
| 153 for (InputIter i = node->inputs().begin(); i != node->inputs().end(); ++i) { |
| 154 // TODO(mstarzinger): Another cheap hack for use counts. |
| 155 if (GetData(*i)->placement_ == kFixed) continue; |
| 156 DecrementUnscheduledUseCount(*i, i.edge().from()); |
| 157 } |
| 158 } |
| 159 data->placement_ = placement; |
| 160 } |
| 161 |
| 162 |
| 117 void Scheduler::IncrementUnscheduledUseCount(Node* node, Node* from) { | 163 void Scheduler::IncrementUnscheduledUseCount(Node* node, Node* from) { |
| 118 if (GetPlacement(node) == kCoupled) { | 164 if (GetPlacement(node) == kCoupled) { |
| 119 // Use count for coupled nodes is summed up on their control. | 165 // Use count for coupled nodes is summed up on their control. |
| 120 Node* control = NodeProperties::GetControlInput(node); | 166 Node* control = NodeProperties::GetControlInput(node); |
| 121 return IncrementUnscheduledUseCount(control, from); | 167 return IncrementUnscheduledUseCount(control, from); |
| 122 } | 168 } |
| 123 ++(GetData(node)->unscheduled_count_); | 169 ++(GetData(node)->unscheduled_count_); |
| 124 if (FLAG_trace_turbo_scheduler) { | 170 if (FLAG_trace_turbo_scheduler) { |
| 125 Trace(" Use count of #%d:%s (used by #%d:%s)++ = %d\n", node->id(), | 171 Trace(" Use count of #%d:%s (used by #%d:%s)++ = %d\n", node->id(), |
| 126 node->op()->mnemonic(), from->id(), from->op()->mnemonic(), | 172 node->op()->mnemonic(), from->id(), from->op()->mnemonic(), |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 170 } | 216 } |
| 171 return b1; | 217 return b1; |
| 172 } | 218 } |
| 173 | 219 |
| 174 | 220 |
| 175 // ----------------------------------------------------------------------------- | 221 // ----------------------------------------------------------------------------- |
| 176 // Phase 1: Build control-flow graph. | 222 // Phase 1: Build control-flow graph. |
| 177 | 223 |
| 178 | 224 |
| 179 // Internal class to build a control flow graph (i.e the basic blocks and edges | 225 // Internal class to build a control flow graph (i.e the basic blocks and edges |
| 180 // between them within a Schedule) from the node graph. | 226 // between them within a Schedule) from the node graph. Visits control edges of |
| 181 // Visits the control edges of the graph backwards from end in order to find | 227 // the graph backwards from an end node in order to find the connected control |
| 182 // the connected control subgraph, needed for scheduling. | 228 // subgraph, needed for scheduling. |
| 183 class CFGBuilder { | 229 class CFGBuilder { |
| 184 public: | 230 public: |
| 185 Scheduler* scheduler_; | |
| 186 Schedule* schedule_; | |
| 187 ZoneQueue<Node*> queue_; | |
| 188 NodeVector control_; | |
| 189 | |
| 190 CFGBuilder(Zone* zone, Scheduler* scheduler) | 231 CFGBuilder(Zone* zone, Scheduler* scheduler) |
| 191 : scheduler_(scheduler), | 232 : scheduler_(scheduler), |
| 192 schedule_(scheduler->schedule_), | 233 schedule_(scheduler->schedule_), |
| 193 queue_(zone), | 234 queue_(zone), |
| 194 control_(zone) {} | 235 control_(zone), |
| 236 component_head_(NULL), |
| 237 component_start_(NULL), |
| 238 component_end_(NULL) {} |
| 195 | 239 |
| 196 // Run the control flow graph construction algorithm by walking the graph | 240 // Run the control flow graph construction algorithm by walking the graph |
| 197 // backwards from end through control edges, building and connecting the | 241 // backwards from end through control edges, building and connecting the |
| 198 // basic blocks for control nodes. | 242 // basic blocks for control nodes. |
| 199 void Run() { | 243 void Run() { |
| 200 Graph* graph = scheduler_->graph_; | 244 Graph* graph = scheduler_->graph_; |
| 201 FixNode(schedule_->start(), graph->start()); | 245 FixNode(schedule_->start(), graph->start()); |
| 202 Queue(graph->end()); | 246 Queue(graph->end()); |
| 203 | 247 |
| 204 while (!queue_.empty()) { // Breadth-first backwards traversal. | 248 while (!queue_.empty()) { // Breadth-first backwards traversal. |
| 205 Node* node = queue_.front(); | 249 Node* node = queue_.front(); |
| 206 queue_.pop(); | 250 queue_.pop(); |
| 207 int max = NodeProperties::PastControlIndex(node); | 251 int max = NodeProperties::PastControlIndex(node); |
| 208 for (int i = NodeProperties::FirstControlIndex(node); i < max; i++) { | 252 for (int i = NodeProperties::FirstControlIndex(node); i < max; i++) { |
| 209 Queue(node->InputAt(i)); | 253 Queue(node->InputAt(i)); |
| 210 } | 254 } |
| 211 } | 255 } |
| 212 | 256 |
| 213 for (NodeVector::iterator i = control_.begin(); i != control_.end(); ++i) { | 257 for (NodeVector::iterator i = control_.begin(); i != control_.end(); ++i) { |
| 214 ConnectBlocks(*i); // Connect block to its predecessor/successors. | 258 ConnectBlocks(*i); // Connect block to its predecessor/successors. |
| 215 } | 259 } |
| 216 | 260 |
| 217 FixNode(schedule_->end(), graph->end()); | 261 FixNode(schedule_->end(), graph->end()); |
| 218 } | 262 } |
| 219 | 263 |
| 264 // Run the control flow graph construction for a minimal control-connected |
| 265 // component ending in {node} and merge that component into an existing |
| 266 // control flow graph at the bottom of {block}. |
| 267 void Run(BasicBlock* block, Node* node) { |
| 268 Queue(node); |
| 269 |
| 270 component_start_ = block; |
| 271 component_end_ = schedule_->block(node); |
| 272 while (!queue_.empty()) { // Breadth-first backwards traversal. |
| 273 Node* node = queue_.front(); |
| 274 queue_.pop(); |
| 275 bool is_dom = true; |
| 276 int max = NodeProperties::PastControlIndex(node); |
| 277 for (int i = NodeProperties::FirstControlIndex(node); i < max; i++) { |
| 278 is_dom = is_dom && |
| 279 !scheduler_->GetData(node->InputAt(i))->is_floating_control_; |
| 280 Queue(node->InputAt(i)); |
| 281 } |
| 282 // TODO(mstarzinger): This is a hacky way to find component dominator. |
| 283 if (is_dom) component_head_ = node; |
| 284 } |
| 285 DCHECK_NOT_NULL(component_head_); |
| 286 |
| 287 for (NodeVector::iterator i = control_.begin(); i != control_.end(); ++i) { |
| 288 scheduler_->GetData(*i)->is_floating_control_ = false; |
| 289 ConnectBlocks(*i); // Connect block to its predecessor/successors. |
| 290 } |
| 291 } |
| 292 |
| 293 private: |
| 220 void FixNode(BasicBlock* block, Node* node) { | 294 void FixNode(BasicBlock* block, Node* node) { |
| 221 schedule_->AddNode(block, node); | 295 schedule_->AddNode(block, node); |
| 222 scheduler_->GetData(node)->is_connected_control_ = true; | 296 scheduler_->GetData(node)->is_connected_control_ = true; |
| 223 scheduler_->GetData(node)->placement_ = Scheduler::kFixed; | 297 scheduler_->UpdatePlacement(node, Scheduler::kFixed); |
| 224 } | 298 } |
| 225 | 299 |
| 226 void Queue(Node* node) { | 300 void Queue(Node* node) { |
| 227 // Mark the connected control nodes as they queued. | 301 // Mark the connected control nodes as they queued. |
| 228 Scheduler::SchedulerData* data = scheduler_->GetData(node); | 302 Scheduler::SchedulerData* data = scheduler_->GetData(node); |
| 229 if (!data->is_connected_control_) { | 303 if (!data->is_connected_control_) { |
| 230 BuildBlocks(node); | 304 BuildBlocks(node); |
| 231 queue_.push(node); | 305 queue_.push(node); |
| 232 control_.push_back(node); | 306 control_.push_back(node); |
| 233 data->is_connected_control_ = true; | 307 data->is_connected_control_ = true; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 250 } | 324 } |
| 251 } | 325 } |
| 252 | 326 |
| 253 void ConnectBlocks(Node* node) { | 327 void ConnectBlocks(Node* node) { |
| 254 switch (node->opcode()) { | 328 switch (node->opcode()) { |
| 255 case IrOpcode::kLoop: | 329 case IrOpcode::kLoop: |
| 256 case IrOpcode::kMerge: | 330 case IrOpcode::kMerge: |
| 257 ConnectMerge(node); | 331 ConnectMerge(node); |
| 258 break; | 332 break; |
| 259 case IrOpcode::kBranch: | 333 case IrOpcode::kBranch: |
| 260 scheduler_->schedule_root_nodes_.push_back(node); | 334 scheduler_->UpdatePlacement(node, Scheduler::kFixed); |
| 261 ConnectBranch(node); | 335 ConnectBranch(node); |
| 262 break; | 336 break; |
| 263 case IrOpcode::kReturn: | 337 case IrOpcode::kReturn: |
| 264 scheduler_->schedule_root_nodes_.push_back(node); | 338 scheduler_->UpdatePlacement(node, Scheduler::kFixed); |
| 265 ConnectReturn(node); | 339 ConnectReturn(node); |
| 266 break; | 340 break; |
| 267 default: | 341 default: |
| 268 break; | 342 break; |
| 269 } | 343 } |
| 270 } | 344 } |
| 271 | 345 |
| 272 void BuildBlockForNode(Node* node) { | 346 void BuildBlockForNode(Node* node) { |
| 273 if (schedule_->block(node) == NULL) { | 347 if (schedule_->block(node) == NULL) { |
| 274 BasicBlock* block = schedule_->NewBasicBlock(); | 348 BasicBlock* block = schedule_->NewBasicBlock(); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 311 void CollectSuccessorBlocks(Node* node, BasicBlock** buffer, | 385 void CollectSuccessorBlocks(Node* node, BasicBlock** buffer, |
| 312 IrOpcode::Value true_opcode, | 386 IrOpcode::Value true_opcode, |
| 313 IrOpcode::Value false_opcode) { | 387 IrOpcode::Value false_opcode) { |
| 314 Node* successors[2]; | 388 Node* successors[2]; |
| 315 CollectSuccessorProjections(node, successors, true_opcode, false_opcode); | 389 CollectSuccessorProjections(node, successors, true_opcode, false_opcode); |
| 316 buffer[0] = schedule_->block(successors[0]); | 390 buffer[0] = schedule_->block(successors[0]); |
| 317 buffer[1] = schedule_->block(successors[1]); | 391 buffer[1] = schedule_->block(successors[1]); |
| 318 } | 392 } |
| 319 | 393 |
| 320 void ConnectBranch(Node* branch) { | 394 void ConnectBranch(Node* branch) { |
| 321 Node* branch_block_node = NodeProperties::GetControlInput(branch); | |
| 322 BasicBlock* branch_block = schedule_->block(branch_block_node); | |
| 323 DCHECK(branch_block != NULL); | |
| 324 | |
| 325 BasicBlock* successor_blocks[2]; | 395 BasicBlock* successor_blocks[2]; |
| 326 CollectSuccessorBlocks(branch, successor_blocks, IrOpcode::kIfTrue, | 396 CollectSuccessorBlocks(branch, successor_blocks, IrOpcode::kIfTrue, |
| 327 IrOpcode::kIfFalse); | 397 IrOpcode::kIfFalse); |
| 328 | 398 |
| 329 TraceConnect(branch, branch_block, successor_blocks[0]); | |
| 330 TraceConnect(branch, branch_block, successor_blocks[1]); | |
| 331 | |
| 332 // Consider branch hints. | 399 // Consider branch hints. |
| 333 // TODO(turbofan): Propagate the deferred flag to all blocks dominated by | 400 // TODO(turbofan): Propagate the deferred flag to all blocks dominated by |
| 334 // this IfTrue/IfFalse later. | 401 // this IfTrue/IfFalse later. |
| 335 switch (BranchHintOf(branch->op())) { | 402 switch (BranchHintOf(branch->op())) { |
| 336 case BranchHint::kNone: | 403 case BranchHint::kNone: |
| 337 break; | 404 break; |
| 338 case BranchHint::kTrue: | 405 case BranchHint::kTrue: |
| 339 successor_blocks[1]->set_deferred(true); | 406 successor_blocks[1]->set_deferred(true); |
| 340 break; | 407 break; |
| 341 case BranchHint::kFalse: | 408 case BranchHint::kFalse: |
| 342 successor_blocks[0]->set_deferred(true); | 409 successor_blocks[0]->set_deferred(true); |
| 343 break; | 410 break; |
| 344 } | 411 } |
| 345 | 412 |
| 346 schedule_->AddBranch(branch_block, branch, successor_blocks[0], | 413 if (branch == component_head_) { |
| 347 successor_blocks[1]); | 414 TraceConnect(branch, component_start_, successor_blocks[0]); |
| 415 TraceConnect(branch, component_start_, successor_blocks[1]); |
| 416 schedule_->InsertBranch(component_start_, component_end_, branch, |
| 417 successor_blocks[0], successor_blocks[1]); |
| 418 } else { |
| 419 Node* branch_block_node = NodeProperties::GetControlInput(branch); |
| 420 BasicBlock* branch_block = schedule_->block(branch_block_node); |
| 421 DCHECK(branch_block != NULL); |
| 422 |
| 423 TraceConnect(branch, branch_block, successor_blocks[0]); |
| 424 TraceConnect(branch, branch_block, successor_blocks[1]); |
| 425 schedule_->AddBranch(branch_block, branch, successor_blocks[0], |
| 426 successor_blocks[1]); |
| 427 } |
| 348 } | 428 } |
| 349 | 429 |
| 350 void ConnectMerge(Node* merge) { | 430 void ConnectMerge(Node* merge) { |
| 351 // Don't connect the special merge at the end to its predecessors. | 431 // Don't connect the special merge at the end to its predecessors. |
| 352 if (IsFinalMerge(merge)) return; | 432 if (IsFinalMerge(merge)) return; |
| 353 | 433 |
| 354 BasicBlock* block = schedule_->block(merge); | 434 BasicBlock* block = schedule_->block(merge); |
| 355 DCHECK(block != NULL); | 435 DCHECK(block != NULL); |
| 356 // For all of the merge's control inputs, add a goto at the end to the | 436 // For all of the merge's control inputs, add a goto at the end to the |
| 357 // merge's basic block. | 437 // merge's basic block. |
| (...skipping 20 matching lines...) Expand all Loading... |
| 378 } else { | 458 } else { |
| 379 Trace("Connect #%d:%s, B%d -> B%d\n", node->id(), node->op()->mnemonic(), | 459 Trace("Connect #%d:%s, B%d -> B%d\n", node->id(), node->op()->mnemonic(), |
| 380 block->id().ToInt(), succ->id().ToInt()); | 460 block->id().ToInt(), succ->id().ToInt()); |
| 381 } | 461 } |
| 382 } | 462 } |
| 383 | 463 |
| 384 bool IsFinalMerge(Node* node) { | 464 bool IsFinalMerge(Node* node) { |
| 385 return (node->opcode() == IrOpcode::kMerge && | 465 return (node->opcode() == IrOpcode::kMerge && |
| 386 node == scheduler_->graph_->end()->InputAt(0)); | 466 node == scheduler_->graph_->end()->InputAt(0)); |
| 387 } | 467 } |
| 468 |
| 469 Scheduler* scheduler_; |
| 470 Schedule* schedule_; |
| 471 ZoneQueue<Node*> queue_; |
| 472 NodeVector control_; |
| 473 Node* component_head_; |
| 474 BasicBlock* component_start_; |
| 475 BasicBlock* component_end_; |
| 388 }; | 476 }; |
| 389 | 477 |
| 390 | 478 |
| 391 void Scheduler::BuildCFG() { | 479 void Scheduler::BuildCFG() { |
| 392 Trace("--- CREATING CFG -------------------------------------------\n"); | 480 Trace("--- CREATING CFG -------------------------------------------\n"); |
| 481 |
| 482 // Build a control-flow graph for the main control-connected component that |
| 483 // is being spanned by the graph's start and end nodes. |
| 393 CFGBuilder cfg_builder(zone_, this); | 484 CFGBuilder cfg_builder(zone_, this); |
| 394 cfg_builder.Run(); | 485 cfg_builder.Run(); |
| 486 |
| 395 // Initialize per-block data. | 487 // Initialize per-block data. |
| 396 scheduled_nodes_.resize(schedule_->BasicBlockCount(), NodeVector(zone_)); | 488 scheduled_nodes_.resize(schedule_->BasicBlockCount(), NodeVector(zone_)); |
| 397 } | 489 } |
| 398 | 490 |
| 399 | 491 |
| 400 // ----------------------------------------------------------------------------- | 492 // ----------------------------------------------------------------------------- |
| 401 // Phase 2: Compute special RPO and dominator tree. | 493 // Phase 2: Compute special RPO and dominator tree. |
| 402 | 494 |
| 403 | 495 |
| 404 // Compute the special reverse-post-order block ordering, which is essentially | 496 // Compute the special reverse-post-order block ordering, which is essentially |
| (...skipping 455 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 860 // TODO(danno): consider using Lengauer & Tarjan's if this becomes too slow. | 952 // TODO(danno): consider using Lengauer & Tarjan's if this becomes too slow. |
| 861 for (size_t i = 0; i < schedule_->rpo_order_.size(); i++) { | 953 for (size_t i = 0; i < schedule_->rpo_order_.size(); i++) { |
| 862 BasicBlock* current_rpo = schedule_->rpo_order_[i]; | 954 BasicBlock* current_rpo = schedule_->rpo_order_[i]; |
| 863 if (current_rpo != schedule_->start()) { | 955 if (current_rpo != schedule_->start()) { |
| 864 BasicBlock::Predecessors::iterator current_pred = | 956 BasicBlock::Predecessors::iterator current_pred = |
| 865 current_rpo->predecessors_begin(); | 957 current_rpo->predecessors_begin(); |
| 866 BasicBlock::Predecessors::iterator end = current_rpo->predecessors_end(); | 958 BasicBlock::Predecessors::iterator end = current_rpo->predecessors_end(); |
| 867 DCHECK(current_pred != end); | 959 DCHECK(current_pred != end); |
| 868 BasicBlock* dominator = *current_pred; | 960 BasicBlock* dominator = *current_pred; |
| 869 ++current_pred; | 961 ++current_pred; |
| 870 // For multiple predecessors, walk up the rpo ordering until a common | 962 // For multiple predecessors, walk up the RPO ordering until a common |
| 871 // dominator is found. | 963 // dominator is found. |
| 872 int current_rpo_pos = GetRPONumber(current_rpo); | 964 int current_rpo_pos = GetRPONumber(current_rpo); |
| 873 while (current_pred != end) { | 965 while (current_pred != end) { |
| 874 // Don't examine backwards edges | 966 // Don't examine backwards edges |
| 875 BasicBlock* pred = *current_pred; | 967 BasicBlock* pred = *current_pred; |
| 876 if (GetRPONumber(pred) < current_rpo_pos) { | 968 if (GetRPONumber(pred) < current_rpo_pos) { |
| 877 dominator = GetCommonDominator(dominator, *current_pred); | 969 dominator = GetCommonDominator(dominator, *current_pred); |
| 878 } | 970 } |
| 879 ++current_pred; | 971 ++current_pred; |
| 880 } | 972 } |
| (...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1084 // optimal scheduling position. | 1176 // optimal scheduling position. |
| 1085 void VisitNode(Node* node) { | 1177 void VisitNode(Node* node) { |
| 1086 DCHECK_EQ(0, scheduler_->GetData(node)->unscheduled_count_); | 1178 DCHECK_EQ(0, scheduler_->GetData(node)->unscheduled_count_); |
| 1087 | 1179 |
| 1088 // Don't schedule nodes that are already scheduled. | 1180 // Don't schedule nodes that are already scheduled. |
| 1089 if (schedule_->IsScheduled(node)) return; | 1181 if (schedule_->IsScheduled(node)) return; |
| 1090 DCHECK_EQ(Scheduler::kSchedulable, scheduler_->GetPlacement(node)); | 1182 DCHECK_EQ(Scheduler::kSchedulable, scheduler_->GetPlacement(node)); |
| 1091 | 1183 |
| 1092 // Determine the dominating block for all of the uses of this node. It is | 1184 // Determine the dominating block for all of the uses of this node. It is |
| 1093 // the latest block that this node can be scheduled in. | 1185 // the latest block that this node can be scheduled in. |
| 1186 Trace("Scheduling #%d:%s\n", node->id(), node->op()->mnemonic()); |
| 1094 BasicBlock* block = GetCommonDominatorOfUses(node); | 1187 BasicBlock* block = GetCommonDominatorOfUses(node); |
| 1095 DCHECK_NOT_NULL(block); | 1188 DCHECK_NOT_NULL(block); |
| 1096 | 1189 |
| 1097 int min_rpo = scheduler_->GetData(node)->minimum_block_->rpo_number(); | 1190 int min_rpo = scheduler_->GetData(node)->minimum_block_->rpo_number(); |
| 1098 Trace("Schedule late of #%d:%s is B%d at loop depth %d, minimum_rpo = %d\n", | 1191 Trace("Schedule late of #%d:%s is B%d at loop depth %d, minimum_rpo = %d\n", |
| 1099 node->id(), node->op()->mnemonic(), block->id().ToInt(), | 1192 node->id(), node->op()->mnemonic(), block->id().ToInt(), |
| 1100 block->loop_depth(), min_rpo); | 1193 block->loop_depth(), min_rpo); |
| 1101 | 1194 |
| 1102 // Hoist nodes out of loops if possible. Nodes can be hoisted iteratively | 1195 // Hoist nodes out of loops if possible. Nodes can be hoisted iteratively |
| 1103 // into enclosing loop pre-headers until they would preceed their | 1196 // into enclosing loop pre-headers until they would preceed their |
| 1104 // ScheduleEarly position. | 1197 // ScheduleEarly position. |
| 1105 BasicBlock* hoist_block = GetPreHeader(block); | 1198 BasicBlock* hoist_block = GetPreHeader(block); |
| 1106 while (hoist_block != NULL && hoist_block->rpo_number() >= min_rpo) { | 1199 while (hoist_block != NULL && hoist_block->rpo_number() >= min_rpo) { |
| 1107 Trace(" hoisting #%d:%s to block %d\n", node->id(), | 1200 Trace(" hoisting #%d:%s to block %d\n", node->id(), |
| 1108 node->op()->mnemonic(), hoist_block->id().ToInt()); | 1201 node->op()->mnemonic(), hoist_block->id().ToInt()); |
| 1109 DCHECK_LT(hoist_block->loop_depth(), block->loop_depth()); | 1202 DCHECK_LT(hoist_block->loop_depth(), block->loop_depth()); |
| 1110 block = hoist_block; | 1203 block = hoist_block; |
| 1111 hoist_block = GetPreHeader(hoist_block); | 1204 hoist_block = GetPreHeader(hoist_block); |
| 1112 } | 1205 } |
| 1113 | 1206 |
| 1114 ScheduleNode(block, node); | 1207 // Schedule the node or a floating control structure. |
| 1208 if (NodeProperties::IsControl(node)) { |
| 1209 ScheduleFloatingControl(block, node); |
| 1210 } else { |
| 1211 ScheduleNode(block, node); |
| 1212 } |
| 1115 } | 1213 } |
| 1116 | 1214 |
| 1117 BasicBlock* GetPreHeader(BasicBlock* block) { | 1215 BasicBlock* GetPreHeader(BasicBlock* block) { |
| 1118 if (block->IsLoopHeader()) { | 1216 if (block->IsLoopHeader()) { |
| 1119 return block->dominator(); | 1217 return block->dominator(); |
| 1120 } else if (block->loop_header() != NULL) { | 1218 } else if (block->loop_header() != NULL) { |
| 1121 return block->loop_header()->dominator(); | 1219 return block->loop_header()->dominator(); |
| 1122 } else { | 1220 } else { |
| 1123 return NULL; | 1221 return NULL; |
| 1124 } | 1222 } |
| (...skipping 12 matching lines...) Expand all Loading... |
| 1137 return block; | 1235 return block; |
| 1138 } | 1236 } |
| 1139 | 1237 |
| 1140 BasicBlock* GetBlockForUse(Node::Edge edge) { | 1238 BasicBlock* GetBlockForUse(Node::Edge edge) { |
| 1141 Node* use = edge.from(); | 1239 Node* use = edge.from(); |
| 1142 IrOpcode::Value opcode = use->opcode(); | 1240 IrOpcode::Value opcode = use->opcode(); |
| 1143 if (opcode == IrOpcode::kPhi || opcode == IrOpcode::kEffectPhi) { | 1241 if (opcode == IrOpcode::kPhi || opcode == IrOpcode::kEffectPhi) { |
| 1144 // If the use is from a coupled (i.e. floating) phi, compute the common | 1242 // If the use is from a coupled (i.e. floating) phi, compute the common |
| 1145 // dominator of its uses. This will not recurse more than one level. | 1243 // dominator of its uses. This will not recurse more than one level. |
| 1146 if (scheduler_->GetPlacement(use) == Scheduler::kCoupled) { | 1244 if (scheduler_->GetPlacement(use) == Scheduler::kCoupled) { |
| 1147 Trace(" inspecting uses of coupled phi #%d:%s\n", use->id(), | 1245 Trace(" inspecting uses of coupled #%d:%s\n", use->id(), |
| 1148 use->op()->mnemonic()); | 1246 use->op()->mnemonic()); |
| 1149 DCHECK_EQ(edge.to(), NodeProperties::GetControlInput(use)); | 1247 DCHECK_EQ(edge.to(), NodeProperties::GetControlInput(use)); |
| 1150 return GetCommonDominatorOfUses(use); | 1248 return GetCommonDominatorOfUses(use); |
| 1151 } | 1249 } |
| 1152 // If the use is from a fixed (i.e. non-floating) phi, use the block | 1250 // If the use is from a fixed (i.e. non-floating) phi, use the block |
| 1153 // of the corresponding control input to the merge. | 1251 // of the corresponding control input to the merge. |
| 1154 if (scheduler_->GetPlacement(use) == Scheduler::kFixed) { | 1252 if (scheduler_->GetPlacement(use) == Scheduler::kFixed) { |
| 1155 Trace(" input@%d into a fixed phi #%d:%s\n", edge.index(), use->id(), | 1253 Trace(" input@%d into a fixed phi #%d:%s\n", edge.index(), use->id(), |
| 1156 use->op()->mnemonic()); | 1254 use->op()->mnemonic()); |
| 1157 Node* merge = NodeProperties::GetControlInput(use, 0); | 1255 Node* merge = NodeProperties::GetControlInput(use, 0); |
| 1158 opcode = merge->opcode(); | 1256 opcode = merge->opcode(); |
| 1159 DCHECK(opcode == IrOpcode::kMerge || opcode == IrOpcode::kLoop); | 1257 DCHECK(opcode == IrOpcode::kMerge || opcode == IrOpcode::kLoop); |
| 1160 use = NodeProperties::GetControlInput(merge, edge.index()); | 1258 use = NodeProperties::GetControlInput(merge, edge.index()); |
| 1161 } | 1259 } |
| 1162 } | 1260 } |
| 1163 BasicBlock* result = schedule_->block(use); | 1261 BasicBlock* result = schedule_->block(use); |
| 1164 if (result == NULL) return NULL; | 1262 if (result == NULL) return NULL; |
| 1165 Trace(" must dominate use #%d:%s in B%d\n", use->id(), | 1263 Trace(" must dominate use #%d:%s in B%d\n", use->id(), |
| 1166 use->op()->mnemonic(), result->id().ToInt()); | 1264 use->op()->mnemonic(), result->id().ToInt()); |
| 1167 return result; | 1265 return result; |
| 1168 } | 1266 } |
| 1169 | 1267 |
| 1268 void ScheduleFloatingControl(BasicBlock* block, Node* node) { |
| 1269 DCHECK(scheduler_->GetData(node)->is_floating_control_); |
| 1270 scheduler_->FuseFloatingControl(block, node); |
| 1271 } |
| 1272 |
| 1170 void ScheduleNode(BasicBlock* block, Node* node) { | 1273 void ScheduleNode(BasicBlock* block, Node* node) { |
| 1171 schedule_->PlanNode(block, node); | 1274 schedule_->PlanNode(block, node); |
| 1172 scheduler_->scheduled_nodes_[block->id().ToSize()].push_back(node); | 1275 scheduler_->scheduled_nodes_[block->id().ToSize()].push_back(node); |
| 1173 | 1276 scheduler_->UpdatePlacement(node, Scheduler::kScheduled); |
| 1174 // Reduce the use count of the node's inputs to potentially make them | |
| 1175 // schedulable. If all the uses of a node have been scheduled, then the node | |
| 1176 // itself can be scheduled. | |
| 1177 for (InputIter i = node->inputs().begin(); i != node->inputs().end(); ++i) { | |
| 1178 scheduler_->DecrementUnscheduledUseCount(*i, i.edge().from()); | |
| 1179 } | |
| 1180 } | 1277 } |
| 1181 | 1278 |
| 1182 Scheduler* scheduler_; | 1279 Scheduler* scheduler_; |
| 1183 Schedule* schedule_; | 1280 Schedule* schedule_; |
| 1184 }; | 1281 }; |
| 1185 | 1282 |
| 1186 | 1283 |
| 1187 void Scheduler::ScheduleLate() { | 1284 void Scheduler::ScheduleLate() { |
| 1188 Trace("--- SCHEDULE LATE ------------------------------------------\n"); | 1285 Trace("--- SCHEDULE LATE ------------------------------------------\n"); |
| 1189 if (FLAG_trace_turbo_scheduler) { | 1286 if (FLAG_trace_turbo_scheduler) { |
| (...skipping 17 matching lines...) Expand all Loading... |
| 1207 schedule_->AddNode(schedule_->all_blocks_.at(block_num), *j); | 1304 schedule_->AddNode(schedule_->all_blocks_.at(block_num), *j); |
| 1208 } | 1305 } |
| 1209 block_num++; | 1306 block_num++; |
| 1210 } | 1307 } |
| 1211 } | 1308 } |
| 1212 | 1309 |
| 1213 | 1310 |
| 1214 // ----------------------------------------------------------------------------- | 1311 // ----------------------------------------------------------------------------- |
| 1215 | 1312 |
| 1216 | 1313 |
| 1217 bool Scheduler::ConnectFloatingControl() { | 1314 void Scheduler::FuseFloatingControl(BasicBlock* block, Node* node) { |
| 1218 if (!has_floating_control_) return false; | 1315 Trace("--- FUSE FLOATING CONTROL ----------------------------------\n"); |
| 1219 | 1316 if (FLAG_trace_turbo_scheduler) { |
| 1220 Trace("Connecting floating control...\n"); | 1317 OFStream os(stdout); |
| 1221 | 1318 os << "Schedule before control flow fusion:\n" << *schedule_; |
| 1222 // Process blocks and instructions backwards to find and connect floating | |
| 1223 // control nodes into the control graph according to the block they were | |
| 1224 // scheduled into. | |
| 1225 int max = static_cast<int>(schedule_->rpo_order()->size()); | |
| 1226 for (int i = max - 1; i >= 0; i--) { | |
| 1227 BasicBlock* block = schedule_->rpo_order()->at(i); | |
| 1228 // TODO(titzer): we place at most one floating control structure per | |
| 1229 // basic block because scheduling currently can interleave phis from | |
| 1230 // one subgraph with the merges from another subgraph. | |
| 1231 for (size_t j = 0; j < block->NodeCount(); j++) { | |
| 1232 Node* node = block->NodeAt(block->NodeCount() - 1 - j); | |
| 1233 SchedulerData* data = GetData(node); | |
| 1234 if (data->is_floating_control_ && !data->is_connected_control_) { | |
| 1235 Trace(" Floating control #%d:%s was scheduled in B%d\n", node->id(), | |
| 1236 node->op()->mnemonic(), block->id().ToInt()); | |
| 1237 ConnectFloatingControlSubgraph(block, node); | |
| 1238 break; | |
| 1239 } | |
| 1240 } | |
| 1241 } | 1319 } |
| 1242 | 1320 |
| 1243 return true; | 1321 // Iterate on phase 1: Build control-flow graph. |
| 1322 CFGBuilder cfg_builder(zone_, this); |
| 1323 cfg_builder.Run(block, node); |
| 1324 |
| 1325 // Iterate on phase 2: Compute special RPO and dominator tree. |
| 1326 // TODO(mstarzinger): Currently "iterate on" means "re-run". Fix that. |
| 1327 BasicBlockVector* rpo = schedule_->rpo_order(); |
| 1328 for (BasicBlockVectorIter i = rpo->begin(); i != rpo->end(); ++i) { |
| 1329 BasicBlock* block = *i; |
| 1330 block->set_rpo_number(-1); |
| 1331 block->set_loop_header(NULL); |
| 1332 block->set_loop_depth(0); |
| 1333 block->set_loop_end(-1); |
| 1334 } |
| 1335 schedule_->rpo_order()->clear(); |
| 1336 SpecialRPONumberer numberer(zone_, schedule_); |
| 1337 numberer.ComputeSpecialRPO(); |
| 1338 GenerateImmediateDominatorTree(); |
| 1339 scheduled_nodes_.resize(schedule_->BasicBlockCount(), NodeVector(zone_)); |
| 1340 |
| 1341 // Move previously planned nodes. |
| 1342 // TODO(mstarzinger): Improve that by supporting bulk moves. |
| 1343 MovePlannedNodes(block, schedule_->block(node)); |
| 1344 |
| 1345 if (FLAG_trace_turbo_scheduler) { |
| 1346 OFStream os(stdout); |
| 1347 os << "Schedule after control flow fusion:" << *schedule_; |
| 1348 } |
| 1244 } | 1349 } |
| 1245 | 1350 |
| 1246 | 1351 |
| 1247 void Scheduler::ConnectFloatingControlSubgraph(BasicBlock* block, Node* end) { | 1352 void Scheduler::MovePlannedNodes(BasicBlock* from, BasicBlock* to) { |
| 1248 Node* block_start = block->NodeAt(0); | 1353 Trace("Move planned nodes from B%d to B%d\n", from->id().ToInt(), |
| 1249 DCHECK(IrOpcode::IsControlOpcode(block_start->opcode())); | 1354 to->id().ToInt()); |
| 1250 // Find the current "control successor" of the node that starts the block | 1355 NodeVector* nodes = &(scheduled_nodes_[from->id().ToSize()]); |
| 1251 // by searching the control uses for a control input edge from a connected | 1356 for (NodeVectorIter i = nodes->begin(); i != nodes->end(); ++i) { |
| 1252 // control node. | 1357 schedule_->SetBlockForNode(to, *i); |
| 1253 Node* control_succ = NULL; | 1358 scheduled_nodes_[to->id().ToSize()].push_back(*i); |
| 1254 for (UseIter i = block_start->uses().begin(); i != block_start->uses().end(); | |
| 1255 ++i) { | |
| 1256 Node::Edge edge = i.edge(); | |
| 1257 if (NodeProperties::IsControlEdge(edge) && | |
| 1258 GetData(edge.from())->is_connected_control_) { | |
| 1259 DCHECK_EQ(NULL, control_succ); | |
| 1260 control_succ = edge.from(); | |
| 1261 control_succ->ReplaceInput(edge.index(), end); | |
| 1262 } | |
| 1263 } | 1359 } |
| 1264 DCHECK_NE(NULL, control_succ); | 1360 nodes->clear(); |
| 1265 Trace(" Inserting floating control end %d:%s between %d:%s -> %d:%s\n", | |
| 1266 end->id(), end->op()->mnemonic(), control_succ->id(), | |
| 1267 control_succ->op()->mnemonic(), block_start->id(), | |
| 1268 block_start->op()->mnemonic()); | |
| 1269 | |
| 1270 // Find the "start" node of the control subgraph, which should be the | |
| 1271 // unique node that is itself floating control but has a control input that | |
| 1272 // is not floating. | |
| 1273 Node* start = NULL; | |
| 1274 ZoneQueue<Node*> queue(zone_); | |
| 1275 queue.push(end); | |
| 1276 GetData(end)->is_connected_control_ = true; | |
| 1277 while (!queue.empty()) { | |
| 1278 Node* node = queue.front(); | |
| 1279 queue.pop(); | |
| 1280 Trace(" Search #%d:%s for control subgraph start\n", node->id(), | |
| 1281 node->op()->mnemonic()); | |
| 1282 int max = NodeProperties::PastControlIndex(node); | |
| 1283 for (int i = NodeProperties::FirstControlIndex(node); i < max; i++) { | |
| 1284 Node* input = node->InputAt(i); | |
| 1285 SchedulerData* data = GetData(input); | |
| 1286 if (data->is_floating_control_) { | |
| 1287 // {input} is floating control. | |
| 1288 if (!data->is_connected_control_) { | |
| 1289 // First time seeing {input} during this traversal, queue it. | |
| 1290 queue.push(input); | |
| 1291 data->is_connected_control_ = true; | |
| 1292 } | |
| 1293 } else { | |
| 1294 // Otherwise, {node} is the start node, because it is floating control | |
| 1295 // but is connected to {input} that is not floating control. | |
| 1296 DCHECK_EQ(NULL, start); // There can be only one. | |
| 1297 start = node; | |
| 1298 } | |
| 1299 } | |
| 1300 } | |
| 1301 | |
| 1302 DCHECK_NE(NULL, start); | |
| 1303 start->ReplaceInput(NodeProperties::FirstControlIndex(start), block_start); | |
| 1304 | |
| 1305 Trace(" Connecting floating control start %d:%s to %d:%s\n", start->id(), | |
| 1306 start->op()->mnemonic(), block_start->id(), | |
| 1307 block_start->op()->mnemonic()); | |
| 1308 } | 1361 } |
| 1309 | 1362 |
| 1310 } // namespace compiler | 1363 } // namespace compiler |
| 1311 } // namespace internal | 1364 } // namespace internal |
| 1312 } // namespace v8 | 1365 } // namespace v8 |
| OLD | NEW |