emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] feature/libjit a166e8f 4/4: JIT compiler


From: Tom Tromey
Subject: [Emacs-diffs] feature/libjit a166e8f 4/4: JIT compiler
Date: Mon, 13 Aug 2018 20:16:59 -0400 (EDT)

branch: feature/libjit
commit a166e8fabda6fbc7a49d2da2c9ae99efc2e3c23e
Author: Tom Tromey <address@hidden>
Commit: Tom Tromey <address@hidden>

    JIT compiler
    
    * lisp/emacs-lisp/jit-support.el: New file.
    * src/alloc.c (make_byte_code): Remove.
    (Fmake_byte_code): Rewrite.
    * src/data.c (Fsubr_arity, notify_variable_watchers): Update.
    * src/emacs.c (main): Call syms_of_jit, init_jit.
    * src/eval.c (eval_sub, Fapply, FUNCTIONP, Ffuncall, funcall_subr)
    (funcall_lambda): Update.
    * src/jit.c: New file.
    * src/lisp.h (struct subr_function): New struct, extracted from
    Lisp_Subr.
    (SUBR_MAX_ARGS): New define.
    (struct Lisp_Subr): Use struct subr_function.
    (COMPILED_JIT_CODE): New constant.
    (DEFUN): Update.
    (make_byte_code): Don't declare.
    (funcall_subr): Add error_obj argument.
    (syms_of_jit, init_jit, emacs_jit_compile): Declare.
    * src/lread.c (read1): Use Fmake_byte_code.
    * test/src/jit-tests.el: New file.
---
 lisp/emacs-lisp/jit-support.el |   37 +
 src/alloc.c                    |   43 +-
 src/data.c                     |    7 +-
 src/emacs.c                    |    8 +
 src/eval.c                     |   92 +-
 src/jit.c                      | 2367 ++++++++++++++++++++++++++++++++++++++++
 src/lisp.h                     |   37 +-
 src/lread.c                    |    3 +-
 test/src/jit-tests.el          |  304 ++++++
 9 files changed, 2825 insertions(+), 73 deletions(-)

diff --git a/lisp/emacs-lisp/jit-support.el b/lisp/emacs-lisp/jit-support.el
new file mode 100644
index 0000000..b318f3b
--- /dev/null
+++ b/lisp/emacs-lisp/jit-support.el
@@ -0,0 +1,37 @@
+;;; jit-support.el --- helper functions for JIT compilation -*- 
lexical-binding: t -*-
+
+;; Copyright (C) 2018 Free Software Foundation, Inc.
+
+;; Keywords: lisp
+;; Package: emacs
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;;###autoload
+(defun jit-disassemble (func)
+  (interactive "aDisassemble function: ")
+  (when (symbolp func)
+    (setf func (symbol-function func)))
+  (let ((str (jit-disassemble-to-string func)))
+    (with-current-buffer (get-buffer-create "*JIT*")
+      (erase-buffer)
+      (save-excursion
+        (insert str))
+      (pop-to-buffer (current-buffer)))))
+
+(provide 'jit-support)
+
+;;; jit-support.el ends here
diff --git a/src/alloc.c b/src/alloc.c
index 337668f..ca70c0c 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -3515,23 +3515,6 @@ usage: (vector &rest OBJECTS)  */)
   return val;
 }
 
-void
-make_byte_code (struct Lisp_Vector *v)
-{
-  /* Don't allow the global zero_vector to become a byte code object.  */
-  eassert (0 < v->header.size);
-
-  if (v->header.size > 1 && STRINGP (v->contents[1])
-      && STRING_MULTIBYTE (v->contents[1]))
-    /* BYTECODE-STRING must have been produced by Emacs 20.2 or the
-       earlier because they produced a raw 8-bit string for byte-code
-       and now such a byte-code string is loaded as multibyte while
-       raw 8-bit characters converted to multibyte form.  Thus, now we
-       must convert them back to the original unibyte form.  */
-    v->contents[1] = Fstring_as_unibyte (v->contents[1]);
-  XSETPVECTYPE (v, PVEC_COMPILED);
-}
-
 DEFUN ("make-byte-code", Fmake_byte_code, Smake_byte_code, 4, MANY, 0,
        doc: /* Create a byte-code object with specified arguments as elements.
 The arguments should be the ARGLIST, bytecode-string BYTE-CODE, constant
@@ -3550,8 +3533,14 @@ stack before executing the byte-code.
 usage: (make-byte-code ARGLIST BYTE-CODE CONSTANTS DEPTH &optional DOCSTRING 
INTERACTIVE-SPEC &rest ELEMENTS)  */)
   (ptrdiff_t nargs, Lisp_Object *args)
 {
-  Lisp_Object val = make_uninit_vector (nargs);
-  struct Lisp_Vector *p = XVECTOR (val);
+  Lisp_Object val;
+  struct Lisp_Vector *p = allocate_pseudovector (COMPILED_JIT_CODE + 1,
+                                                COMPILED_JIT_CODE,
+                                                COMPILED_JIT_CODE + 1,
+                                                PVEC_COMPILED);
+
+  /* Don't allow the global zero_vector to become a byte code object.  */
+  eassert (0 < nargs);
 
   /* We used to purecopy everything here, if purify-flag was set.  This worked
      OK for Emacs-23, but with Emacs-24's lexical binding code, it can be
@@ -3562,7 +3551,21 @@ usage: (make-byte-code ARGLIST BYTE-CODE CONSTANTS DEPTH 
&optional DOCSTRING INT
      to be setcar'd).  */
 
   memcpy (p->contents, args, nargs * sizeof *args);
-  make_byte_code (p);
+  for (int i = nargs; i < COMPILED_JIT_CODE; ++i)
+    p->contents[i] = Qnil;
+
+  /* Not really a Lisp_Object.  */
+  p->contents[COMPILED_JIT_CODE] = (Lisp_Object) NULL;
+
+  if (STRINGP (p->contents[COMPILED_BYTECODE])
+      && STRING_MULTIBYTE (p->contents[COMPILED_BYTECODE]))
+    /* BYTECODE-STRING must have been produced by Emacs 20.2 or the
+       earlier because they produced a raw 8-bit string for byte-code
+       and now such a byte-code string is loaded as multibyte while
+       raw 8-bit characters converted to multibyte form.  Thus, now we
+       must convert them back to the original unibyte form.  */
+    p->contents[COMPILED_BYTECODE] = Fstring_as_unibyte 
(p->contents[COMPILED_BYTECODE]);
+
   XSETCOMPILED (val, p);
   return val;
 }
