https://github.com/python/cpython/commit/89e6607e051ad4ffae8878cee1e0e6d72c618568
commit: 89e6607e051ad4ffae8878cee1e0e6d72c618568
branch: main
author: Hai Zhu <[email protected]>
committer: markshannon <[email protected]>
date: 2026-02-02T17:12:01Z
summary:
gh-139109: Replace `_CHECK_STACK_SPACE` with `_CHECK_STACK_SPACE_OPERAND` in
JIT optiimizer (GH-144394)
files:
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/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py
index a379d1be2f9bd3..437cc340fc90e3 100644
--- a/Lib/test/test_capi/test_opt.py
+++ b/Lib/test/test_capi/test_opt.py
@@ -1022,7 +1022,6 @@ def return_hello():
# 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 @@ def testfunc(n):
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 @@ def testfunc(n):
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 @@ def testfunc(n):
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 @@ def testfunc(n):
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 @@ def testfunc(n):
("_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 @@ def testfunc(n):
("_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:
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 9e41382240ea5d..a990ab28577c73 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -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, (--)) {
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index 339789b61d86aa..9c82f1acdef493 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -18265,11 +18265,6 @@
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;
@@ -18287,12 +18282,6 @@
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());
@@ -18313,13 +18302,6 @@
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);
@@ -18343,14 +18325,6 @@
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;
diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c
index 1f6a64026d8e15..89c6707160326c 100644
--- a/Python/optimizer_bytecodes.c
+++ b/Python/optimizer_bytecodes.c
@@ -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 -- )) {
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index e7b31ad6ac02c3..61a30314c21789 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -2996,6 +2996,13 @@
}
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;
}
@@ -3899,7 +3906,6 @@
case _CHECK_STACK_SPACE_OPERAND: {
uint32_t framesize = (uint32_t)this_instr->operand0;
(void)framesize;
- Py_UNREACHABLE();
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]