;;; Copyright (c) 1991, 1994 Free Software Foundation, Inc.
-;; Author: Jamie Zawinski <jwz@jwz.org>
-;; Hallvard Furuseth <hbf@ulrik.uio.no>
+;; Authors: Jamie Zawinski <jwz@jwz.org>
+;; Hallvard Furuseth <hbf@ulrik.uio.no>
+;; Martin Buchholz <martin@xemacs.org>
;; Keywords: internal
;; This file is part of XEmacs.
;; General Public License for more details.
;; You should have received a copy of the GNU General Public License
-;; along with XEmacs; see the file COPYING. If not, write to the
+;; along with XEmacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
-;;; Synched up with: FSF 19.30.
+;;; Synched up with: FSF 20.7 except where marked.
+;;; [[ Synched up with: FSF 20.7. ]]
+;;; DO NOT PUT IN AN INVALID SYNC MESSAGE WHEN YOU DO A PARTIAL SYNC. --ben
+
+;; BEGIN SYNC WITH 20.7.
;;; Commentary:
;; You can, however, make a faster pig."
;;
;; Or, to put it another way, the emacs byte compiler is a VW Bug. This code
-;; makes it be a VW Bug with fuel injection and a turbocharger... You're
+;; makes it be a VW Bug with fuel injection and a turbocharger... You're
;; still not going to make it go faster than 70 mph, but it might be easier
;; to get it there.
;;
;; Simple defsubsts often produce forms like
;; (let ((v1 (f1)) (v2 (f2)) ...)
;; (FN v1 v2 ...))
-;; It would be nice if we could optimize this to
+;; It would be nice if we could optimize this to
;; (FN (f1) (f2) ...)
;; but we can't unless FN is dynamically-safe (it might be dynamically
;; referring to the bindings that the lambda arglist established.)
;; One of the uncountable lossages introduced by dynamic scope...
;;
-;; Maybe there should be a control-structure that says "turn on
+;; Maybe there should be a control-structure that says "turn on
;; fast-and-loose type-assumptive optimizations here." Then when
;; we see a form like (car foo) we can from then on assume that
;; the variable foo is of type cons, and optimize based on that.
-;; But, this won't win much because of (you guessed it) dynamic
+;; But, this won't win much because of (you guessed it) dynamic
;; scope. Anything down the stack could change the value.
;; (Another reason it doesn't work is that it is perfectly valid
;; to call car with a null argument.) A better approach might
;;
;; However, if there was even a single let-binding around the COND,
;; it could not be byte-compiled, because there would be an "unbind"
-;; byte-op between the final "call" and "return." Adding a
+;; byte-op between the final "call" and "return." Adding a
;; Bunbind_all byteop would fix this.
;;
;; (defun foo (x y z) ... (foo a b c))
;;
;; Wouldn't it be nice if Emacs Lisp had lexical scope.
;;
-;; Idea: the form (lexical-scope) in a file means that the file may be
-;; compiled lexically. This proclamation is file-local. Then, within
+;; Idea: the form (lexical-scope) in a file means that the file may be
+;; compiled lexically. This proclamation is file-local. Then, within
;; that file, "let" would establish lexical bindings, and "let-dynamic"
;; would do things the old way. (Or we could use CL "declare" forms.)
;; We'd have to notice defvars and defconsts, since those variables should
;; in the file being compiled (doing a boundp check isn't good enough.)
;; Fdefvar() would have to be modified to add something to the plist.
;;
-;; A major disadvantage of this scheme is that the interpreter and compiler
-;; would have different semantics for files compiled with (dynamic-scope).
+;; A major disadvantage of this scheme is that the interpreter and compiler
+;; would have different semantics for files compiled with (dynamic-scope).
;; Since this would be a file-local optimization, there would be no way to
-;; modify the interpreter to obey this (unless the loader was hacked
+;; modify the interpreter to obey this (unless the loader was hacked
;; in some grody way, but that's a really bad idea.)
;;
;; HA! RMS removed the following paragraph from his version of
;; byte-optimize.el.
;;
;; Really the Right Thing is to make lexical scope the default across
-;; the board, in the interpreter and compiler, and just FIX all of
+;; the board, in the interpreter and compiler, and just FIX all of
;; the code that relies on dynamic scope of non-defvarred variables.
;; Other things to consider:
;; error free also they may act as true-constants.
;;(disassemble #'(lambda (x) (and (point) (foo))))
-;; When
+;; When
;; - all but one arguments to a function are constant
;; - the non-constant argument is an if-expression (cond-expression?)
;; then the outer function can be distributed. If the guarding
;;(disassemble #'(lambda (x) (eq (if (point) 'a 'b) 'c)))
;;(disassemble #'(lambda (x) (if (point) (eq 'a 'c) (eq 'b 'c))))
-;; (car (cons A B)) -> (progn B A)
+;; (car (cons A B)) -> (prog1 A B)
;;(disassemble #'(lambda (x) (car (cons (foo) 42))))
;; (cdr (cons A B)) -> (progn A B)
;;(disassemble #'(lambda (x) (cdr (cons 42 (foo)))))
-;; (car (list A B ...)) -> (progn B ... A)
+;; (car (list A B ...)) -> (prog1 A ... B)
;;(disassemble #'(lambda (x) (car (list (foo) 42 (bar)))))
;; (cdr (list A B ...)) -> (progn A (list B ...))
(compiled-function-constants fn)
(compiled-function-stack-depth fn)))
(cdr form)))
- (if (not (eq (car fn) 'lambda)) (error "%s is not a lambda" name))
- (cons fn (cdr form)))))))
+ (if (eq (car-safe fn) 'lambda)
+ (cons fn (cdr form))
+ ;; Give up on inlining.
+ form))))))
;;; ((lambda ...) ...)
-;;;
+;;;
(defun byte-compile-unfold-lambda (form &optional name)
(or name (setq name "anonymous lambda"))
(let ((lambda (car form))
(byte-compile-warn
"attempt to open-code %s with too many arguments" name))
form)
- (let ((newform
+ ;; This line, introduced in v1.10, can cause an infinite
+ ;; recursion when inlining recursive defsubst's
+; (setq body (mapcar 'byte-optimize-form body))
+ (let ((newform
(if bindings
(cons 'let (cons (nreverse bindings) body))
(cons 'progn body))))
(cons (byte-optimize-form (nth 1 form) t)
(cons (byte-optimize-form (nth 2 form) for-effect)
(byte-optimize-body (cdr (cdr (cdr form))) t)))))
-
+
((memq fn '(save-excursion save-restriction save-current-buffer))
;; those subrs which have an implicit progn; it's not quite good
;; enough to treat these like normal function calls.
;; This can turn (save-excursion ...) into (save-excursion) which
;; will be optimized away in the lap-optimize pass.
(cons fn (byte-optimize-body (cdr form) for-effect)))
-
+
((eq fn 'with-output-to-temp-buffer)
;; this is just like the above, except for the first argument.
(cons fn
(cons
(byte-optimize-form (nth 1 form) nil)
(byte-optimize-body (cdr (cdr form)) for-effect))))
-
+
((eq fn 'if)
(cons fn
(cons (byte-optimize-form (nth 1 form) nil)
(cons
(byte-optimize-form (nth 2 form) for-effect)
(byte-optimize-body (nthcdr 3 form) for-effect)))))
-
+
((memq fn '(and or)) ; remember, and/or are control structures.
;; take forms off the back until we can't any more.
;; In the future it could conceivably be a problem that the
(if (and (cdr form) (null backwards))
(byte-compile-log
" all subforms of %s called for effect; deleted" form))
- (and backwards
- (cons fn (nreverse backwards))))
+ (when backwards
+ ;; Now optimize the rest of the forms. We need the return
+ ;; values. We already did the car.
+ (setcdr backwards
+ (mapcar 'byte-optimize-form (cdr backwards))))
+ (cons fn (nreverse backwards)))
(cons fn (mapcar 'byte-optimize-form (cdr form)))))
((eq fn 'interactive)
(byte-compile-warn "misplaced interactive spec: %s"
(prin1-to-string form))
nil)
-
+
((memq fn '(defun defmacro function
condition-case save-window-excursion))
;; These forms are compiled as constants or by breaking out
(cons fn
(cons (byte-optimize-form (nth 1 form) for-effect)
(cdr (cdr form)))))
-
+
((eq fn 'catch)
;; the body of a catch is compiled (and thus optimized) as a
;; top-level form, so don't do it here. The tag is never
(setq form (macroexpand form
byte-compile-macro-environment))))
(byte-optimize-form form for-effect))
-
+
+ ;; Support compiler macros as in cl.el.
+ ((and (fboundp 'compiler-macroexpand)
+ (symbolp (car-safe form))
+ (get (car-safe form) 'cl-compiler-macro)
+ (not (eq form
+ (setq form (compiler-macroexpand form)))))
+ (byte-optimize-form form for-effect))
+
((not (symbolp fn))
(or (eq 'mocklisp (car-safe fn)) ; ha!
(byte-compile-warn "%s is a malformed function"
;; appending a nil here might not be necessary, but it can't hurt.
(byte-optimize-form
(cons 'progn (append (cdr form) '(nil))) t))
-
+
(t
;; Otherwise, no args can be considered to be for-effect,
;; even if the called function is for-effect, because we
((keywordp ,form))))
;; If the function is being called with constant numeric args,
-;; evaluate as much as possible at compile-time. This optimizer
+;; evaluate as much as possible at compile-time. This optimizer
;; assumes that the function is associative, like + or *.
(defun byte-optimize-associative-math (form)
(let ((args nil)
(list (apply fun (nreverse constants)))))))))
form))
-(defun byte-optimize-plus (form)
- (setq form (byte-optimize-delay-constants-math form 1 '+))
- (if (memq 0 form) (setq form (delq 0 (copy-sequence form))))
- ;;(setq form (byte-optimize-associative-two-args-math form))
- (case (length (cdr form))
- ((0)
- (condition-case ()
- (eval form)
- (error form)))
-
- ;; `add1' and `sub1' are a marginally fewer instructions
- ;; than `plus' and `minus', so use them when possible.
- ((2)
- (cond
- ((eq (nth 1 form) 1) `(1+ ,(nth 2 form))) ; (+ 1 x) --> (1+ x)
- ((eq (nth 2 form) 1) `(1+ ,(nth 1 form))) ; (+ x 1) --> (1+ x)
- ((eq (nth 1 form) -1) `(1- ,(nth 2 form))) ; (+ -1 x) --> (1- x)
- ((eq (nth 2 form) -1) `(1- ,(nth 1 form))) ; (+ x -1) --> (1- x)
- (t form)))
-
- ;; It is not safe to delete the function entirely
- ;; (actually, it would be safe if we know the sole arg
- ;; is not a marker).
- ;; ((null (cdr (cdr form))) (nth 1 form))
- (t form)))
+;; END SYNC WITH 20.7.
-(defun byte-optimize-minus (form)
- ;; Put constants at the end, except the last constant.
- (setq form (byte-optimize-delay-constants-math form 2 '+))
- ;; Now only first and last element can be a number.
- (let ((last (car (reverse (nthcdr 3 form)))))
- (cond ((eq 0 last)
- ;; (- x y ... 0) --> (- x y ...)
- (setq form (copy-sequence form))
- (setcdr (cdr (cdr form)) (delq 0 (nthcdr 3 form))))
- ;; If form is (- CONST foo... CONST), merge first and last.
- ((and (numberp (nth 1 form))
- (numberp last))
- (setq form (nconc (list '- (- (nth 1 form) last) (nth 2 form))
- (delq last (copy-sequence (nthcdr 3 form))))))))
- (setq form
-;;; It is not safe to delete the function entirely
-;;; (actually, it would be safe if we know the sole arg
-;;; is not a marker).
-;;; (if (eq (nth 2 form) 0)
-;;; (nth 1 form) ; (- x 0) --> x
- (byte-optimize-predicate
- (if (and (null (cdr (cdr (cdr form))))
- (eq (nth 1 form) 0)) ; (- 0 x) --> (- x)
- (cons (car form) (cdr (cdr form)))
- form))
-;;; )
- )
-
- ;; `add1' and `sub1' are a marginally fewer instructions than `plus'
- ;; and `minus', so use them when possible.
- (cond ((and (null (nthcdr 3 form))
- (eq (nth 2 form) 1))
- (list '1- (nth 1 form))) ; (- x 1) --> (1- x)
- ((and (null (nthcdr 3 form))
- (eq (nth 2 form) -1))
- (list '1+ (nth 1 form))) ; (- x -1) --> (1+ x)
- (t
- form))
- )
+;;; It is not safe to optimize calls to arithmetic ops with one arg
+;;; away entirely (actually, it would be safe if we know the sole arg
+;;; is not a marker or if it appears in other arithmetic).
+
+;;; But this degree of paranoia is normally unjustified, so optimize unless
+;;; the user has done (declaim (optimize (safety 3))). See bytecomp.el.
+
+(defun byte-optimize-plus (form)
+ (byte-optimize-predicate (byte-optimize-delay-constants-math form 1 '+)))
(defun byte-optimize-multiply (form)
(setq form (byte-optimize-delay-constants-math form 1 '*))
- ;; If there is a constant in FORM, it is now the last element.
- (cond ((null (cdr form)) 1)
-;;; It is not safe to delete the function entirely
-;;; (actually, it would be safe if we know the sole arg
-;;; is not a marker or if it appears in other arithmetic).
-;;; ((null (cdr (cdr form))) (nth 1 form))
- ((let ((last (car (reverse form))))
- (cond ((eq 0 last) (cons 'progn (cdr form)))
- ((eq 1 last) (delq 1 (copy-sequence form)))
- ((eq -1 last) (list '- (delq -1 (copy-sequence form))))
- ((and (eq 2 last)
- (memq t (mapcar 'symbolp (cdr form))))
- (prog1 (setq form (delq 2 (copy-sequence form)))
- (while (not (symbolp (car (setq form (cdr form))))))
- (setcar form (list '+ (car form) (car form)))))
- (form))))))
+ ;; If there is a constant integer in FORM, it is now the last element.
+
+ (case (car (last form))
+ ;; (* x y 0) --> (progn x y 0)
+ (0 (cons 'progn (cdr form)))
+ (t (byte-optimize-predicate form))))
+
+(defun byte-optimize-minus (form)
+ ;; Put constants at the end, except the first arg.
+ (setq form (byte-optimize-delay-constants-math form 2 '+))
+ ;; Now only the first and last args can be integers.
+ (let ((last (car (last (nthcdr 3 form)))))
+ (cond
+ ;; If form is (- CONST foo... CONST), merge first and last.
+ ((and (numberp (nth 1 form)) (numberp last))
+ (decf (nth 1 form) last)
+ (butlast form))
+
+ ;; (- 0 ...) -->
+ ((eq 0 (nth 1 form))
+ (case (length form)
+ ;; (- 0) --> 0
+ (2 0)
+ ;; (- 0 x) --> (- x)
+ (3 `(- ,(nth 2 form)))
+ ;; (- 0 x y ...) --> (- (- x) y ...)
+ (t `(- (- ,(nth 2 form)) ,@(nthcdr 3 form)))))
+
+ (t (byte-optimize-predicate form)))))
(defun byte-optimize-divide (form)
+ ;; Put constants at the end, except the first arg.
(setq form (byte-optimize-delay-constants-math form 2 '*))
- (let ((last (car (reverse (cdr (cdr form))))))
- (if (numberp last)
- (cond ((= (length form) 3)
- (if (and (numberp (nth 1 form))
- (not (zerop last))
- (condition-case nil
- (/ (nth 1 form) last)
- (error nil)))
- (setq form (list 'progn (/ (nth 1 form) last)))))
- ((= last 1)
- (setq form (butlast form)))
- ((numberp (nth 1 form))
- (setq form (cons (car form)
- (cons (/ (nth 1 form) last)
- (butlast (cdr (cdr form)))))
- last nil))))
- (cond
-;;; ((null (cdr (cdr form)))
-;;; (nth 1 form))
- ((eq (nth 1 form) 0)
- (append '(progn) (cdr (cdr form)) '(0)))
- ((eq last -1)
- (list '- (if (nthcdr 3 form)
- (butlast form)
- (nth 1 form))))
- (form))))
+ ;; Now only the first and last args can be integers.
+ (let ((last (car (last (nthcdr 3 form)))))
+ (cond
+ ;; If form is (/ CONST foo... CONST), merge first and last.
+ ((and (numberp (nth 1 form)) (numberp last))
+ (condition-case nil
+ (cons (nth 0 form)
+ (cons (/ (nth 1 form) last)
+ (butlast (cdr (cdr form)))))
+ (error form)))
+
+ ;; (/ 0 x y) --> (progn x y 0)
+ ((eq (nth 1 form) 0)
+ (append '(progn) (cdr (cdr form)) '(0)))
+
+ ;; We don't have to check for divide-by-zero because `/' does.
+ (t (byte-optimize-predicate form)))))
+
+;; BEGIN SYNC WITH 20.7.
(defun byte-optimize-logmumble (form)
(setq form (byte-optimize-delay-constants-math form 1 (car form)))
(setq ok (byte-compile-constp (car rest))
rest (cdr rest)))
(if ok
- (condition-case ()
+ (condition-case err
(list 'quote (eval form))
- (error form))
+ (error
+ (byte-compile-warn "evaluating %s: %s" form err)
+ form))
form)))
(defun byte-optimize-identity (form)
(if (= 1 (length (cdr form))) "" "s"))
form))
+(defun byte-optimize-car (form)
+ (let ((arg (cadr form)))
+ (cond
+ ((and (byte-compile-trueconstp arg)
+ (not (and (consp arg)
+ (eq (car arg) 'quote)
+ (listp (cadr arg)))))
+ (byte-compile-warn
+ "taking car of a constant: %s" arg)
+ form)
+ ((and (eq (car-safe arg) 'cons)
+ (eq (length arg) 3))
+ `(prog1 ,(nth 1 arg) ,(nth 2 arg)))
+ ((eq (car-safe arg) 'list)
+ `(prog1 ,@(cdr arg)))
+ (t
+ (byte-optimize-predicate form)))))
+
+(defun byte-optimize-cdr (form)
+ (let ((arg (cadr form)))
+ (cond
+ ((and (byte-compile-trueconstp arg)
+ (not (and (consp arg)
+ (eq (car arg) 'quote)
+ (listp (cadr arg)))))
+ (byte-compile-warn
+ "taking cdr of a constant: %s" arg)
+ form)
+ ((and (eq (car-safe arg) 'cons)
+ (eq (length arg) 3))
+ `(progn ,(nth 1 arg) ,(nth 2 arg)))
+ ((eq (car-safe arg) 'list)
+ (if (> (length arg) 2)
+ `(progn ,(cadr arg) (list ,@(cddr arg)))
+ (cadr arg)))
+ (t
+ (byte-optimize-predicate form)))))
+
(put 'identity 'byte-optimizer 'byte-optimize-identity)
(put '+ 'byte-optimizer 'byte-optimize-plus)
(put '* 'byte-optimizer 'byte-optimize-multiply)
(put '- 'byte-optimizer 'byte-optimize-minus)
(put '/ 'byte-optimizer 'byte-optimize-divide)
+(put '% 'byte-optimizer 'byte-optimize-predicate)
(put 'max 'byte-optimizer 'byte-optimize-associative-math)
(put 'min 'byte-optimizer 'byte-optimize-associative-math)
-(put '= 'byte-optimizer 'byte-optimize-binary-predicate)
(put 'eq 'byte-optimizer 'byte-optimize-binary-predicate)
(put 'eql 'byte-optimizer 'byte-optimize-binary-predicate)
(put 'equal 'byte-optimizer 'byte-optimize-binary-predicate)
(put 'string= 'byte-optimizer 'byte-optimize-binary-predicate)
(put 'string-equal 'byte-optimizer 'byte-optimize-binary-predicate)
+(put '= 'byte-optimizer 'byte-optimize-predicate)
(put '< 'byte-optimizer 'byte-optimize-predicate)
(put '> 'byte-optimizer 'byte-optimize-predicate)
(put '<= 'byte-optimizer 'byte-optimize-predicate)
(put 'stringp 'byte-optimizer 'byte-optimize-predicate)
(put 'string< 'byte-optimizer 'byte-optimize-predicate)
(put 'string-lessp 'byte-optimizer 'byte-optimize-predicate)
+(put 'length 'byte-optimizer 'byte-optimize-predicate)
(put 'logand 'byte-optimizer 'byte-optimize-logmumble)
(put 'logior 'byte-optimizer 'byte-optimize-logmumble)
(put 'logxor 'byte-optimizer 'byte-optimize-logmumble)
(put 'lognot 'byte-optimizer 'byte-optimize-predicate)
-(put 'car 'byte-optimizer 'byte-optimize-predicate)
-(put 'cdr 'byte-optimizer 'byte-optimize-predicate)
+(put 'car 'byte-optimizer 'byte-optimize-car)
+(put 'cdr 'byte-optimizer 'byte-optimize-cdr)
(put 'car-safe 'byte-optimizer 'byte-optimize-predicate)
(put 'cdr-safe 'byte-optimizer 'byte-optimize-predicate)
-;; I'm not convinced that this is necessary. Doesn't the optimizer loop
+;; I'm not convinced that this is necessary. Doesn't the optimizer loop
;; take care of this? - Jamie
;; I think this may some times be necessary to reduce eg. (quote 5) to 5,
;; so arithmetic optimizers recognize the numeric constant. - Hallvard
(byte-optimize-predicate form)
(nth 1 form))))
+;; END SYNC WITH 20.7.
+
+;;; For the byte optimizer, `cond' is just overly sweet syntactic sugar.
+;;; So we rewrite (cond ...) in terms of `if' and `or',
+;;; which are easier to optimize.
(defun byte-optimize-cond (form)
- ;; if any clauses have a literal nil as their test, throw them away.
- ;; if any clause has a literal non-nil constant as its test, throw
- ;; away all following clauses.
- (let (rest)
- ;; This must be first, to reduce (cond (t ...) (nil)) to (progn t ...)
- (while (setq rest (assq nil (cdr form)))
- (setq form (delq rest (copy-sequence form))))
- (if (memq nil (cdr form))
- (setq form (delq nil (copy-sequence form))))
- (setq rest form)
- (while (setq rest (cdr rest))
- (cond ((byte-compile-trueconstp (car-safe (car rest)))
- (cond ((eq rest (cdr form))
- (setq form
- (if (cdr (car rest))
- (if (cdr (cdr (car rest)))
- (cons 'progn (cdr (car rest)))
- (nth 1 (car rest)))
- (car (car rest)))))
- ((cdr rest)
- (setq form (copy-sequence form))
- (setcdr (memq (car rest) form) nil)))
- (setq rest nil)))))
- ;;
- ;; Turn (cond (( <x> )) ... ) into (or <x> (cond ... ))
- (if (eq 'cond (car-safe form))
- (let ((clauses (cdr form)))
- (if (and (consp (car clauses))
- (null (cdr (car clauses))))
- (list 'or (car (car clauses))
- (byte-optimize-cond
- (cons (car form) (cdr (cdr form)))))
- form))
- form))
+ (byte-optimize-cond-1 (cdr form)))
+
+(defun byte-optimize-cond-1 (clauses)
+ (cond
+ ((null clauses) nil)
+ ((consp (car clauses))
+ (nconc
+ (case (length (car clauses))
+ (1 `(or ,(nth 0 (car clauses))))
+ (2 `(if ,(nth 0 (car clauses)) ,(nth 1 (car clauses))))
+ (t `(if ,(nth 0 (car clauses)) (progn ,@(cdr (car clauses))))))
+ (when (cdr clauses) (list (byte-optimize-cond-1 (cdr clauses))))))
+ (t (error "malformed cond clause %s" (car clauses)))))
+
+;; BEGIN SYNC WITH 20.7.
(defun byte-optimize-if (form)
;; (if <true-constant> <then> <else...>) ==> <then>
(put 'if 'byte-optimizer 'byte-optimize-if)
(put 'while 'byte-optimizer 'byte-optimize-while)
-;; Remove any reason for avoiding `char-before'.
+;; The supply of bytecodes is small and constrained by backward compatibility.
+;; Several functions have byte-coded versions and hence are very efficient.
+;; Related functions which can be expressed in terms of the byte-coded
+;; ones should be transformed into bytecoded calls for efficiency.
+;; This is especially the case for functions with a backward- and
+;; forward- version, but with a bytecode only for the forward one.
+
+;; Some programmers have hand-optimized calls like (backward-char)
+;; into the call (forward-char -1).
+;; But it's so much nicer for the byte-compiler to do this automatically!
+
+;; (char-before) ==> (char-after (1- (point)))
+(put 'char-before 'byte-optimizer 'byte-optimize-char-before)
(defun byte-optimize-char-before (form)
- `(char-after (1- ,(or (nth 1 form) '(point))) ,@(cdr (cdr form))))
-
-(put 'char-before 'byte-optimizer 'byte-optimize-char-before)
+ `(char-after
+ ,(cond
+ ((null (nth 1 form))
+ '(1- (point)))
+ ((equal '(point) (nth 1 form))
+ '(1- (point)))
+ (t `(1- (or ,(nth 1 form) (point)))))
+ ,@(cdr (cdr form))))
+
+;; (backward-char n) ==> (forward-char (- n))
+(put 'backward-char 'byte-optimizer 'byte-optimize-backward-char)
+(defun byte-optimize-backward-char (form)
+ `(forward-char
+ ,(typecase (nth 1 form)
+ (null -1)
+ (integer (- (nth 1 form)))
+ (t `(- (or ,(nth 1 form) 1))))
+ ,@(cdr (cdr form))))
+
+;; (backward-word n) ==> (forward-word (- n))
+(put 'backward-word 'byte-optimizer 'byte-optimize-backward-word)
+(defun byte-optimize-backward-word (form)
+ `(forward-word
+ ,(typecase (nth 1 form)
+ (null -1)
+ (integer (- (nth 1 form)))
+ (t `(- (or ,(nth 1 form) 1))))
+ ,@(cdr (cdr form))))
+
+;; The following would be a valid optimization of the above kind, but
+;; the gain in performance is very small, since the saved funcall is
+;; counterbalanced by the necessity of adding a bytecode for (point).
+;;
+;; Also, users are more likely to have modified the behavior of
+;; delete-char via advice or some similar mechanism. This is much
+;; less of a problem for the previous functions because it wouldn't
+;; make sense to modify the behaviour of `backward-char' without also
+;; modifying `forward-char', for example.
+
+;; (delete-char n) ==> (delete-region (point) (+ (point) n))
+;; (put 'delete-char 'byte-optimizer 'byte-optimize-delete-char)
+;; (defun byte-optimize-delete-char (form)
+;; (case (length (cdr form))
+;; (0 `(delete-region (point) (1+ (point))))
+;; (1 `(delete-region (point) (+ (point) ,(nth 1 form))))
+;; (t form)))
;; byte-compile-negation-optimizer lives in bytecomp.el
;(put '/= 'byte-optimizer 'byte-compile-negation-optimizer)
(put 'atom 'byte-optimizer 'byte-compile-negation-optimizer)
(put 'nlistp 'byte-optimizer 'byte-compile-negation-optimizer)
-
(defun byte-optimize-funcall (form)
;; (funcall '(lambda ...) ...) ==> ((lambda ...) ...)
;; (funcall 'foo ...) ==> (foo ...)
(while (>= (setq count (1- count)) 0)
(setq form (list 'cdr form)))
form)))
+
+(put 'concat 'byte-optimizer 'byte-optimize-concat)
+(defun byte-optimize-concat (form)
+ (let ((args (cdr form))
+ (constant t))
+ (while (and args constant)
+ (or (byte-compile-constp (car args))
+ (setq constant nil))
+ (setq args (cdr args)))
+ (if constant
+ (eval form)
+ form)))
\f
-;;; enumerating those functions which need not be called if the returned
+;;; enumerating those functions which need not be called if the returned
;;; value is not used. That is, something like
;;; (progn (list (something-with-side-effects) (yow))
;;; (foo))
length log log10 logand logb logior lognot logxor lsh
marker-buffer max member memq min mod
next-window nth nthcdr number-to-string
- parse-colon-path previous-window
+ parse-colon-path plist-get previous-window
radians-to-degrees rassq regexp-quote reverse round
sin sqrt string< string= string-equal string-lessp string-to-char
string-to-int string-to-number substring symbol-plist
abs expt signum last butlast ldiff
pairlis gcd lcm
isqrt floor* ceiling* truncate* round* mod* rem* subseq
- list-length get* getf
+ list-length getf
))
(side-effect-and-error-free-fns
'(arrayp atom
hash-table-p
identity ignore integerp integer-or-marker-p interactive-p
invocation-directory invocation-name
- ;; keymapp may autoload in XEmacs, so not on this list!
- list listp
+ keymapp list listp
make-marker mark mark-marker markerp memory-limit minibuffer-window
;; mouse-movement-p not in XEmacs
natnump nlistp not null number-or-marker-p numberp
(defun byte-compile-splice-in-already-compiled-code (form)
;; form is (byte-code "..." [...] n)
- (if (not (memq byte-optimize '(t lap)))
+ (if (not (memq byte-optimize '(t byte)))
(byte-compile-normal-call form)
(byte-inline-lapcode
(byte-decompile-bytecode-1 (nth 1 form) (nth 2 form) t))
;; fetch and return the offset for the current opcode.
;; return NIL if this opcode has no offset
;; OP, PTR and BYTES are used and set dynamically
- (defvar op)
- (defvar ptr)
- (defvar bytes)
+ (declare (special op ptr bytes))
(cond ((< op byte-nth)
(let ((tem (logand op 7)))
(setq op (logand op 248))
tags)))))))
((cond ((eq op 'byte-constant2) (setq op 'byte-constant) t)
((memq op byte-constref-ops)))
- (setq tmp (aref constvec offset)
+ (setq tmp (if (>= offset (length constvec))
+ (list 'out-of-range offset)
+ (aref constvec offset))
offset (if (eq op 'byte-constant)
(byte-compile-get-constant tmp)
(or (assq tmp byte-compile-variables)
(defconst byte-after-unbind-ops
'(byte-constant byte-dup
byte-symbolp byte-consp byte-stringp byte-listp byte-numberp byte-integerp
- byte-eq byte-equal byte-not
+ byte-eq byte-not
byte-cons byte-list1 byte-list2 ; byte-list3 byte-list4
byte-interactive-p)
;; How about other side-effect-free-ops? Is it safe to move an
;; error invocation (such as from nth) out of an unwind-protect?
+ ;; No, it is not, because the unwind-protect forms can alter
+ ;; the inside of the object to which nth would apply.
+ ;; For the same reason, byte-equal was deleted from this list.
"Byte-codes that can be moved past an unbind.")
(defconst byte-compile-side-effect-and-error-free-ops
byte-current-buffer byte-interactive-p))
(defconst byte-compile-side-effect-free-ops
- (nconc
+ (nconc
'(byte-varref byte-nth byte-memq byte-car byte-cdr byte-length byte-aref
byte-symbol-value byte-get byte-concat2 byte-concat3 byte-sub1 byte-add1
byte-eqlsign byte-gtr byte-lss byte-leq byte-geq byte-diff byte-negate
;;; varbind pop-up-windows
;;; not
;;;
-;;; we break the program, because it will appear that pop-up-windows and
+;;; we break the program, because it will appear that pop-up-windows and
;;; old-pop-ups are not EQ when really they are. So we have to know what
;;; the BOOL variables are, and not perform this optimization on them.
;;;
;;; variables.
;(defconst byte-boolean-vars
-; '(abbrev-all-caps purify-flag find-file-compare-truenames
-; find-file-use-truenames delete-auto-save-files byte-metering-on
-; x-seppuku-on-epipe zmacs-regions zmacs-region-active-p
-; zmacs-region-stays atomic-extent-goto-char-p
-; suppress-early-error-handler-backtrace noninteractive
-; inhibit-early-packages inhibit-autoloads debug-paths
-; inhibit-site-lisp debug-on-quit debug-on-next-call
-; modifier-keys-are-sticky x-allow-sendevents
-; mswindows-dynamic-frame-resize focus-follows-mouse
-; inhibit-input-event-recording enable-multibyte-characters
-; disable-auto-save-when-buffer-shrinks
-; allow-deletion-of-last-visible-frame indent-tabs-mode
-; load-in-progress load-warn-when-source-newer
-; load-warn-when-source-only load-ignore-elc-files
-; load-force-doc-strings fail-on-bucky-bit-character-escapes
-; popup-menu-titles menubar-show-keybindings completion-ignore-case
-; canna-empty-info canna-through-info canna-underline
-; canna-inhibit-hankakukana enable-multibyte-characters
-; re-short-flag x-handle-non-fully-specified-fonts
-; print-escape-newlines print-readably delete-exited-processes
-; windowed-process-io visible-bell no-redraw-on-reenter
-; cursor-in-echo-area inhibit-warning-display
-; column-number-start-at-one parse-sexp-ignore-comments
-; words-include-escapes scroll-on-clipped-lines)
-; "DEFVAR_BOOL variables. Giving these any non-nil value sets them to t.
-;If this does not enumerate all DEFVAR_BOOL variables, the byte-optimizer
-;may generate incorrect code.")
+; ...)
(defun byte-optimize-lapcode (lap &optional for-effect)
"Simple peephole optimizer. LAP is both modified and returned."
- (let (lap0 ;; off0 unused
- lap1 ;; off1
- lap2 ;; off2
+ (let (lap0
+ lap1
+ lap2
+ variable-frequency
(keep-going 'first-time)
(add-depth 0)
rest tmp tmp2 tmp3
;; goto-X-if-non-nil goto-Y X: --> goto-Y-if-nil X:
;;
;; it is wrong to do the same thing for the -else-pop variants.
- ;;
+ ;;
((and (or (eq 'byte-goto-if-nil (car lap0))
(eq 'byte-goto-if-not-nil (car lap0))) ; gotoX
(eq 'byte-goto (car lap1)) ; gotoY
str (concat str " %s")
i (1+ i))))
(if opt-p
- (let ((tagstr
+ (let ((tagstr
(if (eq 'TAG (car (car tmp)))
(format "%d:" (car (cdr (car tmp))))
(or (car tmp) ""))))
(byte-goto-if-not-nil-else-pop .
byte-goto-if-nil-else-pop))))
newtag)
-
+
(nth 1 newtag)
)
(setcdr tmp (cons (setcdr lap0 newtag) (cdr tmp)))
;; Rebuild byte-compile-constants / byte-compile-variables.
;; Simple optimizations that would inhibit other optimizations if they
;; were done in the optimizing loop, and optimizations which there is no
- ;; need to do more than once.
+ ;; need to do more than once.
(setq byte-compile-constants nil
- byte-compile-variables nil)
+ byte-compile-variables nil
+ variable-frequency (make-hash-table :test 'eq))
(setq rest lap)
(while rest
(setq lap0 (car rest)
lap1 (nth 1 rest))
(if (memq (car lap0) byte-constref-ops)
- (if (eq (cdr lap0) 'byte-constant)
- (or (memq (cdr lap0) byte-compile-variables)
- (setq byte-compile-variables (cons (cdr lap0)
- byte-compile-variables)))
+ (if (not (eq (car lap0) 'byte-constant))
+ (progn
+ (incf (gethash (cdr lap0) variable-frequency 0))
+ (or (memq (cdr lap0) byte-compile-variables)
+ (setq byte-compile-variables
+ (cons (cdr lap0) byte-compile-variables))))
(or (memq (cdr lap0) byte-compile-constants)
(setq byte-compile-constants (cons (cdr lap0)
byte-compile-constants)))))
(cond (;;
- ;; const-C varset-X const-C --> const-C dup varset-X
+ ;; const-C varset-X const-C --> const-C dup varset-X
;; const-C varbind-X const-C --> const-C dup varbind-X
;;
(and (eq (car lap0) 'byte-constant)
(eq (car (nth 2 rest)) 'byte-constant)
- (eq (cdr lap0) (car (nth 2 rest)))
+ (eq (cdr lap0) (cdr (nth 2 rest)))
(memq (car lap1) '(byte-varbind byte-varset)))
(byte-compile-log-lap " %s %s %s\t-->\t%s dup %s"
lap0 lap1 lap0 lap0 lap1)
(setcdr lap1 (+ (cdr lap1) (cdr lap0))))
)
(setq rest (cdr rest)))
+ ;; Since the first 6 entries of the compiled-function constants
+ ;; vector are most efficient for varref/set/bind ops, we sort by
+ ;; reference count. This generates maximally space efficient and
+ ;; pretty time-efficient byte-code. See `byte-compile-constants-vector'.
+ (setq byte-compile-variables
+ (sort byte-compile-variables
+ #'(lambda (v1 v2)
+ (< (gethash v1 variable-frequency)
+ (gethash v2 variable-frequency)))))
+ ;; Another hack - put the most used variable in position 6, for
+ ;; better locality of reference with adjoining constants.
+ (let ((tail (last byte-compile-variables 6)))
+ (setq byte-compile-variables
+ (append (nbutlast byte-compile-variables 6)
+ (nreverse tail))))
(setq byte-compile-maxdepth (+ byte-compile-maxdepth add-depth)))
lap)
byte-optimize-lapcode))))
nil)
+;; END SYNC WITH 20.7.
+
;;; byte-optimize.el ends here