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.
> 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.
>
> Jakub
>
>