【问题标题】:Disable "if(0)" elimination in gcc在 gcc 中禁用“if(0)”消除
【发布时间】:2012-07-01 11:14:34
【问题描述】:

如何防止 GCC 消除 if(0) 块内的代码?

当我使用 Visual Studio 时,我的调试技巧之一就是将这样的代码放入我的程序中:

if (0)
    do_some_debug_printing_and_checking();

然后,当一个断点被命中时,我点击 do_some_debug_printing_and_checking() 行,选择“设置下一条语句”并强制它执行。

当我使用 gcc/gdb 作为后端时,“设置下一条语句”不再起作用,因为 GCC 只是从 if(0) 语句中删除了代码。 p>

我当然使用 -O0 标志来禁用优化。我还尝试了 -fno-dce -fno-tree-dce 标志来明确禁用死代码消除,但它没有效果: if(0) 的内容只是不存在于二进制文件中,我无法使用 set next statement 跳转到它。

有什么好的方法可以告诉 gcc 禁止删除 if(0) 内容吗?

编辑:

感谢“附加变量”解决方法,但是有两点我不喜欢它:

  1. 还是多出一行代码
  2. 当我构建发布版本并希望那些调试内容消失时,它不会自动优化。我当然可以使用#ifdef-s,但那是更多的额外行。

真的,绝对没有办法让 GCC 保留那些死代码?

【问题讨论】:

  • 我知道这不是您要的,但请尝试为您的 ifs 使用设置为 false 的全局变量。这样编译器就不会优化你的 if 语句。
  • 当你想运行那个函数时,为什么不直接在 gdb 中 call do_some_debug_printing_and_checking() 呢?

标签: c++ optimization gcc gdb


【解决方案1】:

最简单的做法是使检查依赖于(例如)具有外部链接的变量。

例如

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

命名空间范围内的某处:

bool debug = false;

【讨论】:

  • 这样编译器就不会假设变量的值在两点之间没有变化
  • @Kos:但这里需要的只是防止编译器消除ifvolatile 将是多余的。
  • 实际上,我可以接受这样的冗余。我仍然认为这很重要
  • @Kos:这有什么关系,我错过了什么吗?
  • 如果两个 if 之间的代码是已知的,那么编译器假设变量没有改变难道不是安全的吗?见pastebin.com/ugweBG82 和输出pastebin.com/F3jH6Fhapastebin.com/PdwcUWEw;这里的 DEBUG 确实检查了两次,但这仍然感觉多余
【解决方案2】:

我不会依赖 gcc 编译器标志来执行此操作。编译器标志可以在 gcc 版本之间更改,并且在编译器之间进行更改。您可能会发现自己需要在六个月内使用 Visual C++ 调试相同的代码...

@CharlesBailey 对如何使用extern 变量提出了一个很好的建议。这是一种不需要将变量公开给整个模块或保存在静态存储中的替代方法。

if语句的范围内声明一个临时变量volatile

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

这使得临时变量的范围非常狭窄。 volatile 限定符不允许编译器对变量进行任何假设,或优化分支。

要记住的一点是,变量总是在堆栈上分配,并且将一直保留在堆栈上,直到函数退出。这种方法和extern 方法都应该有效,但权衡取舍略有不同(并且可能可以忽略不计)。

如果您愿意使用宏来帮助解决这个问题,那么您可以在将代码发布到生产环境时轻松禁用临时变量:

#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

然后将您的if 声明声明为:

if ( TMP_DBG_FLAG )
{
  do_some_debug_printing_and_checking();
}

当您将 IS_DEBUGGING 定义为 1 时,将创建、声明为 volatile 并保留局部变量。当您将 IS_DEBUGGING 定义为 0 时,宏扩展为常量 false,编译器优化分支。 extern 方法也可以做类似的事情。

这几行额外的代码,但它们与您使用 TMP_DBG_FLAG 的次数无关。该代码也比使用大量ifdefs 更具可读性。可以使宏更安全一些(通过将__LINE__ 的值附加到它),但这需要三个宏,并且可能没有必要:

#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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-17
    • 2023-03-30
    • 1970-01-01
    • 2021-01-20
    • 2010-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多