From: waffl3x Date: Tue, 15 Apr 2025 20:34:38 +0000 (-0600) Subject: OpenMP: omp.h omp::allocator C++ Allocator interface X-Git-Tag: basepoints/gcc-16~49 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=99835bd68e5360b0b3c8ad9c61ea23f70ad3dce6;p=thirdparty%2Fgcc.git 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): New struct template. (null_allocator): New struct template. (default_mem): Likewise. (large_cap_mem): Likewise. (const_mem): Likewise. (high_bw_mem): Likewise. (low_lat_mem): Likewise. (cgroup_mem): Likewise. (pteam_mem): Likewise. (thread_mem): Likewise. (ompx::allocator::gnu_pinned_mem): Likewise. * testsuite/libgomp.c++/allocator-1.C: New test. * testsuite/libgomp.c++/allocator-2.C: New test. Signed-off-by: waffl3x --- 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 + +namespace omp +{ +namespace allocator +{ + +namespace __detail +{ + +template +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(__p), __Handle); + } +}; + +template +constexpr bool +operator== (const __allocator_templ<__T, __Handle>&, + const __allocator_templ<__U, __Handle>&) __GOMP_NOTHROW +{ + return true; +} + +template +constexpr bool +operator== (const __allocator_templ<__T, __Handle>&, + const __allocator_templ<__U, __UHandle>&) __GOMP_NOTHROW +{ + return false; +} + +template +constexpr bool +operator!= (const __allocator_templ<__T, __Handle>&, + const __allocator_templ<__U, __Handle>&) __GOMP_NOTHROW +{ + return false; +} + +template +constexpr bool +operator!= (const __allocator_templ<__T, __Handle>&, + const __allocator_templ<__U, __UHandle>&) __GOMP_NOTHROW +{ + return true; +} + +} /* namespace __detail */ + +template +struct null_allocator + : __detail::__allocator_templ<__T, omp_null_allocator> {}; + +template +struct default_mem + : __detail::__allocator_templ<__T, omp_default_mem_alloc> {}; + +template +struct large_cap_mem + : __detail::__allocator_templ<__T, omp_large_cap_mem_alloc> {}; + +template +struct const_mem + : __detail::__allocator_templ<__T, omp_const_mem_alloc> {}; + +template +struct high_bw_mem + : __detail::__allocator_templ<__T, omp_high_bw_mem_alloc> {}; + +template +struct low_lat_mem + : __detail::__allocator_templ<__T, omp_low_lat_mem_alloc> {}; + +template +struct cgroup_mem + : __detail::__allocator_templ<__T, omp_cgroup_mem_alloc> {}; + +template +struct pteam_mem + : __detail::__allocator_templ<__T, omp_pteam_mem_alloc> {}; + +template +struct thread_mem + : __detail::__allocator_templ<__T, omp_thread_mem_alloc> {}; + +} /* namespace allocator */ + +} /* namespace omp */ + +namespace ompx +{ + +namespace allocator +{ + +template +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..f8207228488 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/allocator-1.C @@ -0,0 +1,158 @@ +// { dg-do run } + +#include +#include +#include + +template class Alloc> +void test (T const initial_value = T()) +{ + using Allocator = Alloc; + Allocator a; + using Traits = std::allocator_traits; + 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(Traits::select_on_container_copy_construction (a)); + + if (!(a == Allocator())) + __builtin_abort (); + if (a != Allocator()) + __builtin_abort (); + if (!(a == Alloc())) + __builtin_abort (); + if (a != Alloc()) + __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, other_alloc_templ)) \ + { \ + other_alloc_templ other; \ + if (a == other) \ + __builtin_abort (); \ + if (!(a != other)) \ + __builtin_abort (); \ + } \ +} while (false) + +template class AllocTempl> +void test_inequality () +{ + using Allocator = AllocTempl; + 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(42); + test(42); + test(42); + test(42); + test(42); + test(42); + test(42); + test(42); + test(42); + test(42); + + test(42); + test(42); + test(42); + test(42); + test(42); + test(42); + test(42); + test(42); + test(42); + test(42); + + test( S{42, true, 128.f}); + test( S{42, true, 128.f}); + test( S{42, true, 128.f}); + test( S{42, true, 128.f}); + test( S{42, true, 128.f}); + test( S{42, true, 128.f}); + test( S{42, true, 128.f}); + test( S{42, true, 128.f}); + test( S{42, true, 128.f}); + test(S{42, true, 128.f}); + + test_inequality(); + test_inequality(); + test_inequality(); + test_inequality(); + test_inequality(); + test_inequality(); + test_inequality(); + test_inequality(); + test_inequality(); + test_inequality(); +} diff --git a/libgomp/testsuite/libgomp.c++/allocator-2.C b/libgomp/testsuite/libgomp.c++/allocator-2.C new file mode 100644 index 00000000000..d25b75501a9 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/allocator-2.C @@ -0,0 +1,132 @@ +// { dg-do run } +// { dg-additional-options "-Wno-psabi" } + +#include +#include + +template +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(ptr)); + return (ptr_value % alignment) == 0; +} + +template class Alloc> +void f (T v0, T v1, T v2, T v3) +{ + std::vector> 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(0, 1, 2, 3); + f(0, 1, 2, 3); + f(0, 1, 2, 3); + f(0, 1, 2, 3); + f(0, 1, 2, 3); + f(0, 1, 2, 3); + f(0, 1, 2, 3); + f(0, 1, 2, 3); + f(0, 1, 2, 3); + f(0, 1, 2, 3); + + f(0, 1, 2, 3); + f(0, 1, 2, 3); + f(0, 1, 2, 3); + f(0, 1, 2, 3); + f(0, 1, 2, 3); + f(0, 1, 2, 3); + f(0, 1, 2, 3); + f(0, 1, 2, 3); + f(0, 1, 2, 3); + f(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_0, s0_1, s0_2, s0_3); + f(s0_0, s0_1, s0_2, s0_3); + f(s0_0, s0_1, s0_2, s0_3); + f(s0_0, s0_1, s0_2, s0_3); + f(s0_0, s0_1, s0_2, s0_3); + f(s0_0, s0_1, s0_2, s0_3); + f(s0_0, s0_1, s0_2, s0_3); + f(s0_0, s0_1, s0_2, s0_3); + f(s0_0, s0_1, s0_2, s0_3); + f(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_0, s1_1, s1_2, s1_3); + f(s1_0, s1_1, s1_2, s1_3); + f(s1_0, s1_1, s1_2, s1_3); + f(s1_0, s1_1, s1_2, s1_3); + f(s1_0, s1_1, s1_2, s1_3); + f(s1_0, s1_1, s1_2, s1_3); + f(s1_0, s1_1, s1_2, s1_3); + f(s1_0, s1_1, s1_2, s1_3); + f(s1_0, s1_1, s1_2, s1_3); + f(s1_0, s1_1, s1_2, s1_3); +}