JNI compiler: Rewrite exception polling.

Make the slow path explicit in the JNI compiler. Fix the
CFI data for the exceptional path of synchronized methods.

Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Test: run-gtests.sh
Test: testrunner.py --target --optimizing
Bug: 172332525
Change-Id: If64965eef15c063e36b78dd8bb6cba5af34ab4fa
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 25eb919..5752c75 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -288,6 +288,8 @@
   // 5. Call into appropriate JniMethodStart passing Thread* so that transition out of Runnable
   //    can occur. We abuse the JNI calling convention here, that is guaranteed to support passing
   //    two pointer arguments.
+  std::unique_ptr<JNIMacroLabel> monitor_enter_exception_slow_path =
+      UNLIKELY(is_synchronized) ? __ CreateLabel() : nullptr;
   if (LIKELY(!is_critical_native)) {
     // Skip this for @CriticalNative methods. They do not call JniMethodStart.
     ThreadOffset<kPointerSize> jni_start =
@@ -328,7 +330,7 @@
     }
     method_register = ManagedRegister::NoRegister();  // Method register is clobbered.
     if (is_synchronized) {  // Check for exceptions from monitor enter.
-      __ ExceptionPoll(main_out_arg_size);
+      __ ExceptionPoll(monitor_enter_exception_slow_path.get());
     }
   }
 
@@ -602,8 +604,10 @@
 
   // 17. Process pending exceptions from JNI call or monitor exit.
   //     @CriticalNative methods do not need exception poll in the stub.
+  std::unique_ptr<JNIMacroLabel> exception_slow_path =
+      LIKELY(!is_critical_native) ? __ CreateLabel() : nullptr;
   if (LIKELY(!is_critical_native)) {
-    __ ExceptionPoll(/* stack_adjust= */ 0);
+    __ ExceptionPoll(exception_slow_path.get());
   }
 
   // 18. Remove activation - need to restore callee save registers since the GC may have changed
@@ -673,7 +677,21 @@
     }
   }
 
-  // 20. Finalize code generation
+  // 20. Emit exception poll slow paths.
+  if (LIKELY(!is_critical_native)) {
+    if (UNLIKELY(is_synchronized)) {
+      __ Bind(monitor_enter_exception_slow_path.get());
+      if (main_out_arg_size != 0) {
+        jni_asm->cfi().AdjustCFAOffset(main_out_arg_size);
+        __ DecreaseFrameSize(main_out_arg_size);
+      }
+    }
+    DCHECK_EQ(jni_asm->cfi().GetCurrentCFAOffset(), static_cast<int>(current_frame_size));
+    __ Bind(exception_slow_path.get());
+    __ DeliverPendingException();
+  }
+
+  // 21. Finalize code generation
   __ FinalizeCode();
   size_t cs = __ CodeSize();
   std::vector<uint8_t> managed_code(cs);
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index c23d682..c59262d 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -68,10 +68,6 @@
 }
 
 void ArmVIXLJNIMacroAssembler::FinalizeCode() {
-  for (const std::unique_ptr<
-      ArmVIXLJNIMacroAssembler::ArmException>& exception : exception_blocks_) {
-    EmitExceptionPoll(exception.get());
-  }
   asm_.FinalizeCode();
 }
 
@@ -986,23 +982,33 @@
   asm_.StoreToOffset(kStoreWord, tr, sp, dest_offset.Int32Value());
 }
 
