On Fri, 29 Aug 2025, Tomasz Kaminski wrote:

> On Fri, Aug 29, 2025 at 9:03 AM Jakub Jelinek <[email protected]> wrote:
> 
> > On Fri, Aug 29, 2025 at 08:48:02AM +0200, Tomasz Kaminski wrote:
> > > >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))));
> >
> > It can't, we optimize memmove(p,p,whatever) away pretty much instantly,
> 
> so all it ends up with is __builtin_launder (and later comparison against
> > the original pointer with the launder return can get us back to the
> > original
> > pointer).
> >
> memove has the power to implicitly create objects, so the calls to need need
> to force optimizers to also forget about where p was pointing to, in the
> same manner
> as it starts for its lifetime as.

I don't think memmove can do such thing, not portably across C and C++
at least.  It transfers the effective type of storage, so it's really
a no-op TBAA-wise, according to the C standard at least.  

> 
> > And, if we didn't, then it wouldn't work for the const case and would
> > create
> > data races where there shouldn't be one otherwise.
> > Or is
> > struct S { int a[2]; };
> > struct T { long long b; };
> > const struct S s[2] = { { 1, 2 }, { 3, 4 } };
> > long long
> > foo ()
> > {
> >   auto p = std::start_lifetime_as_array<T> (reinterpret_cast<const void *>
> > (&s[0]), 2);
> >   return p[0].b + p[0].c;
> > }
> > instead for some reason?
> >
> I think modifies a const object, at starting the lifetime of a new object,
> reuses storage,
> and then destroys the old object. But, I agree you can construct valid
> cases where it should
> work, like:
> union {
>   const S s;
> } u;
> U* u = start_lifetime_as<const S>(&u.s);
> So yes, memove is not really an option.
> 
> >
> > Or multiple threads doing start_lifetime_as on the same block of memory
> > concurrently?
> >
> I think it is unclear if that is causing a data race, but I agree that it
> would be
> better for it to not create a data race if threads are only reading from
> memory.
> 
> > My understanding is that it is as if it is a store but not really.

For the middle-end it needs to appear to be a store, but if it isn't
really then it may not appear as a true store.  But my hunch is that
people will expect std::start_lifetime_as<T> to _not_ be sth like
a full optimization barrier.

But yeah, if std::start_lifetime_as<T> is a thing for 'const' storage
(huh - how can you start a new object lifetime in something immutable?)
then actual stores are not going to work.  But then you need to hide
the const-ness from the middle-end because aliasing will happily say
it isn't a store then and move loads across causing TBAA issues again.

So, is std::start_lifetime_as<T> a thing on readonly objects?

Richard.

Reply via email to