< prev index next >

src/hotspot/share/runtime/deoptimization.cpp

Print this page
rev 56101 : 8227745: Enable Escape Analysis for better performance when debugging
Reviewed-by: ???

@@ -54,10 +54,11 @@
 #include "runtime/fieldDescriptor.inline.hpp"
 #include "runtime/frame.inline.hpp"
 #include "runtime/jniHandles.inline.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/interfaceSupport.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"

@@ -156,62 +157,40 @@
 
   return fetch_unroll_info_helper(thread, exec_mode);
 JRT_END
 
 
-// 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) {
-
-  // Note: there is a safepoint safety issue here. No matter whether we enter
-  // via vanilla deopt or uncommon trap we MUST NOT stop at a safepoint once
-  // the vframeArray is created.
-  //
 
-  // Allocate our special deoptimization ResourceMark
-  DeoptResourceMark* dmark = new DeoptResourceMark(thread);
-  assert(thread->deopt_mark() == NULL, "Pending deopt!");
-  thread->set_deopt_mark(dmark);
 
-  frame stub_frame = thread->last_frame(); // Makes stack walkable as side effect
-  RegisterMap map(thread, true);
-  RegisterMap dummy_map(thread, false);
-  // Now get the deoptee with a valid map
-  frame deoptee = stub_frame.sender(&map);
-  // Set the deoptee nmethod
-  assert(thread->deopt_compiled_method() == NULL, "Pending deopt!");
-  CompiledMethod* cm = deoptee.cb()->as_compiled_method_or_null();
-  thread->set_deopt_compiled_method(cm);
-
-  if (VerifyStack) {
-    thread->validate_frame_layout();
-  }
+#if COMPILER2_OR_JVMCI
+// Deoptimize objects, that is reallocate and relock them. Either because the holding
+// compiled frame is being replaced by corresponding interpreter frames or because
+// they are about to escape through JVMTI (exec_mode == Unpack_none).
+bool Deoptimization::deoptimize_objects(JavaThread* thread, GrowableArray<compiledVFrame*>* chunk, bool& realloc_failures, int exec_mode) {
+  bool deoptimized_objects = false;
 
-  // Create a growable array of VFrames where each VFrame represents an inlined
-  // Java frame.  This storage is allocated with the usual system arena.
-  assert(deoptee.is_compiled_frame(), "Wrong frame type");
-  GrowableArray<compiledVFrame*>* chunk = new GrowableArray<compiledVFrame*>(10);
-  vframe* vf = vframe::new_vframe(&deoptee, &map, thread);
-  while (!vf->is_top()) {
-    assert(vf->is_compiled_frame(), "Wrong frame type");
-    chunk->push(compiledVFrame::cast(vf));
-    vf = vf->sender();
-  }
-  assert(vf->is_compiled_frame(), "Wrong frame type");
-  chunk->push(compiledVFrame::cast(vf));
+  NOT_JVMCI(if (DoEscapeAnalysis || EliminateNestedLocks))
+  {
+    frame deoptee = chunk->at(0)->fr();
+    JavaThread* deoptee_thread = chunk->at(0)->thread();
+    const RegisterMap* map = chunk->at(0)->register_map();
 
-  bool realloc_failures = false;
+    assert(!JVMTIEscapeBarrier::objs_are_deoptimized(deoptee_thread, deoptee.id()), "must relock just once");
+    assert(exec_mode == Unpack_none || (deoptee_thread == thread), "a frame can only be deoptimized by the owner thread");
 
-#if COMPILER2_OR_JVMCI
-  // Reallocate the non-escaping objects and restore their fields. Then
-  // relock objects if synchronization on them was eliminated.
-#if !INCLUDE_JVMCI
-  if (DoEscapeAnalysis || EliminateNestedLocks) {
-    if (EliminateAllocations) {
-#endif // INCLUDE_JVMCI
+    NOT_JVMCI(if (EliminateAllocations))
+    {
       assert (chunk->at(0)->scope() != NULL,"expect only compiled java frames");
       GrowableArray<ScopeValue*>* objects = chunk->at(0)->scope()->objects();
 
+      if (objects != NULL) {
+        if (exec_mode == Unpack_none) {
+          assert(thread->thread_state() == _thread_in_vm, "assumption");
+          Thread* THREAD = thread;
+          // Clear pending OOM if reallocation fails and return false, i.e. no objects deoptimized.
+          realloc_failures = realloc_objects(thread, &deoptee, map, objects, CHECK_AND_CLEAR_false);
+        } else {
       // 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
       // on slow code path) and any other calls generated in compiled code.
       // It is not guaranteed that we can get such information here only

@@ -222,60 +201,75 @@
       bool save_oop_result = chunk->at(0)->scope()->return_oop() && !thread->popframe_forcing_deopt_reexecution() && (exec_mode == Unpack_deopt);
       Handle return_value;
       if (save_oop_result) {
         // Reallocation may trigger GC. If deoptimization happened on return from
         // call which returns oop we need to save it since it is not in oopmap.
-        oop result = deoptee.saved_oop_result(&map);
+            oop result = deoptee.saved_oop_result(map);
         assert(oopDesc::is_oop_or_null(result), "must be oop");
         return_value = Handle(thread, result);
         assert(Universe::heap()->is_in_or_null(result), "must be heap pointer");
         if (TraceDeoptimization) {
           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 = realloc_objects(thread, &deoptee, &map, objects, THREAD);
+            realloc_failures = realloc_objects(thread, &deoptee, map, objects, THREAD);
         JRT_END
+          if (save_oop_result) {
+            // Restore result.
+            deoptee.set_saved_oop_result(map, return_value());
+          }
+          if (JVMTIEscapeBarrier::objs_are_deoptimized(deoptee_thread, deoptee.id())) {
+            // A concurrent JVMTI agent thread stop the current thread in the JRT_BLOCK above
+            // and deoptimized its objects
+            realloc_failures = false; // ignore realloc failures if any occurred
+            return false;             // current thread did not deoptimize objects
+          }
+        }
+        CompiledMethod* cm = deoptee.cb()->as_compiled_method_or_null();
         bool skip_internal = (cm != NULL) && !cm->is_compiled_by_jvmci();
-        reassign_fields(&deoptee, &map, objects, realloc_failures, skip_internal);
+        reassign_fields(&deoptee, map, objects, realloc_failures, skip_internal);
+        deoptimized_objects = true;
 #ifndef PRODUCT
         if (TraceDeoptimization) {
           ttyLocker ttyl;
-          tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, p2i(thread));
+          tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, p2i(deoptee_thread));
           print_objects(objects, realloc_failures);
         }
 #endif
       }
-      if (save_oop_result) {
-        // Restore result.
-        deoptee.set_saved_oop_result(&map, return_value());
-      }
-#if !INCLUDE_JVMCI
     }
-    if (EliminateLocks) {
-#endif // INCLUDE_JVMCI
+    NOT_JVMCI(if (EliminateLocks))
+    {
 #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()) {
-          relock_objects(monitors, thread, realloc_failures);
+          bool relocked = 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()));

@@ -284,14 +278,71 @@
             }
           }
 #endif // !PRODUCT
         }
       }
