diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2016-09-10 00:40:38 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2016-09-10 00:40:38 (GMT) |
commit | f0ccbbbc577f686ae5b4ab28a8e61bb524433d2d (patch) | |
tree | 1454cb02eef602371ce84bdf3c88621375a46df9 | |
parent | a9efb2f56eb6bcb97cebfadf1e778b4cb979357f (diff) | |
download | cpython-f0ccbbbc577f686ae5b4ab28a8e61bb524433d2d.zip cpython-f0ccbbbc577f686ae5b4ab28a8e61bb524433d2d.tar.gz cpython-f0ccbbbc577f686ae5b4ab28a8e61bb524433d2d.tar.bz2 |
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
-rw-r--r-- | Include/modsupport.h | 3 | ||||
-rw-r--r-- | Python/getargs.c | 184 | ||||
-rwxr-xr-x | Tools/clinic/clinic.py | 18 |
3 files changed, 178 insertions, 27 deletions
diff --git a/Include/modsupport.h b/Include/modsupport.h index 6b0acb8..99581a3 100644 --- a/Include/modsupport.h +++ b/Include/modsupport.h @@ -58,10 +58,13 @@ typedef struct _PyArg_Parser { } _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 index 5e85ea4..017098e 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -79,6 +79,10 @@ static int vgetargskeywords(PyObject *, PyObject *, 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 @@ _PyArg_ParseTupleAndKeywordsFast_SizeT(PyObject *args, PyObject *keywords, 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 @@ parser_clear(struct _PyArg_Parser *parser) 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 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords, 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 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords, 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 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords, 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 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords, } } else if (i < nargs) { - current_arg = PyTuple_GET_ITEM(args, i); + current_arg = args[i]; } if (current_arg) { @@ -2040,24 +2127,52 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords, /* 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); + } + + 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); } - return cleanreturn(0, &freelist); } } } @@ -2065,6 +2180,21 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords, 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 index afd1a5f..75ac673 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -705,6 +705,11 @@ class CLanguage(Language): {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 @@ class CLanguage(Language): }} """, 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" |