Emulating GCC's __builtin_unreachable?
Asked Answered
L

5

26

I get a whole lot of warnings about switches that only partially covers the range of an enumeration switched over. Therefor, I would like to have a "default" for all those switches and put __builtin_unreachable (GCC builtin) in that case, so that the compiler know that case is not reachable.

However, I came to know that GCC4.3 does not support that builtin yet. Is there any good way to emulate that functionality? I thought about dereferencing a null pointer instead, but that may have other undesirable effects/warnings and such. Do you have any better idea?

Luxurious answered 17/5, 2011 at 13:52 Comment(1)
switch((int)myenum) /*silence warnings*/ ^_^Eraeradiate
U
13

The upcoming 2023 revision of the C standard (C23, ISO/IEC 9899:2023) is going to have a new macro unreachable

#include <stddef.h>
void unreachable(void);

with the effect of gcc's __builtin_unreachable.

On older C standards, you may be able to call an inline function declared _Noreturn to mark anything after that call as unreachable. The compiler is allowed to throw out any code after such a function. If the function itself is static (and does return), the compiler will usually also inline the function. Here is an example:

static _Noreturn void unreachable() {
    return; /* intentional */
}

/* ... */

foo();
bar(); /* should better not return */
unreachable();
baz(); /* compiler will know this is not reachable */

Notice that you invoke undefined behavior if a function marked _Noreturn indeed returns. Be sure that said function will never be called.

Uppsala answered 15/3, 2014 at 16:54 Comment(10)
It warns by saying "Function marked noreturn returns".Luxurious
@Johannes Schaub That's right. gcc warns you but accepts the code. As long as your code never actually reached unreachable(), your code should not exhibit undefined behavior.Uppsala
This is the proper basis of __builtin_unreachable that evolved into the builtin as-is. Disable the specific warning as it is intentionally returning. There may be some further trick other than returning, but this as-is is the equivalent to __builtin_unreachable. Compare ideone.com/f8yrLX with ideone.com/VP6NOBTermination
@FUZxxl Shouldn't you call abort() instead of return. Not only it doesn't cause undefined behavior, it is also correct, as the abort function is defined with the _Noreturn function specifier.Badmouth
@Badmouth The idea of __builtin_unreachable() is to give the compiler a hint it cannot interfere on its own. This objective is not reached by inserting a call to abort(). __builtin_unreachable() is about optimization, not about safety.Uppsala
@FUZxxl Yeah I just tried abort and it didn't optimize it.Badmouth
@Uppsala what is actually the point of the return statement? Is it there just to avoid the compiler optimizing the call to unreachable() away completely?Gaidano
@JesperMatthiesen It's for documentation, it can be removed.Uppsala
My gcc 4.3.3 does not support _Noreturn, in fact all I can find says it is supported (in C11) from gcc 4.7. If that is really the case, there's no point since __builtin_unreachable() is supported since 4.5 according to the answer below. I suppose __attribute__ ((noreturn)) could be an alternative!?Gaidano
@JesperMatthiesen If you are programming for such an old C compiler, you may need to use a cascade of #if directives to find the right implementation for your use case.Uppsala
G
8

Hmm, something like (since __builtin_unreachable() appeared in 4.5):


#define GCC_VERSION (__GNUC__ * 10000 \
                               + __GNUC_MINOR__ * 100 \
                               + __GNUC_PATCHLEVEL__)
#if GCC_VERSION >= 40500
#define my_unreachable()  __builtin_unreachable()
#else
#define my_unreachable() do { printf("Oh noes!!!111\n"); abort(); } while(0)
#endif

