From cfb70cb2fdb02457c02b7ffb3792da78d2292ba4 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Wed, 4 Dec 2024 21:37:34 +0100 Subject: [PATCH 01/27] Faster check for small ints in long_dealloc --- Objects/longobject.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index 4aa35685b509f2..20362fad3bd012 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -3611,24 +3611,28 @@ long_richcompare(PyObject *self, PyObject *other, int op) Py_RETURN_RICHCOMPARE(result, 0, op); } +static inline int +/// Return 1 if the object is one of the immortal small ints +_long_is_small_int(PyObject *op) +{ + return (((PyObject *)(&_PyLong_SMALL_INTS[0]) <= op) + && (op <= (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS +_PY_NSMALLPOSINTS])); +} static void long_dealloc(PyObject *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. - */ - PyLongObject *pylong = (PyLongObject*)self; - if (pylong && _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) { - _Py_SetImmortal(self); - return; - } - } + #ifndef Py_GIL_DISABLED + 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; } + #endif Py_TYPE(self)->tp_free(self); } From 426dd09ad15cc3d1d72b086d492b8567ea0b4889 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 22:14:45 +0000 Subject: [PATCH 02/27] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2024-12-04-22-14-40.gh-issue-127119._hpyFE.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-12-04-22-14-40.gh-issue-127119._hpyFE.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-04-22-14-40.gh-issue-127119._hpyFE.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-04-22-14-40.gh-issue-127119._hpyFE.rst new file mode 100644 index 00000000000000..f021bd490f488c --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-04-22-14-40.gh-issue-127119._hpyFE.rst @@ -0,0 +1 @@ +Slightly optimize the :class:`int` deallocator. From 46396428b45085d9643b99e33f51d1491163cb3c Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 5 Dec 2024 20:39:07 +0100 Subject: [PATCH 03/27] Use int immortality bit for small int check --- Include/internal/pycore_long.h | 7 +++++-- Modules/_testcapi/immortal.c | 10 +++++++++- Objects/longobject.c | 6 ++++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h index 196b4152280a35..8b05d8a6e1be2a 100644 --- a/Include/internal/pycore_long.h +++ b/Include/internal/pycore_long.h @@ -157,13 +157,14 @@ PyAPI_FUNC(int) _PyLong_Size_t_Converter(PyObject *, void *); /* Long value tag bits: * 0-1: Sign bits value = (1-sign), ie. negative=2, positive=0, zero=1. - * 2: Reserved for immortality bit + * 2: Reserved for immortality bit. 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. @@ -262,6 +263,8 @@ _PyLong_SameSign(const PyLongObject *a, const PyLongObject *b) return (a->long_value.lv_tag & SIGN_MASK) == (b->long_value.lv_tag & SIGN_MASK); } +#define IMMORTAL_BIT(val) ((_PY_NSMALLNEGINTS <= val < _PY_NSMALLPOSINTS) * IMMORTALITY_BIT_MASK) + #define TAG_FROM_SIGN_AND_SIZE(sign, size) \ ((uintptr_t)(1 - (sign)) | ((uintptr_t)(size) << NON_SIZE_BITS)) @@ -296,7 +299,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 ? 0 : 1) | IMMORTAL_BIT(val), \ { ((val) >= 0 ? (val) : -(val)) }, \ } \ } diff --git a/Modules/_testcapi/immortal.c b/Modules/_testcapi/immortal.c index 9f81389811c645..2352a43affb638 100644 --- a/Modules/_testcapi/immortal.c +++ b/Modules/_testcapi/immortal.c @@ -26,7 +26,15 @@ static PyObject * 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 & (1 << 2); + assert(has_int_immortal_bit); + } + for (int i = 257; i <= 260; i++) { + PyObject *obj = PyLong_FromLong(i); + int has_int_immortal_bit = ((PyLongObject *)obj)->long_value.lv_tag & (1 << 2); + assert(!has_int_immortal_bit); } Py_RETURN_NONE; } diff --git a/Objects/longobject.c b/Objects/longobject.c index 20362fad3bd012..56ee9891f65c47 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -3615,9 +3615,11 @@ static inline int /// Return 1 if the object is one of the immortal small ints _long_is_small_int(PyObject *op) { - return (((PyObject *)(&_PyLong_SMALL_INTS[0]) <= op) - && (op <= (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS +_PY_NSMALLPOSINTS])); + PyLongObject *long_object = (PyLongObject *)op; + + return long_object->long_value.lv_tag | IMMORTALITY_BIT_MASK; } + static void long_dealloc(PyObject *self) { From 3b6e1fe9bf1dd085e4f74f3ed95af6cc54c8fcce Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 5 Dec 2024 20:44:40 +0100 Subject: [PATCH 04/27] fix compiler warnings --- Include/internal/pycore_long.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h index 8b05d8a6e1be2a..cb88a81abed85c 100644 --- a/Include/internal/pycore_long.h +++ b/Include/internal/pycore_long.h @@ -263,7 +263,7 @@ _PyLong_SameSign(const PyLongObject *a, const PyLongObject *b) return (a->long_value.lv_tag & SIGN_MASK) == (b->long_value.lv_tag & SIGN_MASK); } -#define IMMORTAL_BIT(val) ((_PY_NSMALLNEGINTS <= val < _PY_NSMALLPOSINTS) * IMMORTALITY_BIT_MASK) +#define IMMORTAL_BIT(val) ((_PY_NSMALLNEGINTS <= val) && (val < _PY_NSMALLPOSINTS)) * IMMORTALITY_BIT_MASK) #define TAG_FROM_SIGN_AND_SIZE(sign, size) \ ((uintptr_t)(1 - (sign)) | ((uintptr_t)(size) << NON_SIZE_BITS)) From 5eca8120f4400aba1cb4878c6977d896d1eef6b2 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 5 Dec 2024 20:52:58 +0100 Subject: [PATCH 05/27] compiler warnings --- Include/internal/pycore_long.h | 2 +- Objects/longobject.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h index cb88a81abed85c..b82c2ca53964b4 100644 --- a/Include/internal/pycore_long.h +++ b/Include/internal/pycore_long.h @@ -263,7 +263,7 @@ _PyLong_SameSign(const PyLongObject *a, const PyLongObject *b) return (a->long_value.lv_tag & SIGN_MASK) == (b->long_value.lv_tag & SIGN_MASK); } -#define IMMORTAL_BIT(val) ((_PY_NSMALLNEGINTS <= val) && (val < _PY_NSMALLPOSINTS)) * IMMORTALITY_BIT_MASK) +#define IMMORTAL_BIT(val) (((_PY_NSMALLNEGINTS <= val) && (val < _PY_NSMALLPOSINTS)) * IMMORTALITY_BIT_MASK) #define TAG_FROM_SIGN_AND_SIZE(sign, size) \ ((uintptr_t)(1 - (sign)) | ((uintptr_t)(size) << NON_SIZE_BITS)) diff --git a/Objects/longobject.c b/Objects/longobject.c index 56ee9891f65c47..eeabe7a04e24ab 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -3617,7 +3617,7 @@ _long_is_small_int(PyObject *op) { PyLongObject *long_object = (PyLongObject *)op; - return long_object->long_value.lv_tag | IMMORTALITY_BIT_MASK; + return (int)(long_object->long_value.lv_tag | IMMORTALITY_BIT_MASK); } static void From fedc102ec8d9a33b99bfd49b87de57b4d8187c02 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 5 Dec 2024 21:04:22 +0100 Subject: [PATCH 06/27] some documentation updates --- Include/cpython/longintrepr.h | 4 ++-- Tools/gdb/libpython.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Include/cpython/longintrepr.h b/Include/cpython/longintrepr.h index c60ccc463653f9..d4d6eadb8841b6 100644 --- a/Include/cpython/longintrepr.h +++ b/Include/cpython/longintrepr.h @@ -76,8 +76,8 @@ typedef long stwodigits; /* signed variant of twodigits */ - 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 reserved for an immortality flag, and is + set to 1 for the small ints. In a normalized number, ob_digit[ndigits-1] (the most significant digit) is never zero. Also, in all cases, for all valid i, diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 698ecbd3b549aa..c6dfba9e0ecb19 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -890,7 +890,7 @@ class PyLongObjectPtr(PyObjectPtr): 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 */ @@ -909,8 +909,8 @@ def proxyval(self, visited): - 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 reserved for an immortality flag,and is + set to 1 for the small ints. where SHIFT can be either: #define PyLong_SHIFT 30 From c733d255e9d707de0540fa02a9dd8436824e447c Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 5 Dec 2024 21:04:49 +0100 Subject: [PATCH 07/27] Update Objects/longobject.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Objects/longobject.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index eeabe7a04e24ab..36ff31c6cdcd5d 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -3623,7 +3623,7 @@ _long_is_small_int(PyObject *op) static void long_dealloc(PyObject *self) { - #ifndef Py_GIL_DISABLED +#ifndef Py_GIL_DISABLED 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, @@ -3634,7 +3634,7 @@ long_dealloc(PyObject *self) _Py_SetImmortal(self); return; } - #endif +#endif Py_TYPE(self)->tp_free(self); } From 39da0ea5649b9e09020c0b59fbf72f5f0837ca15 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 5 Dec 2024 21:06:35 +0100 Subject: [PATCH 08/27] Update Tools/gdb/libpython.py --- Tools/gdb/libpython.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index c6dfba9e0ecb19..5013da2b74c5e0 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -909,7 +909,7 @@ def proxyval(self, visited): - 0: Positive - 1: Zero - 2: Negative - The third lowest bit of lv_tag is reserved for an immortality flag,and is + The third lowest bit of lv_tag is reserved for an immortality flag, and is set to 1 for the small ints. where SHIFT can be either: From 5058e53c4e62c8181914f084f7b17ac50cf1a8d7 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 5 Dec 2024 22:10:00 +0100 Subject: [PATCH 09/27] tests! --- Include/internal/pycore_long.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h index b82c2ca53964b4..f3e7073e0460d2 100644 --- a/Include/internal/pycore_long.h +++ b/Include/internal/pycore_long.h @@ -263,7 +263,7 @@ _PyLong_SameSign(const PyLongObject *a, const PyLongObject *b) return (a->long_value.lv_tag & SIGN_MASK) == (b->long_value.lv_tag & SIGN_MASK); } -#define IMMORTAL_BIT(val) (((_PY_NSMALLNEGINTS <= val) && (val < _PY_NSMALLPOSINTS)) * IMMORTALITY_BIT_MASK) +#define IMMORTAL_BIT(val) (((-_PY_NSMALLNEGINTS <= val) && (val < _PY_NSMALLPOSINTS)) * IMMORTALITY_BIT_MASK) #define TAG_FROM_SIGN_AND_SIZE(sign, size) \ ((uintptr_t)(1 - (sign)) | ((uintptr_t)(size) << NON_SIZE_BITS)) From 8a3d00fe222f2fdc1b5867cb4e88725e884cf281 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 5 Dec 2024 22:34:21 +0100 Subject: [PATCH 10/27] tests! --- Objects/longobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index 36ff31c6cdcd5d..2a78abf8f86918 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -3617,7 +3617,7 @@ _long_is_small_int(PyObject *op) { PyLongObject *long_object = (PyLongObject *)op; - return (int)(long_object->long_value.lv_tag | IMMORTALITY_BIT_MASK); + return (int)(long_object->long_value.lv_tag & IMMORTALITY_BIT_MASK); } static void From 878207ab70b658dbef1e22286b2ed990a200ed8b Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 5 Dec 2024 23:41:56 +0100 Subject: [PATCH 11/27] update _PyLong_IsNonNegativeCompact --- Include/internal/pycore_long.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h index f3e7073e0460d2..1456c204cb8349 100644 --- a/Include/internal/pycore_long.h +++ b/Include/internal/pycore_long.h @@ -195,7 +195,7 @@ PyAPI_FUNC(int) _PyLong_Size_t_Converter(PyObject *, void *); 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)); } From 068a16acd258a73495a1cef86bc1fa5da92ad62e Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Wed, 11 Dec 2024 20:04:22 +0100 Subject: [PATCH 12/27] Apply suggestions from code review Co-authored-by: Mark Shannon --- Include/internal/pycore_long.h | 2 +- Objects/longobject.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h index 1456c204cb8349..0a52279960d27c 100644 --- a/Include/internal/pycore_long.h +++ b/Include/internal/pycore_long.h @@ -157,7 +157,7 @@ PyAPI_FUNC(int) _PyLong_Size_t_Converter(PyObject *, void *); /* Long value tag bits: * 0-1: Sign bits value = (1-sign), ie. negative=2, positive=0, zero=1. - * 2: Reserved for immortality bit. Set to 1 for the small ints + * 2: Set to 1 for the small ints * 3+ Unsigned digit count */ #define SIGN_MASK 3 diff --git a/Objects/longobject.c b/Objects/longobject.c index 2a78abf8f86918..bb98c603c2e881 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -3617,7 +3617,7 @@ _long_is_small_int(PyObject *op) { PyLongObject *long_object = (PyLongObject *)op; - return (int)(long_object->long_value.lv_tag & IMMORTALITY_BIT_MASK); + return (long_object->long_value.lv_tag & IMMORTALITY_BIT_MASK) != 0; } static void From a65ec5a6d3f122c1af473a3e4817c340587fb73d Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Wed, 11 Dec 2024 20:04:39 +0100 Subject: [PATCH 13/27] review comments --- Include/internal/pycore_long.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h index 1456c204cb8349..9cde805e68e893 100644 --- a/Include/internal/pycore_long.h +++ b/Include/internal/pycore_long.h @@ -263,8 +263,6 @@ _PyLong_SameSign(const PyLongObject *a, const PyLongObject *b) return (a->long_value.lv_tag & SIGN_MASK) == (b->long_value.lv_tag & SIGN_MASK); } -#define IMMORTAL_BIT(val) (((-_PY_NSMALLNEGINTS <= val) && (val < _PY_NSMALLPOSINTS)) * IMMORTALITY_BIT_MASK) - #define TAG_FROM_SIGN_AND_SIZE(sign, size) \ ((uintptr_t)(1 - (sign)) | ((uintptr_t)(size) << NON_SIZE_BITS)) @@ -299,7 +297,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) | IMMORTAL_BIT(val), \ + (val) == 0 ? 0 : 1) | IMMORTALITY_BIT_MASK, \ { ((val) >= 0 ? (val) : -(val)) }, \ } \ } From 32b6e44268cc66d104d94f27dde799e257f3ccb9 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Wed, 11 Dec 2024 20:05:56 +0100 Subject: [PATCH 14/27] spacing --- Objects/longobject.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index bb98c603c2e881..f3e4aeb7141651 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -3616,7 +3616,6 @@ static inline int _long_is_small_int(PyObject *op) { PyLongObject *long_object = (PyLongObject *)op; - return (long_object->long_value.lv_tag & IMMORTALITY_BIT_MASK) != 0; } From e232ca4b4ceb106ea6146b677c88f7b476e56ea5 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Wed, 11 Dec 2024 21:21:01 +0100 Subject: [PATCH 15/27] Update Tools/gdb/libpython.py --- Tools/gdb/libpython.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 5013da2b74c5e0..97d7517208d2a7 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -909,8 +909,7 @@ def proxyval(self, visited): - 0: Positive - 1: Zero - 2: Negative - The third lowest bit of lv_tag is reserved for an immortality flag, and is - set to 1 for the small ints. + 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 From 99a2fc755fb25463c7b587bdecf7fb938de65a57 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Fri, 13 Dec 2024 11:52:58 +0100 Subject: [PATCH 16/27] whitespace --- Objects/longobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index a59f3f80ff8b3a..5ec88089f64853 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -3655,7 +3655,7 @@ long_dealloc(PyObject *self) _Py_SetImmortal(self); return; } -#endif +#endif if (PyLong_CheckExact(self) && _PyLong_IsCompact((PyLongObject *)self)) { _Py_FREELIST_FREE(ints, self, PyObject_Free); return; From 989486604d7a6bab50878136ea97f198f824d938 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 17 Dec 2024 23:33:17 +0100 Subject: [PATCH 17/27] Apply suggestions from code review --- Modules/_testcapi/immortal.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Modules/_testcapi/immortal.c b/Modules/_testcapi/immortal.c index 2352a43affb638..8571530404ea98 100644 --- a/Modules/_testcapi/immortal.c +++ b/Modules/_testcapi/immortal.c @@ -33,8 +33,10 @@ test_immortal_small_ints(PyObject *self, PyObject *Py_UNUSED(ignored)) } 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 & (1 << 2); assert(!has_int_immortal_bit); + Py_DECREF(obj); } Py_RETURN_NONE; } From ebc7e17afc983aa85b041a0fa12feda1fd183411 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sun, 22 Dec 2024 16:40:14 +0100 Subject: [PATCH 18/27] Update Modules/_testcapi/immortal.c Co-authored-by: Mark Shannon --- Modules/_testcapi/immortal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_testcapi/immortal.c b/Modules/_testcapi/immortal.c index 8571530404ea98..29315514214a59 100644 --- a/Modules/_testcapi/immortal.c +++ b/Modules/_testcapi/immortal.c @@ -28,7 +28,7 @@ test_immortal_small_ints(PyObject *self, PyObject *Py_UNUSED(ignored)) for (int i = -5; i <= 256; i++) { PyObject *obj = PyLong_FromLong(i); assert(verify_immortality(obj)); - int has_int_immortal_bit = ((PyLongObject *)obj)->long_value.lv_tag & (1 << 2); + 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++) { From aaf110b70d9916a582d3a220f16e697da5176aa6 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sun, 22 Dec 2024 16:40:22 +0100 Subject: [PATCH 19/27] Update Modules/_testcapi/immortal.c Co-authored-by: Mark Shannon --- Modules/_testcapi/immortal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_testcapi/immortal.c b/Modules/_testcapi/immortal.c index 29315514214a59..5599b38ca395eb 100644 --- a/Modules/_testcapi/immortal.c +++ b/Modules/_testcapi/immortal.c @@ -34,7 +34,7 @@ test_immortal_small_ints(PyObject *self, PyObject *Py_UNUSED(ignored)) 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 & (1 << 2); + int has_int_immortal_bit = ((PyLongObject *)obj)->long_value.lv_tag & IMMORTALITY_BIT_MASK; assert(!has_int_immortal_bit); Py_DECREF(obj); } From 8fcc1d01f609d3fed549813622e9eed0129d6e67 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sun, 22 Dec 2024 16:40:32 +0100 Subject: [PATCH 20/27] Update Include/cpython/longintrepr.h Co-authored-by: Mark Shannon --- Include/cpython/longintrepr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/cpython/longintrepr.h b/Include/cpython/longintrepr.h index 5e65fec63759df..1b1a1bf6b7cf75 100644 --- a/Include/cpython/longintrepr.h +++ b/Include/cpython/longintrepr.h @@ -76,7 +76,7 @@ typedef long stwodigits; /* signed variant of twodigits */ - 1: Zero - 2: Negative - The third lowest bit of lv_tag is reserved for an immortality flag, and is + The third lowest bit of lv_tag is set to 1 for the small ints. In a normalized number, ob_digit[ndigits-1] (the most significant From ebb0bca50a2960ff86b2e868f35fdb51ac4b58d0 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sun, 22 Dec 2024 16:45:45 +0100 Subject: [PATCH 21/27] review comments --- Objects/longobject.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index 11a77f826493c0..78e42617c69c5a 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -3627,13 +3627,11 @@ void _PyLong_ExactDealloc(PyObject *self) { assert(PyLong_CheckExact(self)); -#ifndef Py_GIL_DISABLED if (_long_is_small_int(self)) { // See PEP 683, section Accidental De-Immortalizing for details _Py_SetImmortal(self); return; } -#endif if (_PyLong_IsCompact((PyLongObject *)self)) { _Py_FREELIST_FREE(ints, self, PyObject_Free); return; @@ -3644,7 +3642,6 @@ _PyLong_ExactDealloc(PyObject *self) static void long_dealloc(PyObject *self) { -#ifndef Py_GIL_DISABLED 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, @@ -3655,7 +3652,6 @@ long_dealloc(PyObject *self) _Py_SetImmortal(self); return; } -#endif if (PyLong_CheckExact(self) && _PyLong_IsCompact((PyLongObject *)self)) { _Py_FREELIST_FREE(ints, self, PyObject_Free); return; From 8d8e7942f0524349a0a99361a1065c4d369d8dd6 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sun, 22 Dec 2024 20:20:37 +0100 Subject: [PATCH 22/27] include pycore_long.h --- Modules/_testcapi/immortal.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_testcapi/immortal.c b/Modules/_testcapi/immortal.c index 5599b38ca395eb..4c4621d8935d04 100644 --- a/Modules/_testcapi/immortal.c +++ b/Modules/_testcapi/immortal.c @@ -1,4 +1,5 @@ #include "parts.h" +#include "internal/pycore_long.h" // IMMORTALITY_BIT_MASK int verify_immortality(PyObject *object) { From 4a07ba1d0b0ff1f6eaae9d27572b970de5d54761 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sun, 22 Dec 2024 20:23:50 +0100 Subject: [PATCH 23/27] header --- Modules/_testcapi/immortal.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/_testcapi/immortal.c b/Modules/_testcapi/immortal.c index 4c4621d8935d04..cec2253647b55b 100644 --- a/Modules/_testcapi/immortal.c +++ b/Modules/_testcapi/immortal.c @@ -1,3 +1,7 @@ +#ifndef Py_BUILD_CORE_BUILTIN +# define Py_BUILD_CORE_MODULE 1 +#endif + #include "parts.h" #include "internal/pycore_long.h" // IMMORTALITY_BIT_MASK From 1d5b2e08845397d05bd0d393af22906d415e96fc Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sun, 22 Dec 2024 20:26:42 +0100 Subject: [PATCH 24/27] include pycore_long.h --- Modules/_testcapi/immortal.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Modules/_testcapi/immortal.c b/Modules/_testcapi/immortal.c index cec2253647b55b..2d366762717ff4 100644 --- a/Modules/_testcapi/immortal.c +++ b/Modules/_testcapi/immortal.c @@ -1,8 +1,6 @@ -#ifndef Py_BUILD_CORE_BUILTIN -# define Py_BUILD_CORE_MODULE 1 -#endif - #include "parts.h" + +#define Py_BUILD_CORE #include "internal/pycore_long.h" // IMMORTALITY_BIT_MASK int verify_immortality(PyObject *object) From cfb91201527e6c344d0f558e586a35e35239fd00 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sun, 22 Dec 2024 22:20:37 +0100 Subject: [PATCH 25/27] debug build failure --- Objects/longobject.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Objects/longobject.c b/Objects/longobject.c index 78e42617c69c5a..c9b6d3c2cfce0c 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -3649,7 +3649,9 @@ long_dealloc(PyObject *self) * * See PEP 683, section Accidental De-Immortalizing for details */ +#ifndef Py_GIL_DISABLED _Py_SetImmortal(self); +#endif return; } if (PyLong_CheckExact(self) && _PyLong_IsCompact((PyLongObject *)self)) { From b4c8444746e097a22a2b34e1a6c5bd0d20b7b1d1 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 23 Dec 2024 21:46:54 +0100 Subject: [PATCH 26/27] fix bug when copying the immortal bit --- Objects/longobject.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index c9b6d3c2cfce0c..f611e1c453ca10 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -3649,9 +3649,8 @@ long_dealloc(PyObject *self) * * See PEP 683, section Accidental De-Immortalizing for details */ -#ifndef Py_GIL_DISABLED + assert(0); _Py_SetImmortal(self); -#endif return; } if (PyLong_CheckExact(self) && _PyLong_IsCompact((PyLongObject *)self)) { @@ -6019,7 +6018,7 @@ long_subtype_new(PyTypeObject *type, PyObject *x, PyObject *obase) 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]; } From f76c0e2a58ad5cb8c9b6ce476f8d4cbcd16868f1 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 23 Dec 2024 22:37:33 +0100 Subject: [PATCH 27/27] add assert to make sure the immortal bit is not set on subclasses --- Objects/longobject.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index f611e1c453ca10..13cdf64c2a2f06 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -3620,7 +3620,9 @@ static inline int _long_is_small_int(PyObject *op) { PyLongObject *long_object = (PyLongObject *)op; - return (long_object->long_value.lv_tag & IMMORTALITY_BIT_MASK) != 0; + 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 @@ -3649,7 +3651,6 @@ long_dealloc(PyObject *self) * * See PEP 683, section Accidental De-Immortalizing for details */ - assert(0); _Py_SetImmortal(self); return; }