all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* Inefficiency in Bgotoifnil byte-code instruction
@ 2012-06-28  3:01 John Wiegley
  2012-06-28  4:38 ` Stefan Monnier
                   ` (2 more replies)
  0 siblings, 3 replies; 23+ messages in thread
From: John Wiegley @ 2012-06-28  3:01 UTC (permalink / raw)
  To: emacs-devel

I've been doing some byte-code profiling, and have discovered that the opcodes
Bgotoifnil and Bgotoifnonnil are high up, in the top ten:

    | Bcall+3       |  1228846 | 21464419 | 21.464 |
    | Bcall+1       | 15379936 | 17113001 | 17.113 |
    | Bcall         |   895713 | 15189629 |  15.19 |
    | Bcall+6       |   105234 |  6720697 | 6.7207 |
    | Bcall+2       |  5616918 |  6043517 | 6.0435 |
    | Bgotoifnil    | 43048427 |  4416549 | 4.4165 |
    | Bvarref+6     | 63539569 |  3507246 | 3.5072 |
    | Bgotoifnonnil | 24043617 |  2474823 | 2.4748 |
    | Bgoto_char    |   517466 |  1867576 | 1.8676 |
    | Bcar          | 32846356 |  1741051 | 1.7411 |

My recommendation is that we split these into two ops: Bgotoifnil and
Bgotoifnonnil, which use only FETCH and a positive/negative offset from the
current pc, and Bgotoifnil2 and Bgotoifnonnil2 which use the old logic
(FETCH2, and an absolute offset from the start of the bytecode stream).

The resulting interpreter code would look like this:

    #ifdef BYTE_CODE_SAFE
    #define CHECK_OFFSET_RANGE(ARG)                                \
      {                                                            \
        int curpos = start.pc - stack.byte_string_start;           \
                                                                   \
        if (curpos + ARG >= bytestr_length || curpos + ARG < 0)    \
          abort ()                                                 \
      }
    #else /* not BYTE_CODE_SAFE */
    #define CHECK_OFFSET_RANGE(ARG)
    #endif /* not BYTE_CODE_SAFE */

	case Bgotoifnil:
	  {
	    Lisp_Object v1;
	    MAYBE_CG ();
	    op = FETCH;
	    v1 = POP;
	    if (NILP (v1))
	      {
		BYTE_CODE_QUIT;
		CHECK_OFFSET_RANGE (op);
		stack.pc += op;
	      }
	    break;
	  }

	case Bgotoifnil2:
	  {
	    Lisp_Object v1;
	    MAYBE_GC ();
	    op = FETCH2;
	    v1 = POP;
	    if (NILP (v1))
	      {
		BYTE_CODE_QUIT;
		CHECK_RANGE (op);
		stack.pc = stack.byte_string_start + op;
	      }
	    break;
	  }

        /* and likewise for Bgotoifnonnil... */

This way we optimize for the most used case for such a common instruction.

Making this change in the C code is easy, but changing bytecomp.el wasn't
obvious to me.  Can anyone help me with that?

John



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-06-28  3:01 Inefficiency in Bgotoifnil byte-code instruction John Wiegley
@ 2012-06-28  4:38 ` Stefan Monnier
  2012-06-29 16:39   ` Tom Tromey
  2012-06-28  4:41 ` Stefan Monnier
  2012-06-28 13:07 ` Richard Stallman
  2 siblings, 1 reply; 23+ messages in thread
From: Stefan Monnier @ 2012-06-28  4:38 UTC (permalink / raw)
  To: emacs-devel

> My recommendation is that we split these into two ops: Bgotoifnil and
> Bgotoifnonnil, which use only FETCH and a positive/negative offset from the
> current pc, and Bgotoifnil2 and Bgotoifnonnil2 which use the old logic
> (FETCH2, and an absolute offset from the start of the bytecode stream).

You mean, provide "short" forms of Bgotoifnil and Bgotoifnonnil?
I'm not sure it's worth the trouble: the only benefit is to replace
a FETCH2 by a FETCH, which doesn't seem like it would save a large
fraction of the time to execute those operations.

Of course, when it comes to performance, intuition is often wrong, so
feel free to try it out.

> Making this change in the C code is easy, but changing bytecomp.el wasn't
> obvious to me.  Can anyone help me with that?

You want to look at byte-compile-lapcode, where:

        (cond ((memq op byte-goto-ops)
               ;; goto
               (byte-compile-push-bytecodes opcode nil (cdr off) bytes pc)
               (push bytes patchlist))

is the code that outputs the 3 bytes (opcode, nil, and (cdr off)), where
the last 2 are not real bytes yet (they'll be filled later via
`patchlist').

So you can probably get what you want by only changing:

    (dolist (bytes-tail patchlist)
      (setq pc (caar bytes-tail))	; Pick PC from goto's tag.
      (setcar (cdr bytes-tail) (logand pc 255))
      (setcar bytes-tail (lsh pc -8))
      ;; FIXME: Replace this by some workaround.
      (if (> (car bytes-tail) 255) (error "Bytecode overflow")))

such that if the `pc' (which is the target address) is sufficiently
close to the current address (which the current code doesn't compute
currently but which should be (length bytes-tail), IIRC), then change
the opcode, use setcdr to drop a byte, and set the offset byte.
Of course, since that will change the offsets of subsequent code, you
might prefer to traverse patchlist in the reverse order and keep
a counter of dropped bytes, and you'll have to change
(setq pc (caar bytes-tail)) to something like
(setq pc (- (caar bytes-tail) dropped-bytes)).

I hope that makes sense to you,


        Stefan



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-06-28  3:01 Inefficiency in Bgotoifnil byte-code instruction John Wiegley
  2012-06-28  4:38 ` Stefan Monnier
@ 2012-06-28  4:41 ` Stefan Monnier
  2012-06-28 13:07 ` Richard Stallman
  2 siblings, 0 replies; 23+ messages in thread
From: Stefan Monnier @ 2012-06-28  4:41 UTC (permalink / raw)
  To: emacs-devel

> I've been doing some byte-code profiling, and have discovered that the
> opcodes Bgotoifnil and Bgotoifnonnil are high up, in the top ten:

BTW, I do not regard the performance of dynamically-scoped code as worth
optimizing nowadays.  So, when you do such profiling, make sure you do
it for code that was compiled with lexical-binding (it shouldn't make
much difference w.r.t Bgotoifnil, tho).


        Stefan




^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-06-28  3:01 Inefficiency in Bgotoifnil byte-code instruction John Wiegley
  2012-06-28  4:38 ` Stefan Monnier
  2012-06-28  4:41 ` Stefan Monnier
@ 2012-06-28 13:07 ` Richard Stallman
  2012-06-28 21:24   ` John Wiegley
  2 siblings, 1 reply; 23+ messages in thread
From: Richard Stallman @ 2012-06-28 13:07 UTC (permalink / raw)
  To: John Wiegley; +Cc: emacs-devel

    My recommendation is that we split these into two ops: Bgotoifnil and
    Bgotoifnonnil, which use only FETCH and a positive/negative offset from the
    current pc, and Bgotoifnil2 and Bgotoifnonnil2 which use the old logic
    (FETCH2, and an absolute offset from the start of the bytecode stream).

This incompatibility might be more trouble than it is worth.

--
Dr Richard Stallman
President, Free Software Foundation
51 Franklin St
Boston MA 02110
USA
www.fsf.org  www.gnu.org
Skype: No way! That's nonfree (freedom-denying) software.
  Use Ekiga or an ordinary phone call



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-06-28 13:07 ` Richard Stallman
@ 2012-06-28 21:24   ` John Wiegley
  0 siblings, 0 replies; 23+ messages in thread
From: John Wiegley @ 2012-06-28 21:24 UTC (permalink / raw)
  To: emacs-devel

>>>>> Richard Stallman <rms@gnu.org> writes:

>     My recommendation is that we split these into two ops: Bgotoifnil and
>     Bgotoifnonnil, which use only FETCH and a positive/negative offset from the
>     current pc, and Bgotoifnil2 and Bgotoifnonnil2 which use the old logic
>     (FETCH2, and an absolute offset from the start of the bytecode stream).

> This incompatibility might be more trouble than it is worth.

I hadn't considered that, and I think you're exactly right.

John



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-06-28  4:38 ` Stefan Monnier
@ 2012-06-29 16:39   ` Tom Tromey
  2012-06-30  4:07     ` Stefan Monnier
  2012-07-02  3:51     ` John Wiegley
  0 siblings, 2 replies; 23+ messages in thread
From: Tom Tromey @ 2012-06-29 16:39 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Stefan> Of course, when it comes to performance, intuition is often wrong, so
Stefan> feel free to try it out.

Maybe it is finally time to push in the token-threading patch.

Tom



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-06-29 16:39   ` Tom Tromey
@ 2012-06-30  4:07     ` Stefan Monnier
  2012-07-02 19:17       ` Tom Tromey
  2012-07-02  3:51     ` John Wiegley
  1 sibling, 1 reply; 23+ messages in thread
From: Stefan Monnier @ 2012-06-30  4:07 UTC (permalink / raw)
  To: Tom Tromey; +Cc: emacs-devel

Stefan> Of course, when it comes to performance, intuition is often wrong, so
Stefan> feel free to try it out.
> Maybe it is finally time to push in the token-threading patch.

Could be, yes.  Could you re-post the URL to the latest/cleanest version
of it?


        Stefan



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-06-29 16:39   ` Tom Tromey
  2012-06-30  4:07     ` Stefan Monnier
@ 2012-07-02  3:51     ` John Wiegley
  2012-07-02 14:08       ` Tom Tromey
  1 sibling, 1 reply; 23+ messages in thread
From: John Wiegley @ 2012-07-02  3:51 UTC (permalink / raw)
  To: emacs-devel

>>>>> Tom Tromey <tromey@redhat.com> writes:

> Maybe it is finally time to push in the token-threading patch.

What does that do?

John



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-07-02  3:51     ` John Wiegley
@ 2012-07-02 14:08       ` Tom Tromey
  0 siblings, 0 replies; 23+ messages in thread
From: Tom Tromey @ 2012-07-02 14:08 UTC (permalink / raw)
  To: emacs-devel

Tom> Maybe it is finally time to push in the token-threading patch.

John> What does that do?

It is a performance optimization that changes the emacs bytecode
interpreter to use token threading:

    http://en.wikipedia.org/wiki/Threaded_code#Token_threading

The old patch is here, I haven't updated it yet:

    http://debbugs.gnu.org/cgi/bugreport.cgi?bug=4470

Tom



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-06-30  4:07     ` Stefan Monnier
@ 2012-07-02 19:17       ` Tom Tromey
  2012-07-03 13:19         ` Stefan Monnier
  0 siblings, 1 reply; 23+ messages in thread
From: Tom Tromey @ 2012-07-02 19:17 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Stefan> Could be, yes.  Could you re-post the URL to the latest/cleanest version
Stefan> of it?

I pulled it from the bug:

    http://debbugs.gnu.org/cgi/bugreport.cgi?bug=4470

(btw, gnus-read-ephemeral-emacs-bug-group is awesome) and rebased it.
The appended bootstraps for me.

No ChangeLog, but I can write one if you are still interested in the
patch.

I didn't do any performance testing this time around.


FWIW I suspect a bigger win would be had by applying this treatment --
or even better, a full threaded interpreter -- to the regexp matcher.
Back when I did some profiling of Emacs, the regexp matcher was higher
in my profiles than the bytecode interpreter.

However, this fact doesn't make this patch less desirable.

Tom

diff --git a/src/bytecode.c b/src/bytecode.c
index 08a02ea..0fe4456 100644
--- a/src/bytecode.c
+++ b/src/bytecode.c
@@ -54,6 +54,13 @@ by Hallvard:
 /* #define BYTE_CODE_SAFE */
 /* #define BYTE_CODE_METER */
 
+/* If BYTE_CODE_THREADED is defined, then the interpreter will be
+   indirect threaded, using GCC's computed goto extension.  This is
+   incompatible with BYTE_CODE_SAFE and BYTE_CODE_METER.  */
+#if defined (__GNUC__) && !defined (BYTE_CODE_SAFE) && !defined (BYTE_CODE_METER)
+#define BYTE_CODE_THREADED
+#endif
+
 \f
 #ifdef BYTE_CODE_METER
 
@@ -84,11 +91,53 @@ Lisp_Object Qbytecode;
 /*  Byte codes: */
 
 #define Bstack_ref 0 /* Actually, Bstack_ref+0 is not implemented: use dup.  */
+#define Bstack_ref1 1
+#define Bstack_ref2 2
+#define Bstack_ref3 3
+#define Bstack_ref4 4
+#define Bstack_ref5 5
+#define Bstack_ref6 6
+#define Bstack_ref7 7
 #define Bvarref 010
+#define Bvarref1 011
+#define Bvarref2 012
+#define Bvarref3 013
+#define Bvarref4 014
+#define Bvarref5 015
+#define Bvarref6 016
+#define Bvarref7 017
 #define Bvarset 020
+#define Bvarset1 021
+#define Bvarset2 022
+#define Bvarset3 023
+#define Bvarset4 024
+#define Bvarset5 025
+#define Bvarset6 026
+#define Bvarset7 027
 #define Bvarbind 030
+#define Bvarbind1 031
+#define Bvarbind2 032
+#define Bvarbind3 033
+#define Bvarbind4 034
+#define Bvarbind5 035
+#define Bvarbind6 036
+#define Bvarbind7 037
 #define Bcall 040
+#define Bcall1 041
+#define Bcall2 042
+#define Bcall3 043
+#define Bcall4 044
+#define Bcall5 045
+#define Bcall6 046
+#define Bcall7 047
 #define Bunbind 050
+#define Bunbind1 051
+#define Bunbind2 052
+#define Bunbind3 053
+#define Bunbind4 054
+#define Bunbind5 055
+#define Bunbind6 056
+#define Bunbind7 057
 
 #define Bnth 070
 #define Bsymbolp 071
@@ -561,27 +610,249 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
       this_op = op = FETCH;
       METER_CODE (prev_op, op);
 #else
+#ifndef BYTE_CODE_THREADED
       op = FETCH;
 #endif
+#endif
+
+      /* The interpreter can be compiled one of two ways: as an
+	 ordinary switch-based interpreter, or as a threaded
+	 interpreter.  The threaded interpreter relies on GCC's
+	 computed goto extension, so it is not available everywhere.
+	 Threading provides a performance boost.  These macros are how
+	 we allow the code to be compiled both ways.  */
+#ifdef BYTE_CODE_THREADED
+      /* The CASE macro introduces an instruction's body.  It is
+	 either a label or a case label.  */
+#define CASE(OP) insn_ ## OP
+      /* NEXT is invoked at the end of an instruction to go to the
+	 next instruction.  It is either a computed goto, or a
+	 plain break.  */
+#define NEXT goto *(targets[op = FETCH])
+      /* FIRST is like NEXT, but is only used at the start of the
+	 interpreter body.  In the switch-based interpreter it is the
+	 switch, so the threaded definition must include a semicolon.  */
+#define FIRST NEXT;
+      /* Most cases are labeled with the CASE macro, above.
+	 CASE_DEFAULT is one exception; it is used if the interpreter
+	 being built requires a default case.  The threaded
+	 interpreter does not, because the dispatch table is
+	 completely filled.  */
+#define CASE_DEFAULT
+      /* This introduces an instruction that is known to call abort.  */
+#define CASE_ABORT CASE (default)
+#else
+      /* See above for the meaning of the various defines.  */
+#define CASE(OP) case OP
+#define NEXT break
+#define FIRST switch (op)
+#define CASE_DEFAULT case 255: default:
+#define CASE_ABORT case 0
+#endif
+
+#ifdef BYTE_CODE_THREADED
+
+      /* A convenience define that saves us a lot of typing and makes
+	 the table clearer.  */
+#define LABEL(OP) [OP] = &&insn_ ## OP
+
+      /* This is the dispatch table for the threaded interpreter.  */
+      static const void *const targets[256] =
+	{
+	  [0 ... (Bconstant - 1)] = &&insn_default,
+	  [Bconstant ... 255] = &&insn_Bconstant,
+
+	  LABEL (Bstack_ref1),
+	  LABEL (Bstack_ref2),
+	  LABEL (Bstack_ref3),
+	  LABEL (Bstack_ref4),
+	  LABEL (Bstack_ref5),
+	  LABEL (Bstack_ref6),
+	  LABEL (Bstack_ref7),
+	  LABEL (Bvarref),
+	  LABEL (Bvarref1),
+	  LABEL (Bvarref2),
+	  LABEL (Bvarref3),
+	  LABEL (Bvarref4),
+	  LABEL (Bvarref5),
+	  LABEL (Bvarref6),
+	  LABEL (Bvarref7),
+	  LABEL (Bvarset),
+	  LABEL (Bvarset1),
+	  LABEL (Bvarset2),
+	  LABEL (Bvarset3),
+	  LABEL (Bvarset4),
+	  LABEL (Bvarset5),
+	  LABEL (Bvarset6),
+	  LABEL (Bvarset7),
+	  LABEL (Bvarbind),
+	  LABEL (Bvarbind1),
+	  LABEL (Bvarbind2),
+	  LABEL (Bvarbind3),
+	  LABEL (Bvarbind4),
+	  LABEL (Bvarbind5),
+	  LABEL (Bvarbind6),
+	  LABEL (Bvarbind7),
+	  LABEL (Bcall),
+	  LABEL (Bcall1),
+	  LABEL (Bcall2),
+	  LABEL (Bcall3),
+	  LABEL (Bcall4),
+	  LABEL (Bcall5),
+	  LABEL (Bcall6),
+	  LABEL (Bcall7),
+	  LABEL (Bunbind),
+	  LABEL (Bunbind1),
+	  LABEL (Bunbind2),
+	  LABEL (Bunbind3),
+	  LABEL (Bunbind4),
+	  LABEL (Bunbind5),
+	  LABEL (Bunbind6),
+	  LABEL (Bunbind7),
+	  LABEL (Bnth),
+	  LABEL (Bsymbolp),
+	  LABEL (Bconsp),
+	  LABEL (Bstringp),
+	  LABEL (Blistp),
+	  LABEL (Beq),
+	  LABEL (Bmemq),
+	  LABEL (Bnot),
+	  LABEL (Bcar),
+	  LABEL (Bcdr),
+	  LABEL (Bcons),
+	  LABEL (Blist1),
+	  LABEL (Blist2),
+	  LABEL (Blist3),
+	  LABEL (Blist4),
+	  LABEL (Blength),
+	  LABEL (Baref),
+	  LABEL (Baset),
+	  LABEL (Bsymbol_value),
+	  LABEL (Bsymbol_function),
+	  LABEL (Bset),
+	  LABEL (Bfset),
+	  LABEL (Bget),
+	  LABEL (Bsubstring),
+	  LABEL (Bconcat2),
+	  LABEL (Bconcat3),
+	  LABEL (Bconcat4),
+	  LABEL (Bsub1),
+	  LABEL (Badd1),
+	  LABEL (Beqlsign),
+	  LABEL (Bgtr),
+	  LABEL (Blss),
+	  LABEL (Bleq),
+	  LABEL (Bgeq),
+	  LABEL (Bdiff),
+	  LABEL (Bnegate),
+	  LABEL (Bplus),
+	  LABEL (Bmax),
+	  LABEL (Bmin),
+	  LABEL (Bmult),
+	  LABEL (Bpoint),
+	  LABEL (Bsave_current_buffer),
+	  LABEL (Bgoto_char),
+	  LABEL (Binsert),
+	  LABEL (Bpoint_max),
+	  LABEL (Bpoint_min),
+	  LABEL (Bchar_after),
+	  LABEL (Bfollowing_char),
+	  LABEL (Bpreceding_char),
+	  LABEL (Bcurrent_column),
+	  LABEL (Bindent_to),
+	  LABEL (Beolp),
+	  LABEL (Beobp),
+	  LABEL (Bbolp),
+	  LABEL (Bbobp),
+	  LABEL (Bcurrent_buffer),
+	  LABEL (Bset_buffer),
+	  LABEL (Bsave_current_buffer_1),
+	  LABEL (Binteractive_p),
+	  LABEL (Bforward_char),
+	  LABEL (Bforward_word),
+	  LABEL (Bskip_chars_forward),
+	  LABEL (Bskip_chars_backward),
+	  LABEL (Bforward_line),
+	  LABEL (Bchar_syntax),
+	  LABEL (Bbuffer_substring),
+	  LABEL (Bdelete_region),
+	  LABEL (Bnarrow_to_region),
+	  LABEL (Bwiden),
+	  LABEL (Bend_of_line),
+	  LABEL (Bconstant2),
+	  LABEL (Bgoto),
+	  LABEL (Bgotoifnil),
+	  LABEL (Bgotoifnonnil),
+	  LABEL (Bgotoifnilelsepop),
+	  LABEL (Bgotoifnonnilelsepop),
+	  LABEL (Breturn),
+	  LABEL (Bdiscard),
+	  LABEL (Bdup),
+	  LABEL (Bsave_excursion),
+	  LABEL (Bsave_window_excursion),
+	  LABEL (Bsave_restriction),
+	  LABEL (Bcatch),
+	  LABEL (Bunwind_protect),
+	  LABEL (Bcondition_case),
+	  LABEL (Btemp_output_buffer_setup),
+	  LABEL (Btemp_output_buffer_show),
+	  LABEL (Bunbind_all),
+	  LABEL (Bset_marker),
+	  LABEL (Bmatch_beginning),
+	  LABEL (Bmatch_end),
+	  LABEL (Bupcase),
+	  LABEL (Bdowncase),
+	  LABEL (Bstringeqlsign),
+	  LABEL (Bstringlss),
+	  LABEL (Bequal),
+	  LABEL (Bnthcdr),
+	  LABEL (Belt),
+	  LABEL (Bmember),
+	  LABEL (Bassq),
+	  LABEL (Bnreverse),
+	  LABEL (Bsetcar),
+	  LABEL (Bsetcdr),
+	  LABEL (Bcar_safe),
+	  LABEL (Bcdr_safe),
+	  LABEL (Bnconc),
+	  LABEL (Bquo),
+	  LABEL (Brem),
+	  LABEL (Bnumberp),
+	  LABEL (Bintegerp),
+	  LABEL (BRgoto),
+	  LABEL (BRgotoifnil),
+	  LABEL (BRgotoifnonnil),
+	  LABEL (BRgotoifnilelsepop),
+	  LABEL (BRgotoifnonnilelsepop),
+	  LABEL (BlistN),
+	  LABEL (BconcatN),
+	  LABEL (BinsertN),
+	  LABEL (Bstack_set),
+	  LABEL (Bstack_set2),
+	  LABEL (BdiscardN),
+	  LABEL (Bconstant)
+	};
+#endif
+
 
-      switch (op)
+      FIRST
 	{
-	case Bvarref + 7:
+	CASE (Bvarref7):
 	  op = FETCH2;
 	  goto varref;
 
-	case Bvarref:
-	case Bvarref + 1:
-	case Bvarref + 2:
-	case Bvarref + 3:
-	case Bvarref + 4:
-	case Bvarref + 5:
+	CASE (Bvarref):
+	CASE (Bvarref1):
+	CASE (Bvarref2):
+	CASE (Bvarref3):
+	CASE (Bvarref4):
+	CASE (Bvarref5):
 	  op = op - Bvarref;
 	  goto varref;
 
 	/* This seems to be the most frequently executed byte-code
 	   among the Bvarref's, so avoid a goto here.  */
-	case Bvarref+6:
+	CASE (Bvarref6):
 	  op = FETCH;
 	varref:
 	  {
@@ -606,10 +877,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		AFTER_POTENTIAL_GC ();
 	      }
 	    PUSH (v2);
-	    break;
+	    NEXT;
 	  }
 
-	case Bgotoifnil:
+	CASE (Bgotoifnil):
 	  {
 	    Lisp_Object v1;
 	    MAYBE_GC ();
@@ -621,10 +892,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		CHECK_RANGE (op);
 		stack.pc = stack.byte_string_start + op;
 	      }
-	    break;
+	    NEXT;
 	  }
 
-	case Bcar:
+	CASE (Bcar):
 	  {
 	    Lisp_Object v1;
 	    v1 = TOP;
@@ -638,28 +909,28 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		wrong_type_argument (Qlistp, v1);
 		AFTER_POTENTIAL_GC ();
 	      }
-	    break;
+	    NEXT;
 	  }
 
