[New-bugs-announce] [issue43447] Generate vectorcall code to parse arguments using Argument Clinic
STINNER Victor
report at bugs.python.org
Tue Mar 9 06:34:24 EST 2021
New submission from STINNER Victor <vstinner at python.org>:
To optimize the creation of objects, a lot of "tp_new" methods are defined twice: once in the legacy way (tp_new slot), once with the new VECTORCALL calling convention (tp_vectorcall slot). My concern is that the VECTORCALL implementation copy/paste most of the code just to parse arguments, whereas the specific code is just a few lines.
Example with the float type constructor:
--------------
static PyObject *
float_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
PyObject *return_value = NULL;
PyObject *x = NULL;
if ((type == &PyFloat_Type) &&
!_PyArg_NoKeywords("float", kwargs)) {
goto exit;
}
if (!_PyArg_CheckPositional("float", PyTuple_GET_SIZE(args), 0, 1)) {
goto exit;
}
if (PyTuple_GET_SIZE(args) < 1) {
goto skip_optional;
}
x = PyTuple_GET_ITEM(args, 0);
skip_optional:
return_value = float_new_impl(type, x);
exit:
return return_value;
}
/*[clinic input]
@classmethod
float.__new__ as float_new
x: object(c_default="NULL") = 0
/
Convert a string or number to a floating point number, if possible.
[clinic start generated code]*/
static PyObject *
float_new_impl(PyTypeObject *type, PyObject *x)
/*[clinic end generated code: output=ccf1e8dc460ba6ba input=f43661b7de03e9d8]*/
{
if (type != &PyFloat_Type) {
if (x == NULL) {
x = _PyLong_GetZero();
}
return float_subtype_new(type, x); /* Wimp out */
}
if (x == NULL) {
return PyFloat_FromDouble(0.0);
}
/* If it's a string, but not a string subclass, use
PyFloat_FromString. */
if (PyUnicode_CheckExact(x))
return PyFloat_FromString(x);
return PyNumber_Float(x);
}
static PyObject *
float_vectorcall(PyObject *type, PyObject * const*args,
size_t nargsf, PyObject *kwnames)
{
if (!_PyArg_NoKwnames("float", kwnames)) {
return NULL;
}
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
if (!_PyArg_CheckPositional("float", nargs, 0, 1)) {
return NULL;
}
PyObject *x = nargs >= 1 ? args[0] : NULL;
return float_new_impl((PyTypeObject *)type, x);
}
--------------
Here the float_new() function (tp_new slot) is implemented with Argument Clinic: float_new() C code is generated from the [clinic input] DSL: good!
My concern is that float_vectorcall() code is hand written, it's boring to write and boring to maintain.
Would it be possible to add a new [clinic input] DSL for vectorcall? I expect something like that:
--------------
static PyObject *
float_vectorcall(PyObject *type, PyObject * const*args,
size_t nargsf, PyObject *kwnames)
{
if (!_PyArg_NoKwnames("float", kwnames)) {
return NULL;
}
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
if (!_PyArg_CheckPositional("float", nargs, 0, 1)) {
return NULL;
}
PyObject *x = nargs >= 1 ? args[0] : NULL;
return float_vectorcall_impl(type, x);
}
static PyObject *
float_vectorcall_impl(PyObject *type, PyObject *x)
{
return float_new_impl((PyTypeObject *)type, x);
}
--------------
where float_vectorcall() C code would be generated, and float_vectorcall_impl() would be the only part written manually. float_vectorcall_impl() gets a clean API and its body is way simpler to write and to maintain!
----------
components: C API
messages: 388354
nosy: corona10, serhiy.storchaka, vstinner
priority: normal
severity: normal
status: open
title: Generate vectorcall code to parse arguments using Argument Clinic
versions: Python 3.10
_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue43447>
_______________________________________
More information about the New-bugs-announce
mailing list