From 03420974ef3e2c02058f6b289ba44961b3a0a220 Mon Sep 17 00:00:00 2001 From: cocolato Date: Sun, 4 Oct 2026 14:30:36 +0800 Subject: [PATCH 1/2] fix vector call opt --- Lib/test/test_capi/test_opt.py | 20 +++++++++++++ Lib/test/test_opcache.py | 36 ++++++++++++++++++++++++ Modules/_testinternalcapi/test_cases.c.h | 2 +- Python/bytecodes.c | 2 +- Python/executor_cases.c.h | 2 +- Python/generated_cases.c.h | 2 +- Python/specialize.c | 6 ++-- 7 files changed, 63 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 88077aacecbf5b1..8af57b4ab56fc66 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -3300,6 +3300,26 @@ def testfunc(n): self.assertIn("_CALL_BUILTIN_CLASS", uops) self.assertNotIn("_GUARD_CALLABLE_BUILTIN_CLASS", uops) + def test_call_builtin_class_custom_metaclass(self): + _testcapi = import_helper.import_module("_testcapi") + + def testfunc(cls, n): + for _ in range(n): + result = cls() + return result + + Meta = _testcapi.make_vectorcall_class(type) + C = type.__new__(Meta, "C", (), {}) + Meta.set_vectorcall(C, type) + + testfunc(list, TIER2_THRESHOLD) + ex = get_first_executor(testfunc) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_GUARD_CALLABLE_BUILTIN_CLASS", uops) + self.assertIn("_CALL_BUILTIN_CLASS", uops) + self.assertEqual(testfunc(C, 16), "tp_call") + def test_call_builtin_o(self): def testfunc(n): x = 0 diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index 60879e2774e7077..43a67d870296f2a 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -497,6 +497,42 @@ def __init__(self, arg): class TestCallCache(TestBase): + @requires_jit_disabled + @requires_specialization + def test_call_builtin_class_custom_metaclass(self): + _testcapi = import_module("_testcapi") + + def call(cls): + return cls() + + Meta = _testcapi.make_vectorcall_class(type) + C = type.__new__(Meta, "C", (), {}) + Meta.set_vectorcall(C, type) + + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + call(list) + self.assert_specialized(call, "CALL_BUILTIN_CLASS") + Meta.__call__ = lambda cls: "custom" + self.assertEqual(call(C), "custom") + + @requires_jit_disabled + @requires_specialization + def test_call_immutable_class_custom_metaclass(self): + _testcapi = import_module("_testcapi") + _testlimitedcapi = import_module("_testlimitedcapi") + + def call(cls): + return cls() + + Meta = _testcapi.make_vectorcall_class(type) + C = type.__new__(Meta, "C", (), {}) + Meta.set_vectorcall(C, type) + _testlimitedcapi.type_freeze(C) + + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + self.assertEqual(call(C), "tp_call") + self.assert_specialized(call, "CALL_NON_PY_GENERAL") + def test_too_many_defaults_0(self): def f(): pass diff --git a/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h index 3bdc16437e2bc61..1713115723cbd18 100644 --- a/Modules/_testinternalcapi/test_cases.c.h +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -2390,7 +2390,7 @@ { callable = stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - if (!PyType_Check(callable_o)) { + if (!Py_IS_TYPE(callable_o, &PyType_Type)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 31eaeab0d67841f..522210e433c8ef0 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -4794,7 +4794,7 @@ dummy_func( op(_GUARD_CALLABLE_BUILTIN_CLASS, (callable, unused, unused[oparg] -- callable, unused, unused[oparg])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - EXIT_IF(!PyType_Check(callable_o)); + EXIT_IF(!Py_IS_TYPE(callable_o, &PyType_Type)); PyTypeObject *tp = (PyTypeObject *)callable_o; EXIT_IF(tp->tp_vectorcall == NULL); } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index c5b2dfcf5f618f3..d5fa28d9f85c368 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -18392,7 +18392,7 @@ oparg = CURRENT_OPARG(); callable = stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - if (!PyType_Check(callable_o)) { + if (!Py_IS_TYPE(callable_o, &PyType_Type)) { UOP_STAT_INC(uopcode, miss); SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index dd0ce41e4b06b4a..4e71c31f6f9bd83 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2390,7 +2390,7 @@ { callable = stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - if (!PyType_Check(callable_o)) { + if (!Py_IS_TYPE(callable_o, &PyType_Type)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); diff --git a/Python/specialize.c b/Python/specialize.c index f658df3bc5d2228..6c60a36170c173d 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1659,6 +1659,9 @@ specialize_class_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs) { assert(PyType_Check(callable)); PyTypeObject *tp = _PyType_CAST(callable); + if (Py_TYPE(tp) != &PyType_Type) { + goto generic; + } if (tp->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) { int oparg = instr->op.arg; if (nargs == 1 && oparg == 1) { @@ -1681,9 +1684,6 @@ specialize_class_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs) } goto generic; } - if (Py_TYPE(tp) != &PyType_Type) { - goto generic; - } if (tp->tp_new == PyBaseObject_Type.tp_new) { unsigned int tp_version = 0; PyObject *init = get_init_for_simple_managed_python_class(tp, &tp_version); From cd6e1b9e5e99ee032ce7562ee649e6bbc34efb6f Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sun, 4 Oct 2026 06:34:04 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-10-04-06-34-01.gh-issue-158788.NlHDwy.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-10-04-06-34-01.gh-issue-158788.NlHDwy.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-10-04-06-34-01.gh-issue-158788.NlHDwy.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-04-06-34-01.gh-issue-158788.NlHDwy.rst new file mode 100644 index 000000000000000..39a639d482dcb15 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-04-06-34-01.gh-issue-158788.NlHDwy.rst @@ -0,0 +1 @@ +Fix an issue where the vectorcall optimization for class calls could bypass a custom metaclass's ``__call__`` method.