On 5/27/26 11:14 AM, Tomasz Kaminski wrote:


On Wed, May 27, 2026 at 11:02 AM Nathan Myers <[email protected] <mailto:[email protected]>> 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


Reply via email to