* StringTest.cs: Added test for Concat when one of the arguments is an
[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 static guint64 
260 mono_lshl (guint64 a, gint32 shamt)
261 {
262         guint64 res;
263
264         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
265         res = a << shamt;
266
267         /*printf ("TESTL %lld << %d = %lld\n", a, shamt, res);*/
268
269         return res;
270 }
271
272 static guint64 
273 mono_lshr_un (guint64 a, gint32 shamt)
274 {
275         guint64 res;
276
277         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
278         res = a >> shamt;
279
280         /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/
281
282         return res;
283 }
284
285 static gint64 
286 mono_lshr (gint64 a, gint32 shamt)
287 {
288         gint64 res;
289
290         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
291         res = a >> shamt;
292
293         /*printf ("TESTR %lld >> %d = %lld\n", a, shamt, res);*/
294
295         return res;
296 }
297
298 /**
299  * ves_array_element_address:
300  * @this: a pointer to the array object
301  *
302  * Returns: the address of an array element.
303  */
304 static gpointer 
305 ves_array_element_address (MonoArray *this, ...)
306 {
307         MonoClass *class;
308         va_list ap;
309         int i, ind, esize, realidx;
310         gpointer ea;
311
312         MONO_ARCH_SAVE_REGS;
313
314         g_assert (this != NULL);
315
316         va_start(ap, this);
317
318         class = this->obj.vtable->klass;
319
320         g_assert (this->bounds != NULL);
321
322         esize = mono_array_element_size (class);
323         ind = va_arg(ap, int);
324         ind -= (int)this->bounds [0].lower_bound;
325         if ((guint32)ind >= (guint32)this->bounds [0].length)
326                 mono_raise_exception (mono_get_exception_index_out_of_range ());
327         for (i = 1; i < class->rank; i++) {
328                 realidx = va_arg(ap, int) - (int)this->bounds [i].lower_bound;
329                 if ((guint32)realidx >= (guint32)this->bounds [i].length)
330                         mono_raise_exception (mono_get_exception_index_out_of_range ());
331                 ind *= this->bounds [i].length;
332                 ind += realidx;
333         }
334         esize *= ind;
335
336         ea = (gpointer*)((char*)this->vector + esize);
337
338         va_end(ap);
339
340         return ea;
341 }
342
343 static MonoArray *
344 mono_array_new_va (MonoMethod *cm, ...)
345 {
346         MonoDomain *domain = mono_domain_get ();
347         va_list ap;
348         guint32 *lengths;
349         guint32 *lower_bounds;
350         int pcount;
351         int rank;
352         int i, d;
353
354         MONO_ARCH_SAVE_REGS;
355
356         pcount = cm->signature->param_count;
357         rank = cm->klass->rank;
358
359         va_start (ap, cm);
360         
361         lengths = alloca (sizeof (guint32) * pcount);
362         for (i = 0; i < pcount; ++i)
363                 lengths [i] = d = va_arg(ap, int);
364
365         if (rank == pcount) {
366                 /* Only lengths provided. */
367                 lower_bounds = NULL;
368         } else {
369                 g_assert (pcount == (rank * 2));
370                 /* lower bounds are first. */
371                 lower_bounds = lengths;
372                 lengths += rank;
373         }
374         va_end(ap);
375
376         return mono_array_new_full (domain, cm->klass, lengths, lower_bounds);
377 }
378
379 static gpointer
380 mono_class_static_field_address (MonoDomain *domain, MonoClassField *field)
381 {
382         MonoVTable *vtable;
383         gpointer addr;
384         
385         MONO_ARCH_SAVE_REGS;
386
387         //printf ("SFLDA0 %s.%s::%s %d\n", field->parent->name_space, field->parent->name, field->name, field->offset, field->parent->inited);
388
389         mono_class_init (field->parent);
390
391         vtable = mono_class_vtable (domain, field->parent);
392         if (!vtable->initialized)
393                 mono_runtime_class_init (vtable);
394
395         //printf ("SFLDA1 %p\n", (char*)vtable->data + field->offset);
396
397         if (domain->special_static_fields && (addr = g_hash_table_lookup (domain->special_static_fields, field)))
398                 addr = mono_get_special_static_data (GPOINTER_TO_UINT (addr));
399         else
400                 addr = (char*)vtable->data + field->offset;
401         
402         return addr;
403 }
404
405 static gpointer
406 mono_ldtoken_wrapper (MonoImage *image, int token)
407 {
408         MonoClass *handle_class;
409         gpointer res;
410
411         MONO_ARCH_SAVE_REGS;
412         res = mono_ldtoken (image, token, &handle_class);       
413         mono_class_init (handle_class);
414
415         return res;
416 }
417
418 static guint64
419 mono_fconv_u8 (double v)
420 {
421         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
422         return (guint64)v;
423 }
424
425 #ifdef MONO_ARCH_EMULATE_FCONV_TO_I8
426 static gint64
427 mono_fconv_i8 (double v)
428 {
429         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
430         return (gint64)v;
431 }
432 #endif
433
434 static guint32
435 mono_fconv_u4 (double v)
436 {
437         /* no need, no exceptions: MONO_ARCH_SAVE_REGS;*/
438         return (guint32)v;
439 }
440
441 #ifndef HAVE_TRUNCL
442 /* should mostly work */
443 static double 
444 truncl (double x)
445 {
446         if (x < 0)
447                 x += 1.0;
448         return floor (x);
449 }
450
451 #endif
452
453 static gint64
454 mono_fconv_ovf_i8 (double v)
455 {
456         gint64 res;
457
458         MONO_ARCH_SAVE_REGS;
459
460         res = (gint64)v;
461
462         if (isnan(v) || truncl (v) != res) {
463                 mono_raise_exception (mono_get_exception_overflow ());
464         }
465         return res;
466 }
467
468 static guint64
469 mono_fconv_ovf_u8 (double v)
470 {
471         guint64 res;
472
473         MONO_ARCH_SAVE_REGS;
474     
475         res = (guint64)v;
476
477         if (isnan(v) || truncl (v) != res) {
478                 mono_raise_exception (mono_get_exception_overflow ());
479         }
480         return res;
481 }
482
483 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R8
484 static double
485 mono_lconv_to_r8 (gint64 a)
486 {
487         return (double)a;
488 }
489 #endif
490
491 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R4
492 static float
493 mono_lconv_to_r4 (gint64 a)
494 {
495         return (float)a;
496 }
497 #endif
498
499 #ifdef MONO_ARCH_EMULATE_LCONV_TO_R8_UN
500 static double
501 mono_lconv_to_r8_un (guint64 a)
502 {
503         return (double)a;
504 }
505 #endif
506