Skip to content

Commit a882ae1

Browse files
authored
gh-142472: Clean-up _PyStackRef functions (gh-142479)
This combines most _PyStackRef functions and macros between the free threaded and default builds. - Remove Py_TAG_DEFERRED (same as Py_TAG_REFCNT) - Remove PyStackRef_IsDeferred (same as !PyStackRef_RefcountOnObject)
1 parent abaaeee commit a882ae1

File tree

5 files changed

+57
-200
lines changed

5 files changed

+57
-200
lines changed

Include/internal/pycore_object.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,9 @@ static inline void Py_DECREF_MORTAL_SPECIALIZED(PyObject *op, destructor destruc
496496
#define Py_DECREF_MORTAL_SPECIALIZED(op, destruct) Py_DECREF_MORTAL_SPECIALIZED(_PyObject_CAST(op), destruct)
497497

498498
#endif
499+
#else // Py_GIL_DISABLED
500+
# define Py_DECREF_MORTAL(op) Py_DECREF(op)
501+
# define Py_DECREF_MORTAL_SPECIALIZED(op, destruct) Py_DECREF(op)
499502
#endif
500503

501504
/* Inline functions trading binary compatibility for speed:
@@ -1045,6 +1048,8 @@ static inline Py_ALWAYS_INLINE void _Py_INCREF_MORTAL(PyObject *op)
10451048
}
10461049
#endif
10471050
}
1051+
#else
1052+
# define _Py_INCREF_MORTAL(op) Py_INCREF(op)
10481053
#endif
10491054

10501055
/* Utility for the tp_traverse slot of mutable heap types that have no other

Include/internal/pycore_stackref.h

Lines changed: 48 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -452,184 +452,6 @@ PyStackRef_IncrementTaggedIntNoOverflow(_PyStackRef ref)
452452
return (_PyStackRef){ .bits = ref.bits + (1 << Py_TAGGED_SHIFT) };
453453
}
454454

455-
#define PyStackRef_IsDeferredOrTaggedInt(ref) (((ref).bits & Py_TAG_REFCNT) != 0)
456-
457-
#ifdef Py_GIL_DISABLED
458-
459-
#define Py_TAG_DEFERRED Py_TAG_REFCNT
460-
461-
#define Py_TAG_PTR ((uintptr_t)0)
462-
463-
464-
static const _PyStackRef PyStackRef_NULL = { .bits = Py_TAG_DEFERRED};
465-
466-
#define PyStackRef_IsNull(stackref) ((stackref).bits == PyStackRef_NULL.bits)
467-
#define PyStackRef_True ((_PyStackRef){.bits = ((uintptr_t)&_Py_TrueStruct) | Py_TAG_DEFERRED })
468-
#define PyStackRef_False ((_PyStackRef){.bits = ((uintptr_t)&_Py_FalseStruct) | Py_TAG_DEFERRED })
469-
#define PyStackRef_None ((_PyStackRef){.bits = ((uintptr_t)&_Py_NoneStruct) | Py_TAG_DEFERRED })
470-
471-
// Checks that mask out the deferred bit in the free threading build.
472-
#define PyStackRef_IsNone(ref) (PyStackRef_AsPyObjectBorrow(ref) == Py_None)
473-
#define PyStackRef_IsTrue(ref) (PyStackRef_AsPyObjectBorrow(ref) == Py_True)
474-
#define PyStackRef_IsFalse(ref) (PyStackRef_AsPyObjectBorrow(ref) == Py_False)
475-
476-
#define PyStackRef_IsNullOrInt(stackref) (PyStackRef_IsNull(stackref) || PyStackRef_IsTaggedInt(stackref))
477-
478-
static inline PyObject *
479-
PyStackRef_AsPyObjectBorrow(_PyStackRef stackref)
480-
{
481-
assert(!PyStackRef_IsTaggedInt(stackref));
482-
PyObject *cleared = ((PyObject *)((stackref).bits & (~Py_TAG_BITS)));
483-
return cleared;
484-
}
485-
486-
#define PyStackRef_IsDeferred(ref) (((ref).bits & Py_TAG_BITS) == Py_TAG_DEFERRED)
487-
488-
static inline PyObject *
489-
PyStackRef_AsPyObjectSteal(_PyStackRef stackref)
490-
{
491-
assert(!PyStackRef_IsNull(stackref));
492-
if (PyStackRef_IsDeferred(stackref)) {
493-
return Py_NewRef(PyStackRef_AsPyObjectBorrow(stackref));
494-
}
495-
return PyStackRef_AsPyObjectBorrow(stackref);
496-
}
497-
498-
static inline _PyStackRef
499-
_PyStackRef_FromPyObjectSteal(PyObject *obj)
500-
{
501-
assert(obj != NULL);
502-
// Make sure we don't take an already tagged value.
503-
assert(((uintptr_t)obj & Py_TAG_BITS) == 0);
504-
return (_PyStackRef){ .bits = (uintptr_t)obj };
505-
}
506-
# define PyStackRef_FromPyObjectSteal(obj) _PyStackRef_FromPyObjectSteal(_PyObject_CAST(obj))
507-
508-
static inline bool
509-
PyStackRef_IsHeapSafe(_PyStackRef stackref)
510-
{
511-
if (PyStackRef_IsDeferred(stackref)) {
512-
PyObject *obj = PyStackRef_AsPyObjectBorrow(stackref);
513-
return obj == NULL || _Py_IsImmortal(obj) || _PyObject_HasDeferredRefcount(obj);
514-
}
515-
return true;
516-
}
517-
518-
static inline _PyStackRef
519-
PyStackRef_MakeHeapSafe(_PyStackRef stackref)
520-
{
521-
if (PyStackRef_IsHeapSafe(stackref)) {
522-
return stackref;
523-
}
524-
PyObject *obj = PyStackRef_AsPyObjectBorrow(stackref);
525-
return (_PyStackRef){ .bits = (uintptr_t)(Py_NewRef(obj)) | Py_TAG_PTR };
526-
}
527-
528-
static inline _PyStackRef
529-
PyStackRef_FromPyObjectStealMortal(PyObject *obj)
530-
{
531-
assert(obj != NULL);
532-
assert(!_Py_IsImmortal(obj));
533-
// Make sure we don't take an already tagged value.
534-
assert(((uintptr_t)obj & Py_TAG_BITS) == 0);
535-
return (_PyStackRef){ .bits = (uintptr_t)obj };
536-
}
537-
538-
static inline _PyStackRef
539-
PyStackRef_FromPyObjectNew(PyObject *obj)
540-
{
541-
// Make sure we don't take an already tagged value.
542-
assert(((uintptr_t)obj & Py_TAG_BITS) == 0);
543-
assert(obj != NULL);
544-
if (_PyObject_HasDeferredRefcount(obj)) {
545-
return (_PyStackRef){ .bits = (uintptr_t)obj | Py_TAG_DEFERRED };
546-
}
547-
else {
548-
return (_PyStackRef){ .bits = (uintptr_t)(Py_NewRef(obj)) | Py_TAG_PTR };
549-
}
550-
}
551-
#define PyStackRef_FromPyObjectNew(obj) PyStackRef_FromPyObjectNew(_PyObject_CAST(obj))
552-
553-
static inline _PyStackRef
554-
PyStackRef_FromPyObjectBorrow(PyObject *obj)
555-
{
556-
// Make sure we don't take an already tagged value.
557-
assert(((uintptr_t)obj & Py_TAG_BITS) == 0);
558-
assert(obj != NULL);
559-
return (_PyStackRef){ .bits = (uintptr_t)obj | Py_TAG_DEFERRED };
560-
}
561-
#define PyStackRef_FromPyObjectBorrow(obj) PyStackRef_FromPyObjectBorrow(_PyObject_CAST(obj))
562-
563-
#define PyStackRef_CLOSE(REF) \
564-
do { \
565-
_PyStackRef _close_tmp = (REF); \
566-
assert(!PyStackRef_IsNull(_close_tmp)); \
567-
if (!PyStackRef_IsDeferredOrTaggedInt(_close_tmp)) { \
568-
Py_DECREF(PyStackRef_AsPyObjectBorrow(_close_tmp)); \
569-
} \
570-
} while (0)
571-
572-
static inline void
573-
PyStackRef_CLOSE_SPECIALIZED(_PyStackRef ref, destructor destruct)
574-
{
575-
(void)destruct;
576-
PyStackRef_CLOSE(ref);
577-
}
578-
579-
static inline int
580-
PyStackRef_RefcountOnObject(_PyStackRef ref)
581-
{
582-
return (ref.bits & Py_TAG_REFCNT) == 0;
583-
}
584-
585-
static inline _PyStackRef
586-
PyStackRef_DUP(_PyStackRef stackref)
587-
{
588-
assert(!PyStackRef_IsNull(stackref));
589-
if (PyStackRef_IsDeferredOrTaggedInt(stackref)) {
590-
return stackref;
591-
}
592-
Py_INCREF(PyStackRef_AsPyObjectBorrow(stackref));
593-
return stackref;
594-
}
595-
596-
static inline _PyStackRef
597-
PyStackRef_Borrow(_PyStackRef stackref)
598-
{
599-
return (_PyStackRef){ .bits = stackref.bits | Py_TAG_DEFERRED };
600-
}
601-
602-
// Convert a possibly deferred reference to a strong reference.
603-
static inline _PyStackRef
604-
PyStackRef_AsStrongReference(_PyStackRef stackref)
605-
{
606-
return PyStackRef_FromPyObjectSteal(PyStackRef_AsPyObjectSteal(stackref));
607-
}
608-
609-
#define PyStackRef_XCLOSE(stackref) \
610-
do { \
611-
_PyStackRef _tmp = (stackref); \
612-
if (!PyStackRef_IsNull(_tmp)) { \
613-
PyStackRef_CLOSE(_tmp); \
614-
} \
615-
} while (0);
616-
617-
#define PyStackRef_CLEAR(op) \
618-
do { \
619-
_PyStackRef *_tmp_op_ptr = &(op); \
620-
_PyStackRef _tmp_old_op = (*_tmp_op_ptr); \
621-
if (!PyStackRef_IsNull(_tmp_old_op)) { \
622-
*_tmp_op_ptr = PyStackRef_NULL; \
623-
PyStackRef_CLOSE(_tmp_old_op); \
624-
} \
625-
} while (0)
626-
627-
#define PyStackRef_FromPyObjectNewMortal PyStackRef_FromPyObjectNew
628-
629-
#else // Py_GIL_DISABLED
630-
631-
// With GIL
632-
633455
/* References to immortal objects always have their tag bit set to Py_TAG_REFCNT
634456
* as they can (must) have their reclamation deferred */
635457

@@ -648,13 +470,24 @@ static const _PyStackRef PyStackRef_NULL = { .bits = PyStackRef_NULL_BITS };
648470
#define PyStackRef_False ((_PyStackRef){.bits = ((uintptr_t)&_Py_FalseStruct) | Py_TAG_REFCNT })
649471
#define PyStackRef_None ((_PyStackRef){.bits = ((uintptr_t)&_Py_NoneStruct) | Py_TAG_REFCNT })
650472

473+
#ifdef Py_GIL_DISABLED
474+
// Checks that mask out the deferred bit in the free threading build.
475+
#define PyStackRef_IsNone(REF) (((REF).bits & ~Py_TAG_REFCNT) == (uintptr_t)&_Py_NoneStruct)
476+
#define PyStackRef_IsTrue(REF) (((REF).bits & ~Py_TAG_REFCNT) == (uintptr_t)&_Py_TrueStruct)
477+
#define PyStackRef_IsFalse(REF) (((REF).bits & ~Py_TAG_REFCNT) == (uintptr_t)&_Py_FalseStruct)
478+
#else
651479
#define PyStackRef_IsTrue(REF) ((REF).bits == (((uintptr_t)&_Py_TrueStruct) | Py_TAG_REFCNT))
652480
#define PyStackRef_IsFalse(REF) ((REF).bits == (((uintptr_t)&_Py_FalseStruct) | Py_TAG_REFCNT))
653481
#define PyStackRef_IsNone(REF) ((REF).bits == (((uintptr_t)&_Py_NoneStruct) | Py_TAG_REFCNT))
482+
#endif
654483

655-
#ifdef Py_DEBUG
484+
#define PyStackRef_IsNullOrInt(stackref) (PyStackRef_IsNull(stackref) || PyStackRef_IsTaggedInt(stackref))
485+
486+
#if defined(Py_DEBUG) && !defined(Py_GIL_DISABLED)
656487

657-
static inline void PyStackRef_CheckValid(_PyStackRef ref) {
488+
static inline void
489+
PyStackRef_CheckValid(_PyStackRef ref)
490+
{
658491
assert(ref.bits != 0);
659492
int tag = ref.bits & Py_TAG_BITS;
660493
PyObject *obj = BITS_TO_PTR_MASKED(ref);
@@ -705,6 +538,8 @@ PyStackRef_Borrow(_PyStackRef ref)
705538
static inline PyObject *
706539
PyStackRef_AsPyObjectSteal(_PyStackRef ref)
707540
{
541+
assert(!PyStackRef_IsNull(ref));
542+
assert(!PyStackRef_IsTaggedInt(ref));
708543
if (PyStackRef_RefcountOnObject(ref)) {
709544
return BITS_TO_PTR(ref);
710545
}
@@ -717,22 +552,26 @@ static inline _PyStackRef
717552
PyStackRef_FromPyObjectSteal(PyObject *obj)
718553
{
719554
assert(obj != NULL);
720-
#if SIZEOF_VOID_P > 4
721-
unsigned int tag = obj->ob_flags & Py_TAG_REFCNT;
555+
#ifdef Py_GIL_DISABLED
556+
return (_PyStackRef){ .bits = (uintptr_t)obj };
722557
#else
558+
# if SIZEOF_VOID_P > 4
559+
unsigned int tag = obj->ob_flags & Py_TAG_REFCNT;
560+
# else
723561
unsigned int tag = _Py_IsImmortal(obj) ? Py_TAG_REFCNT : 0;
724-
#endif
562+
# endif
725563
_PyStackRef ref = ((_PyStackRef){.bits = ((uintptr_t)(obj)) | tag});
726564
PyStackRef_CheckValid(ref);
727565
return ref;
566+
#endif
728567
}
729568

730569
static inline _PyStackRef
731570
PyStackRef_FromPyObjectStealMortal(PyObject *obj)
732571
{
733572
assert(obj != NULL);
734573
assert(!_Py_IsImmortal(obj));
735-
_PyStackRef ref = ((_PyStackRef){.bits = ((uintptr_t)(obj)) });
574+
_PyStackRef ref = (_PyStackRef){ .bits = (uintptr_t)obj };
736575
PyStackRef_CheckValid(ref);
737576
return ref;
738577
}
@@ -741,9 +580,15 @@ static inline _PyStackRef
741580
_PyStackRef_FromPyObjectNew(PyObject *obj)
742581
{
743582
assert(obj != NULL);
583+
#ifdef Py_GIL_DISABLED
584+
if (_PyObject_HasDeferredRefcount(obj)) {
585+
return (_PyStackRef){ .bits = (uintptr_t)obj | Py_TAG_REFCNT };
586+
}
587+
#else
744588
if (_Py_IsImmortal(obj)) {
745-
return (_PyStackRef){ .bits = ((uintptr_t)obj) | Py_TAG_REFCNT};
589+
return (_PyStackRef){ .bits = (uintptr_t)obj | Py_TAG_REFCNT };
746590
}
591+
#endif
747592
_Py_INCREF_MORTAL(obj);
748593
_PyStackRef ref = (_PyStackRef){ .bits = (uintptr_t)obj };
749594
PyStackRef_CheckValid(ref);
@@ -766,6 +611,7 @@ _PyStackRef_FromPyObjectNewMortal(PyObject *obj)
766611
static inline _PyStackRef
767612
PyStackRef_FromPyObjectBorrow(PyObject *obj)
768613
{
614+
assert(obj != NULL);
769615
return (_PyStackRef){ .bits = (uintptr_t)obj | Py_TAG_REFCNT};
770616
}
771617

@@ -788,7 +634,15 @@ PyStackRef_DUP(_PyStackRef ref)
788634
static inline bool
789635
PyStackRef_IsHeapSafe(_PyStackRef ref)
790636
{
791-
return (ref.bits & Py_TAG_BITS) != Py_TAG_REFCNT || ref.bits == PyStackRef_NULL_BITS || _Py_IsImmortal(BITS_TO_PTR_MASKED(ref));
637+
#ifdef Py_GIL_DISABLED
638+
if ((ref.bits & Py_TAG_BITS) != Py_TAG_REFCNT) {
639+
return true;
640+
}
641+
PyObject *obj = BITS_TO_PTR_MASKED(ref);
642+
return obj == NULL || _PyObject_HasDeferredRefcount(obj);
643+
#else
644+
return (ref.bits & Py_TAG_BITS) != Py_TAG_REFCNT || ref.bits == PyStackRef_NULL_BITS || _Py_IsImmortal(BITS_TO_PTR_MASKED(ref));
645+
#endif
792646
}
793647

794648
static inline _PyStackRef
@@ -804,6 +658,13 @@ PyStackRef_MakeHeapSafe(_PyStackRef ref)
804658
return ref;
805659
}
806660

661+
// Convert a possibly deferred reference to a strong reference.
662+
static inline _PyStackRef
663+
PyStackRef_AsStrongReference(_PyStackRef stackref)
664+
{
665+
return PyStackRef_FromPyObjectSteal(PyStackRef_AsPyObjectSteal(stackref));
666+
}
667+
807668
#ifdef _WIN32
808669
#define PyStackRef_CLOSE(REF) \
809670
do { \
@@ -821,12 +682,6 @@ PyStackRef_CLOSE(_PyStackRef ref)
821682
}
822683
#endif
823684

824-
static inline bool
825-
PyStackRef_IsNullOrInt(_PyStackRef ref)
826-
{
827-
return PyStackRef_IsNull(ref) || PyStackRef_IsTaggedInt(ref);
828-
}
829-
830685
static inline void
831686
PyStackRef_CLOSE_SPECIALIZED(_PyStackRef ref, destructor destruct)
832687
{
@@ -859,8 +714,6 @@ PyStackRef_XCLOSE(_PyStackRef ref)
859714
} while (0)
860715

861716

862-
#endif // Py_GIL_DISABLED
863-
864717
// Note: this is a macro because MSVC (Windows) has trouble inlining it.
865718

866719
#define PyStackRef_Is(a, b) (((a).bits & (~Py_TAG_REFCNT)) == ((b).bits & (~Py_TAG_REFCNT)))
@@ -945,7 +798,7 @@ static inline int
945798
_Py_TryIncrefCompareStackRef(PyObject **src, PyObject *op, _PyStackRef *out)
946799
{
947800
if (_PyObject_HasDeferredRefcount(op)) {
948-
*out = (_PyStackRef){ .bits = (uintptr_t)op | Py_TAG_DEFERRED };
801+
*out = (_PyStackRef){ .bits = (uintptr_t)op | Py_TAG_REFCNT };
949802
return 1;
950803
}
951804
if (_Py_TryIncrefCompare(src, op)) {

InternalDocs/stackrefs.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ these values. Type checks use `PyStackRef_IsTaggedInt` and `PyStackRef_LongCheck
6464

6565
## Free threading considerations
6666

67-
With `Py_GIL_DISABLED`, `Py_TAG_DEFERRED` is an alias for `Py_TAG_REFCNT`.
6867
Objects that support deferred reference counting can be pushed to the evaluation
6968
stack and stored in local variables without directly incrementing the reference
7069
count because they are only freed during cyclic garbage collection. This avoids

Objects/dictobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1599,7 +1599,7 @@ lookup_threadsafe_unicode(PyDictKeysObject *dk, PyObject *key, Py_hash_t hash, _
15991599
return DKIX_EMPTY;
16001600
}
16011601
if (_PyObject_HasDeferredRefcount(value)) {
1602-
*value_addr = (_PyStackRef){ .bits = (uintptr_t)value | Py_TAG_DEFERRED };
1602+
*value_addr = (_PyStackRef){ .bits = (uintptr_t)value | Py_TAG_REFCNT };
16031603
return ix;
16041604
}
16051605
if (_Py_TryIncrefCompare(addr_of_value, value)) {

0 commit comments

Comments
 (0)