-void ArmVIXLJNIMacroAssembler::ExceptionPoll(size_t stack_adjust) {
-  CHECK_ALIGNED(stack_adjust, kAapcsStackAlignment);
+void ArmVIXLJNIMacroAssembler::ExceptionPoll(JNIMacroLabel* label) {
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
   vixl32::Register scratch = temps.Acquire();
-  exception_blocks_.emplace_back(
-      new ArmVIXLJNIMacroAssembler::ArmException(scratch, stack_adjust));
   asm_.LoadFromOffset(kLoadWord,
                       scratch,
                       tr,
                       Thread::ExceptionOffset<kArmPointerSize>().Int32Value());
 
   ___ Cmp(scratch, 0);
-  vixl32::Label* label = exception_blocks_.back()->Entry();
-  ___ BPreferNear(ne, label);
+  ___ BPreferNear(ne, ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
   // TODO: think about using CBNZ here.
 }
 
+void ArmVIXLJNIMacroAssembler::DeliverPendingException() {
+  // Pass exception object as argument.
+  // Don't care about preserving r0 as this won't return.
+  // Note: The scratch register from `ExceptionPoll()` may have been clobbered.
+  asm_.LoadFromOffset(kLoadWord,
+                      r0,
+                      tr,
+                      Thread::ExceptionOffset<kArmPointerSize>().Int32Value());
+  ___ Ldr(lr,
+          MemOperand(tr,
+              QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, pDeliverException).Int32Value()));
+  ___ Blx(lr);
+}
+
 std::unique_ptr<JNIMacroLabel> ArmVIXLJNIMacroAssembler::CreateLabel() {
   return std::unique_ptr<JNIMacroLabel>(new ArmVIXLJNIMacroLabel());
 }
@@ -1048,25 +1054,6 @@
   ___ Bind(ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
 }
 
-void ArmVIXLJNIMacroAssembler::EmitExceptionPoll(
-    ArmVIXLJNIMacroAssembler::ArmException* exception) {
-  ___ Bind(exception->Entry());
-  if (exception->stack_adjust_ != 0) {  // Fix up the frame.
-    DecreaseFrameSize(exception->stack_adjust_);
-  }
-
-  vixl32::Register scratch = exception->scratch_;
-  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(scratch);
-  // Pass exception object as argument.
-  // Don't care about preserving r0 as this won't return.
-  ___ Mov(r0, scratch);
-  ___ Ldr(lr,
-          MemOperand(tr,
-              QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, pDeliverException).Int32Value()));
-  ___ Blx(lr);
-}
-
 void ArmVIXLJNIMacroAssembler::MemoryBarrier(ManagedRegister scratch ATTRIBUTE_UNUSED) {
   UNIMPLEMENTED(FATAL);
 }
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.h b/compiler/utils/arm/jni_macro_assembler_arm_vixl.h
index d98f688..89805ce 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.h
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.h
@@ -34,12 +34,9 @@
 
 class ArmVIXLJNIMacroAssembler final
     : public JNIMacroAssemblerFwd<ArmVIXLAssembler, PointerSize::k32> {
- private:
-  class ArmException;
  public:
   explicit ArmVIXLJNIMacroAssembler(ArenaAllocator* allocator)
-      : JNIMacroAssemblerFwd(allocator),
-        exception_blocks_(allocator->Adapter(kArenaAllocAssembler)) {}
+      : JNIMacroAssemblerFwd(allocator) {}
 
   virtual ~ArmVIXLJNIMacroAssembler() {}
   void FinalizeCode() override;
@@ -186,8 +183,10 @@
   void CallFromThread(ThreadOffset32 offset) override;
 
   // Generate code to check if Thread::Current()->exception_ is non-null
-  // and branch to a ExceptionSlowPath if it is.
-  void ExceptionPoll(size_t stack_adjust) override;
+  // and branch to the `label` if it is.
+  void ExceptionPoll(JNIMacroLabel* label) override;
+  // Deliver pending exception.
+  void DeliverPendingException() override;
 
   // Create a new label that can be used with Jump/Bind calls.
   std::unique_ptr<JNIMacroLabel> CreateLabel() override;
@@ -200,31 +199,9 @@
 
   void MemoryBarrier(ManagedRegister scratch) override;
 
-  void EmitExceptionPoll(ArmVIXLJNIMacroAssembler::ArmException *exception);
   void Load(ArmManagedRegister dest, vixl32::Register base, int32_t offset, size_t size);
 
  private:
-  class ArmException {
-   private:
-    ArmException(vixl32::Register scratch, size_t stack_adjust)
-        : scratch_(scratch), stack_adjust_(stack_adjust) {}
-
-    vixl32::Label* Entry() { return &exception_entry_; }
-
-    // Register used for passing Thread::Current()->exception_ .
-    const vixl32::Register scratch_;
-
-    // Stack adjust for ExceptionPool.
-    const size_t stack_adjust_;
-
-    vixl32::Label exception_entry_;
-
-    friend class ArmVIXLJNIMacroAssembler;
-    DISALLOW_COPY_AND_ASSIGN(ArmException);
-  };
-
-  // List of exception blocks to generate at the end of the code cache.
-  ArenaVector<std::unique_ptr<ArmVIXLJNIMacroAssembler::ArmException>> exception_blocks_;
   // Used for testing.
   friend class ArmVIXLAssemblerTest_VixlLoadFromOffset_Test;
   friend class ArmVIXLAssemblerTest_VixlStoreToOffset_Test;
diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.cc b/compiler/utils/arm64/jni_macro_assembler_arm64.cc
index 33fff55..bb16841 100644
--- a/compiler/utils/arm64/jni_macro_assembler_arm64.cc
+++ b/compiler/utils/arm64/jni_macro_assembler_arm64.cc
@@ -45,9 +45,6 @@
 }
 
 void Arm64JNIMacroAssembler::FinalizeCode() {
-  for (const std::unique_ptr<Arm64Exception>& exception : exception_blocks_) {
-    EmitExceptionPoll(exception.get());
-  }
   ___ FinalizeCode();
 }
 
