Skip to content

Commit e8263f9

Browse files
committed
add unique type propagation
1 parent 51d1b11 commit e8263f9

6 files changed

Lines changed: 59 additions & 21 deletions

File tree

Include/internal/pycore_code.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,10 @@ typedef struct {
499499
/* Static type of the result, or NULL if unknown. Used by the tier 2
500500
optimizer to propagate type information through _BINARY_OP_EXTEND. */
501501
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;
502506
} _PyBinaryOpSpecializationDescr;
503507

504508
/* 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_binary_op_extend_list_concat_type_propagation(self):
38173840
# list + list is specialized via BINARY_OP_EXTEND. The tier 2 optimizer
38183841
# should learn that the result is a list and eliminate subsequent
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
Specialize ``BINARY_OP`` for concatenation of lists and tuples.
1+
Specialize ``BINARY_OP`` for concatenation of lists and tuples, and
2+
propagate the result type through ``_BINARY_OP_EXTEND`` in the tier 2
3+
optimizer so that follow-up type guards can be eliminated.

Python/optimizer_bytecodes.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,9 @@ dummy_func(void) {
413413
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr;
414414
if (d != NULL && d->result_type != NULL) {
415415
res = sym_new_type(ctx, d->result_type);
416+
if (d->result_unique) {
417+
res = PyJitRef_MakeUnique(res);
418+
}
416419
}
417420
else {
418421
res = sym_new_not_null(ctx);

Python/optimizer_cases.c.h

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

Python/specialize.c

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2224,28 +2224,31 @@ LONG_FLOAT_ACTION(compactlong_float_true_div, /)
22242224

22252225
static _PyBinaryOpSpecializationDescr binaryop_extend_descrs[] = {
22262226
/* long-long arithmetic */
2227-
{NB_OR, compactlongs_guard, compactlongs_or, &PyLong_Type},
2228-
{NB_AND, compactlongs_guard, compactlongs_and, &PyLong_Type},
2229-
{NB_XOR, compactlongs_guard, compactlongs_xor, &PyLong_Type},
2230-
{NB_INPLACE_OR, compactlongs_guard, compactlongs_or, &PyLong_Type},
2231-
{NB_INPLACE_AND, compactlongs_guard, compactlongs_and, &PyLong_Type},
2232-
{NB_INPLACE_XOR, compactlongs_guard, compactlongs_xor, &PyLong_Type},
2227+
{NB_OR, compactlongs_guard, compactlongs_or, &PyLong_Type, 1},
2228+
{NB_AND, compactlongs_guard, compactlongs_and, &PyLong_Type, 1},
2229+
{NB_XOR, compactlongs_guard, compactlongs_xor, &PyLong_Type, 1},
2230+
{NB_INPLACE_OR, compactlongs_guard, compactlongs_or, &PyLong_Type, 1},
2231+
{NB_INPLACE_AND, compactlongs_guard, compactlongs_and, &PyLong_Type, 1},
2232+
{NB_INPLACE_XOR, compactlongs_guard, compactlongs_xor, &PyLong_Type, 1},
22332233

22342234
/* float-long arithemetic */
2235-
{NB_ADD, float_compactlong_guard, float_compactlong_add, &PyFloat_Type},
2236-
{NB_SUBTRACT, float_compactlong_guard, float_compactlong_subtract, &PyFloat_Type},
2237-
{NB_TRUE_DIVIDE, nonzero_float_compactlong_guard, float_compactlong_true_div, &PyFloat_Type},
2238-
{NB_MULTIPLY, float_compactlong_guard, float_compactlong_multiply, &PyFloat_Type},
2239-
2240-
/* float-float arithmetic */
2241-
{NB_ADD, compactlong_float_guard, compactlong_float_add, &PyFloat_Type},
2242-
{NB_SUBTRACT, compactlong_float_guard, compactlong_float_subtract, &PyFloat_Type},
2243-
{NB_TRUE_DIVIDE, nonzero_compactlong_float_guard, compactlong_float_true_div, &PyFloat_Type},
2244-
{NB_MULTIPLY, compactlong_float_guard, compactlong_float_multiply, &PyFloat_Type},
2245-
2246-
/* list-list and tuple-tuple concatenation */
2247-
{NB_ADD, list_list_guard, list_list_add, &PyList_Type},
2248-
{NB_ADD, tuple_tuple_guard, tuple_tuple_add, &PyTuple_Type},
2235+
{NB_ADD, float_compactlong_guard, float_compactlong_add, &PyFloat_Type, 1},
2236+
{NB_SUBTRACT, float_compactlong_guard, float_compactlong_subtract, &PyFloat_Type, 1},
2237+
{NB_TRUE_DIVIDE, nonzero_float_compactlong_guard, float_compactlong_true_div, &PyFloat_Type, 1},
2238+
{NB_MULTIPLY, float_compactlong_guard, float_compactlong_multiply, &PyFloat_Type, 1},
2239+
2240+
/* long-float arithmetic */
2241+
{NB_ADD, compactlong_float_guard, compactlong_float_add, &PyFloat_Type, 1},
2242+
{NB_SUBTRACT, compactlong_float_guard, compactlong_float_subtract, &PyFloat_Type, 1},
2243+
{NB_TRUE_DIVIDE, nonzero_compactlong_float_guard, compactlong_float_true_div, &PyFloat_Type, 1},
2244+
{NB_MULTIPLY, compactlong_float_guard, compactlong_float_multiply, &PyFloat_Type, 1},
2245+
2246+
/* list-list concatenation: _PyList_Concat always allocates a new list */
2247+
{NB_ADD, list_list_guard, list_list_add, &PyList_Type, 1},
2248+
/* tuple-tuple concatenation: _PyTuple_Concat has a zero-length shortcut
2249+
that can return one of the operands, so the result is not guaranteed
2250+
to be a freshly allocated object. */
2251+
{NB_ADD, tuple_tuple_guard, tuple_tuple_add, &PyTuple_Type, 0},
22492252
};
22502253

22512254
static int

0 commit comments

Comments
 (0)