From 69ba0e931d61a4fcdbf88cee6d612fb3842766db Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Thu, 26 Sep 2024 11:34:13 +0300 Subject: [PATCH 01/32] initial implementation --- Modules/_functoolsmodule.c | 132 +++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 50 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 2b3bd7c3de1176..cd95ea3ff2f29f 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -334,31 +334,19 @@ partial_descr_get(PyObject *self, PyObject *obj, PyObject *type) return PyMethod_New(self, obj); } -/* Merging keyword arguments using the vectorcall convention is messy, so - * if we would need to do that, we stop using vectorcall and fall back - * to using partial_call() instead. */ -Py_NO_INLINE static PyObject * -partial_vectorcall_fallback(PyThreadState *tstate, partialobject *pto, - PyObject *const *args, size_t nargsf, - PyObject *kwnames) -{ - pto->vectorcall = NULL; - Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); - return _PyObject_MakeTpCall(tstate, (PyObject *)pto, - args, nargs, kwnames); -} - static PyObject * partial_vectorcall(partialobject *pto, PyObject *const *args, size_t nargsf, PyObject *kwnames) { PyThreadState *tstate = _PyThreadState_GET(); + /* Sizes */ + Py_ssize_t pto_nargs = PyTuple_GET_SIZE(pto->args); + Py_ssize_t pto_nkwds = PyDict_GET_SIZE(pto->kw); Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + Py_ssize_t nkwds = kwnames == NULL ? 0 : PyTuple_GET_SIZE(kwnames); + Py_ssize_t nargskw = nargs + nkwds; - /* pto->kw is mutable, so need to check every time */ - if (PyDict_GET_SIZE(pto->kw)) { - return partial_vectorcall_fallback(tstate, pto, args, nargsf, kwnames); - } + /* Placeholder check */ Py_ssize_t pto_phcount = pto->phcount; if (nargs < pto_phcount) { PyErr_Format(PyExc_TypeError, @@ -367,36 +355,55 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, return NULL; } - Py_ssize_t nargskw = nargs; - if (kwnames != NULL) { - nargskw += PyTuple_GET_SIZE(kwnames); - } - + /* Divergence on whether pto has kwds */ + Py_ssize_t tot_nargs = pto_nargs + nargs - pto_phcount; + Py_ssize_t tot_nkwds; + Py_ssize_t tot_nargskw; PyObject **pto_args = _PyTuple_ITEMS(pto->args); - Py_ssize_t pto_nargs = PyTuple_GET_SIZE(pto->args); + PyObject *new_kw, *key, *val; + if (pto_nkwds == 0) { + tot_nkwds = nkwds; + + /* Fast path if we're called without arguments */ + if (nargskw == 0) { + return _PyObject_VectorcallTstate(tstate, pto->fn, pto_args, + pto_nargs, NULL); + } - /* Fast path if we're called without arguments */ - if (nargskw == 0) { - return _PyObject_VectorcallTstate(tstate, pto->fn, - pto_args, pto_nargs, NULL); + /* Use PY_VECTORCALL_ARGUMENTS_OFFSET to prepend a single + * positional argument */ + if (pto_nargs == 1 && (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET)) { + PyObject **newargs = (PyObject **)args - 1; + PyObject *tmp = newargs[0]; + newargs[0] = pto_args[0]; + PyObject *ret = _PyObject_VectorcallTstate(tstate, pto->fn, newargs, + nargs + 1, kwnames); + newargs[0] = tmp; + return ret; + } } + else { + /* Merge Keywords */ + new_kw = PyDict_Copy(pto->kw); + if (new_kw == NULL) { + return NULL; + } - /* Fast path using PY_VECTORCALL_ARGUMENTS_OFFSET to prepend a single - * positional argument */ - if (pto_nargs == 1 && (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET)) { - PyObject **newargs = (PyObject **)args - 1; - PyObject *tmp = newargs[0]; - newargs[0] = pto_args[0]; - PyObject *ret = _PyObject_VectorcallTstate(tstate, pto->fn, - newargs, nargs + 1, kwnames); - newargs[0] = tmp; - return ret; + for (Py_ssize_t i=0; i < nkwds; i++) { + key = PyTuple_GET_ITEM(kwnames, i); + if (PyDict_SetItem(new_kw, key, args[nargs + i]) != 0) { + Py_DECREF(new_kw); + return NULL; + } + } + + tot_nkwds = PyDict_GET_SIZE(new_kw); } + tot_nargskw = tot_nargs + tot_nkwds; + /* Allocate Stack */ PyObject *small_stack[_PY_FASTCALL_SMALL_STACK]; PyObject **stack; - - Py_ssize_t tot_nargskw = pto_nargs + nargskw - pto_phcount; if (tot_nargskw <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { stack = small_stack; } @@ -408,12 +415,11 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, } } - Py_ssize_t tot_nargs; + /* Copy Positionals to new stack */ if (pto_phcount) { - tot_nargs = pto_nargs + nargs - pto_phcount; Py_ssize_t j = 0; // New args index for (Py_ssize_t i = 0; i < pto_nargs; i++) { - if (pto_args[i] == pto->placeholder) { + if (pto_args[i] == pto->placeholder){ stack[i] = args[j]; j += 1; } @@ -422,18 +428,44 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, } } assert(j == pto_phcount); - if (nargskw > pto_phcount) { - memcpy(stack + pto_nargs, args + j, (nargskw - j) * sizeof(PyObject*)); + /* Add remaining args from new_args */ + if (nargs > pto_phcount) { + memcpy(stack + pto_nargs, args + j, (nargs - j) * sizeof(PyObject*)); } } else { - tot_nargs = pto_nargs + nargs; - /* Copy to new stack, using borrowed references */ memcpy(stack, pto_args, pto_nargs * sizeof(PyObject*)); - memcpy(stack + pto_nargs, args, nargskw * sizeof(PyObject*)); + memcpy(stack + pto_nargs, args, nargs * sizeof(PyObject*)); + } + + /* Copy Keywords to new stack */ + PyObject *ret; + if (pto_nkwds) { + PyObject *new_kwnames = PyTuple_New(tot_nkwds); + if (new_kwnames == NULL) { + Py_DECREF(new_kw); + return NULL; + } + + Py_ssize_t pos = 0, i = 0; + while (PyDict_Next(new_kw, &pos, &key, &val)) { + PyTuple_SET_ITEM(new_kwnames, i, key); + Py_INCREF(key); + stack[tot_nargs + i] = val; + i += 1; + } + ret = _PyObject_VectorcallTstate(tstate, pto->fn, stack, tot_nargs, new_kwnames); + Py_DECREF(new_kwnames); + Py_DECREF(new_kw); } - PyObject *ret = _PyObject_VectorcallTstate(tstate, pto->fn, - stack, tot_nargs, kwnames); + else { + if (nkwds) { + memcpy(stack + tot_nargs, args + nargs, nkwds * sizeof(PyObject*)); + } + ret = _PyObject_VectorcallTstate(tstate, pto->fn, stack, tot_nargs, kwnames); + } + + /* Free stack & Return */ if (stack != small_stack) { PyMem_Free(stack); } From 9a21b5573c640076b1c3f2070e0e350dbed633c9 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Thu, 26 Sep 2024 15:29:44 +0300 Subject: [PATCH 02/32] V2 --- Modules/_functoolsmodule.c | 200 +++++++++++++++++++++++-------------- 1 file changed, 127 insertions(+), 73 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index cd95ea3ff2f29f..ee3b9ae9567ad6 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -355,15 +355,14 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, return NULL; } - /* Divergence on whether pto has kwds */ Py_ssize_t tot_nargs = pto_nargs + nargs - pto_phcount; Py_ssize_t tot_nkwds; Py_ssize_t tot_nargskw; PyObject **pto_args = _PyTuple_ITEMS(pto->args); - PyObject *new_kw, *key, *val; - if (pto_nkwds == 0) { - tot_nkwds = nkwds; + PyObject *ret; + /* Divergence on whether pto has kwds */ + if (pto_nkwds == 0) { /* Fast path if we're called without arguments */ if (nargskw == 0) { return _PyObject_VectorcallTstate(tstate, pto->fn, pto_args, @@ -376,100 +375,155 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, PyObject **newargs = (PyObject **)args - 1; PyObject *tmp = newargs[0]; newargs[0] = pto_args[0]; - PyObject *ret = _PyObject_VectorcallTstate(tstate, pto->fn, newargs, - nargs + 1, kwnames); + ret = _PyObject_VectorcallTstate(tstate, pto->fn, newargs, + nargs + 1, kwnames); newargs[0] = tmp; return ret; } - } - else { - /* Merge Keywords */ - new_kw = PyDict_Copy(pto->kw); - if (new_kw == NULL) { - return NULL; - } - for (Py_ssize_t i=0; i < nkwds; i++) { - key = PyTuple_GET_ITEM(kwnames, i); - if (PyDict_SetItem(new_kw, key, args[nargs + i]) != 0) { - Py_DECREF(new_kw); + tot_nkwds = nkwds; + tot_nargskw = tot_nargs + tot_nkwds; + + /* Allocate Stack */ + PyObject *small_stack[_PY_FASTCALL_SMALL_STACK]; + PyObject **stack; + if (tot_nargskw <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { + stack = small_stack; + } + else { + stack = PyMem_Malloc(tot_nargskw * sizeof(PyObject *)); + if (stack == NULL) { + PyErr_NoMemory(); return NULL; } } - tot_nkwds = PyDict_GET_SIZE(new_kw); - } - tot_nargskw = tot_nargs + tot_nkwds; + /* Copy Positionals to new stack */ + if (pto_phcount) { + Py_ssize_t j = 0; // New args index + for (Py_ssize_t i = 0; i < pto_nargs; i++) { + if (pto_args[i] == pto->placeholder){ + stack[i] = args[j]; + j += 1; + } + else { + stack[i] = pto_args[i]; + } + } + assert(j == pto_phcount); + /* Add remaining args from new_args */ + if (nargs > pto_phcount) { + memcpy(stack + pto_nargs, args + j, (nargskw - j) * sizeof(PyObject*)); + } + } + else { + memcpy(stack, pto_args, pto_nargs * sizeof(PyObject*)); + memcpy(stack + pto_nargs, args, nargskw * sizeof(PyObject*)); + } + ret = _PyObject_VectorcallTstate(tstate, pto->fn, stack, tot_nargs, kwnames); - /* Allocate Stack */ - PyObject *small_stack[_PY_FASTCALL_SMALL_STACK]; - PyObject **stack; - if (tot_nargskw <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { - stack = small_stack; + /* Free stack & Return */ + if (stack != small_stack) { + PyMem_Free(stack); + } + return ret; } else { - stack = PyMem_Malloc(tot_nargskw * sizeof(PyObject *)); - if (stack == NULL) { - PyErr_NoMemory(); - return NULL; + PyObject *key, *val; + tot_nkwds = PyDict_GET_SIZE(pto->kw) + nkwds; + for (Py_ssize_t i = 0; i < nkwds; i++) { + key = PyTuple_GET_ITEM(kwnames, i); + if (PyDict_Contains(pto->kw, key)) { + tot_nkwds--; + } } - } + tot_nargskw = tot_nargs + tot_nkwds; - /* Copy Positionals to new stack */ - if (pto_phcount) { - Py_ssize_t j = 0; // New args index - for (Py_ssize_t i = 0; i < pto_nargs; i++) { - if (pto_args[i] == pto->placeholder){ - stack[i] = args[j]; - j += 1; + /* Allocate Stack */ + PyObject *small_stack[_PY_FASTCALL_SMALL_STACK]; + PyObject **stack; + if (tot_nargskw <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { + stack = small_stack; + } + else { + stack = PyMem_Malloc(tot_nargskw * sizeof(PyObject *)); + if (stack == NULL) { + PyErr_NoMemory(); + return NULL; } - else { - stack[i] = pto_args[i]; + } + + /* Copy Positionals to new stack */ + if (pto_phcount) { + Py_ssize_t j = 0; // New args index + for (Py_ssize_t i = 0; i < pto_nargs; i++) { + if (pto_args[i] == pto->placeholder){ + stack[i] = args[j]; + j += 1; + } + else { + stack[i] = pto_args[i]; + } + } + assert(j == pto_phcount); + /* Add remaining args from new_args */ + if (nargs > pto_phcount) { + memcpy(stack + pto_nargs, args + j, (nargs - j) * sizeof(PyObject*)); } } - assert(j == pto_phcount); - /* Add remaining args from new_args */ - if (nargs > pto_phcount) { - memcpy(stack + pto_nargs, args + j, (nargs - j) * sizeof(PyObject*)); + else { + memcpy(stack, pto_args, pto_nargs * sizeof(PyObject*)); + memcpy(stack + pto_nargs, args, nargs * sizeof(PyObject*)); } - } - else { - memcpy(stack, pto_args, pto_nargs * sizeof(PyObject*)); - memcpy(stack + pto_nargs, args, nargs * sizeof(PyObject*)); - } - /* Copy Keywords to new stack */ - PyObject *ret; - if (pto_nkwds) { - PyObject *new_kwnames = PyTuple_New(tot_nkwds); - if (new_kwnames == NULL) { - Py_DECREF(new_kw); + /* Copy Keywords to new stack */ + PyObject *tot_kwnames = PyTuple_New(tot_nkwds); + if (tot_kwnames == NULL) { return NULL; } + if (nkwds) { + /* Merge kwds */ + PyObject *new_kw = PyDict_Copy(pto->kw); + if (new_kw == NULL) { + return NULL; + } + + for (Py_ssize_t i=0; i < nkwds; i++) { + key = PyTuple_GET_ITEM(kwnames, i); + if (PyDict_SetItem(new_kw, key, args[nargs + i])) { + Py_DECREF(new_kw); + return NULL; + } + } - Py_ssize_t pos = 0, i = 0; - while (PyDict_Next(new_kw, &pos, &key, &val)) { - PyTuple_SET_ITEM(new_kwnames, i, key); - Py_INCREF(key); - stack[tot_nargs + i] = val; - i += 1; + Py_ssize_t pos = 0, i = 0; + while (PyDict_Next(new_kw, &pos, &key, &val)) { + PyTuple_SET_ITEM(tot_kwnames, i, key); + Py_INCREF(key); + stack[tot_nargs + i] = val; + i += 1; + } + Py_DECREF(new_kw); } - ret = _PyObject_VectorcallTstate(tstate, pto->fn, stack, tot_nargs, new_kwnames); - Py_DECREF(new_kwnames); - Py_DECREF(new_kw); - } - else { - if (nkwds) { - memcpy(stack + tot_nargs, args + nargs, nkwds * sizeof(PyObject*)); + else { + /* Set pto->kw only */ + Py_ssize_t pos = 0, i = 0; + while (PyDict_Next(pto->kw, &pos, &key, &val)) { + PyTuple_SET_ITEM(tot_kwnames, i, key); + Py_INCREF(key); + stack[tot_nargs + i] = val; + i += 1; + } } - ret = _PyObject_VectorcallTstate(tstate, pto->fn, stack, tot_nargs, kwnames); - } + ret = _PyObject_VectorcallTstate(tstate, pto->fn, stack, tot_nargs, tot_kwnames); + Py_DECREF(tot_kwnames); - /* Free stack & Return */ - if (stack != small_stack) { - PyMem_Free(stack); + /* Free stack & Return */ + if (stack != small_stack) { + PyMem_Free(stack); + } + return ret; } - return ret; } /* Set pto->vectorcall depending on the parameters of the partial object */ From f23021ceeef17c9b064bc72c9f793c5467e102c4 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Thu, 26 Sep 2024 17:35:08 +0300 Subject: [PATCH 03/32] small fixes --- Modules/_functoolsmodule.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index ee3b9ae9567ad6..54d7c66ebc5b37 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -482,31 +482,33 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, return NULL; } if (nkwds) { - /* Merge kwds */ - PyObject *new_kw = PyDict_Copy(pto->kw); - if (new_kw == NULL) { + /* Merge keywords with pto->kw */ + PyObject *tot_kw = PyDict_Copy(pto->kw); + if (tot_kw == NULL) { + Py_DECREF(tot_kwnames); return NULL; } - for (Py_ssize_t i=0; i < nkwds; i++) { key = PyTuple_GET_ITEM(kwnames, i); - if (PyDict_SetItem(new_kw, key, args[nargs + i])) { - Py_DECREF(new_kw); + val = args[nargs + i]; + if (PyDict_SetItem(tot_kw, key, val)) { + Py_DECREF(tot_kwnames); + Py_DECREF(tot_kw); return NULL; } } Py_ssize_t pos = 0, i = 0; - while (PyDict_Next(new_kw, &pos, &key, &val)) { + while (PyDict_Next(tot_kw, &pos, &key, &val)) { PyTuple_SET_ITEM(tot_kwnames, i, key); Py_INCREF(key); stack[tot_nargs + i] = val; i += 1; } - Py_DECREF(new_kw); + Py_DECREF(tot_kw); } else { - /* Set pto->kw only */ + /* Call with pto->kw only */ Py_ssize_t pos = 0, i = 0; while (PyDict_Next(pto->kw, &pos, &key, &val)) { PyTuple_SET_ITEM(tot_kwnames, i, key); From d840ad73fb6a7618198acb6271ff4270f587fee3 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Thu, 26 Sep 2024 19:22:40 +0300 Subject: [PATCH 04/32] V3 --- Modules/_functoolsmodule.c | 74 ++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 43 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 54d7c66ebc5b37..9588ecc6ce2d9c 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -429,15 +429,31 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, return ret; } else { + PyObject *tot_kw = NULL; + PyObject *tot_kwnames = NULL; PyObject *key, *val; - tot_nkwds = PyDict_GET_SIZE(pto->kw) + nkwds; - for (Py_ssize_t i = 0; i < nkwds; i++) { - key = PyTuple_GET_ITEM(kwnames, i); - if (PyDict_Contains(pto->kw, key)) { - tot_nkwds--; + int has_nkwds = 0; + + if (!nkwds) { + tot_kw = pto->kw; + tot_nkwds = pto_nkwds; + tot_nargskw = tot_nargs + tot_nkwds; + } + else { + has_nkwds = 1; + tot_kw = PyDict_Copy(pto->kw); + for (Py_ssize_t i=0; i < nkwds; i++) { + key = PyTuple_GET_ITEM(kwnames, i); + val = args[nargs + i]; + if (PyDict_SetItem(tot_kw, key, val)) { + Py_DECREF(tot_kw); + return NULL; + } } + + tot_nkwds = PyDict_GET_SIZE(tot_kw); + tot_nargskw = tot_nargs + tot_nkwds; } - tot_nargskw = tot_nargs + tot_nkwds; /* Allocate Stack */ PyObject *small_stack[_PY_FASTCALL_SMALL_STACK]; @@ -477,46 +493,18 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, } /* Copy Keywords to new stack */ - PyObject *tot_kwnames = PyTuple_New(tot_nkwds); - if (tot_kwnames == NULL) { - return NULL; + tot_kwnames = PyTuple_New(tot_nkwds); + Py_ssize_t pos = 0, i = 0; + while (PyDict_Next(tot_kw, &pos, &key, &val)) { + PyTuple_SET_ITEM(tot_kwnames, i, key); + Py_INCREF(key); + stack[tot_nargs + i] = val; + i += 1; } - if (nkwds) { - /* Merge keywords with pto->kw */ - PyObject *tot_kw = PyDict_Copy(pto->kw); - if (tot_kw == NULL) { - Py_DECREF(tot_kwnames); - return NULL; - } - for (Py_ssize_t i=0; i < nkwds; i++) { - key = PyTuple_GET_ITEM(kwnames, i); - val = args[nargs + i]; - if (PyDict_SetItem(tot_kw, key, val)) { - Py_DECREF(tot_kwnames); - Py_DECREF(tot_kw); - return NULL; - } - } - - Py_ssize_t pos = 0, i = 0; - while (PyDict_Next(tot_kw, &pos, &key, &val)) { - PyTuple_SET_ITEM(tot_kwnames, i, key); - Py_INCREF(key); - stack[tot_nargs + i] = val; - i += 1; - } + if (has_nkwds) { Py_DECREF(tot_kw); } - else { - /* Call with pto->kw only */ - Py_ssize_t pos = 0, i = 0; - while (PyDict_Next(pto->kw, &pos, &key, &val)) { - PyTuple_SET_ITEM(tot_kwnames, i, key); - Py_INCREF(key); - stack[tot_nargs + i] = val; - i += 1; - } - } + ret = _PyObject_VectorcallTstate(tstate, pto->fn, stack, tot_nargs, tot_kwnames); Py_DECREF(tot_kwnames); From 2dd7568565ef131cce6d9f6f33cc2b0188a1a822 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Fri, 27 Sep 2024 17:49:48 +0300 Subject: [PATCH 05/32] V4 --- Modules/_functoolsmodule.c | 162 ++++++++++++++++++++++--------------- 1 file changed, 96 insertions(+), 66 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 9588ecc6ce2d9c..3f7cb6f156f4be 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -334,6 +334,27 @@ partial_descr_get(PyObject *self, PyObject *obj, PyObject *type) return PyMethod_New(self, obj); } +#define ALLOCATE_STACK(type, size, small_stack, stack) \ + do { \ + if (size <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { \ + stack = small_stack; \ + } \ + else { \ + stack = PyMem_Malloc(size * sizeof(type *)); \ + if (stack == NULL) { \ + PyErr_NoMemory(); \ + return NULL; \ + } \ + } \ + } while (0) + +#define DEALLOCATE_STACK(small_stack, stack) \ + do { \ + if (stack != small_stack) { \ + PyMem_Free(stack); \ + } \ + } while (0) + static PyObject * partial_vectorcall(partialobject *pto, PyObject *const *args, size_t nargsf, PyObject *kwnames) @@ -355,13 +376,10 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, return NULL; } - Py_ssize_t tot_nargs = pto_nargs + nargs - pto_phcount; - Py_ssize_t tot_nkwds; - Py_ssize_t tot_nargskw; PyObject **pto_args = _PyTuple_ITEMS(pto->args); PyObject *ret; - /* Divergence on whether pto has kwds */ + /* Special cases */ if (pto_nkwds == 0) { /* Fast path if we're called without arguments */ if (nargskw == 0) { @@ -370,7 +388,8 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, } /* Use PY_VECTORCALL_ARGUMENTS_OFFSET to prepend a single - * positional argument */ + * positional argument. + * NOTE: Could add (Placeholder, arg) case in similar manner */ if (pto_nargs == 1 && (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET)) { PyObject **newargs = (PyObject **)args - 1; PyObject *tmp = newargs[0]; @@ -381,22 +400,21 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, return ret; } + } + + /* Size variables */ + Py_ssize_t tot_nargs = pto_nargs + nargs - pto_phcount; + Py_ssize_t tot_nkwds; + Py_ssize_t tot_nargskw; + + /* Divergence on whether pto has kwds */ + if (pto_nkwds == 0) { tot_nkwds = nkwds; tot_nargskw = tot_nargs + tot_nkwds; /* Allocate Stack */ - PyObject *small_stack[_PY_FASTCALL_SMALL_STACK]; - PyObject **stack; - if (tot_nargskw <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { - stack = small_stack; - } - else { - stack = PyMem_Malloc(tot_nargskw * sizeof(PyObject *)); - if (stack == NULL) { - PyErr_NoMemory(); - return NULL; - } - } + PyObject **stack, *small_stack[_PY_FASTCALL_SMALL_STACK]; + ALLOCATE_STACK(PyObject, tot_nargskw, small_stack, stack); /* Copy Positionals to new stack */ if (pto_phcount) { @@ -420,56 +438,52 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, memcpy(stack, pto_args, pto_nargs * sizeof(PyObject*)); memcpy(stack + pto_nargs, args, nargskw * sizeof(PyObject*)); } - ret = _PyObject_VectorcallTstate(tstate, pto->fn, stack, tot_nargs, kwnames); - /* Free stack & Return */ - if (stack != small_stack) { - PyMem_Free(stack); - } + /* Call / Maintenance / Return */ + ret = _PyObject_VectorcallTstate(tstate, pto->fn, stack, tot_nargs, kwnames); + DEALLOCATE_STACK(small_stack, stack); return ret; } else { - PyObject *tot_kw = NULL; - PyObject *tot_kwnames = NULL; PyObject *key, *val; - int has_nkwds = 0; - if (!nkwds) { - tot_kw = pto->kw; - tot_nkwds = pto_nkwds; - tot_nargskw = tot_nargs + tot_nkwds; + PyObject **pto_kwkeys, *small_pto_kwkeys[_PY_FASTCALL_SMALL_STACK]; + PyObject **pto_kwvals, *small_pto_kwvals[_PY_FASTCALL_SMALL_STACK]; + ALLOCATE_STACK(PyObject, pto_nkwds, small_pto_kwkeys, pto_kwkeys); + ALLOCATE_STACK(PyObject, pto_nkwds, small_pto_kwvals, pto_kwvals); + Py_ssize_t pos = 0, i = 0; + while (PyDict_Next(pto->kw, &pos, &key, &val)) { + pto_kwkeys[i] = key; + pto_kwvals[i] = val; + i++; } - else { - has_nkwds = 1; - tot_kw = PyDict_Copy(pto->kw); + + /* Calculate total kw size and positions of items to override */ + Py_ssize_t *positions, small_positions[_PY_FASTCALL_SMALL_STACK]; + tot_nkwds = pto_nkwds + nkwds; + if (nkwds) { + ALLOCATE_STACK(Py_ssize_t, nkwds, small_positions, positions); for (Py_ssize_t i=0; i < nkwds; i++) { + positions[i] = -1; key = PyTuple_GET_ITEM(kwnames, i); - val = args[nargs + i]; - if (PyDict_SetItem(tot_kw, key, val)) { - Py_DECREF(tot_kw); - return NULL; + if (PyDict_Contains(pto->kw, key)) { + tot_nkwds--; + for (Py_ssize_t j=0; j < pto_nkwds; j++) { + if (PyObject_RichCompareBool(key, pto_kwkeys[j], Py_EQ)) { + positions[i] = j; + break; + } + } } } - - tot_nkwds = PyDict_GET_SIZE(tot_kw); - tot_nargskw = tot_nargs + tot_nkwds; } + tot_nargskw = tot_nargs + tot_nkwds; /* Allocate Stack */ - PyObject *small_stack[_PY_FASTCALL_SMALL_STACK]; - PyObject **stack; - if (tot_nargskw <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { - stack = small_stack; - } - else { - stack = PyMem_Malloc(tot_nargskw * sizeof(PyObject *)); - if (stack == NULL) { - PyErr_NoMemory(); - return NULL; - } - } + PyObject **stack, *small_stack[_PY_FASTCALL_SMALL_STACK]; + ALLOCATE_STACK(PyObject, tot_nargskw, small_stack, stack); - /* Copy Positionals to new stack */ + /* Copy Positionals to stack */ if (pto_phcount) { Py_ssize_t j = 0; // New args index for (Py_ssize_t i = 0; i < pto_nargs; i++) { @@ -492,26 +506,43 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, memcpy(stack + pto_nargs, args, nargs * sizeof(PyObject*)); } - /* Copy Keywords to new stack */ - tot_kwnames = PyTuple_New(tot_nkwds); - Py_ssize_t pos = 0, i = 0; - while (PyDict_Next(tot_kw, &pos, &key, &val)) { + /* Copy Pto Keywords to stack */ + memcpy(stack + tot_nargs, pto_kwvals, pto_nkwds * sizeof(PyObject*)); + DEALLOCATE_STACK(small_pto_kwvals, pto_kwvals); + + /* Copy New Keywords to stack */ + PyObject *tot_kwnames = PyTuple_New(tot_nkwds); + for (Py_ssize_t i=0; i < pto_nkwds; i++) { + key = pto_kwkeys[i]; PyTuple_SET_ITEM(tot_kwnames, i, key); Py_INCREF(key); - stack[tot_nargs + i] = val; - i += 1; } - if (has_nkwds) { - Py_DECREF(tot_kw); + DEALLOCATE_STACK(small_pto_kwkeys, pto_kwkeys); + + if (nkwds) { + Py_ssize_t k = 0; + Py_ssize_t j; + for (Py_ssize_t i=0; i < nkwds; i++) { + key = PyTuple_GET_ITEM(kwnames, i); + val = args[nargs + i]; + j = positions[i]; + if (j != -1) { + stack[tot_nargs + j] = val; + } + else { + PyTuple_SET_ITEM(tot_kwnames, pto_nkwds + k, key); + Py_INCREF(key); + stack[tot_nargs + pto_nkwds + k] = val; + k++; + } + } + DEALLOCATE_STACK(small_positions, positions); } + /* Call / Maintenance / Return */ ret = _PyObject_VectorcallTstate(tstate, pto->fn, stack, tot_nargs, tot_kwnames); + DEALLOCATE_STACK(small_stack, stack); Py_DECREF(tot_kwnames); - - /* Free stack & Return */ - if (stack != small_stack) { - PyMem_Free(stack); - } return ret; } } @@ -532,7 +563,6 @@ partial_setvectorcall(partialobject *pto) } } - // Not converted to argument clinic, because of `*args, **kwargs` arguments. static PyObject * partial_call(partialobject *pto, PyObject *args, PyObject *kwargs) From 862097f5770a855dd66816ce64c6d306f45359f9 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Fri, 27 Sep 2024 18:04:39 +0300 Subject: [PATCH 06/32] fix compiler warnings --- Modules/_functoolsmodule.c | 94 +++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 38 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 3f7cb6f156f4be..05b14b00f9c94d 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -459,9 +459,11 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, } /* Calculate total kw size and positions of items to override */ - Py_ssize_t *positions, small_positions[_PY_FASTCALL_SMALL_STACK]; + PyObject *tot_kwnames; + PyObject **stack, *small_stack[_PY_FASTCALL_SMALL_STACK]; tot_nkwds = pto_nkwds + nkwds; if (nkwds) { + Py_ssize_t *positions, small_positions[_PY_FASTCALL_SMALL_STACK]; ALLOCATE_STACK(Py_ssize_t, nkwds, small_positions, positions); for (Py_ssize_t i=0; i < nkwds; i++) { positions[i] = -1; @@ -476,12 +478,61 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, } } } + tot_nargskw = tot_nargs + tot_nkwds; + + /* Allocate Stack */ + ALLOCATE_STACK(PyObject, tot_nargskw, small_stack, stack); + + /* Copy Pto Keywords to stack */ + memcpy(stack + tot_nargs, pto_kwvals, pto_nkwds * sizeof(PyObject*)); + DEALLOCATE_STACK(small_pto_kwvals, pto_kwvals); + + /* Copy New Keywords to stack */ + tot_kwnames = PyTuple_New(tot_nkwds); + for (Py_ssize_t i=0; i < pto_nkwds; i++) { + key = pto_kwkeys[i]; + PyTuple_SET_ITEM(tot_kwnames, i, key); + Py_INCREF(key); + } + DEALLOCATE_STACK(small_pto_kwkeys, pto_kwkeys); + + Py_ssize_t k = 0; + Py_ssize_t j; + for (Py_ssize_t i=0; i < nkwds; i++) { + key = PyTuple_GET_ITEM(kwnames, i); + val = args[nargs + i]; + j = positions[i]; + if (j != -1) { + stack[tot_nargs + j] = val; + } + else { + PyTuple_SET_ITEM(tot_kwnames, pto_nkwds + k, key); + Py_INCREF(key); + stack[tot_nargs + pto_nkwds + k] = val; + k++; + } + } + DEALLOCATE_STACK(small_positions, positions); } - tot_nargskw = tot_nargs + tot_nkwds; + else { + tot_nargskw = tot_nargs + tot_nkwds; - /* Allocate Stack */ - PyObject **stack, *small_stack[_PY_FASTCALL_SMALL_STACK]; - ALLOCATE_STACK(PyObject, tot_nargskw, small_stack, stack); + /* Allocate Stack */ + ALLOCATE_STACK(PyObject, tot_nargskw, small_stack, stack); + + /* Copy Pto Keywords to stack */ + memcpy(stack + tot_nargs, pto_kwvals, pto_nkwds * sizeof(PyObject*)); + DEALLOCATE_STACK(small_pto_kwvals, pto_kwvals); + + /* Copy New Keywords to stack */ + tot_kwnames = PyTuple_New(tot_nkwds); + for (Py_ssize_t i=0; i < pto_nkwds; i++) { + key = pto_kwkeys[i]; + PyTuple_SET_ITEM(tot_kwnames, i, key); + Py_INCREF(key); + } + DEALLOCATE_STACK(small_pto_kwkeys, pto_kwkeys); + } /* Copy Positionals to stack */ if (pto_phcount) { @@ -506,39 +557,6 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, memcpy(stack + pto_nargs, args, nargs * sizeof(PyObject*)); } - /* Copy Pto Keywords to stack */ - memcpy(stack + tot_nargs, pto_kwvals, pto_nkwds * sizeof(PyObject*)); - DEALLOCATE_STACK(small_pto_kwvals, pto_kwvals); - - /* Copy New Keywords to stack */ - PyObject *tot_kwnames = PyTuple_New(tot_nkwds); - for (Py_ssize_t i=0; i < pto_nkwds; i++) { - key = pto_kwkeys[i]; - PyTuple_SET_ITEM(tot_kwnames, i, key); - Py_INCREF(key); - } - DEALLOCATE_STACK(small_pto_kwkeys, pto_kwkeys); - - if (nkwds) { - Py_ssize_t k = 0; - Py_ssize_t j; - for (Py_ssize_t i=0; i < nkwds; i++) { - key = PyTuple_GET_ITEM(kwnames, i); - val = args[nargs + i]; - j = positions[i]; - if (j != -1) { - stack[tot_nargs + j] = val; - } - else { - PyTuple_SET_ITEM(tot_kwnames, pto_nkwds + k, key); - Py_INCREF(key); - stack[tot_nargs + pto_nkwds + k] = val; - k++; - } - } - DEALLOCATE_STACK(small_positions, positions); - } - /* Call / Maintenance / Return */ ret = _PyObject_VectorcallTstate(tstate, pto->fn, stack, tot_nargs, tot_kwnames); DEALLOCATE_STACK(small_stack, stack); From 64c889b3a87a0780c9eece930bf60c598316e0c8 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Sun, 29 Sep 2024 10:10:18 +0300 Subject: [PATCH 07/32] V5 stable --- Modules/_functoolsmodule.c | 230 ++++++++++++++----------------------- 1 file changed, 87 insertions(+), 143 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 05b14b00f9c94d..508d70099b8108 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -376,11 +376,15 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, return NULL; } + /* Total sizes */ + Py_ssize_t tot_nargs = pto_nargs + nargs - pto_phcount; + Py_ssize_t tot_nkwds; + Py_ssize_t tot_nargskw; + PyObject **pto_args = _PyTuple_ITEMS(pto->args); - PyObject *ret; /* Special cases */ - if (pto_nkwds == 0) { + if (!pto_nkwds) { /* Fast path if we're called without arguments */ if (nargskw == 0) { return _PyObject_VectorcallTstate(tstate, pto->fn, pto_args, @@ -394,175 +398,115 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, PyObject **newargs = (PyObject **)args - 1; PyObject *tmp = newargs[0]; newargs[0] = pto_args[0]; - ret = _PyObject_VectorcallTstate(tstate, pto->fn, newargs, - nargs + 1, kwnames); + PyObject *ret = _PyObject_VectorcallTstate(tstate, pto->fn, newargs, + nargs + 1, kwnames); newargs[0] = tmp; return ret; } - } - /* Size variables */ - Py_ssize_t tot_nargs = pto_nargs + nargs - pto_phcount; - Py_ssize_t tot_nkwds; - Py_ssize_t tot_nargskw; - - /* Divergence on whether pto has kwds */ - if (pto_nkwds == 0) { - tot_nkwds = nkwds; - tot_nargskw = tot_nargs + tot_nkwds; + tot_nkwds = pto_nkwds + nkwds; + tot_nargskw = tot_nargs + tot_nkwds; + PyObject **stack, *small_stack[_PY_FASTCALL_SMALL_STACK]; + PyObject *tot_kwnames = kwnames; + /* Initialize stack & copy keywords to stack */ + if (!pto_nkwds) { /* Allocate Stack */ - PyObject **stack, *small_stack[_PY_FASTCALL_SMALL_STACK]; + tot_nkwds = pto_nkwds + nkwds; + tot_nargskw = tot_nargs + tot_nkwds; ALLOCATE_STACK(PyObject, tot_nargskw, small_stack, stack); - /* Copy Positionals to new stack */ - if (pto_phcount) { - Py_ssize_t j = 0; // New args index - for (Py_ssize_t i = 0; i < pto_nargs; i++) { - if (pto_args[i] == pto->placeholder){ - stack[i] = args[j]; - j += 1; - } - else { - stack[i] = pto_args[i]; - } - } - assert(j == pto_phcount); - /* Add remaining args from new_args */ - if (nargs > pto_phcount) { - memcpy(stack + pto_nargs, args + j, (nargskw - j) * sizeof(PyObject*)); - } - } - else { - memcpy(stack, pto_args, pto_nargs * sizeof(PyObject*)); - memcpy(stack + pto_nargs, args, nargskw * sizeof(PyObject*)); + if (nkwds) { + /* if !pto_nkwds & nkwds, then simply append kw */ + memcpy(stack + tot_nargs, args + nargs, nkwds * sizeof(PyObject*)); } - - /* Call / Maintenance / Return */ - ret = _PyObject_VectorcallTstate(tstate, pto->fn, stack, tot_nargs, kwnames); - DEALLOCATE_STACK(small_stack, stack); - return ret; } else { PyObject *key, *val; - - PyObject **pto_kwkeys, *small_pto_kwkeys[_PY_FASTCALL_SMALL_STACK]; - PyObject **pto_kwvals, *small_pto_kwvals[_PY_FASTCALL_SMALL_STACK]; - ALLOCATE_STACK(PyObject, pto_nkwds, small_pto_kwkeys, pto_kwkeys); - ALLOCATE_STACK(PyObject, pto_nkwds, small_pto_kwvals, pto_kwvals); - Py_ssize_t pos = 0, i = 0; - while (PyDict_Next(pto->kw, &pos, &key, &val)) { - pto_kwkeys[i] = key; - pto_kwvals[i] = val; - i++; - } - - /* Calculate total kw size and positions of items to override */ - PyObject *tot_kwnames; - PyObject **stack, *small_stack[_PY_FASTCALL_SMALL_STACK]; - tot_nkwds = pto_nkwds + nkwds; - if (nkwds) { - Py_ssize_t *positions, small_positions[_PY_FASTCALL_SMALL_STACK]; - ALLOCATE_STACK(Py_ssize_t, nkwds, small_positions, positions); - for (Py_ssize_t i=0; i < nkwds; i++) { - positions[i] = -1; - key = PyTuple_GET_ITEM(kwnames, i); - if (PyDict_Contains(pto->kw, key)) { - tot_nkwds--; - for (Py_ssize_t j=0; j < pto_nkwds; j++) { - if (PyObject_RichCompareBool(key, pto_kwkeys[j], Py_EQ)) { - positions[i] = j; - break; - } - } + PyObject *pto_kw = NULL; // pto_kw with duplicates merged (if any) + + /* Temporary stack for keywords that are not in pto->kw */ + PyObject **kwtail, *small_kwtail[_PY_FASTCALL_SMALL_STACK * 2]; + ALLOCATE_STACK(PyObject, nkwds * 2, small_kwtail, kwtail); + + /* Merge kw to pto_kw or add to tail (if not duplicate) */ + Py_ssize_t n_tail = 0; + for (Py_ssize_t i = 0; i < nkwds; ++i) { + key = PyTuple_GET_ITEM(kwnames, i); + val = args[nargs + i]; + if (PyDict_Contains(pto->kw, key)) { + if (pto_kw == NULL) { + pto_kw = PyDict_Copy(pto->kw); } + PyDict_SetItem(pto_kw, key, val); } - tot_nargskw = tot_nargs + tot_nkwds; - - /* Allocate Stack */ - ALLOCATE_STACK(PyObject, tot_nargskw, small_stack, stack); - - /* Copy Pto Keywords to stack */ - memcpy(stack + tot_nargs, pto_kwvals, pto_nkwds * sizeof(PyObject*)); - DEALLOCATE_STACK(small_pto_kwvals, pto_kwvals); - - /* Copy New Keywords to stack */ - tot_kwnames = PyTuple_New(tot_nkwds); - for (Py_ssize_t i=0; i < pto_nkwds; i++) { - key = pto_kwkeys[i]; - PyTuple_SET_ITEM(tot_kwnames, i, key); - Py_INCREF(key); - } - DEALLOCATE_STACK(small_pto_kwkeys, pto_kwkeys); - - Py_ssize_t k = 0; - Py_ssize_t j; - for (Py_ssize_t i=0; i < nkwds; i++) { - key = PyTuple_GET_ITEM(kwnames, i); - val = args[nargs + i]; - j = positions[i]; - if (j != -1) { - stack[tot_nargs + j] = val; - } - else { - PyTuple_SET_ITEM(tot_kwnames, pto_nkwds + k, key); - Py_INCREF(key); - stack[tot_nargs + pto_nkwds + k] = val; - k++; - } + else { + kwtail[n_tail] = key; + kwtail[n_tail + nkwds] = val; + n_tail++; } - DEALLOCATE_STACK(small_positions, positions); } - else { - tot_nargskw = tot_nargs + tot_nkwds; - /* Allocate Stack */ - ALLOCATE_STACK(PyObject, tot_nargskw, small_stack, stack); - - /* Copy Pto Keywords to stack */ - memcpy(stack + tot_nargs, pto_kwvals, pto_nkwds * sizeof(PyObject*)); - DEALLOCATE_STACK(small_pto_kwvals, pto_kwvals); + /* Allocate Stack */ + Py_ssize_t n_merges = nkwds - n_tail; + tot_nkwds = pto_nkwds + nkwds - n_merges; + tot_nargskw = tot_nargs + tot_nkwds; + ALLOCATE_STACK(PyObject, tot_nargskw, small_stack, stack); + tot_kwnames = PyTuple_New(tot_nkwds); - /* Copy New Keywords to stack */ - tot_kwnames = PyTuple_New(tot_nkwds); - for (Py_ssize_t i=0; i < pto_nkwds; i++) { - key = pto_kwkeys[i]; - PyTuple_SET_ITEM(tot_kwnames, i, key); - Py_INCREF(key); - } - DEALLOCATE_STACK(small_pto_kwkeys, pto_kwkeys); + /* Copy pto_kw to stack */ + Py_ssize_t pos = 0, i = 0; + while (PyDict_Next(n_merges ? pto_kw : pto->kw, &pos, &key, &val)) { + PyTuple_SET_ITEM(tot_kwnames, i, key); + Py_INCREF(key); + stack[tot_nargs + i] = val; + i++; } + Py_XDECREF(pto_kw); + + /* Copy kw tail to stack */ + for (Py_ssize_t i = 0; i < n_tail; ++i) { + key = kwtail[i]; + val = kwtail[i + nkwds]; + PyTuple_SET_ITEM(tot_kwnames, pto_nkwds + i, key); + Py_INCREF(key); + stack[tot_nargs + pto_nkwds + i] = val; + } + DEALLOCATE_STACK(small_kwtail, kwtail); + } - /* Copy Positionals to stack */ - if (pto_phcount) { - Py_ssize_t j = 0; // New args index - for (Py_ssize_t i = 0; i < pto_nargs; i++) { - if (pto_args[i] == pto->placeholder){ - stack[i] = args[j]; - j += 1; - } - else { - stack[i] = pto_args[i]; - } + /* Copy Positionals to stack */ + if (pto_phcount) { + Py_ssize_t j = 0; // New args index + for (Py_ssize_t i = 0; i < pto_nargs; i++) { + if (pto_args[i] == pto->placeholder){ + stack[i] = args[j]; + j += 1; } - assert(j == pto_phcount); - /* Add remaining args from new_args */ - if (nargs > pto_phcount) { - memcpy(stack + pto_nargs, args + j, (nargs - j) * sizeof(PyObject*)); + else { + stack[i] = pto_args[i]; } } - else { - memcpy(stack, pto_args, pto_nargs * sizeof(PyObject*)); - memcpy(stack + pto_nargs, args, nargs * sizeof(PyObject*)); + assert(j == pto_phcount); + /* Add remaining args from new_args */ + if (nargs > pto_phcount) { + memcpy(stack + pto_nargs, args + j, (nargs - j) * sizeof(PyObject*)); } + } + else { + memcpy(stack, pto_args, pto_nargs * sizeof(PyObject*)); + memcpy(stack + pto_nargs, args, nargs * sizeof(PyObject*)); + } - /* Call / Maintenance / Return */ - ret = _PyObject_VectorcallTstate(tstate, pto->fn, stack, tot_nargs, tot_kwnames); - DEALLOCATE_STACK(small_stack, stack); + /* Call / Maintenance / Return */ + PyObject *ret = _PyObject_VectorcallTstate(tstate, pto->fn, stack, + tot_nargs, tot_kwnames); + DEALLOCATE_STACK(small_stack, stack); + if (pto_nkwds) { Py_DECREF(tot_kwnames); - return ret; } + return ret; } /* Set pto->vectorcall depending on the parameters of the partial object */ From a7142d57440ee003ff18397bb63135f22e3f13ae Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Tue, 1 Oct 2024 00:46:29 +0300 Subject: [PATCH 08/32] add commented fix if merging after gh-124652 --- Modules/_functoolsmodule.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 508d70099b8108..5c2a1576289de5 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -391,9 +391,19 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, pto_nargs, NULL); } + // TO BE ADDED ON MERGE TO MAIN + // IF https://github.com/python/cpython/pull/124788 + // IS MERGED BEFORE THIS + // /* Fast path if all Placeholders */ + // if (pto_nargs == pto_phcount) { + // /* NOTE: Without this, following single argument Fast Path + // * is incorrect */ + // return _PyObject_VectorcallTstate(tstate, pto->fn, + // args, nargs, kwnames); + // } + /* Use PY_VECTORCALL_ARGUMENTS_OFFSET to prepend a single - * positional argument. - * NOTE: Could add (Placeholder, arg) case in similar manner */ + * positional argument. */ if (pto_nargs == 1 && (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET)) { PyObject **newargs = (PyObject **)args - 1; PyObject *tmp = newargs[0]; From ba36d0155598694a08b6fe84402d86e44bc49273 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Fri, 4 Oct 2024 16:36:19 +0300 Subject: [PATCH 09/32] error check --- Modules/_functoolsmodule.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 5c2a1576289de5..659b14e9626b55 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -449,7 +449,10 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, if (pto_kw == NULL) { pto_kw = PyDict_Copy(pto->kw); } - PyDict_SetItem(pto_kw, key, val); + if (PyDict_SetItem(pto_kw, key, val)) { + DEALLOCATE_STACK(small_kwtail, kwtail); + return NULL; + } } else { kwtail[n_tail] = key; From acba26928853c506f6926f821da4a0f0e28403ae Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Fri, 4 Oct 2024 17:00:28 +0300 Subject: [PATCH 10/32] fix error check --- Modules/_functoolsmodule.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 659b14e9626b55..8c9d1ff44eeb4c 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -448,9 +448,14 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, if (PyDict_Contains(pto->kw, key)) { if (pto_kw == NULL) { pto_kw = PyDict_Copy(pto->kw); + if (pto_kw == NULL) { + DEALLOCATE_STACK(small_kwtail, kwtail); + return NULL; + } } if (PyDict_SetItem(pto_kw, key, val)) { DEALLOCATE_STACK(small_kwtail, kwtail); + Py_DECREF(pto_kw); return NULL; } } From 898a104065411bcc5e6a5bf167b60c8293a308a7 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Sat, 5 Oct 2024 16:16:28 +0300 Subject: [PATCH 11/32] minor macro edit --- Modules/_functoolsmodule.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 8c9d1ff44eeb4c..3489cb27a9b518 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -334,13 +334,13 @@ partial_descr_get(PyObject *self, PyObject *obj, PyObject *type) return PyMethod_New(self, obj); } -#define ALLOCATE_STACK(type, size, small_stack, stack) \ +#define ALLOCATE_STACK(elsize, size, small_stack, stack) \ do { \ if (size <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { \ stack = small_stack; \ } \ else { \ - stack = PyMem_Malloc(size * sizeof(type *)); \ + stack = PyMem_Malloc(size * elsize); \ if (stack == NULL) { \ PyErr_NoMemory(); \ return NULL; \ @@ -425,7 +425,7 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, /* Allocate Stack */ tot_nkwds = pto_nkwds + nkwds; tot_nargskw = tot_nargs + tot_nkwds; - ALLOCATE_STACK(PyObject, tot_nargskw, small_stack, stack); + ALLOCATE_STACK(sizeof(PyObject *), tot_nargskw, small_stack, stack); if (nkwds) { /* if !pto_nkwds & nkwds, then simply append kw */ @@ -438,7 +438,7 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, /* Temporary stack for keywords that are not in pto->kw */ PyObject **kwtail, *small_kwtail[_PY_FASTCALL_SMALL_STACK * 2]; - ALLOCATE_STACK(PyObject, nkwds * 2, small_kwtail, kwtail); + ALLOCATE_STACK(sizeof(PyObject *), nkwds * 2, small_kwtail, kwtail); /* Merge kw to pto_kw or add to tail (if not duplicate) */ Py_ssize_t n_tail = 0; @@ -470,7 +470,7 @@ partial_vectorcall(partialobject *pto, PyObject *const *args, Py_ssize_t n_merges = nkwds - n_tail; tot_nkwds = pto_nkwds + nkwds - n_merges; tot_nargskw = tot_nargs + tot_nkwds; - ALLOCATE_STACK(PyObject, tot_nargskw, small_stack, stack); + ALLOCATE_STACK(sizeof(PyObject *), tot_nargskw, small_stack, stack); tot_kwnames = PyTuple_New(tot_nkwds); /* Copy pto_kw to stack */ From 3647c25476aa7ee6ec96387a00c72471da58a824 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 01:12:24 +0000 Subject: [PATCH 12/32] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2024-10-17-01-12-22.gh-issue-119109.u4hcvb.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2024-10-17-01-12-22.gh-issue-119109.u4hcvb.rst diff --git a/Misc/NEWS.d/next/Library/2024-10-17-01-12-22.gh-issue-119109.u4hcvb.rst b/Misc/NEWS.d/next/Library/2024-10-17-01-12-22.gh-issue-119109.u4hcvb.rst new file mode 100644 index 00000000000000..782b2eb8d74c8e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-17-01-12-22.gh-issue-119109.u4hcvb.rst @@ -0,0 +1 @@ +:func:`functools.partial` calls are now faster when keyword arguments are used. From f9e3fd42b34d6d9c3e523c92326753262e8dbb0b Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Thu, 19 Dec 2024 00:57:09 +0200 Subject: [PATCH 13/32] small edits --- Modules/_functoolsmodule.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 8bf7fd2ff01193..99442b704c77b6 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -398,11 +398,6 @@ partial_vectorcall(PyObject *self, PyObject *const *args, return NULL; } - /* Total sizes */ - Py_ssize_t tot_nargs = pto_nargs + nargs - pto_phcount; - Py_ssize_t tot_nkwds; - Py_ssize_t tot_nargskw; - PyObject **pto_args = _PyTuple_ITEMS(pto->args); /* Special cases */ @@ -437,16 +432,17 @@ partial_vectorcall(PyObject *self, PyObject *const *args, } } - tot_nkwds = pto_nkwds + nkwds; - tot_nargskw = tot_nargs + tot_nkwds; + /* Total sizes */ + Py_ssize_t tot_nargs = pto_nargs + nargs - pto_phcount; + Py_ssize_t tot_nkwds = pto_nkwds + nkwds; + Py_ssize_t tot_nargskw = tot_nargs + tot_nkwds; + PyObject **stack, *small_stack[_PY_FASTCALL_SMALL_STACK]; PyObject *tot_kwnames = kwnames; /* Initialize stack & copy keywords to stack */ if (!pto_nkwds) { /* Allocate Stack */ - tot_nkwds = pto_nkwds + nkwds; - tot_nargskw = tot_nargs + tot_nkwds; ALLOCATE_STACK(sizeof(PyObject *), tot_nargskw, small_stack, stack); if (nkwds) { From 42f3dc7567bcf305986cd97a9a4ba2d3ea6a59a3 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Sun, 5 Jan 2025 10:30:58 +0200 Subject: [PATCH 14/32] idiomatic C --- Modules/_functoolsmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 99442b704c77b6..9abefa39caf167 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -471,7 +471,7 @@ partial_vectorcall(PyObject *self, PyObject *const *args, return NULL; } } - if (PyDict_SetItem(pto_kw, key, val)) { + if (PyDict_SetItem(pto_kw, key, val) < 0) { DEALLOCATE_STACK(small_kwtail, kwtail); Py_DECREF(pto_kw); return NULL; From acd9c565a574d1fa55d58f174cddbd7eec7ee0ca Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Sun, 5 Jan 2025 14:49:23 +0200 Subject: [PATCH 15/32] small edits and fixes --- Modules/_functoolsmodule.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 796d03e0a74e0b..d7fa15d03f8f86 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -361,11 +361,8 @@ partial_descr_get(PyObject *self, PyObject *obj, PyObject *type) stack = small_stack; \ } \ else { \ + /* NOTE, size * elsize in theory could overflow */ \ stack = PyMem_Malloc(size * elsize); \ - if (stack == NULL) { \ - PyErr_NoMemory(); \ - return NULL; \ - } \ } \ } while (0) @@ -438,12 +435,17 @@ partial_vectorcall(PyObject *self, PyObject *const *args, Py_ssize_t tot_nargskw = tot_nargs + tot_nkwds; PyObject **stack, *small_stack[_PY_FASTCALL_SMALL_STACK]; - PyObject *tot_kwnames = kwnames; + PyObject *tot_kwnames; /* Initialize stack & copy keywords to stack */ if (!pto_nkwds) { /* Allocate Stack */ ALLOCATE_STACK(sizeof(PyObject *), tot_nargskw, small_stack, stack); + if (stack == NULL) { + PyErr_NoMemory(); + return NULL; + } + tot_kwnames = kwnames; if (nkwds) { /* if !pto_nkwds & nkwds, then simply append kw */ @@ -457,6 +459,10 @@ partial_vectorcall(PyObject *self, PyObject *const *args, /* Temporary stack for keywords that are not in pto->kw */ PyObject **kwtail, *small_kwtail[_PY_FASTCALL_SMALL_STACK * 2]; ALLOCATE_STACK(sizeof(PyObject *), nkwds * 2, small_kwtail, kwtail); + if (kwtail == NULL) { + PyErr_NoMemory(); + return NULL; + } /* Merge kw to pto_kw or add to tail (if not duplicate) */ Py_ssize_t n_tail = 0; @@ -489,6 +495,11 @@ partial_vectorcall(PyObject *self, PyObject *const *args, tot_nkwds = pto_nkwds + nkwds - n_merges; tot_nargskw = tot_nargs + tot_nkwds; ALLOCATE_STACK(sizeof(PyObject *), tot_nargskw, small_stack, stack); + if (stack == NULL) { + Py_XDECREF(pto_kw); + PyErr_NoMemory(); + return NULL; + } tot_kwnames = PyTuple_New(tot_nkwds); /* Copy pto_kw to stack */ @@ -545,6 +556,9 @@ partial_vectorcall(PyObject *self, PyObject *const *args, return ret; } +#undef ALLOCATE_STACK +#undef DEALLOCATE_STACK + /* Set pto->vectorcall depending on the parameters of the partial object */ static void partial_setvectorcall(partialobject *pto) @@ -561,6 +575,7 @@ partial_setvectorcall(partialobject *pto) } } + // Not converted to argument clinic, because of `*args, **kwargs` arguments. static PyObject * partial_call(PyObject *self, PyObject *args, PyObject *kwargs) From cc557e9751e6c68b5268fc43e50a2fe632aa40c5 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Sun, 5 Jan 2025 16:33:42 +0200 Subject: [PATCH 16/32] macros removed, post-resizing instead --- Modules/_functoolsmodule.c | 127 ++++++++++++++++--------------------- 1 file changed, 56 insertions(+), 71 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index d7fa15d03f8f86..c8ed5d6158eadb 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -355,24 +355,6 @@ partial_descr_get(PyObject *self, PyObject *obj, PyObject *type) return PyMethod_New(self, obj); } -#define ALLOCATE_STACK(elsize, size, small_stack, stack) \ - do { \ - if (size <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { \ - stack = small_stack; \ - } \ - else { \ - /* NOTE, size * elsize in theory could overflow */ \ - stack = PyMem_Malloc(size * elsize); \ - } \ - } while (0) - -#define DEALLOCATE_STACK(small_stack, stack) \ - do { \ - if (stack != small_stack) { \ - PyMem_Free(stack); \ - } \ - } while (0) - static PyObject * partial_vectorcall(PyObject *self, PyObject *const *args, size_t nargsf, PyObject *kwnames) @@ -434,17 +416,25 @@ partial_vectorcall(PyObject *self, PyObject *const *args, Py_ssize_t tot_nkwds = pto_nkwds + nkwds; Py_ssize_t tot_nargskw = tot_nargs + tot_nkwds; - PyObject **stack, *small_stack[_PY_FASTCALL_SMALL_STACK]; - PyObject *tot_kwnames; - - /* Initialize stack & copy keywords to stack */ - if (!pto_nkwds) { - /* Allocate Stack */ - ALLOCATE_STACK(sizeof(PyObject *), tot_nargskw, small_stack, stack); + /* Allocate Stack */ + PyObject **tmp_stack, **stack, *small_stack[_PY_FASTCALL_SMALL_STACK]; + if (tot_nargskw <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { + stack = small_stack; + } + else { + /* NOTE, size * elsize could overflow */ + stack = PyMem_Malloc(tot_nargskw * sizeof(PyObject *)); if (stack == NULL) { PyErr_NoMemory(); return NULL; } + } + + PyObject *pto_kw_merged = NULL; // pto_kw with duplicates merged (if any) + PyObject *tot_kwnames; + + /* Copy keywords to stack */ + if (!pto_nkwds) { tot_kwnames = kwnames; if (nkwds) { @@ -453,16 +443,8 @@ partial_vectorcall(PyObject *self, PyObject *const *args, } } else { + tot_kwnames = PyTuple_New(tot_nkwds); PyObject *key, *val; - PyObject *pto_kw = NULL; // pto_kw with duplicates merged (if any) - - /* Temporary stack for keywords that are not in pto->kw */ - PyObject **kwtail, *small_kwtail[_PY_FASTCALL_SMALL_STACK * 2]; - ALLOCATE_STACK(sizeof(PyObject *), nkwds * 2, small_kwtail, kwtail); - if (kwtail == NULL) { - PyErr_NoMemory(); - return NULL; - } /* Merge kw to pto_kw or add to tail (if not duplicate) */ Py_ssize_t n_tail = 0; @@ -470,57 +452,50 @@ partial_vectorcall(PyObject *self, PyObject *const *args, key = PyTuple_GET_ITEM(kwnames, i); val = args[nargs + i]; if (PyDict_Contains(pto->kw, key)) { - if (pto_kw == NULL) { - pto_kw = PyDict_Copy(pto->kw); - if (pto_kw == NULL) { - DEALLOCATE_STACK(small_kwtail, kwtail); - return NULL; + if (pto_kw_merged == NULL) { + pto_kw_merged = PyDict_Copy(pto->kw); + if (pto_kw_merged == NULL) { + goto error_2; } } - if (PyDict_SetItem(pto_kw, key, val) < 0) { - DEALLOCATE_STACK(small_kwtail, kwtail); - Py_DECREF(pto_kw); - return NULL; + if (PyDict_SetItem(pto_kw_merged, key, val) < 0) { + goto error_1; } } else { - kwtail[n_tail] = key; - kwtail[n_tail + nkwds] = val; + /* Copy keyword tail to stack */ + PyTuple_SET_ITEM(tot_kwnames, pto_nkwds + n_tail, key); + Py_INCREF(key); + stack[tot_nargs + pto_nkwds + n_tail] = val; n_tail++; } } - /* Allocate Stack */ + /* Resize Stack and tot_kwnames */ Py_ssize_t n_merges = nkwds - n_tail; - tot_nkwds = pto_nkwds + nkwds - n_merges; - tot_nargskw = tot_nargs + tot_nkwds; - ALLOCATE_STACK(sizeof(PyObject *), tot_nargskw, small_stack, stack); - if (stack == NULL) { - Py_XDECREF(pto_kw); - PyErr_NoMemory(); - return NULL; + if (n_merges) { + if (stack != small_stack) { + tmp_stack = PyMem_Realloc(stack, (tot_nargskw - n_merges) * sizeof(PyObject *)); + if (tmp_stack == NULL) { + PyErr_NoMemory(); + goto error_1; + } + stack = tmp_stack; + } + if (_PyTuple_Resize(&tot_kwnames, (tot_nkwds - n_merges)) != 0) { + goto error_1; + } } - tot_kwnames = PyTuple_New(tot_nkwds); - /* Copy pto_kw to stack */ + /* Copy pto_kw_merged to stack */ Py_ssize_t pos = 0, i = 0; - while (PyDict_Next(n_merges ? pto_kw : pto->kw, &pos, &key, &val)) { + while (PyDict_Next(n_merges ? pto_kw_merged : pto->kw, &pos, &key, &val)) { PyTuple_SET_ITEM(tot_kwnames, i, key); Py_INCREF(key); stack[tot_nargs + i] = val; i++; } - Py_XDECREF(pto_kw); - - /* Copy kw tail to stack */ - for (Py_ssize_t i = 0; i < n_tail; ++i) { - key = kwtail[i]; - val = kwtail[i + nkwds]; - PyTuple_SET_ITEM(tot_kwnames, pto_nkwds + i, key); - Py_INCREF(key); - stack[tot_nargs + pto_nkwds + i] = val; - } - DEALLOCATE_STACK(small_kwtail, kwtail); + Py_XDECREF(pto_kw_merged); } /* Copy Positionals to stack */ @@ -549,15 +524,25 @@ partial_vectorcall(PyObject *self, PyObject *const *args, /* Call / Maintenance / Return */ PyObject *ret = _PyObject_VectorcallTstate(tstate, pto->fn, stack, tot_nargs, tot_kwnames); - DEALLOCATE_STACK(small_stack, stack); + if (stack != small_stack) { + PyMem_Free(stack); + } if (pto_nkwds) { Py_DECREF(tot_kwnames); } return ret; -} -#undef ALLOCATE_STACK -#undef DEALLOCATE_STACK +error_1: + Py_XDECREF(pto_kw_merged); +error_2: + if (stack != small_stack) { + PyMem_Free(stack); + } + if (pto_nkwds) { + Py_DECREF(tot_kwnames); + } + return NULL; +} /* Set pto->vectorcall depending on the parameters of the partial object */ static void From e8fbaf8f37989cb88040a11992aa3feb7acfbb6b Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Mon, 6 Jan 2025 13:29:56 +0200 Subject: [PATCH 17/32] regain previous performance --- Modules/_functoolsmodule.c | 60 +++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index c8ed5d6158eadb..807d069bbe505b 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -416,34 +416,39 @@ partial_vectorcall(PyObject *self, PyObject *const *args, Py_ssize_t tot_nkwds = pto_nkwds + nkwds; Py_ssize_t tot_nargskw = tot_nargs + tot_nkwds; + PyObject *pto_kw_merged = NULL; // pto_kw with duplicates merged (if any) + PyObject *tot_kwnames; + /* Allocate Stack */ PyObject **tmp_stack, **stack, *small_stack[_PY_FASTCALL_SMALL_STACK]; - if (tot_nargskw <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { + Py_ssize_t init_stack_size = tot_nargskw; + if (pto_nkwds) { + // If pto_nkwds, allocate additional space for temporary new keys + init_stack_size += nkwds; + } + if (init_stack_size <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { stack = small_stack; } else { - /* NOTE, size * elsize could overflow */ - stack = PyMem_Malloc(tot_nargskw * sizeof(PyObject *)); + /* NOTE, in theory size * elsize could overflow */ + stack = PyMem_Malloc(init_stack_size * sizeof(PyObject *)); if (stack == NULL) { PyErr_NoMemory(); return NULL; } } - PyObject *pto_kw_merged = NULL; // pto_kw with duplicates merged (if any) - PyObject *tot_kwnames; - /* Copy keywords to stack */ if (!pto_nkwds) { tot_kwnames = kwnames; - if (nkwds) { /* if !pto_nkwds & nkwds, then simply append kw */ memcpy(stack + tot_nargs, args + nargs, nkwds * sizeof(PyObject*)); } } else { - tot_kwnames = PyTuple_New(tot_nkwds); + /* stack is now [, , , ] + * Will truncate to [, ] */ PyObject *key, *val; /* Merge kw to pto_kw or add to tail (if not duplicate) */ @@ -464,27 +469,19 @@ partial_vectorcall(PyObject *self, PyObject *const *args, } else { /* Copy keyword tail to stack */ - PyTuple_SET_ITEM(tot_kwnames, pto_nkwds + n_tail, key); - Py_INCREF(key); stack[tot_nargs + pto_nkwds + n_tail] = val; + stack[tot_nargskw + n_tail] = key; n_tail++; } } - - /* Resize Stack and tot_kwnames */ Py_ssize_t n_merges = nkwds - n_tail; - if (n_merges) { - if (stack != small_stack) { - tmp_stack = PyMem_Realloc(stack, (tot_nargskw - n_merges) * sizeof(PyObject *)); - if (tmp_stack == NULL) { - PyErr_NoMemory(); - goto error_1; - } - stack = tmp_stack; - } - if (_PyTuple_Resize(&tot_kwnames, (tot_nkwds - n_merges)) != 0) { - goto error_1; - } + + /* Create tot_kwnames */ + tot_kwnames = PyTuple_New(pto_nkwds + n_tail); + for (Py_ssize_t i = 0; i < n_tail; ++i) { + key = stack[tot_nargskw + i]; + PyTuple_SET_ITEM(tot_kwnames, pto_nkwds + i, key); + Py_INCREF(key); } /* Copy pto_kw_merged to stack */ @@ -496,6 +493,16 @@ partial_vectorcall(PyObject *self, PyObject *const *args, i++; } Py_XDECREF(pto_kw_merged); + + /* Resize Stack */ + if (n_merges && stack != small_stack) { + tmp_stack = PyMem_Realloc(stack, (tot_nargskw - n_merges) * sizeof(PyObject *)); + if (tmp_stack == NULL) { + PyErr_NoMemory(); + goto error_0; + } + stack = tmp_stack; + } } /* Copy Positionals to stack */ @@ -532,15 +539,14 @@ partial_vectorcall(PyObject *self, PyObject *const *args, } return ret; +error_0: + Py_DECREF(tot_kwnames); error_1: Py_XDECREF(pto_kw_merged); error_2: if (stack != small_stack) { PyMem_Free(stack); } - if (pto_nkwds) { - Py_DECREF(tot_kwnames); - } return NULL; } From 1a8a56c95c46392ba0f6bd1e7c9a60f6a08debd2 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Mon, 6 Jan 2025 13:32:41 +0200 Subject: [PATCH 18/32] small edit --- Modules/_functoolsmodule.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 807d069bbe505b..301be3eb6306b3 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -492,7 +492,6 @@ partial_vectorcall(PyObject *self, PyObject *const *args, stack[tot_nargs + i] = val; i++; } - Py_XDECREF(pto_kw_merged); /* Resize Stack */ if (n_merges && stack != small_stack) { @@ -503,6 +502,8 @@ partial_vectorcall(PyObject *self, PyObject *const *args, } stack = tmp_stack; } + + Py_XDECREF(pto_kw_merged); } /* Copy Positionals to stack */ From b3ff73d33b9748b90bd6c43ea454d23085f3e01b Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Mon, 6 Jan 2025 14:04:40 +0200 Subject: [PATCH 19/32] labels removed --- Modules/_functoolsmodule.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 301be3eb6306b3..a4aaf5e27b870d 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -460,11 +460,18 @@ partial_vectorcall(PyObject *self, PyObject *const *args, if (pto_kw_merged == NULL) { pto_kw_merged = PyDict_Copy(pto->kw); if (pto_kw_merged == NULL) { - goto error_2; + if (stack != small_stack) { + PyMem_Free(stack); + } + return NULL; } } if (PyDict_SetItem(pto_kw_merged, key, val) < 0) { - goto error_1; + Py_XDECREF(pto_kw_merged); + if (stack != small_stack) { + PyMem_Free(stack); + } + return NULL; } } else { @@ -497,8 +504,13 @@ partial_vectorcall(PyObject *self, PyObject *const *args, if (n_merges && stack != small_stack) { tmp_stack = PyMem_Realloc(stack, (tot_nargskw - n_merges) * sizeof(PyObject *)); if (tmp_stack == NULL) { + Py_DECREF(tot_kwnames); + Py_XDECREF(pto_kw_merged); + if (stack != small_stack) { + PyMem_Free(stack); + } PyErr_NoMemory(); - goto error_0; + return NULL; } stack = tmp_stack; } @@ -539,16 +551,6 @@ partial_vectorcall(PyObject *self, PyObject *const *args, Py_DECREF(tot_kwnames); } return ret; - -error_0: - Py_DECREF(tot_kwnames); -error_1: - Py_XDECREF(pto_kw_merged); -error_2: - if (stack != small_stack) { - PyMem_Free(stack); - } - return NULL; } /* Set pto->vectorcall depending on the parameters of the partial object */ From 00ebb4b5b4d26d81425d5f4203589ae97549ff75 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Mon, 6 Jan 2025 15:05:54 +0200 Subject: [PATCH 20/32] comment edits --- Modules/_functoolsmodule.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index a4aaf5e27b870d..6f0477ff9b547f 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -361,7 +361,7 @@ partial_vectorcall(PyObject *self, PyObject *const *args, { partialobject *pto = _PyPartialObject_CAST(self);; PyThreadState *tstate = _PyThreadState_GET(); - /* Sizes */ + Py_ssize_t pto_nargs = PyTuple_GET_SIZE(pto->args); Py_ssize_t pto_nkwds = PyDict_GET_SIZE(pto->kw); Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); @@ -447,8 +447,8 @@ partial_vectorcall(PyObject *self, PyObject *const *args, } } else { - /* stack is now [, , , ] - * Will truncate to [, ] */ + /* stack is now [, , , ] + * Will resize later to [, ] */ PyObject *key, *val; /* Merge kw to pto_kw or add to tail (if not duplicate) */ @@ -541,7 +541,6 @@ partial_vectorcall(PyObject *self, PyObject *const *args, memcpy(stack + pto_nargs, args, nargs * sizeof(PyObject*)); } - /* Call / Maintenance / Return */ PyObject *ret = _PyObject_VectorcallTstate(tstate, pto->fn, stack, tot_nargs, tot_kwnames); if (stack != small_stack) { From 4575b6c1171b1b0ffd2171b642ff0ab96a333564 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Mon, 6 Jan 2025 15:10:55 +0200 Subject: [PATCH 21/32] minor fixes and improvements --- Modules/_functoolsmodule.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 6f0477ff9b547f..10de3784514864 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -433,8 +433,7 @@ partial_vectorcall(PyObject *self, PyObject *const *args, /* NOTE, in theory size * elsize could overflow */ stack = PyMem_Malloc(init_stack_size * sizeof(PyObject *)); if (stack == NULL) { - PyErr_NoMemory(); - return NULL; + return PyErr_NoMemory(); } } @@ -467,7 +466,7 @@ partial_vectorcall(PyObject *self, PyObject *const *args, } } if (PyDict_SetItem(pto_kw_merged, key, val) < 0) { - Py_XDECREF(pto_kw_merged); + Py_DECREF(pto_kw_merged); if (stack != small_stack) { PyMem_Free(stack); } @@ -486,16 +485,14 @@ partial_vectorcall(PyObject *self, PyObject *const *args, /* Create tot_kwnames */ tot_kwnames = PyTuple_New(pto_nkwds + n_tail); for (Py_ssize_t i = 0; i < n_tail; ++i) { - key = stack[tot_nargskw + i]; + key = Py_NewRef(stack[tot_nargskw + i]); PyTuple_SET_ITEM(tot_kwnames, pto_nkwds + i, key); - Py_INCREF(key); } /* Copy pto_kw_merged to stack */ Py_ssize_t pos = 0, i = 0; while (PyDict_Next(n_merges ? pto_kw_merged : pto->kw, &pos, &key, &val)) { - PyTuple_SET_ITEM(tot_kwnames, i, key); - Py_INCREF(key); + PyTuple_SET_ITEM(tot_kwnames, i, Py_NewRef(key)); stack[tot_nargs + i] = val; i++; } @@ -509,8 +506,7 @@ partial_vectorcall(PyObject *self, PyObject *const *args, if (stack != small_stack) { PyMem_Free(stack); } - PyErr_NoMemory(); - return NULL; + return PyErr_NoMemory(); } stack = tmp_stack; } From 25a91aa98c1fc6d89077773b9d810f5a1c234e37 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Wed, 8 Jan 2025 17:21:17 +0200 Subject: [PATCH 22/32] small stack size doubled + small edits --- Modules/_functoolsmodule.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 10de3784514864..8b07154419a14c 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -419,8 +419,13 @@ partial_vectorcall(PyObject *self, PyObject *const *args, PyObject *pto_kw_merged = NULL; // pto_kw with duplicates merged (if any) PyObject *tot_kwnames; - /* Allocate Stack */ - PyObject **tmp_stack, **stack, *small_stack[_PY_FASTCALL_SMALL_STACK]; + /* Allocate Stack + * Note, _PY_FASTCALL_SMALL_STACK is optimal for positional only + * This case might have keyword arguments + * furthermore, it might use extra stack space for temporary key storage + * thus, double small_stack size is used, which is 10 * 8 = 80 bytes */ + PyObject *small_stack[_PY_FASTCALL_SMALL_STACK * 2]; + PyObject **tmp_stack, **stack; Py_ssize_t init_stack_size = tot_nargskw; if (pto_nkwds) { // If pto_nkwds, allocate additional space for temporary new keys @@ -430,7 +435,6 @@ partial_vectorcall(PyObject *self, PyObject *const *args, stack = small_stack; } else { - /* NOTE, in theory size * elsize could overflow */ stack = PyMem_Malloc(init_stack_size * sizeof(PyObject *)); if (stack == NULL) { return PyErr_NoMemory(); @@ -482,27 +486,27 @@ partial_vectorcall(PyObject *self, PyObject *const *args, } Py_ssize_t n_merges = nkwds - n_tail; - /* Create tot_kwnames */ - tot_kwnames = PyTuple_New(pto_nkwds + n_tail); + /* Create total kwnames */ + tot_kwnames = PyTuple_New(tot_nkwds - n_merges); for (Py_ssize_t i = 0; i < n_tail; ++i) { key = Py_NewRef(stack[tot_nargskw + i]); PyTuple_SET_ITEM(tot_kwnames, pto_nkwds + i, key); } - /* Copy pto_kw_merged to stack */ + /* Copy pto_keywords with overlapping call keywords merged */ Py_ssize_t pos = 0, i = 0; while (PyDict_Next(n_merges ? pto_kw_merged : pto->kw, &pos, &key, &val)) { PyTuple_SET_ITEM(tot_kwnames, i, Py_NewRef(key)); stack[tot_nargs + i] = val; i++; } + Py_XDECREF(pto_kw_merged); - /* Resize Stack */ - if (n_merges && stack != small_stack) { + /* Resize Stack if the call has keywords */ + if (nkwds && stack != small_stack) { tmp_stack = PyMem_Realloc(stack, (tot_nargskw - n_merges) * sizeof(PyObject *)); if (tmp_stack == NULL) { Py_DECREF(tot_kwnames); - Py_XDECREF(pto_kw_merged); if (stack != small_stack) { PyMem_Free(stack); } @@ -510,8 +514,6 @@ partial_vectorcall(PyObject *self, PyObject *const *args, } stack = tmp_stack; } - - Py_XDECREF(pto_kw_merged); } /* Copy Positionals to stack */ From e326fcf946f59b4b1b1a556d51f8e34d486d8a95 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Fri, 10 Jan 2025 00:59:36 +0200 Subject: [PATCH 23/32] removed commented fix when trailing placeholders allowed --- Modules/_functoolsmodule.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 8b07154419a14c..96266ca274e945 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -387,17 +387,6 @@ partial_vectorcall(PyObject *self, PyObject *const *args, pto_nargs, NULL); } - // TO BE ADDED ON MERGE TO MAIN - // IF https://github.com/python/cpython/pull/124788 - // IS MERGED BEFORE THIS - // /* Fast path if all Placeholders */ - // if (pto_nargs == pto_phcount) { - // /* NOTE: Without this, following single argument Fast Path - // * is incorrect */ - // return _PyObject_VectorcallTstate(tstate, pto->fn, - // args, nargs, kwnames); - // } - /* Use PY_VECTORCALL_ARGUMENTS_OFFSET to prepend a single * positional argument. */ if (pto_nargs == 1 && (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET)) { From 85d658fe536f42f985ee8d1a1bab8c9dd718aa33 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Fri, 10 Jan 2025 10:46:10 +0200 Subject: [PATCH 24/32] moved declarations to more sensible place --- Modules/_functoolsmodule.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 96266ca274e945..f05e8bcc681e91 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -361,12 +361,7 @@ partial_vectorcall(PyObject *self, PyObject *const *args, { partialobject *pto = _PyPartialObject_CAST(self);; PyThreadState *tstate = _PyThreadState_GET(); - - Py_ssize_t pto_nargs = PyTuple_GET_SIZE(pto->args); - Py_ssize_t pto_nkwds = PyDict_GET_SIZE(pto->kw); Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); - Py_ssize_t nkwds = kwnames == NULL ? 0 : PyTuple_GET_SIZE(kwnames); - Py_ssize_t nargskw = nargs + nkwds; /* Placeholder check */ Py_ssize_t pto_phcount = pto->phcount; @@ -377,6 +372,10 @@ partial_vectorcall(PyObject *self, PyObject *const *args, return NULL; } + Py_ssize_t nkwds = kwnames == NULL ? 0 : PyTuple_GET_SIZE(kwnames); + Py_ssize_t nargskw = nargs + nkwds; + Py_ssize_t pto_nargs = PyTuple_GET_SIZE(pto->args); + Py_ssize_t pto_nkwds = PyDict_GET_SIZE(pto->kw); PyObject **pto_args = _PyTuple_ITEMS(pto->args); /* Special cases */ From cefa7d8953c1ed6a367beb58dd08b94663bb6ae5 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Fri, 10 Jan 2025 10:48:05 +0200 Subject: [PATCH 25/32] reorder declarations --- Modules/_functoolsmodule.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index f05e8bcc681e91..698ad9798cdc5c 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -372,11 +372,11 @@ partial_vectorcall(PyObject *self, PyObject *const *args, return NULL; } - Py_ssize_t nkwds = kwnames == NULL ? 0 : PyTuple_GET_SIZE(kwnames); - Py_ssize_t nargskw = nargs + nkwds; + PyObject **pto_args = _PyTuple_ITEMS(pto->args); Py_ssize_t pto_nargs = PyTuple_GET_SIZE(pto->args); Py_ssize_t pto_nkwds = PyDict_GET_SIZE(pto->kw); - PyObject **pto_args = _PyTuple_ITEMS(pto->args); + Py_ssize_t nkwds = kwnames == NULL ? 0 : PyTuple_GET_SIZE(kwnames); + Py_ssize_t nargskw = nargs + nkwds; /* Special cases */ if (!pto_nkwds) { From adacecf43dcac1ffdf4ac16984d2af87d32e1b88 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Tue, 10 Jun 2025 17:00:29 +0300 Subject: [PATCH 26/32] null fix --- Modules/_functoolsmodule.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index d810aa8d55a55d..3f828ccc5f90f8 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -489,6 +489,12 @@ partial_vectorcall(PyObject *self, PyObject *const *args, /* Create total kwnames */ tot_kwnames = PyTuple_New(tot_nkwds - n_merges); + if (tot_kwnames == NULL) { + if (stack != small_stack) { + PyMem_Free(stack); + } + return NULL; + } for (Py_ssize_t i = 0; i < n_tail; ++i) { key = Py_NewRef(stack[tot_nargskw + i]); PyTuple_SET_ITEM(tot_kwnames, pto_nkwds + i, key); From 192d261827dd6246f6ba37b7a5da6188f4a4510a Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Mon, 7 Jul 2025 18:26:37 +0300 Subject: [PATCH 27/32] few more brushes based on ss review --- Modules/_functoolsmodule.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index b270c2b1dc885d..b3dccd24eddba9 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -463,18 +463,12 @@ partial_vectorcall(PyObject *self, PyObject *const *args, if (pto_kw_merged == NULL) { pto_kw_merged = PyDict_Copy(pto->kw); if (pto_kw_merged == NULL) { - if (stack != small_stack) { - PyMem_Free(stack); - } - return NULL; + goto error; } } if (PyDict_SetItem(pto_kw_merged, key, val) < 0) { Py_DECREF(pto_kw_merged); - if (stack != small_stack) { - PyMem_Free(stack); - } - return NULL; + goto error; } } else { @@ -489,10 +483,8 @@ partial_vectorcall(PyObject *self, PyObject *const *args, /* Create total kwnames */ tot_kwnames = PyTuple_New(tot_nkwds - n_merges); if (tot_kwnames == NULL) { - if (stack != small_stack) { - PyMem_Free(stack); - } - return NULL; + Py_XDECREF(pto_kw_merged); + goto error; } for (Py_ssize_t i = 0; i < n_tail; ++i) { key = Py_NewRef(stack[tot_nargskw + i]); @@ -502,14 +494,17 @@ partial_vectorcall(PyObject *self, PyObject *const *args, /* Copy pto_keywords with overlapping call keywords merged */ Py_ssize_t pos = 0, i = 0; while (PyDict_Next(n_merges ? pto_kw_merged : pto->kw, &pos, &key, &val)) { + assert(i < tot_nkwds - n_merges); PyTuple_SET_ITEM(tot_kwnames, i, Py_NewRef(key)); stack[tot_nargs + i] = val; i++; } + assert(i == tot_nkwds - n_merges); Py_XDECREF(pto_kw_merged); - /* Resize Stack if the call has keywords */ - if (nkwds && stack != small_stack) { + /* Resize Stack if the call has keywords + * Only resize if nkwds > 6 (1% of github use cases have 7 or more kwds) */ + if (nkwds > 6 && stack != small_stack) { tmp_stack = PyMem_Realloc(stack, (tot_nargskw - n_merges) * sizeof(PyObject *)); if (tmp_stack == NULL) { Py_DECREF(tot_kwnames); @@ -526,7 +521,7 @@ partial_vectorcall(PyObject *self, PyObject *const *args, if (pto_phcount) { Py_ssize_t j = 0; // New args index for (Py_ssize_t i = 0; i < pto_nargs; i++) { - if (pto_args[i] == pto->placeholder){ + if (pto_args[i] == pto->placeholder) { stack[i] = args[j]; j += 1; } @@ -554,6 +549,12 @@ partial_vectorcall(PyObject *self, PyObject *const *args, Py_DECREF(tot_kwnames); } return ret; + + error: + if (stack != small_stack) { + PyMem_Free(stack); + } + return NULL; } /* Set pto->vectorcall depending on the parameters of the partial object */ From 1f21e7403cb09e32769de1d19d415685585bcc4e Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Mon, 7 Jul 2025 18:27:20 +0300 Subject: [PATCH 28/32] comment --- Modules/_functoolsmodule.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index b3dccd24eddba9..19a2b4cec31e1d 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -502,8 +502,8 @@ partial_vectorcall(PyObject *self, PyObject *const *args, assert(i == tot_nkwds - n_merges); Py_XDECREF(pto_kw_merged); - /* Resize Stack if the call has keywords - * Only resize if nkwds > 6 (1% of github use cases have 7 or more kwds) */ + /* Resize Stack if the call has more than 6 keywords + * (1% of github use cases have 7 or more kwds) */ if (nkwds > 6 && stack != small_stack) { tmp_stack = PyMem_Realloc(stack, (tot_nargskw - n_merges) * sizeof(PyObject *)); if (tmp_stack == NULL) { From 3070c676305a3a0c21190d9e8507fdfb6a4111cb Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Mon, 7 Jul 2025 19:05:44 +0300 Subject: [PATCH 29/32] assertion fixes --- Modules/_functoolsmodule.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 19a2b4cec31e1d..f7c7e1885ca532 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -491,15 +491,16 @@ partial_vectorcall(PyObject *self, PyObject *const *args, PyTuple_SET_ITEM(tot_kwnames, pto_nkwds + i, key); } - /* Copy pto_keywords with overlapping call keywords merged */ + /* Copy pto_keywords with overlapping call keywords merged + * Note, tail is already coppied. */ Py_ssize_t pos = 0, i = 0; while (PyDict_Next(n_merges ? pto_kw_merged : pto->kw, &pos, &key, &val)) { - assert(i < tot_nkwds - n_merges); + assert(i < pto_nkwds); PyTuple_SET_ITEM(tot_kwnames, i, Py_NewRef(key)); stack[tot_nargs + i] = val; i++; } - assert(i == tot_nkwds - n_merges); + assert(i == pto_nkwds); Py_XDECREF(pto_kw_merged); /* Resize Stack if the call has more than 6 keywords From 2a56327163ab1c6dd83a131f2b8ec53a8fde367b Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Mon, 7 Jul 2025 19:54:24 +0300 Subject: [PATCH 30/32] stack resize rule --- Modules/_functoolsmodule.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index f7c7e1885ca532..d0ef6e8df91d56 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -504,8 +504,9 @@ partial_vectorcall(PyObject *self, PyObject *const *args, Py_XDECREF(pto_kw_merged); /* Resize Stack if the call has more than 6 keywords - * (1% of github use cases have 7 or more kwds) */ - if (nkwds > 6 && stack != small_stack) { + * NOTE: This whole block can be removed without breaking anything */ + Py_ssize_t noveralloc = n_merges + nkwds; + if (stack != small_stack && noveralloc > 6 && noveralloc * 10 > init_stack_size) { tmp_stack = PyMem_Realloc(stack, (tot_nargskw - n_merges) * sizeof(PyObject *)); if (tmp_stack == NULL) { Py_DECREF(tot_kwnames); From e83099b630b61454aba16e368bb66bfbc3a63526 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Mon, 7 Jul 2025 20:17:26 +0300 Subject: [PATCH 31/32] comment edit --- Modules/_functoolsmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index d0ef6e8df91d56..0478e32202a449 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -503,7 +503,7 @@ partial_vectorcall(PyObject *self, PyObject *const *args, assert(i == pto_nkwds); Py_XDECREF(pto_kw_merged); - /* Resize Stack if the call has more than 6 keywords + /* Resize Stack if the removing overallocation saves some noticable memory * NOTE: This whole block can be removed without breaking anything */ Py_ssize_t noveralloc = n_merges + nkwds; if (stack != small_stack && noveralloc > 6 && noveralloc * 10 > init_stack_size) { From 482336d6fe39dea65bd012ae9d42e792c8191463 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Tue, 8 Jul 2025 22:10:41 +0300 Subject: [PATCH 32/32] potential overflow fix --- Modules/_functoolsmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 0478e32202a449..1c888295cb07f1 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -506,7 +506,7 @@ partial_vectorcall(PyObject *self, PyObject *const *args, /* Resize Stack if the removing overallocation saves some noticable memory * NOTE: This whole block can be removed without breaking anything */ Py_ssize_t noveralloc = n_merges + nkwds; - if (stack != small_stack && noveralloc > 6 && noveralloc * 10 > init_stack_size) { + if (stack != small_stack && noveralloc > 6 && noveralloc > init_stack_size / 10) { tmp_stack = PyMem_Realloc(stack, (tot_nargskw - n_merges) * sizeof(PyObject *)); if (tmp_stack == NULL) { Py_DECREF(tot_kwnames);