Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

strange integer behavior with gcc -O2

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

void sanity_check(int x)
{
    if (x < 0)
    {
        x = -x;
    }
    if (x == INT_MIN)
    {
        printf("%d == %d\n", x, INT_MIN);
    }
    else
    {
        printf("%d != %d\n", x, INT_MIN);
    }
    if (x < 0)
    {
        printf("negative number: %d\n", x);
    }
    else
    {
        printf("positive number: %d\n", x);
    }
}

int main(void)
{
    sanity_check(42);
    sanity_check(-97);
    sanity_check(INT_MIN);
    return 0;
}

When I compile the above program with gcc wtf.c, I get the expected output:

42 != -2147483648
positive number: 42
97 != -2147483648
positive number: 97
-2147483648 == -2147483648
negative number: -2147483648

However, when I compile the program with gcc -O2 wtf.c, I get a different output:

42 != -2147483648
positive number: 42
97 != -2147483648
positive number: 97
-2147483648 != -2147483648
positive number: -2147483648

Note the last two lines. What on earth is going on here? Is gcc 4.6.3 optimizing a bit too eagerly?

(I also tested this with g++ 4.6.3, and I observed the same strange behavior, hence the C++ tag.)

like image 881
fredoverflow Avatar asked Oct 04 '12 14:10

fredoverflow


2 Answers

When you do -(INT_MIN) you're invoking undefined behavior, since that result can't fit in an int.

gcc -O2 notices that x can never be negative and optimizes thereafter. It doesn't care that you overflowed the value since that's undefined and it can treat it however it wants.

like image 170
Per Johansson Avatar answered Oct 04 '22 11:10

Per Johansson


I think this could help you, is from here :here

-fstrict-overflow Allow the compiler to assume strict signed overflow rules, depending on the language being compiled. For C (and C++) this means that overflow when doing arithmetic with signed numbers is undefined, which means that the compiler may assume that it will not happen. This permits various optimizations. For example, the compiler will assume that an expression like i + 10 > i will always be true for signed i. This assumption is only valid if signed overflow is undefined, as the expression is false if i + 10 overflows when using twos complement arithmetic. When this option is in effect any attempt to determine whether an operation on signed numbers will overflow must be written carefully to not actually involve overflow. This option also allows the compiler to assume strict pointer semantics: given a pointer to an object, if adding an offset to that pointer does not produce a pointer to the same object, the addition is undefined. This permits the compiler to conclude that p + u > p is always true for a pointer p and unsigned integer u. This assumption is only valid because pointer wraparound is undefined, as the expression is false if p + u overflows using twos complement arithmetic.

See also the -fwrapv option. Using -fwrapv means that integer signed overflow is fully defined: it wraps. When -fwrapv is used, there is no difference between -fstrict-overflow and -fno-strict-overflow for integers. With -fwrapv certain types of overflow are permitted. For example, if the compiler gets an overflow when doing arithmetic on constants, the overflowed value can still be used with -fwrapv, but not otherwise.

The -fstrict-overflow option is enabled at levels -O2, -O3, -Os.

like image 24
pedr0 Avatar answered Oct 04 '22 13:10

pedr0