diff --git a/src/data.c b/src/data.c
index a1215b9..7a0659d 100644
--- a/src/data.c
+++ b/src/data.c
@@ -866,8 +866,8 @@ function with `&rest' args, or `unevalled' for a special 
form.  */)
 {
   short minargs, maxargs;
   CHECK_SUBR (subr);
-  minargs = XSUBR (subr)->min_args;
-  maxargs = XSUBR (subr)->max_args;
+  minargs = XSUBR (subr)->function.min_args;
+  maxargs = XSUBR (subr)->function.max_args;
   return Fcons (make_fixnum (minargs),
                maxargs == MANY ?        Qmany
                : maxargs == UNEVALLED ? Qunevalled
@@ -1571,7 +1571,8 @@ notify_variable_watchers (Lisp_Object symbol,
       if (SUBRP (watcher))
         {
           Lisp_Object args[] = { symbol, newval, operation, where };
-          funcall_subr (XSUBR (watcher), ARRAYELTS (args), args);
+          funcall_subr (watcher, &XSUBR (watcher)->function,
+                       ARRAYELTS (args), args);
         }
       else
         CALLN (Ffuncall, watcher, symbol, newval, operation, where);
diff --git a/src/emacs.c b/src/emacs.c
index 97205d2..25493c2 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -1643,6 +1643,10 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
       syms_of_json ();
 #endif
 
+#ifdef HAVE_LIBJIT
+      syms_of_jit ();
+#endif
+
       keys_of_casefiddle ();
       keys_of_cmds ();
       keys_of_buffer ();
@@ -1663,6 +1667,10 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
 #if defined WINDOWSNT || defined HAVE_NTGUI
       globals_of_w32select ();
 #endif
+
+#ifdef HAVE_LIBJIT
+      init_jit ();
+#endif
     }
 
   init_charset ();
diff --git a/src/eval.c b/src/eval.c
index 8745ba9..de87f9b 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -30,6 +30,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "dispextern.h"
 #include "buffer.h"
 
+#ifdef HAVE_LIBJIT
+#include <jit/jit.h>
+#endif
+
 /* CACHEABLE is ordinarily nothing, except it is 'volatile' if
    necessary to cajole GCC into not warning incorrectly that a
    variable should be volatile.  */
@@ -2230,14 +2234,14 @@ eval_sub (Lisp_Object form)
 
       check_cons_list ();
 
-      if (XFIXNUM (numargs) < XSUBR (fun)->min_args
-         || (XSUBR (fun)->max_args >= 0
-             && XSUBR (fun)->max_args < XFIXNUM (numargs)))
+      if (XFIXNUM (numargs) < XSUBR (fun)->function.min_args
+         || (XSUBR (fun)->function.max_args >= 0
+             && XSUBR (fun)->function.max_args < XFIXNUM (numargs)))
        xsignal2 (Qwrong_number_of_arguments, original_fun, numargs);
 
-      else if (XSUBR (fun)->max_args == UNEVALLED)
-       val = (XSUBR (fun)->function.aUNEVALLED) (args_left);
-      else if (XSUBR (fun)->max_args == MANY)
+      else if (XSUBR (fun)->function.max_args == UNEVALLED)
+       val = (XSUBR (fun)->function.function.aUNEVALLED) (args_left);
+      else if (XSUBR (fun)->function.max_args == MANY)
        {
          /* Pass a vector of evaluated arguments.  */
          Lisp_Object *vals;
@@ -2255,7 +2259,7 @@ eval_sub (Lisp_Object form)
 
          set_backtrace_args (specpdl + count, vals, argnum);
 
-         val = XSUBR (fun)->function.aMANY (argnum, vals);
+         val = XSUBR (fun)->function.function.aMANY (argnum, vals);
 
          check_cons_list ();
          lisp_eval_depth--;
@@ -2268,7 +2272,7 @@ eval_sub (Lisp_Object form)
        }
       else
        {
-         int i, maxargs = XSUBR (fun)->max_args;
+         int i, maxargs = XSUBR (fun)->function.max_args;
 
          for (i = 0; i < maxargs; i++)
            {
@@ -2281,40 +2285,40 @@ eval_sub (Lisp_Object form)
          switch (i)
            {
            case 0:
-             val = (XSUBR (fun)->function.a0 ());
+             val = (XSUBR (fun)->function.function.a0 ());
              break;
            case 1:
-             val = (XSUBR (fun)->function.a1 (argvals[0]));
+             val = (XSUBR (fun)->function.function.a1 (argvals[0]));
              break;
            case 2:
-             val = (XSUBR (fun)->function.a2 (argvals[0], argvals[1]));
+             val = (XSUBR (fun)->function.function.a2 (argvals[0], 
argvals[1]));
              break;
            case 3:
-             val = (XSUBR (fun)->function.a3
+             val = (XSUBR (fun)->function.function.a3
                     (argvals[0], argvals[1], argvals[2]));
              break;
            case 4:
-             val = (XSUBR (fun)->function.a4
+             val = (XSUBR (fun)->function.function.a4
                     (argvals[0], argvals[1], argvals[2], argvals[3]));
              break;
            case 5:
-             val = (XSUBR (fun)->function.a5
+             val = (XSUBR (fun)->function.function.a5
                     (argvals[0], argvals[1], argvals[2], argvals[3],
                      argvals[4]));
              break;
            case 6:
-             val = (XSUBR (fun)->function.a6
+             val = (XSUBR (fun)->function.function.a6
                     (argvals[0], argvals[1], argvals[2], argvals[3],
                      argvals[4], argvals[5]));
              break;
            case 7:
-             val = (XSUBR (fun)->function.a7
+             val = (XSUBR (fun)->function.function.a7
                     (argvals[0], argvals[1], argvals[2], argvals[3],
                      argvals[4], argvals[5], argvals[6]));
              break;
 
            case 8:
-             val = (XSUBR (fun)->function.a8
+             val = (XSUBR (fun)->function.function.a8
                     (argvals[0], argvals[1], argvals[2], argvals[3],
                      argvals[4], argvals[5], argvals[6], argvals[7]));
              break;
@@ -2411,16 +2415,16 @@ usage: (apply FUNCTION &rest ARGUMENTS)  */)
        fun = args[0];
     }
 
-  if (SUBRP (fun) && XSUBR (fun)->max_args > numargs
+  if (SUBRP (fun) && XSUBR (fun)->function.max_args > numargs
       /* Don't hide an error by adding missing arguments.  */
-      && numargs >= XSUBR (fun)->min_args)
+      && numargs >= XSUBR (fun)->function.min_args)
     {
       /* Avoid making funcall cons up a yet another new vector of arguments
         by explicitly supplying nil's for optional values.  */
-      SAFE_ALLOCA_LISP (funcall_args, 1 + XSUBR (fun)->max_args);
+      SAFE_ALLOCA_LISP (funcall_args, 1 + XSUBR (fun)->function.max_args);
       memclear (funcall_args + numargs + 1,
-               (XSUBR (fun)->max_args - numargs) * word_size);
-      funcall_nargs = 1 + XSUBR (fun)->max_args;
+               (XSUBR (fun)->function.max_args - numargs) * word_size);
+      funcall_nargs = 1 + XSUBR (fun)->function.max_args;
     }
   else
     { /* We add 1 to numargs because funcall_args includes the
@@ -2764,7 +2768,7 @@ FUNCTIONP (Lisp_Object object)
     }
 
   if (SUBRP (object))
-    return XSUBR (object)->max_args != UNEVALLED;
+    return XSUBR (object)->function.max_args != UNEVALLED;
   else if (COMPILEDP (object) || MODULE_FUNCTIONP (object))
     return true;
   else if (CONSP (object))
@@ -2819,7 +2823,12 @@ usage: (funcall FUNCTION &rest ARGUMENTS)  */)
     fun = indirect_function (fun);
 
   if (SUBRP (fun))
-    val = funcall_subr (XSUBR (fun), numargs, args + 1);
+    val = funcall_subr (fun, &XSUBR (fun)->function, numargs, args + 1);
+  else if (COMPILEDP (fun)
+          && XVECTOR (fun)->contents[COMPILED_JIT_CODE] != NULL)
+    val = funcall_subr (fun,
+                       (struct subr_function *) XVECTOR 
(fun)->contents[COMPILED_JIT_CODE],
+                       numargs, args + 1);
   else if (COMPILEDP (fun) || MODULE_FUNCTIONP (fun))
     val = funcall_lambda (fun, numargs, args + 1);
   else
@@ -2856,28 +2865,19 @@ usage: (funcall FUNCTION &rest ARGUMENTS)  */)
    and return the result of evaluation.  */
 
 Lisp_Object
-funcall_subr (struct Lisp_Subr *subr, ptrdiff_t numargs, Lisp_Object *args)
+funcall_subr (Lisp_Object error_obj, struct subr_function *subr,
+             ptrdiff_t numargs, Lisp_Object *args)
 {
   if (numargs < subr->min_args
       || (subr->max_args >= 0 && subr->max_args < numargs))
-    {
-      Lisp_Object fun;
-      XSETSUBR (fun, subr);
-      xsignal2 (Qwrong_number_of_arguments, fun, make_fixnum (numargs));
-    }
-
+    xsignal2 (Qwrong_number_of_arguments, error_obj, make_fixnum (numargs));
   else if (subr->max_args == UNEVALLED)
-    {
-      Lisp_Object fun;
-      XSETSUBR (fun, subr);
-      xsignal1 (Qinvalid_function, fun);
-    }
-
+    xsignal1 (Qinvalid_function, error_obj);
   else if (subr->max_args == MANY)
     return (subr->function.aMANY) (numargs, args);
   else
     {
-      Lisp_Object internal_argbuf[8];
+      Lisp_Object internal_argbuf[SUBR_MAX_ARGS];
       Lisp_Object *internal_args;
       if (subr->max_args > numargs)
         {
@@ -3020,6 +3020,22 @@ funcall_lambda (Lisp_Object fun, ptrdiff_t nargs,
             and constants vector yet, fetch them from the file.  */
          if (CONSP (AREF (fun, COMPILED_BYTECODE)))
            Ffetch_bytecode (fun);
+
+#ifdef HAVE_LIBJIT
+         if (initialized)
+           {
+             struct Lisp_Vector *vec = XVECTOR (fun);
+
+             if (vec->contents[COMPILED_JIT_CODE] == NULL)
+               emacs_jit_compile (fun);
+
+             if (vec->contents[COMPILED_JIT_CODE] != NULL)
+               return funcall_subr (fun,
+                                    (struct subr_function *) 
vec->contents[COMPILED_JIT_CODE],
+                                    nargs, arg_vector);
+           }
+#endif /* HAVE_LIBJIT */
+
          return exec_byte_code (AREF (fun, COMPILED_BYTECODE),
                                 AREF (fun, COMPILED_CONSTANTS),
                                 AREF (fun, COMPILED_STACK_DEPTH),
diff --git a/src/jit.c b/src/jit.c
new file mode 100644
index 0000000..94a83d2
--- /dev/null
+++ b/src/jit.c
@@ -0,0 +1,2367 @@
+/* Execution of byte code produced by bytecomp.el.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#ifdef HAVE_LIBJIT
+
+#include "lisp.h"
+#include "buffer.h"
+#include "bytecode.h"
+#include "window.h"
+
+#include <stdio.h>
+#include <jit/jit.h>
+#include <jit/jit-dump.h>
+
+static bool emacs_jit_initialized;
+
+jit_context_t emacs_jit_context;
+
+static jit_type_t nullary_signature;
+static jit_type_t unary_signature;
+static jit_type_t binary_signature;
+static jit_type_t ternary_signature;
+static jit_type_t unbind_n_signature;
+static jit_type_t temp_output_buffer_show_signature;
+static jit_type_t arithcompare_signature;
+static jit_type_t callN_signature;
+static jit_type_t compiled_signature;
+static jit_type_t wrong_number_of_arguments_signature;
+static jit_type_t set_internal_signature;
+static jit_type_t specbind_signature;
+static jit_type_t record_unwind_protect_excursion_signature;
+static jit_type_t record_unwind_protect_signature;
+static jit_type_t void_void_signature;
+static jit_type_t push_handler_signature;
+static jit_type_t setjmp_signature;
+
+static jit_type_t subr_signature[SUBR_MAX_ARGS];
+
+static jit_type_t ptrdiff_t_type;
+
+
+/* Make a pointer constant.  */
+#define CONSTANT(FUNC, VAL) \
+  jit_value_create_long_constant (FUNC, jit_type_void_ptr, (jit_long) (VAL))
+
+/* Fetch the next byte from the bytecode stream.  */
+
+#define FETCH (bytestr_data[pc++])
+
+/* Fetch two bytes from the bytecode stream and make a 16-bit number
+   out of them.  */
+
+#define FETCH2 (op = FETCH, op + (FETCH << 8))
+
+#define PUSH(VALUE) \
+  jit_insn_store (func, stack[++stack_pointer], VALUE)
+
+#define POP stack[stack_pointer--]
+
+/* Discard n values from the execution stack.  */
+
+#define DISCARD(n) (stack_pointer -= (n))
+
+/* Get the value which is at the top of the execution stack, but don't
+   pop it.  */
+
+#define TOP stack[stack_pointer]
+
+/* Compile code that extracts the type from VAL.  */
+
+static jit_value_t
+get_type (jit_function_t func, jit_value_t val)
+{
+#if USE_LSB_TAG
+  jit_value_t mask = jit_value_create_nint_constant (func, jit_type_void_ptr,
+                                                    ~VALMASK);
+  return jit_insn_and (func, val, mask);
+#else /* USE_LSB_TAG */
+  jit_value_t shift = jit_value_create_nint_constant (func, jit_type_uint,
+                                                     VALBITS);
+  return jit_insn_ushr (func, val, shift);
+#endif /* not USE_LSB_TAG */
+}
+
+static jit_value_t
+untag (jit_function_t func, jit_value_t val, EMACS_UINT utype)
+{
+  jit_value_t tem;
+
+  utype = utype << (USE_LSB_TAG ? 0 : VALBITS);
+
+  tem = jit_value_create_nint_constant (func, jit_type_void_ptr, utype);
+  return jit_insn_sub (func, val, tem);
+}
+
+static jit_value_t
+to_int (jit_function_t func, jit_value_t val)
+{
+  jit_value_t shift = jit_value_create_nint_constant (func, jit_type_uint,
+                                                     INTTYPEBITS);
+#if !USE_LSB_TAG
+  val = jit_insn_shl (func, val, shift);
+#endif
+  return jit_insn_sshr (func, val, shift);
+}
+
+static jit_value_t
+eq_nil (jit_function_t func, jit_value_t val)
+{
+  jit_value_t nilval = CONSTANT (func, Qnil);
+  return jit_insn_eq (func, val, nilval);
+}
+
+static jit_value_t
+compare_type (jit_function_t func, jit_value_t val, int to_be_checked)
+{
+  jit_value_t real_type = get_type (func, val);
+  jit_value_t type_val
+    = jit_value_create_nint_constant (func, jit_type_void_ptr, to_be_checked);
+  return jit_insn_eq (func, real_type, type_val);
+}
+
+/* If the next instruction in the stream is a conditional branch,
+   return true and compile jumps based on COMPARE.  Otherwise, return
+   false.  */
+
+static bool
+peek_condition (jit_function_t func,
+               unsigned char *bytestr_data, ptrdiff_t pc,
+               jit_value_t compare, jit_value_t dest,
+               jit_label_t *labels)
+{
+  int op = FETCH;
+
+  switch (op)
+    {
+    case Bgotoifnil:
+      op = FETCH2;
+      jit_insn_branch_if_not (func, compare, &labels[op]);
+      break;
+
+    case Bgotoifnonnil:
+      op = FETCH2;
+      jit_insn_branch_if (func, compare, &labels[op]);
+      break;
+
+    case Bgotoifnilelsepop:
+      op = FETCH2;
+      jit_insn_store (func, dest, CONSTANT (func, Qnil));
+      jit_insn_branch_if_not (func, compare, &labels[op]);
+      break;
+
+    case Bgotoifnonnilelsepop:
+      op = FETCH2;
+      jit_insn_store (func, dest, CONSTANT (func, Qt));
+      jit_insn_branch_if (func, compare, &labels[op]);
+      break;
+
+    case BRgotoifnil:
+      op = FETCH - 128;
+      op += pc;
+      jit_insn_branch_if_not (func, compare, &labels[op]);
+      break;
+
+    case BRgotoifnonnil:
+      op = FETCH - 128;
+      op += pc;
+      jit_insn_branch_if (func, compare, &labels[op]);
+      break;
+
+    case BRgotoifnilelsepop:
+      op = FETCH - 128;
+      op += pc;
+      jit_insn_store (func, dest, CONSTANT (func, Qnil));
+      jit_insn_branch_if_not (func, compare, &labels[op]);
+      break;
+
+    case BRgotoifnonnilelsepop:
+      op = FETCH - 128;
+      op += pc;
+      jit_insn_store (func, dest, CONSTANT (func, Qt));
+      jit_insn_branch_if (func, compare, &labels[op]);
+      break;
+
+    default:
+      return false;
+    }
+
+  /* This is necessary to bypass the (probably dead) code that will be
+     emitted for the branch in the main JIT loop.  */
+  jit_insn_branch (func, &labels[pc]);
+
+  return true;
+}
+
+static void
+emit_qnil_or_qt (jit_function_t func,
+                unsigned char *bytestr_data, ptrdiff_t pc,
+                jit_value_t compare, jit_value_t dest,
+                jit_label_t *labels)
+{
+  jit_value_t tem;
+
+  /* Optimize the case where we see bytecode like:
+     Beq
+     Bgotoifnil [...]
+     Here, we don't actually need to load and store the `nil' or `t'
+     -- we can just branch directly based on the condition we just
+     computed.  */
+  if (!peek_condition (func, bytestr_data, pc, compare, dest, labels))
+    {
+      /* Actually must emit a load of Qt or Qnil.  */
+      jit_label_t nil_label = jit_label_undefined;
+      jit_insn_branch_if_not (func, compare, &nil_label);
+      tem = CONSTANT (func, Qt);
+      jit_insn_store (func, dest, tem);
+      jit_insn_branch (func, &labels[pc]);
+      jit_insn_label (func, &nil_label);
+      tem = CONSTANT (func, Qnil);
+      jit_insn_store (func, dest, tem);
+    }
+}
+
+static jit_value_t
+compile_nullary (jit_function_t func, const char *name,
+                Lisp_Object (*callee) (void))
+{
+  return jit_insn_call_native (func, name, (void *) callee,
+                              nullary_signature, NULL, 0,
+                              JIT_CALL_NOTHROW);
+}
+
+static void
+
+compile_unary (jit_function_t func, const char *name,
+              Lisp_Object (*callee) (Lisp_Object),
+              jit_value_t arg_and_dest)
+{
+  jit_value_t result = jit_insn_call_native (func, name, (void *) callee,
+                                            unary_signature, &arg_and_dest, 1,
+                                            JIT_CALL_NOTHROW);
+  jit_insn_store (func, arg_and_dest, result);
+}
+
+static void
+compile_binary (jit_function_t func, const char *name,
+               Lisp_Object (*callee) (Lisp_Object, Lisp_Object),
+               jit_value_t arg_and_dest, jit_value_t arg2)
+{
+  jit_value_t args[2] = { arg_and_dest, arg2 };
+
+  jit_value_t result = jit_insn_call_native (func, name, (void *) callee,
+                                            binary_signature, args, 2,
+                                            JIT_CALL_NOTHROW);
+  jit_insn_store (func, arg_and_dest, result);
+}
+
+static void
+compile_ternary (jit_function_t func, const char *name,
+                Lisp_Object (*callee) (Lisp_Object, Lisp_Object, Lisp_Object),
+                jit_value_t arg_and_dest, jit_value_t arg2, jit_value_t arg3)
+{
+  jit_value_t args[3] = { arg_and_dest, arg2, arg3 };
+
+  jit_value_t result = jit_insn_call_native (func, name, (void *) callee,
+                                            ternary_signature, args, 3,
+                                            JIT_CALL_NOTHROW);
+  jit_insn_store (func, arg_and_dest, result);
+}
+
+static void
+compile_arithcompare (jit_function_t func, const char *name,
+                     jit_value_t arg_and_dest, jit_value_t arg2, int arg3)
+{
+  jit_value_t tem
+    = jit_value_create_nint_constant (func, jit_type_sys_int, arg3);
+  jit_value_t args[3] = { arg_and_dest, arg2, tem };
+
+  jit_value_t result = jit_insn_call_native (func, name, (void *) arithcompare,
+                                            arithcompare_signature, args, 3,
+                                            JIT_CALL_NOTHROW);
+  jit_insn_store (func, arg_and_dest, result);
+}
+
+static jit_value_t
+compile_make_natnum (jit_function_t func, jit_value_t untagged_int)
+{
+#if USE_LSB_TAG
+  jit_value_t nbits
+    = jit_value_create_nint_constant (func, jit_type_int, INTTYPEBITS);
+  jit_value_t val = jit_insn_shl (func, untagged_int, nbits);
+  jit_value_t tag
+    = jit_value_create_nint_constant (func, jit_type_void_ptr, Lisp_Int0);
+  return jit_insn_add (func, val, tag);
+#else /* USE_LSB_TAG */
+  jit_value_t tag
+    = jit_value_create_nint_constant (func, jit_type_void_ptr,
+                                     ((EMACS_INT) Lisp_Int0) << VALBITS);
+  return jit_insn_add (func, untagged_int, tag);
+#endif /* not USE_LSB_TAG */
+}
+
+static jit_value_t
+compile_make_number (jit_function_t func, jit_value_t untagged_int)
+{
+#if USE_LSB_TAG
+  jit_value_t nbits
+    = jit_value_create_nint_constant (func, jit_type_int, INTTYPEBITS);
+  jit_value_t val = jit_insn_shl (func, untagged_int, nbits);
+  jit_value_t tag
+    = jit_value_create_nint_constant (func, jit_type_void_ptr, Lisp_Int0);
+  return jit_insn_add (func, val, tag);
+#else /* USE_LSB_TAG */
+  jit_value_t mask
+    = jit_value_create_nint_constant (func, jit_type_void_ptr, INTMASK);
+  jit_value_t tag
+    = jit_value_create_nint_constant (func, jit_type_void_ptr,
+                                     ((EMACS_INT) Lisp_Int0) << VALBITS);
+  jit_value_t val = jit_insn_and (func, untagged_int, mask);
+  return jit_insn_add (func, val, tag);
+#endif /* not USE_LSB_TAG */
+}
+
+static jit_value_t
+compile_current_thread (jit_function_t func)
+{
+  jit_value_t thread_ptr = CONSTANT (func, &current_thread);
+  return jit_insn_load_relative (func, thread_ptr, 0, jit_type_void_ptr);
+}
+
+static jit_value_t
+compile_current_buffer (jit_function_t func)
+{
+  jit_value_t current_thread = compile_current_thread (func);
+  return jit_insn_load_relative (func, current_thread,
+                                offsetof (struct thread_state,
+                                          m_current_buffer),
+                                jit_type_void_ptr);
+}
+
+static jit_value_t
+compile_buffer_int (jit_function_t func, off_t offset)
+{
+  jit_value_t current_buffer_val = compile_current_buffer (func);
+  jit_value_t value
+    = jit_insn_load_relative (func, current_buffer_val, offset,
+                             ptrdiff_t_type);
+
+  return compile_make_natnum (func, value);
+}
+
+static jit_value_t
+compare_buffer_ints (jit_function_t func, off_t off1, off_t off2)
+{
+  jit_value_t current_buffer_val = compile_current_buffer (func);
+  jit_value_t value1
+    = jit_insn_load_relative (func, current_buffer_val, off1,
+                             ptrdiff_t_type);
+  jit_value_t value2
+    = jit_insn_load_relative (func, current_buffer_val, off2,
+                             ptrdiff_t_type);
+  return jit_insn_eq (func, value1, value2);
+}
+
+static void
+car_or_cdr (jit_function_t func, jit_value_t val, off_t offset,
+           jit_label_t *next_insn, bool safe,
+           bool *called_wtype, jit_label_t *wtype_label,
+           jit_value_t wtype_arg)
+{
+  jit_value_t tem;
+  jit_label_t not_a_cons = jit_label_undefined;
+
+  jit_value_t is_cons = compare_type (func, val, Lisp_Cons);
+  jit_insn_branch_if_not (func, is_cons, &not_a_cons);
+
+  /* Is a cons.  */
+  tem = untag (func, val, Lisp_Cons);
+  tem = jit_insn_load_relative (func, tem, offset,
+                               jit_type_void_ptr);
+  jit_insn_store (func, val, tem);
+  jit_insn_branch (func, next_insn);
+
+  jit_insn_label (func, &not_a_cons);
+  if (safe)
+    {
+      /* Not a cons, so just use nil.  */
+      jit_value_t nilval = CONSTANT (func, Qnil);
+      jit_insn_store (func, val, nilval);
+    }
+  else
+    {
+      /* Check if it is nil. */
+      tem = eq_nil (func, val);
+      /* If it is nil, VAL is already correct and we can carry on.  */
+      jit_insn_branch_if (func, tem, next_insn);
+
+      /* Wrong type.  */
+      jit_insn_store (func, wtype_arg, val);
+      jit_insn_branch (func, wtype_label);
+      *called_wtype = true;
+    }
+}
+
+static void
+compile_wrong_type_argument (jit_function_t func, jit_label_t *label,
+                            jit_value_t wtype_arg)
+{
+  jit_value_t args[2];
+
+  args[0] = CONSTANT (func, Qlistp);
+  args[1] = wtype_arg;
+
+  jit_insn_label (func, label);
+  jit_insn_call_native (func, "wrong_type_argument",
+                       (void *) wrong_type_argument,
+                       /* FIXME incorrect signature.  */
+                       binary_signature, args, 2,
+                       JIT_CALL_NORETURN);
+}
+
+static jit_value_t
+compare_integerp (jit_function_t func, jit_value_t val, jit_value_t *type_out)
+{
+  jit_value_t type = get_type (func, val);
+  if (type_out != NULL)
+    *type_out = type;
+
+  jit_value_t c1
+    = jit_value_create_nint_constant (func, jit_type_void_ptr,
+                                     Lisp_Int0 | ~Lisp_Int1);
+  jit_value_t c2
+    = jit_value_create_nint_constant (func, jit_type_void_ptr,
+                                     Lisp_Int0);
+  jit_value_t tem = jit_insn_and (func, type, c1);
+
+  return jit_insn_eq (func, tem, c2);
+}
+
+static Lisp_Object
+negate (Lisp_Object arg)
+{
+  return Fminus (1, &arg);
+}
+
+enum math_op
+{
+  SUB1,
+  ADD1,
+  NEGATE
+};
+
+static void
+unary_intmath (jit_function_t func, jit_value_t val, enum math_op op,
+              jit_label_t *next_insn)
+{
+  jit_label_t not_an_int = jit_label_undefined;
+  jit_value_t compare = compare_integerp (func, val, NULL);
+
+  jit_insn_branch_if_not (func, compare, &not_an_int);
+
+  /* Got an integer.  */
+  jit_value_t result = to_int (func, val);
+  jit_value_t tem;
+  switch (op)
+    {
+    case SUB1:
+      /* Don't allow (1- most-negative-fixnum).  */
+      tem = jit_value_create_nint_constant (func, jit_type_sys_int,
+                                           MOST_NEGATIVE_FIXNUM);
+      compare = jit_insn_eq (func, result, tem);
+      jit_insn_branch_if (func, compare, &not_an_int);
+
+      tem = jit_value_create_nint_constant (func, jit_type_sys_int, 1);
+      result = jit_insn_sub (func, result, tem);
+      break;
+
+    case ADD1:
+      /* Don't allow (1+ most-positive-fixnum).  */
+      tem = jit_value_create_nint_constant (func, jit_type_sys_int,
+                                           MOST_POSITIVE_FIXNUM);
+      compare = jit_insn_eq (func, result, tem);
+      jit_insn_branch_if (func, compare, &not_an_int);
+
+      tem = jit_value_create_nint_constant (func, jit_type_sys_int, 1);
+      result = jit_insn_add (func, result, tem);
+      break;
+
+    case NEGATE:
+      /* Don't allow (- most-negative-fixnum).  */
+      tem = jit_value_create_nint_constant (func, jit_type_sys_int,
+                                           MOST_NEGATIVE_FIXNUM);
+      compare = jit_insn_eq (func, result, tem);
+      jit_insn_branch_if (func, compare, &not_an_int);
+
+      result = jit_insn_neg (func, result);
+      break;
+
+    default:
+      emacs_abort ();
+    }
+
+  result = compile_make_number (func, result);
+  jit_insn_store (func, val, result);
+  jit_insn_branch (func, next_insn);
+
+  jit_insn_label (func, &not_an_int);
+
+  const char *name;
+  void *callee;
+  switch (op)
+    {
+    case SUB1:
+      name = "Fsub1";
+      callee = (void *) Fsub1;
+      break;
+    case ADD1:
+      name = "Fadd1";
+      callee = (void *) Fadd1;
+      break;
+    case NEGATE:
+      name = "negate";
+      callee = (void *) negate;
+      break;
+    default:
+      emacs_abort ();
+    }
+
+  tem = jit_insn_call_native (func, name, (void *) callee,
+                             unary_signature, &val, 1,
+                             JIT_CALL_NOTHROW);
+  jit_insn_store (func, val, tem);
+}
+
+static jit_value_t
+compile_callN (jit_function_t func, const char *name,
+              Lisp_Object (*callee) (ptrdiff_t, Lisp_Object *),
+              int howmany, jit_value_t scratch, jit_value_t *stack)
+{
+  jit_value_t args[2];
+
+  args[0] = jit_value_create_nint_constant (func, ptrdiff_t_type, howmany);
+  args[1] = scratch;
+
+  int i;
+  for (i = 0; i < howmany; ++i)
+    jit_insn_store_relative (func, scratch, i * sizeof (Lisp_Object),
+                            stack[1 + i]);
+
+  return jit_insn_call_native (func, name, (void *) callee,
+                              callN_signature, args, 2, JIT_CALL_NOTHROW);
+}
+
+static jit_value_t
+compile_next_handlerlist (jit_function_t func)
+{
+  jit_value_t current_thread = compile_current_thread (func);
+  jit_value_t hlist_ptr
+    = jit_insn_load_relative (func, current_thread,
+                             offsetof (struct thread_state,
+                                       m_handlerlist),
+                             jit_type_void_ptr);
+  jit_value_t next
+    = jit_insn_load_relative (func, hlist_ptr,
+                             offsetof (struct handler, next),
+                             jit_type_void_ptr);
+  jit_insn_store_relative (func, current_thread,
+                          offsetof (struct thread_state,
+                                    m_handlerlist),
+                          next);
+
+  return hlist_ptr;
+}
+
+#define COMPILE_CALLN(FUNC, N)                                 \
+  do {                                                         \
+    jit_value_t result;                                                \
+    DISCARD (N);                                               \
+    result = compile_callN (func, # FUNC, FUNC,                        \
+                           N, scratch, &stack[stack_pointer]); \
+    if (N > scratch_slots_needed)                              \
+      scratch_slots_needed = N;                                        \
+    PUSH (result);                                             \
+  } while (0)
+
+static void
+bcall0 (Lisp_Object f)
+{
+  if (FUNCTIONP (f))
+    Ffuncall (1, &f);
+}
+
+static Lisp_Object
+native_save_window_excursion (Lisp_Object v1)
+{
+  ptrdiff_t count1 = SPECPDL_INDEX ();
+  record_unwind_protect (restore_window_configuration,
+                        Fcurrent_window_configuration (Qnil));
+  v1 = Fprogn (v1);
+  unbind_to (count1, v1);
+  return v1;
+}
+
+static Lisp_Object
+native_temp_output_buffer_setup (Lisp_Object x)
+{
+  CHECK_STRING (x);
+  temp_output_buffer_setup (SSDATA (x));
+  return Vstandard_output;
+}
+
+static Lisp_Object
+unbind_n (int val)
+{
+  return unbind_to (SPECPDL_INDEX () - val, Qnil);
+}
+
+static void
+wrong_number_of_arguments (int mandatory, int nonrest, int nargs)
+{
+  Fsignal (Qwrong_number_of_arguments,
+          list2 (Fcons (make_fixnum (mandatory), make_fixnum (nonrest)),
+                 make_fixnum (nargs)));
+}
+
+struct pc_list
+{
+  /* PC at which to (re-)start compilation.  */
+  int pc;
+  /* Saved stack pointer.  */
+  int stack_pointer;
+  struct pc_list *next;
+};
+
+#define PUSH_PC(insn)                                          \
+  do {                                                         \
+    struct pc_list *new = xmalloc (sizeof (struct pc_list));   \
+    new->pc = insn;                                            \
+    new->stack_pointer = stack_pointer;                                \
+    new->next = pc_list;                                       \
+    pc_list = new;                                             \
+  } while (0)
+
+#define COMPILE_BUFFER_INT(FIELD) \
+  compile_buffer_int (func, offsetof (struct buffer, FIELD))
+
+#define COMPARE_BUFFER_INTS(FIELD1, FIELD2)                    \
+  compare_buffer_ints (func,                                   \
+                      offsetof (struct buffer, FIELD1),        \
+                      offsetof (struct buffer, FIELD2))
+
+static bool
+find_hash_min_max_pc (struct Lisp_Hash_Table *htab,
+                     EMACS_INT *min_pc, EMACS_INT *max_pc)
+{
+  for (ptrdiff_t i = 0; i < HASH_TABLE_SIZE (htab); ++i)
+    if (!NILP (HASH_HASH (htab, i)))
+      {
+       Lisp_Object pc = HASH_VALUE (htab, i);
+       if (!FIXNUMP (pc))
+         return false;
+       EMACS_INT pcval = XFIXNUM (pc);
+       if (pcval < *min_pc)
+         *min_pc = pcval;
+       if (pcval > *max_pc)
+         *max_pc = pcval;
+      }
+
+  ++*max_pc;
+  return true;
+}
+
+static struct subr_function *
+compile (ptrdiff_t bytestr_length, unsigned char *bytestr_data,
+        EMACS_INT stack_depth, Lisp_Object *vectorp,
+        ptrdiff_t vector_size, Lisp_Object args_template)
+{
+  int type;
+  struct pc_list *pc_list = NULL;
+
+  /* Note that any error before this is attached to the function must
+     free this object.  */
+  struct subr_function *result = xmalloc (sizeof (struct subr_function));
+  result->min_args = 0;
+  result->max_args = MANY;
+
+  jit_type_t function_signature = compiled_signature;
+
+  bool parse_args = true;
+  if (FIXNUMP (args_template))
+    {
+      ptrdiff_t at = XFIXNUM (args_template);
+      bool rest = (at & 128) != 0;
+      int mandatory = at & 127;
+      ptrdiff_t nonrest = at >> 8;
+
+      /* Always set this correctly so that funcall_subr will do some
+        checking for us.  */
+      result->min_args = mandatory;
+
+      if (!rest && nonrest < SUBR_MAX_ARGS)
+       {
+         result->max_args = nonrest;
+         function_signature = subr_signature[nonrest];
+         parse_args = false;
+       }
+    }
+
+  jit_function_t func = jit_function_create (emacs_jit_context,
+                                            function_signature);
+  ptrdiff_t pc = 0;
+  jit_value_t *stack = (jit_value_t *) xmalloc (stack_depth
+                                               * sizeof (jit_value_t));
+  int stack_pointer = -1;
+  jit_label_t *labels = (jit_label_t *) xmalloc (bytestr_length
+                                                * sizeof (jit_label_t));
+  /* Temporary array used only for switches.  */
+  jit_label_t *sw_labels = (jit_label_t *) xmalloc (bytestr_length
+                                                   * sizeof (jit_label_t));
+  int *stack_depths = (int *) xmalloc (bytestr_length * sizeof (int));
+  jit_value_t n_args, arg_vec;
+
+  /* On failure this will also free RESULT.  */
+  jit_function_set_meta (func, 0, result, xfree, 0);
+
+  for (int i = 0; i < bytestr_length; ++i)
+    {
+      labels[i] = jit_label_undefined;
+      sw_labels[i] = jit_label_undefined;
+      stack_depths[i] = -1;
+    }
+
+  for (int i = 0; i < stack_depth; ++i)
+    stack[i] = jit_value_create (func, jit_type_void_ptr);
+
+  /* This is a placeholder; once we know how much space we'll need, we
+     will allocate it and move it into place at the start of the
+     function.  */
+  jit_value_t scratch = jit_value_create (func, jit_type_void_ptr);
+  int scratch_slots_needed = 0;
+
+  /* State needed if we need to emit a call to
+     wrong_type_argument.  */
+  bool called_wtype = false;
+  jit_label_t wtype_label = jit_label_undefined;
+  jit_value_t wtype_arg = jit_value_create (func, jit_type_void_ptr);
+
+  jit_label_t argfail = jit_label_undefined;
+  bool need_argfail = false;
+  jit_value_t mandatory_val, nonrest_val;
+
+  if (!parse_args)
+    {
+      /* We can emit function that doesn't need to manually decipher
+        its arguments.  */
+      ptrdiff_t at = XFIXNUM (args_template);
+      ptrdiff_t nonrest = at >> 8;
+
+      for (ptrdiff_t i = 0; i < nonrest; ++i)
+       PUSH (jit_value_get_param (func, i));
+    }
+  else
+    {
+      /* Prologue.  */
+      n_args = jit_value_get_param (func, 0);
+      arg_vec = jit_value_get_param (func, 1);
+
+      if (FIXNUMP (args_template))
+       {
+         ptrdiff_t at = XFIXNUM (args_template);
+         bool rest = (at & 128) != 0;
+         int mandatory = at & 127;
+         ptrdiff_t nonrest = at >> 8;
+
+         mandatory_val
+           = jit_value_create_long_constant (func, ptrdiff_t_type, mandatory);
+         nonrest_val
+           = jit_value_create_nint_constant (func, ptrdiff_t_type, nonrest);
+
+         /* If there are no rest arguments and we have more than the
+            maximum, error.  Note that funcall_subr ensures that, no
+            matter what, we'll never see fewer than the minimum
+            number of arguments.  */
+         if (!rest)
+           {
+             jit_value_t compare = jit_insn_gt (func, n_args, nonrest_val);
+             jit_insn_branch_if (func, compare, &argfail);
+             need_argfail = true;
+           }
+
+         /* Load mandatory arguments.  */
+         for (ptrdiff_t i = 0; i < mandatory; ++i)
+           {
+             jit_value_t loaded
+               = jit_insn_load_relative (func, arg_vec, i * sizeof 
(Lisp_Object),
+                                         jit_type_void_ptr);
+             jit_insn_store (func, stack[i], loaded);
+           }
+
+         /* &optional arguments are a bit weirder since we can't refer to
+            the appropriate stack slot by index at runtime.  */
+         if (nonrest > mandatory)
+           {
+             jit_value_t qnil = CONSTANT (func, Qnil);
+             jit_label_t *opt_labels
+               = (jit_label_t *) xmalloc ((nonrest - mandatory)
+                                          * sizeof (jit_label_t));
+             jit_label_t opts_done = jit_label_undefined;
+
+             for (ptrdiff_t i = mandatory; i < nonrest; ++i)
+               {
+                 opt_labels[i - mandatory] = jit_label_undefined;
+
+                 jit_value_t this_arg
+                   = jit_value_create_nint_constant (func, jit_type_sys_int, 
i);
+                 jit_value_t cmp = jit_insn_le (func, n_args, this_arg);
+                 /* If this argument wasn't found, then neither are the
+                    subsequent ones; so branch into the correct spot in a
+                    series of loads of Qnil.  */
+                 jit_insn_branch_if (func, cmp, &opt_labels[i - mandatory]);
+
+                 jit_value_t loaded
+                   = jit_insn_load_relative (func, arg_vec,
+                                             i * sizeof (Lisp_Object),
+                                             jit_type_void_ptr);
+                 jit_insn_store (func, stack[i], loaded);
+               }
+
+             jit_insn_branch (func, &opts_done);
+
+             for (ptrdiff_t i = mandatory; i < nonrest; ++i)
+               {
+                 jit_insn_label (func, &opt_labels[i - mandatory]);
+                 jit_insn_store (func, stack[i], qnil);
+               }
+
+             jit_insn_label (func, &opts_done);
+             xfree (opt_labels);
+           }
+
+         stack_pointer = nonrest - 1;
+
+         /* Now handle rest arguments, if any.  */
+         if (rest)
+           {
+             jit_label_t no_rest = jit_label_undefined;
+             jit_value_t cmp = jit_insn_lt (func, nonrest_val, n_args);
+             jit_insn_branch_if_not (func, cmp, &no_rest);
+
+             jit_value_t vec_addr
+               = jit_insn_load_elem_address (func, arg_vec, nonrest_val,
+                                             jit_type_void_ptr);
+             jit_value_t new_args
+               = jit_insn_sub (func, n_args, nonrest_val);
+
+             jit_value_t args[2] = { new_args, vec_addr };
+             jit_value_t listval
+               = jit_insn_call_native (func, "list", (void *) Flist,
+                                       callN_signature,
+                                       args, 2, JIT_CALL_NOTHROW);
+             PUSH (listval);
+             jit_insn_branch (func, &labels[0]);
+
+             /* Since we emitted a branch.  */
+             --stack_pointer;
+             jit_insn_label (func, &no_rest);
+             jit_value_t qnil = CONSTANT (func, Qnil);
+             PUSH (qnil);
+           }
+
+         /* Fall through to the main body.  */
+       }
+    }
+
+  for (;;)
+    {
+      if (pc == bytestr_length)
+       {
+         /* Falling off the end would be bad.  */
+         goto fail;
+       }
+      else if (pc != -1 && (stack_depths[pc] != -1))
+       {
+         /* We've already compiled this code, and we're expecting to
+            fall through.  So, emit a goto and then resume work at
+            some other PC.  */
+         jit_insn_branch (func, &labels[pc]);
+         pc = -1;
+       }
+
+      /* If we don't have a PC currently, pop a new one from the list
+        and work there.  */
+      while (pc == -1 && pc_list != NULL)
+       {
+         struct pc_list *next;
+
+         pc = pc_list->pc;
+         stack_pointer = pc_list->stack_pointer;
+         next = pc_list->next;
+         xfree (pc_list);
+         pc_list = next;
+
+         if (stack_depths[pc] == -1)
+           {
+             /* Work on this one.  */
+             stack_depths[pc] = stack_pointer + 1;
+             break;
+           }
+         else if (stack_depths[pc] == stack_pointer + 1)
+           {
+             /* Already compiled this.  */
+             pc = -1;
+           }
+         else
+           {
+             /* Oops - failure.  */
+             goto fail;
+           }
+       }
+
+      if (pc == -1 && pc_list == NULL)
+       {
+         /* No more blocks to examine.  */
+         break;
+       }
+
+      jit_insn_label (func, &labels[pc]);
+
+      int op = FETCH;
+      switch (op)
+       {
+       case Bvarref7:
+         op = FETCH2;
+         goto varref;
+
+       case Bvarref:
+       case Bvarref1:
+       case Bvarref2:
+       case Bvarref3:
+       case Bvarref4:
+       case Bvarref5:
+         op -= Bvarref;
+         goto varref;
+
+       case Bvarref6:
+         op = FETCH;
+       varref:
+         {
+           jit_value_t sym, result;
+
+           sym = CONSTANT (func, vectorp[op]);
+           result = jit_insn_call_native (func, "symbol-value",
+                                          (void *) Fsymbol_value,
+                                          unary_signature, &sym, 1,
+                                          JIT_CALL_NOTHROW);
+           PUSH (result);
+           break;
+         }
+
+       case Bcar:
+         car_or_cdr (func, TOP, offsetof (struct Lisp_Cons, u.s.car),
+                     &labels[pc], false,
+                     &called_wtype, &wtype_label, wtype_arg);
+         break;
+
+       case Beq:
+         {
+           jit_value_t v1 = POP;
+           jit_value_t compare = jit_insn_eq (func, v1, TOP);
+           emit_qnil_or_qt (func, bytestr_data, pc, compare, TOP, labels);
+           break;
+         }
+
+       case Bmemq:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "memq", Fmemq, TOP, v1);
+           break;
+         }
+
+       case Bcdr:
+         car_or_cdr (func, TOP, offsetof (struct Lisp_Cons, u.s.u.cdr),
+                     &labels[pc], false,
+                     &called_wtype, &wtype_label, wtype_arg);
+         break;
+
+       case Bvarset:
+       case Bvarset1:
+       case Bvarset2:
+       case Bvarset3:
+       case Bvarset4:
+       case Bvarset5:
+         op -= Bvarset;
+         goto varset;
+
+       case Bvarset7:
+         op = FETCH2;
+         goto varset;
+
+       case Bvarset6:
+         op = FETCH;
+       varset:
+         {
+           jit_value_t args[4];
+
+           args[0] = CONSTANT (func, vectorp[op]);
+           args[1] = POP;
+           args[2] = CONSTANT (func, Qnil);
+           args[3] = jit_value_create_nint_constant (func, jit_type_sys_int,
+                                                     SET_INTERNAL_SET);
+
+           jit_insn_call_native (func, "set_internal", (void *) set_internal,
+                                 set_internal_signature, args, 4,
+                                 JIT_CALL_NOTHROW);
+         }
+         break;
+
+       case Bdup:
+         {
+           jit_value_t v1 = TOP;
+           PUSH (v1);
+           break;
+         }
+
+       /* ------------------ */
+
+       case Bvarbind6:
+         op = FETCH;
+         goto varbind;
+
+       case Bvarbind7:
+         op = FETCH2;
+         goto varbind;
+
+       case Bvarbind:
+       case Bvarbind1:
+       case Bvarbind2:
+       case Bvarbind3:
+       case Bvarbind4:
+       case Bvarbind5:
+         op -= Bvarbind;
+       varbind:
+         {
+           jit_value_t vals[2];
+
+           vals[0] = CONSTANT (func, vectorp[op]);
+           vals[1] = POP;
+
+           jit_insn_call_native (func, "specbind", (void *) specbind,
+                                 specbind_signature, vals, 2,
+                                 JIT_CALL_NOTHROW);
+           break;
+         }
+
+       case Bcall6:
+         op = FETCH;
+         goto docall;
+
+       case Bcall7:
+         op = FETCH2;
+         goto docall;
+
+       case Bcall:
+       case Bcall1:
+       case Bcall2:
+       case Bcall3:
+       case Bcall4:
+       case Bcall5:
+         op -= Bcall;
+       docall:
+         {
+           COMPILE_CALLN (Ffuncall, op + 1);
+           break;
+         }
+
+       case Bunbind6:
+         op = FETCH;
+         goto dounbind;
+
+       case Bunbind7:
+         op = FETCH2;
+         goto dounbind;
+
+       case Bunbind:
+       case Bunbind1:
+       case Bunbind2:
+       case Bunbind3:
+       case Bunbind4:
+       case Bunbind5:
+         op -= Bunbind;
+       dounbind:
+         {
+           jit_value_t val = jit_value_create_nint_constant (func,
+                                                             jit_type_sys_int,
+                                                             op);
+           jit_insn_call_native (func, "unbind_n", (void *) unbind_n,
+                                 unbind_n_signature, &val, 1,
+                                 JIT_CALL_NOTHROW);
+         }
+         break;
+
+       case Bgoto:
+         op = FETCH2;
+         /* This looks funny but it circumvents the code above that
+            handles the case where fall-through actually requires a
+            branch.  */
+         PUSH_PC (op);
+         pc = -1;
+         jit_insn_branch (func, &labels[op]);
+         break;
+
+       case Bgotoifnil:
+         {
+           jit_value_t v1 = POP;
+           jit_value_t compare = eq_nil (func, v1);
+           op = FETCH2;
+           PUSH_PC (op);
+           jit_insn_branch_if (func, compare, &labels[op]);
+           break;
+         }
+
+       case Bgotoifnonnil:
+         {
+           jit_value_t val = POP;
+           jit_value_t compare = eq_nil (func, val);
+           op = FETCH2;
+           PUSH_PC (op);
+           jit_insn_branch_if_not (func, compare, &labels[op]);
+           break;
+         }
+
+       case Bgotoifnilelsepop:
+         {
+           jit_value_t v1 = TOP;
+           jit_value_t compare = eq_nil (func, v1);
+           op = FETCH2;
+           PUSH_PC (op);
+           jit_insn_branch_if (func, compare, &labels[op]);
+           DISCARD (1);
+           break;
+         }
+         break;
+
+       case Bgotoifnonnilelsepop:
+         {
+           jit_value_t v1 = TOP;
+           jit_value_t compare = eq_nil (func, v1);
+           op = FETCH2;
+           PUSH_PC (op);
+           jit_insn_branch_if_not (func, compare, &labels[op]);
+           DISCARD (1);
+           break;
+         }
+         break;
+
+       case BRgoto:
+         {
+           op = FETCH - 128;
+           op += pc;
+           /* This looks funny but it circumvents the code above that
+              handles the case where fall-through actually requires a
+              branch.  */
+           PUSH_PC (op);
+           pc = -1;
+           jit_insn_branch (func, &labels[op]);
+           break;
+         }
+
+       case BRgotoifnil:
+         {
+           jit_value_t v1 = POP;
+           jit_value_t compare = eq_nil (func, v1);
+           op = FETCH - 128;
+           op += pc;
+           PUSH_PC (op);
+           jit_insn_branch_if (func, compare, &labels[op]);
+           break;
+         }
+
+       case BRgotoifnonnil:
+         {
+           jit_value_t v1 = POP;
+           jit_value_t compare = eq_nil (func, v1);
+           op = FETCH - 128;
+           op += pc;
+           PUSH_PC (op);
+           jit_insn_branch_if_not (func, compare, &labels[op]);
+           break;
+         }
+
+       case BRgotoifnilelsepop:
+         {
+           jit_value_t v1 = TOP;
+           jit_value_t compare = eq_nil (func, v1);
+           op = FETCH - 128;
+           op += pc;
+           PUSH_PC (op);
+           jit_insn_branch_if (func, compare, &labels[op]);
+           DISCARD (1);
+           break;
+         }
+
+       case BRgotoifnonnilelsepop:
+         {
+           jit_value_t v1 = TOP;
+           jit_value_t compare = eq_nil (func, v1);
+           op = FETCH - 128;
+           op += pc;
+           PUSH_PC (op);
+           jit_insn_branch_if_not (func, compare, &labels[op]);
+           DISCARD (1);
+           break;
+         }
+
+       case Breturn:
+         jit_insn_return (func, TOP);
+         pc = -1;
+         break;
+
+       case Bdiscard:
+         DISCARD (1);
+         break;
+
+       case Bsave_excursion:
+         jit_insn_call_native (func, "record_unwind_protect_excursion",
+                               (void *) record_unwind_protect_excursion,
+                               record_unwind_protect_excursion_signature,
+                               NULL, 0, JIT_CALL_NOTHROW);
+         break;
+
+       case Bsave_current_buffer: /* Obsolete since ??.  */
+       case Bsave_current_buffer_1:
+         jit_insn_call_native (func, "record_unwind_current_buffer",
+                               (void *) record_unwind_current_buffer,
+                               void_void_signature,
+                               NULL, 0, JIT_CALL_NOTHROW);
+         break;
+
+       case Bsave_window_excursion: /* Obsolete since 24.1.  */
+         {
+           compile_unary (func, "save-window-excursion",
+                          native_save_window_excursion, TOP);
+           break;
+         }
+
+       case Bsave_restriction:
+         {
+           jit_value_t vals[2];
+
+           vals[0] = CONSTANT (func, save_restriction_restore);
+           vals[1] = jit_insn_call_native (func, "save_restriction_save",
+                                           (void *) save_restriction_save,
+                                           void_void_signature,
+                                           NULL, 0, JIT_CALL_NOTHROW);
+           jit_insn_call_native (func, "record_unwind_protect",
+                                 (void *) record_unwind_protect,
+                                 record_unwind_protect_signature,
+                                 vals, 2, JIT_CALL_NOTHROW);
+           break;
+         }
+
+       case Bcatch:            /* Obsolete since 24.4.  */
+         {
+           jit_value_t args[3];
+
+           args[1] = CONSTANT (func, eval_sub);
+           args[2] = POP;
+           args[0] = POP;
+
+           /* FIXME this lies about the signature.  */
+           jit_value_t result = jit_insn_call_native (func, "internal_catch",
+                                                      internal_catch,
+                                                      ternary_signature,
+                                                      args, 3,
+                                                      JIT_CALL_NOTHROW);
+           PUSH (result);
+           break;
+         }
+
+       case Bpushcatch:        /* New in 24.4.  */
+         type = CATCHER;
+         goto pushhandler;
+       case Bpushconditioncase: /* New in 24.4.  */
+         type = CONDITION_CASE;
+       pushhandler:
+         {
+           jit_value_t args[2];
+           jit_value_t handler, cond;
+           int handler_pc = FETCH2;
+
+           args[0] = POP;
+           args[1] = jit_value_create_nint_constant (func, jit_type_sys_int,
+                                                     type);
+
+           handler = jit_insn_call_native (func, "push_handler", push_handler,
+                                           push_handler_signature,
+                                           args, 2, JIT_CALL_NOTHROW);
+           jit_value_t jmp
+             = jit_insn_add_relative (func, handler,
+                                      offsetof (struct handler, jmp));
+
+           /* FIXME probably should be using the same as the rest of
+              emacs.  */
+           cond = jit_insn_call_native (func, "sys_setjmp", setjmp,
+                                        setjmp_signature,
+                                        &jmp, 1, JIT_CALL_NOTHROW);
+           PUSH_PC (pc);
+           jit_insn_branch_if_not (func, cond, &labels[pc]);
+
+           /* Something threw to here.  */
+           jit_value_t hlist = compile_next_handlerlist (func);
+
+           jit_value_t val
+             = jit_insn_load_relative (func, hlist,
+                                       offsetof (struct handler, val),
+                                       jit_type_void_ptr);
+
+           PUSH (val);
+           PUSH_PC (handler_pc);
+           jit_insn_branch (func, &labels[handler_pc]);
+
+           pc = -1;
+           break;
+         }
+
+       case Bpophandler:       /* New in 24.4.  */
+         {
+           compile_next_handlerlist (func);
+           break;
+         }
+
+       case Bunwind_protect:   /* FIXME: avoid closure for lexbind.  */
+         {
+           jit_value_t args[2];
+
+           args[0] = CONSTANT (func, bcall0);
+           args[1] = POP;
+           jit_insn_call_native (func, "record_unwind_protect",
+                                 (void *) record_unwind_protect,
+                                 record_unwind_protect_signature,
+                                 args, 2, JIT_CALL_NOTHROW);
+           break;
+         }
+
+       case Bcondition_case:           /* Obsolete since 24.4.  */
+         {
+           jit_value_t handlers = POP, body = POP;
+           compile_ternary (func, "condition-case",
+                            internal_lisp_condition_case, TOP,
+                            body, handlers);
+           break;
+         }
+
+       case Btemp_output_buffer_setup: /* Obsolete since 24.1.  */
+         {
+           compile_unary (func, "temp-output-buffer-setup",
+                          native_temp_output_buffer_setup, TOP);
+           break;
+         }
+
+       case Btemp_output_buffer_show: /* Obsolete since 24.1.  */
+         {
+           jit_value_t v1 = POP;
+           jit_value_t v2 = TOP;
+           jit_value_t tem;
+
+           jit_insn_call_native (func, "temp_output_buffer_show",
+                                 (void *) temp_output_buffer_show,
+                                 temp_output_buffer_show_signature,
+                                 &v2, 1, JIT_CALL_NOTHROW);
+           jit_insn_store (func, TOP, v1);
+
+           tem = jit_value_create_nint_constant (func, jit_type_sys_int, 1);
+           jit_insn_call_native (func, "unbind_n", (void *) unbind_n,
+                                 unbind_n_signature, &tem, 1,
+                                 JIT_CALL_NOTHROW);
+           break;
+         }
+
+       case Bnth:
+         {
+           jit_value_t v2 = POP;
+           compile_binary (func, "nth", Fnth, TOP, v2);
+           break;
+         }
+
+       case Bsymbolp:
+         {
+           jit_value_t compare = compare_type (func, TOP, Lisp_Symbol);
+           emit_qnil_or_qt (func, bytestr_data, pc, compare, TOP, labels);
+           break;
+         }
+
+       case Bconsp:
+         {
+           jit_value_t compare = compare_type (func, TOP, Lisp_Cons);
+           emit_qnil_or_qt (func, bytestr_data, pc, compare, TOP, labels);
+           break;
+         }
+
+       case Bstringp:
+         {
+           jit_value_t compare = compare_type (func, TOP, Lisp_String);
+           emit_qnil_or_qt (func, bytestr_data, pc, compare, TOP, labels);
+           break;
+         }
+
+       case Blistp:
+         {
+           jit_value_t tem, nilval;
+           jit_label_t not_a_cons = jit_label_undefined;
+           jit_label_t not_nil = jit_label_undefined;
+
+           jit_value_t is_cons = compare_type (func, TOP, Lisp_Cons);
+           jit_insn_branch_if_not (func, is_cons, &not_a_cons);
+
+           /* Is a cons.  */
+           tem = CONSTANT (func, Qt);
+           jit_insn_store (func, TOP, tem);
+           jit_insn_branch (func, &labels[pc]);
+
+           jit_insn_label (func, &not_a_cons);
+
+           nilval = CONSTANT (func, Qnil);
+           tem = jit_insn_eq (func, TOP, nilval);
+
+           jit_insn_branch_if_not (func, tem, &not_nil);
+
+           /* Is nil.  */
+           tem = CONSTANT (func, Qt);
+           jit_insn_store (func, TOP, tem);
+           jit_insn_branch (func, &labels[pc]);
+
+           jit_insn_label (func, &not_nil);
+           jit_insn_store (func, TOP, nilval);
+         }
+         break;
+
+       case Bnot:
+         {
+           jit_value_t compare = eq_nil (func, TOP);
+           emit_qnil_or_qt (func, bytestr_data, pc, compare, TOP, labels);
+           break;
+         }
+
+       case Bcons:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "cons", Fcons, TOP, v1);
+           break;
+         }
+
+       case BlistN:
+         op = FETCH;
+         goto make_list;
+
+       case Blist1:
+       case Blist2:
+       case Blist3:
+       case Blist4:
+         op = op + 1 - Blist1;
+       make_list:
+         {
+           jit_value_t args[2];
+
+           int i;
+           args[1] = CONSTANT (func, Qnil);
+
+           for (i = 0; i < op; ++i)
+             {
+               args[0] = POP;
+               args[1] = jit_insn_call_native (func, "cons", (void *) Fcons,
+                                               binary_signature, args, 2,
+                                               JIT_CALL_NOTHROW);
+             }
+
+           PUSH (args[1]);
+           break;
+         }
+
+       case Blength:
+         compile_unary (func, "length", Flength, TOP);
+         break;
+
+       case Baref:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "aref", Faref, TOP, v1);
+           break;
+         }
+
+       case Baset:
+         {
+           jit_value_t v2 = POP, v1 = POP;
+           compile_ternary (func, "aset", Faset, TOP, v1, v2);
+           break;
+         }
+
+       case Bsymbol_value:
+         compile_unary (func, "symbol-value", Fsymbol_value, TOP);
+         break;
+
+       case Bsymbol_function:
+         compile_unary (func, "symbol-function", Fsymbol_function, TOP);
+         break;
+
+       case Bset:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "set", Fset, TOP, v1);
+           break;
+         }
+
+       case Bfset:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "fset", Ffset, TOP, v1);
+           break;
+         }
+
+       case Bget:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "get", Fget, TOP, v1);
+           break;
+         }
+
+       case Bsubstring:
+         {
+           jit_value_t v2 = POP, v1 = POP;
+           compile_ternary (func, "substring", Fsubstring, TOP, v1, v2);
+           break;
+         }
+
+       case Bconcat2:
+         {
+           COMPILE_CALLN (Fconcat, 2);
+           break;
+         }
+
+       case Bconcat3:
+         {
+           COMPILE_CALLN (Fconcat, 3);
+           break;
+         }
+
+       case Bconcat4:
+         {
+           COMPILE_CALLN (Fconcat, 4);
+           break;
+         }
+
+       case BconcatN:
+         {
+           op = FETCH;
+           COMPILE_CALLN (Fconcat, op);
+           break;
+         }
+
+       case Bsub1:
+         unary_intmath (func, TOP, SUB1, &labels[pc]);
+         break;
+
+       case Badd1:
+         unary_intmath (func, TOP, ADD1, &labels[pc]);
+         break;
+
+       case Beqlsign:
+         {
+           jit_value_t v1 = POP;
+           compile_arithcompare (func, "=", TOP, v1, ARITH_EQUAL);
+           break;
+         }
+
+       case Bgtr:
+         {
+           jit_value_t v1 = POP;
+           compile_arithcompare (func, ">", TOP, v1, ARITH_GRTR);
+           break;
+         }
+
+       case Blss:
+         {
+           jit_value_t v1 = POP;
+           compile_arithcompare (func, "<", TOP, v1, ARITH_LESS);
+           break;
+         }
+
+       case Bleq:
+         {
+           jit_value_t v1 = POP;
+           compile_arithcompare (func, "<=", TOP, v1, ARITH_LESS_OR_EQUAL);
+           break;
+         }
+
+       case Bgeq:
+         {
+           jit_value_t v1 = POP;
+           compile_arithcompare (func, ">=", TOP, v1, ARITH_GRTR_OR_EQUAL);
+           break;
+         }
+
+       case Bdiff:
+         {
+           COMPILE_CALLN (Fminus, 2);
+           break;
+         }
+
+       case Bnegate:
+         unary_intmath (func, TOP, NEGATE, &labels[pc]);
+         break;
+
+       case Bplus:
+         {
+           COMPILE_CALLN (Fplus, 2);
+           break;
+         }
+
+       case Bmax:
+         {
+           COMPILE_CALLN (Fmax, 2);
+           break;
+         }
+
+       case Bmin:
+         {
+           COMPILE_CALLN (Fmin, 2);
+           break;
+         }
+
+       case Bmult:
+         {
+           COMPILE_CALLN (Ftimes, 2);
+           break;
+         }
+
+       case Bquo:
+         {
+           COMPILE_CALLN (Fquo, 2);
+           break;
+         }
+
+       case Brem:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "rem", Frem, TOP, v1);
+           break;
+         }
+
+       case Bpoint:
+         PUSH (COMPILE_BUFFER_INT (pt));
+         break;
+
+       case Bgoto_char:
+         compile_unary (func, "goto-char", Fgoto_char, TOP);
+         break;
+
+       case Binsert:
+         {
+           COMPILE_CALLN (Finsert, 1);
+           break;
+         }
+
+       case BinsertN:
+         {
+           op = FETCH;
+           COMPILE_CALLN (Finsert, op);
+           break;
+         }
+
+       case Bpoint_max:
+         PUSH (COMPILE_BUFFER_INT (zv));
+         break;
+
+       case Bpoint_min:
+         PUSH (COMPILE_BUFFER_INT (begv));
+         break;
+
+       case Bchar_after:
+         compile_unary (func, "char-after", Fchar_after, TOP);
+         break;
+
+       case Bfollowing_char:
+         PUSH (compile_nullary (func, "following-char", Ffollowing_char));
+         break;
+
+       case Bpreceding_char:
+         PUSH (compile_nullary (func, "previous-char", Fprevious_char));
+         break;
+
+       case Bcurrent_column:
+         PUSH (compile_nullary (func, "current-column", Fcurrent_column));
+         break;
+
+       case Bindent_to:
+         {
+           jit_value_t tem = CONSTANT (func, Qnil);
+           compile_binary (func, "indent-to", Findent_to, TOP, tem);
+           break;
+         }
+
+       case Beolp:
+         PUSH (compile_nullary (func, "eolp", Feolp));
+         break;
+
+       case Beobp:
+         {
+           jit_value_t compare = COMPARE_BUFFER_INTS (pt, zv);
+           ++stack_pointer;
+           emit_qnil_or_qt (func, bytestr_data, pc, compare, TOP, labels);
+           break;
+         }
+
+       case Bbolp:
+         PUSH (compile_nullary (func, "bolp", Fbolp));
+         break;
+
+       case Bbobp:
+         {
+           jit_value_t compare = COMPARE_BUFFER_INTS (pt, begv);
+           ++stack_pointer;
+           emit_qnil_or_qt (func, bytestr_data, pc, compare, TOP, labels);
+           break;
+         }
+
+       case Bcurrent_buffer:
+         PUSH (compile_nullary (func, "current-buffer", Fcurrent_buffer));
+         break;
+
+       case Bset_buffer:
+         compile_unary (func, "set-buffer", Fset_buffer, TOP);
+         break;
+
+       case Binteractive_p:    /* Obsolete since 24.1.  */
+         {
+           jit_value_t arg = CONSTANT (func, Qinteractive_p);
+           jit_value_t result = jit_insn_call_native (func, "interactive-p",
+                                                      (void *) call0,
+                                                      unary_signature, &arg, 1,
+                                                      JIT_CALL_NOTHROW);
+           PUSH (result);
+           break;
+         }
+
+       case Bforward_char:
+         compile_unary (func, "forward-char", Fforward_char, TOP);
+         break;
+
+       case Bforward_word:
+         compile_unary (func, "forward-word", Fforward_word, TOP);
+         break;
+
+       case Bskip_chars_forward:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "skip-chars-forward", Fskip_chars_forward,
+                           TOP, v1);
+           break;
+         }
+
+       case Bskip_chars_backward:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "skip-chars-backward", Fskip_chars_backward,
+                           TOP, v1);
+           break;
+         }
+
+       case Bforward_line:
+         compile_unary (func, "forward-line", Fforward_line, TOP);
+         break;
+
+       case Bchar_syntax:
+         compile_unary (func, "char-syntax", Fchar_syntax, TOP);
+         break;
+
+       case Bbuffer_substring:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "buffer-substring", Fbuffer_substring,
+                           TOP, v1);
+           break;
+         }
+
+       case Bdelete_region:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "delete-region", Fdelete_region, TOP, v1);
+           break;
+         }
+
+       case Bnarrow_to_region:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "narrow-to-region", Fnarrow_to_region,
+                           TOP, v1);
+           break;
+         }
+
+       case Bwiden:
+         PUSH (compile_nullary (func, "widen", Fwiden));
+         break;
+
+       case Bend_of_line:
+         compile_unary (func, "end-of-line", Fend_of_line, TOP);
+         break;
+
+       case Bset_marker:
+         {
+           jit_value_t v2 = POP, v1 = POP;
+           compile_ternary (func, "set-marker", Fset_marker, TOP, v1, v2);
+           break;
+         }
+
+       case Bmatch_beginning:
+         compile_unary (func, "match-beginning", Fmatch_beginning, TOP);
+         break;
+
+       case Bmatch_end:
+         compile_unary (func, "match-end", Fmatch_end, TOP);
+         break;
+
+       case Bupcase:
+         compile_unary (func, "upcase", Fupcase, TOP);
+         break;
+
+       case Bdowncase:
+         compile_unary (func, "downcase", Fdowncase, TOP);
+         break;
+
+       case Bstringeqlsign:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "string=", Fstring_equal, TOP, v1);
+           break;
+         }
+
+       case Bstringlss:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "string<", Fstring_lessp, TOP, v1);
+           break;
+         }
+
+       case Bequal:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "equal", Fequal, TOP, v1);
+           break;
+         }
+
+       case Bnthcdr:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "nthcdr", Fnthcdr, TOP, v1);
+           break;
+         }
+
+       case Belt:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "elt", Felt, TOP, v1);
+           break;
+         }
+
+       case Bmember:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "member", Fmember, TOP, v1);
+           break;
+         }
+
+       case Bassq:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "assq", Fassq, TOP, v1);
+           break;
+         }
+
+       case Bnreverse:
+         compile_unary (func, "nreverse", Fnreverse, TOP);
+         break;
+
+       case Bsetcar:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "setcar", Fsetcar, TOP, v1);
+           break;
+         }
+
+       case Bsetcdr:
+         {
+           jit_value_t v1 = POP;
+           compile_binary (func, "setcdr", Fsetcdr, TOP, v1);
+           break;
+         }
+
+       case Bcar_safe:
+         car_or_cdr (func, TOP, offsetof (struct Lisp_Cons, u.s.car),
+                     &labels[pc], true,
+                     &called_wtype, &wtype_label, wtype_arg);
+         break;
+
+       case Bcdr_safe:
+         car_or_cdr (func, TOP, offsetof (struct Lisp_Cons, u.s.u.cdr),
+                     &labels[pc], true,
+                     &called_wtype, &wtype_label, wtype_arg);
+         break;
+
+       case Bnconc:
+         {
+           COMPILE_CALLN (Fnconc, 2);
+           break;
+         }
+
+       case Bnumberp:
+         {
+           jit_label_t push_t = jit_label_undefined;
+           jit_label_t push_nil = jit_label_undefined;
+
+           jit_value_t val = POP;
+           jit_value_t type;
+           jit_value_t compare = compare_integerp (func, val, &type);
+           jit_insn_branch_if (func, compare, &push_t);
+
+           jit_value_t type_val
+             = jit_value_create_nint_constant (func, jit_type_void_ptr,
+                                               Lisp_Float);
+           compare = jit_insn_eq (func, type, type_val);
+
+           jit_insn_branch_if (func, compare, &push_t);
+
+           jit_insn_label (func, &push_nil);
+           PUSH (CONSTANT (func, Qnil));
+           jit_insn_branch (func, &labels[pc]);
+
+           --stack_pointer;
+           jit_insn_label (func, &push_t);
+           PUSH (CONSTANT (func, Qt));
+
+           break;
+         }
+
+       case Bintegerp:
+         {
+           jit_value_t compare = compare_integerp (func, TOP, NULL);
+           emit_qnil_or_qt (func, bytestr_data, pc, compare, TOP, labels);
+           break;
+         }
+
+         /* Handy byte-codes for lexical binding.  */
+       case Bstack_ref1:
+       case Bstack_ref2:
+       case Bstack_ref3:
+       case Bstack_ref4:
+       case Bstack_ref5:
+         {
+           jit_value_t v1 = stack[stack_pointer - (op - Bstack_ref)];
+           PUSH (v1);
+           break;
+         }
+       case Bstack_ref6:
+         {
+           jit_value_t v1 = stack[stack_pointer - FETCH];
+           PUSH (v1);
+           break;
+         }
+       case Bstack_ref7:
+         {
+           jit_value_t v1 = stack[stack_pointer - FETCH2];
+           PUSH (v1);
+           break;
+         }
+       case Bstack_set:
+         /* stack-set-0 = discard; stack-set-1 = discard-1-preserve-tos.  */
+         {
+           jit_value_t tos = POP;
+           op = FETCH;
+           if (op > 0)
+             jit_insn_store (func, stack[stack_pointer + 1 - op], tos);
+           break;
+         }
+       case Bstack_set2:
+         {
+           jit_value_t tos = POP;
+           op = FETCH2;
+           if (op > 0)
+             jit_insn_store (func, stack[stack_pointer + 1 - op], tos);
+           break;
+         }
+       case BdiscardN:
+         op = FETCH;
+         if (op & 0x80)
+           {
+             op &= 0x7F;
+             jit_insn_store (func, stack[stack_pointer - op], TOP);
+           }
+         DISCARD (op);
+         break;
+
+        case Bswitch:
+         /* The cases of Bswitch that we handle (which in theory is
+            all of them) are done in Bconstant, below.  This is done
+            due to a design issue with Bswitch -- it should have
+            taken a constant pool index inline, but instead looks for
+            a constant on the stack.  */
+         goto fail;
+
+       case Bconstant2:
+         op = FETCH2;
+         goto do_constant;
+
+       default:
+       case Bconstant:
+         {
+           if (op < Bconstant || op > Bconstant + vector_size)
+             goto fail;
+
+           op -= Bconstant;
+
+         do_constant:
+
+           /* See the Bswitch case for commentary.  */
+           if (pc >= bytestr_length || bytestr_data[pc] != Bswitch)
+             {
+               jit_value_t c = CONSTANT (func, vectorp[op]);
+               PUSH (c);
+               break;
+             }
+
+           /* We're compiling Bswitch instead.  */
+           ++pc;
+           Lisp_Object htab = vectorp[op];
+            struct Lisp_Hash_Table *h = XHASH_TABLE (vectorp[op]);
+
+           /* Minimum and maximum PC values for the table.  */
+           EMACS_INT min_pc = bytestr_length, max_pc = 0;
+           if (!find_hash_min_max_pc (h, &min_pc, &max_pc))
+             goto fail;
+
+           jit_value_t args[3];
+           args[0] = POP;
+           args[1] = CONSTANT (func, htab);
+           args[2] = CONSTANT (func, Qnil);
+
+           jit_value_t value
+             = jit_insn_call_native (func, "Fgethash", (void *) Fgethash,
+                                     ternary_signature, args, 3,
+                                     JIT_CALL_NOTHROW);
+           jit_value_t compare = jit_insn_eq (func, value, args[2]);
+
+           jit_label_t default_case = jit_function_reserve_label (func);
+           jit_insn_branch_if (func, compare, &default_case);
+
+           /* Note that we know the type because we check it when
+              walking the hash table, and the hash table is
+              (effectively) immutable.  */
+           value = to_int (func, value);
+           jit_value_t min_pc_val
+             = jit_value_create_nint_constant (func, jit_type_sys_int,
+                                               min_pc);
+           value = jit_insn_sub (func, value, min_pc_val);
+
+           /* Initialize switch labels.  */
+           for (int i = 0; i < max_pc - min_pc; ++i)
+             sw_labels[i] = default_case;
+
+           /* Fill in the switch table.  */
+           for (ptrdiff_t i = 0; i < HASH_TABLE_SIZE (h); ++i)
+             {
+               if (!NILP (HASH_HASH (h, i)))
+                 {
+                   Lisp_Object pc = HASH_VALUE (h, i);
+                   /* This was already checked by
+                      find_hash_min_max_pc.  */
+                   eassert (FIXNUMP (pc));
+                   EMACS_INT pcval = XFIXNUM (pc);
+
+                   /* Make sure that the label we'll need is defined.  */
+                   if (labels[pcval] == jit_label_undefined)
+                     labels[pcval] = jit_function_reserve_label (func);
+
+                   sw_labels[pcval - min_pc] = labels[pcval];
+
+                   PUSH_PC (pcval);
+                 }
+             }
+
+           jit_insn_jump_table (func, value, sw_labels, max_pc - min_pc);
+           jit_insn_label (func, &default_case);
+           break;
+         }
+       }
+    }
+
+  if (scratch_slots_needed > 0)
+    {
+      jit_label_t init_start = jit_label_undefined;
+      jit_label_t init_end = jit_label_undefined;
+
+      jit_insn_label (func, &init_start);
+      jit_value_t scratch_size
+       = jit_value_create_nint_constant (func, jit_type_sys_int,
+                                         (scratch_slots_needed
+                                          * sizeof (Lisp_Object)));
+      jit_value_t allocated = jit_insn_alloca (func, scratch_size);
+      jit_insn_store (func, scratch, allocated);
+      jit_insn_label (func, &init_end);
+
+      jit_insn_move_blocks_to_start (func, init_start, init_end);
+    }
+
+  if (need_argfail)
+    {
+      jit_value_t args[3];
+      jit_insn_label (func, &argfail);
+      args[0] = mandatory_val;
+      args[1] = nonrest_val;
+      args[2] = n_args;
+      jit_insn_call_native (func, "wrong-number-of-arguments",
+                           (void *) wrong_number_of_arguments,
+                           wrong_number_of_arguments_signature,
+                           args, 3, JIT_CALL_NORETURN);
+    }
+
+  if (called_wtype)
+    compile_wrong_type_argument (func, &wtype_label, wtype_arg);
+
+  if (!jit_function_compile (func))
+    {
+      /* Boo!  */
+    fail:
+      jit_function_abandon (func);
+      result = NULL;
+
+      /* Be sure to clean up.  */
+      while (pc_list != NULL)
+       {
+         struct pc_list *next = pc_list->next;
+         xfree (pc_list);
+         pc_list = next;
+       }
+    }
+  else
+    {
+      /* FIXME bogus cast.  */
+      result->function.a0
+       = (Lisp_Object (*) (void)) jit_function_to_closure (func);
+    }
+
+  xfree (stack);
+  xfree (labels);
+  xfree (sw_labels);
+  xfree (stack_depths);
+  eassert (pc_list == NULL);
+
+  return result;
+}
+
+void
+emacs_jit_compile (Lisp_Object func)
+{
+  if (!emacs_jit_initialized)
+    return;
+
+  Lisp_Object bytestr = AREF (func, COMPILED_BYTECODE);
+  CHECK_STRING (bytestr);
+  if (STRING_MULTIBYTE (bytestr))
+    /* BYTESTR must have been produced by Emacs 20.2 or the earlier
+       because they produced a raw 8-bit string for byte-code and now
+       such a byte-code string is loaded as multibyte while raw 8-bit
+       characters converted to multibyte form.  Thus, now we must
+       convert them back to the originally intended unibyte form.  */
+    bytestr = Fstring_as_unibyte (bytestr);
+
+  ptrdiff_t bytestr_length = SBYTES (bytestr);
+
+  Lisp_Object vector = AREF (func, COMPILED_CONSTANTS);
+  CHECK_VECTOR (vector);
+  Lisp_Object *vectorp = XVECTOR (vector)->contents;
+
+  Lisp_Object maxdepth = AREF (func, COMPILED_STACK_DEPTH);
+  CHECK_FIXNAT (maxdepth);
+
+  jit_context_build_start (emacs_jit_context);
+  struct subr_function *subr = compile (bytestr_length, SDATA (bytestr),
+                                       XFIXNAT (maxdepth) + 1,
+                                       vectorp, ASIZE (vector),
+                                       AREF (func, COMPILED_ARGLIST));
+
+  XVECTOR (func)->contents[COMPILED_JIT_CODE] = (Lisp_Object) subr;
+
+  jit_context_build_end (emacs_jit_context);
+}
+
+DEFUN ("jit-compile", Fjit_compile, Sjit_compile,
+       1, 1, 0,
+       doc: /* JIT compile a function.  */)
+  (Lisp_Object func)
+{
+  struct Lisp_Vector *vec;
+
+  if (!COMPILEDP (func))
+    error ("Not a byte-compiled function");
+
+  vec = XVECTOR (func);
+  if (vec->contents[COMPILED_JIT_CODE] == NULL)
+    emacs_jit_compile (func);
+
+  return Qnil;
+}
+
+DEFUN ("jit-disassemble-to-string", Fjit_disassemble_to_string,
+       Sjit_disassemble_to_string, 1, 1, 0,
+       doc: /* Disassemble a JIT-compiled function and return a string with 
the disassembly.  */)
+  (Lisp_Object func)
+{
+  char *buffer = NULL;
+  size_t size = 0;
+  FILE *stream;
+  Lisp_Object str;
+  struct Lisp_Vector *vec;
+  jit_function_t cfunc;
+  struct subr_function *sfunc;
+
+  if (!COMPILEDP (func))
+    error ("Not a byte-compiled function");
+
+#ifdef HAVE_OPEN_MEMSTREAM
+  vec = XVECTOR (func);
+  sfunc = (struct subr_function *) vec->contents[COMPILED_JIT_CODE];
+  if (sfunc == NULL)
+    error ("Not JIT-compiled");
+
+  cfunc = jit_function_from_closure (emacs_jit_context, sfunc->function.a0);
+  stream = open_memstream (&buffer, &size);
+  jit_dump_function (stream, cfunc, "Function");
+  fclose (stream);
+
+  str = make_string (buffer, size);
+
+  xfree (buffer);
+  return str;
+#else
+  error ("Cannot disassemble JIT code in this build: open_memstream missing");
+#endif
+}
+
+void
+syms_of_jit (void)
+{
+  defsubr (&Sjit_compile);
+  defsubr (&Sjit_disassemble_to_string);
+  DEFSYM (Qinteractive_p, "interactive-p");
+}
+
+void
+init_jit (void)
+{
+#define LEN SUBR_MAX_ARGS
+
+  jit_type_t params[LEN];
+  int i;
+
+  jit_init ();
+  emacs_jit_context = jit_context_create ();
+
+  if (sizeof (ptrdiff_t) == 8)
+    ptrdiff_t_type = jit_type_ulong;
+  else
+    {
+      eassert (sizeof (ptrdiff_t) == 4);
+      ptrdiff_t_type = jit_type_uint;
+    }
+
+  for (i = 0; i < LEN; ++i)
+    params[i] = jit_type_void_ptr;
+
+  for (i = 0; i < SUBR_MAX_ARGS; ++i)
+    subr_signature[i] = jit_type_create_signature (jit_abi_cdecl,
+                                                  jit_type_void_ptr,
+                                                  params, i, 1);
+
+  nullary_signature = jit_type_create_signature (jit_abi_cdecl,
+                                                jit_type_void_ptr, params, 0,
+                                                1);
+  unary_signature = jit_type_create_signature (jit_abi_cdecl,
+                                              jit_type_void_ptr, params, 1,
+                                              1);
+  binary_signature = jit_type_create_signature (jit_abi_cdecl,
+                                               jit_type_void_ptr, params, 2,
+                                               1);
+  ternary_signature = jit_type_create_signature (jit_abi_cdecl,
+                                                jit_type_void_ptr, params, 3,
+                                                1);
+  specbind_signature = jit_type_create_signature (jit_abi_cdecl,
+                                                 jit_type_void, params, 2, 1);
+  record_unwind_protect_excursion_signature
+    = jit_type_create_signature (jit_abi_cdecl, jit_type_void_ptr, NULL, 0, 1);
+  record_unwind_protect_signature
+    = jit_type_create_signature (jit_abi_cdecl, jit_type_void, params, 2, 1);
+  void_void_signature = jit_type_create_signature (jit_abi_cdecl,
+                                                  jit_type_void, NULL, 0, 1);
+
+  temp_output_buffer_show_signature
+    = jit_type_create_signature (jit_abi_cdecl, jit_type_void_ptr,
+                                params, 1, 1);
+
+  params[2] = jit_type_sys_int;
+  arithcompare_signature = jit_type_create_signature (jit_abi_cdecl,
+                                                     jit_type_void_ptr,
+                                                     params, 3, 1);
+
+  params[0] = jit_type_sys_int;
+  unbind_n_signature = jit_type_create_signature (jit_abi_cdecl,
+                                                 jit_type_void_ptr, params, 1,
+                                                 1);
+
+  params[0] = ptrdiff_t_type;
+  callN_signature = jit_type_create_signature (jit_abi_cdecl,
+                                              jit_type_void_ptr, params, 2,
+                                              1);
+  compiled_signature = callN_signature;
+
+  params[0] = jit_type_sys_int;
+  params[1] = jit_type_sys_int;
+  params[2] = jit_type_sys_int;
+  wrong_number_of_arguments_signature
+    = jit_type_create_signature (jit_abi_cdecl, jit_type_void, params, 3, 1);
+
+  params[0] = jit_type_void_ptr;
+  params[1] = jit_type_void_ptr;
+  params[2] = jit_type_void_ptr;
+  params[3] = jit_type_sys_int;
+  set_internal_signature
+    = jit_type_create_signature (jit_abi_cdecl, jit_type_void, params, 4, 1);
+
+  setjmp_signature = jit_type_create_signature (jit_abi_cdecl,
+                                               jit_type_sys_int,
+                                               params, 1, 1);
+
+  params[1] = jit_type_sys_int;
+  push_handler_signature = jit_type_create_signature (jit_abi_cdecl,
+                                                     jit_type_void_ptr,
+                                                     params, 2, 1);
+
+  emacs_jit_initialized = true;
+}
+
+#endif /* HAVE_LIBJIT */
diff --git a/src/lisp.h b/src/lisp.h
index 18d5353..b44d632 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -1891,13 +1891,12 @@ CHAR_TABLE_SET (Lisp_Object ct, int idx, Lisp_Object 
val)
     char_table_set (ct, idx, val);
 }
 
