On Thu, 28 May 2026 at 20:53, Nathan Myers <[email protected]> wrote:
>
> Changes in v5:
>  - Rename __ask -> __bytes in __new_allocator<>::allocate_at_least.
>  - Guard __new_allocator<>::allocate_at_least body vs. __cpp_aligned_new.
>  - Make new test pass when __cpp_aligned_new is not defined.
>  - Make new test pass when __cpp_sized_deallocation is not defined
>  - Include stdlib.h in test, to get malloc and free.
>  - Omit unused parameter name in defn of op delete(p, n).
>
> Changes in v4:
>  - Simplify calculation.
>  - Drop __builtin_unreachable(), as compiler seems unable to use it.
>  - Distinguish objects of size 8 or more, where __bonus can be only
>   0 or 1, as compilers do not notice.
>  - Test more thoroughly.
>
> 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. The new version is implemented 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: Add tests.
>         * testsuite/23_containers/vector/modifiers/insert_vs_emplace.cc:
>         Fix for -m32 and new allocation results.

OK, thanks

> ---
>  libstdc++-v3/include/bits/new_allocator.h     | 30 ++++++---
>  .../20_util/allocator/allocate_at_least2.cc   | 66 +++++++++++++++++++
>  .../vector/modifiers/insert_vs_emplace.cc     | 48 +++++++++-----
>  3 files changed, 116 insertions(+), 28 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..e7666b5831c 100644
> --- a/libstdc++-v3/include/bits/new_allocator.h
> +++ b/libstdc++-v3/include/bits/new_allocator.h
> @@ -177,6 +177,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        [[nodiscard]] constexpr std::allocation_result<_Tp*, size_t>
>        allocate_at_least(size_t __n)
>        {
> +#if __cpp_aligned_new
>         if ! consteval
>           {
>             if constexpr (requires { sizeof(_Tp); })
> @@ -184,19 +185,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>             if constexpr ( sizeof(_Tp) <  __STDCPP_DEFAULT_NEW_ALIGNMENT__)
>               {
>                 _S_check_allocation_limit(__n);
> -               const size_t __need = __n * sizeof(_Tp);
> +               size_t __bytes = __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 = (__bytes + __mask) & ~__mask;
> +               // Avoid seeming to ask for 2^63 bytes (PR108377):
> +               __max -= __max >> (__SIZE_WIDTH__ - 1);
> +               auto __spare = static_cast<unsigned>(__max - __bytes);
> +               if constexpr (sizeof(_Tp) < (__mask + 1) / 2)
> +                 {
> +                   auto __bonus = __spare / sizeof(_Tp);
> +                   __n += __bonus;
> +                   __bytes += __bonus * sizeof(_Tp);
> +                 }
> +               else if (sizeof(_Tp) <= __spare)
> +                 {
> +                   __n += 1;
> +                   __bytes += sizeof(_Tp);
> +                 }
> +               void* __p = _GLIBCXX_OPERATOR_NEW(__bytes);
> +               return { static_cast<_Tp*>(__p), __n };
>               }
>           }
> +#endif
>         return { allocate(__n), __n };
>        }
>  #endif
> 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..4f8645ed50d
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least2.cc
> @@ -0,0 +1,66 @@
> +// { dg-do run { target c++23 } }
> +
> +#include <memory>
> +#include <stdlib.h>
> +#include <testsuite_hooks.h>
> +
> +std::size_t gn = 0;
> +void* operator new(std::size_t n)
> +{
> +  gn = n;
> +  return ::malloc(n);
> +}
> +
> +// Failing to define ops delete, too, would generate warnings.
> +
> +void operator delete(void* p) noexcept
> +{ ::free(p); }
> +
> +#if __cpp_sized_deallocation
> +void operator delete(void* p, std::size_t) noexcept
> +{ ::free(p); }
> +#endif
> +
> +template <unsigned size, unsigned n>
> +void test_nm()
> +{
> +  struct A { char a[size]; };
> +  std::allocator<A> alloc;
> +  using alloc_traits = std::allocator_traits<std::allocator<A>>;
> +  auto [p, m] = alloc_traits::allocate_at_least(alloc, n);
> +
> +#if __cpp_aligned_new
> +  unsigned mod = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
> +  unsigned max = ((n * size) + mod - 1) & ~(mod - 1);
> +  unsigned count = max / sizeof(A);
> +#else
> +  unsigned count = n;
> +#endif
> +  VERIFY(m == count);
> +  VERIFY(gn == count * sizeof(A));
> +  VERIFY(p != nullptr);  // named it, use it.
> +}
> +
> +void test()
> +{                  //  m  gn
> +  test_nm<1,3>();  // 16  16
> +  test_nm<2,3>();  //  8  16
> +  test_nm<3,3>();  //  5  15
> +  test_nm<4,3>();  //  4  16
> +  test_nm<5,3>();  //  3  15
> +  test_nm<6,3>();  //  5  30
> +  test_nm<7,3>();  //  4  28
> +  test_nm<8,3>();  //  4  32
> +  test_nm<9,3>();  //  3  27
> +  test_nm<10,3>(); //  3  30
> +  test_nm<11,3>(); //  4  44
> +  test_nm<12,3>(); //  4  48
> +  test_nm<13,3>(); //  3  39
> +  test_nm<14,3>(); //  3  42
> +  test_nm<15,3>(); //  3  45
> +}
> +
> +int main()
> +{
> +  test();
> +}
> 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