Skip to content

LXR: introduce ObjectEnvelope and address non-unified obj ref - #1577

Draft
qinsoon wants to merge 4 commits into
mmtk:masterfrom
qinsoon:lxr-envelope-obj-ref
Draft

qinsoon wants to merge 4 commits into
mmtk:masterfrom
qinsoon:lxr-envelope-obj-ref

Conversation

@qinsoon

@qinsoon qinsoon commented Sep 3, 2026

Copy link
Copy Markdown
Member

No description provided.

Comment thread src/util/rc.rs Outdated
/// To avoid the envelope start being outside of the block where the object is in, we reserve the leading 'slack'
/// bytes of each block for the envelope reservation. See `ImmixAllocator::block_usable_start`.
///
/// TODO: Currently both LOS and Immix objects in LXR are using object envelope. But we don't reserve 'slack' for

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is no longer true. LOS also reserves some bytes.

@wks wks left a comment

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 think it is good to use helper types to make the logic of RC-related address calculation clear.

I have some suggestions on the implementation of ObjectEnvelope. I think it is easier to let ObjectEnvelope encapsulate ObjectRefrerence instead of the starting address, and it is clearer to let ObjectEnvelope represent a range instead of just the starting address.

And I think ObjectEnvelope deserves its own module because it is not only relevant to RC or LXR, but also tracing-based Immix, too.

I also have some thoughts of opting out the optimization of "using constants and envelope to estimate the object start" if the VM does not have an UPPER_BOUND (in other words, the UPPER_BOUND is infinite) in the case of, for example, the SableVM layout. We should discuss it with Steve tomorrow.

Comment thread src/vm/object_model.rs Outdated
Comment on lines +494 to +497
// The doc comment above links to `crate::util::rc::ObjectEnvelope`. The `util::rc` module is
// private (not re-exported at the crate root), so that item is unreachable from outside the
// crate even though it is declared `pub`. `cargo doc --document-private-items` (used by GC
// implementers and by `ci-doc.sh`) documents it anyway, so the link does resolve there.

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 think we can move ObjectEnvelop to a dedicated pub(crate) or pub mod, such as util::envelope. It is not only useful for RC, but also useful for tracing-based Immix. In tracing-based Immix, it is still useful for deciding the first line to mark given an object reference.

It could also possibly be used by other places to find the first region an object could possibly overlap with.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

For now we just keep ObjectEnvelope with the rc module -- only rc is using it. If we plan to use ObjectEnvelope in other places, we can move it out of rc.

The document unnecessarily mentions ObjectEnvelope (Claude wrote it). What this constant defines is more general than ObjectEnvelope -- ObjectEnvelope is just the first consumer of this constant. I changed the doc here.

Comment thread src/util/rc.rs Outdated
/// large objects space.
#[repr(transparent)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct ObjectEnvelope(Address);

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.

It is a bit awkward if ObjectEnvelope only represents the starting address of the envelope. Intuitively it should represent an address range, conceptually including both the start and the end.

I suggest we let ObjectEnvelope wrap the ObjectReference itself, while it still provides query functions to find the envelope start and end addresses. In this case, ObjectEnvelope::start() will just return objref - UPPER_BOUND.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I changed to ObjectEnvelope(ObjectReference). However, I still kept a few places where we take size as an argument (including end()). LXR tries to avoid redundantly reading object size, and passes the object size around (e.g. promote and promote_with_size). I kept the same convention for ObjectEnvelope.

Comment thread src/util/rc.rs Outdated
Comment thread src/util/rc.rs
/// Every guard that decides whether to mark, unmark, or assert goes through here, so they
/// cannot disagree: a mark that unmark does not clear is a stray count that outlives its
/// object.
pub fn needs_straddle_marks<VM: VMBinding>(size: usize) -> bool {

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.

This method is highly specific to the Immix algorithm. I think it is more appropriate to move this method to Line or a dedicated LineMarkTable type. It can call Envelope::slack::<VM>() to get the slack value for the VM.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I moved it to RefCountHelper instead. I do want to constrain the changes of this PR to RC only at the moment -- we haven't tested if/how this works outside RC.

Comment thread src/util/rc.rs Outdated
Comment thread src/util/rc.rs Outdated
Comment thread src/util/rc.rs Outdated
Comment thread src/util/rc.rs Outdated
fn straddle_line_range(&self, o: ObjectReference, size: usize) -> (Line, Line) {
let envelope = ObjectEnvelope::of::<VM>(o);
let start_line = envelope.line().next();
let end_line = Line::from_unaligned_address(envelope.end::<VM>(size));

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.

The last overlapping region of an envelope can be computed using Line::from_unaligned_address(envelope.end::<VM>(size) - 1) because envelope.end::<VM>(size) - 1 must be a byte inside the envelope, and the line constructed from Line::from_unaligned_address(...) must contain that byte. This will make the subsequent check if end_line > block_end_line redundant (but we can assert that just to be safe).

We can implement this as Envelope::last_overlapping_region::<VM, R>() for convenience. I think this can be generalized to all non-empty address ranges, but we currently don't have a dedicated "address range" type.

Comment on lines +256 to +258
self.bump_pointer.cursor = start_line
.start()
.max(self.block_usable_start(line.block()));

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.

@qinsoon

qinsoon commented Sep 3, 2026

Copy link
Copy Markdown
Member Author

Wrapping around an ObjectReference sounds fine to me.

And I think ObjectEnvelope deserves its own module because it is not only relevant to RC or LXR, but also tracing-based Immix, too.

On the contrary, I was considering naming ObjectEnvelope as RCObjectEnvelope. It is at the moment only used for RC related metadata (RC_TABLE and RC_STRADDLE_LINES). It is possible that it can be used for other cases -- by that time, this type can be moved to where appropriate.

@wks

wks commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

... I was considering naming ObjectEnvelope as RCObjectEnvelope. It is at the moment only used for RC related metadata (RC_TABLE and RC_STRADDLE_LINES). It is possible that it can be used for other cases -- by that time, this type can be moved to where appropriate.

Well, that's still OK. We can make it specific to LXR, and later generalize it if we find appropriate.

@qinsoon
qinsoon marked this pull request as ready for review September 4, 2026 01:55
@qinsoon

qinsoon commented Sep 4, 2026

Copy link
Copy Markdown
Member Author

mmtk/mmtk-openjdk#378 is testing this PR.

@qinsoon
qinsoon marked this pull request as draft September 4, 2026 03:35
@qinsoon

qinsoon commented Sep 4, 2026

Copy link
Copy Markdown
Member Author

Using ObjectEnvelope like this PR means we mark and access metadata both at an offset address from object reference. Steve clarified that this is not necessary. We can still mark metadata at object reference, and only need to consider 'envelope' when we check RC table occupancy to find holes. I think for now, we just go for the solution in #1576. I turned this PR into a draft.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants