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