diff --git a/Include/cpython/ceval.h b/Include/cpython/ceval.h index ca8109e3248a8d..bbab8d35b75cb2 100644 --- a/Include/cpython/ceval.h +++ b/Include/cpython/ceval.h @@ -23,6 +23,9 @@ _PyEval_RequestCodeExtraIndex(freefunc f) { PyAPI_FUNC(int) _PyEval_SliceIndex(PyObject *, Py_ssize_t *); PyAPI_FUNC(int) _PyEval_SliceIndexNotNone(PyObject *, Py_ssize_t *); +PyAPI_FUNC(int) _PyEval_UnpackIndices(PyObject *, PyObject *, + Py_ssize_t, + Py_ssize_t *, Py_ssize_t *); // Trampoline API diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-02-08-13-14-00.gh-issue-144569.pjlJVe.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-08-13-14-00.gh-issue-144569.pjlJVe.rst new file mode 100644 index 00000000000000..bdd0d6c2f7b944 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-08-13-14-00.gh-issue-144569.pjlJVe.rst @@ -0,0 +1 @@ +Optimize ``BINARY_SLICE`` for list, tuple, and unicode by avoiding temporary ``slice`` object creation. diff --git a/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h index ddd8fcdc231bf1..cc881f30b1a3cb 100644 --- a/Modules/_testinternalcapi/test_cases.c.h +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -1396,28 +1396,70 @@ stop = stack_pointer[-1]; start = stack_pointer[-2]; container = stack_pointer[-3]; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *slice = _PyBuildSlice_ConsumeRefs(PyStackRef_AsPyObjectSteal(start), - PyStackRef_AsPyObjectSteal(stop)); - stack_pointer = _PyFrame_GetStackPointer(frame); + PyObject *container_o = PyStackRef_AsPyObjectBorrow(container); + PyObject *start_o = PyStackRef_AsPyObjectBorrow(start); + PyObject *stop_o = PyStackRef_AsPyObjectBorrow(stop); PyObject *res_o; - if (slice == NULL) { - res_o = NULL; - } - else { - stack_pointer += -2; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if ((PyList_CheckExact(container_o) || + PyTuple_CheckExact(container_o) || + PyUnicode_CheckExact(container_o)) && + (start_o == Py_None || PyLong_CheckExact(start_o)) && + (stop_o == Py_None || PyLong_CheckExact(stop_o))) { + Py_ssize_t len = PyUnicode_CheckExact(container_o) + ? PyUnicode_GET_LENGTH(container_o) + : Py_SIZE(container_o); + Py_ssize_t istart, istop; _PyFrame_SetStackPointer(frame, stack_pointer); - res_o = PyObject_GetItem(PyStackRef_AsPyObjectBorrow(container), slice); - Py_DECREF(slice); + int err = _PyEval_UnpackIndices(start_o, stop_o, len, + &istart, &istop); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += 2; + if (err == 0) { + res_o = NULL; + } + else if (PyList_CheckExact(container_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = PyList_GetSlice(container_o, istart, istop); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else if (PyTuple_CheckExact(container_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = PyTuple_GetSlice(container_o, istart, istop); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = PyUnicode_Substring(container_o, istart, istop); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + else { + PyObject *slice = PySlice_New(start_o, stop_o, NULL); + if (slice == NULL) { + res_o = NULL; + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = PyObject_GetItem(container_o, slice); + Py_DECREF(slice); + stack_pointer = _PyFrame_GetStackPointer(frame); + } } - stack_pointer += -3; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(container); + _PyStackRef tmp = stop; + stop = PyStackRef_NULL; + stack_pointer[-1] = stop; + PyStackRef_CLOSE(tmp); + tmp = start; + start = PyStackRef_NULL; + stack_pointer[-2] = start; + PyStackRef_CLOSE(tmp); + tmp = container; + container = PyStackRef_NULL; + stack_pointer[-3] = container; + PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (res_o == NULL) { JUMP_TO_LABEL(error); } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index b461f9b5bea8a6..0fbeecab74661c 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -866,19 +866,45 @@ dummy_func( } op(_BINARY_SLICE, (container, start, stop -- res)) { - PyObject *slice = _PyBuildSlice_ConsumeRefs(PyStackRef_AsPyObjectSteal(start), - PyStackRef_AsPyObjectSteal(stop)); + PyObject *container_o = PyStackRef_AsPyObjectBorrow(container); + PyObject *start_o = PyStackRef_AsPyObjectBorrow(start); + PyObject *stop_o = PyStackRef_AsPyObjectBorrow(stop); PyObject *res_o; - // Can't use ERROR_IF() here, because we haven't - // DECREF'ed container yet, and we still own slice. - if (slice == NULL) { - res_o = NULL; + if ((PyList_CheckExact(container_o) || + PyTuple_CheckExact(container_o) || + PyUnicode_CheckExact(container_o)) && + (start_o == Py_None || PyLong_CheckExact(start_o)) && + (stop_o == Py_None || PyLong_CheckExact(stop_o))) { + Py_ssize_t len = PyUnicode_CheckExact(container_o) + ? PyUnicode_GET_LENGTH(container_o) + : Py_SIZE(container_o); + Py_ssize_t istart, istop; + int err = _PyEval_UnpackIndices(start_o, stop_o, len, + &istart, &istop); + if (err == 0) { + res_o = NULL; + } + else if (PyList_CheckExact(container_o)) { + res_o = PyList_GetSlice(container_o, istart, istop); + } + else if (PyTuple_CheckExact(container_o)) { + res_o = PyTuple_GetSlice(container_o, istart, istop); + } + else { + res_o = PyUnicode_Substring(container_o, istart, istop); + } } else { - res_o = PyObject_GetItem(PyStackRef_AsPyObjectBorrow(container), slice); - Py_DECREF(slice); + PyObject *slice = PySlice_New(start_o, stop_o, NULL); + if (slice == NULL) { + res_o = NULL; + } + else { + res_o = PyObject_GetItem(container_o, slice); + Py_DECREF(slice); + } } - PyStackRef_CLOSE(container); + DECREF_INPUTS(); ERROR_IF(res_o == NULL); res = PyStackRef_FromPyObjectSteal(res_o); } diff --git a/Python/ceval.c b/Python/ceval.c index ab2eef560370f5..ff003bfa47ae36 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2922,6 +2922,26 @@ _PyEval_SliceIndexNotNone(PyObject *v, Py_ssize_t *pi) return 1; } +int +_PyEval_UnpackIndices(PyObject *start, PyObject *stop, + Py_ssize_t len, + Py_ssize_t *istart, Py_ssize_t *istop) +{ + if (len < 0) { + return 0; + } + *istart = 0; + *istop = PY_SSIZE_T_MAX; + if (!_PyEval_SliceIndex(start, istart)) { + return 0; + } + if (!_PyEval_SliceIndex(stop, istop)) { + return 0; + } + PySlice_AdjustIndices(len, istart, istop, 1); + return 1; +} + PyObject * _PyEval_ImportName(PyThreadState *tstate, PyObject *builtins, PyObject *globals, PyObject *locals, PyObject *name, diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 9dead4eecc7826..34d02a7a087c1c 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -5198,33 +5198,84 @@ stop = _stack_item_2; start = _stack_item_1; container = _stack_item_0; - stack_pointer[0] = container; - stack_pointer[1] = start; - stack_pointer[2] = stop; - stack_pointer += 3; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *slice = _PyBuildSlice_ConsumeRefs(PyStackRef_AsPyObjectSteal(start), - PyStackRef_AsPyObjectSteal(stop)); - stack_pointer = _PyFrame_GetStackPointer(frame); + PyObject *container_o = PyStackRef_AsPyObjectBorrow(container); + PyObject *start_o = PyStackRef_AsPyObjectBorrow(start); + PyObject *stop_o = PyStackRef_AsPyObjectBorrow(stop); PyObject *res_o; - if (slice == NULL) { - res_o = NULL; - } - else { - stack_pointer += -2; + if ((PyList_CheckExact(container_o) || + PyTuple_CheckExact(container_o) || + PyUnicode_CheckExact(container_o)) && + (start_o == Py_None || PyLong_CheckExact(start_o)) && + (stop_o == Py_None || PyLong_CheckExact(stop_o))) { + Py_ssize_t len = PyUnicode_CheckExact(container_o) + ? PyUnicode_GET_LENGTH(container_o) + : Py_SIZE(container_o); + Py_ssize_t istart, istop; + stack_pointer[0] = container; + stack_pointer[1] = start; + stack_pointer[2] = stop; + stack_pointer += 3; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - res_o = PyObject_GetItem(PyStackRef_AsPyObjectBorrow(container), slice); - Py_DECREF(slice); + int err = _PyEval_UnpackIndices(start_o, stop_o, len, + &istart, &istop); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += 2; + if (err == 0) { + res_o = NULL; + } + else if (PyList_CheckExact(container_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = PyList_GetSlice(container_o, istart, istop); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else if (PyTuple_CheckExact(container_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = PyTuple_GetSlice(container_o, istart, istop); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = PyUnicode_Substring(container_o, istart, istop); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + else { + PyObject *slice = PySlice_New(start_o, stop_o, NULL); + if (slice == NULL) { + res_o = NULL; + } + else { + stack_pointer[0] = container; + stack_pointer[1] = start; + stack_pointer[2] = stop; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = PyObject_GetItem(container_o, slice); + Py_DECREF(slice); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + } + stack_pointer += 3; } - stack_pointer += -3; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(container); + _PyStackRef tmp = stop; + stop = PyStackRef_NULL; + stack_pointer[-3] = container; + stack_pointer[-2] = start; + stack_pointer[-1] = stop; + PyStackRef_CLOSE(tmp); + tmp = start; + start = PyStackRef_NULL; + stack_pointer[-2] = start; + PyStackRef_CLOSE(tmp); + tmp = container; + container = PyStackRef_NULL; + stack_pointer[-3] = container; + PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (res_o == NULL) { SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 37fa6d679190dd..10765ad4c39b4d 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1396,28 +1396,70 @@ stop = stack_pointer[-1]; start = stack_pointer[-2]; container = stack_pointer[-3]; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *slice = _PyBuildSlice_ConsumeRefs(PyStackRef_AsPyObjectSteal(start), - PyStackRef_AsPyObjectSteal(stop)); - stack_pointer = _PyFrame_GetStackPointer(frame); + PyObject *container_o = PyStackRef_AsPyObjectBorrow(container); + PyObject *start_o = PyStackRef_AsPyObjectBorrow(start); + PyObject *stop_o = PyStackRef_AsPyObjectBorrow(stop); PyObject *res_o; - if (slice == NULL) { - res_o = NULL; - } - else { - stack_pointer += -2; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if ((PyList_CheckExact(container_o) || + PyTuple_CheckExact(container_o) || + PyUnicode_CheckExact(container_o)) && + (start_o == Py_None || PyLong_CheckExact(start_o)) && + (stop_o == Py_None || PyLong_CheckExact(stop_o))) { + Py_ssize_t len = PyUnicode_CheckExact(container_o) + ? PyUnicode_GET_LENGTH(container_o) + : Py_SIZE(container_o); + Py_ssize_t istart, istop; _PyFrame_SetStackPointer(frame, stack_pointer); - res_o = PyObject_GetItem(PyStackRef_AsPyObjectBorrow(container), slice); - Py_DECREF(slice); + int err = _PyEval_UnpackIndices(start_o, stop_o, len, + &istart, &istop); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += 2; + if (err == 0) { + res_o = NULL; + } + else if (PyList_CheckExact(container_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = PyList_GetSlice(container_o, istart, istop); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else if (PyTuple_CheckExact(container_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = PyTuple_GetSlice(container_o, istart, istop); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = PyUnicode_Substring(container_o, istart, istop); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + else { + PyObject *slice = PySlice_New(start_o, stop_o, NULL); + if (slice == NULL) { + res_o = NULL; + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = PyObject_GetItem(container_o, slice); + Py_DECREF(slice); + stack_pointer = _PyFrame_GetStackPointer(frame); + } } - stack_pointer += -3; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(container); + _PyStackRef tmp = stop; + stop = PyStackRef_NULL; + stack_pointer[-1] = stop; + PyStackRef_CLOSE(tmp); + tmp = start; + start = PyStackRef_NULL; + stack_pointer[-2] = start; + PyStackRef_CLOSE(tmp); + tmp = container; + container = PyStackRef_NULL; + stack_pointer[-3] = container; + PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (res_o == NULL) { JUMP_TO_LABEL(error); }