-/* This structure describes a built-in function.
-   It is generated by the DEFUN macro only.
-   defsubr makes it into a Lisp object.  */
+/* The inner part of a Lisp_Subr, used when calling the function.
+   This is separate so it can be reused by the JIT compiler without
+   requiring an entire Lisp_Subr to be created there.  */
 
-struct Lisp_Subr
+struct subr_function
   {
-    union vectorlike_header header;
     union {
       Lisp_Object (*a0) (void);
       Lisp_Object (*a1) (Lisp_Object);
@@ -1912,6 +1911,18 @@ struct Lisp_Subr
       Lisp_Object (*aMANY) (ptrdiff_t, Lisp_Object *);
     } function;
     short min_args, max_args;
+  };
+
+#define SUBR_MAX_ARGS 9
+
+/* This structure describes a built-in function.
+   It is generated by the DEFUN macro only.
+   defsubr makes it into a Lisp object.  */
+
+struct Lisp_Subr
+  {
+    union vectorlike_header header;
+    struct subr_function function;
     const char *symbol_name;
     const char *intspec;
     EMACS_INT doc;
@@ -2634,7 +2645,8 @@ enum Lisp_Compiled
     COMPILED_CONSTANTS = 2,
     COMPILED_STACK_DEPTH = 3,
     COMPILED_DOC_STRING = 4,
-    COMPILED_INTERACTIVE = 5
+    COMPILED_INTERACTIVE = 5,
+    COMPILED_JIT_CODE = 6
   };
 
 /* Flag bits in a character.  These also get used in termhooks.h.
@@ -2916,8 +2928,8 @@ CHECK_FIXNUM_CDR (Lisp_Object x)
 #define DEFUN(lname, fnname, sname, minargs, maxargs, intspec, doc)    \
    static struct Lisp_Subr sname =                             \
      { { PVEC_SUBR << PSEUDOVECTOR_AREA_BITS },                                
\
-       { .a ## maxargs = fnname },                                     \
-       minargs, maxargs, lname, intspec, 0};                           \
+       { { .a ## maxargs = fnname },                                   \
+       minargs, maxargs }, lname, intspec, 0};                         \
    Lisp_Object fnname
 
 /* defsubr (Sname);
@@ -3649,7 +3661,6 @@ build_string (const char *str)
 }
 
 extern Lisp_Object pure_cons (Lisp_Object, Lisp_Object);
-extern void make_byte_code (struct Lisp_Vector *);
 extern struct Lisp_Vector *allocate_vector (EMACS_INT);
 
 /* Make an uninitialized vector for SIZE objects.  NOTE: you must
@@ -3848,7 +3859,9 @@ extern _Noreturn void xsignal3 (Lisp_Object, Lisp_Object, 
Lisp_Object,
                                Lisp_Object);
 extern _Noreturn void signal_error (const char *, Lisp_Object);
 extern bool FUNCTIONP (Lisp_Object);
-extern Lisp_Object funcall_subr (struct Lisp_Subr *subr, ptrdiff_t numargs, 
Lisp_Object *arg_vector);
+extern Lisp_Object funcall_subr (Lisp_Object error_obj,
+                                struct subr_function *subr,
+                                ptrdiff_t numargs, Lisp_Object *arg_vector);
 extern Lisp_Object eval_sub (Lisp_Object form);
 extern Lisp_Object apply1 (Lisp_Object, Lisp_Object);
 extern Lisp_Object call0 (Lisp_Object);
@@ -4437,6 +4450,10 @@ extern bool profiler_memory_running;
 extern void malloc_probe (size_t);
 extern void syms_of_profiler (void);
 
+/* Defined in jit.c.  */
+extern void syms_of_jit (void);
+extern void init_jit (void);
+extern void emacs_jit_compile (Lisp_Object);
 
 #ifdef DOS_NT
 /* Defined in msdos.c, w32.c.  */
