From dc7680e7c3a3a459d3b40b8b908bdaf456ab3d3d Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 3 Jun 2019 16:59:01 +0200 Subject: [PATCH 1/4] bpo-36974: separate vectorcall functions for each calling convention --- Include/descrobject.h | 3 - Include/methodobject.h | 14 +- Lib/test/test_gdb.py | 8 +- .../2019-06-11-12-59-38.bpo-36974.bVYmSA.rst | 2 + Objects/call.c | 85 ++++++- Objects/descrobject.c | 220 +++++++++++++----- Objects/methodobject.c | 36 ++- Python/ceval.c | 8 +- Tools/gdb/libpython.py | 4 +- 9 files changed, 281 insertions(+), 99 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2019-06-11-12-59-38.bpo-36974.bVYmSA.rst diff --git a/Include/descrobject.h b/Include/descrobject.h index d7114852c1e210..ead269d1d2f796 100644 --- a/Include/descrobject.h +++ b/Include/descrobject.h @@ -91,9 +91,6 @@ PyAPI_FUNC(PyObject *) PyDescr_NewMember(PyTypeObject *, PyAPI_FUNC(PyObject *) PyDescr_NewGetSet(PyTypeObject *, struct PyGetSetDef *); #ifndef Py_LIMITED_API - -PyAPI_FUNC(PyObject *) _PyMethodDescr_Vectorcall( - PyObject *descrobj, PyObject *const *args, size_t nargsf, PyObject *kwnames); PyAPI_FUNC(PyObject *) PyDescr_NewWrapper(PyTypeObject *, struct wrapperbase *, void *); #define PyDescr_IsData(d) (Py_TYPE(d)->tp_descr_set != NULL) diff --git a/Include/methodobject.h b/Include/methodobject.h index f4a6682830c3f0..aedb8706daf5e9 100644 --- a/Include/methodobject.h +++ b/Include/methodobject.h @@ -41,11 +41,15 @@ PyAPI_FUNC(int) PyCFunction_GetFlags(PyObject *); #endif PyAPI_FUNC(PyObject *) PyCFunction_Call(PyObject *, PyObject *, PyObject *); -#ifndef Py_LIMITED_API -PyAPI_FUNC(PyObject *) _PyCFunction_Vectorcall(PyObject *func, - PyObject *const *stack, - size_t nargsf, - PyObject *kwnames); +#ifdef Py_BUILD_CORE +extern PyObject * _PyCFunction_Vectorcall_FASTCALL( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +extern PyObject * _PyCFunction_Vectorcall_FASTCALL_KEYWORDS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +extern PyObject * _PyCFunction_Vectorcall_NOARGS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +extern PyObject * _PyCFunction_Vectorcall_O( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); #endif struct PyMethodDef { diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py index 1c5e18b678ca7f..e07d3273a4552d 100644 --- a/Lib/test/test_gdb.py +++ b/Lib/test/test_gdb.py @@ -850,10 +850,10 @@ def test_pycfunction(self): # called, so test a variety of calling conventions. for py_name, py_args, c_name, expected_frame_number in ( ('gmtime', '', 'time_gmtime', 1), # METH_VARARGS - ('len', '[]', 'builtin_len', 2), # METH_O - ('locals', '', 'builtin_locals', 2), # METH_NOARGS - ('iter', '[]', 'builtin_iter', 2), # METH_FASTCALL - ('sorted', '[]', 'builtin_sorted', 2), # METH_FASTCALL|METH_KEYWORDS + ('len', '[]', 'builtin_len', 1), # METH_O + ('locals', '', 'builtin_locals', 1), # METH_NOARGS + ('iter', '[]', 'builtin_iter', 1), # METH_FASTCALL + ('sorted', '[]', 'builtin_sorted', 1), # METH_FASTCALL|METH_KEYWORDS ): with self.subTest(c_name): cmd = ('from time import gmtime\n' # (not always needed) diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-11-12-59-38.bpo-36974.bVYmSA.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-11-12-59-38.bpo-36974.bVYmSA.rst new file mode 100644 index 00000000000000..6080ef361814a8 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-06-11-12-59-38.bpo-36974.bVYmSA.rst @@ -0,0 +1,2 @@ +Implemented separate vectorcall functions for every calling convention of +builtin functions and methods. This improves performance for calls. diff --git a/Objects/call.c b/Objects/call.c index 8e0d271ab78a2c..72352d1f9e65ed 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -626,22 +626,83 @@ _PyMethodDef_RawFastCallKeywords(PyMethodDef *method, PyObject *self, } +/* Vectorcall functions for each of the PyCFunction calling conventions, + * except for METH_VARARGS (possibly combined with METH_KEYWORDS) which + * doesn't use vectorcall. + * + * First two macros to define common boilerplate + */ +#define PyCFunction_VECTORCALL_BEGIN(ALLOW_KWARGS) \ + assert(!PyErr_Occurred()); \ + assert(PyCFunction_Check(func)); \ + PyCFunctionObject *f = (PyCFunctionObject *)func; \ + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); \ + PyObject *self = f->m_self; \ + PyMethodDef *method = f->m_ml; \ + const char *name = method->ml_name; \ + void(*meth)(void) = (void(*)(void))method->ml_meth; \ + PyObject *result = NULL; \ + \ + if (!ALLOW_KWARGS && kwnames && PyTuple_GET_SIZE(kwnames) > 0) { \ + PyErr_Format(PyExc_TypeError, \ + "%.200s() takes no keyword arguments", name); \ + return NULL; \ + } \ + if (Py_EnterRecursiveCall(" while calling a Python object")) { \ + return NULL; \ + } + +#define PyCFunction_VECTORCALL_END \ + Py_LeaveRecursiveCall(); \ + return _Py_CheckFunctionResult(func, result, NULL); + +/* Now the actual vectorcall functions */ PyObject * -_PyCFunction_Vectorcall(PyObject *func, - PyObject *const *args, size_t nargsf, - PyObject *kwnames) +_PyCFunction_Vectorcall_FASTCALL( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { - PyObject *result; + PyCFunction_VECTORCALL_BEGIN(0) + result = ((_PyCFunctionFast)meth)(self, args, nargs); + PyCFunction_VECTORCALL_END +} - assert(func != NULL); - assert(PyCFunction_Check(func)); - Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); +PyObject * +_PyCFunction_Vectorcall_FASTCALL_KEYWORDS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + PyCFunction_VECTORCALL_BEGIN(1) + result = ((_PyCFunctionFastWithKeywords)meth)(self, args, nargs, kwnames); + PyCFunction_VECTORCALL_END +} - result = _PyMethodDef_RawFastCallKeywords(((PyCFunctionObject*)func)->m_ml, - PyCFunction_GET_SELF(func), - args, nargs, kwnames); - result = _Py_CheckFunctionResult(func, result, NULL); - return result; +PyObject * +_PyCFunction_Vectorcall_NOARGS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + PyCFunction_VECTORCALL_BEGIN(0) + if (nargs != 0) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no arguments (%zd given)", name, nargs); + } + else { + result = ((PyCFunction)meth)(self, NULL); + } + PyCFunction_VECTORCALL_END +} + +PyObject * +_PyCFunction_Vectorcall_O( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + PyCFunction_VECTORCALL_BEGIN(0) + if (nargs != 1) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes exactly one argument (%zd given)", name, nargs); + } + else { + result = ((PyCFunction)meth)(self, args[0]); + } + PyCFunction_VECTORCALL_END } diff --git a/Objects/descrobject.c b/Objects/descrobject.c index a0eb5057af04fc..e7f17313b68f95 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -226,80 +226,151 @@ getset_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value) return -1; } + +/* Vectorcall functions for each of the PyMethodDescr calling conventions. + * + * First two macros to define common boilerplate + */ +#define PyMethodDescr_VECTORCALL_BEGIN(ALLOW_KWARGS) \ + assert(!PyErr_Occurred()); \ + assert(PyObject_TypeCheck(func, &PyMethodDescr_Type)); \ + PyMethodDescrObject *f = (PyMethodDescrObject *)func; \ + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); \ + PyMethodDef *method = f->d_method; \ + const char *name = method->ml_name; \ + void(*meth)(void) = (void(*)(void))method->ml_meth; \ + PyObject *result = NULL; \ + \ + /* self is the first positional argument */ \ + if (nargs < 1) { \ + PyErr_Format(PyExc_TypeError, \ + "descriptor '%.200s' of '%.100s' " \ + "object needs an argument", \ + name, PyDescr_TYPE(func)->tp_name); \ + return NULL; \ + } \ + PyObject *self = args[0]; \ + args++; \ + nargs--; \ + if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self), \ + (PyObject *)PyDescr_TYPE(func))) \ + { \ + PyErr_Format(PyExc_TypeError, \ + "descriptor '%.200s' for '%.100s' objects " \ + "doesn't apply to a '%.100s' object", \ + name, PyDescr_TYPE(func)->tp_name, \ + Py_TYPE(self)->tp_name); \ + return NULL; \ + } \ + \ + if (!ALLOW_KWARGS && kwnames && PyTuple_GET_SIZE(kwnames) > 0) { \ + PyErr_Format(PyExc_TypeError, \ + "%.200s() takes no keyword arguments", name); \ + return NULL; \ + } \ + if (Py_EnterRecursiveCall(" while calling a Python object")) { \ + return NULL; \ + } + +#define PyMethodDescr_VECTORCALL_END \ + Py_LeaveRecursiveCall(); \ + return _Py_CheckFunctionResult(func, result, NULL); + +/* Now the actual vectorcall functions */ static PyObject * -methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwargs) -{ - Py_ssize_t nargs; - PyObject *self, *result; +_PyMethodDescr_Vectorcall_VARARGS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + PyMethodDescr_VECTORCALL_BEGIN(0) + /* Create a temporary tuple for positional arguments */ + PyObject *argstuple = _PyTuple_FromArray(args, nargs); + if (argstuple == NULL) { + goto exit; + } + result = ((PyCFunction)meth)(self, argstuple); + Py_DECREF(argstuple); +exit: + PyMethodDescr_VECTORCALL_END +} - /* Make sure that the first argument is acceptable as 'self' */ - assert(PyTuple_Check(args)); - nargs = PyTuple_GET_SIZE(args); - if (nargs < 1) { - PyErr_Format(PyExc_TypeError, - "descriptor '%V' of '%.100s' " - "object needs an argument", - descr_name((PyDescrObject *)descr), "?", - PyDescr_TYPE(descr)->tp_name); - return NULL; +static PyObject * +_PyMethodDescr_Vectorcall_VARARGS_KEYWORDS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + PyMethodDescr_VECTORCALL_BEGIN(1) + /* Create a temporary tuple for positional arguments */ + PyObject *argstuple = _PyTuple_FromArray(args, nargs); + if (argstuple == NULL) { + goto exit; } - self = PyTuple_GET_ITEM(args, 0); - if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self), - (PyObject *)PyDescr_TYPE(descr))) { - PyErr_Format(PyExc_TypeError, - "descriptor '%V' for '%.100s' objects " - "doesn't apply to a '%.100s' object", - descr_name((PyDescrObject *)descr), "?", - PyDescr_TYPE(descr)->tp_name, - self->ob_type->tp_name); - return NULL; + /* Create a temporary dict for keyword arguments */ + PyObject *kwdict; + if (kwnames == NULL || PyTuple_GET_SIZE(kwnames) == 0) { + kwdict = NULL; } + else { + kwdict = _PyStack_AsDict(args + nargs, kwnames); + if (kwdict == NULL) { + Py_DECREF(argstuple); + goto exit; + } + } + result = ((PyCFunctionWithKeywords)meth)(self, argstuple, kwdict); + Py_DECREF(argstuple); + Py_XDECREF(kwdict); +exit: + PyMethodDescr_VECTORCALL_END +} - result = _PyMethodDef_RawFastCallDict(descr->d_method, self, - &_PyTuple_ITEMS(args)[1], nargs - 1, - kwargs); - result = _Py_CheckFunctionResult((PyObject *)descr, result, NULL); - return result; +static PyObject * +_PyMethodDescr_Vectorcall_FASTCALL( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + PyMethodDescr_VECTORCALL_BEGIN(0) + result = ((_PyCFunctionFast)meth)(self, args, nargs); + PyMethodDescr_VECTORCALL_END } -// same to methoddescr_call(), but use FASTCALL convention. -PyObject * -_PyMethodDescr_Vectorcall(PyObject *descrobj, - PyObject *const *args, size_t nargsf, - PyObject *kwnames) +static PyObject * +_PyMethodDescr_Vectorcall_FASTCALL_KEYWORDS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { - assert(Py_TYPE(descrobj) == &PyMethodDescr_Type); - PyMethodDescrObject *descr = (PyMethodDescrObject *)descrobj; - PyObject *self, *result; + PyMethodDescr_VECTORCALL_BEGIN(1) + result = ((_PyCFunctionFastWithKeywords)meth)(self, args, nargs, kwnames); + PyMethodDescr_VECTORCALL_END +} - Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); - /* Make sure that the first argument is acceptable as 'self' */ - if (nargs < 1) { +static PyObject * +_PyMethodDescr_Vectorcall_NOARGS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + PyMethodDescr_VECTORCALL_BEGIN(0) + if (nargs != 0) { PyErr_Format(PyExc_TypeError, - "descriptor '%V' of '%.100s' " - "object needs an argument", - descr_name((PyDescrObject *)descr), "?", - PyDescr_TYPE(descr)->tp_name); - return NULL; + "%.200s() takes no arguments (%zd given)", name, nargs); } - self = args[0]; - if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self), - (PyObject *)PyDescr_TYPE(descr))) { - PyErr_Format(PyExc_TypeError, - "descriptor '%V' for '%.100s' objects " - "doesn't apply to a '%.100s' object", - descr_name((PyDescrObject *)descr), "?", - PyDescr_TYPE(descr)->tp_name, - self->ob_type->tp_name); - return NULL; + else { + result = ((PyCFunction)meth)(self, NULL); } + PyMethodDescr_VECTORCALL_END +} - result = _PyMethodDef_RawFastCallKeywords(descr->d_method, self, - args+1, nargs-1, kwnames); - result = _Py_CheckFunctionResult((PyObject *)descr, result, NULL); - return result; +static PyObject * +_PyMethodDescr_Vectorcall_O( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + PyMethodDescr_VECTORCALL_BEGIN(0) + if (nargs != 1) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes exactly one argument (%zd given)", name, nargs); + } + else { + result = ((PyCFunction)meth)(self, args[0]); + } + PyMethodDescr_VECTORCALL_END } + /* Instances of classmethod_descriptor are unlikely to be called directly. For one, the analogous class "classmethod" (for Python classes) is not callable. Second, users are not likely to access a classmethod_descriptor @@ -540,7 +611,7 @@ PyTypeObject PyMethodDescr_Type = { 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ - (ternaryfunc)methoddescr_call, /* tp_call */ + PyVectorcall_Call, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ @@ -738,13 +809,40 @@ descr_new(PyTypeObject *descrtype, PyTypeObject *type, const char *name) PyObject * PyDescr_NewMethod(PyTypeObject *type, PyMethodDef *method) { + /* Figure out correct vectorcall function to use */ + vectorcallfunc vectorcall; + switch (method->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O | METH_KEYWORDS)) + { + case METH_VARARGS: + vectorcall = _PyMethodDescr_Vectorcall_VARARGS; + break; + case METH_VARARGS | METH_KEYWORDS: + vectorcall = _PyMethodDescr_Vectorcall_VARARGS_KEYWORDS; + break; + case METH_FASTCALL: + vectorcall = _PyMethodDescr_Vectorcall_FASTCALL; + break; + case METH_FASTCALL | METH_KEYWORDS: + vectorcall = _PyMethodDescr_Vectorcall_FASTCALL_KEYWORDS; + break; + case METH_NOARGS: + vectorcall = _PyMethodDescr_Vectorcall_NOARGS; + break; + case METH_O: + vectorcall = _PyMethodDescr_Vectorcall_O; + break; + default: + PyErr_SetString(PyExc_SystemError, "bad call flags"); + return NULL; + } + PyMethodDescrObject *descr; descr = (PyMethodDescrObject *)descr_new(&PyMethodDescr_Type, type, method->ml_name); if (descr != NULL) { descr->d_method = method; - descr->vectorcall = _PyMethodDescr_Vectorcall; + descr->vectorcall = vectorcall; } return (PyObject *)descr; } diff --git a/Objects/methodobject.c b/Objects/methodobject.c index c3bc0184796e40..e81b5b04ef9203 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -28,6 +28,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 = _PyCFunction_Vectorcall_FASTCALL; + break; + case METH_FASTCALL | METH_KEYWORDS: + vectorcall = _PyCFunction_Vectorcall_FASTCALL_KEYWORDS; + break; + case METH_NOARGS: + vectorcall = _PyCFunction_Vectorcall_NOARGS; + break; + case METH_O: + vectorcall = _PyCFunction_Vectorcall_O; + break; + default: + PyErr_SetString(PyExc_SystemError, "bad call flags"); + return NULL; + } + PyCFunctionObject *op; op = free_list; if (op != NULL) { @@ -46,14 +73,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; } diff --git a/Python/ceval.c b/Python/ceval.c index 5d29d41cf80c3a..6b3ea0fcea4845 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4938,7 +4938,7 @@ trace_call_function(PyThreadState *tstate, { PyObject *x; if (PyCFunction_Check(func)) { - C_TRACE(x, _PyCFunction_Vectorcall(func, args, nargs, kwnames)); + C_TRACE(x, _PyObject_Vectorcall(func, args, nargs, kwnames)); return x; } else if (Py_TYPE(func) == &PyMethodDescr_Type && nargs > 0) { @@ -4954,9 +4954,9 @@ trace_call_function(PyThreadState *tstate, if (func == NULL) { return NULL; } - C_TRACE(x, _PyCFunction_Vectorcall(func, - args+1, nargs-1, - kwnames)); + C_TRACE(x, _PyObject_Vectorcall(func, + args+1, nargs-1, + kwnames)); Py_DECREF(func); return x; } diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index abfea964a3d4f8..c40ddc773587a1 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -1563,8 +1563,8 @@ def is_other_python_frame(self): if not caller: return False - if caller in ('_PyCFunction_Vectorcall', - 'cfunction_call_varargs'): + if (caller.startswith('_PyCFunction_Vectorcall') or + caller == 'cfunction_call_varargs'): arg_name = 'func' # Within that frame: # "func" is the local containing the PyObject* of the From d9422852fb2d5795d9584094e8f8f1bc0858444f Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 24 Jun 2019 15:21:28 +0200 Subject: [PATCH 2/4] Make PyCFunction vectorcall truly private and break up big macro --- Include/methodobject.h | 11 ---- Objects/call.c | 81 ----------------------- Objects/methodobject.c | 146 +++++++++++++++++++++++++++++++++++++++-- Tools/gdb/libpython.py | 2 +- 4 files changed, 143 insertions(+), 97 deletions(-) diff --git a/Include/methodobject.h b/Include/methodobject.h index aedb8706daf5e9..b43847110e1844 100644 --- a/Include/methodobject.h +++ b/Include/methodobject.h @@ -41,17 +41,6 @@ PyAPI_FUNC(int) PyCFunction_GetFlags(PyObject *); #endif PyAPI_FUNC(PyObject *) PyCFunction_Call(PyObject *, PyObject *, PyObject *); -#ifdef Py_BUILD_CORE -extern PyObject * _PyCFunction_Vectorcall_FASTCALL( - PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); -extern PyObject * _PyCFunction_Vectorcall_FASTCALL_KEYWORDS( - PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); -extern PyObject * _PyCFunction_Vectorcall_NOARGS( - PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); -extern PyObject * _PyCFunction_Vectorcall_O( - PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); -#endif - struct PyMethodDef { const char *ml_name; /* The name of the built-in function/method */ PyCFunction ml_meth; /* The C function that implements it */ diff --git a/Objects/call.c b/Objects/call.c index 72352d1f9e65ed..e84a6df6099c44 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -625,87 +625,6 @@ _PyMethodDef_RawFastCallKeywords(PyMethodDef *method, PyObject *self, return result; } - -/* Vectorcall functions for each of the PyCFunction calling conventions, - * except for METH_VARARGS (possibly combined with METH_KEYWORDS) which - * doesn't use vectorcall. - * - * First two macros to define common boilerplate - */ -#define PyCFunction_VECTORCALL_BEGIN(ALLOW_KWARGS) \ - assert(!PyErr_Occurred()); \ - assert(PyCFunction_Check(func)); \ - PyCFunctionObject *f = (PyCFunctionObject *)func; \ - Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); \ - PyObject *self = f->m_self; \ - PyMethodDef *method = f->m_ml; \ - const char *name = method->ml_name; \ - void(*meth)(void) = (void(*)(void))method->ml_meth; \ - PyObject *result = NULL; \ - \ - if (!ALLOW_KWARGS && kwnames && PyTuple_GET_SIZE(kwnames) > 0) { \ - PyErr_Format(PyExc_TypeError, \ - "%.200s() takes no keyword arguments", name); \ - return NULL; \ - } \ - if (Py_EnterRecursiveCall(" while calling a Python object")) { \ - return NULL; \ - } - -#define PyCFunction_VECTORCALL_END \ - Py_LeaveRecursiveCall(); \ - return _Py_CheckFunctionResult(func, result, NULL); - -/* Now the actual vectorcall functions */ -PyObject * -_PyCFunction_Vectorcall_FASTCALL( - PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) -{ - PyCFunction_VECTORCALL_BEGIN(0) - result = ((_PyCFunctionFast)meth)(self, args, nargs); - PyCFunction_VECTORCALL_END -} - -PyObject * -_PyCFunction_Vectorcall_FASTCALL_KEYWORDS( - PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) -{ - PyCFunction_VECTORCALL_BEGIN(1) - result = ((_PyCFunctionFastWithKeywords)meth)(self, args, nargs, kwnames); - PyCFunction_VECTORCALL_END -} - -PyObject * -_PyCFunction_Vectorcall_NOARGS( - PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) -{ - PyCFunction_VECTORCALL_BEGIN(0) - if (nargs != 0) { - PyErr_Format(PyExc_TypeError, - "%.200s() takes no arguments (%zd given)", name, nargs); - } - else { - result = ((PyCFunction)meth)(self, NULL); - } - PyCFunction_VECTORCALL_END -} - -PyObject * -_PyCFunction_Vectorcall_O( - PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) -{ - PyCFunction_VECTORCALL_BEGIN(0) - if (nargs != 1) { - PyErr_Format(PyExc_TypeError, - "%.200s() takes exactly one argument (%zd given)", name, nargs); - } - else { - result = ((PyCFunction)meth)(self, args[0]); - } - PyCFunction_VECTORCALL_END -} - - static PyObject * cfunction_call_varargs(PyObject *func, PyObject *args, PyObject *kwargs) { diff --git a/Objects/methodobject.c b/Objects/methodobject.c index e81b5b04ef9203..7e42c0046bf0ad 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 inline PyObject * vectorcall_FASTCALL( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +static inline PyObject * vectorcall_FASTCALL_KEYWORDS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +static inline PyObject * vectorcall_NOARGS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +static inline PyObject * vectorcall_O( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); + + PyObject * PyCFunction_New(PyMethodDef *ml, PyObject *self) { @@ -39,16 +50,16 @@ PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module) vectorcall = NULL; break; case METH_FASTCALL: - vectorcall = _PyCFunction_Vectorcall_FASTCALL; + vectorcall = vectorcall_FASTCALL; break; case METH_FASTCALL | METH_KEYWORDS: - vectorcall = _PyCFunction_Vectorcall_FASTCALL_KEYWORDS; + vectorcall = vectorcall_FASTCALL_KEYWORDS; break; case METH_NOARGS: - vectorcall = _PyCFunction_Vectorcall_NOARGS; + vectorcall = vectorcall_NOARGS; break; case METH_O: - vectorcall = _PyCFunction_Vectorcall_O; + vectorcall = vectorcall_O; break; default: PyErr_SetString(PyExc_SystemError, "bad call flags"); @@ -353,3 +364,130 @@ _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 inline PyObject * +get_self(PyObject *func) { + return ((PyCFunctionObject *)func)->m_self; +} + +static inline const char * +get_name(PyObject *func) { + PyMethodDef *method = ((PyCFunctionObject *)func)->m_ml; + return method->ml_name; +} + +typedef void (*funcptr)(void); + +static inline funcptr +get_meth(PyObject *func) { + PyMethodDef *method = ((PyCFunctionObject *)func)->m_ml; + return (funcptr)method->ml_meth; +} + +static inline int +check_no_kwargs(PyObject *func, PyObject *kwnames) { + assert(PyCFunction_Check(func)); + if (kwnames && PyTuple_GET_SIZE(kwnames) > 0) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no keyword arguments", get_name(func)); + return 0; + } + return 1; +} + +static inline int +vectorcall_begin(PyObject *func) { + assert(!PyErr_Occurred()); + assert(PyCFunction_Check(func)); + if (Py_EnterRecursiveCall(" while calling a Python object")) { + return 0; + } + return 1; +} + +static inline PyObject * +vectorcall_end(PyObject *func, PyObject *result) { + Py_LeaveRecursiveCall(); + return _Py_CheckFunctionResult(func, result, NULL); +} + + +/* Now the actual vectorcall functions */ +static inline PyObject * +vectorcall_FASTCALL( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + if (!check_no_kwargs(func, kwnames)) { + return NULL; + } + if (!vectorcall_begin(func)) { + return NULL; + } + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + _PyCFunctionFast meth = (_PyCFunctionFast)get_meth(func); + PyObject *result = meth(get_self(func), args, nargs); + return vectorcall_end(func, result); +} + +static inline PyObject * +vectorcall_FASTCALL_KEYWORDS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + if (!vectorcall_begin(func)) { + return NULL; + } + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + _PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords)get_meth(func); + PyObject *result = meth(get_self(func), args, nargs, kwnames); + return vectorcall_end(func, result); +} + +static inline PyObject * +vectorcall_NOARGS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + if (!check_no_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; + } + if (!vectorcall_begin(func)) { + return NULL; + } + PyCFunction meth = (PyCFunction)get_meth(func); + PyObject *result = meth(get_self(func), NULL); + return vectorcall_end(func, result); +} + +static inline PyObject * +vectorcall_O( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + if (!check_no_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; + } + if (!vectorcall_begin(func)) { + return NULL; + } + PyCFunction meth = (PyCFunction)get_meth(func); + PyObject *result = meth(get_self(func), args[0]); + return vectorcall_end(func, result); +} diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index c40ddc773587a1..a7f89a86249341 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -1563,7 +1563,7 @@ def is_other_python_frame(self): if not caller: return False - if (caller.startswith('_PyCFunction_Vectorcall') or + if (caller.startswith('vectorcall_') or caller == 'cfunction_call_varargs'): arg_name = 'func' # Within that frame: From 54e4e69c0f937c039db8977013d196a7a34912d1 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 24 Jun 2019 16:29:26 +0200 Subject: [PATCH 3/4] Break up the long macro in descrobject.c --- Objects/descrobject.c | 221 +++++++++++++++++++++++++++--------------- 1 file changed, 143 insertions(+), 78 deletions(-) diff --git a/Objects/descrobject.c b/Objects/descrobject.c index e7f17313b68f95..2ce9fcca08f350 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -229,145 +229,210 @@ getset_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value) /* Vectorcall functions for each of the PyMethodDescr calling conventions. * - * First two macros to define common boilerplate + * First, common helpers */ -#define PyMethodDescr_VECTORCALL_BEGIN(ALLOW_KWARGS) \ - assert(!PyErr_Occurred()); \ - assert(PyObject_TypeCheck(func, &PyMethodDescr_Type)); \ - PyMethodDescrObject *f = (PyMethodDescrObject *)func; \ - Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); \ - PyMethodDef *method = f->d_method; \ - const char *name = method->ml_name; \ - void(*meth)(void) = (void(*)(void))method->ml_meth; \ - PyObject *result = NULL; \ - \ - /* self is the first positional argument */ \ - if (nargs < 1) { \ - PyErr_Format(PyExc_TypeError, \ - "descriptor '%.200s' of '%.100s' " \ - "object needs an argument", \ - name, PyDescr_TYPE(func)->tp_name); \ - return NULL; \ - } \ - PyObject *self = args[0]; \ - args++; \ - nargs--; \ - if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self), \ - (PyObject *)PyDescr_TYPE(func))) \ - { \ - PyErr_Format(PyExc_TypeError, \ - "descriptor '%.200s' for '%.100s' objects " \ - "doesn't apply to a '%.100s' object", \ - name, PyDescr_TYPE(func)->tp_name, \ - Py_TYPE(self)->tp_name); \ - return NULL; \ - } \ - \ - if (!ALLOW_KWARGS && kwnames && PyTuple_GET_SIZE(kwnames) > 0) { \ - PyErr_Format(PyExc_TypeError, \ - "%.200s() takes no keyword arguments", name); \ - return NULL; \ - } \ - if (Py_EnterRecursiveCall(" while calling a Python object")) { \ - return NULL; \ - } - -#define PyMethodDescr_VECTORCALL_END \ - Py_LeaveRecursiveCall(); \ +static inline const char * +get_name(PyObject *func) { + return ((PyMethodDescrObject *)func)->d_method->ml_name; +} + +typedef void (*funcptr)(void); + +static inline funcptr +get_meth(PyObject *func) { + PyMethodDef *method = ((PyMethodDescrObject *)func)->d_method; + return (funcptr)method->ml_meth; +} + +static inline int +vectorcall_check(PyObject *func, size_t nargs, PyObject *const *args) { + assert(!PyErr_Occurred()); + assert(PyObject_TypeCheck(func, &PyMethodDescr_Type)); + if (nargs < 1) { + PyErr_Format(PyExc_TypeError, + "descriptor '%.200s' of '%.100s' " + "object needs an argument", + get_name(func), PyDescr_TYPE(func)->tp_name); + return 0; + } + PyObject *self = args[0]; + if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self), + (PyObject *)PyDescr_TYPE(func))) + { + PyErr_Format(PyExc_TypeError, + "descriptor '%.200s' for '%.100s' objects " + "doesn't apply to a '%.100s' object", + get_name(func), PyDescr_TYPE(func)->tp_name, + Py_TYPE(self)->tp_name); + return 0; + } + return 1; +} + +static inline int +check_no_kwargs(PyObject *func, PyObject *kwnames) { + if (kwnames && PyTuple_GET_SIZE(kwnames) > 0) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no keyword arguments", get_name(func)); + return 0; + } + return 1; +} + +static inline int +vectorcall_begin() { + if (Py_EnterRecursiveCall(" while calling a Python object")) { + return 0; + } + return 1; +} + +static inline PyObject * +vectorcall_end(PyObject *func, PyObject *result) { + Py_LeaveRecursiveCall(); return _Py_CheckFunctionResult(func, result, NULL); +} /* Now the actual vectorcall functions */ static PyObject * _PyMethodDescr_Vectorcall_VARARGS( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { - PyMethodDescr_VECTORCALL_BEGIN(0) - /* Create a temporary tuple for positional arguments */ - PyObject *argstuple = _PyTuple_FromArray(args, nargs); + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (!vectorcall_check(func, nargs, args)) { + return NULL; + } + if (!check_no_kwargs(func, kwnames)) { + return NULL; + } + PyObject *argstuple = _PyTuple_FromArray(args+1, nargs-1); if (argstuple == NULL) { - goto exit; + return NULL; + } + if (!vectorcall_begin()) { + return NULL; } - result = ((PyCFunction)meth)(self, argstuple); + PyCFunction meth = (PyCFunction)get_meth(func); + PyObject *result = meth(args[0], argstuple); Py_DECREF(argstuple); -exit: - PyMethodDescr_VECTORCALL_END + return vectorcall_end(func, result); } static PyObject * _PyMethodDescr_Vectorcall_VARARGS_KEYWORDS( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { - PyMethodDescr_VECTORCALL_BEGIN(1) - /* Create a temporary tuple for positional arguments */ - PyObject *argstuple = _PyTuple_FromArray(args, nargs); + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (!vectorcall_check(func, nargs, args)) { + return NULL; + } + PyObject *argstuple = _PyTuple_FromArray(args+1, nargs-1); if (argstuple == NULL) { - goto exit; + return NULL; } + PyObject *result = NULL; /* Create a temporary dict for keyword arguments */ PyObject *kwdict; if (kwnames == NULL || PyTuple_GET_SIZE(kwnames) == 0) { kwdict = NULL; } else { - kwdict = _PyStack_AsDict(args + nargs, kwnames); + kwdict = _PyStack_AsDict(args+1 + nargs, kwnames); if (kwdict == NULL) { Py_DECREF(argstuple); - goto exit; + return NULL; } } - result = ((PyCFunctionWithKeywords)meth)(self, argstuple, kwdict); + if (!vectorcall_begin()) { + return NULL; + } + PyCFunctionWithKeywords meth = (PyCFunctionWithKeywords)get_meth(func); + result = meth(args[0], argstuple, kwdict); Py_DECREF(argstuple); Py_XDECREF(kwdict); -exit: - PyMethodDescr_VECTORCALL_END + return vectorcall_end(func, result); } static PyObject * _PyMethodDescr_Vectorcall_FASTCALL( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { - PyMethodDescr_VECTORCALL_BEGIN(0) - result = ((_PyCFunctionFast)meth)(self, args, nargs); - PyMethodDescr_VECTORCALL_END + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (!vectorcall_check(func, nargs, args)) { + return NULL; + } + if (!check_no_kwargs(func, kwnames)) { + return NULL; + } + if (!vectorcall_begin()) { + return NULL; + } + _PyCFunctionFast meth = (_PyCFunctionFast)get_meth(func); + PyObject *result = meth(args[0], args+1, nargs-1); + return vectorcall_end(func, result); } static PyObject * _PyMethodDescr_Vectorcall_FASTCALL_KEYWORDS( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { - PyMethodDescr_VECTORCALL_BEGIN(1) - result = ((_PyCFunctionFastWithKeywords)meth)(self, args, nargs, kwnames); - PyMethodDescr_VECTORCALL_END + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (!vectorcall_check(func, nargs, args)) { + return NULL; + } + vectorcall_begin(); + _PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords)get_meth(func); + PyObject *result = meth(args[0], args+1, nargs-1, kwnames); + return vectorcall_end(func, result); } static PyObject * _PyMethodDescr_Vectorcall_NOARGS( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { - PyMethodDescr_VECTORCALL_BEGIN(0) - if (nargs != 0) { + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (!vectorcall_check(func, nargs, args)) { + return NULL; + } + if (!check_no_kwargs(func, kwnames)) { + return NULL; + } + if (nargs != 1) { PyErr_Format(PyExc_TypeError, - "%.200s() takes no arguments (%zd given)", name, nargs); + "%.200s() takes no arguments (%zd given)", get_name(func), nargs-1); + return NULL; } - else { - result = ((PyCFunction)meth)(self, NULL); + if (!vectorcall_begin()) { + return NULL; } - PyMethodDescr_VECTORCALL_END + PyCFunction meth = (PyCFunction)get_meth(func); + PyObject *result = meth(args[0], NULL); + return vectorcall_end(func, result); } static PyObject * _PyMethodDescr_Vectorcall_O( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { - PyMethodDescr_VECTORCALL_BEGIN(0) - if (nargs != 1) { + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (!vectorcall_check(func, nargs, args)) { + return NULL; + } + if (!check_no_kwargs(func, kwnames)) { + return NULL; + } + if (nargs != 2) { PyErr_Format(PyExc_TypeError, - "%.200s() takes exactly one argument (%zd given)", name, nargs); + "%.200s() takes exactly one argument (%zd given)", + get_name(func), nargs-1); + return NULL; } - else { - result = ((PyCFunction)meth)(self, args[0]); + if (!vectorcall_begin()) { + return NULL; } - PyMethodDescr_VECTORCALL_END + PyCFunction meth = (PyCFunction)get_meth(func); + PyObject *result = meth(args[0], args[1]); + return vectorcall_end(func, result); } From 800b8a81014ab272ee8abf4503be95d09edda13c Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 26 Jun 2019 13:34:42 +0200 Subject: [PATCH 4/4] bpo-36974: further cleanup and fixes --- Lib/test/test_call.py | 2 + Objects/call.c | 2 +- Objects/descrobject.c | 153 ++++++++++++++++++----------------------- Objects/methodobject.c | 125 ++++++++++++++++----------------- Tools/gdb/libpython.py | 2 +- 5 files changed, 130 insertions(+), 154 deletions(-) diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index b252ca1076ad99..0bff7ded4670f1 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -586,6 +586,8 @@ def __call__(self, *args): return super().__call__(*args) calls += [ + (dict.update, ({},), {"key":True}, None), + ({}.update, ({},), {"key":True}, None), (MethodDescriptorHeap(), (0,), {}, True), (MethodDescriptorOverridden(), (0,), {}, 'new'), (MethodDescriptorSuper(), (0,), {}, True), diff --git a/Objects/call.c b/Objects/call.c index e84a6df6099c44..861ab87a074532 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -216,7 +216,7 @@ PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs) PyObject *result = func(callable, args, nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames); _PyStack_UnpackDict_Free(args, nargs, kwnames); - return result; + return _Py_CheckFunctionResult(callable, result, NULL); } diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 2ce9fcca08f350..39c6540b27bb9e 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -231,21 +231,17 @@ getset_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value) * * First, common helpers */ -static inline const char * +static const char * get_name(PyObject *func) { + assert(PyObject_TypeCheck(func, &PyMethodDescr_Type)); return ((PyMethodDescrObject *)func)->d_method->ml_name; } typedef void (*funcptr)(void); -static inline funcptr -get_meth(PyObject *func) { - PyMethodDef *method = ((PyMethodDescrObject *)func)->d_method; - return (funcptr)method->ml_meth; -} - static inline int -vectorcall_check(PyObject *func, size_t nargs, PyObject *const *args) { +method_check_args(PyObject *func, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ assert(!PyErr_Occurred()); assert(PyObject_TypeCheck(func, &PyMethodDescr_Type)); if (nargs < 1) { @@ -253,7 +249,7 @@ vectorcall_check(PyObject *func, size_t nargs, PyObject *const *args) { "descriptor '%.200s' of '%.100s' " "object needs an argument", get_name(func), PyDescr_TYPE(func)->tp_name); - return 0; + return -1; } PyObject *self = args[0]; if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self), @@ -264,66 +260,55 @@ vectorcall_check(PyObject *func, size_t nargs, PyObject *const *args) { "doesn't apply to a '%.100s' object", get_name(func), PyDescr_TYPE(func)->tp_name, Py_TYPE(self)->tp_name); - return 0; + return -1; } - return 1; -} - -static inline int -check_no_kwargs(PyObject *func, PyObject *kwnames) { - if (kwnames && PyTuple_GET_SIZE(kwnames) > 0) { + if (kwnames && PyTuple_GET_SIZE(kwnames)) { PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", get_name(func)); - return 0; + return -1; } - return 1; + return 0; } -static inline int -vectorcall_begin() { +static inline funcptr +method_enter_call(PyObject *func) +{ if (Py_EnterRecursiveCall(" while calling a Python object")) { - return 0; + return NULL; } - return 1; -} - -static inline PyObject * -vectorcall_end(PyObject *func, PyObject *result) { - Py_LeaveRecursiveCall(); - return _Py_CheckFunctionResult(func, result, NULL); + return (funcptr)((PyMethodDescrObject *)func)->d_method->ml_meth; } /* Now the actual vectorcall functions */ static PyObject * -_PyMethodDescr_Vectorcall_VARARGS( +method_vectorcall_VARARGS( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); - if (!vectorcall_check(func, nargs, args)) { - return NULL; - } - if (!check_no_kwargs(func, kwnames)) { + if (method_check_args(func, args, nargs, kwnames)) { return NULL; } PyObject *argstuple = _PyTuple_FromArray(args+1, nargs-1); if (argstuple == NULL) { return NULL; } - if (!vectorcall_begin()) { + PyCFunction meth = (PyCFunction)method_enter_call(func); + if (meth == NULL) { + Py_DECREF(argstuple); return NULL; } - PyCFunction meth = (PyCFunction)get_meth(func); PyObject *result = meth(args[0], argstuple); Py_DECREF(argstuple); - return vectorcall_end(func, result); + Py_LeaveRecursiveCall(); + return result; } static PyObject * -_PyMethodDescr_Vectorcall_VARARGS_KEYWORDS( +method_vectorcall_VARARGS_KEYWORDS( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); - if (!vectorcall_check(func, nargs, args)) { + if (method_check_args(func, args, nargs, NULL)) { return NULL; } PyObject *argstuple = _PyTuple_FromArray(args+1, nargs-1); @@ -332,69 +317,68 @@ _PyMethodDescr_Vectorcall_VARARGS_KEYWORDS( } PyObject *result = NULL; /* Create a temporary dict for keyword arguments */ - PyObject *kwdict; - if (kwnames == NULL || PyTuple_GET_SIZE(kwnames) == 0) { - kwdict = NULL; - } - else { - kwdict = _PyStack_AsDict(args+1 + nargs, kwnames); + PyObject *kwdict = NULL; + if (kwnames != NULL && PyTuple_GET_SIZE(kwnames) > 0) { + kwdict = _PyStack_AsDict(args + nargs, kwnames); if (kwdict == NULL) { - Py_DECREF(argstuple); - return NULL; + goto exit; } } - if (!vectorcall_begin()) { - return NULL; + PyCFunctionWithKeywords meth = (PyCFunctionWithKeywords) + method_enter_call(func); + if (meth == NULL) { + goto exit; } - PyCFunctionWithKeywords meth = (PyCFunctionWithKeywords)get_meth(func); result = meth(args[0], argstuple, kwdict); + Py_LeaveRecursiveCall(); +exit: Py_DECREF(argstuple); Py_XDECREF(kwdict); - return vectorcall_end(func, result); + return result; } static PyObject * -_PyMethodDescr_Vectorcall_FASTCALL( +method_vectorcall_FASTCALL( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); - if (!vectorcall_check(func, nargs, args)) { + if (method_check_args(func, args, nargs, kwnames)) { return NULL; } - if (!check_no_kwargs(func, kwnames)) { + _PyCFunctionFast meth = (_PyCFunctionFast) + method_enter_call(func); + if (meth == NULL) { return NULL; } - if (!vectorcall_begin()) { - return NULL; - } - _PyCFunctionFast meth = (_PyCFunctionFast)get_meth(func); PyObject *result = meth(args[0], args+1, nargs-1); - return vectorcall_end(func, result); + Py_LeaveRecursiveCall(); + return result; } static PyObject * -_PyMethodDescr_Vectorcall_FASTCALL_KEYWORDS( +method_vectorcall_FASTCALL_KEYWORDS( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); - if (!vectorcall_check(func, nargs, args)) { + if (method_check_args(func, args, nargs, NULL)) { + return NULL; + } + _PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords) + method_enter_call(func); + if (meth == NULL) { return NULL; } - vectorcall_begin(); - _PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords)get_meth(func); PyObject *result = meth(args[0], args+1, nargs-1, kwnames); - return vectorcall_end(func, result); + Py_LeaveRecursiveCall(); + return result; } static PyObject * -_PyMethodDescr_Vectorcall_NOARGS( +method_vectorcall_NOARGS( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); - if (!vectorcall_check(func, nargs, args)) { - return NULL; - } - if (!check_no_kwargs(func, kwnames)) { + if (method_check_args(func, args, nargs, kwnames)) { return NULL; } if (nargs != 1) { @@ -402,23 +386,21 @@ _PyMethodDescr_Vectorcall_NOARGS( "%.200s() takes no arguments (%zd given)", get_name(func), nargs-1); return NULL; } - if (!vectorcall_begin()) { + PyCFunction meth = (PyCFunction)method_enter_call(func); + if (meth == NULL) { return NULL; } - PyCFunction meth = (PyCFunction)get_meth(func); PyObject *result = meth(args[0], NULL); - return vectorcall_end(func, result); + Py_LeaveRecursiveCall(); + return result; } static PyObject * -_PyMethodDescr_Vectorcall_O( +method_vectorcall_O( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); - if (!vectorcall_check(func, nargs, args)) { - return NULL; - } - if (!check_no_kwargs(func, kwnames)) { + if (method_check_args(func, args, nargs, kwnames)) { return NULL; } if (nargs != 2) { @@ -427,12 +409,13 @@ _PyMethodDescr_Vectorcall_O( get_name(func), nargs-1); return NULL; } - if (!vectorcall_begin()) { + PyCFunction meth = (PyCFunction)method_enter_call(func); + if (meth == NULL) { return NULL; } - PyCFunction meth = (PyCFunction)get_meth(func); PyObject *result = meth(args[0], args[1]); - return vectorcall_end(func, result); + Py_LeaveRecursiveCall(); + return result; } @@ -879,22 +862,22 @@ PyDescr_NewMethod(PyTypeObject *type, PyMethodDef *method) switch (method->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O | METH_KEYWORDS)) { case METH_VARARGS: - vectorcall = _PyMethodDescr_Vectorcall_VARARGS; + vectorcall = method_vectorcall_VARARGS; break; case METH_VARARGS | METH_KEYWORDS: - vectorcall = _PyMethodDescr_Vectorcall_VARARGS_KEYWORDS; + vectorcall = method_vectorcall_VARARGS_KEYWORDS; break; case METH_FASTCALL: - vectorcall = _PyMethodDescr_Vectorcall_FASTCALL; + vectorcall = method_vectorcall_FASTCALL; break; case METH_FASTCALL | METH_KEYWORDS: - vectorcall = _PyMethodDescr_Vectorcall_FASTCALL_KEYWORDS; + vectorcall = method_vectorcall_FASTCALL_KEYWORDS; break; case METH_NOARGS: - vectorcall = _PyMethodDescr_Vectorcall_NOARGS; + vectorcall = method_vectorcall_NOARGS; break; case METH_O: - vectorcall = _PyMethodDescr_Vectorcall_O; + vectorcall = method_vectorcall_O; break; default: PyErr_SetString(PyExc_SystemError, "bad call flags"); diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 7e42c0046bf0ad..3494f11d80fe75 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -20,13 +20,13 @@ static int numfree = 0; #undef PyCFunction_New /* Forward declarations */ -static inline PyObject * vectorcall_FASTCALL( +static PyObject * cfunction_vectorcall_FASTCALL( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); -static inline PyObject * vectorcall_FASTCALL_KEYWORDS( +static PyObject * cfunction_vectorcall_FASTCALL_KEYWORDS( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); -static inline PyObject * vectorcall_NOARGS( +static PyObject * cfunction_vectorcall_NOARGS( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); -static inline PyObject * vectorcall_O( +static PyObject * cfunction_vectorcall_O( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); @@ -50,16 +50,16 @@ PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module) vectorcall = NULL; break; case METH_FASTCALL: - vectorcall = vectorcall_FASTCALL; + vectorcall = cfunction_vectorcall_FASTCALL; break; case METH_FASTCALL | METH_KEYWORDS: - vectorcall = vectorcall_FASTCALL_KEYWORDS; + vectorcall = cfunction_vectorcall_FASTCALL_KEYWORDS; break; case METH_NOARGS: - vectorcall = vectorcall_NOARGS; + vectorcall = cfunction_vectorcall_NOARGS; break; case METH_O: - vectorcall = vectorcall_O; + vectorcall = cfunction_vectorcall_O; break; default: PyErr_SetString(PyExc_SystemError, "bad call flags"); @@ -372,88 +372,77 @@ _PyCFunction_DebugMallocStats(FILE *out) * * First, common helpers */ -static inline PyObject * -get_self(PyObject *func) { - return ((PyCFunctionObject *)func)->m_self; -} - -static inline const char * -get_name(PyObject *func) { +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 funcptr -get_meth(PyObject *func) { - PyMethodDef *method = ((PyCFunctionObject *)func)->m_ml; - return (funcptr)method->ml_meth; -} - static inline int -check_no_kwargs(PyObject *func, PyObject *kwnames) { +cfunction_check_kwargs(PyObject *func, PyObject *kwnames) +{ + assert(!PyErr_Occurred()); assert(PyCFunction_Check(func)); - if (kwnames && PyTuple_GET_SIZE(kwnames) > 0) { + if (kwnames && PyTuple_GET_SIZE(kwnames)) { PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", get_name(func)); - return 0; + return -1; } - return 1; + return 0; } -static inline int -vectorcall_begin(PyObject *func) { - assert(!PyErr_Occurred()); - assert(PyCFunction_Check(func)); +static inline funcptr +cfunction_enter_call(PyObject *func) +{ if (Py_EnterRecursiveCall(" while calling a Python object")) { - return 0; + return NULL; } - return 1; + return (funcptr)PyCFunction_GET_FUNCTION(func); } -static inline PyObject * -vectorcall_end(PyObject *func, PyObject *result) { - Py_LeaveRecursiveCall(); - return _Py_CheckFunctionResult(func, result, NULL); -} - - /* Now the actual vectorcall functions */ -static inline PyObject * -vectorcall_FASTCALL( +static PyObject * +cfunction_vectorcall_FASTCALL( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { - if (!check_no_kwargs(func, kwnames)) { + if (cfunction_check_kwargs(func, kwnames)) { return NULL; } - if (!vectorcall_begin(func)) { + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + _PyCFunctionFast meth = (_PyCFunctionFast) + cfunction_enter_call(func); + if (meth == NULL) { return NULL; } - Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); - _PyCFunctionFast meth = (_PyCFunctionFast)get_meth(func); - PyObject *result = meth(get_self(func), args, nargs); - return vectorcall_end(func, result); + PyObject *result = meth(PyCFunction_GET_SELF(func), args, nargs); + Py_LeaveRecursiveCall(); + return result; } -static inline PyObject * -vectorcall_FASTCALL_KEYWORDS( +static PyObject * +cfunction_vectorcall_FASTCALL_KEYWORDS( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { - if (!vectorcall_begin(func)) { + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + _PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords) + cfunction_enter_call(func); + if (meth == NULL) { return NULL; } - Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); - _PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords)get_meth(func); - PyObject *result = meth(get_self(func), args, nargs, kwnames); - return vectorcall_end(func, result); + PyObject *result = meth(PyCFunction_GET_SELF(func), args, nargs, kwnames); + Py_LeaveRecursiveCall(); + return result; } -static inline PyObject * -vectorcall_NOARGS( +static PyObject * +cfunction_vectorcall_NOARGS( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { - if (!check_no_kwargs(func, kwnames)) { + if (cfunction_check_kwargs(func, kwnames)) { return NULL; } Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); @@ -462,19 +451,20 @@ vectorcall_NOARGS( "%.200s() takes no arguments (%zd given)", get_name(func), nargs); return NULL; } - if (!vectorcall_begin(func)) { + PyCFunction meth = (PyCFunction)cfunction_enter_call(func); + if (meth == NULL) { return NULL; } - PyCFunction meth = (PyCFunction)get_meth(func); - PyObject *result = meth(get_self(func), NULL); - return vectorcall_end(func, result); + PyObject *result = meth(PyCFunction_GET_SELF(func), NULL); + Py_LeaveRecursiveCall(); + return result; } -static inline PyObject * -vectorcall_O( +static PyObject * +cfunction_vectorcall_O( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { - if (!check_no_kwargs(func, kwnames)) { + if (cfunction_check_kwargs(func, kwnames)) { return NULL; } Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); @@ -484,10 +474,11 @@ vectorcall_O( get_name(func), nargs); return NULL; } - if (!vectorcall_begin(func)) { + PyCFunction meth = (PyCFunction)cfunction_enter_call(func); + if (meth == NULL) { return NULL; } - PyCFunction meth = (PyCFunction)get_meth(func); - PyObject *result = meth(get_self(func), args[0]); - return vectorcall_end(func, result); + PyObject *result = meth(PyCFunction_GET_SELF(func), args[0]); + Py_LeaveRecursiveCall(); + return result; } diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index a7f89a86249341..e40f79b5c3c7d3 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -1563,7 +1563,7 @@ def is_other_python_frame(self): if not caller: return False - if (caller.startswith('vectorcall_') or + if (caller.startswith('cfunction_vectorcall_') or caller == 'cfunction_call_varargs'): arg_name = 'func' # Within that frame: