GH-133231: Add JIT utilities in `sys._jit` by brandtbucher · Pull Request #133233 · python/cpython · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions Doc/library/sys.rst
8 changes: 8 additions & 0 deletions Doc/using/cmdline.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1279,6 +1279,14 @@ conflict.

.. versionadded:: 3.14

.. envvar:: PYTHON_JIT

On builds where experimental just-in-time compilation is available, this
variable can force the JIT to be disabled (``0``) or enabled (``1``) at
interpreter startup.

.. versionadded:: 3.13
Comment on lines +1282 to +1288

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should backport this, should it be split out into a different PR for ease?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can just backport it separately, since I need the ref for this PR's changes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth using miss-islington for the backport here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll just do it manually in a bit.


Debug-mode variables
~~~~~~~~~~~~~~~~~~~~

Expand Down
40 changes: 4 additions & 36 deletions Lib/test/libregrtest/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,43 +335,11 @@ def get_build_info():
build.append('with_assert')

# --enable-experimental-jit
tier2 = re.search('-D_Py_TIER2=([0-9]+)', cflags)
if tier2:
tier2 = int(tier2.group(1))

if not sys.flags.ignore_environment:
PYTHON_JIT = os.environ.get('PYTHON_JIT', None)
if PYTHON_JIT:
PYTHON_JIT = (PYTHON_JIT != '0')
else:
PYTHON_JIT = None

if tier2 == 1: # =yes
if PYTHON_JIT == False:
jit = 'JIT=off'
else:
jit = 'JIT'
elif tier2 == 3: # =yes-off
if PYTHON_JIT:
jit = 'JIT'
if sys._jit.is_available():
if sys._jit.is_enabled():
build.append("JIT")
else:
jit = 'JIT=off'
elif tier2 == 4: # =interpreter
if PYTHON_JIT == False:
jit = 'JIT-interpreter=off'
else:
jit = 'JIT-interpreter'
elif tier2 == 6: # =interpreter-off (Secret option!)
if PYTHON_JIT:
jit = 'JIT-interpreter'
else:
jit = 'JIT-interpreter=off'
elif '-D_Py_JIT' in cflags:
jit = 'JIT'
else:
jit = None
if jit:
build.append(jit)
build.append("JIT (disabled)")

# --enable-framework=name
framework = sysconfig.get_config_var('PYTHONFRAMEWORK')
Expand Down
10 changes: 3 additions & 7 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2648,13 +2648,9 @@ def exceeds_recursion_limit():

Py_TRACE_REFS = hasattr(sys, 'getobjects')

try:
from _testinternalcapi import jit_enabled
except ImportError:
requires_jit_enabled = requires_jit_disabled = unittest.skip("requires _testinternalcapi")
else:
requires_jit_enabled = unittest.skipUnless(jit_enabled(), "requires JIT enabled")
requires_jit_disabled = unittest.skipIf(jit_enabled(), "requires JIT disabled")
_JIT_ENABLED = sys._jit.is_enabled()
requires_jit_enabled = unittest.skipUnless(_JIT_ENABLED, "requires JIT enabled")
requires_jit_disabled = unittest.skipIf(_JIT_ENABLED, "requires JIT disabled")


_BASE_COPY_SRC_DIR_IGNORED_NAMES = frozenset({
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_capi/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ def test_getitem_with_error(self):
CURRENT_THREAD_REGEX +
r' File .*, line 6 in <module>\n'
r'\n'
r'Extension modules: _testcapi, _testinternalcapi \(total: 2\)\n')
r'Extension modules: _testcapi \(total: 1\)\n')
else:
# Python built with NDEBUG macro defined:
# test _Py_CheckFunctionResult() instead.
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -1336,7 +1336,7 @@ def test_loop_quicken(self):
# Loop can trigger a quicken where the loop is located
self.code_quicken(loop_test)
got = self.get_disassembly(loop_test, adaptive=True)
jit = import_helper.import_module("_testinternalcapi").jit_enabled()
jit = sys._jit.is_enabled()
expected = dis_loop_test_quickened_code.format("JIT" if jit else "NO_JIT")
self.do_disassembly_compare(got, expected)

Expand Down
58 changes: 58 additions & 0 deletions Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -2196,6 +2196,64 @@ def test_remote_exec_in_process_without_debug_fails_xoption(self):
self.assertIn(b"Remote debugging is not enabled", err)
self.assertEqual(out, b"")

class TestSysJIT(unittest.TestCase):

def test_jit_is_available(self):
available = sys._jit.is_available()
script = f"import sys; assert sys._jit.is_available() is {available}"
assert_python_ok("-c", script, PYTHON_JIT="0")
assert_python_ok("-c", script, PYTHON_JIT="1")

def test_jit_is_enabled(self):
available = sys._jit.is_available()
script = "import sys; assert sys._jit.is_enabled() is {enabled}"
assert_python_ok("-c", script.format(enabled=False), PYTHON_JIT="0")
assert_python_ok("-c", script.format(enabled=available), PYTHON_JIT="1")

def test_jit_is_active(self):
available = sys._jit.is_available()
script = textwrap.dedent(
"""
import _testcapi
import _testinternalcapi
import sys

def frame_0_interpreter() -> None:
assert sys._jit.is_active() is False

def frame_1_interpreter() -> None:
assert sys._jit.is_active() is False
frame_0_interpreter()
assert sys._jit.is_active() is False

def frame_2_jit(expected: bool) -> None:
# Inlined into the last loop of frame_3_jit:
assert sys._jit.is_active() is expected
# Insert C frame:
_testcapi.pyobject_vectorcall(frame_1_interpreter, None, None)
assert sys._jit.is_active() is expected

def frame_3_jit() -> None:
# JITs just before the last loop:
for i in range(_testinternalcapi.TIER2_THRESHOLD + 1):
# Careful, doing this in the reverse order breaks tracing:
expected = {enabled} and i == _testinternalcapi.TIER2_THRESHOLD
assert sys._jit.is_active() is expected
frame_2_jit(expected)
assert sys._jit.is_active() is expected

def frame_4_interpreter() -> None:
assert sys._jit.is_active() is False
frame_3_jit()
assert sys._jit.is_active() is False

assert sys._jit.is_active() is False
frame_4_interpreter()
assert sys._jit.is_active() is False
"""
)
assert_python_ok("-c", script.format(enabled=False), PYTHON_JIT="0")
assert_python_ok("-c", script.format(enabled=available), PYTHON_JIT="1")


if __name__ == "__main__":
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Add new utilities of observing JIT compilation:
:func:`sys._jit.is_available`, :func:`sys._jit.is_enabled`, and
:func:`sys._jit.is_active`.
8 changes: 0 additions & 8 deletions Modules/_testinternalcapi.c
86 changes: 85 additions & 1 deletion Python/clinic/sysmodule.c.h

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

Loading