gh-114626: add PyCFunctionFast and PyCFunctionFastWithKeywords (GH-114627)

Co-authored-by: Petr Viktorin <encukou@gmail.com>
This commit is contained in:
David Hewitt 2024-02-15 10:05:20 +00:00 committed by GitHub
parent 32f8ab1ab6
commit 9e3729bbd7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 57 additions and 41 deletions

View File

@ -187,23 +187,23 @@ Implementing functions and methods
PyObject *kwargs); PyObject *kwargs);
.. c:type:: _PyCFunctionFast .. c:type:: PyCFunctionFast
Type of the functions used to implement Python callables in C Type of the functions used to implement Python callables in C
with signature :c:macro:`METH_FASTCALL`. with signature :c:macro:`METH_FASTCALL`.
The function signature is:: The function signature is::
PyObject *_PyCFunctionFast(PyObject *self, PyObject *PyCFunctionFast(PyObject *self,
PyObject *const *args, PyObject *const *args,
Py_ssize_t nargs); Py_ssize_t nargs);
.. c:type:: _PyCFunctionFastWithKeywords .. c:type:: PyCFunctionFastWithKeywords
Type of the functions used to implement Python callables in C Type of the functions used to implement Python callables in C
with signature :ref:`METH_FASTCALL | METH_KEYWORDS <METH_FASTCALL-METH_KEYWORDS>`. with signature :ref:`METH_FASTCALL | METH_KEYWORDS <METH_FASTCALL-METH_KEYWORDS>`.
The function signature is:: The function signature is::
PyObject *_PyCFunctionFastWithKeywords(PyObject *self, PyObject *PyCFunctionFastWithKeywords(PyObject *self,
PyObject *const *args, PyObject *const *args,
Py_ssize_t nargs, Py_ssize_t nargs,
PyObject *kwnames); PyObject *kwnames);
@ -290,7 +290,7 @@ There are these calling conventions:
.. c:macro:: METH_FASTCALL .. c:macro:: METH_FASTCALL
Fast calling convention supporting only positional arguments. Fast calling convention supporting only positional arguments.
The methods have the type :c:type:`_PyCFunctionFast`. The methods have the type :c:type:`PyCFunctionFast`.
The first parameter is *self*, the second parameter is a C array The first parameter is *self*, the second parameter is a C array
of :c:expr:`PyObject*` values indicating the arguments and the third of :c:expr:`PyObject*` values indicating the arguments and the third
parameter is the number of arguments (the length of the array). parameter is the number of arguments (the length of the array).
@ -306,7 +306,7 @@ There are these calling conventions:
:c:expr:`METH_FASTCALL | METH_KEYWORDS` :c:expr:`METH_FASTCALL | METH_KEYWORDS`
Extension of :c:macro:`METH_FASTCALL` supporting also keyword arguments, Extension of :c:macro:`METH_FASTCALL` supporting also keyword arguments,
with methods of type :c:type:`_PyCFunctionFastWithKeywords`. with methods of type :c:type:`PyCFunctionFastWithKeywords`.
Keyword arguments are passed the same way as in the Keyword arguments are passed the same way as in the
:ref:`vectorcall protocol <vectorcall>`: :ref:`vectorcall protocol <vectorcall>`:
there is an additional fourth :c:expr:`PyObject*` parameter there is an additional fourth :c:expr:`PyObject*` parameter

View File

@ -42,6 +42,8 @@ function,PyBytes_Repr,3.2,,
function,PyBytes_Size,3.2,, function,PyBytes_Size,3.2,,
var,PyBytes_Type,3.2,, var,PyBytes_Type,3.2,,
type,PyCFunction,3.2,, type,PyCFunction,3.2,,
type,PyCFunctionFast,3.13,,
type,PyCFunctionFastWithKeywords,3.13,,
type,PyCFunctionWithKeywords,3.2,, type,PyCFunctionWithKeywords,3.2,,
function,PyCFunction_GetFlags,3.2,, function,PyCFunction_GetFlags,3.2,,
function,PyCFunction_GetFunction,3.2,, function,PyCFunction_GetFunction,3.2,,

View File

