[Python-checkins] cpython: Emit METH_FASTCALL code in Argument Clinic

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


https://hg.python.org/cpython/rev/633f850038c3
changeset:   103542:633f850038c3
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Fri Sep 09 17:40:38 2016 -0700
summary:
  Emit METH_FASTCALL code in Argument Clinic

Issue #27810:

* Modify vgetargskeywordsfast() to work on a C array of PyObject* rather than
  working on a tuple directly.
* Add _PyArg_ParseStack()
* Argument Clinic now emits code using the new METH_FASTCALL calling convention

files:
  Include/modsupport.h   |    3 +
  Python/getargs.c       |  184 ++++++++++++++++++++++++----
  Tools/clinic/clinic.py |   18 ++
  3 files changed, 178 insertions(+), 27 deletions(-)


diff --git a/Include/modsupport.h b/Include/modsupport.h
--- a/Include/modsupport.h
+++ b/Include/modsupport.h
@@ -58,10 +58,13 @@
 } _PyArg_Parser;
 #ifdef PY_SSIZE_T_CLEAN
 #define _PyArg_ParseTupleAndKeywordsFast  _PyArg_ParseTupleAndKeywordsFast_SizeT
+#define _PyArg_ParseStack  _PyArg_ParseStack_SizeT
 #define _PyArg_VaParseTupleAndKeywordsFast  _PyArg_VaParseTupleAndKeywordsFast_SizeT
 #endif
 PyAPI_FUNC(int) _PyArg_ParseTupleAndKeywordsFast(PyObject *, PyObject *,
                                                  struct _PyArg_Parser *, ...);
+PyAPI_FUNC(int) _PyArg_ParseStack(PyObject **args, Py_ssize_t nargs, PyObject *kwnames,
+                                  struct _PyArg_Parser *, ...);
 PyAPI_FUNC(int) _PyArg_VaParseTupleAndKeywordsFast(PyObject *, PyObject *,
                                                    struct _PyArg_Parser *, va_list);
 void _PyArg_Fini(void);
diff --git a/Python/getargs.c b/Python/getargs.c
--- a/Python/getargs.c
+++ b/Python/getargs.c
@@ -79,6 +79,10 @@
                             const char *, char **, va_list *, int);
 static int vgetargskeywordsfast(PyObject *, PyObject *,
                             struct _PyArg_Parser *, va_list *, int);
+static int vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs,
+                          PyObject *keywords, PyObject *kwnames,
+                          struct _PyArg_Parser *parser,
+                          va_list *p_va, int flags);
 static const char *skipitem(const char **, va_list *, int);
 
 int
@@ -1469,6 +1473,46 @@
     return retval;
 }
 
+int
+_PyArg_ParseStack(PyObject **args, Py_ssize_t nargs, PyObject *kwnames,
+                  struct _PyArg_Parser *parser, ...)
+{
+    int retval;
+    va_list va;
+
+    if ((kwnames != NULL && !PyTuple_Check(kwnames)) ||
+        parser == NULL)
+    {
+        PyErr_BadInternalCall();
+        return 0;
+    }
+
+    va_start(va, parser);
+    retval = vgetargskeywordsfast_impl(args, nargs, NULL, kwnames, parser, &va, 0);
+    va_end(va);
+    return retval;
+}
+
+int
+_PyArg_ParseStack_SizeT(PyObject **args, Py_ssize_t nargs, PyObject *kwnames,
+                        struct _PyArg_Parser *parser, ...)
+{
+    int retval;
+    va_list va;
+
+    if ((kwnames != NULL && !PyTuple_Check(kwnames)) ||
+        parser == NULL)
+    {
+        PyErr_BadInternalCall();
+        return 0;
+    }
+
+    va_start(va, parser);
+    retval = vgetargskeywordsfast_impl(args, nargs, NULL, kwnames, parser, &va, FLAG_SIZE_T);
+    va_end(va);
+    return retval;
+}
+
 
 int
 _PyArg_VaParseTupleAndKeywordsFast(PyObject *args, PyObject *keywords,
@@ -1899,10 +1943,37 @@
     Py_CLEAR(parser->kwtuple);
 }
 
