< prev index next > src/hotspot/share/ci/ciEnv.cpp
Print this page
* questions.
*
*/
#include "precompiled.hpp"
+ #include "cds/archiveBuilder.hpp"
+ #include "cds/cdsConfig.hpp"
#include "ci/ciConstant.hpp"
#include "ci/ciEnv.hpp"
#include "ci/ciField.hpp"
#include "ci/ciInstance.hpp"
#include "ci/ciInstanceKlass.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/vmClasses.hpp"
#include "classfile/vmSymbols.hpp"
#include "code/codeCache.hpp"
#include "code/scopeDesc.hpp"
+ #include "code/SCCache.hpp"
#include "compiler/compilationLog.hpp"
#include "compiler/compilationPolicy.hpp"
#include "compiler/compileBroker.hpp"
#include "compiler/compilerEvent.hpp"
#include "compiler/compileLog.hpp"
#include "compiler/compileTask.hpp"
#include "compiler/disassembler.hpp"
#include "gc/shared/collectedHeap.inline.hpp"
+ #include "gc/shared/barrierSetNMethod.hpp"
#include "interpreter/bytecodeStream.hpp"
#include "interpreter/linkResolver.hpp"
#include "jfr/jfrEvents.hpp"
#include "jvm.h"
#include "logging/log.hpp"
#include "oops/objArrayKlass.hpp"
#include "oops/objArrayOop.inline.hpp"
#include "oops/oop.inline.hpp"
#include "oops/resolvedIndyEntry.hpp"
#include "oops/symbolHandle.hpp"
+ #include "oops/trainingData.hpp"
#include "prims/jvmtiExport.hpp"
#include "prims/methodHandles.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
+ #include "runtime/flags/flagSetting.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/init.hpp"
#include "runtime/javaThread.hpp"
#include "runtime/jniHandles.inline.hpp"
#include "runtime/reflection.hpp"
_jvmti_can_hotswap_or_post_breakpoint = false;
_jvmti_can_access_local_variables = false;
_jvmti_can_post_on_exceptions = false;
_jvmti_can_pop_frame = false;
+ _scc_clinit_barriers_entry = nullptr;
+
_dyno_klasses = nullptr;
_dyno_locs = nullptr;
_dyno_name[0] = '\0';
}
_jvmti_can_hotswap_or_post_breakpoint = false;
_jvmti_can_access_local_variables = false;
_jvmti_can_post_on_exceptions = false;
_jvmti_can_pop_frame = false;
+ _scc_clinit_barriers_entry = nullptr;
+
_dyno_klasses = nullptr;
_dyno_locs = nullptr;
}
ciEnv::~ciEnv() {
ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
int index, int obj_index,
ciInstanceKlass* accessor) {
if (obj_index >= 0) {
ciConstant con = get_resolved_constant(cpool, obj_index);
! if (con.is_valid()) {
return con;
}
}
constantTag tag = cpool->tag_at(index);
if (tag.is_int()) {
ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
int index, int obj_index,
ciInstanceKlass* accessor) {
if (obj_index >= 0) {
ciConstant con = get_resolved_constant(cpool, obj_index);
! if (con.should_be_constant()) {
return con;
}
}
constantTag tag = cpool->tag_at(index);
if (tag.is_int()) {
if (holder_is_accessible) { // Our declared holder is loaded.
constantTag tag = cpool->tag_ref_at(index, bc);
assert(accessor->get_instanceKlass() == cpool->pool_holder(), "not the pool holder?");
Method* m = lookup_method(accessor, holder, name_sym, sig_sym, bc, tag);
! if (m != nullptr &&
! (bc == Bytecodes::_invokestatic
! ? m->method_holder()->is_not_initialized()
! : !m->method_holder()->is_loaded())) {
! m = nullptr;
}
if (m != nullptr && ReplayCompiles && !ciReplay::is_loaded(m)) {
m = nullptr;
}
if (m != nullptr) {
if (holder_is_accessible) { // Our declared holder is loaded.
constantTag tag = cpool->tag_ref_at(index, bc);
assert(accessor->get_instanceKlass() == cpool->pool_holder(), "not the pool holder?");
Method* m = lookup_method(accessor, holder, name_sym, sig_sym, bc, tag);
! if (m != nullptr) {
! ciInstanceKlass* cik = get_instance_klass(m->method_holder());
! if ((bc == Bytecodes::_invokestatic && cik->is_not_initialized()) || !cik->is_loaded()) {
! m = nullptr;
! }
}
if (m != nullptr && ReplayCompiles && !ciReplay::is_loaded(m)) {
m = nullptr;
}
if (m != nullptr) {
// ciEnv::validate_compile_task_dependencies
//
// Check for changes during compilation (e.g. class loads, evolution,
// breakpoints, call site invalidation).
void ciEnv::validate_compile_task_dependencies(ciMethod* target) {
! if (failing()) return; // no need for further checks
Dependencies::DepType result = dependencies()->validate_dependencies(_task);
if (result != Dependencies::end_marker) {
if (result == Dependencies::call_site_target_value) {
_inc_decompile_count_on_failure = false;
// ciEnv::validate_compile_task_dependencies
//
// Check for changes during compilation (e.g. class loads, evolution,
// breakpoints, call site invalidation).
void ciEnv::validate_compile_task_dependencies(ciMethod* target) {
! assert(!failing(), "should not call this when failing");
Dependencies::DepType result = dependencies()->validate_dependencies(_task);
if (result != Dependencies::end_marker) {
if (result == Dependencies::call_site_target_value) {
_inc_decompile_count_on_failure = false;
int frame_words,
OopMapSet* oop_map_set,
ExceptionHandlerTable* handler_table,
ImplicitExceptionTable* inc_table,
AbstractCompiler* compiler,
bool has_unsafe_access,
bool has_wide_vectors,
bool has_monitors,
bool has_scoped_access,
! int immediate_oops_patched) {
VM_ENTRY_MARK;
nmethod* nm = nullptr;
{
methodHandle method(THREAD, target->get_Method());
// We require method counters to store some method state (max compilation levels) required by the compilation policy.
! if (method->get_method_counters(THREAD) == nullptr) {
record_failure("can't create method counters");
// All buffers in the CodeBuffer are allocated in the CodeCache.
// If the code buffer is created on each compile attempt
// as in C2, then it must be freed.
code_buffer->free_blob();
return;
}
// Check if memory should be freed before allocation
int frame_words,
OopMapSet* oop_map_set,
ExceptionHandlerTable* handler_table,
ImplicitExceptionTable* inc_table,
AbstractCompiler* compiler,
+ bool has_clinit_barriers,
+ bool for_preload,
bool has_unsafe_access,
bool has_wide_vectors,
bool has_monitors,
bool has_scoped_access,
! int immediate_oops_patched,
+ bool install_code,
+ SCCEntry* scc_entry) {
VM_ENTRY_MARK;
nmethod* nm = nullptr;
{
methodHandle method(THREAD, target->get_Method());
+ bool preload = task()->preload(); // Code is preloaded before Java method execution
// We require method counters to store some method state (max compilation levels) required by the compilation policy.
! if (!preload && method->get_method_counters(THREAD) == nullptr) {
record_failure("can't create method counters");
// All buffers in the CodeBuffer are allocated in the CodeCache.
// If the code buffer is created on each compile attempt
// as in C2, then it must be freed.
+ // But keep shared code.
code_buffer->free_blob();
return;
}
// Check if memory should be freed before allocation
// and invalidating our dependencies until we install this method.
// No safepoints are allowed. Otherwise, class redefinition can occur in between.
MutexLocker ml(Compile_lock);
NoSafepointVerifier nsv;
+ if (scc_entry != nullptr) {
+ // Invalid compilation states:
+ // - SCCache is closed, SCC entry is garbage.
+ // - SCC entry indicates this shared code was marked invalid while it was loaded.
+ if (!SCCache::is_on() || scc_entry->not_entrant()) {
+ code_buffer->free_blob();
+ return;
+ }
+ }
+
// Change in Jvmti state may invalidate compilation.
if (!failing() && jvmti_state_changed()) {
record_failure("Jvmti state change invalidated dependencies");
}
( (!dtrace_method_probes() && DTraceMethodProbes) ||
(!dtrace_alloc_probes() && DTraceAllocProbes) )) {
record_failure("DTrace flags change invalidated dependencies");
}
! if (!failing() && target->needs_clinit_barrier() &&
target->holder()->is_in_error_state()) {
record_failure("method holder is in error state");
}
! if (!failing()) {
if (log() != nullptr) {
// Log the dependencies which this compilation declares.
dependencies()->log_all_dependencies();
}
// Encode the dependencies now, so we can check them right away.
dependencies()->encode_content_bytes();
!
// Check for {class loads, evolution, breakpoints, ...} during compilation
validate_compile_task_dependencies(target);
}
if (failing()) {
// While not a true deoptimization, it is a preemptive decompile.
MethodData* mdo = method()->method_data();
( (!dtrace_method_probes() && DTraceMethodProbes) ||
(!dtrace_alloc_probes() && DTraceAllocProbes) )) {
record_failure("DTrace flags change invalidated dependencies");
}
! if (!preload && !failing() && target->needs_clinit_barrier() &&
target->holder()->is_in_error_state()) {
record_failure("method holder is in error state");
}
! if (!failing() && (scc_entry == nullptr)) {
if (log() != nullptr) {
// Log the dependencies which this compilation declares.
dependencies()->log_all_dependencies();
}
// Encode the dependencies now, so we can check them right away.
dependencies()->encode_content_bytes();
! }
+ // Check for {class loads, evolution, breakpoints, ...} during compilation
+ if (!failing() && install_code) {
// Check for {class loads, evolution, breakpoints, ...} during compilation
validate_compile_task_dependencies(target);
+ if (failing() && preload) {
+ ResourceMark rm;
+ char *method_name = method->name_and_sig_as_C_string();
+ log_info(scc)("preload code for '%s' failed dependency check", method_name);
+ }
}
if (failing()) {
// While not a true deoptimization, it is a preemptive decompile.
MethodData* mdo = method()->method_data();
}
assert(offsets->value(CodeOffsets::Deopt) != -1, "must have deopt entry");
assert(offsets->value(CodeOffsets::Exceptions) != -1, "must have exception entry");
! nm = nmethod::new_nmethod(method,
! compile_id(),
! entry_bci,
! offsets,
! orig_pc_offset,
! debug_info(), dependencies(), code_buffer,
! frame_words, oop_map_set,
! handler_table, inc_table,
! compiler, CompLevel(task()->comp_level()));
!
// Free codeBlobs
code_buffer->free_blob();
if (nm != nullptr) {
nm->set_has_unsafe_access(has_unsafe_access);
nm->set_has_wide_vectors(has_wide_vectors);
nm->set_has_monitors(has_monitors);
nm->set_has_scoped_access(has_scoped_access);
assert(!method->is_synchronized() || nm->has_monitors(), "");
if (entry_bci == InvocationEntryBci) {
if (TieredCompilation) {
// If there is an old version we're done with it
}
assert(offsets->value(CodeOffsets::Deopt) != -1, "must have deopt entry");
assert(offsets->value(CodeOffsets::Exceptions) != -1, "must have exception entry");
! if (scc_entry == nullptr) {
! scc_entry = SCCache::store_nmethod(method,
! compile_id(),
! entry_bci,
! offsets,
! orig_pc_offset,
! debug_info(), dependencies(), code_buffer,
! frame_words, oop_map_set,
! handler_table, inc_table,
! compiler,
+ CompLevel(task()->comp_level()),
+ has_clinit_barriers,
+ for_preload,
+ has_unsafe_access,
+ has_wide_vectors,
+ has_monitors,
+ has_scoped_access);
+ if (scc_entry != nullptr) {
+ scc_entry->set_inlined_bytecodes(num_inlined_bytecodes());
+ if (has_clinit_barriers) {
+ set_scc_clinit_barriers_entry(scc_entry); // Record it
+ // Build second version of code without class initialization barriers
+ code_buffer->free_blob();
+ return;
+ } else if (!for_preload) {
+ SCCEntry* previous_entry = scc_clinit_barriers_entry();
+ scc_entry->set_next(previous_entry); // Link it for case of deoptimization
+ }
+ }
+ }
+ if (install_code) {
+ nm = nmethod::new_nmethod(method,
+ compile_id(),
+ entry_bci,
+ offsets,
+ orig_pc_offset,
+ debug_info(), dependencies(), code_buffer,
+ frame_words, oop_map_set,
+ handler_table, inc_table,
+ compiler, CompLevel(task()->comp_level()),
+ scc_entry);
+ }
// Free codeBlobs
code_buffer->free_blob();
if (nm != nullptr) {
nm->set_has_unsafe_access(has_unsafe_access);
nm->set_has_wide_vectors(has_wide_vectors);
nm->set_has_monitors(has_monitors);
nm->set_has_scoped_access(has_scoped_access);
+ nm->set_preloaded(preload);
+ nm->set_has_clinit_barriers(has_clinit_barriers);
assert(!method->is_synchronized() || nm->has_monitors(), "");
if (entry_bci == InvocationEntryBci) {
if (TieredCompilation) {
// If there is an old version we're done with it
LogTarget(Info, nmethod, install) lt;
if (lt.is_enabled()) {
ResourceMark rm;
char *method_name = method->name_and_sig_as_C_string();
! lt.print("Installing method (%d) %s ",
! task()->comp_level(), method_name);
}
// Allow the code to be executed
MutexLocker ml(NMethodState_lock, Mutex::_no_safepoint_check_flag);
if (nm->make_in_use()) {
! method->set_code(method, nm);
}
} else {
LogTarget(Info, nmethod, install) lt;
if (lt.is_enabled()) {
ResourceMark rm;
char *method_name = method->name_and_sig_as_C_string();
! lt.print("Installing osr method (%d) %s @ %d",
! task()->comp_level(), method_name, entry_bci);
}
MutexLocker ml(NMethodState_lock, Mutex::_no_safepoint_check_flag);
if (nm->make_in_use()) {
method->method_holder()->add_osr_nmethod(nm);
}
LogTarget(Info, nmethod, install) lt;
if (lt.is_enabled()) {
ResourceMark rm;
char *method_name = method->name_and_sig_as_C_string();
! lt.print("Installing method (L%d) %s id=%d scc=%s%s%u",
! task()->comp_level(), method_name, compile_id(),
+ task()->is_scc() ? "A" : "", preload ? "P" : "",
+ (scc_entry != nullptr ? scc_entry->offset() : 0));
}
// Allow the code to be executed
MutexLocker ml(NMethodState_lock, Mutex::_no_safepoint_check_flag);
if (nm->make_in_use()) {
! #ifdef ASSERT
+ BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod();
+ if (bs_nm != nullptr && bs_nm->supports_entry_barrier(nm)) {
+ if (!bs_nm->is_armed(nm)) {
+ log_info(init)("nmethod %d %d not armed", nm->compile_id(), nm->comp_level());
+ }
+ }
+ #endif // ASSERT
+ if (preload) {
+ method->set_preload_code(nm);
+ }
+ if (!preload || target->holder()->is_linked()) {
+ method->set_code(method, nm);
+ }
}
} else {
LogTarget(Info, nmethod, install) lt;
if (lt.is_enabled()) {
ResourceMark rm;
char *method_name = method->name_and_sig_as_C_string();
! lt.print("Installing osr method (L%d) %s @ %d id=%u scc=%s%u",
! task()->comp_level(), method_name, entry_bci, compile_id(),
+ task()->is_scc() ? "A" : "",
+ (scc_entry != nullptr ? scc_entry->offset() : 0));
}
MutexLocker ml(NMethodState_lock, Mutex::_no_safepoint_check_flag);
if (nm->make_in_use()) {
method->method_holder()->add_osr_nmethod(nm);
}
NoSafepointVerifier nsv;
if (nm != nullptr) {
// Compilation succeeded, post what we know about it
nm->post_compiled_method(task());
task()->set_num_inlined_bytecodes(num_inlined_bytecodes());
! } else {
// The CodeCache is full.
record_failure("code cache is full");
}
// safepoints are allowed again
}
NoSafepointVerifier nsv;
if (nm != nullptr) {
// Compilation succeeded, post what we know about it
nm->post_compiled_method(task());
task()->set_num_inlined_bytecodes(num_inlined_bytecodes());
! } else if (install_code) {
// The CodeCache is full.
record_failure("code cache is full");
+ } else {
+ task()->set_num_inlined_bytecodes(num_inlined_bytecodes());
}
// safepoints are allowed again
}
// ------------------------------------------------------------------
// ciEnv::notice_inlined_method()
void ciEnv::notice_inlined_method(ciMethod* method) {
_num_inlined_bytecodes += method->code_size_for_inlining();
+ CompileTrainingData* tdata = task()->training_data();
+ if (tdata != nullptr) {
+ GUARDED_VM_ENTRY({
+ methodHandle mh(Thread::current(), method->get_Method());
+ tdata->notice_inlined_method(task(), mh);
+ });
+ }
}
// ------------------------------------------------------------------
// ciEnv::num_inlined_bytecodes()
int ciEnv::num_inlined_bytecodes() const {
}
void ciEnv::dump_replay_data_version(outputStream* out) {
out->print_cr("version %d", REPLAY_VERSION);
}
+
+ bool ciEnv::is_precompiled() {
+ return (task() != nullptr) && (task()->compile_reason() == CompileTask::Reason_Precompile ||
+ task()->compile_reason() == CompileTask::Reason_PrecompileForPreload);
+ }
+
+ bool ciEnv::is_fully_initialized(InstanceKlass* ik) {
+ assert(is_precompiled(), "");
+ if (task()->method()->method_holder() == ik) {
+ return true; // FIXME: may be too strong; being_initialized, at least
+ }
+ switch (task()->compile_reason()) {
+ case CompileTask::Reason_Precompile: {
+ // check init dependencies
+ MethodTrainingData* mtd = TrainingData::lookup_for(task()->method());
+ if (mtd != nullptr) {
+ CompileTrainingData* ctd = mtd->last_toplevel_compile(task()->comp_level());
+ if (ctd != nullptr) {
+ for (int i = 0; i < ctd->init_dep_count(); i++) {
+ KlassTrainingData* ktd = ctd->init_dep(i);
+ if (ktd->has_holder() && (ktd->holder() == ik)) {
+ log_trace(precompile)("%d: init_dependency: %s: %s", task()->compile_id(), InstanceKlass::state2name(ik->init_state()), ik->external_name());
+ return true; // init dependency present
+ }
+ }
+ }
+ }
+ return false; // no init dependency
+ }
+ case CompileTask::Reason_PrecompileForPreload: {
+ // FIXME: assumes that all shared classes are initialized
+ if (ik->is_shared()) {
+ return true; // class init barriers
+ }
+ if (CDSConfig::is_dumping_final_static_archive() && ArchiveBuilder::is_active() &&
+ ArchiveBuilder::current()->has_been_archived((address)ik)) {
+ return true; // class init barriers
+ }
+ return false;
+ }
+ default: fatal("%s", CompileTask::reason_name(task()->compile_reason()));
+ }
+ return false;
+ }
+
+ InstanceKlass::ClassState ciEnv::compute_init_state_for_precompiled(InstanceKlass* ik) {
+ ASSERT_IN_VM;
+ assert(is_precompiled(), "");
+ ResourceMark rm;
+ if (is_fully_initialized(ik)) {
+ log_trace(precompile)("%d: fully_initialized: %s", task()->compile_id(), ik->external_name());
+ return InstanceKlass::ClassState::fully_initialized;
+ } else if (MetaspaceObj::is_shared(ik)) {
+ guarantee(ik->is_loaded(), ""); // FIXME: assumes pre-loading by CDS; ik->is_linked() requires pre-linking
+ log_trace(precompile)("%d: %s: %s", task()->compile_id(), InstanceKlass::state2name(ik->init_state()), ik->external_name());
+ return ik->init_state(); // not yet initialized
+ } else if (CDSConfig::is_dumping_final_static_archive() && ArchiveBuilder::is_active()) {
+ if (!ArchiveBuilder::current()->has_been_archived((address)ik)) {
+ fatal("New workflow: should not compile code for unarchived class: %s", ik->external_name());
+ }
+ guarantee(ik->is_loaded(), "");
+ log_trace(precompile)("%d: %s: %s", task()->compile_id(), InstanceKlass::state2name(ik->init_state()), ik->external_name());
+ return ik->init_state(); // not yet initialized
+ } else {
+ // Not present in the archive.
+ fatal("unloaded: %s", ik->external_name());
+ // guarantee(SystemDictionaryShared::lookup_init_state(ik) == ik->init_state(), "");
+ log_trace(precompile)("%d: allocated: %s", task()->compile_id(), ik->external_name());
+ return InstanceKlass::ClassState::allocated; // not yet linked
+ }
+ }
< prev index next >