[Python-checkins] cpython: Issue #27810: Add _PyCFunction_FastCallKeywords()

victor.stinner python-checkins at python.org
Fri Sep 9 17:23:12 EDT 2016


https://hg.python.org/cpython/rev/a25c39873d93
changeset:   103467:a25c39873d93
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Fri Sep 09 14:07:44 2016 -0700
summary:
  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.

files:
  Include/abstract.h     |    9 +-
  Include/methodobject.h |    5 +
  Objects/abstract.c     |    7 +-
  Objects/methodobject.c |   26 ++++
  Python/ceval.c         |  170 +++++-----------------------
  5 files changed, 75 insertions(+), 142 deletions(-)


diff --git a/Include/abstract.h b/Include/abstract.h
--- a/Include/abstract.h
+++ b/Include/abstract.h
@@ -267,9 +267,16 @@
                                           PyObject *args, PyObject *kwargs);
 
 #ifndef Py_LIMITED_API
-    PyAPI_FUNC(PyObject*) _PyStack_AsTuple(PyObject **stack,
+    PyAPI_FUNC(PyObject*) _PyStack_AsTuple(
+        PyObject **stack,
         Py_ssize_t nargs);
 
+    PyAPI_FUNC(PyObject *) _PyStack_AsDict(
+        PyObject **values,
+        Py_ssize_t nkwargs,
+        PyObject *kwnames,
+        PyObject *func);
+
      /* Call the callable object func with the "fast call" calling convention:
         args is a C array for positional arguments (nargs is the number of
         positional arguments), kwargs is a dictionary for keyword arguments.
diff --git a/Include/methodobject.h b/Include/methodobject.h
--- a/Include/methodobject.h
+++ b/Include/methodobject.h
@@ -42,6 +42,11 @@
     PyObject **args,
     Py_ssize_t nargs,
     PyObject *kwargs);
+
+PyAPI_FUNC(PyObject *) _PyCFunction_FastCallKeywords(PyObject *func,
+    PyObject **stack,
+    Py_ssize_t nargs,
+    PyObject *kwnames);
 #endif
 
 struct PyMethodDef {
diff --git a/Objects/abstract.c b/Objects/abstract.c
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -2366,7 +2366,7 @@
     return result;
 }
 
-static PyObject *
+PyObject *
 _PyStack_AsDict(PyObject **values, Py_ssize_t nkwargs, PyObject *kwnames,
                 PyObject *func)
 {
@@ -2415,10 +2415,13 @@
     assert((nargs == 0 && nkwargs == 0) || stack != NULL);
 
     if (PyFunction_Check(func)) {
-        /* Fast-path: avoid temporary tuple or dict */
         return _PyFunction_FastCallKeywords(func, stack, nargs, kwnames);
     }
 