@@ -737,13 +734,24 @@
   ___ Str(scratch, MEM_OP(reg_x(SP), out_off.Int32Value()));
 }
 
-void Arm64JNIMacroAssembler::ExceptionPoll(size_t stack_adjust) {
-  CHECK_ALIGNED(stack_adjust, kStackAlignment);
+void Arm64JNIMacroAssembler::ExceptionPoll(JNIMacroLabel* label) {
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
   Register scratch = temps.AcquireX();
-  exception_blocks_.emplace_back(new Arm64Exception(scratch, stack_adjust));
   ___ Ldr(scratch, MEM_OP(reg_x(TR), Thread::ExceptionOffset<kArm64PointerSize>().Int32Value()));
-  ___ Cbnz(scratch, exception_blocks_.back()->Entry());
+  ___ Cbnz(scratch, Arm64JNIMacroLabel::Cast(label)->AsArm64());
+}
+
+void Arm64JNIMacroAssembler::DeliverPendingException() {
+  // Pass exception object as argument.
+  // Don't care about preserving X0 as this won't return.
+  // Note: The scratch register from `ExceptionPoll()` may have been clobbered.
+  ___ Ldr(reg_x(X0), MEM_OP(reg_x(TR), Thread::ExceptionOffset<kArm64PointerSize>().Int32Value()));
+  ___ Ldr(lr,
+          MEM_OP(reg_x(TR),
+                 QUICK_ENTRYPOINT_OFFSET(kArm64PointerSize, pDeliverException).Int32Value()));
+  ___ Blr(lr);
+  // Call should never return.
+  ___ Brk();
 }
 
 std::unique_ptr<JNIMacroLabel> Arm64JNIMacroAssembler::CreateLabel() {
@@ -792,27 +800,6 @@
   ___ Bind(Arm64JNIMacroLabel::Cast(label)->AsArm64());
 }
 
-void Arm64JNIMacroAssembler::EmitExceptionPoll(Arm64Exception* exception) {
-  UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
-  temps.Exclude(exception->scratch_);
-
-  // Bind exception poll entry.
-  ___ Bind(exception->Entry());
-  if (exception->stack_adjust_ != 0) {  // Fix up the frame.
-    DecreaseFrameSize(exception->stack_adjust_);
-  }
-  // Pass exception object as argument.
-  // Don't care about preserving X0 as this won't return.
-  ___ Mov(reg_x(X0), exception->scratch_);
-  ___ Ldr(lr,
-          MEM_OP(reg_x(TR),
-                 QUICK_ENTRYPOINT_OFFSET(kArm64PointerSize, pDeliverException).Int32Value()));
-
-  ___ Blr(lr);
-  // Call should never return.
-  ___ Brk();
-}
-
 void Arm64JNIMacroAssembler::BuildFrame(size_t frame_size,
                                         ManagedRegister method_reg,
                                         ArrayRef<const ManagedRegister> callee_save_regs) {
diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.h b/compiler/utils/arm64/jni_macro_assembler_arm64.h
index 2c4b252..363bce9 100644
--- a/compiler/utils/arm64/jni_macro_assembler_arm64.h
+++ b/compiler/utils/arm64/jni_macro_assembler_arm64.h
@@ -43,8 +43,7 @@
 class Arm64JNIMacroAssembler final : public JNIMacroAssemblerFwd<Arm64Assembler, PointerSize::k64> {
  public:
   explicit Arm64JNIMacroAssembler(ArenaAllocator* allocator)
-      : JNIMacroAssemblerFwd(allocator),
-        exception_blocks_(allocator->Adapter(kArenaAllocAssembler)) {}
+      : JNIMacroAssemblerFwd(allocator) {}
 
   ~Arm64JNIMacroAssembler();
 
@@ -168,8 +167,10 @@
   void CallFromThread(ThreadOffset64 offset) override;
 
   // Generate code to check if Thread::Current()->exception_ is non-null
-  // and branch to a ExceptionSlowPath if it is.
-  void ExceptionPoll(size_t stack_adjust) override;
+  // and branch to the `label` if it is.
+  void ExceptionPoll(JNIMacroLabel* label) override;
+  // Deliver pending exception.
+  void DeliverPendingException() override;
 
   // Create a new label that can be used with Jump/Bind calls.
   std::unique_ptr<JNIMacroLabel> CreateLabel() override;
@@ -181,28 +182,6 @@
   void Bind(JNIMacroLabel* label) override;
 
  private:
-  class Arm64Exception {
-   public:
-    Arm64Exception(vixl::aarch64::Register scratch, size_t stack_adjust)
-        : scratch_(scratch), stack_adjust_(stack_adjust) {}
-
-    vixl::aarch64::Label* Entry() { return &exception_entry_; }
-
-    // Register used for passing Thread::Current()->exception_ .
-    const vixl::aarch64::Register scratch_;
-
-    // Stack adjust for ExceptionPool.
-    const size_t stack_adjust_;
-
-    vixl::aarch64::Label exception_entry_;
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(Arm64Exception);
-  };
-
-  // Emits Exception block.
-  void EmitExceptionPoll(Arm64Exception *exception);
-
   void StoreWToOffset(StoreOperandType type,
                       WRegister source,
                       XRegister base,
@@ -229,9 +208,6 @@
                    XRegister rn,
                    int32_t value,
                    vixl::aarch64::Condition cond = vixl::aarch64::al);
-
-  // List of exception blocks to generate at the end of the code cache.
-  ArenaVector<std::unique_ptr<Arm64Exception>> exception_blocks_;
 };
 
 class Arm64JNIMacroLabel final
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 96f4a6c..83b7eeb 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -177,7 +177,8 @@
   __ CreateJObject(method_register, FrameOffset(1025), scratch_register, true);
   __ CreateJObject(scratch_register, FrameOffset(1025), scratch_register, true);
 
-  __ ExceptionPoll(0);
+  std::unique_ptr<JNIMacroLabel> exception_slow_path = __ CreateLabel();
+  __ ExceptionPoll(exception_slow_path.get());
 
   // Push the target out of range of branch emitted by ExceptionPoll.
   for (int i = 0; i < 64; i++) {
@@ -188,6 +189,9 @@
   __ DecreaseFrameSize(32);
   __ RemoveFrame(frame_size, callee_save_regs, /* may_suspend= */ true);
 
+  __ Bind(exception_slow_path.get());
+  __ DeliverPendingException();
+
   EmitAndCheck("VixlJniHelpers", VixlJniHelpersResults);
 }
 
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 78fd90c..fbbcbde 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -152,9 +152,9 @@
   "     218: bd e8 e0 4d   pop.w {r5, r6, r7, r8, r10, r11, lr}\n"
   "     21c: d9 f8 24 80   ldr.w r8, [r9, #36]\n"
   "     220: 70 47         bx lr\n"
-  "     222: 60 46         mov r0, r12\n"
-  "     224: d9 f8 d0 e2   ldr.w lr, [r9, #720]\n"
-  "     228: f0 47         blx lr\n"
+  "     222: d9 f8 8c 00   ldr.w r0, [r9, #140]\n"
+  "     226: d9 f8 d0 e2   ldr.w lr, [r9, #720]\n"
+  "     22a: f0 47         blx lr\n"
 };
 
 const char* const VixlLoadFromOffsetResults = {
diff --git a/compiler/utils/jni_macro_assembler.h b/compiler/utils/jni_macro_assembler.h
index a9d9f54..5da70c1 100644
--- a/compiler/utils/jni_macro_assembler.h
+++ b/compiler/utils/jni_macro_assembler.h
@@ -243,8 +243,10 @@
   virtual void CallFromThread(ThreadOffset<kPointerSize> offset) = 0;
 
   // Generate code to check if Thread::Current()->exception_ is non-null
-  // and branch to a ExceptionSlowPath if it is.
-  virtual void ExceptionPoll(size_t stack_adjust) = 0;
+  // and branch to the `label` if it is.
+  virtual void ExceptionPoll(JNIMacroLabel* label) = 0;
+  // Deliver pending exception.
+  virtual void DeliverPendingException() = 0;
 
   // Create a new label that can be used with Jump/Bind calls.
   virtual std::unique_ptr<JNIMacroLabel> CreateLabel() = 0;
diff --git a/compiler/utils/x86/jni_macro_assembler_x86.cc b/compiler/utils/x86/jni_macro_assembler_x86.cc
index 3c88447..b08503e 100644
--- a/compiler/utils/x86/jni_macro_assembler_x86.cc
+++ b/compiler/utils/x86/jni_macro_assembler_x86.cc
@@ -30,15 +30,6 @@
   return ECX;
 }
 
-// Slowpath entered when Thread::Current()->_exception is non-null
-class X86ExceptionSlowPath final : public SlowPath {
- public:
-  explicit X86ExceptionSlowPath(size_t stack_adjust) : stack_adjust_(stack_adjust) {}
-  void Emit(Assembler *sp_asm) override;
- private:
-  const size_t stack_adjust_;
-};
-
 static dwarf::Reg DWARFReg(Register reg) {
   return dwarf::Reg::X86Core(static_cast<int>(reg));
 }
@@ -572,11 +563,17 @@
   __ movl(Address(ESP, offset), scratch);
 }
 
