1 /*  bit.c -- bitwise operators                                */
2 /*  Copyright (c) 2009-2017 Alex Shinn.  All rights reserved. */
3 /*  BSD-style license: http://synthcode.com/license.txt       */
4 
5 #include <chibi/eval.h>
6 
7 #ifndef PLAN9
8 #include <limits.h>
9 #else
10 #define CHAR_BIT 8
11 #endif
12 
13 #if SEXP_USE_BIGNUMS
14 #include <chibi/bignum.h>
15 #else
16 #define sexp_bignum_normalize(x) x
17 #endif
18 
19 /* The twos complement form of a negative bignum has a -1 sign */
20 /* and bits adjusted as usual, extending just the high word with */
21 /* leading ones.  Bitwise operations are then performed as usual. */
22 /* If the result has a leading extended one from a twos complement */
23 /* number, the complement is reversed and sign remains negative. */
24 /* Otherwise, the result is positive, the sign is set to 1 and there's */
25 /* no need to undo the complement. */
sexp_set_twos_complement(sexp a)26 static void sexp_set_twos_complement (sexp a) {
27   int i, len=sexp_bignum_length(a), carry = 1;
28   sexp_uint_t* data = sexp_bignum_data(a), n;
29   for (i=len-1; i >=0; --i)
30     data[i] = ~data[i];
31   /* sexp_bignum_fxadd with no final carry */
32   i = 0;
33   do { n = data[i];
34        data[i] += carry;
35        carry = (n > (SEXP_UINT_T_MAX - carry));
36   } while (++i<len && carry);
37 }
38 
sexp_twos_complement(sexp ctx,sexp x)39 static sexp sexp_twos_complement (sexp ctx, sexp x) {
40   sexp_gc_var1(res);
41   if (sexp_bignump(x) && sexp_bignum_sign(x) < 0) {
42     sexp_gc_preserve1(ctx, res);
43     res = sexp_copy_bignum(ctx, NULL, x, 0);
44     sexp_set_twos_complement(res);
45     sexp_gc_release1(ctx);
46     return res;
47   }
48   return x;
49 }
50 
sexp_fixnum_to_twos_complement(sexp ctx,sexp x,int len)51 static sexp sexp_fixnum_to_twos_complement (sexp ctx, sexp x, int len) {
52   int i;
53   sexp_gc_var1(res);
54   sexp_gc_preserve1(ctx, res);
55   res = sexp_make_bignum(ctx, len);
56   if (sexp_unbox_fixnum(x) < 0)
57     for (i = len-1; i > 0; i--)
58       sexp_bignum_data(res)[i] = (sexp_uint_t)((sexp_sint_t)-1);
59   sexp_bignum_data(res)[0] = ~(-(sexp_unbox_fixnum(x)));
60   res = sexp_bignum_fxadd(ctx, res, 1);
61   if (sexp_bignum_length(res) == len + 1 && sexp_bignum_data(res)[len] == 1)
62     sexp_bignum_data(res)[len] = -1;
63   if (sexp_unbox_fixnum(x) < 0)
64     sexp_bignum_sign(res) = -1;
65   sexp_gc_release1(ctx);
66   return res;
67 }
68 
sexp_bit_and(sexp ctx,sexp self,sexp_sint_t n,sexp x,sexp y)69 sexp sexp_bit_and (sexp ctx, sexp self, sexp_sint_t n, sexp x, sexp y) {
70 #if SEXP_USE_BIGNUMS
71   sexp_sint_t len, lenx, leny, i;
72 #endif
73   sexp_gc_var3(res, x2, y2);
74   if (sexp_fixnump(x) && sexp_fixnump(y)) {
75     return (sexp) ((sexp_uint_t)x & (sexp_uint_t)y);  /* safe to AND tags */
76 #if SEXP_USE_BIGNUMS
77   } else if (sexp_fixnump(x) && sexp_bignump(y)) {
78     return sexp_bit_and(ctx, self, n, y, x);
79   } else if (sexp_bignump(x)) {
80     sexp_gc_preserve3(ctx, res, x2, y2);
81     x2 = sexp_twos_complement(ctx, x);
82     y2 = sexp_twos_complement(ctx, y);
83     if (sexp_fixnump(y2) && sexp_unbox_fixnum(y2) < 0)
84       y2 = sexp_fixnum_to_twos_complement(ctx, y2, sexp_bignum_length(x2));
85     if (sexp_fixnump(y2)) {
86       res = sexp_make_fixnum(sexp_unbox_fixnum(y2) & sexp_bignum_data(x2)[0]);
87     } else if (sexp_bignump(y2)) {
88       lenx = sexp_bignum_length(x2);
89       leny = sexp_bignum_length(y2);
90       if (leny < lenx)
91         res = sexp_copy_bignum(ctx, NULL, x2, 0);
92       else
93         res = sexp_copy_bignum(ctx, NULL, y2, 0);
94       for (i=0, len=sexp_bignum_length(res); i<len; i++)
95         sexp_bignum_data(res)[i]
96           = (i<lenx ? sexp_bignum_data(x2)[i] : sexp_bignum_sign(x2) < 0 ? -1 : 0) &
97             (i<leny ? sexp_bignum_data(y2)[i] : sexp_bignum_sign(y2) < 0 ? -1 : 0);
98       if ((sexp_bignum_sign(x2) < 0 || sexp_bignum_sign(y2) < 0) && ((sexp_sint_t)(sexp_bignum_data(res)[len-1])) < 0) {
99         sexp_set_twos_complement(res);
100         if (sexp_bignum_sign(res) > 0) {
101           sexp_negate_exact(res);
102         }
103       } else if (sexp_bignum_sign(res) < 0) {
104         sexp_negate_exact(res);
105       }
106     } else {
107       res = sexp_type_exception(ctx, self, SEXP_FIXNUM, y2);
108     }
109     sexp_gc_release3(ctx);
110     return sexp_bignum_normalize(res);
111 #endif
112   } else {
113     return sexp_type_exception(ctx, self, SEXP_FIXNUM, x);
114   }
115 }
116 
sexp_bit_ior(sexp ctx,sexp self,sexp_sint_t n,sexp x,sexp y)117 sexp sexp_bit_ior (sexp ctx, sexp self, sexp_sint_t n, sexp x, sexp y) {
118 #if SEXP_USE_BIGNUMS
119   sexp_sint_t len, tmplen, i;
120 #endif
121   sexp_gc_var2(res, tmp);
122   if (sexp_fixnump(x)) {
123     if (sexp_fixnump(y))
124       res = (sexp) ((sexp_uint_t)x | (sexp_uint_t)y);
125 #if SEXP_USE_BIGNUMS
126     else if (sexp_bignump(y))
127       res = sexp_bit_ior(ctx, self, n, y, x);
128 #endif
129     else
130       res = sexp_type_exception(ctx, self, SEXP_FIXNUM, y);
131 #if SEXP_USE_BIGNUMS
132   } else if (sexp_bignump(x)) {
133     sexp_gc_preserve2(ctx, res, tmp);
134     if (sexp_fixnump(y) && sexp_unbox_fixnum(y) >= 0) {
135       res = sexp_copy_bignum(ctx, NULL, x, 0);
136       if (sexp_bignum_sign(res) < 0)
137         sexp_set_twos_complement(res);
138       sexp_bignum_data(res)[0] |= (sexp_uint_t)sexp_unbox_fixnum(y);
139       if (sexp_bignum_sign(res) < 0)
140         sexp_set_twos_complement(res);
141     } else if (sexp_bignump(y) || sexp_fixnump(y)) {
142       if (sexp_fixnump(y) || sexp_bignum_length(x) >= sexp_bignum_length(y)) {
143         res = sexp_copy_bignum(ctx, NULL, x, 0);
144         len = sexp_bignum_length(res);
145         tmp = sexp_fixnump(y) ? sexp_fixnum_to_twos_complement(ctx, y, len) : sexp_twos_complement(ctx, y);
146       } else {
147         res = sexp_copy_bignum(ctx, NULL, y, 0);
148         len = sexp_bignum_length(res);
149         tmp = sexp_twos_complement(ctx, x);
150       }
151       if (sexp_bignum_sign(res) < 0)
152         sexp_set_twos_complement(res);
153       tmplen = sexp_bignum_length(tmp);
154       for (i=0; i<len; i++)
155         sexp_bignum_data(res)[i] |= (i<tmplen ? sexp_bignum_data(tmp)[i] : sexp_bignum_sign(tmp) < 0 ? -1 : 0);
156       if ((sexp_bignum_sign(res) < 0 || sexp_bignum_sign(tmp) < 0) && ((sexp_sint_t)(sexp_bignum_data(res)[len-1])) < 0) {
157         sexp_set_twos_complement(res);
158         if (sexp_bignum_sign(res) > 0) {
159           sexp_negate_exact(res);
160         }
161       }
162     } else {
163       res = sexp_type_exception(ctx, self, SEXP_FIXNUM, y);
164     }
165     sexp_gc_release2(ctx);
166 #endif
167   } else {
168     res = sexp_type_exception(ctx, self, SEXP_FIXNUM, x);
169   }
170   return sexp_bignum_normalize(res);
171 }
172 
sexp_bit_xor(sexp ctx,sexp self,sexp_sint_t n,sexp x,sexp y)173 sexp sexp_bit_xor (sexp ctx, sexp self, sexp_sint_t n, sexp x, sexp y) {
174 #if SEXP_USE_BIGNUMS
175   sexp_sint_t len, tmplen, i;
176 #endif
177   sexp_gc_var2(res, tmp);
178   if (sexp_fixnump(x)) {
179     if (sexp_fixnump(y))
180       res = sexp_make_fixnum(sexp_unbox_fixnum(x) ^ sexp_unbox_fixnum(y));
181 #if SEXP_USE_BIGNUMS
182     else if (sexp_bignump(y))
183       res = sexp_bit_xor(ctx, self, n, y, x);
184 #endif
185     else
186       res = sexp_type_exception(ctx, self, SEXP_FIXNUM, y);
187 #if SEXP_USE_BIGNUMS
188   } else if (sexp_bignump(x)) {
189     sexp_gc_preserve2(ctx, res, tmp);
190     if (sexp_fixnump(y) && sexp_unbox_fixnum(y) >= 0) {
191       res = sexp_copy_bignum(ctx, NULL, x, 0);
192       if (sexp_bignum_sign(res) < 0)
193         sexp_set_twos_complement(res);
194       sexp_bignum_data(res)[0] ^= sexp_unbox_fixnum(y);
195       if (sexp_bignum_sign(res) < 0)
196         sexp_set_twos_complement(res);
197     } else if (sexp_bignump(y) || sexp_fixnump(y)) {
198       if (sexp_fixnump(y) || sexp_bignum_length(x) >= sexp_bignum_length(y)) {
199         res = sexp_copy_bignum(ctx, NULL, x, 0);
200         tmp = sexp_fixnump(y) ? sexp_fixnum_to_twos_complement(ctx, y, sexp_bignum_length(x)) : sexp_twos_complement(ctx, y);
201         len = sexp_bignum_length(tmp);
202       } else {
203         res = sexp_copy_bignum(ctx, NULL, y, 0);
204         tmp = sexp_twos_complement(ctx, y);
205         len = sexp_bignum_length(tmp);
206       }
207       if (sexp_bignum_sign(res) < 0)
208         sexp_set_twos_complement(res);
209       tmplen = sexp_bignum_length(tmp);
210       for (i=0; i<len; i++)
211         sexp_bignum_data(res)[i] ^= (i<tmplen ? sexp_bignum_data(tmp)[i] : sexp_bignum_sign(tmp) < 0 ? -1 : 0);
212       if ((sexp_bignum_sign(x) < 0) ^ (sexp_fixnump(y) || sexp_bignum_sign(y) < 0))
213         sexp_set_twos_complement(res);
214       if (sexp_fixnump(y) || sexp_bignum_sign(y) < 0) {
215         sexp_negate_exact(res);
216       }
217     } else {
218       res = sexp_type_exception(ctx, self, SEXP_FIXNUM, y);
219     }
220     sexp_gc_release2(ctx);
221 #endif
222   } else {
223     res = sexp_type_exception(ctx, self, SEXP_FIXNUM, x);
224   }
225   return sexp_bignum_normalize(res);
226 }
227 
log2i(sexp_uint_t v)228 static int log2i(sexp_uint_t v) {
229   int i;
230   for (i = 0; i < sizeof(v)*8; i++)
231     if (((sexp_uint_t)1<<(i+1)) > v)
232       break;
233   return i;
234 }
235 
236 /* should probably split into left and right shifts, that's a better */
237 /* interface anyway */
sexp_arithmetic_shift(sexp ctx,sexp self,sexp_sint_t n,sexp i,sexp count)238 sexp sexp_arithmetic_shift (sexp ctx, sexp self, sexp_sint_t n, sexp i, sexp count) {
239   sexp_uint_t tmp;
240   sexp_sint_t c;
241 #if SEXP_USE_BIGNUMS
242   sexp_sint_t len, offset, bit_shift, tail_shift, j;
243   sexp_gc_var1(res);
244 #else
245   sexp res;
246 #endif
247   if (! sexp_fixnump(count))
248     return sexp_type_exception(ctx, self, SEXP_FIXNUM, count);
249   c = sexp_unbox_fixnum(count);
250   if (c == 0) return i;
251   if (sexp_fixnump(i)) {
252     if (c < 0) {
253       res = sexp_make_fixnum(c > -sizeof(sexp_sint_t)*CHAR_BIT ? sexp_unbox_fixnum(i) >> -c : 0);
254     } else {
255 #if SEXP_USE_BIGNUMS
256       if ((log2i(sexp_unbox_fixnum(i)) + c + 1)
257           < (sizeof(sexp_uint_t)*CHAR_BIT - SEXP_FIXNUM_BITS)) {
258 #endif
259         tmp = (sexp_uint_t)sexp_unbox_fixnum(i) << c;
260         res = sexp_make_fixnum(tmp * sexp_fx_sign(i));
261 #if SEXP_USE_BIGNUMS
262       } else {
263         sexp_gc_preserve1(ctx, res);
264         res = sexp_fixnum_to_bignum(ctx, i);
265         res = sexp_arithmetic_shift(ctx, self, n, res, count);
266         sexp_gc_release1(ctx);
267       }
268 #endif
269     }
270 #if SEXP_USE_BIGNUMS
271   } else if (sexp_bignump(i)) {
272     len = sexp_bignum_hi(i);
273     if (c < 0) {
274       c = -c;
275       offset = c / (sizeof(sexp_uint_t)*CHAR_BIT);
276       bit_shift = c - offset*(sizeof(sexp_uint_t)*CHAR_BIT);
277       if (len < offset) {
278         res = sexp_make_fixnum(sexp_bignum_sign(i) > 0 ? 0 : -1);
279       } else {
280         res = sexp_make_bignum(ctx, len - offset + 1);
281         if (!sexp_exceptionp(res)) {
282           sexp_bignum_sign(res) = sexp_bignum_sign(i);
283           for (j=len-offset-1, tmp=0; j>=0; j--) {
284             sexp_bignum_data(res)[j]
285               = (sexp_bignum_data(i)[j+offset] >> bit_shift)+ tmp;
286             if (bit_shift != 0)
287               tmp = sexp_bignum_data(i)[j+offset]
288                 << (sizeof(sexp_uint_t)*CHAR_BIT-bit_shift);
289           }
290           if (sexp_bignum_sign(res) < 0)
291             res = sexp_bignum_fxadd(ctx, res, 1);
292         }
293       }
294     } else {
295       offset = c / (sizeof(sexp_uint_t)*CHAR_BIT);
296       bit_shift = c - offset*(sizeof(sexp_uint_t)*CHAR_BIT);
297       tail_shift = (sizeof(sexp_uint_t)*CHAR_BIT-bit_shift);
298       res = sexp_make_bignum(ctx, len + offset + 1);
299       if (!sexp_exceptionp(res)) {
300         sexp_bignum_sign(res) = sexp_bignum_sign(i);
301         for (j=tmp=0; j<len; j++) {
302           sexp_bignum_data(res)[j+offset]
303             = (sexp_bignum_data(i)[j] << bit_shift) + tmp;
304           if (bit_shift != 0)
305             tmp = sexp_bignum_data(i)[j] >> tail_shift;
306         }
307         if (bit_shift != 0) sexp_bignum_data(res)[len+offset] = tmp;
308       }
309     }
310 #endif
311   } else {
312     res = sexp_type_exception(ctx, self, SEXP_FIXNUM, i);
313   }
314   return sexp_bignum_normalize(res);
315 }
316 
317 /* bit-count and integer-length were adapted from: */
318 /* http://graphics.stanford.edu/~seander/bithacks.html */
bit_count(sexp_uint_t i)319 static sexp_uint_t bit_count (sexp_uint_t i) {
320   i -= ((i >> 1) & (sexp_uint_t)~(sexp_uint_t)0/3);
321   i = ((i & (sexp_uint_t)~(sexp_uint_t)0/15*3)
322        + ((i >> 2) & (sexp_uint_t)~(sexp_uint_t)0/15*3));
323   i = (i + (i >> 4)) & (sexp_uint_t)~(sexp_uint_t)0/255*15;
324   return ((sexp_uint_t)(i * ((sexp_uint_t)~(sexp_uint_t)0/255))
325           >> (sizeof(i) - 1) * CHAR_BIT);
326 }
327 
sexp_bit_count(sexp ctx,sexp self,sexp_sint_t n,sexp x)328 sexp sexp_bit_count (sexp ctx, sexp self, sexp_sint_t n, sexp x) {
329   sexp res;
330   sexp_sint_t i;
331 #if SEXP_USE_BIGNUMS
332   sexp_uint_t count;
333 #endif
334   if (sexp_fixnump(x)) {
335     i = sexp_unbox_fixnum(x);
336     res = sexp_make_fixnum(bit_count(i<0 ? ~i : i));
337 #if SEXP_USE_BIGNUMS
338   } else if (sexp_bignump(x)) {
339     for (i=count=0; i<(sexp_sint_t)sexp_bignum_length(x); i++)
340       count += bit_count(sexp_bignum_data(x)[i]);
341     res = sexp_make_fixnum(count);
342 #endif
343   } else {
344     res = sexp_type_exception(ctx, self, SEXP_FIXNUM, x);
345   }
346   return res;
347 }
348 
349 static const char log_table_256[256] =
350 {
351 #define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n
352   0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
353   LT(5), LT(6), LT(6), LT(7), LT(7), LT(7), LT(7),
354   LT(8), LT(8), LT(8), LT(8), LT(8), LT(8), LT(8), LT(8)
355 };
356 
integer_log2(sexp_uint_t x)357 static sexp_uint_t integer_log2 (sexp_uint_t x) {
358   sexp_uint_t t, tt;
359 #if SEXP_64_BIT
360   if ((tt = x >> 32))
361     return integer_log2(tt) + 32;
362   else
363 #endif
364   if ((tt = x >> 16))
365     return (t = tt >> 8) ? 24 + log_table_256[t] : 16 + log_table_256[tt];
366   else
367     return (t = x >> 8) ? 8 + log_table_256[t] : log_table_256[x];
368 }
369 
sexp_integer_length(sexp ctx,sexp self,sexp_sint_t n,sexp x)370 sexp sexp_integer_length (sexp ctx, sexp self, sexp_sint_t n, sexp x) {
371   sexp_sint_t tmp;
372 #if SEXP_USE_BIGNUMS
373   sexp_sint_t hi;
374 #endif
375   if (sexp_fixnump(x)) {
376     tmp = sexp_unbox_fixnum(x);
377     return sexp_make_fixnum(integer_log2(tmp < 0 ? -tmp-1 : tmp));
378 #if SEXP_USE_BIGNUMS
379   } else if (sexp_bignump(x)) {
380     hi = sexp_bignum_hi(x);
381     return sexp_make_fixnum(integer_log2(sexp_bignum_data(x)[hi-1])
382                             + (hi-1)*sizeof(sexp_uint_t)*CHAR_BIT);
383 #endif
384   } else {
385     return sexp_type_exception(ctx, self, SEXP_FIXNUM, x);
386   }
387 }
388 
sexp_bit_set_p(sexp ctx,sexp self,sexp_sint_t n,sexp i,sexp x)389 sexp sexp_bit_set_p (sexp ctx, sexp self, sexp_sint_t n, sexp i, sexp x) {
390   sexp_sint_t pos;
391 #if SEXP_USE_BIGNUMS
392   sexp_sint_t rem;
393 #endif
394   if (! sexp_fixnump(i))
395     return sexp_type_exception(ctx, self, SEXP_FIXNUM, i);
396   pos = sexp_unbox_fixnum(i);
397   if (pos < 0)
398     return sexp_xtype_exception(ctx, self, "index must be non-negative", i);
399   if (sexp_fixnump(x)) {
400     return sexp_make_boolean((pos < sizeof(sexp_uint_t)*CHAR_BIT)
401                              ? (sexp_unbox_fixnum(x) & ((sexp_uint_t)1<<pos))
402                              : sexp_unbox_fixnum(x) < 0);
403 #if SEXP_USE_BIGNUMS
404   } else if (sexp_bignump(x)) {
405     pos /= (sizeof(sexp_uint_t)*CHAR_BIT);
406     rem = (sexp_unbox_fixnum(i) - pos*sizeof(sexp_uint_t)*CHAR_BIT);
407     return sexp_make_boolean((pos < (sexp_sint_t)sexp_bignum_length(x))
408                              ? (sexp_bignum_data(x)[pos] & ((sexp_uint_t)1<<rem))
409                              : sexp_bignum_sign(x) < 0);
410 #endif
411   } else {
412     return sexp_type_exception(ctx, self, SEXP_FIXNUM, x);
413   }
414 }
415 
sexp_init_library(sexp ctx,sexp self,sexp_sint_t n,sexp env,const char * version,const sexp_abi_identifier_t abi)416 sexp sexp_init_library (sexp ctx, sexp self, sexp_sint_t n, sexp env, const char* version, const sexp_abi_identifier_t abi) {
417   if (!(sexp_version_compatible(ctx, version, sexp_version)
418         && sexp_abi_compatible(ctx, abi, SEXP_ABI_IDENTIFIER)))
419     return SEXP_ABI_ERROR;
420   sexp_define_foreign(ctx, env, "bit-and",    2, sexp_bit_and);
421   sexp_define_foreign(ctx, env, "bit-ior",    2, sexp_bit_ior);
422   sexp_define_foreign(ctx, env, "bit-xor",    2, sexp_bit_xor);
423   sexp_define_foreign(ctx, env, "arithmetic-shift",  2, sexp_arithmetic_shift);
424   sexp_define_foreign(ctx, env, "bit-count",  1, sexp_bit_count);
425   sexp_define_foreign(ctx, env, "integer-length",  1, sexp_integer_length);
426   sexp_define_foreign(ctx, env, "bit-set?", 2, sexp_bit_set_p);
427   return SEXP_VOID;
428 }
429