diff options
author | Jeroen Demeyer <J.Demeyer@UGent.be> | 2019-07-05 12:48:24 (GMT) |
---|---|---|
committer | Petr Viktorin <encukou@gmail.com> | 2019-07-05 12:48:24 (GMT) |
commit | 0d722f3cd602e5f5492f9c65c8af57ea9d3743b6 (patch) | |
tree | e64e73f12cbad55824a13f3e7871051b9b34d98c /Objects/methodobject.c | |
parent | 6e43d07324ca799118e805751a10a7eff71d5a04 (diff) | |
download | cpython-0d722f3cd602e5f5492f9c65c8af57ea9d3743b6.zip cpython-0d722f3cd602e5f5492f9c65c8af57ea9d3743b6.tar.gz cpython-0d722f3cd602e5f5492f9c65c8af57ea9d3743b6.tar.bz2 |
bpo-36974: separate vectorcall functions for each calling convention (GH-13781)
Diffstat (limited to 'Objects/methodobject.c')
-rw-r--r-- | Objects/methodobject.c | 165 |
1 files changed, 157 insertions, 8 deletions
diff --git a/Objects/methodobject.c b/Objects/methodobject.c index c3bc018..3494f11 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -19,6 +19,17 @@ static int numfree = 0; /* undefine macro trampoline to PyCFunction_NewEx */ #undef PyCFunction_New +/* Forward declarations */ +static PyObject * cfunction_vectorcall_FASTCALL( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +static PyObject * cfunction_vectorcall_FASTCALL_KEYWORDS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +static PyObject * cfunction_vectorcall_NOARGS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +static PyObject * cfunction_vectorcall_O( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); + + PyObject * PyCFunction_New(PyMethodDef *ml, PyObject *self) { @@ -28,6 +39,33 @@ PyCFunction_New(PyMethodDef *ml, PyObject *self) PyObject * PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module) { + /* Figure out correct vectorcall function to use */ + vectorcallfunc vectorcall; + switch (ml->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O | METH_KEYWORDS)) + { + case METH_VARARGS: + case METH_VARARGS | METH_KEYWORDS: + /* For METH_VARARGS functions, it's more efficient to use tp_call + * instead of vectorcall. */ + vectorcall = NULL; + break; + case METH_FASTCALL: + vectorcall = cfunction_vectorcall_FASTCALL; + break; + case METH_FASTCALL | METH_KEYWORDS: + vectorcall = cfunction_vectorcall_FASTCALL_KEYWORDS; + break; + case METH_NOARGS: + vectorcall = cfunction_vectorcall_NOARGS; + break; + case METH_O: + vectorcall = cfunction_vectorcall_O; + break; + default: + PyErr_SetString(PyExc_SystemError, "bad call flags"); + return NULL; + } + PyCFunctionObject *op; op = free_list; if (op != NULL) { @@ -46,14 +84,7 @@ PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module) op->m_self = self; Py_XINCREF(module); op->m_module = module; - if (ml->ml_flags & METH_VARARGS) { - /* For METH_VARARGS functions, it's more efficient to use tp_call - * instead of vectorcall. */ - op->vectorcall = NULL; - } - else { - op->vectorcall = _PyCFunction_Vectorcall; - } + op->vectorcall = vectorcall; _PyObject_GC_TRACK(op); return (PyObject *)op; } @@ -333,3 +364,121 @@ _PyCFunction_DebugMallocStats(FILE *out) "free PyCFunctionObject", numfree, sizeof(PyCFunctionObject)); } + + +/* Vectorcall functions for each of the PyCFunction calling conventions, + * except for METH_VARARGS (possibly combined with METH_KEYWORDS) which + * doesn't use vectorcall. + * + * First, common helpers + */ +static const char * +get_name(PyObject *func) +{ + assert(PyCFunction_Check(func)); + PyMethodDef *method = ((PyCFunctionObject *)func)->m_ml; + return method->ml_name; +} + +typedef void (*funcptr)(void); + +static inline int +cfunction_check_kwargs(PyObject *func, PyObject *kwnames) +{ + assert(!PyErr_Occurred()); + assert(PyCFunction_Check(func)); + if (kwnames && PyTuple_GET_SIZE(kwnames)) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no keyword arguments", get_name(func)); + return -1; + } + return 0; +} + +static inline funcptr +cfunction_enter_call(PyObject *func) +{ + if (Py_EnterRecursiveCall(" while calling a Python object")) { + return NULL; + } + return (funcptr)PyCFunction_GET_FUNCTION(func); +} + +/* Now the actual vectorcall functions */ +static PyObject * +cfunction_vectorcall_FASTCALL( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + if (cfunction_check_kwargs(func, kwnames)) { + return NULL; + } + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + _PyCFunctionFast meth = (_PyCFunctionFast) + cfunction_enter_call(func); + if (meth == NULL) { + return NULL; + } + PyObject *result = meth(PyCFunction_GET_SELF(func), args, nargs); + Py_LeaveRecursiveCall(); + return result; +} + +static PyObject * +cfunction_vectorcall_FASTCALL_KEYWORDS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + _PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords) + cfunction_enter_call(func); + if (meth == NULL) { + return NULL; + } + PyObject *result = meth(PyCFunction_GET_SELF(func), args, nargs, kwnames); + Py_LeaveRecursiveCall(); + return result; +} + +static PyObject * +cfunction_vectorcall_NOARGS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + if (cfunction_check_kwargs(func, kwnames)) { + return NULL; + } + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (nargs != 0) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no arguments (%zd given)", get_name(func), nargs); + return NULL; + } + PyCFunction meth = (PyCFunction)cfunction_enter_call(func); + if (meth == NULL) { + return NULL; + } + PyObject *result = meth(PyCFunction_GET_SELF(func), NULL); + Py_LeaveRecursiveCall(); + return result; +} + +static PyObject * +cfunction_vectorcall_O( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + if (cfunction_check_kwargs(func, kwnames)) { + return NULL; + } + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (nargs != 1) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes exactly one argument (%zd given)", + get_name(func), nargs); + return NULL; + } + PyCFunction meth = (PyCFunction)cfunction_enter_call(func); + if (meth == NULL) { + return NULL; + } + PyObject *result = meth(PyCFunction_GET_SELF(func), args[0]); + Py_LeaveRecursiveCall(); + return result; +} |