Switch-interpreter: Refactor exception handling.

Move exception handling code to the main loop.

The byte-code handlers must return false if there is an exception,
and the main loop will then handle it in the common slow-path.

This allows us to remove the remaining helper macros,
and it shrinks the interpreter uber-function by ~10%.

Test: test.py -b -r --host --64 --interpreter
Test: run-libjdwp-tests.sh --mode=host --variant=X64 --debug --no-jit
Change-Id: I38e9b31e33c609db800065c16df77023bea7c2e5
diff --git a/runtime/interpreter/interpreter_switch_impl-inl.h b/runtime/interpreter/interpreter_switch_impl-inl.h
index 13aa9e7..2689838 100644
--- a/runtime/interpreter/interpreter_switch_impl-inl.h
+++ b/runtime/interpreter/interpreter_switch_impl-inl.h
@@ -44,13 +44,11 @@
 namespace interpreter {
 
 // Short-lived helper class which executes single DEX bytecode.  It is inlined by compiler.
+// Any relevant execution information is stored in the fields - it should be kept to minimum.
+// All instance functions must be inlined so that the fields can be stored in registers.
 //
 // The function names must match the names from dex_instruction_list.h and have no arguments.
-//
-// Any relevant execution information is stored in the fields - it should be kept to minimum.
-//
-// Helper methods may return boolean value - in which case 'false' always means
-// "stop executing current opcode" (which does not necessarily exit the interpreter loop).
+// Return value: The handlers must return false if the instruction throws or returns (exits).
 //
 template<bool do_access_check, bool transaction_active, Instruction::Format kFormat>
 class InstructionHandler {
@@ -69,21 +67,18 @@
     return true;
   }
 
-  NO_INLINE WARN_UNUSED bool HandlePendingExceptionWithInstrumentationImpl(
-      const instrumentation::Instrumentation* instr)
+  ALWAYS_INLINE WARN_UNUSED bool HandlePendingException()
       REQUIRES_SHARED(Locks::mutator_lock_) {
     DCHECK(self->IsExceptionPending());
     self->AllowThreadSuspension();
     if (!CheckForceReturn()) {
       return false;
     }
-    if (!MoveToExceptionHandler(self, shadow_frame, instr)) {
+    bool skip_event = shadow_frame.GetSkipNextExceptionEvent();
+    shadow_frame.SetSkipNextExceptionEvent(false);
+    if (!MoveToExceptionHandler(self, shadow_frame, skip_event ? nullptr : instrumentation)) {
       /* Structured locking is to be enforced for abnormal termination, too. */
       DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame);
-      if (ctx->interpret_one_instruction) {
-        /* Signal mterp to return to caller */
-        shadow_frame.SetDexPC(dex::kDexNoIndex);
-      }
       ctx->result = JValue(); /* Handled in caller. */
       exit_interpreter_loop = true;
       return false;  // Return to caller.
@@ -94,36 +89,10 @@
     int32_t displacement =
         static_cast<int32_t>(shadow_frame.GetDexPC()) - static_cast<int32_t>(dex_pc);
     SetNextInstruction(inst->RelativeAt(displacement));
-    return false;  // Stop executing this opcode and continue in the exception handler.
+    return true;
   }
 
-  // Forwards the call to the NO_INLINE HandlePendingExceptionWithInstrumentationImpl.
-  ALWAYS_INLINE WARN_UNUSED bool HandlePendingExceptionWithInstrumentation(
-      const instrumentation::Instrumentation* instr)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    // We need to help the compiler a bit to make the NO_INLINE call efficient.
-    //  * All handler fields should be in registers, so we do not want to take the object
-    //    address (for 'this' argument). Make a copy of the handler just for the slow path.
-    //  * The modifiable fields should also be in registers, so we don't want to store their
-    //    address even in the handler copy. Make a copy of them just for the call as well.
-    const Instruction* next_copy = next;
-    bool exit_copy = exit_interpreter_loop;
-    InstructionHandler<do_access_check, transaction_active, kFormat> handler_copy(
-        ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next_copy, exit_copy);
-    bool result = handler_copy.HandlePendingExceptionWithInstrumentationImpl(instr);
-    next = next_copy;
-    exit_interpreter_loop = exit_copy;
-    return result;
-  }
-
-  ALWAYS_INLINE WARN_UNUSED bool HandlePendingException()
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    return HandlePendingExceptionWithInstrumentation(instrumentation);
-  }
-
-  ALWAYS_INLINE WARN_UNUSED bool PossiblyHandlePendingExceptionOnInvokeImpl(
-      bool is_exception_pending,
-      const Instruction* next_inst)
+  ALWAYS_INLINE WARN_UNUSED bool PossiblyHandlePendingExceptionOnInvoke(bool is_exception_pending)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     if (UNLIKELY(shadow_frame.GetForceRetryInstruction())) {
       /* Don't need to do anything except clear the flag and exception. We leave the */
@@ -142,27 +111,7 @@
     } else if (UNLIKELY(is_exception_pending)) {
       /* Should have succeeded. */
       DCHECK(!shadow_frame.GetForceRetryInstruction());
-      if (!HandlePendingException()) {
-        return false;
-      }
-    } else {
-      SetNextInstruction(next_inst);
-    }
-    return true;
-  }
-
-  ALWAYS_INLINE WARN_UNUSED bool PossiblyHandlePendingException(
-      bool is_exception_pending,
-      const Instruction* next_inst)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    /* Should only be on invoke instructions. */
-    DCHECK(!shadow_frame.GetForceRetryInstruction());
-    if (UNLIKELY(is_exception_pending)) {
-      if (!HandlePendingException()) {
-        return false;
-      }
-    } else {
-      SetNextInstruction(next_inst);
+      return false;  // Pending exception.
     }
     return true;
   }
