Thu Nov 23 20:01:12 CET 2006 Paolo Molaro <lupus@ximian.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  * @m: method pointer
30  * @addr: pointer to native code for @m
31  *
32  * when value type methods are called through the vtable we need to unbox the
33  * this argument. This method returns a pointer to a trampoline which does
34  * unboxing before calling the method
35  */
36 static gpointer
37 get_unbox_trampoline (MonoMethod *m, gpointer addr)
38 {
39         guint8 *code, *start;
40         int this_pos = mips_a0;
41         MonoDomain *domain = mono_domain_get ();
42
43         if (!mono_method_signature (m)->ret->byref && MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret))
44                 this_pos = mips_a1;
45             
46         mono_domain_lock (domain);
47         start = code = mono_code_manager_reserve (domain->code_mp, 20);
48         mono_domain_unlock (domain);
49
50         mips_load (code, mips_t9, addr);
51         mips_addiu (code, this_pos, this_pos, sizeof (MonoObject));
52         mips_jr (code, mips_t9);
53         mips_nop (code);
54
55         mono_arch_flush_icache (start, code - start);
56         g_assert ((code - start) <= 20);
57         /*g_print ("unbox trampoline at %d for %s:%s\n", this_pos, m->klass->name, m->name);
58         g_print ("unbox code is at %p for method at %p\n", start, addr);*/
59
60         return start;
61 }
62
63 /* Stack size for trampoline function 
64  * MIPS_MINIMAL_STACK_SIZE + 16 (args + alignment to mips_magic_trampoline)
65  * + MonoLMF + 14 fp regs + 13 gregs + alignment
66  * #define STACK (MIPS_MINIMAL_STACK_SIZE + 4 * sizeof (gulong) + sizeof (MonoLMF) + 14 * sizeof (double) + 13 * (sizeof (gulong)))
67  * STACK would be 444 for 32 bit darwin
68  */
69
70 #define STACK (4*4 + 8 + sizeof(MonoLMF) + 32)
71
72
73 /* Method-specific trampoline code fragment size */
74 #define METHOD_TRAMPOLINE_SIZE 64
75
76 /* Jump-specific trampoline code fragment size */
77 #define JUMP_TRAMPOLINE_SIZE   64
78
79 /**
80  * mips_magic_trampoline:
81  * @code: pointer into caller code
82  * @method: the method to translate
83  * @sp: stack pointer
84  *
85  * This method is called by the function 'arch_create_jit_trampoline', which in
86  * turn is called by the trampoline functions for virtual methods.
87  * After having called the JIT compiler to compile the method, it inspects the
88  * caller code to find the address of the method-specific part of the
89  * trampoline vtable slot for this method, updates it with a fragment that calls
90  * the newly compiled code and returns this address of the compiled code to
91  * 'arch_create_jit_trampoline' 
92  */
93 static gpointer
94 mips_magic_trampoline (MonoMethod *method, guint32 *code, char *sp)
95 {
96         char *vtable = NULL;
97         gpointer addr;
98         MonoJitInfo *ji, *target_ji;
99         int reg, offset = 0;
100         guint32 base = 0;
101
102         addr = mono_compile_method (method);
103         g_assert (addr);
104
105         if (!code)
106                 return addr;
107
108         /* We can't trampoline across domains */
109         ji = mono_jit_info_table_find (mono_domain_get (), code);
110         target_ji = mono_jit_info_table_find (mono_domain_get (), addr);
111         if (!mono_method_same_domain (ji, target_ji))
112                 return addr;
113
114 #if 0
115         g_print ("mips_magic: method code at %p from %p for %s:%s\n",
116                  addr, code, method->klass->name, method->name);
117 #endif
118         /* Locate the address of the method-specific trampoline. The call using
119         the vtable slot that took the processing flow to 'arch_create_jit_trampoline' 
120         looks something like this:
121
122                 jal     XXXXYYYY
123
124                 lui     t9, XXXX
125                 addiu   t9, YYYY
126                 jalr    t9
127                 nop
128
129         On entry, 'code' points just after one of the above sequences.
130         */
131         
132         /* The jal case */
133         if ((code[-2] >> 26) == 0x03) {
134                 g_print ("direct patching\n");
135                 mips_patch ((char*)(code-2), addr);
136                 return addr;
137         }
138         
139         /* Sanity check: look for the jalr */
140         g_assert((code[-2] & 0xfc1f003f) == 0x00000009);
141
142         reg = (code[-2] >> 21) & 0x1f;
143
144         //printf ("mips_magic_trampoline: jalr @ 0x%0x, w/ reg %d\n", code-2, reg);
145
146         /* The lui / addiu / jalr case */
147         if ((code [-4] >> 26) == 0x0f && (code [-3] >> 26) == 0x09 && (code [-2] >> 26) == 0) {
148                 mips_patch ((char*)(code-4), addr);
149                 return addr;
150         }
151
152         //printf ("mips_magic_trampoline: 0x%08x @ 0x%0x\n", *(code-2), code-2);
153
154         /* Probably a vtable lookup */
155
156         /* Walk backwards to find 'lw reg,XX(base)' */
157         for(; --code;) {
158                 guint32 mask = (0x3f << 26) | (0x1f << 16);
159                 guint32 match = (0x23 << 26) | (reg << 16);
160                 if((*code & mask) == match) {
161                         gint16 soff;
162                         gint reg_offset;
163
164                         /* lw reg,XX(base) */
165                         base = (*code >> 21) & 0x1f;
166                         soff = (*code & 0xffff);
167                         if (soff & 0x8000)
168                                 soff |= 0xffff0000;
169                         offset = soff;
170                         reg_offset = STACK - sizeof (MonoLMF)
171                                 + G_STRUCT_OFFSET (MonoLMF, iregs[base]);
172                         /* o contains now the value of register reg */
173                         vtable = *((char**) (sp + reg_offset));
174 #if 0
175                         g_print ("patching reg is %d, offset %d (vtable %p) @ %p\n",
176                                  base, offset, vtable, code);
177 #endif
178                         break;
179                 }
180         }
181
182         /* this is not done for non-virtual calls, because in that case
183            we won't have an object, but the actual pointer to the 
184            valuetype as the this argument
185          */
186         if (method->klass->valuetype && !mono_aot_is_got_entry (code, vtable))
187                 addr = get_unbox_trampoline (method, addr);
188
189         vtable += offset;
190         if (mono_aot_is_got_entry (code, vtable) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable))
191                 *((gpointer *)vtable) = addr;
192         return addr;
193 }
194
195 static void
196 mips_class_init_trampoline (void *vtable, guint32 *code, char *sp)
197 {
198         //g_print ("mips_class_init: vtable=%p code=%p sp=%p\n", vtable, code, sp);
199
200         mono_runtime_class_init (vtable);
201
202         /* back up to the jal/jalr instruction */
203         code -= 2;
204
205         /* Check for jal/jalr -- and NOP it out */
206         if ((((*code)&0xfc000000) == 0x0c000000)
207             || (((*code)&0xfc1f003f) == 0x00000009)) {
208                 mips_nop (code);
209                 mono_arch_flush_icache (code-1, 4);
210                 return;
211         } else {
212                 g_assert_not_reached ();
213         }
214 }
215
216 /*
217  * Stack frame description when the generic trampoline is called.
218  * caller frame
219  * --------------------
220  *  MonoLMF
221  *  -------------------
222  *  Saved FP registers 0-13
223  *  -------------------
224  *  Saved general registers 0-12
225  *  -------------------
226  *  param area for 3 args to mips_magic_trampoline
227  *  -------------------
228  *  linkage area
229  *  -------------------
230  */
231 guchar*
232 mono_arch_create_trampoline_code (MonoTrampolineType tramp_type)
233 {
234         guint8 *buf, *code = NULL;
235         int i, offset, lmf;
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 (512);
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         /* offset of MonoLMF from sp */
247         lmf = STACK - sizeof (MonoLMF);
248         for (i = 0; i < MONO_MAX_IREGS; i++)
249                 mips_sw (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
250         for (i = 0; i < MONO_MAX_FREGS; i++)
251                 mips_swc1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
252
253         /* jump to mono_get_lmf_addr here */
254         mips_load (code, mips_t9, mono_get_lmf_addr);
255         mips_jalr (code, mips_t9, mips_ra);
256         mips_nop (code);
257
258         /* v0 now points at the (MonoLMF **) for the current thread */
259
260         /* we build the MonoLMF structure on the stack - see mini-mips.h
261          * The pointer to the struct is put in mips_s0 (new_lmf).
262          */
263
264         mips_addiu (code, mips_t2, mips_sp, lmf);
265
266         /* new_lmf->lmf_addr = lmf_addr -- useful when unwinding */
267         mips_sw (code, mips_v0, mips_t2, G_STRUCT_OFFSET(MonoLMF, lmf_addr));
268
269         /* new_lmf->previous_lmf = *lmf_addr */
270         mips_lw (code, mips_at, mips_v0, 0);
271         mips_sw (code, mips_at, mips_t2, G_STRUCT_OFFSET(MonoLMF, previous_lmf));
272
273         /* *(lmf_addr) = t2 */
274         mips_sw (code, mips_t2, mips_v0, 0);
275
276         /* save method info (it was in t8) */
277         mips_lw (code, mips_at, mips_t2, G_STRUCT_OFFSET(MonoLMF, iregs[mips_t8]));
278         mips_sw (code, mips_at, mips_t2, G_STRUCT_OFFSET(MonoLMF, method));
279
280         mips_sw (code, mips_sp, mips_t2, G_STRUCT_OFFSET(MonoLMF, ebp));
281
282         /* save the IP (caller ip) */
283         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
284                 mips_sw (code, mips_zero, mips_t2, G_STRUCT_OFFSET(MonoLMF, eip));
285         } else {
286                 mips_lw (code, mips_at, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
287                 mips_sw (code, mips_at, mips_t2, G_STRUCT_OFFSET(MonoLMF, eip));
288         }
289
290         /*
291          * Now we're ready to call mips_magic_trampoline ().
292          */
293
294         /* Arg 1: MonoMethod *method. */
295         mips_lw (code, mips_a0, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[mips_t8]));
296                 
297         /* Arg 2: code (next address to the instruction that called us) */
298         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
299                 mips_move (code, mips_a1, mips_zero);
300         } else {
301                 mips_lw (code, mips_a1, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
302         }
303                 
304         /* Arg 3: stack pointer so that the magic trampoline can access the
305          * registers we saved above
306          */
307         mips_move (code, mips_a2, mips_sp);
308                 
309         /* Now go to the trampoline */
310         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT) {
311                 mips_load (code, mips_t9, (guint32)mips_class_init_trampoline);
312         } else {
313                 mips_load (code, mips_t9, (guint32)mips_magic_trampoline);
314         }
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         /* XXX - Restore the float registers */
338
339         /* Non-standard function epilogue. Instead of doing a proper
340          * return, we just jump to the compiled code.
341          */
342         /* Restore ra & stack pointer, and jump to the code */
343
344         mips_lw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
345         mips_addiu (code, mips_sp, mips_sp, STACK);
346         mips_jr (code, mips_at);
347         mips_nop (code);
348
349         /* Flush instruction cache, since we've generated code */
350         mono_arch_flush_icache (buf, code - buf);
351         
352         /* Sanity check */
353         g_assert ((code - buf) <= 512);
354
355         return buf;
356 }
357
358 static MonoJitInfo*
359 create_specific_tramp (MonoMethod *method, guint8* tramp, MonoDomain *domain) {
360         guint8 *code, *buf;
361         MonoJitInfo *ji;
362
363         mono_domain_lock (domain);
364         code = buf = mono_code_manager_reserve (domain->code_mp, 32);
365         mono_domain_unlock (domain);
366
367         /* Prepare the jump to the generic trampoline code
368          * mono_arch_create_trampoline_code() knows we're putting this in t8
369          */
370         mips_load (code, mips_t8, method);
371         
372         /* Now jump to the generic trampoline code */
373         mips_load (code, mips_at, tramp);
374         mips_jr (code, mips_at);
375         mips_nop (code);
376
377         /* Flush instruction cache, since we've generated code */
378         mono_arch_flush_icache (buf, code - buf);
379
380         g_assert ((code - buf) <= 32);
381
382         ji = g_new0 (MonoJitInfo, 1);
383         ji->method = method;
384         ji->code_start = buf;
385         ji->code_size = code - buf;
386
387         mono_jit_stats.method_trampolines++;
388
389         return ji;
390 }
391
392 MonoJitInfo*
393 mono_arch_create_jump_trampoline (MonoMethod *method)
394 {
395         guint8 *tramp;
396         MonoDomain* domain = mono_domain_get ();
397         
398         tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_JUMP);
399         return create_specific_tramp (method, tramp, domain);
400 }
401
402 /**
403  * arch_create_jit_trampoline:
404  * @method: pointer to the method info
405  *
406  * Creates a trampoline function for virtual methods. If the created
407  * code is called it first starts JIT compilation of method,
408  * and then calls the newly created method. It also replaces the
409  * corresponding vtable entry (see mips_magic_trampoline).
410  *
411  * A trampoline consists of two parts: a main fragment, shared by all method
412  * trampolines, and some code specific to each method, which hard-codes a
413  * reference to that method and then calls the main fragment.
414  *
415  * The main fragment contains a call to 'mips_magic_trampoline', which performs
416  * call to the JIT compiler and substitutes the method-specific fragment with
417  * some code that directly calls the JIT-compiled method.
418  * 
419  * Returns: a pointer to the newly created code 
420  */
421 gpointer
422 mono_arch_create_jit_trampoline (MonoMethod *method)
423 {
424         guint8 *tramp;
425         MonoJitInfo *ji;
426         MonoDomain* domain = mono_domain_get ();
427         gpointer code_start;
428
429         tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_GENERIC);
430         /* FIXME: should pass the domain down to this function */
431         ji = create_specific_tramp (method, tramp, domain);
432         code_start = ji->code_start;
433         g_free (ji);
434
435         return code_start;
436 }
437
438 /**
439  * mono_arch_create_class_init_trampoline:
440  *  @vtable: the type to initialize
441  *
442  * Creates a trampoline function to run a type initializer. 
443  * If the trampoline is called, it calls mono_runtime_class_init with the
444  * given vtable, then patches the caller code so it does not get called any
445  * more.
446  * 
447  * Returns: a pointer to the newly created code 
448  */
449 gpointer
450 mono_arch_create_class_init_trampoline (MonoVTable *vtable)
451 {
452         guint8 *code, *buf, *tramp;
453
454         tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
455
456         /* This is the method-specific part of the trampoline. Its purpose is
457         to provide the generic part with the MonoMethod *method pointer. We'll
458         use r11 to keep that value, for instance. However, the generic part of
459         the trampoline relies on r11 having the same value it had before coming
460         here, so we must save it before. */
461         mono_domain_lock (vtable->domain);
462         code = buf = mono_code_manager_reserve (vtable->domain->code_mp, METHOD_TRAMPOLINE_SIZE);
463         mono_domain_unlock (vtable->domain);
464
465         //g_print ("mips_class_init_tramp buf=%p tramp=%p\n", buf, tramp);
466
467         mips_addiu (code, mips_sp, mips_sp, -MIPS_MINIMAL_STACK_SIZE);
468         mips_sw (code, mips_ra, mips_sp, MIPS_MINIMAL_STACK_SIZE + MIPS_RET_ADDR_OFFSET);
469
470         /* Probably need to save/restore a0-a3 here */
471
472         mips_load (code, mips_a0, vtable);
473         mips_move (code, mips_a1, mips_ra);
474         mips_move (code, mips_a2, mips_zero);
475
476         mips_load (code, mips_t9, mips_class_init_trampoline);
477         mips_jalr (code, mips_t9, mips_ra);
478         mips_nop (code);
479
480         mips_lw (code, mips_ra, mips_sp, MIPS_MINIMAL_STACK_SIZE + MIPS_RET_ADDR_OFFSET);
481         mips_addiu (code, mips_sp, mips_sp, MIPS_MINIMAL_STACK_SIZE);
482         mips_jr (code, mips_ra);
483         mips_nop (code);
484
485         /* Flush instruction cache, since we've generated code */
486         mono_arch_flush_icache (buf, code - buf);
487                 
488         /* Sanity check */
489         g_assert ((code - buf) <= METHOD_TRAMPOLINE_SIZE);
490         mono_jit_stats.method_trampolines++;
491
492         return buf;
493 }
494
495 /*
496  * This method is only called when running in the Mono Debugger.
497  */
498 gpointer
499 mono_debugger_create_notification_function (MonoCodeManager *codeman)
500 {
501 #if 0
502         guint8 *ptr, *buf;
503
504         codeman = mono_code_manager_reserve (codeman, 16);
505         mips_break (buf, 0xd0);
506         mips_jr (buf, mips_ra);
507         mips_nop (buf);
508         mono_arch_flush_icache (ptr, buf - ptr);
509
510         return ptr;
511 #else
512         return NULL;
513 #endif
514 }
515