+    if (PyCFunction_Check(func)) {
+        return _PyCFunction_FastCallKeywords(func, stack, nargs, kwnames);
+    }
+
     if (nkwargs > 0) {
         kwdict = _PyStack_AsDict(stack + nargs, nkwargs, kwnames, func);
         if (kwdict == NULL) {
diff --git a/Objects/methodobject.c b/Objects/methodobject.c
--- a/Objects/methodobject.c
+++ b/Objects/methodobject.c
@@ -155,6 +155,7 @@
     PyObject *result;
     int flags;
 
+    assert(PyCFunction_Check(func));
     assert(func != NULL);
     assert(nargs >= 0);
     assert(nargs == 0 || args != NULL);
@@ -243,6 +244,31 @@
     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
diff --git a/Python/ceval.c b/Python/ceval.c
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -115,8 +115,6 @@
 #endif
 static PyObject * fast_function(PyObject *, PyObject **, Py_ssize_t, PyObject *);
 static PyObject * do_call_core(PyObject *, PyObject *, PyObject *);
-static PyObject * create_keyword_args(PyObject *, PyObject ***, PyObject *);
-static PyObject * load_args(PyObject ***, Py_ssize_t);
 
 #ifdef LLTRACE
 static int lltrace;
@@ -4892,21 +4890,6 @@
         return " object";
 }
 
-static void
-err_args(PyObject *func, int flags, Py_ssize_t nargs)
-{
-    if (flags & METH_NOARGS)
-        PyErr_Format(PyExc_TypeError,
-                     "%.200s() takes no arguments (%zd given)",
-                     ((PyCFunctionObject *)func)->m_ml->ml_name,
-                     nargs);
-    else
-        PyErr_Format(PyExc_TypeError,
-                     "%.200s() takes exactly one argument (%zd given)",
-                     ((PyCFunctionObject *)func)->m_ml->ml_name,
-                     nargs);
-}
-
 #define C_TRACE(x, call) \
 if (tstate->use_tracing && tstate->c_profilefunc) { \
     if (call_trace(tstate->c_profilefunc, tstate->c_profileobj, \
@@ -4950,91 +4933,49 @@
     PyObject *x, *w;
     Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
     Py_ssize_t nargs = oparg - nkwargs;
+    PyObject **stack;
 
     /* Always dispatch PyCFunction first, because these are
        presumed to be the most frequent callable object.
     */
     if (PyCFunction_Check(func)) {
-        int flags = PyCFunction_GET_FLAGS(func);
         PyThreadState *tstate = PyThreadState_GET();
 
         PCALL(PCALL_CFUNCTION);
-        if (kwnames == NULL && flags & (METH_NOARGS | METH_O)) {
-            PyCFunction meth = PyCFunction_GET_FUNCTION(func);
-            PyObject *self = PyCFunction_GET_SELF(func);
-            if (flags & METH_NOARGS && nargs == 0) {
-                C_TRACE(x, (*meth)(self,NULL));
-
-                x = _Py_CheckFunctionResult(func, x, NULL);
-            }
-            else if (flags & METH_O && nargs == 1) {
-                PyObject *arg = EXT_POP(*pp_stack);
-                C_TRACE(x, (*meth)(self,arg));
-                Py_DECREF(arg);
-
-                x = _Py_CheckFunctionResult(func, x, NULL);
-            }
-            else {
-                err_args(func, flags, nargs);
-                x = NULL;
-            }
-        }
-        else {
-            PyObject *callargs, *kwdict = NULL;
-            if (kwnames != NULL) {
-                kwdict = create_keyword_args(kwnames, pp_stack, func);
-                if (kwdict == NULL) {
-                    x = NULL;
-                    goto cfuncerror;
-                }
-            }
-            callargs = load_args(pp_stack, nargs);
-            if (callargs != NULL) {
-                READ_TIMESTAMP(*pintr0);
-                C_TRACE(x, PyCFunction_Call(func, callargs, kwdict));
-                READ_TIMESTAMP(*pintr1);
-                Py_DECREF(callargs);
-            }
-            else {
-                x = NULL;
-            }
-            Py_XDECREF(kwdict);
-        }
+
+        stack = (*pp_stack) - nargs - nkwargs;
+        C_TRACE(x, _PyCFunction_FastCallKeywords(func, stack, nargs, kwnames));
     }
     else {
-      Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
-      PyObject **stack;
-
-      if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) {
-          /* optimize access to bound methods */
-          PyObject *self = PyMethod_GET_SELF(func);
-          PCALL(PCALL_METHOD);
-          PCALL(PCALL_BOUND_METHOD);
-          Py_INCREF(self);
-          func = PyMethod_GET_FUNCTION(func);
-          Py_INCREF(func);
-          Py_SETREF(*pfunc, self);
-          nargs++;
-      }
-      else {
-          Py_INCREF(func);
-      }
-
-      stack = (*pp_stack) - nargs - nkwargs;
-
-      READ_TIMESTAMP(*pintr0);
-      if (PyFunction_Check(func)) {
-          x = fast_function(func, stack, nargs, kwnames);
-      }
-      else {
-          x = _PyObject_FastCallKeywords(func, stack, nargs, kwnames);
-      }
-      READ_TIMESTAMP(*pintr1);
-
-      Py_DECREF(func);
+        if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) {
+            /* optimize access to bound methods */
+            PyObject *self = PyMethod_GET_SELF(func);
+            PCALL(PCALL_METHOD);
+            PCALL(PCALL_BOUND_METHOD);
+            Py_INCREF(self);
+            func = PyMethod_GET_FUNCTION(func);
+            Py_INCREF(func);
+            Py_SETREF(*pfunc, self);
+            nargs++;
+        }
+        else {
+            Py_INCREF(func);
+        }
+
+        stack = (*pp_stack) - nargs - nkwargs;
+
+        READ_TIMESTAMP(*pintr0);
+        if (PyFunction_Check(func)) {
+            x = fast_function(func, stack, nargs, kwnames);
+        }
+        else {
+            x = _PyObject_FastCallKeywords(func, stack, nargs, kwnames);
+        }
+        READ_TIMESTAMP(*pintr1);
+
+        Py_DECREF(func);
     }
 
-cfuncerror:
     assert((x != NULL) ^ (PyErr_Occurred() != NULL));
 
     /* Clear the stack of the function object.  Also removes
@@ -5243,55 +5184,6 @@
 }
 
 static PyObject *
-create_keyword_args(PyObject *names, PyObject ***pp_stack,
-                    PyObject *func)
-{
-    Py_ssize_t nk = PyTuple_GET_SIZE(names);
-    PyObject *kwdict = _PyDict_NewPresized(nk);
-    if (kwdict == NULL)
-        return NULL;
-    while (--nk >= 0) {
-        int err;
-        PyObject *key = PyTuple_GET_ITEM(names, nk);
-        PyObject *value = EXT_POP(*pp_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(value);
-            Py_DECREF(kwdict);
-            return NULL;
-        }
-        err = PyDict_SetItem(kwdict, key, value);
-        Py_DECREF(value);
-        if (err) {
-            Py_DECREF(kwdict);
-            return NULL;
-        }
-    }
-    return kwdict;
-}
-
-static PyObject *
-load_args(PyObject ***pp_stack, Py_ssize_t nargs)
-{
-    PyObject *args = PyTuple_New(nargs);
-
-    if (args == NULL) {
-        return NULL;
-    }
-
-    while (--nargs >= 0) {
-        PyObject *arg= EXT_POP(*pp_stack);
-        PyTuple_SET_ITEM(args, nargs, arg);
-    }
-    return args;
-}
-
-static PyObject *
 do_call_core(PyObject *func, PyObject *callargs, PyObject *kwdict)
 {
 #ifdef CALL_PROFILE

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list