Skip to content

Commit efda60e

Browse files
authored
gh-100239: Propagate type info through _BINARY_OP_EXTEND in tier 2 (GH-148146)
1 parent 476fadc commit efda60e

File tree

6 files changed

+67
-18
lines changed

6 files changed

+67
-18
lines changed

Include/internal/pycore_code.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,13 @@ typedef struct {
496496
int oparg;
497497
binaryopguardfunc guard;
498498
binaryopactionfunc action;
499+
/* Static type of the result, or NULL if unknown. Used by the tier 2
500+
optimizer to propagate type information through _BINARY_OP_EXTEND. */
501+
PyTypeObject *result_type;
502+
/* Nonzero iff `action` always returns a freshly allocated object (not
503+
aliased to either operand). Used by the tier 2 optimizer to enable
504+
inplace follow-up ops. */
505+
int result_unique;
499506
} _PyBinaryOpSpecializationDescr;
500507

501508
/* Comparison bit masks. */

Lib/test/test_capi/test_opt.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3813,6 +3813,29 @@ def f(n):
38133813
self.assertIn("_UNPACK_SEQUENCE_TWO_TUPLE", uops)
38143814
self.assertNotIn("_GUARD_TOS_TUPLE", uops)
38153815

3816+
def test_binary_op_extend_float_result_enables_inplace_multiply(self):
3817+
# (2 + x) * y with x, y floats: `2 + x` goes through _BINARY_OP_EXTEND
3818+
# (int + float). The result_type/result_unique info should let the
3819+
# subsequent float multiply use the inplace variant.
3820+
def testfunc(n):
3821+
x = 3.5
3822+
y = 2.0
3823+
res = 0.0
3824+
for _ in range(n):
3825+
res = (2 + x) * y
3826+
return res
3827+
3828+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
3829+
self.assertEqual(res, 11.0)
3830+
self.assertIsNotNone(ex)
3831+
uops = get_opnames(ex)
3832+
self.assertIn("_BINARY_OP_EXTEND", uops)
3833+
self.assertIn("_BINARY_OP_MULTIPLY_FLOAT_INPLACE", uops)
3834+
self.assertNotIn("_BINARY_OP_MULTIPLY_FLOAT", uops)
3835+
# NOS guard on the multiply is eliminated because _BINARY_OP_EXTEND
3836+
# propagates PyFloat_Type.
3837+
self.assertNotIn("_GUARD_NOS_FLOAT", uops)
3838+
38163839
def test_unary_invert_long_type(self):
38173840
def testfunc(n):
38183841
for _ in range(n):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Propagate result type and uniqueness information through
2+
``_BINARY_OP_EXTEND`` in the tier 2 optimizer, enabling elimination of
3+
downstream type guards and selection of inplace float operations.

Python/optimizer_bytecodes.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -410,8 +410,16 @@ dummy_func(void) {
410410
}
411411

412412
op(_BINARY_OP_EXTEND, (descr/4, left, right -- res, l, r)) {
413-
(void)descr;
414-
res = sym_new_not_null(ctx);
413+
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr;
414+
if (d != NULL && d->result_type != NULL) {
415+
res = sym_new_type(ctx, d->result_type);
416+
if (d->result_unique) {
417+
res = PyJitRef_MakeUnique(res);
418+
}
419+
}
420+
else {
421+
res = sym_new_not_null(ctx);
422+
}
415423
l = left;
416424
r = right;
417425
}

Python/optimizer_cases.c.h

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

Python/specialize.c

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2195,24 +2195,24 @@ LONG_FLOAT_ACTION(compactlong_float_true_div, /)
21952195

21962196
static _PyBinaryOpSpecializationDescr binaryop_extend_descrs[] = {
21972197
/* long-long arithmetic */
2198-
{NB_OR, compactlongs_guard, compactlongs_or},
2199-
{NB_AND, compactlongs_guard, compactlongs_and},
2200-
{NB_XOR, compactlongs_guard, compactlongs_xor},
2201-
{NB_INPLACE_OR, compactlongs_guard, compactlongs_or},
2202-
{NB_INPLACE_AND, compactlongs_guard, compactlongs_and},
2203-
{NB_INPLACE_XOR, compactlongs_guard, compactlongs_xor},
2198+
{NB_OR, compactlongs_guard, compactlongs_or, &PyLong_Type, 1},
2199+
{NB_AND, compactlongs_guard, compactlongs_and, &PyLong_Type, 1},
2200+
{NB_XOR, compactlongs_guard, compactlongs_xor, &PyLong_Type, 1},
2201+
{NB_INPLACE_OR, compactlongs_guard, compactlongs_or, &PyLong_Type, 1},
2202+
{NB_INPLACE_AND, compactlongs_guard, compactlongs_and, &PyLong_Type, 1},
2203+
{NB_INPLACE_XOR, compactlongs_guard, compactlongs_xor, &PyLong_Type, 1},
22042204

22052205
/* float-long arithemetic */
2206-
{NB_ADD, float_compactlong_guard, float_compactlong_add},
2207-
{NB_SUBTRACT, float_compactlong_guard, float_compactlong_subtract},
2208-
{NB_TRUE_DIVIDE, nonzero_float_compactlong_guard, float_compactlong_true_div},
2209-
{NB_MULTIPLY, float_compactlong_guard, float_compactlong_multiply},
2206+
{NB_ADD, float_compactlong_guard, float_compactlong_add, &PyFloat_Type, 1},
2207+
{NB_SUBTRACT, float_compactlong_guard, float_compactlong_subtract, &PyFloat_Type, 1},
2208+
{NB_TRUE_DIVIDE, nonzero_float_compactlong_guard, float_compactlong_true_div, &PyFloat_Type, 1},
2209+
{NB_MULTIPLY, float_compactlong_guard, float_compactlong_multiply, &PyFloat_Type, 1},
22102210

22112211
/* float-float arithmetic */
2212-
{NB_ADD, compactlong_float_guard, compactlong_float_add},
2213-
{NB_SUBTRACT, compactlong_float_guard, compactlong_float_subtract},
2214-
{NB_TRUE_DIVIDE, nonzero_compactlong_float_guard, compactlong_float_true_div},
2215-
{NB_MULTIPLY, compactlong_float_guard, compactlong_float_multiply},
2212+
{NB_ADD, compactlong_float_guard, compactlong_float_add, &PyFloat_Type, 1},
2213+
{NB_SUBTRACT, compactlong_float_guard, compactlong_float_subtract, &PyFloat_Type, 1},
2214+
{NB_TRUE_DIVIDE, nonzero_compactlong_float_guard, compactlong_float_true_div, &PyFloat_Type, 1},
2215+
{NB_MULTIPLY, compactlong_float_guard, compactlong_float_multiply, &PyFloat_Type, 1},
22162216
};
22172217

22182218
static int

0 commit comments

Comments
 (0)