changeset 1872:ba5d4644582d

Several bug fixes to compiling methods with exception handlers
author Edward Nevill <ed@camswl.com>
date Tue, 02 Feb 2010 11:08:36 +0000
parents 22be2c99a89b
children cc7232b07731
files ChangeLog ports/hotspot/src/cpu/zero/vm/asm_helper.cpp ports/hotspot/src/cpu/zero/vm/bytecodes_arm.def ports/hotspot/src/cpu/zero/vm/cppInterpreter_arm.S ports/hotspot/src/cpu/zero/vm/thumb2.cpp
diffstat 5 files changed, 242 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Feb 01 12:28:24 2010 +0100
+++ b/ChangeLog	Tue Feb 02 11:08:36 2010 +0000
@@ -1,3 +1,11 @@
+2010-02-02  Edward Nevill <ed@camswl.com>
+
+	* asm_helper.cpp, cppInterpreter_arm.S, thumb2.cpp, bytecodes_arm.def
+	Several bug fixes to compiled exection handling methods.
+	Copyright notices updated to 2010 and added to thumb2.cpp
+	Assertions turned off in PRODUCT build
+	Compilation summary and stats to stderr instead of stdout
+
 2010-02-01  Xerxes RĂ„nby  <xerxes@zafena.se>
 
 	* ports/hotspot/src/share/vm/shark/sharkCompiler.cpp
--- a/ports/hotspot/src/cpu/zero/vm/asm_helper.cpp	Mon Feb 01 12:28:24 2010 +0100
+++ b/ports/hotspot/src/cpu/zero/vm/asm_helper.cpp	Tue Feb 02 11:08:36 2010 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009 Edward Nevill
+ * Copyright 2009, 2010 Edward Nevill
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
--- a/ports/hotspot/src/cpu/zero/vm/bytecodes_arm.def	Mon Feb 01 12:28:24 2010 +0100
+++ b/ports/hotspot/src/cpu/zero/vm/bytecodes_arm.def	Tue Feb 02 11:08:36 2010 +0000
@@ -1,4 +1,4 @@
-@ Copyright 2009 Edward Nevill
+@ Copyright 2009, 2010 Edward Nevill
 @ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 @
 @ This code is free software; you can redistribute it and/or modify it
--- a/ports/hotspot/src/cpu/zero/vm/cppInterpreter_arm.S	Mon Feb 01 12:28:24 2010 +0100
+++ b/ports/hotspot/src/cpu/zero/vm/cppInterpreter_arm.S	Tue Feb 02 11:08:36 2010 +0000
@@ -1,4 +1,4 @@
-@ Copyright 2009 Edward Nevill
+@ Copyright 2009, 2010 Edward Nevill
 @ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 @
 @ This code is free software; you can redistribute it and/or modify it
@@ -2996,9 +2996,12 @@
 	DECACHE_JPC
 handle_exception_with_bcp:
 	bl	load_dispatch
