Emit METH_FASTCALL code in Argument Clinic · pythoncapi/cpython@f0ccbbb · GitHub
Skip to content

Commit f0ccbbb

Browse files
committed
Emit METH_FASTCALL code in Argument Clinic
Issue python#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
1 parent a9efb2f commit f0ccbbb

3 files changed

Lines changed: 178 additions & 27 deletions

File tree

Include/modsupport.h

Lines changed: 3 additions & 0 deletions

Python/getargs.c

Lines changed: 157 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ static int vgetargskeywords(PyObject *, PyObject *,
7979
const char *, char **, va_list *, int);
8080
static int vgetargskeywordsfast(PyObject *, PyObject *,
8181
struct _PyArg_Parser *, va_list *, int);
82+
static int vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs,
83+
PyObject *keywords, PyObject *kwnames,
84+
struct _PyArg_Parser *parser,
85+
va_list *p_va, int flags);
8286
static const char *skipitem(const char **, va_list *, int);
8387

8488
int
@@ -1469,6 +1473,46 @@ _PyArg_ParseTupleAndKeywordsFast_SizeT(PyObject *args, PyObject *keywords,
14691473
return retval;
14701474
}
14711475

1476+
int
1477+
_PyArg_ParseStack(PyObject **args, Py_ssize_t nargs, PyObject *kwnames,
1478+
struct _PyArg_Parser *parser, ...)
1479+
{
1480+
int retval;
1481+
va_list va;
1482+
1483+
if ((kwnames != NULL && !PyTuple_Check(kwnames)) ||
1484+
parser == NULL)
1485+
{
1486+
PyErr_BadInternalCall();
1487+
return 0;
1488+
}
1489+
1490+
va_start(va, parser);
1491+
retval = vgetargskeywordsfast_impl(args, nargs, NULL, kwnames, parser, &va, 0);
1492+
va_end(va);
1493+
return retval;
1494+
}
1495+
1496+
int
1497+
_PyArg_ParseStack_SizeT(PyObject **args, Py_ssize_t nargs, PyObject *kwnames,
1498+
struct _PyArg_Parser *parser, ...)
1499+
{
1500+
int retval;
1501+
va_list va;
1502+
1503+
if ((kwnames != NULL && !PyTuple_Check(kwnames)) ||
1504+
parser == NULL)
1505+
{
1506+
PyErr_BadInternalCall();
1507+
return 0;
1508+
}
1509+
1510+
va_start(va, parser);
1511+
retval = vgetargskeywordsfast_impl(args, nargs, NULL, kwnames, parser, &va, FLAG_SIZE_T);
1512+
va_end(va);
1513+
return retval;
1514+
}
1515+
14721516

14731517
int
14741518
_PyArg_VaParseTupleAndKeywordsFast(PyObject *args, PyObject *keywords,
@@ -1899,10 +1943,37 @@ parser_clear(struct _PyArg_Parser *parser)
18991943
Py_CLEAR(parser->kwtuple);
19001944
}
19011945

1946+
static PyObject*
1947+
find_keyword(PyObject *kwnames, PyObject **kwstack, PyObject *key)
1948+
{
1949+
Py_ssize_t i, nkwargs;
1950+
1951+
nkwargs = PyTuple_GET_SIZE(kwnames);
1952+
for (i=0; i < nkwargs; i++) {
1953+
PyObject *kwname = PyTuple_GET_ITEM(kwnames, i);
1954+
1955+
/* ptr==ptr should match in most cases since keyword keys
1956+
should be interned strings */
1957+
if (kwname == key) {
1958+
return kwstack[i];
1959+
}
1960+
if (!PyUnicode_Check(kwname)) {
1961+
/* ignore non-string keyword keys:
1962+
an error will be raised above */
1963+
continue;
1964+
}
1965+
if (_PyUnicode_EQ(kwname, key)) {
1966+
return kwstack[i];
1967+
}
1968+
}
1969+
return NULL;
1970+
}
1971+
19021972
static int
1903-
vgetargskeywordsfast(PyObject *args, PyObject *keywords,
1904-
struct _PyArg_Parser *parser,
1905-
va_list *p_va, int flags)
1973+
vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs,
1974+
PyObject *keywords, PyObject *kwnames,
1975+
struct _PyArg_Parser *parser,
1976+
va_list *p_va, int flags)
19061977
{
19071978
PyObject *kwtuple;
19081979
char msgbuf[512];
@@ -1911,17 +1982,20 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
19111982
const char *msg;
19121983
PyObject *keyword;
19131984
int i, pos, len;
1914-
Py_ssize_t nargs, nkeywords;
1985+
Py_ssize_t nkeywords;
19151986
PyObject *current_arg;
19161987
freelistentry_t static_entries[STATIC_FREELIST_ENTRIES];
19171988
freelist_t freelist;
1989+
PyObject **kwstack = NULL;
19181990

19191991
freelist.entries = static_entries;
19201992
freelist.first_available = 0;
19211993
freelist.entries_malloced = 0;
19221994

1923-
assert(args != NULL && PyTuple_Check(args));
19241995
assert(keywords == NULL || PyDict_Check(keywords));
1996+
assert(kwnames == NULL || PyTuple_Check(kwnames));
1997+
assert((keywords != NULL || kwnames != NULL)
1998+
|| (keywords == NULL && kwnames == NULL));
19251999
assert(parser != NULL);
19262000
assert(p_va != NULL);
19272001

@@ -1942,8 +2016,16 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
19422016
freelist.entries_malloced = 1;
19432017
}
19442018

