Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

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

Merged
merged 42 commits into from May 1, 2024
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
1eb8fea
Step 1: replace 'python -Xuops' with 'configure --enable-experimental…
gvanrossum Apr 26, 2024
0863c17
Make JIT imply TIER2, for convenience
gvanrossum Apr 26, 2024
9e43331
Step 2: Remove all code from optimizer*.c unless TIER2; fix deps exce…
gvanrossum Apr 26, 2024
064de58
Remove TIER2 stuff from _testinternalcapi
gvanrossum Apr 26, 2024
13b8ef0
Fix test_dis.py: _opcode.get_executor() may raise RuntimeError
gvanrossum Apr 26, 2024
edbaeef
Fix test_regrtest (by tweaking without_getoptimizer)
gvanrossum Apr 26, 2024
9311198
Fix test_optimizer.py
gvanrossum Apr 26, 2024
300ae6d
Fix test_opcache
gvanrossum Apr 27, 2024
db911e4
Fix test_monitoring
gvanrossum Apr 27, 2024
8cb71dd
Fix test_capi.test_opt (by disabling unless get_optimizer exists)
gvanrossum Apr 27, 2024
7e79509
Fix _opcode.c
gvanrossum Apr 27, 2024
638b7ec
Fix warning in sysmodule.c
gvanrossum Apr 27, 2024
8fc905f
When compiling _testinternalcapi.c, define _Py_JIT
gvanrossum Apr 29, 2024
877a005
Try making WASI Debug recursion limit 400
gvanrossum Apr 29, 2024
e4464d4
Merge main into tier2-flag
gvanrossum Apr 29, 2024
ffd7722
Revert "Try making WASI Debug recursion limit 400"
gvanrossum Apr 30, 2024
6e2777b
Update jit/README.md to require LLVM 18 everywhere
gvanrossum Apr 30, 2024
f228a06
Rip out --enable-experimental-tier2, in favor of --enable-experimenta…
gvanrossum Apr 30, 2024
70e18a5
Rip out _Py_TIER2, replace with _Py_JIT
gvanrossum Apr 30, 2024
17f1674
configure: set _Py_JIT={1|3|4} (for now)
gvanrossum Apr 30, 2024
e23c688
Try making WASI Debug recursion limit 300
gvanrossum Apr 29, 2024
ebd8025
Revert "Rip out _Py_TIER2, replace with _Py_JIT"
gvanrossum Apr 30, 2024
e367c27
Make the configure script set both _Py_JIT and _Py_TIER2, as appropriate
gvanrossum Apr 30, 2024
8681813
Pass flag bit in _Py_TIER2 instead of _Py_JIT
gvanrossum Apr 30, 2024
8f89b3b
Honor default-off and PYTHON_JIT=0|1
gvanrossum Apr 30, 2024
fe94220
Add interpreter-default-off as secret menu item
gvanrossum Apr 30, 2024
2963ccb
Revert "Try making WASI Debug recursion limit 300"
gvanrossum Apr 30, 2024
783c3cc
Merge remote-tracking branch 'origin/main' into tier2-flag
gvanrossum Apr 30, 2024
f45f7ca
Also predefine _Py_TIER2 on Windows if UseJIT is true
gvanrossum Apr 30, 2024
391bd46
Trashcan consumes more C stack (experimental, targeting WASI Debug bu…
gvanrossum Apr 30, 2024
c5d8c0a
Update what's new for new --enable-experimental-jit options
gvanrossum Apr 30, 2024
f45bf67
Revert "Trashcan consumes more C stack (experimental, targeting WASI …
gvanrossum Apr 30, 2024
a6cc5ee
Just disable test_trashcan_16602 on WASI Debug builds
gvanrossum Apr 30, 2024
35cb5bd
Fix test_weakref :-(
gvanrossum Apr 30, 2024
8276a9a
Shorten to --enable-exerimental-jit=yes-off
gvanrossum Apr 30, 2024
f950e25
Tweak code in test/support/__init__.py without_optimizer()
gvanrossum Apr 30, 2024
6be70c0
Fix news blurb
gvanrossum Apr 30, 2024
fdbe440
Fix typo in _opcode.c
gvanrossum Apr 30, 2024
3c919bb
Shut up annoying warning
gvanrossum Apr 30, 2024
d5a3ff0
Use Py_FatalError for ENTER_EXECUTOR in non-TIER2 build
gvanrossum Apr 30, 2024
07c485e
Add --experimental-jit-{off|interpreter} to PCbuild/build.bat
gvanrossum May 1, 2024
a2d4f30
Revert "Shut up annoying warning"
gvanrossum May 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 23 additions & 7 deletions Doc/whatsnew/3.13.rst
Expand Up @@ -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.

Expand All @@ -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.)
brandtbucher marked this conversation as resolved.
Show resolved Hide resolved

See :pep:`744` for more details.

(JIT by Brandt Bucher, inspired by a paper by Haoran Xu and Fredrik Kjolstad.
Expand Down
4 changes: 2 additions & 2 deletions Include/internal/pycore_opcode_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Include/internal/pycore_uop_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Lib/dis.py
Expand Up @@ -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:
Expand Down
8 changes: 4 additions & 4 deletions Lib/test/support/__init__.py
Expand Up @@ -2539,17 +2539,17 @@ def exceeds_recursion_limit():
# 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


Expand Down
8 changes: 8 additions & 0 deletions Lib/test/test_capi/test_opt.py
Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -215,6 +219,8 @@ def f():


@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):

Expand Down Expand Up @@ -579,6 +585,8 @@ def testfunc(n):


@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):

