diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index ac132465b227ad..c96b4ffe277e0d 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -550,6 +550,58 @@ def instantiate(): with self.assertRaises(TypeError): instantiate() + def test_call_specialized_non_method_on_class(self): + class C: + pass + o = C() + for callable in (isinstance, len, str, tuple, type): + setattr(C, callable.__name__, callable) + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + self.assertIs(o.isinstance("Spam", str), True) + self.assertEqual(o.len("Spam"), 4) + self.assertEqual(o.str("Spam"), "Spam") + self.assertEqual(o.tuple("Spam"), ("S", "p", "a", "m")) + self.assertIs(o.type("Spam"), str) + + def test_call_specialized_non_method_on_instance(self): + class C: + pass + o = C() + for callable in (isinstance, len, str, tuple, type): + setattr(o, callable.__name__, callable) + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + self.assertIs(o.isinstance("Spam", str), True) + self.assertEqual(o.len("Spam"), 4) + self.assertEqual(o.str("Spam"), "Spam") + self.assertEqual(o.tuple("Spam"), ("S", "p", "a", "m")) + self.assertIs(o.type("Spam"), str) + + def test_call_specialized_method_on_class(self): + class C: + pass + o = C() + for callable in (isinstance, len, str, tuple, type): + setattr(C, callable.__name__, types.MethodType(callable, "Spam")) + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + self.assertIs(o.isinstance(str), True) + self.assertEqual(o.len(), 4) + self.assertEqual(o.str(), "Spam") + self.assertEqual(o.tuple(), ("S", "p", "a", "m")) + self.assertIs(o.type(), str) + + def test_call_specialized_method_on_instance(self): + class C: + pass + o = C() + for callable in (isinstance, len, str, tuple, type): + setattr(o, callable.__name__, types.MethodType(callable, "Spam")) + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + self.assertIs(o.isinstance(str), True) + self.assertEqual(o.len(), 4) + self.assertEqual(o.str(), "Spam") + self.assertEqual(o.tuple(), ("S", "p", "a", "m")) + self.assertIs(o.type(), str) + def make_deferred_ref_count_obj(): """Create an object that uses deferred reference counting. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-08-08-59-25.gh-issue-131798.WrcdVS.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-08-08-59-25.gh-issue-131798.WrcdVS.rst new file mode 100644 index 00000000000000..459336e7c61a71 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-08-08-59-25.gh-issue-131798.WrcdVS.rst @@ -0,0 +1,2 @@ +Remove some unnecessary checks for some non-method :opcode:`CALL` +specializations. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index d17cac2473b101..070de325973d1b 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -3912,10 +3912,10 @@ dummy_func( PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); assert(oparg == 1); - DEOPT_IF(!PyStackRef_IsNull(null)); - DEAD(null); DEOPT_IF(callable_o != (PyObject *)&PyType_Type); DEAD(callable); + assert(PyStackRef_IsNull(null)); + DEAD(null); STAT_INC(CALL, hit); res = PyStackRef_FromPyObjectNew(Py_TYPE(arg_o)); PyStackRef_CLOSE(arg); @@ -3926,8 +3926,8 @@ dummy_func( PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); assert(oparg == 1); - DEOPT_IF(!PyStackRef_IsNull(null)); DEOPT_IF(callable_o != (PyObject *)&PyUnicode_Type); + assert(PyStackRef_IsNull(null)); STAT_INC(CALL, hit); PyObject *res_o = PyObject_Str(arg_o); DEAD(null); @@ -3948,8 +3948,8 @@ dummy_func( PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); assert(oparg == 1); - DEOPT_IF(!PyStackRef_IsNull(null)); DEOPT_IF(callable_o != (PyObject *)&PyTuple_Type); + assert(PyStackRef_IsNull(null)); STAT_INC(CALL, hit); PyObject *res_o = PySequence_Tuple(arg_o); DEAD(null); @@ -4036,20 +4036,15 @@ dummy_func( PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); DEOPT_IF(!PyType_Check(callable_o)); PyTypeObject *tp = (PyTypeObject *)callable_o; - int total_args = oparg; - _PyStackRef *arguments = args; - if (!PyStackRef_IsNull(self_or_null[0])) { - arguments--; - total_args++; - } + DEOPT_IF(!PyStackRef_IsNull(self_or_null[0])); DEOPT_IF(tp->tp_vectorcall == NULL); STAT_INC(CALL, hit); - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); + STACKREFS_TO_PYOBJECTS(args, oparg, args_o); if (CONVERSION_FAILED(args_o)) { DECREF_INPUTS(); ERROR_IF(true, error); } - PyObject *res_o = tp->tp_vectorcall((PyObject *)tp, args_o, total_args, NULL); + PyObject *res_o = tp->tp_vectorcall((PyObject *)tp, args_o, oparg, NULL); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); DECREF_INPUTS(); ERROR_IF(res_o == NULL, error); @@ -4175,14 +4170,10 @@ dummy_func( /* len(o) */ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - int total_args = oparg; - if (!PyStackRef_IsNull(self_or_null[0])) { - args--; - total_args++; - } - DEOPT_IF(total_args != 1); + assert(oparg == 1); PyInterpreterState *interp = tstate->interp; DEOPT_IF(callable_o != interp->callable_cache.len); + assert(PyStackRef_IsNull(self_or_null[0])); STAT_INC(CALL, hit); _PyStackRef arg_stackref = args[0]; PyObject *arg = PyStackRef_AsPyObjectBorrow(arg_stackref); @@ -4206,24 +4197,18 @@ dummy_func( /* isinstance(o, o2) */ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - _PyStackRef *arguments = args; - if (!PyStackRef_IsNull(self_or_null[0])) { - arguments--; - total_args++; - } - DEOPT_IF(total_args != 2); + assert(oparg == 2); PyInterpreterState *interp = tstate->interp; DEOPT_IF(callable_o != interp->callable_cache.isinstance); + assert(PyStackRef_IsNull(self_or_null[0])); STAT_INC(CALL, hit); - _PyStackRef cls_stackref = arguments[1]; - _PyStackRef inst_stackref = arguments[0]; + _PyStackRef cls_stackref = args[1]; + _PyStackRef inst_stackref = args[0]; int retval = PyObject_IsInstance(PyStackRef_AsPyObjectBorrow(inst_stackref), PyStackRef_AsPyObjectBorrow(cls_stackref)); if (retval < 0) { ERROR_NO_POP(); } res = retval ? PyStackRef_True : PyStackRef_False; - assert((!PyStackRef_IsNull(res)) ^ (_PyErr_Occurred(tstate) != NULL)); DECREF_INPUTS(); } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 497aa909b329c1..0ff9b5ff5d1599 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -5092,14 +5092,11 @@ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); assert(oparg == 1); - if (!PyStackRef_IsNull(null)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } if (callable_o != (PyObject *)&PyType_Type) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } + assert(PyStackRef_IsNull(null)); STAT_INC(CALL, hit); res = PyStackRef_FromPyObjectNew(Py_TYPE(arg_o)); stack_pointer[-3] = res; @@ -5123,14 +5120,11 @@ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); assert(oparg == 1); - if (!PyStackRef_IsNull(null)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } if (callable_o != (PyObject *)&PyUnicode_Type) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } + assert(PyStackRef_IsNull(null)); STAT_INC(CALL, hit); _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PyObject_Str(arg_o); @@ -5162,14 +5156,11 @@ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); assert(oparg == 1); - if (!PyStackRef_IsNull(null)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } if (callable_o != (PyObject *)&PyTuple_Type) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } + assert(PyStackRef_IsNull(null)); STAT_INC(CALL, hit); _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PySequence_Tuple(arg_o); @@ -5307,18 +5298,16 @@ JUMP_TO_JUMP_TARGET(); } PyTypeObject *tp = (PyTypeObject *)callable_o; - int total_args = oparg; - _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - arguments--; - total_args++; + UOP_STAT_INC(uopcode, miss); + JUMP_TO_JUMP_TARGET(); } if (tp->tp_vectorcall == NULL) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } STAT_INC(CALL, hit); - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); + STACKREFS_TO_PYOBJECTS(args, oparg, args_o); if (CONVERSION_FAILED(args_o)) { _PyFrame_SetStackPointer(frame, stack_pointer); _PyStackRef tmp; @@ -5339,7 +5328,7 @@ JUMP_TO_ERROR(); } _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = tp->tp_vectorcall((PyObject *)tp, args_o, total_args, NULL); + PyObject *res_o = tp->tp_vectorcall((PyObject *)tp, args_o, oparg, NULL); stack_pointer = _PyFrame_GetStackPointer(frame); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -5596,20 +5585,13 @@ self_or_null = &stack_pointer[-1 - oparg]; callable = &stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - int total_args = oparg; - if (!PyStackRef_IsNull(self_or_null[0])) { - args--; - total_args++; - } - if (total_args != 1) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + assert(oparg == 1); PyInterpreterState *interp = tstate->interp; if (callable_o != interp->callable_cache.len) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } + assert(PyStackRef_IsNull(self_or_null[0])); STAT_INC(CALL, hit); _PyStackRef arg_stackref = args[0]; PyObject *arg = PyStackRef_AsPyObjectBorrow(arg_stackref); @@ -5649,24 +5631,16 @@ self_or_null = &stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - _PyStackRef *arguments = args; - if (!PyStackRef_IsNull(self_or_null[0])) { - arguments--; - total_args++; - } - if (total_args != 2) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + assert(oparg == 2); PyInterpreterState *interp = tstate->interp; if (callable_o != interp->callable_cache.isinstance) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } + assert(PyStackRef_IsNull(self_or_null[0])); STAT_INC(CALL, hit); - _PyStackRef cls_stackref = arguments[1]; - _PyStackRef inst_stackref = arguments[0]; + _PyStackRef cls_stackref = args[1]; + _PyStackRef inst_stackref = args[0]; _PyFrame_SetStackPointer(frame, stack_pointer); int retval = PyObject_IsInstance(PyStackRef_AsPyObjectBorrow(inst_stackref), PyStackRef_AsPyObjectBorrow(cls_stackref)); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -5674,7 +5648,6 @@ JUMP_TO_ERROR(); } res = retval ? PyStackRef_True : PyStackRef_False; - assert((!PyStackRef_IsNull(res)) ^ (_PyErr_Occurred(tstate) != NULL)); _PyFrame_SetStackPointer(frame, stack_pointer); _PyStackRef tmp = callable; callable = res; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index fa3de197f4bcab..57855d177058d0 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1909,11 +1909,10 @@ JUMP_TO_PREDICTED(CALL); } PyTypeObject *tp = (PyTypeObject *)callable_o; - int total_args = oparg; - _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null[0])) { - arguments--; - total_args++; + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); } if (tp->tp_vectorcall == NULL) { UPDATE_MISS_STATS(CALL); @@ -1921,7 +1920,7 @@ JUMP_TO_PREDICTED(CALL); } STAT_INC(CALL, hit); - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); + STACKREFS_TO_PYOBJECTS(args, oparg, args_o); if (CONVERSION_FAILED(args_o)) { _PyFrame_SetStackPointer(frame, stack_pointer); _PyStackRef tmp; @@ -1942,7 +1941,7 @@ JUMP_TO_LABEL(error); } _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = tp->tp_vectorcall((PyObject *)tp, args_o, total_args, NULL); + PyObject *res_o = tp->tp_vectorcall((PyObject *)tp, args_o, oparg, NULL); stack_pointer = _PyFrame_GetStackPointer(frame); STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -2585,26 +2584,17 @@ self_or_null = &stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - _PyStackRef *arguments = args; - if (!PyStackRef_IsNull(self_or_null[0])) { - arguments--; - total_args++; - } - if (total_args != 2) { - UPDATE_MISS_STATS(CALL); - assert(_PyOpcode_Deopt[opcode] == (CALL)); - JUMP_TO_PREDICTED(CALL); - } + assert(oparg == 2); PyInterpreterState *interp = tstate->interp; if (callable_o != interp->callable_cache.isinstance) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } + assert(PyStackRef_IsNull(self_or_null[0])); STAT_INC(CALL, hit); - _PyStackRef cls_stackref = arguments[1]; - _PyStackRef inst_stackref = arguments[0]; + _PyStackRef cls_stackref = args[1]; + _PyStackRef inst_stackref = args[0]; _PyFrame_SetStackPointer(frame, stack_pointer); int retval = PyObject_IsInstance(PyStackRef_AsPyObjectBorrow(inst_stackref), PyStackRef_AsPyObjectBorrow(cls_stackref)); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -2612,7 +2602,6 @@ JUMP_TO_LABEL(error); } res = retval ? PyStackRef_True : PyStackRef_False; - assert((!PyStackRef_IsNull(res)) ^ (_PyErr_Occurred(tstate) != NULL)); _PyFrame_SetStackPointer(frame, stack_pointer); _PyStackRef tmp = callable; callable = res; @@ -3192,22 +3181,14 @@ self_or_null = &stack_pointer[-1 - oparg]; callable = &stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]); - int total_args = oparg; - if (!PyStackRef_IsNull(self_or_null[0])) { - args--; - total_args++; - } - if (total_args != 1) { - UPDATE_MISS_STATS(CALL); - assert(_PyOpcode_Deopt[opcode] == (CALL)); - JUMP_TO_PREDICTED(CALL); - } + assert(oparg == 1); PyInterpreterState *interp = tstate->interp; if (callable_o != interp->callable_cache.len) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } + assert(PyStackRef_IsNull(self_or_null[0])); STAT_INC(CALL, hit); _PyStackRef arg_stackref = args[0]; PyObject *arg = PyStackRef_AsPyObjectBorrow(arg_stackref); @@ -4104,16 +4085,12 @@ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); assert(oparg == 1); - if (!PyStackRef_IsNull(null)) { - UPDATE_MISS_STATS(CALL); - assert(_PyOpcode_Deopt[opcode] == (CALL)); - JUMP_TO_PREDICTED(CALL); - } if (callable_o != (PyObject *)&PyUnicode_Type) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } + assert(PyStackRef_IsNull(null)); STAT_INC(CALL, hit); _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PyObject_Str(arg_o); @@ -4176,16 +4153,12 @@ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); assert(oparg == 1); - if (!PyStackRef_IsNull(null)) { - UPDATE_MISS_STATS(CALL); - assert(_PyOpcode_Deopt[opcode] == (CALL)); - JUMP_TO_PREDICTED(CALL); - } if (callable_o != (PyObject *)&PyTuple_Type) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } + assert(PyStackRef_IsNull(null)); STAT_INC(CALL, hit); _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PySequence_Tuple(arg_o); @@ -4246,16 +4219,12 @@ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); assert(oparg == 1); - if (!PyStackRef_IsNull(null)) { - UPDATE_MISS_STATS(CALL); - assert(_PyOpcode_Deopt[opcode] == (CALL)); - JUMP_TO_PREDICTED(CALL); - } if (callable_o != (PyObject *)&PyType_Type) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } + assert(PyStackRef_IsNull(null)); STAT_INC(CALL, hit); res = PyStackRef_FromPyObjectNew(Py_TYPE(arg_o)); stack_pointer[-3] = res;