On Wed, 27 May 2026 at 10:02, Nathan Myers <[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, 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>

We don't want every container (and many other headers) to include all
of <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();

Just use __builtin_unreachable() instead of std::unreachable().

(I'm still reviewing the rest)

> +               const size_t __bonus = __spare / sizeof(_Tp);
> +               const size_t __left =  __spare % sizeof(_Tp);
> +               const size_t __ask = __max - __left;
> +               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