https://gcc.gnu.org/g:b0efdcbf58a76af3b8fff75f1d53d334fb5b46ee

commit r15-997-gb0efdcbf58a76af3b8fff75f1d53d334fb5b46ee
Author: Jonathan Wakely <jwak...@redhat.com>
Date:   Wed Apr 13 13:03:44 2022 +0100

    libstdc++: Handle extended alignment in std::get_temporary_buffer [PR105258]
    
    This adds extended alignment support to std::get_temporary_buffer etc.
    so that when std::stable_sort uses a temporary buffer it works for
    overaligned types.
    
    Also simplify the _Temporary_buffer type by using RAII for the
    allocation, via a new data member. This simplifies the _Temporary_buffer
    constructor and destructor by makingthem only responsible for
    constructing and destroying the elements, not managing the memory.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/105258
            * include/bits/stl_tempbuf.h (__detail::__get_temporary_buffer):
            New function to do allocation for get_temporary_buffer, with
            extended alignment support.
            (__detail::__return_temporary_buffer): Support extended
            alignment.
            (get_temporary_buffer): Use __get_temporary_buffer.
            (return_temporary_buffer): Support extended alignment. Add
            deprecated attribute.
            (_Temporary_buffer): Move allocation and deallocation into a
            subobject and remove try-catch block in constructor.
            (__uninitialized_construct_buf): Use argument deduction for
            value type.
            * testsuite/20_util/temporary_buffer.cc: Add dg-warning for new
            deprecated warning.
            * testsuite/25_algorithms/stable_sort/overaligned.cc: New test.

Diff:
---
 libstdc++-v3/include/bits/stl_tempbuf.h            | 131 ++++++++++++++-------
 libstdc++-v3/testsuite/20_util/temporary_buffer.cc |   2 +-
 .../25_algorithms/stable_sort/overaligned.cc       |  29 +++++
 3 files changed, 116 insertions(+), 46 deletions(-)

