Skip to content

Commit f6d9e59

Browse files
authored
GH-113464: Add a JIT backend for tier 2 (GH-113465)
Add an option (--enable-experimental-jit for configure-based builds or --experimental-jit for PCbuild-based ones) to build an *experimental* just-in-time compiler, based on copy-and-patch (https://fredrikbk.com/publications/copy-and-patch.pdf). See Tools/jit/README.md for more information on how to install the required build-time tooling.
1 parent f7c05d7 commit f6d9e59

29 files changed

+1738
-5
lines changed

.github/workflows/jit.yml

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
name: JIT
2+
on:
3+
pull_request:
4+
paths: '**jit**'
5+
push:
6+
paths: '**jit**'
7+
workflow_dispatch:
8+
jobs:
9+
jit:
10+
name: ${{ matrix.target }} (${{ matrix.debug && 'Debug' || 'Release' }})
11+
runs-on: ${{ matrix.runner }}
12+
strategy:
13+
fail-fast: false
14+
matrix:
15+
target:
16+
- i686-pc-windows-msvc/msvc
17+
- x86_64-pc-windows-msvc/msvc
18+
- x86_64-apple-darwin/clang
19+
- x86_64-unknown-linux-gnu/gcc
20+
- x86_64-unknown-linux-gnu/clang
21+
- aarch64-unknown-linux-gnu/gcc
22+
- aarch64-unknown-linux-gnu/clang
23+
debug:
24+
- true
25+
- false
26+
llvm:
27+
- 16
28+
include:
29+
- target: i686-pc-windows-msvc/msvc
30+
architecture: Win32
31+
runner: windows-latest
32+
compiler: msvc
33+
- target: x86_64-pc-windows-msvc/msvc
34+
architecture: x64
35+
runner: windows-latest
36+
compiler: msvc
37+
- target: x86_64-apple-darwin/clang
38+
architecture: x86_64
39+
runner: macos-latest
40+
compiler: clang
41+
exclude: test_embed
42+
- target: x86_64-unknown-linux-gnu/gcc
43+
architecture: x86_64
44+
runner: ubuntu-latest
45+
compiler: gcc
46+
- target: x86_64-unknown-linux-gnu/clang
47+
architecture: x86_64
48+
runner: ubuntu-latest
49+
compiler: clang
50+
- target: aarch64-unknown-linux-gnu/gcc
51+
architecture: aarch64
52+
runner: ubuntu-latest
53+
compiler: gcc
54+
# These fail because of emulation, not because of the JIT:
55+
exclude: test_unix_events test_init test_process_pool test_shutdown test_multiprocessing_fork test_cmd_line test_faulthandler test_os test_perf_profiler test_posix test_signal test_socket test_subprocess test_threading test_venv
56+
- target: aarch64-unknown-linux-gnu/clang
57+
architecture: aarch64
58+
runner: ubuntu-latest
59+
compiler: clang
60+
# These fail because of emulation, not because of the JIT:
61+
exclude: test_unix_events test_init test_process_pool test_shutdown test_multiprocessing_fork test_cmd_line test_faulthandler test_os test_perf_profiler test_posix test_signal test_socket test_subprocess test_threading test_venv
62+
env:
63+
CC: ${{ matrix.compiler }}
64+
steps:
65+
- uses: actions/checkout@v4
66+
- uses: actions/setup-python@v5
67+
with:
68+
python-version: '3.11'
69+
70+
- name: Windows
71+
if: runner.os == 'Windows'
72+
run: |
73+
choco install llvm --allow-downgrade --no-progress --version ${{ matrix.llvm }}
74+
./PCbuild/build.bat --experimental-jit ${{ matrix.debug && '-d' || '--pgo' }} -p ${{ matrix.architecture }}
75+
./PCbuild/rt.bat ${{ matrix.debug && '-d' }} -p ${{ matrix.architecture }} -q --exclude ${{ matrix.exclude }} --multiprocess 0 --timeout 3600 --verbose2 --verbose3
76+
77+
- name: macOS
78+
if: runner.os == 'macOS'
79+
run: |
80+
brew install llvm@${{ matrix.llvm }}
81+
export SDKROOT="$(xcrun --show-sdk-path)"
82+
./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations --with-lto' }}
83+
make all --jobs 3
84+
./python.exe -m test --exclude ${{ matrix.exclude }} --multiprocess 0 --timeout 3600 --verbose2 --verbose3
85+
86+
- name: Native Linux
87+
if: runner.os == 'Linux' && matrix.architecture == 'x86_64'
88+
run: |
89+
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ matrix.llvm }}
90+
export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH"
91+
./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations --with-lto' }}
92+
make all --jobs 4
93+
./python -m test --exclude ${{ matrix.exclude }} --multiprocess 0 --timeout 3600 --verbose2 --verbose3
94+
- name: Emulated Linux
95+
if: runner.os == 'Linux' && matrix.architecture != 'x86_64'
96+
run: |
97+
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ matrix.llvm }}
98+
export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH"
99+
./configure --prefix="$(pwd)/../build"
100+
make install --jobs 4
101+
make clean --jobs 4
102+
export HOST=${{ matrix.architecture }}-linux-gnu
103+
sudo apt install --yes "gcc-$HOST" qemu-user
104+
${{ !matrix.debug && matrix.compiler == 'clang' && './configure --enable-optimizations' || '' }}
105+
${{ !matrix.debug && matrix.compiler == 'clang' && 'make profile-run-stamp --jobs 4' || '' }}
106+
export CC="${{ matrix.compiler == 'clang' && 'clang --target=$HOST' || '$HOST-gcc' }}"
107+
export CPP="$CC --preprocess"
108+
export HOSTRUNNER=qemu-${{ matrix.architecture }}
109+
export QEMU_LD_PREFIX="/usr/$HOST"
110+
./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations --with-lto' }} --build=x86_64-linux-gnu --host="$HOST" --with-build-python=../build/bin/python3 --with-pkg-config=no ac_cv_buggy_getaddrinfo=no ac_cv_file__dev_ptc=no ac_cv_file__dev_ptmx=yes
111+
make all --jobs 4
112+
./python -m test --exclude ${{ matrix.exclude }} --multiprocess 0 --timeout 3600 --verbose2 --verbose3

