mirror of https://github.com/python/cpython
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:
parent
9c468e2c5d
commit
7d83f7bcc4
|
@ -888,7 +888,7 @@ Experimental JIT Compiler
|
|||
=========================
|
||||
|
||||
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.
|
||||
|
||||
|
@ -905,19 +905,35 @@ The internal architecture is roughly as follows.
|
|||
before it is interpreted or translated to machine code.
|
||||
|
||||
* 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
|
||||
enabled, the Tier 2 interpreter can be invoked by passing Python the
|
||||
``-X uops`` option or by setting the ``PYTHON_UOPS`` environment
|
||||
variable to ``1``.
|
||||
the earlier stages of the optimization pipeline.
|
||||
The Tier 2 interpreter can be enabled by configuring Python
|
||||
with ``--enable-experimental-jit=interpreter``.
|
||||
|
||||
* 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.
|
||||
This does not require additional runtime options.
|
||||
|
||||
* The machine code translation process uses an architecture called
|
||||
*copy-and-patch*. It has no runtime dependencies, but there is a new
|
||||
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.
|
||||
|
||||
(JIT by Brandt Bucher, inspired by a paper by Haoran Xu and Fredrik Kjolstad.
|
||||
|
|
|
@ -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_STRING] = { 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_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 },
|
||||
|
@ -1121,7 +1121,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = {
|
|||
[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 },
|
||||
[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_CHECK] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG },
|
||||
[RETURN_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG },
|
||||
|
|
|
@ -249,7 +249,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
|
|||
[_COLD_EXIT] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG,
|
||||
[_DYNAMIC_EXIT] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG,
|
||||
[_START_EXECUTOR] = HAS_DEOPT_FLAG,
|
||||
[_FATAL_ERROR] = HAS_ESCAPES_FLAG,
|
||||
[_FATAL_ERROR] = 0,
|
||||
[_CHECK_VALIDITY_AND_SET_IP] = HAS_DEOPT_FLAG,
|
||||
[_DEOPT] = 0,
|
||||
[_SIDE_EXIT] = 0,
|
||||
|
|
|
@ -216,7 +216,7 @@ def _get_code_array(co, adaptive):
|
|||
if op == ENTER_EXECUTOR:
|
||||
try:
|
||||
ex = get_executor(co, i)
|
||||
except ValueError:
|
||||
except (ValueError, RuntimeError):
|
||||
ex = None
|
||||
|
||||
if ex:
|
||||
|
|
|
@ -2539,17 +2539,17 @@ Py_TRACE_REFS = hasattr(sys, 'getobjects')
|
|||
# Decorator to disable optimizer while a function run
|
||||
def without_optimizer(func):
|
||||
try:
|
||||
import _testinternalcapi
|
||||
from _testinternalcapi import get_optimizer, set_optimizer
|
||||
except ImportError:
|
||||
return func
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
save_opt = _testinternalcapi.get_optimizer()
|
||||
save_opt = get_optimizer()
|
||||
try:
|
||||
_testinternalcapi.set_optimizer(None)
|
||||
set_optimizer(None)
|
||||
return func(*args, **kwargs)
|
||||
finally:
|
||||
_testinternalcapi.set_optimizer(save_opt)
|
||||
set_optimizer(save_opt)
|
||||
return wrapper
|
||||
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ def clear_executors(func):
|
|||
|
||||
|
||||
@requires_specialization
|
||||
@unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"),
|
||||
"Requires optimizer infrastructure")
|
||||
class TestOptimizerAPI(unittest.TestCase):
|
||||
|
||||
def test_new_counter_optimizer_dealloc(self):
|
||||
|
@ -136,6 +138,8 @@ def get_opnames(ex):
|
|||
|
||||
|
||||
@requires_specialization
|
||||
@unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"),
|
||||
"Requires optimizer infrastructure")
|
||||
class TestExecutorInvalidation(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
@ -215,6 +219,8 @@ class TestExecutorInvalidation(unittest.TestCase):
|
|||
|
||||
|
||||
@requires_specialization
|
||||
@unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"),
|
||||
"Requires optimizer infrastructure")
|
||||
@unittest.skipIf(os.getenv("PYTHON_UOPS_OPTIMIZE") == "0", "Needs uop optimizer to run.")
|
||||
class TestUops(unittest.TestCase):
|
||||
|
||||
|
@ -579,6 +585,8 @@ class TestUops(unittest.TestCase):
|
|||
|
||||
|
||||
@requires_specialization
|
||||
@unittest.skipUnless(hasattr(_testinternalcapi, "get_optimizer"),
|
||||
"Requires optimizer infrastructure")
|
||||
@unittest.skipIf(os.getenv("PYTHON_UOPS_OPTIMIZE") == "0", "Needs uop optimizer to run.")
|
||||
class TestUopsOptimization(unittest.TestCase):
|
||||
|
||||
|
|
|
@ -1831,6 +1831,7 @@ class TestOptimizer(MonitoringTestBase, unittest.TestCase):
|
|||
|
||||
def setUp(self):
|
||||
_testinternalcapi = import_module("_testinternalcapi")
|
||||
if hasattr(_testinternalcapi, "get_optimizer"):
|
||||
self.old_opt = _testinternalcapi.get_optimizer()
|
||||
opt = _testinternalcapi.new_counter_optimizer()
|
||||
_testinternalcapi.set_optimizer(opt)
|
||||
|
@ -1839,6 +1840,7 @@ class TestOptimizer(MonitoringTestBase, unittest.TestCase):
|
|||
def tearDown(self):
|
||||
super(TestOptimizer, self).tearDown()
|
||||
import _testinternalcapi
|
||||
if hasattr(_testinternalcapi, "get_optimizer"):
|
||||
_testinternalcapi.set_optimizer(self.old_opt)
|
||||
|
||||
def test_for_loop(self):
|
||||
|
|
|
@ -16,6 +16,8 @@ _testinternalcapi = import_module("_testinternalcapi")
|
|||
|
||||
def disabling_optimizer(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
if not hasattr(_testinternalcapi, "get_optimizer"):
|
||||
return func(*args, **kwargs)
|
||||
old_opt = _testinternalcapi.get_optimizer()
|
||||
_testinternalcapi.set_optimizer(None)
|
||||
try:
|
||||
|
|
|
@ -80,6 +80,8 @@ class TestRareEventCounters(unittest.TestCase):
|
|||
|
||||
class TestOptimizerSymbols(unittest.TestCase):
|
||||
|
||||
@unittest.skipUnless(hasattr(_testinternalcapi, "uop_symbols_test"),
|
||||
"requires _testinternalcapi.uop_symbols_test")
|
||||
def test_optimizer_symbols(self):
|
||||
_testinternalcapi.uop_symbols_test()
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ from test.support import script_helper, ALWAYS_EQ, suppress_immortalization
|
|||
from test.support import gc_collect
|
||||
from test.support import import_helper
|
||||
from test.support import threading_helper
|
||||
from test.support import is_wasi, Py_DEBUG
|
||||
|
||||
# Used in ReferencesTestCase.test_ref_created_during_del() .
|
||||
ref_from_del = None
|
||||
|
@ -960,6 +961,7 @@ class ReferencesTestCase(TestBase):
|
|||
self.assertEqual(hash(a), hash(42))
|
||||
self.assertRaises(TypeError, hash, b)
|
||||
|
||||
@unittest.skipIf(is_wasi and Py_DEBUG, "requires deep stack")
|
||||
def test_trashcan_16602(self):
|
||||
# Issue #16602: when a weakref's target was part of a long
|
||||
# deallocation chain, the trashcan mechanism could delay clearing
|
||||
|
|
|
@ -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``.
|
|
@ -367,7 +367,13 @@ _opcode_get_executor_impl(PyObject *module, PyObject *code, int offset)
|
|||
Py_TYPE(code)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
#ifdef _Py_TIER2
|
||||
return (PyObject *)PyUnstable_GetExecutor((PyCodeObject *)code, offset);
|
||||
#else
|
||||
PyErr_Format(PyExc_RuntimeError,
|
||||
"Executors are not available in this build");
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static PyMethodDef
|
||||
|
|
|
@ -985,6 +985,8 @@ get_co_framesize(PyObject *self, PyObject *arg)
|
|||
return PyLong_FromLong(code->co_framesize);
|
||||
}
|
||||
|
||||
#ifdef _Py_TIER2
|
||||
|
||||
static PyObject *
|
||||
new_counter_optimizer(PyObject *self, PyObject *arg)
|
||||
{
|
||||
|
@ -1012,7 +1014,10 @@ set_optimizer(PyObject *self, PyObject *opt)
|
|||
static PyObject *
|
||||
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) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
@ -1045,6 +1050,8 @@ invalidate_executors(PyObject *self, PyObject *obj)
|
|||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int _pending_callback(void *arg)
|
||||
{
|
||||
/* 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_getlasti", iframe_getlasti, METH_O, NULL},
|
||||
{"get_co_framesize", get_co_framesize, METH_O, NULL},
|
||||
#ifdef _Py_TIER2
|
||||
{"get_optimizer", get_optimizer, METH_NOARGS, NULL},
|
||||
{"set_optimizer", set_optimizer, METH_O, NULL},
|
||||
{"new_counter_optimizer", new_counter_optimizer, METH_NOARGS, NULL},
|
||||
{"new_uop_optimizer", new_uop_optimizer, METH_NOARGS, NULL},
|
||||
{"add_executor_dependency", add_executor_dependency, METH_VARARGS, NULL},
|
||||
{"invalidate_executors", invalidate_executors, METH_O, NULL},
|
||||
#endif
|
||||
{"pending_threadfunc", _PyCFunction_CAST(pending_threadfunc),
|
||||
METH_VARARGS | METH_KEYWORDS},
|
||||
{"pending_identify", pending_identify, METH_VARARGS, NULL},
|
||||
|
@ -2072,7 +2081,9 @@ static PyMethodDef module_functions[] = {
|
|||
{"py_thread_id", get_py_thread_id, METH_NOARGS},
|
||||
#endif
|
||||
{"set_immortalize_deferred", set_immortalize_deferred, METH_VARARGS},
|
||||
#ifdef _Py_TIER2
|
||||
{"uop_symbols_test", _Py_uop_symbols_test, METH_NOARGS},
|
||||
#endif
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
|
|
|
@ -1496,6 +1496,8 @@ PyCode_GetFreevars(PyCodeObject *code)
|
|||
return _PyCode_GetFreevars(code);
|
||||
}
|
||||
|
||||
#ifdef _Py_TIER2
|
||||
|
||||
static void
|
||||
clear_executors(PyCodeObject *co)
|
||||
{
|
||||
|
@ -1515,6 +1517,8 @@ _PyCode_Clear_Executors(PyCodeObject *code)
|
|||
clear_executors(code);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void
|
||||
deopt_code(PyCodeObject *code, _Py_CODEUNIT *instructions)
|
||||
{
|
||||
|
@ -1739,9 +1743,11 @@ code_dealloc(PyCodeObject *co)
|
|||
|
||||
PyMem_Free(co_extra);
|
||||
}
|
||||
#ifdef _Py_TIER2
|
||||
if (co->co_executors != NULL) {
|
||||
clear_executors(co);
|
||||
}
|
||||
#endif
|
||||
|
||||
Py_XDECREF(co->co_consts);
|
||||
Py_XDECREF(co->co_names);
|
||||
|
|
|
@ -2281,9 +2281,11 @@ static PyTypeObject* static_types[] = {
|
|||
&_PyBufferWrapper_Type,
|
||||
&_PyContextTokenMissing_Type,
|
||||
&_PyCoroWrapper_Type,
|
||||
#ifdef _Py_TIER2
|
||||
&_PyCounterExecutor_Type,
|
||||
&_PyCounterOptimizer_Type,
|
||||
&_PyDefaultOptimizer_Type,
|
||||
#endif
|
||||
&_Py_GenericAliasIterType,
|
||||
&_PyHamtItems_Type,
|
||||
&_PyHamtKeys_Type,
|
||||
|
@ -2304,8 +2306,10 @@ static PyTypeObject* static_types[] = {
|
|||
&_PyPositionsIterator,
|
||||
&_PyUnicodeASCIIIter_Type,
|
||||
&_PyUnion_Type,
|
||||
#ifdef _Py_TIER2
|
||||
&_PyUOpExecutor_Type,
|
||||
&_PyUOpOptimizer_Type,
|
||||
#endif
|
||||
&_PyWeakref_CallableProxyType,
|
||||
&_PyWeakref_ProxyType,
|
||||
&_PyWeakref_RefType,
|
||||
|
|
|
@ -108,6 +108,12 @@
|
|||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
</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" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
|
|
|
@ -37,6 +37,8 @@ echo. --disable-gil Enable experimental support for running without the GIL.
|
|||
echo. --test-marker Enable the test marker within the build.
|
||||
echo. --regen Regenerate all opcodes, grammar and tokens.
|
||||
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.Available flags to avoid building certain modules.
|
||||
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 do_pgo=
|
||||
set pgo_job=-m test --pgo
|
||||
set UseTIER2=0
|
||||
|
||||
:CheckOpts
|
||||
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"=="-V" shift & goto Version
|
||||
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 them in through the environment, but we specify them on the command line
|
||||
rem anyway for visibility so set defaults after this
|
||||
|
@ -179,6 +185,7 @@ echo on
|
|||
/p:DisableGil=%UseDisableGil%^
|
||||
/p:UseTestMarker=%UseTestMarker% %GITProperty%^
|
||||
/p:UseJIT=%UseJIT%^
|
||||
/p:UseTIER2=%UseTIER2%^
|
||||
%1 %2 %3 %4 %5 %6 %7 %8 %9
|
||||
|
||||
@echo off
|
||||
|
|
|
@ -105,6 +105,7 @@
|
|||
<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="'$(UseJIT)' == 'true'">_Py_JIT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="'$(UseTIER2)' != '0'">_Py_TIER2=$(UseTIER2);%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>version.lib;ws2_32.lib;pathcch.lib;bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
|
|
|
@ -2363,6 +2363,7 @@ dummy_func(
|
|||
CHECK_EVAL_BREAKER();
|
||||
assert(oparg <= INSTR_OFFSET());
|
||||
JUMPBY(-oparg);
|
||||
#ifdef _Py_TIER2
|
||||
#if ENABLE_SPECIALIZATION
|
||||
_Py_BackoffCounter counter = this_instr[1].counter;
|
||||
if (backoff_counter_triggers(counter) && this_instr->op.code == JUMP_BACKWARD) {
|
||||
|
@ -2388,6 +2389,7 @@ dummy_func(
|
|||
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
|
||||
}
|
||||
#endif /* ENABLE_SPECIALIZATION */
|
||||
#endif /* _Py_TIER2 */
|
||||
}
|
||||
|
||||
pseudo(JUMP) = {
|
||||
|
@ -2401,6 +2403,7 @@ dummy_func(
|
|||
};
|
||||
|
||||
tier1 inst(ENTER_EXECUTOR, (--)) {
|
||||
#ifdef _Py_TIER2
|
||||
int prevoparg = oparg;
|
||||
CHECK_EVAL_BREAKER();
|
||||
if (this_instr->op.code != ENTER_EXECUTOR ||
|
||||
|
@ -2418,6 +2421,9 @@ dummy_func(
|
|||
tstate->previous_executor = Py_None;
|
||||
Py_INCREF(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 -- )) {
|
||||
|
|
|
@ -755,7 +755,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
|
|||
_Py_CODEUNIT *next_instr;
|
||||
PyObject **stack_pointer;
|
||||
|
||||
#ifndef _Py_JIT
|
||||
#if defined(_Py_TIER2) && !defined(_Py_JIT)
|
||||
/* Tier 2 interpreter state */
|
||||
_PyExecutorObject *current_executor = NULL;
|
||||
const _PyUOpInstruction *next_uop = NULL;
|
||||
|
@ -959,6 +959,7 @@ resume_with_error:
|
|||
goto error;
|
||||
|
||||
|
||||
#ifdef _Py_TIER2
|
||||
|
||||
// Tier 2 is also here!
|
||||
enter_tier_two:
|
||||
|
@ -1113,6 +1114,8 @@ exit_to_trace:
|
|||
|
||||
#endif // _Py_JIT
|
||||
|
||||
#endif // _Py_TIER2
|
||||
|
||||
}
|
||||
|
||||
#if defined(__GNUC__)
|
||||
|
|
|
@ -2492,6 +2492,7 @@
|
|||
(void)this_instr;
|
||||
next_instr += 1;
|
||||
INSTRUCTION_STATS(ENTER_EXECUTOR);
|
||||
#ifdef _Py_TIER2
|
||||
int prevoparg = oparg;
|
||||
CHECK_EVAL_BREAKER();
|
||||
if (this_instr->op.code != ENTER_EXECUTOR ||
|
||||
|
@ -2508,6 +2509,9 @@
|
|||
tstate->previous_executor = Py_None;
|
||||
Py_INCREF(executor);
|
||||
GOTO_TIER_TWO(executor);
|
||||
#else
|
||||
Py_FatalError("ENTER_EXECUTOR is not supported in this build");
|
||||
#endif /* _Py_TIER2 */
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
|
@ -3432,6 +3436,7 @@
|
|||
CHECK_EVAL_BREAKER();
|
||||
assert(oparg <= INSTR_OFFSET());
|
||||
JUMPBY(-oparg);
|
||||
#ifdef _Py_TIER2
|
||||
#if ENABLE_SPECIALIZATION
|
||||
_Py_BackoffCounter counter = this_instr[1].counter;
|
||||
if (backoff_counter_triggers(counter) && this_instr->op.code == JUMP_BACKWARD) {
|
||||
|
@ -3457,6 +3462,7 @@
|
|||
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
|
||||
}
|
||||
#endif /* ENABLE_SPECIALIZATION */
|
||||
#endif /* _Py_TIER2 */
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
|
|
|
@ -1705,10 +1705,12 @@ instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp)
|
|||
);
|
||||
return 0;
|
||||
}
|
||||
#ifdef _Py_TIER2
|
||||
if (code->co_executors != NULL) {
|
||||
_PyCode_Clear_Executors(code);
|
||||
}
|
||||
_Py_Executors_InvalidateDependency(interp, code, 1);
|
||||
#endif
|
||||
int code_len = (int)Py_SIZE(code);
|
||||
/* Exit early to avoid creating instrumentation
|
||||
* data for potential statically allocated code
|
||||
|
@ -1946,7 +1948,9 @@ _PyMonitoring_SetEvents(int tool_id, _PyMonitoringEventSet events)
|
|||
goto done;
|
||||
}
|
||||
set_global_version(tstate, new_version);
|
||||
#ifdef _Py_TIER2
|
||||
_Py_Executors_InvalidateAll(interp, 1);
|
||||
#endif
|
||||
res = instrument_all_executing_code_objects(interp);
|
||||
done:
|
||||
_PyEval_StartTheWorld(interp);
|
||||
|
@ -1986,7 +1990,9 @@ _PyMonitoring_SetLocalEvents(PyCodeObject *code, int tool_id, _PyMonitoringEvent
|
|||
code->_co_instrumentation_version -= MONITORING_VERSION_INCREMENT;
|
||||
}
|
||||
|
||||
#ifdef _Py_TIER2
|
||||
_Py_Executors_InvalidateDependency(interp, code, 1);
|
||||
#endif
|
||||
|
||||
res = instrument_lock_held(code, interp);
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#ifdef _Py_TIER2
|
||||
|
||||
#include "Python.h"
|
||||
#include "opcode.h"
|
||||
#include "pycore_interp.h"
|
||||
|
@ -1622,3 +1624,5 @@ _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* _Py_TIER2 */
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#ifdef _Py_TIER2
|
||||
|
||||
/*
|
||||
* This file contains the support code for CPython's uops optimizer.
|
||||
* It also performs some simple optimizations.
|
||||
|
@ -603,3 +605,5 @@ _Py_uop_analyze_and_optimize(
|
|||
OPT_STAT_INC(optimizer_successes);
|
||||
return length;
|
||||
}
|
||||
|
||||
#endif /* _Py_TIER2 */
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#ifdef _Py_TIER2
|
||||
|
||||
#include "Python.h"
|
||||
|
||||
|
@ -506,3 +507,5 @@ fail:
|
|||
Py_XDECREF(val_43);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* _Py_TIER2 */
|
||||
|
|
|
@ -624,9 +624,11 @@ static int
|
|||
builtins_dict_watcher(PyDict_WatchEvent event, PyObject *dict, PyObject *key, PyObject *new_value)
|
||||
{
|
||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||
#ifdef _Py_TIER2
|
||||
if (interp->rare_events.builtin_dict < _Py_MAX_ALLOWED_BUILTINS_MODIFICATIONS) {
|
||||
_Py_Executors_InvalidateAll(interp, 1);
|
||||
}
|
||||
#endif
|
||||
RARE_EVENT_INTERP_INC(interp, builtin_dict);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1272,30 +1274,30 @@ init_interp_main(PyThreadState *tstate)
|
|||
}
|
||||
|
||||
// Turn on experimental tier 2 (uops-based) optimizer
|
||||
// This is also needed when the JIT is enabled
|
||||
#ifdef _Py_TIER2
|
||||
if (is_main_interp) {
|
||||
#ifndef _Py_JIT
|
||||
// No JIT, maybe use the tier two interpreter:
|
||||
char *envvar = Py_GETENV("PYTHON_UOPS");
|
||||
int enabled = envvar != NULL && *envvar > '0';
|
||||
if (_Py_get_xoption(&config->xoptions, L"uops") != NULL) {
|
||||
enabled = 1;
|
||||
int enabled = 1;
|
||||
#if _Py_TIER2 & 2
|
||||
enabled = 0;
|
||||
#endif
|
||||
char *env = Py_GETENV("PYTHON_JIT");
|
||||
if (env && *env != '\0') {
|
||||
// PYTHON_JIT=0|1 overrides the default
|
||||
enabled = *env != '0';
|
||||
}
|
||||
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();
|
||||
if (opt == NULL) {
|
||||
return _PyStatus_ERR("can't initialize optimizer");
|
||||
}
|
||||
if (PyUnstable_SetOptimizer((_PyOptimizerObject *)opt)) {
|
||||
return _PyStatus_ERR("can't initialize optimizer");
|
||||
return _PyStatus_ERR("can't install optimizer");
|
||||
}
|
||||
Py_DECREF(opt);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!is_main_interp) {
|
||||
// The main interpreter is handled in Py_Main(), for now.
|
||||
|
@ -1655,10 +1657,12 @@ finalize_modules(PyThreadState *tstate)
|
|||
{
|
||||
PyInterpreterState *interp = tstate->interp;
|
||||
|
||||
#ifdef _Py_TIER2
|
||||
// Invalidate all executors and turn off tier 2 optimizer
|
||||
_Py_Executors_InvalidateAll(interp, 0);
|
||||
_PyOptimizerObject *old = _Py_SetOptimizer(interp, NULL);
|
||||
Py_XDECREF(old);
|
||||
#endif
|
||||
|
||||
// Stop watching __builtin__ modifications
|
||||
PyDict_Unwatch(0, interp->builtins);
|
||||
|
|
|
@ -653,8 +653,10 @@ init_interpreter(PyInterpreterState *interp,
|
|||
}
|
||||
interp->sys_profile_initialized = false;
|
||||
interp->sys_trace_initialized = false;
|
||||
#ifdef _Py_TIER2
|
||||
(void)_Py_SetOptimizer(interp, NULL);
|
||||
interp->executor_list_head = NULL;
|
||||
#endif
|
||||
if (interp != &runtime->_main_interpreter) {
|
||||
/* Fix the self-referential, statically initialized fields. */
|
||||
interp->dtoa = (struct _dtoa_state)_dtoa_state_INIT(interp);
|
||||
|
@ -806,9 +808,11 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
|
|||
tstate->_status.cleared = 0;
|
||||
}
|
||||
|
||||
#ifdef _Py_TIER2
|
||||
_PyOptimizerObject *old = _Py_SetOptimizer(interp, NULL);
|
||||
assert(old != NULL);
|
||||
Py_DECREF(old);
|
||||
#endif
|
||||
|
||||
/* It is possible that any of the objects below have a finalizer
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
#ifdef _Py_TIER2
|
||||
if (eval_frame != NULL) {
|
||||
_Py_Executors_InvalidateAll(interp, 1);
|
||||
}
|
||||
#endif
|
||||
RARE_EVENT_INC(set_eval_frame_func);
|
||||
interp->eval_frame = eval_frame;
|
||||
}
|
||||
|
|
|
@ -2165,8 +2165,10 @@ static PyObject *
|
|||
sys__clear_internal_caches_impl(PyObject *module)
|
||||
/*[clinic end generated code: output=0ee128670a4966d6 input=253e741ca744f6e8]*/
|
||||
{
|
||||
#ifdef _Py_TIER2
|
||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||
_Py_Executors_InvalidateAll(interp, 0);
|
||||
#endif
|
||||
PyType_ClearCache();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
|
|
@ -411,6 +411,7 @@ NON_ESCAPING_FUNCTIONS = (
|
|||
"PyCell_New",
|
||||
"PyFloat_AS_DOUBLE",
|
||||
"_PyFrame_PushUnchecked",
|
||||
"Py_FatalError",
|
||||
)
|
||||
|
||||
ESCAPING_FUNCTIONS = (
|
||||
|
|
|
@ -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).
|
||||
|
||||
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:
|
||||
|
||||
### Linux
|
||||
|
||||
Install LLVM 16 on Ubuntu/Debian:
|
||||
Install LLVM 18 on Ubuntu/Debian:
|
||||
|
||||
```sh
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 16
|
||||
sudo ./llvm.sh 18
|
||||
```
|
||||
|
||||
### macOS
|
||||
|
|
|
@ -1818,7 +1818,7 @@ Optional Features:
|
|||
--disable-gil enable experimental support for running without the
|
||||
GIL (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
|
||||
(default is no)
|
||||
--enable-optimizations enable expensive, stable optimizations (PGO, etc.)
|
||||
|
@ -8229,11 +8229,19 @@ else $as_nop
|
|||
enable_experimental_jit=no
|
||||
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
|
||||
then :
|
||||
|
||||
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"
|
||||
JIT_STENCILS_H="jit_stencils.h"
|
||||
if test "x$Py_DEBUG" = xtrue
|
||||
|
|
13
configure.ac
13
configure.ac
|
@ -1768,14 +1768,23 @@ fi
|
|||
# Check for --enable-experimental-jit:
|
||||
AC_MSG_CHECKING([for --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)])],
|
||||
[],
|
||||
[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],
|
||||
[no],
|
||||
[],
|
||||
[AS_VAR_APPEND([CFLAGS_NODIST], [" -D_Py_JIT"])
|
||||
[AS_VAR_APPEND([CFLAGS_NODIST], [" $enable_experimental_jit"])
|
||||
AS_VAR_SET([REGEN_JIT_COMMAND],
|
||||
["\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py $host"])
|
||||
AS_VAR_SET([JIT_STENCILS_H], ["jit_stencils.h"])
|
||||
|
|
Loading…
Reference in New Issue