(I probably mis-categorized this, please feel free to move)
llvm_unreachable
is being widely misused as an assertion, but in reality, its behavior is totally different depending on whether assertions are enabled. When asserts are enabled, it is an assertion. When asserts are disabled, it becomes an assumption, which is a serious departure from the typical behavior of assertions which is to merely get disabled. This misunderstanding traces back to its own comment as it says,
/// Use this instead of assert(0). It conveys intent more clearly and
/// allows compilers to omit some unnecessary code.
Iâm not denying that there are legitimate uses for llvm_unreachable
. But they are a too small minority (see next paragraphs) for such blanket statements to be useful. That is then further expanded on in the coding guide section on asserts which promotes llvm_unreachable
as âsomething much betterâ than assert(0)
without qualification.
Most people writing assertions have the mental modal that they are enforcing something: code past the assertion will be able to assume that the asserted condition has already been enforced.
But __builtin_unreachable
does the opposite: instead of enforcing, what it does is tell the compiler to assume that the condition has already been enforced. That may help the compiler generate better code (thus itâs an optimization) but at the cost of great unsafety if the assumption is ever defeated. That can only be the right trade-off of safety for performance in a small minority of performance-critical code.
Regarding the llvm_unreachable
macro specifically as opposed to __builtin_unreachable
, there is an additional concern: llvm_unreachable
is a macro that expands to one thing or its opposite (âenforceâ vs âassume previously enforcedâ) depending on whether asserts are enabled. I understand that can still make sense for some situations but the point is, that should be made explicit.
A quick code search shows llvm_unreachable
being widely used across llvm and several dependent projects in a way thatâs clearly not falling into that category. My own experience in code reviews is that reviewers will tell me to use llvm_unreachable
where an assertion is meant. Iâve also seen this idiom propagate in all sorts of projects that ex-LLVM developers join e.g. this is where the pandemic reached mozilla (it was overturned fairly quickly).
Iâm sure I donât need to tell here how unsafe optimizations a compiler can do when it is given a __builtin_unreachable
to work with. This Compiler Explorer playground shows for instance how a switch
jump table drops its bounds-check, and how an unexpected if
-branch is taken.
Potential action items:
- Fix the comments and the coding guide to make it clear that
llvm_unreachable
is only useful when performance is critical and one is willing to trade safety for it, or something like that. Remove blanket statements presenting it as âlike assert(0)â. - Consider renaming
llvm_unreachable
to something more explicit, possiblyllvm_assume_unreachable
. - If assert(0) is deemed unsatisfactory for some purpose, consider adding some new macro.