[llvm] r207098 - [LCG] Incorporate the core trick of improvements on the naive Tarjan's

Chandler Carruth chandlerc at gmail.com
Thu Apr 24 04:05:21 PDT 2014


Author: chandlerc
Date: Thu Apr 24 06:05:20 2014
New Revision: 207098

URL: http://llvm.org/viewvc/llvm-project?rev=207098&view=rev
Log:
[LCG] Incorporate the core trick of improvements on the naive Tarjan's
algorithm here: http://dl.acm.org/citation.cfm?id=177301.

The idea of isolating the roots has even more relevance when using the
stack not just to implement the DFS but also to implement the recursive
step. Because we use it for the recursive step, to isolate the roots we
need to maintain two stacks: one for our recursive DFS walk, and another
of the nodes that have been walked. The nice thing is that the latter
will be half the size. It also fixes a complete hack where we scanned
backwards over the stack to find the next potential-root to continue
processing. Now that is always the top of the DFS stack.

While this is a really nice improvement already (IMO) it further opens
the door for two important simplifications:

1) De-duplicating some of the code across the two different walks. I've
   actually made the duplication a bit worse in some senses with this
   patch because the two are starting to converge.
2) Dramatically simplifying the loop structures of both walks.

I wanted to do those separately as they'll be essentially *just* CFG
restructuring. This patch on the other hand actually uses different
datastructures to implement the algorithm itself.

Modified:
    llvm/trunk/include/llvm/Analysis/LazyCallGraph.h
    llvm/trunk/lib/Analysis/LazyCallGraph.cpp

Modified: llvm/trunk/include/llvm/Analysis/LazyCallGraph.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Analysis/LazyCallGraph.h?rev=207098&r1=207097&r2=207098&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Analysis/LazyCallGraph.h (original)
+++ llvm/trunk/include/llvm/Analysis/LazyCallGraph.h Thu Apr 24 06:05:20 2014
@@ -370,12 +370,15 @@ private:
   /// These are all of the SCCs which have no children.
   SmallVector<SCC *, 4> LeafSCCs;
 
-  /// \brief Stack of nodes not-yet-processed into SCCs.
+  /// \brief Stack of nodes in the DFS walk.
   SmallVector<std::pair<Node *, iterator>, 4> DFSStack;
 
   /// \brief Set of entry nodes not-yet-processed into SCCs.
   SmallSetVector<Function *, 4> SCCEntryNodes;
 
+  /// \brief Stack of nodes the DFS has walked but not yet put into a SCC.
+  SmallVector<Node *, 4> PendingSCCStack;
+
   /// \brief Counter for the next DFS number to assign.
   int NextDFSNumber;
 
@@ -388,9 +391,7 @@ private:
 
   /// \brief Helper to form a new SCC out of the top of a DFSStack-like
   /// structure.
-  SCC *formSCCFromDFSStack(
-      SmallVectorImpl<std::pair<Node *, Node::iterator>> &DFSStack,
-      SmallVectorImpl<std::pair<Node *, Node::iterator>>::iterator SCCBegin);
+  SCC *formSCC(Node *RootN, SmallVectorImpl<Node *> &NodeStack);
 
   /// \brief Retrieve the next node in the post-order SCC walk of the call graph.
   SCC *getNextSCCInPostOrder();

