Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions Lib/test/test_capi/test_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix stack underflow for ``BINARY_OP`` in tier 2.
8 changes: 5 additions & 3 deletions Python/optimizer_bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
}
Expand Down Expand Up @@ -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)) {
Expand Down
8 changes: 5 additions & 3 deletions Python/optimizer_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading