8072008: Emit direct call instead of linkTo* for recursive indy/MH.invoke* calls
Reviewed-by: jrose, dlong, aph, forax
This commit is contained in:
parent
8c5da27f19
commit
d60a09e9c5
@ -4667,17 +4667,12 @@ encode %{
|
||||
if (!_method) {
|
||||
// A call to a runtime wrapper, e.g. new, new_typeArray_Java, uncommon_trap.
|
||||
call = __ trampoline_call(Address(addr, relocInfo::runtime_call_type), &cbuf);
|
||||
} else if (_optimized_virtual) {
|
||||
call = __ trampoline_call(Address(addr, relocInfo::opt_virtual_call_type), &cbuf);
|
||||
} else {
|
||||
call = __ trampoline_call(Address(addr, relocInfo::static_call_type), &cbuf);
|
||||
}
|
||||
if (call == NULL) {
|
||||
ciEnv::current()->record_failure("CodeCache is full");
|
||||
return;
|
||||
}
|
||||
int method_index = resolved_method_index(cbuf);
|
||||
RelocationHolder rspec = _optimized_virtual ? opt_virtual_call_Relocation::spec(method_index)
|
||||
: static_call_Relocation::spec(method_index);
|
||||
call = __ trampoline_call(Address(addr, rspec), &cbuf);
|
||||
|
||||
if (_method) {
|
||||
// Emit stub for static call
|
||||
address stub = CompiledStaticCall::emit_to_interp_stub(cbuf);
|
||||
if (stub == NULL) {
|
||||
@ -4685,11 +4680,16 @@ encode %{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (call == NULL) {
|
||||
ciEnv::current()->record_failure("CodeCache is full");
|
||||
return;
|
||||
}
|
||||
%}
|
||||
|
||||
enc_class aarch64_enc_java_dynamic_call(method meth) %{
|
||||
MacroAssembler _masm(&cbuf);
|
||||
address call = __ ic_call((address)$meth$$method);
|
||||
int method_index = resolved_method_index(cbuf);
|
||||
address call = __ ic_call((address)$meth$$method, method_index);
|
||||
if (call == NULL) {
|
||||
ciEnv::current()->record_failure("CodeCache is full");
|
||||
return;
|
||||
|
@ -732,8 +732,8 @@ address MacroAssembler::emit_trampoline_stub(int insts_call_instruction_offset,
|
||||
return stub;
|
||||
}
|
||||
|
||||
address MacroAssembler::ic_call(address entry) {
|
||||
RelocationHolder rh = virtual_call_Relocation::spec(pc());
|
||||
address MacroAssembler::ic_call(address entry, jint method_index) {
|
||||
RelocationHolder rh = virtual_call_Relocation::spec(pc(), method_index);
|
||||
// address const_ptr = long_constant((jlong)Universe::non_oop_word());
|
||||
// unsigned long offset;
|
||||
// ldr_constant(rscratch2, const_ptr);
|
||||
|
@ -983,7 +983,7 @@ public:
|
||||
}
|
||||
|
||||
// Emit the CompiledIC call idiom
|
||||
address ic_call(address entry);
|
||||
address ic_call(address entry, jint method_index = 0);
|
||||
|
||||
public:
|
||||
|
||||
|
@ -3396,11 +3396,13 @@ encode %{
|
||||
}
|
||||
const int entry_point_toc_offset = __ offset_to_method_toc(entry_point_toc_addr);
|
||||
|
||||
|
||||
// Emit the trampoline stub which will be related to the branch-and-link below.
|
||||
CallStubImpl::emit_trampoline_stub(_masm, entry_point_toc_offset, start_offset);
|
||||
if (ciEnv::current()->failing()) { return; } // Code cache may be full.
|
||||
__ relocate(_optimized_virtual ?
|
||||
relocInfo::opt_virtual_call_type : relocInfo::static_call_type);
|
||||
int method_index = resolved_method_index(cbuf);
|
||||
__ relocate(_optimized_virtual ? opt_virtual_call_Relocate::spec(method_index)
|
||||
: static_call_Relocate::spec(method_index));
|
||||
}
|
||||
|
||||
// The real call.
|
||||
@ -3450,8 +3452,8 @@ encode %{
|
||||
const address virtual_call_oop_addr = __ addr_at(virtual_call_oop_addr_offset);
|
||||
assert(MacroAssembler::is_load_const_from_method_toc_at(virtual_call_oop_addr),
|
||||
"should be load from TOC");
|
||||
|
||||
__ relocate(virtual_call_Relocation::spec(virtual_call_oop_addr));
|
||||
int method_index = resolved_method_index(cbuf);
|
||||
__ relocate(virtual_call_Relocation::spec(virtual_call_oop_addr, method_index));
|
||||
}
|
||||
|
||||
// At this point I do not have the address of the trampoline stub,
|
||||
|
@ -816,6 +816,8 @@ public:
|
||||
inline void call( address d, relocInfo::relocType rt = relocInfo::runtime_call_type );
|
||||
inline void call( Label& L, relocInfo::relocType rt = relocInfo::runtime_call_type );
|
||||
|
||||
inline void call( address d, RelocationHolder const& rspec );
|
||||
|
||||
public:
|
||||
|
||||
// pp 150
|
||||
|
@ -76,6 +76,8 @@ inline void Assembler::cbcond(Condition c, CC cc, Register s1, int simm5, Label&
|
||||
inline void Assembler::call( address d, relocInfo::relocType rt ) { insert_nop_after_cbcond(); cti(); emit_data( op(call_op) | wdisp(intptr_t(d), intptr_t(pc()), 30), rt); has_delay_slot(); assert(rt != relocInfo::virtual_call_type, "must use virtual_call_Relocation::spec"); }
|
||||
inline void Assembler::call( Label& L, relocInfo::relocType rt ) { insert_nop_after_cbcond(); call( target(L), rt); }
|
||||
|
||||
inline void Assembler::call( address d, RelocationHolder const& rspec ) { insert_nop_after_cbcond(); cti(); emit_data( op(call_op) | wdisp(intptr_t(d), intptr_t(pc()), 30), rspec); has_delay_slot(); assert(rspec.type() != relocInfo::virtual_call_type, "must use virtual_call_Relocation::spec"); }
|
||||
|
||||
inline void Assembler::flush( Register s1, Register s2) { emit_int32( op(arith_op) | op3(flush_op3) | rs1(s1) | rs2(s2)); }
|
||||
inline void Assembler::flush( Register s1, int simm13a) { emit_data( op(arith_op) | op3(flush_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); }
|
||||
|
||||
|
@ -770,8 +770,8 @@ void MacroAssembler::set_vm_result(Register oop_result) {
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::ic_call(address entry, bool emit_delay) {
|
||||
RelocationHolder rspec = virtual_call_Relocation::spec(pc());
|
||||
void MacroAssembler::ic_call(address entry, bool emit_delay, jint method_index) {
|
||||
RelocationHolder rspec = virtual_call_Relocation::spec(pc(), method_index);
|
||||
patchable_set((intptr_t)Universe::non_oop_word(), G5_inline_cache_reg);
|
||||
relocate(rspec);
|
||||
call(entry, relocInfo::none);
|
||||
@ -780,7 +780,6 @@ void MacroAssembler::ic_call(address entry, bool emit_delay) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::card_table_write(jbyte* byte_map_base,
|
||||
Register tmp, Register obj) {
|
||||
#ifdef _LP64
|
||||
|
@ -729,7 +729,11 @@ class MacroAssembler : public Assembler {
|
||||
// Check if the call target is out of wdisp30 range (relative to the code cache)
|
||||
static inline bool is_far_target(address d);
|
||||
inline void call( address d, relocInfo::relocType rt = relocInfo::runtime_call_type );
|
||||
inline void call( address d, RelocationHolder const& rspec);
|
||||
|
||||
inline void call( Label& L, relocInfo::relocType rt = relocInfo::runtime_call_type );
|
||||
inline void call( Label& L, RelocationHolder const& rspec);
|
||||
|
||||
inline void callr( Register s1, Register s2 );
|
||||
inline void callr( Register s1, int simm13a, RelocationHolder const& rspec = RelocationHolder() );
|
||||
|
||||
@ -1146,7 +1150,7 @@ public:
|
||||
void set_vm_result(Register oop_result);
|
||||
|
||||
// Emit the CompiledIC call idiom
|
||||
void ic_call(address entry, bool emit_delay = true);
|
||||
void ic_call(address entry, bool emit_delay = true, jint method_index = 0);
|
||||
|
||||
// if call_VM_base was called with check_exceptions=false, then call
|
||||
// check_and_forward_exception to handle exceptions when it is safe
|
||||
|
@ -298,6 +298,10 @@ inline bool MacroAssembler::is_far_target(address d) {
|
||||
// expense of relocation and if we overflow the displacement
|
||||
// of the quick call instruction.
|
||||
inline void MacroAssembler::call( address d, relocInfo::relocType rt ) {
|
||||
MacroAssembler::call(d, Relocation::spec_simple(rt));
|
||||
}
|
||||
|
||||
inline void MacroAssembler::call( address d, RelocationHolder const& rspec ) {
|
||||
#ifdef _LP64
|
||||
intptr_t disp;
|
||||
// NULL is ok because it will be relocated later.
|
||||
@ -309,14 +313,14 @@ inline void MacroAssembler::call( address d, relocInfo::relocType rt ) {
|
||||
// Is this address within range of the call instruction?
|
||||
// If not, use the expensive instruction sequence
|
||||
if (is_far_target(d)) {
|
||||
relocate(rt);
|
||||
relocate(rspec);
|
||||
AddressLiteral dest(d);
|
||||
jumpl_to(dest, O7, O7);
|
||||
} else {
|
||||
Assembler::call(d, rt);
|
||||
Assembler::call(d, rspec);
|
||||
}
|
||||
#else
|
||||
Assembler::call( d, rt );
|
||||
Assembler::call( d, rspec );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -131,8 +131,9 @@ bool NativeInstruction::is_load_store_with_small_offset(Register reg) {
|
||||
void NativeCall::verify() {
|
||||
NativeInstruction::verify();
|
||||
// make sure code pattern is actually a call instruction
|
||||
if (!is_op(long_at(0), Assembler::call_op)) {
|
||||
fatal("not a call");
|
||||
int x = long_at(0);
|
||||
if (!is_op(x, Assembler::call_op)) {
|
||||
fatal("not a call: 0x%x @ " INTPTR_FORMAT, x, p2i(instruction_address()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1001,7 +1001,7 @@ void emit_form3_mem_reg(CodeBuffer &cbuf, PhaseRegAlloc* ra, const MachNode* n,
|
||||
#endif
|
||||
}
|
||||
|
||||
void emit_call_reloc(CodeBuffer &cbuf, intptr_t entry_point, relocInfo::relocType rtype, bool preserve_g2 = false) {
|
||||
void emit_call_reloc(CodeBuffer &cbuf, intptr_t entry_point, RelocationHolder const& rspec, bool preserve_g2 = false) {
|
||||
// The method which records debug information at every safepoint
|
||||
// expects the call to be the first instruction in the snippet as
|
||||
// it creates a PcDesc structure which tracks the offset of a call
|
||||
@ -1023,7 +1023,7 @@ void emit_call_reloc(CodeBuffer &cbuf, intptr_t entry_point, relocInfo::relocTyp
|
||||
int startpos = __ offset();
|
||||
#endif /* ASSERT */
|
||||
|
||||
__ call((address)entry_point, rtype);
|
||||
__ call((address)entry_point, rspec);
|
||||
|
||||
if (preserve_g2) __ delayed()->mov(G2, L7);
|
||||
else __ delayed()->nop();
|
||||
@ -2593,8 +2593,7 @@ encode %{
|
||||
enc_class Java_To_Runtime (method meth) %{ // CALL Java_To_Runtime
|
||||
// CALL directly to the runtime
|
||||
// The user of this is responsible for ensuring that R_L7 is empty (killed).
|
||||
emit_call_reloc(cbuf, $meth$$method, relocInfo::runtime_call_type,
|
||||
/*preserve_g2=*/true);
|
||||
emit_call_reloc(cbuf, $meth$$method, runtime_call_Relocation::spec(), /*preserve_g2=*/true);
|
||||
%}
|
||||
|
||||
enc_class preserve_SP %{
|
||||
@ -2611,13 +2610,14 @@ encode %{
|
||||
// CALL to fixup routine. Fixup routine uses ScopeDesc info to determine
|
||||
// who we intended to call.
|
||||
if (!_method) {
|
||||
emit_call_reloc(cbuf, $meth$$method, relocInfo::runtime_call_type);
|
||||
} else if (_optimized_virtual) {
|
||||
emit_call_reloc(cbuf, $meth$$method, relocInfo::opt_virtual_call_type);
|
||||
emit_call_reloc(cbuf, $meth$$method, runtime_call_Relocation::spec());
|
||||
} else {
|
||||
emit_call_reloc(cbuf, $meth$$method, relocInfo::static_call_type);
|
||||
}
|
||||
if (_method) { // Emit stub for static call.
|
||||
int method_index = resolved_method_index(cbuf);
|
||||
RelocationHolder rspec = _optimized_virtual ? opt_virtual_call_Relocation::spec(method_index)
|
||||
: static_call_Relocation::spec(method_index);
|
||||
emit_call_reloc(cbuf, $meth$$method, rspec);
|
||||
|
||||
// Emit stub for static call.
|
||||
address stub = CompiledStaticCall::emit_to_interp_stub(cbuf);
|
||||
// Stub does not fit into scratch buffer if TraceJumps is enabled
|
||||
if (stub == NULL && !(TraceJumps && Compile::current()->in_scratch_emit_size())) {
|
||||
@ -2638,7 +2638,7 @@ encode %{
|
||||
Register G5_ic_reg = reg_to_register_object(Matcher::inline_cache_reg_encode());
|
||||
assert(G5_ic_reg == G5_inline_cache_reg, "G5_inline_cache_reg used in assemble_ic_buffer_code()");
|
||||
assert(G5_ic_reg == G5_megamorphic_method, "G5_megamorphic_method used in megamorphic call stub");
|
||||
__ ic_call((address)$meth$$method);
|
||||
__ ic_call((address)$meth$$method, /*emit_delay=*/true, resolved_method_index(cbuf));
|
||||
} else {
|
||||
assert(!UseInlineCaches, "expect vtable calls only if not using ICs");
|
||||
// Just go thru the vtable
|
||||
@ -10069,10 +10069,10 @@ instruct string_compareL(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, not
|
||||
format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp" %}
|
||||
ins_encode %{
|
||||
__ string_compare($str1$$Register, $str2$$Register,
|
||||
$cnt1$$Register, $cnt2$$Register,
|
||||
$cnt1$$Register, $cnt2$$Register,
|
||||
$tmp$$Register, $tmp$$Register,
|
||||
$result$$Register, StrIntrinsicNode::LL);
|
||||
%}
|
||||
%}
|
||||
ins_pipe(long_memory_op);
|
||||
%}
|
||||
|
||||
@ -10088,7 +10088,7 @@ instruct string_compareU(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, not
|
||||
$cnt1$$Register, $cnt2$$Register,
|
||||
$tmp$$Register, $tmp$$Register,
|
||||
$result$$Register, StrIntrinsicNode::UU);
|
||||
%}
|
||||
%}
|
||||
ins_pipe(long_memory_op);
|
||||
%}
|
||||
|
||||
@ -10104,7 +10104,7 @@ instruct string_compareLU(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, no
|
||||
$cnt1$$Register, $cnt2$$Register,
|
||||
$tmp1$$Register, $tmp2$$Register,
|
||||
$result$$Register, StrIntrinsicNode::LU);
|
||||
%}
|
||||
%}
|
||||
ins_pipe(long_memory_op);
|
||||
%}
|
||||
|
||||
@ -10117,10 +10117,10 @@ instruct string_compareUL(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, no
|
||||
format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1,$tmp2" %}
|
||||
ins_encode %{
|
||||
__ string_compare($str2$$Register, $str1$$Register,
|
||||
$cnt2$$Register, $cnt1$$Register,
|
||||
$cnt2$$Register, $cnt1$$Register,
|
||||
$tmp1$$Register, $tmp2$$Register,
|
||||
$result$$Register, StrIntrinsicNode::UL);
|
||||
%}
|
||||
%}
|
||||
ins_pipe(long_memory_op);
|
||||
%}
|
||||
|
||||
|
@ -2260,8 +2260,8 @@ void MacroAssembler::call(AddressLiteral entry) {
|
||||
}
|
||||
}
|
||||
|
||||
void MacroAssembler::ic_call(address entry) {
|
||||
RelocationHolder rh = virtual_call_Relocation::spec(pc());
|
||||
void MacroAssembler::ic_call(address entry, jint method_index) {
|
||||
RelocationHolder rh = virtual_call_Relocation::spec(pc(), method_index);
|
||||
movptr(rax, (intptr_t)Universe::non_oop_word());
|
||||
call(AddressLiteral(entry, rh));
|
||||
}
|
||||
|
@ -850,7 +850,7 @@ class MacroAssembler: public Assembler {
|
||||
void call(AddressLiteral entry);
|
||||
|
||||
// Emit the CompiledIC call idiom
|
||||
void ic_call(address entry);
|
||||
void ic_call(address entry, jint method_index = 0);
|
||||
|
||||
// Jumps
|
||||
|
||||
|
@ -1898,17 +1898,18 @@ encode %{
|
||||
// who we intended to call.
|
||||
cbuf.set_insts_mark();
|
||||
$$$emit8$primary;
|
||||
|
||||
if (!_method) {
|
||||
emit_d32_reloc(cbuf, ($meth$$method - (int)(cbuf.insts_end()) - 4),
|
||||
runtime_call_Relocation::spec(), RELOC_IMM32 );
|
||||
} else if (_optimized_virtual) {
|
||||
emit_d32_reloc(cbuf, ($meth$$method - (int)(cbuf.insts_end()) - 4),
|
||||
opt_virtual_call_Relocation::spec(), RELOC_IMM32 );
|
||||
runtime_call_Relocation::spec(),
|
||||
RELOC_IMM32);
|
||||
} else {
|
||||
int method_index = resolved_method_index(cbuf);
|
||||
RelocationHolder rspec = _optimized_virtual ? opt_virtual_call_Relocation::spec(method_index)
|
||||
: static_call_Relocation::spec(method_index);
|
||||
emit_d32_reloc(cbuf, ($meth$$method - (int)(cbuf.insts_end()) - 4),
|
||||
static_call_Relocation::spec(), RELOC_IMM32 );
|
||||
}
|
||||
if (_method) { // Emit stub for static call.
|
||||
rspec, RELOC_DISP32);
|
||||
// Emit stubs for static call.
|
||||
address stub = CompiledStaticCall::emit_to_interp_stub(cbuf);
|
||||
if (stub == NULL) {
|
||||
ciEnv::current()->record_failure("CodeCache is full");
|
||||
@ -1919,7 +1920,7 @@ encode %{
|
||||
|
||||
enc_class Java_Dynamic_Call (method meth) %{ // JAVA DYNAMIC CALL
|
||||
MacroAssembler _masm(&cbuf);
|
||||
__ ic_call((address)$meth$$method);
|
||||
__ ic_call((address)$meth$$method, resolved_method_index(cbuf));
|
||||
%}
|
||||
|
||||
enc_class Java_Compiled_Call (method meth) %{ // JAVA COMPILED CALL
|
||||
@ -11504,7 +11505,7 @@ instruct string_equals(eDIRegP str1, eSIRegP str2, eCXRegI cnt, eAXRegI result,
|
||||
__ arrays_equals(false, $str1$$Register, $str2$$Register,
|
||||
$cnt$$Register, $result$$Register, $tmp3$$Register,
|
||||
$tmp1$$XMMRegister, $tmp2$$XMMRegister, false /* char */);
|
||||
%}
|
||||
%}
|
||||
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
@ -2120,22 +2120,15 @@ encode %{
|
||||
$$$emit8$primary;
|
||||
|
||||
if (!_method) {
|
||||
emit_d32_reloc(cbuf,
|
||||
(int) ($meth$$method - ((intptr_t) cbuf.insts_end()) - 4),
|
||||
emit_d32_reloc(cbuf, (int) ($meth$$method - ((intptr_t) cbuf.insts_end()) - 4),
|
||||
runtime_call_Relocation::spec(),
|
||||
RELOC_DISP32);
|
||||
} else if (_optimized_virtual) {
|
||||
emit_d32_reloc(cbuf,
|
||||
(int) ($meth$$method - ((intptr_t) cbuf.insts_end()) - 4),
|
||||
opt_virtual_call_Relocation::spec(),
|
||||
RELOC_DISP32);
|
||||
} else {
|
||||
emit_d32_reloc(cbuf,
|
||||
(int) ($meth$$method - ((intptr_t) cbuf.insts_end()) - 4),
|
||||
static_call_Relocation::spec(),
|
||||
RELOC_DISP32);
|
||||
}
|
||||
if (_method) {
|
||||
int method_index = resolved_method_index(cbuf);
|
||||
RelocationHolder rspec = _optimized_virtual ? opt_virtual_call_Relocation::spec(method_index)
|
||||
: static_call_Relocation::spec(method_index);
|
||||
emit_d32_reloc(cbuf, (int) ($meth$$method - ((intptr_t) cbuf.insts_end()) - 4),
|
||||
rspec, RELOC_DISP32);
|
||||
// Emit stubs for static call.
|
||||
address mark = cbuf.insts_mark();
|
||||
address stub = CompiledStaticCall::emit_to_interp_stub(cbuf, mark);
|
||||
@ -2148,7 +2141,7 @@ encode %{
|
||||
|
||||
enc_class Java_Dynamic_Call(method meth) %{
|
||||
MacroAssembler _masm(&cbuf);
|
||||
__ ic_call((address)$meth$$method);
|
||||
__ ic_call((address)$meth$$method, resolved_method_index(cbuf));
|
||||
%}
|
||||
|
||||
enc_class Java_Compiled_Call(method meth)
|
||||
|
@ -305,6 +305,31 @@ address CodeSection::target(Label& L, address branch_pc) {
|
||||
}
|
||||
}
|
||||
|
||||
void CodeSection::relocate(address at, relocInfo::relocType rtype, int format, jint method_index) {
|
||||
RelocationHolder rh;
|
||||
switch (rtype) {
|
||||
case relocInfo::none: return;
|
||||
case relocInfo::opt_virtual_call_type: {
|
||||
rh = opt_virtual_call_Relocation::spec(method_index);
|
||||
break;
|
||||
}
|
||||
case relocInfo::static_call_type: {
|
||||
rh = static_call_Relocation::spec(method_index);
|
||||
break;
|
||||
}
|
||||
case relocInfo::virtual_call_type: {
|
||||
assert(method_index == 0, "resolved method overriding is not supported");
|
||||
rh = Relocation::spec_simple(rtype);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
rh = Relocation::spec_simple(rtype);
|
||||
break;
|
||||
}
|
||||
}
|
||||
relocate(at, rh, format);
|
||||
}
|
||||
|
||||
void CodeSection::relocate(address at, RelocationHolder const& spec, int format) {
|
||||
Relocation* reloc = spec.reloc();
|
||||
relocInfo::relocType rtype = (relocInfo::relocType) reloc->type();
|
||||
|
@ -209,10 +209,7 @@ class CodeSection VALUE_OBJ_CLASS_SPEC {
|
||||
|
||||
// Emit a relocation.
|
||||
void relocate(address at, RelocationHolder const& rspec, int format = 0);
|
||||
void relocate(address at, relocInfo::relocType rtype, int format = 0) {
|
||||
if (rtype != relocInfo::none)
|
||||
relocate(at, Relocation::spec_simple(rtype), format);
|
||||
}
|
||||
void relocate(address at, relocInfo::relocType rtype, int format = 0, jint method_index = 0);
|
||||
|
||||
// alignment requirement for starting offset
|
||||
// Requirements are that the instruction area and the
|
||||
|
@ -250,6 +250,12 @@ class ciMethod : public ciMetadata {
|
||||
|
||||
ciField* get_field_at_bci( int bci, bool &will_link);
|
||||
ciMethod* get_method_at_bci(int bci, bool &will_link, ciSignature* *declared_signature);
|
||||
ciMethod* get_method_at_bci(int bci) {
|
||||
bool ignored_will_link;
|
||||
ciSignature* ignored_declared_signature;
|
||||
return get_method_at_bci(bci, ignored_will_link, &ignored_declared_signature);
|
||||
}
|
||||
|
||||
// Given a certain calling environment, find the monomorphic target
|
||||
// for the call. Return NULL if the call is not monomorphic in
|
||||
// its calling environment.
|
||||
|
@ -1054,6 +1054,11 @@
|
||||
do_name( isCompileConstant_name, "isCompileConstant") \
|
||||
do_alias( isCompileConstant_signature, object_boolean_signature) \
|
||||
\
|
||||
do_class(sun_hotspot_WhiteBox, "sun/hotspot/WhiteBox") \
|
||||
do_intrinsic(_deoptimize, sun_hotspot_WhiteBox, deoptimize_name, deoptimize_signature, F_R) \
|
||||
do_name( deoptimize_name, "deoptimize") \
|
||||
do_alias( deoptimize_signature, void_method_signature) \
|
||||
\
|
||||
/* unsafe memory references (there are a lot of them...) */ \
|
||||
do_signature(getObject_signature, "(Ljava/lang/Object;J)Ljava/lang/Object;") \
|
||||
do_signature(putObject_signature, "(Ljava/lang/Object;JLjava/lang/Object;)V") \
|
||||
|
@ -434,7 +434,7 @@ void CompiledIC::set_to_monomorphic(CompiledICInfo& info) {
|
||||
InlineCacheBuffer::create_transition_stub(this, info.cached_metadata(), info.entry());
|
||||
} else {
|
||||
if (is_optimized()) {
|
||||
set_ic_destination(info.entry());
|
||||
set_ic_destination(info.entry());
|
||||
} else {
|
||||
set_ic_destination_and_value(info.entry(), info.cached_metadata());
|
||||
}
|
||||
|
@ -978,19 +978,23 @@ void nmethod::print_nmethod(bool printmethod) {
|
||||
oop_maps()->print();
|
||||
}
|
||||
}
|
||||
if (PrintDebugInfo || CompilerOracle::has_option_string(_method, "PrintDebugInfo")) {
|
||||
if (printmethod || PrintDebugInfo || CompilerOracle::has_option_string(_method, "PrintDebugInfo")) {
|
||||
print_scopes();
|
||||
}
|
||||
if (PrintRelocations || CompilerOracle::has_option_string(_method, "PrintRelocations")) {
|
||||
if (printmethod || PrintRelocations || CompilerOracle::has_option_string(_method, "PrintRelocations")) {
|
||||
print_relocations();
|
||||
}
|
||||
if (PrintDependencies || CompilerOracle::has_option_string(_method, "PrintDependencies")) {
|
||||
if (printmethod || PrintDependencies || CompilerOracle::has_option_string(_method, "PrintDependencies")) {
|
||||
print_dependencies();
|
||||
}
|
||||
if (PrintExceptionHandlers) {
|
||||
if (printmethod || PrintExceptionHandlers) {
|
||||
print_handler_table();
|
||||
print_nul_chk_table();
|
||||
}
|
||||
if (printmethod) {
|
||||
print_recorded_oops();
|
||||
print_recorded_metadata();
|
||||
}
|
||||
if (xtty != NULL) {
|
||||
xtty->tail("print_nmethod");
|
||||
}
|
||||
@ -3013,6 +3017,26 @@ void nmethod::print_pcs() {
|
||||
}
|
||||
}
|
||||
|
||||
void nmethod::print_recorded_oops() {
|
||||
tty->print_cr("Recorded oops:");
|
||||
for (int i = 0; i < oops_count(); i++) {
|
||||
oop o = oop_at(i);
|
||||
tty->print("#%3d: " INTPTR_FORMAT " ", i, p2i(o));
|
||||
o->print_value();
|
||||
tty->cr();
|
||||
}
|
||||
}
|
||||
|
||||
void nmethod::print_recorded_metadata() {
|
||||
tty->print_cr("Recorded metadata:");
|
||||
for (int i = 0; i < metadata_count(); i++) {
|
||||
Metadata* m = metadata_at(i);
|
||||
tty->print("#%3d: " INTPTR_FORMAT " ", i, p2i(m));
|
||||
m->print_value_on_maybe_null(tty);
|
||||
tty->cr();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // PRODUCT
|
||||
|
||||
const char* nmethod::reloc_string_for(u_char* begin, u_char* end) {
|
||||
@ -3053,9 +3077,39 @@ const char* nmethod::reloc_string_for(u_char* begin, u_char* end) {
|
||||
}
|
||||
return st.as_string();
|
||||
}
|
||||
case relocInfo::virtual_call_type: return "virtual_call";
|
||||
case relocInfo::opt_virtual_call_type: return "optimized virtual_call";
|
||||
case relocInfo::static_call_type: return "static_call";
|
||||
case relocInfo::virtual_call_type: {
|
||||
stringStream st;
|
||||
st.print_raw("virtual_call");
|
||||
virtual_call_Relocation* r = iter.virtual_call_reloc();
|
||||
Method* m = r->method_value();
|
||||
if (m != NULL) {
|
||||
assert(m->is_method(), "");
|
||||
m->print_short_name(&st);
|
||||
}
|
||||
return st.as_string();
|
||||
}
|
||||
case relocInfo::opt_virtual_call_type: {
|
||||
stringStream st;
|
||||
st.print_raw("optimized virtual_call");
|
||||
opt_virtual_call_Relocation* r = iter.opt_virtual_call_reloc();
|
||||
Method* m = r->method_value();
|
||||
if (m != NULL) {
|
||||
assert(m->is_method(), "");
|
||||
m->print_short_name(&st);
|
||||
}
|
||||
return st.as_string();
|
||||
}
|
||||
case relocInfo::static_call_type: {
|
||||
stringStream st;
|
||||
st.print_raw("static_call");
|
||||
static_call_Relocation* r = iter.static_call_reloc();
|
||||
Method* m = r->method_value();
|
||||
if (m != NULL) {
|
||||
assert(m->is_method(), "");
|
||||
m->print_short_name(&st);
|
||||
}
|
||||
return st.as_string();
|
||||
}
|
||||
case relocInfo::static_stub_type: return "static_stub";
|
||||
case relocInfo::external_word_type: return "external_word";
|
||||
case relocInfo::internal_word_type: return "internal_word";
|
||||
@ -3393,3 +3447,19 @@ char* nmethod::jvmci_installed_code_name(char* buf, size_t buflen) {
|
||||
return buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
Method* nmethod::attached_method(address call_instr) {
|
||||
assert(code_contains(call_instr), "not part of the nmethod");
|
||||
RelocIterator iter(this, call_instr, call_instr + 1);
|
||||
while (iter.next()) {
|
||||
if (iter.addr() == call_instr) {
|
||||
switch(iter.type()) {
|
||||
case relocInfo::static_call_type: return iter.static_call_reloc()->method_value();
|
||||
case relocInfo::opt_virtual_call_type: return iter.opt_virtual_call_reloc()->method_value();
|
||||
case relocInfo::virtual_call_type: return iter.virtual_call_reloc()->method_value();
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL; // not found
|
||||
}
|
||||
|
||||
|
@ -392,6 +392,9 @@ class nmethod : public CodeBlob {
|
||||
int handler_table_size() const { return handler_table_end() - handler_table_begin(); }
|
||||
int nul_chk_table_size() const { return nul_chk_table_end() - nul_chk_table_begin(); }
|
||||
|
||||
int oops_count() const { assert(oops_size() % oopSize == 0, ""); return (oops_size() / oopSize) + 1; }
|
||||
int metadata_count() const { assert(metadata_size() % wordSize == 0, ""); return (metadata_size() / wordSize) + 1; }
|
||||
|
||||
int total_size () const;
|
||||
|
||||
void dec_hotness_counter() { _hotness_counter--; }
|
||||
@ -491,7 +494,7 @@ class nmethod : public CodeBlob {
|
||||
oop oop_at(int index) const { return index == 0 ? (oop) NULL: *oop_addr_at(index); }
|
||||
oop* oop_addr_at(int index) const { // for GC
|
||||
// relocation indexes are biased by 1 (because 0 is reserved)
|
||||
assert(index > 0 && index <= oops_size(), "must be a valid non-zero index");
|
||||
assert(index > 0 && index <= oops_count(), "must be a valid non-zero index");
|
||||
assert(!_oops_are_stale, "oops are stale");
|
||||
return &oops_begin()[index - 1];
|
||||
}
|
||||
@ -501,13 +504,15 @@ class nmethod : public CodeBlob {
|
||||
Metadata* metadata_at(int index) const { return index == 0 ? NULL: *metadata_addr_at(index); }
|
||||
Metadata** metadata_addr_at(int index) const { // for GC
|
||||
// relocation indexes are biased by 1 (because 0 is reserved)
|
||||
assert(index > 0 && index <= metadata_size(), "must be a valid non-zero index");
|
||||
assert(index > 0 && index <= metadata_count(), "must be a valid non-zero index");
|
||||
return &metadata_begin()[index - 1];
|
||||
}
|
||||
|
||||
void copy_values(GrowableArray<jobject>* oops);
|
||||
void copy_values(GrowableArray<Metadata*>* metadata);
|
||||
|
||||
Method* attached_method(address call_pc);
|
||||
|
||||
// Relocation support
|
||||
private:
|
||||
void fix_oop_relocations(address begin, address end, bool initialize_immediates);
|
||||
@ -696,6 +701,8 @@ public:
|
||||
void print_calls(outputStream* st) PRODUCT_RETURN;
|
||||
void print_handler_table() PRODUCT_RETURN;
|
||||
void print_nul_chk_table() PRODUCT_RETURN;
|
||||
void print_recorded_oops() PRODUCT_RETURN;
|
||||
void print_recorded_metadata() PRODUCT_RETURN;
|
||||
void print_nmethod(bool print_code);
|
||||
|
||||
// need to re-define this from CodeBlob else the overload hides it
|
||||
|
@ -581,13 +581,14 @@ void virtual_call_Relocation::pack_data_to(CodeSection* dest) {
|
||||
|
||||
normalize_address(_cached_value, dest);
|
||||
jint x0 = scaled_offset_null_special(_cached_value, point);
|
||||
p = pack_1_int_to(p, x0);
|
||||
p = pack_2_ints_to(p, x0, _method_index);
|
||||
dest->set_locs_end((relocInfo*) p);
|
||||
}
|
||||
|
||||
|
||||
void virtual_call_Relocation::unpack_data() {
|
||||
jint x0 = unpack_1_int();
|
||||
jint x0 = 0;
|
||||
unpack_2_ints(x0, _method_index);
|
||||
address point = addr();
|
||||
_cached_value = x0==0? NULL: address_from_scaled_offset(x0, point);
|
||||
}
|
||||
@ -793,6 +794,12 @@ address virtual_call_Relocation::cached_value() {
|
||||
return _cached_value;
|
||||
}
|
||||
|
||||
Method* virtual_call_Relocation::method_value() {
|
||||
Metadata* m = code()->metadata_at(_method_index);
|
||||
assert(m != NULL || _method_index == 0, "should be non-null for non-zero index");
|
||||
assert(m == NULL || m->is_method(), "not a method");
|
||||
return (Method*)m;
|
||||
}
|
||||
|
||||
void virtual_call_Relocation::clear_inline_cache() {
|
||||
// No stubs for ICs
|
||||
@ -803,6 +810,23 @@ void virtual_call_Relocation::clear_inline_cache() {
|
||||
}
|
||||
|
||||
|
||||
void opt_virtual_call_Relocation::pack_data_to(CodeSection* dest) {
|
||||
short* p = (short*) dest->locs_end();
|
||||
p = pack_1_int_to(p, _method_index);
|
||||
dest->set_locs_end((relocInfo*) p);
|
||||
}
|
||||
|
||||
void opt_virtual_call_Relocation::unpack_data() {
|
||||
_method_index = unpack_1_int();
|
||||
}
|
||||
|
||||
Method* opt_virtual_call_Relocation::method_value() {
|
||||
Metadata* m = code()->metadata_at(_method_index);
|
||||
assert(m != NULL || _method_index == 0, "should be non-null for non-zero index");
|
||||
assert(m == NULL || m->is_method(), "not a method");
|
||||
return (Method*)m;
|
||||
}
|
||||
|
||||
void opt_virtual_call_Relocation::clear_inline_cache() {
|
||||
// No stubs for ICs
|
||||
// Clean IC
|
||||
@ -827,6 +851,22 @@ address opt_virtual_call_Relocation::static_stub() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Method* static_call_Relocation::method_value() {
|
||||
Metadata* m = code()->metadata_at(_method_index);
|
||||
assert(m != NULL || _method_index == 0, "should be non-null for non-zero index");
|
||||
assert(m == NULL || m->is_method(), "not a method");
|
||||
return (Method*)m;
|
||||
}
|
||||
|
||||
void static_call_Relocation::pack_data_to(CodeSection* dest) {
|
||||
short* p = (short*) dest->locs_end();
|
||||
p = pack_1_int_to(p, _method_index);
|
||||
dest->set_locs_end((relocInfo*) p);
|
||||
}
|
||||
|
||||
void static_call_Relocation::unpack_data() {
|
||||
_method_index = unpack_1_int();
|
||||
}
|
||||
|
||||
void static_call_Relocation::clear_inline_cache() {
|
||||
// Safe call site info
|
||||
@ -1014,6 +1054,12 @@ void RelocIterator::print_current() {
|
||||
break;
|
||||
}
|
||||
case relocInfo::static_call_type:
|
||||
{
|
||||
static_call_Relocation* r = (static_call_Relocation*) reloc();
|
||||
tty->print(" | [destination=" INTPTR_FORMAT " metadata=" INTPTR_FORMAT "]",
|
||||
p2i(r->destination()), p2i(r->method_value()));
|
||||
break;
|
||||
}
|
||||
case relocInfo::runtime_call_type:
|
||||
{
|
||||
CallRelocation* r = (CallRelocation*) reloc();
|
||||
@ -1023,8 +1069,8 @@ void RelocIterator::print_current() {
|
||||
case relocInfo::virtual_call_type:
|
||||
{
|
||||
virtual_call_Relocation* r = (virtual_call_Relocation*) reloc();
|
||||
tty->print(" | [destination=" INTPTR_FORMAT " cached_value=" INTPTR_FORMAT "]",
|
||||
p2i(r->destination()), p2i(r->cached_value()));
|
||||
tty->print(" | [destination=" INTPTR_FORMAT " cached_value=" INTPTR_FORMAT " metadata=" INTPTR_FORMAT "]",
|
||||
p2i(r->destination()), p2i(r->cached_value()), p2i(r->method_value()));
|
||||
break;
|
||||
}
|
||||
case relocInfo::static_stub_type:
|
||||
@ -1039,6 +1085,13 @@ void RelocIterator::print_current() {
|
||||
tty->print(" | [trampoline owner=" INTPTR_FORMAT "]", p2i(r->owner()));
|
||||
break;
|
||||
}
|
||||
case relocInfo::opt_virtual_call_type:
|
||||
{
|
||||
opt_virtual_call_Relocation* r = (opt_virtual_call_Relocation*) reloc();
|
||||
tty->print(" | [destination=" INTPTR_FORMAT " metadata=" INTPTR_FORMAT "]",
|
||||
p2i(r->destination()), p2i(r->method_value()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
tty->cr();
|
||||
}
|
||||
|
@ -1044,27 +1044,31 @@ class virtual_call_Relocation : public CallRelocation {
|
||||
// "cached_value" points to the first associated set-oop.
|
||||
// The oop_limit helps find the last associated set-oop.
|
||||
// (See comments at the top of this file.)
|
||||
static RelocationHolder spec(address cached_value) {
|
||||
static RelocationHolder spec(address cached_value, jint method_index = 0) {
|
||||
RelocationHolder rh = newHolder();
|
||||
new(rh) virtual_call_Relocation(cached_value);
|
||||
new(rh) virtual_call_Relocation(cached_value, method_index);
|
||||
return rh;
|
||||
}
|
||||
|
||||
virtual_call_Relocation(address cached_value) {
|
||||
private:
|
||||
address _cached_value; // location of set-value instruction
|
||||
jint _method_index; // resolved method for a Java call
|
||||
|
||||
virtual_call_Relocation(address cached_value, int method_index) {
|
||||
_cached_value = cached_value;
|
||||
_method_index = method_index;
|
||||
assert(cached_value != NULL, "first oop address must be specified");
|
||||
}
|
||||
|
||||
private:
|
||||
address _cached_value; // location of set-value instruction
|
||||
|
||||
friend class RelocIterator;
|
||||
virtual_call_Relocation() { }
|
||||
|
||||
|
||||
public:
|
||||
address cached_value();
|
||||
|
||||
int method_index() { return _method_index; }
|
||||
Method* method_value();
|
||||
|
||||
// data is packed as scaled offsets in "2_ints" format: [f l] or [Ff Ll]
|
||||
// oop_limit is set to 0 if the limit falls somewhere within the call.
|
||||
// When unpacking, a zero oop_limit is taken to refer to the end of the call.
|
||||
@ -1080,17 +1084,29 @@ class opt_virtual_call_Relocation : public CallRelocation {
|
||||
relocInfo::relocType type() { return relocInfo::opt_virtual_call_type; }
|
||||
|
||||
public:
|
||||
static RelocationHolder spec() {
|
||||
static RelocationHolder spec(int method_index = 0) {
|
||||
RelocationHolder rh = newHolder();
|
||||
new(rh) opt_virtual_call_Relocation();
|
||||
new(rh) opt_virtual_call_Relocation(method_index);
|
||||
return rh;
|
||||
}
|
||||
|
||||
private:
|
||||
jint _method_index; // resolved method for a Java call
|
||||
|
||||
opt_virtual_call_Relocation(int method_index) {
|
||||
_method_index = method_index;
|
||||
}
|
||||
|
||||
friend class RelocIterator;
|
||||
opt_virtual_call_Relocation() { }
|
||||
opt_virtual_call_Relocation() {}
|
||||
|
||||
public:
|
||||
int method_index() { return _method_index; }
|
||||
Method* method_value();
|
||||
|
||||
void pack_data_to(CodeSection* dest);
|
||||
void unpack_data();
|
||||
|
||||
void clear_inline_cache();
|
||||
|
||||
// find the matching static_stub
|
||||
@ -1102,17 +1118,29 @@ class static_call_Relocation : public CallRelocation {
|
||||
relocInfo::relocType type() { return relocInfo::static_call_type; }
|
||||
|
||||
public:
|
||||
static RelocationHolder spec() {
|
||||
static RelocationHolder spec(int method_index = 0) {
|
||||
RelocationHolder rh = newHolder();
|
||||
new(rh) static_call_Relocation();
|
||||
new(rh) static_call_Relocation(method_index);
|
||||
return rh;
|
||||
}
|
||||
|
||||
private:
|
||||
jint _method_index; // resolved method for a Java call
|
||||
|
||||
static_call_Relocation(int method_index) {
|
||||
_method_index = method_index;
|
||||
}
|
||||
|
||||
friend class RelocIterator;
|
||||
static_call_Relocation() { }
|
||||
static_call_Relocation() {}
|
||||
|
||||
public:
|
||||
int method_index() { return _method_index; }
|
||||
Method* method_value();
|
||||
|
||||
void pack_data_to(CodeSection* dest);
|
||||
void unpack_data();
|
||||
|
||||
void clear_inline_cache();
|
||||
|
||||
// find the matching static_stub
|
||||
|
@ -1456,6 +1456,33 @@ void LinkResolver::resolve_invoke(CallInfo& result, Handle recv, const constantP
|
||||
return;
|
||||
}
|
||||
|
||||
void LinkResolver::resolve_invoke(CallInfo& result, Handle& recv,
|
||||
const methodHandle& attached_method,
|
||||
Bytecodes::Code byte, TRAPS) {
|
||||
KlassHandle defc = attached_method->method_holder();
|
||||
Symbol* name = attached_method->name();
|
||||
Symbol* type = attached_method->signature();
|
||||
LinkInfo link_info(defc, name, type, KlassHandle(), /*check_access=*/false);
|
||||
switch(byte) {
|
||||
case Bytecodes::_invokevirtual:
|
||||
resolve_virtual_call(result, recv, recv->klass(), link_info,
|
||||
/*check_null_and_abstract=*/true, CHECK);
|
||||
break;
|
||||
case Bytecodes::_invokeinterface:
|
||||
resolve_interface_call(result, recv, recv->klass(), link_info,
|
||||
/*check_null_and_abstract=*/true, CHECK);
|
||||
break;
|
||||
case Bytecodes::_invokestatic:
|
||||
resolve_static_call(result, link_info, /*initialize_class=*/false, CHECK);
|
||||
break;
|
||||
case Bytecodes::_invokespecial:
|
||||
resolve_special_call(result, link_info, CHECK);
|
||||
break;
|
||||
default:
|
||||
fatal("bad call: %s", Bytecodes::name(byte));
|
||||
}
|
||||
}
|
||||
|
||||
void LinkResolver::resolve_invokestatic(CallInfo& result, const constantPoolHandle& pool, int index, TRAPS) {
|
||||
LinkInfo link_info(pool, index, CHECK);
|
||||
resolve_static_call(result, link_info, /*initialize_class*/true, CHECK);
|
||||
|
@ -295,6 +295,12 @@ class LinkResolver: AllStatic {
|
||||
static void resolve_invoke(CallInfo& result, Handle recv,
|
||||
const constantPoolHandle& pool, int index,
|
||||
Bytecodes::Code byte, TRAPS);
|
||||
|
||||
// runtime resolving from attached method
|
||||
static void resolve_invoke(CallInfo& result, Handle& recv,
|
||||
const methodHandle& attached_method,
|
||||
Bytecodes::Code byte, TRAPS);
|
||||
|
||||
private:
|
||||
static void trace_method_resolution(const char* prefix, KlassHandle klass,
|
||||
KlassHandle resolved_klass,
|
||||
|
@ -46,6 +46,11 @@ const TypeFunc* CallGenerator::tf() const {
|
||||
return TypeFunc::make(method());
|
||||
}
|
||||
|
||||
bool CallGenerator::is_inlined_mh_linker(JVMState* jvms, ciMethod* callee) {
|
||||
ciMethod* symbolic_info = jvms->method()->get_method_at_bci(jvms->bci());
|
||||
return symbolic_info->is_method_handle_intrinsic() && !callee->is_method_handle_intrinsic();
|
||||
}
|
||||
|
||||
//-----------------------------ParseGenerator---------------------------------
|
||||
// Internal class which handles all direct bytecode traversal.
|
||||
class ParseGenerator : public InlineCallGenerator {
|
||||
@ -137,6 +142,13 @@ JVMState* DirectCallGenerator::generate(JVMState* jvms) {
|
||||
}
|
||||
|
||||
CallStaticJavaNode *call = new CallStaticJavaNode(kit.C, tf(), target, method(), kit.bci());
|
||||
if (is_inlined_mh_linker(jvms, method())) {
|
||||
// To be able to issue a direct call and skip a call to MH.linkTo*/invokeBasic adapter,
|
||||
// additional information about the method being invoked should be attached
|
||||
// to the call site to make resolution logic work
|
||||
// (see SharedRuntime::resolve_static_call_C).
|
||||
call->set_override_symbolic_info(true);
|
||||
}
|
||||
_call_node = call; // Save the call node in case we need it later
|
||||
if (!is_static) {
|
||||
// Make an explicit receiver null_check as part of this call.
|
||||
@ -192,7 +204,10 @@ JVMState* VirtualCallGenerator::generate(JVMState* jvms) {
|
||||
// the call instruction will have a seemingly deficient out-count.
|
||||
// (The bailout says something misleading about an "infinite loop".)
|
||||
if (kit.gvn().type(receiver)->higher_equal(TypePtr::NULL_PTR)) {
|
||||
kit.inc_sp(method()->arg_size()); // restore arguments
|
||||
assert(Bytecodes::is_invoke(kit.java_bc()), "%d: %s", kit.java_bc(), Bytecodes::name(kit.java_bc()));
|
||||
ciMethod* declared_method = kit.method()->get_method_at_bci(kit.bci());
|
||||
int arg_size = declared_method->signature()->arg_size_for_bc(kit.java_bc());
|
||||
kit.inc_sp(arg_size); // restore arguments
|
||||
kit.uncommon_trap(Deoptimization::Reason_null_check,
|
||||
Deoptimization::Action_none,
|
||||
NULL, "null receiver");
|
||||
@ -226,6 +241,13 @@ JVMState* VirtualCallGenerator::generate(JVMState* jvms) {
|
||||
address target = SharedRuntime::get_resolve_virtual_call_stub();
|
||||
// Normal inline cache used for call
|
||||
CallDynamicJavaNode *call = new CallDynamicJavaNode(tf(), target, method(), _vtable_index, kit.bci());
|
||||
if (is_inlined_mh_linker(jvms, method())) {
|
||||
// To be able to issue a direct call (optimized virtual or virtual)
|
||||
// and skip a call to MH.linkTo*/invokeBasic adapter, additional information
|
||||
// about the method being invoked should be attached to the call site to
|
||||
// make resolution logic work (see SharedRuntime::resolve_{virtual,opt_virtual}_call_C).
|
||||
call->set_override_symbolic_info(true);
|
||||
}
|
||||
kit.set_arguments_for_java_call(call);
|
||||
kit.set_edges_for_java_call(call);
|
||||
Node* ret = kit.set_results_for_java_call(call);
|
||||
@ -463,8 +485,8 @@ bool LateInlineMHCallGenerator::do_late_inline_check(JVMState* jvms) {
|
||||
_attempt++;
|
||||
}
|
||||
|
||||
if (cg != NULL) {
|
||||
assert(!cg->is_late_inline() && cg->is_inline(), "we're doing late inlining");
|
||||
if (cg != NULL && cg->is_inline()) {
|
||||
assert(!cg->is_late_inline(), "we're doing late inlining");
|
||||
_inline_cg = cg;
|
||||
Compile::current()->dec_number_of_mh_late_inlines();
|
||||
return true;
|
||||
@ -807,8 +829,7 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod*
|
||||
const int vtable_index = Method::invalid_vtable_index;
|
||||
CallGenerator* cg = C->call_generator(target, vtable_index, false, jvms, true, PROB_ALWAYS, NULL, true, true);
|
||||
assert(cg == NULL || !cg->is_late_inline() || cg->is_mh_late_inline(), "no late inline here");
|
||||
if (cg != NULL && cg->is_inline())
|
||||
return cg;
|
||||
return cg;
|
||||
} else {
|
||||
const char* msg = "receiver not constant";
|
||||
if (PrintInlining) C->print_inlining(callee, jvms->depth() - 1, jvms->bci(), msg);
|
||||
@ -829,7 +850,7 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod*
|
||||
const TypeOopPtr* oop_ptr = member_name->bottom_type()->is_oopptr();
|
||||
ciMethod* target = oop_ptr->const_oop()->as_member_name()->get_vmtarget();
|
||||
|
||||
// In lamda forms we erase signature types to avoid resolving issues
|
||||
// In lambda forms we erase signature types to avoid resolving issues
|
||||
// involving class loaders. When we optimize a method handle invoke
|
||||
// to a direct call we must cast the receiver and arguments to its
|
||||
// actual types.
|
||||
@ -882,10 +903,9 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod*
|
||||
// provide us with a type
|
||||
speculative_receiver_type = (receiver_type != NULL) ? receiver_type->speculative_type() : NULL;
|
||||
}
|
||||
CallGenerator* cg = C->call_generator(target, vtable_index, call_does_dispatch, jvms, true, PROB_ALWAYS, speculative_receiver_type, true, true);
|
||||
CallGenerator* cg = C->call_generator(target, vtable_index, call_does_dispatch, jvms, /*allow_inline=*/true, PROB_ALWAYS, speculative_receiver_type, true, true);
|
||||
assert(cg == NULL || !cg->is_late_inline() || cg->is_mh_late_inline(), "no late inline here");
|
||||
if (cg != NULL && cg->is_inline())
|
||||
return cg;
|
||||
return cg;
|
||||
} else {
|
||||
const char* msg = "member_name not constant";
|
||||
if (PrintInlining) C->print_inlining(callee, jvms->depth() - 1, jvms->bci(), msg);
|
||||
|
@ -49,7 +49,7 @@ class CallGenerator : public ResourceObj {
|
||||
|
||||
public:
|
||||
// Accessors
|
||||
ciMethod* method() const { return _method; }
|
||||
ciMethod* method() const { return _method; }
|
||||
|
||||
// is_inline: At least some code implementing the method is copied here.
|
||||
virtual bool is_inline() const { return false; }
|
||||
@ -123,7 +123,6 @@ class CallGenerator : public ResourceObj {
|
||||
// How to generate vanilla out-of-line call sites:
|
||||
static CallGenerator* for_direct_call(ciMethod* m, bool separate_io_projs = false); // static, special
|
||||
static CallGenerator* for_virtual_call(ciMethod* m, int vtable_index); // virtual, interface
|
||||
static CallGenerator* for_dynamic_call(ciMethod* m); // invokedynamic
|
||||
|
||||
static CallGenerator* for_method_handle_call( JVMState* jvms, ciMethod* caller, ciMethod* callee, bool delayed_forbidden);
|
||||
static CallGenerator* for_method_handle_inline(JVMState* jvms, ciMethod* caller, ciMethod* callee, bool& input_not_const);
|
||||
@ -170,6 +169,8 @@ class CallGenerator : public ResourceObj {
|
||||
C->print_inlining(callee, inline_level, bci, msg);
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_inlined_mh_linker(JVMState* jvms, ciMethod* m);
|
||||
};
|
||||
|
||||
|
||||
|
@ -959,7 +959,8 @@ bool CallNode::is_call_to_arraycopystub() const {
|
||||
uint CallJavaNode::size_of() const { return sizeof(*this); }
|
||||
uint CallJavaNode::cmp( const Node &n ) const {
|
||||
CallJavaNode &call = (CallJavaNode&)n;
|
||||
return CallNode::cmp(call) && _method == call._method;
|
||||
return CallNode::cmp(call) && _method == call._method &&
|
||||
_override_symbolic_info == call._override_symbolic_info;
|
||||
}
|
||||
#ifndef PRODUCT
|
||||
void CallJavaNode::dump_spec(outputStream *st) const {
|
||||
|
@ -657,25 +657,29 @@ protected:
|
||||
|
||||
bool _optimized_virtual;
|
||||
bool _method_handle_invoke;
|
||||
ciMethod* _method; // Method being direct called
|
||||
bool _override_symbolic_info; // Override symbolic call site info from bytecode
|
||||
ciMethod* _method; // Method being direct called
|
||||
public:
|
||||
const int _bci; // Byte Code Index of call byte code
|
||||
CallJavaNode(const TypeFunc* tf , address addr, ciMethod* method, int bci)
|
||||
: CallNode(tf, addr, TypePtr::BOTTOM),
|
||||
_method(method), _bci(bci),
|
||||
_optimized_virtual(false),
|
||||
_method_handle_invoke(false)
|
||||
_method_handle_invoke(false),
|
||||
_override_symbolic_info(false)
|
||||
{
|
||||
init_class_id(Class_CallJava);
|
||||
}
|
||||
|
||||
virtual int Opcode() const;
|
||||
ciMethod* method() const { return _method; }
|
||||
void set_method(ciMethod *m) { _method = m; }
|
||||
void set_optimized_virtual(bool f) { _optimized_virtual = f; }
|
||||
bool is_optimized_virtual() const { return _optimized_virtual; }
|
||||
void set_method_handle_invoke(bool f) { _method_handle_invoke = f; }
|
||||
bool is_method_handle_invoke() const { return _method_handle_invoke; }
|
||||
ciMethod* method() const { return _method; }
|
||||
void set_method(ciMethod *m) { _method = m; }
|
||||
void set_optimized_virtual(bool f) { _optimized_virtual = f; }
|
||||
bool is_optimized_virtual() const { return _optimized_virtual; }
|
||||
void set_method_handle_invoke(bool f) { _method_handle_invoke = f; }
|
||||
bool is_method_handle_invoke() const { return _method_handle_invoke; }
|
||||
void set_override_symbolic_info(bool f) { _override_symbolic_info = f; }
|
||||
bool override_symbolic_info() const { return _override_symbolic_info; }
|
||||
|
||||
#ifndef PRODUCT
|
||||
virtual void dump_spec(outputStream *st) const;
|
||||
|
@ -393,6 +393,100 @@ bool Parse::can_not_compile_call_site(ciMethod *dest_method, ciInstanceKlass* kl
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
static bool check_type(ciType* t1, ciType* t2) {
|
||||
// Either oop-oop or prim-prim pair.
|
||||
if (t1->is_primitive_type() && t2->is_primitive_type()) {
|
||||
return t1->size() == t2->size(); // argument sizes should match
|
||||
} else {
|
||||
return !t1->is_primitive_type() && !t2->is_primitive_type(); // oop-oop
|
||||
}
|
||||
}
|
||||
|
||||
static bool check_inlined_mh_linker_info(ciMethod* symbolic_info, ciMethod* resolved_method) {
|
||||
assert(symbolic_info->is_method_handle_intrinsic(), "sanity");
|
||||
assert(!resolved_method->is_method_handle_intrinsic(), "sanity");
|
||||
|
||||
if (!symbolic_info->is_loaded() || !resolved_method->is_loaded()) {
|
||||
return true; // Don't compare unloaded methods.
|
||||
}
|
||||
// Linkers have appendix argument which is not passed to callee.
|
||||
int has_appendix = MethodHandles::has_member_arg(symbolic_info->intrinsic_id()) ? 1 : 0;
|
||||
if (symbolic_info->arg_size() != (resolved_method->arg_size() + has_appendix)) {
|
||||
return false; // Total size of arguments on stack mismatch.
|
||||
}
|
||||
if (!check_type(symbolic_info->return_type(), resolved_method->return_type())) {
|
||||
return false; // Return value size or type mismatch encountered.
|
||||
}
|
||||
|
||||
switch (symbolic_info->intrinsic_id()) {
|
||||
case vmIntrinsics::_linkToVirtual:
|
||||
case vmIntrinsics::_linkToInterface:
|
||||
case vmIntrinsics::_linkToSpecial: {
|
||||
if (resolved_method->is_static()) return false;
|
||||
break;
|
||||
}
|
||||
case vmIntrinsics::_linkToStatic: {
|
||||
if (!resolved_method->is_static()) return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ciSignature* symbolic_sig = symbolic_info->signature();
|
||||
ciSignature* resolved_sig = resolved_method->signature();
|
||||
|
||||
if (symbolic_sig->count() + (symbolic_info->is_static() ? 0 : 1) !=
|
||||
resolved_sig->count() + (resolved_method->is_static() ? 0 : 1) + has_appendix) {
|
||||
return false; // Argument count mismatch
|
||||
}
|
||||
|
||||
int sbase = 0, rbase = 0;
|
||||
int arg_count = MIN2(symbolic_sig->count() - has_appendix, resolved_sig->count());
|
||||
ciType* recv_type = NULL;
|
||||
if (symbolic_info->is_static() && !resolved_method->is_static()) {
|
||||
recv_type = symbolic_sig->type_at(0);
|
||||
sbase = 1;
|
||||
} else if (!symbolic_info->is_static() && resolved_method->is_static()) {
|
||||
recv_type = resolved_sig->type_at(0);
|
||||
rbase = 1;
|
||||
}
|
||||
if (recv_type != NULL && recv_type->is_primitive_type()) {
|
||||
return false; // Receiver should be an oop.
|
||||
}
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
if (!check_type(symbolic_sig->type_at(sbase + i), resolved_sig->type_at(rbase + i))) {
|
||||
return false; // Argument size or type mismatch encountered.
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_call_consistent_with_jvms(JVMState* jvms, CallGenerator* cg) {
|
||||
ciMethod* symbolic_info = jvms->method()->get_method_at_bci(jvms->bci());
|
||||
ciMethod* resolved_method = cg->method();
|
||||
|
||||
if (CallGenerator::is_inlined_mh_linker(jvms, resolved_method)) {
|
||||
return check_inlined_mh_linker_info(symbolic_info, resolved_method);
|
||||
} else {
|
||||
// Method name & descriptor should stay the same.
|
||||
return (symbolic_info->get_Method()->name() == resolved_method->get_Method()->name()) &&
|
||||
(symbolic_info->get_Method()->signature() == resolved_method->get_Method()->signature());
|
||||
}
|
||||
}
|
||||
|
||||
static bool check_call_consistency(JVMState* jvms, CallGenerator* cg) {
|
||||
if (!is_call_consistent_with_jvms(jvms, cg)) {
|
||||
tty->print_cr("JVMS:");
|
||||
jvms->dump();
|
||||
tty->print_cr("Bytecode info:");
|
||||
jvms->method()->get_method_at_bci(jvms->bci())->print(); tty->cr();
|
||||
tty->print_cr("Resolved method:");
|
||||
cg->method()->print(); tty->cr();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif // ASSERT
|
||||
|
||||
//------------------------------do_call----------------------------------------
|
||||
// Handle your basic call. Inline if we can & want to, else just setup call.
|
||||
@ -571,6 +665,8 @@ void Parse::do_call() {
|
||||
set_jvms(new_jvms);
|
||||
}
|
||||
|
||||
assert(check_call_consistency(jvms, cg), "inconsistent info");
|
||||
|
||||
if (!stopped()) {
|
||||
// This was some sort of virtual call, which did a null check for us.
|
||||
// Now we can assert receiver-not-null, on the normal return path.
|
||||
|
@ -315,6 +315,8 @@ class LibraryCallKit : public GraphKit {
|
||||
|
||||
bool inline_profileBoolean();
|
||||
bool inline_isCompileConstant();
|
||||
|
||||
bool inline_deoptimize();
|
||||
};
|
||||
|
||||
//---------------------------make_vm_intrinsic----------------------------
|
||||
@ -750,6 +752,9 @@ bool LibraryCallKit::try_to_inline(int predicate) {
|
||||
case vmIntrinsics::_hasNegatives:
|
||||
return inline_hasNegatives();
|
||||
|
||||
case vmIntrinsics::_deoptimize:
|
||||
return inline_deoptimize();
|
||||
|
||||
default:
|
||||
// If you get here, it may be that someone has added a new intrinsic
|
||||
// to the list in vmSymbols.hpp without implementing it here.
|
||||
@ -6574,3 +6579,12 @@ bool LibraryCallKit::inline_isCompileConstant() {
|
||||
set_result(n->is_Con() ? intcon(1) : intcon(0));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LibraryCallKit::inline_deoptimize() {
|
||||
assert(WhiteBoxAPI, "");
|
||||
PreserveReexecuteState preexecs(this);
|
||||
jvms()->set_should_reexecute(false);
|
||||
uncommon_trap(Deoptimization::Reason_intrinsic,
|
||||
Deoptimization::Action_none);
|
||||
return true;
|
||||
}
|
||||
|
@ -707,7 +707,8 @@ const RegMask &MachCallNode::in_RegMask(uint idx) const {
|
||||
uint MachCallJavaNode::size_of() const { return sizeof(*this); }
|
||||
uint MachCallJavaNode::cmp( const Node &n ) const {
|
||||
MachCallJavaNode &call = (MachCallJavaNode&)n;
|
||||
return MachCallNode::cmp(call) && _method->equals(call._method);
|
||||
return MachCallNode::cmp(call) && _method->equals(call._method) &&
|
||||
_override_symbolic_info == call._override_symbolic_info;
|
||||
}
|
||||
#ifndef PRODUCT
|
||||
void MachCallJavaNode::dump_spec(outputStream *st) const {
|
||||
|
@ -885,16 +885,28 @@ protected:
|
||||
virtual uint cmp( const Node &n ) const;
|
||||
virtual uint size_of() const; // Size is bigger
|
||||
public:
|
||||
ciMethod* _method; // Method being direct called
|
||||
int _bci; // Byte Code index of call byte code
|
||||
bool _optimized_virtual; // Tells if node is a static call or an optimized virtual
|
||||
bool _method_handle_invoke; // Tells if the call has to preserve SP
|
||||
MachCallJavaNode() : MachCallNode() {
|
||||
ciMethod* _method; // Method being direct called
|
||||
bool _override_symbolic_info; // Override symbolic call site info from bytecode
|
||||
int _bci; // Byte Code index of call byte code
|
||||
bool _optimized_virtual; // Tells if node is a static call or an optimized virtual
|
||||
bool _method_handle_invoke; // Tells if the call has to preserve SP
|
||||
MachCallJavaNode() : MachCallNode(), _override_symbolic_info(false) {
|
||||
init_class_id(Class_MachCallJava);
|
||||
}
|
||||
|
||||
virtual const RegMask &in_RegMask(uint) const;
|
||||
|
||||
int resolved_method_index(CodeBuffer &cbuf) const {
|
||||
if (_override_symbolic_info) {
|
||||
// Attach corresponding Method* to the call site, so VM can use it during resolution
|
||||
// instead of querying symbolic info from bytecode.
|
||||
assert(_method != NULL, "method should be set");
|
||||
assert(_method->constant_encoding()->is_method(), "should point to a Method");
|
||||
return cbuf.oop_recorder()->find_index(_method->constant_encoding());
|
||||
}
|
||||
return 0; // Use symbolic info from bytecode (resolved_method == NULL).
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
virtual void dump_spec(outputStream *st) const;
|
||||
#endif
|
||||
|
@ -1201,6 +1201,7 @@ MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) {
|
||||
mcall_java->_optimized_virtual = call_java->is_optimized_virtual();
|
||||
is_method_handle_invoke = call_java->is_method_handle_invoke();
|
||||
mcall_java->_method_handle_invoke = is_method_handle_invoke;
|
||||
mcall_java->_override_symbolic_info = call_java->override_symbolic_info();
|
||||
if (is_method_handle_invoke) {
|
||||
C->set_has_method_handle_invokes(true);
|
||||
}
|
||||
|
@ -358,6 +358,19 @@ Symbol* MethodHandles::signature_polymorphic_intrinsic_name(vmIntrinsics::ID iid
|
||||
return 0;
|
||||
}
|
||||
|
||||
Bytecodes::Code MethodHandles::signature_polymorphic_intrinsic_bytecode(vmIntrinsics::ID id) {
|
||||
switch(id) {
|
||||
case vmIntrinsics::_linkToVirtual: return Bytecodes::_invokevirtual;
|
||||
case vmIntrinsics::_linkToInterface: return Bytecodes::_invokeinterface;
|
||||
case vmIntrinsics::_linkToStatic: return Bytecodes::_invokestatic;
|
||||
case vmIntrinsics::_linkToSpecial: return Bytecodes::_invokespecial;
|
||||
case vmIntrinsics::_invokeBasic: return Bytecodes::_invokehandle;
|
||||
default:
|
||||
fatal("unexpected id: (%d) %s", (uint)id, vmIntrinsics::name_at(id));
|
||||
return Bytecodes::_illegal;
|
||||
}
|
||||
}
|
||||
|
||||
int MethodHandles::signature_polymorphic_intrinsic_ref_kind(vmIntrinsics::ID iid) {
|
||||
switch (iid) {
|
||||
case vmIntrinsics::_invokeBasic: return 0;
|
||||
|
@ -91,6 +91,10 @@ class MethodHandles: AllStatic {
|
||||
iid <= vmIntrinsics::LAST_MH_SIG_POLY);
|
||||
}
|
||||
|
||||
static bool is_signature_polymorphic_method(Method* m) {
|
||||
return is_signature_polymorphic(m->intrinsic_id());
|
||||
}
|
||||
|
||||
static bool is_signature_polymorphic_intrinsic(vmIntrinsics::ID iid) {
|
||||
assert(is_signature_polymorphic(iid), "");
|
||||
// Most sig-poly methods are intrinsics which do not require an
|
||||
@ -131,6 +135,8 @@ class MethodHandles: AllStatic {
|
||||
return signature_polymorphic_name_id(klass, name) != vmIntrinsics::_none;
|
||||
}
|
||||
|
||||
static Bytecodes::Code signature_polymorphic_intrinsic_bytecode(vmIntrinsics::ID id);
|
||||
|
||||
static int get_named_constant(int which, Handle name_box, TRAPS);
|
||||
|
||||
public:
|
||||
|
@ -1290,6 +1290,11 @@ WB_ENTRY(jlong, WB_GetConstantPool(JNIEnv* env, jobject wb, jclass klass))
|
||||
return (jlong) ikh->constants();
|
||||
WB_END
|
||||
|
||||
WB_ENTRY(void, WB_ClearInlineCaches(JNIEnv* env, jobject wb))
|
||||
VM_ClearICs clear_ics;
|
||||
VMThread::execute(&clear_ics);
|
||||
WB_END
|
||||
|
||||
template <typename T>
|
||||
static bool GetMethodOption(JavaThread* thread, JNIEnv* env, jobject method, jstring name, T* value) {
|
||||
assert(value != NULL, "sanity");
|
||||
@ -1615,6 +1620,7 @@ static JNINativeMethod methods[] = {
|
||||
(void*)&WB_GetMethodStringOption},
|
||||
{CC"isShared", CC"(Ljava/lang/Object;)Z", (void*)&WB_IsShared },
|
||||
{CC"areSharedStringsIgnored", CC"()Z", (void*)&WB_AreSharedStringsIgnored },
|
||||
{CC"clearInlineCaches", CC"()V", (void*)&WB_ClearInlineCaches },
|
||||
};
|
||||
|
||||
#undef CC
|
||||
|
@ -1070,6 +1070,21 @@ Handle SharedRuntime::find_callee_info(JavaThread* thread, Bytecodes::Code& bc,
|
||||
return find_callee_info_helper(thread, vfst, bc, callinfo, THREAD);
|
||||
}
|
||||
|
||||
methodHandle SharedRuntime::extract_attached_method(vframeStream& vfst) {
|
||||
nmethod* caller_nm = vfst.nm();
|
||||
|
||||
nmethodLocker caller_lock(caller_nm);
|
||||
|
||||
address pc = vfst.frame_pc();
|
||||
{ // Get call instruction under lock because another thread may be busy patching it.
|
||||
MutexLockerEx ml_patch(Patching_lock, Mutex::_no_safepoint_check_flag);
|
||||
if (NativeCall::is_call_before(pc)) {
|
||||
NativeCall* ncall = nativeCall_before(pc);
|
||||
return caller_nm->attached_method(ncall->instruction_address());
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Finds receiver, CallInfo (i.e. receiver method), and calling bytecode
|
||||
// for a call current in progress, i.e., arguments has been pushed on stack
|
||||
@ -1087,15 +1102,37 @@ Handle SharedRuntime::find_callee_info_helper(JavaThread* thread,
|
||||
methodHandle caller(THREAD, vfst.method());
|
||||
int bci = vfst.bci();
|
||||
|
||||
// Find bytecode
|
||||
Bytecode_invoke bytecode(caller, bci);
|
||||
bc = bytecode.invoke_code();
|
||||
int bytecode_index = bytecode.index();
|
||||
|
||||
methodHandle attached_method = extract_attached_method(vfst);
|
||||
if (attached_method.not_null()) {
|
||||
methodHandle callee = bytecode.static_target(CHECK_NH);
|
||||
vmIntrinsics::ID id = callee->intrinsic_id();
|
||||
// When VM replaces MH.invokeBasic/linkTo* call with a direct/virtual call,
|
||||
// it attaches statically resolved method to the call site.
|
||||
if (MethodHandles::is_signature_polymorphic(id) &&
|
||||
MethodHandles::is_signature_polymorphic_intrinsic(id)) {
|
||||
bc = MethodHandles::signature_polymorphic_intrinsic_bytecode(id);
|
||||
|
||||
// Need to adjust invokehandle since inlining through signature-polymorphic
|
||||
// method happened.
|
||||
if (bc == Bytecodes::_invokehandle &&
|
||||
!MethodHandles::is_signature_polymorphic_method(attached_method())) {
|
||||
bc = attached_method->is_static() ? Bytecodes::_invokestatic
|
||||
: Bytecodes::_invokevirtual;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bc = bytecode.invoke_code();
|
||||
}
|
||||
|
||||
bool has_receiver = bc != Bytecodes::_invokestatic &&
|
||||
bc != Bytecodes::_invokedynamic &&
|
||||
bc != Bytecodes::_invokehandle;
|
||||
|
||||
// Find receiver for non-static call
|
||||
if (bc != Bytecodes::_invokestatic &&
|
||||
bc != Bytecodes::_invokedynamic &&
|
||||
bc != Bytecodes::_invokehandle) {
|
||||
if (has_receiver) {
|
||||
// This register map must be update since we need to find the receiver for
|
||||
// compiled frames. The receiver might be in a register.
|
||||
RegisterMap reg_map2(thread);
|
||||
@ -1103,10 +1140,13 @@ Handle SharedRuntime::find_callee_info_helper(JavaThread* thread,
|
||||
// Caller-frame is a compiled frame
|
||||
frame callerFrame = stubFrame.sender(®_map2);
|
||||
|
||||
methodHandle callee = bytecode.static_target(CHECK_(nullHandle));
|
||||
if (callee.is_null()) {
|
||||
THROW_(vmSymbols::java_lang_NoSuchMethodException(), nullHandle);
|
||||
if (attached_method.is_null()) {
|
||||
methodHandle callee = bytecode.static_target(CHECK_NH);
|
||||
if (callee.is_null()) {
|
||||
THROW_(vmSymbols::java_lang_NoSuchMethodException(), nullHandle);
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve from a compiled argument list
|
||||
receiver = Handle(THREAD, callerFrame.retrieve_receiver(®_map2));
|
||||
|
||||
@ -1115,26 +1155,35 @@ Handle SharedRuntime::find_callee_info_helper(JavaThread* thread,
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve method. This is parameterized by bytecode.
|
||||
constantPoolHandle constants(THREAD, caller->constants());
|
||||
assert(receiver.is_null() || receiver->is_oop(), "wrong receiver");
|
||||
LinkResolver::resolve_invoke(callinfo, receiver, constants, bytecode_index, bc, CHECK_(nullHandle));
|
||||
|
||||
// Resolve method
|
||||
if (attached_method.not_null()) {
|
||||
// Parameterized by attached method.
|
||||
LinkResolver::resolve_invoke(callinfo, receiver, attached_method, bc, CHECK_NH);
|
||||
} else {
|
||||
// Parameterized by bytecode.
|
||||
constantPoolHandle constants(THREAD, caller->constants());
|
||||
LinkResolver::resolve_invoke(callinfo, receiver, constants, bytecode_index, bc, CHECK_NH);
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
// Check that the receiver klass is of the right subtype and that it is initialized for virtual calls
|
||||
if (bc != Bytecodes::_invokestatic && bc != Bytecodes::_invokedynamic && bc != Bytecodes::_invokehandle) {
|
||||
if (has_receiver) {
|
||||
assert(receiver.not_null(), "should have thrown exception");
|
||||
KlassHandle receiver_klass(THREAD, receiver->klass());
|
||||
Klass* rk = constants->klass_ref_at(bytecode_index, CHECK_(nullHandle));
|
||||
// klass is already loaded
|
||||
Klass* rk = NULL;
|
||||
if (attached_method.not_null()) {
|
||||
// In case there's resolved method attached, use its holder during the check.
|
||||
rk = attached_method->method_holder();
|
||||
} else {
|
||||
// Klass is already loaded.
|
||||
constantPoolHandle constants(THREAD, caller->constants());
|
||||
rk = constants->klass_ref_at(bytecode_index, CHECK_NH);
|
||||
}
|
||||
KlassHandle static_receiver_klass(THREAD, rk);
|
||||
// Method handle invokes might have been optimized to a direct call
|
||||
// so don't check for the receiver class.
|
||||
// FIXME this weakens the assert too much
|
||||
methodHandle callee = callinfo.selected_method();
|
||||
assert(receiver_klass->is_subtype_of(static_receiver_klass()) ||
|
||||
callee->is_method_handle_intrinsic() ||
|
||||
callee->is_compiled_lambda_form(),
|
||||
assert(receiver_klass->is_subtype_of(static_receiver_klass()),
|
||||
"actual receiver must be subclass of static receiver klass");
|
||||
if (receiver_klass->is_instance_klass()) {
|
||||
if (InstanceKlass::cast(receiver_klass())->is_not_initialized()) {
|
||||
@ -1670,7 +1719,6 @@ methodHandle SharedRuntime::reresolve_call_site(JavaThread *thread, TRAPS) {
|
||||
inline_cache->set_to_clean();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
methodHandle callee_method = find_callee_method(thread, CHECK_(methodHandle()));
|
||||
|
@ -343,6 +343,8 @@ class SharedRuntime: AllStatic {
|
||||
Bytecodes::Code& bc,
|
||||
CallInfo& callinfo, TRAPS);
|
||||
|
||||
static methodHandle extract_attached_method(vframeStream& vfst);
|
||||
|
||||
static address clean_virtual_call_entry();
|
||||
static address clean_opt_virtual_call_entry();
|
||||
static address clean_static_call_entry();
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "oops/oop.hpp"
|
||||
#include "runtime/thread.hpp"
|
||||
#include "utilities/top.hpp"
|
||||
#include "code/codeCache.hpp"
|
||||
|
||||
// The following classes are used for operations
|
||||
// initiated by a Java thread but that must
|
||||
@ -44,6 +45,7 @@
|
||||
template(ThreadDump) \
|
||||
template(PrintThreads) \
|
||||
template(FindDeadlocks) \
|
||||
template(ClearICs) \
|
||||
template(ForceSafepoint) \
|
||||
template(ForceAsyncSafepoint) \
|
||||
template(Deoptimize) \
|
||||
@ -230,6 +232,13 @@ class VM_ThreadStop: public VM_Operation {
|
||||
}
|
||||
};
|
||||
|
||||
class VM_ClearICs: public VM_Operation {
|
||||
public:
|
||||
VM_ClearICs() {}
|
||||
void doit() { CodeCache::clear_inline_caches(); }
|
||||
VMOp_Type type() const { return VMOp_ClearICs; }
|
||||
};
|
||||
|
||||
// dummy vm op, evaluated just to force a safepoint
|
||||
class VM_ForceSafepoint: public VM_Operation {
|
||||
public:
|
||||
|
48
hotspot/test/compiler/jsr292/NonInlinedCall/Agent.java
Normal file
48
hotspot/test/compiler/jsr292/NonInlinedCall/Agent.java
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
import java.io.File;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class Agent {
|
||||
public static void main(String[] args) throws Exception {
|
||||
String jarName = args[0];
|
||||
String className = args[1];
|
||||
String manifestName = "manifest.mf";
|
||||
|
||||
System.out.println("Creating "+manifestName);
|
||||
try (PrintStream out = new PrintStream(new File(manifestName))) {
|
||||
out.println("Premain-Class: " + className);
|
||||
out.println("Can-Redefine-Classes: true");
|
||||
}
|
||||
System.out.println("Building "+jarName);
|
||||
String[] jarArgs = new String[] {"-cfm", jarName, manifestName };
|
||||
|
||||
System.out.println("Running jar " + Arrays.toString(jarArgs));
|
||||
sun.tools.jar.Main jarTool = new sun.tools.jar.Main(System.out, System.err, "jar");
|
||||
if (!jarTool.run(jarArgs)) {
|
||||
throw new Error("jar failed: args=" + Arrays.toString(args));
|
||||
}
|
||||
}
|
||||
}
|
105
hotspot/test/compiler/jsr292/NonInlinedCall/GCTest.java
Normal file
105
hotspot/test/compiler/jsr292/NonInlinedCall/GCTest.java
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8072008
|
||||
* @library /testlibrary /../../test/lib
|
||||
* @build GCTest NonInlinedReinvoker
|
||||
* @run main ClassFileInstaller sun.hotspot.WhiteBox
|
||||
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||
* java.lang.invoke.GCTest
|
||||
* java.lang.invoke.GCTest$T
|
||||
* java.lang.invoke.NonInlinedReinvoker
|
||||
* jdk.test.lib.Asserts
|
||||
* @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions
|
||||
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
|
||||
* -Xbatch -XX:-TieredCompilation -XX:CICompilerCount=1
|
||||
* java.lang.invoke.GCTest
|
||||
*/
|
||||
package java.lang.invoke;
|
||||
|
||||
import sun.hotspot.WhiteBox;
|
||||
|
||||
import java.lang.ref.*;
|
||||
import static jdk.test.lib.Asserts.*;
|
||||
|
||||
public class GCTest {
|
||||
static final MethodHandles.Lookup LOOKUP = MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
|
||||
static class T {
|
||||
static int f1() { return 0; }
|
||||
static int f2() { return 1; }
|
||||
}
|
||||
|
||||
static @Stable MethodHandle mh;
|
||||
static PhantomReference<LambdaForm> lform;
|
||||
|
||||
static final ReferenceQueue<LambdaForm> rq = new ReferenceQueue<>();
|
||||
static final WhiteBox WB = WhiteBox.getWhiteBox();
|
||||
|
||||
@DontInline
|
||||
static int invokeBasic() {
|
||||
try {
|
||||
return (int) mh.invokeBasic();
|
||||
} catch (Throwable e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void test(int expected) {
|
||||
for (int i = 0; i < 20_000; i++) {
|
||||
invokeBasic();
|
||||
}
|
||||
assertEquals(invokeBasic(), expected);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
mh = NonInlinedReinvoker.make(
|
||||
LOOKUP.findStatic(T.class, "f1", MethodType.methodType(int.class)));
|
||||
|
||||
// Monitor LambdaForm GC
|
||||
lform = new PhantomReference<>(mh.form, rq);
|
||||
|
||||
test(0);
|
||||
WB.clearInlineCaches();
|
||||
test(0);
|
||||
|
||||
mh = NonInlinedReinvoker.make(
|
||||
LOOKUP.findStatic(T.class, "f2", MethodType.methodType(int.class)));
|
||||
|
||||
Reference<?> ref = null;
|
||||
while (ref == null) {
|
||||
WB.fullGC();
|
||||
try {
|
||||
ref = rq.remove(1000);
|
||||
} catch (InterruptedException e) { /*ignore*/ }
|
||||
}
|
||||
|
||||
test(1);
|
||||
WB.clearInlineCaches();
|
||||
test(1);
|
||||
|
||||
System.out.println("TEST PASSED");
|
||||
}
|
||||
}
|
218
hotspot/test/compiler/jsr292/NonInlinedCall/InvokeTest.java
Normal file
218
hotspot/test/compiler/jsr292/NonInlinedCall/InvokeTest.java
Normal file
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8072008
|
||||
* @library /testlibrary /../../test/lib
|
||||
* @build InvokeTest NonInlinedReinvoker
|
||||
* @run main ClassFileInstaller sun.hotspot.WhiteBox
|
||||
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||
* java.lang.invoke.InvokeTest
|
||||
* java.lang.invoke.InvokeTest$T
|
||||
* java.lang.invoke.InvokeTest$P1
|
||||
* java.lang.invoke.InvokeTest$P2
|
||||
* java.lang.invoke.InvokeTest$I
|
||||
* java.lang.invoke.NonInlinedReinvoker
|
||||
* jdk.test.lib.Asserts
|
||||
* @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions
|
||||
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
|
||||
* -Xbatch -XX:-TieredCompilation -XX:CICompilerCount=1
|
||||
* java.lang.invoke.InvokeTest
|
||||
*/
|
||||
package java.lang.invoke;
|
||||
|
||||
import sun.hotspot.WhiteBox;
|
||||
import static jdk.test.lib.Asserts.*;
|
||||
|
||||
public class InvokeTest {
|
||||
static MethodHandles.Lookup LOOKUP = MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
|
||||
static final MethodHandle virtualMH; // invokevirtual T.f1
|
||||
static final MethodHandle staticMH; // invokestatic T.f2
|
||||
static final MethodHandle intfMH; // invokeinterface I.f1
|
||||
static final MethodHandle specialMH; // invokespecial T.f4 T
|
||||
static final MethodHandle basicMH;
|
||||
|
||||
static final WhiteBox WB = WhiteBox.getWhiteBox();
|
||||
|
||||
static volatile boolean doDeopt = false;
|
||||
|
||||
static {
|
||||
try {
|
||||
MethodType mtype = MethodType.methodType(Class.class);
|
||||
|
||||
virtualMH = LOOKUP.findVirtual(T.class, "f1", mtype);
|
||||
staticMH = LOOKUP.findStatic (T.class, "f2", mtype);
|
||||
intfMH = LOOKUP.findVirtual(I.class, "f3", mtype);
|
||||
specialMH = LOOKUP.findSpecial(T.class, "f4", mtype, T.class);
|
||||
basicMH = NonInlinedReinvoker.make(staticMH);
|
||||
} catch (Exception e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static class T implements I {
|
||||
@DontInline public Class<?> f1() { if (doDeopt) WB.deoptimize(); return T.class; }
|
||||
@DontInline public static Class<?> f2() { if (doDeopt) WB.deoptimize(); return T.class; }
|
||||
@DontInline private Class<?> f4() { if (doDeopt) WB.deoptimize(); return T.class; }
|
||||
}
|
||||
|
||||
static class P1 extends T {
|
||||
@DontInline public Class<?> f1() { if (doDeopt) WB.deoptimize(); return P1.class; }
|
||||
@DontInline public Class<?> f3() { if (doDeopt) WB.deoptimize(); return P1.class; }
|
||||
}
|
||||
|
||||
static class P2 extends T {
|
||||
@DontInline public Class<?> f1() { if (doDeopt) WB.deoptimize(); return P2.class; }
|
||||
@DontInline public Class<?> f3() { if (doDeopt) WB.deoptimize(); return P2.class; }
|
||||
}
|
||||
|
||||
static interface I {
|
||||
@DontInline default Class<?> f3() { if (doDeopt) WB.deoptimize(); return I.class; }
|
||||
}
|
||||
|
||||
@DontInline
|
||||
static void linkToVirtual(Object obj, Class<?> extecpted) {
|
||||
try {
|
||||
Class<?> cls = (Class<?>)virtualMH.invokeExact((T)obj);
|
||||
assertEquals(cls, obj.getClass());
|
||||
} catch (Throwable e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@DontInline
|
||||
static void linkToInterface(Object obj, Class<?> expected) {
|
||||
try {
|
||||
Class<?> cls = (Class<?>)intfMH.invokeExact((I)obj);
|
||||
assertEquals(cls, expected);
|
||||
} catch (Throwable e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@DontInline
|
||||
static void linkToStatic() {
|
||||
try {
|
||||
Class<?> cls = (Class<?>)staticMH.invokeExact();
|
||||
assertEquals(cls, T.class);
|
||||
} catch (Throwable e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@DontInline
|
||||
static void linkToSpecial(Object obj, Class<?> expected) {
|
||||
try {
|
||||
Class<?> cls = (Class<?>)specialMH.invokeExact((T)obj);
|
||||
assertEquals(cls, expected);
|
||||
} catch (Throwable e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@DontInline
|
||||
static void invokeBasic() {
|
||||
try {
|
||||
Class<?> cls = (Class<?>)basicMH.invokeBasic();
|
||||
assertEquals(cls, T.class);
|
||||
} catch (Throwable e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void run(Runnable r) {
|
||||
for (int i = 0; i < 20_000; i++) {
|
||||
r.run();
|
||||
}
|
||||
|
||||
doDeopt = true;
|
||||
r.run();
|
||||
doDeopt = false;
|
||||
|
||||
WB.clearInlineCaches();
|
||||
|
||||
for (int i = 0; i < 20_000; i++) {
|
||||
r.run();
|
||||
}
|
||||
|
||||
doDeopt = true;
|
||||
r.run();
|
||||
doDeopt = false;
|
||||
}
|
||||
|
||||
static void testVirtual() {
|
||||
System.out.println("linkToVirtual");
|
||||
|
||||
// Monomorphic case (optimized virtual call)
|
||||
run(() -> linkToVirtual(new T(), T.class));
|
||||
|
||||
// Megamorphic case (virtual call)
|
||||
Object[] recv = new Object[] { new T(), new P1(), new P2() };
|
||||
run(() -> {
|
||||
for (Object r : recv) {
|
||||
linkToVirtual(r, r.getClass());
|
||||
}});
|
||||
}
|
||||
|
||||
static void testInterface() {
|
||||
System.out.println("linkToInterface");
|
||||
|
||||
// Monomorphic case (optimized virtual call)
|
||||
run(() -> linkToInterface(new T(), I.class));
|
||||
|
||||
// Megamorphic case (virtual call)
|
||||
Object[][] recv = new Object[][] {{new T(), I.class}, {new P1(), P1.class}, {new P2(), P2.class}};
|
||||
run(() -> {
|
||||
for (Object[] r : recv) {
|
||||
linkToInterface(r[0], (Class<?>)r[1]);
|
||||
}});
|
||||
}
|
||||
|
||||
static void testSpecial() {
|
||||
System.out.println("linkToSpecial");
|
||||
// Monomorphic case (optimized virtual call)
|
||||
run(() -> linkToSpecial(new T(), T.class));
|
||||
}
|
||||
|
||||
static void testStatic() {
|
||||
System.out.println("linkToStatic");
|
||||
// static call
|
||||
run(() -> linkToStatic());
|
||||
}
|
||||
|
||||
static void testBasic() {
|
||||
System.out.println("invokeBasic");
|
||||
// static call
|
||||
run(() -> invokeBasic());
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
testVirtual();
|
||||
testInterface();
|
||||
testSpecial();
|
||||
testStatic();
|
||||
testBasic();
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package java.lang.invoke;
|
||||
|
||||
class NonInlinedReinvoker extends DelegatingMethodHandle {
|
||||
private final MethodHandle target;
|
||||
|
||||
private NonInlinedReinvoker(MethodHandle target, LambdaForm lf) {
|
||||
super(target.type(), lf);
|
||||
this.target = target;
|
||||
}
|
||||
@Override
|
||||
protected MethodHandle getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodHandle asTypeUncached(MethodType newType) {
|
||||
return asTypeCache = target.asType(newType);
|
||||
}
|
||||
|
||||
static MethodHandle make(MethodHandle target) {
|
||||
LambdaForm lform = DelegatingMethodHandle.makeReinvokerForm(
|
||||
target, -1, DelegatingMethodHandle.class, "reinvoker.dontInline",
|
||||
/*forceInline=*/false, DelegatingMethodHandle.NF_getTarget, null);
|
||||
return new NonInlinedReinvoker(target, lform);
|
||||
}
|
||||
}
|
157
hotspot/test/compiler/jsr292/NonInlinedCall/RedefineTest.java
Normal file
157
hotspot/test/compiler/jsr292/NonInlinedCall/RedefineTest.java
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8072008
|
||||
* @library /testlibrary /../../test/lib
|
||||
* @build RedefineTest Agent
|
||||
* @run main ClassFileInstaller sun.hotspot.WhiteBox
|
||||
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||
* java.lang.invoke.RedefineTest
|
||||
* Agent
|
||||
* jdk.test.lib.Asserts
|
||||
* @run main Agent agent.jar java.lang.invoke.RedefineTest
|
||||
* @run main/othervm -Xbootclasspath/a:. -javaagent:agent.jar
|
||||
* -XX:+IgnoreUnrecognizedVMOptions
|
||||
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
|
||||
* -Xbatch -XX:-TieredCompilation -XX:CICompilerCount=1
|
||||
* java.lang.invoke.RedefineTest
|
||||
*/
|
||||
package java.lang.invoke;
|
||||
|
||||
import sun.hotspot.WhiteBox;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
|
||||
import java.lang.instrument.ClassDefinition;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
public class RedefineTest {
|
||||
static final MethodHandles.Lookup LOOKUP = MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||
|
||||
static final String NAME = "java/lang/invoke/RedefineTest$T";
|
||||
|
||||
static Class<?> getClass(int r) {
|
||||
byte[] classFile = getClassFile(r);
|
||||
return UNSAFE.defineClass(NAME, classFile, 0, classFile.length, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a class of the following shape:
|
||||
* static class T {
|
||||
* @DontInline public static int f() { return $r; }
|
||||
* }
|
||||
*/
|
||||
static byte[] getClassFile(int r) {
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
MethodVisitor mv;
|
||||
cw.visit(52, ACC_PUBLIC | ACC_SUPER, NAME, null, "java/lang/Object", null);
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "f", "()I", null, null);
|
||||
mv.visitAnnotation("Ljava/lang/invoke/DontInline;", true);
|
||||
mv.visitCode();
|
||||
mv.visitLdcInsn(r);
|
||||
mv.visitInsn(IRETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
static final MethodHandle mh;
|
||||
static final Class<?> CLS = getClass(0);
|
||||
static {
|
||||
try {
|
||||
mh = LOOKUP.findStatic(CLS, "f", MethodType.methodType(int.class));
|
||||
} catch (Exception e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static final WhiteBox WB = WhiteBox.getWhiteBox();
|
||||
|
||||
@DontInline
|
||||
static int invokeBasic() {
|
||||
try {
|
||||
return (int)mh.invokeExact();
|
||||
} catch (Throwable e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static Instrumentation instr;
|
||||
public static void premain(String args, Instrumentation instr) {
|
||||
RedefineTest.instr = instr;
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
for (int i = 0; i < 20_000; i++) {
|
||||
int r = invokeBasic();
|
||||
if (r != 0) {
|
||||
throw new Error(r + " != 0");
|
||||
}
|
||||
}
|
||||
// WB.ensureCompiled();
|
||||
|
||||
redefine();
|
||||
|
||||
int exp = (instr != null) ? 1 : 0;
|
||||
|
||||
for (int i = 0; i < 20_000; i++) {
|
||||
if (invokeBasic() != exp) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
WB.clearInlineCaches();
|
||||
|
||||
for (int i = 0; i < 20_000; i++) {
|
||||
if (invokeBasic() != exp) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
// WB.ensureCompiled();
|
||||
}
|
||||
|
||||
static void redefine() {
|
||||
if (instr == null) {
|
||||
System.out.println("NOT REDEFINED");
|
||||
return;
|
||||
}
|
||||
ClassDefinition cd = new ClassDefinition(CLS, getClassFile(1));
|
||||
try {
|
||||
instr.redefineClasses(cd);
|
||||
} catch (Exception e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
System.out.println("REDEFINED");
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@
|
||||
* @library /testlibrary
|
||||
* @compile WhiteBox.java
|
||||
* @run main ClassFileInstaller sun.hotspot.WhiteBox
|
||||
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI sun.hotspot.WhiteBox
|
||||
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-CheckIntrinsics sun.hotspot.WhiteBox
|
||||
*/
|
||||
|
||||
package sun.hotspot;
|
||||
|
Loading…
Reference in New Issue
Block a user