4 ms·
One thing people are missing is that you may need to use this to satisfy conditions that the compiler might encounter to emit a warning (or error if equivalent
by apaprocki 4y ago
One thing people are missing is that you may need to use this to satisfy conditions that the compiler might encounter to emit a warning (or error if equivalent of -Werror is enabled). If you have strict warnings on, but no way to tell the compiler that a location should not be reachable, you wind up in situations where you are doing something like `assert(!"not reached!")` and it won't be enabled in all build types. This is similar to how `__attribute__((noreturn))` is used in a "fatal" function that isn't always enabled but needs to convey its intention to static analyzers so that they stop evaluating branches past that call.
- gpderetta 4y agoFor that abort() is just fine. Being UB, unreachable is more about optimizations .
- dahfizz 4y ago>> and it won't be enabled in all build types. The typical usecase would to wrap this in a #define, so that it aborts on a debug build but you get faster code in a production build.
- deleted 4y ago[deleted]
- apaprocki 4y agoThe literal example in the Clang documentation is not about optimization: > For example, without the __builtin_unreachable in the example below, the compiler assumes that the inline asm can fall through and prints a “function declared ‘noreturn’ should not return” warning. void myabort(void) __attribute__((noreturn)); void myabort(void) { asm("int3"); __builtin_unreachable(); }
- a1369209993 4y ago> the compiler assumes that the inline asm can fall through and prints a "function declared 'noreturn' should not return" warning. It actually can (Hint: what happens if the operating system `iret`s from its int 3 handler?), although it's probably not a issue in practice. Regardless, you don't need __builtin_unreachable to write: void myabort(void) __attribute__((noreturn)); void myabort(void) { asm("int3"); myabort(); // might need `return myabort();` to force TCO, // but gcc doesn't like that and it should work anyway } # Assuming tail-call optimization etcetera, this produces: myabort: int3 jmp myabort which is a correct implementation. However, if you're implementing built-in/standard functions like abort, you presumably know what compiler you're using and don't need a std interface in the first place. There's zero legitimate reason to use a undefined-behaviour-based `unreachable` in application code. Claiming std::unreachable is useful for implementing abort is like proposing a std::manual_copy function because your compiler optimized a implementation of memcpy to a call to itself - at some point you do in fact have to resort to implementaion-specific details to define the abstractions that abstract away said details, and "in literally the same function as the (also-nonstandard, IIRC) inline assembly that hopefully doesn't return" seems at if not noticeably past that point.
- gpderetta 4y agoRight elsethread I said that it is both about optimizations and static analysis. That's a static analysis case.
- apaprocki 4y agoIt seems like optimization cases where this makes sense are generally of the sort where `noreturn` can't be used because it is conditional. That makes sense to me (although, could this have covered most use cases by making `noreturn` support being passed the name of a function argument?). One example was interesting, though. I could see someone believing these might produce the same optimized assembly (-O2): void a(int& x, int& y) { if (&x == &y) __builtin_unreachable(); x ^= y; y ^= x; x ^= y; } void b(int& __restrict x, int& __restrict y) { x ^= y; y ^= x; x ^= y; } However, it produces: a(int&, int&): # @a(int&, int&) mov eax, dword ptr [rdi] xor eax, dword ptr [rsi] mov dword ptr [rdi], eax xor eax, dword ptr [rsi] mov dword ptr [rsi], eax xor dword ptr [rdi], eax ret b(int&, int&): # @b(int&, int&) mov eax, dword ptr [rsi] mov ecx, dword ptr [rdi] mov dword ptr [rsi], ecx mov dword ptr [rdi], eax ret Is this something compiler contributors would optimize once they know about it? Similar question probably exists with using `__builtin_unreachable` if values aren't aligned versus `__builtin_assume_aligned`. I'd love to see these things illustrated with more real-world examples.
- CJefferson 4y agoTechnically these would still produce different results if y started two bytes after x -- I can't remember if that is itself legal however!
- gpderetta 4y agoNo that wouldn't be legal. Distinct objects can't partially overlap, unless one is a subject of the other.
- gpderetta 4y ago