| //===- subzero/src/IceLoopAnalyzer.cpp - Loop Analysis --------------------===// | |
| // | |
| // The Subzero Code Generator | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// | |
| /// \file | |
| /// \brief Implements the loop analysis on the CFG. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #include "IceLoopAnalyzer.h" | |
| #include "IceCfg.h" | |
| #include "IceCfgNode.h" | |
| #include <algorithm> | |
| namespace Ice { | |
| class LoopAnalyzer { | |
| public: | |
| explicit LoopAnalyzer(Cfg *Func); | |
| /// Use Tarjan's strongly connected components algorithm to identify outermost | |
| /// to innermost loops. By deleting the head of the loop from the graph, inner | |
| /// loops can be found. This assumes that the head node is not shared between | |
| /// loops but instead all paths to the head come from 'continue' constructs. | |
| /// | |
| /// This only computes the loop nest depth within the function and does not | |
| /// take into account whether the function was called from within a loop. | |
| // TODO(ascull): this currently uses a extension of Tarjan's algorithm with | |
| // is bounded linear. ncbray suggests another algorithm which is linear in | |
| // practice but not bounded linear. I think it also finds dominators. | |
| // http://lenx.100871.net/papers/loop-SAS.pdf | |
| CfgVector<CfgUnorderedSet<SizeT>> getLoopBodies() { return Loops; } | |
| private: | |
| LoopAnalyzer() = delete; | |
| LoopAnalyzer(const LoopAnalyzer &) = delete; | |
| LoopAnalyzer &operator=(const LoopAnalyzer &) = delete; | |
| void computeLoopNestDepth(); | |
| using IndexT = uint32_t; | |
| static constexpr IndexT UndefinedIndex = 0; | |
| static constexpr IndexT FirstDefinedIndex = 1; | |
| // TODO(ascull): classify the other fields | |
| class LoopNode { | |
| LoopNode() = delete; | |
| LoopNode operator=(const LoopNode &) = delete; | |
| public: | |
| explicit LoopNode(CfgNode *BB) : BB(BB) { reset(); } | |
| LoopNode(const LoopNode &) = default; | |
| void reset(); | |
| NodeList::const_iterator successorsEnd() const; | |
| NodeList::const_iterator currentSuccessor() const { return Succ; } | |
| void nextSuccessor() { ++Succ; } | |
| void visit(IndexT VisitIndex) { Index = LowLink = VisitIndex; } | |
| bool isVisited() const { return Index != UndefinedIndex; } | |
| IndexT getIndex() const { return Index; } | |
| void tryLink(IndexT NewLink) { | |
| if (NewLink < LowLink) | |
| LowLink = NewLink; | |
| } | |
| IndexT getLowLink() const { return LowLink; } | |
| void setOnStack(bool NewValue = true) { OnStack = NewValue; } | |
| bool isOnStack() const { return OnStack; } | |
| void setDeleted() { Deleted = true; } | |
| bool isDeleted() const { return Deleted; } | |
| void incrementLoopNestDepth(); | |
| bool hasSelfEdge() const; | |
| CfgNode *getNode() { return BB; } | |
| private: | |
| CfgNode *BB; | |
| NodeList::const_iterator Succ; | |
| IndexT Index; | |
| IndexT LowLink; | |
| bool OnStack; | |
| bool Deleted = false; | |
| }; | |
| using LoopNodeList = CfgVector<LoopNode>; | |
| using LoopNodePtrList = CfgVector<LoopNode *>; | |
| /// Process the node as part as part of Tarjan's algorithm and return either a | |
| /// node to recurse into or nullptr when the node has been fully processed. | |
| LoopNode *processNode(LoopNode &Node); | |
| /// The function to analyze for loops. | |
| Cfg *const Func; | |
| /// A list of decorated nodes in the same order as Func->getNodes() which | |
| /// means the node's index will also be valid in this list. | |
| LoopNodeList AllNodes; | |
| /// This is used as a replacement for the call stack. | |
| LoopNodePtrList WorkStack; | |
| /// Track which loop a node belongs to. | |
| LoopNodePtrList LoopStack; | |
| /// The index to assign to the next visited node. | |
| IndexT NextIndex = FirstDefinedIndex; | |
| /// The number of nodes which have been marked deleted. This is used to track | |
| /// when the iteration should end. | |
| LoopNodePtrList::size_type NumDeletedNodes = 0; | |
| /// All the Loops, in descending order of size | |
| CfgVector<CfgUnorderedSet<SizeT>> Loops; | |
| }; | |
| void LoopAnalyzer::LoopNode::reset() { | |
| if (Deleted) | |
| return; | |
| Succ = BB->getOutEdges().begin(); | |
| Index = LowLink = UndefinedIndex; | |
| OnStack = false; | |
| } | |
| NodeList::const_iterator LoopAnalyzer::LoopNode::successorsEnd() const { | |
| return BB->getOutEdges().end(); | |
| } | |
| void LoopAnalyzer::LoopNode::incrementLoopNestDepth() { | |
| BB->incrementLoopNestDepth(); | |
| } | |
| bool LoopAnalyzer::LoopNode::hasSelfEdge() const { | |
| for (CfgNode *Succ : BB->getOutEdges()) { | |
| if (Succ == BB) | |
| return true; | |
| } | |
| return false; | |
| } | |
| LoopAnalyzer::LoopAnalyzer(Cfg *Fn) : Func(Fn) { | |
| const NodeList &Nodes = Func->getNodes(); | |
| // Allocate memory ahead of time. This is why a vector is used instead of a | |
| // stack which doesn't support reserving (or bulk erasure used below). | |
| AllNodes.reserve(Nodes.size()); | |
| WorkStack.reserve(Nodes.size()); | |
| LoopStack.reserve(Nodes.size()); | |
| // Create the LoopNodes from the function's CFG | |
| for (CfgNode *Node : Nodes) | |
| AllNodes.emplace_back(Node); | |
| computeLoopNestDepth(); | |
| } | |
| void LoopAnalyzer::computeLoopNestDepth() { | |
| assert(AllNodes.size() == Func->getNodes().size()); | |
| assert(NextIndex == FirstDefinedIndex); | |
| assert(NumDeletedNodes == 0); | |
| while (NumDeletedNodes < AllNodes.size()) { | |
| // Prepare to run Tarjan's | |
| for (LoopNode &Node : AllNodes) | |
| Node.reset(); | |
| assert(WorkStack.empty()); | |
| assert(LoopStack.empty()); | |
| for (LoopNode &Node : AllNodes) { | |
| if (Node.isDeleted() || Node.isVisited()) | |
| continue; | |
| WorkStack.push_back(&Node); | |
| while (!WorkStack.empty()) { | |
| LoopNode &WorkNode = *WorkStack.back(); | |
| if (LoopNode *Succ = processNode(WorkNode)) | |
| WorkStack.push_back(Succ); | |
| else | |
| WorkStack.pop_back(); | |
| } | |
| } | |
| } | |
| } | |
| LoopAnalyzer::LoopNode * | |
| LoopAnalyzer::processNode(LoopAnalyzer::LoopNode &Node) { | |
| if (!Node.isVisited()) { | |
| Node.visit(NextIndex++); | |
| LoopStack.push_back(&Node); | |
| Node.setOnStack(); | |
| } else { | |
| // Returning to a node after having recursed into Succ so continue | |
| // iterating through successors after using the Succ.LowLink value that was | |
| // computed in the recursion. | |
| LoopNode &Succ = AllNodes[(*Node.currentSuccessor())->getIndex()]; | |
| Node.tryLink(Succ.getLowLink()); | |
| Node.nextSuccessor(); | |
| } | |
| // Visit the successors and recurse into unvisited nodes. The recursion could | |
| // cause the iteration to be suspended but it will resume as the stack is | |
| // unwound. | |
| auto SuccEnd = Node.successorsEnd(); | |
| for (; Node.currentSuccessor() != SuccEnd; Node.nextSuccessor()) { | |
| LoopNode &Succ = AllNodes[(*Node.currentSuccessor())->getIndex()]; | |
| if (Succ.isDeleted()) | |
| continue; | |
| if (!Succ.isVisited()) | |
| return &Succ; | |
| else if (Succ.isOnStack()) | |
| Node.tryLink(Succ.getIndex()); | |
| } | |
| if (Node.getLowLink() != Node.getIndex()) | |
| return nullptr; | |
| // Single node means no loop in the CFG | |
| if (LoopStack.back() == &Node) { | |
| LoopStack.back()->setOnStack(false); | |
| if (Node.hasSelfEdge()) | |
| LoopStack.back()->incrementLoopNestDepth(); | |
| LoopStack.back()->setDeleted(); | |
| ++NumDeletedNodes; | |
| LoopStack.pop_back(); | |
| return nullptr; | |
| } | |
| // Reaching here means a loop has been found! It consists of the nodes on the | |
| // top of the stack, down until the current node being processed, Node, is | |
| // found. | |
| for (auto It = LoopStack.rbegin(); It != LoopStack.rend(); ++It) { | |
| (*It)->setOnStack(false); | |
| (*It)->incrementLoopNestDepth(); | |
| // Remove the loop from the stack and delete the head node | |
| if (*It == &Node) { | |
| (*It)->setDeleted(); | |
| ++NumDeletedNodes; | |
| CfgUnorderedSet<SizeT> LoopNodes; | |
| for (auto LoopIter = It.base() - 1; LoopIter != LoopStack.end(); | |
| ++LoopIter) { | |
| LoopNodes.insert((*LoopIter)->getNode()->getIndex()); | |
| } | |
| Loops.push_back(LoopNodes); | |
| LoopStack.erase(It.base() - 1, LoopStack.end()); | |
| break; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| CfgVector<Loop> ComputeLoopInfo(Cfg *Func) { | |
| auto LoopBodies = LoopAnalyzer(Func).getLoopBodies(); | |
| CfgVector<Loop> Loops; | |
| Loops.reserve(LoopBodies.size()); | |
| std::sort( | |
| LoopBodies.begin(), LoopBodies.end(), | |
| [](const CfgUnorderedSet<SizeT> &A, const CfgUnorderedSet<SizeT> &B) { | |
| return A.size() > B.size(); | |
| }); | |
| for (auto &LoopBody : LoopBodies) { | |
| CfgNode *Header = nullptr; | |
| bool IsSimpleLoop = true; | |
| for (auto NodeIndex : LoopBody) { | |
| CfgNode *Cur = Func->getNodes()[NodeIndex]; | |
| for (auto *Prev : Cur->getInEdges()) { | |
| if (LoopBody.find(Prev->getIndex()) == | |
| LoopBody.end()) { // coming from outside | |
| if (Header == nullptr) { | |
| Header = Cur; | |
| } else { | |
| Header = nullptr; | |
| IsSimpleLoop = false; | |
| break; | |
| } | |
| } | |
| } | |
| if (!IsSimpleLoop) { | |
| break; | |
| } | |
| } | |
| if (!IsSimpleLoop) | |
| continue; // To next potential loop | |
| CfgNode *PreHeader = nullptr; | |
| for (auto *Prev : Header->getInEdges()) { | |
| if (LoopBody.find(Prev->getIndex()) == LoopBody.end()) { | |
| if (PreHeader == nullptr) { | |
| PreHeader = Prev; | |
| } else { | |
| PreHeader = nullptr; | |
| break; | |
| } | |
| } | |
| } | |
| Loops.emplace_back(Header, PreHeader, LoopBody); | |
| } | |
| return Loops; | |
| } | |
| } // end of namespace Ice |