Normalize line endings.
[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, gpointer *got, mgreg_t *regs, 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, mgreg_t *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 void
180 mono_arch_nullify_plt_entry (guint8 *code, mgreg_t *regs)
181 {
182         g_assert_not_reached ();
183 }
184
185 void
186 mono_arch_nullify_class_init_trampoline (guint8 *code, mgreg_t *regs)
187 {
188         guint32 *code32 = (guint32*)code;
189
190         /* back up to the jal/jalr instruction */
191         code32 -= 2;
192
193         /* Check for jal/jalr -- and NOP it out */
194         if ((((*code32)&0xfc000000) == 0x0c000000)
195             || (((*code32)&0xfc1f003f) == 0x00000009)) {
196                 mips_nop (code32);
197                 mono_arch_flush_icache ((gpointer)(code32 - 1), 4);
198                 return;
199         } else {
200                 g_assert_not_reached ();
201         }
202 }
203
204 /*
205  * Stack frame description when the generic trampoline is called.
206  * caller frame
207  * --------------------
208  *  MonoLMF
209  *  -------------------
210  *  Saved FP registers 0-13
211  *  -------------------
212  *  Saved general registers 0-12
213  *  -------------------
214  *  param area for 3 args to mips_magic_trampoline
215  *  -------------------
216  *  linkage area
217  *  -------------------
218  */
219 guchar*
220 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
221 {
222         guint8 *buf, *tramp, *code = NULL;
223         int i, lmf;
224         int max_code_len = 768;
225
226         g_assert (!aot);
227         if (info)
228                 *info = NULL;
229
230         /* Now we'll create in 'buf' the MIPS trampoline code. This
231            is the trampoline code common to all methods  */
232                 
233         code = buf = mono_global_codeman_reserve (max_code_len);
234                 
235         /* Allocate the stack frame, and save the return address */
236         mips_addiu (code, mips_sp, mips_sp, -STACK);
237         mips_sw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
238
239         /* we build the MonoLMF structure on the stack - see mini-mips.h */
240         /* offset of MonoLMF from sp */
241         lmf = STACK - sizeof (MonoLMF) - 8;
242
243         for (i = 0; i < MONO_MAX_IREGS; i++)
244                 MIPS_SW (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
245         for (i = 0; i < MONO_MAX_FREGS; i++)
246                 MIPS_SWC1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
247
248         /* Set the magic number */
249         mips_load_const (code, mips_at, MIPS_LMF_MAGIC2);
250         mips_sw (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, magic));
251
252         /* save method info (it was in t8) */
253         mips_sw (code, mips_t8, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, method));
254
255         /* save frame pointer (caller fp) */
256         MIPS_SW (code, mips_fp, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, ebp));
257
258         /* save the IP (caller ip) */
259         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
260                 mips_sw (code, mips_zero, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, eip));
261         } else {
262                 mips_sw (code, mips_ra, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, eip));
263         }
264
265         /* jump to mono_get_lmf_addr here */
266         mips_load (code, mips_t9, mono_get_lmf_addr);
267         mips_jalr (code, mips_t9, mips_ra);
268         mips_nop (code);
269
270         /* v0 now points at the (MonoLMF **) for the current thread */
271
272         /* new_lmf->lmf_addr = lmf_addr -- useful when unwinding */
273         mips_sw (code, mips_v0, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
274
275         /* new_lmf->previous_lmf = *lmf_addr */
276         mips_lw (code, mips_at, mips_v0, 0);
277         mips_sw (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
278
279         /* *(lmf_addr) = new_lmf */
280         mips_addiu (code, mips_at, mips_sp, lmf);
281         mips_sw (code, mips_at, mips_v0, 0);
282
283         /*
284          * Now we're ready to call mips_magic_trampoline ().
285          */
286
287         /* Arg 1: stack pointer so that the magic trampoline can access the
288          * registers we saved above
289          */
290         mips_move (code, mips_a0, mips_sp);
291                 
292         /* Arg 2: code (next address to the instruction that called us) */
293         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
294                 mips_move (code, mips_a1, mips_zero);
295         } else {
296                 mips_lw (code, mips_a1, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
297         }
298
299         /* Arg 3: MonoMethod *method. */
300         mips_lw (code, mips_a2, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, method));
301
302         /* Arg 4: Trampoline */
303         mips_move (code, mips_a3, mips_zero);
304                 
305         /* Now go to the trampoline */
306         tramp = (guint8*)mono_get_trampoline_func (tramp_type);
307         mips_load (code, mips_t9, (guint32)tramp);
308         mips_jalr (code, mips_t9, mips_ra);
309         mips_nop (code);
310                 
311         /* Code address is now in v0, move it to at */
312         mips_move (code, mips_at, mips_v0);
313
314         /*
315          * Now unwind the MonoLMF
316          */
317
318         /* t0 = current_lmf->previous_lmf */
319         mips_lw (code, mips_t0, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
320         /* t1 = lmf_addr */
321         mips_lw (code, mips_t1, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
322         /* (*lmf_addr) = previous_lmf */
323         mips_sw (code, mips_t0, mips_t1, 0);
324
325         /* Restore the callee-saved & argument registers */
326         for (i = 0; i < MONO_MAX_IREGS; i++) {
327                 if ((MONO_ARCH_CALLEE_SAVED_REGS | MONO_ARCH_CALLEE_REGS | MIPS_ARG_REGS) & (1 << i))
328                     MIPS_LW (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
329         }
330         for (i = 0; i < MONO_MAX_FREGS; i++)
331                 MIPS_LWC1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
332
333         /* Non-standard function epilogue. Instead of doing a proper
334          * return, we just jump to the compiled code.
335          */
336         /* Restore ra & stack pointer, and jump to the code */
337
338         mips_lw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
339         mips_addiu (code, mips_sp, mips_sp, STACK);
340         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
341                 mips_jr (code, mips_ra);
342         else
343                 mips_jr (code, mips_at);
344         mips_nop (code);
345
346         /* Flush instruction cache, since we've generated code */
347         mono_arch_flush_icache (buf, code - buf);
348         
349         /* Sanity check */
350         g_assert ((code - buf) <= max_code_len);
351
352         return buf;
353 }
354
355 gpointer
356 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
357 {
358         guint8 *code, *buf, *tramp;
359
360         tramp = mono_get_trampoline_code (tramp_type);
361
362         code = buf = mono_domain_code_reserve (domain, 32);
363
364         /* Prepare the jump to the generic trampoline code
365          * mono_arch_create_trampoline_code() knows we're putting this in t8
366          */
367         mips_load (code, mips_t8, arg1);
368         
369         /* Now jump to the generic trampoline code */
370         mips_load (code, mips_at, tramp);
371         mips_jr (code, mips_at);
372         mips_nop (code);
373
374         /* Flush instruction cache, since we've generated code */
375         mono_arch_flush_icache (buf, code - buf);
376
377         g_assert ((code - buf) <= 32);
378
379         if (code_len)
380                 *code_len = code - buf;
381
382         return buf;
383 }
384
385 gpointer
386 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
387 {
388         /* FIXME: implement! */
389         g_assert_not_reached ();
390         return NULL;
391 }
392
393 gpointer
394 mono_arch_create_generic_class_init_trampoline (MonoTrampInfo **info, gboolean aot)
395 {
396         /* FIXME: implement! */
397         g_assert_not_reached ();
398         return NULL;
399 }