Merge pull request #995 from yjoly/master
[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 /*
28  * get_unbox_trampoline:
29  * @m: method pointer
30  * @addr: pointer to native code for @m
31  *
32  * when value type methods are called through the vtable we need to unbox the
33  * this argument. This method returns a pointer to a trampoline which does
34  * unboxing before calling the method
35  */
36 gpointer
37 mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
38 {
39         guint8 *code, *start;
40         MonoDomain *domain = mono_domain_get ();
41             
42         start = code = mono_domain_code_reserve (domain, 20);
43
44         mips_load (code, mips_t9, addr);
45         /* The this pointer is kept in a0 */
46         mips_addiu (code, mips_a0, mips_a0, sizeof (MonoObject));
47         mips_jr (code, mips_t9);
48         mips_nop (code);
49
50         mono_arch_flush_icache (start, code - start);
51         g_assert ((code - start) <= 20);
52         /*g_print ("unbox trampoline at %d for %s:%s\n", this_pos, m->klass->name, m->name);
53         g_print ("unbox code is at %p for method at %p\n", start, addr);*/
54
55         return start;
56 }
57
58 void
59 mono_arch_patch_callsite (guint8 *method_start, guint8 *orig_code, guint8 *addr)
60 {
61         guint32 *code = (guint32*)orig_code;
62
63         /* Locate the address of the method-specific trampoline.
64         The call using the vtable slot that took the processing flow to
65         'arch_create_jit_trampoline' looks something like one of these:
66
67                 jal     XXXXYYYY
68                 nop
69
70                 lui     t9, XXXX
71                 addiu   t9, YYYY
72                 jalr    t9
73                 nop
74
75         On entry, 'code' points just after one of the above sequences.
76         */
77         
78         /* The jal case */
79         if ((code[-2] >> 26) == 0x03) {
80                 //g_print ("direct patching\n");
81                 mips_patch ((code-2), (gsize)addr);
82                 return;
83         }
84         /* Look for the jalr */
85         if ((code[-2] & 0xfc1f003f) == 0x00000009) {
86                 /* The lui / addiu / jalr case */
87                 if ((code [-4] >> 26) == 0x0f && (code [-3] >> 26) == 0x09
88                     && (code [-2] >> 26) == 0) {
89                         mips_patch ((code-4), (gsize)addr);
90                         return;
91                 }
92         }
93         g_print("error: bad patch at 0x%08x\n", code);
94         g_assert_not_reached ();
95 }
96
97 void
98 mono_arch_patch_plt_entry (guint8 *code, gpointer *got, mgreg_t *regs, guint8 *addr)
99 {
100         g_assert_not_reached ();
101 }
102
103 /* Stack size for trampoline function 
104  * MIPS_MINIMAL_STACK_SIZE + 16 (args + alignment to mips_magic_trampoline)
105  * + MonoLMF + 14 fp regs + 13 gregs + alignment
106  * #define STACK (MIPS_MINIMAL_STACK_SIZE + 4 * sizeof (gulong) + sizeof (MonoLMF) + 14 * sizeof (double) + 13 * (sizeof (gulong)))
107  * STACK would be 444 for 32 bit darwin
108  */
109
110 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
111
112 #define STACK (int)(ALIGN_TO(4*IREG_SIZE + 8 + sizeof(MonoLMF) + 32, 8))
113
114 void
115 mono_arch_nullify_class_init_trampoline (guint8 *code, mgreg_t *regs)
116 {
117         guint32 *code32 = (guint32*)code;
118
119         /* back up to the jal/jalr instruction */
120         code32 -= 2;
121
122         /* Check for jal/jalr -- and NOP it out */
123         if ((((*code32)&0xfc000000) == 0x0c000000)
124             || (((*code32)&0xfc1f003f) == 0x00000009)) {
125                 mips_nop (code32);
126                 mono_arch_flush_icache ((gpointer)(code32 - 1), 4);
127                 return;
128         }
129         g_assert_not_reached ();
130 }
131
132 gpointer
133 mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo **info)
134 {
135         guint8 *buf, *code;
136
137         code = buf = mono_global_codeman_reserve (16);
138
139         mips_jr (code, mips_ra);
140         mips_nop (code);
141
142         mono_arch_flush_icache (buf, code - buf);
143
144         if (info)
145                 *info = mono_tramp_info_create ("nullified_class_init_trampoline", buf, code - buf, NULL, NULL);
146
147         return buf;
148 }
149
150 /*
151  * Stack frame description when the generic trampoline is called.
152  * caller frame
153  * --------------------
154  *  MonoLMF
155  *  -------------------
156  *  Saved FP registers 0-13
157  *  -------------------
158  *  Saved general registers 0-12
159  *  -------------------
160  *  param area for 3 args to mips_magic_trampoline
161  *  -------------------
162  *  linkage area
163  *  -------------------
164  */
165 guchar*
166 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
167 {
168         char *tramp_name;
169         guint8 *buf, *tramp, *code = NULL;
170         int i, lmf;
171         GSList *unwind_ops = NULL;
172         MonoJumpInfo *ji = NULL;
173         int max_code_len = 768;
174
175         /* AOT not supported on MIPS yet */
176         g_assert (!aot);
177
178         /* Now we'll create in 'buf' the MIPS trampoline code. This
179            is the trampoline code common to all methods  */
180
181         code = buf = mono_global_codeman_reserve (max_code_len);
182
183         /* Allocate the stack frame, and save the return address */
184         mips_addiu (code, mips_sp, mips_sp, -STACK);
185         mips_sw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
186
187         /* we build the MonoLMF structure on the stack - see mini-mips.h */
188         /* offset of MonoLMF from sp */
189         lmf = STACK - sizeof (MonoLMF) - 8;
190
191         for (i = 0; i < MONO_MAX_IREGS; i++)
192                 MIPS_SW (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
193         for (i = 0; i < MONO_MAX_FREGS; i++)
194                 MIPS_SWC1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
195
196         /* Set the magic number */
197         mips_load_const (code, mips_at, MIPS_LMF_MAGIC2);
198         mips_sw (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, magic));
199
200         /* Save caller sp */
201         mips_addiu (code, mips_at, mips_sp, STACK);
202         MIPS_SW (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[mips_sp]));
203
204         /* save method info (it was in t8) */
205         mips_sw (code, mips_t8, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, method));
206
207         /* save the IP (caller ip) */
208         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
209                 mips_sw (code, mips_zero, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, eip));
210         } else {
211                 mips_sw (code, mips_ra, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, eip));
212         }
213
214         /* jump to mono_get_lmf_addr here */
215         mips_load (code, mips_t9, mono_get_lmf_addr);
216         mips_jalr (code, mips_t9, mips_ra);
217         mips_nop (code);
218
219         /* v0 now points at the (MonoLMF **) for the current thread */
220
221         /* new_lmf->lmf_addr = lmf_addr -- useful when unwinding */
222         mips_sw (code, mips_v0, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
223
224         /* new_lmf->previous_lmf = *lmf_addr */
225         mips_lw (code, mips_at, mips_v0, 0);
226         mips_sw (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
227
228         /* *(lmf_addr) = new_lmf */
229         mips_addiu (code, mips_at, mips_sp, lmf);
230         mips_sw (code, mips_at, mips_v0, 0);
231
232         /*
233          * Now we're ready to call mips_magic_trampoline ().
234          */
235
236         /* Arg 1: pointer to registers so that the magic trampoline can
237          * access what we saved above
238          */
239         mips_addiu (code, mips_a0, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[0]));
240
241         /* Arg 2: code (next address to the instruction that called us) */
242         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
243                 mips_move (code, mips_a1, mips_zero);
244         } else {
245                 mips_lw (code, mips_a1, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
246         }
247
248         /* Arg 3: MonoMethod *method. */
249         if (tramp_type == MONO_TRAMPOLINE_GENERIC_CLASS_INIT)
250                 mips_lw (code, mips_a2, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs [mips_a0]));
251         else
252                 mips_lw (code, mips_a2, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, method));
253
254         /* Arg 4: Trampoline */
255         mips_move (code, mips_a3, mips_zero);
256                 
257         /* Now go to the trampoline */
258         tramp = (guint8*)mono_get_trampoline_func (tramp_type);
259         mips_load (code, mips_t9, (guint32)tramp);
260         mips_jalr (code, mips_t9, mips_ra);
261         mips_nop (code);
262                 
263         /* Code address is now in v0, move it to at */
264         mips_move (code, mips_at, mips_v0);
265
266         /*
267          * Now unwind the MonoLMF
268          */
269
270         /* t0 = current_lmf->previous_lmf */
271         mips_lw (code, mips_t0, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
272         /* t1 = lmf_addr */
273         mips_lw (code, mips_t1, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
274         /* (*lmf_addr) = previous_lmf */
275         mips_sw (code, mips_t0, mips_t1, 0);
276
277         /* Restore the callee-saved & argument registers */
278         for (i = 0; i < MONO_MAX_IREGS; i++) {
279                 if ((MONO_ARCH_CALLEE_SAVED_REGS | MONO_ARCH_CALLEE_REGS | MIPS_ARG_REGS) & (1 << i))
280                     MIPS_LW (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
281         }
282         for (i = 0; i < MONO_MAX_FREGS; i++)
283                 MIPS_LWC1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
284
285         /* Non-standard function epilogue. Instead of doing a proper
286          * return, we just jump to the compiled code.
287          */
288         /* Restore ra & stack pointer, and jump to the code */
289
290         if (tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH)
291                 mips_move (code, mips_v0, mips_at);
292         mips_lw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
293         mips_addiu (code, mips_sp, mips_sp, STACK);
294         if (MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type))
295                 mips_jr (code, mips_ra);
296         else
297                 mips_jr (code, mips_at);
298         mips_nop (code);
299
300         /* Flush instruction cache, since we've generated code */
301         mono_arch_flush_icache (buf, code - buf);
302         
303         /* Sanity check */
304         g_assert ((code - buf) <= max_code_len);
305
306         if (info) {
307                 tramp_name = mono_get_generic_trampoline_name (tramp_type);
308                 *info = mono_tramp_info_create (tramp_name, buf, code - buf, ji, unwind_ops);
309                 g_free (tramp_name);
310         }
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 (G_STRUCT_OFFSET (MonoVTable, runtime_generic_context)));
412                 mips_lw (code, mips_a1, mips_a0, G_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 }
482
483 gpointer
484 mono_arch_create_generic_class_init_trampoline (MonoTrampInfo **info, gboolean aot)
485 {
486         guint8 *tramp;
487         guint8 *code, *buf;
488         static int byte_offset = -1;
489         static guint8 bitmask;
490         guint8 *jump;
491         int tramp_size;
492         guint32 code_len;
493         GSList *unwind_ops = NULL;
494         MonoJumpInfo *ji = NULL;
495
496         tramp_size = 64;
497
498         code = buf = mono_global_codeman_reserve (tramp_size);
499
500         if (byte_offset < 0)
501                 mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask);
502
503         /* if (!(vtable->initialized)) */
504         mips_lbu (code, mips_at, MONO_ARCH_VTABLE_REG, byte_offset);
505         g_assert (!(bitmask & 0xffff0000));
506         mips_andi (code, mips_at, mips_at, bitmask);
507         jump = code;
508         mips_beq (code, mips_at, mips_zero, 0);
509         mips_nop (code);
510         /* Initialized case */
511         mips_jr (code, mips_ra);
512         mips_nop (code);
513
514         /* Uninitialized case */
515         mips_patch ((guint32*)jump, (guint32)code);
516
517         if (aot) {
518                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "specific_trampoline_generic_class_init");
519                 mips_load (code, mips_at, 0);
520                 mips_jr (code, mips_at);
521                 mips_nop (code);
522         } else {
523                 tramp = mono_arch_create_specific_trampoline (NULL, MONO_TRAMPOLINE_GENERIC_CLASS_INIT, mono_get_root_domain (), &code_len);
524                 mips_load (code, mips_at, tramp);
525                 mips_jr (code, mips_at);
526                 mips_nop (code);
527         }
528
529         mono_arch_flush_icache (buf, code - buf);
530
531         g_assert (code - buf <= tramp_size);
532
533         if (info)
534                 *info = mono_tramp_info_create ("generic_class_init_trampoline", buf, code - buf, ji, unwind_ops);
535
536         return buf;
537 }