py/nlrx86: Fix nlr_push to build with Clang 19.

This version is believed to work from Clang 3.0 to 22.1.0
(all versions on godbolt at the time of writing).

Clang rejects the `(void)x;` notation for a used variable
in a naked asm function, so do this only conditionally.

Introduces use of `__builtin_unreachable()` with gcc.
This saves 1 byte by causing gcc not to emit an `ud2` opcode
at the end. However, the unreachable sanitizer (enabled by
default(!) on Ubuntu 24.04 with gcc version 13.3.0) corrupts
the ebx register, so it must be disabled.

Clang does not accept `__builtin_unreachable`
or `return 0;` here, UNREACHABLE must expand to nothing.

Closes: #17415

Signed-off-by: Jeff Epler <jepler@unpythonic.net>
This commit is contained in:
Jeff Epler
2026-04-16 13:48:08 -05:00
committed by Damien George
parent d6a1d37156
commit a254bbca79
+24 -14
View File
@@ -40,23 +40,35 @@ __attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr);
#endif
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 8
// Since gcc 8.0 the naked attribute is supported
#define USE_NAKED (1)
#define UNDO_PRELUDE (0)
// Since gcc 8.0 the naked and no-sanitize attributes are supported
#define NLR_PUSH_ATTRIBUTE __attribute__((naked, no_sanitize("unreachable")))
#define UNDO_PRELUDE (0)
#define ARG_USED(x) (void)x;
#define NLR_UNREACHABLE __builtin_unreachable();
#elif defined(__ZEPHYR__) || defined(__ANDROID__)
// Zephyr and Android use a different calling convention by default
#define USE_NAKED (0)
#define UNDO_PRELUDE (0)
#define NLR_PUSH_ATTRIBUTE /* NOTHING */
#define UNDO_PRELUDE (0)
#define ARG_USED(x) (void)x;
#define NLR_UNREACHABLE return 0;
#elif defined(__clang__)
// clang on Ubuntu 24.04 enables -fsanitize=unreachable by default, but this
// destroys the content of the ebx register.
#define NLR_PUSH_ATTRIBUTE __attribute__((naked, no_sanitize("unreachable")))
#define UNDO_PRELUDE (0)
#define ARG_USED(x) /* NOTHING */
#define NLR_UNREACHABLE /* NOTHING */
#else
#define USE_NAKED (0)
#define UNDO_PRELUDE (1)
// gcc before 8 unavoidably emits a 'push %ebp' prologue instruction
#define NLR_PUSH_ATTRIBUTE /* NOTHING */
#define UNDO_PRELUDE (1)
#define ARG_USED(x) (void)x;
#define NLR_UNREACHABLE return 0;
#endif
#if USE_NAKED
__attribute__((naked))
#endif
NLR_PUSH_ATTRIBUTE
unsigned int nlr_push(nlr_buf_t *nlr) {
(void)nlr;
ARG_USED(nlr)
__asm volatile (
#if UNDO_PRELUDE
@@ -73,9 +85,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) {
"jmp nlr_push_tail \n" // do the rest in C
);
#if !USE_NAKED
return 0; // needed to silence compiler warning
#endif
NLR_UNREACHABLE
}
MP_NORETURN void nlr_jump(void *val) {