.github/workflows/mypy.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ on:
1212
- "Tools/build/generate_sbom.py"
1313
- "Tools/cases_generator/**"
1414
- "Tools/clinic/**"
15+
- "Tools/jit/**"
1516
- "Tools/peg_generator/**"
1617
- "Tools/requirements-dev.txt"
1718
- "Tools/wasm/**"
@@ -38,6 +39,7 @@ jobs:
3839
"Tools/build/",
3940
"Tools/cases_generator",
4041
"Tools/clinic",
42+
"Tools/jit",
4143
"Tools/peg_generator",
4244
"Tools/wasm",
4345
]

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ Tools/unicode/data/
126126
# hendrikmuhs/ccache-action@v1
127127
/.ccache
128128
/cross-build/
129+
/jit_stencils.h
129130
/platform
130131
/profile-clean-stamp
131132
/profile-run-stamp

Include/cpython/optimizer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ typedef struct {
3939
typedef struct _PyExecutorObject {
4040
PyObject_VAR_HEAD
4141
_PyVMData vm_data; /* Used by the VM, but opaque to the optimizer */
42+
void *jit_code;
43+
size_t jit_size;
4244
_PyUOpInstruction trace[1];
4345
} _PyExecutorObject;
4446

Include/internal/pycore_jit.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#ifndef Py_INTERNAL_JIT_H
2+
#define Py_INTERNAL_JIT_H
3+
4+
#ifdef __cplusplus
5+
extern "C" {
6+
#endif
7+
8+
#ifndef Py_BUILD_CORE
9+
# error "this header requires Py_BUILD_CORE define"
10+
#endif
11+
12+
#ifdef _Py_JIT
13+
14+
typedef _Py_CODEUNIT *(*jit_func)(_PyInterpreterFrame *frame, PyObject **stack_pointer, PyThreadState *tstate);
15+
16+
int _PyJIT_Compile(_PyExecutorObject *executor, _PyUOpInstruction *trace, size_t length);
17+
void _PyJIT_Free(_PyExecutorObject *executor);
18+
19+
#endif // _Py_JIT
20+
21+
#ifdef __cplusplus
22+
}
23+
#endif
24+
25+
#endif // !Py_INTERNAL_JIT_H

Include/internal/pycore_object.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ _Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
178178
}
179179
_Py_DECREF_STAT_INC();
180180
#ifdef Py_REF_DEBUG
181-
_Py_DEC_REFTOTAL(_PyInterpreterState_GET());
181+
_Py_DEC_REFTOTAL(PyInterpreterState_Get());
182182
#endif
183183
if (--op->ob_refcnt != 0) {
184184
assert(op->ob_refcnt > 0);
@@ -199,7 +199,7 @@ _Py_DECREF_NO_DEALLOC(PyObject *op)
199199
}
200200
_Py_DECREF_STAT_INC();
201201
#ifdef Py_REF_DEBUG
202-
_Py_DEC_REFTOTAL(_PyInterpreterState_GET());
202+
_Py_DEC_REFTOTAL(PyInterpreterState_Get());
203203
#endif
204204
op->ob_refcnt--;
205205
#ifdef Py_DEBUG

