]> git.eshelyaron.com Git - emacs.git/commitdiff
Optimise let and let* whose body is constant or the last variable
authorMattias Engdegård <mattiase@acm.org>
Thu, 29 Jul 2021 13:35:55 +0000 (15:35 +0200)
committerMattias Engdegård <mattiase@acm.org>
Fri, 30 Jul 2021 07:54:30 +0000 (09:54 +0200)
Simplify  (let ((X1 E1) ... (Xn En)) Xn)
      =>  (progn E1 ... En)

and       (let* ((X1 E1) ... (Xn En)) Xn)
      =>  (let* ((X1 E1) ... (Xn-1 En-1)) En)

and similarly the case where the body is a constant, extending a
previous optimisation that only applied to the constant nil.
This reduces the number of bound variables, shortens the code, and
enables further optimisations.

* lisp/emacs-lisp/byte-opt.el (byte-optimize-letX): Rewrite using
`pcase` and add the aforementioned transformations.
* test/lisp/emacs-lisp/bytecomp-tests.el (bytecomp-tests--test-cases):
Add test cases.

lisp/emacs-lisp/byte-opt.el
test/lisp/emacs-lisp/bytecomp-tests.el

index c9dfa69aeb23f69e3d19fad28c263313adda1c01..d444d7006eb6ff8fe883d80c7328bacf08de341e 100644 (file)
@@ -1250,18 +1250,31 @@ See Info node `(elisp) Integer Basics'."
 (put 'let 'byte-optimizer #'byte-optimize-letX)
 (put 'let* 'byte-optimizer #'byte-optimize-letX)
 (defun byte-optimize-letX (form)
-  (cond ((null (nth 1 form))
-        ;; No bindings
-        (cons 'progn (cdr (cdr form))))
-       ((or (nth 2 form) (nthcdr 3 form))
-        form)
-        ;; The body is nil
-       ((eq (car form) 'let)
-        (append '(progn) (mapcar 'car-safe (mapcar 'cdr-safe (nth 1 form)))
-                '(nil)))
-       (t
-        (let ((binds (reverse (nth 1 form))))
-          (list 'let* (reverse (cdr binds)) (nth 1 (car binds)) nil)))))
+  (pcase form
+    ;; No bindings.
+    (`(,_ () . ,body)
+     `(progn . ,body))
+
+    ;; Body is empty or just contains a constant.
+    (`(,head ,bindings . ,(or '() `(,(and const (pred macroexp-const-p)))))
+     (if (eq head 'let)
+         `(progn ,@(mapcar (lambda (binding)
+                             (and (consp binding) (cadr binding)))
+                           bindings)
+                 ,const)
+       `(let* ,(butlast bindings) ,(cadar (last bindings)) ,const)))
+
+    ;; Body is last variable.
+    (`(,head ,bindings ,(and var (pred symbolp) (pred (not keywordp))
+                             (pred (not booleanp))
+                             (guard (eq var (caar (last bindings))))))
+     (if (eq head 'let)
+         `(progn ,@(mapcar (lambda (binding)
+                             (and (consp binding) (cadr binding)))
+                           bindings))
+       `(let* ,(butlast bindings) ,(cadar (last bindings)))))
+
+    (_ form)))
 
 
 (put 'nth 'byte-optimizer #'byte-optimize-nth)
index ee0f931c192ff72ae1d2b141d8276848b9f9a863..5aa853c721263eaed4fed0549a98036d4c3a1114 100644 (file)
                               ((member x '("b" "c")) 2)
                               ((not x) 3)))
             '("a" "b" "c" "d" nil))
+
+    ;; `let' and `let*' optimisations with body being constant or variable
+    (let* (a
+           (b (progn (setq a (cons 1 a)) 2))
+           (c (1+ b))
+           (d (list a c)))
+      d)
+    (let ((a nil))
+      (let ((b (progn (setq a (cons 1 a)) 2))
+            (c (progn (setq a (cons 3 a))))
+            (d (list a)))
+        d))
+    (let* ((_a 1)
+           (_b 2))
+      'z)
+    (let ((_a 1)
+          (_b 2))
+      'z)
     )
   "List of expressions for cross-testing interpreted and compiled code.")