Skip to content
7 changes: 2 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,8 @@ ro_space = []
# TODO: This is not properly implemented yet. We currently use an immortal space instead, and all our spaces have execution permission at the moment.
code_space = []

# metadata
global_alloc_bit = []

# conservative garbage collection support
is_mmtk_object = ["global_alloc_bit"]
# Valid-object bit, useful for conservative garbage collection.
vo_bit = []

# The following two features are useful for using Immix for VMs that do not support moving GC.

Expand Down
52 changes: 2 additions & 50 deletions src/memory_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -570,54 +570,6 @@ pub fn is_live_object(object: ObjectReference) -> bool {
object.is_live()
}

/// Check if `addr` is the address of an object reference to an MMTk object.
///
/// Concretely:
/// 1. Return true if `addr.to_object_reference()` is a valid object reference to an object in any
/// space in MMTk.
/// 2. Also return true if there exists an `objref: ObjectReference` such that
/// - `objref` is a valid object reference to an object in any space in MMTk, and
/// - `lo <= objref.to_address() < hi`, where
/// - `lo = addr.align_down(ALLOC_BIT_REGION_SIZE)` and
/// - `hi = lo + ALLOC_BIT_REGION_SIZE` and
/// - `ALLOC_BIT_REGION_SIZE` is [`crate::util::is_mmtk_object::ALLOC_BIT_REGION_SIZE`].
/// It is the byte granularity of the alloc bit.
/// 3. Return false otherwise. This function never panics.
///
/// Case 2 means **this function is imprecise for misaligned addresses**.
/// This function uses the "alloc bits" side metadata, i.e. a bitmap.
/// For space efficiency, each bit of the bitmap governs a small region of memory.
/// The size of a region is currently defined as the [minimum object size](crate::util::constants::MIN_OBJECT_SIZE),
/// which is currently defined as the [word size](crate::util::constants::BYTES_IN_WORD),
/// which is 4 bytes on 32-bit systems or 8 bytes on 64-bit systems.
/// The alignment of a region is also the region size.
/// If an alloc bit is `1`, the bitmap cannot tell which address within the 4-byte or 8-byte region
/// is the valid object reference.
/// Therefore, if the input `addr` is not properly aligned, but is close to a valid object
/// reference, this function may still return true.
///
/// For the reason above, the VM **must check if `addr` is properly aligned** before calling this
/// function. For most VMs, valid object references are always aligned to the word size, so
/// checking `addr.is_aligned_to(BYTES_IN_WORD)` should usually work. If you are paranoid, you can
/// always check against [`crate::util::is_mmtk_object::ALLOC_BIT_REGION_SIZE`].
///
/// This function is useful for conservative root scanning. The VM can iterate through all words in
/// a stack, filter out zeros, misaligned words, obviously out-of-range words (such as addresses
/// greater than `0x0000_7fff_ffff_ffff` on Linux on x86_64), and use this function to deside if the
/// word is really a reference.
///
/// Note: This function has special behaviors if the VM space (enabled by the `vm_space` feature)
/// is present. See `crate::plan::global::BasePlan::vm_space`.
///
/// Argument:
/// * `addr`: An arbitrary address.
#[cfg(feature = "is_mmtk_object")]
pub fn is_mmtk_object(addr: Address) -> bool {
use crate::mmtk::SFT_MAP;
use crate::policy::sft_map::SFTMap;
SFT_MAP.get_checked(addr).is_mmtk_object(addr)
}

