mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 22:34:27 +02:00
8062280: C2: inlining failure due to access checks being too strict
Reviewed-by: kvn
This commit is contained in:
parent
ce40bf07b2
commit
5ff12acb9d
8 changed files with 249 additions and 27 deletions
|
@ -688,7 +688,8 @@ bool ciMethod::parameter_profiled_type(int i, ciKlass*& type, bool& maybe_null)
|
|||
// 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 @@ ciMethod* ciMethod::find_monomorphic_target(ciInstanceKlass* caller,
|
|||
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 @@ ciMethod* ciMethod::find_monomorphic_target(ciInstanceKlass* caller,
|
|||
//
|
||||
// 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 @@ ciMethod* ciMethod::resolve_invoke(ciKlass* caller, ciKlass* exact_receiver) {
|
|||
||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -255,11 +255,12 @@ class ciMethod : public ciMetadata {
|
|||
// 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);
|
||||
|
|
|
@ -1406,10 +1406,11 @@ methodHandle LinkResolver::resolve_virtual_call_or_null(
|
|||
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 @@ methodHandle LinkResolver::resolve_interface_call_or_null(
|
|||
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 @@ methodHandle LinkResolver::resolve_static_call_or_null(
|
|||
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 @@ methodHandle LinkResolver::resolve_static_call_or_null(
|
|||
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();
|
||||
|
|
|
@ -181,10 +181,10 @@ class LinkResolver: AllStatic {
|
|||
|
||||
// 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
|
||||
|
|
|
@ -876,7 +876,8 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod*
|
|||
// 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;
|
||||
|
|
|
@ -879,9 +879,11 @@ class Compile : public Phase {
|
|||
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.
|
||||
|
|
|
@ -959,13 +959,15 @@ void Parse::count_compiled_calls(bool at_method_entry, bool is_inline) {
|
|||
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 @@ ciMethod* Compile::optimize_virtual_call(ciMethod* caller, int bci, ciInstanceKl
|
|||
|
||||
// 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 @@ ciMethod* Compile::optimize_inlining(ciMethod* caller, int bci, ciInstanceKlass*
|
|||
}
|
||||
|
||||
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"?
|
||||
|
|
207
hotspot/test/compiler/jsr292/MHInlineTest.java
Normal file
207
hotspot/test/compiler/jsr292/MHInlineTest.java
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue