开发者

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

开发者 https://www.devze.com 2023-01-09 13:56 出处:网络
It\'s common for compilers to provi开发者_C百科de a switch to warn when code is unreachable. I\'ve also seen macros for some libraries, that provide assertions for unreachable code.

It's common for compilers to provi开发者_C百科de a switch to warn when code is unreachable. I've also seen macros for some libraries, that provide assertions for unreachable code.

Is there a hint, such as through a pragma, or builtin that I can pass to GCC (or any other compilers for that matter), that will warn or error during compilation if it's determined that a line expected to be unreachable can actually be reached?

Here's an example:

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

The value of this is in detecting, after changes in conditions above the expected unreachable line, that the line is in fact reachable.


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


__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.


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.


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

精彩评论

暂无评论...
验证码 换一张
取 消