/// Return true if the `object` lies in a region of memory where
/// - only MMTk can allocate into, or
/// - only MMTk's delegated memory allocator (such as a malloc implementation) can allocate into
Expand All @@ -632,8 +584,8 @@ pub fn is_mmtk_object(addr: Address) -> bool {
/// object for the VM in response to `memory_manager::alloc`, this function will return true; but
/// if the VM directly called `malloc` to allocate the object, this function will return false.
///
/// If `is_mmtk_object(object.to_address())` returns true, `is_in_mmtk_spaces(object)` must also
/// return true.
/// If `src::util::metadata::vo_bit::is_valid_mmtk_object(object)` returns true,
/// `is_in_mmtk_spaces(object)` must also return true.
///
/// This function is useful if an object reference in the VM can be either a pointer into the MMTk
/// heap, or a pointer to non-MMTk objects. If the VM has a pre-built boot image that contains
Expand Down
11 changes: 6 additions & 5 deletions src/plan/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -419,12 +419,13 @@ pub struct BasePlan<VM: VMBinding> {
/// A VM space is a space allocated and populated by the VM. Currently it is used by JikesRVM
/// for boot image.
///
/// If VM space is present, it has some special interaction with the
/// `memory_manager::is_mmtk_object` and the `memory_manager::is_in_mmtk_spaces` functions.
/// If VM space is present, it has some special interaction with the VO-bit metadata and the
/// `memory_manager::is_in_mmtk_spaces` functions.
///
/// - The `is_mmtk_object` funciton requires the alloc_bit side metadata to identify objects,
/// but currently we do not require the boot image to provide it, so it will not work if the
/// address argument is in the VM space.
/// - Currently we do not require the boot image to provide a bitmap to be used as the VO-bit
/// metadata, so features that require the VO-bit will not work. Conservative GC will not
/// be able to identify if an address is a valid object reference if it points into the VM
/// space.
///
/// - The `is_in_mmtk_spaces` currently returns `true` if the given object reference is in
/// the VM space.
Expand Down
16 changes: 8 additions & 8 deletions src/plan/markcompact/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ use crate::policy::space::Space;
use crate::scheduler::gc_work::*;
use crate::scheduler::*;
use crate::util::alloc::allocators::AllocatorSelector;
#[cfg(not(feature = "global_alloc_bit"))]
use crate::util::alloc_bit::ALLOC_SIDE_METADATA_SPEC;
use crate::util::copy::CopySemantics;
use crate::util::heap::layout::heap_layout::Mmapper;
use crate::util::heap::layout::heap_layout::VMMap;
use crate::util::heap::HeapMeta;
use crate::util::heap::VMRequest;
use crate::util::metadata::side_metadata::{SideMetadataContext, SideMetadataSanity};
#[cfg(not(feature = "vo_bit"))]
use crate::util::metadata::vo_bit::VO_BIT_SIDE_METADATA_SPEC;
use crate::util::opaque_pointer::*;
use crate::util::options::Options;
use crate::vm::VMBinding;
Expand Down Expand Up @@ -179,15 +179,15 @@ impl<VM: VMBinding> Plan for MarkCompact<VM> {
impl<VM: VMBinding> MarkCompact<VM> {
pub fn new(vm_map: &'static VMMap, mmapper: &'static Mmapper, options: Arc<Options>) -> Self {
let mut heap = HeapMeta::new(&options);
// if global_alloc_bit is enabled, ALLOC_SIDE_METADATA_SPEC will be added to
// if vo_bit is enabled, VO_BIT_SIDE_METADATA_SPEC will be added to
// SideMetadataContext by default, so we don't need to add it here.
#[cfg(feature = "global_alloc_bit")]
#[cfg(feature = "vo_bit")]
let global_metadata_specs = SideMetadataContext::new_global_specs(&[]);
// if global_alloc_bit is NOT enabled,
// we need to add ALLOC_SIDE_METADATA_SPEC to SideMetadataContext here.
#[cfg(not(feature = "global_alloc_bit"))]
// if vo_bit is NOT enabled,
// we need to add VO_BIT_SIDE_METADATA_SPEC to SideMetadataContext here.
#[cfg(not(feature = "vo_bit"))]
let global_metadata_specs =
SideMetadataContext::new_global_specs(&[ALLOC_SIDE_METADATA_SPEC]);
SideMetadataContext::new_global_specs(&[VO_BIT_SIDE_METADATA_SPEC]);

let mc_space = MarkCompactSpace::new(
"mark_compact_space",
Expand Down
16 changes: 8 additions & 8 deletions src/plan/marksweep/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ use crate::policy::mallocspace::MallocSpace;
use crate::policy::space::Space;
use crate::scheduler::*;
use crate::util::alloc::allocators::AllocatorSelector;
#[cfg(not(feature = "global_alloc_bit"))]
use crate::util::alloc_bit::ALLOC_SIDE_METADATA_SPEC;
use crate::util::heap::layout::heap_layout::Mmapper;
use crate::util::heap::layout::heap_layout::VMMap;
use crate::util::heap::HeapMeta;
use crate::util::metadata::side_metadata::{SideMetadataContext, SideMetadataSanity};
#[cfg(not(feature = "vo_bit"))]
use crate::util::metadata::vo_bit::VO_BIT_SIDE_METADATA_SPEC;
use crate::util::options::Options;
use crate::util::VMWorkerThread;
use crate::vm::VMBinding;
Expand Down Expand Up @@ -97,16 +97,16 @@ impl<VM: VMBinding> Plan for MarkSweep<VM> {
impl<VM: VMBinding> MarkSweep<VM> {
pub fn new(vm_map: &'static VMMap, mmapper: &'static Mmapper, options: Arc<Options>) -> Self {
let heap = HeapMeta::new(&options);
// if global_alloc_bit is enabled, ALLOC_SIDE_METADATA_SPEC will be added to
// if vo_bit is enabled, VO_BIT_SIDE_METADATA_SPEC will be added to
// SideMetadataContext by default, so we don't need to add it here.
#[cfg(feature = "global_alloc_bit")]
#[cfg(feature = "vo_bit")]
let global_metadata_specs =
SideMetadataContext::new_global_specs(&[ACTIVE_CHUNK_METADATA_SPEC]);
// if global_alloc_bit is NOT enabled,
// we need to add ALLOC_SIDE_METADATA_SPEC to SideMetadataContext here.
#[cfg(not(feature = "global_alloc_bit"))]
// if vo_bit is NOT enabled,
// we need to add VO_BIT_SIDE_METADATA_SPEC to SideMetadataContext here.
#[cfg(not(feature = "vo_bit"))]
let global_metadata_specs = SideMetadataContext::new_global_specs(&[
ALLOC_SIDE_METADATA_SPEC,
VO_BIT_SIDE_METADATA_SPEC,
ACTIVE_CHUNK_METADATA_SPEC,
]);

Expand Down
24 changes: 12 additions & 12 deletions src/policy/copyspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::policy::space::{CommonSpace, Space};
use crate::scheduler::GCWorker;
use crate::util::copy::*;
use crate::util::heap::layout::heap_layout::{Mmapper, VMMap};
#[cfg(feature = "global_alloc_bit")]
#[cfg(feature = "vo_bit")]
use crate::util::heap::layout::vm_layout_constants::BYTES_IN_CHUNK;
use crate::util::heap::HeapMeta;
use crate::util::heap::VMRequest;
Expand Down Expand Up @@ -46,8 +46,8 @@ impl<VM: VMBinding> SFT for CopySpace<VM> {
}

fn initialize_object_metadata(&self, _object: ObjectReference, _alloc: bool) {
#[cfg(feature = "global_alloc_bit")]
crate::util::alloc_bit::set_alloc_bit(_object);
#[cfg(feature = "vo_bit")]
crate::util::metadata::vo_bit::set_vo_bit(_object);
}

#[inline(always)]
Expand Down Expand Up @@ -182,21 +182,21 @@ impl<VM: VMBinding> CopySpace<VM> {

pub fn release(&self) {
unsafe {
#[cfg(feature = "global_alloc_bit")]
self.reset_alloc_bit();
#[cfg(feature = "vo_bit")]
self.reset_vo_bit();
self.pr.reset();
}
self.common.metadata.reset();
self.from_space.store(false, Ordering::SeqCst);
}

#[cfg(feature = "global_alloc_bit")]
unsafe fn reset_alloc_bit(&self) {
#[cfg(feature = "vo_bit")]
unsafe fn reset_vo_bit(&self) {
let current_chunk = self.pr.get_current_chunk();
if self.common.contiguous {
// If we have allocated something into this space, we need to clear its alloc bit.
// If we have allocated something into this space, we need to clear its VO-bit.
if current_chunk != self.common.start {
crate::util::alloc_bit::bzero_alloc_bit(
crate::util::metadata::vo_bit::bzero_vo_bit(
self.common.start,
current_chunk + BYTES_IN_CHUNK - self.common.start,
);
Expand Down Expand Up @@ -229,10 +229,10 @@ impl<VM: VMBinding> CopySpace<VM> {
// This object is in from space, we will copy. Make sure we have a valid copy semantic.
debug_assert!(semantics.is_some());

#[cfg(feature = "global_alloc_bit")]
#[cfg(feature = "vo_bit")]
debug_assert!(
crate::util::alloc_bit::is_alloced(object),
"{:x}: alloc bit not set",
crate::util::metadata::vo_bit::is_vo_bit_set(object),
"{:x}: VO-bit not set",
object
);

Expand Down
6 changes: 4 additions & 2 deletions src/policy/immix/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,10 @@ impl Block {
/// Deinitalize a block before releasing.
#[inline]
pub fn deinit(&self) {
#[cfg(feature = "global_alloc_bit")]
crate::util::alloc_bit::bzero_alloc_bit(self.start(), Self::BYTES);
#[cfg(feature = "vo_bit")]
unsafe {
crate::util::metadata::vo_bit::bzero_vo_bit(self.start(), Self::BYTES);
}
self.set_state(BlockState::Unallocated);
}

Expand Down
14 changes: 7 additions & 7 deletions src/policy/immix/immixspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ impl<VM: VMBinding> SFT for ImmixSpace<VM> {
true
}
fn initialize_object_metadata(&self, _object: ObjectReference, _alloc: bool) {
#[cfg(feature = "global_alloc_bit")]
crate::util::alloc_bit::set_alloc_bit(_object);
#[cfg(feature = "vo_bit")]
crate::util::metadata::vo_bit::set_vo_bit(_object);
}
#[inline(always)]
fn sft_trace_object(
Expand Down Expand Up @@ -388,10 +388,10 @@ impl<VM: VMBinding> ImmixSpace<VM> {
semantics: CopySemantics,
worker: &mut GCWorker<VM>,
) -> ObjectReference {
#[cfg(feature = "global_alloc_bit")]
#[cfg(feature = "vo_bit")]
debug_assert!(
crate::util::alloc_bit::is_alloced(object),
"{:x}: alloc bit not set",
crate::util::metadata::vo_bit::is_vo_bit_set(object),
"{:x}: VO-bit not set",
object
);
if Block::containing::<VM>(object).is_defrag_source() {
Expand Down Expand Up @@ -482,8 +482,8 @@ impl<VM: VMBinding> ImmixSpace<VM> {
Block::containing::<VM>(object).set_state(BlockState::Marked);
object
} else {
#[cfg(feature = "global_alloc_bit")]
crate::util::alloc_bit::unset_alloc_bit(object);
#[cfg(feature = "vo_bit")]
crate::util::metadata::vo_bit::unset_vo_bit(object);
ForwardingWord::forward_object::<VM>(object, semantics, copy_context)
};
debug_assert_eq!(
Expand Down
10 changes: 5 additions & 5 deletions src/policy/immortalspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ impl<VM: VMBinding> SFT for ImmortalSpace<VM> {
if self.common.needs_log_bit {
VM::VMObjectModel::GLOBAL_LOG_BIT_SPEC.mark_as_unlogged::<VM>(object, Ordering::SeqCst);
}
#[cfg(feature = "global_alloc_bit")]
crate::util::alloc_bit::set_alloc_bit(object);
#[cfg(feature = "vo_bit")]
crate::util::metadata::vo_bit::set_vo_bit(object);
}
#[inline(always)]
fn sft_trace_object(
Expand Down Expand Up @@ -207,10 +207,10 @@ impl<VM: VMBinding> ImmortalSpace<VM> {
queue: &mut Q,
object: ObjectReference,
) -> ObjectReference {
#[cfg(feature = "global_alloc_bit")]
#[cfg(feature = "vo_bit")]
debug_assert!(
crate::util::alloc_bit::is_alloced(object),
"{:x}: alloc bit not set",
crate::util::metadata::vo_bit::is_vo_bit_set(object),
"{:x}: VO-bit not set",
object
);
if ImmortalSpace::<VM>::test_and_mark(object, self.mark_state) {
Expand Down
22 changes: 13 additions & 9 deletions src/policy/largeobjectspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ impl<VM: VMBinding> SFT for LargeObjectSpace<VM> {
VM::VMObjectModel::GLOBAL_LOG_BIT_SPEC.mark_as_unlogged::<VM>(object, Ordering::SeqCst);
}

#[cfg(feature = "global_alloc_bit")]
crate::util::alloc_bit::set_alloc_bit(object);
#[cfg(feature = "vo_bit")]
crate::util::metadata::vo_bit::set_vo_bit(object);
let cell = VM::VMObjectModel::object_start_ref(object);
self.treadmill.add_to_treadmill(cell, alloc);
}
Expand Down Expand Up @@ -203,10 +203,10 @@ impl<VM: VMBinding> LargeObjectSpace<VM> {
queue: &mut Q,
object: ObjectReference,
) -> ObjectReference {
#[cfg(feature = "global_alloc_bit")]
#[cfg(feature = "vo_bit")]
debug_assert!(
crate::util::alloc_bit::is_alloced(object),
"{:x}: alloc bit not set",
crate::util::metadata::vo_bit::is_vo_bit_set(object),
"{:x}: VO-bit not set",
object
);
let nursery_object = self.is_in_nursery(object);
Expand All @@ -231,18 +231,22 @@ impl<VM: VMBinding> LargeObjectSpace<VM> {
// FIXME: borrow checker fighting
// didn't call self.release_multiple_pages
// so the compiler knows I'm borrowing two different fields

// FIXME: Use local metadata instead of VO-bit.
// The `unsafe { cell.to_object_reference() }` below indicates that VO-bit is not what we
// want here. VO-bit is set at ObjectReference, which is different from the cell address.
if sweep_nursery {
for cell in self.treadmill.collect_nursery() {
// println!("- cn {}", cell);
#[cfg(feature = "global_alloc_bit")]
crate::util::alloc_bit::unset_addr_alloc_bit(cell);
#[cfg(feature = "vo_bit")]
crate::util::metadata::vo_bit::unset_vo_bit(unsafe { cell.to_object_reference() });
self.pr.release_pages(get_super_page(cell));
}
} else {
for cell in self.treadmill.collect() {
// println!("- ts {}", cell);
#[cfg(feature = "global_alloc_bit")]
crate::util::alloc_bit::unset_addr_alloc_bit(cell);
#[cfg(feature = "vo_bit")]
crate::util::metadata::vo_bit::unset_vo_bit(unsafe { cell.to_object_reference() });
self.pr.release_pages(get_super_page(cell));
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/policy/lockfreeimmortalspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ impl<VM: VMBinding> SFT for LockFreeImmortalSpace<VM> {
unimplemented!()
}
fn initialize_object_metadata(&self, _object: ObjectReference, _alloc: bool) {
#[cfg(feature = "global_alloc_bit")]
crate::util::alloc_bit::set_alloc_bit(_object);
#[cfg(feature = "vo_bit")]
crate::util::metadata::vo_bit::set_vo_bit(_object);
}
fn sft_trace_object(
&self,
Expand Down
Loading