[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