Repository navigation
GH-135904: Add tests for the JIT build process #136766
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
Changes from 1 commit
7a6d819
a322ad4
e1eb85d
f4c05b3
00cd7e3
202fb6f
4e5554c
9db0563
c4c3ccc
c6fc7bd
558c83b
867a686
7c9c3ff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,49 +1,67 @@ | ||
|
|
||
| import pathlib | ||
| import shlex | ||
| import sys | ||
| import sysconfig | ||
| import tempfile | ||
| import test.support | ||
| import unittest | ||
|
|
||
| import test.support.script_helper | ||
|
|
||
| import unittest | ||
|
|
||
| _CPYTHON = pathlib.Path(test.support.REPO_ROOT).resolve() | ||
| _TOOLS_JIT = _CPYTHON / "Tools" / "jit" | ||
| _TOOLS_JIT_TEST = _TOOLS_JIT / "test" | ||
| _TOOLS_JIT_TEST_TEST_EXECUTOR_CASES_C_H = _TOOLS_JIT_TEST / "test_executor_cases.c.h" | ||
| _TOOLS_JIT_BUILD_PY = _TOOLS_JIT / "build.py" | ||
|
|
||
| @unittest.skipIf(test.support.Py_DEBUG, "XXX") | ||
| @unittest.skipUnless(sys._jit.is_available(), "XXX") | ||
| @unittest.skipIf(test.support.Py_GIL_DISABLED, "XXX") | ||
| @unittest.skipUnless(sysconfig.is_python_build(), "XXX") | ||
|
|
||
| @test.support.cpython_only | ||
| @unittest.skipIf(test.support.Py_DEBUG, "Debug stencils aren't tested.") | ||
| @unittest.skipIf(test.support.Py_GIL_DISABLED, "Free-threaded stencils aren't tested.") | ||
| @unittest.skipUnless(sysconfig.is_python_build(), "Requires a local Python build.") | ||
| class TestJITStencils(unittest.TestCase): | ||
|
|
||
| def _build_jit_stencils(self, target: str) -> str: | ||
| with tempfile.TemporaryDirectory() as work: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, I'll take a look!
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it, like, better? Haha.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Couldn't tell you 😅 but it seems quite common. Though both are used so it probably doesn't matter :) |
||
| jit_stencils_h = pathlib.Path(work, f"jit_stencils-{target}.h").resolve() | ||
| pyconfig_h = pathlib.Path(sysconfig.get_config_h_filename()).resolve() | ||
| result, args = test.support.script_helper.run_python_until_end( | ||
| _TOOLS_JIT_BUILD_PY, | ||
| "--input-file", _TOOLS_JIT_TEST_TEST_EXECUTOR_CASES_C_H, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is just for my understanding. is this needed in case we need to regenerate this during development and we need to test it, right? |
||
| "--output-dir", jit_stencils_h.parent, | ||
| "--pyconfig-dir", pyconfig_h.parent, | ||
| target, | ||
| __isolated=False, | ||
| ) | ||
| if result.rc: | ||
| self.skipTest(f"Build failed: {shlex.join(map(str, args))}") | ||
| body = jit_stencils_h.read_text() | ||
| # Strip out two lines of header comments: | ||
| _, _, body = body.split("\n", 2) | ||
| return body | ||
|
|
||
| def _check_jit_stencils( | ||
| self, expected: str, actual: str, test_jit_stencils_h: pathlib.Path | ||
| ) -> None: | ||
| try: | ||
| self.assertEqual(expected.strip("\n"), actual.strip("\n")) | ||
| except AssertionError as e: | ||
| # Make it easy to re-validate the expected output: | ||
| relative = test_jit_stencils_h.relative_to(_CPYTHON) | ||
| message = f"If this is expected, replace {relative} with:" | ||
| banner = "=" * len(message) | ||
| e.add_note("\n".join([banner, message, banner])) | ||
| e.add_note(actual) | ||
| raise | ||
|
|
||
| def test_jit_stencils(self): | ||
| self.maxDiff = None | ||
| found = False | ||
| pyconfig_dir = pathlib.Path(sysconfig.get_config_h_filename()).parent | ||
| with tempfile.TemporaryDirectory() as work: | ||
| output_dir = pathlib.Path(work).resolve() | ||
| for test_jit_stencils_h in sorted(_TOOLS_JIT_TEST.glob("test_jit_stencils-*.h")): | ||
| > | ||
| jit_stencils_h = output_dir / f"jit_stencils-{target}.h" | ||
| with self.subTest(target): | ||
| # relative = jit_stencils_h.relative_to(_CPYTHON) | ||
| result, args = test.support.script_helper.run_python_until_end( | ||
| _TOOLS_JIT_BUILD_PY, | ||
| "--input-file", _TOOLS_JIT_TEST / "test_executor_cases.c.h", | ||
| "--output-dir", output_dir, | ||
| "--pyconfig-dir", pyconfig_dir, | ||
| target, | ||
| __isolated=False | ||
| ) | ||
| if result.rc: | ||
| self.skipTest(shlex.join(map(str, args))) | ||
| found = True | ||
| expected = test_jit_stencils_h.read_text() | ||
| actual = "".join(jit_stencils_h.read_text().splitlines(True)[3:]) | ||
| self.assertEqual(expected, actual) | ||
| self.assertTrue(found, "No JIT stencil tests run!") | ||
| for test_jit_stencils_h in _TOOLS_JIT_TEST.glob("test_jit_stencils-*.h"): | ||
| > | ||
| with self.subTest(target): | ||
| expected = test_jit_stencils_h.read_text() | ||
| actual = self._build_jit_stencils(target) | ||
| found = True | ||
| self._check_jit_stencils(expected, actual, test_jit_stencils_h) | ||
| # This is a local build. If the JIT is available, at least one test should run: | ||
| assert found or not sys._jit.is_available(), "No JIT stencils built!" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,27 +1,29 @@ | ||
| case 0: { | ||
| // Zero-length jumps should be removed: | ||
| break; | ||
| } | ||
|
|
||
| case 1: { | ||
| if (CURRENT_OPARG()) { | ||
| JUMP_TO_JUMP_TARGET(); | ||
| // -Os duplicates less code than -O3: | ||
| PyAPI_DATA(bool) sausage; | ||
| PyAPI_DATA(bool) spammed; | ||
| PyAPI_FUNC(void) order_eggs_and_bacon(void); | ||
| PyAPI_FUNC(void) order_eggs_sausage_and_bacon(void); | ||
| if (!sausage) { | ||
| order_eggs_and_bacon(); | ||
| } | ||
| else { | ||
| order_eggs_sausage_and_bacon(); | ||
| } | ||
| spammed = false; | ||
| break; | ||
| } | ||
|
|
||
| case 2: { | ||
| if (CURRENT_OPARG()) { | ||
| // The assembly optimizer inverts hot branches: | ||
| PyAPI_DATA(bool) spam; | ||
| if (spam) { | ||
| JUMP_TO_ERROR(); | ||
| } | ||
| break; | ||
| } | ||
|
|
||
| case 3: { | ||
| GOTO_TIER_ONE((void *)CURRENT_OPERAND0() + CURRENT_TARGET()); | ||
| break; | ||
| } | ||
|
|
||
| case 4: { | ||
| GOTO_TIER_TWO((void *)CURRENT_OPERAND1()); | ||
| break; | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.