[clang] 8cadac4 - [clang][dataflow] Add equivalence relation `Value` type.
Yitzhak Mandelbaum via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 19 05:44:39 PDT 2022
Author: Yitzhak Mandelbaum
Date: 2022-10-19T12:23:09Z
New Revision: 8cadac41e9f63b2494805042573792cc2cc2a8ea
URL: https://github.com/llvm/llvm-project/commit/8cadac41e9f63b2494805042573792cc2cc2a8ea
DIFF: https://github.com/llvm/llvm-project/commit/8cadac41e9f63b2494805042573792cc2cc2a8ea.diff
LOG: [clang][dataflow] Add equivalence relation `Value` type.
Defines an equivalence relation on the `Value` type to standardize several
places in the code where we replicate the ~same equivalence comparison.
Differential Revision: https://reviews.llvm.org/D135964
Added:
clang/lib/Analysis/FlowSensitive/Value.cpp
clang/unittests/Analysis/FlowSensitive/ValueTest.cpp
Modified:
clang/include/clang/Analysis/FlowSensitive/Value.h
clang/lib/Analysis/FlowSensitive/CMakeLists.txt
clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Analysis/FlowSensitive/Value.h b/clang/include/clang/Analysis/FlowSensitive/Value.h
index b60f93a51965b..334c1388ecf75 100644
--- a/clang/include/clang/Analysis/FlowSensitive/Value.h
+++ b/clang/include/clang/Analysis/FlowSensitive/Value.h
@@ -77,6 +77,17 @@ class Value {
llvm::StringMap<Value *> Properties;
};
+/// An equivalence relation for values. It obeys reflexivity, symmetry and
+/// transitivity. It does *not* include comparison of `Properties`.
+///
+/// Computes equivalence for these subclasses:
+/// * ReferenceValue, PointerValue -- pointee locations are equal. Does not
+/// compute deep equality of `Value` at said location.
+/// * TopBoolValue -- both are `TopBoolValue`s.
+///
+/// Otherwise, falls back to pointer equality.
+bool areEquivalentValues(const Value &Val1, const Value &Val2);
+
/// Models a boolean.
class BoolValue : public Value {
public:
diff --git a/clang/lib/Analysis/FlowSensitive/CMakeLists.txt b/clang/lib/Analysis/FlowSensitive/CMakeLists.txt
index b57ecc97d630f..1a49998c39c20 100644
--- a/clang/lib/Analysis/FlowSensitive/CMakeLists.txt
+++ b/clang/lib/Analysis/FlowSensitive/CMakeLists.txt
@@ -4,6 +4,7 @@ add_clang_library(clangAnalysisFlowSensitive
DataflowEnvironment.cpp
Transfer.cpp
TypeErasedDataflowAnalysis.cpp
+ Value.cpp
WatchedLiteralsSolver.cpp
DebugSupport.cpp
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index 29fb2b7603e30..0b098c43ba3d1 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -49,28 +49,6 @@ llvm::DenseMap<K, V> intersectDenseMaps(const llvm::DenseMap<K, V> &Map1,
return Result;
}
-static bool areEquivalentIndirectionValues(Value *Val1, Value *Val2) {
- if (auto *IndVal1 = dyn_cast<ReferenceValue>(Val1)) {
- auto *IndVal2 = cast<ReferenceValue>(Val2);
- return &IndVal1->getReferentLoc() == &IndVal2->getReferentLoc();
- }
- if (auto *IndVal1 = dyn_cast<PointerValue>(Val1)) {
- auto *IndVal2 = cast<PointerValue>(Val2);
- return &IndVal1->getPointeeLoc() == &IndVal2->getPointeeLoc();
- }
- return false;
-}
-
-/// Returns true if and only if `Val1` is equivalent to `Val2`.
-static bool equivalentValues(QualType Type, Value *Val1,
- const Environment &Env1, Value *Val2,
- const Environment &Env2,
- Environment::ValueModel &Model) {
- return Val1 == Val2 || (isa<TopBoolValue>(Val1) && isa<TopBoolValue>(Val2)) ||
- areEquivalentIndirectionValues(Val1, Val2) ||
- Model.compareEquivalent(Type, *Val1, Env1, *Val2, Env2);
-}
-
/// Attempts to merge distinct values `Val1` and `Val2` in `Env1` and `Env2`,
/// respectively, of the same type `Type`. Merging generally produces a single
/// value that (soundly) approximates the two inputs, although the actual
@@ -93,11 +71,6 @@ static Value *mergeDistinctValues(QualType Type, Value *Val1,
return &MergedVal;
}
- // FIXME: add unit tests that cover this statement.
- if (areEquivalentIndirectionValues(Val1, Val2)) {
- return Val1;
- }
-
// FIXME: Consider destroying `MergedValue` immediately if `ValueModel::merge`
// returns false to avoid storing unneeded values in `DACtx`.
if (Value *MergedVal = MergedEnv.createValue(Type))
@@ -321,7 +294,9 @@ bool Environment::equivalentTo(const Environment &Other,
continue;
assert(It->second != nullptr);
- if (!equivalentValues(Loc->getType(), Val, *this, It->second, Other, Model))
+ if (!areEquivalentValues(*Val, *It->second) &&
+ !Model.compareEquivalent(Loc->getType(), *Val, *this, *It->second,
+ Other))
return false;
}
@@ -372,8 +347,7 @@ LatticeJoinEffect Environment::join(const Environment &Other,
continue;
assert(It->second != nullptr);
- if (Val == It->second ||
- (isa<TopBoolValue>(Val) && isa<TopBoolValue>(It->second))) {
+ if (areEquivalentValues(*Val, *It->second)) {
JoinedEnv.LocToVal.insert({Loc, Val});
continue;
}
diff --git a/clang/lib/Analysis/FlowSensitive/Value.cpp b/clang/lib/Analysis/FlowSensitive/Value.cpp
new file mode 100644
index 0000000000000..84c1ea8ca4e97
--- /dev/null
+++ b/clang/lib/Analysis/FlowSensitive/Value.cpp
@@ -0,0 +1,39 @@
+//===-- Value.cpp -----------------------------------------------*- C++ -*-===//
+//
+// 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 support functions for the `Value` type.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/FlowSensitive/Value.h"
+#include "llvm/Support/Casting.h"
+
+namespace clang {
+namespace dataflow {
+
+static bool areEquivalentIndirectionValues(const Value &Val1,
+ const Value &Val2) {
+ if (auto *IndVal1 = dyn_cast<ReferenceValue>(&Val1)) {
+ auto *IndVal2 = cast<ReferenceValue>(&Val2);
+ return &IndVal1->getReferentLoc() == &IndVal2->getReferentLoc();
+ }
+ if (auto *IndVal1 = dyn_cast<PointerValue>(&Val1)) {
+ auto *IndVal2 = cast<PointerValue>(&Val2);
+ return &IndVal1->getPointeeLoc() == &IndVal2->getPointeeLoc();
+ }
+ return false;
+}
+
+bool areEquivalentValues(const Value &Val1, const Value &Val2) {
+ return &Val1 == &Val2 || (Val1.getKind() == Val2.getKind() &&
+ (isa<TopBoolValue>(&Val1) ||
+ areEquivalentIndirectionValues(Val1, Val2)));
+}
+
+} // namespace dataflow
+} // namespace clang
diff --git a/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt b/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
index 85499ecb30010..0ab80440ce7d9 100644
--- a/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
+++ b/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
@@ -19,6 +19,7 @@ add_clang_unittest(ClangAnalysisFlowSensitiveTests
TypeErasedDataflowAnalysisTest.cpp
SolverTest.cpp
UncheckedOptionalAccessModelTest.cpp
+ ValueTest.cpp
)
clang_target_link_libraries(ClangAnalysisFlowSensitiveTests
diff --git a/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp b/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
index 96f87a8ed555d..e36f207389e4c 100644
--- a/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -517,11 +517,10 @@ class OptionalIntAnalysis final
Type->getAsCXXRecordDecl()->getQualifiedNameAsString() != "OptionalInt")
return false;
- auto *Prop1 = Val1.getProperty("has_value");
+ auto *Prop1 = Val1.getProperty("has_value");
auto *Prop2 = Val2.getProperty("has_value");
- return Prop1 == Prop2 ||
- (Prop1 != nullptr && Prop2 != nullptr && isa<TopBoolValue>(Prop1) &&
- isa<TopBoolValue>(Prop2));
+ assert(Prop1 != nullptr && Prop2 != nullptr);
+ return areEquivalentValues(*Prop1, *Prop2);
}
bool merge(QualType Type, const Value &Val1, const Environment &Env1,
diff --git a/clang/unittests/Analysis/FlowSensitive/ValueTest.cpp b/clang/unittests/Analysis/FlowSensitive/ValueTest.cpp
new file mode 100644
index 0000000000000..2b7341b0839e5
--- /dev/null
+++ b/clang/unittests/Analysis/FlowSensitive/ValueTest.cpp
@@ -0,0 +1,85 @@
+//===- unittests/Analysis/FlowSensitive/ValueTest.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/FlowSensitive/Value.h"
+#include "clang/Analysis/FlowSensitive/StorageLocation.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <memory>
+
+namespace {
+
+using namespace clang;
+using namespace dataflow;
+
+TEST(ValueTest, EquivalenceReflexive) {
+ StructValue V;
+ EXPECT_TRUE(areEquivalentValues(V, V));
+}
+
+TEST(ValueTest, AliasedReferencesEquivalent) {
+ auto L = ScalarStorageLocation(QualType());
+ ReferenceValue V1(L);
+ ReferenceValue V2(L);
+ EXPECT_TRUE(areEquivalentValues(V1, V2));
+ EXPECT_TRUE(areEquivalentValues(V2, V1));
+}
+
+TEST(ValueTest, AliasedPointersEquivalent) {
+ auto L = ScalarStorageLocation(QualType());
+ PointerValue V1(L);
+ PointerValue V2(L);
+ EXPECT_TRUE(areEquivalentValues(V1, V2));
+ EXPECT_TRUE(areEquivalentValues(V2, V1));
+}
+
+TEST(ValueTest, TopsEquivalent) {
+ TopBoolValue V1;
+ TopBoolValue V2;
+ EXPECT_TRUE(areEquivalentValues(V1, V2));
+ EXPECT_TRUE(areEquivalentValues(V2, V1));
+}
+
+TEST(ValueTest, EquivalentValuesWithDifferentPropsEquivalent) {
+ TopBoolValue Prop1;
+ TopBoolValue Prop2;
+ TopBoolValue V1;
+ TopBoolValue V2;
+ V1.setProperty("foo", Prop1);
+ V2.setProperty("bar", Prop2);
+ EXPECT_TRUE(areEquivalentValues(V1, V2));
+ EXPECT_TRUE(areEquivalentValues(V2, V1));
+}
+
+TEST(ValueTest, DifferentKindsNotEquivalent) {
+ auto L = ScalarStorageLocation(QualType());
+ ReferenceValue V1(L);
+ TopBoolValue V2;
+ EXPECT_FALSE(areEquivalentValues(V1, V2));
+ EXPECT_FALSE(areEquivalentValues(V2, V1));
+}
+
+TEST(ValueTest, NotAliasedReferencesNotEquivalent) {
+ auto L1 = ScalarStorageLocation(QualType());
+ ReferenceValue V1(L1);
+ auto L2 = ScalarStorageLocation(QualType());
+ ReferenceValue V2(L2);
+ EXPECT_FALSE(areEquivalentValues(V1, V2));
+ EXPECT_FALSE(areEquivalentValues(V2, V1));
+}
+
+TEST(ValueTest, NotAliasedPointersNotEquivalent) {
+ auto L1 = ScalarStorageLocation(QualType());
+ PointerValue V1(L1);
+ auto L2 = ScalarStorageLocation(QualType());
+ PointerValue V2(L2);
+ EXPECT_FALSE(areEquivalentValues(V1, V2));
+ EXPECT_FALSE(areEquivalentValues(V2, V1));
+}
+
+} // namespace
More information about the cfe-commits
mailing list