~ chicken-core (chicken-5) ff20cb560683b520a008a46d8455491e1abf7c05
commit ff20cb560683b520a008a46d8455491e1abf7c05 Author: Peter Bex <peter@more-magic.net> AuthorDate: Sat Jan 31 16:01:12 2015 +0100 Commit: Peter Bex <peter@more-magic.net> CommitDate: Sun May 31 14:14:25 2015 +0200 Convert bitwise operators to accept bignums: - bitwise-and - bitwise-xor - bitwise-ior - bitwise-not - arithmetic-shift - bit-set? These now accept only exact integers, so no more flonums! diff --git a/c-platform.scm b/c-platform.scm index 8808160e..204a0edf 100644 --- a/c-platform.scm +++ b/c-platform.scm @@ -526,12 +526,6 @@ (rewrite 'abs 14 'fixnum 1 "C_fixnum_abs" "C_fixnum_abs") -(rewrite 'bitwise-xor 21 0 "C_fixnum_xor" "C_fixnum_xor" "C_a_i_bitwise_xor" words-per-flonum) -(rewrite 'bitwise-and 21 -1 "C_fixnum_and" "C_u_fixnum_and" "C_a_i_bitwise_and" words-per-flonum) -(rewrite 'bitwise-ior 21 0 "C_fixnum_or" "C_u_fixnum_or" "C_a_i_bitwise_ior" words-per-flonum) - -(rewrite 'bitwise-not 22 1 "C_a_i_bitwise_not" #t words-per-flonum "C_fixnum_not") - (rewrite 'fp+ 16 2 "C_a_i_flonum_plus" #f words-per-flonum) (rewrite 'fp- 16 2 "C_a_i_flonum_difference" #f words-per-flonum) (rewrite 'fp* 16 2 "C_a_i_flonum_times" #f words-per-flonum) @@ -726,34 +720,6 @@ (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_a_i_arithmetic_shift" words-per-flonum) <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_a_i_arithmetic_shift" words-per-flonum) - 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 1e7ce304..1b52ef33 100644 --- a/chicken.h +++ b/chicken.h @@ -663,6 +663,7 @@ static inline int isinf_ld (long double x) #define C_BAD_ARGUMENT_TYPE_NO_INEXACT_ERROR 49 #define C_BAD_ARGUMENT_TYPE_NO_REAL_ERROR 50 #define C_BAD_ARGUMENT_TYPE_COMPLEX_NO_ORDERING_ERROR 51 +#define C_BAD_ARGUMENT_TYPE_NO_EXACT_INTEGER_ERROR 52 /* Platform information */ #if defined(C_BIG_ENDIAN) @@ -1810,6 +1811,7 @@ C_fctexport void C_unbound_error(C_word sym) C_noret; C_fctexport void C_no_closure_error(C_word x) C_noret; C_fctexport void C_div_by_zero_error(char *loc) C_noret; C_fctexport void C_not_an_integer_error(char *loc, C_word x) C_noret; +C_fctexport void C_not_an_uinteger_error(char *loc, C_word x) C_noret; C_fctexport C_word C_closure(C_word **ptr, int cells, C_word proc, ...); C_fctexport C_word C_fcall C_pair(C_word **ptr, C_word car, C_word cdr) C_regparm; C_fctexport C_word C_fcall C_number(C_word **ptr, double n) C_regparm; @@ -1904,6 +1906,11 @@ 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; + C_fctexport void C_ccall C_nequalp(C_word c, C_word closure, C_word k, ...) C_noret; C_fctexport void C_ccall C_greaterp(C_word c, C_word closure, C_word k, ...) C_noret; C_fctexport void C_ccall C_lessp(C_word c, C_word closure, C_word k, ...) C_noret; @@ -2036,7 +2043,7 @@ C_fctexport C_word C_fcall C_2_times(C_word **ptr, C_word x, C_word y) C_regparm C_fctexport C_word C_fcall C_2_plus(C_word **ptr, C_word x, C_word y) C_regparm; /* XXX TODO OBSOLETE: This can be removed after recompiling c-platform.scm */ C_fctexport C_word C_fcall C_2_minus(C_word **ptr, C_word x, C_word y) C_regparm; - /* XXX TODO OBSOLETE: This can be removed after recompiling c-platform.scm */ +/* XXX TODO OBSOLETE: This can be removed after recompiling c-platform.scm */ C_fctexport C_word C_fcall C_2_divide(C_word **ptr, C_word x, C_word y) C_regparm; C_fctexport C_word C_fcall C_i_bignum_cmp(C_word x, C_word y) C_regparm; C_fctexport C_word C_fcall C_i_nequalp(C_word x, C_word y) C_regparm; @@ -2056,12 +2063,17 @@ C_fctexport C_word C_fcall C_i_null_pointerp(C_word x) C_regparm; C_fctexport C_word C_fcall C_i_locative_set(C_word loc, C_word x) C_regparm; C_fctexport C_word C_fcall C_i_locative_to_object(C_word loc) C_regparm; C_fctexport C_word C_fcall C_a_i_make_locative(C_word **a, int c, C_word type, C_word object, C_word index, C_word weak) C_regparm; +/* XXX TODO OBSOLETE: This can be removed after recompiling c-platform.scm */ C_fctexport C_word C_fcall C_a_i_bitwise_and(C_word **a, int c, C_word n1, C_word n2) C_regparm; +/* XXX TODO OBSOLETE: This can be removed after recompiling c-platform.scm */ C_fctexport C_word C_fcall C_a_i_bitwise_ior(C_word **a, int c, C_word n1, C_word n2) C_regparm; +/* XXX TODO OBSOLETE: This can be removed after recompiling c-platform.scm */ C_fctexport C_word C_fcall C_a_i_bitwise_not(C_word **a, int c, C_word n1) C_regparm; C_fctexport C_word C_fcall C_i_bit_setp(C_word n, C_word i) C_regparm; C_fctexport C_word C_fcall C_i_integer_length(C_word x) C_regparm; +/* XXX TODO OBSOLETE: This can be removed after recompiling c-platform.scm */ C_fctexport C_word C_fcall C_a_i_bitwise_xor(C_word **a, int c, C_word n1, C_word n2) C_regparm; +/* XXX TODO OBSOLETE: This can be removed after recompiling c-platform.scm */ C_fctexport C_word C_fcall C_a_i_arithmetic_shift(C_word **a, int c, C_word n1, C_word n2) C_regparm; C_fctexport C_word C_fcall C_a_i_exp(C_word **a, int c, C_word n) C_regparm; C_fctexport C_word C_fcall C_a_i_log(C_word **a, int c, C_word n) C_regparm; @@ -2734,6 +2746,17 @@ C_inline C_word C_a_i_fixnum_negate(C_word **ptr, C_word n, C_word x) return C_fix(-C_unfix(x)); } +C_inline C_word C_i_fixnum_bit_setp(C_word n, C_word i) +{ + if (i & C_INT_SIGN_BIT) { + C_not_an_uinteger_error("bit-set?", i); + } else { + i = C_unfix(i); + if (i >= C_WORD_SIZE) return C_mk_bool(n & C_INT_SIGN_BIT); + else return C_mk_bool((C_unfix(n) & ((C_word)1 << i)) != 0); + } +} + C_inline C_word C_a_i_fixnum_difference(C_word **ptr, C_word n, C_word x, C_word y) { C_word z = C_unfix(x) - C_unfix(y); diff --git a/library.scm b/library.scm index 347ec574..bd5a6cf6 100644 --- a/library.scm +++ b/library.scm @@ -315,6 +315,10 @@ EOF (unless (##core#inline "C_i_integerp" x) (##sys#error-bad-integer x (and (pair? loc) (car loc))) ) ) +(define (##sys#check-exact-integer x . loc) + (unless (##core#inline "C_i_exact_integerp" x) + (##sys#error-bad-exact-integer x (and (pair? loc) (car loc))) ) ) + (define (##sys#check-real x . loc) (unless (##core#inline "C_i_realp" x) (##sys#error-bad-real x (and (pair? loc) (car loc))) ) ) @@ -446,6 +450,10 @@ EOF (##sys#error-hook (foreign-value "C_BAD_ARGUMENT_TYPE_NO_INTEGER_ERROR" int) loc arg)) +(define (##sys#error-bad-exact-integer arg #!optional loc) + (##sys#error-hook + (foreign-value "C_BAD_ARGUMENT_TYPE_NO_INTEGER_ERROR" int) loc arg)) + (define (##sys#error-bad-inexact arg #!optional loc) (##sys#error-hook (foreign-value "C_CANT_REPRESENT_INEXACT_ERROR" int) loc arg)) @@ -1122,7 +1130,7 @@ EOF ;; bringing the two numbers to within the same powers of two. ;; See algorithms M & N in Knuth, 4.2.1 (let* ((n1 (%ratnum-numerator x)) - (an ((##core#primitive "C_u_integer_abs") n1)) + (an (##sys#integer-abs n1)) (d1 (%ratnum-denominator x)) ;; Approximate distance between the numbers in powers ;; of 2 ie, 2^e-1 < n/d < 2^e+1 (e is the *un*biased @@ -4314,36 +4322,67 @@ EOF ;; From SRFI-33 (define (integer-length x) (##core#inline "C_i_integer_length" x)) +(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) - (let loop ([x -1] [xs xs]) - (if (null? xs) - x - (loop (##core#inline_allocate ("C_a_i_bitwise_and" 4) x (##sys#slot xs 0)) - (##sys#slot xs 1)) ) ) ) + (if (null? xs) + -1 + (let ((x1 (##sys#slot xs 0))) + (##sys#check-exact-integer x1 'bitwise-and) + (let loop ((x x1) (xs (##sys#slot xs 1))) + (if (null? xs) + x + (let ((xi (##sys#slot xs 0))) + (##sys#check-exact-integer xi 'bitwise-and) + (loop + (##sys#integer-bitwise-and x xi) + (##sys#slot xs 1) ) ) ) ))) ) (define (bitwise-ior . xs) - (let loop ([x 0] [xs xs]) - (if (null? xs) - x - (loop (##core#inline_allocate ("C_a_i_bitwise_ior" 4) x (##sys#slot xs 0)) - (##sys#slot xs 1)) ) ) ) + (if (null? xs) + 0 + (let ((x1 (##sys#slot xs 0))) + (##sys#check-exact-integer x1 'bitwise-ior) + (let loop ((x x1) (xs (##sys#slot xs 1))) + (if (null? xs) + x + (let ((xi (##sys#slot xs 0))) + (##sys#check-exact-integer xi 'bitwise-ior) + (loop + (##sys#integer-bitwise-ior x xi) + (##sys#slot xs 1) ) ) ) ))) ) (define (bitwise-xor . xs) - (let loop ([x 0] [xs xs]) - (if (null? xs) - x - (loop (##core#inline_allocate ("C_a_i_bitwise_xor" 4) x (##sys#slot xs 0)) - (##sys#slot xs 1)) ) ) ) - -(define (bitwise-not x) - (##core#inline_allocate ("C_a_i_bitwise_not" 4) x) ) - -(define (arithmetic-shift x y) - (##core#inline_allocate ("C_a_i_arithmetic_shift" 4) x y) ) - -(define (bit-set? n i) - (##core#inline "C_i_bit_setp" n i) ) - + (if (null? xs) + 0 + (let ((x1 (##sys#slot xs 0))) + (##sys#check-exact-integer x1 'bitwise-xor) + (let loop ((x x1) (xs (##sys#slot xs 1))) + (if (null? xs) + x + (let ((xi (##sys#slot xs 0))) + (##sys#check-exact-integer xi 'bitwise-xor) + (loop + (##sys#integer-bitwise-xor x xi) + (##sys#slot xs 1) ) ) ) ))) ) + +(define (bitwise-not n) + (##sys#check-exact-integer n 'bitwise-not) + (##sys#integer-minus -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)) + +(define (bit-set? n i) (##core#inline "C_i_bit_setp" n i)) ;;; String ports: ; @@ -5103,6 +5142,7 @@ EOF ((49) (apply ##sys#signal-hook #:type-error loc "bad argument type - not an inexact number" args)) ((50) (apply ##sys#signal-hook #:type-error loc "bad argument type - not a real" args)) ((51) (apply ##sys#signal-hook #:type-error loc "bad argument type - complex number has no ordering" args)) + ((52) (apply ##sys#signal-hook #:type-error loc "bad argument type - not an exact integer" args)) (else (apply ##sys#signal-hook #:runtime-error loc "unknown internal error" args)) ) ) ) ) diff --git a/manual/Unit library b/manual/Unit library index 45139149..05e1a588 100644 --- a/manual/Unit library +++ b/manual/Unit library @@ -27,9 +27,7 @@ Adds/subtracts 1 from {{N}}. Binary integer operations. {{arithmetic-shift}} shifts the argument {{N1}} by {{N2}} bits to the left. If {{N2}} is negative, then {{N1}} is shifted to the -right. These operations only accept exact integers or inexact integers in word -range (32 bit signed on 32-bit platforms, or 64 bit signed on 64-bit -platforms). +right. These operations only accept exact integers. ==== bit-set? diff --git a/runtime.c b/runtime.c index 154e26cf..ddad4e91 100644 --- a/runtime.c +++ b/runtime.c @@ -232,25 +232,13 @@ extern void _C_do_apply_hack(void *proc, C_word *args, int count) C_noret; #define ptr_to_fptr(x) ((((x) >> FORWARDING_BIT_SHIFT) & 1) | C_GC_FORWARDING_BIT | ((x) & ~1)) #define fptr_to_ptr(x) (((x) << FORWARDING_BIT_SHIFT) | ((x) & ~(C_GC_FORWARDING_BIT | 1))) -#define C_check_flonum(x, w) if(C_immediatep(x) || C_block_header(x) != C_FLONUM_TAG) \ - barf(C_BAD_ARGUMENT_TYPE_NO_FLONUM_ERROR, w, x); #define C_check_real(x, w, v) if(((x) & C_FIXNUM_BIT) != 0) v = C_unfix(x); \ else if(C_immediatep(x) || C_block_header(x) != C_FLONUM_TAG) \ barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, w, x); \ else v = C_flonum_magnitude(x); -/* these could be shorter in unsafe mode: */ -#define C_check_int(x, f, n, w) if(((x) & C_FIXNUM_BIT) != 0) n = C_unfix(x); \ - else if(C_immediatep(x) || C_block_header(x) != C_FLONUM_TAG) \ - barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, w, x); \ - else { double _m; \ - f = C_flonum_magnitude(x); \ - if(modf(f, &_m) != 0.0 || f < C_WORD_MIN || f > C_WORD_MAX) \ - barf(C_BAD_ARGUMENT_TYPE_NO_INTEGER_ERROR, w, x); \ - else n = (C_word)f; \ - } - +/* XXX TODO OBSOLETE: This can be removed after recompiling c-platform.scm */ #ifdef BITWISE_UINT_ONLY #define C_check_uint(x, f, n, w) if(((x) & C_FIXNUM_BIT) != 0) n = C_unfix(x); \ else if(C_immediatep(x) || C_block_header(x) != C_FLONUM_TAG) \ @@ -273,6 +261,7 @@ extern void _C_do_apply_hack(void *proc, C_word *args, int count) C_noret; } #endif +/* XXX TODO OBSOLETE: This can be removed after recompiling c-platform.scm */ #ifdef C_SIXTY_FOUR #define C_limit_fixnum(n) ((n) & C_MOST_POSITIVE_FIXNUM) #else @@ -513,6 +502,10 @@ static C_ccall void values_continuation(C_word c, C_word closure, C_word dummy, static C_word add_symbol(C_word **ptr, C_word key, C_word string, C_SYMBOL_TABLE *stable); static C_regparm int C_fcall C_in_new_heapp(C_word x); static void bignum_negate_2(C_word c, C_word self, C_word new_big); +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; @@ -557,6 +550,7 @@ static void gc_2(void *dummy) C_noret; static void allocate_vector_2(void *dummy) C_noret; static void allocate_bignum_2(void *dummy) C_noret; static C_word allocate_tmp_bignum(C_word size, C_word negp, C_word initp); +static void bignum_digits_destructive_negate(C_word bignum); static C_uword bignum_digits_destructive_scale_up_with_carry(C_uword *start, C_uword *end, C_uword factor, C_uword carry); static C_uword bignum_digits_destructive_scale_down(C_uword *start, C_uword *end, C_uword denominator); static C_uword bignum_digits_destructive_shift_right(C_uword *start, C_uword *end, int shift_right, int negp); @@ -838,7 +832,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) * 77); int i = 0; if(pt == NULL) @@ -897,7 +891,6 @@ static C_PTABLE_ENTRY *create_initial_ptable() C_pte(C_peek_unsigned_integer); C_pte(C_context_switch); C_pte(C_register_finalizer); - /* IMPORTANT: have you read the comments at the start and the end of this function? */ C_pte(C_locative_ref); C_pte(C_copy_closure); C_pte(C_dump_heap_state); @@ -906,6 +899,7 @@ static C_PTABLE_ENTRY *create_initial_ptable() C_pte(C_fixnum_to_string); C_pte(C_integer_to_string); C_pte(C_flonum_to_string); + /* IMPORTANT: have you read the comments at the start and the end of this function? */ C_pte(C_abs); C_pte(C_u_integer_abs); C_pte(C_negate); @@ -922,6 +916,10 @@ static C_PTABLE_ENTRY *create_initial_ptable() C_pte(C_u_integer_quotient); C_pte(C_u_integer_remainder); C_pte(C_u_integer_divrem); + 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; @@ -1851,6 +1849,11 @@ void barf(int code, char *loc, ...) c = 1; break; + case C_BAD_ARGUMENT_TYPE_NO_EXACT_INTEGER_ERROR: + msg = C_text("bad argument type - not an exact integer"); + c = 1; + break; + default: panic(C_text("illegal internal error code")); } @@ -2498,6 +2501,11 @@ void C_not_an_integer_error(char *loc, C_word x) barf(C_BAD_ARGUMENT_TYPE_NO_INTEGER_ERROR, loc, x); } +void C_not_an_uinteger_error(char *loc, C_word x) +{ + barf(C_BAD_ARGUMENT_TYPE_NO_UINTEGER_ERROR, loc, x); +} + /* Allocate and initialize record: */ C_regparm C_word C_fcall C_string(C_word **ptr, int len, C_char *str) @@ -5547,6 +5555,7 @@ static void bignum_negate_2(C_word c, C_word self, C_word new_big) C_kontinue(C_block_item(self, 1), C_bignum_simplify(new_big)); } +/* XXX TODO OBSOLETE: This can be removed after recompiling c-platform.scm */ C_regparm C_word C_fcall C_a_i_bitwise_and(C_word **a, int c, C_word n1, C_word n2) { double f1, f2; @@ -5561,6 +5570,7 @@ C_regparm C_word C_fcall C_a_i_bitwise_and(C_word **a, int c, C_word n1, C_word } +/* XXX TODO OBSOLETE: This can be removed after recompiling c-platform.scm */ C_regparm C_word C_fcall C_a_i_bitwise_ior(C_word **a, int c, C_word n1, C_word n2) { double f1, f2; @@ -5575,6 +5585,7 @@ C_regparm C_word C_fcall C_a_i_bitwise_ior(C_word **a, int c, C_word n1, C_word } +/* XXX TODO OBSOLETE: This can be removed after recompiling c-platform.scm */ C_regparm C_word C_fcall C_a_i_bitwise_xor(C_word **a, int c, C_word n1, C_word n2) { double f1, f2; @@ -5606,29 +5617,65 @@ C_regparm C_word C_fcall C_i_integer_length(C_word x) } return C_fix(result + last_digit_length); } else { - barf(C_BAD_ARGUMENT_TYPE_NO_EXACT_ERROR, "integer-length", x); + barf(C_BAD_ARGUMENT_TYPE_NO_EXACT_INTEGER_ERROR, "integer-length", x); } } -C_regparm C_word C_fcall C_i_bit_setp(C_word n, C_word i) +/* This returns a tmp bignum negated copy of X (must be freed!) when + * the number is negative, or #f if it doesn't need to be negated. + * The size can be larger or smaller than X (it may be 1-padded). + */ +C_inline C_word maybe_negate_bignum_for_bitwise_op(C_word x, C_word size) { - double f1; - C_uword nn1; - int index; - - if((i & C_FIXNUM_BIT) == 0) - barf(C_BAD_ARGUMENT_TYPE_NO_FIXNUM_ERROR, "bit-set?", i); + C_word nx = C_SCHEME_FALSE, xsize; + if (C_bignum_negativep(x)) { + nx = allocate_tmp_bignum(C_fix(size), C_SCHEME_FALSE, C_SCHEME_FALSE); + xsize = C_bignum_size(x); + /* Copy up until requested size, and init any remaining upper digits */ + C_memcpy(C_bignum_digits(nx), C_bignum_digits(x), + C_wordstobytes(nmin(size, xsize))); + if (size > xsize) + C_memset(C_bignum_digits(nx)+xsize, 0, C_wordstobytes(size-xsize)); + bignum_digits_destructive_negate(nx); + } + return nx; +} - index = C_unfix(i); +C_regparm C_word C_fcall C_i_bit_setp(C_word n, C_word i) +{ + if (!C_truep(C_i_exact_integerp(n))) { + barf(C_BAD_ARGUMENT_TYPE_NO_EXACT_INTEGER_ERROR, "bit-set?", n); + } else if (!(i & C_FIXNUM_BIT)) { + if (!C_immediatep(i) && (C_header_bits(i) == C_BIGNUM_TYPE) && + !C_bignum_negativep(i)) { + return C_i_integer_negativep(n); /* A bit silly, but strictly correct */ + } else { + barf(C_BAD_ARGUMENT_TYPE_NO_UINTEGER_ERROR, "bit-set?", i); + } + } else if (i & C_INT_SIGN_BIT) { + barf(C_BAD_ARGUMENT_TYPE_NO_UINTEGER_ERROR, "bit-set?", i); + } else { + i = C_unfix(i); + if (n & C_FIXNUM_BIT) { + if (i >= C_WORD_SIZE) return C_mk_bool(n & C_INT_SIGN_BIT); + else return C_mk_bool((C_unfix(n) & ((C_word)1 << i)) != 0); + } else { + C_word nn, d; + d = i / C_BIGNUM_DIGIT_LENGTH; + if (d >= C_bignum_size(n)) return C_mk_bool(C_bignum_negativep(n)); - if(index < 0 || index >= C_WORD_SIZE) - barf(C_OUT_OF_RANGE_ERROR, "bit-set?", n, i); + /* TODO: this isn't necessary, is it? */ + if (C_truep(nn = maybe_negate_bignum_for_bitwise_op(n, d))) n = nn; - C_check_uint(n, f1, nn1, "bit-set?"); - return C_mk_bool((nn1 & (1 << index)) != 0); + i %= C_BIGNUM_DIGIT_LENGTH; + d = C_mk_bool((C_bignum_digits(n)[d] & (C_uword)1 << i) != 0); + if (C_truep(nn)) free_tmp_bignum(nn); + return d; + } + } } - +/* XXX TODO OBSOLETE: This can be removed after recompiling c-platform.scm */ C_regparm C_word C_fcall C_a_i_bitwise_not(C_word **a, int c, C_word n) { double f; @@ -5641,7 +5688,163 @@ C_regparm C_word C_fcall C_a_i_bitwise_not(C_word **a, int c, C_word n) else return C_flonum(a, nn); } +void C_ccall +C_u_2_integer_bitwise_and(C_word c, C_word self, C_word k, C_word x, C_word y) +{ + if ((x & y) & C_FIXNUM_BIT) { + C_kontinue(k, C_u_fixnum_and(x, y)); + } else { + C_word kab[C_SIZEOF_FIX_BIGNUM*2], *ka = kab, negp, size, k2; + if (x & C_FIXNUM_BIT) x = C_a_u_i_fix_to_big(&ka, x); + if (y & C_FIXNUM_BIT) y = C_a_u_i_fix_to_big(&ka, y); + + negp = C_mk_bool(C_bignum_negativep(x) && C_bignum_negativep(y)); + /* Allow negative 1-bits to propagate */ + if (C_bignum_negativep(x) || C_bignum_negativep(y)) + size = C_fix(nmax(C_bignum_size(x), C_bignum_size(y)) + 1); + else + size = C_fix(nmin(C_bignum_size(x), C_bignum_size(y))); + + ka = C_alloc(C_SIZEOF_CLOSURE(4)); /* Why faster than static alloc? */ + k2 = C_closure(&ka, 4, (C_word)bignum_bitwise_and_2, k, x, y); + C_allocate_bignum(5, (C_word)NULL, k2, C_fix(size), negp, C_SCHEME_FALSE); + } +} + +static void bignum_bitwise_and_2(C_word c, C_word self, C_word result) +{ + C_word k = C_block_item(self, 1), + x = C_block_item(self, 2), + y = C_block_item(self, 3), + size = C_bignum_size(result), nx, ny; + C_uword *scanr = C_bignum_digits(result), + *endr = scanr + C_bignum_size(result), + *scans1, *ends1, *scans2; + + if (C_truep(nx = maybe_negate_bignum_for_bitwise_op(x, size))) x = nx; + if (C_truep(ny = maybe_negate_bignum_for_bitwise_op(y, size))) y = ny; + + if (C_bignum_size(x) < C_bignum_size(y)) { + scans1 = C_bignum_digits(x); ends1 = scans1 + C_bignum_size(x); + scans2 = C_bignum_digits(y); + } else { + scans1 = C_bignum_digits(y); ends1 = scans1 + C_bignum_size(y); + scans2 = C_bignum_digits(x); + } + + while (scans1 < ends1) *scanr++ = *scans1++ & *scans2++; + C_memset(scanr, 0, C_wordstobytes(endr - scanr)); + + if (C_truep(nx)) free_tmp_bignum(nx); + if (C_truep(ny)) free_tmp_bignum(ny); + if (C_bignum_negativep(result)) bignum_digits_destructive_negate(result); + + C_kontinue(k, C_bignum_simplify(result)); +} + +void C_ccall +C_u_2_integer_bitwise_ior(C_word c, C_word self, C_word k, C_word x, C_word y) +{ + if ((x & y) & C_FIXNUM_BIT) { + C_kontinue(k, C_u_fixnum_or(x, y)); + } else { + C_word kab[C_SIZEOF_FIX_BIGNUM*2], *ka = kab, negp, size, k2; + if (x & C_FIXNUM_BIT) x = C_a_u_i_fix_to_big(&ka, x); + if (y & C_FIXNUM_BIT) y = C_a_u_i_fix_to_big(&ka, y); + + ka = C_alloc(C_SIZEOF_CLOSURE(4)); /* Why faster than static alloc? */ + k2 = C_closure(&ka, 4, (C_word)bignum_bitwise_ior_2, k, x, y); + size = C_fix(nmax(C_bignum_size(x), C_bignum_size(y)) + 1); + negp = C_mk_bool(C_bignum_negativep(x) || C_bignum_negativep(y)); + C_allocate_bignum(5, (C_word)NULL, k2, size, negp, C_SCHEME_FALSE); + } +} + +static void bignum_bitwise_ior_2(C_word c, C_word self, C_word result) +{ + C_word k = C_block_item(self, 1), + x = C_block_item(self, 2), + y = C_block_item(self, 3), + size = C_bignum_size(result), nx, ny; + C_uword *scanr = C_bignum_digits(result), + *endr = scanr + C_bignum_size(result), + *scans1, *ends1, *scans2, *ends2; + + if (C_truep(nx = maybe_negate_bignum_for_bitwise_op(x, size))) x = nx; + if (C_truep(ny = maybe_negate_bignum_for_bitwise_op(y, size))) y = ny; + + if (C_bignum_size(x) < C_bignum_size(y)) { + scans1 = C_bignum_digits(x); ends1 = scans1 + C_bignum_size(x); + scans2 = C_bignum_digits(y); ends2 = scans2 + C_bignum_size(y); + } else { + scans1 = C_bignum_digits(y); ends1 = scans1 + C_bignum_size(y); + scans2 = C_bignum_digits(x); ends2 = scans2 + C_bignum_size(x); + } + + while (scans1 < ends1) *scanr++ = *scans1++ | *scans2++; + while (scans2 < ends2) *scanr++ = *scans2++; + if (scanr < endr) *scanr++ = 0; /* Only done when result is positive */ + assert(scanr == endr); + if (C_truep(nx)) free_tmp_bignum(nx); + if (C_truep(ny)) free_tmp_bignum(ny); + if (C_bignum_negativep(result)) bignum_digits_destructive_negate(result); + + C_kontinue(k, C_bignum_simplify(result)); +} + +void C_ccall +C_u_2_integer_bitwise_xor(C_word c, C_word self, C_word k, C_word x, C_word y) +{ + if ((x & y) & C_FIXNUM_BIT) { + C_kontinue(k, C_fixnum_xor(x, y)); + } else { + C_word kab[C_SIZEOF_FIX_BIGNUM*2], *ka = kab, size, k2, negp; + if (x & C_FIXNUM_BIT) x = C_a_u_i_fix_to_big(&ka, x); + if (y & C_FIXNUM_BIT) y = C_a_u_i_fix_to_big(&ka, y); + + ka = C_alloc(C_SIZEOF_CLOSURE(4)); /* Why faster than static alloc? */ + k2 = C_closure(&ka, 4, (C_word)bignum_bitwise_xor_2, k, x, y); + size = C_fix(nmax(C_bignum_size(x), C_bignum_size(y)) + 1); + negp = C_mk_bool(C_bignum_negativep(x) != C_bignum_negativep(y)); + C_allocate_bignum(5, (C_word)NULL, k2, size, negp, C_SCHEME_FALSE); + } +} + +static void bignum_bitwise_xor_2(C_word c, C_word self, C_word result) +{ + C_word k = C_block_item(self, 1), + x = C_block_item(self, 2), + y = C_block_item(self, 3), + size = C_bignum_size(result), nx, ny; + C_uword *scanr = C_bignum_digits(result), + *endr = scanr + C_bignum_size(result), + *scans1, *ends1, *scans2, *ends2; + + if (C_truep(nx = maybe_negate_bignum_for_bitwise_op(x, size))) x = nx; + if (C_truep(ny = maybe_negate_bignum_for_bitwise_op(y, size))) y = ny; + + if (C_bignum_size(x) < C_bignum_size(y)) { + scans1 = C_bignum_digits(x); ends1 = scans1 + C_bignum_size(x); + scans2 = C_bignum_digits(y); ends2 = scans2 + C_bignum_size(y); + } else { + scans1 = C_bignum_digits(y); ends1 = scans1 + C_bignum_size(y); + scans2 = C_bignum_digits(x); ends2 = scans2 + C_bignum_size(x); + } + + while (scans1 < ends1) *scanr++ = *scans1++ ^ *scans2++; + while (scans2 < ends2) *scanr++ = *scans2++; + if (scanr < endr) *scanr++ = 0; /* Only done when result is positive */ + assert(scanr == endr); + + if (C_truep(nx)) free_tmp_bignum(nx); + if (C_truep(ny)) free_tmp_bignum(ny); + if (C_bignum_negativep(result)) bignum_digits_destructive_negate(result); + + C_kontinue(k, C_bignum_simplify(result)); +} + +/* XXX TODO OBSOLETE: This can be removed after recompiling c-platform.scm */ C_regparm C_word C_fcall C_a_i_arithmetic_shift(C_word **a, int c, C_word n1, C_word n2) { C_word nn; @@ -5700,6 +5903,106 @@ 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_word ab[C_SIZEOF_FIX_BIGNUM], *a = ab; + + y = C_unfix(y); + if (y == 0 || x == C_fix(0)) { /* Done (no shift) */ + C_kontinue(k, 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)); + } else { + C_kontinue(k, (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)); + } else { + x = C_a_u_i_fix_to_big(&a, 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)); + + 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; + + 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)); + + size = C_fix(C_bignum_size(x) - digit_offset); + C_allocate_bignum(5, (C_word)NULL, k2, size, negp, C_SCHEME_FALSE); + } + } +} + +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; + startr += digit_offset; + /* Can't use bignum_digits_destructive_copy because it assumes + * we want to copy from the start. + */ + 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; + 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 += digit_offset; + /* Can't use bignum_digits_destructive_copy because that assumes + * target is at least as big as source. + */ + C_memcpy(startr, startx, C_wordstobytes(endr-startr)); + if(bit_offset > 0) + bignum_digits_destructive_shift_right(startr,endr,bit_offset,C_truep(nx)); + + if (C_truep(nx)) { + free_tmp_bignum(nx); + bignum_digits_destructive_negate(result); + } + } + C_kontinue(k, C_bignum_simplify(result)); +} + /* I */ C_regparm C_word C_fcall C_a_i_exp(C_word **a, int c, C_word n) @@ -8753,6 +9056,24 @@ C_regparm C_word C_fcall C_bignum_simplify(C_word big) } } +static void bignum_digits_destructive_negate(C_word result) +{ + C_uword *scan, *end, digit, sum; + + scan = C_bignum_digits(result); + end = scan + C_bignum_size(result); + + do { + digit = ~*scan; + sum = digit + 1; + *scan++ = sum; + } while (sum == 0 && scan < end); + + for (; scan < end; scan++) { + *scan = ~*scan; + } +} + static C_uword bignum_digits_destructive_scale_up_with_carry(C_uword *start, C_uword *end, C_uword factor, C_uword carry) { diff --git a/types.db b/types.db index ab1575c7..e5819d7e 100644 --- a/types.db +++ b/types.db @@ -842,29 +842,42 @@ (argc+argv (#(procedure #:clean) argc+argv () fixnum (list-of string) fixnum)) (argv (#(procedure #:clean) argv () (list-of string))) -(arithmetic-shift (#(procedure #:clean #:enforce #:foldable) arithmetic-shift (number number) number)) (integer-length (#(procedure #:clean #:enforce #:foldable) integer-length (integer) fixnum) ((fixnum) (##core#inline "C_i_fixnum_length" #(1))) ((*) (##core#inline "C_i_integer_length" #(1)))) -(bignum? (#(procedure #:pure #:predicate bignum) bignum? (*) boolean)) - -(bit-set? (#(procedure #:clean #:enforce #:foldable) bit-set? (number fixnum) boolean) - ((fixnum fixnum) (##core#inline "C_u_i_bit_setp" #(1) #(2)))) - -(bitwise-and (#(procedure #:clean #:enforce #:foldable) bitwise-and (#!rest number) number) - ((fixnum fixnum) (fixnum) - (##core#inline "C_fixnum_and" #(1) #(2)))) +(arithmetic-shift (#(procedure #:clean #:enforce #:foldable) arithmetic-shift (integer fixnum) integer) + ((integer fixnum) (##sys#integer-shift #(1) #(2)))) -(bitwise-ior (#(procedure #:clean #:enforce #:foldable) bitwise-ior (#!rest number) number) - ((fixnum fixnum) (fixnum) - (##core#inline "C_fixnum_or" #(1) #(2)))) - -(bitwise-not (#(procedure #:clean #:enforce #:foldable) bitwise-not (number) number)) +(bignum? (#(procedure #:pure #:predicate bignum) bignum? (*) boolean)) -(bitwise-xor (#(procedure #:clean #:enforce #:foldable) bitwise-xor (#!rest number) number) - ((fixnum fixnum) (fixnum) - (##core#inline "C_fixnum_xor" #(1) #(2)))) +(bit-set? (#(procedure #:clean #:enforce #:foldable) bit-set? (integer integer) boolean) + ((fixnum fixnum) (##core#inline "C_i_fixnum_bit_setp" #(1) #(2))) + ((* *) (##core#inline "C_i_bit_setp" #(1) #(2)))) + +(bitwise-and (#(procedure #:clean #:enforce #:foldable) bitwise-and (#!rest integer) integer) + (() '-1) + ((fixnum) (fixnum) #(1)) + ((integer) #(1)) + ((fixnum fixnum) (fixnum) (##core#inline "C_u_fixnum_and" #(1) #(2))) + ((integer integer) (##sys#integer-bitwise-and #(1) #(2)))) + +(bitwise-ior (#(procedure #:clean #:enforce #:foldable) bitwise-ior (#!rest integer) integer) + (() '0) + ((fixnum) (fixnum) #(1)) + ((integer) #(1)) + ((fixnum fixnum) (fixnum) (##core#inline "C_u_fixnum_or" #(1) #(2))) + ((integer integer) (##sys#integer-bitwise-ior #(1) #(2)))) + +(bitwise-xor (#(procedure #:clean #:enforce #:foldable) bitwise-xor (#!rest integer) integer) + (() '0) + ((fixnum) (fixnum) #(1)) + ((integer) #(1)) + ((fixnum fixnum) (fixnum) (##core#inline "C_fixnum_xor" #(1) #(2))) + ((integer integer) (##sys#integer-bitwise-xor #(1) #(2)))) + +(bitwise-not (#(procedure #:clean #:enforce #:foldable) bitwise-not (integer) integer) + ((integer) (##sys#integer-minus '-1 #(1)))) (blob->string (#(procedure #:clean #:enforce) blob->string (blob) string))Trap