@ -17,15 +17,22 @@ PyAPI_DATA(PyTypeObject) PyCFunction_Type;
#define PyCFunction_Check(op) PyObject_TypeCheck((op), &PyCFunction_Type) #define PyCFunction_Check(op) PyObject_TypeCheck((op), &PyCFunction_Type)
typedef PyObject *(*PyCFunction)(PyObject *, PyObject *); typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
typedef PyObject *(*_PyCFunctionFast) (PyObject *, PyObject *const *, Py_ssize_t); typedef PyObject *(*PyCFunctionFast) (PyObject *, PyObject *const *, Py_ssize_t);
typedef PyObject *(*PyCFunctionWithKeywords)(PyObject *, PyObject *, typedef PyObject *(*PyCFunctionWithKeywords)(PyObject *, PyObject *,
PyObject *); PyObject *);
typedef PyObject *(*_PyCFunctionFastWithKeywords) (PyObject *, typedef PyObject *(*PyCFunctionFastWithKeywords) (PyObject *,
PyObject *const *, Py_ssize_t, PyObject *const *, Py_ssize_t,
PyObject *); PyObject *);
typedef PyObject *(*PyCMethod)(PyObject *, PyTypeObject *, PyObject *const *, typedef PyObject *(*PyCMethod)(PyObject *, PyTypeObject *, PyObject *const *,
size_t, PyObject *); size_t, PyObject *);
// For backwards compatibility. `METH_FASTCALL` was added to the stable API in
// 3.10 alongside `_PyCFunctionFastWithKeywords` and `_PyCFunctionFast`.
// Note that the underscore-prefixed names were documented in public docs;
// people may be using them.
typedef PyCFunctionFast _PyCFunctionFast;
typedef PyCFunctionWithKeywords _PyCFunctionWithKeywords;
// Cast an function to the PyCFunction type to use it with PyMethodDef. // Cast an function to the PyCFunction type to use it with PyMethodDef.
// //
// This macro can be used to prevent compiler warnings if the first parameter // This macro can be used to prevent compiler warnings if the first parameter

View File

@ -0,0 +1 @@
Add ``PyCFunctionFast`` and ``PyCFunctionFastWithKeywords`` typedefs (identical to the existing ``_PyCFunctionFast`` and ``_PyCFunctionFastWithKeywords`` typedefs, just without a leading ``_`` prefix).

View File

@ -2489,3 +2489,10 @@
added = '3.13' added = '3.13'
[function.PyList_GetItemRef] [function.PyList_GetItemRef]
added = '3.13' added = '3.13'
[typedef.PyCFunctionFast]
added = '3.13'
# "abi-only" since 3.10. (Callback type names aren't used in C code,
# but this function signature was expected with METH_FASTCALL.)
[typedef.PyCFunctionFastWithKeywords]
added = '3.13'
# "abi-only" since 3.10. (Same story as PyCFunctionFast.)

View File

