RFR: 8189871: Refactor GC barriers to use declarative semantics
Erik Österlund
erik.osterlund at oracle.com
Fri Nov 10 08:33:58 UTC 2017
Hi Per,
Thank you. :)
/Erik
On 2017-11-10 09:25, Per Liden wrote:
> Looks good! Awesome work Erik!
>
> (I pre-reviewed this before Erik sent it out, so all my comments have
> already been taken care of)
>
> cheers,
> Per
>
> On 2017-11-09 18:00, Erik Österlund wrote:
>> Hi,
>>
>> In an effort to remove explicit calls to GC barriers (and other
>> orthogonal forms of barriers, like encoding/decoding oops for compressed
>> oops and fencing for memory ordering), I have built an API that I call
>> "Access". Its purpose is to perform accesses with declarative semantics,
>> to handle multiple orthogonal concerns that affect how an access is
>> performed, including memory ordering, compressed oops, GC barriers for
>> marking, reference strength, etc, and as a result making GCs more
>> modular, and as a result allow new concurrently compacting GC schemes
>> utilizing load barriers to live in harmony in hotspot without everyone
>> going crazy manually inserting barriers if UseBlahGC is enabled.
>>
>> CR:
>> https://bugs.openjdk.java.net/browse/JDK-8189871
>>
>> Webrev:
>> http://cr.openjdk.java.net/~eosterlund/8189871/webrev.00/
>>
>> So there are three views of this I suppose:
>>
>> 1) The frontend: how this is actually used in shared code
>> 2) The backends: how anyone writing a GC sticks their required barriers
>> in there
>> 3) The internals: how accesses find their way from the frontend to the
>> corresponding backend
>>
>> == Frontend ==
>>
>> Let's start with the frontend. I hope I made this fairly simple! You can
>> find it in runtime/access.hpp
>> Each access annotates its declarative semantics with a set of
>> "decorators", which is the name of the attributes/properties affecting
>> how an access is performed.
>> There is an Access<decorator> API that makes the declarative semantics
>> possible.
>>
>> For example, if I want to perform a load acquire of an oop in the heap
>> that has "weak" strength, I would do something like:
>> oop result = Access<MO_ACQUIRE | IN_HEAP |
>> ON_WEAK_OOP_REF>::oop_load_at(obj, offset);
>>
>> The Access API would then send the access through some GC backend, that
>> overrides the whole access and tells it to perform a "raw" load acquire,
>> and then possibly keep it alive if necessary (G1 SATB enqueue barriers).
>>
>> To make life easier, there are some helpers for the most common access
>> patterns that merely add some default decorator for the involved type of
>> access. For example, there is a RawAccess for performing AS_RAW accesses
>> (that bypasses runtime checks and GC barriers), HeapAccess sets the
>> IN_HEAP decorator and RootAccess sets the IN_ROOT decorator for
>> accessing root oops. So for the previous call, I could simply do:
>>
>> oop result = HeapAccess<MO_ACQUIRE | ON_WEAK_OOP_REF>::oop_load_at(obj,
>> offset);
>>
>> The access.hpp file introduces each decorator (belonging to some
>> category) with an explanation what it is for. It also introduces all
>> operations you can make with access (loads, stores, cmpxchg, xchg,
>> arraycopy and clone).
>>
>> This changeset mostly introduces the Access API but is not complete in
>> annotating the code more than where it gets very awkward if I don't.
>>
>> == Backend ==
>>
>> For a GC maintainer, the BarrierSet::AccessBarrier is the top level
>> backend that provides basic accesses that may be overridden. By default,
>> it just performs raw accesses without any GC barriers, that handle
>> things like compressed oops and memory ordering only. The ModRef barrier
>> set introduces the notion of pre/post write barriers, that can be
>> overridden for each GC. The CardTableModRef barrier set overrides the
>> post write barrier to mark cards, and G1 overrides it to mark cards
>> slightly differently and do some SATB enqueueing. G1 also overrides
>> loads to see if we need to perform SATB enqueue on weak references.
>>
>> The raw accesses go to the RawAccessBarrier (living in
>> accessBackend.hpp) that performs the actual accesses. It connects to
>> Atomic and OrderAccess for accesses that require that.
>>
>> == Internals ==
>>
>> Internally, the accesses go through a number of stages in
>> access.inline.hpp as documented at the top.
>>
>> 1) set default decorators and get rid of CV qualifiers etc. Sanity
>> checking also happens here: we check that the decorators make sense for
>> the access being performed, and that the passed in types are not bogus.
>> 2) reduce types so if we have a different type of the address and value,
>> then either it is not allowed or it implies we use compressed oops and
>> remember that we know something about whether compressed oops are used
>> or not, before erasing address type
>> 3) pre-runtime dispatch: figure out if all runtime checks can be
>> bypassed into a raw access
>> 4) runtime dispatch: send the access through a function pointer that
>> upon the first invocation resolves the intended GC AccessBarrier
>> accessor on the BarrierSet that handles this access, as well as figures
>> out whether we are using compressed oops or not while we are at it, and
>> then calls it through the post-runtime dispatch
>> 5) post-runtime dispatch: fix some erased types that were not known at
>> compile time such as whether the address is a narrowOop* or oop*
>> depending on whether compressed oops was selected at runtime or not, and
>> call the resolved BarrierSet::AccessBarrier accessor (load/store/etc)
>> with all the call-site build-time and run-time resolved decorators and
>> type information that describes the access.
>>
>> Testing: mach5 tier1-5
>>
>> Thanks,
>> /Erik
More information about the hotspot-dev
mailing list