comparison lisp/byte-optimize.el @ 251:677f6a0ee643 r20-5b24

Import from CVS: tag r20-5b24
author cvs
date Mon, 13 Aug 2007 10:19:59 +0200
parents 51092a27c943
children 6330739388db
comparison
equal deleted inserted replaced
250:f385a461c9aa 251:677f6a0ee643
25 25
26 ;;; Synched up with: FSF 19.30. 26 ;;; Synched up with: FSF 19.30.
27 27
28 ;;; Commentary: 28 ;;; Commentary:
29 29
30 ;;; ======================================================================== 30 ;; ========================================================================
31 ;;; "No matter how hard you try, you can't make a racehorse out of a pig. 31 ;; "No matter how hard you try, you can't make a racehorse out of a pig.
32 ;;; You can, however, make a faster pig." 32 ;; You can, however, make a faster pig."
33 ;;; 33 ;;
34 ;;; Or, to put it another way, the emacs byte compiler is a VW Bug. This code 34 ;; Or, to put it another way, the emacs byte compiler is a VW Bug. This code
35 ;;; makes it be a VW Bug with fuel injection and a turbocharger... You're 35 ;; makes it be a VW Bug with fuel injection and a turbocharger... You're
36 ;;; still not going to make it go faster than 70 mph, but it might be easier 36 ;; still not going to make it go faster than 70 mph, but it might be easier
37 ;;; to get it there. 37 ;; to get it there.
38 ;;; 38 ;;
39 39
40 ;;; TO DO: 40 ;; TO DO:
41 ;;; 41 ;;
42 ;;; (apply '(lambda (x &rest y) ...) 1 (foo)) 42 ;; (apply '(lambda (x &rest y) ...) 1 (foo))
43 ;;; 43 ;;
44 ;;; maintain a list of functions known not to access any global variables 44 ;; maintain a list of functions known not to access any global variables
45 ;;; (actually, give them a 'dynamically-safe property) and then 45 ;; (actually, give them a 'dynamically-safe property) and then
46 ;;; (let ( v1 v2 ... vM vN ) <...dynamically-safe...> ) ==> 46 ;; (let ( v1 v2 ... vM vN ) <...dynamically-safe...> ) ==>
47 ;;; (let ( v1 v2 ... vM ) vN <...dynamically-safe...> ) 47 ;; (let ( v1 v2 ... vM ) vN <...dynamically-safe...> )
48 ;;; by recursing on this, we might be able to eliminate the entire let. 48 ;; by recursing on this, we might be able to eliminate the entire let.
49 ;;; However certain variables should never have their bindings optimized 49 ;; However certain variables should never have their bindings optimized
50 ;;; away, because they affect everything. 50 ;; away, because they affect everything.
51 ;;; (put 'debug-on-error 'binding-is-magic t) 51 ;; (put 'debug-on-error 'binding-is-magic t)
52 ;;; (put 'debug-on-abort 'binding-is-magic t) 52 ;; (put 'debug-on-abort 'binding-is-magic t)
53 ;;; (put 'debug-on-next-call 'binding-is-magic t) 53 ;; (put 'debug-on-next-call 'binding-is-magic t)
54 ;;; (put 'mocklisp-arguments 'binding-is-magic t) 54 ;; (put 'mocklisp-arguments 'binding-is-magic t)
55 ;;; (put 'inhibit-quit 'binding-is-magic t) 55 ;; (put 'inhibit-quit 'binding-is-magic t)
56 ;;; (put 'quit-flag 'binding-is-magic t) 56 ;; (put 'quit-flag 'binding-is-magic t)
57 ;;; (put 't 'binding-is-magic t) 57 ;; (put 't 'binding-is-magic t)
58 ;;; (put 'nil 'binding-is-magic t) 58 ;; (put 'nil 'binding-is-magic t)
59 ;;; possibly also 59 ;; possibly also
60 ;;; (put 'gc-cons-threshold 'binding-is-magic t) 60 ;; (put 'gc-cons-threshold 'binding-is-magic t)
61 ;;; (put 'track-mouse 'binding-is-magic t) 61 ;; (put 'track-mouse 'binding-is-magic t)
62 ;;; others? 62 ;; others?
63 ;;; 63 ;;
64 ;;; Simple defsubsts often produce forms like 64 ;; Simple defsubsts often produce forms like
65 ;;; (let ((v1 (f1)) (v2 (f2)) ...) 65 ;; (let ((v1 (f1)) (v2 (f2)) ...)
66 ;;; (FN v1 v2 ...)) 66 ;; (FN v1 v2 ...))
67 ;;; It would be nice if we could optimize this to 67 ;; It would be nice if we could optimize this to
68 ;;; (FN (f1) (f2) ...) 68 ;; (FN (f1) (f2) ...)
69 ;;; but we can't unless FN is dynamically-safe (it might be dynamically 69 ;; but we can't unless FN is dynamically-safe (it might be dynamically
70 ;;; referring to the bindings that the lambda arglist established.) 70 ;; referring to the bindings that the lambda arglist established.)
71 ;;; One of the uncountable lossages introduced by dynamic scope... 71 ;; One of the uncountable lossages introduced by dynamic scope...
72 ;;; 72 ;;
73 ;;; Maybe there should be a control-structure that says "turn on 73 ;; Maybe there should be a control-structure that says "turn on
74 ;;; fast-and-loose type-assumptive optimizations here." Then when 74 ;; fast-and-loose type-assumptive optimizations here." Then when
75 ;;; we see a form like (car foo) we can from then on assume that 75 ;; we see a form like (car foo) we can from then on assume that
76 ;;; the variable foo is of type cons, and optimize based on that. 76 ;; the variable foo is of type cons, and optimize based on that.
77 ;;; But, this won't win much because of (you guessed it) dynamic 77 ;; But, this won't win much because of (you guessed it) dynamic
78 ;;; scope. Anything down the stack could change the value. 78 ;; scope. Anything down the stack could change the value.
79 ;;; (Another reason it doesn't work is that it is perfectly valid 79 ;; (Another reason it doesn't work is that it is perfectly valid
80 ;;; to call car with a null argument.) A better approach might 80 ;; to call car with a null argument.) A better approach might
81 ;;; be to allow type-specification of the form 81 ;; be to allow type-specification of the form
82 ;;; (put 'foo 'arg-types '(float (list integer) dynamic)) 82 ;; (put 'foo 'arg-types '(float (list integer) dynamic))
83 ;;; (put 'foo 'result-type 'bool) 83 ;; (put 'foo 'result-type 'bool)
84 ;;; It should be possible to have these types checked to a certain 84 ;; It should be possible to have these types checked to a certain
85 ;;; degree. 85 ;; degree.
86 ;;; 86 ;;
87 ;;; collapse common subexpressions 87 ;; collapse common subexpressions
88 ;;; 88 ;;
89 ;;; It would be nice if redundant sequences could be factored out as well, 89 ;; It would be nice if redundant sequences could be factored out as well,
90 ;;; when they are known to have no side-effects: 90 ;; when they are known to have no side-effects:
91 ;;; (list (+ a b c) (+ a b c)) --> a b add c add dup list-2 91 ;; (list (+ a b c) (+ a b c)) --> a b add c add dup list-2
92 ;;; but beware of traps like 92 ;; but beware of traps like
93 ;;; (cons (list x y) (list x y)) 93 ;; (cons (list x y) (list x y))
94 ;;; 94 ;;
95 ;;; Tail-recursion elimination is not really possible in Emacs Lisp. 95 ;; Tail-recursion elimination is not really possible in Emacs Lisp.
96 ;;; Tail-recursion elimination is almost always impossible when all variables 96 ;; Tail-recursion elimination is almost always impossible when all variables
97 ;;; have dynamic scope, but given that the "return" byteop requires the 97 ;; have dynamic scope, but given that the "return" byteop requires the
98 ;;; binding stack to be empty (rather than emptying it itself), there can be 98 ;; binding stack to be empty (rather than emptying it itself), there can be
99 ;;; no truly tail-recursive Emacs Lisp functions that take any arguments or 99 ;; no truly tail-recursive Emacs Lisp functions that take any arguments or
100 ;;; make any bindings. 100 ;; make any bindings.
101 ;;; 101 ;;
102 ;;; Here is an example of an Emacs Lisp function which could safely be 102 ;; Here is an example of an Emacs Lisp function which could safely be
103 ;;; byte-compiled tail-recursively: 103 ;; byte-compiled tail-recursively:
104 ;;; 104 ;;
105 ;;; (defun tail-map (fn list) 105 ;; (defun tail-map (fn list)
106 ;;; (cond (list 106 ;; (cond (list
107 ;;; (funcall fn (car list)) 107 ;; (funcall fn (car list))
108 ;;; (tail-map fn (cdr list))))) 108 ;; (tail-map fn (cdr list)))))
109 ;;; 109 ;;
110 ;;; However, if there was even a single let-binding around the COND, 110 ;; However, if there was even a single let-binding around the COND,
111 ;;; it could not be byte-compiled, because there would be an "unbind" 111 ;; it could not be byte-compiled, because there would be an "unbind"
112 ;;; byte-op between the final "call" and "return." Adding a 112 ;; byte-op between the final "call" and "return." Adding a
113 ;;; Bunbind_all byteop would fix this. 113 ;; Bunbind_all byteop would fix this.
114 ;;; 114 ;;
115 ;;; (defun foo (x y z) ... (foo a b c)) 115 ;; (defun foo (x y z) ... (foo a b c))
116 ;;; ... (const foo) (varref a) (varref b) (varref c) (call 3) END: (return) 116 ;; ... (const foo) (varref a) (varref b) (varref c) (call 3) END: (return)
117 ;;; ... (varref a) (varbind x) (varref b) (varbind y) (varref c) (varbind z) (goto 0) END: (unbind-all) (return) 117 ;; ... (varref a) (varbind x) (varref b) (varbind y) (varref c) (varbind z) (goto 0) END: (unbind-all) (return)
118 ;;; ... (varref a) (varset x) (varref b) (varset y) (varref c) (varset z) (goto 0) END: (return) 118 ;; ... (varref a) (varset x) (varref b) (varset y) (varref c) (varset z) (goto 0) END: (return)
119 ;;; 119 ;;
120 ;;; this also can be considered tail recursion: 120 ;; this also can be considered tail recursion:
121 ;;; 121 ;;
122 ;;; ... (const foo) (varref a) (call 1) (goto X) ... X: (return) 122 ;; ... (const foo) (varref a) (call 1) (goto X) ... X: (return)
123 ;;; could generalize this by doing the optimization 123 ;; could generalize this by doing the optimization
124 ;;; (goto X) ... X: (return) --> (return) 124 ;; (goto X) ... X: (return) --> (return)
125 ;;; 125 ;;
126 ;;; But this doesn't solve all of the problems: although by doing tail- 126 ;; But this doesn't solve all of the problems: although by doing tail-
127 ;;; recursion elimination in this way, the call-stack does not grow, the 127 ;; recursion elimination in this way, the call-stack does not grow, the
128 ;;; binding-stack would grow with each recursive step, and would eventually 128 ;; binding-stack would grow with each recursive step, and would eventually
129 ;;; overflow. I don't believe there is any way around this without lexical 129 ;; overflow. I don't believe there is any way around this without lexical
130 ;;; scope. 130 ;; scope.
131 ;;; 131 ;;
132 ;;; Wouldn't it be nice if Emacs Lisp had lexical scope. 132 ;; Wouldn't it be nice if Emacs Lisp had lexical scope.
133 ;;; 133 ;;
134 ;;; Idea: the form (lexical-scope) in a file means that the file may be 134 ;; Idea: the form (lexical-scope) in a file means that the file may be
135 ;;; compiled lexically. This proclamation is file-local. Then, within 135 ;; compiled lexically. This proclamation is file-local. Then, within
136 ;;; that file, "let" would establish lexical bindings, and "let-dynamic" 136 ;; that file, "let" would establish lexical bindings, and "let-dynamic"
137 ;;; would do things the old way. (Or we could use CL "declare" forms.) 137 ;; would do things the old way. (Or we could use CL "declare" forms.)
138 ;;; We'd have to notice defvars and defconsts, since those variables should 138 ;; We'd have to notice defvars and defconsts, since those variables should
139 ;;; always be dynamic, and attempting to do a lexical binding of them 139 ;; always be dynamic, and attempting to do a lexical binding of them
140 ;;; should simply do a dynamic binding instead. 140 ;; should simply do a dynamic binding instead.
141 ;;; But! We need to know about variables that were not necessarily defvarred 141 ;; But! We need to know about variables that were not necessarily defvarred
142 ;;; in the file being compiled (doing a boundp check isn't good enough.) 142 ;; in the file being compiled (doing a boundp check isn't good enough.)
143 ;;; Fdefvar() would have to be modified to add something to the plist. 143 ;; Fdefvar() would have to be modified to add something to the plist.
144 ;;; 144 ;;
145 ;;; A major disadvantage of this scheme is that the interpreter and compiler 145 ;; A major disadvantage of this scheme is that the interpreter and compiler
146 ;;; would have different semantics for files compiled with (dynamic-scope). 146 ;; would have different semantics for files compiled with (dynamic-scope).
147 ;;; Since this would be a file-local optimization, there would be no way to 147 ;; Since this would be a file-local optimization, there would be no way to
148 ;;; modify the interpreter to obey this (unless the loader was hacked 148 ;; modify the interpreter to obey this (unless the loader was hacked
149 ;;; in some grody way, but that's a really bad idea.) 149 ;; in some grody way, but that's a really bad idea.)
150 ;;; 150 ;;
151 ;;; HA! HA! HA! RMS removed the following paragraph from his version of 151 ;; Opinions are mixed on the following paragraph. -slb.
152 ;;; byte-opt.el, proving once again his stubborn refusal to accept any 152 ;;
153 ;;; developments in computer science that occurred after the late 1970's. 153 ;; Really the Right Thing is to make lexical scope the default across
154 ;;; 154 ;; the board, in the interpreter and compiler, and just FIX all of
155 ;;; Really the Right Thing is to make lexical scope the default across 155 ;; the code that relies on dynamic scope of non-defvarred variables.
156 ;;; the board, in the interpreter and compiler, and just FIX all of
157 ;;; the code that relies on dynamic scope of non-defvarred variables.
158 156
159 ;; Other things to consider: 157 ;; Other things to consider:
160 158
161 ;;;;; Associative math should recognize subcalls to identical function: 159 ;; Associative math should recognize subcalls to identical function:
162 ;;;(disassemble (lambda (x) (+ (+ (foo) 1) (+ (bar) 2)))) 160 ;;(disassemble (lambda (x) (+ (+ (foo) 1) (+ (bar) 2))))
163 ;;;;; This should generate the same as (1+ x) and (1- x) 161 ;; This should generate the same as (1+ x) and (1- x)
164 162
165 ;;;(disassemble (lambda (x) (cons (+ x 1) (- x 1)))) 163 ;;(disassemble (lambda (x) (cons (+ x 1) (- x 1))))
166 ;;;;; An awful lot of functions always return a non-nil value. If they're 164 ;; An awful lot of functions always return a non-nil value. If they're
167 ;;;;; error free also they may act as true-constants. 165 ;; error free also they may act as true-constants.
168 166
169 ;;;(disassemble (lambda (x) (and (point) (foo)))) 167 ;;(disassemble (lambda (x) (and (point) (foo))))
170 ;;;;; When 168 ;; When
171 ;;;;; - all but one arguments to a function are constant 169 ;; - all but one arguments to a function are constant
172 ;;;;; - the non-constant argument is an if-expression (cond-expression?) 170 ;; - the non-constant argument is an if-expression (cond-expression?)
173 ;;;;; then the outer function can be distributed. If the guarding 171 ;; then the outer function can be distributed. If the guarding
174 ;;;;; condition is side-effect-free [assignment-free] then the other 172 ;; condition is side-effect-free [assignment-free] then the other
175 ;;;;; arguments may be any expressions. Since, however, the code size 173 ;; arguments may be any expressions. Since, however, the code size
176 ;;;;; can increase this way they should be "simple". Compare: 174 ;; can increase this way they should be "simple". Compare:
177 175
178 ;;;(disassemble (lambda (x) (eq (if (point) 'a 'b) 'c))) 176 ;;(disassemble (lambda (x) (eq (if (point) 'a 'b) 'c)))
179 ;;;(disassemble (lambda (x) (if (point) (eq 'a 'c) (eq 'b 'c)))) 177 ;;(disassemble (lambda (x) (if (point) (eq 'a 'c) (eq 'b 'c))))
180 178
181 ;;;;; (car (cons A B)) -> (progn B A) 179 ;; (car (cons A B)) -> (progn B A)
182 ;;;(disassemble (lambda (x) (car (cons (foo) 42)))) 180 ;;(disassemble (lambda (x) (car (cons (foo) 42))))
183 181
184 ;;;;; (cdr (cons A B)) -> (progn A B) 182 ;; (cdr (cons A B)) -> (progn A B)
185 ;;;(disassemble (lambda (x) (cdr (cons 42 (foo))))) 183 ;;(disassemble (lambda (x) (cdr (cons 42 (foo)))))
186 184
187 ;;;;; (car (list A B ...)) -> (progn B ... A) 185 ;; (car (list A B ...)) -> (progn B ... A)
188 ;;;(disassemble (lambda (x) (car (list (foo) 42 (bar))))) 186 ;;(disassemble (lambda (x) (car (list (foo) 42 (bar)))))
189 187
190 ;;;;; (cdr (list A B ...)) -> (progn A (list B ...)) 188 ;; (cdr (list A B ...)) -> (progn A (list B ...))
191 ;;;(disassemble (lambda (x) (cdr (list 42 (foo) (bar))))) 189 ;;(disassemble (lambda (x) (cdr (list 42 (foo) (bar)))))
192 190
193 191
194 ;;; Code: 192 ;;; Code:
195 193
196 (require 'byte-compile "bytecomp") 194 (require 'byte-compile "bytecomp")