@ -393,7 +393,7 @@ method_vectorcall_FASTCALL(
if (method_check_args(func, args, nargs, kwnames)) { if (method_check_args(func, args, nargs, kwnames)) {
return NULL; return NULL;
} }
_PyCFunctionFast meth = (_PyCFunctionFast) PyCFunctionFast meth = (PyCFunctionFast)
method_enter_call(tstate, func); method_enter_call(tstate, func);
if (meth == NULL) { if (meth == NULL) {
return NULL; return NULL;
@ -412,7 +412,7 @@ method_vectorcall_FASTCALL_KEYWORDS(
if (method_check_args(func, args, nargs, NULL)) { if (method_check_args(func, args, nargs, NULL)) {
return NULL; return NULL;
} }
_PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords) PyCFunctionFastWithKeywords meth = (PyCFunctionFastWithKeywords)
method_enter_call(tstate, func); method_enter_call(tstate, func);
if (meth == NULL) { if (meth == NULL) {
return NULL; return NULL;

View File

@ -417,7 +417,7 @@ cfunction_vectorcall_FASTCALL(
return NULL; return NULL;
} }
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
_PyCFunctionFast meth = (_PyCFunctionFast) PyCFunctionFast meth = (PyCFunctionFast)
cfunction_enter_call(tstate, func); cfunction_enter_call(tstate, func);
if (meth == NULL) { if (meth == NULL) {
return NULL; return NULL;
@ -433,7 +433,7 @@ cfunction_vectorcall_FASTCALL_KEYWORDS(
{ {
PyThreadState *tstate = _PyThreadState_GET(); PyThreadState *tstate = _PyThreadState_GET();
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
_PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords) PyCFunctionFastWithKeywords meth = (PyCFunctionFastWithKeywords)
cfunction_enter_call(tstate, func); cfunction_enter_call(tstate, func);
if (meth == NULL) { if (meth == NULL) {
return NULL; return NULL;
@ -552,4 +552,3 @@ cfunction_call(PyObject *func, PyObject *args, PyObject *kwargs)
} }
return _Py_CheckFunctionResult(tstate, func, result, NULL); return _Py_CheckFunctionResult(tstate, func, result, NULL);
} }

View File

@ -3376,7 +3376,7 @@ dummy_func(
STAT_INC(CALL, hit); STAT_INC(CALL, hit);
PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable);
/* res = func(self, args, nargs) */ /* res = func(self, args, nargs) */
res = ((_PyCFunctionFast)(void(*)(void))cfunc)( res = ((PyCFunctionFast)(void(*)(void))cfunc)(
PyCFunction_GET_SELF(callable), PyCFunction_GET_SELF(callable),
args, args,
total_args); total_args);
@ -3407,8 +3407,8 @@ dummy_func(
DEOPT_IF(PyCFunction_GET_FLAGS(callable) != (METH_FASTCALL | METH_KEYWORDS)); DEOPT_IF(PyCFunction_GET_FLAGS(callable) != (METH_FASTCALL | METH_KEYWORDS));
STAT_INC(CALL, hit); STAT_INC(CALL, hit);
/* res = func(self, args, nargs, kwnames) */ /* res = func(self, args, nargs, kwnames) */
_PyCFunctionFastWithKeywords cfunc = PyCFunctionFastWithKeywords cfunc =
(_PyCFunctionFastWithKeywords)(void(*)(void)) (PyCFunctionFastWithKeywords)(void(*)(void))
PyCFunction_GET_FUNCTION(callable); PyCFunction_GET_FUNCTION(callable);
res = cfunc(PyCFunction_GET_SELF(callable), args, total_args, NULL); res = cfunc(PyCFunction_GET_SELF(callable), args, total_args, NULL);
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
@ -3539,8 +3539,8 @@ dummy_func(
DEOPT_IF(!Py_IS_TYPE(self, d_type)); DEOPT_IF(!Py_IS_TYPE(self, d_type));
STAT_INC(CALL, hit); STAT_INC(CALL, hit);
int nargs = total_args - 1; int nargs = total_args - 1;
_PyCFunctionFastWithKeywords cfunc = PyCFunctionFastWithKeywords cfunc =
(_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; (PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth;
res = cfunc(self, args + 1, nargs, NULL); res = cfunc(self, args + 1, nargs, NULL);
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
@ -3597,8 +3597,8 @@ dummy_func(
PyObject *self = args[0]; PyObject *self = args[0];
DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type)); DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type));
STAT_INC(CALL, hit); STAT_INC(CALL, hit);
_PyCFunctionFast cfunc = PyCFunctionFast cfunc =
(_PyCFunctionFast)(void(*)(void))meth->ml_meth; (PyCFunctionFast)(void(*)(void))meth->ml_meth;
int nargs = total_args - 1; int nargs = total_args - 1;
res = cfunc(self, args + 1, nargs); res = cfunc(self, args + 1, nargs);
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));

View File

@ -2843,7 +2843,7 @@
STAT_INC(CALL, hit); STAT_INC(CALL, hit);
PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable);
/* res = func(self, args, nargs) */ /* res = func(self, args, nargs) */
res = ((_PyCFunctionFast)(void(*)(void))cfunc)( res = ((PyCFunctionFast)(void(*)(void))cfunc)(
PyCFunction_GET_SELF(callable), PyCFunction_GET_SELF(callable),
args, args,
total_args); total_args);
@ -2884,8 +2884,8 @@
if (PyCFunction_GET_FLAGS(callable) != (METH_FASTCALL | METH_KEYWORDS)) goto deoptimize; if (PyCFunction_GET_FLAGS(callable) != (METH_FASTCALL | METH_KEYWORDS)) goto deoptimize;
STAT_INC(CALL, hit); STAT_INC(CALL, hit);
/* res = func(self, args, nargs, kwnames) */ /* res = func(self, args, nargs, kwnames) */
_PyCFunctionFastWithKeywords cfunc = PyCFunctionFastWithKeywords cfunc =
(_PyCFunctionFastWithKeywords)(void(*)(void)) (PyCFunctionFastWithKeywords)(void(*)(void))
PyCFunction_GET_FUNCTION(callable); PyCFunction_GET_FUNCTION(callable);
res = cfunc(PyCFunction_GET_SELF(callable), args, total_args, NULL); res = cfunc(PyCFunction_GET_SELF(callable), args, total_args, NULL);
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
@ -3036,8 +3036,8 @@
if (!Py_IS_TYPE(self, d_type)) goto deoptimize; if (!Py_IS_TYPE(self, d_type)) goto deoptimize;
STAT_INC(CALL, hit); STAT_INC(CALL, hit);
int nargs = total_args - 1; int nargs = total_args - 1;
_PyCFunctionFastWithKeywords cfunc = PyCFunctionFastWithKeywords cfunc =
(_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; (PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth;
res = cfunc(self, args + 1, nargs, NULL); res = cfunc(self, args + 1, nargs, NULL);
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
/* Free the arguments. */ /* Free the arguments. */
@ -3115,8 +3115,8 @@
PyObject *self = args[0]; PyObject *self = args[0];
if (!Py_IS_TYPE(self, method->d_common.d_type)) goto deoptimize; if (!Py_IS_TYPE(self, method->d_common.d_type)) goto deoptimize;
STAT_INC(CALL, hit); STAT_INC(CALL, hit);
_PyCFunctionFast cfunc = PyCFunctionFast cfunc =
(_PyCFunctionFast)(void(*)(void))meth->ml_meth; (PyCFunctionFast)(void(*)(void))meth->ml_meth;
int nargs = total_args - 1; int nargs = total_args - 1;
res = cfunc(self, args + 1, nargs); res = cfunc(self, args + 1, nargs);
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));

View File

@ -1069,7 +1069,7 @@
STAT_INC(CALL, hit); STAT_INC(CALL, hit);
PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable); PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable);
/* res = func(self, args, nargs) */ /* res = func(self, args, nargs) */
res = ((_PyCFunctionFast)(void(*)(void))cfunc)( res = ((PyCFunctionFast)(void(*)(void))cfunc)(
PyCFunction_GET_SELF(callable), PyCFunction_GET_SELF(callable),
args, args,
total_args); total_args);
@ -1115,8 +1115,8 @@
DEOPT_IF(PyCFunction_GET_FLAGS(callable) != (METH_FASTCALL | METH_KEYWORDS), CALL); DEOPT_IF(PyCFunction_GET_FLAGS(callable) != (METH_FASTCALL | METH_KEYWORDS), CALL);
STAT_INC(CALL, hit); STAT_INC(CALL, hit);
/* res = func(self, args, nargs, kwnames) */ /* res = func(self, args, nargs, kwnames) */
_PyCFunctionFastWithKeywords cfunc = PyCFunctionFastWithKeywords cfunc =
(_PyCFunctionFastWithKeywords)(void(*)(void)) (PyCFunctionFastWithKeywords)(void(*)(void))
PyCFunction_GET_FUNCTION(callable); PyCFunction_GET_FUNCTION(callable);
res = cfunc(PyCFunction_GET_SELF(callable), args, total_args, NULL); res = cfunc(PyCFunction_GET_SELF(callable), args, total_args, NULL);
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
@ -1523,8 +1523,8 @@
PyObject *self = args[0]; PyObject *self = args[0];
DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type), CALL); DEOPT_IF(!Py_IS_TYPE(self, method->d_common.d_type), CALL);
STAT_INC(CALL, hit); STAT_INC(CALL, hit);
_PyCFunctionFast cfunc = PyCFunctionFast cfunc =
(_PyCFunctionFast)(void(*)(void))meth->ml_meth; (PyCFunctionFast)(void(*)(void))meth->ml_meth;
int nargs = total_args - 1; int nargs = total_args - 1;
res = cfunc(self, args + 1, nargs); res = cfunc(self, args + 1, nargs);
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
@ -1568,8 +1568,8 @@
DEOPT_IF(!Py_IS_TYPE(self, d_type), CALL); DEOPT_IF(!Py_IS_TYPE(self, d_type), CALL);
STAT_INC(CALL, hit); STAT_INC(CALL, hit);
int nargs = total_args - 1; int nargs = total_args - 1;
_PyCFunctionFastWithKeywords cfunc = PyCFunctionFastWithKeywords cfunc =
(_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; (PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth;
res = cfunc(self, args + 1, nargs, NULL); res = cfunc(self, args + 1, nargs, NULL);
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
/* Free the arguments. */ /* Free the arguments. */