Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 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 | }; |
| 39 | |
| 40 | int32_t rpo_number_; // special RPO number of the block. |
| 41 | BasicBlock* dominator_; // Immediate dominator of the block. |
| 42 | BasicBlock* loop_header_; // Pointer to dominating loop header basic block, |
| 43 | // NULL if none. For loop headers, this points to |
| 44 | // enclosing loop header. |
| 45 | int32_t loop_depth_; // loop nesting, 0 is top-level |
| 46 | int32_t loop_end_; // end of the loop, if this block is a loop header. |
| 47 | int32_t code_start_; // start index of arch-specific code. |
| 48 | int32_t code_end_; // end index of arch-specific code. |
| 49 | bool deferred_; // {true} if this block is considered the slow |
| 50 | // path. |
| 51 | Control control_; // Control at the end of the block. |
| 52 | Node* control_input_; // Input value for control. |
| 53 | NodeVector nodes_; // nodes of this block in forward order. |
| 54 | |
| 55 | explicit BasicBlockData(Zone* zone) |
| 56 | : rpo_number_(-1), |
| 57 | dominator_(NULL), |
| 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_(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 | DCHECK(rpo_number_ >= 0); |
| 72 | DCHECK(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 | DCHECK(code_start_ >= 0); |
| 78 | DCHECK(code_end_ > 0); |
| 79 | DCHECK(code_end_ >= code_start_); |
| 80 | return code_start_; |
| 81 | } |
| 82 | int last_instruction_index() { |
| 83 | DCHECK(code_start_ >= 0); |
| 84 | DCHECK(code_end_ > 0); |
| 85 | DCHECK(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 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 ZoneVector<BasicBlock*> BasicBlockVector; |
| 149 | typedef BasicBlockVector::iterator BasicBlockVectorIter; |
| 150 | typedef BasicBlockVector::reverse_iterator BasicBlockVectorRIter; |
| 151 | |
| 152 | // A schedule represents the result of assigning nodes to basic blocks |
| 153 | // and ordering them within basic blocks. Prior to computing a schedule, |
| 154 | // a graph has no notion of control flow ordering other than that induced |
| 155 | // by the graph's dependencies. A schedule is required to generate code. |
| 156 | class Schedule : public GenericGraph<BasicBlock> { |
| 157 | public: |
| 158 | explicit Schedule(Zone* zone) |
| 159 | : GenericGraph<BasicBlock>(zone), |
| 160 | zone_(zone), |
| 161 | all_blocks_(zone), |
| 162 | nodeid_to_block_(zone), |
| 163 | rpo_order_(zone) { |
| 164 | SetStart(NewBasicBlock()); // entry. |
| 165 | SetEnd(NewBasicBlock()); // exit. |
| 166 | } |
| 167 | |
| 168 | // Return the block which contains {node}, if any. |
| 169 | BasicBlock* block(Node* node) const { |
| 170 | if (node->id() < static_cast<NodeId>(nodeid_to_block_.size())) { |
| 171 | return nodeid_to_block_[node->id()]; |
| 172 | } |
| 173 | return NULL; |
| 174 | } |
| 175 | |
| 176 | bool IsScheduled(Node* node) { |
| 177 | int length = static_cast<int>(nodeid_to_block_.size()); |
| 178 | if (node->id() >= length) return false; |
| 179 | return nodeid_to_block_[node->id()] != NULL; |
| 180 | } |
| 181 | |
| 182 | BasicBlock* GetBlockById(int block_id) { return all_blocks_[block_id]; } |
| 183 | |
| 184 | int BasicBlockCount() const { return NodeCount(); } |
| 185 | int RpoBlockCount() const { return static_cast<int>(rpo_order_.size()); } |
| 186 | |
| 187 | typedef ContainerPointerWrapper<BasicBlockVector> BasicBlocks; |
| 188 | |
| 189 | // Return a list of all the blocks in the schedule, in arbitrary order. |
| 190 | BasicBlocks all_blocks() { return BasicBlocks(&all_blocks_); } |
| 191 | |
| 192 | // Check if nodes {a} and {b} are in the same block. |
| 193 | inline bool SameBasicBlock(Node* a, Node* b) const { |
| 194 | BasicBlock* block = this->block(a); |
| 195 | return block != NULL && block == this->block(b); |
| 196 | } |
| 197 | |
| 198 | // BasicBlock building: create a new block. |
| 199 | inline BasicBlock* NewBasicBlock() { |
| 200 | BasicBlock* block = |
| 201 | BasicBlock::New(this, 0, static_cast<BasicBlock**>(NULL)); |
| 202 | all_blocks_.push_back(block); |
| 203 | return block; |
| 204 | } |
| 205 | |
| 206 | // BasicBlock building: records that a node will later be added to a block but |
| 207 | // doesn't actually add the node to the block. |
| 208 | inline void PlanNode(BasicBlock* block, Node* node) { |
| 209 | if (FLAG_trace_turbo_scheduler) { |
| 210 | PrintF("Planning #%d:%s for future add to B%d\n", node->id(), |
| 211 | node->op()->mnemonic(), block->id()); |
| 212 | } |
| 213 | DCHECK(this->block(node) == NULL); |
| 214 | SetBlockForNode(block, node); |
| 215 | } |
| 216 | |
| 217 | // BasicBlock building: add a node to the end of the block. |
| 218 | inline void AddNode(BasicBlock* block, Node* node) { |
| 219 | if (FLAG_trace_turbo_scheduler) { |
| 220 | PrintF("Adding #%d:%s to B%d\n", node->id(), node->op()->mnemonic(), |
| 221 | block->id()); |
| 222 | } |
| 223 | DCHECK(this->block(node) == NULL || this->block(node) == block); |
| 224 | block->nodes_.push_back(node); |
| 225 | SetBlockForNode(block, node); |
| 226 | } |
| 227 | |
| 228 | // BasicBlock building: add a goto to the end of {block}. |
| 229 | void AddGoto(BasicBlock* block, BasicBlock* succ) { |
| 230 | DCHECK(block->control_ == BasicBlock::kNone); |
| 231 | block->control_ = BasicBlock::kGoto; |
| 232 | AddSuccessor(block, succ); |
| 233 | } |
| 234 | |
| 235 | // BasicBlock building: add a branch at the end of {block}. |
| 236 | void AddBranch(BasicBlock* block, Node* branch, BasicBlock* tblock, |
| 237 | BasicBlock* fblock) { |
| 238 | DCHECK(block->control_ == BasicBlock::kNone); |
| 239 | DCHECK(branch->opcode() == IrOpcode::kBranch); |
| 240 | block->control_ = BasicBlock::kBranch; |
| 241 | AddSuccessor(block, tblock); |
| 242 | AddSuccessor(block, fblock); |
| 243 | SetControlInput(block, branch); |
| 244 | if (branch->opcode() == IrOpcode::kBranch) { |
| 245 | // TODO(titzer): require a Branch node here. (sloppy tests). |
| 246 | SetBlockForNode(block, branch); |
| 247 | } |
| 248 | } |
| 249 | |
| 250 | // BasicBlock building: add a return at the end of {block}. |
| 251 | void AddReturn(BasicBlock* block, Node* input) { |
| 252 | DCHECK(block->control_ == BasicBlock::kNone); |
| 253 | block->control_ = BasicBlock::kReturn; |
| 254 | SetControlInput(block, input); |
| 255 | if (block != end()) AddSuccessor(block, end()); |
| 256 | if (input->opcode() == IrOpcode::kReturn) { |
| 257 | // TODO(titzer): require a Return node here. (sloppy tests). |
| 258 | SetBlockForNode(block, input); |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | // BasicBlock building: add a throw at the end of {block}. |
| 263 | void AddThrow(BasicBlock* block, Node* input) { |
| 264 | DCHECK(block->control_ == BasicBlock::kNone); |
| 265 | block->control_ = BasicBlock::kThrow; |
| 266 | SetControlInput(block, input); |
| 267 | if (block != end()) AddSuccessor(block, end()); |
| 268 | } |
| 269 | |
| 270 | friend class Scheduler; |
| 271 | friend class CodeGenerator; |
| 272 | |
| 273 | void AddSuccessor(BasicBlock* block, BasicBlock* succ) { |
| 274 | succ->AppendInput(zone_, block); |
| 275 | } |
| 276 | |
| 277 | BasicBlockVector* rpo_order() { return &rpo_order_; } |
| 278 | |
| 279 | private: |
| 280 | friend class ScheduleVisualizer; |
| 281 | |
| 282 | void SetControlInput(BasicBlock* block, Node* node) { |
| 283 | block->control_input_ = node; |
| 284 | SetBlockForNode(block, node); |
| 285 | } |
| 286 | |
| 287 | void SetBlockForNode(BasicBlock* block, Node* node) { |
| 288 | int length = static_cast<int>(nodeid_to_block_.size()); |
| 289 | if (node->id() >= length) { |
| 290 | nodeid_to_block_.resize(node->id() + 1); |
| 291 | } |
| 292 | nodeid_to_block_[node->id()] = block; |
| 293 | } |
| 294 | |
| 295 | Zone* zone_; |
| 296 | BasicBlockVector all_blocks_; // All basic blocks in the schedule. |
| 297 | BasicBlockVector nodeid_to_block_; // Map from node to containing block. |
| 298 | BasicBlockVector rpo_order_; // Reverse-post-order block list. |
| 299 | }; |
| 300 | |
| 301 | OStream& operator<<(OStream& os, const Schedule& s); |
| 302 | } |
| 303 | } |
| 304 | } // namespace v8::internal::compiler |
| 305 | |
| 306 | #endif // V8_COMPILER_SCHEDULE_H_ |