https://gcc.gnu.org/g:14617bda37f8c7e2da8efa335b47b41332a559eb
commit r17-987-g14617bda37f8c7e2da8efa335b47b41332a559eb Author: Nathan Myers <[email protected]> Date: Tue May 26 10:41:27 2026 -0400 libstdc++: allocate_at_least ask only what it reports (P0401) 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 emits code for multiplication, instead. In addition, the remaining -m32 test that failed under the new allocation method is fixed, and guards are added for building with -fno-aligned-new and for -fno-sized-deallocation. 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. Diff: --- 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(-) diff --git a/libstdc++-v3/include/bits/new_allocator.h b/libstdc++-v3/include/bits/new_allocator.h index 4524355a4a0f..e7666b5831c1 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 000000000000..4f8645ed50d5 --- /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 187e433d9d37..d226490d6f3b 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));
