diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp index c6611b89956..0cc1871e588 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp @@ -1833,3 +1833,14 @@ void InterpreterMacroAssembler::profile_parameters_type(Register mdp, Register t bind(profile_continue); } } + +void InterpreterMacroAssembler::load_resolved_indy_entry(Register cache, Register index) { + // Get index out of bytecode pointer, get_cache_entry_pointer_at_bcp + get_cache_index_at_bcp(index, 1, sizeof(u4)); + // Get address of invokedynamic array + ldr(cache, Address(rcpool, in_bytes(ConstantPoolCache::invokedynamic_entries_offset()))); + // Scale the index to be the entry index * sizeof(ResolvedInvokeDynamicInfo) + lsl(index, index, log2i_exact(sizeof(ResolvedIndyEntry))); + add(cache, cache, Array::base_offset_in_bytes()); + lea(cache, Address(cache, index)); +} diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp index 6fd494af10a..0ff570b2e94 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp @@ -319,6 +319,8 @@ class InterpreterMacroAssembler: public MacroAssembler { set_last_Java_frame(esp, rfp, (address) pc(), rscratch1); MacroAssembler::_call_Unimplemented(call_site); } + + void load_resolved_indy_entry(Register cache, Register index); }; #endif // CPU_AARCH64_INTERP_MASM_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp index 7b2dd16b00e..ecd77e8e6f0 100644 --- a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp @@ -479,12 +479,21 @@ address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, __ profile_return_type(mdp, obj, tmp); } - // Pop N words from the stack - __ get_cache_and_index_at_bcp(r1, r2, 1, index_size); - __ ldr(r1, Address(r1, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset())); - __ andr(r1, r1, ConstantPoolCacheEntry::parameter_size_mask); + const Register cache = r1; + const Register index = r2; - __ add(esp, esp, r1, Assembler::LSL, 3); + if (index_size == sizeof(u4)) { + __ load_resolved_indy_entry(cache, index); + __ load_unsigned_short(cache, Address(cache, in_bytes(ResolvedIndyEntry::num_parameters_offset()))); + __ add(esp, esp, cache, Assembler::LSL, 3); + } else { + // Pop N words from the stack + __ get_cache_and_index_at_bcp(cache, index, 1, index_size); + __ ldr(cache, Address(cache, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset())); + __ andr(cache, cache, ConstantPoolCacheEntry::parameter_size_mask); + + __ add(esp, esp, cache, Assembler::LSL, 3); + } // Restore machine SP __ restore_sp_after_call(); diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp index 00866e5d956..7d35b0456cd 100644 --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -2320,13 +2320,82 @@ void TemplateTable::load_field_cp_cache_entry(Register obj, } } +// The rmethod register is input and overwritten to be the adapter method for the +// indy call. Link Register (lr) is set to the return address for the adapter and +// an appendix may be pushed to the stack. Registers r0-r3 are clobbered +void TemplateTable::load_invokedynamic_entry(Register method) { + // setup registers + const Register appendix = r0; + const Register cache = r2; + const Register index = r3; + assert_different_registers(method, appendix, cache, index, rcpool); + + __ save_bcp(); + + Label resolved; + + __ load_resolved_indy_entry(cache, index); + // Load-acquire the adapter method to match store-release in ResolvedIndyEntry::fill_in() + __ lea(method, Address(cache, in_bytes(ResolvedIndyEntry::method_offset()))); + __ ldar(method, method); + + // Compare the method to zero + __ cbnz(method, resolved); + + Bytecodes::Code code = bytecode(); + + // Call to the interpreter runtime to resolve invokedynamic + address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_from_cache); + __ mov(method, code); // this is essentially Bytecodes::_invokedynamic + __ call_VM(noreg, entry, method); + // Update registers with resolved info + __ load_resolved_indy_entry(cache, index); + // Load-acquire the adapter method to match store-release in ResolvedIndyEntry::fill_in() + __ lea(method, Address(cache, in_bytes(ResolvedIndyEntry::method_offset()))); + __ ldar(method, method); + +#ifdef ASSERT + __ cbnz(method, resolved); + __ stop("Should be resolved by now"); +#endif // ASSERT + __ bind(resolved); + + Label L_no_push; + // Check if there is an appendix + __ load_unsigned_byte(index, Address(cache, in_bytes(ResolvedIndyEntry::flags_offset()))); + __ tbz(index, ResolvedIndyEntry::has_appendix_shift, L_no_push); + + // Get appendix + __ load_unsigned_short(index, Address(cache, in_bytes(ResolvedIndyEntry::resolved_references_index_offset()))); + // Push the appendix as a trailing parameter + // since the parameter_size includes it. + __ push(method); + __ mov(method, index); + __ load_resolved_reference_at_index(appendix, method); + __ verify_oop(appendix); + __ pop(method); + __ push(appendix); // push appendix (MethodType, CallSite, etc.) + __ bind(L_no_push); + + // compute return type + __ load_unsigned_byte(index, Address(cache, in_bytes(ResolvedIndyEntry::result_type_offset()))); + // load return address + // Return address is loaded into link register(lr) and not pushed to the stack + // like x86 + { + const address table_addr = (address) Interpreter::invoke_return_entry_table_for(code); + __ mov(rscratch1, table_addr); + __ ldr(lr, Address(rscratch1, index, Address::lsl(3))); + } +} + void TemplateTable::load_invoke_cp_cache_entry(int byte_no, Register method, Register itable_index, Register flags, bool is_invokevirtual, bool is_invokevfinal, /*unused*/ - bool is_invokedynamic) { + bool is_invokedynamic /*unused*/) { // setup registers const Register cache = rscratch2; const Register index = r4; @@ -2347,7 +2416,7 @@ void TemplateTable::load_invoke_cp_cache_entry(int byte_no, const int index_offset = in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::f2_offset()); - size_t index_size = (is_invokedynamic ? sizeof(u4) : sizeof(u2)); + size_t index_size = sizeof(u2); resolve_cache_and_index(byte_no, cache, index, index_size); __ ldr(method, Address(cache, method_offset)); @@ -3167,7 +3236,7 @@ void TemplateTable::prepare_invoke(int byte_no, load_invoke_cp_cache_entry(byte_no, method, index, flags, is_invokevirtual, false, is_invokedynamic); // maybe push appendix to arguments (just before return address) - if (is_invokedynamic || is_invokehandle) { + if (is_invokehandle) { Label L_no_push; __ tbz(flags, ConstantPoolCacheEntry::has_appendix_shift, L_no_push); // Push the appendix as a trailing parameter. @@ -3438,7 +3507,7 @@ void TemplateTable::invokedynamic(int byte_no) { transition(vtos, vtos); assert(byte_no == f1_byte, "use this argument"); - prepare_invoke(byte_no, rmethod, r0); + load_invokedynamic_entry(rmethod); // r0: CallSite object (from cpool->resolved_references[]) // rmethod: MH.linkToCallSite method (from f2) diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp index db0747f6d39..14042588886 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2021 SAP SE. All rights reserved. + * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023 SAP SE. 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 @@ -127,6 +127,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void get_cache_index_at_bcp(Register Rdst, int bcp_offset, size_t index_size); void get_cache_and_index_at_bcp(Register cache, int bcp_offset, size_t index_size = sizeof(u2)); + void load_resolved_indy_entry(Register cache, Register index); void get_u4(Register Rdst, Register Rsrc, int offset, signedOrNot is_signed); diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp index a407b4a6142..f3b2a4ec300 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2022 SAP SE. All rights reserved. + * Copyright (c) 2012, 2023 SAP SE. 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 @@ -480,6 +480,18 @@ void InterpreterMacroAssembler::get_u4(Register Rdst, Register Rsrc, int offset, #endif } +void InterpreterMacroAssembler::load_resolved_indy_entry(Register cache, Register index) { + // Get index out of bytecode pointer, get_cache_entry_pointer_at_bcp + get_cache_index_at_bcp(index, 1, sizeof(u4)); + + // Get address of invokedynamic array + ld_ptr(cache, in_bytes(ConstantPoolCache::invokedynamic_entries_offset()), R27_constPoolCache); + // Scale the index to be the entry index * sizeof(ResolvedInvokeDynamicInfo) + sldi(index, index, log2i_exact(sizeof(ResolvedIndyEntry))); + addi(index, index, Array::base_offset_in_bytes()); + add(cache, cache, index); +} + // Load object from cpool->resolved_references(index). // Kills: // - index diff --git a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp index 8f425c1ac37..3aa78d00d76 100644 --- a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2015, 2022 SAP SE. All rights reserved. + * Copyright (c) 2015, 2023 SAP SE. 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 @@ -26,6 +26,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" #include "classfile/javaClasses.hpp" +#include "compiler/disassembler.hpp" #include "gc/shared/barrierSetAssembler.hpp" #include "interpreter/bytecodeHistogram.hpp" #include "interpreter/interpreter.hpp" @@ -53,7 +54,7 @@ #include "utilities/macros.hpp" #undef __ -#define __ _masm-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> // Size of interpreter code. Increase if too small. Interpreter will // fail with a guarantee ("not enough space for interpreter generation"); @@ -642,14 +643,24 @@ address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, const Register cache = R11_scratch1; const Register size = R12_scratch2; - __ get_cache_and_index_at_bcp(cache, 1, index_size); + if (index_size == sizeof(u4)) { + __ get_cache_index_at_bcp(size, 1, index_size); // Load index. + // Get address of invokedynamic array + __ ld_ptr(cache, in_bytes(ConstantPoolCache::invokedynamic_entries_offset()), R27_constPoolCache); + // Scale the index to be the entry index * sizeof(ResolvedInvokeDynamicInfo) + __ sldi(size, size, log2i_exact(sizeof(ResolvedIndyEntry))); + __ add(cache, cache, size); + __ lhz(size, Array::base_offset_in_bytes() + in_bytes(ResolvedIndyEntry::num_parameters_offset()), cache); + } else { + __ get_cache_and_index_at_bcp(cache, 1, index_size); - // Get least significant byte of 64 bit value: + // Get least significant byte of 64 bit value: #if defined(VM_LITTLE_ENDIAN) - __ lbz(size, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset()), cache); + __ lbz(size, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset()), cache); #else - __ lbz(size, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset()) + 7, cache); + __ lbz(size, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset()) + 7, cache); #endif + } __ sldi(size, size, Interpreter::logStackElementSize); __ add(R15_esp, R15_esp, size); diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp index fc55dbf390b..9a691887745 100644 --- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013, 2022 SAP SE. All rights reserved. + * Copyright (c) 2013, 2023 SAP SE. 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 @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" +#include "compiler/disassembler.hpp" #include "gc/shared/barrierSetAssembler.hpp" #include "gc/shared/tlab_globals.hpp" #include "interpreter/interpreter.hpp" @@ -49,7 +50,7 @@ #include "utilities/powerOfTwo.hpp" #undef __ -#define __ _masm-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> // ============================================================================ // Misc helpers @@ -2273,6 +2274,73 @@ void TemplateTable::load_field_cp_cache_entry(Register Robj, } } +// Sets registers: +// `method` Target method for invokedynamic +// R3_RET Return address for invoke +// +// Kills: R11, R21, R30, R31 +void TemplateTable::load_invokedynamic_entry(Register method) { + // setup registers + const Register ret_addr = R3_RET; + const Register appendix = R30; + const Register cache = R31; + const Register index = R21_tmp1; + const Register tmp = R11_scratch1; + assert_different_registers(method, appendix, cache, index, tmp); + + Label resolved; + + __ load_resolved_indy_entry(cache, index); + __ ld_ptr(method, in_bytes(ResolvedIndyEntry::method_offset()), cache); + + // The invokedynamic is unresolved iff method is NULL + __ cmpdi(CCR0, method, 0); + __ bne(CCR0, resolved); + + Bytecodes::Code code = bytecode(); + + // Call to the interpreter runtime to resolve invokedynamic + address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_from_cache); + __ li(R4_ARG2, code); + __ call_VM(noreg, entry, R4_ARG2, true); + // Update registers with resolved info + __ load_resolved_indy_entry(cache, index); + __ ld_ptr(method, in_bytes(ResolvedIndyEntry::method_offset()), cache); + + DEBUG_ONLY(__ cmpdi(CCR0, method, 0)); + __ asm_assert_ne("Should be resolved by now"); + __ bind(resolved); + __ isync(); // Order load wrt. succeeding loads. + + Label L_no_push; + // Check if there is an appendix + __ lbz(index, in_bytes(ResolvedIndyEntry::flags_offset()), cache); + __ rldicl_(R0, index, 64-ResolvedIndyEntry::has_appendix_shift, 63); + __ beq(CCR0, L_no_push); + + // Get appendix + __ lhz(index, in_bytes(ResolvedIndyEntry::resolved_references_index_offset()), cache); + // Push the appendix as a trailing parameter + assert(cache->is_nonvolatile(), "C-call in resolve_oop_handle"); + __ load_resolved_reference_at_index(appendix, index, /* temp */ ret_addr, tmp); + __ verify_oop(appendix); + __ push_ptr(appendix); // push appendix (MethodType, CallSite, etc.) + __ bind(L_no_push); + + // load return address + { + Register Rtable_addr = tmp; + address table_addr = (address) Interpreter::invoke_return_entry_table_for(code); + + // compute return type + __ lbz(index, in_bytes(ResolvedIndyEntry::result_type_offset()), cache); + __ load_dispatch_table(Rtable_addr, (address*)table_addr); + __ sldi(index, index, LogBytesPerWord); + // Get return address. + __ ldx(ret_addr, Rtable_addr, index); + } +} + // Load the constant pool cache entry at invokes into registers. // Resolve if necessary. @@ -2293,7 +2361,7 @@ void TemplateTable::load_invoke_cp_cache_entry(int byte_no, Register Rflags, bool is_invokevirtual, bool is_invokevfinal, - bool is_invokedynamic) { + bool is_invokedynamic /*unused*/) { ByteSize cp_base_offset = ConstantPoolCache::base_offset(); // Determine constant pool cache field offsets. @@ -2310,7 +2378,7 @@ void TemplateTable::load_invoke_cp_cache_entry(int byte_no, // Already resolved. __ get_cache_and_index_at_bcp(Rcache, 1); } else { - resolve_cache_and_index(byte_no, Rcache, /* temp */ Rmethod, is_invokedynamic ? sizeof(u4) : sizeof(u2)); + resolve_cache_and_index(byte_no, Rcache, /* temp */ Rmethod, sizeof(u2)); } __ ld(Rmethod, method_offset, Rcache); @@ -3327,7 +3395,7 @@ void TemplateTable::prepare_invoke(int byte_no, // Determine flags. const Bytecodes::Code code = bytecode(); const bool is_invokeinterface = code == Bytecodes::_invokeinterface; - const bool is_invokedynamic = code == Bytecodes::_invokedynamic; + const bool is_invokedynamic = false; // should not reach here with invokedynamic const bool is_invokehandle = code == Bytecodes::_invokehandle; const bool is_invokevirtual = code == Bytecodes::_invokevirtual; const bool is_invokespecial = code == Bytecodes::_invokespecial; @@ -3343,7 +3411,7 @@ void TemplateTable::prepare_invoke(int byte_no, // Saving of SP done in call_from_interpreter. // Maybe push "appendix" to arguments. - if (is_invokedynamic || is_invokehandle) { + if (is_invokehandle) { Label Ldone; Register reference = Rscratch1; @@ -3653,14 +3721,13 @@ void TemplateTable::invokeinterface(int byte_no) { void TemplateTable::invokedynamic(int byte_no) { transition(vtos, vtos); - const Register Rret_addr = R3_ARG1, - Rflags = R31, - Rmethod = R22_tmp2, - Rscratch1 = R30, - Rscratch2 = R11_scratch1, - Rscratch3 = R12_scratch2; + const Register Rret_addr = R3_RET; + const Register Rmethod = R22_tmp2; + const Register Rscratch1 = R30; + const Register Rscratch2 = R11_scratch1; - prepare_invoke(byte_no, Rmethod, Rret_addr, Rscratch1, noreg, Rflags, Rscratch2, Rscratch3); + // Returns target method in Rmethod and return address in R3_RET. Kills all argument registers. + load_invokedynamic_entry(Rmethod); // Profile this call. __ profile_call(Rscratch1, Rscratch2); diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp index 06412dd613d..0c6037b521a 100644 --- a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp @@ -1916,6 +1916,18 @@ void InterpreterMacroAssembler::profile_parameters_type(Register mdp, Register t } } +void InterpreterMacroAssembler::load_resolved_indy_entry(Register cache, Register index) { + // Get index out of bytecode pointer, get_cache_entry_pointer_at_bcp + get_cache_index_at_bcp(index, 1, sizeof(u4)); + // Get address of invokedynamic array + ld(cache, Address(xcpool, in_bytes(ConstantPoolCache::invokedynamic_entries_offset()))); + // Scale the index to be the entry index * sizeof(ResolvedInvokeDynamicInfo) + slli(index, index, log2i_exact(sizeof(ResolvedIndyEntry))); + add(cache, cache, Array::base_offset_in_bytes()); + add(cache, cache, index); + la(cache, Address(cache, 0)); +} + void InterpreterMacroAssembler::get_method_counters(Register method, Register mcs, Label& skip) { Label has_counters; diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp index 72926ad7228..87453e4f36f 100644 --- a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp @@ -299,6 +299,8 @@ class InterpreterMacroAssembler: public MacroAssembler { MacroAssembler::_call_Unimplemented(call_site); } + void load_resolved_indy_entry(Register cache, Register index); + #ifdef ASSERT void verify_access_flags(Register access_flags, uint32_t flag_bits, const char* msg, bool stop_by_hit = true); diff --git a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp index 9e24cee7cb8..0e4fff2327b 100644 --- a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp @@ -440,18 +440,27 @@ address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, __ profile_return_type(mdp, obj, tmp); } - // Pop N words from the stack - __ get_cache_and_index_at_bcp(x11, x12, 1, index_size); - __ ld(x11, Address(x11, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset())); - __ andi(x11, x11, ConstantPoolCacheEntry::parameter_size_mask); + const Register cache = x11; + const Register index = x12; - __ shadd(esp, x11, esp, t0, 3); + if (index_size == sizeof(u4)) { + __ load_resolved_indy_entry(cache, index); + __ load_unsigned_short(cache, Address(cache, in_bytes(ResolvedIndyEntry::num_parameters_offset()))); + __ shadd(esp, cache, esp, t0, 3); + } else { + // Pop N words from the stack + __ get_cache_and_index_at_bcp(cache, index, 1, index_size); + __ ld(cache, Address(cache, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset())); + __ andi(cache, cache, ConstantPoolCacheEntry::parameter_size_mask); + + __ shadd(esp, cache, esp, t0, 3); + } // Restore machine SP __ restore_sp_after_call(); - __ check_and_handle_popframe(xthread); - __ check_and_handle_earlyret(xthread); + __ check_and_handle_popframe(xthread); + __ check_and_handle_earlyret(xthread); __ get_dispatch(); __ dispatch_next(state, step); diff --git a/src/hotspot/cpu/riscv/templateTable_riscv.cpp b/src/hotspot/cpu/riscv/templateTable_riscv.cpp index 2a68f176214..0a454e85389 100644 --- a/src/hotspot/cpu/riscv/templateTable_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateTable_riscv.cpp @@ -2215,13 +2215,83 @@ void TemplateTable::load_field_cp_cache_entry(Register obj, } } +// The xmethod register is input and overwritten to be the adapter method for the +// indy call. Return address (ra) is set to the return address for the adapter and +// an appendix may be pushed to the stack. Registers x10-x13 are clobbered. +void TemplateTable::load_invokedynamic_entry(Register method) { + // setup registers + const Register appendix = x10; + const Register cache = x12; + const Register index = x13; + assert_different_registers(method, appendix, cache, index, xcpool); + + __ save_bcp(); + + Label resolved; + + __ load_resolved_indy_entry(cache, index); + __ membar(MacroAssembler::AnyAny); + __ ld(method, Address(cache, in_bytes(ResolvedIndyEntry::method_offset()))); + __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); + + // Compare the method to zero + __ bnez(method, resolved); + + Bytecodes::Code code = bytecode(); + + // Call to the interpreter runtime to resolve invokedynamic + address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_from_cache); + __ mv(method, code); // this is essentially Bytecodes::_invokedynamic + __ call_VM(noreg, entry, method); + // Update registers with resolved info + __ load_resolved_indy_entry(cache, index); + __ membar(MacroAssembler::AnyAny); + __ ld(method, Address(cache, in_bytes(ResolvedIndyEntry::method_offset()))); + __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); + +#ifdef ASSERT + __ bnez(method, resolved); + __ stop("Should be resolved by now"); +#endif // ASSERT + __ bind(resolved); + + Label L_no_push; + // Check if there is an appendix + __ load_unsigned_byte(index, Address(cache, in_bytes(ResolvedIndyEntry::flags_offset()))); + __ andi(t0, index, 1UL << ResolvedIndyEntry::has_appendix_shift); + __ beqz(t0, L_no_push); + + // Get appendix + __ load_unsigned_short(index, Address(cache, in_bytes(ResolvedIndyEntry::resolved_references_index_offset()))); + // Push the appendix as a trailing parameter + // since the parameter_size includes it. + __ push_reg(method); + __ mv(method, index); + __ load_resolved_reference_at_index(appendix, method); + __ verify_oop(appendix); + __ pop_reg(method); + __ push_reg(appendix); // push appendix (MethodType, CallSite, etc.) + __ bind(L_no_push); + + // compute return type + __ load_unsigned_byte(index, Address(cache, in_bytes(ResolvedIndyEntry::result_type_offset()))); + // load return address + // Return address is loaded into ra and not pushed to the stack like x86 + { + const address table_addr = (address) Interpreter::invoke_return_entry_table_for(code); + __ mv(t0, table_addr); + __ shadd(t0, index, t0, index, 3); + __ ld(ra, Address(t0, 0)); + } +} + void TemplateTable::load_invoke_cp_cache_entry(int byte_no, Register method, Register itable_index, Register flags, bool is_invokevirtual, bool is_invokevfinal, /*unused*/ - bool is_invokedynamic) { + bool is_invokedynamic /*unused*/) { // setup registers const Register cache = t1; const Register index = x14; @@ -2241,7 +2311,7 @@ void TemplateTable::load_invoke_cp_cache_entry(int byte_no, const int index_offset = in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::f2_offset()); - const size_t index_size = (is_invokedynamic ? sizeof(u4) : sizeof(u2)); + size_t index_size = sizeof(u2); resolve_cache_and_index(byte_no, cache, index, index_size); __ ld(method, Address(cache, method_offset)); @@ -3086,7 +3156,7 @@ void TemplateTable::prepare_invoke(int byte_no, load_invoke_cp_cache_entry(byte_no, method, index, flags, is_invokevirtual, false, is_invokedynamic); // maybe push appendix to arguments (just before return address) - if (is_invokedynamic || is_invokehandle) { + if (is_invokehandle) { Label L_no_push; __ andi(t0, flags, 1UL << ConstantPoolCacheEntry::has_appendix_shift); __ beqz(t0, L_no_push); @@ -3347,7 +3417,7 @@ void TemplateTable::invokedynamic(int byte_no) { transition(vtos, vtos); assert(byte_no == f1_byte, "use this argument"); - prepare_invoke(byte_no, xmethod, x10); + load_invokedynamic_entry(xmethod); // x10: CallSite object (from cpool->resolved_references[]) // xmethod: MH.linkToCallSite method (from f2) diff --git a/src/hotspot/cpu/s390/interp_masm_s390.cpp b/src/hotspot/cpu/s390/interp_masm_s390.cpp index b8f354d3e58..b4a7a041725 100644 --- a/src/hotspot/cpu/s390/interp_masm_s390.cpp +++ b/src/hotspot/cpu/s390/interp_masm_s390.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2020 SAP SE. All rights reserved. + * Copyright (c) 2016, 2023 SAP SE. 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 @@ -346,6 +346,17 @@ void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache, Regis BLOCK_COMMENT("}"); } +void InterpreterMacroAssembler::load_resolved_indy_entry(Register cache, Register index) { + // Get index out of bytecode pointer, get_cache_entry_pointer_at_bcp + get_cache_index_at_bcp(index, 1, sizeof(u4)); + // Get address of invokedynamic array + get_constant_pool_cache(cache); + z_lg(cache, Address(cache, in_bytes(ConstantPoolCache::invokedynamic_entries_offset()))); + // Scale the index to be the entry index * sizeof(ResolvedInvokeDynamicInfo) + z_sllg(index, index, exact_log2(sizeof(ResolvedIndyEntry))); + z_la(cache, Array::base_offset_in_bytes(), index, cache); +} + // Kills Z_R0_scratch. void InterpreterMacroAssembler::get_cache_and_index_and_bytecode_at_bcp(Register cache, Register cpe_offset, @@ -741,6 +752,11 @@ void InterpreterMacroAssembler::get_constant_pool(Register Rdst) { mem2reg_opt(Rdst, Address(Rdst, ConstMethod::constants_offset())); } +void InterpreterMacroAssembler::get_constant_pool_cache(Register Rdst) { + get_constant_pool(Rdst); + mem2reg_opt(Rdst, Address(Rdst, ConstantPool::cache_offset_in_bytes())); +} + void InterpreterMacroAssembler::get_cpool_and_tags(Register Rcpool, Register Rtags) { get_constant_pool(Rcpool); mem2reg_opt(Rtags, Address(Rcpool, ConstantPool::tags_offset_in_bytes())); diff --git a/src/hotspot/cpu/s390/interp_masm_s390.hpp b/src/hotspot/cpu/s390/interp_masm_s390.hpp index ac2904a4ab9..755861dd044 100644 --- a/src/hotspot/cpu/s390/interp_masm_s390.hpp +++ b/src/hotspot/cpu/s390/interp_masm_s390.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2017 SAP SE. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023 SAP SE. 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 @@ -112,6 +112,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void gen_subtype_check(Register sub_klass, Register super_klass, Register tmp1, Register tmp2, Label &ok_is_subtype); void get_cache_and_index_at_bcp(Register cache, Register cpe_offset, int bcp_offset, size_t index_size = sizeof(u2)); + void load_resolved_indy_entry(Register cache, Register index); void get_cache_and_index_and_bytecode_at_bcp(Register cache, Register cpe_offset, Register bytecode, int byte_no, int bcp_offset, size_t index_size = sizeof(u2)); void get_cache_entry_pointer_at_bcp(Register cache, Register tmp, int bcp_offset, size_t index_size = sizeof(u2)); diff --git a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp index 996ef31dba8..87e23aacd69 100644 --- a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp @@ -26,6 +26,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" #include "classfile/javaClasses.hpp" +#include "compiler/disassembler.hpp" #include "gc/shared/barrierSetAssembler.hpp" #include "interpreter/abstractInterpreter.hpp" #include "interpreter/bytecodeHistogram.hpp" @@ -60,9 +61,9 @@ int TemplateInterpreter::InterpreterCodeSize = 320*K; #undef __ #ifdef PRODUCT - #define __ _masm-> + #define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> #else - #define __ _masm-> + #define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> // #define __ (Verbose ? (_masm->block_comment(FILE_AND_LINE),_masm):_masm)-> #endif @@ -649,14 +650,19 @@ address TemplateInterpreterGenerator::generate_return_entry_for (TosState state, } Register cache = Z_tmp_1; - Register size = Z_tmp_1; - Register offset = Z_tmp_2; - const int flags_offset = in_bytes(ConstantPoolCache::base_offset() + - ConstantPoolCacheEntry::flags_offset()); - __ get_cache_and_index_at_bcp(cache, offset, 1, index_size); + Register size = Z_tmp_2; + Register index = Z_tmp_2; + if (index_size == sizeof(u4)) { + __ load_resolved_indy_entry(cache, index); + __ z_llgh(size, in_bytes(ResolvedIndyEntry::num_parameters_offset()), cache); + } else { + const int flags_offset = in_bytes(ConstantPoolCache::base_offset() + + ConstantPoolCacheEntry::flags_offset()); + __ get_cache_and_index_at_bcp(cache, index, 1, index_size); - // #args is in rightmost byte of the _flags field. - __ z_llgc(size, Address(cache, offset, flags_offset+(sizeof(size_t)-1))); + // #args is in rightmost byte of the _flags field. + __ z_llgc(size, Address(cache, index, flags_offset + (sizeof(size_t) - 1))); + } __ z_sllg(size, size, Interpreter::logStackElementSize); // Each argument size in bytes. __ z_agr(Z_esp, size); // Pop arguments. diff --git a/src/hotspot/cpu/s390/templateTable_s390.cpp b/src/hotspot/cpu/s390/templateTable_s390.cpp index d952b685676..c8fa9449175 100644 --- a/src/hotspot/cpu/s390/templateTable_s390.cpp +++ b/src/hotspot/cpu/s390/templateTable_s390.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2020 SAP SE. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023 SAP SE. 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 @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" +#include "compiler/disassembler.hpp" #include "gc/shared/barrierSetAssembler.hpp" #include "gc/shared/tlab_globals.hpp" #include "interpreter/interpreter.hpp" @@ -51,7 +52,8 @@ #define BLOCK_COMMENT(str) #define BIND(label) __ bind(label); #else -#define __ (PRODUCT_ONLY(false&&)Verbose ? (_masm->block_comment(FILE_AND_LINE),_masm):_masm)-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> +// #define __ (PRODUCT_ONLY(false&&)Verbose ? (_masm->block_comment(FILE_AND_LINE),_masm):_masm)-> #define BLOCK_COMMENT(str) __ block_comment(str) #define BIND(label) __ bind(label); BLOCK_COMMENT(#label ":") #endif @@ -2416,13 +2418,71 @@ void TemplateTable::load_field_cp_cache_entry(Register obj, } } +void TemplateTable::load_invokedynamic_entry(Register method) { + const Register cache = Z_tmp_1; + const Register index = Z_tmp_3; + const Register appendix = Z_R1_scratch; + assert_different_registers(cache, index, appendix, method); + + Label resolved; + __ load_resolved_indy_entry(cache, index); + __ z_lg(method, Address(cache, in_bytes(ResolvedIndyEntry::method_offset()))); + + // The invokedynamic is unresolved iff method is NULL + __ z_clgij(method, (unsigned long)nullptr, Assembler::bcondNotEqual, resolved); // method != 0, jump to resolved + Bytecodes::Code code = bytecode(); + // Call to the interpreter runtime to resolve invokedynamic + address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_from_cache); + __ load_const_optimized(Z_ARG2, (int)code); + __ call_VM(noreg, entry, Z_ARG2); + // Update registers with resolved info + __ load_resolved_indy_entry(cache, index); + __ z_lg(method, Address(cache, in_bytes(ResolvedIndyEntry::method_offset()))); +#ifdef ASSERT + __ z_clgij(method, (unsigned long)nullptr, Assembler::bcondNotEqual, resolved); // method != 0, jump to resolved + __ stop("should be resolved by now"); +#endif // ASSERT + __ bind(resolved); + + Label L_no_push; + __ z_llgc(index, Address(cache, in_bytes(ResolvedIndyEntry::flags_offset()))); + __ testbit(index, ResolvedIndyEntry::has_appendix_shift); + __ z_bfalse(L_no_push); + // get appendix + __ z_llgh(index, Address(cache, in_bytes(ResolvedIndyEntry::resolved_references_index_offset()))); + // Push the appendix as a trailing parameter. + // This must be done before we get the receiver, + // since the parameter_size includes it. + __ load_resolved_reference_at_index(appendix, index); + __ verify_oop(appendix); + __ push_ptr(appendix); // Push appendix (MethodType, CallSite, etc.). + __ bind(L_no_push); + + // Compute return type. + Register ret_type = index; + __ z_llgc(ret_type, Address(cache, in_bytes(ResolvedIndyEntry::result_type_offset()))); + + const address table_addr = (address)Interpreter::invoke_return_entry_table_for(code); + __ load_absolute_address(Z_R14, table_addr); + + const int bit_shift = LogBytesPerWord; // Size of each table entry. + // const int r_bitpos = 63 - bit_shift; + // const int l_bitpos = r_bitpos - ConstantPoolCacheEntry::tos_state_bits + 1; + // const int n_rotate = bit_shift-ConstantPoolCacheEntry::tos_state_shift; + // __ rotate_then_insert(ret_type, Z_R0_scratch, l_bitpos, r_bitpos, n_rotate, true); + // Make sure we don't need to mask flags for tos_state after the above shift. + __ z_sllg(ret_type, ret_type, bit_shift); + ConstantPoolCacheEntry::verify_tos_state_shift(); + __ z_lg(Z_R14, Address(Z_R14, ret_type)); +} + void TemplateTable::load_invoke_cp_cache_entry(int byte_no, Register method, Register itable_index, Register flags, bool is_invokevirtual, bool is_invokevfinal, // unused - bool is_invokedynamic) { + bool is_invokedynamic /* unused */) { BLOCK_COMMENT("load_invoke_cp_cache_entry {"); // Setup registers. const Register cache = Z_ARG1; @@ -2445,7 +2505,7 @@ void TemplateTable::load_invoke_cp_cache_entry(int byte_no, __ get_cache_and_index_at_bcp(cache, cpe_offset, 1); } else { // Need to resolve. - resolve_cache_and_index(byte_no, cache, cpe_offset, is_invokedynamic ? sizeof(u4) : sizeof(u2)); + resolve_cache_and_index(byte_no, cache, cpe_offset, sizeof(u2)); } __ z_lg(method, Address(cache, cpe_offset, method_offset)); @@ -3382,7 +3442,7 @@ void TemplateTable::prepare_invoke(int byte_no, // Determine flags. const Bytecodes::Code code = bytecode(); const bool is_invokeinterface = code == Bytecodes::_invokeinterface; - const bool is_invokedynamic = code == Bytecodes::_invokedynamic; + const bool is_invokedynamic = false; // should not reach here with invokedynamic const bool is_invokehandle = code == Bytecodes::_invokehandle; const bool is_invokevirtual = code == Bytecodes::_invokevirtual; const bool is_invokespecial = code == Bytecodes::_invokespecial; @@ -3399,7 +3459,7 @@ void TemplateTable::prepare_invoke(int byte_no, load_invoke_cp_cache_entry(byte_no, method, index, flags, is_invokevirtual, false, is_invokedynamic); // Maybe push appendix to arguments. - if (is_invokedynamic || is_invokehandle) { + if (is_invokehandle) { Label L_no_push; Register resolved_reference = Z_R1_scratch; __ testbit(flags, ConstantPoolCacheEntry::has_appendix_shift); @@ -3698,9 +3758,8 @@ void TemplateTable::invokedynamic(int byte_no) { transition(vtos, vtos); const Register Rmethod = Z_tmp_2; - const Register Rcallsite = Z_tmp_1; - prepare_invoke(byte_no, Rmethod, Rcallsite); + load_invokedynamic_entry(Rmethod); // Rmethod: CallSite object (from f1) // Rcallsite: MH.linkToCallSite method (from f2) diff --git a/src/hotspot/cpu/x86/interp_masm_x86.cpp b/src/hotspot/cpu/x86/interp_masm_x86.cpp index 1a9fca32c1f..2b9db41b99c 100644 --- a/src/hotspot/cpu/x86/interp_masm_x86.cpp +++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp @@ -2065,3 +2065,17 @@ void InterpreterMacroAssembler::notify_method_exit( pop(state); } } + +void InterpreterMacroAssembler::load_resolved_indy_entry(Register cache, Register index) { + // Get index out of bytecode pointer, get_cache_entry_pointer_at_bcp + get_cache_index_at_bcp(index, 1, sizeof(u4)); + // Get address of invokedynamic array + movptr(cache, Address(rbp, frame::interpreter_frame_cache_offset * wordSize)); + movptr(cache, Address(cache, in_bytes(ConstantPoolCache::invokedynamic_entries_offset()))); + if (is_power_of_2(sizeof(ResolvedIndyEntry))) { + shll(index, log2i_exact(sizeof(ResolvedIndyEntry))); // Scale index by power of 2 + } else { + imull(index, index, sizeof(ResolvedIndyEntry)); // Scale the index to be the entry index * sizeof(ResolvedInvokeDynamicInfo) + } + lea(cache, Address(cache, index, Address::times_1, Array::base_offset_in_bytes())); +} diff --git a/src/hotspot/cpu/x86/interp_masm_x86.hpp b/src/hotspot/cpu/x86/interp_masm_x86.hpp index 49b0aaa9caa..db2361f6fdb 100644 --- a/src/hotspot/cpu/x86/interp_masm_x86.hpp +++ b/src/hotspot/cpu/x86/interp_masm_x86.hpp @@ -306,6 +306,8 @@ class InterpreterMacroAssembler: public MacroAssembler { void profile_return_type(Register mdp, Register ret, Register tmp); void profile_parameters_type(Register mdp, Register tmp1, Register tmp2); + void load_resolved_indy_entry(Register cache, Register index); + }; #endif // CPU_X86_INTERP_MASM_X86_HPP diff --git a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp index f31c551c5f8..fee7554d67a 100644 --- a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp +++ b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp @@ -220,12 +220,17 @@ address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, const Register cache = rbx; const Register index = rcx; - __ get_cache_and_index_at_bcp(cache, index, 1, index_size); - - const Register flags = cache; - __ movl(flags, Address(cache, index, Address::times_ptr, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset())); - __ andl(flags, ConstantPoolCacheEntry::parameter_size_mask); - __ lea(rsp, Address(rsp, flags, Interpreter::stackElementScale())); + if (index_size == sizeof(u4)) { + __ load_resolved_indy_entry(cache, index); + __ load_unsigned_short(cache, Address(cache, in_bytes(ResolvedIndyEntry::num_parameters_offset()))); + __ lea(rsp, Address(rsp, cache, Interpreter::stackElementScale())); + } else { + __ get_cache_and_index_at_bcp(cache, index, 1, index_size); + Register flags = cache; + __ movl(flags, Address(cache, index, Address::times_ptr, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset())); + __ andl(flags, ConstantPoolCacheEntry::parameter_size_mask); + __ lea(rsp, Address(rsp, flags, Interpreter::stackElementScale())); + } const Register java_thread = NOT_LP64(rcx) LP64_ONLY(r15_thread); if (JvmtiExport::can_pop_frame()) { diff --git a/src/hotspot/cpu/x86/templateTable_x86.cpp b/src/hotspot/cpu/x86/templateTable_x86.cpp index 20d279c240a..a36551382bd 100644 --- a/src/hotspot/cpu/x86/templateTable_x86.cpp +++ b/src/hotspot/cpu/x86/templateTable_x86.cpp @@ -2721,13 +2721,81 @@ void TemplateTable::load_field_cp_cache_entry(Register obj, } } +void TemplateTable::load_invokedynamic_entry(Register method) { + // setup registers + const Register appendix = rax; + const Register cache = rcx; + const Register index = rdx; + assert_different_registers(method, appendix, cache, index); + + __ save_bcp(); + + Label resolved; + + __ load_resolved_indy_entry(cache, index); + __ movptr(method, Address(cache, in_bytes(ResolvedIndyEntry::method_offset()))); + + // Compare the method to zero + __ testptr(method, method); + __ jcc(Assembler::notZero, resolved); + + Bytecodes::Code code = bytecode(); + + // Call to the interpreter runtime to resolve invokedynamic + address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_from_cache); + __ movl(method, code); // this is essentially Bytecodes::_invokedynamic + __ call_VM(noreg, entry, method); + // Update registers with resolved info + __ load_resolved_indy_entry(cache, index); + __ movptr(method, Address(cache, in_bytes(ResolvedIndyEntry::method_offset()))); + +#ifdef ASSERT + __ testptr(method, method); + __ jcc(Assembler::notZero, resolved); + __ stop("Should be resolved by now"); +#endif // ASSERT + __ bind(resolved); + + Label L_no_push; + // Check if there is an appendix + __ load_unsigned_byte(index, Address(cache, in_bytes(ResolvedIndyEntry::flags_offset()))); + __ testl(index, (1 << ResolvedIndyEntry::has_appendix_shift)); + __ jcc(Assembler::zero, L_no_push); + + // Get appendix + __ load_unsigned_short(index, Address(cache, in_bytes(ResolvedIndyEntry::resolved_references_index_offset()))); + // Push the appendix as a trailing parameter + // since the parameter_size includes it. + __ load_resolved_reference_at_index(appendix, index); + __ verify_oop(appendix); + __ push(appendix); // push appendix (MethodType, CallSite, etc.) + __ bind(L_no_push); + + // compute return type + __ load_unsigned_byte(index, Address(cache, in_bytes(ResolvedIndyEntry::result_type_offset()))); + // load return address + { + const address table_addr = (address) Interpreter::invoke_return_entry_table_for(code); + ExternalAddress table(table_addr); +#ifdef _LP64 + __ lea(rscratch1, table); + __ movptr(index, Address(rscratch1, index, Address::times_ptr)); +#else + __ movptr(index, ArrayAddress(table, Address(noreg, index, Address::times_ptr))); +#endif // _LP64 + } + + // push return address + __ push(index); +} + void TemplateTable::load_invoke_cp_cache_entry(int byte_no, Register method, Register itable_index, Register flags, bool is_invokevirtual, bool is_invokevfinal, /*unused*/ - bool is_invokedynamic) { + bool is_invokedynamic /*unused*/) { // setup registers const Register cache = rcx; const Register index = rdx; @@ -2743,7 +2811,7 @@ void TemplateTable::load_invoke_cp_cache_entry(int byte_no, const int index_offset = in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::f2_offset()); - size_t index_size = (is_invokedynamic ? sizeof(u4) : sizeof(u2)); + size_t index_size = sizeof(u2); resolve_cache_and_index(byte_no, cache, index, index_size); __ load_resolved_method_at_index(byte_no, method, cache, index); @@ -3570,7 +3638,7 @@ void TemplateTable::prepare_invoke(int byte_no, load_invoke_cp_cache_entry(byte_no, method, index, flags, is_invokevirtual, false, is_invokedynamic); // maybe push appendix to arguments (just before return address) - if (is_invokedynamic || is_invokehandle) { + if (is_invokehandle) { Label L_no_push; __ testl(flags, (1 << ConstantPoolCacheEntry::has_appendix_shift)); __ jcc(Assembler::zero, L_no_push); @@ -3893,8 +3961,7 @@ void TemplateTable::invokedynamic(int byte_no) { const Register rbx_method = rbx; const Register rax_callsite = rax; - prepare_invoke(byte_no, rbx_method, rax_callsite); - + load_invokedynamic_entry(rbx_method); // rax: CallSite object (from cpool->resolved_references[f1]) // rbx: MH.linkToCallSite method (from f2) diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp index 4efb95fff80..bfe83c019be 100644 --- a/src/hotspot/share/c1/c1_Runtime1.cpp +++ b/src/hotspot/share/c1/c1_Runtime1.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2023, 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 @@ -1062,9 +1062,8 @@ JRT_ENTRY(void, Runtime1::patch_code(JavaThread* current, Runtime1::StubID stub_ break; } case Bytecodes::_invokedynamic: { - ConstantPoolCacheEntry* cpce = pool->invokedynamic_cp_cache_entry_at(index); - cpce->set_dynamic_call(pool, info); - appendix = Handle(current, cpce->appendix_if_resolved(pool)); // just in case somebody already resolved the entry + int indy_index = pool->decode_invokedynamic_index(index); + appendix = Handle(current, pool->cache()->set_dynamic_call(info, indy_index)); break; } default: fatal("unexpected bytecode for load_appendix_patching_id"); diff --git a/src/hotspot/share/cds/classListParser.cpp b/src/hotspot/share/cds/classListParser.cpp index 753fe7507ea..a1e1a413106 100644 --- a/src/hotspot/share/cds/classListParser.cpp +++ b/src/hotspot/share/cds/classListParser.cpp @@ -570,31 +570,31 @@ void ClassListParser::resolve_indy_impl(Symbol* class_name_symbol, TRAPS) { ConstantPool* cp = ik->constants(); ConstantPoolCache* cpcache = cp->cache(); bool found = false; - for (int cpcindex = 0; cpcindex < cpcache->length(); cpcindex ++) { - int indy_index = ConstantPool::encode_invokedynamic_index(cpcindex); - ConstantPoolCacheEntry* cpce = cpcache->entry_at(cpcindex); - int pool_index = cpce->constant_pool_index(); + for (int indy_index = 0; indy_index < cpcache->resolved_indy_entries_length(); indy_index++) { + int pool_index = cpcache->resolved_indy_entry_at(indy_index)->constant_pool_index(); constantPoolHandle pool(THREAD, cp); - if (pool->tag_at(pool_index).is_invoke_dynamic()) { - BootstrapInfo bootstrap_specifier(pool, pool_index, indy_index); - Handle bsm = bootstrap_specifier.resolve_bsm(CHECK); - if (!SystemDictionaryShared::is_supported_invokedynamic(&bootstrap_specifier)) { - log_debug(cds, lambda)("is_supported_invokedynamic check failed for cp_index %d", pool_index); - continue; - } - bool matched = is_matching_cp_entry(pool, pool_index, CHECK); - if (matched) { - found = true; - CallInfo info; - bool is_done = bootstrap_specifier.resolve_previously_linked_invokedynamic(info, CHECK); - if (!is_done) { - // resolve it - Handle recv; - LinkResolver::resolve_invoke(info, recv, pool, indy_index, Bytecodes::_invokedynamic, CHECK); - break; - } - cpce->set_dynamic_call(pool, info); + BootstrapInfo bootstrap_specifier(pool, pool_index, indy_index); + Handle bsm = bootstrap_specifier.resolve_bsm(CHECK); + if (!SystemDictionaryShared::is_supported_invokedynamic(&bootstrap_specifier)) { + log_debug(cds, lambda)("is_supported_invokedynamic check failed for cp_index %d", pool_index); + continue; + } + bool matched = is_matching_cp_entry(pool, pool_index, CHECK); + if (matched) { + found = true; + CallInfo info; + bool is_done = bootstrap_specifier.resolve_previously_linked_invokedynamic(info, CHECK); + if (!is_done) { + // resolve it + Handle recv; + LinkResolver::resolve_invoke(info, + recv, + pool, + ConstantPool::encode_invokedynamic_index(indy_index), + Bytecodes::_invokedynamic, CHECK); + break; } + cpcache->set_dynamic_call(info, indy_index); } } if (!found) { diff --git a/src/hotspot/share/ci/ciEnv.cpp b/src/hotspot/share/ci/ciEnv.cpp index df5c790d748..c01b5d0165d 100644 --- a/src/hotspot/share/ci/ciEnv.cpp +++ b/src/hotspot/share/ci/ciEnv.cpp @@ -872,8 +872,6 @@ ciMethod* ciEnv::get_method_by_index_impl(const constantPoolHandle& cpool, assert(cpool.not_null(), "need constant pool"); assert(accessor != nullptr, "need origin of access"); if (bc == Bytecodes::_invokedynamic) { - ConstantPoolCacheEntry* cpce = cpool->invokedynamic_cp_cache_entry_at(index); - bool is_resolved = !cpce->is_f1_null(); // FIXME: code generation could allow for null (unlinked) call site // The call site could be made patchable as follows: // Load the appendix argument from the constant pool. @@ -881,11 +879,12 @@ ciMethod* ciEnv::get_method_by_index_impl(const constantPoolHandle& cpool, // Jump through a patchable call site, which is initially a deopt routine. // Patch the call site to the nmethod entry point of the static compiled lambda form. // As with other two-component call sites, both values must be independently verified. - - if (is_resolved) { - // Get the invoker Method* from the constant pool. - // (The appendix argument, if any, will be noted in the method's signature.) - Method* adapter = cpce->f1_as_method(); + int indy_index = cpool->decode_invokedynamic_index(index); + assert (indy_index >= 0, "should be"); + assert(indy_index < cpool->cache()->resolved_indy_entries_length(), "impossible"); + Method* adapter = cpool->resolved_indy_entry_at(indy_index)->method(); + // Resolved if the adapter is non null. + if (adapter != nullptr) { return get_method(adapter); } @@ -1518,20 +1517,21 @@ void ciEnv::record_call_site_method(Thread* thread, Method* adapter) { // Process an invokedynamic call site and record any dynamic locations. void ciEnv::process_invokedynamic(const constantPoolHandle &cp, int indy_index, JavaThread* thread) { - ConstantPoolCacheEntry* cp_cache_entry = cp->invokedynamic_cp_cache_entry_at(indy_index); - if (cp_cache_entry->is_resolved(Bytecodes::_invokedynamic)) { + int index = cp->decode_invokedynamic_index(indy_index); + ResolvedIndyEntry* indy_info = cp->resolved_indy_entry_at(index); + if (indy_info->method() != nullptr) { // process the adapter - Method* adapter = cp_cache_entry->f1_as_method(); + Method* adapter = indy_info->method(); record_call_site_method(thread, adapter); // process the appendix - oop appendix = cp_cache_entry->appendix_if_resolved(cp); + oop appendix = cp->resolved_reference_from_indy(index); { RecordLocation fp(this, ""); record_call_site_obj(thread, appendix); } // process the BSM - int pool_index = cp_cache_entry->constant_pool_index(); - BootstrapInfo bootstrap_specifier(cp, pool_index, indy_index); + int pool_index = indy_info->constant_pool_index(); + BootstrapInfo bootstrap_specifier(cp, pool_index, index); oop bsm = cp->resolve_possibly_cached_constant_at(bootstrap_specifier.bsm_index(), thread); { RecordLocation fp(this, ""); diff --git a/src/hotspot/share/ci/ciReplay.cpp b/src/hotspot/share/ci/ciReplay.cpp index ad48903294c..771adc602a2 100644 --- a/src/hotspot/share/ci/ciReplay.cpp +++ b/src/hotspot/share/ci/ciReplay.cpp @@ -410,9 +410,22 @@ class CompileReplay : public StackObj { CallInfo callInfo; Bytecodes::Code bc = bytecode.invoke_code(); LinkResolver::resolve_invoke(callInfo, Handle(), cp, index, bc, CHECK_NULL); + + // ResolvedIndyEntry and ConstantPoolCacheEntry must currently coexist. + // To address this, the variables below contain the values that *might* + // be used to avoid multiple blocks of similar code. When CPCE is obsoleted + // these can be removed + oop appendix = nullptr; + Method* adapter_method = nullptr; + int pool_index = 0; + if (bytecode.is_invokedynamic()) { - cp_cache_entry = cp->invokedynamic_cp_cache_entry_at(index); - cp_cache_entry->set_dynamic_call(cp, callInfo); + index = cp->decode_invokedynamic_index(index); + cp->cache()->set_dynamic_call(callInfo, index); + + appendix = cp->resolved_reference_from_indy(index); + adapter_method = cp->resolved_indy_entry_at(index)->method(); + pool_index = cp->resolved_indy_entry_at(index)->constant_pool_index(); } else if (bytecode.is_invokehandle()) { #ifdef ASSERT Klass* holder = cp->klass_ref_at(index, CHECK_NULL); @@ -421,26 +434,29 @@ class CompileReplay : public StackObj { #endif cp_cache_entry = cp->cache()->entry_at(cp->decode_cpcache_index(index)); cp_cache_entry->set_method_handle(cp, callInfo); + + appendix = cp_cache_entry->appendix_if_resolved(cp); + adapter_method = cp_cache_entry->f1_as_method(); + pool_index = cp_cache_entry->constant_pool_index(); } else { report_error("no dynamic invoke found"); return nullptr; } char* dyno_ref = parse_string(); if (strcmp(dyno_ref, "") == 0) { - obj = cp_cache_entry->appendix_if_resolved(cp); + obj = appendix; } else if (strcmp(dyno_ref, "") == 0) { if (!parse_terminator()) { report_error("no dynamic invoke found"); return nullptr; } - Method* adapter = cp_cache_entry->f1_as_method(); + Method* adapter = adapter_method; if (adapter == nullptr) { report_error("no adapter found"); return nullptr; } return adapter->method_holder(); } else if (strcmp(dyno_ref, "") == 0) { - int pool_index = cp_cache_entry->constant_pool_index(); BootstrapInfo bootstrap_specifier(cp, pool_index, index); obj = cp->resolve_possibly_cached_constant_at(bootstrap_specifier.bsm_index(), CHECK_NULL); } else { diff --git a/src/hotspot/share/interpreter/abstractInterpreter.cpp b/src/hotspot/share/interpreter/abstractInterpreter.cpp index 03ee3718fbb..2883630c37d 100644 --- a/src/hotspot/share/interpreter/abstractInterpreter.cpp +++ b/src/hotspot/share/interpreter/abstractInterpreter.cpp @@ -261,8 +261,8 @@ bool AbstractInterpreter::is_not_reached(const methodHandle& method, int bci) { switch (code) { case Bytecodes::_invokedynamic: { assert(invoke_bc.has_index_u4(code), "sanity"); - int method_index = invoke_bc.get_index_u4(code); - return cpool->invokedynamic_cp_cache_entry_at(method_index)->is_f1_null(); + int method_index = cpool->decode_invokedynamic_index(invoke_bc.get_index_u4(code)); + return cpool->resolved_indy_entry_at(method_index)->is_resolved(); } case Bytecodes::_invokevirtual: // fall-through case Bytecodes::_invokeinterface: // fall-through @@ -394,7 +394,8 @@ address AbstractInterpreter::deopt_continue_after_entry(Method* method, address // (NOT needed for the old calling convention) if (!is_top_frame) { int index = Bytes::get_native_u4(bcp+1); - method->constants()->invokedynamic_cp_cache_entry_at(index)->set_parameter_size(callee_parameters); + int indy_index = method->constants()->decode_invokedynamic_index(index); + method->constants()->resolved_indy_entry_at(indy_index)->set_num_parameters(callee_parameters); } break; } diff --git a/src/hotspot/share/interpreter/bootstrapInfo.cpp b/src/hotspot/share/interpreter/bootstrapInfo.cpp index cd8063d10f3..cb41bb4ac63 100644 --- a/src/hotspot/share/interpreter/bootstrapInfo.cpp +++ b/src/hotspot/share/interpreter/bootstrapInfo.cpp @@ -55,7 +55,7 @@ BootstrapInfo::BootstrapInfo(const constantPoolHandle& pool, int bss_index, int { _is_resolved = false; assert(pool->tag_at(bss_index).has_bootstrap(), ""); - assert(indy_index == -1 || pool->invokedynamic_bootstrap_ref_index_at(indy_index) == bss_index, "invalid bootstrap specifier index"); + assert(indy_index == -1 || pool->resolved_indy_entry_at(indy_index)->constant_pool_index() == bss_index, "invalid bootstrap specifier index"); } // If there is evidence this call site was already linked, set the @@ -63,16 +63,17 @@ BootstrapInfo::BootstrapInfo(const constantPoolHandle& pool, int bss_index, int // Return true if either action is taken, else false. bool BootstrapInfo::resolve_previously_linked_invokedynamic(CallInfo& result, TRAPS) { assert(_indy_index != -1, ""); - ConstantPoolCacheEntry* cpce = invokedynamic_cp_cache_entry(); - if (!cpce->is_f1_null()) { - methodHandle method( THREAD, cpce->f1_as_method()); - Handle appendix( THREAD, cpce->appendix_if_resolved(_pool)); + // Check if method is not null + ResolvedIndyEntry* indy_entry = _pool->resolved_indy_entry_at(_indy_index); + if (indy_entry->method() != nullptr) { + methodHandle method(THREAD, indy_entry->method()); + Handle appendix(THREAD, _pool->resolved_reference_from_indy(_indy_index)); result.set_handle(vmClasses::MethodHandle_klass(), method, appendix, THREAD); Exceptions::wrap_dynamic_exception(/* is_indy */ true, CHECK_false); return true; - } else if (cpce->indy_resolution_failed()) { - int encoded_index = ResolutionErrorTable::encode_cpcache_index(_indy_index); - ConstantPool::throw_resolution_error(_pool, encoded_index, CHECK_false); + } else if (indy_entry->resolution_failed()) { + int encoded_index = ResolutionErrorTable::encode_cpcache_index(ConstantPool::encode_invokedynamic_index(_indy_index)); + ConstantPool::throw_resolution_error(_pool, encoded_index, CHECK_false); // Doesn't necessarily need to be resolved yet return true; } else { return false; @@ -210,12 +211,11 @@ void BootstrapInfo::resolve_args(TRAPS) { bool BootstrapInfo::save_and_throw_indy_exc(TRAPS) { assert(HAS_PENDING_EXCEPTION, ""); assert(_indy_index != -1, ""); - ConstantPoolCacheEntry* cpce = invokedynamic_cp_cache_entry(); - int encoded_index = ResolutionErrorTable::encode_cpcache_index(_indy_index); - bool recorded_res_status = cpce->save_and_throw_indy_exc(_pool, _bss_index, - encoded_index, - pool()->tag_at(_bss_index), - CHECK_false); + assert(_indy_index >= 0, "Indy index must be decoded by now"); + bool recorded_res_status = _pool->cache()->save_and_throw_indy_exc(_pool, _bss_index, + _indy_index, + pool()->tag_at(_bss_index), + CHECK_false); return recorded_res_status; } @@ -229,10 +229,11 @@ void BootstrapInfo::print_msg_on(outputStream* st, const char* msg) { char what[20]; st = st ? st : tty; - if (_indy_index != -1) - os::snprintf_checked(what, sizeof(what), "indy#%d", decode_indy_index()); - else + if (_indy_index > -1) { + os::snprintf_checked(what, sizeof(what), "indy#%d", _indy_index); + } else { os::snprintf_checked(what, sizeof(what), "condy"); + } bool have_msg = (msg != nullptr && strlen(msg) > 0); st->print_cr("%s%sBootstrap in %s %s@CP[%d] %s:%s%s BSMS[%d] BSM@CP[%d]%s argc=%d%s", (have_msg ? msg : ""), (have_msg ? " " : ""), diff --git a/src/hotspot/share/interpreter/bootstrapInfo.hpp b/src/hotspot/share/interpreter/bootstrapInfo.hpp index a021198495d..d73d4389329 100644 --- a/src/hotspot/share/interpreter/bootstrapInfo.hpp +++ b/src/hotspot/share/interpreter/bootstrapInfo.hpp @@ -82,12 +82,6 @@ class BootstrapInfo : public StackObj { //int argc() is eagerly cached in _argc int arg_index(int i) const { return _pool->bootstrap_argument_index_at(_bss_index, i); } - // CP cache entry for call site (indy only) - ConstantPoolCacheEntry* invokedynamic_cp_cache_entry() const { - assert(is_method_call(), ""); - return _pool->invokedynamic_cp_cache_entry_at(_indy_index); - } - // If there is evidence this call site was already linked, set the // existing linkage data into result, or throw previous exception. // Return true if either action is taken, else false. diff --git a/src/hotspot/share/interpreter/bytecode.cpp b/src/hotspot/share/interpreter/bytecode.cpp index c6c4a417abd..ef02022f638 100644 --- a/src/hotspot/share/interpreter/bytecode.cpp +++ b/src/hotspot/share/interpreter/bytecode.cpp @@ -28,6 +28,7 @@ #include "oops/constantPool.hpp" #include "oops/cpCache.inline.hpp" #include "oops/oop.inline.hpp" +#include "oops/resolvedIndyEntry.hpp" #include "runtime/handles.inline.hpp" #include "runtime/safepoint.hpp" #include "runtime/signature.hpp" @@ -159,7 +160,7 @@ Method* Bytecode_invoke::static_target(TRAPS) { int Bytecode_member_ref::index() const { // Note: Rewriter::rewrite changes the Java_u2 of an invokedynamic to a native_u4, - // at the same time it allocates per-call-site CP cache entries. + // at the same time it allocates per-call-site resolved indy entries. Bytecodes::Code rawc = code(); if (has_index_u4(rawc)) return get_index_u4(rawc); @@ -168,14 +169,25 @@ int Bytecode_member_ref::index() const { } int Bytecode_member_ref::pool_index() const { - return cpcache_entry()->constant_pool_index(); + if (invoke_code() == Bytecodes::_invokedynamic) { + return resolved_indy_entry()->constant_pool_index(); + } else { + return cpcache_entry()->constant_pool_index(); + } } ConstantPoolCacheEntry* Bytecode_member_ref::cpcache_entry() const { int index = this->index(); + assert(invoke_code() != Bytecodes::_invokedynamic, "should not call this"); return cpcache()->entry_at(ConstantPool::decode_cpcache_index(index, true)); } +ResolvedIndyEntry* Bytecode_member_ref::resolved_indy_entry() const { + int index = this->index(); + assert(invoke_code() == Bytecodes::_invokedynamic, "should not call this"); + return cpcache()->resolved_indy_entry_at(ConstantPool::decode_invokedynamic_index(index)); +} + // Implementation of Bytecode_field void Bytecode_field::verify() const { diff --git a/src/hotspot/share/interpreter/bytecode.hpp b/src/hotspot/share/interpreter/bytecode.hpp index e9361803a35..d9943aa7412 100644 --- a/src/hotspot/share/interpreter/bytecode.hpp +++ b/src/hotspot/share/interpreter/bytecode.hpp @@ -188,6 +188,7 @@ class Bytecode_member_ref: public Bytecode { ConstantPool* constants() const { return _method->constants(); } ConstantPoolCache* cpcache() const { return _method->constants()->cache(); } ConstantPoolCacheEntry* cpcache_entry() const; + ResolvedIndyEntry* resolved_indy_entry() const; public: int index() const; // cache index (loaded from instruction) diff --git a/src/hotspot/share/interpreter/bytecode.inline.hpp b/src/hotspot/share/interpreter/bytecode.inline.hpp index 1181a8eed6e..9e38428c160 100644 --- a/src/hotspot/share/interpreter/bytecode.inline.hpp +++ b/src/hotspot/share/interpreter/bytecode.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2023, 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 @@ -29,6 +29,12 @@ #include "oops/cpCache.inline.hpp" -inline bool Bytecode_invoke::has_appendix() { return cpcache_entry()->has_appendix(); } +inline bool Bytecode_invoke::has_appendix() { + if (invoke_code() == Bytecodes::_invokedynamic) { + return resolved_indy_entry()->has_appendix(); + } else { + return cpcache_entry()->has_appendix(); + } +} #endif // SHARE_INTERPRETER_BYTECODE_INLINE_HPP diff --git a/src/hotspot/share/interpreter/bytecodeTracer.cpp b/src/hotspot/share/interpreter/bytecodeTracer.cpp index b167ba7050e..1bf12f5483e 100644 --- a/src/hotspot/share/interpreter/bytecodeTracer.cpp +++ b/src/hotspot/share/interpreter/bytecodeTracer.cpp @@ -425,9 +425,10 @@ void BytecodePrinter::print_dynamic(int orig_i, int bsm_cpindex, constantTag tag } st->print_cr(" }"); if (tag.is_invoke_dynamic()) { - int indy_index = orig_i; - int cpc_index = constants->invokedynamic_cp_cache_index(indy_index); - print_cpcache_entry(cpc_index, st); + int indy_index = constants->decode_invokedynamic_index(orig_i); + ResolvedIndyEntry* indy_entry = constants->resolved_indy_entry_at(indy_index); + st->print(" ResolvedIndyEntry: "); + indy_entry->print_on(st); } else { // TODO: print info for tag.is_dynamic_constant() } diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp index b1dd60a6ae8..f9a795c5235 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -947,8 +947,7 @@ void InterpreterRuntime::resolve_invokedynamic(JavaThread* current) { index, bytecode, CHECK); } // end JvmtiHideSingleStepping - ConstantPoolCacheEntry* cp_cache_entry = pool->invokedynamic_cp_cache_entry_at(index); - cp_cache_entry->set_dynamic_call(pool, info); + pool->cache()->set_dynamic_call(info, pool->decode_invokedynamic_index(index)); } // This function is the interface to the assembly code. It returns the resolved diff --git a/src/hotspot/share/interpreter/linkResolver.cpp b/src/hotspot/share/interpreter/linkResolver.cpp index 80153639c9b..40e2f127027 100644 --- a/src/hotspot/share/interpreter/linkResolver.cpp +++ b/src/hotspot/share/interpreter/linkResolver.cpp @@ -1763,11 +1763,11 @@ void LinkResolver::resolve_handle_call(CallInfo& result, } void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHandle& pool, int indy_index, TRAPS) { - ConstantPoolCacheEntry* cpce = pool->invokedynamic_cp_cache_entry_at(indy_index); - int pool_index = cpce->constant_pool_index(); + int index = pool->decode_invokedynamic_index(indy_index); + int pool_index = pool->resolved_indy_entry_at(index)->constant_pool_index(); // Resolve the bootstrap specifier (BSM + optional arguments). - BootstrapInfo bootstrap_specifier(pool, pool_index, indy_index); + BootstrapInfo bootstrap_specifier(pool, pool_index, index); // Check if CallSite has been bound already or failed already, and short circuit: { @@ -1779,8 +1779,8 @@ void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHan // reference to a method handle which will be the bootstrap method for a dynamic // call site. If resolution for the java.lang.invoke.MethodHandle for the bootstrap // method fails, then a MethodHandleInError is stored at the corresponding bootstrap - // method's CP index for the CONSTANT_MethodHandle_info. So, there is no need to - // set the indy_rf flag since any subsequent invokedynamic instruction which shares + // method's CP index for the CONSTANT_MethodHandle_info. + // Any subsequent invokedynamic instruction which shares // this bootstrap method will encounter the resolution of MethodHandleInError. resolve_dynamic_call(result, bootstrap_specifier, CHECK); @@ -1793,10 +1793,10 @@ void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHan // The returned linkage result is provisional up to the moment // the interpreter or runtime performs a serialized check of - // the relevant CPCE::f1 field. This is done by the caller - // of this method, via CPCE::set_dynamic_call, which uses + // the relevant ResolvedIndyEntry::method field. This is done by the caller + // of this method, via CPC::set_dynamic_call, which uses // a lock to do the final serialization of updates - // to CPCE state, including f1. + // to ResolvedIndyEntry state, including method. // Log dynamic info to CDS classlist. ArchiveUtils::log_to_classlist(&bootstrap_specifier, CHECK); @@ -1827,15 +1827,15 @@ void LinkResolver::resolve_dynamic_call(CallInfo& result, // instance of LinkageError (or a subclass), then subsequent attempts to // resolve the reference always fail with the same error that was thrown // as a result of the initial resolution attempt. - bool recorded_res_status = bootstrap_specifier.save_and_throw_indy_exc(CHECK); - if (!recorded_res_status) { - // Another thread got here just before we did. So, either use the method - // that it resolved or throw the LinkageError exception that it threw. - bool is_done = bootstrap_specifier.resolve_previously_linked_invokedynamic(result, CHECK); - if (is_done) return; - } - assert(bootstrap_specifier.invokedynamic_cp_cache_entry()->indy_resolution_failed(), - "Resolution failure flag wasn't set"); + bool recorded_res_status = bootstrap_specifier.save_and_throw_indy_exc(CHECK); + if (!recorded_res_status) { + // Another thread got here just before we did. So, either use the method + // that it resolved or throw the LinkageError exception that it threw. + bool is_done = bootstrap_specifier.resolve_previously_linked_invokedynamic(result, CHECK); + if (is_done) return; + } + assert(bootstrap_specifier.pool()->resolved_indy_entry_at(bootstrap_specifier.indy_index())->resolution_failed(), + "Resolution should have failed"); } bootstrap_specifier.resolve_newly_linked_invokedynamic(result, CHECK); diff --git a/src/hotspot/share/interpreter/rewriter.cpp b/src/hotspot/share/interpreter/rewriter.cpp index 5a99fc86e8b..40734daff0b 100644 --- a/src/hotspot/share/interpreter/rewriter.cpp +++ b/src/hotspot/share/interpreter/rewriter.cpp @@ -30,7 +30,6 @@ #include "interpreter/rewriter.hpp" #include "memory/metadataFactory.hpp" #include "memory/resourceArea.hpp" -#include "oops/constantPool.hpp" #include "oops/generateOopMap.hpp" #include "prims/methodHandles.hpp" #include "runtime/arguments.hpp" @@ -99,8 +98,7 @@ void Rewriter::make_constant_pool_cache(TRAPS) { ClassLoaderData* loader_data = _pool->pool_holder()->class_loader_data(); ConstantPoolCache* cache = ConstantPoolCache::allocate(loader_data, _cp_cache_map, - _invokedynamic_cp_cache_map, - _invokedynamic_references_map, CHECK); + _invokedynamic_references_map, _initialized_indy_entries, CHECK); // initialize object cache in constant pool _pool->set_cache(cache); @@ -269,30 +267,29 @@ void Rewriter::rewrite_invokedynamic(address bcp, int offset, bool reverse) { assert(p[-1] == Bytecodes::_invokedynamic, "not invokedynamic bytecode"); if (!reverse) { int cp_index = Bytes::get_Java_u2(p); - int cache_index = add_invokedynamic_cp_cache_entry(cp_index); - int resolved_index = add_invokedynamic_resolved_references_entry(cp_index, cache_index); - // Replace the trailing four bytes with a CPC index for the dynamic - // call site. Unlike other CPC entries, there is one per bytecode, - // not just one per distinct CP entry. In other words, the - // CPC-to-CP relation is many-to-one for invokedynamic entries. + int resolved_index = add_invokedynamic_resolved_references_entry(cp_index, -1); // Indy no longer has a CPCE + // Replace the trailing four bytes with an index to the array of + // indy resolution information in the CPC. There is one entry for + // each bytecode, even if they make the same call. In other words, + // the CPC-to-CP relation is many-to-one for invokedynamic entries. // This means we must use a larger index size than u2 to address // all these entries. That is the main reason invokedynamic // must have a five-byte instruction format. (Of course, other JVM // implementations can use the bytes for other purposes.) // Note: We use native_u4 format exclusively for 4-byte indexes. - Bytes::put_native_u4(p, ConstantPool::encode_invokedynamic_index(cache_index)); - // add the bcp in case we need to patch this bytecode if we also find a - // invokespecial/InterfaceMethodref in the bytecode stream - _patch_invokedynamic_bcps->push(p); - _patch_invokedynamic_refs->push(resolved_index); + Bytes::put_native_u4(p, ConstantPool::encode_invokedynamic_index(_invokedynamic_index)); + _invokedynamic_index++; + + // Collect invokedynamic information before creating ResolvedInvokeDynamicInfo array + _initialized_indy_entries.push(ResolvedIndyEntry((u2)resolved_index, (u2)cp_index)); } else { + // Should do nothing since we are not patching this bytecode int cache_index = ConstantPool::decode_invokedynamic_index( Bytes::get_native_u4(p)); // We will reverse the bytecode rewriting _after_ adjusting them. // Adjust the cache index by offset to the invokedynamic entries in the // cpCache plus the delta if the invokedynamic bytecodes were adjusted. - int adjustment = cp_cache_delta() + _first_iteration_cp_cache_limit; - int cp_index = invokedynamic_cp_cache_entry_pool_index(cache_index - adjustment); + int cp_index = _initialized_indy_entries.at(cache_index).constant_pool_index(); assert(_pool->tag_at(cp_index).is_invoke_dynamic(), "wrong index"); // zero out 4 bytes Bytes::put_Java_u4(p, 0); @@ -300,32 +297,6 @@ void Rewriter::rewrite_invokedynamic(address bcp, int offset, bool reverse) { } } -void Rewriter::patch_invokedynamic_bytecodes() { - // If the end of the cp_cache is the same as after initializing with the - // cpool, nothing needs to be done. Invokedynamic bytecodes are at the - // correct offsets. ie. no invokespecials added - int delta = cp_cache_delta(); - if (delta > 0) { - int length = _patch_invokedynamic_bcps->length(); - assert(length == _patch_invokedynamic_refs->length(), - "lengths should match"); - for (int i = 0; i < length; i++) { - address p = _patch_invokedynamic_bcps->at(i); - int cache_index = ConstantPool::decode_invokedynamic_index( - Bytes::get_native_u4(p)); - Bytes::put_native_u4(p, ConstantPool::encode_invokedynamic_index(cache_index + delta)); - - // invokedynamic resolved references map also points to cp cache and must - // add delta to each. - int resolved_index = _patch_invokedynamic_refs->at(i); - assert(_invokedynamic_references_map.at(resolved_index) == cache_index, - "should be the same index"); - _invokedynamic_references_map.at_put(resolved_index, cache_index + delta); - } - } -} - - // Rewrite some ldc bytecodes to _fast_aldc void Rewriter::maybe_rewrite_ldc(address bcp, int offset, bool is_wide, bool reverse) { @@ -569,10 +540,6 @@ void Rewriter::rewrite_bytecodes(TRAPS) { return; } } - - // May have to fix invokedynamic bytecodes if invokestatic/InterfaceMethodref - // entries had to be added. - patch_invokedynamic_bytecodes(); } void Rewriter::rewrite(InstanceKlass* klass, TRAPS) { @@ -597,7 +564,7 @@ Rewriter::Rewriter(InstanceKlass* klass, const constantPoolHandle& cpool, Array< _resolved_references_map(cpool->length() / 2), _invokedynamic_references_map(cpool->length() / 2), _method_handle_invokers(cpool->length()), - _invokedynamic_cp_cache_map(cpool->length() / 4) + _invokedynamic_index(0) { // Rewrite bytecodes - exception here exits. diff --git a/src/hotspot/share/interpreter/rewriter.hpp b/src/hotspot/share/interpreter/rewriter.hpp index 928e15ac149..d5b4f7c3dc4 100644 --- a/src/hotspot/share/interpreter/rewriter.hpp +++ b/src/hotspot/share/interpreter/rewriter.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2023, 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 @@ -26,6 +26,8 @@ #define SHARE_INTERPRETER_REWRITER_HPP #include "memory/allocation.hpp" +#include "oops/constantPool.hpp" +#include "oops/resolvedIndyEntry.hpp" #include "utilities/growableArray.hpp" // The Rewriter adds caches to the constant pool and rewrites bytecode indices @@ -44,17 +46,11 @@ class Rewriter: public StackObj { GrowableArray _invokedynamic_references_map; // for invokedynamic resolved refs GrowableArray _method_handle_invokers; int _resolved_reference_limit; + int _invokedynamic_index; - // For mapping invokedynamic bytecodes, which are discovered during method - // scanning. The invokedynamic entries are added at the end of the cpCache. - // If there are any invokespecial/InterfaceMethodref special case bytecodes, - // these entries are added before invokedynamic entries so that the - // invokespecial bytecode 16 bit index doesn't overflow. - GrowableArray _invokedynamic_cp_cache_map; - - // For patching. - GrowableArray
* _patch_invokedynamic_bcps; - GrowableArray* _patch_invokedynamic_refs; + // For collecting information about invokedynamic bytecodes before resolution + // With this, we can know how many indy calls there are and resolve them later + GrowableArray _initialized_indy_entries; void init_maps(int length) { _cp_map.trunc_to(0); @@ -70,11 +66,6 @@ class Rewriter: public StackObj { _invokedynamic_references_map.trunc_to(0); _resolved_reference_limit = -1; _first_iteration_cp_cache_limit = -1; - - // invokedynamic specific fields - _invokedynamic_cp_cache_map.trunc_to(0); - _patch_invokedynamic_bcps = new GrowableArray
(length / 4); - _patch_invokedynamic_refs = new GrowableArray(length / 4); } int _first_iteration_cp_cache_limit; @@ -111,22 +102,6 @@ class Rewriter: public StackObj { return cache_index; } - int add_invokedynamic_cp_cache_entry(int cp_index) { - assert(_pool->tag_at(cp_index).value() == JVM_CONSTANT_InvokeDynamic, "use non-indy version"); - assert(_first_iteration_cp_cache_limit >= 0, "add indy cache entries after first iteration"); - // add to the invokedynamic index map. - int cache_index = _invokedynamic_cp_cache_map.append(cp_index); - // do not update _cp_map, since the mapping is one-to-many - assert(invokedynamic_cp_cache_entry_pool_index(cache_index) == cp_index, ""); - // this index starts at one but in the bytecode it's appended to the end. - return cache_index + _first_iteration_cp_cache_limit; - } - - int invokedynamic_cp_cache_entry_pool_index(int cache_index) { - int cp_index = _invokedynamic_cp_cache_map.at(cache_index); - return cp_index; - } - // add a new CP cache entry beyond the normal cache for the special case of // invokespecial with InterfaceMethodref as cpool operand. int add_invokespecial_cp_cache_entry(int cp_index) { @@ -164,7 +139,9 @@ class Rewriter: public StackObj { assert(_resolved_reference_limit >= 0, "must add indy refs after first iteration"); int ref_index = _resolved_references_map.append(cp_index); // many-to-one assert(ref_index >= _resolved_reference_limit, ""); - _invokedynamic_references_map.at_put_grow(ref_index, cache_index, -1); + if (_pool->tag_at(cp_index).value() != JVM_CONSTANT_InvokeDynamic) { + _invokedynamic_references_map.at_put_grow(ref_index, cache_index, -1); + } return ref_index; } @@ -192,8 +169,6 @@ class Rewriter: public StackObj { void maybe_rewrite_ldc(address bcp, int offset, bool is_wide, bool reverse); void rewrite_invokespecial(address bcp, int offset, bool reverse, bool* invokespecial_error); - void patch_invokedynamic_bytecodes(); - // Do all the work. void rewrite_bytecodes(TRAPS); diff --git a/src/hotspot/share/interpreter/templateTable.hpp b/src/hotspot/share/interpreter/templateTable.hpp index 079f5e5fc1e..e8409b1dc0f 100644 --- a/src/hotspot/share/interpreter/templateTable.hpp +++ b/src/hotspot/share/interpreter/templateTable.hpp @@ -266,6 +266,7 @@ class TemplateTable: AllStatic { Register cache, // output for CP cache Register index, // output for CP index size_t index_size); // one of 1,2,4 + static void load_invokedynamic_entry(Register method); static void load_invoke_cp_cache_entry(int byte_no, Register method, Register itable_index, diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp index 0f79863646e..8ee8d9ffd5d 100644 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp +++ b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp @@ -2247,25 +2247,19 @@ run: } CASE(_invokedynamic): { - - u4 index = Bytes::get_native_u4(pc+1); - ConstantPoolCacheEntry* cache = cp->constant_pool()->invokedynamic_cp_cache_entry_at(index); - - // We are resolved if the resolved_references array contains a non-null object (CallSite, etc.) - // This kind of CP cache entry does not need to match the flags byte, because - // there is a 1-1 relation between bytecode type and CP entry type. - if (! cache->is_resolved((Bytecodes::Code) opcode)) { + u4 index = cp->constant_pool()->decode_invokedynamic_index(Bytes::get_native_u4(pc+1)); // index is originally negative + ResolvedIndyEntry* indy_info = cp->resolved_indy_entry_at(index); + if (!indy_info->is_resolved()) { CALL_VM(InterpreterRuntime::resolve_from_cache(THREAD, (Bytecodes::Code)opcode), handle_exception); - cache = cp->constant_pool()->invokedynamic_cp_cache_entry_at(index); + indy_info = cp->resolved_indy_entry_at(index); // get resolved entry } - - Method* method = cache->f1_as_method(); + Method* method = indy_info->method(); if (VerifyOops) method->verify(); - if (cache->has_appendix()) { + if (indy_info->has_appendix()) { constantPoolHandle cp(THREAD, METHOD->constants()); - SET_STACK_OBJECT(cache->appendix_if_resolved(cp), 0); + SET_STACK_OBJECT(cp->resolved_reference_from_indy(index), 0); MORE_STACK(1); } diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp index 784f8fb2991..07d27295e95 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp @@ -652,8 +652,15 @@ C2V_VMENTRY_NULL(jobjectArray, resolveBootstrapMethod, (JNIEnv* env, jobject, AR if (!(is_condy || is_indy)) { JVMCI_THROW_MSG_0(IllegalArgumentException, err_msg("Unexpected constant pool tag at index %d: %d", index, tag.value())); } + // Get the indy entry based on CP index + int indy_index = -1; + for (int i = 0; i < cp->resolved_indy_entries_length(); i++) { + if (cp->resolved_indy_entry_at(i)->constant_pool_index() == index) { + indy_index = i; + } + } // Resolve the bootstrap specifier, its name, type, and static arguments - BootstrapInfo bootstrap_specifier(cp, index); + BootstrapInfo bootstrap_specifier(cp, index, indy_index); Handle bsm = bootstrap_specifier.resolve_bsm(CHECK_NULL); // call java.lang.invoke.MethodHandle::asFixedArity() -> MethodHandle @@ -1482,8 +1489,7 @@ C2V_VMENTRY(void, resolveInvokeDynamicInPool, (JNIEnv* env, jobject, ARGUMENT_PA constantPoolHandle cp(THREAD, UNPACK_PAIR(ConstantPool, cp)); CallInfo callInfo; LinkResolver::resolve_invoke(callInfo, Handle(), cp, index, Bytecodes::_invokedynamic, CHECK); - ConstantPoolCacheEntry* cp_cache_entry = cp->invokedynamic_cp_cache_entry_at(index); - cp_cache_entry->set_dynamic_call(cp, callInfo); + cp->cache()->set_dynamic_call(callInfo, index); // Index already decoded C2V_END C2V_VMENTRY(void, resolveInvokeHandleInPool, (JNIEnv* env, jobject, ARGUMENT_PAIR(cp), jint index)) @@ -1532,8 +1538,10 @@ C2V_VMENTRY_0(jint, isResolvedInvokeHandleInPool, (JNIEnv* env, jobject, ARGUMEN return Bytecodes::_invokevirtual; } - if (cp_cache_entry->is_resolved(Bytecodes::_invokedynamic)) { - return Bytecodes::_invokedynamic; + if (cp->is_invokedynamic_index(index)) { + if (cp->resolved_indy_entry_at(cp->decode_cpcache_index(index))->is_resolved()) { + return Bytecodes::_invokedynamic; + } } return -1; C2V_END diff --git a/src/hotspot/share/jvmci/jvmciRuntime.cpp b/src/hotspot/share/jvmci/jvmciRuntime.cpp index 4325e0d3991..8e935fdbdb7 100644 --- a/src/hotspot/share/jvmci/jvmciRuntime.cpp +++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp @@ -1874,13 +1874,9 @@ Method* JVMCIRuntime::get_method_by_index_impl(const constantPoolHandle& cpool, int index, Bytecodes::Code bc, InstanceKlass* accessor) { if (bc == Bytecodes::_invokedynamic) { - ConstantPoolCacheEntry* cpce = cpool->invokedynamic_cp_cache_entry_at(index); - bool is_resolved = !cpce->is_f1_null(); - if (is_resolved) { - // Get the invoker Method* from the constant pool. - // (The appendix argument, if any, will be noted in the method's signature.) - Method* adapter = cpce->f1_as_method(); - return adapter; + int indy_index = cpool->decode_invokedynamic_index(index); + if (cpool->resolved_indy_entry_at(indy_index)->is_resolved()) { + return cpool->resolved_indy_entry_at(indy_index)->method(); } return nullptr; diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index 7783e7388e5..fff402241a1 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -636,24 +636,38 @@ Method* ConstantPool::method_at_if_loaded(const constantPoolHandle& cpool, bool ConstantPool::has_appendix_at_if_loaded(const constantPoolHandle& cpool, int which) { if (cpool->cache() == nullptr) return false; // nothing to load yet - int cache_index = decode_cpcache_index(which, true); - ConstantPoolCacheEntry* e = cpool->cache()->entry_at(cache_index); - return e->has_appendix(); + if (is_invokedynamic_index(which)) { + int indy_index = decode_invokedynamic_index(which); + return cpool->resolved_indy_entry_at(indy_index)->has_appendix(); + } else { + int cache_index = decode_cpcache_index(which, true); + ConstantPoolCacheEntry* e = cpool->cache()->entry_at(cache_index); + return e->has_appendix(); + } } oop ConstantPool::appendix_at_if_loaded(const constantPoolHandle& cpool, int which) { if (cpool->cache() == nullptr) return nullptr; // nothing to load yet - int cache_index = decode_cpcache_index(which, true); - ConstantPoolCacheEntry* e = cpool->cache()->entry_at(cache_index); - return e->appendix_if_resolved(cpool); + if (is_invokedynamic_index(which)) { + int indy_index = decode_invokedynamic_index(which); + return cpool->resolved_reference_from_indy(indy_index); + } else { + int cache_index = decode_cpcache_index(which, true); + ConstantPoolCacheEntry* e = cpool->cache()->entry_at(cache_index); + return e->appendix_if_resolved(cpool); + } } bool ConstantPool::has_local_signature_at_if_loaded(const constantPoolHandle& cpool, int which) { if (cpool->cache() == nullptr) return false; // nothing to load yet int cache_index = decode_cpcache_index(which, true); - ConstantPoolCacheEntry* e = cpool->cache()->entry_at(cache_index); - return e->has_local_signature(); + if (is_invokedynamic_index(which)) { + return cpool->resolved_indy_entry_at(cache_index)->has_local_signature(); + } else { + ConstantPoolCacheEntry* e = cpool->cache()->entry_at(cache_index); + return e->has_local_signature(); + } } Symbol* ConstantPool::impl_name_ref_at(int which, bool uncached) { @@ -671,7 +685,7 @@ int ConstantPool::impl_name_and_type_ref_index_at(int which, bool uncached) { int i = which; if (!uncached && cache() != nullptr) { if (ConstantPool::is_invokedynamic_index(which)) { - // Invokedynamic index is index into the constant pool cache + // Invokedynamic index is index into the resolved indy array in the constant pool cache int pool_index = invokedynamic_bootstrap_ref_index_at(which); pool_index = bootstrap_name_and_type_ref_index_at(pool_index); assert(tag_at(pool_index).is_name_and_type(), ""); diff --git a/src/hotspot/share/oops/constantPool.hpp b/src/hotspot/share/oops/constantPool.hpp index a98786a600a..93a4f504448 100644 --- a/src/hotspot/share/oops/constantPool.hpp +++ b/src/hotspot/share/oops/constantPool.hpp @@ -256,25 +256,11 @@ class ConstantPool : public Metadata { static int decode_invokedynamic_index(int i) { assert(is_invokedynamic_index(i), ""); return ~i; } static int encode_invokedynamic_index(int i) { assert(!is_invokedynamic_index(i), ""); return ~i; } - - // The invokedynamic points at a CP cache entry. This entry points back - // at the original CP entry (CONSTANT_InvokeDynamic) and also (via f2) at an entry - // in the resolved_references array (which provides the appendix argument). - int invokedynamic_cp_cache_index(int indy_index) const { - assert(is_invokedynamic_index(indy_index), "should be a invokedynamic index"); - int cache_index = decode_invokedynamic_index(indy_index); - return cache_index; - } - ConstantPoolCacheEntry* invokedynamic_cp_cache_entry_at(int indy_index) const { - // decode index that invokedynamic points to. - int cp_cache_index = invokedynamic_cp_cache_index(indy_index); - return cache()->entry_at(cp_cache_index); - } // Given the per-instruction index of an indy instruction, report the // main constant pool entry for its bootstrap specifier. // From there, uncached_name/signature_ref_at will get the name/type. int invokedynamic_bootstrap_ref_index_at(int indy_index) const { - return invokedynamic_cp_cache_entry_at(indy_index)->constant_pool_index(); + return cache()->resolved_indy_entry_at(decode_invokedynamic_index(indy_index))->constant_pool_index(); } // Assembly code support @@ -927,6 +913,17 @@ class ConstantPool : public Metadata { void print_entry_on(int index, outputStream* st); const char* internal_name() const { return "{constant pool}"; } + + // ResolvedIndyEntry getters + ResolvedIndyEntry* resolved_indy_entry_at(int index) { + return cache()->resolved_indy_entry_at(index); + } + int resolved_indy_entries_length() { + return cache()->resolved_indy_entries_length(); + } + oop resolved_reference_from_indy(int index) { + return resolved_references()->obj_at(cache()->resolved_indy_entry_at(index)->resolved_references_index()); + } }; #endif // SHARE_OOPS_CONSTANTPOOL_HPP diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp index 8a884b508a1..84af4d500ec 100644 --- a/src/hotspot/share/oops/cpCache.cpp +++ b/src/hotspot/share/oops/cpCache.cpp @@ -343,10 +343,6 @@ void ConstantPoolCacheEntry::set_method_handle(const constantPoolHandle& cpool, set_method_handle_common(cpool, Bytecodes::_invokehandle, call_info); } -void ConstantPoolCacheEntry::set_dynamic_call(const constantPoolHandle& cpool, const CallInfo &call_info) { - set_method_handle_common(cpool, Bytecodes::_invokedynamic, call_info); -} - void ConstantPoolCacheEntry::set_method_handle_common(const constantPoolHandle& cpool, Bytecodes::Code invoke_code, const CallInfo &call_info) { @@ -451,33 +447,6 @@ void ConstantPoolCacheEntry::set_method_handle_common(const constantPoolHandle& assert(this->has_local_signature(), "proper storage of signature flag"); } -bool ConstantPoolCacheEntry::save_and_throw_indy_exc( - const constantPoolHandle& cpool, int cpool_index, int index, constantTag tag, TRAPS) { - - assert(HAS_PENDING_EXCEPTION, "No exception got thrown!"); - assert(PENDING_EXCEPTION->is_a(vmClasses::LinkageError_klass()), - "No LinkageError exception"); - - MutexLocker ml(THREAD, cpool->pool_holder()->init_monitor()); - - // if f1 is not null or the indy_resolution_failed flag is set then another - // thread either succeeded in resolving the method or got a LinkageError - // exception, before this thread was able to record its failure. So, clear - // this thread's exception and return false so caller can use the earlier - // thread's result. - if (!is_f1_null() || indy_resolution_failed()) { - CLEAR_PENDING_EXCEPTION; - return false; - } - - Symbol* error = PENDING_EXCEPTION->klass()->name(); - Symbol* message = java_lang_Throwable::detail_message(PENDING_EXCEPTION); - - SystemDictionary::add_resolution_error(cpool, index, error, message); - set_indy_resolution_failed(); - return true; -} - Method* ConstantPoolCacheEntry::method_if_resolved(const constantPoolHandle& cpool) const { // Decode the action of set_method and set_interface_call Bytecodes::Code invoke_code = bytecode_1(); @@ -492,9 +461,10 @@ Method* ConstantPoolCacheEntry::method_if_resolved(const constantPoolHandle& cpo case Bytecodes::_invokespecial: assert(!has_appendix(), ""); case Bytecodes::_invokehandle: - case Bytecodes::_invokedynamic: assert(f1->is_method(), ""); return (Method*)f1; + case Bytecodes::_invokedynamic: + ShouldNotReachHere(); default: break; } @@ -644,8 +614,7 @@ void ConstantPoolCacheEntry::print(outputStream* st, int index, const ConstantPo type2name(as_BasicType(flag_state())), has_local_signature(), has_appendix(), is_forced_virtual(), is_final(), is_vfinal(), indy_resolution_failed(), parameter_size()); - if (bytecode_1() == Bytecodes::_invokehandle || - bytecode_1() == Bytecodes::_invokedynamic) { + if ((bytecode_1() == Bytecodes::_invokehandle)) { oop appendix = appendix_if_resolved(cph); if (appendix != nullptr) { st->print(" appendix: "); @@ -672,18 +641,29 @@ void ConstantPoolCacheEntry::verify(outputStream* st) const { ConstantPoolCache* ConstantPoolCache::allocate(ClassLoaderData* loader_data, const intStack& index_map, - const intStack& invokedynamic_index_map, - const intStack& invokedynamic_map, TRAPS) { + const intStack& invokedynamic_map, + const GrowableArray indy_entries, + TRAPS) { - const int length = index_map.length() + invokedynamic_index_map.length(); + const int length = index_map.length(); int size = ConstantPoolCache::size(length); + // Initialize ResolvedIndyEntry array with available data + Array* resolved_indy_entries; + if (indy_entries.length()) { + resolved_indy_entries = MetadataFactory::new_array(loader_data, indy_entries.length(), CHECK_NULL); + for (int i = 0; i < indy_entries.length(); i++) { + resolved_indy_entries->at_put(i, indy_entries.at(i)); + } + } else { + resolved_indy_entries = nullptr; + } + return new (loader_data, size, MetaspaceObj::ConstantPoolCacheType, THREAD) - ConstantPoolCache(length, index_map, invokedynamic_index_map, invokedynamic_map); + ConstantPoolCache(length, index_map, invokedynamic_map, resolved_indy_entries); } void ConstantPoolCache::initialize(const intArray& inverse_index_map, - const intArray& invokedynamic_inverse_index_map, const intArray& invokedynamic_references_map) { for (int i = 0; i < inverse_index_map.length(); i++) { ConstantPoolCacheEntry* e = entry_at(i); @@ -692,16 +672,6 @@ void ConstantPoolCache::initialize(const intArray& inverse_index_map, assert(entry_at(i) == e, "sanity"); } - // Append invokedynamic entries at the end - int invokedynamic_offset = inverse_index_map.length(); - for (int i = 0; i < invokedynamic_inverse_index_map.length(); i++) { - int offset = i + invokedynamic_offset; - ConstantPoolCacheEntry* e = entry_at(offset); - int original_index = invokedynamic_inverse_index_map.at(i); - e->initialize_entry(original_index); - assert(entry_at(offset) == e, "sanity"); - } - for (int ref = 0; ref < invokedynamic_references_map.length(); ref++) { const int cpci = invokedynamic_references_map.at(ref); if (cpci >= 0) { @@ -737,6 +707,12 @@ void ConstantPoolCache::remove_unshareable_info() { *entry_at(i) = _initial_entries->at(i); } _initial_entries = nullptr; + + if (_resolved_indy_entries != nullptr) { + for (int i = 0; i < _resolved_indy_entries->length(); i++) { + resolved_indy_entry_at(i)->remove_unshareable_info(); + } + } } #endif // INCLUDE_CDS @@ -750,6 +726,8 @@ void ConstantPoolCache::deallocate_contents(ClassLoaderData* data) { if (_initial_entries != nullptr) { Arguments::assert_is_dumping_archive(); MetadataFactory::free_array(data, _initial_entries); + if (_resolved_indy_entries) + MetadataFactory::free_array(data, _resolved_indy_entries); _initial_entries = nullptr; } #endif @@ -781,6 +759,17 @@ void ConstantPoolCache::set_archived_references(int root_index) { // If any entry of this ConstantPoolCache points to any of // old_methods, replace it with the corresponding new_method. void ConstantPoolCache::adjust_method_entries(bool * trace_name_printed) { + if (_resolved_indy_entries != nullptr) { + for (int j = 0; j < _resolved_indy_entries->length(); j++) { + Method* old_method = resolved_indy_entry_at(j)->method(); + if (old_method == nullptr || !old_method->is_old()) { + continue; + } + Method* new_method = old_method->get_new_method(); + resolved_indy_entry_at(j)->adjust_method_entry(new_method); + log_adjust("indy", old_method, new_method, trace_name_printed); + } + } for (int i = 0; i < length(); i++) { ConstantPoolCacheEntry* entry = entry_at(i); Method* old_method = entry->get_interesting_method_entry(); @@ -800,6 +789,18 @@ void ConstantPoolCache::adjust_method_entries(bool * trace_name_printed) { // the constant pool cache should never contain old or obsolete methods bool ConstantPoolCache::check_no_old_or_obsolete_entries() { ResourceMark rm; + if (_resolved_indy_entries) { + for (int i = 0; i < _resolved_indy_entries->length(); i++) { + Method* m = resolved_indy_entry_at(i)->method(); + if (m != nullptr && !resolved_indy_entry_at(i)->check_no_old_or_obsolete_entry()) { + log_trace(redefine, class, update, constantpool) + ("cpcache check found old method entry: class: %s, old: %d, obsolete: %d, method: %s", + constant_pool()->pool_holder()->external_name(), m->is_old(), m->is_obsolete(), m->external_name()); + return false; + } + } + } + for (int i = 1; i < length(); i++) { Method* m = entry_at(i)->get_interesting_method_entry(); if (m != nullptr && !entry_at(i)->check_no_old_or_obsolete_entries()) { @@ -825,6 +826,95 @@ void ConstantPoolCache::metaspace_pointers_do(MetaspaceClosure* it) { log_trace(cds)("Iter(ConstantPoolCache): %p", this); it->push(&_constant_pool); it->push(&_reference_map); + if (_resolved_indy_entries != nullptr) { + it->push(&_resolved_indy_entries, MetaspaceClosure::_writable); + } +} + +bool ConstantPoolCache::save_and_throw_indy_exc( + const constantPoolHandle& cpool, int cpool_index, int index, constantTag tag, TRAPS) { + + assert(HAS_PENDING_EXCEPTION, "No exception got thrown!"); + assert(PENDING_EXCEPTION->is_a(vmClasses::LinkageError_klass()), + "No LinkageError exception"); + + MutexLocker ml(THREAD, cpool->pool_holder()->init_monitor()); + + // if the indy_info is resolved or the indy_resolution_failed flag is set then another + // thread either succeeded in resolving the method or got a LinkageError + // exception, before this thread was able to record its failure. So, clear + // this thread's exception and return false so caller can use the earlier + // thread's result. + if (resolved_indy_entry_at(index)->is_resolved() || resolved_indy_entry_at(index)->resolution_failed()) { + CLEAR_PENDING_EXCEPTION; + return false; + } + + Symbol* error = PENDING_EXCEPTION->klass()->name(); + Symbol* message = java_lang_Throwable::detail_message(PENDING_EXCEPTION); + + int encoded_index = ResolutionErrorTable::encode_cpcache_index( + ConstantPool::encode_invokedynamic_index(index)); + SystemDictionary::add_resolution_error(cpool, encoded_index, error, message); + resolved_indy_entry_at(index)->set_resolution_failed(); + return true; +} + +oop ConstantPoolCache::set_dynamic_call(const CallInfo &call_info, int index) { + ResourceMark rm; + MutexLocker ml(constant_pool()->pool_holder()->init_monitor()); + assert(index >= 0, "Indy index must be positive at this point"); + + if (resolved_indy_entry_at(index)->method() != nullptr) { + return constant_pool()->resolved_reference_from_indy(index); + } + + if (resolved_indy_entry_at(index)->resolution_failed()) { + // Before we got here, another thread got a LinkageError exception during + // resolution. Ignore our success and throw their exception. + guarantee(index >= 0, "Invalid indy index"); + int encoded_index = ResolutionErrorTable::encode_cpcache_index( + ConstantPool::encode_invokedynamic_index(index)); + JavaThread* THREAD = JavaThread::current(); // For exception macros. + constantPoolHandle cp(THREAD, constant_pool()); + ConstantPool::throw_resolution_error(cp, encoded_index, THREAD); + return nullptr; + } + + Method* adapter = call_info.resolved_method(); + const Handle appendix = call_info.resolved_appendix(); + const bool has_appendix = appendix.not_null(); + + LogStream* log_stream = NULL; + LogStreamHandle(Debug, methodhandles, indy) lsh_indy; + if (lsh_indy.is_enabled()) { + ResourceMark rm; + log_stream = &lsh_indy; + log_stream->print_cr("set_method_handle bc=%d appendix=" PTR_FORMAT "%s method=" PTR_FORMAT " (local signature) ", + 0xba, + p2i(appendix()), + (has_appendix ? "" : " (unused)"), + p2i(adapter)); + adapter->print_on(log_stream); + if (has_appendix) appendix()->print_on(log_stream); + } + + if (has_appendix) { + const int appendix_index = resolved_indy_entry_at(index)->resolved_references_index(); + objArrayOop resolved_references = constant_pool()->resolved_references(); + assert(appendix_index >= 0 && appendix_index < resolved_references->length(), "oob"); + assert(resolved_references->obj_at(appendix_index) == NULL, "init just once"); + resolved_references->obj_at_put(appendix_index, appendix()); + } + + // Populate entry with resolved information + assert(resolved_indy_entries() != nullptr, "Invokedynamic array is empty, cannot fill with resolved information"); + resolved_indy_entry_at(index)->fill_in(adapter, adapter->size_of_parameters(), as_TosState(adapter->result_type()), has_appendix); + + if (log_stream != NULL) { + resolved_indy_entry_at(index)->print_on(log_stream); + } + return appendix(); } // Printing @@ -833,6 +923,14 @@ void ConstantPoolCache::print_on(outputStream* st) const { st->print_cr("%s", internal_name()); // print constant pool cache entries for (int i = 0; i < length(); i++) entry_at(i)->print(st, i, this); + for (int i = 0; i < resolved_indy_entries_length(); i++) { + ResolvedIndyEntry* indy_entry = resolved_indy_entry_at(i); + indy_entry->print_on(st); + if (indy_entry->has_appendix()) { + st->print(" appendix: "); + constant_pool()->resolved_reference_from_indy(i)->print_on(st); + } + } } void ConstantPoolCache::print_value_on(outputStream* st) const { diff --git a/src/hotspot/share/oops/cpCache.hpp b/src/hotspot/share/oops/cpCache.hpp index 3ba5cc17eaa..4602e3241b4 100644 --- a/src/hotspot/share/oops/cpCache.hpp +++ b/src/hotspot/share/oops/cpCache.hpp @@ -29,6 +29,7 @@ #include "memory/allocation.hpp" #include "oops/array.hpp" #include "oops/oopHandle.hpp" +#include "oops/resolvedIndyEntry.hpp" #include "runtime/handles.hpp" #include "utilities/align.hpp" #include "utilities/constantTag.hpp" @@ -256,11 +257,6 @@ class ConstantPoolCacheEntry { const CallInfo &call_info // Call link information ); - void set_dynamic_call( - const constantPoolHandle& cpool, // holding constant pool (required for locking) - const CallInfo &call_info // Call link information - ); - // Common code for invokedynamic and MH invocations. // The "appendix" is an optional call-site-specific parameter which is @@ -282,13 +278,6 @@ class ConstantPoolCacheEntry { const CallInfo &call_info // Call link information ); - // Return TRUE if resolution failed and this thread got to record the failure - // status. Return FALSE if another thread succeeded or failed in resolving - // the method and recorded the success or failure before this thread had a - // chance to record its failure. - bool save_and_throw_indy_exc(const constantPoolHandle& cpool, int cpool_index, - int index, constantTag tag, TRAPS); - // invokedynamic and invokehandle call sites have an "appendix" item in the // resolved references array. Method* method_if_resolved(const constantPoolHandle& cpool) const; @@ -417,6 +406,8 @@ class ConstantPoolCache: public MetaspaceObj { // RedefineClasses support uint64_t _gc_epoch; + Array* _resolved_indy_entries; + CDS_ONLY(Array* _initial_entries;) // Sizing @@ -425,18 +416,18 @@ class ConstantPoolCache: public MetaspaceObj { // Constructor ConstantPoolCache(int length, const intStack& inverse_index_map, - const intStack& invokedynamic_inverse_index_map, - const intStack& invokedynamic_references_map); + const intStack& invokedynamic_references_map, + Array* indy_info); // Initialization void initialize(const intArray& inverse_index_map, - const intArray& invokedynamic_inverse_index_map, const intArray& invokedynamic_references_map); public: static ConstantPoolCache* allocate(ClassLoaderData* loader_data, const intStack& cp_cache_map, - const intStack& invokedynamic_cp_cache_map, - const intStack& invokedynamic_references_map, TRAPS); + const intStack& invokedynamic_references_map, + const GrowableArray indy_entries, + TRAPS); int length() const { return _length; } void metaspace_pointers_do(MetaspaceClosure* it); @@ -451,8 +442,18 @@ class ConstantPoolCache: public MetaspaceObj { Array* reference_map() const { return _reference_map; } void set_reference_map(Array* o) { _reference_map = o; } + Array* resolved_indy_entries() { return _resolved_indy_entries; } + ResolvedIndyEntry* resolved_indy_entry_at(int index) const { return _resolved_indy_entries->adr_at(index); } + int resolved_indy_entries_length() const { return _resolved_indy_entries->length(); } + void print_resolved_indy_entries(outputStream* st) const { + for (int i = 0; i < _resolved_indy_entries->length(); i++) { + _resolved_indy_entries->at(i).print_on(st); + } + } + // Assembly code support static int resolved_references_offset_in_bytes() { return offset_of(ConstantPoolCache, _resolved_references); } + static ByteSize invokedynamic_entries_offset() { return byte_offset_of(ConstantPoolCache, _resolved_indy_entries); } #if INCLUDE_CDS void remove_unshareable_info(); @@ -512,6 +513,13 @@ class ConstantPoolCache: public MetaspaceObj { void record_gc_epoch(); uint64_t gc_epoch() { return _gc_epoch; } + // Return TRUE if resolution failed and this thread got to record the failure + // status. Return FALSE if another thread succeeded or failed in resolving + // the method and recorded the success or failure before this thread had a + // chance to record its failure. + bool save_and_throw_indy_exc(const constantPoolHandle& cpool, int cpool_index, int index, constantTag tag, TRAPS); + oop set_dynamic_call(const CallInfo &call_info, int index); + // Printing void print_on(outputStream* st) const; void print_value_on(outputStream* st) const; diff --git a/src/hotspot/share/oops/cpCache.inline.hpp b/src/hotspot/share/oops/cpCache.inline.hpp index 4befcb19498..c0cfc21df99 100644 --- a/src/hotspot/share/oops/cpCache.inline.hpp +++ b/src/hotspot/share/oops/cpCache.inline.hpp @@ -86,13 +86,14 @@ inline bool ConstantPoolCacheEntry::indy_resolution_failed() const { // Constructor inline ConstantPoolCache::ConstantPoolCache(int length, const intStack& inverse_index_map, - const intStack& invokedynamic_inverse_index_map, - const intStack& invokedynamic_references_map) : + const intStack& invokedynamic_references_map, + Array* invokedynamic_info) : _length(length), _constant_pool(nullptr), - _gc_epoch(0) { + _gc_epoch(0), + _resolved_indy_entries(invokedynamic_info) { CDS_JAVA_HEAP_ONLY(_archived_references_index = -1;) - initialize(inverse_index_map, invokedynamic_inverse_index_map, + initialize(inverse_index_map, invokedynamic_references_map); for (int i = 0; i < length; i++) { assert(entry_at(i)->is_f1_null(), "Failed to clear?"); diff --git a/src/hotspot/share/oops/resolvedIndyEntry.cpp b/src/hotspot/share/oops/resolvedIndyEntry.cpp new file mode 100644 index 00000000000..eff543a5448 --- /dev/null +++ b/src/hotspot/share/oops/resolvedIndyEntry.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023, 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/compressedStream.hpp" +#include "oops/method.hpp" +#include "oops/resolvedIndyEntry.hpp" + +bool ResolvedIndyEntry::check_no_old_or_obsolete_entry() { + // return false if m refers to a non-deleted old or obsolete method + if (_method != nullptr) { + assert(_method->is_valid() && _method->is_method(), "m is a valid method"); + return !_method->is_old() && !_method->is_obsolete(); // old is always set for old and obsolete + } else { + return true; + } +} + +void ResolvedIndyEntry::remove_unshareable_info() { + u2 saved_resolved_references_index = _resolved_references_index; + u2 saved_cpool_index = _cpool_index; + memset(this, 0, sizeof(*this)); + _resolved_references_index = saved_resolved_references_index; + _cpool_index = saved_cpool_index; +} + +void ResolvedIndyEntry::print_on(outputStream* st) const { + st->print_cr("Resolved InvokeDynamic Info:"); + if (_method != nullptr) { + st->print_cr(" - Method: " INTPTR_FORMAT " %s", p2i(method()), method()->external_name()); + } else { + st->print_cr(" - Method: null"); + } + st->print_cr(" - Resolved References Index: %d", resolved_references_index()); + st->print_cr(" - CP Index: %d", constant_pool_index()); + st->print_cr(" - Num Parameters: %d", num_parameters()); + st->print_cr(" - Return type: %s", type2name(as_BasicType((TosState)return_type()))); + st->print_cr(" - Has Appendix: %d", has_appendix()); + st->print_cr(" - Resolution Failed %d", resolution_failed()); +} diff --git a/src/hotspot/share/oops/resolvedIndyEntry.hpp b/src/hotspot/share/oops/resolvedIndyEntry.hpp new file mode 100644 index 00000000000..25797d338c4 --- /dev/null +++ b/src/hotspot/share/oops/resolvedIndyEntry.hpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2023, 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. + * + */ + +#ifndef SHARE_OOPS_RESOLVEDINDYENTRY_HPP +#define SHARE_OOPS_RESOLVEDINDYENTRY_HPP + +// ResolvedIndyEntry contains the resolution information for invokedynamic bytecodes. +// A member of this class can be initialized with the resolved references index and +// constant pool index before any resolution is done, where "resolution" refers to finding the target +// method and its relevant information, like number of parameters and return type. These entries are contained +// within the ConstantPoolCache and are accessed with indices added to the invokedynamic bytecode after +// rewriting. + +// The invokedynamic bytecode starts with an Constant Pool index as its operand which is then rewritten +// to become an "indy index", an index into the array of ResolvedIndyEntry. The method here is an adapter method +// which will be something like linkToTargetMethod. When an indy call is resolved, we no longer need to invoke +// the bootstrap method (BSM) and we can get the target method (the method actually doing stuff, i.e. a string concat) +// from the CallSite. The CallSite is generated when the BSM is invoked and it simply contains a MethodHandle for +// the target method. The adapter will propagate information to and from the target method and the JVM. + + +class Method; +class ResolvedIndyEntry { + friend class VMStructs; + + Method* _method; // Adapter method for indy call + u2 _resolved_references_index; // Index of resolved references array that holds the appendix oop + u2 _cpool_index; // Constant pool index + u2 _number_of_parameters; // Number of arguments for adapter method + u1 _return_type; // Adapter method return type + u1 _flags; // Flags: [0000|00|has_appendix|resolution_failed] + +public: + ResolvedIndyEntry() : + _method(nullptr), + _resolved_references_index(0), + _cpool_index(0), + _number_of_parameters(0), + _return_type(0), + _flags(0) {} + ResolvedIndyEntry(u2 resolved_references_index, u2 cpool_index) : + _method(nullptr), + _resolved_references_index(resolved_references_index), + _cpool_index(cpool_index), + _number_of_parameters(0), + _return_type(0), + _flags(0) {} + + // Bit shift to get flags + // Note: Only two flags exists at the moment but more could be added + enum { + has_appendix_shift = 1, + }; + + // Getters + Method* method() const { return Atomic::load_acquire(&_method); } + u2 resolved_references_index() const { return _resolved_references_index; } + u2 constant_pool_index() const { return _cpool_index; } + u2 num_parameters() const { return _number_of_parameters; } + u1 return_type() const { return _return_type; } + bool is_resolved() const { return method() != nullptr; } + bool has_appendix() const { return (_flags & (1 << has_appendix_shift)) != 0; } + bool resolution_failed() const { return (_flags & 1) != 0; } + bool is_vfinal() const { return false; } + bool is_final() const { return false; } + bool has_local_signature() const { return true; } + + // Printing + void print_on(outputStream* st) const; + + // Initialize with fields available before resolution + void init(u2 resolved_references_index, u2 cpool_index) { + _resolved_references_index = resolved_references_index; + _cpool_index = cpool_index; + } + + void set_num_parameters(int value) { + assert(_number_of_parameters == 0 || _number_of_parameters == value, + "size must not change: parameter_size=%d, value=%d", _number_of_parameters, value); + Atomic::store(&_number_of_parameters, (u2)value); + guarantee(_number_of_parameters == value, + "size must not change: parameter_size=%d, value=%d", _number_of_parameters, value); + } + + // Populate structure with resolution information + void fill_in(Method* m, u2 num_params, u1 return_type, bool has_appendix) { + set_num_parameters(num_params); + _return_type = return_type; + set_flags(has_appendix); + // Set the method last since it is read lock free. + // Resolution is indicated by whether or not the method is set. + Atomic::release_store(&_method, m); + } + + // has_appendix is currently the only other flag besides resolution_failed + void set_flags(bool has_appendix) { + u1 new_flags = (has_appendix << has_appendix_shift); + assert((new_flags & 1) == 0, "New flags should not change resolution flag"); + // Preserve the resolution_failed bit + _flags = (_flags & 1) | new_flags; + } + + void set_resolution_failed() { + _flags = _flags | 1; + } + + void adjust_method_entry(Method* new_method) { _method = new_method; } + bool check_no_old_or_obsolete_entry(); + + // CDS + void remove_unshareable_info(); + + // Offsets + static ByteSize method_offset() { return byte_offset_of(ResolvedIndyEntry, _method); } + static ByteSize resolved_references_index_offset() { return byte_offset_of(ResolvedIndyEntry, _resolved_references_index); } + static ByteSize result_type_offset() { return byte_offset_of(ResolvedIndyEntry, _return_type); } + static ByteSize num_parameters_offset() { return byte_offset_of(ResolvedIndyEntry, _number_of_parameters); } + static ByteSize flags_offset() { return byte_offset_of(ResolvedIndyEntry, _flags); } +}; + +#endif // SHARE_OOPS_RESOLVEDINDYENTRY_HPP diff --git a/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp b/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp index b7e4ff0c057..566406433b7 100644 --- a/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp +++ b/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp @@ -1039,16 +1039,17 @@ void JvmtiClassFileReconstituter::copy_bytecodes(const methodHandle& mh, int cpci = Bytes::get_native_u2(bcp+1); bool is_invokedynamic = (code == Bytecodes::_invokedynamic); ConstantPoolCacheEntry* entry; + int pool_index; if (is_invokedynamic) { cpci = Bytes::get_native_u4(bcp+1); - entry = mh->constants()->invokedynamic_cp_cache_entry_at(cpci); + pool_index = mh->constants()->resolved_indy_entry_at(mh->constants()->decode_invokedynamic_index(cpci))->constant_pool_index(); } else { // cache cannot be pre-fetched since some classes won't have it yet entry = mh->constants()->cache()->entry_at(cpci); + pool_index = entry->constant_pool_index(); } - int i = entry->constant_pool_index(); - assert(i < mh->constants()->length(), "sanity check"); - Bytes::put_Java_u2((address)(p+1), (u2)i); // java byte ordering + assert(pool_index < mh->constants()->length(), "sanity check"); + Bytes::put_Java_u2((address)(p+1), (u2)pool_index); // java byte ordering if (is_invokedynamic) *(p+3) = *(p+4) = 0; break; } diff --git a/src/hotspot/share/prims/methodComparator.cpp b/src/hotspot/share/prims/methodComparator.cpp index 778528b4e76..170375029c0 100644 --- a/src/hotspot/share/prims/methodComparator.cpp +++ b/src/hotspot/share/prims/methodComparator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2023, 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 @@ -103,22 +103,27 @@ bool MethodComparator::args_same(Bytecodes::Code const c_old, Bytecodes::Code c break; } case Bytecodes::_invokedynamic: { - int cpci_old = s_old->get_index_u4(); - int cpci_new = s_new->get_index_u4(); + // Encoded indy index, should be negative + int index_old = s_old->get_index_u4(); + int index_new = s_new->get_index_u4(); + + int indy_index_old = old_cp->decode_invokedynamic_index(index_old); + int indy_index_new = new_cp->decode_invokedynamic_index(index_new); // Check if the names of classes, field/method names and signatures at these indexes // are the same. Indices which are really into constantpool cache (rather than constant // pool itself) are accepted by the constantpool query routines below. - if ((old_cp->name_ref_at(cpci_old) != new_cp->name_ref_at(cpci_new)) || - (old_cp->signature_ref_at(cpci_old) != new_cp->signature_ref_at(cpci_new))) + // Currently needs encoded indy_index + if ((old_cp->name_ref_at(index_old) != new_cp->name_ref_at(index_new)) || + (old_cp->signature_ref_at(index_old) != new_cp->signature_ref_at(index_new))) return false; - // Translate object indexes to constant pool cache indexes. - cpci_old = old_cp->invokedynamic_cp_cache_index(cpci_old); - cpci_new = new_cp->invokedynamic_cp_cache_index(cpci_new); + int cpi_old = old_cp->cache()->resolved_indy_entry_at(indy_index_old)->constant_pool_index(); + int cpi_new = new_cp->cache()->resolved_indy_entry_at(indy_index_new)->constant_pool_index(); + if ((old_cp->uncached_name_ref_at(cpi_old) != new_cp->uncached_name_ref_at(cpi_new)) || + (old_cp->uncached_signature_ref_at(cpi_old) != new_cp->uncached_signature_ref_at(cpi_new))) + return false; - int cpi_old = old_cp->cache()->entry_at(cpci_old)->constant_pool_index(); - int cpi_new = new_cp->cache()->entry_at(cpci_new)->constant_pool_index(); int bsm_old = old_cp->bootstrap_method_ref_index_at(cpi_old); int bsm_new = new_cp->bootstrap_method_ref_index_at(cpi_new); if (!pool_constants_same(bsm_old, bsm_new, old_cp, new_cp)) diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 403da9e1d03..0fd9a9d5a28 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -1870,6 +1870,24 @@ WB_ENTRY(jint, WB_ConstantPoolEncodeIndyIndex(JNIEnv* env, jobject wb, jint inde return ConstantPool::encode_invokedynamic_index(index); WB_END +WB_ENTRY(jint, WB_getIndyInfoLength(JNIEnv* env, jobject wb, jclass klass)) + InstanceKlass* ik = InstanceKlass::cast(java_lang_Class::as_Klass(JNIHandles::resolve(klass))); + ConstantPool* cp = ik->constants(); + if (cp->cache() == NULL) { + return -1; + } + return cp->resolved_indy_entries_length(); +WB_END + +WB_ENTRY(jint, WB_getIndyCPIndex(JNIEnv* env, jobject wb, jclass klass, jint index)) + InstanceKlass* ik = InstanceKlass::cast(java_lang_Class::as_Klass(JNIHandles::resolve(klass))); + ConstantPool* cp = ik->constants(); + if (cp->cache() == NULL) { + return -1; + } + return cp->resolved_indy_entry_at(index)->constant_pool_index(); +WB_END + WB_ENTRY(void, WB_ClearInlineCaches(JNIEnv* env, jobject wb, jboolean preserve_static_stubs)) VM_ClearICs clear_ics(preserve_static_stubs == JNI_TRUE); VMThread::execute(&clear_ics); @@ -2709,6 +2727,8 @@ static JNINativeMethod methods[] = { CC"(Ljava/lang/Class;I)I", (void*)&WB_ConstantPoolRemapInstructionOperandFromCache}, {CC"encodeConstantPoolIndyIndex0", CC"(I)I", (void*)&WB_ConstantPoolEncodeIndyIndex}, + {CC"getIndyInfoLength0", CC"(Ljava/lang/Class;)I", (void*)&WB_getIndyInfoLength}, + {CC"getIndyCPIndex0", CC"(Ljava/lang/Class;I)I", (void*)&WB_getIndyCPIndex}, {CC"getMethodBooleanOption", CC"(Ljava/lang/reflect/Executable;Ljava/lang/String;)Ljava/lang/Boolean;", (void*)&WB_GetMethodBooleaneOption}, diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 9ff307c0a6e..b3d8584f82c 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -221,6 +221,8 @@ nonstatic_field(ConstantPoolCache, _reference_map, Array*) \ nonstatic_field(ConstantPoolCache, _length, int) \ nonstatic_field(ConstantPoolCache, _constant_pool, ConstantPool*) \ + nonstatic_field(ConstantPoolCache, _resolved_indy_entries, Array*) \ + nonstatic_field(ResolvedIndyEntry, _cpool_index, u2) \ volatile_nonstatic_field(InstanceKlass, _array_klasses, ObjArrayKlass*) \ nonstatic_field(InstanceKlass, _methods, Array*) \ nonstatic_field(InstanceKlass, _default_methods, Array*) \ @@ -473,6 +475,8 @@ \ nonstatic_field(Array, _length, int) \ nonstatic_field(Array, _data[0], Klass*) \ + nonstatic_field(Array, _length, int) \ + nonstatic_field(Array, _data[0], ResolvedIndyEntry) \ \ /*******************/ \ /* GrowableArrays */ \ @@ -1019,12 +1023,13 @@ /* Array */ \ /************/ \ \ - nonstatic_field(Array, _length, int) \ - unchecked_nonstatic_field(Array, _data, sizeof(int)) \ - unchecked_nonstatic_field(Array, _data, sizeof(u1)) \ - unchecked_nonstatic_field(Array, _data, sizeof(u2)) \ - unchecked_nonstatic_field(Array, _data, sizeof(Method*)) \ - unchecked_nonstatic_field(Array, _data, sizeof(Klass*)) \ + nonstatic_field(Array, _length, int) \ + unchecked_nonstatic_field(Array, _data, sizeof(int)) \ + unchecked_nonstatic_field(Array, _data, sizeof(u1)) \ + unchecked_nonstatic_field(Array, _data, sizeof(u2)) \ + unchecked_nonstatic_field(Array, _data, sizeof(Method*)) \ + unchecked_nonstatic_field(Array, _data, sizeof(Klass*)) \ + unchecked_nonstatic_field(Array, _data, sizeof(ResolvedIndyEntry)) \ \ /*********************************/ \ /* java_lang_Class fields */ \ @@ -1954,6 +1959,7 @@ declare_type(Array, MetaspaceObj) \ declare_type(Array, MetaspaceObj) \ declare_type(Array, MetaspaceObj) \ + declare_type(Array, MetaspaceObj) \ \ declare_toplevel_type(BitMap) \ declare_type(BitMapView, BitMap) \ @@ -1970,6 +1976,7 @@ declare_toplevel_type(RuntimeBlob*) \ declare_toplevel_type(CompressedWriteStream*) \ declare_toplevel_type(ConstantPoolCacheEntry) \ + declare_toplevel_type(ResolvedIndyEntry) \ declare_toplevel_type(elapsedTimer) \ declare_toplevel_type(frame) \ declare_toplevel_type(intptr_t*) \ diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/interpreter/BytecodeWithCPIndex.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/interpreter/BytecodeWithCPIndex.java index 1d2c87b8fb6..7a0429043fd 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/interpreter/BytecodeWithCPIndex.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/interpreter/BytecodeWithCPIndex.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2023, 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 @@ -54,8 +54,10 @@ public abstract class BytecodeWithCPIndex extends Bytecode { int cpCacheIndex = index(); if (cpCache == null) { return cpCacheIndex; + } else if (code() == Bytecodes._invokedynamic) { + return cpCache.getIndyEntryAt(cpCacheIndex).getConstantPoolIndex(); } else { - return cpCache.getEntryAt((int) (0xFFFF & cpCacheIndex)).getConstantPoolIndex(); + return cpCache.getEntryAt((int) (0xFFFF & cpCacheIndex)).getConstantPoolIndex(); } } } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPool.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPool.java index 158ed12a7c3..bcc24ddb361 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPool.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2023, 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 @@ -312,7 +312,7 @@ public class ConstantPool extends Metadata implements ClassConstants { if (!uncached && getCache() != null) { if (isInvokedynamicIndex(which)) { // Invokedynamic index is index into resolved_references - int poolIndex = invokedynamicCPCacheEntryAt(which).getConstantPoolIndex(); + int poolIndex = getCache().getIndyEntryAt(which).getConstantPoolIndex(); poolIndex = invokeDynamicNameAndTypeRefIndexAt(poolIndex); Assert.that(getTagAt(poolIndex).isNameAndType(), ""); return poolIndex; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPoolCache.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPoolCache.java index 4590cffcc10..5e8379559ac 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPoolCache.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPoolCache.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2023, 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 @@ -55,6 +55,7 @@ public class ConstantPoolCache extends Metadata { intSize = VM.getVM().getObjectHeap().getIntSize(); resolvedReferences = type.getAddressField("_resolved_references"); referenceMap = type.getAddressField("_reference_map"); + resolvedIndyArray = type.getAddressField("_resolved_indy_entries"); } public ConstantPoolCache(Address addr) { @@ -71,6 +72,7 @@ public class ConstantPoolCache extends Metadata { private static long intSize; private static AddressField resolvedReferences; private static AddressField referenceMap; + private static AddressField resolvedIndyArray; public ConstantPool getConstants() { return (ConstantPool) constants.getValue(this); } @@ -83,6 +85,12 @@ public class ConstantPoolCache extends Metadata { return new ConstantPoolCacheEntry(this, i); } + public ResolvedIndyEntry getIndyEntryAt(int i) { + Address addr = resolvedIndyArray.getValue(getAddress()); + ResolvedIndyArray array = new ResolvedIndyArray(addr); + return array.getAt(i); + } + public int getIntAt(int entry, int fld) { long offset = baseOffset + entry * elementSize + fld * intSize; return (int) getAddress().getCIntegerAt(offset, intSize, true ); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ResolvedIndyArray.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ResolvedIndyArray.java new file mode 100644 index 00000000000..83b6e883dd4 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ResolvedIndyArray.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2023, 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 sun.jvm.hotspot.oops; + +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.runtime.VM; +import sun.jvm.hotspot.types.Type; +import sun.jvm.hotspot.types.TypeDataBase; +import sun.jvm.hotspot.types.WrongTypeException; +import sun.jvm.hotspot.utilities.GenericArray; +import sun.jvm.hotspot.utilities.Observable; +import sun.jvm.hotspot.utilities.Observer; + +public class ResolvedIndyArray extends GenericArray { + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + elemType = db.lookupType("ResolvedIndyEntry"); + + Type type = db.lookupType("Array"); + dataFieldOffset = type.getAddressField("_data").getOffset(); + } + + private static long dataFieldOffset; + protected static Type elemType; + + public ResolvedIndyArray(Address addr) { + super(addr, dataFieldOffset); + } + + public ResolvedIndyEntry getAt(int index) { + if (index < 0 || index >= length()) throw new ArrayIndexOutOfBoundsException(index + " " + length()); + + Type elemType = getElemType(); + + Address data = getAddress().addOffsetTo(dataFieldOffset); + long elemSize = elemType.getSize(); + + return new ResolvedIndyEntry(data.addOffsetTo(index* elemSize)); + } + + public Type getElemType() { + return elemType; + } +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ResolvedIndyEntry.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ResolvedIndyEntry.java new file mode 100644 index 00000000000..6852cf07397 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ResolvedIndyEntry.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023, 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 sun.jvm.hotspot.oops; + +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; +import sun.jvm.hotspot.utilities.Observable; +import sun.jvm.hotspot.utilities.Observer; + +public class ResolvedIndyEntry extends VMObject { + private static long size; + private static long baseOffset; + private static CIntegerField cpIndex; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("ResolvedIndyEntry"); + size = type.getSize(); + + cpIndex = type.getCIntegerField("_cpool_index"); + } + + ResolvedIndyEntry(Address addr) { + super(addr); + } + + public int getConstantPoolIndex() { + return this.getAddress().getJShortAt(cpIndex.getOffset()); + } + + public void iterateFields(MetadataVisitor visitor) { } +} diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotConstantPool.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotConstantPool.java index fd158d0cc21..373e2d6dbd2 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotConstantPool.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotConstantPool.java @@ -845,7 +845,7 @@ public final class HotSpotConstantPool implements ConstantPool, MetaspaceHandleO if (opcode != Bytecodes.INVOKEDYNAMIC) { throw new IllegalArgumentException("expected INVOKEDYNAMIC at " + rawIndex + ", got " + opcode); } - index = decodeInvokedynamicIndex(rawIndex) + config().constantPoolCpCacheIndexTag; + return index = decodeInvokedynamicIndex(rawIndex); } else { if (opcode == Bytecodes.INVOKEDYNAMIC) { throw new IllegalArgumentException("unexpected INVOKEDYNAMIC at " + rawIndex); @@ -876,9 +876,11 @@ public final class HotSpotConstantPool implements ConstantPool, MetaspaceHandleO index = cpi; break; case Bytecodes.INVOKEDYNAMIC: { - // invokedynamic instructions point to a constant pool cache entry. - index = decodeConstantPoolCacheIndex(cpi) + config().constantPoolCpCacheIndexTag; - index = compilerToVM().constantPoolRemapInstructionOperandFromCache(this, index); + // invokedynamic indices are different from constant pool cache indices + index = decodeConstantPoolCacheIndex(cpi); + if (isInvokedynamicIndex(cpi)) { + compilerToVM().resolveInvokeDynamicInPool(this, index); + } break; } case Bytecodes.GETSTATIC: @@ -929,9 +931,7 @@ public final class HotSpotConstantPool implements ConstantPool, MetaspaceHandleO break; case "InvokeDynamic": - if (isInvokedynamicIndex(cpi)) { - compilerToVM().resolveInvokeDynamicInPool(this, cpi); - } + // nothing break; default: // nothing diff --git a/test/hotspot/jtreg/compiler/jvmci/compilerToVM/ConstantPoolTestsHelper.java b/test/hotspot/jtreg/compiler/jvmci/compilerToVM/ConstantPoolTestsHelper.java index d3e05c6cca5..769914889dc 100644 --- a/test/hotspot/jtreg/compiler/jvmci/compilerToVM/ConstantPoolTestsHelper.java +++ b/test/hotspot/jtreg/compiler/jvmci/compilerToVM/ConstantPoolTestsHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, 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 @@ -76,6 +76,13 @@ public class ConstantPoolTestsHelper { } public int getCPCacheIndex(int cpi) { + if (constantPoolSS.getTagAt(cpi).equals(Tag.INVOKEDYNAMIC)) { + for (int indy_index = 0; indy_index < WB.getIndyInfoLength(this.klass); indy_index++) { + if (WB.getIndyCPIndex(this.klass, indy_index) == cpi) { + return ~indy_index; + } + } + } int cacheLength = WB.getConstantPoolCacheLength(this.klass); int indexTag = WB.getConstantPoolCacheIndexTag(); for (int cpci = indexTag; cpci < cacheLength + indexTag; cpci++) { diff --git a/test/lib/jdk/test/whitebox/WhiteBox.java b/test/lib/jdk/test/whitebox/WhiteBox.java index ae8b15c2f7b..e60f5eac92f 100644 --- a/test/lib/jdk/test/whitebox/WhiteBox.java +++ b/test/lib/jdk/test/whitebox/WhiteBox.java @@ -151,6 +151,18 @@ public class WhiteBox { return encodeConstantPoolIndyIndex0(index); } + private native int getIndyInfoLength0(Class aClass); + public int getIndyInfoLength(Class aClass) { + Objects.requireNonNull(aClass); + return getIndyInfoLength0(aClass); + } + + private native int getIndyCPIndex0(Class aClass, int index); + public int getIndyCPIndex(Class aClass, int index) { + Objects.requireNonNull(aClass); + return getIndyCPIndex0(aClass, index); + } + // JVMTI private native void addToBootstrapClassLoaderSearch0(String segment); public void addToBootstrapClassLoaderSearch(String segment){