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