Hi, >From recent discussions, my understand was that this function can be implemented purely in library as: return launder(static_cast<T*>(memmove(p, p, sizeof(T))));
The memove performs implicit object creation ( https://eel.is/c++draft/c.strings#cstring.syn-3), so it does create (amongst other) objects of type T, in the provided memory region. However, it does not produce a pointer to a suitable create object https://eel.is/c++draft/intro.object#12, and we have all possible objects created. Now, the launder now helps to address both: * The precondition https://eel.is/c++draft/ptr.launder#2, filtres all created objects to one of type similar to T, to make behavior defined * We get proper address to that pointer We Regards, Tomasz On Thu, Aug 28, 2025 at 8:12 PM Jakub Jelinek <[email protected]> wrote: > Hi! > > As I can't think of how the middle-end would treat > __builtin_start_lifetime_as other than a blackbox and probably would > need to be implemented as such inline asm in RTL, this patch > just implements it using inline asm in the library. > If not anything else, it can serve as fallback before we and/or clang > get some builtin for it. > > Right now the inline asms pretend (potential) read from the whole > memory region and make optimizers forget where the return value points to. > If the optimizers don't know where it points to, I think that should be > good enough, but I'm a little bit afraid of possibly future optimizations > trying to optimize > q->c = 1; > q->d = 2; > auto p = std::start_lifetime_as<S>(q); > if (p == reinterpret_cast<decltype (p)>(q)) > return p->a + p->b; > that because of the guarding condition or perhaps assertion we could > simply use the q pointer in MEM_REFs with S type and be surprised by TBAA. > Though if it is a must-alias case, then we should be fine as well. > Though guess that would be the same case with a builtin. > Or shall we also pretend the inline asm modifies (or could modify) the > memory? > > 2025-08-28 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. > * 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-28 19:47:26.101011256 +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,117 @@ _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) : "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); > + __asm__ __volatile__("" : "=g" (__q) : "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); > + __asm__ __volatile__("" : "=g" (__q) : "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); > + __asm__ __volatile__("" : "=g" (__q) : "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) : "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)); > + __asm__ __volatile__("" : "=g" (__q) : "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)); > + __asm__ __volatile__("" : "=g" (__q) : "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)); > + __asm__ __volatile__("" : "=g" (__q) : "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/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 > >
