8174962: Better interface invocations
Reviewed-by: jrose, coleenp, ahgross, acorn, iignatyev
This commit is contained in:
parent
65fe47bdf2
commit
fa8b858052
@ -774,7 +774,7 @@ AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm
|
|||||||
__ load_klass(rscratch1, receiver);
|
__ load_klass(rscratch1, receiver);
|
||||||
__ ldr(tmp, Address(holder, CompiledICHolder::holder_klass_offset()));
|
__ ldr(tmp, Address(holder, CompiledICHolder::holder_klass_offset()));
|
||||||
__ cmp(rscratch1, tmp);
|
__ cmp(rscratch1, tmp);
|
||||||
__ ldr(rmethod, Address(holder, CompiledICHolder::holder_method_offset()));
|
__ ldr(rmethod, Address(holder, CompiledICHolder::holder_metadata_offset()));
|
||||||
__ br(Assembler::EQ, ok);
|
__ br(Assembler::EQ, ok);
|
||||||
__ far_jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub()));
|
__ far_jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub()));
|
||||||
|
|
||||||
|
@ -2475,49 +2475,65 @@ void MacroAssembler::store_sized_value(Register src, Address dst, size_t size_in
|
|||||||
// On success, the result will be in method_result, and execution falls through.
|
// On success, the result will be in method_result, and execution falls through.
|
||||||
// On failure, execution transfers to the given label.
|
// On failure, execution transfers to the given label.
|
||||||
void MacroAssembler::lookup_interface_method(Register Rklass,
|
void MacroAssembler::lookup_interface_method(Register Rklass,
|
||||||
Register Rinterf,
|
Register Rintf,
|
||||||
Register Rindex,
|
RegisterOrConstant itable_index,
|
||||||
Register method_result,
|
Register method_result,
|
||||||
Register temp_reg1,
|
Register Rscan,
|
||||||
Register temp_reg2,
|
Register Rtmp,
|
||||||
Label& L_no_such_interface) {
|
Label& L_no_such_interface) {
|
||||||
|
|
||||||
assert_different_registers(Rklass, Rinterf, temp_reg1, temp_reg2, Rindex);
|
assert_different_registers(Rklass, Rintf, Rscan, Rtmp);
|
||||||
|
|
||||||
Register Ritable = temp_reg1;
|
const int entry_size = itableOffsetEntry::size() * HeapWordSize;
|
||||||
|
assert(itableOffsetEntry::interface_offset_in_bytes() == 0, "not added for convenience");
|
||||||
|
|
||||||
// Compute start of first itableOffsetEntry (which is at the end of the vtable)
|
// Compute start of first itableOffsetEntry (which is at the end of the vtable)
|
||||||
const int base = in_bytes(Klass::vtable_start_offset());
|
const int base = in_bytes(Klass::vtable_start_offset());
|
||||||
const int scale = exact_log2(vtableEntry::size_in_bytes());
|
const int scale = exact_log2(vtableEntry::size_in_bytes());
|
||||||
ldr_s32(temp_reg2, Address(Rklass, Klass::vtable_length_offset())); // Get length of vtable
|
ldr_s32(Rtmp, Address(Rklass, Klass::vtable_length_offset())); // Get length of vtable
|
||||||
add(Ritable, Rklass, base);
|
add(Rscan, Rklass, base);
|
||||||
add(Ritable, Ritable, AsmOperand(temp_reg2, lsl, scale));
|
add(Rscan, Rscan, AsmOperand(Rtmp, lsl, scale));
|
||||||
|
|
||||||
Label entry, search;
|
// Search through the itable for an interface equal to incoming Rintf
|
||||||
|
// itable looks like [intface][offset][intface][offset][intface][offset]
|
||||||
|
|
||||||
b(entry);
|
Label loop;
|
||||||
|
bind(loop);
|
||||||
|
ldr(Rtmp, Address(Rscan, entry_size, post_indexed));
|
||||||
|
#ifdef AARCH64
|
||||||
|
Label found;
|
||||||
|
cmp(Rtmp, Rintf);
|
||||||
|
b(found, eq);
|
||||||
|
cbnz(Rtmp, loop);
|
||||||
|
#else
|
||||||
|
cmp(Rtmp, Rintf); // set ZF and CF if interface is found
|
||||||
|
cmn(Rtmp, 0, ne); // check if tmp == 0 and clear CF if it is
|
||||||
|
b(loop, ne);
|
||||||
|
#endif // AARCH64
|
||||||
|
|
||||||
bind(search);
|
#ifdef AARCH64
|
||||||
add(Ritable, Ritable, itableOffsetEntry::size() * HeapWordSize);
|
b(L_no_such_interface);
|
||||||
|
bind(found);
|
||||||
|
#else
|
||||||
|
// CF == 0 means we reached the end of itable without finding icklass
|
||||||
|
b(L_no_such_interface, cc);
|
||||||
|
#endif // !AARCH64
|
||||||
|
|
||||||
bind(entry);
|
if (method_result != noreg) {
|
||||||
|
// Interface found at previous position of Rscan, now load the method
|
||||||
// Check that the entry is non-null. A null entry means that the receiver
|
ldr_s32(Rtmp, Address(Rscan, itableOffsetEntry::offset_offset_in_bytes() - entry_size));
|
||||||
// class doesn't implement the interface, and wasn't the same as the
|
if (itable_index.is_register()) {
|
||||||
// receiver class checked when the interface was resolved.
|
add(Rtmp, Rtmp, Rklass); // Add offset to Klass*
|
||||||
|
|
||||||
ldr(temp_reg2, Address(Ritable, itableOffsetEntry::interface_offset_in_bytes()));
|
|
||||||
cbz(temp_reg2, L_no_such_interface);
|
|
||||||
|
|
||||||
cmp(Rinterf, temp_reg2);
|
|
||||||
b(search, ne);
|
|
||||||
|
|
||||||
ldr_s32(temp_reg2, Address(Ritable, itableOffsetEntry::offset_offset_in_bytes()));
|
|
||||||
add(temp_reg2, temp_reg2, Rklass); // Add offset to Klass*
|
|
||||||
assert(itableMethodEntry::size() * HeapWordSize == wordSize, "adjust the scaling in the code below");
|
assert(itableMethodEntry::size() * HeapWordSize == wordSize, "adjust the scaling in the code below");
|
||||||
assert(itableMethodEntry::method_offset_in_bytes() == 0, "adjust the offset in the code below");
|
assert(itableMethodEntry::method_offset_in_bytes() == 0, "adjust the offset in the code below");
|
||||||
|
ldr(method_result, Address::indexed_ptr(Rtmp, itable_index.as_register()));
|
||||||
ldr(method_result, Address::indexed_ptr(temp_reg2, Rindex));
|
} else {
|
||||||
|
int method_offset = itableMethodEntry::size() * HeapWordSize * itable_index.as_constant() +
|
||||||
|
itableMethodEntry::method_offset_in_bytes();
|
||||||
|
add_slow(method_result, Rklass, method_offset);
|
||||||
|
ldr(method_result, Address(method_result, Rtmp));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef COMPILER2
|
#ifdef COMPILER2
|
||||||
|
@ -1316,7 +1316,7 @@ public:
|
|||||||
|
|
||||||
void lookup_interface_method(Register recv_klass,
|
void lookup_interface_method(Register recv_klass,
|
||||||
Register intf_klass,
|
Register intf_klass,
|
||||||
Register itable_index,
|
RegisterOrConstant itable_index,
|
||||||
Register method_result,
|
Register method_result,
|
||||||
Register temp_reg1,
|
Register temp_reg1,
|
||||||
Register temp_reg2,
|
Register temp_reg2,
|
||||||
|
@ -984,7 +984,7 @@ AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm
|
|||||||
|
|
||||||
__ load_klass(receiver_klass, receiver);
|
__ load_klass(receiver_klass, receiver);
|
||||||
__ ldr(holder_klass, Address(Ricklass, CompiledICHolder::holder_klass_offset()));
|
__ ldr(holder_klass, Address(Ricklass, CompiledICHolder::holder_klass_offset()));
|
||||||
__ ldr(Rmethod, Address(Ricklass, CompiledICHolder::holder_method_offset()));
|
__ ldr(Rmethod, Address(Ricklass, CompiledICHolder::holder_metadata_offset()));
|
||||||
__ cmp(receiver_klass, holder_klass);
|
__ cmp(receiver_klass, holder_klass);
|
||||||
|
|
||||||
#ifdef AARCH64
|
#ifdef AARCH64
|
||||||
|
@ -4198,7 +4198,7 @@ void TemplateTable::invokeinterface(int byte_no) {
|
|||||||
const Register Rflags = R3_tmp;
|
const Register Rflags = R3_tmp;
|
||||||
const Register Rklass = R3_tmp;
|
const Register Rklass = R3_tmp;
|
||||||
|
|
||||||
prepare_invoke(byte_no, Rinterf, Rindex, Rrecv, Rflags);
|
prepare_invoke(byte_no, Rinterf, Rmethod, Rrecv, Rflags);
|
||||||
|
|
||||||
// Special case of invokeinterface called for virtual method of
|
// Special case of invokeinterface called for virtual method of
|
||||||
// java.lang.Object. See cpCacheOop.cpp for details.
|
// java.lang.Object. See cpCacheOop.cpp for details.
|
||||||
@ -4207,56 +4207,39 @@ void TemplateTable::invokeinterface(int byte_no) {
|
|||||||
Label notMethod;
|
Label notMethod;
|
||||||
__ tbz(Rflags, ConstantPoolCacheEntry::is_forced_virtual_shift, notMethod);
|
__ tbz(Rflags, ConstantPoolCacheEntry::is_forced_virtual_shift, notMethod);
|
||||||
|
|
||||||
__ mov(Rmethod, Rindex);
|
|
||||||
invokevirtual_helper(Rmethod, Rrecv, Rflags);
|
invokevirtual_helper(Rmethod, Rrecv, Rflags);
|
||||||
__ bind(notMethod);
|
__ bind(notMethod);
|
||||||
|
|
||||||
// Get receiver klass into Rklass - also a null check
|
// Get receiver klass into Rklass - also a null check
|
||||||
__ load_klass(Rklass, Rrecv);
|
__ load_klass(Rklass, Rrecv);
|
||||||
|
|
||||||
|
Label no_such_interface;
|
||||||
|
|
||||||
|
// Receiver subtype check against REFC.
|
||||||
|
__ lookup_interface_method(// inputs: rec. class, interface
|
||||||
|
Rklass, Rinterf, noreg,
|
||||||
|
// outputs: scan temp. reg1, scan temp. reg2
|
||||||
|
noreg, Ritable, Rtemp,
|
||||||
|
no_such_interface);
|
||||||
|
|
||||||
// profile this call
|
// profile this call
|
||||||
__ profile_virtual_call(R0_tmp, Rklass);
|
__ profile_virtual_call(R0_tmp, Rklass);
|
||||||
|
|
||||||
// Compute start of first itableOffsetEntry (which is at the end of the vtable)
|
// Get declaring interface class from method
|
||||||
const int base = in_bytes(Klass::vtable_start_offset());
|
__ ldr(Rtemp, Address(Rmethod, Method::const_offset()));
|
||||||
assert(vtableEntry::size() == 1, "adjust the scaling in the code below");
|
__ ldr(Rtemp, Address(Rtemp, ConstMethod::constants_offset()));
|
||||||
__ ldr_s32(Rtemp, Address(Rklass, Klass::vtable_length_offset())); // Get length of vtable
|
__ ldr(Rinterf, Address(Rtemp, ConstantPool::pool_holder_offset_in_bytes()));
|
||||||
__ add(Ritable, Rklass, base);
|
|
||||||
__ add(Ritable, Ritable, AsmOperand(Rtemp, lsl, LogBytesPerWord));
|
|
||||||
|
|
||||||
Label entry, search, interface_ok;
|
// Get itable index from method
|
||||||
|
__ ldr_s32(Rtemp, Address(Rmethod, Method::itable_index_offset()));
|
||||||
|
__ add(Rtemp, Rtemp, (-Method::itable_index_max)); // small negative constant is too large for an immediate on arm32
|
||||||
|
__ neg(Rindex, Rtemp);
|
||||||
|
|
||||||
__ b(entry);
|
__ lookup_interface_method(// inputs: rec. class, interface
|
||||||
|
Rklass, Rinterf, Rindex,
|
||||||
__ bind(search);
|
// outputs: scan temp. reg1, scan temp. reg2
|
||||||
__ add(Ritable, Ritable, itableOffsetEntry::size() * HeapWordSize);
|
Rmethod, Ritable, Rtemp,
|
||||||
|
no_such_interface);
|
||||||
__ bind(entry);
|
|
||||||
|
|
||||||
// Check that the entry is non-null. A null entry means that the receiver
|
|
||||||
// class doesn't implement the interface, and wasn't the same as the
|
|
||||||
// receiver class checked when the interface was resolved.
|
|
||||||
|
|
||||||
__ ldr(Rtemp, Address(Ritable, itableOffsetEntry::interface_offset_in_bytes()));
|
|
||||||
__ cbnz(Rtemp, interface_ok);
|
|
||||||
|
|
||||||
// throw exception
|
|
||||||
__ call_VM(noreg, CAST_FROM_FN_PTR(address,
|
|
||||||
InterpreterRuntime::throw_IncompatibleClassChangeError));
|
|
||||||
|
|
||||||
// the call_VM checks for exception, so we should never return here.
|
|
||||||
__ should_not_reach_here();
|
|
||||||
|
|
||||||
__ bind(interface_ok);
|
|
||||||
|
|
||||||
__ cmp(Rinterf, Rtemp);
|
|
||||||
__ b(search, ne);
|
|
||||||
|
|
||||||
__ ldr_s32(Rtemp, Address(Ritable, itableOffsetEntry::offset_offset_in_bytes()));
|
|
||||||
__ add(Rtemp, Rtemp, Rklass); // Add offset to Klass*
|
|
||||||
assert(itableMethodEntry::size() == 1, "adjust the scaling in the code below");
|
|
||||||
|
|
||||||
__ ldr(Rmethod, Address::indexed_ptr(Rtemp, Rindex));
|
|
||||||
|
|
||||||
// Rmethod: Method* to call
|
// Rmethod: Method* to call
|
||||||
|
|
||||||
@ -4278,6 +4261,13 @@ void TemplateTable::invokeinterface(int byte_no) {
|
|||||||
|
|
||||||
// do the call
|
// do the call
|
||||||
__ jump_from_interpreted(Rmethod);
|
__ jump_from_interpreted(Rmethod);
|
||||||
|
|
||||||
|
// throw exception
|
||||||
|
__ bind(no_such_interface);
|
||||||
|
__ restore_method();
|
||||||
|
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_IncompatibleClassChangeError));
|
||||||
|
// the call_VM checks for exception, so we should never return here.
|
||||||
|
__ should_not_reach_here();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TemplateTable::invokehandle(int byte_no) {
|
void TemplateTable::invokehandle(int byte_no) {
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "code/vtableStubs.hpp"
|
#include "code/vtableStubs.hpp"
|
||||||
#include "interp_masm_arm.hpp"
|
#include "interp_masm_arm.hpp"
|
||||||
#include "memory/resourceArea.hpp"
|
#include "memory/resourceArea.hpp"
|
||||||
|
#include "oops/compiledICHolder.hpp"
|
||||||
#include "oops/instanceKlass.hpp"
|
#include "oops/instanceKlass.hpp"
|
||||||
#include "oops/klassVtable.hpp"
|
#include "oops/klassVtable.hpp"
|
||||||
#include "runtime/sharedRuntime.hpp"
|
#include "runtime/sharedRuntime.hpp"
|
||||||
@ -118,67 +119,48 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) {
|
|||||||
|
|
||||||
// R0-R3 / R0-R7 registers hold the arguments and cannot be spoiled
|
// R0-R3 / R0-R7 registers hold the arguments and cannot be spoiled
|
||||||
const Register Rclass = AARCH64_ONLY(R9) NOT_AARCH64(R4);
|
const Register Rclass = AARCH64_ONLY(R9) NOT_AARCH64(R4);
|
||||||
const Register Rlength = AARCH64_ONLY(R10) NOT_AARCH64(R5);
|
const Register Rintf = AARCH64_ONLY(R10) NOT_AARCH64(R5);
|
||||||
const Register Rscan = AARCH64_ONLY(R11) NOT_AARCH64(R6);
|
const Register Rscan = AARCH64_ONLY(R11) NOT_AARCH64(R6);
|
||||||
const Register tmp = Rtemp;
|
|
||||||
|
|
||||||
assert_different_registers(Ricklass, Rclass, Rlength, Rscan, tmp);
|
assert_different_registers(Ricklass, Rclass, Rintf, Rscan, Rtemp);
|
||||||
|
|
||||||
// Calculate the start of itable (itable goes after vtable)
|
// Calculate the start of itable (itable goes after vtable)
|
||||||
const int scale = exact_log2(vtableEntry::size_in_bytes());
|
const int scale = exact_log2(vtableEntry::size_in_bytes());
|
||||||
address npe_addr = __ pc();
|
address npe_addr = __ pc();
|
||||||
__ load_klass(Rclass, R0);
|
__ load_klass(Rclass, R0);
|
||||||
__ ldr_s32(Rlength, Address(Rclass, Klass::vtable_length_offset()));
|
|
||||||
|
|
||||||
__ add(Rscan, Rclass, in_bytes(Klass::vtable_start_offset()));
|
Label L_no_such_interface;
|
||||||
__ add(Rscan, Rscan, AsmOperand(Rlength, lsl, scale));
|
|
||||||
|
|
||||||
// Search through the itable for an interface equal to incoming Ricklass
|
// Receiver subtype check against REFC.
|
||||||
// itable looks like [intface][offset][intface][offset][intface][offset]
|
__ ldr(Rintf, Address(Ricklass, CompiledICHolder::holder_klass_offset()));
|
||||||
const int entry_size = itableOffsetEntry::size() * HeapWordSize;
|
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
||||||
assert(itableOffsetEntry::interface_offset_in_bytes() == 0, "not added for convenience");
|
Rclass, Rintf, noreg,
|
||||||
|
// outputs: temp reg1, temp reg2
|
||||||
|
noreg, Rscan, Rtemp,
|
||||||
|
L_no_such_interface);
|
||||||
|
|
||||||
Label loop;
|
// Get Method* and entry point for compiler
|
||||||
__ bind(loop);
|
__ ldr(Rintf, Address(Ricklass, CompiledICHolder::holder_metadata_offset()));
|
||||||
__ ldr(tmp, Address(Rscan, entry_size, post_indexed));
|
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
||||||
#ifdef AARCH64
|
Rclass, Rintf, itable_index,
|
||||||
Label found;
|
// outputs: temp reg1, temp reg2, temp reg3
|
||||||
__ cmp(tmp, Ricklass);
|
Rmethod, Rscan, Rtemp,
|
||||||
__ b(found, eq);
|
L_no_such_interface);
|
||||||
__ cbnz(tmp, loop);
|
|
||||||
#else
|
|
||||||
__ cmp(tmp, Ricklass); // set ZF and CF if interface is found
|
|
||||||
__ cmn(tmp, 0, ne); // check if tmp == 0 and clear CF if it is
|
|
||||||
__ b(loop, ne);
|
|
||||||
#endif // AARCH64
|
|
||||||
|
|
||||||
assert(StubRoutines::throw_IncompatibleClassChangeError_entry() != NULL, "Check initialization order");
|
|
||||||
#ifdef AARCH64
|
|
||||||
__ jump(StubRoutines::throw_IncompatibleClassChangeError_entry(), relocInfo::runtime_call_type, tmp);
|
|
||||||
__ bind(found);
|
|
||||||
#else
|
|
||||||
// CF == 0 means we reached the end of itable without finding icklass
|
|
||||||
__ jump(StubRoutines::throw_IncompatibleClassChangeError_entry(), relocInfo::runtime_call_type, noreg, cc);
|
|
||||||
#endif // !AARCH64
|
|
||||||
|
|
||||||
// Interface found at previous position of Rscan, now load the method oop
|
|
||||||
__ ldr_s32(tmp, Address(Rscan, itableOffsetEntry::offset_offset_in_bytes() - entry_size));
|
|
||||||
{
|
|
||||||
const int method_offset = itableMethodEntry::size() * HeapWordSize * itable_index +
|
|
||||||
itableMethodEntry::method_offset_in_bytes();
|
|
||||||
__ add_slow(Rmethod, Rclass, method_offset);
|
|
||||||
}
|
|
||||||
__ ldr(Rmethod, Address(Rmethod, tmp));
|
|
||||||
|
|
||||||
address ame_addr = __ pc();
|
address ame_addr = __ pc();
|
||||||
|
|
||||||
#ifdef AARCH64
|
#ifdef AARCH64
|
||||||
__ ldr(tmp, Address(Rmethod, Method::from_compiled_offset()));
|
__ ldr(Rtemp, Address(Rmethod, Method::from_compiled_offset()));
|
||||||
__ br(tmp);
|
__ br(Rtemp);
|
||||||
#else
|
#else
|
||||||
__ ldr(PC, Address(Rmethod, Method::from_compiled_offset()));
|
__ ldr(PC, Address(Rmethod, Method::from_compiled_offset()));
|
||||||
#endif // AARCH64
|
#endif // AARCH64
|
||||||
|
|
||||||
|
__ bind(L_no_such_interface);
|
||||||
|
|
||||||
|
assert(StubRoutines::throw_IncompatibleClassChangeError_entry() != NULL, "check initialization order");
|
||||||
|
__ jump(StubRoutines::throw_IncompatibleClassChangeError_entry(), relocInfo::runtime_call_type, Rtemp);
|
||||||
|
|
||||||
masm->flush();
|
masm->flush();
|
||||||
|
|
||||||
if (PrintMiscellaneous && (WizardMode || Verbose)) {
|
if (PrintMiscellaneous && (WizardMode || Verbose)) {
|
||||||
@ -205,7 +187,7 @@ int VtableStub::pd_code_size_limit(bool is_vtable_stub) {
|
|||||||
instr_count = NOT_AARCH64(4) AARCH64_ONLY(5);
|
instr_count = NOT_AARCH64(4) AARCH64_ONLY(5);
|
||||||
} else {
|
} else {
|
||||||
// itable stub size
|
// itable stub size
|
||||||
instr_count = NOT_AARCH64(20) AARCH64_ONLY(20);
|
instr_count = NOT_AARCH64(31) AARCH64_ONLY(31);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef AARCH64
|
#ifdef AARCH64
|
||||||
|
@ -1188,7 +1188,7 @@ AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm
|
|||||||
// Argument is valid and klass is as expected, continue.
|
// Argument is valid and klass is as expected, continue.
|
||||||
|
|
||||||
// Extract method from inline cache, verified entry point needs it.
|
// Extract method from inline cache, verified entry point needs it.
|
||||||
__ ld(R19_method, CompiledICHolder::holder_method_offset(), ic);
|
__ ld(R19_method, CompiledICHolder::holder_metadata_offset(), ic);
|
||||||
assert(R19_method == ic, "the inline cache register is dead here");
|
assert(R19_method == ic, "the inline cache register is dead here");
|
||||||
|
|
||||||
__ ld(code, method_(code));
|
__ ld(code, method_(code));
|
||||||
|
@ -2662,7 +2662,7 @@ AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm
|
|||||||
Label ic_miss;
|
Label ic_miss;
|
||||||
const int klass_offset = oopDesc::klass_offset_in_bytes();
|
const int klass_offset = oopDesc::klass_offset_in_bytes();
|
||||||
const int holder_klass_offset = CompiledICHolder::holder_klass_offset();
|
const int holder_klass_offset = CompiledICHolder::holder_klass_offset();
|
||||||
const int holder_method_offset = CompiledICHolder::holder_method_offset();
|
const int holder_metadata_offset = CompiledICHolder::holder_metadata_offset();
|
||||||
|
|
||||||
// Out-of-line call to ic_miss handler.
|
// Out-of-line call to ic_miss handler.
|
||||||
__ call_ic_miss_handler(ic_miss, 0x11, 0, Z_R1_scratch);
|
__ call_ic_miss_handler(ic_miss, 0x11, 0, Z_R1_scratch);
|
||||||
@ -2691,7 +2691,7 @@ AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm
|
|||||||
// This def MUST MATCH code in gen_c2i_adapter!
|
// This def MUST MATCH code in gen_c2i_adapter!
|
||||||
const Register code = Z_R11;
|
const Register code = Z_R11;
|
||||||
|
|
||||||
__ z_lg(Z_method, holder_method_offset, Z_method);
|
__ z_lg(Z_method, holder_metadata_offset, Z_method);
|
||||||
__ load_and_test_long(Z_R0, method_(code));
|
__ load_and_test_long(Z_R0, method_(code));
|
||||||
__ z_brne(ic_miss); // Cache miss: call runtime to handle this.
|
__ z_brne(ic_miss); // Cache miss: call runtime to handle this.
|
||||||
|
|
||||||
|
@ -2058,9 +2058,10 @@ void MacroAssembler::lookup_interface_method(Register recv_klass,
|
|||||||
Register method_result,
|
Register method_result,
|
||||||
Register scan_temp,
|
Register scan_temp,
|
||||||
Register sethi_temp,
|
Register sethi_temp,
|
||||||
Label& L_no_such_interface) {
|
Label& L_no_such_interface,
|
||||||
|
bool return_method) {
|
||||||
assert_different_registers(recv_klass, intf_klass, method_result, scan_temp);
|
assert_different_registers(recv_klass, intf_klass, method_result, scan_temp);
|
||||||
assert(itable_index.is_constant() || itable_index.as_register() == method_result,
|
assert(!return_method || itable_index.is_constant() || itable_index.as_register() == method_result,
|
||||||
"caller must use same register for non-constant itable index as for method");
|
"caller must use same register for non-constant itable index as for method");
|
||||||
|
|
||||||
Label L_no_such_interface_restore;
|
Label L_no_such_interface_restore;
|
||||||
@ -2092,11 +2093,13 @@ void MacroAssembler::lookup_interface_method(Register recv_klass,
|
|||||||
add(scan_temp, itb_offset, scan_temp);
|
add(scan_temp, itb_offset, scan_temp);
|
||||||
add(recv_klass, scan_temp, scan_temp);
|
add(recv_klass, scan_temp, scan_temp);
|
||||||
|
|
||||||
|
if (return_method) {
|
||||||
// Adjust recv_klass by scaled itable_index, so we can free itable_index.
|
// Adjust recv_klass by scaled itable_index, so we can free itable_index.
|
||||||
RegisterOrConstant itable_offset = itable_index;
|
RegisterOrConstant itable_offset = itable_index;
|
||||||
itable_offset = regcon_sll_ptr(itable_index, exact_log2(itableMethodEntry::size() * wordSize), itable_offset);
|
itable_offset = regcon_sll_ptr(itable_index, exact_log2(itableMethodEntry::size() * wordSize), itable_offset);
|
||||||
itable_offset = regcon_inc_ptr(itable_offset, itableMethodEntry::method_offset_in_bytes(), itable_offset);
|
itable_offset = regcon_inc_ptr(itable_offset, itableMethodEntry::method_offset_in_bytes(), itable_offset);
|
||||||
add(recv_klass, ensure_simm13_or_reg(itable_offset, sethi_temp), recv_klass);
|
add(recv_klass, ensure_simm13_or_reg(itable_offset, sethi_temp), recv_klass);
|
||||||
|
}
|
||||||
|
|
||||||
// for (scan = klass->itable(); scan->interface() != NULL; scan += scan_step) {
|
// for (scan = klass->itable(); scan->interface() != NULL; scan += scan_step) {
|
||||||
// if (scan->interface() == intf) {
|
// if (scan->interface() == intf) {
|
||||||
@ -2131,12 +2134,14 @@ void MacroAssembler::lookup_interface_method(Register recv_klass,
|
|||||||
|
|
||||||
bind(L_found_method);
|
bind(L_found_method);
|
||||||
|
|
||||||
|
if (return_method) {
|
||||||
// Got a hit.
|
// Got a hit.
|
||||||
int ito_offset = itableOffsetEntry::offset_offset_in_bytes();
|
int ito_offset = itableOffsetEntry::offset_offset_in_bytes();
|
||||||
// scan_temp[-scan_step] points to the vtable offset we need
|
// scan_temp[-scan_step] points to the vtable offset we need
|
||||||
ito_offset -= scan_step;
|
ito_offset -= scan_step;
|
||||||
lduw(scan_temp, ito_offset, scan_temp);
|
lduw(scan_temp, ito_offset, scan_temp);
|
||||||
ld_ptr(recv_klass, scan_temp, method_result);
|
ld_ptr(recv_klass, scan_temp, method_result);
|
||||||
|
}
|
||||||
|
|
||||||
if (did_save) {
|
if (did_save) {
|
||||||
Label L_done;
|
Label L_done;
|
||||||
|
@ -1277,7 +1277,8 @@ public:
|
|||||||
RegisterOrConstant itable_index,
|
RegisterOrConstant itable_index,
|
||||||
Register method_result,
|
Register method_result,
|
||||||
Register temp_reg, Register temp2_reg,
|
Register temp_reg, Register temp2_reg,
|
||||||
Label& no_such_interface);
|
Label& no_such_interface,
|
||||||
|
bool return_method = true);
|
||||||
|
|
||||||
// virtual method calling
|
// virtual method calling
|
||||||
void lookup_virtual_method(Register recv_klass,
|
void lookup_virtual_method(Register recv_klass,
|
||||||
|
@ -904,7 +904,7 @@ AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm
|
|||||||
|
|
||||||
Label ok, ok2;
|
Label ok, ok2;
|
||||||
__ brx(Assembler::equal, false, Assembler::pt, ok);
|
__ brx(Assembler::equal, false, Assembler::pt, ok);
|
||||||
__ delayed()->ld_ptr(G5_method, CompiledICHolder::holder_method_offset(), G5_method);
|
__ delayed()->ld_ptr(G5_method, CompiledICHolder::holder_metadata_offset(), G5_method);
|
||||||
__ jump_to(ic_miss, G3_scratch);
|
__ jump_to(ic_miss, G3_scratch);
|
||||||
__ delayed()->nop();
|
__ delayed()->nop();
|
||||||
|
|
||||||
|
@ -3081,15 +3081,15 @@ void TemplateTable::invokeinterface(int byte_no) {
|
|||||||
assert(byte_no == f1_byte, "use this argument");
|
assert(byte_no == f1_byte, "use this argument");
|
||||||
|
|
||||||
const Register Rinterface = G1_scratch;
|
const Register Rinterface = G1_scratch;
|
||||||
|
const Register Rmethod = Lscratch;
|
||||||
const Register Rret = G3_scratch;
|
const Register Rret = G3_scratch;
|
||||||
const Register Rindex = Lscratch;
|
|
||||||
const Register O0_recv = O0;
|
const Register O0_recv = O0;
|
||||||
const Register O1_flags = O1;
|
const Register O1_flags = O1;
|
||||||
const Register O2_Klass = O2;
|
const Register O2_Klass = O2;
|
||||||
const Register Rscratch = G4_scratch;
|
const Register Rscratch = G4_scratch;
|
||||||
assert_different_registers(Rscratch, G5_method);
|
assert_different_registers(Rscratch, G5_method);
|
||||||
|
|
||||||
prepare_invoke(byte_no, Rinterface, Rret, Rindex, O0_recv, O1_flags);
|
prepare_invoke(byte_no, Rinterface, Rret, Rmethod, O0_recv, O1_flags);
|
||||||
|
|
||||||
// get receiver klass
|
// get receiver klass
|
||||||
__ null_check(O0_recv, oopDesc::klass_offset_in_bytes());
|
__ null_check(O0_recv, oopDesc::klass_offset_in_bytes());
|
||||||
@ -3109,55 +3109,40 @@ void TemplateTable::invokeinterface(int byte_no) {
|
|||||||
|
|
||||||
__ bind(notMethod);
|
__ bind(notMethod);
|
||||||
|
|
||||||
|
Register Rtemp = O1_flags;
|
||||||
|
|
||||||
|
Label L_no_such_interface;
|
||||||
|
|
||||||
|
// Receiver subtype check against REFC.
|
||||||
|
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
||||||
|
O2_Klass, Rinterface, noreg,
|
||||||
|
// outputs: temp reg1, temp reg2, temp reg3
|
||||||
|
G5_method, Rscratch, Rtemp,
|
||||||
|
L_no_such_interface,
|
||||||
|
/*return_method=*/false);
|
||||||
|
|
||||||
__ profile_virtual_call(O2_Klass, O4);
|
__ profile_virtual_call(O2_Klass, O4);
|
||||||
|
|
||||||
//
|
//
|
||||||
// find entry point to call
|
// find entry point to call
|
||||||
//
|
//
|
||||||
|
|
||||||
// compute start of first itableOffsetEntry (which is at end of vtable)
|
// Get declaring interface class from method
|
||||||
const int base = in_bytes(Klass::vtable_start_offset());
|
__ ld_ptr(Rmethod, Method::const_offset(), Rinterface);
|
||||||
Label search;
|
__ ld_ptr(Rinterface, ConstMethod::constants_offset(), Rinterface);
|
||||||
Register Rtemp = O1_flags;
|
__ ld_ptr(Rinterface, ConstantPool::pool_holder_offset_in_bytes(), Rinterface);
|
||||||
|
|
||||||
__ ld(O2_Klass, in_bytes(Klass::vtable_length_offset()), Rtemp);
|
// Get itable index from method
|
||||||
__ sll(Rtemp, LogBytesPerWord, Rtemp); // Rscratch *= 4;
|
const Register Rindex = G5_method;
|
||||||
if (Assembler::is_simm13(base)) {
|
__ ld(Rmethod, Method::itable_index_offset(), Rindex);
|
||||||
__ add(Rtemp, base, Rtemp);
|
__ sub(Rindex, Method::itable_index_max, Rindex);
|
||||||
} else {
|
__ neg(Rindex);
|
||||||
__ set(base, Rscratch);
|
|
||||||
__ add(Rscratch, Rtemp, Rtemp);
|
|
||||||
}
|
|
||||||
__ add(O2_Klass, Rtemp, Rscratch);
|
|
||||||
|
|
||||||
__ bind(search);
|
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
||||||
|
O2_Klass, Rinterface, Rindex,
|
||||||
__ ld_ptr(Rscratch, itableOffsetEntry::interface_offset_in_bytes(), Rtemp);
|
// outputs: method, scan temp reg, temp reg
|
||||||
{
|
G5_method, Rscratch, Rtemp,
|
||||||
Label ok;
|
L_no_such_interface);
|
||||||
|
|
||||||
// Check that entry is non-null. Null entries are probably a bytecode
|
|
||||||
// problem. If the interface isn't implemented by the receiver class,
|
|
||||||
// the VM should throw IncompatibleClassChangeError. linkResolver checks
|
|
||||||
// this too but that's only if the entry isn't already resolved, so we
|
|
||||||
// need to check again.
|
|
||||||
__ br_notnull_short( Rtemp, Assembler::pt, ok);
|
|
||||||
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_IncompatibleClassChangeError));
|
|
||||||
__ should_not_reach_here();
|
|
||||||
__ bind(ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
__ cmp(Rinterface, Rtemp);
|
|
||||||
__ brx(Assembler::notEqual, true, Assembler::pn, search);
|
|
||||||
__ delayed()->add(Rscratch, itableOffsetEntry::size() * wordSize, Rscratch);
|
|
||||||
|
|
||||||
// entry found and Rscratch points to it
|
|
||||||
__ ld(Rscratch, itableOffsetEntry::offset_offset_in_bytes(), Rscratch);
|
|
||||||
|
|
||||||
assert(itableMethodEntry::method_offset_in_bytes() == 0, "adjust instruction below");
|
|
||||||
__ sll(Rindex, exact_log2(itableMethodEntry::size() * wordSize), Rindex); // Rindex *= 8;
|
|
||||||
__ add(Rscratch, Rindex, Rscratch);
|
|
||||||
__ ld_ptr(O2_Klass, Rscratch, G5_method);
|
|
||||||
|
|
||||||
// Check for abstract method error.
|
// Check for abstract method error.
|
||||||
{
|
{
|
||||||
@ -3174,6 +3159,10 @@ void TemplateTable::invokeinterface(int byte_no) {
|
|||||||
__ profile_arguments_type(G5_method, Rcall, Gargs, true);
|
__ profile_arguments_type(G5_method, Rcall, Gargs, true);
|
||||||
__ profile_called_method(G5_method, Rscratch);
|
__ profile_called_method(G5_method, Rscratch);
|
||||||
__ call_from_interpreter(Rcall, Gargs, Rret);
|
__ call_from_interpreter(Rcall, Gargs, Rret);
|
||||||
|
|
||||||
|
__ bind(L_no_such_interface);
|
||||||
|
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_IncompatibleClassChangeError));
|
||||||
|
__ should_not_reach_here();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TemplateTable::invokehandle(int byte_no) {
|
void TemplateTable::invokehandle(int byte_no) {
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "code/vtableStubs.hpp"
|
#include "code/vtableStubs.hpp"
|
||||||
#include "interp_masm_sparc.hpp"
|
#include "interp_masm_sparc.hpp"
|
||||||
#include "memory/resourceArea.hpp"
|
#include "memory/resourceArea.hpp"
|
||||||
|
#include "oops/compiledICHolder.hpp"
|
||||||
#include "oops/instanceKlass.hpp"
|
#include "oops/instanceKlass.hpp"
|
||||||
#include "oops/klassVtable.hpp"
|
#include "oops/klassVtable.hpp"
|
||||||
#include "runtime/sharedRuntime.hpp"
|
#include "runtime/sharedRuntime.hpp"
|
||||||
@ -140,7 +141,8 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) {
|
|||||||
MacroAssembler* masm = new MacroAssembler(&cb);
|
MacroAssembler* masm = new MacroAssembler(&cb);
|
||||||
|
|
||||||
Register G3_Klass = G3_scratch;
|
Register G3_Klass = G3_scratch;
|
||||||
Register G5_interface = G5; // Passed in as an argument
|
Register G5_icholder = G5; // Passed in as an argument
|
||||||
|
Register G4_interface = G4_scratch;
|
||||||
Label search;
|
Label search;
|
||||||
|
|
||||||
// Entry arguments:
|
// Entry arguments:
|
||||||
@ -164,14 +166,26 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) {
|
|||||||
}
|
}
|
||||||
#endif /* PRODUCT */
|
#endif /* PRODUCT */
|
||||||
|
|
||||||
Label throw_icce;
|
Label L_no_such_interface;
|
||||||
|
|
||||||
Register L5_method = L5;
|
Register L5_method = L5;
|
||||||
|
|
||||||
|
// Receiver subtype check against REFC.
|
||||||
|
__ ld_ptr(G5_icholder, CompiledICHolder::holder_klass_offset(), G4_interface);
|
||||||
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
||||||
G3_Klass, G5_interface, itable_index,
|
G3_Klass, G4_interface, itable_index,
|
||||||
|
// outputs: scan temp. reg1, scan temp. reg2
|
||||||
|
L5_method, L2, L3,
|
||||||
|
L_no_such_interface,
|
||||||
|
/*return_method=*/ false);
|
||||||
|
|
||||||
|
// Get Method* and entrypoint for compiler
|
||||||
|
__ ld_ptr(G5_icholder, CompiledICHolder::holder_metadata_offset(), G4_interface);
|
||||||
|
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
||||||
|
G3_Klass, G4_interface, itable_index,
|
||||||
// outputs: method, scan temp. reg
|
// outputs: method, scan temp. reg
|
||||||
L5_method, L2, L3,
|
L5_method, L2, L3,
|
||||||
throw_icce);
|
L_no_such_interface);
|
||||||
|
|
||||||
#ifndef PRODUCT
|
#ifndef PRODUCT
|
||||||
if (DebugVtables) {
|
if (DebugVtables) {
|
||||||
@ -197,7 +211,7 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) {
|
|||||||
__ JMP(G3_scratch, 0);
|
__ JMP(G3_scratch, 0);
|
||||||
__ delayed()->nop();
|
__ delayed()->nop();
|
||||||
|
|
||||||
__ bind(throw_icce);
|
__ bind(L_no_such_interface);
|
||||||
AddressLiteral icce(StubRoutines::throw_IncompatibleClassChangeError_entry());
|
AddressLiteral icce(StubRoutines::throw_IncompatibleClassChangeError_entry());
|
||||||
__ jump_to(icce, G3_scratch);
|
__ jump_to(icce, G3_scratch);
|
||||||
__ delayed()->restore();
|
__ delayed()->restore();
|
||||||
@ -232,7 +246,7 @@ int VtableStub::pd_code_size_limit(bool is_vtable_stub) {
|
|||||||
MacroAssembler::instr_size_for_decode_klass_not_null() : 0);
|
MacroAssembler::instr_size_for_decode_klass_not_null() : 0);
|
||||||
return basic + slop;
|
return basic + slop;
|
||||||
} else {
|
} else {
|
||||||
const int basic = 34 * BytesPerInstWord +
|
const int basic = 54 * BytesPerInstWord +
|
||||||
// shift;add for load_klass (only shift with zero heap based)
|
// shift;add for load_klass (only shift with zero heap based)
|
||||||
(UseCompressedClassPointers ?
|
(UseCompressedClassPointers ?
|
||||||
MacroAssembler::instr_size_for_decode_klass_not_null() : 0);
|
MacroAssembler::instr_size_for_decode_klass_not_null() : 0);
|
||||||
|
@ -5809,8 +5809,13 @@ void MacroAssembler::lookup_interface_method(Register recv_klass,
|
|||||||
RegisterOrConstant itable_index,
|
RegisterOrConstant itable_index,
|
||||||
Register method_result,
|
Register method_result,
|
||||||
Register scan_temp,
|
Register scan_temp,
|
||||||
Label& L_no_such_interface) {
|
Label& L_no_such_interface,
|
||||||
assert_different_registers(recv_klass, intf_klass, method_result, scan_temp);
|
bool return_method) {
|
||||||
|
assert_different_registers(recv_klass, intf_klass, scan_temp);
|
||||||
|
assert_different_registers(method_result, intf_klass, scan_temp);
|
||||||
|
assert(recv_klass != method_result || !return_method,
|
||||||
|
"recv_klass can be destroyed when method isn't needed");
|
||||||
|
|
||||||
assert(itable_index.is_constant() || itable_index.as_register() == method_result,
|
assert(itable_index.is_constant() || itable_index.as_register() == method_result,
|
||||||
"caller must use same register for non-constant itable index as for method");
|
"caller must use same register for non-constant itable index as for method");
|
||||||
|
|
||||||
@ -5827,9 +5832,11 @@ void MacroAssembler::lookup_interface_method(Register recv_klass,
|
|||||||
// %%% Could store the aligned, prescaled offset in the klassoop.
|
// %%% Could store the aligned, prescaled offset in the klassoop.
|
||||||
lea(scan_temp, Address(recv_klass, scan_temp, times_vte_scale, vtable_base));
|
lea(scan_temp, Address(recv_klass, scan_temp, times_vte_scale, vtable_base));
|
||||||
|
|
||||||
|
if (return_method) {
|
||||||
// Adjust recv_klass by scaled itable_index, so we can free itable_index.
|
// Adjust recv_klass by scaled itable_index, so we can free itable_index.
|
||||||
assert(itableMethodEntry::size() * wordSize == wordSize, "adjust the scaling in the code below");
|
assert(itableMethodEntry::size() * wordSize == wordSize, "adjust the scaling in the code below");
|
||||||
lea(recv_klass, Address(recv_klass, itable_index, Address::times_ptr, itentry_off));
|
lea(recv_klass, Address(recv_klass, itable_index, Address::times_ptr, itentry_off));
|
||||||
|
}
|
||||||
|
|
||||||
// for (scan = klass->itable(); scan->interface() != NULL; scan += scan_step) {
|
// for (scan = klass->itable(); scan->interface() != NULL; scan += scan_step) {
|
||||||
// if (scan->interface() == intf) {
|
// if (scan->interface() == intf) {
|
||||||
@ -5863,9 +5870,11 @@ void MacroAssembler::lookup_interface_method(Register recv_klass,
|
|||||||
|
|
||||||
bind(found_method);
|
bind(found_method);
|
||||||
|
|
||||||
|
if (return_method) {
|
||||||
// Got a hit.
|
// Got a hit.
|
||||||
movl(scan_temp, Address(scan_temp, itableOffsetEntry::offset_offset_in_bytes()));
|
movl(scan_temp, Address(scan_temp, itableOffsetEntry::offset_offset_in_bytes()));
|
||||||
movptr(method_result, Address(recv_klass, scan_temp, Address::times_1));
|
movptr(method_result, Address(recv_klass, scan_temp, Address::times_1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -544,7 +544,8 @@ class MacroAssembler: public Assembler {
|
|||||||
RegisterOrConstant itable_index,
|
RegisterOrConstant itable_index,
|
||||||
Register method_result,
|
Register method_result,
|
||||||
Register scan_temp,
|
Register scan_temp,
|
||||||
Label& no_such_interface);
|
Label& no_such_interface,
|
||||||
|
bool return_method = true);
|
||||||
|
|
||||||
// virtual method calling
|
// virtual method calling
|
||||||
void lookup_virtual_method(Register recv_klass,
|
void lookup_virtual_method(Register recv_klass,
|
||||||
|
@ -957,7 +957,7 @@ AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm
|
|||||||
Label missed;
|
Label missed;
|
||||||
__ movptr(temp, Address(receiver, oopDesc::klass_offset_in_bytes()));
|
__ movptr(temp, Address(receiver, oopDesc::klass_offset_in_bytes()));
|
||||||
__ cmpptr(temp, Address(holder, CompiledICHolder::holder_klass_offset()));
|
__ cmpptr(temp, Address(holder, CompiledICHolder::holder_klass_offset()));
|
||||||
__ movptr(rbx, Address(holder, CompiledICHolder::holder_method_offset()));
|
__ movptr(rbx, Address(holder, CompiledICHolder::holder_metadata_offset()));
|
||||||
__ jcc(Assembler::notEqual, missed);
|
__ jcc(Assembler::notEqual, missed);
|
||||||
// Method might have been compiled since the call site was patched to
|
// Method might have been compiled since the call site was patched to
|
||||||
// interpreted if that is the case treat it as a miss so we can get
|
// interpreted if that is the case treat it as a miss so we can get
|
||||||
|
@ -949,7 +949,7 @@ AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm
|
|||||||
{
|
{
|
||||||
__ load_klass(temp, receiver);
|
__ load_klass(temp, receiver);
|
||||||
__ cmpptr(temp, Address(holder, CompiledICHolder::holder_klass_offset()));
|
__ cmpptr(temp, Address(holder, CompiledICHolder::holder_klass_offset()));
|
||||||
__ movptr(rbx, Address(holder, CompiledICHolder::holder_method_offset()));
|
__ movptr(rbx, Address(holder, CompiledICHolder::holder_metadata_offset()));
|
||||||
__ jcc(Assembler::equal, ok);
|
__ jcc(Assembler::equal, ok);
|
||||||
__ jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub()));
|
__ jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub()));
|
||||||
|
|
||||||
|
@ -3712,11 +3712,11 @@ void TemplateTable::fast_invokevfinal(int byte_no) {
|
|||||||
void TemplateTable::invokeinterface(int byte_no) {
|
void TemplateTable::invokeinterface(int byte_no) {
|
||||||
transition(vtos, vtos);
|
transition(vtos, vtos);
|
||||||
assert(byte_no == f1_byte, "use this argument");
|
assert(byte_no == f1_byte, "use this argument");
|
||||||
prepare_invoke(byte_no, rax, rbx, // get f1 Klass*, f2 itable index
|
prepare_invoke(byte_no, rax, rbx, // get f1 Klass*, f2 Method*
|
||||||
rcx, rdx); // recv, flags
|
rcx, rdx); // recv, flags
|
||||||
|
|
||||||
// rax: interface klass (from f1)
|
// rax: reference klass (from f1)
|
||||||
// rbx: itable index (from f2)
|
// rbx: method (from f2)
|
||||||
// rcx: receiver
|
// rcx: receiver
|
||||||
// rdx: flags
|
// rdx: flags
|
||||||
|
|
||||||
@ -3738,10 +3738,28 @@ void TemplateTable::invokeinterface(int byte_no) {
|
|||||||
__ null_check(rcx, oopDesc::klass_offset_in_bytes());
|
__ null_check(rcx, oopDesc::klass_offset_in_bytes());
|
||||||
__ load_klass(rdx, rcx);
|
__ load_klass(rdx, rcx);
|
||||||
|
|
||||||
|
Label no_such_interface, no_such_method;
|
||||||
|
|
||||||
|
// Receiver subtype check against REFC.
|
||||||
|
// Superklass in rax. Subklass in rdx. Blows rcx, rdi.
|
||||||
|
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
||||||
|
rdx, rax, noreg,
|
||||||
|
// outputs: scan temp. reg, scan temp. reg
|
||||||
|
rbcp, rlocals,
|
||||||
|
no_such_interface,
|
||||||
|
/*return_method=*/false);
|
||||||
|
|
||||||
// profile this call
|
// profile this call
|
||||||
|
__ restore_bcp(); // rbcp was destroyed by receiver type check
|
||||||
__ profile_virtual_call(rdx, rbcp, rlocals);
|
__ profile_virtual_call(rdx, rbcp, rlocals);
|
||||||
|
|
||||||
Label no_such_interface, no_such_method;
|
// Get declaring interface class from method, and itable index
|
||||||
|
__ movptr(rax, Address(rbx, Method::const_offset()));
|
||||||
|
__ movptr(rax, Address(rax, ConstMethod::constants_offset()));
|
||||||
|
__ movptr(rax, Address(rax, ConstantPool::pool_holder_offset_in_bytes()));
|
||||||
|
__ movl(rbx, Address(rbx, Method::itable_index_offset()));
|
||||||
|
__ subl(rbx, Method::itable_index_max);
|
||||||
|
__ negl(rbx);
|
||||||
|
|
||||||
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
||||||
rdx, rax, rbx,
|
rdx, rax, rbx,
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "code/vtableStubs.hpp"
|
#include "code/vtableStubs.hpp"
|
||||||
#include "interp_masm_x86.hpp"
|
#include "interp_masm_x86.hpp"
|
||||||
#include "memory/resourceArea.hpp"
|
#include "memory/resourceArea.hpp"
|
||||||
|
#include "oops/compiledICHolder.hpp"
|
||||||
#include "oops/instanceKlass.hpp"
|
#include "oops/instanceKlass.hpp"
|
||||||
#include "oops/klassVtable.hpp"
|
#include "oops/klassVtable.hpp"
|
||||||
#include "runtime/sharedRuntime.hpp"
|
#include "runtime/sharedRuntime.hpp"
|
||||||
@ -147,7 +148,7 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) {
|
|||||||
MacroAssembler* masm = new MacroAssembler(&cb);
|
MacroAssembler* masm = new MacroAssembler(&cb);
|
||||||
|
|
||||||
// Entry arguments:
|
// Entry arguments:
|
||||||
// rax,: Interface
|
// rax: CompiledICHolder
|
||||||
// rcx: Receiver
|
// rcx: Receiver
|
||||||
|
|
||||||
#ifndef PRODUCT
|
#ifndef PRODUCT
|
||||||
@ -155,25 +156,42 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) {
|
|||||||
__ incrementl(ExternalAddress((address) SharedRuntime::nof_megamorphic_calls_addr()));
|
__ incrementl(ExternalAddress((address) SharedRuntime::nof_megamorphic_calls_addr()));
|
||||||
}
|
}
|
||||||
#endif /* PRODUCT */
|
#endif /* PRODUCT */
|
||||||
// get receiver (need to skip return address on top of stack)
|
|
||||||
|
|
||||||
assert(VtableStub::receiver_location() == rcx->as_VMReg(), "receiver expected in rcx");
|
|
||||||
|
|
||||||
// get receiver klass (also an implicit null-check)
|
|
||||||
address npe_addr = __ pc();
|
|
||||||
__ movptr(rsi, Address(rcx, oopDesc::klass_offset_in_bytes()));
|
|
||||||
|
|
||||||
// Most registers are in use; we'll use rax, rbx, rsi, rdi
|
// Most registers are in use; we'll use rax, rbx, rsi, rdi
|
||||||
// (If we need to make rsi, rdi callee-save, do a push/pop here.)
|
// (If we need to make rsi, rdi callee-save, do a push/pop here.)
|
||||||
const Register method = rbx;
|
const Register recv_klass_reg = rsi;
|
||||||
Label throw_icce;
|
const Register holder_klass_reg = rax; // declaring interface klass (DECC)
|
||||||
|
const Register resolved_klass_reg = rbx; // resolved interface klass (REFC)
|
||||||
|
const Register temp_reg = rdi;
|
||||||
|
|
||||||
// Get Method* and entrypoint for compiler
|
const Register icholder_reg = rax;
|
||||||
|
__ movptr(resolved_klass_reg, Address(icholder_reg, CompiledICHolder::holder_klass_offset()));
|
||||||
|
__ movptr(holder_klass_reg, Address(icholder_reg, CompiledICHolder::holder_metadata_offset()));
|
||||||
|
|
||||||
|
Label L_no_such_interface;
|
||||||
|
|
||||||
|
// get receiver klass (also an implicit null-check)
|
||||||
|
address npe_addr = __ pc();
|
||||||
|
assert(VtableStub::receiver_location() == rcx->as_VMReg(), "receiver expected in rcx");
|
||||||
|
__ load_klass(recv_klass_reg, rcx);
|
||||||
|
|
||||||
|
// Receiver subtype check against REFC.
|
||||||
|
// Destroys recv_klass_reg value.
|
||||||
|
__ lookup_interface_method(// inputs: rec. class, interface
|
||||||
|
recv_klass_reg, resolved_klass_reg, noreg,
|
||||||
|
// outputs: scan temp. reg1, scan temp. reg2
|
||||||
|
recv_klass_reg, temp_reg,
|
||||||
|
L_no_such_interface,
|
||||||
|
/*return_method=*/false);
|
||||||
|
|
||||||
|
// Get selected method from declaring class and itable index
|
||||||
|
const Register method = rbx;
|
||||||
|
__ load_klass(recv_klass_reg, rcx); // restore recv_klass_reg
|
||||||
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
||||||
rsi, rax, itable_index,
|
recv_klass_reg, holder_klass_reg, itable_index,
|
||||||
// outputs: method, scan temp. reg
|
// outputs: method, scan temp. reg
|
||||||
method, rdi,
|
method, temp_reg,
|
||||||
throw_icce);
|
L_no_such_interface);
|
||||||
|
|
||||||
// method (rbx): Method*
|
// method (rbx): Method*
|
||||||
// rcx: receiver
|
// rcx: receiver
|
||||||
@ -193,9 +211,10 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) {
|
|||||||
address ame_addr = __ pc();
|
address ame_addr = __ pc();
|
||||||
__ jmp(Address(method, Method::from_compiled_offset()));
|
__ jmp(Address(method, Method::from_compiled_offset()));
|
||||||
|
|
||||||
__ bind(throw_icce);
|
__ bind(L_no_such_interface);
|
||||||
__ jump(RuntimeAddress(StubRoutines::throw_IncompatibleClassChangeError_entry()));
|
__ jump(RuntimeAddress(StubRoutines::throw_IncompatibleClassChangeError_entry()));
|
||||||
masm->flush();
|
|
||||||
|
__ flush();
|
||||||
|
|
||||||
if (PrintMiscellaneous && (WizardMode || Verbose)) {
|
if (PrintMiscellaneous && (WizardMode || Verbose)) {
|
||||||
tty->print_cr("itable #%d at " PTR_FORMAT "[%d] left over: %d",
|
tty->print_cr("itable #%d at " PTR_FORMAT "[%d] left over: %d",
|
||||||
@ -220,7 +239,7 @@ int VtableStub::pd_code_size_limit(bool is_vtable_stub) {
|
|||||||
return (DebugVtables ? 210 : 16) + (CountCompiledCalls ? 6 : 0);
|
return (DebugVtables ? 210 : 16) + (CountCompiledCalls ? 6 : 0);
|
||||||
} else {
|
} else {
|
||||||
// Itable stub size
|
// Itable stub size
|
||||||
return (DebugVtables ? 256 : 66) + (CountCompiledCalls ? 6 : 0);
|
return (DebugVtables ? 256 : 110) + (CountCompiledCalls ? 6 : 0);
|
||||||
}
|
}
|
||||||
// In order to tune these parameters, run the JVM with VM options
|
// In order to tune these parameters, run the JVM with VM options
|
||||||
// +PrintMiscellaneous and +WizardMode to see information about
|
// +PrintMiscellaneous and +WizardMode to see information about
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "code/vtableStubs.hpp"
|
#include "code/vtableStubs.hpp"
|
||||||
#include "interp_masm_x86.hpp"
|
#include "interp_masm_x86.hpp"
|
||||||
#include "memory/resourceArea.hpp"
|
#include "memory/resourceArea.hpp"
|
||||||
|
#include "oops/compiledICHolder.hpp"
|
||||||
#include "oops/instanceKlass.hpp"
|
#include "oops/instanceKlass.hpp"
|
||||||
#include "oops/klassVtable.hpp"
|
#include "oops/klassVtable.hpp"
|
||||||
#include "runtime/sharedRuntime.hpp"
|
#include "runtime/sharedRuntime.hpp"
|
||||||
@ -147,36 +148,50 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Entry arguments:
|
// Entry arguments:
|
||||||
// rax: Interface
|
// rax: CompiledICHolder
|
||||||
// j_rarg0: Receiver
|
// j_rarg0: Receiver
|
||||||
|
|
||||||
// Free registers (non-args) are rax (interface), rbx
|
|
||||||
|
|
||||||
// get receiver (need to skip return address on top of stack)
|
|
||||||
|
|
||||||
assert(VtableStub::receiver_location() == j_rarg0->as_VMReg(), "receiver expected in j_rarg0");
|
|
||||||
// get receiver klass (also an implicit null-check)
|
|
||||||
address npe_addr = __ pc();
|
|
||||||
|
|
||||||
// Most registers are in use; we'll use rax, rbx, r10, r11
|
// Most registers are in use; we'll use rax, rbx, r10, r11
|
||||||
// (various calling sequences use r[cd]x, r[sd]i, r[89]; stay away from them)
|
// (various calling sequences use r[cd]x, r[sd]i, r[89]; stay away from them)
|
||||||
__ load_klass(r10, j_rarg0);
|
const Register recv_klass_reg = r10;
|
||||||
|
const Register holder_klass_reg = rax; // declaring interface klass (DECC)
|
||||||
|
const Register resolved_klass_reg = rbx; // resolved interface klass (REFC)
|
||||||
|
const Register temp_reg = r11;
|
||||||
|
|
||||||
|
Label L_no_such_interface;
|
||||||
|
|
||||||
|
const Register icholder_reg = rax;
|
||||||
|
__ movptr(resolved_klass_reg, Address(icholder_reg, CompiledICHolder::holder_klass_offset()));
|
||||||
|
__ movptr(holder_klass_reg, Address(icholder_reg, CompiledICHolder::holder_metadata_offset()));
|
||||||
|
|
||||||
|
// get receiver klass (also an implicit null-check)
|
||||||
|
assert(VtableStub::receiver_location() == j_rarg0->as_VMReg(), "receiver expected in j_rarg0");
|
||||||
|
address npe_addr = __ pc();
|
||||||
|
__ load_klass(recv_klass_reg, j_rarg0);
|
||||||
|
|
||||||
|
// Receiver subtype check against REFC.
|
||||||
|
// Destroys recv_klass_reg value.
|
||||||
|
__ lookup_interface_method(// inputs: rec. class, interface
|
||||||
|
recv_klass_reg, resolved_klass_reg, noreg,
|
||||||
|
// outputs: scan temp. reg1, scan temp. reg2
|
||||||
|
recv_klass_reg, temp_reg,
|
||||||
|
L_no_such_interface,
|
||||||
|
/*return_method=*/false);
|
||||||
|
|
||||||
|
// Get selected method from declaring class and itable index
|
||||||
|
const Register method = rbx;
|
||||||
|
__ load_klass(recv_klass_reg, j_rarg0); // restore recv_klass_reg
|
||||||
|
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
||||||
|
recv_klass_reg, holder_klass_reg, itable_index,
|
||||||
|
// outputs: method, scan temp. reg
|
||||||
|
method, temp_reg,
|
||||||
|
L_no_such_interface);
|
||||||
|
|
||||||
// If we take a trap while this arg is on the stack we will not
|
// If we take a trap while this arg is on the stack we will not
|
||||||
// be able to walk the stack properly. This is not an issue except
|
// be able to walk the stack properly. This is not an issue except
|
||||||
// when there are mistakes in this assembly code that could generate
|
// when there are mistakes in this assembly code that could generate
|
||||||
// a spurious fault. Ask me how I know...
|
// a spurious fault. Ask me how I know...
|
||||||
|
|
||||||
const Register method = rbx;
|
|
||||||
Label throw_icce;
|
|
||||||
|
|
||||||
// Get Method* and entrypoint for compiler
|
|
||||||
__ lookup_interface_method(// inputs: rec. class, interface, itable index
|
|
||||||
r10, rax, itable_index,
|
|
||||||
// outputs: method, scan temp. reg
|
|
||||||
method, r11,
|
|
||||||
throw_icce);
|
|
||||||
|
|
||||||
// method (rbx): Method*
|
// method (rbx): Method*
|
||||||
// j_rarg0: receiver
|
// j_rarg0: receiver
|
||||||
|
|
||||||
@ -197,7 +212,7 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) {
|
|||||||
address ame_addr = __ pc();
|
address ame_addr = __ pc();
|
||||||
__ jmp(Address(method, Method::from_compiled_offset()));
|
__ jmp(Address(method, Method::from_compiled_offset()));
|
||||||
|
|
||||||
__ bind(throw_icce);
|
__ bind(L_no_such_interface);
|
||||||
__ jump(RuntimeAddress(StubRoutines::throw_IncompatibleClassChangeError_entry()));
|
__ jump(RuntimeAddress(StubRoutines::throw_IncompatibleClassChangeError_entry()));
|
||||||
|
|
||||||
__ flush();
|
__ flush();
|
||||||
@ -224,8 +239,8 @@ int VtableStub::pd_code_size_limit(bool is_vtable_stub) {
|
|||||||
(UseCompressedClassPointers ? MacroAssembler::instr_size_for_decode_klass_not_null() : 0);
|
(UseCompressedClassPointers ? MacroAssembler::instr_size_for_decode_klass_not_null() : 0);
|
||||||
} else {
|
} else {
|
||||||
// Itable stub size
|
// Itable stub size
|
||||||
return (DebugVtables ? 512 : 74) + (CountCompiledCalls ? 13 : 0) +
|
return (DebugVtables ? 512 : 140) + (CountCompiledCalls ? 13 : 0) +
|
||||||
(UseCompressedClassPointers ? MacroAssembler::instr_size_for_decode_klass_not_null() : 0);
|
(UseCompressedClassPointers ? 2 * MacroAssembler::instr_size_for_decode_klass_not_null() : 0);
|
||||||
}
|
}
|
||||||
// In order to tune these parameters, run the JVM with VM options
|
// In order to tune these parameters, run the JVM with VM options
|
||||||
// +PrintMiscellaneous and +WizardMode to see information about
|
// +PrintMiscellaneous and +WizardMode to see information about
|
||||||
|
@ -270,7 +270,7 @@ void AOTCompiledMethod::metadata_do(void f(Metadata*)) {
|
|||||||
CompiledIC *ic = CompiledIC_at(&iter);
|
CompiledIC *ic = CompiledIC_at(&iter);
|
||||||
if (ic->is_icholder_call()) {
|
if (ic->is_icholder_call()) {
|
||||||
CompiledICHolder* cichk = ic->cached_icholder();
|
CompiledICHolder* cichk = ic->cached_icholder();
|
||||||
f(cichk->holder_method());
|
f(cichk->holder_metadata());
|
||||||
f(cichk->holder_klass());
|
f(cichk->holder_klass());
|
||||||
} else {
|
} else {
|
||||||
// Get Klass* or NULL (if value is -1) from GOT cell of virtual call PLT stub.
|
// Get Klass* or NULL (if value is -1) from GOT cell of virtual call PLT stub.
|
||||||
|
@ -230,10 +230,13 @@ bool CompiledIC::set_to_megamorphic(CallInfo* call_info, Bytecodes::Code bytecod
|
|||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
int index = call_info->resolved_method()->itable_index();
|
int index = call_info->resolved_method()->itable_index();
|
||||||
assert(index == itable_index, "CallInfo pre-computes this");
|
assert(index == itable_index, "CallInfo pre-computes this");
|
||||||
#endif //ASSERT
|
|
||||||
InstanceKlass* k = call_info->resolved_method()->method_holder();
|
InstanceKlass* k = call_info->resolved_method()->method_holder();
|
||||||
assert(k->verify_itable_index(itable_index), "sanity check");
|
assert(k->verify_itable_index(itable_index), "sanity check");
|
||||||
InlineCacheBuffer::create_transition_stub(this, k, entry);
|
#endif //ASSERT
|
||||||
|
CompiledICHolder* holder = new CompiledICHolder(call_info->resolved_method()->method_holder(),
|
||||||
|
call_info->resolved_klass());
|
||||||
|
holder->claim();
|
||||||
|
InlineCacheBuffer::create_transition_stub(this, holder, entry);
|
||||||
} else {
|
} else {
|
||||||
assert(call_info->call_kind() == CallInfo::vtable_call, "either itable or vtable");
|
assert(call_info->call_kind() == CallInfo::vtable_call, "either itable or vtable");
|
||||||
// Can be different than selected_method->vtable_index(), due to package-private etc.
|
// Can be different than selected_method->vtable_index(), due to package-private etc.
|
||||||
@ -517,7 +520,14 @@ void CompiledIC::compute_monomorphic_entry(const methodHandle& method,
|
|||||||
|
|
||||||
bool CompiledIC::is_icholder_entry(address entry) {
|
bool CompiledIC::is_icholder_entry(address entry) {
|
||||||
CodeBlob* cb = CodeCache::find_blob_unsafe(entry);
|
CodeBlob* cb = CodeCache::find_blob_unsafe(entry);
|
||||||
return (cb != NULL && cb->is_adapter_blob());
|
if (cb != NULL && cb->is_adapter_blob()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// itable stubs also use CompiledICHolder
|
||||||
|
if (VtableStubs::is_entry_point(entry) && VtableStubs::stub_containing(entry)->is_itable_stub()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CompiledIC::is_icholder_call_site(virtual_call_Relocation* call_site, const CompiledMethod* cm) {
|
bool CompiledIC::is_icholder_call_site(virtual_call_Relocation* call_site, const CompiledMethod* cm) {
|
||||||
|
@ -45,11 +45,11 @@
|
|||||||
// \ / \ /
|
// \ / \ /
|
||||||
// [4] \ / [4] \->-/
|
// [4] \ / [4] \->-/
|
||||||
// \->- Megamorphic -<-/
|
// \->- Megamorphic -<-/
|
||||||
// (Method*)
|
// (CompiledICHolder*)
|
||||||
//
|
//
|
||||||
// The text in paranteses () refere to the value of the inline cache receiver (mov instruction)
|
// The text in parentheses () refers to the value of the inline cache receiver (mov instruction)
|
||||||
//
|
//
|
||||||
// The numbers in square brackets refere to the kind of transition:
|
// The numbers in square brackets refer to the kind of transition:
|
||||||
// [1]: Initial fixup. Receiver it found from debug information
|
// [1]: Initial fixup. Receiver it found from debug information
|
||||||
// [2]: Compilation of a method
|
// [2]: Compilation of a method
|
||||||
// [3]: Recompilation of a method (note: only entry is changed. The Klass* must stay the same)
|
// [3]: Recompilation of a method (note: only entry is changed. The Klass* must stay the same)
|
||||||
|
@ -404,8 +404,7 @@ void CompiledMethod::clean_ic_if_metadata_is_dead(CompiledIC *ic, BoolObjectClos
|
|||||||
// yet be marked below. (We check this further below).
|
// yet be marked below. (We check this further below).
|
||||||
CompiledICHolder* cichk_oop = ic->cached_icholder();
|
CompiledICHolder* cichk_oop = ic->cached_icholder();
|
||||||
|
|
||||||
if (cichk_oop->holder_method()->method_holder()->is_loader_alive(is_alive) &&
|
if (cichk_oop->is_loader_alive(is_alive)) {
|
||||||
cichk_oop->holder_klass()->is_loader_alive(is_alive)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1545,7 +1545,7 @@ void nmethod::metadata_do(void f(Metadata*)) {
|
|||||||
CompiledIC *ic = CompiledIC_at(&iter);
|
CompiledIC *ic = CompiledIC_at(&iter);
|
||||||
if (ic->is_icholder_call()) {
|
if (ic->is_icholder_call()) {
|
||||||
CompiledICHolder* cichk = ic->cached_icholder();
|
CompiledICHolder* cichk = ic->cached_icholder();
|
||||||
f(cichk->holder_method());
|
f(cichk->holder_metadata());
|
||||||
f(cichk->holder_klass());
|
f(cichk->holder_klass());
|
||||||
} else {
|
} else {
|
||||||
Metadata* ic_oop = ic->cached_metadata();
|
Metadata* ic_oop = ic->cached_metadata();
|
||||||
|
@ -822,6 +822,7 @@ void InterpreterRuntime::resolve_invoke(JavaThread* thread, Bytecodes::Code byte
|
|||||||
case CallInfo::itable_call:
|
case CallInfo::itable_call:
|
||||||
cp_cache_entry->set_itable_call(
|
cp_cache_entry->set_itable_call(
|
||||||
bytecode,
|
bytecode,
|
||||||
|
info.resolved_klass(),
|
||||||
info.resolved_method(),
|
info.resolved_method(),
|
||||||
info.itable_index());
|
info.itable_index());
|
||||||
break;
|
break;
|
||||||
|
@ -32,8 +32,8 @@ volatile int CompiledICHolder::_live_count;
|
|||||||
volatile int CompiledICHolder::_live_not_claimed_count;
|
volatile int CompiledICHolder::_live_not_claimed_count;
|
||||||
|
|
||||||
|
|
||||||
CompiledICHolder::CompiledICHolder(Method* method, Klass* klass)
|
CompiledICHolder::CompiledICHolder(Metadata* metadata, Klass* klass)
|
||||||
: _holder_method(method), _holder_klass(klass) {
|
: _holder_metadata(metadata), _holder_klass(klass) {
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
Atomic::inc(&_live_count);
|
Atomic::inc(&_live_count);
|
||||||
Atomic::inc(&_live_not_claimed_count);
|
Atomic::inc(&_live_not_claimed_count);
|
||||||
@ -47,11 +47,27 @@ CompiledICHolder::~CompiledICHolder() {
|
|||||||
}
|
}
|
||||||
#endif // ASSERT
|
#endif // ASSERT
|
||||||
|
|
||||||
|
bool CompiledICHolder::is_loader_alive(BoolObjectClosure* is_alive) {
|
||||||
|
if (_holder_metadata->is_method()) {
|
||||||
|
if (!((Method*)_holder_metadata)->method_holder()->is_loader_alive(is_alive)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (_holder_metadata->is_klass()) {
|
||||||
|
if (!((Klass*)_holder_metadata)->is_loader_alive(is_alive)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!_holder_klass->is_loader_alive(is_alive)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Printing
|
// Printing
|
||||||
|
|
||||||
void CompiledICHolder::print_on(outputStream* st) const {
|
void CompiledICHolder::print_on(outputStream* st) const {
|
||||||
st->print("%s", internal_name());
|
st->print("%s", internal_name());
|
||||||
st->print(" - method: "); holder_method()->print_value_on(st); st->cr();
|
st->print(" - metadata: "); holder_metadata()->print_value_on(st); st->cr();
|
||||||
st->print(" - klass: "); holder_klass()->print_value_on(st); st->cr();
|
st->print(" - klass: "); holder_klass()->print_value_on(st); st->cr();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +79,7 @@ void CompiledICHolder::print_value_on(outputStream* st) const {
|
|||||||
// Verification
|
// Verification
|
||||||
|
|
||||||
void CompiledICHolder::verify_on(outputStream* st) {
|
void CompiledICHolder::verify_on(outputStream* st) {
|
||||||
guarantee(holder_method()->is_method(), "should be method");
|
guarantee(holder_metadata()->is_method() || holder_metadata()->is_klass(), "should be method or klass");
|
||||||
guarantee(holder_klass()->is_klass(), "should be klass");
|
guarantee(holder_klass()->is_klass(), "should be klass");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,8 +29,9 @@
|
|||||||
#include "utilities/macros.hpp"
|
#include "utilities/macros.hpp"
|
||||||
|
|
||||||
// A CompiledICHolder* is a helper object for the inline cache implementation.
|
// A CompiledICHolder* is a helper object for the inline cache implementation.
|
||||||
// It holds an intermediate value (method+klass pair) used when converting from
|
// It holds:
|
||||||
// compiled to an interpreted call.
|
// (1) (method+klass pair) when converting from compiled to an interpreted call
|
||||||
|
// (2) (klass+klass pair) when calling itable stub from megamorphic compiled call
|
||||||
//
|
//
|
||||||
// These are always allocated in the C heap and are freed during a
|
// These are always allocated in the C heap and are freed during a
|
||||||
// safepoint by the ICBuffer logic. It's unsafe to free them earlier
|
// safepoint by the ICBuffer logic. It's unsafe to free them earlier
|
||||||
@ -45,32 +46,33 @@ class CompiledICHolder : public CHeapObj<mtCompiler> {
|
|||||||
static volatile int _live_not_claimed_count; // allocated but not yet in use so not
|
static volatile int _live_not_claimed_count; // allocated but not yet in use so not
|
||||||
// reachable by iterating over nmethods
|
// reachable by iterating over nmethods
|
||||||
|
|
||||||
Method* _holder_method;
|
Metadata* _holder_metadata;
|
||||||
Klass* _holder_klass; // to avoid name conflict with oopDesc::_klass
|
Klass* _holder_klass; // to avoid name conflict with oopDesc::_klass
|
||||||
CompiledICHolder* _next;
|
CompiledICHolder* _next;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Constructor
|
||||||
CompiledICHolder(Method* method, Klass* klass);
|
CompiledICHolder(Metadata* metadata, Klass* klass);
|
||||||
~CompiledICHolder() NOT_DEBUG_RETURN;
|
~CompiledICHolder() NOT_DEBUG_RETURN;
|
||||||
|
|
||||||
static int live_count() { return _live_count; }
|
static int live_count() { return _live_count; }
|
||||||
static int live_not_claimed_count() { return _live_not_claimed_count; }
|
static int live_not_claimed_count() { return _live_not_claimed_count; }
|
||||||
|
|
||||||
// accessors
|
// accessors
|
||||||
Method* holder_method() const { return _holder_method; }
|
|
||||||
Klass* holder_klass() const { return _holder_klass; }
|
Klass* holder_klass() const { return _holder_klass; }
|
||||||
|
Metadata* holder_metadata() const { return _holder_metadata; }
|
||||||
|
|
||||||
void set_holder_method(Method* m) { _holder_method = m; }
|
void set_holder_metadata(Metadata* m) { _holder_metadata = m; }
|
||||||
void set_holder_klass(Klass* k) { _holder_klass = k; }
|
void set_holder_klass(Klass* k) { _holder_klass = k; }
|
||||||
|
|
||||||
// interpreter support (offsets in bytes)
|
static int holder_metadata_offset() { return offset_of(CompiledICHolder, _holder_metadata); }
|
||||||
static int holder_method_offset() { return offset_of(CompiledICHolder, _holder_method); }
|
|
||||||
static int holder_klass_offset() { return offset_of(CompiledICHolder, _holder_klass); }
|
static int holder_klass_offset() { return offset_of(CompiledICHolder, _holder_klass); }
|
||||||
|
|
||||||
CompiledICHolder* next() { return _next; }
|
CompiledICHolder* next() { return _next; }
|
||||||
void set_next(CompiledICHolder* n) { _next = n; }
|
void set_next(CompiledICHolder* n) { _next = n; }
|
||||||
|
|
||||||
|
bool is_loader_alive(BoolObjectClosure* is_alive);
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
void verify_on(outputStream* st);
|
void verify_on(outputStream* st);
|
||||||
|
|
||||||
|
@ -277,14 +277,16 @@ void ConstantPoolCacheEntry::set_vtable_call(Bytecodes::Code invoke_code, const
|
|||||||
set_direct_or_vtable_call(invoke_code, method, index, false);
|
set_direct_or_vtable_call(invoke_code, method, index, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstantPoolCacheEntry::set_itable_call(Bytecodes::Code invoke_code, const methodHandle& method, int index) {
|
void ConstantPoolCacheEntry::set_itable_call(Bytecodes::Code invoke_code,
|
||||||
|
Klass* referenced_klass,
|
||||||
|
const methodHandle& method, int index) {
|
||||||
assert(method->method_holder()->verify_itable_index(index), "");
|
assert(method->method_holder()->verify_itable_index(index), "");
|
||||||
assert(invoke_code == Bytecodes::_invokeinterface, "");
|
assert(invoke_code == Bytecodes::_invokeinterface, "");
|
||||||
InstanceKlass* interf = method->method_holder();
|
InstanceKlass* interf = method->method_holder();
|
||||||
assert(interf->is_interface(), "must be an interface");
|
assert(interf->is_interface(), "must be an interface");
|
||||||
assert(!method->is_final_method(), "interfaces do not have final methods; cannot link to one here");
|
assert(!method->is_final_method(), "interfaces do not have final methods; cannot link to one here");
|
||||||
set_f1(interf);
|
set_f1(referenced_klass);
|
||||||
set_f2(index);
|
set_f2((intx)method());
|
||||||
set_method_flags(as_TosState(method->result_type()),
|
set_method_flags(as_TosState(method->result_type()),
|
||||||
0, // no option bits
|
0, // no option bits
|
||||||
method()->size_of_parameters());
|
method()->size_of_parameters());
|
||||||
@ -513,10 +515,23 @@ oop ConstantPoolCacheEntry::method_type_if_resolved(const constantPoolHandle& cp
|
|||||||
|
|
||||||
|
|
||||||
#if INCLUDE_JVMTI
|
#if INCLUDE_JVMTI
|
||||||
|
|
||||||
|
void log_adjust(const char* entry_type, Method* old_method, Method* new_method, bool* trace_name_printed) {
|
||||||
|
if (log_is_enabled(Info, redefine, class, update)) {
|
||||||
|
ResourceMark rm;
|
||||||
|
if (!(*trace_name_printed)) {
|
||||||
|
log_info(redefine, class, update)("adjust: name=%s", old_method->method_holder()->external_name());
|
||||||
|
*trace_name_printed = true;
|
||||||
|
}
|
||||||
|
log_debug(redefine, class, update, constantpool)
|
||||||
|
("cpc %s entry update: %s(%s)", entry_type, new_method->name()->as_C_string(), new_method->signature()->as_C_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// RedefineClasses() API support:
|
// RedefineClasses() API support:
|
||||||
// If this ConstantPoolCacheEntry refers to old_method then update it
|
// If this ConstantPoolCacheEntry refers to old_method then update it
|
||||||
// to refer to new_method.
|
// to refer to new_method.
|
||||||
bool ConstantPoolCacheEntry::adjust_method_entry(Method* old_method,
|
void ConstantPoolCacheEntry::adjust_method_entry(Method* old_method,
|
||||||
Method* new_method, bool * trace_name_printed) {
|
Method* new_method, bool * trace_name_printed) {
|
||||||
|
|
||||||
if (is_vfinal()) {
|
if (is_vfinal()) {
|
||||||
@ -525,63 +540,35 @@ bool ConstantPoolCacheEntry::adjust_method_entry(Method* old_method,
|
|||||||
// match old_method so need an update
|
// match old_method so need an update
|
||||||
// NOTE: can't use set_f2_as_vfinal_method as it asserts on different values
|
// NOTE: can't use set_f2_as_vfinal_method as it asserts on different values
|
||||||
_f2 = (intptr_t)new_method;
|
_f2 = (intptr_t)new_method;
|
||||||
if (log_is_enabled(Info, redefine, class, update)) {
|
log_adjust("vfinal", old_method, new_method, trace_name_printed);
|
||||||
ResourceMark rm;
|
|
||||||
if (!(*trace_name_printed)) {
|
|
||||||
log_info(redefine, class, update)("adjust: name=%s", old_method->method_holder()->external_name());
|
|
||||||
*trace_name_printed = true;
|
|
||||||
}
|
}
|
||||||
log_debug(redefine, class, update, constantpool)
|
return;
|
||||||
("cpc vf-entry update: %s(%s)", new_method->name()->as_C_string(), new_method->signature()->as_C_string());
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// f1() is not used with virtual entries so bail out
|
assert (_f1 != NULL, "should not call with uninteresting entry");
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_f1 == NULL) {
|
if (!(_f1->is_method())) {
|
||||||
// NULL f1() means this is a virtual entry so bail out
|
// _f1 is a Klass* for an interface, _f2 is the method
|
||||||
// We are assuming that the vtable index does not need change.
|
if (f2_as_interface_method() == old_method) {
|
||||||
return false;
|
_f2 = (intptr_t)new_method;
|
||||||
|
log_adjust("interface", old_method, new_method, trace_name_printed);
|
||||||
}
|
}
|
||||||
|
} else if (_f1 == old_method) {
|
||||||
if (_f1 == old_method) {
|
|
||||||
_f1 = new_method;
|
_f1 = new_method;
|
||||||
if (log_is_enabled(Info, redefine, class, update)) {
|
log_adjust("special, static or dynamic", old_method, new_method, trace_name_printed);
|
||||||
ResourceMark rm;
|
|
||||||
if (!(*trace_name_printed)) {
|
|
||||||
log_info(redefine, class, update)("adjust: name=%s", old_method->method_holder()->external_name());
|
|
||||||
*trace_name_printed = true;
|
|
||||||
}
|
}
|
||||||
log_debug(redefine, class, update, constantpool)
|
|
||||||
("cpc entry update: %s(%s)", new_method->name()->as_C_string(), new_method->signature()->as_C_string());
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// a constant pool cache entry should never contain old or obsolete methods
|
// a constant pool cache entry should never contain old or obsolete methods
|
||||||
bool ConstantPoolCacheEntry::check_no_old_or_obsolete_entries() {
|
bool ConstantPoolCacheEntry::check_no_old_or_obsolete_entries() {
|
||||||
if (is_vfinal()) {
|
Method* m = get_interesting_method_entry(NULL);
|
||||||
// virtual and final so _f2 contains method ptr instead of vtable index
|
// return false if m refers to a non-deleted old or obsolete method
|
||||||
Metadata* f2 = (Metadata*)_f2;
|
if (m != NULL) {
|
||||||
// Return false if _f2 refers to an old or an obsolete method.
|
assert(m->is_valid() && m->is_method(), "m is a valid method");
|
||||||
// _f2 == NULL || !_f2->is_method() are just as unexpected here.
|
return !m->is_old() && !m->is_obsolete(); // old is always set for old and obsolete
|
||||||
return (f2 != NULL NOT_PRODUCT(&& f2->is_valid()) && f2->is_method() &&
|
} else {
|
||||||
!((Method*)f2)->is_old() && !((Method*)f2)->is_obsolete());
|
|
||||||
} else if (_f1 == NULL ||
|
|
||||||
(NOT_PRODUCT(_f1->is_valid() &&) !_f1->is_method())) {
|
|
||||||
// _f1 == NULL || !_f1->is_method() are OK here
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// return false if _f1 refers to a non-deleted old or obsolete method
|
|
||||||
return (NOT_PRODUCT(_f1->is_valid() &&) _f1->is_method() &&
|
|
||||||
(f1_as_method()->is_deleted() ||
|
|
||||||
(!f1_as_method()->is_old() && !f1_as_method()->is_obsolete())));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Method* ConstantPoolCacheEntry::get_interesting_method_entry(Klass* k) {
|
Method* ConstantPoolCacheEntry::get_interesting_method_entry(Klass* k) {
|
||||||
@ -598,11 +585,12 @@ Method* ConstantPoolCacheEntry::get_interesting_method_entry(Klass* k) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
} else {
|
||||||
if (!(_f1->is_method())) {
|
if (!(_f1->is_method())) {
|
||||||
// _f1 can also contain a Klass* for an interface
|
// _f1 is a Klass* for an interface
|
||||||
return NULL;
|
m = f2_as_interface_method();
|
||||||
}
|
} else {
|
||||||
m = f1_as_method();
|
m = f1_as_method();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
assert(m != NULL && m->is_method(), "sanity check");
|
assert(m != NULL && m->is_method(), "sanity check");
|
||||||
if (m == NULL || !m->is_method() || (k != NULL && m->method_holder() != k)) {
|
if (m == NULL || !m->is_method() || (k != NULL && m->method_holder() != k)) {
|
||||||
// robustness for above sanity checks or method is not in
|
// robustness for above sanity checks or method is not in
|
||||||
|
@ -249,6 +249,7 @@ class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC {
|
|||||||
|
|
||||||
void set_itable_call(
|
void set_itable_call(
|
||||||
Bytecodes::Code invoke_code, // the bytecode used; must be invokeinterface
|
Bytecodes::Code invoke_code, // the bytecode used; must be invokeinterface
|
||||||
|
Klass* referenced_klass, // the referenced klass in the InterfaceMethodref
|
||||||
const methodHandle& method, // the resolved interface method
|
const methodHandle& method, // the resolved interface method
|
||||||
int itable_index // index into itable for the method
|
int itable_index // index into itable for the method
|
||||||
);
|
);
|
||||||
@ -352,6 +353,7 @@ class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC {
|
|||||||
bool is_f1_null() const { Metadata* f1 = f1_ord(); return f1 == NULL; } // classifies a CPC entry as unbound
|
bool is_f1_null() const { Metadata* f1 = f1_ord(); return f1 == NULL; } // classifies a CPC entry as unbound
|
||||||
int f2_as_index() const { assert(!is_vfinal(), ""); return (int) _f2; }
|
int f2_as_index() const { assert(!is_vfinal(), ""); return (int) _f2; }
|
||||||
Method* f2_as_vfinal_method() const { assert(is_vfinal(), ""); return (Method*)_f2; }
|
Method* f2_as_vfinal_method() const { assert(is_vfinal(), ""); return (Method*)_f2; }
|
||||||
|
Method* f2_as_interface_method() const { assert(bytecode_1() == Bytecodes::_invokeinterface, ""); return (Method*)_f2; }
|
||||||
intx flags_ord() const { return (intx)OrderAccess::load_acquire(&_flags); }
|
intx flags_ord() const { return (intx)OrderAccess::load_acquire(&_flags); }
|
||||||
int field_index() const { assert(is_field_entry(), ""); return (_flags & field_index_mask); }
|
int field_index() const { assert(is_field_entry(), ""); return (_flags & field_index_mask); }
|
||||||
int parameter_size() const { assert(is_method_entry(), ""); return (_flags & parameter_size_mask); }
|
int parameter_size() const { assert(is_method_entry(), ""); return (_flags & parameter_size_mask); }
|
||||||
@ -387,7 +389,7 @@ class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC {
|
|||||||
// trace_name_printed is set to true if the current call has
|
// trace_name_printed is set to true if the current call has
|
||||||
// printed the klass name so that other routines in the adjust_*
|
// printed the klass name so that other routines in the adjust_*
|
||||||
// group don't print the klass name.
|
// group don't print the klass name.
|
||||||
bool adjust_method_entry(Method* old_method, Method* new_method,
|
void adjust_method_entry(Method* old_method, Method* new_method,
|
||||||
bool* trace_name_printed);
|
bool* trace_name_printed);
|
||||||
bool check_no_old_or_obsolete_entries();
|
bool check_no_old_or_obsolete_entries();
|
||||||
Method* get_interesting_method_entry(Klass* k);
|
Method* get_interesting_method_entry(Klass* k);
|
||||||
|
@ -1185,7 +1185,6 @@ void klassItable::initialize_itable_for_interface(int method_table_offset, Klass
|
|||||||
Array<Method*>* methods = InstanceKlass::cast(interf)->methods();
|
Array<Method*>* methods = InstanceKlass::cast(interf)->methods();
|
||||||
int nof_methods = methods->length();
|
int nof_methods = methods->length();
|
||||||
HandleMark hm;
|
HandleMark hm;
|
||||||
assert(nof_methods > 0, "at least one method must exist for interface to be in vtable");
|
|
||||||
Handle interface_loader (THREAD, InstanceKlass::cast(interf)->class_loader());
|
Handle interface_loader (THREAD, InstanceKlass::cast(interf)->class_loader());
|
||||||
|
|
||||||
int ime_count = method_count_for_interface(interf);
|
int ime_count = method_count_for_interface(interf);
|
||||||
@ -1354,8 +1353,10 @@ void visit_all_interfaces(Array<Klass*>* transitive_intf, InterfaceVisiterClosur
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only count interfaces with at least one method
|
// Visit all interfaces which either have any methods or can participate in receiver type check.
|
||||||
if (method_count > 0) {
|
// We do not bother to count methods in transitive interfaces, although that would allow us to skip
|
||||||
|
// this step in the rare case of a zero-method interface extending another zero-method interface.
|
||||||
|
if (method_count > 0 || InstanceKlass::cast(intf)->transitive_interfaces()->length() > 0) {
|
||||||
blk->doit(intf, method_count);
|
blk->doit(intf, method_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -698,6 +698,7 @@ class Method : public Metadata {
|
|||||||
static ByteSize from_interpreted_offset() { return byte_offset_of(Method, _from_interpreted_entry ); }
|
static ByteSize from_interpreted_offset() { return byte_offset_of(Method, _from_interpreted_entry ); }
|
||||||
static ByteSize interpreter_entry_offset() { return byte_offset_of(Method, _i2i_entry ); }
|
static ByteSize interpreter_entry_offset() { return byte_offset_of(Method, _i2i_entry ); }
|
||||||
static ByteSize signature_handler_offset() { return in_ByteSize(sizeof(Method) + wordSize); }
|
static ByteSize signature_handler_offset() { return in_ByteSize(sizeof(Method) + wordSize); }
|
||||||
|
static ByteSize itable_index_offset() { return byte_offset_of(Method, _vtable_index ); }
|
||||||
|
|
||||||
// for code generation
|
// for code generation
|
||||||
static int method_data_offset_in_bytes() { return offset_of(Method, _method_data); }
|
static int method_data_offset_in_bytes() { return offset_of(Method, _method_data); }
|
||||||
|
@ -233,7 +233,7 @@ typedef PaddedEnd<ObjectMonitor> PaddedObjectMonitor;
|
|||||||
nonstatic_field(ArrayKlass, _dimension, int) \
|
nonstatic_field(ArrayKlass, _dimension, int) \
|
||||||
volatile_nonstatic_field(ArrayKlass, _higher_dimension, Klass*) \
|
volatile_nonstatic_field(ArrayKlass, _higher_dimension, Klass*) \
|
||||||
volatile_nonstatic_field(ArrayKlass, _lower_dimension, Klass*) \
|
volatile_nonstatic_field(ArrayKlass, _lower_dimension, Klass*) \
|
||||||
nonstatic_field(CompiledICHolder, _holder_method, Method*) \
|
nonstatic_field(CompiledICHolder, _holder_metadata, Metadata*) \
|
||||||
nonstatic_field(CompiledICHolder, _holder_klass, Klass*) \
|
nonstatic_field(CompiledICHolder, _holder_klass, Klass*) \
|
||||||
nonstatic_field(ConstantPool, _tags, Array<u1>*) \
|
nonstatic_field(ConstantPool, _tags, Array<u1>*) \
|
||||||
nonstatic_field(ConstantPool, _cache, ConstantPoolCache*) \
|
nonstatic_field(ConstantPool, _cache, ConstantPoolCache*) \
|
||||||
|
@ -80,14 +80,21 @@ class DirectMethodHandle extends MethodHandle {
|
|||||||
mtype = mtype.insertParameterTypes(0, receiver);
|
mtype = mtype.insertParameterTypes(0, receiver);
|
||||||
}
|
}
|
||||||
if (!member.isField()) {
|
if (!member.isField()) {
|
||||||
if (refKind == REF_invokeSpecial) {
|
switch (refKind) {
|
||||||
|
case REF_invokeSpecial: {
|
||||||
member = member.asSpecial();
|
member = member.asSpecial();
|
||||||
LambdaForm lform = preparedLambdaForm(member);
|
LambdaForm lform = preparedLambdaForm(member);
|
||||||
return new Special(mtype, lform, member);
|
return new Special(mtype, lform, member);
|
||||||
} else {
|
}
|
||||||
|
case REF_invokeInterface: {
|
||||||
|
LambdaForm lform = preparedLambdaForm(member);
|
||||||
|
return new Interface(mtype, lform, member, receiver);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
LambdaForm lform = preparedLambdaForm(member);
|
LambdaForm lform = preparedLambdaForm(member);
|
||||||
return new DirectMethodHandle(mtype, lform, member);
|
return new DirectMethodHandle(mtype, lform, member);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
LambdaForm lform = preparedFieldLambdaForm(member);
|
LambdaForm lform = preparedFieldLambdaForm(member);
|
||||||
if (member.isStatic()) {
|
if (member.isStatic()) {
|
||||||
@ -190,6 +197,7 @@ class DirectMethodHandle extends MethodHandle {
|
|||||||
static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) {
|
static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) {
|
||||||
boolean needsInit = (which == LF_INVSTATIC_INIT);
|
boolean needsInit = (which == LF_INVSTATIC_INIT);
|
||||||
boolean doesAlloc = (which == LF_NEWINVSPECIAL);
|
boolean doesAlloc = (which == LF_NEWINVSPECIAL);
|
||||||
|
boolean needsReceiverCheck = (which == LF_INVINTERFACE);
|
||||||
String linkerName;
|
String linkerName;
|
||||||
LambdaForm.Kind kind;
|
LambdaForm.Kind kind;
|
||||||
switch (which) {
|
switch (which) {
|
||||||
@ -219,6 +227,7 @@ class DirectMethodHandle extends MethodHandle {
|
|||||||
int nameCursor = ARG_LIMIT;
|
int nameCursor = ARG_LIMIT;
|
||||||
final int NEW_OBJ = (doesAlloc ? nameCursor++ : -1);
|
final int NEW_OBJ = (doesAlloc ? nameCursor++ : -1);
|
||||||
final int GET_MEMBER = nameCursor++;
|
final int GET_MEMBER = nameCursor++;
|
||||||
|
final int CHECK_RECEIVER = (needsReceiverCheck ? nameCursor++ : -1);
|
||||||
final int LINKER_CALL = nameCursor++;
|
final int LINKER_CALL = nameCursor++;
|
||||||
Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
|
Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
|
||||||
assert(names.length == nameCursor);
|
assert(names.length == nameCursor);
|
||||||
@ -233,6 +242,10 @@ class DirectMethodHandle extends MethodHandle {
|
|||||||
}
|
}
|
||||||
assert(findDirectMethodHandle(names[GET_MEMBER]) == names[DMH_THIS]);
|
assert(findDirectMethodHandle(names[GET_MEMBER]) == names[DMH_THIS]);
|
||||||
Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class);
|
Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class);
|
||||||
|
if (needsReceiverCheck) {
|
||||||
|
names[CHECK_RECEIVER] = new Name(getFunction(NF_checkReceiver), names[DMH_THIS], names[ARG_BASE]);
|
||||||
|
outArgs[0] = names[CHECK_RECEIVER];
|
||||||
|
}
|
||||||
assert(outArgs[outArgs.length-1] == names[GET_MEMBER]); // look, shifted args!
|
assert(outArgs[outArgs.length-1] == names[GET_MEMBER]); // look, shifted args!
|
||||||
int result = LAST_RESULT;
|
int result = LAST_RESULT;
|
||||||
if (doesAlloc) {
|
if (doesAlloc) {
|
||||||
@ -376,6 +389,29 @@ class DirectMethodHandle extends MethodHandle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** This subclass represents invokeinterface instructions. */
|
||||||
|
static class Interface extends DirectMethodHandle {
|
||||||
|
private final Class<?> refc;
|
||||||
|
private Interface(MethodType mtype, LambdaForm form, MemberName member, Class<?> refc) {
|
||||||
|
super(mtype, form, member);
|
||||||
|
assert refc.isInterface() : refc;
|
||||||
|
this.refc = refc;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
MethodHandle copyWith(MethodType mt, LambdaForm lf) {
|
||||||
|
return new Interface(mt, lf, member, refc);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object checkReceiver(Object recv) {
|
||||||
|
if (!refc.isInstance(recv)) {
|
||||||
|
String msg = String.format("Class %s does not implement the requested interface %s",
|
||||||
|
recv.getClass().getName(), refc.getName());
|
||||||
|
throw new IncompatibleClassChangeError(msg);
|
||||||
|
}
|
||||||
|
return recv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** This subclass handles constructor references. */
|
/** This subclass handles constructor references. */
|
||||||
static class Constructor extends DirectMethodHandle {
|
static class Constructor extends DirectMethodHandle {
|
||||||
final MemberName initMethod;
|
final MemberName initMethod;
|
||||||
@ -738,7 +774,8 @@ class DirectMethodHandle extends MethodHandle {
|
|||||||
NF_allocateInstance = 8,
|
NF_allocateInstance = 8,
|
||||||
NF_constructorMethod = 9,
|
NF_constructorMethod = 9,
|
||||||
NF_UNSAFE = 10,
|
NF_UNSAFE = 10,
|
||||||
NF_LIMIT = 11;
|
NF_checkReceiver = 11,
|
||||||
|
NF_LIMIT = 12;
|
||||||
|
|
||||||
private static final @Stable NamedFunction[] NFS = new NamedFunction[NF_LIMIT];
|
private static final @Stable NamedFunction[] NFS = new NamedFunction[NF_LIMIT];
|
||||||
|
|
||||||
@ -785,6 +822,11 @@ class DirectMethodHandle extends MethodHandle {
|
|||||||
return new NamedFunction(
|
return new NamedFunction(
|
||||||
MemberName.getFactory()
|
MemberName.getFactory()
|
||||||
.resolveOrFail(REF_getField, member, DirectMethodHandle.class, NoSuchMethodException.class));
|
.resolveOrFail(REF_getField, member, DirectMethodHandle.class, NoSuchMethodException.class));
|
||||||
|
case NF_checkReceiver:
|
||||||
|
member = new MemberName(Interface.class, "checkReceiver", OBJ_OBJ_TYPE, REF_invokeVirtual);
|
||||||
|
return new NamedFunction(
|
||||||
|
MemberName.getFactory()
|
||||||
|
.resolveOrFail(REF_invokeVirtual, member, Interface.class, NoSuchMethodException.class));
|
||||||
default:
|
default:
|
||||||
throw newInternalError("Unknown function: " + func);
|
throw newInternalError("Unknown function: " + func);
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ public class CompiledICHolder extends VMObject {
|
|||||||
|
|
||||||
private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
|
private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
|
||||||
Type type = db.lookupType("CompiledICHolder");
|
Type type = db.lookupType("CompiledICHolder");
|
||||||
holderMethod = new MetadataField(type.getAddressField("_holder_method"), 0);
|
holderMetadata = new MetadataField(type.getAddressField("_holder_metadata"), 0);
|
||||||
holderKlass = new MetadataField(type.getAddressField("_holder_klass"), 0);
|
holderKlass = new MetadataField(type.getAddressField("_holder_klass"), 0);
|
||||||
headerSize = type.getSize();
|
headerSize = type.getSize();
|
||||||
}
|
}
|
||||||
@ -55,11 +55,11 @@ public class CompiledICHolder extends VMObject {
|
|||||||
private static long headerSize;
|
private static long headerSize;
|
||||||
|
|
||||||
// Fields
|
// Fields
|
||||||
private static MetadataField holderMethod;
|
private static MetadataField holderMetadata;
|
||||||
private static MetadataField holderKlass;
|
private static MetadataField holderKlass;
|
||||||
|
|
||||||
// Accessors for declared fields
|
// Accessors for declared fields
|
||||||
public Method getHolderMethod() { return (Method) holderMethod.getValue(this); }
|
public Metadata getHolderMetadata() { return (Metadata) holderMetadata.getValue(this); }
|
||||||
public Klass getHolderKlass() { return (Klass) holderKlass.getValue(this); }
|
public Klass getHolderKlass() { return (Klass) holderKlass.getValue(this); }
|
||||||
|
|
||||||
public void printValueOn(PrintStream tty) {
|
public void printValueOn(PrintStream tty) {
|
||||||
|
52
test/hotspot/gtest/code/test_vtableStub.cpp
Normal file
52
test/hotspot/gtest/code/test_vtableStub.cpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "precompiled.hpp"
|
||||||
|
#include "code/vtableStubs.hpp"
|
||||||
|
#include "runtime/interfaceSupport.hpp"
|
||||||
|
#include "unittest.hpp"
|
||||||
|
|
||||||
|
TEST_VM(code, vtableStubs) {
|
||||||
|
// Should be in VM to use locks
|
||||||
|
ThreadInVMfromNative ThreadInVMfromNative(JavaThread::current());
|
||||||
|
|
||||||
|
VtableStubs::find_vtable_stub(0); // min vtable index
|
||||||
|
for (int i = 0; i < 15; i++) {
|
||||||
|
VtableStubs::find_vtable_stub((1 << i) - 1);
|
||||||
|
VtableStubs::find_vtable_stub((1 << i));
|
||||||
|
}
|
||||||
|
VtableStubs::find_vtable_stub((1 << 15) - 1); // max vtable index
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_VM(code, itableStubs) {
|
||||||
|
// Should be in VM to use locks
|
||||||
|
ThreadInVMfromNative ThreadInVMfromNative(JavaThread::current());
|
||||||
|
|
||||||
|
VtableStubs::find_itable_stub(0); // min itable index
|
||||||
|
for (int i = 0; i < 15; i++) {
|
||||||
|
VtableStubs::find_itable_stub((1 << i) - 1);
|
||||||
|
VtableStubs::find_itable_stub((1 << i));
|
||||||
|
}
|
||||||
|
VtableStubs::find_itable_stub((1 << 15) - 1); // max itable index
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, 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 8174962
|
||||||
|
* @summary Redefine class with interface method call
|
||||||
|
* @library /test/lib
|
||||||
|
* @modules java.base/jdk.internal.misc
|
||||||
|
* @modules java.compiler
|
||||||
|
* java.instrument
|
||||||
|
* jdk.jartool/sun.tools.jar
|
||||||
|
* @run main RedefineClassHelper
|
||||||
|
* @run main/othervm -javaagent:redefineagent.jar -Xlog:redefine+class+update*=trace RedefineInterfaceCall
|
||||||
|
*/
|
||||||
|
|
||||||
|
import static jdk.test.lib.Asserts.assertEquals;
|
||||||
|
|
||||||
|
interface I1 { default int m() { return 0; } }
|
||||||
|
interface I2 extends I1 {}
|
||||||
|
|
||||||
|
public class RedefineInterfaceCall {
|
||||||
|
|
||||||
|
public static class C implements I2 {
|
||||||
|
public int test(I2 i) {
|
||||||
|
return i.m(); // invokeinterface cpCacheEntry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String newI1 =
|
||||||
|
"interface I1 { default int m() { return 1; } }";
|
||||||
|
|
||||||
|
static String newC =
|
||||||
|
"public class RedefineInterfaceCall$C implements I2 { " +
|
||||||
|
" public int test(I2 i) { " +
|
||||||
|
" return i.m(); " +
|
||||||
|
" } " +
|
||||||
|
"} ";
|
||||||
|
|
||||||
|
static int test(I2 i) {
|
||||||
|
return i.m(); // invokeinterface cpCacheEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
C c = new C();
|
||||||
|
|
||||||
|
assertEquals(test(c), 0);
|
||||||
|
assertEquals(c.test(c), 0);
|
||||||
|
|
||||||
|
RedefineClassHelper.redefineClass(C.class, newC);
|
||||||
|
|
||||||
|
assertEquals(c.test(c), 0);
|
||||||
|
|
||||||
|
RedefineClassHelper.redefineClass(I1.class, newI1);
|
||||||
|
|
||||||
|
assertEquals(test(c), 1);
|
||||||
|
assertEquals(c.test(c), 1);
|
||||||
|
|
||||||
|
RedefineClassHelper.redefineClass(C.class, newC);
|
||||||
|
|
||||||
|
assertEquals(c.test(c), 1);
|
||||||
|
}
|
||||||
|
}
|
@ -63,8 +63,8 @@ public class TransformTestCommon {
|
|||||||
String parent, String child)
|
String parent, String child)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
String parentSharedMatch = parent + " source: shared objects file";
|
String parentSharedMatch = " " + parent + " source: shared objects file";
|
||||||
String childSharedMatch = child + " source: shared objects file";
|
String childSharedMatch = " " + child + " source: shared objects file";
|
||||||
|
|
||||||
if (entry.isParentExpectedShared)
|
if (entry.isParentExpectedShared)
|
||||||
out.shouldContain(parentSharedMatch);
|
out.shouldContain(parentSharedMatch);
|
||||||
|
Loading…
Reference in New Issue
Block a user