lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


>>>>> "Philippe" == Philippe Verdy <verdy_p@wanadoo.fr> writes:

 >> fprintf(stderr,"n=%lli x=%llx
 >> <≤=≥>:%i%i%i%i%i\n",n,n,n<0,n<=0,n==0,n>=0,n>0);
 >> if (n<0) n=-n;
 >> fprintf(stderr,"n=%lli x=%llx
 >> <≤=≥>:%i%i%i%i%i\n",n,n,n<0,n<=0,n==0,n>=0,n>0);
 >> 
 >> produces (GNU compiler):
 >> 
 >> n=-9223372036854775808 x=8000000000000000  <≤=≥>:11000
 >> n=-9223372036854775808 x=8000000000000000  <≤=≥>:01010

 Philippe> What you show here is a bug of the C compiler in its
 Philippe> optimizer

Possibly unfortunately, this is not a bug. The program invokes undefined
behavior when it does n=-n on the the value shown; the compiler is
entitled to produce any output at all as a result.

In this case it's fairly obvious what happened: after seeing the line

if (n<0) n=-n;

the compiler is entitled to assume that n>=0 is true regardless of the
prior value of n, since only undefined behavior could cause that not to
be the case and the compiler is allowed to assume that undefined
behavior never occurs. So in the second fprintf, the compiler can
optimize (n>=0) to a constant 1, and (n<0) to a constant 0, while still
computing the remaining tests.

 Philippe> I tried just compiling your example using only an "int n;" or
 Philippe> "long n;" declaration (and all other basic integer types of
 Philippe> various size), and no custom class at all (or other incorrect
 Philippe> macros transcluded from your platform headers, possibly
 Philippe> patched by you), and I get consistant results, even with GCC,
 Philippe> in C like in C++ and various other dialects, and as well in
 Philippe> Java, J#, C#...

When dealing with modern compilers you can't just take a fragment of
code like the above and expect to reproduce the results; you need a
complete compilable program. It matters for example whether "n" is a
compile-time constant, because if it is, the compiler will do all the
evaluations at compile time and not need to make the (n>=0) assumption.

Here is an example program to reproduce the issue:

#include <stdio.h>
#include <limits.h>

static void tst(long n)
{
    fprintf(stderr,"n=%li x=%lx  <≤=≥>:%i%i%i%i%i\n",n,n,n<0,n<=0,n==0,n>=0,n>0);
    if (n<0) n=-n;
    fprintf(stderr,"n=%li x=%lx  <≤=≥>:%i%i%i%i%i\n",n,n,n<0,n<=0,n==0,n>=0,n>0);
}

int main(int argc, char **argv)
{
    tst(argc > 0 ? LONG_MIN : 1);
    return 0;
}

The use of argc here is to force the value not to be compile-time
constant. (The asm output is a bit more readable if you force tst() not
to be inlined, but I didn't include that for portability reasons.) This
shows the issue when compiled with either gcc8 or clang8 at any
optimization level above 0, unless you use -fwrapv.

Interesting changes you can do to the above program to see what the
optimizer does:

1. change to tst(LONG_MIN) and the unexpected output disappears, because
   now the compiler is doing all the tests at compile time.

2. change to tst(argc > 0 ? LONG_MIN : 0) and the output becomes even
   more strange in a different way (explanation left as an exercise for
   the reader)

-- 
Andrew.