]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-139109: Replace `_CHECK_STACK_SPACE` with `_CHECK_STACK_SPACE_OPERAND` in JIT...
authorHai Zhu <haiizhu@outlook.com>
Mon, 2 Feb 2026 17:12:01 +0000 (01:12 +0800)
committerGitHub <noreply@github.com>
Mon, 2 Feb 2026 17:12:01 +0000 (17:12 +0000)
Lib/test/test_capi/test_opt.py
Python/bytecodes.c
Python/executor_cases.c.h
Python/optimizer_bytecodes.c
Python/optimizer_cases.c.h

index a379d1be2f9bd3f71565253f314c2f86f02b5cd1..437cc340fc90e3b67992fb7778769ba62d2ffa42 100644 (file)
@@ -1022,7 +1022,6 @@ class TestUopsOptimization(unittest.TestCase):
         # Constant narrowing allows constant folding for second comparison
         self.assertLessEqual(count_ops(ex, "_COMPARE_OP_STR"), 1)
 
-    @unittest.skip("gh-139109 WIP")
     def test_combine_stack_space_checks_sequential(self):
         def dummy12(x):
             return x - 1
@@ -1046,12 +1045,14 @@ class TestUopsOptimization(unittest.TestCase):
         self.assertEqual(uop_names.count("_PUSH_FRAME"), 2)
         self.assertEqual(uop_names.count("_RETURN_VALUE"), 2)
         self.assertEqual(uop_names.count("_CHECK_STACK_SPACE"), 0)
-        self.assertEqual(uop_names.count("_CHECK_STACK_SPACE_OPERAND"), 1)
-        # sequential calls: max(12, 13) == 13
-        largest_stack = _testinternalcapi.get_co_framesize(dummy13.__code__)
-        self.assertIn(("_CHECK_STACK_SPACE_OPERAND", largest_stack), uops_and_operands)
+        # Each call gets its own _CHECK_STACK_SPACE_OPERAND
+        self.assertEqual(uop_names.count("_CHECK_STACK_SPACE_OPERAND"), 2)
+        # Each _CHECK_STACK_SPACE_OPERAND has the framesize of its function
+        self.assertIn(("_CHECK_STACK_SPACE_OPERAND",
+                       _testinternalcapi.get_co_framesize(dummy12.__code__)), uops_and_operands)
+        self.assertIn(("_CHECK_STACK_SPACE_OPERAND",
+                       _testinternalcapi.get_co_framesize(dummy13.__code__)), uops_and_operands)
 
-    @unittest.skip("gh-139109 WIP")
     def test_combine_stack_space_checks_nested(self):
         def dummy12(x):
             return x + 3
@@ -1074,15 +1075,12 @@ class TestUopsOptimization(unittest.TestCase):
         self.assertEqual(uop_names.count("_PUSH_FRAME"), 2)
         self.assertEqual(uop_names.count("_RETURN_VALUE"), 2)
         self.assertEqual(uop_names.count("_CHECK_STACK_SPACE"), 0)
-        self.assertEqual(uop_names.count("_CHECK_STACK_SPACE_OPERAND"), 1)
-        # nested calls: 15 + 12 == 27
-        largest_stack = (
-            _testinternalcapi.get_co_framesize(dummy15.__code__) +
-            _testinternalcapi.get_co_framesize(dummy12.__code__)
-        )
-        self.assertIn(("_CHECK_STACK_SPACE_OPERAND", largest_stack), uops_and_operands)
+        self.assertEqual(uop_names.count("_CHECK_STACK_SPACE_OPERAND"), 2)
+        self.assertIn(("_CHECK_STACK_SPACE_OPERAND",
+                       _testinternalcapi.get_co_framesize(dummy15.__code__)), uops_and_operands)
+        self.assertIn(("_CHECK_STACK_SPACE_OPERAND",
+                       _testinternalcapi.get_co_framesize(dummy12.__code__)), uops_and_operands)
 
-    @unittest.skip("gh-139109 WIP")
     def test_combine_stack_space_checks_several_calls(self):
         def dummy12(x):
             return x + 3
@@ -1110,15 +1108,14 @@ class TestUopsOptimization(unittest.TestCase):
         self.assertEqual(uop_names.count("_PUSH_FRAME"), 4)
         self.assertEqual(uop_names.count("_RETURN_VALUE"), 4)
         self.assertEqual(uop_names.count("_CHECK_STACK_SPACE"), 0)
