From 63e6ad798ec379a3a1b5b601c3ad9ca2b98b3d4d Mon Sep 17 00:00:00 2001 From: redi Date: Thu, 21 Jun 2018 14:01:11 +0000 Subject: [PATCH] PR libstdc++/70940 make pmr::resource_adaptor return aligned memory PR libstdc++/70940 * include/experimental/memory_resource (__resource_adaptor_common): New base class. (__resource_adaptor_common::_AlignMgr): Helper for obtaining aligned pointer from unaligned, and vice versa. (__resource_adaptor_imp::do_allocate): Use _AlignMgr to adjust allocated pointer to meet alignment request. (__resource_adaptor_imp::do_deallocate): Use _AlignMgr to retrieve original pointer for deallocation. (__resource_adaptor_imp::do_is_equal): Reformat. (__resource_adaptor_imp::_S_aligned_size): Remove. (__resource_adaptor_imp::_S_supported): Remove. (new_delete_resource): Use __gnu_cxx::new_allocator. * testsuite/experimental/memory_resource/resource_adaptor.cc: Test extended alignments and use debug_allocator to check for matching allocate/deallocate pairs. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@261849 138bc75d-0d04-0410-961f-82ee72b054a4 --- libstdc++-v3/ChangeLog | 19 +++ libstdc++-v3/include/experimental/memory_resource | 154 ++++++++++++++++----- .../memory_resource/resource_adaptor.cc | 52 ++++++- 3 files changed, 186 insertions(+), 39 deletions(-) diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 77605730bad..699ec136ae6 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,22 @@ +2018-06-21 Jonathan Wakely + + PR libstdc++/70940 + * include/experimental/memory_resource (__resource_adaptor_common): + New base class. + (__resource_adaptor_common::_AlignMgr): Helper for obtaining aligned + pointer from unaligned, and vice versa. + (__resource_adaptor_imp::do_allocate): Use _AlignMgr to adjust + allocated pointer to meet alignment request. + (__resource_adaptor_imp::do_deallocate): Use _AlignMgr to retrieve + original pointer for deallocation. + (__resource_adaptor_imp::do_is_equal): Reformat. + (__resource_adaptor_imp::_S_aligned_size): Remove. + (__resource_adaptor_imp::_S_supported): Remove. + (new_delete_resource): Use __gnu_cxx::new_allocator. + * testsuite/experimental/memory_resource/resource_adaptor.cc: Test + extended alignments and use debug_allocator to check for matching + allocate/deallocate pairs. + 2018-06-21 François Dumont * include/debug/debug.h diff --git a/libstdc++-v3/include/experimental/memory_resource b/libstdc++-v3/include/experimental/memory_resource index 670a2210804..3d2dce19868 100644 --- a/libstdc++-v3/include/experimental/memory_resource +++ b/libstdc++-v3/include/experimental/memory_resource @@ -33,6 +33,7 @@ #include #include #include +#include #include namespace std { @@ -253,9 +254,103 @@ namespace pmr { const polymorphic_allocator<_Tp2>& __b) noexcept { return !(__a == __b); } + class __resource_adaptor_common + { + template friend class __resource_adaptor_imp; + + struct _AlignMgr + { + _AlignMgr(size_t __nbytes, size_t __align) + : _M_nbytes(__nbytes), _M_align(__align) + { } + + // Total size that needs to be allocated. + size_t + _M_alloc_size() const { return _M_buf_size() + _M_token_size(); } + + void* + _M_adjust(void* __ptr) const + { + const auto __orig_ptr = static_cast(__ptr); + size_t __space = _M_buf_size(); + // Align the pointer within the buffer: + std::align(_M_align, _M_nbytes, __ptr, __space); + const auto __aligned_ptr = static_cast(__ptr); + const auto __token_size = _M_token_size(); + // Store token immediately after the aligned block: + char* const __end = __aligned_ptr + _M_nbytes; + if (__token_size == 1) + _S_write(__end, __aligned_ptr - __orig_ptr); + else if (__token_size == sizeof(short)) + _S_write(__end, __aligned_ptr - __orig_ptr); + else if (__token_size == sizeof(int) && sizeof(int) < sizeof(char*)) + _S_write(__end, __aligned_ptr - __orig_ptr); + else // (__token_size == sizeof(char*)) + // Just store the original pointer: + _S_write(__end, __orig_ptr); + return __aligned_ptr; + } + + char* + _M_unadjust(char* __ptr) const + { + const char* const __end = __ptr + _M_nbytes; + char* __orig_ptr; + const auto __token_size = _M_token_size(); + // Read the token and restore the original pointer: + if (__token_size == 1) + __orig_ptr = __ptr - _S_read(__end); + else if (__token_size == sizeof(short)) + __orig_ptr = __ptr - _S_read(__end); + else if (__token_size == sizeof(int) + && sizeof(int) < sizeof(char*)) + __orig_ptr = __ptr - _S_read(__end); + else // (__token_size == sizeof(char*)) + __orig_ptr = _S_read(__end); + return __orig_ptr; + } + + private: + size_t _M_nbytes; + size_t _M_align; + + // Number of bytes needed to fit block of given size and alignment. + size_t + _M_buf_size() const { return _M_nbytes + _M_align - 1; } + + // Number of additional bytes needed to write the token. + int + _M_token_size() const + { + if (_M_align <= (1ul << __CHAR_BIT__)) + return 1; + if (_M_align <= (1ul << (sizeof(short) * __CHAR_BIT__))) + return sizeof(short); + if (_M_align <= (1ul << (sizeof(int) * __CHAR_BIT__))) + return sizeof(int); + return sizeof(char*); + } + + template + static void + _S_write(void* __to, _Tp __val) + { __builtin_memcpy(__to, &__val, sizeof(_Tp)); } + + template + static _Tp + _S_read(const void* __from) + { + _Tp __val; + __builtin_memcpy(&__val, __from, sizeof(_Tp)); + return __val; + } + }; + }; + // 8.7.1 __resource_adaptor_imp template - class __resource_adaptor_imp : public memory_resource + class __resource_adaptor_imp + : public memory_resource, private __resource_adaptor_common { static_assert(is_same::value_type>::value, @@ -295,50 +390,41 @@ namespace pmr { protected: virtual void* - do_allocate(size_t __bytes, size_t __alignment) + do_allocate(size_t __bytes, size_t __alignment) override { - using _Aligned_alloc = std::__alloc_rebind<_Alloc, char>; - size_t __new_size = _S_aligned_size(__bytes, - _S_supported(__alignment) ? - __alignment : _S_max_align); - return _Aligned_alloc(_M_alloc).allocate(__new_size); + if (__alignment == 1) + return _M_alloc.allocate(__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 + // extra bytes to store a token for retrieving the original pointer. + return __mgr._M_adjust(_M_alloc.allocate(__mgr._M_alloc_size())); } virtual void - do_deallocate(void* __p, size_t __bytes, size_t __alignment) + do_deallocate(void* __p, size_t __bytes, size_t __alignment) noexcept + override { - using _Aligned_alloc = std::__alloc_rebind<_Alloc, char>; - size_t __new_size = _S_aligned_size(__bytes, - _S_supported(__alignment) ? - __alignment : _S_max_align); - using _Ptr = typename allocator_traits<_Aligned_alloc>::pointer; - _Aligned_alloc(_M_alloc).deallocate(static_cast<_Ptr>(__p), - __new_size); + auto __ptr = static_cast(__p); + if (__alignment == 1) + _M_alloc.deallocate(__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()); } virtual bool - do_is_equal(const memory_resource& __other) const noexcept + do_is_equal(const memory_resource& __other) const noexcept override { - auto __p = dynamic_cast(&__other); - return __p ? (_M_alloc == __p->_M_alloc) : false; + if (auto __p = dynamic_cast(&__other)) + return _M_alloc == __p->_M_alloc; + return false; } private: - // Calculate Aligned Size - // Returns a size that is larger than or equal to __size and divisible - // by __alignment, where __alignment is required to be a power of 2. - static size_t - _S_aligned_size(size_t __size, size_t __alignment) - { return ((__size - 1)|(__alignment - 1)) + 1; } - - // Determine whether alignment meets one of those preconditions: - // 1. Equal to Zero - // 2. Is power of two - static bool - _S_supported (size_t __x) - { return ((__x != 0) && !(__x & (__x - 1))); } - - _Alloc _M_alloc; + _Alloc _M_alloc{}; }; // Global memory resources @@ -352,7 +438,7 @@ namespace pmr { inline memory_resource* new_delete_resource() noexcept { - using type = resource_adaptor>; + using type = resource_adaptor<__gnu_cxx::new_allocator>; alignas(type) static unsigned char __buf[sizeof(type)]; static type* __r = new(__buf) type; return __r; diff --git a/libstdc++-v3/testsuite/experimental/memory_resource/resource_adaptor.cc b/libstdc++-v3/testsuite/experimental/memory_resource/resource_adaptor.cc index 340276fe96d..4e39e773248 100644 --- a/libstdc++-v3/testsuite/experimental/memory_resource/resource_adaptor.cc +++ b/libstdc++-v3/testsuite/experimental/memory_resource/resource_adaptor.cc @@ -19,6 +19,7 @@ // . #include +#include #include #include @@ -34,18 +35,22 @@ template Allocator(const Allocator&) { } }; -template +template bool aligned(void* p) { - return (reinterpret_cast(p) % alignof(T)) == 0; + return (reinterpret_cast(p) % A) == 0; } +template + bool aligned(void* p) + { return aligned(p); } + // resource_adaptor void test05() { using std::max_align_t; - using std::uintptr_t; + using std::size_t; void* p = nullptr; Allocator a1(1), a2(2); // minimal interface allocators @@ -61,12 +66,18 @@ test05() p = r1.allocate(1, alignof(long)); VERIFY( aligned(p) ); r1.deallocate(p, 1, alignof(long)); + constexpr size_t big_al = alignof(max_align_t) * 8; + p = r1.allocate(1, big_al); + VERIFY( aligned(p) ); + r1.deallocate(p, 1, big_al); __gnu_test::uneq_allocator a3(3), a4(4); // non-equal allocators resource_adaptor r3(a3), r4(a4); VERIFY( r3 == r3 ); VERIFY( r4 == r4 ); VERIFY( r3 != r4 ); + VERIFY( r3 != r1 ); + VERIFY( r3 != r2 ); p = r3.allocate(1); VERIFY( aligned(p) ); r3.deallocate(p, 1); @@ -76,9 +87,40 @@ test05() p = r3.allocate(1, alignof(long)); VERIFY( aligned(p) ); r3.deallocate(p, 1, alignof(long)); + p = r3.allocate(1, big_al); + VERIFY( aligned(p) ); + r3.deallocate(p, 1, big_al); + + __gnu_cxx::debug_allocator> a5; + resource_adaptor r5(a5), r6(a5); + VERIFY( r5 == r5 ); + VERIFY( r5 == r6 ); + VERIFY( r5 != r1 ); + VERIFY( r5 != r3 ); + p = r5.allocate(1); + VERIFY( aligned(p) ); + r5.deallocate(p, 1); + p = r5.allocate(1, alignof(short)); + VERIFY( aligned(p) ); + r5.deallocate(p, 1, alignof(short)); + p = r5.allocate(1, alignof(long)); + VERIFY( aligned(p) ); + r5.deallocate(p, 1, alignof(long)); + p = r5.allocate(1, big_al); + VERIFY( aligned(p) ); + r5.deallocate(p, 1, big_al); - // TODO test with an allocator that doesn't use new or malloc, so - // returns pointers that are not suitably aligned for any type. + // Test extended alignments + constexpr size_t al6 = (1ul << 6), al12 = (1ul << 12), al18 = (1ul << 18); + p = r5.allocate(1024, al6); + VERIFY( aligned(p) ); + r5.deallocate(p, 1024, al6); + p = r5.allocate(1024, al12); + VERIFY( aligned(p) ); + r5.deallocate(p, 1024, al12); + p = r5.allocate(1024, al18); + VERIFY( aligned(p) ); + r5.deallocate(p, 1024, al18); } int main() -- 2.11.4.GIT