C++ code gets strangely skipped with no optimizations. Any ideas why?
I looked for an answer for two days to this with no success. I've never come across this problem before so I'll try my best. Please bear with me.
I returned to a C++ project I created over a year ago, which at the time ran without problems. I came across this interesting and incredibly annoying problem the other day as I was trying to get the same program to run. The code was something like:
file.h
...
short id;
...
file.cc
id = 0;
while (id < some_large_number)
{
id = foo();
if (id == 2)
{
//do something
}
else if (id == 2900)
{
//do something
}
else if (id == 30000)
{
//do something
}
else if (id == 40000)
{
//do something
}
else if (id == 45000)
{
//do something
}
else
{
//do something else
}
}
The constant numbers were macros in hex notation that I expanded for this example. Turns out that this was truly a bug, but the debugger did not make it easy to discover. Heres what happened:
As开发者_高级运维 I was trying to step through the code using GDB (with no optimizations), I noticed that GDB would jump straight to the else statement after reaching if (id == 30000)
, everytime. Because the numbers were c macros in hex notation, I did not notice at first that 40000
was beyond the limit of a signed short
. This was very misleading, and spent hours trying to figure it out: I recompiled external libraries, reinstalled g++, among other things.
Obviously, making id
an unsigned short
fixed the problem. The other problem seems like a compiler issue. But I still don't understand, why were those sections of code completely skipped during execution, and with no optimizations? Why would it not go through each if
statement and that way I could identify the real problem? Any ideas?
Thanks so much. I hope this is okay for a first question.
If you enable all the warnings from gcc, it will tell you at compile time that this is going to happen.
short is 16 bits long and its range is -32768 to 32767. So it can never be 40000 or 45000 and compiler eliminated dead code (as it will never be reached).
GCC is an excellent optimizing compiler, however even when error and warning information is enabled via -Werror, -Wall, etc GCC still doesn't produce the same level of information a diagnostic compile does. While developing the code I would recommend using Clang, a diagnostic compiler, to help in finding bugs and error. Clang is intended to compatible with GCC, and with the exception of some more esoteric features I have had no problem changing my CC between the two in my Makefile.
Being an optimizing compiler, I believe GCC by default enable dead-code elimination. This would cause all branches which the compiler detected impossible, such as those outside the bounds of your id variable, to be eliminated. You might be able to disable that type of dead-code elimination.
Recall that during compiling, the C++ compiler looks through the code and determines what parts of the code execute in what order. If the compiler determines that a part of the code is never going to run, it will not optimize it.
For example:
int i = 0;
if( i == 1) {
printf("This will never be printed\n");
}
Here, there is no reason to optimize the if-statement, as it will never execute.
This sort of instance would be picked up if you compile with:
g++ -Wall mycode.c
where -Wall
means show all warnings, and mycode.c
is your project file.
As for execution, stepping through GDB shows the current flow of the program. If a branch (in an if-statement) is false, why would it ever go through that section of the code? You can only take one branch in an if-elseif-else statement.
I hope that helps you out.
My conclusion is the same as yours: It seems like it was optimized out even without "optimizations" turned on. Maybe these constant predicates "always true"/"always false" are used to skip the code somewhere directly in the code generation step, i.e. way sooner than any -O switch optimizations are being performed. Just a guess.
精彩评论