Merge "Only MADV_RANDOM for low RAM devices"
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index cf2bfee..ea8d501 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -1832,55 +1832,53 @@
     mov   r12, r0        @ r12 holds reference to code
     ldr   r0, [sp, #4]   @ restore r0
     RESTORE_SAVE_REFS_AND_ARGS_FRAME
+    adr   lr, art_quick_instrumentation_exit + /* thumb mode */ 1
+                         @ load art_quick_instrumentation_exit into lr in thumb mode
     REFRESH_MARKING_REGISTER
-    blx   r12            @ call method with lr set to art_quick_instrumentation_exit
-@ Deliberate fall-through into art_quick_instrumentation_exit.
-    .type art_quick_instrumentation_exit, #function
-    .global art_quick_instrumentation_exit
-art_quick_instrumentation_exit:
-    mov   lr, #0         @ link register is to here, so clobber with 0 for later checks
-    SETUP_SAVE_REFS_ONLY_FRAME r2  @ set up frame knowing r2 and r3 must be dead on exit
-    mov   r12, sp        @ remember bottom of caller's frame
-    push  {r0-r1}        @ save return value
-    .cfi_adjust_cfa_offset 8
-    .cfi_rel_offset r0, 0
-    .cfi_rel_offset r1, 4
-    mov   r2, sp         @ store gpr_res pointer.
-    vpush {d0}           @ save fp return value
-    .cfi_adjust_cfa_offset 8
-    mov   r3, sp         @ store fpr_res pointer
-    mov   r1, r12        @ pass SP
-    mov   r0, r9         @ pass Thread::Current
-    blx   artInstrumentationMethodExitFromCode  @ (Thread*, SP, gpr_res*, fpr_res*)
-
-    mov   r2, r0         @ link register saved by instrumentation
-    mov   lr, r1         @ r1 is holding link register if we're to bounce to deoptimize
-    vpop  {d0}           @ restore fp return value
-    .cfi_adjust_cfa_offset -8
-    pop   {r0, r1}       @ restore return value
-    .cfi_adjust_cfa_offset -8
-    .cfi_restore r0
-    .cfi_restore r1
-    RESTORE_SAVE_REFS_ONLY_FRAME
-    REFRESH_MARKING_REGISTER
-    cbz   r2, .Ldo_deliver_instrumentation_exception
-                         @ Deliver exception if we got nullptr as function.
-    bx    r2             @ Otherwise, return
+    bx    r12            @ call method with lr set to art_quick_instrumentation_exit
 .Ldeliver_instrumentation_entry_exception:
     @ Deliver exception for art_quick_instrumentation_entry placed after
     @ art_quick_instrumentation_exit so that the fallthrough works.
     RESTORE_SAVE_REFS_AND_ARGS_FRAME
-.Ldo_deliver_instrumentation_exception:
     DELIVER_PENDING_EXCEPTION
 END art_quick_instrumentation_entry
 
+ENTRY art_quick_instrumentation_exit
+    mov   lr, #0         @ link register is to here, so clobber with 0 for later checks
+    SETUP_SAVE_EVERYTHING_FRAME r2
+
+    add   r3, sp, #8     @ store fpr_res pointer, in kSaveEverything frame
+    add   r2, sp, #136   @ store gpr_res pointer, in kSaveEverything frame
+    mov   r1, sp         @ pass SP
+    mov   r0, r9         @ pass Thread::Current
+    blx   artInstrumentationMethodExitFromCode  @ (Thread*, SP, gpr_res*, fpr_res*)
+
+    cbz   r0, .Ldo_deliver_instrumentation_exception
+                         @ Deliver exception if we got nullptr as function.
+    cbnz  r1, .Ldeoptimize
+    // Normal return.
+    str   r0, [sp, #FRAME_SIZE_SAVE_EVERYTHING - 4]
+                         @ Set return pc.
+    RESTORE_SAVE_EVERYTHING_FRAME
+    REFRESH_MARKING_REGISTER
+    bx lr
+.Ldo_deliver_instrumentation_exception:
+    DELIVER_PENDING_EXCEPTION_FRAME_READY
+.Ldeoptimize:
+    str   r1, [sp, #FRAME_SIZE_SAVE_EVERYTHING - 4]
+                         @ Set return pc.
+    RESTORE_SAVE_EVERYTHING_FRAME
+    // Jump to art_quick_deoptimize.
+    b     art_quick_deoptimize
+END art_quick_instrumentation_exit
+
     /*
      * Instrumentation has requested that we deoptimize into the interpreter. The deoptimization
      * will long jump to the upcall with a special exception of -1.
      */
     .extern artDeoptimize
 ENTRY art_quick_deoptimize
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r0
+    SETUP_SAVE_EVERYTHING_FRAME r0
     mov    r0, r9         @ pass Thread::Current
     blx    artDeoptimize  @ (Thread*)
 END art_quick_deoptimize
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 3d8ca40..6c9ce93 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -2355,32 +2355,31 @@
     .extern artInstrumentationMethodExitFromCode
 ENTRY art_quick_instrumentation_exit
     mov   xLR, #0             // Clobber LR for later checks.
+    SETUP_SAVE_EVERYTHING_FRAME
 
-    SETUP_SAVE_REFS_ONLY_FRAME
-
-    str x0, [sp, #-16]!       // Save integer result.
-    .cfi_adjust_cfa_offset 16
-    str d0, [sp, #8]          // Save floating-point result.
-
-    add   x3, sp, #8          // Pass floating-point result pointer.
-    mov   x2, sp              // Pass integer result pointer.
-    add   x1, sp, #16         // Pass SP.
+    add   x3, sp, #8          // Pass floating-point result pointer, in kSaveEverything frame.
+    add   x2, sp, #264        // Pass integer result pointer, in kSaveEverything frame.
+    mov   x1, sp              // Pass SP.
     mov   x0, xSELF           // Pass Thread.
     bl   artInstrumentationMethodExitFromCode    // (Thread*, SP, gpr_res*, fpr_res*)
 
-    mov   xIP0, x0            // Return address from instrumentation call.
-    mov   xLR, x1             // r1 is holding link register if we're to bounce to deoptimize
-
-    ldr   d0, [sp, #8]        // Restore floating-point result.
-    ldr   x0, [sp], #16       // Restore integer result, and drop stack area.
-    .cfi_adjust_cfa_offset -16
-
-    RESTORE_SAVE_REFS_ONLY_FRAME
+    cbz   x0, .Ldo_deliver_instrumentation_exception
+                              // Handle error
+    cbnz  x1, .Ldeoptimize
+    // Normal return.
+    str   x0, [sp, #FRAME_SIZE_SAVE_EVERYTHING - 8]
+                              // Set return pc.
+    RESTORE_SAVE_EVERYTHING_FRAME
     REFRESH_MARKING_REGISTER
-    cbz   xIP0, 1f            // Handle error
-    br    xIP0                // Tail-call out.
-1:
-    DELIVER_PENDING_EXCEPTION
+    br    lr
+.Ldo_deliver_instrumentation_exception:
+    DELIVER_PENDING_EXCEPTION_FRAME_READY
+.Ldeoptimize:
+    str   x1, [sp, #FRAME_SIZE_SAVE_EVERYTHING - 8]
+                              // Set return pc.
+    RESTORE_SAVE_EVERYTHING_FRAME
+    // Jump to art_quick_deoptimize.
+    b     art_quick_deoptimize
 END art_quick_instrumentation_exit
 
     /*
@@ -2389,7 +2388,7 @@
      */
     .extern artDeoptimize
 ENTRY art_quick_deoptimize
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
+    SETUP_SAVE_EVERYTHING_FRAME
     mov    x0, xSELF          // Pass thread.
     bl     artDeoptimize      // (Thread*)
     brk 0
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 4e5e93a..af82e08 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -2056,42 +2056,43 @@
 DEFINE_FUNCTION_CUSTOM_CFA art_quick_instrumentation_exit, 0
     pushl LITERAL(0)              // Push a fake return PC as there will be none on the stack.
     CFI_ADJUST_CFA_OFFSET(4)
-    SETUP_SAVE_REFS_ONLY_FRAME ebx, ebx
-    mov  %esp, %ecx               // Remember SP
-    subl LITERAL(8), %esp         // Save float return value.
+    SETUP_SAVE_EVERYTHING_FRAME ebx, ebx
+
+    movl %esp, %ecx               // Remember SP
+    subl LITERAL(8), %esp         // Align stack.
     CFI_ADJUST_CFA_OFFSET(8)
-    movq %xmm0, (%esp)
-    PUSH edx                      // Save gpr return value.
+    PUSH edx                      // Save gpr return value. edx and eax need to be together,
+                                  // which isn't the case in kSaveEverything frame.
     PUSH eax
-    leal 8(%esp), %eax            // Get pointer to fpr_result
+    leal 32(%esp), %eax           // Get pointer to fpr_result, in kSaveEverything frame
     movl %esp, %edx               // Get pointer to gpr_result
     PUSH eax                      // Pass fpr_result
     PUSH edx                      // Pass gpr_result
-    PUSH ecx                      // Pass SP.
+    PUSH ecx                      // Pass SP
     pushl %fs:THREAD_SELF_OFFSET  // Pass Thread::Current.
     CFI_ADJUST_CFA_OFFSET(4)
+
     call SYMBOL(artInstrumentationMethodExitFromCode)  // (Thread*, SP, gpr_result*, fpr_result*)
-    testl %eax, %eax              // Check if we returned error.
-    jz 1f
-    mov   %eax, %ecx              // Move returned link register.
-    addl LITERAL(16), %esp        // Pop arguments.
-    CFI_ADJUST_CFA_OFFSET(-16)
-    movl %edx, %ebx               // Move returned link register for deopt
-                                  // (ebx is pretending to be our LR).
-    POP eax                       // Restore gpr return value.
-    POP edx
-    movq (%esp), %xmm0            // Restore fpr return value.
-    addl LITERAL(8), %esp
-    CFI_ADJUST_CFA_OFFSET(-8)
-    RESTORE_SAVE_REFS_ONLY_FRAME
-    addl LITERAL(4), %esp         // Remove fake return pc.
-    CFI_ADJUST_CFA_OFFSET(-4)
-    jmp   *%ecx                   // Return.
-1:
-    addl LITERAL(32), %esp
+    // Return result could have been changed if it's a reference.
+    movl 16(%esp), %ecx
+    movl %ecx, (80+32)(%esp)
+    addl LITERAL(32), %esp        // Pop arguments and grp_result.
     CFI_ADJUST_CFA_OFFSET(-32)
-    RESTORE_SAVE_REFS_ONLY_FRAME
-    DELIVER_PENDING_EXCEPTION
+
+    testl %eax, %eax              // Check if we returned error.
+    jz .Ldo_deliver_instrumentation_exception
+    testl %edx, %edx
+    jnz .Ldeoptimize
+    // Normal return.
+    movl %eax, FRAME_SIZE_SAVE_EVERYTHING-4(%esp)   // Set return pc.
+    RESTORE_SAVE_EVERYTHING_FRAME
+    ret
+.Ldeoptimize:
+    mov %edx, (FRAME_SIZE_SAVE_EVERYTHING-4)(%esp)  // Set return pc.
+    RESTORE_SAVE_EVERYTHING_FRAME
+    jmp SYMBOL(art_quick_deoptimize)
+.Ldo_deliver_instrumentation_exception:
+    DELIVER_PENDING_EXCEPTION_FRAME_READY
 END_FUNCTION art_quick_instrumentation_exit
 
     /*
@@ -2099,8 +2100,7 @@
      * will long jump to the upcall with a special exception of -1.
      */
 DEFINE_FUNCTION art_quick_deoptimize
-    PUSH ebx                      // Entry point for a jump. Fake that we were called.
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME ebx, ebx
+    SETUP_SAVE_EVERYTHING_FRAME ebx, ebx
     subl LITERAL(12), %esp        // Align stack.
     CFI_ADJUST_CFA_OFFSET(12)
     pushl %fs:THREAD_SELF_OFFSET  // Pass Thread::Current().
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 73e8610..6bf0828 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -2019,45 +2019,31 @@
     pushq LITERAL(0)          // Push a fake return PC as there will be none on the stack.
     CFI_ADJUST_CFA_OFFSET(8)
 
-    SETUP_SAVE_REFS_ONLY_FRAME
+    SETUP_SAVE_EVERYTHING_FRAME
 
-    // We need to save rax and xmm0. We could use a callee-save from SETUP_REF_ONLY, but then
-    // we would need to fully restore it. As there are a good number of callee-save registers, it
-    // seems easier to have an extra small stack area. But this should be revisited.
-
-    movq  %rsp, %rsi                          // Pass SP.
-
-    PUSH rax                  // Save integer result.
-    movq %rsp, %rdx           // Pass integer result pointer.
-
-    subq LITERAL(8), %rsp     // Save floating-point result.
-    CFI_ADJUST_CFA_OFFSET(8)
-    movq %xmm0, (%rsp)
-    movq %rsp, %rcx           // Pass floating-point result pointer.
-
-    movq  %gs:THREAD_SELF_OFFSET, %rdi        // Pass Thread.
+    leaq 16(%rsp), %rcx       // Pass floating-point result pointer, in kSaveEverything frame.
+    leaq 144(%rsp), %rdx      // Pass integer result pointer, in kSaveEverything frame.
+    movq %rsp, %rsi           // Pass SP.
+    movq %gs:THREAD_SELF_OFFSET, %rdi  // Pass Thread.
 
     call SYMBOL(artInstrumentationMethodExitFromCode)   // (Thread*, SP, gpr_res*, fpr_res*)
 
-    movq  %rax, %rdi          // Store return PC
-    movq  %rdx, %rsi          // Store second return PC in hidden arg.
-
-    movq (%rsp), %xmm0        // Restore floating-point result.
-    addq LITERAL(8), %rsp
-    CFI_ADJUST_CFA_OFFSET(-8)
-    POP rax                   // Restore integer result.
-
-    RESTORE_SAVE_REFS_ONLY_FRAME
-
-    testq %rdi, %rdi          // Check if we have a return-pc to go to. If we don't then there was
+    testq %rax, %rax          // Check if we have a return-pc to go to. If we don't then there was
                               // an exception
-    jz 1f
-
-    addq LITERAL(8), %rsp     // Drop fake return pc.
-
-    jmp   *%rdi               // Return.
-1:
-    DELIVER_PENDING_EXCEPTION
+    jz .Ldo_deliver_instrumentation_exception
+    testq %rdx, %rdx
+    jnz .Ldeoptimize
+    // Normal return.
+    movq %rax, FRAME_SIZE_SAVE_EVERYTHING-8(%rsp)  // Set return pc.
+    RESTORE_SAVE_EVERYTHING_FRAME
+    ret
+.Ldeoptimize:
+    movq %rdx, FRAME_SIZE_SAVE_EVERYTHING-8(%rsp)  // Set return pc.
+    RESTORE_SAVE_EVERYTHING_FRAME
+    // Jump to art_quick_deoptimize.
+    jmp SYMBOL(art_quick_deoptimize)
+.Ldo_deliver_instrumentation_exception:
+    DELIVER_PENDING_EXCEPTION_FRAME_READY
 END_FUNCTION art_quick_instrumentation_exit
 
     /*
@@ -2065,10 +2051,7 @@
      * will long jump to the upcall with a special exception of -1.
      */
 DEFINE_FUNCTION art_quick_deoptimize
-    pushq %rsi                         // Entry point for a jump. Fake that we were called.
-                                       // Use hidden arg.
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
-                                       // Stack should be aligned now.
+    SETUP_SAVE_EVERYTHING_FRAME        // Stack should be aligned now.
     movq %gs:THREAD_SELF_OFFSET, %rdi  // Pass Thread.
     call SYMBOL(artDeoptimize)         // (Thread*)
     UNREACHABLE
diff --git a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
index 53f0727..5f40711 100644
--- a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
@@ -73,7 +73,11 @@
   // Before deoptimizing to interpreter, we must push the deoptimization context.
   JValue return_value;
   return_value.SetJ(0);  // we never deoptimize from compiled code with an invoke result.
-  self->PushDeoptimizationContext(return_value, false, /* from_code */ true, self->GetException());
+  self->PushDeoptimizationContext(return_value,
+                                  false /* is_reference */,
+                                  self->GetException(),
+                                  true /* from_code */,
+                                  DeoptimizationMethodType::kDefault);
   artDeoptimizeImpl(self, kind, true);
 }
 
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index e08319d..5f71326 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -744,7 +744,11 @@
 
     ObjPtr<mirror::Throwable> pending_exception;
     bool from_code = false;
-    self->PopDeoptimizationContext(&result, &pending_exception, /* out */ &from_code);
+    DeoptimizationMethodType method_type;
+    self->PopDeoptimizationContext(/* out */ &result,
+                                   /* out */ &pending_exception,
+                                   /* out */ &from_code,
+                                   /* out */ &method_type);
 
     // Push a transition back into managed code onto the linked list in thread.
     self->PushManagedStackFragment(&fragment);
@@ -771,7 +775,11 @@
     if (pending_exception != nullptr) {
       self->SetException(pending_exception);
     }
-    interpreter::EnterInterpreterFromDeoptimize(self, deopt_frame, from_code, &result);
+    interpreter::EnterInterpreterFromDeoptimize(self,
+                                                deopt_frame,
+                                                &result,
+                                                from_code,
+                                                DeoptimizationMethodType::kDefault);
   } else {
     const char* old_cause = self->StartAssertNoThreadSuspension(
         "Building interpreter shadow frame");
@@ -823,7 +831,11 @@
       // Push the context of the deoptimization stack so we can restore the return value and the
       // exception before executing the deoptimized frames.
       self->PushDeoptimizationContext(
-          result, shorty[0] == 'L', /* from_code */ false, self->GetException());
+          result,
+          shorty[0] == 'L' || shorty[0] == '[',  /* class or array */
+          self->GetException(),
+          false /* from_code */,
+          DeoptimizationMethodType::kDefault);
 
       // Set special exception to cause deoptimization.
       self->SetException(Thread::GetDeoptimizationException());
@@ -1041,7 +1053,8 @@
   CHECK(!self->IsExceptionPending()) << "Enter instrumentation exit stub with pending exception "
                                      << self->GetException()->Dump();
   // Compute address of return PC and sanity check that it currently holds 0.
-  size_t return_pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveRefsOnly);
+  size_t return_pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA,
+                                                        CalleeSaveType::kSaveEverything);
   uintptr_t* return_pc = reinterpret_cast<uintptr_t*>(reinterpret_cast<uint8_t*>(sp) +
                                                       return_pc_offset);
   CHECK_EQ(*return_pc, 0U);
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 758c9e4..474e368 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -26,6 +26,7 @@
 #include "class_linker.h"
 #include "debugger.h"
 #include "dex_file-inl.h"
+#include "dex_instruction-inl.h"
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "entrypoints/runtime_asm_entrypoints.h"
@@ -227,39 +228,32 @@
         return true;  // Continue.
       }
       uintptr_t return_pc = GetReturnPc();
-      if (m->IsRuntimeMethod()) {
-        if (return_pc == instrumentation_exit_pc_) {
-          if (kVerboseInstrumentation) {
-            LOG(INFO) << "  Handling quick to interpreter transition. Frame " << GetFrameId();
-          }
-          CHECK_LT(instrumentation_stack_depth_, instrumentation_stack_->size());
-          const InstrumentationStackFrame& frame =
-              instrumentation_stack_->at(instrumentation_stack_depth_);
-          CHECK(frame.interpreter_entry_);
-          // This is an interpreter frame so method enter event must have been reported. However we
-          // need to push a DEX pc into the dex_pcs_ list to match size of instrumentation stack.
-          // Since we won't report method entry here, we can safely push any DEX pc.
-          dex_pcs_.push_back(0);
-          last_return_pc_ = frame.return_pc_;
-          ++instrumentation_stack_depth_;
-          return true;
-        } else {
-          if (kVerboseInstrumentation) {
-            LOG(INFO) << "  Skipping runtime method. Frame " << GetFrameId();
-          }
-          last_return_pc_ = GetReturnPc();
-          return true;  // Ignore unresolved methods since they will be instrumented after resolution.
-        }
-      }
       if (kVerboseInstrumentation) {
         LOG(INFO) << "  Installing exit stub in " << DescribeLocation();
       }
       if (return_pc == instrumentation_exit_pc_) {
+        CHECK_LT(instrumentation_stack_depth_, instrumentation_stack_->size());
+
+        if (m->IsRuntimeMethod()) {
+          const InstrumentationStackFrame& frame =
+              instrumentation_stack_->at(instrumentation_stack_depth_);
+          if (frame.interpreter_entry_) {
+            // This instrumentation frame is for an interpreter bridge and is
+            // pushed when executing the instrumented interpreter bridge. So method
+            // enter event must have been reported. However we need to push a DEX pc
+            // into the dex_pcs_ list to match size of instrumentation stack.
+            uint32_t dex_pc = DexFile::kDexNoIndex;
+            dex_pcs_.push_back(dex_pc);
+            last_return_pc_ = frame.return_pc_;
+            ++instrumentation_stack_depth_;
+            return true;
+          }
+        }
+
         // We've reached a frame which has already been installed with instrumentation exit stub.
         // We should have already installed instrumentation on previous frames.
         reached_existing_instrumentation_frames_ = true;
 
-        CHECK_LT(instrumentation_stack_depth_, instrumentation_stack_->size());
         const InstrumentationStackFrame& frame =
             instrumentation_stack_->at(instrumentation_stack_depth_);
         CHECK_EQ(m, frame.method_) << "Expected " << ArtMethod::PrettyMethod(m)
@@ -271,8 +265,12 @@
       } else {
         CHECK_NE(return_pc, 0U);
         CHECK(!reached_existing_instrumentation_frames_);
-        InstrumentationStackFrame instrumentation_frame(GetThisObject(), m, return_pc, GetFrameId(),
-                                                        false);
+        InstrumentationStackFrame instrumentation_frame(
+            m->IsRuntimeMethod() ? nullptr : GetThisObject(),
+            m,
+            return_pc,
+            GetFrameId(),    // A runtime method still gets a frame id.
+            false);
         if (kVerboseInstrumentation) {
           LOG(INFO) << "Pushing frame " << instrumentation_frame.Dump();
         }
@@ -289,9 +287,12 @@
         instrumentation_stack_->insert(it, instrumentation_frame);
         SetReturnPc(instrumentation_exit_pc_);
       }
-      dex_pcs_.push_back((GetCurrentOatQuickMethodHeader() == nullptr)
-          ? DexFile::kDexNoIndex
-          : GetCurrentOatQuickMethodHeader()->ToDexPc(m, last_return_pc_));
+      uint32_t dex_pc = DexFile::kDexNoIndex;
+      if (last_return_pc_ != 0 &&
+          GetCurrentOatQuickMethodHeader() != nullptr) {
+        dex_pc = GetCurrentOatQuickMethodHeader()->ToDexPc(m, last_return_pc_);
+      }
+      dex_pcs_.push_back(dex_pc);
       last_return_pc_ = return_pc;
       ++instrumentation_stack_depth_;
       return true;  // Continue.
@@ -389,7 +390,8 @@
             CHECK(m == instrumentation_frame.method_) << ArtMethod::PrettyMethod(m);
           }
           SetReturnPc(instrumentation_frame.return_pc_);
-          if (instrumentation_->ShouldNotifyMethodEnterExitEvents()) {
+          if (instrumentation_->ShouldNotifyMethodEnterExitEvents() &&
+              !m->IsRuntimeMethod()) {
             // Create the method exit events. As the methods didn't really exit the result is 0.
             // We only do this if no debugger is attached to prevent from posting events twice.
             instrumentation_->MethodExitEvent(thread_, instrumentation_frame.this_object_, m,
@@ -947,6 +949,7 @@
                                            ObjPtr<mirror::Object> this_object,
                                            ArtMethod* method,
                                            uint32_t dex_pc) const {
+  DCHECK(!method->IsRuntimeMethod());
   if (HasMethodEntryListeners()) {
     Thread* self = Thread::Current();
     StackHandleScope<1> hs(self);
@@ -1151,6 +1154,54 @@
   stack->push_front(instrumentation_frame);
 }
 
+DeoptimizationMethodType Instrumentation::GetDeoptimizationMethodType(ArtMethod* method) {
+  if (method->IsRuntimeMethod()) {
+    // Certain methods have strict requirement on whether the dex instruction
+    // should be re-executed upon deoptimization.
+    if (method == Runtime::Current()->GetCalleeSaveMethod(
+        CalleeSaveType::kSaveEverythingForClinit)) {
+      return DeoptimizationMethodType::kKeepDexPc;
+    }
+    if (method == Runtime::Current()->GetCalleeSaveMethod(
+        CalleeSaveType::kSaveEverythingForSuspendCheck)) {
+      return DeoptimizationMethodType::kKeepDexPc;
+    }
+  }
+  return DeoptimizationMethodType::kDefault;
+}
+
+// Try to get the shorty of a runtime method if it's an invocation stub.
+struct RuntimeMethodShortyVisitor : public StackVisitor {
+  explicit RuntimeMethodShortyVisitor(Thread* thread)
+      : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+        shorty('V') {}
+
+  bool VisitFrame() REQUIRES_SHARED(Locks::mutator_lock_) {
+    ArtMethod* m = GetMethod();
+    if (m != nullptr && !m->IsRuntimeMethod()) {
+      // The first Java method.
+      if (m->IsNative()) {
+        // Use JNI method's shorty for the jni stub.
+        shorty = m->GetShorty()[0];
+        return false;
+      }
+      const DexFile::CodeItem* code_item = m->GetCodeItem();
+      const Instruction* instr = Instruction::At(&code_item->insns_[GetDexPc()]);
+      if (instr->IsInvoke()) {
+        // If it's an invoke, use its shorty.
+        uint32_t method_idx = instr->VRegB();
+        shorty = m->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetDexFile()
+            ->GetMethodShorty(method_idx)[0];
+      }
+      // Stop stack walking since we've seen a Java frame.
+      return false;
+    }
+    return true;
+  }
+
+  char shorty;
+};
+
 TwoWordReturn Instrumentation::PopInstrumentationStackFrame(Thread* self,
                                                             uintptr_t* return_pc,
                                                             uint64_t* gpr_result,
@@ -1171,7 +1222,36 @@
   ArtMethod* method = instrumentation_frame.method_;
   uint32_t length;
   const PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-  char return_shorty = method->GetInterfaceMethodIfProxy(pointer_size)->GetShorty(&length)[0];
+  char return_shorty;
+
+  // Runtime method does not call into MethodExitEvent() so there should not be
+  // suspension point below.
+  ScopedAssertNoThreadSuspension ants(__FUNCTION__, method->IsRuntimeMethod());
+  if (method->IsRuntimeMethod()) {
+    if (method != Runtime::Current()->GetCalleeSaveMethod(
+        CalleeSaveType::kSaveEverythingForClinit)) {
+      // If the caller is at an invocation point and the runtime method is not
+      // for clinit, we need to pass return results to the caller.
+      // We need the correct shorty to decide whether we need to pass the return
+      // result for deoptimization below.
+      RuntimeMethodShortyVisitor visitor(self);
+      visitor.WalkStack();
+      return_shorty = visitor.shorty;
+    } else {
+      // Some runtime methods such as allocations, unresolved field getters, etc.
+      // have return value. We don't need to set return_value since MethodExitEvent()
+      // below isn't called for runtime methods. Deoptimization doesn't need the
+      // value either since the dex instruction will be re-executed by the
+      // interpreter, except these two cases:
+      // (1) For an invoke, which is handled above to get the correct shorty.
+      // (2) For MONITOR_ENTER/EXIT, which cannot be re-executed since it's not
+      //     idempotent. However there is no return value for it anyway.
+      return_shorty = 'V';
+    }
+  } else {
+    return_shorty = method->GetInterfaceMethodIfProxy(pointer_size)->GetShorty(&length)[0];
+  }
+
   bool is_ref = return_shorty == '[' || return_shorty == 'L';
   StackHandleScope<1> hs(self);
   MutableHandle<mirror::Object> res(hs.NewHandle<mirror::Object>(nullptr));
@@ -1191,7 +1271,7 @@
   //       return_pc.
   uint32_t dex_pc = DexFile::kDexNoIndex;
   mirror::Object* this_object = instrumentation_frame.this_object_;
-  if (!instrumentation_frame.interpreter_entry_) {
+  if (!method->IsRuntimeMethod() && !instrumentation_frame.interpreter_entry_) {
     MethodExitEvent(self, this_object, instrumentation_frame.method_, dex_pc, return_value);
   }
 
@@ -1217,10 +1297,12 @@
                 << " in "
                 << *self;
     }
+    DeoptimizationMethodType deopt_method_type = GetDeoptimizationMethodType(method);
     self->PushDeoptimizationContext(return_value,
-                                    return_shorty == 'L',
+                                    return_shorty == 'L' || return_shorty == '[',
+                                    nullptr /* no pending exception */,
                                     false /* from_code */,
-                                    nullptr /* no pending exception */);
+                                    deopt_method_type);
     return GetTwoWordSuccessValue(*return_pc,
                                   reinterpret_cast<uintptr_t>(GetQuickDeoptimizationEntryPoint()));
   } else {
@@ -1257,7 +1339,9 @@
     // TODO: improve the dex pc information here, requires knowledge of current PC as opposed to
     //       return_pc.
     uint32_t dex_pc = DexFile::kDexNoIndex;
-    MethodUnwindEvent(self, instrumentation_frame.this_object_, method, dex_pc);
+    if (!method->IsRuntimeMethod()) {
+      MethodUnwindEvent(self, instrumentation_frame.this_object_, method, dex_pc);
+    }
   }
   // TODO: bring back CheckStackDepth(self, instrumentation_frame, 2);
   CHECK_EQ(stack->size(), idx);
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index f6efb5f..de416c7 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -39,6 +39,7 @@
 template <typename T> class Handle;
 union JValue;
 class Thread;
+enum class DeoptimizationMethodType;
 
 namespace instrumentation {
 
@@ -435,6 +436,9 @@
                                      bool interpreter_entry)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  DeoptimizationMethodType GetDeoptimizationMethodType(ArtMethod* method)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Called when an instrumented method is exited. Removes the pushed instrumentation frame
   // returning the intended link register. Generates method exit events. The gpr_result and
   // fpr_result pointers are pointers to the locations where the integer/pointer and floating point
@@ -661,9 +665,15 @@
 
 // An element in the instrumentation side stack maintained in art::Thread.
 struct InstrumentationStackFrame {
-  InstrumentationStackFrame(mirror::Object* this_object, ArtMethod* method,
-                            uintptr_t return_pc, size_t frame_id, bool interpreter_entry)
-      : this_object_(this_object), method_(method), return_pc_(return_pc), frame_id_(frame_id),
+  InstrumentationStackFrame(mirror::Object* this_object,
+                            ArtMethod* method,
+                            uintptr_t return_pc,
+                            size_t frame_id,
+                            bool interpreter_entry)
+      : this_object_(this_object),
+        method_(method),
+        return_pc_(return_pc),
+        frame_id_(frame_id),
         interpreter_entry_(interpreter_entry) {
   }
 
diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc
index 19ce299..5ec07e3 100644
--- a/runtime/instrumentation_test.cc
+++ b/runtime/instrumentation_test.cc
@@ -473,7 +473,23 @@
 
 // Test instrumentation listeners for each event.
 TEST_F(InstrumentationTest, MethodEntryEvent) {
-  TestEvent(instrumentation::Instrumentation::kMethodEntered);
+  ScopedObjectAccess soa(Thread::Current());
+  jobject class_loader = LoadDex("Instrumentation");
+  Runtime* const runtime = Runtime::Current();
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
+  mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
+  ASSERT_TRUE(klass != nullptr);
+  ArtMethod* method =
+      klass->FindClassMethod("returnReference", "()Ljava/lang/Object;", kRuntimePointerSize);
+  ASSERT_TRUE(method != nullptr);
+  ASSERT_TRUE(method->IsDirect());
+  ASSERT_TRUE(method->GetDeclaringClass() == klass);
+  TestEvent(instrumentation::Instrumentation::kMethodEntered,
+            /*event_method*/ method,
+            /*event_field*/ nullptr,
+            /*with_object*/ true);
 }
 
 TEST_F(InstrumentationTest, MethodExitObjectEvent) {
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 9cb74f7..34332d7 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -499,8 +499,9 @@
 
 void EnterInterpreterFromDeoptimize(Thread* self,
                                     ShadowFrame* shadow_frame,
+                                    JValue* ret_val,
                                     bool from_code,
-                                    JValue* ret_val)
+                                    DeoptimizationMethodType deopt_method_type)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   JValue value;
   // Set value to last known result in case the shadow frame chain is empty.
@@ -527,11 +528,27 @@
       new_dex_pc = found_dex_pc;  // the dex pc of a matching catch handler
                                   // or DexFile::kDexNoIndex if there is none.
     } else if (!from_code) {
-      // For the debugger and full deoptimization stack, we must go past the invoke
-      // instruction, as it already executed.
-      // TODO: should be tested more once b/17586779 is fixed.
+      // Deoptimization is not called from code directly.
       const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]);
-      if (instr->IsInvoke()) {
+      if (deopt_method_type == DeoptimizationMethodType::kKeepDexPc) {
+        DCHECK(first);
+        // Need to re-execute the dex instruction.
+        // (1) An invocation might be split into class initialization and invoke.
+        //     In this case, the invoke should not be skipped.
+        // (2) A suspend check should also execute the dex instruction at the
+        //     corresponding dex pc.
+        DCHECK_EQ(new_dex_pc, dex_pc);
+      } else if (instr->Opcode() == Instruction::MONITOR_ENTER ||
+                 instr->Opcode() == Instruction::MONITOR_EXIT) {
+        DCHECK(deopt_method_type == DeoptimizationMethodType::kDefault);
+        DCHECK(first);
+        // Non-idempotent dex instruction should not be re-executed.
+        // On the other hand, if a MONITOR_ENTER is at the dex_pc of a suspend
+        // check, that MONITOR_ENTER should be executed. That case is handled
+        // above.
+        new_dex_pc = dex_pc + instr->SizeInCodeUnits();
+      } else if (instr->IsInvoke()) {
+        DCHECK(deopt_method_type == DeoptimizationMethodType::kDefault);
         if (IsStringInit(instr, shadow_frame->GetMethod())) {
           uint16_t this_obj_vreg = GetReceiverRegisterForStringInit(instr);
           // Move the StringFactory.newStringFromChars() result into the register representing
@@ -544,30 +561,27 @@
         }
         new_dex_pc = dex_pc + instr->SizeInCodeUnits();
       } else if (instr->Opcode() == Instruction::NEW_INSTANCE) {
-        // It's possible to deoptimize at a NEW_INSTANCE dex instruciton that's for a
-        // java string, which is turned into a call into StringFactory.newEmptyString();
-        // Move the StringFactory.newEmptyString() result into the destination register.
-        DCHECK(value.GetL()->IsString());
-        shadow_frame->SetVRegReference(instr->VRegA_21c(), value.GetL());
-        // new-instance doesn't generate a result value.
-        value.SetJ(0);
-        // Skip the dex instruction since we essentially come back from an invocation.
-        new_dex_pc = dex_pc + instr->SizeInCodeUnits();
-        if (kIsDebugBuild) {
-          ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-          // This is a suspend point. But it's ok since value has been set into shadow_frame.
-          ObjPtr<mirror::Class> klass = class_linker->ResolveType(
-              dex::TypeIndex(instr->VRegB_21c()), shadow_frame->GetMethod());
-          DCHECK(klass->IsStringClass());
-        }
+        // A NEW_INSTANCE is simply re-executed, including
+        // "new-instance String" which is compiled into a call into
+        // StringFactory.newEmptyString().
+        DCHECK_EQ(new_dex_pc, dex_pc);
       } else {
-        CHECK(false) << "Unexpected instruction opcode " << instr->Opcode()
-                     << " at dex_pc " << dex_pc
-                     << " of method: " << ArtMethod::PrettyMethod(shadow_frame->GetMethod(), false);
+        DCHECK(deopt_method_type == DeoptimizationMethodType::kDefault);
+        DCHECK(first);
+        // By default, we re-execute the dex instruction since if they are not
+        // an invoke, so that we don't have to decode the dex instruction to move
+        // result into the right vreg. All slow paths have been audited to be
+        // idempotent except monitor-enter/exit and invocation stubs.
+        // TODO: move result and advance dex pc. That also requires that we
+        // can tell the return type of a runtime method, possibly by decoding
+        // the dex instruction at the caller.
+        DCHECK_EQ(new_dex_pc, dex_pc);
       }
     } else {
       // Nothing to do, the dex_pc is the one at which the code requested
       // the deoptimization.
+      DCHECK(first);
+      DCHECK_EQ(new_dex_pc, dex_pc);
     }
     if (new_dex_pc != DexFile::kDexNoIndex) {
       shadow_frame->SetDexPC(new_dex_pc);
@@ -576,8 +590,10 @@
     ShadowFrame* old_frame = shadow_frame;
     shadow_frame = shadow_frame->GetLink();
     ShadowFrame::DeleteDeoptimizedFrame(old_frame);
-    // Following deoptimizations of shadow frames must pass the invoke instruction.
+    // Following deoptimizations of shadow frames must be at invocation point
+    // and should advance dex pc past the invoke instruction.
     from_code = false;
+    deopt_method_type = DeoptimizationMethodType::kDefault;
     first = false;
   }
   ret_val->SetJ(value.GetJ());
diff --git a/runtime/interpreter/interpreter.h b/runtime/interpreter/interpreter.h
index 65cfade..df8568e 100644
--- a/runtime/interpreter/interpreter.h
+++ b/runtime/interpreter/interpreter.h
@@ -30,6 +30,7 @@
 union JValue;
 class ShadowFrame;
 class Thread;
+enum class DeoptimizationMethodType;
 
 namespace interpreter {
 
@@ -44,8 +45,11 @@
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 // 'from_code' denotes whether the deoptimization was explicitly triggered by compiled code.
-extern void EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame* shadow_frame, bool from_code,
-                                           JValue* ret_val)
+extern void EnterInterpreterFromDeoptimize(Thread* self,
+                                           ShadowFrame* shadow_frame,
+                                           JValue* ret_val,
+                                           bool from_code,
+                                           DeoptimizationMethodType method_type)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 extern JValue EnterInterpreterFromEntryPoint(Thread* self, const DexFile::CodeItem* code_item,
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 3f23926..57b3a75 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -166,11 +166,13 @@
                               bool is_reference,
                               bool from_code,
                               ObjPtr<mirror::Throwable> pending_exception,
+                              DeoptimizationMethodType method_type,
                               DeoptimizationContextRecord* link)
       : ret_val_(ret_val),
         is_reference_(is_reference),
         from_code_(from_code),
         pending_exception_(pending_exception.Ptr()),
+        deopt_method_type_(method_type),
         link_(link) {}
 
   JValue GetReturnValue() const { return ret_val_; }
@@ -185,6 +187,9 @@
   mirror::Object** GetPendingExceptionAsGCRoot() {
     return reinterpret_cast<mirror::Object**>(&pending_exception_);
   }
+  DeoptimizationMethodType GetDeoptimizationMethodType() const {
+    return deopt_method_type_;
+  }
 
  private:
   // The value returned by the method at the top of the stack before deoptimization.
@@ -200,6 +205,9 @@
   // exception).
   mirror::Throwable* pending_exception_;
 
+  // Whether the context was created for an (idempotent) runtime method.
+  const DeoptimizationMethodType deopt_method_type_;
+
   // A link to the previous DeoptimizationContextRecord.
   DeoptimizationContextRecord* const link_;
 
@@ -229,26 +237,30 @@
 
 void Thread::PushDeoptimizationContext(const JValue& return_value,
                                        bool is_reference,
+                                       ObjPtr<mirror::Throwable> exception,
                                        bool from_code,
-                                       ObjPtr<mirror::Throwable> exception) {
+                                       DeoptimizationMethodType method_type) {
   DeoptimizationContextRecord* record = new DeoptimizationContextRecord(
       return_value,
       is_reference,
       from_code,
       exception,
+      method_type,
       tlsPtr_.deoptimization_context_stack);
   tlsPtr_.deoptimization_context_stack = record;
 }
 
 void Thread::PopDeoptimizationContext(JValue* result,
                                       ObjPtr<mirror::Throwable>* exception,
-                                      bool* from_code) {
+                                      bool* from_code,
+                                      DeoptimizationMethodType* method_type) {
   AssertHasDeoptimizationContext();
   DeoptimizationContextRecord* record = tlsPtr_.deoptimization_context_stack;
   tlsPtr_.deoptimization_context_stack = record->GetLink();
   result->SetJ(record->GetReturnValue().GetJ());
   *exception = record->GetPendingException();
   *from_code = record->GetFromCode();
+  *method_type = record->GetDeoptimizationMethodType();
   delete record;
 }
 
@@ -3084,10 +3096,16 @@
     NthCallerVisitor visitor(this, 0, false);
     visitor.WalkStack();
     if (Runtime::Current()->IsAsyncDeoptimizeable(visitor.caller_pc)) {
+      // method_type shouldn't matter due to exception handling.
+      const DeoptimizationMethodType method_type = DeoptimizationMethodType::kDefault;
       // Save the exception into the deoptimization context so it can be restored
       // before entering the interpreter.
       PushDeoptimizationContext(
-          JValue(), /*is_reference */ false, /* from_code */ false, exception);
+          JValue(),
+          false /* is_reference */,
+          exception,
+          false /* from_code */,
+          method_type);
       artDeoptimize(this);
       UNREACHABLE();
     } else {
@@ -3647,7 +3665,8 @@
       PopStackedShadowFrame(StackedShadowFrameType::kDeoptimizationShadowFrame);
   ObjPtr<mirror::Throwable> pending_exception;
   bool from_code = false;
-  PopDeoptimizationContext(result, &pending_exception, &from_code);
+  DeoptimizationMethodType method_type;
+  PopDeoptimizationContext(result, &pending_exception, &from_code, &method_type);
   SetTopOfStack(nullptr);
   SetTopOfShadowStack(shadow_frame);
 
@@ -3656,7 +3675,11 @@
   if (pending_exception != nullptr) {
     SetException(pending_exception);
   }
-  interpreter::EnterInterpreterFromDeoptimize(this, shadow_frame, from_code, result);
+  interpreter::EnterInterpreterFromDeoptimize(this,
+                                              shadow_frame,
+                                              result,
+                                              from_code,
+                                              method_type);
 }
 
 void Thread::SetException(ObjPtr<mirror::Throwable> new_exception) {
diff --git a/runtime/thread.h b/runtime/thread.h
index 7540fd2..ad4506e 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -117,6 +117,13 @@
   kDeoptimizationShadowFrame,
 };
 
+// The type of method that triggers deoptimization. It contains info on whether
+// the deoptimized method should advance dex_pc.
+enum class DeoptimizationMethodType {
+  kKeepDexPc,  // dex pc is required to be kept upon deoptimization.
+  kDefault     // dex pc may or may not advance depending on other conditions.
+};
+
 // This should match RosAlloc::kNumThreadLocalSizeBrackets.
 static constexpr size_t kNumRosAllocThreadLocalSizeBracketsInThread = 16;
 
@@ -960,14 +967,18 @@
   // values on stacks.
   // 'from_code' denotes whether the deoptimization was explicitly made from
   // compiled code.
+  // 'method_type' contains info on whether deoptimization should advance
+  // dex_pc.
   void PushDeoptimizationContext(const JValue& return_value,
                                  bool is_reference,
+                                 ObjPtr<mirror::Throwable> exception,
                                  bool from_code,
-                                 ObjPtr<mirror::Throwable> exception)
+                                 DeoptimizationMethodType method_type)
       REQUIRES_SHARED(Locks::mutator_lock_);
   void PopDeoptimizationContext(JValue* result,
                                 ObjPtr<mirror::Throwable>* exception,
-                                bool* from_code)
+                                bool* from_code,
+                                DeoptimizationMethodType* method_type)
       REQUIRES_SHARED(Locks::mutator_lock_);
   void AssertHasDeoptimizationContext()
       REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/test/597-deopt-busy-loop/expected.txt b/test/597-deopt-busy-loop/expected.txt