-        self.assertEqual(uop_names.count("_CHECK_STACK_SPACE_OPERAND"), 1)
-        # max(12, 18 + max(12, 13)) == 31
-        largest_stack = (
-            _testinternalcapi.get_co_framesize(dummy18.__code__) +
-            _testinternalcapi.get_co_framesize(dummy13.__code__)
-        )
-        self.assertIn(("_CHECK_STACK_SPACE_OPERAND", largest_stack), uops_and_operands)
+        self.assertEqual(uop_names.count("_CHECK_STACK_SPACE_OPERAND"), 4)
+        self.assertIn(("_CHECK_STACK_SPACE_OPERAND",
+                       _testinternalcapi.get_co_framesize(dummy12.__code__)), uops_and_operands)
+        self.assertIn(("_CHECK_STACK_SPACE_OPERAND",
+                       _testinternalcapi.get_co_framesize(dummy13.__code__)), uops_and_operands)
+        self.assertIn(("_CHECK_STACK_SPACE_OPERAND",
+                       _testinternalcapi.get_co_framesize(dummy18.__code__)), uops_and_operands)
 
-    @unittest.skip("gh-139109 WIP")
     def test_combine_stack_space_checks_several_calls_different_order(self):
         # same as `several_calls` but with top-level calls reversed
         def dummy12(x):
@@ -1147,15 +1144,15 @@ class TestUopsOptimization(unittest.TestCase):
         self.assertEqual(uop_names.count("_PUSH_FRAME"), 4)
         self.assertEqual(uop_names.count("_RETURN_VALUE"), 4)
         self.assertEqual(uop_names.count("_CHECK_STACK_SPACE"), 0)
-        self.assertEqual(uop_names.count("_CHECK_STACK_SPACE_OPERAND"), 1)
-        # max(18 + max(12, 13), 12) == 31
-        largest_stack = (
-            _testinternalcapi.get_co_framesize(dummy18.__code__) +
-            _testinternalcapi.get_co_framesize(dummy13.__code__)
-        )
-        self.assertIn(("_CHECK_STACK_SPACE_OPERAND", largest_stack), uops_and_operands)
-
-    @unittest.skip("gh-139109 WIP")
+        self.assertEqual(uop_names.count("_CHECK_STACK_SPACE_OPERAND"), 4)
+        self.assertIn(("_CHECK_STACK_SPACE_OPERAND",
+                       _testinternalcapi.get_co_framesize(dummy12.__code__)), uops_and_operands)
+        self.assertIn(("_CHECK_STACK_SPACE_OPERAND",
+                       _testinternalcapi.get_co_framesize(dummy13.__code__)), uops_and_operands)
+        self.assertIn(("_CHECK_STACK_SPACE_OPERAND",
+                       _testinternalcapi.get_co_framesize(dummy18.__code__)), uops_and_operands)
+
+    @unittest.skip("reopen when we combine multiple stack space checks into one")
     def test_combine_stack_space_complex(self):
         def dummy0(x):
             return x
@@ -1205,7 +1202,7 @@ class TestUopsOptimization(unittest.TestCase):
             ("_CHECK_STACK_SPACE_OPERAND", largest_stack), uops_and_operands
         )
 
-    @unittest.skip("gh-139109 WIP")
+    @unittest.skip("reopen when we combine multiple stack space checks into one")
     def test_combine_stack_space_checks_large_framesize(self):
         # Create a function with a large framesize. This ensures _CHECK_STACK_SPACE is
         # actually doing its job. Note that the resulting trace hits
@@ -1267,7 +1264,7 @@ class TestUopsOptimization(unittest.TestCase):
             ("_CHECK_STACK_SPACE_OPERAND", largest_stack), uops_and_operands
         )
 
-    @unittest.skip("gh-139109 WIP")
+    @unittest.skip("reopen when we combine multiple stack space checks into one")
     def test_combine_stack_space_checks_recursion(self):
         def dummy15(x):
             while x > 0:
index 9e41382240ea5dbfaa2c04d4c4add6cd7f24336d..a990ab28577c733f2cff4498da228b081e9a8b67 100644 (file)
@@ -5372,7 +5372,6 @@ dummy_func(
         tier2 op(_CHECK_STACK_SPACE_OPERAND, (framesize/2 --)) {
             assert(framesize <= INT_MAX);
             DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, framesize));
-            DEOPT_IF(tstate->py_recursion_remaining <= 1);
         }
 
         op(_SAVE_RETURN_OFFSET, (--)) {
index 339789b61d86aaf1cadd77282eb7e9d058f48b15..9c82f1acdef493d6c0d90e8c00c58379f80ff45d 100644 (file)
                 SET_CURRENT_CACHED_VALUES(0);
                 JUMP_TO_JUMP_TARGET();
             }
-            if (tstate->py_recursion_remaining <= 1) {
-                UOP_STAT_INC(uopcode, miss);
-                SET_CURRENT_CACHED_VALUES(0);
-                JUMP_TO_JUMP_TARGET();
-            }
             SET_CURRENT_CACHED_VALUES(0);
             assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
             break;
                 SET_CURRENT_CACHED_VALUES(1);
                 JUMP_TO_JUMP_TARGET();
             }
-            if (tstate->py_recursion_remaining <= 1) {
-                UOP_STAT_INC(uopcode, miss);
-                _tos_cache0 = _stack_item_0;
-                SET_CURRENT_CACHED_VALUES(1);
-                JUMP_TO_JUMP_TARGET();
-            }
             _tos_cache0 = _stack_item_0;
             SET_CURRENT_CACHED_VALUES(1);
             assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE());
                 SET_CURRENT_CACHED_VALUES(2);
                 JUMP_TO_JUMP_TARGET();
             }
-            if (tstate->py_recursion_remaining <= 1) {
-                UOP_STAT_INC(uopcode, miss);
-                _tos_cache1 = _stack_item_1;
-                _tos_cache0 = _stack_item_0;
-                SET_CURRENT_CACHED_VALUES(2);
-                JUMP_TO_JUMP_TARGET();
-            }
             _tos_cache1 = _stack_item_1;
             _tos_cache0 = _stack_item_0;
             SET_CURRENT_CACHED_VALUES(2);
                 SET_CURRENT_CACHED_VALUES(3);
                 JUMP_TO_JUMP_TARGET();
             }
-            if (tstate->py_recursion_remaining <= 1) {
-                UOP_STAT_INC(uopcode, miss);
-                _tos_cache2 = _stack_item_2;
-                _tos_cache1 = _stack_item_1;
-                _tos_cache0 = _stack_item_0;
-                SET_CURRENT_CACHED_VALUES(3);
-                JUMP_TO_JUMP_TARGET();
-            }
             _tos_cache2 = _stack_item_2;
             _tos_cache1 = _stack_item_1;
             _tos_cache0 = _stack_item_0;
index 1f6a64026d8e153df63c12b0a58ff7890d0fa147..89c6707160326c9487b4c8391500b3e41351f42f 100644 (file)
@@ -1068,13 +1068,17 @@ dummy_func(void) {
     }
 
     op(_CHECK_STACK_SPACE, (unused, unused, unused[oparg] -- unused, unused, unused[oparg])) {
+        assert((this_instr + 4)->opcode == _PUSH_FRAME);
+        PyCodeObject *co = get_code_with_logging((this_instr + 4));
+        if (co == NULL) {
+            ctx->done = true;
+            break;
+        }
+        ADD_OP(_CHECK_STACK_SPACE_OPERAND, 0, co->co_framesize);
     }
 
     op (_CHECK_STACK_SPACE_OPERAND, (framesize/2 -- )) {
         (void)framesize;
-        /* We should never see _CHECK_STACK_SPACE_OPERANDs.
-        * They are only created at the end of this pass. */
-        Py_UNREACHABLE();
     }
 
     op(_PUSH_FRAME, (new_frame -- )) {
index e7b31ad6ac02c3fcb42f5bb55f9ed32a41739e55..61a30314c217892a403a750529657f1c7d0a8038 100644 (file)
         }
 
         case _CHECK_STACK_SPACE: {
+            assert((this_instr + 4)->opcode == _PUSH_FRAME);
+            PyCodeObject *co = get_code_with_logging((this_instr + 4));
+            if (co == NULL) {
+                ctx->done = true;
+                break;
+            }
+            ADD_OP(_CHECK_STACK_SPACE_OPERAND, 0, co->co_framesize);
             break;
         }
 
         case _CHECK_STACK_SPACE_OPERAND: {
             uint32_t framesize = (uint32_t)this_instr->operand0;
             (void)framesize;
-            Py_UNREACHABLE();
             break;
         }