On Fri, Oct 24, 2025 at 8:08 PM Jonathan Wakely <[email protected]> wrote:
> Switch std::valarray<T> memory allocation to use std::__new_allocator<T>
> to allocate and deallocate memory. This adds support for extended
> alignment types without needing to duplicate all the logic from
> __new_allocator::allocate and __new_allocator::dellocate.
>
> std::__new_allocator is used instead of std::allocator because we want
> to ensure that the memory is still allocated with operator new, so we
> don't want to use any possible program-defined specialization of
> std::allocator<T> which the user might have provided.
>
> To make using an allocator possible, __valarray_release_memory needs to
> become a function template so that it knows the type T. It also needs an
> additional parameter specifying the size of the allocation.
>
> This change doesn't cause an ABI change for types with fundamental
> alignment, because __new_allocator still uses the same operator delete
> function (or the sized version, which is ABI compatible) to free the
> memory. So if memory for a valarray is allocated in one translation unit
> and deallocated in another, and those TUs are compiled with different
> versions of GCC, we still get memory from the same operator new and
> release it with the same operator delete (or the compatibled sized
> version). For types with extended alignment this does potentially cause
> an ABI change on targets where the aligned version of operator delete
> doesn't just call free(p), but support for extended alignment types was
> previously just broken and had undefined behaviour.
>
> libstdc++-v3/ChangeLog:
>
> PR libstdc++/108951
> * include/bits/valarray_array.h( __valarray_get_storage): Use
> std::__new_allocator.
> (__valarray_release_memory): Likewise.
> * include/std/valarray: Pass _M_size to
> __valarray_release_memory.
> * testsuite/26_numerics/valarray/108951.cc: New test.
> ---
>
> Tested powerpc64le-linux.
>
LGTM.
>
> I don't know why the allocating function is "get_storage" and the
> deallocating one is "release_memory" rather than "release_storage".
>
We are changing the signature, and mangled name anyway, so we could rename
it to release_storage. I am OK either way.
>
> libstdc++-v3/include/bits/valarray_array.h | 10 +++++----
> libstdc++-v3/include/std/valarray | 12 +++++-----
> .../testsuite/26_numerics/valarray/108951.cc | 22 +++++++++++++++++++
> 3 files changed, 34 insertions(+), 10 deletions(-)
> create mode 100644 libstdc++-v3/testsuite/26_numerics/valarray/108951.cc
>
> diff --git a/libstdc++-v3/include/bits/valarray_array.h
> b/libstdc++-v3/include/bits/valarray_array.h
> index b5c02b77b105..d1b712c3485a 100644
> --- a/libstdc++-v3/include/bits/valarray_array.h
> +++ b/libstdc++-v3/include/bits/valarray_array.h
> @@ -38,6 +38,7 @@
>
> #include <bits/c++config.h>
> #include <bits/cpp_type_traits.h>
> +#include <bits/new_allocator.h>
> #include <cstdlib>
> #include <new>
>
> @@ -57,12 +58,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> template<typename _Tp>
> inline _Tp*
> __valarray_get_storage(size_t __n)
> - { return static_cast<_Tp*>(operator new(__n * sizeof(_Tp))); }
> + { return std::__new_allocator<_Tp>().allocate(__n); }
>
> // Return memory to the system
> - inline void
> - __valarray_release_memory(void* __p)
> - { operator delete(__p); }
> + template<typename _Tp>
> + inline void
> + __valarray_release_memory(_Tp* __p, size_t __n)
> + { std::__new_allocator<_Tp>().deallocate(__p, __n); }
>
> #pragma GCC diagnostic push
> #pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
> diff --git a/libstdc++-v3/include/std/valarray
> b/libstdc++-v3/include/std/valarray
> index 82b58efad8ca..ac15e79d040e 100644
> --- a/libstdc++-v3/include/std/valarray
> +++ b/libstdc++-v3/include/std/valarray
> @@ -720,7 +720,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> valarray<_Tp>::~valarray() _GLIBCXX_NOEXCEPT
> {
> std::__valarray_destroy_elements(_M_data, _M_data + _M_size);
> - std::__valarray_release_memory(_M_data);
> + std::__valarray_release_memory(_M_data, _M_size);
> }
>
> template<typename _Tp>
> @@ -736,7 +736,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> if (_M_data)
> {
> std::__valarray_destroy_elements(_M_data, _M_data + _M_size);
> - std::__valarray_release_memory(_M_data);
> + std::__valarray_release_memory(_M_data, _M_size);
> }
> _M_size = __v._M_size;
> _M_data = __valarray_get_storage<_Tp>(_M_size);
> @@ -754,7 +754,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> if (_M_data)
> {
> std::__valarray_destroy_elements(_M_data, _M_data + _M_size);
> - std::__valarray_release_memory(_M_data);
> + std::__valarray_release_memory(_M_data, _M_size);
> }
> _M_size = __v._M_size;
> _M_data = __v._M_data;
> @@ -776,7 +776,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> if (_M_data)
> {
> std::__valarray_destroy_elements(_M_data, _M_data + _M_size);
> - std::__valarray_release_memory(_M_data);
> + std::__valarray_release_memory(_M_data, _M_size);
> }
> _M_size = __l.size();
> _M_data = __valarray_get_storage<_Tp>(_M_size);
> @@ -854,7 +854,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> if (_M_data)
> {
> std::__valarray_destroy_elements(_M_data, _M_data + _M_size);
> - std::__valarray_release_memory(_M_data);
> + std::__valarray_release_memory(_M_data, _M_size);
> }
> _M_size = __e.size();
> _M_data = __valarray_get_storage<_Tp>(_M_size);
> @@ -1049,7 +1049,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> std::__valarray_destroy_elements(_M_data, _M_data + _M_size);
> if (_M_size != __n)
> {
> - std::__valarray_release_memory(_M_data);
> + std::__valarray_release_memory(_M_data, _M_size);
> _M_size = __n;
> _M_data = __valarray_get_storage<_Tp>(__n);
> }
> diff --git a/libstdc++-v3/testsuite/26_numerics/valarray/108951.cc
> b/libstdc++-v3/testsuite/26_numerics/valarray/108951.cc
> new file mode 100644
> index 000000000000..929a1d499285
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/26_numerics/valarray/108951.cc
> @@ -0,0 +1,22 @@
> +// { dg-do run { target c++11 } }
> +// { dg-additional-options "-faligned-new" { target c++14_down } }
> +
> +#include <valarray>
> +#include <cstdint>
> +#include <testsuite_hooks.h>
> +
> +struct alignas(64) Num
> +{
> + Num()
> + {
> + VERIFY(reinterpret_cast<std::uintptr_t>(this) % alignof(*this) == 0);
> + }
> +
> + double val{};
> +};
> +
> +int main()
> +{
> + std::valarray<Num> v(2);
> + v.resize(4, {});
> +}
> --
> 2.51.0
>
>