[cfe-commits] r154625 - in /cfe/trunk: include/clang/Analysis/CallGraph.h include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h lib/Analysis/CallGraph.cpp lib/StaticAnalyzer/Core/ExprEngine.cpp lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp test/Analysis/check-deserialization.cpp test/Analysis/objc-method-coverage.m
Anna Zaks
ganna at apple.com
Thu Apr 12 15:36:49 PDT 2012
Author: zaks
Date: Thu Apr 12 17:36:48 2012
New Revision: 154625
URL: http://llvm.org/viewvc/llvm-project?rev=154625&view=rev
Log:
[analyzer] PCH deserialization optimization.
We should not deserialize unused declarations from the PCH file. Achieve
this by storing the top level declarations during parsing
(HandleTopLevelDecl ASTConsumer callback) and analyzing/building a call
graph only for those.
Tested the patch on a sample ObjC file that uses PCH. With the patch,
the analyzes is 17.5% faster and clang consumes 40% less memory.
Got about 10% overall build/analyzes time decrease on a large Objective
C project.
A bit of CallGraph refactoring/cleanup as well..
Added:
cfe/trunk/test/Analysis/check-deserialization.cpp
cfe/trunk/test/Analysis/objc-method-coverage.m
Modified:
cfe/trunk/include/clang/Analysis/CallGraph.h
cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h
cfe/trunk/lib/Analysis/CallGraph.cpp
cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
cfe/trunk/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
Modified: cfe/trunk/include/clang/Analysis/CallGraph.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/CallGraph.h?rev=154625&r1=154624&r2=154625&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/CallGraph.h (original)
+++ cfe/trunk/include/clang/Analysis/CallGraph.h Thu Apr 12 17:36:48 2012
@@ -18,6 +18,7 @@
#define LLVM_CLANG_ANALYSIS_CALLGRAPH
#include "clang/AST/DeclBase.h"
+#include "clang/AST/RecursiveASTVisitor.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/GraphTraits.h"
#include "llvm/ADT/SetVector.h"
@@ -25,8 +26,14 @@
namespace clang {
class CallGraphNode;
-class CallGraph {
+/// \class The AST-based call graph.
+///
+/// The call graph extends itself with the given declarations by implementing
+/// the recursive AST visitor, which constructs the graph by visiting the given
+/// declarations.
+class CallGraph : public RecursiveASTVisitor<CallGraph> {
friend class CallGraphNode;
+
typedef llvm::DenseMap<const Decl *, CallGraphNode *> FunctionMapTy;
/// FunctionMap owns all CallGraphNodes.
@@ -45,19 +52,23 @@
CallGraph();
~CallGraph();
- /// \brief Add the given declaration to the call graph.
- void addToCallGraph(Decl *D, bool IsGlobal);
+ /// \brief Populate the call graph with the functions in the given
+ /// declaration.
+ ///
+ /// Recursively walks the declaration to find all the dependent Decls as well.
+ void addToCallGraph(Decl *D) {
+ TraverseDecl(D);
+ }
- /// \brief Populate the call graph with the functions in the given translation
- /// unit.
- void addToCallGraph(TranslationUnitDecl *TU);
+ /// \brief Determine if a declaration should be included in the graph.
+ static bool includeInGraph(const Decl *D);
/// \brief Lookup the node for the given declaration.
CallGraphNode *getNode(const Decl *) const;
/// \brief Lookup the node for the given declaration. If none found, insert
/// one into the graph.
- CallGraphNode *getOrInsertFunction(Decl *);
+ CallGraphNode *getOrInsertNode(Decl *);
/// Iterators through all the elements in the graph. Note, this gives
/// non-deterministic order.
@@ -90,6 +101,32 @@
void print(raw_ostream &os) const;
void dump() const;
void viewGraph() const;
+
+ /// Part of recursive declaration visitation.
+ bool VisitFunctionDecl(FunctionDecl *FD) {
+ // We skip function template definitions, as their semantics is
+ // only determined when they are instantiated.
+ if (includeInGraph(FD))
+ // If this function has external linkage, anything could call it.
+ // Note, we are not precise here. For example, the function could have
+ // its address taken.
+ addNodeForDecl(FD, FD->isGlobal());
+ return true;
+ }
+
+ /// Part of recursive declaration visitation.
+ bool VisitObjCMethodDecl(ObjCMethodDecl *MD) {
+ if (includeInGraph(MD))
+ addNodeForDecl(MD, true);
+ return true;
+ }
+
+private:
+ /// \brief Add the given declaration to the call graph.
+ void addNodeForDecl(Decl *D, bool IsGlobal);
+
+ /// \brief Allocate a new node in the graph.
+ CallGraphNode *allocateNewNode(Decl *);
};
class CallGraphNode {
Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h?rev=154625&r1=154624&r2=154625&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h Thu Apr 12 17:36:48 2012
@@ -82,7 +82,7 @@
/// The functions which have been analyzed through inlining. This is owned by
/// AnalysisConsumer. It can be null.
- SetOfDecls *AnalyzedCallees;
+ SetOfConstDecls *AnalyzedCallees;
/// The information about functions shared by the whole translation unit.
/// (This data is owned by AnalysisConsumer.)
@@ -109,7 +109,7 @@
public:
/// Construct a CoreEngine object to analyze the provided CFG using
/// a DFS exploration of the exploded graph.
- CoreEngine(SubEngine& subengine, SetOfDecls *VisitedCallees,
+ CoreEngine(SubEngine& subengine, SetOfConstDecls *VisitedCallees,
FunctionSummariesTy *FS)
: SubEng(subengine), G(new ExplodedGraph()),
WList(WorkList::makeBFS()),
Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h?rev=154625&r1=154624&r2=154625&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h Thu Apr 12 17:36:48 2012
@@ -91,7 +91,8 @@
GRBugReporter BR;
public:
- ExprEngine(AnalysisManager &mgr, bool gcEnabled, SetOfDecls *VisitedCallees,
+ ExprEngine(AnalysisManager &mgr, bool gcEnabled,
+ SetOfConstDecls *VisitedCallees,
FunctionSummariesTy *FS);
~ExprEngine();
Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h?rev=154625&r1=154624&r2=154625&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h Thu Apr 12 17:36:48 2012
@@ -21,7 +21,8 @@
namespace clang {
namespace ento {
-typedef llvm::SmallPtrSet<const Decl*,24> SetOfDecls;
+typedef llvm::SmallPtrSet<Decl*, 24> SetOfDecls;
+typedef llvm::SmallPtrSet<const Decl*, 24> SetOfConstDecls;
class FunctionSummariesTy {
struct FunctionSummary {
Modified: cfe/trunk/lib/Analysis/CallGraph.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CallGraph.cpp?rev=154625&r1=154624&r2=154625&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/CallGraph.cpp (original)
+++ cfe/trunk/lib/Analysis/CallGraph.cpp Thu Apr 12 17:36:48 2012
@@ -14,35 +14,12 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
-#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/StmtVisitor.h"
#include "llvm/Support/GraphWriter.h"
using namespace clang;
-/// Determine if a declaration should be included in the graph.
-static bool includeInGraph(const Decl *D) {
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
- // We skip function template definitions, as their semantics is
- // only determined when they are instantiated.
- if (!FD->isThisDeclarationADefinition() ||
- FD->isDependentContext())
- return false;
-
- IdentifierInfo *II = FD->getIdentifier();
- if (II && II->getName().startswith("__inline"))
- return false;
- }
-
- if (const ObjCMethodDecl *ID = dyn_cast<ObjCMethodDecl>(D)) {
- if (!ID->isThisDeclarationADefinition())
- return false;
- }
-
- return true;
-}
-
namespace {
/// A helper class, which walks the AST and locates all the call sites in the
/// given function body.
@@ -60,8 +37,8 @@
void VisitCallExpr(CallExpr *CE) {
// TODO: We need to handle ObjC method calls as well.
if (FunctionDecl *CalleeDecl = CE->getDirectCallee())
- if (includeInGraph(CalleeDecl)) {
- CallGraphNode *CalleeNode = G->getOrInsertFunction(CalleeDecl);
+ if (G->includeInGraph(CalleeDecl)) {
+ CallGraphNode *CalleeNode = G->getOrInsertNode(CalleeDecl);
CallerNode->addCallee(CalleeNode, G);
}
}
@@ -73,38 +50,10 @@
}
};
-/// A helper class which walks the AST declarations.
-// TODO: We might want to specialize the visitor to shrink the call graph.
-// For example, we might not want to include the inline methods from header
-// files.
-class CGDeclVisitor : public RecursiveASTVisitor<CGDeclVisitor> {
- CallGraph *CG;
-
-public:
- CGDeclVisitor(CallGraph * InCG) : CG(InCG) {}
-
- bool VisitFunctionDecl(FunctionDecl *FD) {
- // We skip function template definitions, as their semantics is
- // only determined when they are instantiated.
- if (includeInGraph(FD))
- // If this function has external linkage, anything could call it.
- // Note, we are not precise here. For example, the function could have
- // its address taken.
- CG->addToCallGraph(FD, FD->isGlobal());
- return true;
- }
-
- bool VisitObjCMethodDecl(ObjCMethodDecl *MD) {
- if (includeInGraph(MD))
- CG->addToCallGraph(MD, true);
- return true;
- }
-};
-
} // end anonymous namespace
CallGraph::CallGraph() {
- Root = getOrInsertFunction(0);
+ Root = getOrInsertNode(0);
}
CallGraph::~CallGraph() {
@@ -116,10 +65,36 @@
}
}
-void CallGraph::addToCallGraph(Decl* D, bool IsGlobal) {
+bool CallGraph::includeInGraph(const Decl *D) {
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+ // We skip function template definitions, as their semantics is
+ // only determined when they are instantiated.
+ if (!FD->isThisDeclarationADefinition() ||
+ FD->isDependentContext())
+ return false;
+
+ IdentifierInfo *II = FD->getIdentifier();
+ if (II && II->getName().startswith("__inline"))
+ return false;
+ }
+
+ if (const ObjCMethodDecl *ID = dyn_cast<ObjCMethodDecl>(D)) {
+ if (!ID->isThisDeclarationADefinition())
+ return false;
+ }
+
+ return true;
+}
+
+void CallGraph::addNodeForDecl(Decl* D, bool IsGlobal) {
assert(D);
- CallGraphNode *Node = getOrInsertFunction(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);
@@ -129,17 +104,13 @@
builder.Visit(Body);
}
-void CallGraph::addToCallGraph(TranslationUnitDecl *TU) {
- CGDeclVisitor(this).TraverseDecl(TU);
-}
-
CallGraphNode *CallGraph::getNode(const Decl *F) const {
FunctionMapTy::const_iterator I = FunctionMap.find(F);
if (I == FunctionMap.end()) return 0;
return I->second;
}
-CallGraphNode *CallGraph::getOrInsertFunction(Decl *F) {
+CallGraphNode *CallGraph::getOrInsertNode(Decl *F) {
CallGraphNode *&Node = FunctionMap[F];
if (Node)
return Node;
Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp?rev=154625&r1=154624&r2=154625&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp Thu Apr 12 17:36:48 2012
@@ -67,7 +67,7 @@
//===----------------------------------------------------------------------===//
ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled,
- SetOfDecls *VisitedCallees,
+ SetOfConstDecls *VisitedCallees,
FunctionSummariesTy *FS)
: AMgr(mgr),
AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()),
Modified: cfe/trunk/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp?rev=154625&r1=154624&r2=154625&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp Thu Apr 12 17:36:48 2012
@@ -41,6 +41,7 @@
#include "llvm/Support/Timer.h"
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/OwningPtr.h"
+#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/Statistic.h"
#include <queue>
@@ -95,6 +96,13 @@
AnalyzerOptions Opts;
ArrayRef<std::string> Plugins;
+ /// \brief Stores the declarations from the local translation unit.
+ /// Note, we pre-compute the local declarations at parse time as an
+ /// optimization to make sure we do not deserialize everything from disk.
+ /// The local declaration to all declarations ratio might be very small when
+ /// working with a PCH file.
+ SetOfDecls LocalTUDecls;
+
// PD is owned by AnalysisManager.
PathDiagnosticConsumer *PD;
@@ -214,11 +222,16 @@
Opts.NoRetryExhausted));
}
+ /// \brief Store the top level decls in the set to be processed later on.
+ /// (Doing this pre-processing avoids deserialization of data from PCH.)
+ virtual bool HandleTopLevelDecl(DeclGroupRef D);
+ virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef D);
+
virtual void HandleTranslationUnit(ASTContext &C);
- /// \brief Build the call graph for the TU and use it to define the order
- /// in which the functions should be visited.
- void HandleDeclsGallGraph(TranslationUnitDecl *TU);
+ /// \brief Build the call graph for all the top level decls of this TU and
+ /// use it to define the order in which the functions should be visited.
+ void HandleDeclsGallGraph();
/// \brief Run analyzes(syntax or path sensitive) on the given function.
/// \param Mode - determines if we are requesting syntax only or path
@@ -226,13 +239,12 @@
/// \param VisitedCallees - The output parameter, which is populated with the
/// set of functions which should be considered analyzed after analyzing the
/// given root function.
- void HandleCode(Decl *D, AnalysisMode Mode, SetOfDecls *VisitedCallees = 0);
-
- /// \brief Check if we should skip (not analyze) the given function.
- bool skipFunction(Decl *D);
+ void HandleCode(Decl *D, AnalysisMode Mode,
+ SetOfConstDecls *VisitedCallees = 0);
- void RunPathSensitiveChecks(Decl *D, SetOfDecls *VisitedCallees);
- void ActionExprEngine(Decl *D, bool ObjCGCEnabled, SetOfDecls *VisitedCallees);
+ void RunPathSensitiveChecks(Decl *D, SetOfConstDecls *VisitedCallees);
+ void ActionExprEngine(Decl *D, bool ObjCGCEnabled,
+ SetOfConstDecls *VisitedCallees);
/// Visitors for the RecursiveASTVisitor.
@@ -262,6 +274,13 @@
HandleCode(MD, RecVisitorMode);
return true;
}
+
+private:
+ void storeTopLevelDecls(DeclGroupRef DG);
+
+ /// \brief Check if we should skip (not analyze) the given function.
+ bool skipFunction(Decl *D);
+
};
} // end anonymous namespace
@@ -271,11 +290,35 @@
//===----------------------------------------------------------------------===//
llvm::Timer* AnalysisConsumer::TUTotalTimer = 0;
-void AnalysisConsumer::HandleDeclsGallGraph(TranslationUnitDecl *TU) {
+bool AnalysisConsumer::HandleTopLevelDecl(DeclGroupRef DG) {
+ storeTopLevelDecls(DG);
+ return true;
+}
+
+void AnalysisConsumer::HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) {
+ storeTopLevelDecls(DG);
+}
+
+void AnalysisConsumer::storeTopLevelDecls(DeclGroupRef DG) {
+ for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) {
+
+ // Skip ObjCMethodDecl, wait for the objc container to avoid
+ // analyzing twice.
+ if (isa<ObjCMethodDecl>(*I))
+ continue;
+
+ LocalTUDecls.insert(*I);
+ }
+}
+
+void AnalysisConsumer::HandleDeclsGallGraph() {
// Otherwise, use the Callgraph to derive the order.
// Build the Call Graph.
CallGraph CG;
- CG.addToCallGraph(TU);
+ // Add all the top level declarations to the graph.
+ for (SetOfDecls::iterator I = LocalTUDecls.begin(),
+ E = LocalTUDecls.end(); I != E; ++I)
+ CG.addToCallGraph(*I);
// Find the top level nodes - children of root + the unreachable (parentless)
// nodes.
@@ -316,15 +359,15 @@
continue;
// Analyze the function.
- SetOfDecls VisitedCallees;
+ SetOfConstDecls VisitedCallees;
Decl *D = N->getDecl();
assert(D);
HandleCode(D, ANALYSIS_PATH,
(Mgr->InliningMode == All ? 0 : &VisitedCallees));
// Add the visited callees to the global visited set.
- for (SetOfDecls::const_iterator I = VisitedCallees.begin(),
- E = VisitedCallees.end(); I != E; ++I) {
+ for (SetOfConstDecls::const_iterator I = VisitedCallees.begin(),
+ E = VisitedCallees.end(); I != E; ++I){
CallGraphNode *VN = CG.getNode(*I);
if (VN)
Visited.insert(VN);
@@ -358,10 +401,14 @@
// sensitive analyzes as well.
RecVisitorMode = (Mgr->shouldInlineCall() ? ANALYSIS_SYNTAX : ANALYSIS_ALL);
RecVisitorBR = &BR;
- TraverseDecl(TU);
+
+ // Process all the top level declarations.
+ for (SetOfDecls::iterator I = LocalTUDecls.begin(),
+ E = LocalTUDecls.end(); I != E; ++I)
+ TraverseDecl(*I);
if (Mgr->shouldInlineCall())
- HandleDeclsGallGraph(TU);
+ HandleDeclsGallGraph();
// After all decls handled, run checkers on the entire TranslationUnit.
checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR);
@@ -424,7 +471,7 @@
}
void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode,
- SetOfDecls *VisitedCallees) {
+ SetOfConstDecls *VisitedCallees) {
if (skipFunction(D))
return;
@@ -458,7 +505,7 @@
//===----------------------------------------------------------------------===//
void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled,
- SetOfDecls *VisitedCallees) {
+ SetOfConstDecls *VisitedCallees) {
// Construct the analysis engine. First check if the CFG is valid.
// FIXME: Inter-procedural analysis will need to handle invalid CFGs.
if (!Mgr->getCFG(D))
@@ -489,7 +536,8 @@
Eng.getBugReporter().FlushReports();
}
-void AnalysisConsumer::RunPathSensitiveChecks(Decl *D, SetOfDecls *Visited) {
+void AnalysisConsumer::RunPathSensitiveChecks(Decl *D,
+ SetOfConstDecls *Visited) {
switch (Mgr->getLangOpts().getGC()) {
case LangOptions::NonGC:
Added: cfe/trunk/test/Analysis/check-deserialization.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/check-deserialization.cpp?rev=154625&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/check-deserialization.cpp (added)
+++ cfe/trunk/test/Analysis/check-deserialization.cpp Thu Apr 12 17:36:48 2012
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -emit-pch -o %t %s
+// RUN: %clang_cc1 -error-on-deserialized-decl S1_method -include-pch %t -analyze -analyzer-checker=core %s
+// RUN: %clang_cc1 -include-pch %t -analyze -analyzer-checker=core -verify %s
+
+#ifndef HEADER
+#define HEADER
+// Header.
+
+void S1_method(); // This should not be deserialized.
+
+
+#else
+// Using the header.
+
+int test() {
+ int x = 0;
+ return 5/x; //expected-warning {{Division by zero}}
+}
+
+#endif
Added: cfe/trunk/test/Analysis/objc-method-coverage.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/objc-method-coverage.m?rev=154625&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/objc-method-coverage.m (added)
+++ cfe/trunk/test/Analysis/objc-method-coverage.m Thu Apr 12 17:36:48 2012
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-ipa=inlining -analyzer-stats -fblocks %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-ipa=none -analyzer-stats -fblocks %s 2>&1 | FileCheck %s
+
+ at interface I
+int f() {
+ return 0;
+}
+ at end
+
+ at implementation I
++ (void *)ff{
+ return (void*)0;
+}
+ at end
+
+// CHECK: ... Statistics Collected ...
+// CHECK: 2 AnalysisConsumer - The # of functions analysed (as top level).
\ No newline at end of file
More information about the cfe-commits
mailing list