diff --git a/README.md b/README.md index a35a893..f39f647 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,17 @@ Code emitted by the compiler depends upon the following libraries: $ makepkg -si ``` +* [`GMP`](https://gmplib.org/) + + To install this library, you can download and compile the source code from this [`website`](https://gmplib.org/). Following unzipping, below are the instructions to install GMP in a Unix-like environment. + + ```console + $ cd gmp-6.2.1 (or whatever release) + $ ./configure + $ make + $ make check + $ make install + ``` ## Reference -- [standard libraries](stdlibs.md) \ No newline at end of file +- [standard libraries](stdlibs.md) diff --git a/a86/interp.rkt b/a86/interp.rkt index ef84279..f2b08aa 100644 --- a/a86/interp.rkt +++ b/a86/interp.rkt @@ -169,7 +169,7 @@ "" "-z defs ")) (unless (parameterize ((current-error-port err-port)) - (system (format "gcc ~a-v -shared ~a ~a -o ~a -lunistring -lm" + (system (format "gcc ~a-v -shared ~a ~a -o ~a -lunistring -lm -lgmp" -z-defs-maybe t.o objs t.so))) (define err-msg diff --git a/villain/Makefile b/villain/Makefile index d2c0b42..e473bb6 100644 --- a/villain/Makefile +++ b/villain/Makefile @@ -16,6 +16,7 @@ objs = \ io.o \ symbol.o \ str.o \ + bignums.o \ wrap.o \ utf8.o @@ -29,12 +30,13 @@ char.o: villain.h utf8.h char.h io.o: runtime.h villain.h utf8.h symbol.o: str.h types.h villain.h str.o: types.h villain.h +bignums.o: villain.h types.h %.run: %.o @ racket -t formdps.rkt -m make $@ %.run2: %.o runtime.o - gcc runtime.o $(shell cat modulefiles) $< -o $@ $(libs) # -lm + gcc runtime.o $(shell cat modulefiles) $< -o $@ $(libs) -lgmp # -lm @ racket -t formdps.rkt -m mv $@ rm -f formdps $(shell cat modulefiles) modulefiles find . -name "*.s" -not -name libraries-lmdefs.s -delete @@ -74,6 +76,7 @@ clean: -not -name "symbol.o" \ -not -name "str.o" \ -not -name "wrap.o" \ + -not -name "bignums.o" \ -not -name "utf8.o" -delete \ -or -name "*.s" -not -name \ "libraries-lmdefs.s" -delete \ diff --git a/villain/ast.rkt b/villain/ast.rkt index fe54484..763eace 100644 --- a/villain/ast.rkt +++ b/villain/ast.rkt @@ -19,6 +19,7 @@ ;; | (Int Integer) ;; | (Bool Boolean) ;; | (Char Character) +;; | (Bignum Bignum) ;; | (Flonum f) ;; | (String String) ;; | (Vec (Listof Expr)) @@ -76,6 +77,7 @@ (struct Char (c) #:prefab) (struct Flonum (f) #:prefab) (struct String (s) #:prefab) +(struct Bignum (i) #:prefab) (struct Symbol (s) #:prefab) (struct Prim0 (p) #:prefab) (struct Prim1 (p e) #:prefab) diff --git a/villain/bignums.c b/villain/bignums.c new file mode 100644 index 0000000..1b21fd6 --- /dev/null +++ b/villain/bignums.c @@ -0,0 +1,472 @@ +#include +#include +#include +#include +#include "types.h" +#include "villain.h" + +void load_bignum(mpz_t, int64_t*); +void load_any_to_bignum(mpz_t, int64_t); +int64_t integer_comparison(int64_t, int64_t, int); +int64_t return_bignum_maybe_fixnum(mpz_t, int64_t); +int64_t return_fixnum_maybe_bignum(int64_t, int64_t); + +int64_t bound = (int64_t) ((int64_t) 1 << (63 - int_shift)); + +void print_bignum(vl_bignum* h) { + mpz_t integ; + mpz_init(integ); + load_bignum(integ, (int64_t *) h); + mpz_out_str(stdout,10,integ); + mpz_clear(integ); +} + +int64_t bignum_length(int64_t *h) { + mpz_t integ; + mpz_init(integ); + load_bignum(integ, h); // we already xor'ed out bignum tag + size_t sz; // to store the length of bignum (return value) + + // Note: We ignore when sign is near 0 since bignums will not be in that range. + if(mpz_sgn(integ) > 0) { + + sz = mpz_sizeinbase (integ, 2); + mpz_clear(integ); + return (int64_t) ((int64_t) sz << int_shift); + + } else { // if negative, take absolute value, subtract one and take integer length + + mpz_abs(integ, integ); + mpz_sub_ui(integ, integ, (unsigned long int) 1); + + sz = mpz_sizeinbase(integ, 2); + mpz_clear(integ); + return (int64_t) ((int64_t) sz << int_shift); + + } +} + +int64_t add_or_sub1(int64_t val, int64_t heap, int64_t delta) { // rdi, rsi, rdx + // if our input is an integer + if (int_type_tag == (int_type_mask & val)) { + + return return_fixnum_maybe_bignum((val >> int_shift) + delta, heap); + + } else { // input is bignum + int64_t ret; + mpz_t integ; + mpz_init(integ); + load_bignum(integ, (int64_t *) (val ^ bignum_type_tag)); // load value + + // Value is now fully loaded + if(delta < 0) { + mpz_sub_ui(integ, integ, (unsigned long) 1); + } else { + mpz_add_ui(integ, integ, (unsigned long) 1); + } + + ret = return_bignum_maybe_fixnum(integ, heap); + + mpz_clear(integ); + return ret; + } +} + +int64_t integer_g(int64_t val1, int64_t val2) { // rdi, rsi + return integer_comparison(val1, val2, 1); +} + +int64_t integer_geq(int64_t val1, int64_t val2) { // rdi, rsi + return integer_comparison(val1, val2, 2); +} + +int64_t integer_leq(int64_t val1, int64_t val2) { // rdi, rsi + return integer_comparison(val1, val2, 3); +} + +int64_t integer_l(int64_t val1, int64_t val2) { // rdi, rsi + return integer_comparison(val1, val2, 4); +} + +// comparison: 1 for val1 > val2, 2 for val1 >= val2, 3 for val1 <= val2, 4 for val1 < val2 +int64_t integer_comparison(int64_t val1, int64_t val2, int comparison) { + + if (int_type_tag == (int_type_mask & val1) && int_type_tag == (int_type_mask & val2)) { + // both values are fixnum + switch(comparison){ + case 1: + return (val1 > val2) ? val_true : val_false; + break; + case 2: + return (val1 >= val2) ? val_true : val_false; + break; + case 3: + return (val1 <= val2) ? val_true : val_false; + break; + default: + return (val1 < val2) ? val_true : val_false; + break; + } + } else if (bignum_type_tag == (ptr_type_mask & val1) && int_type_tag == (int_type_mask & val2)) { + // val1 bignum, val2 fixnum + + int64_t ret; + mpz_t integ; + mpz_init(integ); + + load_bignum(integ, (int64_t *) (val1 ^ bignum_type_tag)); // load value + val2 = val2 >> int_shift; // adjust integer value + + switch(comparison){ + case 1: + ret = (mpz_cmp_d (integ, (double) val2) > 0) ? val_true : val_false; + break; + case 2: + ret = (mpz_cmp_d (integ, (double) val2) >= 0) ? val_true : val_false; + break; + case 3: + ret = (mpz_cmp_d (integ, (double) val2) <= 0) ? val_true : val_false; + break; + default: + ret = (mpz_cmp_d (integ, (double) val2) < 0) ? val_true : val_false; + break; + } + + mpz_clear(integ); + return ret; + + } else if ( int_type_tag == (int_type_mask & val1) && bignum_type_tag == (ptr_type_mask & val2)) { + // val1 fixnum, val2 bignum + + int64_t ret; + mpz_t integ; + mpz_init(integ); + + load_bignum(integ, (int64_t *) (val2 ^ bignum_type_tag)); // load value + val1 = val1 >> int_shift; // adjust integer value + + // everything is flipped since we have val2 on the left side + switch(comparison){ + case 1: + ret = (mpz_cmp_d (integ, (double) val1) < 0) ? val_true : val_false; + break; + case 2: + ret = (mpz_cmp_d (integ, (double) val1) <= 0) ? val_true : val_false; + break; + case 3: + ret = (mpz_cmp_d (integ, (double) val1) >= 0) ? val_true : val_false; + break; + default: + ret = (mpz_cmp_d (integ, (double) val1) > 0) ? val_true : val_false; + break; + } + + mpz_clear(integ); + return ret; + + } else { + // both are bignum + + int64_t ret; + mpz_t integ1, integ2; + mpz_init(integ1); mpz_init(integ2); + + load_bignum(integ1, (int64_t *) (val1 ^ bignum_type_tag)); // load value + load_bignum(integ2, (int64_t *) (val2 ^ bignum_type_tag)); // load value + + switch(comparison){ + case 1: + ret = (mpz_cmp(integ1, integ2) > 0) ? val_true : val_false; + break; + case 2: + ret = (mpz_cmp(integ1, integ2) >= 0) ? val_true : val_false; + break; + case 3: + ret = (mpz_cmp(integ1, integ2) <= 0) ? val_true : val_false; + break; + default: + ret = (mpz_cmp(integ1, integ2) < 0) ? val_true : val_false; + break; + } + + mpz_clear(integ1); + mpz_clear(integ2); + return ret; + + } +} + +int64_t integer_add(int64_t val1, int64_t val2, int64_t heap) { // rdi, rsi, rdx + if (int_type_tag == (int_type_mask & val1) && int_type_tag == (int_type_mask & val2)) { + // both values are fixnum + + return return_fixnum_maybe_bignum((val1 >> int_shift) + (val2 >> int_shift), heap); + + } else if (bignum_type_tag == (ptr_type_mask & val1) && int_type_tag == (int_type_mask & val2)) { + // val1 bignum, val2 fixnum + + int64_t ret; + mpz_t integ; + mpz_init(integ); + + load_bignum(integ, (int64_t *) (val1 ^ bignum_type_tag)); // load value + val2 = val2 >> int_shift; // adjust integer value + + // add together + if( val2 < 0 ) { + mpz_sub_ui(integ, integ, (unsigned long int) (- val2)); + } else { + mpz_add_ui(integ, integ, (unsigned long int) val2); + } + + ret = return_bignum_maybe_fixnum(integ, heap); + + mpz_clear(integ); + return ret; + + } else if ( int_type_tag == (int_type_mask & val1) && bignum_type_tag == (ptr_type_mask & val2)) { + // val1 fixnum, val2 bignum + + int64_t ret; + mpz_t integ; + mpz_init(integ); + + load_bignum(integ, (int64_t *) (val2 ^ bignum_type_tag)); // load value + val1 = val1 >> int_shift; // adjust integer value + + // add together + if( val1 < 0 ) { + mpz_sub_ui(integ, integ, (unsigned long) (- val1)); + } else { + mpz_add_ui(integ, integ, (unsigned long) val1); + } + + ret = return_bignum_maybe_fixnum(integ, heap); + + mpz_clear(integ); + return ret; + + } else { + // both are bignum + + int64_t ret; + mpz_t integ1, integ2; + mpz_init(integ1); mpz_init(integ2); + + load_bignum(integ1, (int64_t *) (val1 ^ bignum_type_tag)); // load value + load_bignum(integ2, (int64_t *) (val2 ^ bignum_type_tag)); // load value + + mpz_add(integ1, integ1, integ2); + + ret = return_bignum_maybe_fixnum(integ1, heap); + + mpz_clear(integ1); + mpz_clear(integ2); + return ret; + + } +} + +int64_t integer_sub(int64_t val1, int64_t val2, int64_t heap) { // rdi, rsi, rdx + if (int_type_tag == (int_type_mask & val1) && int_type_tag == (int_type_mask & val2)) { + // both values are fixnum + + return return_fixnum_maybe_bignum((val1 >> int_shift) - (val2 >> int_shift), heap); + + } else if (bignum_type_tag == (ptr_type_mask & val1) && int_type_tag == (int_type_mask & val2)) { + // val1 bignum, val2 fixnum + + int64_t ret; + mpz_t integ; + mpz_init(integ); + + load_bignum(integ, (int64_t *) (val1 ^ bignum_type_tag)); // load value + val2 = val2 >> int_shift; // adjust integer value + + // subtract + if( val2 < 0 ) { + mpz_add_ui(integ, integ, (unsigned long int) (- val2)); + } else { + mpz_sub_ui(integ, integ, (unsigned long int) val2); + } + + ret = return_bignum_maybe_fixnum(integ, heap); + + mpz_clear(integ); + return ret; + + } else if ( int_type_tag == (int_type_mask & val1) && bignum_type_tag == (ptr_type_mask & val2)) { + // val1 fixnum, val2 bignum + + int64_t ret; + mpz_t integ; + mpz_init(integ); + + load_bignum(integ, (int64_t *) (val2 ^ bignum_type_tag)); // load value + val1 = val1 >> int_shift; // adjust integer value + + // subtract (we will do (- (- val2 val1))) + if( val1 < 0 ) { + mpz_add_ui(integ, integ, (unsigned long) (- val1)); + } else { + mpz_sub_ui(integ, integ, (unsigned long) val1); + } + + mpz_neg(integ, integ); // need to negate since we subtract in opposite direction + + ret = return_bignum_maybe_fixnum(integ, heap); + + mpz_clear(integ); + return ret; + + } else { + // both are bignum + + int64_t ret; + mpz_t integ1, integ2; + mpz_init(integ1); mpz_init(integ2); + + load_bignum(integ1, (int64_t *) (val1 ^ bignum_type_tag)); // load value + load_bignum(integ2, (int64_t *) (val2 ^ bignum_type_tag)); // load value + + mpz_sub(integ1, integ1, integ2); + + ret = return_bignum_maybe_fixnum(integ1, heap); + + mpz_clear(integ1); + mpz_clear(integ2); + return ret; + + } +} + +int64_t integer_quotient(int64_t val1, int64_t val2, int64_t heap) { // rdi, rsi, rdx + + int64_t ret; + mpz_t integ1, integ2; + mpz_init(integ1); mpz_init(integ2); + + load_any_to_bignum(integ1, val1); // load value + load_any_to_bignum(integ2, val2); // load value + + mpz_tdiv_q(integ1, integ1, integ2); + + ret = return_bignum_maybe_fixnum(integ1, heap); + + mpz_clear(integ1); + mpz_clear(integ2); + return ret; + +} + +int64_t integer_remainder(int64_t val1, int64_t val2, int64_t heap) { // rdi, rsi, rdx + + int64_t ret; + mpz_t integ1, integ2; + mpz_init(integ1); mpz_init(integ2); + + load_any_to_bignum(integ1, val1); // load value + load_any_to_bignum(integ2, val2); // load value + + mpz_tdiv_r(integ1, integ1, integ2); + + ret = return_bignum_maybe_fixnum(integ1, heap); + + mpz_clear(integ1); + mpz_clear(integ2); + return ret; + +} + +void load_any_to_bignum(mpz_t integ, int64_t val) { + if (int_type_tag == (int_type_mask & val)) { + val = val >> int_shift; + + if( val < 0 ) { + int64_t absval = - val; + mpz_import(integ, 1, 0, 8, 0, 0, &absval); + mpz_neg(integ, integ); + } else { + mpz_import(integ, 1, 0, 8, 0, 0, &val); + } + + } else { + int64_t* hp = (int64_t *) (val ^ bignum_type_tag); + int64_t len = (hp[0] >> int_shift); + size_t abslen; + + if(len < 0) { // check if the sign is negative + abslen = (size_t) (- len); // WARNING: potentially lossy conversion + } else { + abslen = (size_t) len; + } + + mpz_import(integ, abslen, -1, (size_t) 8, 0, 0, (void*) (hp+1)); + if(len < 0) { // if sign is negative, negate integer + mpz_neg(integ, integ); + } + } +} + +void load_bignum(mpz_t integ, int64_t* hp) { + int64_t len = (hp[0] >> int_shift); + size_t abslen; + + if(len < 0) { // check if the sign is negative + abslen = (size_t) (- len); // WARNING: potentially lossy conversion + } else { + abslen = (size_t) len; + } + + mpz_import(integ, abslen, -1, (size_t) 8, 0, 0, (void*) (hp+1)); + if(len < 0) { // if sign is negative, negate integer + mpz_neg(integ, integ); + } +} + +int64_t return_bignum_maybe_fixnum(mpz_t integ, int64_t heap) { + + // return value as bignum or fixnum + if( mpz_cmp_d(integ, (double) bound) >= 0 || mpz_cmp_d(integ, (double) (- bound)) < 0) { // if out of range of fixnum, load into heap + int64_t* hp = (int64_t *) heap; + size_t temp = 0; + + mpz_export((void *) (hp + 1), &temp, -1, 8, 0, 0, integ); + + if(mpz_sgn(integ) >= 0) { + hp[0] = (int64_t) (temp << int_shift); + } else { + hp[0] = (int64_t) (- temp << int_shift); + } + + return heap | bignum_type_tag; + } else { // in fixnum range, load into int64_t and return + int64_t ret = 0; + + mpz_export((void *) &ret, NULL, 0, 8, 0, 0, integ); + + if(mpz_sgn(integ) < 0) { + ret = -ret; + } + + return ret << int_shift; + } +} + +int64_t return_fixnum_maybe_bignum(int64_t val, int64_t heap) { + if( val < bound && val >= -bound ) { // if value is in fixnum bounds, return value + return val << int_shift; + } else { // out of bounds, build & export bignum + int64_t* hp = (int64_t *) heap; + + if( val < 0 ) { + hp[0] = -1 << int_shift; + hp[1] = -val; // negate since we use absolute value representation, issue when heap overflows? + } else { + hp[0] = 1 << int_shift; + hp[1] = val; // issue when heap overflows? + } + + return heap | bignum_type_tag; + } +} diff --git a/villain/compile.rkt b/villain/compile.rkt index 34a01c6..873516e 100644 --- a/villain/compile.rkt +++ b/villain/compile.rkt @@ -9,19 +9,19 @@ (define rax 'rax) ; return ; the dividend of div in string-ref and string-set! (define rbx 'rbx) ; heap (define rdx 'rdx) ; return, 2 ; remainder of division and scratch in string-ref - ; and string-set! - + ; and string-set! ; arg3 (define r8 'r8) ; scratch in +, -, compile-chars, compile-prim2, string-ref, ; make-string, compile-prim3, string-ref!, integer-length, match, - ; compile-define, open-input-file + ; compile-define, open-input-file, integer? (define r9 'r9) ; scratch in assert-type, compile-str-chars, string-ref, ; string-set!, make-string, compile-define, fl<= - ; compile-vector, vector-set!, vector-ref + ; compile-vector, vector-set!, vector-ref, compile-bignum + ; add1, sub1, integer?, integer-length, +, -, assert-integer/bignum (define rsp 'rsp) ; stack (define rdi 'rdi) ; arg (define rsi 'rsi) ; arg2 (define r10 'r10) ; scratch in compile-prim3, make-string, string-set!, compile-vector, vector-set! - ; compile-define, fl<= + ; compile-define, fl<=, compile-bignum, integer?, integer-length, assert-integer/bignum (define rcx 'rcx) ; arity indicator (define al 'al) ; low byte of rax ; open-input-file (define xmm0 'xmm0) ; registers to hold double precision floating numbers @@ -50,6 +50,7 @@ (Extern 'raise_error) (Global 'raise_error_align) (Extern 'str_to_symbol) + (bignum-externs) lib-ls-ids-exts ;; externs of library lambda defs (externs p) (apply seq libexts) ;; externs in library lambdas @@ -82,6 +83,7 @@ (Extern 'raise_error) (Extern 'raise_error_align) (Extern 'str_to_symbol) + (bignum-externs) (compile-defines ds))])) (define (libraries-fs-ls) @@ -116,6 +118,7 @@ (Extern 'raise_error) (Extern 'raise_error_align) (Extern 'str_to_symbol) + (bignum-externs) (compile-λ-definitions (apply append (map λs ls))))) (define (λs-labels ls) @@ -138,6 +141,19 @@ (seq (Global (symbol->label x)) (compile-provides xs))])) +;; -> Asm +(define (bignum-externs) + (seq (Extern 'bignum_length) + (Extern 'add_or_sub1) + (Extern 'integer_g) + (Extern 'integer_geq) + (Extern 'integer_leq) + (Extern 'integer_l) + (Extern 'integer_add) + (Extern 'integer_sub) + (Extern 'integer_quotient) + (Extern 'integer_remainder))) + ;; [Listof Id] -> Asm (define (compile-module-provides ls) (match ls @@ -182,6 +198,7 @@ (Extern 'raise_error) (Global 'raise_error_align) (Extern 'str_to_symbol) + (bignum-externs) (Label 'entry) (Mov rbx rdi) ; recv heap pointer (compile-e-tail (Letrec (append fs+ fs) (append ls+ ls) e) '()) @@ -206,6 +223,7 @@ (Extern 'raise_error) (Extern 'raise_error_align) (Extern 'str_to_symbol) + (bignum-externs) (compile-λ-definitions (apply append (map λs dfλs))))])) (define (error-label c) @@ -220,6 +238,7 @@ [(Bool b) (compile-value b)] [(Char c) (compile-value c)] [(Flonum f) (compile-flonum f)] + [(Bignum i) (compile-bignum i c)] [(Eof) (compile-value eof)] [(Empty) (compile-value '())] [(String s) (compile-string s)] @@ -263,6 +282,7 @@ [(Bool b) '()] [(Char c) '()] [(Flonum f) '()] + [(Bignum b) '()] [(Eof) '()] [(Empty) '()] [(String s) '()] @@ -311,6 +331,7 @@ [(Bool b) '()] [(Char c) '()] [(Flonum f) '()] + [(Bignum b) '()] [(Eof) '()] [(Empty) '()] [(String s) '()] @@ -593,6 +614,38 @@ (Add rbx 8)) ) +;; String -> Asm +(define (compile-bignum i c) + (let ((length (ceiling (/ (integer-length (abs i)) 64))) + (sign (if (>= i 0) 1 -1))) + (seq (Mov r9 (imm->bits (* sign length))) + (Mov (Offset rbx 0) r9) ;; write length in word 0 + (compile-bignum-words (bignum->list (abs i)) 1) + (Mov rax rbx) ;; bignum is on heap + (Add rbx (* 8 (add1 length))) + (Mov r10 type-bignum) + (Or rax r10)))) + +;; Integer -> (Listof Integers) +;; Breaks an integer down into 64 bit chunks +;; input should always be positive +(define (bignum->list i) + (if (< i (arithmetic-shift 1 64)) + (list i) + (cons (bitwise-and i (sub1 (arithmetic-shift 1 64))) + (bignum->list (arithmetic-shift i -64))))) + +;; (Listof Integers) Integer -> Asm +;; Takes list of 64-bit integers and places them on the heap +;; Note: least significant 64-bit integers are placed first +(define (compile-bignum-words ws n) + (match ws + ['() (seq)] + [(cons w ws) + (seq (Mov r9 w) + (Mov (Offset rbx (* 8 n)) r9) + (compile-bignum-words ws (add1 n)))])) + ;; String -> Asm (define (compile-string s) (let ((len (string-length s))) @@ -801,14 +854,58 @@ (seq (compile-e-nontail e c) (match p ['add1 - (seq (assert-integer rax c) - (Add rax (imm->bits 1)))] + (let ((end (gensym)) + (pos-bignum (gensym))) + (seq (assert-integer/bignum rax c) + (pad-stack c) + (Mov rdi rax) + (Mov rsi rbx) + (Mov rdx 1) + (Call 'add_or_sub1) + (unpad-stack c) + (Mov r9 rax) ; first check if return value is fixnum + (And r9 mask-int) + (Xor r9 type-int) + (Cmp r9 0) + (Je end) ; if not fixnum, we should adjust rbx + (Mov r9 (Offset rbx 0)) + (Cmp r9 -1) + (Jg pos-bignum) ; get absolute value of length + (Mov r8 0) + (Sub r8 r9) + (Mov r9 r8) + (Label pos-bignum) + (Sar r9 (- int-shift imm-shift)) + (Add rbx r9) + (Label end)))] ['sub1 - (seq (assert-integer rax c) - (Sub rax (imm->bits 1)))] + (let ((end (gensym)) + (pos-bignum (gensym))) + (seq (assert-integer/bignum rax c) + (pad-stack c) + (Mov rdi rax) + (Mov rsi rbx) + (Mov rdx -1) + (Call 'add_or_sub1) + (unpad-stack c) + (Mov r9 rax) ; first check if return value is fixnum + (And r9 mask-int) + (Xor r9 type-int) + (Cmp r9 0) + (Je end) ; if not fixnum, we should adjust rbx + (Mov r9 (Offset rbx 0)) + (Cmp r9 -1) + (Jg pos-bignum) ; get absolute value of length + (Mov r8 0) + (Sub r8 r9) + (Mov r9 r8) + (Label pos-bignum) + (Sar r9 (- int-shift imm-shift)) + (Add rbx r9) + (Label end)))] ['zero? (let ((l1 (gensym))) - (seq (assert-integer rax c) + (seq (assert-integer/bignum rax c) (Cmp rax 0) (Mov rax val-true) (Je l1) @@ -816,21 +913,46 @@ (Label l1)))] ['integer? (let ((l1 (gensym))) - (seq (And rax mask-int) - (Xor rax type-int) - (Cmp rax 0) - (Mov rax val-true) + (seq (Mov r8 val-true) ; preemptively store true as result + (Mov r9 rax) ; first check if it's an integer + (And r9 mask-int) + (Xor r9 type-int) + (Cmp r9 0) (Je l1) - (Mov rax val-false) - (Label l1)))] + (Mov r9 rax) ; if not integer, check if bignum + (Mov r10 ptr-mask) + (And r9 r10) + (Mov r10 type-bignum) + (Xor r9 r10) + (Cmp r9 0) + (Je l1) + (Mov r8 val-false) ; if neither, this line stores false as result + (Label l1) ; move result into rax + (Mov rax r8)))] ['integer-length - (seq (assert-integer rax c) - (Sar rax imm-shift) - (Mov r8 rax) - (Sar r8 63) - (Xor rax r8) - (Bsr rax rax) - (Sal rax int-shift))] + (let ((fixnum-length (gensym)) + (end (gensym))) + (seq (assert-integer/bignum rax c) + (Mov r9 rax) ; first check if it's an integer + (And r9 mask-int) + (Xor r9 type-int) + (Cmp r9 0) + (Je fixnum-length) + (Mov r10 type-bignum) + (Xor rax r10) ; take out tag + (pad-stack c) + (Mov rdi rax) + (Call 'bignum_length) + (unpad-stack c) + (Jmp end) ; jump to end, avoid integer-length for fixnum branch + (Label fixnum-length) + (Sar rax imm-shift) ; if integer, take absolute value and get most significant bit + (Mov r8 rax) + (Sar r8 63) + (Xor rax r8) + (Bsr rax rax) + (Sal rax int-shift) + (Label end)))] ['char? (let ((l1 (gensym))) (seq (And rax mask-char) @@ -1000,78 +1122,153 @@ (compile-e-nontail e2 (cons #f c)) (match p ['+ - (seq (Pop r8) - (assert-integer r8 c) - (assert-integer rax c) - (Add rax r8))] + (let ((end (gensym)) + (pos-bignum (gensym))) + (seq (Pop r8) + (assert-integer/bignum r8 c) + (assert-integer/bignum rax c) + (pad-stack c) + (Mov rdi r8) + (Mov rsi rax) + (Mov rdx rbx) + (Call 'integer_add) + (unpad-stack c) + (Mov r9 rax) ; first check if return value is fixnum + (And r9 mask-int) + (Xor r9 type-int) + (Cmp r9 0) + (Je end) ; if not fixnum, we should adjust rbx + (Mov r9 (Offset rbx 0)) + (Cmp r9 -1) + (Jg pos-bignum) ; get absolute value of length + (Mov r8 0) + (Sub r8 r9) + (Mov r9 r8) + (Label pos-bignum) + (Sar r9 (- int-shift imm-shift)) + (Add rbx r9) + (Label end)))] ['- - (seq (Pop r8) - (assert-integer r8 c) - (assert-integer rax c) - (Sub r8 rax) - (Mov rax r8))] + (let ((end (gensym)) + (pos-bignum (gensym))) + (seq (Pop r8) + (assert-integer/bignum r8 c) + (assert-integer/bignum rax c) + (pad-stack c) + (Mov rdi r8) + (Mov rsi rax) + (Mov rdx rbx) + (Call 'integer_sub) + (unpad-stack c) + (Mov r9 rax) ; first check if return value is fixnum + (And r9 mask-int) + (Xor r9 type-int) + (Cmp r9 0) + (Je end) ; if not fixnum, we should adjust rbx + (Mov r9 (Offset rbx 0)) + (Cmp r9 -1) + (Jg pos-bignum) ; get absolute value of length + (Mov r8 0) + (Sub r8 r9) + (Mov r9 r8) + (Label pos-bignum) + (Sar r9 (- int-shift imm-shift)) + (Add rbx r9) + (Label end)))] ['quotient - (seq (Mov r8 rax) - (Pop rax) - (assert-integer r8 c) - (assert-integer rax c) - (Cmp r8 (imm->bits 0)) + (let ((end (gensym)) + (pos-bignum (gensym))) + (seq (Pop r8) + (assert-integer/bignum r8 c) + (assert-integer/bignum rax c) + (Cmp rax (imm->bits 0)) ; error out if divisor is 0 (Je (error-label c)) - (Cqo) - (IDiv r8) - (Sal rax int-shift) - )] - ['remainder - (seq (Mov r8 rax) - (Pop rax) - (assert-integer r8 c) - (assert-integer rax c) - (Cmp r8 (imm->bits 0)) + (pad-stack c) + (Mov rdi r8) + (Mov rsi rax) + (Mov rdx rbx) + (Call 'integer_quotient) + (unpad-stack c) + (Mov r9 rax) ; first check if return value is fixnum + (And r9 mask-int) + (Xor r9 type-int) + (Cmp r9 0) + (Je end) ; if not fixnum, we should adjust rbx + (Mov r9 (Offset rbx 0)) + (Cmp r9 -1) + (Jg pos-bignum) ; get absolute value of length + (Mov r8 0) + (Sub r8 r9) + (Mov r9 r8) + (Label pos-bignum) + (Sar r9 (- int-shift imm-shift)) + (Add rbx r9) + (Label end)))] + ['remainder + (let ((end (gensym)) + (pos-bignum (gensym))) + (seq (Pop r8) + (assert-integer/bignum r8 c) + (assert-integer/bignum rax c) + (Cmp rax (imm->bits 0)) ; error out if divisor is 0 (Je (error-label c)) - (Cqo) - (IDiv r8) - (Mov rax rdx) - )] - ['> - (let ((gt-true (gensym 'gt))) + (pad-stack c) + (Mov rdi r8) + (Mov rsi rax) + (Mov rdx rbx) + (Call 'integer_remainder) + (unpad-stack c) + (Mov r9 rax) ; first check if return value is fixnum + (And r9 mask-int) + (Xor r9 type-int) + (Cmp r9 0) + (Je end) ; if not fixnum, we should adjust rbx + (Mov r9 (Offset rbx 0)) + (Cmp r9 -1) + (Jg pos-bignum) ; get absolute value of length + (Mov r8 0) + (Sub r8 r9) + (Mov r9 r8) + (Label pos-bignum) + (Sar r9 (- int-shift imm-shift)) + (Add rbx r9) + (Label end)))] + ['> (seq (Pop r8) - (assert-integer r8 c) - (assert-integer rax c) - (Cmp r8 rax) - (Mov rax (imm->bits #t)) - (Jg gt-true) - (Mov rax (imm->bits #f)) - (Label gt-true)))] - ['< - (let ((lt-true (gensym 'lt))) + (assert-integer/bignum r8 c) + (assert-integer/bignum rax c) + (pad-stack c) + (Mov rdi r8) + (Mov rsi rax) + (Call 'integer_g) + (unpad-stack c))] + ['< (seq (Pop r8) - (assert-integer r8 c) - (assert-integer rax c) - (Cmp r8 rax) - (Mov rax (imm->bits #t)) - (Jl lt-true) - (Mov rax (imm->bits #f)) - (Label lt-true)))] + (assert-integer/bignum r8 c) + (assert-integer/bignum rax c) + (pad-stack c) + (Mov rdi r8) + (Mov rsi rax) + (Call 'integer_l) + (unpad-stack c))] ['<= - (let ((leq-true (gensym 'leq))) (seq (Pop r8) - (assert-integer r8 c) - (assert-integer rax c) - (Cmp r8 rax) - (Mov rax (imm->bits #t)) - (Jle leq-true) - (Mov rax (imm->bits #f)) - (Label leq-true)))] + (assert-integer/bignum r8 c) + (assert-integer/bignum rax c) + (pad-stack c) + (Mov rdi r8) + (Mov rsi rax) + (Call 'integer_leq) + (unpad-stack c))] ['>= - (let ((geq-true (gensym 'geq))) (seq (Pop r8) - (assert-integer r8 c) - (assert-integer rax c) - (Cmp r8 rax) - (Mov rax (imm->bits #t)) - (Jge geq-true) - (Mov rax (imm->bits #f)) - (Label geq-true)))] + (assert-integer/bignum r8 c) + (assert-integer/bignum rax c) + (pad-stack c) + (Mov rdi r8) + (Mov rsi rax) + (Call 'integer_geq) + (unpad-stack c))] ['eq? (let ((l (gensym))) (seq (Pop r8) @@ -1508,7 +1705,8 @@ (Jmp return))] [(Box x) (seq (Mov r8 rax) - (And r8 ptr-mask) + (Mov r9 ptr-mask) + (And r8 r9) (Cmp r8 type-box) (Jne next) (Xor rax type-box) @@ -1519,7 +1717,8 @@ (Jmp return))] [(Cons x1 x2) (seq (Mov r8 rax) - (And r8 ptr-mask) + (Mov r9 ptr-mask) + (And r8 r9) (Cmp r8 type-cons) (Jne next) (Xor rax type-cons) @@ -1611,6 +1810,22 @@ (define assert-proc (assert-type proc-mask type-proc)) +(define assert-integer/bignum + (λ (arg c) + (let ((ok (gensym "intorbig"))) + (seq (Mov r9 arg) ; first check if integer + (And r9 mask-int) + (Cmp r9 type-int) + (Je ok) + (Mov r9 arg) ; then check if bignum + (Mov r10 ptr-mask) + (And r9 r10) + (Mov r10 type-bignum) + (Cmp r9 r10) + (Je ok) + (Jmp (error-label c)) + (Label ok))))) + (define (assert-codepoint c) (let ((ok (gensym))) (seq (assert-integer rax c) @@ -1664,6 +1879,7 @@ (Extern 'raise_error) (Global 'raise_error_align) (Extern 'str_to_symbol) + (bignum-externs) (Label 'entry) (Mov rbx rdi) ; recv heap pointer (compile-e-tail p (reverse fs)) diff --git a/villain/interp-prims.rkt b/villain/interp-prims.rkt index cbdc04d..6b16eb7 100644 --- a/villain/interp-prims.rkt +++ b/villain/interp-prims.rkt @@ -49,8 +49,8 @@ [(list '+ (? integer?) (? integer?)) (+ v1 v2)] [(list '- (? integer?) (? integer?)) (- v1 v2)] [(list '* (? integer?) (? integer?)) (* v1 v2)] - [(list 'quotient (? integer?) (? integer?)) (quotient v1 v2)] - [(list 'remainder (? integer?) (? integer?)) (remainder v1 v2)] + [(list 'quotient (? integer?) (? integer?)) (if (= v2 0) 'err (quotient v1 v2))] + [(list 'remainder (? integer?) (? integer?)) (if (= v2 0) 'err (remainder v1 v2))] [(list '< (? integer?) (? integer?)) (< v1 v2)] [(list '> (? integer?) (? integer?)) (> v1 v2)] [(list '<= (? integer?) (? integer?)) (<= v1 v2)] diff --git a/villain/interp.rkt b/villain/interp.rkt index a7057ba..2cedeba 100644 --- a/villain/interp.rkt +++ b/villain/interp.rkt @@ -35,6 +35,7 @@ (define (interp-env e r ds) (match e [(Int i) i] + [(Bignum i) i] [(Bool b) b] [(Char c) c] [(Flonum f) f] diff --git a/villain/main.c b/villain/main.c index 1f03de5..ba43a62 100644 --- a/villain/main.c +++ b/villain/main.c @@ -2,12 +2,15 @@ #include #include "villain.h" #include "runtime.h" +#include +#include FILE* in; FILE* out; void (*error_handler)(); int64_t *heap; + void error_exit() { printf("err\n"); @@ -23,6 +26,7 @@ void print_str(vl_str *); void print_char(vl_char); void print_cons(vl_cons *); void print_vector(vl_vec *); +void print_bignum(vl_bignum *); vl_str *symbol_to_str(vl_symbol s); void print_result(vl_val x) @@ -58,6 +62,7 @@ void print_result(vl_val x) printf(")"); break; case VL_STR: + printf("test"); putchar('"'); print_str(vl_unwrap_str(x)); putchar('"'); @@ -77,6 +82,9 @@ void print_result(vl_val x) case VL_PORT: printf("#"); break; + case VL_BIGNUM: + print_bignum(vl_unwrap_bignum(x)); + break; case VL_INVALID: default: error_exit(); diff --git a/villain/parse.rkt b/villain/parse.rkt index 715e0a5..80f2f43 100644 --- a/villain/parse.rkt +++ b/villain/parse.rkt @@ -1,6 +1,6 @@ #lang racket (provide parse parse-e desugar desugar-def parse-library desugar-def-lib) -(require "ast.rkt") +(require "ast.rkt" "types.rkt") ;; S-Expr -> (Letrec (Lisof Id) (Listof Lambda) Expr) (define (parse s) @@ -58,7 +58,7 @@ ;; S-Expr -> Expr (define (parse-e s) (match s - [(? integer?) (Int s)] + [(? integer?) (if (bignum? s) (Bignum s) (Int s))] [(? boolean?) (Bool s)] [(? char?) (Char s)] [(? flonum?) (Flonum s)] @@ -200,6 +200,7 @@ [(Bool b) e] [(Char c) e] [(Flonum f) e] + [(Bignum b) e] [(Eof) e] [(Empty) e] [(String s) e] diff --git a/villain/test/test-runner.rkt b/villain/test/test-runner.rkt index bb1dc37..1e0a33e 100644 --- a/villain/test/test-runner.rkt +++ b/villain/test/test-runner.rkt @@ -570,6 +570,71 @@ (check-equal? (run '(cond (#f 2) (else 1))) 1) (check-equal? (run '(cond (0 2) (else 1))) 2) + ;; Bignums + (check-equal? (run 18446744073709551615) 18446744073709551615) + (check-equal? (run -18446744073709551615) -18446744073709551615) + (check-equal? (run 18446744073709551616) 18446744073709551616) + (check-equal? (run -18446744073709551616) -18446744073709551616) + + (check-equal? (run '(zero? -18446744073709551615)) (zero? -18446744073709551615)) + (check-equal? (run '(zero? 18446744073709551616)) (zero? 18446744073709551616)) + + (check-equal? (run '(integer? 16)) (integer? 16)) + (check-equal? (run '(integer? -18446744073709551616)) (integer? -18446744073709551616)) + + (check-equal? (run '(integer-length -16)) (integer-length -16)) + (check-equal? (run '(integer-length -18446744073709551617)) (integer-length -18446744073709551617)) + (check-equal? (run '(integer-length 18446744073709551616)) (integer-length 18446744073709551616)) + (check-equal? (run '(integer-length -18446744073709551616)) (integer-length -18446744073709551616)) + + (check-equal? (run '(sub1 -576460752303423488)) (sub1 -576460752303423488)) + (check-equal? (run '(sub1 576460752303423488)) (sub1 576460752303423488)) + (check-equal? (run '(sub1 576460752303423489)) (sub1 576460752303423489)) + (check-equal? (run '(sub1 0)) (sub1 0)) + (check-equal? (run '(add1 576460752303423487)) (add1 576460752303423487)) + (check-equal? (run '(add1 -576460752303423489)) (add1 -576460752303423489)) + (check-equal? (run '(add1 576460752303423489)) (add1 576460752303423489)) + (check-equal? (run '(add1 0)) (add1 0)) + + (check-equal? (run '(<= 0 0)) (<= 0 0)) + (check-equal? (run '(<= 0 -1)) (<= 0 -1)) + (check-equal? (run '(<= 0 1)) (<= 0 1)) + (check-equal? (run '(<= 0 -10000000000000000000)) (<= 0 -10000000000000000000)) + (check-equal? (run '(<= 0 10000000000000000000)) (<= 0 10000000000000000000)) + (check-equal? (run '(<= -10000000000000000000 0)) (<= -10000000000000000000 0)) + (check-equal? (run '(<= 10000000000000000000 0)) (<= 10000000000000000000 0)) + (check-equal? (run '(<= -10000000000000000000 -1000000000000000000)) (<= -10000000000000000000 -1000000000000000000)) + (check-equal? (run '(<= 10000000000000000000 -10000000000000000000)) (<= 10000000000000000000 -10000000000000000000)) + + (check-equal? (run '(< 0 0)) (< 0 0)) + (check-equal? (run '(< 0 -1)) (< 0 -1)) + (check-equal? (run '(< 0 1)) (< 0 1)) + (check-equal? (run '(< 0 10000000000000000000)) (< 0 10000000000000000000)) + (check-equal? (run '(< -10000000000000000000 0)) (< -10000000000000000000 0)) + (check-equal? (run '(< -10000000000000000000 -1000000000000000000)) (< -10000000000000000000 -1000000000000000000)) + + (check-equal? (run '(> 0 0)) (> 0 0)) + (check-equal? (run '(> 0 -1)) (> 0 -1)) + (check-equal? (run '(> 0 1)) (> 0 1)) + (check-equal? (run '(> 0 10000000000000000000)) (> 0 10000000000000000000)) + (check-equal? (run '(> -10000000000000000000 0)) (> -10000000000000000000 0)) + (check-equal? (run '(> -10000000000000000000 -1000000000000000000)) (> -10000000000000000000 -1000000000000000000)) + + (check-equal? (run '(+ 0 0)) (+ 0 0)) + (check-equal? (run '(+ 576460752303423487 2)) (+ 576460752303423487 2)) + (check-equal? (run '(+ -576460752303423487 -2)) (+ -576460752303423487 -2)) + (check-equal? (run '(+ -576460752303423487 -2)) (+ -576460752303423487 -2)) + (check-equal? (run '(+ -576460752303423490 576460752303423490)) (+ -576460752303423490 576460752303423490)) + (check-equal? (run '(+ -576460752303423490 10000000000000000000000000)) (+ -576460752303423490 10000000000000000000000000)) + (check-equal? (run '(+ 576460752303423490 10000000000000000000000000)) (+ 576460752303423490 10000000000000000000000000)) + + (check-equal? (run '(- 1 3)) (- 1 3)) + (check-equal? (run '(- 576460752303423489 2)) (- 576460752303423489 2)) + (check-equal? (run '(- -576460752303423487 2)) (- -576460752303423487 2)) + (check-equal? (run '(- -576460752303423489 -2)) (- -576460752303423489 -2)) + (check-equal? (run '(- 576460752303423490 576460752303423490)) (- 576460752303423490 576460752303423490)) + (check-equal? (run '(- -576460752303423490 10000000000000000000000000)) (- -576460752303423490 10000000000000000000000000)) + (check-equal? (run '(- 576460752303423490 10000000000000000000000000)) (- 576460752303423490 10000000000000000000000000)) ;; Vector Examples (check-equal? (run '#(2 3)) (vector 2 3)) diff --git a/villain/types.h b/villain/types.h index a9a3d75..8f1c6c5 100644 --- a/villain/types.h +++ b/villain/types.h @@ -18,15 +18,17 @@ - Empty: #b100 11 000 */ #define imm_shift 3 -#define ptr_type_mask ((1 << imm_shift) - 1) -#define ptr_addr_mask ~ptr_type_mask -#define box_type_tag 1 -#define cons_type_tag 2 -#define str_type_tag 3 -#define symbol_type_tag 4 -#define port_type_tag 5 -#define vector_type_tag 6 -#define flonum_type_tag 7 +#define ptr_type_mask (uint64_t)((((uint64_t)7) << 60) | (uint64_t)((1 << imm_shift) - 1)) +#define ptr_addr_mask (uint64_t)~ptr_type_mask +#define box_type_tag (uint64_t)0x1 +#define cons_type_tag (uint64_t)0x2 +#define str_type_tag (uint64_t)0x3 +#define symbol_type_tag (uint64_t)0x4 +#define port_type_tag (uint64_t)0x5 +#define vector_type_tag (uint64_t)0x6 +#define flonum_type_tag (uint64_t)0x7 +#define bignum_type_tag (uint64_t)0x1000000000000003 + #define int_shift (1 + imm_shift) #define int_type_mask ((1 << int_shift) - 1) #define int_type_tag (0 << (int_shift - 1)) diff --git a/villain/types.rkt b/villain/types.rkt index 54626af..92018d9 100644 --- a/villain/types.rkt +++ b/villain/types.rkt @@ -3,16 +3,20 @@ (define imm-shift 3) (define imm-mask #b111) -(define ptr-mask #b111) -(define type-box #b001) -(define type-cons #b010) -(define type-string #b011) -(define type-symbol #b100) -(define type-port #b101) -(define type-vector #b110) -(define type-flonum #b111) +(define ptr-mask #x7000000000000007) +(define ptr-top-mask #x7000000000000000) +(define ptr-bottom-mask #x0000000000000007) +(define type-box #x0000000000000001) +(define type-cons #x0000000000000002) +(define type-string #x0000000000000003) +(define type-symbol #x0000000000000004) +(define type-port #x0000000000000005) +(define type-vector #x0000000000000006) +(define type-flonum #x0000000000000007) (define type-proc #x1000000000000002) (define proc-mask #x7000000000000002) +(define type-bignum #x1000000000000003) + (define int-shift (+ 1 imm-shift)) (define char-shift (+ 2 imm-shift)) (define type-int #b0000) @@ -53,9 +57,6 @@ [(empty? v) val-empty] )) - - - ;; converts a flonum to bits with the IEEE protocol ;; so that the sign, exponent, and mantissa can be stored ;; where the flonum approximates to (-1)^sign * 2^exponent @@ -126,7 +127,6 @@ - (define (imm-bits? v) (zero? (bitwise-and v imm-mask))) @@ -137,25 +137,33 @@ (= type-char (bitwise-and v mask-char))) (define (flonum-bits? v) - (zero? (bitwise-xor (bitwise-and v imm-mask) type-flonum))) + (zero? (bitwise-xor (bitwise-and v ptr-mask) type-flonum))) (define (cons-bits? v) - (zero? (bitwise-xor (bitwise-and v imm-mask) type-cons))) + (zero? (bitwise-xor (bitwise-and v ptr-mask) type-cons))) (define (box-bits? v) - (zero? (bitwise-xor (bitwise-and v imm-mask) type-box))) + (zero? (bitwise-xor (bitwise-and v ptr-mask) type-box))) (define (string-bits? v) - (zero? (bitwise-xor (bitwise-and v imm-mask) type-string))) + (zero? (bitwise-xor (bitwise-and v ptr-mask) type-string))) (define (symbol-bits? v) - (zero? (bitwise-xor (bitwise-and v imm-mask) type-symbol))) + (zero? (bitwise-xor (bitwise-and v ptr-mask) type-symbol))) + +(define (bignum? v) + (or (>= v (arithmetic-shift 1 (- 63 int-shift))) + (< v (- (arithmetic-shift 1 (- 63 int-shift)))))) + +(define (bignum-bits? v) + (zero? (bitwise-xor (bitwise-and v ptr-mask) type-bignum))) (define (port-bits? v) - (zero? (bitwise-xor (bitwise-and v imm-mask) type-port))) + (zero? (bitwise-xor (bitwise-and v ptr-mask) type-port))) (define (vector-bits? v) (zero? (bitwise-xor (bitwise-and v imm-mask) type-vector))) (define (proc-bits? v) (zero? (bitwise-xor (bitwise-and v proc-mask) type-proc))) + \ No newline at end of file diff --git a/villain/unload-bits-asm.rkt b/villain/unload-bits-asm.rkt index 67dcf21..466a371 100644 --- a/villain/unload-bits-asm.rkt +++ b/villain/unload-bits-asm.rkt @@ -28,6 +28,10 @@ (let ((length (unload-value (heap-ref i)))) (let ((str-chars (string-loop length i))) (list->string (reverse str-chars)))))] + [(? bignum-bits? i) + (let ((b (bitwise-xor i type-bignum))) + (let ((length-sign (unload-value (heap-ref-signed b)))) + (* (/ length-sign (abs length-sign)) (bignum-loop (abs length-sign) b))))] [(? vector-bits? i) (let ((length (heap-ref i))) (let ((elems (vector-loop length i))) @@ -66,12 +70,16 @@ (+ twoExp 1)))])) (define (untag i) - (arithmetic-shift (arithmetic-shift i (- (integer-length ptr-mask))) - (integer-length ptr-mask))) + (arithmetic-shift (arithmetic-shift (arithmetic-shift i 4) (- (+ 4 (integer-length ptr-bottom-mask)))) + (integer-length ptr-bottom-mask))) + (define (heap-ref i) (ptr-ref (cast (untag i) _int64 _pointer) _uint64)) +(define (heap-ref-signed i) + (ptr-ref (cast (untag i) _int64 _pointer) _sint64)) + (define string-loop (λ (n i) (match n @@ -82,6 +90,13 @@ (let ((v2 (arithmetic-shift v1 -43))) (cons (unload-value v2) (string-loop (- n 1) i))))]))) +(define bignum-loop + (λ (n i) + (match n + [0 0] + [n (let ((v1 (heap-ref (+ i (arithmetic-shift n imm-shift))))) + (+ (arithmetic-shift v1 (* 64 (sub1 n))) (bignum-loop (- n 1) i)))]))) + (define vector-loop (λ (n i) (match n diff --git a/villain/villain.h b/villain/villain.h index f812196..fee9861 100644 --- a/villain/villain.h +++ b/villain/villain.h @@ -24,7 +24,8 @@ typedef enum vl_type { VL_SYMBOL, VL_VEC, VL_FLONUM, - VL_PORT + VL_PORT, + VL_BIGNUM } vl_type; typedef struct { @@ -51,6 +52,10 @@ typedef struct vl_port { int8_t closed; char buf[]; } vl_port; +typedef struct vl_bignum { + int64_t signlen; + int64_t buf[]; +} vl_bignum; /* return the type of x */ vl_type vl_typeof(vl_val x); @@ -90,6 +95,9 @@ vl_val vl_wrap_symbol(vl_symbol s); vl_port* vl_unwrap_port(vl_val x); vl_val vl_wrap_port(vl_port *p); +vl_bignum* vl_unwrap_bignum(vl_val x); +vl_val vl_wrap_bignum(vl_bignum *p); + vl_val vl_wrap_eof(void); vl_val vl_wrap_empty(void); vl_val vl_wrap_void(void); diff --git a/villain/wrap.c b/villain/wrap.c index f60a6f3..08af7f0 100644 --- a/villain/wrap.c +++ b/villain/wrap.c @@ -16,6 +16,8 @@ vl_type vl_typeof(vl_val x) return VL_VEC; case flonum_type_tag: return VL_FLONUM; + case bignum_type_tag: + return VL_BIGNUM; default: if ((int_type_mask & x) == int_type_tag) return VL_INT; @@ -171,6 +173,15 @@ vl_val vl_wrap_port(vl_port *p) return ((vl_val)p) | port_type_tag; } +vl_bignum* vl_unwrap_bignum(vl_val x) +{ + return (vl_bignum *)(x ^ bignum_type_tag); +} +vl_val vl_wrap_bignum(vl_bignum *p) +{ + return ((vl_val) p) | bignum_type_tag; +} + vl_val vl_wrap_eof(void) { return val_eof;