https://github.com/python/cpython/commit/7fe51ceae8513e40452829d7d3ae6ff2a2576a0b
commit: 7fe51ceae8513e40452829d7d3ae6ff2a2576a0b
branch: main
author: Pieter Eendebak <[email protected]>
committer: markshannon <[email protected]>
date: 2026-04-30T10:36:04+01:00
summary:
gh-149049: Fix jit binary op stack underflow (GH-149076)
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2026-04-28-21-19-21.gh-issue-149049.98u2Ib.rst
M Lib/test/test_capi/test_opt.py
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 79643587a60002..7118dfeed9faee 100644
--- a/Lib/test/test_capi/test_opt.py
+++ b/Lib/test/test_capi/test_opt.py
@@ -3919,6 +3919,38 @@ def testfunc(args):
expected = TIER2_THRESHOLD * (5.0 / Fraction(4))
self.assertAlmostEqual(res, float(expected))
+ def test_float_truediv_partial_float_no_stack_underflow(self):
+ # gh-149049: a speculative _GUARD_*_FLOAT for a partially-float
+ # truediv/remainder must not drop the original _BINARY_OP.
+ def truediv(args):
+ n, = args
+ nan = float("nan")
+ def victim(a=0, b=nan, c=2):
+ return (a + b) / c
+ for _ in range(n):
+ victim()
+
+ def remainder(args):
+ n, = args
+ nan = float("nan")
+ def victim(a=0, b=nan, c=2):
+ return (a + b) % c
+ for _ in range(n):
+ victim()
+
+ for testfunc in (truediv, remainder):
+ with self.subTest(op=testfunc.__name__):
+ # Iterations must be high enough that the buggy trace
+ # is not only built but executed (where it underflows).
+ _, ex = self._run_with_optimizer(
+ testfunc, (TIER2_THRESHOLD * 10,))
+ self.assertIsNotNone(ex)
+ uops = get_opnames(ex)
+ self.assertTrue(
+ "_GUARD_TOS_FLOAT" in uops or "_GUARD_NOS_FLOAT" in uops,
+ uops,
+ )
+
def test_int_add_inplace_unique_lhs(self):
# a * b produces a unique compact int; adding c reuses it in place
def testfunc(args):
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-28-21-19-21.gh-issue-149049.98u2Ib.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-28-21-19-21.gh-issue-149049.98u2Ib.rst
new file mode 100644
index 00000000000000..4c8f7e08a44859
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-28-21-19-21.gh-issue-149049.98u2Ib.rst
@@ -0,0 +1 @@
+Fix stack underflow for ``BINARY_OP`` in tier 2.
diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c
index ae9e19341441ea..15d4d0bc1818f5 100644
--- a/Python/optimizer_bytecodes.c
+++ b/Python/optimizer_bytecodes.c
@@ -293,6 +293,7 @@ dummy_func(void) {
|| oparg == NB_INPLACE_TRUE_DIVIDE);
bool is_remainder = (oparg == NB_REMAINDER
|| oparg == NB_INPLACE_REMAINDER);
+ int emit_op = _BINARY_OP;
// Promote probable-float operands to known floats via speculative
// guards. _RECORD_TOS_TYPE / _RECORD_NOS_TYPE in the BINARY_OP macro
// record the observed operand type during tracing, which
@@ -318,17 +319,17 @@ dummy_func(void) {
}
if (is_truediv && lhs_float && rhs_float) {
if (PyJitRef_IsUnique(lhs)) {
- ADD_OP(_BINARY_OP_TRUEDIV_FLOAT_INPLACE, 0, 0);
+ emit_op = _BINARY_OP_TRUEDIV_FLOAT_INPLACE;
l = sym_new_null(ctx);
r = rhs;
}
else if (PyJitRef_IsUnique(rhs)) {
- ADD_OP(_BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT, 0, 0);
+ emit_op = _BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT;
l = lhs;
r = sym_new_null(ctx);
}
else {
- ADD_OP(_BINARY_OP_TRUEDIV_FLOAT, 0, 0);
+ emit_op = _BINARY_OP_TRUEDIV_FLOAT;
l = lhs;
r = rhs;
}
@@ -382,6 +383,7 @@ dummy_func(void) {
else {
res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type));
}
+ ADD_OP(emit_op, oparg, 0);
}
op(_BINARY_OP_ADD_INT, (left, right -- res, l, r)) {
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index d48f38a95f7b16..b09aca910fc1ee 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -5242,6 +5242,7 @@
|| oparg == NB_INPLACE_TRUE_DIVIDE);
bool is_remainder = (oparg == NB_REMAINDER
|| oparg == NB_INPLACE_REMAINDER);
+ int emit_op = _BINARY_OP;
if (is_truediv || is_remainder) {
if (!sym_has_type(rhs)
&& sym_get_probable_type(rhs) == &PyFloat_Type) {
@@ -5258,17 +5259,17 @@
}
if (is_truediv && lhs_float && rhs_float) {
if (PyJitRef_IsUnique(lhs)) {
- ADD_OP(_BINARY_OP_TRUEDIV_FLOAT_INPLACE, 0, 0);
+ emit_op = _BINARY_OP_TRUEDIV_FLOAT_INPLACE;
l = sym_new_null(ctx);
r = rhs;
}
else if (PyJitRef_IsUnique(rhs)) {
- ADD_OP(_BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT, 0, 0);
+ emit_op = _BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT;
l = lhs;
r = sym_new_null(ctx);
}
else {
- ADD_OP(_BINARY_OP_TRUEDIV_FLOAT, 0, 0);
+ emit_op = _BINARY_OP_TRUEDIV_FLOAT;
l = lhs;
r = rhs;
}
@@ -5304,6 +5305,7 @@
else {
res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type));
}
+ ADD_OP(emit_op, oparg, 0);
CHECK_STACK_BOUNDS(1);
stack_pointer[-2] = res;
stack_pointer[-1] = l;
_______________________________________________
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]