~ chicken-core (chicken-5) fe500d835565e373891e9b92c52a1ff4db4c7301


commit fe500d835565e373891e9b92c52a1ff4db4c7301
Author:     Peter Bex <peter@more-magic.net>
AuthorDate: Sun Mar 22 15:45:24 2015 +0100
Commit:     Peter Bex <peter@more-magic.net>
CommitDate: Sun May 31 14:55:24 2015 +0200

    Make arithmetic-shift inlineable again, and restore compiler rewrites for it.
    
    This makes it possible to drastically simplify exact->inexact and rat/flo comparisons.

diff --git a/c-platform.scm b/c-platform.scm
index a79e1b54..48091d08 100644
--- a/c-platform.scm
+++ b/c-platform.scm
@@ -750,6 +750,40 @@
 (rewrite 'fxmod 17 2 "C_fixnum_modulo" "C_u_fixnum_modulo")
 (rewrite 'fxrem 17 2 "C_i_fixnum_remainder_checked")
 
+(rewrite
+ 'arithmetic-shift 8
+ (lambda (db classargs cont callargs)
+   ;; (arithmetic-shift <x> <-int>)
+   ;;           -> (##core#inline "C_fixnum_shift_right" <x> -<int>)
+   ;; (arithmetic-shift <x> <+int>)
+   ;;           -> (##core#inline "C_fixnum_shift_left" <x> <int>)
+   ;; _ -> (##core#inline "C_a_i_arithmetic_shift" <x> <y>)
+   ;;
+   ;; not in fixnum-mode:
+   ;; _ -> (##core#inline_allocate ("C_s_a_i_arithmetic_shift" 6) <x> <y>)
+   (and (= 2 (length callargs))
+	(let ((val (second callargs)))
+	  (make-node
+	   '##core#call (list #t)
+	   (list cont
+		 (or (and-let* (((eq? 'quote (node-class val)))
+				((eq? number-type 'fixnum))
+				(n (first (node-parameters val)))
+				((and (fixnum? n) (not (big-fixnum? n)))) )
+		       (if (negative? n)
+			   (make-node
+			    '##core#inline '("C_fixnum_shift_right")
+			    (list (first callargs) (qnode (- n))) )
+			   (make-node
+			    '##core#inline '("C_fixnum_shift_left")
+			    (list (first callargs) val) ) ) )
+		     (if (eq? number-type 'fixnum)
+			 (make-node '##core#inline
+				    '("C_i_fixnum_arithmetic_shift") callargs)
+			 (make-node '##core#inline_allocate
+				    (list "C_s_a_i_arithmetic_shift" 6)
+				    callargs) ) ) ) ) ) ) ) )
+
 (rewrite '##sys#byte 17 2 "C_subbyte")
 (rewrite '##sys#setbyte 17 3 "C_setbyte")
 (rewrite '##sys#peek-fixnum 17 2 "C_peek_fixnum")
diff --git a/chicken.h b/chicken.h
index 4f6c6ab8..788fe305 100644
--- a/chicken.h
+++ b/chicken.h
@@ -1973,7 +1973,6 @@ C_fctexport void C_ccall C_u_integer_remainder(C_word c, C_word self, C_word k,
 C_fctexport void C_ccall C_basic_divrem(C_word c, C_word self, C_word k, C_word x, C_word y) C_noret;
 C_fctexport void C_ccall C_u_integer_divrem(C_word c, C_word self, C_word k, C_word x, C_word y) C_noret;
 C_fctexport void C_ccall C_u_flo_to_int(C_word c, C_word self, C_word k, C_word x) C_noret;
-C_fctexport void C_ccall C_u_integer_shift(C_word c, C_word self, C_word k, C_word x, C_word y) C_noret;
 C_fctexport void C_ccall C_u_2_integer_bitwise_and(C_word c, C_word self, C_word k, C_word x, C_word y) C_noret;
 C_fctexport void C_ccall C_u_2_integer_bitwise_ior(C_word c, C_word self, C_word k, C_word x, C_word y) C_noret;
 C_fctexport void C_ccall C_u_2_integer_bitwise_xor(C_word c, C_word self, C_word k, C_word x, C_word y) C_noret;
@@ -2189,6 +2188,7 @@ C_fctexport C_word C_fcall C_s_a_u_i_integer_minus(C_word **ptr, C_word n, C_wor
 C_fctexport C_word C_fcall C_s_a_i_plus(C_word **ptr, C_word n, C_word x, C_word y) C_regparm;
 C_fctexport C_word C_fcall C_s_a_u_i_integer_plus(C_word **ptr, C_word n, C_word x, C_word y) C_regparm;
 C_fctexport C_word C_fcall C_s_a_u_i_integer_gcd(C_word **ptr, C_word n, C_word x, C_word y) C_regparm;
+C_fctexport C_word C_fcall C_s_a_i_arithmetic_shift(C_word **ptr, C_word n, C_word x, C_word y) C_regparm;
 C_fctexport C_word C_fcall C_s_a_u_i_bignum_extract_digits(C_word **ptr, C_word n, C_word x, C_word start, C_word end) C_regparm;
 
 
diff --git a/library.scm b/library.scm
index 4bdbde1c..352af4ba 100644
--- a/library.scm
+++ b/library.scm
@@ -1152,16 +1152,16 @@ EOF
                (b  (##sys#*-2 x-lo x-lo))
                (ab (- x-hi x-lo))
                (c  (##sys#*-2 ab ab)))
-          (+ (##sys#integer-shift a (fxshl bits 1))
-	     (+ (##sys#integer-shift (+ b (- a c)) bits) b)))
+          (+ (arithmetic-shift a (fxshl bits 1))
+	     (+ (arithmetic-shift (+ b (- a c)) bits) b)))
         (let* ((y (##core#inline_allocate ("C_s_a_u_i_integer_abs" 6) y))
                (y-hi (##sys#bignum-extract-digits y n/2 #f))
                (y-lo (##sys#bignum-extract-digits y 0 n/2))
                (a  (##sys#*-2 x-hi y-hi))
                (b  (##sys#*-2 x-lo y-lo))
                (c  (##sys#*-2 (- x-hi x-lo) (- y-hi y-lo))))
-          (##sys#*-2 rs (+ (##sys#integer-shift a (fxshl bits 1))
-			   (+ (##sys#integer-shift (+ b (- a c)) bits) b)))))))
+          (##sys#*-2 rs (+ (arithmetic-shift a (fxshl bits 1))
+			   (+ (arithmetic-shift (+ b (- a c)) bits) b)))))))
 
 (define (##sys#extended-times x y)
   (define (nonrat*rat x y)
@@ -1280,15 +1280,15 @@ EOF
   ;; up the number more than once.
   (define (burnikel-ziegler-3n/2n a12 a3 b b1 b2 n)
     (receive (q^ r1)
-        (if (< (##sys#integer-shift a12 (fxneg (digit-bits n))) b1)
+        (if (< (arithmetic-shift a12 (fxneg (digit-bits n))) b1)
             (let* ((n/2 (fxshr n 1))
                    (b11 (##sys#bignum-extract-digits b1 n/2 #f))
                    (b12 (##sys#bignum-extract-digits b1 0 n/2)))
               (burnikel-ziegler-2n/1n a12 b1 b11 b12 n))
             (let ((base*n (digit-bits n)))
-              (values (- (##sys#integer-shift 1 base*n) 1) ; B^n-1
-                      (+ (- a12 (##sys#integer-shift b1 base*n)) b1))))
-      (let ((r1a3 (+ (##sys#integer-shift r1 (digit-bits n)) a3)))
+              (values (- (arithmetic-shift 1 base*n) 1) ; B^n-1
+                      (+ (- a12 (arithmetic-shift b1 base*n)) b1))))
+      (let ((r1a3 (+ (arithmetic-shift r1 (digit-bits n)) a3)))
         (let lp ((r^ (- r1a3 (##sys#*-2 q^ b2)))
                  (q^ q^))
           (if (negative? r^)
@@ -1305,7 +1305,7 @@ EOF
                (a4  (##sys#bignum-extract-digits a 0 n/2)))
           (receive (q1 r1) (burnikel-ziegler-3n/2n a12 a3 b b1 b2 n/2)
             (receive (q2 r) (burnikel-ziegler-3n/2n r1 a4 b b1 b2 n/2)
-              (values (+ (##sys#integer-shift q1 (digit-bits n/2)) q2)
+              (values (+ (arithmetic-shift q1 (digit-bits n/2)) q2)
 		      r))))))
 
   ;; The caller will ensure that abs(x) > abs(y)
@@ -1322,29 +1322,29 @@ EOF
          (j (fx/ (fx+ s (fx- m 1)) m))  ; j = s/m, rounded up
          (n (fx* j m))
          (norm-shift (fx- (digit-bits n) (integer-length y)))
-         (x (##sys#integer-shift x norm-shift))
-         (y (##sys#integer-shift y norm-shift))
+         (x (arithmetic-shift x norm-shift))
+         (y (arithmetic-shift y norm-shift))
          ;; l needs to be the smallest value so that a < base^{l*n}/2
          (l (fx/ (fx+ (%bignum-digit-count x) (fx- n 1)) n))
          (l (if (fx= (digit-bits l) (integer-length x)) (fx+ l 1) l))
          (t (fxmax l 2))
          (y-hi (##sys#bignum-extract-digits y (fxshr n 1) #f))
          (y-lo (##sys#bignum-extract-digits y 0 (fxshr n 1))))
-    (let lp ((zi (##sys#integer-shift x (fxneg (digit-bits (fx* (fx- t 2) n)))))
+    (let lp ((zi (arithmetic-shift x (fxneg (digit-bits (fx* (fx- t 2) n)))))
              (i (fx- t 2))
              (quot 0))
       (receive (qi ri) (burnikel-ziegler-2n/1n zi y y-hi y-lo n)
-        (let ((quot (+ (##sys#integer-shift quot (digit-bits n)) qi)))
+        (let ((quot (+ (arithmetic-shift quot (digit-bits n)) qi)))
           (if (fx> i 0)
               (let ((zi-1 (let* ((base*n*i-1 (fx* n (fx- i 1)))
                                  (base*n*i   (fx* n i))
                                  (xi-1 (##sys#bignum-extract-digits
 					x base*n*i-1 base*n*i)))
-                            (+ (##sys#integer-shift ri (digit-bits n)) xi-1))))
+                            (+ (arithmetic-shift ri (digit-bits n)) xi-1))))
                 (lp zi-1 (fx- i 1) quot))
               (let ((rem (if (or (not return-rem?) (eq? 0 norm-shift))
                              ri
-                             (##sys#integer-shift ri (fxneg norm-shift)))))
+                             (arithmetic-shift ri (fxneg norm-shift)))))
                 ;; Return requested values (quot, rem or both) with correct sign:
                 (cond ((and return-quot? return-rem?)
                        (values (if q-neg? (- quot) quot)
@@ -1576,20 +1576,19 @@ EOF
           (((len/4) (fxshr (fx+ (integer-length a) 1) 2))
            ((len/2) (fxshl len/4 1))
            ((s^ r^) (##sys#exact-integer-sqrt
-		     (##sys#integer-shift a (fxneg len/2))))
-           ((mask)  (- (##sys#integer-shift 1 len/4) 1))
+		     (arithmetic-shift a (fxneg len/2))))
+           ((mask)  (- (arithmetic-shift 1 len/4) 1))
            ((a0)    (##sys#integer-bitwise-and a mask))
            ((a1)    (##sys#integer-bitwise-and
-		     (##sys#integer-shift a (fxneg len/4)) mask))
+		     (arithmetic-shift a (fxneg len/4)) mask))
            ((q u)   (##sys#integer-quotient&remainder
 		     (+ (arithmetic-shift r^ len/4) a1)
-		     (##sys#integer-shift s^ 1)))
-           ((s)     (+ (##sys#integer-shift s^ len/4) q))
-           ((r)     (+ (##sys#integer-shift u len/4)
-		       (- a0 (##sys#*-2 q q)))))
+		     (arithmetic-shift s^ 1)))
+           ((s)     (+ (arithmetic-shift s^ len/4) q))
+           ((r)     (+ (arithmetic-shift u len/4) (- a0 (##sys#*-2 q q)))))
         (if (negative? r)
             (values (- s 1)
-		    (- (+ r (##sys#integer-shift s 1)) 1))
+		    (- (+ r (arithmetic-shift s 1)) 1))
             (values s r)))))
 
 (define (exact-integer-sqrt x)
@@ -1661,7 +1660,7 @@ EOF
         (cond
          ((eq? e2 0) res)
          ((even? e2)	     ; recursion is faster than iteration here
-          (##sys#*-2 res (square (lp 1 (##sys#integer-shift e2 -1)))))
+          (##sys#*-2 res (square (lp 1 (arithmetic-shift e2 -1)))))
          (else
           (lp (##sys#*-2 res base) (- e2 1)))))))
 
@@ -1812,7 +1811,7 @@ EOF
 	     (bex (fx- (fx- (integer-length mant) (integer-length scl))
                        flonum-precision)))
         (if (fx< bex 0)
-            (let* ((num (##sys#integer-shift mant (fxneg bex)))
+            (let* ((num (arithmetic-shift mant (fxneg bex)))
                    (quo (round-quotient num scl)))
               (cond ((> (integer-length quo) flonum-precision)
                      ;; Too many bits of quotient; readjust
@@ -4379,7 +4378,6 @@ EOF
 (define ##sys#integer-bitwise-and (##core#primitive "C_u_2_integer_bitwise_and"))
 (define ##sys#integer-bitwise-ior (##core#primitive "C_u_2_integer_bitwise_ior"))
 (define ##sys#integer-bitwise-xor (##core#primitive "C_u_2_integer_bitwise_xor"))
-(define ##sys#integer-shift (##core#primitive "C_u_integer_shift"))
 
 (define (bitwise-and . xs)
   (if (null? xs)
@@ -4428,13 +4426,7 @@ EOF
   (##core#inline_allocate ("C_s_a_u_i_integer_minus" 6) -1 n))
 
 (define (arithmetic-shift n m)
-  (##sys#check-exact-integer n 'arithmetic-shift)
-  ;; Strictly speaking, shifting *right* is okay for any number
-  ;; (ie, shifting by a negative bignum would just result in 0 or -1)...
-  (unless (##core#inline "C_fixnump" m)
-    (##sys#signal-hook #:type-error 'arithmetic-shift
-                       "can only shift by fixnum amounts" n m))
-  (##sys#integer-shift n m))
+  (##core#inline_allocate ("C_s_a_i_arithmetic_shift" 6) n m))
 
 (define (bit-set? n i) (##core#inline "C_i_bit_setp" n i))
 
diff --git a/runtime.c b/runtime.c
index 3068a64d..83be4c05 100644
--- a/runtime.c
+++ b/runtime.c
@@ -515,7 +515,6 @@ static C_regparm int C_fcall C_in_new_heapp(C_word x);
 static void bignum_bitwise_and_2(C_word c, C_word self, C_word result) C_noret;
 static void bignum_bitwise_ior_2(C_word c, C_word self, C_word result) C_noret;
 static void bignum_bitwise_xor_2(C_word c, C_word self, C_word result) C_noret;
-static void bignum_actual_shift(C_word c, C_word self, C_word result) C_noret;
 static void bignum_times_bignum_unsigned(C_word k, C_word x, C_word y, C_word negp) C_noret;
 static void bignum_times_bignum_unsigned_2(C_word c, C_word self, C_word result) C_noret;
 static void integer_times_2(C_word c, C_word self, C_word new_big) C_noret;
@@ -848,7 +847,7 @@ static C_PTABLE_ENTRY *create_initial_ptable()
 {
   /* IMPORTANT: hardcoded table size -
      this must match the number of C_pte calls + 1 (NULL terminator)! */
-  C_PTABLE_ENTRY *pt = (C_PTABLE_ENTRY *)C_malloc(sizeof(C_PTABLE_ENTRY) * 73);
+  C_PTABLE_ENTRY *pt = (C_PTABLE_ENTRY *)C_malloc(sizeof(C_PTABLE_ENTRY) * 72);
   int i = 0;
 
   if(pt == NULL)
@@ -928,7 +927,6 @@ static C_PTABLE_ENTRY *create_initial_ptable()
   C_pte(C_u_2_integer_bitwise_and);
   C_pte(C_u_2_integer_bitwise_ior);
   C_pte(C_u_2_integer_bitwise_xor);
-  C_pte(C_u_integer_shift);
 
   /* IMPORTANT: did you remember the hardcoded pte table size? */
   pt[ i ].id = NULL;
@@ -6233,74 +6231,54 @@ C_regparm C_word C_fcall C_a_i_arithmetic_shift(C_word **a, int c, C_word n1, C_
   }
 }
 
-void C_ccall /* x is any exact integer but y is _always_ a fixnum */
-C_u_integer_shift(C_word c, C_word self, C_word k, C_word x, C_word y)
+C_regparm C_word C_fcall
+C_s_a_i_arithmetic_shift(C_word **ptr, C_word n, C_word x, C_word y)
 {
-  C_word ab[C_SIZEOF_FIX_BIGNUM], *a = ab;
+  C_word ab[C_SIZEOF_FIX_BIGNUM], *a = ab, size, negp, res,
+         digit_offset, bit_offset;
+
+  if (!(y & C_FIXNUM_BIT))
+    barf(C_BAD_ARGUMENT_TYPE_NO_FIXNUM_ERROR, "arithmetic-shift", y);
 
   y = C_unfix(y);
   if (y == 0 || x == C_fix(0)) { /* Done (no shift) */
-    C_kontinue(k, x);
+    return x;
   } else if (x & C_FIXNUM_BIT) {
     if (y < 0) {
       /* Don't shift more than a word's length (that's undefined in C!) */
       if (-y < C_WORD_SIZE) {
-        C_kontinue(k, C_fix(C_unfix(x) >> -y));
+        return C_fix(C_unfix(x) >> -y);
       } else {
-        C_kontinue(k, (x < 0) ? C_fix(-1) : C_fix(0));
+        return (x < 0) ? C_fix(-1) : C_fix(0);
       }
     } else if (y > 0 && y < C_WORD_SIZE-2 &&
                /* After shifting, the length still fits a fixnum */
                (C_ilen(C_unfix(x)) + y) < C_WORD_SIZE-2) {
-      C_kontinue(k, C_fix(C_unfix(x) << y));
+      return C_fix(C_unfix(x) << y);
     } else {
       x = C_a_u_i_fix_to_big(&a, x);
     }
+  } else if (!C_truep(C_i_bignump(x))) {
+    barf(C_BAD_ARGUMENT_TYPE_NO_EXACT_INTEGER_ERROR, "arithmetic-shift", x);
   }
 
-  {
-    C_word ab[C_SIZEOF_CLOSURE(6)], *a = ab,
-           k2, size, negp, digit_offset, bit_offset;
-
-    negp = C_mk_bool(C_bignum_negativep(x));
+  negp = C_mk_bool(C_bignum_negativep(x));
   
-    if (y > 0) { /* y is guaranteed not to be 0 here */
-      digit_offset = y / C_BIGNUM_DIGIT_LENGTH;
-      bit_offset =   y % C_BIGNUM_DIGIT_LENGTH;
+  if (y > 0) {                  /* Shift left */
+    C_uword *startr, *startx, *endx, *endr;
 
-      k2 = C_closure(&a, 6, (C_word)bignum_actual_shift, k,
-                     x, C_SCHEME_TRUE, C_fix(digit_offset), C_fix(bit_offset));
-      size = C_fix(C_bignum_size(x) + digit_offset + 1);
-      C_allocate_bignum(5, (C_word)NULL, k2, size, negp, C_SCHEME_FALSE);
-    } else if (-y >= C_bignum_size(x) * (C_word)C_BIGNUM_DIGIT_LENGTH) {
-      /* All bits are shifted out, just return 0 or -1 */
-      C_kontinue(k, C_truep(negp) ? C_fix(-1) : C_fix(0));
-    } else {
-      digit_offset = -y / C_BIGNUM_DIGIT_LENGTH;
-      bit_offset =   -y % C_BIGNUM_DIGIT_LENGTH;
-    
-      k2 = C_closure(&a, 6, (C_word)bignum_actual_shift, k,
-                     x, C_SCHEME_FALSE, C_fix(digit_offset), C_fix(bit_offset));
+    digit_offset = y / C_BIGNUM_DIGIT_LENGTH;
+    bit_offset =   y % C_BIGNUM_DIGIT_LENGTH;
 
-      size = C_fix(C_bignum_size(x) - digit_offset);
-      C_allocate_bignum(5, (C_word)NULL, k2, size, negp, C_SCHEME_FALSE);
-    }
-  }
-}
+    size = C_fix(C_bignum_size(x) + digit_offset + 1);
+    res = C_allocate_scratch_bignum(ptr, size, negp, C_SCHEME_FALSE);
+
+    startr = C_bignum_digits(res);
+    endr = startr + C_bignum_size(res);
+
+    startx = C_bignum_digits(x);
+    endx = startx + C_bignum_size(x);
 
-static void bignum_actual_shift(C_word c, C_word self, C_word result)
-{
-  C_word k = C_block_item(self, 1),
-         x = C_block_item(self, 2),
-         shift_left = C_truep(C_block_item(self, 3)),
-         digit_offset = C_unfix(C_block_item(self, 4)),
-         bit_offset = C_unfix(C_block_item(self, 5));
-  C_uword *startr = C_bignum_digits(result),
-          *startx = C_bignum_digits(x),
-          *endx = startx + C_bignum_size(x),
-          *endr = startr + C_bignum_size(result);
-
-  if (shift_left) {
     /* Initialize only the lower digits we're skipping and the MSD */
     C_memset(startr, 0, C_wordstobytes(digit_offset));
     *(endr-1) = 0;
@@ -6311,13 +6289,30 @@ static void bignum_actual_shift(C_word c, C_word self, C_word result)
     C_memcpy(startr, startx, C_wordstobytes(endx-startx));
     if(bit_offset > 0)
       bignum_digits_destructive_shift_left(startr, endr, bit_offset);
-  } else {
-    C_word nx, size = C_bignum_size(x) + 1;
+
+    return C_bignum_simplify(res);
+  } else if (-y >= C_bignum_size(x) * (C_word)C_BIGNUM_DIGIT_LENGTH) {
+    /* All bits are shifted out, just return 0 or -1 */
+    return C_truep(negp) ? C_fix(-1) : C_fix(0);
+  } else {                      /* Shift right */
+    C_uword *startr, *startx, *endr;
+    C_word nx;
+
+    digit_offset = -y / C_BIGNUM_DIGIT_LENGTH;
+    bit_offset =   -y % C_BIGNUM_DIGIT_LENGTH;
+
+    size = C_fix(C_bignum_size(x) - digit_offset);
+    res = C_allocate_scratch_bignum(ptr, size, negp, C_SCHEME_FALSE);
+
+    startr = C_bignum_digits(res);
+    endr = startr + C_bignum_size(res);
+
+    size = C_bignum_size(x) + 1;
     if (C_truep(nx = maybe_negate_bignum_for_bitwise_op(x, size))) {
-      startx = C_bignum_digits(nx); /* update startx; x and endx are unused */
+      startx = C_bignum_digits(nx) + digit_offset;
+    } else {
+      startx = C_bignum_digits(x) + digit_offset;
     }
-
-    startx += digit_offset;
     /* Can't use bignum_digits_destructive_copy because that assumes
      * target is at least as big as source.
      */
@@ -6327,10 +6322,10 @@ static void bignum_actual_shift(C_word c, C_word self, C_word result)
 
     if (C_truep(nx)) {
       free_tmp_bignum(nx);
-      bignum_digits_destructive_negate(result);
+      bignum_digits_destructive_negate(res);
     }
+    return C_bignum_simplify(res);
   }
-  C_kontinue(k, C_bignum_simplify(result));
 }
 
 
@@ -8912,9 +8907,9 @@ static C_word flo_int_cmp(C_word flonum, C_word intnum)
 static C_word rat_flo_cmp(C_word ratnum, C_word flonum)
 {
   C_word ab[C_SIZEOF_FIX_BIGNUM * 2 + C_SIZEOF_FLONUM], *a = ab,
-         num, denom, ibig, res, nscaled, iscaled, negp;
+         num, denom, ibig, res, nscaled, iscaled, negp, shift_amount;
   C_uword *scan;
-  int shift_amount, ilen, nlen;
+  int ilen;
   double i, f;
 
   f = C_flonum_magnitude(flonum);
@@ -8938,21 +8933,17 @@ static C_word rat_flo_cmp(C_word ratnum, C_word flonum)
     i = f; /* TODO: split i and f so it'll work for denormalized flonums */
 
     num = C_block_item(ratnum, 1);
-    num = (num & C_FIXNUM_BIT) ? C_a_u_i_fix_to_big(&a, num) : num;
+    negp = C_i_negativep(num);
 
-    if (C_bignum_negativep(num) && i >= 0.0) { /* Save time if signs differ */
+    if (C_truep(negp) && i >= 0.0) { /* Save some time if signs differ */
       return C_fix(-1);
-    } else if (!C_bignum_negativep(num) && i <= 0.0) { /* num is never 0 */
+    } else if (!C_truep(negp) && i <= 0.0) { /* num is never 0 */
       return C_fix(1);
     } else {
-      negp = C_mk_bool(C_bignum_negativep(num));
-
       denom = C_block_item(ratnum, 2);
       denom = (denom & C_FIXNUM_BIT) ? C_a_u_i_fix_to_big(&a, denom) : denom;
 
       ibig = flo_to_tmp_bignum(C_flonum(&a, i));
-
-      nlen = C_bignum_size(num) + C_bignum_size(denom);
       ilen = C_bignum_size(ibig) + C_bignum_size(denom);
 
       /* Now, multiply the scaled flonum by the denominator, so we can
@@ -8965,26 +8956,11 @@ static C_word rat_flo_cmp(C_word ratnum, C_word flonum)
       bignum_digits_multiply(denom, ibig, iscaled); /* Swap args if i < d? */
       free_tmp_bignum(ibig);
 
-      nlen += C_BIGNUM_BITS_TO_DIGITS(shift_amount);
-      nscaled = allocate_tmp_bignum(C_fix(nlen), negp, C_SCHEME_TRUE);
-
-      scan = C_bignum_digits(nscaled) + shift_amount / C_BIGNUM_DIGIT_LENGTH;
-      C_memcpy(scan, C_bignum_digits(num), C_wordstobytes(C_bignum_size(num)));
-      shift_amount = shift_amount % C_BIGNUM_DIGIT_LENGTH;
-      if(shift_amount > 0) {
-        bignum_digits_destructive_shift_left(
-         scan, C_bignum_digits(nscaled) + nlen, shift_amount);
-      }
-
-      /* Shorten the numbers if needed */
-      for (scan = C_bignum_digits(iscaled)+ilen-1; *scan == 0; scan--) ilen--;
-      C_bignum_mutate_size(iscaled, ilen);
-      for (scan = C_bignum_digits(nscaled)+nlen-1; *scan == 0; scan--) nlen--;
-      C_bignum_mutate_size(nscaled, nlen);
+      nscaled = C_s_a_i_arithmetic_shift(&a, 2, num, C_fix(shift_amount));
 
       /* Finally, we're ready to compare them! */
-      res = C_i_bignum_cmp(nscaled, iscaled);
-      free_tmp_bignum(nscaled);
+      res = basic_cmp(nscaled, C_bignum_simplify(iscaled), "rat_flo_cmp", 0);
+      clear_buffer_object(ab, nscaled);
       free_tmp_bignum(iscaled);
 
       return res;
@@ -10084,67 +10060,34 @@ C_a_i_exact_to_inexact(C_word **ptr, int c, C_word n)
               * e_w in M2.  TODO: What if b!=2 (ie, flonum-radix isn't 2)?
               */
              e = integer_length_abs(num) - integer_length_abs(denom),
-             ab[C_SIZEOF_FIX_BIGNUM*4], *a = ab, tmp1 = 0, tmp2 = 0, tmp3 = 0,
-             shift_amount, negp = C_i_integer_negativep(num), q, r, len;
+             ab[C_SIZEOF_FIX_BIGNUM*6], *a = ab, tmp, q, r, len,
+             shift_amount, negp = C_i_integer_negativep(num);
      C_uword *d;
      double res, fraction;
 
-     /* Simplify logic by ensuring bignums */
-     if (num & C_FIXNUM_BIT) num = C_a_u_i_fix_to_big(&a, num);
-     if (denom & C_FIXNUM_BIT) denom = C_a_u_i_fix_to_big(&a, denom);
+     /* Align by shifting the smaller to the size of the larger */
+     if (e < 0)      num = C_s_a_i_arithmetic_shift(&a, 2, num, C_fix(-e));
+     else if (e > 0) denom = C_s_a_i_arithmetic_shift(&a, 2, denom, C_fix(e));
 
-     /* Align numbers by shifting the smaller to the same size of the
-      * larger. After this, "f" in alg. N is represented by num/denom.
-      */
-     if (e < 0) {
-       tmp1 = allocate_tmp_bignum(C_fix(C_bignum_size(denom)),
-                                  C_SCHEME_FALSE, C_SCHEME_TRUE);
-       d = C_bignum_digits(tmp1) - e / C_BIGNUM_DIGIT_LENGTH;
-       C_memcpy(d, C_bignum_digits(num), C_wordstobytes(C_bignum_size(num)));
-       shift_amount = -e % C_BIGNUM_DIGIT_LENGTH;
-       if(shift_amount > 0) {
-         bignum_digits_destructive_shift_left(
-           d, C_bignum_digits(tmp1) + C_bignum_size(tmp1), shift_amount);
-       }
-       num = tmp1;
-     } else if (e > 0) {
-       tmp1 = allocate_tmp_bignum(C_fix(C_bignum_size(num)),
-                                  C_SCHEME_FALSE, C_SCHEME_TRUE);
-       d = C_bignum_digits(tmp1) + e / C_BIGNUM_DIGIT_LENGTH;
-       C_memcpy(d, C_bignum_digits(denom), C_wordstobytes(C_bignum_size(denom)));
-       shift_amount = e % C_BIGNUM_DIGIT_LENGTH;
-       if(shift_amount > 0) {
-         bignum_digits_destructive_shift_left(
-           d, C_bignum_digits(tmp1) + C_bignum_size(tmp1), shift_amount);
-       }
-       denom = tmp1;
-     }
-     /* From here on, 1/2 <= n/d < 2 [N3] */
+     /* Here, 1/2 <= n/d < 2 [N3] */
      if (C_truep(C_i_integer_lessp(num, denom))) { /* n/d < 1? */
-       len = C_bignum_size(num) + 1;
-       tmp2 = allocate_tmp_bignum(C_fix(len), C_SCHEME_FALSE, C_SCHEME_FALSE);
-       bignum_digits_destructive_copy(tmp2, num);
-       d = C_bignum_digits(tmp2);
-       d[len-1] = 0; /* Init most significant digit */
-       bignum_digits_destructive_shift_left(d, d + len, 1);
-       num = tmp2;
-       e -= 1;
+       tmp = C_s_a_i_arithmetic_shift(&a, 2, num, C_fix(1));
+       clear_buffer_object(ab, num); /* "knows" shift creates fresh numbers */
+       num = tmp;
+       e--;
      }
 
      /* Here, 1 <= n/d < 2 (normalized) [N5] */
      shift_amount = nmin(DBL_MANT_DIG-1, e - (DBL_MIN_EXP - DBL_MANT_DIG));
 
-     len = C_bignum_size(num) + shift_amount / C_BIGNUM_DIGIT_LENGTH + 1;
-     tmp3 = allocate_tmp_bignum(C_fix(len), C_SCHEME_FALSE, C_SCHEME_TRUE);
-     d = C_bignum_digits(tmp3) + shift_amount / C_BIGNUM_DIGIT_LENGTH;
-     C_memcpy(d, C_bignum_digits(num), C_wordstobytes(C_bignum_size(num)));
-     shift_amount = shift_amount % C_BIGNUM_DIGIT_LENGTH;
-     if (shift_amount > 0) {
-       bignum_digits_destructive_shift_left(
-         d, C_bignum_digits(tmp3) + len, shift_amount);
-     }
-     num = tmp3;
+     tmp = C_s_a_i_arithmetic_shift(&a, 2, num, C_fix(shift_amount));
+     clear_buffer_object(ab, num); /* "knows" shift creates fresh numbers */
+     num = tmp;
 
+     /* Ensure num and denom are bignums, for simplicity */
+     if (num & C_FIXNUM_BIT) num = C_a_u_i_fix_to_big(&a, num);
+     if (denom & C_FIXNUM_BIT) denom = C_a_u_i_fix_to_big(&a, denom);
+     
      /* Now, calculate round(num/denom).  We start with a quotient&remainder */
      switch(bignum_cmp_unsigned(num, denom)) {
      case 0:                    /* q = 1, r = 0 */
@@ -10195,9 +10138,8 @@ C_a_i_exact_to_inexact(C_word **ptr, int c, C_word n)
 
      free_tmp_bignum(q);
      free_tmp_bignum(r);
-     if (tmp1) free_tmp_bignum(tmp1);
-     if (tmp2) free_tmp_bignum(tmp2);
-     if (tmp3) free_tmp_bignum(tmp3);
+     clear_buffer_object(ab, num);
+     clear_buffer_object(ab, denom);
 
      shift_amount = nmin(DBL_MANT_DIG-1, e - (DBL_MIN_EXP - DBL_MANT_DIG));
      res = ldexp(fraction, e - shift_amount);
diff --git a/types.db b/types.db
index cf53d1e6..840cc12f 100644
--- a/types.db
+++ b/types.db
@@ -862,7 +862,8 @@
 		((*) (##core#inline "C_i_integer_length" #(1))))
 
 (arithmetic-shift (#(procedure #:clean #:enforce #:foldable) arithmetic-shift (integer fixnum) integer)
-		  ((integer fixnum) (##sys#integer-shift #(1) #(2))))
+		  ((* *) (##core#inline_allocate ("C_s_a_i_arithmetic_shift" 6)
+						 #(1) #(2))))
 
 (exact-integer-nth-root (#(procedure #:clean #:enforce #:foldable) exact-integer-nth-root (integer integer) integer integer)
 		    ((integer integer) (##sys#exact-integer-nth-root/loc 'exact-integer-nth-root #(1) #(2))))
Trap