-void X86JNIMacroAssembler::ExceptionPoll(size_t stack_adjust) {
-  X86ExceptionSlowPath* slow = new (__ GetAllocator()) X86ExceptionSlowPath(stack_adjust);
-  __ GetBuffer()->EnqueueSlowPath(slow);
+void X86JNIMacroAssembler::ExceptionPoll(JNIMacroLabel* label) {
   __ fs()->cmpl(Address::Absolute(Thread::ExceptionOffset<kX86PointerSize>()), Immediate(0));
-  __ j(kNotEqual, slow->Entry());
+  __ j(kNotEqual, X86JNIMacroLabel::Cast(label)->AsX86());
+}
+
+void X86JNIMacroAssembler::DeliverPendingException() {
+  // Pass exception as argument in EAX
+  __ fs()->movl(EAX, Address::Absolute(Thread::ExceptionOffset<kX86PointerSize>()));
+  __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86PointerSize, pDeliverException)));
+  // this call should never return
+  __ int3();
 }
 
 std::unique_ptr<JNIMacroLabel> X86JNIMacroAssembler::CreateLabel() {
@@ -618,21 +615,5 @@
 
 #undef __
 
-void X86ExceptionSlowPath::Emit(Assembler *sasm) {
-  X86Assembler* sp_asm = down_cast<X86Assembler*>(sasm);
-#define __ sp_asm->
-  __ Bind(&entry_);
-  // Note: the return value is dead
-  if (stack_adjust_ != 0) {  // Fix up the frame.
-    DecreaseFrameSizeImpl(sp_asm, stack_adjust_);
-  }
-  // Pass exception as argument in EAX
-  __ fs()->movl(EAX, Address::Absolute(Thread::ExceptionOffset<kX86PointerSize>()));
-  __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86PointerSize, pDeliverException)));
-  // this call should never return
-  __ int3();
-#undef __
-}
-
 }  // namespace x86
 }  // namespace art
