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