On Sun, May 21, 2017 at 06:25:09PM +0200, Peter Bex wrote:
> Consider the case where you're compiling on a
> 64-bit machine.  The compiler folds the fixnum operations at
> compile-time, resulting in a fixnum.  However, if the fixnum is not
> representable as a fixnum on the target computer, it will be represented
> as a bignum (or on CHICKEN 4, as a flonum).
> 
> This not only produces a different answer than when the same code would
> be compiled on the 32-bit machine and in the interpreter, but it also
> may trigger other unexpected behaviour resulting from a mismatch of the
> expectation that you're dealing with fixnums only.  For example, a
> specialised unsafe version of a generic procedure might end up being
> specialised for fixnums, but on a 32-bit platform it might still receive
> a bignum (or a flonum on master).
> 
> Hm, I wonder if this stuff causes other problems as well...

Yep, it does.

Try compiling this program to C on a 64-bit machine:

(use (chicken bitwise) (chicken io))
(print (bitwise-and (vector-length (read)) #xfffffffffffffff))

Then compile the resulting C on a 32-bit machine.  I expected bogus
output, but the program actually hung on the tests that I did.
You'll notice that the compiler decided to specialise bitwise-and for
fixnums because both #xfffffffffffffff and the result of vector-length
are fixnums.  This is completely bogus, of course.

Luckily, the fix is quite simple: just don't commit to calling the
literal a fixnum (or bignum!) if you know on another platform it
might not be the same type.  Instead, use 'integer.

This issue also affects master, so I went ahead and added a patch
for that as well.  This is slightly different because we don't have
the integer type, of course.

I also removed some dead code: the (number?) check would never be
reached because the preceding checks are exhaustive.

Cheers,
Peter
From c05ea153dc52ae79fca3ac4bc099dc7c712c9208 Mon Sep 17 00:00:00 2001
From: Peter Bex <pe...@more-magic.net>
Date: Sun, 21 May 2017 18:54:38 +0200
Subject: [PATCH] In the scrutinizer, do not assume big fixnums will fit into
 32 bits

When the scrutinizer applies a specialization for a fixnum, it should
make sure it really is a fixnum.  If compiling on a 64-bit platform, a
fixnum literal might no longer be a fixnum when the program is running
on a 32-bit platform.

Thus, we check whether the literal is a big-fixnum? first.

Similarly, small bignums are rewritten to 'integer for the reverse
situation: when compiling on 32-bit, a bignum might become a fixnum
when running on 64-bit.
---
 NEWS                      |  4 ++++
 lfa2.scm                  | 11 ++++-------
 scrutinizer.scm           | 11 +++++------
 support.scm               |  7 ++++++-
 tests/typematch-tests.scm | 21 +++++++++++++++++++++
 5 files changed, 40 insertions(+), 14 deletions(-)

diff --git a/NEWS b/NEWS
index 492546a..aa626c7 100644
--- a/NEWS
+++ b/NEWS
@@ -105,6 +105,10 @@
 - Build system
   - Fixed broken compilation on NetBSD, due to missing _NETBSD_SOURCE.
 
+- Compiler
+  - The scrutinizer no longer uses 'fixnum as the type for fixnums
+    that might not fit into a fixnum on 32-bit architectures.
+
 
 4.12.0
 
diff --git a/lfa2.scm b/lfa2.scm
index 0fd4612..4c7ff84 100644
--- a/lfa2.scm
+++ b/lfa2.scm
@@ -173,17 +173,14 @@
       ;; a simplified variant of the one in scrutinizer.scm
       (cond ((string? lit) 'string)
 	    ((symbol? lit) 'symbol)
+	    ;; Do not assume fixnum width matches target platform's!
+	    ((or (big-fixnum? lit)
+		 (bignum? lit))
+	     'integer)
 	    ((fixnum? lit) 'fixnum)
-	    ((bignum? lit) 'bignum)
 	    ((flonum? lit) 'float)
 	    ((ratnum? lit) 'ratnum)
 	    ((cplxnum? lit) 'cplxnum)
-	    ((exact-integer? lit) 'integer)
-	    ((number? lit) 
-	     (case number-type 
-	       ((fixnum) 'fixnum)
-	       ((flonum) 'flonum)
-	       (else 'number)))
 	    ((boolean? lit) 'boolean)
 	    ((null? lit) 'null)
 	    ((list? lit) 'list)
diff --git a/scrutinizer.scm b/scrutinizer.scm
index cf7c6ad..2d63f19 100644
--- a/scrutinizer.scm
+++ b/scrutinizer.scm
@@ -196,16 +196,15 @@
     (define (constant-result lit)
       (cond ((string? lit) 'string)
 	    ((symbol? lit) 'symbol)
+	    ;; Do not assume fixnum width matches target platform's!
+	    ((or (big-fixnum? lit)
+		 (small-bignum? lit))
+	     'integer)
 	    ((fixnum? lit) 'fixnum)
-	    ((flonum? lit) 'float)	; Why not "flonum", for consistency?
 	    ((bignum? lit) 'bignum)
+	    ((flonum? lit) 'float)	; Why not "flonum", for consistency?
 	    ((ratnum? lit) 'ratnum)
 	    ((cplxnum? lit) 'cplxnum)
-	    ((number? lit) 
-	     (case number-type 
-	       ((fixnum) 'fixnum)
-	       ((flonum) 'flonum)
-	       (else 'number)))		; in case...
 	    ((boolean? lit)
 	     (if lit 'true 'false))
 	    ((null? lit) 'null)
diff --git a/support.scm b/support.scm
index 0048836..731c484 100644
--- a/support.scm
+++ b/support.scm
@@ -64,7 +64,7 @@
      clear-real-name-table! get-real-name set-real-name!
      real-name real-name2 display-real-name-table
      source-info->string source-info->line call-info constant-form-eval
-     dump-nodes read-info-hook read/source-info big-fixnum?
+     dump-nodes read-info-hook read/source-info big-fixnum? small-bignum?
      hide-variable export-variable variable-hidden? variable-visible?
      mark-variable variable-mark intrinsic? predicate? foldable?
      load-identifier-database
@@ -1596,6 +1596,11 @@
        (or (fx> x 1073741823)
 	   (fx< x -1073741824) ) ) )
 
+(define (small-bignum? x) ;; XXX: This should probably be in c-platform
+  (and (bignum? x)
+       (not (feature? #:64bit))
+       (fx<= (integer-length x) 62) ) )
+
 
 ;;; symbol visibility and other global variable properties
 
diff --git a/tests/typematch-tests.scm b/tests/typematch-tests.scm
index 26d36d8..565ea24 100644
--- a/tests/typematch-tests.scm
+++ b/tests/typematch-tests.scm
@@ -219,6 +219,7 @@
 (mx (forall (a) (procedure (#!rest a) a)) +)
 (mx (list fixnum) '(1))
 
+
 (mx port (open-input-string "foo"))
 (mx input-port (open-input-string "bar"))
 (mx port (open-output-string))
@@ -374,3 +375,23 @@
  (compiler-typecase 1
    (number #t)
    (fixnum #f)))
+
+;; Always a fixnum
+(assert
+ (compiler-typecase #x3fffffff
+   (bignum #f)
+   (fixnum #t)))
+
+;; Is a fixnum on 64-bit, bignum on 32-bit, thus type must be 'integer
+(assert
+ (compiler-typecase #x4fffffff
+   (fixnum #f)
+   (bignum #f)
+   (integer #t)))
+
+;; Always a bignum
+(assert
+ (compiler-typecase #x7fffffffffffffff
+   (fixnum #f)
+   (bignum #t)))
+
-- 
2.1.4

From 6663545560870d1d3d72f8369b472b80b2d427a1 Mon Sep 17 00:00:00 2001
From: Peter Bex <pe...@more-magic.net>
Date: Sun, 21 May 2017 18:54:38 +0200
Subject: [PATCH] In the scrutinizer, do not assume big fixnums will fit into
 32 bits

When the scrutinizer applies a specialization for a fixnum, it should
make sure it really is a fixnum.  If compiling on a 64-bit platform, a
fixnum literal might no longer be a fixnum when the program is running
on a 32-bit platform.

Thus, we check whether the literal is a big-fixnum? first.

Unfortunately, we can't do the converse in CHICKEN 4: when compiling
on a 32-bit platform, a "big fixnum" literal will always be read in as
a flonum.  However, this will result in consistent behaviour when
compiling the resulting C code on 32-bit or 64-bit.
---
 NEWS                      |  4 ++++
 lfa2.scm                  |  7 ++-----
 scrutinizer.scm           |  7 ++-----
 tests/typematch-tests.scm | 20 ++++++++++++++++++++
 4 files changed, 28 insertions(+), 10 deletions(-)

diff --git a/NEWS b/NEWS
index c23bcf1..7e395ac 100644
--- a/NEWS
+++ b/NEWS
@@ -20,6 +20,10 @@
 - Build system
   - Fixed broken compilation on NetBSD, due to missing _NETBSD_SOURCE.
 
+- Compiler
+  - The scrutinizer no longer uses 'fixnum as the type for fixnums
+    that might not fit into a fixnum on 32-bit architectures.
+
 
 4.12.0
 
diff --git a/lfa2.scm b/lfa2.scm
index 2a5fd52..71cf955 100644
--- a/lfa2.scm
+++ b/lfa2.scm
@@ -159,13 +159,10 @@
       ;; a simplified variant of the one in scrutinizer.scm
       (cond ((string? lit) 'string)
 	    ((symbol? lit) 'symbol)
+	    ;; Do not assume fixnum width matches target platform's!
+	    ((big-fixnum? lit) 'number)
 	    ((fixnum? lit) 'fixnum)
 	    ((flonum? lit) 'float)
-	    ((number? lit) 
-	     (case number-type 
-	       ((fixnum) 'fixnum)
-	       ((flonum) 'flonum)
-	       (else 'number)))
 	    ((boolean? lit) 'boolean)
 	    ((null? lit) 'null)
 	    ((list? lit) 'list)
diff --git a/scrutinizer.scm b/scrutinizer.scm
index ad7856f..e72a821 100644
--- a/scrutinizer.scm
+++ b/scrutinizer.scm
@@ -163,13 +163,10 @@
     (define (constant-result lit)
       (cond ((string? lit) 'string)
 	    ((symbol? lit) 'symbol)
+	    ;; Do not assume fixnum width matches target platform's!
+	    ((big-fixnum? lit) 'number)
 	    ((fixnum? lit) 'fixnum)
 	    ((flonum? lit) 'float)
-	    ((number? lit) 
-	     (case number-type 
-	       ((fixnum) 'fixnum)
-	       ((flonum) 'flonum)
-	       (else 'number)))		; in case...
 	    ((boolean? lit)
 	     (if lit 'true 'false))
 	    ((null? lit) 'null)
diff --git a/tests/typematch-tests.scm b/tests/typematch-tests.scm
index 4d841ce..40b8b66 100644
--- a/tests/typematch-tests.scm
+++ b/tests/typematch-tests.scm
@@ -353,3 +353,23 @@
  (compiler-typecase 1
    (number #t)
    (fixnum #f)))
+
+;; Always a fixnum
+(assert
+ (compiler-typecase #x3fffffff
+   (float #f)
+   (fixnum #t)))
+
+;; Is a fixnum on 64-bit, flonum on 32-bit.  The best we can do is to
+;; check for 'number (on 32-bits it will always be a float)
+(assert
+ (compiler-typecase #x4fffffff
+   (fixnum #f)
+   (number #t)))
+
+;; Always a flonum
+(assert
+ (compiler-typecase #x7fffffffffffffff
+   (fixnum #f)
+   (float #t)))
+
-- 
2.1.4

Attachment: signature.asc
Description: Digital signature

_______________________________________________
Chicken-hackers mailing list
Chicken-hackers@nongnu.org
https://lists.nongnu.org/mailman/listinfo/chicken-hackers

Reply via email to