+static PyObject*
+find_keyword(PyObject *kwnames, PyObject **kwstack, PyObject *key)
+{
+    Py_ssize_t i, nkwargs;
+
+    nkwargs = PyTuple_GET_SIZE(kwnames);
+    for (i=0; i < nkwargs; i++) {
+        PyObject *kwname = PyTuple_GET_ITEM(kwnames, i);
+
+        /* ptr==ptr should match in most cases since keyword keys
+           should be interned strings */
+        if (kwname == key) {
+            return kwstack[i];
+        }
+        if (!PyUnicode_Check(kwname)) {
+            /* ignore non-string keyword keys:
+               an error will be raised above */
+            continue;
+        }
+        if (_PyUnicode_EQ(kwname, key)) {
+            return kwstack[i];
+        }
+    }
+    return NULL;
+}
+
 static int
-vgetargskeywordsfast(PyObject *args, PyObject *keywords,
-                     struct _PyArg_Parser *parser,
-                     va_list *p_va, int flags)
+vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs,
+                          PyObject *keywords, PyObject *kwnames,
+                          struct _PyArg_Parser *parser,
+                          va_list *p_va, int flags)
 {
     PyObject *kwtuple;
     char msgbuf[512];
@@ -1911,17 +1982,20 @@
     const char *msg;
     PyObject *keyword;
     int i, pos, len;
-    Py_ssize_t nargs, nkeywords;
+    Py_ssize_t nkeywords;
     PyObject *current_arg;
     freelistentry_t static_entries[STATIC_FREELIST_ENTRIES];
     freelist_t freelist;
+    PyObject **kwstack = NULL;
 
     freelist.entries = static_entries;
     freelist.first_available = 0;
     freelist.entries_malloced = 0;
 
-    assert(args != NULL && PyTuple_Check(args));
     assert(keywords == NULL || PyDict_Check(keywords));
+    assert(kwnames == NULL || PyTuple_Check(kwnames));
+    assert((keywords != NULL || kwnames != NULL)
+           || (keywords == NULL && kwnames == NULL));
     assert(parser != NULL);
     assert(p_va != NULL);
 
@@ -1942,8 +2016,16 @@
         freelist.entries_malloced = 1;
     }
 
