diff options
-rw-r--r-- | configure.ac | 13 | ||||
-rw-r--r-- | src/Makefile.in | 10 | ||||
-rw-r--r-- | src/alloc.c | 53 | ||||
-rw-r--r-- | src/bytecode-jit.c | 1606 | ||||
-rw-r--r-- | src/bytecode.c | 253 | ||||
-rw-r--r-- | src/bytecode.h | 320 | ||||
-rw-r--r-- | src/data.c | 1 | ||||
-rw-r--r-- | src/emacs.c | 3 | ||||
-rw-r--r-- | src/eval.c | 18 | ||||
-rw-r--r-- | src/lisp.h | 20 | ||||
-rw-r--r-- | src/lread.c | 6 |
11 files changed, 2036 insertions, 267 deletions
diff --git a/configure.ac b/configure.ac index dcba7eb2c24..0c9f70369c6 100644 --- a/configure.ac +++ b/configure.ac @@ -356,6 +356,7 @@ OPTION_DEFAULT_ON([gnutls],[don't use -lgnutls for SSL/TLS support]) OPTION_DEFAULT_ON([zlib],[don't compile with zlib decompression support]) OPTION_DEFAULT_OFF([modules],[compile with dynamic modules support]) OPTION_DEFAULT_ON([threads],[don't compile with elisp threading support]) +OPTION_DEFAULT_OFF([libjit],[compile with emacs lisp jit support]) AC_ARG_WITH([file-notification],[AS_HELP_STRING([--with-file-notification=LIB], [use a file notification library (LIB one of: yes, inotify, kqueue, gfile, w32, no)])], @@ -3416,6 +3417,17 @@ if test "${HAVE_ZLIB}" = "yes"; then fi AC_SUBST(LIBZ) +HAVE_LIBJIT=no +LIBJIT= +if test "${with_libjit}" != "no"; then + LIBJIT_REQUIRED=0.0.1 + LIBJIT_MODULES="libjit >= $LIBJIT_REQUIRED" + EMACS_CHECK_MODULES([LIBJIT], [$LIBJIT_MODULES]) + if test "${HAVE_LIBJIT}" = "yes"; then + AC_DEFINE([HAVE_LIBJIT], 1, [Define to 1 if you have the libjit library (-ljit).]) + fi +fi + ### Dynamic modules support LIBMODULES= HAVE_MODULES=no @@ -5332,6 +5344,7 @@ AS_ECHO([" Does Emacs use -lXaw3d? ${HAVE_XAW3D Does Emacs use toolkit scroll bars? ${USE_TOOLKIT_SCROLL_BARS} Does Emacs support Xwidgets (requires gtk3)? ${HAVE_XWIDGETS} Does Emacs have threading support in lisp? ${threads_enabled} + Does Emacs have lisp JIT support? ${HAVE_LIBJIT} "]) if test -n "${EMACSDATA}"; then diff --git a/src/Makefile.in b/src/Makefile.in index ab319837249..e5e534296dc 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -237,6 +237,9 @@ GETADDRINFO_A_LIBS = @GETADDRINFO_A_LIBS@ LIBZ = @LIBZ@ +LIBJIT = @LIBJIT_LIBS@ +LIBJIT_CFLAGS = @LIBJIT_CFLAGS@ + ## system-specific libs for dynamic modules, else empty LIBMODULES = @LIBMODULES@ ## dynlib.o emacs-module.o if modules enabled, else empty @@ -375,7 +378,7 @@ EMACS_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \ $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \ $(LIBSYSTEMD_CFLAGS) \ $(LIBGNUTLS_CFLAGS) $(NOTIFY_CFLAGS) $(CAIRO_CFLAGS) \ - $(WERROR_CFLAGS) + $(WERROR_CFLAGS) $(LIBJIT_CFLAGS) ALL_CFLAGS = $(EMACS_CFLAGS) $(WARN_CFLAGS) $(CFLAGS) ALL_OBJC_CFLAGS = $(EMACS_CFLAGS) \ $(filter-out $(NON_OBJC_CFLAGS),$(WARN_CFLAGS)) $(CFLAGS) \ @@ -400,7 +403,7 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \ cmds.o casetab.o casefiddle.o indent.o search.o regex.o undo.o \ alloc.o data.o doc.o editfns.o callint.o \ eval.o floatfns.o fns.o font.o print.o lread.o $(MODULES_OBJ) \ - syntax.o $(UNEXEC_OBJ) bytecode.o \ + syntax.o $(UNEXEC_OBJ) bytecode.o bytecode-jit.o \ process.o gnutls.o callproc.o \ region-cache.o sound.o atimer.o \ doprnt.o intervals.o textprop.o composite.o xml.o $(NOTIFY_OBJ) \ @@ -495,7 +498,8 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) $(LIBIMAGE) \ $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \ $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \ $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(GETADDRINFO_A_LIBS) \ - $(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) + $(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) \ + $(LIBJIT) $(leimdir)/leim-list.el: bootstrap-emacs$(EXEEXT) $(MAKE) -C ../leim leim-list.el EMACS="$(bootstrap_exe)" diff --git a/src/alloc.c b/src/alloc.c index e909d312c4e..a2302a6f462 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -30,6 +30,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include <pthread.h> #endif +#ifdef HAVE_LIBJIT +#include <jit.h> +#endif + #include "lisp.h" #include "dispextern.h" #include "intervals.h" @@ -3184,6 +3188,16 @@ cleanup_vector (struct Lisp_Vector *vector) finalize_one_mutex ((struct Lisp_Mutex *) vector); else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_CONDVAR)) finalize_one_condvar ((struct Lisp_CondVar *) vector); + else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_COMPILED) + && vector->contents[COMPILED_JIT_CTXT] != (Lisp_Object )NULL) + { +#ifdef HAVE_LIBJIT + jit_context_t ctxt = (jit_context_t )vector->contents[COMPILED_JIT_CTXT]; + jit_context_destroy (ctxt); +#endif + vector->contents[COMPILED_JIT_CTXT] = (Lisp_Object )NULL; + vector->contents[COMPILED_JIT_CLOSURE] = (Lisp_Object )NULL; + } } /* Reclaim space used by unmarked vectors. */ @@ -3422,23 +3436,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 @@ -3457,8 +3454,12 @@ 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); + Lisp_Object val = make_uninit_vector (max(nargs, COMPILED_JIT_CLOSURE + 1)); struct Lisp_Vector *p = XVECTOR (val); + size_t size = min(nargs, COMPILED_JIT_CLOSURE); + + /* 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 @@ -3469,7 +3470,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); + + 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]); + + /* set rest size so that total footprint = COMPILED_JIT_CLOSURE + 1 */ + XSETPVECTYPESIZE (p, PVEC_COMPILED, size, COMPILED_JIT_CLOSURE + 1 - size); + p->contents[COMPILED_INTERPRETER] = (Lisp_Object )exec_byte_code; + p->contents[COMPILED_JIT_CTXT] = (Lisp_Object )NULL; + p->contents[COMPILED_JIT_CLOSURE] = (Lisp_Object )NULL; XSETCOMPILED (val, p); return val; } diff --git a/src/bytecode-jit.c b/src/bytecode-jit.c new file mode 100644 index 00000000000..f97083395dd --- /dev/null +++ b/src/bytecode-jit.c @@ -0,0 +1,1606 @@ +/* JIT compilation of byte code produced by bytecomp.el. + Copyright (C) 2016 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 <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +#ifdef HAVE_LIBJIT +#include "bytecode.h" +#include "lisp.h" +#include "blockinput.h" +#include "character.h" +#include "buffer.h" +#include "keyboard.h" +#include "syntax.h" +#include "window.h" + +#include <stdarg.h> +#include <jit.h> + +/* Fetch the next byte from the bytecode stream. */ + +#define FETCH *pc++ + +/* Fetch two bytes from the bytecode stream and make a 16-bit number + out of them. */ + +#define FETCH2 (op = FETCH, op + (FETCH << 8)) + +/* Push x onto the execution stack. This used to be #define PUSH(x) + (*++stackp = (x)) This oddity is necessary because Alliant can't be + bothered to compile the preincrement operator properly, as of 4/91. + -JimB */ + +#define PUSH(x) (top++, *top = (x)) + +#undef BEFORE_POTENTIAL_GC +#undef AFTER_POTENTIAL_GC +#define BEFORE_POTENTIAL_GC() ((void )0) +#define AFTER_POTENTIAL_GC() ((void )0) + +/* Check for jumping out of range. */ + +#if BYTE_CODE_SAFE + +#define CHECK_RANGE(ARG) \ + if (ARG >= bytestr_length) emacs_abort () + +#else /* not BYTE_CODE_SAFE */ + +#define CHECK_RANGE(ARG) + +#endif /* not BYTE_CODE_SAFE */ + +/* A version of the QUIT macro which makes sure that the stack top is + set before signaling `quit'. */ + +#define BYTE_CODE_QUIT \ + do { \ + if (!NILP (Vquit_flag) && NILP (Vinhibit_quit)) \ + { \ + Lisp_Object flag = Vquit_flag; \ + Vquit_flag = Qnil; \ + BEFORE_POTENTIAL_GC (); \ + if (EQ (Vthrow_on_input, flag)) \ + Fthrow (Vthrow_on_input, Qt); \ + Fsignal (Qquit, Qnil); \ + AFTER_POTENTIAL_GC (); \ + } \ + else if (pending_signals) \ + process_pending_signals (); \ + } while (0) + +/* Global jit context */ +jit_context_t jit_context = NULL; + +#define jit_type_Lisp_Object jit_type_nuint + +jit_type_t native_varref_sig; +static Lisp_Object +native_varref (Lisp_Object v1) +{ + Lisp_Object v2; + + if (SYMBOLP (v1)) + { + if (XSYMBOL (v1)->redirect != SYMBOL_PLAINVAL + || (v2 = SYMBOL_VAL (XSYMBOL (v1)), + EQ (v2, Qunbound))) + { + v2 = Fsymbol_value (v1); + } + } + else + { + v2 = Fsymbol_value (v1); + } + return v2; +} + +jit_type_t native_ifnil_sig; +static bool +native_ifnil (Lisp_Object v1) +{ + maybe_gc (); + if (NILP (v1)) + { + BYTE_CODE_QUIT; + return true; + } + else + return false; +} + +jit_type_t native_ifnonnil_sig; +static bool +native_ifnonnil (Lisp_Object v1) +{ + maybe_gc (); + if (!NILP (v1)) + { + BYTE_CODE_QUIT; + return true; + } + else + return false; + +} + +jit_type_t native_car_sig; +static Lisp_Object +native_car (Lisp_Object v1) +{ + if (CONSP (v1)) + return XCAR (v1); + else if (NILP (v1)) + return Qnil; + else + { + wrong_type_argument (Qlistp, v1); + } +} + +jit_type_t native_eq_sig; +static Lisp_Object +native_eq (Lisp_Object v1, Lisp_Object v2) +{ + return EQ (v1, v2) ? Qt : Qnil; +} + +jit_type_t native_memq_sig; +static Lisp_Object +native_memq (Lisp_Object v1, Lisp_Object v2) +{ + v1 = Fmemq (v1, v2); + return v1; +} + +jit_type_t native_cdr_sig; +static Lisp_Object +native_cdr (Lisp_Object v1) +{ + if (CONSP (v1)) + return XCDR (v1); + else if (NILP (v1)) + return Qnil; + else + { + wrong_type_argument (Qlistp, v1); + } +} + +jit_type_t native_varset_sig; +static void +native_varset (Lisp_Object sym, Lisp_Object val) +{ + /* Inline the most common case. */ + if (SYMBOLP (sym) + && !EQ (val, Qunbound) + && !XSYMBOL (sym)->redirect + && !SYMBOL_CONSTANT_P (sym)) + SET_SYMBOL_VAL (XSYMBOL (sym), val); + else + { + set_internal (sym, val, Qnil, 0); + } +} + +jit_type_t specbind_sig; +jit_type_t Ffuncall_sig; + +jit_type_t native_unbind_to_sig; +static Lisp_Object +native_unbind_to (ptrdiff_t x, Lisp_Object q) +{ + return unbind_to (SPECPDL_INDEX () - x, q); +} + +jit_type_t unbind_to_sig; + +jit_type_t byte_code_quit_sig; +static void +byte_code_quit (void) +{ + maybe_gc (); + BYTE_CODE_QUIT; +} + +jit_type_t native_save_excursion_sig; +static void +native_save_excursion (void) +{ + record_unwind_protect (save_excursion_restore, + save_excursion_save ()); +} + +jit_type_t native_save_restriction_sig; +static void +native_save_restriction (void) +{ + record_unwind_protect (save_restriction_restore, + save_restriction_save ()); +} + + +jit_type_t native_save_window_excursion_sig; +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; +} + +jit_type_t native_catch_sig; +static Lisp_Object +native_catch (Lisp_Object v2, Lisp_Object v1) +{ + return internal_catch (v2, eval_sub, v1); +} + +jit_type_t native_pophandler_sig; +static void +native_pophandler (void) +{ + handlerlist = handlerlist->next; +} + +jit_type_t native_pushhandler1_sig; +static void * +native_pushhandler1 (Lisp_Object **stack, Lisp_Object tag, + int type) +{ + struct handler *c = push_handler (tag, type); + c->stack = *stack; + return c->jmp; +} + +jit_type_t native_pushhandler2_sig; +static void +native_pushhandler2 (Lisp_Object **stack) +{ + struct handler *c = handlerlist; + native_pophandler (); + *stack = c->stack; + (*stack)++; + **stack = c->val; +} + +jit_type_t native_unwind_protect_sig; +static void +native_unwind_protect (Lisp_Object handler) +{ + record_unwind_protect (FUNCTIONP (handler) ? bcall0 : prog_ignore, handler); +} + +jit_type_t native_temp_output_buffer_setup_sig; +static Lisp_Object +native_temp_output_buffer_setup (Lisp_Object x) +{ + CHECK_STRING (x); + temp_output_buffer_setup (SSDATA (x)); + return Vstandard_output; +} + +jit_type_t native_nth_sig; +static Lisp_Object +native_nth (Lisp_Object v1, Lisp_Object v2) +{ + EMACS_INT n; + CHECK_NUMBER (v1); + n = XINT (v1); + immediate_quit = 1; + while (--n >= 0 && CONSP (v2)) + v2 = XCDR (v2); + immediate_quit = 0; + return CAR (v2); +} + +jit_type_t native_symbolp_sig; +jit_type_t native_consp_sig; +jit_type_t native_stringp_sig; +jit_type_t native_listp_sig; +jit_type_t native_not_sig; +static Lisp_Object +native_symbolp (Lisp_Object v1) +{ + return SYMBOLP (v1) ? Qt : Qnil; +} +static Lisp_Object +native_consp (Lisp_Object v1) +{ + return CONSP (v1) ? Qt : Qnil; +} +static Lisp_Object +native_stringp (Lisp_Object v1) +{ + return STRINGP (v1) ? Qt : Qnil; +} +static Lisp_Object +native_listp (Lisp_Object v1) +{ + return CONSP (v1) || NILP (v1) ? Qt : Qnil; +} +static Lisp_Object +native_not (Lisp_Object v1) +{ + return NILP (v1) ? Qt : Qnil; +} + +jit_type_t native_add1_sig; +static Lisp_Object +native_add1 (Lisp_Object v1, bool add) +{ + if (INTEGERP (v1)) + { + XSETINT (v1, XINT (v1) + (add ? 1 : -1)); + return v1; + } + else if (add) + return Fadd1 (v1); + else + return Fsub1 (v1); +} + +jit_type_t native_eqlsign_sig; +static Lisp_Object +native_eqlsign (Lisp_Object v1, Lisp_Object v2) +{ + CHECK_NUMBER_OR_FLOAT_COERCE_MARKER (v1); + CHECK_NUMBER_OR_FLOAT_COERCE_MARKER (v2); + if (FLOATP (v1) || FLOATP (v2)) + { + double f1, f2; + + f1 = (FLOATP (v1) ? XFLOAT_DATA (v1) : XINT (v1)); + f2 = (FLOATP (v2) ? XFLOAT_DATA (v2) : XINT (v2)); + return (f1 == f2 ? Qt : Qnil); + } + else + return (XINT (v1) == XINT (v2) ? Qt : Qnil); +} + +jit_type_t arithcompare_sig; +jit_type_t native_negate_sig; +static Lisp_Object +native_negate (Lisp_Object v) +{ + if (INTEGERP (v)) + { + XSETINT (v, - XINT (v)); + return v; + } + else + return Fminus (1, &v); +} + +jit_type_t native_point_sig; +static Lisp_Object +native_point (void) +{ + Lisp_Object v1; + XSETFASTINT (v1, PT); + return v1; +} + +jit_type_t native_point_max_sig; +static Lisp_Object +native_point_max (void) +{ + Lisp_Object v1; + XSETFASTINT (v1, ZV); + return v1; +} + +jit_type_t native_point_min_sig; +static Lisp_Object +native_point_min (void) +{ + Lisp_Object v1; + XSETFASTINT (v1, BEGV); + return v1; +} + +jit_type_t native_current_column_sig; +static Lisp_Object +native_current_column (void) +{ + Lisp_Object v1; + XSETFASTINT (v1, current_column ()); + return v1; +} + +jit_type_t native_interactive_p_sig; +static Lisp_Object +native_interactive_p (void) +{ + return call0 (intern ("interactive-p")); +} + +jit_type_t native_char_syntax_sig; +static Lisp_Object +native_char_syntax (Lisp_Object v) +{ + int c; + + CHECK_CHARACTER (v); + c = XFASTINT (v); + if (NILP (BVAR (current_buffer, enable_multibyte_characters))) + MAKE_CHAR_MULTIBYTE (c); + XSETFASTINT (v, syntax_code_spec[SYNTAX (c)]); + return v; +} + +jit_type_t native_elt_sig; +static Lisp_Object +native_elt (Lisp_Object v1, Lisp_Object v2) +{ + if (CONSP (v2)) + { + /* Exchange args and then do nth. */ + EMACS_INT n; + CHECK_NUMBER (v2); + n = XINT (v2); + immediate_quit = 1; + while (--n >= 0 && CONSP (v1)) + v1 = XCDR (v1); + immediate_quit = 0; + return CAR (v1); + } + else + return Felt (v1, v2); +} + +jit_type_t native_car_safe_sig; +static Lisp_Object +native_car_safe (Lisp_Object v) +{ + return CAR_SAFE (v); +} +jit_type_t native_cdr_safe_sig; +static Lisp_Object +native_cdr_safe (Lisp_Object v) +{ + return CDR_SAFE (v); +} + +jit_type_t native_number_p_sig; +static Lisp_Object +native_number_p (Lisp_Object v) +{ + return NUMBERP (v) ? Qt : Qnil; +} +jit_type_t native_integer_p_sig; +static Lisp_Object +native_integer_p (Lisp_Object v) +{ + return INTEGERP (v) ? Qt : Qnil; +} + +jit_type_t setjmp_sig; + +struct emacs_jit_context +{ + jit_context_t libjit_ctxt; + jit_function_t func; + jit_type_t stack_many; + jit_type_t stack_n[4]; + jit_value_t stack; +}; + +static bool jit_initialized = false; + +static void +emacs_jit_start (struct emacs_jit_context *ctxt) +{ +#define JIT_SIG_(f, ret, params) \ + f = jit_type_create_signature (jit_abi_cdecl, ret, params, \ + sizeof (params) / sizeof (params[0]), 1); +#define JIT_SIG(f, ret, ...) \ + do { \ + jit_type_t params[] = \ + { \ + __VA_ARGS__ \ + }; \ + JIT_SIG_ (f, ret, params); \ + } while (0) + + ctxt->libjit_ctxt = jit_context_create (); + jit_type_t jit_type_Lisp_Object_ptr = + jit_type_create_pointer (jit_type_Lisp_Object, 1); + jit_type_t func_sig; + JIT_SIG (func_sig, jit_type_Lisp_Object, jit_type_Lisp_Object_ptr); + ctxt->func = jit_function_create (ctxt->libjit_ctxt, func_sig); + jit_function_set_optimization_level (ctxt->func, + jit_function_get_max_optimization_level ()); + ctxt->stack = jit_value_get_param (ctxt->func, 0); + + JIT_SIG (ctxt->stack_many, jit_type_Lisp_Object, + jit_type_nuint, jit_type_Lisp_Object_ptr); + + jit_type_t params[] = { + jit_type_Lisp_Object, + jit_type_Lisp_Object, + jit_type_Lisp_Object, + jit_type_Lisp_Object + }; + int i; + for (i = 0; i < sizeof (params) / sizeof (params[0]); i++) + { + ctxt->stack_n[i] = + jit_type_create_signature (jit_abi_cdecl, jit_type_Lisp_Object, + params, i, 1); + } +#undef JIT_SIG +#undef JIT_SIG_ +} + +static void +emacs_jit_init (void) +{ +#define JIT_SIG_(f, ret, params) \ + do { \ + f##_sig = \ + jit_type_create_signature ( \ + jit_abi_cdecl, \ + ret, \ + params, \ + sizeof (params) / sizeof (params[0]), \ + 1); \ + } while (0) +#define JIT_SIG(f, ret, ...) \ + do { \ + jit_type_t params[] = \ + { \ + __VA_ARGS__ \ + }; \ + JIT_SIG_ (f, ret, params); \ + } while (0) + + do { + jit_type_t params[] = + { + jit_type_void_ptr, +#if !defined (HAVE__SETJMP) && defined (HAVE_SIGSETJMP) + jit_type_sys_int +#endif + }; + setjmp_sig = jit_type_create_signature (jit_abi_cdecl, + jit_type_sys_int, params, +#if !defined (HAVE__SETJMP) && defined (HAVE_SIGSETJMP) + 2, +#else + 1, +#endif + 1); + } while (0); + JIT_SIG (native_ifnil, jit_type_sys_bool, jit_type_Lisp_Object); + JIT_SIG (native_ifnonnil, jit_type_sys_bool, jit_type_Lisp_Object); + JIT_SIG (native_varset, jit_type_void, jit_type_Lisp_Object, jit_type_Lisp_Object); + JIT_SIG (specbind, jit_type_void, jit_type_Lisp_Object, jit_type_Lisp_Object); + JIT_SIG (Ffuncall, jit_type_Lisp_Object, jit_type_nuint, jit_type_void_ptr); + JIT_SIG (byte_code_quit, jit_type_void); + JIT_SIG (native_save_excursion, jit_type_void); + JIT_SIG (native_save_restriction, jit_type_void); + JIT_SIG (native_pophandler, jit_type_void); + JIT_SIG (native_pushhandler1, jit_type_void_ptr, jit_type_create_pointer (jit_type_void_ptr, 1), jit_type_Lisp_Object, jit_type_nint); + JIT_SIG (native_pushhandler2, jit_type_void, jit_type_create_pointer (jit_type_void_ptr, 1)); + JIT_SIG (native_unwind_protect, jit_type_void, jit_type_Lisp_Object); + JIT_SIG (native_add1, jit_type_Lisp_Object, jit_type_Lisp_Object, jit_type_sys_bool); + JIT_SIG (arithcompare, jit_type_Lisp_Object, jit_type_Lisp_Object, jit_type_Lisp_Object, jit_type_nuint); + + jit_initialized = true; +} + +Lisp_Object +jit_exec (Lisp_Object byte_code, Lisp_Object args_template, ptrdiff_t nargs, Lisp_Object *args) +{ + Lisp_Object *top; + Lisp_Object maxdepth = XVECTOR (byte_code)->contents[COMPILED_STACK_DEPTH]; + + CHECK_NATNUM (maxdepth); + if (MAX_ALLOCA / word_size <= XFASTINT (maxdepth)) + memory_full (SIZE_MAX); + top = alloca ((XFASTINT (maxdepth) + 1) * sizeof *top); + + if (INTEGERP (args_template)) + { + ptrdiff_t at = XINT (args_template); + bool rest = (at & 128) != 0; + int mandatory = at & 127; + ptrdiff_t nonrest = at >> 8; + eassert (mandatory <= nonrest); + if (nargs <= nonrest) + { + ptrdiff_t i; + for (i = 0 ; i < nargs; i++, args++) + PUSH (*args); + if (nargs < mandatory) + /* Too few arguments. */ + Fsignal (Qwrong_number_of_arguments, + list2 (Fcons (make_number (mandatory), + rest ? Qand_rest : make_number (nonrest)), + make_number (nargs))); + else + { + for (; i < nonrest; i++) + PUSH (Qnil); + if (rest) + PUSH (Qnil); + } + } + else if (rest) + { + ptrdiff_t i; + for (i = 0 ; i < nonrest; i++, args++) + PUSH (*args); + PUSH (Flist (nargs - nonrest, args)); + } + else + /* Too many arguments. */ + Fsignal (Qwrong_number_of_arguments, + list2 (Fcons (make_number (mandatory), make_number (nonrest)), + make_number (nargs))); + } + else if (! NILP (args_template)) + /* We should push some arguments on the stack. */ + { + error ("Unknown args template!"); + } + + { + Lisp_Object (*func)(Lisp_Object *) = + (void *)AREF (byte_code, COMPILED_JIT_CLOSURE); + return func (top); + } +} + +static inline +void jit_inc (struct emacs_jit_context *ctxt, jit_value_t v, long n) +{ + jit_value_t i = jit_insn_add_relative (ctxt->func, v, (jit_nint )n); + if (!i || !jit_insn_store (ctxt->func, v, i)) + emacs_abort (); +} + +static inline +void jit_push (struct emacs_jit_context *ctxt, jit_value_t v) +{ + jit_inc (ctxt, ctxt->stack, sizeof (Lisp_Object)); + if (!jit_insn_store_relative (ctxt->func, ctxt->stack, (jit_nint )0, v)) + emacs_abort (); +} + +static inline +jit_value_t jit_top (struct emacs_jit_context *ctxt) +{ + jit_value_t v = jit_insn_load_relative (ctxt->func, ctxt->stack, + (jit_nint )0, jit_type_Lisp_Object); + if (!v) + emacs_abort (); + return v; +} + +static inline +jit_value_t jit_pop (struct emacs_jit_context *ctxt) +{ + jit_value_t v = jit_top (ctxt); + jit_inc (ctxt, ctxt->stack, -sizeof (Lisp_Object)); + return v; +} + +static inline +jit_value_t jit_call (struct emacs_jit_context *ctxt, void *f, + const char *name, jit_type_t sig, jit_value_t *args, + size_t nargs) +{ + return jit_insn_call_native (ctxt->func, name, f, sig, args, nargs, + JIT_CALL_NOTHROW); +} + +static inline +jit_value_t jit_call_vaarg (struct emacs_jit_context *ctxt, void *f, + const char *name, jit_type_t sig, ...) +{ + jit_value_t *args; + int i, count; + va_list ap; + + /* Determine the number of passed arguments. */ + va_start (ap, sig); + for (count = 0; va_arg (ap, jit_value_t) != NULL; count++); + va_end (ap); + + /* Collect args and setup the call */ + args = (count > 0) ? alloca (count * sizeof (*args)) : NULL; + va_start (ap, sig); + for (i = 0; i < count; i++) + args[i] = va_arg (ap, jit_value_t); + va_end (ap); + + return jit_call (ctxt, f, name, sig, args, count); +} + +static inline +void jit_call_with_stack_n (struct emacs_jit_context *ctxt, void *f, + const char *name, int n) +{ + jit_value_t *args = (n > 0) ? alloca (n * sizeof (*args)) : NULL; + int i; + + for (i = 1; i <= n; i++) + args[n-i] = jit_pop (ctxt); + jit_push (ctxt, jit_call (ctxt, f, name, ctxt->stack_n[n], args, n)); +} + +#define JIT_CONSTANT(f, t, v) \ + jit_value_create_nint_constant (f, t, v) + +static inline +void jit_call_with_stack_many (struct emacs_jit_context *ctxt, void *f, + const char *name, int n) +{ + jit_inc (ctxt, ctxt->stack, -(n - 1) * sizeof (Lisp_Object)); + jit_insn_store_relative (ctxt->func, ctxt->stack, (jit_nint )0, + jit_call_vaarg (ctxt, f, name, ctxt->stack_many, + JIT_CONSTANT (ctxt->func, + jit_type_nuint, n), + ctxt->stack, NULL)); +} + +#undef JIT_CONSTANT + +struct { + void * const ptr; + const char * const name; + int n; +} static const functions[256] = { +#undef DEFINE_FIXED +#define DEFINE_FIXED(bname, value, fname, num) \ + [value] = { .ptr = (void *)(&fname), .name = #fname, .n = num }, +#define DEFINE(bname, value) + + BYTE_CODES + +#undef DEFINE_FIXED +#undef DEFINE +#define DEFINE_FIXED(bname, value, fname, num) DEFINE (bname, value) +}; + +void +jit_byte_code__ (Lisp_Object byte_code) +{ + ptrdiff_t count = SPECPDL_INDEX (); + int op; + Lisp_Object *vectorp; +#if BYTE_CODE_SAFE + ptrdiff_t const_length; + Lisp_Object *stacke; +#endif + ptrdiff_t bytestr_length; + Lisp_Object bytestr; + Lisp_Object vector; + Lisp_Object maxdepth; + enum handlertype type; + + unsigned char *byte_string_start, *pc; + + /* jit-specific variables */ + struct emacs_jit_context ctxt; + jit_label_t *labels; + + /* ensure this is a byte-coded function _before_ doing anything else */ + CHECK_COMPILED (byte_code); + + /* check if function has already been compiled */ + if (XVECTOR (byte_code)->contents[COMPILED_JIT_CTXT] != (Lisp_Object )NULL) + return; + if (!jit_initialized) + emacs_jit_init (); + + emacs_jit_start (&ctxt); + + bytestr = XVECTOR (byte_code)->contents[COMPILED_BYTECODE]; + vector = XVECTOR (byte_code)->contents[COMPILED_CONSTANTS]; + maxdepth = XVECTOR (byte_code)->contents[COMPILED_STACK_DEPTH]; + CHECK_STRING (bytestr); + CHECK_VECTOR (vector); + CHECK_NATNUM (maxdepth); + +#if BYTE_CODE_SAFE + const_length = ASIZE (vector); +#endif + + 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); + + bytestr_length = SBYTES (bytestr); + vectorp = XVECTOR (vector)->contents; + + pc = byte_string_start = SDATA (bytestr); + if (MAX_ALLOCA / word_size <= XFASTINT (maxdepth)) + memory_full (SIZE_MAX); + + /* prepare for jit */ + jit_context_build_start (ctxt.libjit_ctxt); + labels = alloca (sizeof (*labels) * SBYTES (bytestr)); + { + /* give each instruction a label. the labels won't be initialized + until we attach code to them, but they work as a placeholder. */ + int i; + for (i = 0; i < SBYTES (bytestr); i++) + labels[i] = jit_label_undefined; + } + + while (pc < byte_string_start + bytestr_length) + { +#ifndef BYTE_CODE_THREADED + op = FETCH; +#endif + + /* The interpreter can be compiled one of two ways: as an + ordinary switch-based interpreter, or as a threaded + interpreter. The threaded interpreter relies on GCC's + computed goto extension, so it is not available everywhere. + Threading provides a performance boost. These macros are how + we allow the code to be compiled both ways. */ +#ifdef BYTE_CODE_THREADED + /* The CASE macro introduces an instruction's body. It is + either a label or a case label. */ +#define CASE(OP) insn_ ## OP + /* NEXT is invoked at the end of an instruction to go to the + next instruction. It is either a computed goto, or a + plain break. */ +#define NEXT \ + do { \ + if (pc >= byte_string_start + bytestr_length) \ + goto exit; \ + else \ + { \ + /* Create a new block and attach a label to it. */ \ + /* Since fetching the instruction incrememnts pc, do */ \ + /* this before we fetch the instruction, so pc is right. */ \ + jit_insn_label (ctxt.func, &labels[JIT_PC]); \ + op = FETCH; \ + goto *(targets[op]); \ + } \ + } while (0) + /* FIRST is like NEXT, but is only used at the start of the + interpreter body. In the switch-based interpreter it is the + switch, so the threaded definition must include a semicolon. */ +#define FIRST NEXT; + /* Most cases are labeled with the CASE macro, above. + CASE_DEFAULT is one exception; it is used if the interpreter + being built requires a default case. The threaded + interpreter does not, because the dispatch table is + completely filled. */ +#define CASE_DEFAULT + /* This introduces an instruction that is known to call abort. */ +#define CASE_ABORT CASE (Bstack_ref): CASE (default) +#else + /* See above for the meaning of the various defines. */ +#define CASE(OP) case OP +#define NEXT break +#define FIRST switch (op) +#define CASE_DEFAULT case 255: default: +#define CASE_ABORT case 0 +#endif + +#ifdef BYTE_CODE_THREADED + + /* A convenience define that saves us a lot of typing and makes + the table clearer. */ +#define LABEL(OP) [OP] = &&insn_ ## OP + +#if 4 < __GNUC__ + (6 <= __GNUC_MINOR__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Woverride-init" +#elif defined __clang__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Winitializer-overrides" +#endif + + /* This is the dispatch table for the threaded interpreter. */ + static const void *const targets[256] = + { + [0 ... (Bconstant - 1)] = &&insn_default, + [Bconstant ... 255] = &&insn_Bconstant, + +#define DEFINE(name, value) LABEL (name) , + BYTE_CODES +#undef DEFINE + }; + +#if 4 < __GNUC__ + (6 <= __GNUC_MINOR__) || defined __clang__ +# pragma GCC diagnostic pop +#endif + +#endif + +#define JIT_PC (pc - byte_string_start) +#define JIT_NEED_STACK jit_value_ref (ctxt.func, ctxt.stack) +#define JIT_NEXT \ + do { \ + if (!jit_insn_branch ( \ + ctxt.func, \ + &labels[JIT_PC])) \ + emacs_abort (); \ + } while (0) + +#define JIT_INC(v, n) \ + jit_inc (&ctxt, v, n) + +#define JIT_PUSH(v) \ + jit_push (&ctxt, v) + +#define JIT_TOP() \ + jit_top (&ctxt) + +#define JIT_POP() \ + jit_pop (&ctxt) + +#define JIT_CALL(f, args, n) \ + jit_call (&ctxt, (void *)&f, #f, f##_sig, args, n) + +#define JIT_CALL_ARGS(f, ...) \ + jit_call_vaarg (&ctxt, (void *)&f, #f, f##_sig, __VA_ARGS__, NULL) + +#define JIT_CONSTANT(t, v) \ + jit_value_create_nint_constant ( \ + ctxt.func, \ + t, \ + v) + +#define JIT_CALL_WITH_STACK_N(f, n) \ + jit_call_with_stack_n (&ctxt, (void *)&f, #f, n) + +#define JIT_CALL_WITH_STACK_MANY(f, n) \ + jit_call_with_stack_many (&ctxt, (void *)&f, #f, n) + +#ifndef BYTE_CODE_THREADED + /* create a new block and attach a label to it */ + jit_insn_label (ctxt.func, &labels[JIT_PC]); +#endif + + FIRST + { + CASE (Bcall): + CASE (Bcall1): + CASE (Bcall2): + CASE (Bcall3): + CASE (Bcall4): + CASE (Bcall5): + CASE (Bcall6): + CASE (Bcall7): + CASE (Blist3): + CASE (Blist4): + CASE (BlistN): + CASE (Bconcat2): + CASE (Bconcat3): + CASE (Bconcat4): + CASE (BconcatN): + CASE (Bdiff): + CASE (Bplus): + CASE (Bmax): + CASE (Bmin): + CASE (Bmult): + CASE (Bquo): + CASE (Binsert): + CASE (BinsertN): + CASE (Bnconc): + { + int args = functions[op].n; + if (args < 0) + { + if (args == -1) + args = FETCH; + else if (args == -2) + args = FETCH2; + if (op == Bcall6 || op == Bcall7) + args += 1; + } + JIT_NEED_STACK; + jit_call_with_stack_many (&ctxt, functions[op].ptr, + functions[op].name, args); + JIT_NEXT; + NEXT; + } + CASE (Blist1): + CASE (Blist2): + CASE (Bcar): + CASE (Beq): + CASE (Bmemq): + CASE (Bcdr): + CASE (Bsave_window_excursion): /* Obsolete since 24.1. */ + CASE (Bcatch): /* Obsolete since 24.4. */ + CASE (Bcondition_case): /* Obsolete since 24.4. */ + CASE (Btemp_output_buffer_setup): /* Obsolete since 24.1. */ + CASE (Bnth): + CASE (Bsymbolp): + CASE (Bconsp): + CASE (Bstringp): + CASE (Blistp): + CASE (Bnot): + CASE (Bcons): + CASE (Blength): + CASE (Baref): + CASE (Baset): + CASE (Bsymbol_value): + CASE (Bsymbol_function): + CASE (Bset): + CASE (Bfset): + CASE (Bget): + CASE (Bsubstring): + CASE (Beqlsign): + CASE (Bnegate): + CASE (Brem): + CASE (Bpoint): + CASE (Bgoto_char): + CASE (Bpoint_max): + CASE (Bpoint_min): + CASE (Bchar_after): + CASE (Bfollowing_char): + CASE (Bpreceding_char): + CASE (Bcurrent_column): + CASE (Beolp): + CASE (Beobp): + CASE (Bbolp): + CASE (Bbobp): + CASE (Bcurrent_buffer): + CASE (Bset_buffer): + CASE (Binteractive_p): /* Obsolete since 24.1. */ + CASE (Bforward_char): + CASE (Bforward_word): + CASE (Bskip_chars_forward): + CASE (Bskip_chars_backward): + CASE (Bforward_line): + CASE (Bchar_syntax): + CASE (Bbuffer_substring): + CASE (Bdelete_region): + CASE (Bnarrow_to_region): + CASE (Bwiden): + CASE (Bend_of_line): + CASE (Bset_marker): + CASE (Bmatch_beginning): + CASE (Bmatch_end): + CASE (Bupcase): + CASE (Bdowncase): + CASE (Bstringeqlsign): + CASE (Bstringlss): + CASE (Bequal): + CASE (Bnthcdr): + CASE (Belt): + CASE (Bmember): + CASE (Bassq): + CASE (Bnreverse): + CASE (Bsetcar): + CASE (Bsetcdr): + CASE (Bcar_safe): + CASE (Bcdr_safe): + CASE (Bnumberp): + CASE (Bintegerp): + { + JIT_NEED_STACK; + jit_call_with_stack_n (&ctxt, functions[op].ptr, + functions[op].name, functions[op].n); + JIT_NEXT; + NEXT; + } + + CASE (Bvarref7): + op = FETCH2; + goto varref; + + CASE (Bvarref): + CASE (Bvarref1): + CASE (Bvarref2): + CASE (Bvarref3): + CASE (Bvarref4): + CASE (Bvarref5): + op = op - Bvarref; + goto varref; + + /* This seems to be the most frequently executed byte-code + among the Bvarref's, so avoid a goto here. */ + CASE (Bvarref6): + op = FETCH; + varref: + { + JIT_NEED_STACK; + JIT_PUSH (JIT_CONSTANT (jit_type_nuint, vectorp[op])); + JIT_CALL_WITH_STACK_N (native_varref, 1); + JIT_NEXT; + NEXT; + } + + 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_NEED_STACK; + JIT_CALL_ARGS (native_varset, JIT_CONSTANT (jit_type_Lisp_Object, + vectorp[op]), + JIT_POP ()); + + JIT_NEXT; + NEXT; + } + + CASE (Bdup): + { + JIT_NEED_STACK; + JIT_PUSH (JIT_TOP ()); + JIT_NEXT; + NEXT; + } + + /* ------------------ */ + + 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_NEED_STACK; + JIT_CALL_ARGS (specbind, + JIT_CONSTANT (jit_type_Lisp_Object, + vectorp[op]), + JIT_POP ()); + JIT_NEXT; + NEXT; + } + + + 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 args[] = + { + JIT_CONSTANT (jit_type_nuint, op), + JIT_CONSTANT (jit_type_Lisp_Object, Qnil) + }; + jit_call (&ctxt, (void *)&native_unbind_to, "native_unbind_to", + ctxt.stack_many, args, 2); + JIT_NEXT; + NEXT; + } + + CASE (Bunbind_all): /* Obsolete. Never used. */ + /* To unbind back to the beginning of this frame. Not used yet, + but will be needed for tail-recursion elimination. */ + { + jit_value_t args[] = + { + JIT_CONSTANT (jit_type_nuint, count), + JIT_CONSTANT (jit_type_Lisp_Object, Qnil) + }; + jit_call (&ctxt, (void *)&unbind_to, "unbind_to", + ctxt.stack_many, args, 2); + JIT_NEXT; + NEXT; + } + + CASE (Bgoto): + { + op = FETCH2; + CHECK_RANGE (op); + JIT_CALL (byte_code_quit, NULL, 0); + jit_insn_branch ( + ctxt.func, + &labels[op]); + NEXT; + } + + CASE (Bgotoifnil): + CASE (BRgotoifnil): + CASE (Bgotoifnonnil): + CASE (BRgotoifnonnil): + CASE (Bgotoifnilelsepop): + CASE (BRgotoifnilelsepop): + CASE (Bgotoifnonnilelsepop): + CASE (BRgotoifnonnilelsepop): + { + jit_value_t v2, v3; + int insn = op; + if (insn >= Bgotoifnil && insn <= Bgotoifnonnilelsepop) + op = FETCH2; + else + { + op = FETCH - 128; + op += (pc - byte_string_start); + } + CHECK_RANGE (op); + JIT_NEED_STACK; + v2 = JIT_POP (); + if (insn == Bgotoifnil || insn == BRgotoifnil + || insn == Bgotoifnilelsepop || insn == BRgotoifnilelsepop) + v3 = JIT_CALL_ARGS (native_ifnil, v2); + else + v3 = JIT_CALL_ARGS (native_ifnonnil, v2); + if (insn == Bgotoifnilelsepop || insn == Bgotoifnonnilelsepop + || insn == BRgotoifnilelsepop || insn == BRgotoifnonnilelsepop) + JIT_PUSH (v2); + jit_insn_branch_if ( + ctxt.func, + v3, + &labels[op]); + if (insn == Bgotoifnilelsepop || insn == Bgotoifnonnilelsepop + || insn == BRgotoifnilelsepop || insn == BRgotoifnonnilelsepop) + JIT_INC (ctxt.stack, -sizeof (Lisp_Object)); + JIT_NEXT; + NEXT; + } + + CASE (BRgoto): + { + op = FETCH - 128; + const int dest = (pc - byte_string_start) + op; + JIT_CALL (byte_code_quit, NULL, 0); + jit_insn_branch ( + ctxt.func, + &labels[dest]); + NEXT; + } + + CASE (Breturn): + { + JIT_NEED_STACK; + jit_insn_return (ctxt.func, JIT_POP ()); + NEXT; + } + + CASE (Bdiscard): + { + JIT_NEED_STACK; + JIT_INC (ctxt.stack, -sizeof (Lisp_Object)); + JIT_NEXT; + NEXT; + } + + CASE (Bconstant2): + { + JIT_NEED_STACK; + JIT_PUSH (JIT_CONSTANT (jit_type_Lisp_Object, vectorp[FETCH2])); + JIT_NEXT; + NEXT; + } + + CASE (Bsave_excursion): + { + JIT_CALL (native_save_excursion, NULL, 0); + JIT_NEXT; + NEXT; + } + + CASE (Bsave_current_buffer): /* Obsolete since ??. */ + CASE (Bsave_current_buffer_1): + { + jit_type_t record_unwind_current_buffer_sig; + JIT_SIG (record_unwind_current_buffer, jit_type_void); + JIT_CALL (record_unwind_current_buffer, NULL, 0); + JIT_NEXT; + NEXT; + } + + CASE (Bsave_restriction): + JIT_CALL (native_save_restriction, NULL, 0); + JIT_NEXT; + NEXT; + + CASE (Bpushcatch): /* New in 24.4. */ + type = CATCHER; + goto pushhandler; + CASE (Bpushconditioncase): /* New in 24.4. */ + type = CONDITION_CASE; + pushhandler: + { + jit_value_t stackp, jmp, result, result2; + int dest = FETCH2; + JIT_NEED_STACK; + stackp = jit_insn_address_of (ctxt.func, ctxt.stack); + jmp = JIT_CALL_ARGS (native_pushhandler1, stackp, JIT_POP (), + JIT_CONSTANT (jit_type_nint, type)); + do { + void *f; + int n; + jit_value_t args[2] = { jmp }; +#ifdef HAVE__SETJMP + f = (void *)&_setjmp; + n = 1; +#elif defined HAVE_SIGSETJMP + f = (void *)&sigsetjmp; + n = 2; + args[1] = JIT_CONSTANT (jit_type_sys_int, 0); +#else + f = (void *)&setjmp; + n = 1; +#endif + result = jit_insn_call_native (ctxt.func, "setjmp", f, + setjmp_sig, args, n, + JIT_CALL_NOTHROW); + } while (0); + jit_insn_branch_if_not (ctxt.func, result, &labels[JIT_PC]); + JIT_CALL (native_pushhandler2, &stackp, 1); + jit_insn_branch (ctxt.func, &labels[dest]); + NEXT; + } + + CASE (Bpophandler): /* New in 24.4. */ + { + JIT_CALL (native_pophandler, NULL, 0); + JIT_NEXT; + NEXT; + } + + CASE (Bunwind_protect): /* FIXME: avoid closure for lexbind. */ + { + jit_value_t handler; + JIT_NEED_STACK; + handler = JIT_POP (); + JIT_CALL (native_unwind_protect, &handler, 1); + JIT_NEXT; + NEXT; + } + + CASE (Btemp_output_buffer_show): /* Obsolete since 24.1. */ + { + jit_type_t temp_output_buffer_show_sig; + jit_value_t v1, v2, c, q; + JIT_NEED_STACK; + JIT_SIG (temp_output_buffer_show, + jit_type_void, + jit_type_Lisp_Object); + v1 = JIT_POP (); + v2 = JIT_POP (); + JIT_CALL (temp_output_buffer_show, &v2, 1); + JIT_PUSH (v1); + c = JIT_CONSTANT (jit_type_nuint, 1); + q = JIT_CONSTANT (jit_type_Lisp_Object, Qnil); + JIT_CALL_ARGS (native_unbind_to, c, q); + JIT_NEXT; + NEXT; + } + + CASE (Bsub1): + { + JIT_NEED_STACK; + JIT_PUSH (JIT_CALL_ARGS (native_add1, JIT_POP (), + JIT_CONSTANT (jit_type_sys_bool, 0))); + JIT_NEXT; + NEXT; + } + + CASE (Badd1): + { + JIT_NEED_STACK; + JIT_PUSH (JIT_CALL_ARGS (native_add1, JIT_POP (), + JIT_CONSTANT (jit_type_sys_bool, 1))); + JIT_NEXT; + NEXT; + } + + CASE (Bgtr): + CASE (Blss): + CASE (Bleq): + CASE (Bgeq): + { + jit_value_t v1, v2, c; + enum Arith_Comparison v[] = + { + ARITH_GRTR, + ARITH_LESS, + ARITH_LESS_OR_EQUAL, + ARITH_GRTR_OR_EQUAL + }; + JIT_NEED_STACK; + c = JIT_CONSTANT (jit_type_nuint, v[op-Bgtr]); + v2 = JIT_POP (); + v1 = JIT_POP (); + JIT_PUSH (JIT_CALL_ARGS (arithcompare, v1, v2, c)); + JIT_NEXT; + NEXT; + } + + CASE (Bindent_to): + { + JIT_PUSH (JIT_CONSTANT (jit_type_Lisp_Object, Qnil)); + JIT_CALL_WITH_STACK_N (Findent_to, 2); + JIT_NEXT; + NEXT; + } + +#if BYTE_CODE_SAFE + /* These are intentionally written using 'case' syntax, + because they are incompatible with the threaded + interpreter. */ + + case Bset_mark: + error ("set-mark is an obsolete bytecode"); + break; + case Bscan_buffer: + error ("scan-buffer is an obsolete bytecode"); + break; +#endif + + CASE_ABORT: + /* Actually this is Bstack_ref with offset 0, but we use Bdup + for that instead. */ + /* CASE (Bstack_ref): */ + call3 (Qerror, + build_string ("Invalid byte opcode: op=%s, ptr=%d"), + make_number (op), + make_number (pc - 1 - byte_string_start)); + + /* Handy byte-codes for lexical binding. */ + CASE (Bstack_ref1): + CASE (Bstack_ref2): + CASE (Bstack_ref3): + CASE (Bstack_ref4): + CASE (Bstack_ref5): + CASE (Bstack_ref6): + CASE (Bstack_ref7): + { + jit_value_t v1; + int offs = op - Bstack_ref; + if (offs == 6) + offs = FETCH; + else if (offs == 7) + offs = FETCH2; + + JIT_NEED_STACK; + JIT_INC (ctxt.stack, -offs * sizeof (Lisp_Object)); + v1 = JIT_TOP (); + JIT_INC (ctxt.stack, offs * sizeof (Lisp_Object)); + JIT_PUSH (v1); + JIT_NEXT; + NEXT; + } + CASE (Bstack_set): + CASE (Bstack_set2): + /* stack-set-0 = discard; stack-set-1 = discard-1-preserve-tos. */ + { + jit_value_t v1; + int offs = (op == Bstack_set) ? FETCH : FETCH2; + JIT_NEED_STACK; + v1 = JIT_TOP (); + if (offs != 0) + JIT_INC (ctxt.stack, -(offs + 1) * sizeof (Lisp_Object)); + JIT_PUSH (v1); + JIT_INC (ctxt.stack, (offs - 1) * sizeof (Lisp_Object)); + JIT_NEXT; + NEXT; + } + CASE (BdiscardN): + { + op = FETCH; + JIT_NEED_STACK; + if (op & 0x80) + { + jit_value_t v1; + op &= 0x7F; + v1 = JIT_TOP (); + JIT_INC (ctxt.stack, -(op + 1) * sizeof (Lisp_Object)); + JIT_PUSH (v1); + } + else + JIT_INC (ctxt.stack, -op * sizeof (Lisp_Object)); + JIT_NEXT; + NEXT; + } + + CASE_DEFAULT + CASE (Bconstant): + { + jit_value_t c; +#if BYTE_CODE_SAFE + if (op < Bconstant) + { + emacs_abort (); + } + if ((op -= Bconstant) >= const_length) + { + emacs_abort (); + } +#endif + c = JIT_CONSTANT (jit_type_Lisp_Object, vectorp[op - Bconstant]); + JIT_PUSH (c); + JIT_NEXT; + NEXT; + } + } + } + + exit: + + { + int err = !jit_function_compile (ctxt.func); + jit_context_build_end (ctxt.libjit_ctxt); + if (err) + emacs_abort (); + ASET (byte_code, COMPILED_INTERPRETER, (Lisp_Object )jit_exec); + ASET (byte_code, COMPILED_JIT_CTXT, (Lisp_Object )ctxt.libjit_ctxt); + ASET (byte_code, COMPILED_JIT_CLOSURE, + (Lisp_Object )jit_function_to_closure (ctxt.func)); + } +} + +DEFUN ("jit-compile", Fjit_compile, Sjit_compile, 1, 1, 0, + doc: /* Function used internally in byte-compiled code. + The first argument, BYTECODE, is a compiled byte code object. */) + (Lisp_Object byte_code) +{ + jit_byte_code__ (byte_code); + return byte_code; +} + +void +syms_of_bytecode_jit (void) +{ + defsubr (&Sjit_compile); + DEFVAR_BOOL ("byte-code-jit-on", byte_code_jit_on, + doc: /* If non-nil, compile byte-code to machine code + before execution. */); + byte_code_jit_on = 0; +} +#endif /* HAVE_LIBJIT */ diff --git a/src/bytecode.c b/src/bytecode.c index 0f7420c19ee..a6019f7c1a5 100644 --- a/src/bytecode.c +++ b/src/bytecode.c @@ -19,6 +19,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include <config.h> +#include "bytecode.h" #include "lisp.h" #include "blockinput.h" #include "character.h" @@ -32,25 +33,6 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ # pragma GCC diagnostic ignored "-Wclobbered" #endif -/* Define BYTE_CODE_SAFE true to enable some minor sanity checking, - useful for debugging the byte compiler. It defaults to false. */ - -#ifndef BYTE_CODE_SAFE -# define BYTE_CODE_SAFE false -#endif - -/* Define BYTE_CODE_METER to generate a byte-op usage histogram. */ -/* #define BYTE_CODE_METER */ - -/* If BYTE_CODE_THREADED is defined, then the interpreter will be - indirect threaded, using GCC's computed goto extension. This code, - as currently implemented, is incompatible with BYTE_CODE_SAFE and - BYTE_CODE_METER. */ -#if (defined __GNUC__ && !defined __STRICT_ANSI__ && !defined __CHKP__ \ - && !BYTE_CODE_SAFE && !defined BYTE_CODE_METER) -#define BYTE_CODE_THREADED -#endif - #ifdef BYTE_CODE_METER @@ -74,212 +56,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ } #endif /* BYTE_CODE_METER */ - -/* Byte codes: */ - -#define BYTE_CODES \ -DEFINE (Bstack_ref, 0) /* Actually, Bstack_ref+0 is not implemented: use dup. */ \ -DEFINE (Bstack_ref1, 1) \ -DEFINE (Bstack_ref2, 2) \ -DEFINE (Bstack_ref3, 3) \ -DEFINE (Bstack_ref4, 4) \ -DEFINE (Bstack_ref5, 5) \ -DEFINE (Bstack_ref6, 6) \ -DEFINE (Bstack_ref7, 7) \ -DEFINE (Bvarref, 010) \ -DEFINE (Bvarref1, 011) \ -DEFINE (Bvarref2, 012) \ -DEFINE (Bvarref3, 013) \ -DEFINE (Bvarref4, 014) \ -DEFINE (Bvarref5, 015) \ -DEFINE (Bvarref6, 016) \ -DEFINE (Bvarref7, 017) \ -DEFINE (Bvarset, 020) \ -DEFINE (Bvarset1, 021) \ -DEFINE (Bvarset2, 022) \ -DEFINE (Bvarset3, 023) \ -DEFINE (Bvarset4, 024) \ -DEFINE (Bvarset5, 025) \ -DEFINE (Bvarset6, 026) \ -DEFINE (Bvarset7, 027) \ -DEFINE (Bvarbind, 030) \ -DEFINE (Bvarbind1, 031) \ -DEFINE (Bvarbind2, 032) \ -DEFINE (Bvarbind3, 033) \ -DEFINE (Bvarbind4, 034) \ -DEFINE (Bvarbind5, 035) \ -DEFINE (Bvarbind6, 036) \ -DEFINE (Bvarbind7, 037) \ -DEFINE (Bcall, 040) \ -DEFINE (Bcall1, 041) \ -DEFINE (Bcall2, 042) \ -DEFINE (Bcall3, 043) \ -DEFINE (Bcall4, 044) \ -DEFINE (Bcall5, 045) \ -DEFINE (Bcall6, 046) \ -DEFINE (Bcall7, 047) \ -DEFINE (Bunbind, 050) \ -DEFINE (Bunbind1, 051) \ -DEFINE (Bunbind2, 052) \ -DEFINE (Bunbind3, 053) \ -DEFINE (Bunbind4, 054) \ -DEFINE (Bunbind5, 055) \ -DEFINE (Bunbind6, 056) \ -DEFINE (Bunbind7, 057) \ - \ -DEFINE (Bpophandler, 060) \ -DEFINE (Bpushconditioncase, 061) \ -DEFINE (Bpushcatch, 062) \ - \ -DEFINE (Bnth, 070) \ -DEFINE (Bsymbolp, 071) \ -DEFINE (Bconsp, 072) \ -DEFINE (Bstringp, 073) \ -DEFINE (Blistp, 074) \ -DEFINE (Beq, 075) \ -DEFINE (Bmemq, 076) \ -DEFINE (Bnot, 077) \ -DEFINE (Bcar, 0100) \ -DEFINE (Bcdr, 0101) \ -DEFINE (Bcons, 0102) \ -DEFINE (Blist1, 0103) \ -DEFINE (Blist2, 0104) \ -DEFINE (Blist3, 0105) \ -DEFINE (Blist4, 0106) \ -DEFINE (Blength, 0107) \ -DEFINE (Baref, 0110) \ -DEFINE (Baset, 0111) \ -DEFINE (Bsymbol_value, 0112) \ -DEFINE (Bsymbol_function, 0113) \ -DEFINE (Bset, 0114) \ -DEFINE (Bfset, 0115) \ -DEFINE (Bget, 0116) \ -DEFINE (Bsubstring, 0117) \ -DEFINE (Bconcat2, 0120) \ -DEFINE (Bconcat3, 0121) \ -DEFINE (Bconcat4, 0122) \ -DEFINE (Bsub1, 0123) \ -DEFINE (Badd1, 0124) \ -DEFINE (Beqlsign, 0125) \ -DEFINE (Bgtr, 0126) \ -DEFINE (Blss, 0127) \ -DEFINE (Bleq, 0130) \ -DEFINE (Bgeq, 0131) \ -DEFINE (Bdiff, 0132) \ -DEFINE (Bnegate, 0133) \ -DEFINE (Bplus, 0134) \ -DEFINE (Bmax, 0135) \ -DEFINE (Bmin, 0136) \ -DEFINE (Bmult, 0137) \ - \ -DEFINE (Bpoint, 0140) \ -/* Was Bmark in v17. */ \ -DEFINE (Bsave_current_buffer, 0141) /* Obsolete. */ \ -DEFINE (Bgoto_char, 0142) \ -DEFINE (Binsert, 0143) \ -DEFINE (Bpoint_max, 0144) \ -DEFINE (Bpoint_min, 0145) \ -DEFINE (Bchar_after, 0146) \ -DEFINE (Bfollowing_char, 0147) \ -DEFINE (Bpreceding_char, 0150) \ -DEFINE (Bcurrent_column, 0151) \ -DEFINE (Bindent_to, 0152) \ -DEFINE (Beolp, 0154) \ -DEFINE (Beobp, 0155) \ -DEFINE (Bbolp, 0156) \ -DEFINE (Bbobp, 0157) \ -DEFINE (Bcurrent_buffer, 0160) \ -DEFINE (Bset_buffer, 0161) \ -DEFINE (Bsave_current_buffer_1, 0162) /* Replacing Bsave_current_buffer. */ \ -DEFINE (Binteractive_p, 0164) /* Obsolete since Emacs-24.1. */ \ - \ -DEFINE (Bforward_char, 0165) \ -DEFINE (Bforward_word, 0166) \ -DEFINE (Bskip_chars_forward, 0167) \ -DEFINE (Bskip_chars_backward, 0170) \ -DEFINE (Bforward_line, 0171) \ -DEFINE (Bchar_syntax, 0172) \ -DEFINE (Bbuffer_substring, 0173) \ -DEFINE (Bdelete_region, 0174) \ -DEFINE (Bnarrow_to_region, 0175) \ -DEFINE (Bwiden, 0176) \ -DEFINE (Bend_of_line, 0177) \ - \ -DEFINE (Bconstant2, 0201) \ -DEFINE (Bgoto, 0202) \ -DEFINE (Bgotoifnil, 0203) \ -DEFINE (Bgotoifnonnil, 0204) \ -DEFINE (Bgotoifnilelsepop, 0205) \ -DEFINE (Bgotoifnonnilelsepop, 0206) \ -DEFINE (Breturn, 0207) \ -DEFINE (Bdiscard, 0210) \ -DEFINE (Bdup, 0211) \ - \ -DEFINE (Bsave_excursion, 0212) \ -DEFINE (Bsave_window_excursion, 0213) /* Obsolete since Emacs-24.1. */ \ -DEFINE (Bsave_restriction, 0214) \ -DEFINE (Bcatch, 0215) \ - \ -DEFINE (Bunwind_protect, 0216) \ -DEFINE (Bcondition_case, 0217) \ -DEFINE (Btemp_output_buffer_setup, 0220) /* Obsolete since Emacs-24.1. */ \ -DEFINE (Btemp_output_buffer_show, 0221) /* Obsolete since Emacs-24.1. */ \ - \ -DEFINE (Bunbind_all, 0222) /* Obsolete. Never used. */ \ - \ -DEFINE (Bset_marker, 0223) \ -DEFINE (Bmatch_beginning, 0224) \ -DEFINE (Bmatch_end, 0225) \ -DEFINE (Bupcase, 0226) \ -DEFINE (Bdowncase, 0227) \ - \ -DEFINE (Bstringeqlsign, 0230) \ -DEFINE (Bstringlss, 0231) \ -DEFINE (Bequal, 0232) \ -DEFINE (Bnthcdr, 0233) \ -DEFINE (Belt, 0234) \ -DEFINE (Bmember, 0235) \ -DEFINE (Bassq, 0236) \ -DEFINE (Bnreverse, 0237) \ -DEFINE (Bsetcar, 0240) \ -DEFINE (Bsetcdr, 0241) \ -DEFINE (Bcar_safe, 0242) \ -DEFINE (Bcdr_safe, 0243) \ -DEFINE (Bnconc, 0244) \ -DEFINE (Bquo, 0245) \ -DEFINE (Brem, 0246) \ -DEFINE (Bnumberp, 0247) \ -DEFINE (Bintegerp, 0250) \ - \ -DEFINE (BRgoto, 0252) \ -DEFINE (BRgotoifnil, 0253) \ -DEFINE (BRgotoifnonnil, 0254) \ -DEFINE (BRgotoifnilelsepop, 0255) \ -DEFINE (BRgotoifnonnilelsepop, 0256) \ - \ -DEFINE (BlistN, 0257) \ -DEFINE (BconcatN, 0260) \ -DEFINE (BinsertN, 0261) \ - \ -/* Bstack_ref is code 0. */ \ -DEFINE (Bstack_set, 0262) \ -DEFINE (Bstack_set2, 0263) \ -DEFINE (BdiscardN, 0266) \ - \ -DEFINE (Bconstant, 0300) - -enum byte_code_op -{ -#define DEFINE(name, value) name = value, - BYTE_CODES -#undef DEFINE - -#if BYTE_CODE_SAFE - Bscan_buffer = 0153, /* No longer generated as of v18. */ - Bset_mark = 0163, /* this loser is no longer generated as of v18 */ -#endif -}; /* Fetch the next byte from the bytecode stream. */ @@ -316,10 +93,10 @@ the third, MAXDEPTH, the maximum stack depth used in this function. If the third argument is incorrect, Emacs may crash. */) (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth) { - return exec_byte_code (bytestr, vector, maxdepth, Qnil, 0, NULL); + return exec_byte_code__ (bytestr, vector, maxdepth, Qnil, 0, NULL); } -static void +void bcall0 (Lisp_Object f) { Ffuncall (1, &f); @@ -334,8 +111,8 @@ bcall0 (Lisp_Object f) executing BYTESTR. */ Lisp_Object -exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth, - Lisp_Object args_template, ptrdiff_t nargs, Lisp_Object *args) +exec_byte_code__ (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth, + Lisp_Object args_template, ptrdiff_t nargs, Lisp_Object *args) { #ifdef BYTE_CODE_METER int volatile this_op = 0; @@ -463,7 +240,6 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth, { [0 ... (Bconstant - 1)] = &&insn_default, [Bconstant ... 255] = &&insn_Bconstant, - #define DEFINE(name, value) LABEL (name) , BYTE_CODES #undef DEFINE @@ -1436,6 +1212,25 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth, return result; } +Lisp_Object +exec_byte_code (Lisp_Object byte_code, Lisp_Object args_template, + ptrdiff_t nargs, Lisp_Object *args) +{ +#ifdef HAVE_LIBJIT + if (byte_code_jit_on) + { + jit_byte_code__ (byte_code); + return jit_exec (byte_code, args_template, nargs, args); + } + else +#endif + return exec_byte_code__ (AREF (byte_code, COMPILED_BYTECODE), + AREF (byte_code, COMPILED_CONSTANTS), + AREF (byte_code, COMPILED_STACK_DEPTH), + args_template, nargs, args); +} + + /* `args_template' has the same meaning as in exec_byte_code() above. */ Lisp_Object get_byte_code_arity (Lisp_Object args_template) diff --git a/src/bytecode.h b/src/bytecode.h new file mode 100644 index 00000000000..18c9c267069 --- /dev/null +++ b/src/bytecode.h @@ -0,0 +1,320 @@ +/* Shared definitions for src/bytecode{,-jit}.c + Copyright (C) 2016 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 <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include "lisp.h" + +/* Define BYTE_CODE_SAFE true to enable some minor sanity checking, + useful for debugging the byte compiler. It defaults to false. */ + +#ifndef BYTE_CODE_SAFE +# define BYTE_CODE_SAFE false +#endif + +/* Define BYTE_CODE_METER to generate a byte-op usage histogram. */ +/* #define BYTE_CODE_METER */ + +/* If BYTE_CODE_THREADED is defined, then the interpreter will be + indirect threaded, using GCC's computed goto extension. This code, + as currently implemented, is incompatible with BYTE_CODE_SAFE and + BYTE_CODE_METER. */ +#if (defined __GNUC__ && !defined __STRICT_ANSI__ && !defined __CHKP__ \ + && !BYTE_CODE_SAFE && !defined BYTE_CODE_METER) +#define BYTE_CODE_THREADED +#endif + +/* Byte codes: */ + +#define BYTE_CODES \ + DEFINE (Bstack_ref, 0) /* Actually, Bstack_ref+0 is not implemented: use dup. */ \ + DEFINE (Bstack_ref1, 1) \ + DEFINE (Bstack_ref2, 2) \ + DEFINE (Bstack_ref3, 3) \ + DEFINE (Bstack_ref4, 4) \ + DEFINE (Bstack_ref5, 5) \ + DEFINE (Bstack_ref6, 6) \ + DEFINE (Bstack_ref7, 7) \ + DEFINE (Bvarref, 010) \ + DEFINE (Bvarref1, 011) \ + DEFINE (Bvarref2, 012) \ + DEFINE (Bvarref3, 013) \ + DEFINE (Bvarref4, 014) \ + DEFINE (Bvarref5, 015) \ + DEFINE (Bvarref6, 016) \ + DEFINE (Bvarref7, 017) \ + DEFINE (Bvarset, 020) \ + DEFINE (Bvarset1, 021) \ + DEFINE (Bvarset2, 022) \ + DEFINE (Bvarset3, 023) \ + DEFINE (Bvarset4, 024) \ + DEFINE (Bvarset5, 025) \ + DEFINE (Bvarset6, 026) \ + DEFINE (Bvarset7, 027) \ + DEFINE (Bvarbind, 030) \ + DEFINE (Bvarbind1, 031) \ + DEFINE (Bvarbind2, 032) \ + DEFINE (Bvarbind3, 033) \ + DEFINE (Bvarbind4, 034) \ + DEFINE (Bvarbind5, 035) \ + DEFINE (Bvarbind6, 036) \ + DEFINE (Bvarbind7, 037) \ + DEFINE_MANY (Bcall, 040, Ffuncall, 1) \ + DEFINE_MANY (Bcall1, 041, Ffuncall, 2) \ + DEFINE_MANY (Bcall2, 042, Ffuncall, 3) \ + DEFINE_MANY (Bcall3, 043, Ffuncall, 4) \ + DEFINE_MANY (Bcall4, 044, Ffuncall, 5) \ + DEFINE_MANY (Bcall5, 045, Ffuncall, 6) \ + DEFINE_MANY (Bcall6, 046, Ffuncall, -1) \ + DEFINE_MANY (Bcall7, 047, Ffuncall, -2) \ + DEFINE (Bunbind, 050) \ + DEFINE (Bunbind1, 051) \ + DEFINE (Bunbind2, 052) \ + DEFINE (Bunbind3, 053) \ + DEFINE (Bunbind4, 054) \ + DEFINE (Bunbind5, 055) \ + DEFINE (Bunbind6, 056) \ + DEFINE (Bunbind7, 057) \ + \ + DEFINE (Bpophandler, 060) \ + DEFINE (Bpushconditioncase, 061) \ + DEFINE (Bpushcatch, 062) \ + \ + DEFINE_FIXED (Bnth, 070, native_nth, 2) \ + DEFINE_FIXED (Bsymbolp, 071, native_symbolp, 1) \ + DEFINE_FIXED (Bconsp, 072, native_consp, 1) \ + DEFINE_FIXED (Bstringp, 073, native_stringp, 1) \ + DEFINE_FIXED (Blistp, 074, native_listp, 1) \ + DEFINE_FIXED (Beq, 075, native_eq, 2) \ + DEFINE_FIXED (Bmemq, 076, native_memq, 2) \ + DEFINE_FIXED (Bnot, 077, native_not, 1) \ + DEFINE_FIXED (Bcar, 0100, native_car, 1) \ + DEFINE_FIXED (Bcdr, 0101, native_cdr, 1) \ + DEFINE_FIXED (Bcons, 0102, Fcons, 2) \ + DEFINE_FIXED (Blist1, 0103, list1, 1) \ + DEFINE_FIXED (Blist2, 0104, list2, 2) \ + DEFINE_MANY (Blist3, 0105, Flist, 3) \ + DEFINE_MANY (Blist4, 0106, Flist, 4) \ + DEFINE_FIXED (Blength, 0107, Flength, 1) \ + DEFINE_FIXED (Baref, 0110, Faref, 2) \ + DEFINE_FIXED (Baset, 0111, Faset, 3) \ + DEFINE_FIXED (Bsymbol_value, 0112, Fsymbol_value, 1) \ + DEFINE_FIXED (Bsymbol_function, 0113, Fsymbol_function, 1) \ + DEFINE_FIXED (Bset, 0114, Fset, 2) \ + DEFINE_FIXED (Bfset, 0115, Ffset, 2) \ + DEFINE_FIXED (Bget, 0116, Fget, 2) \ + DEFINE_FIXED (Bsubstring, 0117, Fsubstring, 3) \ + DEFINE_MANY (Bconcat2, 0120, Fconcat, 2) \ + DEFINE_MANY (Bconcat3, 0121, Fconcat, 3) \ + DEFINE_MANY (Bconcat4, 0122, Fconcat, 4) \ + DEFINE (Bsub1, 0123) \ + DEFINE (Badd1, 0124) \ + DEFINE_FIXED (Beqlsign, 0125, native_eqlsign, 2) \ + DEFINE (Bgtr, 0126) \ + DEFINE (Blss, 0127) \ + DEFINE (Bleq, 0130) \ + DEFINE (Bgeq, 0131) \ + DEFINE_MANY (Bdiff, 0132, Fminus, 2) \ + DEFINE_FIXED (Bnegate, 0133, native_negate, 1) \ + DEFINE_MANY (Bplus, 0134, Fplus, 2) \ + DEFINE_MANY (Bmax, 0135, Fmax, 2) \ + DEFINE_MANY (Bmin, 0136, Fmin, 2) \ + DEFINE_MANY (Bmult, 0137, Ftimes, 2) \ + \ + DEFINE_FIXED (Bpoint, 0140, native_point, 0) \ + /* Was Bmark in v17. */ \ + DEFINE (Bsave_current_buffer, 0141) /* Obsolete. */ \ + DEFINE_FIXED (Bgoto_char, 0142, Fgoto_char, 1) \ + DEFINE_MANY (Binsert, 0143, Finsert, 1) \ + DEFINE_FIXED (Bpoint_max, 0144, native_point_max, 0) \ + DEFINE_FIXED (Bpoint_min, 0145, native_point_min, 0) \ + DEFINE_FIXED (Bchar_after, 0146, Fchar_after, 1) \ + DEFINE_FIXED (Bfollowing_char, 0147, Ffollowing_char, 0) \ + DEFINE_FIXED (Bpreceding_char, 0150, Fprevious_char, 0) \ + DEFINE_FIXED (Bcurrent_column, 0151, native_current_column, 0) \ + DEFINE (Bindent_to, 0152) \ + DEFINE_FIXED (Beolp, 0154, Feolp, 0) \ + DEFINE_FIXED (Beobp, 0155, Feobp, 0) \ + DEFINE_FIXED (Bbolp, 0156, Fbolp, 0) \ + DEFINE_FIXED (Bbobp, 0157, Fbobp, 0) \ + DEFINE_FIXED (Bcurrent_buffer, 0160, Fcurrent_buffer, 0) \ + DEFINE_FIXED (Bset_buffer, 0161, Fset_buffer, 1) \ + DEFINE (Bsave_current_buffer_1, 0162) /* Replacing Bsave_current_buffer. */ \ + DEFINE_FIXED (Binteractive_p, 0164, native_interactive_p, 0) /* Obsolete since Emacs-24.1. */ \ + \ + DEFINE_FIXED (Bforward_char, 0165, Fforward_char, 1) \ + DEFINE_FIXED (Bforward_word, 0166, Fforward_word, 1) \ + DEFINE_FIXED (Bskip_chars_forward, 0167, Fskip_chars_forward, 2) \ + DEFINE_FIXED (Bskip_chars_backward, 0170, Fskip_chars_backward, 2) \ + DEFINE_FIXED (Bforward_line, 0171, Fforward_line, 1) \ + DEFINE_FIXED (Bchar_syntax, 0172, native_char_syntax, 1) \ + DEFINE_FIXED (Bbuffer_substring, 0173, Fbuffer_substring, 2) \ + DEFINE_FIXED (Bdelete_region, 0174, Fdelete_region, 2) \ + DEFINE_FIXED (Bnarrow_to_region, 0175, Fnarrow_to_region, 2) \ + DEFINE_FIXED (Bwiden, 0176, Fwiden, 0) \ + DEFINE_FIXED (Bend_of_line, 0177, Fend_of_line, 1) \ + \ + DEFINE (Bconstant2, 0201) \ + DEFINE (Bgoto, 0202) \ + DEFINE (Bgotoifnil, 0203) \ + DEFINE (Bgotoifnonnil, 0204) \ + DEFINE (Bgotoifnilelsepop, 0205) \ + DEFINE (Bgotoifnonnilelsepop, 0206) \ + DEFINE (Breturn, 0207) \ + DEFINE (Bdiscard, 0210) \ + DEFINE (Bdup, 0211) \ + \ + DEFINE (Bsave_excursion, 0212) \ + DEFINE_FIXED (Bsave_window_excursion, 0213, native_save_window_excursion, 1) /* Obsolete since Emacs-24.1. */ \ + DEFINE (Bsave_restriction, 0214) \ + DEFINE_FIXED (Bcatch, 0215, native_catch, 2) \ + \ + DEFINE (Bunwind_protect, 0216) \ + DEFINE_FIXED (Bcondition_case, 0217, internal_lisp_condition_case, 3) \ + DEFINE_FIXED (Btemp_output_buffer_setup, 0220, native_temp_output_buffer_setup, 1) /* Obsolete since Emacs-24.1. */ \ + DEFINE (Btemp_output_buffer_show, 0221) /* Obsolete since Emacs-24.1. */ \ + \ + DEFINE (Bunbind_all, 0222) /* Obsolete. Never used. */ \ + \ + DEFINE_FIXED (Bset_marker, 0223, Fset_marker, 3) \ + DEFINE_FIXED (Bmatch_beginning, 0224, Fmatch_beginning, 1) \ + DEFINE_FIXED (Bmatch_end, 0225, Fmatch_end, 1) \ + DEFINE_FIXED (Bupcase, 0226, Fupcase, 1) \ + DEFINE_FIXED (Bdowncase, 0227, Fdowncase, 1) \ + \ + DEFINE_FIXED (Bstringeqlsign, 0230, Fstring_equal, 2) \ + DEFINE_FIXED (Bstringlss, 0231, Fstring_lessp, 2) \ + DEFINE_FIXED (Bequal, 0232, Fequal, 2) \ + DEFINE_FIXED (Bnthcdr, 0233, Fnthcdr, 2) \ + DEFINE_FIXED (Belt, 0234, native_elt, 2) \ + DEFINE_FIXED (Bmember, 0235, Fmember, 2) \ + DEFINE_FIXED (Bassq, 0236, Fassq, 2) \ + DEFINE_FIXED (Bnreverse, 0237, Fnreverse, 1) \ + DEFINE_FIXED (Bsetcar, 0240, Fsetcar, 2) \ + DEFINE_FIXED (Bsetcdr, 0241, Fsetcdr, 2) \ + DEFINE_FIXED (Bcar_safe, 0242, native_car_safe, 1) \ + DEFINE_FIXED (Bcdr_safe, 0243, native_cdr_safe, 1) \ + DEFINE_MANY (Bnconc, 0244, Fnconc, 2) \ + DEFINE_MANY (Bquo, 0245, Fquo, 2) \ + DEFINE_FIXED (Brem, 0246, Frem, 2) \ + DEFINE_FIXED (Bnumberp, 0247, native_number_p, 1) \ + DEFINE_FIXED (Bintegerp, 0250, native_integer_p, 1) \ + \ + DEFINE (BRgoto, 0252) \ + DEFINE (BRgotoifnil, 0253) \ + DEFINE (BRgotoifnonnil, 0254) \ + DEFINE (BRgotoifnilelsepop, 0255) \ + DEFINE (BRgotoifnonnilelsepop, 0256) \ + \ + DEFINE_MANY (BlistN, 0257, Flist, -1) \ + DEFINE_MANY (BconcatN, 0260, Fconcat, -1) \ + DEFINE_MANY (BinsertN, 0261, Finsert, -1) \ + \ + /* Bstack_ref is code 0. */ \ + DEFINE (Bstack_set, 0262) \ + DEFINE (Bstack_set2, 0263) \ + DEFINE (BdiscardN, 0266) \ + \ + DEFINE (Bconstant, 0300) + +enum byte_code_op +{ +#define DEFINE_FIXED(bname, value, fname, num) DEFINE (bname, value) +#define DEFINE_MANY(bname, value, fname, num) \ + DEFINE_FIXED (bname, value, fname, num) +#define DEFINE(name, value) name = value, + BYTE_CODES +#undef DEFINE + +#if BYTE_CODE_SAFE + Bscan_buffer = 0153, /* No longer generated as of v18. */ + Bset_mark = 0163, /* this loser is no longer generated as of v18 */ +#endif +}; + +/* Whether to maintain a `top' and `bottom' field in the stack frame. */ +#define BYTE_MAINTAIN_TOP BYTE_CODE_SAFE + +/* Structure describing a value stack used during byte-code execution + in Fbyte_code. */ + +struct byte_stack +{ + /* Program counter. This points into the byte_string below + and is relocated when that string is relocated. */ + const unsigned char *pc; + + /* Top and bottom of stack. The bottom points to an area of memory + allocated with alloca in Fbyte_code. */ +#if BYTE_MAINTAIN_TOP + Lisp_Object *top, *bottom; +#endif + + /* The string containing the byte-code, and its current address. + Storing this here protects it from GC because mark_byte_stack + marks it. */ + Lisp_Object byte_string; + const unsigned char *byte_string_start; + + /* Next entry in byte_stack_list. */ + struct byte_stack *next; +}; + +/* A list of currently active byte-code execution value stacks. + Fbyte_code adds an entry to the head of this list before it starts + processing byte-code, and it removes the entry again when it is + done. Signaling an error truncates the list. + + byte_stack_list is a macro defined in thread.h. */ +/* struct byte_stack *byte_stack_list; */ + +/* Actions that must be performed before and after calling a function + that might GC. */ + +#if !BYTE_MAINTAIN_TOP +#define BEFORE_POTENTIAL_GC() ((void)0) +#define AFTER_POTENTIAL_GC() ((void)0) +#else +#define BEFORE_POTENTIAL_GC() stack.top = top +#define AFTER_POTENTIAL_GC() stack.top = NULL +#endif + +/* Garbage collect if we have consed enough since the last time. + We do this at every branch, to avoid loops that never GC. */ + +#define MAYBE_GC() \ + do { \ + BEFORE_POTENTIAL_GC (); \ + maybe_gc (); \ + AFTER_POTENTIAL_GC (); \ + } while (0) + +extern void +bcall0 (Lisp_Object f); + +extern Lisp_Object +exec_byte_code__ (Lisp_Object, Lisp_Object, Lisp_Object, + Lisp_Object, ptrdiff_t, Lisp_Object *); + +#ifdef HAVE_LIBJIT +extern void +jit_byte_code__ (Lisp_Object); + +extern Lisp_Object +jit_exec (Lisp_Object, Lisp_Object, ptrdiff_t, Lisp_Object *); +#endif diff --git a/src/data.c b/src/data.c index 8e07bf01b44..c73f04cf638 100644 --- a/src/data.c +++ b/src/data.c @@ -3578,6 +3578,7 @@ syms_of_data (void) DEFSYM (Qsequencep, "sequencep"); DEFSYM (Qbufferp, "bufferp"); DEFSYM (Qvectorp, "vectorp"); + DEFSYM (Qcompiledp, "compiledp"); DEFSYM (Qbool_vector_p, "bool-vector-p"); DEFSYM (Qchar_or_string_p, "char-or-string-p"); DEFSYM (Qmarkerp, "markerp"); diff --git a/src/emacs.c b/src/emacs.c index 28b395c4fb4..fa2b6ebb9c6 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -1457,6 +1457,9 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem syms_of_buffer (); syms_of_bytecode (); +#ifdef HAVE_LIBJIT + syms_of_bytecode_jit (); +#endif syms_of_callint (); syms_of_casefiddle (); syms_of_casetab (); diff --git a/src/eval.c b/src/eval.c index 22b02b49521..54a646b69e9 100644 --- a/src/eval.c +++ b/src/eval.c @@ -2962,11 +2962,11 @@ 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); - return exec_byte_code (AREF (fun, COMPILED_BYTECODE), - AREF (fun, COMPILED_CONSTANTS), - AREF (fun, COMPILED_STACK_DEPTH), - syms_left, - nargs, arg_vector); + Lisp_Object (*interpreter)(Lisp_Object, Lisp_Object, + ptrdiff_t, Lisp_Object *) = + (void *)AREF (fun, COMPILED_INTERPRETER); + return interpreter (fun, syms_left, + nargs, arg_vector); } lexenv = Qnil; } @@ -3040,10 +3040,10 @@ 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); - val = exec_byte_code (AREF (fun, COMPILED_BYTECODE), - AREF (fun, COMPILED_CONSTANTS), - AREF (fun, COMPILED_STACK_DEPTH), - Qnil, 0, 0); + Lisp_Object (*interpreter)(Lisp_Object, Lisp_Object, + ptrdiff_t, Lisp_Object *) = + (void *)AREF (fun, COMPILED_INTERPRETER); + val = interpreter (fun, Qnil, 0, 0); } return unbind_to (count, val); diff --git a/src/lisp.h b/src/lisp.h index 1ac38164c27..44b59f6bac5 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -2657,7 +2657,10 @@ enum Lisp_Compiled COMPILED_CONSTANTS = 2, COMPILED_STACK_DEPTH = 3, COMPILED_DOC_STRING = 4, - COMPILED_INTERACTIVE = 5 + COMPILED_INTERACTIVE = 5, + COMPILED_INTERPRETER = 6, + COMPILED_JIT_CTXT = 7, + COMPILED_JIT_CLOSURE = 8 }; /* Flag bits in a character. These also get used in termhooks.h. @@ -2769,6 +2772,11 @@ CHECK_STRING_CAR (Lisp_Object x) { CHECK_TYPE (STRINGP (XCAR (x)), Qstringp, XCAR (x)); } +INLINE void +CHECK_COMPILED (Lisp_Object x) +{ + CHECK_TYPE (COMPILEDP (x), Qcompiledp, x); +} /* This is a bit special because we always need size afterwards. */ INLINE ptrdiff_t CHECK_VECTOR_OR_STRING (Lisp_Object x) @@ -3101,6 +3109,7 @@ struct handler enum handlertype type; Lisp_Object tag_or_ch; Lisp_Object val; + Lisp_Object *stack; struct handler *next; struct handler *nextfree; @@ -3602,7 +3611,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 @@ -4159,10 +4167,14 @@ extern int read_bytecode_char (bool); /* Defined in bytecode.c. */ extern void syms_of_bytecode (void); -extern Lisp_Object exec_byte_code (Lisp_Object, Lisp_Object, Lisp_Object, - Lisp_Object, ptrdiff_t, Lisp_Object *); +extern Lisp_Object exec_byte_code (Lisp_Object, Lisp_Object, ptrdiff_t, Lisp_Object *); extern Lisp_Object get_byte_code_arity (Lisp_Object); +/* Defined in bytecode-jit.c */ +#ifdef HAVE_LIBJIT +extern void syms_of_bytecode_jit (void); +#endif + /* Defined in macros.c. */ extern void init_macros (void); extern void syms_of_macros (void); diff --git a/src/lread.c b/src/lread.c index 094aa628eec..f0a764f2dea 100644 --- a/src/lread.c +++ b/src/lread.c @@ -2755,14 +2755,14 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list) { /* Accept compiled functions at read-time so that we don't have to build them using function calls. */ - Lisp_Object tmp; + Lisp_Object tmp, ret; struct Lisp_Vector *vec; tmp = read_vector (readcharfun, 1); vec = XVECTOR (tmp); if (vec->header.size == 0) invalid_syntax ("Empty byte-code object"); - make_byte_code (vec); - return tmp; + ret = Fmake_byte_code (vec->header.size, vec->contents); + return ret; } if (c == '(') { |