8301995: Move invokedynamic resolution information out of ConstantPoolCacheEntry

Co-authored-by: Richard Reingruber <rrich@openjdk.org>
Co-authored-by: Dingli Zhang <dzhang@openjdk.org>
Co-authored-by: Gui Cao <gcao@openjdk.org>
Co-authored-by: Amit Kumar <amitkumar@openjdk.org>
Reviewed-by: coleenp, dnsimon, fparain, gcao, aph, fyang, amitkumar, lucy
This commit is contained in:
Matias Saavedra Silva 2023-03-28 19:50:38 +00:00
parent 50a995f03a
commit 3fbbfd1749
58 changed files with 1342 additions and 406 deletions

View File

@ -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<ResolvedIndyEntry>::base_offset_in_bytes());
lea(cache, Address(cache, index));
}

View File

@ -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

View File

@ -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();

View File

@ -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)

View File

@ -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);

View File

@ -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<ResolvedIndyEntry>::base_offset_in_bytes());
add(cache, cache, index);
}
// Load object from cpool->resolved_references(index).
// Kills:
// - index

View File

@ -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<InterpreterMacroAssembler>(__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<ResolvedIndyEntry>::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);

View File

@ -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<InterpreterMacroAssembler>(__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);

View File

@ -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<ResolvedIndyEntry>::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;

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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<ResolvedIndyEntry>::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()));

View File

@ -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));

View File

@ -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<InterpreterMacroAssembler>(__FILE__, __LINE__, _masm)->
#else
#define __ _masm->
#define __ Disassembler::hook<InterpreterMacroAssembler>(__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.

View File

@ -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<InterpreterMacroAssembler>(__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)

View File

@ -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<ResolvedIndyEntry>::base_offset_in_bytes()));
}

View File

@ -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

View File

@ -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()) {

View File

@ -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)

View File

@ -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");

View File

@ -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) {

View File

@ -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, "<appendix>");
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, "<bsm>");

View File

@ -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, "<appendix>") == 0) {
obj = cp_cache_entry->appendix_if_resolved(cp);
obj = appendix;
} else if (strcmp(dyno_ref, "<adapter>") == 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, "<bsm>") == 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 {

View File

@ -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;
}

View File

@ -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 ? " " : ""),

View File

@ -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.

View File

@ -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 {

View File

@ -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)

View File

@ -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

View File

@ -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()
}

View File

@ -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

View File

@ -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);

View File

@ -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.

View File

@ -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<int> _invokedynamic_references_map; // for invokedynamic resolved refs
GrowableArray<int> _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<int> _invokedynamic_cp_cache_map;
// For patching.
GrowableArray<address>* _patch_invokedynamic_bcps;
GrowableArray<int>* _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<ResolvedIndyEntry> _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<address>(length / 4);
_patch_invokedynamic_refs = new GrowableArray<int>(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);

View File

@ -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,

View File

@ -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);
}

View File

@ -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

View File

@ -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;

View File

@ -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(), "");

View File

@ -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

View File

@ -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<ResolvedIndyEntry> 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<ResolvedIndyEntry>* resolved_indy_entries;
if (indy_entries.length()) {
resolved_indy_entries = MetadataFactory::new_array<ResolvedIndyEntry>(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<ConstantPoolCacheEntry>(data, _initial_entries);
if (_resolved_indy_entries)
MetadataFactory::free_array<ResolvedIndyEntry>(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 {

View File

@ -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<ResolvedIndyEntry>* _resolved_indy_entries;
CDS_ONLY(Array<ConstantPoolCacheEntry>* _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<ResolvedIndyEntry>* 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<ResolvedIndyEntry> indy_entries,
TRAPS);
int length() const { return _length; }
void metaspace_pointers_do(MetaspaceClosure* it);
@ -451,8 +442,18 @@ class ConstantPoolCache: public MetaspaceObj {
Array<u2>* reference_map() const { return _reference_map; }
void set_reference_map(Array<u2>* o) { _reference_map = o; }
Array<ResolvedIndyEntry>* 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;

View File

@ -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<ResolvedIndyEntry>* 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?");

View File

@ -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());
}

View File

@ -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

View File

@ -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;
}

View File

@ -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))

View File

@ -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},

View File

@ -221,6 +221,8 @@
nonstatic_field(ConstantPoolCache, _reference_map, Array<u2>*) \
nonstatic_field(ConstantPoolCache, _length, int) \
nonstatic_field(ConstantPoolCache, _constant_pool, ConstantPool*) \
nonstatic_field(ConstantPoolCache, _resolved_indy_entries, Array<ResolvedIndyEntry>*) \
nonstatic_field(ResolvedIndyEntry, _cpool_index, u2) \
volatile_nonstatic_field(InstanceKlass, _array_klasses, ObjArrayKlass*) \
nonstatic_field(InstanceKlass, _methods, Array<Method*>*) \
nonstatic_field(InstanceKlass, _default_methods, Array<Method*>*) \
@ -473,6 +475,8 @@
\
nonstatic_field(Array<Klass*>, _length, int) \
nonstatic_field(Array<Klass*>, _data[0], Klass*) \
nonstatic_field(Array<ResolvedIndyEntry>, _length, int) \
nonstatic_field(Array<ResolvedIndyEntry>, _data[0], ResolvedIndyEntry) \
\
/*******************/ \
/* GrowableArrays */ \
@ -1019,12 +1023,13 @@
/* Array<T> */ \
/************/ \
\
nonstatic_field(Array<int>, _length, int) \
unchecked_nonstatic_field(Array<int>, _data, sizeof(int)) \
unchecked_nonstatic_field(Array<u1>, _data, sizeof(u1)) \
unchecked_nonstatic_field(Array<u2>, _data, sizeof(u2)) \
unchecked_nonstatic_field(Array<Method*>, _data, sizeof(Method*)) \
unchecked_nonstatic_field(Array<Klass*>, _data, sizeof(Klass*)) \
nonstatic_field(Array<int>, _length, int) \
unchecked_nonstatic_field(Array<int>, _data, sizeof(int)) \
unchecked_nonstatic_field(Array<u1>, _data, sizeof(u1)) \
unchecked_nonstatic_field(Array<u2>, _data, sizeof(u2)) \
unchecked_nonstatic_field(Array<Method*>, _data, sizeof(Method*)) \
unchecked_nonstatic_field(Array<Klass*>, _data, sizeof(Klass*)) \
unchecked_nonstatic_field(Array<ResolvedIndyEntry>, _data, sizeof(ResolvedIndyEntry)) \
\
/*********************************/ \
/* java_lang_Class fields */ \
@ -1954,6 +1959,7 @@
declare_type(Array<u2>, MetaspaceObj) \
declare_type(Array<Klass*>, MetaspaceObj) \
declare_type(Array<Method*>, MetaspaceObj) \
declare_type(Array<ResolvedIndyEntry>, 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*) \

View File

@ -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();
}
}
}

View File

@ -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;

View File

@ -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 );

View File

@ -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<ResolvedIndyEntry>");
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;
}
}

View File

@ -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) { }
}

View File

@ -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

View File

@ -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++) {

View File

@ -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){