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); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy