https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128463
Bug ID: 128463
Summary: wrong code at -O2: store-merging moves stores below a
call that changes the effective type of malloc memory
since r11-3641
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Keywords: wrong-code
Severity: normal
Priority: P3
Component: tree-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: jeffchen006 at gmail dot com
Target Milestone: ---
Target: x86_64-pc-linux-gnu
int printf(const char *, ...);
void *malloc(unsigned long);
struct Q {
float x;
int y;
};
int sum;
int set(void *p, long long v, int n)
{
int r = 0;
for (int i = 0; i < n; i++)
switch (i % 8) {
case 0: r = r * 3 + 1; break;
case 1: r = r * 5 + 2; break;
case 2: r = r * 7 + 3; break;
case 3: r = r * 9 + 4; break;
case 4: r = r * 11 + 5; break;
case 5: r = r * 13 + 6; break;
case 6: r = r * 15 + 7; break;
case 7: r = r * 17 + 8; break;
}
*(long long *)p = v;
return r;
}
int get(struct Q *q, int n)
{
int r = 0;
for (int i = 0; i < n; i++)
switch (i % 8) {
case 0: r = r * 3 + 1; break;
case 1: r = r * 5 + 2; break;
case 2: r = r * 7 + 3; break;
case 3: r = r * 9 + 4; break;
case 4: r = r * 11 + 5; break;
case 5: r = r * 13 + 6; break;
case 6: r = r * 15 + 7; break;
case 7: r = r * 17 + 8; break;
}
return r + q->x + q->y;
}
int main(int argc, char **argv)
{
int n = argc - 1;
char *b = malloc(32);
for (int k = 1; k < 4; k++) {
struct Q *q = (struct Q *)(b + k * 8);
q->x = 1;
q->y = 2;
sum += q->y;
}
sum += set(b + 16, 5, n);
for (int k = 0; k < 8; k++)
b[8 + k] = b[24 + k];
sum += get((struct Q *)(b + 8), n);
printf("%d %lld\n", sum, *(long long *)(b + 16));
return 0;
}
$ gcc -O0 t.c && ./a.out
9 5
$ gcc -O2 t.c && ./a.out
9 9655287808
-O2, -O3 fail; -O1 is correct.
Expected: 9 5. set() stores a long long at b + 16. The memory has no declared
type, so that store changes the effective type (C11 6.5p6), and the read of
*(long long *)(b + 16) must give 5. The wrong value 9655287808 is 0x23F800000,
which is 1.0f and 2: the two struct stores were done after set().
store-merging combines q->x = 1; q->y = 2; and sinks them below the call,
because the modref summary says set writes only long long and TBAA says that
cannot alias float or int. The large switch loops in set and get are there only
to keep them out of line; they run 0 times. With noinline on both functions,
the loops can be removed.
Avoided by: -fno-ipa-modref, -fno-store-merging, -fno-strict-aliasing,
-fno-tree-fre, -fno-tree-forwprop.
Bisected to r11-3641-gc34db4b6f8a.
Correct on 8.5.0, 10.5.0.
Wrong on 12.5.0, 13.4.0, 14.3.0, 15.2.0, 16.2.0, trunk.
-O0 -fsanitize=undefined,address is clean.
gcc version 17.0.0 20261029 (experimental), r17-5747-g85867b0c935,
x86_64-pc-linux-gnu
Configured with: --enable-languages=c,c++ --disable-multilib
--disable-bootstrap --enable-checking=release