Greenwood answered 17/5, 2011 at 14:15 Comment(8)
if (x) y(); else my_unreachable(); does not work with your definitionDepth
Can be easily fixed by putting the prinf..abort block in a do/while such as: do { <block here> }while(0)Sena
@Luther: Sure, it was just meant as a simple example of how to use the _GNUC* macros. For a real implementation one would use e.g. the usual do-while(0) trick and maybe do something else in the actual implementation than print "oh no!".Greenwood
But this still assumes that the code is reachable. The compiler is not allowed to optimize that printf and that abort away, so it has to emit that code. I want to specifically tell the compiler that the place the macro is put into is unreachable, so that it omits codegen or warnings for all control flow points after it.Luxurious
@litb: So in effect, you're asking "my compiler doesn't allow me to specify code as unreachable, how do I tell my compiler that code is unreachable". Doesn't really compute. Anyways, at least on my system abort() is marked with attribute ((noreturn)) so the compiler should be able to get rid of any code below that call. I guess you still get the warnings though.Greenwood
@Greenwood no, i'm looking for a work-around to achieve similar things to __builtin_unreachable. A call to "abort" will not do it, because it has defined behavior, so the compiler still has to put the call to "abort" at that place :(Luxurious
How sure are you that your enums will never hit that default: case ? It sounds more like it'd be an assert (or an abort()) there in case your assumptions were wrong. __builtin_unreachable() are only for cases where you absolutly, 100% positivly know the code can not ever be reached - and if that builtin is not available, I don't think there's a real substitute, it has to be a compiler intristic.Tingey
@Tingey it will hurt performance. this place has shown to be a bottleneck, so i need to avoid any useless branches and boilerplate the compiler emits on grounds that it thinks that certain code is reachable (and if logic proves that it cannot be reached, perhaps based on higher level logic, not visible by purely analyzing the code statically).Luxurious
A
4

Would abort (leaving a core dump) or throw (allowing for alternate data capture) accommodate your needs?

Do you really want to have switch statements that don't cover the full enumeration? I nearly always try to list all the possible cases (to no-op) with no default case so that gcc will warn me if new enumerations are added, as it may be required to handle them rather than letting it silently (during compile) fall into the default.

Almost answered 17/5, 2011 at 14:15 Comment(0)
S
3

keep it simple:

assert(false);

or, better yet:

#define UNREACHABLE (!"Unreachable code executed!")

assert(UNREACHABLE);
Sanalda answered 17/5, 2011 at 14:19 Comment(5)
Too simple. The compiler will still treat anything following the assert as reachable, which can lead to unwanted warnings.Depth
are you sure? POSIX says that assert() is a macro, and the compiler should be able to statically see that false can never evaluate to true, and therefore turn assert(false) ultimately into a call to abort()...Sanalda
@Sanalda this is only true if NDEBUG is not defined. Assertions are meant to be no-ops in non-debug builds.Salcido
@Salcido both gcc and clang emit a ud2 instruction on x86 if compiled with -O0 -DNDEBUG, but completely optimize the branch away in -O3Companion
@BalázsKovacsics I can't confirm.Salcido
T
2
template<unsigned int LINE> class Unreachable_At_Line {}; 
#define __builtin_unreachable() throw Unreachable_At_Line<__LINE__>()

Edit:

Since you want to have unreachable code to be omitted by compiler, below is the simplest way.

#define __builtin_unreachable() { struct X {X& operator=(const X&); } x; x=x; }

Compiler optimizes away x = x; instruction especially when it's unreachable. Here is the usage:

int foo (int i)
{
  switch(i)
  {
  case 0:  return 0;
  case 1:  return 1;
  default: return -1;
  }
  __builtin_unreachable();  // never executed; so compiler optimizes away
}

If you put __builtin_unreachable() in the beginning of foo() then compiler generates a linker error for unimplemented operator =. I ran these tests in gcc 3.4.6 (64-bit).

They answered 17/5, 2011 at 14:18 Comment(3)
LINE will always expand to the line where __builtin_unreachable() is defined not usedDepth
This has the same problem as the solution by @janneb. The compiler is required to emit the throw.Luxurious
@Johannes, can you check the edited answer ? Here the compiler either gives error or optimizes the unreachable part. So there is no real time complications.They

© 2022 - 2024 — McMap. All rights reserved.