Am 02.10.24 um 15:55 schrieb Georg-Johann Lay:
I am having problems understanding test case gcc.dg/signbit-6.c
which fails on a 16-bit platform (avr).
https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/testsuite/gcc.dg/signbit-6.c;h=da186624cfa057dfc3780c8af4f6b1335ba07e7e;hb=HEAD
The relevant part of the code is:
int main ()
{
TYPE a[N];
TYPE b[N];
a[0] = INT_MIN;
b[0] = INT_MIN;
for (int i = 1; i < N; ++i) ...
fun1 (a, N);
fun2 (b, N);
if (DEBUG)
printf ("%d = 0x%x == 0x%x\n", 0, a[0], b[0]);
if (a[0] != 0x0 || b[0] != -1)
__builtin_abort (); // <-- triggers
where TYPE=int32_t, and the error occurs for a[0] and b[0] so
the test can be compiled with -DN=1 and still fails.
fun1() and fun2() have the same C code but different optimization level,
so how are a[0] and b[0] supposed to be different?
__attribute__ ((noinline, noipa))
void fun1(TYPE *x, int n)
{
for (int i = 0; i < n; i++)
x[i] = (-x[i]) >> 31;
}
__attribute__ ((noinline, noipa, optimize("O0")))
void fun2(TYPE *x, int n)
{
for (int i = 0; i < n; i++)
x[i] = (-x[i]) >> 31;
}
IIUC the compiler may exploit that x and -x will always have different
sign bits because "- INT_MIN" is UB. In fact, on x86_64 the test
passes, here with gcc v13.2:
$ gcc signbit-6.c -O1 -o x.x -DN=1 && ./x.x
0 = 0x0 == 0xffffffff
With -fwrapv so that -INT_MIN is not more UB, it actually fails
due to the bad condition "if (a[0] != 0x0 || b[0] != -1)" :
$ gcc signbit-6.c -O1 -o x.x -DN=1 -fwrapv && ./x.x
0 = 0xffffffff == 0xffffffff
On avr (int=int16_t), the test aborts after printing "0 = 0x0 == 0x0".
So as far as I can see, that test has at least 3 bugs?
1) Missing -fwrapv so that -INT_MIN is no more UB, hence the
test would assert that a[0] == b[0].
2) When testing for a specific value of a[0] and b[0], it depends
on whether int == int32_t or smaller: Better use INT32_MIN to
initialize a[0] and b[0] instead of just INT_MIN.
3) The test case has bogus printf format strings and should
use PRIx32 from inttypes.h instead of %x.
With theses fixes, expected result for a[0] and b[0] is
(-INT32_MIN) >> 31 = INT_MIN >> 31 = INT32_C (-1)
Johann
The test case also passes on avr when a[0] and b[0] are initialized
to INT32_MIN (instead of INT_MIN), without -fwrapv, and fixing %x:
0 = 0x0 == 0xffffffff
But IMO a test case should not rely on any specific behavior of
code that is UB.
FYI, there is also gcc.dg/signbit-4.c that comes with -fwrapv
and doesn't assume a specific result for a[0], b[0] and just
that a[0] == b[0] (also uses INT_MIN for init instead of INT32_MIN).
Johann