mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8280469: C2: CHA support for interface calls when inlining through method handle linker
Reviewed-by: kvn, roland
This commit is contained in:
parent
b086e34f71
commit
256a9beffc
@ -605,7 +605,7 @@ bool ciInstanceKlass::is_leaf_type() {
|
||||
if (is_shared()) {
|
||||
return is_final(); // approximately correct
|
||||
} else {
|
||||
return !has_subklass() && (nof_implementors() == 0);
|
||||
return !has_subklass() && (!is_interface() || nof_implementors() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -619,6 +619,7 @@ bool ciInstanceKlass::is_leaf_type() {
|
||||
// This is OK, since any dependencies we decide to assert
|
||||
// will be checked later under the Compile_lock.
|
||||
ciInstanceKlass* ciInstanceKlass::implementor() {
|
||||
assert(is_interface(), "required");
|
||||
ciInstanceKlass* impl = _implementor;
|
||||
if (impl == nullptr) {
|
||||
if (is_shared()) {
|
||||
|
||||
@ -259,6 +259,7 @@ public:
|
||||
|
||||
ciInstanceKlass* unique_implementor() {
|
||||
assert(is_loaded(), "must be loaded");
|
||||
assert(is_interface(), "must be");
|
||||
ciInstanceKlass* impl = implementor();
|
||||
return (impl != this ? impl : nullptr);
|
||||
}
|
||||
|
||||
@ -97,10 +97,9 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool
|
||||
Bytecodes::Code bytecode = caller->java_code_at_bci(bci);
|
||||
ciMethod* orig_callee = caller->get_method_at_bci(bci);
|
||||
|
||||
const bool is_virtual_or_interface = (bytecode == Bytecodes::_invokevirtual) ||
|
||||
(bytecode == Bytecodes::_invokeinterface) ||
|
||||
(orig_callee->intrinsic_id() == vmIntrinsics::_linkToVirtual) ||
|
||||
(orig_callee->intrinsic_id() == vmIntrinsics::_linkToInterface);
|
||||
const bool is_virtual = (bytecode == Bytecodes::_invokevirtual) || (orig_callee->intrinsic_id() == vmIntrinsics::_linkToVirtual);
|
||||
const bool is_interface = (bytecode == Bytecodes::_invokeinterface) || (orig_callee->intrinsic_id() == vmIntrinsics::_linkToInterface);
|
||||
const bool is_virtual_or_interface = is_virtual || is_interface;
|
||||
|
||||
const bool check_access = !orig_callee->is_method_handle_intrinsic(); // method handle intrinsics don't perform access checks
|
||||
|
||||
@ -339,17 +338,25 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool
|
||||
// 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.
|
||||
if (call_does_dispatch && bytecode == Bytecodes::_invokeinterface) {
|
||||
ciInstanceKlass* declared_interface =
|
||||
caller->get_declared_method_holder_at_bci(bci)->as_instance_klass();
|
||||
if (call_does_dispatch && is_interface) {
|
||||
ciInstanceKlass* declared_interface = nullptr;
|
||||
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();
|
||||
} else {
|
||||
assert(!orig_callee->is_method_handle_intrinsic(), "not allowed");
|
||||
declared_interface = caller->get_declared_method_holder_at_bci(bci)->as_instance_klass();
|
||||
}
|
||||
assert(declared_interface->is_interface(), "required");
|
||||
ciInstanceKlass* singleton = declared_interface->unique_implementor();
|
||||
|
||||
if (singleton != nullptr) {
|
||||
assert(singleton != declared_interface, "not a unique implementor");
|
||||
|
||||
assert(check_access, "required");
|
||||
ciMethod* cha_monomorphic_target =
|
||||
callee->find_monomorphic_target(caller->holder(), declared_interface, singleton);
|
||||
callee->find_monomorphic_target(caller->holder(), declared_interface, singleton, check_access);
|
||||
|
||||
if (cha_monomorphic_target != nullptr &&
|
||||
cha_monomorphic_target->holder() != env()->Object_klass()) { // subtype check against Object is useless
|
||||
@ -372,7 +379,7 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool
|
||||
}
|
||||
}
|
||||
}
|
||||
} // call_does_dispatch && bytecode == Bytecodes::_invokeinterface
|
||||
} // call_does_dispatch && is_interface
|
||||
|
||||
// Nothing claimed the intrinsic, we go with straight-forward inlining
|
||||
// for already discovered intrinsic.
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
/*
|
||||
* @test
|
||||
* @requires !vm.graal.enabled
|
||||
* @requires vm.opt.StressMethodHandleLinkerInlining == null | !vm.opt.StressMethodHandleLinkerInlining
|
||||
* @requires vm.opt.StressUnstableIfTraps == null | !vm.opt.StressUnstableIfTraps
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.base/jdk.internal.vm.annotation
|
||||
@ -55,6 +56,9 @@ package compiler.cha;
|
||||
|
||||
import jdk.internal.vm.annotation.DontInline;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
import static compiler.cha.Utils.*;
|
||||
|
||||
public class StrengthReduceInterfaceCall {
|
||||
@ -66,6 +70,18 @@ public class StrengthReduceInterfaceCall {
|
||||
run(ThreeLevelHierarchyAbstractVsDefault.class);
|
||||
run(ThreeLevelDefaultHierarchy.class);
|
||||
run(ThreeLevelDefaultHierarchy1.class);
|
||||
|
||||
// Implementation limitation: CHA is not performed by C1 during inlining through MH linkers.
|
||||
if (!jdk.test.whitebox.code.Compiler.isC1Enabled()) {
|
||||
run(ObjectToString.TestMH.class, ObjectToString.class);
|
||||
run(ObjectHashCode.TestMH.class, ObjectHashCode.class);
|
||||
run(TwoLevelHierarchyLinear.TestMH.class, TwoLevelHierarchyLinear.class);
|
||||
run(ThreeLevelHierarchyLinear.TestMH.class, ThreeLevelHierarchyLinear.class);
|
||||
run(ThreeLevelHierarchyAbstractVsDefault.TestMH.class, ThreeLevelHierarchyAbstractVsDefault.class);
|
||||
run(ThreeLevelDefaultHierarchy.TestMH.class, ThreeLevelDefaultHierarchy.class);
|
||||
run(ThreeLevelDefaultHierarchy1.TestMH.class, ThreeLevelDefaultHierarchy1.class);
|
||||
}
|
||||
|
||||
System.out.println("TEST PASSED");
|
||||
}
|
||||
|
||||
@ -87,7 +103,7 @@ public class StrengthReduceInterfaceCall {
|
||||
static class DJ2 implements J { public String toString() { return "DJ2"; }}
|
||||
|
||||
@Override
|
||||
public Object test(I i) { return ObjectToStringHelper.testHelper(i); /* invokeinterface I.toString() */ }
|
||||
public Object test(I i) throws Throwable { return ObjectToStringHelper.testHelper(i); /* invokeinterface I.toString() */ }
|
||||
|
||||
@TestCase
|
||||
public void testMono() {
|
||||
@ -155,6 +171,20 @@ public class StrengthReduceInterfaceCall {
|
||||
});
|
||||
assertCompiled();
|
||||
}
|
||||
|
||||
public static class TestMH extends ObjectToString {
|
||||
static final MethodHandle TEST_MH = findVirtualHelper(I.class, "toString", String.class, MethodHandles.lookup());
|
||||
|
||||
@Override
|
||||
public void checkInvalidReceiver() {
|
||||
// receiver type check failures trigger nmethod invalidation
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object test(I obj) throws Throwable {
|
||||
return (String)TEST_MH.invokeExact(obj); // invokeinterface I.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ObjectHashCode extends ATest<ObjectHashCode.I> {
|
||||
@ -175,7 +205,7 @@ public class StrengthReduceInterfaceCall {
|
||||
static class DJ2 implements J { public int hashCode() { return super.hashCode(); }}
|
||||
|
||||
@Override
|
||||
public Object test(I i) {
|
||||
public Object test(I i) throws Throwable {
|
||||
return ObjectHashCodeHelper.testHelper(i); /* invokeinterface I.hashCode() */
|
||||
}
|
||||
|
||||
@ -242,6 +272,20 @@ public class StrengthReduceInterfaceCall {
|
||||
});
|
||||
assertCompiled();
|
||||
}
|
||||
|
||||
public static class TestMH extends ObjectHashCode {
|
||||
static final MethodHandle TEST_MH = findVirtualHelper(I.class, "hashCode", int.class, MethodHandles.lookup());
|
||||
|
||||
@Override
|
||||
public void checkInvalidReceiver() {
|
||||
// receiver type check failures trigger nmethod invalidation
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object test(I obj) throws Throwable {
|
||||
return (int)TEST_MH.invokeExact(obj); // invokeinterface I.hashCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class TwoLevelHierarchyLinear extends ATest<TwoLevelHierarchyLinear.I> {
|
||||
@ -263,7 +307,7 @@ public class StrengthReduceInterfaceCall {
|
||||
static class DJ2 implements J { public Object m() { return WRONG; }}
|
||||
|
||||
@DontInline
|
||||
public Object test(I i) {
|
||||
public Object test(I i) throws Throwable {
|
||||
return i.m();
|
||||
}
|
||||
|
||||
@ -366,6 +410,20 @@ public class StrengthReduceInterfaceCall {
|
||||
});
|
||||
assertCompiled();
|
||||
}
|
||||
|
||||
public static class TestMH extends TwoLevelHierarchyLinear {
|
||||
static final MethodHandle TEST_MH = findVirtualHelper(I.class, "m", Object.class, MethodHandles.lookup());
|
||||
|
||||
@Override
|
||||
public void checkInvalidReceiver() {
|
||||
// receiver type check failures trigger nmethod invalidation
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object test(I obj) throws Throwable {
|
||||
return TEST_MH.invokeExact(obj); // invokeinterface I.m()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ThreeLevelHierarchyLinear extends ATest<ThreeLevelHierarchyLinear.I> {
|
||||
@ -385,7 +443,7 @@ public class StrengthReduceInterfaceCall {
|
||||
static class DJ implements J { public Object m() { return WRONG; }}
|
||||
|
||||
@DontInline
|
||||
public Object test(I i) {
|
||||
public Object test(I i) throws Throwable {
|
||||
return i.m(); // I <: J.m ABSTRACT
|
||||
}
|
||||
|
||||
@ -404,10 +462,16 @@ public class StrengthReduceInterfaceCall {
|
||||
assertCompiled(); // No deopt on not-yet-seen receiver
|
||||
|
||||
// 2. No dependency invalidation: different context
|
||||
initialize(DJ.class, // DJ.m <: intf J.m ABSTRACT
|
||||
K1.class, // intf K1 <: intf I <: intf J.m ABSTRACT
|
||||
K2.class); // intf K2.m ABSTRACT <: intf I <: intf J.m ABSTRACT
|
||||
assertCompiled();
|
||||
if (contextClass() == I.class) {
|
||||
initialize(DJ.class, // DJ.m <: intf J.m ABSTRACT
|
||||
K1.class, // intf K1 <: intf I <: intf J.m ABSTRACT
|
||||
K2.class); // intf K2.m ABSTRACT <: intf I <: intf J.m ABSTRACT
|
||||
assertCompiled();
|
||||
} else if (contextClass() == J.class) {
|
||||
// no classes to initialize w/o breaking a dependency
|
||||
} else {
|
||||
throw new InternalError("unsupported context: " + contextClass());
|
||||
}
|
||||
|
||||
// 3. Dependency invalidation: DI.m <: I
|
||||
initialize(DI.class); // DI.m <: intf I <: intf J.m ABSTRACT
|
||||
@ -491,6 +555,30 @@ public class StrengthReduceInterfaceCall {
|
||||
});
|
||||
assertCompiled();
|
||||
}
|
||||
|
||||
Class<?> contextClass() {
|
||||
return I.class;
|
||||
}
|
||||
|
||||
public static class TestMH extends ThreeLevelHierarchyLinear {
|
||||
static final MethodHandle TEST_MH = findVirtualHelper(I.class, "m", Object.class, MethodHandles.lookup());
|
||||
|
||||
@Override
|
||||
public void checkInvalidReceiver() {
|
||||
// receiver type check failures trigger nmethod invalidation
|
||||
}
|
||||
|
||||
@Override
|
||||
Class<?> contextClass() {
|
||||
return J.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object test(I obj) throws Throwable {
|
||||
return TEST_MH.invokeExact(obj); // invokeinterface I.m()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ThreeLevelHierarchyAbstractVsDefault extends ATest<ThreeLevelHierarchyAbstractVsDefault.I> {
|
||||
@ -503,7 +591,7 @@ public class StrengthReduceInterfaceCall {
|
||||
static class C implements I { public Object m() { return CORRECT; }}
|
||||
|
||||
@DontInline
|
||||
public Object test(I i) {
|
||||
public Object test(I i) throws Throwable {
|
||||
return i.m(); // intf I.m OVERPASS
|
||||
}
|
||||
|
||||
@ -598,6 +686,20 @@ public class StrengthReduceInterfaceCall {
|
||||
});
|
||||
assertCompiled();
|
||||
}
|
||||
|
||||
public static class TestMH extends ThreeLevelHierarchyAbstractVsDefault {
|
||||
static final MethodHandle TEST_MH = findVirtualHelper(I.class, "m", Object.class, MethodHandles.lookup());
|
||||
|
||||
@Override
|
||||
public void checkInvalidReceiver() {
|
||||
// receiver type check failures trigger nmethod invalidation
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object test(I obj) throws Throwable {
|
||||
return TEST_MH.invokeExact(obj); // invokeinterface I.m()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ThreeLevelDefaultHierarchy extends ATest<ThreeLevelDefaultHierarchy.I> {
|
||||
@ -617,7 +719,7 @@ public class StrengthReduceInterfaceCall {
|
||||
static class DK3 implements K3 {}
|
||||
|
||||
@DontInline
|
||||
public Object test(I i) {
|
||||
public Object test(I i) throws Throwable {
|
||||
return i.m();
|
||||
}
|
||||
|
||||
@ -636,11 +738,17 @@ public class StrengthReduceInterfaceCall {
|
||||
assertCompiled();
|
||||
|
||||
// 2. No dependency invalidation
|
||||
initialize(DJ.class, // DJ.m <: intf J.m ABSTRACT
|
||||
K1.class, // intf K1 <: intf I <: intf J.m ABSTRACT
|
||||
K2.class, // intf K2.m ABSTRACT <: intf I <: intf J.m ABSTRACT
|
||||
DK3.class); // DK3.m <: intf K3.m DEFAULT <: intf J.m ABSTRACT
|
||||
assertCompiled();
|
||||
if (contextClass() == I.class) {
|
||||
initialize(DJ.class, // DJ.m <: intf J.m ABSTRACT
|
||||
K1.class, // intf K1 <: intf I <: intf J.m ABSTRACT
|
||||
K2.class, // intf K2.m ABSTRACT <: intf I <: intf J.m ABSTRACT
|
||||
DK3.class); // DK3.m <: intf K3.m DEFAULT <: intf J.m ABSTRACT
|
||||
assertCompiled();
|
||||
} else if (contextClass() == J.class) {
|
||||
// no classes to initialize w/o breaking a dependency
|
||||
} else {
|
||||
throw new InternalError("unsupported context: " + contextClass());
|
||||
}
|
||||
|
||||
// 3. Dependency invalidation
|
||||
initialize(DI.class); // DI.m <: intf I <: intf J.m ABSTRACT
|
||||
@ -666,6 +774,29 @@ public class StrengthReduceInterfaceCall {
|
||||
});
|
||||
assertCompiled();
|
||||
}
|
||||
|
||||
Class<?> contextClass() {
|
||||
return I.class;
|
||||
}
|
||||
|
||||
public static class TestMH extends ThreeLevelDefaultHierarchy {
|
||||
static final MethodHandle TEST_MH = findVirtualHelper(I.class, "m", Object.class, MethodHandles.lookup());
|
||||
|
||||
@Override
|
||||
public void checkInvalidReceiver() {
|
||||
// receiver type check failures trigger nmethod invalidation
|
||||
}
|
||||
|
||||
@Override
|
||||
Class<?> contextClass() {
|
||||
return J.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object test(I obj) throws Throwable {
|
||||
return TEST_MH.invokeExact(obj); // invokeinterface I.m()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ThreeLevelDefaultHierarchy1 extends ATest<ThreeLevelDefaultHierarchy1.I> {
|
||||
@ -686,7 +817,7 @@ public class StrengthReduceInterfaceCall {
|
||||
static class DJ2 implements J2 { public Object m() { return WRONG; }}
|
||||
|
||||
@DontInline
|
||||
public Object test(I i) {
|
||||
public Object test(I i) throws Throwable {
|
||||
return i.m();
|
||||
}
|
||||
|
||||
@ -705,11 +836,20 @@ public class StrengthReduceInterfaceCall {
|
||||
assertCompiled();
|
||||
|
||||
// 2. No dependency invalidation
|
||||
initialize(DJ1.class,
|
||||
DJ2.class,
|
||||
K1.class,
|
||||
K2.class,
|
||||
K3.class);
|
||||
if (contextClass() == I.class) {
|
||||
initialize(DJ1.class, // DJ1.m <: intf J1
|
||||
DJ2.class, // DJ2.m <: intf J2.m ABSTRACT
|
||||
K1.class, // intf K1 <: intf I <: intf J1, intf J2.m ABSTRACT
|
||||
K2.class, // intf K2.m ABSTRACT <: intf I <: intf J1, intf J2.m ABSTRACT
|
||||
K3.class); // intf K3.m DEFAULT <: intf I <: intf J1, intf J2.m ABSTRACT
|
||||
} else if (contextClass() == J2.class) {
|
||||
initialize(DJ1.class, // DJ1.m <: intf J1
|
||||
K1.class, // intf K1 <: intf I <: intf J1, intf J2.m ABSTRACT
|
||||
K2.class, // intf K2.m ABSTRACT <: intf I <: intf J1, intf J2.m ABSTRACT
|
||||
K3.class); // intf K3.m DEFAULT <: intf I <: intf J1, intf J2.m ABSTRACT
|
||||
} else {
|
||||
throw new InternalError("unsupported context: " + contextClass());
|
||||
}
|
||||
assertCompiled();
|
||||
|
||||
// 3. Dependency invalidation
|
||||
@ -742,5 +882,28 @@ public class StrengthReduceInterfaceCall {
|
||||
});
|
||||
assertCompiled();
|
||||
}
|
||||
|
||||
Class<?> contextClass() {
|
||||
return I.class;
|
||||
}
|
||||
|
||||
public static class TestMH extends ThreeLevelDefaultHierarchy1 {
|
||||
static final MethodHandle TEST_MH = findVirtualHelper(I.class, "m", Object.class, MethodHandles.lookup());
|
||||
|
||||
@Override
|
||||
public void checkInvalidReceiver() {
|
||||
// receiver type check failures trigger nmethod invalidation
|
||||
}
|
||||
|
||||
@Override
|
||||
Class<?> contextClass() {
|
||||
return J2.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object test(I obj) throws Throwable {
|
||||
return TEST_MH.invokeExact(obj); // invokeinterface I.m()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user