Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-123714: Update JIT compilation to use LLVM 19 #124093

Closed
wants to merge 13 commits into from
Closed
10 changes: 7 additions & 3 deletions .github/workflows/jit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ jobs:
- true
- false
llvm:
- 18
- 19
include:
- target: i686-pc-windows-msvc/msvc
architecture: Win32
Expand Down Expand Up @@ -165,15 +165,19 @@ jobs:
name: Free-Threaded (Debug)
needs: interpreter
runs-on: ubuntu-latest
strategy:
matrix:
llvm:
- 19
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Build with JIT enabled and GIL disabled
run: |
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh 18
export PATH="$(llvm-config-18 --bindir):$PATH"
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ matrix.llvm }}
export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH"
./configure --enable-experimental-jit --with-pydebug --disable-gil
make all --jobs 4
- name: Run tests
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Update JIT compilation to use LLVM 19
21 changes: 9 additions & 12 deletions Tools/jit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,49 +7,46 @@ This version of CPython can be built with an experimental just-in-time compiler[

The JIT compiler does not require end users to install any third-party dependencies, but part of it must be *built* using LLVM[^why-llvm]. You are *not* required to build the rest of CPython using LLVM, or even the same version of LLVM (in fact, this is uncommon).

LLVM version 18 is required. Both `clang` and `llvm-readobj` need to be installed and discoverable (version suffixes, like `clang-18`, are okay). It's highly recommended that you also have `llvm-objdump` available, since this allows the build script to dump human-readable assembly for the generated code.
LLVM version 19 is required. Both `clang` and `llvm-readobj` need to be installed and discoverable (version suffixes, like `clang-19`, are okay). It's highly recommended that you also have `llvm-objdump` available, since this allows the build script to dump human-readable assembly for the generated code.

It's easy to install all of the required tools:

### Linux

Install LLVM 18 on Ubuntu/Debian:
Install LLVM 19 on Ubuntu/Debian:

```sh
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 18
sudo ./llvm.sh 19
```

Install LLVM 18 on Fedora Linux 40 or newer:
Install LLVM 19 on Fedora Linux 40 or newer:

```sh
sudo dnf install 'clang(major) = 18' 'llvm(major) = 18'
sudo dnf install 'clang(major) = 19' 'llvm(major) = 19'
```

### macOS

Install LLVM 18 with [Homebrew](https://brew.sh):
Install LLVM 19 with [Homebrew](https://brew.sh):

```sh
brew install llvm@18
brew install llvm@19
```

Homebrew won't add any of the tools to your `$PATH`. That's okay; the build script knows how to find them.

### Windows

Install LLVM 18 [by searching for it on LLVM's GitHub releases page](https://github.com/llvm/llvm-project/releases?q=18), clicking on "Assets", downloading the appropriate Windows installer for your platform (likely the file ending with `-win64.exe`), and running it. **When installing, be sure to select the option labeled "Add LLVM to the system PATH".**
Install LLVM 19 [by searching for it on LLVM's GitHub releases page](https://github.com/llvm/llvm-project/releases?q=19), clicking on "Assets", downloading the appropriate Windows installer for your platform (likely the file ending with `-win64.exe`), and running it. **When installing, be sure to select the option labeled "Add LLVM to the system PATH".**

Alternatively, you can use [chocolatey](https://chocolatey.org):

```sh
choco install llvm --version=18.1.6
choco install llvm --version=19.1.0
```

### Dev Containers

If you are working CPython in a [Codespaces instance](https://devguide.python.org/getting-started/setup-building/#using-codespaces), there's no need to install LLVM as the Fedora 40 base image includes LLVM 18 out of the box.

## Building

Expand Down
2 changes: 1 addition & 1 deletion Tools/jit/_llvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import subprocess
import typing

_LLVM_VERSION = 18
_LLVM_VERSION = 19
_LLVM_VERSION_PATTERN = re.compile(rf"version\s+{_LLVM_VERSION}\.\d+\.\d+\S*\s+")

_P = typing.ParamSpec("_P")
Expand Down
14 changes: 13 additions & 1 deletion Tools/jit/_stencils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import dataclasses
import enum
import sys
import typing

import _schema
Expand Down Expand Up @@ -132,15 +133,26 @@ class Hole:
def __post_init__(self) -> None:
self.func = _PATCH_FUNCS[self.kind]

def fold(self, other: typing.Self) -> typing.Self | None:
def fold(self, other: typing.Self, body: bytes) -> typing.Self | None:
"""Combine two holes into a single hole, if possible."""
instruction_a = int.from_bytes(
body[self.offset : self.offset + 4], byteorder=sys.byteorder
)
instruction_b = int.from_bytes(
body[other.offset : other.offset + 4], byteorder=sys.byteorder
)
reg_a = instruction_a & 0b11111
reg_b1 = instruction_b & 0b11111
reg_b2 = (instruction_b >> 5) & 0b11111

if (
self.offset + 4 == other.offset
and self.value == other.value
and self.symbol == other.symbol
and self.addend == other.addend
and self.func == "patch_aarch64_21rx"
and other.func == "patch_aarch64_12x"
and reg_a == reg_b1 == reg_b2
):
# These can *only* be properly relaxed when they appear together and
# patch the same value:
Expand Down
2 changes: 1 addition & 1 deletion Tools/jit/_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def _dump_stencil(opname: str, group: _stencils.StencilGroup) -> typing.Iterator
if skip:
skip = False
continue
if pair and (folded := hole.fold(pair)):
if pair and (folded := hole.fold(pair, stencil.body)):
skip = True
hole = folded
yield f" {hole.as_c(part)}"
Expand Down
Loading