From 52ea1caf28cdcdff8c9b2be844e3f1be6c369f16 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Wed, 22 May 2019 21:29:39 +0100 Subject: [PATCH] PR libstdc++/77691 fix resource_adaptor failures due to max_align_t bugs Remove the hardcoded whitelist of allocators expected to return memory aligned to alignof(max_align_t), because that doesn't work when the platform's malloc() and GCC's max_align_t do not agree what the largest fundamental alignment is. It's also sub-optimal for user-defined allocators that return memory suitable for any fundamental alignment. Instead use a hardcoded list of alignments that are definitely supported by the platform malloc, and use a copy of the allocator rebound to a POD type with the requested alignment. Only allocate an oversized buffer to use with std::align for alignments larger than any of the hardcoded values. For 32-bit Solaris x86 do not include alignof(max_align_t) in the hardcoded values. PR libstdc++/77691 * include/experimental/memory_resource: Add system header pragma. (__resource_adaptor_common::__guaranteed_alignment): Remove. (__resource_adaptor_common::_Types) (__resource_adaptor_common::__new_list) (__resource_adaptor_common::_New_list) (__resource_adaptor_common::_Alignments) (__resource_adaptor_common::_Fund_align_types): New utilities for creating a list of types with fundamental alignments. (__resource_adaptor_imp::do_allocate): Call new _M_allocate function. (__resource_adaptor_imp::do_deallocate): Call new _M_deallocate function. (__resource_adaptor_imp::_M_allocate): New function that first tries to use an allocator rebound to a type with a fundamental alignment. (__resource_adaptor_imp::_M_deallocate): Likewise for deallocation. * testsuite/experimental/memory_resource/new_delete_resource.cc: Adjust expected allocation sizes. * testsuite/experimental/memory_resource/resource_adaptor.cc: Remove xfail. From-SVN: r271522 --- libstdc++-v3/ChangeLog | 18 ++++ .../include/experimental/memory_resource | 92 ++++++++++++------- .../memory_resource/new_delete_resource.cc | 26 ++++-- .../memory_resource/resource_adaptor.cc | 1 - 4 files changed, 94 insertions(+), 43 deletions(-) diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 056e35447de..cf9d86a809c 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,21 @@ +2019-05-22 Jonathan Wakely + + PR libstdc++/77691 + * include/experimental/memory_resource: Add system header pragma and + do not define anything unless compiled as C++14 or later. + (__resource_adaptor_common::__guaranteed_alignment): Remove. + (__resource_adaptor_imp::do_allocate): If the requested alignment + is a fundamental alignment then either allocate directly from _M_alloc + or call the new _M_allocate function. + (__resource_adaptor_imp::do_deallocate): Likewise for deallocation. + (__resource_adaptor_imp::_M_allocate): New function that uses a copy + of the allocator rebound to a POD type with the specified alignment. + (__resource_adaptor_imp::_M_deallocate): Likewise for deallocation. + * testsuite/experimental/memory_resource/new_delete_resource.cc: + Adjust expected allocation sizes. + * testsuite/experimental/memory_resource/resource_adaptor.cc: Remove + xfail for Solaris x86. + 2019-05-21 Thomas Rodgers LWG 3062 - Unnecessary decay_t in is_execution_policy_v diff --git a/libstdc++-v3/include/experimental/memory_resource b/libstdc++-v3/include/experimental/memory_resource index a0b30632154..dde3753fab7 100644 --- a/libstdc++-v3/include/experimental/memory_resource +++ b/libstdc++-v3/include/experimental/memory_resource @@ -30,6 +30,10 @@ #ifndef _GLIBCXX_EXPERIMENTAL_MEMORY_RESOURCE #define _GLIBCXX_EXPERIMENTAL_MEMORY_RESOURCE 1 +#pragma GCC system_header + +#if __cplusplus >= 201402L + #include // align, uses_allocator, __uses_alloc #include // pair, experimental::erased_type #include // atomic @@ -358,23 +362,6 @@ namespace pmr { return __val; } }; - - template - struct __guaranteed_alignment : std::integral_constant { }; - - template - struct __guaranteed_alignment<__gnu_cxx::new_allocator<_Tp>> - : std::alignment_of::type { }; - - template - struct __guaranteed_alignment<__gnu_cxx::malloc_allocator<_Tp>> - : std::alignment_of::type { }; - -#if _GLIBCXX_USE_ALLOCATOR_NEW - template - struct __guaranteed_alignment> - : std::alignment_of::type { }; -#endif }; /// @endcond @@ -425,14 +412,22 @@ namespace pmr { virtual void* do_allocate(size_t __bytes, size_t __alignment) override { - if (__alignment <= __guaranteed_alignment<_Alloc>::value) + // Cannot use max_align_t on 32-bit Solaris x86, see PR libstdc++/77691 +#if ! (defined __sun__ && defined __i386__) + if (__alignment == alignof(max_align_t)) + return _M_allocate(__bytes); +#endif + switch (__alignment) { - if (__bytes < __alignment) - __bytes = __alignment; + case 1: return _M_alloc.allocate(__bytes); + case 2: + return _M_allocate<2>(__bytes); + case 4: + return _M_allocate<4>(__bytes); + case 8: + return _M_allocate<8>(__bytes); } - - const _AlignMgr __mgr(__bytes, __alignment); // Assume _M_alloc returns 1-byte aligned memory, so allocate enough // space to fit a block of the right size and alignment, plus some @@ -441,21 +436,28 @@ namespace pmr { } virtual void - do_deallocate(void* __p, size_t __bytes, size_t __alignment) noexcept + do_deallocate(void* __ptr, size_t __bytes, size_t __alignment) noexcept override { - auto __ptr = static_cast(__p); - if (__alignment <= __guaranteed_alignment<_Alloc>::value) +#if ! (defined __sun__ && defined __i386__) + if (__alignment == alignof(max_align_t)) + return (void) _M_deallocate(__ptr, __bytes); +#endif + switch (__alignment) { - if (__bytes < __alignment) - __bytes = __alignment; - _M_alloc.deallocate(__ptr, __bytes); - return; + case 1: + return (void) _M_alloc.deallocate((char*)__ptr, __bytes); + case 2: + return (void) _M_deallocate<2>(__ptr, __bytes); + case 4: + return (void) _M_deallocate<4>(__ptr, __bytes); + case 8: + return (void) _M_deallocate<8>(__ptr, __bytes); } - const _AlignMgr __mgr(__bytes, __alignment); - // Use the stored token to retrieve the original pointer to deallocate. - _M_alloc.deallocate(__mgr._M_unadjust(__ptr), __mgr._M_alloc_size()); + // Use the stored token to retrieve the original pointer. + _M_alloc.deallocate(__mgr._M_unadjust((char*)__ptr), + __mgr._M_alloc_size()); } virtual bool @@ -467,6 +469,31 @@ namespace pmr { } private: + template + struct _Aligned_type { alignas(_Num) char __c[_Num]; }; + + // Rebind the allocator to the specified type and use it to allocate. + template> + void* + _M_allocate(size_t __bytes) + { + typename allocator_traits<_Alloc>::template + rebind_alloc<_Tp> __a2(_M_alloc); + const size_t __n = (__bytes + _Num - 1) / _Num; + return __a2.allocate(__n); + } + + // Rebind the allocator to the specified type and use it to deallocate. + template> + void + _M_deallocate(void* __ptr, size_t __bytes) noexcept + { + typename allocator_traits<_Alloc>::template + rebind_alloc<_Tp> __a2(_M_alloc); + const size_t __n = (__bytes + _Num - 1) / _Num; + __a2.deallocate((_Tp*)__ptr, __n); + } + _Alloc _M_alloc{}; }; @@ -537,4 +564,5 @@ namespace pmr { _GLIBCXX_END_NAMESPACE_VERSION } // namespace std +#endif // C++14 #endif // _GLIBCXX_EXPERIMENTAL_MEMORY_RESOURCE diff --git a/libstdc++-v3/testsuite/experimental/memory_resource/new_delete_resource.cc b/libstdc++-v3/testsuite/experimental/memory_resource/new_delete_resource.cc index 41232b6fc67..7dcb408f3f7 100644 --- a/libstdc++-v3/testsuite/experimental/memory_resource/new_delete_resource.cc +++ b/libstdc++-v3/testsuite/experimental/memory_resource/new_delete_resource.cc @@ -23,6 +23,11 @@ #include #include +#if defined __sun__ && defined __i386__ +// See PR libstdc++/77691 +# define BAD_MAX_ALIGN_T 1 +#endif + bool new_called = false; bool delete_called = false; std::size_t bytes_allocated = 0; @@ -38,14 +43,14 @@ void* operator new(std::size_t n) throw std::bad_alloc(); } -void operator delete(void* p) +void operator delete(void* p) noexcept { delete_called = true; std::free(p); bytes_allocated = 0; // assume everything getting deleted } -void operator delete(void* p, std::size_t n) +void operator delete(void* p, std::size_t n) noexcept { delete_called = true; std::free(p); @@ -55,9 +60,7 @@ void operator delete(void* p, std::size_t n) template bool aligned(void* p) - { - return (reinterpret_cast(p) % A) == 0; - } + { return (reinterpret_cast(p) % A) == 0; } template bool aligned(void* p) @@ -108,13 +111,17 @@ test03() using std::size_t; void* p = nullptr; - auto max = [](int n, int a) { return n > a ? n : a; }; + auto round = [](size_t n, size_t a) { return n % a ? n + a - (n % a) : n; }; bytes_allocated = 0; memory_resource* r1 = new_delete_resource(); p = r1->allocate(1); // uses alignment = alignof(max_align_t) - VERIFY( bytes_allocated <= alignof(max_align_t) ); +#ifdef BAD_MAX_ALIGN_T + VERIFY( bytes_allocated != 0 ); +#else + VERIFY( bytes_allocated == alignof(max_align_t) ); +#endif VERIFY( aligned(p) ); r1->deallocate(p, 1); VERIFY( bytes_allocated == 0 ); @@ -123,17 +130,16 @@ test03() VERIFY( bytes_allocated == 2 ); VERIFY( aligned(p) ); r1->deallocate(p, 2, alignof(char)); - __builtin_printf("%d\n", (int)bytes_allocated); VERIFY( bytes_allocated == 0 ); p = r1->allocate(3, alignof(short)); - VERIFY( bytes_allocated == max(3, alignof(short)) ); + VERIFY( bytes_allocated == round(3, alignof(short)) ); VERIFY( aligned(p) ); r1->deallocate(p, 3, alignof(short)); VERIFY( bytes_allocated == 0 ); p = r1->allocate(4, alignof(long)); - VERIFY( bytes_allocated == max(4, alignof(long)) ); + VERIFY( bytes_allocated == round(4, alignof(long)) ); VERIFY( aligned(p) ); r1->deallocate(p, 4, alignof(long)); VERIFY( bytes_allocated == 0 ); diff --git a/libstdc++-v3/testsuite/experimental/memory_resource/resource_adaptor.cc b/libstdc++-v3/testsuite/experimental/memory_resource/resource_adaptor.cc index 27ee1078dd4..56c8ffa2803 100644 --- a/libstdc++-v3/testsuite/experimental/memory_resource/resource_adaptor.cc +++ b/libstdc++-v3/testsuite/experimental/memory_resource/resource_adaptor.cc @@ -1,6 +1,5 @@ // { dg-do run { target c++14 } } // { dg-require-cstdint "" } -// { dg-xfail-run-if "PR libstdc++/77691" { { i?86-*-solaris2.* x86_64-*-solaris2.* } && ilp32 } } // Copyright (C) 2016-2019 Free Software Foundation, Inc. // -- 2.30.2