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

Reply via email to