5 ms·
The literal example in the Clang documentation is not about optimization: > For example, without the __builtin_unreachable in the example below, the compiler a
by apaprocki 4y ago
The 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 agoAttempting to emulate restricted with __builtin_unreachable has been one of the first thing I tried when I learned about unreachable. I periodically try again, but generally I have been underwhelmed with trying to give gcc value range informations with it.