llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: None (rymrg)

<details>
<summary>Changes</summary>

Initial RSan without value extension

https://discourse.llvm.org/t/rfc-robustess-sanitizer/86831/

Paper: https://doi.org/10.1145/3729277
Preprint version: https://arxiv.org/pdf/2504.15036

Proper race detection depends on 
https://github.com/llvm/llvm-project/pull/142579

---

Patch is 85.83 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/145540.diff


21 Files Affected:

- (modified) compiler-rt/lib/tsan/rtl/CMakeLists.txt (+7) 
- (added) compiler-rt/lib/tsan/rtl/rsan.cpp (+8) 
- (added) compiler-rt/lib/tsan/rtl/rsan_action.hpp (+97) 
- (added) compiler-rt/lib/tsan/rtl/rsan_arena.hpp (+45) 
- (added) compiler-rt/lib/tsan/rtl/rsan_defs.hpp (+96) 
- (added) compiler-rt/lib/tsan/rtl/rsan_dense_map.h (+714) 
- (added) compiler-rt/lib/tsan/rtl/rsan_instrument.hpp (+358) 
- (added) compiler-rt/lib/tsan/rtl/rsan_lock.hpp (+33) 
- (added) compiler-rt/lib/tsan/rtl/rsan_map.hpp (+88) 
- (added) compiler-rt/lib/tsan/rtl/rsan_memoryorder.hpp (+65) 
- (added) compiler-rt/lib/tsan/rtl/rsan_report.cpp (+72) 
- (added) compiler-rt/lib/tsan/rtl/rsan_report.hpp (+85) 
- (added) compiler-rt/lib/tsan/rtl/rsan_robustnessmodel.hpp (+280) 
- (added) compiler-rt/lib/tsan/rtl/rsan_stacktrace.cpp (+134) 
- (added) compiler-rt/lib/tsan/rtl/rsan_stacktrace.hpp (+92) 
- (added) compiler-rt/lib/tsan/rtl/rsan_vector.h (+178) 
- (added) compiler-rt/lib/tsan/rtl/rsan_vectorclock.hpp (+115) 
- (modified) compiler-rt/lib/tsan/rtl/tsan_flags.inc (+3) 
- (modified) compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp (+80-27) 
- (modified) compiler-rt/lib/tsan/rtl/tsan_mman.cpp (+8) 
- (modified) compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp (+11) 


