blob: 070691e4c6e8af7d6293875bae714abfeead9cb9 [file] [log] [blame]
// Copyright 2013 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_COMPILER_SCHEDULE_H_
#define V8_COMPILER_SCHEDULE_H_
#include <vector>
#include "src/v8.h"
#include "src/compiler/generic-algorithm.h"
#include "src/compiler/generic-graph.h"
#include "src/compiler/generic-node.h"
#include "src/compiler/generic-node-inl.h"
#include "src/compiler/node.h"
#include "src/compiler/opcodes.h"
#include "src/zone.h"
namespace v8 {
namespace internal {
namespace compiler {
class BasicBlock;
class Graph;
class ConstructScheduleData;
class CodeGenerator; // Because of a namespace bug in clang.
class BasicBlockData {
public:
// Possible control nodes that can end a block.
enum Control {
kNone, // Control not initialized yet.
kGoto, // Goto a single successor block.
kBranch, // Branch if true to first successor, otherwise second.
kReturn, // Return a value from this method.
kThrow // Throw an exception.
};
int32_t rpo_number_; // special RPO number of the block.
BasicBlock* dominator_; // Immediate dominator of the block.
BasicBlock* loop_header_; // Pointer to dominating loop header basic block,
// NULL if none. For loop headers, this points to
// enclosing loop header.
int32_t loop_depth_; // loop nesting, 0 is top-level
int32_t loop_end_; // end of the loop, if this block is a loop header.
int32_t code_start_; // start index of arch-specific code.
int32_t code_end_; // end index of arch-specific code.
bool deferred_; // {true} if this block is considered the slow
// path.
Control control_; // Control at the end of the block.
Node* control_input_; // Input value for control.
NodeVector nodes_; // nodes of this block in forward order.
explicit BasicBlockData(Zone* zone)
: rpo_number_(-1),
dominator_(NULL),
loop_header_(NULL),
loop_depth_(0),
loop_end_(-1),
code_start_(-1),
code_end_(-1),
deferred_(false),
control_(kNone),
control_input_(NULL),
nodes_(zone) {}
inline bool IsLoopHeader() const { return loop_end_ >= 0; }
inline bool LoopContains(BasicBlockData* block) const {
// RPO numbers must be initialized.
DCHECK(rpo_number_ >= 0);
DCHECK(block->rpo_number_ >= 0);
if (loop_end_ < 0) return false; // This is not a loop.
return block->rpo_number_ >= rpo_number_ && block->rpo_number_ < loop_end_;
}
int first_instruction_index() {
DCHECK(code_start_ >= 0);
DCHECK(code_end_ > 0);
DCHECK(code_end_ >= code_start_);
return code_start_;
}
int last_instruction_index() {
DCHECK(code_start_ >= 0);
DCHECK(code_end_ > 0);
DCHECK(code_end_ >= code_start_);
return code_end_ - 1;
}
};
OStream& operator<<(OStream& os, const BasicBlockData::Control& c);
// A basic block contains an ordered list of nodes and ends with a control
// node. Note that if a basic block has phis, then all phis must appear as the
// first nodes in the block.
class BasicBlock FINAL : public GenericNode<BasicBlockData, BasicBlock> {
public:
BasicBlock(GenericGraphBase* graph, int input_count)
: GenericNode<BasicBlockData, BasicBlock>(graph, input_count) {}
typedef Uses Successors;
typedef Inputs Predecessors;
Successors successors() { return static_cast<Successors>(uses()); }
Predecessors predecessors() { return static_cast<Predecessors>(inputs()); }
int PredecessorCount() { return InputCount(); }
BasicBlock* PredecessorAt(int index) { return InputAt(index); }
int SuccessorCount() { return UseCount(); }
BasicBlock* SuccessorAt(int index) { return UseAt(index); }
int PredecessorIndexOf(BasicBlock* predecessor) {
BasicBlock::Predecessors predecessors = this->predecessors();
for (BasicBlock::Predecessors::iterator i = predecessors.begin();
i != predecessors.end(); ++i) {
if (*i == predecessor) return i.index();
}
return -1;
}
inline BasicBlock* loop_header() {
return static_cast<BasicBlock*>(loop_header_);
}
inline BasicBlock* ContainingLoop() {
if (IsLoopHeader()) return this;
return static_cast<BasicBlock*>(loop_header_);
}
typedef NodeVector::iterator iterator;
iterator begin() { return nodes_.begin(); }
iterator end() { return nodes_.end(); }
typedef NodeVector::const_iterator const_iterator;
const_iterator begin() const { return nodes_.begin(); }
const_iterator end() const { return nodes_.end(); }
typedef NodeVector::reverse_iterator reverse_iterator;
reverse_iterator rbegin() { return nodes_.rbegin(); }
reverse_iterator rend() { return nodes_.rend(); }
private:
DISALLOW_COPY_AND_ASSIGN(BasicBlock);
};
typedef GenericGraphVisit::NullNodeVisitor<BasicBlockData, BasicBlock>
NullBasicBlockVisitor;
typedef ZoneVector<BasicBlock*> BasicBlockVector;
typedef BasicBlockVector::iterator BasicBlockVectorIter;
typedef BasicBlockVector::reverse_iterator BasicBlockVectorRIter;
// A schedule represents the result of assigning nodes to basic blocks
// and ordering them within basic blocks. Prior to computing a schedule,
// a graph has no notion of control flow ordering other than that induced
// by the graph's dependencies. A schedule is required to generate code.
class Schedule : public GenericGraph<BasicBlock> {
public:
explicit Schedule(Zone* zone)
: GenericGraph<BasicBlock>(zone),
zone_(zone),
all_blocks_(zone),
nodeid_to_block_(zone),
rpo_order_(zone) {
SetStart(NewBasicBlock()); // entry.
SetEnd(NewBasicBlock()); // exit.
}
// Return the block which contains {node}, if any.
BasicBlock* block(Node* node) const {
if (node->id() < static_cast<NodeId>(nodeid_to_block_.size())) {
return nodeid_to_block_[node->id()];
}
return NULL;
}
bool IsScheduled(Node* node) {
int length = static_cast<int>(nodeid_to_block_.size());
if (node->id() >= length) return false;
return nodeid_to_block_[node->id()] != NULL;
}
BasicBlock* GetBlockById(int block_id) { return all_blocks_[block_id]; }
int BasicBlockCount() const { return NodeCount(); }
int RpoBlockCount() const { return static_cast<int>(rpo_order_.size()); }
typedef ContainerPointerWrapper<BasicBlockVector> BasicBlocks;
// Return a list of all the blocks in the schedule, in arbitrary order.
BasicBlocks all_blocks() { return BasicBlocks(&all_blocks_); }
// Check if nodes {a} and {b} are in the same block.
inline bool SameBasicBlock(Node* a, Node* b) const {
BasicBlock* block = this->block(a);
return block != NULL && block == this->block(b);
}
// BasicBlock building: create a new block.
inline BasicBlock* NewBasicBlock() {
BasicBlock* block =
BasicBlock::New(this, 0, static_cast<BasicBlock**>(NULL));
all_blocks_.push_back(block);
return block;
}
// BasicBlock building: records that a node will later be added to a block but
// doesn't actually add the node to the block.
inline void PlanNode(BasicBlock* block, Node* node) {
if (FLAG_trace_turbo_scheduler) {
PrintF("Planning #%d:%s for future add to B%d\n", node->id(),
node->op()->mnemonic(), block->id());
}
DCHECK(this->block(node) == NULL);
SetBlockForNode(block, node);
}
// BasicBlock building: add a node to the end of the block.
inline void AddNode(BasicBlock* block, Node* node) {
if (FLAG_trace_turbo_scheduler) {
PrintF("Adding #%d:%s to B%d\n", node->id(), node->op()->mnemonic(),
block->id());
}
DCHECK(this->block(node) == NULL || this->block(node) == block);
block->nodes_.push_back(node);
SetBlockForNode(block, node);
}
// BasicBlock building: add a goto to the end of {block}.
void AddGoto(BasicBlock* block, BasicBlock* succ) {
DCHECK(block->control_ == BasicBlock::kNone);
block->control_ = BasicBlock::kGoto;
AddSuccessor(block, succ);
}
// BasicBlock building: add a branch at the end of {block}.
void AddBranch(BasicBlock* block, Node* branch, BasicBlock* tblock,
BasicBlock* fblock) {
DCHECK(block->control_ == BasicBlock::kNone);
DCHECK(branch->opcode() == IrOpcode::kBranch);
block->control_ = BasicBlock::kBranch;
AddSuccessor(block, tblock);
AddSuccessor(block, fblock);
SetControlInput(block, branch);
if (branch->opcode() == IrOpcode::kBranch) {
// TODO(titzer): require a Branch node here. (sloppy tests).
SetBlockForNode(block, branch);
}
}
// BasicBlock building: add a return at the end of {block}.
void AddReturn(BasicBlock* block, Node* input) {
DCHECK(block->control_ == BasicBlock::kNone);
block->control_ = BasicBlock::kReturn;
SetControlInput(block, input);
if (block != end()) AddSuccessor(block, end());
if (input->opcode() == IrOpcode::kReturn) {
// TODO(titzer): require a Return node here. (sloppy tests).
SetBlockForNode(block, input);
}
}
// BasicBlock building: add a throw at the end of {block}.
void AddThrow(BasicBlock* block, Node* input) {
DCHECK(block->control_ == BasicBlock::kNone);
block->control_ = BasicBlock::kThrow;
SetControlInput(block, input);
if (block != end()) AddSuccessor(block, end());
}
friend class Scheduler;
friend class CodeGenerator;
void AddSuccessor(BasicBlock* block, BasicBlock* succ) {
succ->AppendInput(zone_, block);
}
BasicBlockVector* rpo_order() { return &rpo_order_; }
private:
friend class ScheduleVisualizer;
void SetControlInput(BasicBlock* block, Node* node) {
block->control_input_ = node;
SetBlockForNode(block, node);
}
void SetBlockForNode(BasicBlock* block, Node* node) {
int length = static_cast<int>(nodeid_to_block_.size());
if (node->id() >= length) {
nodeid_to_block_.resize(node->id() + 1);
}
nodeid_to_block_[node->id()] = block;
}
Zone* zone_;
BasicBlockVector all_blocks_; // All basic blocks in the schedule.
BasicBlockVector nodeid_to_block_; // Map from node to containing block.
BasicBlockVector rpo_order_; // Reverse-post-order block list.
};
OStream& operator<<(OStream& os, const Schedule& s);
}
}
} // namespace v8::internal::compiler
#endif // V8_COMPILER_SCHEDULE_H_