gh-118335: Configure Tier 2 interpreter at build time (#118339)

The code for Tier 2 is now only compiled when configured
with `--enable-experimental-jit[=yes|interpreter]`.

We drop support for `PYTHON_UOPS` and -`Xuops`,
but you can disable the interpreter or JIT
at runtime by setting `PYTHON_JIT=0`.
You can also build it without enabling it by default
using `--enable-experimental-jit=yes-off`;
enable with `PYTHON_JIT=1`.

On Windows, the `build.bat` script supports
`--experimental-jit`, `--experimental-jit-off`,
`--experimental-interpreter`.

In the C code, `_Py_JIT` is defined as before
when the JIT is enabled; the new variable
`_Py_TIER2` is defined when the JIT *or* the
interpreter is enabled. It is actually a bitmask:
1: JIT; 2: default-off; 4: interpreter.
This commit is contained in:
Guido van Rossum 2024-04-30 18:26:34 -07:00 committed by GitHub
parent 9c468e2c5d
commit 7d83f7bcc4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 181 additions and 42 deletions

View File

@ -888,7 +888,7 @@ Experimental JIT Compiler
========================= =========================
When CPython is configured using the ``--enable-experimental-jit`` option, When CPython is configured using the ``--enable-experimental-jit`` option,
a just-in-time compiler is added which can speed up some Python programs. a just-in-time compiler is added which may speed up some Python programs.
The internal architecture is roughly as follows. The internal architecture is roughly as follows.
@ -905,19 +905,35 @@ The internal architecture is roughly as follows.
before it is interpreted or translated to machine code. before it is interpreted or translated to machine code.
* There is a Tier 2 interpreter, but it is mostly intended for debugging * There is a Tier 2 interpreter, but it is mostly intended for debugging
the earlier stages of the optimization pipeline. If the JIT is not the earlier stages of the optimization pipeline.
enabled, the Tier 2 interpreter can be invoked by passing Python the The Tier 2 interpreter can be enabled by configuring Python
``-X uops`` option or by setting the ``PYTHON_UOPS`` environment with ``--enable-experimental-jit=interpreter``.
variable to ``1``.
* When the ``--enable-experimental-jit`` option is used, the optimized * When the JIT is enabled, the optimized
Tier 2 IR is translated to machine code, which is then executed. Tier 2 IR is translated to machine code, which is then executed.
This does not require additional runtime options.
* The machine code translation process uses an architecture called * The machine code translation process uses an architecture called
*copy-and-patch*. It has no runtime dependencies, but there is a new *copy-and-patch*. It has no runtime dependencies, but there is a new
build-time dependency on LLVM. build-time dependency on LLVM.
The ``--enable-experimental-jit`` flag has the following optional values:
* ``no`` (default) -- Disable the entire Tier 2 and JIT pipeline.
* ``yes`` (default if the flag is present without optional value)
-- Enable the JIT. To disable the JIT at runtime,
pass the environment variable ``PYTHON_JIT=0``.
* ``yes-off`` -- Build the JIT but disable it by default.
To enable the JIT at runtime, pass the environment variable
``PYTHON_JIT=1``.
* ``interpreter`` -- Enable the Tier 2 interpreter but disable the JIT.
The interpreter can be disabled by running with
``PYTHON_JIT=0``.
(On Windows, use ``PCbuild/build.bat --enable-jit`` to enable the JIT.)
See :pep:`744` for more details. See :pep:`744` for more details.
(JIT by Brandt Bucher, inspired by a paper by Haoran Xu and Fredrik Kjolstad. (JIT by Brandt Bucher, inspired by a paper by Haoran Xu and Fredrik Kjolstad.

View File

@ -981,7 +981,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = {
[BUILD_SLICE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG }, [BUILD_SLICE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG },
[BUILD_STRING] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG }, [BUILD_STRING] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG },
[BUILD_TUPLE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG }, [BUILD_TUPLE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG },
[CACHE] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG }, [CACHE] = { true, INSTR_FMT_IX, 0 },
[CALL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [CALL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
[CALL_ALLOC_AND_ENTER_INIT] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [CALL_ALLOC_AND_ENTER_INIT] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
[CALL_BOUND_METHOD_EXACT_ARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG }, [CALL_BOUND_METHOD_EXACT_ARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG },
@ -1121,7 +1121,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = {
[PUSH_NULL] = { true, INSTR_FMT_IX, HAS_PURE_FLAG }, [PUSH_NULL] = { true, INSTR_FMT_IX, HAS_PURE_FLAG },
[RAISE_VARARGS] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [RAISE_VARARGS] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
[RERAISE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [RERAISE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
[RESERVED] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG }, [RESERVED] = { true, INSTR_FMT_IX, 0 },
[RESUME] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [RESUME] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
[RESUME_CHECK] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG }, [RESUME_CHECK] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG },
[RETURN_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG }, [RETURN_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG },

View File

@ -249,7 +249,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
[_COLD_EXIT] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_COLD_EXIT] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG,
[_DYNAMIC_EXIT] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_DYNAMIC_EXIT] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG,
[_START_EXECUTOR] = HAS_DEOPT_FLAG, [_START_EXECUTOR] = HAS_DEOPT_FLAG,
[_FATAL_ERROR] = HAS_ESCAPES_FLAG, [_FATAL_ERROR] = 0,
[_CHECK_VALIDITY_AND_SET_IP] = HAS_DEOPT_FLAG, [_CHECK_VALIDITY_AND_SET_IP] = HAS_DEOPT_FLAG,
[_DEOPT] = 0, [_DEOPT] = 0,
[_SIDE_EXIT] = 0, [_SIDE_EXIT] = 0,

View File

@ -216,7 +216,7 @@ def _get_code_array(co, adaptive):
if op == ENTER_EXECUTOR: if op == ENTER_EXECUTOR:
try: try:
ex = get_executor(co, i) ex = get_executor(co, i)
except ValueError: except (ValueError, RuntimeError):
ex = None ex = None
if ex: if ex:

View File

@ -2539,17 +2539,17 @@ Py_TRACE_REFS = hasattr(sys, 'getobjects')
# Decorator to disable optimizer while a function run # Decorator to disable optimizer while a function run
def without_optimizer(func): def without_optimizer(func):
try: try:
import _testinternalcapi from _testinternalcapi import get_optimizer, set_optimizer
except ImportError: except ImportError:
return func return func
@functools.wraps(func) @functools.wraps(func)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
save_opt = _testinternalcapi.get_optimizer() save_opt = get_optimizer()
try: try:
_testinternalcapi.set_optimizer(None) set_optimizer(None)
return func(*args, **kwargs) return func(*args, **kwargs)
finally: finally:
_testinternalcapi.set_optimizer(save_opt) set_optimizer(save_opt)
return wrapper return wrapper

View File

@ -34,6 +34,8 @@ def clear_executors(func):
@requires_specialization @requires_specialization
@unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"),
"Requires optimizer infrastructure")
class TestOptimizerAPI(unittest.TestCase): class TestOptimizerAPI(unittest.TestCase):
def test_new_counter_optimizer_dealloc(self): def test_new_counter_optimizer_dealloc(self):
@ -136,6 +138,8 @@ def get_opnames(ex):
@requires_specialization @requires_specialization
@unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"),
"Requires optimizer infrastructure")
class TestExecutorInvalidation(unittest.TestCase): class TestExecutorInvalidation(unittest.TestCase):
def setUp(self): def setUp(self):
@ -215,6 +219,8 @@ class TestExecutorInvalidation(unittest.TestCase):
@requires_specialization @requires_specialization
@unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"),
"Requires optimizer infrastructure")
@unittest.skipIf(os.getenv("PYTHON_UOPS_OPTIMIZE") == "0", "Needs uop optimizer to run.") @unittest.skipIf(os.getenv("PYTHON_UOPS_OPTIMIZE") == "0", "Needs uop optimizer to run.")
class TestUops(unittest.TestCase): class TestUops(unittest.TestCase):
@ -579,6 +585,8 @@ class TestUops(unittest.TestCase):
@requires_specialization @requires_specialization
@unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"),
"Requires optimizer infrastructure")
@unittest.skipIf(os.getenv("PYTHON_UOPS_OPTIMIZE") == "0", "Needs uop optimizer to run.") @unittest.skipIf(os.getenv("PYTHON_UOPS_OPTIMIZE") == "0", "Needs uop optimizer to run.")
class TestUopsOptimization(unittest.TestCase): class TestUopsOptimization(unittest.TestCase):

