mmtk-core icon indicating copy to clipboard operation
mmtk-core copied to clipboard

New object-scanning API for barriers

Open wks opened this issue 4 months ago • 7 comments

As I mentioned in this Zulip conversation, the current API Scanning::scan_object is flawed because its tls: VMWorkerThread argument prevents it from being used by mutator threads. But both SATB barriers and coalescing RC barriers need to scan objects, and they are executed by mutators. Currently, the lxr branch works around this issue by passing VMWorkerThread(VMThread::UNINITIALIZED) to the VM binding, and it works because the OpenJDK binding ignores that value.

We should introduce another API function in Scanning which differs from the current scan_object and scan_object_and_trace_edges function in the following ways:

  1. It takes a parameter tls: VMMutatorThread instead.
  2. It only needs to visit the values of reference fields, and does not need to update those fields.

And it will be used by write barriers.

It is similar to the old JikesRVM API back in 2006 named enumeratePointers, except I think we only need to enumerate pointer values instead of pointer locations.

wks avatar Aug 22 '25 17:08 wks

We can just change the tls type to VMThread, so both VMWorkerThread and VMMutatorThread could use the API.

tls appears in most of APIs, as for JikesRVM, the binding needs to know the thread local pointer to make an up call to JikesRVM. It doesn't matter if the thread is a VM thread, or a mutator thread -- as long as it is the correct thread local pointer for the thread that is running the code. We differentiated VMWorkerThread and VMMutatorThread just for clarity. If in this case, the scan object API may be called on a mutator thread, we can just generalize the type to VMThread.

qinsoon avatar Aug 22 '25 22:08 qinsoon

I agree with the part of using tls: VMThread.

But what do you think about only visiting the values of reference fields instead of addresses? The write barriers are not supposed to update the fields, but both Scanning::scan_objects and Scanning::scan_objects_and_trace_edges permit updating fields.

wks avatar Aug 23 '25 01:08 wks

But what do you think about only visiting the values of reference fields instead of addresses?

I feel it is not a big issue. It is MMTk core that provides the slot visitor before calling scan_object so it is MMTk core that makes sure it should just visit the fields (and not update the slots). It is not like that the bindings may accidentally update the slots.

qinsoon avatar Aug 23 '25 09:08 qinsoon

But what do you think about only visiting the values of reference fields instead of addresses?

I feel it is not a big issue. It is MMTk core that provides the slot visitor before calling scan_object so it is MMTk core that makes sure it should just visit the fields (and not update the slots). It is not like that the bindings may accidentally update the slots.

The problem is that some VM bindings cannot implement scan_object at all (at least not efficiently). CRuby objects, for example, are backed by C functions of the form

void foo_mark(struct Foo *foo) {
    gc_mark_movable(foo->x);
}
void foo_compact(struct Foo *foo) {
    foo->x = gc_location(foo->x);
}

There is no way to get the slot address. We can make a FakeSlot { slot_value: ObjectReference } that encapsulates the field value in the fake slot so that it can be "loaded". But we do plan to specialize the scanning of some objects (especially arrays, but not third-party types which we don't know) using knowledge about their object layouts, and use proper Slot and MemorySlice implementations to handle their slots. Since we currently have a unique VMBinding::VMSlot per VM binding, having both FakeSlot and a proper RubySlot will require VMBinding::VMSlot to be an enum which is inefficient.

So it should be beneficial to have one more API function that calls back on field values instead of slots (probably with a default implementation that wraps Scanning::scan_object). Let's call it Scanning::scan_object_for_children for now. CRuby, for example can implement Scanning::scan_object_and_trace_edges and Scanning::scan_object_for_children, while OpenJDK can implement them, too, and also Scanning::scan_object.

wks avatar Sep 10 '25 03:09 wks

The problem is that some VM bindings cannot implement scan_object at all (at least not efficiently).

How is this implemented for the Ruby binding right now? This seems not an issue for object scanning for barrier. It is an issue for object scanning in general.

qinsoon avatar Sep 10 '25 03:09 qinsoon

The problem is that some VM bindings cannot implement scan_object at all (at least not efficiently).

How is this implemented for the Ruby binding right now? This seems not an issue for object scanning for barrier. It is an issue for object scanning in general.

CRuby implements Scanning::scan_object_and_trace_edges, but not Scanning::scan_object. It supports StickyImmix, and the barrier is an object-remembering barrier, so the barrier only needs to remember the object. There is no need to scan objects in barriers.

Yes. It is for object scanning in general. STAB barrier and deferred RC barrier scans objects in barriers, and don't update fields. I don't know if any barrier will scan objects and update fields at the same time, but I guess there are. There may be other plans that scan objects during GC and doesn't update fields. In theory, MarkSweep belongs to this category. So "whether it is used for barriers" and "whether it updates the fields" should be orthogonal. It just happens that barriers have such use cases.

wks avatar Sep 10 '25 04:09 wks

CRuby implements Scanning::scan_object_and_trace_edges, but not Scanning::scan_object

We can repurpose scan_object_and_trace_edges and use it in the barriers.

In my opinion, the binding just provides a way to enumerate the references or the slots -- it is the same for object scanning in GC and object scanning in barriers. A binding does not need to know the difference. The difference is how MMTk core uses the API, and it is internal to MMTk core. We can ask for slots, and we don't update it. In fact, in non-moving plans, that is what we do -- we are not using a different scan object method because we don't update slots in non-moving plans.

qinsoon avatar Sep 10 '25 05:09 qinsoon