/* Long value tag bits:
* 0-1: Sign bits value = (1-sign), ie. negative=2, positive=0, zero=1.
- * 2: Reserved for immortality bit
+ * 2: Set to 1 for the small ints
* 3+ Unsigned digit count
*/
#define SIGN_MASK 3
#define SIGN_ZERO 1
#define SIGN_NEGATIVE 2
#define NON_SIZE_BITS 3
+#define IMMORTALITY_BIT_MASK (1 << 2)
/* The functions _PyLong_IsCompact and _PyLong_CompactValue are defined
* in Include/cpython/longobject.h, since they need to be inline.
static inline int
_PyLong_IsNonNegativeCompact(const PyLongObject* op) {
assert(PyLong_Check(op));
- return op->long_value.lv_tag <= (1 << NON_SIZE_BITS);
+ return ((op->long_value.lv_tag & ~IMMORTALITY_BIT_MASK) <= (1 << NON_SIZE_BITS));
}
.long_value = { \
.lv_tag = TAG_FROM_SIGN_AND_SIZE( \
(val) == 0 ? 0 : ((val) < 0 ? -1 : 1), \
- (val) == 0 ? 0 : 1), \
+ (val) == 0 ? 0 : 1) | IMMORTALITY_BIT_MASK, \
{ ((val) >= 0 ? (val) : -(val)) }, \
} \
}
#include "parts.h"
+#define Py_BUILD_CORE
+#include "internal/pycore_long.h" // IMMORTALITY_BIT_MASK
+
int verify_immortality(PyObject *object)
{
assert(_Py_IsImmortal(object));
test_immortal_small_ints(PyObject *self, PyObject *Py_UNUSED(ignored))
{
for (int i = -5; i <= 256; i++) {
- assert(verify_immortality(PyLong_FromLong(i)));
+ PyObject *obj = PyLong_FromLong(i);
+ assert(verify_immortality(obj));
+ int has_int_immortal_bit = ((PyLongObject *)obj)->long_value.lv_tag & IMMORTALITY_BIT_MASK;
+ assert(has_int_immortal_bit);
+ }
+ for (int i = 257; i <= 260; i++) {
+ PyObject *obj = PyLong_FromLong(i);
+ assert(obj);
+ int has_int_immortal_bit = ((PyLongObject *)obj)->long_value.lv_tag & IMMORTALITY_BIT_MASK;
+ assert(!has_int_immortal_bit);
+ Py_DECREF(obj);
}
Py_RETURN_NONE;
}
}
static inline int
-compact_int_is_small(PyObject *self)
+/// Return 1 if the object is one of the immortal small ints
+_long_is_small_int(PyObject *op)
{
- PyLongObject *pylong = (PyLongObject *)self;
- assert(_PyLong_IsCompact(pylong));
- stwodigits ival = medium_value(pylong);
- if (IS_SMALL_INT(ival)) {
- PyLongObject *small_pylong = (PyLongObject *)get_small_int((sdigit)ival);
- if (pylong == small_pylong) {
- return 1;
- }
- }
- return 0;
+ PyLongObject *long_object = (PyLongObject *)op;
+ int is_small_int = (long_object->long_value.lv_tag & IMMORTALITY_BIT_MASK) != 0;
+ assert((!is_small_int) || PyLong_CheckExact(op));
+ return is_small_int;
}
void
_PyLong_ExactDealloc(PyObject *self)
{
assert(PyLong_CheckExact(self));
+ if (_long_is_small_int(self)) {
+ // See PEP 683, section Accidental De-Immortalizing for details
+ _Py_SetImmortal(self);
+ return;
+ }
if (_PyLong_IsCompact((PyLongObject *)self)) {
- #ifndef Py_GIL_DISABLED
- if (compact_int_is_small(self)) {
- // See PEP 683, section Accidental De-Immortalizing for details
- _Py_SetImmortal(self);
- return;
- }
- #endif
_Py_FREELIST_FREE(ints, self, PyObject_Free);
return;
}
static void
long_dealloc(PyObject *self)
{
- assert(self);
- if (_PyLong_IsCompact((PyLongObject *)self)) {
- if (compact_int_is_small(self)) {
- /* This should never get called, but we also don't want to SEGV if
- * we accidentally decref small Ints out of existence. Instead,
- * since small Ints are immortal, re-set the reference count.
- *
- * See PEP 683, section Accidental De-Immortalizing for details
- */
- _Py_SetImmortal(self);
- return;
- }
- if (PyLong_CheckExact(self)) {
- _Py_FREELIST_FREE(ints, self, PyObject_Free);
- return;
- }
+ if (_long_is_small_int(self)) {
+ /* This should never get called, but we also don't want to SEGV if
+ * we accidentally decref small Ints out of existence. Instead,
+ * since small Ints are immortal, re-set the reference count.
+ *
+ * See PEP 683, section Accidental De-Immortalizing for details
+ */
+ _Py_SetImmortal(self);
+ return;
+ }
+ if (PyLong_CheckExact(self) && _PyLong_IsCompact((PyLongObject *)self)) {
+ _Py_FREELIST_FREE(ints, self, PyObject_Free);
+ return;
}
-
Py_TYPE(self)->tp_free(self);
}
return NULL;
}
assert(PyLong_Check(newobj));
- newobj->long_value.lv_tag = tmp->long_value.lv_tag;
+ newobj->long_value.lv_tag = tmp->long_value.lv_tag & ~IMMORTALITY_BIT_MASK;
for (i = 0; i < n; i++) {
newobj->long_value.ob_digit[i] = tmp->long_value.ob_digit[i];
}
def proxyval(self, visited):
'''
- Python's Include/longinterpr.h has this declaration:
+ Python's Include/cpython/longinterpr.h has this declaration:
typedef struct _PyLongValue {
uintptr_t lv_tag; /* Number of digits, sign and flags */
- 0: Positive
- 1: Zero
- 2: Negative
- The third lowest bit of lv_tag is reserved for an immortality flag, but is
- not currently used.
+ The third lowest bit of lv_tag is set to 1 for the small ints and 0 otherwise.
where SHIFT can be either:
#define PyLong_SHIFT 30