View File

@ -1831,15 +1831,17 @@ class TestOptimizer(MonitoringTestBase, unittest.TestCase):
def setUp(self): def setUp(self):
_testinternalcapi = import_module("_testinternalcapi") _testinternalcapi = import_module("_testinternalcapi")
self.old_opt = _testinternalcapi.get_optimizer() if hasattr(_testinternalcapi, "get_optimizer"):
opt = _testinternalcapi.new_counter_optimizer() self.old_opt = _testinternalcapi.get_optimizer()
_testinternalcapi.set_optimizer(opt) opt = _testinternalcapi.new_counter_optimizer()
_testinternalcapi.set_optimizer(opt)
super(TestOptimizer, self).setUp() super(TestOptimizer, self).setUp()
def tearDown(self): def tearDown(self):
super(TestOptimizer, self).tearDown() super(TestOptimizer, self).tearDown()
import _testinternalcapi import _testinternalcapi
_testinternalcapi.set_optimizer(self.old_opt) if hasattr(_testinternalcapi, "get_optimizer"):
_testinternalcapi.set_optimizer(self.old_opt)
def test_for_loop(self): def test_for_loop(self):
def test_func(x): def test_func(x):

View File

@ -16,6 +16,8 @@ _testinternalcapi = import_module("_testinternalcapi")
def disabling_optimizer(func): def disabling_optimizer(func):
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
if not hasattr(_testinternalcapi, "get_optimizer"):
return func(*args, **kwargs)
old_opt = _testinternalcapi.get_optimizer() old_opt = _testinternalcapi.get_optimizer()
_testinternalcapi.set_optimizer(None) _testinternalcapi.set_optimizer(None)
try: try:

View File

@ -80,6 +80,8 @@ class TestRareEventCounters(unittest.TestCase):
class TestOptimizerSymbols(unittest.TestCase): class TestOptimizerSymbols(unittest.TestCase):
@unittest.skipUnless(hasattr(_testinternalcapi, "uop_symbols_test"),
"requires _testinternalcapi.uop_symbols_test")
def test_optimizer_symbols(self): def test_optimizer_symbols(self):
_testinternalcapi.uop_symbols_test() _testinternalcapi.uop_symbols_test()

View File