Makefile.pre.in

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@ PYTHON_OBJS= \
433433
Python/initconfig.o \
434434
Python/instrumentation.o \
435435
Python/intrinsics.o \
436+
Python/jit.o \
436437
Python/legacy_tracing.o \
437438
Python/lock.o \
438439
Python/marshal.o \
@@ -1365,7 +1366,7 @@ regen-unicodedata:
13651366
regen-all: regen-cases regen-typeslots \
13661367
regen-token regen-ast regen-keyword regen-sre regen-frozen \
13671368
regen-pegen-metaparser regen-pegen regen-test-frozenmain \
1368-
regen-test-levenshtein regen-global-objects regen-sbom
1369+
regen-test-levenshtein regen-global-objects regen-sbom regen-jit
13691370
@echo
13701371
@echo "Note: make regen-stdlib-module-names, make regen-limited-abi, "
13711372
@echo "make regen-configure and make regen-unicodedata should be run manually"
@@ -1846,6 +1847,7 @@ PYTHON_HEADERS= \
18461847
$(srcdir)/Include/internal/pycore_initconfig.h \
18471848
$(srcdir)/Include/internal/pycore_interp.h \
18481849
$(srcdir)/Include/internal/pycore_intrinsics.h \
1850+
$(srcdir)/Include/internal/pycore_jit.h \
18491851
$(srcdir)/Include/internal/pycore_list.h \
18501852
$(srcdir)/Include/internal/pycore_llist.h \
18511853
$(srcdir)/Include/internal/pycore_lock.h \
@@ -2641,6 +2643,12 @@ config.status: $(srcdir)/configure
26412643
Python/asm_trampoline.o: $(srcdir)/Python/asm_trampoline.S
26422644
$(CC) -c $(PY_CORE_CFLAGS) -o $@ $<
26432645

2646+
Python/jit.o: regen-jit
2647+
2648+
.PHONY: regen-jit
2649+
regen-jit:
2650+
@REGEN_JIT_COMMAND@
2651+
26442652
# Some make's put the object file in the current directory
26452653
.c.o:
26462654
$(CC) -c $(PY_CORE_CFLAGS) -o $@ $<
@@ -2733,6 +2741,7 @@ clean-retain-profile: pycremoval
27332741
-rm -f Python/deepfreeze/*.[co]
27342742
-rm -f Python/frozen_modules/*.h
27352743
-rm -f Python/frozen_modules/MANIFEST
2744+
-rm -f jit_stencils.h
27362745
-find build -type f -a ! -name '*.gc??' -exec rm -f {} ';'
27372746
-rm -f Include/pydtrace_probes.h
27382747
-rm -f profile-gen-stamp
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Add an option (``--enable-experimental-jit`` for ``configure``-based builds
2+
or ``--experimental-jit`` for ``PCbuild``-based ones) to build an
3+
*experimental* just-in-time compiler, based on `copy-and-patch
4+
<https://fredrikbk.com/publications/copy-and-patch.pdf>`_

PCbuild/_freeze_module.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@
224224
<ClCompile Include="..\Python\initconfig.c" />
225225
<ClCompile Include="..\Python\intrinsics.c" />
226226
<ClCompile Include="..\Python\instrumentation.c" />
227+
<ClCompile Include="..\Python\jit.c" />
227228
<ClCompile Include="..\Python\legacy_tracing.c" />
228229
<ClCompile Include="..\Python\lock.c" />
229230
<ClCompile Include="..\Python\marshal.c" />

PCbuild/_freeze_module.vcxproj.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,9 @@
250250
<ClCompile Include="..\Objects\iterobject.c">
251251
<Filter>Source Files</Filter>
252252
</ClCompile>
253+
<ClCompile Include="..\Python\jit.c">
254+
<Filter>Source Files</Filter>
255+
</ClCompile>
253256
<ClCompile Include="..\Objects\listobject.c">
254257
<Filter>Source Files</Filter>
255258
</ClCompile>

0 commit comments

Comments
 (0)