Expand Down
10 changes: 6 additions & 4 deletions Lib/test/test_monitoring.py
Expand Up @@ -1831,15 +1831,17 @@ class TestOptimizer(MonitoringTestBase, unittest.TestCase):

def setUp(self):
_testinternalcapi = import_module("_testinternalcapi")
self.old_opt = _testinternalcapi.get_optimizer()
opt = _testinternalcapi.new_counter_optimizer()
_testinternalcapi.set_optimizer(opt)
if hasattr(_testinternalcapi, "get_optimizer"):
self.old_opt = _testinternalcapi.get_optimizer()
opt = _testinternalcapi.new_counter_optimizer()
_testinternalcapi.set_optimizer(opt)
super(TestOptimizer, self).setUp()

def tearDown(self):
super(TestOptimizer, self).tearDown()
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_func(x):
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_opcache.py
Expand Up @@ -16,6 +16,8 @@

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:
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_optimizer.py
Expand Up @@ -80,6 +80,8 @@ def func(x=0):

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()

Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_weakref.py
Expand Up @@ -17,6 +17,7 @@
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
Expand Down Expand Up @@ -960,6 +961,7 @@ def test_hashing(self):
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
Expand Down
@@ -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``.
6 changes: 6 additions & 0 deletions Modules/_opcode.c
Expand Up @@ -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
Expand Down
13 changes: 12 additions & 1 deletion Modules/_testinternalcapi.c
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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},
Expand Down Expand Up @@ -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 */
};

Expand Down
6 changes: 6 additions & 0 deletions Objects/codeobject.c
Expand Up @@ -1496,6 +1496,8 @@ PyCode_GetFreevars(PyCodeObject *code)
return _PyCode_GetFreevars(code);
}

#ifdef _Py_TIER2

static void
clear_executors(PyCodeObject *co)
{
Expand All @@ -1515,6 +1517,8 @@ _PyCode_Clear_Executors(PyCodeObject *code)
clear_executors(code);
}

#endif

static void
deopt_code(PyCodeObject *code, _Py_CODEUNIT *instructions)
{
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions Objects/object.c
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions PCbuild/_testinternalcapi.vcxproj
Expand Up @@ -108,6 +108,12 @@
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions Condition="'$(UseJIT)' == 'true'">_Py_JIT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(UseJIT)' == 'true'">_Py_TIER2;%(PreprocessorDefinitions)</PreprocessorDefinitions>
brandtbucher marked this conversation as resolved.
Show resolved Hide resolved
</ClCompile>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
Expand Down
1 change: 1 addition & 0 deletions PCbuild/pythoncore.vcxproj
Expand Up @@ -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="'$(UseJIT)' == 'true'">_Py_TIER2;%(PreprocessorDefinitions)</PreprocessorDefinitions>
brandtbucher marked this conversation as resolved.
Show resolved Hide resolved
</ClCompile>
<Link>
<AdditionalDependencies>version.lib;ws2_32.lib;pathcch.lib;bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
Expand Down
6 changes: 6 additions & 0 deletions Python/bytecodes.c
Expand Up @@ -2361,6 +2361,7 @@ dummy_func(
CHECK_EVAL_BREAKER();
assert(oparg <= INSTR_OFFSET());
JUMPBY(-oparg);
#ifdef _Py_TIER2
#if ENABLE_SPECIALIZATION
brandtbucher marked this conversation as resolved.
Show resolved Hide resolved
_Py_BackoffCounter counter = this_instr[1].counter;
if (backoff_counter_triggers(counter) && this_instr->op.code == JUMP_BACKWARD) {
Expand All @@ -2386,6 +2387,7 @@ dummy_func(
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
}
#endif /* ENABLE_SPECIALIZATION */
#endif /* _Py_TIER2 */
brandtbucher marked this conversation as resolved.
Show resolved Hide resolved
}

pseudo(JUMP) = {
Expand All @@ -2399,6 +2401,7 @@ dummy_func(
};

tier1 inst(ENTER_EXECUTOR, (--)) {
#ifdef _Py_TIER2
int prevoparg = oparg;
CHECK_EVAL_BREAKER();
if (this_instr->op.code != ENTER_EXECUTOR ||
Expand All @@ -2416,6 +2419,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 -- )) {
Expand Down