[PATCH] D121750: Add a cmake flag to turn `llvm_unreachable()` into builtin_trap() when assertions are disabled
Mehdi AMINI via Phabricator via llvm-commits
llvm-commits at lists.llvm.org
Mon Mar 21 13:00:56 PDT 2022
mehdi_amini added a comment.
In D121750#3397382 <https://reviews.llvm.org/D121750#3397382>, @dexonsmith wrote:
> I suggest:
>
> #ifndef NDEBUG
> #define llvm_unreachable(msg) \
> ::llvm::llvm_unreachable_internal(msg, __FILE__, __LINE__)
> #elif !defined(LLVM_BUILTIN_UNREACHABLE)
> #define llvm_unreachable(msg) ::llvm::llvm_unreachable_internal()
> #elif LLVM_UNREACHABLE_OPTIMIZE
> #define llvm_unreachable(msg) LLVM_BUILTIN_UNREACHABLE
> #else
> #define llvm_unreachable(msg) LLVM_BUILTIN_TRAP, LLVM_BUILTIN_UNREACHABLE
> #endif
>
> I *think* this will have all the intended effects:
>
> - If asserts are off, use the function (like a fancy assertion).
> - Else, if there is no LLVM_BUILTIN_UNREACHABLE, call the function without args (works again as of https://reviews.llvm.org/D122167).
> - Else, always call LLVM_BUILTIN_UNREACHABLE to suppress warnings about this control flow path...
> - ... but if !LLVM_UNREACHABLE_OPTIMIZE, first call LLVM_BUILTIN_TRAP to block optimizations.
> - ... and if LLVM_BUILTIN_TRAP happens to be marked no-return, the extra LLVM_BUILTIN_UNREACHABLE should get cleaned up by the optimizer.
LG: https://reviews.llvm.org/D122170
================
Comment at: llvm/include/llvm/Support/ErrorHandling.h:144
+#define llvm_unreachable(msg) LLVM_BUILTIN_TRAP
#elif defined(LLVM_BUILTIN_UNREACHABLE)
#define llvm_unreachable(msg) LLVM_BUILTIN_UNREACHABLE
----------------
dexonsmith wrote:
> dexonsmith wrote:
> > mehdi_amini wrote:
> > > dexonsmith wrote:
> > > > Just noticed that this pre-existing logic was wrong.
> > > >
> > > > LLVM_BUILTIN_UNREACHABLE is defined whenever llvm/Support/Compiler.h is included:
> > > > ```
> > > > lang=c++
> > > > #if __has_builtin(__builtin_unreachable) || defined(__GNUC__)
> > > > # define LLVM_BUILTIN_UNREACHABLE __builtin_unreachable()
> > > > #elif defined(_MSC_VER)
> > > > # define LLVM_BUILTIN_UNREACHABLE __assume(false)
> > > > #else
> > > > # define LLVM_BUILTIN_UNREACHABLE
> > > > #endif
> > > > ```
> > > > That probably didn't used to be true.
> > > >
> > > > I think the intent of this code is to check whether `LLVM_BUILTIN_UNREACHABLE` is empty. If the Compiler.h logic exports another variable that can be used for detecting that, this could become:
> > > > ```
> > > > lang=c++
> > > > #ifndef NDEBUG
> > > > #define llvm_unreachable(msg) \
> > > > ::llvm::llvm_unreachable_internal(msg, __FILE__, __LINE__)
> > > > #elif LLVM_BUILTIN_UNREACHABLE_NON_EMPTY
> > > > #define llvm_unreachable(msg) LLVM_BUILTIN_UNREACHABLE
> > > > #else
> > > > #define llvm_unreachable(msg) ::llvm::llvm_unreachable_internal()
> > > > #endif
> > > > ```
> > > > (before your patch)
> > > >
> > > I wonder if just:
> > >
> > > ```
> > > #ifndef NDEBUG
> > > #define llvm_unreachable(msg) \
> > > ::llvm::llvm_unreachable_internal(msg, __FILE__, __LINE__)
> > > #elif!LLVM_UNREACHABLE_OPTIMIZE
> > > #define llvm_unreachable(msg) LLVM_BUILTIN_UNREACHABLE
> > > #else
> > > #define llvm_unreachable(msg) LLVM_BUILTIN_TRAP
> > > #endif
> > > ```
> > >
> > > Wouldn't be enough? Yes it would accounts to "nothing" when LLVM_BUILTIN_UNREACHABLE is empty, but that seems just correct and maybe even desirable: when LLVM_UNREACHABLE_OPTIMIZE is enabled "do nothing"
> > > seems closer to the behavior we're looking for rather than calling `llvm_unreachable_internal()` right?
> > >
> > Did you mean:
> > ```
> > lang=c++
> > #ifndef NDEBUG
> > #define llvm_unreachable(msg) \
> > ::llvm::llvm_unreachable_internal(msg, __FILE__, __LINE__)
> > #elif LLVM_UNREACHABLE_OPTIMIZE
> > #define llvm_unreachable(msg) LLVM_BUILTIN_UNREACHABLE
> > #else
> > #define llvm_unreachable(msg) LLVM_BUILTIN_TRAP
> > #endif
> > ```
> > ? (Or maybe I'm reading it backwards)
> >
> >
> > Regardless, one of the important pieces of `llvm_unreachable()` is that it suppresses diagnostics about the control path it's on. If it expands to "empty"/nothing then it won't suppress diagnostics.
> >
> > I think the behaviour here changed accidentally. @aeubanks updated LLVM_BUILTIN_UNREACHABLE as a drive-by in 26827337dff26ba3450721f880d4c6caaf2a8219 / https://reviews.llvm.org/D111581:
> > ```
> > -#if __has_builtin(__builtin_unreachable) || LLVM_GNUC_PREREQ(4, 5, 0)
> > +#if __has_builtin(__builtin_unreachable) || defined(__GNUC__)
> > # define LLVM_BUILTIN_UNREACHABLE __builtin_unreachable()
> > #elif defined(_MSC_VER)
> > # define LLVM_BUILTIN_UNREACHABLE __assume(false)
> > +#else
> > +# define LLVM_BUILTIN_UNREACHABLE
> > #endif
> > ```
> > > Add a missing #endif for LLVM_ATTRIBUTE_UNREACHABLE.
> >
> > Before that, the fallback behaviour was to hit the `#else` case here so that `llvm_unreachable()` always expanded to something that was no-return.
> >
> > Maybe we should revert that part of 26827337dff26ba3450721f880d4c6caaf2a8219 patch before landing this.
> Done in https://reviews.llvm.org/D122167 / 892c104fb71b86dc6399e36a82ae920e00ccae17.
> Regardless, one of the important pieces of llvm_unreachable() is that it suppresses diagnostics about the control path it's on. If it expands to "empty"/nothing then it won't suppress diagnostics.
Ah I forgot about this. That said: when you encounter a toolchain that does not support a proper "unreachable", that would just mean they don't have a way to suppress the diagnostics and have to live with warnings. For the `*(volatile int*)0x11 = 0`, you can wrap it in a macro with a noreturn attribute: `[]() __attribute__((__noreturn__)) { *(volatile int*)0x11 = 0; }();` (the same may apply to Windows debugbreak() builtin by the way).
Anyway I think your solution will work just fine!
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D121750/new/
https://reviews.llvm.org/D121750
More information about the llvm-commits
mailing list