How to hint to GCC that a line should be unreachable at compile time?

前端 未结 4 407
渐次进展
渐次进展 2021-01-04 03:20

It\'s common for compilers to provide a switch to warn when code is unreachable. I\'ve also seen macros for some libraries, that provide assertions for unreachable code.

相关标签:
4条回答
  • 2021-01-04 03:59

    gcc 4.5 supports the __builtin_unreachable() compiler inline, combining this with -Wunreachable-code might do what you want, but will probably cause spurious warnings

    0 讨论(0)
  • 2021-01-04 04:05

    __builtin_unreachable() does not generate any compile time warnings as far as I can see on GCC 7.3.0

    Neither can I find anything in the docs that suggest that it would.

    For example, the following example compiles without any warning:

    #include <stdio.h>
    
    int main(void) {
        __builtin_unreachable();
        puts("hello")
        return 0;
    }
    

    with:

    gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -Wunreachable-code main.c
    

    The only thing I think it does do, is to allow the compiler to do certain optimizations based on the fact that a certain line of code is never reached, and give undefined behaviour if you make a programming error and it ever does.

    For example, executing the above example appears to exit normally, but does not print hello as expected. Our assembly analysis then shows that the normal looking exit was just an UB coincidence.

    The -fsanitize=unreachable flag to GCC converts the __builtin_unreachable(); to an assertion which fails at runtime with:

    <stdin>:1:17: runtime error: execution reached a __builtin_unreachable() call
    

    That flag is broken in Ubuntu 16.04 though: ld: unrecognized option '--push-state--no-as-needed'

    What does __builtin_unreachable() do to the executable?

    If we disassemble both the code with and without __builtin_unreachable with:

    objdump -S a.out
    

    we see that the one without it calls 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)
    

    while the one without does only:

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

    and does not even return, so I think it is just an undefined behaviour coincidence that it did not just blow up.

    Why isn't GCC able to determine if some code is unreachable?

    I gather the following answers:

    • determining unreachable code automatically is too hard for GCC for some reason, which is why for years now -Wunreachable-code does nothing: gcc does not warn for unreachable code

    • users may use inline assembly that implies unreachability, but GCC cannot determine that. This is mentioned on the GCC manual:

      One such case is immediately following an asm statement that either never terminates, or one that transfers control elsewhere and never returns. In this example, without the __builtin_unreachable, GCC issues a warning that control reaches the end of a non-void function. It also generates code to return after the asm.

      int f (int c, int v)
      {
        if (c)
          {
            return v;
          }
        else
          {
            asm("jmp error_handler");
            __builtin_unreachable ();
          }
      }
      

    Tested on GCC 7.3.0, Ubuntu 18.04.

    0 讨论(0)
  • 2021-01-04 04:10

    If your compiler does not have the warning that you need, it can be complemented with a static analyzer. The kind of analyzer I am talking about would have its own annotation language and/or recognize C assert, and use these for hints of properties that should be true at specific points of the execution. If there isn't a specific annotation for unreachable statements, you could probably use assert (false);.

    I am not personally familiar with them but Klokwork and CodeSonar are two famous analyzers. Goanna is a third one.

    0 讨论(0)
  • 2021-01-04 04:11

    With gcc 4.4.0 Windows cross compiler to PowerPC compiling with -O2 or -O3 the following works for me:

    #define unreachable asm("unreachable\n")

    The assembler fails with unknown operation if the compiler doesn't optimise it away because it has concluded that it is unreachable.

    Yes, it is quite probably `highly unpredictable under different optimization options', and likely to break when I finally update the compiler, but for the moment it's better then nothing.

    0 讨论(0)
提交回复
热议问题