forked from emacs-mirror/emacs
-
Notifications
You must be signed in to change notification settings - Fork 4
/
byte-opt.el
2425 lines (2273 loc) · 94.1 KB
/
byte-opt.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;;; byte-opt.el --- the optimization passes of the emacs-lisp byte compiler -*- lexical-binding: t -*-
;; Copyright (C) 1991, 1994, 2000-2021 Free Software Foundation, Inc.
;; Author: Jamie Zawinski <jwz@lucid.com>
;; Hallvard Furuseth <hbf@ulrik.uio.no>
;; Maintainer: emacs-devel@gnu.org
;; Keywords: internal
;; Package: emacs
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; 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.
;;
;; 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 '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 defvared
;; 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:
;; ;; Associative math should recognize subcalls to identical function:
;; (disassemble (lambda (x) (+ (+ (foo) 1) (+ (bar) 2))))
;; ;; This should generate the same as (1+ x) and (1- x)
;; (disassemble (lambda (x) (cons (+ x 1) (- x 1))))
;; ;; An awful lot of functions always return a non-nil value. If they're
;; ;; error free also they may act as true-constants.
;; (disassemble (lambda (x) (and (point) (foo))))
;; ;; 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
;; ;; condition is side-effect-free [assignment-free] then the other
;; ;; arguments may be any expressions. Since, however, the code size
;; ;; can increase this way they should be "simple". Compare:
;; (disassemble (lambda (x) (eq (if (point) 'a 'b) 'c)))
;; (disassemble (lambda (x) (if (point) (eq 'a 'c) (eq 'b 'c))))
;; ;; (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 ...)) -> (prog1 A B ...)
;; (disassemble (lambda (x) (car (list (foo) 42 (bar)))))
;; ;; (cdr (list A B ...)) -> (progn A (list B ...))
;; (disassemble (lambda (x) (cdr (list 42 (foo) (bar)))))
;;; Code:
(require 'bytecomp)
(eval-when-compile (require 'cl-lib))
(require 'macroexp)
(eval-when-compile (require 'subr-x))
(defun byte-compile-log-lap-1 (format &rest args)
;; Newer byte codes for stack-ref make the slot 0 non-nil again.
;; But the "old disassembler" is *really* ancient by now.
;; (if (aref byte-code-vector 0)
;; (error "The old version of the disassembler is loaded. Reload new-bytecomp as well"))
(byte-compile-log-1
(apply #'format-message format
(let (c a)
(mapcar (lambda (arg)
(if (not (consp arg))
(if (and (symbolp arg)
(string-match "^byte-" (symbol-name arg)))
(intern (substring (symbol-name arg) 5))
arg)
(if (integerp (setq c (car arg)))
(error "non-symbolic byte-op %s" c))
(if (eq c 'TAG)
(setq c arg)
(setq a (cond ((memq c byte-goto-ops)
(car (cdr (cdr arg))))
((memq c byte-constref-ops)
(car (cdr arg)))
(t (cdr arg))))
(setq c (symbol-name c))
(if (string-match "^byte-." c)
(setq c (intern (substring c 5)))))
(if (eq c 'constant) (setq c 'const))
(if (and (eq (cdr arg) 0)
(not (memq c '(unbind call const))))
c
(format "(%s %s)" c a))))
args)))))
(defmacro byte-compile-log-lap (format-string &rest args)
`(and (memq byte-optimize-log '(t byte))
(byte-compile-log-lap-1 ,format-string ,@args)))
(defvar byte-optimize--lexvars nil
"Lexical variables in scope, in reverse order of declaration.
Each element is on the form (NAME KEEP [VALUE]), where:
NAME is the variable name,
KEEP is a boolean indicating whether the binding must be retained,
VALUE, if present, is a substitutable expression.
Earlier variables shadow later ones with the same name.")
;;; byte-compile optimizers to support inlining
(put 'inline 'byte-optimizer #'byte-optimize-inline-handler)
(defun byte-optimize-inline-handler (form)
"byte-optimize-handler for the `inline' special-form."
(cons 'progn
(mapcar
(lambda (sexp)
(let ((f (car-safe sexp)))
(if (and (symbolp f)
(or (cdr (assq f byte-compile-function-environment))
(not (or (not (fboundp f))
(cdr (assq f byte-compile-macro-environment))
(and (consp (setq f (symbol-function f)))
(eq (car f) 'macro))
(subrp f)))))
(byte-compile-inline-expand sexp)
sexp)))
(cdr form))))
(defun byte-compile-inline-expand (form)
(let* ((name (car form))
(localfn (cdr (assq name byte-compile-function-environment)))
(fn (or localfn (symbol-function name))))
(when (autoloadp fn)
(autoload-do-load fn)
(setq fn (or (symbol-function name)
(cdr (assq name byte-compile-function-environment)))))
(pcase fn
('nil
(byte-compile-warn "attempt to inline `%s' before it was defined"
name)
form)
(`(autoload . ,_)
(error "File `%s' didn't define `%s'" (nth 1 fn) name))
((and (pred symbolp) (guard (not (eq fn t)))) ;A function alias.
(byte-compile-inline-expand (cons fn (cdr form))))
((pred byte-code-function-p)
;; (message "Inlining byte-code for %S!" name)
;; The byte-code will be really inlined in byte-compile-unfold-bcf.
(byte-compile--check-arity-bytecode form fn)
`(,fn ,@(cdr form)))
((or `(lambda . ,_) `(closure . ,_))
;; While byte-compile-unfold-bcf can inline dynbind byte-code into
;; letbind byte-code (or any other combination for that matter), we
;; can only inline dynbind source into dynbind source or letbind
;; source into letbind source.
;; When the function comes from another file, we byte-compile
;; the inlined function first, and then inline its byte-code.
;; This also has the advantage that the final code does not
;; depend on the order of compilation of ELisp files, making
;; the build more reproducible.
(if (eq fn localfn)
;; From the same file => same mode.
(macroexp--unfold-lambda `(,fn ,@(cdr form)))
;; Since we are called from inside the optimiser, we need to make
;; sure not to propagate lexvar values.
(let ((byte-optimize--lexvars nil)
;; Silence all compilation warnings: the useful ones should
;; be displayed when the function's source file will be
;; compiled anyway, but more importantly we would otherwise
;; emit spurious warnings here because we don't have the full
;; context, such as `declare-functions' placed earlier in the
;; source file's code or `with-suppressed-warnings' that
;; surrounded the `defsubst'.
(byte-compile-warnings nil))
(byte-compile name))
(let ((bc (symbol-function name)))
(byte-compile--check-arity-bytecode form bc)
`(,bc ,@(cdr form)))))
(_ ;; Give up on inlining.
form))))
;;; implementing source-level optimizers
(defvar byte-optimize--vars-outside-condition nil
"Alist of variables lexically bound outside conditionally executed code.
Variables here are sensitive to mutation inside the conditional code,
since their contents in sequentially later code depends on the path taken
and may no longer be statically known.
Same format as `byte-optimize--lexvars', with shared structure and contents.")
(defvar byte-optimize--vars-outside-loop nil
"Alist of variables lexically bound outside the innermost `while' loop.
Variables here are sensitive to mutation inside the loop, since this can
occur an indeterminate number of times and thus have effect on code
sequentially preceding the mutation itself.
Same format as `byte-optimize--lexvars', with shared structure and contents.")
(defvar byte-optimize--dynamic-vars nil
"List of variables declared as dynamic during optimisation.")
(defun byte-optimize--substitutable-p (expr)
"Whether EXPR is a constant that can be propagated."
;; Only consider numbers, symbols and strings to be values for substitution
;; purposes. Numbers and symbols are immutable, and mutating string
;; literals (or results from constant-evaluated string-returning functions)
;; can be considered undefined.
;; (What about other quoted values, like conses?)
(or (booleanp expr)
(numberp expr)
(stringp expr)
(and (consp expr)
(memq (car expr) '(quote function))
(symbolp (cadr expr)))
(keywordp expr)))
(defmacro byte-optimize--pcase (exp &rest cases)
;; When we do
;;
;; (pcase EXP
;; (`(if ,exp ,then ,else) (DO-TEST))
;; (`(plus ,e2 ,e2) (DO-ADD))
;; (`(times ,e2 ,e2) (DO-MULT))
;; ...)
;;
;; we usually don't want to fall back to the default case if
;; the value of EXP is of a form like `(if E1 E2)' or `(plus E1)'
;; or `(times E1 E2 E3)', instead we either want to signal an error
;; that EXP has an unexpected shape, or we want to carry on as if
;; it had the right shape (ignore the extra data and pretend the missing
;; data is nil) because it should simply never happen.
;;
;; The macro below implements the second option by rewriting patterns
;; like `(if ,exp ,then ,else)'
;; to `(if . (or `(,exp ,then ,else) pcase--dontcare))'.
;;
;; The resulting macroexpansion is also significantly cleaner/smaller/faster.
(declare (indent 1) (debug pcase))
`(pcase ,exp
. ,(mapcar (lambda (case)
`(,(pcase (car case)
((and `(,'\` (,_ . (,'\, ,_))) pat) pat)
(`(,'\` (,head . ,tail))
(list '\`
(cons head
(list '\, `(or ,(list '\` tail) pcase--dontcare)))))
(pat pat))
. ,(cdr case)))
cases)))
(defun byte-optimize-form-code-walker (form for-effect)
;;
;; For normal function calls, We can just mapcar the optimizer the cdr. But
;; we need to have special knowledge of the syntax of the special forms
;; like let and defun (that's why they're special forms :-). (Actually,
;; the important aspect is that they are subrs that don't evaluate all of
;; their args.)
;;
;; FIXME: There are a bunch of `byte-compile-warn' here which arguably
;; have no place in an optimizer: the corresponding tests should be
;; performed in `macroexpand-all', or in `cconv', or in `bytecomp'.
(let ((fn (car-safe form)))
(byte-optimize--pcase form
((pred (not consp))
(cond
((and for-effect
(or byte-compile-delete-errors
(not (symbolp form))
(eq form t)
(keywordp form)))
nil)
((symbolp form)
(let ((lexvar (assq form byte-optimize--lexvars)))
(cond
((not lexvar) form)
(for-effect nil)
((cddr lexvar) ; Value available?
(if (assq form byte-optimize--vars-outside-loop)
;; Cannot substitute; mark for retention to avoid the
;; variable being eliminated.
(progn
(setcar (cdr lexvar) t)
form)
;; variable value to use
(caddr lexvar)))
(t form))))
(t form)))
(`(quote . ,v)
(if (or (not v) (cdr v))
(byte-compile-warn "malformed quote form: `%s'"
(prin1-to-string form)))
;; Map (quote nil) to nil to simplify optimizer logic.
;; Map quoted constants to nil if for-effect (just because).
(and (car v)
(not for-effect)
form))
(`(,(or 'let 'let*) . ,rest)
(cons fn (byte-optimize-let-form fn rest for-effect)))
(`(cond . ,clauses)
;; The condition in the first clause is always executed, but
;; right now we treat all of them as conditional for simplicity.
(let ((byte-optimize--vars-outside-condition byte-optimize--lexvars))
(cons fn
(mapcar (lambda (clause)
(if (consp clause)
(cons
(byte-optimize-form (car clause) nil)
(byte-optimize-body (cdr clause) for-effect))
(byte-compile-warn "malformed cond form: `%s'"
(prin1-to-string clause))
clause))
clauses))))
(`(progn . ,exps)
;; As an extra added bonus, this simplifies (progn <x>) --> <x>.
(if (cdr exps)
(macroexp-progn (byte-optimize-body exps for-effect))
(byte-optimize-form (car exps) for-effect)))
(`(prog1 ,exp . ,exps)
(let ((exp-opt (byte-optimize-form exp for-effect)))
(if exps
(let ((exps-opt (byte-optimize-body exps t)))
(if (macroexp-const-p exp-opt)
`(progn ,@exps-opt ,exp-opt)
`(prog1 ,exp-opt ,@exps-opt)))
exp-opt)))
(`(,(or `save-excursion `save-restriction `save-current-buffer) . ,exps)
;; 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 exps for-effect)))
(`(if ,test ,then . ,else)
;; FIXME: We are conservative here: any variable changed in the
;; THEN branch will be barred from substitution in the ELSE
;; branch, despite the branches being mutually exclusive.
;; The test is always executed.
(let* ((test-opt (byte-optimize-form test nil))
(const (macroexp-const-p test-opt))
;; The branches are traversed unconditionally when possible.
(byte-optimize--vars-outside-condition
(if const
byte-optimize--vars-outside-condition
byte-optimize--lexvars))
;; Avoid traversing dead branches.
(then-opt (and test-opt (byte-optimize-form then for-effect)))
(else-opt (and (not (and test-opt const))
(byte-optimize-body else for-effect))))
`(if ,test-opt ,then-opt . ,else-opt)))
(`(,(or 'and 'or) . ,exps) ; Remember, and/or are control structures.
;; FIXME: We have to traverse the expressions in left-to-right
;; order (because that is the order of evaluation and variable
;; mutations must be found prior to their use), but doing so we miss
;; some optimisation opportunities:
;; consider (and A B) in a for-effect context, where B => nil.
;; Then A could be optimised in a for-effect context too.
(let ((tail exps)
(args nil))
(when tail
;; The first argument is always unconditional.
(push (byte-optimize-form
(car tail) (and for-effect (null (cdr tail))))
args)
(setq tail (cdr tail))
;; Remaining arguments are conditional.
(let ((byte-optimize--vars-outside-condition byte-optimize--lexvars))
(while tail
(push (byte-optimize-form
(car tail) (and for-effect (null (cdr tail))))
args)
(setq tail (cdr tail)))))
(cons fn (nreverse args))))
(`(while ,exp . ,exps)
;; FIXME: We conservatively prevent the substitution of any variable
;; bound outside the loop in case it is mutated later in the loop,
;; but this misses many opportunities: variables not mutated in the
;; loop at all, and variables affecting the initial condition (which
;; is always executed unconditionally).
(let* ((byte-optimize--vars-outside-condition byte-optimize--lexvars)
(byte-optimize--vars-outside-loop byte-optimize--lexvars)
(condition (byte-optimize-form exp nil))
(body (byte-optimize-body exps t)))
`(while ,condition . ,body)))
(`(interactive . ,_)
(byte-compile-warn "misplaced interactive spec: `%s'"
(prin1-to-string form))
nil)
(`(function . ,_)
;; This forms is compiled as constant or by breaking out
;; all the subexpressions and compiling them separately.
form)
(`(condition-case ,var ,exp . ,clauses)
(let ((byte-optimize--vars-outside-condition byte-optimize--lexvars))
`(condition-case ,var ;Not evaluated.
,(byte-optimize-form exp for-effect)
,@(mapcar (lambda (clause)
(let ((byte-optimize--lexvars
(and lexical-binding
(if var
(cons (list var t)
byte-optimize--lexvars)
byte-optimize--lexvars))))
(cons (car clause)
(byte-optimize-body (cdr clause) for-effect))))
clauses))))
(`(unwind-protect ,exp . ,exps)
;; The unwinding part of an unwind-protect is compiled (and thus
;; optimized) as a top-level form, but run the optimizer for it here
;; anyway for lexical variable usage and substitution. But the
;; protected part has the same for-effect status as the
;; unwind-protect itself. (The unwinding part is always for effect,
;; but that isn't handled properly yet.)
(let* ((byte-optimize--vars-outside-condition byte-optimize--lexvars)
(bodyform (byte-optimize-form exp for-effect)))
(pcase exps
(`(:fun-body ,f)
`(unwind-protect ,bodyform
:fun-body ,(byte-optimize-form f nil)))
(_
`(unwind-protect ,bodyform
. ,(byte-optimize-body exps t))))))
(`(catch ,tag . ,exps)
(let ((byte-optimize--vars-outside-condition byte-optimize--lexvars))
`(catch ,(byte-optimize-form tag nil)
. ,(byte-optimize-body exps for-effect))))
(`(ignore . ,exps)
;; Don't treat the args to `ignore' as being
;; computed for effect. We want to avoid the warnings
;; that might occur if they were treated that way.
;; However, don't actually bother calling `ignore'.
`(progn ,@(mapcar #'byte-optimize-form exps) nil))
;; Needed as long as we run byte-optimize-form after cconv.
(`(internal-make-closure . ,_)
;; Look up free vars and mark them to be kept, so that they
;; won't be optimised away.
(dolist (var (caddr form))
(let ((lexvar (assq var byte-optimize--lexvars)))
(when lexvar
(setcar (cdr lexvar) t))))
form)
(`((lambda . ,_) . ,_)
(let ((newform (macroexp--unfold-lambda form)))
(if (eq newform form)
;; Some error occurred, avoid infinite recursion.
form
(byte-optimize-form newform for-effect))))
;; FIXME: Strictly speaking, I think this is a bug: (closure...)
;; is a *value* and shouldn't appear in the car.
(`((closure . ,_) . ,_) form)
(`(setq . ,args)
(let ((var-expr-list nil))
(while args
(unless (and (consp args)
(symbolp (car args)) (consp (cdr args)))
(byte-compile-warn "malformed setq form: %S" form))
(let* ((var (car args))
(expr (cadr args))
(lexvar (assq var byte-optimize--lexvars))
(value (byte-optimize-form expr nil)))
(when lexvar
(setcar (cdr lexvar) t) ; Mark variable to be kept.
(setcdr (cdr lexvar) nil)) ; Inhibit further substitution.
(push var var-expr-list)
(push value var-expr-list))
(setq args (cddr args)))
(cons fn (nreverse var-expr-list))))
(`(defvar ,(and (pred symbolp) name) . ,rest)
(let ((optimized-rest (and rest
(cons (byte-optimize-form (car rest) nil)
(cdr rest)))))
(push name byte-optimize--dynamic-vars)
`(defvar ,name . ,optimized-rest)))
(`(,(pred byte-code-function-p) . ,exps)
(cons fn (mapcar #'byte-optimize-form exps)))
(`(,(pred (not symbolp)) . ,_)
(byte-compile-warn "`%s' is a malformed function"
(prin1-to-string fn))
form)
((guard (when for-effect
(if-let ((tmp (get fn 'side-effect-free)))
(or byte-compile-delete-errors
(eq tmp 'error-free)
(progn
(byte-compile-warn "value returned from %s is unused"
(prin1-to-string form))
nil)))))
(byte-compile-log " %s called for effect; deleted" fn)
;; appending a nil here might not be necessary, but it can't hurt.
(byte-optimize-form
(cons 'progn (append (cdr form) '(nil))) t))
(_
;; Otherwise, no args can be considered to be for-effect,
;; even if the called function is for-effect, because we
;; don't know anything about that function.
(let ((form (cons fn (mapcar #'byte-optimize-form (cdr form)))))
(if (get fn 'pure)
(byte-optimize-constant-args form)
form))))))
(defun byte-optimize-one-form (form &optional for-effect)
"The source-level pass of the optimizer."
;; Make optimiser aware of lexical arguments.
(let ((byte-optimize--lexvars
(mapcar (lambda (v) (list (car v) t))
byte-compile--lexical-environment)))
(byte-optimize-form form for-effect)))
(defun byte-optimize-form (form &optional for-effect)
(while
(progn
;; First, optimize all sub-forms of this one.
(setq form (byte-optimize-form-code-walker form for-effect))
;; If a form-specific optimiser is available, run it and start over
;; until a fixpoint has been reached.
(and (consp form)
(symbolp (car form))
(let ((opt (function-get (car form) 'byte-optimizer)))
(and opt
(let ((old form)
(new (funcall opt form)))
(byte-compile-log " %s\t==>\t%s" old new)
(setq form new)
(not (eq new old))))))))
form)
(defun byte-optimize-let-form (head form for-effect)
;; Recursively enter the optimizer for the bindings and body
;; of a let or let*. This for depth-firstness: forms that
;; are more deeply nested are optimized first.
(if lexical-binding
(let* ((byte-optimize--lexvars byte-optimize--lexvars)
(new-lexvars nil)
(let-vars nil))
(dolist (binding (car form))
(let* ((name (car binding))
(expr (byte-optimize-form (cadr binding) nil))
(value (and (byte-optimize--substitutable-p expr)
(list expr)))
(lexical (not (or (special-variable-p name)
(memq name byte-compile-bound-variables)
(memq name byte-optimize--dynamic-vars))))
(lexinfo (and lexical (cons name (cons nil value)))))
(push (cons name (cons expr (cdr lexinfo))) let-vars)
(when lexinfo
(push lexinfo (if (eq head 'let*)
byte-optimize--lexvars
new-lexvars)))))
(setq byte-optimize--lexvars
(append new-lexvars byte-optimize--lexvars))
;; Walk the body expressions, which may mutate some of the records,
;; and generate new bindings that exclude unused variables.
(let* ((byte-optimize--dynamic-vars byte-optimize--dynamic-vars)
(opt-body (byte-optimize-body (cdr form) for-effect))
(bindings nil))
(dolist (var let-vars)
;; VAR is (NAME EXPR [KEEP [VALUE]])
(when (or (not (nthcdr 3 var)) (nth 2 var))
;; Value not present, or variable marked to be kept.
(push (list (nth 0 var) (nth 1 var)) bindings)))
(cons bindings opt-body)))
;; With dynamic binding, no substitutions are in effect.
(let ((byte-optimize--lexvars nil))
(cons
(mapcar (lambda (binding)
(if (symbolp binding)
binding
(when (or (atom binding) (cddr binding))
(byte-compile-warn "malformed let binding: `%S'" binding))
(list (car binding)
(byte-optimize-form (nth 1 binding) nil))))
(car form))
(byte-optimize-body (cdr form) for-effect)))))
(defun byte-optimize-body (forms all-for-effect)
;; Optimize the cdr of a progn or implicit progn; all forms is a list of
;; forms, all but the last of which are optimized with the assumption that
;; they are being called for effect. the last is for-effect as well if
;; all-for-effect is true. returns a new list of forms.
(let ((rest forms)
(result nil)
fe new)
(while rest
(setq fe (or all-for-effect (cdr rest)))
(setq new (and (car rest) (byte-optimize-form (car rest) fe)))
(if (or new (not fe))
(setq result (cons new result)))
(setq rest (cdr rest)))
(nreverse result)))
;; some source-level optimizers
;;
;; when writing optimizers, be VERY careful that the optimizer returns
;; something not EQ to its argument if and ONLY if it has made a change.
;; This implies that you cannot simply destructively modify the list;
;; you must return something not EQ to it if you make an optimization.
;;
;; It is now safe to optimize code such that it introduces new bindings.
(defsubst byte-compile-trueconstp (form)
"Return non-nil if FORM always evaluates to a non-nil value."
(while (eq (car-safe form) 'progn)
(setq form (car (last (cdr form)))))
(cond ((consp form)
(pcase (car form)
('quote (cadr form))
;; Can't use recursion in a defsubst.
;; (`progn (byte-compile-trueconstp (car (last (cdr form)))))
))
((not (symbolp form)))
((eq form t))
((keywordp form))))
(defsubst byte-compile-nilconstp (form)
"Return non-nil if FORM always evaluates to a nil value."
(while (eq (car-safe form) 'progn)
(setq form (car (last (cdr form)))))
(cond ((consp form)
(pcase (car form)
('quote (null (cadr form)))
;; Can't use recursion in a defsubst.
;; (`progn (byte-compile-nilconstp (car (last (cdr form)))))
))
((not (symbolp form)) nil)
((null form))))
;; If the function is being called with constant integer args,
;; evaluate as much as possible at compile-time. This optimizer
;; assumes that the function is associative, like min or max.
(defun byte-optimize-associative-math (form)
(let ((args nil)
(constants nil)
(rest (cdr form)))
(while rest
(if (integerp (car rest))
(setq constants (cons (car rest) constants))
(setq args (cons (car rest) args)))
(setq rest (cdr rest)))
(if (cdr constants)
(let ((const (apply (car form) (nreverse constants))))
(if args
(append (list (car form) const)
(nreverse args))
const))
form)))
(defun byte-optimize-min-max (form)
"Optimize `min' and `max'."
(let ((opt (byte-optimize-associative-math form)))
(if (and (consp opt) (memq (car opt) '(min max))
(= (length opt) 4))
;; (OP x y z) -> (OP (OP x y) z), in order to use binary byte ops.
(list (car opt)
(list (car opt) (nth 1 opt) (nth 2 opt))
(nth 3 opt))
opt)))
;; Use OP to reduce any leading prefix of constant numbers in the list
;; (cons ACCUM ARGS) down to a single number, and return the
;; resulting list A of arguments. The idea is that applying OP to A
;; is equivalent to (but likely more efficient than) applying OP to
;; (cons ACCUM ARGS), on any Emacs platform. Do not make any special
;; provision for (- X) or (/ X); for example, it is the caller’s
;; responsibility that (- 1 0) should not be "optimized" to (- 1).
(defun byte-opt--arith-reduce (op accum args)
(when (numberp accum)
(let (accum1)
(while (and (numberp (car args))
(numberp
(setq accum1 (condition-case ()
(funcall op accum (car args))
(error))))
(= accum1 (funcall op (float accum) (car args))))
(setq accum accum1)
(setq args (cdr args)))))
(cons accum args))
(defun byte-optimize-plus (form)
(let ((args (remq 0 (byte-opt--arith-reduce #'+ 0 (cdr form)))))
(cond
;; (+) -> 0
((null args) 0)
;; (+ n) -> n, where n is a number
((and (null (cdr args)) (numberp (car args))) (car args))
;; (+ x 1) --> (1+ x) and (+ x -1) --> (1- x).
((and (null (cddr args)) (or (memq 1 args) (memq -1 args)))
(let* ((arg1 (car args)) (arg2 (cadr args))
(integer-is-first (memq arg1 '(1 -1)))
(integer (if integer-is-first arg1 arg2))
(other (if integer-is-first arg2 arg1)))
(list (if (eq integer 1) '1+ '1-) other)))
;; (+ x y z) -> (+ (+ x y) z)
((= (length args) 3)
`(+ ,(byte-optimize-plus `(+ ,(car args) ,(cadr args))) ,@(cddr args)))
;; not further optimized
((equal args (cdr form)) form)
(t (cons '+ args)))))
(defun byte-optimize-minus (form)
(let ((args (cdr form)))
(if (and (cdr args)
(null (cdr (setq args (byte-opt--arith-reduce
#'- (car args) (cdr args)))))
(numberp (car args)))
;; The entire argument list reduced to a constant; return it.
(car args)
;; Remove non-leading zeros, except for (- x 0).
(when (memq 0 (cdr args))
(setq args (cons (car args) (or (remq 0 (cdr args)) (list 0)))))
(cond
;; (- x 1) --> (1- x)
((equal (cdr args) '(1))
(list '1- (car args)))
;; (- x -1) --> (1+ x)
((equal (cdr args) '(-1))
(list '1+ (car args)))
;; (- n) -> -n, where n and -n are constant numbers.
;; This must be done separately since byte-opt--arith-reduce
;; is not applied to (- n).
((and (null (cdr args))
(numberp (car args)))
(- (car args)))
;; (- x y z) -> (- (- x y) z)
((= (length args) 3)
`(- ,(byte-optimize-minus `(- ,(car args) ,(cadr args))) ,@(cddr args)))
;; not further optimized
((equal args (cdr form)) form)
(t (cons '- args))))))
(defun byte-optimize-multiply (form)
(let* ((args (remq 1 (byte-opt--arith-reduce #'* 1 (cdr form)))))
(cond
;; (*) -> 1
((null args) 1)
;; (* n) -> n, where n is a number
((and (null (cdr args)) (numberp (car args))) (car args))
;; (* x y z) -> (* (* x y) z)
((= (length args) 3)
`(* ,(byte-optimize-multiply `(* ,(car args) ,(cadr args)))
,@(cddr args)))
;; not further optimized
((equal args (cdr form)) form)
(t (cons '* args)))))
(defun byte-optimize-divide (form)
(let ((args (cdr form)))
(if (and (cdr args)
(null (cdr (setq args (byte-opt--arith-reduce
#'/ (car args) (cdr args)))))
(numberp (car args)))
;; The entire argument list reduced to a constant; return it.
(car args)
;; Remove non-leading 1s, except for (/ x 1).
(when (memq 1 (cdr args))
(setq args (cons (car args) (or (remq 1 (cdr args)) (list 1)))))
(if (equal args (cdr form))
form
(cons '/ args)))))
(defun byte-optimize-binary-predicate (form)
(cond
((or (not (macroexp-const-p (nth 1 form)))
(nthcdr 3 form)) ;; In case there are more than 2 args.
form)
((macroexp-const-p (nth 2 form))
(condition-case ()
(list 'quote (eval form))
(error form)))
(t ;; Moving the constant to the end can enable some lapcode optimizations.
(list (car form) (nth 2 form) (nth 1 form)))))
(defun byte-optimize-constant-args (form)
(let ((ok t)
(rest (cdr form)))
(while (and rest ok)
(setq ok (macroexp-const-p (car rest))
rest (cdr rest)))
(if ok
(condition-case ()
(list 'quote (eval form))
(error form))
form)))
(defun byte-optimize-identity (form)
(if (and (cdr form) (null (cdr (cdr form))))
(nth 1 form)
form))
(defun byte-optimize--constant-symbol-p (expr)
"Whether EXPR is a constant symbol."
(and (macroexp-const-p expr) (symbolp (eval expr))))
(defun byte-optimize--fixnump (o)
"Return whether O is guaranteed to be a fixnum in all Emacsen.
See Info node `(elisp) Integer Basics'."
(and (fixnump o) (<= -536870912 o 536870911)))
(defun byte-optimize-equal (form)
;; Replace `equal' or `eql' with `eq' if at least one arg is a
;; symbol or fixnum.
(byte-optimize-binary-predicate
(if (= (length (cdr form)) 2)
(if (or (byte-optimize--constant-symbol-p (nth 1 form))
(byte-optimize--constant-symbol-p (nth 2 form))
(byte-optimize--fixnump (nth 1 form))
(byte-optimize--fixnump (nth 2 form)))
(cons 'eq (cdr form))
form)
;; Arity errors reported elsewhere.
form)))
(defun byte-optimize-eq (form)
(pcase (cdr form)
((or `(,x nil) `(nil ,x)) `(not ,x))
(_ (byte-optimize-binary-predicate form))))
(defun byte-optimize-member (form)
(cond
((/= (length (cdr form)) 2) form) ; arity error
((null (nth 2 form)) ; empty list
`(progn ,(nth 1 form) nil))
;; Replace `member' or `memql' with `memq' if the first arg is a symbol
;; or fixnum, or the second arg is a list of symbols or fixnums.
((or (byte-optimize--constant-symbol-p (nth 1 form))
(byte-optimize--fixnump (nth 1 form))
(let ((arg2 (nth 2 form)))
(and (macroexp-const-p arg2)
(let ((listval (eval arg2)))
(and (listp listval)
(not (memq nil (mapcar
(lambda (o)
(or (symbolp o)
(byte-optimize--fixnump o)))
listval))))))))
(cons 'memq (cdr form)))
(t form)))
(defun byte-optimize-assoc (form)
;; Replace 2-argument `assoc' with `assq', `rassoc' with `rassq',
;; if the first arg is a symbol or fixnum.
(cond
((/= (length form) 3)
form)
((null (nth 2 form)) ; empty list
`(progn ,(nth 1 form) nil))
((or (byte-optimize--constant-symbol-p (nth 1 form))
(byte-optimize--fixnump (nth 1 form)))
(cons (if (eq (car form) 'assoc) 'assq 'rassq)