unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Floating-point constant folding in Emacs byte compiler
@ 2018-03-22 23:04 Paul Eggert
  2018-03-23  1:26 ` Stefan Monnier
  2018-03-23 20:52 ` Pip Cet
  0 siblings, 2 replies; 39+ messages in thread
From: Paul Eggert @ 2018-03-22 23:04 UTC (permalink / raw)
  To: Emacs development discussions

[-- Attachment #1: Type: text/plain, Size: 1085 bytes --]

While preparing and installing the attached patch to Emacs master, I 
noticed that the byte optimizer assumes that floating-point arithmetic 
behaves the same at compile-time that it does at runtime. For example, 
on my x86-64 platform the byte compiler optimizes (/ 0 0.0) to -NaN, (- 
1 1.0000000000000001) to 0.0, and (< 1 1.0000000000000001) to nil, even 
though these expressions will evaluate to different values on (say) an 
IBM mainframe that uses IBM floating point, and the first expression 
yields +NaN on some IEEE platforms (e.g., ARM).

These discrepancies mean that .elc files containing floating-point 
constants might not be platform-independent, in that byte-compiling a 
file on one machine X and running it on another machine Y can yield 
different results than byte-compiling and running the same file on Y. Is 
this sort of discrepancy intended? If so, should it be documented in the 
Emacs Lisp manual? On the one hand, I doubt whether this sort of 
optimization buys us much performance; on the other, I doubt whether 
many users care about the discrepancies.


[-- Attachment #2: 0001-Fix-byte-opt-lists-of-pure-functions-etc.txt --]
[-- Type: text/plain, Size: 5339 bytes --]

From 3b1024ac8162a66cc6baf6f7f339d9f73bcb6b90 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Thu, 22 Mar 2018 11:25:42 -0700
Subject: [PATCH] Fix byte-opt lists of pure functions etc.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This fixes a bug where a byte-compiler running on 64-bit Emacs
optimized (lsh -1 -1) to #x1fffffffffffffff, an optimization
that is incorrect for .elc files intended for either 32- or
64-bit Emacs.  While I was in the neighborhood, I noticed other
glitches in the lists of pure and side-effect-free functions, and
fixed the errors that I found.
* lisp/emacs-lisp/byte-opt.el (side-effect-free-fns):
Move some functions here from side-effect-and-error-free-fns,
since they can now signal errors.  The affected functions are
current-time-string, current-time-zone,
line-beginning-position, line-end-position.  Rename langinfo
to locale-info.  Add logcount.  Remove string-to-int.
(side-effect-and-error-free-fns): Remove minibuffer-window, a
function that can signal errors, and that is already in
side-effect-free-fns.
(pure-fns): Remove ash, lsh, and logb, since they are
platform-dependent and .elc files should be
platform-independent.  Add %, logand, logcount.  Sort.
Clarify what is meant by “pure”.
---
 lisp/emacs-lisp/byte-opt.el | 37 +++++++++++++++++++++++--------------
 1 file changed, 23 insertions(+), 14 deletions(-)

diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index a316364761..55343e1e3a 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -1186,6 +1186,7 @@ byte-optimize-set
 	 char-equal char-to-string char-width compare-strings
 	 compare-window-configurations concat coordinates-in-window-p
 	 copy-alist copy-sequence copy-marker cos count-lines
+	 current-time-string current-time-zone
 	 decode-char
 	 decode-time default-boundp default-value documentation downcase
 	 elt encode-char exp expt encode-time error-message-string
@@ -1199,8 +1200,9 @@ byte-optimize-set
 	 hash-table-count
 	 int-to-string intern-soft
 	 keymap-parent
-	 length local-variable-if-set-p local-variable-p log log10 logand
-	 logb logior lognot logxor lsh langinfo
+	 length line-beginning-position line-end-position
+	 local-variable-if-set-p local-variable-p locale-info
+	 log log10 logand logb logcount logior lognot logxor lsh
 	 make-list make-string make-symbol marker-buffer max member memq min
 	 minibuffer-selected-window minibuffer-window
 	 mod multibyte-char-to-unibyte next-window nth nthcdr number-to-string
@@ -1210,7 +1212,7 @@ byte-optimize-set
 	 radians-to-degrees rassq rassoc read-from-string regexp-quote
 	 region-beginning region-end reverse round
 	 sin sqrt string string< string= string-equal string-lessp string-to-char
-	 string-to-int string-to-number substring
+	 string-to-number substring
 	 sxhash sxhash-equal sxhash-eq sxhash-eql
 	 symbol-function symbol-name symbol-plist symbol-value string-make-unibyte
 	 string-make-multibyte string-as-multibyte string-as-unibyte
@@ -1240,7 +1242,6 @@ byte-optimize-set
 	 charsetp commandp cons consp
 	 current-buffer current-global-map current-indentation
 	 current-local-map current-minor-mode-maps current-time
-	 current-time-string current-time-zone
 	 eobp eolp eq equal eventp
 	 floatp following-char framep
 	 get-largest-window get-lru-window
@@ -1248,9 +1249,9 @@ byte-optimize-set
 	 identity ignore integerp integer-or-marker-p interactive-p
 	 invocation-directory invocation-name
 	 keymapp keywordp
-	 line-beginning-position line-end-position list listp
+	 list listp
 	 make-marker mark mark-marker markerp max-char
-	 memory-limit minibuffer-window
+	 memory-limit
 	 mouse-movement-p
 	 natnump nlistp not null number-or-marker-p numberp
 	 one-window-p overlayp
@@ -1275,16 +1276,24 @@ byte-optimize-set
   nil)
 
 \f
-;; pure functions are side-effect free functions whose values depend
-;; only on their arguments. For these functions, calls with constant
-;; arguments can be evaluated at compile time. This may shift run time
-;; errors to compile time.
+;; Pure functions are side-effect free functions whose values depend
+;; only on their arguments, not on the platform.  For these functions,
+;; calls with constant arguments can be evaluated at compile time.
+;; This may shift runtime errors to compile time.  For example, logand
+;; is pure since its results are machine-independent, whereas ash is
+;; not pure because (ash 1 29)'s value depends on machine word size.
+;;
+;; When deciding whether a function is pure, do not worry about
+;; mutable strings or markers, as they are so unlikely in real code
+;; that they are not worth worrying about.  Thus string-to-char is
+;; pure even though it might return different values if a string is
+;; changed, and logand is pure even though it might return different
+;; values if a marker is moved.
 
 (let ((pure-fns
-       '(concat symbol-name regexp-opt regexp-quote string-to-syntax
-         string-to-char
-         ash lsh logb lognot logior logxor
-         ceiling floor)))
+       '(% concat logand logcount logior lognot logxor
+	 regexp-opt regexp-quote
+	 string-to-char string-to-syntax symbol-name)))
   (while pure-fns
     (put (car pure-fns) 'pure t)
     (setq pure-fns (cdr pure-fns)))
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-22 23:04 Floating-point constant folding in Emacs byte compiler Paul Eggert
@ 2018-03-23  1:26 ` Stefan Monnier
  2018-03-23  5:22   ` Paul Eggert
  2018-03-23  8:15   ` Eli Zaretskii
  2018-03-23 20:52 ` Pip Cet
  1 sibling, 2 replies; 39+ messages in thread
From: Stefan Monnier @ 2018-03-23  1:26 UTC (permalink / raw)
  To: emacs-devel

> These discrepancies mean that .elc files containing floating-point constants
> might not be platform-independent, in that byte-compiling a file on one
> machine X and running it on another machine Y can yield different results
> than byte-compiling and running the same file on Y. Is this sort of
> discrepancy intended?

I'm pretty sure those runtime differences are not on-purpose, so whether
they show up at run-time or compile-time doesn't matter.
IOW we can keep optimizing those.


        Stefan




^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-23  1:26 ` Stefan Monnier
@ 2018-03-23  5:22   ` Paul Eggert
  2018-03-23  8:24     ` Eli Zaretskii
  2018-03-23  8:15   ` Eli Zaretskii
  1 sibling, 1 reply; 39+ messages in thread
From: Paul Eggert @ 2018-03-23  5:22 UTC (permalink / raw)
  To: Stefan Monnier, emacs-devel

Stefan Monnier wrote:
> I'm pretty sure those runtime differences are not on-purpose

Although that's most likely true for the examples I gave, it's a problematic 
assumption for code that intentionally inspects runtime behavior.

I mention this because my recently-installed byte-opt patch fixed a problem of 
this sort with lsh. byte-opt was evaluating (lsh -1 -1) at compile-time, whereas 
the intent of the (lsh -1 -1) was to calculate most-positive-fixnum at run-time. 
It's not hard to imagine similar sorts of code to inspect properties of the 
machine's floating-point behavior, code that would yield the wrong value if 
compiled on a machine with different floating-point properties.

Nowadays Emacs hosts almost invariably use IEEE floating point, and I suppose we 
could hardwire that assumption into Emacs. Even so, IEEE floating-point is not 
bit-for-bit identical on all platforms, so if we're constant-folding 
floating-point expressions surely we should at least document that we're doing 
so, so that programmers know that they can't rely on constant expressions 
yielding a different value than what run-time evaluation would deliver.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-23  1:26 ` Stefan Monnier
  2018-03-23  5:22   ` Paul Eggert
@ 2018-03-23  8:15   ` Eli Zaretskii
  1 sibling, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2018-03-23  8:15 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

> From: Stefan Monnier <monnier@iro.umontreal.ca>
> Date: Thu, 22 Mar 2018 21:26:00 -0400
> 
> > These discrepancies mean that .elc files containing floating-point constants
> > might not be platform-independent, in that byte-compiling a file on one
> > machine X and running it on another machine Y can yield different results
> > than byte-compiling and running the same file on Y. Is this sort of
> > discrepancy intended?
> 
> I'm pretty sure those runtime differences are not on-purpose, so whether
> they show up at run-time or compile-time doesn't matter.
> IOW we can keep optimizing those.

I don't understand your conclusion: if this is unintended, then IMO we
should stop optimizing these, so that byte code is more portable.  Or
am I missing something?



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-23  5:22   ` Paul Eggert
@ 2018-03-23  8:24     ` Eli Zaretskii
  2018-03-23 20:00       ` Paul Eggert
  0 siblings, 1 reply; 39+ messages in thread
From: Eli Zaretskii @ 2018-03-23  8:24 UTC (permalink / raw)
  To: Paul Eggert; +Cc: monnier, emacs-devel

> From: Paul Eggert <eggert@cs.ucla.edu>
> Date: Thu, 22 Mar 2018 22:22:25 -0700
> 
> Nowadays Emacs hosts almost invariably use IEEE floating point, and I suppose we 
> could hardwire that assumption into Emacs. Even so, IEEE floating-point is not 
> bit-for-bit identical on all platforms, so if we're constant-folding 
> floating-point expressions surely we should at least document that we're doing 
> so, so that programmers know that they can't rely on constant expressions 
> yielding a different value than what run-time evaluation would deliver.

I think we should strive to leave the byte code platform-independent.
If nothing else, it will make sure the *.elc files in a release
tarball can be used on all supported platforms with identical
results.  Otherwise, we will need to byte-compile files at build time,
something that will make the build much longer than it should be.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-23  8:24     ` Eli Zaretskii
@ 2018-03-23 20:00       ` Paul Eggert
  0 siblings, 0 replies; 39+ messages in thread
From: Paul Eggert @ 2018-03-23 20:00 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: monnier, emacs-devel

[-- Attachment #1: Type: text/plain, Size: 889 bytes --]

On 03/23/2018 01:24 AM, Eli Zaretskii wrote:
> I think we should strive to leave the byte code platform-independent.

That's my thought as well. The byte-compiler was taking the Fortran 
attitude that if an optimization would be allowed on an 
infinite-precision machine, then do it even if it results in different 
answers on real machines due to rounding error or overflow. This loses 
predictability and means that .elc files are not machine-independent. 
Althopugh performance trumps predictability for Fortran, predictability 
is more important for Emacs, as its users will not notice any 
performance loss but they might be affected by the differing answers.

I took a quick look through the byte optimizer and removed the 
Fortran-style floating-point optimizations that I found, by installing 
the attached. This doesn't seem to affect performance significantly on 
Emacs itself.


[-- Attachment #2: 0001-Avoid-Fortran-style-floating-point-optimization.patch --]
[-- Type: text/x-patch, Size: 11689 bytes --]

From 34a2afa85daf513631512309f01aea55f77f6fec Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Fri, 23 Mar 2018 12:57:39 -0700
Subject: [PATCH] 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.
---
 lisp/emacs-lisp/byte-opt.el            | 168 ++++-----------------------------
 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 55343e1e3a..a5e0e21964 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -656,15 +656,15 @@ byte-compile-nilconstp
         ((not (symbolp form)) nil)
         ((null form))))
 
-;; If the function is being called with constant numeric args,
+;; 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 + or *.
+;; 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 (numberp (car rest))
+      (if (integerp (car rest))
 	  (setq constants (cons (car rest) constants))
 	  (setq args (cons (car rest) args)))
       (setq rest (cdr rest)))
