https://gcc.gnu.org/g:99835bd68e5360b0b3c8ad9c61ea23f70ad3dce6

commit r15-9504-g99835bd68e5360b0b3c8ad9c61ea23f70ad3dce6
Author: waffl3x <waff...@baylibre.com>
Date:   Tue Apr 15 14:34:38 2025 -0600

    OpenMP: omp.h omp::allocator C++ Allocator interface
    
    The implementation of each allocator is simplified by inheriting from
    __detail::__allocator_templ.  At the moment, none of the implementations
    diverge in any way, simply passing in the allocator handle to be used when
    an allocation is made.  In the future, const_mem will need special handling
    added to it to support constant memory space.
    
    libgomp/ChangeLog:
    
            * omp.h.in: Add omp::allocator::* and ompx::allocator::* allocators.
            (__detail::__allocator_templ<T, omp_allocator_handle_t>):
            New struct template.
            (null_allocator<T>): New struct template.
            (default_mem<T>): Likewise.
            (large_cap_mem<T>): Likewise.
            (const_mem<T>): Likewise.
            (high_bw_mem<T>): Likewise.
            (low_lat_mem<T>): Likewise.
            (cgroup_mem<T>): Likewise.
            (pteam_mem<T>): Likewise.
            (thread_mem<T>): Likewise.
            (ompx::allocator::gnu_pinned_mem<T>): Likewise.
            * testsuite/libgomp.c++/allocator-1.C: New test.
            * testsuite/libgomp.c++/allocator-2.C: New test.
    
    Signed-off-by: waffl3x <waff...@baylibre.com>

Diff:
---
 libgomp/omp.h.in                            | 132 +++++++++++++++++++++++
 libgomp/testsuite/libgomp.c++/allocator-1.C | 158 ++++++++++++++++++++++++++++
 libgomp/testsuite/libgomp.c++/allocator-2.C | 132 +++++++++++++++++++++++
 3 files changed, 422 insertions(+)

diff --git a/libgomp/omp.h.in b/libgomp/omp.h.in
index d5e8be46e944..8d17db1da9a7 100644
--- a/libgomp/omp.h.in
+++ b/libgomp/omp.h.in
@@ -432,4 +432,136 @@ extern const char *omp_get_uid_from_device (int) 
__GOMP_NOTHROW;
 }
 #endif
 
+#if __cplusplus >= 201103L
+
+/* std::__throw_bad_alloc and std::__throw_bad_array_new_length.  */
+#include <bits/functexcept.h>
+
+namespace omp
+{
+namespace allocator
+{
+
+namespace __detail
+{
+
+template<typename __T, omp_allocator_handle_t __Handle>
+struct __allocator_templ
+{
+  using value_type = __T;
+  using pointer = __T*;
+  using const_pointer = const __T*;
+  using size_type = __SIZE_TYPE__;
+  using difference_type = __PTRDIFF_TYPE__;
+
+  __T*
+  allocate (size_type __n)
+  {
+    if (__SIZE_MAX__ / sizeof(__T) < __n)
+      std::__throw_bad_array_new_length ();
+    void *__p = omp_aligned_alloc (alignof(__T), __n * sizeof(__T), __Handle);
+    if (!__p)
+      std::__throw_bad_alloc ();
+    return static_cast<__T*>(__p);
+  }
+
+  void
+  deallocate (__T *__p, size_type) __GOMP_NOTHROW
+  {
+    omp_free (static_cast<void*>(__p), __Handle);
+  }
+};
+
+template<typename __T, typename __U, omp_allocator_handle_t __Handle>
+constexpr bool
+operator== (const __allocator_templ<__T, __Handle>&,
+           const __allocator_templ<__U, __Handle>&) __GOMP_NOTHROW
+{
+  return true;
+}
+
+template<typename __T, omp_allocator_handle_t __Handle,
+        typename __U, omp_allocator_handle_t __UHandle>
+constexpr bool
+operator== (const __allocator_templ<__T, __Handle>&,
+           const __allocator_templ<__U, __UHandle>&) __GOMP_NOTHROW
+{
+  return false;
+}
+
+template<typename __T, typename __U, omp_allocator_handle_t __Handle>
+constexpr bool
+operator!= (const __allocator_templ<__T, __Handle>&,
+           const __allocator_templ<__U, __Handle>&) __GOMP_NOTHROW
+{
+  return false;
+}
+
+template<typename __T, omp_allocator_handle_t __Handle,
+        typename __U, omp_allocator_handle_t __UHandle>
+constexpr bool
+operator!= (const __allocator_templ<__T, __Handle>&,
+           const __allocator_templ<__U, __UHandle>&) __GOMP_NOTHROW
+{
+  return true;
+}
+
+} /* namespace __detail */
+
+template<typename __T>
+struct null_allocator
+  : __detail::__allocator_templ<__T, omp_null_allocator> {};
+
+template<typename __T>
+struct default_mem
+  : __detail::__allocator_templ<__T, omp_default_mem_alloc> {};
+
+template<typename __T>
+struct large_cap_mem
+  : __detail::__allocator_templ<__T, omp_large_cap_mem_alloc> {};
+
+template<typename __T>
+struct const_mem
+  : __detail::__allocator_templ<__T, omp_const_mem_alloc> {};
+
+template<typename __T>
+struct high_bw_mem
+  : __detail::__allocator_templ<__T, omp_high_bw_mem_alloc> {};
+
+template<typename __T>
+struct low_lat_mem
+  : __detail::__allocator_templ<__T, omp_low_lat_mem_alloc> {};
+
+template<typename __T>
+struct cgroup_mem
+  : __detail::__allocator_templ<__T, omp_cgroup_mem_alloc> {};
+
+template<typename __T>
+struct pteam_mem
+  : __detail::__allocator_templ<__T, omp_pteam_mem_alloc> {};
+
+template<typename __T>
+struct thread_mem
+  : __detail::__allocator_templ<__T, omp_thread_mem_alloc> {};
+
+} /* namespace allocator */
+
+} /* namespace omp */
+
+namespace ompx
+{
+
+namespace allocator
+{
+
+template<typename __T>
+struct gnu_pinned_mem
+  : omp::allocator::__detail::__allocator_templ<__T, 
ompx_gnu_pinned_mem_alloc> {};
+
+} /* namespace allocator */
+
+} /* namespace ompx */
+
+#endif /* __cplusplus */
+
 #endif /* _OMP_H */
