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
>