63e611cd5d
Reviewed-by: chagedorn, kvn
747 lines
30 KiB
Java
747 lines
30 KiB
Java
/*
|
|
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
/*
|
|
* @test
|
|
* @requires !vm.graal.enabled
|
|
* @requires vm.opt.StressUnstableIfTraps == null | !vm.opt.StressUnstableIfTraps
|
|
* @modules java.base/jdk.internal.org.objectweb.asm
|
|
* java.base/jdk.internal.misc
|
|
* java.base/jdk.internal.vm.annotation
|
|
* @library /test/lib /
|
|
* @compile Utils.java
|
|
* @build jdk.test.whitebox.WhiteBox
|
|
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
|
|
*
|
|
* @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
|
|
* -XX:+PrintCompilation -XX:+PrintInlining -Xlog:dependencies=debug -verbose:class -XX:CompileCommand=quiet
|
|
* -XX:CompileCommand=compileonly,*::m
|
|
* -XX:CompileCommand=compileonly,*::test -XX:CompileCommand=dontinline,*::test
|
|
* -XX:CompileCommand=compileonly,*::testHelper -XX:CompileCommand=inline,*::testHelper
|
|
* -Xbatch -Xmixed -XX:+WhiteBoxAPI
|
|
* -XX:-TieredCompilation
|
|
* compiler.cha.StrengthReduceInterfaceCall
|
|
*
|
|
* @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
|
|
* -XX:+PrintCompilation -XX:+PrintInlining -Xlog:dependencies=debug -verbose:class -XX:CompileCommand=quiet
|
|
* -XX:CompileCommand=compileonly,*::m
|
|
* -XX:CompileCommand=compileonly,*::test -XX:CompileCommand=dontinline,*::test
|
|
* -XX:CompileCommand=compileonly,*::testHelper -XX:CompileCommand=inline,*::testHelper
|
|
* -Xbatch -Xmixed -XX:+WhiteBoxAPI
|
|
* -XX:+TieredCompilation -XX:TieredStopAtLevel=1
|
|
* compiler.cha.StrengthReduceInterfaceCall
|
|
*/
|
|
package compiler.cha;
|
|
|
|
import jdk.internal.vm.annotation.DontInline;
|
|
|
|
import static compiler.cha.Utils.*;
|
|
|
|
public class StrengthReduceInterfaceCall {
|
|
public static void main(String[] args) {
|
|
run(ObjectToString.class);
|
|
run(ObjectHashCode.class);
|
|
run(TwoLevelHierarchyLinear.class);
|
|
run(ThreeLevelHierarchyLinear.class);
|
|
run(ThreeLevelHierarchyAbstractVsDefault.class);
|
|
run(ThreeLevelDefaultHierarchy.class);
|
|
run(ThreeLevelDefaultHierarchy1.class);
|
|
System.out.println("TEST PASSED");
|
|
}
|
|
|
|
public static class ObjectToString extends ATest<ObjectToString.I> {
|
|
public ObjectToString() { super(I.class, C.class); }
|
|
|
|
interface J { String toString(); }
|
|
interface I extends J {}
|
|
|
|
static class C implements I {}
|
|
|
|
interface K1 extends I {}
|
|
interface K2 extends I { String toString(); } // K2.tS() ABSTRACT
|
|
// interface K3 extends I { default String toString() { return "K3"; } // K2.tS() DEFAULT
|
|
|
|
static class D implements I { public String toString() { return "D"; }}
|
|
|
|
static class DJ1 implements J {}
|
|
static class DJ2 implements J { public String toString() { return "DJ2"; }}
|
|
|
|
@Override
|
|
public Object test(I i) { return ObjectToStringHelper.testHelper(i); /* invokeinterface I.toString() */ }
|
|
|
|
@TestCase
|
|
public void testMono() {
|
|
// 0. Trigger compilation of a monomorphic call site
|
|
compile(monomophic()); // C1 <: C <: intf I <: intf J <: Object.toString()
|
|
assertCompiled();
|
|
|
|
// Dependency: none
|
|
|
|
call(new C() { public String toString() { return "Cn"; }}); // Cn.tS <: C.tS <: intf I
|
|
assertCompiled();
|
|
}
|
|
|
|
@TestCase
|
|
public void testBi() {
|
|
// 0. Trigger compilation of a bimorphic call site
|
|
compile(bimorphic()); // C1 <: C <: intf I <: intf J <: Object.toString()
|
|
assertCompiled();
|
|
|
|
// Dependency: none
|
|
|
|
call(new C() { public String toString() { return "Cn"; }}); // Cn.tS <: C.tS <: intf I
|
|
assertCompiled();
|
|
}
|
|
|
|
@TestCase
|
|
public void testMega() {
|
|
// 0. Trigger compilation of a megamorphic call site
|
|
compile(megamorphic()); // C1,C2,C3 <: C <: intf I <: intf J <: Object.toString()
|
|
assertCompiled();
|
|
|
|
// Dependency: none
|
|
// compiler.cha.StrengthReduceInterfaceCall$ObjectToString::test (5 bytes)
|
|
// @ 1 compiler.cha.StrengthReduceInterfaceCall$ObjectToStringHelper::test (7 bytes) inline (hot)
|
|
// @ 1 java.lang.Object::toString (36 bytes) virtual call
|
|
|
|
// No dependency - no invalidation
|
|
repeat(100, () -> call(new C(){})); // Cn <: C <: intf I
|
|
assertCompiled();
|
|
|
|
initialize(K1.class, // intf K1 <: intf I <: intf J
|
|
K2.class, // intf K2.tS ABSTRACT <: intf I <: intf J
|
|
DJ1.class, // DJ1 <: intf J
|
|
DJ2.class); // DJ2.tS <: intf J
|
|
assertCompiled();
|
|
|
|
initialize(D.class); // D.tS <: intf I <: intf J
|
|
assertCompiled();
|
|
|
|
call(new C() { public String toString() { return "Cn"; }}); // Cn.tS <: C.tS <: intf I
|
|
assertCompiled();
|
|
}
|
|
|
|
@Override
|
|
public void checkInvalidReceiver() {
|
|
shouldThrow(IncompatibleClassChangeError.class, () -> {
|
|
I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated
|
|
test(o);
|
|
});
|
|
assertCompiled();
|
|
|
|
shouldThrow(IncompatibleClassChangeError.class, () -> {
|
|
I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J() {}); // super interface
|
|
test(j);
|
|
});
|
|
assertCompiled();
|
|
}
|
|
}
|
|
|
|
public static class ObjectHashCode extends ATest<ObjectHashCode.I> {
|
|
public ObjectHashCode() { super(I.class, C.class); }
|
|
|
|
interface J {}
|
|
interface I extends J {}
|
|
|
|
static class C implements I {}
|
|
|
|
interface K1 extends I {}
|
|
interface K2 extends I { int hashCode(); } // K2.hC() ABSTRACT
|
|
// interface K3 extends I { default int hashCode() { return CORRECT; } // K2.hC() DEFAULT
|
|
|
|
static class D implements I { public int hashCode() { return super.hashCode(); }}
|
|
|
|
static class DJ1 implements J {}
|
|
static class DJ2 implements J { public int hashCode() { return super.hashCode(); }}
|
|
|
|
@Override
|
|
public Object test(I i) {
|
|
return ObjectHashCodeHelper.testHelper(i); /* invokeinterface I.hashCode() */
|
|
}
|
|
|
|
@TestCase
|
|
public void testMono() {
|
|
// 0. Trigger compilation of a monomorphic call site
|
|
compile(monomophic()); // C1 <: C <: intf I <: intf J <: Object.hashCode()
|
|
assertCompiled();
|
|
|
|
// Dependency: none
|
|
|
|
call(new C() { public int hashCode() { return super.hashCode(); }}); // Cn.hC <: C.hC <: intf I
|
|
assertCompiled();
|
|
}
|
|
|
|
@TestCase
|
|
public void testBi() {
|
|
// 0. Trigger compilation of a bimorphic call site
|
|
compile(bimorphic()); // C1 <: C <: intf I <: intf J <: Object.toString()
|
|
assertCompiled();
|
|
|
|
// Dependency: none
|
|
|
|
call(new C() { public int hashCode() { return super.hashCode(); }}); // Cn.hC <: C.hC <: intf I
|
|
assertCompiled();
|
|
}
|
|
|
|
@TestCase
|
|
public void testMega() {
|
|
// 0. Trigger compilation of a megamorphic call site
|
|
compile(megamorphic()); // C1,C2,C3 <: C <: intf I <: intf J <: Object.hashCode()
|
|
assertCompiled();
|
|
|
|
// Dependency: none
|
|
|
|
// No dependency - no invalidation
|
|
repeat(100, () -> call(new C(){})); // Cn <: C <: intf I
|
|
assertCompiled();
|
|
|
|
initialize(K1.class, // intf K1 <: intf I <: intf J
|
|
K2.class, // intf K2.hC ABSTRACT <: intf I <: intf J
|
|
DJ1.class, // DJ1 <: intf J
|
|
DJ2.class); // DJ2.hC <: intf J
|
|
assertCompiled();
|
|
|
|
initialize(D.class); // D.hC <: intf I <: intf J
|
|
assertCompiled();
|
|
|
|
call(new C() { public int hashCode() { return super.hashCode(); }}); // Cn.hC <: C.hC <: intf I
|
|
assertCompiled();
|
|
}
|
|
|
|
@Override
|
|
public void checkInvalidReceiver() {
|
|
shouldThrow(IncompatibleClassChangeError.class, () -> {
|
|
I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated
|
|
test(o);
|
|
});
|
|
assertCompiled();
|
|
|
|
shouldThrow(IncompatibleClassChangeError.class, () -> {
|
|
I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J() {}); // super interface
|
|
test(j);
|
|
});
|
|
assertCompiled();
|
|
}
|
|
}
|
|
|
|
public static class TwoLevelHierarchyLinear extends ATest<TwoLevelHierarchyLinear.I> {
|
|
public TwoLevelHierarchyLinear() { super(I.class, C.class); }
|
|
|
|
interface J { default Object m() { return WRONG; } }
|
|
|
|
interface I extends J { Object m(); }
|
|
static class C implements I { public Object m() { return CORRECT; }}
|
|
|
|
interface K1 extends I {}
|
|
interface K2 extends I { Object m(); }
|
|
interface K3 extends I { default Object m() { return WRONG; }}
|
|
interface K4 extends I { default Object m() { return CORRECT; }}
|
|
|
|
static class D implements I { public Object m() { return WRONG; }}
|
|
|
|
static class DJ1 implements J {}
|
|
static class DJ2 implements J { public Object m() { return WRONG; }}
|
|
|
|
@DontInline
|
|
public Object test(I i) {
|
|
return i.m();
|
|
}
|
|
|
|
@TestCase
|
|
public void testMega1() {
|
|
// 0. Trigger compilation of a megamorphic call site
|
|
compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I.m ABSTRACT <: intf J.m ABSTRACT
|
|
assertCompiled();
|
|
|
|
// Dependency: type = unique_concrete_method, context = I, method = C.m
|
|
|
|
checkInvalidReceiver(); // ensure proper type check is preserved
|
|
|
|
// 1. No deoptimization/invalidation on not-yet-seen receiver
|
|
repeat(100, () -> call(new C(){})); // Cn <: C.m <: intf I.m ABSTRACT <: intf J.m DEFAULT
|
|
assertCompiled();
|
|
|
|
// 2. No dependency invalidation on class loading of unrelated classes: different context
|
|
initialize(K1.class, // intf K1 <: intf I.m ABSTRACT <: intf J.m DEFAULT
|
|
K2.class, // intf K2.m ABSTRACT <: intf I.m ABSTRACT <: intf J.m DEFAULT
|
|
DJ1.class, // DJ1 <: intf J.m DEFAULT
|
|
DJ2.class); // DJ2.m <: intf J.m DEFAULT
|
|
assertCompiled();
|
|
|
|
// 3. Dependency invalidation on D <: I
|
|
initialize(D.class); // D.m <: intf I.m ABSTRACT <: intf J.m DEFAULT
|
|
assertNotCompiled();
|
|
|
|
// 4. Recompilation: no inlining, no dependencies
|
|
compile(megamorphic());
|
|
call(new C() { public Object m() { return CORRECT; }}); // Cn.m <: C.m <: intf I.m ABSTRACT <: intf J.m DEFAULT
|
|
assertCompiled();
|
|
|
|
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
|
}
|
|
|
|
@TestCase
|
|
public void testMega2() {
|
|
// 0. Trigger compilation of a megamorphic call site
|
|
compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I.m ABSTRACT <: intf J.m DEFAULT
|
|
assertCompiled();
|
|
|
|
// Dependency: type = unique_concrete_method, context = I, method = C.m
|
|
|
|
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
|
|
|
// 1. No invalidation: interfaces don't participate in CHA.
|
|
initialize(K3.class); // intf K3.m DEFAULT <: intf I.m ABSTRACT <: intf J.m DEFAULT
|
|
assertCompiled();
|
|
|
|
// 2. Dependency invalidation on K3n <: I with concrete method.
|
|
call(new K3() { public Object m() { return CORRECT; }}); // K3n.m <: intf K3.m DEFAULT <: intf I.m ABSTRACT <: intf J.m ABSTRACT
|
|
assertNotCompiled();
|
|
|
|
// 3. Recompilation: no inlining, no dependencies
|
|
compile(megamorphic());
|
|
call(new K3() { public Object m() { return CORRECT; }}); // K3n.m <: intf K3.m DEFAULT <: intf I.m ABSTRACT <: intf J.m DEFAULT
|
|
assertCompiled();
|
|
|
|
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
|
}
|
|
|
|
@TestCase
|
|
public void testMega3() {
|
|
// 0. Trigger compilation of a megamorphic call site
|
|
compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I.m ABSTRACT <: intf J.m DEFAULT
|
|
assertCompiled();
|
|
|
|
// Dependency: type = unique_concrete_method, context = I, method = C.m
|
|
|
|
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
|
|
|
// 1. No invalidation: interfaces don't participate in CHA.
|
|
initialize(K4.class); // intf K4.m DEFAULT <: intf I.m ABSTRACT <: intf J.m DEFAULT
|
|
assertCompiled();
|
|
|
|
// 2. Dependency invalidation on K4n <: I with default method.
|
|
call(new K4() { /* default method K4.m */ }); // K4n <: intf K4.m DEFAULT <: intf I.m ABSTRACT <: intf J.m ABSTRACT
|
|
assertNotCompiled();
|
|
|
|
// 3. Recompilation: no inlining, no dependencies
|
|
compile(megamorphic());
|
|
call(new K4() { /* default method K4.m */ }); // K4n <: intf K3.m DEFAULT <: intf I.m ABSTRACT <: intf J.m DEFAULT
|
|
assertCompiled();
|
|
|
|
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
|
}
|
|
|
|
@Override
|
|
public void checkInvalidReceiver() {
|
|
shouldThrow(IncompatibleClassChangeError.class, () -> {
|
|
I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated
|
|
test(o);
|
|
});
|
|
assertCompiled();
|
|
|
|
shouldThrow(IncompatibleClassChangeError.class, () -> {
|
|
I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J() {}); // super interface
|
|
test(j);
|
|
});
|
|
assertCompiled();
|
|
}
|
|
}
|
|
|
|
public static class ThreeLevelHierarchyLinear extends ATest<ThreeLevelHierarchyLinear.I> {
|
|
public ThreeLevelHierarchyLinear() { super(I.class, C.class); }
|
|
|
|
interface J { Object m(); }
|
|
interface I extends J {}
|
|
|
|
interface K1 extends I {}
|
|
interface K2 extends I { Object m(); }
|
|
interface K3 extends I { default Object m() { return WRONG; }}
|
|
interface K4 extends I { default Object m() { return CORRECT; }}
|
|
|
|
static class C implements I { public Object m() { return CORRECT; }}
|
|
|
|
static class DI implements I { public Object m() { return WRONG; }}
|
|
static class DJ implements J { public Object m() { return WRONG; }}
|
|
|
|
@DontInline
|
|
public Object test(I i) {
|
|
return i.m(); // I <: J.m ABSTRACT
|
|
}
|
|
|
|
@TestCase
|
|
public void testMega1() {
|
|
// 0. Trigger compilation of a megamorphic call site
|
|
compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I <: intf J.m ABSTRACT
|
|
assertCompiled();
|
|
|
|
// Dependency: type = unique_concrete_method, context = I, method = C.m
|
|
|
|
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
|
|
|
// 1. No deoptimization/invalidation on not-yet-seen receiver
|
|
repeat(100, () -> call(new C(){})); // Cn <: C.m <: intf I
|
|
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();
|
|
|
|
// 3. Dependency invalidation: DI.m <: I
|
|
initialize(DI.class); // DI.m <: intf I <: intf J.m ABSTRACT
|
|
assertNotCompiled();
|
|
|
|
// 4. Recompilation w/o a dependency
|
|
compile(megamorphic());
|
|
call(new C() { public Object m() { return CORRECT; }}); // Cn.m <: C.m <: intf I <: intf J.m ABSTRACT
|
|
assertCompiled(); // no dependency
|
|
|
|
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
|
}
|
|
|
|
@TestCase
|
|
public void testMega2() {
|
|
compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I <: intf J.m ABSTRACT
|
|
assertCompiled();
|
|
|
|
// Dependency: type = unique_concrete_method, context = I, method = C.m
|
|
|
|
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
|
|
|
// No invalidation: interfaces don't participate in CHA.
|
|
initialize(K3.class); // intf K3.m DEFAULT <: intf I;
|
|
assertCompiled();
|
|
|
|
// Dependency: type = unique_concrete_method, context = I, method = C.m
|
|
|
|
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
|
|
|
call(new K3() { public Object m() { return CORRECT; }}); // Kn.m <: K3.m DEFAULT <: intf I <: intf J.m ABSTRACT
|
|
assertNotCompiled();
|
|
|
|
// Recompilation w/o a dependency
|
|
compile(megamorphic());
|
|
// Dependency: none
|
|
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
|
call(new C() { public Object m() { return CORRECT; }}); // Cn.m <: C.m <: intf I <: intf J.m ABSTRACT
|
|
assertCompiled();
|
|
}
|
|
|
|
@TestCase
|
|
public void testMega3() {
|
|
compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I <: intf J.m ABSTRACT
|
|
assertCompiled();
|
|
|
|
// Dependency: type = unique_concrete_method, context = I, method = C.m
|
|
|
|
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
|
|
|
// No invalidation: interfaces don't participate in CHA.
|
|
initialize(K4.class); // intf K3.m DEFAULT <: intf I;
|
|
assertCompiled();
|
|
|
|
// Dependency: type = unique_concrete_method, context = I, method = C.m
|
|
|
|
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
|
|
|
call(new K4() { /* default method K4.m */ }); // K4n <: K4.m DEFAULT <: intf I <: intf J.m ABSTRACT
|
|
assertNotCompiled();
|
|
|
|
// Recompilation w/o a dependency
|
|
compile(megamorphic());
|
|
// Dependency: none
|
|
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
|
call(new C() { public Object m() { return CORRECT; }}); // Cn.m <: C.m <: intf I <: intf J.m ABSTRACT
|
|
assertCompiled();
|
|
}
|
|
|
|
@Override
|
|
public void checkInvalidReceiver() {
|
|
shouldThrow(IncompatibleClassChangeError.class, () -> {
|
|
I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated
|
|
test(o);
|
|
});
|
|
assertCompiled();
|
|
|
|
shouldThrow(IncompatibleClassChangeError.class, () -> {
|
|
I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J() { public Object m() { return WRONG; }}); // super interface
|
|
test(j);
|
|
});
|
|
assertCompiled();
|
|
}
|
|
}
|
|
|
|
public static class ThreeLevelHierarchyAbstractVsDefault extends ATest<ThreeLevelHierarchyAbstractVsDefault.I> {
|
|
public ThreeLevelHierarchyAbstractVsDefault() { super(I.class, C.class); }
|
|
|
|
interface J1 { default Object m() { return WRONG; } } // intf J1.m DEFAULT
|
|
interface J2 extends J1 { Object m(); } // intf J2.m ABSTRACT <: intf J1
|
|
interface I extends J1, J2 {} // intf I.m OVERPASS <: intf J1,J2
|
|
|
|
static class C implements I { public Object m() { return CORRECT; }}
|
|
|
|
@DontInline
|
|
public Object test(I i) {
|
|
return i.m(); // intf I.m OVERPASS
|
|
}
|
|
|
|
static class DI implements I { public Object m() { return WRONG; }}
|
|
|
|
static class DJ11 implements J1 {}
|
|
static class DJ12 implements J1 { public Object m() { return WRONG; }}
|
|
|
|
static class DJ2 implements J2 { public Object m() { return WRONG; }}
|
|
|
|
interface K11 extends J1 {}
|
|
interface K12 extends J1 { Object m(); }
|
|
interface K13 extends J1 { default Object m() { return WRONG; }}
|
|
interface K21 extends J2 {}
|
|
interface K22 extends J2 { Object m(); }
|
|
interface K23 extends J2 { default Object m() { return WRONG; }}
|
|
|
|
|
|
public void testMega1() {
|
|
// 0. Trigger compilation of megamorphic call site
|
|
compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I.m OVERPASS <: intf J2.m ABSTRACT <: intf J1.m DEFAULT
|
|
assertCompiled();
|
|
|
|
// Dependency: type = unique_concrete_method, context = I, method = C.m
|
|
|
|
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
|
|
|
// 1. No deopt/invalidation on not-yet-seen receiver
|
|
repeat(100, () -> call(new C(){})); // Cn <: C.m <: intf I.m OVERPASS <: intf J2.m ABSTRACT <: intf J1.m DEFAULT
|
|
assertCompiled();
|
|
|
|
// 2. No dependency invalidation: different context
|
|
initialize(K11.class, K12.class, K13.class,
|
|
K21.class, K22.class, K23.class);
|
|
|
|
// 3. Dependency invalidation: Cn.m <: C <: I
|
|
call(new C() { public Object m() { return CORRECT; }}); // Cn.m <: C.m <: intf I.m OVERPASS <: intf J2.m ABSTRACT <: intf J1.m DEFAULT
|
|
assertNotCompiled();
|
|
|
|
// 4. Recompilation w/o a dependency
|
|
compile(megamorphic());
|
|
call(new C() { public Object m() { return CORRECT; }});
|
|
assertCompiled(); // no inlining
|
|
|
|
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
|
}
|
|
|
|
public void testMega2() {
|
|
// 0. Trigger compilation of a megamorphic call site
|
|
compile(megamorphic());
|
|
assertCompiled();
|
|
|
|
// Dependency: type = unique_concrete_method, context = I, method = C.m
|
|
|
|
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
|
|
|
// 1. No dependency invalidation: different context
|
|
initialize(DJ11.class,
|
|
DJ12.class,
|
|
DJ2.class);
|
|
assertCompiled();
|
|
|
|
// 2. Dependency invalidation: DI.m <: I
|
|
initialize(DI.class);
|
|
assertNotCompiled();
|
|
|
|
// 3. Recompilation w/o a dependency
|
|
compile(megamorphic());
|
|
call(new C() { public Object m() { return CORRECT; }});
|
|
assertCompiled(); // no inlining
|
|
|
|
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
|
}
|
|
|
|
@Override
|
|
public void checkInvalidReceiver() {
|
|
shouldThrow(IncompatibleClassChangeError.class, () -> {
|
|
I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated
|
|
test(o);
|
|
});
|
|
assertCompiled();
|
|
|
|
shouldThrow(IncompatibleClassChangeError.class, () -> {
|
|
I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J1() {}); // super interface
|
|
test(j);
|
|
});
|
|
assertCompiled();
|
|
|
|
shouldThrow(IncompatibleClassChangeError.class, () -> {
|
|
I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J2() { public Object m() { return WRONG; }}); // super interface
|
|
test(j);
|
|
});
|
|
assertCompiled();
|
|
}
|
|
}
|
|
|
|
public static class ThreeLevelDefaultHierarchy extends ATest<ThreeLevelDefaultHierarchy.I> {
|
|
public ThreeLevelDefaultHierarchy() { super(I.class, C.class); }
|
|
|
|
interface J { default Object m() { return WRONG; }}
|
|
interface I extends J {}
|
|
|
|
static class C implements I { public Object m() { return CORRECT; }}
|
|
|
|
interface K1 extends I {}
|
|
interface K2 extends I { Object m(); }
|
|
interface K3 extends J { default Object m() { return WRONG; }}
|
|
|
|
static class DI implements I { public Object m() { return WRONG; }}
|
|
static class DJ implements J { public Object m() { return WRONG; }}
|
|
static class DK3 implements K3 {}
|
|
|
|
@DontInline
|
|
public Object test(I i) {
|
|
return i.m();
|
|
}
|
|
|
|
@TestCase
|
|
public void testMega() {
|
|
// 0. Trigger compilation of a megamorphic call site
|
|
compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I <: intf J.m ABSTRACT
|
|
assertCompiled();
|
|
|
|
// Dependency: type = unique_concrete_method, context = I, method = C.m
|
|
|
|
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
|
|
|
// 1. No deoptimization/invalidation on not-yet-seen receiver
|
|
repeat(100, () -> call(new C() {}));
|
|
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();
|
|
|
|
// 3. Dependency invalidation
|
|
initialize(DI.class); // DI.m <: intf I <: intf J.m ABSTRACT
|
|
assertNotCompiled();
|
|
|
|
// 4. Recompilation w/o a dependency
|
|
compile(megamorphic());
|
|
call(new C() { public Object m() { return CORRECT; }});
|
|
assertCompiled(); // no inlining
|
|
}
|
|
|
|
@Override
|
|
public void checkInvalidReceiver() {
|
|
shouldThrow(IncompatibleClassChangeError.class, () -> {
|
|
I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated
|
|
test(o);
|
|
});
|
|
assertCompiled();
|
|
|
|
shouldThrow(IncompatibleClassChangeError.class, () -> {
|
|
I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J() {}); // super interface
|
|
test(j);
|
|
});
|
|
assertCompiled();
|
|
}
|
|
}
|
|
|
|
public static class ThreeLevelDefaultHierarchy1 extends ATest<ThreeLevelDefaultHierarchy1.I> {
|
|
public ThreeLevelDefaultHierarchy1() { super(I.class, C.class); }
|
|
|
|
interface J1 { Object m();}
|
|
interface J2 extends J1 { default Object m() { return WRONG; } }
|
|
interface I extends J1, J2 {}
|
|
|
|
static class C implements I { public Object m() { return CORRECT; }}
|
|
|
|
interface K1 extends I {}
|
|
interface K2 extends I { Object m(); }
|
|
interface K3 extends I { default Object m() { return WRONG; }}
|
|
|
|
static class DI implements I { public Object m() { return WRONG; }}
|
|
static class DJ1 implements J1 { public Object m() { return WRONG; }}
|
|
static class DJ2 implements J2 { public Object m() { return WRONG; }}
|
|
|
|
@DontInline
|
|
public Object test(I i) {
|
|
return i.m();
|
|
}
|
|
|
|
@TestCase
|
|
public void testMega() {
|
|
// 0. Trigger compilation of a megamorphic call site
|
|
compile(megamorphic());
|
|
assertCompiled();
|
|
|
|
// Dependency: type = unique_concrete_method, context = I, method = C.m
|
|
|
|
checkInvalidReceiver(); // ensure proper type check on receiver is preserved
|
|
|
|
// 1. No deoptimization/invalidation on not-yet-seen receiver
|
|
repeat(100, () -> call(new C() {}));
|
|
assertCompiled();
|
|
|
|
// 2. No dependency invalidation
|
|
initialize(DJ1.class,
|
|
DJ2.class,
|
|
K1.class,
|
|
K2.class,
|
|
K3.class);
|
|
assertCompiled();
|
|
|
|
// 3. Dependency invalidation
|
|
initialize(DI.class); // DI.m <: intf I <: intf J.m ABSTRACT
|
|
assertNotCompiled();
|
|
|
|
// 4. Recompilation w/o a dependency
|
|
compile(megamorphic());
|
|
call(new C() { public Object m() { return CORRECT; }});
|
|
assertCompiled(); // no inlining
|
|
}
|
|
|
|
@Override
|
|
public void checkInvalidReceiver() {
|
|
shouldThrow(IncompatibleClassChangeError.class, () -> {
|
|
I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated
|
|
test(o);
|
|
});
|
|
assertCompiled();
|
|
|
|
shouldThrow(IncompatibleClassChangeError.class, () -> {
|
|
I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J1() { public Object m() { return WRONG; } }); // super interface
|
|
test(j);
|
|
});
|
|
assertCompiled();
|
|
|
|
shouldThrow(IncompatibleClassChangeError.class, () -> {
|
|
I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J2() {}); // super interface
|
|
test(j);
|
|
});
|
|
assertCompiled();
|
|
}
|
|
}
|
|
}
|