@ -17,6 +17,7 @@ from test.support import script_helper, ALWAYS_EQ, suppress_immortalization
from test.support import gc_collect from test.support import gc_collect
from test.support import import_helper from test.support import import_helper
from test.support import threading_helper from test.support import threading_helper
from test.support import is_wasi, Py_DEBUG
# Used in ReferencesTestCase.test_ref_created_during_del() . # Used in ReferencesTestCase.test_ref_created_during_del() .
ref_from_del = None ref_from_del = None
@ -960,6 +961,7 @@ class ReferencesTestCase(TestBase):
self.assertEqual(hash(a), hash(42)) self.assertEqual(hash(a), hash(42))
self.assertRaises(TypeError, hash, b) self.assertRaises(TypeError, hash, b)
@unittest.skipIf(is_wasi and Py_DEBUG, "requires deep stack")
def test_trashcan_16602(self): def test_trashcan_16602(self):
# Issue #16602: when a weakref's target was part of a long # Issue #16602: when a weakref's target was part of a long
# deallocation chain, the trashcan mechanism could delay clearing # deallocation chain, the trashcan mechanism could delay clearing

View File

@ -0,0 +1,4 @@
Change how to use the tier 2 interpreter. Instead of running Python with
``-X uops`` or setting the environment variable ``PYTHON_UOPS=1``, this
choice is now made at build time by configuring with
``--enable-experimental-jit=interpreter``.

View File

@ -367,7 +367,13 @@ _opcode_get_executor_impl(PyObject *module, PyObject *code, int offset)
Py_TYPE(code)->tp_name); Py_TYPE(code)->tp_name);
return NULL; return NULL;
} }
#ifdef _Py_TIER2
return (PyObject *)PyUnstable_GetExecutor((PyCodeObject *)code, offset); return (PyObject *)PyUnstable_GetExecutor((PyCodeObject *)code, offset);
#else
PyErr_Format(PyExc_RuntimeError,
"Executors are not available in this build");
return NULL;
#endif
} }
static PyMethodDef static PyMethodDef

View File

