GH-101291: Rearrange the size bits in PyLongObject (GH-102464)

* Eliminate all remaining uses of Py_SIZE and Py_SET_SIZE on PyLongObject, adding asserts.

* Change layout of size/sign bits in longobject to support future addition of immortal ints and tagged medium ints.

* Add functions to hide some internals of long object, and for setting sign and digit count.

* Replace uses of IS_MEDIUM_VALUE macro with _PyLong_IsCompact().
This commit is contained in:
Mark Shannon 2023-03-22 14:49:51 +00:00 committed by GitHub
parent 713df2c534
commit 7559f5fda9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 982 additions and 898 deletions

View file

@ -80,7 +80,7 @@ typedef long stwodigits; /* signed variant of twodigits */
*/
typedef struct _PyLongValue {
Py_ssize_t ob_size; /* Number of items in variable part */
uintptr_t lv_tag; /* Number of digits, sign and flags */
digit ob_digit[1];
} _PyLongValue;
@ -94,6 +94,10 @@ PyAPI_FUNC(PyLongObject *) _PyLong_New(Py_ssize_t);
/* Return a copy of src. */
PyAPI_FUNC(PyObject *) _PyLong_Copy(PyLongObject *src);
PyAPI_FUNC(PyLongObject *)
_PyLong_FromDigits(int negative, Py_ssize_t digit_count, digit *digits);
#ifdef __cplusplus
}
#endif

View file

