From d7a4362203dbd80afd88e89b46e649d7c3095821 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 19 Aug 2016 16:11:43 +0200 Subject: Add _PyObject_FastCall() Issue #27128: Add _PyObject_FastCall(), a new calling convention avoiding a temporary tuple to pass positional parameters in most cases, but create a temporary tuple if needed (ex: for the tp_call slot). The API is prepared to support keyword parameters, but the full implementation will come later (_PyFunction_FastCall() doesn't support keyword parameters yet). Add also: * _PyStack_AsTuple() helper function: convert a "stack" of parameters to a tuple. * _PyCFunction_FastCall(): fast call implementation for C functions * _PyFunction_FastCall(): fast call implementation for Python functions --- Objects/methodobject.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) (limited to 'Objects/methodobject.c') diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 946357f24a..0e26232194 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -145,6 +145,99 @@ PyCFunction_Call(PyObject *func, PyObject *args, PyObject *kwds) return _Py_CheckFunctionResult(func, res, NULL); } +PyObject * +_PyCFunction_FastCall(PyObject *func_obj, PyObject **args, int nargs, + PyObject *kwargs) +{ + PyCFunctionObject* func = (PyCFunctionObject*)func_obj; + PyCFunction meth = PyCFunction_GET_FUNCTION(func); + PyObject *self = PyCFunction_GET_SELF(func); + PyObject *result; + int flags; + + /* _PyCFunction_FastCall() must not be called with an exception set, + because it may clear it (directly or indirectly) and so the + caller loses its exception */ + assert(!PyErr_Occurred()); + + flags = PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST); + + switch (flags) + { + case METH_NOARGS: + if (kwargs != NULL && PyDict_Size(kwargs) != 0) { + PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", + func->m_ml->ml_name); + return NULL; + } + + if (nargs != 0) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no arguments (%zd given)", + func->m_ml->ml_name, nargs); + return NULL; + } + + result = (*meth) (self, NULL); + break; + + case METH_O: + if (kwargs != NULL && PyDict_Size(kwargs) != 0) { + PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", + func->m_ml->ml_name); + return NULL; + } + + if (nargs != 1) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes exactly one argument (%zd given)", + func->m_ml->ml_name, nargs); + return NULL; + } + + result = (*meth) (self, args[0]); + break; + + case METH_VARARGS: + case METH_VARARGS | METH_KEYWORDS: + { + /* Slow-path: create a temporary tuple */ + PyObject *tuple; + + if (!(flags & METH_KEYWORDS) && kwargs != NULL && PyDict_Size(kwargs) != 0) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no keyword arguments", + func->m_ml->ml_name); + return NULL; + } + + tuple = _PyStack_AsTuple(args, nargs); + if (tuple == NULL) { + return NULL; + } + + if (flags & METH_KEYWORDS) { + result = (*(PyCFunctionWithKeywords)meth) (self, tuple, kwargs); + } + else { + result = (*meth) (self, tuple); + } + Py_DECREF(tuple); + break; + } + + default: + PyErr_SetString(PyExc_SystemError, + "Bad call flags in PyCFunction_Call. " + "METH_OLDARGS is no longer supported!"); + return NULL; + } + + result = _Py_CheckFunctionResult(func_obj, result, NULL); + + return result; +} + /* Methods (the standard built-in methods, that is) */ static void -- cgit v1.2.1 From 9dae6156b5e3127cb8bfaa1bd36cecadaad5d60b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 22 Aug 2016 23:15:44 +0200 Subject: _PyFunction_FastCallDict() supports keyword args Issue #27809: * Rename _PyFunction_FastCall() to _PyFunction_FastCallDict() * Rename _PyCFunction_FastCall() to _PyCFunction_FastCallDict() * _PyFunction_FastCallDict() now supports keyword arguments --- Objects/methodobject.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Objects/methodobject.c') diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 0e26232194..edb2fc013c 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -146,8 +146,8 @@ PyCFunction_Call(PyObject *func, PyObject *args, PyObject *kwds) } PyObject * -_PyCFunction_FastCall(PyObject *func_obj, PyObject **args, int nargs, - PyObject *kwargs) +_PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, int nargs, + PyObject *kwargs) { PyCFunctionObject* func = (PyCFunctionObject*)func_obj; PyCFunction meth = PyCFunction_GET_FUNCTION(func); @@ -155,7 +155,7 @@ _PyCFunction_FastCall(PyObject *func_obj, PyObject **args, int nargs, PyObject *result; int flags; - /* _PyCFunction_FastCall() must not be called with an exception set, + /* _PyCFunction_FastCallDict() must not be called with an exception set, because it may clear it (directly or indirectly) and so the caller loses its exception */ assert(!PyErr_Occurred()); -- cgit v1.2.1 From c79e5123bcf2d1e514a0420e89c62fa334d57ba9 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 25 Aug 2016 00:04:09 +0200 Subject: Use Py_ssize_t type for number of arguments Issue #27848: use Py_ssize_t rather than C int for the number of function positional and keyword arguments. --- Objects/methodobject.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'Objects/methodobject.c') diff --git a/Objects/methodobject.c b/Objects/methodobject.c index edb2fc013c..394f1f4502 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -146,15 +146,20 @@ PyCFunction_Call(PyObject *func, PyObject *args, PyObject *kwds) } PyObject * -_PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, int nargs, +_PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs, PyObject *kwargs) { - PyCFunctionObject* func = (PyCFunctionObject*)func_obj; + PyCFunctionObject *func = (PyCFunctionObject*)func_obj; PyCFunction meth = PyCFunction_GET_FUNCTION(func); PyObject *self = PyCFunction_GET_SELF(func); PyObject *result; int flags; + assert(func != NULL); + assert(nargs >= 0); + assert(nargs == 0 || args != NULL); + assert(kwargs == NULL || PyDict_Check(kwargs)); + /* _PyCFunction_FastCallDict() must not be called with an exception set, because it may clear it (directly or indirectly) and so the caller loses its exception */ -- cgit v1.2.1 From 2615f24fb54cece9f22c5e2b1d206e8dd559631a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 9 Sep 2016 14:07:44 -0700 Subject: Issue #27810: Add _PyCFunction_FastCallKeywords() Use _PyCFunction_FastCallKeywords() in ceval.c: it allows to remove a lot of code from ceval.c which was only used to call C functions. --- Objects/methodobject.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'Objects/methodobject.c') diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 394f1f4502..0fe3315417 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -155,6 +155,7 @@ _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs, PyObject *result; int flags; + assert(PyCFunction_Check(func)); assert(func != NULL); assert(nargs >= 0); assert(nargs == 0 || args != NULL); @@ -243,6 +244,31 @@ _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs, return result; } +PyObject * +_PyCFunction_FastCallKeywords(PyObject *func, PyObject **stack, + Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *kwdict, *result; + Py_ssize_t nkwargs; + + assert(PyCFunction_Check(func)); + + nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); + if (nkwargs > 0) { + kwdict = _PyStack_AsDict(stack + nargs, nkwargs, kwnames, func); + if (kwdict == NULL) { + return NULL; + } + } + else { + kwdict = NULL; + } + + result = _PyCFunction_FastCallDict(func, stack, nargs, kwdict); + Py_XDECREF(kwdict); + return result; +} + /* Methods (the standard built-in methods, that is) */ static void -- cgit v1.2.1 From 14e79902895c8f92b5cf80483619fac1e3832425 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 9 Sep 2016 17:40:22 -0700 Subject: Add METH_FASTCALL calling convention Issue #27810: Add a new calling convention for C functions: PyObject* func(PyObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames); Where args is a C array of positional arguments followed by values of keyword arguments. nargs is the number of positional arguments, kwnames are keys of keyword arguments. kwnames can be NULL. --- Objects/methodobject.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'Objects/methodobject.c') diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 0fe3315417..487ccd7a30 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -97,6 +97,11 @@ PyCFunction_Call(PyObject *func, PyObject *args, PyObject *kwds) if (flags == (METH_VARARGS | METH_KEYWORDS)) { res = (*(PyCFunctionWithKeywords)meth)(self, args, kwds); } + else if (flags == METH_FASTCALL) { + PyObject **stack = &PyTuple_GET_ITEM(args, 0); + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + res = _PyCFunction_FastCallDict(func, stack, nargs, kwds); + } else { if (kwds != NULL && PyDict_Size(kwds) != 0) { PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", @@ -232,6 +237,25 @@ _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs, break; } + case METH_FASTCALL: + { + PyObject **stack; + PyObject *kwnames; + _PyCFunctionFast fastmeth = (_PyCFunctionFast)meth; + + stack = _PyStack_UnpackDict(args, nargs, kwargs, &kwnames, func_obj); + if (stack == NULL) { + return NULL; + } + + result = (*fastmeth) (self, stack, nargs, kwnames); + if (stack != args) { + PyMem_Free(stack); + } + Py_XDECREF(kwnames); + break; + } + default: PyErr_SetString(PyExc_SystemError, "Bad call flags in PyCFunction_Call. " -- cgit v1.2.1 From e74d571e49d3fdb4389b79fa1392dd75e2acb916 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 12 Sep 2016 00:52:40 +0300 Subject: Issue #27213: Fixed different issues with reworked CALL_FUNCTION* opcodes. * BUILD_TUPLE_UNPACK and BUILD_MAP_UNPACK_WITH_CALL no longer generated with single tuple or dict. * Restored more informative error messages for incorrect var-positional and var-keyword arguments. * Removed code duplications in _PyEval_EvalCodeWithName(). * Removed redundant runtime checks and parameters in _PyStack_AsDict(). * Added a workaround and enabled previously disabled test in test_traceback. * Removed dead code from the dis module. --- Objects/methodobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Objects/methodobject.c') diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 487ccd7a30..90c473ee97 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -279,7 +279,7 @@ _PyCFunction_FastCallKeywords(PyObject *func, PyObject **stack, nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); if (nkwargs > 0) { - kwdict = _PyStack_AsDict(stack + nargs, nkwargs, kwnames, func); + kwdict = _PyStack_AsDict(stack + nargs, kwnames); if (kwdict == NULL) { return NULL; } -- cgit v1.2.1 From 2f777ef4d923e4c8ef9567d06d4959fc9bd3b8e9 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 12 Sep 2016 12:55:28 +0200 Subject: ssue #27213: Reintroduce checks in _PyStack_AsDict() --- Objects/methodobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Objects/methodobject.c') diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 90c473ee97..487ccd7a30 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -279,7 +279,7 @@ _PyCFunction_FastCallKeywords(PyObject *func, PyObject **stack, nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); if (nkwargs > 0) { - kwdict = _PyStack_AsDict(stack + nargs, kwnames); + kwdict = _PyStack_AsDict(stack + nargs, nkwargs, kwnames, func); if (kwdict == NULL) { return NULL; } -- cgit v1.2.1 From 92011c90edeea1c883b0811039c3c9d102ccfbd9 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 12 Sep 2016 13:30:02 +0200 Subject: Revert change f860b7a775c5 Revert change "Issue #27213: Reintroduce checks in _PyStack_AsDict()", pushed by mistake. --- Objects/methodobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Objects/methodobject.c') diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 487ccd7a30..90c473ee97 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -279,7 +279,7 @@ _PyCFunction_FastCallKeywords(PyObject *func, PyObject **stack, nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); if (nkwargs > 0) { - kwdict = _PyStack_AsDict(stack + nargs, nkwargs, kwnames, func); + kwdict = _PyStack_AsDict(stack + nargs, kwnames); if (kwdict == NULL) { return NULL; } -- cgit v1.2.1 From af28671b2c1ff0e7b5c27f588b5620de8d17e676 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 12 Sep 2016 13:37:07 +0200 Subject: Document kwnames in _PyObject_FastCallKeywords() and _PyStack_AsDict() Issue #27213. --- Objects/methodobject.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'Objects/methodobject.c') diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 90c473ee97..19e8114d4a 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -276,6 +276,11 @@ _PyCFunction_FastCallKeywords(PyObject *func, PyObject **stack, Py_ssize_t nkwargs; assert(PyCFunction_Check(func)); + assert(nargs >= 0); + assert(kwnames == NULL || PyTuple_CheckExact(kwnames)); + assert((nargs == 0 && nkwargs == 0) || stack != NULL); + /* kwnames must only contains str strings, no subclass, and all keys must + be unique */ nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); if (nkwargs > 0) { -- cgit v1.2.1 From f82037329c779713994ac5de1c56d3aa25467c18 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 12 Sep 2016 15:33:26 -0400 Subject: Fix warning in _PyCFunction_FastCallKeywords() Issue #28105. --- Objects/methodobject.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'Objects/methodobject.c') diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 19e8114d4a..c2001f0169 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -273,7 +273,7 @@ _PyCFunction_FastCallKeywords(PyObject *func, PyObject **stack, Py_ssize_t nargs, PyObject *kwnames) { PyObject *kwdict, *result; - Py_ssize_t nkwargs; + Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); assert(PyCFunction_Check(func)); assert(nargs >= 0); @@ -282,7 +282,6 @@ _PyCFunction_FastCallKeywords(PyObject *func, PyObject **stack, /* kwnames must only contains str strings, no subclass, and all keys must be unique */ - nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); if (nkwargs > 0) { kwdict = _PyStack_AsDict(stack + nargs, kwnames); if (kwdict == NULL) { -- cgit v1.2.1