Skip to content

Commit

Permalink
PROGRESS
Browse files Browse the repository at this point in the history
  • Loading branch information
mdboom committed Dec 2, 2023
1 parent 05a370a commit b86af90
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 22 deletions.
8 changes: 5 additions & 3 deletions Include/cpython/longintrepr.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,14 @@ PyAPI_FUNC(PyLongObject*) _PyLong_FromDigits(
* if user code didn't need them inlined. */

#define _PyLong_SIGN_MASK 3
#define _PyLong_NON_SIZE_BITS 3
#define _PyLong_NON_SIZE_BITS 4
#define _PyLong_COMPACT_FLAG 8


static inline int
_PyLong_IsCompact(const PyLongObject* op) {
assert(PyType_HasFeature((op)->ob_base.ob_type, Py_TPFLAGS_LONG_SUBCLASS));
return op->long_value.lv_tag < (2 << _PyLong_NON_SIZE_BITS);
return op->long_value.lv_tag & _PyLong_COMPACT_FLAG;
}

#define PyUnstable_Long_IsCompact _PyLong_IsCompact
Expand All @@ -121,7 +122,8 @@ _PyLong_CompactValue(const PyLongObject *op)
assert(PyType_HasFeature((op)->ob_base.ob_type, Py_TPFLAGS_LONG_SUBCLASS));
assert(PyUnstable_Long_IsCompact(op));
Py_ssize_t sign = 1 - (op->long_value.lv_tag & _PyLong_SIGN_MASK);
return sign * (Py_ssize_t)op->long_value.ob_digit[0];
// MGDTODO: Constant
return sign * ((Py_ssize_t)op->long_value.lv_tag >> 32);
}

#define PyUnstable_Long_CompactValue _PyLong_CompactValue
Expand Down
27 changes: 18 additions & 9 deletions Include/internal/pycore_long.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ PyAPI_FUNC(int) _PyLong_Size_t_Converter(PyObject *, void *);
#define SIGN_MASK 3
#define SIGN_ZERO 1
#define SIGN_NEGATIVE 2
#define NON_SIZE_BITS 3
#define NON_SIZE_BITS 4

/* The functions _PyLong_IsCompact and _PyLong_CompactValue are defined
* in Include/cpython/longobject.h, since they need to be inline.
Expand All @@ -202,15 +202,17 @@ static_assert(NON_SIZE_BITS == _PyLong_NON_SIZE_BITS, "NON_SIZE_BITS does not ma
static inline int
_PyLong_IsNonNegativeCompact(const PyLongObject* op) {
assert(PyLong_Check(op));
return op->long_value.lv_tag <= (1 << NON_SIZE_BITS);
// MGDTODO: Constant
return (op->long_value.lv_tag & 8) && !(op->long_value.lv_tag & SIGN_MASK);
}


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);
// MGDTODO: Constant
return (a->long_value.lv_tag & b->long_value.lv_tag) & 8;
}

static inline bool
Expand All @@ -235,7 +237,12 @@ static inline Py_ssize_t
_PyLong_DigitCount(const PyLongObject *op)
{
assert(PyLong_Check(op));
return op->long_value.lv_tag >> NON_SIZE_BITS;
// MGDTODO: Constant
if (op->long_value.lv_tag & 8) {
return 1;
} else {
return op->long_value.lv_tag >> NON_SIZE_BITS;
}
}

/* Equivalent to _PyLong_DigitCount(op) * _PyLong_NonCompactSign(op) */
Expand All @@ -244,7 +251,7 @@ _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);
return sign * _PyLong_DigitCount(op);
}

static inline int
Expand All @@ -270,22 +277,25 @@ _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))
// MGDTODO: Constant
#define TAG_FROM_SIGN_AND_SIZE(sign, size) ((1 - (sign)) | ((size) << NON_SIZE_BITS) | ((size == 1) ? 8 : 0))

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);
// MGDTODO: Don't overwrite high bytes if size == 1
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);
// MGDTODO: Don't overwrite high bytes if size == 1
op->long_value.lv_tag = (((size_t)size) << NON_SIZE_BITS) | (op->long_value.lv_tag & SIGN_MASK) | ((size == 1) ? 8 : 0);
}

#define NON_SIZE_MASK ~((1 << NON_SIZE_BITS) - 1)
Expand All @@ -303,8 +313,7 @@ _PyLong_FlipSign(PyLongObject *op) {
.long_value = { \
.lv_tag = TAG_FROM_SIGN_AND_SIZE( \
(val) == 0 ? 0 : ((val) < 0 ? -1 : 1), \
(val) == 0 ? 0 : 1), \
{ ((val) >= 0 ? (val) : -(val)) }, \
(val) == 0 ? 0 : 1) | ((Py_ssize_t)val << 32) \
} \
}

Expand Down
49 changes: 39 additions & 10 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ long_normalize(PyLongObject *v)
Py_ssize_t j = _PyLong_DigitCount(v);
Py_ssize_t i = j;

// MGDTODO: Assert this works with compact

