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