]> granicus.if.org Git - clang/commitdiff
[analyzer] Re-apply r170826 and make the dumping of the GallGraph
authorAnna Zaks <ganna@apple.com>
Fri, 21 Dec 2012 17:27:01 +0000 (17:27 +0000)
committerAnna Zaks <ganna@apple.com>
Fri, 21 Dec 2012 17:27:01 +0000 (17:27 +0000)
deterministic.

Commit message for r170826:

[analyzer] Traverse the Call Graph in topological order.

Modify the call graph by removing the parentless nodes. Instead all
nodes are children of root to ensure they are all reachable. Remove the
tracking of nodes that are "top level" or global. This information is
not used and can be obtained from the Decls stored inside
CallGraphNodes.

Instead of existing ordering hacks, analyze the functions in topological
order over the Call Graph.

Together with the addition of devirtualizable ObjC message sends and
blocks to the call graph, this gives around 6% performance improvement
on several large ObjC benchmarks.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@170906 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Analysis/CallGraph.h
lib/Analysis/CallGraph.cpp
lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
test/Analysis/debug-CallGraph.c

index d554f3a7af321a79755fc46dadab36a18c950698..998076d1e16a2e3559c9d18d6394783dd4f0d0c7 100644 (file)
@@ -39,15 +39,9 @@ class CallGraph : public RecursiveASTVisitor<CallGraph> {
   /// FunctionMap owns all CallGraphNodes.
   FunctionMapTy FunctionMap;
 
-  /// This is a virtual root node that has edges to all the global functions -
-  /// 'main' or functions accessible from other translation units.
+  /// This is a virtual root node that has edges to all the functions.
   CallGraphNode *Root;
 
-  /// The list of nodes that have no parent. These are unreachable from Root.
-  /// Declarations can get to this list due to impressions in the graph, for
-  /// example, we do not track functions whose addresses were taken.
-  llvm::SetVector<CallGraphNode *> ParentlessNodes;
-
 public:
   CallGraph();
   ~CallGraph();
@@ -91,12 +85,6 @@ public:
   /// failing to add a call edge due to the analysis imprecision.
   typedef llvm::SetVector<CallGraphNode *>::iterator nodes_iterator;
   typedef llvm::SetVector<CallGraphNode *>::const_iterator const_nodes_iterator;
-  nodes_iterator parentless_begin() { return ParentlessNodes.begin(); }
-  nodes_iterator parentless_end() { return ParentlessNodes.end(); }
-  const_nodes_iterator
-    parentless_begin() const { return ParentlessNodes.begin(); }
-  const_nodes_iterator
-    parentless_end() const { return ParentlessNodes.end(); }
 
   void print(raw_ostream &os) const;
   void dump() const;
@@ -170,7 +158,6 @@ public:
 
   void addCallee(CallGraphNode *N, CallGraph *CG) {
     CalledFunctions.push_back(N);
-    CG->ParentlessNodes.remove(N);
   }
 
   Decl *getDecl() const { return FD; }
@@ -206,7 +193,7 @@ template <> struct GraphTraits<const clang::CallGraphNode*> {
   typedef NodeType::const_iterator ChildIteratorType;
   static NodeType *getEntryNode(const clang::CallGraphNode *CGN) { return CGN; }
   static inline ChildIteratorType child_begin(NodeType *N) { return N->begin();}
-  static inline ChildIteratorType child_end  (NodeType *N) { return N->end(); }
+  static inline ChildIteratorType child_end(NodeType *N) { return N->end(); }
 };
 
 template <> struct GraphTraits<clang::CallGraph*>
index 424e5c795e26d768f8eaab677ac6803dc87f9329..4082e2dc0ea0cbe392b557bfadfc7a06a00c17c5 100644 (file)
@@ -16,6 +16,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/StmtVisitor.h"
+#include "llvm/ADT/PostOrderIterator.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/Support/GraphWriter.h"
 
@@ -141,14 +142,8 @@ bool CallGraph::includeInGraph(const Decl *D) {
 void CallGraph::addNodeForDecl(Decl* D, bool IsGlobal) {
   assert(D);
 
-  // Do nothing if the node already exists.
-  if (FunctionMap.find(D) != FunctionMap.end())
-    return;
-
   // Allocate a new node, mark it as root, and process it's calls.
   CallGraphNode *Node = getOrInsertNode(D);
-  if (IsGlobal)
-    Root->addCallee(Node, this);
 
   // Process all the calls by this function as well.
   CGBuilder builder(this, Node);
@@ -168,23 +163,31 @@ CallGraphNode *CallGraph::getOrInsertNode(Decl *F) {
     return Node;
 
   Node = new CallGraphNode(F);
-  // If not root, add to the parentless list.
+  // Make Root node a parent of all functions to make sure all are reachable.
   if (F != 0)
-    ParentlessNodes.insert(Node);
+    Root->addCallee(Node, this);
   return Node;
 }
 
 void CallGraph::print(raw_ostream &OS) const {
   OS << " --- Call graph Dump --- \n";
-  for (const_iterator I = begin(), E = end(); I != E; ++I) {
+
+  // We are going to print the graph in reverse post order, partially, to make
+  // sure the output is deterministic.
+  llvm::ReversePostOrderTraversal<const clang::CallGraph*> RPOT(this);
+  for (llvm::ReversePostOrderTraversal<const clang::CallGraph*>::rpo_iterator
+         I = RPOT.begin(), E = RPOT.end(); I != E; ++I) {
+    const CallGraphNode *N = *I;
+
     OS << "  Function: ";
-    if (I->second == Root)
+    if (N == Root)
       OS << "< root >";
     else
-      I->second->print(OS);
+      N->print(OS);
+
     OS << " calls: ";
-    for (CallGraphNode::iterator CI = I->second->begin(),
-        CE = I->second->end(); CI != CE; ++CI) {
+    for (CallGraphNode::const_iterator CI = N->begin(),
+                                       CE = N->end(); CI != CE; ++CI) {
       assert(*CI != Root && "No one can call the root node.");
       (*CI)->print(OS);
       OS << " ";
index 025891adf8e531e4329be226181a86f504826558..07aa5b074ae04f21f34b7ab53ecc26dce6364d1d 100644 (file)
@@ -39,6 +39,7 @@
 #include "llvm/ADT/OwningPtr.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/Statistic.h"
+#include "llvm/ADT/PostOrderIterator.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Program.h"
 #include "llvm/Support/Timer.h"
@@ -417,61 +418,34 @@ AnalysisConsumer::getInliningModeForFunction(const Decl *D,
 }
 
 void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) {
-  // Otherwise, use the Callgraph to derive the order.
-  // Build the Call Graph.
-  CallGraph CG;
-
-  // Add all the top level declarations to the graph.
+  // Build the Call Graph by adding all the top level declarations to the graph.
   // Note: CallGraph can trigger deserialization of more items from a pch
   // (though HandleInterestingDecl); triggering additions to LocalTUDecls.
   // We rely on random access to add the initially processed Decls to CG.
+  CallGraph CG;
   for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) {
     CG.addToCallGraph(LocalTUDecls[i]);
   }
 
-  // Find the top level nodes - children of root + the unreachable (parentless)
-  // nodes.
-  llvm::SmallVector<CallGraphNode*, 24> TopLevelFunctions;
-  for (CallGraph::nodes_iterator TI = CG.parentless_begin(),
-                                 TE = CG.parentless_end(); TI != TE; ++TI) {
-    TopLevelFunctions.push_back(*TI);
-    NumFunctionTopLevel++;
-  }
-  CallGraphNode *Entry = CG.getRoot();
-  for (CallGraphNode::iterator I = Entry->begin(),
-                               E = Entry->end(); I != E; ++I) {
-    TopLevelFunctions.push_back(*I);
-    NumFunctionTopLevel++;
-  }
-
-  // Make sure the nodes are sorted in order reverse of their definition in the 
-  // translation unit. This step is very important for performance. It ensures 
-  // that we analyze the root functions before the externally available 
-  // subroutines.
-  std::deque<CallGraphNode*> BFSQueue;
-  for (llvm::SmallVector<CallGraphNode*, 24>::reverse_iterator
-         TI = TopLevelFunctions.rbegin(), TE = TopLevelFunctions.rend();
-         TI != TE; ++TI)
-    BFSQueue.push_back(*TI);
-
-  // BFS over all of the functions, while skipping the ones inlined into
-  // the previously processed functions. Use external Visited set, which is
-  // also modified when we inline a function.
+  // Walk over all of the call graph nodes in topological order, so that we
+  // analyze parents before the children. Skip the functions inlined into
+  // the previously processed functions. Use external Visited set to identify
+  // inlined functions. The topological order allows the "do not reanalyze
+  // previously inlined function" performance heuristic to be triggered more
+  // often.
   SetOfConstDecls Visited;
   SetOfConstDecls VisitedAsTopLevel;
-  while(!BFSQueue.empty()) {
-    CallGraphNode *N = BFSQueue.front();
-    BFSQueue.pop_front();
-
-    // Push the children into the queue.
-    for (CallGraphNode::const_iterator CI = N->begin(),
-         CE = N->end(); CI != CE; ++CI) {
-      if (!shouldSkipFunction((*CI)->getDecl(), Visited, VisitedAsTopLevel))
-        BFSQueue.push_back(*CI);
-    }
+  llvm::ReversePostOrderTraversal<clang::CallGraph*> RPOT(&CG);
+  for (llvm::ReversePostOrderTraversal<clang::CallGraph*>::rpo_iterator
+         I = RPOT.begin(), E = RPOT.end(); I != E; ++I) {
+    NumFunctionTopLevel++;
 
+    CallGraphNode *N = *I;
     Decl *D = N->getDecl();
-    assert(D);
+    
+    // Skip the abstract root node.
+    if (!D)
+      continue;
 
     // Skip the functions which have been processed already or previously
     // inlined.
index b7c7c8a8447d73b34b8e7ae4e7e814be0affc0f5..4523c789351b337622dc33b37de3827f039b0d4d 100644 (file)
@@ -1,9 +1,9 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=debug.DumpCallGraph %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=debug.DumpCallGraph %s -fblocks 2>&1 | FileCheck %s
 
 static void mmm(int y) {
   if (y != 0)
       y++;
-  y = y/0;
+  y = y/y;
 }
 
 static int foo(int x, int y) {
@@ -17,5 +17,17 @@ void aaa() {
   foo(1,2);
 }
 
+void bbb(int y) {
+  int x = (y > 2);
+  ^ {
+      foo(x, y);
+  }();
+}
+
 // CHECK:--- Call graph Dump ---
-// CHECK: Function: < root > calls: aaa
+// CHECK: Function: < root > calls: mmm foo aaa < > bbb
+// CHECK: Function: bbb calls: < >
+// CHECK: Function: < > calls: foo
+// CHECK: Function: aaa calls: foo
+// CHECK: Function: foo calls: mmm
+// CHECK: Function: mmm calls: