https://github.com/python/cpython/commit/b98c065756ea336707c30885831e4afcb85aac84
commit: b98c065756ea336707c30885831e4afcb85aac84
branch: main
author: Hai Zhu <[email protected]>
committer: markshannon <[email protected]>
date: 2026-09-30T14:18:13+01:00
summary:

gh-158380: Fix the stack operand used by JIT dict access guards (GH-158381)

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-09-29-07-28-20.gh-issue-158380.F0J0PO.rst
M Include/internal/pycore_uop_ids.h
M Include/internal/pycore_uop_metadata.h
M Lib/test/test_capi/test_opt.py
M Python/bytecodes.c
M Python/executor_cases.c.h
M Python/optimizer_bytecodes.c
M Python/optimizer_cases.c.h

diff --git a/Include/internal/pycore_uop_ids.h 
b/Include/internal/pycore_uop_ids.h
index 625c33a6640b85..840a32f0e4481f 100644
--- a/Include/internal/pycore_uop_ids.h
+++ b/Include/internal/pycore_uop_ids.h
@@ -184,6 +184,7 @@ enum {
     _GUARD_NOS_NULL,
     _GUARD_NOS_OVERFLOWED,
     _GUARD_NOS_TUPLE,
+    _GUARD_NOS_TYPE,
     _GUARD_NOS_TYPE_VERSION,
     _GUARD_NOS_UNICODE,
     _GUARD_NOT_EXHAUSTED_LIST,
@@ -207,7 +208,6 @@ enum {
     _GUARD_TOS_TUPLE,
     _GUARD_TOS_UNICODE,
     _GUARD_TYPE,
-    _GUARD_TYPE_ITER,
     _GUARD_TYPE_VERSION,
     _GUARD_TYPE_VERSION_LOCKED,
     _HANDLE_PENDING_AND_DEOPT,
@@ -891,6 +891,10 @@ enum {
     _GUARD_NOS_TUPLE_r12,
     _GUARD_NOS_TUPLE_r22,
     _GUARD_NOS_TUPLE_r33,
+    _GUARD_NOS_TYPE_r02,
+    _GUARD_NOS_TYPE_r12,
+    _GUARD_NOS_TYPE_r22,
+    _GUARD_NOS_TYPE_r33,
     _GUARD_NOS_TYPE_VERSION_r02,
     _GUARD_NOS_TYPE_VERSION_r12,
     _GUARD_NOS_TYPE_VERSION_r22,
@@ -983,10 +987,6 @@ enum {
     _GUARD_TYPE_r11,
     _GUARD_TYPE_r22,
     _GUARD_TYPE_r33,
-    _GUARD_TYPE_ITER_r02,
-    _GUARD_TYPE_ITER_r12,
-    _GUARD_TYPE_ITER_r22,
-    _GUARD_TYPE_ITER_r33,
     _GUARD_TYPE_VERSION_r01,
     _GUARD_TYPE_VERSION_r11,
     _GUARD_TYPE_VERSION_r22,
diff --git a/Include/internal/pycore_uop_metadata.h 
b/Include/internal/pycore_uop_metadata.h
index f464effe23bc0b..1c440fe9f1e978 100644
--- a/Include/internal/pycore_uop_metadata.h
+++ b/Include/internal/pycore_uop_metadata.h
@@ -265,7 +265,7 @@ const uint32_t _PyUop_Flags[MAX_UOP_ID+1] = {
     [_PUSH_TAGGED_ZERO] = 0,
     [_GET_ITER_TRAD] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
     [_FOR_ITER_TIER_TWO] = HAS_EXIT_FLAG | HAS_ERROR_FLAG | 
HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG,
-    [_GUARD_TYPE_ITER] = HAS_EXIT_FLAG,
+    [_GUARD_NOS_TYPE] = HAS_EXIT_FLAG,
     [_ITER_NEXT_INLINE] = HAS_EXIT_FLAG | HAS_ERROR_FLAG | 
HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG,
     [_GUARD_NOS_ITER_VIRTUAL] = HAS_EXIT_FLAG,
     [_GUARD_TOS_NOT_NULL] = HAS_EXIT_FLAG,
@@ -2515,13 +2515,13 @@ const _PyUopCachingInfo _PyUop_Caching[MAX_UOP_ID+1] = {
             { -1, -1, -1 },
         },
     },
-    [_GUARD_TYPE_ITER] = {
+    [_GUARD_NOS_TYPE] = {
         .best = { 0, 1, 2, 3 },
         .entries = {
-            { 2, 0, _GUARD_TYPE_ITER_r02 },
-            { 2, 1, _GUARD_TYPE_ITER_r12 },
-            { 2, 2, _GUARD_TYPE_ITER_r22 },
-            { 3, 3, _GUARD_TYPE_ITER_r33 },
+            { 2, 0, _GUARD_NOS_TYPE_r02 },
+            { 2, 1, _GUARD_NOS_TYPE_r12 },
+            { 2, 2, _GUARD_NOS_TYPE_r22 },
+            { 3, 3, _GUARD_NOS_TYPE_r33 },
         },
     },
     [_ITER_NEXT_INLINE] = {
@@ -4446,10 +4446,10 @@ const uint16_t _PyUop_Uncached[MAX_UOP_REGS_ID+1] = {
     [_PUSH_TAGGED_ZERO_r23] = _PUSH_TAGGED_ZERO,
     [_GET_ITER_TRAD_r12] = _GET_ITER_TRAD,
     [_FOR_ITER_TIER_TWO_r23] = _FOR_ITER_TIER_TWO,
-    [_GUARD_TYPE_ITER_r02] = _GUARD_TYPE_ITER,
-    [_GUARD_TYPE_ITER_r12] = _GUARD_TYPE_ITER,
-    [_GUARD_TYPE_ITER_r22] = _GUARD_TYPE_ITER,
-    [_GUARD_TYPE_ITER_r33] = _GUARD_TYPE_ITER,
+    [_GUARD_NOS_TYPE_r02] = _GUARD_NOS_TYPE,
+    [_GUARD_NOS_TYPE_r12] = _GUARD_NOS_TYPE,
+    [_GUARD_NOS_TYPE_r22] = _GUARD_NOS_TYPE,
+    [_GUARD_NOS_TYPE_r33] = _GUARD_NOS_TYPE,
     [_ITER_NEXT_INLINE_r23] = _ITER_NEXT_INLINE,
     [_GUARD_NOS_ITER_VIRTUAL_r02] = _GUARD_NOS_ITER_VIRTUAL,
     [_GUARD_NOS_ITER_VIRTUAL_r12] = _GUARD_NOS_ITER_VIRTUAL,
@@ -5475,6 +5475,11 @@ const char *const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1] 
= {
     [_GUARD_NOS_TUPLE_r12] = "_GUARD_NOS_TUPLE_r12",
     [_GUARD_NOS_TUPLE_r22] = "_GUARD_NOS_TUPLE_r22",
     [_GUARD_NOS_TUPLE_r33] = "_GUARD_NOS_TUPLE_r33",
+    [_GUARD_NOS_TYPE] = "_GUARD_NOS_TYPE",
+    [_GUARD_NOS_TYPE_r02] = "_GUARD_NOS_TYPE_r02",
+    [_GUARD_NOS_TYPE_r12] = "_GUARD_NOS_TYPE_r12",
+    [_GUARD_NOS_TYPE_r22] = "_GUARD_NOS_TYPE_r22",
+    [_GUARD_NOS_TYPE_r33] = "_GUARD_NOS_TYPE_r33",
     [_GUARD_NOS_TYPE_VERSION] = "_GUARD_NOS_TYPE_VERSION",
     [_GUARD_NOS_TYPE_VERSION_r02] = "_GUARD_NOS_TYPE_VERSION_r02",
     [_GUARD_NOS_TYPE_VERSION_r12] = "_GUARD_NOS_TYPE_VERSION_r12",
@@ -5590,11 +5595,6 @@ const char *const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1] 
= {
     [_GUARD_TYPE_r11] = "_GUARD_TYPE_r11",
     [_GUARD_TYPE_r22] = "_GUARD_TYPE_r22",
     [_GUARD_TYPE_r33] = "_GUARD_TYPE_r33",
-    [_GUARD_TYPE_ITER] = "_GUARD_TYPE_ITER",
-    [_GUARD_TYPE_ITER_r02] = "_GUARD_TYPE_ITER_r02",
-    [_GUARD_TYPE_ITER_r12] = "_GUARD_TYPE_ITER_r12",
-    [_GUARD_TYPE_ITER_r22] = "_GUARD_TYPE_ITER_r22",
-    [_GUARD_TYPE_ITER_r33] = "_GUARD_TYPE_ITER_r33",
     [_GUARD_TYPE_VERSION] = "_GUARD_TYPE_VERSION",
     [_GUARD_TYPE_VERSION_r01] = "_GUARD_TYPE_VERSION_r01",
     [_GUARD_TYPE_VERSION_r11] = "_GUARD_TYPE_VERSION_r11",
@@ -6626,7 +6626,7 @@ int _PyUop_num_popped(int opcode, int oparg)
             return 1;
         case _FOR_ITER_TIER_TWO:
             return 0;
-        case _GUARD_TYPE_ITER:
+        case _GUARD_NOS_TYPE:
             return 0;
         case _ITER_NEXT_INLINE:
             return 0;
diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py
index ca0545dba264f2..31440c8e609213 100644
--- a/Lib/test/test_capi/test_opt.py
+++ b/Lib/test/test_capi/test_opt.py
@@ -2445,7 +2445,18 @@ def f(n):
         self.assertEqual(uops.count("_STORE_SUBSCR_DICT_KNOWN_HASH"), 1)
         self.assertEqual(uops.count("_GUARD_NOS_DICT_SUBSCRIPT"), 0)
         self.assertEqual(uops.count("_GUARD_NOS_DICT_STORE_SUBSCRIPT"), 0)
-        self.assertEqual(uops.count("_GUARD_TYPE"), 1)
+        self.assertEqual(uops.count("_GUARD_NOS_TYPE"), 1)
+
+    def test_dict_subscr_probable_type(self):
+        def f(d):
+            for _ in range(TIER2_THRESHOLD):
+                value = d["key"]
+            return value
+
+        res, ex = self._run_with_optimizer(f, {"key": 1})
+        self.assertEqual(res, 1)
+        self.assertIsNotNone(ex)
+        self.assertIn("_GUARD_NOS_TYPE", get_opnames(ex))
 
     def test_dict_subclass_subscr_with_override(self):
         class MyDict(dict):
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-09-29-07-28-20.gh-issue-158380.F0J0PO.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-29-07-28-20.gh-issue-158380.F0J0PO.rst
new file mode 100644
index 00000000000000..2b3f1d9301e733
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-29-07-28-20.gh-issue-158380.F0J0PO.rst
@@ -0,0 +1 @@
+Fix incorrect type guards for ``dict`` reads and writes in the experimental 
JIT.
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index fdfd5a0e609046..2ca52766c110b9 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -3846,9 +3846,9 @@ dummy_func(
             next = item;
         }
 
-        tier2 op(_GUARD_TYPE_ITER, (expected_type/4, iter, null_or_index -- 
iter, null_or_index)) {
-            PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter);
-            EXIT_IF(Py_TYPE(iter_o) != (PyTypeObject *)expected_type);
+        tier2 op(_GUARD_NOS_TYPE, (expected_type/4, nos, unused -- nos, 
unused)) {
+            PyObject *nos_o = PyStackRef_AsPyObjectBorrow(nos);
+            EXIT_IF(Py_TYPE(nos_o) != (PyTypeObject *)expected_type);
         }
 
         tier2 op(_ITER_NEXT_INLINE, (iternext_fn/4, iter, null_or_index -- 
iter, null_or_index, next)) {
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index e3dc2e781a3beb..0045296f1bc868 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -14816,20 +14816,20 @@
             break;
         }
 
-        case _GUARD_TYPE_ITER_r02: {
+        case _GUARD_NOS_TYPE_r02: {
             CHECK_CURRENT_CACHED_VALUES(0);
             ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__);
-            _PyStackRef iter;
-            iter = stack_pointer[-2];
+            _PyStackRef nos;
+            nos = stack_pointer[-2];
             PyObject *expected_type = (PyObject *)CURRENT_OPERAND0_64();
-            PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter);
-            if (Py_TYPE(iter_o) != (PyTypeObject *)expected_type) {
+            PyObject *nos_o = PyStackRef_AsPyObjectBorrow(nos);
+            if (Py_TYPE(nos_o) != (PyTypeObject *)expected_type) {
                 UOP_STAT_INC(uopcode, miss);
                 SET_CURRENT_CACHED_VALUES(0);
                 JUMP_TO_JUMP_TARGET();
             }
             _tos_cache1 = stack_pointer[-1];
-            _tos_cache0 = iter;
+            _tos_cache0 = nos;
             SET_CURRENT_CACHED_VALUES(2);
             stack_pointer += -2;
             ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
@@ -14837,22 +14837,22 @@
             break;
         }
 
-        case _GUARD_TYPE_ITER_r12: {
+        case _GUARD_NOS_TYPE_r12: {
             CHECK_CURRENT_CACHED_VALUES(1);
             ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__);
-            _PyStackRef iter;
+            _PyStackRef nos;
             _PyStackRef _stack_item_0 = _tos_cache0;
-            iter = stack_pointer[-1];
+            nos = stack_pointer[-1];
             PyObject *expected_type = (PyObject *)CURRENT_OPERAND0_64();
-            PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter);
-            if (Py_TYPE(iter_o) != (PyTypeObject *)expected_type) {
+            PyObject *nos_o = PyStackRef_AsPyObjectBorrow(nos);
+            if (Py_TYPE(nos_o) != (PyTypeObject *)expected_type) {
                 UOP_STAT_INC(uopcode, miss);
                 _tos_cache0 = _stack_item_0;
                 SET_CURRENT_CACHED_VALUES(1);
                 JUMP_TO_JUMP_TARGET();
             }
             _tos_cache1 = _stack_item_0;
-            _tos_cache0 = iter;
+            _tos_cache0 = nos;
             SET_CURRENT_CACHED_VALUES(2);
             stack_pointer += -1;
             ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
@@ -14860,49 +14860,49 @@
             break;
         }
 
-        case _GUARD_TYPE_ITER_r22: {
+        case _GUARD_NOS_TYPE_r22: {
             CHECK_CURRENT_CACHED_VALUES(2);
             ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__);
-            _PyStackRef iter;
+            _PyStackRef nos;
             _PyStackRef _stack_item_0 = _tos_cache0;
             _PyStackRef _stack_item_1 = _tos_cache1;
-            iter = _stack_item_0;
+            nos = _stack_item_0;
             PyObject *expected_type = (PyObject *)CURRENT_OPERAND0_64();
-            PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter);
-            if (Py_TYPE(iter_o) != (PyTypeObject *)expected_type) {
+            PyObject *nos_o = PyStackRef_AsPyObjectBorrow(nos);
+            if (Py_TYPE(nos_o) != (PyTypeObject *)expected_type) {
                 UOP_STAT_INC(uopcode, miss);
                 _tos_cache1 = _stack_item_1;
-                _tos_cache0 = iter;
+                _tos_cache0 = nos;
                 SET_CURRENT_CACHED_VALUES(2);
                 JUMP_TO_JUMP_TARGET();
             }
             _tos_cache1 = _stack_item_1;
-            _tos_cache0 = iter;
+            _tos_cache0 = nos;
             SET_CURRENT_CACHED_VALUES(2);
             ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__);
             break;
         }
 
-        case _GUARD_TYPE_ITER_r33: {
+        case _GUARD_NOS_TYPE_r33: {
             CHECK_CURRENT_CACHED_VALUES(3);
             ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__);
-            _PyStackRef iter;
+            _PyStackRef nos;
             _PyStackRef _stack_item_0 = _tos_cache0;
             _PyStackRef _stack_item_1 = _tos_cache1;
             _PyStackRef _stack_item_2 = _tos_cache2;
-            iter = _stack_item_1;
+            nos = _stack_item_1;
             PyObject *expected_type = (PyObject *)CURRENT_OPERAND0_64();
-            PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter);
-            if (Py_TYPE(iter_o) != (PyTypeObject *)expected_type) {
+            PyObject *nos_o = PyStackRef_AsPyObjectBorrow(nos);
+            if (Py_TYPE(nos_o) != (PyTypeObject *)expected_type) {
                 UOP_STAT_INC(uopcode, miss);
                 _tos_cache2 = _stack_item_2;
-                _tos_cache1 = iter;
+                _tos_cache1 = nos;
                 _tos_cache0 = _stack_item_0;
                 SET_CURRENT_CACHED_VALUES(3);
                 JUMP_TO_JUMP_TARGET();
             }
             _tos_cache2 = _stack_item_2;
-            _tos_cache1 = iter;
+            _tos_cache1 = nos;
             _tos_cache0 = _stack_item_0;
             SET_CURRENT_CACHED_VALUES(3);
             ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__);
diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c
index 5246e50633461b..54b01e1ffbd322 100644
--- a/Python/optimizer_bytecodes.c
+++ b/Python/optimizer_bytecodes.c
@@ -1472,7 +1472,7 @@ dummy_func(void) {
                 sym_set_type(iter, type);
                 assert((this_instr - 1)->opcode == _RECORD_NOS_TYPE);
                 int32_t orig_target = (this_instr - 1)->target;
-                ADD_OP(_GUARD_TYPE_ITER, 0, (uintptr_t)type);
+                ADD_OP(_GUARD_NOS_TYPE, 0, (uintptr_t)type);
                 uop_buffer_last(&ctx->out_buffer)->target = orig_target;
             }
             ADD_OP(_ITER_NEXT_INLINE, 0, (uintptr_t)type->tp_iternext);
@@ -2216,7 +2216,7 @@ dummy_func(void) {
                 ADD_OP(_NOP, 0, 0);
             }
             else {
-                ADD_OP(_GUARD_TYPE, 0, (uintptr_t)tp);
+                ADD_OP(_GUARD_NOS_TYPE, 0, (uintptr_t)tp);
                 sym_set_type(nos, tp);
             }
             PyType_Watch(TYPE_WATCHER_ID, (PyObject *)tp);
@@ -2237,7 +2237,7 @@ dummy_func(void) {
                 ADD_OP(_NOP, 0, 0);
             }
             else {
-                ADD_OP(_GUARD_TYPE, 0, (uintptr_t)tp);
+                ADD_OP(_GUARD_NOS_TYPE, 0, (uintptr_t)tp);
                 sym_set_type(nos, tp);
             }
             PyType_Watch(TYPE_WATCHER_ID, (PyObject *)tp);
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index 21f275f27cafe0..73a86bfe86dacd 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -1500,7 +1500,7 @@
                     ADD_OP(_NOP, 0, 0);
                 }
                 else {
-                    ADD_OP(_GUARD_TYPE, 0, (uintptr_t)tp);
+                    ADD_OP(_GUARD_NOS_TYPE, 0, (uintptr_t)tp);
                     sym_set_type(nos, tp);
                 }
                 PyType_Watch(TYPE_WATCHER_ID, (PyObject *)tp);
@@ -1524,7 +1524,7 @@
                     ADD_OP(_NOP, 0, 0);
                 }
                 else {
-                    ADD_OP(_GUARD_TYPE, 0, (uintptr_t)tp);
+                    ADD_OP(_GUARD_NOS_TYPE, 0, (uintptr_t)tp);
                     sym_set_type(nos, tp);
                 }
                 PyType_Watch(TYPE_WATCHER_ID, (PyObject *)tp);
@@ -3705,7 +3705,7 @@
                     sym_set_type(iter, type);
                     assert((this_instr - 1)->opcode == _RECORD_NOS_TYPE);
                     int32_t orig_target = (this_instr - 1)->target;
-                    ADD_OP(_GUARD_TYPE_ITER, 0, (uintptr_t)type);
+                    ADD_OP(_GUARD_NOS_TYPE, 0, (uintptr_t)type);
                     uop_buffer_last(&ctx->out_buffer)->target = orig_target;
                 }
                 ADD_OP(_ITER_NEXT_INLINE, 0, (uintptr_t)type->tp_iternext);
@@ -3718,7 +3718,7 @@
             break;
         }
 
-        case _GUARD_TYPE_ITER: {
+        case _GUARD_NOS_TYPE: {
             break;
         }
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to