On Wed, 10 Jun 2026, 16:20 Nathan Myers, <[email protected]> wrote:
>
> I could see it identifying v with v1, but why does the backing
> storage (whatever ends up owning it) not need to be deallocated?
> Who will do it instead?
>
There is no backing store. The point of the test is to verify that the
unused copy of v is completely optimised away.
> And why would this vary by target?
>
> Nathan Myers
>
> On 6/10/26 9:03 AM, Torbjorn SVENSSON wrote:
> > 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 <[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
> >>>
> >>
> >
>
>