This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[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         method = mono_object_get_virtual_method (obj, method);
47
48         return mono_ldftn (method);
49 }
50
51 static void
52 helper_initobj (void *addr, int size)
53 {
54         MONO_ARCH_SAVE_REGS;
55
56         memset (addr, 0, size);
57 }
58
59 static void
60 helper_memcpy (void *addr, void *src, int size)
61 {
62         MONO_ARCH_SAVE_REGS;
63
64         memcpy (addr, src, size);
65 }
66
67 static void
68 helper_memset (void *addr, int val, int size)
69 {
70         MONO_ARCH_SAVE_REGS;
71
72         memset (addr, val, size);
73 }
74
75 static void
76 helper_stelem_ref (MonoArray *array, int index, MonoObject *val)
77 {
78         MONO_ARCH_SAVE_REGS;
79
80         if (index >= array->max_length)
81                 mono_raise_exception (mono_get_exception_index_out_of_range ());
82
83         if (val && !mono_object_isinst (val, array->obj.vtable->klass->element_class))
84                 mono_raise_exception (mono_get_exception_array_type_mismatch ());
85
86         mono_array_set (array, gpointer, index, val);
87 }
88
89 static void
90 helper_stelem_ref_check (MonoArray *array, MonoObject *val)
91 {
92         MONO_ARCH_SAVE_REGS;
93
94         if (val && !mono_object_isinst (val, array->obj.vtable->klass->element_class))
95                 mono_raise_exception (mono_get_exception_array_type_mismatch ());
96 }
97
98 static gint64 
99 mono_llmult (gint64 a, gint64 b)
100 {
101         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
102         return a * b;
103 }
104
105 static guint64  
106 mono_llmult_ovf_un (guint64 a, guint64 b)
107 {
108         guint32 al = a;
109         guint32 ah = a >> 32;
110         guint32 bl = b;
111         guint32 bh = b >> 32; 
112         guint64 res, t1;
113
114         MONO_ARCH_SAVE_REGS;
115
116         // fixme: this is incredible slow
117
118         if (ah && bh)
119                 goto raise_exception;
120
121         res = (guint64)al * (guint64)bl;
122
123         t1 = (guint64)ah * (guint64)bl + (guint64)al * (guint64)bh;
124
125         if (t1 > 0xffffffff)
126                 goto raise_exception;
127
128         res += ((guint64)t1) << 32; 
129
130         return res;
131
132  raise_exception:
133         mono_raise_exception (mono_get_exception_overflow ());
134         return 0;
135 }
136
137
138 static guint64  
139 mono_llmult_ovf (gint64 a, gint64 b) 
140 {
141         guint32 al = a;
142         gint32 ah = a >> 32;
143         guint32 bl = b;
144         gint32 bh = b >> 32; 
145         /*
146         Use Karatsuba algorithm where:
147                 a*b is: AhBh(R^2+R)+(Ah-Al)(Bl-Bh)R+AlBl(R+1)
148                 where Ah is the "high half" (most significant 32 bits) of a and
149                 where Al is the "low half" (least significant 32 bits) of a and
150                 where  Bh is the "high half" of b and Bl is the "low half" and
151                 where R is the Radix or "size of the half" (in our case 32 bits)
152
153         Note, for the product of two 64 bit numbers to fit into a 64
154         result, ah and/or bh must be 0.  This will save us from doing
155         the AhBh term at all.
156
157         Also note that we refactor so that we don't overflow 64 bits with 
158         intermediate results. So we use [(Ah-Al)(Bl-Bh)+AlBl]R+AlBl
159         */
160
161         gint64 res, t1;
162         gint32 sign;
163
164         MONO_ARCH_SAVE_REGS;
165
166         /* need to work with absoulte values, so find out what the
167            resulting sign will be and convert any negative numbers
168            from two's complement
169         */
170         sign = ah ^ bh;
171         if (ah < 0) {
172                 if (((guint32)ah == 0x80000000) && (al == 0)) {
173                         /* This has no two's complement */
174                         if (b == 0)
175                                 return 0;
176                         else if (b == 1)
177                                 return a;
178                         else
179                                 goto raise_exception;
180                 }
181
182                 /* flip the bits and add 1 */
183                 ah ^= ~0;
184                 if (al ==  0)
185                         ah += 1;
186                 else {
187                         al ^= ~0;
188                         al +=1;
189                 }
190         }
191
192         if (bh < 0) {
193                 if (((guint32)bh == 0x80000000) && (bl == 0)) {
194                         /* This has no two's complement */
195                         if (a == 0)
196                                 return 0;
197                         else if (a == 1)
198                                 return b;
199                         else
200                                 goto raise_exception;
201                 }
202
203                 /* flip the bits and add 1 */
204                 bh ^= ~0;
205                 if (bl ==  0)
206                         bh += 1;
207                 else {
208                         bl ^= ~0;
209                         bl +=1;
210                 }
211         }
212                 
213         /* we overflow for sure if both upper halves are greater 
214            than zero because we would need to shift their 
215            product 64 bits to the left and that will not fit
216            in a 64 bit result */
217         if (ah && bh)
218                 goto raise_exception;
219
220         /* do the AlBl term first */
221         t1 = (gint64)al * (gint64)bl;
222
223         res = t1;
224
225         /* now do the [(Ah-Al)(Bl-Bh)+AlBl]R term */
226         t1 += (gint64)(ah - al) * (gint64)(bl - bh);
227         t1 <<= 32;
228         /* check for overflow */
229         if (t1 > (0x7FFFFFFFFFFFFFFFLL - res))
230                 goto raise_exception;
231
232         res += t1;
233
234         if (res < 0)
235                 goto raise_exception;
236
237         if (sign < 0)
238                 return -res;
239         else
240                 return res;
241
242  raise_exception:
243         mono_raise_exception (mono_get_exception_overflow ());
244         return 0;
245 }
246
247 static gint64 
248 mono_lldiv (gint64 a, gint64 b)
249 {
250         MONO_ARCH_SAVE_REGS;
251
252 #ifdef MONO_ARCH_NEED_DIV_CHECK
253         if (!b)
254                 mono_raise_exception (mono_get_exception_divide_by_zero ());
255         else if (b == -1 && a == (-9223372036854775807LL - 1LL))
256                 mono_raise_exception (mono_get_exception_arithmetic ());
257 #endif
258         return a / b;
259 }
260
261 static gint64 
262 mono_llrem (gint64 a, gint64 b)
263 {
264         MONO_ARCH_SAVE_REGS;
265
266 #ifdef MONO_ARCH_NEED_DIV_CHECK
267         if (!b)
268                 mono_raise_exception (mono_get_exception_divide_by_zero ());
269         else if (b == -1 && a == (-9223372036854775807LL - 1LL))
270                 mono_raise_exception (mono_get_exception_arithmetic ());
271 #endif
272         return a % b;
273 }
274
275 static guint64 
276 mono_lldiv_un (guint64 a, guint64 b)
277 {
278         MONO_ARCH_SAVE_REGS;
279
280 #ifdef MONO_ARCH_NEED_DIV_CHECK
281         if (!b)
282                 mono_raise_exception (mono_get_exception_divide_by_zero ());
283 #endif
284         return a / b;
285 }
286
287 static guint64 
288 mono_llrem_un (guint64 a, guint64 b)
289 {
290         MONO_ARCH_SAVE_REGS;
291
292 #ifdef MONO_ARCH_NEED_DIV_CHECK
293         if (!b)
294                 mono_raise_exception (mono_get_exception_divide_by_zero ());
295 #endif
296         return a % b;
297 }
298
299 #ifndef MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS
300
301 static guint64 
302 mono_lshl (guint64 a, gint32 shamt)
303 {
304         guint64 res;
305
306         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
307         res = a << shamt;
308
309         /*printf ("TESTL %lld << %d = %lld\n", a, shamt, res);*/
310
311         return res;
312 }
313
314 static guint64 
315 mono_lshr_un (guint64 a, gint32 shamt)
316 {
317         guint64 res;
318
319         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
320         res = a >> shamt;
321
322         /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/
323
324         return res;
325 }
326
327 static gint64 
328 mono_lshr (gint64 a, gint32 shamt)
329 {
330         gint64 res;
331
332         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
333         res = a >> shamt;
334
335         /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/
336
337         return res;
338 }
339
340 #endif
341
342 /**
343  * ves_array_element_address:
344  * @this: a pointer to the array object
345  *
346  * Returns: the address of an array element.
347  */
348 static gpointer 
349 ves_array_element_address (MonoArray *this, ...)
350 {
351         MonoClass *class;
352         va_list ap;
353         int i, ind, esize, realidx;
354         gpointer ea;
355
356         MONO_ARCH_SAVE_REGS;
357
358         g_assert (this != NULL);
359
360         va_start(ap, this);
361
362         class = this->obj.vtable->klass;
363
364         g_assert (this->bounds != NULL);
365
366         esize = mono_array_element_size (class);
367         ind = va_arg(ap, int);
368         ind -= (int)this->bounds [0].lower_bound;
369         if ((guint32)ind >= (guint32)this->bounds [0].length)
370                 mono_raise_exception (mono_get_exception_index_out_of_range ());
371         for (i = 1; i < class->rank; i++) {
372                 realidx = va_arg(ap, int) - (int)this->bounds [i].lower_bound;
373                 if ((guint32)realidx >= (guint32)this->bounds [i].length)
374                         mono_raise_exception (mono_get_exception_index_out_of_range ());
375                 ind *= this->bounds [i].length;
376                 ind += realidx;
377         }
378         esize *= ind;
379
380         ea = (gpointer*)((char*)this->vector + esize);
381
382         va_end(ap);
383
384         return ea;
385 }
386
387 static MonoArray *
388 mono_array_new_va (MonoMethod *cm, ...)
389 {
390         MonoDomain *domain = mono_domain_get ();
391         va_list ap;
392         guint32 *lengths;
393         guint32 *lower_bounds;
394         int pcount;
395         int rank;
396         int i, d;
397
398         MONO_ARCH_SAVE_REGS;
399
400         pcount = cm->signature->param_count;
401         rank = cm->klass->rank;
402
403         va_start (ap, cm);
404         
405         lengths = alloca (sizeof (guint32) * pcount);
406         for (i = 0; i < pcount; ++i)
407                 lengths [i] = d = va_arg(ap, int);
408
409         if (rank == pcount) {
410                 /* Only lengths provided. */
411                 lower_bounds = NULL;
412         } else {
413                 g_assert (pcount == (rank * 2));
414                 /* lower bounds are first. */
415                 lower_bounds = lengths;
416                 lengths += rank;
417         }
418         va_end(ap);
419
420         return mono_array_new_full (domain, cm->klass, lengths, lower_bounds);
421 }
422
423 static gpointer
424 mono_class_static_field_address (MonoDomain *domain, MonoClassField *field)
425 {
426         MonoVTable *vtable;
427         gpointer addr;
428         
429         MONO_ARCH_SAVE_REGS;
430
431         //printf ("SFLDA0 %s.%s::%s %d\n", field->parent->name_space, field->parent->name, field->name, field->offset, field->parent->inited);
432
433         mono_class_init (field->parent);
434
435         vtable = mono_class_vtable (domain, field->parent);
436         if (!vtable->initialized)
437                 mono_runtime_class_init (vtable);
438
439         //printf ("SFLDA1 %p\n", (char*)vtable->data + field->offset);
440
441         if (domain->special_static_fields && (addr = g_hash_table_lookup (domain->special_static_fields, field)))
442                 addr = mono_get_special_static_data (GPOINTER_TO_UINT (addr));
443         else
444                 addr = (char*)vtable->data + field->offset;
445         
446         return addr;
447 }
448
449 static gpointer
450 mono_ldtoken_wrapper (MonoImage *image, int token)
451 {
452         MonoClass *handle_class;
453         gpointer res;
454
455         MONO_ARCH_SAVE_REGS;
456         res = mono_ldtoken (image, token, &handle_class, NULL); 
457         mono_class_init (handle_class);
458
459         return res;
460 }
461
462 static guint64
463 mono_fconv_u8 (double v)
464 {
465         return (guint64)v;
466 }
467
468 #ifdef MONO_ARCH_EMULATE_FCONV_TO_I8
469 static gint64
470 mono_fconv_i8 (double v)
471 {
472         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
473         return (gint64)v;
474 }
475 #endif
476
477 static guint32
478 mono_fconv_u4 (double v)
479 {
480         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
481         return (guint32)v;
482 }
483
484 #ifndef HAVE_TRUNC
485 /* Solaris doesn't have trunc */
486 #ifdef HAVE_AINTL
487 extern long double aintl (long double);
488 #define trunc aintl
489 #else
490 /* FIXME: This means we will never throw overflow exceptions */
491 #define trunc
492 #endif
493 #endif /* HAVE_TRUNC */
494
495 static gint64
496 mono_fconv_ovf_i8 (double v)
497 {
498         gint64 res;
499
500         MONO_ARCH_SAVE_REGS;
501
502         res = (gint64)v;
503
504         if (isnan(v) || trunc (v) != res) {
505                 mono_raise_exception (mono_get_exception_overflow ());
506         }
507         return res;
508 }
509
510 static guint64
511 mono_fconv_ovf_u8 (double v)
512 {
513         guint64 res;
514
515         MONO_ARCH_SAVE_REGS;
516     
517         res = (guint64)v;
518
519         if (isnan(v) || trunc (v) != res) {
520                 mono_raise_exception (mono_get_exception_overflow ());
521         }
522         return res;
523 }
524
525 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R8
526 static double
527 mono_lconv_to_r8 (gint64 a)
528 {
529         return (double)a;
530 }
531 #endif
532
533 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R4
534 static float
535 mono_lconv_to_r4 (gint64 a)
536 {
537         return (float)a;
538 }
539 #endif
540
541 #ifdef MONO_ARCH_EMULATE_CONV_R8_UN
542 static double
543 mono_conv_to_r8_un (guint32 a)
544 {
545         return (double)a;
546 }
547 #endif
548
549 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R8_UN
550 static double
551 mono_lconv_to_r8_un (guint64 a)
552 {
553         return (double)a;
554 }
555 #endif
556