Opened 3 weeks ago
Last modified 2 weeks ago
#1867 new defect
Cannot display literal values over 2^30 but within fixnum 64-bit range when declaring fixnum-arithmetic
| Reported by: | leo-ard | Owned by: | |
|---|---|---|---|
| Priority: | major | Milestone: | someday |
| Component: | unknown | Version: | 5.4.0 |
| Keywords: | Cc: | ||
| Estimated difficulty: |
Description
Hi!
I encountered a problem using the Chicken Scheme Compiler that I have trouble understanding. When I use (declare (fixnum-arithmetic)), literal values that are (expt 2 30) or over but within the fixnum 64-bit range cause the following error:
Error: cannot coerce inexact literal `2166136261' to fixnum
What is stranger is that printing the most-positive-fixnum value from (import (chicken fixnum)) works fine and is a value way larger than 230.
I included the various tests I performed. Uncomment the relevant parts for one particular test, the results I got are included after each test:
;File: test-chicken.scm (declare (fixnum-arithmetic)) ;; always there for all tests ;(import (chicken fixnum)) ;(display most-positive-fixnum) ;;; works fine, prints 4611686018427387903 ;(display (exact? 1073741824)) ;;; works fine, prints #t ;(display 1073741824) ;;; craches with Error: cannot coerce inexact literal `1073741824' to fixnum ;(display 1073741824) ;;; works fine, prints 1073741824
I compiled and ran the tests using the following command: csc test-chicken.scm -o test-chick && ./test-chick
For context, I found this bug working on Ribbit, another scheme implementation because of the inclusion of the literal for a hash function: https://github.com/udem-dlteam/ribbit/blob/7666b6c017a73a0a82e8df7f9de119d2408da404/src/rsc.scm#L1035
Here are infos about my csc version:
> csc -version CHICKEN (c) 2008-2022, The CHICKEN Team (c) 2000-2007, Felix L. Winkelmann Version 5.4.0 (rev 1a1d1495) macosx-unix-clang-arm64 [ 64bit dload ptables ]
Change History (2)
comment:2 by , 2 weeks ago
Hi!
Thanks for taking the time to write these clear explanations! With the goal of generating 32-bit compatible C code, I understand that its tricky to fit greater than 32-bit literals inside fixnums as the code could be compiled for both 32-bit or 64-bit platforms afterwards.
For my use case, I'll remove the fixnum-arithmetic declaration as I value portability over performance (and people might compile Ribbit on a 32-bit platform!).
From the two approaches you presented, I feel like the compile-time option would be the least prone to errors, as a programmer developing their code on a 64-bit platform and testing once in a while on a 32-bit platform might never see (or be aware of) a well-hidden 64-bit literal value.
Thinking about this, I also came up with other possible solutions:
A third option could be to have a declaration telling the compiler that the code will only run on a 64-bit platform, allowing it to optimize with this knowledge, for example: (declare 64-bit-only). This is similar to the compile-time option you proposed, but with an explicit input from the programmer to make sure he is aware that 32-bit portability is lost for more performance: (declare 64-bit-only fixnum-arithmetic). This also opens the door for better optimizations when generating 64-bit code only.
A fourth option would be to have a dual system, where only fixnums would be used for 64-bit platforms and bignums/fixnums for 32-bit platforms (essentially emulating 64-bit values). This choice would be taken at the compilation of the C code with #ifdef C_SIXTY_FOUR, essentially generating a fixnum-only binary for 64-bit platforms and bignum/fixnum binary for 32-bit platforms. I feel like this would be the most difficult option to achieve as it duplicates the logic for all arithmetic operations inside the C code (and might not be worth spending this much time optimizing this quite specific use case). However, for my use case, this would be the best option as no portability is lost and performance is gained for 64-bit platforms.

Hi there,
This is not a bug, but something of a gotcha. Remember that CHICKEN is a Scheme compiler that uses C as its compilation target. This means we can only emit fixnum literals that would fit in a 32-bit machine word, because the emitted C code must be portable to both 64-bit and 32-bit machines.
Now, in normal operation when we see a fixnum integer literal larger than the maximum which would fit into a 32 bit fixnum, we compile this the same way we compile any bignum. It gets converted into an expression which reads the literal and then tries to fit it into a fixnum. But if you specifically request fixnum mode, the compiler refuses to do that, because the result may end up being a bignum.
Now that I'm thinking of this, we could make this more flexible:
One alternative would be to trigger a runtime error when the read-back literal doesn't fit in a fixnum, essentially making the code 64-bit only.
Another would be to emit code that causes a compile-time error, but in the C compiler when compiling for a 32-bit target. Something like this:
And if we don't go for either of those, perhaps we can at least improve the error message (which is a holdover from the early days before the full numeric tower was integrated, when we had only fixnums and flonums)