PR libstdc++/70940 make pmr::resource_adaptor return aligned memory
authorJonathan Wakely <jwakely@redhat.com>
Thu, 21 Jun 2018 14:01:11 +0000 (15:01 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Thu, 21 Jun 2018 14:01:11 +0000 (15:01 +0100)
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.

From-SVN: r261849

libstdc++-v3/ChangeLog
libstdc++-v3/include/experimental/memory_resource
libstdc++-v3/testsuite/experimental/memory_resource/resource_adaptor.cc

index 77605730bad40ce014294e9bb09d95c37f769b3a..699ec136ae6db92a066f7122508136923b727adb 100644 (file)
@@ -1,3 +1,22 @@
+2018-06-21  Jonathan Wakely  <jwakely@redhat.com>
+
+       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  <fdumont@gcc.gnu.org>
 
        * include/debug/debug.h
index 670a2210804a2033187640d664324bc068b6c0d5..3d2dce19868e6fcad6636225a02fe2d2fc81dd01 100644 (file)
@@ -33,6 +33,7 @@
 #include <new>
 #include <atomic>
 #include <cstddef>
+#include <ext/new_allocator.h>
 #include <experimental/bits/lfts_config.h>
 
 namespace std {
@@ -253,9 +254,103 @@ namespace pmr {
                    const polymorphic_allocator<_Tp2>& __b) noexcept
     { return !(__a == __b); }
 
+  class __resource_adaptor_common
+  {
+    template<typename> 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<char*>(__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<char*>(__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<unsigned char>(__end, __aligned_ptr - __orig_ptr);
+       else if (__token_size == sizeof(short))
+         _S_write<unsigned short>(__end, __aligned_ptr - __orig_ptr);
+       else if (__token_size == sizeof(int) && sizeof(int) < sizeof(char*))
+         _S_write<unsigned int>(__end, __aligned_ptr - __orig_ptr);
+       else // (__token_size == sizeof(char*))
+         // Just store the original pointer:
+         _S_write<char*>(__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<unsigned char>(__end);
+       else if (__token_size == sizeof(short))
+         __orig_ptr = __ptr - _S_read<unsigned short>(__end);
+       else if (__token_size == sizeof(int)
+           && sizeof(int) < sizeof(char*))
+         __orig_ptr = __ptr - _S_read<unsigned int>(__end);
+       else // (__token_size == sizeof(char*))
+         __orig_ptr = _S_read<char*>(__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<typename _Tp>
+       static void
+       _S_write(void* __to, _Tp __val)
+       { __builtin_memcpy(__to, &__val, sizeof(_Tp)); }
+
+      template<typename _Tp>
+       static _Tp
+       _S_read(const void* __from)
+       {
+         _Tp __val;
+         __builtin_memcpy(&__val, __from, sizeof(_Tp));
+         return __val;
+       }
+    };
+  };
+
   // 8.7.1 __resource_adaptor_imp
   template <typename _Alloc>
-    class __resource_adaptor_imp : public memory_resource
+    class __resource_adaptor_imp
+    : public memory_resource, private __resource_adaptor_common
     {
       static_assert(is_same<char,
          typename allocator_traits<_Alloc>::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<char*>(__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<const __resource_adaptor_imp*>(&__other);
-       return __p ? (_M_alloc == __p->_M_alloc) : false;
+       if (auto __p = dynamic_cast<const __resource_adaptor_imp*>(&__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<std::allocator<char>>;
+    using type = resource_adaptor<__gnu_cxx::new_allocator<char>>;
     alignas(type) static unsigned char __buf[sizeof(type)];
     static type* __r = new(__buf) type;
     return __r;
index 340276fe96d1d5044c6bd56ae5d2a08c7e2cccaa..4e39e773248b6c7f4dc07d7e14db09cb115608ea 100644 (file)
@@ -19,6 +19,7 @@
 // <http://www.gnu.org/licenses/>.
 
 #include <experimental/memory_resource>
+#include <ext/debug_allocator.h>
 #include <testsuite_hooks.h>
 #include <testsuite_allocator.h>
 
@@ -34,18 +35,22 @@ template<typename T>
       Allocator(const Allocator<U>&) { }
   };
 
-template<typename T>
+template<std::size_t A>
   bool aligned(void* p)
   {
-    return (reinterpret_cast<std::uintptr_t>(p) % alignof(T)) == 0;
+    return (reinterpret_cast<std::uintptr_t>(p) % A) == 0;
   }
 
+template<typename T>
+  bool aligned(void* p)
+  { return aligned<alignof(T)>(p); }
+
 // resource_adaptor
 void
 test05()
 {
   using std::max_align_t;
-  using std::uintptr_t;
+  using std::size_t;
   void* p = nullptr;
 
   Allocator<int> a1(1), a2(2); // minimal interface allocators
@@ -61,12 +66,18 @@ test05()
   p = r1.allocate(1, alignof(long));
   VERIFY( aligned<long>(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<big_al>(p) );
+  r1.deallocate(p, 1, big_al);
 
   __gnu_test::uneq_allocator<double> a3(3), a4(4); // non-equal allocators
   resource_adaptor<decltype(a3)> 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<max_align_t>(p) );
   r3.deallocate(p, 1);
@@ -76,9 +87,40 @@ test05()
   p = r3.allocate(1, alignof(long));
   VERIFY( aligned<long>(p) );
   r3.deallocate(p, 1, alignof(long));
+  p = r3.allocate(1, big_al);
+  VERIFY( aligned<big_al>(p) );
+  r3.deallocate(p, 1, big_al);
+
+  __gnu_cxx::debug_allocator<std::allocator<short>> a5;
+  resource_adaptor<decltype(a5)> r5(a5), r6(a5);
+  VERIFY( r5 == r5 );
+  VERIFY( r5 == r6 );
+  VERIFY( r5 != r1 );
+  VERIFY( r5 != r3 );
+  p = r5.allocate(1);
+  VERIFY( aligned<max_align_t>(p) );
+  r5.deallocate(p, 1);
+  p = r5.allocate(1, alignof(short));
+  VERIFY( aligned<short>(p) );
+  r5.deallocate(p, 1, alignof(short));
+  p = r5.allocate(1, alignof(long));
+  VERIFY( aligned<long>(p) );
+  r5.deallocate(p, 1, alignof(long));
+  p = r5.allocate(1, big_al);
+  VERIFY( aligned<big_al>(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<al6>(p) );
+  r5.deallocate(p, 1024, al6);
+  p = r5.allocate(1024, al12);
+  VERIFY( aligned<al12>(p) );
+  r5.deallocate(p, 1024, al12);
+  p = r5.allocate(1024, al18);
+  VERIFY( aligned<al18>(p) );
+  r5.deallocate(p, 1024, al18);
 }
 
 int main()