RFR: 8267807: C2: Downcast receiver to target holder during inlining

Vladimir Ivanov vlivanov at openjdk.java.net
Wed Jun 2 23:34:40 UTC 2021


On Wed, 2 Jun 2021 23:24:06 GMT, Vladimir Ivanov <vlivanov at openjdk.org> wrote:

>> I closely looked at the relevant logic and couldn't find any possibility to bypass receiver subtype check. 
>> 
>> There are 2 call site shapes possible:
>> 
>> 1. `invokevirtual C.m RECV => J.m DEFAULT` (`J.m` - resolved and selected method)
>> 
>> Verifier guarantees that RECV <: C (REFC) and method resolution ensures that C <: J (REFC <: DECC).
>> 
>> 
>> 2. `invokeinterface I.m RECV => J.m DEFAULT` (`J.m` - resolved and selected method)
>> 
>> `I` (REFC) is required to be an interface (structural constraint of `invokeinterface` instruction).
>> 
>> Then, here's the relevant code:
>> 
>>   ciInstanceKlass* actual_receiver = resolved_iklass;
>>   ...
>>   ciInstanceKlass *ikl = receiver_type->klass()->as_instance_klass();
>>   if (ikl->is_loaded() && ikl->is_initialized() && !ikl->is_interface() &&
>>       (ikl == actual_receiver || ikl->is_subtype_of(actual_receiver))) {
>>     // ikl is a same or better type than the original actual_receiver,
>>     // e.g. static receiver from bytecodes.
>>     actual_receiver = ikl;
>>     // Is the actual_receiver exact?
>>     actual_receiver_is_exact = receiver_type->klass_is_exact();
>>   }
>> 
>>   ciInstanceKlass*   calling_klass = caller->holder();
>>   ciMethod* cha_monomorphic_target = resolved_method->find_monomorphic_target(calling_klass, resolved_iklass, actual_receiver, check_access);
>> 
>> ...
>> 
>> ciMethod* ciMethod::find_monomorphic_target(...) {
>>   ...
>>   if (actual_recv->is_interface()) {
>>     // %%% We cannot trust interface types, yet.  See bug 6312651.
>>     return NULL;
>>   }
>> 
>> 
>> 
>> `receiver_type` can represent either an interface (1a) or a class (1b `ikl`).
>> 
>> 1a. But if `ikl->is_interface()`, then `actual_receiver` stays as an interface (`I`, REFC) and `find_monomorphic_target()` returns NULL.
>> 
>> 1b. If `ikl` is a class, then `ikl != actual_receiver` and depending on whether `ikl->is_subtype_of(actual_receiver))`, `actual_receiver` is either REFC (interface) or `receiver_type`.
>> 
>> `find_monomorphic_target()` can succeed only in the latter case (`actual_receiver` is a class and not an interface), but previous static subtype check guarantees that the receiver type (a class) implements the interface.  
>> 
>> Q.E.D
>
> So, CHA is still kept conservative when interface types on receiver are encountered. 
> Currently, only single implementor case is optimised, but there are proper receiver subtype checks in place.
> 
> It should be quite straightforward to relax aforementioned constraints on receiver types (only classes, no interfaces), but then we would require additional receiver subtype check accompanying `Compile::optimize_virtual_call()` calls.

Anyway, as a defense in depth measure, we could still insert receiver subtype check when CHA returns a default method and hope that the check will go away once the type system better tracks interface types. Though the subtype check against an interface can be quite expensive, I don't expect any performance regressions considering CHA hasn't optimized default methods before.

-------------

PR: https://git.openjdk.java.net/jdk/pull/4212


More information about the hotspot-compiler-dev mailing list