Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/hotspot/share/opto/callGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,7 @@ bool LateInlineVirtualCallGenerator::do_late_inline_check(Compile* C, JVMState*
jvms,
allow_inline,
_prof_factor,
nullptr /*receiver_type*/,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there no benefit to passing receiver_type here?

nullptr /*speculative_receiver_type*/,
true /*allow_intrinsics*/);

Expand Down Expand Up @@ -1103,11 +1104,12 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod*
int vtable_index = Method::invalid_vtable_index;
bool call_does_dispatch = false;

const TypeOopPtr* receiver_type = nullptr;
ciKlass* speculative_receiver_type = nullptr;
if (is_virtual_or_interface) {
ciInstanceKlass* klass = target->holder();
Node* receiver_node = kit.argument(0);
const TypeOopPtr* receiver_type = gvn.type(receiver_node)->isa_oopptr();
Node* receiver_node = kit.argument(0);
receiver_type = gvn.type(receiver_node)->isa_oopptr();
// call_does_dispatch and vtable_index are out-parameters. They might be changed.
// optimize_virtual_call() takes 2 different holder
// arguments for a corner case that doesn't apply here (see
Expand All @@ -1123,6 +1125,7 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod*
CallGenerator* cg = C->call_generator(target, vtable_index, call_does_dispatch, jvms,
allow_inline,
PROB_ALWAYS,
receiver_type,
speculative_receiver_type);
return cg;
} else {
Expand Down
4 changes: 3 additions & 1 deletion src/hotspot/share/opto/compile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,9 @@ class Compile : public Phase {
// Decide how to build a call.
// The profile factor is a discount to apply to this site's interp. profile.
CallGenerator* call_generator(ciMethod* call_method, int vtable_index, bool call_does_dispatch,
JVMState* jvms, bool allow_inline, float profile_factor, ciKlass* speculative_receiver_type = nullptr,
JVMState* jvms, bool allow_inline, float profile_factor,
const TypeOopPtr* receiver_type = nullptr,
ciKlass* speculative_receiver_type = nullptr,
bool allow_intrinsics = true);
bool should_delay_inlining(ciMethod* call_method, JVMState* jvms) {
return C->directive()->should_delay_inline(call_method) ||
Expand Down
75 changes: 44 additions & 31 deletions src/hotspot/share/opto/doCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,9 @@ static void trace_type_profile(Compile* C, ciMethod* method, JVMState* jvms,
}

CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool call_does_dispatch,
JVMState* jvms, bool allow_inline,
float prof_factor, ciKlass* speculative_receiver_type,
JVMState* jvms, bool allow_inline, float prof_factor,
const TypeOopPtr* receiver_type,
ciKlass* speculative_receiver_type,
bool allow_intrinsics) {
assert(callee != nullptr, "failed method resolution");

Expand Down Expand Up @@ -151,8 +152,8 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool
if (cg != nullptr) {
if (cg->is_predicated()) {
// Code without intrinsic but, hopefully, inlined.
CallGenerator* inline_cg = this->call_generator(callee,
vtable_index, call_does_dispatch, jvms, allow_inline, prof_factor, speculative_receiver_type, false);
CallGenerator* inline_cg = this->call_generator(callee, vtable_index, call_does_dispatch, jvms, allow_inline,
prof_factor, receiver_type, speculative_receiver_type, false);
if (inline_cg != nullptr) {
cg = CallGenerator::for_predicated_intrinsic(cg, inline_cg);
}
Expand Down Expand Up @@ -325,56 +326,64 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool
}

// If there is only one implementor of this interface then we
// may be able to bind this invoke directly to the implementing
// klass but we need both a dependence on the single interface
// and on the method we bind to. Additionally since all we know
// may be able to bind this invoke directly to the implementing klass,
// but we need both a dependence on the single interface and
// on the method we bind to. Additionally, since all we know
// about the receiver type is that it's supposed to implement the
// interface we have to insert a check that it's the class we
// expect. Interface types are not checked by the verifier so
// they are roughly equivalent to Object.
// The number of implementors for declared_interface is less or
// equal to the number of implementors for target->holder() so
// if number of implementors of target->holder() == 1 then
// number of implementors for decl_interface is 0 or 1. If
// it's 0 then no class implements decl_interface and there's
// no point in inlining.
// they are roughly equivalent to Object, but receiver type can be used
// to narrow context type beyond declared_interface, so a more specific
// interface with a unique implementor can be used instead.
if (call_does_dispatch && is_interface) {
ciInstanceKlass* declared_interface = nullptr;
ciInstanceKlass* context_intf = nullptr;
// Use declared interface class as a starting point.
if (orig_callee->intrinsic_id() == vmIntrinsics::_linkToInterface) {
// MemberName doesn't keep information about resolved interface class (REFC) once
// resolution is over, but resolved method holder (DECC) can be used as a
// conservative approximation.
declared_interface = callee->holder();
context_intf = callee->holder();
} else {
assert(!orig_callee->is_method_handle_intrinsic(), "not allowed");
declared_interface = caller->get_declared_method_holder_at_bci(bci)->as_instance_klass();
context_intf = caller->get_declared_method_holder_at_bci(bci)->as_instance_klass();
}
assert(context_intf->is_interface(), "required");

// Based on receiver info, narrow context to one of most specific superinterfaces with a unique implementor.
if (receiver_type != nullptr && receiver_type->isa_instptr()) {
ciInstanceKlass* intf = receiver_type->is_instptr()->has_unique_implementor(context_intf);
if (intf != nullptr && intf != context_intf) {
assert(intf->is_subtype_of(context_intf), "not related");
assert(intf->unique_implementor() != nullptr, "no unique implementor");
context_intf = intf;
}
}
assert(declared_interface->is_interface(), "required");
ciInstanceKlass* singleton = declared_interface->unique_implementor();

ciInstanceKlass* singleton = context_intf->unique_implementor();
if (singleton != nullptr) {
assert(singleton != declared_interface, "not a unique implementor");
assert(singleton != context_intf, "not a unique implementor");

ciMethod* cha_monomorphic_target =
callee->find_monomorphic_target(caller->holder(), declared_interface, singleton, check_access);
callee->find_monomorphic_target(caller->holder(), context_intf, singleton, check_access);

if (cha_monomorphic_target != nullptr &&
cha_monomorphic_target->holder() != env()->Object_klass()) { // subtype check against Object is useless
ciKlass* holder = cha_monomorphic_target->holder();

// Try to inline the method found by CHA. Inlined method is guarded by the type check.
CallGenerator* hit_cg = call_generator(cha_monomorphic_target,
vtable_index, !call_does_dispatch, jvms, allow_inline, prof_factor);
CallGenerator* hit_cg = call_generator(cha_monomorphic_target, vtable_index, !call_does_dispatch, jvms,
allow_inline, prof_factor);

// Deoptimize on type check fail. The interpreter will throw ICCE for us.
CallGenerator* miss_cg = CallGenerator::for_uncommon_trap(callee,
Deoptimization::Reason_class_check, Deoptimization::Action_none);
Deoptimization::Reason_class_check,
Deoptimization::Action_none);

ciKlass* constraint = (holder->is_subclass_of(singleton) ? holder : singleton); // avoid upcasts
CallGenerator* cg = CallGenerator::for_guarded_call(constraint, miss_cg, hit_cg);
if (hit_cg != nullptr && cg != nullptr) {
dependencies()->assert_unique_implementor(declared_interface, singleton);
dependencies()->assert_unique_concrete_method(declared_interface, cha_monomorphic_target, declared_interface, callee);
dependencies()->assert_unique_implementor(context_intf, singleton);
dependencies()->assert_unique_concrete_method(context_intf, cha_monomorphic_target, context_intf, callee);
return cg;
}
}
Expand Down Expand Up @@ -595,10 +604,11 @@ void Parse::do_call() {
bool call_does_dispatch = false;

// Speculative type of the receiver if any
const TypeOopPtr* receiver_type = nullptr;
ciKlass* speculative_receiver_type = nullptr;
if (is_virtual_or_interface) {
Node* receiver_node = stack(sp() - nargs);
const TypeOopPtr* receiver_type = _gvn.type(receiver_node)->isa_oopptr();
Node* receiver_node = stack(sp() - nargs);
receiver_type = _gvn.type(receiver_node)->isa_oopptr();
// call_does_dispatch and vtable_index are out-parameters. They might be changed.
// For arrays, klass below is Object. When vtable calls are used,
// resolving the call with Object would allow an illegal call to
Expand All @@ -609,7 +619,7 @@ void Parse::do_call() {
callee = C->optimize_virtual_call(method(), klass, holder, orig_callee,
receiver_type, is_virtual,
call_does_dispatch, vtable_index); // out-parameters
speculative_receiver_type = receiver_type != nullptr ? receiver_type->speculative_type() : nullptr;
speculative_receiver_type = (receiver_type != nullptr ? receiver_type->speculative_type() : nullptr);
}

// Additional receiver subtype checks for interface calls via invokespecial or invokeinterface.
Expand Down Expand Up @@ -655,7 +665,8 @@ void Parse::do_call() {
// Decide call tactic.
// This call checks with CHA, the interpreter profile, intrinsics table, etc.
// It decides whether inlining is desirable or not.
CallGenerator* cg = C->call_generator(callee, vtable_index, call_does_dispatch, jvms, try_inline, prof_factor(), speculative_receiver_type);
CallGenerator* cg = C->call_generator(callee, vtable_index, call_does_dispatch, jvms, try_inline, prof_factor(),
receiver_type, speculative_receiver_type);

// NOTE: Don't use orig_callee and callee after this point! Use cg->method() instead.
orig_callee = callee = nullptr;
Expand Down Expand Up @@ -700,7 +711,9 @@ void Parse::do_call() {
// the call site, perhaps because it did not match a pattern the
// intrinsic was expecting to optimize. Should always be possible to
// get a normal java call that may inline in that case
cg = C->call_generator(cg->method(), vtable_index, call_does_dispatch, jvms, try_inline, prof_factor(), speculative_receiver_type, /* allow_intrinsics= */ false);
cg = C->call_generator(cg->method(), vtable_index, call_does_dispatch, jvms, try_inline, prof_factor(),
receiver_type, speculative_receiver_type,
false /*allow_intrinsics*/);
new_jvms = cg->generate(jvms);
if (new_jvms == nullptr) {
guarantee(failing(), "call failed to generate: calls should work");
Expand Down
29 changes: 29 additions & 0 deletions src/hotspot/share/opto/type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3438,6 +3438,22 @@ bool TypeInterfaces::has_non_array_interface() const {
return !TypeAryPtr::_array_interfaces->contains(this);
}

// Look for a leaf interface with unique implementor under context class. Report one of possibly many.
ciInstanceKlass* TypeInterfaces::has_unique_implementor(ciInstanceKlass* context) const {
assert(context->is_interface(), "not an interface");
ciInstanceKlass* candidate = nullptr;
for (int i = 0; i < _interfaces.length(); i++) {
ciInstanceKlass* intf = _interfaces.at(i);
if (intf->is_subtype_of(context) && (intf->unique_implementor() != nullptr)) {
if (candidate == nullptr || intf->is_subtype_of(candidate)) {
assert(candidate == nullptr || (candidate->unique_implementor() != nullptr), "not a candidate");
candidate = intf;
}
}
}
return candidate;
}

//------------------------------TypeOopPtr-------------------------------------
TypeOopPtr::TypeOopPtr(TYPES t, PTR ptr, ciKlass* k, const TypeInterfaces* interfaces, bool xk, ciObject* o, int offset,
int instance_id, const TypePtr* speculative, int inline_depth)
Expand Down Expand Up @@ -4547,6 +4563,19 @@ const TypeKlassPtr* TypeInstPtr::as_klass_type(bool try_for_exact) const {
return TypeInstKlassPtr::make(xk ? TypePtr::Constant : TypePtr::NotNull, klass(), _interfaces, 0);
}

// Does the type represent an interface instance?
bool TypeInstPtr::is_interface() const {
return (instance_klass() == ciEnv::current()->Object_klass()) && !_interfaces->empty();
}

// For an interface instance reports one of most specific superinterfaces with a unique implementor.
ciInstanceKlass* TypeInstPtr::has_unique_implementor(ciInstanceKlass* context_intf) const {
if (is_interface() && context_intf->is_interface()) {
return _interfaces->has_unique_implementor(context_intf);
}
return nullptr;
}

template <class T1, class T2> bool TypePtr::is_meet_subtype_of_helper_for_instance(const T1* this_one, const T2* other, bool this_xk, bool other_xk) {
static_assert(std::is_base_of<T2, T1>::value, "");

Expand Down
7 changes: 7 additions & 0 deletions src/hotspot/share/opto/type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1128,6 +1128,8 @@ class TypeInterfaces : public Type {

bool singleton(void) const;
bool has_non_array_interface() const;

ciInstanceKlass* has_unique_implementor(ciInstanceKlass* context) const;
};

//------------------------------TypePtr----------------------------------------
Expand Down Expand Up @@ -1585,6 +1587,11 @@ class TypeInstPtr : public TypeOopPtr {

const TypeKlassPtr* as_klass_type(bool try_for_exact = false) const;

bool is_interface() const;

// Find one (of possibly many) most specific superinterfaces with a unique implementor.
ciInstanceKlass* has_unique_implementor(ciInstanceKlass* context_intf) const;

// Convenience common pre-built types.
static const TypeInstPtr *NOTNULL;
static const TypeInstPtr *BOTTOM;
Expand Down
Loading