@@ -170,9 +119,7 @@
   ALWAYS_INLINE WARN_UNUSED bool HandleMonitorChecks()
       REQUIRES_SHARED(Locks::mutator_lock_) {
     if (!DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame)) {
-      if (!HandlePendingException()) {
-        return false;
-      }
+      return false;  // Pending exception.
     }
     return true;
   }
@@ -195,9 +142,10 @@
                                      dex_pc,
                                      instrumentation,
                                      save_ref))) {
-        if (!HandlePendingException()) {
-          return false;
-        }
+        DCHECK(self->IsExceptionPending());
+        // Do not raise exception event if it is caused by other instrumentation event.
+        shadow_frame.SetSkipNextExceptionEvent(true);
+        return false;  // Pending exception.
       }
       if (!CheckForceReturn()) {
         return false;
@@ -217,10 +165,6 @@
                                             dex_pc,
                                             offset,
                                             &result)) {
-      if (ctx->interpret_one_instruction) {
-        /* OSR has completed execution of the method.  Signal mterp to return to caller */
-        shadow_frame.SetDexPC(dex::kDexNoIndex);
-      }
       ctx->result = result;
       exit_interpreter_loop = true;
       return false;
@@ -239,9 +183,7 @@
   ALWAYS_INLINE WARN_UNUSED bool HandleAsyncException()
       REQUIRES_SHARED(Locks::mutator_lock_) {
     if (UNLIKELY(self->ObserveAsyncException())) {
-      if (!HandlePendingException()) {
-        return false;
-      }
+      return false;  // Pending exception.
     }
     return true;
   }
@@ -284,7 +226,7 @@
       // We just let this exception replace the old one.
       // TODO It would be good to add the old exception to the
       // suppressed exceptions of the new one if possible.
-      return false;
+      return false;  // Pending exception.
     } else {
       if (UNLIKELY(!thr.IsNull())) {
         self->SetException(thr.Get());
@@ -293,31 +235,6 @@
     }
   }
 