+	CACHE_JPC
 	ldr	stack, [istate, #ISTATE_STACK_BASE]
 	sub	stack, stack, #4
 	DECACHE_STACK
+	cmp	jpc, #0
+	beq	1f
 
 	mov	r0, istate
 	ldr	r1, [istate, #ISTATE_THREAD]
@@ -6151,38 +6154,108 @@
 dc_18:
 	.word     0x38e38e39
 
+#define TBIT 1
+
 	.global	Thumb2_DivZero_Handler
 Thumb2_DivZero_Handler:
+#ifdef THUMB2EE
+
+#define JAZ_V1	r5
+#define JAZ_V2	r6
+#define JAZ_V3	r7
+#define	JAZ_V4	r10
+#define	JAZ_V5	r11
+
+#define JAZ_REGSET	JAZ_V1,JAZ_V2,JAZ_V3,JAZ_V4,JAZ_V5,ip
+#define JAZ_REGSET_LEN	6
+
 	adrl	r0, idiv_clz_ret
 	cmp	r0, lr
 	addne	r0, r0, #irem_clz_ret - idiv_clz_ret
 	cmpne	r0, lr
 	beq	divide_by_zero_exception
-	ldr	r0, [istate, #ISTATE_METHOD]
-        ldr     jpc, [r0, #METHOD_CONSTMETHOD]
+	stmdb	sp!, {JAZ_REGSET}
+	bic	r0, lr, #TBIT
+	ldr	r1, [istate, #ISTATE_METHOD]
+        ldr     jpc, [r1, #METHOD_CONSTMETHOD]
 	add	jpc, jpc, #CONSTMETHOD_CODEOFFSET
+	mov	r2, sp
+	ldr	r3, [istate, #ISTATE_LOCALS]
+	bl	Thumb2_lr_to_bci
+	add	sp, sp, #JAZ_REGSET_LEN * 4
+	cmp	r0, #-1
+	moveq	jpc, #0
+	addne	jpc, jpc, r0
 	bl	load_dispatch
+#endif // THUMB2EE
 	b	divide_by_zero_exception
 
 #ifdef THUMB2EE
 
 	.global	Thumb2_Handle_Exception
+	.global	Thumb2_Handle_Exception_NoRegs
 	.global	Thumb2_ArrayBounds_Handler
 	.global	Thumb2_NullPtr_Handler
 	.global Thumb2_Stack_Overflow
 Thumb2_ArrayBounds_Handler:
-	ldr	r0, [istate, #ISTATE_METHOD]
-        ldr     jpc, [r0, #METHOD_CONSTMETHOD]
+	stmdb	sp!, {JAZ_REGSET}
+	bic	r0, lr, #TBIT
+	ldr	r1, [istate, #ISTATE_METHOD]
+        ldr     jpc, [r1, #METHOD_CONSTMETHOD]
 	add	jpc, jpc, #CONSTMETHOD_CODEOFFSET
+	mov	r2, sp
+	ldr	r3, [istate, #ISTATE_LOCALS]
+	bl	Thumb2_lr_to_bci
+	add	sp, sp, #JAZ_REGSET_LEN * 4
+	cmp	r0, #-1
+	moveq	jpc, #0
+	addne	jpc, jpc, r0
 	bl	load_dispatch
 	mov	r0, #VMSYMBOLS_ArrayIndexOutOfBounds
 	b	raise_exception
 Thumb2_Handle_Exception:
-	ldr	r0, [istate, #ISTATE_METHOD]
-        ldr     jpc, [r0, #METHOD_CONSTMETHOD]
+	stmdb	sp!, {JAZ_REGSET}
+	bic	r0, lr, #TBIT
+	ldr	r1, [istate, #ISTATE_METHOD]
+        ldr     jpc, [r1, #METHOD_CONSTMETHOD]
 	add	jpc, jpc, #CONSTMETHOD_CODEOFFSET
+	mov	r2, sp
+	ldr	r3, [istate, #ISTATE_LOCALS]
+	bl	Thumb2_lr_to_bci
+	add	sp, sp, #JAZ_REGSET_LEN * 4
+	cmp	r0, #-1
+	moveq	jpc, #0
+	addne	jpc, jpc, r0
 	bl	load_dispatch
 	b	handle_exception
+Thumb2_Handle_Exception_NoRegs:
+	bic	r0, lr, #TBIT
+	ldr	r1, [istate, #ISTATE_METHOD]
+        ldr     jpc, [r1, #METHOD_CONSTMETHOD]
+	add	jpc, jpc, #CONSTMETHOD_CODEOFFSET
+	mov	r2, #0
+	bl	Thumb2_lr_to_bci
+	cmp	r0, #-1
+	moveq	jpc, #0
+	addne	jpc, jpc, r0
+	bl	load_dispatch
+	b	handle_exception
+Thumb2_NullPtr_Handler:
+	stmdb	sp!, {JAZ_REGSET}
+	bic	r0, lr, #TBIT
+	ldr	r1, [istate, #ISTATE_METHOD]
+        ldr     jpc, [r1, #METHOD_CONSTMETHOD]
+	add	jpc, jpc, #CONSTMETHOD_CODEOFFSET
+	mov	r2, sp
+	ldr	r3, [istate, #ISTATE_LOCALS]
+	bl	Thumb2_lr_to_bci
+	add	sp, sp, #JAZ_REGSET_LEN * 4
+	cmp	r0, #-1
+	moveq	jpc, #0
+	addne	jpc, jpc, r0
+	bl	load_dispatch
+	b	null_ptr_exception
+
 Thumb2_Stack_Overflow:
 	mov	r0, r2
 	ldr	ip, [r0, #THREAD_TOP_ZERO_FRAME]
@@ -6198,13 +6271,6 @@
 	CACHE_LOCALS
 	DISPATCH	0
 
-Thumb2_NullPtr_Handler:
-	ldr	r0, [istate, #ISTATE_METHOD]
-        ldr     jpc, [r0, #METHOD_CONSTMETHOD]
-	add	jpc, jpc, #CONSTMETHOD_CODEOFFSET
-	bl	load_dispatch
-	b	null_ptr_exception
-
 	.global	Thumb2_Clear_Cache
 Thumb2_Clear_Cache:
 	stmdb	sp!, {r7}
--- a/ports/hotspot/src/cpu/zero/vm/thumb2.cpp	Mon Feb 01 12:28:24 2010 +0100
+++ b/ports/hotspot/src/cpu/zero/vm/thumb2.cpp	Tue Feb 02 11:08:36 2010 +0000
@@ -1,3 +1,21 @@
+/*
+ * Copyright 2009, 2010 Edward Nevill
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
 #ifdef THUMB2EE
 
 #define T2EE_PRINT_COMPILATION
@@ -363,7 +381,9 @@
 
 #define H_STACK_OVERFLOW		59
 
-unsigned handlers[60];
+#define H_HANDLE_EXCEPTION_NO_REGS	60
+
+unsigned handlers[61];
 
 #define LEAF_STACK_SIZE			200
 #define STACK_SPARE			40
@@ -377,12 +397,7 @@
 
 #ifdef PRODUCT
 
-#define JASSERT(cond, msg)					\
-	do {							\
-	  if (!(cond))						\
-	    longjmp(compiler_error_env, COMPILER_RESULT_FATAL);	\
-	} while (0)
-
+#define JASSERT(cond, msg)	0
 #define J_Unimplemented() longjmp(compiler_error_env, COMPILER_RESULT_FATAL)
 
 #else
@@ -546,17 +561,22 @@
     // 		bl	fast_entry
     // 		pop	{r4, r5, r6, r7, r9, r10, r11, pc}
     unsigned slow_entry[3];
-    // osr_tablep:				@ pointer to the osr table
-    // 		.word	osr_table
-    unsigned *osr_table;
+    unsigned *osr_table;			// pointer to the osr table
+    unsigned *exception_table;
     Compiled_Method *next;
+    // The next 6 halfword give the register mapping for JAZ_V1 to JAZ_v5
+    // This is used when receovering from an exception so we can push
+    // the register back into the local variables pool.
+    short regusage[6];
     // OSR Entry point:
     // 	R0 = entry point within compiled method
-    // 	R1 = locals
+    // 	R1 = locals - 4000 * 4
     // 	R2 = thread
+    // 	R3 = locals - 31 * 4
     // osr_entry:
     // 		@ Load each local into it register allocated register
-    // 		ldr	<reg>, [R1, #-<local> * 4]
+    // 		ldr	<reg>, [R1, #(4000-<local>) * 4]
+    //    or	ldr	<reg>, [R3, #(31-<local>) * 4]
     // 		...
     // 		mov	Rthread, R2
     // 		bx	R0
@@ -4484,8 +4504,13 @@
   out_32(jinfo->codebuf, 0);
 
   out_32(jinfo->codebuf, 0);	// pointer to osr table
+  out_32(jinfo->codebuf, 0);	// Space for exception_table pointer
   out_32(jinfo->codebuf, 0);	// next compiled method
 
+  out_32(jinfo->codebuf, 0);    // regusage
+  out_32(jinfo->codebuf, 0);
+  out_32(jinfo->codebuf, 0);
+
   // OSR entry point
   mov_reg(jinfo->codebuf, ARM_PC, ARM_R0);
 
@@ -4524,9 +4549,14 @@
   bl(jinfo->codebuf, out_pos(jinfo->codebuf) + CODE_ALIGN - 4);
   ldm(jinfo->codebuf, I_REGSET + (1<<ARM_PC), ARM_SP, POP_FD, 1);
 
-  out_32(jinfo->codebuf, 0);	// Space for osr_table point
+  out_32(jinfo->codebuf, 0);	// Space for osr_table pointer
+  out_32(jinfo->codebuf, 0);	// Space for exception_table pointer
   out_32(jinfo->codebuf, 0);	// Pointer to next method
 
+  out_32(jinfo->codebuf, 0);    // regusage
+  out_32(jinfo->codebuf, 0);
+  out_32(jinfo->codebuf, 0);
+
   // OSR entry point == Slow entry + 16 - caller save
   // R0 = entry point within compiled method
   // R1 = locals - THUMB2_MAXLOCALS * 4
@@ -4577,7 +4607,7 @@
 
     for (i = 0; i < extra_locals; i++) {
       unsigned linfo = locals_info[parms+i];
-      if (linfo & (1<< LOCAL_REF))
+      if (linfo & (1<< LOCAL_REF) || ((linfo >> LOCAL_INT) & 0x1f) == 0)
 	str_imm(jinfo->codebuf, ARM_R1, Rstack, (extra_locals-1 - i) * 4, 1, 0);
     }
   }
@@ -4671,7 +4701,7 @@
     mov_imm(jinfo->codebuf, ARM_R0, 0+CONSTMETHOD_CODEOFFSET);
     bl(jinfo->codebuf, handlers[H_SYNCHRONIZED_ENTER]);
     loc_exception = forward_16(jinfo->codebuf);
-    bl(jinfo->codebuf, handlers[H_HANDLE_EXCEPTION]);
+    bl(jinfo->codebuf, handlers[H_HANDLE_EXCEPTION_NO_REGS]);
     cbz_patch(jinfo->codebuf, ARM_R0, loc_exception);
     cbz_patch(jinfo->codebuf, ARM_R0, loc_success);
 //    mov_imm(jinfo->codebuf, ARM_R0, 0+CONSTMETHOD_CODEOFFSET);
@@ -4777,6 +4807,8 @@
 	if (stackinfo & BC_BRANCH_TARGET) break;
 	if (!(IS_DEAD(stackinfo) || IS_ZOMBIE(stackinfo))) break;
 
+	bc_stackinfo[bci] = (stackinfo & BC_FLAGS_MASK) | (codebuf->idx * 2);
+
 	if (opcode > OPC_LAST_JAVA_OP) {
 	  if (Bytecodes::is_defined((Bytecodes::Code)opcode))
 	    opcode = (unsigned)Bytecodes::java_code((Bytecodes::Code)opcode);
@@ -5824,7 +5856,7 @@
 	str_imm(jinfo->codebuf, ARM_R1, Rthread, THREAD_LAST_JAVA_SP, 1, 0);
 	cmp_imm(jinfo->codebuf, ARM_R3, 0);
 	it(jinfo->codebuf, COND_NE, IT_MASK_T);
-	bl(jinfo->codebuf, handlers[H_HANDLE_EXCEPTION]);
+	bl(jinfo->codebuf, handlers[H_HANDLE_EXCEPTION_NO_REGS]);
 	break;
       }
 
@@ -5941,7 +5973,7 @@
 	  str_imm(jinfo->codebuf, ARM_R1, Rthread, THREAD_LAST_JAVA_SP, 1, 0);
 	cmp_imm(jinfo->codebuf, ARM_R3, 0);
 	it(jinfo->codebuf, COND_NE, IT_MASK_T);
-	bl(jinfo->codebuf, handlers[H_HANDLE_EXCEPTION]);
+	bl(jinfo->codebuf, handlers[H_HANDLE_EXCEPTION_NO_REGS]);
 	  break;
 	} else {
   ldr_imm(jinfo->codebuf, ARM_R2, Ristate, ISTATE_METHOD, 1, 0);
@@ -5980,7 +6012,7 @@
 	  str_imm(jinfo->codebuf, ARM_R1, Rthread, THREAD_LAST_JAVA_SP, 1, 0);
 	cmp_imm(jinfo->codebuf, ARM_R3, 0);
 	it(jinfo->codebuf, COND_NE, IT_MASK_T);
-	bl(jinfo->codebuf, handlers[H_HANDLE_EXCEPTION]);
+	bl(jinfo->codebuf, handlers[H_HANDLE_EXCEPTION_NO_REGS]);
 	}
 	break;
       }
@@ -6447,7 +6479,6 @@
       }
 
       default:
-	printf("unknown bytecode = %d\n", opcode);
 	JASSERT(0, "unknown bytecode");
 	break;
     }
@@ -6461,17 +6492,95 @@
   }
 }
 
-void Thumb2_tablegen(Thumb2_Info *jinfo)
+#define BEG_BCI_OFFSET		0
+#define END_BCI_OFFSET		1
+#define HANDLER_BCI_OFFSET	2
+#define KLASS_INDEX_OFFSET	3
+#define ENTRY_SIZE		4
+
+extern "C" int Thumb2_lr_to_bci(unsigned lr, methodOop method, Reg *regs, unsigned *locals)
+{
+  Compiled_Method *cmethod = compiled_method_list;
+  typeArrayOop table = method->exception_table();
+  constantPoolOop pool = method->constants();
+  int length = table->length();
+
+  while (cmethod) {
+    unsigned *exception_table = cmethod->exception_table;
+    if (exception_table) {
+      unsigned code_base = (unsigned)cmethod;
+      if (code_base <= lr && lr <= (unsigned)exception_table) {
+	int exception_index = -1;
+	unsigned exception_found = 0;
+
+	for (int i = 0; i < length; i += ENTRY_SIZE) {
+	  unsigned offsets = *exception_table++;
+	  unsigned exc_beg = code_base + ((offsets >> 16) << 1);
+	  unsigned exc_end = code_base + ((offsets & 0xffff) << 1);
+
+	  if (exc_beg <= lr && lr <= exc_end) {
+	    if (exc_beg > exception_found) {
+	      // With nested try catch blocks, choose the most deeply nested
+	      exception_found = exc_beg;
+	      exception_index = i;
+	    }	    
+	  }
+	  if (exception_index >= 0) {
+	    if (regs) {
+	      for (unsigned i = 0; i < PREGS; i++) {
+		int local = cmethod->regusage[i];
+		if (local >= 0) {
+		  locals[-local] = regs[i];
+		}
+	      }
+	    }
+	    return table->int_at(exception_index + BEG_BCI_OFFSET);
+	  }
+	}
+      }
+    }
+    cmethod = cmethod->next;
+  }
+  return -1;
+}
+
+void Thumb2_generate_exception_table(Compiled_Method *cmethod, Thumb2_Info *jinfo)
+{
+  methodOop method = jinfo->method;
+  typeArrayOop table = method->exception_table();
+  constantPoolOop pool = method->constants();
+  int length = table->length();
+  unsigned *bc_stackinfo = jinfo->bc_stackinfo;
+
+  cmethod->exception_table = (unsigned *)out_pos(jinfo->codebuf);
+  for (int i = 0; i < length; i += ENTRY_SIZE) {
+    int beg_bci = table->int_at(i + BEG_BCI_OFFSET);
+    int end_bci = table->int_at(i + END_BCI_OFFSET);
+    unsigned stackinfo;
+    unsigned beg_offset, end_offset;
+
+    stackinfo = bc_stackinfo[beg_bci];
+    beg_offset = (stackinfo & ~BC_FLAGS_MASK) >> 1;
+    stackinfo = bc_stackinfo[end_bci];
+    end_offset = (stackinfo & ~BC_FLAGS_MASK) >> 1;
+    if (!(beg_offset != 0 && end_offset >= beg_offset && end_offset < 65536)) {
+	longjmp(compiler_error_env, COMPILER_RESULT_FAILED);
+    }
+    out_32(jinfo->codebuf, (beg_offset << 16) | (end_offset));
+  }
+}
+
+void Thumb2_tablegen(Compiled_Method *cmethod, Thumb2_Info *jinfo)
 {
   unsigned code_size = jinfo->code_size;
   jubyte *code_base = jinfo->code_base;
   unsigned *bc_stackinfo = jinfo->bc_stackinfo;
   unsigned bci;
-  unsigned *count_pos = (unsigned *)out_pos(jinfo->codebuf);
   unsigned count = 0;
   unsigned i;
   CodeBuf *codebuf = jinfo->codebuf;
 
+  cmethod->osr_table = (unsigned *)out_pos(jinfo->codebuf);
   out_32(codebuf, 0);
   bc_stackinfo[0] |= BC_BACK_TARGET;
   for (bci = 0; bci < code_size;) {
@@ -6501,7 +6610,9 @@
       bci += len;
     }
   }
-  *count_pos = count;
+  *cmethod->osr_table = count;
+  if (jinfo->method->has_exception_handler())
+    Thumb2_generate_exception_table(cmethod, jinfo);
 }
 
 extern "C" void Thumb2_Clear_Cache(char *base, char *limit);
@@ -6625,7 +6736,7 @@
 extern "C" unsigned cmpxchg_ptr(unsigned new_value, volatile unsigned *ptr, unsigned cmp_value);
 static volatile unsigned compiling;
 static unsigned CompileCount = 0;
-static unsigned MaxCompile = 35;
+static unsigned MaxCompile = 130;
 
 #define COMPILE_ONLY	0
 #define COMPILE_COUNT	0
@@ -6705,8 +6816,7 @@
   //
   if (code_size > THUMB2_MAX_BYTECODE_SIZE ||
 		(method->max_locals() + method->max_stack()) >= 1000 ||
-		method->has_monitor_bytecodes() ||
-		method->has_exception_handler()) {
+		method->has_monitor_bytecodes()) {
         method->set_not_compilable();
 	return 0;
   }
@@ -6748,8 +6858,11 @@
 
 #ifdef T2EE_PRINT_COMPILATION
   if (t2ee_print_compilation) {
-    tty->print("Compiling (%d) ", compiled_methods);
-    tty->print_cr("%s", method->name_and_sig_as_C_string());
+    fprintf(stderr, "Compiling %d %c%c %s\n",
+	compiled_methods,
+	method->is_synchronized() ? 'S' : ' ',
+	method->has_exception_handler() ? 'E' : ' ',
+	method->name_and_sig_as_C_string());
   }
 #endif
 
@@ -6825,6 +6938,9 @@
 	Thumb2_disass(&jinfo_str);
 #endif
 
+  for (int i = 0; i < PREGS; i++)
+    cmethod->regusage[i] = jregs_str.mapping[i];
+
   Thumb2_Clear_Cache(cb->hp, cb->hp + codebuf_str.idx * 2);
 
 #ifdef T2EE_PRINT_STATISTICS
@@ -6836,7 +6952,7 @@
     bytecodes_compiled += code_size;
     arm_code_generated += codegen;
     total_zombie_bytes += jinfo_str.zombie_bytes;
-    tty->print("%d bytecodes => %d bytes code in %.2f sec, totals: %d => %d in %.2f sec\n",
+    fprintf(stderr, "%d bytecodes => %d bytes code in %.2f sec, totals: %d => %d in %.2f sec\n",
       code_size, codegen, (double)compile_time/(double)CLOCKS_PER_SEC,
     bytecodes_compiled, arm_code_generated, (double)total_compile_time/(double)CLOCKS_PER_SEC);
   }
@@ -6846,13 +6962,8 @@
 
   out_32(&codebuf_str, slow_entry);
 
-  if (!compiled_accessor) {
-    unsigned osr_entry_table = out_pos(jinfo_str.codebuf);
-
-    Thumb2_tablegen(&jinfo_str);
-
-    cmethod->osr_table = (unsigned *)osr_entry_table;
-  }
+  if (!compiled_accessor)
+    Thumb2_tablegen(cmethod, &jinfo_str);
 
   cb->hp += codebuf_str.idx * 2;
 
@@ -6875,6 +6986,7 @@
 extern "C" void Thumb2_DivZero_Handler(void);
 extern "C" void Thumb2_ArrayBounds_Handler(void);
 extern "C" void Thumb2_Handle_Exception(void);
+extern "C" void Thumb2_Handle_Exception_NoRegs(void);
 extern "C" void Thumb2_Exit_To_Interpreter(void);
 extern "C" void Thumb2_Stack_Overflow(void);
 
@@ -6968,6 +7080,7 @@
   Thumb2_CodeBuf *cb = thumb2_codebuf;
   if (!(CPUInfo & ARCH_THUMBEE)) return 0;
   if (IS_COMPILED(pc, cb)) {
+    regs[ARM_LR] = pc;
     regs[ARM_PC] = (unsigned)Thumb2_NullPtr_Handler;
     regs[ARM_CPSR] &= ~CPSR_THUMB_BIT;
     return 1;
@@ -7074,6 +7187,10 @@
   mov_imm(&codebuf, ARM_R3, (u32)Thumb2_Handle_Exception);
   mov_reg(&codebuf, ARM_PC, ARM_R3);
 
+  handlers[H_HANDLE_EXCEPTION_NO_REGS] = out_pos(&codebuf);
+  mov_imm(&codebuf, ARM_R3, (u32)Thumb2_Handle_Exception_NoRegs);
+  mov_reg(&codebuf, ARM_PC, ARM_R3);
+
   handlers[H_STACK_OVERFLOW] = out_pos(&codebuf);
   mov_imm(&codebuf, ARM_R3, (u32)Thumb2_Stack_Overflow);
   mov_reg(&codebuf, ARM_PC, ARM_R3);