8062280: C2: inlining failure due to access checks being too strict
Reviewed-by: kvn
--- a/hotspot/src/share/vm/ci/ciMethod.cpp Tue Apr 14 18:03:43 2015 +0300
+++ b/hotspot/src/share/vm/ci/ciMethod.cpp Tue Apr 14 18:11:06 2015 +0300
@@ -688,7 +688,8 @@
// via assert_unique_concrete_method or assert_leaf_type.
ciMethod* ciMethod::find_monomorphic_target(ciInstanceKlass* caller,
ciInstanceKlass* callee_holder,
- ciInstanceKlass* actual_recv) {
+ ciInstanceKlass* actual_recv,
+ bool check_access) {
check_is_loaded();
if (actual_recv->is_interface()) {
@@ -696,7 +697,7 @@
return NULL;
}
- ciMethod* root_m = resolve_invoke(caller, actual_recv);
+ ciMethod* root_m = resolve_invoke(caller, actual_recv, check_access);
if (root_m == NULL) {
// Something went wrong looking up the actual receiver method.
return NULL;
@@ -775,7 +776,7 @@
//
// Given a known receiver klass, find the target for the call.
// Return NULL if the call has no target or the target is abstract.
-ciMethod* ciMethod::resolve_invoke(ciKlass* caller, ciKlass* exact_receiver) {
+ciMethod* ciMethod::resolve_invoke(ciKlass* caller, ciKlass* exact_receiver, bool check_access) {
check_is_loaded();
VM_ENTRY_MARK;
@@ -792,9 +793,9 @@
||
InstanceKlass::cast(h_recv())->is_linked() && !exact_receiver->is_interface()) {
if (holder()->is_interface()) {
- m = LinkResolver::resolve_interface_call_or_null(h_recv, h_resolved, h_name, h_signature, caller_klass);
+ m = LinkResolver::resolve_interface_call_or_null(h_recv, h_resolved, h_name, h_signature, caller_klass, check_access);
} else {
- m = LinkResolver::resolve_virtual_call_or_null(h_recv, h_resolved, h_name, h_signature, caller_klass);
+ m = LinkResolver::resolve_virtual_call_or_null(h_recv, h_resolved, h_name, h_signature, caller_klass, check_access);
}
}
--- a/hotspot/src/share/vm/ci/ciMethod.hpp Tue Apr 14 18:03:43 2015 +0300
+++ b/hotspot/src/share/vm/ci/ciMethod.hpp Tue Apr 14 18:11:06 2015 +0300
@@ -255,11 +255,12 @@
// its calling environment.
ciMethod* find_monomorphic_target(ciInstanceKlass* caller,
ciInstanceKlass* callee_holder,
- ciInstanceKlass* actual_receiver);
+ ciInstanceKlass* actual_receiver,
+ bool check_access = true);
// Given a known receiver klass, find the target for the call.
// Return NULL if the call has no target or is abstract.
- ciMethod* resolve_invoke(ciKlass* caller, ciKlass* exact_receiver);
+ ciMethod* resolve_invoke(ciKlass* caller, ciKlass* exact_receiver, bool check_access = true);
// Find the proper vtable index to invoke this method.
int resolve_vtable_index(ciKlass* caller, ciKlass* receiver);
--- a/hotspot/src/share/vm/interpreter/linkResolver.cpp Tue Apr 14 18:03:43 2015 +0300
+++ b/hotspot/src/share/vm/interpreter/linkResolver.cpp Tue Apr 14 18:11:06 2015 +0300
@@ -1406,10 +1406,11 @@
KlassHandle resolved_klass,
Symbol* name,
Symbol* signature,
- KlassHandle current_klass) {
+ KlassHandle current_klass,
+ bool check_access) {
EXCEPTION_MARK;
CallInfo info;
- resolve_virtual_call(info, Handle(), receiver_klass, resolved_klass, name, signature, current_klass, true, false, THREAD);
+ resolve_virtual_call(info, Handle(), receiver_klass, resolved_klass, name, signature, current_klass, check_access, false, THREAD);
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
return methodHandle();
@@ -1422,10 +1423,11 @@
KlassHandle resolved_klass,
Symbol* name,
Symbol* signature,
- KlassHandle current_klass) {
+ KlassHandle current_klass,
+ bool check_access) {
EXCEPTION_MARK;
CallInfo info;
- resolve_interface_call(info, Handle(), receiver_klass, resolved_klass, name, signature, current_klass, true, false, THREAD);
+ resolve_interface_call(info, Handle(), receiver_klass, resolved_klass, name, signature, current_klass, check_access, false, THREAD);
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
return methodHandle();
@@ -1453,10 +1455,11 @@
KlassHandle resolved_klass,
Symbol* name,
Symbol* signature,
- KlassHandle current_klass) {
+ KlassHandle current_klass,
+ bool check_access) {
EXCEPTION_MARK;
CallInfo info;
- resolve_static_call(info, resolved_klass, name, signature, current_klass, true, false, THREAD);
+ resolve_static_call(info, resolved_klass, name, signature, current_klass, check_access, false, THREAD);
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
return methodHandle();
@@ -1464,11 +1467,15 @@
return info.selected_method();
}
-methodHandle LinkResolver::resolve_special_call_or_null(KlassHandle resolved_klass, Symbol* name, Symbol* signature,
- KlassHandle current_klass) {
+methodHandle LinkResolver::resolve_special_call_or_null(
+ KlassHandle resolved_klass,
+ Symbol* name,
+ Symbol* signature,
+ KlassHandle current_klass,
+ bool check_access) {
EXCEPTION_MARK;
CallInfo info;
- resolve_special_call(info, resolved_klass, name, signature, current_klass, true, THREAD);
+ resolve_special_call(info, resolved_klass, name, signature, current_klass, check_access, THREAD);
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
return methodHandle();
--- a/hotspot/src/share/vm/interpreter/linkResolver.hpp Tue Apr 14 18:03:43 2015 +0300
+++ b/hotspot/src/share/vm/interpreter/linkResolver.hpp Tue Apr 14 18:11:06 2015 +0300
@@ -181,10 +181,10 @@
// same as above for compile-time resolution; but returns null handle instead of throwing an exception on error
// also, does not initialize klass (i.e., no side effects)
- static methodHandle resolve_virtual_call_or_null (KlassHandle receiver_klass, KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature, KlassHandle current_klass);
- static methodHandle resolve_interface_call_or_null(KlassHandle receiver_klass, KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature, KlassHandle current_klass);
- static methodHandle resolve_static_call_or_null (KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature, KlassHandle current_klass);
- static methodHandle resolve_special_call_or_null (KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature, KlassHandle current_klass);
+ static methodHandle resolve_virtual_call_or_null (KlassHandle receiver_klass, KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature, KlassHandle current_klass, bool check_access = true);
+ static methodHandle resolve_interface_call_or_null(KlassHandle receiver_klass, KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature, KlassHandle current_klass, bool check_access = true);
+ static methodHandle resolve_static_call_or_null (KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature, KlassHandle current_klass, bool check_access = true);
+ static methodHandle resolve_special_call_or_null (KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature, KlassHandle current_klass, bool check_access = true);
static int vtable_index_of_interface_method(KlassHandle klass, methodHandle resolved_method);
// same as above for compile-time resolution; returns vtable_index if current_klass if linked
--- a/hotspot/src/share/vm/opto/callGenerator.cpp Tue Apr 14 18:03:43 2015 +0300
+++ b/hotspot/src/share/vm/opto/callGenerator.cpp Tue Apr 14 18:11:06 2015 +0300
@@ -876,7 +876,8 @@
// Parse::do_call())
target = C->optimize_virtual_call(caller, jvms->bci(), klass, klass,
target, receiver_type, is_virtual,
- call_does_dispatch, vtable_index); // out-parameters
+ call_does_dispatch, vtable_index, // out-parameters
+ /*check_access=*/false);
// We lack profiling at this call but type speculation may
// provide us with a type
speculative_receiver_type = (receiver_type != NULL) ? receiver_type->speculative_type() : NULL;
--- a/hotspot/src/share/vm/opto/compile.hpp Tue Apr 14 18:03:43 2015 +0300
+++ b/hotspot/src/share/vm/opto/compile.hpp Tue Apr 14 18:11:06 2015 +0300
@@ -879,9 +879,11 @@
ciMethod* optimize_virtual_call(ciMethod* caller, int bci, ciInstanceKlass* klass,
ciKlass* holder, ciMethod* callee,
const TypeOopPtr* receiver_type, bool is_virtual,
- bool &call_does_dispatch, int &vtable_index);
+ bool &call_does_dispatch, int &vtable_index,
+ bool check_access = true);
ciMethod* optimize_inlining(ciMethod* caller, int bci, ciInstanceKlass* klass,
- ciMethod* callee, const TypeOopPtr* receiver_type);
+ ciMethod* callee, const TypeOopPtr* receiver_type,
+ bool check_access = true);
// Report if there were too many traps at a current method and bci.
// Report if a trap was recorded, and/or PerMethodTrapLimit was exceeded.
--- a/hotspot/src/share/vm/opto/doCall.cpp Tue Apr 14 18:03:43 2015 +0300
+++ b/hotspot/src/share/vm/opto/doCall.cpp Tue Apr 14 18:11:06 2015 +0300
@@ -959,13 +959,15 @@
ciMethod* Compile::optimize_virtual_call(ciMethod* caller, int bci, ciInstanceKlass* klass,
ciKlass* holder, ciMethod* callee,
const TypeOopPtr* receiver_type, bool is_virtual,
- bool& call_does_dispatch, int& vtable_index) {
+ bool& call_does_dispatch, int& vtable_index,
+ bool check_access) {
// Set default values for out-parameters.
call_does_dispatch = true;
vtable_index = Method::invalid_vtable_index;
// Choose call strategy.
- ciMethod* optimized_virtual_method = optimize_inlining(caller, bci, klass, callee, receiver_type);
+ ciMethod* optimized_virtual_method = optimize_inlining(caller, bci, klass, callee,
+ receiver_type, check_access);
// Have the call been sufficiently improved such that it is no longer a virtual?
if (optimized_virtual_method != NULL) {
@@ -980,7 +982,8 @@
// Identify possible target method and inlining style
ciMethod* Compile::optimize_inlining(ciMethod* caller, int bci, ciInstanceKlass* klass,
- ciMethod* callee, const TypeOopPtr* receiver_type) {
+ ciMethod* callee, const TypeOopPtr* receiver_type,
+ bool check_access) {
// only use for virtual or interface calls
// If it is obviously final, do not bother to call find_monomorphic_target,
@@ -1020,7 +1023,7 @@
}
ciInstanceKlass* calling_klass = caller->holder();
- ciMethod* cha_monomorphic_target = callee->find_monomorphic_target(calling_klass, klass, actual_receiver);
+ ciMethod* cha_monomorphic_target = callee->find_monomorphic_target(calling_klass, klass, actual_receiver, check_access);
if (cha_monomorphic_target != NULL) {
assert(!cha_monomorphic_target->is_abstract(), "");
// Look at the method-receiver type. Does it add "too much information"?
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/jsr292/MHInlineTest.java Tue Apr 14 18:11:06 2015 +0300
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2015, 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
+ * @bug 8062280
+ * @summary C2: inlining failure due to access checks being too strict
+ * @library /testlibrary
+ * @run main/othervm MHInlineTest
+ */
+import java.lang.invoke.*;
+import com.oracle.java.testlibrary.*;
+import static com.oracle.java.testlibrary.Asserts.*;
+
+public class MHInlineTest {
+ public static void main(String[] args) throws Exception {
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+ "-XX:+IgnoreUnrecognizedVMOptions", "-showversion",
+ "-server", "-XX:-TieredCompilation", "-Xbatch",
+ "-XX:+PrintCompilation", "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintInlining",
+ "-XX:CompileCommand=dontinline,MHInlineTest::test*",
+ "MHInlineTest$Launcher");
+
+ OutputAnalyzer analyzer = new OutputAnalyzer(pb.start());
+
+ analyzer.shouldHaveExitValue(0);
+
+ // The test is applicable only to C2 (present in Server VM).
+ if (analyzer.getStderr().contains("Server VM")) {
+ analyzer.shouldContain("MHInlineTest$B::public_x (3 bytes) inline (hot)");
+ analyzer.shouldContain( "MHInlineTest$B::protected_x (3 bytes) inline (hot)");
+ analyzer.shouldContain( "MHInlineTest$B::package_x (3 bytes) inline (hot)");
+ analyzer.shouldContain("MHInlineTest$A::package_final_x (3 bytes) inline (hot)");
+ analyzer.shouldContain("MHInlineTest$B::private_x (3 bytes) inline (hot)");
+ analyzer.shouldContain("MHInlineTest$B::private_static_x (3 bytes) inline (hot)");
+ analyzer.shouldContain("MHInlineTest$A::package_static_x (3 bytes) inline (hot)");
+
+ analyzer.shouldNotContain("MHInlineTest$A::protected_x (3 bytes) virtual call");
+ analyzer.shouldNotContain("MHInlineTest$A::package_x (3 bytes) virtual call");
+ }
+ }
+
+ static class A {
+ public static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+
+ public Class<?> public_x() { return A.class; }
+ protected Class<?> protected_x() { return A.class; }
+ Class<?> package_x() { return A.class; }
+ final Class<?> package_final_x() { return A.class; }
+
+ static Class<?> package_static_x() { return A.class; }
+ }
+
+ static class B extends A {
+ public static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+
+ @Override public Class<?> public_x() { return B.class; }
+ @Override protected Class<?> protected_x() { return B.class; }
+ @Override Class<?> package_x() { return B.class; }
+
+ private Class<?> private_x() { return B.class; }
+ static Class<?> private_static_x() { return B.class; }
+ }
+
+ static final MethodHandle A_PUBLIC_X;
+ static final MethodHandle A_PROTECTED_X;
+ static final MethodHandle A_PACKAGE_X;
+ static final MethodHandle A_PACKAGE_STATIC_X;
+ static final MethodHandle A_PACKAGE_FINAL_X;
+
+ static final MethodHandle B_PRIVATE_X;
+ static final MethodHandle B_PRIVATE_STATIC_X;
+
+ static {
+ try {
+ MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+
+ A_PUBLIC_X = LOOKUP.findVirtual(
+ A.class, "public_x", MethodType.methodType(Class.class));
+ A_PROTECTED_X = LOOKUP.findVirtual(
+ A.class, "protected_x", MethodType.methodType(Class.class));
+ A_PACKAGE_X = LOOKUP.findVirtual(
+ A.class, "package_x", MethodType.methodType(Class.class));
+ A_PACKAGE_FINAL_X = LOOKUP.findVirtual(
+ A.class, "package_final_x", MethodType.methodType(Class.class));
+ A_PACKAGE_STATIC_X = LOOKUP.findStatic(
+ A.class, "package_static_x", MethodType.methodType(Class.class));
+
+ B_PRIVATE_X = B.LOOKUP.findVirtual(
+ B.class, "private_x", MethodType.methodType(Class.class));
+ B_PRIVATE_STATIC_X = B.LOOKUP.findStatic(
+ B.class, "private_static_x", MethodType.methodType(Class.class));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+
+ static final A a = new B();
+
+ private static void testPublicMH() {
+ try {
+ Class<?> r = (Class<?>)A_PUBLIC_X.invokeExact(a);
+ assertEquals(r, B.class);
+ } catch (Throwable throwable) {
+ throw new Error(throwable);
+ }
+ }
+
+ private static void testProtectedMH() {
+ try {
+ Class<?> r = (Class<?>)A_PROTECTED_X.invokeExact(a);
+ assertEquals(r, B.class);
+ } catch (Throwable throwable) {
+ throw new Error(throwable);
+ }
+ }
+
+ private static void testPackageMH() {
+ try {
+ Class<?> r = (Class<?>)A_PACKAGE_X.invokeExact(a);
+ assertEquals(r, B.class);
+ } catch (Throwable throwable) {
+ throw new Error(throwable);
+ }
+ }
+
+ private static void testPackageFinalMH() {
+ try {
+ Class<?> r = (Class<?>)A_PACKAGE_FINAL_X.invokeExact(a);
+ assertEquals(r, A.class);
+ } catch (Throwable throwable) {
+ throw new Error(throwable);
+ }
+ }
+
+ private static void testPackageStaticMH() {
+ try {
+ Class<?> r = (Class<?>)A_PACKAGE_STATIC_X.invokeExact();
+ assertEquals(r, A.class);
+ } catch (Throwable throwable) {
+ throw new Error(throwable);
+ }
+ }
+
+ private static void testPrivateMH() {
+ try {
+ Class<?> r = (Class<?>)B_PRIVATE_X.invokeExact((B)a);
+ assertEquals(r, B.class);
+ } catch (Throwable throwable) {
+ throw new Error(throwable);
+ }
+ }
+
+ private static void testPrivateStaticMH() {
+ try {
+ Class<?> r = (Class<?>)B_PRIVATE_STATIC_X.invokeExact();
+ assertEquals(r, B.class);
+ } catch (Throwable throwable) {
+ throw new Error(throwable);
+ }
+ }
+ static class Launcher {
+ public static void main(String[] args) throws Exception {
+ for (int i = 0; i < 20_000; i++) {
+ testPublicMH();
+ }
+ for (int i = 0; i < 20_000; i++) {
+ testProtectedMH();
+ }
+ for (int i = 0; i < 20_000; i++) {
+ testPackageMH();
+ }
+ for (int i = 0; i < 20_000; i++) {
+ testPackageFinalMH();
+ }
+ for (int i = 0; i < 20_000; i++) {
+ testPackageStaticMH();
+ }
+ for (int i = 0; i < 20_000; i++) {
+ testPrivateMH();
+ }
+ for (int i = 0; i < 20_000; i++) {
+ testPrivateStaticMH();
+ }
+ }
+ }
+}