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.
This commit is contained in:
Mattias Engdegård 2021-07-29 15:35:55 +02:00
parent ab9c06449d
commit 52a55e11de
2 changed files with 43 additions and 12 deletions

View 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)

View file

@ -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.")