;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING. If not, write to
-;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;; along with GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
;;; Commentary:
-;;; ========================================================================
-;;; "No matter how hard you try, you can't make a racehorse out of a pig.
-;;; 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
-;;; still not going to make it go faster than 70 mph, but it might be easier
-;;; to get it there.
-;;;
+;; ========================================================================
+;; "No matter how hard you try, you can't make a racehorse out of a pig.
+;; 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
+;; still not going to make it go faster than 70 mph, but it might be easier
+;; to get it there.
+;;
-;;; TO DO:
-;;;
-;;; (apply '(lambda (x &rest y) ...) 1 (foo))
-;;;
-;;; maintain a list of functions known not to access any global variables
-;;; (actually, give them a 'dynamically-safe property) and then
-;;; (let ( v1 v2 ... vM vN ) <...dynamically-safe...> ) ==>
-;;; (let ( v1 v2 ... vM ) vN <...dynamically-safe...> )
-;;; by recursing on this, we might be able to eliminate the entire let.
-;;; However certain variables should never have their bindings optimized
-;;; away, because they affect everything.
-;;; (put 'debug-on-error 'binding-is-magic t)
-;;; (put 'debug-on-abort 'binding-is-magic t)
-;;; (put 'debug-on-next-call 'binding-is-magic t)
-;;; (put 'mocklisp-arguments 'binding-is-magic t)
-;;; (put 'inhibit-quit 'binding-is-magic t)
-;;; (put 'quit-flag 'binding-is-magic t)
-;;; (put 't 'binding-is-magic t)
-;;; (put 'nil 'binding-is-magic t)
-;;; possibly also
-;;; (put 'gc-cons-threshold 'binding-is-magic t)
-;;; (put 'track-mouse 'binding-is-magic t)
-;;; others?
-;;;
-;;; Simple defsubsts often produce forms like
-;;; (let ((v1 (f1)) (v2 (f2)) ...)
-;;; (FN v1 v2 ...))
-;;; 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
-;;; 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
-;;; 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
-;;; be to allow type-specification of the form
-;;; (put 'foo 'arg-types '(float (list integer) dynamic))
-;;; (put 'foo 'result-type 'bool)
-;;; It should be possible to have these types checked to a certain
-;;; degree.
-;;;
-;;; collapse common subexpressions
-;;;
-;;; It would be nice if redundant sequences could be factored out as well,
-;;; when they are known to have no side-effects:
-;;; (list (+ a b c) (+ a b c)) --> a b add c add dup list-2
-;;; but beware of traps like
-;;; (cons (list x y) (list x y))
-;;;
-;;; Tail-recursion elimination is not really possible in Emacs Lisp.
-;;; Tail-recursion elimination is almost always impossible when all variables
-;;; have dynamic scope, but given that the "return" byteop requires the
-;;; binding stack to be empty (rather than emptying it itself), there can be
-;;; no truly tail-recursive Emacs Lisp functions that take any arguments or
-;;; make any bindings.
-;;;
-;;; Here is an example of an Emacs Lisp function which could safely be
-;;; byte-compiled tail-recursively:
-;;;
-;;; (defun tail-map (fn list)
-;;; (cond (list
-;;; (funcall fn (car list))
-;;; (tail-map fn (cdr list)))))
-;;;
-;;; 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
-;;; Bunbind_all byteop would fix this.
-;;;
-;;; (defun foo (x y z) ... (foo a b c))
-;;; ... (const foo) (varref a) (varref b) (varref c) (call 3) END: (return)
-;;; ... (varref a) (varbind x) (varref b) (varbind y) (varref c) (varbind z) (goto 0) END: (unbind-all) (return)
-;;; ... (varref a) (varset x) (varref b) (varset y) (varref c) (varset z) (goto 0) END: (return)
-;;;
-;;; this also can be considered tail recursion:
-;;;
-;;; ... (const foo) (varref a) (call 1) (goto X) ... X: (return)
-;;; could generalize this by doing the optimization
-;;; (goto X) ... X: (return) --> (return)
-;;;
-;;; But this doesn't solve all of the problems: although by doing tail-
-;;; recursion elimination in this way, the call-stack does not grow, the
-;;; binding-stack would grow with each recursive step, and would eventually
-;;; overflow. I don't believe there is any way around this without lexical
-;;; scope.
-;;;
-;;; 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
-;;; 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
-;;; always be dynamic, and attempting to do a lexical binding of them
-;;; should simply do a dynamic binding instead.
-;;; But! We need to know about variables that were not necessarily defvarred
-;;; 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).
-;;; 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
-;;; in some grody way, but that's a really bad idea.)
+;; TO DO:
+;;
+;; (apply '(lambda (x &rest y) ...) 1 (foo))
+;;
+;; maintain a list of functions known not to access any global variables
+;; (actually, give them a 'dynamically-safe property) and then
+;; (let ( v1 v2 ... vM vN ) <...dynamically-safe...> ) ==>
+;; (let ( v1 v2 ... vM ) vN <...dynamically-safe...> )
+;; by recursing on this, we might be able to eliminate the entire let.
+;; However certain variables should never have their bindings optimized
+;; away, because they affect everything.
+;; (put 'debug-on-error 'binding-is-magic t)
+;; (put 'debug-on-abort 'binding-is-magic t)
+;; (put 'debug-on-next-call 'binding-is-magic t)
+;; (put 'mocklisp-arguments 'binding-is-magic t)
+;; (put 'inhibit-quit 'binding-is-magic t)
+;; (put 'quit-flag 'binding-is-magic t)
+;; (put 't 'binding-is-magic t)
+;; (put 'nil 'binding-is-magic t)
+;; possibly also
+;; (put 'gc-cons-threshold 'binding-is-magic t)
+;; (put 'track-mouse 'binding-is-magic t)
+;; others?
+;;
+;; Simple defsubsts often produce forms like
+;; (let ((v1 (f1)) (v2 (f2)) ...)
+;; (FN v1 v2 ...))
+;; 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
+;; 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
+;; 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
+;; be to allow type-specification of the form
+;; (put 'foo 'arg-types '(float (list integer) dynamic))
+;; (put 'foo 'result-type 'bool)
+;; It should be possible to have these types checked to a certain
+;; degree.
+;;
+;; collapse common subexpressions
+;;
+;; It would be nice if redundant sequences could be factored out as well,
+;; when they are known to have no side-effects:
+;; (list (+ a b c) (+ a b c)) --> a b add c add dup list-2
+;; but beware of traps like
+;; (cons (list x y) (list x y))
+;;
+;; Tail-recursion elimination is not really possible in Emacs Lisp.
+;; Tail-recursion elimination is almost always impossible when all variables
+;; have dynamic scope, but given that the "return" byteop requires the
+;; binding stack to be empty (rather than emptying it itself), there can be
+;; no truly tail-recursive Emacs Lisp functions that take any arguments or
+;; make any bindings.
+;;
+;; Here is an example of an Emacs Lisp function which could safely be
+;; byte-compiled tail-recursively:
+;;
+;; (defun tail-map (fn list)
+;; (cond (list
+;; (funcall fn (car list))
+;; (tail-map fn (cdr list)))))
+;;
+;; 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
+;; Bunbind_all byteop would fix this.
+;;
+;; (defun foo (x y z) ... (foo a b c))
+;; ... (const foo) (varref a) (varref b) (varref c) (call 3) END: (return)
+;; ... (varref a) (varbind x) (varref b) (varbind y) (varref c) (varbind z) (goto 0) END: (unbind-all) (return)
+;; ... (varref a) (varset x) (varref b) (varset y) (varref c) (varset z) (goto 0) END: (return)
+;;
+;; this also can be considered tail recursion:
+;;
+;; ... (const foo) (varref a) (call 1) (goto X) ... X: (return)
+;; could generalize this by doing the optimization
+;; (goto X) ... X: (return) --> (return)
+;;
+;; But this doesn't solve all of the problems: although by doing tail-
+;; recursion elimination in this way, the call-stack does not grow, the
+;; binding-stack would grow with each recursive step, and would eventually
+;; overflow. I don't believe there is any way around this without lexical
+;; scope.
+;;
+;; 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
+;; 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
+;; always be dynamic, and attempting to do a lexical binding of them
+;; should simply do a dynamic binding instead.
+;; But! We need to know about variables that were not necessarily defvarred
+;; 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).
+;; 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
+;; in some grody way, but that's a really bad idea.)
;; Other things to consider:
form)
;; else
(if (and (consp fn) (eq (car fn) 'autoload))
- (load (nth 1 fn)))
+ (progn
+ (load (nth 1 fn))
+ (setq fn (or (cdr (assq name byte-compile-function-environment))
+ (and (fboundp name) (symbol-function name))))))
(if (and (consp fn) (eq (car fn) 'autoload))
(error "file \"%s\" didn't define \"%s\"" (nth 1 fn) name))
(if (symbolp fn)
(byte-compile-inline-expand (cons fn (cdr form)))
(if (byte-code-function-p fn)
- (progn
+ (let (string)
(fetch-bytecode fn)
+ (setq string (aref fn 1))
+ (if (fboundp 'string-as-unibyte)
+ (setq string (string-as-unibyte string)))
(cons (list 'lambda (aref fn 0)
- (list 'byte-code (aref fn 1) (aref fn 2) (aref fn 3)))
+ (list 'byte-code string (aref fn 2) (aref fn 3)))
(cdr form)))
(if (not (eq (car fn) 'lambda)) (error "%s is not a lambda" name))
(cons fn (cdr form)))))))
(byte-compile-warn
"attempt to open-code %s with too many arguments" name))
form)
+ (setq body (mapcar 'byte-optimize-form body))
(let ((newform
(if bindings
(cons 'let (cons (nreverse bindings) body))
(cons (byte-optimize-form (nth 2 form) for-effect)
(byte-optimize-body (cdr (cdr (cdr form))) t)))))
- ((memq fn '(save-excursion save-restriction))
+ ((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
(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!
;; form))
(defun byte-optimize-approx-equal (x y)
- (< (* (abs (- x y)) 100) (abs (+ x y))))
+ (<= (* (abs (- x y)) 100) (abs (+ x y))))
;; Collect all the constants from FORM, after the STARTth arg,
;; and apply FUN to them to make one argument at the end.
;;; (actually, it would be safe if we know the sole arg
;;; is not a marker).
;; ((null (cdr (cdr form))) (nth 1 form))
+ ((and (null (nthcdr 3 form))
+ (or (memq (nth 1 form) '(1 -1))
+ (memq (nth 2 form) '(1 -1))))
+ ;; Optimize (+ x 1) into (1+ x) and (+ x -1) into (1- x).
+ (let ((integer
+ (if (memq (nth 1 form) '(1 -1))
+ (nth 1 form)
+ (nth 2 form)))
+ (other
+ (if (memq (nth 1 form) '(1 -1))
+ (nth 2 form)
+ (nth 1 form))))
+ (list (if (eq integer 1) '1+ '1-)
+ other)))
(t form)))
(defun byte-optimize-minus (form)
;; (- x y ... 0) --> (- x y ...)
(setq form (copy-sequence form))
(setcdr (cdr (cdr form)) (delq 0 (nthcdr 3 form))))
+ ((equal (nthcdr 2 form) '(1))
+ (setq form (list '1- (nth 1 form))))
+ ((equal (nthcdr 2 form) '(-1))
+ (setq form (list '1+ (nth 1 form))))
;; If form is (- CONST foo... CONST), merge first and last.
((and (numberp (nth 1 form))
(numberp last))
(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
;;; value is not used. That is, something like
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-member byte-assq byte-quo byte-rem)
byte-compile-side-effect-and-error-free-ops))
-;;; This piece of shit is because of the way DEFVAR_BOOL() variables work.
+;;; This crock is because of the way DEFVAR_BOOL variables work.
;;; Consider the code
;;;
;;; (defun foo (flag)
;;; the BOOL variables are, and not perform this optimization on them.
;;;
(defconst byte-boolean-vars
- '(abbrev-all-caps abbrevs-changed byte-metering-on
- cannot-suspend completion-auto-help completion-ignore-case
- cursor-in-echo-area debug-on-next-call debug-on-quit
- delete-exited-processes enable-recursive-minibuffers
- highlight-nonselected-windows indent-tabs-mode inhibit-local-menu-bar-menus
- insert-default-directory inverse-video load-force-doc-strings
- load-in-progress menu-prompting minibuffer-auto-raise
- mode-line-inverse-video multiple-frames no-redraw-on-reenter noninteractive
- parse-sexp-ignore-comments pop-up-frames pop-up-windows
- print-escape-newlines system-uses-terminfo truncate-partial-width-windows
+ '(abbrev-all-caps abbrevs-changed byte-debug-flag byte-metering-on
+ cannot-suspend check-markers-debug-flag completion-auto-help
+ completion-ignore-case cursor-in-echo-area debug-on-next-call
+ debug-on-quit delete-exited-processes enable-recursive-minibuffers
+ garbage-collection-messages highlight-nonselected-windows
+ indent-tabs-mode inherit-process-coding-system inhibit-eol-conversion
+ inhibit-local-menu-bar-menus insert-default-directory inverse-video
+ keyword-symbols-constant-flag load-convert-to-unibyte
+ load-force-doc-strings load-in-progress menu-prompting
+ minibuffer-allow-text-properties minibuffer-auto-raise
+ mode-line-inverse-video multiple-frames no-redraw-on-reenter
+ noninteractive parse-sexp-ignore-comments parse-sexp-lookup-properties
+ pop-up-frames pop-up-windows print-escape-multibyte
+ print-escape-newlines
+ print-escape-nonascii print-quoted scroll-preserve-screen-position
+ system-uses-terminfo truncate-partial-width-windows use-dialog-box
visible-bell vms-stmlf-recfm words-include-escapes)
"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
(setq lap0 (car rest)
lap1 (nth 1 rest))
(if (memq (car lap0) byte-constref-ops)
- (if (eq (cdr lap0) 'byte-constant)
+ (if (not (eq (car lap0) 'byte-constant))
(or (memq (cdr lap0) byte-compile-variables)
(setq byte-compile-variables (cons (cdr lap0)
byte-compile-variables)))