[PATCH v2] libstdc++: allocate_at_least ask only what it reports (P0401)

Nathan Myers ncm@cantrip.org
Tue May 26 20:24:08 GMT 2026


Please ignore this patch posted in error.

On 5/26/26 4:16 PM, Nathan Myers wrote:
> allocate_at_least is rounding up the allocation request size to
> its default alignment, which may be more than an integral
> multiple of the object size requested. When the memory is freed,
> what the container reports it is freeing differs from the amount
> that was allocated. This patch rounds the request size back down
> to what will be reported to the caller. The added test checks
> the value actually passed to ::operator new.
> 
> The algorithm to compute the allocation is altered in response
> to findings on godbolt.org, which indicate dropping to uint8 to
> perform the division is a pessimization everywhere other than
> x86. Most targets implement the altered code with multiplication,
> instead.
> 
> In addition, the dance to avoid provoking the PR108377 spurious
> warning is changed so as not to generate redundant code.
> 
> Tested on x86 -m64/-m32.
> 
> libstdc++-v3/ChangeLog:
> 	* include/bits/new_allocator.h (allocate_at_least): Reduce
> 	allocation to match what is reported.
> 	* testsuite/20_util/allocator/allocate_at_least2.cc: New tests.
> ---
>   libstdc++-v3/include/bits/new_allocator.h     | 20 +++----
>   .../20_util/allocator/allocate_at_least2.cc   | 52 +++++++++++++++++++
>   2 files changed, 63 insertions(+), 9 deletions(-)
>   create mode 100644 libstdc++-v3/testsuite/20_util/allocator/allocate_at_least2.cc
> 
> diff --git a/libstdc++-v3/include/bits/new_allocator.h b/libstdc++-v3/include/bits/new_allocator.h
> index 4524355a4a0..2890f9af8d5 100644
> --- a/libstdc++-v3/include/bits/new_allocator.h
> +++ b/libstdc++-v3/include/bits/new_allocator.h
> @@ -31,6 +31,7 @@
>   #define _STD_NEW_ALLOCATOR_H 1
>   
>   #include <bits/c++config.h>
> +#include <utility>
>   #include <new>
>   #include <bits/new_throw.h>
>   #include <bits/move.h>
> @@ -186,15 +187,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   		_S_check_allocation_limit(__n);
>   		const size_t __need = __n * sizeof(_Tp);
>   		const size_t __mask = __STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1;
> -		size_t __ask = (__need + __mask) & ~__mask;
> -		// Avoid rounding up to and asking for 2^63 bytes (PR108377):
> -		__ask -= __ask >> (__SIZE_WIDTH__ - 1);
> -		auto* __p = static_cast<_Tp*>(_GLIBCXX_OPERATOR_NEW(__ask));
> -		using _U8 = const unsigned char;
> -		static_assert(sizeof(_Tp) <= ~_U8());
> -		// Use 8-bit division for minimal latency:
> -		_U8 __spare = __ask - __need, __size = sizeof(_Tp);
> -		return { __p , __n + __spare / __size };
> +		const size_t __max = (__need + __mask) & ~__mask;
> +		const size_t __spare = __max - __need;
> +		if (__spare > __mask) std::unreachable();
> +		const size_t __bonus = __spare / sizeof(_Tp);
> +		const size_t __left =  __spare % sizeof(_Tp);
> +		const size_t __ask = __max - __left;
> +		// Avoid appearing to ask for 2^63 bytes (PR108377):
> +		if (__ask >> (__SIZE_WIDTH__ - 1)) std::unreachable();
> +		void* __p = _GLIBCXX_OPERATOR_NEW(__ask);
> +		return { static_cast<_Tp*>(__p), __n + __bonus };
>   	      }
>   	  }
>   	return { allocate(__n), __n };
> diff --git a/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least2.cc b/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least2.cc
> new file mode 100644
> index 00000000000..809e61ff8f9
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least2.cc
> @@ -0,0 +1,52 @@
> +// { dg-do run { target c++23 } }
> +
> +#include <memory>
> +#include <testsuite_hooks.h>
> +
> +std::size_t gn = 0;
> +void* operator new(std::size_t n)
> +{
> +  gn = n;
> +  return ::malloc(n);
> +}
> +
> +void operator delete(void* p) noexcept
> +{ ::free(p); }
> +
> +void operator delete(void* p, std::size_t n) noexcept
> +{ (void)n; ::free(p); }
> +
> +void a11()
> +{
> +  struct A { char a[11]; };
> +  std::allocator<A> alloc;
> +  using alloc_traits = std::allocator_traits<std::allocator<A>>;
> +  auto [p, m] = alloc_traits::allocate_at_least(alloc, 3);
> +  VERIFY(m == 4);
> +  VERIFY(gn == 44);
> +}
> +void a12()
> +{
> +  struct A { char a[12]; };
> +  std::allocator<A> alloc;
> +  using alloc_traits = std::allocator_traits<std::allocator<A>>;
> +  auto [p, m] = alloc_traits::allocate_at_least(alloc, 3);
> +  VERIFY(m == 4);
> +  VERIFY(gn == 48);
> +}
> +void a13()
> +{
> +  struct A { char a[13]; };
> +  std::allocator<A> alloc;
> +  using alloc_traits = std::allocator_traits<std::allocator<A>>;
> +  auto [p, m] = alloc_traits::allocate_at_least(alloc, 3);
> +  VERIFY(m == 3);
> +  VERIFY(gn == 39);
> +}
> +
> +int main()
> +{
> +  a11();
> +  a12();
> +  a13();
> +}



More information about the Libstdc++ mailing list