@@ -3,6 +3,7 @@ use bytemuck::NoUninit;
3
3
4
4
use std:: fmt;
5
5
use std:: mem;
6
+ use std:: num:: NonZeroUsize ;
6
7
use std:: ops:: * ;
7
8
use std:: sync:: atomic:: Ordering ;
8
9
@@ -477,30 +478,52 @@ use crate::vm::VMBinding;
477
478
/// their layout. We now only allow a binding to define their semantics through a set of
478
479
/// methods in [`crate::vm::ObjectModel`]. Major refactoring is needed in MMTk to allow
479
480
/// the opaque `ObjectReference` type, and we haven't seen a use case for now.
481
+ ///
482
+ /// Note that [`ObjectReference`] cannot be null. For the cases where a non-null object reference
483
+ /// may or may not exist, (such as the result of [`crate::vm::edge_shape::Edge::load`])
484
+ /// `Option<ObjectReference>` should be used. [`ObjectReference`] is backed by `NonZeroUsize`
485
+ /// which cannot be zero, and it has the `#[repr(transparent)]` attribute. Thanks to [null pointer
486
+ /// optimization (NPO)][NPO], `Option<ObjectReference>` has the same size as `NonZeroUsize` and
487
+ /// `usize`. For the convenience of passing `Option<ObjectReference>` to and from native (C/C++)
488
+ /// programs, mmtk-core provides [`crate::util::api_util::NullableObjectReference`].
489
+ ///
490
+ /// [NPO]: https://doc.rust-lang.org/std/option/index.html#representation
480
491
#[ repr( transparent) ]
481
492
#[ derive( Copy , Clone , Eq , Hash , PartialOrd , Ord , PartialEq , NoUninit ) ]
482
- pub struct ObjectReference ( usize ) ;
493
+ pub struct ObjectReference ( NonZeroUsize ) ;
483
494
484
495
impl ObjectReference {
485
- /// The null object reference, represented as zero.
486
- pub const NULL : ObjectReference = ObjectReference ( 0 ) ;
487
-
488
496
/// Cast the object reference to its raw address. This method is mostly for the convinience of a binding.
489
497
///
490
498
/// MMTk should not make any assumption on the actual location of the address with the object reference.
491
499
/// MMTk should not assume the address returned by this method is in our allocation. For the purposes of
492
500
/// setting object metadata, MMTk should use [`crate::vm::ObjectModel::ref_to_address()`] or [`crate::vm::ObjectModel::ref_to_header()`].
493
501
pub fn to_raw_address ( self ) -> Address {
494
- Address ( self . 0 )
502
+ Address ( self . 0 . get ( ) )
495
503
}
496
504
497
505
/// Cast a raw address to an object reference. This method is mostly for the convinience of a binding.
498
506
/// This is how a binding creates `ObjectReference` instances.
499
507
///
508
+ /// If `addr` is 0, the result is `None`.
509
+ ///
500
510
/// MMTk should not assume an arbitrary address can be turned into an object reference. MMTk can use [`crate::vm::ObjectModel::address_to_ref()`]
501
511
/// to turn addresses that are from [`crate::vm::ObjectModel::ref_to_address()`] back to object.
502
- pub fn from_raw_address ( addr : Address ) -> ObjectReference {
503
- ObjectReference ( addr. 0 )
512
+ pub fn from_raw_address ( addr : Address ) -> Option < ObjectReference > {
513
+ NonZeroUsize :: new ( addr. 0 ) . map ( ObjectReference )
514
+ }
515
+
516
+ /// Like `from_raw_address`, but assume `addr` is not zero. This can be used to elide a check
517
+ /// against zero for performance-critical code.
518
+ ///
519
+ /// # Safety
520
+ ///
521
+ /// This method assumes `addr` is not zero. It should only be used in cases where we know at
522
+ /// compile time that the input cannot be zero. For example, if we compute the address by
523
+ /// adding a positive offset to a non-zero address, we know the result must not be zero.
524
+ pub unsafe fn from_raw_address_unchecked ( addr : Address ) -> ObjectReference {
525
+ debug_assert ! ( !addr. is_zero( ) ) ;
526
+ ObjectReference ( NonZeroUsize :: new_unchecked ( addr. 0 ) )
504
527
}
505
528
506
529
/// Get the in-heap address from an object reference. This method is used by MMTk to get an in-heap address
@@ -541,28 +564,15 @@ impl ObjectReference {
541
564
obj
542
565
}
543
566
544
- /// is this object reference null reference?
545
- pub fn is_null ( self ) -> bool {
546
- self . 0 == 0
547
- }
548
-
549
567
/// Is the object reachable, determined by the policy?
550
568
/// Note: Objects in ImmortalSpace may have `is_live = true` but are actually unreachable.
551
569
pub fn is_reachable < VM : VMBinding > ( self ) -> bool {
552
- if self . is_null ( ) {
553
- false
554
- } else {
555
- unsafe { SFT_MAP . get_unchecked ( self . to_address :: < VM > ( ) ) } . is_reachable ( self )
556
- }
570
+ unsafe { SFT_MAP . get_unchecked ( self . to_address :: < VM > ( ) ) } . is_reachable ( self )
557
571
}
558
572
559
573
/// Is the object live, determined by the policy?
560
574
pub fn is_live < VM : VMBinding > ( self ) -> bool {
561
- if self . 0 == 0 {
562
- false
563
- } else {
564
- unsafe { SFT_MAP . get_unchecked ( self . to_address :: < VM > ( ) ) } . is_live ( self )
565
- }
575
+ unsafe { SFT_MAP . get_unchecked ( self . to_address :: < VM > ( ) ) } . is_live ( self )
566
576
}
567
577
568
578
/// Can the object be moved?
0 commit comments