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.

Reply via email to