/* 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 . */ #include #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 #include /* 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 */