Update mcs/class/System.Core/System/TimeZoneInfo.cs
[mono.git] / mono / mini / tramp-mips.c
1 /*
2  * tramp-mips.c: JIT trampoline code for MIPS
3  *
4  * Authors:
5  *    Mark Mason (mason@broadcom.com)
6  *
7  * Based on tramp-ppc.c by:
8  *   Dietmar Maurer (dietmar@ximian.com)
9  *   Paolo Molaro (lupus@ximian.com)
10  *   Carlos Valiente <yo@virutass.net>
11  *
12  * (C) 2006 Broadcom
13  * (C) 2001 Ximian, Inc.
14  */
15
16 #include <config.h>
17 #include <glib.h>
18
19 #include <mono/metadata/appdomain.h>
20 #include <mono/metadata/marshal.h>
21 #include <mono/metadata/tabledefs.h>
22 #include <mono/arch/mips/mips-codegen.h>
23
24 #include "mini.h"
25 #include "mini-mips.h"
26
27 static guint8* nullified_class_init_trampoline;
28
29 /*
30  * get_unbox_trampoline:
31  * @m: method pointer
32  * @addr: pointer to native code for @m
33  *
34  * when value type methods are called through the vtable we need to unbox the
35  * this argument. This method returns a pointer to a trampoline which does
36  * unboxing before calling the method
37  */
38 gpointer
39 mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
40 {
41         guint8 *code, *start;
42         MonoDomain *domain = mono_domain_get ();
43             
44         start = code = mono_domain_code_reserve (domain, 20);
45
46         mips_load (code, mips_t9, addr);
47         /* The this pointer is kept in a0 */
48         mips_addiu (code, mips_a0, mips_a0, sizeof (MonoObject));
49         mips_jr (code, mips_t9);
50         mips_nop (code);
51
52         mono_arch_flush_icache (start, code - start);
53         g_assert ((code - start) <= 20);
54         /*g_print ("unbox trampoline at %d for %s:%s\n", this_pos, m->klass->name, m->name);
55         g_print ("unbox code is at %p for method at %p\n", start, addr);*/
56
57         return start;
58 }
59
60 void
61 mono_arch_patch_callsite (guint8 *method_start, guint8 *orig_code, guint8 *addr)
62 {
63         guint32 *code = (guint32*)orig_code;
64
65         /* Locate the address of the method-specific trampoline.
66         The call using the vtable slot that took the processing flow to
67         'arch_create_jit_trampoline' looks something like one of these:
68
69                 jal     XXXXYYYY
70                 nop
71
72                 lui     t9, XXXX
73                 addiu   t9, YYYY
74                 jalr    t9
75                 nop
76
77         On entry, 'code' points just after one of the above sequences.
78         */
79         
80         /* The jal case */
81         if ((code[-2] >> 26) == 0x03) {
82                 //g_print ("direct patching\n");
83                 mips_patch ((code-2), (gsize)addr);
84                 return;
85         }
86         /* Look for the jalr */
87         if ((code[-2] & 0xfc1f003f) == 0x00000009) {
88                 /* The lui / addiu / jalr case */
89                 if ((code [-4] >> 26) == 0x0f && (code [-3] >> 26) == 0x09
90                     && (code [-2] >> 26) == 0) {
91                         mips_patch ((code-4), (gsize)addr);
92                         return;
93                 }
94         }
95         g_print("error: bad patch at 0x%08x\n", code);
96         g_assert_not_reached ();
97 }
98
99 void
100 mono_arch_patch_plt_entry (guint8 *code, gpointer *got, mgreg_t *regs, guint8 *addr)
101 {
102         g_assert_not_reached ();
103 }
104
105 /* Stack size for trampoline function 
106  * MIPS_MINIMAL_STACK_SIZE + 16 (args + alignment to mips_magic_trampoline)
107  * + MonoLMF + 14 fp regs + 13 gregs + alignment
108  * #define STACK (MIPS_MINIMAL_STACK_SIZE + 4 * sizeof (gulong) + sizeof (MonoLMF) + 14 * sizeof (double) + 13 * (sizeof (gulong)))
109  * STACK would be 444 for 32 bit darwin
110  */
111
112 #define STACK (4*IREG_SIZE + 8 + sizeof(MonoLMF) + 32)
113
114 void
115 mono_arch_nullify_plt_entry (guint8 *code, mgreg_t *regs)
116 {
117         if (mono_aot_only && !nullified_class_init_trampoline)
118                 nullified_class_init_trampoline = mono_aot_get_trampoline ("nullified_class_init_trampoline");
119
120         mono_arch_patch_plt_entry (code, NULL, regs, nullified_class_init_trampoline);
121 }
122
123 void
124 mono_arch_nullify_class_init_trampoline (guint8 *code, mgreg_t *regs)
125 {
126         guint32 *code32 = (guint32*)code;
127
128         /* back up to the jal/jalr instruction */
129         code32 -= 2;
130
131         /* Check for jal/jalr -- and NOP it out */
132         if ((((*code32)&0xfc000000) == 0x0c000000)
133             || (((*code32)&0xfc1f003f) == 0x00000009)) {
134                 mips_nop (code32);
135                 mono_arch_flush_icache ((gpointer)(code32 - 1), 4);
136                 return;
137         }
138         g_assert_not_reached ();
139 }
140
141 gpointer
142 mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo **info)
143 {
144         guint8 *buf, *code;
145
146         code = buf = mono_global_codeman_reserve (16);
147
148         mips_jr (code, mips_ra);
149         mips_nop (code);
150
151         mono_arch_flush_icache (buf, code - buf);
152
153         if (info)
154                 *info = mono_tramp_info_create (g_strdup_printf ("nullified_class_init_trampoline"), buf, code - buf, NULL, NULL);
155
156         return buf;
157 }
158
159 /*
160  * Stack frame description when the generic trampoline is called.
161  * caller frame
162  * --------------------
163  *  MonoLMF
164  *  -------------------
165  *  Saved FP registers 0-13
166  *  -------------------
167  *  Saved general registers 0-12
168  *  -------------------
169  *  param area for 3 args to mips_magic_trampoline
170  *  -------------------
171  *  linkage area
172  *  -------------------
173  */
174 guchar*
175 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
176 {
177         guint8 *buf, *tramp, *code = NULL;
178         int i, lmf;
179         GSList *unwind_ops = NULL;
180         MonoJumpInfo *ji = NULL;
181         int max_code_len = 768;
182
183         /* AOT not supported on MIPS yet */
184         g_assert (!aot);
185
186         /* Now we'll create in 'buf' the MIPS trampoline code. This
187            is the trampoline code common to all methods  */
188                 
189         code = buf = mono_global_codeman_reserve (max_code_len);
190                 
191         /* Allocate the stack frame, and save the return address */
192         mips_addiu (code, mips_sp, mips_sp, -STACK);
193         mips_sw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
194
195         /* we build the MonoLMF structure on the stack - see mini-mips.h */
196         /* offset of MonoLMF from sp */
197         lmf = STACK - sizeof (MonoLMF) - 8;
198
199         for (i = 0; i < MONO_MAX_IREGS; i++)
200                 MIPS_SW (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
201         for (i = 0; i < MONO_MAX_FREGS; i++)
202                 MIPS_SWC1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
203
204         /* Set the magic number */
205         mips_load_const (code, mips_at, MIPS_LMF_MAGIC2);
206         mips_sw (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, magic));
207
208         /* Save caller sp */
209         mips_addiu (code, mips_at, mips_sp, STACK);
210         MIPS_SW (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[mips_sp]));
211
212         /* save method info (it was in t8) */
213         mips_sw (code, mips_t8, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, method));
214
215         /* save the IP (caller ip) */
216         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
217                 mips_sw (code, mips_zero, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, eip));
218         } else {
219                 mips_sw (code, mips_ra, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, eip));
220         }
221
222         /* jump to mono_get_lmf_addr here */
223         mips_load (code, mips_t9, mono_get_lmf_addr);
224         mips_jalr (code, mips_t9, mips_ra);
225         mips_nop (code);
226
227         /* v0 now points at the (MonoLMF **) for the current thread */
228
229         /* new_lmf->lmf_addr = lmf_addr -- useful when unwinding */
230         mips_sw (code, mips_v0, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
231
232         /* new_lmf->previous_lmf = *lmf_addr */
233         mips_lw (code, mips_at, mips_v0, 0);
234         mips_sw (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
235
236         /* *(lmf_addr) = new_lmf */
237         mips_addiu (code, mips_at, mips_sp, lmf);
238         mips_sw (code, mips_at, mips_v0, 0);
239
240         /*
241          * Now we're ready to call mips_magic_trampoline ().
242          */
243
244         /* Arg 1: pointer to registers so that the magic trampoline can
245          * access what we saved above
246          */
247         mips_addiu (code, mips_a0, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[0]));
248
249         /* Arg 2: code (next address to the instruction that called us) */
250         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
251                 mips_move (code, mips_a1, mips_zero);
252         } else {
253                 mips_lw (code, mips_a1, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
254         }
255
256         /* Arg 3: MonoMethod *method. */
257         if (tramp_type == MONO_TRAMPOLINE_GENERIC_CLASS_INIT)
258                 mips_lw (code, mips_a2, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs [mips_a0]));
259         else
260                 mips_lw (code, mips_a2, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, method));
261
262         /* Arg 4: Trampoline */
263         mips_move (code, mips_a3, mips_zero);
264                 
265         /* Now go to the trampoline */
266         tramp = (guint8*)mono_get_trampoline_func (tramp_type);
267         mips_load (code, mips_t9, (guint32)tramp);
268         mips_jalr (code, mips_t9, mips_ra);
269         mips_nop (code);
270                 
271         /* Code address is now in v0, move it to at */
272         mips_move (code, mips_at, mips_v0);
273
274         /*
275          * Now unwind the MonoLMF
276          */
277
278         /* t0 = current_lmf->previous_lmf */
279         mips_lw (code, mips_t0, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
280         /* t1 = lmf_addr */
281         mips_lw (code, mips_t1, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
282         /* (*lmf_addr) = previous_lmf */
283         mips_sw (code, mips_t0, mips_t1, 0);
284
285         /* Restore the callee-saved & argument registers */
286         for (i = 0; i < MONO_MAX_IREGS; i++) {
287                 if ((MONO_ARCH_CALLEE_SAVED_REGS | MONO_ARCH_CALLEE_REGS | MIPS_ARG_REGS) & (1 << i))
288                     MIPS_LW (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
289         }
290         for (i = 0; i < MONO_MAX_FREGS; i++)
291                 MIPS_LWC1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
292
293         /* Non-standard function epilogue. Instead of doing a proper
294          * return, we just jump to the compiled code.
295          */
296         /* Restore ra & stack pointer, and jump to the code */
297
298         if (tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH)
299                 mips_move (code, mips_v0, mips_at);
300         mips_lw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
301         mips_addiu (code, mips_sp, mips_sp, STACK);
302         if (MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type))
303                 mips_jr (code, mips_ra);
304         else
305                 mips_jr (code, mips_at);
306         mips_nop (code);
307
308         /* Flush instruction cache, since we've generated code */
309         mono_arch_flush_icache (buf, code - buf);
310         
311         /* Sanity check */
312         g_assert ((code - buf) <= max_code_len);
313
314         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
315                 /* Initialize the nullified class init trampoline used in the AOT case */
316                 nullified_class_init_trampoline = mono_arch_get_nullified_class_init_trampoline (NULL);
317
318         if (info)
319                 *info = mono_tramp_info_create (mono_get_generic_trampoline_name (tramp_type), buf, code - buf, ji, unwind_ops);
320
321         return buf;
322 }
323
324 gpointer
325 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
326 {
327         guint8 *code, *buf, *tramp;
328
329         tramp = mono_get_trampoline_code (tramp_type);
330
331         code = buf = mono_domain_code_reserve (domain, 32);
332
333         /* Prepare the jump to the generic trampoline code
334          * mono_arch_create_trampoline_code() knows we're putting this in t8
335          */
336         mips_load (code, mips_t8, arg1);
337         
338         /* Now jump to the generic trampoline code */
339         mips_load (code, mips_at, tramp);
340         mips_jr (code, mips_at);
341         mips_nop (code);
342
343         /* Flush instruction cache, since we've generated code */
344         mono_arch_flush_icache (buf, code - buf);
345
346         g_assert ((code - buf) <= 32);
347
348         if (code_len)
349                 *code_len = code - buf;
350
351         return buf;
352 }
353
354 gpointer
355 mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr)
356 {
357         guint8 *code, *start;
358         int buf_len;
359
360         MonoDomain *domain = mono_domain_get ();
361
362         buf_len = 24;
363
364         start = code = mono_domain_code_reserve (domain, buf_len);
365
366         mips_load (code, MONO_ARCH_RGCTX_REG, mrgctx);
367         mips_load (code, mips_at, addr);
368         mips_jr (code, mips_at);
369         mips_nop (code);
370
371         g_assert ((code - start) <= buf_len);
372
373         mono_arch_flush_icache (start, code - start);
374
375         return start;
376 }
377
378 gpointer
379 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
380 {
381         guint8 *tramp;
382         guint8 *code, *buf;
383         int tramp_size;
384         guint32 code_len;
385         guint8 **rgctx_null_jumps;
386         int depth, index;
387         int i, njumps;
388         gboolean mrgctx;
389         MonoJumpInfo *ji = NULL;
390         GSList *unwind_ops = NULL;
391
392         mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
393         index = MONO_RGCTX_SLOT_INDEX (slot);
394         if (mrgctx)
395                 index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
396         for (depth = 0; ; ++depth) {
397                 int size = mono_class_rgctx_get_array_size (depth, mrgctx);
398
399                 if (index < size - 1)
400                         break;
401                 index -= size - 1;
402         }
403
404         tramp_size = 64 + 16 * depth;
405
406         code = buf = mono_global_codeman_reserve (tramp_size);
407
408         mono_add_unwind_op_def_cfa (unwind_ops, code, buf, mips_sp, 0);
409
410         rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2));
411         njumps = 0;
412
413         /* The vtable/mrgctx is in a0 */
414         g_assert (MONO_ARCH_VTABLE_REG == mips_a0);
415         if (mrgctx) {
416                 /* get mrgctx ptr */
417                 mips_move (code, mips_a1, mips_a0);
418         } else {
419                 /* load rgctx ptr from vtable */
420                 g_assert (mips_is_imm16 (G_STRUCT_OFFSET (MonoVTable, runtime_generic_context)));
421                 mips_lw (code, mips_a1, mips_a0, G_STRUCT_OFFSET (MonoVTable, runtime_generic_context));
422                 /* is the rgctx ptr null? */
423                 /* if yes, jump to actual trampoline */
424                 rgctx_null_jumps [njumps ++] = code;
425                 mips_beq (code, mips_a1, mips_zero, 0);
426                 mips_nop (code);
427         }
428
429         for (i = 0; i < depth; ++i) {
430                 /* load ptr to next array */
431                 if (mrgctx && i == 0) {
432                         g_assert (mips_is_imm16 (MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT));
433                         mips_lw (code, mips_a1, mips_a1, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT);
434                 } else {
435                         mips_lw (code, mips_a1, mips_a1, 0);
436                 }
437                 /* is the ptr null? */
438                 /* if yes, jump to actual trampoline */
439                 rgctx_null_jumps [njumps ++] = code;
440                 mips_beq (code, mips_a1, mips_zero, 0);
441                 mips_nop (code);
442         }
443
444         /* fetch slot */
445         g_assert (mips_is_imm16 (sizeof (gpointer) * (index + 1)));
446         mips_lw (code, mips_a1, mips_a1, sizeof (gpointer) * (index + 1));
447         /* is the slot null? */
448         /* if yes, jump to actual trampoline */
449         rgctx_null_jumps [njumps ++] = code;
450         mips_beq (code, mips_a1, mips_zero, 0);
451         mips_nop (code);
452         /* otherwise return, result is in R1 */
453         mips_move (code, mips_v0, mips_a1);
454         mips_jr (code, mips_ra);
455         mips_nop (code);
456
457         g_assert (njumps <= depth + 2);
458         for (i = 0; i < njumps; ++i)
459                 mips_patch ((guint32*)rgctx_null_jumps [i], (guint32)code);
460
461         g_free (rgctx_null_jumps);
462
463         /* Slowpath */
464
465         /* The vtable/mrgctx is still in a0 */
466
467         if (aot) {
468                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", slot));
469                 mips_load (code, mips_at, 0);
470                 mips_jr (code, mips_at);
471                 mips_nop (code);
472         } else {
473                 tramp = mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), &code_len);
474                 mips_load (code, mips_at, tramp);
475                 mips_jr (code, mips_at);
476                 mips_nop (code);
477         }
478
479         mono_arch_flush_icache (buf, code - buf);
480
481         g_assert (code - buf <= tramp_size);
482
483         if (info)
484                 *info = mono_tramp_info_create (mono_get_rgctx_fetch_trampoline_name (slot), buf, code - buf, ji, unwind_ops);
485
486         return buf;
487 }
488
489 gpointer
490 mono_arch_create_generic_class_init_trampoline (MonoTrampInfo **info, gboolean aot)
491 {
492         guint8 *tramp;
493         guint8 *code, *buf;
494         static int byte_offset = -1;
495         static guint8 bitmask;
496         guint8 *jump;
497         int tramp_size;
498         guint32 code_len;
499         GSList *unwind_ops = NULL;
500         MonoJumpInfo *ji = NULL;
501
502         tramp_size = 64;
503
504         code = buf = mono_global_codeman_reserve (tramp_size);
505
506         if (byte_offset < 0)
507                 mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask);
508
509         /* if (!(vtable->initialized)) */
510         mips_lbu (code, mips_at, MONO_ARCH_VTABLE_REG, byte_offset);
511         g_assert (!(bitmask & 0xffff0000));
512         mips_andi (code, mips_at, mips_at, bitmask);
513         jump = code;
514         mips_beq (code, mips_at, mips_zero, 0);
515         mips_nop (code);
516         /* Initialized case */
517         mips_jr (code, mips_ra);
518         mips_nop (code);
519
520         /* Uninitialized case */
521         mips_patch ((guint32*)jump, (guint32)code);
522
523         if (aot) {
524                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "specific_trampoline_generic_class_init");
525                 mips_load (code, mips_at, 0);
526                 mips_jr (code, mips_at);
527                 mips_nop (code);
528         } else {
529                 tramp = mono_arch_create_specific_trampoline (NULL, MONO_TRAMPOLINE_GENERIC_CLASS_INIT, mono_get_root_domain (), &code_len);
530                 mips_load (code, mips_at, tramp);
531                 mips_jr (code, mips_at);
532                 mips_nop (code);
533         }
534
535         mono_arch_flush_icache (buf, code - buf);
536
537         g_assert (code - buf <= tramp_size);
538
539         if (info)
540                 *info = mono_tramp_info_create (g_strdup_printf ("generic_class_init_trampoline"), buf, code - buf, ji, unwind_ops);
541
542         return buf;
543 }