On Fri, Aug 29, 2025 at 9:40 AM Richard Biener <rguent...@suse.de> wrote:
> On Fri, 29 Aug 2025, Tomasz Kaminski wrote: > > > On Fri, Aug 29, 2025 at 9:03 AM Jakub Jelinek <ja...@redhat.com> 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. > It is required to implicitly create objects per C++ standard, same as start_lifetime_as do so if it is not in C++ mode, that would mean non-conformance: https://eel.is/c++draft/c.strings#cstring.syn-3 But maybe we are speaking about something else. As far I understand the implicit object creation only relax strict aliasing rules, so you interpret char buffer as int, if you memmoved/ memcopied an int representation to it before. > > > > > > 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.