[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