[clang] 7ea103d - [clang][dataflow] Add support for global storage values
Stanislav Gatev via cfe-commits
cfe-commits at lists.llvm.org
Wed Feb 23 00:28:53 PST 2022
Author: Stanislav Gatev
Date: 2022-02-23T08:27:58Z
New Revision: 7ea103de140b59a64fc884fa90afd2213619384d
URL: https://github.com/llvm/llvm-project/commit/7ea103de140b59a64fc884fa90afd2213619384d
DIFF: https://github.com/llvm/llvm-project/commit/7ea103de140b59a64fc884fa90afd2213619384d.diff
LOG: [clang][dataflow] Add support for global storage values
This is part of the implementation of the dataflow analysis framework.
See "[RFC] A dataflow analysis framework for Clang AST" on cfe-dev.
Reviewed-by: ymandel, xazax.hun
Differential Revision: https://reviews.llvm.org/D120149
Added:
Modified:
clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
clang/lib/Analysis/FlowSensitive/Transfer.cpp
clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
index af613c95bb8dc..bab20418a016a 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -49,6 +49,11 @@ enum class SkipPast {
};
/// Holds the state of the program (store and heap) at a given program point.
+///
+/// WARNING: Symbolic values that are created by the environment for static
+/// local and global variables are not currently invalidated on function calls.
+/// This is unsound and should be taken into account when designing dataflow
+/// analyses.
class Environment {
public:
/// Supplements `Environment` with non-standard comparison and join
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index eca58b313761b..f20c747c56c2d 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -22,6 +22,7 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/Support/ErrorHandling.h"
+#include <cassert>
#include <memory>
#include <utility>
@@ -56,10 +57,55 @@ static bool equivalentValues(QualType Type, Value *Val1, Value *Val2,
return Model.compareEquivalent(Type, *Val1, *Val2);
}
+/// Initializes a global storage value.
+static void initGlobalVar(const VarDecl &D, Environment &Env) {
+ if (!D.hasGlobalStorage() ||
+ Env.getStorageLocation(D, SkipPast::None) != nullptr)
+ return;
+
+ auto &Loc = Env.createStorageLocation(D);
+ Env.setStorageLocation(D, Loc);
+ if (auto *Val = Env.createValue(D.getType()))
+ Env.setValue(Loc, *Val);
+}
+
+/// Initializes a global storage value.
+static void initGlobalVar(const Decl &D, Environment &Env) {
+ if (auto *V = dyn_cast<VarDecl>(&D))
+ initGlobalVar(*V, Env);
+}
+
+/// Initializes global storage values that are declared or referenced from
+/// sub-statements of `S`.
+// FIXME: Add support for resetting globals after function calls to enable
+// the implementation of sound analyses.
+static void initGlobalVars(const Stmt &S, Environment &Env) {
+ for (auto *Child : S.children()) {
+ if (Child != nullptr)
+ initGlobalVars(*Child, Env);
+ }
+
+ if (auto *DS = dyn_cast<DeclStmt>(&S)) {
+ if (DS->isSingleDecl()) {
+ const auto &D = *cast<VarDecl>(DS->getSingleDecl());
+ initGlobalVar(D, Env);
+ } else {
+ for (auto *D : DS->getDeclGroup())
+ initGlobalVar(*D, Env);
+ }
+ } else if (auto *E = dyn_cast<DeclRefExpr>(&S)) {
+ initGlobalVar(*E->getDecl(), Env);
+ } else if (auto *E = dyn_cast<MemberExpr>(&S)) {
+ initGlobalVar(*E->getMemberDecl(), Env);
+ }
+}
+
Environment::Environment(DataflowAnalysisContext &DACtx,
const DeclContext &DeclCtx)
: Environment(DACtx) {
if (const auto *FuncDecl = dyn_cast<FunctionDecl>(&DeclCtx)) {
+ assert(FuncDecl->getBody() != nullptr);
+ initGlobalVars(*FuncDecl->getBody(), *this);
for (const auto *ParamDecl : FuncDecl->parameters()) {
assert(ParamDecl != nullptr);
auto &ParamLoc = createStorageLocation(*ParamDecl);
diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index cd9b8b0e454e4..4b5d23593a4bd 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -136,6 +136,11 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
// Group decls are converted into single decls in the CFG so the cast below
// is safe.
const auto &D = *cast<VarDecl>(S->getSingleDecl());
+
+ // Static local vars are already initialized in `Environment`.
+ if (D.hasGlobalStorage())
+ return;
+
auto &Loc = Env.createStorageLocation(D);
Env.setStorageLocation(D, Loc);
@@ -291,6 +296,24 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
if (Member->isFunctionOrFunctionTemplate())
return;
+ if (auto *D = dyn_cast<VarDecl>(Member)) {
+ if (D->hasGlobalStorage()) {
+ auto *VarDeclLoc = Env.getStorageLocation(*D, SkipPast::None);
+ if (VarDeclLoc == nullptr)
+ return;
+
+ if (VarDeclLoc->getType()->isReferenceType()) {
+ Env.setStorageLocation(*S, *VarDeclLoc);
+ } else {
+ auto &Loc = Env.createStorageLocation(*S);
+ Env.setStorageLocation(*S, Loc);
+ Env.setValue(Loc, Env.takeOwnership(
+ std::make_unique<ReferenceValue>(*VarDeclLoc)));
+ }
+ return;
+ }
+ }
+
// The receiver can be either a value or a pointer to a value. Skip past the
// indirection to handle both cases.
auto *BaseLoc = cast_or_null<AggregateStorageLocation>(
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index 83ccba1a25382..fda4af435c4a7 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -2153,4 +2153,171 @@ TEST_F(TransferTest, AssignFromBoolNegation) {
});
}
+TEST_F(TransferTest, StaticIntSingleVarDecl) {
+ std::string Code = R"(
+ void target() {
+ static int Foo;
+ // [[p]]
+ }
+ )";
+ runDataflow(Code,
+ [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ const StorageLocation *FooLoc =
+ Env.getStorageLocation(*FooDecl, SkipPast::None);
+ ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
+
+ const Value *FooVal = Env.getValue(*FooLoc);
+ EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
+ });
+}
+
+TEST_F(TransferTest, StaticIntGroupVarDecl) {
+ std::string Code = R"(
+ void target() {
+ static int Foo, Bar;
+ (void)0;
+ // [[p]]
+ }
+ )";
+ runDataflow(Code,
+ [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ const StorageLocation *FooLoc =
+ Env.getStorageLocation(*FooDecl, SkipPast::None);
+ ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
+
+ const StorageLocation *BarLoc =
+ Env.getStorageLocation(*BarDecl, SkipPast::None);
+ ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
+
+ const Value *FooVal = Env.getValue(*FooLoc);
+ EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
+
+ const Value *BarVal = Env.getValue(*BarLoc);
+ EXPECT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
+
+ EXPECT_NE(FooVal, BarVal);
+ });
+}
+
+TEST_F(TransferTest, GlobalIntVarDecl) {
+ std::string Code = R"(
+ static int Foo;
+
+ void target() {
+ int Bar = Foo;
+ int Baz = Foo;
+ // [[p]]
+ }
+ )";
+ runDataflow(Code,
+ [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
+ ASSERT_THAT(BazDecl, NotNull());
+
+ const Value *BarVal =
+ cast<IntegerValue>(Env.getValue(*BarDecl, SkipPast::None));
+ const Value *BazVal =
+ cast<IntegerValue>(Env.getValue(*BazDecl, SkipPast::None));
+ EXPECT_EQ(BarVal, BazVal);
+ });
+}
+
+TEST_F(TransferTest, StaticMemberIntVarDecl) {
+ std::string Code = R"(
+ struct A {
+ static int Foo;
+ };
+
+ void target(A a) {
+ int Bar = a.Foo;
+ int Baz = a.Foo;
+ // [[p]]
+ }
+ )";
+ runDataflow(Code,
+ [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
+ ASSERT_THAT(BazDecl, NotNull());
+
+ const Value *BarVal =
+ cast<IntegerValue>(Env.getValue(*BarDecl, SkipPast::None));
+ const Value *BazVal =
+ cast<IntegerValue>(Env.getValue(*BazDecl, SkipPast::None));
+ EXPECT_EQ(BarVal, BazVal);
+ });
+}
+
+TEST_F(TransferTest, StaticMemberRefVarDecl) {
+ std::string Code = R"(
+ struct A {
+ static int &Foo;
+ };
+
+ void target(A a) {
+ int Bar = a.Foo;
+ int Baz = a.Foo;
+ // [[p]]
+ }
+ )";
+ runDataflow(Code,
+ [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
+ ASSERT_THAT(BazDecl, NotNull());
+
+ const Value *BarVal =
+ cast<IntegerValue>(Env.getValue(*BarDecl, SkipPast::None));
+ const Value *BazVal =
+ cast<IntegerValue>(Env.getValue(*BazDecl, SkipPast::None));
+ EXPECT_EQ(BarVal, BazVal);
+ });
+}
+
} // namespace
More information about the cfe-commits
mailing list