2005-07-06 Martin Baulig <martin@ximian.com>
[mono.git] / mono / mini / jit-icalls.c
1 /*
2  * jit-icalls.c: internal calls used by the JIT
3  *
4  * Author:
5  *   Dietmar Maurer (dietmar@ximian.com)
6  *   Paolo Molaro (lupus@ximian.com)
7  *
8  * (C) 2002 Ximian, Inc.
9  */
10
11 #include <math.h>
12
13 static void*
14 mono_ldftn (MonoMethod *method)
15 {
16         gpointer addr;
17
18         MONO_ARCH_SAVE_REGS;
19
20         addr = mono_create_jump_trampoline (mono_domain_get (), method, TRUE);
21
22         return addr;
23 }
24
25 /*
26  * Same as mono_ldftn, but do not add a synchronized wrapper. Used in the
27  * synchronized wrappers to avoid infinite recursion.
28  */
29 static void*
30 mono_ldftn_nosync (MonoMethod *method)
31 {
32         gpointer addr;
33
34         MONO_ARCH_SAVE_REGS;
35
36         addr = mono_create_jump_trampoline (mono_domain_get (), method, FALSE);
37
38         return addr;
39 }
40
41 static void*
42 mono_ldvirtfn (MonoObject *obj, MonoMethod *method) 
43 {
44         MONO_ARCH_SAVE_REGS;
45
46         if (obj == NULL)
47                 mono_raise_exception (mono_get_exception_null_reference ());
48
49         method = mono_object_get_virtual_method (obj, method);
50
51         return mono_ldftn (method);
52 }
53
54 static void
55 helper_stelem_ref (MonoArray *array, int index, MonoObject *val)
56 {
57         MONO_ARCH_SAVE_REGS;
58
59         if (index >= array->max_length)
60                 mono_raise_exception (mono_get_exception_index_out_of_range ());
61
62         if (val && !mono_object_isinst (val, array->obj.vtable->klass->element_class))
63                 mono_raise_exception (mono_get_exception_array_type_mismatch ());
64
65         mono_array_set (array, gpointer, index, val);
66 }
67
68 static void
69 helper_stelem_ref_check (MonoArray *array, MonoObject *val)
70 {
71         MONO_ARCH_SAVE_REGS;
72
73         if (val && !mono_object_isinst (val, array->obj.vtable->klass->element_class))
74                 mono_raise_exception (mono_get_exception_array_type_mismatch ());
75 }
76
77 #ifndef MONO_ARCH_NO_EMULATE_LONG_MUL_OPTS
78
79 static gint64 
80 mono_llmult (gint64 a, gint64 b)
81 {
82         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
83         return a * b;
84 }
85
86 static guint64  
87 mono_llmult_ovf_un (guint64 a, guint64 b)
88 {
89         guint32 al = a;
90         guint32 ah = a >> 32;
91         guint32 bl = b;
92         guint32 bh = b >> 32; 
93         guint64 res, t1;
94
95         MONO_ARCH_SAVE_REGS;
96
97         // fixme: this is incredible slow
98
99         if (ah && bh)
100                 goto raise_exception;
101
102         res = (guint64)al * (guint64)bl;
103
104         t1 = (guint64)ah * (guint64)bl + (guint64)al * (guint64)bh;
105
106         if (t1 > 0xffffffff)
107                 goto raise_exception;
108
109         res += ((guint64)t1) << 32; 
110
111         return res;
112
113  raise_exception:
114         mono_raise_exception (mono_get_exception_overflow ());
115         return 0;
116 }
117
118 static guint64  
119 mono_llmult_ovf (gint64 a, gint64 b) 
120 {
121         guint32 al = a;
122         gint32 ah = a >> 32;
123         guint32 bl = b;
124         gint32 bh = b >> 32; 
125         /*
126         Use Karatsuba algorithm where:
127                 a*b is: AhBh(R^2+R)+(Ah-Al)(Bl-Bh)R+AlBl(R+1)
128                 where Ah is the "high half" (most significant 32 bits) of a and
129                 where Al is the "low half" (least significant 32 bits) of a and
130                 where  Bh is the "high half" of b and Bl is the "low half" and
131                 where R is the Radix or "size of the half" (in our case 32 bits)
132
133         Note, for the product of two 64 bit numbers to fit into a 64
134         result, ah and/or bh must be 0.  This will save us from doing
135         the AhBh term at all.
136
137         Also note that we refactor so that we don't overflow 64 bits with 
138         intermediate results. So we use [(Ah-Al)(Bl-Bh)+AlBl]R+AlBl
139         */
140
141         gint64 res, t1;
142         gint32 sign;
143
144         MONO_ARCH_SAVE_REGS;
145
146         /* need to work with absoulte values, so find out what the
147            resulting sign will be and convert any negative numbers
148            from two's complement
149         */
150         sign = ah ^ bh;
151         if (ah < 0) {
152                 if (((guint32)ah == 0x80000000) && (al == 0)) {
153                         /* This has no two's complement */
154                         if (b == 0)
155                                 return 0;
156                         else if (b == 1)
157                                 return a;
158                         else
159                                 goto raise_exception;
160                 }
161
162                 /* flip the bits and add 1 */
163                 ah ^= ~0;
164                 if (al ==  0)
165                         ah += 1;
166                 else {
167                         al ^= ~0;
168                         al +=1;
169                 }
170         }
171
172         if (bh < 0) {
173                 if (((guint32)bh == 0x80000000) && (bl == 0)) {
174                         /* This has no two's complement */
175                         if (a == 0)
176                                 return 0;
177                         else if (a == 1)
178                                 return b;
179                         else
180                                 goto raise_exception;
181                 }
182
183                 /* flip the bits and add 1 */
184                 bh ^= ~0;
185                 if (bl ==  0)
186                         bh += 1;
187                 else {
188                         bl ^= ~0;
189                         bl +=1;
190                 }
191         }
192                 
193         /* we overflow for sure if both upper halves are greater 
194            than zero because we would need to shift their 
195            product 64 bits to the left and that will not fit
196            in a 64 bit result */
197         if (ah && bh)
198                 goto raise_exception;
199         if ((gint64)((gint64)ah * (gint64)bl) > (gint64)0x80000000 || (gint64)((gint64)al * (gint64)bh) > (gint64)0x80000000)
200                 goto raise_exception;
201
202         /* do the AlBl term first */
203         t1 = (gint64)al * (gint64)bl;
204
205         res = t1;
206
207         /* now do the [(Ah-Al)(Bl-Bh)+AlBl]R term */
208         t1 += (gint64)(ah - al) * (gint64)(bl - bh);
209         /* check for overflow */
210         t1 <<= 32;
211         if (t1 > (0x7FFFFFFFFFFFFFFFLL - res))
212                 goto raise_exception;
213
214         res += t1;
215
216         if (res < 0)
217                 goto raise_exception;
218
219         if (sign < 0)
220                 return -res;
221         else
222                 return res;
223
224  raise_exception:
225         mono_raise_exception (mono_get_exception_overflow ());
226         return 0;
227 }
228
229 #ifdef MONO_ARCH_EMULATE_MUL_DIV
230
231 static gint32
232 mono_imul (gint32 a, gint32 b)
233 {
234         MONO_ARCH_SAVE_REGS;
235
236         return a * b;
237 }
238
239 static gint32
240 mono_idiv (gint32 a, gint32 b)
241 {
242         MONO_ARCH_SAVE_REGS;
243
244 #ifdef MONO_ARCH_NEED_DIV_CHECK
245         if (!b)
246                 mono_raise_exception (mono_get_exception_divide_by_zero ());
247         else if (b == -1 && a == (0x80000000))
248                 mono_raise_exception (mono_get_exception_arithmetic ());
249 #endif
250         return a / b;
251 }
252
253 static guint32
254 mono_idiv_un (guint32 a, guint32 b)
255 {
256         MONO_ARCH_SAVE_REGS;
257
258 #ifdef MONO_ARCH_NEED_DIV_CHECK
259         if (!b)
260                 mono_raise_exception (mono_get_exception_divide_by_zero ());
261 #endif
262         return a / b;
263 }
264
265 static gint32
266 mono_irem (gint32 a, gint32 b)
267 {
268         MONO_ARCH_SAVE_REGS;
269
270 #ifdef MONO_ARCH_NEED_DIV_CHECK
271         if (!b)
272                 mono_raise_exception (mono_get_exception_divide_by_zero ());
273         else if (b == -1 && a == (0x80000000))
274                 mono_raise_exception (mono_get_exception_arithmetic ());
275 #endif
276
277         return a % b;
278 }
279
280 static guint32
281 mono_irem_un (guint32 a, guint32 b)
282 {
283         MONO_ARCH_SAVE_REGS;
284
285 #ifdef MONO_ARCH_NEED_DIV_CHECK
286         if (!b)
287                 mono_raise_exception (mono_get_exception_divide_by_zero ());
288 #endif
289         return a % b;
290 }
291
292 static gint32
293 mono_imul_ovf (gint32 a, gint32 b)
294 {
295         gint64 res;
296
297         MONO_ARCH_SAVE_REGS;
298
299         res = (gint64)a * (gint64)b;
300
301         if ((res > 0x7fffffffL) || (res < -2147483648))
302                 mono_raise_exception (mono_get_exception_overflow ());
303
304         return res;
305 }
306
307 static gint32
308 mono_imul_ovf_un (guint32 a, guint32 b)
309 {
310         guint64 res;
311
312         MONO_ARCH_SAVE_REGS;
313
314         res = (guint64)a * (guint64)b;
315
316         if ((res >> 32))
317                 mono_raise_exception (mono_get_exception_overflow ());
318
319         return res;
320 }
321
322 static double
323 mono_fdiv (double a, double b)
324 {
325         MONO_ARCH_SAVE_REGS;
326
327         return a / b;
328 }
329 #endif
330
331 static gint64 
332 mono_lldiv (gint64 a, gint64 b)
333 {
334         MONO_ARCH_SAVE_REGS;
335
336 #ifdef MONO_ARCH_NEED_DIV_CHECK
337         if (!b)
338                 mono_raise_exception (mono_get_exception_divide_by_zero ());
339         else if (b == -1 && a == (-9223372036854775807LL - 1LL))
340                 mono_raise_exception (mono_get_exception_arithmetic ());
341 #endif
342         return a / b;
343 }
344
345 static gint64 
346 mono_llrem (gint64 a, gint64 b)
347 {
348         MONO_ARCH_SAVE_REGS;
349
350 #ifdef MONO_ARCH_NEED_DIV_CHECK
351         if (!b)
352                 mono_raise_exception (mono_get_exception_divide_by_zero ());
353         else if (b == -1 && a == (-9223372036854775807LL - 1LL))
354                 mono_raise_exception (mono_get_exception_arithmetic ());
355 #endif
356         return a % b;
357 }
358
359 static guint64 
360 mono_lldiv_un (guint64 a, guint64 b)
361 {
362         MONO_ARCH_SAVE_REGS;
363
364 #ifdef MONO_ARCH_NEED_DIV_CHECK
365         if (!b)
366                 mono_raise_exception (mono_get_exception_divide_by_zero ());
367 #endif
368         return a / b;
369 }
370
371 static guint64 
372 mono_llrem_un (guint64 a, guint64 b)
373 {
374         MONO_ARCH_SAVE_REGS;
375
376 #ifdef MONO_ARCH_NEED_DIV_CHECK
377         if (!b)
378                 mono_raise_exception (mono_get_exception_divide_by_zero ());
379 #endif
380         return a % b;
381 }
382
383 #endif
384
385 #ifndef MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS
386
387 static guint64 
388 mono_lshl (guint64 a, gint32 shamt)
389 {
390         guint64 res;
391
392         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
393         res = a << shamt;
394
395         /*printf ("TESTL %lld << %d = %lld\n", a, shamt, res);*/
396
397         return res;
398 }
399
400 static guint64 
401 mono_lshr_un (guint64 a, gint32 shamt)
402 {
403         guint64 res;
404
405         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
406         res = a >> shamt;
407
408         /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/
409
410         return res;
411 }
412
413 static gint64 
414 mono_lshr (gint64 a, gint32 shamt)
415 {
416         gint64 res;
417
418         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
419         res = a >> shamt;
420
421         /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/
422
423         return res;
424 }
425
426 #endif
427
428 /**
429  * ves_array_element_address:
430  * @this: a pointer to the array object
431  *
432  * Returns: the address of an array element.
433  */
434 static gpointer 
435 ves_array_element_address (MonoArray *this, ...)
436 {
437         MonoClass *class;
438         va_list ap;
439         int i, ind, esize, realidx;
440         gpointer ea;
441
442         MONO_ARCH_SAVE_REGS;
443
444         g_assert (this != NULL);
445
446         va_start(ap, this);
447
448         class = this->obj.vtable->klass;
449
450         g_assert (this->bounds != NULL);
451
452         esize = mono_array_element_size (class);
453         ind = va_arg(ap, int);
454         ind -= (int)this->bounds [0].lower_bound;
455         if ((guint32)ind >= (guint32)this->bounds [0].length)
456                 mono_raise_exception (mono_get_exception_index_out_of_range ());
457         for (i = 1; i < class->rank; i++) {
458                 realidx = va_arg(ap, int) - (int)this->bounds [i].lower_bound;
459                 if ((guint32)realidx >= (guint32)this->bounds [i].length)
460                         mono_raise_exception (mono_get_exception_index_out_of_range ());
461                 ind *= this->bounds [i].length;
462                 ind += realidx;
463         }
464         esize *= ind;
465
466         ea = (gpointer*)(gpointer)((char*)this->vector + esize);
467
468         va_end(ap);
469
470         return ea;
471 }
472
473 static MonoArray *
474 mono_array_new_va (MonoMethod *cm, ...)
475 {
476         MonoDomain *domain = mono_domain_get ();
477         va_list ap;
478         guint32 *lengths;
479         guint32 *lower_bounds;
480         int pcount;
481         int rank;
482         int i, d;
483
484         MONO_ARCH_SAVE_REGS;
485
486         pcount = mono_method_signature (cm)->param_count;
487         rank = cm->klass->rank;
488
489         va_start (ap, cm);
490         
491         lengths = alloca (sizeof (guint32) * pcount);
492         for (i = 0; i < pcount; ++i)
493                 lengths [i] = d = va_arg(ap, int);
494
495         if (rank == pcount) {
496                 /* Only lengths provided. */
497                 lower_bounds = NULL;
498         } else {
499                 g_assert (pcount == (rank * 2));
500                 /* lower bounds are first. */
501                 lower_bounds = lengths;
502                 lengths += rank;
503         }
504         va_end(ap);
505
506         return mono_array_new_full (domain, cm->klass, lengths, lower_bounds);
507 }
508
509 static gpointer
510 mono_class_static_field_address (MonoDomain *domain, MonoClassField *field)
511 {
512         MonoVTable *vtable;
513         gpointer addr;
514         
515         MONO_ARCH_SAVE_REGS;
516
517         //printf ("SFLDA0 %s.%s::%s %d\n", field->parent->name_space, field->parent->name, field->name, field->offset, field->parent->inited);
518
519         mono_class_init (field->parent);
520
521         vtable = mono_class_vtable (domain, field->parent);
522         if (!vtable->initialized)
523                 mono_runtime_class_init (vtable);
524
525         //printf ("SFLDA1 %p\n", (char*)vtable->data + field->offset);
526
527         if (domain->special_static_fields && (addr = g_hash_table_lookup (domain->special_static_fields, field)))
528                 addr = mono_get_special_static_data (GPOINTER_TO_UINT (addr));
529         else
530                 addr = (char*)vtable->data + field->offset;
531         
532         return addr;
533 }
534
535 static gpointer
536 mono_ldtoken_wrapper (MonoImage *image, int token, MonoGenericContext *context)
537 {
538         MonoClass *handle_class;
539         gpointer res;
540
541         MONO_ARCH_SAVE_REGS;
542         res = mono_ldtoken (image, token, &handle_class, context);      
543         mono_class_init (handle_class);
544
545         return res;
546 }
547
548 static guint64
549 mono_fconv_u8 (double v)
550 {
551         return (guint64)v;
552 }
553
554 #ifdef MONO_ARCH_EMULATE_FCONV_TO_I8
555 static gint64
556 mono_fconv_i8 (double v)
557 {
558         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
559         return (gint64)v;
560 }
561 #endif
562
563 static guint32
564 mono_fconv_u4 (double v)
565 {
566         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
567         return (guint32)v;
568 }
569
570 #ifndef HAVE_TRUNC
571 /* Solaris doesn't have trunc */
572 #ifdef HAVE_AINTL
573 extern long double aintl (long double);
574 #define trunc aintl
575 #else
576 /* FIXME: This means we will never throw overflow exceptions */
577 #define trunc(v) res
578 #endif
579 #endif /* HAVE_TRUNC */
580
581 static gint64
582 mono_fconv_ovf_i8 (double v)
583 {
584         gint64 res;
585
586         MONO_ARCH_SAVE_REGS;
587
588         res = (gint64)v;
589
590         if (isnan(v) || trunc (v) != res) {
591                 mono_raise_exception (mono_get_exception_overflow ());
592         }
593         return res;
594 }
595
596 static guint64
597 mono_fconv_ovf_u8 (double v)
598 {
599         guint64 res;
600
601         MONO_ARCH_SAVE_REGS;
602     
603         res = (guint64)v;
604
605         if (isnan(v) || trunc (v) != res) {
606                 mono_raise_exception (mono_get_exception_overflow ());
607         }
608         return res;
609 }
610
611 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R8
612 static double
613 mono_lconv_to_r8 (gint64 a)
614 {
615         return (double)a;
616 }
617 #endif
618
619 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R4
620 static float
621 mono_lconv_to_r4 (gint64 a)
622 {
623         return (float)a;
624 }
625 #endif
626
627 #ifdef MONO_ARCH_EMULATE_CONV_R8_UN
628 static double
629 mono_conv_to_r8_un (guint32 a)
630 {
631         return (double)a;
632 }
633 #endif
634
635 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R8_UN
636 static double
637 mono_lconv_to_r8_un (guint64 a)
638 {
639         return (double)a;
640 }
641 #endif
642
643 static gpointer
644 helper_compile_generic_method (MonoObject *obj, MonoMethod *method, MonoGenericContext *context)
645 {
646         MonoMethod *vmethod, *inflated;
647         gpointer addr;
648
649         vmethod = mono_object_get_virtual_method (obj, method);
650         inflated = mono_class_inflate_generic_method (vmethod, context);
651         inflated = mono_get_inflated_method (inflated);
652         addr = mono_compile_method (inflated);
653
654         return addr;
655 }