Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c846269
gh-146306: Specialize float/float true division in tier 2 optimizer
eendebakpt Mar 24, 2026
97889f5
Mark results of float-producing _BINARY_OP as unique
eendebakpt Mar 24, 2026
67869e4
Fix truediv type propagation for non-numeric types
eendebakpt Mar 25, 2026
5c4e3bf
add guards
eendebakpt Mar 25, 2026
228bfa9
news entry
eendebakpt Mar 25, 2026
8bf12bf
Merge branch 'main' into jit_float_truediv
eendebakpt Mar 25, 2026
46c241e
Merge remote-tracking branch 'upstream/main' into pr/146397
Fidget-Spinner Mar 29, 2026
53dd383
avoid speculative guards
eendebakpt Mar 29, 2026
44ee7f0
fix test
eendebakpt Mar 29, 2026
e302112
Merge branch 'main' into jit_float_truediv
eendebakpt Apr 3, 2026
d2c03bc
Regenerate pycore_uop_ids.h and pycore_uop_metadata.h with truediv fl…
eendebakpt Apr 3, 2026
dba0bb3
Merge branch 'main' into jit_float_truediv
eendebakpt Apr 3, 2026
8615577
Merge branch 'main' into jit_float_truediv
eendebakpt Apr 4, 2026
e715dac
review comments
eendebakpt Apr 5, 2026
925841c
Merge branch 'main' into jit_float_truediv
eendebakpt Apr 5, 2026
43f4987
mark more returns as unique
eendebakpt Apr 6, 2026
a49018e
Merge branch 'main' into jit_float_truediv
eendebakpt Apr 6, 2026
9cab900
mark more returns as unique
eendebakpt Apr 6, 2026
b432e1a
Merge branch 'main' into jit_float_truediv
eendebakpt Apr 11, 2026
0c89654
Merge branch 'main' into jit_float_truediv
eendebakpt Apr 12, 2026
fa97ab3
Merge branch 'main' into jit_float_truediv
eendebakpt Apr 14, 2026
3ba0c85
add back recording ops and guards
eendebakpt Apr 14, 2026
e7fdb04
simplify
eendebakpt Apr 14, 2026
44666f7
Merge remote-tracking branch 'upstream/main' into pr/146397
Fidget-Spinner Apr 14, 2026
9a8c143
Merge branch 'jit_float_truediv' of github.com:eendebakpt/cpython int…
Fidget-Spinner Apr 14, 2026
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
Prev Previous commit
Next Next commit
add back recording ops and guards
  • Loading branch information
eendebakpt committed Apr 14, 2026
commit 3ba0c85add2009c850f5f65a2b42d2b62ed4e56f
4 changes: 2 additions & 2 deletions Include/internal/pycore_opcode_metadata.h

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

42 changes: 42 additions & 0 deletions Lib/test/test_capi/test_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -3688,6 +3688,48 @@ def testfunc(args):
uops = get_opnames(ex)
self.assertIn("_BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT", uops)

def test_float_truediv_speculative_guards_from_tracing(self):
# a, b are locals with no statically known type. _RECORD_TOS /
# _RECORD_NOS (added to the BINARY_OP macro) capture the observed
# operand types during tracing, and the optimizer then speculatively
# emits _GUARD_{TOS,NOS}_FLOAT and specializes the division.
def testfunc(args):
a, b, n = args
total = 0.0
for _ in range(n):
total += a / b
return total

res, ex = self._run_with_optimizer(testfunc, (10.0, 3.0, TIER2_THRESHOLD))
self.assertAlmostEqual(res, TIER2_THRESHOLD * (10.0 / 3.0))
self.assertIsNotNone(ex)
uops = get_opnames(ex)
self.assertIn("_GUARD_TOS_FLOAT", uops)
self.assertIn("_GUARD_NOS_FLOAT", uops)
self.assertIn("_BINARY_OP_TRUEDIV_FLOAT", uops)

def test_float_remainder_speculative_guards_from_tracing(self):
# a, b are locals with no statically known type. Tracing records
# them as floats; the optimizer then speculatively emits
# _GUARD_{TOS,NOS}_FLOAT for NB_REMAINDER. That narrows both
# operands to float, and the _BINARY_OP handler marks the result
# as a unique float. Downstream, `* 2.0` therefore specializes
# to _BINARY_OP_MULTIPLY_FLOAT_INPLACE.
def testfunc(args):
a, b, n = args
total = 0.0
for _ in range(n):
total += (a % b) * 2.0
return total

res, ex = self._run_with_optimizer(testfunc, (10.0, 3.0, TIER2_THRESHOLD))
self.assertAlmostEqual(res, TIER2_THRESHOLD * (10.0 % 3.0) * 2.0)
self.assertIsNotNone(ex)
uops = get_opnames(ex)
self.assertIn("_GUARD_TOS_FLOAT", uops)
self.assertIn("_GUARD_NOS_FLOAT", uops)
self.assertIn("_BINARY_OP_MULTIPLY_FLOAT_INPLACE", uops)

def test_float_truediv_type_propagation(self):
# Test the _BINARY_OP_TRUEDIV_FLOAT propagates type information
def testfunc(args):
Expand Down
2 changes: 1 addition & 1 deletion Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -5728,7 +5728,7 @@ dummy_func(
DEAD(rhs);
}

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

pure replicate(2:4) inst(SWAP, (bottom, unused[oparg-2], top --
bottom, unused[oparg-2], top)) {
Expand Down
25 changes: 25 additions & 0 deletions Python/optimizer_bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,31 @@ dummy_func(void) {
bool rhs_int = sym_matches_type(rhs, &PyLong_Type);
bool lhs_float = sym_matches_type(lhs, &PyFloat_Type);
bool rhs_float = sym_matches_type(rhs, &PyFloat_Type);
// Speculatively promote probable floats to known floats. The
// _RECORD_TOS / _RECORD_NOS uops preceding _BINARY_OP record the
// observed operands during tracing; if the probable type is float
// but no static type is known, emit a guard and narrow the symbol.
// For NB_TRUE_DIVIDE this enables the specialized float path
// below; for NB_REMAINDER it lets the result type propagate as
// float so downstream ops can specialize. NB_POWER is intentionally
// excluded: speculative guards there broke
// test_power_type_depends_on_input_values (see GH-127844), likely
// due to subtle interactions with the case A-G dispatch.
if (oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE
|| oparg == NB_REMAINDER || oparg == NB_INPLACE_REMAINDER) {
if (!rhs_float && !sym_has_type(rhs)
&& sym_get_probable_type(rhs) == &PyFloat_Type) {
ADD_OP(_GUARD_TOS_FLOAT, 0, 0);
sym_set_type(rhs, &PyFloat_Type);
rhs_float = true;
}
if (!lhs_float && !sym_has_type(lhs)
&& sym_get_probable_type(lhs) == &PyFloat_Type) {
ADD_OP(_GUARD_NOS_FLOAT, 0, 0);
sym_set_type(lhs, &PyFloat_Type);
lhs_float = true;
}
}
if ((oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE)
&& lhs_float && rhs_float) {
if (PyJitRef_IsUnique(lhs)) {
Expand Down
15 changes: 15 additions & 0 deletions Python/optimizer_cases.c.h

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

5 changes: 4 additions & 1 deletion Python/record_functions.c.h

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

Loading