@ -985,6 +985,8 @@ get_co_framesize(PyObject *self, PyObject *arg)
return PyLong_FromLong(code->co_framesize); return PyLong_FromLong(code->co_framesize);
} }
#ifdef _Py_TIER2
static PyObject * static PyObject *
new_counter_optimizer(PyObject *self, PyObject *arg) new_counter_optimizer(PyObject *self, PyObject *arg)
{ {
@ -1012,7 +1014,10 @@ set_optimizer(PyObject *self, PyObject *opt)
static PyObject * static PyObject *
get_optimizer(PyObject *self, PyObject *Py_UNUSED(ignored)) get_optimizer(PyObject *self, PyObject *Py_UNUSED(ignored))
{ {
PyObject *opt = (PyObject *)PyUnstable_GetOptimizer(); PyObject *opt = NULL;
#ifdef _Py_TIER2
opt = (PyObject *)PyUnstable_GetOptimizer();
#endif
if (opt == NULL) { if (opt == NULL) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
@ -1045,6 +1050,8 @@ invalidate_executors(PyObject *self, PyObject *obj)
Py_RETURN_NONE; Py_RETURN_NONE;
} }
#endif
static int _pending_callback(void *arg) static int _pending_callback(void *arg)
{ {
/* we assume the argument is callable object to which we own a reference */ /* we assume the argument is callable object to which we own a reference */
@ -2020,12 +2027,14 @@ static PyMethodDef module_functions[] = {
{"iframe_getline", iframe_getline, METH_O, NULL}, {"iframe_getline", iframe_getline, METH_O, NULL},
{"iframe_getlasti", iframe_getlasti, METH_O, NULL}, {"iframe_getlasti", iframe_getlasti, METH_O, NULL},
{"get_co_framesize", get_co_framesize, METH_O, NULL}, {"get_co_framesize", get_co_framesize, METH_O, NULL},
#ifdef _Py_TIER2
{"get_optimizer", get_optimizer, METH_NOARGS, NULL}, {"get_optimizer", get_optimizer, METH_NOARGS, NULL},
{"set_optimizer", set_optimizer, METH_O, NULL}, {"set_optimizer", set_optimizer, METH_O, NULL},
{"new_counter_optimizer", new_counter_optimizer, METH_NOARGS, NULL}, {"new_counter_optimizer", new_counter_optimizer, METH_NOARGS, NULL},
{"new_uop_optimizer", new_uop_optimizer, METH_NOARGS, NULL}, {"new_uop_optimizer", new_uop_optimizer, METH_NOARGS, NULL},
{"add_executor_dependency", add_executor_dependency, METH_VARARGS, NULL}, {"add_executor_dependency", add_executor_dependency, METH_VARARGS, NULL},
{"invalidate_executors", invalidate_executors, METH_O, NULL}, {"invalidate_executors", invalidate_executors, METH_O, NULL},
#endif
{"pending_threadfunc", _PyCFunction_CAST(pending_threadfunc), {"pending_threadfunc", _PyCFunction_CAST(pending_threadfunc),
METH_VARARGS | METH_KEYWORDS}, METH_VARARGS | METH_KEYWORDS},
{"pending_identify", pending_identify, METH_VARARGS, NULL}, {"pending_identify", pending_identify, METH_VARARGS, NULL},
@ -2072,7 +2081,9 @@ static PyMethodDef module_functions[] = {
{"py_thread_id", get_py_thread_id, METH_NOARGS}, {"py_thread_id", get_py_thread_id, METH_NOARGS},
#endif #endif
{"set_immortalize_deferred", set_immortalize_deferred, METH_VARARGS}, {"set_immortalize_deferred", set_immortalize_deferred, METH_VARARGS},
#ifdef _Py_TIER2
{"uop_symbols_test", _Py_uop_symbols_test, METH_NOARGS}, {"uop_symbols_test", _Py_uop_symbols_test, METH_NOARGS},
#endif
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };

View File

@ -1496,6 +1496,8 @@ PyCode_GetFreevars(PyCodeObject *code)
return _PyCode_GetFreevars(code); return _PyCode_GetFreevars(code);
} }
#ifdef _Py_TIER2
static void static void
clear_executors(PyCodeObject *co) clear_executors(PyCodeObject *co)
{ {
@ -1515,6 +1517,8 @@ _PyCode_Clear_Executors(PyCodeObject *code)
clear_executors(code); clear_executors(code);
} }
#endif
static void static void
deopt_code(PyCodeObject *code, _Py_CODEUNIT *instructions) deopt_code(PyCodeObject *code, _Py_CODEUNIT *instructions)
{ {
@ -1739,9 +1743,11 @@ code_dealloc(PyCodeObject *co)
PyMem_Free(co_extra); PyMem_Free(co_extra);
} }
#ifdef _Py_TIER2
if (co->co_executors != NULL) { if (co->co_executors != NULL) {
clear_executors(co); clear_executors(co);
} }
#endif
Py_XDECREF(co->co_consts); Py_XDECREF(co->co_consts);
Py_XDECREF(co->co_names); Py_XDECREF(co->co_names);

View File

@ -2281,9 +2281,11 @@ static PyTypeObject* static_types[] = {
&_PyBufferWrapper_Type, &_PyBufferWrapper_Type,
&_PyContextTokenMissing_Type, &_PyContextTokenMissing_Type,
&_PyCoroWrapper_Type, &_PyCoroWrapper_Type,
#ifdef _Py_TIER2
&_PyCounterExecutor_Type, &_PyCounterExecutor_Type,
&_PyCounterOptimizer_Type, &_PyCounterOptimizer_Type,
&_PyDefaultOptimizer_Type, &_PyDefaultOptimizer_Type,
#endif
&_Py_GenericAliasIterType, &_Py_GenericAliasIterType,
&_PyHamtItems_Type, &_PyHamtItems_Type,
&_PyHamtKeys_Type, &_PyHamtKeys_Type,
@ -2304,8 +2306,10 @@ static PyTypeObject* static_types[] = {
&_PyPositionsIterator, &_PyPositionsIterator,
&_PyUnicodeASCIIIter_Type, &_PyUnicodeASCIIIter_Type,
&_PyUnion_Type, &_PyUnion_Type,
#ifdef _Py_TIER2
&_PyUOpExecutor_Type, &_PyUOpExecutor_Type,
&_PyUOpOptimizer_Type, &_PyUOpOptimizer_Type,
#endif
&_PyWeakref_CallableProxyType, &_PyWeakref_CallableProxyType,
&_PyWeakref_ProxyType, &_PyWeakref_ProxyType,
&_PyWeakref_RefType, &_PyWeakref_RefType,

View File

@ -108,6 +108,12 @@
<ReferenceOutputAssembly>false</ReferenceOutputAssembly> <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions Condition="'$(UseJIT)' == 'true'">_Py_JIT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(UseTIER2)' != '0'">_Py_TIER2=$(UseTIER2);%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
</ImportGroup> </ImportGroup>

View File

@ -36,7 +36,9 @@ echo. overrides -c and -d
echo. --disable-gil Enable experimental support for running without the GIL. echo. --disable-gil Enable experimental support for running without the GIL.
echo. --test-marker Enable the test marker within the build. echo. --test-marker Enable the test marker within the build.
echo. --regen Regenerate all opcodes, grammar and tokens. echo. --regen Regenerate all opcodes, grammar and tokens.
echo. --experimental-jit Enable the experimental just-in-time compiler. echo. --experimental-jit Enable the experimental just-in-time compiler.
echo. --experimental-jit-off Ditto but off by default (PYTHON_JIT=1 enables).
echo. --experimental-interpreter Enable the experimental Tier 2 interpreter.
echo. echo.
echo.Available flags to avoid building certain modules. echo.Available flags to avoid building certain modules.
echo.These flags have no effect if '-e' is not given: echo.These flags have no effect if '-e' is not given:
@ -66,6 +68,7 @@ set verbose=/nologo /v:m /clp:summary
set kill= set kill=
set do_pgo= set do_pgo=
set pgo_job=-m test --pgo set pgo_job=-m test --pgo
set UseTIER2=0
:CheckOpts :CheckOpts
if "%~1"=="-h" goto Usage if "%~1"=="-h" goto Usage
@ -86,7 +89,10 @@ if "%~1"=="--disable-gil" (set UseDisableGil=true) & shift & goto CheckOpts
if "%~1"=="--test-marker" (set UseTestMarker=true) & shift & goto CheckOpts if "%~1"=="--test-marker" (set UseTestMarker=true) & shift & goto CheckOpts
if "%~1"=="-V" shift & goto Version if "%~1"=="-V" shift & goto Version
if "%~1"=="--regen" (set Regen=true) & shift & goto CheckOpts if "%~1"=="--regen" (set Regen=true) & shift & goto CheckOpts
if "%~1"=="--experimental-jit" (set UseJIT=true) & shift & goto CheckOpts if "%~1"=="--experimental-jit" (set UseJIT=true) & (set UseTIER2=1) & shift & goto CheckOpts
if "%~1"=="--experimental-jit-off" (set UseJIT=true) & (set UseTIER2=3) & shift & goto CheckOpts
if "%~1"=="--experimental-interpreter" (set UseTIER2=4) & shift & goto CheckOpts
if "%~1"=="--experimental-interpreter-off" (set UseTIER2=6) & shift & goto CheckOpts
rem These use the actual property names used by MSBuild. We could just let rem These use the actual property names used by MSBuild. We could just let
rem them in through the environment, but we specify them on the command line rem them in through the environment, but we specify them on the command line
rem anyway for visibility so set defaults after this rem anyway for visibility so set defaults after this
@ -179,6 +185,7 @@ echo on
/p:DisableGil=%UseDisableGil%^ /p:DisableGil=%UseDisableGil%^
/p:UseTestMarker=%UseTestMarker% %GITProperty%^ /p:UseTestMarker=%UseTestMarker% %GITProperty%^
/p:UseJIT=%UseJIT%^ /p:UseJIT=%UseJIT%^
/p:UseTIER2=%UseTIER2%^
%1 %2 %3 %4 %5 %6 %7 %8 %9 %1 %2 %3 %4 %5 %6 %7 %8 %9
@echo off @echo off

View File

@ -105,6 +105,7 @@
<PreprocessorDefinitions>_USRDLL;Py_BUILD_CORE;Py_BUILD_CORE_BUILTIN;Py_ENABLE_SHARED;MS_DLL_ID="$(SysWinVer)";%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_USRDLL;Py_BUILD_CORE;Py_BUILD_CORE_BUILTIN;Py_ENABLE_SHARED;MS_DLL_ID="$(SysWinVer)";%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="$(IncludeExternals)">_Py_HAVE_ZLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions Condition="$(IncludeExternals)">_Py_HAVE_ZLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(UseJIT)' == 'true'">_Py_JIT;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions Condition="'$(UseJIT)' == 'true'">_Py_JIT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(UseTIER2)' != '0'">_Py_TIER2=$(UseTIER2);%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile> </ClCompile>
<Link> <Link>
<AdditionalDependencies>version.lib;ws2_32.lib;pathcch.lib;bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>version.lib;ws2_32.lib;pathcch.lib;bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>

View File

@ -2363,6 +2363,7 @@ dummy_func(
CHECK_EVAL_BREAKER(); CHECK_EVAL_BREAKER();
assert(oparg <= INSTR_OFFSET()); assert(oparg <= INSTR_OFFSET());
JUMPBY(-oparg); JUMPBY(-oparg);
#ifdef _Py_TIER2
#if ENABLE_SPECIALIZATION #if ENABLE_SPECIALIZATION
_Py_BackoffCounter counter = this_instr[1].counter; _Py_BackoffCounter counter = this_instr[1].counter;
if (backoff_counter_triggers(counter) && this_instr->op.code == JUMP_BACKWARD) { if (backoff_counter_triggers(counter) && this_instr->op.code == JUMP_BACKWARD) {
@ -2388,6 +2389,7 @@ dummy_func(
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
} }
#endif /* ENABLE_SPECIALIZATION */ #endif /* ENABLE_SPECIALIZATION */
#endif /* _Py_TIER2 */
} }
pseudo(JUMP) = { pseudo(JUMP) = {
@ -2401,6 +2403,7 @@ dummy_func(
}; };
tier1 inst(ENTER_EXECUTOR, (--)) { tier1 inst(ENTER_EXECUTOR, (--)) {
#ifdef _Py_TIER2
int prevoparg = oparg; int prevoparg = oparg;
CHECK_EVAL_BREAKER(); CHECK_EVAL_BREAKER();
if (this_instr->op.code != ENTER_EXECUTOR || if (this_instr->op.code != ENTER_EXECUTOR ||
@ -2418,6 +2421,9 @@ dummy_func(
tstate->previous_executor = Py_None; tstate->previous_executor = Py_None;
Py_INCREF(executor); Py_INCREF(executor);
GOTO_TIER_TWO(executor); GOTO_TIER_TWO(executor);
#else
Py_FatalError("ENTER_EXECUTOR is not supported in this build");
#endif /* _Py_TIER2 */
} }
replaced op(_POP_JUMP_IF_FALSE, (cond -- )) { replaced op(_POP_JUMP_IF_FALSE, (cond -- )) {

View File

@ -755,7 +755,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
_Py_CODEUNIT *next_instr; _Py_CODEUNIT *next_instr;
PyObject **stack_pointer; PyObject **stack_pointer;
#ifndef _Py_JIT #if defined(_Py_TIER2) && !defined(_Py_JIT)
/* Tier 2 interpreter state */ /* Tier 2 interpreter state */
_PyExecutorObject *current_executor = NULL; _PyExecutorObject *current_executor = NULL;
const _PyUOpInstruction *next_uop = NULL; const _PyUOpInstruction *next_uop = NULL;
@ -959,6 +959,7 @@ resume_with_error:
goto error; goto error;
#ifdef _Py_TIER2
// Tier 2 is also here! // Tier 2 is also here!
enter_tier_two: enter_tier_two:
@ -1113,6 +1114,8 @@ exit_to_trace:
#endif // _Py_JIT #endif // _Py_JIT
#endif // _Py_TIER2
} }
#if defined(__GNUC__) #if defined(__GNUC__)

View File

@ -2492,6 +2492,7 @@
(void)this_instr; (void)this_instr;
next_instr += 1; next_instr += 1;
INSTRUCTION_STATS(ENTER_EXECUTOR); INSTRUCTION_STATS(ENTER_EXECUTOR);
#ifdef _Py_TIER2
int prevoparg = oparg; int prevoparg = oparg;
CHECK_EVAL_BREAKER(); CHECK_EVAL_BREAKER();
if (this_instr->op.code != ENTER_EXECUTOR || if (this_instr->op.code != ENTER_EXECUTOR ||
@ -2508,6 +2509,9 @@
tstate->previous_executor = Py_None; tstate->previous_executor = Py_None;
Py_INCREF(executor); Py_INCREF(executor);
GOTO_TIER_TWO(executor); GOTO_TIER_TWO(executor);
#else
Py_FatalError("ENTER_EXECUTOR is not supported in this build");
#endif /* _Py_TIER2 */
DISPATCH(); DISPATCH();
} }
@ -3432,6 +3436,7 @@
CHECK_EVAL_BREAKER(); CHECK_EVAL_BREAKER();
assert(oparg <= INSTR_OFFSET()); assert(oparg <= INSTR_OFFSET());
JUMPBY(-oparg); JUMPBY(-oparg);
#ifdef _Py_TIER2
#if ENABLE_SPECIALIZATION #if ENABLE_SPECIALIZATION
_Py_BackoffCounter counter = this_instr[1].counter; _Py_BackoffCounter counter = this_instr[1].counter;
if (backoff_counter_triggers(counter) && this_instr->op.code == JUMP_BACKWARD) { if (backoff_counter_triggers(counter) && this_instr->op.code == JUMP_BACKWARD) {
@ -3457,6 +3462,7 @@
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
} }
#endif /* ENABLE_SPECIALIZATION */ #endif /* ENABLE_SPECIALIZATION */
#endif /* _Py_TIER2 */
DISPATCH(); DISPATCH();
} }

View File

@ -1705,10 +1705,12 @@ instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp)
); );
return 0; return 0;
} }
#ifdef _Py_TIER2
if (code->co_executors != NULL) { if (code->co_executors != NULL) {
_PyCode_Clear_Executors(code); _PyCode_Clear_Executors(code);
} }
_Py_Executors_InvalidateDependency(interp, code, 1); _Py_Executors_InvalidateDependency(interp, code, 1);
#endif
int code_len = (int)Py_SIZE(code); int code_len = (int)Py_SIZE(code);
/* Exit early to avoid creating instrumentation /* Exit early to avoid creating instrumentation
* data for potential statically allocated code * data for potential statically allocated code
@ -1946,7 +1948,9 @@ _PyMonitoring_SetEvents(int tool_id, _PyMonitoringEventSet events)
goto done; goto done;
} }
set_global_version(tstate, new_version); set_global_version(tstate, new_version);
#ifdef _Py_TIER2
_Py_Executors_InvalidateAll(interp, 1); _Py_Executors_InvalidateAll(interp, 1);
#endif
res = instrument_all_executing_code_objects(interp); res = instrument_all_executing_code_objects(interp);
done: done:
_PyEval_StartTheWorld(interp); _PyEval_StartTheWorld(interp);
@ -1986,7 +1990,9 @@ _PyMonitoring_SetLocalEvents(PyCodeObject *code, int tool_id, _PyMonitoringEvent
code->_co_instrumentation_version -= MONITORING_VERSION_INCREMENT; code->_co_instrumentation_version -= MONITORING_VERSION_INCREMENT;
} }
#ifdef _Py_TIER2
_Py_Executors_InvalidateDependency(interp, code, 1); _Py_Executors_InvalidateDependency(interp, code, 1);
#endif
res = instrument_lock_held(code, interp); res = instrument_lock_held(code, interp);

View File

@ -1,3 +1,5 @@
#ifdef _Py_TIER2
#include "Python.h" #include "Python.h"
#include "opcode.h" #include "opcode.h"
#include "pycore_interp.h" #include "pycore_interp.h"
@ -1622,3 +1624,5 @@ _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation)
} }
} }
} }
#endif /* _Py_TIER2 */

View File

@ -1,3 +1,5 @@
#ifdef _Py_TIER2
/* /*
* This file contains the support code for CPython's uops optimizer. * This file contains the support code for CPython's uops optimizer.
* It also performs some simple optimizations. * It also performs some simple optimizations.
@ -603,3 +605,5 @@ _Py_uop_analyze_and_optimize(
OPT_STAT_INC(optimizer_successes); OPT_STAT_INC(optimizer_successes);
return length; return length;
} }
#endif /* _Py_TIER2 */

View File

@ -1,3 +1,4 @@
#ifdef _Py_TIER2
#include "Python.h" #include "Python.h"
@ -506,3 +507,5 @@ fail:
Py_XDECREF(val_43); Py_XDECREF(val_43);
return NULL; return NULL;
} }
#endif /* _Py_TIER2 */

View File

@ -624,9 +624,11 @@ static int
builtins_dict_watcher(PyDict_WatchEvent event, PyObject *dict, PyObject *key, PyObject *new_value) builtins_dict_watcher(PyDict_WatchEvent event, PyObject *dict, PyObject *key, PyObject *new_value)
{ {
PyInterpreterState *interp = _PyInterpreterState_GET(); PyInterpreterState *interp = _PyInterpreterState_GET();
#ifdef _Py_TIER2
if (interp->rare_events.builtin_dict < _Py_MAX_ALLOWED_BUILTINS_MODIFICATIONS) { if (interp->rare_events.builtin_dict < _Py_MAX_ALLOWED_BUILTINS_MODIFICATIONS) {
_Py_Executors_InvalidateAll(interp, 1); _Py_Executors_InvalidateAll(interp, 1);
} }
#endif
RARE_EVENT_INTERP_INC(interp, builtin_dict); RARE_EVENT_INTERP_INC(interp, builtin_dict);
return 0; return 0;
} }
@ -1272,30 +1274,30 @@ init_interp_main(PyThreadState *tstate)
} }
// Turn on experimental tier 2 (uops-based) optimizer // Turn on experimental tier 2 (uops-based) optimizer
// This is also needed when the JIT is enabled
#ifdef _Py_TIER2
if (is_main_interp) { if (is_main_interp) {
#ifndef _Py_JIT int enabled = 1;
// No JIT, maybe use the tier two interpreter: #if _Py_TIER2 & 2
char *envvar = Py_GETENV("PYTHON_UOPS"); enabled = 0;
int enabled = envvar != NULL && *envvar > '0'; #endif
if (_Py_get_xoption(&config->xoptions, L"uops") != NULL) { char *env = Py_GETENV("PYTHON_JIT");
enabled = 1; if (env && *env != '\0') {
// PYTHON_JIT=0|1 overrides the default
enabled = *env != '0';
} }
if (enabled) { if (enabled) {
#else
// Always enable tier two for JIT builds (ignoring the environment
// variable and command-line option above):
if (true) {
#endif
PyObject *opt = PyUnstable_Optimizer_NewUOpOptimizer(); PyObject *opt = PyUnstable_Optimizer_NewUOpOptimizer();
if (opt == NULL) { if (opt == NULL) {
return _PyStatus_ERR("can't initialize optimizer"); return _PyStatus_ERR("can't initialize optimizer");
} }
if (PyUnstable_SetOptimizer((_PyOptimizerObject *)opt)) { if (PyUnstable_SetOptimizer((_PyOptimizerObject *)opt)) {
return _PyStatus_ERR("can't initialize optimizer"); return _PyStatus_ERR("can't install optimizer");
} }
Py_DECREF(opt); Py_DECREF(opt);
} }
} }
#endif
if (!is_main_interp) { if (!is_main_interp) {
// The main interpreter is handled in Py_Main(), for now. // The main interpreter is handled in Py_Main(), for now.
@ -1655,10 +1657,12 @@ finalize_modules(PyThreadState *tstate)
{ {
PyInterpreterState *interp = tstate->interp; PyInterpreterState *interp = tstate->interp;
#ifdef _Py_TIER2
// Invalidate all executors and turn off tier 2 optimizer // Invalidate all executors and turn off tier 2 optimizer
_Py_Executors_InvalidateAll(interp, 0); _Py_Executors_InvalidateAll(interp, 0);
_PyOptimizerObject *old = _Py_SetOptimizer(interp, NULL); _PyOptimizerObject *old = _Py_SetOptimizer(interp, NULL);
Py_XDECREF(old); Py_XDECREF(old);
#endif
// Stop watching __builtin__ modifications // Stop watching __builtin__ modifications
PyDict_Unwatch(0, interp->builtins); PyDict_Unwatch(0, interp->builtins);

View File

@ -653,8 +653,10 @@ init_interpreter(PyInterpreterState *interp,
} }
interp->sys_profile_initialized = false; interp->sys_profile_initialized = false;
interp->sys_trace_initialized = false; interp->sys_trace_initialized = false;
#ifdef _Py_TIER2
(void)_Py_SetOptimizer(interp, NULL); (void)_Py_SetOptimizer(interp, NULL);
interp->executor_list_head = NULL; interp->executor_list_head = NULL;
#endif
if (interp != &runtime->_main_interpreter) { if (interp != &runtime->_main_interpreter) {
/* Fix the self-referential, statically initialized fields. */ /* Fix the self-referential, statically initialized fields. */
interp->dtoa = (struct _dtoa_state)_dtoa_state_INIT(interp); interp->dtoa = (struct _dtoa_state)_dtoa_state_INIT(interp);
@ -806,9 +808,11 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
tstate->_status.cleared = 0; tstate->_status.cleared = 0;
} }
#ifdef _Py_TIER2
_PyOptimizerObject *old = _Py_SetOptimizer(interp, NULL); _PyOptimizerObject *old = _Py_SetOptimizer(interp, NULL);
assert(old != NULL); assert(old != NULL);
Py_DECREF(old); Py_DECREF(old);
#endif
/* It is possible that any of the objects below have a finalizer /* It is possible that any of the objects below have a finalizer
that runs Python code or otherwise relies on a thread state that runs Python code or otherwise relies on a thread state
@ -2821,9 +2825,11 @@ _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp,
if (eval_frame == interp->eval_frame) { if (eval_frame == interp->eval_frame) {
return; return;
} }
#ifdef _Py_TIER2
if (eval_frame != NULL) { if (eval_frame != NULL) {
_Py_Executors_InvalidateAll(interp, 1); _Py_Executors_InvalidateAll(interp, 1);
} }
#endif
RARE_EVENT_INC(set_eval_frame_func); RARE_EVENT_INC(set_eval_frame_func);
interp->eval_frame = eval_frame; interp->eval_frame = eval_frame;
} }

View File

@ -2165,8 +2165,10 @@ static PyObject *
sys__clear_internal_caches_impl(PyObject *module) sys__clear_internal_caches_impl(PyObject *module)
/*[clinic end generated code: output=0ee128670a4966d6 input=253e741ca744f6e8]*/ /*[clinic end generated code: output=0ee128670a4966d6 input=253e741ca744f6e8]*/
{ {
#ifdef _Py_TIER2
PyInterpreterState *interp = _PyInterpreterState_GET(); PyInterpreterState *interp = _PyInterpreterState_GET();
_Py_Executors_InvalidateAll(interp, 0); _Py_Executors_InvalidateAll(interp, 0);
#endif
PyType_ClearCache(); PyType_ClearCache();
Py_RETURN_NONE; Py_RETURN_NONE;
} }

View File

@ -411,6 +411,7 @@ NON_ESCAPING_FUNCTIONS = (
"PyCell_New", "PyCell_New",
"PyFloat_AS_DOUBLE", "PyFloat_AS_DOUBLE",
"_PyFrame_PushUnchecked", "_PyFrame_PushUnchecked",
"Py_FatalError",
) )
ESCAPING_FUNCTIONS = ( ESCAPING_FUNCTIONS = (

View File

@ -7,18 +7,18 @@ 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). 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 16 is required. Both `clang` and `llvm-readobj` need to be installed and discoverable (version suffixes, like `clang-16`, 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 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.
It's easy to install all of the required tools: It's easy to install all of the required tools:
### Linux ### Linux
Install LLVM 16 on Ubuntu/Debian: Install LLVM 18 on Ubuntu/Debian:
```sh ```sh
wget https://apt.llvm.org/llvm.sh wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh chmod +x llvm.sh
sudo ./llvm.sh 16 sudo ./llvm.sh 18
``` ```
### macOS ### macOS

12
configure generated vendored
View File

@ -1818,7 +1818,7 @@ Optional Features:
--disable-gil enable experimental support for running without the --disable-gil enable experimental support for running without the
GIL (default is no) GIL (default is no)
--enable-pystats enable internal statistics gathering (default is no) --enable-pystats enable internal statistics gathering (default is no)
--enable-experimental-jit --enable-experimental-jit[=no|yes|yes-off|interpreter]
build the experimental just-in-time compiler build the experimental just-in-time compiler
(default is no) (default is no)
--enable-optimizations enable expensive, stable optimizations (PGO, etc.) --enable-optimizations enable expensive, stable optimizations (PGO, etc.)
@ -8229,11 +8229,19 @@ else $as_nop
enable_experimental_jit=no enable_experimental_jit=no
fi fi
case $enable_experimental_jit in
no) enable_experimental_jit=no ;;
yes) enable_experimental_jit="-D_Py_JIT -D_Py_TIER2=1" ;;
yes-off) enable_experimental_jit="-D_Py_JIT -D_Py_TIER2=3" ;;
interpreter) enable_experimental_jit="-D_Py_TIER2=4" ;;
interpreter-off) enable_experimental_jit="-D_Py_TIER2=6" ;; # Secret option
*) as_fn_error $? "invalid argument: --enable-experimental-jit=$enable_experimental_jit; expected no|yes|yes-off|interpreter" "$LINENO" 5 ;;
esac
if test "x$enable_experimental_jit" = xno if test "x$enable_experimental_jit" = xno
then : then :
else $as_nop else $as_nop
as_fn_append CFLAGS_NODIST " -D_Py_JIT" as_fn_append CFLAGS_NODIST " $enable_experimental_jit"
REGEN_JIT_COMMAND="\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py $host" REGEN_JIT_COMMAND="\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py $host"
JIT_STENCILS_H="jit_stencils.h" JIT_STENCILS_H="jit_stencils.h"
if test "x$Py_DEBUG" = xtrue if test "x$Py_DEBUG" = xtrue

