[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
master 52a55e1 5/5: Optimise let and let* whose body is constant or the
From: |
Mattias Engdegård |
Subject: |
master 52a55e1 5/5: Optimise let and let* whose body is constant or the last variable |
Date: |
Fri, 30 Jul 2021 04:54:07 -0400 (EDT) |
branch: master
commit 52a55e11deb7822c67a8d7e6f2544b8f41d25a4e
Author: Mattias Engdegård <mattiase@acm.org>
Commit: Mattias Engdegård <mattiase@acm.org>
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 c9dfa69..d444d70 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 ee0f931..5aa853c 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.")