Modified: llvm/trunk/lib/Analysis/LazyCallGraph.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/LazyCallGraph.cpp?rev=207098&r1=207097&r2=207098&view=diff
==============================================================================
--- llvm/trunk/lib/Analysis/LazyCallGraph.cpp (original)
+++ llvm/trunk/lib/Analysis/LazyCallGraph.cpp Thu Apr 24 06:05:20 2014
@@ -185,6 +185,7 @@ LazyCallGraph::SCC::removeInternalEdge(L
   // We're going to do a full mini-Tarjan's walk using a local stack here.
   int NextDFSNumber;
   SmallVector<std::pair<Node *, Node::iterator>, 4> DFSStack;
+  SmallVector<Node *, 4> PendingSCCStack;
 
   // The worklist is every node in the original SCC. FIXME: switch the SCC to
   // use a SmallSetVector and swap here.
@@ -213,18 +214,20 @@ LazyCallGraph::SCC::removeInternalEdge(L
       N->LowLink = N->DFSNumber = 1;
       NextDFSNumber = 2;
       DFSStack.push_back(std::make_pair(N, N->begin()));
+      assert(PendingSCCStack.empty() && "Cannot start a fresh DFS walk with "
+                                        "pending nodes from a prior walk.");
     }
 
-    Node *N = DFSStack.back().first;
+    // We simulate recursion by popping out of all the nested loops and
+    // continuing.
+    bool Recurse = false;
 
+    do {
+    Node *N = DFSStack.back().first;
     assert(N->DFSNumber != 0 && "We should always assign a DFS number "
                                 "before placing a node onto the stack.");
 
-    auto SI = DFSStack.rbegin();
-    bool PushedChildNode = false;
-    do {
-      N = SI->first;
-      for (auto I = SI->second, E = N->end(); I != E; ++I) {
+      for (auto I = DFSStack.back().second, E = N->end(); I != E; ++I) {
         Node &ChildN = *I;
         // If this child isn't currently in this SCC, no need to process it.
         // However, we do need to remove this SCC from its SCC's parent set.
@@ -237,22 +240,25 @@ LazyCallGraph::SCC::removeInternalEdge(L
         // Check if we have reached a node in the new (known connected) set. If
         // so, the entire stack is necessarily in that set and we can re-start.
         if (NewNodes.count(&ChildN)) {
+          while (!PendingSCCStack.empty())
+            NewNodes.insert(PendingSCCStack.pop_back_val());
           while (!DFSStack.empty())
             NewNodes.insert(DFSStack.pop_back_val().first);
-          continue;
+          Recurse = true;
+          break;
         }
 
         if (ChildN.DFSNumber == 0) {
           // Mark that we should start at this child when next this node is the
           // top of the stack. We don't start at the next child to ensure this
           // child's lowlink is reflected.
-          SI->second = I;
+          DFSStack.back().second = I;
 
           // Recurse onto this node via a tail call.
           ChildN.LowLink = ChildN.DFSNumber = NextDFSNumber++;
           Worklist.remove(&ChildN);
           DFSStack.push_back(std::make_pair(&ChildN, ChildN.begin()));
-          PushedChildNode = true;
+          Recurse = true;
           break;
         }
 
@@ -263,21 +269,28 @@ LazyCallGraph::SCC::removeInternalEdge(L
         if (ChildN.LowLink >= 0 && ChildN.LowLink < N->LowLink)
           N->LowLink = ChildN.LowLink;
       }
-      if (!PushedChildNode)
-        // No more children to process for this stack entry.
-        SI->second = N->end();
-
-      ++SI;
-      // If nothing is new on the stack and this isn't the SCC root, walk
-      // upward.
-    } while (!PushedChildNode && N->LowLink != N->DFSNumber &&
-             SI != DFSStack.rend());
+      if (Recurse)
+        break;
+
+      // No more children to process, pop it off the core DFS stack.
+      DFSStack.pop_back();
+
+      if (N->LowLink == N->DFSNumber) {
+        ResultSCCs.push_back(G.formSCC(N, PendingSCCStack));
+        break;
+      }
+
+      assert(!DFSStack.empty() && "We shouldn't have an empty stack!");
 
-    if (DFSStack.empty() || PushedChildNode)
-      continue;
+      // At this point we know that N cannot ever be an SCC root. Its low-link
+      // is not its dfs-number, and we've processed all of its children. It is
+      // just sitting here waiting until some node further down the stack gets
+      // low-link == dfs-number and pops it off as well. Move it to the pending
+      // stack which is pulled into the next SCC to be formed.
+      PendingSCCStack.push_back(N);
+    } while (!DFSStack.empty());
 
-    // Form the new SCC out of the top of the DFS stack.
-    ResultSCCs.push_back(G.formSCCFromDFSStack(DFSStack, SI.base()));
+    // We reach here when we're going to "recurse".
   }
 
   // Replace this SCC with the NewNodes we collected above.
@@ -377,23 +390,26 @@ void LazyCallGraph::updateGraphPtrs() {
   }
 }
 
-LazyCallGraph::SCC *LazyCallGraph::formSCCFromDFSStack(
-    SmallVectorImpl<std::pair<Node *, Node::iterator>> &DFSStack,
-    SmallVectorImpl<std::pair<Node *, Node::iterator>>::iterator SCCBegin) {
+LazyCallGraph::SCC *LazyCallGraph::formSCC(Node *RootN,
+                                           SmallVectorImpl<Node *> &NodeStack) {
   // The tail of the stack is the new SCC. Allocate the SCC and pop the stack
   // into it.
   SCC *NewSCC = new (SCCBPA.Allocate()) SCC();
 
-  for (auto I = SCCBegin, E = DFSStack.end(); I != E; ++I) {
-    Node *SCCN = I->first;
-    assert(SCCN->LowLink >= SCCBegin->first->LowLink &&
+  SCCMap[RootN] = NewSCC;
+  NewSCC->Nodes.push_back(RootN);
+
+  while (!NodeStack.empty() && NodeStack.back()->DFSNumber > RootN->DFSNumber) {
+    Node *SCCN = NodeStack.pop_back_val();
+    assert(SCCN->LowLink >= RootN->LowLink &&
            "We cannot have a low link in an SCC lower than its root on the "
            "stack!");
+    SCCN->DFSNumber = SCCN->LowLink = -1;
 
     SCCMap[SCCN] = NewSCC;
     NewSCC->Nodes.push_back(SCCN);
   }
-  DFSStack.erase(SCCBegin, DFSStack.end());
+  RootN->DFSNumber = RootN->LowLink = -1;
 
   // A final pass over all edges in the SCC (this remains linear as we only
   // do this once when we build the SCC) to connect it to the parent sets of
@@ -428,19 +444,18 @@ LazyCallGraph::SCC *LazyCallGraph::getNe
     DFSStack.push_back(std::make_pair(&N, N.begin()));
   }
 
-  auto SI = DFSStack.rbegin();
-  assert(SI->first->DFSNumber != 0 && "We should always assign a DFS number "
-                                      "before placing a node onto the stack.");
-
   do {
-    Node *N = SI->first;
-    for (auto I = SI->second, E = N->end(); I != E; ++I) {
+    Node *N = DFSStack.back().first;
+    assert(N->DFSNumber != 0 && "We should always assign a DFS number "
+                                "before placing a node onto the stack.");
+
+    for (auto I = DFSStack.back().second, E = N->end(); I != E; ++I) {
       Node &ChildN = *I;
       if (ChildN.DFSNumber == 0) {
         // Mark that we should start at this child when next this node is the
         // top of the stack. We don't start at the next child to ensure this
         // child's lowlink is reflected.
-        SI->second = I;
+        DFSStack.back().second = I;
 
         // Recurse onto this node via a tail call.
         assert(!SCCMap.count(&ChildN) &&
@@ -457,15 +472,20 @@ LazyCallGraph::SCC *LazyCallGraph::getNe
       if (ChildN.LowLink >= 0 && ChildN.LowLink < N->LowLink)
         N->LowLink = ChildN.LowLink;
     }
-    // No more children to process for this stack entry.
-    SI->second = N->end();
+    // No more children to process here, pop the node off the stack.
+    DFSStack.pop_back();
 
     if (N->LowLink == N->DFSNumber)
       // Form the new SCC out of the top of the DFS stack.
-      return formSCCFromDFSStack(DFSStack, std::prev(SI.base()));
+      return formSCC(N, PendingSCCStack);
 
-    ++SI;
-  } while (SI != DFSStack.rend());
+    // At this point we know that N cannot ever be an SCC root. Its low-link
+    // is not its dfs-number, and we've processed all of its children. It is
+    // just sitting here waiting until some node further down the stack gets
+    // low-link == dfs-number and pops it off as well. Move it to the pending
+    // stack which is pulled into the next SCC to be formed.
+    PendingSCCStack.push_back(N);
+  } while (!DFSStack.empty());
 
   llvm_unreachable(
       "We cannot reach the bottom of the stack without popping an SCC.");





More information about the llvm-commits mailing list