[clang][dataflow] Update StructValue child when assigning a value

When assigning a value to a storage location of a struct member we
need to also update the value in the corresponding `StructValue`.

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/D120414
This commit is contained in:
Stanislav Gatev 2022-02-23 15:39:26 +00:00
parent ba18c360b2
commit baa0f221d6
3 changed files with 76 additions and 1 deletions

View file

@ -265,6 +265,12 @@ private:
llvm::DenseMap<const StorageLocation *, Value *> LocToVal;
// Maps locations of struct members to symbolic values of the structs that own
// them and the decls of the struct members.
llvm::DenseMap<const StorageLocation *,
std::pair<StructValue *, const ValueDecl *>>
MemberLocToStruct;
// FIXME: Add flow condition constraints.
};

View file

@ -138,6 +138,9 @@ bool Environment::equivalentTo(const Environment &Other,
if (ExprToLoc != Other.ExprToLoc)
return false;
if (MemberLocToStruct != Other.MemberLocToStruct)
return false;
if (LocToVal.size() != Other.LocToVal.size())
return false;
@ -176,6 +179,12 @@ LatticeJoinEffect Environment::join(const Environment &Other,
if (ExprToLocSizeBefore != ExprToLoc.size())
Effect = LatticeJoinEffect::Changed;
const unsigned MemberLocToStructSizeBefore = MemberLocToStruct.size();
MemberLocToStruct =
intersectDenseMaps(MemberLocToStruct, Other.MemberLocToStruct);
if (MemberLocToStructSizeBefore != MemberLocToStruct.size())
Effect = LatticeJoinEffect::Changed;
// Move `LocToVal` so that `Environment::ValueModel::merge` can safely assign
// values to storage locations while this code iterates over the current
// assignments.
@ -285,9 +294,25 @@ void Environment::setValue(const StorageLocation &Loc, Value &Val) {
for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) {
assert(Field != nullptr);
setValue(AggregateLoc.getChild(*Field), StructVal->getChild(*Field));
StorageLocation &FieldLoc = AggregateLoc.getChild(*Field);
MemberLocToStruct[&FieldLoc] = std::make_pair(StructVal, Field);
setValue(FieldLoc, StructVal->getChild(*Field));
}
}
auto IT = MemberLocToStruct.find(&Loc);
if (IT != MemberLocToStruct.end()) {
// `Loc` is the location of a struct member so we need to also update the
// value of the member in the corresponding `StructValue`.
assert(IT->second.first != nullptr);
StructValue &StructVal = *IT->second.first;
assert(IT->second.second != nullptr);
const ValueDecl &Member = *IT->second.second;
StructVal.setChild(Member, Val);
}
}
Value *Environment::getValue(const StorageLocation &Loc) const {

View file

@ -2320,4 +2320,48 @@ TEST_F(TransferTest, StaticMemberRefVarDecl) {
});
}
TEST_F(TransferTest, AssignMemberBeforeCopy) {
std::string Code = R"(
struct A {
int Foo;
};
void target() {
A A1;
A A2;
int Bar;
A1.Foo = Bar;
A2 = A1;
// [[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 ValueDecl *A1Decl = findValueDecl(ASTCtx, "A1");
ASSERT_THAT(A1Decl, NotNull());
const ValueDecl *A2Decl = findValueDecl(ASTCtx, "A2");
ASSERT_THAT(A2Decl, NotNull());
const auto *BarVal =
cast<IntegerValue>(Env.getValue(*BarDecl, SkipPast::None));
const auto *A2Val =
cast<StructValue>(Env.getValue(*A2Decl, SkipPast::None));
EXPECT_EQ(&A2Val->getChild(*FooDecl), BarVal);
});
}
} // namespace