@@ -678,82 +678,7 @@ byte-optimize-associative-math
 	    (apply (car form) constants))
 	form)))
 
-;; If the function is being called with constant numeric args,
-;; evaluate as much as possible at compile-time.  This optimizer
-;; assumes that the function satisfies
-;;   (op x1 x2 ... xn) == (op ...(op (op x1 x2) x3) ...xn)
-;; like - and /.
-(defun byte-optimize-nonassociative-math (form)
-  (if (or (not (numberp (car (cdr form))))
-	  (not (numberp (car (cdr (cdr form))))))
-      form
-    (let ((constant (car (cdr form)))
-	  (rest (cdr (cdr form))))
-      (while (numberp (car rest))
-	(setq constant (funcall (car form) constant (car rest))
-	      rest (cdr rest)))
-      (if rest
-	  (cons (car form) (cons constant rest))
-	  constant))))
-
-;;(defun byte-optimize-associative-two-args-math (form)
-;;  (setq form (byte-optimize-associative-math form))
-;;  (if (consp form)
-;;      (byte-optimize-two-args-left form)
-;;      form))
-
-;;(defun byte-optimize-nonassociative-two-args-math (form)
-;;  (setq form (byte-optimize-nonassociative-math form))
-;;  (if (consp form)
-;;      (byte-optimize-two-args-right form)
-;;      form))
-
-(defun byte-optimize-approx-equal (x y)
-  (<= (* (abs (- x y)) 100) (abs (+ x y))))
-
-;; Collect all the constants from FORM, after the STARTth arg,
-;; and apply FUN to them to make one argument at the end.
-;; For functions that can handle floats, that optimization
-;; can be incorrect because reordering can cause an overflow
-;; that would otherwise be avoided by encountering an arg that is a float.
-;; We avoid this problem by (1) not moving float constants and
-;; (2) not moving anything if it would cause an overflow.
-(defun byte-optimize-delay-constants-math (form start fun)
-  ;; Merge all FORM's constants from number START, call FUN on them
-  ;; and put the result at the end.
-  (let ((rest (nthcdr (1- start) form))
-	(orig form)
-	;; t means we must check for overflow.
-	(overflow (memq fun '(+ *))))
-    (while (cdr (setq rest (cdr rest)))
-      (if (integerp (car rest))
-	  (let (constants)
-	    (setq form (copy-sequence form)
-		  rest (nthcdr (1- start) form))
-	    (while (setq rest (cdr rest))
-	      (cond ((integerp (car rest))
-		     (setq constants (cons (car rest) constants))
-		     (setcar rest nil))))
-	    ;; If necessary, check now for overflow
-	    ;; that might be caused by reordering.
-	    (if (and overflow
-		     ;; We have overflow if the result of doing the arithmetic
-		     ;; on floats is not even close to the result
-		     ;; of doing it on integers.
-		     (not (byte-optimize-approx-equal
-			    (apply fun (mapcar 'float constants))
-			    (float (apply fun constants)))))
-		(setq form orig)
-	      (setq form (nconc (delq nil form)
-				(list (apply fun (nreverse constants)))))))))
-    form))
-
-(defsubst byte-compile-butlast (form)
-  (nreverse (cdr (reverse form))))
-
 (defun byte-optimize-plus (form)
-  ;; Don't call `byte-optimize-delay-constants-math' (bug#1334).
-  ;;(setq form (byte-optimize-delay-constants-math form 1 '+))
   (if (memq 0 form) (setq form (delq 0 (copy-sequence form))))
   ;; For (+ constants...), byte-optimize-predicate does the work.
   (when (memq nil (mapcar 'numberp (cdr form)))
@@ -767,26 +692,19 @@ byte-optimize-plus
 	    (setq integer (nth 1 form) other (nth 2 form))
 	  (setq integer (nth 2 form) other (nth 1 form)))
 	(setq form
-	      (list (if (eq integer 1) '1+ '1-) other))))
-     ;; Here, we could also do
-     ;;  (+ x y ... 1) --> (1+ (+ x y ...))
-     ;;  (+ x y ... -1) --> (1- (+ x y ...))
-     ;; The resulting bytecode is smaller, but is it faster? -- cyd
-     ))
+	      (list (if (eq integer 1) '1+ '1-) other))))))
   (byte-optimize-predicate form))
 
 (defun byte-optimize-minus (form)
-  ;; Don't call `byte-optimize-delay-constants-math' (bug#1334).
-  ;;(setq form (byte-optimize-delay-constants-math form 2 '+))
   ;; Remove zeros.
   (when (and (nthcdr 3 form)
 	     (memq 0 (cddr form)))
     (setq form (nconc (list (car form) (cadr form))
 		      (delq 0 (copy-sequence (cddr form)))))
-    ;; After the above, we must turn (- x) back into (- x 0)
+    ;; After the above, we must turn (- x) back into (- x 0).
     (or (cddr form)
 	(setq form (nconc form (list 0)))))
-  ;; For (- constants..), byte-optimize-predicate does the work.
+  ;; For (- constants...), byte-optimize-predicate does the work.
   (when (memq nil (mapcar 'numberp (cdr form)))
     (cond
      ;; (- x 1) --> (1- x)
@@ -794,71 +712,25 @@ byte-optimize-minus
       (setq form (list '1- (nth 1 form))))
      ;; (- x -1) --> (1+ x)
      ((equal (nthcdr 2 form) '(-1))
-      (setq form (list '1+ (nth 1 form))))
-     ;; (- 0 x) --> (- x)
-     ((and (eq (nth 1 form) 0)
-	   (= (length form) 3))
-      (setq form (list '- (nth 2 form))))
-     ;; Here, we could also do
-     ;;  (- x y ... 1) --> (1- (- x y ...))
-     ;;  (- x y ... -1) --> (1+ (- x y ...))
-     ;; The resulting bytecode is smaller, but is it faster? -- cyd
-     ))
+      (setq form (list '1+ (nth 1 form))))))
   (byte-optimize-predicate form))
 
 (defun byte-optimize-multiply (form)
-  (setq form (byte-optimize-delay-constants-math form 1 '*))
-  ;; For (* constants..), byte-optimize-predicate does the work.
-  (when (memq nil (mapcar 'numberp (cdr form)))
-    ;; After `byte-optimize-predicate', if there is a INTEGER constant
-    ;; in FORM, it is in the last element.
-    (let ((last (car (reverse (cdr form)))))
-      (cond
-       ;; Would handling (* ... 0) here cause floating point errors?
-       ;; See bug#1334.
-       ((eq 1 last) (setq form (byte-compile-butlast form)))
-       ((eq -1 last)
-	(setq form (list '- (if (nthcdr 3 form)
-				(byte-compile-butlast form)
-			      (nth 1 form))))))))
+  (if (memq 1 form) (setq form (delq 1 (copy-sequence form))))
+  ;; For (* integers..), byte-optimize-predicate does the work.
   (byte-optimize-predicate form))
 
 (defun byte-optimize-divide (form)
-  (setq form (byte-optimize-delay-constants-math form 2 '*))
-  ;; After `byte-optimize-predicate', if there is a INTEGER constant
-  ;; in FORM, it is in the last element.
-  (let ((last (car (reverse (cdr (cdr form))))))
-    (cond
-     ;; Runtime error (leave it intact).
-     ((or (null last)
-	  (eq last 0)
-	  (memql 0.0 (cddr form))))
-     ;; No constants in expression
-     ((not (numberp last)))
-     ;; For (* constants..), byte-optimize-predicate does the work.
-     ((null (memq nil (mapcar 'numberp (cdr form)))))
-     ;; (/ x y.. 1) --> (/ x y..)
-     ((and (eq last 1) (nthcdr 3 form))
-      (setq form (byte-compile-butlast form)))
-     ;; (/ x -1), (/ x .. -1)  --> (- x), (- (/ x ..))
-     ((eq last -1)
-      (setq form (list '- (if (nthcdr 3 form)
-			      (byte-compile-butlast form)
-			    (nth 1 form)))))))
+  ;; Remove 1s.
+  (when (and (nthcdr 3 form)
+	     (memq 1 (cddr form)))
+    (setq form (nconc (list (car form) (cadr form))
+		      (delq 1 (copy-sequence (cddr form)))))
+    ;; After the above, we must turn (/ x) back into (/ x 1).
+    (or (cddr form)
+	(setq form (nconc form (list 1)))))
   (byte-optimize-predicate form))
 
-(defun byte-optimize-logmumble (form)
-  (setq form (byte-optimize-delay-constants-math form 1 (car form)))
-  (byte-optimize-predicate
-   (cond ((memq 0 form)
-	  (setq form (if (eq (car form) 'logand)
-			 (cons 'progn (cdr form))
-		       (delq 0 (copy-sequence form)))))
-	 ((and (eq (car-safe form) 'logior)
-	       (memq -1 form))
-	  (cons 'progn (cdr form)))
-	 (form))))
-
 
 (defun byte-optimize-binary-predicate (form)
   (cond
@@ -923,9 +795,9 @@ byte-optimize-identity
 (put 'string< 'byte-optimizer 'byte-optimize-predicate)
 (put 'string-lessp 'byte-optimizer 'byte-optimize-predicate)
 
-(put 'logand 'byte-optimizer 'byte-optimize-logmumble)
-(put 'logior 'byte-optimizer 'byte-optimize-logmumble)
-(put 'logxor 'byte-optimizer 'byte-optimize-logmumble)
+(put 'logand 'byte-optimizer 'byte-optimize-predicate)
+(put 'logior 'byte-optimizer 'byte-optimize-predicate)
+(put 'logxor 'byte-optimizer 'byte-optimize-predicate)
 (put 'lognot 'byte-optimizer 'byte-optimize-predicate)
 
 (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 6ae7cdb9f9..7330c67614 100644
--- a/test/lisp/emacs-lisp/bytecomp-tests.el
+++ b/test/lisp/emacs-lisp/bytecomp-tests.el
@@ -38,8 +38,7 @@ byte-opt-testsuite-arith-data
     (let ((a 3) (b 2) (c 1.0))                     (/ a b c))
     (let ((a (+ 1 (expt 2 -64))) (b (expt 2 -65))) (+ a -1 b))
     (let ((a (+ 1 (expt 2 -64))) (b (expt 2 -65))) (- a 1 (- b)))
-    ;; This fails.  Should it be a bug?
-    ;; (let ((a (expt 2 -1074)) (b 0.125))		   (* a 8 b))
+    (let ((a (expt 2 -1074)) (b 0.125))		   (* a 8 b))
     (let ((a 1.0))				   (* a 0))
     (let ((a 1.0))				   (* a 2.0 0))
     (let ((a 1.0))				   (/ 0 a))
@@ -244,6 +243,9 @@ byte-opt-testsuite-arith-data
     (let ((a 3) (b 2) (c 1.0)) (/ a b c 0))
     (let ((a 3) (b 2) (c 1.0)) (/ a b c 1))
     (let ((a 3) (b 2) (c 1.0)) (/ a b c -1))
+
+    (let ((a t)) (logand 0 a))
+
     ;; Test switch bytecode
     (let ((a 3)) (cond ((eq a 1) 'one) ((eq a 2) 'two) ((eq a 3) 'three) (t t)))
     (let ((a 'three)) (cond ((eq a 'one) 1) ((eq a 2) 'two) ((eq a 'three) 3)
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-22 23:04 Floating-point constant folding in Emacs byte compiler Paul Eggert
  2018-03-23  1:26 ` Stefan Monnier
@ 2018-03-23 20:52 ` Pip Cet
  2018-03-24  6:25   ` Eli Zaretskii
  1 sibling, 1 reply; 39+ messages in thread
From: Pip Cet @ 2018-03-23 20:52 UTC (permalink / raw)
  To: Paul Eggert; +Cc: Emacs development discussions

Is this really specific to floating-point expressions, though?

(byte-compile (lambda (x) (* #x10000 #x10000 #x10000 #x10000)))
#[(x) "À\207" [0] 1]

(at least on this somewhat oudated version of Emacs). Note the 0 in
the constant vector, which hardcodes the <65-bit-ness of fixnums.

I imagine that on a 32-bit version of emacs, (byte-compile (lambda (x)
(* #x10000 #x10000))) similarly produces a constant-zero function,
which will produce the wrong result when fixnums are actually 62 bits
long.

(This is tangential, but I think that it might make sense, at some
point in the future, to switch to JavaScript-like
everything-is-a-float-but-there's-just-one-NaN "numbers"; that would
be quite a bit of work already, since several functions do different
things for integer and float arguments (I feel they should be fixed
anyway), and it would be nice to be able to point to the current
byte-opt situation as evidence that if you're relying on unusual
number behavior, you're already doing something wrong)



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-23 20:52 ` Pip Cet
@ 2018-03-24  6:25   ` Eli Zaretskii
  2018-03-26  9:39     ` Robert Pluim
  0 siblings, 1 reply; 39+ messages in thread
From: Eli Zaretskii @ 2018-03-24  6:25 UTC (permalink / raw)
  To: Pip Cet; +Cc: eggert, emacs-devel

> From: Pip Cet <pipcet@gmail.com>
> Date: Fri, 23 Mar 2018 20:52:52 +0000
> Cc: Emacs development discussions <emacs-devel@gnu.org>
> 
> Is this really specific to floating-point expressions, though?
> 
> (byte-compile (lambda (x) (* #x10000 #x10000 #x10000 #x10000)))
> #[(x) "À\207" [0] 1]
> 
> (at least on this somewhat oudated version of Emacs). Note the 0 in
> the constant vector, which hardcodes the <65-bit-ness of fixnums.
> 
> I imagine that on a 32-bit version of emacs, (byte-compile (lambda (x)
> (* #x10000 #x10000))) similarly produces a constant-zero function,

The result depends on whether Emacs was build --with-wide-int.  If it
was, the result is #[(x) "\300\207" [4294967296] 1], if it wasn't, I
get #[(x) "\300\207" [0] 1].



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-24  6:25   ` Eli Zaretskii
@ 2018-03-26  9:39     ` Robert Pluim
  2018-03-26 15:13       ` Eli Zaretskii
  0 siblings, 1 reply; 39+ messages in thread
From: Robert Pluim @ 2018-03-26  9:39 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: eggert, Pip Cet, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Pip Cet <pipcet@gmail.com>
>> Date: Fri, 23 Mar 2018 20:52:52 +0000
>> Cc: Emacs development discussions <emacs-devel@gnu.org>
>> 
>> Is this really specific to floating-point expressions, though?
>> 
>> (byte-compile (lambda (x) (* #x10000 #x10000 #x10000 #x10000)))
>> #[(x) "À\207" [0] 1]
>> 
>> (at least on this somewhat oudated version of Emacs). Note the 0 in
>> the constant vector, which hardcodes the <65-bit-ness of fixnums.
>> 
>> I imagine that on a 32-bit version of emacs, (byte-compile (lambda (x)
>> (* #x10000 #x10000))) similarly produces a constant-zero function,
>
> The result depends on whether Emacs was build --with-wide-int.  If it
> was, the result is #[(x) "\300\207" [4294967296] 1], if it wasn't, I
> get #[(x) "\300\207" [0] 1].

Hmm, would it be worthwhile to have Emacs signal overflow in such a
situation (perhaps controlled by a configuration variable) so we could
fix such issues?

Robert



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-26  9:39     ` Robert Pluim
@ 2018-03-26 15:13       ` Eli Zaretskii
  2018-03-26 15:57         ` Robert Pluim
  2018-03-26 17:52         ` Stefan Monnier
  0 siblings, 2 replies; 39+ messages in thread
From: Eli Zaretskii @ 2018-03-26 15:13 UTC (permalink / raw)
  To: Robert Pluim; +Cc: emacs-devel

> X-Spam-Status: No, score=-0.5 required=5.0 tests=BAYES_05,FREEMAIL_FROM,
> 	T_DKIM_INVALID autolearn=disabled version=3.3.2
> From: Robert Pluim <rpluim@gmail.com>
> Cc: Pip Cet <pipcet@gmail.com>,  eggert@cs.ucla.edu,  emacs-devel@gnu.org
> Gmane-Reply-To-List: yes
> Date: Mon, 26 Mar 2018 11:39:25 +0200
> 
> >> I imagine that on a 32-bit version of emacs, (byte-compile (lambda (x)
> >> (* #x10000 #x10000))) similarly produces a constant-zero function,
> >
> > The result depends on whether Emacs was build --with-wide-int.  If it
> > was, the result is #[(x) "\300\207" [4294967296] 1], if it wasn't, I
> > get #[(x) "\300\207" [0] 1].
> 
> Hmm, would it be worthwhile to have Emacs signal overflow in such a
> situation (perhaps controlled by a configuration variable) so we could
> fix such issues?

In which of these two cases do you see overflow?

I also don't think I understand the utility of signaling an overflow
error from the byte compiler that happened because it did constant
folding.  What else except bug reports could this yield?



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-26 15:13       ` Eli Zaretskii
@ 2018-03-26 15:57         ` Robert Pluim
  2018-03-26 16:02           ` Eli Zaretskii
  2018-03-26 17:52         ` Stefan Monnier
  1 sibling, 1 reply; 39+ messages in thread
From: Robert Pluim @ 2018-03-26 15:57 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> X-Spam-Status: No, score=-0.5 required=5.0 tests=BAYES_05,FREEMAIL_FROM,
>> 	T_DKIM_INVALID autolearn=disabled version=3.3.2
>> From: Robert Pluim <rpluim@gmail.com>
>> Cc: Pip Cet <pipcet@gmail.com>,  eggert@cs.ucla.edu,  emacs-devel@gnu.org
>> Gmane-Reply-To-List: yes
>> Date: Mon, 26 Mar 2018 11:39:25 +0200
>> 
>> >> I imagine that on a 32-bit version of emacs, (byte-compile (lambda (x)
>> >> (* #x10000 #x10000))) similarly produces a constant-zero function,
>> >
>> > The result depends on whether Emacs was build --with-wide-int.  If it
>> > was, the result is #[(x) "\300\207" [4294967296] 1], if it wasn't, I
>> > get #[(x) "\300\207" [0] 1].
>> 
>> Hmm, would it be worthwhile to have Emacs signal overflow in such a
>> situation (perhaps controlled by a configuration variable) so we could
>> fix such issues?
>
> In which of these two cases do you see overflow?
>

Does (* #x10000 #x10000) not overflow on 32-bit? Or have I missed a
bit somewhere?

> I also don't think I understand the utility of signaling an overflow
> error from the byte compiler that happened because it did constant
> folding.  What else except bug reports could this yield?

I was proposing it as a debug option, not a generally enabled one. Of
course if there's no overflow, it would be kind of useless :-)

Robert



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-26 15:57         ` Robert Pluim
@ 2018-03-26 16:02           ` Eli Zaretskii
  2018-03-26 18:23             ` Pip Cet
  0 siblings, 1 reply; 39+ messages in thread
From: Eli Zaretskii @ 2018-03-26 16:02 UTC (permalink / raw)
  To: Robert Pluim; +Cc: emacs-devel

> From: Robert Pluim <rpluim@gmail.com>
> Cc: emacs-devel@gnu.org
> Date: Mon, 26 Mar 2018 17:57:01 +0200
> 
> >> >> I imagine that on a 32-bit version of emacs, (byte-compile (lambda (x)
> >> >> (* #x10000 #x10000))) similarly produces a constant-zero function,
> >> >
> >> > The result depends on whether Emacs was build --with-wide-int.  If it
> >> > was, the result is #[(x) "\300\207" [4294967296] 1], if it wasn't, I
> >> > get #[(x) "\300\207" [0] 1].
> >> 
> >> Hmm, would it be worthwhile to have Emacs signal overflow in such a
> >> situation (perhaps controlled by a configuration variable) so we could
> >> fix such issues?
> >
> > In which of these two cases do you see overflow?
> >
> 
> Does (* #x10000 #x10000) not overflow on 32-bit?

Only if Emacs was not built --with-wide-int.

> > I also don't think I understand the utility of signaling an overflow
> > error from the byte compiler that happened because it did constant
> > folding.  What else except bug reports could this yield?
> 
> I was proposing it as a debug option, not a generally enabled one. Of
> course if there's no overflow, it would be kind of useless :-)

If we want the byte code to be portable, the results of compiling
cannot depend on the machine on which the code was byte-compiled.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-26 15:13       ` Eli Zaretskii
  2018-03-26 15:57         ` Robert Pluim
@ 2018-03-26 17:52         ` Stefan Monnier
  2018-03-26 18:30           ` Eli Zaretskii
  2018-03-27  0:08           ` Paul Eggert
  1 sibling, 2 replies; 39+ messages in thread
From: Stefan Monnier @ 2018-03-26 17:52 UTC (permalink / raw)
  To: emacs-devel

>> Hmm, would it be worthwhile to have Emacs signal overflow in such a
>> situation (perhaps controlled by a configuration variable) so we could
>> fix such issues?

I think TRT is for the byte-compiler to refrain from performing this
optimization when the result doesn't fit within 30bits.


        Stefan




^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-26 16:02           ` Eli Zaretskii
@ 2018-03-26 18:23             ` Pip Cet
  2018-03-26 18:29               ` Eli Zaretskii
  2018-03-27  0:28               ` Paul Eggert
  0 siblings, 2 replies; 39+ messages in thread
From: Pip Cet @ 2018-03-26 18:23 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Robert Pluim, emacs-devel

Well, right now the byte code (or at least the constant vector) does
depend on the machine on which the code was byte-compiled, but it's
also read differently: if I understand things correctly, large
integers in decimal notation are automatically converted to floats,
while large hex constants signal an overflow error. I believe the
former case applies to the read syntax for byte-compiled functions.

But is this really a problem for any actual code? Good code shouldn't
depend on more than the 30 bits Emacs guarantees, and IMHO it
shouldn't distinguish between integers and their floating-point
representations. In other words, I think we should specify less
behavior for our numbers, not more, and keep the byte compiler simple.
If code is broken for any of 30-bit integers, 62-bit integers, 32-bit
integers, 48-bit integers, integers-as-IEEE-doubles, or bignums, or
any combination of two of those for compilation and reading/running
bytecode, that's a bug which makes it harder to play around with Emacs
internals.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-26 18:23             ` Pip Cet
@ 2018-03-26 18:29               ` Eli Zaretskii
  2018-03-27  0:28               ` Paul Eggert
  1 sibling, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2018-03-26 18:29 UTC (permalink / raw)
  To: Pip Cet; +Cc: rpluim, emacs-devel

> From: Pip Cet <pipcet@gmail.com>
> Date: Mon, 26 Mar 2018 18:23:15 +0000
> Cc: Robert Pluim <rpluim@gmail.com>, emacs-devel@gnu.org
> 
> But is this really a problem for any actual code? Good code shouldn't
> depend on more than the 30 bits Emacs guarantees

IMO, it is no longer reasonable nowadays to require "good code" to be
limited to 30 bits.  Most people use 64-bit platforms, and those who
don't should be able to run the code with some run-time penalty, but
without overflows, errors, and any similar calamities.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-26 17:52         ` Stefan Monnier
@ 2018-03-26 18:30           ` Eli Zaretskii
  2018-03-27  0:08           ` Paul Eggert
  1 sibling, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2018-03-26 18:30 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

> From: Stefan Monnier <monnier@iro.umontreal.ca>
> Date: Mon, 26 Mar 2018 13:52:33 -0400
> 
> >> Hmm, would it be worthwhile to have Emacs signal overflow in such a
> >> situation (perhaps controlled by a configuration variable) so we could
> >> fix such issues?
> 
> I think TRT is for the byte-compiler to refrain from performing this
> optimization when the result doesn't fit within 30bits.

I agree.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-26 17:52         ` Stefan Monnier
  2018-03-26 18:30           ` Eli Zaretskii
@ 2018-03-27  0:08           ` Paul Eggert
  1 sibling, 0 replies; 39+ messages in thread
From: Paul Eggert @ 2018-03-27  0:08 UTC (permalink / raw)
  To: Stefan Monnier, emacs-devel

[-- Attachment #1: Type: text/plain, Size: 378 bytes --]

On 03/26/2018 10:52 AM, Stefan Monnier wrote:
> I think TRT is for the byte-compiler to refrain from performing this
> optimization when the result doesn't fit within 30bits.

Yes, this seems like the right way to go for Emacs, since we value 
reproducibility and safety over performance for numeric computation. I 
installed the attached patch into master, to try to do that.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Fix-constant-folding-of-overflows.patch --]
[-- Type: text/x-patch; name="0001-Fix-constant-folding-of-overflows.patch", Size: 10156 bytes --]

From e3b742dcc3d2f1946969d87839e3b72b0f52c513 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Mon, 26 Mar 2018 17:03:54 -0700
Subject: [PATCH] Fix constant folding of overflows

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.
---
 lisp/emacs-lisp/byte-opt.el | 175 +++++++++++++++++++++++++++++++-------------
 1 file changed, 124 insertions(+), 51 deletions(-)

diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index 004f58cc12..3bc4c438d6 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -678,59 +678,134 @@ byte-optimize-associative-math
 	    (apply (car form) constants))
 	form)))
 
+;; Portable Emacs integers fall in this range.
+(defconst byte-opt--portable-max #x1fffffff)
+(defconst byte-opt--portable-min (- -1 byte-opt--portable-max))
+
+;; True if N is a number that works the same on all Emacs platforms.
+;; Portable Emacs fixnums are exactly representable as floats on all
+;; Emacs platforms, and (except for -0.0) any floating-point number
+;; that equals one of these integers must be the same on all
+;; platforms.  Although other floating-point numbers such as 0.5 are
+;; also portable, it can be tricky to characterize them portably so
+;; they are not optimized.
+(defun byte-opt--portable-numberp (n)
+  (and (numberp n)
+       (<= byte-opt--portable-min n byte-opt--portable-max)
+       (= n (floor n))
+       (not (and (floatp n) (zerop n)
+                 (condition-case () (< (/ n) 0) (error))))))
+
+;; Use OP to reduce any leading prefix of portable numbers in the list
+;; (cons ACCUM ARGS) down to a single portable 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 (byte-opt--portable-numberp accum)
+    (let (accum1)
+      (while (and (byte-opt--portable-numberp (car args))
+                  (byte-opt--portable-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)
-  (if (memq 0 form) (setq form (delq 0 (copy-sequence form))))
-  ;; For (+ constants...), byte-optimize-predicate does the work.
-  (when (memq nil (mapcar 'numberp (cdr 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 (= (length form) 3)
-	   (or (memq (nth 1 form) '(1 -1))
-	       (memq (nth 2 form) '(1 -1))))
-      (let (integer other)
-	(if (memq (nth 1 form) '(1 -1))
-	    (setq integer (nth 1 form) other (nth 2 form))
-	  (setq integer (nth 2 form) other (nth 1 form)))
-	(setq form
-	      (list (if (eq integer 1) '1+ '1-) other))))))
-  (byte-optimize-predicate form))
+     ((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)))
+     ;; not further optimized
+     ((equal args (cdr form)) form)
+     (t (cons '+ args)))))
 
 (defun byte-optimize-minus (form)
-  ;; Remove zeros.
-  (when (and (nthcdr 3 form)
-	     (memq 0 (cddr form)))
-    (setq form (nconc (list (car form) (cadr form))
-		      (delq 0 (copy-sequence (cddr form)))))
-    ;; After the above, we must turn (- x) back into (- x 0).
-    (or (cddr form)
-	(setq form (nconc form (list 0)))))
-  ;; For (- constants...), byte-optimize-predicate does the work.
-  (when (memq nil (mapcar 'numberp (cdr form)))
-    (cond
-     ;; (- x 1) --> (1- x)
-     ((equal (nthcdr 2 form) '(1))
-      (setq form (list '1- (nth 1 form))))
-     ;; (- x -1) --> (1+ x)
-     ((equal (nthcdr 2 form) '(-1))
-      (setq form (list '1+ (nth 1 form))))))
-  (byte-optimize-predicate 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 portable numbers.
+       ;; This must be done separately since byte-opt--arith-reduce
+       ;; is not applied to (- n).
+       ((and (null (cdr args))
+             (byte-opt--portable-numberp (car args))
+             (byte-opt--portable-numberp (- (car args))))
+        (- (car args)))
+       ;; not further optimized
+       ((equal args (cdr form)) form)
+       (t (cons '- args))))))
+
+(defun byte-optimize-1+ (form)
+  (let ((args (cdr form)))
+    (when (null (cdr args))
+      (let ((n (car args)))
+        (when (and (byte-opt--portable-numberp n)
+                   (byte-opt--portable-numberp (1+ n)))
+          (setq form (1+ n))))))
+  form)
+
+(defun byte-optimize-1- (form)
+  (let ((args (cdr form)))
+    (when (null (cdr args))
+      (let ((n (car args)))
+        (when (and (byte-opt--portable-numberp n)
+                   (byte-opt--portable-numberp (1- n)))
+          (setq form (1- n))))))
+  form)
 
 (defun byte-optimize-multiply (form)
-  (if (memq 1 form) (setq form (delq 1 (copy-sequence form))))
-  ;; For (* integers..), byte-optimize-predicate does the work.
-  (byte-optimize-predicate 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))
+     ;; not further optimized
+     ((equal args (cdr form)) form)
+     (t (cons '* args)))))
 
 (defun byte-optimize-divide (form)
-  ;; Remove 1s.
-  (when (and (nthcdr 3 form)
-	     (memq 1 (cddr form)))
-    (setq form (nconc (list (car form) (cadr form))
-		      (delq 1 (copy-sequence (cddr form)))))
-    ;; After the above, we must turn (/ x) back into (/ x 1).
-    (or (cddr form)
-	(setq form (nconc form (list 1)))))
-  (byte-optimize-predicate 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
@@ -800,8 +875,8 @@ byte-optimize-memq
 (put '>   'byte-optimizer 'byte-optimize-predicate)
 (put '<=  'byte-optimizer 'byte-optimize-predicate)
 (put '>=  'byte-optimizer 'byte-optimize-predicate)
-(put '1+  'byte-optimizer 'byte-optimize-predicate)
-(put '1-  'byte-optimizer 'byte-optimize-predicate)
+(put '1+  'byte-optimizer 'byte-optimize-1+)
+(put '1-  'byte-optimizer 'byte-optimize-1-)
 (put 'not 'byte-optimizer 'byte-optimize-predicate)
 (put 'null  'byte-optimizer 'byte-optimize-predicate)
 (put 'consp 'byte-optimizer 'byte-optimize-predicate)
@@ -854,8 +929,7 @@ byte-optimize-or
   ;; Throw away nil's, and simplify if less than 2 args.
   ;; If there is a literal non-nil constant in the args to `or', throw away all
   ;; following forms.
-  (if (memq nil form)
-      (setq form (delq nil (copy-sequence form))))
+  (setq form (remq nil form))
   (let ((rest form))
     (while (cdr (setq rest (cdr rest)))
       (if (byte-compile-trueconstp (car rest))
@@ -872,9 +946,8 @@ byte-optimize-cond
   (let (rest)
     ;; This must be first, to reduce (cond (t ...) (nil)) to (progn t ...)
     (while (setq rest (assq nil (cdr form)))
-      (setq form (delq rest (copy-sequence form))))
-    (if (memq nil (cdr form))
-	(setq form (delq nil (copy-sequence form))))
+      (setq form (remq rest form)))
+    (setq form (remq nil form))
     (setq rest form)
     (while (setq rest (cdr rest))
       (cond ((byte-compile-trueconstp (car-safe (car rest)))
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-26 18:23             ` Pip Cet
  2018-03-26 18:29               ` Eli Zaretskii
@ 2018-03-27  0:28               ` Paul Eggert
  2018-03-27 23:28                 ` Paul Eggert
  1 sibling, 1 reply; 39+ messages in thread
From: Paul Eggert @ 2018-03-27  0:28 UTC (permalink / raw)
  To: Pip Cet, Eli Zaretskii; +Cc: Robert Pluim, emacs-devel

On 03/26/2018 11:23 AM, Pip Cet wrote:
> if I understand things correctly, large integers in decimal notation 
> are automatically converted to floats, while large hex constants 
> signal an overflow error. I believe the former case applies to the 
> read syntax for byte-compiled functions.

Yes, that's right. This thread got started, though, partly because I'm 
thinking of changing the behavior for plain integers to be more like 
that of other radixes, because the automatic-float conversion causes too 
many problems in practice. This is still work in progress. For a recent 
discussion please see:

https://debbugs.gnu.org/30408

> is this really a problem for any actual code? Good code shouldn't 
> depend on more than the 30 bits Emacs guarantees,

It is a real issue, I'm afraid.(I suppose this means the code isn't 
good....) See these recent fixes in the master branch:

https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=f92c6dd6cd09991c2ab4c0612574064ca061d927
https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=011186279c1041e790b81af72981547d623d9b29
https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=cae9e0fa0358d6323b57a288b5661dc72d36356d

Better treatment of large integers at compile-time will help catch these 
gotchas.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-27  0:28               ` Paul Eggert
@ 2018-03-27 23:28                 ` Paul Eggert
  2018-03-30 16:26                   ` Pip Cet
  0 siblings, 1 reply; 39+ messages in thread
From: Paul Eggert @ 2018-03-27 23:28 UTC (permalink / raw)
  To: Pip Cet; +Cc: Robert Pluim, emacs-devel

On 03/26/2018 05:28 PM, Paul Eggert wrote:
> Better treatment of large integers at compile-time will help catch 
> these gotchas. 

I proposed a patch along these lines here:

https://debbugs.gnu.org/30408#43




^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-27 23:28                 ` Paul Eggert
@ 2018-03-30 16:26                   ` Pip Cet
  2018-03-30 16:31                     ` Noam Postavsky
                                       ` (2 more replies)
  0 siblings, 3 replies; 39+ messages in thread
From: Pip Cet @ 2018-03-30 16:26 UTC (permalink / raw)
  To: Paul Eggert; +Cc: Robert Pluim, emacs-devel

Here's another interesting case of machine-dependent byte compliation:

(defun f (x) (* (+ x 1024.0) (- x 1024.0)))

(byte-compile 'f)

The resulting constvec is [x 1024.0 1024.0] on this machine, but using
NaN-boxed-doubles-as-Lisp-Objects, it's [x 1024.0]. It's easy enough
to see why (identical float values aren't eq on standard Emacs), but
it results in bytecode that's not only machine-dependent, but depends
on details that aren't visible in the form's read syntax:

(setq v 1024.0)
(setq form1 '(lambda (x) (* (+ x 1024.0) (- x 1024.0))))
(setq form2 `(lambda (x) (* (+ x ,v) (- x ,v))))
(message "%S %S" form1 (byte-compile form1))
(message "%S %S" form2 (byte-compile form2))

produces:

(lambda (x) (* (+ x 1024.0) (- x 1024.0))) #[(x) " \301\\ \302Z_\207"
[x 1024.0 1024.0] 3]
(lambda (x) (* (+ x 1024.0) (- x 1024.0))) #[(x) " \301\\ \301Z_\207"
[x 1024.0] 3]

It's easy to fix this by merging the constvec based on eql rather than
eq, but that makes the byte compiler inconsistent: with optimization,
(lambda () (eq 1024.0 1024.0)) will still be false, but without
optimization, it will turn into bytecode that always returns true.

This is biting me because I want bytecode to be exactly the same as on
standard Emacs.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-30 16:26                   ` Pip Cet
@ 2018-03-30 16:31                     ` Noam Postavsky
  2018-03-30 16:39                     ` Paul Eggert
  2018-04-02 12:57                     ` Noam Postavsky
  2 siblings, 0 replies; 39+ messages in thread
From: Noam Postavsky @ 2018-03-30 16:31 UTC (permalink / raw)
  To: Pip Cet; +Cc: Robert Pluim, Paul Eggert, Emacs developers

On 30 March 2018 at 12:26, Pip Cet <pipcet@gmail.com> wrote:

> It's easy to fix this by merging the constvec based on eql rather than
> eq, but that makes the byte compiler inconsistent: with optimization,
> (lambda () (eq 1024.0 1024.0)) will still be false, but without
> optimization, it will turn into bytecode that always returns true.

I don't think there should be an expectation that `eq' on floats is consistent.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-30 16:26                   ` Pip Cet
  2018-03-30 16:31                     ` Noam Postavsky
@ 2018-03-30 16:39                     ` Paul Eggert
  2018-04-02 10:56                       ` Pip Cet
  2018-04-02 12:57                     ` Noam Postavsky
  2 siblings, 1 reply; 39+ messages in thread
From: Paul Eggert @ 2018-03-30 16:39 UTC (permalink / raw)
  To: Pip Cet; +Cc: Robert Pluim, emacs-devel

On 03/30/2018 09:26 AM, Pip Cet wrote:
> with optimization,
> (lambda () (eq 1024.0 1024.0)) will still be false, but without
> optimization, it will turn into bytecode that always returns true

This problem is endemic to Lisp implementations and I wouldn't worry 
about it too much. In Scheme, for example, it is unspecifed whether (eq? 
2.0 2.0) returns true or false, and Emacs Lisp could do the same. We'll 
have similar problems with bignums if we ever get around to implementing 
them.




^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-30 16:39                     ` Paul Eggert
@ 2018-04-02 10:56                       ` Pip Cet
  2018-04-02 11:22                         ` Eli Zaretskii
  2018-04-02 14:50                         ` Stefan Monnier
  0 siblings, 2 replies; 39+ messages in thread
From: Pip Cet @ 2018-04-02 10:56 UTC (permalink / raw)
  To: Paul Eggert; +Cc: Robert Pluim, emacs-devel

I believe the currently-documented behavior is that it's undefined;
however, merely leaving it undefined doesn't make bytecode compilation
reproducible. To do that, right now, I redefine EQ to be true for
same-value floats. I'd prefer the byte compiler to be fixed
(unfortunately, flet-binding eq around the invocation of the byte
compiler isn't an option since byte-compiled code won't let you
redefine eq), but there appear to be several places where the
assumption is built into it, and I haven't found them all. The
half-fixed compiler generates obviously-suboptimal bytecode sequences
like "const X; const X;".

I think it makes sense to say that bytecode should be portable but not
necessarily identical between implementations, provided there's also a
way to canonicalize bytecode generation so the result is actually
target-independent (in the sense that if there are still bytecode
differences between targets, that's a bug in the Lisp code); I hope
redefining EQ is sufficient for that.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-04-02 10:56                       ` Pip Cet
@ 2018-04-02 11:22                         ` Eli Zaretskii
  2018-04-02 11:42                           ` Pip Cet
  2018-04-02 14:50                         ` Stefan Monnier
  1 sibling, 1 reply; 39+ messages in thread
From: Eli Zaretskii @ 2018-04-02 11:22 UTC (permalink / raw)
  To: Pip Cet; +Cc: rpluim, eggert, emacs-devel

> From: Pip Cet <pipcet@gmail.com>
> Date: Mon, 2 Apr 2018 10:56:25 +0000
> Cc: Robert Pluim <rpluim@gmail.com>, emacs-devel@gnu.org
> 
> I believe the currently-documented behavior is that it's undefined;
> however, merely leaving it undefined doesn't make bytecode compilation
> reproducible. To do that, right now, I redefine EQ to be true for
> same-value floats. I'd prefer the byte compiler to be fixed
> (unfortunately, flet-binding eq around the invocation of the byte
> compiler isn't an option since byte-compiled code won't let you
> redefine eq), but there appear to be several places where the
> assumption is built into it, and I haven't found them all. The
> half-fixed compiler generates obviously-suboptimal bytecode sequences
> like "const X; const X;".
> 
> I think it makes sense to say that bytecode should be portable but not
> necessarily identical between implementations, provided there's also a
> way to canonicalize bytecode generation so the result is actually
> target-independent (in the sense that if there are still bytecode
> differences between targets, that's a bug in the Lisp code); I hope
> redefining EQ is sufficient for that.

Can you make a step back and explain what is it that you are trying to
achieve, and why?  Making potentially backward-incompatible changes
without a very good reason is not something we should be enthusiastic
about, IMO.  E.g., the fact that two floats are never EQ is burned
into too many muscle memories, and I expect subtle problems with at
least C code that uses EQ.

Thanks.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-04-02 11:22                         ` Eli Zaretskii
@ 2018-04-02 11:42                           ` Pip Cet
  2018-04-02 12:50                             ` Eli Zaretskii
  0 siblings, 1 reply; 39+ messages in thread
From: Pip Cet @ 2018-04-02 11:42 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: rpluim, eggert, emacs-devel

I'm trying to make bytecode reproducible for my code, which uses
SpiderMonkey's JS::Value type, and in which two same-value floats are
always equal (because their bit representations are), and standard
Emacs on a standard GNU/Linux machine, where, as I've just learned,
two same-value floats are never eq (I'd previously thought that was
undefined, and actually unpredictable in practice).

Finding subtle problems in C code that assumes floats are never EQ
would be one reason why I'm doing this, to understand and hopefully
one day find bugs in the Emacs code base.

Again, this is the first time I hear that "two floats are never EQ" is
actually intentional behavior that some code might rely on, rather
than a mere accident of the current float implementation. Do you
happen to have any examples at hand for code that relies on this?

In the case of byte code, what I've seen so far is that the code
generated is actually suboptimal, and runs fine without duplicated
float constants in the constants vector.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-04-02 11:42                           ` Pip Cet
@ 2018-04-02 12:50                             ` Eli Zaretskii
  0 siblings, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2018-04-02 12:50 UTC (permalink / raw)
  To: Pip Cet; +Cc: rpluim, eggert, emacs-devel

> From: Pip Cet <pipcet@gmail.com>
> Date: Mon, 2 Apr 2018 11:42:59 +0000
> Cc: eggert@cs.ucla.edu, rpluim@gmail.com, emacs-devel@gnu.org
> 
> I'm trying to make bytecode reproducible for my code, which uses
> SpiderMonkey's JS::Value type, and in which two same-value floats are
> always equal (because their bit representations are), and standard
> Emacs on a standard GNU/Linux machine, where, as I've just learned,
> two same-value floats are never eq (I'd previously thought that was
> undefined, and actually unpredictable in practice).

Well, maybe what I said could be misinterpreted: they _can_ be EQ iff
they reference the same Lisp_Float object.  If they reference
different objects, they aren't EQ.  When these floats come from Lisp,
it's extremely unlikely for them to reference the same object, though.

> Again, this is the first time I hear that "two floats are never EQ" is
> actually intentional behavior that some code might rely on, rather
> than a mere accident of the current float implementation. Do you
> happen to have any examples at hand for code that relies on this?

Not off the top of my head, no.  But IME such implementation details
tend to leak into the code, and I would be surprised if we didn't have
something like that in Emacs.

> In the case of byte code, what I've seen so far is that the code
> generated is actually suboptimal, and runs fine without duplicated
> float constants in the constants vector.

Comparing FP values for equivalence on the C level is tricky at best,
due to extended precision, guard bits, etc.  What is your plan for
overcoming these obstacles in a predictable and reliable manner?  Did
you consider solving the problem the other way around, i.e. teaching
JS::Value about this issue?  (Caveat: I know nothing about JS.)




^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-03-30 16:26                   ` Pip Cet
  2018-03-30 16:31                     ` Noam Postavsky
  2018-03-30 16:39                     ` Paul Eggert
@ 2018-04-02 12:57                     ` Noam Postavsky
  2018-04-02 13:30                       ` Eli Zaretskii
       [not found]                       ` <<83y3i568i0.fsf@gnu.org>
  2 siblings, 2 replies; 39+ messages in thread
From: Noam Postavsky @ 2018-04-02 12:57 UTC (permalink / raw)
  To: Pip Cet; +Cc: Robert Pluim, Paul Eggert, Emacs developers

On 30 March 2018 at 12:26, Pip Cet <pipcet@gmail.com> wrote:

> It's easy to fix this by merging the constvec based on eql rather than
> eq, but that makes the byte compiler inconsistent: with optimization,
> (lambda () (eq 1024.0 1024.0)) will still be false, but without
> optimization, it will turn into bytecode that always returns true.

The byte-compiler is already inconsistent in this way for string literals:

(funcall (byte-compile (lambda ()
                         (let ((s "one") (s2 "one"))
                           (eq s s2)))))
;=> t

(funcall (lambda ()
           (let ((s "one") (s2 "one"))
             (eq s s2))))

;=> nil

Although surprisingly:

(funcall (byte-compile (lambda ()
                         (eq "one" "one"))))
;=> nil



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-04-02 12:57                     ` Noam Postavsky
@ 2018-04-02 13:30                       ` Eli Zaretskii
  2018-04-02 14:48                         ` Stefan Monnier
       [not found]                       ` <<83y3i568i0.fsf@gnu.org>
  1 sibling, 1 reply; 39+ messages in thread
From: Eli Zaretskii @ 2018-04-02 13:30 UTC (permalink / raw)
  To: Noam Postavsky; +Cc: rpluim, eggert, pipcet, emacs-devel

> From: Noam Postavsky <npostavs@gmail.com>
> Date: Mon, 2 Apr 2018 08:57:30 -0400
> Cc: Robert Pluim <rpluim@gmail.com>, Paul Eggert <eggert@cs.ucla.edu>,
> 	Emacs developers <emacs-devel@gnu.org>
> 
> On 30 March 2018 at 12:26, Pip Cet <pipcet@gmail.com> wrote:
> 
> > It's easy to fix this by merging the constvec based on eql rather than
> > eq, but that makes the byte compiler inconsistent: with optimization,
> > (lambda () (eq 1024.0 1024.0)) will still be false, but without
> > optimization, it will turn into bytecode that always returns true.
> 
> The byte-compiler is already inconsistent in this way for string literals:

Yes, because strings share the same EQ issue with floats.  And because
we sometimes try to "optimize" that.  For example, in make_pure_string.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* RE: Floating-point constant folding in Emacs byte compiler
       [not found]                       ` <<83y3i568i0.fsf@gnu.org>
@ 2018-04-02 13:37                         ` Drew Adams
  2018-04-02 14:05                           ` Eli Zaretskii
  2018-04-02 14:54                           ` Pip Cet
  0 siblings, 2 replies; 39+ messages in thread
From: Drew Adams @ 2018-04-02 13:37 UTC (permalink / raw)
  To: Eli Zaretskii, Noam Postavsky; +Cc: rpluim, eggert, pipcet, emacs-devel

(Apologies for not following this thread.)

Would it be possible for a user or Lisp code to be
able to specify that it does not want the byte compiler
to perform such floating-point optimization, in order
to provide more platform-independence?

Many uses of Emacs Lisp and C code don't really need
such optimization, I would think.  Couldn't the byte
compiler be made to respect a user (or Lisp-code)
choice here?



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-04-02 13:37                         ` Drew Adams
@ 2018-04-02 14:05                           ` Eli Zaretskii
  2018-04-02 14:54                           ` Pip Cet
  1 sibling, 0 replies; 39+ messages in thread
From: Eli Zaretskii @ 2018-04-02 14:05 UTC (permalink / raw)
  To: Drew Adams; +Cc: rpluim, eggert, npostavs, pipcet, emacs-devel

> Date: Mon, 2 Apr 2018 06:37:40 -0700 (PDT)
> From: Drew Adams <drew.adams@oracle.com>
> Cc: rpluim@gmail.com, eggert@cs.ucla.edu, pipcet@gmail.com, emacs-devel@gnu.org
> 
> Would it be possible for a user or Lisp code to be
> able to specify that it does not want the byte compiler
> to perform such floating-point optimization, in order
> to provide more platform-independence?

We didn't yet decide to make any changes in the byte compiler, so I
see no need to discuss such fire escapes.  The current trend is to
disable optimizations when they harm portability or cause undefined
or unspecified behavior.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-04-02 13:30                       ` Eli Zaretskii
@ 2018-04-02 14:48                         ` Stefan Monnier
  2018-04-02 19:20                           ` Paul Eggert
  0 siblings, 1 reply; 39+ messages in thread
From: Stefan Monnier @ 2018-04-02 14:48 UTC (permalink / raw)
  To: emacs-devel

> Yes, because strings share the same EQ issue with floats.  And because
> we sometimes try to "optimize" that.  For example, in make_pure_string.

It's actually subtly different: with floats, EQ (and functions that
use it) is the *only* way to distinguish two equal floats.
With strings it's not the case because you can modify the object in
place (e.g. by changing its text-properties).

So for strings, the semantics is much less undefined: we're not free to
merge two strings just because they're `equal`, whereas for floats
we are.


        Stefan





^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-04-02 10:56                       ` Pip Cet
  2018-04-02 11:22                         ` Eli Zaretskii
@ 2018-04-02 14:50                         ` Stefan Monnier
  2018-04-02 15:02                           ` Pip Cet
  1 sibling, 1 reply; 39+ messages in thread
From: Stefan Monnier @ 2018-04-02 14:50 UTC (permalink / raw)
  To: emacs-devel

> reproducible.  To do that, right now, I redefine EQ to be true for
> same-value floats.

So you redefine EQ to mean EQL?  That should work.


        Stefan




^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-04-02 13:37                         ` Drew Adams
  2018-04-02 14:05                           ` Eli Zaretskii
@ 2018-04-02 14:54                           ` Pip Cet
  2018-04-02 15:02                             ` Drew Adams
  1 sibling, 1 reply; 39+ messages in thread
From: Pip Cet @ 2018-04-02 14:54 UTC (permalink / raw)
  To: Drew Adams; +Cc: Eli Zaretskii, eggert, emacs-devel, Noam Postavsky, rpluim

On Mon, Apr 2, 2018 at 1:37 PM, Drew Adams <drew.adams@oracle.com> wrote:
> Would it be possible for a user or Lisp code to be
> able to specify that it does not want the byte compiler
> to perform such floating-point optimization, in order
> to provide more platform-independence?

I think it's slightly different: I think it should be possible to
instruct the compiler, in special cases (when building byte code that
is to be run on several different machines/Emacs implementations), to
perform the optimizations, which result in code that is very slightly
more efficient and less platform-dependent (but might break certain
long-standing assumptions about independently constructed floats never
being eq). It's the unoptimized code that makes it easy to rely on
platform-dependent features, and that depends on implementation
details in ways that the optimized code does not.

> Many uses of Emacs Lisp and C code don't really need
> such optimization, I would think.  Couldn't the byte
> compiler be made to respect a user (or Lisp-code)
> choice here?

If I understand Eli correctly, there's no way to enable the new option
by default, because old code might break in subtle ways; that also
makes it questionable whether it's worth it to include the new option
at all.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-04-02 14:50                         ` Stefan Monnier
@ 2018-04-02 15:02                           ` Pip Cet
  0 siblings, 0 replies; 39+ messages in thread
From: Pip Cet @ 2018-04-02 15:02 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

On Mon, Apr 2, 2018 at 2:50 PM, Stefan Monnier <monnier@iro.umontreal.ca> wrote:
>> reproducible.  To do that, right now, I redefine EQ to be true for
>> same-value floats.
>
> So you redefine EQ to mean EQL?  That should work.

It does! I can now produce the same lisp/ directory as standard Emacs
+ EQ=EQL + CANNOT_DUMP=yes (however, even vanilla Emacs will produce
different lisp/ directories with and without CANNOT_DUMP=yes. One
instance is cus-load.el, which includes entries for preloaded files
when CANNOT_DUMP=yes, which is arguably a feature; I'm looking at the
other differences).

I redefined the C EQ macro, not (just) Feq.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* RE: Floating-point constant folding in Emacs byte compiler
  2018-04-02 14:54                           ` Pip Cet
@ 2018-04-02 15:02                             ` Drew Adams
  0 siblings, 0 replies; 39+ messages in thread
From: Drew Adams @ 2018-04-02 15:02 UTC (permalink / raw)
  To: Pip Cet; +Cc: Eli Zaretskii, eggert, emacs-devel, Noam Postavsky, rpluim

> > Would it be possible for a user or Lisp code to be
> > able to specify that it does not want the byte compiler
> > to perform such floating-point optimization, in order
> > to provide more platform-independence?
> 
> I think it's slightly different: I think it should be possible to
> instruct the compiler, in special cases (when building byte code that
> is to be run on several different machines/Emacs implementations), to
> perform the optimizations, which result in code that is very slightly
> more efficient and less platform-dependent (but might break certain
> long-standing assumptions about independently constructed floats never
> being eq). It's the unoptimized code that makes it easy to rely on
> platform-dependent features, and that depends on implementation
> details in ways that the optimized code does not.
> 
> > Many uses of Emacs Lisp and C code don't really need
> > such optimization, I would think.  Couldn't the byte
> > compiler be made to respect a user (or Lisp-code)
> > choice here?
> 
> If I understand Eli correctly, there's no way to enable the new option
> by default, because old code might break in subtle ways; that also
> makes it questionable whether it's worth it to include the new option
> at all.

My concern is to not have platform-independence suffer more,
or at least be able to optionally prevent that as much as
possible.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-04-02 14:48                         ` Stefan Monnier
@ 2018-04-02 19:20                           ` Paul Eggert
  2018-04-02 19:39                             ` Pip Cet
  0 siblings, 1 reply; 39+ messages in thread
From: Paul Eggert @ 2018-04-02 19:20 UTC (permalink / raw)
  To: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 502 bytes --]

On 04/02/2018 07:48 AM, Stefan Monnier wrote:
> we're not free to
> merge two strings just because they're `equal`, whereas for floats
> we are.
Yes, that's the key point here. I see that the Elisp documentation does 
not specify how eq behaves on floats with the same values, so I took a 
crack at specifying this the same way that Common Lisp and Scheme do by 
installing the attached into master. I doubt whether Common Lisp/Scheme 
semantics here will break real Elisp code in any significant way.

[-- Attachment #2: 0001-Clarify-eq-on-floats.patch --]
[-- Type: text/x-patch, Size: 1333 bytes --]

From b89189f686a599817d8cdcc81038b62a38a7baa7 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Mon, 2 Apr 2018 12:19:00 -0700
Subject: [PATCH] Clarify eq on floats

* doc/lispref/objects.texi (Equality Predicates):
Say that two floats with the same values might or might not be eq.
---
 doc/lispref/objects.texi | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/doc/lispref/objects.texi b/doc/lispref/objects.texi
index af740625ad..78a7dccc88 100644
--- a/doc/lispref/objects.texi
+++ b/doc/lispref/objects.texi
@@ -2083,6 +2083,10 @@ Equality Predicates
 necessarily @code{eq} to each other: they are @code{eq} only if they
 are the same object, meaning that a change in the contents of one will
 be reflected by the same change in the contents of the other.
+For other types of objects whose contents cannot be changed (e.g.,
+floats), two arguments with the same contents might or might not be
+the same object, and @code{eq} returns @code{t} or @code{nil}
+depending on whether the Lisp interpreter created one object or two.
 
 @example
 @group
@@ -2095,6 +2099,12 @@ Equality Predicates
      @result{} t
 @end group
 
+@group
+(eq 3.0 3.0)
+     @result{} t @r{or} nil
+;; @r{The result is implementation-dependent.}
+@end group
+
 @group
 (eq "asdf" "asdf")
      @result{} nil
-- 
2.14.3


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-04-02 19:20                           ` Paul Eggert
@ 2018-04-02 19:39                             ` Pip Cet
  2018-04-02 19:58                               ` Eli Zaretskii
  0 siblings, 1 reply; 39+ messages in thread
From: Pip Cet @ 2018-04-02 19:39 UTC (permalink / raw)
  To: Paul Eggert; +Cc: emacs-devel

Actually, there's this bit in the documentation for eql:

Floating-point numbers of equal value are ‘eql’, but they may not be ‘eq’.

I think that matches what I want (which is to be free to merge all
floats of equal value right away), and it matches your change, but not
what Eli suggested was legitimate.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-04-02 19:39                             ` Pip Cet
@ 2018-04-02 19:58                               ` Eli Zaretskii
  2018-04-02 20:55                                 ` Pip Cet
  0 siblings, 1 reply; 39+ messages in thread
From: Eli Zaretskii @ 2018-04-02 19:58 UTC (permalink / raw)
  To: Pip Cet; +Cc: eggert, emacs-devel

> From: Pip Cet <pipcet@gmail.com>
> Date: Mon, 2 Apr 2018 19:39:13 +0000
> Cc: emacs-devel@gnu.org
> 
> Floating-point numbers of equal value are ‘eql’, but they may not be ‘eq’.
> 
> I think that matches what I want (which is to be free to merge all
> floats of equal value right away), and it matches your change, but not
> what Eli suggested was legitimate.

We could be mis-communicating: you said "EQ", not 'eq', so I thought
you wanted to supplant the EQ macro on the C level to return non-zero
for two float objects whose values compare equal.  Now it sounds like
you were talking about something entirely different, something that
only matters on the Lisp level.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: Floating-point constant folding in Emacs byte compiler
  2018-04-02 19:58                               ` Eli Zaretskii
@ 2018-04-02 20:55                                 ` Pip Cet
  0 siblings, 0 replies; 39+ messages in thread
From: Pip Cet @ 2018-04-02 20:55 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: eggert, emacs-devel

> We could be mis-communicating: you said "EQ", not 'eq', so I thought
> you wanted to supplant the EQ macro on the C level to return non-zero
> for two float objects whose values compare equal.  Now it sounds like
> you were talking about something entirely different, something that
> only matters on the Lisp level.

Sorry for the confusion. You're right, I was talking about replacing
EQ, and I was ignoring the difference between eq and EQ. TBH, I'm not
too worried about the C code and EQ, though I would be worried about
making EQ and eq mean different things.



^ permalink raw reply	[flat|nested] 39+ messages in thread

end of thread, other threads:[~2018-04-02 20:55 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-03-22 23:04 Floating-point constant folding in Emacs byte compiler Paul Eggert
2018-03-23  1:26 ` Stefan Monnier
2018-03-23  5:22   ` Paul Eggert
2018-03-23  8:24     ` Eli Zaretskii
2018-03-23 20:00       ` Paul Eggert
2018-03-23  8:15   ` Eli Zaretskii
2018-03-23 20:52 ` Pip Cet
2018-03-24  6:25   ` Eli Zaretskii
2018-03-26  9:39     ` Robert Pluim
2018-03-26 15:13       ` Eli Zaretskii
2018-03-26 15:57         ` Robert Pluim
2018-03-26 16:02           ` Eli Zaretskii
2018-03-26 18:23             ` Pip Cet
2018-03-26 18:29               ` Eli Zaretskii
2018-03-27  0:28               ` Paul Eggert
2018-03-27 23:28                 ` Paul Eggert
2018-03-30 16:26                   ` Pip Cet
2018-03-30 16:31                     ` Noam Postavsky
2018-03-30 16:39                     ` Paul Eggert
2018-04-02 10:56                       ` Pip Cet
2018-04-02 11:22                         ` Eli Zaretskii
2018-04-02 11:42                           ` Pip Cet
2018-04-02 12:50                             ` Eli Zaretskii
2018-04-02 14:50                         ` Stefan Monnier
2018-04-02 15:02                           ` Pip Cet
2018-04-02 12:57                     ` Noam Postavsky
2018-04-02 13:30                       ` Eli Zaretskii
2018-04-02 14:48                         ` Stefan Monnier
2018-04-02 19:20                           ` Paul Eggert
2018-04-02 19:39                             ` Pip Cet
2018-04-02 19:58                               ` Eli Zaretskii
2018-04-02 20:55                                 ` Pip Cet
     [not found]                       ` <<83y3i568i0.fsf@gnu.org>
2018-04-02 13:37                         ` Drew Adams
2018-04-02 14:05                           ` Eli Zaretskii
2018-04-02 14:54                           ` Pip Cet
2018-04-02 15:02                             ` Drew Adams
2018-03-26 17:52         ` Stefan Monnier
2018-03-26 18:30           ` Eli Zaretskii
2018-03-27  0:08           ` Paul Eggert

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).