[Python-checkins] cpython: Add METH_FASTCALL calling convention

victor.stinner python-checkins at python.org
Fri Sep 9 23:22:39 EDT 2016


https://hg.python.org/cpython/rev/86b0f5a900c7
changeset:   103541:86b0f5a900c7
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Fri Sep 09 17:40:22 2016 -0700
summary:
  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.

files:
  Include/abstract.h     |  16 ++++++++
  Include/methodobject.h |   4 ++
  Objects/abstract.c     |  56 ++++++++++++++++++++++++++++++
  Objects/methodobject.c |  24 ++++++++++++
  Python/getargs.c       |   3 +-
  5 files changed, 102 insertions(+), 1 deletions(-)


diff --git a/Include/abstract.h b/Include/abstract.h
--- a/Include/abstract.h
+++ b/Include/abstract.h
@@ -277,6 +277,22 @@
         PyObject *kwnames,
         PyObject *func);
 
+    /* Convert (args, nargs, kwargs) into a (stack, nargs, kwnames).
+
+       Return a new stack which should be released by PyMem_Free(), or return
+       args unchanged if kwargs is NULL or an empty dictionary.
+
+       The stack uses borrowed references.
+
+       The type of keyword keys is not checked, these checks should be done
+       later (ex: _PyArg_ParseStack). */
+    PyAPI_FUNC(PyObject **) _PyStack_UnpackDict(
+        PyObject **args,
+        Py_ssize_t nargs,
+        PyObject *kwargs,
+        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
@@ -16,6 +16,8 @@
 #define PyCFunction_Check(op) (Py_TYPE(op) == &PyCFunction_Type)
 
 typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
+typedef PyObject *(*_PyCFunctionFast) (PyObject *self, PyObject **args,
+                                       Py_ssize_t nargs, PyObject *kwnames);
 typedef PyObject *(*PyCFunctionWithKeywords)(PyObject *, PyObject *,
                                              PyObject *);
 typedef PyObject *(*PyNoArgsFunction)(PyObject *);
@@ -83,6 +85,8 @@
 
 #define METH_COEXIST   0x0040
 
+#define METH_FASTCALL  0x0080
+
 #ifndef Py_LIMITED_API
 typedef struct {
     PyObject_HEAD
diff --git a/Objects/abstract.c b/Objects/abstract.c
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -2403,6 +2403,62 @@
     return kwdict;
 }
 
+PyObject **
+_PyStack_UnpackDict(PyObject **args, Py_ssize_t nargs, PyObject *kwargs,
+                    PyObject **p_kwnames, PyObject *func)
+{
+    PyObject **stack, **kwstack;
+    Py_ssize_t nkwargs;
+    Py_ssize_t pos, i;
+    PyObject *key, *value;
+    PyObject *kwnames;
+
+    assert(nargs >= 0);
+    assert(kwargs == NULL || PyDict_CheckExact(kwargs));
+
+    nkwargs = (kwargs != NULL) ? PyDict_Size(kwargs) : 0;
+    if (!nkwargs) {
+        *p_kwnames = NULL;
+        return args;
+    }
+
+    if ((size_t)nargs > PY_SSIZE_T_MAX / sizeof(stack[0]) - (size_t)nkwargs) {
+        PyErr_NoMemory();
+        return NULL;
+    }
+
+    stack = PyMem_Malloc((nargs + nkwargs) * sizeof(stack[0]));
+    if (stack == NULL) {
+        PyErr_NoMemory();
+        return NULL;
+    }
+
+    kwnames = PyTuple_New(nkwargs);
+    if (kwnames == NULL) {
+        PyMem_Free(stack);
+        return NULL;
+    }
+
+    /* Copy position arguments (borrowed references) */
+    Py_MEMCPY(stack, args, nargs * sizeof(stack[0]));
+
+    kwstack = stack + nargs;
+    pos = i = 0;
+    /* This loop doesn't support lookup function mutating the dictionary
+       to change its size. It's a deliberate choice for speed, this function is
+       called in the performance critical hot code. */
+    while (PyDict_Next(kwargs, &pos, &key, &value)) {
+        Py_INCREF(key);
+        PyTuple_SET_ITEM(kwnames, i, key);
+        /* The stack contains borrowed references */
+        kwstack[i] = value;
+        i++;
+    }
+
+    *p_kwnames = kwnames;
+    return stack;
+}
+
 PyObject *
 _PyObject_FastCallKeywords(PyObject *func, PyObject **stack, Py_ssize_t nargs,
                            PyObject *kwnames)
diff --git a/Objects/methodobject.c b/Objects/methodobject.c
--- a/Objects/methodobject.c
+++ b/Objects/methodobject.c
@@ -97,6 +97,11 @@
     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 @@
         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. "
diff --git a/Python/getargs.c b/Python/getargs.c
--- a/Python/getargs.c
+++ b/Python/getargs.c
@@ -1992,8 +1992,9 @@
                 return cleanreturn(0, &freelist);
             }
         }
-        else if (i < nargs)
+        else if (i < nargs) {
             current_arg = PyTuple_GET_ITEM(args, i);
+        }
 
         if (current_arg) {
             msg = convertitem(current_arg, &format, p_va, flags,

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


More information about the Python-checkins mailing list