Troubles with GCC signed integer overflow optimization

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
Dieser Beitrag wurde unter Computing abgelegt und mit , , , , verschlagwortet. Setze ein Lesezeichen auf den Permalink.

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

CAPTCHA-Bild

*

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>