v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
schedule.cc
Go to the documentation of this file.
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
6
8#include "src/compiler/node.h"
10
11namespace v8 {
12namespace internal {
13namespace compiler {
14
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),
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#ifdef LOG_BUILTIN_BLOCK_COUNT
34 pgo_execution_count_(0),
35#endif
36 id_(id) {
37}
38
40 // RPO numbers must be initialized.
42 DCHECK_LE(0, block->rpo_number_);
43 if (loop_end_ == nullptr) return false; // This is not a loop.
44 return block->rpo_number_ >= rpo_number_ &&
45 block->rpo_number_ < loop_end_->rpo_number_;
46}
47
49 successors_.push_back(successor);
50}
51
53 predecessors_.push_back(predecessor);
54}
55
59
61
63
64void BasicBlock::set_control_input(Node* control_input) {
65 if (!nodes_.empty() && control_input == nodes_.back()) {
67 }
69}
70
71void BasicBlock::set_loop_depth(int32_t loop_depth) {
73}
74
75void BasicBlock::set_rpo_number(int32_t rpo_number) {
77}
78
80
84
85void BasicBlock::TrimNodes(iterator new_end) { nodes_.erase(new_end, end()); }
86
88 loop_number_ = -1;
89 rpo_number_ = -1;
91 dominator_ = nullptr;
92 rpo_next_ = nullptr;
93 loop_header_ = nullptr;
94 loop_end_ = nullptr;
95 loop_depth_ = 0;
96}
97
98// static
100 while (b1 != b2) {
101 if (b1->dominator_depth() < b2->dominator_depth()) {
102 b2 = b2->dominator();
103 } else {
104 b1 = b1->dominator();
105 }
106 }
107 return b1;
108}
109
110void BasicBlock::Print() { StdoutStream{} << *this << "\n"; }
111
112std::ostream& operator<<(std::ostream& os, const BasicBlock& block) {
113 os << "id:" << block.id();
114#if DEBUG
115 AssemblerDebugInfo info = block.debug_info();
116 if (info.name) os << info;
117 // Print predecessor blocks for better debugging.
118 const int kMaxDisplayedBlocks = 4;
119 int i = 0;
120 const BasicBlock* current_block = &block;
121 while (current_block->PredecessorCount() > 0 && i++ < kMaxDisplayedBlocks) {
122 current_block = current_block->predecessors().front();
123 os << " <= id:" << current_block->id();
124 info = current_block->debug_info();
125 if (info.name) os << info;
126 }
127#endif
128 return os;
129}
130
131std::ostream& operator<<(std::ostream& os, const BasicBlock::Control& c) {
132 switch (c) {
134 return os << "none";
136 return os << "goto";
138 return os << "call";
140 return os << "branch";
142 return os << "switch";
144 return os << "deoptimize";
146 return os << "tailcall";
148 return os << "return";
150 return os << "throw";
151 }
152 UNREACHABLE();
153}
154
155std::ostream& operator<<(std::ostream& os, const BasicBlock::Id& id) {
156 return os << id.ToSize();
157}
158
159Schedule::Schedule(Zone* zone, size_t node_count_hint)
160 : zone_(zone),
161 all_blocks_(zone),
162 nodeid_to_block_(zone),
163 rpo_order_(zone),
164 start_(NewBasicBlock()),
165 end_(NewBasicBlock()) {
166 nodeid_to_block_.reserve(node_count_hint);
167}
168
170 if (node->id() < static_cast<NodeId>(nodeid_to_block_.size())) {
171 return nodeid_to_block_[node->id()];
172 }
173 return nullptr;
174}
175
177 if (node->id() >= nodeid_to_block_.size()) return false;
178 return nodeid_to_block_[node->id()] != nullptr;
179}
180
182 DCHECK(block_id.ToSize() < all_blocks_.size());
183 return all_blocks_[block_id.ToSize()];
184}
185
187 DCHECK(block_id.ToSize() < all_blocks_.size());
188 all_blocks_[block_id.ToSize()] = nullptr;
189}
190
192 BasicBlock* block = this->block(a);
193 return block != nullptr && block == this->block(b);
194}
195
202
203void Schedule::PlanNode(BasicBlock* block, Node* node) {
204 if (v8_flags.trace_turbo_scheduler) {
205 StdoutStream{} << "Planning #" << node->id() << ":"
206 << node->op()->mnemonic()
207 << " for future add to id:" << block->id() << "\n";
208 }
209 DCHECK_NULL(this->block(node));
210 SetBlockForNode(block, node);
211}
212
213void Schedule::AddNode(BasicBlock* block, Node* node) {
214 if (v8_flags.trace_turbo_scheduler) {
215 StdoutStream{} << "Adding #" << node->id() << ":" << node->op()->mnemonic()
216 << " to id:" << block->id() << "\n";
217 }
218 DCHECK(this->block(node) == nullptr || this->block(node) == block);
219 block->AddNode(node);
220 SetBlockForNode(block, node);
221}
222
224 CHECK_EQ(BasicBlock::kNone, block->control());
225 block->set_control(BasicBlock::kGoto);
226 AddSuccessor(block, succ);
227}
228
229#if DEBUG
230namespace {
231
232bool IsPotentiallyThrowingCall(IrOpcode::Value opcode) {
233 switch (opcode) {
234#define BUILD_BLOCK_JS_CASE(Name, ...) case IrOpcode::k##Name:
236#undef BUILD_BLOCK_JS_CASE
237 case IrOpcode::kCall:
238 case IrOpcode::kFastApiCall:
239 return true;
240 default:
241 return false;
242 }
243}
244
245} // namespace
246#endif // DEBUG
247
248void Schedule::AddCall(BasicBlock* block, Node* call, BasicBlock* success_block,
249 BasicBlock* exception_block) {
250 CHECK_EQ(BasicBlock::kNone, block->control());
251 DCHECK(IsPotentiallyThrowingCall(call->opcode()));
252 block->set_control(BasicBlock::kCall);
253 AddSuccessor(block, success_block);
254 AddSuccessor(block, exception_block);
255 SetControlInput(block, call);
256}
257
258void Schedule::AddBranch(BasicBlock* block, Node* branch, BasicBlock* tblock,
259 BasicBlock* fblock) {
260 CHECK_EQ(BasicBlock::kNone, block->control());
261 DCHECK_EQ(IrOpcode::kBranch, branch->opcode());
262 block->set_control(BasicBlock::kBranch);
263 AddSuccessor(block, tblock);
264 AddSuccessor(block, fblock);
265 SetControlInput(block, branch);
266}
267
268void Schedule::AddSwitch(BasicBlock* block, Node* sw, BasicBlock** succ_blocks,
269 size_t succ_count) {
270 CHECK_EQ(BasicBlock::kNone, block->control());
271 DCHECK_EQ(IrOpcode::kSwitch, sw->opcode());
272 block->set_control(BasicBlock::kSwitch);
273 for (size_t index = 0; index < succ_count; ++index) {
274 AddSuccessor(block, succ_blocks[index]);
275 }
276 SetControlInput(block, sw);
277}
278
280 CHECK_EQ(BasicBlock::kNone, block->control());
281 block->set_control(BasicBlock::kTailCall);
282 SetControlInput(block, input);
283 if (block != end()) AddSuccessor(block, end());
284}
285
286void Schedule::AddReturn(BasicBlock* block, Node* input) {
287 CHECK_EQ(BasicBlock::kNone, block->control());
288 block->set_control(BasicBlock::kReturn);
289 SetControlInput(block, input);
290 if (block != end()) AddSuccessor(block, end());
291}
292
294 CHECK_EQ(BasicBlock::kNone, block->control());
295 block->set_control(BasicBlock::kDeoptimize);
296 SetControlInput(block, input);
297 if (block != end()) AddSuccessor(block, end());
298}
299
300void Schedule::AddThrow(BasicBlock* block, Node* input) {
301 CHECK_EQ(BasicBlock::kNone, block->control());
302 block->set_control(BasicBlock::kThrow);
303 SetControlInput(block, input);
304 if (block != end()) AddSuccessor(block, end());
305}
306
308 BasicBlock* tblock, BasicBlock* fblock) {
309 CHECK_NE(BasicBlock::kNone, block->control());
311 end->set_control(block->control());
312 block->set_control(BasicBlock::kBranch);
313 MoveSuccessors(block, end);
314 AddSuccessor(block, tblock);
315 AddSuccessor(block, fblock);
316 if (block->control_input() != nullptr) {
318 }
319 SetControlInput(block, branch);
320}
321
323 BasicBlock** succ_blocks, size_t succ_count) {
324 CHECK_NE(BasicBlock::kNone, block->control());
326 end->set_control(block->control());
327 block->set_control(BasicBlock::kSwitch);
328 MoveSuccessors(block, end);
329 for (size_t index = 0; index < succ_count; ++index) {
330 AddSuccessor(block, succ_blocks[index]);
331 }
332 if (block->control_input() != nullptr) {
334 }
335 SetControlInput(block, sw);
336}
337
339 // Ensure there are no critical edges.
340 for (BasicBlock* block : all_blocks_) {
341 if (block->PredecessorCount() > 1) {
342 if (block != end_) {
343 EnsureSplitEdgeForm(block);
344 }
345 }
346 }
347
349}
350
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
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
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
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
437 block->AddSuccessor(succ);
438 succ->AddPredecessor(block);
439}
440
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
452 block->set_control_input(node);
453 SetBlockForNode(block, node);
454}
455
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
463std::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 os << "--- BLOCK B" << block->rpo_number() << " id" << block->id();
468#ifdef LOG_BUILTIN_BLOCK_COUNT
469 os << " PGO Execution Count:" << block->pgo_execution_count();
470#endif
471 if (block->deferred()) os << " (deferred)";
472 if (block->PredecessorCount() != 0) os << " <- ";
473 bool comma = false;
474 for (BasicBlock const* predecessor : block->predecessors()) {
475 if (comma) os << ", ";
476 comma = true;
477 os << "B" << predecessor->rpo_number();
478 }
479 os << " ---\n";
480 for (Node* node : *block) {
481 os << " " << *node;
482 if (NodeProperties::IsTyped(node)) {
483 os << " : " << NodeProperties::GetType(node);
484 }
485 os << "\n";
486 }
487 BasicBlock::Control control = block->control();
488 if (control != BasicBlock::kNone) {
489 os << " ";
490 if (block->control_input() != nullptr) {
491 os << *block->control_input();
492 } else {
493 os << "Goto";
494 }
495 os << " -> ";
496 comma = false;
497 for (BasicBlock const* successor : block->successors()) {
498 if (comma) os << ", ";
499 comma = true;
500 os << "B" << successor->rpo_number();
501 }
502 os << "\n";
503 }
504 }
505 return os;
506}
507
508} // namespace compiler
509} // namespace internal
510} // namespace v8
void reserve(size_t new_cap)
void resize(size_t new_size)
void push_back(const T &value)
T * New(Args &&... args)
Definition zone.h:114
static Id FromSize(size_t index)
Definition schedule.h:50
void set_loop_header(BasicBlock *loop_header)
Definition schedule.cc:81
BasicBlock * dominator() const
Definition schedule.h:138
bool LoopContains(BasicBlock *block) const
Definition schedule.cc:39
NodeVector::iterator iterator
Definition schedule.h:99
void set_control(Control control)
Definition schedule.cc:62
BasicBlock * loop_header() const
Definition schedule.h:144
BasicBlock(Zone *zone, Id id)
Definition schedule.cc:15
void AddPredecessor(BasicBlock *predecessor)
Definition schedule.cc:52
void AddSuccessor(BasicBlock *successor)
Definition schedule.cc:48
void set_control_input(Node *control_input)
Definition schedule.cc:64
BasicBlockVector & successors()
Definition schedule.h:82
void RemovePredecessor(size_t index)
Definition schedule.cc:56
void TrimNodes(iterator new_end)
Definition schedule.cc:85
BasicBlockVector & predecessors()
Definition schedule.h:73
BasicBlock * loop_end() const
Definition schedule.h:147
void set_rpo_number(int32_t rpo_number)
Definition schedule.cc:75
static BasicBlock * GetCommonDominator(BasicBlock *b1, BasicBlock *b2)
Definition schedule.cc:99
void set_loop_end(BasicBlock *loop_end)
Definition schedule.cc:79
void set_loop_depth(int32_t loop_depth)
Definition schedule.cc:71
static Type GetType(const Node *node)
static bool IsTyped(const Node *node)
constexpr IrOpcode::Value opcode() const
Definition node.h:52
Node * InputAt(int index) const
Definition node.h:70
void ReplaceUses(Node *replace_to)
Definition node.cc:306
void SetBlockForNode(BasicBlock *block, Node *node)
Definition schedule.cc:456
void AddDeoptimize(BasicBlock *block, Node *input)
Definition schedule.cc:293
void AddThrow(BasicBlock *block, Node *input)
Definition schedule.cc:300
void MoveSuccessors(BasicBlock *from, BasicBlock *to)
Definition schedule.cc:441
void PlanNode(BasicBlock *block, Node *node)
Definition schedule.cc:203
void AddGoto(BasicBlock *block, BasicBlock *succ)
Definition schedule.cc:223
void InsertSwitch(BasicBlock *block, BasicBlock *end, Node *sw, BasicBlock **succ_blocks, size_t succ_count)
Definition schedule.cc:322
void AddTailCall(BasicBlock *block, Node *input)
Definition schedule.cc:279
void AddSuccessor(BasicBlock *block, BasicBlock *succ)
Definition schedule.cc:436
void AddReturn(BasicBlock *block, Node *input)
Definition schedule.cc:286
void AddCall(BasicBlock *block, Node *call, BasicBlock *success_block, BasicBlock *exception_block)
Definition schedule.cc:248
BasicBlock * GetBlockById(BasicBlock::Id block_id)
Definition schedule.cc:181
void ClearBlockById(BasicBlock::Id block_id)
Definition schedule.cc:186
void EnsureSplitEdgeForm(BasicBlock *block)
Definition schedule.cc:387
bool SameBasicBlock(Node *a, Node *b) const
Definition schedule.cc:191
void InsertBranch(BasicBlock *block, BasicBlock *end, Node *branch, BasicBlock *tblock, BasicBlock *fblock)
Definition schedule.cc:307
void AddBranch(BasicBlock *block, Node *branch, BasicBlock *tblock, BasicBlock *fblock)
Definition schedule.cc:258
BasicBlockVector nodeid_to_block_
Definition schedule.h:316
void SetControlInput(BasicBlock *block, Node *node)
Definition schedule.cc:451
void AddNode(BasicBlock *block, Node *node)
Definition schedule.cc:213
BasicBlock * block(Node *node) const
Definition schedule.cc:169
void AddSwitch(BasicBlock *block, Node *sw, BasicBlock **succ_blocks, size_t succ_count)
Definition schedule.cc:268
Schedule(Zone *zone, size_t node_count_hint=0)
Definition schedule.cc:159
void MovePhis(BasicBlock *from, BasicBlock *to)
Definition schedule.cc:398
Zone * zone_
uint8_t *const start_
Definition assembler.cc:131
const v8::base::TimeTicks end_
Definition sweeper.cc:54
Handle< SharedFunctionInfo > info
int end
ZoneLinkedList< BFEntry > nodes_
Control control_
Node * node
RpoNumber block
Point to
std::ostream & operator<<(std::ostream &os, AccessMode access_mode)
V8_EXPORT_PRIVATE FlagValues v8_flags
#define JS_OP_LIST(V)
Definition opcodes.h:257
#define BUILD_BLOCK_JS_CASE(Name,...)
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_NULL(val)
Definition logging.h:491
#define CHECK_NE(lhs, rhs)
#define CHECK_EQ(lhs, rhs)
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485