Skip to content

JIT build fails on macOS 26: due to .cfi directives #148560

Description

@DarkaMaul

Bug report

Bug description:

How to reproduce

CPython Commit: 4af46b4 (April 15)

./configure --enable-experimental-jit --with-pydebug
make

The following command fails with the following errors:

❯ make -j1
python3.15 ./Tools/jit/build.py aarch64-apple-darwin25.4.0 --output-dir . --pyconfig-dir . --cflags="" --llvm-version="" --debug
===========================================================
JIT support for aarch64-apple-darwin is still experimental!
Please report any issues you encounter.
===========================================================
/var/folders/m9/4q7skk_n3vj6k6bvy2vc4tnr0000gn/T/_BINARY_OP_ADD_FLOAT_INPLACE_RIGHT_r13-0ccf97.s:879:1: error: starting new .cfi frame before finishing the previous one
; %bb.0:
^
/var/folders/m9/4q7skk_n3vj6k6bvy2vc4tnr0000gn/T/_BINARY_OP_ADD_FLOAT_INPLACE_RIGHT_r13-0ccf97.s:890:1: error: starting new .cfi frame before finishing the previous one
; %bb.0:
^
/var/folders/m9/4q7skk_n3vj6k6bvy2vc4tnr0000gn/T/_BINARY_OP_ADD_FLOAT_INPLACE_RIGHT_r13-0ccf97.s:901:1: error: starting new .cfi frame before finishing the previous one
; %bb.0:
...
^
+ Exception Group Traceback (most recent call last):
| File "/Users/dm/Projects/cpython-upstream/Tools/jit/_targets.py", line 213, in _build_stencils
| async with asyncio.TaskGroup() as group:
| ~~~~~~~~~~~~~~~~~^^
| File "/Users/dm/.local/share/uv/python/cpython-3.15.0a7-macos-aarch64-none/lib/python3.15/asyncio/taskgroups.py", line 72, in __aexit__
| return await self._aexit(et, exc)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/Users/dm/.local/share/uv/python/cpython-3.15.0a7-macos-aarch64-none/lib/python3.15/asyncio/taskgroups.py", line 174, in _aexit
| raise BaseExceptionGroup(
| ...<2 lines>...
| ) from None
| ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "/Users/dm/Projects/cpython-upstream/Tools/jit/_targets.py", line 198, in _compile
| await _llvm.run(
| "clang", args_o, echo=self.verbose, llvm_version=self.llvm_version
| )
| File "/Users/dm/Projects/cpython-upstream/Tools/jit/_llvm.py", line 132, in run
| output = await maybe_run(tool, args, echo=echo, llvm_version=llvm_version)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/Users/dm/Projects/cpython-upstream/Tools/jit/_llvm.py", line 121, in maybe_run
| return path and await _run(path, args, echo=echo)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| File "/Users/dm/Projects/cpython-upstream/Tools/jit/_llvm.py", line 63, in _run
| raise RuntimeError(f"{tool} exited with return code {process.returncode}")
| RuntimeError: clang exited with return code 1
+------------------------------------
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/dm/Projects/cpython-upstream/./Tools/jit/build.py", line 55, in <module>
target.build(
~~~~~~~~~~~~^
comment=comment,
^^^^^^^^^^^^^^^^
force=args.force,
^^^^^^^^^^^^^^^^^
jit_stencils=args.output_dir / f"jit_stencils-{target.triple}.h",
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/Users/dm/Projects/cpython-upstream/Tools/jit/_targets.py", line 256, in build
stencil_groups = ASYNCIO_RUNNER.run(self._build_stencils())
File "/Users/dm/.local/share/uv/python/cpython-3.15.0a7-macos-aarch64-none/lib/python3.15/asyncio/runners.py", line 127, in run
return self._loop.run_until_complete(task)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "/Users/dm/.local/share/uv/python/cpython-3.15.0a7-macos-aarch64-none/lib/python3.15/asyncio/base_events.py", line 719, in run_until_complete
return future.result()
~~~~~~~~~~~~~^^
File "/Users/dm/Projects/cpython-upstream/Tools/jit/_targets.py", line 211, in _build_stencils
with tempfile.TemporaryDirectory() as tempdir:
~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
File "/Users/dm/.local/share/uv/python/cpython-3.15.0a7-macos-aarch64-none/lib/python3.15/tempfile.py", line 971, in __exit__
self.cleanup()
~~~~~~~~~~~~^^
File "/Users/dm/.local/share/uv/python/cpython-3.15.0a7-macos-aarch64-none/lib/python3.15/tempfile.py", line 975, in cleanup
self._rmtree(self.name, ignore_errors=self._ignore_cleanup_errors)
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/dm/.local/share/uv/python/cpython-3.15.0a7-macos-aarch64-none/lib/python3.15/tempfile.py", line 955, in _rmtree
_shutil.rmtree(name, onexc=onexc)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
File "/Users/dm/.local/share/uv/python/cpython-3.15.0a7-macos-aarch64-none/lib/python3.15/shutil.py", line 852, in rmtree
_rmtree_impl(path, dir_fd, onexc)
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
File "/Users/dm/.local/share/uv/python/cpython-3.15.0a7-macos-aarch64-none/lib/python3.15/shutil.py", line 721, in _rmtree_safe_fd
_rmtree_safe_fd_step(stack, onexc)
~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
File "/Users/dm/.local/share/uv/python/cpython-3.15.0a7-macos-aarch64-none/lib/python3.15/shutil.py", line 802, in _rmtree_safe_fd_step
onexc(func, path, err)
~~~~~^^^^^^^^^^^^^^^^^
File "/Users/dm/.local/share/uv/python/cpython-3.15.0a7-macos-aarch64-none/lib/python3.15/shutil.py", line 753, in _rmtree_safe_fd_step
os.rmdir(name, dir_fd=dirfd)
~~~~~~~~^^^^^^^^^^^^^^^^^^^^
OSError: [Errno 66] Directory not empty: '/var/folders/m9/4q7skk_n3vj6k6bvy2vc4tnr0000gn/T/tmpjlgkd9jf'
make: *** [jit_stencils-aarch64-apple-darwin.h] Error 1

Root cause

Warning: this analysis was performed by Claude, and seemed plausible, but that's not my area of expertise.

LLVM 21's machine outliner (enabled by -Os) generates _OUTLINED_FUNCTION_* thunks at the end of the assembly output. Each thunk has .cfi_startproc but no matching .cfi_endproc:

_OUTLINED_FUNCTION_1: ; @OUTLINED_FUNCTION_1 Thunk	.cfi_startproc; %bb.0:	...	b	___assert_rtn_OUTLINED_FUNCTION_2: ; @OUTLINED_FUNCTION_2 Thunk	.cfi_startproc ; <-- ERROR: previous .cfi frame never ended; %bb.0:	...

The Xcode 26 assembler (Apple clang 21.0.0 / clang-2100) is stricter about nested .cfi frames than previous versions. CPython CI passes because it runs on macOS 15 with an older Xcode.

Note that -fno-asynchronous-unwind-tables is already passed during compilation, so the .cfi directives in these outlined thunks serve no purpose — the outliner just emits them unconditionally.

Environnement

  • macOS 26.4.1 (Darwin 25.4.0), aarch64
  • Xcode 26.4
  • LLVM 21.1.8 via Homebrew (brew install llvm@21)

Patch

Applying the following (Claude-generated) patch worked on my computer:

--- a/Tools/jit/_targets.py+++ b/Tools/jit/_targets.py@@ -167,6 +167,9 @@
# nicer debugging experience... but that needs a lot more research:
"-fno-asynchronous-unwind-tables",
+ # The machine outliner can generate outlined thunks with+ # .cfi_startproc but no .cfi_endproc, causing assembler errors+ # on newer toolchains (e.g. Xcode 26 / macOS 26):+ "-mno-outline",
# Don't call built-in functions that we can't find or patch:
"-fno-builtin",

Relevant issues

CPython versions tested on:

CPython main branch

Operating systems tested on:

macOS

Linked PRs

Metadata

Metadata

Labels

OS-macbuildThe build process and cross-buildtopic-JITtype-bugAn unexpected behavior, bug, or error

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions