On Thu, 28 Aug 2025, Iain Sandoe wrote: > > > > On 28 Aug 2025, at 16:38, Jakub Jelinek <ja...@redhat.com> wrote: > > > > On Thu, Aug 28, 2025 at 04:50:15PM +0200, Jakub Jelinek wrote: > >> On Thu, Aug 28, 2025 at 04:24:46PM +0200, Richard Biener wrote: > >>> That said, if we ever want to improve on this (and solve the redundant > >>> store elimination issue), we need an actual GIMPLE/RTL statement > >>> doing what std::start_lifetime_as <T> does (but not generate code). > >>> > >>> Meaning, I'd vote for a p = __builtin_start_lifetime_as (p, (T *)0); > >> > >> I thought earlier about just providing size. > >> null pointer with the new type would provide even more information, > >> iff we only support constant size types. > >> And we can then handle it in GIMPLE as if it is (or could be) > >> void *__builtin_start_lifetime_as (void *p) > >> { > >> union { old_type old; T new; } u; > >> __builtin_memcpy (u.old, p, sizeof (T)); > >> __builtin_memcpy (p, u.new, sizeof (T)); > >> return p; > >> } > >> (but of course for the const case or even for data races we actually > >> don't want either of those copies). > >> Now, I bet any kind of user function could do something like that if > >> it isn't inlined/exposed to the compiler, so not sure if we need to > >> handle it in too many places through GIMPLE, perhaps say that it > >> does or could access directly just the [p, p + sz) bytes (and indirectly > >> anything reachable). > >> But how to lower it into RTL is unclear, having it as CALL_EXPR will > >> mean it has to follow the call ABIs and all extra overhead. > >> UNSPEC_VOLATILE might be nice, but I guess we don't have a precedent > >> of backend independent constants for those. > >> Or expand it (just for RTL?) as inline asm, > >> asm volatile ("" : "=g" (ptr), "m" (*(T *)ptr) : "0" (ptr), "m" (*(const > >> T *)ptr)); > >> ? > > > > Or implement it using inline asm in the headers at least for now, until > > we find a reason to add a builtin for that. > > I believe for inline asm we really don't care about the alias set of "m" > > arguments, the accesses are or can be done in any way the asm likes to.
Note since the semantics are that of a store I'd lower it to one. A store is likely also the least disrupting implementation and allows for natural elision where possible (short of the few existing bugs around redundant store removal). I suppose variable sized types would not work with (T *)0 as we fail to gimplify them in such context. The alternative would have been sth like *p = __builtin_start_lifetime_as (p); but I'm not sure we want to nail this down as an actual store. So maybe have p = __builtin_start_lifetime_as (p, sizeof (T), (T *)0); I understand the use is for inside of the standard library and possibly C / GIMPLE testcases. The above should be lowered then, during gimplification or gimple lowering, to MEM<T>[p, (T *)0] = MEM<T>[p, (void *)0]; aka store with T, load with alias-set zero. I'd expect that for double foo (long x) { return *std::start_lifetime_as<double> (&x); } the extra store would be elided. That would be difficult if we chose asm(). > Is the std::observable() ( for which I have an implementation to be posted > that > is implemented with a builtin lowered to nothing expand() ) possibly > useful also in this context? Not sure, what's that exactly? Richard.