From ad5e812ca49f3430e96cadaa8d9d40508fdb67d5 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 26 Oct 2024 13:15:38 +0300 Subject: [PATCH] gh-90370: avoid temporary tuple creation for vararg in AC Current patch partially address issue, working in case when all arguments either positional-only or vararg. This also converts gcd(), lcm() and hypot() functions of the math module to the Argument Clinic. Fix issue python/cpython#101123. Objects/setobject.c and Modules/gcmodule.c adapted. This fixes slight performance regression for set methods, introduced by python/cpython#115112: | Benchmark | ref | patch | |----------------------|:------:|:--------------------:| | set().update(s1, s2) | 354 ns | 264 ns: 1.34x faster | Benchmark code: ```py import pyperf s1, s2 = {1}, {2} runner = pyperf.Runner() runner.bench_func('set().update(s1, s2)', set().update, s1, s2) ``` --- Lib/test/clinic.test.c | 48 ++++----- Lib/test/test_clinic.py | 4 +- ...4-10-27-20-31-43.gh-issue-90370.IP_W3a.rst | 2 + Modules/_testclinic.c | 43 ++++++-- Modules/clinic/_testclinic.c.h | 51 +++------ Modules/clinic/gcmodule.c.h | 36 +++---- Modules/clinic/mathmodule.c.h | 100 ++++++++++++++++- Modules/gcmodule.c | 46 ++++++-- Modules/mathmodule.c | 80 +++++++------- Objects/clinic/setobject.c.h | 102 ++++++------------ Objects/setobject.c | 56 +++++----- Tools/clinic/libclinic/clanguage.py | 14 ++- Tools/clinic/libclinic/parse_args.py | 31 +++--- 13 files changed, 360 insertions(+), 253 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-10-27-20-31-43.gh-issue-90370.IP_W3a.rst diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index 2a071f8485a2b8..66f30509688f2e 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -4148,36 +4148,32 @@ PyDoc_STRVAR(test_vararg_and_posonly__doc__, {"test_vararg_and_posonly", _PyCFunction_CAST(test_vararg_and_posonly), METH_FASTCALL, test_vararg_and_posonly__doc__}, static PyObject * -test_vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args); +test_vararg_and_posonly_impl(PyObject *module, PyObject *a, Py_ssize_t nargs, + PyObject *const *args); static PyObject * test_vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; + Py_ssize_t nvararg = Py_MAX(nargs - 1, 0); PyObject *a; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("test_vararg_and_posonly", nargs, 1, PY_SSIZE_T_MAX)) { goto exit; } a = args[0]; - __clinic_args = PyTuple_New(nargs - 1); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 1; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[1 + i])); - } - return_value = test_vararg_and_posonly_impl(module, a, __clinic_args); + __clinic_args = args + 1; + return_value = test_vararg_and_posonly_impl(module, a, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } static PyObject * -test_vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args) -/*[clinic end generated code: output=79b75dc07decc8d6 input=9cfa748bbff09877]*/ +test_vararg_and_posonly_impl(PyObject *module, PyObject *a, Py_ssize_t nargs, + PyObject *const *args) +/*[clinic end generated code: output=f0f68154d891dd6d input=9cfa748bbff09877]*/ /*[clinic input] test_vararg @@ -4931,14 +4927,14 @@ PyDoc_STRVAR(Test___init____doc__, "Varargs init method. For example, nargs is translated to PyTuple_GET_SIZE."); static int -Test___init___impl(TestObj *self, PyObject *args); +Test___init___impl(TestObj *self, Py_ssize_t nargs, PyObject *const *args); static int Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; PyTypeObject *base_tp = TestType; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args = NULL; if ((Py_IS_TYPE(self, base_tp) || Py_TYPE(self)->tp_new == base_tp->tp_new) && @@ -4948,17 +4944,16 @@ Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) if (!_PyArg_CheckPositional("Test", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_GetSlice(0, -1); - return_value = Test___init___impl((TestObj *)self, __clinic_args); + __clinic_args = _PyTuple_CAST(args)->ob_item; + return_value = Test___init___impl((TestObj *)self, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } static int -Test___init___impl(TestObj *self, PyObject *args) -/*[clinic end generated code: output=0ed1009fe0dcf98d input=2a8bd0033c9ac772]*/ +Test___init___impl(TestObj *self, Py_ssize_t nargs, PyObject *const *args) +/*[clinic end generated code: output=6a64b417c9080a73 input=2a8bd0033c9ac772]*/ /*[clinic input] @@ -4976,14 +4971,14 @@ PyDoc_STRVAR(Test__doc__, "Varargs new method. For example, nargs is translated to PyTuple_GET_SIZE."); static PyObject * -Test_impl(PyTypeObject *type, PyObject *args); +Test_impl(PyTypeObject *type, Py_ssize_t nargs, PyObject *const *args); static PyObject * Test(PyTypeObject *type, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; PyTypeObject *base_tp = TestType; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args = NULL; if ((type == base_tp || type->tp_init == base_tp->tp_init) && !_PyArg_NoKeywords("Test", kwargs)) { @@ -4992,17 +4987,16 @@ Test(PyTypeObject *type, PyObject *args, PyObject *kwargs) if (!_PyArg_CheckPositional("Test", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_GetSlice(0, -1); - return_value = Test_impl(type, __clinic_args); + __clinic_args = _PyTuple_CAST(args)->ob_item; + return_value = Test_impl(type, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } static PyObject * -Test_impl(PyTypeObject *type, PyObject *args) -/*[clinic end generated code: output=8b219f6633e2a2e9 input=70ad829df3dd9b84]*/ +Test_impl(PyTypeObject *type, Py_ssize_t nargs, PyObject *const *args) +/*[clinic end generated code: output=bf22f942407383a5 input=70ad829df3dd9b84]*/ /*[clinic input] diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 402106194f169f..d492ea1d76aa3a 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -3381,8 +3381,8 @@ def test_keyword_only_parameter(self): def test_varpos(self): # fn(*args) fn = ac_tester.varpos - self.assertEqual(fn(), ()) - self.assertEqual(fn(1, 2), (1, 2)) + self.assertEqual(fn(), ((),)) + self.assertEqual(fn(1, 2), ((1, 2),)) def test_posonly_varpos(self): # fn(a, b, /, *args) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-27-20-31-43.gh-issue-90370.IP_W3a.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-27-20-31-43.gh-issue-90370.IP_W3a.rst new file mode 100644 index 00000000000000..b6a19c06a228ca --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-27-20-31-43.gh-issue-90370.IP_W3a.rst @@ -0,0 +1,2 @@ +Avoid temporary tuple creation for vararg in argument passing with Argument +Clinic generated code (if arguments either vararg or positional-only). diff --git a/Modules/_testclinic.c b/Modules/_testclinic.c index ca884af1aa29b8..e3c8ba9b0b5074 100644 --- a/Modules/_testclinic.c +++ b/Modules/_testclinic.c @@ -58,6 +58,20 @@ pack_arguments_newref(int argc, ...) return tuple; } +static PyObject * +pack_varargs_to_tuple(Py_ssize_t varargssize, PyObject *const *args) +{ + assert(!PyErr_Occurred()); + PyObject *tuple = PyTuple_New(varargssize); + if (!tuple) { + return NULL; + } + for (Py_ssize_t i = 0; i < varargssize; i++) { + PyTuple_SET_ITEM(tuple, i, Py_NewRef(args[i])); + } + return tuple; +} + /* Pack arguments to a tuple. * `wrapper` is function which converts primitive type to PyObject. * `arg_type` is type that arguments should be converted to before wrapped. */ @@ -970,10 +984,16 @@ varpos [clinic start generated code]*/ static PyObject * -varpos_impl(PyObject *module, PyObject *args) -/*[clinic end generated code: output=7b0b9545872bdca4 input=f87cd674145d394c]*/ +varpos_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) +/*[clinic end generated code: output=b65096f423fb5dcc input=f87cd674145d394c]*/ { - return Py_NewRef(args); + PyObject *vararg_tuple = pack_varargs_to_tuple(nargs, args); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(1, vararg_tuple); + Py_DECREF(vararg_tuple); + return result; } @@ -989,10 +1009,16 @@ posonly_varpos static PyObject * posonly_varpos_impl(PyObject *module, PyObject *a, PyObject *b, - PyObject *args) -/*[clinic end generated code: output=5dae5eb2a0d623cd input=c9fd7895cfbaabba]*/ + Py_ssize_t nargs, PyObject *const *args) +/*[clinic end generated code: output=d10d43d86d117ab3 input=c9fd7895cfbaabba]*/ { - return pack_arguments_newref(3, a, b, args); + PyObject *vararg_tuple = pack_varargs_to_tuple(nargs, args); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(3, a, b, vararg_tuple); + Py_DECREF(vararg_tuple); + return result; } @@ -1157,8 +1183,9 @@ Proof-of-concept of GH-99233 refcount error bug. [clinic start generated code]*/ static PyObject * -gh_99233_refcount_impl(PyObject *module, PyObject *args) -/*[clinic end generated code: output=585855abfbca9a7f input=eecfdc2092d90dc3]*/ +gh_99233_refcount_impl(PyObject *module, Py_ssize_t nargs, + PyObject *const *args) +/*[clinic end generated code: output=b570007e61e5c670 input=eecfdc2092d90dc3]*/ { Py_RETURN_NONE; } diff --git a/Modules/clinic/_testclinic.c.h b/Modules/clinic/_testclinic.c.h index 1988c06971087d..b94d8abcb29310 100644 --- a/Modules/clinic/_testclinic.c.h +++ b/Modules/clinic/_testclinic.c.h @@ -2530,28 +2530,22 @@ PyDoc_STRVAR(varpos__doc__, {"varpos", _PyCFunction_CAST(varpos), METH_FASTCALL, varpos__doc__}, static PyObject * -varpos_impl(PyObject *module, PyObject *args); +varpos_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args); static PyObject * varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("varpos", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = varpos_impl(module, __clinic_args); + __clinic_args = args + 0; + return_value = varpos_impl(module, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -2565,32 +2559,26 @@ PyDoc_STRVAR(posonly_varpos__doc__, static PyObject * posonly_varpos_impl(PyObject *module, PyObject *a, PyObject *b, - PyObject *args); + Py_ssize_t nargs, PyObject *const *args); static PyObject * posonly_varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; + Py_ssize_t nvararg = Py_MAX(nargs - 2, 0); PyObject *a; PyObject *b; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("posonly_varpos", nargs, 2, PY_SSIZE_T_MAX)) { goto exit; } a = args[0]; b = args[1]; - __clinic_args = PyTuple_New(nargs - 2); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 2; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[2 + i])); - } - return_value = posonly_varpos_impl(module, a, b, __clinic_args); + __clinic_args = args + 2; + return_value = posonly_varpos_impl(module, a, b, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -3136,28 +3124,23 @@ PyDoc_STRVAR(gh_99233_refcount__doc__, {"gh_99233_refcount", _PyCFunction_CAST(gh_99233_refcount), METH_FASTCALL, gh_99233_refcount__doc__}, static PyObject * -gh_99233_refcount_impl(PyObject *module, PyObject *args); +gh_99233_refcount_impl(PyObject *module, Py_ssize_t nargs, + PyObject *const *args); static PyObject * gh_99233_refcount(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("gh_99233_refcount", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = gh_99233_refcount_impl(module, __clinic_args); + __clinic_args = args + 0; + return_value = gh_99233_refcount_impl(module, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -3693,4 +3676,4 @@ _testclinic_TestClass_defclass_posonly_varpos(PyObject *self, PyTypeObject *cls, Py_XDECREF(__clinic_args); return return_value; } -/*[clinic end generated code: output=76ecbb38c632bde8 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c3ed3d007eb394f1 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/gcmodule.c.h b/Modules/clinic/gcmodule.c.h index 9fff4da616ba00..9afe612bbc54f4 100644 --- a/Modules/clinic/gcmodule.c.h +++ b/Modules/clinic/gcmodule.c.h @@ -312,28 +312,23 @@ PyDoc_STRVAR(gc_get_referrers__doc__, {"get_referrers", _PyCFunction_CAST(gc_get_referrers), METH_FASTCALL, gc_get_referrers__doc__}, static PyObject * -gc_get_referrers_impl(PyObject *module, PyObject *args); +gc_get_referrers_impl(PyObject *module, Py_ssize_t nargs, + PyObject *const *args); static PyObject * gc_get_referrers(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("get_referrers", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = gc_get_referrers_impl(module, __clinic_args); + __clinic_args = args + 0; + return_value = gc_get_referrers_impl(module, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -347,28 +342,23 @@ PyDoc_STRVAR(gc_get_referents__doc__, {"get_referents", _PyCFunction_CAST(gc_get_referents), METH_FASTCALL, gc_get_referents__doc__}, static PyObject * -gc_get_referents_impl(PyObject *module, PyObject *args); +gc_get_referents_impl(PyObject *module, Py_ssize_t nargs, + PyObject *const *args); static PyObject * gc_get_referents(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("get_referents", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = gc_get_referents_impl(module, __clinic_args); + __clinic_args = args + 0; + return_value = gc_get_referents_impl(module, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -585,4 +575,4 @@ gc_get_freeze_count(PyObject *module, PyObject *Py_UNUSED(ignored)) exit: return return_value; } -/*[clinic end generated code: output=0a7e91917adcb937 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b827f188bde9435d input=a9049054013a1b77]*/ diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index 81eec310ddb21d..54bf75dbf2468e 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -8,6 +8,64 @@ preserve #endif #include "pycore_modsupport.h" // _PyArg_CheckPositional() +PyDoc_STRVAR(math_gcd__doc__, +"gcd($module, /, *integers)\n" +"--\n" +"\n" +"Greatest Common Divisor."); + +#define MATH_GCD_METHODDEF \ + {"gcd", _PyCFunction_CAST(math_gcd), METH_FASTCALL, math_gcd__doc__}, + +static PyObject * +math_gcd_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args); + +static PyObject * +math_gcd(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; + + if (!_PyArg_CheckPositional("gcd", nargs, 0, PY_SSIZE_T_MAX)) { + goto exit; + } + __clinic_args = args + 0; + return_value = math_gcd_impl(module, nvararg, __clinic_args); + +exit: + return return_value; +} + +PyDoc_STRVAR(math_lcm__doc__, +"lcm($module, /, *integers)\n" +"--\n" +"\n" +"Least Common Multiple."); + +#define MATH_LCM_METHODDEF \ + {"lcm", _PyCFunction_CAST(math_lcm), METH_FASTCALL, math_lcm__doc__}, + +static PyObject * +math_lcm_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args); + +static PyObject * +math_lcm(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; + + if (!_PyArg_CheckPositional("lcm", nargs, 0, PY_SSIZE_T_MAX)) { + goto exit; + } + __clinic_args = args + 0; + return_value = math_lcm_impl(module, nvararg, __clinic_args); + +exit: + return return_value; +} + PyDoc_STRVAR(math_ceil__doc__, "ceil($module, x, /)\n" "--\n" @@ -351,6 +409,46 @@ math_dist(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } +PyDoc_STRVAR(math_hypot__doc__, +"hypot($module, /, *coordinates)\n" +"--\n" +"\n" +"Multidimensional Euclidean distance from the origin to a point.\n" +"\n" +"Roughly equivalent to:\n" +" sqrt(sum(x**2 for x in coordinates))\n" +"\n" +"For a two dimensional point (x, y), gives the hypotenuse\n" +"using the Pythagorean theorem: sqrt(x*x + y*y).\n" +"\n" +"For example, the hypotenuse of a 3/4/5 right triangle is:\n" +"\n" +" >>> hypot(3.0, 4.0)\n" +" 5.0"); + +#define MATH_HYPOT_METHODDEF \ + {"hypot", _PyCFunction_CAST(math_hypot), METH_FASTCALL, math_hypot__doc__}, + +static PyObject * +math_hypot_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args); + +static PyObject * +math_hypot(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; + + if (!_PyArg_CheckPositional("hypot", nargs, 0, PY_SSIZE_T_MAX)) { + goto exit; + } + __clinic_args = args + 0; + return_value = math_hypot_impl(module, nvararg, __clinic_args); + +exit: + return return_value; +} + PyDoc_STRVAR(math_sumprod__doc__, "sumprod($module, p, q, /)\n" "--\n" @@ -1011,4 +1109,4 @@ math_ulp(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=755da3b1dbd9e45f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=69376c19d96a9c50 input=a9049054013a1b77]*/ diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 57e4aae9ed557e..8404a14144eed0 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -221,15 +221,29 @@ Return the list of objects that directly refer to any of 'objs'. [clinic start generated code]*/ static PyObject * -gc_get_referrers_impl(PyObject *module, PyObject *args) -/*[clinic end generated code: output=296a09587f6a86b5 input=bae96961b14a0922]*/ +gc_get_referrers_impl(PyObject *module, Py_ssize_t nargs, + PyObject *const *args) +/*[clinic end generated code: output=1d44a7695ea25c40 input=bae96961b14a0922]*/ { - if (PySys_Audit("gc.get_referrers", "(O)", args) < 0) { + PyObject *varargs = PyTuple_New(nargs); + + if (!varargs) { + return NULL; + } + for (Py_ssize_t i = 0; i < nargs; i++) { + PyTuple_SET_ITEM(varargs, i, Py_NewRef(args[i])); + } + + if (PySys_Audit("gc.get_referrers", "(O)", varargs) < 0) { + Py_DECREF(varargs); return NULL; } PyInterpreterState *interp = _PyInterpreterState_GET(); - return _PyGC_GetReferrers(interp, args); + PyObject *result = _PyGC_GetReferrers(interp, varargs); + + Py_DECREF(varargs); + return result; } /* Append obj to list; return true if error (out of memory), false if OK. */ @@ -269,27 +283,41 @@ Return the list of objects that are directly referred to by 'objs'. [clinic start generated code]*/ static PyObject * -gc_get_referents_impl(PyObject *module, PyObject *args) -/*[clinic end generated code: output=d47dc02cefd06fe8 input=b3ceab0c34038cbf]*/ +gc_get_referents_impl(PyObject *module, Py_ssize_t nargs, + PyObject *const *args) +/*[clinic end generated code: output=e459f3e8c0d19311 input=b3ceab0c34038cbf]*/ { - if (PySys_Audit("gc.get_referents", "(O)", args) < 0) { + PyObject *varargs = PyTuple_New(nargs); + + if (!varargs) { + return NULL; + } + for (Py_ssize_t i = 0; i < nargs; i++) { + PyTuple_SET_ITEM(varargs, i, Py_NewRef(args[i])); + } + + if (PySys_Audit("gc.get_referents", "(O)", varargs) < 0) { + Py_DECREF(varargs); return NULL; } PyInterpreterState *interp = _PyInterpreterState_GET(); PyObject *result = PyList_New(0); - if (result == NULL) + if (result == NULL) { + Py_DECREF(varargs); return NULL; + } // NOTE: stop the world is a no-op in default build _PyEval_StopTheWorld(interp); - int err = append_referrents(result, args); + int err = append_referrents(result, varargs); _PyEval_StartTheWorld(interp); if (err < 0) { Py_CLEAR(result); } + Py_DECREF(varargs); return result; } diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 058f57770755aa..ad23dadd7b86cc 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -719,8 +719,17 @@ m_log10(double x) } +/*[clinic input] +math.gcd + + *integers as args: object + +Greatest Common Divisor. +[clinic start generated code]*/ + static PyObject * -math_gcd(PyObject *module, PyObject * const *args, Py_ssize_t nargs) +math_gcd_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) +/*[clinic end generated code: output=b57687fcf431c1b8 input=94e675b7ceeaf0c9]*/ { // Fast-path for the common case: gcd(int, int) if (nargs == 2 && PyLong_CheckExact(args[0]) && PyLong_CheckExact(args[1])) @@ -763,12 +772,6 @@ math_gcd(PyObject *module, PyObject * const *args, Py_ssize_t nargs) return res; } -PyDoc_STRVAR(math_gcd_doc, -"gcd($module, *integers)\n" -"--\n" -"\n" -"Greatest Common Divisor."); - static PyObject * long_lcm(PyObject *a, PyObject *b) @@ -798,8 +801,17 @@ long_lcm(PyObject *a, PyObject *b) } +/*[clinic input] +math.lcm + + *integers as args: object + +Least Common Multiple. +[clinic start generated code]*/ + static PyObject * -math_lcm(PyObject *module, PyObject * const *args, Py_ssize_t nargs) +math_lcm_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) +/*[clinic end generated code: output=f3eff0c25e4d7030 input=e64c33e85f4c47c6]*/ { PyObject *res, *x; Py_ssize_t i; @@ -839,13 +851,6 @@ math_lcm(PyObject *module, PyObject * const *args, Py_ssize_t nargs) } -PyDoc_STRVAR(math_lcm_doc, -"lcm($module, *integers)\n" -"--\n" -"\n" -"Least Common Multiple."); - - /* Call is_error when errno != 0, and where x is the result libm * returned. is_error will usually set up an exception and return * true (1), but may return false (0) without setting up an exception. @@ -2621,9 +2626,28 @@ math_dist_impl(PyObject *module, PyObject *p, PyObject *q) return NULL; } -/* AC: cannot convert yet, waiting for *args support */ +/*[clinic input] +math.hypot + + *coordinates as args: object + +Multidimensional Euclidean distance from the origin to a point. + +Roughly equivalent to: + sqrt(sum(x**2 for x in coordinates)) + +For a two dimensional point (x, y), gives the hypotenuse +using the Pythagorean theorem: sqrt(x*x + y*y). + +For example, the hypotenuse of a 3/4/5 right triangle is: + + >>> hypot(3.0, 4.0) + 5.0 +[clinic start generated code]*/ + static PyObject * -math_hypot(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +math_hypot_impl(PyObject *module, Py_ssize_t nargs, PyObject *const *args) +/*[clinic end generated code: output=dcb6d4b7a1102ee1 input=5c0061a2d11235ed]*/ { Py_ssize_t i; PyObject *item; @@ -2664,22 +2688,6 @@ math_hypot(PyObject *self, PyObject *const *args, Py_ssize_t nargs) #undef NUM_STACK_ELEMS -PyDoc_STRVAR(math_hypot_doc, - "hypot(*coordinates) -> value\n\n\ -Multidimensional Euclidean distance from the origin to a point.\n\ -\n\ -Roughly equivalent to:\n\ - sqrt(sum(x**2 for x in coordinates))\n\ -\n\ -For a two dimensional point (x, y), gives the hypotenuse\n\ -using the Pythagorean theorem: sqrt(x*x + y*y).\n\ -\n\ -For example, the hypotenuse of a 3/4/5 right triangle is:\n\ -\n\ - >>> hypot(3.0, 4.0)\n\ - 5.0\n\ -"); - /** sumprod() ***************************************************************/ /* Forward declaration */ @@ -4112,14 +4120,14 @@ static PyMethodDef math_methods[] = { MATH_FREXP_METHODDEF MATH_FSUM_METHODDEF {"gamma", math_gamma, METH_O, math_gamma_doc}, - {"gcd", _PyCFunction_CAST(math_gcd), METH_FASTCALL, math_gcd_doc}, - {"hypot", _PyCFunction_CAST(math_hypot), METH_FASTCALL, math_hypot_doc}, + MATH_GCD_METHODDEF + MATH_HYPOT_METHODDEF MATH_ISCLOSE_METHODDEF MATH_ISFINITE_METHODDEF MATH_ISINF_METHODDEF MATH_ISNAN_METHODDEF MATH_ISQRT_METHODDEF - {"lcm", _PyCFunction_CAST(math_lcm), METH_FASTCALL, math_lcm_doc}, + MATH_LCM_METHODDEF MATH_LDEXP_METHODDEF {"lgamma", math_lgamma, METH_O, math_lgamma_doc}, {"log", _PyCFunction_CAST(math_log), METH_FASTCALL, math_log_doc}, diff --git a/Objects/clinic/setobject.c.h b/Objects/clinic/setobject.c.h index 3853ce3bce685b..1fc1b1166d06b6 100644 --- a/Objects/clinic/setobject.c.h +++ b/Objects/clinic/setobject.c.h @@ -41,28 +41,22 @@ PyDoc_STRVAR(set_update__doc__, {"update", _PyCFunction_CAST(set_update), METH_FASTCALL, set_update__doc__}, static PyObject * -set_update_impl(PySetObject *so, PyObject *args); +set_update_impl(PySetObject *so, Py_ssize_t nargs, PyObject *const *args); static PyObject * set_update(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("update", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = set_update_impl(so, __clinic_args); + __clinic_args = args + 0; + return_value = set_update_impl(so, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -148,28 +142,22 @@ PyDoc_STRVAR(set_union__doc__, {"union", _PyCFunction_CAST(set_union), METH_FASTCALL, set_union__doc__}, static PyObject * -set_union_impl(PySetObject *so, PyObject *args); +set_union_impl(PySetObject *so, Py_ssize_t nargs, PyObject *const *args); static PyObject * set_union(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("union", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = set_union_impl(so, __clinic_args); + __clinic_args = args + 0; + return_value = set_union_impl(so, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -183,28 +171,23 @@ PyDoc_STRVAR(set_intersection_multi__doc__, {"intersection", _PyCFunction_CAST(set_intersection_multi), METH_FASTCALL, set_intersection_multi__doc__}, static PyObject * -set_intersection_multi_impl(PySetObject *so, PyObject *args); +set_intersection_multi_impl(PySetObject *so, Py_ssize_t nargs, + PyObject *const *args); static PyObject * set_intersection_multi(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("intersection", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = set_intersection_multi_impl(so, __clinic_args); + __clinic_args = args + 0; + return_value = set_intersection_multi_impl(so, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -218,28 +201,23 @@ PyDoc_STRVAR(set_intersection_update_multi__doc__, {"intersection_update", _PyCFunction_CAST(set_intersection_update_multi), METH_FASTCALL, set_intersection_update_multi__doc__}, static PyObject * -set_intersection_update_multi_impl(PySetObject *so, PyObject *args); +set_intersection_update_multi_impl(PySetObject *so, Py_ssize_t nargs, + PyObject *const *args); static PyObject * set_intersection_update_multi(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("intersection_update", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = set_intersection_update_multi_impl(so, __clinic_args); + __clinic_args = args + 0; + return_value = set_intersection_update_multi_impl(so, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -277,28 +255,23 @@ PyDoc_STRVAR(set_difference_update__doc__, {"difference_update", _PyCFunction_CAST(set_difference_update), METH_FASTCALL, set_difference_update__doc__}, static PyObject * -set_difference_update_impl(PySetObject *so, PyObject *args); +set_difference_update_impl(PySetObject *so, Py_ssize_t nargs, + PyObject *const *args); static PyObject * set_difference_update(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("difference_update", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = set_difference_update_impl(so, __clinic_args); + __clinic_args = args + 0; + return_value = set_difference_update_impl(so, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -312,28 +285,23 @@ PyDoc_STRVAR(set_difference_multi__doc__, {"difference", _PyCFunction_CAST(set_difference_multi), METH_FASTCALL, set_difference_multi__doc__}, static PyObject * -set_difference_multi_impl(PySetObject *so, PyObject *args); +set_difference_multi_impl(PySetObject *so, Py_ssize_t nargs, + PyObject *const *args); static PyObject * set_difference_multi(PySetObject *so, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t nvararg = nargs; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("difference", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = set_difference_multi_impl(so, __clinic_args); + __clinic_args = args + 0; + return_value = set_difference_multi_impl(so, nvararg, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -568,4 +536,4 @@ set___sizeof__(PySetObject *so, PyObject *Py_UNUSED(ignored)) return return_value; } -/*[clinic end generated code: output=de4ee725bd29f758 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=240fed957387b958 input=a9049054013a1b77]*/ diff --git a/Objects/setobject.c b/Objects/setobject.c index 9f40e085f06fa6..66d7fc730c555c 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -1060,13 +1060,13 @@ Update the set, adding elements from all others. [clinic start generated code]*/ static PyObject * -set_update_impl(PySetObject *so, PyObject *args) -/*[clinic end generated code: output=34f6371704974c8a input=df4fe486e38cd337]*/ +set_update_impl(PySetObject *so, Py_ssize_t nargs, PyObject *const *args) +/*[clinic end generated code: output=050e2a21f8d7d16a input=df4fe486e38cd337]*/ { Py_ssize_t i; - for (i=0 ; i None: nargs = 'PyTuple_GET_SIZE(args)' argname_fmt = 'PyTuple_GET_ITEM(args, %d)' + if self.vararg != NO_VARARG: + if self.max_pos == 0: + self.declarations = "Py_ssize_t nvararg = %s;" % nargs + else: + self.declarations = "Py_ssize_t nvararg = Py_MAX(%s - %d, 0);" % (nargs, self.max_pos) + else: + self.declarations = "" + left_args = f"{nargs} - {self.max_pos}" max_args = NO_VARARG if (self.vararg != NO_VARARG) else self.max_pos if self.limited_capi: @@ -520,28 +528,15 @@ def parse_pos_only(self) -> None: if p.is_vararg(): if self.fastcall: parser_code.append(libclinic.normalize_snippet(""" - %s = PyTuple_New(%s); - if (!%s) {{ - goto exit; - }} - for (Py_ssize_t i = 0; i < %s; ++i) {{ - PyTuple_SET_ITEM(%s, i, Py_NewRef(args[%d + i])); - }} + %s = args + %d; """ % ( p.converter.parser_name, - left_args, - p.converter.parser_name, - left_args, - p.converter.parser_name, - self.max_pos + self.vararg ), indent=4)) else: parser_code.append(libclinic.normalize_snippet(""" - %s = PyTuple_GetSlice(%d, -1); - """ % ( - p.converter.parser_name, - self.max_pos - ), indent=4)) + %s = _PyTuple_CAST(args)->ob_item; + """ % p.converter.parser_name, indent=4)) continue displayname = p.get_displayname(i+1) @@ -588,7 +583,7 @@ def parse_pos_only(self) -> None: goto exit; }} """, indent=4)] - self.parser_body(*parser_code) + self.parser_body(*parser_code, declarations=self.declarations) def parse_general(self, clang: CLanguage) -> None: parsearg: str | None