]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Add METH_FASTCALL calling convention
authorVictor Stinner <victor.stinner@gmail.com>
Sat, 10 Sep 2016 00:40:22 +0000 (17:40 -0700)
committerVictor Stinner <victor.stinner@gmail.com>
Sat, 10 Sep 2016 00:40:22 +0000 (17:40 -0700)
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.

Include/abstract.h
Include/methodobject.h
Objects/abstract.c
Objects/methodobject.c
Python/getargs.c

index 3f398daa00c21f1a1740bed423eaf216e5e648e2..3e630b18374bc0e8d9de33c5f2864d38c2892e7b 100644 (file)
@@ -277,6 +277,22 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
         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.
index 1f4f06cff8f865e5337df6392596e31db0aa94c5..9dba58f2c5104860aed55cb3e4dcb6846407d13d 100644 (file)
@@ -16,6 +16,8 @@ PyAPI_DATA(PyTypeObject) PyCFunction_Type;
 #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 @@ PyAPI_FUNC(PyObject *) PyCFunction_NewEx(PyMethodDef *, PyObject *,
 
 #define METH_COEXIST   0x0040
 
+#define METH_FASTCALL  0x0080
+
 #ifndef Py_LIMITED_API
 typedef struct {
     PyObject_HEAD
index 9de6b83344d011a0dd5445b88aa6245859669992..f9e5009f78a0862725d81a5260cb26c84189e176 100644 (file)
@@ -2403,6 +2403,62 @@ _PyStack_AsDict(PyObject **values, Py_ssize_t nkwargs, PyObject *kwnames,
     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)
index 0fe33154179bfb179066befc55a524895036bc7b..487ccd7a3008a285e6352415628a538bea9f735c 100644 (file)
@@ -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. "
index 0854cc45e6ad40fedfb1ea9311633cdd8af2ecc9..5e85ea4fc128c47813d0c34bd5dae2916ecb0447 100644 (file)
@@ -1992,8 +1992,9 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
                 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,