-
Notifications
You must be signed in to change notification settings - Fork 75
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
Edge trait #606
Conversation
5003b66
to
af2575f
Compare
cd0618f
to
c540854
Compare
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>>(...) { ... }
There was a problem hiding this 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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
vmbindings/dummyvm/src/edges.rs
Outdated
|
||
/// This edge presents the object reference itself to mmtk-core. | ||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
pub struct ValueEdge { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
I suggest we also get a review from @wenyuzhao on this PR. |
vmbindings/dummyvm/src/edges.rs
Outdated
} | ||
|
||
fn store(&self, _object: ObjectReference) { | ||
// No-op. Value edges are immutable. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
More comments on why bindings should use `SimpleEdge` instead of `Address`. Add `#[repr(transparent)]` to `SimpleEdge`.
I updated the PR.
|
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 invmbindings/dummyvm/src/edges.rs
. There is a test casevmbindings/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