while (i > 0 && v->long_value.ob_digit[i-1] == 0)
--i;
if (i != j) {
Expand All @@ -133,6 +135,8 @@ long_normalize(PyLongObject *v)
/* Allocate a new int object with size digits.
Return NULL and set exception if we run out of memory. */

// MGDTODO: I think this is wrong

#define MAX_LONG_DIGITS \
((PY_SSIZE_T_MAX - offsetof(PyLongObject, long_value.ob_digit))/sizeof(digit))

Expand All @@ -149,13 +153,14 @@ _PyLong_New(Py_ssize_t size)
/* Fast operations for single digit integers (including zero)
* assume that there is always at least one digit present. */
Py_ssize_t ndigits = size ? size : 1;
Py_ssize_t ndigits_memory = ndigits <= 1 ? 0 : ndigits;
/* Number of bytes needed is: offsetof(PyLongObject, ob_digit) +
sizeof(digit)*size. Previous incarnations of this code used
sizeof() instead of the offsetof, but this risks being
incorrect in the presence of padding between the header
and the digits. */
result = PyObject_Malloc(offsetof(PyLongObject, long_value.ob_digit) +
ndigits*sizeof(digit));
ndigits_memory*sizeof(digit));
if (!result) {
PyErr_NoMemory();
return NULL;
Expand All @@ -164,14 +169,16 @@ _PyLong_New(Py_ssize_t size)
_PyObject_Init((PyObject*)result, &PyLong_Type);
/* The digit has to be initialized explicitly to avoid
* use-of-uninitialized-value. */
result->long_value.ob_digit[0] = 0;
if (ndigits > 1) {
result->long_value.ob_digit[0] = 0;
}
return result;
}

PyLongObject *
_PyLong_FromDigits(int negative, Py_ssize_t digit_count, digit *digits)
{
assert(digit_count >= 0);
assert(digit_count > 0);
if (digit_count == 0) {
return (PyLongObject *)_PyLong_GetZero();
}
Expand All @@ -194,6 +201,14 @@ _PyLong_Copy(PyLongObject *src)
stwodigits ival = medium_value(src);
if (IS_SMALL_INT(ival)) {
return get_small_int((sdigit)ival);
} else {
PyLongObject *result = _PyLong_New(1);
if (result == NULL) {
PyErr_NoMemory();
return NULL;
}
result->long_value.lv_tag = src->long_value.lv_tag;
return (PyObject *)result;
}
}
Py_ssize_t size = _PyLong_DigitCount(src);
Expand All @@ -206,15 +221,16 @@ _PyLong_FromMedium(sdigit x)
assert(!IS_SMALL_INT(x));
assert(is_medium_int(x));
/* We could use a freelist here */
PyLongObject *v = PyObject_Malloc(sizeof(PyLongObject));
// MGDTODO: This is weird
PyLongObject *v = PyObject_Malloc(sizeof(PyLongObject) - sizeof(digit));
if (v == NULL) {
PyErr_NoMemory();
return NULL;
}
digit abs_x = x < 0 ? -x : x;
_PyLong_SetSignAndDigitCount(v, x<0?-1:1, 1);
_PyObject_Init((PyObject*)v, &PyLong_Type);
v->long_value.ob_digit[0] = abs_x;
v->long_value.lv_tag |= ((Py_ssize_t)abs_x << 32);
return (PyObject*)v;
}

Expand Down Expand Up @@ -791,8 +807,13 @@ _PyLong_NumBits(PyObject *vv)
assert(v != NULL);
assert(PyLong_Check(v));
ndigits = _PyLong_DigitCount(v);
assert(ndigits == 0 || v->long_value.ob_digit[ndigits - 1] != 0);
if (ndigits > 0) {
// MGDTODO: Constants
assert(ndigits == 0 || v->long_value.lv_tag & 8 || v->long_value.ob_digit[ndigits - 1] != 0);
if (ndigits == 1) {
digit msd = v->long_value.ob_digit[ndigits - 1];
result = bit_length_digit(msd);
}
else if (ndigits > 1) {
digit msd = v->long_value.ob_digit[ndigits - 1];
if ((size_t)(ndigits - 1) > SIZE_MAX / (size_t)PyLong_SHIFT)
goto Overflow;
Expand Down Expand Up @@ -974,7 +995,14 @@ _PyLong_AsByteArray(PyLongObject* v,
accumbits = 0;
carry = do_twos_comp ? 1 : 0;
for (i = 0; i < ndigits; ++i) {
digit thisdigit = v->long_value.ob_digit[i];
digit thisdigit;
// MGDTODO: Performance
if (ndigits <= 1) {
thisdigit = (digit)(v->long_value.lv_tag >> 32);
}
else {
digit thisdigit = v->long_value.ob_digit[i];
}
if (do_twos_comp) {
thisdigit = (thisdigit ^ PyLong_MASK) + carry;
carry = thisdigit >> PyLong_SHIFT;
Expand Down Expand Up @@ -5608,8 +5636,9 @@ long_subtype_new(PyTypeObject *type, PyObject *x, PyObject *obase)
n = _PyLong_DigitCount(tmp);
/* Fast operations for single digit integers (including zero)
* assume that there is always at least one digit present. */
if (n == 0) {
n = 1;
// MGDTODO: Comment
if (n <= 2) {
n = 0;
}
newobj = (PyLongObject *)type->tp_alloc(type, n);
if (newobj == NULL) {
Expand Down

0 comments on commit b86af90

Please sign in to comment.