diff options
| author | Paul Eggert | 2018-03-26 17:03:54 -0700 |
|---|---|---|
| committer | Andrew G Cohen | 2018-12-11 14:18:31 +0800 |
| commit | 5b0fed956e755eaf0b0dce148b3de39a390d1195 (patch) | |
| tree | dacbc54147ed33613ccc5f71efc8215db884c04c | |
| parent | 5a330784781cf184033dd57f0c386b6a34e06674 (diff) | |
| download | emacs-feature/gnus-select.tar.gz emacs-feature/gnus-select.zip | |
Fix constant folding of overflowsfeature/gnus-select
This suppresses some byte-code optimizations that were invalid in
the presence of integer overflows, because they meant that .elc
files assumed the runtime behavior of the compiling platform, as
opposed to the runtime platform. Problem reported by Pip Cet in:
https://lists.gnu.org/r/emacs-devel/2018-03/msg00753.html
* lisp/emacs-lisp/byte-opt.el (byte-opt--portable-max)
(byte-opt--portable-min): New constants.
(byte-opt--portable-numberp, byte-opt--arith-reduce)
(byte-optimize-1+, byte-optimize-1-): New functions.
(byte-optimize-plus, byte-optimize-minus, byte-optimize-multiply)
(byte-optimize-divide): Avoid invalid optimizations.
(1+, 1-): Use new optimizers.
(byte-optimize-or, byte-optimize-cond): Simplify by using
remq instead of delq and copy-sequence.
| -rw-r--r-- | lisp/emacs-lisp/byte-opt.el | 175 |
1 files changed, 124 insertions, 51 deletions
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el index 004f58cc12f..3bc4c438d6a 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el | |||
| @@ -678,59 +678,134 @@ | |||
| 678 | (apply (car form) constants)) | 678 | (apply (car form) constants)) |
| 679 | form))) | 679 | form))) |
| 680 | 680 | ||
| 681 | ;; Portable Emacs integers fall in this range. | ||
| 682 | (defconst byte-opt--portable-max #x1fffffff) | ||
| 683 | (defconst byte-opt--portable-min (- -1 byte-opt--portable-max)) | ||
| 684 | |||
| 685 | ;; True if N is a number that works the same on all Emacs platforms. | ||
| 686 | ;; Portable Emacs fixnums are exactly representable as floats on all | ||
| 687 | ;; Emacs platforms, and (except for -0.0) any floating-point number | ||
| 688 | ;; that equals one of these integers must be the same on all | ||
| 689 | ;; platforms. Although other floating-point numbers such as 0.5 are | ||
| 690 | ;; also portable, it can be tricky to characterize them portably so | ||
| 691 | ;; they are not optimized. | ||
| 692 | (defun byte-opt--portable-numberp (n) | ||
| 693 | (and (numberp n) | ||
| 694 | (<= byte-opt--portable-min n byte-opt--portable-max) | ||
| 695 | (= n (floor n)) | ||
| 696 | (not (and (floatp n) (zerop n) | ||
| 697 | (condition-case () (< (/ n) 0) (error)))))) | ||
| 698 | |||
| 699 | ;; Use OP to reduce any leading prefix of portable numbers in the list | ||
| 700 | ;; (cons ACCUM ARGS) down to a single portable number, and return the | ||
| 701 | ;; resulting list A of arguments. The idea is that applying OP to A | ||
| 702 | ;; is equivalent to (but likely more efficient than) applying OP to | ||
| 703 | ;; (cons ACCUM ARGS), on any Emacs platform. Do not make any special | ||
| 704 | ;; provision for (- X) or (/ X); for example, it is the caller’s | ||
| 705 | ;; responsibility that (- 1 0) should not be "optimized" to (- 1). | ||
| 706 | (defun byte-opt--arith-reduce (op accum args) | ||
| 707 | (when (byte-opt--portable-numberp accum) | ||
| 708 | (let (accum1) | ||
| 709 | (while (and (byte-opt--portable-numberp (car args)) | ||
| 710 | (byte-opt--portable-numberp | ||
| 711 | (setq accum1 (condition-case () | ||
| 712 | (funcall op accum (car args)) | ||
| 713 | (error)))) | ||
| 714 | (= accum1 (funcall op (float accum) (car args)))) | ||
| 715 | (setq accum accum1) | ||
| 716 | (setq args (cdr args))))) | ||
| 717 | (cons accum args)) | ||
| 718 | |||
| 681 | (defun byte-optimize-plus (form) | 719 | (defun byte-optimize-plus (form) |
| 682 | (if (memq 0 form) (setq form (delq 0 (copy-sequence form)))) | 720 | (let ((args (remq 0 (byte-opt--arith-reduce #'+ 0 (cdr form))))) |
| 683 | ;; For (+ constants...), byte-optimize-predicate does the work. | ||
| 684 | (when (memq nil (mapcar 'numberp (cdr form))) | ||
| 685 | (cond | 721 | (cond |
| 722 | ;; (+) -> 0 | ||
| 723 | ((null args) 0) | ||
| 724 | ;; (+ n) -> n, where n is a number | ||
| 725 | ((and (null (cdr args)) (numberp (car args))) (car args)) | ||
| 686 | ;; (+ x 1) --> (1+ x) and (+ x -1) --> (1- x). | 726 | ;; (+ x 1) --> (1+ x) and (+ x -1) --> (1- x). |
| 687 | ((and (= (length form) 3) | 727 | ((and (null (cddr args)) (or (memq 1 args) (memq -1 args))) |
| 688 | (or (memq (nth 1 form) '(1 -1)) | 728 | (let* ((arg1 (car args)) (arg2 (cadr args)) |
| 689 | (memq (nth 2 form) '(1 -1)))) | 729 | (integer-is-first (memq arg1 '(1 -1))) |
| 690 | (let (integer other) | 730 | (integer (if integer-is-first arg1 arg2)) |
| 691 | (if (memq (nth 1 form) '(1 -1)) | 731 | (other (if integer-is-first arg2 arg1))) |
| 692 | (setq integer (nth 1 form) other (nth 2 form)) | 732 | (list (if (eq integer 1) '1+ '1-) other))) |
| 693 | (setq integer (nth 2 form) other (nth 1 form))) | 733 | ;; not further optimized |
| 694 | (setq form | 734 | ((equal args (cdr form)) form) |
| 695 | (list (if (eq integer 1) '1+ '1-) other)))))) | 735 | (t (cons '+ args))))) |
| 696 | (byte-optimize-predicate form)) | ||
| 697 | 736 | ||
| 698 | (defun byte-optimize-minus (form) | 737 | (defun byte-optimize-minus (form) |
| 699 | ;; Remove zeros. | 738 | (let ((args (cdr form))) |
| 700 | (when (and (nthcdr 3 form) | 739 | (if (and (cdr args) |
| 701 | (memq 0 (cddr form))) | 740 | (null (cdr (setq args (byte-opt--arith-reduce |
| 702 | (setq form (nconc (list (car form) (cadr form)) | 741 | #'- (car args) (cdr args))))) |
| 703 | (delq 0 (copy-sequence (cddr form))))) | 742 | (numberp (car args))) |
| 704 | ;; After the above, we must turn (- x) back into (- x 0). | 743 | ;; The entire argument list reduced to a constant; return it. |
| 705 | (or (cddr form) | 744 | (car args) |
| 706 | (setq form (nconc form (list 0))))) | 745 | ;; Remove non-leading zeros, except for (- x 0). |
| 707 | ;; For (- constants...), byte-optimize-predicate does the work. | 746 | (when (memq 0 (cdr args)) |
| 708 | (when (memq nil (mapcar 'numberp (cdr form))) | 747 | (setq args (cons (car args) (or (remq 0 (cdr args)) (list 0))))) |
| 709 | (cond | 748 | (cond |
| 710 | ;; (- x 1) --> (1- x) | 749 | ;; (- x 1) --> (1- x) |
| 711 | ((equal (nthcdr 2 form) '(1)) | 750 | ((equal (cdr args) '(1)) |
| 712 | (setq form (list '1- (nth 1 form)))) | 751 | (list '1- (car args))) |
| 713 | ;; (- x -1) --> (1+ x) | 752 | ;; (- x -1) --> (1+ x) |
| 714 | ((equal (nthcdr 2 form) '(-1)) | 753 | ((equal (cdr args) '(-1)) |
| 715 | (setq form (list '1+ (nth 1 form)))))) | 754 | (list '1+ (car args))) |
| 716 | (byte-optimize-predicate form)) | 755 | ;; (- n) -> -n, where n and -n are portable numbers. |
| 756 | ;; This must be done separately since byte-opt--arith-reduce | ||
| 757 | ;; is not applied to (- n). | ||
| 758 | ((and (null (cdr args)) | ||
| 759 | (byte-opt--portable-numberp (car args)) | ||
| 760 | (byte-opt--portable-numberp (- (car args)))) | ||
| 761 | (- (car args))) | ||
| 762 | ;; not further optimized | ||
| 763 | ((equal args (cdr form)) form) | ||
| 764 | (t (cons '- args)))))) | ||
| 765 | |||
| 766 | (defun byte-optimize-1+ (form) | ||
| 767 | (let ((args (cdr form))) | ||
| 768 | (when (null (cdr args)) | ||
| 769 | (let ((n (car args))) | ||
| 770 | (when (and (byte-opt--portable-numberp n) | ||
| 771 | (byte-opt--portable-numberp (1+ n))) | ||
| 772 | (setq form (1+ n)))))) | ||
| 773 | form) | ||
| 774 | |||
| 775 | (defun byte-optimize-1- (form) | ||
| 776 | (let ((args (cdr form))) | ||
| 777 | (when (null (cdr args)) | ||
| 778 | (let ((n (car args))) | ||
| 779 | (when (and (byte-opt--portable-numberp n) | ||
| 780 | (byte-opt--portable-numberp (1- n))) | ||
| 781 | (setq form (1- n)))))) | ||
| 782 | form) | ||
| 717 | 783 | ||
| 718 | (defun byte-optimize-multiply (form) | 784 | (defun byte-optimize-multiply (form) |
| 719 | (if (memq 1 form) (setq form (delq 1 (copy-sequence form)))) | 785 | (let* ((args (remq 1 (byte-opt--arith-reduce #'* 1 (cdr form))))) |
| 720 | ;; For (* integers..), byte-optimize-predicate does the work. | 786 | (cond |
| 721 | (byte-optimize-predicate form)) | 787 | ;; (*) -> 1 |
| 788 | ((null args) 1) | ||
| 789 | ;; (* n) -> n, where n is a number | ||
| 790 | ((and (null (cdr args)) (numberp (car args))) (car args)) | ||
| 791 | ;; not further optimized | ||
| 792 | ((equal args (cdr form)) form) | ||
| 793 | (t (cons '* args))))) | ||
| 722 | 794 | ||
| 723 | (defun byte-optimize-divide (form) | 795 | (defun byte-optimize-divide (form) |
| 724 | ;; Remove 1s. | 796 | (let ((args (cdr form))) |
| 725 | (when (and (nthcdr 3 form) | 797 | (if (and (cdr args) |
| 726 | (memq 1 (cddr form))) | 798 | (null (cdr (setq args (byte-opt--arith-reduce |
| 727 | (setq form (nconc (list (car form) (cadr form)) | 799 | #'/ (car args) (cdr args))))) |
| 728 | (delq 1 (copy-sequence (cddr form))))) | 800 | (numberp (car args))) |
| 729 | ;; After the above, we must turn (/ x) back into (/ x 1). | 801 | ;; The entire argument list reduced to a constant; return it. |
| 730 | (or (cddr form) | 802 | (car args) |
| 731 | (setq form (nconc form (list 1))))) | 803 | ;; Remove non-leading 1s, except for (/ x 1). |
| 732 | (byte-optimize-predicate form)) | 804 | (when (memq 1 (cdr args)) |
| 733 | 805 | (setq args (cons (car args) (or (remq 1 (cdr args)) (list 1))))) | |
| 806 | (if (equal args (cdr form)) | ||
| 807 | form | ||
| 808 | (cons '/ args))))) | ||
| 734 | 809 | ||
| 735 | (defun byte-optimize-binary-predicate (form) | 810 | (defun byte-optimize-binary-predicate (form) |
| 736 | (cond | 811 | (cond |
| @@ -800,8 +875,8 @@ | |||
| 800 | (put '> 'byte-optimizer 'byte-optimize-predicate) | 875 | (put '> 'byte-optimizer 'byte-optimize-predicate) |
| 801 | (put '<= 'byte-optimizer 'byte-optimize-predicate) | 876 | (put '<= 'byte-optimizer 'byte-optimize-predicate) |
| 802 | (put '>= 'byte-optimizer 'byte-optimize-predicate) | 877 | (put '>= 'byte-optimizer 'byte-optimize-predicate) |
| 803 | (put '1+ 'byte-optimizer 'byte-optimize-predicate) | 878 | (put '1+ 'byte-optimizer 'byte-optimize-1+) |
| 804 | (put '1- 'byte-optimizer 'byte-optimize-predicate) | 879 | (put '1- 'byte-optimizer 'byte-optimize-1-) |
| 805 | (put 'not 'byte-optimizer 'byte-optimize-predicate) | 880 | (put 'not 'byte-optimizer 'byte-optimize-predicate) |
| 806 | (put 'null 'byte-optimizer 'byte-optimize-predicate) | 881 | (put 'null 'byte-optimizer 'byte-optimize-predicate) |
| 807 | (put 'consp 'byte-optimizer 'byte-optimize-predicate) | 882 | (put 'consp 'byte-optimizer 'byte-optimize-predicate) |
| @@ -854,8 +929,7 @@ | |||
| 854 | ;; Throw away nil's, and simplify if less than 2 args. | 929 | ;; Throw away nil's, and simplify if less than 2 args. |
| 855 | ;; If there is a literal non-nil constant in the args to `or', throw away all | 930 | ;; If there is a literal non-nil constant in the args to `or', throw away all |
| 856 | ;; following forms. | 931 | ;; following forms. |
| 857 | (if (memq nil form) | 932 | (setq form (remq nil form)) |
| 858 | (setq form (delq nil (copy-sequence form)))) | ||
| 859 | (let ((rest form)) | 933 | (let ((rest form)) |
| 860 | (while (cdr (setq rest (cdr rest))) | 934 | (while (cdr (setq rest (cdr rest))) |
| 861 | (if (byte-compile-trueconstp (car rest)) | 935 | (if (byte-compile-trueconstp (car rest)) |
| @@ -872,9 +946,8 @@ | |||
| 872 | (let (rest) | 946 | (let (rest) |
| 873 | ;; This must be first, to reduce (cond (t ...) (nil)) to (progn t ...) | 947 | ;; This must be first, to reduce (cond (t ...) (nil)) to (progn t ...) |
| 874 | (while (setq rest (assq nil (cdr form))) | 948 | (while (setq rest (assq nil (cdr form))) |
| 875 | (setq form (delq rest (copy-sequence form)))) | 949 | (setq form (remq rest form))) |
| 876 | (if (memq nil (cdr form)) | 950 | (setq form (remq nil form)) |
| 877 | (setq form (delq nil (copy-sequence form)))) | ||
| 878 | (setq rest form) | 951 | (setq rest form) |
| 879 | (while (setq rest (cdr rest)) | 952 | (while (setq rest (cdr rest)) |
| 880 | (cond ((byte-compile-trueconstp (car-safe (car rest))) | 953 | (cond ((byte-compile-trueconstp (car-safe (car rest))) |