mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-20 07:58:55 +00:00
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:
parent
50a995f03a
commit
3fbbfd1749
@ -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));
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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()));
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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()));
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()) {
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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>");
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 ? " " : ""),
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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(), "");
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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?");
|
||||
|
||||
61
src/hotspot/share/oops/resolvedIndyEntry.cpp
Normal file
61
src/hotspot/share/oops/resolvedIndyEntry.cpp
Normal 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());
|
||||
}
|
||||
142
src/hotspot/share/oops/resolvedIndyEntry.hpp
Normal file
142
src/hotspot/share/oops/resolvedIndyEntry.hpp
Normal 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
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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},
|
||||
|
||||
@ -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*) \
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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) { }
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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++) {
|
||||
|
||||
@ -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){
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user