https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127558
Bug ID: 127558
Summary: [loongarch64] wrong code at -O2: a live sign-extension
replaced with a copy
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: ---
Target: loongarch64
Live Reproducer: https://godbolt.org/z/vhf64fr13
A value narrowed to int keeps its upper 32 bits from -O2 up, because the
sign-extension that produces it is deleted while those bits are still live.
$ cat t1.c
#include <stdio.h>
volatile long A;
long B = 0x80000001;
long D = 1;
long m[2];
long e = 1;
unsigned long long t;
int main(void) {
int z = 0;
_BitInt(17) g = A;
for (; A; z = (int)B)
;
t = (int)(D ? B : -g) ^ (_BitInt(7))e;
m[0] = m[(unsigned)B & 1] / B;
int r = 0 == z ? 2 * B : 0;
printf("%llu\n", r * t);
}
A is zero, so the loop does not run and z stays 0, and g is 0. D is 1, so the
ternary gives B, and (int)B must wrap 0x80000001 to -2147483647; xor with 1
gives -2147483648, so t is 2^64-2^31. r is (int)(2*B) == 2, so r*t must be
2 * (2^64 - 2^31) mod 2^64 == 2^64 - 2^32 == 18446744069414584320.
$ loongarch64-unknown-linux-gnu-gcc -O1 -static t1.c -o t1 && qemu-loongarch64
./t1
18446744069414584320 (correct)
$ loongarch64-unknown-linux-gnu-gcc -O2 -static t1.c -o t1 && qemu-loongarch64
./t1
4294967296 (wrong)
4294967296 is 2 * (2147483649), so I guess the (int) never happened.
16.1.0 wrong
trunk (4015b65b) wrong
trunk (20260920) wrong
trunk (20260922) wrong
-O0, -O1 and -Os are correct; -O2 and -O3 are wrong. x86-64 is correct at every
level.