``````````diff
diff --git a/compiler-rt/lib/tsan/rtl/CMakeLists.txt 
b/compiler-rt/lib/tsan/rtl/CMakeLists.txt
index d7d84706bfd58..eb5f4a84fa359 100644
--- a/compiler-rt/lib/tsan/rtl/CMakeLists.txt
+++ b/compiler-rt/lib/tsan/rtl/CMakeLists.txt
@@ -49,6 +49,9 @@ set(TSAN_SOURCES
   tsan_symbolize.cpp
   tsan_sync.cpp
   tsan_vector_clock.cpp
+  rsan.cpp
+  rsan_report.cpp
+  rsan_stacktrace.cpp
   )
 
 set(TSAN_CXX_SOURCES
@@ -59,6 +62,10 @@ set(TSAN_PREINIT_SOURCES
   tsan_preinit.cpp
   )
 
+set_source_files_properties(tsan_interface_atomic.cpp PROPERTIES COMPILE_FLAGS 
-std=c++20)
+set_source_files_properties(tsan_mman.cpp PROPERTIES COMPILE_FLAGS -std=c++20)
+set_source_files_properties(tsan_rtl_mutex.cpp PROPERTIES COMPILE_FLAGS 
-std=c++20)
+
 if(APPLE)
   list(APPEND TSAN_SOURCES
     tsan_interceptors_mac.cpp
diff --git a/compiler-rt/lib/tsan/rtl/rsan.cpp 
b/compiler-rt/lib/tsan/rtl/rsan.cpp
new file mode 100644
index 0000000000000..fb696eb277b98
--- /dev/null
+++ b/compiler-rt/lib/tsan/rtl/rsan.cpp
@@ -0,0 +1,8 @@
+#include "rsan_vectorclock.hpp"
+#include "rsan_robustnessmodel.hpp"
+#include "rsan_instrument.hpp"
+#include "rsan_map.hpp"
+#include "rsan_arena.hpp"
+
+namespace Robustness{
+} // namespace Robustness
diff --git a/compiler-rt/lib/tsan/rtl/rsan_action.hpp 
b/compiler-rt/lib/tsan/rtl/rsan_action.hpp
new file mode 100644
index 0000000000000..a066b4e6ea8fc
--- /dev/null
+++ b/compiler-rt/lib/tsan/rtl/rsan_action.hpp
@@ -0,0 +1,97 @@
+#pragma once
+#include "rsan_defs.hpp"
+namespace Robustness::Action{
+       struct StoreAction{
+               ThreadId tid;
+               Address addr;
+               int size;
+       };
+       struct LoadAction{
+               ThreadId tid;
+               Address addr;
+               int size;
+       };
+       struct AtomicVerifyAction{
+               ThreadId tid;
+               Address addr;
+               morder mo;
+               int size;
+       };
+       struct AtomicVerifyStoreAction{
+               ThreadId tid;
+               Address addr;
+               morder mo;
+               int size;
+       };
+       struct AtomicLoadAction{
+               ThreadId tid;
+               Address addr;
+               morder mo;
+               int size;
+               bool rmw;
+               DebugInfo dbg;
+       };
+       struct AtomicStoreAction{
+               ThreadId tid;
+               Address addr;
+               morder mo;
+               int size;
+               uint64_t oldValue;
+               uint64_t newValue;
+               DebugInfo dbg;
+       };
+       struct AtomicRMWAction{
+               ThreadId tid;
+               Address addr;
+               morder mo;
+               int size;
+               uint64_t oldValue;
+               uint64_t newValue;
+               DebugInfo dbg;
+       };
+       struct AtomicCasAction{
+               ThreadId tid;
+               Address addr;
+               morder mo;
+               int size;
+               uint64_t oldValue;
+               uint64_t newValue;
+               bool success;
+               DebugInfo dbg;
+       };
+       struct FenceAction{
+               ThreadId tid;
+               morder mo;
+       };
+       struct TrackAction{
+               ThreadId tid;
+               Address addr;
+               uint64_t value;
+       };
+       struct WaitAction{
+               ThreadId tid;
+               Address addr;
+               uint64_t value;
+               DebugInfo dbg;
+       };
+       struct BcasAction{
+               ThreadId tid;
+               Address addr;
+               uint64_t value;
+               DebugInfo dbg;
+       };
+       struct ThreadCreate{
+               ThreadId creator, createe;
+       };
+       struct ThreadJoin{
+               ThreadId absorber, absorbee;
+       };
+       struct Free{
+               ThreadId tid;
+               Address addr;
+               uptr size;
+               DebugInfo dbg;
+       };
+}
+
+
diff --git a/compiler-rt/lib/tsan/rtl/rsan_arena.hpp 
b/compiler-rt/lib/tsan/rtl/rsan_arena.hpp
new file mode 100644
index 0000000000000..95fe3c5229942
--- /dev/null
+++ b/compiler-rt/lib/tsan/rtl/rsan_arena.hpp
@@ -0,0 +1,45 @@
+#pragma once
+#include "rsan_vector.h"
+#include "rsan_defs.hpp"
+#include "sanitizer_common/sanitizer_placement_new.h"
+
+namespace Robustness {
+       template< class T >
+               class Arena {
+
+                       //const FACTOR = 2;
+                       static const u8 BASE = 8;
+
+                       u64 cv = 0;
+                       u64 ci = 0;
+
+                       Vector<Vector<T>> vs;
+                       Arena(const Arena&) = delete;
+
+
+                       public:
+                       Arena() = default;
+                       ~Arena() {
+                               for (auto& v : vs)
+                                       v.clear();
+                       }
+
+                       T* allocate(){
+                               if (cv == vs.size()){
+                                       vs.push_back();
+                                       vs[cv].resize(BASE << (cv));
+                                       ci = 0;
+                               }
+                               DCHECK_GT(vs.size(), cv);
+                               DCHECK_GT(vs[cv].size(), ci);
+                               auto ret = &vs[cv][ci++];
+                               DCHECK_GT(ret, 0);
+                               if (ci >= vs[cv].size()){
+                                       ++cv;
+                               }
+
+                               new (ret) T();
+                               return ret;
+                       }
+               };
+} // namespace Robustness
diff --git a/compiler-rt/lib/tsan/rtl/rsan_defs.hpp 
b/compiler-rt/lib/tsan/rtl/rsan_defs.hpp
new file mode 100644
index 0000000000000..6b11897ce9a76
--- /dev/null
+++ b/compiler-rt/lib/tsan/rtl/rsan_defs.hpp
@@ -0,0 +1,96 @@
+#pragma once
+#include "tsan_defs.h"
+#include "tsan_rtl.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_allocator_internal.h"
+
+//class __tsan::ThreadState;
+
+namespace Robustness{
+       using __tsan::s8;
+       using __tsan::u8;
+       using __tsan::s16;
+       using __tsan::u16;
+       using __tsan::s32;
+       using __tsan::u32;
+       using __tsan::s64;
+       using __tsan::u64;
+       using __tsan::uptr;
+       using __tsan::Epoch;
+       using __tsan::EpochInc;
+       using __tsan::EpochOverflow;
+       using __tsan::kEpochZero;
+       using __tsan::kEpochOver;
+       using __tsan::kEpochLast;
+       typedef __tsan::Epoch timestamp_t;
+       typedef s64 ssize_t;
+       typedef u64 uint64_t;
+       typedef s64 int64_t;
+       typedef __PTRDIFF_TYPE__ ptrdiff_t;
+       typedef __SIZE_TYPE__ size_t;
+
+       typedef u8 uint8_t;;
+
+       typedef u64 Address;
+       typedef u64 LocationId;
+       
+       typedef u32 ThreadId;
+
+       using __tsan::InternalScopedString;
+
+       using __tsan::flags;
+
+       using __sanitizer::IsAligned;
+
+       using __sanitizer::LowLevelAllocator;
+       using __sanitizer::InternalAlloc;
+       using __sanitizer::InternalFree;
+       using __sanitizer::internal_memcpy;
+       using __sanitizer::internal_memmove;
+       using __sanitizer::internal_memset;
+       using __sanitizer::RoundUpTo;
+       using __sanitizer::RoundUpToPowerOfTwo;
+       using __sanitizer::GetPageSizeCached;
+       using __sanitizer::MostSignificantSetBitIndex;
+       using __sanitizer::MmapOrDie;
+       using __sanitizer::UnmapOrDie;
+       using __sanitizer::Max;
+       using __sanitizer::Swap;
+       using __sanitizer::forward;
+       using __sanitizer::move;
+
+       using __sanitizer::Printf;
+       using __sanitizer::Report;
+
+       using __sanitizer::Lock;
+       using __sanitizer::Mutex;
+
+       template <typename T1, typename T2>
+       struct Pair{
+               T1 first;
+               T2 second;
+       };
+       template <typename T1, typename T2>
+       auto pair(T1 fst, T2 snd){
+               return Pair<T1, T2>{fst, snd};
+       }
+
+       using __tsan::max;
+       using __tsan::min;
+
+       enum class ViolationType{
+               read, write,
+       };
+
+       struct DebugInfo {
+               __tsan::ThreadState* thr = nullptr;
+               uptr pc = 0xDEADBEEF;
+       };
+
+       template<class>
+               inline constexpr bool always_false_v = false;
+
+       inline bool isRobustness() {
+               return __tsan::flags()->enable_robustness;
+       }
+} //  namespace Robustness
diff --git a/compiler-rt/lib/tsan/rtl/rsan_dense_map.h 
b/compiler-rt/lib/tsan/rtl/rsan_dense_map.h
new file mode 100644
index 0000000000000..57775613ed00b
--- /dev/null
+++ b/compiler-rt/lib/tsan/rtl/rsan_dense_map.h
@@ -0,0 +1,714 @@
+//===- sanitizer_dense_map.h - Dense probed hash table ----------*- 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 is fork of llvm/ADT/DenseMap.h class with the following changes:
+//  * Use mmap to allocate.
+//  * No iterators.
+//  * Does not shrink.
+//
+//===----------------------------------------------------------------------===//
+
+#pragma once
+
+#include "rsan_defs.hpp"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_dense_map_info.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_type_traits.h"
+
+namespace Robustness {
+       namespace detail{
+               using __sanitizer::detail::DenseMapPair;
+               using __sanitizer::detail::combineHashValue;
+       } // namespace detail
+       using __sanitizer::DenseMapInfo;
+
+template <typename DerivedT, typename KeyT, typename ValueT, typename KeyInfoT,
+          typename BucketT>
+class DenseMapBase {
+ public:
+  using size_type = unsigned;
+  using key_type = KeyT;
+  using mapped_type = ValueT;
+  using value_type = BucketT;
+
+  WARN_UNUSED_RESULT bool empty() const { return getNumEntries() == 0; }
+  unsigned size() const { return getNumEntries(); }
+
+  /// Grow the densemap so that it can contain at least \p NumEntries items
+  /// before resizing again.
+  void reserve(size_type NumEntries) {
+    auto NumBuckets = getMinBucketToReserveForEntries(NumEntries);
+    if (NumBuckets > getNumBuckets())
+      grow(NumBuckets);
+  }
+
+  void clear() {
+    if (getNumEntries() == 0 && getNumTombstones() == 0)
+      return;
+
+    const KeyT EmptyKey = getEmptyKey(), TombstoneKey = getTombstoneKey();
+    if (__sanitizer::is_trivially_destructible<ValueT>::value) {
+      // Use a simpler loop when values don't need destruction.
+      for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P)
+        P->getFirst() = EmptyKey;
+    } else {
+      unsigned NumEntries = getNumEntries();
+      for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P) {
+        if (!KeyInfoT::isEqual(P->getFirst(), EmptyKey)) {
+          if (!KeyInfoT::isEqual(P->getFirst(), TombstoneKey)) {
+            P->getSecond().~ValueT();
+            --NumEntries;
+          }
+          P->getFirst() = EmptyKey;
+        }
+      }
+      CHECK_EQ(NumEntries, 0);
+    }
+    setNumEntries(0);
+    setNumTombstones(0);
+  }
+
+  /// Return 1 if the specified key is in the map, 0 otherwise.
+  size_type count(const KeyT &Key) const {
+    const BucketT *TheBucket;
+    return LookupBucketFor(Key, TheBucket) ? 1 : 0;
+  }
+
+  value_type *find(const KeyT &Key) {
+    BucketT *TheBucket;
+    if (LookupBucketFor(Key, TheBucket))
+      return TheBucket;
+    return nullptr;
+  }
+  const value_type *find(const KeyT &Key) const {
+    const BucketT *TheBucket;
+    if (LookupBucketFor(Key, TheBucket))
+      return TheBucket;
+    return nullptr;
+  }
+  bool contains(const KeyT &Key) {
+    BucketT *TheBucket;
+    if (LookupBucketFor(Key, TheBucket))
+      return true;
+    return false;
+  }
+
+  /// Alternate version of find() which allows a different, and possibly
+  /// less expensive, key type.
+  /// The DenseMapInfo is responsible for supplying methods
+  /// getHashValue(LookupKeyT) and isEqual(LookupKeyT, KeyT) for each key
+  /// type used.
+  template <class LookupKeyT>
+  value_type *find_as(const LookupKeyT &Key) {
+    BucketT *TheBucket;
+    if (LookupBucketFor(Key, TheBucket))
+      return TheBucket;
+    return nullptr;
+  }
+  template <class LookupKeyT>
+  const value_type *find_as(const LookupKeyT &Key) const {
+    const BucketT *TheBucket;
+    if (LookupBucketFor(Key, TheBucket))
+      return TheBucket;
+    return nullptr;
+  }
+
+  /// lookup - Return the entry for the specified key, or a default
+  /// constructed value if no such entry exists.
+  ValueT lookup(const KeyT &Key) const {
+    const BucketT *TheBucket;
+    if (LookupBucketFor(Key, TheBucket))
+      return TheBucket->getSecond();
+    return ValueT();
+  }
+
+  // Inserts key,value pair into the map if the key isn't already in the map.
+  // If the key is already in the map, it returns false and doesn't update the
+  // value.
+  detail::DenseMapPair<value_type *, bool> insert(const value_type &KV) {
+    return try_emplace(KV.first, KV.second);
+  }
+
+  // Inserts key,value pair into the map if the key isn't already in the map.
+  // If the key is already in the map, it returns false and doesn't update the
+  // value.
+  detail::DenseMapPair<value_type *, bool> insert(value_type &&KV) {
+    return try_emplace(__sanitizer::move(KV.first),
+                       __sanitizer::move(KV.second));
+  }
+
+  // Inserts key,value pair into the map if the key isn't already in the map.
+  // The value is constructed in-place if the key is not in the map, otherwise
+  // it is not moved.
+  template <typename... Ts>
+  detail::DenseMapPair<value_type *, bool> try_emplace(KeyT &&Key,
+                                                       Ts &&...Args) {
+    BucketT *TheBucket;
+    if (LookupBucketFor(Key, TheBucket))
+      return {TheBucket, false};  // Already in map.
+
+    // Otherwise, insert the new element.
+    TheBucket = InsertIntoBucket(TheBucket, __sanitizer::move(Key),
+                                 __sanitizer::forward<Ts>(Args)...);
+    return {TheBucket, true};
+  }
+
+  // Inserts key,value pair into the map if the key isn't already in the map.
+  // The value is constructed in-place if the key is not in the map, otherwise
+  // it is not moved.
+  template <typename... Ts>
+  detail::DenseMapPair<value_type *, bool> try_emplace(const KeyT &Key,
+                                                       Ts &&...Args) {
+    BucketT *TheBucket;
+    if (LookupBucketFor(Key, TheBucket))
+      return {TheBucket, false};  // Already in map.
+
+    // Otherwise, insert the new element.
+    TheBucket =
+        InsertIntoBucket(TheBucket, Key, __sanitizer::forward<Ts>(Args)...);
+    return {TheBucket, true};
+  }
+
+  /// Alternate version of insert() which allows a different, and possibly
+  /// less expensive, key type.
+  /// The DenseMapInfo is responsible for supplying methods
+  /// getHashValue(LookupKeyT) and isEqual(LookupKeyT, KeyT) for each key
+  /// type used.
+  template <typename LookupKeyT>
+  detail::DenseMapPair<value_type *, bool> insert_as(value_type &&KV,
+                                                     const LookupKeyT &Val) {
+    BucketT *TheBucket;
+    if (LookupBucketFor(Val, TheBucket))
+      return {TheBucket, false};  // Already in map.
+
+    // Otherwise, insert the new element.
+    TheBucket =
+        InsertIntoBucketWithLookup(TheBucket, __sanitizer::move(KV.first),
+                                   __sanitizer::move(KV.second), Val);
+    return {TheBucket, true};
+  }
+
+  bool erase(const KeyT &Val) {
+    BucketT *TheBucket;
+    if (!LookupBucketFor(Val, TheBucket))
+      return false;  // not in map.
+
+    TheBucket->getSecond().~ValueT();
+    TheBucket->getFirst() = getTombstoneKey();
+    decrementNumEntries();
+    incrementNumTombstones();
+    return true;
+  }
+
+  void erase(value_type *I) {
+    CHECK_NE(I, nullptr);
+    BucketT *TheBucket = &*I;
+    TheBucket->getSecond().~ValueT();
+    TheBucket->getFirst() = getTombstoneKey();
+    decrementNumEntries();
+    incrementNumTombstones();
+  }
+
+  value_type &FindAndConstruct(const KeyT &Key) {
+    BucketT *TheBucket;
+    if (LookupBucketFor(Key, TheBucket))
+      return *TheBucket;
+
+    return *InsertIntoBucket(TheBucket, Key);
+  }
+
+  ValueT &operator[](const KeyT &Key) { return FindAndConstruct(Key).second; }
+
+  value_type &FindAndConstruct(KeyT &&Key) {
+    BucketT *TheBucket;
+    if (LookupBucketFor(Key, TheBucket))
+      return *TheBucket;
+
+    return *InsertIntoBucket(TheBucket, __sanitizer::move(Key));
+  }
+
+  ValueT &operator[](KeyT &&Key) {
+    return FindAndConstruct(__sanitizer::move(Key)).second;
+  }
+
+  /// Iterate over active entries of the container.
+  ///
+  /// Function can return fast to stop the process.
+  template <class Fn>
+  void forEach(Fn fn) {
+    const KeyT EmptyKey = getEmptyKey(), TombstoneKey = getTombstoneKey();
+    for (auto *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P) {
+      const KeyT K = P->getFirst();
+      if (!KeyInfoT::isEqual(K, EmptyKey) &&
+          !KeyInfoT::isEqual(K, TombstoneKey)) {
+        if (!fn(*P))
+          return;
+      }
+    }
+  }
+
+  template <class Fn>
+  void forEach(Fn fn) const {
+    const_cast<DenseMapBase *>(this)->forEach(
+        [&](const value_type &KV) { return fn(KV); });
+  }
+
+ protected:
+  DenseMapBase() = default;
+
+  void destroyAll() {
+    if (getNumBuckets() == 0)  // Nothing to do.
+      return;
+
+    const KeyT EmptyKey = getEmptyKey(), TombstoneKey = getTombstoneKey();
+    for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P) {
+      if (!KeyInfoT::isEqual(P->getFirst(), EmptyKey) &&
+          !KeyInfoT::isEqual(P->getFirst(), TombstoneKey))
+        P->getSecond().~ValueT();
+      P->getFirst().~KeyT();
+    }
+  }
+
+  void initEmpty() {
+    setNumEntries(0);
+    setNumTombstones(0);
+
+    CHECK_EQ((getNumBuckets() & (getNumBuckets() - 1)), 0);
+    const KeyT EmptyKey = getEmptyKey();
+    for (BucketT *B = getBuckets(), *E = getBucketsEnd(); B != E; ++B)
+      ::new (&B->getFirst()) KeyT(EmptyKey);
+  }
+
+  /// Returns the number of buckets to allocate to ensure that the DenseMap can
+  /// accommodate \p NumEntries without need to grow().
+  unsigned getMinBucketToReserveForEntries(unsigned NumEntries) {
+    // Ensure that "NumEntries * 4 < NumBuckets * 3"
+    if (NumEntries == 0)
+      return 0;
+    // +1 is required because of the strict equality.
+    // For example if NumEntries is 48, we need to return 401.
+    return RoundUpToPowerOfTwo((NumEntries * 4 / 3 + 1) + /* NextPowerOf2 */ 
1);
+  }
+
+  void moveFromOldBuckets(BucketT *OldBucketsBegin, BucketT *OldBucketsEnd) {
+    initEmpty();
+
+    // Insert all the old elements.
+    const KeyT EmptyKey = getEmptyKey();
+    const KeyT TombstoneKey = getTombstoneKey();
+    for (BucketT *B = OldBucketsBegin, *E = OldBucketsEnd; B != E; ++B) {
+      if (!KeyInfoT::isEqual(B->getFirst(), EmptyKey) &&
+          !KeyInfoT::isEqual(B->getFirst(), TombstoneKey)) {
+        // Insert the key/value into the new table.
+        BucketT *DestBucket;
+        bool FoundVal = LookupBucketFor(B->getFirst(), DestBucket);
+        (void)FoundVal;  // silence warning.
+        CHECK(!FoundVal);
+        DestBucket->getFirst() = __sanitizer::move(B->getFirst());
+        ::new (&DestBucket->getSecond())
+            ValueT(__sanitizer::move(B->getSecond()));
+        incrementNumEntries();
+
+        // Free the value.
+        B->getSecond().~ValueT();
+      }
+      B->getFirst().~KeyT();
+    }
+  }
+
+  template <typename OtherBaseT>
+  void copyFrom(
+      const DenseMapBase<OtherBaseT, KeyT, ValueT, KeyInfoT, BucketT> &other) {
+    CHECK_NE(&other, this);
+    CHECK_EQ(getNumBuckets(), other.getNumBuckets());
+
+    setNumEntries(other.getNumEntries());
+    setNumTombstones(other.getNumTombstones());
+
+    if (__sanitizer::is_trivially_copyable<KeyT>::value &&
+        __sanitizer::is_trivially_copyable<ValueT>::value)
+      internal_memcpy(reinterpret_cast<void *>(getBuckets()),
+                      other.getBuckets(), getNumBuckets() * sizeof(BucketT));
+    else
+      for (uptr i = 0; i < getNumBuckets(); ++i) {
+        ::new (&getBuckets()[i].getFirst())
+            KeyT(other.getBuckets()[i].getFirst());
+        if (!KeyInfoT::isEqual(getBuckets()[i].getFirst(), getEmptyKey()) &&
+            !KeyInfoT::isEqual(getBuckets()[i].getFirst(), getTombstoneKey()))
+          ::new (&getBuckets()[i].getSecond())
+              ValueT(other.getBuckets()[i].getSecond());
+      }
+  }
+
+  static unsigned getHashValue(const KeyT &Val) {
+    return KeyInfoT::getHashValue(Val);
+  }
+
+  template <typename LookupKeyT>
+  static unsigned getHashValue(const LookupKeyT &Val) {
+    return KeyInfoT::getHashValue(Val);
+  }
+
+  static const KeyT getEmptyKey() { return KeyInfoT::getEmptyKey(); }
+
+  static const KeyT getTombstoneKey() { return KeyInfoT::getTombstoneKey(); }
+
+ private:
+  unsigned getNumEntries() const {
+    return static_cast<const DerivedT *>(this)->getNumEntries();
+  }
+
+  void setNumEntries(unsigned Num) {
+    static_cast<DerivedT *>(this)->setNumEntries(Num);
+  }
+
+  void incrementNumEntries() { setNumEntries(getNumEntries() + 1); }
+
+  void decrementNumEntries() { setNumEntries(getNumEntries() - 1); }
+
+  unsigned getNumTombstones() const {
+    return static_cast<const DerivedT *>(this)->getNumTombstones();
+  }
+
+  void setNumTombstones(unsigned Num) {
+    static_cast<DerivedT *>(this)->setNumTombstones(Num);
+  }
+
+  void incrementNumTombstones() { setNumTombstones(getNumTombstones() + 1); }
+
+  void decrementNumTombstones() { setNumTombstones(getNumTombstones() - 1); }
+
+  const BucketT *getBuckets() const {
+    return static_cast<const DerivedT *>(this)->getBuckets();
+  }
+
+  BucketT *getBuckets() { return static_cast<DerivedT *>(this)->getBuckets(); }
+
+  unsigned getNumBuckets() const {
+    return static_cast<const DerivedT *>(this)->getNumBuckets();
+  }
+
+  BucketT *getBucketsEnd() { return getBuckets() + getNumBuckets(); }
+
+  const BucketT *getBuckets...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/145540
_______________________________________________
llvm-branch-commits mailing list
llvm-branch-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to