-	case Beq:
+	CASE (Beq):
 	  {
 	    Lisp_Object v1;
 	    v1 = POP;
 	    TOP = EQ (v1, TOP) ? Qt : Qnil;
-	    break;
+	    NEXT;
 	  }
 
-	case Bmemq:
+	CASE (Bmemq):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fmemq (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bcdr:
+	CASE (Bcdr):
 	  {
 	    Lisp_Object v1;
 	    v1 = TOP;
@@ -673,24 +944,23 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		wrong_type_argument (Qlistp, v1);
 		AFTER_POTENTIAL_GC ();
 	      }
-	    break;
-	    break;
+	    NEXT;
 	  }
 
-	case Bvarset:
-	case Bvarset+1:
-	case Bvarset+2:
-	case Bvarset+3:
-	case Bvarset+4:
-	case Bvarset+5:
+	CASE (Bvarset):
+	CASE (Bvarset1):
+	CASE (Bvarset2):
+	CASE (Bvarset3):
+	CASE (Bvarset4):
+	CASE (Bvarset5):
 	  op -= Bvarset;
 	  goto varset;
 
-	case Bvarset+7:
+	CASE (Bvarset7):
 	  op = FETCH2;
 	  goto varset;
 
-	case Bvarset+6:
+	CASE (Bvarset6):
 	  op = FETCH;
 	varset:
 	  {
@@ -713,54 +983,54 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	      }
 	  }
 	  (void) POP;
-	  break;
+	  NEXT;
 
-	case Bdup:
+	CASE (Bdup):
 	  {
 	    Lisp_Object v1;
 	    v1 = TOP;
 	    PUSH (v1);
-	    break;
+	    NEXT;
 	  }
 
 	/* ------------------ */
 
-	case Bvarbind+6:
+	CASE (Bvarbind6):
 	  op = FETCH;
 	  goto varbind;
 
-	case Bvarbind+7:
+	CASE (Bvarbind7):
 	  op = FETCH2;
 	  goto varbind;
 
-	case Bvarbind:
-	case Bvarbind+1:
-	case Bvarbind+2:
-	case Bvarbind+3:
-	case Bvarbind+4:
-	case Bvarbind+5:
+	CASE (Bvarbind):
+	CASE (Bvarbind1):
+	CASE (Bvarbind2):
+	CASE (Bvarbind3):
+	CASE (Bvarbind4):
+	CASE (Bvarbind5):
 	  op -= Bvarbind;
 	varbind:
 	  /* Specbind can signal and thus GC.  */
 	  BEFORE_POTENTIAL_GC ();
 	  specbind (vectorp[op], POP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bcall+6:
+	CASE (Bcall6):
 	  op = FETCH;
 	  goto docall;
 
-	case Bcall+7:
+	CASE (Bcall7):
 	  op = FETCH2;
 	  goto docall;
 
-	case Bcall:
-	case Bcall+1:
-	case Bcall+2:
-	case Bcall+3:
-	case Bcall+4:
-	case Bcall+5:
+	CASE (Bcall):
+	CASE (Bcall1):
+	CASE (Bcall2):
+	CASE (Bcall3):
+	CASE (Bcall4):
+	CASE (Bcall5):
 	  op -= Bcall;
 	docall:
 	  {
@@ -783,47 +1053,47 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 #endif
 	    TOP = Ffuncall (op + 1, &TOP);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bunbind+6:
+	CASE (Bunbind6):
 	  op = FETCH;
 	  goto dounbind;
 
-	case Bunbind+7:
+	CASE (Bunbind7):
 	  op = FETCH2;
 	  goto dounbind;
 
-	case Bunbind:
-	case Bunbind+1:
-	case Bunbind+2:
-	case Bunbind+3:
-	case Bunbind+4:
-	case Bunbind+5:
+	CASE (Bunbind):
+	CASE (Bunbind1):
+	CASE (Bunbind2):
+	CASE (Bunbind3):
+	CASE (Bunbind4):
+	CASE (Bunbind5):
 	  op -= Bunbind;
 	dounbind:
 	  BEFORE_POTENTIAL_GC ();
 	  unbind_to (SPECPDL_INDEX () - op, Qnil);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bunbind_all:	/* Obsolete.  Never used.  */
+	CASE (Bunbind_all):	/* Obsolete.  Never used.  */
 	  /* To unbind back to the beginning of this frame.  Not used yet,
 	     but will be needed for tail-recursion elimination.  */
 	  BEFORE_POTENTIAL_GC ();
 	  unbind_to (count, Qnil);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bgoto:
+	CASE (Bgoto):
 	  MAYBE_GC ();
 	  BYTE_CODE_QUIT;
 	  op = FETCH2;    /* pc = FETCH2 loses since FETCH2 contains pc++ */
 	  CHECK_RANGE (op);
 	  stack.pc = stack.byte_string_start + op;
-	  break;
+	  NEXT;
 
-	case Bgotoifnonnil:
+	CASE (Bgotoifnonnil):
 	  {
 	    Lisp_Object v1;
 	    MAYBE_GC ();
@@ -835,10 +1105,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		CHECK_RANGE (op);
 		stack.pc = stack.byte_string_start + op;
 	      }
-	    break;
+	    NEXT;
 	  }
 
-	case Bgotoifnilelsepop:
+	CASE (Bgotoifnilelsepop):
 	  MAYBE_GC ();
 	  op = FETCH2;
 	  if (NILP (TOP))
@@ -848,9 +1118,9 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	      stack.pc = stack.byte_string_start + op;
 	    }
 	  else DISCARD (1);
-	  break;
+	  NEXT;
 
-	case Bgotoifnonnilelsepop:
+	CASE (Bgotoifnonnilelsepop):
 	  MAYBE_GC ();
 	  op = FETCH2;
 	  if (!NILP (TOP))
@@ -860,15 +1130,15 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	      stack.pc = stack.byte_string_start + op;
 	    }
 	  else DISCARD (1);
-	  break;
+	  NEXT;
 
-	case BRgoto:
+	CASE (BRgoto):
 	  MAYBE_GC ();
 	  BYTE_CODE_QUIT;
 	  stack.pc += (int) *stack.pc - 127;
-	  break;
+	  NEXT;
 
-	case BRgotoifnil:
+	CASE (BRgotoifnil):
 	  {
 	    Lisp_Object v1;
 	    MAYBE_GC ();
@@ -879,10 +1149,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		stack.pc += (int) *stack.pc - 128;
 	      }
 	    stack.pc++;
-	    break;
+	    NEXT;
 	  }
 
-	case BRgotoifnonnil:
+	CASE (BRgotoifnonnil):
 	  {
 	    Lisp_Object v1;
 	    MAYBE_GC ();
@@ -893,10 +1163,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		stack.pc += (int) *stack.pc - 128;
 	      }
 	    stack.pc++;
-	    break;
+	    NEXT;
 	  }
 
-	case BRgotoifnilelsepop:
+	CASE (BRgotoifnilelsepop):
 	  MAYBE_GC ();
 	  op = *stack.pc++;
 	  if (NILP (TOP))
@@ -905,9 +1175,9 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	      stack.pc += op - 128;
 	    }
 	  else DISCARD (1);
-	  break;
+	  NEXT;
 
-	case BRgotoifnonnilelsepop:
+	CASE (BRgotoifnonnilelsepop):
 	  MAYBE_GC ();
 	  op = *stack.pc++;
 	  if (!NILP (TOP))
@@ -916,31 +1186,31 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	      stack.pc += op - 128;
 	    }
 	  else DISCARD (1);
-	  break;
+	  NEXT;
 
-	case Breturn:
+	CASE (Breturn):
 	  result = POP;
 	  goto exit;
 
-	case Bdiscard:
+	CASE (Bdiscard):
 	  DISCARD (1);
-	  break;
+	  NEXT;
 
-	case Bconstant2:
+	CASE (Bconstant2):
 	  PUSH (vectorp[FETCH2]);
-	  break;
+	  NEXT;
 
-	case Bsave_excursion:
+	CASE (Bsave_excursion):
 	  record_unwind_protect (save_excursion_restore,
 				 save_excursion_save ());
-	  break;
+	  NEXT;
 
-	case Bsave_current_buffer: /* Obsolete since ??.  */
-	case Bsave_current_buffer_1:
+	CASE (Bsave_current_buffer): /* Obsolete since ??.  */
+	CASE (Bsave_current_buffer_1):
 	  record_unwind_protect (set_buffer_if_live, Fcurrent_buffer ());
-	  break;
+	  NEXT;
 
