//===- 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