Hi all,

I've opened https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125741 to cover the 
regression for arm-none-eabi.

Kind regards,
Torbjörn

On 2026-06-10 18:15, Jonathan Wakely wrote:


On Wed, 10 Jun 2026, 16:20 Nathan Myers, <[email protected] 
<mailto:[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] 
<mailto:[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 <http://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