diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index af904a567cfb7e..f4ffa24edca453 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -156,7 +156,7 @@ Misc/libabigail.abignore @encukou # ---------------------------------------------------------------------------- # Android -Android/ @mhsmith @freakboy3742 +Platforms/Android/ @mhsmith @freakboy3742 Doc/using/android.rst @mhsmith @freakboy3742 Lib/_android_support.py @mhsmith @freakboy3742 Lib/test/test_android.py @mhsmith @freakboy3742 @@ -164,8 +164,7 @@ Lib/test/test_android.py @mhsmith @freakboy3742 # iOS Doc/using/ios.rst @freakboy3742 Lib/_ios_support.py @freakboy3742 -Apple/ @freakboy3742 -iOS/ @freakboy3742 +Platforms/Apple/ @freakboy3742 # macOS Mac/ @python/macos-team @@ -176,8 +175,8 @@ Lib/test/test__osx_support.py @python/macos-team Tools/wasm/README.md @brettcannon @freakboy3742 @emmatyping # WebAssembly (Emscripten) -Tools/wasm/config.site-wasm32-emscripten @freakboy3742 @emmatyping -Tools/wasm/emscripten @freakboy3742 @emmatyping +Platforms/emscripten @freakboy3742 @emmatyping +Tools/wasm/emscripten @freakboy3742 @emmatyping # WebAssembly (WASI) Platforms/WASI @brettcannon @emmatyping @savannahostrowski diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index a2a0d0d80657eb..556113a97bf772 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -151,7 +151,7 @@ Dictionary objects * If the key is present, set *\*result* to a new :term:`strong reference` to the value and return ``1``. * If the key is missing, set *\*result* to ``NULL`` and return ``0``. - * On error, raise an exception and return ``-1``. + * On error, raise an exception, set *\*result* to ``NULL`` and return ``-1``. The first argument can be a :class:`dict` or a :class:`frozendict`. diff --git a/Include/internal/pycore_bytesobject.h b/Include/internal/pycore_bytesobject.h index 177e6d10134adb..27a7a46152f57b 100644 --- a/Include/internal/pycore_bytesobject.h +++ b/Include/internal/pycore_bytesobject.h @@ -62,9 +62,11 @@ _PyBytes_ReverseFind(const char *haystack, Py_ssize_t len_haystack, // // Export for 'array' shared extension. PyAPI_FUNC(void) -_PyBytes_Repeat(char* dest, Py_ssize_t len_dest, +_PyBytes_RepeatBuffer(char* dest, Py_ssize_t len_dest, const char* src, Py_ssize_t len_src); +PyAPI_FUNC(PyObject *) _PyBytes_Repeat(PyObject *self, Py_ssize_t n); + /* _PyBytesObject_SIZE gives the basic size of a bytes object; any memory allocation for a bytes object of length n should request PyBytesObject_SIZE + n bytes. diff --git a/Include/internal/pycore_tuple.h b/Include/internal/pycore_tuple.h index bf80f96396ea4a..e294b16b3df60f 100644 --- a/Include/internal/pycore_tuple.h +++ b/Include/internal/pycore_tuple.h @@ -29,6 +29,7 @@ PyAPI_FUNC(PyObject *)_PyTuple_FromStackRefStealOnSuccess(const union _PyStackRe PyAPI_FUNC(PyObject *)_PyTuple_FromArraySteal(PyObject *const *, Py_ssize_t); PyAPI_FUNC(PyObject *) _PyTuple_BinarySlice(PyObject *, PyObject *, PyObject *); PyAPI_FUNC(PyObject *) _PyTuple_Concat(PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) _PyTuple_Repeat(PyObject *self, Py_ssize_t n); PyAPI_FUNC(PyObject *) _PyTuple_FromPair(PyObject *, PyObject *); PyAPI_FUNC(PyObject *) _PyTuple_FromPairSteal(PyObject *, PyObject *); diff --git a/Include/internal/pycore_unicodeobject.h b/Include/internal/pycore_unicodeobject.h index 74d84052a2bb2b..75d5068f815b91 100644 --- a/Include/internal/pycore_unicodeobject.h +++ b/Include/internal/pycore_unicodeobject.h @@ -33,6 +33,7 @@ extern PyObject* _PyUnicode_ResizeCompact( Py_ssize_t length); extern PyObject* _PyUnicode_GetEmpty(void); PyAPI_FUNC(PyObject*) _PyUnicode_BinarySlice(PyObject *, PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) _PyUnicode_Repeat(PyObject *str, Py_ssize_t len); /* Generic helper macro to convert characters of different types. diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index aaa5050208ced9..2f606c2c6eba2d 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -6138,6 +6138,20 @@ def __init__(self, x): C(0) if i else str(0) """)) + def test_load_special_type_guard_deopt(self): + script_helper.assert_python_ok("-s", "-c", textwrap.dedent(f""" + def f1(): + class Context: + def __enter__(self): ... + def __exit__(self, e, v, t): ... + + with Context(): + pass + + for _ in range({TIER2_THRESHOLD + 5}): + f1() + """), PYTHON_JIT="1") + def global_identity(x): return x diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-07-03-18-59.gh-issue-149459.5fhAqP.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-07-03-18-59.gh-issue-149459.5fhAqP.rst new file mode 100644 index 00000000000000..4cd0a148df3c70 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-07-03-18-59.gh-issue-149459.5fhAqP.rst @@ -0,0 +1 @@ +Fix a crash in the JIT optimizer when a specialized ``LOAD_SPECIAL`` guard deoptimized after inserting the synthetic ``NULL`` stack entry. diff --git a/Modules/_remote_debugging/threads.c b/Modules/_remote_debugging/threads.c index d775234b8d78d7..4daa5e5f92bcd9 100644 --- a/Modules/_remote_debugging/threads.c +++ b/Modules/_remote_debugging/threads.c @@ -450,12 +450,14 @@ unwind_stack_for_thread( set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to collect frames"); goto error; } - // Update last_profiled_frame for next sample - uintptr_t lpf_addr = - *current_tstate + (uintptr_t)unwinder->debug_offsets.thread_state.last_profiled_frame; - if (_Py_RemoteDebug_WriteRemoteMemory(&unwinder->handle, lpf_addr, - sizeof(uintptr_t), &frame_addr) < 0) { - PyErr_Clear(); // Non-fatal + // Update last_profiled_frame for next sample if it changed + if (frame_addr != ctx.last_profiled_frame) { + uintptr_t lpf_addr = + *current_tstate + (uintptr_t)unwinder->debug_offsets.thread_state.last_profiled_frame; + if (_Py_RemoteDebug_WriteRemoteMemory(&unwinder->handle, lpf_addr, + sizeof(uintptr_t), &frame_addr) < 0) { + PyErr_Clear(); // Non-fatal + } } } else { // No caching - process entire frame chain with base_frame validation diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 472c59ea8c9882..646b73bd4af7db 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -8,7 +8,7 @@ #endif #include "Python.h" -#include "pycore_bytesobject.h" // _PyBytes_Repeat +#include "pycore_bytesobject.h" // _PyBytes_RepeatBuffer #include "pycore_call.h" // _PyObject_CallMethod() #include "pycore_ceval.h" // _PyEval_GetBuiltin() #include "pycore_floatobject.h" // _PY_FLOAT_BIG_ENDIAN @@ -1147,7 +1147,7 @@ array_repeat(PyObject *op, Py_ssize_t n) const Py_ssize_t oldbytes = array_length * a->ob_descr->itemsize; const Py_ssize_t newbytes = oldbytes * n; - _PyBytes_Repeat(np->ob_item, newbytes, a->ob_item, oldbytes); + _PyBytes_RepeatBuffer(np->ob_item, newbytes, a->ob_item, oldbytes); return (PyObject *)np; } @@ -1304,7 +1304,7 @@ array_inplace_repeat(PyObject *op, Py_ssize_t n) if (array_resize(self, n * array_size) == -1) return NULL; - _PyBytes_Repeat(self->ob_item, n*size, self->ob_item, size); + _PyBytes_RepeatBuffer(self->ob_item, n*size, self->ob_item, size); } return Py_NewRef(self); } diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index c583193b5a252c..e698d260795480 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -402,7 +402,7 @@ bytearray_repeat_lock_held(PyObject *op, Py_ssize_t count) PyByteArrayObject* result = (PyByteArrayObject *)PyByteArray_FromStringAndSize(NULL, size); const char* buf = PyByteArray_AS_STRING(self); if (result != NULL && size != 0) { - _PyBytes_Repeat(result->ob_bytes, size, buf, mysize); + _PyBytes_RepeatBuffer(result->ob_bytes, size, buf, mysize); } return (PyObject *)result; } @@ -439,7 +439,7 @@ bytearray_irepeat_lock_held(PyObject *op, Py_ssize_t count) } char* buf = PyByteArray_AS_STRING(self); - _PyBytes_Repeat(buf, size, buf, mysize); + _PyBytes_RepeatBuffer(buf, size, buf, mysize); return Py_NewRef(self); } diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 8a9d1b133affb3..cd8417e2583916 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -3,7 +3,7 @@ #include "Python.h" #include "pycore_abstract.h" // _PyIndex_Check() #include "pycore_bytes_methods.h" // _Py_bytes_startswith() -#include "pycore_bytesobject.h" // _PyBytes_Find(), _PyBytes_Repeat() +#include "pycore_bytesobject.h" // _PyBytes_Find(), _PyBytes_RepeatBuffer() #include "pycore_call.h" // _PyObject_CallNoArgs() #include "pycore_ceval.h" // _PyEval_GetBuiltin() #include "pycore_format.h" // F_LJUST @@ -1581,8 +1581,8 @@ _PyBytes_Concat(PyObject *a, PyObject *b) return result; } -static PyObject * -bytes_repeat(PyObject *self, Py_ssize_t n) +PyObject * +_PyBytes_Repeat(PyObject *self, Py_ssize_t n) { PyBytesObject *a = _PyBytes_CAST(self); if (n < 0) @@ -1613,7 +1613,7 @@ bytes_repeat(PyObject *self, Py_ssize_t n) set_ob_shash(op, -1); op->ob_sval[size] = '\0'; - _PyBytes_Repeat(op->ob_sval, size, a->ob_sval, Py_SIZE(a)); + _PyBytes_RepeatBuffer(op->ob_sval, size, a->ob_sval, Py_SIZE(a)); return (PyObject *) op; } @@ -1805,7 +1805,7 @@ bytes_buffer_getbuffer(PyObject *op, Py_buffer *view, int flags) static PySequenceMethods bytes_as_sequence = { bytes_length, /*sq_length*/ _PyBytes_Concat, /*sq_concat*/ - bytes_repeat, /*sq_repeat*/ + _PyBytes_Repeat, /*sq_repeat*/ bytes_item, /*sq_item*/ 0, /*sq_slice*/ 0, /*sq_ass_item*/ @@ -3555,7 +3555,7 @@ bytes_iter(PyObject *seq) void -_PyBytes_Repeat(char* dest, Py_ssize_t len_dest, +_PyBytes_RepeatBuffer(char* dest, Py_ssize_t len_dest, const char* src, Py_ssize_t len_src) { if (len_dest == 0) { diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index 753c270f525976..94230002427546 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -594,8 +594,8 @@ _PyTuple_Concat(PyObject *aa, PyObject *bb) return (PyObject *)np; } -static PyObject * -tuple_repeat(PyObject *self, Py_ssize_t n) +PyObject * +_PyTuple_Repeat(PyObject *self, Py_ssize_t n) { PyTupleObject *a = _PyTuple_CAST(self); const Py_ssize_t input_size = Py_SIZE(a); @@ -865,7 +865,7 @@ tuple_subtype_new(PyTypeObject *type, PyObject *iterable) static PySequenceMethods tuple_as_sequence = { tuple_length, /* sq_length */ _PyTuple_Concat, /* sq_concat */ - tuple_repeat, /* sq_repeat */ + _PyTuple_Repeat, /* sq_repeat */ tuple_item, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 9aee7120c811de..5c97efd6838ef3 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -41,7 +41,7 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "Python.h" #include "pycore_abstract.h" // _PyIndex_Check() #include "pycore_bytes_methods.h" // _Py_bytes_lower() -#include "pycore_bytesobject.h" // _PyBytes_Repeat() +#include "pycore_bytesobject.h" // _PyBytes_RepeatBuffer() #include "pycore_ceval.h" // _PyEval_GetBuiltin() #include "pycore_codecs.h" // _PyCodec_Lookup() #include "pycore_critical_section.h" // Py_*_CRITICAL_SECTION_SEQUENCE_FAST @@ -12502,8 +12502,8 @@ unicode_rstrip_impl(PyObject *self, PyObject *chars) } -static PyObject* -unicode_repeat(PyObject *str, Py_ssize_t len) +PyObject * +_PyUnicode_Repeat(PyObject *str, Py_ssize_t len) { PyObject *u; Py_ssize_t nchars, n; @@ -12548,7 +12548,7 @@ unicode_repeat(PyObject *str, Py_ssize_t len) else { Py_ssize_t char_size = PyUnicode_KIND(str); char *to = (char *) PyUnicode_DATA(u); - _PyBytes_Repeat(to, nchars * char_size, PyUnicode_DATA(str), + _PyBytes_RepeatBuffer(to, nchars * char_size, PyUnicode_DATA(str), PyUnicode_GET_LENGTH(str) * char_size); } @@ -13734,7 +13734,7 @@ static PyNumberMethods unicode_as_number = { static PySequenceMethods unicode_as_sequence = { unicode_length, /* sq_length */ PyUnicode_Concat, /* sq_concat */ - unicode_repeat, /* sq_repeat */ + _PyUnicode_Repeat, /* sq_repeat */ unicode_getitem, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 39cc36ae79fead..96dbaea5a5797e 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -2043,7 +2043,16 @@ dummy_func(void) { PyObject *name = _Py_SpecialMethods[oparg].name; PyObject *descr = _PyType_Lookup(type, name); if (descr != NULL && (Py_TYPE(descr)->tp_flags & Py_TPFLAGS_METHOD_DESCRIPTOR)) { - ADD_OP(_GUARD_TYPE_VERSION, 0, type->tp_version_tag); + /* LOAD_SPECIAL expands to _RECORD_TOS_TYPE + _INSERT_NULL + + * _LOAD_SPECIAL. Insert _GUARD_TYPE_VERSION before the + * already-emitted _INSERT_NULL so deopt sees the original + * stack shape.*/ + _PyUOpInstruction *insert_null = uop_buffer_last(&ctx->out_buffer); + assert(insert_null->opcode == _INSERT_NULL); + assert(insert_null->target == this_instr->target); + REPLACE_OP(insert_null, _GUARD_TYPE_VERSION, 0, type->tp_version_tag); + ADD_OP(_INSERT_NULL, 0, 0); + bool immortal = _Py_IsImmortal(descr) || (type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE); ADD_OP(immortal ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE, 0, (uintptr_t)descr); diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index db3dcbb97b2645..f336549d2ed244 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -3896,7 +3896,11 @@ PyObject *name = _Py_SpecialMethods[oparg].name; PyObject *descr = _PyType_Lookup(type, name); if (descr != NULL && (Py_TYPE(descr)->tp_flags & Py_TPFLAGS_METHOD_DESCRIPTOR)) { - ADD_OP(_GUARD_TYPE_VERSION, 0, type->tp_version_tag); + _PyUOpInstruction *insert_null = uop_buffer_last(&ctx->out_buffer); + assert(insert_null->opcode == _INSERT_NULL); + assert(insert_null->target == this_instr->target); + REPLACE_OP(insert_null, _GUARD_TYPE_VERSION, 0, type->tp_version_tag); + ADD_OP(_INSERT_NULL, 0, 0); bool immortal = _Py_IsImmortal(descr) || (type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE); ADD_OP(immortal ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE, 0, (uintptr_t)descr); diff --git a/Python/specialize.c b/Python/specialize.c index 459e69de5709b8..2ff0a9d0072cec 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -2124,55 +2124,23 @@ is_compactlong(PyObject *v) _PyLong_IsCompact((PyLongObject *)v); } -/* sequence * int helpers: bypass PyNumber_Multiply dispatch overhead - by calling sq_repeat directly with PyLong_AsSsize_t. */ - -static inline PyObject * -seq_int_multiply(PyObject *seq, PyObject *n, - ssizeargfunc repeat) -{ - Py_ssize_t count = PyLong_AsSsize_t(n); - if (count == -1 && PyErr_Occurred()) { - return NULL; - } - return repeat(seq, count); -} - -static PyObject * -str_int_multiply(PyObject *lhs, PyObject *rhs) -{ - return seq_int_multiply(lhs, rhs, PyUnicode_Type.tp_as_sequence->sq_repeat); -} - -static PyObject * -int_str_multiply(PyObject *lhs, PyObject *rhs) -{ - return seq_int_multiply(rhs, lhs, PyUnicode_Type.tp_as_sequence->sq_repeat); -} - -static PyObject * -bytes_int_multiply(PyObject *lhs, PyObject *rhs) -{ - return seq_int_multiply(lhs, rhs, PyBytes_Type.tp_as_sequence->sq_repeat); -} - -static PyObject * -int_bytes_multiply(PyObject *lhs, PyObject *rhs) -{ - return seq_int_multiply(rhs, lhs, PyBytes_Type.tp_as_sequence->sq_repeat); -} - -static PyObject * -tuple_int_multiply(PyObject *lhs, PyObject *rhs) -{ - return seq_int_multiply(lhs, rhs, PyTuple_Type.tp_as_sequence->sq_repeat); -} - -static PyObject * -int_tuple_multiply(PyObject *lhs, PyObject *rhs) -{ - return seq_int_multiply(rhs, lhs, PyTuple_Type.tp_as_sequence->sq_repeat); -} +#define SEQ_INT_MULTIPLY_ACTION(NAME, REPEAT, SEQ, COUNT) \ + static PyObject * \ + (NAME)(PyObject *lhs, PyObject *rhs) \ + { \ + Py_ssize_t count = PyLong_AsSsize_t(COUNT); \ + if (count == -1 && PyErr_Occurred()) { \ + return NULL; \ + } \ + return REPEAT(SEQ, count); \ + } +SEQ_INT_MULTIPLY_ACTION(str_int_multiply, _PyUnicode_Repeat, lhs, rhs) +SEQ_INT_MULTIPLY_ACTION(int_str_multiply, _PyUnicode_Repeat, rhs, lhs) +SEQ_INT_MULTIPLY_ACTION(bytes_int_multiply, _PyBytes_Repeat, lhs, rhs) +SEQ_INT_MULTIPLY_ACTION(int_bytes_multiply, _PyBytes_Repeat, rhs, lhs) +SEQ_INT_MULTIPLY_ACTION(tuple_int_multiply, _PyTuple_Repeat, lhs, rhs) +SEQ_INT_MULTIPLY_ACTION(int_tuple_multiply, _PyTuple_Repeat, rhs, lhs) +#undef SEQ_INT_MULTIPLY_ACTION static int compactlongs_guard(PyObject *lhs, PyObject *rhs) @@ -2300,8 +2268,8 @@ static _PyBinaryOpSpecializationDescr binaryop_extend_descrs[] = { to be a freshly allocated object. */ {NB_ADD, NULL, _PyTuple_Concat, &PyTuple_Type, 0, &PyTuple_Type, &PyTuple_Type}, - /* str * int / int * str: call unicode_repeat directly. - unicode_repeat returns the original when n == 1. */ + /* str * int / int * str: call _PyUnicode_Repeat directly. + _PyUnicode_Repeat returns the original when n == 1. */ {NB_MULTIPLY, NULL, str_int_multiply, &PyUnicode_Type, 0, &PyUnicode_Type, &PyLong_Type}, {NB_MULTIPLY, NULL, int_str_multiply, &PyUnicode_Type, 0, &PyLong_Type, &PyUnicode_Type}, {NB_INPLACE_MULTIPLY, NULL, str_int_multiply, &PyUnicode_Type, 0, &PyUnicode_Type, &PyLong_Type}, @@ -2312,15 +2280,15 @@ static _PyBinaryOpSpecializationDescr binaryop_extend_descrs[] = { {NB_ADD, NULL, _PyBytes_Concat, &PyBytes_Type, 0, &PyBytes_Type, &PyBytes_Type}, {NB_INPLACE_ADD, NULL, _PyBytes_Concat, &PyBytes_Type, 0, &PyBytes_Type, &PyBytes_Type}, - /* bytes * int / int * bytes: call bytes_repeat directly. - bytes_repeat returns the original when n == 1. */ + /* bytes * int / int * bytes: call _PyBytes_Repeat directly. + _PyBytes_Repeat returns the original when n == 1. */ {NB_MULTIPLY, NULL, bytes_int_multiply, &PyBytes_Type, 0, &PyBytes_Type, &PyLong_Type}, {NB_MULTIPLY, NULL, int_bytes_multiply, &PyBytes_Type, 0, &PyLong_Type, &PyBytes_Type}, {NB_INPLACE_MULTIPLY, NULL, bytes_int_multiply, &PyBytes_Type, 0, &PyBytes_Type, &PyLong_Type}, {NB_INPLACE_MULTIPLY, NULL, int_bytes_multiply, &PyBytes_Type, 0, &PyLong_Type, &PyBytes_Type}, - /* tuple * int / int * tuple: call tuple_repeat directly. - tuple_repeat returns the original when n == 1. */ + /* tuple * int / int * tuple: call _PyTuple_Repeat directly. + _PyTuple_Repeat returns the original when n == 1. */ {NB_MULTIPLY, NULL, tuple_int_multiply, &PyTuple_Type, 0, &PyTuple_Type, &PyLong_Type}, {NB_MULTIPLY, NULL, int_tuple_multiply, &PyTuple_Type, 0, &PyLong_Type, &PyTuple_Type}, {NB_INPLACE_MULTIPLY, NULL, tuple_int_multiply, &PyTuple_Type, 0, &PyTuple_Type, &PyLong_Type},