Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Require object reference to be aligned #1159

Merged
merged 8 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/util/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -486,12 +486,18 @@ use crate::vm::VMBinding;
/// `usize`. For the convenience of passing `Option<ObjectReference>` to and from native (C/C++)
/// programs, mmtk-core provides [`crate::util::api_util::NullableObjectReference`].
///
/// Note that [`ObjectReference`] has to be word aligned.
///
/// [NPO]: https://doc.rust-lang.org/std/option/index.html#representation
#[repr(transparent)]
#[derive(Copy, Clone, Eq, Hash, PartialOrd, Ord, PartialEq, NoUninit)]
pub struct ObjectReference(NonZeroUsize);

impl ObjectReference {
/// The required minimal alignment for object reference. If the object reference's raw address is not aligned to this value,
/// you will see an assertion failure in the debug build.
wks marked this conversation as resolved.
Show resolved Hide resolved
pub const ALIGNMENT: usize = crate::util::constants::BYTES_IN_ADDRESS;

/// Cast the object reference to its raw address. This method is mostly for the convinience of a binding.
///
/// MMTk should not make any assumption on the actual location of the address with the object reference.
Expand All @@ -509,6 +515,10 @@ impl ObjectReference {
/// MMTk should not assume an arbitrary address can be turned into an object reference. MMTk can use [`crate::vm::ObjectModel::address_to_ref()`]
/// to turn addresses that are from [`crate::vm::ObjectModel::ref_to_address()`] back to object.
pub fn from_raw_address(addr: Address) -> Option<ObjectReference> {
debug_assert!(
addr.is_aligned_to(Self::ALIGNMENT),
"ObjectReference is required to be word aligned"
);
NonZeroUsize::new(addr.0).map(ObjectReference)
}

Expand All @@ -522,6 +532,10 @@ impl ObjectReference {
/// adding a positive offset to a non-zero address, we know the result must not be zero.
pub unsafe fn from_raw_address_unchecked(addr: Address) -> ObjectReference {
debug_assert!(!addr.is_zero());
debug_assert!(
addr.is_aligned_to(Self::ALIGNMENT),
"ObjectReference is required to be word aligned"
);
ObjectReference(NonZeroUsize::new_unchecked(addr.0))
}

Expand Down Expand Up @@ -560,6 +574,10 @@ impl ObjectReference {
use crate::vm::ObjectModel;
let obj = VM::VMObjectModel::address_to_ref(addr);
debug_assert!(!VM::VMObjectModel::UNIFIED_OBJECT_REFERENCE_ADDRESS || addr == obj.to_raw_address(), "The binding claims unified object reference address, but for address {}, address_to_ref() returns {}", addr, obj);
debug_assert!(
obj.to_raw_address().is_aligned_to(Self::ALIGNMENT),
"ObjectReference is required to be word aligned"
);
obj
}

Expand Down
2 changes: 1 addition & 1 deletion src/util/test_util/mock_vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use std::sync::Mutex;

/// The offset between object reference and the allocation address if we use
/// the default mock VM.
pub const DEFAULT_OBJECT_REF_OFFSET: usize = 4;
pub const DEFAULT_OBJECT_REF_OFFSET: usize = crate::util::constants::BYTES_IN_ADDRESS;

// To mock static methods, we have to create a static instance of `MockVM`.
lazy_static! {
Expand Down
52 changes: 44 additions & 8 deletions src/vm/object_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ use crate::vm::VMBinding;
///
/// Note that depending on the selected GC plan, only a subset of the methods provided here will be used.
///
/// Side Specs Layout
/// # Side Specs Layout
///
/// Short version
/// ## Short version
///
/// * For *global* side metadata:
/// * The first spec: VMGlobalXXXSpec::side_first()
Expand All @@ -38,7 +38,7 @@ use crate::vm::VMBinding;
/// * The first spec: VMLocalXXXSpec::side_first()
/// * The following specs: VMLocalXXXSpec::side_after(FIRST_LOCAL.as_spec())
///
/// Detailed explanation
/// ## Detailed explanation
///
/// There are two types of side metadata layout in MMTk:
///
Expand All @@ -57,6 +57,37 @@ use crate::vm::VMBinding;
/// and for a third SideMetadataSpec (`LS3`), the `offset` will be `BASE(LS2) + required_metadata_space_per_chunk(LS2)`.
///
/// For all other policies, the `offset` starts from zero. This is safe because no two policies ever manage one chunk, so there will be no overlap.
///
/// # Object Layout Addresses
///
/// MMTk tries to be general to cope with different language implementations and different object models. Thus it does not assume the internal of the object model.
/// Instead, MMTk only uses the following addresses for an object. If you find the MMTk's approach does not work for your language in practice, you are welcome to submit an issue
/// or engage with MMTk team on Zulip to disucss further.
///
/// ### Object Reference
qinsoon marked this conversation as resolved.
Show resolved Hide resolved
///
/// See [`crate::util::address::ObjectReference`]. This is a special address that represents the object.
/// MMTk refers to an object by its object reference. An object reference cannot be NULL, and has to be
/// word aligned ([`crate::util::address::ObjectReference::ALIGNMENT`]). It is allowed that an object
/// reference is not in the allocated memory for the object.
///
/// ### Object Start Address
///
/// The address is returned by an allocation call [`crate::memory_manager::alloc`]. This is the start of the address range of the allocation.
wks marked this conversation as resolved.
Show resolved Hide resolved
/// [`ObjectModel::ref_to_object_start`] should return this address for a given object.
///
/// ### In-object Address
///
/// As the object reference address may be outside the allocated memory, and calculating the object start address may
/// be complex, MMTk requires a fixed and efficient in-object address for each object. The in-object address should be a constant
/// offset from the object reference address, and should be inside the allocated memory. MMTk requires the conversion
wks marked this conversation as resolved.
Show resolved Hide resolved
/// from the object reference to the in-object address ([`ObjectModel::ref_to_address`]) and from the in-object address
/// to the object reference ([`ObjectModel::address_to_ref`]).
///
/// ### Object header address
///
/// If a binding allows MMTk to use its header bits for object metadata, they need to supply an object header
wks marked this conversation as resolved.
Show resolved Hide resolved
/// address ([`ObjectModel::ref_to_header`]). MMTk will access header bits using this address.
pub trait ObjectModel<VM: VMBinding> {
// Per-object Metadata Spec definitions go here
//
Expand Down Expand Up @@ -439,7 +470,7 @@ pub trait ObjectModel<VM: VMBinding> {
/// Return an address guaranteed to be inside the storage associated
/// with an object. The returned address needs to be deterministic
/// for an given object. For a given object, the returned address
/// should be a constant offset from the object reference address.
/// *must* be a constant offset from the object reference address.
///
/// Note that MMTk may forge an arbitrary address
/// directly into a potential object reference, and call this method on the 'object reference'.
Expand All @@ -453,12 +484,17 @@ pub trait ObjectModel<VM: VMBinding> {
fn ref_to_address(object: ObjectReference) -> Address;

/// Return an object for a given address returned by `ref_to_address()`.
/// This does exactly the opposite of `ref_to_address()`. The argument `addr` has
/// to be an address that is previously returned from `ref_to_address()`. Invoking this method
/// with an unexpected address is undefined behavior.
/// This does exactly the opposite of `ref_to_address()`. The returned
/// object reference address *must* be a constant offset from the given address.
///
/// Note that MMTk may forge an address and call this method with the address.
/// Thus the returned object reference may not always be valid. The binding
/// should simply apply a constant offset the given address, and return
/// it as an object reference, and should not assume the returned object reference
/// is always valid. MMTk is reponsible for using the returned object reference.
///
/// Arguments:
/// * `addr`: An address that is returned from `ref_to_address()`
/// * `addr`: An in-object address.
fn address_to_ref(addr: Address) -> ObjectReference;

/// Dump debugging information for an object.
Expand Down
10 changes: 8 additions & 2 deletions src/vm/tests/mock_tests/mock_test_is_in_mmtk_spaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ pub fn max() {
SINGLE_OBJECT.with_fixture(|_fixture| {
assert!(
!memory_manager::is_in_mmtk_spaces::<MockVM>(
ObjectReference::from_raw_address(Address::MAX).unwrap()
ObjectReference::from_raw_address(
Address::MAX.align_down(crate::util::constants::BYTES_IN_ADDRESS)
)
.unwrap()
),
"Address::MAX should not be in any MMTk spaces."
);
Expand Down Expand Up @@ -113,7 +116,10 @@ pub fn negative_offsets() {
// It's just a smoke test. It is hard to predict if the addr is still in any space,
// but it must not crash.
let _ = memory_manager::is_in_mmtk_spaces::<MockVM>(
ObjectReference::from_raw_address(addr).unwrap(),
ObjectReference::from_raw_address(
addr.align_down(crate::util::constants::BYTES_IN_ADDRESS),
)
.unwrap(),
);
}
});
Expand Down
Loading