diff options
| author | Paul Eggert | 2018-03-23 12:57:39 -0700 |
|---|---|---|
| committer | Paul Eggert | 2018-03-23 12:59:18 -0700 |
| commit | 42e7e267e5487f60f4d72e1b5c5cba001ba4d704 (patch) | |
| tree | 8c44c5c43eb779d4478b45b66095e94000b78032 | |
| parent | d0881374734310eb77526823c0acad1fe556f41b (diff) | |
| download | emacs-42e7e267e5487f60f4d72e1b5c5cba001ba4d704.tar.gz emacs-42e7e267e5487f60f4d72e1b5c5cba001ba4d704.zip | |
Avoid Fortran-style floating-point optimization
When optimizing arithmetic operations, avoid optimizations that
are valid for mathematical numbers but invalid for floating-point.
For example, do not optimize (+ 1 v 0.5) to (+ v 1.5), as they may
not be the same due to rounding errors. In general,
floating-point numbers cannot be constant-folded, since that would
make .elc files platform-dependent.
* lisp/emacs-lisp/byte-opt.el (byte-optimize-associative-math):
Do not optimize floats.
(byte-optimize-nonassociative-math, byte-optimize-approx-equal)
(byte-optimize-delay-constants-math, byte-compile-butlast)
(byte-optimize-logmumble):
Remove; no longer used.
(byte-optimize-minus): Do not optimize (- 0 x) to (- x).
(byte-optimize-multiply): Do not optimize (* -1 x) to (- x).
(byte-optimize-divide): Do not optimize (/ x -1) to (- x).
(logand, logior, logxor): Optimize with byte-optimize-predicate
instead of with byte-optimize-logmumble.
* test/lisp/emacs-lisp/bytecomp-tests.el:
(byte-opt-testsuite-arith-data): Add a couple of test cases.
| -rw-r--r-- | lisp/emacs-lisp/byte-opt.el | 168 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/bytecomp-tests.el | 6 |
2 files changed, 24 insertions, 150 deletions
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el index 55343e1e3af..a5e0e219644 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el | |||
| @@ -656,15 +656,15 @@ | |||
| 656 | ((not (symbolp form)) nil) | 656 | ((not (symbolp form)) nil) |
| 657 | ((null form)))) | 657 | ((null form)))) |
| 658 | 658 | ||
| 659 | ;; If the function is being called with constant numeric args, | 659 | ;; If the function is being called with constant integer args, |
| 660 | ;; evaluate as much as possible at compile-time. This optimizer | 660 | ;; evaluate as much as possible at compile-time. This optimizer |
| 661 | ;; assumes that the function is associative, like + or *. | 661 | ;; assumes that the function is associative, like min or max. |
| 662 | (defun byte-optimize-associative-math (form) | 662 | (defun byte-optimize-associative-math (form) |
| 663 | (let ((args nil) | 663 | (let ((args nil) |
| 664 | (constants nil) | 664 | (constants nil) |
| 665 | (rest (cdr form))) | 665 | (rest (cdr form))) |
| 666 | (while rest | 666 | (while rest |
| 667 | (if (numberp (car rest)) | 667 | (if (integerp (car rest)) |
| 668 | (setq constants (cons (car rest) constants)) | 668 | (setq constants (cons (car rest) constants)) |
| 669 | (setq args (cons (car rest) args))) | 669 | (setq args (cons (car rest) args))) |
| 670 | (setq rest (cdr rest))) | 670 | (setq rest (cdr rest))) |
| @@ -678,82 +678,7 @@ | |||
| 678 | (apply (car form) constants)) | 678 | (apply (car form) constants)) |
| 679 | form))) | 679 | form))) |
| 680 | 680 | ||
| 681 | ;; If the function is being called with constant numeric args, | ||
| 682 | ;; evaluate as much as possible at compile-time. This optimizer | ||
| 683 | ;; assumes that the function satisfies | ||
| 684 | ;; (op x1 x2 ... xn) == (op ...(op (op x1 x2) x3) ...xn) | ||
| 685 | ;; like - and /. | ||
| 686 | (defun byte-optimize-nonassociative-math (form) | ||
| 687 | (if (or (not (numberp (car (cdr form)))) | ||
| 688 | (not (numberp (car (cdr (cdr form)))))) | ||
| 689 | form | ||
| 690 | (let ((constant (car (cdr form))) | ||
| 691 | (rest (cdr (cdr form)))) | ||
| 692 | (while (numberp (car rest)) | ||
| 693 | (setq constant (funcall (car form) constant (car rest)) | ||
| 694 | rest (cdr rest))) | ||
| 695 | (if rest | ||
| 696 | (cons (car form) (cons constant rest)) | ||
| 697 | constant)))) | ||
| 698 | |||
| 699 | ;;(defun byte-optimize-associative-two-args-math (form) | ||
| 700 | ;; (setq form (byte-optimize-associative-math form)) | ||
| 701 | ;; (if (consp form) | ||
| 702 | ;; (byte-optimize-two-args-left form) | ||
| 703 | ;; form)) | ||
| 704 | |||
| 705 | ;;(defun byte-optimize-nonassociative-two-args-math (form) | ||
| 706 | ;; (setq form (byte-optimize-nonassociative-math form)) | ||
| 707 | ;; (if (consp form) | ||
| 708 | ;; (byte-optimize-two-args-right form) | ||
| 709 | ;; form)) | ||
| 710 | |||
| 711 | (defun byte-optimize-approx-equal (x y) | ||
| 712 | (<= (* (abs (- x y)) 100) (abs (+ x y)))) | ||
| 713 | |||
| 714 | ;; Collect all the constants from FORM, after the STARTth arg, | ||
| 715 | ;; and apply FUN to them to make one argument at the end. | ||
| 716 | ;; For functions that can handle floats, that optimization | ||
| 717 | ;; can be incorrect because reordering can cause an overflow | ||
| 718 | ;; that would otherwise be avoided by encountering an arg that is a float. | ||
| 719 | ;; We avoid this problem by (1) not moving float constants and | ||
| 720 | ;; (2) not moving anything if it would cause an overflow. | ||
| 721 | (defun byte-optimize-delay-constants-math (form start fun) | ||
| 722 | ;; Merge all FORM's constants from number START, call FUN on them | ||
| 723 | ;; and put the result at the end. | ||
| 724 | (let ((rest (nthcdr (1- start) form)) | ||
| 725 | (orig form) | ||
| 726 | ;; t means we must check for overflow. | ||
| 727 | (overflow (memq fun '(+ *)))) | ||
| 728 | (while (cdr (setq rest (cdr rest))) | ||
| 729 | (if (integerp (car rest)) | ||
| 730 | (let (constants) | ||
| 731 | (setq form (copy-sequence form) | ||
| 732 | rest (nthcdr (1- start) form)) | ||
| 733 | (while (setq rest (cdr rest)) | ||
| 734 | (cond ((integerp (car rest)) | ||
| 735 | (setq constants (cons (car rest) constants)) | ||
| 736 | (setcar rest nil)))) | ||
| 737 | ;; If necessary, check now for overflow | ||
| 738 | ;; that might be caused by reordering. | ||
| 739 | (if (and overflow | ||
| 740 | ;; We have overflow if the result of doing the arithmetic | ||
| 741 | ;; on floats is not even close to the result | ||
| 742 | ;; of doing it on integers. | ||
| 743 | (not (byte-optimize-approx-equal | ||
| 744 | (apply fun (mapcar 'float constants)) | ||
| 745 | (float (apply fun constants))))) | ||
| 746 | (setq form orig) | ||
| 747 | (setq form (nconc (delq nil form) | ||
| 748 | (list (apply fun (nreverse constants))))))))) | ||
| 749 | form)) | ||
| 750 | |||
| 751 | (defsubst byte-compile-butlast (form) | ||
| 752 | (nreverse (cdr (reverse form)))) | ||
| 753 | |||
| 754 | (defun byte-optimize-plus (form) | 681 | (defun byte-optimize-plus (form) |
| 755 | ;; Don't call `byte-optimize-delay-constants-math' (bug#1334). | ||
| 756 | ;;(setq form (byte-optimize-delay-constants-math form 1 '+)) | ||
| 757 | (if (memq 0 form) (setq form (delq 0 (copy-sequence form)))) | 682 | (if (memq 0 form) (setq form (delq 0 (copy-sequence form)))) |
| 758 | ;; For (+ constants...), byte-optimize-predicate does the work. | 683 | ;; For (+ constants...), byte-optimize-predicate does the work. |
| 759 | (when (memq nil (mapcar 'numberp (cdr form))) | 684 | (when (memq nil (mapcar 'numberp (cdr form))) |
| @@ -767,26 +692,19 @@ | |||
| 767 | (setq integer (nth 1 form) other (nth 2 form)) | 692 | (setq integer (nth 1 form) other (nth 2 form)) |
| 768 | (setq integer (nth 2 form) other (nth 1 form))) | 693 | (setq integer (nth 2 form) other (nth 1 form))) |
| 769 | (setq form | 694 | (setq form |
| 770 | (list (if (eq integer 1) '1+ '1-) other)))) | 695 | (list (if (eq integer 1) '1+ '1-) other)))))) |
| 771 | ;; Here, we could also do | ||
| 772 | ;; (+ x y ... 1) --> (1+ (+ x y ...)) | ||
| 773 | ;; (+ x y ... -1) --> (1- (+ x y ...)) | ||
| 774 | ;; The resulting bytecode is smaller, but is it faster? -- cyd | ||
| 775 | )) | ||
| 776 | (byte-optimize-predicate form)) | 696 | (byte-optimize-predicate form)) |
| 777 | 697 | ||
| 778 | (defun byte-optimize-minus (form) | 698 | (defun byte-optimize-minus (form) |
| 779 | ;; Don't call `byte-optimize-delay-constants-math' (bug#1334). | ||
| 780 | ;;(setq form (byte-optimize-delay-constants-math form 2 '+)) | ||
| 781 | ;; Remove zeros. | 699 | ;; Remove zeros. |
| 782 | (when (and (nthcdr 3 form) | 700 | (when (and (nthcdr 3 form) |
| 783 | (memq 0 (cddr form))) | 701 | (memq 0 (cddr form))) |
| 784 | (setq form (nconc (list (car form) (cadr form)) | 702 | (setq form (nconc (list (car form) (cadr form)) |
| 785 | (delq 0 (copy-sequence (cddr form))))) | 703 | (delq 0 (copy-sequence (cddr form))))) |
| 786 | ;; After the above, we must turn (- x) back into (- x 0) | 704 | ;; After the above, we must turn (- x) back into (- x 0). |
| 787 | (or (cddr form) | 705 | (or (cddr form) |
| 788 | (setq form (nconc form (list 0))))) | 706 | (setq form (nconc form (list 0))))) |
| 789 | ;; For (- constants..), byte-optimize-predicate does the work. | 707 | ;; For (- constants...), byte-optimize-predicate does the work. |
| 790 | (when (memq nil (mapcar 'numberp (cdr form))) | 708 | (when (memq nil (mapcar 'numberp (cdr form))) |
| 791 | (cond | 709 | (cond |
| 792 | ;; (- x 1) --> (1- x) | 710 | ;; (- x 1) --> (1- x) |
| @@ -794,71 +712,25 @@ | |||
| 794 | (setq form (list '1- (nth 1 form)))) | 712 | (setq form (list '1- (nth 1 form)))) |
| 795 | ;; (- x -1) --> (1+ x) | 713 | ;; (- x -1) --> (1+ x) |
| 796 | ((equal (nthcdr 2 form) '(-1)) | 714 | ((equal (nthcdr 2 form) '(-1)) |
| 797 | (setq form (list '1+ (nth 1 form)))) | 715 | (setq form (list '1+ (nth 1 form)))))) |
| 798 | ;; (- 0 x) --> (- x) | ||
| 799 | ((and (eq (nth 1 form) 0) | ||
| 800 | (= (length form) 3)) | ||
| 801 | (setq form (list '- (nth 2 form)))) | ||
| 802 | ;; Here, we could also do | ||
| 803 | ;; (- x y ... 1) --> (1- (- x y ...)) | ||
| 804 | ;; (- x y ... -1) --> (1+ (- x y ...)) | ||
| 805 | ;; The resulting bytecode is smaller, but is it faster? -- cyd | ||
| 806 | )) | ||
| 807 | (byte-optimize-predicate form)) | 716 | (byte-optimize-predicate form)) |
| 808 | 717 | ||
| 809 | (defun byte-optimize-multiply (form) | 718 | (defun byte-optimize-multiply (form) |
| 810 | (setq form (byte-optimize-delay-constants-math form 1 '*)) | 719 | (if (memq 1 form) (setq form (delq 1 (copy-sequence form)))) |
| 811 | ;; For (* constants..), byte-optimize-predicate does the work. | 720 | ;; For (* integers..), byte-optimize-predicate does the work. |
| 812 | (when (memq nil (mapcar 'numberp (cdr form))) | ||
| 813 | ;; After `byte-optimize-predicate', if there is a INTEGER constant | ||
| 814 | ;; in FORM, it is in the last element. | ||
| 815 | (let ((last (car (reverse (cdr form))))) | ||
| 816 | (cond | ||
| 817 | ;; Would handling (* ... 0) here cause floating point errors? | ||
| 818 | ;; See bug#1334. | ||
| 819 | ((eq 1 last) (setq form (byte-compile-butlast form))) | ||
| 820 | ((eq -1 last) | ||
| 821 | (setq form (list '- (if (nthcdr 3 form) | ||
| 822 | (byte-compile-butlast form) | ||
| 823 | (nth 1 form)))))))) | ||
| 824 | (byte-optimize-predicate form)) | 721 | (byte-optimize-predicate form)) |
| 825 | 722 | ||
| 826 | (defun byte-optimize-divide (form) | 723 | (defun byte-optimize-divide (form) |
| 827 | (setq form (byte-optimize-delay-constants-math form 2 '*)) | 724 | ;; Remove 1s. |
| 828 | ;; After `byte-optimize-predicate', if there is a INTEGER constant | 725 | (when (and (nthcdr 3 form) |
| 829 | ;; in FORM, it is in the last element. | 726 | (memq 1 (cddr form))) |
| 830 | (let ((last (car (reverse (cdr (cdr form)))))) | 727 | (setq form (nconc (list (car form) (cadr form)) |
| 831 | (cond | 728 | (delq 1 (copy-sequence (cddr form))))) |
| 832 | ;; Runtime error (leave it intact). | 729 | ;; After the above, we must turn (/ x) back into (/ x 1). |
| 833 | ((or (null last) | 730 | (or (cddr form) |
| 834 | (eq last 0) | 731 | (setq form (nconc form (list 1))))) |
| 835 | (memql 0.0 (cddr form)))) | ||
| 836 | ;; No constants in expression | ||
| 837 | ((not (numberp last))) | ||
| 838 | ;; For (* constants..), byte-optimize-predicate does the work. | ||
| 839 | ((null (memq nil (mapcar 'numberp (cdr form))))) | ||
| 840 | ;; (/ x y.. 1) --> (/ x y..) | ||
| 841 | ((and (eq last 1) (nthcdr 3 form)) | ||
| 842 | (setq form (byte-compile-butlast form))) | ||
| 843 | ;; (/ x -1), (/ x .. -1) --> (- x), (- (/ x ..)) | ||
| 844 | ((eq last -1) | ||
| 845 | (setq form (list '- (if (nthcdr 3 form) | ||
| 846 | (byte-compile-butlast form) | ||
| 847 | (nth 1 form))))))) | ||
| 848 | (byte-optimize-predicate form)) | 732 | (byte-optimize-predicate form)) |
| 849 | 733 | ||
| 850 | (defun byte-optimize-logmumble (form) | ||
| 851 | (setq form (byte-optimize-delay-constants-math form 1 (car form))) | ||
| 852 | (byte-optimize-predicate | ||
| 853 | (cond ((memq 0 form) | ||
| 854 | (setq form (if (eq (car form) 'logand) | ||
| 855 | (cons 'progn (cdr form)) | ||
| 856 | (delq 0 (copy-sequence form))))) | ||
| 857 | ((and (eq (car-safe form) 'logior) | ||
| 858 | (memq -1 form)) | ||
| 859 | (cons 'progn (cdr form))) | ||
| 860 | (form)))) | ||
| 861 | |||
| 862 | 734 | ||
| 863 | (defun byte-optimize-binary-predicate (form) | 735 | (defun byte-optimize-binary-predicate (form) |
| 864 | (cond | 736 | (cond |
| @@ -923,9 +795,9 @@ | |||
| 923 | (put 'string< 'byte-optimizer 'byte-optimize-predicate) | 795 | (put 'string< 'byte-optimizer 'byte-optimize-predicate) |
| 924 | (put 'string-lessp 'byte-optimizer 'byte-optimize-predicate) | 796 | (put 'string-lessp 'byte-optimizer 'byte-optimize-predicate) |
| 925 | 797 | ||
| 926 | (put 'logand 'byte-optimizer 'byte-optimize-logmumble) | 798 | (put 'logand 'byte-optimizer 'byte-optimize-predicate) |
| 927 | (put 'logior 'byte-optimizer 'byte-optimize-logmumble) | 799 | (put 'logior 'byte-optimizer 'byte-optimize-predicate) |
| 928 | (put 'logxor 'byte-optimizer 'byte-optimize-logmumble) | 800 | (put 'logxor 'byte-optimizer 'byte-optimize-predicate) |
| 929 | (put 'lognot 'byte-optimizer 'byte-optimize-predicate) | 801 | (put 'lognot 'byte-optimizer 'byte-optimize-predicate) |
| 930 | 802 | ||
| 931 | (put 'car 'byte-optimizer 'byte-optimize-predicate) | 803 | (put 'car 'byte-optimizer 'byte-optimize-predicate) |
diff --git a/test/lisp/emacs-lisp/bytecomp-tests.el b/test/lisp/emacs-lisp/bytecomp-tests.el index 6ae7cdb9f9c..7330c676140 100644 --- a/test/lisp/emacs-lisp/bytecomp-tests.el +++ b/test/lisp/emacs-lisp/bytecomp-tests.el | |||
| @@ -38,8 +38,7 @@ | |||
| 38 | (let ((a 3) (b 2) (c 1.0)) (/ a b c)) | 38 | (let ((a 3) (b 2) (c 1.0)) (/ a b c)) |
| 39 | (let ((a (+ 1 (expt 2 -64))) (b (expt 2 -65))) (+ a -1 b)) | 39 | (let ((a (+ 1 (expt 2 -64))) (b (expt 2 -65))) (+ a -1 b)) |
| 40 | (let ((a (+ 1 (expt 2 -64))) (b (expt 2 -65))) (- a 1 (- b))) | 40 | (let ((a (+ 1 (expt 2 -64))) (b (expt 2 -65))) (- a 1 (- b))) |
| 41 | ;; This fails. Should it be a bug? | 41 | (let ((a (expt 2 -1074)) (b 0.125)) (* a 8 b)) |
| 42 | ;; (let ((a (expt 2 -1074)) (b 0.125)) (* a 8 b)) | ||
| 43 | (let ((a 1.0)) (* a 0)) | 42 | (let ((a 1.0)) (* a 0)) |
| 44 | (let ((a 1.0)) (* a 2.0 0)) | 43 | (let ((a 1.0)) (* a 2.0 0)) |
| 45 | (let ((a 1.0)) (/ 0 a)) | 44 | (let ((a 1.0)) (/ 0 a)) |
| @@ -244,6 +243,9 @@ | |||
| 244 | (let ((a 3) (b 2) (c 1.0)) (/ a b c 0)) | 243 | (let ((a 3) (b 2) (c 1.0)) (/ a b c 0)) |
| 245 | (let ((a 3) (b 2) (c 1.0)) (/ a b c 1)) | 244 | (let ((a 3) (b 2) (c 1.0)) (/ a b c 1)) |
| 246 | (let ((a 3) (b 2) (c 1.0)) (/ a b c -1)) | 245 | (let ((a 3) (b 2) (c 1.0)) (/ a b c -1)) |
| 246 | |||
| 247 | (let ((a t)) (logand 0 a)) | ||
| 248 | |||
| 247 | ;; Test switch bytecode | 249 | ;; Test switch bytecode |
| 248 | (let ((a 3)) (cond ((eq a 1) 'one) ((eq a 2) 'two) ((eq a 3) 'three) (t t))) | 250 | (let ((a 3)) (cond ((eq a 1) 'one) ((eq a 2) 'two) ((eq a 3) 'three) (t t))) |
| 249 | (let ((a 'three)) (cond ((eq a 'one) 1) ((eq a 2) 'two) ((eq a 'three) 3) | 251 | (let ((a 'three)) (cond ((eq a 'one) 1) ((eq a 2) 'two) ((eq a 'three) 3) |