Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
eb67609
JIT: widen int fast path to full int64 range
KRRT7 May 20, 2026
1181bf0
cleanup
KRRT7 May 25, 2026
8624d26
Fix widened JIT int fast paths
KRRT7 May 25, 2026
91854c2
Add widened JIT int boundary tests
KRRT7 May 25, 2026
e0089f7
Merge branch 'main' into jit-wide-int-fastpath
KRRT7 May 25, 2026
160d3cd
fix failing unit tests
KRRT7 May 26, 2026
32d6758
Merge branch 'main' into jit-wide-int-fastpath
KRRT7 May 28, 2026
a67837c
WIP
KRRT7 May 28, 2026
fd45492
Merge branch 'main' into jit-wide-int-fastpath
KRRT7 May 28, 2026
1d667d4
Update Misc/NEWS.d/next/Core_and_Builtins/2026-05-28-15-00-00.gh-issu…
KRRT7 May 28, 2026
41ad8fb
Update pycore_long.h
KRRT7 May 28, 2026
d549674
Restore compact fast path in _PyCompactLong_{Add,Subtract,Multiply}; …
KRRT7 May 28, 2026
094a60a
Split BINARY_OP_ADD/SUBTRACT/MULTIPLY_INT into compact and wide speci…
KRRT7 May 28, 2026
8769659
Update jit_int_benchmark_pyperf.py
KRRT7 May 29, 2026
7775456
Merge branch 'main' into jit-wide-int-fastpath
KRRT7 May 29, 2026
f206055
fix failing tests
KRRT7 May 29, 2026
39ae2f2
Fix wide int guard range and optimizer facts
KRRT7 May 29, 2026
f35d39b
Rename int64 guard helper and drop simple benchmark
KRRT7 May 29, 2026
4d49566
Separate compact int and int64 optimizer facts
KRRT7 May 29, 2026
e6fd6d8
Merge branch 'main' into jit-wide-int-fastpath
KRRT7 May 29, 2026
d4a355c
tier1-merged tier2-split: single BINARY_OP_ADD_INT with optimizer gua…
KRRT7 May 29, 2026
d608803
Port merged design from explore/merged-on-main
KRRT7 May 29, 2026
5e01840
Fix SUBSCR_LIST_INT/STORE_SUBSCR_LIST_INT asserting on wide ints
KRRT7 May 29, 2026
24a0d2a
Update Include/internal/pycore_long.h
KRRT7 May 29, 2026
a4c0ddb
edit based on feedback
KRRT7 May 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions Include/internal/pycore_long.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ PyAPI_DATA(PyObject*) _PyLong_Lshift(PyObject *, int64_t);
PyAPI_FUNC(_PyStackRef) _PyCompactLong_Add(PyLongObject *left, PyLongObject *right);
PyAPI_FUNC(_PyStackRef) _PyCompactLong_Multiply(PyLongObject *left, PyLongObject *right);
PyAPI_FUNC(_PyStackRef) _PyCompactLong_Subtract(PyLongObject *left, PyLongObject *right);
/* Wide variants: accept exact ints in the full int64 range (may be non-compact). */
PyAPI_FUNC(_PyStackRef) _PyCompactLong_AddWide(PyLongObject *left, PyLongObject *right);
PyAPI_FUNC(_PyStackRef) _PyCompactLong_MultiplyWide(PyLongObject *left, PyLongObject *right);
PyAPI_FUNC(_PyStackRef) _PyCompactLong_SubtractWide(PyLongObject *left, PyLongObject *right);

// Export for 'binascii' shared extension.
PyAPI_DATA(unsigned char) _PyLong_DigitValue[256];
Expand Down Expand Up @@ -217,6 +221,7 @@ _PyLong_BothAreCompact(const PyLongObject* a, const PyLongObject* b) {
static inline bool
_PyLong_IsZero(const PyLongObject *op)
{
assert(op != NULL);
return (op->long_value.lv_tag & SIGN_MASK) == SIGN_ZERO;
}

Expand Down Expand Up @@ -346,6 +351,106 @@ _PyLong_CheckExactAndCompact(PyObject *op)
return PyLong_CheckExact(op) && _PyLong_IsCompact((const PyLongObject *)op);
}

/* A cheap range guard used by Tier 2 / JIT integer fast paths.
*
* "Compact" ints are single-digit. Non-compact ints may still fit in int64_t,
* but are limited to a small number of digits (3 for 30-bit digits, 5 for
* 15-bit digits).
*/
#define _PY_LONG_MAX_DIGITS_FOR_INT64 ((64 + PyLong_SHIFT - 1) / PyLong_SHIFT)