-#define BRANCH_INSTRUMENTATION(offset)                                                            \
-  if (!BranchInstrumentation(offset)) {                                                           \
-    return false;                                                                                 \
-  }
-
-#define HANDLE_PENDING_EXCEPTION()                                                                \
-  if (!HandlePendingException()) {                                                                \
-    return false;                                                                                 \
-  }
-
-#define POSSIBLY_HANDLE_PENDING_EXCEPTION(is_exception_pending, next_function)                    \
-  if (!PossiblyHandlePendingException(is_exception_pending, inst->next_function())) {             \
-    return false;                                                                                 \
-  }
-
-#define POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(is_exception_pending)             \
-  if (!PossiblyHandlePendingExceptionOnInvokeImpl(is_exception_pending, inst->Next_4xx())) {      \
-    return false;                                                                                 \
-  }
-
-#define POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(is_exception_pending)                         \
-  if (!PossiblyHandlePendingExceptionOnInvokeImpl(is_exception_pending, inst->Next_3xx())) {      \
-    return false;                                                                                 \
-  }
-
   ALWAYS_INLINE WARN_UNUSED bool HandleReturn(JValue result) REQUIRES_SHARED(Locks::mutator_lock_) {
     self->AllowThreadSuspension();
     if (!HandleMonitorChecks()) {
@@ -331,24 +248,23 @@
                                        shadow_frame.GetMethod(),
                                        inst->GetDexPc(Insns()),
                                        result))) {
-      if (!HandlePendingExceptionWithInstrumentation(nullptr)) {
-        return false;
-      }
-    }
-    if (ctx->interpret_one_instruction) {
-      /* Signal mterp to return to caller */
-      shadow_frame.SetDexPC(dex::kDexNoIndex);
+      DCHECK(self->IsExceptionPending());
+      // Do not raise exception event if it is caused by other instrumentation event.
+      shadow_frame.SetSkipNextExceptionEvent(true);
+      return false;  // Pending exception.
     }
     ctx->result = result;
     exit_interpreter_loop = true;
-    return true;
+    return false;
   }
 
   ALWAYS_INLINE WARN_UNUSED bool HandleGoto(int32_t offset) REQUIRES_SHARED(Locks::mutator_lock_) {
     if (!HandleAsyncException()) {
       return false;
     }
-    BRANCH_INSTRUMENTATION(offset);
+    if (!BranchInstrumentation(offset)) {
+      return false;
+    }
     SetNextInstruction(inst->RelativeAt(offset));
     HandleBackwardBranch(offset);
     return true;
@@ -391,11 +307,15 @@
   ALWAYS_INLINE WARN_UNUSED bool HandleIf(bool cond, int32_t offset)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     if (cond) {
-      BRANCH_INSTRUMENTATION(offset);
+      if (!BranchInstrumentation(offset)) {
+        return false;
+      }
       SetNextInstruction(inst->RelativeAt(offset));
       HandleBackwardBranch(offset);
     } else {
-      BRANCH_INSTRUMENTATION(2);
+      if (!BranchInstrumentation(2)) {
+        return false;
+      }
     }
     return true;
   }
@@ -405,12 +325,12 @@
     ObjPtr<mirror::Object> a = GetVRegReference(B());
     if (UNLIKELY(a == nullptr)) {
       ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
+      return false;  // Pending exception.
     }
     int32_t index = GetVReg(C());
     ObjPtr<ArrayType> array = ObjPtr<ArrayType>::DownCast(a);
     if (UNLIKELY(!array->CheckIsValidIndex(index))) {
-      HANDLE_PENDING_EXCEPTION();
+      return false;  // Pending exception.
     } else {
       (this->*setVReg)(A(), array->GetWithoutChecks(index));
     }
@@ -422,12 +342,12 @@
     ObjPtr<mirror::Object> a = GetVRegReference(B());
     if (UNLIKELY(a == nullptr)) {
       ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
+      return false;  // Pending exception.
     }
     int32_t index = GetVReg(C());
     ObjPtr<ArrayType> array = ObjPtr<ArrayType>::DownCast(a);
     if (UNLIKELY(!array->CheckIsValidIndex(index))) {
-      HANDLE_PENDING_EXCEPTION();
+      return false;  // Pending exception.
     } else {
       array->template SetWithoutChecks<transaction_active>(index, value);
     }
