https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95349

Richard Biener <rguenth at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |rguenth at gcc dot gnu.org
           Keywords|                            |alias, wrong-code

--- Comment #1 from Richard Biener <rguenth at gcc dot gnu.org> ---
I think std::launder merely acts as optimization barrier here and without we
manage to propagate the constant.  We still "miscompile" things dependent on
what exactly the C++ standard says.  When seeing

std::uint64_t* s3(double* p) {
    unsigned char storage[sizeof(std::uint64_t)];
    std::memcpy(storage, p, sizeof(storage));
    auto t = new(p) std::uint64_t;
    std::memcpy(t, storage, sizeof(storage));
    return t;
}

the placement new has no effect (it's just passing through a pointer) and
memcpy has C semantics, transfering the active dynamic type of 'p'
through 'storage' back to 'p'.

std::uint64_t f3() {
    double d = 3.14159;
    return *s3(&d);
}

... which is still 'double'.  Which you then access via an lvalue of type
uint64_t which invokes undefined behavior.  So in GCCs implementation
reading of relevant standards you need -fno-strict-aliasing and your program
is not conforming.

So what goes on is that GCC optimizes s3 to just { return (uint64t *)p; } which
makes f3 effectively do

      double d = 3.14159;
      return *(uint64_t *)&d;

which arguably is bogus.  Without the std::launder we are nice to the
user and "optimize" the above to return the correct value.  With
std::launder we cannot do this since it breaks the pointer flow and
we'll DSE the initialization of 'd' because it is not used (due to the
undefinedness in case the load would alias it).

Reply via email to