< prev index next >
src/hotspot/share/runtime/deoptimization.cpp
Print this page
rev 60137 : 8227745: Enable Escape Analysis for Better Performance in the Presence of JVMTI Agents
Reviewed-by: mdoerr, goetz
*** 58,67 ****
--- 58,68 ----
#include "runtime/fieldDescriptor.inline.hpp"
#include "runtime/frame.inline.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/jniHandles.inline.hpp"
+ #include "runtime/objectMonitor.inline.hpp"
#include "runtime/safepointVerifiers.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/signature.hpp"
#include "runtime/stubRoutines.hpp"
#include "runtime/thread.hpp"
*** 165,178 ****
return fetch_unroll_info_helper(thread, exec_mode);
JRT_END
#if COMPILER2_OR_JVMCI
static bool eliminate_allocations(JavaThread* thread, int exec_mode, CompiledMethod* compiled_method,
! frame& deoptee, RegisterMap& map, GrowableArray<compiledVFrame*>* chunk) {
bool realloc_failures = false;
assert (chunk->at(0)->scope() != NULL,"expect only compiled java frames");
GrowableArray<ScopeValue*>* objects = chunk->at(0)->scope()->objects();
// The flag return_oop() indicates call sites which return oop
// in compiled code. Such sites include java method calls,
// runtime calls (for example, used to allocate new objects/arrays
--- 166,183 ----
return fetch_unroll_info_helper(thread, exec_mode);
JRT_END
#if COMPILER2_OR_JVMCI
static bool eliminate_allocations(JavaThread* thread, int exec_mode, CompiledMethod* compiled_method,
! frame& deoptee, RegisterMap& map, GrowableArray<compiledVFrame*>* chunk,
! bool& deoptimized_objects) {
bool realloc_failures = false;
assert (chunk->at(0)->scope() != NULL,"expect only compiled java frames");
+ JavaThread* deoptee_thread = chunk->at(0)->thread();
+ assert(exec_mode == Deoptimization::Unpack_none || (deoptee_thread == thread), "a frame can only be deoptimized by the owner thread");
+
GrowableArray<ScopeValue*>* objects = chunk->at(0)->scope()->objects();
// The flag return_oop() indicates call sites which return oop
// in compiled code. Such sites include java method calls,
// runtime calls (for example, used to allocate new objects/arrays
*** 195,213 ****
ttyLocker ttyl;
tty->print_cr("SAVED OOP RESULT " INTPTR_FORMAT " in thread " INTPTR_FORMAT, p2i(result), p2i(thread));
}
}
if (objects != NULL) {
JRT_BLOCK
realloc_failures = Deoptimization::realloc_objects(thread, &deoptee, &map, objects, THREAD);
JRT_END
bool skip_internal = (compiled_method != NULL) && !compiled_method->is_compiled_by_jvmci();
Deoptimization::reassign_fields(&deoptee, &map, objects, realloc_failures, skip_internal);
#ifndef PRODUCT
if (TraceDeoptimization) {
ttyLocker ttyl;
! tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, p2i(thread));
Deoptimization::print_objects(objects, realloc_failures);
}
#endif
}
if (save_oop_result) {
--- 200,226 ----
ttyLocker ttyl;
tty->print_cr("SAVED OOP RESULT " INTPTR_FORMAT " in thread " INTPTR_FORMAT, p2i(result), p2i(thread));
}
}
if (objects != NULL) {
+ if (exec_mode == Deoptimization::Unpack_none) {
+ assert(thread->thread_state() == _thread_in_vm, "assumption");
+ Thread* THREAD = thread;
+ // Clear pending OOM if reallocation fails and return true indicating allocation failure
+ realloc_failures = Deoptimization::realloc_objects(thread, &deoptee, &map, objects, CHECK_AND_CLEAR_(true));
+ deoptimized_objects = true;
+ } else {
JRT_BLOCK
realloc_failures = Deoptimization::realloc_objects(thread, &deoptee, &map, objects, THREAD);
JRT_END
+ }
bool skip_internal = (compiled_method != NULL) && !compiled_method->is_compiled_by_jvmci();
Deoptimization::reassign_fields(&deoptee, &map, objects, realloc_failures, skip_internal);
#ifndef PRODUCT
if (TraceDeoptimization) {
ttyLocker ttyl;
! tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, p2i(deoptee_thread));
Deoptimization::print_objects(objects, realloc_failures);
}
#endif
}
if (save_oop_result) {
*** 215,244 ****
deoptee.set_saved_oop_result(&map, return_value());
}
return realloc_failures;
}
! static void eliminate_locks(JavaThread* thread, GrowableArray<compiledVFrame*>* chunk, bool realloc_failures) {
#ifndef PRODUCT
bool first = true;
#endif
for (int i = 0; i < chunk->length(); i++) {
compiledVFrame* cvf = chunk->at(i);
assert (cvf->scope() != NULL,"expect only compiled java frames");
GrowableArray<MonitorInfo*>* monitors = cvf->monitors();
if (monitors->is_nonempty()) {
! Deoptimization::relock_objects(monitors, thread, realloc_failures);
#ifndef PRODUCT
if (PrintDeoptimizationDetails) {
ttyLocker ttyl;
for (int j = 0; j < monitors->length(); j++) {
MonitorInfo* mi = monitors->at(j);
if (mi->eliminated()) {
if (first) {
first = false;
tty->print_cr("RELOCK OBJECTS in thread " INTPTR_FORMAT, p2i(thread));
}
if (mi->owner_is_scalar_replaced()) {
Klass* k = java_lang_Class::as_Klass(mi->owner_klass());
tty->print_cr(" failed reallocation for klass %s", k->external_name());
} else {
tty->print_cr(" object <" INTPTR_FORMAT "> locked", p2i(mi->owner()));
--- 228,269 ----
deoptee.set_saved_oop_result(&map, return_value());
}
return realloc_failures;
}
! static void eliminate_locks(JavaThread* thread, GrowableArray<compiledVFrame*>* chunk, bool realloc_failures,
! frame& deoptee, int exec_mode, bool& deoptimized_objects) {
! JavaThread* deoptee_thread = chunk->at(0)->thread();
! assert(!EscapeBarrier::objs_are_deoptimized(deoptee_thread, deoptee.id()), "must relock just once");
!
#ifndef PRODUCT
bool first = true;
#endif
for (int i = 0; i < chunk->length(); i++) {
compiledVFrame* cvf = chunk->at(i);
assert (cvf->scope() != NULL,"expect only compiled java frames");
GrowableArray<MonitorInfo*>* monitors = cvf->monitors();
if (monitors->is_nonempty()) {
! bool relocked = Deoptimization::relock_objects(thread, monitors, deoptee_thread, deoptee, exec_mode, realloc_failures);
! deoptimized_objects = deoptimized_objects || relocked;
#ifndef PRODUCT
if (PrintDeoptimizationDetails) {
ttyLocker ttyl;
for (int j = 0; j < monitors->length(); j++) {
MonitorInfo* mi = monitors->at(j);
if (mi->eliminated()) {
if (first) {
first = false;
tty->print_cr("RELOCK OBJECTS in thread " INTPTR_FORMAT, p2i(thread));
}
+ if (exec_mode == Deoptimization::Unpack_none) {
+ ObjectMonitor* monitor = deoptee_thread->current_waiting_monitor();
+ if (monitor != NULL && (oop)monitor->object() == mi->owner()) {
+ tty->print_cr(" object <" INTPTR_FORMAT "> DEFERRED relocking after wait", p2i(mi->owner()));
+ continue;
+ }
+ }
if (mi->owner_is_scalar_replaced()) {
Klass* k = java_lang_Class::as_Klass(mi->owner_klass());
tty->print_cr(" failed reallocation for klass %s", k->external_name());
} else {
tty->print_cr(" object <" INTPTR_FORMAT "> locked", p2i(mi->owner()));
*** 248,257 ****
--- 273,311 ----
}
#endif // !PRODUCT
}
}
}
+
+ // Deoptimize objects, that is reallocate and relock them, just before they escape through JVMTI.
+ // The given vframes cover one physical frame.
+ bool Deoptimization::deoptimize_objects_internal(JavaThread* thread, GrowableArray<compiledVFrame*>* chunk, bool& realloc_failures) {
+ frame deoptee = chunk->at(0)->fr();
+ JavaThread* deoptee_thread = chunk->at(0)->thread();
+ CompiledMethod* cm = deoptee.cb()->as_compiled_method_or_null();
+ RegisterMap map(chunk->at(0)->register_map());
+ bool deoptimized_objects = false;
+
+ bool const jvmci_enabled = JVMCI_ONLY(UseJVMCICompiler) NOT_JVMCI(false);
+
+ // Reallocate the non-escaping objects and restore their fields.
+ if (jvmci_enabled COMPILER2_PRESENT(|| (DoEscapeAnalysis && EliminateAllocations))) {
+ realloc_failures = eliminate_allocations(thread, Unpack_none, cm, deoptee, map, chunk, deoptimized_objects);
+ }
+
+ // Revoke biases of objects with eliminated locks in the given frame.
+ Deoptimization::revoke_for_object_deoptimization(deoptee_thread, deoptee, &map, thread);
+
+ // MonitorInfo structures used in eliminate_locks are not GC safe.
+ NoSafepointVerifier no_safepoint;
+
+ // Now relock objects if synchronization on them was eliminated.
+ if (jvmci_enabled COMPILER2_PRESENT(|| ((DoEscapeAnalysis || EliminateNestedLocks) && EliminateLocks))) {
+ eliminate_locks(thread, chunk, realloc_failures, deoptee, Unpack_none, deoptimized_objects);
+ }
+ return deoptimized_objects;
+ }
#endif // COMPILER2_OR_JVMCI
// This is factored, since it is both called from a JRT_LEAF (deoptimization) and a JRT_ENTRY (uncommon_trap)
Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread* thread, int exec_mode) {
*** 302,312 ****
#endif
// Reallocate the non-escaping objects and restore their fields. Then
// relock objects if synchronization on them was eliminated.
if (jvmci_enabled COMPILER2_PRESENT( || (DoEscapeAnalysis && EliminateAllocations) )) {
! realloc_failures = eliminate_allocations(thread, exec_mode, cm, deoptee, map, chunk);
}
#endif // COMPILER2_OR_JVMCI
// Revoke biases, done with in java state.
// No safepoints allowed after this
--- 356,367 ----
#endif
// Reallocate the non-escaping objects and restore their fields. Then
// relock objects if synchronization on them was eliminated.
if (jvmci_enabled COMPILER2_PRESENT( || (DoEscapeAnalysis && EliminateAllocations) )) {
! bool unused;
! realloc_failures = eliminate_allocations(thread, exec_mode, cm, deoptee, map, chunk, unused);
}
#endif // COMPILER2_OR_JVMCI
// Revoke biases, done with in java state.
// No safepoints allowed after this
*** 317,328 ****
// out the java state residing in the vframeArray will be missed.
// Locks may be rebaised in a safepoint.
NoSafepointVerifier no_safepoint;
#if COMPILER2_OR_JVMCI
! if (jvmci_enabled COMPILER2_PRESENT( || ((DoEscapeAnalysis || EliminateNestedLocks) && EliminateLocks) )) {
! eliminate_locks(thread, chunk, realloc_failures);
}
#endif // COMPILER2_OR_JVMCI
ScopeDesc* trap_scope = chunk->at(0)->scope();
Handle exceptionObject;
--- 372,385 ----
// out the java state residing in the vframeArray will be missed.
// Locks may be rebaised in a safepoint.
NoSafepointVerifier no_safepoint;
#if COMPILER2_OR_JVMCI
! if ((jvmci_enabled COMPILER2_PRESENT( || ((DoEscapeAnalysis || EliminateNestedLocks) && EliminateLocks) ))
! && !EscapeBarrier::objs_are_deoptimized(thread, deoptee.id())) {
! bool unused;
! eliminate_locks(thread, chunk, realloc_failures, deoptee, exec_mode, unused);
}
#endif // COMPILER2_OR_JVMCI
ScopeDesc* trap_scope = chunk->at(0)->scope();
Handle exceptionObject;
*** 349,360 ****
// Now that the vframeArray has been created if we have any deferred local writes
// added by jvmti then we can free up that structure as the data is now in the
// vframeArray
! if (thread->deferred_locals() != NULL) {
! GrowableArray<jvmtiDeferredLocalVariableSet*>* list = thread->deferred_locals();
int i = 0;
do {
// Because of inlining we could have multiple vframes for a single frame
// and several of the vframes could have deferred writes. Find them all.
if (list->at(i)->id() == array->original().id()) {
--- 406,417 ----
// Now that the vframeArray has been created if we have any deferred local writes
// added by jvmti then we can free up that structure as the data is now in the
// vframeArray
! if (JvmtiDeferredUpdates::deferred_locals(thread) != NULL) {
! GrowableArray<jvmtiDeferredLocalVariableSet*>* list = JvmtiDeferredUpdates::deferred_locals(thread);
int i = 0;
do {
// Because of inlining we could have multiple vframes for a single frame
// and several of the vframes could have deferred writes. Find them all.
if (list->at(i)->id() == array->original().id()) {
*** 365,377 ****
} else {
i++;
}
} while ( i < list->length() );
if (list->length() == 0) {
! thread->set_deferred_locals(NULL);
! // free the list and elements back to C heap.
! delete list;
}
}
// Compute the caller frame based on the sender sp of stub_frame and stored frame sizes info.
--- 422,435 ----
} else {
i++;
}
} while ( i < list->length() );
if (list->length() == 0) {
! JvmtiDeferredUpdates* updates = thread->deferred_updates();
! thread->set_deferred_updates(NULL);
! // free deferred updates.
! delete updates;
}
}
// Compute the caller frame based on the sender sp of stub_frame and stored frame sizes info.
*** 1351,1384 ****
}
}
// relock objects for which synchronization was eliminated
! void Deoptimization::relock_objects(GrowableArray<MonitorInfo*>* monitors, JavaThread* thread, bool realloc_failures) {
for (int i = 0; i < monitors->length(); i++) {
MonitorInfo* mon_info = monitors->at(i);
if (mon_info->eliminated()) {
assert(!mon_info->owner_is_scalar_replaced() || realloc_failures, "reallocation was missed");
if (!mon_info->owner_is_scalar_replaced()) {
Handle obj(thread, mon_info->owner());
markWord mark = obj->mark();
if (UseBiasedLocking && mark.has_bias_pattern()) {
// New allocated objects may have the mark set to anonymously biased.
// Also the deoptimized method may called methods with synchronization
// where the thread-local object is bias locked to the current thread.
assert(mark.is_biased_anonymously() ||
! mark.biased_locker() == thread, "should be locked to current thread");
// Reset mark word to unbiased prototype.
markWord unbiased_prototype = markWord::prototype().set_age(mark.age());
obj->set_mark(unbiased_prototype);
}
BasicLock* lock = mon_info->lock();
! ObjectSynchronizer::enter(obj, lock, thread);
assert(mon_info->owner()->is_locked(), "object must be locked now");
}
}
}
}
#ifndef PRODUCT
// print information about reallocated objects
--- 1409,1465 ----
}
}
// relock objects for which synchronization was eliminated
! bool Deoptimization::relock_objects(JavaThread* thread, GrowableArray<MonitorInfo*>* monitors,
! JavaThread* deoptee_thread, frame& fr, int exec_mode, bool realloc_failures) {
! bool relocked_objects = false;
for (int i = 0; i < monitors->length(); i++) {
MonitorInfo* mon_info = monitors->at(i);
if (mon_info->eliminated()) {
assert(!mon_info->owner_is_scalar_replaced() || realloc_failures, "reallocation was missed");
+ relocked_objects = true;
if (!mon_info->owner_is_scalar_replaced()) {
Handle obj(thread, mon_info->owner());
markWord mark = obj->mark();
if (UseBiasedLocking && mark.has_bias_pattern()) {
// New allocated objects may have the mark set to anonymously biased.
// Also the deoptimized method may called methods with synchronization
// where the thread-local object is bias locked to the current thread.
assert(mark.is_biased_anonymously() ||
! mark.biased_locker() == deoptee_thread, "should be locked to current thread");
// Reset mark word to unbiased prototype.
markWord unbiased_prototype = markWord::prototype().set_age(mark.age());
obj->set_mark(unbiased_prototype);
+ } else if (exec_mode == Unpack_none) {
+ if (mark.has_locker() && fr.sp() > (intptr_t*)mark.locker()) {
+ // With exec_mode == Unpack_none obj may be thread local and locked in
+ // a callee frame. In this case the bias was revoked before in revoke_for_object_deoptimization().
+ // Make the lock in the callee a recursive lock and restore the displaced header.
+ markWord dmw = mark.displaced_mark_helper();
+ mark.locker()->set_displaced_header(markWord::encode((BasicLock*) NULL));
+ obj->set_mark(dmw);
+ }
+ if (mark.has_monitor()) {
+ // defer relocking if the deoptee thread is currently waiting for obj
+ ObjectMonitor* waiting_monitor = deoptee_thread->current_waiting_monitor();
+ if (waiting_monitor != NULL && (oop)waiting_monitor->object() == obj()) {
+ assert(fr.is_deoptimized_frame(), "frame must be scheduled for deoptimization");
+ mon_info->lock()->set_displaced_header(markWord::unused_mark());
+ JvmtiDeferredUpdates::inc_relock_count_after_wait(deoptee_thread);
+ continue;
+ }
+ }
}
BasicLock* lock = mon_info->lock();
! ObjectSynchronizer::enter(obj, lock, deoptee_thread);
assert(mon_info->owner()->is_locked(), "object must be locked now");
}
}
}
+ return relocked_objects;
}
#ifndef PRODUCT
// print information about reallocated objects
*** 1492,1513 ****
}
}
}
#endif
! static void collect_monitors(compiledVFrame* cvf, GrowableArray<Handle>* objects_to_revoke) {
GrowableArray<MonitorInfo*>* monitors = cvf->monitors();
Thread* thread = Thread::current();
for (int i = 0; i < monitors->length(); i++) {
MonitorInfo* mon_info = monitors->at(i);
! if (!mon_info->eliminated() && mon_info->owner() != NULL) {
objects_to_revoke->append(Handle(thread, mon_info->owner()));
}
}
}
! static void get_monitors_from_stack(GrowableArray<Handle>* objects_to_revoke, JavaThread* thread, frame fr, RegisterMap* map) {
// Unfortunately we don't have a RegisterMap available in most of
// the places we want to call this routine so we need to walk the
// stack again to update the register map.
if (map == NULL || !map->update_map()) {
StackFrameStream sfs(thread, true);
--- 1573,1595 ----
}
}
}
#endif
! static void collect_monitors(compiledVFrame* cvf, GrowableArray<Handle>* objects_to_revoke, bool only_eliminated) {
GrowableArray<MonitorInfo*>* monitors = cvf->monitors();
Thread* thread = Thread::current();
for (int i = 0; i < monitors->length(); i++) {
MonitorInfo* mon_info = monitors->at(i);
! if ((mon_info->eliminated() == only_eliminated) && !mon_info->owner_is_scalar_replaced() && mon_info->owner() != NULL) {
objects_to_revoke->append(Handle(thread, mon_info->owner()));
}
}
}
! static void get_monitors_from_stack(GrowableArray<Handle>* objects_to_revoke, JavaThread* thread, frame fr, RegisterMap* map,
! bool only_eliminated) {
// Unfortunately we don't have a RegisterMap available in most of
// the places we want to call this routine so we need to walk the
// stack again to update the register map.
if (map == NULL || !map->update_map()) {
StackFrameStream sfs(thread, true);
*** 1523,1553 ****
vframe* vf = vframe::new_vframe(&fr, map, thread);
compiledVFrame* cvf = compiledVFrame::cast(vf);
// Revoke monitors' biases in all scopes
while (!cvf->is_top()) {
! collect_monitors(cvf, objects_to_revoke);
cvf = compiledVFrame::cast(cvf->sender());
}
! collect_monitors(cvf, objects_to_revoke);
}
void Deoptimization::revoke_from_deopt_handler(JavaThread* thread, frame fr, RegisterMap* map) {
if (!UseBiasedLocking) {
return;
}
GrowableArray<Handle>* objects_to_revoke = new GrowableArray<Handle>();
! get_monitors_from_stack(objects_to_revoke, thread, fr, map);
int len = objects_to_revoke->length();
for (int i = 0; i < len; i++) {
oop obj = (objects_to_revoke->at(i))();
BiasedLocking::revoke_own_lock(objects_to_revoke->at(i), thread);
assert(!obj->mark().has_bias_pattern(), "biases should be revoked by now");
}
}
void Deoptimization::deoptimize_single_frame(JavaThread* thread, frame fr, Deoptimization::DeoptReason reason) {
assert(fr.can_be_deoptimized(), "checking frame type");
gather_statistics(reason, Action_none, Bytecodes::_illegal);
--- 1605,1663 ----
vframe* vf = vframe::new_vframe(&fr, map, thread);
compiledVFrame* cvf = compiledVFrame::cast(vf);
// Revoke monitors' biases in all scopes
while (!cvf->is_top()) {
! collect_monitors(cvf, objects_to_revoke, only_eliminated);
cvf = compiledVFrame::cast(cvf->sender());
}
! collect_monitors(cvf, objects_to_revoke, only_eliminated);
}
void Deoptimization::revoke_from_deopt_handler(JavaThread* thread, frame fr, RegisterMap* map) {
if (!UseBiasedLocking) {
return;
}
GrowableArray<Handle>* objects_to_revoke = new GrowableArray<Handle>();
! get_monitors_from_stack(objects_to_revoke, thread, fr, map, false);
int len = objects_to_revoke->length();
for (int i = 0; i < len; i++) {
oop obj = (objects_to_revoke->at(i))();
BiasedLocking::revoke_own_lock(objects_to_revoke->at(i), thread);
assert(!obj->mark().has_bias_pattern(), "biases should be revoked by now");
}
}
+ // Revoke the bias of objects with eliminated locking to prepare subsequent relocking.
+ void Deoptimization::revoke_for_object_deoptimization(JavaThread* deoptee_thread, frame fr, RegisterMap* map, JavaThread* thread) {
+ if (!UseBiasedLocking) {
+ return;
+ }
+ GrowableArray<Handle>* objects_to_revoke = new GrowableArray<Handle>();
+ // Collect monitors, but only those with eliminated locking.
+ get_monitors_from_stack(objects_to_revoke, deoptee_thread, fr, map, true);
+
+ int len = objects_to_revoke->length();
+ for (int i = 0; i < len; i++) {
+ oop obj = (objects_to_revoke->at(i))();
+ markWord mark = obj->mark();
+ if (!mark.has_bias_pattern() ||
+ mark.is_biased_anonymously() || // eliminated locking does not bias an object if it wasn't before
+ !obj->klass()->prototype_header().has_bias_pattern() || // bulk revoke ignores eliminated monitors
+ (obj->klass()->prototype_header().bias_epoch() != mark.bias_epoch())) { // bulk rebias ignores eliminated monitors
+ // We reach here regularly if there's just eliminated locking on obj.
+ // We must not call BiasedLocking::revoke_own_lock() in this case, as we would hit assertions, because it is a
+ // prerequisite that there has to be non-eliminated locking on obj by deoptee_thread.
+ // Luckily we don't have to revoke here, because obj has to be a non-escaping obj and can be relocked without
+ // revoking the bias. See Deoptimization::relock_objects().
+ continue;
+ }
+ BiasedLocking::revoke(objects_to_revoke->at(i), thread);
+ assert(!objects_to_revoke->at(i)->mark().has_bias_pattern(), "biases should be revoked by now");
+ }
+ }
void Deoptimization::deoptimize_single_frame(JavaThread* thread, frame fr, Deoptimization::DeoptReason reason) {
assert(fr.can_be_deoptimized(), "checking frame type");
gather_statistics(reason, Action_none, Bytecodes::_illegal);
*** 2596,2605 ****
--- 2706,3042 ----
}
#undef PRINT_STAT_LINE
if (xtty != NULL) xtty->tail("statistics");
}
}
+
+ // Returns true iff objects were reallocated and relocked because of access through JVMTI
+ bool EscapeBarrier::objs_are_deoptimized(JavaThread* thread, intptr_t* fr_id) {
+ // first/oldest update holds the flag
+ GrowableArray<jvmtiDeferredLocalVariableSet*>* list = JvmtiDeferredUpdates::deferred_locals(thread);
+ bool result = false;
+ if (list != NULL) {
+ for (int i = 0; i < list->length(); i++) {
+ if (list->at(i)->matches(fr_id)) {
+ result = list->at(i)->objects_are_deoptimized();
+ break;
+ }
+ }
+ }
+ return result;
+ }
+
+ // Object references of frames up to the given depth are about to be accessed. Frames with
+ // optimizations based on escape state that is potentially changed by the accesses need to be
+ // deoptimized and the referenced objects need to be reallocated and relocked.
+ // Up to depth this is done for frames with not escaping objects in scope. For deeper frames it is
+ // done only, if they pass not escaping objects as arguments, because they potentially escape from
+ // callee frames within the given depth.
+ // The search for deeper frames is ended if an entry frame is found, because arguments to
+ // native methods are considered to escape globally.
+ bool EscapeBarrier::deoptimize_objects(int depth) {
+ if (barrier_active() && deoptee_thread()->has_last_Java_frame()) {
+ ResourceMark rm(calling_thread());
+ HandleMark hm;
+ RegisterMap reg_map(deoptee_thread());
+ vframe* vf = deoptee_thread()->last_java_vframe(®_map);
+ int cur_depth = 0;
+ while (vf != NULL && ((cur_depth <= depth) || !vf->is_entry_frame())) {
+ if (vf->is_compiled_frame()) {
+ compiledVFrame* cvf = compiledVFrame::cast(vf);
+ // Deoptimize frame and local objects if any exist.
+ // If cvf is deeper than depth, then we deoptimize iff local objects are passed as args.
+ bool should_deopt = cur_depth <= depth ? cvf->not_global_escape_in_scope() : cvf->arg_escape();
+ if (should_deopt && !deoptimize_objects(cvf->fr().id())) {
+ // reallocation of scalar replaced objects failed, because heap is exhausted
+ return false;
+ }
+
+ // move to top frame
+ while(!vf->is_top()) {
+ cur_depth++;
+ vf = vf->sender();
+ }
+ }
+
+ // move to next physical frame
+ cur_depth++;
+ vf = vf->sender();
+ }
+ }
+ return true;
+ }
+
+ bool EscapeBarrier::deoptimize_objects_all_threads() {
+ if (!barrier_active()) return true;
+ ResourceMark rm(calling_thread());
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
+ if (jt->has_last_Java_frame()) {
+ RegisterMap reg_map(jt);
+ vframe* vf = jt->last_java_vframe(®_map);
+ assert(jt->frame_anchor()->walkable(),
+ "The stack of JavaThread " PTR_FORMAT " is not walkable. Thread state is %d",
+ p2i(jt), jt->thread_state());
+ while (vf != NULL) {
+ if (vf->is_compiled_frame()) {
+ compiledVFrame* cvf = compiledVFrame::cast(vf);
+ if ((cvf->not_global_escape_in_scope() || cvf->arg_escape()) &&
+ !deoptimize_objects_internal(jt, cvf->fr().id())) {
+ return false; // reallocation failure
+ }
+ // move to top frame
+ while(!vf->is_top()) {
+ vf = vf->sender();
+ }
+ }
+ // move to next physical frame
+ vf = vf->sender();
+ }
+ }
+ }
+ return true; // success
+ }
+
+ bool EscapeBarrier::_deoptimizing_objects_for_all_threads = false;
+ bool EscapeBarrier::_self_deoptimization_in_progress = false;
+
+ class EscapeBarrierSuspendHandshake : public HandshakeClosure {
+ JavaThread* _excluded_thread;
+ public:
+ EscapeBarrierSuspendHandshake(JavaThread* excluded_thread, const char* name) : HandshakeClosure(name), _excluded_thread(excluded_thread) { }
+ void do_thread(Thread* th) {
+ if (th->is_Java_thread() && !th->is_hidden_from_external_view() && (th != _excluded_thread)) {
+ th->set_obj_deopt_flag();
+ }
+ }
+ };
+
+ void EscapeBarrier::sync_and_suspend_one() {
+ assert(_calling_thread != NULL, "calling thread must not be NULL");
+ assert(_deoptee_thread != NULL, "deoptee thread must not be NULL");
+ assert(barrier_active(), "should not call");
+
+ // Sync with other threads that might be doing deoptimizations
+ {
+ // Need to switch to _thread_blocked for the wait() call
+ ThreadBlockInVM tbivm(_calling_thread);
+ MonitorLocker ml(_calling_thread, EscapeBarrier_lock, Mutex::_no_safepoint_check_flag);
+ while (_self_deoptimization_in_progress || _deoptee_thread->is_obj_deopt_suspend()) {
+ ml.wait();
+ }
+
+ if (self_deopt()) {
+ _self_deoptimization_in_progress = true;
+ return;
+ }
+
+ // set suspend flag for target thread
+ _deoptee_thread->set_obj_deopt_flag();
+ }
+
+ // suspend target thread
+ EscapeBarrierSuspendHandshake sh(NULL, "EscapeBarrierSuspendOne");
+ Handshake::execute_direct(&sh, _deoptee_thread);
+ assert(!_deoptee_thread->has_last_Java_frame() || _deoptee_thread->frame_anchor()->walkable(),
+ "stack should be walkable now");
+ }
+
+ void EscapeBarrier::sync_and_suspend_all() {
+ assert(barrier_active(), "should not call");
+ assert(_calling_thread != NULL, "calling thread must not be NULL");
+ assert(all_threads(), "sanity");
+
+ // Sync with other threads that might be doing deoptimizations
+ {
+ // Need to switch to _thread_blocked for the wait() call
+ ThreadBlockInVM tbivm(_calling_thread);
+ MonitorLocker ml(_calling_thread, EscapeBarrier_lock, Mutex::_no_safepoint_check_flag);
+
+ bool deopt_in_progress;
+ do {
+ deopt_in_progress = _self_deoptimization_in_progress;
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
+ deopt_in_progress = (deopt_in_progress || jt->is_obj_deopt_suspend());
+ if (deopt_in_progress) {
+ break;
+ }
+ }
+ if (deopt_in_progress) {
+ ml.wait(); // then check again
+ }
+ } while(deopt_in_progress);
+
+ _self_deoptimization_in_progress = true;
+ _deoptimizing_objects_for_all_threads = true;
+ }
+
+ EscapeBarrierSuspendHandshake sh(_calling_thread, "EscapeBarrierSuspendAll");
+ Handshake::execute(&sh);
+ #ifdef ASSERT
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
+ if (jt->is_hidden_from_external_view()) continue;
+ assert(!jt->has_last_Java_frame() || jt->frame_anchor()->walkable(),
+ "The stack of JavaThread " PTR_FORMAT " is not walkable. Thread state is %d",
+ p2i(jt), jt->thread_state());
+ }
+ #endif // ASSERT
+ }
+
+ void EscapeBarrier::resume_one() {
+ assert(barrier_active(), "should not call");
+ assert(!all_threads(), "use resume_all()");
+ MonitorLocker ml(_calling_thread, EscapeBarrier_lock, Mutex::_no_safepoint_check_flag);
+ if (self_deopt()) {
+ assert(_self_deoptimization_in_progress, "incorrect synchronization");
+ _self_deoptimization_in_progress = false;
+ } else {
+ _deoptee_thread->clear_obj_deopt_flag();
+ }
+ ml.notify_all();
+ }
+
+ void EscapeBarrier::resume_all() {
+ assert(barrier_active(), "should not call");
+ assert(all_threads(), "use resume_one()");
+ MonitorLocker ml(_calling_thread, EscapeBarrier_lock, Mutex::_no_safepoint_check_flag);
+ assert(_self_deoptimization_in_progress, "incorrect synchronization");
+ _deoptimizing_objects_for_all_threads = false;
+ _self_deoptimization_in_progress = false;
+ for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
+ jt->clear_obj_deopt_flag();
+ }
+ ml.notify_all();
+ }
+
+ void EscapeBarrier::thread_added(JavaThread* jt) {
+ if (!jt->is_hidden_from_external_view()) {
+ MutexLocker ml(EscapeBarrier_lock, Mutex::_no_safepoint_check_flag);
+ if (_deoptimizing_objects_for_all_threads) {
+ jt->set_obj_deopt_flag();
+ }
+ }
+ }
+
+ void EscapeBarrier::thread_removed(JavaThread* jt) {
+ MonitorLocker ml(EscapeBarrier_lock, Mutex::_no_safepoint_check_flag);
+ if (jt->is_obj_deopt_suspend()) {
+ // jt terminated before it self suspended.
+ // Other threads might be waiting to perform deoptimizations for it.
+ jt->clear_obj_deopt_flag();
+ ml.notify_all();
+ }
+ }
+
+ // Remember that objects were reallocated and relocked for the compiled frame with the given id
+ static void set_objs_are_deoptimized(JavaThread* thread, intptr_t* fr_id) {
+ // set in first/oldest update
+ GrowableArray<jvmtiDeferredLocalVariableSet*>* list = JvmtiDeferredUpdates::deferred_locals(thread);
+ DEBUG_ONLY(bool found = false);
+ if (list != NULL) {
+ for (int i = 0; i < list->length(); i++) {
+ if (list->at(i)->matches(fr_id)) {
+ DEBUG_ONLY(found = true);
+ list->at(i)->set_objs_are_deoptimized();
+ break;
+ }
+ }
+ }
+ assert(found, "variable set should exist at least for one vframe");
+ }
+
+ // Deoptimize the given frame and deoptimize objects with optimizations based on escape analysis,
+ // i.e. reallocate scalar replaced objects on the heap and relock objects if locking has been
+ // eliminated.
+ // Deoptimized objects are kept as JVMTI deferred updates until the compiled frame is replaced with interpreter frames.
+ // Returns false iff at least one reallocation failed.
+ bool EscapeBarrier::deoptimize_objects_internal(JavaThread* deoptee, intptr_t* fr_id) {
+ if (!barrier_active()) return true;
+
+ JavaThread* ct = calling_thread();
+ bool realloc_failures = false;
+
+ if (!objs_are_deoptimized(deoptee, fr_id)) {
+ // Make sure the frame identified by fr_id is deoptimized and fetch its last vframe
+ compiledVFrame* last_cvf;
+ bool fr_is_deoptimized;
+ do {
+ StackFrameStream fst(deoptee);
+ while (fst.current()->id() != fr_id && !fst.is_done()) {
+ fst.next();
+ }
+ assert(fst.current()->id() == fr_id, "frame not found");
+ assert(fst.current()->is_compiled_frame(), "only compiled frames can contain stack allocated objects");
+ fr_is_deoptimized = fst.current()->is_deoptimized_frame();
+ if (!fr_is_deoptimized) {
+ // Execution must not continue in the compiled method, so we deoptimize the frame.
+ Deoptimization::deoptimize_frame(deoptee, fr_id);
+ } else {
+ last_cvf = compiledVFrame::cast(vframe::new_vframe(fst.current(), fst.register_map(), deoptee));
+ }
+ } while(!fr_is_deoptimized);
+
+ // collect inlined frames
+ compiledVFrame* cvf = last_cvf;
+ GrowableArray<compiledVFrame*>* vfs = new GrowableArray<compiledVFrame*>(10);
+ while (!cvf->is_top()) {
+ vfs->push(cvf);
+ cvf = compiledVFrame::cast(cvf->sender());
+ }
+ vfs->push(cvf);
+
+ // reallocate and relock optimized objects
+ bool deoptimized_objects = Deoptimization::deoptimize_objects_internal(ct, vfs, realloc_failures);
+ if (!realloc_failures && deoptimized_objects) {
+ // now do the updates
+ for (int frame_index = 0; frame_index < vfs->length(); frame_index++) {
+ cvf = vfs->at(frame_index);
+
+ // locals
+ GrowableArray<ScopeValue*>* scopeLocals = cvf->scope()->locals();
+ StackValueCollection* locals = cvf->locals();
+ if (locals != NULL) {
+ for (int i2 = 0; i2 < locals->size(); i2++) {
+ StackValue* var = locals->at(i2);
+ if (var->type() == T_OBJECT && scopeLocals->at(i2)->is_object()) {
+ jvalue val;
+ val.l = cast_from_oop<jobject>(locals->at(i2)->get_obj()());
+ cvf->update_local(T_OBJECT, i2, val);
+ }
+ }
+ }
+
+ // expressions
+ GrowableArray<ScopeValue*>* scopeExpressions = cvf->scope()->expressions();
+ StackValueCollection* expressions = cvf->expressions();
+ if (expressions != NULL) {
+ for (int i2 = 0; i2 < expressions->size(); i2++) {
+ StackValue* var = expressions->at(i2);
+ if (var->type() == T_OBJECT && scopeExpressions->at(i2)->is_object()) {
+ jvalue val;
+ val.l = cast_from_oop<jobject>(expressions->at(i2)->get_obj()());
+ cvf->update_stack(T_OBJECT, i2, val);
+ }
+ }
+ }
+
+ // monitors
+ GrowableArray<MonitorInfo*>* monitors = cvf->monitors();
+ if (monitors != NULL) {
+ for (int i2 = 0; i2 < monitors->length(); i2++) {
+ if (monitors->at(i2)->eliminated()) {
+ assert(!monitors->at(i2)->owner_is_scalar_replaced(), "reallocation failure, should not update");
+ cvf->update_monitor(i2, monitors->at(i2));
+ }
+ }
+ }
+ }
+ set_objs_are_deoptimized(deoptee, fr_id);
+ }
+ }
+ return !realloc_failures;
+ }
+
#else // COMPILER2_OR_JVMCI
// Stubs for C1 only system.
bool Deoptimization::trap_state_is_recompiled(int trap_state) {
< prev index next >