Merge pull request #409 from Alkarex/patch-1
[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 ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
113
114 #define STACK (int)(ALIGN_TO(4*IREG_SIZE + 8 + sizeof(MonoLMF) + 32, 8))
115
116 void
117 mono_arch_nullify_plt_entry (guint8 *code, mgreg_t *regs)
118 {
119         if (mono_aot_only && !nullified_class_init_trampoline)
120                 nullified_class_init_trampoline = mono_aot_get_trampoline ("nullified_class_init_trampoline");
121
122         mono_arch_patch_plt_entry (code, NULL, regs, nullified_class_init_trampoline);
123 }
124
125 void
126 mono_arch_nullify_class_init_trampoline (guint8 *code, mgreg_t *regs)
127 {
128         guint32 *code32 = (guint32*)code;
129
130         /* back up to the jal/jalr instruction */
131         code32 -= 2;
132
133         /* Check for jal/jalr -- and NOP it out */
134         if ((((*code32)&0xfc000000) == 0x0c000000)
135             || (((*code32)&0xfc1f003f) == 0x00000009)) {
136                 mips_nop (code32);
137                 mono_arch_flush_icache ((gpointer)(code32 - 1), 4);
138                 return;
139         }
140         g_assert_not_reached ();
141 }
142
143 gpointer
144 mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo **info)
145 {
146         guint8 *buf, *code;
147
148         code = buf = mono_global_codeman_reserve (16);
149
150         mips_jr (code, mips_ra);
151         mips_nop (code);
152
153         mono_arch_flush_icache (buf, code - buf);
154
155         if (info)
156                 *info = mono_tramp_info_create (g_strdup_printf ("nullified_class_init_trampoline"), buf, code - buf, NULL, NULL);
157
158         return buf;
159 }
160
161 /*
162  * Stack frame description when the generic trampoline is called.
163  * caller frame
164  * --------------------
165  *  MonoLMF
166  *  -------------------
167  *  Saved FP registers 0-13
168  *  -------------------
169  *  Saved general registers 0-12
170  *  -------------------
171  *  param area for 3 args to mips_magic_trampoline
172  *  -------------------
173  *  linkage area
174  *  -------------------
175  */
176 guchar*
177 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
178 {
179         guint8 *buf, *tramp, *code = NULL;
180         int i, lmf;
181         GSList *unwind_ops = NULL;
182         MonoJumpInfo *ji = NULL;
183         int max_code_len = 768;
184
185         /* AOT not supported on MIPS yet */
186         g_assert (!aot);
187
188         /* Now we'll create in 'buf' the MIPS trampoline code. This
189            is the trampoline code common to all methods  */
190
191         code = buf = mono_global_codeman_reserve (max_code_len);
192
193         /* Allocate the stack frame, and save the return address */
194         mips_addiu (code, mips_sp, mips_sp, -STACK);
195         mips_sw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
196
197         /* we build the MonoLMF structure on the stack - see mini-mips.h */
198         /* offset of MonoLMF from sp */
199         lmf = STACK - sizeof (MonoLMF) - 8;
200
201         for (i = 0; i < MONO_MAX_IREGS; i++)
202                 MIPS_SW (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
203         for (i = 0; i < MONO_MAX_FREGS; i++)
204                 MIPS_SWC1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
205
206         /* Set the magic number */
207         mips_load_const (code, mips_at, MIPS_LMF_MAGIC2);
208         mips_sw (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, magic));
209
210         /* Save caller sp */
211         mips_addiu (code, mips_at, mips_sp, STACK);
212         MIPS_SW (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[mips_sp]));
213
214         /* save method info (it was in t8) */
215         mips_sw (code, mips_t8, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, method));
216
217         /* save the IP (caller ip) */
218         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
219                 mips_sw (code, mips_zero, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, eip));
220         } else {
221                 mips_sw (code, mips_ra, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, eip));
222         }
223
224         /* jump to mono_get_lmf_addr here */
225         mips_load (code, mips_t9, mono_get_lmf_addr);
226         mips_jalr (code, mips_t9, mips_ra);
227         mips_nop (code);
228
229         /* v0 now points at the (MonoLMF **) for the current thread */
230
231         /* new_lmf->lmf_addr = lmf_addr -- useful when unwinding */
232         mips_sw (code, mips_v0, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
233
234         /* new_lmf->previous_lmf = *lmf_addr */
235         mips_lw (code, mips_at, mips_v0, 0);
236         mips_sw (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
237
238         /* *(lmf_addr) = new_lmf */
239         mips_addiu (code, mips_at, mips_sp, lmf);
240         mips_sw (code, mips_at, mips_v0, 0);
241
242         /*
243          * Now we're ready to call mips_magic_trampoline ().
244          */
245
246         /* Arg 1: pointer to registers so that the magic trampoline can
247          * access what we saved above
248          */
249         mips_addiu (code, mips_a0, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[0]));
250
251         /* Arg 2: code (next address to the instruction that called us) */
252         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
253                 mips_move (code, mips_a1, mips_zero);
254         } else {
255                 mips_lw (code, mips_a1, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
256         }
257
258         /* Arg 3: MonoMethod *method. */
259         if (tramp_type == MONO_TRAMPOLINE_GENERIC_CLASS_INIT)
260                 mips_lw (code, mips_a2, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs [mips_a0]));
261         else
262                 mips_lw (code, mips_a2, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, method));
263
264         /* Arg 4: Trampoline */
265         mips_move (code, mips_a3, mips_zero);
266                 
267         /* Now go to the trampoline */
268         tramp = (guint8*)mono_get_trampoline_func (tramp_type);
269         mips_load (code, mips_t9, (guint32)tramp);
270         mips_jalr (code, mips_t9, mips_ra);
271         mips_nop (code);
272                 
273         /* Code address is now in v0, move it to at */
274         mips_move (code, mips_at, mips_v0);
275
276         /*
277          * Now unwind the MonoLMF
278          */
279
280         /* t0 = current_lmf->previous_lmf */
281         mips_lw (code, mips_t0, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
282         /* t1 = lmf_addr */
283         mips_lw (code, mips_t1, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
284         /* (*lmf_addr) = previous_lmf */
285         mips_sw (code, mips_t0, mips_t1, 0);
286
287         /* Restore the callee-saved & argument registers */
288         for (i = 0; i < MONO_MAX_IREGS; i++) {
289                 if ((MONO_ARCH_CALLEE_SAVED_REGS | MONO_ARCH_CALLEE_REGS | MIPS_ARG_REGS) & (1 << i))
290                     MIPS_LW (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
291         }
292         for (i = 0; i < MONO_MAX_FREGS; i++)
293                 MIPS_LWC1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
294
295         /* Non-standard function epilogue. Instead of doing a proper
296          * return, we just jump to the compiled code.
297          */
298         /* Restore ra & stack pointer, and jump to the code */
299
300         if (tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH)
301                 mips_move (code, mips_v0, mips_at);
302         mips_lw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
303         mips_addiu (code, mips_sp, mips_sp, STACK);
304         if (MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type))
305                 mips_jr (code, mips_ra);
306         else
307                 mips_jr (code, mips_at);
308         mips_nop (code);
309
310         /* Flush instruction cache, since we've generated code */
311         mono_arch_flush_icache (buf, code - buf);
312         
313         /* Sanity check */
314         g_assert ((code - buf) <= max_code_len);
315
316         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
317                 /* Initialize the nullified class init trampoline used in the AOT case */
318                 nullified_class_init_trampoline = mono_arch_get_nullified_class_init_trampoline (NULL);
319
320         if (info)
321                 *info = mono_tramp_info_create (mono_get_generic_trampoline_name (tramp_type), buf, code - buf, ji, unwind_ops);
322
323         return buf;
324 }
325
326 gpointer
327 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
328 {
329         guint8 *code, *buf, *tramp;
330
331         tramp = mono_get_trampoline_code (tramp_type);
332
333         code = buf = mono_domain_code_reserve (domain, 32);
334
335         /* Prepare the jump to the generic trampoline code
336          * mono_arch_create_trampoline_code() knows we're putting this in t8
337          */
338         mips_load (code, mips_t8, arg1);
339         
340         /* Now jump to the generic trampoline code */
341         mips_load (code, mips_at, tramp);
342         mips_jr (code, mips_at);
343         mips_nop (code);
344
345         /* Flush instruction cache, since we've generated code */
346         mono_arch_flush_icache (buf, code - buf);
347
348         g_assert ((code - buf) <= 32);
349
350         if (code_len)
351                 *code_len = code - buf;
352
353         return buf;
354 }
355
356 gpointer
357 mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr)
358 {
359         guint8 *code, *start;
360         int buf_len;
361
362         MonoDomain *domain = mono_domain_get ();
363
364         buf_len = 24;
365
366         start = code = mono_domain_code_reserve (domain, buf_len);
367
368         mips_load (code, MONO_ARCH_RGCTX_REG, mrgctx);
369         mips_load (code, mips_at, addr);
370         mips_jr (code, mips_at);
371         mips_nop (code);
372
373         g_assert ((code - start) <= buf_len);
374
375         mono_arch_flush_icache (start, code - start);
376
377         return start;
378 }
379
380 gpointer
381 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
382 {
383         guint8 *tramp;
384         guint8 *code, *buf;
385         int tramp_size;
386         guint32 code_len;
387         guint8 **rgctx_null_jumps;
388         int depth, index;
389         int i, njumps;
390         gboolean mrgctx;
391         MonoJumpInfo *ji = NULL;
392         GSList *unwind_ops = NULL;
393
394         mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
395         index = MONO_RGCTX_SLOT_INDEX (slot);
396         if (mrgctx)
397                 index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
398         for (depth = 0; ; ++depth) {
399                 int size = mono_class_rgctx_get_array_size (depth, mrgctx);
400
401                 if (index < size - 1)
402                         break;
403                 index -= size - 1;
404         }
405
406         tramp_size = 64 + 16 * depth;
407
408         code = buf = mono_global_codeman_reserve (tramp_size);
409
410         mono_add_unwind_op_def_cfa (unwind_ops, code, buf, mips_sp, 0);
411
412         rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2));
413         njumps = 0;
414
415         /* The vtable/mrgctx is in a0 */
416         g_assert (MONO_ARCH_VTABLE_REG == mips_a0);
417         if (mrgctx) {
418                 /* get mrgctx ptr */
419                 mips_move (code, mips_a1, mips_a0);
420         } else {
421                 /* load rgctx ptr from vtable */
422                 g_assert (mips_is_imm16 (G_STRUCT_OFFSET (MonoVTable, runtime_generic_context)));
423                 mips_lw (code, mips_a1, mips_a0, G_STRUCT_OFFSET (MonoVTable, runtime_generic_context));
424                 /* is the rgctx ptr null? */
425                 /* if yes, jump to actual trampoline */
426                 rgctx_null_jumps [njumps ++] = code;
427                 mips_beq (code, mips_a1, mips_zero, 0);
428                 mips_nop (code);
429         }
430
431         for (i = 0; i < depth; ++i) {
432                 /* load ptr to next array */
433                 if (mrgctx && i == 0) {
434                         g_assert (mips_is_imm16 (MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT));
435                         mips_lw (code, mips_a1, mips_a1, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT);
436                 } else {
437                         mips_lw (code, mips_a1, mips_a1, 0);
438                 }
439                 /* is the ptr null? */
440                 /* if yes, jump to actual trampoline */
441                 rgctx_null_jumps [njumps ++] = code;
442                 mips_beq (code, mips_a1, mips_zero, 0);
443                 mips_nop (code);
444         }
445
446         /* fetch slot */
447         g_assert (mips_is_imm16 (sizeof (gpointer) * (index + 1)));
448         mips_lw (code, mips_a1, mips_a1, sizeof (gpointer) * (index + 1));
449         /* is the slot null? */
450         /* if yes, jump to actual trampoline */
451         rgctx_null_jumps [njumps ++] = code;
452         mips_beq (code, mips_a1, mips_zero, 0);
453         mips_nop (code);
454         /* otherwise return, result is in R1 */
455         mips_move (code, mips_v0, mips_a1);
456         mips_jr (code, mips_ra);
457         mips_nop (code);
458
459         g_assert (njumps <= depth + 2);
460         for (i = 0; i < njumps; ++i)
461                 mips_patch ((guint32*)rgctx_null_jumps [i], (guint32)code);
462
463         g_free (rgctx_null_jumps);
464
465         /* Slowpath */
466
467         /* The vtable/mrgctx is still in a0 */
468
469         if (aot) {
470                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", slot));
471                 mips_load (code, mips_at, 0);
472                 mips_jr (code, mips_at);
473                 mips_nop (code);
474         } else {
475                 tramp = mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), &code_len);
476                 mips_load (code, mips_at, tramp);
477                 mips_jr (code, mips_at);
478                 mips_nop (code);
479         }
480
481         mono_arch_flush_icache (buf, code - buf);
482
483         g_assert (code - buf <= tramp_size);
484
485         if (info)
486                 *info = mono_tramp_info_create (mono_get_rgctx_fetch_trampoline_name (slot), buf, code - buf, ji, unwind_ops);
487
488         return buf;
489 }
490
491 gpointer
492 mono_arch_create_generic_class_init_trampoline (MonoTrampInfo **info, gboolean aot)
493 {
494         guint8 *tramp;
495         guint8 *code, *buf;
496         static int byte_offset = -1;
497         static guint8 bitmask;
498         guint8 *jump;
499         int tramp_size;
500         guint32 code_len;
501         GSList *unwind_ops = NULL;
502         MonoJumpInfo *ji = NULL;
503
504         tramp_size = 64;
505
506         code = buf = mono_global_codeman_reserve (tramp_size);
507
508         if (byte_offset < 0)
509                 mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask);
510
511         /* if (!(vtable->initialized)) */
512         mips_lbu (code, mips_at, MONO_ARCH_VTABLE_REG, byte_offset);
513         g_assert (!(bitmask & 0xffff0000));
514         mips_andi (code, mips_at, mips_at, bitmask);
515         jump = code;
516         mips_beq (code, mips_at, mips_zero, 0);
517         mips_nop (code);
518         /* Initialized case */
519         mips_jr (code, mips_ra);
520         mips_nop (code);
521
522         /* Uninitialized case */
523         mips_patch ((guint32*)jump, (guint32)code);
524
525         if (aot) {
526                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "specific_trampoline_generic_class_init");
527                 mips_load (code, mips_at, 0);
528                 mips_jr (code, mips_at);
529                 mips_nop (code);
530         } else {
531                 tramp = mono_arch_create_specific_trampoline (NULL, MONO_TRAMPOLINE_GENERIC_CLASS_INIT, mono_get_root_domain (), &code_len);
532                 mips_load (code, mips_at, tramp);
533                 mips_jr (code, mips_at);
534                 mips_nop (code);
535         }
536
537         mono_arch_flush_icache (buf, code - buf);
538
539         g_assert (code - buf <= tramp_size);
540
541         if (info)
542                 *info = mono_tramp_info_create (g_strdup_printf ("generic_class_init_trampoline"), buf, code - buf, ji, unwind_ops);
543
544         return buf;
545 }