]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.15] gh-149459: Fix segfault when `_LOAD_SPECIAL` guard deoptimizes (GH-149478...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Fri, 8 May 2026 11:48:04 +0000 (13:48 +0200)
committerGitHub <noreply@github.com>
Fri, 8 May 2026 11:48:04 +0000 (11:48 +0000)
gh-149459: Fix segfault when `_LOAD_SPECIAL` guard deoptimizes (GH-149478)
(cherry picked from commit c341e341b25cec03d28d1b2c368bb871d76ca88b)

Co-authored-by: Hai Zhu <haiizhu@outlook.com>
Lib/test/test_capi/test_opt.py
Misc/NEWS.d/next/Core_and_Builtins/2026-05-07-03-18-59.gh-issue-149459.5fhAqP.rst [new file with mode: 0644]
Python/optimizer_bytecodes.c
Python/optimizer_cases.c.h

index aaa5050208ced9a0a6912baa2779651a6a016833..2f606c2c6eba2d682a87413e0a9de5b131f57afe 100644 (file)
@@ -6138,6 +6138,20 @@ class TestUopsOptimization(unittest.TestCase):
                 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 (file)
index 0000000..4cd0a14
--- /dev/null
@@ -0,0 +1 @@
+Fix a crash in the JIT optimizer when a specialized ``LOAD_SPECIAL`` guard deoptimized after inserting the synthetic ``NULL`` stack entry.
index 39cc36ae79fead977269dd71de98f38322d91936..96dbaea5a5797ef1e8bf794c72e1cc07525d12d2 100644 (file)
@@ -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);
index db3dcbb97b2645c62756bafcd74102f727e9e402..f336549d2ed24407f95c5ade7940f450f474ed0e 100644 (file)
                 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);