Skip to content

Commit 7b83655

Browse files
committed
Replace per-instance HAS_EXT bit with type flag checks
Remove GcBits::HAS_EXT and determine ObjExt prefix presence from type flags (HAS_DICT, HAS_WEAKREF, member_count), matching CPython's Py_TPFLAGS_PREHEADER approach. Also fix needs_ext to check HAS_DICT flag instead of dict.is_some(), matching CPython where MANAGED_DICT always allocates the preheader.
1 parent d577c3d commit 7b83655

File tree

1 file changed

+30
-13
lines changed

1 file changed

+30
-13
lines changed

crates/vm/src/object/core.rs

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,6 @@ bitflags::bitflags! {
239239
const SHARED_INLINE = 1 << 5;
240240
/// Use deferred reference counting
241241
const DEFERRED = 1 << 6;
242-
/// Object has ObjExt prefix (dict, weak_list, slots)
243-
const HAS_EXT = 1 << 7;
244242
}
245243
}
246244

@@ -285,7 +283,8 @@ impl fmt::Debug for ObjExt {
285283
const EXT_OFFSET: usize = core::mem::size_of::<ObjExt>();
286284
// Guarantee: ObjExt size is a multiple of its alignment, and its alignment
287285
// is >= any PyInner alignment, so Layout::extend produces no inter-padding.
288-
const _: () = assert!(core::mem::size_of::<ObjExt>().is_multiple_of(core::mem::align_of::<ObjExt>()));
286+
const _: () =
287+
assert!(core::mem::size_of::<ObjExt>().is_multiple_of(core::mem::align_of::<ObjExt>()));
289288
const _: () = assert!(core::mem::align_of::<ObjExt>() >= core::mem::align_of::<PyInner<()>>());
290289

291290
/// This is an actual python object. It consists of a `typ` which is the
@@ -305,16 +304,34 @@ pub(super) struct PyInner<T> {
305304
pub(crate) const SIZEOF_PYOBJECT_HEAD: usize = core::mem::size_of::<PyInner<()>>();
306305

307306
impl<T> PyInner<T> {
307+
/// Check if this object has an ObjExt prefix by examining type flags.
308+
/// Equivalent to Py_TPFLAGS_PREHEADER (MANAGED_DICT | MANAGED_WEAKREF)
309+
/// plus member slots.
310+
#[inline(always)]
311+
fn has_ext(&self) -> bool {
312+
let typ = self.typ.deref();
313+
typ.slots
314+
.flags
315+
.has_feature(crate::types::PyTypeFlags::HAS_DICT)
316+
|| typ
317+
.slots
318+
.flags
319+
.has_feature(crate::types::PyTypeFlags::HAS_WEAKREF)
320+
|| typ.slots.member_count > 0
321+
}
322+
308323
/// Access the ObjExt prefix at a negative offset from this PyInner.
309324
/// Returns None if this object was allocated without the prefix.
310325
///
326+
/// Uses type flags (HAS_DICT, HAS_WEAKREF, member_count) to determine
327+
/// if the prefix exists, matching CPython's Py_TPFLAGS_PREHEADER approach.
328+
///
311329
/// Uses exposed provenance to reconstruct a pointer covering the entire
312330
/// allocation (ObjExt prefix + PyInner). The allocation pointer's provenance
313331
/// is exposed at allocation time via `expose_provenance()`.
314332
#[inline(always)]
315333
pub(super) fn ext_ref(&self) -> Option<&ObjExt> {
316-
if !GcBits::from_bits_retain(self.gc_bits.load(Ordering::Relaxed)).contains(GcBits::HAS_EXT)
317-
{
334+
if !self.has_ext() {
318335
return None;
319336
}
320337
let self_addr = (self as *const Self as *const u8).addr();
@@ -859,10 +876,7 @@ impl<T: PyPayload> PyInner<T> {
859876
/// `ptr` must be a valid pointer from `PyInner::new` and must not be used after this call.
860877
unsafe fn dealloc(ptr: *mut Self) {
861878
unsafe {
862-
let has_ext = GcBits::from_bits_retain((*ptr).gc_bits.load(Ordering::Relaxed))
863-
.contains(GcBits::HAS_EXT);
864-
865-
if has_ext {
879+
if (*ptr).has_ext() {
866880
let ext_layout = core::alloc::Layout::new::<ObjExt>();
867881
let inner_layout = core::alloc::Layout::new::<Self>();
868882
let (combined, inner_offset) = ext_layout.extend(inner_layout).unwrap();
@@ -887,7 +901,10 @@ impl<T: PyPayload + core::fmt::Debug> PyInner<T> {
887901
/// For objects with ext, the allocation layout is: [ObjExt][PyInner<T>]
888902
fn new(payload: T, typ: PyTypeRef, dict: Option<PyDictRef>) -> *mut Self {
889903
let member_count = typ.slots.member_count;
890-
let needs_ext = dict.is_some()
904+
let needs_ext = typ
905+
.slots
906+
.flags
907+
.has_feature(crate::types::PyTypeFlags::HAS_DICT)
891908
|| typ
892909
.slots
893910
.flags
@@ -915,7 +932,7 @@ impl<T: PyPayload + core::fmt::Debug> PyInner<T> {
915932
inner_ptr.write(Self {
916933
ref_count: RefCount::new(),
917934
vtable: PyObjVTable::of::<T>(),
918-
gc_bits: Radium::new(GcBits::HAS_EXT.bits()),
935+
gc_bits: Radium::new(0),
919936
typ: PyAtomicRef::from(typ),
920937
payload,
921938
});
@@ -2132,7 +2149,7 @@ pub(crate) fn init_type_hierarchy() -> (PyTypeRef, PyTypeRef, PyTypeRef) {
21322149
PyInner::<PyType> {
21332150
ref_count: RefCount::new(),
21342151
vtable: PyObjVTable::of::<PyType>(),
2135-
gc_bits: Radium::new(GcBits::HAS_EXT.bits()),
2152+
gc_bits: Radium::new(0),
21362153
payload: type_payload,
21372154
},
21382155
Uninit { typ }
@@ -2145,7 +2162,7 @@ pub(crate) fn init_type_hierarchy() -> (PyTypeRef, PyTypeRef, PyTypeRef) {
21452162
PyInner::<PyType> {
21462163
ref_count: RefCount::new(),
21472164
vtable: PyObjVTable::of::<PyType>(),
2148-
gc_bits: Radium::new(GcBits::HAS_EXT.bits()),
2165+
gc_bits: Radium::new(0),
21492166
payload: object_payload,
21502167
},
21512168
Uninit { typ },

0 commit comments

Comments
 (0)