When requested to optimize ( 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
[edit | edit source]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:
inti,j=0; for(i=1;i>0;i+=i){ ++j; }
2. The following:
(i*2000)/1000
is optimized into:
i*2
- Use with to signal cases where code is optimized away.
- Use
If lucky enough to use gcc 4.9 and later, you can use the ubsan sanitizer:
- compile and link the program with option
- use other available flags as needed.
Unsigned Wrap Around
[edit | edit source]The compiler assumes unsigned integers do not wrap. Keep the unsigned variables in the range to avoid surprises.
"Dead Code" Removed
[edit | edit source]The call could be removed because the compiler deems buf unused at that point in the code and after:
voiddo_something(void{ charbuf[123]; ...usebuf... /* Clear it. But removed by gcc: */ memset(buf,0,sizeof(buf)); }
- Use directives to force the code in.
- Use .
Pitfalls
[edit | edit source]Code can be moved around:
volatileintflag=0; charbuf[123]; voidinit_buf(){ for(size_ti=0;i<123;++i){ buf[i]=0;//Do something } flag=1;// Can move! }
could be optimized into:
volatileintflag=0; charbuf[123]; voidinit_buf(){ flag=1;// Moved! for(size_ti=0;i<123;++i){ //Do something } }
- Use compiler intrinsic barriers to prevent the flag moving.
Loops could be optimized into one read call only:
void*ptr=address; while(*((volatileint*)ptr)&flag){}
Pointers
[edit | edit source]- Use
- Use . Null pointer checks are deleted if placed after the first use of the pointer.
