8220623: [JVMCI] Update JVMCI to support JVMCI based Compiler compiled into shared library

Reviewed-by: dnsimon, never, stefank, rehn, neliasso, dholmes, kbarrett, coleenp
This commit is contained in:
Vladimir Kozlov 2019-05-01 12:31:29 -07:00
parent f9bbbb6e27
commit e9c523ae5f
173 changed files with 12881 additions and 5297 deletions

View file

@ -64,7 +64,7 @@
#include "utilities/resourceHash.hpp"
#include "utilities/xmlstream.hpp"
#if INCLUDE_JVMCI
#include "jvmci/jvmciJavaClasses.hpp"
#include "jvmci/jvmciRuntime.hpp"
#endif
#ifdef DTRACE_ENABLED
@ -112,6 +112,10 @@ struct java_nmethod_stats_struct {
int dependencies_size;
int handler_table_size;
int nul_chk_table_size;
#if INCLUDE_JVMCI
int speculations_size;
int jvmci_data_size;
#endif
int oops_size;
int metadata_size;
@ -129,6 +133,10 @@ struct java_nmethod_stats_struct {
dependencies_size += nm->dependencies_size();
handler_table_size += nm->handler_table_size();
nul_chk_table_size += nm->nul_chk_table_size();
#if INCLUDE_JVMCI
speculations_size += nm->speculations_size();
jvmci_data_size += nm->jvmci_data_size();
#endif
}
void print_nmethod_stats(const char* name) {
if (nmethod_count == 0) return;
@ -146,6 +154,10 @@ struct java_nmethod_stats_struct {
if (dependencies_size != 0) tty->print_cr(" dependencies = %d", dependencies_size);
if (handler_table_size != 0) tty->print_cr(" handler table = %d", handler_table_size);
if (nul_chk_table_size != 0) tty->print_cr(" nul chk table = %d", nul_chk_table_size);
#if INCLUDE_JVMCI
if (speculations_size != 0) tty->print_cr(" speculations = %d", speculations_size);
if (jvmci_data_size != 0) tty->print_cr(" JVMCI data = %d", jvmci_data_size);
#endif
}
};
@ -426,11 +438,6 @@ void nmethod::init_defaults() {
#if INCLUDE_RTM_OPT
_rtm_state = NoRTM;
#endif
#if INCLUDE_JVMCI
_jvmci_installed_code = NULL;
_speculation_log = NULL;
_jvmci_installed_code_triggers_invalidation = false;
#endif
}
nmethod* nmethod::new_native_nmethod(const methodHandle& method,
@ -483,8 +490,11 @@ nmethod* nmethod::new_nmethod(const methodHandle& method,
AbstractCompiler* compiler,
int comp_level
#if INCLUDE_JVMCI
, jweak installed_code,
jweak speculationLog
, char* speculations,
int speculations_len,
int nmethod_mirror_index,
const char* nmethod_mirror_name,
FailedSpeculation** failed_speculations
#endif
)
{
@ -493,12 +503,19 @@ nmethod* nmethod::new_nmethod(const methodHandle& method,
// create nmethod
nmethod* nm = NULL;
{ MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
#if INCLUDE_JVMCI
int jvmci_data_size = !compiler->is_jvmci() ? 0 : JVMCINMethodData::compute_size(nmethod_mirror_name);
#endif
int nmethod_size =
CodeBlob::allocation_size(code_buffer, sizeof(nmethod))
+ adjust_pcs_size(debug_info->pcs_size())
+ align_up((int)dependencies->size_in_bytes(), oopSize)
+ align_up(handler_table->size_in_bytes() , oopSize)
+ align_up(nul_chk_table->size_in_bytes() , oopSize)
#if INCLUDE_JVMCI
+ align_up(speculations_len , oopSize)
+ align_up(jvmci_data_size , oopSize)
#endif
+ align_up(debug_info->data_size() , oopSize);
nm = new (nmethod_size, comp_level)
@ -510,12 +527,19 @@ nmethod* nmethod::new_nmethod(const methodHandle& method,
compiler,
comp_level
#if INCLUDE_JVMCI
, installed_code,
speculationLog
, speculations,
speculations_len,
jvmci_data_size
#endif
);
if (nm != NULL) {
#if INCLUDE_JVMCI
if (compiler->is_jvmci()) {
// Initialize the JVMCINMethodData object inlined into nm
nm->jvmci_nmethod_data()->initialize(nmethod_mirror_index, nmethod_mirror_name, failed_speculations);
}
#endif
// To make dependency checking during class loading fast, record
// the nmethod dependencies in the classes it is dependent on.
// This allows the dependency checking code to simply walk the
@ -591,7 +615,13 @@ nmethod::nmethod(
_dependencies_offset = _scopes_pcs_offset;
_handler_table_offset = _dependencies_offset;
_nul_chk_table_offset = _handler_table_offset;
#if INCLUDE_JVMCI
_speculations_offset = _nul_chk_table_offset;
_jvmci_data_offset = _speculations_offset;
_nmethod_end_offset = _jvmci_data_offset;
#else
_nmethod_end_offset = _nul_chk_table_offset;
#endif
_compile_id = compile_id;
_comp_level = CompLevel_none;
_entry_point = code_begin() + offsets->value(CodeOffsets::Entry);
@ -667,8 +697,9 @@ nmethod::nmethod(
AbstractCompiler* compiler,
int comp_level
#if INCLUDE_JVMCI
, jweak installed_code,
jweak speculation_log
, char* speculations,
int speculations_len,
int jvmci_data_size
#endif
)
: CompiledMethod(method, "nmethod", type, nmethod_size, sizeof(nmethod), code_buffer, offsets->value(CodeOffsets::Frame_Complete), frame_size, oop_maps, false),
@ -697,15 +728,6 @@ nmethod::nmethod(
set_ctable_begin(header_begin() + _consts_offset);
#if INCLUDE_JVMCI
_jvmci_installed_code = installed_code;
_speculation_log = speculation_log;
oop obj = JNIHandles::resolve(installed_code);
if (obj == NULL || (obj->is_a(HotSpotNmethod::klass()) && HotSpotNmethod::isDefault(obj))) {
_jvmci_installed_code_triggers_invalidation = false;
} else {
_jvmci_installed_code_triggers_invalidation = true;
}
if (compiler->is_jvmci()) {
// JVMCI might not produce any stub sections
if (offsets->value(CodeOffsets::Exceptions) != -1) {
@ -735,10 +757,10 @@ nmethod::nmethod(
_deopt_mh_handler_begin = (address) this + _stub_offset + offsets->value(CodeOffsets::DeoptMH);
} else {
_deopt_mh_handler_begin = NULL;
}
#if INCLUDE_JVMCI
}
#endif
}
if (offsets->value(CodeOffsets::UnwindHandler) != -1) {
_unwind_handler_offset = code_offset() + offsets->value(CodeOffsets::UnwindHandler);
} else {
@ -753,7 +775,13 @@ nmethod::nmethod(
_dependencies_offset = _scopes_pcs_offset + adjust_pcs_size(debug_info->pcs_size());
_handler_table_offset = _dependencies_offset + align_up((int)dependencies->size_in_bytes (), oopSize);
_nul_chk_table_offset = _handler_table_offset + align_up(handler_table->size_in_bytes(), oopSize);
#if INCLUDE_JVMCI
_speculations_offset = _nul_chk_table_offset + align_up(nul_chk_table->size_in_bytes(), oopSize);
_jvmci_data_offset = _speculations_offset + align_up(speculations_len, oopSize);
_nmethod_end_offset = _jvmci_data_offset + align_up(jvmci_data_size, oopSize);
#else
_nmethod_end_offset = _nul_chk_table_offset + align_up(nul_chk_table->size_in_bytes(), oopSize);
#endif
_entry_point = code_begin() + offsets->value(CodeOffsets::Entry);
_verified_entry_point = code_begin() + offsets->value(CodeOffsets::Verified_Entry);
_osr_entry_point = code_begin() + offsets->value(CodeOffsets::OSR_Entry);
@ -779,6 +807,13 @@ nmethod::nmethod(
handler_table->copy_to(this);
nul_chk_table->copy_to(this);
#if INCLUDE_JVMCI
// Copy speculations to nmethod
if (speculations_size() != 0) {
memcpy(speculations_begin(), speculations, speculations_len);
}
#endif
// we use the information of entry points to find out if a method is
// static or non static
assert(compiler->is_c2() || compiler->is_jvmci() ||
@ -798,13 +833,14 @@ void nmethod::log_identity(xmlStream* log) const {
log->print(" level='%d'", comp_level());
}
#if INCLUDE_JVMCI
char buffer[O_BUFLEN];
char* jvmci_name = jvmci_installed_code_name(buffer, O_BUFLEN);
if (jvmci_nmethod_data() != NULL) {
const char* jvmci_name = jvmci_nmethod_data()->name();
if (jvmci_name != NULL) {
log->print(" jvmci_installed_code_name='");
log->print(" jvmci_mirror_name='");
log->text("%s", jvmci_name);
log->print("'");
}
}
#endif
}
@ -1115,13 +1151,6 @@ void nmethod::make_unloaded() {
// Log the unloading.
log_state_change();
#if INCLUDE_JVMCI
// The method can only be unloaded after the pointer to the installed code
// Java wrapper is no longer alive. Here we need to clear out this weak
// reference to the dead object.
maybe_invalidate_installed_code();
#endif
// The Method* is gone at this point
assert(_method == NULL, "Tautology");
@ -1134,6 +1163,15 @@ void nmethod::make_unloaded() {
// concurrent nmethod unloading. Therefore, there is no need for
// acquire on the loader side.
OrderAccess::release_store(&_state, (signed char)unloaded);
#if INCLUDE_JVMCI
// Clear the link between this nmethod and a HotSpotNmethod mirror
JVMCINMethodData* nmethod_data = jvmci_nmethod_data();
if (nmethod_data != NULL) {
nmethod_data->invalidate_nmethod_mirror(this);
nmethod_data->clear_nmethod_mirror(this);
}
#endif
}
void nmethod::invalidate_osr_method() {
@ -1265,13 +1303,18 @@ bool nmethod::make_not_entrant_or_zombie(int state) {
// Log the transition once
log_state_change();
// Invalidate while holding the patching lock
JVMCI_ONLY(maybe_invalidate_installed_code());
// Remove nmethod from method.
unlink_from_method(false /* already owns Patching_lock */);
} // leave critical region under Patching_lock
#if INCLUDE_JVMCI
// Invalidate can't occur while holding the Patching lock
JVMCINMethodData* nmethod_data = jvmci_nmethod_data();
if (nmethod_data != NULL) {
nmethod_data->invalidate_nmethod_mirror(this);
}
#endif
#ifdef ASSERT
if (is_osr_method() && method() != NULL) {
// Make sure osr nmethod is invalidated, i.e. not on the list
@ -1297,6 +1340,14 @@ bool nmethod::make_not_entrant_or_zombie(int state) {
flush_dependencies(/*delete_immediately*/true);
}
#if INCLUDE_JVMCI
// Now that the nmethod has been unregistered, it's
// safe to clear the HotSpotNmethod mirror oop.
if (nmethod_data != NULL) {
nmethod_data->clear_nmethod_mirror(this);
}
#endif
// Clear ICStubs to prevent back patching stubs of zombie or flushed
// nmethods during the next safepoint (see ICStub::finalize), as well
// as to free up CompiledICHolder resources.
@ -1324,7 +1375,7 @@ bool nmethod::make_not_entrant_or_zombie(int state) {
assert(state == not_entrant, "other cases may need to be handled differently");
}
if (TraceCreateZombies) {
if (TraceCreateZombies && state == zombie) {
ResourceMark m;
tty->print_cr("nmethod <" INTPTR_FORMAT "> %s code made %s", p2i(this), this->method() ? this->method()->name_and_sig_as_C_string() : "null", (state == not_entrant) ? "not entrant" : "zombie");
}
@ -1362,11 +1413,6 @@ void nmethod::flush() {
ec = next;
}
#if INCLUDE_JVMCI
assert(_jvmci_installed_code == NULL, "should have been nulled out when transitioned to zombie");
assert(_speculation_log == NULL, "should have been nulled out when transitioned to zombie");
#endif
Universe::heap()->flush_nmethod(this);
CodeBlob::flush();
@ -1660,17 +1706,6 @@ void nmethod::do_unloading(bool unloading_occurred) {
if (is_unloading()) {
make_unloaded();
} else {
#if INCLUDE_JVMCI
if (_jvmci_installed_code != NULL) {
if (JNIHandles::is_global_weak_cleared(_jvmci_installed_code)) {
if (_jvmci_installed_code_triggers_invalidation) {
make_not_entrant();
}
clear_jvmci_installed_code();
}
}
#endif
guarantee(unload_nmethod_caches(unloading_occurred),
"Should not need transition stubs");
}
@ -2066,7 +2101,7 @@ void nmethodLocker::lock_nmethod(CompiledMethod* cm, bool zombie_ok) {
if (cm->is_aot()) return; // FIXME: Revisit once _lock_count is added to aot_method
nmethod* nm = cm->as_nmethod();
Atomic::inc(&nm->_lock_count);
assert(zombie_ok || !nm->is_zombie(), "cannot lock a zombie method");
assert(zombie_ok || !nm->is_zombie(), "cannot lock a zombie method: %p", nm);
}
void nmethodLocker::unlock_nmethod(CompiledMethod* cm) {
@ -2275,6 +2310,16 @@ void nmethod::print() const {
p2i(nul_chk_table_begin()),
p2i(nul_chk_table_end()),
nul_chk_table_size());
#if INCLUDE_JVMCI
if (speculations_size () > 0) tty->print_cr(" speculations [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
p2i(speculations_begin()),
p2i(speculations_end()),
speculations_size());
if (jvmci_data_size () > 0) tty->print_cr(" JVMCI data [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
p2i(jvmci_data_begin()),
p2i(jvmci_data_end()),
jvmci_data_size());
#endif
}
#ifndef PRODUCT
@ -2857,115 +2902,18 @@ void nmethod::print_statistics() {
#endif // !PRODUCT
#if INCLUDE_JVMCI
void nmethod::clear_jvmci_installed_code() {
assert_locked_or_safepoint(Patching_lock);
if (_jvmci_installed_code != NULL) {
JNIHandles::destroy_weak_global(_jvmci_installed_code);
_jvmci_installed_code = NULL;
void nmethod::update_speculation(JavaThread* thread) {
jlong speculation = thread->pending_failed_speculation();
if (speculation != 0) {
guarantee(jvmci_nmethod_data() != NULL, "failed speculation in nmethod without failed speculation list");
jvmci_nmethod_data()->add_failed_speculation(this, speculation);
thread->set_pending_failed_speculation(0);
}
}
void nmethod::clear_speculation_log() {
assert_locked_or_safepoint(Patching_lock);
if (_speculation_log != NULL) {
JNIHandles::destroy_weak_global(_speculation_log);
_speculation_log = NULL;
}
}
void nmethod::maybe_invalidate_installed_code() {
if (!is_compiled_by_jvmci()) {
return;
}
assert(Patching_lock->is_locked() ||
SafepointSynchronize::is_at_safepoint(), "should be performed under a lock for consistency");
oop installed_code = JNIHandles::resolve(_jvmci_installed_code);
if (installed_code != NULL) {
// Update the values in the InstalledCode instance if it still refers to this nmethod
nmethod* nm = (nmethod*)InstalledCode::address(installed_code);
if (nm == this) {
if (!is_alive() || is_unloading()) {
// Break the link between nmethod and InstalledCode such that the nmethod
// can subsequently be flushed safely. The link must be maintained while
// the method could have live activations since invalidateInstalledCode
// might want to invalidate all existing activations.
InstalledCode::set_address(installed_code, 0);
InstalledCode::set_entryPoint(installed_code, 0);
} else if (is_not_entrant()) {
// Remove the entry point so any invocation will fail but keep
// the address link around that so that existing activations can
// be invalidated.
InstalledCode::set_entryPoint(installed_code, 0);
}
}
}
if (!is_alive() || is_unloading()) {
// Clear these out after the nmethod has been unregistered and any
// updates to the InstalledCode instance have been performed.
clear_jvmci_installed_code();
clear_speculation_log();
}
}
void nmethod::invalidate_installed_code(Handle installedCode, TRAPS) {
if (installedCode() == NULL) {
THROW(vmSymbols::java_lang_NullPointerException());
}
jlong nativeMethod = InstalledCode::address(installedCode);
nmethod* nm = (nmethod*)nativeMethod;
if (nm == NULL) {
// Nothing to do
return;
}
nmethodLocker nml(nm);
#ifdef ASSERT
{
MutexLocker pl(Patching_lock, Mutex::_no_safepoint_check_flag);
// This relationship can only be checked safely under a lock
assert(!nm->is_alive() || nm->is_unloading() || nm->jvmci_installed_code() == installedCode(), "sanity check");
}
#endif
if (nm->is_alive()) {
// Invalidating the InstalledCode means we want the nmethod
// to be deoptimized.
nm->mark_for_deoptimization();
VM_Deoptimize op;
VMThread::execute(&op);
}
// Multiple threads could reach this point so we now need to
// lock and re-check the link to the nmethod so that only one
// thread clears it.
MutexLocker pl(Patching_lock, Mutex::_no_safepoint_check_flag);
if (InstalledCode::address(installedCode) == nativeMethod) {
InstalledCode::set_address(installedCode, 0);
}
}
oop nmethod::jvmci_installed_code() {
return JNIHandles::resolve(_jvmci_installed_code);
}
oop nmethod::speculation_log() {
return JNIHandles::resolve(_speculation_log);
}
char* nmethod::jvmci_installed_code_name(char* buf, size_t buflen) const {
if (!this->is_compiled_by_jvmci()) {
return NULL;
}
oop installed_code = JNIHandles::resolve(_jvmci_installed_code);
if (installed_code != NULL) {
oop installed_code_name = NULL;
if (installed_code->is_a(InstalledCode::klass())) {
installed_code_name = InstalledCode::name(installed_code);
}
if (installed_code_name != NULL) {
return java_lang_String::as_utf8_string(installed_code_name, buf, (int)buflen);
}
const char* nmethod::jvmci_name() {
if (jvmci_nmethod_data() != NULL) {
return jvmci_nmethod_data()->name();
}
return NULL;
}