diff --git a/src/hotspot/share/c1/c1_GraphBuilder.cpp b/src/hotspot/share/c1/c1_GraphBuilder.cpp index b6aa935c6f9..2b7e3125fb4 100644 --- a/src/hotspot/share/c1/c1_GraphBuilder.cpp +++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp @@ -1988,8 +1988,7 @@ void GraphBuilder::invoke(Bytecodes::Code code) { // no point in inlining. ciInstanceKlass* singleton = NULL; ciInstanceKlass* declared_interface = callee_holder; - if (declared_interface->nof_implementors() == 1 && - (!target->is_default_method() || target->is_overpass()) /* CHA doesn't support default methods yet. */) { + if (declared_interface->nof_implementors() == 1) { singleton = declared_interface->implementor(); assert(singleton != NULL && singleton != declared_interface, ""); cha_monomorphic_target = target->find_monomorphic_target(calling_klass, declared_interface, singleton); diff --git a/src/hotspot/share/ci/ciMethod.cpp b/src/hotspot/share/ci/ciMethod.cpp index 460c4443d3f..b7f82ec09b5 100644 --- a/src/hotspot/share/ci/ciMethod.cpp +++ b/src/hotspot/share/ci/ciMethod.cpp @@ -721,11 +721,6 @@ ciMethod* ciMethod::find_monomorphic_target(ciInstanceKlass* caller, VM_ENTRY_MARK; - // Disable CHA for default methods for now - if (root_m->is_default_method()) { - return NULL; - } - methodHandle target; { MutexLocker locker(Compile_lock); diff --git a/src/hotspot/share/code/dependencies.cpp b/src/hotspot/share/code/dependencies.cpp index f21cd69ab7f..907d9de4589 100644 --- a/src/hotspot/share/code/dependencies.cpp +++ b/src/hotspot/share/code/dependencies.cpp @@ -2113,6 +2113,9 @@ Method* Dependencies::find_unique_concrete_method(Klass* ctxk, Method* m, Klass* if (m->is_old()) { return NULL; } + if (m->is_default_method()) { + return NULL; // not supported + } ClassHierarchyWalker wf(m); assert(wf.check_method_context(ctxk, m), "proper context"); wf.record_witnesses(1); diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp index 02039f74efb..36460692d7a 100644 --- a/src/hotspot/share/opto/doCall.cpp +++ b/src/hotspot/share/opto/doCall.cpp @@ -315,8 +315,7 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool ciInstanceKlass* declared_interface = caller->get_declared_method_holder_at_bci(bci)->as_instance_klass(); - if (declared_interface->nof_implementors() == 1 && - (!callee->is_default_method() || callee->is_overpass()) /* CHA doesn't support default methods yet */) { + if (declared_interface->nof_implementors() == 1) { ciInstanceKlass* singleton = declared_interface->implementor(); ciMethod* cha_monomorphic_target = callee->find_monomorphic_target(caller->holder(), declared_interface, singleton); diff --git a/test/hotspot/jtreg/compiler/cha/DefaultRootMethod.java b/test/hotspot/jtreg/compiler/cha/DefaultRootMethod.java new file mode 100644 index 00000000000..354412d7748 --- /dev/null +++ b/test/hotspot/jtreg/compiler/cha/DefaultRootMethod.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2021, 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 & vm.opt.final.UseVtableBasedCHA == true + * @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 sun.hotspot.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -XX:+PrintCompilation -XX:+PrintInlining -XX:+TraceDependencies -verbose:class -XX:CompileCommand=quiet + * -XX:CompileCommand=compileonly,*::m + * -XX:CompileCommand=compileonly,*::test -XX:CompileCommand=dontinline,*::test + * -Xbatch -Xmixed -XX:+WhiteBoxAPI + * -XX:-TieredCompilation + * compiler.cha.DefaultRootMethod + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -XX:+PrintCompilation -XX:+PrintInlining -XX:+TraceDependencies -verbose:class -XX:CompileCommand=quiet + * -XX:CompileCommand=compileonly,*::m + * -XX:CompileCommand=compileonly,*::test -XX:CompileCommand=dontinline,*::test + * -Xbatch -Xmixed -XX:+WhiteBoxAPI + * -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * compiler.cha.DefaultRootMethod + */ +package compiler.cha; + +import static compiler.cha.Utils.*; + +public class DefaultRootMethod { + public static void main(String[] args) { + run(DefaultRoot.class); + run(InheritedDefault.class); + System.out.println("TEST PASSED"); + } + + public static class DefaultRoot extends ATest { + public DefaultRoot() { + super(C.class, D.class); + } + + interface I { default Object m() { return CORRECT; } } + + static class C implements I { /* inherited I.m */} + + static class D extends C { /* inherited I.m */ } + + static abstract class E1 extends C { /* empty */ } + static abstract class E2 extends C { public abstract Object m(); } + static abstract class E3 extends C { public Object m() { return "E3.m"; } } + + interface I1 extends I { Object m(); } + interface I2 extends I { default Object m() { return "I2.m"; } } + + static abstract class F1 extends C implements I1 { } + static abstract class F2 extends C implements I2 { } + + static class G extends C { public Object m() { return CORRECT; } } + + @Override + public Object test(C obj) { + return obj.m(); // invokevirtual C.m() + } + + @Override + public void checkInvalidReceiver() { + // nothing to do: concrete class types are enforced by the verifier + } + + @TestCase + public void test() { + // 0. Trigger compilation of a megamorphic call site + compile(megamorphic()); // Dn <: D.m <: C <: I.m DEFAULT + assertCompiled(); + + // Dependency: type = unique_concrete_method, context = C, method = D.m + + // 1. No invalidation: abstract classes don't participate in CHA. + initialize(E1.class, // ABSTRACT E1 <: C <: I.m DEFAULT + E2.class, // ABSTRACT E2.m ABSTRACT <: C <: I.m DEFAULT + E3.class, // ABSTRACT E3.m <: C <: I.m DEFAULT + F1.class, // ABSTRACT F1 <: C <: I.m DEFAULT, I1.m ABSTRACT + F2.class); // ABSTRACT F2 <: C <: I.m DEFAULT, I2.m DEFAULT + assertCompiled(); + + // 2. Dependency invalidation: G.m <: C <: I.m DEFAULT + load(G.class); + assertCompiled(); + + // 3. Dependency invalidation: G.m <: C <: I.m DEFAULT + initialize(G.class); + assertNotCompiled(); + + // 4. Recompilation: no inlining, no dependencies + compile(megamorphic()); + call(new C() { public Object m() { return CORRECT; } }); // Cn.m <: C <: I.m DEFAULT + call(new G() { public Object m() { return CORRECT; } }); // Gn <: G.m <: C <: I.m DEFAULT + assertCompiled(); + } + } + + public static class InheritedDefault extends ATest { + public InheritedDefault() { + super(C.class, D.class); + } + + interface I { Object m(); } + interface J extends I { default Object m() { return CORRECT; } } + + static abstract class C implements I { /* inherits I.m ABSTRACT */} + + // NB! The class is marked abstract to avoid abstract_with_unique_concrete_subtype dependency + static abstract class D extends C implements J { /* inherits J.m DEFAULT*/ } + + static abstract class E1 extends C { /* empty */ } + static abstract class E2 extends C { public abstract Object m(); } + static abstract class E3 extends C { public Object m() { return "E3.m"; } } + + interface I1 extends I { Object m(); } + interface I2 extends I { default Object m() { return "I2.m"; } } + + static abstract class F1 extends C implements I1 { } + static abstract class F2 extends C implements I2 { } + + interface K extends I { default Object m() { return CORRECT; } } + static class G extends C implements K { /* inherits K.m DEFAULT */ } + + @Override + public Object test(C obj) { + return obj.m(); // invokevirtual C.m() + } + + @Override + public void checkInvalidReceiver() { + // nothing to do: concrete class types are enforced by the verifier + } + + @TestCase + public void test() { + // 0. Trigger compilation of a megamorphic call site + compile(megamorphic()); // Dn <: D.m <: C <: I.m ABSTRACT, J.m DEFAULT + assertCompiled(); + + // Dependency: type = unique_concrete_method, context = C, method = D.m + + // 1. No invalidation: abstract classes don't participate in CHA. + initialize(E1.class, // ABSTRACT E1 <: C <: I.m ABSTRACT + E2.class, // ABSTRACT E2.m ABSTRACT <: C <: I.m ABSTRACT + E3.class, // ABSTRACT E3.m <: C <: I.m ABSTRACT + F1.class, // ABSTRACT F1 <: C <: I.m ABSTRACT, I1.m ABSTRACT + F2.class); // ABSTRACT F2 <: C <: I.m ABSTRACT, I2.m DEFAULT + assertCompiled(); + + // 2. No invalidation: not yet linked classes don't participate in CHA. + load(G.class); + assertCompiled(); + + // 3. Dependency invalidation: G.m <: C <: I.m DEFAULT + initialize(G.class); + assertNotCompiled(); + + // 4. Recompilation: no inlining, no dependencies + compile(megamorphic()); + call(new C() { public Object m() { return CORRECT; } }); // Cn.m <: C <: I.m DEFAULT + call(new G() { public Object m() { return CORRECT; } }); // Gn <: G.m <: C <: I.m DEFAULT + assertCompiled(); + } + } +} diff --git a/test/hotspot/jtreg/compiler/cha/StrengthReduceInterfaceCall.java b/test/hotspot/jtreg/compiler/cha/StrengthReduceInterfaceCall.java index 88a1942bb7f..33f62db80ce 100644 --- a/test/hotspot/jtreg/compiler/cha/StrengthReduceInterfaceCall.java +++ b/test/hotspot/jtreg/compiler/cha/StrengthReduceInterfaceCall.java @@ -610,14 +610,15 @@ 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 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(); // no inlining since J.m is a default method + return i.m(); } @TestCase @@ -626,7 +627,7 @@ public void testMega() { compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I <: intf J.m ABSTRACT assertCompiled(); - // Dependency: none + // Dependency: type = unique_concrete_method, context = I, method = C.m checkInvalidReceiver(); // ensure proper type check on receiver is preserved @@ -634,12 +635,21 @@ public void testMega() { repeat(100, () -> call(new C() {})); assertCompiled(); - // 2. No dependency and no inlining - initialize(DJ.class, // DJ.m <: intf J.m ABSTRACT - DI.class, // DI.m <: intf I <: 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 + // 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 @@ -677,7 +687,7 @@ static class DJ2 implements J2 { public Object m() { return WRONG; }} @DontInline public Object test(I i) { - return i.m(); // no inlining since J.m is a default method + return i.m(); } @TestCase @@ -686,7 +696,7 @@ public void testMega() { compile(megamorphic()); assertCompiled(); - // Dependency: none + // Dependency: type = unique_concrete_method, context = I, method = C.m checkInvalidReceiver(); // ensure proper type check on receiver is preserved @@ -694,15 +704,22 @@ public void testMega() { repeat(100, () -> call(new C() {})); assertCompiled(); - // 2. No dependency, no inlining - // CHA doesn't support default methods yet. + // 2. No dependency invalidation initialize(DJ1.class, DJ2.class, - DI.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