nlrx86: Fix for Clang 19. by jepler · Pull Request #19119 · micropython/micropython · GitHub
Skip to content

nlrx86: Fix for Clang 19.#19119

Open
jepler wants to merge 1 commit intomicropython:masterfrom
jepler:clang-x86-nlr
Open

nlrx86: Fix for Clang 19.#19119
jepler wants to merge 1 commit intomicropython:masterfrom
jepler:clang-x86-nlr

Conversation

@jepler
Copy link
Copy Markdown
Contributor

@jepler jepler commented Apr 16, 2026

Summary

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. Clang does not accept __builtin_unreachable or return 0; here, so it must emit nothing.

Closes: #17415

Testing

Locally, I ran make clean && make CC="clang -m32 -g" -j12 test//basic test//native in ports/unix.

Trade-offs and Alternatives

This combination is not ci-tested. A ci-time test could be added, perhaps nanbox or just a dupe of something else like standard.

Generative AI

I did not use generative AI tools when creating this PR.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 16, 2026

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 16, 2026

Code size report:

Reference:  samd: Convert port to use new event waiting functions. [1f601e8]
Comparison: nlrx86: Fix for Clang 19. [merge of b5a2cd9]
  mpy-cross:    +0 +0.000% 
   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32:    +0 +0.000% PYBV10
      esp32:    +0 +0.000% ESP32_GENERIC
     mimxrt:    +0 +0.000% TEENSY40
        rp2:    +0 +0.000% RPI_PICO_W
       samd:    +0 +0.000% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:    +0 +0.000% VIRT_RV32

@dpgeorge
Copy link
Copy Markdown
Member

No idea why, but there are failures in CI:

6 tests failed: micropython/native_gen.py micropython/native_try.py micropython/native_try_deep.py micropython/native_with.py micropython/viper_try.py micropython/viper_with.py

Maybe it's the use of __builtin_unreachable()? That's the only change to the gcc bit.

@dpgeorge dpgeorge added the py-core Relates to py/ directory in source label Apr 17, 2026
@jepler
Copy link
Copy Markdown
Contributor Author

jepler commented Apr 17, 2026

Well that was interesting.

On Ubuntu 24.04 with gcc version 13.3.0 (Ubuntu 13.3.0-6ubuntu2~24.04.1) (but not on godbolt 13.3.0), using __builtin_unreachable() causes the following to be emitted:

00000000 <nlr_push>:
   0:	e8 fc ff ff ff       	call   1 <nlr_push+0x1>
			1: R_386_PC32	__x86.get_pc_thunk.bx
   5:	81 c3 02 00 00 00    	add    $0x2,%ebx
			7: R_386_GOTPC	_GLOBAL_OFFSET_TABLE_
   b:	8b 54 24 04          	mov    0x4(%esp),%edx
   f:	8b 04 24             	mov    (%esp),%eax
  12:	89 42 08             	mov    %eax,0x8(%edx)
  15:	89 6a 0c             	mov    %ebp,0xc(%edx)
  18:	89 62 10             	mov    %esp,0x10(%edx)
  1b:	89 5a 14             	mov    %ebx,0x14(%edx)
  1e:	89 7a 18             	mov    %edi,0x18(%edx)
  21:	89 72 1c             	mov    %esi,0x1c(%edx)
  24:	e9 fc ff ff ff       	jmp    25 <nlr_push+0x25>
			25: R_386_PC32	nlr_push_tail
  29:	83 ec 0c             	sub    $0xc,%esp
  2c:	8d 83 00 00 00 00    	lea    0x0(%ebx),%eax
			2e: R_386_GOTOFF	.data.rel.local..Lubsan_data0
  32:	50                   	push   %eax
  33:	e8 fc ff ff ff       	call   34 <nlr_push+0x34>
			34: R_386_PLT32	__ubsan_handle_builtin_unreachable

that is, rather than emitting nothing for __builtin_unreachable(), this gcc is emitting a call to __ubsan_handle_builtin_unreachable.

In order to make this happen, a call to __x86.get_pc_thunk.bx is added, which destroys the contents of the bx register. This doesn't usually matter (it's a caller-saved register in the standard calling convention), but it DOES matter for native code, which I guess purposely does not adhere to x86 calling convention when calling nlr_push.

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>
@jepler
Copy link
Copy Markdown
Contributor Author

jepler commented Apr 17, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

py-core Relates to py/ directory in source

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Native & viper failures with setjmp exception handling

2 participants