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