diff -r bf2d88cf039a Include/abstract.h --- a/Include/abstract.h Tue Aug 23 00:25:01 2016 +0200 +++ b/Include/abstract.h Tue Aug 23 01:25:43 2016 +0200 @@ -271,18 +271,33 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Py_ssize_t nargs); /* Call the callable object func with the "fast call" calling convention: - args is a C array for positional parameters (nargs is the number of - positional paramater), kwargs is a dictionary for keyword parameters. + args is a C array for positional arguments (nargs is the number of + positional arguments), kwargs is a dictionary for keyword arguments. If nargs is equal to zero, args can be NULL. kwargs can be NULL. nargs must be greater or equal to zero. - Return the result on success. Raise an exception on return NULL on + Return the result on success. Raise an exception and return NULL on error. */ PyAPI_FUNC(PyObject *) _PyObject_FastCallDict(PyObject *func, PyObject **args, int nargs, PyObject *kwargs); + /* Call the callable object func with the "fast call" calling convention: + args is a C array for positional arguments followed by (key, value) + pairs for keyword arguments. + + nargs is the number of positional parameters at the beginning of stack. + nkwargs is the number of (key, value) pairs at the end of stack. + + If nargs and nkwargs are equal to zero, stack can be NULL. + + Return the result on success. Raise an exception and return NULL on + error. */ + PyAPI_FUNC(PyObject *) _PyObject_FastCallKeywords( + PyObject *func, + PyObject **stack, int nargs, int nkwargs); + #define _PyObject_FastCall(func, args, nargs) \ _PyObject_FastCallDict((func), (args), (nargs), NULL) diff -r bf2d88cf039a Include/funcobject.h --- a/Include/funcobject.h Tue Aug 23 00:25:01 2016 +0200 +++ b/Include/funcobject.h Tue Aug 23 01:25:43 2016 +0200 @@ -63,6 +63,12 @@ PyAPI_FUNC(PyObject *) _PyFunction_FastC PyObject *func, PyObject **args, int nargs, PyObject *kwargs); + +PyAPI_FUNC(PyObject *) PyFunction_FastCallKeywords( + PyObject *func, + PyObject **stack, + int nargs, + int nkwargs); #endif /* Macros for direct access to these values. Type checks are *not* diff -r bf2d88cf039a Include/methodobject.h --- a/Include/methodobject.h Tue Aug 23 00:25:01 2016 +0200 +++ b/Include/methodobject.h Tue Aug 23 01:25:43 2016 +0200 @@ -41,6 +41,11 @@ PyAPI_FUNC(PyObject *) PyCFunction_Call( PyAPI_FUNC(PyObject *) _PyCFunction_FastCallDict(PyObject *func, PyObject **args, int nargs, PyObject *kwargs); + +PyAPI_FUNC(PyObject *) _PyCFunction_FastCallKeywords(PyObject *func, + PyObject **stack, + int nargs, + int nkwargs); #endif struct PyMethodDef { diff -r bf2d88cf039a Objects/abstract.c --- a/Objects/abstract.c Tue Aug 23 00:25:01 2016 +0200 +++ b/Objects/abstract.c Tue Aug 23 01:25:43 2016 +0200 @@ -2309,6 +2309,76 @@ exit: return result; } +static PyObject * +_PyStack_AsDict(PyObject **stack, Py_ssize_t nk, PyObject *func) +{ + PyObject *kwdict; + + kwdict = PyDict_New(); + if (kwdict == NULL) { + return NULL; + } + + while (--nk >= 0) { + int err; + PyObject *key = *stack++; + PyObject *value = *stack++; + if (PyDict_GetItem(kwdict, key) != NULL) { + PyErr_Format(PyExc_TypeError, + "%.200s%s got multiple values " + "for keyword argument '%U'", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func), + key); + Py_DECREF(kwdict); + return NULL; + } + err = PyDict_SetItem(kwdict, key, value); + if (err) { + Py_DECREF(kwdict); + return NULL; + } + } + return kwdict; +} + +PyObject * +_PyObject_FastCallKeywords(PyObject *func, PyObject **stack, int nargs, + int nkwargs) +{ + PyObject *args, *kwdict, *result; + + assert((nargs == 0 && nkwargs == 0) || stack != NULL); + + if (PyFunction_Check(func)) { + /* Fast-path: avoid temporary tuple or dict */ + return PyFunction_FastCallKeywords(func, stack, nargs, nkwargs); + } + + if (nkwargs == 0) { + return _PyObject_FastCallDict(func, stack, nargs, NULL); + } + + /* Slow-path: build temporary tuple and/or dict */ + args = _PyStack_AsTuple(stack, nargs); + + if (nkwargs > 0) { + kwdict = _PyStack_AsDict(stack + nargs, nkwargs, func); + if (kwdict == NULL) { + Py_DECREF(args); + return NULL; + } + } + else { + kwdict = NULL; + } + + result = PyObject_Call(func, args, kwdict); + Py_DECREF(args); + Py_XDECREF(kwdict); + return result; +} + static PyObject* call_function_tail(PyObject *callable, PyObject *args) { diff -r bf2d88cf039a Objects/methodobject.c --- a/Objects/methodobject.c Tue Aug 23 00:25:01 2016 +0200 +++ b/Objects/methodobject.c Tue Aug 23 01:25:43 2016 +0200 @@ -238,6 +238,19 @@ PyObject * return result; } +PyObject * +_PyCFunction_FastCallKeywords(PyObject *func, + PyObject **stack, int nargs, int nkwargs) +{ + assert((nargs == 0 && nkwargs == 0) || stack != NULL); + + if (nkwargs == 0) { + return _PyCFunction_FastCallDict(func, stack, nargs, NULL); + } + + return _PyObject_FastCallKeywords(func, stack, nargs, nkwargs); +} + /* Methods (the standard built-in methods, that is) */ static void diff -r bf2d88cf039a Python/ceval.c --- a/Python/ceval.c Tue Aug 23 00:25:01 2016 +0200 +++ b/Python/ceval.c Tue Aug 23 01:25:43 2016 +0200 @@ -113,8 +113,7 @@ static PyObject * call_function(PyObject #else static PyObject * call_function(PyObject ***, int); #endif -static PyObject * fast_function(PyObject *, PyObject **, int, int, int); -static PyObject * do_call(PyObject *, PyObject ***, int, int); +static PyObject * do_call(PyObject *, PyObject **, int, int); static PyObject * ext_do_call(PyObject *, PyObject ***, int, int, int); static PyObject * update_keyword_args(PyObject *, int, PyObject ***, PyObject *); @@ -4750,10 +4749,10 @@ call_function(PyObject ***pp_stack, int Py_INCREF(func); READ_TIMESTAMP(*pintr0); if (PyFunction_Check(func)) { - x = fast_function(func, (*pp_stack) - n, n, na, nk); + x = PyFunction_FastCallKeywords(func, (*pp_stack) - n, na, nk); } else { - x = do_call(func, pp_stack, na, nk); + x = do_call(func, (*pp_stack) - n, na, nk); } READ_TIMESTAMP(*pintr1); Py_DECREF(func); @@ -4763,7 +4762,7 @@ call_function(PyObject ***pp_stack, int /* Clear the stack of the function object. Also removes the arguments in case they weren't consumed already - (fast_function() and err_args() leave them on the stack). + (PyFunction_FastCallKeywords() and err_args() leave them on the stack). */ while ((*pp_stack) > pfunc) { w = EXT_POP(*pp_stack); @@ -4775,7 +4774,7 @@ call_function(PyObject ***pp_stack, int return x; } -/* The fast_function() function optimize calls for which no argument +/* The PyFunction_FastCallKeywords() function optimize calls for which no argument tuple is necessary; the objects are passed directly from the stack. For the simplest case -- a function that takes only positional arguments and is called with only positional arguments -- it @@ -4823,8 +4822,9 @@ static PyObject* /* Similar to _PyFunction_FastCall() but keywords are passed a (key, value) pairs in stack */ -static PyObject * -fast_function(PyObject *func, PyObject **stack, int n, int nargs, int nk) +PyObject * +PyFunction_FastCallKeywords(PyObject *func, PyObject **stack, + int nargs, int nkwargs) { PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); PyObject *globals = PyFunction_GET_GLOBALS(func); @@ -4833,10 +4833,12 @@ fast_function(PyObject *func, PyObject * PyObject **d; int nd; + assert((nargs == 0 && nkwargs == 0) || stack != NULL); + PCALL(PCALL_FUNCTION); PCALL(PCALL_FAST_FUNCTION); - if (co->co_kwonlyargcount == 0 && nk == 0 && + if (co->co_kwonlyargcount == 0 && nkwargs == 0 && co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { if (argdefs == NULL && co->co_argcount == nargs) { @@ -4867,7 +4869,7 @@ fast_function(PyObject *func, PyObject * } return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, stack, nargs, - stack + nargs, nk, + stack + nargs, nkwargs, d, nd, kwdefs, closure, name, qualname); } @@ -5052,20 +5054,10 @@ load_args(PyObject ***pp_stack, int na) } static PyObject * -do_call(PyObject *func, PyObject ***pp_stack, int na, int nk) +do_call(PyObject *func, PyObject **stack, int na, int nk) { - PyObject *callargs = NULL; - PyObject *kwdict = NULL; - PyObject *result = NULL; - - if (nk > 0) { - kwdict = update_keyword_args(NULL, nk, pp_stack, func); - if (kwdict == NULL) - goto call_fail; - } - callargs = load_args(pp_stack, na); - if (callargs == NULL) - goto call_fail; + PyObject *result; + #ifdef CALL_PROFILE /* At this point, we have to look at the type of func to update the call stats properly. Do it here so as to avoid @@ -5082,15 +5074,14 @@ do_call(PyObject *func, PyObject ***pp_s else PCALL(PCALL_OTHER); #endif + if (PyCFunction_Check(func)) { PyThreadState *tstate = PyThreadState_GET(); - C_TRACE(result, PyCFunction_Call(func, callargs, kwdict)); + C_TRACE(result, _PyCFunction_FastCallKeywords(func, stack, na, nk)); } - else - result = PyObject_Call(func, callargs, kwdict); -call_fail: - Py_XDECREF(callargs); - Py_XDECREF(kwdict); + else { + result = _PyObject_FastCallKeywords(func, stack, na, nk); + } return result; }