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
This commit is contained in:
Victor Stinner 2016-09-09 17:40:38 -07:00
parent a9efb2f56e
commit f0ccbbbc57
3 changed files with 179 additions and 28 deletions

View File

@ -58,10 +58,13 @@ typedef struct _PyArg_Parser {
} _PyArg_Parser; } _PyArg_Parser;
#ifdef PY_SSIZE_T_CLEAN #ifdef PY_SSIZE_T_CLEAN
#define _PyArg_ParseTupleAndKeywordsFast _PyArg_ParseTupleAndKeywordsFast_SizeT #define _PyArg_ParseTupleAndKeywordsFast _PyArg_ParseTupleAndKeywordsFast_SizeT
#define _PyArg_ParseStack _PyArg_ParseStack_SizeT
#define _PyArg_VaParseTupleAndKeywordsFast _PyArg_VaParseTupleAndKeywordsFast_SizeT #define _PyArg_VaParseTupleAndKeywordsFast _PyArg_VaParseTupleAndKeywordsFast_SizeT
#endif #endif
PyAPI_FUNC(int) _PyArg_ParseTupleAndKeywordsFast(PyObject *, PyObject *, PyAPI_FUNC(int) _PyArg_ParseTupleAndKeywordsFast(PyObject *, PyObject *,
struct _PyArg_Parser *, ...); 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 *, PyAPI_FUNC(int) _PyArg_VaParseTupleAndKeywordsFast(PyObject *, PyObject *,
struct _PyArg_Parser *, va_list); struct _PyArg_Parser *, va_list);
void _PyArg_Fini(void); void _PyArg_Fini(void);

View File

@ -79,6 +79,10 @@ static int vgetargskeywords(PyObject *, PyObject *,
const char *, char **, va_list *, int); const char *, char **, va_list *, int);
static int vgetargskeywordsfast(PyObject *, PyObject *, static int vgetargskeywordsfast(PyObject *, PyObject *,
struct _PyArg_Parser *, va_list *, int); 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); static const char *skipitem(const char **, va_list *, int);
int int
@ -1469,6 +1473,46 @@ _PyArg_ParseTupleAndKeywordsFast_SizeT(PyObject *args, PyObject *keywords,
return retval; 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 int
_PyArg_VaParseTupleAndKeywordsFast(PyObject *args, PyObject *keywords, _PyArg_VaParseTupleAndKeywordsFast(PyObject *args, PyObject *keywords,
@ -1899,10 +1943,37 @@ parser_clear(struct _PyArg_Parser *parser)
Py_CLEAR(parser->kwtuple); 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 static int
vgetargskeywordsfast(PyObject *args, PyObject *keywords, vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs,
struct _PyArg_Parser *parser, PyObject *keywords, PyObject *kwnames,
va_list *p_va, int flags) struct _PyArg_Parser *parser,
va_list *p_va, int flags)
{ {
PyObject *kwtuple; PyObject *kwtuple;
char msgbuf[512]; char msgbuf[512];
@ -1911,17 +1982,20 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
const char *msg; const char *msg;
PyObject *keyword; PyObject *keyword;
int i, pos, len; int i, pos, len;
Py_ssize_t nargs, nkeywords; Py_ssize_t nkeywords;
PyObject *current_arg; PyObject *current_arg;
freelistentry_t static_entries[STATIC_FREELIST_ENTRIES]; freelistentry_t static_entries[STATIC_FREELIST_ENTRIES];
freelist_t freelist; freelist_t freelist;
PyObject **kwstack = NULL;
freelist.entries = static_entries; freelist.entries = static_entries;
freelist.first_available = 0; freelist.first_available = 0;
freelist.entries_malloced = 0; freelist.entries_malloced = 0;
assert(args != NULL && PyTuple_Check(args));
assert(keywords == NULL || PyDict_Check(keywords)); 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(parser != NULL);
assert(p_va != NULL); assert(p_va != NULL);
@ -1942,8 +2016,16 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
freelist.entries_malloced = 1; freelist.entries_malloced = 1;
} }
nargs = PyTuple_GET_SIZE(args); if (keywords != NULL) {
nkeywords = (keywords == NULL) ? 0 : PyDict_Size(keywords); nkeywords = PyDict_Size(keywords);
}
else if (kwnames != NULL) {
nkeywords = PyTuple_GET_SIZE(kwnames);
kwstack = args + nargs;
}
else {
nkeywords = 0;
}
if (nargs + nkeywords > len) { if (nargs + nkeywords > len) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"%s%s takes at most %d argument%s (%zd given)", "%s%s takes at most %d argument%s (%zd given)",
@ -1976,9 +2058,14 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
current_arg = NULL; current_arg = NULL;
if (nkeywords && i >= pos) { if (nkeywords && i >= pos) {
current_arg = PyDict_GetItem(keywords, keyword); if (keywords != NULL) {
if (!current_arg && PyErr_Occurred()) { current_arg = PyDict_GetItem(keywords, keyword);
return cleanreturn(0, &freelist); if (!current_arg && PyErr_Occurred()) {
return cleanreturn(0, &freelist);
}
}
else {
current_arg = find_keyword(kwnames, kwstack, keyword);
} }
} }
if (current_arg) { if (current_arg) {
@ -1993,7 +2080,7 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
} }
} }
else if (i < nargs) { else if (i < nargs) {
current_arg = PyTuple_GET_ITEM(args, i); current_arg = args[i];
} }
if (current_arg) { if (current_arg) {
@ -2040,24 +2127,52 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
/* make sure there are no extraneous keyword arguments */ /* make sure there are no extraneous keyword arguments */
if (nkeywords > 0) { if (nkeywords > 0) {
PyObject *key, *value; if (keywords != NULL) {
Py_ssize_t pos = 0; PyObject *key, *value;
while (PyDict_Next(keywords, &pos, &key, &value)) { Py_ssize_t pos = 0;
int match; while (PyDict_Next(keywords, &pos, &key, &value)) {
if (!PyUnicode_Check(key)) { int match;
PyErr_SetString(PyExc_TypeError, if (!PyUnicode_Check(key)) {
"keywords must be strings"); PyErr_SetString(PyExc_TypeError,
return cleanreturn(0, &freelist); "keywords must be strings");
} return cleanreturn(0, &freelist);
match = PySequence_Contains(kwtuple, key); }
if (match <= 0) { match = PySequence_Contains(kwtuple, key);
if (!match) { if (match <= 0) {
PyErr_Format(PyExc_TypeError, if (!match) {
"'%U' is an invalid keyword " PyErr_Format(PyExc_TypeError,
"argument for this function", "'%U' is an invalid keyword "
key); "argument for this function",
key);
}
return cleanreturn(0, &freelist);
}
}
}
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); 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 * static const char *
skipitem(const char **p_format, va_list *p_va, int flags) skipitem(const char **p_format, va_list *p_va, int flags)

View File

@ -705,6 +705,11 @@ class CLanguage(Language):
{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs) {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(""" parser_prototype_varargs = normalize_snippet("""
static PyObject * static PyObject *
{c_basename}({self_type}{self_name}, PyObject *args) {c_basename}({self_type}{self_name}, PyObject *args)
@ -845,6 +850,19 @@ class CLanguage(Language):
}} }}
""", indent=4)) """, 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: else:
# positional-or-keyword arguments # positional-or-keyword arguments
flags = "METH_VARARGS|METH_KEYWORDS" flags = "METH_VARARGS|METH_KEYWORDS"