7023639: JSR 292 method handle invocation needs a fast path for compiled code

6984705: JSR 292 method handle creation should not go through JNI

Remove assembly code for JDK 7 chained method handles

Co-authored-by: John Rose <john.r.rose@oracle.com>
Co-authored-by: Michael Haupt <michael.haupt@oracle.com>
Reviewed-by: jrose, twisti, kvn, mhaupt
This commit is contained in:
Christian Thalinger 2012-07-24 10:51:00 -07:00
parent 893817c28d
commit 12901d0e5b
181 changed files with 5760 additions and 14402 deletions

View file

@ -88,8 +88,6 @@ RuntimeStub* SharedRuntime::_resolve_virtual_call_blob;
RuntimeStub* SharedRuntime::_resolve_static_call_blob;
DeoptimizationBlob* SharedRuntime::_deopt_blob;
RicochetBlob* SharedRuntime::_ricochet_blob;
SafepointBlob* SharedRuntime::_polling_page_safepoint_handler_blob;
SafepointBlob* SharedRuntime::_polling_page_return_handler_blob;
@ -109,7 +107,6 @@ void SharedRuntime::generate_stubs() {
_polling_page_safepoint_handler_blob = generate_handler_blob(CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception), false);
_polling_page_return_handler_blob = generate_handler_blob(CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception), true);
generate_ricochet_blob();
generate_deopt_blob();
#ifdef COMPILER2
@ -117,33 +114,6 @@ void SharedRuntime::generate_stubs() {
#endif // COMPILER2
}
//----------------------------generate_ricochet_blob---------------------------
void SharedRuntime::generate_ricochet_blob() {
if (!EnableInvokeDynamic) return; // leave it as a null
// allocate space for the code
ResourceMark rm;
// setup code generation tools
CodeBuffer buffer("ricochet_blob", 256 LP64_ONLY(+ 256), 256); // XXX x86 LP64L: 512, 512
MacroAssembler* masm = new MacroAssembler(&buffer);
int bounce_offset = -1, exception_offset = -1, frame_size_in_words = -1;
MethodHandles::RicochetFrame::generate_ricochet_blob(masm, &bounce_offset, &exception_offset, &frame_size_in_words);
// -------------
// make sure all code is generated
masm->flush();
// failed to generate?
if (bounce_offset < 0 || exception_offset < 0 || frame_size_in_words < 0) {
assert(false, "bad ricochet blob");
return;
}
_ricochet_blob = RicochetBlob::create(&buffer, bounce_offset, exception_offset, frame_size_in_words);
}
#include <math.h>
#ifndef USDT2
@ -527,10 +497,6 @@ address SharedRuntime::raw_exception_handler_for_return_address(JavaThread* thre
if (Interpreter::contains(return_address)) {
return Interpreter::rethrow_exception_entry();
}
// Ricochet frame unwind code
if (SharedRuntime::ricochet_blob() != NULL && SharedRuntime::ricochet_blob()->returns_to_bounce_addr(return_address)) {
return SharedRuntime::ricochet_blob()->exception_addr();
}
guarantee(blob == NULL || !blob->is_runtime_stub(), "caller should have skipped stub");
guarantee(!VtableStubs::contains(return_address), "NULL exceptions in vtables should have been handled already!");
@ -768,13 +734,6 @@ JRT_ENTRY(void, SharedRuntime::throw_StackOverflowError(JavaThread* thread))
throw_and_post_jvmti_exception(thread, exception);
JRT_END
JRT_ENTRY(void, SharedRuntime::throw_WrongMethodTypeException(JavaThread* thread, oopDesc* required, oopDesc* actual))
assert(thread == JavaThread::current() && required->is_oop() && actual->is_oop(), "bad args");
ResourceMark rm;
char* message = SharedRuntime::generate_wrong_method_type_message(thread, required, actual);
throw_and_post_jvmti_exception(thread, vmSymbols::java_lang_invoke_WrongMethodTypeException(), message);
JRT_END
address SharedRuntime::continuation_for_implicit_exception(JavaThread* thread,
address pc,
SharedRuntime::ImplicitExceptionKind exception_kind)
@ -857,6 +816,12 @@ address SharedRuntime::continuation_for_implicit_exception(JavaThread* thread,
return StubRoutines::throw_NullPointerException_at_call_entry();
}
if (nm->method()->is_method_handle_intrinsic()) {
// exception happened inside MH dispatch code, similar to a vtable stub
Events::log_exception(thread, "NullPointerException in MH adapter " INTPTR_FORMAT, pc);
return StubRoutines::throw_NullPointerException_at_call_entry();
}
#ifndef PRODUCT
_implicit_null_throws++;
#endif
@ -1045,16 +1010,17 @@ Handle SharedRuntime::find_callee_info_helper(JavaThread* thread,
assert(!vfst.at_end(), "Java frame must exist");
// Find caller and bci from vframe
methodHandle caller (THREAD, vfst.method());
int bci = vfst.bci();
methodHandle caller(THREAD, vfst.method());
int bci = vfst.bci();
// Find bytecode
Bytecode_invoke bytecode(caller, bci);
bc = bytecode.java_code();
bc = bytecode.invoke_code();
int bytecode_index = bytecode.index();
// Find receiver for non-static call
if (bc != Bytecodes::_invokestatic) {
if (bc != Bytecodes::_invokestatic &&
bc != Bytecodes::_invokedynamic) {
// This register map must be update since we need to find the receiver for
// compiled frames. The receiver might be in a register.
RegisterMap reg_map2(thread);
@ -1075,25 +1041,32 @@ Handle SharedRuntime::find_callee_info_helper(JavaThread* thread,
}
// Resolve method. This is parameterized by bytecode.
constantPoolHandle constants (THREAD, caller->constants());
assert (receiver.is_null() || receiver->is_oop(), "wrong receiver");
constantPoolHandle constants(THREAD, caller->constants());
assert(receiver.is_null() || receiver->is_oop(), "wrong receiver");
LinkResolver::resolve_invoke(callinfo, receiver, constants, bytecode_index, bc, CHECK_(nullHandle));
#ifdef ASSERT
// Check that the receiver klass is of the right subtype and that it is initialized for virtual calls
if (bc != Bytecodes::_invokestatic && bc != Bytecodes::_invokedynamic) {
assert(receiver.not_null(), "should have thrown exception");
KlassHandle receiver_klass (THREAD, receiver->klass());
KlassHandle receiver_klass(THREAD, receiver->klass());
klassOop rk = constants->klass_ref_at(bytecode_index, CHECK_(nullHandle));
// klass is already loaded
KlassHandle static_receiver_klass (THREAD, rk);
assert(receiver_klass->is_subtype_of(static_receiver_klass()), "actual receiver must be subclass of static receiver klass");
KlassHandle static_receiver_klass(THREAD, rk);
// Method handle invokes might have been optimized to a direct call
// so don't check for the receiver class.
// FIXME this weakens the assert too much
methodHandle callee = callinfo.selected_method();
assert(receiver_klass->is_subtype_of(static_receiver_klass()) ||
callee->is_method_handle_intrinsic() ||
callee->is_compiled_lambda_form(),
"actual receiver must be subclass of static receiver klass");
if (receiver_klass->oop_is_instance()) {
if (instanceKlass::cast(receiver_klass())->is_not_initialized()) {
tty->print_cr("ERROR: Klass not yet initialized!!");
receiver_klass.print();
}
assert (!instanceKlass::cast(receiver_klass())->is_not_initialized(), "receiver_klass must be initialized");
assert(!instanceKlass::cast(receiver_klass())->is_not_initialized(), "receiver_klass must be initialized");
}
}
#endif
@ -1186,8 +1159,10 @@ methodHandle SharedRuntime::resolve_sub_helper(JavaThread *thread,
call_info, CHECK_(methodHandle()));
methodHandle callee_method = call_info.selected_method();
assert((!is_virtual && invoke_code == Bytecodes::_invokestatic) ||
( is_virtual && invoke_code != Bytecodes::_invokestatic), "inconsistent bytecode");
assert((!is_virtual && invoke_code == Bytecodes::_invokestatic ) ||
(!is_virtual && invoke_code == Bytecodes::_invokehandle ) ||
(!is_virtual && invoke_code == Bytecodes::_invokedynamic) ||
( is_virtual && invoke_code != Bytecodes::_invokestatic ), "inconsistent bytecode");
#ifndef PRODUCT
// tracing/debugging/statistics
@ -1202,16 +1177,17 @@ methodHandle SharedRuntime::resolve_sub_helper(JavaThread *thread,
(is_optimized) ? "optimized " : "", (is_virtual) ? "virtual" : "static",
Bytecodes::name(invoke_code));
callee_method->print_short_name(tty);
tty->print_cr(" code: " INTPTR_FORMAT, callee_method->code());
tty->print_cr(" at pc: " INTPTR_FORMAT " to code: " INTPTR_FORMAT, caller_frame.pc(), callee_method->code());
}
#endif
// JSR 292
// JSR 292 key invariant:
// If the resolved method is a MethodHandle invoke target the call
// site must be a MethodHandle call site.
if (callee_method->is_method_handle_invoke()) {
assert(caller_nm->is_method_handle_return(caller_frame.pc()), "must be MH call site");
}
// site must be a MethodHandle call site, because the lambda form might tail-call
// leaving the stack in a state unknown to either caller or callee
// TODO detune for now but we might need it again
// assert(!callee_method->is_compiled_lambda_form() ||
// caller_nm->is_method_handle_return(caller_frame.pc()), "must be MH call site");
// Compute entry points. This might require generation of C2I converter
// frames, so we cannot be holding any locks here. Furthermore, the
@ -1284,7 +1260,6 @@ JRT_BLOCK_ENTRY(address, SharedRuntime::handle_wrong_method_ic_miss(JavaThread*
assert(stub_frame.is_runtime_frame(), "sanity check");
frame caller_frame = stub_frame.sender(&reg_map);
assert(!caller_frame.is_interpreted_frame() && !caller_frame.is_entry_frame(), "unexpected frame");
assert(!caller_frame.is_ricochet_frame(), "unexpected frame");
#endif /* ASSERT */
methodHandle callee_method;
@ -1320,21 +1295,9 @@ JRT_BLOCK_ENTRY(address, SharedRuntime::handle_wrong_method(JavaThread* thread))
address sender_pc = caller_frame.pc();
CodeBlob* sender_cb = caller_frame.cb();
nmethod* sender_nm = sender_cb->as_nmethod_or_null();
bool is_mh_invoke_via_adapter = false; // Direct c2c call or via adapter?
if (sender_nm != NULL && sender_nm->is_method_handle_return(sender_pc)) {
// If the callee_target is set, then we have come here via an i2c
// adapter.
methodOop callee = thread->callee_target();
if (callee != NULL) {
assert(callee->is_method(), "sanity");
is_mh_invoke_via_adapter = true;
}
}
if (caller_frame.is_interpreted_frame() ||
caller_frame.is_entry_frame() ||
caller_frame.is_ricochet_frame() ||
is_mh_invoke_via_adapter) {
caller_frame.is_entry_frame()) {
methodOop callee = thread->callee_target();
guarantee(callee != NULL && callee->is_method(), "bad handshake");
thread->set_vm_result(callee);
@ -1677,12 +1640,6 @@ IRT_LEAF(void, SharedRuntime::fixup_callers_callsite(methodOopDesc* method, addr
// Get the return PC for the passed caller PC.
address return_pc = caller_pc + frame::pc_return_offset;
// Don't fixup method handle call sites as the executed method
// handle adapters are doing the required MethodHandle chain work.
if (nm->is_method_handle_return(return_pc)) {
return;
}
// There is a benign race here. We could be attempting to patch to a compiled
// entry point at the same time the callee is being deoptimized. If that is
// the case then entry_point may in fact point to a c2i and we'd patch the
@ -1788,97 +1745,6 @@ char* SharedRuntime::generate_class_cast_message(
return generate_class_cast_message(objName, targetKlass->external_name());
}
char* SharedRuntime::generate_wrong_method_type_message(JavaThread* thread,
oopDesc* required,
oopDesc* actual) {
if (TraceMethodHandles) {
tty->print_cr("WrongMethodType thread="PTR_FORMAT" req="PTR_FORMAT" act="PTR_FORMAT"",
thread, required, actual);
}
assert(EnableInvokeDynamic, "");
oop singleKlass = wrong_method_type_is_for_single_argument(thread, required);
char* message = NULL;
if (singleKlass != NULL) {
const char* objName = "argument or return value";
if (actual != NULL) {
// be flexible about the junk passed in:
klassOop ak = (actual->is_klass()
? (klassOop)actual
: actual->klass());
objName = Klass::cast(ak)->external_name();
}
Klass* targetKlass = Klass::cast(required->is_klass()
? (klassOop)required
: java_lang_Class::as_klassOop(required));
message = generate_class_cast_message(objName, targetKlass->external_name());
} else {
// %%% need to get the MethodType string, without messing around too much
const char* desc = NULL;
// Get a signature from the invoke instruction
const char* mhName = "method handle";
const char* targetType = "the required signature";
int targetArity = -1, mhArity = -1;
vframeStream vfst(thread, true);
if (!vfst.at_end()) {
Bytecode_invoke call(vfst.method(), vfst.bci());
methodHandle target;
{
EXCEPTION_MARK;
target = call.static_target(THREAD);
if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; }
}
if (target.not_null()
&& target->is_method_handle_invoke()
&& required == target->method_handle_type()) {
targetType = target->signature()->as_C_string();
targetArity = ArgumentCount(target->signature()).size();
}
}
KlassHandle kignore; int dmf_flags = 0;
methodHandle actual_method = MethodHandles::decode_method(actual, kignore, dmf_flags);
if ((dmf_flags & ~(MethodHandles::_dmf_has_receiver |
MethodHandles::_dmf_does_dispatch |
MethodHandles::_dmf_from_interface)) != 0)
actual_method = methodHandle(); // MH does extra binds, drops, etc.
bool has_receiver = ((dmf_flags & MethodHandles::_dmf_has_receiver) != 0);
if (actual_method.not_null()) {
mhName = actual_method->signature()->as_C_string();
mhArity = ArgumentCount(actual_method->signature()).size();
if (!actual_method->is_static()) mhArity += 1;
} else if (java_lang_invoke_MethodHandle::is_instance(actual)) {
oopDesc* mhType = java_lang_invoke_MethodHandle::type(actual);
mhArity = java_lang_invoke_MethodType::ptype_count(mhType);
stringStream st;
java_lang_invoke_MethodType::print_signature(mhType, &st);
mhName = st.as_string();
}
if (targetArity != -1 && targetArity != mhArity) {
if (has_receiver && targetArity == mhArity-1)
desc = " cannot be called without a receiver argument as ";
else
desc = " cannot be called with a different arity as ";
}
message = generate_class_cast_message(mhName, targetType,
desc != NULL ? desc :
" cannot be called as ");
}
if (TraceMethodHandles) {
tty->print_cr("WrongMethodType => message=%s", message);
}
return message;
}
oop SharedRuntime::wrong_method_type_is_for_single_argument(JavaThread* thr,
oopDesc* required) {
if (required == NULL) return NULL;
if (required->klass() == SystemDictionary::Class_klass())
return required;
if (required->is_klass())
return Klass::cast(klassOop(required))->java_mirror();
return NULL;
}
char* SharedRuntime::generate_class_cast_message(
const char* objName, const char* targetKlassName, const char* desc) {
size_t msglen = strlen(objName) + strlen(desc) + strlen(targetKlassName) + 1;
@ -2119,8 +1985,17 @@ void SharedRuntime::print_call_statistics(int comp_total) {
// that allows sharing of adapters for the same calling convention.
class AdapterFingerPrint : public CHeapObj<mtCode> {
private:
enum {
_basic_type_bits = 4,
_basic_type_mask = right_n_bits(_basic_type_bits),
_basic_types_per_int = BitsPerInt / _basic_type_bits,
_compact_int_count = 3
};
// TO DO: Consider integrating this with a more global scheme for compressing signatures.
// For now, 4 bits per components (plus T_VOID gaps after double/long) is not excessive.
union {
int _compact[3];
int _compact[_compact_int_count];
int* _fingerprint;
} _value;
int _length; // A negative length indicates the fingerprint is in the compact form,
@ -2129,8 +2004,7 @@ class AdapterFingerPrint : public CHeapObj<mtCode> {
// Remap BasicTypes that are handled equivalently by the adapters.
// These are correct for the current system but someday it might be
// necessary to make this mapping platform dependent.
static BasicType adapter_encoding(BasicType in) {
assert((~0xf & in) == 0, "must fit in 4 bits");
static int adapter_encoding(BasicType in) {
switch(in) {
case T_BOOLEAN:
case T_BYTE:
@ -2141,6 +2015,8 @@ class AdapterFingerPrint : public CHeapObj<mtCode> {
case T_OBJECT:
case T_ARRAY:
// In other words, we assume that any register good enough for
// an int or long is good enough for a managed pointer.
#ifdef _LP64
return T_LONG;
#else
@ -2165,8 +2041,9 @@ class AdapterFingerPrint : public CHeapObj<mtCode> {
// The fingerprint is based on the BasicType signature encoded
// into an array of ints with eight entries per int.
int* ptr;
int len = (total_args_passed + 7) >> 3;
if (len <= (int)(sizeof(_value._compact) / sizeof(int))) {
int len = (total_args_passed + (_basic_types_per_int-1)) / _basic_types_per_int;
if (len <= _compact_int_count) {
assert(_compact_int_count == 3, "else change next line");
_value._compact[0] = _value._compact[1] = _value._compact[2] = 0;
// Storing the signature encoded as signed chars hits about 98%
// of the time.
@ -2182,10 +2059,12 @@ class AdapterFingerPrint : public CHeapObj<mtCode> {
int sig_index = 0;
for (int index = 0; index < len; index++) {
int value = 0;
for (int byte = 0; byte < 8; byte++) {
if (sig_index < total_args_passed) {
value = (value << 4) | adapter_encoding(sig_bt[sig_index++]);
}
for (int byte = 0; byte < _basic_types_per_int; byte++) {
int bt = ((sig_index < total_args_passed)
? adapter_encoding(sig_bt[sig_index++])
: 0);
assert((bt & _basic_type_mask) == bt, "must fit in 4 bits");
value = (value << _basic_type_bits) | bt;
}
ptr[index] = value;
}
@ -2235,6 +2114,7 @@ class AdapterFingerPrint : public CHeapObj<mtCode> {
return false;
}
if (_length < 0) {
assert(_compact_int_count == 3, "else change next line");
return _value._compact[0] == other->_value._compact[0] &&
_value._compact[1] == other->_value._compact[1] &&
_value._compact[2] == other->_value._compact[2];
@ -2531,13 +2411,17 @@ AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter(methodHandle method) {
entry->relocate(B->content_begin());
#ifndef PRODUCT
// debugging suppport
if (PrintAdapterHandlers) {
tty->cr();
tty->print_cr("i2c argument handler #%d for: %s %s (fingerprint = %s, %d bytes generated)",
if (PrintAdapterHandlers || PrintStubCode) {
entry->print_adapter_on(tty);
tty->print_cr("i2c argument handler #%d for: %s %s (%d bytes generated)",
_adapters->number_of_entries(), (method->is_static() ? "static" : "receiver"),
method->signature()->as_C_string(), fingerprint->as_string(), insts_size );
method->signature()->as_C_string(), insts_size);
tty->print_cr("c2i argument handler starts at %p",entry->get_c2i_entry());
Disassembler::decode(entry->get_i2c_entry(), entry->get_i2c_entry() + insts_size);
if (Verbose || PrintStubCode) {
address first_pc = entry->base_address();
if (first_pc != NULL)
Disassembler::decode(first_pc, first_pc + insts_size);
}
}
#endif
@ -2561,11 +2445,25 @@ AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter(methodHandle method) {
return entry;
}
address AdapterHandlerEntry::base_address() {
address base = _i2c_entry;
if (base == NULL) base = _c2i_entry;
assert(base <= _c2i_entry || _c2i_entry == NULL, "");
assert(base <= _c2i_unverified_entry || _c2i_unverified_entry == NULL, "");
return base;
}
void AdapterHandlerEntry::relocate(address new_base) {
ptrdiff_t delta = new_base - _i2c_entry;
address old_base = base_address();
assert(old_base != NULL, "");
ptrdiff_t delta = new_base - old_base;
if (_i2c_entry != NULL)
_i2c_entry += delta;
if (_c2i_entry != NULL)
_c2i_entry += delta;
if (_c2i_unverified_entry != NULL)
_c2i_unverified_entry += delta;
assert(base_address() == new_base, "");
}
@ -2614,7 +2512,9 @@ nmethod *AdapterHandlerLibrary::create_native_wrapper(methodHandle method, int c
ResourceMark rm;
nmethod* nm = NULL;
assert(method->has_native_function(), "must have something valid to call!");
assert(method->is_native(), "must be native");
assert(method->is_method_handle_intrinsic() ||
method->has_native_function(), "must have something valid to call!");
{
// perform the work while holding the lock, but perform any printing outside the lock
@ -2651,9 +2551,11 @@ nmethod *AdapterHandlerLibrary::create_native_wrapper(methodHandle method, int c
assert( i==total_args_passed, "" );
BasicType ret_type = ss.type();
// Now get the compiled-Java layout as input arguments
int comp_args_on_stack;
comp_args_on_stack = SharedRuntime::java_calling_convention(sig_bt, regs, total_args_passed, false);
// Now get the compiled-Java layout as input (or output) arguments.
// NOTE: Stubs for compiled entry points of method handle intrinsics
// are just trampolines so the argument registers must be outgoing ones.
const bool is_outgoing = method->is_method_handle_intrinsic();
int comp_args_on_stack = SharedRuntime::java_calling_convention(sig_bt, regs, total_args_passed, is_outgoing);
// Generate the compiled-to-native wrapper code
nm = SharedRuntime::generate_native_wrapper(&_masm,
@ -2939,18 +2841,22 @@ void AdapterHandlerLibrary::print_handler_on(outputStream* st, CodeBlob* b) {
AdapterHandlerTableIterator iter(_adapters);
while (iter.has_next()) {
AdapterHandlerEntry* a = iter.next();
if ( b == CodeCache::find_blob(a->get_i2c_entry()) ) {
if (b == CodeCache::find_blob(a->get_i2c_entry())) {
st->print("Adapter for signature: ");
st->print_cr("%s i2c: " INTPTR_FORMAT " c2i: " INTPTR_FORMAT " c2iUV: " INTPTR_FORMAT,
a->fingerprint()->as_string(),
a->get_i2c_entry(), a->get_c2i_entry(), a->get_c2i_unverified_entry());
a->print_adapter_on(tty);
return;
}
}
assert(false, "Should have found handler");
}
void AdapterHandlerEntry::print_adapter_on(outputStream* st) const {
st->print_cr("AHE@" INTPTR_FORMAT ": %s i2c: " INTPTR_FORMAT " c2i: " INTPTR_FORMAT " c2iUV: " INTPTR_FORMAT,
(intptr_t) this, fingerprint()->as_string(),
get_i2c_entry(), get_c2i_entry(), get_c2i_unverified_entry());
}
#ifndef PRODUCT
void AdapterHandlerLibrary::print_statistics() {