[PATCH v5] libstdc++: allocate_at_least ask only what it reports (P0401)
Torbjorn SVENSSON
torbjorn.svensson@foss.st.com
Wed Jun 10 13:03:03 GMT 2026
Hello,
It looks like this change is introducing a regression for arm-none-eabi:
Testing tree-ssa/pr109442.C, -std=gnu++11
doing compile
Executing on host: /build/r17-1372-g80b78b2504fba0/bin/arm-none-eabi-g++ /build/gcc_src/gcc/testsuite/g++.dg/tree-ssa/pr109442.C -mthumb -march=armv6s-m -mtune=cortex-m0 -mfloat-abi=soft -mfpu=auto -fdiagnostics-plain-output -fmessage-length=0 -std=gnu++11 -O2 -fdump-tree-optimized -S -o pr109442.s (timeout = 800)
spawn -ignore SIGHUP /build/r17-1372-g80b78b2504fba0/bin/arm-none-eabi-g++ /build/gcc_src/gcc/testsuite/g++.dg/tree-ssa/pr109442.C -mthumb -march=armv6s-m -mtune=cortex-m0 -mfloat-abi=soft -mfpu=auto -fdiagnostics-plain-output -fmessage-length=0 -std=gnu++11 -O2 -fdump-tree-optimized -S -o pr109442.s
pid is 430895 -430895
pid is -1
output is status 0
PASS: g++.dg/tree-ssa/pr109442.C -std=gnu++11 (test for excess errors)
PASS: g++.dg/tree-ssa/pr109442.C -std=gnu++11 scan-tree-dump-not optimized "_M_start"
PASS: g++.dg/tree-ssa/pr109442.C -std=gnu++11 scan-tree-dump-not optimized "delete"
Testing tree-ssa/pr109442.C, -std=gnu++23
doing compile
Executing on host: /build/r17-1372-g80b78b2504fba0/bin/arm-none-eabi-g++ /build/gcc_src/gcc/testsuite/g++.dg/tree-ssa/pr109442.C -mthumb -march=armv6s-m -mtune=cortex-m0 -mfloat-abi=soft -mfpu=auto -fdiagnostics-plain-output -fmessage-length=0 -std=gnu++23 -O2 -fdump-tree-optimized -S -o pr109442.s (timeout = 800)
spawn -ignore SIGHUP /build/r17-1372-g80b78b2504fba0/bin/arm-none-eabi-g++ /build/gcc_src/gcc/testsuite/g++.dg/tree-ssa/pr109442.C -mthumb -march=armv6s-m -mtune=cortex-m0 -mfloat-abi=soft -mfpu=auto -fdiagnostics-plain-output -fmessage-length=0 -std=gnu++23 -O2 -fdump-tree-optimized -S -o pr109442.s
pid is 431059 -431059
pid is -1
output is status 0
PASS: g++.dg/tree-ssa/pr109442.C -std=gnu++23 (test for excess errors)
FAIL: g++.dg/tree-ssa/pr109442.C -std=gnu++23 scan-tree-dump-not optimized "_M_start"
FAIL: g++.dg/tree-ssa/pr109442.C -std=gnu++23 scan-tree-dump-not optimized "delete"
Testing tree-ssa/pr109442.C, -std=gnu++20
doing compile
Executing on host: /build/r17-1372-g80b78b2504fba0/bin/arm-none-eabi-g++ /build/gcc_src/gcc/testsuite/g++.dg/tree-ssa/pr109442.C -mthumb -march=armv6s-m -mtune=cortex-m0 -mfloat-abi=soft -mfpu=auto -fdiagnostics-plain-output -fmessage-length=0 -std=gnu++20 -O2 -fdump-tree-optimized -S -o pr109442.s (timeout = 800)
spawn -ignore SIGHUP /build/r17-1372-g80b78b2504fba0/bin/arm-none-eabi-g++ /build/gcc_src/gcc/testsuite/g++.dg/tree-ssa/pr109442.C -mthumb -march=armv6s-m -mtune=cortex-m0 -mfloat-abi=soft -mfpu=auto -fdiagnostics-plain-output -fmessage-length=0 -std=gnu++20 -O2 -fdump-tree-optimized -S -o pr109442.s
pid is 431306 -431306
pid is -1
output is status 0
PASS: g++.dg/tree-ssa/pr109442.C -std=gnu++20 (test for excess errors)
PASS: g++.dg/tree-ssa/pr109442.C -std=gnu++20 scan-tree-dump-not optimized "_M_start"
PASS: g++.dg/tree-ssa/pr109442.C -std=gnu++20 scan-tree-dump-not optimized "delete"
std_list: 20 98 26
With -std=gnu+20, it produces code like:
.type _Z4vat1St6vectorIiSaIiEE, %function
_Z4vat1St6vectorIiSaIiEE:
.fnstart
.LFB1718:
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 0, uses_anonymous_args = 0
@ link register save eliminated.
movs r0, #10
@ sp needed
bx lr
.cantunwind
.fnend
.size _Z4vat1St6vectorIiSaIiEE, .-_Z4vat1St6vectorIiSaIiEE
With -std=gnu+23, it instead produces code like:
.type _Z4vat1St6vectorIiSaIiEE, %function
_Z4vat1St6vectorIiSaIiEE:
.fnstart
.LFB1898:
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 0, uses_anonymous_args = 0
ldmia r0, {r0, r3}
subs r0, r3, r0
push {r4, r5, r6, lr}
.save {r4, r5, r6, lr}
asrs r5, r0, #2
beq .L2
movs r2, #7
adds r3, r0, #7
bics r3, r2
asrs r2, r3, #31
subs r3, r3, r0
adds r3, r2, r3
movs r4, r0
cmp r3, #3
bls .L10
adds r0, r0, #4
bl _Znwj
ldr r3, .L11
adds r5, r5, #1
cmp r5, r3
bls .L5
movs r5, r3
.L5:
lsls r4, r5, #2
movs r1, r4
bl _ZdlPvj
.L2:
@ sp needed
movs r0, #10
pop {r4, r5, r6, pc}
.L10:
bl _Znwj
movs r1, r4
bl _ZdlPvj
b .L2
.L12:
.align 2
.L11:
.word 536870911
.fnend
.size _Z4vat1St6vectorIiSaIiEE, .-_Z4vat1St6vectorIiSaIiEE
Kind regards,
Torbjörn
On 2026-05-29 09:04, Jonathan Wakely wrote:
> On Thu, 28 May 2026 at 20:53, Nathan Myers <ncm@cantrip.org> 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
>>
>
More information about the Libstdc++
mailing list