As signed integer wraparound (overflow) is not defined by C/C++ standards, GCC sometimes optimizes expressions with the assumption that wraparound does not happen, even though the CPU very well does perform wraparound. This can lead to cases in which the behaviour of a program depends on the optimization level that was used to compile the code. (To make matters worse, different versions of GCC behave differently, so that in practive the behaviour of the program may depend on the combination of GCC version and optimization level used.)
In the following example code, signed int a is set to the largest value (0x7fffffff for 32bit and 0x7fffffffffffffff for 64bit architectures). Then it is tested, whether increasing it by one gives a higher number. When signed int wraparound works, the result would be false: a wraps to the smallest value (0×80000000 for 32bit), which of course is smaller than the previous value of a. However an optimization assuming that overflow cannot take place will always return true for the statement a+1 > a.
#include <stdio.h>
int wrap(int a) {
return (a + 1 > a);
}
int main(void) {
printf("%s\n", wrap(~0u>>1) ? "no wrap" : "wrapped ok");
}
Conveniently, GCC provides the command line switch -fwrapv, which according to GCC docs
[...] instructs the compiler to assume that signed arithmetic overflow of addition, subtraction and multiplication wraps [...]
Inconveniently, this switch is known to be broken for some versions of GCC. I did some tests of my own (see below for details).
The bottom line:
- Don’t trust signed int wraparound without using the -fwrapv command line parameter, results are unpredictable.
- Don’t trust signed int wraparound for GCC 3.x even when using -wrapv, it is broken.
- With GCC 4.x signed int wraparound works when -wrapv is specified, except for some cases of miscompilation (see links above).
- With GCC 4.2.4 or later, signed int wraparound together with -wrapv seems to work without reservation.
- With GCC 4.2 the option -fno-strict-overflow has been introduced, which has the same effect as -fwrapv but seems to be safer and is preferred by the kernel folks.
Table of test results:
gcc (GCC) 3.4.6 20060404 (Red Hat 3.4.6-11) -O0 --> wrapped ok gcc (GCC) 3.4.6 20060404 (Red Hat 3.4.6-11) -O1 --> no wrap gcc (GCC) 3.4.6 20060404 (Red Hat 3.4.6-11) -O2 --> no wrap gcc (GCC) 3.4.6 20060404 (Red Hat 3.4.6-11) -O3 --> wrapped ok gcc (GCC) 3.4.6 20060404 (Red Hat 3.4.6-11) -Os --> no wrap gcc (GCC) 3.4.6 20060404 (Red Hat 3.4.6-11) -O0 -fwrapv --> wrapped ok gcc (GCC) 3.4.6 20060404 (Red Hat 3.4.6-11) -O1 -fwrapv --> no wrap gcc (GCC) 3.4.6 20060404 (Red Hat 3.4.6-11) -O2 -fwrapv --> no wrap gcc (GCC) 3.4.6 20060404 (Red Hat 3.4.6-11) -O3 -fwrapv --> wrapped ok gcc (GCC) 3.4.6 20060404 (Red Hat 3.4.6-11) -Os -fwrapv --> no wrap gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) -O0 --> no wrap gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) -O1 --> no wrap gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) -O2 --> no wrap gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) -O3 --> no wrap gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) -Os --> no wrap gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) -O0 -fwrapv --> wrapped ok gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) -O1 -fwrapv --> wrapped ok gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) -O2 -fwrapv --> wrapped ok gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) -O3 -fwrapv --> wrapped ok gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) -Os -fwrapv --> wrapped ok gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-46) -O0 --> no wrap gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-46) -O1 --> no wrap gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-46) -O2 --> no wrap gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-46) -O3 --> no wrap gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-46) -Os --> no wrap gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-46) -O0 -fwrapv --> wrapped ok gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-46) -O1 -fwrapv --> wrapped ok gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-46) -O2 -fwrapv --> wrapped ok gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-46) -O3 -fwrapv --> wrapped ok gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-46) -Os -fwrapv --> wrapped ok gcc (Debian 4.3.4-6) 4.3.4 -O0 --> wrapped ok gcc (Debian 4.3.4-6) 4.3.4 -O1 --> wrapped ok gcc (Debian 4.3.4-6) 4.3.4 -O2 --> no wrap gcc (Debian 4.3.4-6) 4.3.4 -O3 --> no wrap gcc (Debian 4.3.4-6) 4.3.4 -Os --> no wrap gcc (Debian 4.3.4-6) 4.3.4 -O0 -fwrapv --> wrapped ok gcc (Debian 4.3.4-6) 4.3.4 -O1 -fwrapv --> wrapped ok gcc (Debian 4.3.4-6) 4.3.4 -O2 -fwrapv --> wrapped ok gcc (Debian 4.3.4-6) 4.3.4 -O3 -fwrapv --> wrapped ok gcc (Debian 4.3.4-6) 4.3.4 -Os -fwrapv --> wrapped ok gcc (Debian 4.3.4-6) 4.3.4 -O0 -fno-strict-overflow --> wrapped ok gcc (Debian 4.3.4-6) 4.3.4 -O1 -fno-strict-overflow --> wrapped ok gcc (Debian 4.3.4-6) 4.3.4 -O2 -fno-strict-overflow --> wrapped ok gcc (Debian 4.3.4-6) 4.3.4 -O3 -fno-strict-overflow --> wrapped ok gcc (Debian 4.3.4-6) 4.3.4 -Os -fno-strict-overflow --> wrapped ok

Thank you, almost 5 years later and I just ran into this.