Bug 93010 - Wrong optimization: provenance affects comparison of saved bits of addresses of dead auto variables
Summary: Wrong optimization: provenance affects comparison of saved bits of addresses ...
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: tree-optimization (show other bugs)
Version: 10.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2019-12-19 14:58 UTC by Alexander Cherepanov
Modified: 2021-09-13 01:40 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Alexander Cherepanov 2019-12-19 14:58:58 UTC
It's known that the value of a pointer to an object becomes indeterminate after the object is dead (C11, 6.2.4p2). Whether its representation becomes indeterminate is up for debate but let's bypass the issue by saving the representation while the object is still alive. For example, we can cast it to an integer. And we'll get an ordinary integer, with a stable value etc., not affected by changes in the life of the original object. Right?

This seems to be broken for the equality operators when the operands are constructed from addresses of automatic variables and at least one of these variables is dead at the time of comparison.

----------------------------------------------------------------------
#include <stdio.h>

int main()
{
    unsigned long u, v;

    {
        int x[5];
        u = (unsigned long)x;
    }

    {
        int y[5];
        v = (unsigned long)y;
    }

    printf("u = %#lx\n", u);
    printf("v = %#lx\n", v);
    printf("diff = %#lx\n", u - v);
    printf("eq = %d\n", u == v);
}
----------------------------------------------------------------------
$ gcc -std=c11 -pedantic -Wall -Wextra -O3 test.c && ./a.out
u = 0x7ffeb6326180
v = 0x7ffeb6326180
diff = 0
eq = 0
----------------------------------------------------------------------
gcc x86-64 version: gcc (GCC) 10.0.0 20191219 (experimental)

If "diff == 0" then "eq" should be 1.
Comment 1 Alexander Cherepanov 2019-12-19 16:15:34 UTC
clang bug -- https://bugs.llvm.org/show_bug.cgi?id=44342

There is a second example there, with memcpy/memcmp, but it doesn't trigger the bug in gcc so not pasting it here. (Generally gcc seems to be much less consistent than clang in optimizing integers vs. memcpy/memcmp.)
Comment 2 Alexander Cherepanov 2019-12-30 13:12:08 UTC
But gcc seems to be better than clang in arranging compound literals in memory so here is a gcc-only testcase with them:

----------------------------------------------------------------------
#include <stdio.h>

int main()
{
    unsigned long u, v;

    {
        u = (unsigned long)&(int){0};
    }

    {
        v = (unsigned long)&(int){0};
    }

    printf("diff = %lu\n", u - v);
    printf("eq = %d\n", u == v);
}
----------------------------------------------------------------------
$ gcc -std=c11 -pedantic -Wall -Wextra -O3 test.c && ./a.out
diff = 0
eq = 0
----------------------------------------------------------------------
gcc x86-64 version: gcc (GCC) 10.0.0 20191230 (experimental)
----------------------------------------------------------------------
Comment 3 Richard Biener 2020-01-08 14:25:13 UTC
I'm not sure GCCs behavior is non-conforming here.  What happens is that we
compute u == v at compile-time, combining stmts from

  <bb 2> :
  D.2438 = 0;
  u_6 = (long unsigned int) &D.2438;
  D.2438 ={v} {CLOBBER};
  D.2439 = 0;
  v_9 = (long unsigned int) &D.2439;
  D.2439 ={v} {CLOBBER};
  _1 = u_6 - v_9;
  printf ("diff = %lu\n", _1);
  _2 = u_6 == v_9;
  _3 = (int) _2;
  printf ("eq = %d\n", _3);
  _13 = 0;
  return _13;

but we can't compute a definitive value for the difference so that gets
computed at runtime where stack layouting happens to place D.2438 and D.2439
at the same address.

So the inconsistency is that GCC first assumes no such thing as stack slot
sharing happens but then does it anyways.  I guess we'd be allowed to
optimize u - v to an arbitrary constant(?) but then enough trickery can
expose the "real" values again (and the "inconsistency").

I see no good way "fixing" GCC here without sacrifying normal usage for this
arcane case.