diff options
| author | Andrea Corallo | 2026-03-12 14:30:54 +0100 |
|---|---|---|
| committer | Andrea Corallo | 2026-03-12 14:30:54 +0100 |
| commit | 6198bb89b453a8b93113ac6bbb102043f5636334 (patch) | |
| tree | 0006eddd65f0a0508be443f1a0cc3f22d437c74f | |
| parent | fe121ef586b4adf2befc7e2af42d65f7acc25891 (diff) | |
| download | emacs-scratch/better-comp.tar.gz emacs-scratch/better-comp.zip | |
nativecomp: optimize local CU calls at speed 2scratch/better-comp
* etc/NEWS: Document speed-2 local call optimization.
* etc/TODO: Remove completed item.
* lisp/emacs-lisp/comp.el
(comp--cu-local-func-c-name-v): New function.
(comp--call-optim-form-call): Enable named local direct calls
at speed 2.
(comp--function-trampoline-form): New helper.
(comp-trampoline-compile, comp-local-function-trampoline-compile):
Share trampoline generation.
* lisp/emacs-lisp/comp-run.el
(comp-local-function-trampoline--install-now): New function.
(comp-local-function-trampoline-install): Install local trampolines.
Defer trampoline compilation until after load.
* src/comp.c (emit_call, emit_ctxt_code, compile_function)
(load_comp_unit, unload_comp_unit): Add local function relocation
support.
(native_comp_local_function_p): New function.
(comp--install-local-function-trampoline): New subr.
(syms_of_comp): Register it and update trampoline docs.
* src/comp.h (Lisp_Native_Comp_Unit): Add local relocation slot.
(native_comp_local_function_p): Declare.
* src/data.c (Ffset): Install local trampolines for redefined
named local native functions. Keep skipping anonymous lambdas.
* src/pdumper.c: Clear local relocation state.
* test/src/comp-tests.el
(comp-tests--run-in-sub-emacs): New helper.
(comp-tests--direct-call-redefinition-form): New helper.
(comp-tests-direct-call-redefinition-speed-split): New test.
(comp-tests-anonymous-lambda-recompile): New test.
(comp-tests-direct-call-with-lambdas): Use an explicit output file.
| -rw-r--r-- | etc/NEWS | 6 | ||||
| -rw-r--r-- | etc/TODO | 9 | ||||
| -rw-r--r-- | lisp/emacs-lisp/comp-run.el | 29 | ||||
| -rw-r--r-- | lisp/emacs-lisp/comp.el | 63 | ||||
| -rw-r--r-- | src/comp.c | 213 | ||||
| -rw-r--r-- | src/comp.h | 10 | ||||
| -rw-r--r-- | src/data.c | 20 | ||||
| -rw-r--r-- | src/pdumper.c | 1 | ||||
| -rw-r--r-- | test/src/comp-tests.el | 85 |
9 files changed, 380 insertions, 56 deletions
| @@ -3885,6 +3885,12 @@ At native compilation speed 2 and above, declared function types are | |||
| 3885 | used for type propagation, value prediction, and optimization within | 3885 | used for type propagation, value prediction, and optimization within |
| 3886 | function bodies. | 3886 | function bodies. |
| 3887 | 3887 | ||
| 3888 | ** Native compilation speed 2 now optimizes local calls. | ||
| 3889 | Named calls between native-compiled functions in the same compilation | ||
| 3890 | unit can now be optimized at speed 2 while still honoring later | ||
| 3891 | redefinition through a trampoline. At speed 3, these calls keep the | ||
| 3892 | previous direct-call behavior. | ||
| 3893 | |||
| 3888 | ** Nested backquotes are not supported any more in Pcase patterns. | 3894 | ** Nested backquotes are not supported any more in Pcase patterns. |
| 3889 | 3895 | ||
| 3890 | --- | 3896 | --- |
| @@ -993,15 +993,6 @@ It would make it easy to add (and remove) mappings like | |||
| 993 | 993 | ||
| 994 | *** Performance | 994 | *** Performance |
| 995 | 995 | ||
| 996 | **** Intra compilation unit call optimization | ||
| 997 | |||
| 998 | We could have a mechanism similar to what we use for optimizing calls | ||
| 999 | to primitive functions. IE using a link table for each compilation | ||
| 1000 | unit (CU) such that calls from functions in a CU targeting functions | ||
| 1001 | in the same CU don't have to go through funcall. If one of these | ||
| 1002 | functions is redefined, a trampoline is compiled and installed to | ||
| 1003 | restore the redirection through funcall. | ||
| 1004 | |||
| 1005 | **** Better runtime function inlining | 996 | **** Better runtime function inlining |
| 1006 | 997 | ||
| 1007 | Several functions could be open-coded in generated code once exposed to | 998 | Several functions could be open-coded in generated code once exposed to |
diff --git a/lisp/emacs-lisp/comp-run.el b/lisp/emacs-lisp/comp-run.el index f329d627392..b63479b8ac4 100644 --- a/lisp/emacs-lisp/comp-run.el +++ b/lisp/emacs-lisp/comp-run.el | |||
| @@ -152,6 +152,7 @@ if `confirm-kill-processes' is non-nil." | |||
| 152 | (defvar native-comp-enable-subr-trampolines) | 152 | (defvar native-comp-enable-subr-trampolines) |
| 153 | 153 | ||
| 154 | (declare-function comp--install-trampoline "comp.c") | 154 | (declare-function comp--install-trampoline "comp.c") |
| 155 | (declare-function comp--install-local-function-trampoline "comp.c") | ||
| 155 | (declare-function comp-el-to-eln-filename "comp.c") | 156 | (declare-function comp-el-to-eln-filename "comp.c") |
| 156 | (declare-function native-elisp-load "comp.c") | 157 | (declare-function native-elisp-load "comp.c") |
| 157 | 158 | ||
| @@ -407,6 +408,7 @@ Return the trampoline if found or nil otherwise." | |||
| 407 | do (cl-return (native-elisp-load filename)))) | 408 | do (cl-return (native-elisp-load filename)))) |
| 408 | 409 | ||
| 409 | (declare-function comp-trampoline-compile "comp") | 410 | (declare-function comp-trampoline-compile "comp") |
| 411 | (declare-function comp-local-function-trampoline-compile "comp") | ||
| 410 | ;;;###autoload | 412 | ;;;###autoload |
| 411 | (defun comp-subr-trampoline-install (subr-name) | 413 | (defun comp-subr-trampoline-install (subr-name) |
| 412 | "Make SUBR-NAME effectively advice-able when called from native code." | 414 | "Make SUBR-NAME effectively advice-able when called from native code." |
| @@ -424,6 +426,33 @@ Return the trampoline if found or nil otherwise." | |||
| 424 | (comp--install-trampoline subr-name trampoline))))) | 426 | (comp--install-trampoline subr-name trampoline))))) |
| 425 | 427 | ||
| 426 | ;;;###autoload | 428 | ;;;###autoload |
| 429 | (defun comp-local-function-trampoline--install-now (function-name function) | ||
| 430 | "Install a trampoline immediately for local FUNCTION-NAME." | ||
| 431 | (when-let* ((trampoline (comp-local-function-trampoline-compile | ||
| 432 | function-name function))) | ||
| 433 | (comp--install-local-function-trampoline function trampoline))) | ||
| 434 | |||
| 435 | ;;;###autoload | ||
| 436 | (defun comp-local-function-trampoline-install (function-name function) | ||
| 437 | "Make local FUNCTION-NAME effectively redefinable for speed-2 native callers." | ||
| 438 | (unless (null native-comp-enable-subr-trampolines) | ||
| 439 | (cl-assert (native-comp-function-p function)) | ||
| 440 | (if (and load-in-progress (stringp load-file-name)) | ||
| 441 | (let* ((loaded-file load-file-name) | ||
| 442 | (installer nil)) | ||
| 443 | ;; Avoid native-compiling the trampoline while the current file is | ||
| 444 | ;; still being loaded, otherwise eager macro-expansion can recurse | ||
| 445 | ;; back into that load. | ||
| 446 | (setq installer | ||
| 447 | (lambda (file) | ||
| 448 | (when (equal file loaded-file) | ||
| 449 | (remove-hook 'after-load-functions installer) | ||
| 450 | (comp-local-function-trampoline--install-now | ||
| 451 | function-name function)))) | ||
| 452 | (add-hook 'after-load-functions installer 'append)) | ||
| 453 | (comp-local-function-trampoline--install-now function-name function)))) | ||
| 454 | |||
| 455 | ;;;###autoload | ||
| 427 | (defun native--compile-async (files &optional recursively load selector) | 456 | (defun native--compile-async (files &optional recursively load selector) |
| 428 | ;; BEWARE, this function is also called directly from C. | 457 | ;; BEWARE, this function is also called directly from C. |
| 429 | "Compile FILES asynchronously. | 458 | "Compile FILES asynchronously. |
diff --git a/lisp/emacs-lisp/comp.el b/lisp/emacs-lisp/comp.el index ec95dfdb8b2..ba9a25c39e1 100644 --- a/lisp/emacs-lisp/comp.el +++ b/lisp/emacs-lisp/comp.el | |||
| @@ -612,6 +612,14 @@ In use by the back-end." | |||
| 612 | finally return t) | 612 | finally return t) |
| 613 | t)) | 613 | t)) |
| 614 | 614 | ||
| 615 | (defun comp--cu-local-func-c-name-v (&optional ctxt) | ||
| 616 | "Return exported local function C names for CTXT in compilation order." | ||
| 617 | (vconcat | ||
| 618 | (cl-loop | ||
| 619 | for form in (comp-ctxt-top-level-forms (or ctxt comp-ctxt)) | ||
| 620 | when (byte-to-native-func-def-p form) | ||
| 621 | collect (byte-to-native-func-def-c-name form)))) | ||
| 622 | |||
| 615 | (defun comp--function-pure-p (f) | 623 | (defun comp--function-pure-p (f) |
| 616 | "Return t if F is pure." | 624 | "Return t if F is pure." |
| 617 | (or (get f 'pure) | 625 | (or (get f 'pure) |
| @@ -2936,10 +2944,9 @@ Return t if something was changed." | |||
| 2936 | ;; - Recursive calls gets optimized into direct calls. | 2944 | ;; - Recursive calls gets optimized into direct calls. |
| 2937 | ;; Triggered at native-comp-speed >= 2. | 2945 | ;; Triggered at native-comp-speed >= 2. |
| 2938 | ;; - Intra compilation unit procedure calls gets optimized into direct calls. | 2946 | ;; - Intra compilation unit procedure calls gets optimized into direct calls. |
| 2939 | ;; This can be a big win and even allow gcc to inline but does not make | 2947 | ;; At native-comp-speed == 2 named calls use a per-CU relocation table so |
| 2940 | ;; function in the compilation unit re-definable safely without recompiling | 2948 | ;; redefinition can still be honored via trampolines. |
| 2941 | ;; the full compilation unit. | 2949 | ;; At native-comp-speed >= 3 keep the previous raw direct-call behavior. |
| 2942 | ;; For this reason this is triggered only at native-comp-speed == 3. | ||
| 2943 | 2950 | ||
| 2944 | (defun comp--func-in-unit (func) | 2951 | (defun comp--func-in-unit (func) |
| 2945 | "Given FUNC return the `comp-fun' definition in the current context. | 2952 | "Given FUNC return the `comp-fun' definition in the current context. |
| @@ -2987,15 +2994,15 @@ FUNCTION can be a function-name or byte compiled function." | |||
| 2987 | (fill-args args maxarg)))) | 2994 | (fill-args args maxarg)))) |
| 2988 | `(,call-type ,callee ,@args))) | 2995 | `(,call-type ,callee ,@args))) |
| 2989 | ;; Intra compilation unit procedure call optimization. | 2996 | ;; Intra compilation unit procedure call optimization. |
| 2990 | ;; Attention speed 3 triggers this for non self calls too!! | ||
| 2991 | ((and comp-func-callee | 2997 | ((and comp-func-callee |
| 2992 | (comp-func-c-name comp-func-callee) | 2998 | (comp-func-c-name comp-func-callee) |
| 2993 | (or (and (>= (comp-func-speed comp-func) 3) | 2999 | (or (and (>= (comp-func-speed comp-func) 3) |
| 2994 | (comp--func-unique-in-cu-p callee)) | 3000 | (comp--func-unique-in-cu-p callee)) |
| 2995 | (and (>= (comp-func-speed comp-func) 2) | 3001 | (and (>= (comp-func-speed comp-func) 2) |
| 2996 | ;; Anonymous lambdas can't be redefined so are | 3002 | (or (comp--func-unique-in-cu-p callee) |
| 2997 | ;; always safe to optimize. | 3003 | ;; Anonymous lambdas can't be redefined so are |
| 2998 | (byte-code-function-p callee)))) | 3004 | ;; always safe to optimize. |
| 3005 | (byte-code-function-p callee))))) | ||
| 2999 | (let* ((func-args (comp-func-l-args comp-func-callee)) | 3006 | (let* ((func-args (comp-func-l-args comp-func-callee)) |
| 3000 | (nargs (comp-nargs-p func-args)) | 3007 | (nargs (comp-nargs-p func-args)) |
| 3001 | (call-type (if nargs 'direct-callref 'direct-call)) | 3008 | (call-type (if nargs 'direct-callref 'direct-call)) |
| @@ -3468,6 +3475,18 @@ Prepare every function for final compilation and drive the C back-end." | |||
| 3468 | (push (gensym "arg") lambda-list)) | 3475 | (push (gensym "arg") lambda-list)) |
| 3469 | (reverse lambda-list))) | 3476 | (reverse lambda-list))) |
| 3470 | 3477 | ||
| 3478 | (defun comp--function-trampoline-form (function-name function) | ||
| 3479 | "Return a trampoline form for FUNCTION-NAME with FUNCTION's ABI." | ||
| 3480 | (let ((lambda-list (comp--make-lambda-list-from-subr function))) | ||
| 3481 | `(lambda ,lambda-list | ||
| 3482 | (let ((f #',function-name)) | ||
| 3483 | (,(if (memq '&rest lambda-list) #'apply 'funcall) | ||
| 3484 | f | ||
| 3485 | ,@(cl-loop | ||
| 3486 | for arg in lambda-list | ||
| 3487 | unless (memq arg '(&optional &rest)) | ||
| 3488 | collect arg)))))) | ||
| 3489 | |||
| 3471 | (defun comp--trampoline-abs-filename (subr-name) | 3490 | (defun comp--trampoline-abs-filename (subr-name) |
| 3472 | "Return the absolute filename for a trampoline for SUBR-NAME." | 3491 | "Return the absolute filename for a trampoline for SUBR-NAME." |
| 3473 | (cl-loop | 3492 | (cl-loop |
| @@ -3497,18 +3516,9 @@ Prepare every function for final compilation and drive the C back-end." | |||
| 3497 | ;;;###autoload | 3516 | ;;;###autoload |
| 3498 | (defun comp-trampoline-compile (subr-name) | 3517 | (defun comp-trampoline-compile (subr-name) |
| 3499 | "Synthesize compile and return a trampoline for SUBR-NAME." | 3518 | "Synthesize compile and return a trampoline for SUBR-NAME." |
| 3500 | (let* ((lambda-list (comp--make-lambda-list-from-subr | 3519 | (let* ((form (comp--function-trampoline-form |
| 3501 | (symbol-function subr-name))) | 3520 | subr-name |
| 3502 | ;; The synthesized trampoline must expose the exact same ABI of | 3521 | (symbol-function subr-name))) |
| 3503 | ;; the primitive we are replacing in the function reloc table. | ||
| 3504 | (form `(lambda ,lambda-list | ||
| 3505 | (let ((f #',subr-name)) | ||
| 3506 | (,(if (memq '&rest lambda-list) #'apply 'funcall) | ||
| 3507 | f | ||
| 3508 | ,@(cl-loop | ||
| 3509 | for arg in lambda-list | ||
| 3510 | unless (memq arg '(&optional &rest)) | ||
| 3511 | collect arg))))) | ||
| 3512 | ;; Use speed 1 for compilation speed and not to optimize away | 3522 | ;; Use speed 1 for compilation speed and not to optimize away |
| 3513 | ;; funcall calls! | 3523 | ;; funcall calls! |
| 3514 | (byte-optimize nil) | 3524 | (byte-optimize nil) |
| @@ -3518,6 +3528,19 @@ Prepare every function for final compilation and drive the C back-end." | |||
| 3518 | form nil | 3528 | form nil |
| 3519 | (comp--trampoline-abs-filename subr-name)))) | 3529 | (comp--trampoline-abs-filename subr-name)))) |
| 3520 | 3530 | ||
| 3531 | ;; Called from comp-run.el | ||
| 3532 | ;;;###autoload | ||
| 3533 | (defun comp-local-function-trampoline-compile (function-name function) | ||
| 3534 | "Synthesize compile and return a trampoline for local FUNCTION-NAME. | ||
| 3535 | FUNCTION provides the ABI that the trampoline must expose." | ||
| 3536 | (let* ((form (comp--function-trampoline-form function-name function)) | ||
| 3537 | ;; Use speed 1 for compilation speed and not to optimize away | ||
| 3538 | ;; funcall calls. | ||
| 3539 | (byte-optimize nil) | ||
| 3540 | (native-comp-speed 1) | ||
| 3541 | (lexical-binding t)) | ||
| 3542 | (comp--native-compile form))) | ||
| 3543 | |||
| 3521 | 3544 | ||
| 3522 | ;; Some entry point support code. | 3545 | ;; Some entry point support code. |
| 3523 | 3546 | ||
diff --git a/src/comp.c b/src/comp.c index ac45eb72cfc..4227a502693 100644 --- a/src/comp.c +++ b/src/comp.c | |||
| @@ -480,10 +480,12 @@ load_gccjit_if_necessary (bool mandatory) | |||
| 480 | #define DATA_RELOC_EPHEMERAL_SYM "d_reloc_eph" | 480 | #define DATA_RELOC_EPHEMERAL_SYM "d_reloc_eph" |
| 481 | 481 | ||
| 482 | #define FUNC_LINK_TABLE_SYM "freloc_link_table" | 482 | #define FUNC_LINK_TABLE_SYM "freloc_link_table" |
| 483 | #define LOCAL_FUNC_LINK_TABLE_SYM "local_freloc_link_table" | ||
| 483 | #define LINK_TABLE_HASH_SYM "freloc_hash" | 484 | #define LINK_TABLE_HASH_SYM "freloc_hash" |
| 484 | #define COMP_UNIT_SYM "comp_unit" | 485 | #define COMP_UNIT_SYM "comp_unit" |
| 485 | #define TEXT_DATA_RELOC_SYM "text_data_reloc" | 486 | #define TEXT_DATA_RELOC_SYM "text_data_reloc" |
| 486 | #define TEXT_DATA_RELOC_EPHEMERAL_SYM "text_data_reloc_eph" | 487 | #define TEXT_DATA_RELOC_EPHEMERAL_SYM "text_data_reloc_eph" |
| 488 | #define TEXT_LOCAL_FUNC_C_NAMES_SYM "text_local_func_c_names" | ||
| 487 | 489 | ||
| 488 | #define TEXT_OPTIM_QLY_SYM "text_optim_qly" | 490 | #define TEXT_OPTIM_QLY_SYM "text_optim_qly" |
| 489 | #define TEXT_FDOC_SYM "text_data_fdoc" | 491 | #define TEXT_FDOC_SYM "text_data_fdoc" |
| @@ -650,6 +652,10 @@ typedef struct { | |||
| 650 | gcc_jit_type *func_relocs_ptr_type; | 652 | gcc_jit_type *func_relocs_ptr_type; |
| 651 | /* Pointer to this structure local to each function. */ | 653 | /* Pointer to this structure local to each function. */ |
| 652 | gcc_jit_lvalue *func_relocs_local; | 654 | gcc_jit_lvalue *func_relocs_local; |
| 655 | /* Per compilation unit redirection table for local named functions. */ | ||
| 656 | gcc_jit_lvalue *local_func_relocs; | ||
| 657 | gcc_jit_lvalue *local_func_relocs_local; | ||
| 658 | Lisp_Object local_func_reloc_idx_h; /* c-name -> relocation index. */ | ||
| 653 | gcc_jit_function *memcpy; | 659 | gcc_jit_function *memcpy; |
| 654 | Lisp_Object d_default_idx; | 660 | Lisp_Object d_default_idx; |
| 655 | Lisp_Object d_ephemeral_idx; | 661 | Lisp_Object d_ephemeral_idx; |
| @@ -947,6 +953,9 @@ emit_comment (const char *str) | |||
| 947 | str); | 953 | str); |
| 948 | } | 954 | } |
| 949 | 955 | ||
| 956 | static gcc_jit_rvalue *emit_coerce (gcc_jit_type *new_type, | ||
| 957 | gcc_jit_rvalue *obj); | ||
| 958 | |||
| 950 | /* | 959 | /* |
| 951 | Declare an imported function. | 960 | Declare an imported function. |
| 952 | When nargs is MANY (ptrdiff_t nargs, Lisp_Object *args) signature is assumed. | 961 | When nargs is MANY (ptrdiff_t nargs, Lisp_Object *args) signature is assumed. |
| @@ -1013,18 +1022,55 @@ static gcc_jit_rvalue * | |||
| 1013 | emit_call (Lisp_Object func, gcc_jit_type *ret_type, ptrdiff_t nargs, | 1022 | emit_call (Lisp_Object func, gcc_jit_type *ret_type, ptrdiff_t nargs, |
| 1014 | gcc_jit_rvalue **args, bool direct) | 1023 | gcc_jit_rvalue **args, bool direct) |
| 1015 | { | 1024 | { |
| 1016 | Lisp_Object gcc_func = | ||
| 1017 | Fgethash (func, | ||
| 1018 | direct ? comp.exported_funcs_h : comp.imported_funcs_h, | ||
| 1019 | Qnil); | ||
| 1020 | |||
| 1021 | if (NILP (gcc_func)) | ||
| 1022 | xsignal2 (Qnative_ice, | ||
| 1023 | build_string ("missing function declaration"), | ||
| 1024 | func); | ||
| 1025 | |||
| 1026 | if (direct) | 1025 | if (direct) |
| 1027 | { | 1026 | { |
| 1027 | Lisp_Object local_idx = Qnil; | ||
| 1028 | if (comp.func_speed == 2) | ||
| 1029 | local_idx = Fgethash (func, comp.local_func_reloc_idx_h, Qnil); | ||
| 1030 | if (!NILP (local_idx)) | ||
| 1031 | { | ||
| 1032 | USE_SAFE_ALLOCA; | ||
| 1033 | gcc_jit_type **types; | ||
| 1034 | SAFE_NALLOCA (types, 1, nargs); | ||
| 1035 | for (ptrdiff_t i = 0; i < nargs; ++i) | ||
| 1036 | types[i] = gcc_jit_rvalue_get_type (args[i]); | ||
| 1037 | |||
| 1038 | gcc_jit_type *f_ptr_type = | ||
| 1039 | gcc_jit_type_get_const ( | ||
| 1040 | gcc_jit_context_new_function_ptr_type (comp.ctxt, | ||
| 1041 | NULL, | ||
| 1042 | ret_type, | ||
| 1043 | nargs, | ||
| 1044 | types, | ||
| 1045 | 0)); | ||
| 1046 | gcc_jit_lvalue *f_ptr = | ||
| 1047 | gcc_jit_context_new_array_access ( | ||
| 1048 | comp.ctxt, | ||
| 1049 | NULL, | ||
| 1050 | gcc_jit_lvalue_as_rvalue (comp.local_func_relocs_local | ||
| 1051 | ? comp.local_func_relocs_local | ||
| 1052 | : comp.local_func_relocs), | ||
| 1053 | gcc_jit_context_new_rvalue_from_int (comp.ctxt, | ||
| 1054 | comp.ptrdiff_type, | ||
| 1055 | XFIXNUM (local_idx))); | ||
| 1056 | gcc_jit_rvalue *res = | ||
| 1057 | gcc_jit_context_new_call_through_ptr ( | ||
| 1058 | comp.ctxt, | ||
| 1059 | NULL, | ||
| 1060 | emit_coerce (f_ptr_type, gcc_jit_lvalue_as_rvalue (f_ptr)), | ||
| 1061 | nargs, | ||
| 1062 | args); | ||
| 1063 | SAFE_FREE (); | ||
| 1064 | emit_comment (format_string ("direct call via local reloc to: %s", | ||
| 1065 | SSDATA (func))); | ||
| 1066 | return res; | ||
| 1067 | } | ||
| 1068 | |||
| 1069 | Lisp_Object gcc_func = Fgethash (func, comp.exported_funcs_h, Qnil); | ||
| 1070 | if (NILP (gcc_func)) | ||
| 1071 | xsignal2 (Qnative_ice, | ||
| 1072 | build_string ("missing function declaration"), | ||
| 1073 | func); | ||
| 1028 | emit_comment (format_string ("direct call to: %s", | 1074 | emit_comment (format_string ("direct call to: %s", |
| 1029 | SSDATA (func))); | 1075 | SSDATA (func))); |
| 1030 | return gcc_jit_context_new_call (comp.ctxt, | 1076 | return gcc_jit_context_new_call (comp.ctxt, |
| @@ -1035,6 +1081,11 @@ emit_call (Lisp_Object func, gcc_jit_type *ret_type, ptrdiff_t nargs, | |||
| 1035 | } | 1081 | } |
| 1036 | else | 1082 | else |
| 1037 | { | 1083 | { |
| 1084 | Lisp_Object gcc_func = Fgethash (func, comp.imported_funcs_h, Qnil); | ||
| 1085 | if (NILP (gcc_func)) | ||
| 1086 | xsignal2 (Qnative_ice, | ||
| 1087 | build_string ("missing function declaration"), | ||
| 1088 | func); | ||
| 1038 | /* Inline functions so far don't have a local variable for | 1089 | /* Inline functions so far don't have a local variable for |
| 1039 | function reloc table so we fall back to the global one. Even | 1090 | function reloc table so we fall back to the global one. Even |
| 1040 | if this is not aesthetic calling into C from open-code is | 1091 | if this is not aesthetic calling into C from open-code is |
| @@ -2981,6 +3032,14 @@ emit_ctxt_code (void) | |||
| 2981 | emit_static_object (TEXT_FDOC_SYM, | 3032 | emit_static_object (TEXT_FDOC_SYM, |
| 2982 | CALLNI (comp-ctxt-function-docs, Vcomp_ctxt)); | 3033 | CALLNI (comp-ctxt-function-docs, Vcomp_ctxt)); |
| 2983 | 3034 | ||
| 3035 | Lisp_Object local_func_c_names | ||
| 3036 | = CALLNI (comp--cu-local-func-c-name-v, Vcomp_ctxt); | ||
| 3037 | emit_static_object (TEXT_LOCAL_FUNC_C_NAMES_SYM, local_func_c_names); | ||
| 3038 | CHECK_VECTOR (local_func_c_names); | ||
| 3039 | for (EMACS_INT i = 0; i < ASIZE (local_func_c_names); ++i) | ||
| 3040 | Fputhash (AREF (local_func_c_names, i), make_fixnum (i), | ||
| 3041 | comp.local_func_reloc_idx_h); | ||
| 3042 | |||
| 2984 | comp.current_thread_ref = | 3043 | comp.current_thread_ref = |
| 2985 | gcc_jit_lvalue_as_rvalue ( | 3044 | gcc_jit_lvalue_as_rvalue ( |
| 2986 | gcc_jit_context_new_global ( | 3045 | gcc_jit_context_new_global ( |
| @@ -2999,6 +3058,13 @@ emit_ctxt_code (void) | |||
| 2999 | comp.bool_ptr_type, | 3058 | comp.bool_ptr_type, |
| 3000 | F_SYMBOLS_WITH_POS_ENABLED_RELOC_SYM)); | 3059 | F_SYMBOLS_WITH_POS_ENABLED_RELOC_SYM)); |
| 3001 | 3060 | ||
| 3061 | comp.local_func_relocs = | ||
| 3062 | gcc_jit_context_new_global (comp.ctxt, | ||
| 3063 | NULL, | ||
| 3064 | GCC_JIT_GLOBAL_EXPORTED, | ||
| 3065 | gcc_jit_type_get_pointer (comp.void_ptr_type), | ||
| 3066 | LOCAL_FUNC_LINK_TABLE_SYM); | ||
| 3067 | |||
| 3002 | gcc_jit_context_new_global ( | 3068 | gcc_jit_context_new_global ( |
| 3003 | comp.ctxt, | 3069 | comp.ctxt, |
| 3004 | NULL, | 3070 | NULL, |
| @@ -4186,6 +4252,11 @@ compile_function (Lisp_Object func) | |||
| 4186 | NULL, | 4252 | NULL, |
| 4187 | comp.func_relocs_ptr_type, | 4253 | comp.func_relocs_ptr_type, |
| 4188 | "freloc"); | 4254 | "freloc"); |
| 4255 | comp.local_func_relocs_local = | ||
| 4256 | gcc_jit_function_new_local (comp.func, | ||
| 4257 | NULL, | ||
| 4258 | gcc_jit_type_get_pointer (comp.void_ptr_type), | ||
| 4259 | "local_freloc"); | ||
| 4189 | 4260 | ||
| 4190 | SAFE_NALLOCA (comp.frame, 1, comp.frame_size); | 4261 | SAFE_NALLOCA (comp.frame, 1, comp.frame_size); |
| 4191 | if (comp.func_has_non_local || !comp.func_speed) | 4262 | if (comp.func_has_non_local || !comp.func_speed) |
| @@ -4242,6 +4313,10 @@ compile_function (Lisp_Object func) | |||
| 4242 | NULL, | 4313 | NULL, |
| 4243 | comp.func_relocs_local, | 4314 | comp.func_relocs_local, |
| 4244 | gcc_jit_lvalue_as_rvalue (comp.func_relocs)); | 4315 | gcc_jit_lvalue_as_rvalue (comp.func_relocs)); |
| 4316 | gcc_jit_block_add_assignment (retrieve_block (Qentry), | ||
| 4317 | NULL, | ||
| 4318 | comp.local_func_relocs_local, | ||
| 4319 | gcc_jit_lvalue_as_rvalue (comp.local_func_relocs)); | ||
| 4245 | 4320 | ||
| 4246 | 4321 | ||
| 4247 | DOHASH (ht, block_name, block) | 4322 | DOHASH (ht, block_name, block) |
| @@ -4617,8 +4692,9 @@ Return t on success. */) | |||
| 4617 | /* | 4692 | /* |
| 4618 | Always reinitialize this cause old function definitions are garbage | 4693 | Always reinitialize this cause old function definitions are garbage |
| 4619 | collected by libgccjit when the ctxt is released. | 4694 | collected by libgccjit when the ctxt is released. |
| 4620 | */ | 4695 | */ |
| 4621 | comp.imported_funcs_h = Fmake_hash_table (0, NULL); | 4696 | comp.imported_funcs_h = Fmake_hash_table (0, NULL); |
| 4697 | comp.local_func_reloc_idx_h = CALLN (Fmake_hash_table, QCtest, Qequal); | ||
| 4622 | 4698 | ||
| 4623 | define_memcpy (); | 4699 | define_memcpy (); |
| 4624 | 4700 | ||
| @@ -4774,6 +4850,7 @@ DEFUN ("comp--compile-ctxt-to-file0", Fcomp__compile_ctxt_to_file0, | |||
| 4774 | Lisp_Object ebase_name = ENCODE_FILE (base_name); | 4850 | Lisp_Object ebase_name = ENCODE_FILE (base_name); |
| 4775 | 4851 | ||
| 4776 | comp.func_relocs_local = NULL; | 4852 | comp.func_relocs_local = NULL; |
| 4853 | comp.local_func_relocs_local = NULL; | ||
| 4777 | 4854 | ||
| 4778 | #ifdef WINDOWSNT | 4855 | #ifdef WINDOWSNT |
| 4779 | ebase_name = ansi_encode_filename (ebase_name); | 4856 | ebase_name = ansi_encode_filename (ebase_name); |
| @@ -5266,22 +5343,47 @@ load_comp_unit (struct Lisp_Native_Comp_Unit *comp_u, bool loading_dump, | |||
| 5266 | dynlib_sym (handle, F_SYMBOLS_WITH_POS_ENABLED_RELOC_SYM); | 5343 | dynlib_sym (handle, F_SYMBOLS_WITH_POS_ENABLED_RELOC_SYM); |
| 5267 | Lisp_Object *data_relocs = comp_u->data_relocs; | 5344 | Lisp_Object *data_relocs = comp_u->data_relocs; |
| 5268 | void **freloc_link_table = dynlib_sym (handle, FUNC_LINK_TABLE_SYM); | 5345 | void **freloc_link_table = dynlib_sym (handle, FUNC_LINK_TABLE_SYM); |
| 5346 | void ***local_freloc_link_table | ||
| 5347 | = dynlib_sym (handle, LOCAL_FUNC_LINK_TABLE_SYM); | ||
| 5348 | Lisp_Object local_func_c_names = | ||
| 5349 | load_static_obj (comp_u, TEXT_LOCAL_FUNC_C_NAMES_SYM); | ||
| 5269 | 5350 | ||
| 5270 | if (!(current_thread_reloc | 5351 | if (!(current_thread_reloc |
| 5271 | && f_symbols_with_pos_enabled_reloc | 5352 | && f_symbols_with_pos_enabled_reloc |
| 5272 | && data_relocs | 5353 | && data_relocs |
| 5273 | && data_eph_relocs | 5354 | && data_eph_relocs |
| 5274 | && freloc_link_table | 5355 | && freloc_link_table |
| 5356 | && local_freloc_link_table | ||
| 5275 | && top_level_run) | 5357 | && top_level_run) |
| 5276 | || NILP (Fstring_equal (load_static_obj (comp_u, LINK_TABLE_HASH_SYM), | 5358 | || NILP (Fstring_equal (load_static_obj (comp_u, LINK_TABLE_HASH_SYM), |
| 5277 | Vcomp_abi_hash))) | 5359 | Vcomp_abi_hash))) |
| 5278 | xsignal1 (Qnative_lisp_file_inconsistent, comp_u->file); | 5360 | xsignal1 (Qnative_lisp_file_inconsistent, comp_u->file); |
| 5361 | if (!VECTORP (local_func_c_names)) | ||
| 5362 | xsignal2 (Qnative_lisp_file_inconsistent, comp_u->file, | ||
| 5363 | build_string ("missing local function relocation vector")); | ||
| 5279 | 5364 | ||
| 5280 | *current_thread_reloc = ¤t_thread; | 5365 | *current_thread_reloc = ¤t_thread; |
| 5281 | *f_symbols_with_pos_enabled_reloc = &symbols_with_pos_enabled; | 5366 | *f_symbols_with_pos_enabled_reloc = &symbols_with_pos_enabled; |
| 5282 | 5367 | ||
| 5283 | /* Imported functions. */ | 5368 | /* Imported functions. */ |
| 5284 | *freloc_link_table = freloc.link_table; | 5369 | *freloc_link_table = freloc.link_table; |
| 5370 | ptrdiff_t n_local_frelocs = ASIZE (local_func_c_names); | ||
| 5371 | comp_u->local_func_relocs = | ||
| 5372 | n_local_frelocs | ||
| 5373 | ? xnmalloc (n_local_frelocs, sizeof (*comp_u->local_func_relocs)) | ||
| 5374 | : NULL; | ||
| 5375 | *local_freloc_link_table = comp_u->local_func_relocs; | ||
| 5376 | for (ptrdiff_t i = 0; i < n_local_frelocs; ++i) | ||
| 5377 | { | ||
| 5378 | Lisp_Object c_name = AREF (local_func_c_names, i); | ||
| 5379 | if (!STRINGP (c_name)) | ||
| 5380 | xsignal2 (Qnative_lisp_file_inconsistent, comp_u->file, | ||
| 5381 | build_string ("invalid local function relocation name")); | ||
| 5382 | void *func = dynlib_sym (handle, SSDATA (c_name)); | ||
| 5383 | if (!func) | ||
| 5384 | xsignal2 (Qnative_lisp_file_inconsistent, comp_u->file, c_name); | ||
| 5385 | comp_u->local_func_relocs[i] = func; | ||
| 5386 | } | ||
| 5285 | 5387 | ||
| 5286 | /* Imported data. */ | 5388 | /* Imported data. */ |
| 5287 | if (!loading_dump) | 5389 | if (!loading_dump) |
| @@ -5351,6 +5453,8 @@ unload_comp_unit (struct Lisp_Native_Comp_Unit *cu) | |||
| 5351 | if (EQ (this_cu, *saved_cu)) | 5453 | if (EQ (this_cu, *saved_cu)) |
| 5352 | *saved_cu = Qnil; | 5454 | *saved_cu = Qnil; |
| 5353 | dynlib_close (cu->handle); | 5455 | dynlib_close (cu->handle); |
| 5456 | xfree (cu->local_func_relocs); | ||
| 5457 | cu->local_func_relocs = NULL; | ||
| 5354 | } | 5458 | } |
| 5355 | 5459 | ||
| 5356 | Lisp_Object | 5460 | Lisp_Object |
| @@ -5370,6 +5474,70 @@ native_function_doc (Lisp_Object function) | |||
| 5370 | return make_fixnum (doc); | 5474 | return make_fixnum (doc); |
| 5371 | } | 5475 | } |
| 5372 | 5476 | ||
| 5477 | static ptrdiff_t | ||
| 5478 | find_comp_unit_local_func_reloc_idx (struct Lisp_Native_Comp_Unit *cu, | ||
| 5479 | const char *c_name) | ||
| 5480 | { | ||
| 5481 | Lisp_Object names = load_static_obj (cu, TEXT_LOCAL_FUNC_C_NAMES_SYM); | ||
| 5482 | if (!VECTORP (names)) | ||
| 5483 | xsignal2 (Qnative_lisp_file_inconsistent, cu->file, | ||
| 5484 | build_string ("missing local function relocation vector")); | ||
| 5485 | |||
| 5486 | Lisp_Object target = build_string (c_name); | ||
| 5487 | ptrdiff_t len = ASIZE (names); | ||
| 5488 | for (ptrdiff_t i = 0; i < len; ++i) | ||
| 5489 | if (!NILP (Fstring_equal (AREF (names, i), target))) | ||
| 5490 | return i; | ||
| 5491 | |||
| 5492 | return -1; | ||
| 5493 | } | ||
| 5494 | |||
| 5495 | bool | ||
| 5496 | native_comp_local_function_p (Lisp_Object function) | ||
| 5497 | { | ||
| 5498 | if (!NATIVE_COMP_FUNCTIONP (function)) | ||
| 5499 | return false; | ||
| 5500 | |||
| 5501 | struct Lisp_Native_Comp_Unit *cu = | ||
| 5502 | XNATIVE_COMP_UNIT (Fsubr_native_comp_unit (function)); | ||
| 5503 | |||
| 5504 | return (cu->local_func_relocs | ||
| 5505 | && find_comp_unit_local_func_reloc_idx ( | ||
| 5506 | cu, XSUBR (function)->native_c_name) >= 0); | ||
| 5507 | } | ||
| 5508 | |||
| 5509 | DEFUN ("comp--install-local-function-trampoline", | ||
| 5510 | Fcomp__install_local_function_trampoline, | ||
| 5511 | Scomp__install_local_function_trampoline, 2, 2, 0, | ||
| 5512 | doc: /* Install TRAMPOLINE for speed-2 local native-compiled FUNCTION. */) | ||
| 5513 | (Lisp_Object function, Lisp_Object trampoline) | ||
| 5514 | { | ||
| 5515 | CHECK_SUBR (function); | ||
| 5516 | CHECK_SUBR (trampoline); | ||
| 5517 | CHECK_TYPE (NATIVE_COMP_FUNCTIONP (function), Qnative_comp_function, | ||
| 5518 | function); | ||
| 5519 | |||
| 5520 | if (will_dump_p ()) | ||
| 5521 | signal_error ("Trying to advice unexpected native function before dumping", | ||
| 5522 | function); | ||
| 5523 | |||
| 5524 | struct Lisp_Native_Comp_Unit *cu = | ||
| 5525 | XNATIVE_COMP_UNIT (Fsubr_native_comp_unit (function)); | ||
| 5526 | if (!cu->local_func_relocs) | ||
| 5527 | signal_error ("Trying to install trampoline for unloaded compilation unit", | ||
| 5528 | function); | ||
| 5529 | |||
| 5530 | ptrdiff_t idx = find_comp_unit_local_func_reloc_idx ( | ||
| 5531 | cu, XSUBR (function)->native_c_name); | ||
| 5532 | if (idx < 0) | ||
| 5533 | signal_error ("Trying to install trampoline for non existent local native function", | ||
| 5534 | function); | ||
| 5535 | |||
| 5536 | cu->local_func_relocs[idx] = XSUBR (trampoline)->function.a0; | ||
| 5537 | Fputhash (trampoline, Qt, cu->lambda_gc_guard_h); | ||
| 5538 | return Qt; | ||
| 5539 | } | ||
| 5540 | |||
| 5373 | static Lisp_Object | 5541 | static Lisp_Object |
| 5374 | make_subr (Lisp_Object symbol_name, Lisp_Object minarg, Lisp_Object maxarg, | 5542 | make_subr (Lisp_Object symbol_name, Lisp_Object minarg, Lisp_Object maxarg, |
| 5375 | Lisp_Object c_name, Lisp_Object type, Lisp_Object doc_idx, | 5543 | Lisp_Object c_name, Lisp_Object type, Lisp_Object doc_idx, |
| @@ -5697,6 +5865,7 @@ natively compiled one. */); | |||
| 5697 | defsubr (&Scomp_native_driver_options_effective_p); | 5865 | defsubr (&Scomp_native_driver_options_effective_p); |
| 5698 | defsubr (&Scomp_native_compiler_options_effective_p); | 5866 | defsubr (&Scomp_native_compiler_options_effective_p); |
| 5699 | defsubr (&Scomp__install_trampoline); | 5867 | defsubr (&Scomp__install_trampoline); |
| 5868 | defsubr (&Scomp__install_local_function_trampoline); | ||
| 5700 | defsubr (&Scomp__init_ctxt); | 5869 | defsubr (&Scomp__init_ctxt); |
| 5701 | defsubr (&Scomp__release_ctxt); | 5870 | defsubr (&Scomp__release_ctxt); |
| 5702 | defsubr (&Scomp__compile_ctxt_to_file0); | 5871 | defsubr (&Scomp__compile_ctxt_to_file0); |
| @@ -5710,6 +5879,8 @@ natively compiled one. */); | |||
| 5710 | comp.exported_funcs_h = Qnil; | 5879 | comp.exported_funcs_h = Qnil; |
| 5711 | staticpro (&comp.imported_funcs_h); | 5880 | staticpro (&comp.imported_funcs_h); |
| 5712 | comp.imported_funcs_h = Qnil; | 5881 | comp.imported_funcs_h = Qnil; |
| 5882 | staticpro (&comp.local_func_reloc_idx_h); | ||
| 5883 | comp.local_func_reloc_idx_h = Qnil; | ||
| 5713 | staticpro (&comp.func_blocks_h); | 5884 | staticpro (&comp.func_blocks_h); |
| 5714 | staticpro (&comp.emitter_dispatcher); | 5885 | staticpro (&comp.emitter_dispatcher); |
| 5715 | comp.emitter_dispatcher = Qnil; | 5886 | comp.emitter_dispatcher = Qnil; |
| @@ -5759,14 +5930,15 @@ Emacs. */); | |||
| 5759 | 5930 | ||
| 5760 | DEFVAR_LISP ("native-comp-enable-subr-trampolines", | 5931 | DEFVAR_LISP ("native-comp-enable-subr-trampolines", |
| 5761 | Vnative_comp_enable_subr_trampolines, | 5932 | Vnative_comp_enable_subr_trampolines, |
| 5762 | doc: /* If non-nil, enable generation of trampolines for calling primitives. | 5933 | doc: /* If non-nil, enable generation of trampolines for optimized calls. |
| 5763 | Trampolines are needed so that Emacs respects redefinition or advice of | 5934 | Trampolines are needed so that Emacs respects redefinition or advice of |
| 5764 | primitive functions when they are called from native-compiled Lisp code | 5935 | primitive functions, and redefinition of named native-compiled |
| 5765 | at `native-comp-speed' of 2. | 5936 | functions inside the same compilation unit, when these calls are |
| 5937 | optimized by native compilation at speed 2. | ||
| 5766 | 5938 | ||
| 5767 | By default, the value is t, and when Emacs sees a redefined or advised | 5939 | By default, the value is t, and when Emacs sees a redefined or advised |
| 5768 | primitive called from native-compiled Lisp, it generates a trampoline | 5940 | optimized function called from native-compiled Lisp, it generates a |
| 5769 | for it on-the-fly. | 5941 | trampoline for it on-the-fly. |
| 5770 | 5942 | ||
| 5771 | If the value is a file name (a string), it specifies the directory in | 5943 | If the value is a file name (a string), it specifies the directory in |
| 5772 | which to deposit the generated trampolines, overriding the directories | 5944 | which to deposit the generated trampolines, overriding the directories |
| @@ -5775,12 +5947,9 @@ in `native-comp-eln-load-path'. | |||
| 5775 | When this variable is nil, generation of trampolines is disabled. | 5947 | When this variable is nil, generation of trampolines is disabled. |
| 5776 | 5948 | ||
| 5777 | Disabling the generation of trampolines, when a trampoline for a redefined | 5949 | Disabling the generation of trampolines, when a trampoline for a redefined |
| 5778 | or advised primitive is not already available from previous compilations, | 5950 | or advised optimized function is not already available, means that such |
| 5779 | means that such redefinition or advice will not have effect when calling | 5951 | redefinition or advice will not have effect when calling that function |
| 5780 | primitives from native-compiled Lisp code. That is, calls to primitives | 5952 | from native-compiled Lisp code. */); |
| 5781 | without existing trampolines from native-compiled Lisp will behave as if | ||
| 5782 | the primitive was called directly from C, and will ignore its redefinition | ||
| 5783 | and advice. */); | ||
| 5784 | 5953 | ||
| 5785 | DEFVAR_LISP ("comp-installed-trampolines-h", Vcomp_installed_trampolines_h, | 5954 | DEFVAR_LISP ("comp-installed-trampolines-h", Vcomp_installed_trampolines_h, |
| 5786 | doc: /* Hash table subr-name -> installed trampoline. | 5955 | doc: /* Hash table subr-name -> installed trampoline. |
diff --git a/src/comp.h b/src/comp.h index 16f2aab7b9a..5b4fec9d132 100644 --- a/src/comp.h +++ b/src/comp.h | |||
| @@ -44,6 +44,7 @@ struct Lisp_Native_Comp_Unit | |||
| 44 | Lisp_Object data_vec; | 44 | Lisp_Object data_vec; |
| 45 | /* STUFFS WE DO NOT DUMP!! */ | 45 | /* STUFFS WE DO NOT DUMP!! */ |
| 46 | Lisp_Object *data_relocs; | 46 | Lisp_Object *data_relocs; |
| 47 | void **local_func_relocs; | ||
| 47 | bool loaded_once; | 48 | bool loaded_once; |
| 48 | bool load_ongoing; | 49 | bool load_ongoing; |
| 49 | dynlib_handle_ptr handle; | 50 | dynlib_handle_ptr handle; |
| @@ -75,6 +76,8 @@ extern Lisp_Object load_comp_unit (struct Lisp_Native_Comp_Unit *comp_u, | |||
| 75 | 76 | ||
| 76 | extern void unload_comp_unit (struct Lisp_Native_Comp_Unit *); | 77 | extern void unload_comp_unit (struct Lisp_Native_Comp_Unit *); |
| 77 | 78 | ||
| 79 | extern bool native_comp_local_function_p (Lisp_Object function); | ||
| 80 | |||
| 78 | extern Lisp_Object native_function_doc (Lisp_Object function); | 81 | extern Lisp_Object native_function_doc (Lisp_Object function); |
| 79 | 82 | ||
| 80 | extern void syms_of_comp (void); | 83 | extern void syms_of_comp (void); |
| @@ -97,6 +100,13 @@ static inline | |||
| 97 | void unload_comp_unit (struct Lisp_Native_Comp_Unit *cu) | 100 | void unload_comp_unit (struct Lisp_Native_Comp_Unit *cu) |
| 98 | {} | 101 | {} |
| 99 | 102 | ||
| 103 | static inline bool | ||
| 104 | native_comp_local_function_p (Lisp_Object function) | ||
| 105 | { | ||
| 106 | (void) function; | ||
| 107 | return false; | ||
| 108 | } | ||
| 109 | |||
| 100 | extern void syms_of_comp (void); | 110 | extern void syms_of_comp (void); |
| 101 | 111 | ||
| 102 | INLINE_HEADER_END | 112 | INLINE_HEADER_END |
diff --git a/src/data.c b/src/data.c index 4973d577c1c..2360033bb6b 100644 --- a/src/data.c +++ b/src/data.c | |||
| @@ -912,10 +912,24 @@ signal a `cyclic-function-indirection' error. */) | |||
| 912 | register Lisp_Object function = XSYMBOL (symbol)->u.s.function; | 912 | register Lisp_Object function = XSYMBOL (symbol)->u.s.function; |
| 913 | 913 | ||
| 914 | if (!NILP (Vnative_comp_enable_subr_trampolines) | 914 | if (!NILP (Vnative_comp_enable_subr_trampolines) |
| 915 | && SUBRP (function) | ||
| 916 | && !NATIVE_COMP_FUNCTIONP (function) | ||
| 917 | && !EQ (definition, Fsymbol_function (symbol))) | 915 | && !EQ (definition, Fsymbol_function (symbol))) |
| 918 | calln (Qcomp_subr_trampoline_install, symbol); | 916 | { |
| 917 | if (SUBRP (function) && !NATIVE_COMP_FUNCTIONP (function)) | ||
| 918 | calln (Qcomp_subr_trampoline_install, symbol); | ||
| 919 | else if (NATIVE_COMP_FUNCTIONP (function)) | ||
| 920 | { | ||
| 921 | if (!EQ (symbol, intern_c_string ("--anonymous-lambda")) | ||
| 922 | && native_comp_local_function_p (function) | ||
| 923 | && !(NATIVE_COMP_FUNCTIONP (definition) | ||
| 924 | && EQ (Fsubr_native_comp_unit (function), | ||
| 925 | Fsubr_native_comp_unit (definition)))) | ||
| 926 | { | ||
| 927 | calln (intern_c_string ("require"), intern_c_string ("comp-run")); | ||
| 928 | calln (intern_c_string ("comp-local-function-trampoline-install"), | ||
| 929 | symbol, function); | ||
| 930 | } | ||
| 931 | } | ||
| 932 | } | ||
| 919 | #endif | 933 | #endif |
| 920 | 934 | ||
| 921 | set_symbol_function (symbol, definition); | 935 | set_symbol_function (symbol, definition); |
diff --git a/src/pdumper.c b/src/pdumper.c index c21af24d9f1..7108aa64788 100644 --- a/src/pdumper.c +++ b/src/pdumper.c | |||
| @@ -2960,6 +2960,7 @@ dump_native_comp_unit (struct dump_context *ctx, | |||
| 2960 | START_DUMP_PVEC (ctx, &comp_u->header, struct Lisp_Native_Comp_Unit, out); | 2960 | START_DUMP_PVEC (ctx, &comp_u->header, struct Lisp_Native_Comp_Unit, out); |
| 2961 | dump_pseudovector_lisp_fields (ctx, &out->header, &comp_u->header); | 2961 | dump_pseudovector_lisp_fields (ctx, &out->header, &comp_u->header); |
| 2962 | out->handle = NULL; | 2962 | out->handle = NULL; |
| 2963 | out->local_func_relocs = NULL; | ||
| 2963 | 2964 | ||
| 2964 | dump_off comp_u_off = finish_dump_pvec (ctx, &out->header); | 2965 | dump_off comp_u_off = finish_dump_pvec (ctx, &out->header); |
| 2965 | if (ctx->flags.dump_object_contents) | 2966 | if (ctx->flags.dump_object_contents) |
diff --git a/test/src/comp-tests.el b/test/src/comp-tests.el index e322bdb057e..32d5859562f 100644 --- a/test/src/comp-tests.el +++ b/test/src/comp-tests.el | |||
| @@ -1648,6 +1648,63 @@ folded." | |||
| 1648 | (or (comp-tests-mentioned-p 'direct-call insn) | 1648 | (or (comp-tests-mentioned-p 'direct-call insn) |
| 1649 | (comp-tests-mentioned-p 'direct-callref insn)))))) | 1649 | (comp-tests-mentioned-p 'direct-callref insn)))))) |
| 1650 | 1650 | ||
| 1651 | (defun comp-tests--unbind-direct-call-functions () | ||
| 1652 | "Clear shared definitions used by the direct-call fixture." | ||
| 1653 | (dolist (sym '(comp-tests-direct-call-caller-f | ||
| 1654 | comp-tests-direct-call-callee-f)) | ||
| 1655 | (when (fboundp sym) | ||
| 1656 | (fmakunbound sym)))) | ||
| 1657 | |||
| 1658 | (defun comp-tests--run-in-sub-emacs (form) | ||
| 1659 | "Run FORM in a fresh batch Emacs and return (STATUS . OUTPUT)." | ||
| 1660 | (let* ((default-directory (expand-file-name ".." invocation-directory)) | ||
| 1661 | (emacs (expand-file-name invocation-name invocation-directory)) | ||
| 1662 | (buf (generate-new-buffer " *comp-sub-emacs*"))) | ||
| 1663 | (unwind-protect | ||
| 1664 | (cons | ||
| 1665 | (call-process emacs nil buf nil | ||
| 1666 | "--batch" "--no-init-file" "--no-site-file" | ||
| 1667 | "--no-site-lisp" | ||
| 1668 | "--eval" "(setq native-comp-eln-load-path (list temporary-file-directory))" | ||
| 1669 | "-L" "test" "-l" "ert" | ||
| 1670 | "-l" "test/src/comp-tests.el" | ||
| 1671 | "--eval" (prin1-to-string form)) | ||
| 1672 | (with-current-buffer buf | ||
| 1673 | (buffer-string))) | ||
| 1674 | (kill-buffer buf)))) | ||
| 1675 | |||
| 1676 | (defun comp-tests--direct-call-redefinition-form (speed expected-first expected-second) | ||
| 1677 | "Return a form checking direct-call redefinition at SPEED. | ||
| 1678 | The caller should produce EXPECTED-FIRST and EXPECTED-SECOND after | ||
| 1679 | successive callee redefinitions." | ||
| 1680 | `(let* ((native-comp-speed ,speed) | ||
| 1681 | (native-comp-eln-load-path (list temporary-file-directory)) | ||
| 1682 | (source ,(ert-resource-file "comp-test-direct-call.el")) | ||
| 1683 | (output (make-temp-file ,(format "comp-test-direct-call-speed%d-" speed) | ||
| 1684 | nil ".eln"))) | ||
| 1685 | (comp-tests--unbind-direct-call-functions) | ||
| 1686 | (delete-file output) | ||
| 1687 | (let ((comp-post-pass-hooks | ||
| 1688 | '((comp--final | ||
| 1689 | (lambda (_) | ||
| 1690 | (unless (comp-tests-has-direct-call-p | ||
| 1691 | 'comp-tests-direct-call-caller-f) | ||
| 1692 | (error "missing direct call optimization"))))))) | ||
| 1693 | (native-compile source output)) | ||
| 1694 | (load output) | ||
| 1695 | (let ((orig (symbol-function 'comp-tests-direct-call-callee-f))) | ||
| 1696 | (unwind-protect | ||
| 1697 | (progn | ||
| 1698 | (fset 'comp-tests-direct-call-callee-f | ||
| 1699 | (lambda (x) (+ x 100))) | ||
| 1700 | (unless (= (comp-tests-direct-call-caller-f 3) ,expected-first) | ||
| 1701 | (error "unexpected first result at speed %d" ,speed)) | ||
| 1702 | (fset 'comp-tests-direct-call-callee-f | ||
| 1703 | (lambda (x) (+ x 200))) | ||
| 1704 | (unless (= (comp-tests-direct-call-caller-f 3) ,expected-second) | ||
| 1705 | (error "unexpected second result at speed %d" ,speed))) | ||
| 1706 | (fset 'comp-tests-direct-call-callee-f orig))))) | ||
| 1707 | |||
| 1651 | (comp-deftest direct-call-with-lambdas () | 1708 | (comp-deftest direct-call-with-lambdas () |
| 1652 | "Check that anonymous lambdas don't prevent direct calls at speed 3. | 1709 | "Check that anonymous lambdas don't prevent direct calls at speed 3. |
| 1653 | See `comp--func-unique-in-cu-p'." | 1710 | See `comp--func-unique-in-cu-p'." |
| @@ -1657,13 +1714,37 @@ See `comp--func-unique-in-cu-p'." | |||
| 1657 | (lambda (_) | 1714 | (lambda (_) |
| 1658 | (should (comp-tests-has-direct-call-p | 1715 | (should (comp-tests-has-direct-call-p |
| 1659 | 'comp-tests-direct-call-caller-f))))))) | 1716 | 'comp-tests-direct-call-caller-f))))))) |
| 1660 | (load (native-compile | 1717 | (let* ((source (ert-resource-file "comp-test-direct-call.el")) |
| 1661 | (ert-resource-file "comp-test-direct-call.el"))) | 1718 | (output (make-temp-file "comp-test-direct-call-lambdas-" nil ".eln"))) |
| 1719 | (comp-tests--unbind-direct-call-functions) | ||
| 1720 | (delete-file output) | ||
| 1721 | (native-compile source output) | ||
| 1722 | (load output)) | ||
| 1662 | (declare-function comp-tests-direct-call-caller-f nil) | 1723 | (declare-function comp-tests-direct-call-caller-f nil) |
| 1663 | (should (native-comp-function-p | 1724 | (should (native-comp-function-p |
| 1664 | (symbol-function 'comp-tests-direct-call-caller-f))) | 1725 | (symbol-function 'comp-tests-direct-call-caller-f))) |
| 1665 | (should (= (comp-tests-direct-call-caller-f 3) 4)))) | 1726 | (should (= (comp-tests-direct-call-caller-f 3) 4)))) |
| 1666 | 1727 | ||
| 1728 | (comp-deftest anonymous-lambda-recompile () | ||
| 1729 | "Check that recompiling standalone lambdas does not recurse via `fset'." | ||
| 1730 | (let ((f1 (native-compile '(lambda () 1))) | ||
| 1731 | (f2 (native-compile '(lambda () 2)))) | ||
| 1732 | (should (native-comp-function-p f1)) | ||
| 1733 | (should (native-comp-function-p f2)) | ||
| 1734 | (should (= (funcall f1) 1)) | ||
| 1735 | (should (= (funcall f2) 2)))) | ||
| 1736 | |||
| 1737 | (comp-deftest direct-call-redefinition-speed-split () | ||
| 1738 | "Check speed-2 and speed-3 redefinition behavior for named direct calls." | ||
| 1739 | (dolist (case '((2 103 203) (3 4 4))) | ||
| 1740 | (pcase-let* ((`(,speed ,expected-first ,expected-second) case) | ||
| 1741 | (`(,status . ,output) | ||
| 1742 | (comp-tests--run-in-sub-emacs | ||
| 1743 | (comp-tests--direct-call-redefinition-form | ||
| 1744 | speed expected-first expected-second)))) | ||
| 1745 | (ert-info ((format "speed %d subprocess output:\n%s" speed output)) | ||
| 1746 | (should (zerop status)))))) | ||
| 1747 | |||
| 1667 | (comp-deftest direct-call-with-duplicate-names () | 1748 | (comp-deftest direct-call-with-duplicate-names () |
| 1668 | "Check that duplicate names only block their own direct calls. | 1749 | "Check that duplicate names only block their own direct calls. |
| 1669 | See `comp--func-unique-in-cu-p'." | 1750 | See `comp--func-unique-in-cu-p'." |