8203480: IncompatibleClassChangeError thrown at sites linked to default interface methods
Reviewed-by: kvn
--- a/src/hotspot/share/opto/c2_globals.hpp Thu Jun 07 17:40:15 2018 +0200
+++ b/src/hotspot/share/opto/c2_globals.hpp Wed Jun 06 23:36:08 2018 +0300
@@ -53,6 +53,9 @@
diagnostic(bool, StressGCM, false, \
"Randomize instruction scheduling in GCM") \
\
+ develop(bool, StressMethodHandleLinkerInlining, false, \
+ "Stress inlining through method handle linkers") \
+ \
develop(intx, OptoPrologueNops, 0, \
"Insert this many extra nop instructions " \
"in the prologue of every nmethod") \
--- a/src/hotspot/share/opto/callGenerator.cpp Thu Jun 07 17:40:15 2018 +0200
+++ b/src/hotspot/share/opto/callGenerator.cpp Wed Jun 06 23:36:08 2018 +0300
@@ -932,7 +932,7 @@
speculative_receiver_type = (receiver_type != NULL) ? receiver_type->speculative_type() : NULL;
}
CallGenerator* cg = C->call_generator(target, vtable_index, call_does_dispatch, jvms,
- true /* allow_inline */,
+ !StressMethodHandleLinkerInlining /* allow_inline */,
PROB_ALWAYS,
speculative_receiver_type);
return cg;
--- a/src/hotspot/share/runtime/sharedRuntime.cpp Thu Jun 07 17:40:15 2018 +0200
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp Wed Jun 06 23:36:08 2018 +0300
@@ -1082,6 +1082,7 @@
Bytecode_invoke bytecode(caller, bci);
int bytecode_index = bytecode.index();
+ bc = bytecode.invoke_code();
methodHandle attached_method = extract_attached_method(vfst);
if (attached_method.not_null()) {
@@ -1095,6 +1096,11 @@
// Adjust invocation mode according to the attached method.
switch (bc) {
+ case Bytecodes::_invokevirtual:
+ if (attached_method->method_holder()->is_interface()) {
+ bc = Bytecodes::_invokeinterface;
+ }
+ break;
case Bytecodes::_invokeinterface:
if (!attached_method->method_holder()->is_interface()) {
bc = Bytecodes::_invokevirtual;
@@ -1110,10 +1116,10 @@
break;
}
}
- } else {
- bc = bytecode.invoke_code();
}
+ assert(bc != Bytecodes::_illegal, "not initialized");
+
bool has_receiver = bc != Bytecodes::_invokestatic &&
bc != Bytecodes::_invokedynamic &&
bc != Bytecodes::_invokehandle;
--- a/test/hotspot/jtreg/compiler/jsr292/NonInlinedCall/InvokeTest.java Thu Jun 07 17:40:15 2018 +0200
+++ b/test/hotspot/jtreg/compiler/jsr292/NonInlinedCall/InvokeTest.java Wed Jun 06 23:36:08 2018 +0300
@@ -52,10 +52,12 @@
public class InvokeTest {
static MethodHandles.Lookup LOOKUP = MethodHandleHelper.IMPL_LOOKUP;
- static final MethodHandle virtualMH; // invokevirtual T.f1
- static final MethodHandle staticMH; // invokestatic T.f2
- static final MethodHandle intfMH; // invokeinterface I.f1
- static final MethodHandle specialMH; // invokespecial T.f4 T
+ static final MethodHandle virtualMH; // invokevirtual T.f1
+ static final MethodHandle staticMH; // invokestatic T.f2
+ static final MethodHandle intfMH; // invokeinterface I.f3
+ static final MethodHandle defaultMH; // invokevirtual T.f3
+ static final MethodHandle specialMH; // invokespecial T.f4 T
+ static final MethodHandle privateMH; // invokespecial I.f4 T
static final MethodHandle basicMH;
static final WhiteBox WB = WhiteBox.getWhiteBox();
@@ -69,7 +71,9 @@
virtualMH = LOOKUP.findVirtual(T.class, "f1", mtype);
staticMH = LOOKUP.findStatic (T.class, "f2", mtype);
intfMH = LOOKUP.findVirtual(I.class, "f3", mtype);
+ defaultMH = LOOKUP.findVirtual(T.class, "f3", mtype);
specialMH = LOOKUP.findSpecial(T.class, "f4", mtype, T.class);
+ privateMH = LOOKUP.findSpecial(I.class, "f4", mtype, I.class);
basicMH = NonInlinedReinvoker.make(staticMH);
} catch (Exception e) {
throw new Error(e);
@@ -92,24 +96,51 @@
@DontInline public Class<?> f3() { if (doDeopt) WB.deoptimizeAll(); return P2.class; }
}
- static interface I {
+ interface I {
@DontInline default Class<?> f3() { if (doDeopt) WB.deoptimizeAll(); return I.class; }
+ @DontInline private Class<?> f4() { if (doDeopt) WB.deoptimizeAll(); return I.class; }
}
+ interface J1 extends I {
+ @DontInline default Class<?> f3() { if (doDeopt) WB.deoptimizeAll(); return J1.class; }
+ }
+
+ interface J2 extends I {
+ @DontInline default Class<?> f3() { if (doDeopt) WB.deoptimizeAll(); return J2.class; }
+ }
+
+ interface J3 extends I {
+ @DontInline default Class<?> f3() { if (doDeopt) WB.deoptimizeAll(); return J3.class; }
+ }
+
+ static class Q1 extends T implements J1 {}
+ static class Q2 extends T implements J2 {}
+ static class Q3 extends T implements J3 {}
+
@DontInline
- static void linkToVirtual(Object obj, Class<?> extecpted) {
+ static void linkToVirtual(T recv, Class<?> expected) {
try {
- Class<?> cls = (Class<?>)virtualMH.invokeExact((T)obj);
- assertEquals(cls, obj.getClass());
+ Class<?> cls = (Class<?>)virtualMH.invokeExact(recv);
+ assertEquals(cls, expected);
} catch (Throwable e) {
throw new Error(e);
}
}
@DontInline
- static void linkToInterface(Object obj, Class<?> expected) {
+ static void linkToVirtualDefault(T recv, Class<?> expected) {
try {
- Class<?> cls = (Class<?>)intfMH.invokeExact((I)obj);
+ Class<?> cls = (Class<?>)defaultMH.invokeExact(recv);
+ assertEquals(cls, expected);
+ } catch (Throwable e) {
+ throw new Error(e);
+ }
+ }
+
+ @DontInline
+ static void linkToInterface(I recv, Class<?> expected) {
+ try {
+ Class<?> cls = (Class<?>)intfMH.invokeExact(recv);
assertEquals(cls, expected);
} catch (Throwable e) {
throw new Error(e);
@@ -127,9 +158,9 @@
}
@DontInline
- static void linkToSpecial(Object obj, Class<?> expected) {
+ static void linkToSpecial(T recv, Class<?> expected) {
try {
- Class<?> cls = (Class<?>)specialMH.invokeExact((T)obj);
+ Class<?> cls = (Class<?>)specialMH.invokeExact(recv);
assertEquals(cls, expected);
} catch (Throwable e) {
throw new Error(e);
@@ -137,6 +168,17 @@
}
@DontInline
+ static void linkToSpecialIntf(I recv, Class<?> expected) {
+ try {
+ Class<?> cls = (Class<?>)privateMH.invokeExact(recv);
+ assertEquals(cls, expected);
+ } catch (Throwable e) {
+ throw new Error(e);
+ }
+ }
+
+
+ @DontInline
static void invokeBasic() {
try {
Class<?> cls = (Class<?>)MethodHandleHelper.invokeBasicL(basicMH);
@@ -171,13 +213,33 @@
// Monomorphic case (optimized virtual call)
run(() -> linkToVirtual(new T(), T.class));
+ run(() -> linkToVirtualDefault(new T(), I.class));
- // Megamorphic case (virtual call)
- Object[] recv = new Object[] { new T(), new P1(), new P2() };
+ // Megamorphic case (optimized virtual call)
+ run(() -> {
+ linkToVirtual(new T() {}, T.class);
+ linkToVirtual(new T() {}, T.class);
+ linkToVirtual(new T() {}, T.class);
+ });
+
run(() -> {
- for (Object r : recv) {
- linkToVirtual(r, r.getClass());
- }});
+ linkToVirtualDefault(new T(){}, I.class);
+ linkToVirtualDefault(new T(){}, I.class);
+ linkToVirtualDefault(new T(){}, I.class);
+ });
+
+ // Megamorphic case (virtual call), multiple implementations
+ run(() -> {
+ linkToVirtual(new T(), T.class);
+ linkToVirtual(new P1(), P1.class);
+ linkToVirtual(new P2(), P2.class);
+ });
+
+ run(() -> {
+ linkToVirtualDefault(new Q1(), J1.class);
+ linkToVirtualDefault(new Q2(), J2.class);
+ linkToVirtualDefault(new Q3(), J3.class);
+ });
}
static void testInterface() {
@@ -190,17 +252,18 @@
run(() -> linkToInterface(new T(), I.class));
// Megamorphic case (virtual call)
- Object[][] recv = new Object[][] {{new T(), I.class}, {new P1(), P1.class}, {new P2(), P2.class}};
run(() -> {
- for (Object[] r : recv) {
- linkToInterface(r[0], (Class<?>)r[1]);
- }});
+ linkToInterface(new T(), I.class);
+ linkToInterface(new P1(), P1.class);
+ linkToInterface(new P2(), P2.class);
+ });
}
static void testSpecial() {
System.out.println("linkToSpecial");
// Monomorphic case (optimized virtual call)
run(() -> linkToSpecial(new T(), T.class));
+ run(() -> linkToSpecialIntf(new T(), I.class));
}
static void testStatic() {