@@ -436,41 +356,32 @@
 
   template<FindFieldType find_type, Primitive::Type field_type>
   ALWAYS_INLINE WARN_UNUSED bool HandleGet() REQUIRES_SHARED(Locks::mutator_lock_) {
-    bool success = DoFieldGet<find_type, field_type, do_access_check, transaction_active>(
+    return DoFieldGet<find_type, field_type, do_access_check, transaction_active>(
         self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-    return true;
   }
 
   template<Primitive::Type field_type>
   ALWAYS_INLINE WARN_UNUSED bool HandleGetQuick() REQUIRES_SHARED(Locks::mutator_lock_) {
-    bool success = DoIGetQuick<field_type>(shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-    return true;
+    return DoIGetQuick<field_type>(shadow_frame, inst, inst_data);
   }
 
   template<FindFieldType find_type, Primitive::Type field_type>
   ALWAYS_INLINE WARN_UNUSED bool HandlePut() REQUIRES_SHARED(Locks::mutator_lock_) {
-    bool success = DoFieldPut<find_type, field_type, do_access_check, transaction_active>(
+    return DoFieldPut<find_type, field_type, do_access_check, transaction_active>(
         self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-    return true;
   }
 
   template<Primitive::Type field_type>
   ALWAYS_INLINE WARN_UNUSED bool HandlePutQuick() REQUIRES_SHARED(Locks::mutator_lock_) {
-    bool success = DoIPutQuick<field_type, transaction_active>(
+    return DoIPutQuick<field_type, transaction_active>(
         shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-    return true;
   }
 
   template<InvokeType type, bool is_range, bool is_quick = false>
   ALWAYS_INLINE WARN_UNUSED bool HandleInvoke() REQUIRES_SHARED(Locks::mutator_lock_) {
     bool success = DoInvoke<type, is_range, do_access_check, /*is_mterp=*/ false, is_quick>(
         self, shadow_frame, inst, inst_data, ResultRegister());
-    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
-    return true;
+    return PossiblyHandlePendingExceptionOnInvoke(!success);
   }
 
   ALWAYS_INLINE WARN_UNUSED bool HandleUnused() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -588,7 +499,7 @@
       obj_result = GetVRegReference(ref_idx);
       if (return_type == nullptr) {
         // Return the pending exception.
-        HANDLE_PENDING_EXCEPTION();
+        return false;  // Pending exception.
       }
       if (!obj_result->VerifierInstanceOf(return_type)) {
         // This should never happen.
@@ -597,7 +508,7 @@
                                  "Returning '%s' that is not instance of return type '%s'",
                                  obj_result->GetClass()->GetDescriptor(&temp1),
                                  return_type->GetDescriptor(&temp2));
-        HANDLE_PENDING_EXCEPTION();
+        return false;  // Pending exception.
       }
     }
     StackHandleScope<1> hs(self);
@@ -611,19 +522,16 @@
                                        shadow_frame.GetMethod(),
                                        inst->GetDexPc(Insns()),
                                        h_result))) {
-      if (!HandlePendingExceptionWithInstrumentation(nullptr)) {
-        return false;
-      }
+      DCHECK(self->IsExceptionPending());
+      // Do not raise exception event if it is caused by other instrumentation event.
+      shadow_frame.SetSkipNextExceptionEvent(true);
+      return false;  // Pending exception.
     }
     // Re-load since it might have moved or been replaced during the MethodExitEvent.
     result.SetL(h_result.Get());
-    if (ctx->interpret_one_instruction) {
-      /* Signal mterp to return to caller */
-      shadow_frame.SetDexPC(dex::kDexNoIndex);
-    }
     ctx->result = result;
     exit_interpreter_loop = true;
-    return true;
+    return false;
   }
 
   ALWAYS_INLINE bool CONST_4() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -689,7 +597,7 @@
   ALWAYS_INLINE bool CONST_STRING() REQUIRES_SHARED(Locks::mutator_lock_) {
     ObjPtr<mirror::String> s = ResolveString(self, shadow_frame, dex::StringIndex(B()));
     if (UNLIKELY(s == nullptr)) {
-      HANDLE_PENDING_EXCEPTION();
+      return false;  // Pending exception.
     } else {
       SetVRegReference(A(), s);
     }
@@ -699,7 +607,7 @@
   ALWAYS_INLINE bool CONST_STRING_JUMBO() REQUIRES_SHARED(Locks::mutator_lock_) {
     ObjPtr<mirror::String> s = ResolveString(self, shadow_frame, dex::StringIndex(B()));
     if (UNLIKELY(s == nullptr)) {
-      HANDLE_PENDING_EXCEPTION();
+      return false;  // Pending exception.
     } else {
       SetVRegReference(A(), s);
     }
@@ -713,7 +621,7 @@
                                                      false,
                                                      do_access_check);
     if (UNLIKELY(c == nullptr)) {
-      HANDLE_PENDING_EXCEPTION();
+      return false;  // Pending exception.
     } else {
       SetVRegReference(A(), c);
     }
@@ -726,7 +634,7 @@
                                                               B(),
                                                               shadow_frame.GetMethod());
     if (UNLIKELY(mh == nullptr)) {
-      HANDLE_PENDING_EXCEPTION();
+      return false;  // Pending exception.
     } else {
       SetVRegReference(A(), mh);
     }
@@ -739,7 +647,7 @@
                                                           dex::ProtoIndex(B()),
                                                           shadow_frame.GetMethod());
     if (UNLIKELY(mt == nullptr)) {
-      HANDLE_PENDING_EXCEPTION();
+      return false;  // Pending exception.
     } else {
       SetVRegReference(A(), mt);
     }
