Skip to content

Commit 3ba0c85

Browse files
committed
add back recording ops and guards
1 parent fa97ab3 commit 3ba0c85

File tree

6 files changed

+89
-4
lines changed

6 files changed

+89
-4
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
@@ -3688,6 +3688,48 @@ def testfunc(args):
36883688
uops = get_opnames(ex)
36893689
self.assertIn("_BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT", uops)
36903690

3691+
def test_float_truediv_speculative_guards_from_tracing(self):
3692+
# a, b are locals with no statically known type. _RECORD_TOS /
3693+
# _RECORD_NOS (added to the BINARY_OP macro) capture the observed
3694+
# operand types during tracing, and the optimizer then speculatively
3695+
# emits _GUARD_{TOS,NOS}_FLOAT and specializes the division.
3696+
def testfunc(args):
3697+
a, b, n = args
3698+
total = 0.0
3699+
for _ in range(n):
3700+
total += a / b
3701+
return total
3702+
3703+
res, ex = self._run_with_optimizer(testfunc, (10.0, 3.0, TIER2_THRESHOLD))
3704+
self.assertAlmostEqual(res, TIER2_THRESHOLD * (10.0 / 3.0))
3705+
self.assertIsNotNone(ex)
3706+
uops = get_opnames(ex)
3707+
self.assertIn("_GUARD_TOS_FLOAT", uops)
3708+
self.assertIn("_GUARD_NOS_FLOAT", uops)
3709+
self.assertIn("_BINARY_OP_TRUEDIV_FLOAT", uops)
3710+
3711+
def test_float_remainder_speculative_guards_from_tracing(self):
3712+
# a, b are locals with no statically known type. Tracing records
3713+
# them as floats; the optimizer then speculatively emits
3714+
# _GUARD_{TOS,NOS}_FLOAT for NB_REMAINDER. That narrows both
3715+
# operands to float, and the _BINARY_OP handler marks the result
3716+
# as a unique float. Downstream, `* 2.0` therefore specializes
3717+
# to _BINARY_OP_MULTIPLY_FLOAT_INPLACE.
3718+
def testfunc(args):
3719+
a, b, n = args
3720+
total = 0.0
3721+
for _ in range(n):
3722+
total += (a % b) * 2.0
3723+
return total
3724+
3725+
res, ex = self._run_with_optimizer(testfunc, (10.0, 3.0, TIER2_THRESHOLD))
3726+
self.assertAlmostEqual(res, TIER2_THRESHOLD * (10.0 % 3.0) * 2.0)
3727+
self.assertIsNotNone(ex)
3728+
uops = get_opnames(ex)
3729+
self.assertIn("_GUARD_TOS_FLOAT", uops)
3730+
self.assertIn("_GUARD_NOS_FLOAT", uops)
3731+
self.assertIn("_BINARY_OP_MULTIPLY_FLOAT_INPLACE", uops)
3732+
36913733
def test_float_truediv_type_propagation(self):
36923734
# Test the _BINARY_OP_TRUEDIV_FLOAT propagates type information
36933735
def testfunc(args):

Python/bytecodes.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5728,7 +5728,7 @@ dummy_func(
57285728
DEAD(rhs);
57295729
}
57305730

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

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

Python/optimizer_bytecodes.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,31 @@ dummy_func(void) {
279279
bool rhs_int = sym_matches_type(rhs, &PyLong_Type);
280280
bool lhs_float = sym_matches_type(lhs, &PyFloat_Type);
281281
bool rhs_float = sym_matches_type(rhs, &PyFloat_Type);
282+
// Speculatively promote probable floats to known floats. The
283+
// _RECORD_TOS / _RECORD_NOS uops preceding _BINARY_OP record the
284+
// observed operands during tracing; if the probable type is float
285+
// but no static type is known, emit a guard and narrow the symbol.
286+
// For NB_TRUE_DIVIDE this enables the specialized float path
287+
// below; for NB_REMAINDER it lets the result type propagate as
288+
// float so downstream ops can specialize. NB_POWER is intentionally
289+
// excluded: speculative guards there broke
290+
// test_power_type_depends_on_input_values (see GH-127844), likely
291+
// due to subtle interactions with the case A-G dispatch.
292+
if (oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE
293+
|| oparg == NB_REMAINDER || oparg == NB_INPLACE_REMAINDER) {
294+
if (!rhs_float && !sym_has_type(rhs)
295+
&& sym_get_probable_type(rhs) == &PyFloat_Type) {
296+
ADD_OP(_GUARD_TOS_FLOAT, 0, 0);
297+
sym_set_type(rhs, &PyFloat_Type);
298+
rhs_float = true;
299+
}
300+
if (!lhs_float && !sym_has_type(lhs)
301+
&& sym_get_probable_type(lhs) == &PyFloat_Type) {
302+
ADD_OP(_GUARD_NOS_FLOAT, 0, 0);
303+
sym_set_type(lhs, &PyFloat_Type);
304+
lhs_float = true;
305+
}
306+
}
282307
if ((oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE)
283308
&& lhs_float && rhs_float) {
284309
if (PyJitRef_IsUnique(lhs)) {

Python/optimizer_cases.c.h

Lines changed: 15 additions & 0 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)