Thu May 15 17:17:27 CEST 2003 Paolo Molaro <lupus@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 (sign < 0)
186                 return -res;
187         else
188                 return res;
189
190  raise_exception:
191         mono_raise_exception (mono_get_exception_overflow ());
192         return 0;
193 }
194
195 static gint64 
196 mono_lldiv (gint64 a, gint64 b)
197 {
198         MONO_ARCH_SAVE_REGS;
199
200         return a / b;
201 }
202
203 static gint64 
204 mono_llrem (gint64 a, gint64 b)
205 {
206         MONO_ARCH_SAVE_REGS;
207
208         return a % b;
209 }
210
211 static guint64 
212 mono_lldiv_un (guint64 a, guint64 b)
213 {
214         MONO_ARCH_SAVE_REGS;
215
216         return a / b;
217 }
218
219 static guint64 
220 mono_llrem_un (guint64 a, guint64 b)
221 {
222         MONO_ARCH_SAVE_REGS;
223
224         return a % b;
225 }
226
227 static guint64 
228 mono_lshl (guint64 a, gint32 shamt)
229 {
230         guint64 res;
231
232         MONO_ARCH_SAVE_REGS;
233         res = a << shamt;
234
235         /*printf ("TESTL %lld << %d = %lld\n", a, shamt, res);*/
236
237         return res;
238 }
239
240 static guint64 
241 mono_lshr_un (guint64 a, gint32 shamt)
242 {
243         guint64 res;
244
245         MONO_ARCH_SAVE_REGS;
246         res = a >> shamt;
247
248         /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/
249
250         return res;
251 }
252
253 static gint64 
254 mono_lshr (gint64 a, gint32 shamt)
255 {
256         gint64 res;
257
258         MONO_ARCH_SAVE_REGS;
259         res = a >> shamt;
260
261         /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/
262
263         return res;
264 }
265
266 /**
267  * ves_array_element_address:
268  * @this: a pointer to the array object
269  *
270  * Returns: the address of an array element.
271  */
272 static gpointer 
273 ves_array_element_address (MonoArray *this, ...)
274 {
275         MonoClass *class;
276         va_list ap;
277         int i, ind, esize, realidx;
278         gpointer ea;
279
280         MONO_ARCH_SAVE_REGS;
281
282         g_assert (this != NULL);
283
284         va_start(ap, this);
285
286         class = this->obj.vtable->klass;
287
288         g_assert (this->bounds != NULL);
289
290         esize = mono_array_element_size (class);
291         ind = va_arg(ap, int);
292         ind -= (int)this->bounds [0].lower_bound;
293         if ((guint32)ind >= (guint32)this->bounds [0].length)
294                 mono_raise_exception (mono_get_exception_index_out_of_range ());
295         for (i = 1; i < class->rank; i++) {
296                 realidx = va_arg(ap, int) - (int)this->bounds [i].lower_bound;
297                 if ((guint32)realidx >= (guint32)this->bounds [i].length)
298                         mono_raise_exception (mono_get_exception_index_out_of_range ());
299                 ind *= this->bounds [i].length;
300                 ind += realidx;
301         }
302         esize *= ind;
303
304         ea = (gpointer*)((char*)this->vector + esize);
305
306         va_end(ap);
307
308         return ea;
309 }
310
311 static MonoArray *
312 mono_array_new_va (MonoMethod *cm, ...)
313 {
314         MonoDomain *domain = mono_domain_get ();
315         va_list ap;
316         guint32 *lengths;
317         guint32 *lower_bounds;
318         int pcount;
319         int rank;
320         int i, d;
321
322         MONO_ARCH_SAVE_REGS;
323
324         pcount = cm->signature->param_count;
325         rank = cm->klass->rank;
326
327         va_start (ap, cm);
328         
329         lengths = alloca (sizeof (guint32) * pcount);
330         for (i = 0; i < pcount; ++i)
331                 lengths [i] = d = va_arg(ap, int);
332
333         if (rank == pcount) {
334                 /* Only lengths provided. */
335                 lower_bounds = NULL;
336         } else {
337                 g_assert (pcount == (rank * 2));
338                 /* lower bounds are first. */
339                 lower_bounds = lengths;
340                 lengths += rank;
341         }
342         va_end(ap);
343
344         return mono_array_new_full (domain, cm->klass, lengths, lower_bounds);
345 }
346
347 static gpointer
348 mono_class_static_field_address (MonoDomain *domain, MonoClassField *field)
349 {
350         MonoVTable *vtable;
351         gpointer addr;
352         
353         MONO_ARCH_SAVE_REGS;
354
355         //printf ("SFLDA0 %s.%s::%s %d\n", field->parent->name_space, field->parent->name, field->name, field->offset, field->parent->inited);
356
357         mono_class_init (field->parent);
358
359         vtable = mono_class_vtable (domain, field->parent);
360
361         //printf ("SFLDA1 %p\n", (char*)vtable->data + field->offset);
362
363         if (!domain->thread_static_fields || !(addr = g_hash_table_lookup (domain->thread_static_fields, field)))
364                 addr = (char*)vtable->data + field->offset;
365         else
366                 addr = mono_threads_get_static_data (GPOINTER_TO_UINT (addr));
367         
368         return addr;
369 }
370
371 static gpointer
372 mono_ldtoken_wrapper (MonoImage *image, int token)
373 {
374         MonoClass *handle_class;
375         gpointer res;
376
377         MONO_ARCH_SAVE_REGS;
378         res = mono_ldtoken (image, token, &handle_class);       
379         mono_class_init (handle_class);
380
381         return res;
382 }
383
384 static guint64
385 mono_fconv_u8 (double v)
386 {
387         MONO_ARCH_SAVE_REGS;
388         return (guint64)v;
389 }
390
391 static guint32
392 mono_fconv_u4 (double v)
393 {
394         MONO_ARCH_SAVE_REGS;
395         return (guint32)v;
396 }
397
398 static gint64
399 mono_fconv_ovf_i8 (double v)
400 {
401         gint64 res;
402
403         MONO_ARCH_SAVE_REGS;
404
405         res = (gint64)v;
406
407         if (isnan(v) || v != res) {
408                 mono_raise_exception (mono_get_exception_overflow ());
409         }
410         return res;
411 }
412
413 static guint64
414 mono_fconv_ovf_u8 (double v)
415 {
416         guint64 res;
417
418         MONO_ARCH_SAVE_REGS;
419     
420         res = (guint64)v;
421
422         if (isnan(v) || v != res) {
423                 mono_raise_exception (mono_get_exception_overflow ());
424         }
425         return res;
426 }