GH-132554: "Virtual" iterators (GH-132555)

* FOR_ITER now pushes either the iterator and NULL or leaves the iterable and pushes tagged zero

* NEXT_ITER uses the tagged int as the index into the sequence or, if TOS is NULL, iterates as before.
This commit is contained in:
Mark Shannon 2025-05-27 15:59:45 +01:00 committed by GitHub
parent 9300a596d3
commit f6f4e8a662
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 713 additions and 618 deletions

View file

@ -232,6 +232,9 @@ extern intptr_t PyStackRef_UntagInt(_PyStackRef ref);
extern _PyStackRef PyStackRef_TagInt(intptr_t i);
/* Increments a tagged int, but does not check for overflow */
extern _PyStackRef PyStackRef_IncrementTaggedIntNoOverflow(_PyStackRef ref);
extern bool
PyStackRef_IsNullOrInt(_PyStackRef ref);
@ -239,11 +242,12 @@ PyStackRef_IsNullOrInt(_PyStackRef ref);
#define Py_INT_TAG 3
#define Py_TAG_REFCNT 1
#define Py_TAG_BITS 3
static inline bool
PyStackRef_IsTaggedInt(_PyStackRef i)
{
return (i.bits & Py_INT_TAG) == Py_INT_TAG;
return (i.bits & Py_TAG_BITS) == Py_INT_TAG;
}
static inline _PyStackRef
@ -262,12 +266,21 @@ PyStackRef_UntagInt(_PyStackRef i)
}
static inline _PyStackRef
PyStackRef_IncrementTaggedIntNoOverflow(_PyStackRef ref)
{
assert((ref.bits & Py_TAG_BITS) == Py_INT_TAG); // Is tagged int
assert((ref.bits & (~Py_TAG_BITS)) != (INT_MAX & (~Py_TAG_BITS))); // Isn't about to overflow
return (_PyStackRef){ .bits = ref.bits + 4 };
}
#define PyStackRef_IsDeferredOrTaggedInt(ref) (((ref).bits & Py_TAG_REFCNT) != 0)
#ifdef Py_GIL_DISABLED
#define Py_TAG_DEFERRED Py_TAG_REFCNT
#define Py_TAG_PTR ((uintptr_t)0)
#define Py_TAG_BITS ((uintptr_t)1)
static const _PyStackRef PyStackRef_NULL = { .bits = Py_TAG_DEFERRED};
@ -379,7 +392,7 @@ PyStackRef_FromPyObjectBorrow(PyObject *obj)
do { \
_PyStackRef _close_tmp = (REF); \
assert(!PyStackRef_IsNull(_close_tmp)); \
if (!PyStackRef_IsDeferred(_close_tmp)) { \
if (!PyStackRef_IsDeferredOrTaggedInt(_close_tmp)) { \
Py_DECREF(PyStackRef_AsPyObjectBorrow(_close_tmp)); \
} \
} while (0)
@ -395,7 +408,7 @@ static inline _PyStackRef
PyStackRef_DUP(_PyStackRef stackref)
{
assert(!PyStackRef_IsNull(stackref));
if (PyStackRef_IsDeferred(stackref)) {
if (PyStackRef_IsDeferredOrTaggedInt(stackref)) {
return stackref;
}
Py_INCREF(PyStackRef_AsPyObjectBorrow(stackref));
@ -442,7 +455,6 @@ PyStackRef_AsStrongReference(_PyStackRef stackref)
/* References to immortal objects always have their tag bit set to Py_TAG_REFCNT
* as they can (must) have their reclamation deferred */
#define Py_TAG_BITS 3
#if _Py_IMMORTAL_FLAGS != Py_TAG_REFCNT
# error "_Py_IMMORTAL_FLAGS != Py_TAG_REFCNT"
#endif
@ -678,7 +690,13 @@ PyStackRef_XCLOSE(_PyStackRef ref)
#endif // !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG)
#define PyStackRef_TYPE(stackref) Py_TYPE(PyStackRef_AsPyObjectBorrow(stackref))
static inline PyTypeObject *
PyStackRef_TYPE(_PyStackRef stackref) {
if (PyStackRef_IsTaggedInt(stackref)) {
return &PyLong_Type;
}
return Py_TYPE(PyStackRef_AsPyObjectBorrow(stackref));
}
// Converts a PyStackRef back to a PyObject *, converting the
// stackref to a new reference.
@ -686,42 +704,30 @@ PyStackRef_XCLOSE(_PyStackRef ref)
// StackRef type checks
static inline bool
PyStackRef_GenCheck(_PyStackRef stackref)
{
return PyGen_Check(PyStackRef_AsPyObjectBorrow(stackref));
}
#define STACKREF_CHECK_FUNC(T) \
static inline bool \
PyStackRef_ ## T ## Check(_PyStackRef stackref) { \
if (PyStackRef_IsTaggedInt(stackref)) { \
return false; \
} \
return Py ## T ## _Check(PyStackRef_AsPyObjectBorrow(stackref)); \
}
static inline bool
PyStackRef_BoolCheck(_PyStackRef stackref)
{
return PyBool_Check(PyStackRef_AsPyObjectBorrow(stackref));
}
STACKREF_CHECK_FUNC(Gen)
STACKREF_CHECK_FUNC(Bool)
STACKREF_CHECK_FUNC(ExceptionInstance)
STACKREF_CHECK_FUNC(Code)
STACKREF_CHECK_FUNC(Function)
static inline bool
PyStackRef_LongCheck(_PyStackRef stackref)
{
if (PyStackRef_IsTaggedInt(stackref)) {
return true;
}
return PyLong_Check(PyStackRef_AsPyObjectBorrow(stackref));
}
static inline bool
PyStackRef_ExceptionInstanceCheck(_PyStackRef stackref)
{
return PyExceptionInstance_Check(PyStackRef_AsPyObjectBorrow(stackref));
}
static inline bool
PyStackRef_CodeCheck(_PyStackRef stackref)
{
return PyCode_Check(PyStackRef_AsPyObjectBorrow(stackref));
}
static inline bool
PyStackRef_FunctionCheck(_PyStackRef stackref)
{
return PyFunction_Check(PyStackRef_AsPyObjectBorrow(stackref));
}
static inline void
_PyThreadState_PushCStackRef(PyThreadState *tstate, _PyCStackRef *ref)
{