From 7bc5cf6479a83c2b99eb659496f4dd21c90b8b1c Mon Sep 17 00:00:00 2001 From: Manjusaka Date: Mon, 2 Jun 2025 20:13:00 +0800 Subject: [PATCH 1/7] gh-131798: JIT: replace _CHECK_METHOD_VERSION with _CHECK_FUNCTION_VERSION_INLINE Signed-off-by: Manjusaka --- Lib/test/test_capi/test_opt.py | 25 +++++++++++++++++++++++++ Python/optimizer_bytecodes.c | 10 ++++++++++ Python/optimizer_cases.c.h | 10 ++++++++++ 3 files changed, 45 insertions(+) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index cb6eae484149ee..6256d769e840cf 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -1370,6 +1370,23 @@ def testfunc(n): # Removed guard self.assertNotIn("_CHECK_FUNCTION_EXACT_ARGS", uops) + def test_method_guards_removed_or_reduced(self): + + def testfunc(n): + for i in range(n): + test_bound_method(i) + + + testfunc(TIER2_THRESHOLD) + + ex = get_first_executor(testfunc) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_PUSH_FRAME", uops) + # Strength reduced version + self.assertIn("_CHECK_FUNCTION_VERSION_INLINE", uops) + self.assertNotIn("_CHECK_METHOD_VERSION", uops) + def test_jit_error_pops(self): """ Tests that the correct number of pops are inserted into the @@ -2219,6 +2236,14 @@ def f(n): self.assertNotIn("_LOAD_ATTR_METHOD_NO_DICT", uops) self.assertNotIn("_LOAD_ATTR_METHOD_LAZY_DICT", uops) +class TestObject: + def test(self, *args, **kwargs): + return args[0] + +temp_object = TestObject() + +test_bound_method = TestObject.test.__get__(temp_object) + def global_identity(x): return x diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index e1209209660f92..c3922b3e511cc8 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -708,6 +708,16 @@ dummy_func(void) { sym_set_type(callable, &PyFunction_Type); } + op(_CHECK_METHOD_VERSION, (func_version/2, callable, null, unused[oparg] -- callable, null, unused[oparg])) { + if (sym_is_const(ctx, callable) && sym_matches_type(callable, &PyMethod_Type)) { + assert(PyMethod_Check(sym_get_const(ctx, callable))); + PyMethodObject *method = (PyMethodObject *)sym_get_const(ctx, callable); + REPLACE_OP(this_instr, _CHECK_FUNCTION_VERSION_INLINE, 0, func_version); + this_instr->operand1 = (uintptr_t)method->im_func; + } + sym_set_type(callable, &PyMethod_Type); + } + op(_CHECK_FUNCTION_EXACT_ARGS, (callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { assert(sym_matches_type(callable, &PyFunction_Type)); if (sym_is_const(ctx, callable)) { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index db86edcc7859b5..5fec71805bb059 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1864,6 +1864,16 @@ } case _CHECK_METHOD_VERSION: { + JitOptSymbol *callable; + callable = stack_pointer[-2 - oparg]; + uint32_t func_version = (uint32_t)this_instr->operand0; + if (sym_is_const(ctx, callable) && sym_matches_type(callable, &PyMethod_Type)) { + assert(PyMethod_Check(sym_get_const(ctx, callable))); + PyMethodObject *method = (PyMethodObject *)sym_get_const(ctx, callable); + REPLACE_OP(this_instr, _CHECK_FUNCTION_VERSION_INLINE, 0, func_version); + this_instr->operand1 = (uintptr_t)method->im_func; + } + sym_set_type(callable, &PyMethod_Type); break; } From baaff8e16cac5ec30ebe3eeb208176999e0067ae Mon Sep 17 00:00:00 2001 From: Manjusaka Date: Mon, 2 Jun 2025 20:14:02 +0800 Subject: [PATCH 2/7] Update news Signed-off-by: Manjusaka --- .../2025-06-02-20-13-37.gh-issue-131798.JQRFvR.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-02-20-13-37.gh-issue-131798.JQRFvR.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-02-20-13-37.gh-issue-131798.JQRFvR.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-02-20-13-37.gh-issue-131798.JQRFvR.rst new file mode 100644 index 00000000000000..8ebbe6ea313d40 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-02-20-13-37.gh-issue-131798.JQRFvR.rst @@ -0,0 +1 @@ +Replace ``_CHECK_METHOD_VERSION`` with ``_CHECK_FUNCTION_VERSION_INLINE`` From 68f93b530cc584ea095ce6d0a62eec9ea2434dd2 Mon Sep 17 00:00:00 2001 From: Manjusaka Date: Mon, 2 Jun 2025 20:15:37 +0800 Subject: [PATCH 3/7] fix code style Signed-off-by: Manjusaka --- Lib/test/test_capi/test_opt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 6256d769e840cf..d01ad6a436e9b7 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2240,9 +2240,9 @@ class TestObject: def test(self, *args, **kwargs): return args[0] -temp_object = TestObject() +test_object = TestObject() -test_bound_method = TestObject.test.__get__(temp_object) +test_bound_method = TestObject.test.__get__(test_object) def global_identity(x): From cc8ecf130bcc9f46680a72644a36b33b3134b35c Mon Sep 17 00:00:00 2001 From: Manjusaka Date: Mon, 2 Jun 2025 20:17:38 +0800 Subject: [PATCH 4/7] fix code style Signed-off-by: Manjusaka --- Lib/test/test_capi/test_opt.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index d01ad6a436e9b7..07321d58823afb 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2241,7 +2241,6 @@ def test(self, *args, **kwargs): return args[0] test_object = TestObject() - test_bound_method = TestObject.test.__get__(test_object) From 2841780c8b409f1c63d017c3cd6349022aa840f7 Mon Sep 17 00:00:00 2001 From: Nadeshiko Manju Date: Sat, 7 Jun 2025 08:16:44 +0800 Subject: [PATCH 5/7] Update Misc/NEWS.d/next/Core_and_Builtins/2025-06-02-20-13-37.gh-issue-131798.JQRFvR.rst Co-authored-by: Brandt Bucher --- .../2025-06-02-20-13-37.gh-issue-131798.JQRFvR.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-02-20-13-37.gh-issue-131798.JQRFvR.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-02-20-13-37.gh-issue-131798.JQRFvR.rst index 8ebbe6ea313d40..0e68c793e5e133 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-02-20-13-37.gh-issue-131798.JQRFvR.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-02-20-13-37.gh-issue-131798.JQRFvR.rst @@ -1 +1 @@ -Replace ``_CHECK_METHOD_VERSION`` with ``_CHECK_FUNCTION_VERSION_INLINE`` +Optimize ``_CHECK_METHOD_VERSION`` into ``_CHECK_FUNCTION_VERSION_INLINE`` in JIT-compiled code. From 41838e9e293f85ec343643ed535c67998e3da878 Mon Sep 17 00:00:00 2001 From: Manjusaka Date: Sat, 7 Jun 2025 08:31:15 +0800 Subject: [PATCH 6/7] fix review idea Signed-off-by: Manjusaka --- Lib/test/test_capi/test_opt.py | 25 +++++++++++-------------- Python/optimizer_bytecodes.c | 2 +- Python/optimizer_cases.c.h | 2 +- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 07321d58823afb..6f776fea730bab 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -1371,15 +1371,20 @@ def testfunc(n): self.assertNotIn("_CHECK_FUNCTION_EXACT_ARGS", uops) def test_method_guards_removed_or_reduced(self): + class TestObject: + def test(self, *args, **kwargs): + return args[0] + + test_object = TestObject() + test_bound_method = TestObject.test.__get__(test_object) def testfunc(n): + result = 0 for i in range(n): - test_bound_method(i) - - - testfunc(TIER2_THRESHOLD) - - ex = get_first_executor(testfunc) + result += test_bound_method(i) + return result + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, sum(range(TIER2_THRESHOLD))) self.assertIsNotNone(ex) uops = get_opnames(ex) self.assertIn("_PUSH_FRAME", uops) @@ -2236,14 +2241,6 @@ def f(n): self.assertNotIn("_LOAD_ATTR_METHOD_NO_DICT", uops) self.assertNotIn("_LOAD_ATTR_METHOD_LAZY_DICT", uops) -class TestObject: - def test(self, *args, **kwargs): - return args[0] - -test_object = TestObject() -test_bound_method = TestObject.test.__get__(test_object) - - def global_identity(x): return x diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index c3922b3e511cc8..9466d834eb8323 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -710,8 +710,8 @@ dummy_func(void) { op(_CHECK_METHOD_VERSION, (func_version/2, callable, null, unused[oparg] -- callable, null, unused[oparg])) { if (sym_is_const(ctx, callable) && sym_matches_type(callable, &PyMethod_Type)) { - assert(PyMethod_Check(sym_get_const(ctx, callable))); PyMethodObject *method = (PyMethodObject *)sym_get_const(ctx, callable); + assert(PyMethod_Check(method)); REPLACE_OP(this_instr, _CHECK_FUNCTION_VERSION_INLINE, 0, func_version); this_instr->operand1 = (uintptr_t)method->im_func; } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 5fec71805bb059..689ec702c17144 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1868,8 +1868,8 @@ callable = stack_pointer[-2 - oparg]; uint32_t func_version = (uint32_t)this_instr->operand0; if (sym_is_const(ctx, callable) && sym_matches_type(callable, &PyMethod_Type)) { - assert(PyMethod_Check(sym_get_const(ctx, callable))); PyMethodObject *method = (PyMethodObject *)sym_get_const(ctx, callable); + assert(PyMethod_Check(method)); REPLACE_OP(this_instr, _CHECK_FUNCTION_VERSION_INLINE, 0, func_version); this_instr->operand1 = (uintptr_t)method->im_func; } From 37bac8118f6fa57d431c3dbc0f00a52ae8bf195d Mon Sep 17 00:00:00 2001 From: Manjusaka Date: Sun, 8 Jun 2025 18:27:21 +0800 Subject: [PATCH 7/7] lint code Signed-off-by: Manjusaka --- Lib/test/test_capi/test_opt.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 8661cbf61dd5fb..0cdc6926a7bd12 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2297,6 +2297,7 @@ def f(n): self.assertIn("_UNPACK_SEQUENCE_TWO_TUPLE", uops) self.assertNotIn("_GUARD_TOS_TUPLE", uops) + def global_identity(x): return x