diff --git a/compiler/utils/x86/jni_macro_assembler_x86.h b/compiler/utils/x86/jni_macro_assembler_x86.h
index 1f9355a..1de4eb1 100644
--- a/compiler/utils/x86/jni_macro_assembler_x86.h
+++ b/compiler/utils/x86/jni_macro_assembler_x86.h
@@ -159,8 +159,10 @@
   void CallFromThread(ThreadOffset32 offset) override;
 
   // Generate code to check if Thread::Current()->exception_ is non-null
-  // and branch to a ExceptionSlowPath if it is.
-  void ExceptionPoll(size_t stack_adjust) override;
+  // and branch to the `label` if it is.
+  void ExceptionPoll(JNIMacroLabel* label) override;
+  // Deliver pending exception.
+  void DeliverPendingException() override;
 
   // Create a new label that can be used with Jump/Bind calls.
   std::unique_ptr<JNIMacroLabel> CreateLabel() override;
diff --git a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
index d9f05df..b145e97 100644
--- a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
+++ b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
@@ -642,21 +642,20 @@
   __ movq(Address(CpuRegister(RSP), offset), scratch);
 }
 
-// Slowpath entered when Thread::Current()->_exception is non-null
-class X86_64ExceptionSlowPath final : public SlowPath {
- public:
-  explicit X86_64ExceptionSlowPath(size_t stack_adjust) : stack_adjust_(stack_adjust) {}
-  void Emit(Assembler *sp_asm) override;
- private:
-  const size_t stack_adjust_;
-};
-
-void X86_64JNIMacroAssembler::ExceptionPoll(size_t stack_adjust) {
-  X86_64ExceptionSlowPath* slow = new (__ GetAllocator()) X86_64ExceptionSlowPath(stack_adjust);
-  __ GetBuffer()->EnqueueSlowPath(slow);
+void X86_64JNIMacroAssembler::ExceptionPoll(JNIMacroLabel* label) {
   __ gs()->cmpl(Address::Absolute(Thread::ExceptionOffset<kX86_64PointerSize>(), true),
                 Immediate(0));
-  __ j(kNotEqual, slow->Entry());
+  __ j(kNotEqual, X86_64JNIMacroLabel::Cast(label)->AsX86_64());
+}
+
+void X86_64JNIMacroAssembler::DeliverPendingException() {
+  // Pass exception as argument in RDI
+  __ gs()->movq(CpuRegister(RDI),
+                Address::Absolute(Thread::ExceptionOffset<kX86_64PointerSize>(), true));
+  __ gs()->call(
+      Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64PointerSize, pDeliverException), true));
+  // this call should never return
+  __ int3();
 }
 
 std::unique_ptr<JNIMacroLabel> X86_64JNIMacroAssembler::CreateLabel() {
@@ -699,23 +698,5 @@
 
 #undef __
 
-void X86_64ExceptionSlowPath::Emit(Assembler *sasm) {
-  X86_64Assembler* sp_asm = down_cast<X86_64Assembler*>(sasm);
-#define __ sp_asm->
-  __ Bind(&entry_);
-  // Note: the return value is dead
-  if (stack_adjust_ != 0) {  // Fix up the frame.
-    DecreaseFrameSizeImpl(stack_adjust_, sp_asm);
-  }
-  // Pass exception as argument in RDI
-  __ gs()->movq(CpuRegister(RDI),
-                Address::Absolute(Thread::ExceptionOffset<kX86_64PointerSize>(), true));
-  __ gs()->call(
-      Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64PointerSize, pDeliverException), true));
-  // this call should never return
-  __ int3();
-#undef __
-}
-
 }  // namespace x86_64
 }  // namespace art
diff --git a/compiler/utils/x86_64/jni_macro_assembler_x86_64.h b/compiler/utils/x86_64/jni_macro_assembler_x86_64.h
index f1ec74f..0468901 100644
--- a/compiler/utils/x86_64/jni_macro_assembler_x86_64.h
+++ b/compiler/utils/x86_64/jni_macro_assembler_x86_64.h
@@ -179,8 +179,10 @@
   void CallFromThread(ThreadOffset64 offset) override;
 
   // Generate code to check if Thread::Current()->exception_ is non-null
-  // and branch to a ExceptionSlowPath if it is.
-  void ExceptionPoll(size_t stack_adjust) override;
+  // and branch to the `label` if it is.
+  void ExceptionPoll(JNIMacroLabel* label) override;
+  // Deliver pending exception.
+  void DeliverPendingException() override;
 
   // Create a new label that can be used with Jump/Bind calls.
   std::unique_ptr<JNIMacroLabel> CreateLabel() override;