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