Tested on x86_64-pc-linux-gnu, this is only a library addition (and a
few tests) so it shouldn't cause any major impacts.  I also tested
libgomp C to ensure the conditional compile was working.

Okay for trunk?
From 1ef3fe0a1f026689e64963ec9ab0b04b7e6b1bde Mon Sep 17 00:00:00 2001
From: waffl3x <waff...@baylibre.com>
Date: Tue, 15 Apr 2025 04:12:55 -0600
Subject: [PATCH] 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>
---
 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(+)
 create mode 100644 libgomp/testsuite/libgomp.c++/allocator-1.C
 create mode 100644 libgomp/testsuite/libgomp.c++/allocator-2.C

diff --git a/libgomp/omp.h.in b/libgomp/omp.h.in
index d5e8be46e94..8d17db1da9a 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 00000000000..725beade0c8
--- /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 00000000000..6f7a061f725
--- /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);
+}
-- 
2.47.1

Reply via email to