diff --git a/clang/include/clang/CodeGen/CodeGenMangling.h b/clang/include/clang/CodeGen/CodeGenMangling.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/CodeGen/CodeGenMangling.h @@ -0,0 +1,27 @@ +//==----- CodeGenMangling.h - Get mangled names from the CodeGenModule -----==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// CodeGenMangling provides name mangling that depends on the target set for +// the given CodeGen. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CODEGEN_CODEGENMANGLING_H +#define LLVM_CLANG_CODEGEN_CODEGENMANGLING_H + +#include "clang/AST/GlobalDecl.h" +#include "llvm/ADT/StringRef.h" + +namespace clang { +namespace CodeGen { +class CodeGenModule; + +llvm::StringRef getMangledName(CodeGenModule &CGM, GlobalDecl GD); + +} // namespace CodeGen +} // namespace clang + +#endif diff --git a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def --- a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def +++ b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def @@ -314,6 +314,11 @@ "Display the checker name for textual outputs", true) +ANALYZER_OPTION(bool, GenerateLLVMIR, "generate-llvm-ir", + "Whether to generate LLVM IR for the translation unit. " + "The analyzer can be more precise by using info from the IR.", + false) + //===----------------------------------------------------------------------===// // Unsigned analyzer options. //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/StaticAnalyzer/Core/IRContext.h b/clang/include/clang/StaticAnalyzer/Core/IRContext.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/StaticAnalyzer/Core/IRContext.h @@ -0,0 +1,48 @@ +//== IRContext.h - Get info from the IR in CSA -*- C++ -*--------------------=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines auxilary structures for getting data from the IR inside +// the Clang Static Analyzer. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_IRCONTEXT_H +#define LLVM_CLANG_STATICANALYZER_CORE_IRCONTEXT_H + +#include "clang/AST/GlobalDecl.h" + +namespace llvm { +class Function; +} // namespace llvm + +namespace clang { +class CodeGenerator; +class FunctionDecl; + +namespace ento { + +/// Static Analyzer components can get access to the LLVM IR of a translation +/// unit throught this class. +class IRContext { + /// Set by AnalysisAction if the AnalyzerOptions requires that. + clang::CodeGenerator *&CodeGen; + +public: + IRContext(clang::CodeGenerator *&CodeGen) : CodeGen(CodeGen) {} + void runPipeline(); + llvm::Function *getFunction(GlobalDecl GD); + /// Get the LLVM code for a function. We use the complete versions of the + /// constructors and desctructors in this overload. Use the other overload to + /// get the base object ctor/dtor. + llvm::Function *getFunction(const FunctionDecl *FD); +}; + +} // namespace ento +} // namespace clang + +#endif diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -87,6 +87,7 @@ class ExplodedNodeSet; class ExplodedNode; class IndirectGotoNodeBuilder; +class IRContext; class MemRegion; struct NodeBuilderContext; class NodeBuilderWithSinks; @@ -140,6 +141,8 @@ private: cross_tu::CrossTranslationUnitContext &CTU; + IRContext &IRCtx; + AnalysisManager &AMgr; AnalysisDeclContextManager &AnalysisDeclContexts; @@ -181,8 +184,8 @@ InliningModes HowToInline; public: - ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, AnalysisManager &mgr, - SetOfConstDecls *VisitedCalleesIn, + ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, IRContext &IRCtx, + AnalysisManager &mgr, SetOfConstDecls *VisitedCalleesIn, FunctionSummariesTy *FS, InliningModes HowToInlineIn); virtual ~ExprEngine() = default; @@ -224,6 +227,8 @@ return &CTU; } + IRContext *getIRContext() { return &IRCtx; } + const NodeBuilderContext &getBuilderContext() { assert(currBldrCtx); return *currBldrCtx; diff --git a/clang/include/clang/StaticAnalyzer/Frontend/AnalysisConsumer.h b/clang/include/clang/StaticAnalyzer/Frontend/AnalysisConsumer.h --- a/clang/include/clang/StaticAnalyzer/Frontend/AnalysisConsumer.h +++ b/clang/include/clang/StaticAnalyzer/Frontend/AnalysisConsumer.h @@ -24,6 +24,7 @@ class Preprocessor; class DiagnosticsEngine; class CodeInjector; +class CodeGenerator; class CompilerInstance; namespace ento { @@ -32,6 +33,9 @@ class CheckerRegistry; class AnalysisASTConsumer : public ASTConsumer { +protected: + CodeGenerator *CG = nullptr; + public: virtual void AddDiagnosticConsumer(PathDiagnosticConsumer *Consumer) = 0; @@ -47,6 +51,8 @@ /// }); virtual void AddCheckerRegistrationFn(std::function Fn) = 0; + + void setCodeGen(CodeGenerator *Cg) { CG = Cg; } }; /// CreateAnalysisConsumer - Creates an ASTConsumer to run various code @@ -57,6 +63,6 @@ } // namespace ento -} // end clang namespace +} // namespace clang #endif diff --git a/clang/include/clang/StaticAnalyzer/Frontend/FrontendActions.h b/clang/include/clang/StaticAnalyzer/Frontend/FrontendActions.h --- a/clang/include/clang/StaticAnalyzer/Frontend/FrontendActions.h +++ b/clang/include/clang/StaticAnalyzer/Frontend/FrontendActions.h @@ -13,10 +13,15 @@ #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" -namespace clang { +#include + +namespace llvm { +class LLVMContext; +} // namespace llvm +namespace clang { +class CodeGenerator; class Stmt; -class AnalyzerOptions; namespace ento { @@ -27,11 +32,24 @@ //===----------------------------------------------------------------------===// class AnalysisAction : public ASTFrontendAction { + // Cannot be a unique_ptr because then ClangFrontendTool would + // depend on this lib. + std::shared_ptr LLVMCtx; + std::unique_ptr CodeGenDiags; + protected: std::unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override; + +public: + ~AnalysisAction(); }; +std::unique_ptr CreateAnalysisAction(); +std::unique_ptr BuildCodeGen(CompilerInstance &CI, + llvm::LLVMContext &LLVMCtx, + DiagnosticsEngine &CodeGenDiags); + /// Frontend action to parse model files. /// /// This frontend action is responsible for parsing model files. Model files can diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt --- a/clang/lib/CodeGen/CMakeLists.txt +++ b/clang/lib/CodeGen/CMakeLists.txt @@ -68,6 +68,7 @@ CodeGenABITypes.cpp CodeGenAction.cpp CodeGenFunction.cpp + CodeGenMangling.cpp CodeGenModule.cpp CodeGenPGO.cpp CodeGenTBAA.cpp diff --git a/clang/lib/CodeGen/CodeGenMangling.cpp b/clang/lib/CodeGen/CodeGenMangling.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/CodeGen/CodeGenMangling.cpp @@ -0,0 +1,25 @@ +//==--- CodeGenMangling.cpp - Get mangled names from the CodeGenModule -----==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// CodeGenMangling provides name mangling that depends on the target set for +// the given CodeGen. +//===----------------------------------------------------------------------===// + +#include "clang/CodeGen/CodeGenMangling.h" +#include "CodeGenModule.h" + +using namespace llvm; + +namespace clang { +namespace CodeGen { + +StringRef getMangledName(CodeGenModule &CGM, GlobalDecl GD) { + return CGM.getMangledName(GD); +} + +} // namespace CodeGen +} // namespace clang diff --git a/clang/lib/StaticAnalyzer/Core/CMakeLists.txt b/clang/lib/StaticAnalyzer/Core/CMakeLists.txt --- a/clang/lib/StaticAnalyzer/Core/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Core/CMakeLists.txt @@ -31,6 +31,7 @@ ExprEngineObjC.cpp FunctionSummary.cpp HTMLDiagnostics.cpp + IRContext.cpp IssueHash.cpp LoopUnrolling.cpp LoopWidening.cpp @@ -52,10 +53,15 @@ WorkList.cpp LINK_LIBS + LLVMAnalysis + LLVMCore + LLVMipo + LLVMPasses clangAST clangASTMatchers clangAnalysis clangBasic + clangCodeGen clangCrossTU clangFrontend clangLex diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -200,24 +200,18 @@ static const char* TagProviderName = "ExprEngine"; ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, - AnalysisManager &mgr, + IRContext &IRCtx, AnalysisManager &mgr, SetOfConstDecls *VisitedCalleesIn, - FunctionSummariesTy *FS, - InliningModes HowToInlineIn) - : CTU(CTU), AMgr(mgr), + FunctionSummariesTy *FS, InliningModes HowToInlineIn) + : CTU(CTU), IRCtx(IRCtx), AMgr(mgr), AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()), Engine(*this, FS, mgr.getAnalyzerOptions()), G(Engine.getGraph()), StateMgr(getContext(), mgr.getStoreManagerCreator(), - mgr.getConstraintManagerCreator(), G.getAllocator(), - this), - SymMgr(StateMgr.getSymbolManager()), - MRMgr(StateMgr.getRegionManager()), - svalBuilder(StateMgr.getSValBuilder()), - ObjCNoRet(mgr.getASTContext()), - BR(mgr, *this), - VisitedCallees(VisitedCalleesIn), - HowToInline(HowToInlineIn) - { + mgr.getConstraintManagerCreator(), G.getAllocator(), this), + SymMgr(StateMgr.getSymbolManager()), MRMgr(StateMgr.getRegionManager()), + svalBuilder(StateMgr.getSValBuilder()), ObjCNoRet(mgr.getASTContext()), + BR(mgr, *this), VisitedCallees(VisitedCalleesIn), + HowToInline(HowToInlineIn) { unsigned TrimInterval = mgr.options.GraphTrimInterval; if (TrimInterval != 0) { // Enable eager node reclamation when constructing the ExplodedGraph. diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -17,10 +17,12 @@ #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/ConstructionContext.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/IRContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/Statistic.h" +#include "llvm/IR/Function.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/SaveAndRestore.h" @@ -707,7 +709,22 @@ // a conjured return value. void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr, ExplodedNode *Pred, ProgramStateRef State) { - State = Call.invalidateRegions(currBldrCtx->blockCount(), State); + // Query the LLVM IR whether this function is pure. + // This is false, if the IR for the function is not available. + const bool IsPure = [this](const CallEvent &Call) { + if (const Decl *D = Call.getRuntimeDefinition().getDecl()) + if (const FunctionDecl *FD = dyn_cast(D)) + if (FD->getDefinition()) + if (auto *F = getIRContext()->getFunction(FD)) + // Pure function. + if (F->getAttributes().hasFnAttribute(llvm::Attribute::ReadNone) || + F->getAttributes().hasFnAttribute(llvm::Attribute::ReadOnly)) + return true; + return false; + }(Call); + + if (!IsPure) + State = Call.invalidateRegions(currBldrCtx->blockCount(), State); State = bindReturnValue(Call, Pred->getLocationContext(), State); // And make the result node. diff --git a/clang/lib/StaticAnalyzer/Core/IRContext.cpp b/clang/lib/StaticAnalyzer/Core/IRContext.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Core/IRContext.cpp @@ -0,0 +1,98 @@ +//===-------------- IRContext.cpp -----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/IRContext.h" +#include "clang/CodeGen/CodeGenMangling.h" +#include "clang/CodeGen/ModuleBuilder.h" + +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Transforms/IPO/FunctionAttrs.h" + +using namespace llvm; +using namespace clang; +using namespace ento; + +void IRContext::runPipeline() { + if (CodeGen == nullptr) + return; + + // TargetMachine is not set, so we will not do optimizations based on + // target-aware cost modeling of IR contructs. Still, a default + // TargetIRAnalysis is registerd in registerFunctionAnalyses. That will use + // the module's datalayout to construct a baseline conservative result. + PassBuilder PB; + + // FIXME make this an option. + constexpr const bool Debug = false; + + LoopAnalysisManager LAM(Debug); + FunctionAnalysisManager FAM(Debug); + CGSCCAnalysisManager CGAM(Debug); + ModuleAnalysisManager MAM(Debug); + + // Register the AA manager first so that our version is the one used. + FAM.registerPass([&] { return PB.buildDefaultAAPipeline(); }); + + auto *M = CodeGen->GetModule(); + + // Register the target library analysis directly and give it a customized + // preset TLI. + Triple TargetTriple(M->getTargetTriple()); + std::unique_ptr TLII( + new TargetLibraryInfoImpl(TargetTriple)); + FAM.registerPass([&] { return TargetLibraryAnalysis(*TLII); }); + + // Register all the basic analyses with the managers. + PB.registerModuleAnalyses(MAM); + PB.registerCGSCCAnalyses(CGAM); + PB.registerFunctionAnalyses(FAM); + PB.registerLoopAnalyses(LAM); + PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); + + ModulePassManager MPM(Debug); + + MPM.addPass(RequireAnalysisPass()); + // Deduce any function attributes based in the current code. + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor( + PostOrderFunctionAttrsPass())); + + MPM.run(*M, MAM); +} + +llvm::Function *IRContext::getFunction(GlobalDecl GD) { + if (CodeGen == nullptr) + return nullptr; + + CodeGen::CodeGenModule &CGM = CodeGen->CGM(); + StringRef Name = getMangledName(CGM, GD); + + auto *M = CodeGen->GetModule(); + // There are functions which are not generated. E.g. not used operator=, etc. + return M->getFunction(Name); +} + +llvm::Function *IRContext::getFunction(const FunctionDecl *FD) { + if (CodeGen == nullptr) + return nullptr; + + // We use the complete versions of the constructors and desctructors. + // Use the other overload of getFunction to get the base object ctor/dtor. + GlobalDecl GD; + if (const auto *CD = dyn_cast(FD)) + GD = GlobalDecl(CD, Ctor_Complete); + else if (const auto *DD = dyn_cast(FD)) + GD = GlobalDecl(DD, Dtor_Complete); + else if (FD->hasAttr()) + GD = GlobalDecl(FD, KernelReferenceKind::Kernel); + else + GD = GlobalDecl(FD); + + return getFunction(GD); +} diff --git a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp --- a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -30,6 +30,7 @@ #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/IRContext.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" @@ -90,6 +91,7 @@ ArrayRef Plugins; CodeInjector *Injector; cross_tu::CrossTranslationUnitContext CTU; + IRContext IRCtx; /// Stores the declarations from the local translation unit. /// Note, we pre-compute the local declarations at parse time as an @@ -122,7 +124,7 @@ CodeInjector *injector) : RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr), PP(CI.getPreprocessor()), OutDir(outdir), Opts(std::move(opts)), - Plugins(plugins), Injector(injector), CTU(CI) { + Plugins(plugins), Injector(injector), CTU(CI), IRCtx(CG) { DigestAnalyzerOptions(); if (Opts->PrintStats || Opts->ShouldSerializeStats) { AnalyzerTimers = std::make_unique( @@ -541,6 +543,7 @@ reportAnalyzerProgress("All checks are disabled using a supplied option\n"); } else { // Otherwise, just run the analysis. + IRCtx.runPipeline(); runAnalysisOnTranslationUnit(C); } @@ -693,7 +696,7 @@ if (!Mgr->getAnalysisDeclContext(D)->getAnalysis()) return; - ExprEngine Eng(CTU, *Mgr, VisitedCallees, &FunctionSummaries, IMode); + ExprEngine Eng(CTU, IRCtx, *Mgr, VisitedCallees, &FunctionSummaries, IMode); // Execute the worklist algorithm. if (ExprEngineTimer) diff --git a/clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt b/clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt --- a/clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt @@ -14,10 +14,12 @@ ModelInjector.cpp LINK_LIBS + LLVMCore clangAST clangASTMatchers clangAnalysis clangBasic + clangCodeGen clangCrossTU clangFrontend clangLex diff --git a/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp b/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp --- a/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp +++ b/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp @@ -7,14 +7,60 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Frontend/FrontendActions.h" +#include "clang/Frontend/MultiplexConsumer.h" #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" #include "clang/StaticAnalyzer/Frontend/ModelConsumer.h" + +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "llvm/IR/LLVMContext.h" + using namespace clang; using namespace ento; +std::unique_ptr +clang::ento::BuildCodeGen(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx, + DiagnosticsEngine &CodeGenDiags) { + StringRef ModuleName("csa_module"); + // Set all CodeGen options to the default. + CodeGenOptions CGO = CodeGenOptions(); + // Set the optimization level, so CodeGenFunciton would emit lifetime + // markers which are used by some LLVM analysis (e.g. AliasAnalysis). + CGO.OptimizationLevel = 2; // -O2 + + return std::unique_ptr( + CreateLLVMCodeGen(CodeGenDiags, ModuleName, CI.getHeaderSearchOpts(), + CI.getPreprocessorOpts(), CGO, LLVMCtx)); +} + +AnalysisAction::~AnalysisAction() {} + std::unique_ptr AnalysisAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { - return CreateAnalysisConsumer(CI); + std::vector> ASTConsumers; + auto AConsumer = CreateAnalysisConsumer(CI); + + AnalyzerOptionsRef Opts = CI.getAnalyzerOpts(); + if (Opts->GenerateLLVMIR) { + DiagnosticsEngine &SADiagEng = CI.getDiagnostics(); + // Supress diagnostics during CodeGen. + CodeGenDiags = std::make_unique( + SADiagEng.getDiagnosticIDs(), &SADiagEng.getDiagnosticOptions(), + SADiagEng.getClient(), /*ShouldOwnClient=*/false); + CodeGenDiags->setSuppressAllDiagnostics(true); + LLVMCtx = std::make_shared(); + auto CGConsumer = BuildCodeGen(CI, *LLVMCtx, *CodeGenDiags); + AConsumer->setCodeGen(CGConsumer.get()); + ASTConsumers.push_back(std::move(CGConsumer)); + } + + ASTConsumers.push_back(std::move(AConsumer)); + return std::make_unique(std::move(ASTConsumers)); +} + +std::unique_ptr CreateAnalysisAction() { + return std::make_unique(); } ParseModelFileAction::ParseModelFileAction(llvm::StringMap &Bodies) diff --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c --- a/clang/test/Analysis/analyzer-config.c +++ b/clang/test/Analysis/analyzer-config.c @@ -80,6 +80,7 @@ // CHECK-NEXT: experimental-enable-naive-ctu-analysis = false // CHECK-NEXT: exploration_strategy = unexplored_first_queue // CHECK-NEXT: faux-bodies = true +// CHECK-NEXT: generate-llvm-ir = false // CHECK-NEXT: graph-trim-interval = 1000 // CHECK-NEXT: inline-lambdas = true // CHECK-NEXT: ipa = dynamic-bifurcate diff --git a/clang/test/Analysis/ctu-different-triples.cpp b/clang/test/Analysis/ctu-different-triples.cpp --- a/clang/test/Analysis/ctu-different-triples.cpp +++ b/clang/test/Analysis/ctu-different-triples.cpp @@ -12,6 +12,8 @@ // We expect an error in this file, but without a location. // expected-error-re@./ctu-different-triples.cpp:*{{imported AST from {{.*}} had been generated for a different target, current: powerpc64-montavista-linux-gnu, imported: x86_64-pc-linux-gnu}} +// FIXME Why do we get the error twice? +// expected-error-re@./ctu-different-triples.cpp:*{{imported AST from {{.*}} had been generated for a different target, current: powerpc64-montavista-linux-gnu, imported: x86_64-pc-linux-gnu}} int f(int); diff --git a/clang/test/Analysis/ircontext.cpp b/clang/test/Analysis/ircontext.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/ircontext.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_analyze_cc1 %s \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -analyzer-config ipa=none \ +// RUN: -analyzer-config generate-llvm-ir=true \ +// RUN: -triple i686-unknown-linux \ +// RUN: -verify + +void clang_analyzer_eval(int); + +int g = 0; +static int foo(int *x) { return *x; } + +void test() { + g = 3; + int l = 0; + foo(&l); + clang_analyzer_eval(g == 3); // expected-warning{{TRUE}} +} diff --git a/clang/unittests/StaticAnalyzer/CMakeLists.txt b/clang/unittests/StaticAnalyzer/CMakeLists.txt --- a/clang/unittests/StaticAnalyzer/CMakeLists.txt +++ b/clang/unittests/StaticAnalyzer/CMakeLists.txt @@ -8,20 +8,23 @@ CallDescriptionTest.cpp CallEventTest.cpp FalsePositiveRefutationBRVisitorTest.cpp + IRContextTest.cpp ParamRegionTest.cpp RangeSetTest.cpp RegisterCustomCheckersTest.cpp - StoreTest.cpp + StoreTest.cpp SymbolReaperTest.cpp TestReturnValueUnderConstruction.cpp ) clang_target_link_libraries(StaticAnalysisTests PRIVATE + LLVMCore clangBasic clangAnalysis clangAST clangASTMatchers + clangCodeGen clangCrossTU clangFrontend clangSerialization diff --git a/clang/unittests/StaticAnalyzer/IRContextTest.cpp b/clang/unittests/StaticAnalyzer/IRContextTest.cpp new file mode 100644 --- /dev/null +++ b/clang/unittests/StaticAnalyzer/IRContextTest.cpp @@ -0,0 +1,101 @@ +//===- unittests/StaticAnalyzer/IRContextTest.cpp -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Reusables.h" + +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/StaticAnalyzer/Core/IRContext.h" +#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" +#include "clang/StaticAnalyzer/Frontend/FrontendActions.h" +#include "clang/Tooling/Tooling.h" + +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" + +#include "gtest/gtest.h" + +namespace clang { +namespace ento { +namespace { + +using namespace ast_matchers; + +const auto TestCode = R"( + int foo(int *x) { return *x; } + static int bar(int *x) { return *x; } + int baz(int *x) { int l; return foo(&l); } + + // Must have at least one CallExpr for static functions, otherwise + // CodeGen will not emit the definition. + void callerOfBar() { int l; bar(&l); } + )"; + +class IRContextTestConsumer : public ASTConsumer { + IRContext IRCtx; + CodeGenerator *CG = nullptr; + +public: + IRContextTestConsumer() : IRCtx(CG) {} + void setCodeGen(CodeGenerator *Cg) { CG = Cg; } + void HandleTranslationUnit(ASTContext &Ctx) override { + IRCtx.runPipeline(); + CG->GetModule()->dump(); + TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl(); + const FunctionDecl *Foo = + findNode(TU, functionDecl(hasName("foo"))); + ASSERT_TRUE(Foo); + const FunctionDecl *Bar = + findNode(TU, functionDecl(hasName("bar"))); + ASSERT_TRUE(Bar); + const FunctionDecl *Baz = + findNode(TU, functionDecl(hasName("baz"))); + ASSERT_TRUE(Baz); + // We should be able to get the corresponding representation in IR. + llvm::Function *IRFoo = IRCtx.getFunction(Foo); + ASSERT_TRUE(IRFoo); + llvm::Function *IRBar = IRCtx.getFunction(Bar); + ASSERT_TRUE(IRBar); + llvm::Function *IRBaz = IRCtx.getFunction(Baz); + ASSERT_TRUE(IRBaz); + // "readonly" attribute should be set for all functions. + EXPECT_TRUE( + IRFoo->getAttributes().hasFnAttribute(llvm::Attribute::ReadOnly)); + EXPECT_TRUE( + IRBar->getAttributes().hasFnAttribute(llvm::Attribute::ReadOnly)); + EXPECT_TRUE( + IRBaz->getAttributes().hasFnAttribute(llvm::Attribute::ReadOnly)); + } +}; + +class IRContextTestAction : public ASTFrontendAction { + std::shared_ptr LLVMCtx; + +public: + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef File) override { + std::vector> ASTConsumers; + LLVMCtx = std::make_shared(); + auto CGConsumer = BuildCodeGen(CI, *LLVMCtx, CI.getDiagnostics()); + auto TestConsumer = std::make_unique(); + TestConsumer->setCodeGen(CGConsumer.get()); + ASTConsumers.push_back(std::move(CGConsumer)); + ASTConsumers.push_back(std::move(TestConsumer)); + return std::make_unique(std::move(ASTConsumers)); + } +}; + +TEST(IRContext, TestReadOnly) { + EXPECT_TRUE(tooling::runToolOnCode(std::make_unique(), + TestCode)); +} + +} // namespace +} // namespace ento +} // namespace clang diff --git a/clang/unittests/StaticAnalyzer/Reusables.h b/clang/unittests/StaticAnalyzer/Reusables.h --- a/clang/unittests/StaticAnalyzer/Reusables.h +++ b/clang/unittests/StaticAnalyzer/Reusables.h @@ -10,8 +10,9 @@ #define LLVM_CLANG_UNITTESTS_STATICANALYZER_REUSABLES_H #include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/Frontend/CompilerInstance.h" #include "clang/CrossTU/CrossTranslationUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/StaticAnalyzer/Core/IRContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" namespace clang { @@ -41,11 +42,13 @@ class ExprEngineConsumer : public ASTConsumer { protected: CompilerInstance &C; + CodeGenerator *CG = nullptr; private: // We need to construct all of these in order to construct ExprEngine. CheckerManager ChkMgr; cross_tu::CrossTranslationUnitContext CTU; + IRContext IRCtx; PathDiagnosticConsumers Consumers; AnalysisManager AMgr; SetOfConstDecls VisitedCallees; @@ -58,12 +61,12 @@ ExprEngineConsumer(CompilerInstance &C) : C(C), ChkMgr(C.getASTContext(), *C.getAnalyzerOpts(), C.getPreprocessor()), - CTU(C), Consumers(), + CTU(C), IRCtx(CG), Consumers(), AMgr(C.getASTContext(), C.getPreprocessor(), Consumers, CreateRegionStoreManager, CreateRangeConstraintManager, &ChkMgr, *C.getAnalyzerOpts()), - VisitedCallees(), FS(), - Eng(CTU, AMgr, &VisitedCallees, &FS, ExprEngine::Inline_Regular) {} + VisitedCallees(), FS(), Eng(CTU, IRCtx, AMgr, &VisitedCallees, &FS, + ExprEngine::Inline_Regular) {} }; } // namespace ento