mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-12 14:39:49 +00:00
8271911: replay compilations of methods which use JSR292 (easy cases)
8012267: ciReplay: fails to resolve @SignaturePolymorphic methods in replay data 8012268: ciReplay: process_ciInstanceKlass: JVM_CONSTANT_MethodHandle not supported Reviewed-by: kvn, vlivanov
This commit is contained in:
parent
d414a88d88
commit
14a3ac09fe
@ -35,6 +35,7 @@
|
||||
#include "ci/ciSymbols.hpp"
|
||||
#include "ci/ciUtilities.inline.hpp"
|
||||
#include "classfile/javaClasses.hpp"
|
||||
#include "classfile/javaClasses.inline.hpp"
|
||||
#include "classfile/symbolTable.hpp"
|
||||
#include "classfile/systemDictionary.hpp"
|
||||
#include "classfile/vmClasses.hpp"
|
||||
@ -48,6 +49,7 @@
|
||||
#include "compiler/compileTask.hpp"
|
||||
#include "compiler/disassembler.hpp"
|
||||
#include "gc/shared/collectedHeap.inline.hpp"
|
||||
#include "interpreter/bytecodeStream.hpp"
|
||||
#include "interpreter/linkResolver.hpp"
|
||||
#include "jfr/jfrEvents.hpp"
|
||||
#include "logging/log.hpp"
|
||||
@ -64,6 +66,7 @@
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "prims/jvmtiExport.hpp"
|
||||
#include "prims/methodHandles.hpp"
|
||||
#include "runtime/fieldDescriptor.inline.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
#include "runtime/init.hpp"
|
||||
#include "runtime/reflection.hpp"
|
||||
@ -166,8 +169,73 @@ ciEnv::ciEnv(CompileTask* task)
|
||||
_jvmti_can_access_local_variables = false;
|
||||
_jvmti_can_post_on_exceptions = false;
|
||||
_jvmti_can_pop_frame = false;
|
||||
|
||||
_dyno_klasses = NULL;
|
||||
_dyno_locs = NULL;
|
||||
_dyno_name[0] = '\0';
|
||||
}
|
||||
|
||||
// Record components of a location descriptor string. Components are appended by the constructor and
|
||||
// removed by the destructor, like a stack, so scope matters. These location descriptors are used to
|
||||
// locate dynamic classes, and terminate at a Method* or oop field associated with dynamic/hidden class.
|
||||
//
|
||||
// Example use:
|
||||
//
|
||||
// {
|
||||
// RecordLocation fp(this, "field1");
|
||||
// // location: "field1"
|
||||
// { RecordLocation fp(this, " field2"); // location: "field1 field2" }
|
||||
// // location: "field1"
|
||||
// { RecordLocation fp(this, " field3"); // location: "field1 field3" }
|
||||
// // location: "field1"
|
||||
// }
|
||||
// // location: ""
|
||||
//
|
||||
// Examples of actual locations
|
||||
// @bci compiler/ciReplay/CiReplayBase$TestMain test (I)V 1 <appendix> argL0 ;
|
||||
// // resolve invokedynamic at bci 1 of TestMain.test, then read field "argL0" from appendix
|
||||
// @bci compiler/ciReplay/CiReplayBase$TestMain main ([Ljava/lang/String;)V 0 <appendix> form vmentry <vmtarget> ;
|
||||
// // resolve invokedynamic at bci 0 of TestMain.main, then read field "form.vmentry.method.vmtarget" from appendix
|
||||
// @cpi compiler/ciReplay/CiReplayBase$TestMain 56 form vmentry <vmtarget> ;
|
||||
// // resolve MethodHandle at cpi 56 of TestMain, then read field "vmentry.method.vmtarget" from resolved MethodHandle
|
||||
class RecordLocation {
|
||||
private:
|
||||
char* end;
|
||||
|
||||
ATTRIBUTE_PRINTF(3, 4)
|
||||
void push(ciEnv* ci, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
push_va(ci, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
public:
|
||||
ATTRIBUTE_PRINTF(3, 0)
|
||||
void push_va(ciEnv* ci, const char* fmt, va_list args) {
|
||||
char *e = ci->_dyno_name + strlen(ci->_dyno_name);
|
||||
char *m = ci->_dyno_name + ARRAY_SIZE(ci->_dyno_name) - 1;
|
||||
os::vsnprintf(e, m - e, fmt, args);
|
||||
assert(strlen(ci->_dyno_name) < (ARRAY_SIZE(ci->_dyno_name) - 1), "overflow");
|
||||
}
|
||||
|
||||
// append a new component
|
||||
ATTRIBUTE_PRINTF(3, 4)
|
||||
RecordLocation(ciEnv* ci, const char* fmt, ...) {
|
||||
end = ci->_dyno_name + strlen(ci->_dyno_name);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
push(ci, " ");
|
||||
push_va(ci, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// reset to previous state
|
||||
~RecordLocation() {
|
||||
*end = '\0';
|
||||
}
|
||||
};
|
||||
|
||||
ciEnv::ciEnv(Arena* arena) : _ciEnv_arena(mtCompiler) {
|
||||
ASSERT_IN_VM;
|
||||
|
||||
@ -222,6 +290,9 @@ ciEnv::ciEnv(Arena* arena) : _ciEnv_arena(mtCompiler) {
|
||||
_jvmti_can_access_local_variables = false;
|
||||
_jvmti_can_post_on_exceptions = false;
|
||||
_jvmti_can_pop_frame = false;
|
||||
|
||||
_dyno_klasses = NULL;
|
||||
_dyno_locs = NULL;
|
||||
}
|
||||
|
||||
ciEnv::~ciEnv() {
|
||||
@ -1190,10 +1261,340 @@ ciInstance* ciEnv::unloaded_ciinstance() {
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ciEnv::dump_replay_data*
|
||||
// Replay support
|
||||
|
||||
// Don't change thread state and acquire any locks.
|
||||
// Safe to call from VM error reporter.
|
||||
|
||||
// Lookup location descriptor for the class, if any.
|
||||
// Returns false if not found.
|
||||
bool ciEnv::dyno_loc(const InstanceKlass* ik, const char *&loc) const {
|
||||
bool found = false;
|
||||
int pos = _dyno_klasses->find_sorted<const InstanceKlass*, klass_compare>(ik, found);
|
||||
if (!found) {
|
||||
return false;
|
||||
}
|
||||
loc = _dyno_locs->at(pos);
|
||||
return found;
|
||||
}
|
||||
|
||||
// Associate the current location descriptor with the given class and record for later lookup.
|
||||
void ciEnv::set_dyno_loc(const InstanceKlass* ik) {
|
||||
const char *loc = os::strdup(_dyno_name);
|
||||
bool found = false;
|
||||
int pos = _dyno_klasses->find_sorted<const InstanceKlass*, klass_compare>(ik, found);
|
||||
if (found) {
|
||||
_dyno_locs->at_put(pos, loc);
|
||||
} else {
|
||||
_dyno_klasses->insert_before(pos, ik);
|
||||
_dyno_locs->insert_before(pos, loc);
|
||||
}
|
||||
}
|
||||
|
||||
// Associate the current location descriptor with the given class and record for later lookup.
|
||||
// If it turns out that there are multiple locations for the given class, that conflict should
|
||||
// be handled here. Currently we choose the first location found.
|
||||
void ciEnv::record_best_dyno_loc(const InstanceKlass* ik) {
|
||||
if (!ik->is_hidden()) {
|
||||
return;
|
||||
}
|
||||
const char *loc0;
|
||||
if (dyno_loc(ik, loc0)) {
|
||||
// TODO: found multiple references, see if we can improve
|
||||
if (Verbose) {
|
||||
tty->print_cr("existing call site @ %s for %s",
|
||||
loc0, ik->external_name());
|
||||
}
|
||||
} else {
|
||||
set_dyno_loc(ik);
|
||||
}
|
||||
}
|
||||
|
||||
// Look up the location descriptor for the given class and print it to the output stream.
|
||||
bool ciEnv::print_dyno_loc(outputStream* out, const InstanceKlass* ik) const {
|
||||
const char *loc;
|
||||
if (dyno_loc(ik, loc)) {
|
||||
out->print("%s", loc);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Look up the location descriptor for the given class and return it as a string.
|
||||
// Returns NULL if no location is found.
|
||||
const char *ciEnv::dyno_name(const InstanceKlass* ik) const {
|
||||
if (ik->is_hidden()) {
|
||||
stringStream ss;
|
||||
if (print_dyno_loc(&ss, ik)) {
|
||||
ss.print(" ;"); // add terminator
|
||||
const char* call_site = ss.as_string();
|
||||
return call_site;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Look up the location descriptor for the given class and return it as a string.
|
||||
// Returns the class name as a fallback if no location is found.
|
||||
const char *ciEnv::replay_name(ciKlass* k) const {
|
||||
if (k->is_instance_klass()) {
|
||||
return replay_name(k->as_instance_klass()->get_instanceKlass());
|
||||
}
|
||||
return k->name()->as_quoted_ascii();
|
||||
}
|
||||
|
||||
// Look up the location descriptor for the given class and return it as a string.
|
||||
// Returns the class name as a fallback if no location is found.
|
||||
const char *ciEnv::replay_name(const InstanceKlass* ik) const {
|
||||
const char* name = dyno_name(ik);
|
||||
if (name != NULL) {
|
||||
return name;
|
||||
}
|
||||
return ik->name()->as_quoted_ascii();
|
||||
}
|
||||
|
||||
// Process a java.lang.invoke.MemberName object and record any dynamic locations.
|
||||
void ciEnv::record_member(Thread* thread, oop member) {
|
||||
assert(java_lang_invoke_MemberName::is_instance(member), "!");
|
||||
// Check MemberName.clazz field
|
||||
oop clazz = java_lang_invoke_MemberName::clazz(member);
|
||||
if (clazz->klass()->is_instance_klass()) {
|
||||
RecordLocation fp(this, "clazz");
|
||||
InstanceKlass* ik = InstanceKlass::cast(clazz->klass());
|
||||
record_best_dyno_loc(ik);
|
||||
}
|
||||
// Check MemberName.method.vmtarget field
|
||||
Method* vmtarget = java_lang_invoke_MemberName::vmtarget(member);
|
||||
if (vmtarget != NULL) {
|
||||
RecordLocation fp2(this, "<vmtarget>");
|
||||
InstanceKlass* ik = vmtarget->method_holder();
|
||||
record_best_dyno_loc(ik);
|
||||
}
|
||||
}
|
||||
|
||||
// Read an object field. Lookup is done by name only.
|
||||
static inline oop obj_field(oop obj, const char* name) {
|
||||
return ciReplay::obj_field(obj, name);
|
||||
}
|
||||
|
||||
// Process a java.lang.invoke.LambdaForm object and record any dynamic locations.
|
||||
void ciEnv::record_lambdaform(Thread* thread, oop form) {
|
||||
assert(java_lang_invoke_LambdaForm::is_instance(form), "!");
|
||||
|
||||
{
|
||||
// Check LambdaForm.vmentry field
|
||||
oop member = java_lang_invoke_LambdaForm::vmentry(form);
|
||||
RecordLocation fp0(this, "vmentry");
|
||||
record_member(thread, member);
|
||||
}
|
||||
|
||||
// Check LambdaForm.names array
|
||||
objArrayOop names = (objArrayOop)obj_field(form, "names");
|
||||
if (names != NULL) {
|
||||
RecordLocation lp0(this, "names");
|
||||
int len = names->length();
|
||||
for (int i = 0; i < len; ++i) {
|
||||
oop name = names->obj_at(i);
|
||||
RecordLocation lp1(this, "%d", i);
|
||||
// Check LambdaForm.names[i].function field
|
||||
RecordLocation lp2(this, "function");
|
||||
oop function = obj_field(name, "function");
|
||||
if (function != NULL) {
|
||||
// Check LambdaForm.names[i].function.member field
|
||||
oop member = obj_field(function, "member");
|
||||
if (member != NULL) {
|
||||
RecordLocation lp3(this, "member");
|
||||
record_member(thread, member);
|
||||
}
|
||||
// Check LambdaForm.names[i].function.resolvedHandle field
|
||||
oop mh = obj_field(function, "resolvedHandle");
|
||||
if (mh != NULL) {
|
||||
RecordLocation lp3(this, "resolvedHandle");
|
||||
record_mh(thread, mh);
|
||||
}
|
||||
// Check LambdaForm.names[i].function.invoker field
|
||||
oop invoker = obj_field(function, "invoker");
|
||||
if (invoker != NULL) {
|
||||
RecordLocation lp3(this, "invoker");
|
||||
record_mh(thread, invoker);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process a java.lang.invoke.MethodHandle object and record any dynamic locations.
|
||||
void ciEnv::record_mh(Thread* thread, oop mh) {
|
||||
{
|
||||
// Check MethodHandle.form field
|
||||
oop form = java_lang_invoke_MethodHandle::form(mh);
|
||||
RecordLocation fp(this, "form");
|
||||
record_lambdaform(thread, form);
|
||||
}
|
||||
// Check DirectMethodHandle.member field
|
||||
if (java_lang_invoke_DirectMethodHandle::is_instance(mh)) {
|
||||
oop member = java_lang_invoke_DirectMethodHandle::member(mh);
|
||||
RecordLocation fp(this, "member");
|
||||
record_member(thread, member);
|
||||
} else {
|
||||
// Check <MethodHandle subclass>.argL0 field
|
||||
// Probably BoundMethodHandle.Species_L, but we only care if the field exists
|
||||
oop arg = obj_field(mh, "argL0");
|
||||
if (arg != NULL) {
|
||||
RecordLocation fp(this, "argL0");
|
||||
if (arg->klass()->is_instance_klass()) {
|
||||
InstanceKlass* ik2 = InstanceKlass::cast(arg->klass());
|
||||
record_best_dyno_loc(ik2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process an object found at an invokedynamic/invokehandle call site and record any dynamic locations.
|
||||
// Types currently supported are MethodHandle and CallSite.
|
||||
// The object is typically the "appendix" object, or Bootstrap Method (BSM) object.
|
||||
void ciEnv::record_call_site_obj(Thread* thread, const constantPoolHandle& pool, const Handle obj)
|
||||
{
|
||||
if (obj.not_null()) {
|
||||
if (java_lang_invoke_MethodHandle::is_instance(obj())) {
|
||||
record_mh(thread, obj());
|
||||
} else if (java_lang_invoke_ConstantCallSite::is_instance(obj())) {
|
||||
oop target = java_lang_invoke_CallSite::target(obj());
|
||||
if (target->klass()->is_instance_klass()) {
|
||||
RecordLocation fp(this, "target");
|
||||
InstanceKlass* ik = InstanceKlass::cast(target->klass());
|
||||
record_best_dyno_loc(ik);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process an adapter Method* found at an invokedynamic/invokehandle call site and record any dynamic locations.
|
||||
void ciEnv::record_call_site_method(Thread* thread, const constantPoolHandle& pool, Method* adapter) {
|
||||
InstanceKlass* holder = adapter->method_holder();
|
||||
if (!holder->is_hidden()) {
|
||||
return;
|
||||
}
|
||||
RecordLocation fp(this, "<adapter>");
|
||||
record_best_dyno_loc(holder);
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
// process the adapter
|
||||
Method* adapter = cp_cache_entry->f1_as_method();
|
||||
record_call_site_method(thread, cp, adapter);
|
||||
// process the appendix
|
||||
Handle appendix(thread, cp_cache_entry->appendix_if_resolved(cp));
|
||||
{
|
||||
RecordLocation fp(this, "<appendix>");
|
||||
record_call_site_obj(thread, cp, appendix);
|
||||
}
|
||||
// process the BSM
|
||||
int pool_index = cp_cache_entry->constant_pool_index();
|
||||
BootstrapInfo bootstrap_specifier(cp, pool_index, indy_index);
|
||||
oop bsm_oop = cp->resolve_possibly_cached_constant_at(bootstrap_specifier.bsm_index(), thread);
|
||||
Handle bsm(thread, bsm_oop);
|
||||
{
|
||||
RecordLocation fp(this, "<bsm>");
|
||||
record_call_site_obj(thread, cp, bsm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process an invokehandle call site and record any dynamic locations.
|
||||
void ciEnv::process_invokehandle(const constantPoolHandle &cp, int index, JavaThread* thread) {
|
||||
const int holder_index = cp->klass_ref_index_at(index);
|
||||
if (!cp->tag_at(holder_index).is_klass()) {
|
||||
return; // not resolved
|
||||
}
|
||||
Klass* holder = ConstantPool::klass_at_if_loaded(cp, holder_index);
|
||||
Symbol* name = cp->name_ref_at(index);
|
||||
if (MethodHandles::is_signature_polymorphic_name(holder, name)) {
|
||||
ConstantPoolCacheEntry* cp_cache_entry = cp->cache()->entry_at(cp->decode_cpcache_index(index));
|
||||
if (cp_cache_entry->is_resolved(Bytecodes::_invokehandle)) {
|
||||
// process the adapter
|
||||
Method* adapter = cp_cache_entry->f1_as_method();
|
||||
Handle appendix(thread, cp_cache_entry->appendix_if_resolved(cp));
|
||||
record_call_site_method(thread, cp, adapter);
|
||||
// process the appendix
|
||||
{
|
||||
RecordLocation fp(this, "<appendix>");
|
||||
record_call_site_obj(thread, cp, appendix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search the class hierarchy for dynamic classes reachable through dynamic call sites or
|
||||
// constant pool entries and record for future lookup.
|
||||
void ciEnv::find_dynamic_call_sites() {
|
||||
_dyno_klasses = new (arena()) GrowableArray<const InstanceKlass*>(arena(), 100, 0, NULL);
|
||||
_dyno_locs = new (arena()) GrowableArray<const char *>(arena(), 100, 0, NULL);
|
||||
|
||||
// Iterate over the class hierarchy
|
||||
for (ClassHierarchyIterator iter(vmClasses::Object_klass()); !iter.done(); iter.next()) {
|
||||
Klass* sub = iter.klass();
|
||||
if (sub->is_instance_klass()) {
|
||||
InstanceKlass *isub = InstanceKlass::cast(sub);
|
||||
InstanceKlass* ik = isub;
|
||||
if (!ik->is_linked()) {
|
||||
continue;
|
||||
}
|
||||
if (ik->is_hidden()) {
|
||||
continue;
|
||||
}
|
||||
JavaThread* thread = JavaThread::current();
|
||||
const constantPoolHandle pool(thread, ik->constants());
|
||||
|
||||
// Look for invokedynamic/invokehandle call sites
|
||||
for (int i = 0; i < ik->methods()->length(); ++i) {
|
||||
Method* m = ik->methods()->at(i);
|
||||
|
||||
BytecodeStream bcs(methodHandle(thread, m));
|
||||
while (!bcs.is_last_bytecode()) {
|
||||
Bytecodes::Code opcode = bcs.next();
|
||||
opcode = bcs.raw_code();
|
||||
switch (opcode) {
|
||||
case Bytecodes::_invokedynamic:
|
||||
case Bytecodes::_invokehandle: {
|
||||
RecordLocation fp(this, "@bci %s %s %s %d",
|
||||
ik->name()->as_quoted_ascii(),
|
||||
m->name()->as_quoted_ascii(), m->signature()->as_quoted_ascii(),
|
||||
bcs.bci());
|
||||
if (opcode == Bytecodes::_invokedynamic) {
|
||||
int index = bcs.get_index_u4();
|
||||
process_invokedynamic(pool, index, thread);
|
||||
} else {
|
||||
assert(opcode == Bytecodes::_invokehandle, "new switch label added?");
|
||||
int cp_cache_index = bcs.get_index_u2_cpcache();
|
||||
process_invokehandle(pool, cp_cache_index, thread);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look for MethodHandle contant pool entries
|
||||
RecordLocation fp(this, "@cpi %s", ik->name()->as_quoted_ascii());
|
||||
int len = pool->length();
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (pool->tag_at(i).is_method_handle()) {
|
||||
bool found_it;
|
||||
oop mh = pool->find_cached_constant_at(i, found_it, thread);
|
||||
if (mh != NULL) {
|
||||
RecordLocation fp(this, "%d", i);
|
||||
record_mh(thread, mh);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ciEnv::dump_compile_data(outputStream* out) {
|
||||
CompileTask* task = this->task();
|
||||
@ -1201,11 +1602,9 @@ void ciEnv::dump_compile_data(outputStream* out) {
|
||||
Method* method = task->method();
|
||||
int entry_bci = task->osr_bci();
|
||||
int comp_level = task->comp_level();
|
||||
out->print("compile %s %s %s %d %d",
|
||||
method->klass_name()->as_quoted_ascii(),
|
||||
method->name()->as_quoted_ascii(),
|
||||
method->signature()->as_quoted_ascii(),
|
||||
entry_bci, comp_level);
|
||||
out->print("compile ");
|
||||
get_method(method)->dump_name_as_ascii(out);
|
||||
out->print(" %d %d", entry_bci, comp_level);
|
||||
if (compiler_data() != NULL) {
|
||||
if (is_c2_compile(comp_level)) {
|
||||
#ifdef COMPILER2
|
||||
@ -1223,14 +1622,21 @@ void ciEnv::dump_compile_data(outputStream* out) {
|
||||
}
|
||||
}
|
||||
|
||||
void ciEnv::dump_replay_data_unsafe(outputStream* out) {
|
||||
// Called from VM error reporter, so be careful.
|
||||
// Don't safepoint or acquire any locks.
|
||||
//
|
||||
void ciEnv::dump_replay_data_helper(outputStream* out) {
|
||||
NoSafepointVerifier no_safepoint;
|
||||
ResourceMark rm;
|
||||
|
||||
#if INCLUDE_JVMTI
|
||||
out->print_cr("JvmtiExport can_access_local_variables %d", _jvmti_can_access_local_variables);
|
||||
out->print_cr("JvmtiExport can_hotswap_or_post_breakpoint %d", _jvmti_can_hotswap_or_post_breakpoint);
|
||||
out->print_cr("JvmtiExport can_post_on_exceptions %d", _jvmti_can_post_on_exceptions);
|
||||
#endif // INCLUDE_JVMTI
|
||||
|
||||
find_dynamic_call_sites();
|
||||
|
||||
GrowableArray<ciMetadata*>* objects = _factory->get_ci_metadata();
|
||||
out->print_cr("# %d ciObject found", objects->length());
|
||||
for (int i = 0; i < objects->length(); i++) {
|
||||
@ -1240,10 +1646,19 @@ void ciEnv::dump_replay_data_unsafe(outputStream* out) {
|
||||
out->flush();
|
||||
}
|
||||
|
||||
// Called from VM error reporter, so be careful.
|
||||
// Don't safepoint or acquire any locks.
|
||||
//
|
||||
void ciEnv::dump_replay_data_unsafe(outputStream* out) {
|
||||
GUARDED_VM_ENTRY(
|
||||
dump_replay_data_helper(out);
|
||||
)
|
||||
}
|
||||
|
||||
void ciEnv::dump_replay_data(outputStream* out) {
|
||||
GUARDED_VM_ENTRY(
|
||||
MutexLocker ml(Compile_lock);
|
||||
dump_replay_data_unsafe(out);
|
||||
dump_replay_data_helper(out);
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -44,9 +44,9 @@ class OopMapSet;
|
||||
// to the VM.
|
||||
class ciEnv : StackObj {
|
||||
CI_PACKAGE_ACCESS_TO
|
||||
|
||||
friend class CompileBroker;
|
||||
friend class Dependencies; // for get_object, during logging
|
||||
friend class RecordLocation;
|
||||
friend class PrepareExtraDataClosure;
|
||||
|
||||
private:
|
||||
@ -460,12 +460,49 @@ public:
|
||||
// RedefineClasses support
|
||||
void metadata_do(MetadataClosure* f) { _factory->metadata_do(f); }
|
||||
|
||||
// Replay support
|
||||
private:
|
||||
static int klass_compare(const InstanceKlass* const &ik1, const InstanceKlass* const &ik2) {
|
||||
if (ik1 > ik2) {
|
||||
return 1;
|
||||
} else if (ik1 < ik2) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
bool dyno_loc(const InstanceKlass* ik, const char *&loc) const;
|
||||
void set_dyno_loc(const InstanceKlass* ik);
|
||||
void record_best_dyno_loc(const InstanceKlass* ik);
|
||||
bool print_dyno_loc(outputStream* out, const InstanceKlass* ik) const;
|
||||
|
||||
GrowableArray<const InstanceKlass*>* _dyno_klasses;
|
||||
GrowableArray<const char *>* _dyno_locs;
|
||||
|
||||
#define MAX_DYNO_NAME_LENGTH 1024
|
||||
char _dyno_name[MAX_DYNO_NAME_LENGTH+1];
|
||||
|
||||
public:
|
||||
// Dump the compilation replay data for the ciEnv to the stream.
|
||||
void dump_replay_data(int compile_id);
|
||||
void dump_inline_data(int compile_id);
|
||||
void dump_replay_data(outputStream* out);
|
||||
void dump_replay_data_unsafe(outputStream* out);
|
||||
void dump_replay_data_helper(outputStream* out);
|
||||
void dump_compile_data(outputStream* out);
|
||||
|
||||
const char *dyno_name(const InstanceKlass* ik) const;
|
||||
const char *replay_name(const InstanceKlass* ik) const;
|
||||
const char *replay_name(ciKlass* i) const;
|
||||
|
||||
void record_lambdaform(Thread* thread, oop obj);
|
||||
void record_member(Thread* thread, oop obj);
|
||||
void record_mh(Thread* thread, oop obj);
|
||||
void record_call_site_obj(Thread* thread, const constantPoolHandle& pool, const Handle appendix);
|
||||
void record_call_site_method(Thread* thread, const constantPoolHandle& pool, Method* adapter);
|
||||
void process_invokedynamic(const constantPoolHandle &cp, int index, JavaThread* thread);
|
||||
void process_invokehandle(const constantPoolHandle &cp, int index, JavaThread* thread);
|
||||
void find_dynamic_call_sites();
|
||||
};
|
||||
|
||||
#endif // SHARE_CI_CIENV_HPP
|
||||
|
||||
@ -731,6 +731,9 @@ class StaticFinalFieldPrinter : public FieldClosure {
|
||||
}
|
||||
};
|
||||
|
||||
const char *ciInstanceKlass::replay_name() const {
|
||||
return CURRENT_ENV->replay_name(get_instanceKlass());
|
||||
}
|
||||
|
||||
void ciInstanceKlass::dump_replay_data(outputStream* out) {
|
||||
ResourceMark rm;
|
||||
@ -741,8 +744,18 @@ void ciInstanceKlass::dump_replay_data(outputStream* out) {
|
||||
// Try to record related loaded classes
|
||||
Klass* sub = ik->subklass();
|
||||
while (sub != NULL) {
|
||||
if (sub->is_instance_klass() && !sub->is_hidden()) {
|
||||
out->print_cr("instanceKlass %s", sub->name()->as_quoted_ascii());
|
||||
if (sub->is_instance_klass()) {
|
||||
InstanceKlass *isub = InstanceKlass::cast(sub);
|
||||
if (isub->is_hidden()) {
|
||||
const char *name = CURRENT_ENV->dyno_name(isub);
|
||||
if (name != NULL) {
|
||||
out->print_cr("instanceKlass %s # %s", name, sub->name()->as_quoted_ascii());
|
||||
} else {
|
||||
out->print_cr("# instanceKlass %s", sub->name()->as_quoted_ascii());
|
||||
}
|
||||
} else {
|
||||
out->print_cr("instanceKlass %s", sub->name()->as_quoted_ascii());
|
||||
}
|
||||
}
|
||||
sub = sub->next_sibling();
|
||||
}
|
||||
@ -751,7 +764,8 @@ void ciInstanceKlass::dump_replay_data(outputStream* out) {
|
||||
// tags will be validated for things which shouldn't change and
|
||||
// classes will be resolved if the tags indicate that they were
|
||||
// resolved at compile time.
|
||||
out->print("ciInstanceKlass %s %d %d %d", ik->name()->as_quoted_ascii(),
|
||||
const char *name = replay_name();
|
||||
out->print("ciInstanceKlass %s %d %d %d", name,
|
||||
is_linked(), is_initialized(), cp->length());
|
||||
for (int index = 1; index < cp->length(); index++) {
|
||||
out->print(" %d", cp->tags()->at(index));
|
||||
@ -760,7 +774,7 @@ void ciInstanceKlass::dump_replay_data(outputStream* out) {
|
||||
if (is_initialized()) {
|
||||
// Dump out the static final fields in case the compilation relies
|
||||
// on their value for correct replay.
|
||||
StaticFinalFieldPrinter sffp(out, ik->name()->as_quoted_ascii());
|
||||
StaticFinalFieldPrinter sffp(out, name);
|
||||
ik->do_local_static_fields(&sffp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -293,9 +293,14 @@ public:
|
||||
return !is_interface() && !is_abstract();
|
||||
}
|
||||
|
||||
// Replay support
|
||||
|
||||
// Dump the current state of this klass for compilation replay.
|
||||
virtual void dump_replay_data(outputStream* out);
|
||||
|
||||
// Return stable class name suitable for replay file.
|
||||
const char *replay_name() const;
|
||||
|
||||
#ifdef ASSERT
|
||||
bool debug_final_field_at(int offset);
|
||||
bool debug_stable_field_at(int offset);
|
||||
|
||||
@ -70,6 +70,7 @@ ciMethod::ciMethod(const methodHandle& h_m, ciInstanceKlass* holder) :
|
||||
_holder(holder)
|
||||
{
|
||||
assert(h_m() != NULL, "no null method");
|
||||
assert(_holder->get_instanceKlass() == h_m->method_holder(), "");
|
||||
|
||||
if (LogTouchedMethods) {
|
||||
h_m->log_touched(Thread::current());
|
||||
@ -1288,17 +1289,25 @@ ciMethodBlocks *ciMethod::get_method_blocks() {
|
||||
|
||||
#undef FETCH_FLAG_FROM_VM
|
||||
|
||||
void ciMethod::dump_name_as_ascii(outputStream* st) {
|
||||
Method* method = get_Method();
|
||||
void ciMethod::dump_name_as_ascii(outputStream* st, Method* method) {
|
||||
st->print("%s %s %s",
|
||||
method->klass_name()->as_quoted_ascii(),
|
||||
CURRENT_ENV->replay_name(method->method_holder()),
|
||||
method->name()->as_quoted_ascii(),
|
||||
method->signature()->as_quoted_ascii());
|
||||
}
|
||||
|
||||
void ciMethod::dump_name_as_ascii(outputStream* st) {
|
||||
Method* method = get_Method();
|
||||
dump_name_as_ascii(st, method);
|
||||
}
|
||||
|
||||
void ciMethod::dump_replay_data(outputStream* st) {
|
||||
ResourceMark rm;
|
||||
Method* method = get_Method();
|
||||
if (MethodHandles::is_signature_polymorphic_method(method)) {
|
||||
// ignore for now
|
||||
return;
|
||||
}
|
||||
MethodCounters* mcs = method->method_counters();
|
||||
st->print("ciMethod ");
|
||||
dump_name_as_ascii(st);
|
||||
|
||||
@ -365,6 +365,7 @@ class ciMethod : public ciMetadata {
|
||||
bool can_be_statically_bound(ciInstanceKlass* context) const;
|
||||
|
||||
// Replay data methods
|
||||
static void dump_name_as_ascii(outputStream* st, Method* method);
|
||||
void dump_name_as_ascii(outputStream* st);
|
||||
void dump_replay_data(outputStream* st);
|
||||
|
||||
|
||||
@ -647,7 +647,8 @@ void ciMethodData::dump_replay_data_type_helper(outputStream* out, int round, in
|
||||
if (round == 0) {
|
||||
count++;
|
||||
} else {
|
||||
out->print(" %d %s", (int)(dp_to_di(pdata->dp() + in_bytes(offset)) / sizeof(intptr_t)), k->name()->as_quoted_ascii());
|
||||
out->print(" %d %s", (int)(dp_to_di(pdata->dp() + in_bytes(offset)) / sizeof(intptr_t)),
|
||||
CURRENT_ENV->replay_name(k));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -703,13 +704,9 @@ void ciMethodData::dump_replay_data(outputStream* out) {
|
||||
ResourceMark rm;
|
||||
MethodData* mdo = get_MethodData();
|
||||
Method* method = mdo->method();
|
||||
Klass* holder = method->method_holder();
|
||||
out->print("ciMethodData %s %s %s %d %d",
|
||||
holder->name()->as_quoted_ascii(),
|
||||
method->name()->as_quoted_ascii(),
|
||||
method->signature()->as_quoted_ascii(),
|
||||
_state,
|
||||
current_mileage());
|
||||
out->print("ciMethodData ");
|
||||
ciMethod::dump_name_as_ascii(out, method);
|
||||
out->print(" %d %d", _state, current_mileage());
|
||||
|
||||
// dump the contents of the MDO header as raw data
|
||||
unsigned char* orig = (unsigned char*)&_orig;
|
||||
|
||||
@ -34,14 +34,18 @@
|
||||
#include "classfile/systemDictionary.hpp"
|
||||
#include "compiler/compilationPolicy.hpp"
|
||||
#include "compiler/compileBroker.hpp"
|
||||
#include "interpreter/linkResolver.hpp"
|
||||
#include "memory/allocation.inline.hpp"
|
||||
#include "memory/oopFactory.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "oops/constantPool.hpp"
|
||||
#include "oops/cpCache.inline.hpp"
|
||||
#include "oops/fieldStreams.inline.hpp"
|
||||
#include "oops/klass.inline.hpp"
|
||||
#include "oops/method.inline.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "prims/jvmtiExport.hpp"
|
||||
#include "prims/methodHandles.hpp"
|
||||
#include "runtime/fieldDescriptor.inline.hpp"
|
||||
#include "runtime/globals_extension.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
@ -257,7 +261,7 @@ class CompileReplay : public StackObj {
|
||||
}
|
||||
}
|
||||
|
||||
const char* parse_escaped_string() {
|
||||
char* parse_escaped_string() {
|
||||
char* result = parse_quoted_string();
|
||||
if (result != NULL) {
|
||||
unescape_string(result);
|
||||
@ -340,7 +344,7 @@ class CompileReplay : public StackObj {
|
||||
}
|
||||
|
||||
// Parse a possibly quoted version of a symbol into a symbolOop
|
||||
Symbol* parse_symbol(TRAPS) {
|
||||
Symbol* parse_symbol() {
|
||||
const char* str = parse_escaped_string();
|
||||
if (str != NULL) {
|
||||
Symbol* sym = SymbolTable::new_symbol(str);
|
||||
@ -349,9 +353,174 @@ class CompileReplay : public StackObj {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool parse_terminator() {
|
||||
char* terminator = parse_string();
|
||||
if (terminator != NULL && strcmp(terminator, ";") == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse a special hidden klass location syntax
|
||||
// syntax: @bci <klass> <name> <signature> <bci> <location>* ;
|
||||
// syntax: @cpi <klass> <cpi> <location>* ;
|
||||
Klass* parse_cp_ref(TRAPS) {
|
||||
JavaThread* thread = THREAD;
|
||||
oop obj = NULL;
|
||||
char* ref = parse_string();
|
||||
if (strcmp(ref, "bci") == 0) {
|
||||
Method* m = parse_method(CHECK_NULL);
|
||||
|
||||
InstanceKlass* ik = m->method_holder();
|
||||
const constantPoolHandle cp(Thread::current(), ik->constants());
|
||||
|
||||
// invokedynamic or invokehandle
|
||||
|
||||
methodHandle caller(Thread::current(), m);
|
||||
int bci = parse_int("bci");
|
||||
if (m->validate_bci(bci) != bci) {
|
||||
report_error("bad bci");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ik->link_class(CHECK_NULL);
|
||||
|
||||
Bytecode_invoke bytecode(caller, bci);
|
||||
int index = bytecode.index();
|
||||
|
||||
ConstantPoolCacheEntry* cp_cache_entry = NULL;
|
||||
CallInfo callInfo;
|
||||
Bytecodes::Code bc = bytecode.invoke_code();
|
||||
LinkResolver::resolve_invoke(callInfo, Handle(), cp, index, bc, CHECK_NULL);
|
||||
if (bytecode.is_invokedynamic()) {
|
||||
cp_cache_entry = cp->invokedynamic_cp_cache_entry_at(index);
|
||||
cp_cache_entry->set_dynamic_call(cp, callInfo);
|
||||
} else if (bytecode.is_invokehandle()) {
|
||||
#ifdef ASSERT
|
||||
Klass* holder = cp->klass_ref_at(index, CHECK_NULL);
|
||||
Symbol* name = cp->name_ref_at(index);
|
||||
assert(MethodHandles::is_signature_polymorphic_name(holder, name), "");
|
||||
#endif
|
||||
cp_cache_entry = cp->cache()->entry_at(cp->decode_cpcache_index(index));
|
||||
cp_cache_entry->set_method_handle(cp, callInfo);
|
||||
} else {
|
||||
report_error("no dynamic invoke found");
|
||||
return NULL;
|
||||
}
|
||||
char* dyno_ref = parse_string();
|
||||
if (strcmp(dyno_ref, "<appendix>") == 0) {
|
||||
obj = cp_cache_entry->appendix_if_resolved(cp);
|
||||
} else if (strcmp(dyno_ref, "<adapter>") == 0) {
|
||||
if (!parse_terminator()) {
|
||||
report_error("no dynamic invoke found");
|
||||
return NULL;
|
||||
}
|
||||
Method* adapter = cp_cache_entry->f1_as_method();
|
||||
if (adapter == NULL) {
|
||||
report_error("no adapter found");
|
||||
return NULL;
|
||||
}
|
||||
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(), thread);
|
||||
} else {
|
||||
report_error("unrecognized token");
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
// constant pool ref (MethodHandle)
|
||||
if (strcmp(ref, "cpi") != 0) {
|
||||
report_error("unexpected token");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Klass* k = parse_klass(CHECK_NULL);
|
||||
InstanceKlass* ik = InstanceKlass::cast(k);
|
||||
const constantPoolHandle cp(Thread::current(), ik->constants());
|
||||
|
||||
int cpi = parse_int("cpi");
|
||||
|
||||
if (cpi >= cp->length()) {
|
||||
report_error("bad cpi");
|
||||
return NULL;
|
||||
}
|
||||
if (!cp->tag_at(cpi).is_method_handle()) {
|
||||
report_error("no method handle found at cpi");
|
||||
return NULL;
|
||||
}
|
||||
{
|
||||
bool found_it;
|
||||
obj = cp->find_cached_constant_at(cpi, found_it, thread);
|
||||
}
|
||||
}
|
||||
Klass* k = NULL;
|
||||
if (obj != NULL) {
|
||||
skip_ws();
|
||||
// loop: read fields
|
||||
char* field = NULL;
|
||||
do {
|
||||
field = parse_string();
|
||||
if (field == NULL) {
|
||||
report_error("no field found");
|
||||
return NULL;
|
||||
}
|
||||
if (strcmp(field, ";") == 0) {
|
||||
break;
|
||||
}
|
||||
// raw Method*
|
||||
if (strcmp(field, "<vmtarget>") == 0) {
|
||||
Method* vmtarget = java_lang_invoke_MemberName::vmtarget(obj);
|
||||
k = (vmtarget == NULL) ? NULL : vmtarget->method_holder();
|
||||
if (k == NULL) {
|
||||
report_error("null vmtarget found");
|
||||
return NULL;
|
||||
}
|
||||
if (!parse_terminator()) {
|
||||
report_error("missing terminator");
|
||||
return NULL;
|
||||
}
|
||||
return k;
|
||||
}
|
||||
obj = ciReplay::obj_field(obj, field);
|
||||
// array
|
||||
if (obj != NULL && obj->is_objArray()) {
|
||||
objArrayOop arr = (objArrayOop)obj;
|
||||
int index = parse_int("index");
|
||||
if (index >= arr->length()) {
|
||||
report_error("bad array index");
|
||||
return NULL;
|
||||
}
|
||||
obj = arr->obj_at(index);
|
||||
}
|
||||
} while (obj != NULL);
|
||||
if (obj == NULL) {
|
||||
report_error("null field found");
|
||||
return NULL;
|
||||
}
|
||||
k = obj->klass();
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
// Parse a valid klass name and look it up
|
||||
// syntax: <name>
|
||||
// syntax: <constant pool ref>
|
||||
Klass* parse_klass(TRAPS) {
|
||||
const char* str = parse_escaped_string();
|
||||
skip_ws();
|
||||
// check for constant pool object reference (for a dynamic/hidden class)
|
||||
bool cp_ref = (*_bufptr == '@');
|
||||
if (cp_ref) {
|
||||
++_bufptr;
|
||||
Klass* k = parse_cp_ref(CHECK_NULL);
|
||||
if (k != NULL && !k->is_hidden()) {
|
||||
report_error("expected hidden class");
|
||||
return NULL;
|
||||
}
|
||||
return k;
|
||||
}
|
||||
char* str = parse_escaped_string();
|
||||
Symbol* klass_name = SymbolTable::new_symbol(str);
|
||||
if (klass_name != NULL) {
|
||||
Klass* k = NULL;
|
||||
@ -389,8 +558,8 @@ class CompileReplay : public StackObj {
|
||||
report_error("Can't find holder klass");
|
||||
return NULL;
|
||||
}
|
||||
Symbol* method_name = parse_symbol(CHECK_NULL);
|
||||
Symbol* method_signature = parse_symbol(CHECK_NULL);
|
||||
Symbol* method_name = parse_symbol();
|
||||
Symbol* method_signature = parse_symbol();
|
||||
Method* m = k->find_method(method_name, method_signature);
|
||||
if (m == NULL) {
|
||||
report_error("Can't find method");
|
||||
@ -679,12 +848,26 @@ class CompileReplay : public StackObj {
|
||||
}
|
||||
|
||||
// instanceKlass <name>
|
||||
// instanceKlass <constant pool ref> # <original hidden class name>
|
||||
//
|
||||
// Loads and initializes the klass 'name'. This can be used to
|
||||
// create particular class loading environments
|
||||
void process_instanceKlass(TRAPS) {
|
||||
// just load the referenced class
|
||||
Klass* k = parse_klass(CHECK);
|
||||
if (k == NULL) {
|
||||
return;
|
||||
}
|
||||
const char* comment = parse_string();
|
||||
bool is_comment = comment != NULL && strcmp(comment, "#") == 0;
|
||||
if (k->is_hidden() != is_comment) {
|
||||
report_error("hidden class with comment expected");
|
||||
return;
|
||||
}
|
||||
if (is_comment && Verbose) {
|
||||
const char* hidden = parse_string();
|
||||
tty->print_cr("Found %s for %s", k->name()->as_quoted_ascii(), hidden);
|
||||
}
|
||||
}
|
||||
|
||||
// ciInstanceKlass <name> <is_linked> <is_initialized> <length> tag*
|
||||
@ -1279,3 +1462,40 @@ bool ciReplay::is_loaded(Method* method) {
|
||||
return rec != NULL;
|
||||
}
|
||||
#endif // PRODUCT
|
||||
|
||||
oop ciReplay::obj_field(oop obj, Symbol* name) {
|
||||
InstanceKlass* ik = InstanceKlass::cast(obj->klass());
|
||||
|
||||
do {
|
||||
if (!ik->has_nonstatic_fields()) {
|
||||
ik = ik->java_super();
|
||||
continue;
|
||||
}
|
||||
|
||||
for (JavaFieldStream fs(ik); !fs.done(); fs.next()) {
|
||||
if (fs.access_flags().is_static()) {
|
||||
continue;
|
||||
}
|
||||
if (fs.name() == name) {
|
||||
int offset = fs.offset();
|
||||
#ifdef ASSERT
|
||||
fieldDescriptor fd = fs.field_descriptor();
|
||||
assert(fd.offset() == ik->field_offset(fd.index()), "!");
|
||||
#endif
|
||||
oop f = obj->obj_field(offset);
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
ik = ik->java_super();
|
||||
} while (ik != NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
oop ciReplay::obj_field(oop obj, const char *name) {
|
||||
Symbol* fname = SymbolTable::probe(name, (int)strlen(name));
|
||||
if (fname == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return obj_field(obj, fname);
|
||||
}
|
||||
|
||||
@ -120,8 +120,12 @@ class ciReplay {
|
||||
static bool should_not_inline(ciMethod* method);
|
||||
static bool should_inline(void* data, ciMethod* method, int bci, int inline_depth);
|
||||
static bool should_not_inline(void* data, ciMethod* method, int bci, int inline_depth);
|
||||
|
||||
#endif
|
||||
|
||||
public:
|
||||
static oop obj_field(oop obj, Symbol* name);
|
||||
static oop obj_field(oop obj, const char *name);
|
||||
|
||||
};
|
||||
|
||||
#endif // SHARE_CI_CIREPLAY_HPP
|
||||
|
||||
@ -91,6 +91,7 @@ public abstract class CiReplayBase {
|
||||
}
|
||||
|
||||
static void test(int i) {
|
||||
i += ((Lambda)(() -> 0)).value();
|
||||
if ((i % 1000) == 0) {
|
||||
System.out.println("Hello World!");
|
||||
}
|
||||
|
||||
@ -51,6 +51,7 @@ public class TestInlining extends CiReplayBase {
|
||||
|
||||
@Override
|
||||
public void testAction() {
|
||||
positiveTest(TIERED_DISABLED_VM_OPTION);
|
||||
try {
|
||||
Path replayFilePath = Paths.get(REPLAY_FILE_NAME);
|
||||
List<String> replayContent = Files.readAllLines(replayFilePath);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user