new file mode 100644
index 0000000..f993efc
--- /dev/null
+++ b/test/597-deopt-busy-loop/expected.txt
@@ -0,0 +1,2 @@
+JNI_OnLoad called
+Finishing
diff --git a/test/597-deopt-busy-loop/info.txt b/test/597-deopt-busy-loop/info.txt
new file mode 100644
index 0000000..2c50dbb
--- /dev/null
+++ b/test/597-deopt-busy-loop/info.txt
@@ -0,0 +1 @@
+Test deoptimizing when returning from suspend-check runtime method.
diff --git a/test/597-deopt-busy-loop/run b/test/597-deopt-busy-loop/run
new file mode 100644
index 0000000..bc04498
--- /dev/null
+++ b/test/597-deopt-busy-loop/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# We want to run in debuggable mode and compiled.
+exec ${RUN} --jit -Xcompiler-option --debuggable "${@}"
diff --git a/test/597-deopt-busy-loop/src/Main.java b/test/597-deopt-busy-loop/src/Main.java
new file mode 100644
index 0000000..46b6bbf
--- /dev/null
+++ b/test/597-deopt-busy-loop/src/Main.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main implements Runnable {
+    static final int numberOfThreads = 2;
+    volatile static boolean sExitFlag = false;
+    volatile static boolean sEntered = false;
+    int threadIndex;
+
+    private static native void deoptimizeAll();
+    private static native void assertIsInterpreted();
+    private static native void assertIsManaged();
+    private static native void ensureJitCompiled(Class<?> cls, String methodName);
+
+    Main(int index) {
+        threadIndex = index;
+    }
+
+    public static void main(String[] args) throws Exception {
+        System.loadLibrary(args[0]);
+
+        final Thread[] threads = new Thread[numberOfThreads];
+        for (int t = 0; t < threads.length; t++) {
+            threads[t] = new Thread(new Main(t));
+            threads[t].start();
+        }
+        for (Thread t : threads) {
+            t.join();
+        }
+        System.out.println("Finishing");
+    }
+
+    public void $noinline$busyLoop() {
+        assertIsManaged();
+        sEntered = true;
+        for (;;) {
+            if (sExitFlag) {
+                break;
+            }
+        }
+        assertIsInterpreted();
+    }
+
+    public void run() {
+        if (threadIndex == 0) {
+            while (!sEntered) {
+              Thread.yield();
+            }
+            deoptimizeAll();
+            sExitFlag = true;
+        } else {
+            ensureJitCompiled(Main.class, "$noinline$busyLoop");
+            $noinline$busyLoop();
+        }
+    }
+}
diff --git a/test/597-deopt-invoke-stub/expected.txt b/test/597-deopt-invoke-stub/expected.txt
new file mode 100644
index 0000000..f993efc
--- /dev/null
+++ b/test/597-deopt-invoke-stub/expected.txt
@@ -0,0 +1,2 @@
+JNI_OnLoad called
+Finishing
diff --git a/test/597-deopt-invoke-stub/info.txt b/test/597-deopt-invoke-stub/info.txt
new file mode 100644
index 0000000..31960a9
--- /dev/null
+++ b/test/597-deopt-invoke-stub/info.txt
@@ -0,0 +1 @@
+Test deoptimizing when returning from a quick-to-interpreter bridge.
diff --git a/test/597-deopt-invoke-stub/run b/test/597-deopt-invoke-stub/run
new file mode 100644
index 0000000..bc04498
--- /dev/null
+++ b/test/597-deopt-invoke-stub/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# We want to run in debuggable mode and compiled.
+exec ${RUN} --jit -Xcompiler-option --debuggable "${@}"
diff --git a/test/597-deopt-invoke-stub/src/Main.java b/test/597-deopt-invoke-stub/src/Main.java
new file mode 100644
index 0000000..0751783
--- /dev/null
+++ b/test/597-deopt-invoke-stub/src/Main.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main implements Runnable {
+    static final int numberOfThreads = 2;
+    volatile static boolean sExitFlag = false;
+    volatile static boolean sEntered = false;
+    int threadIndex;
+
+    private static native void deoptimizeAll();
+    private static native void assertIsInterpreted();
+    private static native void assertIsManaged();
+    private static native void ensureJitCompiled(Class<?> cls, String methodName);
+
+    Main(int index) {
+        threadIndex = index;
+    }
+
+    public static void main(String[] args) throws Exception {
+        System.loadLibrary(args[0]);
+
+        final Thread[] threads = new Thread[numberOfThreads];
+        for (int t = 0; t < threads.length; t++) {
+            threads[t] = new Thread(new Main(t));
+            threads[t].start();
+        }
+        for (Thread t : threads) {
+            t.join();
+        }
+        System.out.println("Finishing");
+    }
+
+    private static int $noinline$bar() {
+        // Should be entered via interpreter bridge.
+        assertIsInterpreted();
+        sEntered = true;
+        while (!sExitFlag) {}
+        assertIsInterpreted();
+        return 0x1234;
+    }
+
+    public void $noinline$foo() {
+        assertIsManaged();
+        if ($noinline$bar() != 0x1234) {
+            System.out.println("Bad return value");
+        }
+        assertIsInterpreted();
+    }
+
+    public void run() {
+        if (threadIndex == 0) {
+            while (!sEntered) {
+              Thread.yield();
+            }
+            deoptimizeAll();
+            sExitFlag = true;
+        } else {
+            ensureJitCompiled(Main.class, "$noinline$foo");
+            $noinline$foo();
+        }
+    }
+}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 20cfc34..d76103a 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -207,7 +207,9 @@
         "variant": "trace | stream"
     },
     {
-        "tests": ["604-hot-static-interface",
+        "tests": ["597-deopt-busy-loop",
+                  "597-deopt-invoke-stub",
+                  "604-hot-static-interface",
                   "612-jit-dex-cache",
                   "613-inlining-dex-cache",
                   "626-set-resolved-string"],