On Fri, 29 Aug 2025, Richard Biener wrote:
> On Fri, 29 Aug 2025, Jakub Jelinek wrote:
>
> > Hi!
> >
> > Here is a variant of the patch which pretends there are also stores
> > in the inline asms.
> > Plus the forgotten std.cc exports.
>
> Can you add a testcase on actual constant objects? I wonder why
> we do not diagnose asm outputs on such?
In particular I wonder about
void foo(int n)
{
const short a[8] = { 1,2,3,4,5,6,7,8 };
int sum = 0;
for (int i = 0; i < n; ++i)
{
const int *p = std::start_lifetime_as<int> (a);
sum += ((int *)a)[2];
}
return sum;
}
I'd expect RTL LIM to hoist the ((int *)a)[2] load across the asm()
as 'a' is readonly. GIMPLE alias analysis of course treats all
asm()s with a VDEF as black-boxes.
The hoisting would be a TBAA violation, possibly wrong-code if 'a'
is put on the stack.
Richard.
> > 2025-08-29 Jakub Jelinek <[email protected]>
> >
> > * include/bits/version.def: Implement C++23 P2590R2 - Explicit
> > lifetime management.
> > (start_lifetime_as): New.
> > * include/bits/version.h: Regenerate.
> > * include/std/memory (std::start_lifetime_as,
> > std::start_lifetime_as_array): New function templates.
> > * src/c++23/std.cc.in (std::start_lifetime_as,
> > std::start_lifetime_as_array): Export.
> > * testsuite/std/memory/start_lifetime_as/start_lifetime_as.cc: New test.
> >
> > --- libstdc++-v3/include/bits/version.def.jj 2025-08-28
> > 17:06:44.190031458 +0200
> > +++ libstdc++-v3/include/bits/version.def 2025-08-28 18:44:00.947678928
> > +0200
> > @@ -2100,6 +2100,14 @@ ftms = {
> > };
> > };
> >
> > +ftms = {
> > + name = start_lifetime_as;
> > + values = {
> > + v = 202207;
> > + cxxmin = 23;
> > + };
> > +};
> > +
> > // Standard test specifications.
> > stds[97] = ">= 199711L";
> > stds[03] = ">= 199711L";
> > --- libstdc++-v3/include/bits/version.h.jj 2025-08-28 17:06:49.160883915
> > +0200
> > +++ libstdc++-v3/include/bits/version.h 2025-08-28 18:44:08.431550790
> > +0200
> > @@ -2353,4 +2353,14 @@
> > #endif /* !defined(__cpp_lib_is_implicit_lifetime) &&
> > defined(__glibcxx_want_is_implicit_lifetime) */
> > #undef __glibcxx_want_is_implicit_lifetime
> >
> > +#if !defined(__cpp_lib_start_lifetime_as)
> > +# if (__cplusplus >= 202100L)
> > +# define __glibcxx_start_lifetime_as 202207L
> > +# if defined(__glibcxx_want_all) ||
> > defined(__glibcxx_want_start_lifetime_as)
> > +# define __cpp_lib_start_lifetime_as 202207L
> > +# endif
> > +# endif
> > +#endif /* !defined(__cpp_lib_start_lifetime_as) &&
> > defined(__glibcxx_want_start_lifetime_as) */
> > +#undef __glibcxx_want_start_lifetime_as
> > +
> > #undef __glibcxx_want_all
> > --- libstdc++-v3/include/std/memory.jj 2025-08-23 15:00:05.208775420
> > +0200
> > +++ libstdc++-v3/include/std/memory 2025-08-29 09:21:59.402103595 +0200
> > @@ -120,6 +120,7 @@
> > #define __glibcxx_want_shared_ptr_arrays
> > #define __glibcxx_want_shared_ptr_weak_type
> > #define __glibcxx_want_smart_ptr_for_overwrite
> > +#define __glibcxx_want_start_lifetime_as
> > #define __glibcxx_want_to_address
> > #define __glibcxx_want_transparent_operators
> > #define __glibcxx_want_smart_ptr_owner_equality
> > @@ -172,6 +173,134 @@ _GLIBCXX_END_NAMESPACE_VERSION
> > } // namespace
> > #endif // C++11 to C++20
> >
> > +#if __cpp_lib_start_lifetime_as >= 202207L // C++ >= 23
> > +namespace std _GLIBCXX_VISIBILITY(default)
> > +{
> > +_GLIBCXX_BEGIN_NAMESPACE_VERSION
> > + template<typename _Tp>
> > + [[__gnu__::__always_inline__]]
> > + inline _Tp*
> > + start_lifetime_as(void* __p) noexcept
> > + {
> > +#if __cpp_lib_is_implicit_lifetime >= 202302L
> > + static_assert(is_implicit_lifetime_v<_Tp>);
> > +#endif
> > + auto __q = reinterpret_cast<_Tp*>(__p);
> > + __asm__ __volatile__("" : "=g" (__q), "=m" (*__q)
> > + : "0" (__q), "m" (*__q));
> > + return __q;
> > + }
> > +
> > + template<typename _Tp>
> > + [[__gnu__::__always_inline__]]
> > + inline const _Tp*
> > + start_lifetime_as(const void* __p) noexcept
> > + {
> > +#if __cpp_lib_is_implicit_lifetime >= 202302L
> > + static_assert(is_implicit_lifetime_v<_Tp>);
> > +#endif
> > + auto __q = reinterpret_cast<const _Tp*>(__p);
> > + auto __r = reinterpret_cast<_Tp*>(const_cast<void*>(__p));
> > + __asm__ __volatile__("" : "=g" (__q), "=m" (*__r)
> > + : "0" (__q), "m" (*__q));
> > + return __q;
> > + }
> > +
> > + template<typename _Tp>
> > + [[__gnu__::__always_inline__]]
> > + inline volatile _Tp*
> > + start_lifetime_as(volatile void* __p) noexcept
> > + {
> > +#if __cpp_lib_is_implicit_lifetime >= 202302L
> > + static_assert(is_implicit_lifetime_v<_Tp>);
> > +#endif
> > + auto __q = reinterpret_cast<volatile _Tp*>(__p);
> > + auto __r = reinterpret_cast<_Tp*>(const_cast<void*>(__p));
> > + __asm__ __volatile__("" : "=g" (__q), "=m" (*__r)
> > + : "0" (__q), "m" (*__q));
> > + return __q;
> > + }
> > +
> > + template<typename _Tp>
> > + [[__gnu__::__always_inline__]]
> > + inline const volatile _Tp*
> > + start_lifetime_as(const volatile void* __p) noexcept
> > + {
> > +#if __cpp_lib_is_implicit_lifetime >= 202302L
> > + static_assert(is_implicit_lifetime_v<_Tp>);
> > +#endif
> > + auto __q = reinterpret_cast<const volatile _Tp*>(__p);
> > + auto __r = reinterpret_cast<_Tp*>(const_cast<void*>(__p));
> > + __asm__ __volatile__("" : "=g" (__q), "=m" (*__r)
> > + : "0" (__q), "m" (*__q));
> > + return __q;
> > + }
> > +
> > + template<typename _Tp>
> > + [[__gnu__::__always_inline__]]
> > + inline _Tp*
> > + start_lifetime_as_array(void* __p, size_t __n) noexcept
> > + {
> > + auto __q = reinterpret_cast<_Tp*>(__p);
> > + if (!__n)
> > + return __q;
> > + auto __r = (__extension__ reinterpret_cast<_Tp(*)[__n]>(__p));
> > + __asm__ __volatile__("" : "=g" (__q), "=m" (*__r)
> > + : "0" (__q), "m" (*__r));
> > + return __q;
> > + }
> > +
> > + template<typename _Tp>
> > + [[__gnu__::__always_inline__]]
> > + inline const _Tp*
> > + start_lifetime_as_array(const void* __p, size_t __n) noexcept
> > + {
> > + auto __q = reinterpret_cast<const _Tp*>(__p);
> > + if (!__n)
> > + return __q;
> > + auto __r = (__extension__ reinterpret_cast<const _Tp(*)[__n]>(__p));
> > + auto __s = (__extension__
> > + reinterpret_cast<_Tp(*)[__n]>(const_cast<void*>(__p)));
> > + __asm__ __volatile__("" : "=g" (__q), "=m" (*__s)
> > + : "0" (__q), "m" (*__r));
> > + return __q;
> > + }
> > +
> > + template<typename _Tp>
> > + [[__gnu__::__always_inline__]]
> > + inline volatile _Tp*
> > + start_lifetime_as_array(volatile void* __p, size_t __n) noexcept
> > + {
> > + auto __q = reinterpret_cast<volatile _Tp*>(__p);
> > + if (!__n)
> > + return __q;
> > + auto __r = (__extension__ reinterpret_cast<volatile
> > _Tp(*)[__n]>(__p));
> > + auto __s = (__extension__
> > + reinterpret_cast<_Tp(*)[__n]>(const_cast<void*>(__p)));
> > + __asm__ __volatile__("" : "=g" (__q), "=m" (*__s)
> > + : "0" (__q), "m" (*__r));
> > + return __q;
> > + }
> > +
> > + template<typename _Tp>
> > + [[__gnu__::__always_inline__]]
> > + inline const volatile _Tp*
> > + start_lifetime_as_array(const volatile void* __p, size_t __n) noexcept
> > + {
> > + auto __q = reinterpret_cast<const volatile _Tp*>(__p);
> > + if (!__n)
> > + return __q;
> > + auto __r = (__extension__ reinterpret_cast<const volatile
> > _Tp(*)[__n]>(__p));
> > + auto __s = (__extension__
> > + reinterpret_cast<_Tp(*)[__n]>(const_cast<void*>(__p)));
> > + __asm__ __volatile__("" : "=g" (__q), "=m" (*__s)
> > + : "0" (__q), "m" (*__r));
> > + return __q;
> > + }
> > +_GLIBCXX_END_NAMESPACE_VERSION
> > +} // namespace
> > +#endif
> > +
> > #ifdef __cpp_lib_parallel_algorithm // C++ >= 17 && HOSTED
> > // Parallel STL algorithms
> > # if _PSTL_EXECUTION_POLICIES_DEFINED
> > --- libstdc++-v3/src/c++23/std.cc.in.jj 2025-08-28 17:10:43.784942152
> > +0200
> > +++ libstdc++-v3/src/c++23/std.cc.in 2025-08-29 09:55:46.164636222
> > +0200
> > @@ -2004,6 +2004,10 @@ export namespace std
> > using std::owner_equal;
> > using std::owner_hash;
> > #endif
> > +#if __cpp_lib_start_lifetime_as
> > + using std::start_lifetime_as;
> > + using std::start_lifetime_as_array;
> > +#endif
> > }
> >
> > // 20.4 <memory_resource>
> > ---
> > libstdc++-v3/testsuite/std/memory/start_lifetime_as/start_lifetime_as.cc.jj
> > 2025-08-28 19:29:12.865001523 +0200
> > +++
> > libstdc++-v3/testsuite/std/memory/start_lifetime_as/start_lifetime_as.cc
> > 2025-08-28 19:56:44.975864728 +0200
> > @@ -0,0 +1,68 @@
> > +// { dg-do run { target c++23 } }
> > +
> > +#include <bit>
> > +#include <memory>
> > +
> > +#include <testsuite_hooks.h>
> > +#include <testsuite_allocator.h>
> > +
> > +struct S { int a; int b; };
> > +struct T { long long c; };
> > +
> > +void
> > +test01()
> > +{
> > + if (sizeof(S) != sizeof(T))
> > + return;
> > +
> > + union U { unsigned char a[sizeof(S)]; S b; T c; } u;
> > + u.a[0] = 1;
> > + T v = std::bit_cast<T> (S{1, 2});
> > + union V { unsigned char a[3 * sizeof(S)]; S b[3]; T c[3]; } w;
> > + T x = std::bit_cast<T> (S{3, 4});
> > + T y = std::bit_cast<T> (S{5, 6});
> > + S* d = std::start_lifetime_as<S>(reinterpret_cast<void*>(&u.a));
> > + d->a = 1;
> > + d->b = 2;
> > + T* e = std::start_lifetime_as<T>(reinterpret_cast<void*>(d));
> > + VERIFY( e->c == v.c );
> > + const T* f = std::start_lifetime_as<T>(reinterpret_cast<const void*>(d));
> > + VERIFY( f->c == v.c );
> > + volatile T* g = std::start_lifetime_as<T>(reinterpret_cast<volatile
> > void*>(d));
> > + VERIFY( g->c == v.c );
> > + const volatile T* h = std::start_lifetime_as<T>(reinterpret_cast<const
> > volatile void*>(d));
> > + VERIFY( h->c == v.c );
> > + S* i = std::start_lifetime_as_array<S>(reinterpret_cast<void*>(&w.a), 3);
> > + i[0].a = 1;
> > + i[0].b = 2;
> > + i[1].a = 3;
> > + i[1].b = 4;
> > + i[2].a = 5;
> > + i[2].b = 6;
> > + T* j = std::start_lifetime_as_array<T>(reinterpret_cast<void*>(i), 3);
> > + VERIFY( j[0].c == v.c && j[1].c == x.c && j[2].c == y.c );
> > + const T* k = std::start_lifetime_as_array<T>(reinterpret_cast<const
> > void*>(i), 3);
> > + VERIFY( k[0].c == v.c && k[1].c == x.c && k[2].c == y.c );
> > + volatile T* l =
> > std::start_lifetime_as_array<T>(reinterpret_cast<volatile void*>(i), 3);
> > + VERIFY( l[0].c == v.c && l[1].c == x.c && l[2].c == y.c );
> > + const volatile T* m =
> > std::start_lifetime_as_array<T>(reinterpret_cast<const volatile void*>(i),
> > 3);
> > + VERIFY( m[0].c == v.c && m[1].c == x.c && m[2].c == y.c );
> > + T* n = std::start_lifetime_as_array<T>(static_cast<void*>(nullptr), 0);
> > + VERIFY( n == nullptr );
> > + const T* o = std::start_lifetime_as_array<T>(static_cast<const
> > void*>(nullptr), 0);
> > + VERIFY( o == nullptr );
> > + volatile T* p = std::start_lifetime_as_array<T>(static_cast<volatile
> > void*>(nullptr), 0);
> > + VERIFY( p == nullptr );
> > + const volatile T* q = std::start_lifetime_as_array<T>(static_cast<const
> > volatile void*>(nullptr), 0);
> > + VERIFY( q == nullptr );
> > + VERIFY( std::start_lifetime_as_array<T>(reinterpret_cast<void*>(&w.a),
> > 0) == &w.c[0] );
> > + VERIFY( std::start_lifetime_as_array<T>(reinterpret_cast<const
> > void*>(&w.a), 0) == static_cast<const T*>(&w.c[0]) );
> > + VERIFY( std::start_lifetime_as_array<T>(reinterpret_cast<volatile
> > void*>(&w.a), 0) == static_cast<volatile T*>(&w.c[0]) );
> > + VERIFY( std::start_lifetime_as_array<T>(reinterpret_cast<const volatile
> > void*>(&w.a), 0) == static_cast<const volatile T*>(&w.c[0]) );
> > +}
> > +
> > +int
> > +main()
> > +{
> > + test01();
> > +}
> >
> > Jakub
> >
> >
>
>
--
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)