[Python-checkins] GH-84436: Skip refcounting for known immortals (GH-107605)

brandtbucher webhook-mailer at python.org
Fri Aug 4 19:24:54 EDT 2023


https://github.com/python/cpython/commit/05a824f294f1409f33e32f1799d5b413dcf24445
commit: 05a824f294f1409f33e32f1799d5b413dcf24445
branch: main
author: Brandt Bucher <brandtbucher at microsoft.com>
committer: brandtbucher <brandtbucher at gmail.com>
date: 2023-08-04T16:24:50-07:00
summary:

GH-84436: Skip refcounting for known immortals (GH-107605)

files:
A Misc/NEWS.d/next/Core and Builtins/2023-08-03-13-38-14.gh-issue-84436.gl1wHx.rst
M Include/internal/pycore_long.h
M Modules/_asynciomodule.c
M Modules/_io/textio.c
M Modules/_json.c
M Modules/_pickle.c
M Objects/boolobject.c
M Objects/bytesobject.c
M Objects/funcobject.c
M Objects/longobject.c
M Objects/rangeobject.c
M Objects/sliceobject.c
M Objects/tupleobject.c
M Objects/typeobject.c
M Objects/unicodeobject.c
M Python/ceval.c
M Python/context.c
M Python/fileutils.c
M Python/hamt.c

diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h
index 3f8d8adc83b20..3dc00ec7e04c6 100644
--- a/Include/internal/pycore_long.h
+++ b/Include/internal/pycore_long.h
@@ -64,19 +64,19 @@ extern void _PyLong_FiniTypes(PyInterpreterState *interp);
 #  error "_PY_NSMALLPOSINTS must be greater than or equal to 257"
 #endif
 
-// Return a borrowed reference to the zero singleton.
+// Return a reference to the immortal zero singleton.
 // The function cannot return NULL.
 static inline PyObject* _PyLong_GetZero(void)
 { return (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS]; }
 
-// Return a borrowed reference to the one singleton.
+// Return a reference to the immortal one singleton.
 // The function cannot return NULL.
 static inline PyObject* _PyLong_GetOne(void)
 { return (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS+1]; }
 
 static inline PyObject* _PyLong_FromUnsignedChar(unsigned char i)
 {
-    return Py_NewRef((PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS+i]);
+    return (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS+i];
 }
 
 extern PyObject *_PyLong_Add(PyLongObject *left, PyLongObject *right);
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-08-03-13-38-14.gh-issue-84436.gl1wHx.rst b/Misc/NEWS.d/next/Core and Builtins/2023-08-03-13-38-14.gh-issue-84436.gl1wHx.rst
new file mode 100644
index 0000000000000..71044c32feebc
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-08-03-13-38-14.gh-issue-84436.gl1wHx.rst	
@@ -0,0 +1 @@
+Skip reference count modifications for many known immortal objects.
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index f5a589b00c48d..39c803355ba95 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -1398,7 +1398,8 @@ FutureObj_get_state(FutureObj *fut, void *Py_UNUSED(ignored))
     default:
         assert (0);
     }
-    return Py_XNewRef(ret);
+    assert(_Py_IsImmortal(ret));
+    return ret;
 }
 
 static PyObject *
diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c
index 24d846e663437..6ce90b2ed774c 100644
--- a/Modules/_io/textio.c
+++ b/Modules/_io/textio.c
@@ -234,7 +234,7 @@ _io_IncrementalNewlineDecoder___init___impl(nldecoder_object *self,
 {
 
     if (errors == NULL) {
-        errors = Py_NewRef(&_Py_ID(strict));
+        errors = &_Py_ID(strict);
     }
     else {
         errors = Py_NewRef(errors);
@@ -1138,7 +1138,7 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer,
 
     if (encoding == NULL && _PyRuntime.preconfig.utf8_mode) {
         _Py_DECLARE_STR(utf_8, "utf-8");
-        self->encoding = Py_NewRef(&_Py_STR(utf_8));
+        self->encoding = &_Py_STR(utf_8);
     }
     else if (encoding == NULL || (strcmp(encoding, "locale") == 0)) {
         self->encoding = _Py_GetLocaleEncodingObject();
@@ -2267,7 +2267,7 @@ _textiowrapper_readline(textio *self, Py_ssize_t limit)
         Py_CLEAR(chunks);
     }
     if (line == NULL) {
-        line = Py_NewRef(&_Py_STR(empty));
+        line = &_Py_STR(empty);
     }
 
     return line;
diff --git a/Modules/_json.c b/Modules/_json.c
index 4fcaa07d9cfd8..c7cfe50b52faf 100644
--- a/Modules/_json.c
+++ b/Modules/_json.c
@@ -1277,13 +1277,13 @@ _encoded_const(PyObject *obj)
 {
     /* Return the JSON string representation of None, True, False */
     if (obj == Py_None) {
-        return Py_NewRef(&_Py_ID(null));
+        return &_Py_ID(null);
     }
     else if (obj == Py_True) {
-        return Py_NewRef(&_Py_ID(true));
+        return &_Py_ID(true);
     }
     else if (obj == Py_False) {
-        return Py_NewRef(&_Py_ID(false));
+        return &_Py_ID(false);
     }
     else {
         PyErr_SetString(PyExc_ValueError, "not a const");
diff --git a/Modules/_pickle.c b/Modules/_pickle.c
index d4c0be7893523..c2b04cc513a66 100644
--- a/Modules/_pickle.c
+++ b/Modules/_pickle.c
@@ -2029,8 +2029,7 @@ whichmodule(PyObject *global, PyObject *dotted_path)
     }
 
     /* If no module is found, use __main__. */
-    module_name = &_Py_ID(__main__);
-    return Py_NewRef(module_name);
+    return &_Py_ID(__main__);
 }
 
 /* fast_save_enter() and fast_save_leave() are guards against recursive
diff --git a/Objects/boolobject.c b/Objects/boolobject.c
index bbb187cb7121e..e2e359437f0ed 100644
--- a/Objects/boolobject.c
+++ b/Objects/boolobject.c
@@ -13,8 +13,7 @@
 static PyObject *
 bool_repr(PyObject *self)
 {
-    PyObject *res = self == Py_True ? &_Py_ID(True) : &_Py_ID(False);
-    return Py_NewRef(res);
+    return self == Py_True ? &_Py_ID(True) : &_Py_ID(False);
 }
 
 /* Function to return a bool from a C long */
diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c
index 6b9231a9fa769..afe9192720c62 100644
--- a/Objects/bytesobject.c
+++ b/Objects/bytesobject.c
@@ -41,17 +41,12 @@ Py_LOCAL_INLINE(Py_ssize_t) _PyBytesWriter_GetSize(_PyBytesWriter *writer,
 #define EMPTY (&_Py_SINGLETON(bytes_empty))
 
 
-// Return a borrowed reference to the empty bytes string singleton.
+// Return a reference to the immortal empty bytes string singleton.
 static inline PyObject* bytes_get_empty(void)
 {
-    return &EMPTY->ob_base.ob_base;
-}
-
-
-// Return a strong reference to the empty bytes string singleton.
-static inline PyObject* bytes_new_empty(void)
-{
-    return Py_NewRef(EMPTY);
+    PyObject *empty = &EMPTY->ob_base.ob_base;
+    assert(_Py_IsImmortal(empty));
+    return empty;
 }
 
 
@@ -84,7 +79,7 @@ _PyBytes_FromSize(Py_ssize_t size, int use_calloc)
     assert(size >= 0);
 
     if (size == 0) {
-        return bytes_new_empty();
+        return bytes_get_empty();
     }
 
     if ((size_t)size > (size_t)PY_SSIZE_T_MAX - PyBytesObject_SIZE) {
@@ -123,10 +118,11 @@ PyBytes_FromStringAndSize(const char *str, Py_ssize_t size)
     }
     if (size == 1 && str != NULL) {
         op = CHARACTER(*str & 255);
-        return Py_NewRef(op);
+        assert(_Py_IsImmortal(op));
+        return (PyObject *)op;
     }
     if (size == 0) {
-        return bytes_new_empty();
+        return bytes_get_empty();
     }
 
     op = (PyBytesObject *)_PyBytes_FromSize(size, 0);
@@ -154,11 +150,12 @@ PyBytes_FromString(const char *str)
     }
 
     if (size == 0) {
-        return bytes_new_empty();
+        return bytes_get_empty();
     }
     else if (size == 1) {
         op = CHARACTER(*str & 255);
-        return Py_NewRef(op);
+        assert(_Py_IsImmortal(op));
+        return (PyObject *)op;
     }
 
     /* Inline PyObject_NewVar */
@@ -3065,7 +3062,7 @@ _PyBytes_Resize(PyObject **pv, Py_ssize_t newsize)
         goto error;
     }
     if (newsize == 0) {
-        *pv = bytes_new_empty();
+        *pv = bytes_get_empty();
         Py_DECREF(v);
         return 0;
     }
diff --git a/Objects/funcobject.c b/Objects/funcobject.c
index 7fffa1c8bbff9..8c0bface3ac71 100644
--- a/Objects/funcobject.c
+++ b/Objects/funcobject.c
@@ -831,8 +831,8 @@ func_clear(PyFunctionObject *op)
     // However, name and qualname could be str subclasses, so they
     // could have reference cycles. The solution is to replace them
     // with a genuinely immutable string.
-    Py_SETREF(op->func_name, Py_NewRef(&_Py_STR(empty)));
-    Py_SETREF(op->func_qualname, Py_NewRef(&_Py_STR(empty)));
+    Py_SETREF(op->func_name, &_Py_STR(empty));
+    Py_SETREF(op->func_qualname, &_Py_STR(empty));
     return 0;
 }
 
diff --git a/Objects/longobject.c b/Objects/longobject.c
index 5d9b413861478..354cba9d6d800 100644
--- a/Objects/longobject.c
+++ b/Objects/longobject.c
@@ -174,7 +174,7 @@ _PyLong_FromDigits(int negative, Py_ssize_t digit_count, digit *digits)
 {
     assert(digit_count >= 0);
     if (digit_count == 0) {
-        return (PyLongObject *)Py_NewRef(_PyLong_GetZero());
+        return (PyLongObject *)_PyLong_GetZero();
     }
     PyLongObject *result = _PyLong_New(digit_count);
     if (result == NULL) {
@@ -2857,8 +2857,7 @@ long_divrem(PyLongObject *a, PyLongObject *b,
         if (*prem == NULL) {
             return -1;
         }
-        PyObject *zero = _PyLong_GetZero();
-        *pdiv = (PyLongObject*)Py_NewRef(zero);
+        *pdiv = (PyLongObject*)_PyLong_GetZero();
         return 0;
     }
     if (size_b == 1) {
diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c
index 1e3d5acc8ae6f..6e06bef95032c 100644
--- a/Objects/rangeobject.c
+++ b/Objects/rangeobject.c
@@ -106,8 +106,8 @@ range_from_array(PyTypeObject *type, PyObject *const *args, Py_ssize_t num_args)
             if (!stop) {
                 return NULL;
             }
-            start = Py_NewRef(_PyLong_GetZero());
-            step = Py_NewRef(_PyLong_GetOne());
+            start = _PyLong_GetZero();
+            step = _PyLong_GetOne();
             break;
         case 0:
             PyErr_SetString(PyExc_TypeError,
diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c
index dc3aad11ce10e..8cf654fb6f812 100644
--- a/Objects/sliceobject.c
+++ b/Objects/sliceobject.c
@@ -415,7 +415,7 @@ _PySlice_GetLongIndices(PySliceObject *self, PyObject *length,
 
     /* Convert step to an integer; raise for zero step. */
     if (self->step == Py_None) {
-        step = Py_NewRef(_PyLong_GetOne());
+        step = _PyLong_GetOne();
         step_is_negative = 0;
     }
     else {
@@ -443,7 +443,7 @@ _PySlice_GetLongIndices(PySliceObject *self, PyObject *length,
             goto error;
     }
     else {
-        lower = Py_NewRef(_PyLong_GetZero());
+        lower = _PyLong_GetZero();
         upper = Py_NewRef(length);
     }
 
diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c
index c3ff40fdb14c6..b669a3dd8525e 100644
--- a/Objects/tupleobject.c
+++ b/Objects/tupleobject.c
@@ -62,7 +62,7 @@ tuple_alloc(Py_ssize_t size)
 static inline PyObject *
 tuple_get_empty(void)
 {
-    return Py_NewRef(&_Py_SINGLETON(tuple_empty));
+    return (PyObject *)&_Py_SINGLETON(tuple_empty);
 }
 
 PyObject *
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index abe33f1562059..76809494dd881 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -1085,7 +1085,7 @@ type_module(PyTypeObject *type, void *context)
                 PyUnicode_InternInPlace(&mod);
         }
         else {
-            mod = Py_NewRef(&_Py_ID(builtins));
+            mod = &_Py_ID(builtins);
         }
     }
     return mod;
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
index cc979b2ef28d7..c6876d4ca0ef0 100644
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -211,21 +211,13 @@ static int unicode_is_singleton(PyObject *unicode);
 #endif
 
 
-// Return a borrowed reference to the empty string singleton.
+// Return a reference to the immortal empty string singleton.
 static inline PyObject* unicode_get_empty(void)
 {
     _Py_DECLARE_STR(empty, "");
     return &_Py_STR(empty);
 }
 
-
-// Return a strong reference to the empty string singleton.
-static inline PyObject* unicode_new_empty(void)
-{
-    PyObject *empty = unicode_get_empty();
-    return Py_NewRef(empty);
-}
-
 /* This dictionary holds all interned unicode strings.  Note that references
    to strings in this dictionary are *not* counted in the string's ob_refcnt.
    When the interned string reaches a refcnt of 0 the string deallocation
@@ -310,7 +302,7 @@ clear_interned_dict(PyInterpreterState *interp)
 
 #define _Py_RETURN_UNICODE_EMPTY()   \
     do {                             \
-        return unicode_new_empty();  \
+        return unicode_get_empty();  \
     } while (0)
 
 static inline void
@@ -650,7 +642,6 @@ unicode_result(PyObject *unicode)
         PyObject *empty = unicode_get_empty();
         if (unicode != empty) {
             Py_DECREF(unicode);
-            Py_INCREF(empty);
         }
         return empty;
     }
@@ -662,7 +653,6 @@ unicode_result(PyObject *unicode)
             Py_UCS1 ch = data[0];
             PyObject *latin1_char = LATIN1(ch);
             if (unicode != latin1_char) {
-                Py_INCREF(latin1_char);
                 Py_DECREF(unicode);
             }
             return latin1_char;
@@ -1199,7 +1189,7 @@ PyUnicode_New(Py_ssize_t size, Py_UCS4 maxchar)
 {
     /* Optimization for empty strings */
     if (size == 0) {
-        return unicode_new_empty();
+        return unicode_get_empty();
     }
 
     PyObject *obj;
@@ -1669,7 +1659,7 @@ unicode_resize(PyObject **p_unicode, Py_ssize_t length)
         return 0;
 
     if (length == 0) {
-        PyObject *empty = unicode_new_empty();
+        PyObject *empty = unicode_get_empty();
         Py_SETREF(*p_unicode, empty);
         return 0;
     }
@@ -1764,7 +1754,9 @@ unicode_write_cstr(PyObject *unicode, Py_ssize_t index,
 static PyObject*
 get_latin1_char(Py_UCS1 ch)
 {
-    return Py_NewRef(LATIN1(ch));
+    PyObject *o = LATIN1(ch);
+    assert(_Py_IsImmortal(o));
+    return o;
 }
 
 static PyObject*
@@ -1891,7 +1883,7 @@ PyUnicode_FromStringAndSize(const char *u, Py_ssize_t size)
             "NULL string with positive size with NULL passed to PyUnicode_FromStringAndSize");
         return NULL;
     }
-    return unicode_new_empty();
+    return unicode_get_empty();
 }
 
 PyObject *
@@ -10261,7 +10253,7 @@ replace(PyObject *self, PyObject *str1,
         }
         new_size = slen + n * (len2 - len1);
         if (new_size == 0) {
-            u = unicode_new_empty();
+            u = unicode_get_empty();
             goto done;
         }
         if (new_size > (PY_SSIZE_T_MAX / rkind)) {
@@ -14505,7 +14497,7 @@ unicode_new_impl(PyTypeObject *type, PyObject *x, const char *encoding,
 {
     PyObject *unicode;
     if (x == NULL) {
-        unicode = unicode_new_empty();
+        unicode = unicode_get_empty();
     }
     else if (encoding == NULL && errors == NULL) {
         unicode = PyObject_Str(x);
@@ -14994,8 +14986,7 @@ unicode_ascii_iter_next(unicodeiterobject *it)
         Py_UCS1 chr = (Py_UCS1)PyUnicode_READ(PyUnicode_1BYTE_KIND,
                                               data, it->it_index);
         it->it_index++;
-        PyObject *item = (PyObject*)&_Py_SINGLETON(strings).ascii[chr];
-        return Py_NewRef(item);
+        return (PyObject*)&_Py_SINGLETON(strings).ascii[chr];
     }
     it->it_seq = NULL;
     Py_DECREF(seq);
@@ -15025,7 +15016,7 @@ unicodeiter_reduce(unicodeiterobject *it, PyObject *Py_UNUSED(ignored))
     if (it->it_seq != NULL) {
         return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index);
     } else {
-        PyObject *u = unicode_new_empty();
+        PyObject *u = unicode_get_empty();
         if (u == NULL) {
             Py_XDECREF(iter);
             return NULL;
diff --git a/Python/ceval.c b/Python/ceval.c
index b85e9677747af..30f722e45e476 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -1265,7 +1265,7 @@ initialize_locals(PyThreadState *tstate, PyFunctionObject *func,
     if (co->co_flags & CO_VARARGS) {
         PyObject *u = NULL;
         if (argcount == n) {
-            u = Py_NewRef(&_Py_SINGLETON(tuple_empty));
+            u = (PyObject *)&_Py_SINGLETON(tuple_empty);
         }
         else {
             assert(args != NULL);
diff --git a/Python/context.c b/Python/context.c
index 9ac51874fb5be..c94c014219d0e 100644
--- a/Python/context.c
+++ b/Python/context.c
@@ -1267,7 +1267,7 @@ PyTypeObject _PyContextTokenMissing_Type = {
 static PyObject *
 get_token_missing(void)
 {
-    return Py_NewRef(&_Py_SINGLETON(context_token_missing));
+    return (PyObject *)&_Py_SINGLETON(context_token_missing);
 }
 
 
diff --git a/Python/fileutils.c b/Python/fileutils.c
index f262c3e095c9b..19b23f6bd18b3 100644
--- a/Python/fileutils.c
+++ b/Python/fileutils.c
@@ -105,7 +105,7 @@ _Py_device_encoding(int fd)
 #else
     if (_PyRuntime.preconfig.utf8_mode) {
         _Py_DECLARE_STR(utf_8, "utf-8");
-        return Py_NewRef(&_Py_STR(utf_8));
+        return &_Py_STR(utf_8);
     }
     return _Py_GetLocaleEncodingObject();
 #endif
diff --git a/Python/hamt.c b/Python/hamt.c
index c78b5a7fab94f..24265edc2c3fd 100644
--- a/Python/hamt.c
+++ b/Python/hamt.c
@@ -514,7 +514,7 @@ hamt_node_bitmap_new(Py_ssize_t size)
         /* Since bitmap nodes are immutable, we can cache the instance
            for size=0 and reuse it whenever we need an empty bitmap node.
         */
-        return (PyHamtNode *)Py_NewRef(&_Py_SINGLETON(hamt_bitmap_node_empty));
+        return (PyHamtNode *)&_Py_SINGLETON(hamt_bitmap_node_empty);
     }
 
     assert(size >= 0);



More information about the Python-checkins mailing list