Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I prevent the gcc optimizer from producing incorrect bit operations?

Consider the following program.

#include <stdio.h>  int negative(int A) {     return (A & 0x80000000) != 0; } int divide(int A, int B) {     printf("A = %d\n", A);     printf("negative(A) = %d\n", negative(A));     if (negative(A)) {         A = ~A + 1;         printf("A = %d\n", A);         printf("negative(A) = %d\n", negative(A));     }     if (A < B) return 0;     return 1; } int main(){     divide(-2147483648, -1); } 

When it is compiled without compiler optimizations, it produces expected results.

gcc  -Wall -Werror -g -o TestNegative TestNegative.c ./TestNegative A = -2147483648 negative(A) = 1 A = -2147483648 negative(A) = 1 

When it is compiled with compiler optimizations, it produces the following incorrect output.

gcc -O3 -Wall -Werror -g -o TestNegative TestNegative.c ./TestNegative  A = -2147483648 negative(A) = 1 A = -2147483648 negative(A) = 0 

I am running gcc version 5.4.0.

Is there a change I can make in the source code to prevent the compiler from producing this behavior under -O3?

like image 375
merlin2011 Avatar asked Feb 13 '18 07:02

merlin2011


People also ask

How do I stop GCC optimization?

Use the command-line option -O0 (-[capital o][zero]) to disable optimization, and -S to get assembly file. Look here to see more gcc command-line options. Show activity on this post.

How do I enable optimization in GCC?

GCC has a range of optimization levels, plus individual options to enable or disable particular optimizations. The overall compiler optimization level is controlled by the command line option -On, where n is the required optimization level, as follows: -O0 . (default).

Is GCC an optimizing compiler?

GCC performs nearly all supported optimizations that do not involve a space-speed tradeoff. As compared to -O , this option increases both compilation time and the performance of the generated code.

What is GCC optimize?

The compiler optimizes to reduce the size of the binary instead of execution speed. If you do not specify an optimization option, gcc attempts to reduce the compilation time and to make debugging always yield the result expected from reading the source code.


2 Answers

  1. -2147483648 does not do what you think it does. C doesn't have negative constants. Include limits.h and use INT_MIN instead (pretty much every INT_MIN definition on two's complement machines defines it as (-INT_MAX - 1) for a good reason).

  2. A = ~A + 1; invokes undefined behavior because ~A + 1 causes integer overflow.

It's not the compiler, it's your code.

like image 166
Art Avatar answered Oct 14 '22 04:10

Art


The compiler replaces your A = ~A + 1; statement with a single neg instruction, i.e. this code:

int just_negate(int A) {     A = ~A + 1;     return A; } 

will be compiled to:

just_negate(int):   mov eax, edi   neg eax         // just negate the input parameter   ret 

But the compiler is also smart enough to realize that, if A & 0x80000000 was non-zero before negation, it must be zero after negation, unless you are relying on undefined behavior.

This means that the second printf("negative(A) = %d\n", negative(A)); can be "safely" optimized to:

mov edi, OFFSET FLAT:.LC0    // .string "negative(A) = %d\n" xor eax, eax                 // just set eax to zero call printf 

I use the online godbolt compiler explorer to check the assembly for various compiler optimizations.

like image 31
Groo Avatar answered Oct 14 '22 03:10

Groo