Skip to content

Commit

Permalink
[analyzer] Support for naive cross translation unit analysis
Browse files Browse the repository at this point in the history
The aim of this patch is to be minimal to enable incremental development of
the feature on the top of the tree. This patch should be an NFC when the
feature is turned off. It is turned off by default and still considered as
experimental.

Technical details are available in the EuroLLVM Talk: 
http://llvm.org/devmtg/2017-03//2017/02/20/accepted-sessions.html#7

Note that the initial prototype was done by A. Sidorin et al.: http://lists.llvm.org/pipermail/cfe-dev/2015-October/045730.html

Contributions to the measurements and the new version of the code: Peter Szecsi, Zoltan Gera, Daniel Krupp, Kareem Khazem.

Differential Revision: https://reviews.llvm.org/D30691

llvm-svn: 326323
  • Loading branch information
Xazax-hun committed Feb 28, 2018
1 parent 4529aac commit eb0584b
Show file tree
Hide file tree
Showing 23 changed files with 773 additions and 51 deletions.
21 changes: 21 additions & 0 deletions clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
Expand Up @@ -308,6 +308,16 @@ class AnalyzerOptions : public RefCountedBase<AnalyzerOptions> {
/// \sa shouldDisplayNotesAsEvents
Optional<bool> DisplayNotesAsEvents;

/// \sa getCTUDir
Optional<StringRef> CTUDir;

/// \sa getCTUIndexName
Optional<StringRef> CTUIndexName;

/// \sa naiveCTUEnabled
Optional<bool> NaiveCTU;


/// A helper function that retrieves option for a given full-qualified
/// checker name.
/// Options for checkers can be specified via 'analyzer-config' command-line
Expand Down Expand Up @@ -637,6 +647,17 @@ class AnalyzerOptions : public RefCountedBase<AnalyzerOptions> {
/// to false when unset.
bool shouldDisplayNotesAsEvents();

/// Returns the directory containing the CTU related files.
StringRef getCTUDir();

/// Returns the name of the file containing the CTU index of functions.
StringRef getCTUIndexName();

/// Returns true when naive cross translation unit analysis is enabled.
/// This is an experimental feature to inline functions from another
/// translation units.
bool naiveCTUEnabled();

public:
AnalyzerOptions() :
AnalysisStoreOpt(RegionStoreModel),
Expand Down
17 changes: 13 additions & 4 deletions clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
Expand Up @@ -38,6 +38,10 @@ class CXXThisExpr;
class MaterializeTemporaryExpr;
class ObjCAtSynchronizedStmt;
class ObjCForCollectionStmt;

namespace cross_tu {
class CrossTranslationUnitContext;
}

namespace ento {

Expand Down Expand Up @@ -74,6 +78,8 @@ class ExprEngine : public SubEngine {
};

private:
cross_tu::CrossTranslationUnitContext &CTU;

AnalysisManager &AMgr;

AnalysisDeclContextManager &AnalysisDeclContexts;
Expand Down Expand Up @@ -115,10 +121,9 @@ class ExprEngine : public SubEngine {
InliningModes HowToInline;

public:
ExprEngine(AnalysisManager &mgr, bool gcEnabled,
SetOfConstDecls *VisitedCalleesIn,
FunctionSummariesTy *FS,
InliningModes HowToInlineIn);
ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, AnalysisManager &mgr,
bool gcEnabled, SetOfConstDecls *VisitedCalleesIn,
FunctionSummariesTy *FS, InliningModes HowToInlineIn);

~ExprEngine() override;

Expand Down Expand Up @@ -150,6 +155,10 @@ class ExprEngine : public SubEngine {

BugReporter& getBugReporter() { return BR; }

cross_tu::CrossTranslationUnitContext *getCrossTranslationUnitContext() {
return &CTU;
}

const NodeBuilderContext &getBuilderContext() {
assert(currBldrCtx);
return *currBldrCtx;
Expand Down
Expand Up @@ -24,6 +24,10 @@ class CFGElement;
class LocationContext;
class Stmt;

namespace cross_tu {
class CrossTranslationUnitContext;
}

namespace ento {

struct NodeBuilderContext;
Expand All @@ -49,6 +53,9 @@ class SubEngine {

virtual AnalysisManager &getAnalysisManager() = 0;

virtual cross_tu::CrossTranslationUnitContext *
getCrossTranslationUnitContext() = 0;

virtual ProgramStateManager &getStateManager() = 0;

/// Called by CoreEngine. Used to generate new successor
Expand Down
23 changes: 23 additions & 0 deletions clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
Expand Up @@ -433,3 +433,26 @@ bool AnalyzerOptions::shouldDisplayNotesAsEvents() {
getBooleanOption("notes-as-events", /*Default=*/false);
return DisplayNotesAsEvents.getValue();
}

StringRef AnalyzerOptions::getCTUDir() {
if (!CTUDir.hasValue()) {
CTUDir = getOptionAsString("ctu-dir", "");
if (!llvm::sys::fs::is_directory(*CTUDir))
CTUDir = "";
}
return CTUDir.getValue();
}

bool AnalyzerOptions::naiveCTUEnabled() {
if (!NaiveCTU.hasValue()) {
NaiveCTU = getBooleanOption("experimental-enable-naive-ctu-analysis",
/*Default=*/false);
}
return NaiveCTU.getValue();
}

StringRef AnalyzerOptions::getCTUIndexName() {
if (!CTUIndexName.hasValue())
CTUIndexName = getOptionAsString("ctu-index-name", "externalFnMap.txt");
return CTUIndexName.getValue();
}
1 change: 1 addition & 0 deletions clang/lib/StaticAnalyzer/Core/CMakeLists.txt
Expand Up @@ -58,6 +58,7 @@ add_clang_library(clangStaticAnalyzerCore
clangASTMatchers
clangAnalysis
clangBasic
clangCrossTU
clangLex
clangRewrite
${Z3_LINK_FILES}
Expand Down
23 changes: 22 additions & 1 deletion clang/lib/StaticAnalyzer/Core/CallEvent.cpp
Expand Up @@ -28,6 +28,7 @@
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/ProgramPoint.h"
#include "clang/CrossTU/CrossTranslationUnit.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
Expand Down Expand Up @@ -405,7 +406,27 @@ RuntimeDefinition AnyFunctionCall::getRuntimeDefinition() const {
}
}

return {};
SubEngine *Engine = getState()->getStateManager().getOwningEngine();
AnalyzerOptions &Opts = Engine->getAnalysisManager().options;

// Try to get CTU definition only if CTUDir is provided.
if (!Opts.naiveCTUEnabled())
return RuntimeDefinition();

cross_tu::CrossTranslationUnitContext &CTUCtx =
*Engine->getCrossTranslationUnitContext();
llvm::Expected<const FunctionDecl *> CTUDeclOrError =
CTUCtx.getCrossTUDefinition(FD, Opts.getCTUDir(), Opts.getCTUIndexName());

if (!CTUDeclOrError) {
handleAllErrors(CTUDeclOrError.takeError(),
[&](const cross_tu::IndexError &IE) {
CTUCtx.emitCrossTUDiagnostics(IE);
});
return {};
}

return RuntimeDefinition(*CTUDeclOrError);
}

void AnyFunctionCall::getInitialStackFrameContents(
Expand Down
7 changes: 4 additions & 3 deletions clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
Expand Up @@ -23,6 +23,7 @@
#include "clang/Basic/Builtins.h"
#include "clang/Basic/PrettyStackTrace.h"
#include "clang/Basic/SourceManager.h"
#include "clang/CrossTU/CrossTranslationUnit.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
Expand Down Expand Up @@ -98,11 +99,11 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(CXXNewAllocatorValues,

static const char* TagProviderName = "ExprEngine";

ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled,
SetOfConstDecls *VisitedCalleesIn,
ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, AnalysisManager &mgr,
bool gcEnabled, SetOfConstDecls *VisitedCalleesIn,
FunctionSummariesTy *FS,
InliningModes HowToInlineIn)
: AMgr(mgr),
: CTU(CTU), AMgr(mgr),
AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()),
Engine(*this, FS, mgr.getAnalyzerOptions()),
G(Engine.getGraph()),
Expand Down
19 changes: 17 additions & 2 deletions clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
Expand Up @@ -379,11 +379,25 @@ static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y) {
return None;
}

static bool compareCrossTUSourceLocs(FullSourceLoc XL, FullSourceLoc YL) {
std::pair<FileID, unsigned> XOffs = XL.getDecomposedLoc();
std::pair<FileID, unsigned> YOffs = YL.getDecomposedLoc();
const SourceManager &SM = XL.getManager();
std::pair<bool, bool> InSameTU = SM.isInTheSameTranslationUnit(XOffs, YOffs);
if (InSameTU.first)
return XL.isBeforeInTranslationUnitThan(YL);
const FileEntry *XFE = SM.getFileEntryForID(XL.getFileID());
const FileEntry *YFE = SM.getFileEntryForID(YL.getFileID());
if (!XFE || !YFE)
return XFE && !YFE;
return XFE->getName() < YFE->getName();
}

static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y) {
FullSourceLoc XL = X.getLocation().asLocation();
FullSourceLoc YL = Y.getLocation().asLocation();
if (XL != YL)
return XL.isBeforeInTranslationUnitThan(YL);
return compareCrossTUSourceLocs(XL, YL);
if (X.getBugType() != Y.getBugType())
return X.getBugType() < Y.getBugType();
if (X.getCategory() != Y.getCategory())
Expand All @@ -403,7 +417,8 @@ static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y) {
SourceLocation YDL = YD->getLocation();
if (XDL != YDL) {
const SourceManager &SM = XL.getManager();
return SM.isBeforeInTranslationUnit(XDL, YDL);
return compareCrossTUSourceLocs(FullSourceLoc(XDL, SM),
FullSourceLoc(YDL, SM));
}
}
PathDiagnostic::meta_iterator XI = X.meta_begin(), XE = X.meta_end();
Expand Down
15 changes: 9 additions & 6 deletions clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
Expand Up @@ -22,6 +22,7 @@
#include "clang/Analysis/CallGraph.h"
#include "clang/Analysis/CodeInjector.h"
#include "clang/Basic/SourceManager.h"
#include "clang/CrossTU/CrossTranslationUnit.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
Expand Down Expand Up @@ -170,6 +171,7 @@ class AnalysisConsumer : public AnalysisASTConsumer,
AnalyzerOptionsRef Opts;
ArrayRef<std::string> Plugins;
CodeInjector *Injector;
cross_tu::CrossTranslationUnitContext CTU;

/// \brief Stores the declarations from the local translation unit.
/// Note, we pre-compute the local declarations at parse time as an
Expand All @@ -195,12 +197,12 @@ class AnalysisConsumer : public AnalysisASTConsumer,
/// translation unit.
FunctionSummariesTy FunctionSummaries;

AnalysisConsumer(const Preprocessor &pp, const std::string &outdir,
AnalysisConsumer(CompilerInstance &CI, const std::string &outdir,
AnalyzerOptionsRef opts, ArrayRef<std::string> plugins,
CodeInjector *injector)
: RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr), PP(pp),
OutDir(outdir), Opts(std::move(opts)), Plugins(plugins),
Injector(injector) {
: RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr),
PP(CI.getPreprocessor()), OutDir(outdir), Opts(std::move(opts)),
Plugins(plugins), Injector(injector), CTU(CI) {
DigestAnalyzerOptions();
if (Opts->PrintStats || Opts->shouldSerializeStats()) {
AnalyzerTimers = llvm::make_unique<llvm::TimerGroup>(
Expand Down Expand Up @@ -732,7 +734,8 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled,
if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>())
return;

ExprEngine Eng(*Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries,IMode);
ExprEngine Eng(CTU, *Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries,
IMode);

// Set the graph auditor.
std::unique_ptr<ExplodedNode::Auditor> Auditor;
Expand Down Expand Up @@ -790,7 +793,7 @@ ento::CreateAnalysisConsumer(CompilerInstance &CI) {
bool hasModelPath = analyzerOpts->Config.count("model-path") > 0;

return llvm::make_unique<AnalysisConsumer>(
CI.getPreprocessor(), CI.getFrontendOpts().OutputFile, analyzerOpts,
CI, CI.getFrontendOpts().OutputFile, analyzerOpts,
CI.getFrontendOpts().Plugins,
hasModelPath ? new ModelInjector(CI) : nullptr);
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt
Expand Up @@ -15,6 +15,7 @@ add_clang_library(clangStaticAnalyzerFrontend
clangAST
clangAnalysis
clangBasic
clangCrossTU
clangFrontend
clangLex
clangStaticAnalyzerCheckers
Expand Down
20 changes: 20 additions & 0 deletions clang/test/Analysis/Inputs/ctu-chain.cpp
@@ -0,0 +1,20 @@
int h_chain(int x) {
return x * 2;
}

namespace chns {
int chf3(int x);

int chf2(int x) {
return chf3(x);
}

class chcls {
public:
int chf4(int x);
};

int chcls::chf4(int x) {
return x * 3;
}
}
67 changes: 67 additions & 0 deletions clang/test/Analysis/Inputs/ctu-other.cpp
@@ -0,0 +1,67 @@
int callback_to_main(int x);
int f(int x) {
return x - 1;
}

int g(int x) {
return callback_to_main(x) + 1;
}

int h_chain(int);

int h(int x) {
return 2 * h_chain(x);
}

namespace myns {
int fns(int x) {
return x + 7;
}

namespace embed_ns {
int fens(int x) {
return x - 3;
}
}

class embed_cls {
public:
int fecl(int x) {
return x - 7;
}
};
}

class mycls {
public:
int fcl(int x) {
return x + 5;
}
static int fscl(int x) {
return x + 6;
}

class embed_cls2 {
public:
int fecl2(int x) {
return x - 11;
}
};
};

namespace chns {
int chf2(int x);

class chcls {
public:
int chf4(int x);
};

int chf3(int x) {
return chcls().chf4(x);
}

int chf1(int x) {
return chf2(x);
}
}
13 changes: 13 additions & 0 deletions clang/test/Analysis/Inputs/externalFnMap.txt
@@ -0,0 +1,13 @@
c:@N@chns@F@chf1#I# ctu-other.cpp.ast
c:@N@myns@N@embed_ns@F@fens#I# ctu-other.cpp.ast
c:@F@g#I# ctu-other.cpp.ast
c:@S@mycls@F@fscl#I#S ctu-other.cpp.ast
c:@S@mycls@F@fcl#I# ctu-other.cpp.ast
c:@N@myns@S@embed_cls@F@fecl#I# ctu-other.cpp.ast
c:@S@mycls@S@embed_cls2@F@fecl2#I# ctu-other.cpp.ast
c:@F@f#I# ctu-other.cpp.ast
c:@N@myns@F@fns#I# ctu-other.cpp.ast
c:@F@h#I# ctu-other.cpp.ast
c:@F@h_chain#I# ctu-chain.cpp.ast
c:@N@chns@S@chcls@F@chf4#I# ctu-chain.cpp.ast
c:@N@chns@F@chf2#I# ctu-chain.cpp.ast

0 comments on commit eb0584b

Please sign in to comment.