gh-100239: Propagate type info through _BINARY_OP_EXTEND in tier 2 (GH-148146)

This commit is contained in:
Pieter Eendebak 2026-04-06 14:52:42 +02:00 committed by GitHub
parent 476fadc9ae
commit efda60e2ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 67 additions and 18 deletions

View file

@ -496,6 +496,13 @@ typedef struct {
int oparg;
binaryopguardfunc guard;
binaryopactionfunc action;
/* Static type of the result, or NULL if unknown. Used by the tier 2
optimizer to propagate type information through _BINARY_OP_EXTEND. */
PyTypeObject *result_type;
/* Nonzero iff `action` always returns a freshly allocated object (not
aliased to either operand). Used by the tier 2 optimizer to enable
inplace follow-up ops. */
int result_unique;
} _PyBinaryOpSpecializationDescr;
/* Comparison bit masks. */

View file

@ -3813,6 +3813,29 @@ def f(n):
self.assertIn("_UNPACK_SEQUENCE_TWO_TUPLE", uops)
self.assertNotIn("_GUARD_TOS_TUPLE", uops)
def test_binary_op_extend_float_result_enables_inplace_multiply(self):
# (2 + x) * y with x, y floats: `2 + x` goes through _BINARY_OP_EXTEND
# (int + float). The result_type/result_unique info should let the
# subsequent float multiply use the inplace variant.
def testfunc(n):
x = 3.5
y = 2.0
res = 0.0
for _ in range(n):
res = (2 + x) * y
return res
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
self.assertEqual(res, 11.0)
self.assertIsNotNone(ex)
uops = get_opnames(ex)
self.assertIn("_BINARY_OP_EXTEND", uops)
self.assertIn("_BINARY_OP_MULTIPLY_FLOAT_INPLACE", uops)
self.assertNotIn("_BINARY_OP_MULTIPLY_FLOAT", uops)
# NOS guard on the multiply is eliminated because _BINARY_OP_EXTEND
# propagates PyFloat_Type.
self.assertNotIn("_GUARD_NOS_FLOAT", uops)
def test_unary_invert_long_type(self):
def testfunc(n):
for _ in range(n):

View file

@ -0,0 +1,3 @@
Propagate result type and uniqueness information through
``_BINARY_OP_EXTEND`` in the tier 2 optimizer, enabling elimination of
downstream type guards and selection of inplace float operations.

View file

@ -410,8 +410,16 @@ dummy_func(void) {
}
op(_BINARY_OP_EXTEND, (descr/4, left, right -- res, l, r)) {
(void)descr;
res = sym_new_not_null(ctx);
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr;
if (d != NULL && d->result_type != NULL) {
res = sym_new_type(ctx, d->result_type);
if (d->result_unique) {
res = PyJitRef_MakeUnique(res);
}
}
else {
res = sym_new_not_null(ctx);
}
l = left;
r = right;
}

View file

@ -1168,8 +1168,16 @@
right = stack_pointer[-1];
left = stack_pointer[-2];
PyObject *descr = (PyObject *)this_instr->operand0;
(void)descr;
res = sym_new_not_null(ctx);
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr;
if (d != NULL && d->result_type != NULL) {
res = sym_new_type(ctx, d->result_type);
if (d->result_unique) {
res = PyJitRef_MakeUnique(res);
}
}
else {
res = sym_new_not_null(ctx);
}
l = left;
r = right;
CHECK_STACK_BOUNDS(1);

View file

@ -2195,24 +2195,24 @@ LONG_FLOAT_ACTION(compactlong_float_true_div, /)
static _PyBinaryOpSpecializationDescr binaryop_extend_descrs[] = {
/* long-long arithmetic */
{NB_OR, compactlongs_guard, compactlongs_or},
{NB_AND, compactlongs_guard, compactlongs_and},
{NB_XOR, compactlongs_guard, compactlongs_xor},
{NB_INPLACE_OR, compactlongs_guard, compactlongs_or},
{NB_INPLACE_AND, compactlongs_guard, compactlongs_and},
{NB_INPLACE_XOR, compactlongs_guard, compactlongs_xor},
{NB_OR, compactlongs_guard, compactlongs_or, &PyLong_Type, 1},
{NB_AND, compactlongs_guard, compactlongs_and, &PyLong_Type, 1},
{NB_XOR, compactlongs_guard, compactlongs_xor, &PyLong_Type, 1},
{NB_INPLACE_OR, compactlongs_guard, compactlongs_or, &PyLong_Type, 1},
{NB_INPLACE_AND, compactlongs_guard, compactlongs_and, &PyLong_Type, 1},
{NB_INPLACE_XOR, compactlongs_guard, compactlongs_xor, &PyLong_Type, 1},
/* float-long arithemetic */
{NB_ADD, float_compactlong_guard, float_compactlong_add},
{NB_SUBTRACT, float_compactlong_guard, float_compactlong_subtract},
{NB_TRUE_DIVIDE, nonzero_float_compactlong_guard, float_compactlong_true_div},
{NB_MULTIPLY, float_compactlong_guard, float_compactlong_multiply},
{NB_ADD, float_compactlong_guard, float_compactlong_add, &PyFloat_Type, 1},
{NB_SUBTRACT, float_compactlong_guard, float_compactlong_subtract, &PyFloat_Type, 1},
{NB_TRUE_DIVIDE, nonzero_float_compactlong_guard, float_compactlong_true_div, &PyFloat_Type, 1},
{NB_MULTIPLY, float_compactlong_guard, float_compactlong_multiply, &PyFloat_Type, 1},
/* float-float arithmetic */
{NB_ADD, compactlong_float_guard, compactlong_float_add},
{NB_SUBTRACT, compactlong_float_guard, compactlong_float_subtract},
{NB_TRUE_DIVIDE, nonzero_compactlong_float_guard, compactlong_float_true_div},
{NB_MULTIPLY, compactlong_float_guard, compactlong_float_multiply},
{NB_ADD, compactlong_float_guard, compactlong_float_add, &PyFloat_Type, 1},
{NB_SUBTRACT, compactlong_float_guard, compactlong_float_subtract, &PyFloat_Type, 1},
{NB_TRUE_DIVIDE, nonzero_compactlong_float_guard, compactlong_float_true_div, &PyFloat_Type, 1},
{NB_MULTIPLY, compactlong_float_guard, compactlong_float_multiply, &PyFloat_Type, 1},
};
static int