In .:
[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 mono_create_ftnptr (mono_domain_get (), 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 mono_create_ftnptr (mono_domain_get (), 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 #if defined(MONO_ARCH_EMULATE_MUL_DIV) || defined(MONO_ARCH_EMULATE_DIV)
230
231 static gint32
232 mono_idiv (gint32 a, gint32 b)
233 {
234         MONO_ARCH_SAVE_REGS;
235
236 #ifdef MONO_ARCH_NEED_DIV_CHECK
237         if (!b)
238                 mono_raise_exception (mono_get_exception_divide_by_zero ());
239         else if (b == -1 && a == (0x80000000))
240                 mono_raise_exception (mono_get_exception_arithmetic ());
241 #endif
242         return a / b;
243 }
244
245 static guint32
246 mono_idiv_un (guint32 a, guint32 b)
247 {
248         MONO_ARCH_SAVE_REGS;
249
250 #ifdef MONO_ARCH_NEED_DIV_CHECK
251         if (!b)
252                 mono_raise_exception (mono_get_exception_divide_by_zero ());
253 #endif
254         return a / b;
255 }
256
257 static gint32
258 mono_irem (gint32 a, gint32 b)
259 {
260         MONO_ARCH_SAVE_REGS;
261
262 #ifdef MONO_ARCH_NEED_DIV_CHECK
263         if (!b)
264                 mono_raise_exception (mono_get_exception_divide_by_zero ());
265         else if (b == -1 && a == (0x80000000))
266                 mono_raise_exception (mono_get_exception_arithmetic ());
267 #endif
268
269         return a % b;
270 }
271
272 static guint32
273 mono_irem_un (guint32 a, guint32 b)
274 {
275         MONO_ARCH_SAVE_REGS;
276
277 #ifdef MONO_ARCH_NEED_DIV_CHECK
278         if (!b)
279                 mono_raise_exception (mono_get_exception_divide_by_zero ());
280 #endif
281         return a % b;
282 }
283
284 #endif
285
286 #ifdef MONO_ARCH_EMULATE_MUL_DIV
287
288 static gint32
289 mono_imul (gint32 a, gint32 b)
290 {
291         MONO_ARCH_SAVE_REGS;
292
293         return a * b;
294 }
295
296 static gint32
297 mono_imul_ovf (gint32 a, gint32 b)
298 {
299         gint64 res;
300
301         MONO_ARCH_SAVE_REGS;
302
303         res = (gint64)a * (gint64)b;
304
305         if ((res > 0x7fffffffL) || (res < -2147483648))
306                 mono_raise_exception (mono_get_exception_overflow ());
307
308         return res;
309 }
310
311 static gint32
312 mono_imul_ovf_un (guint32 a, guint32 b)
313 {
314         guint64 res;
315
316         MONO_ARCH_SAVE_REGS;
317
318         res = (guint64)a * (guint64)b;
319
320         if ((res >> 32))
321                 mono_raise_exception (mono_get_exception_overflow ());
322
323         return res;
324 }
325
326 static double
327 mono_fdiv (double a, double b)
328 {
329         MONO_ARCH_SAVE_REGS;
330
331         return a / b;
332 }
333 #endif
334
335 static gint64 
336 mono_lldiv (gint64 a, gint64 b)
337 {
338         MONO_ARCH_SAVE_REGS;
339
340 #ifdef MONO_ARCH_NEED_DIV_CHECK
341         if (!b)
342                 mono_raise_exception (mono_get_exception_divide_by_zero ());
343         else if (b == -1 && a == (-9223372036854775807LL - 1LL))
344                 mono_raise_exception (mono_get_exception_arithmetic ());
345 #endif
346         return a / b;
347 }
348
349 static gint64 
350 mono_llrem (gint64 a, gint64 b)
351 {
352         MONO_ARCH_SAVE_REGS;
353
354 #ifdef MONO_ARCH_NEED_DIV_CHECK
355         if (!b)
356                 mono_raise_exception (mono_get_exception_divide_by_zero ());
357         else if (b == -1 && a == (-9223372036854775807LL - 1LL))
358                 mono_raise_exception (mono_get_exception_arithmetic ());
359 #endif
360         return a % b;
361 }
362
363 static guint64 
364 mono_lldiv_un (guint64 a, guint64 b)
365 {
366         MONO_ARCH_SAVE_REGS;
367
368 #ifdef MONO_ARCH_NEED_DIV_CHECK
369         if (!b)
370                 mono_raise_exception (mono_get_exception_divide_by_zero ());
371 #endif
372         return a / b;
373 }
374
375 static guint64 
376 mono_llrem_un (guint64 a, guint64 b)
377 {
378         MONO_ARCH_SAVE_REGS;
379
380 #ifdef MONO_ARCH_NEED_DIV_CHECK
381         if (!b)
382                 mono_raise_exception (mono_get_exception_divide_by_zero ());
383 #endif
384         return a % b;
385 }
386
387 #endif
388
389 #ifndef MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS
390
391 static guint64 
392 mono_lshl (guint64 a, gint32 shamt)
393 {
394         guint64 res;
395
396         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
397         res = a << shamt;
398
399         /*printf ("TESTL %lld << %d = %lld\n", a, shamt, res);*/
400
401         return res;
402 }
403
404 static guint64 
405 mono_lshr_un (guint64 a, gint32 shamt)
406 {
407         guint64 res;
408
409         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
410         res = a >> shamt;
411
412         /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/
413
414         return res;
415 }
416
417 static gint64 
418 mono_lshr (gint64 a, gint32 shamt)
419 {
420         gint64 res;
421
422         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
423         res = a >> shamt;
424
425         /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/
426
427         return res;
428 }
429
430 #endif
431
432 /**
433  * ves_array_element_address:
434  * @this: a pointer to the array object
435  *
436  * Returns: the address of an array element.
437  */
438 static gpointer 
439 ves_array_element_address (MonoArray *this, ...)
440 {
441         MonoClass *class;
442         va_list ap;
443         int i, ind, esize, realidx;
444         gpointer ea;
445
446         MONO_ARCH_SAVE_REGS;
447
448         g_assert (this != NULL);
449
450         va_start(ap, this);
451
452         class = this->obj.vtable->klass;
453
454         g_assert (this->bounds != NULL);
455
456         esize = mono_array_element_size (class);
457         ind = va_arg(ap, int);
458         ind -= (int)this->bounds [0].lower_bound;
459         if ((guint32)ind >= (guint32)this->bounds [0].length)
460                 mono_raise_exception (mono_get_exception_index_out_of_range ());
461         for (i = 1; i < class->rank; i++) {
462                 realidx = va_arg(ap, int) - (int)this->bounds [i].lower_bound;
463                 if ((guint32)realidx >= (guint32)this->bounds [i].length)
464                         mono_raise_exception (mono_get_exception_index_out_of_range ());
465                 ind *= this->bounds [i].length;
466                 ind += realidx;
467         }
468         esize *= ind;
469
470         ea = (gpointer*)(gpointer)((char*)this->vector + esize);
471
472         va_end(ap);
473
474         return ea;
475 }
476
477 static MonoArray *
478 mono_array_new_va (MonoMethod *cm, ...)
479 {
480         MonoDomain *domain = mono_domain_get ();
481         va_list ap;
482         guint32 *lengths;
483         guint32 *lower_bounds;
484         int pcount;
485         int rank;
486         int i, d;
487
488         MONO_ARCH_SAVE_REGS;
489
490         pcount = mono_method_signature (cm)->param_count;
491         rank = cm->klass->rank;
492
493         va_start (ap, cm);
494         
495         lengths = alloca (sizeof (guint32) * pcount);
496         for (i = 0; i < pcount; ++i)
497                 lengths [i] = d = va_arg(ap, int);
498
499         if (rank == pcount) {
500                 /* Only lengths provided. */
501                 lower_bounds = NULL;
502         } else {
503                 g_assert (pcount == (rank * 2));
504                 /* lower bounds are first. */
505                 lower_bounds = lengths;
506                 lengths += rank;
507         }
508         va_end(ap);
509
510         return mono_array_new_full (domain, cm->klass, lengths, lower_bounds);
511 }
512
513 static gpointer
514 mono_class_static_field_address (MonoDomain *domain, MonoClassField *field)
515 {
516         MonoVTable *vtable;
517         gpointer addr;
518         
519         MONO_ARCH_SAVE_REGS;
520
521         //printf ("SFLDA0 %s.%s::%s %d\n", field->parent->name_space, field->parent->name, field->name, field->offset, field->parent->inited);
522
523         mono_class_init (field->parent);
524
525         vtable = mono_class_vtable (domain, field->parent);
526         if (!vtable->initialized)
527                 mono_runtime_class_init (vtable);
528
529         //printf ("SFLDA1 %p\n", (char*)vtable->data + field->offset);
530
531         if (domain->special_static_fields && (addr = g_hash_table_lookup (domain->special_static_fields, field)))
532                 addr = mono_get_special_static_data (GPOINTER_TO_UINT (addr));
533         else
534                 addr = (char*)vtable->data + field->offset;
535         
536         return addr;
537 }
538
539 static gpointer
540 mono_ldtoken_wrapper (MonoImage *image, int token, MonoGenericContext *context)
541 {
542         MonoClass *handle_class;
543         gpointer res;
544
545         MONO_ARCH_SAVE_REGS;
546         res = mono_ldtoken (image, token, &handle_class, context);      
547         mono_class_init (handle_class);
548
549         return res;
550 }
551
552 static guint64
553 mono_fconv_u8 (double v)
554 {
555         return (guint64)v;
556 }
557
558 #ifdef MONO_ARCH_EMULATE_FCONV_TO_I8
559 static gint64
560 mono_fconv_i8 (double v)
561 {
562         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
563         return (gint64)v;
564 }
565 #endif
566
567 static guint32
568 mono_fconv_u4 (double v)
569 {
570         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
571         return (guint32)v;
572 }
573
574 #ifndef HAVE_TRUNC
575 /* Solaris doesn't have trunc */
576 #ifdef HAVE_AINTL
577 extern long double aintl (long double);
578 #define trunc aintl
579 #else
580 /* FIXME: This means we will never throw overflow exceptions */
581 #define trunc(v) res
582 #endif
583 #endif /* HAVE_TRUNC */
584
585 static gint64
586 mono_fconv_ovf_i8 (double v)
587 {
588         gint64 res;
589
590         MONO_ARCH_SAVE_REGS;
591
592         res = (gint64)v;
593
594         if (isnan(v) || trunc (v) != res) {
595                 mono_raise_exception (mono_get_exception_overflow ());
596         }
597         return res;
598 }
599
600 static guint64
601 mono_fconv_ovf_u8 (double v)
602 {
603         guint64 res;
604
605         MONO_ARCH_SAVE_REGS;
606     
607         res = (guint64)v;
608
609         if (isnan(v) || trunc (v) != res) {
610                 mono_raise_exception (mono_get_exception_overflow ());
611         }
612         return res;
613 }
614
615 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R8
616 static double
617 mono_lconv_to_r8 (gint64 a)
618 {
619         return (double)a;
620 }
621 #endif
622
623 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R4
624 static float
625 mono_lconv_to_r4 (gint64 a)
626 {
627         return (float)a;
628 }
629 #endif
630
631 #ifdef MONO_ARCH_EMULATE_CONV_R8_UN
632 static double
633 mono_conv_to_r8_un (guint32 a)
634 {
635         return (double)a;
636 }
637 #endif
638
639 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R8_UN
640 static double
641 mono_lconv_to_r8_un (guint64 a)
642 {
643         return (double)a;
644 }
645 #endif
646
647 static gpointer
648 helper_compile_generic_method (MonoObject *obj, MonoMethod *method, MonoGenericContext *context)
649 {
650         MonoMethod *vmethod, *inflated;
651         gpointer addr;
652
653         vmethod = mono_object_get_virtual_method (obj, method);
654         inflated = mono_class_inflate_generic_method (vmethod, context);
655         inflated = mono_get_inflated_method (inflated);
656         addr = mono_compile_method (inflated);
657
658         return addr;
659 }
660
661 static MonoString*
662 helper_ldstr (MonoImage *image, guint32 idx)
663 {
664         return mono_ldstr (mono_domain_get (), image, idx);
665 }