Date: Sat, 20 Sep 2025 08:33:44 -0700
On Saturday, 20 September 2025 07:52:47 Pacific Daylight Time David Brown via
Std-Discussion wrote:
> > That will_exit() function comes from some third-party library and isn't
> > marked [[noreturn]]. We know it won't return, so we don't want the
> > compiler to generate unnecessary code. So the author of this code needs
> > to insert a __builtin_unreachabble() to suppress the "control reaches end
> > of non-void function" warning. But note how there's no difference in code
> > generation: the __builtin simply suppresses the warning.
>
> There can be a difference in code generation - since the compiler knows
> that a "__builtin_unreachable();" after the "will_exit();" call can't be
> reached, it can simplify the generation of "f" - there is no need for a
> function epilogue, and that may also mean a simplified prologue.
It isn't about will_exit() being reached. It's about it returning: since it is
UB if it did, the compilers can reason it won't. And they do:
https://gcc.godbolt.org/z/vodMbj5x6
All four compilers (including the old Intel) generate the exact same code with
and without the __builtin_unreachable(). Now, ICC and MSVC generated a tail-
call optimisation, which would allow returning to work, but that happens to be
the most optimal code emission anyway.
> That can be with
> compiler flags (like sanitisers) or with "__builtin_trap();" (I see it
> in gcc - I expect clang has it too), or other implementation-specific
> features. (There will also be std::breakpoint() in C++26, which might
> be appropriate. But I don't know what that is supposed to do if you are
> not running with a debugger.)
I expect they will emit the debugging trap instruction, which on x86 is INT 3.
That happens to be what MSVC emits if you add the __assume(0) after the
will_exit() above, actually. If you execute this instruction, it will raise
the #BP (breakpoint) exception and if there's no debugger, that is delivered
to the application as a crash. On Unix systems, it'll be the SIGTRAP signal.
BTW, GCC and Clang emit the UD2 instruction __builtin_trap(), which results in
SIGILL. The old ICC emitted INT 5, which is unexpected... Boundary Range
exception?
> So I am fully in agreement that if you want a particular kind of defined
> behaviour, don't write UB and expect the compiler to read your mind.
> But I still think that sometimes it is possible for a compiler to make a
> guess at the developer's mind!
Indeed, and in those cases the compiler should also print a warning. It's the
famous case of -Wstrict-overflow with GCC.
Std-Discussion wrote:
> > That will_exit() function comes from some third-party library and isn't
> > marked [[noreturn]]. We know it won't return, so we don't want the
> > compiler to generate unnecessary code. So the author of this code needs
> > to insert a __builtin_unreachabble() to suppress the "control reaches end
> > of non-void function" warning. But note how there's no difference in code
> > generation: the __builtin simply suppresses the warning.
>
> There can be a difference in code generation - since the compiler knows
> that a "__builtin_unreachable();" after the "will_exit();" call can't be
> reached, it can simplify the generation of "f" - there is no need for a
> function epilogue, and that may also mean a simplified prologue.
It isn't about will_exit() being reached. It's about it returning: since it is
UB if it did, the compilers can reason it won't. And they do:
https://gcc.godbolt.org/z/vodMbj5x6
All four compilers (including the old Intel) generate the exact same code with
and without the __builtin_unreachable(). Now, ICC and MSVC generated a tail-
call optimisation, which would allow returning to work, but that happens to be
the most optimal code emission anyway.
> That can be with
> compiler flags (like sanitisers) or with "__builtin_trap();" (I see it
> in gcc - I expect clang has it too), or other implementation-specific
> features. (There will also be std::breakpoint() in C++26, which might
> be appropriate. But I don't know what that is supposed to do if you are
> not running with a debugger.)
I expect they will emit the debugging trap instruction, which on x86 is INT 3.
That happens to be what MSVC emits if you add the __assume(0) after the
will_exit() above, actually. If you execute this instruction, it will raise
the #BP (breakpoint) exception and if there's no debugger, that is delivered
to the application as a crash. On Unix systems, it'll be the SIGTRAP signal.
BTW, GCC and Clang emit the UD2 instruction __builtin_trap(), which results in
SIGILL. The old ICC emitted INT 5, which is unexpected... Boundary Range
exception?
> So I am fully in agreement that if you want a particular kind of defined
> behaviour, don't write UB and expect the compiler to read your mind.
> But I still think that sometimes it is possible for a compiler to make a
> guess at the developer's mind!
Indeed, and in those cases the compiler should also print a warning. It's the
famous case of -Wstrict-overflow with GCC.
-- Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org Principal Engineer - Intel Platform & System Engineering
Received on 2025-09-20 15:33:53