Linux Applications Debugging Techniques/The compiler

When requested to optimize (-O2 and above), the compiler is granted a "total license" to modify it: treats undefined behavior (by the standard) as it cannot happen. Thus, the resulting code behaves differently in release-optimized mode than debug mode. Such differences in behavior cannot be found by static analyzers or code reviews.

Signed Integer Overflow
1. This is C99 undefined behavior and the compiler assumes overflow cannot occur. Thus, any checks for overflow are discarded and the following is an infinite loop in optimized code:

2. The following:

is optimized into:


 * Use -Wstrict-overflow=N with N>=3 to signal cases where code is optimized away.
 * Use -fwrapv

If lucky enough to use gcc 4.9 and later, you can use the ubsan sanitizer:
 * compile and link the program with -fsanitize=undefined option
 * use other available flags as needed.

Unsigned Wrap Around
The compiler assumes unsigned integers do not wrap. Keep the unsigned variables in the range [INT_MIN/2, INT_MAX/2] to avoid surprises.

"Dead Code" Removed
The memset call could be removed because the compiler deems buf unused at that point in the code and after:


 * Use #pragma optimize directives to force the code in.
 * Use <tt>volatile</tt>.

<tt>volatile</tt> Pitfalls
Code can be moved around:

could be optimized into:


 * Use compiler intrinsic barriers to prevent the flag moving.

Loops could be optimized into one read call only:

Pointers

 * Use <tt>restrict</tt>
 * Use <tt>-fno-delete-null-pointer-checks</tt>. Null pointer checks are deleted if placed after the first use of the pointer.