2004-05-17 Radek Doulik <rodo@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         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                 /* flip the bits and add 1 */
173                 ah ^= ~0;
174                 if (al ==  0)
175                         ah += 1;
176                 else {
177                         al ^= ~0;
178                         al +=1;
179                 }
180         }
181
182         if (bh < 0) {
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
200         /* do the AlBl term first */
201         t1 = (gint64)al * (gint64)bl;
202
203         res = t1;
204
205         /* now do the [(Ah-Al)(Bl-Bh)+AlBl]R term */
206         t1 += (gint64)(ah - al) * (gint64)(bl - bh);
207         t1 <<= 32;
208         /* check for overflow */
209         if (t1 > (0x7FFFFFFFFFFFFFFFLL - res))
210                 goto raise_exception;
211
212         res += t1;
213
214         if (res < 0)
215                 goto raise_exception;
216
217         if (sign < 0)
218                 return -res;
219         else
220                 return res;
221
222  raise_exception:
223         mono_raise_exception (mono_get_exception_overflow ());
224         return 0;
225 }
226
227 static gint64 
228 mono_lldiv (gint64 a, gint64 b)
229 {
230         MONO_ARCH_SAVE_REGS;
231
232         return a / b;
233 }
234
235 static gint64 
236 mono_llrem (gint64 a, gint64 b)
237 {
238         MONO_ARCH_SAVE_REGS;
239
240         return a % b;
241 }
242
243 static guint64 
244 mono_lldiv_un (guint64 a, guint64 b)
245 {
246         MONO_ARCH_SAVE_REGS;
247
248         return a / b;
249 }
250
251 static guint64 
252 mono_llrem_un (guint64 a, guint64 b)
253 {
254         MONO_ARCH_SAVE_REGS;
255
256         return a % b;
257 }
258
259 #ifndef MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS
260
261 static guint64 
262 mono_lshl (guint64 a, gint32 shamt)
263 {
264         guint64 res;
265
266         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
267         res = a << shamt;
268
269         /*printf ("TESTL %lld << %d = %lld\n", a, shamt, res);*/
270
271         return res;
272 }
273
274 static guint64 
275 mono_lshr_un (guint64 a, gint32 shamt)
276 {
277         guint64 res;
278
279         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
280         res = a >> shamt;
281
282         /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/
283
284         return res;
285 }
286
287 static gint64 
288 mono_lshr (gint64 a, gint32 shamt)
289 {
290         gint64 res;
291
292         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
293         res = a >> shamt;
294
295         /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/
296
297         return res;
298 }
299
300 #endif
301
302 /**
303  * ves_array_element_address:
304  * @this: a pointer to the array object
305  *
306  * Returns: the address of an array element.
307  */
308 static gpointer 
309 ves_array_element_address (MonoArray *this, ...)
310 {
311         MonoClass *class;
312         va_list ap;
313         int i, ind, esize, realidx;
314         gpointer ea;
315
316         MONO_ARCH_SAVE_REGS;
317
318         g_assert (this != NULL);
319
320         va_start(ap, this);
321
322         class = this->obj.vtable->klass;
323
324         g_assert (this->bounds != NULL);
325
326         esize = mono_array_element_size (class);
327         ind = va_arg(ap, int);
328         ind -= (int)this->bounds [0].lower_bound;
329         if ((guint32)ind >= (guint32)this->bounds [0].length)
330                 mono_raise_exception (mono_get_exception_index_out_of_range ());
331         for (i = 1; i < class->rank; i++) {
332                 realidx = va_arg(ap, int) - (int)this->bounds [i].lower_bound;
333                 if ((guint32)realidx >= (guint32)this->bounds [i].length)
334                         mono_raise_exception (mono_get_exception_index_out_of_range ());
335                 ind *= this->bounds [i].length;
336                 ind += realidx;
337         }
338         esize *= ind;
339
340         ea = (gpointer*)((char*)this->vector + esize);
341
342         va_end(ap);
343
344         return ea;
345 }
346
347 static MonoArray *
348 mono_array_new_va (MonoMethod *cm, ...)
349 {
350         MonoDomain *domain = mono_domain_get ();
351         va_list ap;
352         guint32 *lengths;
353         guint32 *lower_bounds;
354         int pcount;
355         int rank;
356         int i, d;
357
358         MONO_ARCH_SAVE_REGS;
359
360         pcount = cm->signature->param_count;
361         rank = cm->klass->rank;
362
363         va_start (ap, cm);
364         
365         lengths = alloca (sizeof (guint32) * pcount);
366         for (i = 0; i < pcount; ++i)
367                 lengths [i] = d = va_arg(ap, int);
368
369         if (rank == pcount) {
370                 /* Only lengths provided. */
371                 lower_bounds = NULL;
372         } else {
373                 g_assert (pcount == (rank * 2));
374                 /* lower bounds are first. */
375                 lower_bounds = lengths;
376                 lengths += rank;
377         }
378         va_end(ap);
379
380         return mono_array_new_full (domain, cm->klass, lengths, lower_bounds);
381 }
382
383 static gpointer
384 mono_class_static_field_address (MonoDomain *domain, MonoClassField *field)
385 {
386         MonoVTable *vtable;
387         gpointer addr;
388         
389         MONO_ARCH_SAVE_REGS;
390
391         //printf ("SFLDA0 %s.%s::%s %d\n", field->parent->name_space, field->parent->name, field->name, field->offset, field->parent->inited);
392
393         mono_class_init (field->parent);
394
395         vtable = mono_class_vtable (domain, field->parent);
396         if (!vtable->initialized)
397                 mono_runtime_class_init (vtable);
398
399         //printf ("SFLDA1 %p\n", (char*)vtable->data + field->offset);
400
401         if (domain->special_static_fields && (addr = g_hash_table_lookup (domain->special_static_fields, field)))
402                 addr = mono_get_special_static_data (GPOINTER_TO_UINT (addr));
403         else
404                 addr = (char*)vtable->data + field->offset;
405         
406         return addr;
407 }
408
409 static gpointer
410 mono_ldtoken_wrapper (MonoImage *image, int token)
411 {
412         MonoClass *handle_class;
413         gpointer res;
414
415         MONO_ARCH_SAVE_REGS;
416         res = mono_ldtoken (image, token, &handle_class, NULL); 
417         mono_class_init (handle_class);
418
419         return res;
420 }
421
422 static guint64
423 mono_fconv_u8 (double v)
424 {
425         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
426         return (guint64)v;
427 }
428
429 #ifdef MONO_ARCH_EMULATE_FCONV_TO_I8
430 static gint64
431 mono_fconv_i8 (double v)
432 {
433         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
434         return (gint64)v;
435 }
436 #endif
437
438 static guint32
439 mono_fconv_u4 (double v)
440 {
441         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
442         return (guint32)v;
443 }
444
445 #ifndef HAVE_TRUNCL
446
447 #ifdef HAVE_AINTL
448 extern long double aintl (long double);
449 #define truncl aintl
450 #else
451 /* should mostly work */
452 static double 
453 truncl (double x)
454 {
455         if (x < 0)
456                 x += 1.0;
457         return floor (x);
458 }
459 #endif /* HAVE_AINTL */
460
461 #endif /* HAVE_TRUNCL */
462
463 static gint64
464 mono_fconv_ovf_i8 (double v)
465 {
466         gint64 res;
467
468         MONO_ARCH_SAVE_REGS;
469
470         res = (gint64)v;
471
472         if (isnan(v) || truncl (v) != res) {
473                 mono_raise_exception (mono_get_exception_overflow ());
474         }
475         return res;
476 }
477
478 static guint64
479 mono_fconv_ovf_u8 (double v)
480 {
481         guint64 res;
482
483         MONO_ARCH_SAVE_REGS;
484     
485         res = (guint64)v;
486
487         if (isnan(v) || truncl (v) != res) {
488                 mono_raise_exception (mono_get_exception_overflow ());
489         }
490         return res;
491 }
492
493 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R8
494 static double
495 mono_lconv_to_r8 (gint64 a)
496 {
497         return (double)a;
498 }
499 #endif
500
501 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R4
502 static float
503 mono_lconv_to_r4 (gint64 a)
504 {
505         return (float)a;
506 }
507 #endif
508
509 #ifdef MONO_ARCH_EMULATE_CONV_R8_UN
510 static double
511 mono_conv_to_r8_un (guint32 a)
512 {
513         return (double)a;
514 }
515 #endif
516
517 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R8_UN
518 static double
519 mono_lconv_to_r8_un (guint64 a)
520 {
521         return (double)a;
522 }
523 #endif
524