https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127926
Bug ID: 127926
Summary: Wrong code with scalar_storage_order after memcpy: FRE
folds a big-endian field load in native byte order
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: kyuwoncho18 at gmail dot com
Target Milestone: ---
Component: tree-optimization
godbolt: https://godbolt.org/z/aTPn78fTs
```c
#include <stdio.h>
struct S
{
unsigned int a;
unsigned int pad[15];
} __attribute__ ((scalar_storage_order ("big-endian")));
static const unsigned char src[64] = { 0x11, 0x22, 0x33, 0x44 };
__attribute__ ((noinline)) static unsigned int
f (struct S *p)
{
__builtin_memcpy (p, src, 64);
return p->a;
}
int
main (void)
{
struct S s;
printf ("%08x\n", f (&s));
return 0;
}
```
GCC trunk on x86_64-linux miscompiles this at -O1 and above:
```console
$ gcc -O0 t.c && ./a.out
11223344
$ gcc -O2 t.c && ./a.out
44332211
$ gcc -O2 -fno-tree-fre t.c && ./a.out
11223344
```
Expected `11223344`: the memcpy stores the bytes `11 22 33 44` at offset 0, and
`a` is a scalar field of a `scalar_storage_order ("big-endian")` struct, so
reading it must give `0x11223344`. Copying bytes in with memcpy and reading
through the declared struct type is the supported use of the attribute.
`fre1` reports `Replaced p_2(D)->a with 1144201745` (0x44332211). In
`vn_reference_lookup_3`, the memcpy case (case 6) rebuilds `vr->operands` as a
plain `MEM_REF` into the source (a zeroed `vn_reference_op_s`, so `reverse` is
lost) and then `fully_constant_vn_reference_p` decodes `src` in target order.
The guard added for PR98502 (r11-1951-gb541b871135c) only fires when the
destination or source is an `ADDR_EXPR` of a reverse-order aggregate, so
`__builtin_memcpy (&s, src, 64); return s.a;` is folded correctly; with the
SSA_NAME destination `p_2(D)` nothing checks
`reverse_storage_order_for_component_p (vr->operands)`, unlike cases 3, 4 and 5
(the case 5 guard was added for PR126153). `__builtin_memmove` and a library
`memcpy` call fail the same way.
GCC 11.1 through trunk (17.0.0 20261009) print 44332211. GCC 10.4 and 10.5
print the correct value: there gimple-fold still turns the memcpy into a
`char[64]` store before FRE and the constant-store case has the PR101925 guard
(9.1 through 10.3 fail through that path; 8.5 is correct).
Disclaimer: This test case was generated by a fuzzer, and the report was
written with LLM assistance.