/// 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 and must not be null.
+ static llvm::Expected<ControlFlowContext> build(const Decl *D, Stmt &S,
+ ASTContext &C);
+
+ // DEPRECATED. Use overload above.
static llvm::Expected<ControlFlowContext> build(const Decl *D, Stmt *S,
ASTContext *C);
+ /// Returns the `Decl` containing the statement used to construct the CFG, if
+ /// available.
+ const Decl *getDecl() const { return ContainingDecl; }
+
/// Returns the CFG that is stored in this context.
const CFG &getCFG() const { return *Cfg; }
}
private:
- ControlFlowContext(std::unique_ptr<CFG> Cfg,
+ // FIXME: Once the deprecated `build` method is removed, mark `D` as "must not
+ // be null" and add an assertion.
+ ControlFlowContext(const Decl *D, std::unique_ptr<CFG> Cfg,
llvm::DenseMap<const Stmt *, const CFGBlock *> StmtToBlock)
- : Cfg(std::move(Cfg)), StmtToBlock(std::move(StmtToBlock)) {}
+ : ContainingDecl(D), Cfg(std::move(Cfg)),
+ StmtToBlock(std::move(StmtToBlock)) {}
+ /// The `Decl` containing the statement used to construct the CFG.
+ const Decl *ContainingDecl;
std::unique_ptr<CFG> Cfg;
llvm::DenseMap<const Stmt *, const CFGBlock *> StmtToBlock;
};
#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"
#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"
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<QualType> {
static QualType getEmptyKey() {
llvm::DenseMap<AtomicBoolValue *, llvm::DenseSet<AtomicBoolValue *>>
FlowConditionDeps;
llvm::DenseMap<AtomicBoolValue *, BoolValue *> FlowConditionConstraints;
+
+ llvm::DenseMap<const FunctionDecl *, ControlFlowContext> FunctionContexts;
};
} // namespace dataflow
#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"
/// 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:
}
llvm::Expected<ControlFlowContext>
-ControlFlowContext::build(const Decl *D, Stmt *S, ASTContext *C) {
+ControlFlowContext::build(const Decl *D, Stmt &S, ASTContext &C) {
CFG::BuildOptions Options;
Options.PruneTriviallyFalseEdges = false;
Options.AddImplicitDtors = true;
// Ensure that all sub-expressions in basic blocks are evaluated.
Options.setAllAlwaysAdd();
- auto Cfg = CFG::buildCFG(D, S, C, Options);
+ auto Cfg = CFG::buildCFG(D, &S, &C, Options);
if (Cfg == nullptr)
return llvm::createStringError(
std::make_error_code(std::errc::invalid_argument),
llvm::DenseMap<const Stmt *, const CFGBlock *> StmtToBlock =
buildStmtToBasicBlockMap(*Cfg);
- return ControlFlowContext(std::move(Cfg), std::move(StmtToBlock));
+ return ControlFlowContext(D, std::move(Cfg), std::move(StmtToBlock));
+}
+
+llvm::Expected<ControlFlowContext>
+ControlFlowContext::build(const Decl *D, Stmt *S, ASTContext *C) {
+ assert(S != nullptr);
+ assert(C != nullptr);
+ return build(D, *S, *C);
}
} // namespace dataflow
llvm::dbgs() << debugString(Constraints, AtomNames);
}
+const ControlFlowContext *
+DataflowAnalysisContext::getControlFlowContext(const FunctionDecl *F) {
+ // Canonicalize the key:
+ F = F->getDefinition();
+ if (F == nullptr)
+ return nullptr;
+ auto It = FunctionContexts.find(F);
+ if (It != FunctionContexts.end())
+ return &It->second;
+
+ if (Stmt *Body = F->getBody()) {
+ auto CFCtx = ControlFlowContext::build(F, *Body, F->getASTContext());
+ // FIXME: Handle errors.
+ assert(CFCtx);
+ auto Result = FunctionContexts.insert({F, std::move(*CFCtx)});
+ return &Result.first->second;
+ }
+
+ return nullptr;
+}
+
} // namespace dataflow
} // namespace clang
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.
+ assert(
+ CFCtx->getDecl() != nullptr &&
+ "ControlFlowContexts in the environment should always carry a decl");
+ auto Analysis = NoopAnalysis(CFCtx->getDecl()->getASTContext(),
+ DataflowAnalysisOptions());
auto BlockToOutputState =
dataflow::runDataflowAnalysis(*CFCtx, Analysis, CalleeEnv);