Skip to content

Commit 9a8c143

Browse files
Merge branch 'jit_float_truediv' of github.com:eendebakpt/cpython into pr/146397
2 parents 44666f7 + e7fdb04 commit 9a8c143

File tree

6 files changed

+98
-10
lines changed

6 files changed

+98
-10
lines changed

Include/internal/pycore_opcode_metadata.h

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_capi/test_opt.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3707,6 +3707,48 @@ def testfunc(args):
37073707
uops = get_opnames(ex)
37083708
self.assertIn("_BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT", uops)
37093709

3710+
def test_float_truediv_speculative_guards_from_tracing(self):
3711+
# a, b are locals with no statically known type. _RECORD_TOS /
3712+
# _RECORD_NOS (added to the BINARY_OP macro) capture the observed
3713+
# operand types during tracing, and the optimizer then speculatively
3714+
# emits _GUARD_{TOS,NOS}_FLOAT and specializes the division.
3715+
def testfunc(args):
3716+
a, b, n = args
3717+
total = 0.0
3718+
for _ in range(n):
3719+
total += a / b
3720+
return total
3721+
3722+
res, ex = self._run_with_optimizer(testfunc, (10.0, 3.0, TIER2_THRESHOLD))
3723+
self.assertAlmostEqual(res, TIER2_THRESHOLD * (10.0 / 3.0))
3724+
self.assertIsNotNone(ex)
3725+
uops = get_opnames(ex)
3726+
self.assertIn("_GUARD_TOS_FLOAT", uops)
3727+
self.assertIn("_GUARD_NOS_FLOAT", uops)
3728+
self.assertIn("_BINARY_OP_TRUEDIV_FLOAT", uops)
3729+
3730+
def test_float_remainder_speculative_guards_from_tracing(self):
3731+
# a, b are locals with no statically known type. Tracing records
3732+
# them as floats; the optimizer then speculatively emits
3733+
# _GUARD_{TOS,NOS}_FLOAT for NB_REMAINDER. That narrows both
3734+
# operands to float, and the _BINARY_OP handler marks the result
3735+
# as a unique float. Downstream, `* 2.0` therefore specializes
3736+
# to _BINARY_OP_MULTIPLY_FLOAT_INPLACE.
3737+
def testfunc(args):
3738+
a, b, n = args
3739+
total = 0.0
3740+
for _ in range(n):
3741+
total += (a % b) * 2.0
3742+
return total
3743+
3744+
res, ex = self._run_with_optimizer(testfunc, (10.0, 3.0, TIER2_THRESHOLD))
3745+
self.assertAlmostEqual(res, TIER2_THRESHOLD * (10.0 % 3.0) * 2.0)
3746+
self.assertIsNotNone(ex)
3747+
uops = get_opnames(ex)
3748+
self.assertIn("_GUARD_TOS_FLOAT", uops)
3749+
self.assertIn("_GUARD_NOS_FLOAT", uops)
3750+
self.assertIn("_BINARY_OP_MULTIPLY_FLOAT_INPLACE", uops)
3751+
37103752
def test_float_truediv_type_propagation(self):
37113753
# Test the _BINARY_OP_TRUEDIV_FLOAT propagates type information
37123754
def testfunc(args):

Python/bytecodes.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5725,7 +5725,7 @@ dummy_func(
57255725
DEAD(rhs);
57265726
}
57275727

5728-
macro(BINARY_OP) = _SPECIALIZE_BINARY_OP + unused/4 + _BINARY_OP + POP_TOP + POP_TOP;
5728+
macro(BINARY_OP) = _SPECIALIZE_BINARY_OP + _RECORD_TOS + _RECORD_NOS + unused/4 + _BINARY_OP + POP_TOP + POP_TOP;
57295729

57305730
pure replicate(2:4) inst(SWAP, (bottom, unused[oparg-2], top --
57315731
bottom, unused[oparg-2], top)) {

Python/optimizer_bytecodes.c

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,34 @@ dummy_func(void) {
289289
bool rhs_int = sym_matches_type(rhs, &PyLong_Type);
290290
bool lhs_float = sym_matches_type(lhs, &PyFloat_Type);
291291
bool rhs_float = sym_matches_type(rhs, &PyFloat_Type);
292-
if ((oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE)
293-
&& lhs_float && rhs_float) {
292+
bool is_truediv = (oparg == NB_TRUE_DIVIDE
293+
|| oparg == NB_INPLACE_TRUE_DIVIDE);
294+
bool is_remainder = (oparg == NB_REMAINDER
295+
|| oparg == NB_INPLACE_REMAINDER);
296+
// Promote probable-float operands to known floats via speculative
297+
// guards. _RECORD_TOS / _RECORD_NOS in the BINARY_OP macro record
298+
// the observed operand during tracing, which sym_get_probable_type
299+
// reads here. Applied only to ops where narrowing unlocks a
300+
// meaningful downstream win:
301+
// - NB_TRUE_DIVIDE: enables the specialized float path below.
302+
// - NB_REMAINDER: lets the float result type propagate.
303+
// NB_POWER is excluded — speculative guards there regressed
304+
// test_power_type_depends_on_input_values (GH-127844).
305+
if (is_truediv || is_remainder) {
306+
if (!sym_has_type(rhs)
307+
&& sym_get_probable_type(rhs) == &PyFloat_Type) {
308+
ADD_OP(_GUARD_TOS_FLOAT, 0, 0);
309+
sym_set_type(rhs, &PyFloat_Type);
310+
rhs_float = true;
311+
}
312+
if (!sym_has_type(lhs)
313+
&& sym_get_probable_type(lhs) == &PyFloat_Type) {
314+
ADD_OP(_GUARD_NOS_FLOAT, 0, 0);
315+
sym_set_type(lhs, &PyFloat_Type);
316+
lhs_float = true;
317+
}
318+
}
319+
if (is_truediv && lhs_float && rhs_float) {
294320
if (PyJitRef_IsUnique(lhs)) {
295321
ADD_OP(_BINARY_OP_TRUEDIV_FLOAT_INPLACE, 0, 0);
296322
l = sym_new_null(ctx);
@@ -308,7 +334,7 @@ dummy_func(void) {
308334
}
309335
res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type));
310336
}
311-
else if ((oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE)
337+
else if (is_truediv
312338
&& (lhs_int || lhs_float) && (rhs_int || rhs_float)) {
313339
res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type));
314340
}

Python/optimizer_cases.c.h

Lines changed: 20 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/record_functions.c.h

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)