Skip to content

Generalize the substituting write barriers #1038

Open
@wks

Description

@wks

Currently, the Barrier trait provides the substituting barrier, object_reference_write, which includes both the write operation to the slot, and the write barrier semantics (such as checking the unlogged bits for the object-remembering barrier). However, this is not enough.

Memory orders

The read and write operations may have acquire and/or release semantics. For example, in Java,

Implementation-wise, those memory orders translate to different machine-level instructions.

Atomic read-modify-write operations

Atomic read-modify-write operations do both a read and (optionally) a write operation. For example, in Java,

Note that for compareAndExchange and getAndUpdate, the write operation may or may not be executed, depending on whether the actual value in the slot matches the expected value. The write barrier should only be executed if the write operation actually takes place.

Updating non-reference fields

The Sapphire algorithm needs write barriers for non-reference fields, too. Sapphire keeps two copies of objects during GC. Read operations read from either copy, but write operations need to write to both copies. This means not only reference fields, but all fields need to write to two different addresses for each language-level field update.

In order to support Sapphire, JikesRVM was refactored (mmtk/jikesrvm@721ca5a) to add write barriers for all field types in Java, but the default implementation (when barrier is not required for a type) is a simple memory write operation. The Sapphire plan overrides the barrier to handle the forwarding (see: https://github.com/perlfu/sapphire/blob/1424b489dc667f6080fc601df5437a3b7f87e828/MMTk/src/org/mmtk/plan/otfsapphire/OTFSapphireMutator.java#L1104).

JikesRVM's approach requires one function for each field type, but Java's field types are limited. There are only byte, short, int, long, char, boolean, float, double and Object. This is feasible for a Java-specific GC framework. But the Rust MMTk is designed to be language-neutral.

Related topic: tagged pointers

Main issue: #1034

In some VMs, such as Ruby, a slot may sometimes hold a reference, and sometimes hold a value (such as small integer). In some VMs, such as V8, a slot may hold a reference together with some tag bits to indicate it is holding a reference (not a value).

If a slot can hold values, an atomic exchange or compare-exchange operation may exchange a reference with a value, or exchange a value with a reference. From the GC's point of view, it is like exchanging a valid reference with a null, and vice versa. But one obvious thing to notice is that the data to store into the slot may not be the reference MMTk cares about. For example, if V8 does an CAS operation to exchange a small integer (SMI) with a reference, and it is successful, then MMTk should observe that 'That slot did not hold a reference, and now it holds a reference', and the MMTk barrier implementation should not see the tag bit in the new reference. Similar is true if the VM exchanges a reference with a SMI. If MMTk needs to remember the old value of the field, it shall remember the old reference in the field, without the tag bits, either.

Related topic: slot layout

In some VMs, a slot has more than a pointer. Lua, for example, uses a two-word struct for each slot. One of them holds a pointer (or value, depending on the tag), while the other holds a tag. In such cases, we can no longer assume a slot holds exactly an ObjectReference. Currently, the Edge trait provides an abstraction over it, but it needs another method for overwriting the field instead of updating the field for forwarding objects due to copying GC.

See also: #1034

Conclusion

In conclusion, one single Barrier::object_reference_write method is insufficient to support the rich semantics the VMs support, and it may need refactoring. There are mainly two things to take care of:

  1. MMTk cares about the old reference and the new reference of the field, without VM-specific tag bits, and does not care about non-reference values such as small integers held in the slots.
  2. The VM cares about actually reading, writing, or performing atomic RMW operations on the slot.

So a substituting write barrier method Barrier::object_reference_write should have both of the two things as arguments. The former may be ObjectReference arguments (or data structures to obtain the old and new ObjectReference in the slot), and the latter may be a call-back (closure) for the VM to do the actual storing or atomic RMW operation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P-lowPriority: Low. A low-priority issue won't be scheduled and assigned. Any help is welcome.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions