| // 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 <iosfwd> |
| |
| #include "src/base/compiler-specific.h" |
| #include "src/common/globals.h" |
| #include "src/zone/zone-containers.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace compiler { |
| |
| // Forward declarations. |
| class BasicBlock; |
| class BasicBlockInstrumentor; |
| class Node; |
| |
| using BasicBlockVector = ZoneVector<BasicBlock*>; |
| using NodeVector = ZoneVector<Node*>; |
| |
| // 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 V8_EXPORT_PRIVATE BasicBlock final |
| : public NON_EXPORTED_BASE(ZoneObject) { |
| public: |
| // Possible control nodes that can end a block. |
| enum Control { |
| kNone, // Control not initialized yet. |
| kGoto, // Goto a single successor block. |
| kCall, // Call with continuation as first successor, exception |
| // second. |
| kBranch, // Branch if true to first successor, otherwise second. |
| kSwitch, // Table dispatch to one of the successor blocks. |
| kDeoptimize, // Return a value from this method. |
| kTailCall, // Tail call another method from this method. |
| kReturn, // Return a value from this method. |
| kThrow // Throw an exception. |
| }; |
| |
| class Id { |
| public: |
| int ToInt() const { return static_cast<int>(index_); } |
| size_t ToSize() const { return index_; } |
| static Id FromSize(size_t index) { return Id(index); } |
| static Id FromInt(int index) { return Id(static_cast<size_t>(index)); } |
| |
| private: |
| explicit Id(size_t index) : index_(index) {} |
| size_t index_; |
| }; |
| |
| BasicBlock(Zone* zone, Id id); |
| BasicBlock(const BasicBlock&) = delete; |
| BasicBlock& operator=(const BasicBlock&) = delete; |
| |
| Id id() const { return id_; } |
| #if DEBUG |
| void set_debug_info(AssemblerDebugInfo debug_info) { |
| debug_info_ = debug_info; |
| } |
| AssemblerDebugInfo debug_info() const { return debug_info_; } |
| #endif // DEBUG |
| |
| void Print(); |
| |
| // Predecessors. |
| BasicBlockVector& predecessors() { return predecessors_; } |
| const BasicBlockVector& predecessors() const { return predecessors_; } |
| size_t PredecessorCount() const { return predecessors_.size(); } |
| BasicBlock* PredecessorAt(size_t index) { return predecessors_[index]; } |
| void ClearPredecessors() { predecessors_.clear(); } |
| void AddPredecessor(BasicBlock* predecessor); |
| void RemovePredecessor(size_t index); |
| |
| // Successors. |
| BasicBlockVector& successors() { return successors_; } |
| const BasicBlockVector& successors() const { return successors_; } |
| size_t SuccessorCount() const { return successors_.size(); } |
| BasicBlock* SuccessorAt(size_t index) { return successors_[index]; } |
| void ClearSuccessors() { successors_.clear(); } |
| void AddSuccessor(BasicBlock* successor); |
| |
| // Nodes in the basic block. |
| using value_type = Node*; |
| bool empty() const { return nodes_.empty(); } |
| size_t size() const { return nodes_.size(); } |
| Node* NodeAt(size_t index) { return nodes_[index]; } |
| size_t NodeCount() const { return nodes_.size(); } |
| |
| value_type& front() { return nodes_.front(); } |
| value_type const& front() const { return nodes_.front(); } |
| |
| using iterator = NodeVector::iterator; |
| iterator begin() { return nodes_.begin(); } |
| iterator end() { return nodes_.end(); } |
| |
| void RemoveNode(iterator it) { nodes_.erase(it); } |
| |
| using const_iterator = NodeVector::const_iterator; |
| const_iterator begin() const { return nodes_.begin(); } |
| const_iterator end() const { return nodes_.end(); } |
| |
| using reverse_iterator = NodeVector::reverse_iterator; |
| reverse_iterator rbegin() { return nodes_.rbegin(); } |
| reverse_iterator rend() { return nodes_.rend(); } |
| |
| void AddNode(Node* node); |
| template <class InputIterator> |
| void InsertNodes(iterator insertion_point, InputIterator insertion_start, |
| InputIterator insertion_end) { |
| nodes_.insert(insertion_point, insertion_start, insertion_end); |
| } |
| |
| // Trim basic block to end at {new_end}. |
| void TrimNodes(iterator new_end); |
| |
| void ResetRPOInfo(); |
| |
| // Accessors. |
| Control control() const { return control_; } |
| void set_control(Control control); |
| |
| Node* control_input() const { return control_input_; } |
| void set_control_input(Node* control_input); |
| |
| bool deferred() const { return deferred_; } |
| void set_deferred(bool deferred) { deferred_ = deferred; } |
| |
| int32_t dominator_depth() const { return dominator_depth_; } |
| void set_dominator_depth(int32_t depth) { dominator_depth_ = depth; } |
| |
| BasicBlock* dominator() const { return dominator_; } |
| void set_dominator(BasicBlock* dominator) { dominator_ = dominator; } |
| |
| BasicBlock* rpo_next() const { return rpo_next_; } |
| void set_rpo_next(BasicBlock* rpo_next) { rpo_next_ = rpo_next; } |
| |
| BasicBlock* loop_header() const { return loop_header_; } |
| void set_loop_header(BasicBlock* loop_header); |
| |
| BasicBlock* loop_end() const { return loop_end_; } |
| void set_loop_end(BasicBlock* loop_end); |
| |
| int32_t loop_depth() const { return loop_depth_; } |
| void set_loop_depth(int32_t loop_depth); |
| |
| int32_t loop_number() const { return loop_number_; } |
| void set_loop_number(int32_t loop_number) { loop_number_ = loop_number; } |
| |
| int32_t rpo_number() const { return rpo_number_; } |
| void set_rpo_number(int32_t rpo_number); |
| |
| NodeVector* nodes() { return &nodes_; } |
| |
| // Loop membership helpers. |
| inline bool IsLoopHeader() const { return loop_end_ != nullptr; } |
| bool LoopContains(BasicBlock* block) const; |
| |
| // Computes the immediate common dominator of {b1} and {b2}. The worst time |
| // complexity is O(N) where N is the height of the dominator tree. |
| static BasicBlock* GetCommonDominator(BasicBlock* b1, BasicBlock* b2); |
| |
| private: |
| int32_t loop_number_; // loop number of the block. |
| int32_t rpo_number_; // special RPO number of the block. |
| bool deferred_; // true if the block contains deferred code. |
| int32_t dominator_depth_; // Depth within the dominator tree. |
| BasicBlock* dominator_; // Immediate dominator of the block. |
| BasicBlock* rpo_next_; // Link to next block in special RPO order. |
| BasicBlock* loop_header_; // Pointer to dominating loop header basic block, |
| // nullptr if none. For loop headers, this points to |
| // enclosing loop header. |
| BasicBlock* loop_end_; // end of the loop, if this block is a loop header. |
| int32_t loop_depth_; // loop nesting, 0 is top-level |
| |
| 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. |
| |
| BasicBlockVector successors_; |
| BasicBlockVector predecessors_; |
| #if DEBUG |
| AssemblerDebugInfo debug_info_; |
| #endif |
| Id id_; |
| }; |
| |
| std::ostream& operator<<(std::ostream&, const BasicBlock&); |
| std::ostream& operator<<(std::ostream&, const BasicBlock::Control&); |
| std::ostream& operator<<(std::ostream&, const BasicBlock::Id&); |
| |
| // 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 V8_EXPORT_PRIVATE Schedule final : public NON_EXPORTED_BASE(ZoneObject) { |
| public: |
| explicit Schedule(Zone* zone, size_t node_count_hint = 0); |
| Schedule(const Schedule&) = delete; |
| Schedule& operator=(const Schedule&) = delete; |
| |
| // Return the block which contains {node}, if any. |
| BasicBlock* block(Node* node) const; |
| |
| bool IsScheduled(Node* node); |
| BasicBlock* GetBlockById(BasicBlock::Id block_id); |
| void ClearBlockById(BasicBlock::Id block_id); |
| |
| size_t BasicBlockCount() const { return all_blocks_.size(); } |
| size_t RpoBlockCount() const { return rpo_order_.size(); } |
| |
| // Check if nodes {a} and {b} are in the same block. |
| bool SameBasicBlock(Node* a, Node* b) const; |
| |
| // BasicBlock building: create a new block. |
| BasicBlock* NewBasicBlock(); |
| |
| // BasicBlock building: records that a node will later be added to a block but |
| // doesn't actually add the node to the block. |
| void PlanNode(BasicBlock* block, Node* node); |
| |
| // BasicBlock building: add a node to the end of the block. |
| void AddNode(BasicBlock* block, Node* node); |
| |
| // BasicBlock building: add a goto to the end of {block}. |
| void AddGoto(BasicBlock* block, BasicBlock* succ); |
| |
| // BasicBlock building: add a call at the end of {block}. |
| void AddCall(BasicBlock* block, Node* call, BasicBlock* success_block, |
| BasicBlock* exception_block); |
| |
| // BasicBlock building: add a branch at the end of {block}. |
| void AddBranch(BasicBlock* block, Node* branch, BasicBlock* tblock, |
| BasicBlock* fblock); |
| |
| // BasicBlock building: add a switch at the end of {block}. |
| void AddSwitch(BasicBlock* block, Node* sw, BasicBlock** succ_blocks, |
| size_t succ_count); |
| |
| // BasicBlock building: add a deoptimize at the end of {block}. |
| void AddDeoptimize(BasicBlock* block, Node* input); |
| |
| // BasicBlock building: add a tailcall at the end of {block}. |
| void AddTailCall(BasicBlock* block, Node* input); |
| |
| // BasicBlock building: add a return at the end of {block}. |
| void AddReturn(BasicBlock* block, Node* input); |
| |
| // BasicBlock building: add a throw at the end of {block}. |
| void AddThrow(BasicBlock* block, Node* input); |
| |
| // BasicBlock mutation: insert a branch into the end of {block}. |
| void InsertBranch(BasicBlock* block, BasicBlock* end, Node* branch, |
| BasicBlock* tblock, BasicBlock* fblock); |
| |
| // BasicBlock mutation: insert a switch into the end of {block}. |
| void InsertSwitch(BasicBlock* block, BasicBlock* end, Node* sw, |
| BasicBlock** succ_blocks, size_t succ_count); |
| |
| // Exposed publicly for testing only. |
| void AddSuccessorForTesting(BasicBlock* block, BasicBlock* succ) { |
| return AddSuccessor(block, succ); |
| } |
| |
| const BasicBlockVector* all_blocks() const { return &all_blocks_; } |
| BasicBlockVector* rpo_order() { return &rpo_order_; } |
| const BasicBlockVector* rpo_order() const { return &rpo_order_; } |
| |
| BasicBlock* start() { return start_; } |
| BasicBlock* end() { return end_; } |
| |
| Zone* zone() const { return zone_; } |
| |
| private: |
| friend class GraphAssembler; |
| friend class Scheduler; |
| friend class BasicBlockInstrumentor; |
| friend class RawMachineAssembler; |
| |
| // For CSA/Torque: Ensure properties of the CFG assumed by further stages. |
| void EnsureCFGWellFormedness(); |
| // For CSA/Torque: Eliminates unnecessary phi nodes, including phis with a |
| // single input. The latter is necessary to ensure the property required for |
| // SSA deconstruction that the target block of a control flow split has no |
| // phis. |
| void EliminateRedundantPhiNodes(); |
| // Ensure split-edge form for a hand-assembled schedule. |
| void EnsureSplitEdgeForm(BasicBlock* block); |
| // Move Phi operands to newly created merger blocks |
| void MovePhis(BasicBlock* from, BasicBlock* to); |
| // Copy deferred block markers down as far as possible |
| void PropagateDeferredMark(); |
| |
| void AddSuccessor(BasicBlock* block, BasicBlock* succ); |
| void MoveSuccessors(BasicBlock* from, BasicBlock* to); |
| |
| void SetControlInput(BasicBlock* block, Node* node); |
| void SetBlockForNode(BasicBlock* block, Node* node); |
| |
| 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. |
| BasicBlock* start_; |
| BasicBlock* end_; |
| }; |
| |
| V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream&, const Schedule&); |
| |
| } // namespace compiler |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_COMPILER_SCHEDULE_H_ |