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