@@ -753,12 +661,11 @@
     ObjPtr<mirror::Object> obj = GetVRegReference(A());
     if (UNLIKELY(obj == nullptr)) {
       ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
+      return false;  // Pending exception.
     } else {
       DoMonitorEnter<do_assignability_check>(self, &shadow_frame, obj);
-      POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
+      return !self->IsExceptionPending();
     }
-    return true;
   }
 
   ALWAYS_INLINE bool MONITOR_EXIT() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -768,12 +675,11 @@
     ObjPtr<mirror::Object> obj = GetVRegReference(A());
     if (UNLIKELY(obj == nullptr)) {
       ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
+      return false;  // Pending exception.
     } else {
       DoMonitorExit<do_assignability_check>(self, &shadow_frame, obj);
-      POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
+      return !self->IsExceptionPending();
     }
-    return true;
   }
 
   ALWAYS_INLINE bool CHECK_CAST() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -783,12 +689,12 @@
                                                      false,
                                                      do_access_check);
     if (UNLIKELY(c == nullptr)) {
-      HANDLE_PENDING_EXCEPTION();
+      return false;  // Pending exception.
     } else {
       ObjPtr<mirror::Object> obj = GetVRegReference(A());
       if (UNLIKELY(obj != nullptr && !obj->InstanceOf(c))) {
         ThrowClassCastException(c, obj->GetClass());
-        HANDLE_PENDING_EXCEPTION();
+        return false;  // Pending exception.
       }
     }
     return true;
@@ -801,7 +707,7 @@
                                                      false,
                                                      do_access_check);
     if (UNLIKELY(c == nullptr)) {
-      HANDLE_PENDING_EXCEPTION();
+      return false;  // Pending exception.
     } else {
       ObjPtr<mirror::Object> obj = GetVRegReference(B());
       SetVReg(A(), (obj != nullptr && obj->InstanceOf(c)) ? 1 : 0);
@@ -813,7 +719,7 @@
     ObjPtr<mirror::Object> array = GetVRegReference(B());
     if (UNLIKELY(array == nullptr)) {
       ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
+      return false;  // Pending exception.
     } else {
       SetVReg(A(), array->AsArray()->GetLength());
     }
@@ -834,7 +740,7 @@
         AbortTransactionF(self,
                           "Allocating finalizable object in transaction: %s",
                           c->PrettyDescriptor().c_str());
-        HANDLE_PENDING_EXCEPTION();
+        return false;  // Pending exception.
       }
       gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
       if (UNLIKELY(c->IsStringClass())) {
@@ -844,7 +750,7 @@
       }
     }
     if (UNLIKELY(obj == nullptr)) {
-      HANDLE_PENDING_EXCEPTION();
+      return false;  // Pending exception.
     } else {
       obj->GetClass()->AssertInitializedOrInitializingInThread(self);
       SetVRegReference(A(), obj);
@@ -861,7 +767,7 @@
         self,
         Runtime::Current()->GetHeap()->GetCurrentAllocator());
     if (UNLIKELY(obj == nullptr)) {
-      HANDLE_PENDING_EXCEPTION();
+      return false;  // Pending exception.
     } else {
       SetVRegReference(A(), obj);
     }
@@ -869,19 +775,13 @@
   }
 
   ALWAYS_INLINE bool FILLED_NEW_ARRAY() REQUIRES_SHARED(Locks::mutator_lock_) {
-    bool success =
-        DoFilledNewArray<false, do_access_check, transaction_active>(inst, shadow_frame, self,
-                                                                     ResultRegister());
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
-    return true;
+    return DoFilledNewArray<false, do_access_check, transaction_active>(
+        inst, shadow_frame, self, ResultRegister());
   }
 
   ALWAYS_INLINE bool FILLED_NEW_ARRAY_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) {
-    bool success =
-        DoFilledNewArray<true, do_access_check, transaction_active>(inst, shadow_frame,
-                                                                    self, ResultRegister());
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
-    return true;
+    return DoFilledNewArray<true, do_access_check, transaction_active>(
+        inst, shadow_frame, self, ResultRegister());
   }
 
   ALWAYS_INLINE bool FILL_ARRAY_DATA() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -889,9 +789,8 @@
     const Instruction::ArrayDataPayload* payload =
         reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr);
     ObjPtr<mirror::Object> obj = GetVRegReference(A());