-#if !INCLUDE_JVMCI
     }
   }
-#endif // INCLUDE_JVMCI
+  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) {
+
+  // Note: there is a safepoint safety issue here. No matter whether we enter
+  // via vanilla deopt or uncommon trap we MUST NOT stop at a safepoint once
+  // the vframeArray is created.
+  //
+
+  // Allocate our special deoptimization ResourceMark
+  DeoptResourceMark* dmark = new DeoptResourceMark(thread);
+  assert(thread->deopt_mark() == NULL, "Pending deopt!");
+  thread->set_deopt_mark(dmark);
+
+  frame stub_frame = thread->last_frame(); // Makes stack walkable as side effect
+  RegisterMap map(thread, true);
+  RegisterMap dummy_map(thread, false);
+  // Now get the deoptee with a valid map
+  frame deoptee = stub_frame.sender(&map);
+  // Set the deoptee nmethod
+  assert(thread->deopt_compiled_method() == NULL, "Pending deopt!");
+  CompiledMethod* cm = deoptee.cb()->as_compiled_method_or_null();
+  thread->set_deopt_compiled_method(cm);
+
+  if (VerifyStack) {
+    thread->validate_frame_layout();
+  }
+
+  // Create a growable array of VFrames where each VFrame represents an inlined
+  // Java frame.  This storage is allocated with the usual system arena.
+  assert(deoptee.is_compiled_frame(), "Wrong frame type");
+  GrowableArray<compiledVFrame*>* chunk = new GrowableArray<compiledVFrame*>(10);
+  vframe* vf = vframe::new_vframe(&deoptee, &map, thread);
+  while (!vf->is_top()) {
+    assert(vf->is_compiled_frame(), "Wrong frame type");
+    chunk->push(compiledVFrame::cast(vf));
+    vf = vf->sender();
+  }
+  assert(vf->is_compiled_frame(), "Wrong frame type");
+  chunk->push(compiledVFrame::cast(vf));
+
+  bool realloc_failures = false;
+
+#if COMPILER2_OR_JVMCI
+  // Reallocate the non-escaping objects and restore their fields. Then
+  // relock objects if synchronization on them was eliminated.
+  if (!JVMTIEscapeBarrier::objs_are_deoptimized(thread, deoptee.id())) {
+    // objects are not yet deoptimized, do it now
+    deoptimize_objects(thread, chunk, realloc_failures, exec_mode);
+  } else {
+    // objects have been deoptimized already for JVMTI access
+    if (TraceDeoptimization) {
+      ttyLocker ttyl;
+      tty->print_cr("ALREADY DEOPTIMIZED OBJECTS for thread " INTPTR_FORMAT, p2i(thread));
+    }
+  }
 #endif // COMPILER2_OR_JVMCI
 
   ScopeDesc* trap_scope = chunk->at(0)->scope();
   Handle exceptionObject;
   if (trap_scope->rethrow_exception()) {

@@ -338,13 +389,14 @@
       } 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;
+      JvmtiDeferredUpdates* updates = thread->deferred_updates();
+      thread->reset_deferred_updates();
+      // free deferred updates.
+      delete updates;
     }
 
   }
 
   // Compute the caller frame based on the sender sp of stub_frame and stored frame sizes info.

