2005-03-29 Zoltan Varga <vargaz@freemail.hu>
[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 static gint64 
230 mono_lldiv (gint64 a, gint64 b)
231 {
232         MONO_ARCH_SAVE_REGS;
233
234 #ifdef MONO_ARCH_NEED_DIV_CHECK
235         if (!b)
236                 mono_raise_exception (mono_get_exception_divide_by_zero ());
237         else if (b == -1 && a == (-9223372036854775807LL - 1LL))
238                 mono_raise_exception (mono_get_exception_arithmetic ());
239 #endif
240         return a / b;
241 }
242
243 static gint64 
244 mono_llrem (gint64 a, gint64 b)
245 {
246         MONO_ARCH_SAVE_REGS;
247
248 #ifdef MONO_ARCH_NEED_DIV_CHECK
249         if (!b)
250                 mono_raise_exception (mono_get_exception_divide_by_zero ());
251         else if (b == -1 && a == (-9223372036854775807LL - 1LL))
252                 mono_raise_exception (mono_get_exception_arithmetic ());
253 #endif
254         return a % b;
255 }
256
257 static guint64 
258 mono_lldiv_un (guint64 a, guint64 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 #endif
266         return a / b;
267 }
268
269 static guint64 
270 mono_llrem_un (guint64 a, guint64 b)
271 {
272         MONO_ARCH_SAVE_REGS;
273
274 #ifdef MONO_ARCH_NEED_DIV_CHECK
275         if (!b)
276                 mono_raise_exception (mono_get_exception_divide_by_zero ());
277 #endif
278         return a % b;
279 }
280
281 #endif
282
283 #ifndef MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS
284
285 static guint64 
286 mono_lshl (guint64 a, gint32 shamt)
287 {
288         guint64 res;
289
290         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
291         res = a << shamt;
292
293         /*printf ("TESTL %lld << %d = %lld\n", a, shamt, res);*/
294
295         return res;
296 }
297
298 static guint64 
299 mono_lshr_un (guint64 a, gint32 shamt)
300 {
301         guint64 res;
302
303         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
304         res = a >> shamt;
305
306         /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/
307
308         return res;
309 }
310
311 static gint64 
312 mono_lshr (gint64 a, gint32 shamt)
313 {
314         gint64 res;
315
316         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
317         res = a >> shamt;
318
319         /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/
320
321         return res;
322 }
323
324 #endif
325
326 /**
327  * ves_array_element_address:
328  * @this: a pointer to the array object
329  *
330  * Returns: the address of an array element.
331  */
332 static gpointer 
333 ves_array_element_address (MonoArray *this, ...)
334 {
335         MonoClass *class;
336         va_list ap;
337         int i, ind, esize, realidx;
338         gpointer ea;
339
340         MONO_ARCH_SAVE_REGS;
341
342         g_assert (this != NULL);
343
344         va_start(ap, this);
345
346         class = this->obj.vtable->klass;
347
348         g_assert (this->bounds != NULL);
349
350         esize = mono_array_element_size (class);
351         ind = va_arg(ap, int);
352         ind -= (int)this->bounds [0].lower_bound;
353         if ((guint32)ind >= (guint32)this->bounds [0].length)
354                 mono_raise_exception (mono_get_exception_index_out_of_range ());
355         for (i = 1; i < class->rank; i++) {
356                 realidx = va_arg(ap, int) - (int)this->bounds [i].lower_bound;
357                 if ((guint32)realidx >= (guint32)this->bounds [i].length)
358                         mono_raise_exception (mono_get_exception_index_out_of_range ());
359                 ind *= this->bounds [i].length;
360                 ind += realidx;
361         }
362         esize *= ind;
363
364         ea = (gpointer*)((char*)this->vector + esize);
365
366         va_end(ap);
367
368         return ea;
369 }
370
371 static MonoArray *
372 mono_array_new_va (MonoMethod *cm, ...)
373 {
374         MonoDomain *domain = mono_domain_get ();
375         va_list ap;
376         guint32 *lengths;
377         guint32 *lower_bounds;
378         int pcount;
379         int rank;
380         int i, d;
381
382         MONO_ARCH_SAVE_REGS;
383
384         pcount = mono_method_signature (cm)->param_count;
385         rank = cm->klass->rank;
386
387         va_start (ap, cm);
388         
389         lengths = alloca (sizeof (guint32) * pcount);
390         for (i = 0; i < pcount; ++i)
391                 lengths [i] = d = va_arg(ap, int);
392
393         if (rank == pcount) {
394                 /* Only lengths provided. */
395                 lower_bounds = NULL;
396         } else {
397                 g_assert (pcount == (rank * 2));
398                 /* lower bounds are first. */
399                 lower_bounds = lengths;
400                 lengths += rank;
401         }
402         va_end(ap);
403
404         return mono_array_new_full (domain, cm->klass, lengths, lower_bounds);
405 }
406
407 static gpointer
408 mono_class_static_field_address (MonoDomain *domain, MonoClassField *field)
409 {
410         MonoVTable *vtable;
411         gpointer addr;
412         
413         MONO_ARCH_SAVE_REGS;
414
415         //printf ("SFLDA0 %s.%s::%s %d\n", field->parent->name_space, field->parent->name, field->name, field->offset, field->parent->inited);
416
417         mono_class_init (field->parent);
418
419         vtable = mono_class_vtable (domain, field->parent);
420         if (!vtable->initialized)
421                 mono_runtime_class_init (vtable);
422
423         //printf ("SFLDA1 %p\n", (char*)vtable->data + field->offset);
424
425         if (domain->special_static_fields && (addr = g_hash_table_lookup (domain->special_static_fields, field)))
426                 addr = mono_get_special_static_data (GPOINTER_TO_UINT (addr));
427         else
428                 addr = (char*)vtable->data + field->offset;
429         
430         return addr;
431 }
432
433 static gpointer
434 mono_ldtoken_wrapper (MonoImage *image, int token, MonoGenericContext *context)
435 {
436         MonoClass *handle_class;
437         gpointer res;
438
439         MONO_ARCH_SAVE_REGS;
440         res = mono_ldtoken (image, token, &handle_class, context);      
441         mono_class_init (handle_class);
442
443         return res;
444 }
445
446 static guint64
447 mono_fconv_u8 (double v)
448 {
449         return (guint64)v;
450 }
451
452 #ifdef MONO_ARCH_EMULATE_FCONV_TO_I8
453 static gint64
454 mono_fconv_i8 (double v)
455 {
456         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
457         return (gint64)v;
458 }
459 #endif
460
461 static guint32
462 mono_fconv_u4 (double v)
463 {
464         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
465         return (guint32)v;
466 }
467
468 #ifndef HAVE_TRUNC
469 /* Solaris doesn't have trunc */
470 #ifdef HAVE_AINTL
471 extern long double aintl (long double);
472 #define trunc aintl
473 #else
474 /* FIXME: This means we will never throw overflow exceptions */
475 #define trunc(v) res
476 #endif
477 #endif /* HAVE_TRUNC */
478
479 static gint64
480 mono_fconv_ovf_i8 (double v)
481 {
482         gint64 res;
483
484         MONO_ARCH_SAVE_REGS;
485
486         res = (gint64)v;
487
488         if (isnan(v) || trunc (v) != res) {
489                 mono_raise_exception (mono_get_exception_overflow ());
490         }
491         return res;
492 }
493
494 static guint64
495 mono_fconv_ovf_u8 (double v)
496 {
497         guint64 res;
498
499         MONO_ARCH_SAVE_REGS;
500     
501         res = (guint64)v;
502
503         if (isnan(v) || trunc (v) != res) {
504                 mono_raise_exception (mono_get_exception_overflow ());
505         }
506         return res;
507 }
508
509 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R8
510 static double
511 mono_lconv_to_r8 (gint64 a)
512 {
513         return (double)a;
514 }
515 #endif
516
517 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R4
518 static float
519 mono_lconv_to_r4 (gint64 a)
520 {
521         return (float)a;
522 }
523 #endif
524
525 #ifdef MONO_ARCH_EMULATE_CONV_R8_UN
526 static double
527 mono_conv_to_r8_un (guint32 a)
528 {
529         return (double)a;
530 }
531 #endif
532
533 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R8_UN
534 static double
535 mono_lconv_to_r8_un (guint64 a)
536 {
537         return (double)a;
538 }
539 #endif
540
541 static gpointer
542 helper_compile_generic_method (MonoObject *obj, MonoMethod *method, MonoGenericContext *context)
543 {
544         MonoMethod *vmethod, *inflated;
545         gpointer addr;
546
547         vmethod = mono_object_get_virtual_method (obj, method);
548         inflated = mono_class_inflate_generic_method (vmethod, context, NULL);
549         inflated = mono_get_inflated_method (inflated);
550         addr = mono_compile_method (inflated);
551
552         return addr;
553 }