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

Nathan Myers ncm@cantrip.org
Wed May 27 17:41:53 GMT 2026


On 5/27/26 11:14 AM, Tomasz Kaminski wrote:
> 
> 
> On Wed, May 27, 2026 at 11:02 AM Nathan Myers <ncm@cantrip.org 
> <mailto:ncm@cantrip.org>> wrote:
> 
>     Changes in v3:
>       - Calculate extra allocations with no attempt to force 8-bit
>        division, which pessimized everything except x86.
>       - Add test to verify allocate_at_least change.
>       - Also fix vector insert_vs_emplace.cc as run built with -m32.
> 
>     (Please ignore v2.)
> 
>     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 algorithm to compute the allocation is altered in response
>     to findings on godbolt.org <http://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 remaining -m32 test that failed under the new
>     allocation method is fixed.
> 
>     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.
>              * testsuite/23_containers/vector/modifiers/
>     insert_vs_emplace.cc:
>              Fix for -m32 and new allocation results.
>     ---
>       libstdc++-v3/include/bits/new_allocator.h     | 20 +++----
>       .../20_util/allocator/allocate_at_least2.cc   | 52 +++++++++++++++++++
>       .../vector/modifiers/insert_vs_emplace.cc     | 48 ++++++++++-------
>       3 files changed, 93 insertions(+), 27 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..5b199a42c56 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 };
>     +               size_t __max = (__need + __mask) & ~__mask;
>     +               // Avoid seeming to ask for 2^63 bytes (PR108377):
>     +               __max -= __max >> (__SIZE_WIDTH__ - 1);
>     +               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;
> 
> The modulus could be replaced with multilication of (__n + __bonus) * 
> sizeof(_Tp).

GCC emits code for (__spare - (__bonus * sizeof(_Tp))) either way.

But I think there is merit in Jonathan's suggestion to distinguish
the case where sizeof(_Tp) is 8 or more, as compilers seem not
to understand that __bonus can be only 0 or 1.

>     +               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();
>     +}
>     diff --git a/libstdc++-v3/testsuite/23_containers/vector/modifiers/
>     insert_vs_emplace.cc b/libstdc++-v3/testsuite/23_containers/vector/
>     modifiers/insert_vs_emplace.cc
>     index 187e433d9d3..d226490d6f3 100644
>     --- a/libstdc++-v3/testsuite/23_containers/vector/modifiers/
>     insert_vs_emplace.cc
>     +++ b/libstdc++-v3/testsuite/23_containers/vector/modifiers/
>     insert_vs_emplace.cc
>     @@ -223,11 +223,12 @@ test03()
>       void
>       test04()
>       {
>     -  const X::special expected{ 0, 3, 1, 0, 3, 0 };
>     +  const X::special expected{ 0, 4, 1, 0, 4, 0 };
>         X::special ins, emp;
>         {
>           std::vector<X> v;
>     -    v.reserve(3);
>     +    v.reserve(4);
>     +    v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>     @@ -241,7 +242,8 @@ test04()
>         }
>         {
>           std::vector<X> v;
>     -    v.reserve(3);
>     +    v.reserve(4);
>     +    v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>     @@ -261,11 +263,12 @@ test04()
>       void
>       test05()
>       {
>     -  const X::special expected{ 0, 3, 0, 0, 4, 0 };
>     +  const X::special expected{ 0, 4, 0, 0, 5, 0 };
>         X::special ins, emp;
>         {
>           std::vector<X> v;
>     -    v.reserve(3);
>     +    v.reserve(4);
>     +    v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>     @@ -279,7 +282,8 @@ test05()
>         }
>         {
>           std::vector<X> v;
>     -    v.reserve(3);
>     +    v.reserve(4);
>     +    v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>     @@ -299,11 +303,12 @@ test05()
>       void
>       test06()
>       {
>     -  const X::special expected{ 1, 4, 0, 0, 4, 0 };
>     +  const X::special expected{ 1, 5, 0, 0, 5, 0 };
>         X::special ins, emp;
>         {
>           std::vector<X> v;
>     -    v.reserve(3);
>     +    v.reserve(4);
>     +    v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>     @@ -316,7 +321,8 @@ test06()
>         }
>         {
>           std::vector<X> v;
>     -    v.reserve(3);
>     +    v.reserve(4);
>     +    v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>     @@ -447,11 +453,12 @@ test09()
>       void
>       test10()
>       {
>     -  const X::special expected{ 0, 3, 1, 0, 3, 0 };
>     +  const X::special expected{ 0, 4, 1, 0, 4, 0 };
>         X::special ins, emp;
>         {
>           std::vector<X> v;
>     -    v.reserve(3);
>     +    v.reserve(4);
>     +    v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>     @@ -465,7 +472,8 @@ test10()
>         }
>         {
>           std::vector<X> v;
>     -    v.reserve(3);
>     +    v.reserve(4);
>     +    v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>     @@ -485,11 +493,12 @@ test10()
>       void
>       test11()
>       {
>     -  const X::special expected{ 0, 3, 0, 0, 4, 0 };
>     +  const X::special expected{ 0, 4, 0, 0, 5, 0 };
>         X::special ins, emp;
>         {
>           std::vector<X> v;
>     -    v.reserve(3);
>     +    v.reserve(4);
>     +    v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>     @@ -503,7 +512,8 @@ test11()
>         }
>         {
>           std::vector<X> v;
>     -    v.reserve(3);
>     +    v.reserve(4);
>     +    v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>     @@ -523,11 +533,12 @@ test11()
>       void
>       test12()
>       {
>     -  const X::special expected{ 1, 4, 0, 0, 4, 0 };
>     +  const X::special expected{ 1, 5, 0, 0, 5, 0 };
>         X::special ins, emp;
>         {
>           std::vector<X> v;
>     -    v.reserve(3);
>     +    v.reserve(4);
>     +    v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>     @@ -540,7 +551,8 @@ test12()
>         }
>         {
>           std::vector<X> v;
>     -    v.reserve(3);
>     +    v.reserve(4);
>     +    v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>           v.push_back(X(0,0));
>     -- 
>     2.54.0
> 



More information about the Libstdc++ mailing list