Merge pull request #944 from ermshiperete/bug-novell-496138
[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         if (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
313         return buf;
314 }
315
316 gpointer
317 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
318 {
319         guint8 *code, *buf, *tramp;
320
321         tramp = mono_get_trampoline_code (tramp_type);
322
323         code = buf = mono_domain_code_reserve (domain, 32);
324
325         /* Prepare the jump to the generic trampoline code
326          * mono_arch_create_trampoline_code() knows we're putting this in t8
327          */
328         mips_load (code, mips_t8, arg1);
329         
330         /* Now jump to the generic trampoline code */
331         mips_load (code, mips_at, tramp);
332         mips_jr (code, mips_at);
333         mips_nop (code);
334
335         /* Flush instruction cache, since we've generated code */
336         mono_arch_flush_icache (buf, code - buf);
337
338         g_assert ((code - buf) <= 32);
339
340         if (code_len)
341                 *code_len = code - buf;
342
343         return buf;
344 }
345
346 gpointer
347 mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr)
348 {
349         guint8 *code, *start;
350         int buf_len;
351
352         MonoDomain *domain = mono_domain_get ();
353
354         buf_len = 24;
355
356         start = code = mono_domain_code_reserve (domain, buf_len);
357
358         mips_load (code, MONO_ARCH_RGCTX_REG, mrgctx);
359         mips_load (code, mips_at, addr);
360         mips_jr (code, mips_at);
361         mips_nop (code);
362
363         g_assert ((code - start) <= buf_len);
364
365         mono_arch_flush_icache (start, code - start);
366
367         return start;
368 }
369
370 gpointer
371 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
372 {
373         guint8 *tramp;
374         guint8 *code, *buf;
375         int tramp_size;
376         guint32 code_len;
377         guint8 **rgctx_null_jumps;
378         int depth, index;
379         int i, njumps;
380         gboolean mrgctx;
381         MonoJumpInfo *ji = NULL;
382         GSList *unwind_ops = NULL;
383
384         mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
385         index = MONO_RGCTX_SLOT_INDEX (slot);
386         if (mrgctx)
387                 index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
388         for (depth = 0; ; ++depth) {
389                 int size = mono_class_rgctx_get_array_size (depth, mrgctx);
390
391                 if (index < size - 1)
392                         break;
393                 index -= size - 1;
394         }
395
396         tramp_size = 64 + 16 * depth;
397
398         code = buf = mono_global_codeman_reserve (tramp_size);
399
400         mono_add_unwind_op_def_cfa (unwind_ops, code, buf, mips_sp, 0);
401
402         rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2));
403         njumps = 0;
404
405         /* The vtable/mrgctx is in a0 */
406         g_assert (MONO_ARCH_VTABLE_REG == mips_a0);
407         if (mrgctx) {
408                 /* get mrgctx ptr */
409                 mips_move (code, mips_a1, mips_a0);
410         } else {
411                 /* load rgctx ptr from vtable */
412                 g_assert (mips_is_imm16 (MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context)));
413                 mips_lw (code, mips_a1, mips_a0, MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context));
414                 /* is the rgctx ptr null? */
415                 /* if yes, jump to actual trampoline */
416                 rgctx_null_jumps [njumps ++] = code;
417                 mips_beq (code, mips_a1, mips_zero, 0);
418                 mips_nop (code);
419         }
420
421         for (i = 0; i < depth; ++i) {
422                 /* load ptr to next array */
423                 if (mrgctx && i == 0) {
424                         g_assert (mips_is_imm16 (MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT));
425                         mips_lw (code, mips_a1, mips_a1, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT);
426                 } else {
427                         mips_lw (code, mips_a1, mips_a1, 0);
428                 }
429                 /* is the ptr null? */
430                 /* if yes, jump to actual trampoline */
431                 rgctx_null_jumps [njumps ++] = code;
432                 mips_beq (code, mips_a1, mips_zero, 0);
433                 mips_nop (code);
434         }
435
436         /* fetch slot */
437         g_assert (mips_is_imm16 (sizeof (gpointer) * (index + 1)));
438         mips_lw (code, mips_a1, mips_a1, sizeof (gpointer) * (index + 1));
439         /* is the slot 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         /* otherwise return, result is in R1 */
445         mips_move (code, mips_v0, mips_a1);
446         mips_jr (code, mips_ra);
447         mips_nop (code);
448
449         g_assert (njumps <= depth + 2);
450         for (i = 0; i < njumps; ++i)
451                 mips_patch ((guint32*)rgctx_null_jumps [i], (guint32)code);
452
453         g_free (rgctx_null_jumps);
454
455         /* Slowpath */
456
457         /* The vtable/mrgctx is still in a0 */
458
459         if (aot) {
460                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", slot));
461                 mips_load (code, mips_at, 0);
462                 mips_jr (code, mips_at);
463                 mips_nop (code);
464         } else {
465                 tramp = mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), &code_len);
466                 mips_load (code, mips_at, tramp);
467                 mips_jr (code, mips_at);
468                 mips_nop (code);
469         }
470
471         mono_arch_flush_icache (buf, code - buf);
472
473         g_assert (code - buf <= tramp_size);
474
475         if (info) {
476                 char *name = mono_get_rgctx_fetch_trampoline_name (slot);
477                 *info = mono_tramp_info_create (name, buf, code - buf, ji, unwind_ops);
478                 g_free (name);
479         }
480
481         return buf;
482 }
483
484 gpointer
485 mono_arch_create_generic_class_init_trampoline (MonoTrampInfo **info, gboolean aot)
486 {
487         guint8 *tramp;
488         guint8 *code, *buf;
489         static int byte_offset = -1;
490         static guint8 bitmask;
491         guint8 *jump;
492         int tramp_size;
493         guint32 code_len;
494         GSList *unwind_ops = NULL;
495         MonoJumpInfo *ji = NULL;
496
497         tramp_size = 64;
498
499         code = buf = mono_global_codeman_reserve (tramp_size);
500
501         if (byte_offset < 0)
502                 mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask);
503
504         /* if (!(vtable->initialized)) */
505         mips_lbu (code, mips_at, MONO_ARCH_VTABLE_REG, byte_offset);
506         g_assert (!(bitmask & 0xffff0000));
507         mips_andi (code, mips_at, mips_at, bitmask);
508         jump = code;
509         mips_beq (code, mips_at, mips_zero, 0);
510         mips_nop (code);
511         /* Initialized case */
512         mips_jr (code, mips_ra);
513         mips_nop (code);
514
515         /* Uninitialized case */
516         mips_patch ((guint32*)jump, (guint32)code);
517
518         if (aot) {
519                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "specific_trampoline_generic_class_init");
520                 mips_load (code, mips_at, 0);
521                 mips_jr (code, mips_at);
522                 mips_nop (code);
523         } else {
524                 tramp = mono_arch_create_specific_trampoline (NULL, MONO_TRAMPOLINE_GENERIC_CLASS_INIT, mono_get_root_domain (), &code_len);
525                 mips_load (code, mips_at, tramp);
526                 mips_jr (code, mips_at);
527                 mips_nop (code);
528         }
529
530         mono_arch_flush_icache (buf, code - buf);
531
532         g_assert (code - buf <= tramp_size);
533
534         if (info)
535                 *info = mono_tramp_info_create ("generic_class_init_trampoline", buf, code - buf, ji, unwind_ops);
536
537         return buf;
538 }