@@ -900,11 +952,11 @@
   }
 };
 
 BooleanBoxCache* BooleanBoxCache::_singleton = NULL;
 
-oop Deoptimization::get_cached_box(AutoBoxObjectValue* bv, frame* fr, RegisterMap* reg_map, TRAPS) {
+oop Deoptimization::get_cached_box(AutoBoxObjectValue* bv, frame* fr, const RegisterMap* reg_map, TRAPS) {
    Klass* k = java_lang_Class::as_Klass(bv->klass()->as_ConstantOopReadValue()->value()());
    BasicType box_type = SystemDictionary::box_klass_type(k);
    if (box_type != T_OBJECT) {
      StackValue* value = StackValue::create_stack_value(fr, reg_map, bv->field_at(box_type == T_LONG ? 1 : 0));
      switch(box_type) {

@@ -920,11 +972,11 @@
    return NULL;
 }
 #endif // INCLUDE_JVMCI || INCLUDE_AOT
 
 #if COMPILER2_OR_JVMCI
-bool Deoptimization::realloc_objects(JavaThread* thread, frame* fr, RegisterMap* reg_map, GrowableArray<ScopeValue*>* objects, TRAPS) {
+bool Deoptimization::realloc_objects(JavaThread* thread, frame* fr, const RegisterMap* reg_map, GrowableArray<ScopeValue*>* objects, TRAPS) {
   Handle pending_exception(THREAD, thread->pending_exception());
   const char* exception_file = thread->exception_file();
   int exception_line = thread->exception_line();
   thread->clear_pending_exception();
 

@@ -981,11 +1033,11 @@
 
   return failures;
 }
 
 // restore elements of an eliminated type array
-void Deoptimization::reassign_type_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, typeArrayOop obj, BasicType type) {
+void Deoptimization::reassign_type_array_elements(frame* fr, const RegisterMap* reg_map, ObjectValue* sv, typeArrayOop obj, BasicType type) {
   int index = 0;
   intptr_t val;
 
   for (int i = 0; i < sv->field_size(); i++) {
     StackValue* value = StackValue::create_stack_value(fr, reg_map, sv->field_at(i));

@@ -1078,11 +1130,11 @@
   }
 }
 
 
 // restore fields of an eliminated object array
-void Deoptimization::reassign_object_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, objArrayOop obj) {
+void Deoptimization::reassign_object_array_elements(frame* fr, const RegisterMap* reg_map, ObjectValue* sv, objArrayOop obj) {
   for (int i = 0; i < sv->field_size(); i++) {
     StackValue* value = StackValue::create_stack_value(fr, reg_map, sv->field_at(i));
     assert(value->type() == T_OBJECT, "object element expected");
     obj->obj_at_put(i, value->get_obj()());
   }

@@ -1103,11 +1155,11 @@
   return left->_offset - right->_offset;
 }
 
 // Restore fields of an eliminated instance object using the same field order
 // returned by HotSpotResolvedObjectTypeImpl.getInstanceFields(true)
-static int reassign_fields_by_klass(InstanceKlass* klass, frame* fr, RegisterMap* reg_map, ObjectValue* sv, int svIndex, oop obj, bool skip_internal) {
+static int reassign_fields_by_klass(InstanceKlass* klass, frame* fr, const RegisterMap* reg_map, ObjectValue* sv, int svIndex, oop obj, bool skip_internal) {
   if (klass->superklass() != NULL) {
     svIndex = reassign_fields_by_klass(klass->superklass(), fr, reg_map, sv, svIndex, obj, skip_internal);
   }
 
   GrowableArray<ReassignedField>* fields = new GrowableArray<ReassignedField>();

@@ -1211,11 +1263,11 @@
   }
   return svIndex;
 }
 
 // restore fields of all eliminated objects and arrays
-void Deoptimization::reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray<ScopeValue*>* objects, bool realloc_failures, bool skip_internal) {
+void Deoptimization::reassign_fields(frame* fr, const RegisterMap* reg_map, GrowableArray<ScopeValue*>* objects, bool realloc_failures, bool skip_internal) {
   for (int i = 0; i < objects->length(); i++) {
     ObjectValue* sv = (ObjectValue*) objects->at(i);
     Klass* k = java_lang_Class::as_Klass(sv->klass()->as_ConstantOopReadValue()->value()());
     Handle obj = sv->value();
     assert(obj.not_null() || realloc_failures, "reallocation was missed");

@@ -1243,34 +1295,56 @@
   }
 }
 
 
 // relock objects for which synchronization was eliminated
-void Deoptimization::relock_objects(GrowableArray<MonitorInfo*>* monitors, JavaThread* thread, bool realloc_failures) {
+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() == thread, "should be locked to current thread");
+                 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.
+            // 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()) {
+              mon_info->lock()->set_displaced_header(markWord::unused_mark());
+              deoptee_thread->inc_relock_count_after_wait();
+              continue;
+            }
+          }
         }
         BasicLock* lock = mon_info->lock();
-        ObjectSynchronizer::enter(obj, lock, thread);
+        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

@@ -2445,10 +2519,402 @@
     }
     #undef PRINT_STAT_LINE
     if (xtty != NULL)  xtty->tail("statistics");
   }
 }
