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:
Dean Long 2021-09-03 23:23:10 +00:00
parent d414a88d88
commit 14a3ac09fe
11 changed files with 736 additions and 32 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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