Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
7 changes: 5 additions & 2 deletions src/plan/lxr/gc_work/mature_sweeping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,12 @@ impl<VM: VMBinding> SweepDeadCycles<VM> {
let c = self.rc.count_by_address(cur_cursor);
if c != 0 {
// Safety: cur_cursor is either a valid object reference, or a straddle line
let o = unsafe { ObjectReference::from_raw_address_unchecked(cur_cursor) };
// But we don't know o is valid now
let o = unsafe { rc::ObjectEnvelope::from_start::<VM>(cur_cursor) }
.to_object_reference();
// TODO: This check is unsound. But if we only use side metadata, it is fine.
if !immix_space.is_marked(o) {
if Line::is_aligned(o.to_raw_address()) {
if Line::is_aligned(cur_cursor) {
if c == 1 && self.rc.object_is_in_straddle_line_no_rc_check(o) {
// this is a straddle line, skip
continue;
Expand Down
6 changes: 6 additions & 0 deletions src/plan/lxr/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,12 @@ impl<VM: VMBinding> LXR<VM> {
self.immix_space.in_space(o) && Block::in_defrag_block(o)
}

/// Whether LXR maintains a reference count for `o`. Only the Immix and large object spaces are reference counted.
#[allow(dead_code)]
pub fn is_rc_object(&self, o: ObjectReference) -> bool {
self.immix_space.in_space(o) || self.common.los.in_space(o)
}

pub fn address_in_defrag(&self, a: Address) -> bool {
self.immix_space.address_in_space(a) && Block::address_in_defrag_block(a)
}
Expand Down
12 changes: 4 additions & 8 deletions src/policy/immix/immixspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::util::metadata::vo_bit;
use crate::util::metadata::{self, MetadataSpec};
use crate::util::object_enum::ObjectEnumerator;
use crate::util::object_forwarding;
use crate::util::rc::RefCountHelper;
use crate::util::rc::{ObjectEnvelope, RefCountHelper};
use crate::util::{copy::*, epilogue, object_enum};
use crate::util::{Address, ObjectReference};
use crate::vm::*;
Expand Down Expand Up @@ -817,12 +817,8 @@ impl<VM: VMBinding> ImmixSpace<VM> {
object: ObjectReference,
) -> ObjectReference {
if self.attempt_mark(object) {
let addr = object.to_raw_address().as_usize();
let straddle = if (addr & 0b11110000) == 0 {
self.rc.object_is_in_straddle_line_no_rc_check(object)
} else {
false
};
let straddle = Line::is_aligned(ObjectEnvelope::of(object).start::<VM>())
&& self.rc.object_is_in_straddle_line_no_rc_check(object);
if !straddle {
queue.enqueue(object);
}
Expand Down Expand Up @@ -1057,7 +1053,7 @@ impl<VM: VMBinding> ImmixSpace<VM> {
)
.expect("to-space overflow");
// Transfer RC count
if new.get_size::<VM>() > Line::BYTES {
if ObjectEnvelope::needs_straddle_marks::<VM>(new.get_size::<VM>()) {
self.rc.mark_straddle_object(new);
}
self.rc.set(new, self.rc.count(object));
Expand Down
21 changes: 17 additions & 4 deletions src/util/alloc/immix_allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::util::alloc::allocator::get_maximum_aligned_size;
use crate::util::alloc::Allocator;
use crate::util::linear_scan::Region;
use crate::util::opaque_pointer::VMThread;
use crate::util::rc::ObjectEnvelope;
use crate::util::rust_util::unlikely;
use crate::util::Address;
use crate::vm::*;
Expand Down Expand Up @@ -191,6 +192,16 @@ impl<VM: VMBinding> ImmixAllocator<VM> {
self.space
}

/// The first address in `block` an object may be allocated at.
fn block_usable_start(&self, block: Block) -> Address {
if self.space.rc_enabled {
// For LXR, we need to reserve the leading slack bytes of a block for the envelope reservation.
block.start() + ObjectEnvelope::slack::<VM>()
} else {
block.start()
}
}

/// Large-object (larger than a line) bump allocation.
fn overflow_alloc(&mut self, size: usize, align: usize, offset: usize) -> Address {
trace!("{:?}: overflow_alloc", self.tls);
Expand Down Expand Up @@ -242,7 +253,9 @@ impl<VM: VMBinding> ImmixAllocator<VM> {
self.immix_space().get_next_available_lines(self.copy, line)
{
// Find recyclable lines. Update the bump allocation cursor and limit.
self.bump_pointer.cursor = start_line.start();
self.bump_pointer.cursor = start_line
.start()
.max(self.block_usable_start(line.block()));
Comment on lines +256 to +258

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggested skipping the first slack() bytes of each block so that the envelope never crosses block boundaries. But after a second thought, we may think of alternatives because

  • If the slack() is large, we may end up wasting too much memory.
  • The upper bound may be infinite, such as the SableVM layout.

In the latter case, we may give MMTk core an option to opt out the optimization of using constants to estimate the object start, but instead always use ObjectModel::ref_to_object_start. One way to do it is to encapsulate related computation to a dedicated type, such as LineMarkTable or LineRCTable or others. Operations include:

  • Find the first line to mark.
  • Find the address to store the RC.
  • How many bytes to skip in the beginning of each Block?
  • Should we skip the first line of each hole when allocating?
  • Should we skip the last line occupied by an object when marking lines?

MMTk core may provide different strategies depending on whether the slack is finite and whether the VM is able to identify small objects (envelope < Line::BYTES) at all.

We should discuss this in tomorrow's meeting and ask Steve for his opinion.

self.bump_pointer.limit = end_line.start();
trace!(
"{:?}: acquire_recyclable_lines -> {:?} [{:?}, {:?}) {:?}",
Expand All @@ -257,7 +270,7 @@ impl<VM: VMBinding> ImmixAllocator<VM> {
self.bump_pointer.limit - self.bump_pointer.cursor,
);
debug_assert!(
align_allocation_no_fill::<VM>(self.bump_pointer.cursor, align, offset) + size
align_allocation_no_fill::<VM>(start_line.start(), align, offset) + size
<= self.bump_pointer.limit
);
let block = line.block();
Expand Down Expand Up @@ -334,10 +347,10 @@ impl<VM: VMBinding> ImmixAllocator<VM> {
}
}
if self.request_for_large {
self.large_bump_pointer.cursor = block.start();
self.large_bump_pointer.cursor = self.block_usable_start(block);
self.large_bump_pointer.limit = block.end();
} else {
self.bump_pointer.cursor = block.start();
self.bump_pointer.cursor = self.block_usable_start(block);
self.bump_pointer.limit = block.end();
}
self.alloc(size, align, offset)
Expand Down
25 changes: 22 additions & 3 deletions src/util/alloc/large_object_allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::policy::largeobjectspace::LargeObjectSpace;
use crate::policy::space::Space;
use crate::util::alloc::{allocator, Allocator};
use crate::util::opaque_pointer::*;
use crate::util::rc::ObjectEnvelope;
use crate::util::Address;
use crate::vm::VMBinding;

Expand Down Expand Up @@ -49,7 +50,8 @@ impl<VM: VMBinding> Allocator<VM> for LargeObjectAllocator<VM> {
}

fn alloc_slow_once(&mut self, size: usize, align: usize, _offset: usize) -> Address {
let maxbytes = allocator::get_maximum_aligned_size::<VM>(size, align);
let reserved = self.envelope_reservation();
let maxbytes = allocator::get_maximum_aligned_size::<VM>(size, align) + reserved;
let pages = crate::util::conversions::bytes_to_pages_up(maxbytes);

if self.handle_obvious_oom_request(
Expand All @@ -59,12 +61,29 @@ impl<VM: VMBinding> Allocator<VM> for LargeObjectAllocator<VM> {
return Address::ZERO;
}

self.space
.allocate_pages(self.tls, pages, self.get_context().get_alloc_options())
let cell =
self.space
.allocate_pages(self.tls, pages, self.get_context().get_alloc_options());
// A zero cell means the VM is out of memory; leave it zero so the caller still sees it.
if cell.is_zero() {
cell
} else {
cell + reserved
}
}
}

impl<VM: VMBinding> LargeObjectAllocator<VM> {
/// Bytes left unused at the front of every cell, so that an object's *envelope* start can
/// never precede the cell it belongs to. See [`ObjectEnvelope`].
fn envelope_reservation(&self) -> usize {
if self.space.rc_enabled {
ObjectEnvelope::slack::<VM>()
} else {
0
}
}

pub(crate) fn new(
tls: VMThread,
space: &'static LargeObjectSpace<VM>,
Expand Down
Loading
Loading