8202377: Modularize C2 GC barriers
nils.eliasson at oracle.com
Wed May 9 14:33:17 UTC 2018
A job well done. This extensive change improves the readability and
quality of the code a lot.
On 2018-05-09 16:32, Erik Österlund wrote:
> I have rebased ZGC on top of these changes. In the process, I found a
> few more hooks that will be needed for ZGC. I added them, so here is
> an incremental update:
> Full webrev:
> Each new hook is a no-op for existing GCs. I am merely adding them to
> pave way for ZGC.
> Also made a few members public that need to be accessible by the ZGC
> barrier set backend.
> Nils has helped me look at this all day. So big thanks to Nils for
> taking the time to look at these changes.
> On 2018-05-01 15:32, Erik Österlund wrote:
>> The GC barriers for C2 are not as modular as they could be. It
>> currently uses switch statements to check which GC barrier set is
>> being used, and call one or another barrier based on that, in a way
>> that it can only be used for write barriers.
>> My proposed solution is to follow the same pattern that has been used
>> by C1 (and the rest of HotSpot), which is to provide a GC barrier set
>> code generation helper for C2. Its name is BarrierSetC2. Each barrier
>> set class has its own BarrierSetC2, following a mirrored inheritance
>> hierarchy to the BarrierSet hierarchy. You generate the accesses
>> using some access_* member functions on GraphKit, which calls into
>> A lot of the design looks very similar to BarrierSetC1. In C1, there
>> was a wrapper object called LIRAccess that wrapped a bunch of context
>> parameters that were passed around in the barrier set hierarchy.
>> There is a similar wrapper for C2 that I call C2Access. Users of the
>> API do not see it. They call, e.g. access_load_at, in GraphKit during
>> parsing. The access functions wrap the access in a C2Access object
>> with a bunch of context parameters, and calls the currently selected
>> BarrierSetC2 backend accessor with this context. For the atomic
>> accesses, there is a C2AtomicAccess, inheriting from C2Access. It
>> contains more context, as required by the atomic accesses (e.g.
>> explicit alias_idx, whether the node needs pinning with an SCM
>> projection, and a memory node).
>> Apart from the normal shared decorators, C2 does use its own
>> additional decorators for its own use:
>> * C2_MISMATCHED and C2_UNALIGNED (describing properties of unsafe
>> * C2_WEAK_CMPXCHG: describing if a cmpxchg may have false negatives
>> * C2_CONTROL_DEPENDENT_LOAD: use when a load should have control
>> * C2_PINNED_LOAD: use for loads that must be pinned
>> * C2_UNSAFE_ACCESS: Used to recognize this is an unsafe access. This
>> decorator implies that loads have control dependency and need
>> pinning, unless it can be proven that the access will be inside the
>> bounds of an object.
>> * C2_READ_ACCESS and C2_WRITE_ACCESS: This denotes whether the access
>> reads or writes to memory. Or both for atomics. It is useful for for
>> figuring out what fencing is required for a given access and ordering
>> semantics, as well as being useful for Shenandoah to figure out what
>> type of barrier to use to ensure memory consistency.
>> The accesses go through a similar process as they do in C1. Let's
>> take BarrierSetC2::store_at for example. It uses the the
>> C2AccessFence scoped object helper to figure out what membars are
>> required to surround the access, resolve the address (no-op for all
>> GCs with a to-space invariant, which is all GCs except Shenandoah in
>> HotSpot at the moment), and then calls store_at_resolved. The
>> store_at_resolved member function generates the access and the
>> barriers around it. The abstract ModRefBarrierSetC2 barrier set
>> introduces the notion of pre/post write barriers, and lets concrete
>> barrier sets do sprinkle their GC barriers in there. It calls
>> BarrierSetC2::store_at_resolved to generate the actual access. For
>> example CardTableBarrierSet only needs to override its post barrier
>> for this to work as expected. The other accesses follow a similar
>> The Compile class now has a type erase (void*) per compilation unit
>> state that is created for each compilation unit (with
>> BarrierSetC2::create_barrier_state). For the GCs in HotSpot today,
>> this is always NULL. But for GCs that have their own macro nodes, the
>> compilation unit can be used for, e.g. lists of barrier-specific
>> macro nodes, that should not pollute the Compile object. Such macro
>> nodes can be expanded during macro expansion using the
>> BarrierSetC2::expand_macro_nodes member function.
>> There are a few other helpers that may be good for a GC to have, like
>> figuring out if a node is a GC barrier (for escape analysis), whether
>> a GC barrier can be eliminated (for example using
>> ReduceInitialCardMarks), whether array_copy requires GC barriers, how
>> to step over a GC barrier. There is also a helper for loop optimizing
>> GC barrier nodes.
>> This work will help to pave way for a new class of collectors
>> utilizing load barriers (ZGC and Shenandoah) for concurrent compaction.
More information about the hotspot-compiler-dev