@ -82,8 +82,6 @@ PyObject *_PyLong_Add(PyLongObject *left, PyLongObject *right);
PyObject *_PyLong_Multiply(PyLongObject *left, PyLongObject *right);
PyObject *_PyLong_Subtract(PyLongObject *left, PyLongObject *right);
int _PyLong_AssignValue(PyObject **target, Py_ssize_t value);
/* Used by Python/mystrtoul.c, _PyBytes_FromHex(),
_PyBytes_DecodeEscape(), etc. */
PyAPI_DATA(unsigned char) _PyLong_DigitValue[256];
@ -110,25 +108,155 @@ PyAPI_FUNC(char*) _PyLong_FormatBytesWriter(
int base,
int alternate);
/* Return 1 if the argument is positive single digit int */
static inline int
_PyLong_IsPositiveSingleDigit(PyObject* sub) {
/* For a positive single digit int, the value of Py_SIZE(sub) is 0 or 1.
We perform a fast check using a single comparison by casting from int
to uint which casts negative numbers to large positive numbers.
For details see Section 14.2 "Bounds Checking" in the Agner Fog
optimization manual found at:
https://www.agner.org/optimize/optimizing_cpp.pdf
The function is not affected by -fwrapv, -fno-wrapv and -ftrapv
compiler options of GCC and clang
/* Long value tag bits:
* 0-1: Sign bits value = (1-sign), ie. negative=2, positive=0, zero=1.
* 2: Reserved for immortality bit
* 3+ Unsigned digit count
*/
assert(PyLong_CheckExact(sub));
Py_ssize_t signed_size = Py_SIZE(sub);
return ((size_t)signed_size) <= 1;
#define SIGN_MASK 3
#define SIGN_ZERO 1
#define SIGN_NEGATIVE 2
#define NON_SIZE_BITS 3
/* All *compact" values are guaranteed to fit into
* a Py_ssize_t with at least one bit to spare.
* In other words, for 64 bit machines, compact
* will be signed 63 (or fewer) bit values
*/
/* Return 1 if the argument is compact int */
static inline int
_PyLong_IsNonNegativeCompact(const PyLongObject* op) {
assert(PyLong_Check(op));
return op->long_value.lv_tag <= (1 << NON_SIZE_BITS);
}
static inline int
_PyLong_IsCompact(const PyLongObject* op) {
assert(PyLong_Check(op));
return op->long_value.lv_tag < (2 << NON_SIZE_BITS);
}
static inline int
_PyLong_BothAreCompact(const PyLongObject* a, const PyLongObject* b) {
assert(PyLong_Check(a));
assert(PyLong_Check(b));
return (a->long_value.lv_tag | b->long_value.lv_tag) < (2 << NON_SIZE_BITS);
}
/* Returns a *compact* value, iff `_PyLong_IsCompact` is true for `op`.
*
* "Compact" values have at least one bit to spare,
* so that addition and subtraction can be performed on the values
* without risk of overflow.
*/
static inline Py_ssize_t
_PyLong_CompactValue(const PyLongObject *op)
{
assert(PyLong_Check(op));
assert(_PyLong_IsCompact(op));
Py_ssize_t sign = 1 - (op->long_value.lv_tag & SIGN_MASK);
return sign * (Py_ssize_t)op->long_value.ob_digit[0];
}
static inline bool
_PyLong_IsZero(const PyLongObject *op)
{
return (op->long_value.lv_tag & SIGN_MASK) == SIGN_ZERO;
}
static inline bool
_PyLong_IsNegative(const PyLongObject *op)
{
return (op->long_value.lv_tag & SIGN_MASK) == SIGN_NEGATIVE;
}
static inline bool
_PyLong_IsPositive(const PyLongObject *op)
{
return (op->long_value.lv_tag & SIGN_MASK) == 0;
}
static inline Py_ssize_t
_PyLong_DigitCount(const PyLongObject *op)
{
assert(PyLong_Check(op));
return op->long_value.lv_tag >> NON_SIZE_BITS;
}
/* Equivalent to _PyLong_DigitCount(op) * _PyLong_NonCompactSign(op) */
static inline Py_ssize_t
_PyLong_SignedDigitCount(const PyLongObject *op)
{
assert(PyLong_Check(op));
Py_ssize_t sign = 1 - (op->long_value.lv_tag & SIGN_MASK);
return sign * (Py_ssize_t)(op->long_value.lv_tag >> NON_SIZE_BITS);
}
static inline int
_PyLong_CompactSign(const PyLongObject *op)
{
assert(PyLong_Check(op));
assert(_PyLong_IsCompact(op));
return 1 - (op->long_value.lv_tag & SIGN_MASK);
}
static inline int
_PyLong_NonCompactSign(const PyLongObject *op)
{
assert(PyLong_Check(op));
assert(!_PyLong_IsCompact(op));
return 1 - (op->long_value.lv_tag & SIGN_MASK);
}
/* Do a and b have the same sign? */
static inline int
_PyLong_SameSign(const PyLongObject *a, const PyLongObject *b)
{
return (a->long_value.lv_tag & SIGN_MASK) == (b->long_value.lv_tag & SIGN_MASK);
}
#define TAG_FROM_SIGN_AND_SIZE(sign, size) ((1 - (sign)) | ((size) << NON_SIZE_BITS))
static inline void
_PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size)
{
assert(size >= 0);
assert(-1 <= sign && sign <= 1);
assert(sign != 0 || size == 0);
op->long_value.lv_tag = TAG_FROM_SIGN_AND_SIZE(sign, (size_t)size);
}
static inline void
_PyLong_SetDigitCount(PyLongObject *op, Py_ssize_t size)
{
assert(size >= 0);
op->long_value.lv_tag = (((size_t)size) << NON_SIZE_BITS) | (op->long_value.lv_tag & SIGN_MASK);
}
#define NON_SIZE_MASK ~((1 << NON_SIZE_BITS) - 1)
static inline void
_PyLong_FlipSign(PyLongObject *op) {
unsigned int flipped_sign = 2 - (op->long_value.lv_tag & SIGN_MASK);
op->long_value.lv_tag &= NON_SIZE_MASK;
op->long_value.lv_tag |= flipped_sign;
}
#define _PyLong_DIGIT_INIT(val) \
{ \
.ob_base = _PyObject_IMMORTAL_INIT(&PyLong_Type), \
.long_value = { \
.lv_tag = TAG_FROM_SIGN_AND_SIZE( \
(val) == 0 ? 0 : ((val) < 0 ? -1 : 1), \
(val) == 0 ? 0 : 1), \
{ ((val) >= 0 ? (val) : -(val)) }, \
} \
}
#define _PyLong_FALSE_TAG TAG_FROM_SIGN_AND_SIZE(0, 0)
#define _PyLong_TRUE_TAG TAG_FROM_SIGN_AND_SIZE(1, 1)
#ifdef __cplusplus
}
#endif

View file

@ -137,8 +137,9 @@ static inline void
_PyObject_InitVar(PyVarObject *op, PyTypeObject *typeobj, Py_ssize_t size)
{
assert(op != NULL);
Py_SET_SIZE(op, size);
assert(typeobj != &PyLong_Type);
_PyObject_Init((PyObject *)op, typeobj);
Py_SET_SIZE(op, size);
}

View file

