如何提示GCC在编译时无法访问一行?

Mat*_*ner 13 c gcc pragma built-in unreachable-code

编译器通常会提供一个开关,以便在代码无法访问时发出警告.我还看到了一些库的宏,它们为无法访问的代码提供断言.

是否存在提示,例如通过编译指示或内置,我可以传递给GCC(或任何其他编译器),如果确定实际可以达到预期无法访问的行,则会在编译期间发出警告或错误?

这是一个例子:

    if (!conf->devpath) {
        conf->devpath = arg;
        return 0;
    } // pass other opts into fuse
    else {
        return 1;
    }
    UNREACHABLE_LINE();
Run Code Online (Sandbox Code Playgroud)

这个值的作用是在预期的不可达行之上的条件发生变化之后检测到该行实际上是可达的.

Has*_*kun 14

gcc 4.5支持__builtin_unreachable()内联编译器,将其与-Wunreachable-code可能做你想做的事情相结合,但可能会引起虚假警告

  • 我想请注意,在更高版本的gcc中删除了`-Wunreachable-code`选项(因为在不同的优化选项下它是高度不可预测的) (3认同)

小智 6

使用 gcc 4.4.0 Windows 交叉编译器到 PowerPC 使用 -O2 或 -O3 进行编译,以下对我有用:

#define unreachable asm("unreachable\n")
Run Code Online (Sandbox Code Playgroud)

如果编译器没有优化它,因为它已经得出结论它无法访问,则汇编器会因未知操作而失败。

是的,它很可能“在不同的优化选项下高度不可预测”,并且在我最终更新编译器时可能会中断,但就目前而言,它总比没有好。


Cir*_*四事件 6

__builtin_unreachable() 就我在 GCC 7.3.0 上看到的而言,不会生成任何编译时警告

我在文档中也找不到任何表明它会这样做的内容。

例如,以下示例编译时没有任何警告:

#include <stdio.h>

int main(void) {
    __builtin_unreachable();
    puts("hello")
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

和:

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -Wunreachable-code main.c
Run Code Online (Sandbox Code Playgroud)

我认为它的唯一的事情不会做,是为了让基于这样一个事实:某行代码是从来没有达到编译器执行某些优化,并给出不确定的行为,如果你犯了一个编程错误,它永远不会。

比如执行上面的例子,看似正常退出,但没有hello按预期打印。然后我们的装配分析表明,正常外观的出口只是 UB 巧合。

-fsanitize=unreachableGCC的标志将 转换为__builtin_unreachable();在运行时失败的断言:

<stdin>:1:17: runtime error: execution reached a __builtin_unreachable() call
Run Code Online (Sandbox Code Playgroud)

不过,该标志在 Ubuntu 16.04 中已损坏:ld: unrecognized option '--push-state--no-as-needed'

这是什么__builtin_unreachable()做的可执行文件?

如果我们使用和不__builtin_unreachable使用 with反汇编代码:

objdump -S a.out
Run Code Online (Sandbox Code Playgroud)

我们看到没有它的那个调用puts

000000000000063a <main>:
#include <stdio.h>

int main(void) {
 63a:   55                      push   %rbp
 63b:   48 89 e5                mov    %rsp,%rbp
    puts("hello");
 63e:   48 8d 3d 9f 00 00 00    lea    0x9f(%rip),%rdi        # 6e4 <_IO_stdin_used+0x4>
 645:   e8 c6 fe ff ff          callq  510 <puts@plt>
    return 0;
 64a:   b8 00 00 00 00          mov    $0x0,%eax
}
 64f:   5d                      pop    %rbp
 650:   c3                      retq
 651:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 658:   00 00 00
 65b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
Run Code Online (Sandbox Code Playgroud)

而没有的只做:

int main(void) {
 5fa:   55                      push   %rbp
 5fb:   48 89 e5                mov    %rsp,%rbp
 5fe:   66 90                   xchg   %ax,%ax
Run Code Online (Sandbox Code Playgroud)

并且甚至没有返回,所以我认为它没有爆炸只是一个未定义的行为巧合。

为什么 GCC 无法确定某些代码是否无法访问?

我收集了以下答案:

  • 由于某种原因,自动确定无法访问的代码对于 GCC 来说太难了,这就是为什么多年来现在-Wunreachable-code什么都不做:gcc 不会对无法访问的代码发出警告

  • 用户可能会使用暗示不可达性的内联汇编,但 GCC 无法确定这一点。GCC手册中提到这一点:

    一个这样的情况是紧跟在一个永远不会终止的 asm 语句之后,或者一个将控制权转移到别处并且永远不会返回的语句。在此示例中,如果没有 __builtin_unreachable,GCC 会发出警告,指出控制已到达非 void 函数的末尾。它还生成在 asm 之后返回的代码。

    int f (int c, int v)
    {
      if (c)
        {
          return v;
        }
      else
        {
          asm("jmp error_handler");
          __builtin_unreachable ();
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

在 GCC 7.3.0、Ubuntu 18.04 上测试。