[clang] bb72d0d - [clang][dataflow] Implement transferBranch
Gabor Marton via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 26 06:25:26 PDT 2022
Author: Gabor Marton
Date: 2022-10-26T15:24:55+02:00
New Revision: bb72d0dde29edc2f240ab3d3650544251795ef9f
URL: https://github.com/llvm/llvm-project/commit/bb72d0dde29edc2f240ab3d3650544251795ef9f
DIFF: https://github.com/llvm/llvm-project/commit/bb72d0dde29edc2f240ab3d3650544251795ef9f.diff
LOG: [clang][dataflow] Implement transferBranch
This patch introduces `transferBranch`, which Applies the analysis
transfer function for a given edge from a CFG block of a conditional
statement.
RFC:
https://discourse.llvm.org/t/rfc-clang-dataflow-signanalysis-edgetransfer-branchtransfer/65220
Differential Revision: https://reviews.llvm.org/D133698
Added:
clang/unittests/Analysis/FlowSensitive/TransferBranchTest.cpp
Modified:
clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
clang/unittests/Analysis/FlowSensitive/TestingSupport.h
Removed:
################################################################################
diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
index 6184f22f786c7..52844302b8532 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
@@ -32,6 +32,16 @@
namespace clang {
namespace dataflow {
+template <typename AnalysisT, typename LatticeT, typename = std::void_t<>>
+struct HasTransferBranchFor : std::false_type {};
+
+template <typename AnalysisT, typename LatticeT>
+struct HasTransferBranchFor<
+ AnalysisT, LatticeT,
+ std::void_t<decltype(std::declval<AnalysisT>().transferBranch(
+ std::declval<bool>(), std::declval<const Stmt *>(),
+ std::declval<LatticeT &>(), std::declval<Environment &>()))>>
+ : std::true_type {};
/// Base class template for dataflow analyses built on a single lattice type.
///
/// Requirements:
@@ -101,6 +111,17 @@ class DataflowAnalysis : public TypeErasedDataflowAnalysis {
static_cast<Derived *>(this)->transfer(Element, L, Env);
}
+ void transferBranchTypeErased(bool Branch, const Stmt *Stmt,
+ TypeErasedLattice &E, Environment &Env) final {
+ if constexpr (HasTransferBranchFor<Derived, LatticeT>::value) {
+ Lattice &L = llvm::any_cast<Lattice &>(E.Value);
+ static_cast<Derived *>(this)->transferBranch(Branch, Stmt, L, Env);
+ }
+ // Silence unused parameter warnings.
+ (void)Branch;
+ (void)Stmt;
+ }
+
private:
ASTContext &Context;
};
diff --git a/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h b/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
index c7dccc36a6a2f..fe8b431ef1579 100644
--- a/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
+++ b/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
@@ -84,6 +84,14 @@ class TypeErasedDataflowAnalysis : public Environment::ValueModel {
virtual void transferTypeErased(const CFGElement *, TypeErasedLattice &,
Environment &) = 0;
+ /// Applies the analysis transfer function for a given edge from a CFG block
+ /// of a conditional statement.
+ /// @param Stmt The condition which is responsible for the split in the CFG.
+ /// @param Branch True if the edge goes to the basic block where the
+ /// condition is true.
+ virtual void transferBranchTypeErased(bool Branch, const Stmt *,
+ TypeErasedLattice &, Environment &) = 0;
+
/// If the built-in transfer functions (which model the heap and stack in the
/// `Environment`) are to be applied, returns the options to be passed to
/// them. Otherwise returns empty.
diff --git a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
index b85a72a8a7b71..ec6a8a3d2e525 100644
--- a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -69,54 +69,64 @@ static int blockIndexInPredecessor(const CFGBlock &Pred,
return BlockPos - Pred.succ_begin();
}
+// The return type of the visit functions in TerminatorVisitor. The first
+// element represents the terminator expression (that is the conditional
+// expression in case of a path split in the CFG). The second element
+// represents whether the condition was true or false.
+using TerminatorVisitorRetTy = std::pair<const Expr *, bool>;
+
/// Extends the flow condition of an environment based on a terminator
/// statement.
-class TerminatorVisitor : public ConstStmtVisitor<TerminatorVisitor> {
+class TerminatorVisitor
+ : public ConstStmtVisitor<TerminatorVisitor, TerminatorVisitorRetTy> {
public:
- TerminatorVisitor(const StmtToEnvMap &StmtToEnv, Environment &Env,
+ TerminatorVisitor(TypeErasedDataflowAnalysis &Analysis,
+ const StmtToEnvMap &StmtToEnv, Environment &Env,
int BlockSuccIdx, TransferOptions TransferOpts)
- : StmtToEnv(StmtToEnv), Env(Env), BlockSuccIdx(BlockSuccIdx),
- TransferOpts(TransferOpts) {}
+ : Analysis(Analysis), StmtToEnv(StmtToEnv), Env(Env),
+ BlockSuccIdx(BlockSuccIdx), TransferOpts(TransferOpts) {}
- void VisitIfStmt(const IfStmt *S) {
+ TerminatorVisitorRetTy VisitIfStmt(const IfStmt *S) {
auto *Cond = S->getCond();
assert(Cond != nullptr);
- extendFlowCondition(*Cond);
+ return extendFlowCondition(*Cond);
}
- void VisitWhileStmt(const WhileStmt *S) {
+ TerminatorVisitorRetTy VisitWhileStmt(const WhileStmt *S) {
auto *Cond = S->getCond();
assert(Cond != nullptr);
- extendFlowCondition(*Cond);
+ return extendFlowCondition(*Cond);
}
- void VisitDoStmt(const DoStmt *S) {
+ TerminatorVisitorRetTy VisitDoStmt(const DoStmt *S) {
auto *Cond = S->getCond();
assert(Cond != nullptr);
- extendFlowCondition(*Cond);
+ return extendFlowCondition(*Cond);
}
- void VisitForStmt(const ForStmt *S) {
+ TerminatorVisitorRetTy VisitForStmt(const ForStmt *S) {
auto *Cond = S->getCond();
if (Cond != nullptr)
- extendFlowCondition(*Cond);
+ return extendFlowCondition(*Cond);
+ return {nullptr, false};
}
- void VisitBinaryOperator(const BinaryOperator *S) {
+ TerminatorVisitorRetTy VisitBinaryOperator(const BinaryOperator *S) {
assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
auto *LHS = S->getLHS();
assert(LHS != nullptr);
- extendFlowCondition(*LHS);
+ return extendFlowCondition(*LHS);
}
- void VisitConditionalOperator(const ConditionalOperator *S) {
+ TerminatorVisitorRetTy
+ VisitConditionalOperator(const ConditionalOperator *S) {
auto *Cond = S->getCond();
assert(Cond != nullptr);
- extendFlowCondition(*Cond);
+ return extendFlowCondition(*Cond);
}
private:
- void extendFlowCondition(const Expr &Cond) {
+ TerminatorVisitorRetTy extendFlowCondition(const Expr &Cond) {
// The terminator sub-expression might not be evaluated.
if (Env.getStorageLocation(Cond, SkipPast::None) == nullptr)
transfer(StmtToEnv, Cond, Env, TransferOpts);
@@ -140,14 +150,19 @@ class TerminatorVisitor : public ConstStmtVisitor<TerminatorVisitor> {
Env.setValue(*Loc, *Val);
}
+ bool ConditionValue = true;
// The condition must be inverted for the successor that encompasses the
// "else" branch, if such exists.
- if (BlockSuccIdx == 1)
+ if (BlockSuccIdx == 1) {
Val = &Env.makeNot(*Val);
+ ConditionValue = false;
+ }
Env.addToFlowCondition(*Val);
+ return {&Cond, ConditionValue};
}
+ TypeErasedDataflowAnalysis &Analysis;
const StmtToEnvMap &StmtToEnv;
Environment &Env;
int BlockSuccIdx;
@@ -239,10 +254,16 @@ computeBlockInputState(const CFGBlock &Block, AnalysisContext &AC) {
if (BuiltinTransferOpts) {
if (const Stmt *PredTerminatorStmt = Pred->getTerminatorStmt()) {
const StmtToEnvMapImpl StmtToEnv(AC.CFCtx, AC.BlockStates);
- TerminatorVisitor(StmtToEnv, PredState.Env,
- blockIndexInPredecessor(*Pred, Block),
- *BuiltinTransferOpts)
- .Visit(PredTerminatorStmt);
+ auto [Cond, CondValue] =
+ TerminatorVisitor(Analysis, StmtToEnv, PredState.Env,
+ blockIndexInPredecessor(*Pred, Block),
+ *BuiltinTransferOpts)
+ .Visit(PredTerminatorStmt);
+ if (Cond != nullptr)
+ // FIXME: Call transferBranchTypeErased even if BuiltinTransferOpts
+ // are not set.
+ Analysis.transferBranchTypeErased(CondValue, Cond, PredState.Lattice,
+ PredState.Env);
}
}
diff --git a/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt b/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
index 434d71ca11e79..a5ad105242bbb 100644
--- a/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
+++ b/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
@@ -17,6 +17,7 @@ add_clang_unittest(ClangAnalysisFlowSensitiveTests
SolverTest.cpp
TestingSupport.cpp
TestingSupportTest.cpp
+ TransferBranchTest.cpp
TransferTest.cpp
TypeErasedDataflowAnalysisTest.cpp
UncheckedOptionalAccessModelTest.cpp
diff --git a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h
index bc5a2bf6cbc6f..511ff71ed1d43 100644
--- a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -344,6 +344,7 @@ checkDataflow(AnalysisInputs<AnalysisT> AI,
llvm::any_cast<typename AnalysisT::Lattice>(&State.Lattice.Value);
auto [_, InsertSuccess] =
AnnotationStates.insert({It->second, StateT{*Lattice, State.Env}});
+ (void)_;
(void)InsertSuccess;
assert(InsertSuccess);
};
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferBranchTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferBranchTest.cpp
new file mode 100644
index 0000000000000..df179dddf02fe
--- /dev/null
+++ b/clang/unittests/Analysis/FlowSensitive/TransferBranchTest.cpp
@@ -0,0 +1,114 @@
+//===- unittests/Analysis/FlowSensitive/SignAnalysisTest.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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a test for the transferBranch function of the
+// TypeErasedDataflowAnalysis.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestingSupport.h"
+#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Testing/Support/Annotations.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+
+namespace clang::dataflow::test {
+namespace {
+
+using namespace ast_matchers;
+
+struct TestLattice {
+ llvm::Optional<bool> Branch;
+ static TestLattice bottom() { return {}; }
+
+ // Does not matter for this test, but we must provide some definition of join.
+ LatticeJoinEffect join(const TestLattice &Other) {
+ return LatticeJoinEffect::Unchanged;
+ }
+ friend bool operator==(const TestLattice &Lhs, const TestLattice &Rhs) {
+ return Lhs.Branch == Rhs.Branch;
+ }
+};
+
+class TestPropagationAnalysis
+ : public DataflowAnalysis<TestPropagationAnalysis, TestLattice> {
+public:
+ explicit TestPropagationAnalysis(ASTContext &Context)
+ : DataflowAnalysis<TestPropagationAnalysis, TestLattice>(Context) {}
+ static TestLattice initialElement() { return TestLattice::bottom(); }
+ void transfer(const CFGElement *, TestLattice &, Environment &) {}
+ void transferBranch(bool Branch, const Stmt *S, TestLattice &L,
+ Environment &Env) {
+ L.Branch = Branch;
+ }
+};
+
+using ::testing::UnorderedElementsAre;
+
+template <typename Matcher>
+void runDataflow(llvm::StringRef Code, Matcher VerifyResults,
+ LangStandard::Kind Std = LangStandard::lang_cxx17,
+ llvm::StringRef TargetFun = "fun") {
+ using ast_matchers::hasName;
+ ASSERT_THAT_ERROR(
+ checkDataflow<TestPropagationAnalysis>(
+ AnalysisInputs<TestPropagationAnalysis>(
+ Code, hasName(TargetFun),
+ [](ASTContext &C, Environment &) {
+ return TestPropagationAnalysis(C);
+ })
+ .withASTBuildArgs(
+ {"-fsyntax-only", "-fno-delayed-template-parsing",
+ "-std=" +
+ std::string(LangStandard::getLangStandardForKind(Std)
+ .getName())}),
+ VerifyResults),
+ llvm::Succeeded());
+}
+
+template <typename LatticeT>
+const LatticeT &getLatticeAtAnnotation(
+ const llvm::StringMap<DataflowAnalysisState<LatticeT>> &AnnotationStates,
+ llvm::StringRef Annotation) {
+ auto It = AnnotationStates.find(Annotation);
+ assert(It != AnnotationStates.end());
+ return It->getValue().Lattice;
+}
+
+TEST(TransferBranchTest, IfElse) {
+ std::string Code = R"(
+ void fun(int a) {
+ if (a > 0) {
+ (void)1;
+ // [[p]]
+ } else {
+ (void)0;
+ // [[q]]
+ }
+ }
+ )";
+ runDataflow(
+ Code,
+ [](const llvm::StringMap<DataflowAnalysisState<TestLattice>> &Results,
+ const AnalysisOutputs &) {
+ ASSERT_THAT(Results.keys(), UnorderedElementsAre("p", "q"));
+
+ const TestLattice &LP = getLatticeAtAnnotation(Results, "p");
+ EXPECT_THAT(LP.Branch, Optional(true));
+
+ const TestLattice &LQ = getLatticeAtAnnotation(Results, "q");
+ EXPECT_THAT(LQ.Branch, Optional(false));
+ },
+ LangStandard::lang_cxx17);
+}
+
+} // namespace
+} // namespace clang::dataflow::test
More information about the cfe-commits
mailing list