1945-
nargs = PyTuple_GET_SIZE(args);
1946-
nkeywords = (keywords == NULL) ? 0 : PyDict_Size(keywords);
2019+
if (keywords != NULL) {
2020+
nkeywords = PyDict_Size(keywords);
2021+
}
2022+
else if (kwnames != NULL) {
2023+
nkeywords = PyTuple_GET_SIZE(kwnames);
2024+
kwstack = args + nargs;
2025+
}
2026+
else {
2027+
nkeywords = 0;
2028+
}
19472029
if (nargs + nkeywords > len) {
19482030
PyErr_Format(PyExc_TypeError,
19492031
"%s%s takes at most %d argument%s (%zd given)",
@@ -1976,9 +2058,14 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
19762058

19772059
current_arg = NULL;
19782060
if (nkeywords && i >= pos) {
1979-
current_arg = PyDict_GetItem(keywords, keyword);
1980-
if (!current_arg && PyErr_Occurred()) {
1981-
return cleanreturn(0, &freelist);
2061+
if (keywords != NULL) {
2062+
current_arg = PyDict_GetItem(keywords, keyword);
2063+
if (!current_arg && PyErr_Occurred()) {
2064+
return cleanreturn(0, &freelist);
2065+
}
2066+
}
2067+
else {
2068+
current_arg = find_keyword(kwnames, kwstack, keyword);
19822069
}
19832070
}
19842071
if (current_arg) {
@@ -1993,7 +2080,7 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
19932080
}
19942081
}
19952082
else if (i < nargs) {
1996-
current_arg = PyTuple_GET_ITEM(args, i);
2083+
current_arg = args[i];
19972084
}
19982085

19992086
if (current_arg) {
@@ -2040,31 +2127,74 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
20402127

20412128
/* make sure there are no extraneous keyword arguments */
20422129
if (nkeywords > 0) {
2043-
PyObject *key, *value;
2044-
Py_ssize_t pos = 0;
2045-
while (PyDict_Next(keywords, &pos, &key, &value)) {
2046-
int match;
2047-
if (!PyUnicode_Check(key)) {
2048-
PyErr_SetString(PyExc_TypeError,
2049-
"keywords must be strings");
2050-
return cleanreturn(0, &freelist);
2130+
if (keywords != NULL) {
2131+
PyObject *key, *value;
2132+
Py_ssize_t pos = 0;
2133+
while (PyDict_Next(keywords, &pos, &key, &value)) {
2134+
int match;
2135+
if (!PyUnicode_Check(key)) {
2136+
PyErr_SetString(PyExc_TypeError,
2137+
"keywords must be strings");
2138+
return cleanreturn(0, &freelist);
2139+
}
2140+
match = PySequence_Contains(kwtuple, key);
2141+
if (match <= 0) {
2142+
if (!match) {
2143+
PyErr_Format(PyExc_TypeError,
2144+
"'%U' is an invalid keyword "
2145+
"argument for this function",
2146+
key);
2147+
}
2148+
return cleanreturn(0, &freelist);
2149+
}
20512150
}
2052-
match = PySequence_Contains(kwtuple, key);
2053-
if (match <= 0) {
2054-
if (!match) {
2055-
PyErr_Format(PyExc_TypeError,
2056-
"'%U' is an invalid keyword "
2057-
"argument for this function",
2058-
key);
2151+
}
2152+
else {
2153+
Py_ssize_t j, nkwargs;
2154+
2155+
nkwargs = PyTuple_GET_SIZE(kwnames);
2156+
for (j=0; j < nkwargs; j++) {
2157+
PyObject *key = PyTuple_GET_ITEM(kwnames, j);
2158+
int match;
2159+
2160+
if (!PyUnicode_Check(key)) {
2161+
PyErr_SetString(PyExc_TypeError,
2162+
"keywords must be strings");
2163+
return cleanreturn(0, &freelist);
2164+
}
2165+
2166+
match = PySequence_Contains(kwtuple, key);
2167+
if (match <= 0) {
2168+
if (!match) {
2169+
PyErr_Format(PyExc_TypeError,
2170+
"'%U' is an invalid keyword "
2171+
"argument for this function",
2172+
key);
2173+
}
2174+
return cleanreturn(0, &freelist);
20592175
}
2060-
return cleanreturn(0, &freelist);
20612176
}
20622177
}
20632178
}
20642179

20652180
return cleanreturn(1, &freelist);
20662181
}
20672182

2183+
static int
2184+
vgetargskeywordsfast(PyObject *args, PyObject *keywords,
2185+
struct _PyArg_Parser *parser, va_list *p_va, int flags)
2186+
{
2187+
PyObject **stack;
2188+
Py_ssize_t nargs;
2189+
2190+
assert(args != NULL && PyTuple_Check(args));
2191+
2192+
stack = &PyTuple_GET_ITEM(args, 0);
2193+
nargs = PyTuple_GET_SIZE(args);
2194+
return vgetargskeywordsfast_impl(stack, nargs, keywords, NULL,
2195+
parser, p_va, flags);
2196+
}
2197+
20682198

20692199
static const char *
20702200
skipitem(const char **p_format, va_list *p_va, int flags)

Tools/clinic/clinic.py

Lines changed: 18 additions & 0 deletions

0 commit comments

Comments
 (0)