aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Eggert2018-03-23 12:57:39 -0700
committerPaul Eggert2018-03-23 12:59:18 -0700
commit42e7e267e5487f60f4d72e1b5c5cba001ba4d704 (patch)
tree8c44c5c43eb779d4478b45b66095e94000b78032
parentd0881374734310eb77526823c0acad1fe556f41b (diff)
downloademacs-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.el168
-rw-r--r--test/lisp/emacs-lisp/bytecomp-tests.el6
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)