-	case Bsave_window_excursion: /* Obsolete since 24.1.  */
+	CASE (Bsave_window_excursion): /* Obsolete since 24.1.  */
 	  {
 	    register ptrdiff_t count1 = SPECPDL_INDEX ();
 	    record_unwind_protect (Fset_window_configuration,
@@ -949,29 +1219,29 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	    TOP = Fprogn (TOP);
 	    unbind_to (count1, TOP);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bsave_restriction:
+	CASE (Bsave_restriction):
 	  record_unwind_protect (save_restriction_restore,
 				 save_restriction_save ());
-	  break;
+	  NEXT;
 
-	case Bcatch:		/* FIXME: ill-suited for lexbind.  */
+	CASE (Bcatch):		/* FIXME: ill-suited for lexbind.  */
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = internal_catch (TOP, eval_sub, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bunwind_protect:	/* FIXME: avoid closure for lexbind.  */
+	CASE (Bunwind_protect):	/* FIXME: avoid closure for lexbind.  */
 	  record_unwind_protect (Fprogn, POP);
-	  break;
+	  NEXT;
 
-	case Bcondition_case:	/* FIXME: ill-suited for lexbind.  */
+	CASE (Bcondition_case):	/* FIXME: ill-suited for lexbind.  */
 	  {
 	    Lisp_Object handlers, body;
 	    handlers = POP;
@@ -979,18 +1249,18 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	    BEFORE_POTENTIAL_GC ();
 	    TOP = internal_lisp_condition_case (TOP, body, handlers);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Btemp_output_buffer_setup: /* Obsolete since 24.1.  */
+	CASE (Btemp_output_buffer_setup): /* Obsolete since 24.1.  */
 	  BEFORE_POTENTIAL_GC ();
 	  CHECK_STRING (TOP);
 	  temp_output_buffer_setup (SSDATA (TOP));
 	  AFTER_POTENTIAL_GC ();
 	  TOP = Vstandard_output;
-	  break;
+	  NEXT;
 
-	case Btemp_output_buffer_show: /* Obsolete since 24.1.  */
+	CASE (Btemp_output_buffer_show): /* Obsolete since 24.1.  */
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
@@ -1000,10 +1270,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	    /* pop binding of standard-output */
 	    unbind_to (SPECPDL_INDEX () - 1, Qnil);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bnth:
+	CASE (Bnth):
 	  {
 	    Lisp_Object v1, v2;
 	    EMACS_INT n;
@@ -1018,173 +1288,173 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	    immediate_quit = 0;
 	    TOP = CAR (v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bsymbolp:
+	CASE (Bsymbolp):
 	  TOP = SYMBOLP (TOP) ? Qt : Qnil;
-	  break;
+	  NEXT;
 
-	case Bconsp:
+	CASE (Bconsp):
 	  TOP = CONSP (TOP) ? Qt : Qnil;
-	  break;
+	  NEXT;
 
-	case Bstringp:
+	CASE (Bstringp):
 	  TOP = STRINGP (TOP) ? Qt : Qnil;
-	  break;
+	  NEXT;
 
-	case Blistp:
+	CASE (Blistp):
 	  TOP = CONSP (TOP) || NILP (TOP) ? Qt : Qnil;
-	  break;
+	  NEXT;
 
-	case Bnot:
+	CASE (Bnot):
 	  TOP = NILP (TOP) ? Qt : Qnil;
-	  break;
+	  NEXT;
 
-	case Bcons:
+	CASE (Bcons):
 	  {
 	    Lisp_Object v1;
 	    v1 = POP;
 	    TOP = Fcons (TOP, v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Blist1:
+	CASE (Blist1):
 	  TOP = Fcons (TOP, Qnil);
-	  break;
+	  NEXT;
 
-	case Blist2:
+	CASE (Blist2):
 	  {
 	    Lisp_Object v1;
 	    v1 = POP;
 	    TOP = Fcons (TOP, Fcons (v1, Qnil));
-	    break;
+	    NEXT;
 	  }
 
-	case Blist3:
+	CASE (Blist3):
 	  DISCARD (2);
 	  TOP = Flist (3, &TOP);
-	  break;
+	  NEXT;
 
-	case Blist4:
+	CASE (Blist4):
 	  DISCARD (3);
 	  TOP = Flist (4, &TOP);
-	  break;
+	  NEXT;
 
-	case BlistN:
+	CASE (BlistN):
 	  op = FETCH;
 	  DISCARD (op - 1);
 	  TOP = Flist (op, &TOP);
-	  break;
+	  NEXT;
 
-	case Blength:
+	CASE (Blength):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Flength (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Baref:
+	CASE (Baref):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Faref (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Baset:
+	CASE (Baset):
 	  {
 	    Lisp_Object v1, v2;
 	    BEFORE_POTENTIAL_GC ();
 	    v2 = POP; v1 = POP;
 	    TOP = Faset (TOP, v1, v2);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bsymbol_value:
+	CASE (Bsymbol_value):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fsymbol_value (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bsymbol_function:
+	CASE (Bsymbol_function):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fsymbol_function (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bset:
+	CASE (Bset):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fset (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bfset:
+	CASE (Bfset):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Ffset (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bget:
+	CASE (Bget):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fget (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bsubstring:
+	CASE (Bsubstring):
 	  {
 	    Lisp_Object v1, v2;
 	    BEFORE_POTENTIAL_GC ();
 	    v2 = POP; v1 = POP;
 	    TOP = Fsubstring (TOP, v1, v2);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bconcat2:
+	CASE (Bconcat2):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (1);
 	  TOP = Fconcat (2, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bconcat3:
+	CASE (Bconcat3):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (2);
 	  TOP = Fconcat (3, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bconcat4:
+	CASE (Bconcat4):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (3);
 	  TOP = Fconcat (4, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case BconcatN:
+	CASE (BconcatN):
 	  op = FETCH;
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (op - 1);
 	  TOP = Fconcat (op, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bsub1:
+	CASE (Bsub1):
 	  {
 	    Lisp_Object v1;
 	    v1 = TOP;
@@ -1199,10 +1469,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		TOP = Fsub1 (v1);
 		AFTER_POTENTIAL_GC ();
 	      }
-	    break;
+	    NEXT;
 	  }
 
-	case Badd1:
+	CASE (Badd1):
 	  {
 	    Lisp_Object v1;
 	    v1 = TOP;
@@ -1217,10 +1487,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		TOP = Fadd1 (v1);
 		AFTER_POTENTIAL_GC ();
 	      }
-	    break;
+	    NEXT;
 	  }
 
-	case Beqlsign:
+	CASE (Beqlsign):
 	  {
 	    Lisp_Object v1, v2;
 	    BEFORE_POTENTIAL_GC ();
@@ -1238,57 +1508,57 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	      }
 	    else
 	      TOP = (XINT (v1) == XINT (v2) ? Qt : Qnil);
-	    break;
+	    NEXT;
 	  }
 
-	case Bgtr:
+	CASE (Bgtr):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fgtr (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Blss:
+	CASE (Blss):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Flss (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bleq:
+	CASE (Bleq):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fleq (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bgeq:
+	CASE (Bgeq):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fgeq (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bdiff:
+	CASE (Bdiff):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (1);
 	  TOP = Fminus (2, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bnegate:
+	CASE (Bnegate):
 	  {
 	    Lisp_Object v1;
 	    v1 = TOP;
@@ -1303,209 +1573,209 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		TOP = Fminus (1, &TOP);
 		AFTER_POTENTIAL_GC ();
 	      }
-	    break;
+	    NEXT;
 	  }
 
-	case Bplus:
+	CASE (Bplus):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (1);
 	  TOP = Fplus (2, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bmax:
+	CASE (Bmax):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (1);
 	  TOP = Fmax (2, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bmin:
+	CASE (Bmin):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (1);
 	  TOP = Fmin (2, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bmult:
+	CASE (Bmult):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (1);
 	  TOP = Ftimes (2, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bquo:
+	CASE (Bquo):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (1);
 	  TOP = Fquo (2, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Brem:
+	CASE (Brem):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Frem (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bpoint:
+	CASE (Bpoint):
 	  {
 	    Lisp_Object v1;
 	    XSETFASTINT (v1, PT);
 	    PUSH (v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Bgoto_char:
+	CASE (Bgoto_char):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fgoto_char (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Binsert:
+	CASE (Binsert):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Finsert (1, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case BinsertN:
+	CASE (BinsertN):
 	  op = FETCH;
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (op - 1);
 	  TOP = Finsert (op, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bpoint_max:
+	CASE (Bpoint_max):
 	  {
 	    Lisp_Object v1;
 	    XSETFASTINT (v1, ZV);
 	    PUSH (v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Bpoint_min:
+	CASE (Bpoint_min):
 	  {
 	    Lisp_Object v1;
 	    XSETFASTINT (v1, BEGV);
 	    PUSH (v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Bchar_after:
+	CASE (Bchar_after):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fchar_after (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bfollowing_char:
+	CASE (Bfollowing_char):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = Ffollowing_char ();
 	    AFTER_POTENTIAL_GC ();
 	    PUSH (v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Bpreceding_char:
+	CASE (Bpreceding_char):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = Fprevious_char ();
 	    AFTER_POTENTIAL_GC ();
 	    PUSH (v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Bcurrent_column:
+	CASE (Bcurrent_column):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    XSETFASTINT (v1, current_column ());
 	    AFTER_POTENTIAL_GC ();
 	    PUSH (v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Bindent_to:
+	CASE (Bindent_to):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Findent_to (TOP, Qnil);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Beolp:
+	CASE (Beolp):
 	  PUSH (Feolp ());
-	  break;
+	  NEXT;
 
-	case Beobp:
+	CASE (Beobp):
 	  PUSH (Feobp ());
-	  break;
+	  NEXT;
 
-	case Bbolp:
+	CASE (Bbolp):
 	  PUSH (Fbolp ());
-	  break;
+	  NEXT;
 
-	case Bbobp:
+	CASE (Bbobp):
 	  PUSH (Fbobp ());
-	  break;
+	  NEXT;
 
-	case Bcurrent_buffer:
+	CASE (Bcurrent_buffer):
 	  PUSH (Fcurrent_buffer ());
-	  break;
+	  NEXT;
 
-	case Bset_buffer:
+	CASE (Bset_buffer):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fset_buffer (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Binteractive_p:	/* Obsolete since 24.1.  */
+	CASE (Binteractive_p):	/* Obsolete since 24.1.  */
 	  PUSH (Finteractive_p ());
-	  break;
+	  NEXT;
 
-	case Bforward_char:
+	CASE (Bforward_char):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fforward_char (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bforward_word:
+	CASE (Bforward_word):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fforward_word (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bskip_chars_forward:
+	CASE (Bskip_chars_forward):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fskip_chars_forward (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bskip_chars_backward:
+	CASE (Bskip_chars_backward):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fskip_chars_backward (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bforward_line:
+	CASE (Bforward_line):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fforward_line (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bchar_syntax:
+	CASE (Bchar_syntax):
 	  {
 	    int c;
 
@@ -1517,51 +1787,51 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	      MAKE_CHAR_MULTIBYTE (c);
 	    XSETFASTINT (TOP, syntax_code_spec[(int) SYNTAX (c)]);
 	  }
-	  break;
+	  NEXT;
 
-	case Bbuffer_substring:
+	CASE (Bbuffer_substring):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fbuffer_substring (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bdelete_region:
+	CASE (Bdelete_region):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fdelete_region (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bnarrow_to_region:
+	CASE (Bnarrow_to_region):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fnarrow_to_region (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bwiden:
+	CASE (Bwiden):
 	  BEFORE_POTENTIAL_GC ();
 	  PUSH (Fwiden ());
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bend_of_line:
+	CASE (Bend_of_line):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fend_of_line (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bset_marker:
+	CASE (Bset_marker):
 	  {
 	    Lisp_Object v1, v2;
 	    BEFORE_POTENTIAL_GC ();
@@ -1569,72 +1839,72 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	    v2 = POP;
 	    TOP = Fset_marker (TOP, v2, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bmatch_beginning:
+	CASE (Bmatch_beginning):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fmatch_beginning (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bmatch_end:
+	CASE (Bmatch_end):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fmatch_end (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bupcase:
+	CASE (Bupcase):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fupcase (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bdowncase:
+	CASE (Bdowncase):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fdowncase (TOP);
 	  AFTER_POTENTIAL_GC ();
-	break;
+	NEXT;
 
-	case Bstringeqlsign:
+      CASE (Bstringeqlsign):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fstring_equal (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bstringlss:
+	CASE (Bstringlss):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fstring_lessp (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bequal:
+	CASE (Bequal):
 	  {
 	    Lisp_Object v1;
 	    v1 = POP;
 	    TOP = Fequal (TOP, v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Bnthcdr:
+	CASE (Bnthcdr):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fnthcdr (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Belt:
+	CASE (Belt):
 	  {
 	    Lisp_Object v1, v2;
 	    if (CONSP (TOP))
@@ -1660,87 +1930,91 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		TOP = Felt (TOP, v1);
 		AFTER_POTENTIAL_GC ();
 	      }
-	    break;
+	    NEXT;
 	  }
 
-	case Bmember:
+	CASE (Bmember):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fmember (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bassq:
+	CASE (Bassq):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fassq (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bnreverse:
+	CASE (Bnreverse):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fnreverse (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bsetcar:
+	CASE (Bsetcar):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fsetcar (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bsetcdr:
+	CASE (Bsetcdr):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fsetcdr (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bcar_safe:
+	CASE (Bcar_safe):
 	  {
 	    Lisp_Object v1;
 	    v1 = TOP;
 	    TOP = CAR_SAFE (v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Bcdr_safe:
+	CASE (Bcdr_safe):
 	  {
 	    Lisp_Object v1;
 	    v1 = TOP;
 	    TOP = CDR_SAFE (v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Bnconc:
+	CASE (Bnconc):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (1);
 	  TOP = Fnconc (2, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bnumberp:
+	CASE (Bnumberp):
 	  TOP = (NUMBERP (TOP) ? Qt : Qnil);
-	  break;
+	  NEXT;
 
-	case Bintegerp:
+	CASE (Bintegerp):
 	  TOP = INTEGERP (TOP) ? Qt : Qnil;
-	  break;
+	  NEXT;
 
 #ifdef BYTE_CODE_SAFE
+	  /* These are intentionally written using 'case' syntax,
+	     because they are incompatible with the threaded
+	     interpreter.  */
+
 	case Bset_mark:
 	  BEFORE_POTENTIAL_GC ();
 	  error ("set-mark is an obsolete bytecode");
@@ -1753,49 +2027,49 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	  break;
 #endif
 
-	case 0:
+	CASE_ABORT:
 	  /* Actually this is Bstack_ref with offset 0, but we use Bdup
 	     for that instead.  */
-	  /* case Bstack_ref: */
+	  /* CASE (Bstack_ref): */
 	  abort ();
 
 	  /* Handy byte-codes for lexical binding.  */
-	case Bstack_ref+1:
-	case Bstack_ref+2:
-	case Bstack_ref+3:
-	case Bstack_ref+4:
-	case Bstack_ref+5:
+	CASE (Bstack_ref1):
+	CASE (Bstack_ref2):
+	CASE (Bstack_ref3):
+	CASE (Bstack_ref4):
+	CASE (Bstack_ref5):
 	  {
 	    Lisp_Object *ptr = top - (op - Bstack_ref);
 	    PUSH (*ptr);
-	    break;
+	    NEXT;
 	  }
-	case Bstack_ref+6:
+	CASE (Bstack_ref6):
 	  {
 	    Lisp_Object *ptr = top - (FETCH);
 	    PUSH (*ptr);
-	    break;
+	    NEXT;
 	  }
-	case Bstack_ref+7:
+	CASE (Bstack_ref7):
 	  {
 	    Lisp_Object *ptr = top - (FETCH2);
 	    PUSH (*ptr);
-	    break;
+	    NEXT;
 	  }
-	case Bstack_set:
+	CASE (Bstack_set):
 	  /* stack-set-0 = discard; stack-set-1 = discard-1-preserve-tos.  */
 	  {
 	    Lisp_Object *ptr = top - (FETCH);
 	    *ptr = POP;
-	    break;
+	    NEXT;
 	  }
-	case Bstack_set2:
+	CASE (Bstack_set2):
 	  {
 	    Lisp_Object *ptr = top - (FETCH2);
 	    *ptr = POP;
-	    break;
+	    NEXT;
 	  }
-	case BdiscardN:
+	CASE (BdiscardN):
 	  op = FETCH;
 	  if (op & 0x80)
 	    {
@@ -1803,10 +2077,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	      top[-op] = TOP;
 	    }
 	  DISCARD (op);
-	  break;
+	  NEXT;
 
-	case 255:
-	default:
+	CASE_DEFAULT
+	CASE (Bconstant):
 #ifdef BYTE_CODE_SAFE
 	  if (op < Bconstant)
 	    {
@@ -1820,6 +2094,7 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 #else
 	  PUSH (vectorp[op - Bconstant]);
 #endif
+	  NEXT;
 	}
     }
 



^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-07-02 19:17       ` Tom Tromey
@ 2012-07-03 13:19         ` Stefan Monnier
  2012-07-03 14:45           ` Tom Tromey
  2012-07-06 20:27           ` Tom Tromey
  0 siblings, 2 replies; 23+ messages in thread
From: Stefan Monnier @ 2012-07-03 13:19 UTC (permalink / raw)
  To: Tom Tromey; +Cc: emacs-devel

> +#define Bstack_ref1 1
[...]
> +	  LABEL (Bstack_ref1),
[...]

Can't we merge those two exhaustive lists (the first is only used in
the non-threaded case and the other only in the threaded case).
If we turn the first set of #defines into an enum, we should be able to
bring the syntax of the two declarations close enough that the
difference can be abstracted out into a few macros.

> FWIW I suspect a bigger win would be had by applying this treatment --
> or even better, a full threaded interpreter -- to the regexp matcher.
> Back when I did some profiling of Emacs, the regexp matcher was higher
> in my profiles than the bytecode interpreter.

There's a lot of room for improvement in the regexp-matcher as well,
yes.  If we could generate the code via something like vmgen it would be
even better.


        Stefan



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-07-03 13:19         ` Stefan Monnier
@ 2012-07-03 14:45           ` Tom Tromey
  2012-07-03 16:58             ` Stefan Monnier
  2012-07-03 19:52             ` John Wiegley
  2012-07-06 20:27           ` Tom Tromey
  1 sibling, 2 replies; 23+ messages in thread
From: Tom Tromey @ 2012-07-03 14:45 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Stefan> Can't we merge those two exhaustive lists (the first is only used in
Stefan> the non-threaded case and the other only in the threaded case).
Stefan> If we turn the first set of #defines into an enum, we should be able to
Stefan> bring the syntax of the two declarations close enough that the
Stefan> difference can be abstracted out into a few macros.

Sure, how about the appended?

I built it both ways (once by commenting out the BYTE_CODE_THREADED
define) on x86-64 Fedora 16.

Tom

diff --git a/src/bytecode.c b/src/bytecode.c
index 08a02ea..30ed6d0 100644
--- a/src/bytecode.c
+++ b/src/bytecode.c
@@ -54,6 +54,13 @@ by Hallvard:
 /* #define BYTE_CODE_SAFE */
 /* #define BYTE_CODE_METER */
 
+/* If BYTE_CODE_THREADED is defined, then the interpreter will be
+   indirect threaded, using GCC's computed goto extension.  This is
+   incompatible with BYTE_CODE_SAFE and BYTE_CODE_METER.  */
+#if defined (__GNUC__) && !defined (BYTE_CODE_SAFE) && !defined (BYTE_CODE_METER)
+#define BYTE_CODE_THREADED
+#endif
+
 \f
 #ifdef BYTE_CODE_METER
 
@@ -83,158 +90,204 @@ Lisp_Object Qbytecode;
 
 /*  Byte codes: */
 
-#define Bstack_ref 0 /* Actually, Bstack_ref+0 is not implemented: use dup.  */
-#define Bvarref 010
-#define Bvarset 020
-#define Bvarbind 030
-#define Bcall 040
-#define Bunbind 050
-
-#define Bnth 070
-#define Bsymbolp 071
-#define Bconsp 072
-#define Bstringp 073
-#define Blistp 074
-#define Beq 075
-#define Bmemq 076
-#define Bnot 077
-#define Bcar 0100
-#define Bcdr 0101
-#define Bcons 0102
-#define Blist1 0103
-#define Blist2 0104
-#define Blist3 0105
-#define Blist4 0106
-#define Blength 0107
-#define Baref 0110
-#define Baset 0111
-#define Bsymbol_value 0112
-#define Bsymbol_function 0113
-#define Bset 0114
-#define Bfset 0115
-#define Bget 0116
-#define Bsubstring 0117
-#define Bconcat2 0120
-#define Bconcat3 0121
-#define Bconcat4 0122
-#define Bsub1 0123
-#define Badd1 0124
-#define Beqlsign 0125
-#define Bgtr 0126
-#define Blss 0127
-#define Bleq 0130
-#define Bgeq 0131
-#define Bdiff 0132
-#define Bnegate 0133
-#define Bplus 0134
-#define Bmax 0135
-#define Bmin 0136
-#define Bmult 0137
-
-#define Bpoint 0140
-/* Was Bmark in v17.  */
-#define Bsave_current_buffer 0141 /* Obsolete.  */
-#define Bgoto_char 0142
-#define Binsert 0143
-#define Bpoint_max 0144
-#define Bpoint_min 0145
-#define Bchar_after 0146
-#define Bfollowing_char 0147
-#define Bpreceding_char 0150
-#define Bcurrent_column 0151
-#define Bindent_to 0152
-#ifdef BYTE_CODE_SAFE
-#define Bscan_buffer 0153 /* No longer generated as of v18.  */
-#endif
-#define Beolp 0154
-#define Beobp 0155
-#define Bbolp 0156
-#define Bbobp 0157
-#define Bcurrent_buffer 0160
-#define Bset_buffer 0161
-#define Bsave_current_buffer_1 0162 /* Replacing Bsave_current_buffer.  */
-#if 0
-#define Bread_char 0162 /* No longer generated as of v19 */
-#endif
+#define BYTE_CODES							\
+DEFINE (Bstack_ref, 0) /* Actually, Bstack_ref+0 is not implemented: use dup.  */ \
+DEFINE (Bstack_ref1, 1)							\
+DEFINE (Bstack_ref2, 2)							\
+DEFINE (Bstack_ref3, 3)							\
+DEFINE (Bstack_ref4, 4)							\
+DEFINE (Bstack_ref5, 5)							\
+DEFINE (Bstack_ref6, 6)							\
+DEFINE (Bstack_ref7, 7)							\
+DEFINE (Bvarref, 010)							\
+DEFINE (Bvarref1, 011)							\
+DEFINE (Bvarref2, 012)							\
+DEFINE (Bvarref3, 013)							\
+DEFINE (Bvarref4, 014)							\
+DEFINE (Bvarref5, 015)							\
+DEFINE (Bvarref6, 016)							\
+DEFINE (Bvarref7, 017)							\
+DEFINE (Bvarset, 020)							\
+DEFINE (Bvarset1, 021)							\
+DEFINE (Bvarset2, 022)							\
+DEFINE (Bvarset3, 023)							\
+DEFINE (Bvarset4, 024)							\
+DEFINE (Bvarset5, 025)							\
+DEFINE (Bvarset6, 026)							\
+DEFINE (Bvarset7, 027)							\
+DEFINE (Bvarbind, 030)							\
+DEFINE (Bvarbind1, 031)							\
+DEFINE (Bvarbind2, 032)							\
+DEFINE (Bvarbind3, 033)							\
+DEFINE (Bvarbind4, 034)							\
+DEFINE (Bvarbind5, 035)							\
+DEFINE (Bvarbind6, 036)							\
+DEFINE (Bvarbind7, 037)							\
+DEFINE (Bcall, 040)							\
+DEFINE (Bcall1, 041)							\
+DEFINE (Bcall2, 042)							\
+DEFINE (Bcall3, 043)							\
+DEFINE (Bcall4, 044)							\
+DEFINE (Bcall5, 045)							\
+DEFINE (Bcall6, 046)							\
+DEFINE (Bcall7, 047)							\
+DEFINE (Bunbind, 050)							\
+DEFINE (Bunbind1, 051)							\
+DEFINE (Bunbind2, 052)							\
+DEFINE (Bunbind3, 053)							\
+DEFINE (Bunbind4, 054)							\
+DEFINE (Bunbind5, 055)							\
+DEFINE (Bunbind6, 056)							\
+DEFINE (Bunbind7, 057)							\
+									\
+DEFINE (Bnth, 070)							\
+DEFINE (Bsymbolp, 071)							\
+DEFINE (Bconsp, 072)							\
+DEFINE (Bstringp, 073)							\
+DEFINE (Blistp, 074)							\
+DEFINE (Beq, 075)							\
+DEFINE (Bmemq, 076)							\
+DEFINE (Bnot, 077)							\
+DEFINE (Bcar, 0100)							\
+DEFINE (Bcdr, 0101)							\
+DEFINE (Bcons, 0102)							\
+DEFINE (Blist1, 0103)							\
+DEFINE (Blist2, 0104)							\
+DEFINE (Blist3, 0105)							\
+DEFINE (Blist4, 0106)							\
+DEFINE (Blength, 0107)							\
+DEFINE (Baref, 0110)							\
+DEFINE (Baset, 0111)							\
+DEFINE (Bsymbol_value, 0112)						\
+DEFINE (Bsymbol_function, 0113)						\
+DEFINE (Bset, 0114)							\
+DEFINE (Bfset, 0115)							\
+DEFINE (Bget, 0116)							\
+DEFINE (Bsubstring, 0117)						\
+DEFINE (Bconcat2, 0120)							\
+DEFINE (Bconcat3, 0121)							\
+DEFINE (Bconcat4, 0122)							\
+DEFINE (Bsub1, 0123)							\
+DEFINE (Badd1, 0124)							\
+DEFINE (Beqlsign, 0125)							\
+DEFINE (Bgtr, 0126)							\
+DEFINE (Blss, 0127)							\
+DEFINE (Bleq, 0130)							\
+DEFINE (Bgeq, 0131)							\
+DEFINE (Bdiff, 0132)							\
+DEFINE (Bnegate, 0133)							\
+DEFINE (Bplus, 0134)							\
+DEFINE (Bmax, 0135)							\
+DEFINE (Bmin, 0136)							\
+DEFINE (Bmult, 0137)							\
+									\
+DEFINE (Bpoint, 0140)							\
+/* Was Bmark in v17.  */						\
+DEFINE (Bsave_current_buffer, 0141) /* Obsolete.  */			\
+DEFINE (Bgoto_char, 0142)						\
+DEFINE (Binsert, 0143)							\
+DEFINE (Bpoint_max, 0144)						\
+DEFINE (Bpoint_min, 0145)						\
+DEFINE (Bchar_after, 0146)						\
+DEFINE (Bfollowing_char, 0147)						\
+DEFINE (Bpreceding_char, 0150)						\
+DEFINE (Bcurrent_column, 0151)						\
+DEFINE (Bindent_to, 0152)						\
+DEFINE (Beolp, 0154)							\
+DEFINE (Beobp, 0155)							\
+DEFINE (Bbolp, 0156)							\
+DEFINE (Bbobp, 0157)							\
+DEFINE (Bcurrent_buffer, 0160)						\
+DEFINE (Bset_buffer, 0161)						\
+DEFINE (Bsave_current_buffer_1, 0162) /* Replacing Bsave_current_buffer.  */ \
+DEFINE (Binteractive_p, 0164) /* Obsolete since Emacs-24.1.  */		\
+									\
+DEFINE (Bforward_char, 0165)						\
+DEFINE (Bforward_word, 0166)						\
+DEFINE (Bskip_chars_forward, 0167)					\
+DEFINE (Bskip_chars_backward, 0170)					\
+DEFINE (Bforward_line, 0171)						\
+DEFINE (Bchar_syntax, 0172)						\
+DEFINE (Bbuffer_substring, 0173)					\
+DEFINE (Bdelete_region, 0174)						\
+DEFINE (Bnarrow_to_region, 0175)					\
+DEFINE (Bwiden, 0176)							\
+DEFINE (Bend_of_line, 0177)						\
+									\
+DEFINE (Bconstant2, 0201)						\
+DEFINE (Bgoto, 0202)							\
+DEFINE (Bgotoifnil, 0203)						\
+DEFINE (Bgotoifnonnil, 0204)						\
+DEFINE (Bgotoifnilelsepop, 0205)					\
+DEFINE (Bgotoifnonnilelsepop, 0206)					\
+DEFINE (Breturn, 0207)							\
+DEFINE (Bdiscard, 0210)							\
+DEFINE (Bdup, 0211)							\
+									\
+DEFINE (Bsave_excursion, 0212)						\
+DEFINE (Bsave_window_excursion, 0213) /* Obsolete since Emacs-24.1.  */	\
+DEFINE (Bsave_restriction, 0214)					\
+DEFINE (Bcatch, 0215)							\
+									\
+DEFINE (Bunwind_protect, 0216)						\
+DEFINE (Bcondition_case, 0217)						\
+DEFINE (Btemp_output_buffer_setup, 0220) /* Obsolete since Emacs-24.1.  */ \
+DEFINE (Btemp_output_buffer_show, 0221)  /* Obsolete since Emacs-24.1.  */ \
+									\
+DEFINE (Bunbind_all, 0222)	/* Obsolete.  Never used.  */		\
+									\
+DEFINE (Bset_marker, 0223)						\
+DEFINE (Bmatch_beginning, 0224)						\
+DEFINE (Bmatch_end, 0225)						\
+DEFINE (Bupcase, 0226)							\
+DEFINE (Bdowncase, 0227)						\
+									\
+DEFINE (Bstringeqlsign, 0230)						\
+DEFINE (Bstringlss, 0231)						\
+DEFINE (Bequal, 0232)							\
+DEFINE (Bnthcdr, 0233)							\
+DEFINE (Belt, 0234)							\
+DEFINE (Bmember, 0235)							\
+DEFINE (Bassq, 0236)							\
+DEFINE (Bnreverse, 0237)						\
+DEFINE (Bsetcar, 0240)							\
+DEFINE (Bsetcdr, 0241)							\
+DEFINE (Bcar_safe, 0242)						\
+DEFINE (Bcdr_safe, 0243)						\
+DEFINE (Bnconc, 0244)							\
+DEFINE (Bquo, 0245)							\
+DEFINE (Brem, 0246)							\
+DEFINE (Bnumberp, 0247)							\
+DEFINE (Bintegerp, 0250)						\
+									\
+DEFINE (BRgoto, 0252)							\
+DEFINE (BRgotoifnil, 0253)						\
+DEFINE (BRgotoifnonnil, 0254)						\
+DEFINE (BRgotoifnilelsepop, 0255)					\
+DEFINE (BRgotoifnonnilelsepop, 0256)					\
+									\
+DEFINE (BlistN, 0257)							\
+DEFINE (BconcatN, 0260)							\
+DEFINE (BinsertN, 0261)							\
+									\
+/* Bstack_ref is code 0.  */						\
+DEFINE (Bstack_set,  0262)						\
+DEFINE (Bstack_set2, 0263)						\
+DEFINE (BdiscardN,   0266)						\
+									\
+DEFINE (Bconstant, 0300)
+
+enum byte_code_op
+{
+#define DEFINE(name, value) name = value,
+    BYTE_CODES
+#undef DEFINE
+
 #ifdef BYTE_CODE_SAFE
-#define Bset_mark 0163 /* this loser is no longer generated as of v18 */
+    Bscan_buffer = 0153, /* No longer generated as of v18.  */
+    Bset_mark = 0163 /* this loser is no longer generated as of v18 */
 #endif
-#define Binteractive_p 0164 /* Obsolete since Emacs-24.1.  */
-
-#define Bforward_char 0165
-#define Bforward_word 0166
-#define Bskip_chars_forward 0167
-#define Bskip_chars_backward 0170
-#define Bforward_line 0171
-#define Bchar_syntax 0172
-#define Bbuffer_substring 0173
-#define Bdelete_region 0174
-#define Bnarrow_to_region 0175
-#define Bwiden 0176
-#define Bend_of_line 0177
-
-#define Bconstant2 0201
-#define Bgoto 0202
-#define Bgotoifnil 0203
-#define Bgotoifnonnil 0204
-#define Bgotoifnilelsepop 0205
-#define Bgotoifnonnilelsepop 0206
-#define Breturn 0207
-#define Bdiscard 0210
-#define Bdup 0211
-
-#define Bsave_excursion 0212
-#define Bsave_window_excursion 0213 /* Obsolete since Emacs-24.1.  */
-#define Bsave_restriction 0214
-#define Bcatch 0215
-
-#define Bunwind_protect 0216
-#define Bcondition_case 0217
-#define Btemp_output_buffer_setup 0220 /* Obsolete since Emacs-24.1.  */
-#define Btemp_output_buffer_show 0221  /* Obsolete since Emacs-24.1.  */
-
-#define Bunbind_all 0222	/* Obsolete.  Never used.  */
-
-#define Bset_marker 0223
-#define Bmatch_beginning 0224
-#define Bmatch_end 0225
-#define Bupcase 0226
-#define Bdowncase 0227
-
-#define Bstringeqlsign 0230
-#define Bstringlss 0231
-#define Bequal 0232
-#define Bnthcdr 0233
-#define Belt 0234
-#define Bmember 0235
-#define Bassq 0236
-#define Bnreverse 0237
-#define Bsetcar 0240
-#define Bsetcdr 0241
-#define Bcar_safe 0242
-#define Bcdr_safe 0243
-#define Bnconc 0244
-#define Bquo 0245
-#define Brem 0246
-#define Bnumberp 0247
-#define Bintegerp 0250
-
-#define BRgoto 0252
-#define BRgotoifnil 0253
-#define BRgotoifnonnil 0254
-#define BRgotoifnilelsepop 0255
-#define BRgotoifnonnilelsepop 0256
-
-#define BlistN 0257
-#define BconcatN 0260
-#define BinsertN 0261
-
-/* Bstack_ref is code 0.  */
-#define Bstack_set  0262
-#define Bstack_set2 0263
-#define BdiscardN   0266
-
-#define Bconstant 0300
+};
 
 /* Whether to maintain a `top' and `bottom' field in the stack frame.  */
 #define BYTE_MAINTAIN_TOP (BYTE_CODE_SAFE || BYTE_MARK_STACK)
@@ -561,27 +614,83 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
       this_op = op = FETCH;
       METER_CODE (prev_op, op);
 #else
+#ifndef BYTE_CODE_THREADED
       op = FETCH;
 #endif
+#endif
+
+      /* The interpreter can be compiled one of two ways: as an
+	 ordinary switch-based interpreter, or as a threaded
+	 interpreter.  The threaded interpreter relies on GCC's
+	 computed goto extension, so it is not available everywhere.
+	 Threading provides a performance boost.  These macros are how
+	 we allow the code to be compiled both ways.  */
+#ifdef BYTE_CODE_THREADED
+      /* The CASE macro introduces an instruction's body.  It is
+	 either a label or a case label.  */
+#define CASE(OP) insn_ ## OP
+      /* NEXT is invoked at the end of an instruction to go to the
+	 next instruction.  It is either a computed goto, or a
+	 plain break.  */
+#define NEXT goto *(targets[op = FETCH])
+      /* FIRST is like NEXT, but is only used at the start of the
+	 interpreter body.  In the switch-based interpreter it is the
+	 switch, so the threaded definition must include a semicolon.  */
+#define FIRST NEXT;
+      /* Most cases are labeled with the CASE macro, above.
+	 CASE_DEFAULT is one exception; it is used if the interpreter
+	 being built requires a default case.  The threaded
+	 interpreter does not, because the dispatch table is
+	 completely filled.  */
+#define CASE_DEFAULT
+      /* This introduces an instruction that is known to call abort.  */
+#define CASE_ABORT CASE (Bstack_ref): CASE (default)
+#else
+      /* See above for the meaning of the various defines.  */
+#define CASE(OP) case OP
+#define NEXT break
+#define FIRST switch (op)
+#define CASE_DEFAULT case 255: default:
+#define CASE_ABORT case 0
+#endif
+
+#ifdef BYTE_CODE_THREADED
+
+      /* A convenience define that saves us a lot of typing and makes
+	 the table clearer.  */
+#define LABEL(OP) [OP] = &&insn_ ## OP
 
-      switch (op)
+      /* This is the dispatch table for the threaded interpreter.  */
+      static const void *const targets[256] =
 	{
-	case Bvarref + 7:
+	  [0 ... (Bconstant - 1)] = &&insn_default,
+	  [Bconstant ... 255] = &&insn_Bconstant,
+
+#define DEFINE(name, value) LABEL (name) ,
+	  BYTE_CODES
+#undef DEFINE
+	};
+#endif
+
+
+      FIRST
+	{
+	CASE (Bvarref7):
 	  op = FETCH2;
 	  goto varref;
 
-	case Bvarref:
-	case Bvarref + 1:
-	case Bvarref + 2:
-	case Bvarref + 3:
-	case Bvarref + 4:
-	case Bvarref + 5:
+	CASE (Bvarref):
+	CASE (Bvarref1):
+	CASE (Bvarref2):
+	CASE (Bvarref3):
+	CASE (Bvarref4):
+	CASE (Bvarref5):
 	  op = op - Bvarref;
 	  goto varref;
 
 	/* This seems to be the most frequently executed byte-code
 	   among the Bvarref's, so avoid a goto here.  */
-	case Bvarref+6:
+	CASE (Bvarref6):
 	  op = FETCH;
 	varref:
 	  {
@@ -606,10 +715,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		AFTER_POTENTIAL_GC ();
 	      }
 	    PUSH (v2);
-	    break;
+	    NEXT;
 	  }
 
-	case Bgotoifnil:
+	CASE (Bgotoifnil):
 	  {
 	    Lisp_Object v1;
 	    MAYBE_GC ();
@@ -621,10 +730,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		CHECK_RANGE (op);
 		stack.pc = stack.byte_string_start + op;
 	      }
-	    break;
+	    NEXT;
 	  }
 
-	case Bcar:
+	CASE (Bcar):
 	  {
 	    Lisp_Object v1;
 	    v1 = TOP;
@@ -638,28 +747,28 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		wrong_type_argument (Qlistp, v1);
 		AFTER_POTENTIAL_GC ();
 	      }
-	    break;
+	    NEXT;
 	  }
 
-	case Beq:
+	CASE (Beq):
 	  {
 	    Lisp_Object v1;
 	    v1 = POP;
 	    TOP = EQ (v1, TOP) ? Qt : Qnil;
-	    break;
+	    NEXT;
 	  }
 
-	case Bmemq:
+	CASE (Bmemq):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fmemq (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bcdr:
+	CASE (Bcdr):
 	  {
 	    Lisp_Object v1;
 	    v1 = TOP;
@@ -673,24 +782,23 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		wrong_type_argument (Qlistp, v1);
 		AFTER_POTENTIAL_GC ();
 	      }
-	    break;
-	    break;
+	    NEXT;
 	  }
 
-	case Bvarset:
-	case Bvarset+1:
-	case Bvarset+2:
-	case Bvarset+3:
-	case Bvarset+4:
-	case Bvarset+5:
+	CASE (Bvarset):
+	CASE (Bvarset1):
+	CASE (Bvarset2):
+	CASE (Bvarset3):
+	CASE (Bvarset4):
+	CASE (Bvarset5):
 	  op -= Bvarset;
 	  goto varset;
 
-	case Bvarset+7:
+	CASE (Bvarset7):
 	  op = FETCH2;
 	  goto varset;
 
-	case Bvarset+6:
+	CASE (Bvarset6):
 	  op = FETCH;
 	varset:
 	  {
@@ -713,54 +821,54 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	      }
 	  }
 	  (void) POP;
-	  break;
+	  NEXT;
 
-	case Bdup:
+	CASE (Bdup):
 	  {
 	    Lisp_Object v1;
 	    v1 = TOP;
 	    PUSH (v1);
-	    break;
+	    NEXT;
 	  }
 
 	/* ------------------ */
 
-	case Bvarbind+6:
+	CASE (Bvarbind6):
 	  op = FETCH;
 	  goto varbind;
 
-	case Bvarbind+7:
+	CASE (Bvarbind7):
 	  op = FETCH2;
 	  goto varbind;
 
-	case Bvarbind:
-	case Bvarbind+1:
-	case Bvarbind+2:
-	case Bvarbind+3:
-	case Bvarbind+4:
-	case Bvarbind+5:
+	CASE (Bvarbind):
+	CASE (Bvarbind1):
+	CASE (Bvarbind2):
+	CASE (Bvarbind3):
+	CASE (Bvarbind4):
+	CASE (Bvarbind5):
 	  op -= Bvarbind;
 	varbind:
 	  /* Specbind can signal and thus GC.  */
 	  BEFORE_POTENTIAL_GC ();
 	  specbind (vectorp[op], POP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bcall+6:
+	CASE (Bcall6):
 	  op = FETCH;
 	  goto docall;
 
-	case Bcall+7:
+	CASE (Bcall7):
 	  op = FETCH2;
 	  goto docall;
 
-	case Bcall:
-	case Bcall+1:
-	case Bcall+2:
-	case Bcall+3:
-	case Bcall+4:
-	case Bcall+5:
+	CASE (Bcall):
+	CASE (Bcall1):
+	CASE (Bcall2):
+	CASE (Bcall3):
+	CASE (Bcall4):
+	CASE (Bcall5):
 	  op -= Bcall;
 	docall:
 	  {
@@ -783,47 +891,47 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 #endif
 	    TOP = Ffuncall (op + 1, &TOP);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bunbind+6:
+	CASE (Bunbind6):
 	  op = FETCH;
 	  goto dounbind;
 
-	case Bunbind+7:
+	CASE (Bunbind7):
 	  op = FETCH2;
 	  goto dounbind;
 
-	case Bunbind:
-	case Bunbind+1:
-	case Bunbind+2:
-	case Bunbind+3:
-	case Bunbind+4:
-	case Bunbind+5:
+	CASE (Bunbind):
+	CASE (Bunbind1):
+	CASE (Bunbind2):
+	CASE (Bunbind3):
+	CASE (Bunbind4):
+	CASE (Bunbind5):
 	  op -= Bunbind;
 	dounbind:
 	  BEFORE_POTENTIAL_GC ();
 	  unbind_to (SPECPDL_INDEX () - op, Qnil);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bunbind_all:	/* Obsolete.  Never used.  */
+	CASE (Bunbind_all):	/* Obsolete.  Never used.  */
 	  /* To unbind back to the beginning of this frame.  Not used yet,
 	     but will be needed for tail-recursion elimination.  */
 	  BEFORE_POTENTIAL_GC ();
 	  unbind_to (count, Qnil);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bgoto:
+	CASE (Bgoto):
 	  MAYBE_GC ();
 	  BYTE_CODE_QUIT;
 	  op = FETCH2;    /* pc = FETCH2 loses since FETCH2 contains pc++ */
 	  CHECK_RANGE (op);
 	  stack.pc = stack.byte_string_start + op;
-	  break;
+	  NEXT;
 
-	case Bgotoifnonnil:
+	CASE (Bgotoifnonnil):
 	  {
 	    Lisp_Object v1;
 	    MAYBE_GC ();
@@ -835,10 +943,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		CHECK_RANGE (op);
 		stack.pc = stack.byte_string_start + op;
 	      }
-	    break;
+	    NEXT;
 	  }
 
-	case Bgotoifnilelsepop:
+	CASE (Bgotoifnilelsepop):
 	  MAYBE_GC ();
 	  op = FETCH2;
 	  if (NILP (TOP))
@@ -848,9 +956,9 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	      stack.pc = stack.byte_string_start + op;
 	    }
 	  else DISCARD (1);
-	  break;
+	  NEXT;
 
-	case Bgotoifnonnilelsepop:
+	CASE (Bgotoifnonnilelsepop):
 	  MAYBE_GC ();
 	  op = FETCH2;
 	  if (!NILP (TOP))
@@ -860,15 +968,15 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	      stack.pc = stack.byte_string_start + op;
 	    }
 	  else DISCARD (1);
-	  break;
+	  NEXT;
 
-	case BRgoto:
+	CASE (BRgoto):
 	  MAYBE_GC ();
 	  BYTE_CODE_QUIT;
 	  stack.pc += (int) *stack.pc - 127;
-	  break;
+	  NEXT;
 
-	case BRgotoifnil:
+	CASE (BRgotoifnil):
 	  {
 	    Lisp_Object v1;
 	    MAYBE_GC ();
@@ -879,10 +987,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		stack.pc += (int) *stack.pc - 128;
 	      }
 	    stack.pc++;
-	    break;
+	    NEXT;
 	  }
 
-	case BRgotoifnonnil:
+	CASE (BRgotoifnonnil):
 	  {
 	    Lisp_Object v1;
 	    MAYBE_GC ();
@@ -893,10 +1001,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		stack.pc += (int) *stack.pc - 128;
 	      }
 	    stack.pc++;
-	    break;
+	    NEXT;
 	  }
 
-	case BRgotoifnilelsepop:
+	CASE (BRgotoifnilelsepop):
 	  MAYBE_GC ();
 	  op = *stack.pc++;
 	  if (NILP (TOP))
@@ -905,9 +1013,9 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	      stack.pc += op - 128;
 	    }
 	  else DISCARD (1);
-	  break;
+	  NEXT;
 
-	case BRgotoifnonnilelsepop:
+	CASE (BRgotoifnonnilelsepop):
 	  MAYBE_GC ();
 	  op = *stack.pc++;
 	  if (!NILP (TOP))
@@ -916,31 +1024,31 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	      stack.pc += op - 128;
 	    }
 	  else DISCARD (1);
-	  break;
+	  NEXT;
 
-	case Breturn:
+	CASE (Breturn):
 	  result = POP;
 	  goto exit;
 
-	case Bdiscard:
+	CASE (Bdiscard):
 	  DISCARD (1);
-	  break;
+	  NEXT;
 
-	case Bconstant2:
+	CASE (Bconstant2):
 	  PUSH (vectorp[FETCH2]);
-	  break;
+	  NEXT;
 
-	case Bsave_excursion:
+	CASE (Bsave_excursion):
 	  record_unwind_protect (save_excursion_restore,
 				 save_excursion_save ());
-	  break;
+	  NEXT;
 
-	case Bsave_current_buffer: /* Obsolete since ??.  */
-	case Bsave_current_buffer_1:
+	CASE (Bsave_current_buffer): /* Obsolete since ??.  */
+	CASE (Bsave_current_buffer_1):
 	  record_unwind_protect (set_buffer_if_live, Fcurrent_buffer ());
-	  break;
+	  NEXT;
 
-	case Bsave_window_excursion: /* Obsolete since 24.1.  */
+	CASE (Bsave_window_excursion): /* Obsolete since 24.1.  */
 	  {
 	    register ptrdiff_t count1 = SPECPDL_INDEX ();
 	    record_unwind_protect (Fset_window_configuration,
@@ -949,29 +1057,29 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	    TOP = Fprogn (TOP);
 	    unbind_to (count1, TOP);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bsave_restriction:
+	CASE (Bsave_restriction):
 	  record_unwind_protect (save_restriction_restore,
 				 save_restriction_save ());
-	  break;
+	  NEXT;
 
-	case Bcatch:		/* FIXME: ill-suited for lexbind.  */
+	CASE (Bcatch):		/* FIXME: ill-suited for lexbind.  */
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = internal_catch (TOP, eval_sub, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bunwind_protect:	/* FIXME: avoid closure for lexbind.  */
+	CASE (Bunwind_protect):	/* FIXME: avoid closure for lexbind.  */
 	  record_unwind_protect (Fprogn, POP);
-	  break;
+	  NEXT;
 
-	case Bcondition_case:	/* FIXME: ill-suited for lexbind.  */
+	CASE (Bcondition_case):	/* FIXME: ill-suited for lexbind.  */
 	  {
 	    Lisp_Object handlers, body;
 	    handlers = POP;
@@ -979,18 +1087,18 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	    BEFORE_POTENTIAL_GC ();
 	    TOP = internal_lisp_condition_case (TOP, body, handlers);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Btemp_output_buffer_setup: /* Obsolete since 24.1.  */
+	CASE (Btemp_output_buffer_setup): /* Obsolete since 24.1.  */
 	  BEFORE_POTENTIAL_GC ();
 	  CHECK_STRING (TOP);
 	  temp_output_buffer_setup (SSDATA (TOP));
 	  AFTER_POTENTIAL_GC ();
 	  TOP = Vstandard_output;
-	  break;
+	  NEXT;
 
-	case Btemp_output_buffer_show: /* Obsolete since 24.1.  */
+	CASE (Btemp_output_buffer_show): /* Obsolete since 24.1.  */
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
@@ -1000,10 +1108,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	    /* pop binding of standard-output */
 	    unbind_to (SPECPDL_INDEX () - 1, Qnil);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bnth:
+	CASE (Bnth):
 	  {
 	    Lisp_Object v1, v2;
 	    EMACS_INT n;
@@ -1018,173 +1126,173 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	    immediate_quit = 0;
 	    TOP = CAR (v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bsymbolp:
+	CASE (Bsymbolp):
 	  TOP = SYMBOLP (TOP) ? Qt : Qnil;
-	  break;
+	  NEXT;
 
-	case Bconsp:
+	CASE (Bconsp):
 	  TOP = CONSP (TOP) ? Qt : Qnil;
-	  break;
+	  NEXT;
 
-	case Bstringp:
+	CASE (Bstringp):
 	  TOP = STRINGP (TOP) ? Qt : Qnil;
-	  break;
+	  NEXT;
 
-	case Blistp:
+	CASE (Blistp):
 	  TOP = CONSP (TOP) || NILP (TOP) ? Qt : Qnil;
-	  break;
+	  NEXT;
 
-	case Bnot:
+	CASE (Bnot):
 	  TOP = NILP (TOP) ? Qt : Qnil;
-	  break;
+	  NEXT;
 
-	case Bcons:
+	CASE (Bcons):
 	  {
 	    Lisp_Object v1;
 	    v1 = POP;
 	    TOP = Fcons (TOP, v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Blist1:
+	CASE (Blist1):
 	  TOP = Fcons (TOP, Qnil);
-	  break;
+	  NEXT;
 
-	case Blist2:
+	CASE (Blist2):
 	  {
 	    Lisp_Object v1;
 	    v1 = POP;
 	    TOP = Fcons (TOP, Fcons (v1, Qnil));
-	    break;
+	    NEXT;
 	  }
 
-	case Blist3:
+	CASE (Blist3):
 	  DISCARD (2);
 	  TOP = Flist (3, &TOP);
-	  break;
+	  NEXT;
 
-	case Blist4:
+	CASE (Blist4):
 	  DISCARD (3);
 	  TOP = Flist (4, &TOP);
-	  break;
+	  NEXT;
 
-	case BlistN:
+	CASE (BlistN):
 	  op = FETCH;
 	  DISCARD (op - 1);
 	  TOP = Flist (op, &TOP);
-	  break;
+	  NEXT;
 
-	case Blength:
+	CASE (Blength):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Flength (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Baref:
+	CASE (Baref):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Faref (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Baset:
+	CASE (Baset):
 	  {
 	    Lisp_Object v1, v2;
 	    BEFORE_POTENTIAL_GC ();
 	    v2 = POP; v1 = POP;
 	    TOP = Faset (TOP, v1, v2);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bsymbol_value:
+	CASE (Bsymbol_value):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fsymbol_value (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bsymbol_function:
+	CASE (Bsymbol_function):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fsymbol_function (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bset:
+	CASE (Bset):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fset (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bfset:
+	CASE (Bfset):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Ffset (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bget:
+	CASE (Bget):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fget (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bsubstring:
+	CASE (Bsubstring):
 	  {
 	    Lisp_Object v1, v2;
 	    BEFORE_POTENTIAL_GC ();
 	    v2 = POP; v1 = POP;
 	    TOP = Fsubstring (TOP, v1, v2);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bconcat2:
+	CASE (Bconcat2):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (1);
 	  TOP = Fconcat (2, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bconcat3:
+	CASE (Bconcat3):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (2);
 	  TOP = Fconcat (3, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bconcat4:
+	CASE (Bconcat4):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (3);
 	  TOP = Fconcat (4, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case BconcatN:
+	CASE (BconcatN):
 	  op = FETCH;
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (op - 1);
 	  TOP = Fconcat (op, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bsub1:
+	CASE (Bsub1):
 	  {
 	    Lisp_Object v1;
 	    v1 = TOP;
@@ -1199,10 +1307,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		TOP = Fsub1 (v1);
 		AFTER_POTENTIAL_GC ();
 	      }
-	    break;
+	    NEXT;
 	  }
 
-	case Badd1:
+	CASE (Badd1):
 	  {
 	    Lisp_Object v1;
 	    v1 = TOP;
@@ -1217,10 +1325,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		TOP = Fadd1 (v1);
 		AFTER_POTENTIAL_GC ();
 	      }
-	    break;
+	    NEXT;
 	  }
 
-	case Beqlsign:
+	CASE (Beqlsign):
 	  {
 	    Lisp_Object v1, v2;
 	    BEFORE_POTENTIAL_GC ();
@@ -1238,57 +1346,57 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	      }
 	    else
 	      TOP = (XINT (v1) == XINT (v2) ? Qt : Qnil);
-	    break;
+	    NEXT;
 	  }
 
-	case Bgtr:
+	CASE (Bgtr):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fgtr (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Blss:
+	CASE (Blss):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Flss (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bleq:
+	CASE (Bleq):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fleq (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bgeq:
+	CASE (Bgeq):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fgeq (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bdiff:
+	CASE (Bdiff):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (1);
 	  TOP = Fminus (2, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bnegate:
+	CASE (Bnegate):
 	  {
 	    Lisp_Object v1;
 	    v1 = TOP;
@@ -1303,209 +1411,209 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		TOP = Fminus (1, &TOP);
 		AFTER_POTENTIAL_GC ();
 	      }
-	    break;
+	    NEXT;
 	  }
 
-	case Bplus:
+	CASE (Bplus):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (1);
 	  TOP = Fplus (2, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bmax:
+	CASE (Bmax):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (1);
 	  TOP = Fmax (2, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bmin:
+	CASE (Bmin):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (1);
 	  TOP = Fmin (2, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bmult:
+	CASE (Bmult):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (1);
 	  TOP = Ftimes (2, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bquo:
+	CASE (Bquo):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (1);
 	  TOP = Fquo (2, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Brem:
+	CASE (Brem):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Frem (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bpoint:
+	CASE (Bpoint):
 	  {
 	    Lisp_Object v1;
 	    XSETFASTINT (v1, PT);
 	    PUSH (v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Bgoto_char:
+	CASE (Bgoto_char):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fgoto_char (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Binsert:
+	CASE (Binsert):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Finsert (1, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case BinsertN:
+	CASE (BinsertN):
 	  op = FETCH;
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (op - 1);
 	  TOP = Finsert (op, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bpoint_max:
+	CASE (Bpoint_max):
 	  {
 	    Lisp_Object v1;
 	    XSETFASTINT (v1, ZV);
 	    PUSH (v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Bpoint_min:
+	CASE (Bpoint_min):
 	  {
 	    Lisp_Object v1;
 	    XSETFASTINT (v1, BEGV);
 	    PUSH (v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Bchar_after:
+	CASE (Bchar_after):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fchar_after (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bfollowing_char:
+	CASE (Bfollowing_char):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = Ffollowing_char ();
 	    AFTER_POTENTIAL_GC ();
 	    PUSH (v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Bpreceding_char:
+	CASE (Bpreceding_char):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = Fprevious_char ();
 	    AFTER_POTENTIAL_GC ();
 	    PUSH (v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Bcurrent_column:
+	CASE (Bcurrent_column):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    XSETFASTINT (v1, current_column ());
 	    AFTER_POTENTIAL_GC ();
 	    PUSH (v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Bindent_to:
+	CASE (Bindent_to):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Findent_to (TOP, Qnil);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Beolp:
+	CASE (Beolp):
 	  PUSH (Feolp ());
-	  break;
+	  NEXT;
 
-	case Beobp:
+	CASE (Beobp):
 	  PUSH (Feobp ());
-	  break;
+	  NEXT;
 
-	case Bbolp:
+	CASE (Bbolp):
 	  PUSH (Fbolp ());
-	  break;
+	  NEXT;
 
-	case Bbobp:
+	CASE (Bbobp):
 	  PUSH (Fbobp ());
-	  break;
+	  NEXT;
 
-	case Bcurrent_buffer:
+	CASE (Bcurrent_buffer):
 	  PUSH (Fcurrent_buffer ());
-	  break;
+	  NEXT;
 
-	case Bset_buffer:
+	CASE (Bset_buffer):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fset_buffer (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Binteractive_p:	/* Obsolete since 24.1.  */
+	CASE (Binteractive_p):	/* Obsolete since 24.1.  */
 	  PUSH (Finteractive_p ());
-	  break;
+	  NEXT;
 
-	case Bforward_char:
+	CASE (Bforward_char):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fforward_char (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bforward_word:
+	CASE (Bforward_word):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fforward_word (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bskip_chars_forward:
+	CASE (Bskip_chars_forward):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fskip_chars_forward (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bskip_chars_backward:
+	CASE (Bskip_chars_backward):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fskip_chars_backward (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bforward_line:
+	CASE (Bforward_line):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fforward_line (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bchar_syntax:
+	CASE (Bchar_syntax):
 	  {
 	    int c;
 
@@ -1517,51 +1625,51 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	      MAKE_CHAR_MULTIBYTE (c);
 	    XSETFASTINT (TOP, syntax_code_spec[(int) SYNTAX (c)]);
 	  }
-	  break;
+	  NEXT;
 
-	case Bbuffer_substring:
+	CASE (Bbuffer_substring):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fbuffer_substring (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bdelete_region:
+	CASE (Bdelete_region):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fdelete_region (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bnarrow_to_region:
+	CASE (Bnarrow_to_region):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fnarrow_to_region (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bwiden:
+	CASE (Bwiden):
 	  BEFORE_POTENTIAL_GC ();
 	  PUSH (Fwiden ());
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bend_of_line:
+	CASE (Bend_of_line):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fend_of_line (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bset_marker:
+	CASE (Bset_marker):
 	  {
 	    Lisp_Object v1, v2;
 	    BEFORE_POTENTIAL_GC ();
@@ -1569,72 +1677,72 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	    v2 = POP;
 	    TOP = Fset_marker (TOP, v2, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bmatch_beginning:
+	CASE (Bmatch_beginning):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fmatch_beginning (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bmatch_end:
+	CASE (Bmatch_end):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fmatch_end (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bupcase:
+	CASE (Bupcase):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fupcase (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bdowncase:
+	CASE (Bdowncase):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fdowncase (TOP);
 	  AFTER_POTENTIAL_GC ();
-	break;
+	NEXT;
 
-	case Bstringeqlsign:
+      CASE (Bstringeqlsign):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fstring_equal (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bstringlss:
+	CASE (Bstringlss):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fstring_lessp (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bequal:
+	CASE (Bequal):
 	  {
 	    Lisp_Object v1;
 	    v1 = POP;
 	    TOP = Fequal (TOP, v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Bnthcdr:
+	CASE (Bnthcdr):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fnthcdr (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Belt:
+	CASE (Belt):
 	  {
 	    Lisp_Object v1, v2;
 	    if (CONSP (TOP))
@@ -1660,87 +1768,91 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 		TOP = Felt (TOP, v1);
 		AFTER_POTENTIAL_GC ();
 	      }
-	    break;
+	    NEXT;
 	  }
 
-	case Bmember:
+	CASE (Bmember):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fmember (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bassq:
+	CASE (Bassq):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fassq (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bnreverse:
+	CASE (Bnreverse):
 	  BEFORE_POTENTIAL_GC ();
 	  TOP = Fnreverse (TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bsetcar:
+	CASE (Bsetcar):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fsetcar (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bsetcdr:
+	CASE (Bsetcdr):
 	  {
 	    Lisp_Object v1;
 	    BEFORE_POTENTIAL_GC ();
 	    v1 = POP;
 	    TOP = Fsetcdr (TOP, v1);
 	    AFTER_POTENTIAL_GC ();
-	    break;
+	    NEXT;
 	  }
 
-	case Bcar_safe:
+	CASE (Bcar_safe):
 	  {
 	    Lisp_Object v1;
 	    v1 = TOP;
 	    TOP = CAR_SAFE (v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Bcdr_safe:
+	CASE (Bcdr_safe):
 	  {
 	    Lisp_Object v1;
 	    v1 = TOP;
 	    TOP = CDR_SAFE (v1);
-	    break;
+	    NEXT;
 	  }
 
-	case Bnconc:
+	CASE (Bnconc):
 	  BEFORE_POTENTIAL_GC ();
 	  DISCARD (1);
 	  TOP = Fnconc (2, &TOP);
 	  AFTER_POTENTIAL_GC ();
-	  break;
+	  NEXT;
 
-	case Bnumberp:
+	CASE (Bnumberp):
 	  TOP = (NUMBERP (TOP) ? Qt : Qnil);
-	  break;
+	  NEXT;
 
-	case Bintegerp:
+	CASE (Bintegerp):
 	  TOP = INTEGERP (TOP) ? Qt : Qnil;
-	  break;
+	  NEXT;
 
 #ifdef BYTE_CODE_SAFE
+	  /* These are intentionally written using 'case' syntax,
+	     because they are incompatible with the threaded
+	     interpreter.  */
+
 	case Bset_mark:
 	  BEFORE_POTENTIAL_GC ();
 	  error ("set-mark is an obsolete bytecode");
@@ -1753,49 +1865,49 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	  break;
 #endif
 
-	case 0:
+	CASE_ABORT:
 	  /* Actually this is Bstack_ref with offset 0, but we use Bdup
 	     for that instead.  */
-	  /* case Bstack_ref: */
+	  /* CASE (Bstack_ref): */
 	  abort ();
 
 	  /* Handy byte-codes for lexical binding.  */
-	case Bstack_ref+1:
-	case Bstack_ref+2:
-	case Bstack_ref+3:
-	case Bstack_ref+4:
-	case Bstack_ref+5:
+	CASE (Bstack_ref1):
+	CASE (Bstack_ref2):
+	CASE (Bstack_ref3):
+	CASE (Bstack_ref4):
+	CASE (Bstack_ref5):
 	  {
 	    Lisp_Object *ptr = top - (op - Bstack_ref);
 	    PUSH (*ptr);
-	    break;
+	    NEXT;
 	  }
-	case Bstack_ref+6:
+	CASE (Bstack_ref6):
 	  {
 	    Lisp_Object *ptr = top - (FETCH);
 	    PUSH (*ptr);
-	    break;
+	    NEXT;
 	  }
-	case Bstack_ref+7:
+	CASE (Bstack_ref7):
 	  {
 	    Lisp_Object *ptr = top - (FETCH2);
 	    PUSH (*ptr);
-	    break;
+	    NEXT;
 	  }
-	case Bstack_set:
+	CASE (Bstack_set):
 	  /* stack-set-0 = discard; stack-set-1 = discard-1-preserve-tos.  */
 	  {
 	    Lisp_Object *ptr = top - (FETCH);
 	    *ptr = POP;
-	    break;
+	    NEXT;
 	  }
-	case Bstack_set2:
+	CASE (Bstack_set2):
 	  {
 	    Lisp_Object *ptr = top - (FETCH2);
 	    *ptr = POP;
-	    break;
+	    NEXT;
 	  }
-	case BdiscardN:
+	CASE (BdiscardN):
 	  op = FETCH;
 	  if (op & 0x80)
 	    {
@@ -1803,10 +1915,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 	      top[-op] = TOP;
 	    }
 	  DISCARD (op);
-	  break;
+	  NEXT;
 
-	case 255:
-	default:
+	CASE_DEFAULT
+	CASE (Bconstant):
 #ifdef BYTE_CODE_SAFE
 	  if (op < Bconstant)
 	    {
@@ -1820,6 +1932,7 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
 #else
 	  PUSH (vectorp[op - Bconstant]);
 #endif
+	  NEXT;
 	}
     }
 



^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-07-03 14:45           ` Tom Tromey
@ 2012-07-03 16:58             ` Stefan Monnier
  2012-07-03 17:22               ` Tom Tromey
  2012-07-03 19:52             ` John Wiegley
  1 sibling, 1 reply; 23+ messages in thread
From: Stefan Monnier @ 2012-07-03 16:58 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Mohamed Ben-Ahssene, emacs-devel

Stefan> Can't we merge those two exhaustive lists (the first is only used in
Stefan> the non-threaded case and the other only in the threaded case).
Stefan> If we turn the first set of #defines into an enum, we should be able to
Stefan> bring the syntax of the two declarations close enough that the
Stefan> difference can be abstracted out into a few macros.
> Sure, how about the appended?

Looks OK.  I'm not too fond of all the backslashes and the `value'
argument to DEFINE, but I guess they're OK.

One more nitpick.  You say:

> +/* If BYTE_CODE_THREADED is defined, then the interpreter will be
> +   indirect threaded, using GCC's computed goto extension.  This is
> +   incompatible with BYTE_CODE_SAFE and BYTE_CODE_METER.  */

But, IIUC there is no incompatibility, really.  It's just that the
current code doesn't handle that particular combination, right?
If so, please make it clear in the comment.


        Stefan


> diff --git a/src/bytecode.c b/src/bytecode.c
> index 08a02ea..30ed6d0 100644
> --- a/src/bytecode.c
> +++ b/src/bytecode.c
> @@ -54,6 +54,13 @@ by Hallvard:
>  /* #define BYTE_CODE_SAFE */
>  /* #define BYTE_CODE_METER */
 
> +/* If BYTE_CODE_THREADED is defined, then the interpreter will be
> +   indirect threaded, using GCC's computed goto extension.  This is
> +   incompatible with BYTE_CODE_SAFE and BYTE_CODE_METER.  */
> +#if defined (__GNUC__) && !defined (BYTE_CODE_SAFE) && !defined (BYTE_CODE_METER)
> +#define BYTE_CODE_THREADED
> +#endif
> +
>  \f
>  #ifdef BYTE_CODE_METER
 
> @@ -83,158 +90,204 @@ Lisp_Object Qbytecode;
 
>  /*  Byte codes: */
 
> -#define Bstack_ref 0 /* Actually, Bstack_ref+0 is not implemented: use dup.  */
> -#define Bvarref 010
> -#define Bvarset 020
> -#define Bvarbind 030
> -#define Bcall 040
> -#define Bunbind 050
> -
> -#define Bnth 070
> -#define Bsymbolp 071
> -#define Bconsp 072
> -#define Bstringp 073
> -#define Blistp 074
> -#define Beq 075
> -#define Bmemq 076
> -#define Bnot 077
> -#define Bcar 0100
> -#define Bcdr 0101
> -#define Bcons 0102
> -#define Blist1 0103
> -#define Blist2 0104
> -#define Blist3 0105
> -#define Blist4 0106
> -#define Blength 0107
> -#define Baref 0110
> -#define Baset 0111
> -#define Bsymbol_value 0112
> -#define Bsymbol_function 0113
> -#define Bset 0114
> -#define Bfset 0115
> -#define Bget 0116
> -#define Bsubstring 0117
> -#define Bconcat2 0120
> -#define Bconcat3 0121
> -#define Bconcat4 0122
> -#define Bsub1 0123
> -#define Badd1 0124
> -#define Beqlsign 0125
> -#define Bgtr 0126
> -#define Blss 0127
> -#define Bleq 0130
> -#define Bgeq 0131
> -#define Bdiff 0132
> -#define Bnegate 0133
> -#define Bplus 0134
> -#define Bmax 0135
> -#define Bmin 0136
> -#define Bmult 0137
> -
> -#define Bpoint 0140
> -/* Was Bmark in v17.  */
> -#define Bsave_current_buffer 0141 /* Obsolete.  */
> -#define Bgoto_char 0142
> -#define Binsert 0143
> -#define Bpoint_max 0144
> -#define Bpoint_min 0145
> -#define Bchar_after 0146
> -#define Bfollowing_char 0147
> -#define Bpreceding_char 0150
> -#define Bcurrent_column 0151
> -#define Bindent_to 0152
> -#ifdef BYTE_CODE_SAFE
> -#define Bscan_buffer 0153 /* No longer generated as of v18.  */
> -#endif
> -#define Beolp 0154
> -#define Beobp 0155
> -#define Bbolp 0156
> -#define Bbobp 0157
> -#define Bcurrent_buffer 0160
> -#define Bset_buffer 0161
> -#define Bsave_current_buffer_1 0162 /* Replacing Bsave_current_buffer.  */
> -#if 0
> -#define Bread_char 0162 /* No longer generated as of v19 */
> -#endif
> +#define BYTE_CODES							\
> +DEFINE (Bstack_ref, 0) /* Actually, Bstack_ref+0 is not implemented: use dup.  */ \
> +DEFINE (Bstack_ref1, 1)							\
> +DEFINE (Bstack_ref2, 2)							\
> +DEFINE (Bstack_ref3, 3)							\
> +DEFINE (Bstack_ref4, 4)							\
> +DEFINE (Bstack_ref5, 5)							\
> +DEFINE (Bstack_ref6, 6)							\
> +DEFINE (Bstack_ref7, 7)							\
> +DEFINE (Bvarref, 010)							\
> +DEFINE (Bvarref1, 011)							\
> +DEFINE (Bvarref2, 012)							\
> +DEFINE (Bvarref3, 013)							\
> +DEFINE (Bvarref4, 014)							\
> +DEFINE (Bvarref5, 015)							\
> +DEFINE (Bvarref6, 016)							\
> +DEFINE (Bvarref7, 017)							\
> +DEFINE (Bvarset, 020)							\
> +DEFINE (Bvarset1, 021)							\
> +DEFINE (Bvarset2, 022)							\
> +DEFINE (Bvarset3, 023)							\
> +DEFINE (Bvarset4, 024)							\
> +DEFINE (Bvarset5, 025)							\
> +DEFINE (Bvarset6, 026)							\
> +DEFINE (Bvarset7, 027)							\
> +DEFINE (Bvarbind, 030)							\
> +DEFINE (Bvarbind1, 031)							\
> +DEFINE (Bvarbind2, 032)							\
> +DEFINE (Bvarbind3, 033)							\
> +DEFINE (Bvarbind4, 034)							\
> +DEFINE (Bvarbind5, 035)							\
> +DEFINE (Bvarbind6, 036)							\
> +DEFINE (Bvarbind7, 037)							\
> +DEFINE (Bcall, 040)							\
> +DEFINE (Bcall1, 041)							\
> +DEFINE (Bcall2, 042)							\
> +DEFINE (Bcall3, 043)							\
> +DEFINE (Bcall4, 044)							\
> +DEFINE (Bcall5, 045)							\
> +DEFINE (Bcall6, 046)							\
> +DEFINE (Bcall7, 047)							\
> +DEFINE (Bunbind, 050)							\
> +DEFINE (Bunbind1, 051)							\
> +DEFINE (Bunbind2, 052)							\
> +DEFINE (Bunbind3, 053)							\
> +DEFINE (Bunbind4, 054)							\
> +DEFINE (Bunbind5, 055)							\
> +DEFINE (Bunbind6, 056)							\
> +DEFINE (Bunbind7, 057)							\
> +									\
> +DEFINE (Bnth, 070)							\
> +DEFINE (Bsymbolp, 071)							\
> +DEFINE (Bconsp, 072)							\
> +DEFINE (Bstringp, 073)							\
> +DEFINE (Blistp, 074)							\
> +DEFINE (Beq, 075)							\
> +DEFINE (Bmemq, 076)							\
> +DEFINE (Bnot, 077)							\
> +DEFINE (Bcar, 0100)							\
> +DEFINE (Bcdr, 0101)							\
> +DEFINE (Bcons, 0102)							\
> +DEFINE (Blist1, 0103)							\
> +DEFINE (Blist2, 0104)							\
> +DEFINE (Blist3, 0105)							\
> +DEFINE (Blist4, 0106)							\
> +DEFINE (Blength, 0107)							\
> +DEFINE (Baref, 0110)							\
> +DEFINE (Baset, 0111)							\
> +DEFINE (Bsymbol_value, 0112)						\
> +DEFINE (Bsymbol_function, 0113)						\
> +DEFINE (Bset, 0114)							\
> +DEFINE (Bfset, 0115)							\
> +DEFINE (Bget, 0116)							\
> +DEFINE (Bsubstring, 0117)						\
> +DEFINE (Bconcat2, 0120)							\
> +DEFINE (Bconcat3, 0121)							\
> +DEFINE (Bconcat4, 0122)							\
> +DEFINE (Bsub1, 0123)							\
> +DEFINE (Badd1, 0124)							\
> +DEFINE (Beqlsign, 0125)							\
> +DEFINE (Bgtr, 0126)							\
> +DEFINE (Blss, 0127)							\
> +DEFINE (Bleq, 0130)							\
> +DEFINE (Bgeq, 0131)							\
> +DEFINE (Bdiff, 0132)							\
> +DEFINE (Bnegate, 0133)							\
> +DEFINE (Bplus, 0134)							\
> +DEFINE (Bmax, 0135)							\
> +DEFINE (Bmin, 0136)							\
> +DEFINE (Bmult, 0137)							\
> +									\
> +DEFINE (Bpoint, 0140)							\
> +/* Was Bmark in v17.  */						\
> +DEFINE (Bsave_current_buffer, 0141) /* Obsolete.  */			\
> +DEFINE (Bgoto_char, 0142)						\
> +DEFINE (Binsert, 0143)							\
> +DEFINE (Bpoint_max, 0144)						\
> +DEFINE (Bpoint_min, 0145)						\
> +DEFINE (Bchar_after, 0146)						\
> +DEFINE (Bfollowing_char, 0147)						\
> +DEFINE (Bpreceding_char, 0150)						\
> +DEFINE (Bcurrent_column, 0151)						\
> +DEFINE (Bindent_to, 0152)						\
> +DEFINE (Beolp, 0154)							\
> +DEFINE (Beobp, 0155)							\
> +DEFINE (Bbolp, 0156)							\
> +DEFINE (Bbobp, 0157)							\
> +DEFINE (Bcurrent_buffer, 0160)						\
> +DEFINE (Bset_buffer, 0161)						\
> +DEFINE (Bsave_current_buffer_1, 0162) /* Replacing Bsave_current_buffer.  */ \
> +DEFINE (Binteractive_p, 0164) /* Obsolete since Emacs-24.1.  */		\
> +									\
> +DEFINE (Bforward_char, 0165)						\
> +DEFINE (Bforward_word, 0166)						\
> +DEFINE (Bskip_chars_forward, 0167)					\
> +DEFINE (Bskip_chars_backward, 0170)					\
> +DEFINE (Bforward_line, 0171)						\
> +DEFINE (Bchar_syntax, 0172)						\
> +DEFINE (Bbuffer_substring, 0173)					\
> +DEFINE (Bdelete_region, 0174)						\
> +DEFINE (Bnarrow_to_region, 0175)					\
> +DEFINE (Bwiden, 0176)							\
> +DEFINE (Bend_of_line, 0177)						\
> +									\
> +DEFINE (Bconstant2, 0201)						\
> +DEFINE (Bgoto, 0202)							\
> +DEFINE (Bgotoifnil, 0203)						\
> +DEFINE (Bgotoifnonnil, 0204)						\
> +DEFINE (Bgotoifnilelsepop, 0205)					\
> +DEFINE (Bgotoifnonnilelsepop, 0206)					\
> +DEFINE (Breturn, 0207)							\
> +DEFINE (Bdiscard, 0210)							\
> +DEFINE (Bdup, 0211)							\
> +									\
> +DEFINE (Bsave_excursion, 0212)						\
> +DEFINE (Bsave_window_excursion, 0213) /* Obsolete since Emacs-24.1.  */	\
> +DEFINE (Bsave_restriction, 0214)					\
> +DEFINE (Bcatch, 0215)							\
> +									\
> +DEFINE (Bunwind_protect, 0216)						\
> +DEFINE (Bcondition_case, 0217)						\
> +DEFINE (Btemp_output_buffer_setup, 0220) /* Obsolete since Emacs-24.1.  */ \
> +DEFINE (Btemp_output_buffer_show, 0221)  /* Obsolete since Emacs-24.1.  */ \
> +									\
> +DEFINE (Bunbind_all, 0222)	/* Obsolete.  Never used.  */		\
> +									\
> +DEFINE (Bset_marker, 0223)						\
> +DEFINE (Bmatch_beginning, 0224)						\
> +DEFINE (Bmatch_end, 0225)						\
> +DEFINE (Bupcase, 0226)							\
> +DEFINE (Bdowncase, 0227)						\
> +									\
> +DEFINE (Bstringeqlsign, 0230)						\
> +DEFINE (Bstringlss, 0231)						\
> +DEFINE (Bequal, 0232)							\
> +DEFINE (Bnthcdr, 0233)							\
> +DEFINE (Belt, 0234)							\
> +DEFINE (Bmember, 0235)							\
> +DEFINE (Bassq, 0236)							\
> +DEFINE (Bnreverse, 0237)						\
> +DEFINE (Bsetcar, 0240)							\
> +DEFINE (Bsetcdr, 0241)							\
> +DEFINE (Bcar_safe, 0242)						\
> +DEFINE (Bcdr_safe, 0243)						\
> +DEFINE (Bnconc, 0244)							\
> +DEFINE (Bquo, 0245)							\
> +DEFINE (Brem, 0246)							\
> +DEFINE (Bnumberp, 0247)							\
> +DEFINE (Bintegerp, 0250)						\
> +									\
> +DEFINE (BRgoto, 0252)							\
> +DEFINE (BRgotoifnil, 0253)						\
> +DEFINE (BRgotoifnonnil, 0254)						\
> +DEFINE (BRgotoifnilelsepop, 0255)					\
> +DEFINE (BRgotoifnonnilelsepop, 0256)					\
> +									\
> +DEFINE (BlistN, 0257)							\
> +DEFINE (BconcatN, 0260)							\
> +DEFINE (BinsertN, 0261)							\
> +									\
> +/* Bstack_ref is code 0.  */						\
> +DEFINE (Bstack_set,  0262)						\
> +DEFINE (Bstack_set2, 0263)						\
> +DEFINE (BdiscardN,   0266)						\
> +									\
> +DEFINE (Bconstant, 0300)
> +
> +enum byte_code_op
> +{
> +#define DEFINE(name, value) name = value,
> +    BYTE_CODES
> +#undef DEFINE
> +
>  #ifdef BYTE_CODE_SAFE
> -#define Bset_mark 0163 /* this loser is no longer generated as of v18 */
> +    Bscan_buffer = 0153, /* No longer generated as of v18.  */
> +    Bset_mark = 0163 /* this loser is no longer generated as of v18 */
>  #endif
> -#define Binteractive_p 0164 /* Obsolete since Emacs-24.1.  */
> -
> -#define Bforward_char 0165
> -#define Bforward_word 0166
> -#define Bskip_chars_forward 0167
> -#define Bskip_chars_backward 0170
> -#define Bforward_line 0171
> -#define Bchar_syntax 0172
> -#define Bbuffer_substring 0173
> -#define Bdelete_region 0174
> -#define Bnarrow_to_region 0175
> -#define Bwiden 0176
> -#define Bend_of_line 0177
> -
> -#define Bconstant2 0201
> -#define Bgoto 0202
> -#define Bgotoifnil 0203
> -#define Bgotoifnonnil 0204
> -#define Bgotoifnilelsepop 0205
> -#define Bgotoifnonnilelsepop 0206
> -#define Breturn 0207
> -#define Bdiscard 0210
> -#define Bdup 0211
> -
> -#define Bsave_excursion 0212
> -#define Bsave_window_excursion 0213 /* Obsolete since Emacs-24.1.  */
> -#define Bsave_restriction 0214
> -#define Bcatch 0215
> -
> -#define Bunwind_protect 0216
> -#define Bcondition_case 0217
> -#define Btemp_output_buffer_setup 0220 /* Obsolete since Emacs-24.1.  */
> -#define Btemp_output_buffer_show 0221  /* Obsolete since Emacs-24.1.  */
> -
> -#define Bunbind_all 0222	/* Obsolete.  Never used.  */
> -
> -#define Bset_marker 0223
> -#define Bmatch_beginning 0224
> -#define Bmatch_end 0225
> -#define Bupcase 0226
> -#define Bdowncase 0227
> -
> -#define Bstringeqlsign 0230
> -#define Bstringlss 0231
> -#define Bequal 0232
> -#define Bnthcdr 0233
> -#define Belt 0234
> -#define Bmember 0235
> -#define Bassq 0236
> -#define Bnreverse 0237
> -#define Bsetcar 0240
> -#define Bsetcdr 0241
> -#define Bcar_safe 0242
> -#define Bcdr_safe 0243
> -#define Bnconc 0244
> -#define Bquo 0245
> -#define Brem 0246
> -#define Bnumberp 0247
> -#define Bintegerp 0250
> -
> -#define BRgoto 0252
> -#define BRgotoifnil 0253
> -#define BRgotoifnonnil 0254
> -#define BRgotoifnilelsepop 0255
> -#define BRgotoifnonnilelsepop 0256
> -
> -#define BlistN 0257
> -#define BconcatN 0260
> -#define BinsertN 0261
> -
> -/* Bstack_ref is code 0.  */
> -#define Bstack_set  0262
> -#define Bstack_set2 0263
> -#define BdiscardN   0266
> -
> -#define Bconstant 0300
> +};
 
>  /* Whether to maintain a `top' and `bottom' field in the stack frame.  */
>  #define BYTE_MAINTAIN_TOP (BYTE_CODE_SAFE || BYTE_MARK_STACK)
> @@ -561,27 +614,83 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>        this_op = op = FETCH;
>        METER_CODE (prev_op, op);
>  #else
> +#ifndef BYTE_CODE_THREADED
>        op = FETCH;
>  #endif
> +#endif
> +
> +      /* The interpreter can be compiled one of two ways: as an
> +	 ordinary switch-based interpreter, or as a threaded
> +	 interpreter.  The threaded interpreter relies on GCC's
> +	 computed goto extension, so it is not available everywhere.
> +	 Threading provides a performance boost.  These macros are how
> +	 we allow the code to be compiled both ways.  */
> +#ifdef BYTE_CODE_THREADED
> +      /* The CASE macro introduces an instruction's body.  It is
> +	 either a label or a case label.  */
> +#define CASE(OP) insn_ ## OP
> +      /* NEXT is invoked at the end of an instruction to go to the
> +	 next instruction.  It is either a computed goto, or a
> +	 plain break.  */
> +#define NEXT goto *(targets[op = FETCH])
> +      /* FIRST is like NEXT, but is only used at the start of the
> +	 interpreter body.  In the switch-based interpreter it is the
> +	 switch, so the threaded definition must include a semicolon.  */
> +#define FIRST NEXT;
> +      /* Most cases are labeled with the CASE macro, above.
> +	 CASE_DEFAULT is one exception; it is used if the interpreter
> +	 being built requires a default case.  The threaded
> +	 interpreter does not, because the dispatch table is
> +	 completely filled.  */
> +#define CASE_DEFAULT
> +      /* This introduces an instruction that is known to call abort.  */
> +#define CASE_ABORT CASE (Bstack_ref): CASE (default)
> +#else
> +      /* See above for the meaning of the various defines.  */
> +#define CASE(OP) case OP
> +#define NEXT break
> +#define FIRST switch (op)
> +#define CASE_DEFAULT case 255: default:
> +#define CASE_ABORT case 0
> +#endif
> +
> +#ifdef BYTE_CODE_THREADED
> +
> +      /* A convenience define that saves us a lot of typing and makes
> +	 the table clearer.  */
> +#define LABEL(OP) [OP] = &&insn_ ## OP
 
> -      switch (op)
> +      /* This is the dispatch table for the threaded interpreter.  */
> +      static const void *const targets[256] =
>  	{
> -	case Bvarref + 7:
> +	  [0 ... (Bconstant - 1)] = &&insn_default,
> +	  [Bconstant ... 255] = &&insn_Bconstant,
> +
> +#define DEFINE(name, value) LABEL (name) ,
> +	  BYTE_CODES
> +#undef DEFINE
> +	};
> +#endif
> +
> +
> +      FIRST
> +	{
> +	CASE (Bvarref7):
>  	  op = FETCH2;
>  	  goto varref;
 
> -	case Bvarref:
> -	case Bvarref + 1:
> -	case Bvarref + 2:
> -	case Bvarref + 3:
> -	case Bvarref + 4:
> -	case Bvarref + 5:
> +	CASE (Bvarref):
> +	CASE (Bvarref1):
> +	CASE (Bvarref2):
> +	CASE (Bvarref3):
> +	CASE (Bvarref4):
> +	CASE (Bvarref5):
>  	  op = op - Bvarref;
>  	  goto varref;
 
>  	/* This seems to be the most frequently executed byte-code
>  	   among the Bvarref's, so avoid a goto here.  */
> -	case Bvarref+6:
> +	CASE (Bvarref6):
>  	  op = FETCH;
>  	varref:
>  	  {
> @@ -606,10 +715,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  		AFTER_POTENTIAL_GC ();
>  	      }
>  	    PUSH (v2);
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bgotoifnil:
> +	CASE (Bgotoifnil):
>  	  {
>  	    Lisp_Object v1;
>  	    MAYBE_GC ();
> @@ -621,10 +730,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  		CHECK_RANGE (op);
>  		stack.pc = stack.byte_string_start + op;
>  	      }
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bcar:
> +	CASE (Bcar):
>  	  {
>  	    Lisp_Object v1;
>  	    v1 = TOP;
> @@ -638,28 +747,28 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  		wrong_type_argument (Qlistp, v1);
>  		AFTER_POTENTIAL_GC ();
>  	      }
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Beq:
> +	CASE (Beq):
>  	  {
>  	    Lisp_Object v1;
>  	    v1 = POP;
>  	    TOP = EQ (v1, TOP) ? Qt : Qnil;
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bmemq:
> +	CASE (Bmemq):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Fmemq (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bcdr:
> +	CASE (Bcdr):
>  	  {
>  	    Lisp_Object v1;
>  	    v1 = TOP;
> @@ -673,24 +782,23 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  		wrong_type_argument (Qlistp, v1);
>  		AFTER_POTENTIAL_GC ();
>  	      }
> -	    break;
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bvarset:
> -	case Bvarset+1:
> -	case Bvarset+2:
> -	case Bvarset+3:
> -	case Bvarset+4:
> -	case Bvarset+5:
> +	CASE (Bvarset):
> +	CASE (Bvarset1):
> +	CASE (Bvarset2):
> +	CASE (Bvarset3):
> +	CASE (Bvarset4):
> +	CASE (Bvarset5):
>  	  op -= Bvarset;
>  	  goto varset;
 
> -	case Bvarset+7:
> +	CASE (Bvarset7):
>  	  op = FETCH2;
>  	  goto varset;
 
> -	case Bvarset+6:
> +	CASE (Bvarset6):
>  	  op = FETCH;
>  	varset:
>  	  {
> @@ -713,54 +821,54 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  	      }
>  	  }
>  	  (void) POP;
> -	  break;
> +	  NEXT;
 
> -	case Bdup:
> +	CASE (Bdup):
>  	  {
>  	    Lisp_Object v1;
>  	    v1 = TOP;
>  	    PUSH (v1);
> -	    break;
> +	    NEXT;
>  	  }
 
>  	/* ------------------ */
 
> -	case Bvarbind+6:
> +	CASE (Bvarbind6):
>  	  op = FETCH;
>  	  goto varbind;
 
> -	case Bvarbind+7:
> +	CASE (Bvarbind7):
>  	  op = FETCH2;
>  	  goto varbind;
 
> -	case Bvarbind:
> -	case Bvarbind+1:
> -	case Bvarbind+2:
> -	case Bvarbind+3:
> -	case Bvarbind+4:
> -	case Bvarbind+5:
> +	CASE (Bvarbind):
> +	CASE (Bvarbind1):
> +	CASE (Bvarbind2):
> +	CASE (Bvarbind3):
> +	CASE (Bvarbind4):
> +	CASE (Bvarbind5):
>  	  op -= Bvarbind;
>  	varbind:
>  	  /* Specbind can signal and thus GC.  */
>  	  BEFORE_POTENTIAL_GC ();
>  	  specbind (vectorp[op], POP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bcall+6:
> +	CASE (Bcall6):
>  	  op = FETCH;
>  	  goto docall;
 
> -	case Bcall+7:
> +	CASE (Bcall7):
>  	  op = FETCH2;
>  	  goto docall;
 
> -	case Bcall:
> -	case Bcall+1:
> -	case Bcall+2:
> -	case Bcall+3:
> -	case Bcall+4:
> -	case Bcall+5:
> +	CASE (Bcall):
> +	CASE (Bcall1):
> +	CASE (Bcall2):
> +	CASE (Bcall3):
> +	CASE (Bcall4):
> +	CASE (Bcall5):
>  	  op -= Bcall;
>  	docall:
>  	  {
> @@ -783,47 +891,47 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  #endif
>  	    TOP = Ffuncall (op + 1, &TOP);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bunbind+6:
> +	CASE (Bunbind6):
>  	  op = FETCH;
>  	  goto dounbind;
 
> -	case Bunbind+7:
> +	CASE (Bunbind7):
>  	  op = FETCH2;
>  	  goto dounbind;
 
> -	case Bunbind:
> -	case Bunbind+1:
> -	case Bunbind+2:
> -	case Bunbind+3:
> -	case Bunbind+4:
> -	case Bunbind+5:
> +	CASE (Bunbind):
> +	CASE (Bunbind1):
> +	CASE (Bunbind2):
> +	CASE (Bunbind3):
> +	CASE (Bunbind4):
> +	CASE (Bunbind5):
>  	  op -= Bunbind;
>  	dounbind:
>  	  BEFORE_POTENTIAL_GC ();
>  	  unbind_to (SPECPDL_INDEX () - op, Qnil);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bunbind_all:	/* Obsolete.  Never used.  */
> +	CASE (Bunbind_all):	/* Obsolete.  Never used.  */
>  	  /* To unbind back to the beginning of this frame.  Not used yet,
>  	     but will be needed for tail-recursion elimination.  */
>  	  BEFORE_POTENTIAL_GC ();
>  	  unbind_to (count, Qnil);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bgoto:
> +	CASE (Bgoto):
>  	  MAYBE_GC ();
>  	  BYTE_CODE_QUIT;
>  	  op = FETCH2;    /* pc = FETCH2 loses since FETCH2 contains pc++ */
>  	  CHECK_RANGE (op);
>  	  stack.pc = stack.byte_string_start + op;
> -	  break;
> +	  NEXT;
 
> -	case Bgotoifnonnil:
> +	CASE (Bgotoifnonnil):
>  	  {
>  	    Lisp_Object v1;
>  	    MAYBE_GC ();
> @@ -835,10 +943,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  		CHECK_RANGE (op);
>  		stack.pc = stack.byte_string_start + op;
>  	      }
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bgotoifnilelsepop:
> +	CASE (Bgotoifnilelsepop):
>  	  MAYBE_GC ();
>  	  op = FETCH2;
>  	  if (NILP (TOP))
> @@ -848,9 +956,9 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  	      stack.pc = stack.byte_string_start + op;
>  	    }
>  	  else DISCARD (1);
> -	  break;
> +	  NEXT;
 
> -	case Bgotoifnonnilelsepop:
> +	CASE (Bgotoifnonnilelsepop):
>  	  MAYBE_GC ();
>  	  op = FETCH2;
>  	  if (!NILP (TOP))
> @@ -860,15 +968,15 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  	      stack.pc = stack.byte_string_start + op;
>  	    }
>  	  else DISCARD (1);
> -	  break;
> +	  NEXT;
 
> -	case BRgoto:
> +	CASE (BRgoto):
>  	  MAYBE_GC ();
>  	  BYTE_CODE_QUIT;
>  	  stack.pc += (int) *stack.pc - 127;
> -	  break;
> +	  NEXT;
 
> -	case BRgotoifnil:
> +	CASE (BRgotoifnil):
>  	  {
>  	    Lisp_Object v1;
>  	    MAYBE_GC ();
> @@ -879,10 +987,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  		stack.pc += (int) *stack.pc - 128;
>  	      }
>  	    stack.pc++;
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case BRgotoifnonnil:
> +	CASE (BRgotoifnonnil):
>  	  {
>  	    Lisp_Object v1;
>  	    MAYBE_GC ();
> @@ -893,10 +1001,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  		stack.pc += (int) *stack.pc - 128;
>  	      }
>  	    stack.pc++;
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case BRgotoifnilelsepop:
> +	CASE (BRgotoifnilelsepop):
>  	  MAYBE_GC ();
>  	  op = *stack.pc++;
>  	  if (NILP (TOP))
> @@ -905,9 +1013,9 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  	      stack.pc += op - 128;
>  	    }
>  	  else DISCARD (1);
> -	  break;
> +	  NEXT;
 
> -	case BRgotoifnonnilelsepop:
> +	CASE (BRgotoifnonnilelsepop):
>  	  MAYBE_GC ();
>  	  op = *stack.pc++;
>  	  if (!NILP (TOP))
> @@ -916,31 +1024,31 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  	      stack.pc += op - 128;
>  	    }
>  	  else DISCARD (1);
> -	  break;
> +	  NEXT;
 
> -	case Breturn:
> +	CASE (Breturn):
>  	  result = POP;
>  	  goto exit;
 
> -	case Bdiscard:
> +	CASE (Bdiscard):
>  	  DISCARD (1);
> -	  break;
> +	  NEXT;
 
> -	case Bconstant2:
> +	CASE (Bconstant2):
>  	  PUSH (vectorp[FETCH2]);
> -	  break;
> +	  NEXT;
 
> -	case Bsave_excursion:
> +	CASE (Bsave_excursion):
>  	  record_unwind_protect (save_excursion_restore,
>  				 save_excursion_save ());
> -	  break;
> +	  NEXT;
 
> -	case Bsave_current_buffer: /* Obsolete since ??.  */
> -	case Bsave_current_buffer_1:
> +	CASE (Bsave_current_buffer): /* Obsolete since ??.  */
> +	CASE (Bsave_current_buffer_1):
>  	  record_unwind_protect (set_buffer_if_live, Fcurrent_buffer ());
> -	  break;
> +	  NEXT;
 
> -	case Bsave_window_excursion: /* Obsolete since 24.1.  */
> +	CASE (Bsave_window_excursion): /* Obsolete since 24.1.  */
>  	  {
>  	    register ptrdiff_t count1 = SPECPDL_INDEX ();
>  	    record_unwind_protect (Fset_window_configuration,
> @@ -949,29 +1057,29 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  	    TOP = Fprogn (TOP);
>  	    unbind_to (count1, TOP);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bsave_restriction:
> +	CASE (Bsave_restriction):
>  	  record_unwind_protect (save_restriction_restore,
>  				 save_restriction_save ());
> -	  break;
> +	  NEXT;
 
> -	case Bcatch:		/* FIXME: ill-suited for lexbind.  */
> +	CASE (Bcatch):		/* FIXME: ill-suited for lexbind.  */
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = internal_catch (TOP, eval_sub, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bunwind_protect:	/* FIXME: avoid closure for lexbind.  */
> +	CASE (Bunwind_protect):	/* FIXME: avoid closure for lexbind.  */
>  	  record_unwind_protect (Fprogn, POP);
> -	  break;
> +	  NEXT;
 
> -	case Bcondition_case:	/* FIXME: ill-suited for lexbind.  */
> +	CASE (Bcondition_case):	/* FIXME: ill-suited for lexbind.  */
>  	  {
>  	    Lisp_Object handlers, body;
>  	    handlers = POP;
> @@ -979,18 +1087,18 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  	    BEFORE_POTENTIAL_GC ();
>  	    TOP = internal_lisp_condition_case (TOP, body, handlers);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Btemp_output_buffer_setup: /* Obsolete since 24.1.  */
> +	CASE (Btemp_output_buffer_setup): /* Obsolete since 24.1.  */
>  	  BEFORE_POTENTIAL_GC ();
>  	  CHECK_STRING (TOP);
>  	  temp_output_buffer_setup (SSDATA (TOP));
>  	  AFTER_POTENTIAL_GC ();
>  	  TOP = Vstandard_output;
> -	  break;
> +	  NEXT;
 
> -	case Btemp_output_buffer_show: /* Obsolete since 24.1.  */
> +	CASE (Btemp_output_buffer_show): /* Obsolete since 24.1.  */
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
> @@ -1000,10 +1108,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  	    /* pop binding of standard-output */
>  	    unbind_to (SPECPDL_INDEX () - 1, Qnil);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bnth:
> +	CASE (Bnth):
>  	  {
>  	    Lisp_Object v1, v2;
>  	    EMACS_INT n;
> @@ -1018,173 +1126,173 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  	    immediate_quit = 0;
>  	    TOP = CAR (v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bsymbolp:
> +	CASE (Bsymbolp):
>  	  TOP = SYMBOLP (TOP) ? Qt : Qnil;
> -	  break;
> +	  NEXT;
 
> -	case Bconsp:
> +	CASE (Bconsp):
>  	  TOP = CONSP (TOP) ? Qt : Qnil;
> -	  break;
> +	  NEXT;
 
> -	case Bstringp:
> +	CASE (Bstringp):
>  	  TOP = STRINGP (TOP) ? Qt : Qnil;
> -	  break;
> +	  NEXT;
 
> -	case Blistp:
> +	CASE (Blistp):
>  	  TOP = CONSP (TOP) || NILP (TOP) ? Qt : Qnil;
> -	  break;
> +	  NEXT;
 
> -	case Bnot:
> +	CASE (Bnot):
>  	  TOP = NILP (TOP) ? Qt : Qnil;
> -	  break;
> +	  NEXT;
 
> -	case Bcons:
> +	CASE (Bcons):
>  	  {
>  	    Lisp_Object v1;
>  	    v1 = POP;
>  	    TOP = Fcons (TOP, v1);
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Blist1:
> +	CASE (Blist1):
>  	  TOP = Fcons (TOP, Qnil);
> -	  break;
> +	  NEXT;
 
> -	case Blist2:
> +	CASE (Blist2):
>  	  {
>  	    Lisp_Object v1;
>  	    v1 = POP;
>  	    TOP = Fcons (TOP, Fcons (v1, Qnil));
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Blist3:
> +	CASE (Blist3):
>  	  DISCARD (2);
>  	  TOP = Flist (3, &TOP);
> -	  break;
> +	  NEXT;
 
> -	case Blist4:
> +	CASE (Blist4):
>  	  DISCARD (3);
>  	  TOP = Flist (4, &TOP);
> -	  break;
> +	  NEXT;
 
> -	case BlistN:
> +	CASE (BlistN):
>  	  op = FETCH;
>  	  DISCARD (op - 1);
>  	  TOP = Flist (op, &TOP);
> -	  break;
> +	  NEXT;
 
> -	case Blength:
> +	CASE (Blength):
>  	  BEFORE_POTENTIAL_GC ();
>  	  TOP = Flength (TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Baref:
> +	CASE (Baref):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Faref (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Baset:
> +	CASE (Baset):
>  	  {
>  	    Lisp_Object v1, v2;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v2 = POP; v1 = POP;
>  	    TOP = Faset (TOP, v1, v2);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bsymbol_value:
> +	CASE (Bsymbol_value):
>  	  BEFORE_POTENTIAL_GC ();
>  	  TOP = Fsymbol_value (TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bsymbol_function:
> +	CASE (Bsymbol_function):
>  	  BEFORE_POTENTIAL_GC ();
>  	  TOP = Fsymbol_function (TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bset:
> +	CASE (Bset):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Fset (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bfset:
> +	CASE (Bfset):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Ffset (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bget:
> +	CASE (Bget):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Fget (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bsubstring:
> +	CASE (Bsubstring):
>  	  {
>  	    Lisp_Object v1, v2;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v2 = POP; v1 = POP;
>  	    TOP = Fsubstring (TOP, v1, v2);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bconcat2:
> +	CASE (Bconcat2):
>  	  BEFORE_POTENTIAL_GC ();
>  	  DISCARD (1);
>  	  TOP = Fconcat (2, &TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bconcat3:
> +	CASE (Bconcat3):
>  	  BEFORE_POTENTIAL_GC ();
>  	  DISCARD (2);
>  	  TOP = Fconcat (3, &TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bconcat4:
> +	CASE (Bconcat4):
>  	  BEFORE_POTENTIAL_GC ();
>  	  DISCARD (3);
>  	  TOP = Fconcat (4, &TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case BconcatN:
> +	CASE (BconcatN):
>  	  op = FETCH;
>  	  BEFORE_POTENTIAL_GC ();
>  	  DISCARD (op - 1);
>  	  TOP = Fconcat (op, &TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bsub1:
> +	CASE (Bsub1):
>  	  {
>  	    Lisp_Object v1;
>  	    v1 = TOP;
> @@ -1199,10 +1307,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  		TOP = Fsub1 (v1);
>  		AFTER_POTENTIAL_GC ();
>  	      }
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Badd1:
> +	CASE (Badd1):
>  	  {
>  	    Lisp_Object v1;
>  	    v1 = TOP;
> @@ -1217,10 +1325,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  		TOP = Fadd1 (v1);
>  		AFTER_POTENTIAL_GC ();
>  	      }
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Beqlsign:
> +	CASE (Beqlsign):
>  	  {
>  	    Lisp_Object v1, v2;
>  	    BEFORE_POTENTIAL_GC ();
> @@ -1238,57 +1346,57 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  	      }
>  	    else
>  	      TOP = (XINT (v1) == XINT (v2) ? Qt : Qnil);
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bgtr:
> +	CASE (Bgtr):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Fgtr (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Blss:
> +	CASE (Blss):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Flss (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bleq:
> +	CASE (Bleq):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Fleq (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bgeq:
> +	CASE (Bgeq):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Fgeq (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bdiff:
> +	CASE (Bdiff):
>  	  BEFORE_POTENTIAL_GC ();
>  	  DISCARD (1);
>  	  TOP = Fminus (2, &TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bnegate:
> +	CASE (Bnegate):
>  	  {
>  	    Lisp_Object v1;
>  	    v1 = TOP;
> @@ -1303,209 +1411,209 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  		TOP = Fminus (1, &TOP);
>  		AFTER_POTENTIAL_GC ();
>  	      }
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bplus:
> +	CASE (Bplus):
>  	  BEFORE_POTENTIAL_GC ();
>  	  DISCARD (1);
>  	  TOP = Fplus (2, &TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bmax:
> +	CASE (Bmax):
>  	  BEFORE_POTENTIAL_GC ();
>  	  DISCARD (1);
>  	  TOP = Fmax (2, &TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bmin:
> +	CASE (Bmin):
>  	  BEFORE_POTENTIAL_GC ();
>  	  DISCARD (1);
>  	  TOP = Fmin (2, &TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bmult:
> +	CASE (Bmult):
>  	  BEFORE_POTENTIAL_GC ();
>  	  DISCARD (1);
>  	  TOP = Ftimes (2, &TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bquo:
> +	CASE (Bquo):
>  	  BEFORE_POTENTIAL_GC ();
>  	  DISCARD (1);
>  	  TOP = Fquo (2, &TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Brem:
> +	CASE (Brem):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Frem (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bpoint:
> +	CASE (Bpoint):
>  	  {
>  	    Lisp_Object v1;
>  	    XSETFASTINT (v1, PT);
>  	    PUSH (v1);
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bgoto_char:
> +	CASE (Bgoto_char):
>  	  BEFORE_POTENTIAL_GC ();
>  	  TOP = Fgoto_char (TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Binsert:
> +	CASE (Binsert):
>  	  BEFORE_POTENTIAL_GC ();
>  	  TOP = Finsert (1, &TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case BinsertN:
> +	CASE (BinsertN):
>  	  op = FETCH;
>  	  BEFORE_POTENTIAL_GC ();
>  	  DISCARD (op - 1);
>  	  TOP = Finsert (op, &TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bpoint_max:
> +	CASE (Bpoint_max):
>  	  {
>  	    Lisp_Object v1;
>  	    XSETFASTINT (v1, ZV);
>  	    PUSH (v1);
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bpoint_min:
> +	CASE (Bpoint_min):
>  	  {
>  	    Lisp_Object v1;
>  	    XSETFASTINT (v1, BEGV);
>  	    PUSH (v1);
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bchar_after:
> +	CASE (Bchar_after):
>  	  BEFORE_POTENTIAL_GC ();
>  	  TOP = Fchar_after (TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bfollowing_char:
> +	CASE (Bfollowing_char):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = Ffollowing_char ();
>  	    AFTER_POTENTIAL_GC ();
>  	    PUSH (v1);
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bpreceding_char:
> +	CASE (Bpreceding_char):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = Fprevious_char ();
>  	    AFTER_POTENTIAL_GC ();
>  	    PUSH (v1);
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bcurrent_column:
> +	CASE (Bcurrent_column):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    XSETFASTINT (v1, current_column ());
>  	    AFTER_POTENTIAL_GC ();
>  	    PUSH (v1);
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bindent_to:
> +	CASE (Bindent_to):
>  	  BEFORE_POTENTIAL_GC ();
>  	  TOP = Findent_to (TOP, Qnil);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Beolp:
> +	CASE (Beolp):
>  	  PUSH (Feolp ());
> -	  break;
> +	  NEXT;
 
> -	case Beobp:
> +	CASE (Beobp):
>  	  PUSH (Feobp ());
> -	  break;
> +	  NEXT;
 
> -	case Bbolp:
> +	CASE (Bbolp):
>  	  PUSH (Fbolp ());
> -	  break;
> +	  NEXT;
 
> -	case Bbobp:
> +	CASE (Bbobp):
>  	  PUSH (Fbobp ());
> -	  break;
> +	  NEXT;
 
> -	case Bcurrent_buffer:
> +	CASE (Bcurrent_buffer):
>  	  PUSH (Fcurrent_buffer ());
> -	  break;
> +	  NEXT;
 
> -	case Bset_buffer:
> +	CASE (Bset_buffer):
>  	  BEFORE_POTENTIAL_GC ();
>  	  TOP = Fset_buffer (TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Binteractive_p:	/* Obsolete since 24.1.  */
> +	CASE (Binteractive_p):	/* Obsolete since 24.1.  */
>  	  PUSH (Finteractive_p ());
> -	  break;
> +	  NEXT;
 
> -	case Bforward_char:
> +	CASE (Bforward_char):
>  	  BEFORE_POTENTIAL_GC ();
>  	  TOP = Fforward_char (TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bforward_word:
> +	CASE (Bforward_word):
>  	  BEFORE_POTENTIAL_GC ();
>  	  TOP = Fforward_word (TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bskip_chars_forward:
> +	CASE (Bskip_chars_forward):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Fskip_chars_forward (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bskip_chars_backward:
> +	CASE (Bskip_chars_backward):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Fskip_chars_backward (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bforward_line:
> +	CASE (Bforward_line):
>  	  BEFORE_POTENTIAL_GC ();
>  	  TOP = Fforward_line (TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bchar_syntax:
> +	CASE (Bchar_syntax):
>  	  {
>  	    int c;
 
> @@ -1517,51 +1625,51 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  	      MAKE_CHAR_MULTIBYTE (c);
>  	    XSETFASTINT (TOP, syntax_code_spec[(int) SYNTAX (c)]);
>  	  }
> -	  break;
> +	  NEXT;
 
> -	case Bbuffer_substring:
> +	CASE (Bbuffer_substring):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Fbuffer_substring (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bdelete_region:
> +	CASE (Bdelete_region):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Fdelete_region (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bnarrow_to_region:
> +	CASE (Bnarrow_to_region):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Fnarrow_to_region (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bwiden:
> +	CASE (Bwiden):
>  	  BEFORE_POTENTIAL_GC ();
>  	  PUSH (Fwiden ());
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bend_of_line:
> +	CASE (Bend_of_line):
>  	  BEFORE_POTENTIAL_GC ();
>  	  TOP = Fend_of_line (TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bset_marker:
> +	CASE (Bset_marker):
>  	  {
>  	    Lisp_Object v1, v2;
>  	    BEFORE_POTENTIAL_GC ();
> @@ -1569,72 +1677,72 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  	    v2 = POP;
>  	    TOP = Fset_marker (TOP, v2, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bmatch_beginning:
> +	CASE (Bmatch_beginning):
>  	  BEFORE_POTENTIAL_GC ();
>  	  TOP = Fmatch_beginning (TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bmatch_end:
> +	CASE (Bmatch_end):
>  	  BEFORE_POTENTIAL_GC ();
>  	  TOP = Fmatch_end (TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bupcase:
> +	CASE (Bupcase):
>  	  BEFORE_POTENTIAL_GC ();
>  	  TOP = Fupcase (TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bdowncase:
> +	CASE (Bdowncase):
>  	  BEFORE_POTENTIAL_GC ();
>  	  TOP = Fdowncase (TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	break;
> +	NEXT;
 
> -	case Bstringeqlsign:
> +      CASE (Bstringeqlsign):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Fstring_equal (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bstringlss:
> +	CASE (Bstringlss):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Fstring_lessp (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bequal:
> +	CASE (Bequal):
>  	  {
>  	    Lisp_Object v1;
>  	    v1 = POP;
>  	    TOP = Fequal (TOP, v1);
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bnthcdr:
> +	CASE (Bnthcdr):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Fnthcdr (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Belt:
> +	CASE (Belt):
>  	  {
>  	    Lisp_Object v1, v2;
>  	    if (CONSP (TOP))
> @@ -1660,87 +1768,91 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  		TOP = Felt (TOP, v1);
>  		AFTER_POTENTIAL_GC ();
>  	      }
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bmember:
> +	CASE (Bmember):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Fmember (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bassq:
> +	CASE (Bassq):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Fassq (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bnreverse:
> +	CASE (Bnreverse):
>  	  BEFORE_POTENTIAL_GC ();
>  	  TOP = Fnreverse (TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bsetcar:
> +	CASE (Bsetcar):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Fsetcar (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bsetcdr:
> +	CASE (Bsetcdr):
>  	  {
>  	    Lisp_Object v1;
>  	    BEFORE_POTENTIAL_GC ();
>  	    v1 = POP;
>  	    TOP = Fsetcdr (TOP, v1);
>  	    AFTER_POTENTIAL_GC ();
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bcar_safe:
> +	CASE (Bcar_safe):
>  	  {
>  	    Lisp_Object v1;
>  	    v1 = TOP;
>  	    TOP = CAR_SAFE (v1);
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bcdr_safe:
> +	CASE (Bcdr_safe):
>  	  {
>  	    Lisp_Object v1;
>  	    v1 = TOP;
>  	    TOP = CDR_SAFE (v1);
> -	    break;
> +	    NEXT;
>  	  }
 
> -	case Bnconc:
> +	CASE (Bnconc):
>  	  BEFORE_POTENTIAL_GC ();
>  	  DISCARD (1);
>  	  TOP = Fnconc (2, &TOP);
>  	  AFTER_POTENTIAL_GC ();
> -	  break;
> +	  NEXT;
 
> -	case Bnumberp:
> +	CASE (Bnumberp):
>  	  TOP = (NUMBERP (TOP) ? Qt : Qnil);
> -	  break;
> +	  NEXT;
 
> -	case Bintegerp:
> +	CASE (Bintegerp):
>  	  TOP = INTEGERP (TOP) ? Qt : Qnil;
> -	  break;
> +	  NEXT;
 
>  #ifdef BYTE_CODE_SAFE
> +	  /* These are intentionally written using 'case' syntax,
> +	     because they are incompatible with the threaded
> +	     interpreter.  */
> +
>  	case Bset_mark:
>  	  BEFORE_POTENTIAL_GC ();
>  	  error ("set-mark is an obsolete bytecode");
> @@ -1753,49 +1865,49 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  	  break;
>  #endif
 
> -	case 0:
> +	CASE_ABORT:
>  	  /* Actually this is Bstack_ref with offset 0, but we use Bdup
>  	     for that instead.  */
> -	  /* case Bstack_ref: */
> +	  /* CASE (Bstack_ref): */
>  	  abort ();
 
>  	  /* Handy byte-codes for lexical binding.  */
> -	case Bstack_ref+1:
> -	case Bstack_ref+2:
> -	case Bstack_ref+3:
> -	case Bstack_ref+4:
> -	case Bstack_ref+5:
> +	CASE (Bstack_ref1):
> +	CASE (Bstack_ref2):
> +	CASE (Bstack_ref3):
> +	CASE (Bstack_ref4):
> +	CASE (Bstack_ref5):
>  	  {
>  	    Lisp_Object *ptr = top - (op - Bstack_ref);
>  	    PUSH (*ptr);
> -	    break;
> +	    NEXT;
>  	  }
> -	case Bstack_ref+6:
> +	CASE (Bstack_ref6):
>  	  {
>  	    Lisp_Object *ptr = top - (FETCH);
>  	    PUSH (*ptr);
> -	    break;
> +	    NEXT;
>  	  }
> -	case Bstack_ref+7:
> +	CASE (Bstack_ref7):
>  	  {
>  	    Lisp_Object *ptr = top - (FETCH2);
>  	    PUSH (*ptr);
> -	    break;
> +	    NEXT;
>  	  }
> -	case Bstack_set:
> +	CASE (Bstack_set):
>  	  /* stack-set-0 = discard; stack-set-1 = discard-1-preserve-tos.  */
>  	  {
>  	    Lisp_Object *ptr = top - (FETCH);
>  	    *ptr = POP;
> -	    break;
> +	    NEXT;
>  	  }
> -	case Bstack_set2:
> +	CASE (Bstack_set2):
>  	  {
>  	    Lisp_Object *ptr = top - (FETCH2);
>  	    *ptr = POP;
> -	    break;
> +	    NEXT;
>  	  }
> -	case BdiscardN:
> +	CASE (BdiscardN):
>  	  op = FETCH;
>  	  if (op & 0x80)
>  	    {
> @@ -1803,10 +1915,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  	      top[-op] = TOP;
>  	    }
>  	  DISCARD (op);
> -	  break;
> +	  NEXT;
 
> -	case 255:
> -	default:
> +	CASE_DEFAULT
> +	CASE (Bconstant):
>  #ifdef BYTE_CODE_SAFE
>  	  if (op < Bconstant)
>  	    {
> @@ -1820,6 +1932,7 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
>  #else
>  	  PUSH (vectorp[op - Bconstant]);
>  #endif
> +	  NEXT;
>  	}
>      }
 



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-07-03 16:58             ` Stefan Monnier
@ 2012-07-03 17:22               ` Tom Tromey
  2012-07-03 17:53                 ` Aurélien Aptel
  2012-07-03 23:02                 ` Stefan Monnier
  0 siblings, 2 replies; 23+ messages in thread
From: Tom Tromey @ 2012-07-03 17:22 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Mohamed Ben-Ahssene, emacs-devel

>>>>> "Stefan" == Stefan Monnier <monnier@iro.umontreal.ca> writes:

Stefan> Looks OK.  I'm not too fond of all the backslashes and the `value'
Stefan> argument to DEFINE, but I guess they're OK.

I took the "value" approach because the bytecode values are fixed, and I
thought it was safer to be explicit about them.

The backslashes are just needed because I made one big #define.
Another approach (used in gcc, gdb, etc) is to put the opcode defines
into a ".def" file and then include it in multiple places.
I can do that if you prefer.

Stefan> One more nitpick.  You say:

>> +/* If BYTE_CODE_THREADED is defined, then the interpreter will be
>> +   indirect threaded, using GCC's computed goto extension.  This is
>> +   incompatible with BYTE_CODE_SAFE and BYTE_CODE_METER.  */

Stefan> But, IIUC there is no incompatibility, really.  It's just that the
Stefan> current code doesn't handle that particular combination, right?
Stefan> If so, please make it clear in the comment.

Correct.  I'll fix.

Tom



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-07-03 17:22               ` Tom Tromey
@ 2012-07-03 17:53                 ` Aurélien Aptel
  2012-07-03 19:24                   ` Tom Tromey
  2012-07-03 23:02                 ` Stefan Monnier
  1 sibling, 1 reply; 23+ messages in thread
From: Aurélien Aptel @ 2012-07-03 17:53 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Mohamed Ben-Ahssene, Stefan Monnier, emacs-devel

Is there a reason why all the opcode definitions (e.g. #Bcar 0100) are
not using an enum? It's easier to read and it logically groups
constants.



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-07-03 17:53                 ` Aurélien Aptel
@ 2012-07-03 19:24                   ` Tom Tromey
  0 siblings, 0 replies; 23+ messages in thread
From: Tom Tromey @ 2012-07-03 19:24 UTC (permalink / raw)
  To: Aurélien Aptel; +Cc: Mohamed Ben-Ahssene, Stefan Monnier, emacs-devel

Aurélien> Is there a reason why all the opcode definitions (e.g. #Bcar 0100) are
Aurélien> not using an enum? It's easier to read and it logically groups
Aurélien> constants.

Just history.

Tom



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-07-03 14:45           ` Tom Tromey
  2012-07-03 16:58             ` Stefan Monnier
@ 2012-07-03 19:52             ` John Wiegley
  1 sibling, 0 replies; 23+ messages in thread
From: John Wiegley @ 2012-07-03 19:52 UTC (permalink / raw)
  To: emacs-devel

>>>>> Tom Tromey <tromey@redhat.com> writes:

> Sure, how about the appended?

> I built it both ways (once by commenting out the BYTE_CODE_THREADED define)
> on x86-64 Fedora 16.

Very nice, Tom.  I was just reading an article this week on the new low-level
interpreter LLint used by JavaScriptCore (http://j.mp/Lktvsv), which uses this
exact same trick.

John



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-07-03 17:22               ` Tom Tromey
  2012-07-03 17:53                 ` Aurélien Aptel
@ 2012-07-03 23:02                 ` Stefan Monnier
  2012-07-06 20:22                   ` Tom Tromey
  2012-07-09 19:12                   ` Tom Tromey
  1 sibling, 2 replies; 23+ messages in thread
From: Stefan Monnier @ 2012-07-03 23:02 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Mohamed Ben-Ahssene, emacs-devel

> I took the "value" approach because the bytecode values are fixed, and I
> thought it was safer to be explicit about them.
> The backslashes are just needed because I made one big #define.

Yes, as I said, I think it's OK.  Maybe better would be if the threaded
version actually checked that the `value' is correct (i.e. is one more
than the previous one) rather than just ignore that argument.

> Another approach (used in gcc, gdb, etc) is to put the opcode defines
> into a ".def" file and then include it in multiple places.

I'm not sure it's better, since, as you say, the values are fixed by
external factors.

Feel free to install,


        Stefan



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-07-03 23:02                 ` Stefan Monnier
@ 2012-07-06 20:22                   ` Tom Tromey
  2012-07-06 21:39                     ` Stefan Monnier
  2012-07-09 19:12                   ` Tom Tromey
  1 sibling, 1 reply; 23+ messages in thread
From: Tom Tromey @ 2012-07-06 20:22 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Mohamed Ben-Ahssene, emacs-devel

Stefan> Yes, as I said, I think it's OK.  Maybe better would be if the threaded
Stefan> version actually checked that the `value' is correct (i.e. is one more
Stefan> than the previous one) rather than just ignore that argument.

I don't know how to check that aside from writing a loop -- slow -- and
anyway there's no requirement that the values be in any order.  

The labels do indirectly use the values:

#define LABEL(OP) [OP] = &&insn_ ## OP

The [OP] on the left-hand-side is using the enum constant to decide
which array slot to initialize.  It could just as well use the value,
same difference.

Tom



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-07-03 13:19         ` Stefan Monnier
  2012-07-03 14:45           ` Tom Tromey
@ 2012-07-06 20:27           ` Tom Tromey
  1 sibling, 0 replies; 23+ messages in thread
From: Tom Tromey @ 2012-07-06 20:27 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Stefan> There's a lot of room for improvement in the regexp-matcher as
Stefan> well, yes.  If we could generate the code via something like
Stefan> vmgen it would be even better.

Yeah.  Translating the bytecode engine to vmgen is not trivial, but at
first blush it seems doable.  This would have a few possible advantages
-- TOS caching, superinstructions, possibly better micro-optimization.

I didn't look at the regexp matcher with this in mind.


It also occurred to me recently that the computed goto treatment could
perhaps be applied to mark_object.

Tom



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-07-06 20:22                   ` Tom Tromey
@ 2012-07-06 21:39                     ` Stefan Monnier
  0 siblings, 0 replies; 23+ messages in thread
From: Stefan Monnier @ 2012-07-06 21:39 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Mohamed Ben-Ahssene, emacs-devel

> The labels do indirectly use the values:
> #define LABEL(OP) [OP] = &&insn_ ## OP

Oh, I missed it, great, sorry for being dense,


        Stefan



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-07-03 23:02                 ` Stefan Monnier
  2012-07-06 20:22                   ` Tom Tromey
@ 2012-07-09 19:12                   ` Tom Tromey
  2012-07-10 10:31                     ` Stefan Monnier
  1 sibling, 1 reply; 23+ messages in thread
From: Tom Tromey @ 2012-07-09 19:12 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Mohamed Ben-Ahssene, emacs-devel

Stefan> Yes, as I said, I think it's OK.

How do you feel about this ChangeLog entry?

+2012-07-09  Tom Tromey  <tromey@redhat.com>
+
+	* bytecode.c (BYTE_CODE_THREADED): New define.
+	(BYTE_CODES): New define.  Replaces all old byte-code defines.
+	(enum byte_code_op): New.
+	(CASE, NEXT, FIRST, CASE_DEFAULT, CASE_ABORT): New defines.
+	(exec_byte_code): Use new defines.


I wasn't sure if you wanted me to spell out all the Bmumble defines that
were replaced.

Tom




^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: Inefficiency in Bgotoifnil byte-code instruction
  2012-07-09 19:12                   ` Tom Tromey
@ 2012-07-10 10:31                     ` Stefan Monnier
  0 siblings, 0 replies; 23+ messages in thread
From: Stefan Monnier @ 2012-07-10 10:31 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Mohamed Ben-Ahssene, emacs-devel

> How do you feel about this ChangeLog entry?

Looks OK.  Nit picks:

> +	* bytecode.c (BYTE_CODE_THREADED): New define.

We usually call cpp definitions "macros" rather than "defines".
Also, add a line saying something like "Use indirect threading when
applicable".

> +	(BYTE_CODES): New define.  Replaces all old byte-code defines.
> +	(enum byte_code_op): New.

This one is a new *type*.

> +	(CASE, NEXT, FIRST, CASE_DEFAULT, CASE_ABORT): New defines.
> +	(exec_byte_code): Use new defines.

And this last one can just be "Use them".

> I wasn't sure if you wanted me to spell out all the Bmumble defines that
> were replaced.

No, that's not necessary.


        Stefan



^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2012-07-10 10:31 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-06-28  3:01 Inefficiency in Bgotoifnil byte-code instruction John Wiegley
2012-06-28  4:38 ` Stefan Monnier
2012-06-29 16:39   ` Tom Tromey
2012-06-30  4:07     ` Stefan Monnier
2012-07-02 19:17       ` Tom Tromey
2012-07-03 13:19         ` Stefan Monnier
2012-07-03 14:45           ` Tom Tromey
2012-07-03 16:58             ` Stefan Monnier
2012-07-03 17:22               ` Tom Tromey
2012-07-03 17:53                 ` Aurélien Aptel
2012-07-03 19:24                   ` Tom Tromey
2012-07-03 23:02                 ` Stefan Monnier
2012-07-06 20:22                   ` Tom Tromey
2012-07-06 21:39                     ` Stefan Monnier
2012-07-09 19:12                   ` Tom Tromey
2012-07-10 10:31                     ` Stefan Monnier
2012-07-03 19:52             ` John Wiegley
2012-07-06 20:27           ` Tom Tromey
2012-07-02  3:51     ` John Wiegley
2012-07-02 14:08       ` Tom Tromey
2012-06-28  4:41 ` Stefan Monnier
2012-06-28 13:07 ` Richard Stallman
2012-06-28 21:24   ` John Wiegley

Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.