]> gitweb.factorcode.org Git - factor.git/blob - vm/math.cpp
vm: speed up primitive_fixnum_divmod by 10-12%.
[factor.git] / vm / math.cpp
1 #include "master.hpp"
2
3 namespace factor
4 {
5
6 void factor_vm::primitive_bignum_to_fixnum()
7 {
8         ctx->replace(tag_fixnum(bignum_to_fixnum(untag<bignum>(ctx->peek()))));
9 }
10
11 void factor_vm::primitive_float_to_fixnum()
12 {
13         ctx->replace(tag_fixnum(float_to_fixnum(ctx->peek())));
14 }
15
16 /* does not allocate, even though from_signed_cell can allocate */
17 /* Division can only overflow when we are dividing the most negative fixnum
18 by -1. */
19 void factor_vm::primitive_fixnum_divint()
20 {
21         fixnum y = untag_fixnum(ctx->pop());
22         fixnum x = untag_fixnum(ctx->peek());
23         fixnum result = x / y;
24         if(result == -fixnum_min)
25                 /* Does not allocate */
26                 ctx->replace(from_signed_cell(-fixnum_min));
27         else
28                 ctx->replace(tag_fixnum(result));
29 }
30
31 /* does not allocate, even though from_signed_cell can allocate */
32 void factor_vm::primitive_fixnum_divmod()
33 {
34         cell *s0 = (cell *)(ctx->datastack);
35         cell *s1 = (cell *)(ctx->datastack - sizeof(cell));
36         fixnum y = untag_fixnum(*s0);
37         fixnum x = untag_fixnum(*s1);
38         if(y == -1 && x == fixnum_min)
39         {
40                 /* Does not allocate */
41                 *s1 = from_signed_cell(-fixnum_min);
42                 *s0 = tag_fixnum(0);
43         }
44         else
45         {
46                 fixnum z = x / y;
47                 fixnum w = x % y;
48                 *s1 = tag_fixnum(z);
49                 *s0 = tag_fixnum(w);
50         }
51 }
52
53 /*
54  * If we're shifting right by n bits, we won't overflow as long as none of the
55  * high WORD_SIZE-TAG_BITS-n bits are set.
56  */
57 inline fixnum factor_vm::sign_mask(fixnum x)
58 {
59         return x >> (WORD_SIZE - 1);
60 }
61
62 inline fixnum factor_vm::branchless_max(fixnum x, fixnum y)
63 {
64         return (x - ((x - y) & sign_mask(x - y)));
65 }
66
67 inline fixnum factor_vm::branchless_abs(fixnum x)
68 {
69         return (x ^ sign_mask(x)) - sign_mask(x);
70 }
71
72 void factor_vm::primitive_fixnum_shift()
73 {
74         fixnum y = untag_fixnum(ctx->pop());
75         fixnum x = untag_fixnum(ctx->peek());
76
77         if(x == 0)
78                 return;
79         else if(y < 0)
80         {
81                 y = branchless_max(y,-WORD_SIZE + 1);
82                 ctx->replace(tag_fixnum(x >> -y));
83                 return;
84         }
85         else if(y < WORD_SIZE - TAG_BITS)
86         {
87                 fixnum mask = -((fixnum)1 << (WORD_SIZE - 1 - TAG_BITS - y));
88                 if(!(branchless_abs(x) & mask))
89                 {
90                         ctx->replace(tag_fixnum(x << y));
91                         return;
92                 }
93         }
94
95         ctx->replace(tag<bignum>(bignum_arithmetic_shift(
96                 fixnum_to_bignum(x),y)));
97 }
98
99 void factor_vm::primitive_fixnum_to_bignum()
100 {
101         ctx->replace(tag<bignum>(fixnum_to_bignum(untag_fixnum(ctx->peek()))));
102 }
103
104 void factor_vm::primitive_float_to_bignum()
105 {
106         ctx->replace(tag<bignum>(float_to_bignum(ctx->peek())));
107 }
108
109 #define POP_BIGNUMS(x,y) \
110         bignum * y = untag<bignum>(ctx->pop()); \
111         bignum * x = untag<bignum>(ctx->pop());
112
113 void factor_vm::primitive_bignum_eq()
114 {
115         POP_BIGNUMS(x,y);
116         ctx->push(tag_boolean(bignum_equal_p(x,y)));
117 }
118
119 void factor_vm::primitive_bignum_add()
120 {
121         POP_BIGNUMS(x,y);
122         ctx->push(tag<bignum>(bignum_add(x,y)));
123 }
124
125 void factor_vm::primitive_bignum_subtract()
126 {
127         POP_BIGNUMS(x,y);
128         ctx->push(tag<bignum>(bignum_subtract(x,y)));
129 }
130
131 void factor_vm::primitive_bignum_multiply()
132 {
133         POP_BIGNUMS(x,y);
134         ctx->push(tag<bignum>(bignum_multiply(x,y)));
135 }
136
137 void factor_vm::primitive_bignum_divint()
138 {
139         POP_BIGNUMS(x,y);
140         ctx->push(tag<bignum>(bignum_quotient(x,y)));
141 }
142
143 void factor_vm::primitive_bignum_divmod()
144 {
145         bignum *q, *r;
146         POP_BIGNUMS(x,y);
147         bignum_divide(x,y,&q,&r);
148         ctx->push(tag<bignum>(q));
149         ctx->push(tag<bignum>(r));
150 }
151
152 void factor_vm::primitive_bignum_mod()
153 {
154         POP_BIGNUMS(x,y);
155         ctx->push(tag<bignum>(bignum_remainder(x,y)));
156 }
157
158 void factor_vm::primitive_bignum_gcd()
159 {
160         POP_BIGNUMS(x,y);
161         ctx->push(tag<bignum>(bignum_gcd(x,y)));
162 }
163
164 void factor_vm::primitive_bignum_and()
165 {
166         POP_BIGNUMS(x,y);
167         ctx->push(tag<bignum>(bignum_bitwise_and(x,y)));
168 }
169
170 void factor_vm::primitive_bignum_or()
171 {
172         POP_BIGNUMS(x,y);
173         ctx->push(tag<bignum>(bignum_bitwise_ior(x,y)));
174 }
175
176 void factor_vm::primitive_bignum_xor()
177 {
178         POP_BIGNUMS(x,y);
179         ctx->push(tag<bignum>(bignum_bitwise_xor(x,y)));
180 }
181
182 void factor_vm::primitive_bignum_shift()
183 {
184         fixnum y = untag_fixnum(ctx->pop());
185         bignum* x = untag<bignum>(ctx->pop());
186         ctx->push(tag<bignum>(bignum_arithmetic_shift(x,y)));
187 }
188
189 void factor_vm::primitive_bignum_less()
190 {
191         POP_BIGNUMS(x,y);
192         ctx->push(tag_boolean(bignum_compare(x,y) == bignum_comparison_less));
193 }
194
195 void factor_vm::primitive_bignum_lesseq()
196 {
197         POP_BIGNUMS(x,y);
198         ctx->push(tag_boolean(bignum_compare(x,y) != bignum_comparison_greater));
199 }
200
201 void factor_vm::primitive_bignum_greater()
202 {
203         POP_BIGNUMS(x,y);
204         ctx->push(tag_boolean(bignum_compare(x,y) == bignum_comparison_greater));
205 }
206
207 void factor_vm::primitive_bignum_greatereq()
208 {
209         POP_BIGNUMS(x,y);
210         ctx->push(tag_boolean(bignum_compare(x,y) != bignum_comparison_less));
211 }
212
213 void factor_vm::primitive_bignum_not()
214 {
215         ctx->replace(tag<bignum>(bignum_bitwise_not(untag<bignum>(ctx->peek()))));
216 }
217
218 void factor_vm::primitive_bignum_bitp()
219 {
220         int bit = (int)to_fixnum(ctx->pop());
221         bignum *x = untag<bignum>(ctx->pop());
222         ctx->push(tag_boolean(bignum_logbitp(bit,x)));
223 }
224
225 void factor_vm::primitive_bignum_log2()
226 {
227         ctx->replace(tag<bignum>(bignum_integer_length(untag<bignum>(ctx->peek()))));
228 }
229
230 /* allocates memory */
231 cell factor_vm::unbox_array_size_slow()
232 {
233         if(tagged<object>(ctx->peek()).type() == BIGNUM_TYPE)
234         {
235                 bignum *zero = untag<bignum>(bignum_zero);
236                 GC_BIGNUM(zero);
237                 bignum *max = cell_to_bignum(array_size_max);
238                 bignum *n = untag<bignum>(ctx->peek());
239                 if(bignum_compare(n,zero) != bignum_comparison_less
240                         && bignum_compare(n,max) == bignum_comparison_less)
241                 {
242                         ctx->pop();
243                         return bignum_to_cell(n);
244                 }
245         }
246
247         general_error(ERROR_ARRAY_SIZE,ctx->pop(),tag_fixnum(array_size_max));
248         return 0; /* can't happen */
249 }
250
251 void factor_vm::primitive_fixnum_to_float()
252 {
253         ctx->replace(allot_float(fixnum_to_float(ctx->peek())));
254 }
255
256 void factor_vm::primitive_format_float()
257 {
258         byte_array *array = allot_byte_array(100);
259         char *format = alien_offset(ctx->pop());
260         double value = untag_float_check(ctx->pop());
261         SNPRINTF(array->data<char>(),99,format,value);
262         ctx->push(tag<byte_array>(array));
263 }
264
265 #define POP_FLOATS(x,y) \
266         double y = untag_float(ctx->pop()); \
267         double x = untag_float(ctx->pop());
268
269 void factor_vm::primitive_float_eq()
270 {
271         POP_FLOATS(x,y);
272         ctx->push(tag_boolean(x == y));
273 }
274
275 void factor_vm::primitive_float_add()
276 {
277         POP_FLOATS(x,y);
278         ctx->push(allot_float(x + y));
279 }
280
281 void factor_vm::primitive_float_subtract()
282 {
283         POP_FLOATS(x,y);
284         ctx->push(allot_float(x - y));
285 }
286
287 void factor_vm::primitive_float_multiply()
288 {
289         POP_FLOATS(x,y);
290         ctx->push(allot_float(x * y));
291 }
292
293 void factor_vm::primitive_float_divfloat()
294 {
295         POP_FLOATS(x,y);
296         ctx->push(allot_float(x / y));
297 }
298
299 void factor_vm::primitive_float_less()
300 {
301         POP_FLOATS(x,y);
302         ctx->push(tag_boolean(x < y));
303 }
304
305 void factor_vm::primitive_float_lesseq()
306 {
307         POP_FLOATS(x,y);
308         ctx->push(tag_boolean(x <= y));
309 }
310
311 void factor_vm::primitive_float_greater()
312 {
313         POP_FLOATS(x,y);
314         ctx->push(tag_boolean(x > y));
315 }
316
317 void factor_vm::primitive_float_greatereq()
318 {
319         POP_FLOATS(x,y);
320         ctx->push(tag_boolean(x >= y));
321 }
322
323 /* Allocates memory */
324 void factor_vm::primitive_float_bits()
325 {
326         ctx->push(from_unsigned_cell(float_bits((float)untag_float_check(ctx->pop()))));
327 }
328
329 /* Allocates memory */
330 void factor_vm::primitive_bits_float()
331 {
332         ctx->push(allot_float(bits_float((u32)to_cell(ctx->pop()))));
333 }
334
335 void factor_vm::primitive_double_bits()
336 {
337         ctx->push(from_unsigned_8(double_bits(untag_float_check(ctx->pop()))));
338 }
339
340 void factor_vm::primitive_bits_double()
341 {
342         ctx->push(allot_float(bits_double(to_unsigned_8(ctx->pop()))));
343 }
344
345 /* Cannot allocate */
346 fixnum factor_vm::to_fixnum(cell tagged)
347 {
348         switch(TAG(tagged))
349         {
350         case FIXNUM_TYPE:
351                 return untag_fixnum(tagged);
352         case BIGNUM_TYPE:
353                 return bignum_to_fixnum(untag<bignum>(tagged));
354         default:
355                 type_error(FIXNUM_TYPE,tagged);
356                 return 0; /* can't happen */
357         }
358 }
359
360 VM_C_API fixnum to_fixnum(cell tagged, factor_vm *parent)
361 {
362         return parent->to_fixnum(tagged);
363 }
364
365 cell factor_vm::to_cell(cell tagged)
366 {
367         return (cell)to_fixnum(tagged);
368 }
369
370 VM_C_API cell to_cell(cell tagged, factor_vm *parent)
371 {
372         return parent->to_cell(tagged);
373 }
374
375 /* Allocates memory */
376 VM_C_API cell from_signed_cell(fixnum integer, factor_vm *parent)
377 {
378         return parent->from_signed_cell(integer);
379 }
380
381 /* Allocates memory */
382 VM_C_API cell from_unsigned_cell(cell integer, factor_vm *parent)
383 {
384         return parent->from_unsigned_cell(integer);
385 }
386
387 /* Allocates memory */
388 cell factor_vm::from_signed_8(s64 n)
389 {
390         if(n < fixnum_min || n > fixnum_max)
391                 return tag<bignum>(long_long_to_bignum(n));
392         else
393                 return tag_fixnum((fixnum)n);
394 }
395
396 VM_C_API cell from_signed_8(s64 n, factor_vm *parent)
397 {
398         return parent->from_signed_8(n);
399 }
400
401 /* Cannot allocate */
402 s64 factor_vm::to_signed_8(cell obj)
403 {
404         switch(tagged<object>(obj).type())
405         {
406         case FIXNUM_TYPE:
407                 return untag_fixnum(obj);
408         case BIGNUM_TYPE:
409                 return bignum_to_long_long(untag<bignum>(obj));
410         default:
411                 type_error(BIGNUM_TYPE,obj);
412                 return 0;
413         }
414 }
415
416 VM_C_API s64 to_signed_8(cell obj, factor_vm *parent)
417 {
418         return parent->to_signed_8(obj);
419 }
420
421 cell factor_vm::from_unsigned_8(u64 n)
422 {
423         if(n > (u64)fixnum_max)
424                 return tag<bignum>(ulong_long_to_bignum(n));
425         else
426                 return tag_fixnum((fixnum)n);
427 }
428
429 VM_C_API cell from_unsigned_8(u64 n, factor_vm *parent)
430 {
431         return parent->from_unsigned_8(n);
432 }
433
434 /* Cannot allocate */
435 u64 factor_vm::to_unsigned_8(cell obj)
436 {
437         switch(tagged<object>(obj).type())
438         {
439         case FIXNUM_TYPE:
440                 return untag_fixnum(obj);
441         case BIGNUM_TYPE:
442                 return bignum_to_ulong_long(untag<bignum>(obj));
443         default:
444                 type_error(BIGNUM_TYPE,obj);
445                 return 0;
446         }
447 }
448
449 VM_C_API u64 to_unsigned_8(cell obj, factor_vm *parent)
450 {
451         return parent->to_unsigned_8(obj);
452 }
453  
454 /* Cannot allocate */
455 float factor_vm::to_float(cell value)
456 {
457         return (float)untag_float_check(value);
458 }
459
460 /* Cannot allocate */
461 double factor_vm::to_double(cell value)
462 {
463         return untag_float_check(value);
464 }
465
466 /* The fixnum+, fixnum- and fixnum* primitives are defined in cpu_*.S. On
467 overflow, they call these functions. */
468 inline void factor_vm::overflow_fixnum_add(fixnum x, fixnum y)
469 {
470         ctx->replace(tag<bignum>(fixnum_to_bignum(
471                 untag_fixnum(x) + untag_fixnum(y))));
472 }
473
474 VM_C_API void overflow_fixnum_add(fixnum x, fixnum y, factor_vm *parent)
475 {
476         parent->overflow_fixnum_add(x,y);
477 }
478
479 inline void factor_vm::overflow_fixnum_subtract(fixnum x, fixnum y)
480 {
481         ctx->replace(tag<bignum>(fixnum_to_bignum(
482                 untag_fixnum(x) - untag_fixnum(y))));
483 }
484
485 VM_C_API void overflow_fixnum_subtract(fixnum x, fixnum y, factor_vm *parent)
486 {
487         parent->overflow_fixnum_subtract(x,y);
488 }
489
490 inline void factor_vm::overflow_fixnum_multiply(fixnum x, fixnum y)
491 {
492         bignum *bx = fixnum_to_bignum(x);
493         GC_BIGNUM(bx);
494         bignum *by = fixnum_to_bignum(y);
495         GC_BIGNUM(by);
496         ctx->replace(tag<bignum>(bignum_multiply(bx,by)));
497 }
498
499 VM_C_API void overflow_fixnum_multiply(fixnum x, fixnum y, factor_vm *parent)
500 {
501         parent->overflow_fixnum_multiply(x,y);
502 }
503
504 }