]> git.eshelyaron.com Git - emacs.git/commitdiff
Faster, more compact, and readable closure creation
authorMattias Engdegård <mattiase@acm.org>
Sun, 21 Feb 2021 14:24:41 +0000 (15:24 +0100)
committerMattias Engdegård <mattiase@acm.org>
Sun, 21 Feb 2021 20:58:25 +0000 (21:58 +0100)
Simplify closure creation by calling a single function at run time
instead of putting it together from small pieces.  This is faster
(by about a factor 2), takes less space on disk and in memory, and
makes internal functions somewhat readable in disassembly listings again.

This is done by creating a prototype function at compile-time whose
closure variables are placeholder values V0, V1... which can be seen
in the disassembly.  The prototype is then cloned at run time using
the new make-closure function that replaces the placeholders with
the actual closure variables.

* lisp/emacs-lisp/bytecomp.el (byte-compile-make-closure):
Generate call to make-closure from a prototype function.
* src/alloc.c (Fmake_closure): New function.
(syms_of_alloc): Defsubr it.
* src/data.c (syms_of_data): Defsym byte-code-function-p.

lisp/emacs-lisp/bytecomp.el
src/alloc.c
src/data.c

index 1b0906b50bb9272ec2eed7bd35e032cd15d584b1..69a63b169cc86171d6f2b3cb02d7687fc36a0681 100644 (file)
@@ -3817,15 +3817,21 @@ discarding."
       (cl-assert (or (> (length env) 0)
                     docstring-exp))    ;Otherwise, we don't need a closure.
       (cl-assert (byte-code-function-p fun))
-      (byte-compile-form `(make-byte-code
-                           ',(aref fun 0) ',(aref fun 1)
-                           (vconcat (vector . ,env) ',(aref fun 2))
-                           ,@(let ((rest (nthcdr 3 (mapcar (lambda (x) `',x) fun))))
-                               (if docstring-exp
-                                   `(,(car rest)
-                                     ,docstring-exp
-                                     ,@(cddr rest))
-                                 rest)))))))
+      (byte-compile-form
+       ;; Use symbols V0, V1 ... as placeholders for closure variables:
+       ;; they should be short (to save space in the .elc file), yet
+       ;; distinct when disassembled.
+       (let* ((dummy-vars (mapcar (lambda (i) (intern (format "V%d" i)))
+                                  (number-sequence 0 (1- (length env)))))
+              (proto-fun
+               (apply #'make-byte-code
+                      (aref fun 0) (aref fun 1)
+                      ;; Prepend dummy cells to the constant vector,
+                      ;; to get the indices right when disassembling.
+                      (vconcat dummy-vars (aref fun 2))
+                      (mapcar (lambda (i) (aref fun i))
+                              (number-sequence 3 (1- (length fun)))))))
+         `(make-closure ,proto-fun ,@env))))))
 
 (defun byte-compile-get-closed-var (form)
   "Byte-compile the special `internal-get-closed-var' form."
index b86ed4ed26213bc934b7dba9f2c5490afa81a33c..e72fc4c4332dee41aa512060df79d49a0641f540 100644 (file)
@@ -3498,6 +3498,38 @@ usage: (make-byte-code ARGLIST BYTE-CODE CONSTANTS DEPTH &optional DOCSTRING INT
   return val;
 }
 
+DEFUN ("make-closure", Fmake_closure, Smake_closure, 1, MANY, 0,
+       doc: /* Create a byte-code closure from PROTOTYPE and CLOSURE-VARS.
+Return a copy of PROTOTYPE, a byte-code object, with CLOSURE-VARS
+replacing the elements in the beginning of the constant-vector.
+usage: (make-closure PROTOTYPE &rest CLOSURE-VARS) */)
+  (ptrdiff_t nargs, Lisp_Object *args)
+{
+  Lisp_Object protofun = args[0];
+  CHECK_TYPE (COMPILEDP (protofun), Qbyte_code_function_p, protofun);
+
+  /* Create a copy of the constant vector, filling it with the closure
+     variables in the beginning.  (The overwritten part should just
+     contain placeholder values.) */
+  Lisp_Object proto_constvec = AREF (protofun, COMPILED_CONSTANTS);
+  ptrdiff_t constsize = ASIZE (proto_constvec);
+  ptrdiff_t nvars = nargs - 1;
+  if (nvars > constsize)
+    error ("Closure vars do not fit in constvec");
+  Lisp_Object constvec = make_uninit_vector (constsize);
+  memcpy (XVECTOR (constvec)->contents, args + 1, nvars * word_size);
+  memcpy (XVECTOR (constvec)->contents + nvars,
+         XVECTOR (proto_constvec)->contents + nvars,
+         (constsize - nvars) * word_size);
+
+  /* Return a copy of the prototype function with the new constant vector. */
+  ptrdiff_t protosize = PVSIZE (protofun);
+  struct Lisp_Vector *v = allocate_vectorlike (protosize, false);
+  v->header = XVECTOR (protofun)->header;
+  memcpy (v->contents, XVECTOR (protofun)->contents, protosize * word_size);
+  v->contents[COMPILED_CONSTANTS] = constvec;
+  return make_lisp_ptr (v, Lisp_Vectorlike);
+}
 
 \f
 /***********************************************************************
@@ -7573,6 +7605,7 @@ N should be nonnegative.  */);
   defsubr (&Srecord);
   defsubr (&Sbool_vector);
   defsubr (&Smake_byte_code);
+  defsubr (&Smake_closure);
   defsubr (&Smake_list);
   defsubr (&Smake_vector);
   defsubr (&Smake_record);
index 9af9131b123bf0b2a1305fec611e08651ae242c3..0fa491b17a114bd745664bd944fa6dc108e69725 100644 (file)
@@ -3989,6 +3989,8 @@ syms_of_data (void)
   DEFSYM (Qinteractive_form, "interactive-form");
   DEFSYM (Qdefalias_fset_function, "defalias-fset-function");
 
+  DEFSYM (Qbyte_code_function_p, "byte-code-function-p");
+
   defsubr (&Sindirect_variable);
   defsubr (&Sinteractive_form);
   defsubr (&Scommand_modes);