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

Forwarding pointer is not an object metadata #1030

Open
wks opened this issue Nov 29, 2023 · 4 comments
Open

Forwarding pointer is not an object metadata #1030

wks opened this issue Nov 29, 2023 · 4 comments
Labels
P-normal Priority: Normal.

Comments

@wks
Copy link
Collaborator

wks commented Nov 29, 2023

Forwarding pointers are associated to dead memory instead of live objects.

Currently, forwarding pointers are implemented as an object metadata, namely const LOCAL_FORWARDING_POINTER_SPEC: VMLocalForwardingPointerSpec; in trait ObjectModel. Like other metadata, the forwarding pointer metadata can be defined as on the side. However,

  1. It is not practical to put it on the side. The forwarding pointer metadata is per (dead) object, and the size and granularity will be 'one word per word'. If we define it as on the side, it will consume a huge amount of memory (50%), making it impossible to run any copying GC with on-the-side forwarding pointers.
  2. It does not need to be put on the side. Forwarding pointers never needs to be set for live objects, so there is no need to reserve space for the forwarding pointers of live objects. Once an object is dead, we can use any part of the dead object to store the forwarding pointer.

There is a prior issue about putting all metadata on the side: #362 Forwarding pointer may be an exception. If the forwarding bits are on the side, we can simply use the first word of a dead object to store the forwarding pointer because it doesn't overlap with the forwarding bits which is on the side.

Even if the forwarding bits metadata is defined as in the header, we can make use of object alignment, and the reset of the word occupied by the forwarding bits to save the forwarding pointer.

What should we change?

We need to replace the const LOCAL_FORWARDING_POINTER_SPEC: VMLocalForwardingPointerSpec;

Offset

We can replace it with an offset:

pub trait ObjectModel<VM: VMBinding> {
    const FORWARDING_POINTER_OFFSET: isize = 0;
}

The unit of this offset will be bits, and it will be relative to the address returned by ref_to_header, or whatever is easy to compute.

Method to compute the address

Alternatively, we can let ObjectModel provide a method ref_to_forwarding_pointer:

pub trait ObjectModel<VM: VMBinding> {
    fn ref_to_forwarding_pointer(object: ObjectReference) -> Address {
        unimplemented!()
    }
}

One advantage of ref_to_forwarding_pointer is that it is always computed from the raw object reference. If both ref_to_header() and ref_to_address() needs some non-trivial computation, conditional branches or memory loading, this may outperform them.

Methods to access the forwarding pointer (and forwarding bits?)

Alternatively, we can let the VM binding implement methods for getting and setting the forwarding pointer of a given object, and provide a default implementation that loads/stores the first word relative to the raw address:

pub trait ObjectModel<VM: VMBinding> {
    fn get_forwarding_pointer(object: ObjectReference) -> ObjectReference {
        unsafe { object.to_raw_address().load::<ObjectReference>() }
    }

    fn set_forwarding_pointer(object: ObjectReference, forwarded: ObjectReference) {
        unsafe { object.to_raw_address().store::<ObjectReference>(forwarded) }
    }
}

We can let the binding implement forwarding states, too. In that case, it will be similar to what the PR #975 tries to implement.

pub trait ObjectModel<VM: VMBinding> {
    // ... other methods

    fn get_forwarding_state(object: ObjectReference) -> ForwardingState {
        unimplemented!()
    }

    fn set_forwarding_state(object: ObjectReference, state: ForwardingState) {
        unimplemented()
    }

    fn set_forwarding_state_and_forwarding_pointer(object: ObjectReference, forwarded: ObjectReference) {
        // The forwarding state should be set to `ForwardingState::Forwarded`
        unimplemented()
    }
}
@playXE
Copy link
Contributor

playXE commented Nov 29, 2023

Methods to access the forwarding pointer (and forwarding bits?)

I feel like this might be a good idea for runtimes that need compressed pointers. The only thing stopping me from compressing object header right now is forwarding pointers

@wks
Copy link
Collaborator Author

wks commented Nov 29, 2023

Methods to access the forwarding pointer (and forwarding bits?)

I feel like this might be a good idea for runtimes that need compressed pointers. The only thing stopping me from compressing object header right now is forwarding pointers

@playXE I don't think the forwarding pointer has anything to do with compressed pointers. The forwarding pointer is stored in a dead object. When an object is dead, its fields are irrelevant. So you don't need to worry about a 64-bit forwarding pointer not fitting into a 32-bit compressed pointer field.

@k-sareen
Copy link
Collaborator

k-sareen commented Nov 29, 2023

We have to be careful with terminology. More precisely, we write a forwarding pointer into dead memory after we have already copied the object. So we can destroy the old memory location. Also @playXE I'm not sure I understand why the forwarding pointer matters with compressed references. MMTk only ever sees uncompressed references (since it does slot.load()) so all references from MMTk's point of view are 64-bit.

@udesou udesou added the P-normal Priority: Normal. label Dec 4, 2023
@steveblackburn
Copy link
Contributor

I suspect the problem here is the relationship between:

  1. forwarding status metadata (ie the forwarding bits) and
  2. the forwarding word.

The former is object metadata the latter is not. The former may be located according to the availability of header bits in a specific VM. The latter is normally located in the memory formerly held by the object, but in some GCs (eg ZGC) may be held elsewhere.

The trouble is that we often want to deal with 1. and 2. in the same (atomic) operation. So this issue straddles both concerns.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
P-normal Priority: Normal.
Projects
None yet
Development

No branches or pull requests

5 participants