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/schedule.h"
6
7 #include "src/compiler/node-properties.h"
8 #include "src/compiler/node.h"
9 #include "src/utils/ostreams.h"
10
11 namespace v8 {
12 namespace internal {
13 namespace compiler {
14
BasicBlock(Zone * zone,Id id)15 BasicBlock::BasicBlock(Zone* zone, Id id)
16 : loop_number_(-1),
17 rpo_number_(-1),
18 deferred_(false),
19 dominator_depth_(-1),
20 dominator_(nullptr),
21 rpo_next_(nullptr),
22 loop_header_(nullptr),
23 loop_end_(nullptr),
24 loop_depth_(0),
25 control_(kNone),
26 control_input_(nullptr),
27 nodes_(zone),
28 successors_(zone),
29 predecessors_(zone),
30 #if DEBUG
31 debug_info_(AssemblerDebugInfo(nullptr, nullptr, -1)),
32 #endif
33 id_(id) {
34 }
35
LoopContains(BasicBlock * block) const36 bool BasicBlock::LoopContains(BasicBlock* block) const {
37 // RPO numbers must be initialized.
38 DCHECK_LE(0, rpo_number_);
39 DCHECK_LE(0, block->rpo_number_);
40 if (loop_end_ == nullptr) return false; // This is not a loop.
41 return block->rpo_number_ >= rpo_number_ &&
42 block->rpo_number_ < loop_end_->rpo_number_;
43 }
44
AddSuccessor(BasicBlock * successor)45 void BasicBlock::AddSuccessor(BasicBlock* successor) {
46 successors_.push_back(successor);
47 }
48
AddPredecessor(BasicBlock * predecessor)49 void BasicBlock::AddPredecessor(BasicBlock* predecessor) {
50 predecessors_.push_back(predecessor);
51 }
52
RemovePredecessor(size_t index)53 void BasicBlock::RemovePredecessor(size_t index) {
54 predecessors_.erase(predecessors_.begin() + index);
55 }
56
AddNode(Node * node)57 void BasicBlock::AddNode(Node* node) { nodes_.push_back(node); }
58
set_control(Control control)59 void BasicBlock::set_control(Control control) { control_ = control; }
60
set_control_input(Node * control_input)61 void BasicBlock::set_control_input(Node* control_input) {
62 if (!nodes_.empty() && control_input == nodes_.back()) {
63 nodes_.pop_back();
64 }
65 control_input_ = control_input;
66 }
67
set_loop_depth(int32_t loop_depth)68 void BasicBlock::set_loop_depth(int32_t loop_depth) {
69 loop_depth_ = loop_depth;
70 }
71
set_rpo_number(int32_t rpo_number)72 void BasicBlock::set_rpo_number(int32_t rpo_number) {
73 rpo_number_ = rpo_number;
74 }
75
set_loop_end(BasicBlock * loop_end)76 void BasicBlock::set_loop_end(BasicBlock* loop_end) { loop_end_ = loop_end; }
77
set_loop_header(BasicBlock * loop_header)78 void BasicBlock::set_loop_header(BasicBlock* loop_header) {
79 loop_header_ = loop_header;
80 }
81
TrimNodes(iterator new_end)82 void BasicBlock::TrimNodes(iterator new_end) { nodes_.erase(new_end, end()); }
83
ResetRPOInfo()84 void BasicBlock::ResetRPOInfo() {
85 loop_number_ = -1;
86 rpo_number_ = -1;
87 dominator_depth_ = -1;
88 dominator_ = nullptr;
89 rpo_next_ = nullptr;
90 loop_header_ = nullptr;
91 loop_end_ = nullptr;
92 loop_depth_ = 0;
93 }
94
95 // static
GetCommonDominator(BasicBlock * b1,BasicBlock * b2)96 BasicBlock* BasicBlock::GetCommonDominator(BasicBlock* b1, BasicBlock* b2) {
97 while (b1 != b2) {
98 if (b1->dominator_depth() < b2->dominator_depth()) {
99 b2 = b2->dominator();
100 } else {
101 b1 = b1->dominator();
102 }
103 }
104 return b1;
105 }
106
Print()107 void BasicBlock::Print() { StdoutStream{} << this; }
108
operator <<(std::ostream & os,const BasicBlock & block)109 std::ostream& operator<<(std::ostream& os, const BasicBlock& block) {
110 os << "B" << block.id();
111 #if DEBUG
112 AssemblerDebugInfo info = block.debug_info();
113 if (info.name) os << info;
114 // Print predecessor blocks for better debugging.
115 const int kMaxDisplayedBlocks = 4;
116 int i = 0;
117 const BasicBlock* current_block = █
118 while (current_block->PredecessorCount() > 0 && i++ < kMaxDisplayedBlocks) {
119 current_block = current_block->predecessors().front();
120 os << " <= B" << current_block->id();
121 info = current_block->debug_info();
122 if (info.name) os << info;
123 }
124 #endif
125 return os;
126 }
127
operator <<(std::ostream & os,const BasicBlock::Control & c)128 std::ostream& operator<<(std::ostream& os, const BasicBlock::Control& c) {
129 switch (c) {
130 case BasicBlock::kNone:
131 return os << "none";
132 case BasicBlock::kGoto:
133 return os << "goto";
134 case BasicBlock::kCall:
135 return os << "call";
136 case BasicBlock::kBranch:
137 return os << "branch";
138 case BasicBlock::kSwitch:
139 return os << "switch";
140 case BasicBlock::kDeoptimize:
141 return os << "deoptimize";
142 case BasicBlock::kTailCall:
143 return os << "tailcall";
144 case BasicBlock::kReturn:
145 return os << "return";
146 case BasicBlock::kThrow:
147 return os << "throw";
148 }
149 UNREACHABLE();
150 }
151
operator <<(std::ostream & os,const BasicBlock::Id & id)152 std::ostream& operator<<(std::ostream& os, const BasicBlock::Id& id) {
153 return os << id.ToSize();
154 }
155
Schedule(Zone * zone,size_t node_count_hint)156 Schedule::Schedule(Zone* zone, size_t node_count_hint)
157 : zone_(zone),
158 all_blocks_(zone),
159 nodeid_to_block_(zone),
160 rpo_order_(zone),
161 start_(NewBasicBlock()),
162 end_(NewBasicBlock()) {
163 nodeid_to_block_.reserve(node_count_hint);
164 }
165
block(Node * node) const166 BasicBlock* Schedule::block(Node* node) const {
167 if (node->id() < static_cast<NodeId>(nodeid_to_block_.size())) {
168 return nodeid_to_block_[node->id()];
169 }
170 return nullptr;
171 }
172
IsScheduled(Node * node)173 bool Schedule::IsScheduled(Node* node) {
174 if (node->id() >= nodeid_to_block_.size()) return false;
175 return nodeid_to_block_[node->id()] != nullptr;
176 }
177
GetBlockById(BasicBlock::Id block_id)178 BasicBlock* Schedule::GetBlockById(BasicBlock::Id block_id) {
179 DCHECK(block_id.ToSize() < all_blocks_.size());
180 return all_blocks_[block_id.ToSize()];
181 }
182
ClearBlockById(BasicBlock::Id block_id)183 void Schedule::ClearBlockById(BasicBlock::Id block_id) {
184 DCHECK(block_id.ToSize() < all_blocks_.size());
185 all_blocks_[block_id.ToSize()] = nullptr;
186 }
187
SameBasicBlock(Node * a,Node * b) const188 bool Schedule::SameBasicBlock(Node* a, Node* b) const {
189 BasicBlock* block = this->block(a);
190 return block != nullptr && block == this->block(b);
191 }
192
NewBasicBlock()193 BasicBlock* Schedule::NewBasicBlock() {
194 BasicBlock* block = zone_->New<BasicBlock>(
195 zone_, BasicBlock::Id::FromSize(all_blocks_.size()));
196 all_blocks_.push_back(block);
197 return block;
198 }
199
PlanNode(BasicBlock * block,Node * node)200 void Schedule::PlanNode(BasicBlock* block, Node* node) {
201 if (FLAG_trace_turbo_scheduler) {
202 StdoutStream{} << "Planning #" << node->id() << ":"
203 << node->op()->mnemonic() << " for future add to B"
204 << block->id() << "\n";
205 }
206 DCHECK_NULL(this->block(node));
207 SetBlockForNode(block, node);
208 }
209
AddNode(BasicBlock * block,Node * node)210 void Schedule::AddNode(BasicBlock* block, Node* node) {
211 if (FLAG_trace_turbo_scheduler) {
212 StdoutStream{} << "Adding #" << node->id() << ":" << node->op()->mnemonic()
213 << " to B" << block->id() << "\n";
214 }
215 DCHECK(this->block(node) == nullptr || this->block(node) == block);
216 block->AddNode(node);
217 SetBlockForNode(block, node);
218 }
219
AddGoto(BasicBlock * block,BasicBlock * succ)220 void Schedule::AddGoto(BasicBlock* block, BasicBlock* succ) {
221 CHECK_EQ(BasicBlock::kNone, block->control());
222 block->set_control(BasicBlock::kGoto);
223 AddSuccessor(block, succ);
224 }
225
226 #if DEBUG
227 namespace {
228
IsPotentiallyThrowingCall(IrOpcode::Value opcode)229 bool IsPotentiallyThrowingCall(IrOpcode::Value opcode) {
230 switch (opcode) {
231 #define BUILD_BLOCK_JS_CASE(Name, ...) case IrOpcode::k##Name:
232 JS_OP_LIST(BUILD_BLOCK_JS_CASE)
233 #undef BUILD_BLOCK_JS_CASE
234 case IrOpcode::kCall:
235 return true;
236 default:
237 return false;
238 }
239 }
240
241 } // namespace
242 #endif // DEBUG
243
AddCall(BasicBlock * block,Node * call,BasicBlock * success_block,BasicBlock * exception_block)244 void Schedule::AddCall(BasicBlock* block, Node* call, BasicBlock* success_block,
245 BasicBlock* exception_block) {
246 CHECK_EQ(BasicBlock::kNone, block->control());
247 DCHECK(IsPotentiallyThrowingCall(call->opcode()));
248 block->set_control(BasicBlock::kCall);
249 AddSuccessor(block, success_block);
250 AddSuccessor(block, exception_block);
251 SetControlInput(block, call);
252 }
253
AddBranch(BasicBlock * block,Node * branch,BasicBlock * tblock,BasicBlock * fblock)254 void Schedule::AddBranch(BasicBlock* block, Node* branch, BasicBlock* tblock,
255 BasicBlock* fblock) {
256 CHECK_EQ(BasicBlock::kNone, block->control());
257 DCHECK_EQ(IrOpcode::kBranch, branch->opcode());
258 block->set_control(BasicBlock::kBranch);
259 AddSuccessor(block, tblock);
260 AddSuccessor(block, fblock);
261 SetControlInput(block, branch);
262 }
263
AddSwitch(BasicBlock * block,Node * sw,BasicBlock ** succ_blocks,size_t succ_count)264 void Schedule::AddSwitch(BasicBlock* block, Node* sw, BasicBlock** succ_blocks,
265 size_t succ_count) {
266 CHECK_EQ(BasicBlock::kNone, block->control());
267 DCHECK_EQ(IrOpcode::kSwitch, sw->opcode());
268 block->set_control(BasicBlock::kSwitch);
269 for (size_t index = 0; index < succ_count; ++index) {
270 AddSuccessor(block, succ_blocks[index]);
271 }
272 SetControlInput(block, sw);
273 }
274
AddTailCall(BasicBlock * block,Node * input)275 void Schedule::AddTailCall(BasicBlock* block, Node* input) {
276 CHECK_EQ(BasicBlock::kNone, block->control());
277 block->set_control(BasicBlock::kTailCall);
278 SetControlInput(block, input);
279 if (block != end()) AddSuccessor(block, end());
280 }
281
AddReturn(BasicBlock * block,Node * input)282 void Schedule::AddReturn(BasicBlock* block, Node* input) {
283 CHECK_EQ(BasicBlock::kNone, block->control());
284 block->set_control(BasicBlock::kReturn);
285 SetControlInput(block, input);
286 if (block != end()) AddSuccessor(block, end());
287 }
288
AddDeoptimize(BasicBlock * block,Node * input)289 void Schedule::AddDeoptimize(BasicBlock* block, Node* input) {
290 CHECK_EQ(BasicBlock::kNone, block->control());
291 block->set_control(BasicBlock::kDeoptimize);
292 SetControlInput(block, input);
293 if (block != end()) AddSuccessor(block, end());
294 }
295
AddThrow(BasicBlock * block,Node * input)296 void Schedule::AddThrow(BasicBlock* block, Node* input) {
297 CHECK_EQ(BasicBlock::kNone, block->control());
298 block->set_control(BasicBlock::kThrow);
299 SetControlInput(block, input);
300 if (block != end()) AddSuccessor(block, end());
301 }
302
InsertBranch(BasicBlock * block,BasicBlock * end,Node * branch,BasicBlock * tblock,BasicBlock * fblock)303 void Schedule::InsertBranch(BasicBlock* block, BasicBlock* end, Node* branch,
304 BasicBlock* tblock, BasicBlock* fblock) {
305 CHECK_NE(BasicBlock::kNone, block->control());
306 CHECK_EQ(BasicBlock::kNone, end->control());
307 end->set_control(block->control());
308 block->set_control(BasicBlock::kBranch);
309 MoveSuccessors(block, end);
310 AddSuccessor(block, tblock);
311 AddSuccessor(block, fblock);
312 if (block->control_input() != nullptr) {
313 SetControlInput(end, block->control_input());
314 }
315 SetControlInput(block, branch);
316 }
317
InsertSwitch(BasicBlock * block,BasicBlock * end,Node * sw,BasicBlock ** succ_blocks,size_t succ_count)318 void Schedule::InsertSwitch(BasicBlock* block, BasicBlock* end, Node* sw,
319 BasicBlock** succ_blocks, size_t succ_count) {
320 CHECK_NE(BasicBlock::kNone, block->control());
321 CHECK_EQ(BasicBlock::kNone, end->control());
322 end->set_control(block->control());
323 block->set_control(BasicBlock::kSwitch);
324 MoveSuccessors(block, end);
325 for (size_t index = 0; index < succ_count; ++index) {
326 AddSuccessor(block, succ_blocks[index]);
327 }
328 if (block->control_input() != nullptr) {
329 SetControlInput(end, block->control_input());
330 }
331 SetControlInput(block, sw);
332 }
333
EnsureCFGWellFormedness()334 void Schedule::EnsureCFGWellFormedness() {
335 // Make a copy of all the blocks for the iteration, since adding the split
336 // edges will allocate new blocks.
337 BasicBlockVector all_blocks_copy(all_blocks_);
338
339 // Insert missing split edge blocks.
340 for (BasicBlock* block : all_blocks_copy) {
341 if (block->PredecessorCount() > 1) {
342 if (block != end_) {
343 EnsureSplitEdgeForm(block);
344 }
345 }
346 }
347
348 EliminateRedundantPhiNodes();
349 }
350
EliminateRedundantPhiNodes()351 void Schedule::EliminateRedundantPhiNodes() {
352 // Ensure that useless phi nodes that only have a single input, identical
353 // inputs, or are a self-referential loop phi,
354 // -- which can happen with the automatically generated code in the CSA and
355 // torque -- are pruned.
356 // Since we have strucured control flow, this is enough to minimize the number
357 // of phi nodes.
358 bool reached_fixed_point = false;
359 while (!reached_fixed_point) {
360 reached_fixed_point = true;
361 for (BasicBlock* block : all_blocks_) {
362 int predecessor_count = static_cast<int>(block->PredecessorCount());
363 for (size_t node_pos = 0; node_pos < block->NodeCount(); ++node_pos) {
364 Node* node = block->NodeAt(node_pos);
365 if (node->opcode() == IrOpcode::kPhi) {
366 Node* first_input = node->InputAt(0);
367 bool inputs_equal = true;
368 for (int i = 1; i < predecessor_count; ++i) {
369 Node* input = node->InputAt(i);
370 if (input != first_input && input != node) {
371 inputs_equal = false;
372 break;
373 }
374 }
375 if (!inputs_equal) continue;
376 node->ReplaceUses(first_input);
377 node->Kill();
378 block->RemoveNode(block->begin() + node_pos);
379 --node_pos;
380 reached_fixed_point = false;
381 }
382 }
383 }
384 }
385 }
386
EnsureSplitEdgeForm(BasicBlock * block)387 void Schedule::EnsureSplitEdgeForm(BasicBlock* block) {
388 #ifdef DEBUG
389 DCHECK(block->PredecessorCount() > 1 && block != end_);
390 for (auto current_pred = block->predecessors().begin();
391 current_pred != block->predecessors().end(); ++current_pred) {
392 BasicBlock* pred = *current_pred;
393 DCHECK_LE(pred->SuccessorCount(), 1);
394 }
395 #endif
396 }
397
MovePhis(BasicBlock * from,BasicBlock * to)398 void Schedule::MovePhis(BasicBlock* from, BasicBlock* to) {
399 for (size_t i = 0; i < from->NodeCount();) {
400 Node* node = from->NodeAt(i);
401 if (node->opcode() == IrOpcode::kPhi) {
402 to->AddNode(node);
403 from->RemoveNode(from->begin() + i);
404 DCHECK_EQ(nodeid_to_block_[node->id()], from);
405 nodeid_to_block_[node->id()] = to;
406 } else {
407 ++i;
408 }
409 }
410 }
411
PropagateDeferredMark()412 void Schedule::PropagateDeferredMark() {
413 // Push forward the deferred block marks through newly inserted blocks and
414 // other improperly marked blocks until a fixed point is reached.
415 // TODO(danno): optimize the propagation
416 bool done = false;
417 while (!done) {
418 done = true;
419 for (auto block : all_blocks_) {
420 if (!block->deferred()) {
421 bool deferred = block->PredecessorCount() > 0;
422 for (auto pred : block->predecessors()) {
423 if (!pred->deferred() && (pred->rpo_number() < block->rpo_number())) {
424 deferred = false;
425 }
426 }
427 if (deferred) {
428 block->set_deferred(true);
429 done = false;
430 }
431 }
432 }
433 }
434 }
435
AddSuccessor(BasicBlock * block,BasicBlock * succ)436 void Schedule::AddSuccessor(BasicBlock* block, BasicBlock* succ) {
437 block->AddSuccessor(succ);
438 succ->AddPredecessor(block);
439 }
440
MoveSuccessors(BasicBlock * from,BasicBlock * to)441 void Schedule::MoveSuccessors(BasicBlock* from, BasicBlock* to) {
442 for (BasicBlock* const successor : from->successors()) {
443 to->AddSuccessor(successor);
444 for (BasicBlock*& predecessor : successor->predecessors()) {
445 if (predecessor == from) predecessor = to;
446 }
447 }
448 from->ClearSuccessors();
449 }
450
SetControlInput(BasicBlock * block,Node * node)451 void Schedule::SetControlInput(BasicBlock* block, Node* node) {
452 block->set_control_input(node);
453 SetBlockForNode(block, node);
454 }
455
SetBlockForNode(BasicBlock * block,Node * node)456 void Schedule::SetBlockForNode(BasicBlock* block, Node* node) {
457 if (node->id() >= nodeid_to_block_.size()) {
458 nodeid_to_block_.resize(node->id() + 1);
459 }
460 nodeid_to_block_[node->id()] = block;
461 }
462
operator <<(std::ostream & os,const Schedule & s)463 std::ostream& operator<<(std::ostream& os, const Schedule& s) {
464 for (BasicBlock* block :
465 ((s.RpoBlockCount() == 0) ? *s.all_blocks() : *s.rpo_order())) {
466 if (block == nullptr) continue;
467 if (block->rpo_number() == -1) {
468 os << "--- BLOCK id:" << block->id().ToInt();
469 } else {
470 os << "--- BLOCK B" << block->rpo_number();
471 }
472 if (block->deferred()) os << " (deferred)";
473 if (block->PredecessorCount() != 0) os << " <- ";
474 bool comma = false;
475 for (BasicBlock const* predecessor : block->predecessors()) {
476 if (comma) os << ", ";
477 comma = true;
478 if (predecessor->rpo_number() == -1) {
479 os << "id:" << predecessor->id().ToInt();
480 } else {
481 os << "B" << predecessor->rpo_number();
482 }
483 }
484 os << " ---\n";
485 for (Node* node : *block) {
486 os << " " << *node;
487 if (NodeProperties::IsTyped(node)) {
488 os << " : " << NodeProperties::GetType(node);
489 }
490 os << "\n";
491 }
492 BasicBlock::Control control = block->control();
493 if (control != BasicBlock::kNone) {
494 os << " ";
495 if (block->control_input() != nullptr) {
496 os << *block->control_input();
497 } else {
498 os << "Goto";
499 }
500 os << " -> ";
501 comma = false;
502 for (BasicBlock const* successor : block->successors()) {
503 if (comma) os << ", ";
504 comma = true;
505 if (successor->rpo_number() == -1) {
506 os << "id:" << successor->id().ToInt();
507 } else {
508 os << "B" << successor->rpo_number();
509 }
510 }
511 os << "\n";
512 }
513 }
514 return os;
515 }
516
517 } // namespace compiler
518 } // namespace internal
519 } // namespace v8
520