• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/compiler/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 << "\n"; }
108 
operator <<(std::ostream & os,const BasicBlock & block)109 std::ostream& operator<<(std::ostream& os, const BasicBlock& block) {
110   os << "id:" << 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 = &block;
118   while (current_block->PredecessorCount() > 0 && i++ < kMaxDisplayedBlocks) {
119     current_block = current_block->predecessors().front();
120     os << " <= id:" << 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()
204                    << " for future add to id:" << 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 id:" << 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();
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();
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();
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