From 53cfe5409c90ae3a5af6134505fc92c6f40e9171 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 14 Jun 2023 02:51:19 +0200 Subject: [PATCH] gh-105059: C99 avoids PyObject.ob_refcnt union Only define PyObject.ob_refcnt as an anonymous union on C11 and newer, or when GCC is used. If GCC is used on C99 and older, add __extension__ on the union. On C99 and older without GCC, Py_INCREF() and Py_DECREF() are implemented as function calls. --- Include/object.h | 57 ++++++++++++++++++++++++++++++++++++++++-------- Objects/object.c | 38 ++++++++++++++++++++++++++++---- 2 files changed, 82 insertions(+), 13 deletions(-) diff --git a/Include/object.h b/Include/object.h index 3ef64511399c66..67d2474d22168a 100644 --- a/Include/object.h +++ b/Include/object.h @@ -62,6 +62,13 @@ whose size is determined when the object is allocated. # error Py_LIMITED_API is incompatible with Py_TRACE_REFS #endif +#if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112L) || defined(__cplusplus) + // On C11 and C++, PyObject.ob_refcnt is implemented as an anonymous union +# define _PYOBJECT_REFCNT_ANON_UNION +//#elif defined(__GNUC__) +//# define _PYOBJECT_REFCNT_ANON_UNION +#endif + #ifdef Py_TRACE_REFS /* Define pointers to support a doubly-linked list of all live heap objects. */ #define _PyObject_HEAD_EXTRA \ @@ -134,13 +141,21 @@ check by comparing the reference count field to the immortality reference count. { _Py_IMMORTAL_REFCNT }, \ (type) \ }, -#else +#elif defined(_PYOBJECT_REFCNT_ANON_UNION) #define PyObject_HEAD_INIT(type) \ { \ _PyObject_EXTRA_INIT \ { 1 }, \ (type) \ }, +#else + // gh-105059: On C99 and older, PyObject.ob_refcnt type is Py_ssize_t +#define PyObject_HEAD_INIT(type) \ + { \ + _PyObject_EXTRA_INIT \ + 1, \ + (type) \ + }, #endif /* Py_BUILD_CORE */ #define PyVarObject_HEAD_INIT(type, size) \ @@ -158,6 +173,7 @@ check by comparing the reference count field to the immortality reference count. #define PyObject_VAR_HEAD PyVarObject ob_base; #define Py_INVALID_SIZE (Py_ssize_t)-1 + /* Nothing is actually declared to be a PyObject, but every pointer to * a Python object can be cast to a PyObject*. This is inheritance built * by hand. Similarly every pointer to a variable-size Python object can, @@ -165,12 +181,23 @@ check by comparing the reference count field to the immortality reference count. */ struct _object { _PyObject_HEAD_EXTRA +#ifdef _PYOBJECT_REFCNT_ANON_UNION + +// #if defined(__GNUC__) && !(defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112L) +// // On C99 and older, anonymous union is a GCC extension +// __extension__ +// #endif union { Py_ssize_t ob_refcnt; #if SIZEOF_VOID_P > 4 PY_UINT32_T ob_refcnt_split[2]; #endif }; +#else + // gh-105059: C99 and older cannot use anonymous union: implement + // Py_INCREF() and Py_DECREF() as opaque function calls. + Py_ssize_t ob_refcnt; +#endif PyTypeObject *ob_type; }; @@ -611,15 +638,19 @@ PyAPI_FUNC(void) _Py_DecRef(PyObject *); static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op) { #if defined(Py_LIMITED_API) && (Py_LIMITED_API+0 >= 0x030c0000 || defined(Py_REF_DEBUG)) - // Stable ABI implements Py_INCREF() as a function call on limited C API - // version 3.12 and newer, and on Python built in debug mode. _Py_IncRef() - // was added to Python 3.10.0a7, use Py_IncRef() on older Python versions. - // Py_IncRef() accepts NULL whereas _Py_IncRef() doesn't. + // In limited C API version 3.12 and newer and on Python built in debug + // mode, Py_INCREF() is implemented as a function call. _Py_IncRef() was + // added to Python 3.10.0a7, use Py_IncRef() on older Python versions. + // Py_IncRef() accepts NULL, whereas _Py_IncRef() doesn't. # if Py_LIMITED_API+0 >= 0x030a00A7 _Py_IncRef(op); # else Py_IncRef(op); # endif +#elif !defined(_PYOBJECT_REFCNT_ANON_UNION) + // gh-105059: On C99 and older, Py_INCREF() is implemented as an opaque + // function call. + _Py_IncRef(op); #else // Non-limited C API and limited C API for Python 3.9 and older access // directly PyObject.ob_refcnt. @@ -649,10 +680,10 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op) #endif #if defined(Py_LIMITED_API) && (Py_LIMITED_API+0 >= 0x030c0000 || defined(Py_REF_DEBUG)) -// Stable ABI implements Py_DECREF() as a function call on limited C API -// version 3.12 and newer, and on Python built in debug mode. _Py_DecRef() was -// added to Python 3.10.0a7, use Py_DecRef() on older Python versions. -// Py_DecRef() accepts NULL whereas _Py_IncRef() doesn't. +// In limited C API version 3.12 and newer and on Python built in debug mode, +// Py_DECREF() is implemented as a function call. _Py_DecRef() was added to +// Python 3.10.0a7, use Py_DecRef() on older Python versions. Py_DecRef() +// accepts NULL, whereas _Py_DecRef() doesn't. static inline void Py_DECREF(PyObject *op) { # if Py_LIMITED_API+0 >= 0x030a00A7 _Py_DecRef(op); @@ -662,6 +693,14 @@ static inline void Py_DECREF(PyObject *op) { } #define Py_DECREF(op) Py_DECREF(_PyObject_CAST(op)) +#elif !defined(_PYOBJECT_REFCNT_ANON_UNION) +// gh-105059: On C99 and older, Py_INCREF() is implemented as an opaque +// function call. +static inline void Py_DECREF(PyObject *op) { + _Py_DecRef(op); +} +#define Py_DECREF(op) Py_DECREF(_PyObject_CAST(op)) + #elif defined(Py_REF_DEBUG) static inline void Py_DECREF(const char *filename, int lineno, PyObject *op) { diff --git a/Objects/object.c b/Objects/object.c index b20e87ef3fb23d..505479338d3fd5 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -271,15 +271,45 @@ Py_DecRef(PyObject *o) } void -_Py_IncRef(PyObject *o) +_Py_IncRef(PyObject *op) { - Py_INCREF(o); +#ifdef _PYOBJECT_REFCNT_ANON_UNION + Py_INCREF(op); +#else + // Explicitly check immortality against the immortal value + if (_Py_IsImmortal(op)) { + return; + } + op->ob_refcnt++; +#endif + _Py_INCREF_STAT_INC(); +#ifdef Py_REF_DEBUG + _Py_IncRefTotal_DO_NOT_USE_THIS(); +#endif } void -_Py_DecRef(PyObject *o) +_Py_DecRef(PyObject *op) { - Py_DECREF(o); +#ifdef _PYOBJECT_REFCNT_ANON_UNION + Py_DECREF(op); +#else + if (_Py_IsImmortal(op)) { + return; + } + _Py_DECREF_STAT_INC(); +#ifdef Py_REF_DEBUG + _Py_DecRefTotal_DO_NOT_USE_THIS(); +#endif + if (--op->ob_refcnt != 0) { + if (op->ob_refcnt < 0) { + _Py_NegativeRefcount(__FILE__, __LINE__, op); + } + } + else { + _Py_Dealloc(op); + } +#endif }