From 52a55e11deb7822c67a8d7e6f2544b8f41d25a4e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Mattias=20Engdeg=C3=A5rd?= Date: Thu, 29 Jul 2021 15:35:55 +0200 Subject: [PATCH] Optimise let and let* whose body is constant or the last variable 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 | 37 +++++++++++++++++--------- test/lisp/emacs-lisp/bytecomp-tests.el | 18 +++++++++++++ 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el index c9dfa69aeb2..d444d7006eb 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el @@ -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) diff --git a/test/lisp/emacs-lisp/bytecomp-tests.el b/test/lisp/emacs-lisp/bytecomp-tests.el index ee0f931c192..5aa853c7212 100644 --- a/test/lisp/emacs-lisp/bytecomp-tests.el +++ b/test/lisp/emacs-lisp/bytecomp-tests.el @@ -509,6 +509,24 @@ ((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.") -- 2.39.2