Skip to content

Commit 7ea103d

Browse files
committed
[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
1 parent ef9a659 commit 7ea103d

File tree

4 files changed

+241
-0
lines changed

4 files changed

+241
-0
lines changed

clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h

+5
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ enum class SkipPast {
4949
};
5050

5151
/// Holds the state of the program (store and heap) at a given program point.
52+
///
53+
/// WARNING: Symbolic values that are created by the environment for static
54+
/// local and global variables are not currently invalidated on function calls.
55+
/// This is unsound and should be taken into account when designing dataflow
56+
/// analyses.
5257
class Environment {
5358
public:
5459
/// Supplements `Environment` with non-standard comparison and join

clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

+46
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "llvm/ADT/DenseMap.h"
2323
#include "llvm/ADT/DenseSet.h"
2424
#include "llvm/Support/ErrorHandling.h"
25+
#include <cassert>
2526
#include <memory>
2627
#include <utility>
2728

@@ -56,10 +57,55 @@ static bool equivalentValues(QualType Type, Value *Val1, Value *Val2,
5657
return Model.compareEquivalent(Type, *Val1, *Val2);
5758
}
5859

60+
/// Initializes a global storage value.
61+
static void initGlobalVar(const VarDecl &D, Environment &Env) {
62+
if (!D.hasGlobalStorage() ||
63+
Env.getStorageLocation(D, SkipPast::None) != nullptr)
64+
return;
65+
66+
auto &Loc = Env.createStorageLocation(D);
67+
Env.setStorageLocation(D, Loc);
68+
if (auto *Val = Env.createValue(D.getType()))
69+
Env.setValue(Loc, *Val);
70+
}
71+
72+
/// Initializes a global storage value.
73+
static void initGlobalVar(const Decl &D, Environment &Env) {
74+
if (auto *V = dyn_cast<VarDecl>(&D))
75+
initGlobalVar(*V, Env);
76+
}
77+
78+
/// Initializes global storage values that are declared or referenced from
79+
/// sub-statements of `S`.
80+
// FIXME: Add support for resetting globals after function calls to enable
81+
// the implementation of sound analyses.
82+
static void initGlobalVars(const Stmt &S, Environment &Env) {
83+
for (auto *Child : S.children()) {
84+
if (Child != nullptr)
85+
initGlobalVars(*Child, Env);
86+
}
87+
88+
if (auto *DS = dyn_cast<DeclStmt>(&S)) {
89+
if (DS->isSingleDecl()) {
90+
const auto &D = *cast<VarDecl>(DS->getSingleDecl());
91+
initGlobalVar(D, Env);
92+
} else {
93+
for (auto *D : DS->getDeclGroup())
94+
initGlobalVar(*D, Env);
95+
}
96+
} else if (auto *E = dyn_cast<DeclRefExpr>(&S)) {
97+
initGlobalVar(*E->getDecl(), Env);
98+
} else if (auto *E = dyn_cast<MemberExpr>(&S)) {
99+
initGlobalVar(*E->getMemberDecl(), Env);
100+
}
101+
}
102+
59103
Environment::Environment(DataflowAnalysisContext &DACtx,
60104
const DeclContext &DeclCtx)
61105
: Environment(DACtx) {
62106
if (const auto *FuncDecl = dyn_cast<FunctionDecl>(&DeclCtx)) {
107+
assert(FuncDecl->getBody() != nullptr);
108+
initGlobalVars(*FuncDecl->getBody(), *this);
63109
for (const auto *ParamDecl : FuncDecl->parameters()) {
64110
assert(ParamDecl != nullptr);
65111
auto &ParamLoc = createStorageLocation(*ParamDecl);

clang/lib/Analysis/FlowSensitive/Transfer.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
136136
// Group decls are converted into single decls in the CFG so the cast below
137137
// is safe.
138138
const auto &D = *cast<VarDecl>(S->getSingleDecl());
139+
140+
// Static local vars are already initialized in `Environment`.
141+
if (D.hasGlobalStorage())
142+
return;
143+
139144
auto &Loc = Env.createStorageLocation(D);
140145
Env.setStorageLocation(D, Loc);
141146

@@ -291,6 +296,24 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
291296
if (Member->isFunctionOrFunctionTemplate())
292297
return;
293298

299+
if (auto *D = dyn_cast<VarDecl>(Member)) {
300+
if (D->hasGlobalStorage()) {
301+
auto *VarDeclLoc = Env.getStorageLocation(*D, SkipPast::None);
302+
if (VarDeclLoc == nullptr)
303+
return;
304+
305+
if (VarDeclLoc->getType()->isReferenceType()) {
306+
Env.setStorageLocation(*S, *VarDeclLoc);
307+
} else {
308+
auto &Loc = Env.createStorageLocation(*S);
309+
Env.setStorageLocation(*S, Loc);
310+
Env.setValue(Loc, Env.takeOwnership(
311+
std::make_unique<ReferenceValue>(*VarDeclLoc)));
312+
}
313+
return;
314+
}
315+
}
316+
294317
// The receiver can be either a value or a pointer to a value. Skip past the
295318
// indirection to handle both cases.
296319
auto *BaseLoc = cast_or_null<AggregateStorageLocation>(

clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

+167
Original file line numberDiff line numberDiff line change
@@ -2153,4 +2153,171 @@ TEST_F(TransferTest, AssignFromBoolNegation) {
21532153
});
21542154
}
21552155

2156+
TEST_F(TransferTest, StaticIntSingleVarDecl) {
2157+
std::string Code = R"(
2158+
void target() {
2159+
static int Foo;
2160+
// [[p]]
2161+
}
2162+
)";
2163+
runDataflow(Code,
2164+
[](llvm::ArrayRef<
2165+
std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
2166+
Results,
2167+
ASTContext &ASTCtx) {
2168+
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
2169+
const Environment &Env = Results[0].second.Env;
2170+
2171+
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
2172+
ASSERT_THAT(FooDecl, NotNull());
2173+
2174+
const StorageLocation *FooLoc =
2175+
Env.getStorageLocation(*FooDecl, SkipPast::None);
2176+
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
2177+
2178+
const Value *FooVal = Env.getValue(*FooLoc);
2179+
EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
2180+
});
2181+
}
2182+
2183+
TEST_F(TransferTest, StaticIntGroupVarDecl) {
2184+
std::string Code = R"(
2185+
void target() {
2186+
static int Foo, Bar;
2187+
(void)0;
2188+
// [[p]]
2189+
}
2190+
)";
2191+
runDataflow(Code,
2192+
[](llvm::ArrayRef<
2193+
std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
2194+
Results,
2195+
ASTContext &ASTCtx) {
2196+
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
2197+
const Environment &Env = Results[0].second.Env;
2198+
2199+
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
2200+
ASSERT_THAT(FooDecl, NotNull());
2201+
2202+
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
2203+
ASSERT_THAT(BarDecl, NotNull());
2204+
2205+
const StorageLocation *FooLoc =
2206+
Env.getStorageLocation(*FooDecl, SkipPast::None);
2207+
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
2208+
2209+
const StorageLocation *BarLoc =
2210+
Env.getStorageLocation(*BarDecl, SkipPast::None);
2211+
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
2212+
2213+
const Value *FooVal = Env.getValue(*FooLoc);
2214+
EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
2215+
2216+
const Value *BarVal = Env.getValue(*BarLoc);
2217+
EXPECT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
2218+
2219+
EXPECT_NE(FooVal, BarVal);
2220+
});
2221+
}
2222+
2223+
TEST_F(TransferTest, GlobalIntVarDecl) {
2224+
std::string Code = R"(
2225+
static int Foo;
2226+
2227+
void target() {
2228+
int Bar = Foo;
2229+
int Baz = Foo;
2230+
// [[p]]
2231+
}
2232+
)";
2233+
runDataflow(Code,
2234+
[](llvm::ArrayRef<
2235+
std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
2236+
Results,
2237+
ASTContext &ASTCtx) {
2238+
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
2239+
const Environment &Env = Results[0].second.Env;
2240+
2241+
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
2242+
ASSERT_THAT(BarDecl, NotNull());
2243+
2244+
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
2245+
ASSERT_THAT(BazDecl, NotNull());
2246+
2247+
const Value *BarVal =
2248+
cast<IntegerValue>(Env.getValue(*BarDecl, SkipPast::None));
2249+
const Value *BazVal =
2250+
cast<IntegerValue>(Env.getValue(*BazDecl, SkipPast::None));
2251+
EXPECT_EQ(BarVal, BazVal);
2252+
});
2253+
}
2254+
2255+
TEST_F(TransferTest, StaticMemberIntVarDecl) {
2256+
std::string Code = R"(
2257+
struct A {
2258+
static int Foo;
2259+
};
2260+
2261+
void target(A a) {
2262+
int Bar = a.Foo;
2263+
int Baz = a.Foo;
2264+
// [[p]]
2265+
}
2266+
)";
2267+
runDataflow(Code,
2268+
[](llvm::ArrayRef<
2269+
std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
2270+
Results,
2271+
ASTContext &ASTCtx) {
2272+
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
2273+
const Environment &Env = Results[0].second.Env;
2274+
2275+
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
2276+
ASSERT_THAT(BarDecl, NotNull());
2277+
2278+
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
2279+
ASSERT_THAT(BazDecl, NotNull());
2280+
2281+
const Value *BarVal =
2282+
cast<IntegerValue>(Env.getValue(*BarDecl, SkipPast::None));
2283+
const Value *BazVal =
2284+
cast<IntegerValue>(Env.getValue(*BazDecl, SkipPast::None));
2285+
EXPECT_EQ(BarVal, BazVal);
2286+
});
2287+
}
2288+
2289+
TEST_F(TransferTest, StaticMemberRefVarDecl) {
2290+
std::string Code = R"(
2291+
struct A {
2292+
static int &Foo;
2293+
};
2294+
2295+
void target(A a) {
2296+
int Bar = a.Foo;
2297+
int Baz = a.Foo;
2298+
// [[p]]
2299+
}
2300+
)";
2301+
runDataflow(Code,
2302+
[](llvm::ArrayRef<
2303+
std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
2304+
Results,
2305+
ASTContext &ASTCtx) {
2306+
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
2307+
const Environment &Env = Results[0].second.Env;
2308+
2309+
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
2310+
ASSERT_THAT(BarDecl, NotNull());
2311+
2312+
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
2313+
ASSERT_THAT(BazDecl, NotNull());
2314+
2315+
const Value *BarVal =
2316+
cast<IntegerValue>(Env.getValue(*BarDecl, SkipPast::None));
2317+
const Value *BazVal =
2318+
cast<IntegerValue>(Env.getValue(*BazDecl, SkipPast::None));
2319+
EXPECT_EQ(BarVal, BazVal);
2320+
});
2321+
}
2322+
21562323
} // namespace

0 commit comments

Comments
 (0)