Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DRAFT] gh-105059: C99 avoids PyObject.ob_refcnt union #105767

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 48 additions & 9 deletions Include/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down Expand Up @@ -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) \
Expand All @@ -158,19 +173,31 @@ 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,
* in addition, be cast to PyVarObject*.
*/
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;
};

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand All @@ -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)
{
Expand Down
38 changes: 34 additions & 4 deletions Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
}


Expand Down