| // Copyright 2014 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_LOOP_ANALYSIS_H_ |
| #define V8_COMPILER_LOOP_ANALYSIS_H_ |
| |
| #include "src/base/iterator.h" |
| #include "src/compiler/graph.h" |
| #include "src/compiler/node.h" |
| #include "src/globals.h" |
| #include "src/zone/zone-containers.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace compiler { |
| |
| // TODO(titzer): don't assume entry edges have a particular index. |
| static const int kAssumedLoopEntryIndex = 0; // assume loops are entered here. |
| |
| class LoopFinderImpl; |
| |
| typedef base::iterator_range<Node**> NodeRange; |
| |
| // Represents a tree of loops in a graph. |
| class LoopTree : public ZoneObject { |
| public: |
| LoopTree(size_t num_nodes, Zone* zone) |
| : zone_(zone), |
| outer_loops_(zone), |
| all_loops_(zone), |
| node_to_loop_num_(static_cast<int>(num_nodes), -1, zone), |
| loop_nodes_(zone) {} |
| |
| // Represents a loop in the tree of loops, including the header nodes, |
| // the body, and any nested loops. |
| class Loop { |
| public: |
| Loop* parent() const { return parent_; } |
| const ZoneVector<Loop*>& children() const { return children_; } |
| size_t HeaderSize() const { return body_start_ - header_start_; } |
| size_t BodySize() const { return exits_start_ - body_start_; } |
| size_t ExitsSize() const { return exits_end_ - exits_start_; } |
| size_t TotalSize() const { return exits_end_ - header_start_; } |
| size_t depth() const { return static_cast<size_t>(depth_); } |
| |
| private: |
| friend class LoopTree; |
| friend class LoopFinderImpl; |
| |
| explicit Loop(Zone* zone) |
| : parent_(nullptr), |
| depth_(0), |
| children_(zone), |
| header_start_(-1), |
| body_start_(-1), |
| exits_start_(-1), |
| exits_end_(-1) {} |
| Loop* parent_; |
| int depth_; |
| ZoneVector<Loop*> children_; |
| int header_start_; |
| int body_start_; |
| int exits_start_; |
| int exits_end_; |
| }; |
| |
| // Return the innermost nested loop, if any, that contains {node}. |
| Loop* ContainingLoop(Node* node) { |
| if (node->id() >= node_to_loop_num_.size()) return nullptr; |
| int num = node_to_loop_num_[node->id()]; |
| return num > 0 ? &all_loops_[num - 1] : nullptr; |
| } |
| |
| // Check if the {loop} contains the {node}, either directly or by containing |
| // a nested loop that contains {node}. |
| bool Contains(Loop* loop, Node* node) { |
| for (Loop* c = ContainingLoop(node); c != nullptr; c = c->parent_) { |
| if (c == loop) return true; |
| } |
| return false; |
| } |
| |
| // Return the list of outer loops. |
| const ZoneVector<Loop*>& outer_loops() const { return outer_loops_; } |
| |
| // Return the unique loop number for a given loop. Loop numbers start at {1}. |
| int LoopNum(Loop* loop) const { |
| return 1 + static_cast<int>(loop - &all_loops_[0]); |
| } |
| |
| // Return a range which can iterate over the header nodes of {loop}. |
| NodeRange HeaderNodes(Loop* loop) { |
| return NodeRange(&loop_nodes_[0] + loop->header_start_, |
| &loop_nodes_[0] + loop->body_start_); |
| } |
| |
| // Return the header control node for a loop. |
| Node* HeaderNode(Loop* loop); |
| |
| // Return a range which can iterate over the body nodes of {loop}. |
| NodeRange BodyNodes(Loop* loop) { |
| return NodeRange(&loop_nodes_[0] + loop->body_start_, |
| &loop_nodes_[0] + loop->exits_start_); |
| } |
| |
| // Return a range which can iterate over the body nodes of {loop}. |
| NodeRange ExitNodes(Loop* loop) { |
| return NodeRange(&loop_nodes_[0] + loop->exits_start_, |
| &loop_nodes_[0] + loop->exits_end_); |
| } |
| |
| // Return a range which can iterate over the nodes of {loop}. |
| NodeRange LoopNodes(Loop* loop) { |
| return NodeRange(&loop_nodes_[0] + loop->header_start_, |
| &loop_nodes_[0] + loop->exits_end_); |
| } |
| |
| // Return the node that represents the control, i.e. the loop node itself. |
| Node* GetLoopControl(Loop* loop) { |
| // TODO(turbofan): make the loop control node always first? |
| for (Node* node : HeaderNodes(loop)) { |
| if (node->opcode() == IrOpcode::kLoop) return node; |
| } |
| UNREACHABLE(); |
| } |
| |
| Zone* zone() const { return zone_; } |
| |
| private: |
| friend class LoopFinderImpl; |
| |
| Loop* NewLoop() { |
| all_loops_.push_back(Loop(zone_)); |
| Loop* result = &all_loops_.back(); |
| return result; |
| } |
| |
| void SetParent(Loop* parent, Loop* child) { |
| if (parent != nullptr) { |
| parent->children_.push_back(child); |
| child->parent_ = parent; |
| child->depth_ = parent->depth_ + 1; |
| } else { |
| outer_loops_.push_back(child); |
| } |
| } |
| |
| Zone* zone_; |
| ZoneVector<Loop*> outer_loops_; |
| ZoneVector<Loop> all_loops_; |
| ZoneVector<int> node_to_loop_num_; |
| ZoneVector<Node*> loop_nodes_; |
| }; |
| |
| class V8_EXPORT_PRIVATE LoopFinder { |
| public: |
| // Build a loop tree for the entire graph. |
| static LoopTree* BuildLoopTree(Graph* graph, Zone* temp_zone); |
| }; |
| |
| |
| } // namespace compiler |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_COMPILER_LOOP_ANALYSIS_H_ |