OLD | NEW |
(Empty) | |
| 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 #ifndef V8_COMPILER_SCHEDULE_H_ |
| 6 #define V8_COMPILER_SCHEDULE_H_ |
| 7 |
| 8 #include <vector> |
| 9 |
| 10 #include "src/v8.h" |
| 11 |
| 12 #include "src/compiler/generic-algorithm.h" |
| 13 #include "src/compiler/generic-graph.h" |
| 14 #include "src/compiler/generic-node.h" |
| 15 #include "src/compiler/generic-node-inl.h" |
| 16 #include "src/compiler/node.h" |
| 17 #include "src/compiler/opcodes.h" |
| 18 #include "src/zone.h" |
| 19 |
| 20 namespace v8 { |
| 21 namespace internal { |
| 22 namespace compiler { |
| 23 |
| 24 class BasicBlock; |
| 25 class Graph; |
| 26 class ConstructScheduleData; |
| 27 class CodeGenerator; // Because of a namespace bug in clang. |
| 28 |
| 29 class BasicBlockData { |
| 30 public: |
| 31 // Possible control nodes that can end a block. |
| 32 enum Control { |
| 33 kNone, // Control not initialized yet. |
| 34 kGoto, // Goto a single successor block. |
| 35 kBranch, // Branch if true to first successor, otherwise second. |
| 36 kReturn, // Return a value from this method. |
| 37 kThrow, // Throw an exception. |
| 38 kCall, // Call to a possibly deoptimizing or throwing function. |
| 39 kDeoptimize // Deoptimize. |
| 40 }; |
| 41 |
| 42 int32_t rpo_number_; // special RPO number of the block. |
| 43 BasicBlock* loop_header_; // Pointer to dominating loop header basic block, |
| 44 // NULL if none. For loop headers, this points to |
| 45 // enclosing loop header. |
| 46 int32_t loop_depth_; // loop nesting, 0 is top-level |
| 47 int32_t loop_end_; // end of the loop, if this block is a loop header. |
| 48 int32_t code_start_; // start index of arch-specific code. |
| 49 int32_t code_end_; // end index of arch-specific code. |
| 50 bool deferred_; // {true} if this block is considered the slow |
| 51 // path. |
| 52 Control control_; // Control at the end of the block. |
| 53 Node* control_input_; // Input value for control. |
| 54 NodeVector nodes_; // nodes of this block in forward order. |
| 55 |
| 56 explicit BasicBlockData(Zone* zone) |
| 57 : rpo_number_(-1), |
| 58 loop_header_(NULL), |
| 59 loop_depth_(0), |
| 60 loop_end_(-1), |
| 61 code_start_(-1), |
| 62 code_end_(-1), |
| 63 deferred_(false), |
| 64 control_(kNone), |
| 65 control_input_(NULL), |
| 66 nodes_(NodeVector::allocator_type(zone)) {} |
| 67 |
| 68 inline bool IsLoopHeader() const { return loop_end_ >= 0; } |
| 69 inline bool LoopContains(BasicBlockData* block) const { |
| 70 // RPO numbers must be initialized. |
| 71 ASSERT(rpo_number_ >= 0); |
| 72 ASSERT(block->rpo_number_ >= 0); |
| 73 if (loop_end_ < 0) return false; // This is not a loop. |
| 74 return block->rpo_number_ >= rpo_number_ && block->rpo_number_ < loop_end_; |
| 75 } |
| 76 int first_instruction_index() { |
| 77 ASSERT(code_start_ >= 0); |
| 78 ASSERT(code_end_ > 0); |
| 79 ASSERT(code_end_ >= code_start_); |
| 80 return code_start_; |
| 81 } |
| 82 int last_instruction_index() { |
| 83 ASSERT(code_start_ >= 0); |
| 84 ASSERT(code_end_ > 0); |
| 85 ASSERT(code_end_ >= code_start_); |
| 86 return code_end_ - 1; |
| 87 } |
| 88 }; |
| 89 |
| 90 OStream& operator<<(OStream& os, const BasicBlockData::Control& c); |
| 91 |
| 92 // A basic block contains an ordered list of nodes and ends with a control |
| 93 // node. Note that if a basic block has phis, then all phis must appear as the |
| 94 // first nodes in the block. |
| 95 class BasicBlock V8_FINAL : public GenericNode<BasicBlockData, BasicBlock> { |
| 96 public: |
| 97 BasicBlock(GenericGraphBase* graph, int input_count) |
| 98 : GenericNode<BasicBlockData, BasicBlock>(graph, input_count) {} |
| 99 |
| 100 typedef Uses Successors; |
| 101 typedef Inputs Predecessors; |
| 102 |
| 103 Successors successors() { return static_cast<Successors>(uses()); } |
| 104 Predecessors predecessors() { return static_cast<Predecessors>(inputs()); } |
| 105 |
| 106 int PredecessorCount() { return InputCount(); } |
| 107 BasicBlock* PredecessorAt(int index) { return InputAt(index); } |
| 108 |
| 109 int SuccessorCount() { return UseCount(); } |
| 110 BasicBlock* SuccessorAt(int index) { return UseAt(index); } |
| 111 |
| 112 int PredecessorIndexOf(BasicBlock* predecessor) { |
| 113 BasicBlock::Predecessors predecessors = this->predecessors(); |
| 114 for (BasicBlock::Predecessors::iterator i = predecessors.begin(); |
| 115 i != predecessors.end(); ++i) { |
| 116 if (*i == predecessor) return i.index(); |
| 117 } |
| 118 return -1; |
| 119 } |
| 120 |
| 121 inline BasicBlock* loop_header() { |
| 122 return static_cast<BasicBlock*>(loop_header_); |
| 123 } |
| 124 inline BasicBlock* ContainingLoop() { |
| 125 if (IsLoopHeader()) return this; |
| 126 return static_cast<BasicBlock*>(loop_header_); |
| 127 } |
| 128 |
| 129 typedef NodeVector::iterator iterator; |
| 130 iterator begin() { return nodes_.begin(); } |
| 131 iterator end() { return nodes_.end(); } |
| 132 |
| 133 typedef NodeVector::const_iterator const_iterator; |
| 134 const_iterator begin() const { return nodes_.begin(); } |
| 135 const_iterator end() const { return nodes_.end(); } |
| 136 |
| 137 typedef NodeVector::reverse_iterator reverse_iterator; |
| 138 reverse_iterator rbegin() { return nodes_.rbegin(); } |
| 139 reverse_iterator rend() { return nodes_.rend(); } |
| 140 |
| 141 private: |
| 142 DISALLOW_COPY_AND_ASSIGN(BasicBlock); |
| 143 }; |
| 144 |
| 145 typedef GenericGraphVisit::NullNodeVisitor<BasicBlockData, BasicBlock> |
| 146 NullBasicBlockVisitor; |
| 147 |
| 148 typedef zone_allocator<BasicBlock*> BasicBlockPtrZoneAllocator; |
| 149 typedef std::vector<BasicBlock*, BasicBlockPtrZoneAllocator> BasicBlockVector; |
| 150 typedef BasicBlockVector::iterator BasicBlockVectorIter; |
| 151 typedef BasicBlockVector::reverse_iterator BasicBlockVectorRIter; |
| 152 |
| 153 // A schedule represents the result of assigning nodes to basic blocks |
| 154 // and ordering them within basic blocks. Prior to computing a schedule, |
| 155 // a graph has no notion of control flow ordering other than that induced |
| 156 // by the graph's dependencies. A schedule is required to generate code. |
| 157 class Schedule : public GenericGraph<BasicBlock> { |
| 158 public: |
| 159 explicit Schedule(Zone* zone) |
| 160 : GenericGraph<BasicBlock>(zone), |
| 161 zone_(zone), |
| 162 all_blocks_(BasicBlockVector::allocator_type(zone)), |
| 163 nodeid_to_block_(BasicBlockVector::allocator_type(zone)), |
| 164 rpo_order_(BasicBlockVector::allocator_type(zone)), |
| 165 immediate_dominator_(BasicBlockVector::allocator_type(zone)) { |
| 166 NewBasicBlock(); // entry. |
| 167 NewBasicBlock(); // exit. |
| 168 SetStart(entry()); |
| 169 SetEnd(exit()); |
| 170 } |
| 171 |
| 172 // TODO(titzer): rewrite users of these methods to use start() and end(). |
| 173 BasicBlock* entry() const { return all_blocks_[0]; } // Return entry block. |
| 174 BasicBlock* exit() const { return all_blocks_[1]; } // Return exit block. |
| 175 |
| 176 // Return the block which contains {node}, if any. |
| 177 BasicBlock* block(Node* node) const { |
| 178 if (node->id() < static_cast<NodeId>(nodeid_to_block_.size())) { |
| 179 return nodeid_to_block_[node->id()]; |
| 180 } |
| 181 return NULL; |
| 182 } |
| 183 |
| 184 BasicBlock* dominator(BasicBlock* block) { |
| 185 return immediate_dominator_[block->id()]; |
| 186 } |
| 187 |
| 188 bool IsScheduled(Node* node) { |
| 189 int length = static_cast<int>(nodeid_to_block_.size()); |
| 190 if (node->id() >= length) return false; |
| 191 return nodeid_to_block_[node->id()] != NULL; |
| 192 } |
| 193 |
| 194 BasicBlock* GetBlockById(int block_id) { return all_blocks_[block_id]; } |
| 195 |
| 196 int BasicBlockCount() const { return NodeCount(); } |
| 197 int RpoBlockCount() const { return rpo_order_.size(); } |
| 198 |
| 199 typedef ContainerPointerWrapper<BasicBlockVector> BasicBlocks; |
| 200 |
| 201 // Return a list of all the blocks in the schedule, in arbitrary order. |
| 202 BasicBlocks all_blocks() { return BasicBlocks(&all_blocks_); } |
| 203 |
| 204 // Check if nodes {a} and {b} are in the same block. |
| 205 inline bool SameBasicBlock(Node* a, Node* b) const { |
| 206 BasicBlock* block = this->block(a); |
| 207 return block != NULL && block == this->block(b); |
| 208 } |
| 209 |
| 210 // BasicBlock building: create a new block. |
| 211 inline BasicBlock* NewBasicBlock() { |
| 212 BasicBlock* block = |
| 213 BasicBlock::New(this, 0, static_cast<BasicBlock**>(NULL)); |
| 214 all_blocks_.push_back(block); |
| 215 return block; |
| 216 } |
| 217 |
| 218 // BasicBlock building: records that a node will later be added to a block but |
| 219 // doesn't actually add the node to the block. |
| 220 inline void PlanNode(BasicBlock* block, Node* node) { |
| 221 if (FLAG_trace_turbo_scheduler) { |
| 222 PrintF("Planning node %d for future add to block %d\n", node->id(), |
| 223 block->id()); |
| 224 } |
| 225 ASSERT(this->block(node) == NULL); |
| 226 SetBlockForNode(block, node); |
| 227 } |
| 228 |
| 229 // BasicBlock building: add a node to the end of the block. |
| 230 inline void AddNode(BasicBlock* block, Node* node) { |
| 231 if (FLAG_trace_turbo_scheduler) { |
| 232 PrintF("Adding node %d to block %d\n", node->id(), block->id()); |
| 233 } |
| 234 ASSERT(this->block(node) == NULL || this->block(node) == block); |
| 235 block->nodes_.push_back(node); |
| 236 SetBlockForNode(block, node); |
| 237 } |
| 238 |
| 239 // BasicBlock building: add a goto to the end of {block}. |
| 240 void AddGoto(BasicBlock* block, BasicBlock* succ) { |
| 241 ASSERT(block->control_ == BasicBlock::kNone); |
| 242 block->control_ = BasicBlock::kGoto; |
| 243 AddSuccessor(block, succ); |
| 244 } |
| 245 |
| 246 // BasicBlock building: add a (branching) call at the end of {block}. |
| 247 void AddCall(BasicBlock* block, Node* call, BasicBlock* cont_block, |
| 248 BasicBlock* deopt_block) { |
| 249 ASSERT(block->control_ == BasicBlock::kNone); |
| 250 ASSERT(call->opcode() == IrOpcode::kCall); |
| 251 block->control_ = BasicBlock::kCall; |
| 252 // Insert the deopt block first so that the RPO order builder picks |
| 253 // it first (and thus it ends up late in the RPO order). |
| 254 AddSuccessor(block, deopt_block); |
| 255 AddSuccessor(block, cont_block); |
| 256 SetControlInput(block, call); |
| 257 } |
| 258 |
| 259 // BasicBlock building: add a branch at the end of {block}. |
| 260 void AddBranch(BasicBlock* block, Node* branch, BasicBlock* tblock, |
| 261 BasicBlock* fblock) { |
| 262 ASSERT(block->control_ == BasicBlock::kNone); |
| 263 ASSERT(branch->opcode() == IrOpcode::kBranch); |
| 264 block->control_ = BasicBlock::kBranch; |
| 265 AddSuccessor(block, tblock); |
| 266 AddSuccessor(block, fblock); |
| 267 SetControlInput(block, branch); |
| 268 } |
| 269 |
| 270 // BasicBlock building: add a return at the end of {block}. |
| 271 void AddReturn(BasicBlock* block, Node* input) { |
| 272 // TODO(titzer): require a Return node here. |
| 273 ASSERT(block->control_ == BasicBlock::kNone); |
| 274 block->control_ = BasicBlock::kReturn; |
| 275 SetControlInput(block, input); |
| 276 if (block != exit()) AddSuccessor(block, exit()); |
| 277 } |
| 278 |
| 279 // BasicBlock building: add a throw at the end of {block}. |
| 280 void AddThrow(BasicBlock* block, Node* input) { |
| 281 ASSERT(block->control_ == BasicBlock::kNone); |
| 282 block->control_ = BasicBlock::kThrow; |
| 283 SetControlInput(block, input); |
| 284 if (block != exit()) AddSuccessor(block, exit()); |
| 285 } |
| 286 |
| 287 // BasicBlock building: add a deopt at the end of {block}. |
| 288 void AddDeoptimize(BasicBlock* block, Node* state) { |
| 289 ASSERT(block->control_ == BasicBlock::kNone); |
| 290 block->control_ = BasicBlock::kDeoptimize; |
| 291 SetControlInput(block, state); |
| 292 block->deferred_ = true; // By default, consider deopts the slow path. |
| 293 if (block != exit()) AddSuccessor(block, exit()); |
| 294 } |
| 295 |
| 296 friend class Scheduler; |
| 297 friend class CodeGenerator; |
| 298 |
| 299 void AddSuccessor(BasicBlock* block, BasicBlock* succ) { |
| 300 succ->AppendInput(zone_, block); |
| 301 } |
| 302 |
| 303 BasicBlockVector* rpo_order() { return &rpo_order_; } |
| 304 |
| 305 private: |
| 306 friend class ScheduleVisualizer; |
| 307 |
| 308 void SetControlInput(BasicBlock* block, Node* node) { |
| 309 block->control_input_ = node; |
| 310 SetBlockForNode(block, node); |
| 311 } |
| 312 |
| 313 void SetBlockForNode(BasicBlock* block, Node* node) { |
| 314 int length = static_cast<int>(nodeid_to_block_.size()); |
| 315 if (node->id() >= length) { |
| 316 nodeid_to_block_.resize(node->id() + 1); |
| 317 } |
| 318 nodeid_to_block_[node->id()] = block; |
| 319 } |
| 320 |
| 321 Zone* zone_; |
| 322 BasicBlockVector all_blocks_; // All basic blocks in the schedule. |
| 323 BasicBlockVector nodeid_to_block_; // Map from node to containing block. |
| 324 BasicBlockVector rpo_order_; // Reverse-post-order block list. |
| 325 BasicBlockVector immediate_dominator_; // Maps to a block's immediate |
| 326 // dominator, indexed by block |
| 327 // id. |
| 328 }; |
| 329 |
| 330 OStream& operator<<(OStream& os, const Schedule& s); |
| 331 } |
| 332 } |
| 333 } // namespace v8::internal::compiler |
| 334 |
| 335 #endif // V8_COMPILER_SCHEDULE_H_ |
OLD | NEW |