static inline int
_PyLong_FitsInt64(const PyLongObject *v)
{
uintptr_t tag = v->long_value.lv_tag;
if (tag < (_PY_LONG_MAX_DIGITS_FOR_INT64 << NON_SIZE_BITS)) {
return 1;
}
Py_ssize_t ndigits = (Py_ssize_t)(tag >> NON_SIZE_BITS);
if (ndigits > _PY_LONG_MAX_DIGITS_FOR_INT64) {
return 0;
}

unsigned int shift = PyLong_SHIFT * (unsigned int)(ndigits - 1);
uint64_t top = (uint64_t)v->long_value.ob_digit[ndigits - 1];
if ((tag & SIGN_MASK) == SIGN_NEGATIVE) {
uint64_t max_top = ((uint64_t)INT64_MAX + 1) >> shift;
if (top < max_top) {
return 1;
}
if (top > max_top) {
return 0;
}
for (Py_ssize_t i = 0; i < ndigits - 1; i++) {
if (v->long_value.ob_digit[i] != 0) {
return 0;
}
}
return 1;
}
uint64_t max_top = (uint64_t)INT64_MAX >> shift;
return top <= max_top;
}

static inline int
_PyLong_CheckExactAndFitsInt64(PyObject *op)
{
return PyLong_CheckExact(op) &&
_PyLong_FitsInt64((const PyLongObject *)op);
}

/* Extract an exact int to int64_t without raising.
*
* Returns true on success and writes to *out; returns false if the value is
* out of int64_t range. Never sets an exception.
*/
static inline bool
_PyLong_TryAsInt64Exact(PyLongObject *v, int64_t *out)
{
assert(PyLong_CheckExact((PyObject *)v));
uintptr_t tag = v->long_value.lv_tag;
int sign = 1 - (tag & SIGN_MASK);
if (tag < (2 << NON_SIZE_BITS)) {
*out = (int64_t)(sign * (Py_ssize_t)v->long_value.ob_digit[0]);
return true;
}
Py_ssize_t ndigits = (Py_ssize_t)(tag >> NON_SIZE_BITS);
if (ndigits > _PY_LONG_MAX_DIGITS_FOR_INT64) {
return false;
}
uint64_t abs_val = 0;
#if PyLong_SHIFT == 30
if (ndigits == 2) {
abs_val = (uint64_t)v->long_value.ob_digit[0] |
((uint64_t)v->long_value.ob_digit[1] << 30);
*out = sign < 0 ? -(int64_t)abs_val : (int64_t)abs_val;
return true;
}
#endif
unsigned int shift = 0;
for (Py_ssize_t i = 0; i < ndigits; i++) {
uint64_t d = (uint64_t)v->long_value.ob_digit[i];
if (ndigits == _PY_LONG_MAX_DIGITS_FOR_INT64 &&
i == ndigits - 1 &&
shift != 0 &&
(d >> (64 - shift)) != 0)
{
return false;
}
abs_val |= d << shift;
shift += PyLong_SHIFT;
}
if (abs_val <= (uint64_t)INT64_MAX) {
*out = sign < 0 ? -(int64_t)abs_val : (int64_t)abs_val;
return true;
}
if (sign < 0 && abs_val == (uint64_t)INT64_MAX + 1) {
*out = INT64_MIN;
Comment thread
KRRT7 marked this conversation as resolved.
return true;
}
return false;
}

#ifdef __cplusplus
}
#endif
Expand Down
8 changes: 4 additions & 4 deletions Include/internal/pycore_opcode_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Include/internal/pycore_optimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,7 @@ extern JitOptRef _Py_uop_sym_tuple_getitem(JitOptContext *ctx, JitOptRef sym, Py
extern Py_ssize_t _Py_uop_sym_tuple_length(JitOptRef sym);
extern JitOptRef _Py_uop_sym_new_truthiness(JitOptContext *ctx, JitOptRef value, bool truthy);
extern bool _Py_uop_sym_is_compact_int(JitOptRef sym);
extern bool _Py_uop_sym_fits_int64(JitOptRef sym);
extern JitOptRef _Py_uop_sym_new_compact_int(JitOptContext *ctx);
extern void _Py_uop_sym_set_compact_int(JitOptContext *ctx, JitOptRef sym);
extern JitOptRef _Py_uop_sym_new_predicate(JitOptContext *ctx, JitOptRef lhs_ref, JitOptRef rhs_ref, JitOptPredicateKind kind);
Expand Down
Loading
Loading