[clang] [analyzer][NFC] Make RegionStore dumps deterministic (PR #115615)

DonĂ¡t Nagy via cfe-commits cfe-commits at lists.llvm.org
Mon Nov 11 08:30:37 PST 2024


================
@@ -232,27 +233,86 @@ class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *,
 
   void printJson(raw_ostream &Out, const char *NL = "\n",
                  unsigned int Space = 0, bool IsDot = false) const {
-    for (iterator I = begin(), E = end(); I != E; ++I) {
-      // TODO: We might need a .printJson for I.getKey() as well.
+    using namespace llvm;
+    DenseMap<const MemRegion *, std::string> StringifyCache;
+    auto ToString = [&StringifyCache](const MemRegion *R) {
+      auto [Place, Inserted] = StringifyCache.try_emplace(R);
+      if (!Inserted)
+        return Place->second;
+      std::string Res;
+      raw_string_ostream OS(Res);
+      OS << R;
+      Place->second = Res;
+      return Res;
+    };
+
+    using Cluster =
+        std::pair<const MemRegion *, ImmutableMap<BindingKey, SVal>>;
+    using Binding = std::pair<BindingKey, SVal>;
+
+    const auto MemSpaceBeforeRegionName = [&ToString](const Cluster *L,
+                                                      const Cluster *R) {
+      if (isa<MemSpaceRegion>(L->first) && !isa<MemSpaceRegion>(R->first))
+        return true;
+      if (!isa<MemSpaceRegion>(L->first) && isa<MemSpaceRegion>(R->first))
+        return false;
+      return ToString(L->first) < ToString(R->first);
+    };
+
+    const auto SymbolicBeforeOffset = [&ToString](const BindingKey &L,
+                                                  const BindingKey &R) {
+      if (L.hasSymbolicOffset() && !R.hasSymbolicOffset())
+        return true;
+      if (!L.hasSymbolicOffset() && R.hasSymbolicOffset())
+        return false;
+      if (L.hasSymbolicOffset() && R.hasSymbolicOffset())
+        return ToString(L.getRegion()) < ToString(R.getRegion());
+      return L.getOffset() < R.getOffset();
+    };
+
+    const auto DefaultBindingBeforeDirectBindings =
+        [&SymbolicBeforeOffset](const Binding *LPtr, const Binding *RPtr) {
+          const BindingKey &L = LPtr->first;
+          const BindingKey &R = RPtr->first;
+          if (L.isDefault() && !R.isDefault())
+            return true;
+          if (!L.isDefault() && R.isDefault())
+            return false;
+          assert(L.isDefault() == R.isDefault());
+          return SymbolicBeforeOffset(L, R);
+        };
+
----------------
NagyDonat wrote:

I think it the implementation of these comparison function could be made clearer if you introduce them as the lexicographical ordering of some tuples derived from the sorted values. For example, to sort the bindings, you could write something like 
```cpp
// Tweak the definition of ToString to ensure that ToString(nullptr) does not crash.
const auto BindingSortKey[&ToString](const Binding *BPtr) {
  const BindingKey &BK = BP->first;
  return std::make_tuple(BK.isDirect(), !BK.hasSymbolicOffset(), ToString(BK.getRegion()), BK.getOffset());
}
const auto CompareBindings[&BindingSortKey](const Binding *LPtr, const Binding *RPtr) {
  return BindingSortKey(LPtr) < BindingSortKey(RPtr);
}
```
and this `CompareBindings` is equivalent to your lambda `DefaultBindingBeforeDirectBindings` with a significantly shorter code (disclaimer: untested code, you might need to tweak it if I miscalculated something in my head).

I presume that the compiler will be able to inline these definitions, so this implementation won't be slower than your hand-unrolled lexicographical comparison.

https://github.com/llvm/llvm-project/pull/115615


More information about the cfe-commits mailing list