Simplify the arm prolog/epilog generation by not saving the sp to the stack, its...
[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         int this_pos = mips_a0;
43         MonoDomain *domain = mono_domain_get ();
44
45         if (MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret))
46                 this_pos = mips_a1;
47             
48         start = code = mono_domain_code_reserve (domain, 20);
49
50         mips_load (code, mips_t9, addr);
51         mips_addiu (code, this_pos, this_pos, sizeof (MonoObject));
52         mips_jr (code, mips_t9);
53         mips_nop (code);
54
55         mono_arch_flush_icache (start, code - start);
56         g_assert ((code - start) <= 20);
57         /*g_print ("unbox trampoline at %d for %s:%s\n", this_pos, m->klass->name, m->name);
58         g_print ("unbox code is at %p for method at %p\n", start, addr);*/
59
60         return start;
61 }
62
63 void
64 mono_arch_patch_callsite (guint8 *method_start, guint8 *orig_code, guint8 *addr)
65 {
66         guint32 *code = (guint32*)orig_code;
67
68         /* Locate the address of the method-specific trampoline.
69         The call using the vtable slot that took the processing flow to
70         'arch_create_jit_trampoline' looks something like one of these:
71
72                 jal     XXXXYYYY
73                 nop
74
75                 lui     t9, XXXX
76                 addiu   t9, YYYY
77                 jalr    t9
78                 nop
79
80         On entry, 'code' points just after one of the above sequences.
81         */
82         
83         /* The jal case */
84         if ((code[-2] >> 26) == 0x03) {
85                 //g_print ("direct patching\n");
86                 mips_patch ((code-2), (gsize)addr);
87                 return;
88         }
89         /* Look for the jalr */
90         if ((code[-2] & 0xfc1f003f) == 0x00000009) {
91                 /* The lui / addiu / jalr case */
92                 if ((code [-4] >> 26) == 0x0f && (code [-3] >> 26) == 0x09
93                     && (code [-2] >> 26) == 0) {
94                         mips_patch ((code-4), (gsize)addr);
95                         return;
96                 }
97         }
98         g_print("error: bad patch at 0x%08x\n", code);
99         g_assert_not_reached ();
100 }
101
102 void
103 mono_arch_patch_plt_entry (guint8 *code, gpointer *got, mgreg_t *regs, guint8 *addr)
104 {
105         g_assert_not_reached ();
106 }
107
108 /* Stack size for trampoline function 
109  * MIPS_MINIMAL_STACK_SIZE + 16 (args + alignment to mips_magic_trampoline)
110  * + MonoLMF + 14 fp regs + 13 gregs + alignment
111  * #define STACK (MIPS_MINIMAL_STACK_SIZE + 4 * sizeof (gulong) + sizeof (MonoLMF) + 14 * sizeof (double) + 13 * (sizeof (gulong)))
112  * STACK would be 444 for 32 bit darwin
113  */
114
115 #define STACK (4*IREG_SIZE + 8 + sizeof(MonoLMF) + 32)
116
117
118 gpointer
119 mono_arch_get_vcall_slot (guint8 *code_ptr, mgreg_t *regs, int *displacement)
120 {
121         char *o = NULL;
122         char *vtable = NULL;
123         int reg, offset = 0;
124         guint32 base = 0;
125         guint32 *code = (guint32*)code_ptr;
126         char *sp;
127
128         /* On MIPS, we are passed sp instead of the register array */
129         sp = (char*)regs;
130
131         //printf ("mips_magic_trampoline: 0x%08x @ 0x%0x\n", *(code-2), code-2);
132         
133         /* The jal case */
134         if ((code[-2] >> 26) == 0x03)
135                 return NULL;
136
137         /* Sanity check: look for the jalr */
138         g_assert((code[-2] & 0xfc1f003f) == 0x00000009);
139
140         reg = (code[-2] >> 21) & 0x1f;
141
142         //printf ("mips_magic_trampoline: jalr @ 0x%0x, w/ reg %d\n", code-2, reg);
143
144         /* The lui / addiu / jalr case */
145         if ((code [-4] >> 26) == 0x0f && (code [-3] >> 26) == 0x09 && (code [-2] >> 26) == 0) {
146                 return NULL;
147         }
148
149         /* Probably a vtable lookup */
150
151         /* Walk backwards to find 'lw reg,XX(base)' */
152         for(; --code;) {
153                 guint32 mask = (0x3f << 26) | (0x1f << 16);
154                 guint32 match = (0x23 << 26) | (reg << 16);
155                 if((*code & mask) == match) {
156                         gint16 soff;
157                         gint reg_offset;
158
159                         /* lw reg,XX(base) */
160                         base = (*code >> 21) & 0x1f;
161                         soff = (*code & 0xffff);
162                         if (soff & 0x8000)
163                                 soff |= 0xffff0000;
164                         offset = soff;
165                         if (1) {
166                                 MonoLMF *lmf = (MonoLMF*)((char *)regs + 12*IREG_SIZE);
167                                 g_assert (lmf->magic == MIPS_LMF_MAGIC2);
168                                 o = (gpointer)lmf->iregs [base];
169                         }
170                         else {
171                                 o = (gpointer) regs [base];
172                         }
173                         break;
174                 }
175         }
176         *displacement = offset;
177         return o;
178 }
179
180 void
181 mono_arch_nullify_plt_entry (guint8 *code, mgreg_t *regs)
182 {
183         if (mono_aot_only && !nullified_class_init_trampoline)
184                 nullified_class_init_trampoline = mono_aot_get_trampoline ("nullified_class_init_trampoline");
185
186         mono_arch_patch_plt_entry (code, NULL, regs, nullified_class_init_trampoline);
187 }
188
189 void
190 mono_arch_nullify_class_init_trampoline (guint8 *code, mgreg_t *regs)
191 {
192         guint32 *code32 = (guint32*)code;
193
194         /* back up to the jal/jalr instruction */
195         code32 -= 2;
196
197         /* Check for jal/jalr -- and NOP it out */
198         if ((((*code32)&0xfc000000) == 0x0c000000)
199             || (((*code32)&0xfc1f003f) == 0x00000009)) {
200                 mips_nop (code32);
201                 mono_arch_flush_icache ((gpointer)(code32 - 1), 4);
202                 return;
203         }
204         g_assert_not_reached ();
205 }
206
207 gpointer
208 mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo **info)
209 {
210         guint8 *buf, *code;
211
212         code = buf = mono_global_codeman_reserve (16);
213
214         mips_jr (code, mips_ra);
215         mips_nop (code);
216
217         mono_arch_flush_icache (buf, code - buf);
218
219         if (info)
220                 *info = mono_tramp_info_create (g_strdup_printf ("nullified_class_init_trampoline"), buf, code - buf, NULL, NULL);
221
222         return buf;
223 }
224
225 /*
226  * Stack frame description when the generic trampoline is called.
227  * caller frame
228  * --------------------
229  *  MonoLMF
230  *  -------------------
231  *  Saved FP registers 0-13
232  *  -------------------
233  *  Saved general registers 0-12
234  *  -------------------
235  *  param area for 3 args to mips_magic_trampoline
236  *  -------------------
237  *  linkage area
238  *  -------------------
239  */
240 guchar*
241 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
242 {
243         guint8 *buf, *tramp, *code = NULL;
244         int i, lmf;
245         GSList *unwind_ops = NULL;
246         MonoJumpInfo *ji = NULL;
247         int max_code_len = 768;
248
249         /* AOT not supported on MIPS yet */
250         g_assert (!aot);
251
252         /* Now we'll create in 'buf' the MIPS trampoline code. This
253            is the trampoline code common to all methods  */
254                 
255         code = buf = mono_global_codeman_reserve (max_code_len);
256                 
257         /* Allocate the stack frame, and save the return address */
258         mips_addiu (code, mips_sp, mips_sp, -STACK);
259         mips_sw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
260
261         /* we build the MonoLMF structure on the stack - see mini-mips.h */
262         /* offset of MonoLMF from sp */
263         lmf = STACK - sizeof (MonoLMF) - 8;
264
265         for (i = 0; i < MONO_MAX_IREGS; i++)
266                 MIPS_SW (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
267         for (i = 0; i < MONO_MAX_FREGS; i++)
268                 MIPS_SWC1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
269
270         /* Set the magic number */
271         mips_load_const (code, mips_at, MIPS_LMF_MAGIC2);
272         mips_sw (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, magic));
273
274         /* save method info (it was in t8) */
275         mips_sw (code, mips_t8, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, method));
276
277         /* save frame pointer (caller fp) */
278         MIPS_SW (code, mips_fp, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, ebp));
279
280         /* save the IP (caller ip) */
281         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
282                 mips_sw (code, mips_zero, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, eip));
283         } else {
284                 mips_sw (code, mips_ra, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, eip));
285         }
286
287         /* jump to mono_get_lmf_addr here */
288         mips_load (code, mips_t9, mono_get_lmf_addr);
289         mips_jalr (code, mips_t9, mips_ra);
290         mips_nop (code);
291
292         /* v0 now points at the (MonoLMF **) for the current thread */
293
294         /* new_lmf->lmf_addr = lmf_addr -- useful when unwinding */
295         mips_sw (code, mips_v0, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
296
297         /* new_lmf->previous_lmf = *lmf_addr */
298         mips_lw (code, mips_at, mips_v0, 0);
299         mips_sw (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
300
301         /* *(lmf_addr) = new_lmf */
302         mips_addiu (code, mips_at, mips_sp, lmf);
303         mips_sw (code, mips_at, mips_v0, 0);
304
305         /*
306          * Now we're ready to call mips_magic_trampoline ().
307          */
308
309         /* Arg 1: pointer to registers so that the magic trampoline can
310          * access what we saved above
311          */
312         mips_addiu (code, mips_a0, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[0]));
313
314         /* Arg 2: code (next address to the instruction that called us) */
315         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
316                 mips_move (code, mips_a1, mips_zero);
317         } else {
318                 mips_lw (code, mips_a1, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
319         }
320
321         /* Arg 3: MonoMethod *method. */
322         mips_lw (code, mips_a2, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, method));
323
324         /* Arg 4: Trampoline */
325         mips_move (code, mips_a3, mips_zero);
326                 
327         /* Now go to the trampoline */
328         tramp = (guint8*)mono_get_trampoline_func (tramp_type);
329         mips_load (code, mips_t9, (guint32)tramp);
330         mips_jalr (code, mips_t9, mips_ra);
331         mips_nop (code);
332                 
333         /* Code address is now in v0, move it to at */
334         mips_move (code, mips_at, mips_v0);
335
336         /*
337          * Now unwind the MonoLMF
338          */
339
340         /* t0 = current_lmf->previous_lmf */
341         mips_lw (code, mips_t0, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
342         /* t1 = lmf_addr */
343         mips_lw (code, mips_t1, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
344         /* (*lmf_addr) = previous_lmf */
345         mips_sw (code, mips_t0, mips_t1, 0);
346
347         /* Restore the callee-saved & argument registers */
348         for (i = 0; i < MONO_MAX_IREGS; i++) {
349                 if ((MONO_ARCH_CALLEE_SAVED_REGS | MONO_ARCH_CALLEE_REGS | MIPS_ARG_REGS) & (1 << i))
350                     MIPS_LW (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
351         }
352         for (i = 0; i < MONO_MAX_FREGS; i++)
353                 MIPS_LWC1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
354
355         /* Non-standard function epilogue. Instead of doing a proper
356          * return, we just jump to the compiled code.
357          */
358         /* Restore ra & stack pointer, and jump to the code */
359
360         mips_lw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
361         mips_addiu (code, mips_sp, mips_sp, STACK);
362         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
363                 mips_jr (code, mips_ra);
364         else
365                 mips_jr (code, mips_at);
366         mips_nop (code);
367
368         /* Flush instruction cache, since we've generated code */
369         mono_arch_flush_icache (buf, code - buf);
370         
371         /* Sanity check */
372         g_assert ((code - buf) <= max_code_len);
373
374         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
375                 /* Initialize the nullified class init trampoline used in the AOT case */
376                 nullified_class_init_trampoline = mono_arch_get_nullified_class_init_trampoline (NULL);
377
378         if (info)
379                 *info = mono_tramp_info_create (mono_get_generic_trampoline_name (tramp_type), buf, code - buf, ji, unwind_ops);
380
381         return buf;
382 }
383
384 gpointer
385 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
386 {
387         guint8 *code, *buf, *tramp;
388
389         tramp = mono_get_trampoline_code (tramp_type);
390
391         code = buf = mono_domain_code_reserve (domain, 32);
392
393         /* Prepare the jump to the generic trampoline code
394          * mono_arch_create_trampoline_code() knows we're putting this in t8
395          */
396         mips_load (code, mips_t8, arg1);
397         
398         /* Now jump to the generic trampoline code */
399         mips_load (code, mips_at, tramp);
400         mips_jr (code, mips_at);
401         mips_nop (code);
402
403         /* Flush instruction cache, since we've generated code */
404         mono_arch_flush_icache (buf, code - buf);
405
406         g_assert ((code - buf) <= 32);
407
408         if (code_len)
409                 *code_len = code - buf;
410
411         return buf;
412 }
413
414 gpointer
415 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
416 {
417         /* FIXME: implement! */
418         g_assert_not_reached ();
419         return NULL;
420 }
421
422 gpointer
423 mono_arch_create_generic_class_init_trampoline (MonoTrampInfo **info, gboolean aot)
424 {
425         /* FIXME: implement! */
426         g_assert_not_reached ();
427         return NULL;
428 }