diff --git a/src/code/README.txt b/src/code/README.txt
index 4b2db2073d0ab29b12d16dcb0de12d7bb8299f5f..8ed093ab40cbdc0f55ffed6c3044a3493e46821a 100644
--- a/src/code/README.txt
+++ b/src/code/README.txt
@@ -192,6 +192,13 @@ OPCODE_INLINE_BRANCH
 	value for those opcodes and jump iff it's nonzero. This is to
 	facilitate easier inlining of branches in the machine code.
 
+OPCODE_INLINE_RETURN
+	If defined, opcode functions that perform INTER_RETURN will
+	return (void *)(ptrdiff_t)-1 when they want to exit from
+	the running interpreter. These opcodes also have the I_RETURN
+	flag set. This is to facilitate easier use of and clean up
+	of INS_ENTRY().
+
 OPCODE_RETURN_JUMPADDR
 	If defined, jump functions that return the address to jump to
 	will be generated for I_JUMP instructions, so the ins_f_byte*
diff --git a/src/interpret.c b/src/interpret.c
index 71de6b44ebf7ec50cc5a95f7944c5d46dffb582b..d67f21fcd08db971b568d2bb58af45777f44e597 100644
--- a/src/interpret.c
+++ b/src/interpret.c
@@ -1107,10 +1107,177 @@ static int catching_eval_instruction (PIKE_OPCODE_T *pc);
 
 
 #ifdef PIKE_USE_MACHINE_CODE
