Skip to content

Commit f6d9e59

Browse files
authoredJan 29, 2024
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

+112
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

+2
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

+1
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

+2
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

+25
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

+2-2
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

+10-1
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
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

+1
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

+3
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>

‎PCbuild/build.bat

+3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ echo. overrides -c and -d
3636
echo. --disable-gil Enable experimental support for running without the GIL.
3737
echo. --test-marker Enable the test marker within the build.
3838
echo. --regen Regenerate all opcodes, grammar and tokens.
39+
echo. --experimental-jit Enable the experimental just-in-time compiler.
3940
echo.
4041
echo.Available flags to avoid building certain modules.
4142
echo.These flags have no effect if '-e' is not given:
@@ -85,6 +86,7 @@ if "%~1"=="--disable-gil" (set UseDisableGil=true) & shift & goto CheckOpts
8586
if "%~1"=="--test-marker" (set UseTestMarker=true) & shift & goto CheckOpts
8687
if "%~1"=="-V" shift & goto Version
8788
if "%~1"=="--regen" (set Regen=true) & shift & goto CheckOpts
89+
if "%~1"=="--experimental-jit" (set UseJIT=true) & shift & goto CheckOpts
8890
rem These use the actual property names used by MSBuild. We could just let
8991
rem them in through the environment, but we specify them on the command line
9092
rem anyway for visibility so set defaults after this
@@ -176,6 +178,7 @@ echo on
176178
/p:IncludeSSL=%IncludeSSL% /p:IncludeTkinter=%IncludeTkinter%^
177179
/p:DisableGil=%UseDisableGil%^
178180
/p:UseTestMarker=%UseTestMarker% %GITProperty%^
181+
/p:UseJIT=%UseJIT%^
179182
%1 %2 %3 %4 %5 %6 %7 %8 %9
180183

181184
@echo off

‎PCbuild/pythoncore.vcxproj

+3
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
<AdditionalIncludeDirectories Condition="$(IncludeExternals)">$(zlibDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
105105
<PreprocessorDefinitions>_USRDLL;Py_BUILD_CORE;Py_BUILD_CORE_BUILTIN;Py_ENABLE_SHARED;MS_DLL_ID="$(SysWinVer)";%(PreprocessorDefinitions)</PreprocessorDefinitions>
106106
<PreprocessorDefinitions Condition="$(IncludeExternals)">_Py_HAVE_ZLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
107+
<PreprocessorDefinitions Condition="'$(UseJIT)' == 'true'">_Py_JIT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
107108
</ClCompile>
108109
<Link>
109110
<AdditionalDependencies>version.lib;ws2_32.lib;pathcch.lib;bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
@@ -247,6 +248,7 @@
247248
<ClInclude Include="..\Include\internal\pycore_initconfig.h" />
248249
<ClInclude Include="..\Include\internal\pycore_interp.h" />
249250
<ClInclude Include="..\Include\internal\pycore_intrinsics.h" />
251+
<ClInclude Include="..\Include\internal\pycore_jit.h" />
250252
<ClInclude Include="..\Include\internal\pycore_list.h" />
251253
<ClInclude Include="..\Include\internal\pycore_llist.h" />
252254
<ClInclude Include="..\Include\internal\pycore_lock.h" />
@@ -585,6 +587,7 @@
585587
<ClCompile Include="..\Python\initconfig.c" />
586588
<ClCompile Include="..\Python\intrinsics.c" />
587589
<ClCompile Include="..\Python\instrumentation.c" />
590+
<ClCompile Include="..\Python\jit.c" />
588591
<ClCompile Include="..\Python\legacy_tracing.c" />
589592
<ClCompile Include="..\Python\lock.c" />
590593
<ClCompile Include="..\Python\marshal.c" />

‎PCbuild/pythoncore.vcxproj.filters

+6
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,9 @@
669669
<ClInclude Include="..\Include\internal\pycore_intrinsics.h">
670670
<Filter>Include\cpython</Filter>
671671
</ClInclude>
672+
<ClInclude Include="..\Include\internal\pycore_jit.h">
673+
<Filter>Include\internal</Filter>
674+
</ClInclude>
672675
<ClInclude Include="..\Include\internal\pycore_list.h">
673676
<Filter>Include\internal</Filter>
674677
</ClInclude>
@@ -1337,6 +1340,9 @@
13371340
<ClCompile Include="..\Python\instrumentation.c">
13381341
<Filter>Source Files</Filter>
13391342
</ClCompile>
1343+
<ClCompile Include="..\Python\jit.c">
1344+
<Filter>Python</Filter>
1345+
</ClCompile>
13401346
<ClCompile Include="..\Python\legacy_tracing.c">
13411347
<Filter>Source Files</Filter>
13421348
</ClCompile>

‎PCbuild/regen.targets

+22-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
</_TokenOutputs>
2929
<_KeywordSources Include="$(PySourcePath)Grammar\python.gram;$(PySourcePath)Grammar\Tokens" />
3030
<_KeywordOutputs Include="$(PySourcePath)Lib\keyword.py" />
31+
<!-- Taken from _Target._compute_digest in Tools\jit\_targets.py: -->
32+
<_JITSources Include="$(PySourcePath)Python\executor_cases.c.h;$(GeneratedPyConfigDir)pyconfig.h;$(PySourcePath)Tools\jit\**"/>
33+
<_JITOutputs Include="$(GeneratedPyConfigDir)jit_stencils.h"/>
3134
</ItemGroup>
3235

3336
<Target Name="_TouchRegenSources" Condition="$(ForceRegen) == 'true'">
@@ -76,10 +79,28 @@
7679
<Exec Command="$(PythonForBuild) Tools\build\generate_global_objects.py"
7780
WorkingDirectory="$(PySourcePath)" />
7881
</Target>
82+
83+
<Target Name="_RegenJIT"
84+
Condition="'$(UseJIT)' == 'true'"
85+
DependsOnTargets="_UpdatePyconfig;FindPythonForBuild"
86+
Inputs="@(_JITSources)"
87+
Outputs="@(_JITOutputs)">
88+
<PropertyGroup>
89+
<JITArgs Condition="$(Platform) == 'ARM64'">aarch64-pc-windows-msvc</JITArgs>
90+
<JITArgs Condition="$(Platform) == 'Win32'">i686-pc-windows-msvc</JITArgs>
91+
<JITArgs Condition="$(Platform) == 'x64'">x86_64-pc-windows-msvc</JITArgs>
92+
<JITArgs Condition="$(Configuration) == 'Debug'">$(JITArgs) --debug</JITArgs>
93+
</PropertyGroup>
94+
<Exec Command='$(PythonForBuild) "$(PySourcePath)Tools\jit\build.py" $(JITArgs)'
95+
WorkingDirectory="$(GeneratedPyConfigDir)"/>
96+
</Target>
7997

80-
<Target Name="Regen"
98+
<Target Name="_RegenNoPGUpdate"
8199
Condition="$(Configuration) != 'PGUpdate'"
82100
DependsOnTargets="_TouchRegenSources;_RegenPegen;_RegenAST_H;_RegenTokens;_RegenKeywords;_RegenGlobalObjects">
101+
</Target>
102+
103+
<Target Name="Regen" DependsOnTargets="_RegenNoPGUpdate;_RegenJIT">
83104
<Message Text="Generated sources are up to date" Importance="high" />
84105
</Target>
85106

0 commit comments

Comments
 (0)