Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disable "if(0)" elimination in gcc

How can I prevent GCC from eliminating the code inside if(0) block?

When I use Visual Studio, one of my debugging techniques is to put code like this in my program:

if (0)
    do_some_debug_printing_and_checking();

Then, when a breakpoint is hit, I click at the do_some_debug_printing_and_checking() line, select "set next statement" and force it to execute.

When I use gcc/gdb as a back-end, the "set next statement" does not work anymore, as GCC simply removes the code from inside the if(0) statement.

I am of course using the -O0 flag to disable optimization. I have also tried the -fno-dce -fno-tree-dce flags to disable dead code elimination explicitly, but it has no effect: the contents of if(0) is just not present in the binary file and I cannot use set next statement to jump into it.

Is there any good way to tell gcc to disable elimination of if(0) contents?

Edit:

Thanks for the "additional variable" workaround, however there are 2 things I don't like about it:

  1. It's still an extra line of code
  2. It won't be automatically optimized out when I build the release version and do want those debug things to disappear. Surely I can use #ifdef-s, but that's even more extra lines.

Really, there's absolutely no option to make GCC keep that dead code?

like image 544
Ivan Shcherbakov Avatar asked Jul 01 '12 11:07

Ivan Shcherbakov


2 Answers

The simplest thing to do is to make the check depend on (say) a variable with external linkage.

E.g.

extern bool debug;
if (debug)
    do_some_debug_printing_and_checking();

The somewhere at namespace scope:

bool debug = false;
like image 108
CB Bailey Avatar answered Oct 30 '22 03:10

CB Bailey


I would not rely on gcc compiler flags to do this. Compiler flags can change across versions of gcc, and do change across compilers. You may find yourself needing to debug the same code in six months in Visual C++...

@CharlesBailey makes a nice suggestion for how to do this with an extern variable. Here's one alternative that doesn't require a variable to be exposed to the entire module or kept in static storage.

Declare a temporary variable volatile in the scope of the if statement:

if (volatile bool dbg = false)
{
  do_some_debug_printing_and_checking();
}

This keeps the scope of the temporary variable quite narrow. The volatile qualifier does not let the compiler assume anything about the variable, or optimize the branch away.

One thing to keep in mind is that the variable is always allocated on the stack, and will be kept on the stack until the function exits. Both this approach and the extern approach should work, but have slightly different (and probably negligible) tradeoffs.

If you're willing to use macros to help solve this problem, then you can easily disable the temporary variable when your release your code into production:

#ifndef IS_DEBUGGING
#  define IS_DEBUGGING 0
#endif

#if IS_DEBUGGING
#  define TMP_DBG_FLAG volatile bool dbg_flag = false
#else
#  define TMP_DBG_FLAG false
#endif

Then declare your if statement as:

if ( TMP_DBG_FLAG )
{
  do_some_debug_printing_and_checking();
}

When you define IS_DEBUGGING to be 1, the local variable is created, declared volatile, and kept. When you define IS_DEBUGGING to be 0, the macro expands to the constant false and the compiler optimizes the branch away. Something very similar could be done for the extern approach, as well.

This few extra lines of code, but they're independent of the number of times you use TMP_DBG_FLAG. The code is also a lot more readable than using tons of ifdefs. The macro could be made a bit safer (by appending the value of __LINE__ to it), but this would require three macros, and is probably not necessary:

#if IS_DEBUGGING
// paste symbols 'x' and 'y' together
#  define TMP_DBG_FLAG_SYMCAT0(x,y) x ## y

// need one level of indirection to expand __LINE__...
#  define TMP_DBG_FLAG_SYMCAT(x,y) TMP_DBG_FLAG_SYMCAT0(x,y)

#  define TMP_DBG_FLAG volatile bool TMP_DBG_FLAG_SYMCAT(dbg_flag_,__LINE__) = false
#else
#  define TMP_DBG_FLAG false
#endif
like image 33
sfstewman Avatar answered Oct 30 '22 05:10

sfstewman