-    nargs = PyTuple_GET_SIZE(args);
-    nkeywords = (keywords == NULL) ? 0 : PyDict_Size(keywords);
+    if (keywords != NULL) {
+        nkeywords = PyDict_Size(keywords);
+    }
+    else if (kwnames != NULL) {
+        nkeywords = PyTuple_GET_SIZE(kwnames);
+        kwstack = args + nargs;
+    }
+    else {
+        nkeywords = 0;
+    }
     if (nargs + nkeywords > len) {
         PyErr_Format(PyExc_TypeError,
                      "%s%s takes at most %d argument%s (%zd given)",
@@ -1976,9 +2058,14 @@
 
         current_arg = NULL;
         if (nkeywords && i >= pos) {
-            current_arg = PyDict_GetItem(keywords, keyword);
-            if (!current_arg && PyErr_Occurred()) {
-                return cleanreturn(0, &freelist);
+            if (keywords != NULL) {
+                current_arg = PyDict_GetItem(keywords, keyword);
+                if (!current_arg && PyErr_Occurred()) {
+                    return cleanreturn(0, &freelist);
+                }
+            }
+            else {
+                current_arg = find_keyword(kwnames, kwstack, keyword);
             }
         }
         if (current_arg) {
@@ -1993,7 +2080,7 @@
             }
         }
         else if (i < nargs) {
-            current_arg = PyTuple_GET_ITEM(args, i);
+            current_arg = args[i];
         }
 
         if (current_arg) {
@@ -2040,24 +2127,52 @@
 
     /* make sure there are no extraneous keyword arguments */
     if (nkeywords > 0) {
-        PyObject *key, *value;
-        Py_ssize_t pos = 0;
-        while (PyDict_Next(keywords, &pos, &key, &value)) {
-            int match;
-            if (!PyUnicode_Check(key)) {
-                PyErr_SetString(PyExc_TypeError,
-                                "keywords must be strings");
-                return cleanreturn(0, &freelist);
+        if (keywords != NULL) {
+            PyObject *key, *value;
+            Py_ssize_t pos = 0;
+            while (PyDict_Next(keywords, &pos, &key, &value)) {
+                int match;
+                if (!PyUnicode_Check(key)) {
+                    PyErr_SetString(PyExc_TypeError,
+                                    "keywords must be strings");
+                    return cleanreturn(0, &freelist);
+                }
+                match = PySequence_Contains(kwtuple, key);
+                if (match <= 0) {
+                    if (!match) {
+                        PyErr_Format(PyExc_TypeError,
+                                     "'%U' is an invalid keyword "
+                                     "argument for this function",
+                                     key);
+                    }
+                    return cleanreturn(0, &freelist);
+                }
             }
-            match = PySequence_Contains(kwtuple, key);
-            if (match <= 0) {
-                if (!match) {
-                    PyErr_Format(PyExc_TypeError,
-                                 "'%U' is an invalid keyword "
-                                 "argument for this function",
-                                 key);
+        }
+        else {
+            Py_ssize_t j, nkwargs;
+
+            nkwargs = PyTuple_GET_SIZE(kwnames);
+            for (j=0; j < nkwargs; j++) {
+                PyObject *key = PyTuple_GET_ITEM(kwnames, j);
+                int match;
+
+                if (!PyUnicode_Check(key)) {
+                    PyErr_SetString(PyExc_TypeError,
+                                    "keywords must be strings");
+                    return cleanreturn(0, &freelist);
                 }
-                return cleanreturn(0, &freelist);
+
+                match = PySequence_Contains(kwtuple, key);
+                if (match <= 0) {
+                    if (!match) {
+                        PyErr_Format(PyExc_TypeError,
+                                     "'%U' is an invalid keyword "
+                                     "argument for this function",
+                                     key);
+                    }
+                    return cleanreturn(0, &freelist);
+                }
             }
         }
     }
@@ -2065,6 +2180,21 @@
     return cleanreturn(1, &freelist);
 }
 
+static int
+vgetargskeywordsfast(PyObject *args, PyObject *keywords,
+                     struct _PyArg_Parser *parser, va_list *p_va, int flags)
+{
+    PyObject **stack;
+    Py_ssize_t nargs;
+
+    assert(args != NULL && PyTuple_Check(args));
+
+    stack = &PyTuple_GET_ITEM(args, 0);
+    nargs = PyTuple_GET_SIZE(args);
+    return vgetargskeywordsfast_impl(stack, nargs, keywords, NULL,
+                                     parser, p_va, flags);
+}
+
 
 static const char *
 skipitem(const char **p_format, va_list *p_va, int flags)
diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py
--- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -705,6 +705,11 @@
             {c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs)
             """)
 
+        parser_prototype_fastcall = normalize_snippet("""
+            static PyObject *
+            {c_basename}({self_type}{self_name}, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
+            """)
+
         parser_prototype_varargs = normalize_snippet("""
             static PyObject *
             {c_basename}({self_type}{self_name}, PyObject *args)
@@ -845,6 +850,19 @@
                 }}
                 """, indent=4))
 
+        elif not new_or_init:
+            flags = "METH_FASTCALL"
+
+            parser_prototype = parser_prototype_fastcall
+
+            body = normalize_snippet("""
+                if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser,
+                    {parse_arguments})) {{
+                    goto exit;
+                }}
+                """, indent=4)
+            parser_definition = parser_body(parser_prototype, body)
+            parser_definition = insert_keywords(parser_definition)
         else:
             # positional-or-keyword arguments
             flags = "METH_VARARGS|METH_KEYWORDS"

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


More information about the Python-checkins mailing list