X Tutup
Skip to content

Commit f0ccbbb

Browse files
committed
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
1 parent a9efb2f commit f0ccbbb

File tree

3 files changed

+178
-27
lines changed

3 files changed

+178
-27
lines changed

Include/modsupport.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,13 @@ typedef struct _PyArg_Parser {
5858
} _PyArg_Parser;
5959
#ifdef PY_SSIZE_T_CLEAN
6060
#define _PyArg_ParseTupleAndKeywordsFast _PyArg_ParseTupleAndKeywordsFast_SizeT
61+
#define _PyArg_ParseStack _PyArg_ParseStack_SizeT
6162
#define _PyArg_VaParseTupleAndKeywordsFast _PyArg_VaParseTupleAndKeywordsFast_SizeT
6263
#endif
6364
PyAPI_FUNC(int) _PyArg_ParseTupleAndKeywordsFast(PyObject *, PyObject *,
6465
struct _PyArg_Parser *, ...);
66+
PyAPI_FUNC(int) _PyArg_ParseStack(PyObject **args, Py_ssize_t nargs, PyObject *kwnames,
67+
struct _PyArg_Parser *, ...);
6568
PyAPI_FUNC(int) _PyArg_VaParseTupleAndKeywordsFast(PyObject *, PyObject *,
6669
struct _PyArg_Parser *, va_list);
6770
void _PyArg_Fini(void);

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
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,11 @@ def output_templates(self, f):
705705
{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs)
706706
""")
707707

708+
parser_prototype_fastcall = normalize_snippet("""
709+
static PyObject *
710+
{c_basename}({self_type}{self_name}, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
711+
""")
712+
708713
parser_prototype_varargs = normalize_snippet("""
709714
static PyObject *
710715
{c_basename}({self_type}{self_name}, PyObject *args)
@@ -845,6 +850,19 @@ def insert_keywords(s):
845850
}}
846851
""", indent=4))
847852

853+
elif not new_or_init:
854+
flags = "METH_FASTCALL"
855+
856+
parser_prototype = parser_prototype_fastcall
857+
858+
body = normalize_snippet("""
859+
if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser,
860+
{parse_arguments})) {{
861+
goto exit;
862+
}}
863+
""", indent=4)
864+
parser_definition = parser_body(parser_prototype, body)
865+
parser_definition = insert_keywords(parser_definition)
848866
else:
849867
# positional-or-keyword arguments
850868
flags = "METH_VARARGS|METH_KEYWORDS"

0 commit comments

Comments
 (0)
X Tutup