+#ifdef OPCODE_INLINE_RETURN
+/* Catch notes:
+ *
+ * Typical F_CATCH use:
+ *
+ *   F_CATCH
+ * 	F_PTR	continue_label
+ *
+ *   ENTRY
+ *
+ *   catch body
+ *
+ *   F_EXIT_CATCH
+ *
+ *   F_BRANCH
+ *	F_PTR	continue_label
+ *
+ *   ENTRY
+ *
+ * continue_label:
+ *
+ *   rest of code.
+ */
+
+/* Modified calling-conventions to simplify code-generation when
+ * INTER_RETURN is inlined.
+ *
+ * cf interpret_functions.h:F_CATCH
+ *
+ * Arguments:
+ *   addr:
+ *     Entry-point for the catch block.
+ *
+ *   continue_addr:
+ *     Offset from addr for code after the catch (and after ENTRY).
+ *
+ * Returns:
+ *   (PIKE_OPCODE_T *)-1 on INTER_RETURN.
+ *   jump_destination otherwise.
+ */
+PIKE_OPCODE_T *inter_return_opcode_F_CATCH(PIKE_OPCODE_T *addr,
+					   INT32 continue_addr)
+{
+  if (d_flag || Pike_interpreter.trace_level > 2) {
+    low_debug_instr_prologue (F_CATCH - F_OFFSET);
+    if (Pike_interpreter.trace_level>3) {
+      sprintf(trace_buffer,
+	      "-    Addr = %p\n"
+	      "-    Continue = 0x%ld\n",
+	      addr, continue_addr);
+      write_to_stderr(trace_buffer,strlen(trace_buffer));
+    }
+  }
+  {
+    struct catch_context *new_catch_ctx = alloc_catch_context();
+#ifdef PIKE_DEBUG
+      new_catch_ctx->frame = Pike_fp;
+      init_recovery (&new_catch_ctx->recovery, 0, 0, PERR_LOCATION());
+#else
+      init_recovery (&new_catch_ctx->recovery, 0);
+#endif
+    new_catch_ctx->save_expendible = Pike_fp->expendible;
+    new_catch_ctx->continue_reladdr = continue_addr
+      /* We need to run the entry prologue... */
+      - ENTRY_PROLOGUE_SIZE;
+
+    new_catch_ctx->next_addr = addr;
+    new_catch_ctx->prev = Pike_interpreter.catch_ctx;
+    Pike_interpreter.catch_ctx = new_catch_ctx;
+    DO_IF_DEBUG({
+	TRACE((3,"-   Pushed catch context %p\n", new_catch_ctx));
+      });
+  }
+
+  Pike_fp->expendible = Pike_fp->locals + Pike_fp->num_locals;
+
+#if 0
+  /* Need to adjust next_addr by sizeof(INT32) to skip past the jump
+   * address to the continue position after the catch block. */
+  addr = (PIKE_OPCODE_T *) ((INT32 *) addr + 1);
+#endif
+
+  if (Pike_interpreter.catching_eval_jmpbuf) {
+    /* There's already a catching_eval_instruction around our
+     * eval_instruction, so we can just continue. */
+    debug_malloc_touch_named (Pike_interpreter.catch_ctx, "(1)");
+    /* Skip past the entry prologue... */
+    addr += ENTRY_PROLOGUE_SIZE;
+    DO_IF_DEBUG({
+	TRACE((3,"-   In active catch; continuing at %p\n", addr));
+      });
+    return addr;
+  }
+  else {
+    debug_malloc_touch_named (Pike_interpreter.catch_ctx, "(2)");
+
+    while (1) {
+      /* Loop here every time an exception is caught. Once we've
+       * gotten here and set things up to run eval_instruction from
+       * inside catching_eval_instruction, we keep doing it until it's
+       * time to return. */
+
+      int res;
+
+      DO_IF_DEBUG({
+	  TRACE((3,"-   Activating catch; calling %p in context %p\n",
+		 addr, Pike_interpreter.catch_ctx));
+	});
+
+      res = catching_eval_instruction (addr);
+
+      DO_IF_DEBUG({
+	  TRACE((3,"-   catching_eval_instruction(%p) returned %d\n",
+		 addr, res));
+	});
+
+      if (res != -3) {
+	/* There was an inter return inside the evaluated code. Just
+	 * propagate it. */
+	DO_IF_DEBUG ({
+	    TRACE((3,"-   Returning from catch.\n"));
+	    if (res != -1) Pike_fatal ("Unexpected return value from "
+				       "catching_eval_instruction: %d\n", res);
+	  });
+	break;
+      }
+
+      else {
+	/* Caught an exception. */
+	struct catch_context *cc = Pike_interpreter.catch_ctx;
+
+	DO_IF_DEBUG ({
+	    TRACE((3,"-   Caught exception. catch context: %p\n", cc));
+	    if (!cc) Pike_fatal ("Catch context dropoff.\n");
+	    if (cc->frame != Pike_fp)
+	      Pike_fatal ("Catch context doesn't belong to this frame.\n");
+	  });
+
+	debug_malloc_touch_named (cc, "(3)");
+	UNSETJMP (cc->recovery);
+	Pike_fp->expendible = cc->save_expendible;
+	move_svalue (Pike_sp++, &throw_value);
+	mark_free_svalue (&throw_value);
+	low_destruct_objects_to_destruct();
+
+	if (cc->continue_reladdr < 0)
+	  FAST_CHECK_THREADS_ON_BRANCH();
+	addr = cc->next_addr + cc->continue_reladdr;
+
+	DO_IF_DEBUG({
+	    TRACE((3,"-   Popping catch context %p ==> %p\n",
+		   cc, cc->prev));
+	    if (!addr) Pike_fatal ("Unexpected null continue addr.\n");
+	  });
+
+	Pike_interpreter.catch_ctx = cc->prev;
+	really_free_catch_context (cc);
+      }
+    }
+
+    return (PIKE_OPCODE_T *)(ptrdiff_t)-1;	/* INTER_RETURN; */
+  }
+}
+
+void *do_inter_return_label = (void*)(ptrdiff_t)-1;
+#else
 /* Labels to jump to to cause eval_instruction to return */
 /* FIXME: Replace these with assembler lables */
 void *do_inter_return_label = NULL;
 void *dummy_label = NULL;
+#endif
 
 #ifndef CALL_MACHINE_CODE
 #define CALL_MACHINE_CODE(pc)					\
@@ -1280,8 +1447,8 @@ C }
 
 #if defined(OPCODE_INLINE_BRANCH) || defined(PIKE_SMALL_EVAL_INSTRUCTION)
 #define TEST_OPCODE0(O,N,F,C) \
