Skip to content

Commit ffc0305

Browse files
committed
nlrx86: Fix for 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: micropython#17415 Signed-off-by: Jeff Epler <jepler@unpythonic.net>
1 parent 8a56be6 commit ffc0305

1 file changed

Lines changed: 25 additions & 14 deletions

File tree

py/nlrx86.c

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,24 +39,37 @@ unsigned int nlr_push_tail(nlr_buf_t *nlr) asm ("nlr_push_tail");
3939
__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr);
4040
#endif
4141

42+
4243
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 8
43-
// Since gcc 8.0 the naked attribute is supported
44-
#define USE_NAKED (1)
45-
#define UNDO_PRELUDE (0)
44+
// Since gcc 8.0 the naked and no-sanitize attributes are supported
45+
#define NLR_PUSH_ATTRIBUTE __attribute__((naked, no_sanitize("unreachable")))
46+
#define UNDO_PRELUDE (0)
47+
#define ARG_USED(x) (void)x;
48+
#define NLR_UNREACHABLE __builtin_unreachable();
4649
#elif defined(__ZEPHYR__) || defined(__ANDROID__)
4750
// Zephyr and Android use a different calling convention by default
48-
#define USE_NAKED (0)
49-
#define UNDO_PRELUDE (0)
51+
#define NLR_PUSH_ATTRIBUTE /* NOTHING */
52+
#define UNDO_PRELUDE (0)
53+
#define ARG_USED(x) (void)x;
54+
#define NLR_UNREACHABLE return 0;
55+
#elif defined(__clang__)
56+
// clang on Ubuntu 24.04 enables -fsanitize=unreachable by default, but this
57+
// destroys the content of the ebx register.
58+
#define NLR_PUSH_ATTRIBUTE __attribute__((naked, no_sanitize("unreachable")))
59+
#define UNDO_PRELUDE (0)
60+
#define ARG_USED(x) /* NOTHING */
61+
#define NLR_UNREACHABLE /* NOTHING */
5062
#else
51-
#define USE_NAKED (0)
52-
#define UNDO_PRELUDE (1)
63+
// gcc before 8 unavoidably emits a 'push %ebp' prologue instruction
64+
#define NLR_PUSH_ATTRIBUTE /* NOTHING */
65+
#define UNDO_PRELUDE (1)
66+
#define ARG_USED(x) (void)x;
67+
#define NLR_UNREACHABLE return 0;
5368
#endif
5469

55-
#if USE_NAKED
56-
__attribute__((naked))
57-
#endif
70+
NLR_PUSH_ATTRIBUTE
5871
unsigned int nlr_push(nlr_buf_t *nlr) {
59-
(void)nlr;
72+
ARG_USED(nlr)
6073

6174
__asm volatile (
6275
#if UNDO_PRELUDE
@@ -73,9 +86,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) {
7386
"jmp nlr_push_tail \n" // do the rest in C
7487
);
7588

76-
#if !USE_NAKED
77-
return 0; // needed to silence compiler warning
78-
#endif
89+
NLR_UNREACHABLE
7990
}
8091

8192
MP_NORETURN void nlr_jump(void *val) {

0 commit comments

Comments
 (0)