diff --git a/clang/include/clang/Analysis/FlowSensitive/ControlFlowContext.h b/clang/include/clang/Analysis/FlowSensitive/ControlFlowContext.h --- a/clang/include/clang/Analysis/FlowSensitive/ControlFlowContext.h +++ b/clang/include/clang/Analysis/FlowSensitive/ControlFlowContext.h @@ -30,10 +30,14 @@ /// analysis. class ControlFlowContext { public: - /// Builds a ControlFlowContext from an AST node. + /// Builds a ControlFlowContext from an AST node. `D` is the function in which + /// `S` resides. All arguments must be non-null. static llvm::Expected build(const Decl *D, Stmt *S, ASTContext *C); + /// Returns the `Decl` containing the statement used to construct the CFG. + const Decl &getDecl() const { return *ContainingDecl; } + /// Returns the CFG that is stored in this context. const CFG &getCFG() const { return *Cfg; } @@ -43,10 +47,17 @@ } private: - ControlFlowContext(std::unique_ptr Cfg, + // `D` must not be null. + ControlFlowContext(const Decl *D, std::unique_ptr Cfg, llvm::DenseMap StmtToBlock) - : Cfg(std::move(Cfg)), StmtToBlock(std::move(StmtToBlock)) {} + : ContainingDecl(D), Cfg(std::move(Cfg)), + StmtToBlock(std::move(StmtToBlock)) { + assert(D != nullptr); + assert(Cfg != nullptr); + } + /// The `Decl` containing the statement used to construct the CFG. + const Decl *ContainingDecl; std::unique_ptr Cfg; llvm::DenseMap StmtToBlock; }; diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h --- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h @@ -20,7 +20,6 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/Stmt.h" -#include "clang/Analysis/CFG.h" #include "clang/Analysis/FlowSensitive/ControlFlowContext.h" #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" #include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h" diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h --- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h @@ -18,6 +18,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" #include "clang/AST/TypeOrdering.h" +#include "clang/Analysis/FlowSensitive/ControlFlowContext.h" #include "clang/Analysis/FlowSensitive/Solver.h" #include "clang/Analysis/FlowSensitive/StorageLocation.h" #include "clang/Analysis/FlowSensitive/Value.h" @@ -254,6 +255,10 @@ LLVM_DUMP_METHOD void dumpFlowCondition(AtomicBoolValue &Token); + /// Returns the `ControlFlowContext` registered for `F`, if any. Otherwise, + /// returns null. + const ControlFlowContext *getControlFlowContext(const FunctionDecl *F); + private: struct NullableQualTypeDenseMapInfo : private llvm::DenseMapInfo { static QualType getEmptyKey() { @@ -360,6 +365,9 @@ llvm::DenseMap> FlowConditionDeps; llvm::DenseMap FlowConditionConstraints; + + // Keyed on the function's fully qualified name. No leading "::". + llvm::StringMap FunctionModels; }; } // namespace dataflow diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h --- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h @@ -19,6 +19,7 @@ #include "clang/AST/DeclBase.h" #include "clang/AST/Expr.h" #include "clang/AST/Type.h" +#include "clang/Analysis/FlowSensitive/ControlFlowContext.h" #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" #include "clang/Analysis/FlowSensitive/DataflowLattice.h" #include "clang/Analysis/FlowSensitive/StorageLocation.h" @@ -343,6 +344,12 @@ /// imply that `Val` is true. bool flowConditionImplies(BoolValue &Val) const; + /// Returns the `ControlFlowContext` registered for `F`, if any. Otherwise, + /// returns null. + const ControlFlowContext *getControlFlowContext(const FunctionDecl *F) { + return DACtx->getControlFlowContext(F); + } + LLVM_DUMP_METHOD void dump() const; private: diff --git a/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp b/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp --- a/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp +++ b/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp @@ -64,7 +64,7 @@ llvm::DenseMap StmtToBlock = buildStmtToBasicBlockMap(*Cfg); - return ControlFlowContext(std::move(Cfg), std::move(StmtToBlock)); + return ControlFlowContext(D, std::move(Cfg), std::move(StmtToBlock)); } } // namespace dataflow diff --git a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp --- a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp @@ -16,6 +16,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/Analysis/FlowSensitive/DebugSupport.h" #include "clang/Analysis/FlowSensitive/Value.h" +#include "clang/Basic/IdentifierTable.h" #include "llvm/Support/Debug.h" #include #include @@ -335,6 +336,30 @@ llvm::dbgs() << debugString(Constraints, AtomNames); } +const ControlFlowContext * +DataflowAnalysisContext::getControlFlowContext(const FunctionDecl *F) { + // FIXME: This is an expensive approach since we *always* print the fully + // qualified name. Instead, we should prefilter by some means (e.g. checking + // the identifier against a set). + std::string QualName; + llvm::raw_string_ostream OS(QualName); + F->printQualifiedName(OS); + auto It = FunctionModels.find(QualName); + if (It != FunctionModels.end()) + return &It->second; + + if (F->getBody() != nullptr) { + auto CFCtx = + ControlFlowContext::build(F, F->getBody(), &F->getASTContext()); + // FIXME: Handle errors. + assert(CFCtx); + auto Result = FunctionModels.insert({QualName, std::move(*CFCtx)}); + return &Result.first->second; + } + + return nullptr; +} + } // namespace dataflow } // namespace clang diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp --- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp +++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp @@ -507,28 +507,27 @@ return; Env.setStorageLocation(*S, *ArgLoc); } else if (const FunctionDecl *F = S->getDirectCallee()) { - // This case is for context-sensitive analysis, which we only do if we - // have the callee body available in the translation unit. - if (!Options.ContextSensitive || F->getBody() == nullptr) + // This case is for context-sensitive analysis. + if (!Options.ContextSensitive) + return; + + const ControlFlowContext *CFCtx = Env.getControlFlowContext(F); + if (!CFCtx) return; // FIXME: We don't support context-sensitive analysis of recursion, so // we should return early here if `F` is the same as the `FunctionDecl` // holding `S` itself. - auto &ASTCtx = F->getASTContext(); - - // FIXME: Cache these CFGs. - auto CFCtx = ControlFlowContext::build(F, F->getBody(), &ASTCtx); - // FIXME: Handle errors here and below. - assert(CFCtx); auto ExitBlock = CFCtx->getCFG().getExit().getBlockID(); auto CalleeEnv = Env.pushCall(S); - // FIXME: Use the same analysis as the caller for the callee. - DataflowAnalysisOptions Options; - auto Analysis = NoopAnalysis(ASTCtx, Options); + // FIXME: Use the same analysis as the caller for the callee. Note, + // though, that doing so would require support for changing the analysis's + // ASTContext. + auto Analysis = NoopAnalysis(CFCtx->getDecl().getASTContext(), + DataflowAnalysisOptions()); auto BlockToOutputState = dataflow::runDataflowAnalysis(*CFCtx, Analysis, CalleeEnv);