[llvm] [OFFLOAD] Add support for indexed per-thread containers (PR #164263)

Kevin Sala Penades via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 18 18:49:35 PST 2025


================
@@ -91,24 +177,114 @@ template <typename ContainerType, typename ObjectType> struct PerThreadTable {
   // Iterators to traverse objects owned by
   // the current thread
   iterator begin() {
-    auto &Entry = getThreadEntry();
+    ContainerType &Entry = getThreadEntry();
     return Entry.begin();
   }
   iterator end() {
-    auto &Entry = getThreadEntry();
+    ContainerType &Entry = getThreadEntry();
     return Entry.end();
   }
 
-  template <class F> void clear(F f) {
-    std::lock_guard<std::mutex> Lock(Mtx);
-    for (auto ThData : ThreadDataList) {
-      if (!ThData->ThEntry || ThData->NElements == 0)
+  template <class ClearFuncTy> void clear(ClearFuncTy ClearFunc) {
+    assert(Mutex.try_lock() && (Mutex.unlock(), true) &&
+           "Clear cannot be called while other threads are adding entries");
+    for (std::shared_ptr<PerThreadData> ThreadData : ThreadDataList) {
+      if (!ThreadData->ThreadEntry || ThreadData->NElements == 0)
         continue;
-      ThData->ThEntry->clear(f);
-      ThData->NElements = 0;
+      if constexpr (has_clearAll<ContainerType>::value) {
+        ThreadData->ThreadEntry->clearAll(ClearFunc);
+      } else if constexpr (has_iterator<ContainerType>::value &&
+                           has_clear<ContainerType>::value) {
+        for (auto &Obj : *ThreadData->ThreadEntry) {
+          if constexpr (is_associative<ContainerType>::value) {
+            ClearFunc(Obj.second);
+          } else {
+            ClearFunc(Obj);
+          }
+        }
+        ThreadData->ThreadEntry->clear();
+      } else {
+        static_assert(true, "Container type not supported");
+      }
+      ThreadData->NElements = 0;
     }
     ThreadDataList.clear();
   }
+
+  template <class DeinitFuncTy> llvm::Error deinit(DeinitFuncTy DeinitFunc) {
+    assert(Mutex.try_lock() && (Mutex.unlock(), true) &&
+           "Deinit cannot be called while other threads are adding entries");
+    for (std::shared_ptr<PerThreadData> ThreadData : ThreadDataList) {
+      if (!ThreadData->ThreadEntry || ThreadData->NElements == 0)
+        continue;
+      for (auto &Obj : *ThreadData->ThreadEntry) {
+        if constexpr (is_associative<ContainerType>::value) {
+          if (auto Err = DeinitFunc(Obj.second))
+            return Err;
+        } else {
+          if (auto Err = DeinitFunc(Obj))
+            return Err;
+        }
+      }
+    }
+    return llvm::Error::success();
+  }
+};
+
+template <typename T, typename = std::void_t<>> struct ContainerValueType {
+  using type = typename T::value_type;
+};
+template <typename T>
+struct ContainerValueType<T, std::void_t<typename T::mapped_type>> {
+  using type = typename T::mapped_type;
+};
+
+template <typename ContainerType, size_t reserveSize = 0>
+struct PerThreadContainer
+    : public PerThreadTable<ContainerType,
+                            typename ContainerValueType<ContainerType>::type> {
+
+  // helpers
+  template <typename T, typename = std::void_t<>> struct indexType {
+    using type = typename T::size_type;
+  };
+  template <typename T> struct indexType<T, std::void_t<typename T::key_type>> {
+    using type = typename T::key_type;
+  };
+  template <typename T, typename = std::void_t<>>
+  struct has_resize : std::false_type {};
+  template <typename T>
+  struct has_resize<T, std::void_t<decltype(std::declval<T>().resize(1))>>
+      : std::true_type {};
+
+  template <typename T, typename = std::void_t<>>
+  struct has_reserve : std::false_type {};
+  template <typename T>
+  struct has_reserve<T, std::void_t<decltype(std::declval<T>().reserve(1))>>
+      : std::true_type {};
----------------
kevinsala wrote:

I think you can use something like this:

```cpp
#include <type_traits>
#include <vector>

template <typename, template <typename> class, typename = std::void_t<>>        
struct has : std::false_type {};                                                
                                                                                
template <typename T, template <typename> class Op>                             
struct has<T, Op, std::void_t<Op<T>>> : std::true_type {};                      
                                                                                
template <typename T>                                                           
using reserve = decltype(std::declval<T>().reserve(1));                        
                                                                                
int main() {                                                                    
    if constexpr (has<std::vector<int>, reserve>::value) {                      
        printf("has reserve\n");                                       
    } else {                                                                    
        printf("hasn't reserve\n");                                    
    }                                                                           
}
```

Then you will only need a `template <typename T> using <...> = decltype(...)` for each function or property to check.

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


More information about the llvm-commits mailing list