-int PIKE_CONCAT(test_opcode_,O)(void) { \
-    int branch_taken = 0;	\
+ptrdiff_t PIKE_CONCAT(test_opcode_,O)(void) { \
+    ptrdiff_t branch_taken = 0;	\
     DEF_PROG_COUNTER; \
     DEBUG_PROLOGUE (O, ;);						\
     C; \
@@ -1289,8 +1456,8 @@ int PIKE_CONCAT(test_opcode_,O)(void) { \
   }
 
 #define TEST_OPCODE1(O,N,F,C) \
-int PIKE_CONCAT(test_opcode_,O)(INT32 arg1) {\
-    int branch_taken = 0;	\
+ptrdiff_t PIKE_CONCAT(test_opcode_,O)(INT32 arg1) {\
+    ptrdiff_t branch_taken = 0;	\
     DEF_PROG_COUNTER; \
     DEBUG_PROLOGUE (O, DEBUG_LOG_ARG (arg1));				\
     C; \
@@ -1299,8 +1466,8 @@ int PIKE_CONCAT(test_opcode_,O)(INT32 arg1) {\
 
 
 #define TEST_OPCODE2(O,N,F,C) \
-int PIKE_CONCAT(test_opcode_,O)(INT32 arg1, INT32 arg2) { \
-    int branch_taken = 0;	\
+ptrdiff_t PIKE_CONCAT(test_opcode_,O)(INT32 arg1, INT32 arg2) { \
+    ptrdiff_t branch_taken = 0;	\
     DEF_PROG_COUNTER; \
     DEBUG_PROLOGUE (O, DEBUG_LOG_ARG (arg1); DEBUG_LOG_ARG2 (arg2));	\
     C; \
@@ -1326,9 +1493,9 @@ int PIKE_CONCAT(test_opcode_,O)(INT32 arg1, INT32 arg2) { \
 #define OPCODE1_TAILPTRJUMP(O,N,F,C) OPCODE1_PTRJUMP(O,N,F,C)
 #define OPCODE2_TAILPTRJUMP(O,N,F,C) OPCODE2_PTRJUMP(O,N,F,C)
 
-#define OPCODE0_RETURN(O,N,F,C) OPCODE0_JUMP(O,N,F,C)
-#define OPCODE1_RETURN(O,N,F,C) OPCODE1_JUMP(O,N,F,C)
-#define OPCODE2_RETURN(O,N,F,C) OPCODE2_JUMP(O,N,F,C)
+#define OPCODE0_RETURN(O,N,F,C) OPCODE0_JUMP(O,N,F | I_RETURN,C)
+#define OPCODE1_RETURN(O,N,F,C) OPCODE1_JUMP(O,N,F | I_RETURN,C)
+#define OPCODE2_RETURN(O,N,F,C) OPCODE2_JUMP(O,N,F | I_RETURN,C)
 #define OPCODE0_TAILRETURN(O,N,F,C) OPCODE0_RETURN(O,N,F,C)
 #define OPCODE1_TAILRETURN(O,N,F,C) OPCODE1_RETURN(O,N,F,C)
 #define OPCODE2_TAILRETURN(O,N,F,C) OPCODE2_RETURN(O,N,F,C)
@@ -1427,6 +1594,7 @@ static int eval_instruction_low(PIKE_OPCODE_T *pc)
 {
   if(pc == NULL) {
 
+#ifndef OPCODE_INLINE_RETURN
     if(do_inter_return_label != NULL)
       Pike_fatal("eval_instruction called with NULL (twice).\n");
 
@@ -1458,6 +1626,7 @@ static int eval_instruction_low(PIKE_OPCODE_T *pc)
     /* Trick optimizer */
     if(!dummy_label)
       return 0;
+#endif /* !OPCODE_INLINE_RETURN */
   }
 
   /* This else is important to avoid an overoptimization bug in (at
@@ -1475,6 +1644,7 @@ static int eval_instruction_low(PIKE_OPCODE_T *pc)
 #endif
   }
 
+#ifndef OPCODE_INLINE_RETURN
 #ifdef __GNUC__
   goto *dummy_label;
 #endif
@@ -1487,6 +1657,7 @@ static int eval_instruction_low(PIKE_OPCODE_T *pc)
   }
 #endif
 
+#endif /* !OPCODE_INLINE_RETURN */
  inter_return_label:
   EXIT_MACHINE_CODE();
 #ifdef PIKE_DEBUG
diff --git a/src/interpret.h b/src/interpret.h
index aac3f258a70a796b302cd2ba3af79631637949ea..bc10d6cbee364e736ac13bdb2a2cb43903c42d3e 100644
--- a/src/interpret.h
+++ b/src/interpret.h
@@ -775,6 +775,10 @@ void call_check_threads_etc();
     defined(INS_F_JUMP_WITH_ARG) || defined(INS_F_JUMP_WITH_TWO_ARGS)
 void branch_check_threads_etc();
 #endif
+#ifdef OPCODE_INLINE_RETURN
+PIKE_OPCODE_T *inter_return_opcode_F_CATCH(PIKE_OPCODE_T *addr,
+					   INT32 continue_addr);
+#endif
 #ifdef PIKE_DEBUG
 void simple_debug_instr_prologue_0 (PIKE_INSTR_T instr);
 void simple_debug_instr_prologue_1 (PIKE_INSTR_T instr, INT32 arg);
diff --git a/src/interpret_functions.h b/src/interpret_functions.h
index 8c08df44a7d556e316209cdc492c332739f4fb78..e995a1887d1e8f8ccf75a063708892ef391ad5f4 100644
--- a/src/interpret_functions.h
+++ b/src/interpret_functions.h
@@ -1312,7 +1312,7 @@ OPCODE0_BRANCH(F_EQ_AND, "==&&", I_UPDATE_SP, {
 /* Ideally this ought to be an OPCODE0_PTRRETURN but I don't fancy
  * adding that variety to this macro hell. At the end of the day there
  * wouldn't be any difference anyway afaics. /mast */
-OPCODE0_PTRJUMP(F_CATCH, "catch", I_UPDATE_ALL, {
+OPCODE0_PTRJUMP(F_CATCH, "catch", I_UPDATE_ALL|I_RETURN, {
   PIKE_OPCODE_T *addr;
 
   {
diff --git a/src/interpreter.h b/src/interpreter.h
index 93578542f448b621b7416bf835794b7f9b67a41c..388bcbb55468f56a6288e9467372d18fe60b9f26 100644
--- a/src/interpreter.h
+++ b/src/interpreter.h
@@ -191,12 +191,12 @@ static int eval_instruction(PIKE_OPCODE_T *pc)
 #define OPCODE1_TAILJUMP	OPCODE1_TAIL
 #define OPCODE2_TAILJUMP	OPCODE2_TAIL
 
-#define OPCODE0_RETURN(OP, DESC, FLAGS, CODE) OPCODE0(OP, DESC, FLAGS, CODE)
-#define OPCODE1_RETURN(OP, DESC, FLAGS, CODE) OPCODE1(OP, DESC, FLAGS, CODE)
-#define OPCODE2_RETURN(OP, DESC, FLAGS, CODE) OPCODE2(OP, DESC, FLAGS, CODE)
-#define OPCODE0_TAILRETURN(OP, DESC, FLAGS, CODE) OPCODE0_TAIL(OP, DESC, FLAGS, CODE)
-#define OPCODE1_TAILRETURN(OP, DESC, FLAGS, CODE) OPCODE1_TAIL(OP, DESC, FLAGS, CODE)
-#define OPCODE2_TAILRETURN(OP, DESC, FLAGS, CODE) OPCODE2_TAIL(OP, DESC, FLAGS, CODE)
+#define OPCODE0_RETURN(OP, DESC, FLAGS, CODE) OPCODE0(OP, DESC, FLAGS | I_RETURN, CODE)
+#define OPCODE1_RETURN(OP, DESC, FLAGS, CODE) OPCODE1(OP, DESC, FLAGS | I_RETURN, CODE)
+#define OPCODE2_RETURN(OP, DESC, FLAGS, CODE) OPCODE2(OP, DESC, FLAGS | I_RETURN, CODE)
+#define OPCODE0_TAILRETURN(OP, DESC, FLAGS, CODE) OPCODE0_TAIL(OP, DESC, FLAGS | I_RETURN, CODE)
+#define OPCODE1_TAILRETURN(OP, DESC, FLAGS, CODE) OPCODE1_TAIL(OP, DESC, FLAGS | I_RETURN, CODE)
+#define OPCODE2_TAILRETURN(OP, DESC, FLAGS, CODE) OPCODE2_TAIL(OP, DESC, FLAGS | I_RETURN, CODE)
 
 #define OPCODE0_PTRJUMP(OP, DESC, FLAGS, CODE) CASE(OP); CODE; DONE
 #define OPCODE0_TAILPTRJUMP(OP, DESC, FLAGS, CODE) CASE(OP); CODE
diff --git a/src/opcodes.c b/src/opcodes.c
index f16d09cffd2ff789416aa02e6ab329461a9f5831..f5f327bbf134fc21e6b4e221321f84d9536908d1 100644
--- a/src/opcodes.c
+++ b/src/opcodes.c
@@ -114,18 +114,18 @@ void present_runned(struct instr_counter *d, int depth, int maxdepth)
 #define OPCODE1_PTRJUMP(OP,DESC,FLAGS) OPCODE1_JUMP(OP, DESC, FLAGS)
 #define OPCODE2_PTRJUMP(OP,DESC,FLAGS) OPCODE2_JUMP(OP, DESC, FLAGS)
 
-#define OPCODE0_RETURN(OP,DESC,FLAGS) OPCODE0_JUMP(OP, DESC, FLAGS)
-#define OPCODE1_RETURN(OP,DESC,FLAGS) OPCODE1_JUMP(OP, DESC, FLAGS)
-#define OPCODE2_RETURN(OP,DESC,FLAGS) OPCODE2_JUMP(OP, DESC, FLAGS)
+#define OPCODE0_RETURN(OP,DESC,FLAGS) OPCODE0_JUMP(OP, DESC, FLAGS | I_RETURN)
+#define OPCODE1_RETURN(OP,DESC,FLAGS) OPCODE1_JUMP(OP, DESC, FLAGS | I_RETURN)
+#define OPCODE2_RETURN(OP,DESC,FLAGS) OPCODE2_JUMP(OP, DESC, FLAGS | I_RETURN)
 
 #define OPCODE0_ALIAS(OP, DESC, FLAGS, ADDR)
 #define OPCODE1_ALIAS(OP, DESC, FLAGS, ADDR)
 #define OPCODE2_ALIAS(OP, DESC, FLAGS, ADDR)
 
 #ifdef OPCODE_INLINE_BRANCH
-#define OPCODE0_BRANCH(OP,DESC,FLAGS) int PIKE_CONCAT(test_opcode_,OP)(void);
-#define OPCODE1_BRANCH(OP,DESC,FLAGS) int PIKE_CONCAT(test_opcode_,OP)(INT32);
-#define OPCODE2_BRANCH(OP,DESC,FLAGS) int PIKE_CONCAT(test_opcode_,OP)(INT32,INT32);
+#define OPCODE0_BRANCH(OP,DESC,FLAGS) ptrdiff_t PIKE_CONCAT(test_opcode_,OP)(void);
+#define OPCODE1_BRANCH(OP,DESC,FLAGS) ptrdiff_t PIKE_CONCAT(test_opcode_,OP)(INT32);
+#define OPCODE2_BRANCH(OP,DESC,FLAGS) ptrdiff_t PIKE_CONCAT(test_opcode_,OP)(INT32,INT32);
 #define BRANCHADDR(X) , (void *)PIKE_CONCAT(test_opcode_,X)
 #else /* !OPCODE_INLINE_BRANCH */
 #define OPCODE0_BRANCH		OPCODE0_PTRJUMP
@@ -220,9 +220,9 @@ void present_runned(struct instr_counter *d, int depth, int maxdepth)
 #define OPCODE1_TAILJUMP(OP, DESC, FLAGS) OPCODE1_JUMP(OP,DESC,FLAGS)
 #define OPCODE2_TAILJUMP(OP, DESC, FLAGS) OPCODE2_JUMP(OP,DESC,FLAGS)
 
-#define OPCODE0_RETURN(OP, DESC, FLAGS) OPCODE0_JUMP(OP,DESC,FLAGS)
-#define OPCODE1_RETURN(OP, DESC, FLAGS) OPCODE1_JUMP(OP,DESC,FLAGS)
-#define OPCODE2_RETURN(OP, DESC, FLAGS) OPCODE2_JUMP(OP,DESC,FLAGS)
+#define OPCODE0_RETURN(OP, DESC, FLAGS) OPCODE0_JUMP(OP,DESC,FLAGS | I_RETURN)
+#define OPCODE1_RETURN(OP, DESC, FLAGS) OPCODE1_JUMP(OP,DESC,FLAGS | I_RETURN)
+#define OPCODE2_RETURN(OP, DESC, FLAGS) OPCODE2_JUMP(OP,DESC,FLAGS | I_RETURN)
 #define OPCODE0_TAILRETURN(OP, DESC, FLAGS) OPCODE0_RETURN(OP,DESC,FLAGS)
 #define OPCODE1_TAILRETURN(OP, DESC, FLAGS) OPCODE1_RETURN(OP,DESC,FLAGS)
 #define OPCODE2_TAILRETURN(OP, DESC, FLAGS) OPCODE2_RETURN(OP,DESC,FLAGS)
diff --git a/src/opcodes.h b/src/opcodes.h
index 4d4f801768f9bf1156cba014fc617ff34d5c297c..956ef539f656ce1065317eddb87e1a16a6947f99 100644
--- a/src/opcodes.h
+++ b/src/opcodes.h
@@ -50,6 +50,8 @@ struct keyword
 #define I_UPDATE_FP	512	/* Opcode modifies Pike_fp */
 #define I_UPDATE_M_SP	1024	/* Opcode modifies Pike_mark_sp */
 
+#define I_RETURN	2048	/* Opcode may return to the previous frame. */
+
 /* Convenience variants */
 #define I_TWO_ARGS	(I_HASARG | I_HASARG2)
 #define I_DATA		(I_HASARG | I__DATA)