diff --git a/libgomp/testsuite/libgomp.c++/allocator-1.C 
b/libgomp/testsuite/libgomp.c++/allocator-1.C
new file mode 100644
index 000000000000..f82072284883
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/allocator-1.C
@@ -0,0 +1,158 @@
+// { dg-do run }
+
+#include <omp.h>
+#include <memory>
+#include <limits>
+
+template<typename T, template<typename> class Alloc>
+void test (T const initial_value = T())
+{
+  using Allocator = Alloc<T>;
+  Allocator a;
+  using Traits = std::allocator_traits<Allocator>;
+  static_assert (__is_same(typename Traits::allocator_type,     Allocator      
 ));
+  static_assert (__is_same(typename Traits::value_type,         T              
 ));
+  static_assert (__is_same(typename Traits::pointer,            T*             
 ));
+  static_assert (__is_same(typename Traits::const_pointer,      T const*       
 ));
+  static_assert (__is_same(typename Traits::void_pointer,       void*          
 ));
+  static_assert (__is_same(typename Traits::const_void_pointer, void const*    
 ));
+  static_assert (__is_same(typename Traits::difference_type,    
__PTRDIFF_TYPE__));
+  static_assert (__is_same(typename Traits::size_type,          __SIZE_TYPE__  
 ));
+  static_assert (Traits::propagate_on_container_copy_assignment::value == 
false);
+  static_assert (Traits::propagate_on_container_move_assignment::value == 
false);
+  static_assert (Traits::propagate_on_container_swap::value == false);
+  static_assert (Traits::is_always_equal::value == true);
+
+  static constexpr __SIZE_TYPE__ correct_max_size
+    = std::numeric_limits<__SIZE_TYPE__>::max () / sizeof (T);
+  if (Traits::max_size (a) != correct_max_size)
+    __builtin_abort ();
+
+  static constexpr __SIZE_TYPE__ alloc_count = 1;
+  T *p = Traits::allocate (a, alloc_count);
+  if (p == nullptr)
+    __builtin_abort ();
+  Traits::construct (a, p, initial_value);
+  if (*p != initial_value)
+    __builtin_abort ();
+  Traits::destroy (a, p);
+  Traits::deallocate (a, p, alloc_count);
+  /* Not interesting but might as well test it.  */
+  static_cast<void>(Traits::select_on_container_copy_construction (a));
+
+  if (!(a == Allocator()))
+    __builtin_abort ();
+  if (a != Allocator())
+    __builtin_abort ();
+  if (!(a == Alloc<void>()))
+    __builtin_abort ();
+  if (a != Alloc<void>())
+    __builtin_abort ();
+}
+
+#define CHECK_INEQUALITY(other_alloc_templ, type) \
+do {                                                                   \
+  /* Skip tests for itself, those are equal.  Intantiate each  */      \
+  /* one with void so we can easily tell if they are the same.  */     \
+  if (!__is_same (AllocTempl<void>, other_alloc_templ<void>))          \
+    {                                                                  \
+      other_alloc_templ<type> other;                                   \
+      if (a == other)                                                  \
+       __builtin_abort ();                                             \
+      if (!(a != other))                                               \
+       __builtin_abort ();                                             \
+    }                                                                  \
+} while (false)
+
+template<typename T, template<typename> class AllocTempl>
+void test_inequality ()
+{
+  using Allocator = AllocTempl<T>;
+  Allocator a;
+  CHECK_INEQUALITY (omp::allocator::null_allocator, void);
+  CHECK_INEQUALITY (omp::allocator::default_mem, void);
+  CHECK_INEQUALITY (omp::allocator::large_cap_mem, void);
+  CHECK_INEQUALITY (omp::allocator::const_mem, void);
+  CHECK_INEQUALITY (omp::allocator::high_bw_mem, void);
+  CHECK_INEQUALITY (omp::allocator::low_lat_mem, void);
+  CHECK_INEQUALITY (omp::allocator::cgroup_mem, void);
+  CHECK_INEQUALITY (omp::allocator::pteam_mem, void);
+  CHECK_INEQUALITY (omp::allocator::thread_mem, void);
+  CHECK_INEQUALITY (ompx::allocator::gnu_pinned_mem, void);
+  /* And again with the same type passed to the allocator.  */
+  CHECK_INEQUALITY (omp::allocator::null_allocator, T);
+  CHECK_INEQUALITY (omp::allocator::default_mem, T);
+  CHECK_INEQUALITY (omp::allocator::large_cap_mem, T);
+  CHECK_INEQUALITY (omp::allocator::const_mem, T);
+  CHECK_INEQUALITY (omp::allocator::high_bw_mem, T);
+  CHECK_INEQUALITY (omp::allocator::low_lat_mem, T);
+  CHECK_INEQUALITY (omp::allocator::cgroup_mem, T);
+  CHECK_INEQUALITY (omp::allocator::pteam_mem, T);
+  CHECK_INEQUALITY (omp::allocator::thread_mem, T);
+  CHECK_INEQUALITY (ompx::allocator::gnu_pinned_mem, T);
+}
+
+#undef CHECK_INEQUALITY
+
+struct S
+{
+  int _v0;
+  bool _v1;
+  float _v2;
+
+  bool operator== (S const& other) const noexcept {
+    return _v0 == other._v0
+          && _v1 == other._v1
+          && _v2 == other._v2;
+  }
+  bool operator!= (S const& other) const noexcept {
+    return !this->operator==(other);
+  }
+};
+
+int main ()
+{
+  test<int, omp::allocator::null_allocator>(42);
+  test<int, omp::allocator::default_mem>(42);
+  test<int, omp::allocator::large_cap_mem>(42);
+  test<int, omp::allocator::const_mem>(42);
+  test<int, omp::allocator::high_bw_mem>(42);
+  test<int, omp::allocator::low_lat_mem>(42);
+  test<int, omp::allocator::cgroup_mem>(42);
+  test<int, omp::allocator::pteam_mem>(42);
+  test<int, omp::allocator::thread_mem>(42);
+  test<int, ompx::allocator::gnu_pinned_mem>(42);
+
+  test<long long, omp::allocator::null_allocator>(42);
+  test<long long, omp::allocator::default_mem>(42);
+  test<long long, omp::allocator::large_cap_mem>(42);
+  test<long long, omp::allocator::const_mem>(42);
+  test<long long, omp::allocator::high_bw_mem>(42);
+  test<long long, omp::allocator::low_lat_mem>(42);
+  test<long long, omp::allocator::cgroup_mem>(42);
+  test<long long, omp::allocator::pteam_mem>(42);
+  test<long long, omp::allocator::thread_mem>(42);
+  test<long long, ompx::allocator::gnu_pinned_mem>(42);
+
+  test<S, omp::allocator::null_allocator>( S{42, true, 128.f});
+  test<S, omp::allocator::default_mem>(    S{42, true, 128.f});
+  test<S, omp::allocator::large_cap_mem>(  S{42, true, 128.f});
+  test<S, omp::allocator::const_mem>(      S{42, true, 128.f});
+  test<S, omp::allocator::high_bw_mem>(    S{42, true, 128.f});
+  test<S, omp::allocator::low_lat_mem>(    S{42, true, 128.f});
+  test<S, omp::allocator::cgroup_mem>(     S{42, true, 128.f});
+  test<S, omp::allocator::pteam_mem>(      S{42, true, 128.f});
+  test<S, omp::allocator::thread_mem>(     S{42, true, 128.f});
+  test<S, ompx::allocator::gnu_pinned_mem>(S{42, true, 128.f});
+
+  test_inequality<int, omp::allocator::null_allocator>();
+  test_inequality<int, omp::allocator::default_mem>();
+  test_inequality<int, omp::allocator::large_cap_mem>();
+  test_inequality<int, omp::allocator::const_mem>();
+  test_inequality<int, omp::allocator::high_bw_mem>();
+  test_inequality<int, omp::allocator::low_lat_mem>();
+  test_inequality<int, omp::allocator::cgroup_mem>();
+  test_inequality<int, omp::allocator::pteam_mem>();
+  test_inequality<int, omp::allocator::thread_mem>();
+  test_inequality<int, ompx::allocator::gnu_pinned_mem>();
+}
diff --git a/libgomp/testsuite/libgomp.c++/allocator-2.C 
b/libgomp/testsuite/libgomp.c++/allocator-2.C
new file mode 100644
index 000000000000..d25b75501a99
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/allocator-2.C
@@ -0,0 +1,132 @@
+// { dg-do run }
+// { dg-additional-options "-Wno-psabi" }
+
+#include <omp.h>
+#include <vector>
+
+template<typename T>
+bool ptr_is_aligned(T *ptr, std::size_t alignment)
+{
+  /* ALIGNMENT must be a power of 2.  */
+  if ((alignment & (alignment - 1)) != 0)
+    __builtin_abort ();
+  __UINTPTR_TYPE__ ptr_value
+    = reinterpret_cast<__UINTPTR_TYPE__>(static_cast<void*>(ptr));
+  return (ptr_value % alignment) == 0;
+}
+
+template<typename T, template<typename> class Alloc>
+void f (T v0, T v1, T v2, T v3)
+{
+  std::vector<T, Alloc<T>> vec;
+  vec.push_back (v0);
+  vec.push_back (v1);
+  vec.push_back (v2);
+  vec.push_back (v3);
+  if (vec.at (0) != v0)
+    __builtin_abort ();
+  if (vec.at (1) != v1)
+    __builtin_abort ();
+  if (vec.at (2) != v2)
+    __builtin_abort ();
+  if (vec.at (3) != v3)
+    __builtin_abort ();
+  if (!ptr_is_aligned (&vec.at (0), alignof (T)))
+    __builtin_abort ();
+  if (!ptr_is_aligned (&vec.at (1), alignof (T)))
+    __builtin_abort ();
+  if (!ptr_is_aligned (&vec.at (2), alignof (T)))
+    __builtin_abort ();
+  if (!ptr_is_aligned (&vec.at (3), alignof (T)))
+    __builtin_abort ();
+}
+
+struct S0
+{
+  int _v0;
+  bool _v1;
+  float _v2;
+
+  bool operator== (S0 const& other) const noexcept {
+    return _v0 == other._v0
+          && _v1 == other._v1
+          && _v2 == other._v2;
+  }
+  bool operator!= (S0 const& other) const noexcept {
+    return !this->operator==(other);
+  }
+};
+
+struct alignas(128) S1
+{
+  int _v0;
+  bool _v1;
+  float _v2;
+
+  bool operator== (S1 const& other) const noexcept {
+    return _v0 == other._v0
+          && _v1 == other._v1
+          && _v2 == other._v2;
+  }
+  bool operator!= (S1 const& other) const noexcept {
+    return !this->operator==(other);
+  }
+};
+
+/* Note: the test for const_mem should be disabled in the future.  */
+
+int main ()
+{
+  f<int, omp::allocator::null_allocator >(0, 1, 2, 3);
+  f<int, omp::allocator::default_mem    >(0, 1, 2, 3);
+  f<int, omp::allocator::large_cap_mem  >(0, 1, 2, 3);
+  f<int, omp::allocator::const_mem      >(0, 1, 2, 3);
+  f<int, omp::allocator::high_bw_mem    >(0, 1, 2, 3);
+  f<int, omp::allocator::low_lat_mem    >(0, 1, 2, 3);
+  f<int, omp::allocator::cgroup_mem     >(0, 1, 2, 3);
+  f<int, omp::allocator::pteam_mem      >(0, 1, 2, 3);
+  f<int, omp::allocator::thread_mem     >(0, 1, 2, 3);
+  f<int, ompx::allocator::gnu_pinned_mem>(0, 1, 2, 3);
+
+  f<long long, omp::allocator::null_allocator >(0, 1, 2, 3);
+  f<long long, omp::allocator::default_mem    >(0, 1, 2, 3);
+  f<long long, omp::allocator::large_cap_mem  >(0, 1, 2, 3);
+  f<long long, omp::allocator::const_mem      >(0, 1, 2, 3);
+  f<long long, omp::allocator::high_bw_mem    >(0, 1, 2, 3);
+  f<long long, omp::allocator::low_lat_mem    >(0, 1, 2, 3);
+  f<long long, omp::allocator::cgroup_mem     >(0, 1, 2, 3);
+  f<long long, omp::allocator::pteam_mem      >(0, 1, 2, 3);
+  f<long long, omp::allocator::thread_mem     >(0, 1, 2, 3);
+  f<long long, ompx::allocator::gnu_pinned_mem>(0, 1, 2, 3);
+
+  S0 s0_0{   42, true,  111128.f};
+  S0 s0_1{  142, false,  11128.f};
+  S0 s0_2{ 1142, true,    1128.f};
+  S0 s0_3{11142, false,    128.f};
+  f<S0, omp::allocator::null_allocator >(s0_0, s0_1, s0_2, s0_3);
+  f<S0, omp::allocator::default_mem    >(s0_0, s0_1, s0_2, s0_3);
+  f<S0, omp::allocator::large_cap_mem  >(s0_0, s0_1, s0_2, s0_3);
+  f<S0, omp::allocator::const_mem      >(s0_0, s0_1, s0_2, s0_3);
+  f<S0, omp::allocator::high_bw_mem    >(s0_0, s0_1, s0_2, s0_3);
+  f<S0, omp::allocator::low_lat_mem    >(s0_0, s0_1, s0_2, s0_3);
+  f<S0, omp::allocator::cgroup_mem     >(s0_0, s0_1, s0_2, s0_3);
+  f<S0, omp::allocator::pteam_mem      >(s0_0, s0_1, s0_2, s0_3);
+  f<S0, omp::allocator::thread_mem     >(s0_0, s0_1, s0_2, s0_3);
+  f<S0, ompx::allocator::gnu_pinned_mem>(s0_0, s0_1, s0_2, s0_3);
+
+  S1 s1_0{   42, true,  111128.f};
+  S1 s1_1{  142, false,  11128.f};
+  S1 s1_2{ 1142, true,    1128.f};
+  S1 s1_3{11142, false,    128.f};
+
+  f<S1, omp::allocator::null_allocator >(s1_0, s1_1, s1_2, s1_3);
+  f<S1, omp::allocator::default_mem    >(s1_0, s1_1, s1_2, s1_3);
+  f<S1, omp::allocator::large_cap_mem  >(s1_0, s1_1, s1_2, s1_3);
+  f<S1, omp::allocator::const_mem      >(s1_0, s1_1, s1_2, s1_3);
+  f<S1, omp::allocator::high_bw_mem    >(s1_0, s1_1, s1_2, s1_3);
+  f<S1, omp::allocator::low_lat_mem    >(s1_0, s1_1, s1_2, s1_3);
+  f<S1, omp::allocator::cgroup_mem     >(s1_0, s1_1, s1_2, s1_3);
+  f<S1, omp::allocator::pteam_mem      >(s1_0, s1_1, s1_2, s1_3);
+  f<S1, omp::allocator::thread_mem     >(s1_0, s1_1, s1_2, s1_3);
+  f<S1, ompx::allocator::gnu_pinned_mem>(s1_0, s1_1, s1_2, s1_3);
+}

Reply via email to