diff --git a/Include/internal/pycore_interpolation.h b/Include/internal/pycore_interpolation.h index dd610c1609c209..1ffc1d39b4c1d3 100644 --- a/Include/internal/pycore_interpolation.h +++ b/Include/internal/pycore_interpolation.h @@ -13,6 +13,7 @@ extern PyTypeObject _PyInterpolation_Type; #define _PyInterpolation_CheckExact(op) Py_IS_TYPE((op), &_PyInterpolation_Type) +// Steals references to value, str, and format_spec (even on failure). PyAPI_FUNC(PyObject *) _PyInterpolation_Build(PyObject *value, PyObject *str, int conversion, PyObject *format_spec); diff --git a/Include/internal/pycore_template.h b/Include/internal/pycore_template.h index f2f8bf9912d3f6..fdd2ea4937d80d 100644 --- a/Include/internal/pycore_template.h +++ b/Include/internal/pycore_template.h @@ -17,6 +17,7 @@ extern PyTypeObject _PyTemplateIter_Type; extern PyObject *_PyTemplate_Concat(PyObject *self, PyObject *other); +// Steals references to strings and interpolations (even on failure). PyAPI_FUNC(PyObject *) _PyTemplate_Build(PyObject *strings, PyObject *interpolations); #ifdef __cplusplus diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-07-12-00-00.gh-issue-145860.pgec7ElZ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-07-12-00-00.gh-issue-145860.pgec7ElZ.rst new file mode 100644 index 00000000000000..22766a9d64a42b --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-07-12-00-00.gh-issue-145860.pgec7ElZ.rst @@ -0,0 +1,4 @@ +Eliminate unnecessary reference count operations in ``BUILD_INTERPOLATION`` +and ``BUILD_TEMPLATE`` bytecode instructions by making +:c:func:`!_PyInterpolation_Build` and :c:func:`!_PyTemplate_Build` steal +references to their arguments. diff --git a/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h index 45cbc58b085851..84004738521e8e 100644 --- a/Modules/_testinternalcapi/test_cases.c.h +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -1468,38 +1468,20 @@ format = &stack_pointer[-(oparg & 1)]; str = stack_pointer[-1 - (oparg & 1)]; value = stack_pointer[-2 - (oparg & 1)]; - PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - PyObject *str_o = PyStackRef_AsPyObjectBorrow(str); + PyObject *value_o = PyStackRef_AsPyObjectSteal(value); + PyObject *str_o = PyStackRef_AsPyObjectSteal(str); int conversion = oparg >> 2; PyObject *format_o; if (oparg & 1) { - format_o = PyStackRef_AsPyObjectBorrow(format[0]); - } - else { - format_o = &_Py_STR(empty); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *interpolation_o = _PyInterpolation_Build(value_o, str_o, conversion, format_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (oparg & 1) { - stack_pointer += -(oparg & 1); - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(format[0]); - stack_pointer = _PyFrame_GetStackPointer(frame); + format_o = PyStackRef_AsPyObjectSteal(format[0]); } else { - stack_pointer += -(oparg & 1); + format_o = Py_NewRef(&_Py_STR(empty)); } - stack_pointer += -1; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(str); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; + stack_pointer += -2 - (oparg & 1); ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); + PyObject *interpolation_o = _PyInterpolation_Build(value_o, str_o, conversion, format_o); stack_pointer = _PyFrame_GetStackPointer(frame); if (interpolation_o == NULL) { JUMP_TO_LABEL(error); @@ -1692,20 +1674,12 @@ _PyStackRef template; interpolations = stack_pointer[-1]; strings = stack_pointer[-2]; - PyObject *strings_o = PyStackRef_AsPyObjectBorrow(strings); - PyObject *interpolations_o = PyStackRef_AsPyObjectBorrow(interpolations); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *template_o = _PyTemplate_Build(strings_o, interpolations_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(interpolations); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; + PyObject *strings_o = PyStackRef_AsPyObjectSteal(strings); + PyObject *interpolations_o = PyStackRef_AsPyObjectSteal(interpolations); + stack_pointer += -2; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(strings); + PyObject *template_o = _PyTemplate_Build(strings_o, interpolations_o); stack_pointer = _PyFrame_GetStackPointer(frame); if (template_o == NULL) { JUMP_TO_LABEL(error); diff --git a/Objects/interpolationobject.c b/Objects/interpolationobject.c index b58adb693f0cae..4c0ccd8632e384 100644 --- a/Objects/interpolationobject.c +++ b/Objects/interpolationobject.c @@ -190,12 +190,15 @@ _PyInterpolation_Build(PyObject *value, PyObject *str, int conversion, PyObject { interpolationobject *interpolation = PyObject_GC_New(interpolationobject, &_PyInterpolation_Type); if (!interpolation) { + Py_DECREF(value); + Py_DECREF(str); + Py_DECREF(format_spec); return NULL; } - interpolation->value = Py_NewRef(value); - interpolation->expression = Py_NewRef(str); - interpolation->format_spec = Py_NewRef(format_spec); + interpolation->value = value; + interpolation->expression = str; + interpolation->format_spec = format_spec; interpolation->conversion = NULL; if (conversion == 0) { diff --git a/Objects/templateobject.c b/Objects/templateobject.c index a05208e4c8fc8e..e2c91faf57c8b1 100644 --- a/Objects/templateobject.c +++ b/Objects/templateobject.c @@ -176,10 +176,7 @@ template_new(PyTypeObject *type, PyObject *args, PyObject *kwds) PyTuple_SET_ITEM(strings, stringsidx++, &_Py_STR(empty)); } - PyObject *template = _PyTemplate_Build(strings, interpolations); - Py_DECREF(strings); - Py_DECREF(interpolations); - return template; + return _PyTemplate_Build(strings, interpolations); } static void @@ -292,10 +289,7 @@ template_concat_templates(templateobject *self, templateobject *other) return NULL; } - PyObject *newtemplate = _PyTemplate_Build(newstrings, newinterpolations); - Py_DECREF(newstrings); - Py_DECREF(newinterpolations); - return newtemplate; + return _PyTemplate_Build(newstrings, newinterpolations); } PyObject * @@ -402,11 +396,13 @@ _PyTemplate_Build(PyObject *strings, PyObject *interpolations) { templateobject *template = PyObject_GC_New(templateobject, &_PyTemplate_Type); if (template == NULL) { + Py_DECREF(strings); + Py_DECREF(interpolations); return NULL; } - template->strings = Py_NewRef(strings); - template->interpolations = Py_NewRef(interpolations); + template->strings = strings; + template->interpolations = interpolations; PyObject_GC_Track(template); return (PyObject *) template; } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index edba5a89cc0f29..b9ce9fae0befc0 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2329,35 +2329,31 @@ dummy_func( } inst(BUILD_INTERPOLATION, (value, str, format[oparg & 1] -- interpolation)) { - PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - PyObject *str_o = PyStackRef_AsPyObjectBorrow(str); + PyObject *value_o = PyStackRef_AsPyObjectSteal(value); + DEAD(value); + PyObject *str_o = PyStackRef_AsPyObjectSteal(str); + DEAD(str); int conversion = oparg >> 2; PyObject *format_o; if (oparg & 1) { - format_o = PyStackRef_AsPyObjectBorrow(format[0]); - } - else { - format_o = &_Py_STR(empty); - } - PyObject *interpolation_o = _PyInterpolation_Build(value_o, str_o, conversion, format_o); - if (oparg & 1) { - PyStackRef_CLOSE(format[0]); + format_o = PyStackRef_AsPyObjectSteal(format[0]); + DEAD(format); } else { + format_o = Py_NewRef(&_Py_STR(empty)); DEAD(format); } - PyStackRef_CLOSE(str); - PyStackRef_CLOSE(value); + PyObject *interpolation_o = _PyInterpolation_Build(value_o, str_o, conversion, format_o); ERROR_IF(interpolation_o == NULL); interpolation = PyStackRef_FromPyObjectSteal(interpolation_o); } inst(BUILD_TEMPLATE, (strings, interpolations -- template)) { - PyObject *strings_o = PyStackRef_AsPyObjectBorrow(strings); - PyObject *interpolations_o = PyStackRef_AsPyObjectBorrow(interpolations); + PyObject *strings_o = PyStackRef_AsPyObjectSteal(strings); + DEAD(strings); + PyObject *interpolations_o = PyStackRef_AsPyObjectSteal(interpolations); + DEAD(interpolations); PyObject *template_o = _PyTemplate_Build(strings_o, interpolations_o); - PyStackRef_CLOSE(interpolations); - PyStackRef_CLOSE(strings); ERROR_IF(template_o == NULL); template = PyStackRef_FromPyObjectSteal(template_o); } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 6600accb37e3f2..983198a0955571 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -9680,38 +9680,20 @@ format = &stack_pointer[-(oparg & 1)]; str = stack_pointer[-1 - (oparg & 1)]; value = stack_pointer[-2 - (oparg & 1)]; - PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - PyObject *str_o = PyStackRef_AsPyObjectBorrow(str); + PyObject *value_o = PyStackRef_AsPyObjectSteal(value); + PyObject *str_o = PyStackRef_AsPyObjectSteal(str); int conversion = oparg >> 2; PyObject *format_o; if (oparg & 1) { - format_o = PyStackRef_AsPyObjectBorrow(format[0]); - } - else { - format_o = &_Py_STR(empty); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *interpolation_o = _PyInterpolation_Build(value_o, str_o, conversion, format_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (oparg & 1) { - stack_pointer += -(oparg & 1); - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(format[0]); - stack_pointer = _PyFrame_GetStackPointer(frame); + format_o = PyStackRef_AsPyObjectSteal(format[0]); } else { - stack_pointer += -(oparg & 1); + format_o = Py_NewRef(&_Py_STR(empty)); } - stack_pointer += -1; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(str); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; + stack_pointer += -2 - (oparg & 1); ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); + PyObject *interpolation_o = _PyInterpolation_Build(value_o, str_o, conversion, format_o); stack_pointer = _PyFrame_GetStackPointer(frame); if (interpolation_o == NULL) { SET_CURRENT_CACHED_VALUES(0); @@ -9736,25 +9718,11 @@ _PyStackRef _stack_item_1 = _tos_cache1; interpolations = _stack_item_1; strings = _stack_item_0; - PyObject *strings_o = PyStackRef_AsPyObjectBorrow(strings); - PyObject *interpolations_o = PyStackRef_AsPyObjectBorrow(interpolations); - stack_pointer[0] = strings; - stack_pointer[1] = interpolations; - stack_pointer += 2; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + PyObject *strings_o = PyStackRef_AsPyObjectSteal(strings); + PyObject *interpolations_o = PyStackRef_AsPyObjectSteal(interpolations); _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *template_o = _PyTemplate_Build(strings_o, interpolations_o); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(interpolations); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(strings); - stack_pointer = _PyFrame_GetStackPointer(frame); if (template_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 127a45ef591053..5707672acf6194 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -1468,38 +1468,20 @@ format = &stack_pointer[-(oparg & 1)]; str = stack_pointer[-1 - (oparg & 1)]; value = stack_pointer[-2 - (oparg & 1)]; - PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - PyObject *str_o = PyStackRef_AsPyObjectBorrow(str); + PyObject *value_o = PyStackRef_AsPyObjectSteal(value); + PyObject *str_o = PyStackRef_AsPyObjectSteal(str); int conversion = oparg >> 2; PyObject *format_o; if (oparg & 1) { - format_o = PyStackRef_AsPyObjectBorrow(format[0]); - } - else { - format_o = &_Py_STR(empty); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *interpolation_o = _PyInterpolation_Build(value_o, str_o, conversion, format_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (oparg & 1) { - stack_pointer += -(oparg & 1); - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(format[0]); - stack_pointer = _PyFrame_GetStackPointer(frame); + format_o = PyStackRef_AsPyObjectSteal(format[0]); } else { - stack_pointer += -(oparg & 1); + format_o = Py_NewRef(&_Py_STR(empty)); } - stack_pointer += -1; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(str); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; + stack_pointer += -2 - (oparg & 1); ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); + PyObject *interpolation_o = _PyInterpolation_Build(value_o, str_o, conversion, format_o); stack_pointer = _PyFrame_GetStackPointer(frame); if (interpolation_o == NULL) { JUMP_TO_LABEL(error); @@ -1692,20 +1674,12 @@ _PyStackRef template; interpolations = stack_pointer[-1]; strings = stack_pointer[-2]; - PyObject *strings_o = PyStackRef_AsPyObjectBorrow(strings); - PyObject *interpolations_o = PyStackRef_AsPyObjectBorrow(interpolations); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *template_o = _PyTemplate_Build(strings_o, interpolations_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(interpolations); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; + PyObject *strings_o = PyStackRef_AsPyObjectSteal(strings); + PyObject *interpolations_o = PyStackRef_AsPyObjectSteal(interpolations); + stack_pointer += -2; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(strings); + PyObject *template_o = _PyTemplate_Build(strings_o, interpolations_o); stack_pointer = _PyFrame_GetStackPointer(frame); if (template_o == NULL) { JUMP_TO_LABEL(error);