RFR(XL): 8224675: Late GC barrier insertion for ZGC

Erik Osterlund erik.osterlund at oracle.com
Fri Jun 7 14:27:40 UTC 2019


Looks good.

/Erik

> On 7 Jun 2019, at 14:53, Nils Eliasson <nils.eliasson at oracle.com> wrote:
> 
> 
> http://cr.openjdk.java.net/~neliasso/8224675/webrev.07/
> 
> 
>> On 2019-06-07 14:45, Nils Eliasson wrote:
>> Hi Erik,
>> 
>> I uploaded webrev with the friend declaration removed.
>> 
>> I also changed back the shape of how the barrier_insertion is called from Compile.cpp. Then that will call PhaseIdealLoop::optimize and use it in the same way as Shenandoah does GC specific loop opts.
>> 
>> I also added a clearing of major progress after the barrier insertion. Otherwise some LoopLimit nodes won't be removed correctly in the following macro expansion phase.
>> 
>> Thank you!
>> 
>> Regards,
>> 
>> / Nils
>> 
>>> On 2019-06-06 21:06, Erik Österlund wrote:
>>> Hi Nils,
>>> 
>>> One final nit... could you please remove the now seemingly unnecessary "friend class ZBarrierSetC2" in PhaseIdealLoop? I don't need another webrev for that.
>>> 
>>> This looks good. Great job Nils. This will improve the robustness of our barriers.
>>> 
>>> /Erik
>>> 
>>>> On 2019-06-06 20:03, Nils Eliasson wrote:
>>>> 
>>>> On 2019-06-05 10:20, Roland Westrelin wrote:
>>>>>> Ah - I think I get it. You mean like this:
>>>>>> 
>>>>>> void ZBarrierSetC2::barrier_insertion_phase(PhaseIterGVN& igvn)const {
>>>>>>     PhaseIdealLoop ideal_loop(igvn,LoopOptsNone);
>>>>>> 
>>>>>>     // First make sure all loads between call and catch are moved to the
>>>>>> catch block clean_catch_blocks(&ideal_loop);
>>>>>> 
>>>>>>     // Then expand barriers on all loads insert_load_barriers(&ideal_loop);
>>>>>> 
>>>>>>     // Handle all Unsafe that need barriers. insert_barriers_on_unsafe(&ideal_loop);
>>>>>> 
>>>>>>     // Cleanup any modified bits igvn.optimize();
>>>>>> 
>>>>>>     igvn.C->clear_major_progress();
>>>>>> }
>>>>>> 
>>>>>> An excellent idea. Then I can remove the new LoopOptsMode::BarrierInsertion.
>>>>> I was thinking something like what we do in Shenandoah for barrier
>>>>> expansion:
>>>>> 
>>>>> PhaseIdealLoop ideal_loop(igvn, LoopOptsShenandoahExpand);
>>>>> 
>>>>> ShenandoahBarrierSetC2::is_gc_specific_loop_opts_pass() returns true for
>>>>> LoopOptsShenandoahExpand and ShenandoahBarrierSetC2::optimize_loops()
>>>>> handles LoopOptsShenandoahExpand. So there's nothing shenandoah specific
>>>>> in PhaseIdealLoop::build_and_optimize().
>>>> 
>>>> I followed your example and changed my implementation inline with that.
>>>> 
>>>> 
>>>>> 
>>>>>> I've been running some experiments with asserts on the clone code.
>>>>>> 
>>>>>> 1) There can never be any control flow here - so now phis or such.
>>>>>> 
>>>>>> 2) Stores have explicit control - and would never be scheduled here either.
>>>>>> 
>>>>>> 3) Loads - they end up here because they can float. They only matter if
>>>>>> there is a use dominated by the catch (after a merge of catch control
>>>>>> flow), or uses in more than one catch-proj branch. The only nodes
>>>>>> observed being cloned is LoadPNodes with barriers, BoolNodes, and CmpP
>>>>>> nodes. It's the same pattern of comparing a pointer. All other load has
>>>>>> it's control in the catch-projs.
>>>>>> 
>>>>>> I will add asserts to the clone in fixup_uses_in_catch to reflect this
>>>>>> conclusion and make sure that I catch any change in behavior.
>>>>> I was thinking of something like:
>>>>> 
>>>>> try {
>>>>>    non_inlined_call1();
>>>>>    int v = some_object.object_field.int_field;
>>>>>    non_inlined_call2(v, v);
>>>>> } catch (..) {
>>>>>    int v = some_object.object_field.int_field;
>>>>>    // some use for v
>>>>> }
>>>>> 
>>>>> So there would be a LoadP, a load barrier and a LoadI right after the
>>>>> call. The LoadI is the first to be cloned. It has 3 uses, so it's cloned
>>>>> 3 times? Which would mean non_inlined_call2 is actually called with:
>>>>> 
>>>>>    SomeObject object = some_object.object_field;
>>>>>    non_inlined_call2(object.int_field, object.int_field);
>>>>> 
>>>>> the field is reloaded and that code doesn't have the same effect as
>>>>> above. Or am I missing something?
>>>> 
>>>> The object_field usually gets a null-check which creates a control flow that anchors the LoadI further down. I managed to find one case in specjbb with a static call followed by a LoadL.
>>>> 
>>>> I fixed this by calling the call_catch_cleanup recursively on these load, when encountered. They will then be handled in the same way as the LoadPs with a barrier.
>>>> 
>>>> I also found an issue that I could get duplicate phis in blocks when connecting the loads together. I fixed this by keeping track on which regions has gotten a phi, and caching them.
>>>> 
>>>> I've also added a number of small fixes after feedback from Erik Ö.
>>>> 
>>>> New webrev: http://cr.openjdk.java.net/~neliasso/8224675/webrev.06/
>>>> 
>>>> Regards,
>>>> 
>>>> Nils
>>>> 
>>>> 
>>>>> 
>>>>> Roland.



More information about the hotspot-compiler-dev mailing list