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

Reply via email to