Skip to content

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

@DarkaMaul

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions