Mercurial > emacs
comparison lisp/emacs-lisp/byte-opt.el @ 14169:83f275dcd93a
Update FSF's address.
author | Erik Naggum <erik@naggum.no> |
---|---|
date | Sun, 14 Jan 1996 07:34:30 +0000 |
parents | 10c76db77107 |
children | 4706508583bd |
comparison
equal
deleted
inserted
replaced
14168:3b925cc52931 | 14169:83f275dcd93a |
---|---|
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | 17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 ;; GNU General Public License for more details. | 19 ;; GNU General Public License for more details. |
20 | 20 |
21 ;; You should have received a copy of the GNU General Public License | 21 ;; You should have received a copy of the GNU General Public License |
22 ;; along with GNU Emacs; see the file COPYING. If not, write to | 22 ;; along with GNU Emacs; see the file COPYING. If not, write to the |
23 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | 23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
24 ;; Boston, MA 02111-1307, USA. | |
24 | 25 |
25 ;;; Commentary: | 26 ;;; Commentary: |
26 | 27 |
27 ;;; ======================================================================== | 28 ;; ======================================================================== |
28 ;;; "No matter how hard you try, you can't make a racehorse out of a pig. | 29 ;; "No matter how hard you try, you can't make a racehorse out of a pig. |
29 ;;; You can, however, make a faster pig." | 30 ;; You can, however, make a faster pig." |
30 ;;; | 31 ;; |
31 ;;; Or, to put it another way, the emacs byte compiler is a VW Bug. This code | 32 ;; Or, to put it another way, the emacs byte compiler is a VW Bug. This code |
32 ;;; makes it be a VW Bug with fuel injection and a turbocharger... You're | 33 ;; makes it be a VW Bug with fuel injection and a turbocharger... You're |
33 ;;; still not going to make it go faster than 70 mph, but it might be easier | 34 ;; still not going to make it go faster than 70 mph, but it might be easier |
34 ;;; to get it there. | 35 ;; to get it there. |
35 ;;; | 36 ;; |
36 | 37 |
37 ;;; TO DO: | 38 ;; TO DO: |
38 ;;; | 39 ;; |
39 ;;; (apply '(lambda (x &rest y) ...) 1 (foo)) | 40 ;; (apply '(lambda (x &rest y) ...) 1 (foo)) |
40 ;;; | 41 ;; |
41 ;;; maintain a list of functions known not to access any global variables | 42 ;; maintain a list of functions known not to access any global variables |
42 ;;; (actually, give them a 'dynamically-safe property) and then | 43 ;; (actually, give them a 'dynamically-safe property) and then |
43 ;;; (let ( v1 v2 ... vM vN ) <...dynamically-safe...> ) ==> | 44 ;; (let ( v1 v2 ... vM vN ) <...dynamically-safe...> ) ==> |
44 ;;; (let ( v1 v2 ... vM ) vN <...dynamically-safe...> ) | 45 ;; (let ( v1 v2 ... vM ) vN <...dynamically-safe...> ) |
45 ;;; by recursing on this, we might be able to eliminate the entire let. | 46 ;; by recursing on this, we might be able to eliminate the entire let. |
46 ;;; However certain variables should never have their bindings optimized | 47 ;; However certain variables should never have their bindings optimized |
47 ;;; away, because they affect everything. | 48 ;; away, because they affect everything. |
48 ;;; (put 'debug-on-error 'binding-is-magic t) | 49 ;; (put 'debug-on-error 'binding-is-magic t) |
49 ;;; (put 'debug-on-abort 'binding-is-magic t) | 50 ;; (put 'debug-on-abort 'binding-is-magic t) |
50 ;;; (put 'debug-on-next-call 'binding-is-magic t) | 51 ;; (put 'debug-on-next-call 'binding-is-magic t) |
51 ;;; (put 'mocklisp-arguments 'binding-is-magic t) | 52 ;; (put 'mocklisp-arguments 'binding-is-magic t) |
52 ;;; (put 'inhibit-quit 'binding-is-magic t) | 53 ;; (put 'inhibit-quit 'binding-is-magic t) |
53 ;;; (put 'quit-flag 'binding-is-magic t) | 54 ;; (put 'quit-flag 'binding-is-magic t) |
54 ;;; (put 't 'binding-is-magic t) | 55 ;; (put 't 'binding-is-magic t) |
55 ;;; (put 'nil 'binding-is-magic t) | 56 ;; (put 'nil 'binding-is-magic t) |
56 ;;; possibly also | 57 ;; possibly also |
57 ;;; (put 'gc-cons-threshold 'binding-is-magic t) | 58 ;; (put 'gc-cons-threshold 'binding-is-magic t) |
58 ;;; (put 'track-mouse 'binding-is-magic t) | 59 ;; (put 'track-mouse 'binding-is-magic t) |
59 ;;; others? | 60 ;; others? |
60 ;;; | 61 ;; |
61 ;;; Simple defsubsts often produce forms like | 62 ;; Simple defsubsts often produce forms like |
62 ;;; (let ((v1 (f1)) (v2 (f2)) ...) | 63 ;; (let ((v1 (f1)) (v2 (f2)) ...) |
63 ;;; (FN v1 v2 ...)) | 64 ;; (FN v1 v2 ...)) |
64 ;;; It would be nice if we could optimize this to | 65 ;; It would be nice if we could optimize this to |
65 ;;; (FN (f1) (f2) ...) | 66 ;; (FN (f1) (f2) ...) |
66 ;;; but we can't unless FN is dynamically-safe (it might be dynamically | 67 ;; but we can't unless FN is dynamically-safe (it might be dynamically |
67 ;;; referring to the bindings that the lambda arglist established.) | 68 ;; referring to the bindings that the lambda arglist established.) |
68 ;;; One of the uncountable lossages introduced by dynamic scope... | 69 ;; One of the uncountable lossages introduced by dynamic scope... |
69 ;;; | 70 ;; |
70 ;;; Maybe there should be a control-structure that says "turn on | 71 ;; Maybe there should be a control-structure that says "turn on |
71 ;;; fast-and-loose type-assumptive optimizations here." Then when | 72 ;; fast-and-loose type-assumptive optimizations here." Then when |
72 ;;; we see a form like (car foo) we can from then on assume that | 73 ;; we see a form like (car foo) we can from then on assume that |
73 ;;; the variable foo is of type cons, and optimize based on that. | 74 ;; the variable foo is of type cons, and optimize based on that. |
74 ;;; But, this won't win much because of (you guessed it) dynamic | 75 ;; But, this won't win much because of (you guessed it) dynamic |
75 ;;; scope. Anything down the stack could change the value. | 76 ;; scope. Anything down the stack could change the value. |
76 ;;; (Another reason it doesn't work is that it is perfectly valid | 77 ;; (Another reason it doesn't work is that it is perfectly valid |
77 ;;; to call car with a null argument.) A better approach might | 78 ;; to call car with a null argument.) A better approach might |
78 ;;; be to allow type-specification of the form | 79 ;; be to allow type-specification of the form |
79 ;;; (put 'foo 'arg-types '(float (list integer) dynamic)) | 80 ;; (put 'foo 'arg-types '(float (list integer) dynamic)) |
80 ;;; (put 'foo 'result-type 'bool) | 81 ;; (put 'foo 'result-type 'bool) |
81 ;;; It should be possible to have these types checked to a certain | 82 ;; It should be possible to have these types checked to a certain |
82 ;;; degree. | 83 ;; degree. |
83 ;;; | 84 ;; |
84 ;;; collapse common subexpressions | 85 ;; collapse common subexpressions |
85 ;;; | 86 ;; |
86 ;;; It would be nice if redundant sequences could be factored out as well, | 87 ;; It would be nice if redundant sequences could be factored out as well, |
87 ;;; when they are known to have no side-effects: | 88 ;; when they are known to have no side-effects: |
88 ;;; (list (+ a b c) (+ a b c)) --> a b add c add dup list-2 | 89 ;; (list (+ a b c) (+ a b c)) --> a b add c add dup list-2 |
89 ;;; but beware of traps like | 90 ;; but beware of traps like |
90 ;;; (cons (list x y) (list x y)) | 91 ;; (cons (list x y) (list x y)) |
91 ;;; | 92 ;; |
92 ;;; Tail-recursion elimination is not really possible in Emacs Lisp. | 93 ;; Tail-recursion elimination is not really possible in Emacs Lisp. |
93 ;;; Tail-recursion elimination is almost always impossible when all variables | 94 ;; Tail-recursion elimination is almost always impossible when all variables |
94 ;;; have dynamic scope, but given that the "return" byteop requires the | 95 ;; have dynamic scope, but given that the "return" byteop requires the |
95 ;;; binding stack to be empty (rather than emptying it itself), there can be | 96 ;; binding stack to be empty (rather than emptying it itself), there can be |
96 ;;; no truly tail-recursive Emacs Lisp functions that take any arguments or | 97 ;; no truly tail-recursive Emacs Lisp functions that take any arguments or |
97 ;;; make any bindings. | 98 ;; make any bindings. |
98 ;;; | 99 ;; |
99 ;;; Here is an example of an Emacs Lisp function which could safely be | 100 ;; Here is an example of an Emacs Lisp function which could safely be |
100 ;;; byte-compiled tail-recursively: | 101 ;; byte-compiled tail-recursively: |
101 ;;; | 102 ;; |
102 ;;; (defun tail-map (fn list) | 103 ;; (defun tail-map (fn list) |
103 ;;; (cond (list | 104 ;; (cond (list |
104 ;;; (funcall fn (car list)) | 105 ;; (funcall fn (car list)) |
105 ;;; (tail-map fn (cdr list))))) | 106 ;; (tail-map fn (cdr list))))) |
106 ;;; | 107 ;; |
107 ;;; However, if there was even a single let-binding around the COND, | 108 ;; However, if there was even a single let-binding around the COND, |
108 ;;; it could not be byte-compiled, because there would be an "unbind" | 109 ;; it could not be byte-compiled, because there would be an "unbind" |
109 ;;; byte-op between the final "call" and "return." Adding a | 110 ;; byte-op between the final "call" and "return." Adding a |
110 ;;; Bunbind_all byteop would fix this. | 111 ;; Bunbind_all byteop would fix this. |
111 ;;; | 112 ;; |
112 ;;; (defun foo (x y z) ... (foo a b c)) | 113 ;; (defun foo (x y z) ... (foo a b c)) |
113 ;;; ... (const foo) (varref a) (varref b) (varref c) (call 3) END: (return) | 114 ;; ... (const foo) (varref a) (varref b) (varref c) (call 3) END: (return) |
114 ;;; ... (varref a) (varbind x) (varref b) (varbind y) (varref c) (varbind z) (goto 0) END: (unbind-all) (return) | 115 ;; ... (varref a) (varbind x) (varref b) (varbind y) (varref c) (varbind z) (goto 0) END: (unbind-all) (return) |
115 ;;; ... (varref a) (varset x) (varref b) (varset y) (varref c) (varset z) (goto 0) END: (return) | 116 ;; ... (varref a) (varset x) (varref b) (varset y) (varref c) (varset z) (goto 0) END: (return) |
116 ;;; | 117 ;; |
117 ;;; this also can be considered tail recursion: | 118 ;; this also can be considered tail recursion: |
118 ;;; | 119 ;; |
119 ;;; ... (const foo) (varref a) (call 1) (goto X) ... X: (return) | 120 ;; ... (const foo) (varref a) (call 1) (goto X) ... X: (return) |
120 ;;; could generalize this by doing the optimization | 121 ;; could generalize this by doing the optimization |
121 ;;; (goto X) ... X: (return) --> (return) | 122 ;; (goto X) ... X: (return) --> (return) |
122 ;;; | 123 ;; |
123 ;;; But this doesn't solve all of the problems: although by doing tail- | 124 ;; But this doesn't solve all of the problems: although by doing tail- |
124 ;;; recursion elimination in this way, the call-stack does not grow, the | 125 ;; recursion elimination in this way, the call-stack does not grow, the |
125 ;;; binding-stack would grow with each recursive step, and would eventually | 126 ;; binding-stack would grow with each recursive step, and would eventually |
126 ;;; overflow. I don't believe there is any way around this without lexical | 127 ;; overflow. I don't believe there is any way around this without lexical |
127 ;;; scope. | 128 ;; scope. |
128 ;;; | 129 ;; |
129 ;;; Wouldn't it be nice if Emacs Lisp had lexical scope. | 130 ;; Wouldn't it be nice if Emacs Lisp had lexical scope. |
130 ;;; | 131 ;; |
131 ;;; Idea: the form (lexical-scope) in a file means that the file may be | 132 ;; Idea: the form (lexical-scope) in a file means that the file may be |
132 ;;; compiled lexically. This proclamation is file-local. Then, within | 133 ;; compiled lexically. This proclamation is file-local. Then, within |
133 ;;; that file, "let" would establish lexical bindings, and "let-dynamic" | 134 ;; that file, "let" would establish lexical bindings, and "let-dynamic" |
134 ;;; would do things the old way. (Or we could use CL "declare" forms.) | 135 ;; would do things the old way. (Or we could use CL "declare" forms.) |
135 ;;; We'd have to notice defvars and defconsts, since those variables should | 136 ;; We'd have to notice defvars and defconsts, since those variables should |
136 ;;; always be dynamic, and attempting to do a lexical binding of them | 137 ;; always be dynamic, and attempting to do a lexical binding of them |
137 ;;; should simply do a dynamic binding instead. | 138 ;; should simply do a dynamic binding instead. |
138 ;;; But! We need to know about variables that were not necessarily defvarred | 139 ;; But! We need to know about variables that were not necessarily defvarred |
139 ;;; in the file being compiled (doing a boundp check isn't good enough.) | 140 ;; in the file being compiled (doing a boundp check isn't good enough.) |
140 ;;; Fdefvar() would have to be modified to add something to the plist. | 141 ;; Fdefvar() would have to be modified to add something to the plist. |
141 ;;; | 142 ;; |
142 ;;; A major disadvantage of this scheme is that the interpreter and compiler | 143 ;; A major disadvantage of this scheme is that the interpreter and compiler |
143 ;;; would have different semantics for files compiled with (dynamic-scope). | 144 ;; would have different semantics for files compiled with (dynamic-scope). |
144 ;;; Since this would be a file-local optimization, there would be no way to | 145 ;; Since this would be a file-local optimization, there would be no way to |
145 ;;; modify the interpreter to obey this (unless the loader was hacked | 146 ;; modify the interpreter to obey this (unless the loader was hacked |
146 ;;; in some grody way, but that's a really bad idea.) | 147 ;; in some grody way, but that's a really bad idea.) |
147 | 148 |
148 ;; Other things to consider: | 149 ;; Other things to consider: |
149 | 150 |
150 ;;;;; Associative math should recognize subcalls to identical function: | 151 ;;;;; Associative math should recognize subcalls to identical function: |
151 ;;;(disassemble (lambda (x) (+ (+ (foo) 1) (+ (bar) 2)))) | 152 ;;;(disassemble (lambda (x) (+ (+ (foo) 1) (+ (bar) 2)))) |