RFR: 8189871: Refactor GC barriers to use declarative semantics

coleen.phillimore at oracle.com coleen.phillimore at oracle.com
Tue Nov 14 01:34:49 UTC 2017


http://cr.openjdk.java.net/~eosterlund/8189871/webrev.00/src/hotspot/share/classfile/javaClasses.cpp.udiff.html

+ assert(!is_reference || 
InstanceKlass::cast(obj->klass())->is_subclass_of(SystemDictionary::Reference_klass()), 
"sanity");


Can you do something like this instead of all the InstanceKlass::cast.

+ InstanceKlass* k = InstanceKlass::cast(obj->klass());
+ bool is_reference = k->reference_type() != REF_NONE;
+ assert(!is_reference || 
k->is_subclass_of(SystemDictionary::Reference_klass()), "sanity");
+ return is_reference;
+}

And do you know that this is an instance rather than array instance?  
InstanceKlass::cast() has an assert that is_instance_klass().


http://cr.openjdk.java.net/~eosterlund/8189871/webrev.00/src/hotspot/share/oops/klass.cpp.udiff.html

Revert file with only one line removed.


http://cr.openjdk.java.net/~eosterlund/8189871/webrev.00/src/hotspot/share/runtime/access.hpp.html

  240   template <typename T>
  241   struct OopOrNarrowOopInternal: AllStatic {
  242     typedef oop type;
  243   };
  244
  245   template <>
  246   struct OopOrNarrowOopInternal<narrowOop>: AllStatic {
  247     typedef narrowOop type;
  248   };
  249

Kim and I agree that we should not have the default template definition 
for oop and have two specializations instead.  Mostly I agree because 
this is confusing.

  240   template <typename T>
  241   struct OopOrNarrowOopInternal;

  240   template <>
  241   struct OopOrNarrowOopInternal<oop>: AllStatic {
  242     typedef oop type;
  243   };
  244
  245   template <>
  246   struct OopOrNarrowOopInternal<narrowOop>: AllStatic {
  247     typedef narrowOop type;
  248   };
  249


We were also trying to figure out how the runtime would know whether to 
use IN_CONCURRENT_ROOT vs IN_ROOT decorator, since it probably varies 
for GCs.  And requires runtime code to know whether the root is scanned 
concurrently or not, which we don't know.

This is all I have for now but I'm going to download the patch and have 
more of a look tomorrow.

Thanks,
Coleen


On 11/9/17 12:00 PM, 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