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