diff --git a/libstdc++-v3/include/bits/stl_tempbuf.h 
b/libstdc++-v3/include/bits/stl_tempbuf.h
index 77b121460f9..fa03fd27704 100644
--- a/libstdc++-v3/include/bits/stl_tempbuf.h
+++ b/libstdc++-v3/include/bits/stl_tempbuf.h
@@ -66,19 +66,58 @@ namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
+#if __has_builtin(__builtin_operator_new) >= 201802L
+# define _GLIBCXX_OPERATOR_NEW __builtin_operator_new
+# define _GLIBCXX_OPERATOR_DELETE __builtin_operator_delete
+#else
+# define _GLIBCXX_OPERATOR_NEW ::operator new
+# define _GLIBCXX_OPERATOR_DELETE ::operator delete
+#endif
+
   namespace __detail
   {
+    // Equivalent to std::get_temporary_buffer but won't return a smaller size.
+    // It either returns a buffer of __len elements, or a null pointer.
+    template<typename _Tp>
+      inline _Tp*
+      __get_temporary_buffer(ptrdiff_t __len) _GLIBCXX_NOTHROW
+      {
+       if (__builtin_expect(__len > (size_t(-1) / sizeof(_Tp)), 0))
+         return 0;
+
+#if __cpp_aligned_new
+       if (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
+         return (_Tp*) _GLIBCXX_OPERATOR_NEW(__len * sizeof(_Tp),
+                                             align_val_t(alignof(_Tp)),
+                                             nothrow_t());
+#endif
+       return (_Tp*) _GLIBCXX_OPERATOR_NEW(__len * sizeof(_Tp), nothrow_t());
+      }
+
+    // Equivalent to std::return_temporary_buffer but with a size argument.
+    // The size is the number of elements, not the number of bytes.
     template<typename _Tp>
       inline void
       __return_temporary_buffer(_Tp* __p,
                                size_t __len __attribute__((__unused__)))
       {
 #if __cpp_sized_deallocation
-       ::operator delete(__p, __len * sizeof(_Tp));
+# define _GLIBCXX_SIZED_DEALLOC(T, p, n) (p), (n) * sizeof(T)
 #else
-       ::operator delete(__p);
+# define _GLIBCXX_SIZED_DEALLOC(T, p, n) (p)
 #endif
+
+#if __cpp_aligned_new
+       if (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
+         {
+           _GLIBCXX_OPERATOR_DELETE(_GLIBCXX_SIZED_DEALLOC(_Tp, __p, __len),
+                                    align_val_t(alignof(_Tp)));
+           return;
+         }
+#endif
+       _GLIBCXX_OPERATOR_DELETE(_GLIBCXX_SIZED_DEALLOC(_Tp, __p, __len));
       }
+#undef _GLIBCXX_SIZED_DEALLOC
   }
 
   /**
@@ -90,7 +129,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    *
    *  This function tries to obtain storage for @c __len adjacent Tp
    *  objects.  The objects themselves are not constructed, of course.
-   *  A pair<> is returned containing <em>the buffer s address and
+   *  A pair<> is returned containing <em>the buffer's address and
    *  capacity (in the units of sizeof(_Tp)), or a pair of 0 values if
    *  no storage can be obtained.</em>  Note that the capacity obtained
    *  may be less than that requested if the memory is unavailable;
@@ -110,13 +149,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       while (__len > 0)
        {
-         _Tp* __tmp = static_cast<_Tp*>(::operator new(__len * sizeof(_Tp),
-                                                       std::nothrow));
-         if (__tmp != 0)
-           return std::pair<_Tp*, ptrdiff_t>(__tmp, __len);
+         if (_Tp* __tmp = __detail::__get_temporary_buffer<_Tp>(__len))
+           return pair<_Tp*, ptrdiff_t>(__tmp, __len);
          __len = __len == 1 ? 0 : ((__len + 1) / 2);
        }
-      return std::pair<_Tp*, ptrdiff_t>(static_cast<_Tp*>(0), 0);
+      return pair<_Tp*, ptrdiff_t>();
     }
 
   /**
@@ -127,9 +164,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    *  Frees the memory pointed to by __p.
    */
   template<typename _Tp>
+    _GLIBCXX17_DEPRECATED
     inline void
     return_temporary_buffer(_Tp* __p)
-    { ::operator delete(__p); }
+    {
+#if __cpp_aligned_new
+      if (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
+       _GLIBCXX_OPERATOR_DELETE(__p, align_val_t(alignof(_Tp)));
+      else
+#endif
+      _GLIBCXX_OPERATOR_DELETE(__p);
+    }
+
+#undef _GLIBCXX_OPERATOR_DELETE
+#undef _GLIBCXX_OPERATOR_NEW
 
   /**
    *  This class is used in two places: stl_algo.h and ext/memory,
@@ -150,14 +198,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
     protected:
       size_type  _M_original_len;
-      size_type  _M_len;
-      pointer    _M_buffer;
+      struct _Impl
+      {
+       explicit
+       _Impl(ptrdiff_t __original_len)
+       {
+         pair<pointer, size_type> __p(
+           std::get_temporary_buffer<value_type>(__original_len));
+         _M_len = __p.second;
+         _M_buffer = __p.first;
+       }
+
+       ~_Impl()
+       { std::__detail::__return_temporary_buffer(_M_buffer, _M_len); }
+
+       size_type  _M_len;
+       pointer    _M_buffer;
+      } _M_impl;
 
     public:
       /// As per Table mumble.
       size_type
       size() const
-      { return _M_len; }
+      { return _M_impl._M_len; }
 
       /// Returns the size requested by the constructor; may be >size().
       size_type
@@ -167,12 +230,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       /// As per Table mumble.
       iterator
       begin()
-      { return _M_buffer; }
+      { return _M_impl._M_buffer; }
 
       /// As per Table mumble.
       iterator
       end()
-      { return _M_buffer + _M_len; }
+      { return _M_impl._M_buffer + _M_impl._M_len; }
 
       /**
        * Constructs a temporary buffer of a size somewhere between
@@ -181,10 +244,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Temporary_buffer(_ForwardIterator __seed, size_type __original_len);
 
       ~_Temporary_buffer()
-      {
-       std::_Destroy(_M_buffer, _M_buffer + _M_len);
-       std::__detail::__return_temporary_buffer(_M_buffer, _M_len);
-      }
+      { std::_Destroy(_M_impl._M_buffer, _M_impl._M_buffer + _M_impl._M_len); }
 
     private:
       // Disable copy constructor and assignment operator.
@@ -203,7 +263,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
         __ucr(_Pointer __first, _Pointer __last,
              _ForwardIterator __seed)
         {
-         if (__first == __last)
+         if (__builtin_expect(__first == __last, 0))
            return;
 
          _Pointer __cur = __first;
@@ -243,17 +303,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // the same value when the algorithm finishes, unless one of the
   // constructions throws.
   //
-  // Requirements: _Pointer::value_type(_Tp&&) is valid.
-  template<typename _Pointer, typename _ForwardIterator>
+  // Requirements:
+  // _Tp is move constructible and constructible from std::move(*__seed).
+  template<typename _Tp, typename _ForwardIterator>
     inline void
-    __uninitialized_construct_buf(_Pointer __first, _Pointer __last,
+    __uninitialized_construct_buf(_Tp* __first, _Tp* __last,
                                  _ForwardIterator __seed)
     {
-      typedef typename std::iterator_traits<_Pointer>::value_type
-       _ValueType;
-
       std::__uninitialized_construct_buf_dispatch<
-        __has_trivial_constructor(_ValueType)>::
+       __has_trivial_constructor(_Tp)>::
          __ucr(__first, __last, __seed);
     }
 
@@ -262,26 +320,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _ForwardIterator, typename _Tp>
     _Temporary_buffer<_ForwardIterator, _Tp>::
     _Temporary_buffer(_ForwardIterator __seed, size_type __original_len)
-    : _M_original_len(__original_len), _M_len(0), _M_buffer(0)
+    : _M_original_len(__original_len), _M_impl(__original_len)
     {
-      std::pair<pointer, size_type> __p(
-               std::get_temporary_buffer<value_type>(_M_original_len));
-
-      if (__p.first)
-       {
-         __try
-           {
-             std::__uninitialized_construct_buf(__p.first, __p.first + 
__p.second,
-                                                __seed);
-             _M_buffer = __p.first;
-             _M_len = __p.second;
-           }
-         __catch(...)
-           {
-             std::__detail::__return_temporary_buffer(__p.first, __p.second);
-             __throw_exception_again;
-           }
-       }
+      std::__uninitialized_construct_buf(begin(), end(), __seed);
     }
 #pragma GCC diagnostic pop
 
diff --git a/libstdc++-v3/testsuite/20_util/temporary_buffer.cc 
b/libstdc++-v3/testsuite/20_util/temporary_buffer.cc
index 155d19034e5..065739be29d 100644
--- a/libstdc++-v3/testsuite/20_util/temporary_buffer.cc
+++ b/libstdc++-v3/testsuite/20_util/temporary_buffer.cc
@@ -44,7 +44,7 @@ int main(void)
       VERIFY( results.first == 0 );
   }
 
-  std::return_temporary_buffer(results.first);
+  std::return_temporary_buffer(results.first); // { dg-warning "deprecated" "" 
{ target c++17 } }
 
   return 0;
 }
diff --git a/libstdc++-v3/testsuite/25_algorithms/stable_sort/overaligned.cc 
b/libstdc++-v3/testsuite/25_algorithms/stable_sort/overaligned.cc
new file mode 100644
index 00000000000..3c200624617
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/stable_sort/overaligned.cc
@@ -0,0 +1,29 @@
+// { dg-do run { target c++17 } }
+// PR libstdc++/105258 std::get_temporary_buffer() does not respect alignment
+
+#include <algorithm>
+#include <cstdint>
+#include <testsuite_hooks.h>
+
+struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2) Overaligned
+{
+  ~Overaligned()
+  {
+    auto alignment = reinterpret_cast<std::uintptr_t>(this);
+    VERIFY( (alignment % alignof(Overaligned)) == 0 );
+  }
+
+  bool operator<(const Overaligned&) const { return false; }
+};
+
+void
+test_pr105258()
+{
+  Overaligned o[2];
+  std::stable_sort(o, o+2);
+}
+
+int main()
+{
+  test_pr105258();
+}

Reply via email to