Skip to content

Commit

Permalink
Introduce a constant IN_OBJECT_ADDRESS_OFFSET
Browse files Browse the repository at this point in the history
  • Loading branch information
qinsoon committed Jul 10, 2024
1 parent 3ea862a commit 6ce0d2c
Show file tree
Hide file tree
Showing 10 changed files with 78 additions and 91 deletions.
4 changes: 1 addition & 3 deletions benches/mock_bench/sft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ use criterion::Criterion;
use mmtk::memory_manager;
use mmtk::util::test_util::fixtures::*;
use mmtk::util::test_util::mock_vm::*;
use mmtk::vm::ObjectModel;
use mmtk::vm::VMBinding;
use mmtk::AllocationSemantics;

pub fn bench(c: &mut Criterion) {
let mut fixture = MutatorFixture::create();
let addr = memory_manager::alloc(&mut fixture.mutator, 8, 8, 0, AllocationSemantics::Default);
let obj = <MockVM as VMBinding>::VMObjectModel::address_to_ref(addr);
let obj = MockVM::object_start_to_ref(addr);

c.bench_function("sft read", |b| {
b.iter(|| memory_manager::is_in_mmtk_spaces::<MockVM>(black_box(obj)))
Expand Down
5 changes: 2 additions & 3 deletions docs/dummyvm/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,6 @@ pub extern "C" fn mmtk_get_malloc_bytes() -> usize {
#[cfg(test)]
mod tests {
use super::*;
use crate::mmtk::vm::ObjectModel;
use std::ffi::CString;

#[test]
Expand Down Expand Up @@ -293,8 +292,8 @@ mod tests {
let addr = mmtk_alloc(mutator, 16, 8, 0, mmtk::AllocationSemantics::Default);
assert!(!addr.is_zero());

// Turn the allocation address into the object reference
let obj = crate::object_model::VMObjectModel::address_to_ref(addr);
// Turn the allocation address into the object reference.
let obj = DummyVM::object_start_to_ref(addr);

// Post allocation
mmtk_post_alloc(mutator, obj, 16, mmtk::AllocationSemantics::Default);
Expand Down
7 changes: 7 additions & 0 deletions docs/dummyvm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ impl VMBinding for DummyVM {
const MAX_ALIGNMENT: usize = 1 << 6;
}

impl DummyVM {
pub fn object_start_to_ref(start: Address) -> ObjectReference {
// Safety: start is the allocation result, and it should not be zero with an offset.
unsafe { ObjectReference::from_raw_address_unchecked(start + crate::object_model::OBJECT_REF_OFFSET) }
}
}

pub static SINGLETON: OnceLock<Box<MMTK<DummyVM>>> = OnceLock::new();

fn mmtk() -> &'static MMTK<DummyVM> {
Expand Down
26 changes: 9 additions & 17 deletions docs/dummyvm/src/object_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ use mmtk::vm::*;

pub struct VMObjectModel {}

// This is the offset from the allocation result to the object reference for the object.
// The binding can set this to a different value if the ObjectReference in the VM has an offset from the allocation starting address.
// Many methods like `address_to_ref` and `ref_to_address` use this constant.
// For bindings that this offset is not a constant, you can implement the calculation in the methods, and
// remove this constant.
/// This is the offset from the allocation result to the object reference for the object.
/// For bindings that this offset is not a constant, you can implement the calculation in the method `ref_to_object_start``, and
/// remove this constant.
pub const OBJECT_REF_OFFSET: usize = 0;

/// This is the offset from the object reference to an in-object address. The binding needs
/// to guarantee the in-object address is inside the storage associated with the object.
/// It has to be a constant offset. See `ObjectModel::IN_OBJECT_ADDRESS_OFFSET`.
pub const IN_OBJECT_ADDRESS_OFFSET: isize = 0;

// This is the offset from the object reference to the object header.
// This value is used in `ref_to_header` where MMTk loads header metadata from.
pub const OBJECT_HEADER_OFFSET: usize = 0;
Expand Down Expand Up @@ -83,18 +86,7 @@ impl ObjectModel<DummyVM> for VMObjectModel {
object.to_raw_address().sub(OBJECT_HEADER_OFFSET)
}

fn ref_to_address(object: ObjectReference) -> Address {
// This method should return an address that is within the allocation.
// Using `ref_to_object_start` is always correct here.
// However, a binding may optimize this method to make it more efficient.
Self::ref_to_object_start(object)
}

fn address_to_ref(addr: Address) -> ObjectReference {
// This is the reverse operation of `ref_to_address`.
// If the implementation of `ref_to_address` is changed, this implementation needs to be changed accordingly.
unsafe { ObjectReference::from_raw_address_unchecked(addr.add(OBJECT_REF_OFFSET)) }
}
const IN_OBJECT_ADDRESS_OFFSET: isize = IN_OBJECT_ADDRESS_OFFSET;

fn dump_object(_object: ObjectReference) {
unimplemented!()
Expand Down
16 changes: 16 additions & 0 deletions docs/userguide/src/migration/prefix.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@ Notes for the mmtk-core developers:

<!-- Insert new versions here -->

## 0.27.0

### Introduce `ObjectModel::IN_OBJECT_ADDRESS_OFFSET`

```admonish tldr
We used to have `ObjectModel::ref_to_address` and `ObjectModel::address_to_ref`, and require
the object reference and the in-object address to have a constant offset. Now, the two methods
are removed, and replaced with a constant `ObjectModel::IN_OBJECT_ADDRESS_OFFSET`.
```

API changes:
* trait `ObjectModel`
- The methods `ref_to_address` and `address_to_ref` are removed.
- Users are required to specify `IN_OBJECT_ADDRESS_OFFSET` instead, which is the offset from the object
reference to the in-object address (which was returned in the old `ref_to_address()`).

## 0.26.0

### Rename "edge" to "slot"
Expand Down
32 changes: 19 additions & 13 deletions src/util/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@ impl Address {
Address(self.0 - size)
}

/// Apply an signed offset to the address.
pub const fn offset(self, offset: isize) -> Address {
Address((self.0 as isize).wrapping_add(offset) as usize)
}

/// Bitwise 'and' with a mask.
pub const fn and(self, mask: usize) -> usize {
self.0 & mask
Expand Down Expand Up @@ -502,7 +507,7 @@ impl ObjectReference {
///
/// MMTk should not make any assumption on the actual location of the address with the object reference.
/// MMTk should not assume the address returned by this method is in our allocation. For the purposes of
/// setting object metadata, MMTk should use [`crate::vm::ObjectModel::ref_to_address()`] or [`crate::vm::ObjectModel::ref_to_header()`].
/// setting object metadata, MMTk should use [`crate::util::ObjectReference::to_address`] or [`crate::util::ObjectReference::to_header`].
pub fn to_raw_address(self) -> Address {
Address(self.0.get())
}
Expand All @@ -512,8 +517,8 @@ impl ObjectReference {
///
/// If `addr` is 0, the result is `None`.
///
/// 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.
/// MMTk should not assume an arbitrary address can be turned into an object reference. MMTk can use [`crate::util::ObjectReference::from_address`]
/// to turn addresses that are from [`crate::util::ObjectReference::to_address`] back to object.
pub fn from_raw_address(addr: Address) -> Option<ObjectReference> {
debug_assert!(
addr.is_aligned_to(Self::ALIGNMENT),
Expand All @@ -540,12 +545,11 @@ impl ObjectReference {
}

/// Get the in-heap address from an object reference. This method is used by MMTk to get an in-heap address
/// for an object reference. This method is syntactic sugar for [`crate::vm::ObjectModel::ref_to_address`]. See the
/// comments on [`crate::vm::ObjectModel::ref_to_address`].
/// for an object reference.
pub fn to_address<VM: VMBinding>(self) -> Address {
use crate::vm::ObjectModel;
let to_address = VM::VMObjectModel::ref_to_address(self);
debug_assert!(!VM::VMObjectModel::UNIFIED_OBJECT_REFERENCE_ADDRESS || to_address == self.to_raw_address(), "The binding claims unified object reference address, but for object reference {}, ref_to_address() returns {}", self, to_address);
let to_address = Address(self.0.get()).offset(VM::VMObjectModel::IN_OBJECT_ADDRESS_OFFSET);
debug_assert!(!VM::VMObjectModel::UNIFIED_OBJECT_REFERENCE_ADDRESS || to_address == self.to_raw_address(), "The binding claims unified object reference address, but for object reference {}, in-object addr is {}", self, to_address);
to_address
}

Expand All @@ -563,17 +567,19 @@ impl ObjectReference {
pub fn to_object_start<VM: VMBinding>(self) -> Address {
use crate::vm::ObjectModel;
let object_start = VM::VMObjectModel::ref_to_object_start(self);
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_address() returns {}", self, object_start);
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);
object_start
}

/// Get the object reference from an address that is returned from [`crate::util::address::ObjectReference::to_address`]
/// or [`crate::vm::ObjectModel::ref_to_address`]. This method is syntactic sugar for [`crate::vm::ObjectModel::address_to_ref`].
/// See the comments on [`crate::vm::ObjectModel::address_to_ref`].
/// Get the object reference from an address that is returned from [`crate::util::address::ObjectReference::to_address`].
pub fn from_address<VM: VMBinding>(addr: Address) -> 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);
let obj = unsafe {
ObjectReference::from_raw_address_unchecked(
addr.offset(-VM::VMObjectModel::IN_OBJECT_ADDRESS_OFFSET),
)
};
debug_assert!(!VM::VMObjectModel::UNIFIED_OBJECT_REFERENCE_ADDRESS || addr == obj.to_raw_address(), "The binding claims unified object reference address, but for address {}, the object reference is {}", addr, obj);
debug_assert!(
obj.to_raw_address().is_aligned_to(Self::ALIGNMENT),
"ObjectReference is required to be word aligned"
Expand Down
8 changes: 3 additions & 5 deletions src/util/test_util/fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,6 @@ pub struct SingleObject {

impl FixtureContent for SingleObject {
fn create() -> Self {
use crate::vm::object_model::ObjectModel;
let mut mutator = MutatorFixture::create();

// A relatively small object, typical for Ruby.
Expand All @@ -232,7 +231,7 @@ impl FixtureContent for SingleObject {
let addr = memory_manager::alloc(&mut mutator.mutator, size, 8, 0, semantics);
assert!(!addr.is_zero());

let objref = MockVM::address_to_ref(addr);
let objref = MockVM::object_start_to_ref(addr);
memory_manager::post_alloc(&mut mutator.mutator, objref, size, semantics);

SingleObject { objref, mutator }
Expand All @@ -257,7 +256,6 @@ pub struct TwoObjects {

impl FixtureContent for TwoObjects {
fn create() -> Self {
use crate::vm::object_model::ObjectModel;
let mut mutator = MutatorFixture::create();

let size = 128;
Expand All @@ -266,13 +264,13 @@ impl FixtureContent for TwoObjects {
let addr1 = memory_manager::alloc(&mut mutator.mutator, size, 8, 0, semantics);
assert!(!addr1.is_zero());

let objref1 = MockVM::address_to_ref(addr1);
let objref1 = MockVM::object_start_to_ref(addr1);
memory_manager::post_alloc(&mut mutator.mutator, objref1, size, semantics);

let addr2 = memory_manager::alloc(&mut mutator.mutator, size, 8, 0, semantics);
assert!(!addr2.is_zero());

let objref2 = MockVM::address_to_ref(addr2);
let objref2 = MockVM::object_start_to_ref(addr2);
memory_manager::post_alloc(&mut mutator.mutator, objref2, size, semantics);

TwoObjects {
Expand Down
23 changes: 8 additions & 15 deletions src/util/test_util/mock_vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,6 @@ pub struct MockVM {
MockMethod<(ObjectReference, Address), ObjectReference>,
pub ref_to_object_start: MockMethod<ObjectReference, Address>,
pub ref_to_header: MockMethod<ObjectReference, Address>,
pub ref_to_address: MockMethod<ObjectReference, Address>,
pub address_to_ref: MockMethod<Address, ObjectReference>,
pub dump_object: MockMethod<ObjectReference, ()>,
// reference glue
pub weakref_clear_referent: MockMethod<ObjectReference, ()>,
Expand Down Expand Up @@ -304,12 +302,6 @@ impl Default for MockVM {
object.to_raw_address().sub(DEFAULT_OBJECT_REF_OFFSET)
})),
ref_to_header: MockMethod::new_fixed(Box::new(|object| object.to_raw_address())),
ref_to_address: MockMethod::new_fixed(Box::new(|object| {
object.to_raw_address().sub(DEFAULT_OBJECT_REF_OFFSET)
})),
address_to_ref: MockMethod::new_fixed(Box::new(|addr| {
ObjectReference::from_raw_address(addr.add(DEFAULT_OBJECT_REF_OFFSET)).unwrap()
})),
dump_object: MockMethod::new_unimplemented(),

weakref_clear_referent: MockMethod::new_unimplemented(),
Expand Down Expand Up @@ -531,13 +523,8 @@ impl crate::vm::ObjectModel<MockVM> for MockVM {
mock!(ref_to_header(object))
}

fn ref_to_address(object: ObjectReference) -> Address {
mock!(ref_to_address(object))
}

fn address_to_ref(addr: Address) -> ObjectReference {
mock!(address_to_ref(addr))
}
// TODO: This is not mocked. We need a way to deal with it.
const IN_OBJECT_ADDRESS_OFFSET: isize = -(DEFAULT_OBJECT_REF_OFFSET as isize);

fn dump_object(object: ObjectReference) {
mock!(dump_object(object))
Expand Down Expand Up @@ -629,3 +616,9 @@ impl crate::vm::Scanning<MockVM> for MockVM {
mock_any!(forward_weak_refs(worker, tracer_context))
}
}

impl MockVM {
pub fn object_start_to_ref(start: Address) -> ObjectReference {
ObjectReference::from_raw_address(start + DEFAULT_OBJECT_REF_OFFSET).unwrap()
}
}
46 changes: 12 additions & 34 deletions src/vm/object_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,8 @@ use crate::vm::VMBinding;
///
/// 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
/// 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`]).
/// offset from the object reference address, and should be inside the allocated memory. MMTk requires the binding to
/// specify the offset from the object reference to the in-object address by [`ObjectModel::IN_OBJECT_ADDRESS_OFFSET`].
///
/// ### Object header address
///
Expand Down Expand Up @@ -433,8 +432,12 @@ pub trait ObjectModel<VM: VMBinding> {
/// mature space for generational plans.
const VM_WORST_CASE_COPY_EXPANSION: f64 = 1.5;

/// If this is true, the binding guarantees that an object reference's raw address is always equal to the return value of the `ref_to_address` method
/// and the return value of the `ref_to_object_start` method. This is a very strong guarantee, but it is also helpful for MMTk to
/// If this is true, the binding guarantees that the object reference's raw address,
/// the in-object address, and the object start is always the same address. To be precise,
/// 1. an object reference's raw address is always equal to the return value of the `ref_to_object_start` method,
/// 2. `IN_OBJECT_ADDRESS_OFFSET` is 0.
///
/// This is a very strong guarantee, but it is also helpful for MMTk to
/// make some assumptions and optimize for this case.
/// If a binding sets this to true, and the related methods return inconsistent results, this is an undefined behavior. MMTk may panic
/// if any assertion catches this error, but may also fail silently.
Expand Down Expand Up @@ -467,35 +470,10 @@ pub trait ObjectModel<VM: VMBinding> {
/// * `object`: The object to be queried.
fn ref_to_header(object: ObjectReference) -> Address;

/// 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
/// *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'.
/// In that case, the argument `object` may not be a valid object reference,
/// and the implementation of this method should not use any object metadata.
///
/// MMTk uses this method more frequently than [`crate::vm::ObjectModel::ref_to_object_start`].
///
/// Arguments:
/// * `object`: The object to be queried.
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 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 in-object address.
fn address_to_ref(addr: Address) -> ObjectReference;
/// The offset from the object reference to an in-object address.
/// The binding needs to guarantee that obj_ref.to_raw_address() + IN_OBJECT_ADDRESS_OFFSET
/// is inside the storage associated with the object.
const IN_OBJECT_ADDRESS_OFFSET: isize;

/// Dump debugging information for an object.
///
Expand Down
2 changes: 1 addition & 1 deletion src/vm/tests/mock_tests/mock_test_vm_layout_default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub fn test_with_vm_layout(layout: Option<VMLayout>) {

// Test allocation
let addr = memory_manager::alloc(&mut fixture.mutator, 8, 8, 0, AllocationSemantics::Default);
let obj = <MockVM as VMBinding>::VMObjectModel::address_to_ref(addr);
let obj = MockVM::object_start_to_ref(addr);
// Test SFT
assert!(memory_manager::is_in_mmtk_spaces::<MockVM>(obj));
// Test mmapper
Expand Down

0 comments on commit 6ce0d2c

Please sign in to comment.