Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
7917e28
feat(longobject): add int64 range helpers and _PyCompactLong_AddWide …
KRRT7 Jun 10, 2026
7a3ab4b
feat(ceval): wire BINARY_OP_ADD_INT to wide path via new micro-ops
KRRT7 Jun 10, 2026
957674b
perf(specialize): widen BINARY_OP_ADD_INT specialization to int64 range
KRRT7 Jun 10, 2026
47f21dd
test(opcache): update test_binary_op for wide int specialization
KRRT7 Jun 10, 2026
5b69f64
test(longobject): add INT64_MIN boundary tests for _PyLong_TryAsInt64…
KRRT7 Jun 10, 2026
87758ec
perf(longobject): add freelist for 2-digit PyLong objects
KRRT7 Jun 10, 2026
a3277e4
perf(longobject): extend freelist to 3-digit PyLong objects
KRRT7 Jun 10, 2026
039d6c3
refactor(longobject): tighten _PyCompactLong_AddWide helpers; keep JI…
KRRT7 Jun 10, 2026
81713ed
perf(specialize): widen BINARY_OP_SUBTRACT_INT to full int64 range
KRRT7 Jun 10, 2026
a4b3e95
perf(longobject): keep wide int helper local
KRRT7 Jun 10, 2026
8d2d3c9
perf(longobject): add wide int fast path
KRRT7 Jun 10, 2026
d8b9f3f
Merge remote-tracking branch 'upstream/main' into wide-int-accel
KRRT7 Jun 10, 2026
05023f4
Misc/NEWS: add blurb for wide int fast path
KRRT7 Jun 10, 2026
c1a95ef
regen opcode cases for wide int fast path
KRRT7 Jun 10, 2026
540d96c
perf(longobject): restore JIT optimizer cases for wide ints
KRRT7 Jun 10, 2026
0f42443
test: make wide int benchmark more stable
KRRT7 Jun 11, 2026
4864750
test: make wide int benchmark import-safe
KRRT7 Jun 11, 2026
24a31a1
Merge branch 'main' into wide-int-accel
KRRT7 Jun 11, 2026
d361dc9
cleanup: drop ints2/ints3 freelists and benchmark script
KRRT7 Jun 11, 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
98 changes: 98 additions & 0 deletions Include/internal/pycore_long.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ 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);
PyAPI_FUNC(_PyStackRef) _PyCompactLong_AddWide(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 @@ -346,6 +348,102 @@ _PyLong_CheckExactAndCompact(PyObject *op)
return PyLong_CheckExact(op) && _PyLong_IsCompact((const PyLongObject *)op);
}

/* Max number of digits a PyLong can have and still fit in int64_t.
* 30-bit builds: ceil(64/30) = 3. 15-bit builds: ceil(64/15) = 5. */
#define _PY_LONG_MAX_DIGITS_FOR_INT64 ((64 + PyLong_SHIFT - 1) / PyLong_SHIFT)

/* Return 1 if v fits in int64_t. Does not require exact type. */
static inline int
_PyLong_FitsInt64(const PyLongObject *v)
{
uintptr_t tag = v->long_value.lv_tag;
/* Fast path: digit count is strictly below the max — always fits. */
if (tag < ((uintptr_t)_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;
}
/* ndigits == _PY_LONG_MAX_DIGITS_FOR_INT64: check the top digit. */
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;
}
/* top == max_top: only INT64_MIN has all lower digits == 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;
}

/* Extract an exact int to int64_t without raising.
* Returns true and writes *out on success; returns false if out of range.
* Never sets a Python 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 - (int)(tag & SIGN_MASK);
/* Compact (0 or 1 digit): fast, branchless extraction. */
if (tag < (2u << 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) {
/* Most common non-compact case on 64-bit builds. */
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 - 1; i++) {
abs_val |= (uint64_t)v->long_value.ob_digit[i] << shift;
shift += PyLong_SHIFT;
}
uint64_t top = (uint64_t)v->long_value.ob_digit[ndigits - 1];
/* Prevent UB from an oversized shift when at the maximum digit count. */
if (ndigits == _PY_LONG_MAX_DIGITS_FOR_INT64 && (top >> (64 - shift)) != 0) {
return false;
}
abs_val |= top << 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;
return true;
}
return false;
}

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

#ifdef __cplusplus
}
#endif
Expand Down
18 changes: 14 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.

178 changes: 90 additions & 88 deletions Include/opcode_ids.h

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

Loading
Loading