Skip to content

Edge trait #606

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

Merged
merged 4 commits into from
Aug 29, 2022
Merged

Edge trait #606

merged 4 commits into from
Aug 29, 2022

Conversation

wks
Copy link
Collaborator

@wks wks commented Jun 2, 2022

We make the representation of edges VM-specific. The MMTk core only
depends on the abstract Edge trait. This allows VM bindings to custom
the behaviour of loading/storing of edges, and support features like
compressed OOPs.

Some example Edge implementations are in vmbindings/dummyvm/src/edges.rs. There is a test case vmbindings/dummyvm/src/tests/edges_test.rs to show what to expect.

This PR involves API change. Migration guide is in the commit message.

Binding PRs:

Closes: #573

@wks wks mentioned this pull request Jun 2, 2022
@wks wks force-pushed the edge-shape branch 3 times, most recently from 5003b66 to af2575f Compare August 5, 2022 09:56
@wks wks force-pushed the edge-shape branch 2 times, most recently from cd0618f to c540854 Compare August 25, 2022 07:53
We make the representation of edges VM-specific.  The MMTk core only
depends on the abstract Edge trait.  This allows VM bindings to custom
the behaviour of loading/storing of edges, and support features like
compressed OOPs.

Binding migration advices:

-   The type of edges is now defined by the VM, and is no longer limited
    to Address.  To transition from the old API, the VM can start by
    defining its vm-specific edge type as an alias of Address, like:

        type OpenJDKEdge = Address;

    and gradually transition to mmtk::vm::edge_shape::SimpleEdge, or
    define a more appropriate type for the VM.

-   The VMBinding trait now requires a VMEdge member type.  Implement it
    with the VM-specific edge type, like:

        type VMEdge = OpenJDKEdge;

-   EdgeVisitor and RootsWorkFactory now become EdgeVisitor<T> and
    RootsWorkFactory<T>.  If an old API function passes an EdgeVisitor
    or RootsWorkFactory object, add the VM's edge type as T.  For
    example, change

        fn scan_object<EV: EdgeVisitor>(...) { ... }

    to

        fn scan_object<EV: EdgeVisitor<OpenJDKEdge>>(...) { ... }
@wks wks marked this pull request as ready for review August 25, 2022 15:42
@wks wks requested a review from qinsoon August 25, 2022 15:50
Copy link
Member

@qinsoon qinsoon left a comment

Choose a reason for hiding this comment

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

LGTM

/// For backword compatibility.
/// We let Address implement Edge so existing bindings that use `Address` to represent an edge can
/// continue to work.
impl Edge for Address {
Copy link
Member

Choose a reason for hiding this comment

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

Maybe it is worth documenting that we suggest using SimpleEdge instead of Address, and why.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes. I'll add more comments about why we should move away from using Address directly as an edge.


/// This edge presents the object reference itself to mmtk-core.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ValueEdge {
Copy link
Member

Choose a reason for hiding this comment

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

Will we use a type like this when a binding wants to do node enqueuing? If so, we probably should just call it Node or at least in the comment, state this is also known as node.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I thought this ValueEdge could help node-enqueuing, but I later found it doesn't help. I already removed all comments that mentioned "edge enqueuing" in this PR, but I want to keep this example, just to demonstrate that the developers can think out of the box when implementing Edge. I'll add some comments to clarify the purpose of this example.

@qinsoon
Copy link
Member

qinsoon commented Aug 25, 2022

I suggest we also get a review from @wenyuzhao on this PR.

@qinsoon qinsoon requested a review from wenyuzhao August 25, 2022 23:19
}

fn store(&self, _object: ObjectReference) {
// No-op. Value edges are immutable.
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we can call this an edge. We call store() to update the edge, but this implementation cannot update. The store() impl should be at least unreachable!() or panic!(), as the impl cannot do what it is asked. However, that will cause MMTk crash, as we will call store. I think we should just remove this type -- it is confusing.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OK. I think I'll just remove the ValueEdge type because it is confusing.

@@ -45,6 +46,9 @@ where
type VMActivePlan: ActivePlan<Self>;
type VMReferenceGlue: ReferenceGlue<Self>;

/// The type of edges in this VM.
type VMEdge: edge_shape::Edge;
Copy link
Member

@wenyuzhao wenyuzhao Aug 26, 2022

Choose a reason for hiding this comment

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

Is it possible to do a runtime switch between different VMEdges? For example, OpenJDK may need to support compressed pointers as an optional run-time feature, and cannot be activated at build-time.

Copy link
Member

Choose a reason for hiding this comment

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

Also probably this can be moved to VMObjectModel?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If the "feature" is a Cargo feature, we can define it like this:

enum OpenJDKEdge {
    Uncompressed(*mut AtomicUsize),
    #[cfg(features = "compressed_oop")]
    Compressed(*mut AtomicU32),
}

But if we want it enabled/disabled by a command-line flag, the easiest way (may affect performance, but need to be confirmed with tests) is simply not use the OpenJDKEdge::Compressed case. As an optimisation, maybe we can parameterise OpenJDK to OpenJDK<EdgeType> so that depending on the command line options, we can instantiate MMTk with different (parameterised) VMBinding implementation.

Copy link
Member

@qinsoon qinsoon Aug 26, 2022

Choose a reason for hiding this comment

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

Maybe we could check the performance impact of having enum OpenJDKEdge (but never using the other case). We do hope that is minimal.

Copy link
Member

Choose a reason for hiding this comment

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

Also probably this can be moved to VMObjectModel?

I don't think ObjectModel is the right place to put it. I think it is fine to put the type in VMBinding (as it is now). If we plan to move it, I think Scanning fits well. All related changes are in the Scanning trait. This is an indication that the type is related with Scanning.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Also probably this can be moved to VMObjectModel?

In theory, we can. But then instead of VM::VMEdge, we will need to write <<VM as VMBinding>::VMObjectModel as ObjectModel<VM>>::VMEdge everywhere.

A refactoring may be possible to let VMBinding require VMObjectModel, instead of including a VMObjectModel as a member. I have seen similar problems when I tried to refactor ReferenceProcessor. I tried to add a function in VMCollection that depends on a type in VMReferenceGlue, and I have to navigate from VMCollection to VMBinding, and from there to VMReferenceGlue. Sometimes the compiler is not smart enough to figure out the VM type passed to the function to VMCollection is the same VM as the VM in VMReferenceGlue<VM>.

I'll elaborate the refactoring in a different issue.

Copy link
Member

@wenyuzhao wenyuzhao Aug 26, 2022

Choose a reason for hiding this comment

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

Using enum OpenJDKEdge will (1) introduce an extra branch for each edge load and store (2) For a mark-queue Vec<Edge>, there will be one word (100%) overhead for each entry. Probably I'll parameterize OpenJDK.

Copy link
Member

Choose a reason for hiding this comment

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

In theory, we can. But then instead of VM::VMEdge, we will need to write <::VMObjectModel as ObjectModel>::VMEdge everywhere.

I see. Yes I agree it should stay in VMBinding.

Copy link
Member

Choose a reason for hiding this comment

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

I think I don't really need another edge type for compressed pointers. The size of an edge address is always one word, no matter whether it's a *mut AtomicU32 or *mut AtomicUsize . The only thing I need to replace is the handler of the load/store operations.

Copy link
Collaborator Author

@wks wks Aug 26, 2022

Choose a reason for hiding this comment

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

If space overhead is a problem, the OpenJDK binding can implement Edge with a tagged pointer. It saves space, but still needs a test on bits.

Other bindings may not be able to use this trick. Ruby already uses tagged pointers (Sorry. It doesn't matter whether the VM uses tagged pointers. It's about the binding differentiating kinds of edges using tag bits.), and Julia has offsetted edges where the offset has to be recorded.

wks added 2 commits August 26, 2022 14:08
More comments on why bindings should use `SimpleEdge` instead of
`Address`.

Add `#[repr(transparent)]` to `SimpleEdge`.
@wks
Copy link
Collaborator Author

wks commented Aug 26, 2022

I updated the PR.

  1. I replaced the ValueEdge example with TaggedEdge, because we do have some VMs using tagged pointers.
  2. I added comments about why we should prefer SimpleEdge over Address.
  3. I added #[repr(transparent)] to SimpleEdge.

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.

Generalise edges
3 participants