@ -8,6 +8,7 @@ extern "C" {
# error "this header requires Py_BUILD_CORE define"
#endif
#include "pycore_long.h"
#include "pycore_object.h"
#include "pycore_parser.h"
#include "pycore_pymem_init.h"
@ -130,15 +131,6 @@ extern PyTypeObject _PyExc_MemoryError;
// global objects
#define _PyLong_DIGIT_INIT(val) \
{ \
.ob_base = _PyObject_IMMORTAL_INIT(&PyLong_Type), \
.long_value = { \
((val) == 0 ? 0 : ((val) > 0 ? 1 : -1)), \
{ ((val) >= 0 ? (val) : -(val)) }, \
} \
}
#define _PyBytes_SIMPLE_INIT(CH, LEN) \
{ \
_PyVarObject_IMMORTAL_INIT(&PyBytes_Type, (LEN)), \

View file

@ -138,8 +138,13 @@ static inline PyTypeObject* Py_TYPE(PyObject *ob) {
# define Py_TYPE(ob) Py_TYPE(_PyObject_CAST(ob))
#endif
PyAPI_DATA(PyTypeObject) PyLong_Type;
PyAPI_DATA(PyTypeObject) PyBool_Type;
// bpo-39573: The Py_SET_SIZE() function must be used to set an object size.
static inline Py_ssize_t Py_SIZE(PyObject *ob) {
assert(ob->ob_type != &PyLong_Type);
assert(ob->ob_type != &PyBool_Type);
PyVarObject *var_ob = _PyVarObject_CAST(ob);
return var_ob->ob_size;
}
@ -171,8 +176,9 @@ static inline void Py_SET_TYPE(PyObject *ob, PyTypeObject *type) {
# define Py_SET_TYPE(ob, type) Py_SET_TYPE(_PyObject_CAST(ob), type)
#endif
static inline void Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) {
assert(ob->ob_base.ob_type != &PyLong_Type);
assert(ob->ob_base.ob_type != &PyBool_Type);
ob->ob_size = size;
}
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000

View file

@ -0,0 +1,7 @@
Rearrage bits in first field (after header) of PyLongObject.
* Bits 0 and 1: 1 - sign. I.e. 0 for positive numbers, 1 for zero and 2 for negative numbers.
* Bit 2 reserved (probably for the immortal bit)
* Bits 3+ the unsigned size.
This makes a few operations slightly more efficient, and will enable a more
compact and faster 2s-complement representation of most ints in future.

View file

@ -30,6 +30,7 @@
#endif
#include <Python.h>
#include "pycore_long.h" // _PyLong_IsZero()
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "complexobject.h"
#include "mpdecimal.h"
@ -2146,35 +2147,25 @@ dec_from_long(PyTypeObject *type, PyObject *v,
{
PyObject *dec;
PyLongObject *l = (PyLongObject *)v;
Py_ssize_t ob_size;
size_t len;
uint8_t sign;
dec = PyDecType_New(type);
if (dec == NULL) {
return NULL;
}
ob_size = Py_SIZE(l);
if (ob_size == 0) {
if (_PyLong_IsZero(l)) {
_dec_settriple(dec, MPD_POS, 0, 0);
return dec;
}
if (ob_size < 0) {
len = -ob_size;
sign = MPD_NEG;
}
else {
len = ob_size;
sign = MPD_POS;
}
uint8_t sign = _PyLong_IsNegative(l) ? MPD_NEG : MPD_POS;
if (len == 1) {
_dec_settriple(dec, sign, *l->long_value.ob_digit, 0);
if (_PyLong_IsCompact(l)) {
_dec_settriple(dec, sign, l->long_value.ob_digit[0], 0);
mpd_qfinalize(MPD(dec), ctx, status);
return dec;
}
size_t len = _PyLong_DigitCount(l);
#if PYLONG_BITS_IN_DIGIT == 30
mpd_qimport_u32(MPD(dec), l->long_value.ob_digit, len, sign, PyLong_BASE,
@ -3482,7 +3473,6 @@ dec_as_long(PyObject *dec, PyObject *context, int round)
PyLongObject *pylong;
digit *ob_digit;
size_t n;
Py_ssize_t i;
mpd_t *x;
mpd_context_t workctx;
uint32_t status = 0;
@ -3536,26 +3526,9 @@ dec_as_long(PyObject *dec, PyObject *context, int round)
}
assert(n > 0);
pylong = _PyLong_New(n);
if (pylong == NULL) {
assert(!mpd_iszero(x));
pylong = _PyLong_FromDigits(mpd_isnegative(x), n, ob_digit);
mpd_free(ob_digit);
mpd_del(x);
return NULL;
}
memcpy(pylong->long_value.ob_digit, ob_digit, n * sizeof(digit));
mpd_free(ob_digit);
i = n;
while ((i > 0) && (pylong->long_value.ob_digit[i-1] == 0)) {
i--;
}
Py_SET_SIZE(pylong, i);
if (mpd_isnegative(x) && !mpd_iszero(x)) {
Py_SET_SIZE(pylong, -i);
}
mpd_del(x);
return (PyObject *) pylong;
}

View file

@ -347,7 +347,7 @@ test_pyobject_new(PyObject *self, PyObject *Py_UNUSED(ignored))
{
PyObject *obj;
PyTypeObject *type = &PyBaseObject_Type;
PyTypeObject *var_type = &PyLong_Type;
PyTypeObject *var_type = &PyBytes_Type;
// PyObject_New()
obj = PyObject_New(PyObject, type);

View file

@ -32,6 +32,8 @@ Copyright (C) 1994 Steen Lumholt.
# include "pycore_fileutils.h" // _Py_stat()
#endif
#include "pycore_long.h"
#ifdef MS_WINDOWS
#include <windows.h>
#endif
@ -886,7 +888,8 @@ asBignumObj(PyObject *value)
const char *hexchars;
mp_int bigValue;
neg = Py_SIZE(value) < 0;
assert(PyLong_Check(value));
neg = _PyLong_IsNegative((PyLongObject *)value);
hexstr = _PyLong_Format(value, 16);
if (hexstr == NULL)
return NULL;
@ -1950,7 +1953,7 @@ _tkinter_tkapp_getboolean(TkappObject *self, PyObject *arg)
int v;
if (PyLong_Check(arg)) { /* int or bool */
return PyBool_FromLong(Py_SIZE(arg) != 0);
return PyBool_FromLong(!_PyLong_IsZero((PyLongObject *)arg));
}
if (PyTclObject_Check(arg)) {

View file

@ -836,7 +836,7 @@ long_lcm(PyObject *a, PyObject *b)
{
PyObject *g, *m, *f, *ab;
if (Py_SIZE(a) == 0 || Py_SIZE(b) == 0) {
if (_PyLong_IsZero((PyLongObject *)a) || _PyLong_IsZero((PyLongObject *)b)) {
return PyLong_FromLong(0);
}
g = _PyLong_GCD(a, b);
@ -1726,13 +1726,13 @@ math_isqrt(PyObject *module, PyObject *n)
return NULL;
}
if (_PyLong_Sign(n) < 0) {
if (_PyLong_IsNegative((PyLongObject *)n)) {
PyErr_SetString(
PyExc_ValueError,
"isqrt() argument must be nonnegative");
goto error;
}
if (_PyLong_Sign(n) == 0) {
if (_PyLong_IsZero((PyLongObject *)n)) {
Py_DECREF(n);
return PyLong_FromLong(0);
}
@ -2254,7 +2254,7 @@ loghelper(PyObject* arg, double (*func)(double))
Py_ssize_t e;
/* Negative or zero inputs give a ValueError. */
if (Py_SIZE(arg) <= 0) {
if (!_PyLong_IsPositive((PyLongObject *)arg)) {
PyErr_SetString(PyExc_ValueError,
"math domain error");
return NULL;
@ -3716,12 +3716,12 @@ math_perm_impl(PyObject *module, PyObject *n, PyObject *k)
}
assert(PyLong_CheckExact(n) && PyLong_CheckExact(k));
if (Py_SIZE(n) < 0) {
if (_PyLong_IsNegative((PyLongObject *)n)) {
PyErr_SetString(PyExc_ValueError,
"n must be a non-negative integer");
goto error;
}
if (Py_SIZE(k) < 0) {
if (_PyLong_IsNegative((PyLongObject *)k)) {
PyErr_SetString(PyExc_ValueError,
"k must be a non-negative integer");
goto error;
@ -3808,12 +3808,12 @@ math_comb_impl(PyObject *module, PyObject *n, PyObject *k)
}
assert(PyLong_CheckExact(n) && PyLong_CheckExact(k));
if (Py_SIZE(n) < 0) {
if (_PyLong_IsNegative((PyLongObject *)n)) {
PyErr_SetString(PyExc_ValueError,
"n must be a non-negative integer");
goto error;
}
if (Py_SIZE(k) < 0) {
if (_PyLong_IsNegative((PyLongObject *)k)) {
PyErr_SetString(PyExc_ValueError,
"k must be a non-negative integer");
goto error;
@ -3845,7 +3845,8 @@ math_comb_impl(PyObject *module, PyObject *n, PyObject *k)
if (temp == NULL) {
goto error;
}
if (Py_SIZE(temp) < 0) {
assert(PyLong_Check(temp));
if (_PyLong_IsNegative((PyLongObject *)temp)) {
Py_DECREF(temp);
result = PyLong_FromLong(0);
goto done;

View file

@ -5,6 +5,7 @@
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate()
#include "pycore_object.h" // _Py_CheckSlotResult()
#include "pycore_long.h" // _Py_IsNegative
#include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "pycore_unionobject.h" // _PyUnion_Check()
@ -1483,7 +1484,7 @@ PyNumber_AsSsize_t(PyObject *item, PyObject *err)
/* Whether or not it is less than or equal to
zero is determined by the sign of ob_size
*/
if (_PyLong_Sign(value) < 0)
if (_PyLong_IsNegative((PyLongObject *)value))
result = PY_SSIZE_T_MIN;
else
result = PY_SSIZE_T_MAX;

View file

@ -2,6 +2,7 @@
#include "Python.h"
#include "pycore_object.h" // _Py_FatalRefcountError()
#include "pycore_long.h" // FALSE_TAG TRUE_TAG
#include "pycore_runtime.h" // _Py_ID()
#include <stddef.h>
@ -198,10 +199,14 @@ PyTypeObject PyBool_Type = {
struct _longobject _Py_FalseStruct = {
PyObject_HEAD_INIT(&PyBool_Type)
{ 0, { 0 } }
{ .lv_tag = _PyLong_FALSE_TAG,
{ 0 }
}
};
struct _longobject _Py_TrueStruct = {
PyObject_HEAD_INIT(&PyBool_Type)
{ 1, { 1 } }
{ .lv_tag = _PyLong_TRUE_TAG,
{ 1 }
}
};

View file

@ -4,6 +4,7 @@
#include "pycore_abstract.h" // _PyIndex_Check()
#include "pycore_interp.h" // PyInterpreterState.list
#include "pycore_list.h" // struct _Py_list_state, _PyListIterObject
#include "pycore_long.h" // _PyLong_DigitCount
#include "pycore_object.h" // _PyObject_GC_TRACK()
#include "pycore_tuple.h" // _PyTuple_FromArray()
#include <stddef.h>
@ -2144,24 +2145,21 @@ unsafe_latin_compare(PyObject *v, PyObject *w, MergeState *ms)
static int
unsafe_long_compare(PyObject *v, PyObject *w, MergeState *ms)
{
PyLongObject *vl, *wl; sdigit v0, w0; int res;
PyLongObject *vl, *wl;
intptr_t v0, w0;
int res;
/* Modified from Objects/longobject.c:long_compare, assuming: */
assert(Py_IS_TYPE(v, &PyLong_Type));
assert(Py_IS_TYPE(w, &PyLong_Type));
assert(Py_ABS(Py_SIZE(v)) <= 1);
assert(Py_ABS(Py_SIZE(w)) <= 1);
assert(_PyLong_IsCompact((PyLongObject *)v));
assert(_PyLong_IsCompact((PyLongObject *)w));
vl = (PyLongObject*)v;
wl = (PyLongObject*)w;
v0 = Py_SIZE(vl) == 0 ? 0 : (sdigit)vl->long_value.ob_digit[0];
w0 = Py_SIZE(wl) == 0 ? 0 : (sdigit)wl->long_value.ob_digit[0];
if (Py_SIZE(vl) < 0)
v0 = -v0;
if (Py_SIZE(wl) < 0)
w0 = -w0;
v0 = _PyLong_CompactValue(vl);
w0 = _PyLong_CompactValue(wl);
res = v0 < w0;
assert(res == PyObject_RichCompareBool(v, w, Py_LT));
@ -2359,7 +2357,7 @@ list_sort_impl(PyListObject *self, PyObject *keyfunc, int reverse)
if (keys_are_all_same_type) {
if (key_type == &PyLong_Type &&
ints_are_bounded &&
Py_ABS(Py_SIZE(key)) > 1) {
!_PyLong_IsCompact((PyLongObject *)key)) {
ints_are_bounded = 0;
}

File diff suppressed because it is too large Load diff

View file

@ -33,7 +33,7 @@ validate_step(PyObject *step)
return PyLong_FromLong(1);
step = PyNumber_Index(step);
if (step && _PyLong_Sign(step) == 0) {
if (step && _PyLong_IsZero((PyLongObject *)step)) {
PyErr_SetString(PyExc_ValueError,
"range() arg 3 must not be zero");
Py_CLEAR(step);

View file

@ -445,7 +445,7 @@ _PySlice_GetLongIndices(PySliceObject *self, PyObject *length,
if (start == NULL)
goto error;
if (_PyLong_Sign(start) < 0) {
if (_PyLong_IsNegative((PyLongObject *)start)) {
/* start += length */
PyObject *tmp = PyNumber_Add(start, length);
Py_SETREF(start, tmp);
@ -478,7 +478,7 @@ _PySlice_GetLongIndices(PySliceObject *self, PyObject *length,
if (stop == NULL)
goto error;
if (_PyLong_Sign(stop) < 0) {
if (_PyLong_IsNegative((PyLongObject *)stop)) {
/* stop += length */
PyObject *tmp = PyNumber_Add(stop, length);
Py_SETREF(stop, tmp);
@ -533,7 +533,7 @@ slice_indices(PySliceObject* self, PyObject* len)
if (length == NULL)
return NULL;
if (_PyLong_Sign(length) < 0) {
if (_PyLong_IsNegative((PyLongObject *)length)) {
PyErr_SetString(PyExc_ValueError,
"length should not be negative");
Py_DECREF(length);

View file

@ -8,6 +8,7 @@
#include "pycore_initconfig.h" // _PyStatus_OK()
#include "pycore_moduleobject.h" // _PyModule_GetDef()
#include "pycore_object.h" // _PyType_HasFeature()
#include "pycore_long.h" // _PyLong_IsNegative()
#include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "pycore_typeobject.h" // struct type_cache
@ -7865,7 +7866,7 @@ slot_sq_length(PyObject *self)
return -1;
assert(PyLong_Check(res));
if (Py_SIZE(res) < 0) {
if (_PyLong_IsNegative((PyLongObject *)res)) {
Py_DECREF(res);
PyErr_SetString(PyExc_ValueError,
"__len__() should return >= 0");

View file

@ -2,6 +2,7 @@
#include "Python.h"
#include "pycore_ast.h" // _PyAST_GetDocString()
#include "pycore_compile.h" // _PyASTOptimizeState
#include "pycore_long.h" // _PyLong
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "pycore_format.h" // F_LJUST
@ -152,7 +153,9 @@ check_complexity(PyObject *obj, Py_ssize_t limit)
static PyObject *
safe_multiply(PyObject *v, PyObject *w)
{
if (PyLong_Check(v) && PyLong_Check(w) && Py_SIZE(v) && Py_SIZE(w)) {
if (PyLong_Check(v) && PyLong_Check(w) &&
!_PyLong_IsZero((PyLongObject *)v) && !_PyLong_IsZero((PyLongObject *)w)
) {
size_t vbits = _PyLong_NumBits(v);
size_t wbits = _PyLong_NumBits(w);
if (vbits == (size_t)-1 || wbits == (size_t)-1) {
@ -198,7 +201,9 @@ safe_multiply(PyObject *v, PyObject *w)
static PyObject *
safe_power(PyObject *v, PyObject *w)
{
if (PyLong_Check(v) && PyLong_Check(w) && Py_SIZE(v) && Py_SIZE(w) > 0) {
if (PyLong_Check(v) && PyLong_Check(w) &&
!_PyLong_IsZero((PyLongObject *)v) && _PyLong_IsPositive((PyLongObject *)w)
) {
size_t vbits = _PyLong_NumBits(v);
size_t wbits = PyLong_AsSize_t(w);
if (vbits == (size_t)-1 || wbits == (size_t)-1) {
@ -215,7 +220,9 @@ safe_power(PyObject *v, PyObject *w)
static PyObject *
safe_lshift(PyObject *v, PyObject *w)
{
if (PyLong_Check(v) && PyLong_Check(w) && Py_SIZE(v) && Py_SIZE(w)) {
if (PyLong_Check(v) && PyLong_Check(w) &&
!_PyLong_IsZero((PyLongObject *)v) && !_PyLong_IsZero((PyLongObject *)w)
) {
size_t vbits = _PyLong_NumBits(v);
size_t wbits = PyLong_AsSize_t(w);
if (vbits == (size_t)-1 || wbits == (size_t)-1) {

View file

@ -5,6 +5,7 @@
#include "pycore_ast.h" // _PyAST_Validate()
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_compile.h" // _PyAST_Compile()
#include "pycore_long.h" // _PyLong_CompactValue
#include "pycore_object.h" // _Py_AddToAllObjects()
#include "pycore_pyerrors.h" // _PyErr_NoMemory()
#include "pycore_pystate.h" // _PyThreadState_GET()
@ -2491,7 +2492,7 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
*/
if (PyLong_CheckExact(result)) {
int overflow;
long i_result = PyLong_AsLongAndOverflow(result, &overflow);
Py_ssize_t i_result = PyLong_AsLongAndOverflow(result, &overflow);
/* If this already overflowed, don't even enter the loop. */
if (overflow == 0) {
Py_SETREF(result, NULL);
@ -2505,15 +2506,14 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
return PyLong_FromLong(i_result);
}
if (PyLong_CheckExact(item) || PyBool_Check(item)) {
long b;
Py_ssize_t b;
overflow = 0;
/* Single digits are common, fast, and cannot overflow on unpacking. */
switch (Py_SIZE(item)) {
case -1: b = -(sdigit) ((PyLongObject*)item)->long_value.ob_digit[0]; break;
// Note: the continue goes to the top of the "while" loop that iterates over the elements
case 0: Py_DECREF(item); continue;
case 1: b = ((PyLongObject*)item)->long_value.ob_digit[0]; break;
default: b = PyLong_AsLongAndOverflow(item, &overflow); break;
if (_PyLong_IsCompact((PyLongObject *)item)) {
b = _PyLong_CompactValue((PyLongObject *)item);
}
else {
b = PyLong_AsLongAndOverflow(item, &overflow);
}
if (overflow == 0 &&
(i_result >= 0 ? (b <= LONG_MAX - i_result)

View file

@ -345,8 +345,7 @@ dummy_func(
DEOPT_IF(!PyList_CheckExact(list), BINARY_SUBSCR);
// Deopt unless 0 <= sub < PyList_Size(list)
DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR);
assert(((PyLongObject *)_PyLong_GetZero())->long_value.ob_digit[0] == 0);
DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR);
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR);
STAT_INC(BINARY_SUBSCR, hit);
@ -363,8 +362,7 @@ dummy_func(
DEOPT_IF(!PyTuple_CheckExact(tuple), BINARY_SUBSCR);
// Deopt unless 0 <= sub < PyTuple_Size(list)
DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR);
assert(((PyLongObject *)_PyLong_GetZero())->long_value.ob_digit[0] == 0);
DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR);
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
DEOPT_IF(index >= PyTuple_GET_SIZE(tuple), BINARY_SUBSCR);
STAT_INC(BINARY_SUBSCR, hit);
@ -456,7 +454,7 @@ dummy_func(
DEOPT_IF(!PyList_CheckExact(list), STORE_SUBSCR);
// Ensure nonnegative, zero-or-one-digit ints.
DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), STORE_SUBSCR);
DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), STORE_SUBSCR);
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
// Ensure index < len(list)
DEOPT_IF(index >= PyList_GET_SIZE(list), STORE_SUBSCR);
@ -1755,12 +1753,13 @@ dummy_func(
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), COMPARE_AND_BRANCH);
DEOPT_IF(!PyLong_CheckExact(right), COMPARE_AND_BRANCH);
DEOPT_IF((size_t)(Py_SIZE(left) + 1) > 2, COMPARE_AND_BRANCH);
DEOPT_IF((size_t)(Py_SIZE(right) + 1) > 2, COMPARE_AND_BRANCH);
DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)left), COMPARE_AND_BRANCH);
DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)right), COMPARE_AND_BRANCH);
STAT_INC(COMPARE_AND_BRANCH, hit);
assert(Py_ABS(Py_SIZE(left)) <= 1 && Py_ABS(Py_SIZE(right)) <= 1);
Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->long_value.ob_digit[0];
Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->long_value.ob_digit[0];
assert(_PyLong_DigitCount((PyLongObject *)left) <= 1 &&
_PyLong_DigitCount((PyLongObject *)right) <= 1);
Py_ssize_t ileft = _PyLong_CompactValue((PyLongObject *)left);
Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right);
// 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg
int sign_ish = COMPARISON_BIT(ileft, iright);
_Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);

File diff suppressed because it is too large Load diff

View file

@ -11,6 +11,7 @@
#include "Python.h"
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_code.h" // _PyCode_New()
#include "pycore_long.h" // _PyLong_DigitCount
#include "pycore_hashtable.h" // _Py_hashtable_t
#include "marshal.h" // Py_MARSHAL_VERSION
@ -232,13 +233,13 @@ w_PyLong(const PyLongObject *ob, char flag, WFILE *p)
digit d;
W_TYPE(TYPE_LONG, p);
if (Py_SIZE(ob) == 0) {
if (_PyLong_IsZero(ob)) {
w_long((long)0, p);
return;
}
/* set l to number of base PyLong_MARSHAL_BASE digits */
n = Py_ABS(Py_SIZE(ob));
n = _PyLong_DigitCount(ob);
l = (n-1) * PyLong_MARSHAL_RATIO;
d = ob->long_value.ob_digit[n-1];
assert(d != 0); /* a PyLong is always normalized */
@ -251,7 +252,7 @@ w_PyLong(const PyLongObject *ob, char flag, WFILE *p)
p->error = WFERR_UNMARSHALLABLE;
return;
}
w_long((long)(Py_SIZE(ob) > 0 ? l : -l), p);
w_long((long)(_PyLong_IsNegative(ob) ? -l : l), p);
for (i=0; i < n-1; i++) {
d = ob->long_value.ob_digit[i];
@ -839,7 +840,7 @@ r_PyLong(RFILE *p)
if (ob == NULL)
return NULL;
Py_SET_SIZE(ob, n > 0 ? size : -size);
_PyLong_SetSignAndDigitCount(ob, n < 0 ? -1 : 1, size);
for (i = 0; i < size-1; i++) {
d = 0;

View file

@ -1308,7 +1308,7 @@ _Py_Specialize_BinarySubscr(
PyTypeObject *container_type = Py_TYPE(container);
if (container_type == &PyList_Type) {
if (PyLong_CheckExact(sub)) {
if (Py_SIZE(sub) == 0 || Py_SIZE(sub) == 1) {
if (_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) {
instr->op.code = BINARY_SUBSCR_LIST_INT;
goto success;
}
@ -1321,7 +1321,7 @@ _Py_Specialize_BinarySubscr(
}
if (container_type == &PyTuple_Type) {
if (PyLong_CheckExact(sub)) {
if (Py_SIZE(sub) == 0 || Py_SIZE(sub) == 1) {
if (_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) {
instr->op.code = BINARY_SUBSCR_TUPLE_INT;
goto success;
}
@ -1389,7 +1389,7 @@ _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub, _Py_CODEUNIT *ins
PyTypeObject *container_type = Py_TYPE(container);
if (container_type == &PyList_Type) {
if (PyLong_CheckExact(sub)) {
if ((Py_SIZE(sub) == 0 || Py_SIZE(sub) == 1)
if (_PyLong_IsNonNegativeCompact((PyLongObject *)sub)
&& ((PyLongObject *)sub)->long_value.ob_digit[0] < (size_t)PyList_GET_SIZE(container))
{
instr->op.code = STORE_SUBSCR_LIST_INT;
@ -2007,7 +2007,7 @@ _Py_Specialize_CompareAndBranch(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *inst
goto success;
}
if (PyLong_CheckExact(lhs)) {
if (Py_ABS(Py_SIZE(lhs)) <= 1 && Py_ABS(Py_SIZE(rhs)) <= 1) {
if (_PyLong_IsCompact((PyLongObject *)lhs) && _PyLong_IsCompact((PyLongObject *)rhs)) {
instr->op.code = COMPARE_AND_BRANCH_INT;
goto success;
}

View file

@ -309,7 +309,7 @@ class Printer:
return f"& {name}._object.ob_base.ob_base"
def _generate_int_for_bits(self, name: str, i: int, digit: int) -> None:
sign = -1 if i < 0 else 0 if i == 0 else +1
sign = (i > 0) - (i < 0)
i = abs(i)
digits: list[int] = []
while i:
@ -318,10 +318,12 @@ class Printer:
self.write("static")
with self.indent():
with self.block("struct"):
self.write("PyObject_VAR_HEAD")
self.write("PyObject ob_base;")
self.write("uintptr_t lv_tag;")
self.write(f"digit ob_digit[{max(1, len(digits))}];")
with self.block(f"{name} =", ";"):
self.object_var_head("PyLong_Type", sign*len(digits))
self.object_head("PyLong_Type")
self.write(f".lv_tag = TAG_FROM_SIGN_AND_SIZE({sign}, {len(digits)}),")
if digits:
ds = ", ".join(map(str, digits))
self.write(f".ob_digit = {{ {ds} }},")
@ -345,7 +347,7 @@ class Printer:
self.write('#error "PYLONG_BITS_IN_DIGIT should be 15 or 30"')
self.write("#endif")
# If neither clause applies, it won't compile
return f"& {name}.ob_base.ob_base"
return f"& {name}.ob_base"
def generate_float(self, name: str, x: float) -> str:
with self.block(f"static PyFloatObject {name} =", ";"):

View file

@ -882,9 +882,15 @@ class PyLongObjectPtr(PyObjectPtr):
def proxyval(self, visited):
'''
Python's Include/longobjrep.h has this declaration:
struct _longobject {
PyObject_VAR_HEAD
typedef struct _PyLongValue {
uintptr_t lv_tag; /* Number of digits, sign and flags */
digit ob_digit[1];
} _PyLongValue;
struct _longobject {
PyObject_HEAD
_PyLongValue long_value;
};
with this description:
@ -897,11 +903,13 @@ class PyLongObjectPtr(PyObjectPtr):
#define PyLong_SHIFT 30
#define PyLong_SHIFT 15
'''
ob_size = int(self.field('ob_size'))
if ob_size == 0:
long_value = self.field('long_value')
lv_tag = int(long_value['lv_tag'])
size = lv_tag >> 3
if size == 0:
return 0
ob_digit = self.field('long_value')['ob_digit']
ob_digit = long_value['ob_digit']
if gdb.lookup_type('digit').sizeof == 2:
SHIFT = 15
@ -909,9 +917,9 @@ class PyLongObjectPtr(PyObjectPtr):
SHIFT = 30
digits = [int(ob_digit[i]) * 2**(SHIFT*i)
for i in safe_range(abs(ob_size))]
for i in safe_range(size)]
result = sum(digits)
if ob_size < 0:
if (lv_tag & 3) == 2:
result = -result
return result