2008-12-01 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         mono_domain_lock (domain);
48         start = code = mono_code_manager_reserve (domain->code_mp, 20);
49         mono_domain_unlock (domain);
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. The call using
70         the vtable slot that took the processing flow to 'arch_create_jit_trampoline' 
71         looks something like this:
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         } else {
89                 /* Sanity check: look for the jalr */
90                 g_assert((code[-2] & 0xfc1f003f) == 0x00000009);
91
92                 //printf ("mips_magic_trampoline: jalr @ 0x%0x, w/ reg %d\n", code-2, reg);
93
94                 /* The lui / addiu / jalr case */
95                 if ((code [-4] >> 26) == 0x0f && (code [-3] >> 26) == 0x09 && (code [-2] >> 26) == 0) {
96                         mips_patch ((code-4), (gsize)addr);
97                 } else {
98                         g_assert_not_reached ();
99                 }
100         }
101 }
102
103 void
104 mono_arch_patch_plt_entry (guint8 *code, 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*4 + 8 + sizeof(MonoLMF) + 32)
117
118
119 gpointer
120 mono_arch_get_vcall_slot (guint8 *code_ptr, gpointer *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*4);
168                                 g_assert (lmf->magic == MIPS_LMF_MAGIC2);
169                                 o = (gpointer)lmf->iregs [base];
170                         }
171                         else {
172                                 o = regs [base];
173                         }
174                         break;
175                 }
176         }
177         *displacement = offset;
178         return o;
179 }
180
181 gpointer*
182 mono_arch_get_vcall_slot_addr (guint8 *code, gpointer *regs)
183 {
184         gpointer vt;
185         int displacement;
186         vt = mono_arch_get_vcall_slot (code, regs, &displacement);
187         if (!vt)
188                 return NULL;
189         return (gpointer*)((char*)vt + displacement);
190 }
191
192 void
193 mono_arch_nullify_plt_entry (guint8 *code)
194 {
195         g_assert_not_reached ();
196 }
197
198 /**
199  * mips_magic_trampoline:
200  * @code: pointer into caller code
201  * @method: the method to translate
202  * @sp: stack pointer
203  *
204  * This method is called by the function 'arch_create_jit_trampoline', which in
205  * turn is called by the trampoline functions for virtual methods.
206  * After having called the JIT compiler to compile the method, it inspects the
207  * caller code to find the address of the method-specific part of the
208  * trampoline vtable slot for this method, updates it with a fragment that calls
209  * the newly compiled code and returns this address of the compiled code to
210  * 'arch_create_jit_trampoline' 
211  */
212 #if 0
213 static gpointer
214 mips_magic_trampoline (MonoMethod *method, guint32 *code, char *sp)
215 {
216         char *vtable = NULL;
217         gpointer addr;
218         MonoJitInfo *ji, *target_ji;
219         int reg, offset = 0;
220         guint32 base = 0;
221
222         addr = mono_compile_method (method);
223         g_assert (addr);
224
225         if (!code)
226                 return addr;
227
228         /* We can't trampoline across domains */
229         ji = mono_jit_info_table_find (mono_domain_get (), code);
230         target_ji = mono_jit_info_table_find (mono_domain_get (), addr);
231         if (!mono_method_same_domain (ji, target_ji))
232                 return addr;
233
234 #if 0
235         g_print ("mips_magic: method code at %p from %p for %s:%s\n",
236                  addr, code, method->klass->name, method->name);
237 #endif
238         /* Locate the address of the method-specific trampoline. The call using
239         the vtable slot that took the processing flow to 'arch_create_jit_trampoline' 
240         looks something like this:
241
242                 jal     XXXXYYYY
243
244                 lui     t9, XXXX
245                 addiu   t9, YYYY
246                 jalr    t9
247                 nop
248
249         On entry, 'code' points just after one of the above sequences.
250         */
251         
252         /* The jal case */
253         if ((code[-2] >> 26) == 0x03) {
254                 g_print ("direct patching\n");
255                 mips_patch ((char*)(code-2), addr);
256                 return addr;
257         }
258         
259         /* Sanity check: look for the jalr */
260         g_assert((code[-2] & 0xfc1f003f) == 0x00000009);
261
262         reg = (code[-2] >> 21) & 0x1f;
263
264         //printf ("mips_magic_trampoline: jalr @ 0x%0x, w/ reg %d\n", code-2, reg);
265
266         /* The lui / addiu / jalr case */
267         if ((code [-4] >> 26) == 0x0f && (code [-3] >> 26) == 0x09 && (code [-2] >> 26) == 0) {
268                 mips_patch ((char*)(code-4), addr);
269                 return addr;
270         }
271
272         //printf ("mips_magic_trampoline: 0x%08x @ 0x%0x\n", *(code-2), code-2);
273
274         /* Probably a vtable lookup */
275
276         /* Walk backwards to find 'lw reg,XX(base)' */
277         for(; --code;) {
278                 guint32 mask = (0x3f << 26) | (0x1f << 16);
279                 guint32 match = (0x23 << 26) | (reg << 16);
280                 if((*code & mask) == match) {
281                         gint16 soff;
282                         gint reg_offset;
283
284                         /* lw reg,XX(base) */
285                         base = (*code >> 21) & 0x1f;
286                         soff = (*code & 0xffff);
287                         if (soff & 0x8000)
288                                 soff |= 0xffff0000;
289                         offset = soff;
290                         reg_offset = STACK - sizeof (MonoLMF)
291                                 + G_STRUCT_OFFSET (MonoLMF, iregs[base]);
292                         /* o contains now the value of register reg */
293                         vtable = *((char**) (sp + reg_offset));
294 #if 0
295                         g_print ("patching reg is %d, offset %d (vtable %p) @ %p\n",
296                                  base, offset, vtable, code);
297 #endif
298                         break;
299                 }
300         }
301
302         /* this is not done for non-virtual calls, because in that case
303            we won't have an object, but the actual pointer to the 
304            valuetype as the this argument
305          */
306         if (method->klass->valuetype && !mono_aot_is_got_entry (code, vtable))
307                 addr = get_unbox_trampoline (method, addr);
308
309         vtable += offset;
310         if (mono_aot_is_got_entry (code, vtable) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable))
311                 *((gpointer *)vtable) = addr;
312         return addr;
313 }
314 #endif
315
316 void
317 mono_arch_nullify_class_init_trampoline (guint8 *code, gssize *regs)
318 {
319         guint32 *code32 = (guint32*)code;
320
321         /* back up to the jal/jalr instruction */
322         code32 -= 2;
323
324         /* Check for jal/jalr -- and NOP it out */
325         if ((((*code32)&0xfc000000) == 0x0c000000)
326             || (((*code32)&0xfc1f003f) == 0x00000009)) {
327                 mips_nop (code32);
328                 mono_arch_flush_icache ((gpointer)(code32 - 1), 4);
329                 return;
330         } else {
331                 g_assert_not_reached ();
332         }
333 }
334
335 /*
336  * Stack frame description when the generic trampoline is called.
337  * caller frame
338  * --------------------
339  *  MonoLMF
340  *  -------------------
341  *  Saved FP registers 0-13
342  *  -------------------
343  *  Saved general registers 0-12
344  *  -------------------
345  *  param area for 3 args to mips_magic_trampoline
346  *  -------------------
347  *  linkage area
348  *  -------------------
349  */
350 guchar*
351 mono_arch_create_trampoline_code (MonoTrampolineType tramp_type)
352 {
353         guint8 *buf, *tramp, *code = NULL;
354         int i, lmf;
355         int max_code_len = 768;
356
357         /* Now we'll create in 'buf' the MIPS trampoline code. This
358            is the trampoline code common to all methods  */
359                 
360         code = buf = mono_global_codeman_reserve (max_code_len);
361                 
362         /* Allocate the stack frame, and save the return address */
363         mips_addiu (code, mips_sp, mips_sp, -STACK);
364         mips_sw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
365
366         /* we build the MonoLMF structure on the stack - see mini-mips.h */
367         /* offset of MonoLMF from sp */
368         lmf = STACK - sizeof (MonoLMF) - 8;
369
370         for (i = 0; i < MONO_MAX_IREGS; i++)
371                 mips_sw (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
372         for (i = 0; i < MONO_MAX_FREGS; i++)
373                 mips_swc1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
374
375         /* Set the magic number */
376         mips_load_const (code, mips_at, MIPS_LMF_MAGIC2);
377         mips_sw (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, magic));
378
379         /* save method info (it was in t8) */
380         mips_sw (code, mips_t8, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, method));
381
382         /* save frame pointer (caller fp) */
383         mips_sw (code, mips_fp, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, ebp));
384
385         /* save the IP (caller ip) */
386         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
387                 mips_sw (code, mips_zero, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, eip));
388         } else {
389                 mips_sw (code, mips_ra, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, eip));
390         }
391
392         /* jump to mono_get_lmf_addr here */
393         mips_load (code, mips_t9, mono_get_lmf_addr);
394         mips_jalr (code, mips_t9, mips_ra);
395         mips_nop (code);
396
397         /* v0 now points at the (MonoLMF **) for the current thread */
398
399         /* new_lmf->lmf_addr = lmf_addr -- useful when unwinding */
400         mips_sw (code, mips_v0, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
401
402         /* new_lmf->previous_lmf = *lmf_addr */
403         mips_lw (code, mips_at, mips_v0, 0);
404         mips_sw (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
405
406         /* *(lmf_addr) = new_lmf */
407         mips_addiu (code, mips_at, mips_sp, lmf);
408         mips_sw (code, mips_at, mips_v0, 0);
409
410         /*
411          * Now we're ready to call mips_magic_trampoline ().
412          */
413
414         /* Arg 1: stack pointer so that the magic trampoline can access the
415          * registers we saved above
416          */
417         mips_move (code, mips_a0, mips_sp);
418                 
419         /* Arg 2: code (next address to the instruction that called us) */
420         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
421                 mips_move (code, mips_a1, mips_zero);
422         } else {
423                 mips_lw (code, mips_a1, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
424         }
425
426         /* Arg 3: MonoMethod *method. */
427         mips_lw (code, mips_a2, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, method));
428
429         /* Arg 4: Trampoline */
430         mips_move (code, mips_a3, mips_zero);
431                 
432         /* Now go to the trampoline */
433         tramp = (guint8*)mono_get_trampoline_func (tramp_type);
434         mips_load (code, mips_t9, (guint32)tramp);
435         mips_jalr (code, mips_t9, mips_ra);
436         mips_nop (code);
437                 
438         /* Code address is now in v0, move it to at */
439         mips_move (code, mips_at, mips_v0);
440
441         /*
442          * Now unwind the MonoLMF
443          */
444
445         /* t0 = current_lmf->previous_lmf */
446         mips_lw (code, mips_t0, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
447         /* t1 = lmf_addr */
448         mips_lw (code, mips_t1, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
449         /* (*lmf_addr) = previous_lmf */
450         mips_sw (code, mips_t0, mips_t1, 0);
451
452         /* Restore the callee-saved & argument registers */
453         for (i = 0; i < MONO_MAX_IREGS; i++) {
454                 if ((MONO_ARCH_CALLEE_SAVED_REGS | MONO_ARCH_CALLEE_REGS | MIPS_ARG_REGS) & (1 << i))
455                     mips_lw (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
456         }
457         for (i = 0; i < MONO_MAX_FREGS; i++)
458                 mips_lwc1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
459
460         /* Non-standard function epilogue. Instead of doing a proper
461          * return, we just jump to the compiled code.
462          */
463         /* Restore ra & stack pointer, and jump to the code */
464
465         mips_lw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
466         mips_addiu (code, mips_sp, mips_sp, STACK);
467         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
468                 mips_jr (code, mips_ra);
469         else
470                 mips_jr (code, mips_at);
471         mips_nop (code);
472
473         /* Flush instruction cache, since we've generated code */
474         mono_arch_flush_icache (buf, code - buf);
475         
476         /* Sanity check */
477         g_assert ((code - buf) <= max_code_len);
478
479         return buf;
480 }
481
482 gpointer
483 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
484 {
485         guint8 *code, *buf, *tramp;
486
487         tramp = mono_get_trampoline_code (tramp_type);
488
489         mono_domain_lock (domain);
490         code = buf = mono_code_manager_reserve (domain->code_mp, 32);
491         mono_domain_unlock (domain);
492
493         /* Prepare the jump to the generic trampoline code
494          * mono_arch_create_trampoline_code() knows we're putting this in t8
495          */
496         mips_load (code, mips_t8, arg1);
497         
498         /* Now jump to the generic trampoline code */
499         mips_load (code, mips_at, tramp);
500         mips_jr (code, mips_at);
501         mips_nop (code);
502
503         /* Flush instruction cache, since we've generated code */
504         mono_arch_flush_icache (buf, code - buf);
505
506         g_assert ((code - buf) <= 32);
507
508         if (code_len)
509                 *code_len = code - buf;
510
511         return buf;
512 }
513
514 gpointer
515 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 encoded_offset)
516 {
517         /* FIXME: implement! */
518         g_assert_not_reached ();
519         return NULL;
520 }
521
522 gpointer
523 mono_arch_create_generic_class_init_trampoline (void)
524 {
525         /* FIXME: implement! */
526         g_assert_not_reached ();
527         return NULL;
528 }