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

Vladimir Ivanov vlivanov at openjdk.java.net
Wed Jun 2 23:14:42 UTC 2021


On Tue, 1 Jun 2021 20:46:01 GMT, John R Rose <jrose at openjdk.org> wrote:

>> Though it doesn't happen in practice now, we can trust interface types on the receiver object because virtual/interface method invocation implies proper type check of the receiver.
>
> I think you need a CheckCastPP as the comment (written by me long ago IIRC) suggests.
> 
> I’d like to believe you can trust the type of a CHA receiver, but I don’t.  What the IR says about interface types is (last time I looked) just as untrustworthy as what the verifier thinks about them.
> 
> (We should fix this decisively someday.  Or did we do this cleanup already and missed it?  We have had dozens of bugs
> in early C2 because of untrustworthy interface types.)
> 
> Suppose CHA (of default methods, yay!) says that since x is interface Foo, then default Foo::m is the only possible target
> of x.m.  Before we execute Foo::m we still need a type check of x, because it could be any Object whatsoever.
> 
> I suggest adding the CheckCastPP in, in all modes, with fallback to de-opt not VM crash.  The IR types are saying there is not a dominating check.

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

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

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


More information about the hotspot-compiler-dev mailing list