-    bool success = FillArrayData(obj, payload);
-    if (!success) {
-      HANDLE_PENDING_EXCEPTION();
+    if (!FillArrayData(obj, payload)) {
+      return false;  // Pending exception.
     }
     if (transaction_active) {
       RecordArrayElementsInTransaction(obj->AsArray(), payload->element_count);
@@ -915,8 +814,7 @@
     } else {
       self->SetException(exception->AsThrowable());
     }
-    HANDLE_PENDING_EXCEPTION();
-    return true;
+    return false;  // Pending exception.
   }
 
   ALWAYS_INLINE bool GOTO() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -933,7 +831,9 @@
 
   ALWAYS_INLINE bool PACKED_SWITCH() REQUIRES_SHARED(Locks::mutator_lock_) {
     int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data);
-    BRANCH_INSTRUMENTATION(offset);
+    if (!BranchInstrumentation(offset)) {
+      return false;
+    }
     SetNextInstruction(inst->RelativeAt(offset));
     HandleBackwardBranch(offset);
     return true;
@@ -941,7 +841,9 @@
 
   ALWAYS_INLINE bool SPARSE_SWITCH() REQUIRES_SHARED(Locks::mutator_lock_) {
     int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data);
-    BRANCH_INSTRUMENTATION(offset);
+    if (!BranchInstrumentation(offset)) {
+      return false;
+    }
     SetNextInstruction(inst->RelativeAt(offset));
     HandleBackwardBranch(offset);
     return true;
@@ -1071,7 +973,7 @@
     ObjPtr<mirror::Object> a = GetVRegReference(B());
     if (UNLIKELY(a == nullptr)) {
       ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
+      return false;  // Pending exception.
     }
     int32_t index = GetVReg(C());
     ObjPtr<mirror::Object> val = GetVRegReference(A());
@@ -1079,7 +981,7 @@
     if (array->CheckIsValidIndex(index) && array->CheckAssignable(val)) {
       array->SetWithoutChecks<transaction_active>(index, val);
     } else {
-      HANDLE_PENDING_EXCEPTION();
+      return false;  // Pending exception.
     }
     return true;
   }
@@ -1304,32 +1206,28 @@
     DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
     bool success = DoInvokePolymorphic</* is_range= */ false>(
         self, shadow_frame, inst, inst_data, ResultRegister());
-    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(!success);
-    return true;
+    return PossiblyHandlePendingExceptionOnInvoke(!success);
   }
 
   ALWAYS_INLINE bool INVOKE_POLYMORPHIC_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) {
     DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
     bool success = DoInvokePolymorphic</* is_range= */ true>(
         self, shadow_frame, inst, inst_data, ResultRegister());
-    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(!success);
-    return true;
+    return PossiblyHandlePendingExceptionOnInvoke(!success);
   }
 
   ALWAYS_INLINE bool INVOKE_CUSTOM() REQUIRES_SHARED(Locks::mutator_lock_) {
     DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
     bool success = DoInvokeCustom</* is_range= */ false>(
         self, shadow_frame, inst, inst_data, ResultRegister());
-    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
-    return true;
+    return PossiblyHandlePendingExceptionOnInvoke(!success);
   }
 
   ALWAYS_INLINE bool INVOKE_CUSTOM_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) {
     DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
     bool success = DoInvokeCustom</* is_range= */ true>(
         self, shadow_frame, inst, inst_data, ResultRegister());
-    POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
-    return true;
+    return PossiblyHandlePendingExceptionOnInvoke(!success);
   }
 
   ALWAYS_INLINE bool NEG_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1461,15 +1359,11 @@
   }
 
   ALWAYS_INLINE bool DIV_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
-    bool success = DoIntDivide(shadow_frame, A(), GetVReg(B()), GetVReg(C()));
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-    return true;
+    return DoIntDivide(shadow_frame, A(), GetVReg(B()), GetVReg(C()));
   }
 
   ALWAYS_INLINE bool REM_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
