2003-05-27 Dietmar Maurer <dietmar@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         EnterCriticalSection (metadata_section);
21         addr = mono_compile_method (method);
22         LeaveCriticalSection (metadata_section);
23
24         return addr;
25 }
26
27 static void*
28 mono_ldvirtfn (MonoObject *obj, MonoMethod *method) 
29 {
30         MONO_ARCH_SAVE_REGS;
31
32         method = mono_object_get_virtual_method (obj, method);
33         if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
34                 method = mono_marshal_get_synchronized_wrapper (method);
35
36         return mono_ldftn (method);
37 }
38
39 static void
40 helper_initobj (void *addr, int size)
41 {
42         MONO_ARCH_SAVE_REGS;
43
44         memset (addr, 0, size);
45 }
46
47 static void
48 helper_memcpy (void *addr, void *src, int size)
49 {
50         MONO_ARCH_SAVE_REGS;
51
52         memcpy (addr, src, size);
53 }
54
55 static void
56 helper_memset (void *addr, int val, int size)
57 {
58         MONO_ARCH_SAVE_REGS;
59
60         memset (addr, val, size);
61 }
62
63 static void
64 helper_stelem_ref (MonoArray *array, int index, MonoObject *val)
65 {
66         MONO_ARCH_SAVE_REGS;
67
68         if (index >= array->max_length)
69                 mono_raise_exception (mono_get_exception_index_out_of_range ());
70
71         if (val && !mono_object_isinst (val, array->obj.vtable->klass->element_class))
72                 mono_raise_exception (mono_get_exception_array_type_mismatch ());
73
74         mono_array_set (array, gpointer, index, val);
75 }
76
77 static gint64 
78 mono_llmult (gint64 a, gint64 b)
79 {
80         MONO_ARCH_SAVE_REGS;
81         return a * b;
82 }
83
84 static guint64  
85 mono_llmult_ovf_un (guint32 al, guint32 ah, guint32 bl, guint32 bh)
86 {
87         guint64 res, t1;
88
89         MONO_ARCH_SAVE_REGS;
90
91         // fixme: this is incredible slow
92
93         if (ah && bh)
94                 goto raise_exception;
95
96         res = (guint64)al * (guint64)bl;
97
98         t1 = (guint64)ah * (guint64)bl + (guint64)al * (guint64)bh;
99
100         if (t1 > 0xffffffff)
101                 goto raise_exception;
102
103         res += ((guint64)t1) << 32; 
104
105         return res;
106
107  raise_exception:
108         mono_raise_exception (mono_get_exception_overflow ());
109         return 0;
110 }
111
112
113 static guint64  
114 mono_llmult_ovf (guint32 al, gint32 ah, guint32 bl, gint32 bh) 
115 {
116         /*
117         Use Karatsuba algorithm where:
118                 a*b is: AhBh(R^2+R)+(Ah-Al)(Bl-Bh)R+AlBl(R+1)
119                 where Ah is the "high half" (most significant 32 bits) of a and
120                 where Al is the "low half" (least significant 32 bits) of a and
121                 where  Bh is the "high half" of b and Bl is the "low half" and
122                 where R is the Radix or "size of the half" (in our case 32 bits)
123
124         Note, for the product of two 64 bit numbers to fit into a 64
125         result, ah and/or bh must be 0.  This will save us from doing
126         the AhBh term at all.
127
128         Also note that we refactor so that we don't overflow 64 bits with 
129         intermediate results. So we use [(Ah-Al)(Bl-Bh)+AlBl]R+AlBl
130         */
131
132         gint64 res, t1;
133         gint32 sign;
134
135         MONO_ARCH_SAVE_REGS;
136
137         /* need to work with absoulte values, so find out what the
138            resulting sign will be and convert any negative numbers
139            from two's complement
140         */
141         sign = ah ^ bh;
142         if (ah < 0) {
143                 /* flip the bits and add 1 */
144                 ah ^= ~0;
145                 if (al ==  0)
146                         ah += 1;
147                 else {
148                         al ^= ~0;
149                         al +=1;
150                 }
151         }
152
153         if (bh < 0) {
154                 /* flip the bits and add 1 */
155                 bh ^= ~0;
156                 if (bl ==  0)
157                         bh += 1;
158                 else {
159                         bl ^= ~0;
160                         bl +=1;
161                 }
162         }
163                 
164         /* we overflow for sure if both upper halves are greater 
165            than zero because we would need to shift their 
166            product 64 bits to the left and that will not fit
167            in a 64 bit result */
168         if (ah && bh)
169                 goto raise_exception;
170
171         /* do the AlBl term first */
172         t1 = (gint64)al * (gint64)bl;
173
174         res = t1;
175
176         /* now do the [(Ah-Al)(Bl-Bh)+AlBl]R term */
177         t1 += (gint64)(ah - al) * (gint64)(bl - bh);
178         t1 <<= 32;
179         /* check for overflow */
180         if (t1 > (0x7FFFFFFFFFFFFFFF - res))
181                 goto raise_exception;
182
183         res += t1;
184
185         if (res < 0)
186                 goto raise_exception;
187
188         if (sign < 0)
189                 return -res;
190         else
191                 return res;
192
193  raise_exception:
194         mono_raise_exception (mono_get_exception_overflow ());
195         return 0;
196 }
197
198 static gint64 
199 mono_lldiv (gint64 a, gint64 b)
200 {
201         MONO_ARCH_SAVE_REGS;
202
203         return a / b;
204 }
205
206 static gint64 
207 mono_llrem (gint64 a, gint64 b)
208 {
209         MONO_ARCH_SAVE_REGS;
210
211         return a % b;
212 }
213
214 static guint64 
215 mono_lldiv_un (guint64 a, guint64 b)
216 {
217         MONO_ARCH_SAVE_REGS;
218
219         return a / b;
220 }
221
222 static guint64 
223 mono_llrem_un (guint64 a, guint64 b)
224 {
225         MONO_ARCH_SAVE_REGS;
226
227         return a % b;
228 }
229
230 static guint64 
231 mono_lshl (guint64 a, gint32 shamt)
232 {
233         guint64 res;
234
235         MONO_ARCH_SAVE_REGS;
236         res = a << shamt;
237
238         /*printf ("TESTL %lld << %d = %lld\n", a, shamt, res);*/
239
240         return res;
241 }
242
243 static guint64 
244 mono_lshr_un (guint64 a, gint32 shamt)
245 {
246         guint64 res;
247
248         MONO_ARCH_SAVE_REGS;
249         res = a >> shamt;
250
251         /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/
252
253         return res;
254 }
255
256 static gint64 
257 mono_lshr (gint64 a, gint32 shamt)
258 {
259         gint64 res;
260
261         MONO_ARCH_SAVE_REGS;
262         res = a >> shamt;
263
264         /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/
265
266         return res;
267 }
268
269 /**
270  * ves_array_element_address:
271  * @this: a pointer to the array object
272  *
273  * Returns: the address of an array element.
274  */
275 static gpointer 
276 ves_array_element_address (MonoArray *this, ...)
277 {
278         MonoClass *class;
279         va_list ap;
280         int i, ind, esize, realidx;
281         gpointer ea;
282
283         MONO_ARCH_SAVE_REGS;
284
285         g_assert (this != NULL);
286
287         va_start(ap, this);
288
289         class = this->obj.vtable->klass;
290
291         g_assert (this->bounds != NULL);
292
293         esize = mono_array_element_size (class);
294         ind = va_arg(ap, int);
295         ind -= (int)this->bounds [0].lower_bound;
296         if ((guint32)ind >= (guint32)this->bounds [0].length)
297                 mono_raise_exception (mono_get_exception_index_out_of_range ());
298         for (i = 1; i < class->rank; i++) {
299                 realidx = va_arg(ap, int) - (int)this->bounds [i].lower_bound;
300                 if ((guint32)realidx >= (guint32)this->bounds [i].length)
301                         mono_raise_exception (mono_get_exception_index_out_of_range ());
302                 ind *= this->bounds [i].length;
303                 ind += realidx;
304         }
305         esize *= ind;
306
307         ea = (gpointer*)((char*)this->vector + esize);
308
309         va_end(ap);
310
311         return ea;
312 }
313
314 static MonoArray *
315 mono_array_new_va (MonoMethod *cm, ...)
316 {
317         MonoDomain *domain = mono_domain_get ();
318         va_list ap;
319         guint32 *lengths;
320         guint32 *lower_bounds;
321         int pcount;
322         int rank;
323         int i, d;
324
325         MONO_ARCH_SAVE_REGS;
326
327         pcount = cm->signature->param_count;
328         rank = cm->klass->rank;
329
330         va_start (ap, cm);
331         
332         lengths = alloca (sizeof (guint32) * pcount);
333         for (i = 0; i < pcount; ++i)
334                 lengths [i] = d = va_arg(ap, int);
335
336         if (rank == pcount) {
337                 /* Only lengths provided. */
338                 lower_bounds = NULL;
339         } else {
340                 g_assert (pcount == (rank * 2));
341                 /* lower bounds are first. */
342                 lower_bounds = lengths;
343                 lengths += rank;
344         }
345         va_end(ap);
346
347         return mono_array_new_full (domain, cm->klass, lengths, lower_bounds);
348 }
349
350 static gpointer
351 mono_class_static_field_address (MonoDomain *domain, MonoClassField *field)
352 {
353         MonoVTable *vtable;
354         gpointer addr;
355         
356         MONO_ARCH_SAVE_REGS;
357
358         //printf ("SFLDA0 %s.%s::%s %d\n", field->parent->name_space, field->parent->name, field->name, field->offset, field->parent->inited);
359
360         mono_class_init (field->parent);
361
362         vtable = mono_class_vtable (domain, field->parent);
363         if (!vtable->initialized)
364                 mono_runtime_class_init (vtable);
365
366         //printf ("SFLDA1 %p\n", (char*)vtable->data + field->offset);
367
368         if (!domain->thread_static_fields || !(addr = g_hash_table_lookup (domain->thread_static_fields, field)))
369                 addr = (char*)vtable->data + field->offset;
370         else
371                 addr = mono_threads_get_static_data (GPOINTER_TO_UINT (addr));
372         
373         return addr;
374 }
375
376 static gpointer
377 mono_ldtoken_wrapper (MonoImage *image, int token)
378 {
379         MonoClass *handle_class;
380         gpointer res;
381
382         MONO_ARCH_SAVE_REGS;
383         res = mono_ldtoken (image, token, &handle_class);       
384         mono_class_init (handle_class);
385
386         return res;
387 }
388
389 static guint64
390 mono_fconv_u8 (double v)
391 {
392         MONO_ARCH_SAVE_REGS;
393         return (guint64)v;
394 }
395
396 static guint32
397 mono_fconv_u4 (double v)
398 {
399         MONO_ARCH_SAVE_REGS;
400         return (guint32)v;
401 }
402
403 static gint64
404 mono_fconv_ovf_i8 (double v)
405 {
406         gint64 res;
407
408         MONO_ARCH_SAVE_REGS;
409
410         res = (gint64)v;
411
412         if (isnan(v) || v != res) {
413                 mono_raise_exception (mono_get_exception_overflow ());
414         }
415         return res;
416 }
417
418 static guint64
419 mono_fconv_ovf_u8 (double v)
420 {
421         guint64 res;
422
423         MONO_ARCH_SAVE_REGS;
424     
425         res = (guint64)v;
426
427         if (isnan(v) || v != res) {
428                 mono_raise_exception (mono_get_exception_overflow ());
429         }
430         return res;
431 }