https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127550
Bug ID: 127550
Summary: [16/17 Regression][loongarch64] wrong code at -O2: AND
with a mask wider than 32 bits on a sign-extended int
is treated as AND with 0xffffffff
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Keywords: wrong-code
Severity: normal
Priority: P3
Component: rtl-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: shimizu2486 at gmail dot com
Target Milestone: ---
Live Reproducer: https://godbolt.org/z/v66ojxP3j
Masking a sign-extended int with a constant wider than 32 bits gives the wrong
value from -O2 up, and the comparison that uses it is folded to a constant so
the branch disappears.
$ cat t1.c
#include <stdio.h>
static volatile int V = 1;
int v, d;
int main(void) {
int *p = V ? &v : &d;
*p = -1;
if ((unsigned)v >= ((unsigned long long)v & 0x1ffffffffULL))
printf("ge\n");
else
printf("lt\n");
}
V is nonzero, so p points at v and the store puts -1 there. (unsigned)v is then
2^32-1 and v & 0x1ffffffff is 2^33-1, so the >= is false and the program must
print lt.
$ loongarch64-unknown-linux-gnu-gcc -O1 -static t1.c -o t1 && qemu-loongarch64
./t1
lt (correct)
$ loongarch64-unknown-linux-gnu-gcc -O2 -static t1.c -o t1 && qemu-loongarch64
./t1
ge (wrong)
-O1 computes both operands and branches:
bstrpick.d $r13,$r12,31,0 # (unsigned)v
bstrpick.d $r12,$r12,32,0 # v & 0x1ffffffff
bltu $r13,$r12,.L3
-O2 emits neither the compare nor the branch: .LC0 ("ge") is loaded
unconditionally and the "lt" block is gone.
Any mask wider than 32 bits shows it, and the width of the extra bits does not
matter:
So the AND behaves as if the mask were 0xffffffff, i.e. the bits of the sign
extension above bit 31 are dropped.
12.2.0 .. 15.2.0 correct
16.1.0 wrong
trunk (02535f61) wrong
-O0 and -O1 are correct; -O2, -O3 and -Os are wrong. riscv64, x86-64 and
aarch64 trunk are all correct on this file, so it looks loongarch64-only.