-    bool success = DoIntRemainder(shadow_frame, A(), GetVReg(B()), GetVReg(C()));
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-    return true;
+    return DoIntRemainder(shadow_frame, A(), GetVReg(B()), GetVReg(C()));
   }
 
   ALWAYS_INLINE bool SHL_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1518,15 +1412,11 @@
   }
 
   ALWAYS_INLINE bool DIV_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
-    DoLongDivide(shadow_frame, A(), GetVRegLong(B()), GetVRegLong(C()));
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx);
-    return true;
+    return DoLongDivide(shadow_frame, A(), GetVRegLong(B()), GetVRegLong(C()));
   }
 
   ALWAYS_INLINE bool REM_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
-    DoLongRemainder(shadow_frame, A(), GetVRegLong(B()), GetVRegLong(C()));
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx);
-    return true;
+    return DoLongRemainder(shadow_frame, A(), GetVRegLong(B()), GetVRegLong(C()));
   }
 
   ALWAYS_INLINE bool AND_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1629,16 +1519,12 @@
 
   ALWAYS_INLINE bool DIV_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
     uint4_t vregA = A();
-    bool success = DoIntDivide(shadow_frame, vregA, GetVReg(vregA), GetVReg(B()));
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx);
-    return true;
+    return DoIntDivide(shadow_frame, vregA, GetVReg(vregA), GetVReg(B()));
   }
 
   ALWAYS_INLINE bool REM_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
     uint4_t vregA = A();
-    bool success = DoIntRemainder(shadow_frame, vregA, GetVReg(vregA), GetVReg(B()));
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx);
-    return true;
+    return DoIntRemainder(shadow_frame, vregA, GetVReg(vregA), GetVReg(B()));
   }
 
   ALWAYS_INLINE bool SHL_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1697,16 +1583,12 @@
 
   ALWAYS_INLINE bool DIV_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
     uint4_t vregA = A();
-    DoLongDivide(shadow_frame, vregA, GetVRegLong(vregA), GetVRegLong(B()));
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
-    return true;
+    return DoLongDivide(shadow_frame, vregA, GetVRegLong(vregA), GetVRegLong(B()));
   }
 
   ALWAYS_INLINE bool REM_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
     uint4_t vregA = A();
-    DoLongRemainder(shadow_frame, vregA, GetVRegLong(vregA), GetVRegLong(B()));
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
-    return true;
+    return DoLongRemainder(shadow_frame, vregA, GetVRegLong(vregA), GetVRegLong(B()));
   }
 
   ALWAYS_INLINE bool AND_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1821,15 +1703,11 @@
   }
 
   ALWAYS_INLINE bool DIV_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) {
-    bool success = DoIntDivide(shadow_frame, A(), GetVReg(B()), C());
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-    return true;
+    return DoIntDivide(shadow_frame, A(), GetVReg(B()), C());
   }
 
   ALWAYS_INLINE bool REM_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) {
-    bool success = DoIntRemainder(shadow_frame, A(), GetVReg(B()), C());
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-    return true;
+    return DoIntRemainder(shadow_frame, A(), GetVReg(B()), C());
   }
 
   ALWAYS_INLINE bool AND_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1863,15 +1741,11 @@
   }
 
   ALWAYS_INLINE bool DIV_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
-    bool success = DoIntDivide(shadow_frame, A(), GetVReg(B()), C());
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-    return true;
+    return DoIntDivide(shadow_frame, A(), GetVReg(B()), C());
   }
 
   ALWAYS_INLINE bool REM_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
-    bool success = DoIntRemainder(shadow_frame, A(), GetVReg(B()), C());
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
-    return true;
+    return DoIntRemainder(shadow_frame, A(), GetVReg(B()), C());
   }
 
   ALWAYS_INLINE bool AND_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -2032,12 +1906,6 @@
   bool& exit_interpreter_loop;
 };
 
-#undef BRANCH_INSTRUMENTATION
-#undef POSSIBLY_HANDLE_PENDING_EXCEPTION
-#undef POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE
-#undef POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC
-#undef HANDLE_PENDING_EXCEPTION
-
 // TODO On ASAN builds this function gets a huge stack frame. Since normally we run in the mterp
 // this shouldn't cause any problems for stack overflow detection. Remove this once b/117341496 is
 // fixed.