+
+#ifdef ASSERT
+// Revert optimizations based on escape analysis for all compiled frames of all Java threads as if
+// objects local to a frame or a thread were escaping. Do it every DeoptimizeObjectsALotInterval
+// milliseconds.
+void Deoptimization::deoptimize_objects_alot_loop() {
+  JavaThread* ct = JavaThread::current();
+  HandleMark hm(ct);
+  while (!ct->is_terminated()) {
+    { // Begin new scope for escape barrier
+      HandleMarkCleaner hmc(ct);
+      ResourceMark rm(ct);
+      JVMTIEscapeBarrier eb(ct, true);
+      eb.deoptimize_objects_all_threads();
+    }
+    // Now sleep after the escape barriers destructor resumed the java threads.
+    os::sleep(ct, DeoptimizeObjectsALotInterval, true);
+  }
+}
+#endif // !ASSERT
+
+// Returns true iff objects were reallocated and relocked because of access through JVMTI
+bool JVMTIEscapeBarrier::objs_are_deoptimized(JavaThread* thread, intptr_t* fr_id) {
+  // first/oldest update holds the flag
+  GrowableArray<jvmtiDeferredLocalVariableSet*>* list = thread->deferred_locals();
+  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;
+}
+
+// Remember that objects were reallocated and relocked for the compiled frame with the given id
+void JVMTIEscapeBarrier::set_objs_are_deoptimized(JavaThread* thread, intptr_t* fr_id) {
+  // set in first/oldest update
+  GrowableArray<jvmtiDeferredLocalVariableSet*>* list = thread->deferred_locals();
+  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");
+}
+
+bool JVMTIEscapeBarrier::deoptimize_objects(compiledVFrame* cvf) {
+  return !barrier_active() || deoptimize_objects(deoptee_thread(), cvf->fr(), cvf->register_map());
+}
+
+// Deoptimize frames with non escaping objects. Deoptimize objects with optimizations based on
+// escape analysis. Do it for all frames within the given depth and continue from there until the
+// entry frame is reached, because thread local objects passed as arguments might escape from callee
+// frames within the given depth.
+bool JVMTIEscapeBarrier::deoptimize_objects(int depth) {
+  if (barrier_active() && deoptee_thread()->has_last_Java_frame()) {
+    ResourceMark rm;
+    HandleMark   hm;
+    RegisterMap  reg_map(deoptee_thread());
+    vframe* vf = deoptee_thread()->last_java_vframe(&reg_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 must only deoptimize if 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)) {
+          // reallocation of scalar replaced objects failed, because heap is exhausted
+          return false;
+        }
+      }
+
+      // move to next physical frame
+      while(!vf->is_top()) {
+        cur_depth++;
+        vf = vf->sender();
+      }
+      cur_depth++;
+      vf = vf->sender();
+    }
+  }
+  return true;
+}
+
+bool JVMTIEscapeBarrier::deoptimize_objects(intptr_t* fr_id) {
+  if (!barrier_active()) return true;
+  // Compute frame and register map based on thread and sp.
+  RegisterMap reg_map(deoptee_thread());
+  frame fr = deoptee_thread()->last_frame();
+  while (fr.id() != fr_id) {
+    fr = fr.sender(&reg_map);
+  }
+  return deoptimize_objects(deoptee_thread(), fr, &reg_map);
+}
+
+
+bool JVMTIEscapeBarrier::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(&reg_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(jt, cvf->fr(), cvf->register_map())) {
+            return false; // reallocation failure
+          }
+        }
+        // move to next physical frame
+        while(!vf->is_top()) {
+          vf = vf->sender();
+        }
+        vf = vf->sender();
+      }
+    }
+  }
+  return true; // success
+}
+
+bool JVMTIEscapeBarrier::_deoptimizing_objects_for_all_threads = false;
+bool JVMTIEscapeBarrier::_self_deoptimization_in_progress      = false;
+
+bool JVMTIEscapeBarrier::deoptimizing_objects_for_all_threads() {
+  assert(Threads_lock->owned_by_self(), "Threads_lock required");
+  return _deoptimizing_objects_for_all_threads;
+}
+
+void JVMTIEscapeBarrier::set_deoptimizing_objects_for_all_threads(bool v) {
+  assert(Threads_lock->owned_by_self(), "Threads_lock required");
+  _deoptimizing_objects_for_all_threads = v;
+  if (!_deoptimizing_objects_for_all_threads) {
+    Threads_lock->notify_all(); // notify waiting threads
+  }
+}
+
+void JVMTIEscapeBarrier::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
+  {
+    MutexLocker ml(JvmtiObjReallocRelock_lock);
+    while (_self_deoptimization_in_progress) {
+      JvmtiObjReallocRelock_lock->wait();
+    }
+
+    if (self_deopt()) {
+      _self_deoptimization_in_progress = true;
+    }
+
+    while (_deoptee_thread->is_ea_obj_deopt_suspend()) {
+      JvmtiObjReallocRelock_lock->wait();
+    }
+
+    if (self_deopt()) {
+      return;
+    }
+
+    // set suspend flag for target thread
+    _deoptee_thread->set_ea_obj_deopt_flag();
+  }
+
+  // suspend target thread
+  uint32_t debug_bits = 0;
+  if (!_deoptee_thread->is_thread_fully_suspended(false, &debug_bits)) {
+    class NopClosure : public ThreadClosure {
+      void do_thread(Thread* th) { }
+    } nop;
+    Handshake::execute(&nop, _deoptee_thread);
+  }
+  assert(!_deoptee_thread->has_last_Java_frame() || _deoptee_thread->frame_anchor()->walkable(),
+         "stack should be walkable now");
+}
+
+class VM_ThreadSuspendAllForObjDeopt : public VM_Operation {
+  public:
+   VMOp_Type type() const { return VMOp_ThreadSuspendAllForObjDeopt; }
+   virtual void doit() {
+     Thread* ct = calling_thread();
+     for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
+       if (jt->is_hidden_from_external_view()) continue;
+       assert(!jt->is_ea_obj_deopt_suspend(), "bad synchronization");
+       if (ct != jt) {
+         jt->set_ea_obj_deopt_flag();
+       }
+     }
+     JVMTIEscapeBarrier::set_deoptimizing_objects_for_all_threads(true);
+   }
+};
+
+void JVMTIEscapeBarrier::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
+  {
+    MutexLocker ml(JvmtiObjReallocRelock_lock);
+    while (_self_deoptimization_in_progress) {
+      JvmtiObjReallocRelock_lock->wait();
+    }
+
+    _self_deoptimization_in_progress = true;
+
+    bool deopt_in_progress;
+    do {
+      deopt_in_progress = false;
+      for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
+        if (jt->is_ea_obj_deopt_suspend()) {
+          deopt_in_progress = true;
+          JvmtiObjReallocRelock_lock->wait();
+          break; // check all threads again
+        }
+      }
+    } while(deopt_in_progress);
+  }
+
+  VM_ThreadSuspendAllForObjDeopt vm_suspend_all;
+  VMThread::execute(&vm_suspend_all);
+#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 JVMTIEscapeBarrier::resume_one() {
+  assert(barrier_active(), "should not call");
+  assert(!all_threads(), "use resume_all()");
+  MutexLocker ml(JvmtiObjReallocRelock_lock);
+  if (self_deopt()) {
+    assert(_self_deoptimization_in_progress, "incorrect synchronization");
+    _self_deoptimization_in_progress = false;
+  } else {
+    _deoptee_thread->clear_ea_obj_deopt_flag();
+  }
+  JvmtiObjReallocRelock_lock->notify_all();
+}
+
+void JVMTIEscapeBarrier::resume_all() {
+  assert(barrier_active(), "should not call");
+  assert(all_threads(), "use resume_one()");
+  {
+    MutexLocker l1(Threads_lock);
+    set_deoptimizing_objects_for_all_threads(false);
+  }
+  MutexLocker l2(JvmtiObjReallocRelock_lock);
+  assert(_self_deoptimization_in_progress, "incorrect synchronization");
+  _self_deoptimization_in_progress = false;
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
+    jt->clear_ea_obj_deopt_flag();
+  }
+  JvmtiObjReallocRelock_lock->notify_all();
+}
+
+// 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 JVMTIEscapeBarrier::deoptimize_objects(JavaThread* deoptee, frame fr, const RegisterMap *reg_map) {
+  if (!barrier_active()) return true;
+
+  JavaThread* ct = calling_thread();
+  bool realloc_failures = false;
+
+  assert(fr.is_compiled_frame(), "only compiled frames can contain stack allocated objects");
+  assert(reg_map->update_map(), "e.g. for values in callee saved registers");
+
+  if (!objs_are_deoptimized(deoptee, fr.id())) {
+    // Execution must not continue in the compiled method, so we deoptimize the frame.
+    // As a side effect all locking biases will be removed which makes relocking
+    // of eliminated nested locks easier.
+    compiledVFrame* last_cvf = compiledVFrame::cast(vframe::new_vframe(&fr, reg_map, deoptee));
+    if (!fr.is_deoptimized_frame()) {
+      Deoptimization::deoptimize_frame(deoptee, fr.id());
+
+      // the frame fr is stale after the deoptimization, we have to fetch it again
+      StackFrameStream fst(deoptee);
+      while (fst.current()->id() != fr.id() && !fst.is_done()) {
+        fst.next();
+      }
+      assert(fst.current()->id() == fr.id(), "frame not found after deoptimization");
+      last_cvf = compiledVFrame::cast(vframe::new_vframe(fst.current(), fst.register_map(), deoptee));
+    }
+
+    // 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);
+
+    // With the exception of not escaping owners biases where revoked when the deoptimization of fr
+    // was requested. Among the non escaping owners of eliminated locks might be some that are still
+    // locked in callee frames. We need their biases revoked and do it here, because we cannot
+    // safepoint in relock_objects(). Note that the markword of such an owner will then point to a
+    // callee frame. This will be fixed in relock_objects().
+    if (UseBiasedLocking && last_cvf->arg_escape()) {
+      GrowableArray<Handle>* arg_esc_owners = new GrowableArray<Handle>();
+      for (int i = 0; i < vfs->length(); i++) {
+        GrowableArray<MonitorInfo*>* monitors = vfs->at(i)->monitors();
+        for (int j = 0; j < monitors->length(); j++) {
+          MonitorInfo* mon_info = monitors->at(j);
+          oop owner = mon_info->owner_is_scalar_replaced() ? oop(NULL) : mon_info->owner();
+          if (mon_info->eliminated() && owner != NULL) {
+            markWord mark = owner->mark();
+            if (mark.has_bias_pattern() && !mark.is_biased_anonymously()) {
+              assert(mark.biased_locker() == deoptee, "not escaping object can only be biased to current thread");
+              arg_esc_owners->push(Handle(ct, owner));
+            }
+          }
+        }
+      }
+      if (arg_esc_owners->length() > 0) {
+        BiasedLocking::revoke(arg_esc_owners, deoptee);
+      }
+    }
+
+    // reallocate and relock optimized objects
+    bool deoptimized_objects = Deoptimization::deoptimize_objects(ct, vfs, realloc_failures, Deoptimization::Unpack_none);
+    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 = (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 = (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 >