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