Skip to content

Commit 57ed81d

Browse files
eendebakptclaude
andcommitted
pythongh-100239: Propagate compact-int result type through BINARY_OP_EXTEND
Add a `result_compact_int` flag to _PyBinaryOpSpecializationDescr. When set, the tier 2 optimizer emits `sym_new_compact_int` for the _BINARY_OP_EXTEND result instead of a generic PyLong_Type symbol. This lets downstream int-typed uops (_GUARD_TOS_INT / _GUARD_NOS_INT) recognize that the value is already compact and elide their guards. Mark the six compactlongs bitwise entries (OR/AND/XOR and inplace variants) and the two RSHIFT entries as compact: bitwise ops and right shift of compact longs always stay within compact range. LSHIFT is left unmarked because `compact << rhs` with `rhs <= 16` can reach ~2^46, which is not compact. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 06900f5 commit 57ed81d

4 files changed

Lines changed: 40 additions & 16 deletions

File tree

Include/internal/pycore_code.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,11 @@ typedef struct {
515515
types are already known. NULL means unknown/don't eliminate. */
516516
PyTypeObject *lhs_type;
517517
PyTypeObject *rhs_type;
518+
/* Nonzero iff the guard+action together ensure the result is a
519+
compact PyLong. Used by the tier 2 optimizer to propagate a
520+
compact-int symbolic type (instead of generic PyLong_Type) so
521+
downstream int-typed ops can elide their own guards. */
522+
int result_compact_int;
518523
} _PyBinaryOpSpecializationDescr;
519524

520525
/* Comparison bit masks. */

Python/optimizer_bytecodes.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,14 @@ dummy_func(void) {
527527

528528
op(_BINARY_OP_EXTEND, (descr/4, left, right -- res, l, r)) {
529529
_PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr;
530-
if (d != NULL && d->result_type != NULL) {
530+
if (d != NULL && d->result_compact_int) {
531+
assert(d->result_type == &PyLong_Type);
532+
res = sym_new_compact_int(ctx);
533+
if (d->result_unique) {
534+
res = PyJitRef_MakeUnique(res);
535+
}
536+
}
537+
else if (d != NULL && d->result_type != NULL) {
531538
res = sym_new_type(ctx, d->result_type);
532539
if (d->result_unique) {
533540
res = PyJitRef_MakeUnique(res);

Python/optimizer_cases.c.h

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/specialize.c

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2308,20 +2308,25 @@ LONG_FLOAT_ACTION(compactlong_float_true_div, /)
23082308

23092309
static _PyBinaryOpSpecializationDescr binaryop_extend_descrs[] = {
23102310
/* long-long arithmetic: guards also check _PyLong_IsCompact, so
2311-
type alone is not sufficient to eliminate the guard. */
2312-
{NB_OR, compactlongs_guard, compactlongs_or, &PyLong_Type, 1, NULL, NULL},
2313-
{NB_AND, compactlongs_guard, compactlongs_and, &PyLong_Type, 1, NULL, NULL},
2314-
{NB_XOR, compactlongs_guard, compactlongs_xor, &PyLong_Type, 1, NULL, NULL},
2315-
{NB_INPLACE_OR, compactlongs_guard, compactlongs_or, &PyLong_Type, 1, NULL, NULL},
2316-
{NB_INPLACE_AND, compactlongs_guard, compactlongs_and, &PyLong_Type, 1, NULL, NULL},
2317-
{NB_INPLACE_XOR, compactlongs_guard, compactlongs_xor, &PyLong_Type, 1, NULL, NULL},
2318-
2319-
/* long-long shifts: guards also check rhs is non-negative and <= 16 to
2320-
avoid undefined behavior and overflow, so type alone is not sufficient. */
2321-
{NB_LSHIFT, shift_guard, compactlongs_lshift, &PyLong_Type, 1, NULL, NULL},
2322-
{NB_RSHIFT, shift_guard, compactlongs_rshift, &PyLong_Type, 1, NULL, NULL},
2323-
{NB_INPLACE_LSHIFT, shift_guard, compactlongs_lshift, &PyLong_Type, 1, NULL, NULL},
2324-
{NB_INPLACE_RSHIFT, shift_guard, compactlongs_rshift, &PyLong_Type, 1, NULL, NULL},
2311+
type alone is not sufficient to eliminate the guard. The result
2312+
is always a compact PyLong: bitwise OR/AND/XOR of two compacts
2313+
cannot exceed the operand magnitudes. */
2314+
{NB_OR, compactlongs_guard, compactlongs_or, &PyLong_Type, 1, NULL, NULL, 1},
2315+
{NB_AND, compactlongs_guard, compactlongs_and, &PyLong_Type, 1, NULL, NULL, 1},
2316+
{NB_XOR, compactlongs_guard, compactlongs_xor, &PyLong_Type, 1, NULL, NULL, 1},
2317+
{NB_INPLACE_OR, compactlongs_guard, compactlongs_or, &PyLong_Type, 1, NULL, NULL, 1},
2318+
{NB_INPLACE_AND,compactlongs_guard, compactlongs_and, &PyLong_Type, 1, NULL, NULL, 1},
2319+
{NB_INPLACE_XOR,compactlongs_guard, compactlongs_xor, &PyLong_Type, 1, NULL, NULL, 1},
2320+
2321+
/* long-long shifts: guards also check rhs is non-negative and <= 16
2322+
to avoid undefined behavior and overflow. RSHIFT result stays
2323+
compact (magnitude only shrinks); LSHIFT of a compact shifted
2324+
by up to 16 can reach ~2^46, so the result is typed PyLong
2325+
but not marked compact. */
2326+
{NB_LSHIFT, shift_guard, compactlongs_lshift, &PyLong_Type, 1, NULL, NULL, 0},
2327+
{NB_RSHIFT, shift_guard, compactlongs_rshift, &PyLong_Type, 1, NULL, NULL, 1},
2328+
{NB_INPLACE_LSHIFT, shift_guard, compactlongs_lshift, &PyLong_Type, 1, NULL, NULL, 0},
2329+
{NB_INPLACE_RSHIFT, shift_guard, compactlongs_rshift, &PyLong_Type, 1, NULL, NULL, 1},
23252330

23262331
/* float-long arithmetic: guards also check NaN and compactness. */
23272332
{NB_ADD, float_compactlong_guard, float_compactlong_add, &PyFloat_Type, 1, NULL, NULL},

0 commit comments

Comments
 (0)