# HG changeset patch # User vlivanov # Date 1495813167 -10800 # Node ID 2e867226b9147e92bcfec6581592048e3a6ab1a9 # Parent d44d912ea9bba154f5f4c01bce3cc4308f0c48c9 8174962: Better interface invocations Reviewed-by: jrose, coleenp, ahgross, acorn, iignatyev diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp Fri May 26 18:39:27 2017 +0300 @@ -774,7 +774,7 @@ __ load_klass(rscratch1, receiver); __ ldr(tmp, Address(holder, CompiledICHolder::holder_klass_offset())); __ cmp(rscratch1, tmp); - __ ldr(rmethod, Address(holder, CompiledICHolder::holder_method_offset())); + __ ldr(rmethod, Address(holder, CompiledICHolder::holder_metadata_offset())); __ br(Assembler::EQ, ok); __ far_jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub())); diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/arm/macroAssembler_arm.cpp --- a/src/hotspot/cpu/arm/macroAssembler_arm.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/arm/macroAssembler_arm.cpp Fri May 26 18:39:27 2017 +0300 @@ -2475,49 +2475,65 @@ // On success, the result will be in method_result, and execution falls through. // On failure, execution transfers to the given label. void MacroAssembler::lookup_interface_method(Register Rklass, - Register Rinterf, - Register Rindex, + Register Rintf, + RegisterOrConstant itable_index, Register method_result, - Register temp_reg1, - Register temp_reg2, + Register Rscan, + Register Rtmp, Label& L_no_such_interface) { - assert_different_registers(Rklass, Rinterf, temp_reg1, temp_reg2, Rindex); - - Register Ritable = temp_reg1; + assert_different_registers(Rklass, Rintf, Rscan, Rtmp); + + 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) const int base = in_bytes(Klass::vtable_start_offset()); const int scale = exact_log2(vtableEntry::size_in_bytes()); - ldr_s32(temp_reg2, Address(Rklass, Klass::vtable_length_offset())); // Get length of vtable - add(Ritable, Rklass, base); - add(Ritable, Ritable, AsmOperand(temp_reg2, lsl, scale)); - - Label entry, search; - - b(entry); - - bind(search); - add(Ritable, Ritable, itableOffsetEntry::size() * HeapWordSize); - - 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(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::method_offset_in_bytes() == 0, "adjust the offset in the code below"); - - ldr(method_result, Address::indexed_ptr(temp_reg2, Rindex)); + ldr_s32(Rtmp, Address(Rklass, Klass::vtable_length_offset())); // Get length of vtable + add(Rscan, Rklass, base); + add(Rscan, Rscan, AsmOperand(Rtmp, lsl, scale)); + + // Search through the itable for an interface equal to incoming Rintf + // itable looks like [intface][offset][intface][offset][intface][offset] + + 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 + +#ifdef AARCH64 + 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 + + if (method_result != noreg) { + // Interface found at previous position of Rscan, now load the method + ldr_s32(Rtmp, Address(Rscan, itableOffsetEntry::offset_offset_in_bytes() - entry_size)); + if (itable_index.is_register()) { + add(Rtmp, Rtmp, Rklass); // Add offset to Klass* + 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"); + ldr(method_result, Address::indexed_ptr(Rtmp, itable_index.as_register())); + } 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 diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/arm/macroAssembler_arm.hpp --- a/src/hotspot/cpu/arm/macroAssembler_arm.hpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/arm/macroAssembler_arm.hpp Fri May 26 18:39:27 2017 +0300 @@ -1316,7 +1316,7 @@ void lookup_interface_method(Register recv_klass, Register intf_klass, - Register itable_index, + RegisterOrConstant itable_index, Register method_result, Register temp_reg1, Register temp_reg2, diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/arm/sharedRuntime_arm.cpp --- a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp Fri May 26 18:39:27 2017 +0300 @@ -984,7 +984,7 @@ __ load_klass(receiver_klass, receiver); __ 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); #ifdef AARCH64 diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/arm/templateTable_arm.cpp --- a/src/hotspot/cpu/arm/templateTable_arm.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/arm/templateTable_arm.cpp Fri May 26 18:39:27 2017 +0300 @@ -4198,7 +4198,7 @@ const Register Rflags = 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 // java.lang.Object. See cpCacheOop.cpp for details. @@ -4207,56 +4207,39 @@ Label notMethod; __ tbz(Rflags, ConstantPoolCacheEntry::is_forced_virtual_shift, notMethod); - __ mov(Rmethod, Rindex); invokevirtual_helper(Rmethod, Rrecv, Rflags); __ bind(notMethod); // Get receiver klass into Rklass - also a null check __ 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_virtual_call(R0_tmp, Rklass); - // Compute start of first itableOffsetEntry (which is at the end of the vtable) - const int base = in_bytes(Klass::vtable_start_offset()); - assert(vtableEntry::size() == 1, "adjust the scaling in the code below"); - __ ldr_s32(Rtemp, Address(Rklass, Klass::vtable_length_offset())); // Get length of vtable - __ add(Ritable, Rklass, base); - __ add(Ritable, Ritable, AsmOperand(Rtemp, lsl, LogBytesPerWord)); - - Label entry, search, interface_ok; - - __ b(entry); - - __ bind(search); - __ add(Ritable, Ritable, itableOffsetEntry::size() * HeapWordSize); - - __ 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)); + // Get declaring interface class from method + __ ldr(Rtemp, Address(Rmethod, Method::const_offset())); + __ ldr(Rtemp, Address(Rtemp, ConstMethod::constants_offset())); + __ ldr(Rinterf, Address(Rtemp, ConstantPool::pool_holder_offset_in_bytes())); + + // 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); + + __ lookup_interface_method(// inputs: rec. class, interface + Rklass, Rinterf, Rindex, + // outputs: scan temp. reg1, scan temp. reg2 + Rmethod, Ritable, Rtemp, + no_such_interface); // Rmethod: Method* to call @@ -4278,6 +4261,13 @@ // do the call __ 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) { diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/arm/vtableStubs_arm.cpp --- a/src/hotspot/cpu/arm/vtableStubs_arm.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/arm/vtableStubs_arm.cpp Fri May 26 18:39:27 2017 +0300 @@ -28,6 +28,7 @@ #include "code/vtableStubs.hpp" #include "interp_masm_arm.hpp" #include "memory/resourceArea.hpp" +#include "oops/compiledICHolder.hpp" #include "oops/instanceKlass.hpp" #include "oops/klassVtable.hpp" #include "runtime/sharedRuntime.hpp" @@ -118,67 +119,48 @@ // R0-R3 / R0-R7 registers hold the arguments and cannot be spoiled 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 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) const int scale = exact_log2(vtableEntry::size_in_bytes()); address npe_addr = __ pc(); __ load_klass(Rclass, R0); - __ ldr_s32(Rlength, Address(Rclass, Klass::vtable_length_offset())); - __ add(Rscan, Rclass, in_bytes(Klass::vtable_start_offset())); - __ add(Rscan, Rscan, AsmOperand(Rlength, lsl, scale)); - - // Search through the itable for an interface equal to incoming Ricklass - // itable looks like [intface][offset][intface][offset][intface][offset] - const int entry_size = itableOffsetEntry::size() * HeapWordSize; - assert(itableOffsetEntry::interface_offset_in_bytes() == 0, "not added for convenience"); + Label L_no_such_interface; - Label loop; - __ bind(loop); - __ ldr(tmp, Address(Rscan, entry_size, post_indexed)); -#ifdef AARCH64 - Label found; - __ cmp(tmp, Ricklass); - __ b(found, eq); - __ 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 + // Receiver subtype check against REFC. + __ ldr(Rintf, Address(Ricklass, CompiledICHolder::holder_klass_offset())); + __ lookup_interface_method(// inputs: rec. class, interface, itable index + Rclass, Rintf, noreg, + // outputs: temp reg1, temp reg2 + noreg, Rscan, Rtemp, + L_no_such_interface); - 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)); + // Get Method* and entry point for compiler + __ ldr(Rintf, Address(Ricklass, CompiledICHolder::holder_metadata_offset())); + __ lookup_interface_method(// inputs: rec. class, interface, itable index + Rclass, Rintf, itable_index, + // outputs: temp reg1, temp reg2, temp reg3 + Rmethod, Rscan, Rtemp, + L_no_such_interface); address ame_addr = __ pc(); #ifdef AARCH64 - __ ldr(tmp, Address(Rmethod, Method::from_compiled_offset())); - __ br(tmp); + __ ldr(Rtemp, Address(Rmethod, Method::from_compiled_offset())); + __ br(Rtemp); #else __ ldr(PC, Address(Rmethod, Method::from_compiled_offset())); #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(); if (PrintMiscellaneous && (WizardMode || Verbose)) { @@ -205,7 +187,7 @@ instr_count = NOT_AARCH64(4) AARCH64_ONLY(5); } else { // itable stub size - instr_count = NOT_AARCH64(20) AARCH64_ONLY(20); + instr_count = NOT_AARCH64(31) AARCH64_ONLY(31); } #ifdef AARCH64 diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp Fri May 26 18:39:27 2017 +0300 @@ -1188,7 +1188,7 @@ // Argument is valid and klass is as expected, continue. // 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"); __ ld(code, method_(code)); diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/s390/sharedRuntime_s390.cpp --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp Fri May 26 18:39:27 2017 +0300 @@ -2660,9 +2660,9 @@ Label skip_fixup; { Label ic_miss; - const int klass_offset = oopDesc::klass_offset_in_bytes(); - const int holder_klass_offset = CompiledICHolder::holder_klass_offset(); - const int holder_method_offset = CompiledICHolder::holder_method_offset(); + const int klass_offset = oopDesc::klass_offset_in_bytes(); + const int holder_klass_offset = CompiledICHolder::holder_klass_offset(); + const int holder_metadata_offset = CompiledICHolder::holder_metadata_offset(); // Out-of-line call to ic_miss handler. __ call_ic_miss_handler(ic_miss, 0x11, 0, Z_R1_scratch); @@ -2691,7 +2691,7 @@ // This def MUST MATCH code in gen_c2i_adapter! 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)); __ z_brne(ic_miss); // Cache miss: call runtime to handle this. diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/sparc/macroAssembler_sparc.cpp --- a/src/hotspot/cpu/sparc/macroAssembler_sparc.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/sparc/macroAssembler_sparc.cpp Fri May 26 18:39:27 2017 +0300 @@ -2058,9 +2058,10 @@ Register method_result, Register scan_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(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"); Label L_no_such_interface_restore; @@ -2092,11 +2093,13 @@ add(scan_temp, itb_offset, scan_temp); add(recv_klass, scan_temp, scan_temp); - // Adjust recv_klass by scaled itable_index, so we can free itable_index. - RegisterOrConstant itable_offset = itable_index; - 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); - add(recv_klass, ensure_simm13_or_reg(itable_offset, sethi_temp), recv_klass); + if (return_method) { + // Adjust recv_klass by scaled itable_index, so we can free itable_index. + RegisterOrConstant itable_offset = itable_index; + 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); + add(recv_klass, ensure_simm13_or_reg(itable_offset, sethi_temp), recv_klass); + } // for (scan = klass->itable(); scan->interface() != NULL; scan += scan_step) { // if (scan->interface() == intf) { @@ -2131,12 +2134,14 @@ bind(L_found_method); - // Got a hit. - int ito_offset = itableOffsetEntry::offset_offset_in_bytes(); - // scan_temp[-scan_step] points to the vtable offset we need - ito_offset -= scan_step; - lduw(scan_temp, ito_offset, scan_temp); - ld_ptr(recv_klass, scan_temp, method_result); + if (return_method) { + // Got a hit. + int ito_offset = itableOffsetEntry::offset_offset_in_bytes(); + // scan_temp[-scan_step] points to the vtable offset we need + ito_offset -= scan_step; + lduw(scan_temp, ito_offset, scan_temp); + ld_ptr(recv_klass, scan_temp, method_result); + } if (did_save) { Label L_done; diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/sparc/macroAssembler_sparc.hpp --- a/src/hotspot/cpu/sparc/macroAssembler_sparc.hpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/sparc/macroAssembler_sparc.hpp Fri May 26 18:39:27 2017 +0300 @@ -1277,7 +1277,8 @@ RegisterOrConstant itable_index, Register method_result, Register temp_reg, Register temp2_reg, - Label& no_such_interface); + Label& no_such_interface, + bool return_method = true); // virtual method calling void lookup_virtual_method(Register recv_klass, diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/sparc/sharedRuntime_sparc.cpp --- a/src/hotspot/cpu/sparc/sharedRuntime_sparc.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/sparc/sharedRuntime_sparc.cpp Fri May 26 18:39:27 2017 +0300 @@ -904,7 +904,7 @@ Label ok, ok2; __ 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); __ delayed()->nop(); diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/sparc/templateTable_sparc.cpp --- a/src/hotspot/cpu/sparc/templateTable_sparc.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/sparc/templateTable_sparc.cpp Fri May 26 18:39:27 2017 +0300 @@ -3081,15 +3081,15 @@ assert(byte_no == f1_byte, "use this argument"); const Register Rinterface = G1_scratch; + const Register Rmethod = Lscratch; const Register Rret = G3_scratch; - const Register Rindex = Lscratch; const Register O0_recv = O0; const Register O1_flags = O1; const Register O2_Klass = O2; const Register Rscratch = G4_scratch; 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 __ null_check(O0_recv, oopDesc::klass_offset_in_bytes()); @@ -3109,55 +3109,40 @@ __ 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); // // find entry point to call // - // compute start of first itableOffsetEntry (which is at end of vtable) - const int base = in_bytes(Klass::vtable_start_offset()); - Label search; - Register Rtemp = O1_flags; - - __ ld(O2_Klass, in_bytes(Klass::vtable_length_offset()), Rtemp); - __ sll(Rtemp, LogBytesPerWord, Rtemp); // Rscratch *= 4; - if (Assembler::is_simm13(base)) { - __ add(Rtemp, base, Rtemp); - } else { - __ set(base, Rscratch); - __ add(Rscratch, Rtemp, Rtemp); - } - __ add(O2_Klass, Rtemp, Rscratch); - - __ bind(search); - - __ ld_ptr(Rscratch, itableOffsetEntry::interface_offset_in_bytes(), Rtemp); - { - Label ok; - - // 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); + // Get declaring interface class from method + __ ld_ptr(Rmethod, Method::const_offset(), Rinterface); + __ ld_ptr(Rinterface, ConstMethod::constants_offset(), Rinterface); + __ ld_ptr(Rinterface, ConstantPool::pool_holder_offset_in_bytes(), Rinterface); + + // Get itable index from method + const Register Rindex = G5_method; + __ ld(Rmethod, Method::itable_index_offset(), Rindex); + __ sub(Rindex, Method::itable_index_max, Rindex); + __ neg(Rindex); + + __ lookup_interface_method(// inputs: rec. class, interface, itable index + O2_Klass, Rinterface, Rindex, + // outputs: method, scan temp reg, temp reg + G5_method, Rscratch, Rtemp, + L_no_such_interface); // Check for abstract method error. { @@ -3174,6 +3159,10 @@ __ profile_arguments_type(G5_method, Rcall, Gargs, true); __ profile_called_method(G5_method, Rscratch); __ 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) { diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/sparc/vtableStubs_sparc.cpp --- a/src/hotspot/cpu/sparc/vtableStubs_sparc.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/sparc/vtableStubs_sparc.cpp Fri May 26 18:39:27 2017 +0300 @@ -27,6 +27,7 @@ #include "code/vtableStubs.hpp" #include "interp_masm_sparc.hpp" #include "memory/resourceArea.hpp" +#include "oops/compiledICHolder.hpp" #include "oops/instanceKlass.hpp" #include "oops/klassVtable.hpp" #include "runtime/sharedRuntime.hpp" @@ -140,7 +141,8 @@ MacroAssembler* masm = new MacroAssembler(&cb); 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; // Entry arguments: @@ -164,14 +166,26 @@ } #endif /* PRODUCT */ - Label throw_icce; + Label L_no_such_interface; 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 - 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 L5_method, L2, L3, - throw_icce); + L_no_such_interface); #ifndef PRODUCT if (DebugVtables) { @@ -197,7 +211,7 @@ __ JMP(G3_scratch, 0); __ delayed()->nop(); - __ bind(throw_icce); + __ bind(L_no_such_interface); AddressLiteral icce(StubRoutines::throw_IncompatibleClassChangeError_entry()); __ jump_to(icce, G3_scratch); __ delayed()->restore(); @@ -232,7 +246,7 @@ MacroAssembler::instr_size_for_decode_klass_not_null() : 0); return basic + slop; } else { - const int basic = 34 * BytesPerInstWord + + const int basic = 54 * BytesPerInstWord + // shift;add for load_klass (only shift with zero heap based) (UseCompressedClassPointers ? MacroAssembler::instr_size_for_decode_klass_not_null() : 0); diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/x86/macroAssembler_x86.cpp --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp Fri May 26 18:39:27 2017 +0300 @@ -5809,8 +5809,13 @@ RegisterOrConstant itable_index, Register method_result, Register scan_temp, - Label& L_no_such_interface) { - assert_different_registers(recv_klass, intf_klass, method_result, scan_temp); + Label& L_no_such_interface, + 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, "caller must use same register for non-constant itable index as for method"); @@ -5827,9 +5832,11 @@ // %%% Could store the aligned, prescaled offset in the klassoop. lea(scan_temp, Address(recv_klass, scan_temp, times_vte_scale, vtable_base)); - // 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"); - lea(recv_klass, Address(recv_klass, itable_index, Address::times_ptr, itentry_off)); + if (return_method) { + // 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"); + lea(recv_klass, Address(recv_klass, itable_index, Address::times_ptr, itentry_off)); + } // for (scan = klass->itable(); scan->interface() != NULL; scan += scan_step) { // if (scan->interface() == intf) { @@ -5863,9 +5870,11 @@ bind(found_method); - // Got a hit. - movl(scan_temp, Address(scan_temp, itableOffsetEntry::offset_offset_in_bytes())); - movptr(method_result, Address(recv_klass, scan_temp, Address::times_1)); + if (return_method) { + // Got a hit. + movl(scan_temp, Address(scan_temp, itableOffsetEntry::offset_offset_in_bytes())); + movptr(method_result, Address(recv_klass, scan_temp, Address::times_1)); + } } diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/x86/macroAssembler_x86.hpp --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp Fri May 26 18:39:27 2017 +0300 @@ -544,7 +544,8 @@ RegisterOrConstant itable_index, Register method_result, Register scan_temp, - Label& no_such_interface); + Label& no_such_interface, + bool return_method = true); // virtual method calling void lookup_virtual_method(Register recv_klass, diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp --- a/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp Fri May 26 18:39:27 2017 +0300 @@ -957,7 +957,7 @@ Label missed; __ movptr(temp, Address(receiver, oopDesc::klass_offset_in_bytes())); __ 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); // 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 diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp Fri May 26 18:39:27 2017 +0300 @@ -949,7 +949,7 @@ { __ load_klass(temp, receiver); __ 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); __ jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub())); diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/x86/templateTable_x86.cpp --- a/src/hotspot/cpu/x86/templateTable_x86.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/x86/templateTable_x86.cpp Fri May 26 18:39:27 2017 +0300 @@ -3712,11 +3712,11 @@ void TemplateTable::invokeinterface(int byte_no) { transition(vtos, vtos); 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 - // rax: interface klass (from f1) - // rbx: itable index (from f2) + // rax: reference klass (from f1) + // rbx: method (from f2) // rcx: receiver // rdx: flags @@ -3738,10 +3738,28 @@ __ null_check(rcx, oopDesc::klass_offset_in_bytes()); __ 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 + __ restore_bcp(); // rbcp was destroyed by receiver type check __ 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 rdx, rax, rbx, diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/x86/vtableStubs_x86_32.cpp --- a/src/hotspot/cpu/x86/vtableStubs_x86_32.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/x86/vtableStubs_x86_32.cpp Fri May 26 18:39:27 2017 +0300 @@ -27,6 +27,7 @@ #include "code/vtableStubs.hpp" #include "interp_masm_x86.hpp" #include "memory/resourceArea.hpp" +#include "oops/compiledICHolder.hpp" #include "oops/instanceKlass.hpp" #include "oops/klassVtable.hpp" #include "runtime/sharedRuntime.hpp" @@ -147,7 +148,7 @@ MacroAssembler* masm = new MacroAssembler(&cb); // Entry arguments: - // rax,: Interface + // rax: CompiledICHolder // rcx: Receiver #ifndef PRODUCT @@ -155,25 +156,42 @@ __ incrementl(ExternalAddress((address) SharedRuntime::nof_megamorphic_calls_addr())); } #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 // (If we need to make rsi, rdi callee-save, do a push/pop here.) + const Register recv_klass_reg = rsi; + 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; + + 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; - Label throw_icce; - - // Get Method* and entrypoint for compiler + __ load_klass(recv_klass_reg, rcx); // restore recv_klass_reg __ 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 - method, rdi, - throw_icce); + method, temp_reg, + L_no_such_interface); // method (rbx): Method* // rcx: receiver @@ -193,9 +211,10 @@ address ame_addr = __ pc(); __ jmp(Address(method, Method::from_compiled_offset())); - __ bind(throw_icce); + __ bind(L_no_such_interface); __ jump(RuntimeAddress(StubRoutines::throw_IncompatibleClassChangeError_entry())); - masm->flush(); + + __ flush(); if (PrintMiscellaneous && (WizardMode || Verbose)) { tty->print_cr("itable #%d at " PTR_FORMAT "[%d] left over: %d", @@ -220,7 +239,7 @@ return (DebugVtables ? 210 : 16) + (CountCompiledCalls ? 6 : 0); } else { // 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 // +PrintMiscellaneous and +WizardMode to see information about diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/cpu/x86/vtableStubs_x86_64.cpp --- a/src/hotspot/cpu/x86/vtableStubs_x86_64.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/cpu/x86/vtableStubs_x86_64.cpp Fri May 26 18:39:27 2017 +0300 @@ -27,6 +27,7 @@ #include "code/vtableStubs.hpp" #include "interp_masm_x86.hpp" #include "memory/resourceArea.hpp" +#include "oops/compiledICHolder.hpp" #include "oops/instanceKlass.hpp" #include "oops/klassVtable.hpp" #include "runtime/sharedRuntime.hpp" @@ -147,36 +148,50 @@ #endif // Entry arguments: - // rax: Interface + // rax: CompiledICHolder // 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 // (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 // be able to walk the stack properly. This is not an issue except // when there are mistakes in this assembly code that could generate // 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* // j_rarg0: receiver @@ -197,7 +212,7 @@ address ame_addr = __ pc(); __ jmp(Address(method, Method::from_compiled_offset())); - __ bind(throw_icce); + __ bind(L_no_such_interface); __ jump(RuntimeAddress(StubRoutines::throw_IncompatibleClassChangeError_entry())); __ flush(); @@ -224,8 +239,8 @@ (UseCompressedClassPointers ? MacroAssembler::instr_size_for_decode_klass_not_null() : 0); } else { // Itable stub size - return (DebugVtables ? 512 : 74) + (CountCompiledCalls ? 13 : 0) + - (UseCompressedClassPointers ? MacroAssembler::instr_size_for_decode_klass_not_null() : 0); + return (DebugVtables ? 512 : 140) + (CountCompiledCalls ? 13 : 0) + + (UseCompressedClassPointers ? 2 * MacroAssembler::instr_size_for_decode_klass_not_null() : 0); } // In order to tune these parameters, run the JVM with VM options // +PrintMiscellaneous and +WizardMode to see information about diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/share/aot/aotCompiledMethod.cpp --- a/src/hotspot/share/aot/aotCompiledMethod.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/share/aot/aotCompiledMethod.cpp Fri May 26 18:39:27 2017 +0300 @@ -270,7 +270,7 @@ CompiledIC *ic = CompiledIC_at(&iter); if (ic->is_icholder_call()) { CompiledICHolder* cichk = ic->cached_icholder(); - f(cichk->holder_method()); + f(cichk->holder_metadata()); f(cichk->holder_klass()); } else { // Get Klass* or NULL (if value is -1) from GOT cell of virtual call PLT stub. diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/share/code/compiledIC.cpp --- a/src/hotspot/share/code/compiledIC.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/share/code/compiledIC.cpp Fri May 26 18:39:27 2017 +0300 @@ -230,10 +230,13 @@ #ifdef ASSERT int index = call_info->resolved_method()->itable_index(); assert(index == itable_index, "CallInfo pre-computes this"); -#endif //ASSERT InstanceKlass* k = call_info->resolved_method()->method_holder(); 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 { 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. @@ -517,7 +520,14 @@ bool CompiledIC::is_icholder_entry(address 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) { diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/share/code/compiledIC.hpp --- a/src/hotspot/share/code/compiledIC.hpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/share/code/compiledIC.hpp Fri May 26 18:39:27 2017 +0300 @@ -45,11 +45,11 @@ // \ / \ / // [4] \ / [4] \->-/ // \->- 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 // [2]: Compilation of a method // [3]: Recompilation of a method (note: only entry is changed. The Klass* must stay the same) diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/share/code/compiledMethod.cpp --- a/src/hotspot/share/code/compiledMethod.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/share/code/compiledMethod.cpp Fri May 26 18:39:27 2017 +0300 @@ -404,8 +404,7 @@ // yet be marked below. (We check this further below). CompiledICHolder* cichk_oop = ic->cached_icholder(); - if (cichk_oop->holder_method()->method_holder()->is_loader_alive(is_alive) && - cichk_oop->holder_klass()->is_loader_alive(is_alive)) { + if (cichk_oop->is_loader_alive(is_alive)) { return; } } else { diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/share/code/nmethod.cpp --- a/src/hotspot/share/code/nmethod.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/share/code/nmethod.cpp Fri May 26 18:39:27 2017 +0300 @@ -1545,7 +1545,7 @@ CompiledIC *ic = CompiledIC_at(&iter); if (ic->is_icholder_call()) { CompiledICHolder* cichk = ic->cached_icholder(); - f(cichk->holder_method()); + f(cichk->holder_metadata()); f(cichk->holder_klass()); } else { Metadata* ic_oop = ic->cached_metadata(); diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/share/interpreter/interpreterRuntime.cpp --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp Fri May 26 18:39:27 2017 +0300 @@ -822,6 +822,7 @@ case CallInfo::itable_call: cp_cache_entry->set_itable_call( bytecode, + info.resolved_klass(), info.resolved_method(), info.itable_index()); break; diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/share/oops/compiledICHolder.cpp --- a/src/hotspot/share/oops/compiledICHolder.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/share/oops/compiledICHolder.cpp Fri May 26 18:39:27 2017 +0300 @@ -32,8 +32,8 @@ volatile int CompiledICHolder::_live_not_claimed_count; -CompiledICHolder::CompiledICHolder(Method* method, Klass* klass) - : _holder_method(method), _holder_klass(klass) { +CompiledICHolder::CompiledICHolder(Metadata* metadata, Klass* klass) + : _holder_metadata(metadata), _holder_klass(klass) { #ifdef ASSERT Atomic::inc(&_live_count); Atomic::inc(&_live_not_claimed_count); @@ -47,12 +47,28 @@ } #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 void CompiledICHolder::print_on(outputStream* st) const { st->print("%s", internal_name()); - st->print(" - method: "); holder_method()->print_value_on(st); st->cr(); - st->print(" - klass: "); holder_klass()->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(); } void CompiledICHolder::print_value_on(outputStream* st) const { @@ -63,7 +79,7 @@ // Verification 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"); } diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/share/oops/compiledICHolder.hpp --- a/src/hotspot/share/oops/compiledICHolder.hpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/share/oops/compiledICHolder.hpp Fri May 26 18:39:27 2017 +0300 @@ -29,8 +29,9 @@ #include "utilities/macros.hpp" // A CompiledICHolder* is a helper object for the inline cache implementation. -// It holds an intermediate value (method+klass pair) used when converting from -// compiled to an interpreted call. +// It holds: +// (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 // safepoint by the ICBuffer logic. It's unsafe to free them earlier @@ -45,32 +46,33 @@ static volatile int _live_not_claimed_count; // allocated but not yet in use so not // reachable by iterating over nmethods - Method* _holder_method; + Metadata* _holder_metadata; Klass* _holder_klass; // to avoid name conflict with oopDesc::_klass CompiledICHolder* _next; public: // Constructor - CompiledICHolder(Method* method, Klass* klass); + CompiledICHolder(Metadata* metadata, Klass* klass); ~CompiledICHolder() NOT_DEBUG_RETURN; static int live_count() { return _live_count; } static int live_not_claimed_count() { return _live_not_claimed_count; } // accessors - Method* holder_method() const { return _holder_method; } 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_klass(Klass* k) { _holder_klass = k; } + void set_holder_metadata(Metadata* m) { _holder_metadata = m; } + void set_holder_klass(Klass* k) { _holder_klass = k; } - // interpreter support (offsets in bytes) - static int holder_method_offset() { return offset_of(CompiledICHolder, _holder_method); } + static int holder_metadata_offset() { return offset_of(CompiledICHolder, _holder_metadata); } static int holder_klass_offset() { return offset_of(CompiledICHolder, _holder_klass); } CompiledICHolder* next() { return _next; } void set_next(CompiledICHolder* n) { _next = n; } + bool is_loader_alive(BoolObjectClosure* is_alive); + // Verify void verify_on(outputStream* st); diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/share/oops/cpCache.cpp --- a/src/hotspot/share/oops/cpCache.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/share/oops/cpCache.cpp Fri May 26 18:39:27 2017 +0300 @@ -277,14 +277,16 @@ 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(invoke_code == Bytecodes::_invokeinterface, ""); InstanceKlass* interf = method->method_holder(); assert(interf->is_interface(), "must be an interface"); assert(!method->is_final_method(), "interfaces do not have final methods; cannot link to one here"); - set_f1(interf); - set_f2(index); + set_f1(referenced_klass); + set_f2((intx)method()); set_method_flags(as_TosState(method->result_type()), 0, // no option bits method()->size_of_parameters()); @@ -513,10 +515,23 @@ #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: // If this ConstantPoolCacheEntry refers to old_method then update it // 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) { if (is_vfinal()) { @@ -525,63 +540,35 @@ // match old_method so need an update // NOTE: can't use set_f2_as_vfinal_method as it asserts on different values _f2 = (intptr_t)new_method; - 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 vf-entry update: %s(%s)", new_method->name()->as_C_string(), new_method->signature()->as_C_string()); - } - return true; + log_adjust("vfinal", old_method, new_method, trace_name_printed); } - - // f1() is not used with virtual entries so bail out - return false; + return; } - if (_f1 == NULL) { - // NULL f1() means this is a virtual entry so bail out - // We are assuming that the vtable index does not need change. - return false; - } + assert (_f1 != NULL, "should not call with uninteresting entry"); - if (_f1 == old_method) { + if (!(_f1->is_method())) { + // _f1 is a Klass* for an interface, _f2 is the method + if (f2_as_interface_method() == old_method) { + _f2 = (intptr_t)new_method; + log_adjust("interface", old_method, new_method, trace_name_printed); + } + } else if (_f1 == old_method) { _f1 = new_method; - 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 entry update: %s(%s)", new_method->name()->as_C_string(), new_method->signature()->as_C_string()); - } - return true; + log_adjust("special, static or dynamic", old_method, new_method, trace_name_printed); } - - return false; } // a constant pool cache entry should never contain old or obsolete methods bool ConstantPoolCacheEntry::check_no_old_or_obsolete_entries() { - if (is_vfinal()) { - // virtual and final so _f2 contains method ptr instead of vtable index - Metadata* f2 = (Metadata*)_f2; - // Return false if _f2 refers to an old or an obsolete method. - // _f2 == NULL || !_f2->is_method() are just as unexpected here. - return (f2 != NULL NOT_PRODUCT(&& f2->is_valid()) && f2->is_method() && - !((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 + Method* m = get_interesting_method_entry(NULL); + // return false if m refers to a non-deleted old or obsolete method + if (m != NULL) { + assert(m->is_valid() && m->is_method(), "m is a valid method"); + return !m->is_old() && !m->is_obsolete(); // old is always set for old and obsolete + } else { 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) { @@ -598,10 +585,11 @@ return NULL; } else { if (!(_f1->is_method())) { - // _f1 can also contain a Klass* for an interface - return NULL; + // _f1 is a Klass* for an interface + m = f2_as_interface_method(); + } else { + m = f1_as_method(); } - m = f1_as_method(); } assert(m != NULL && m->is_method(), "sanity check"); if (m == NULL || !m->is_method() || (k != NULL && m->method_holder() != k)) { diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/share/oops/cpCache.hpp --- a/src/hotspot/share/oops/cpCache.hpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/share/oops/cpCache.hpp Fri May 26 18:39:27 2017 +0300 @@ -249,6 +249,7 @@ void set_itable_call( 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 int itable_index // index into itable for the method ); @@ -352,6 +353,7 @@ 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; } 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); } 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); } @@ -387,7 +389,7 @@ // trace_name_printed is set to true if the current call has // printed the klass name so that other routines in the adjust_* // 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 check_no_old_or_obsolete_entries(); Method* get_interesting_method_entry(Klass* k); diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/share/oops/klassVtable.cpp --- a/src/hotspot/share/oops/klassVtable.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/share/oops/klassVtable.cpp Fri May 26 18:39:27 2017 +0300 @@ -1185,7 +1185,6 @@ Array* methods = InstanceKlass::cast(interf)->methods(); int nof_methods = methods->length(); 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()); int ime_count = method_count_for_interface(interf); @@ -1354,8 +1353,10 @@ } } - // Only count interfaces with at least one method - if (method_count > 0) { + // Visit all interfaces which either have any methods or can participate in receiver type check. + // 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); } } diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/share/oops/method.hpp --- a/src/hotspot/share/oops/method.hpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/share/oops/method.hpp Fri May 26 18:39:27 2017 +0300 @@ -698,6 +698,7 @@ 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 signature_handler_offset() { return in_ByteSize(sizeof(Method) + wordSize); } + static ByteSize itable_index_offset() { return byte_offset_of(Method, _vtable_index ); } // for code generation static int method_data_offset_in_bytes() { return offset_of(Method, _method_data); } diff -r d44d912ea9bb -r 2e867226b914 src/hotspot/share/runtime/vmStructs.cpp --- a/src/hotspot/share/runtime/vmStructs.cpp Thu May 25 15:39:32 2017 -0400 +++ b/src/hotspot/share/runtime/vmStructs.cpp Fri May 26 18:39:27 2017 +0300 @@ -233,7 +233,7 @@ nonstatic_field(ArrayKlass, _dimension, int) \ volatile_nonstatic_field(ArrayKlass, _higher_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(ConstantPool, _tags, Array*) \ nonstatic_field(ConstantPool, _cache, ConstantPoolCache*) \ diff -r d44d912ea9bb -r 2e867226b914 src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java --- a/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java Thu May 25 15:39:32 2017 -0400 +++ b/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java Fri May 26 18:39:27 2017 +0300 @@ -80,13 +80,20 @@ mtype = mtype.insertParameterTypes(0, receiver); } if (!member.isField()) { - if (refKind == REF_invokeSpecial) { - member = member.asSpecial(); - LambdaForm lform = preparedLambdaForm(member); - return new Special(mtype, lform, member); - } else { - LambdaForm lform = preparedLambdaForm(member); - return new DirectMethodHandle(mtype, lform, member); + switch (refKind) { + case REF_invokeSpecial: { + member = member.asSpecial(); + LambdaForm lform = preparedLambdaForm(member); + return new Special(mtype, lform, member); + } + case REF_invokeInterface: { + LambdaForm lform = preparedLambdaForm(member); + return new Interface(mtype, lform, member, receiver); + } + default: { + LambdaForm lform = preparedLambdaForm(member); + return new DirectMethodHandle(mtype, lform, member); + } } } else { LambdaForm lform = preparedFieldLambdaForm(member); @@ -190,6 +197,7 @@ static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) { boolean needsInit = (which == LF_INVSTATIC_INIT); boolean doesAlloc = (which == LF_NEWINVSPECIAL); + boolean needsReceiverCheck = (which == LF_INVINTERFACE); String linkerName; LambdaForm.Kind kind; switch (which) { @@ -219,6 +227,7 @@ int nameCursor = ARG_LIMIT; final int NEW_OBJ = (doesAlloc ? nameCursor++ : -1); final int GET_MEMBER = nameCursor++; + final int CHECK_RECEIVER = (needsReceiverCheck ? nameCursor++ : -1); final int LINKER_CALL = nameCursor++; Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType()); assert(names.length == nameCursor); @@ -233,6 +242,10 @@ } assert(findDirectMethodHandle(names[GET_MEMBER]) == names[DMH_THIS]); 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! int result = LAST_RESULT; if (doesAlloc) { @@ -376,6 +389,29 @@ } } + /** 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. */ static class Constructor extends DirectMethodHandle { final MemberName initMethod; @@ -738,7 +774,8 @@ NF_allocateInstance = 8, NF_constructorMethod = 9, NF_UNSAFE = 10, - NF_LIMIT = 11; + NF_checkReceiver = 11, + NF_LIMIT = 12; private static final @Stable NamedFunction[] NFS = new NamedFunction[NF_LIMIT]; @@ -785,6 +822,11 @@ return new NamedFunction( MemberName.getFactory() .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: throw newInternalError("Unknown function: " + func); } diff -r d44d912ea9bb -r 2e867226b914 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/CompiledICHolder.java --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/CompiledICHolder.java Thu May 25 15:39:32 2017 -0400 +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/CompiledICHolder.java Fri May 26 18:39:27 2017 +0300 @@ -40,10 +40,10 @@ } private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { - Type type = db.lookupType("CompiledICHolder"); - holderMethod = new MetadataField(type.getAddressField("_holder_method"), 0); - holderKlass = new MetadataField(type.getAddressField("_holder_klass"), 0); - headerSize = type.getSize(); + Type type = db.lookupType("CompiledICHolder"); + holderMetadata = new MetadataField(type.getAddressField("_holder_metadata"), 0); + holderKlass = new MetadataField(type.getAddressField("_holder_klass"), 0); + headerSize = type.getSize(); } public CompiledICHolder(Address addr) { @@ -55,12 +55,12 @@ private static long headerSize; // Fields - private static MetadataField holderMethod; + private static MetadataField holderMetadata; private static MetadataField holderKlass; // Accessors for declared fields - public Method getHolderMethod() { return (Method) holderMethod.getValue(this); } - public Klass getHolderKlass() { return (Klass) holderKlass.getValue(this); } + public Metadata getHolderMetadata() { return (Metadata) holderMetadata.getValue(this); } + public Klass getHolderKlass() { return (Klass) holderKlass.getValue(this); } public void printValueOn(PrintStream tty) { tty.print("CompiledICHolder"); diff -r d44d912ea9bb -r 2e867226b914 test/hotspot/gtest/code/test_vtableStub.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/gtest/code/test_vtableStub.cpp Fri May 26 18:39:27 2017 +0300 @@ -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 +} diff -r d44d912ea9bb -r 2e867226b914 test/hotspot/jtreg/runtime/RedefineTests/RedefineInterfaceCall.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/runtime/RedefineTests/RedefineInterfaceCall.java Fri May 26 18:39:27 2017 +0300 @@ -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); + } +} diff -r d44d912ea9bb -r 2e867226b914 test/hotspot/jtreg/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformTestCommon.java --- a/test/hotspot/jtreg/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformTestCommon.java Thu May 25 15:39:32 2017 -0400 +++ b/test/hotspot/jtreg/runtime/SharedArchiveFile/serviceability/transformRelatedClasses/TransformTestCommon.java Fri May 26 18:39:27 2017 +0300 @@ -63,8 +63,8 @@ String parent, String child) throws Exception { - String parentSharedMatch = parent + " source: shared objects file"; - String childSharedMatch = child + " source: shared objects file"; + String parentSharedMatch = " " + parent + " source: shared objects file"; + String childSharedMatch = " " + child + " source: shared objects file"; if (entry.isParentExpectedShared) out.shouldContain(parentSharedMatch);