From: Jonathan Wakely <jwakely@redhat.com>
To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org
Subject: Re: [PATCH v2 1/2] libstdc++: Handle extended alignment in std::get_temporary_buffer [PR105258]
Date: Mon, 3 Jun 2024 21:22:18 +0100 [thread overview]
Message-ID: <CACb0b4kza0aBerwDZrcb0PjnGoQjhYO-nbb4dbk1zwENEPYfLg@mail.gmail.com> (raw)
In-Reply-To: <20240601112310.893276-1-jwakely@redhat.com>
On Sat, 1 Jun 2024 at 12:23, Jonathan Wakely <jwakely@redhat.com> wrote:
>
> Although it's only used from places where we are allocating a sensible
> size, __detail::__get_temporary_buffer should really still check that
> len * sizeof(T) doesn't wrap around to zero and allocate a buffer that's
> smaller than expected.
>
> This v2 patch is the same as v1 except for adding this check:
>
> 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),
Pushed to trunk now.
> -- >8 --
>
> 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.
> ---
> libstdc++-v3/include/bits/stl_tempbuf.h | 131 ++++++++++++------
> .../testsuite/20_util/temporary_buffer.cc | 2 +-
> .../25_algorithms/stable_sort/overaligned.cc | 29 ++++
> 3 files changed, 116 insertions(+), 46 deletions(-)
> create mode 100644 libstdc++-v3/testsuite/25_algorithms/stable_sort/overaligned.cc
>
> 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();
> +}
> --
> 2.45.1
>
next prev parent reply other threads:[~2024-06-03 20:22 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-06-01 10:25 [PATCH " Jonathan Wakely
2024-06-01 10:25 ` [PATCH 2/2] libstdc++: Reuse temporary buffer utils in <stacktrace> Jonathan Wakely
2024-06-03 20:22 ` Jonathan Wakely
2024-06-01 11:19 ` [PATCH v2 1/2] libstdc++: Handle extended alignment in std::get_temporary_buffer [PR105258] Jonathan Wakely
2024-06-03 20:22 ` Jonathan Wakely [this message]
2024-06-18 18:04 ` Stephan Bergmann
2024-06-18 19:38 ` Jonathan Wakely
2024-06-19 13:08 ` [committed] libstdc++: Fix warning regressions in <bits/stl_tempbuf.h> Jonathan Wakely
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CACb0b4kza0aBerwDZrcb0PjnGoQjhYO-nbb4dbk1zwENEPYfLg@mail.gmail.com \
--to=jwakely@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=libstdc++@gcc.gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).