diff --git a/src/lread.c b/src/lread.c
index df2fe58..f2fb8d0 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -2948,8 +2948,7 @@ read1 (Lisp_Object readcharfun, int *pch, bool 
first_in_list)
          vec = XVECTOR (tmp);
          if (vec->header.size == 0)
            invalid_syntax ("Empty byte-code object");
-         make_byte_code (vec);
-         return tmp;
+         return Fmake_byte_code (vec->header.size, vec->contents);
        }
       if (c == '(')
        {
diff --git a/test/src/jit-tests.el b/test/src/jit-tests.el
new file mode 100644
index 0000000..a34f180
--- /dev/null
+++ b/test/src/jit-tests.el
@@ -0,0 +1,304 @@
+;;; jit-tests.el --- unit tests for src/jijt.c      -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2018 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Unit tests for src/jit.c.
+
+;;; Code:
+
+(require 'ert)
+
+(defun jit-test-apply (func &rest args)
+  (unless (byte-code-function-p (symbol-function func))
+    (byte-compile func))
+  (apply func args))
+
+;; Test Bconsp.
+(defun jit-test-consp (x) (consp x))
+
+(ert-deftest jit-consp ()
+  (should-not (jit-test-apply 'jit-test-consp 23))
+  (should-not (jit-test-apply 'jit-test-consp nil))
+  (should (jit-test-apply 'jit-test-consp '(1 . 2))))
+
+;; Test Blistp.
+(defun jit-test-listp (x) (listp x))
+
+(ert-deftest jit-listp ()
+  (should-not (jit-test-apply 'jit-test-listp 23))
+  (should (jit-test-apply 'jit-test-listp nil))
+  (should (jit-test-apply 'jit-test-listp '(1 . 2))))
+
+;; Test Bstringp.
+(defun jit-test-stringp (x) (stringp x))
+
+(ert-deftest jit-stringp ()
+  (should-not (jit-test-apply 'jit-test-stringp 23))
+  (should-not (jit-test-apply 'jit-test-stringp nil))
+  (should (jit-test-apply 'jit-test-stringp "hi")))
+
+;; Test Bsymbolp.
+(defun jit-test-symbolp (x) (symbolp x))
+
+(ert-deftest jit-symbolp ()
+  (should-not (jit-test-apply 'jit-test-symbolp 23))
+  (should-not (jit-test-apply 'jit-test-symbolp "hi"))
+  (should (jit-test-apply 'jit-test-symbolp 'whatever)))
+
+;; Test Bintegerp.
+(defun jit-test-integerp (x) (integerp x))
+
+(ert-deftest jit-integerp ()
+  (should (jit-test-apply 'jit-test-integerp 23))
+  (should-not (jit-test-apply 'jit-test-integerp 57.5))
+  (should-not (jit-test-apply 'jit-test-integerp "hi"))
+  (should-not (jit-test-apply 'jit-test-integerp 'whatever)))
+
+;; Test Bnumberp.
+(defun jit-test-numberp (x) (numberp x))
+
+(ert-deftest jit-numberp ()
+  (should (jit-test-apply 'jit-test-numberp 23))
+  (should (jit-test-apply 'jit-test-numberp 57.5))
+  (should-not (jit-test-apply 'jit-test-numberp "hi"))
+  (should-not (jit-test-apply 'jit-test-numberp 'whatever)))
+
+;; Test Badd1.
+(defun jit-test-add1 (x) (1+ x))
+
+(ert-deftest jit-add1 ()
+  (should (eq (jit-test-apply 'jit-test-add1 23) 24))
+  (should (eq (jit-test-apply 'jit-test-add1 -17) -16))
+  (should (eql (jit-test-apply 'jit-test-add1 1.0) 2.0))
+  (should-error (jit-test-apply 'jit-test-add1 nil)
+                :type 'wrong-type-argument))
+
+;; Test Bsub1.
+(defun jit-test-sub1 (x) (1- x))
+
+(ert-deftest jit-sub1 ()
+  (should (eq (jit-test-apply 'jit-test-sub1 23) 22))
+  (should (eq (jit-test-apply 'jit-test-sub1 -17) -18))
+  (should (eql (jit-test-apply 'jit-test-sub1 1.0) 0.0))
+  (should-error (jit-test-apply 'jit-test-sub1 nil)
+                :type 'wrong-type-argument))
+
+;; Test Bneg.
+(defun jit-test-negate (x) (- x))
+
+(ert-deftest jit-negate ()
+  (should (eq (jit-test-apply 'jit-test-negate 23) -23))
+  (should (eq (jit-test-apply 'jit-test-negate -17) 17))
+  (should (eql (jit-test-apply 'jit-test-negate 1.0) -1.0))
+  (should-error (jit-test-apply 'jit-test-negate nil)
+                :type 'wrong-type-argument))
+
+;; Test Bnot.
+(defun jit-test-not (x) (not x))
+
+(ert-deftest jit-not ()
+  (should (eq (jit-test-apply 'jit-test-not 23) nil))
+  (should (eq (jit-test-apply 'jit-test-not nil) t))
+  (should (eq (jit-test-apply 'jit-test-not t) nil)))
+
+;; Test Bbobp, Beobp, Bpoint, Bpoint_min, Bpoint_max.
+(defun jit-test-bobp () (bobp))
+(defun jit-test-eobp () (eobp))
+(defun jit-test-point () (point))
+(defun jit-test-point-min () (point-min))
+(defun jit-test-point-max () (point-max))
+
+(ert-deftest jit-bobp-and-eobp ()
+  (with-temp-buffer
+    (should (jit-test-apply 'jit-test-bobp))
+    (should (jit-test-apply 'jit-test-eobp))
+    (insert "hi")
+    (goto-char (point-min))
+    (should (eq (jit-test-apply 'jit-test-point-min) (point-min)))
+    (should (eq (jit-test-apply 'jit-test-point) (point-min)))
+    (should (jit-test-apply 'jit-test-bobp))
+    (should-not (jit-test-apply 'jit-test-eobp))
+    (goto-char (point-max))
+    (should (eq (jit-test-apply 'jit-test-point-max) (point-max)))
+    (should (eq (jit-test-apply 'jit-test-point) (point-max)))
+    (should-not (jit-test-apply 'jit-test-bobp))
+    (should (jit-test-apply 'jit-test-eobp))))
+
+;; Test Bcar and Bcdr.
+(defun jit-test-car (x) (car x))
+(defun jit-test-cdr (x) (cdr x))
+
+(ert-deftest jit-car-cdr ()
+  (let ((pair '(1 . b)))
+    (should (eq (jit-test-apply 'jit-test-car pair) 1))
+    (should (eq (jit-test-apply 'jit-test-car nil) nil))
+    (should-error (jit-test-apply 'jit-test-car 23)
+                  :type 'wrong-type-argument)
+    (should (eq (jit-test-apply 'jit-test-cdr pair) 'b))
+    (should (eq (jit-test-apply 'jit-test-cdr nil) nil))
+    (should-error (jit-test-apply 'jit-test-cdr 23)
+                  :type 'wrong-type-argument)))
+
+;; Test Bcar_safe and Bcdr_safe.
+(defun jit-test-car-safe (x) (car-safe x))
+(defun jit-test-cdr-safe (x) (cdr-safe x))
+
+(ert-deftest jit-car-cdr-safe ()
+  (let ((pair '(1 . b)))
+    (should (eq (jit-test-apply 'jit-test-car-safe pair) 1))
+    (should (eq (jit-test-apply 'jit-test-car-safe nil) nil))
+    (should (eq (jit-test-apply 'jit-test-car-safe 23) nil))
+    (should (eq (jit-test-apply 'jit-test-cdr-safe pair) 'b))
+    (should (eq (jit-test-apply 'jit-test-cdr-safe nil) nil))
+    (should (eq (jit-test-apply 'jit-test-cdr-safe 23) nil))))
+
+;; Test Beq.
+(defun jit-test-eq (x y) (eq x y))
+
+(ert-deftest jit-eq ()
+  (should (jit-test-apply 'jit-test-eq 'a 'a))
+  (should (jit-test-apply 'jit-test-eq 5 5))
+  (should-not (jit-test-apply 'jit-test-eq 'a 'b))
+  (should-not (jit-test-apply 'jit-test-eq "x" "x")))
+
+;; Test Bgotoifnil.
+(defun jit-test-if (x y) (if x x y))
+
+(ert-deftest jit-if ()
+  (should (eq (jit-test-apply 'jit-test-if 'a 'b) 'a))
+  (should (eq (jit-test-apply 'jit-test-if 0 23) 0))
+  (should (eq (jit-test-apply 'jit-test-if nil 'b) 'b)))
+
+;; Test Bgotoifnilelsepop.
+(defun jit-test-and (x y) (and x y))
+
+(ert-deftest jit-and ()
+  (should (eq (jit-test-apply 'jit-test-and 'a 'b) 'b))
+  (should (eq (jit-test-apply 'jit-test-and 0 23) 23))
+  (should (eq (jit-test-apply 'jit-test-and nil 'b) nil)))
+
+;; Test Bgotoifnonnilelsepop.
+(defun jit-test-or (x y) (or x y))
+
+(ert-deftest jit-or ()
+  (should (eq (jit-test-apply 'jit-test-or 'a 'b) 'a))
+  (should (eq (jit-test-apply 'jit-test-or 0 23) 0))
+  (should (eq (jit-test-apply 'jit-test-or nil 'b) 'b)))
+
+;; Test Bsave_excursion.
+(defun jit-test-save-excursion ()
+  (save-excursion
+    (insert "XYZ")))
+
+;; Test Bcurrent_buffer.
+(defun jit-test-current-buffer () (current-buffer))
+
+(ert-deftest jit-save-excursion ()
+  (with-temp-buffer
+    (jit-test-apply 'jit-test-save-excursion)
+    (should (eq (point) (point-min)))
+    (should (eq (jit-test-apply 'jit-test-current-buffer) (current-buffer)))))
+
+;; Test Bgtr.
+(defun jit-test-> (a b)
+  (> a b))
+
+(ert-deftest jit-> ()
+  (should (eq (jit-test-apply 'jit-test-> 0 23) nil))
+  (should (eq (jit-test-apply 'jit-test-> 23 0) t)))
+
+;; Test Bpushcatch.
+(defun jit-test-catch (&rest l)
+  (catch 'done
+    (dolist (v l)
+      (when (> v 23)
+        (throw 'done v)))))
+
+(ert-deftest jit-catch ()
+  (should (eq (jit-test-apply 'jit-test-catch 0 1 2 3 4) nil))
+  (should (eq (jit-test-apply 'jit-test-catch 20 21 22 23 24 25 26 27 28) 24)))
+
+;; Test Bmemq.
+(defun jit-test-memq (val list)
+  (memq val list))
+
+(ert-deftest jit-memq ()
+  (should (equal (jit-test-apply 'jit-test-memq 0 '(5 4 3 2 1 0)) '(0)))
+  (should (eq (jit-test-apply 'jit-test-memq 72 '(5 4 3 2 1 0)) nil)))
+
+;; Test BlistN.
+(defun jit-test-listN (x)
+  (list x x x x x x x x x x x x x x x x))
+
+(ert-deftest jit-listN ()
+  (should (equal (jit-test-apply 'jit-test-listN 57)
+                 '(57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57))))
+
+;; Test BconcatN.
+(defun jit-test-concatN (x)
+  (concat x x x x x x))
+
+(ert-deftest jit-concatN ()
+  (should (equal (jit-test-apply 'jit-test-concatN "x") "xxxxxx")))
+
+;; Test optional and rest arguments.
+(defun jit-test-opt-rest (a &optional b &rest c)
+  (list a b c))
+
+(ert-deftest jit-opt-rest ()
+  (should (equal (jit-test-apply 'jit-test-opt-rest 1) '(1 nil nil)))
+  (should (equal (jit-test-apply 'jit-test-opt-rest 1 2) '(1 2 nil)))
+  (should (equal (jit-test-apply 'jit-test-opt-rest 1 2 3) '(1 2 (3))))
+  (should (equal (jit-test-apply 'jit-test-opt-rest 1 2 56 57 58)
+                 '(1 2 (56 57 58)))))
+
+;; Test for too many arguments.
+(defun jit-test-opt (a &optional b)
+  (cons a b))
+
+(ert-deftest jit-opt ()
+  (should (equal (jit-test-apply 'jit-test-opt 23) '(23)))
+  (should (equal (jit-test-apply 'jit-test-opt 23 24) '(23 . 24)))
+  (should-error (jit-test-apply 'jit-test-opt)
+                :type 'wrong-number-of-arguments)
+  (should-error (jit-test-apply 'jit-test-opt nil 24 97)
+                :type 'wrong-number-of-arguments))
+
+;; Test for unwind-protect.
+(defvar jit-test-up-val nil)
+(defun jit-test-unwind-protect (fun)
+  (setq jit-test-up-val nil)
+  (unwind-protect
+      (progn
+        (setq jit-test-up-val 23)
+        (funcall fun)
+        (setq jit-test-up-val 24))
+    (setq jit-test-up-val 999)))
+
+(ert-deftest jit-unwind-protect ()
+  (jit-test-unwind-protect 'ignore)
+  (should (eq jit-test-up-val 999))
+  (condition-case nil
+      (jit-test-unwind-protect (lambda () (error "HI")))
+    (error
+     nil))
+  (should (eq jit-test-up-val 999)))
+
+;;; jit-tests.el ends here



reply via email to

[Prev in Thread] Current Thread [Next in Thread]