View File

@ -1768,14 +1768,23 @@ fi
# Check for --enable-experimental-jit: # Check for --enable-experimental-jit:
AC_MSG_CHECKING([for --enable-experimental-jit]) AC_MSG_CHECKING([for --enable-experimental-jit])
AC_ARG_ENABLE([experimental-jit], AC_ARG_ENABLE([experimental-jit],
[AS_HELP_STRING([--enable-experimental-jit], [AS_HELP_STRING([--enable-experimental-jit@<:@=no|yes|yes-off|interpreter@:>@],
[build the experimental just-in-time compiler (default is no)])], [build the experimental just-in-time compiler (default is no)])],
[], [],
[enable_experimental_jit=no]) [enable_experimental_jit=no])
case $enable_experimental_jit in
no) enable_experimental_jit=no ;;
yes) enable_experimental_jit="-D_Py_JIT -D_Py_TIER2=1" ;;
yes-off) enable_experimental_jit="-D_Py_JIT -D_Py_TIER2=3" ;;
interpreter) enable_experimental_jit="-D_Py_TIER2=4" ;;
interpreter-off) enable_experimental_jit="-D_Py_TIER2=6" ;; # Secret option
*) AC_MSG_ERROR(
[invalid argument: --enable-experimental-jit=$enable_experimental_jit; expected no|yes|yes-off|interpreter]) ;;
esac
AS_VAR_IF([enable_experimental_jit], AS_VAR_IF([enable_experimental_jit],
[no], [no],
[], [],
[AS_VAR_APPEND([CFLAGS_NODIST], [" -D_Py_JIT"]) [AS_VAR_APPEND([CFLAGS_NODIST], [" $enable_experimental_jit"])
AS_VAR_SET([REGEN_JIT_COMMAND], AS_VAR_SET([REGEN_JIT_COMMAND],
["\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py $host"]) ["\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py $host"])
AS_VAR_SET([JIT_STENCILS_H], ["jit_stencils.h"]) AS_VAR_SET([JIT_STENCILS_H], ["jit_stencils.h"])