|
| 1 | +# Stack references (`_PyStackRef`) |
| 2 | + |
| 3 | +Stack references are the interpreter's tagged representation of values on the evaluation stack. |
| 4 | +They carry metadata to track ownership and support optimizations such as tagged small ints. |
| 5 | + |
| 6 | +## Shape and tagging |
| 7 | + |
| 8 | +- A `_PyStackRef` is a tagged pointer-sized value (see `Include/internal/pycore_stackref.h`). |
| 9 | +- Tag bits distinguish three cases: |
| 10 | + - `Py_TAG_REFCNT` unset - reference count lives on the pointed-to object. |
| 11 | + - `Py_TAG_REFCNT` set - ownership is "borrowed" (no refcount to drop on close) or the object is immortal. |
| 12 | + - `Py_INT_TAG` set - tagged small integer stored directly in the stackref (no heap allocation). |
| 13 | +- Special constants: `PyStackRef_NULL`, `PyStackRef_ERROR`, and embedded `None`/`True`/`False`. |
| 14 | + |
| 15 | +In GIL builds, most objects carry their refcount; tagged borrowed refs skip decref on close. In free |
| 16 | +threading builds, the tag is also used to mark deferred refcounted objects so the GC can see them and |
| 17 | +to avoid refcount contention on commonly shared objects. |
| 18 | + |
| 19 | +## Converting to and from PyObject* |
| 20 | + |
| 21 | +Three conversions control ownership: |
| 22 | + |
| 23 | +- `PyStackRef_FromPyObjectNew(obj)` - create a new reference (INCREF if mortal). |
| 24 | +- `PyStackRef_FromPyObjectSteal(obj)` - take over ownership without changing the count unless the |
| 25 | + object is immortal. |
| 26 | +- `PyStackRef_FromPyObjectBorrow(obj)` - create a borrowed stackref (never decref on close). |
| 27 | + |
| 28 | +The `obj` argument must not be `NULL`. |
| 29 | + |
| 30 | +Going back to `PyObject*` mirrors this: |
| 31 | + |
| 32 | +- `PyStackRef_AsPyObjectBorrow(ref)` - borrow the underlying pointer |
| 33 | +- `PyStackRef_AsPyObjectSteal(ref)` - transfer ownership from the stackref; if ref is borrowed or |
| 34 | + deferred, this creates a new owning `PyObject*` reference. |
| 35 | +- `PyStackRef_AsPyObjectNew(ref)` - create a new owning reference |
| 36 | + |
| 37 | +Only `PyStackRef_AsPyObjectBorrow` allows ref to be `PyStackRef_NULL`. |
| 38 | + |
| 39 | +## Operations on stackrefs |
| 40 | + |
| 41 | +The interpreter treats `_PyStackRef` as the unit of stack storage. Ownership must be managed with |
| 42 | +the stackref primitives: |
| 43 | + |
| 44 | +- `PyStackRef_DUP` - like `Py_NewRef` for stackrefs; preserves the original. |
| 45 | +- `PyStackRef_Borrow` - create a borrowed stackref from another stackref. |
| 46 | +- `PyStackRef_CLOSE` / `PyStackRef_XCLOSE` - like `Py_DECREF`; invalidates the stackref. |
| 47 | +- `PyStackRef_CLEAR` - like `Py_CLEAR`; closes and sets the stackref to `PyStackRef_NULL` |
| 48 | +- `PyStackRef_MakeHeapSafe` - converts borrowed reference to owning reference |
| 49 | + |
| 50 | +Borrow tracking (for debug builds with `Py_STACKREF_DEBUG`) records who you borrowed from and reports |
| 51 | +double-close, leaked borrows, or use-after-close via fatal errors. |
| 52 | + |
| 53 | +## Borrow-friendly opcodes |
| 54 | + |
| 55 | +The interpreter can push borrowed references directly. For example, `LOAD_FAST_BORROW` loads a local |
| 56 | +variable as a borrowed `_PyStackRef`, avoiding both INCREF and DECREF for the temporary lifetime on |
| 57 | +the evaluation stack. |
| 58 | + |
| 59 | +## Tagged integers on the stack |
| 60 | + |
| 61 | +Small ints can be stored inline with `Py_INT_TAG`, so no heap object is involved. Helpers like |
| 62 | +`PyStackRef_TagInt`, `PyStackRef_UntagInt`, and `PyStackRef_IncrementTaggedIntNoOverflow` operate on |
| 63 | +these values. Type checks use `PyStackRef_IsTaggedInt` and `PyStackRef_LongCheck`. |
| 64 | + |
| 65 | +## Free threading considerations |
| 66 | + |
| 67 | +With `Py_GIL_DISABLED`, `Py_TAG_DEFERRED` is an alias for `Py_TAG_REFCNT`. |
| 68 | +Objects that support deferred reference counting can be pushed to the evaluation |
| 69 | +stack and stored in local variables without directly incrementing the reference |
| 70 | +count because they are only freed during cyclic garbage collection. This avoids |
| 71 | +reference count contention on commonly shared objects such as methods and types. The GC |
| 72 | +scans each thread's locals and evaluation stack to keep objects that use |
| 73 | +deferred reference counting alive. |
| 74 | + |
| 75 | +## Debugging support |
| 76 | + |
| 77 | +`Py_STACKREF_DEBUG` builds replace the inline tags with table-backed IDs so the runtime can track |
| 78 | +creation sites, borrows, closes, and leaks. Enabling `Py_STACKREF_CLOSE_DEBUG` additionally records |
| 79 | +double closes. The tables live on `PyInterpreterState` and are initialized in `pystate.c`; helper |
| 80 | +routines reside in `Python/stackrefs.c`. |
0 commit comments