mmtk/util/address.rs
1use atomic_traits::Atomic;
2use bytemuck::NoUninit;
3
4use std::fmt;
5use std::mem;
6use std::num::NonZeroUsize;
7use std::ops::*;
8use std::sync::atomic::Ordering;
9
10use crate::mmtk::{MMAPPER, SFT_MAP};
11use crate::util::metadata::log_bit::LOGGED_VALUE;
12use crate::util::VMThread;
13use crate::util::VMWorkerThread;
14use crate::vm::ObjectModel;
15
16/// size in bytes
17pub type ByteSize = usize;
18/// offset in byte
19pub type ByteOffset = isize;
20
21/// Address represents an arbitrary address. This is designed to represent
22/// address and do address arithmetic mostly in a safe way, and to allow
23/// mark some operations as unsafe. This type needs to be zero overhead
24/// (memory wise and time wise). The idea is from the paper
25/// High-level Low-level Programming (VEE09) and JikesRVM.
26#[repr(transparent)]
27#[derive(Copy, Clone, Eq, Hash, PartialOrd, Ord, PartialEq, NoUninit)]
28pub struct Address(usize);
29
30/// Address + ByteSize (positive)
31impl Add<ByteSize> for Address {
32 type Output = Address;
33 fn add(self, offset: ByteSize) -> Address {
34 Address(self.0 + offset)
35 }
36}
37
38/// Address += ByteSize (positive)
39impl AddAssign<ByteSize> for Address {
40 fn add_assign(&mut self, offset: ByteSize) {
41 self.0 += offset;
42 }
43}
44
45/// Address + ByteOffset (positive or negative)
46impl Add<ByteOffset> for Address {
47 type Output = Address;
48 fn add(self, offset: ByteOffset) -> Address {
49 Address((self.0 as isize + offset) as usize)
50 }
51}
52
53/// Address += ByteOffset (positive or negative)
54impl AddAssign<ByteOffset> for Address {
55 fn add_assign(&mut self, offset: ByteOffset) {
56 self.0 = (self.0 as isize + offset) as usize
57 }
58}
59
60/// Address - ByteSize (positive)
61impl Sub<ByteSize> for Address {
62 type Output = Address;
63 fn sub(self, offset: ByteSize) -> Address {
64 Address(self.0 - offset)
65 }
66}
67
68/// Address -= ByteSize (positive)
69impl SubAssign<ByteSize> for Address {
70 fn sub_assign(&mut self, offset: ByteSize) {
71 self.0 -= offset;
72 }
73}
74
75/// Address - Address (the first address must be higher)
76impl Sub<Address> for Address {
77 type Output = ByteSize;
78 fn sub(self, other: Address) -> ByteSize {
79 debug_assert!(
80 self.0 >= other.0,
81 "for (addr_a - addr_b), a({}) needs to be larger than b({})",
82 self,
83 other
84 );
85 self.0 - other.0
86 }
87}
88
89/// Address & mask
90impl BitAnd<usize> for Address {
91 type Output = usize;
92 fn bitand(self, other: usize) -> usize {
93 self.0 & other
94 }
95}
96// Be careful about the return type here. Address & u8 = u8
97// This is different from Address | u8 = usize
98impl BitAnd<u8> for Address {
99 type Output = u8;
100 fn bitand(self, other: u8) -> u8 {
101 (self.0 as u8) & other
102 }
103}
104
105/// Address | mask
106impl BitOr<usize> for Address {
107 type Output = usize;
108 fn bitor(self, other: usize) -> usize {
109 self.0 | other
110 }
111}
112// Be careful about the return type here. Address | u8 = size
113// This is different from Address & u8 = u8
114impl BitOr<u8> for Address {
115 type Output = usize;
116 fn bitor(self, other: u8) -> usize {
117 self.0 | (other as usize)
118 }
119}
120
121/// Address >> shift (get an index)
122impl Shr<usize> for Address {
123 type Output = usize;
124 fn shr(self, shift: usize) -> usize {
125 self.0 >> shift
126 }
127}
128
129/// Address << shift (get an index)
130impl Shl<usize> for Address {
131 type Output = usize;
132 fn shl(self, shift: usize) -> usize {
133 self.0 << shift
134 }
135}
136
137impl Address {
138 /// The lowest possible address.
139 pub const ZERO: Self = Address(0);
140 /// The highest possible address.
141 pub const MAX: Self = Address(usize::MAX);
142
143 /// creates Address from a pointer
144 pub fn from_ptr<T>(ptr: *const T) -> Address {
145 Address(ptr as usize)
146 }
147
148 /// creates Address from a Rust reference
149 pub fn from_ref<T>(r: &T) -> Address {
150 Address(r as *const T as usize)
151 }
152
153 /// creates Address from a mutable pointer
154 pub fn from_mut_ptr<T>(ptr: *mut T) -> Address {
155 Address(ptr as usize)
156 }
157
158 /// creates a null Address (0)
159 /// # Safety
160 /// It is unsafe and the user needs to be aware that they are creating an invalid address.
161 /// The zero address should only be used as unininitialized or sentinel values in performance critical code (where you dont want to use `Option<Address>`).
162 pub const unsafe fn zero() -> Address {
163 Address(0)
164 }
165
166 /// creates an Address of (usize::MAX)
167 /// # Safety
168 /// It is unsafe and the user needs to be aware that they are creating an invalid address.
169 /// The max address should only be used as unininitialized or sentinel values in performance critical code (where you dont want to use `Option<Address>`).
170 pub unsafe fn max() -> Address {
171 Address(usize::MAX)
172 }
173
174 /// creates an arbitrary Address
175 /// # Safety
176 /// It is unsafe and the user needs to be aware that they may create an invalid address.
177 /// This creates arbitrary addresses which may not be valid. This should only be used for hard-coded addresses. Any other uses of this function could be
178 /// replaced with more proper alternatives.
179 pub const unsafe fn from_usize(raw: usize) -> Address {
180 Address(raw)
181 }
182
183 /// shifts the address by N T-typed objects (returns addr + N * size_of(T))
184 pub fn shift<T>(self, offset: isize) -> Self {
185 self + mem::size_of::<T>() as isize * offset
186 }
187
188 // These const functions are duplicated with the operator traits. But we need them,
189 // as we need them to declare constants.
190
191 /// Get the number of bytes between two addresses. The current address needs to be higher than the other address.
192 pub const fn get_extent(self, other: Address) -> ByteSize {
193 self.0 - other.0
194 }
195
196 /// Get the offset from `other` to `self`. The result is negative is `self` is lower than `other`.
197 pub const fn get_offset(self, other: Address) -> ByteOffset {
198 self.0 as isize - other.0 as isize
199 }
200
201 // We implemented the Add trait but we still keep this add function.
202 // The add() function is const fn, and we can use it to declare Address constants.
203 // The Add trait function cannot be const.
204 #[allow(clippy::should_implement_trait)]
205 /// Add an offset to the address.
206 pub const fn add(self, size: usize) -> Address {
207 Address(self.0 + size)
208 }
209
210 /// Wrapping (modular) addition. Computes self + rhs, wrapping around at the boundary of the type.
211 pub const fn wrapping_add(self, size: usize) -> Address {
212 Address(self.0.wrapping_add(size))
213 }
214
215 // We implemented the Sub trait but we still keep this sub function.
216 // The sub() function is const fn, and we can use it to declare Address constants.
217 // The Sub trait function cannot be const.
218 #[allow(clippy::should_implement_trait)]
219 /// Subtract an offset from the address.
220 pub const fn sub(self, size: usize) -> Address {
221 Address(self.0 - size)
222 }
223
224 /// Apply an signed offset to the address.
225 pub const fn offset(self, offset: isize) -> Address {
226 Address(self.0.wrapping_add_signed(offset))
227 }
228
229 /// Bitwise 'and' with a mask.
230 pub const fn and(self, mask: usize) -> usize {
231 self.0 & mask
232 }
233
234 /// Perform a saturating subtract on the Address
235 pub const fn saturating_sub(self, size: usize) -> Address {
236 Address(self.0.saturating_sub(size))
237 }
238
239 /// loads a value of type T from the address
240 /// # Safety
241 /// This could throw a segment fault if the address is invalid
242 pub unsafe fn load<T: Copy>(self) -> T {
243 *(self.0 as *mut T)
244 }
245
246 /// stores a value of type T to the address
247 /// # Safety
248 /// This could throw a segment fault if the address is invalid
249 pub unsafe fn store<T>(self, value: T) {
250 // We use a ptr.write() operation as directly setting the pointer would drop the old value
251 // which may result in unexpected behaviour
252 (self.0 as *mut T).write(value);
253 }
254
255 /// atomic operation: load
256 /// # Safety
257 /// This could throw a segment fault if the address is invalid
258 pub unsafe fn atomic_load<T: Atomic>(self, order: Ordering) -> T::Type {
259 let loc = &*(self.0 as *const T);
260 loc.load(order)
261 }
262
263 /// atomic operation: store
264 /// # Safety
265 /// This could throw a segment fault if the address is invalid
266 pub unsafe fn atomic_store<T: Atomic>(self, val: T::Type, order: Ordering) {
267 let loc = &*(self.0 as *const T);
268 loc.store(val, order)
269 }
270
271 /// atomic operation: compare and exchange usize
272 /// # Safety
273 /// This could throw a segment fault if the address is invalid
274 pub unsafe fn compare_exchange<T: Atomic>(
275 self,
276 old: T::Type,
277 new: T::Type,
278 success: Ordering,
279 failure: Ordering,
280 ) -> Result<T::Type, T::Type> {
281 let loc = &*(self.0 as *const T);
282 loc.compare_exchange(old, new, success, failure)
283 }
284
285 /// is this address zero?
286 pub fn is_zero(self) -> bool {
287 self.0 == 0
288 }
289
290 /// aligns up the address to the given alignment
291 pub const fn align_up(self, align: ByteSize) -> Address {
292 use crate::util::conversions;
293 Address(conversions::raw_align_up(self.0, align))
294 }
295
296 /// aligns down the address to the given alignment
297 pub const fn align_down(self, align: ByteSize) -> Address {
298 use crate::util::conversions;
299 Address(conversions::raw_align_down(self.0, align))
300 }
301
302 /// is this address aligned to the given alignment
303 pub const fn is_aligned_to(self, align: usize) -> bool {
304 use crate::util::conversions;
305 conversions::raw_is_aligned(self.0, align)
306 }
307
308 /// converts the Address to a pointer
309 pub fn to_ptr<T>(self) -> *const T {
310 self.0 as *const T
311 }
312
313 /// converts the Address to a mutable pointer
314 pub fn to_mut_ptr<T>(self) -> *mut T {
315 self.0 as *mut T
316 }
317
318 /// converts the Address to a Rust reference
319 ///
320 /// # Safety
321 /// The caller must guarantee the address actually points to a Rust object.
322 pub unsafe fn as_ref<'a, T>(self) -> &'a T {
323 &*self.to_mut_ptr()
324 }
325
326 /// converts the Address to a mutable Rust reference
327 ///
328 /// # Safety
329 /// The caller must guarantee the address actually points to a Rust object.
330 pub unsafe fn as_mut_ref<'a, T>(self) -> &'a mut T {
331 &mut *self.to_mut_ptr()
332 }
333
334 /// converts the Address to a pointer-sized integer
335 pub const fn as_usize(self) -> usize {
336 self.0
337 }
338
339 /// returns the chunk index for this address
340 pub fn chunk_index(self) -> usize {
341 use crate::util::conversions;
342 conversions::address_to_chunk_index(self)
343 }
344
345 /// return true if the referenced memory is mapped
346 pub fn is_mapped(self) -> bool {
347 if self.0 == 0 {
348 false
349 } else {
350 MMAPPER.is_mapped_address(self)
351 }
352 }
353
354 /// Check whether the field at this address is logged, i.e. whether the field-level write
355 /// barrier has already recorded a write to it and can skip its slow path.
356 pub fn is_field_logged<VM: VMBinding>(self) -> bool {
357 debug_assert!(!self.is_zero());
358 unsafe {
359 VM::VMObjectModel::GLOBAL_FIELD_UNLOG_BIT_SPEC
360 .as_spec()
361 .extract_side_spec()
362 .load::<u8>(self)
363 == LOGGED_VALUE
364 }
365 }
366
367 /// Mark the field(s) covered by this address as unlogged (using a relaxed, non-atomic store),
368 /// so that a subsequent write to them will be caught by the field-level write barrier's slow path again.
369 pub fn unlog_field_relaxed<VM: VMBinding>(self) {
370 debug_assert!(!self.is_zero());
371 let heap_bytes_per_unlog_byte = if VM::VMObjectModel::COMPRESSED_PTR_ENABLED {
372 32usize
373 } else {
374 64
375 };
376 let a = self.align_down(heap_bytes_per_unlog_byte);
377 unsafe {
378 VM::VMObjectModel::GLOBAL_FIELD_UNLOG_BIT_SPEC
379 .as_spec()
380 .extract_side_spec()
381 .store_byte_relaxed(a, 0xffu8)
382 }
383 }
384
385 /// Returns the intersection of the two address ranges. The returned range could
386 /// be empty if there is no intersection between the ranges.
387 pub fn range_intersection(r1: &Range<Address>, r2: &Range<Address>) -> Range<Address> {
388 r1.start.max(r2.start)..r1.end.min(r2.end)
389 }
390}
391
392/// allows print Address as upper-case hex value
393impl fmt::UpperHex for Address {
394 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
395 write!(f, "{:X}", self.0)
396 }
397}
398
399/// allows print Address as lower-case hex value
400impl fmt::LowerHex for Address {
401 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
402 write!(f, "{:x}", self.0)
403 }
404}
405
406/// allows Display format the Address (as upper-case hex value with 0x prefix)
407impl fmt::Display for Address {
408 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
409 write!(f, "{:#x}", self.0)
410 }
411}
412
413/// allows Debug format the Address (as upper-case hex value with 0x prefix)
414impl fmt::Debug for Address {
415 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
416 write!(f, "{:#x}", self.0)
417 }
418}
419
420impl std::str::FromStr for Address {
421 type Err = std::num::ParseIntError;
422
423 fn from_str(s: &str) -> Result<Self, Self::Err> {
424 let raw: usize = s.parse()?;
425 Ok(Address(raw))
426 }
427}
428
429#[cfg(test)]
430mod tests {
431 use crate::util::Address;
432
433 #[test]
434 fn align_up() {
435 unsafe {
436 assert_eq!(
437 Address::from_usize(0x10).align_up(0x10),
438 Address::from_usize(0x10)
439 );
440 assert_eq!(
441 Address::from_usize(0x11).align_up(0x10),
442 Address::from_usize(0x20)
443 );
444 assert_eq!(
445 Address::from_usize(0x20).align_up(0x10),
446 Address::from_usize(0x20)
447 );
448 }
449 }
450
451 #[test]
452 fn align_down() {
453 unsafe {
454 assert_eq!(
455 Address::from_usize(0x10).align_down(0x10),
456 Address::from_usize(0x10)
457 );
458 assert_eq!(
459 Address::from_usize(0x11).align_down(0x10),
460 Address::from_usize(0x10)
461 );
462 assert_eq!(
463 Address::from_usize(0x20).align_down(0x10),
464 Address::from_usize(0x20)
465 );
466 }
467 }
468
469 #[test]
470 fn is_aligned_to() {
471 unsafe {
472 assert!(Address::from_usize(0x10).is_aligned_to(0x10));
473 assert!(!Address::from_usize(0x11).is_aligned_to(0x10));
474 assert!(Address::from_usize(0x10).is_aligned_to(0x8));
475 assert!(!Address::from_usize(0x10).is_aligned_to(0x20));
476 }
477 }
478
479 #[test]
480 fn bit_and() {
481 unsafe {
482 assert_eq!(
483 Address::from_usize(0b1111_1111_1100usize) & 0b1010u8,
484 0b1000u8
485 );
486 assert_eq!(
487 Address::from_usize(0b1111_1111_1100usize) & 0b1000_0000_1010usize,
488 0b1000_0000_1000usize
489 );
490 }
491 }
492
493 #[test]
494 fn bit_or() {
495 unsafe {
496 assert_eq!(
497 Address::from_usize(0b1111_1111_1100usize) | 0b1010u8,
498 0b1111_1111_1110usize
499 );
500 assert_eq!(
501 Address::from_usize(0b1111_1111_1100usize) | 0b1000_0000_1010usize,
502 0b1111_1111_1110usize
503 );
504 }
505 }
506}
507
508use crate::vm::Scanning;
509use crate::vm::VMBinding;
510
511/// `ObjectReference` represents address for an object. Compared with `Address`, operations allowed
512/// on `ObjectReference` are very limited. No address arithmetics are allowed for `ObjectReference`.
513/// The idea is from the paper [Demystifying Magic: High-level Low-level Programming (VEE09)][FBC09]
514/// and [JikesRVM].
515///
516/// In MMTk, `ObjectReference` holds a non-zero address, i.e. its **raw address**. It must satisfy
517/// the following requirements.
518///
519/// - It uniquely references an MMTk object.
520/// - The address must be within the address range of the object it refers to.
521/// - The address must be word-aligned.
522/// - It must be efficient to access object metadata from an `ObjectReference`.
523///
524/// Each `ObjectReference` uniquely identifies exactly one MMTk object. There is no "null
525/// reference" (see below for details).
526///
527/// Conversely, each object has a unique (raw) address used for `ObjectReference`. That address is
528/// nominated by the VM binding right after an object is allocated in the MMTk heap (i.e. the
529/// argument of [`crate::memory_manager::post_alloc`]). The same address is used by all
530/// `ObjectReference` instances that refer to that object until the object is moved, at which time
531/// the VM binding shall choose another address to use as the `ObjectReference` of the new copy (in
532/// [`crate::vm::ObjectModel::copy`] or [`crate::vm::ObjectModel::get_reference_when_copied_to`])
533/// until the object is moved again.
534///
535/// In addition to the raw address, there are also two addresses related to each object allocated in
536/// MMTk heap, namely **starting address** and **header address**. See the
537/// [`crate::vm::ObjectModel`] trait for their precise definition.
538///
539/// The VM binding may, in theory, pick any aligned address within the object, and it doesn't have
540/// to be the starting address. However, during tracing, MMTk will need to access object metadata
541/// from a `ObjectReference`. Particularly, it needs to identify reference fields, and query
542/// information about the object, such as object size. Such information is usually accessed from
543/// object headers. The choice of `ObjectReference` must make such accesses efficient.
544///
545/// Because the raw address is within the object, MMTk will also use the raw address to identify the
546/// space or region (chunk, block, line, etc.) that contains the object, and to access side metadata
547/// and the SFTMap. If a VM binding needs to access side metadata directly (particularly, setting
548/// the "valid-object (VO) bit" in allocation fast paths), it shall use the raw address to compute
549/// the byte and bit address of the metadata bits.
550///
551/// # Notes
552///
553/// ## About VMs own concepts of "object references"
554///
555/// A runtime may define its own concept of "object references" differently from MMTk's
556/// `ObjectReference` type. It may define its object reference as
557///
558/// - the starting address of an object,
559/// - an address inside an object,
560/// - an address at a certain offset outside an object,
561/// - a handle that points to an indirection table entry where a pointer to the object is held, or
562/// - anything else that refers to an object.
563///
564/// Regardless, when passing an `ObjectReference` value to MMTk through the API, MMTk expectes its
565/// value to satisfy MMTk's definition. This means MMTk's `ObjectReference` may not be the value
566/// held in an object field. Some VM bindings may need to do conversions when passing object
567/// references to MMTk. For example, adding an offset to the VM-level object reference so that the
568/// resulting address is within the object. When using handles, the VM binding may use the *pointer
569/// stored in the entry* of the indirection table instead of the *pointer to the entry* itself as
570/// MMTk-level `ObjectReference`.
571///
572/// ## About null references
573///
574/// An [`ObjectReference`] always refers to an object. Some VMs have special values (such as `null`
575/// in Java) that do not refer to any object. Those values cannot be represented by
576/// `ObjectReference`. When scanning roots and object fields, the VM binding should ignore slots
577/// that do not hold a reference to an object. Specifically, [`crate::vm::slot::Slot::load`]
578/// returns `Option<ObjectReference>`. It can return `None` so that MMTk skips that slot.
579///
580/// `Option<ObjectReference>` should be used for the cases where a non-null object reference may or
581/// may not exist, That includes several API functions, including [`crate::vm::slot::Slot::load`].
582/// [`ObjectReference`] is backed by `NonZeroUsize` which cannot be zero, and it has the
583/// `#[repr(transparent)]` attribute. Thanks to [null pointer optimization (NPO)][NPO],
584/// `Option<ObjectReference>` has the same size as `NonZeroUsize` and `usize`.
585///
586/// For the convenience of passing `Option<ObjectReference>` to and from native (C/C++) programs,
587/// mmtk-core provides [`crate::util::api_util::NullableObjectReference`].
588///
589/// ## About the `VMSpace`
590///
591/// The `VMSpace` is managed by the VM binding. The VM binding declare ranges of memory as part of
592/// the `VMSpace`, but MMTk never allocates into it. The VM binding allocates objects into the
593/// `VMSpace` (usually by mapping boot-images), and refers to objects in the `VMSpace` using
594/// `ObjectReference`s whose raw addresses point inside those objects (and must be word-aligned,
595/// too). MMTk will access metadata using methods of [`ObjectModel`] like other objects. MMTk also
596/// has side metadata available for objects in the `VMSpace`.
597///
598/// ## About `ObjectReference` pointing outside MMTk spaces
599///
600/// If a VM binding implements [`crate::vm::ActivePlan::vm_trace_object`], `ObjectReference` is
601/// allowed to point to locations outside any MMTk spaces. When tracing objects, such
602/// `ObjectReference` values will be processed by `ActivePlan::vm_trace_object` so that the VM
603/// binding can trace its own allocated objects during GC. However, **this is an experimental
604/// feature**, and may not interact well with other parts of MMTk. Notably, MMTk will not allocate
605/// side metadata for such `ObjectReference`, and attempts to access side metadata with a non-MMTk
606/// `ObjectReference` will result in crash. Use with caution.
607///
608/// [FBC09]: https://dl.acm.org/doi/10.1145/1508293.1508305
609/// [JikesRVM]: https://www.jikesrvm.org/
610/// [`ObjectModel`]: crate::vm::ObjectModel
611/// [NPO]: https://doc.rust-lang.org/std/option/index.html#representation
612#[repr(transparent)]
613#[derive(Copy, Clone, Eq, Hash, PartialOrd, Ord, PartialEq, NoUninit)]
614pub struct ObjectReference(NonZeroUsize);
615
616impl ObjectReference {
617 /// The required minimal alignment for object reference. If the object reference's raw address is not aligned to this value,
618 /// you will see an assertion failure in the debug build when constructing an object reference instance.
619 pub const ALIGNMENT: usize = crate::util::constants::BYTES_IN_ADDRESS;
620
621 /// Cast the object reference to its raw address.
622 pub fn to_raw_address(self) -> Address {
623 Address(self.0.get())
624 }
625
626 /// Cast a raw address to an object reference.
627 ///
628 /// If `addr` is 0, the result is `None`.
629 pub fn from_raw_address(addr: Address) -> Option<ObjectReference> {
630 debug_assert!(
631 addr.is_aligned_to(Self::ALIGNMENT),
632 "ObjectReference is required to be word aligned. addr: {addr}"
633 );
634 NonZeroUsize::new(addr.0).map(ObjectReference)
635 }
636
637 /// Like `from_raw_address`, but assume `addr` is not zero. This can be used to elide a check
638 /// against zero for performance-critical code.
639 ///
640 /// # Safety
641 ///
642 /// This method assumes `addr` is not zero. It should only be used in cases where we know at
643 /// compile time that the input cannot be zero. For example, if we compute the address by
644 /// adding a positive offset to a non-zero address, we know the result must not be zero.
645 pub unsafe fn from_raw_address_unchecked(addr: Address) -> ObjectReference {
646 debug_assert!(!addr.is_zero());
647 debug_assert!(
648 addr.is_aligned_to(Self::ALIGNMENT),
649 "ObjectReference is required to be word aligned. addr: {addr}"
650 );
651 ObjectReference(NonZeroUsize::new_unchecked(addr.0))
652 }
653
654 /// Get the header base address from an object reference. This method is used by MMTk to get a base address for the
655 /// object header, and access the object header. This method is syntactic sugar for [`crate::vm::ObjectModel::ref_to_header`].
656 /// See the comments on [`crate::vm::ObjectModel::ref_to_header`].
657 pub fn to_header<VM: VMBinding>(self) -> Address {
658 use crate::vm::ObjectModel;
659 VM::VMObjectModel::ref_to_header(self)
660 }
661
662 /// Get the start of the allocation address for the object. This method is used by MMTk to get the start of the allocation
663 /// address originally returned from [`crate::memory_manager::alloc`] for the object.
664 /// This method is syntactic sugar for [`crate::vm::ObjectModel::ref_to_object_start`]. See comments on [`crate::vm::ObjectModel::ref_to_object_start`].
665 pub fn to_object_start<VM: VMBinding>(self) -> Address {
666 use crate::vm::ObjectModel;
667 let object_start = VM::VMObjectModel::ref_to_object_start(self);
668 debug_assert!(!VM::VMObjectModel::UNIFIED_OBJECT_REFERENCE_ADDRESS || object_start == self.to_raw_address(), "The binding claims unified object reference address, but for object reference {}, ref_to_object_start() returns {}", self, object_start);
669 debug_assert!(
670 self.to_raw_address()
671 >= object_start + VM::VMObjectModel::OBJECT_REF_OFFSET_LOWER_BOUND,
672 "The invariant `object_ref >= object_start + OBJECT_REF_OFFSET_LOWER_BOUND` is violated. \
673 object_ref: {}, object_start: {}, OBJECT_REF_OFFSET_LOWER_BOUND: {}",
674 self.to_raw_address(),
675 object_start,
676 VM::VMObjectModel::OBJECT_REF_OFFSET_LOWER_BOUND,
677 );
678 object_start
679 }
680
681 /// Is the object reachable, determined by the policy?
682 ///
683 /// # Scope
684 ///
685 /// This method is primarily used during weak reference processing. It can check if an object
686 /// (particularly finalizable objects and objects pointed by weak references) has been reached
687 /// by following strong references or weak references of higher strength.
688 ///
689 /// This method can also be used during tracing for debug purposes.
690 ///
691 /// When called at other times, particularly during mutator time, the behavior is specific to
692 /// the implementation of the plan and policy due to their strategies of metadata clean-up. If
693 /// the VM needs to know if any given reference is still valid, it should instead use the valid
694 /// object bit (VO-bit) metadata which is enabled by the Cargo feature "vo_bit".
695 ///
696 /// # Return value
697 ///
698 /// It returns `true` if one of the following is true:
699 ///
700 /// 1. The object has been traced (i.e. reached) since tracing started.
701 /// 2. The policy conservatively considers the object reachable even though it has not been
702 /// traced.
703 /// - Particularly, if the plan is generational, this method will return `true` if the
704 /// object is mature during nursery GC.
705 ///
706 /// Due to the conservativeness, if this method returns `true`, it does not necessarily mean the
707 /// object must be reachable from roots. In generational GC, mature objects can be unreachable
708 /// from roots while the GC chooses not to reclaim their memory during nursery GC. Conversely,
709 /// all young objects reachable from the remembered set are retained even though some mature
710 /// objects in the remembered set can be unreachable in the first place. (This is known as
711 /// *nepotism* in GC literature.)
712 ///
713 /// Note: Objects in ImmortalSpace may have `is_live = true` but are actually unreachable.
714 pub fn is_reachable(self) -> bool {
715 unsafe { SFT_MAP.get_unchecked(self.to_raw_address()) }.is_reachable(self)
716 }
717
718 /// Is the object live, determined by the policy?
719 pub fn is_live(self) -> bool {
720 unsafe { SFT_MAP.get_unchecked(self.to_raw_address()) }.is_live(self)
721 }
722
723 /// Can the object be moved?
724 pub fn is_movable(self) -> bool {
725 unsafe { SFT_MAP.get_unchecked(self.to_raw_address()) }.is_movable()
726 }
727
728 /// Get forwarding pointer if the object is forwarded.
729 pub fn get_forwarded_object(self) -> Option<Self> {
730 unsafe { SFT_MAP.get_unchecked(self.to_raw_address()) }.get_forwarded_object(self)
731 }
732
733 /// Is the object in any MMTk spaces?
734 pub fn is_in_any_space(self) -> bool {
735 unsafe { SFT_MAP.get_unchecked(self.to_raw_address()) }.is_in_space(self)
736 }
737
738 /// Is the object sane?
739 #[cfg(feature = "sanity")]
740 pub fn is_sane(self) -> bool {
741 unsafe { SFT_MAP.get_unchecked(self.to_raw_address()) }.is_sane()
742 }
743
744 /// Get the current size (in bytes) of the object, as determined by the VM's object model.
745 pub fn get_size<VM: VMBinding>(self) -> usize {
746 VM::VMObjectModel::get_current_size(self)
747 }
748
749 /// Iterate over the slots (fields) of the object, calling `f` for each slot the VM's scanning
750 /// implementation reports for this object.
751 pub fn iterate_fields<VM: VMBinding, F: FnMut(VM::VMSlot)>(self, _tls: VMThread, mut f: F) {
752 // FIXME: We should use tls from the arguments.
753 // See https://github.com/mmtk/mmtk-core/issues/1375
754 let fake_tls = VMWorkerThread(VMThread::UNINITIALIZED);
755 if !<VM::VMScanning as Scanning<VM>>::support_slot_enqueuing(fake_tls, self) {
756 panic!("SlotIterator::iterate_fields cannot be used on objects that don't support slot-enqueuing");
757 }
758 <VM::VMScanning as Scanning<VM>>::scan_object(fake_tls, self, &mut f);
759 }
760}
761
762/// allows print Address as upper-case hex value
763impl fmt::UpperHex for ObjectReference {
764 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
765 write!(f, "{:X}", self.0)
766 }
767}
768
769/// allows print Address as lower-case hex value
770impl fmt::LowerHex for ObjectReference {
771 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
772 write!(f, "{:x}", self.0)
773 }
774}
775
776/// allows Display format the Address (as upper-case hex value with 0x prefix)
777impl fmt::Display for ObjectReference {
778 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
779 write!(f, "{:#x}", self.0)
780 }
781}
782
783/// allows Debug format the Address (as upper-case hex value with 0x prefix)
784impl fmt::Debug for ObjectReference {
785 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
786 write!(f, "{:#x}", self.0)
787 }
788}