@@ -2057,7 +1925,6 @@
   const auto* const instrumentation = Runtime::Current()->GetInstrumentation();
   const uint16_t* const insns = accessor.Insns();
   const Instruction* next = Instruction::At(insns + dex_pc);
-  uint16_t inst_data;
 
   DCHECK(!shadow_frame.GetForceRetryInstruction())
       << "Entered interpreter from invoke without retry instruction being handled!";
@@ -2068,45 +1935,53 @@
     dex_pc = inst->GetDexPc(insns);
     shadow_frame.SetDexPC(dex_pc);
     TraceExecution(shadow_frame, inst, dex_pc);
-    inst_data = inst->Fetch16(0);
-    {
-      bool exit_loop = false;
-      InstructionHandler<do_access_check, transaction_active, Instruction::kInvalidFormat> handler(
-          ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit_loop);
-      if (!handler.Preamble()) {
-        if (UNLIKELY(exit_loop)) {
-          return;
-        }
-        if (UNLIKELY(interpret_one_instruction)) {
-          break;
-        }
-        continue;
-      }
-    }
-    switch (inst->Opcode(inst_data)) {
+    uint16_t inst_data = inst->Fetch16(0);
+    bool exit = false;
+    if (InstructionHandler<do_access_check, transaction_active, Instruction::kInvalidFormat>(
+            ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit).
+            Preamble()) {
+      switch (inst->Opcode(inst_data)) {
 #define OPCODE_CASE(OPCODE, OPCODE_NAME, NAME, FORMAT, i, a, e, v)                                \
-      case OPCODE: {                                                                              \
-        next = inst->RelativeAt(Instruction::SizeInCodeUnits(Instruction::FORMAT));               \
-        bool exit_loop = false;                                                                   \
-        InstructionHandler<do_access_check, transaction_active, Instruction::FORMAT> handler(     \
-            ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit_loop);  \
-        handler.OPCODE_NAME();                                                                    \
-        if (UNLIKELY(exit_loop)) {                                                                \
-          return;                                                                                 \
-        }                                                                                         \
-        break;                                                                                    \
-      }
-DEX_INSTRUCTION_LIST(OPCODE_CASE)
+        case OPCODE: {                                                                            \
+          DCHECK_EQ(self->IsExceptionPending(), (OPCODE == Instruction::MOVE_EXCEPTION));         \
+          next = inst->RelativeAt(Instruction::SizeInCodeUnits(Instruction::FORMAT));             \
+          InstructionHandler<do_access_check, transaction_active, Instruction::FORMAT> handler(   \
+              ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit);     \
+          if (handler.OPCODE_NAME() && LIKELY(!interpret_one_instruction)) {                      \
+            DCHECK(!exit) << NAME;                                                                \
+            continue;                                                                             \
+          }                                                                                       \
+          if (exit) {                                                                             \
+            shadow_frame.SetDexPC(dex::kDexNoIndex);                                              \
+            return;                                                                               \
+          }                                                                                       \
+          break;                                                                                  \
+        }
+  DEX_INSTRUCTION_LIST(OPCODE_CASE)
 #undef OPCODE_CASE
+      }
+    } else {
+      // Preamble returned false due to debugger event.
+      if (exit) {
+        shadow_frame.SetDexPC(dex::kDexNoIndex);
+        return;  // Return statement or debugger forced exit.
+      }
     }
-    if (UNLIKELY(interpret_one_instruction)) {
-      break;
+    if (self->IsExceptionPending()) {
+      if (!InstructionHandler<do_access_check, transaction_active, Instruction::kInvalidFormat>(
+              ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit).
+              HandlePendingException()) {
+        shadow_frame.SetDexPC(dex::kDexNoIndex);
+        return;  // Locally unhandled exception - return to caller.
+      }
+      // Continue execution in the catch block.
+    }
+    if (interpret_one_instruction) {
+      shadow_frame.SetDexPC(next->GetDexPc(insns));  // Record where we stopped.
+      ctx->result = ctx->result_register;
+      return;
     }
   }
-  // Record where we stopped.
-  shadow_frame.SetDexPC(next->GetDexPc(insns));
-  ctx->result = ctx->result_register;
-  return;
 }  // NOLINT(readability/fn_size)
 
 }  // namespace interpreter