2007-11-28 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  * @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         int max_code_len = 768;
237
238         /* Now we'll create in 'buf' the MIPS trampoline code. This
239            is the trampoline code common to all methods  */
240                 
241         code = buf = mono_global_codeman_reserve (max_code_len);
242                 
243         /* Allocate the stack frame, and save the return address */
244         mips_addiu (code, mips_sp, mips_sp, -STACK);
245         mips_sw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
246
247         /* we build the MonoLMF structure on the stack - see mini-mips.h */
248         /* offset of MonoLMF from sp */
249         lmf = STACK - sizeof (MonoLMF) - 8;
250
251         for (i = 0; i < MONO_MAX_IREGS; i++)
252                 mips_sw (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
253         for (i = 0; i < MONO_MAX_FREGS; i++)
254                 mips_swc1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
255
256         /* Set the magic number */
257         mips_load_const (code, mips_at, MIPS_LMF_MAGIC2);
258         mips_sw (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, magic));
259
260         /* save method info (it was in t8) */
261         mips_sw (code, mips_t8, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, method));
262
263         /* save frame pointer (caller fp) */
264         mips_sw (code, mips_fp, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, ebp));
265
266         /* save the IP (caller ip) */
267         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
268                 mips_sw (code, mips_zero, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, eip));
269         } else {
270                 mips_sw (code, mips_ra, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, eip));
271         }
272
273         /* jump to mono_get_lmf_addr here */
274         mips_load (code, mips_t9, mono_get_lmf_addr);
275         mips_jalr (code, mips_t9, mips_ra);
276         mips_nop (code);
277
278         /* v0 now points at the (MonoLMF **) for the current thread */
279
280         /* new_lmf->lmf_addr = lmf_addr -- useful when unwinding */
281         mips_sw (code, mips_v0, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
282
283         /* new_lmf->previous_lmf = *lmf_addr */
284         mips_lw (code, mips_at, mips_v0, 0);
285         mips_sw (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
286
287         /* *(lmf_addr) = new_lmf */
288         mips_addiu (code, mips_at, mips_sp, lmf);
289         mips_sw (code, mips_at, mips_v0, 0);
290
291         /*
292          * Now we're ready to call mips_magic_trampoline ().
293          */
294
295         /* Arg 1: MonoMethod *method. */
296         mips_lw (code, mips_a0, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, method));
297                 
298         /* Arg 2: code (next address to the instruction that called us) */
299         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
300                 mips_move (code, mips_a1, mips_zero);
301         } else {
302                 mips_lw (code, mips_a1, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
303         }
304                 
305         /* Arg 3: stack pointer so that the magic trampoline can access the
306          * registers we saved above
307          */
308         mips_move (code, mips_a2, mips_sp);
309                 
310         /* Now go to the trampoline */
311         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT) {
312                 mips_load (code, mips_t9, (guint32)mips_class_init_trampoline);
313         } else {
314                 mips_load (code, mips_t9, (guint32)mips_magic_trampoline);
315         }
316         mips_jalr (code, mips_t9, mips_ra);
317         mips_nop (code);
318                 
319         /* Code address is now in v0, move it to at */
320         mips_move (code, mips_at, mips_v0);
321
322         /*
323          * Now unwind the MonoLMF
324          */
325
326         /* t0 = current_lmf->previous_lmf */
327         mips_lw (code, mips_t0, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
328         /* t1 = lmf_addr */
329         mips_lw (code, mips_t1, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
330         /* (*lmf_addr) = previous_lmf */
331         mips_sw (code, mips_t0, mips_t1, 0);
332
333         /* Restore the callee-saved & argument registers */
334         for (i = 0; i < MONO_MAX_IREGS; i++) {
335                 if ((MONO_ARCH_CALLEE_SAVED_REGS | MONO_ARCH_CALLEE_REGS | MIPS_ARG_REGS) & (1 << i))
336                     mips_lw (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
337         }
338         for (i = 0; i < MONO_MAX_FREGS; i++)
339                 mips_lwc1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
340
341         /* Non-standard function epilogue. Instead of doing a proper
342          * return, we just jump to the compiled code.
343          */
344         /* Restore ra & stack pointer, and jump to the code */
345
346         mips_lw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
347         mips_addiu (code, mips_sp, mips_sp, STACK);
348         mips_jr (code, mips_at);
349         mips_nop (code);
350
351         /* Flush instruction cache, since we've generated code */
352         mono_arch_flush_icache (buf, code - buf);
353         
354         /* Sanity check */
355         g_assert ((code - buf) <= max_code_len);
356
357         return buf;
358 }
359
360 static MonoJitInfo*
361 create_specific_tramp (MonoMethod *method, guint8* tramp, MonoDomain *domain) {
362         guint8 *code, *buf;
363         MonoJitInfo *ji;
364
365         mono_domain_lock (domain);
366         code = buf = mono_code_manager_reserve (domain->code_mp, 32);
367         mono_domain_unlock (domain);
368
369         /* Prepare the jump to the generic trampoline code
370          * mono_arch_create_trampoline_code() knows we're putting this in t8
371          */
372         mips_load (code, mips_t8, method);
373         
374         /* Now jump to the generic trampoline code */
375         mips_load (code, mips_at, tramp);
376         mips_jr (code, mips_at);
377         mips_nop (code);
378
379         /* Flush instruction cache, since we've generated code */
380         mono_arch_flush_icache (buf, code - buf);
381
382         g_assert ((code - buf) <= 32);
383
384         ji = g_new0 (MonoJitInfo, 1);
385         ji->method = method;
386         ji->code_start = buf;
387         ji->code_size = code - buf;
388
389         mono_jit_stats.method_trampolines++;
390
391         return ji;
392 }
393
394 MonoJitInfo*
395 mono_arch_create_jump_trampoline (MonoMethod *method)
396 {
397         guint8 *tramp;
398         MonoDomain* domain = mono_domain_get ();
399         
400         tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_JUMP);
401         return create_specific_tramp (method, tramp, domain);
402 }
403
404 /**
405  * arch_create_jit_trampoline:
406  * @method: pointer to the method info
407  *
408  * Creates a trampoline function for virtual methods. If the created
409  * code is called it first starts JIT compilation of method,
410  * and then calls the newly created method. It also replaces the
411  * corresponding vtable entry (see mips_magic_trampoline).
412  *
413  * A trampoline consists of two parts: a main fragment, shared by all method
414  * trampolines, and some code specific to each method, which hard-codes a
415  * reference to that method and then calls the main fragment.
416  *
417  * The main fragment contains a call to 'mips_magic_trampoline', which performs
418  * call to the JIT compiler and substitutes the method-specific fragment with
419  * some code that directly calls the JIT-compiled method.
420  * 
421  * Returns: a pointer to the newly created code 
422  */
423 gpointer
424 mono_arch_create_jit_trampoline (MonoMethod *method)
425 {
426         guint8 *tramp;
427         MonoJitInfo *ji;
428         MonoDomain* domain = mono_domain_get ();
429         gpointer code_start;
430
431         tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_GENERIC);
432         /* FIXME: should pass the domain down to this function */
433         ji = create_specific_tramp (method, tramp, domain);
434         code_start = ji->code_start;
435         g_free (ji);
436
437         return code_start;
438 }
439
440 /**
441  * mono_arch_create_class_init_trampoline:
442  *  @vtable: the type to initialize
443  *
444  * Creates a trampoline function to run a type initializer. 
445  * If the trampoline is called, it calls mono_runtime_class_init with the
446  * given vtable, then patches the caller code so it does not get called any
447  * more.
448  * 
449  * Returns: a pointer to the newly created code 
450  */
451 gpointer
452 mono_arch_create_class_init_trampoline (MonoVTable *vtable)
453 {
454         guint8 *code, *buf, *tramp;
455
456         tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
457
458         /* This is the method-specific part of the trampoline. Its purpose is
459         to provide the generic part with the MonoMethod *method pointer. We'll
460         use r11 to keep that value, for instance. However, the generic part of
461         the trampoline relies on r11 having the same value it had before coming
462         here, so we must save it before. */
463         mono_domain_lock (vtable->domain);
464         code = buf = mono_code_manager_reserve (vtable->domain->code_mp, METHOD_TRAMPOLINE_SIZE);
465         mono_domain_unlock (vtable->domain);
466
467         //g_print ("mips_class_init_tramp buf=%p tramp=%p\n", buf, tramp);
468
469         mips_addiu (code, mips_sp, mips_sp, -MIPS_MINIMAL_STACK_SIZE);
470         mips_sw (code, mips_ra, mips_sp, MIPS_MINIMAL_STACK_SIZE + MIPS_RET_ADDR_OFFSET);
471
472         /* Probably need to save/restore a0-a3 here */
473
474         mips_load (code, mips_a0, vtable);
475         mips_move (code, mips_a1, mips_ra);
476         mips_move (code, mips_a2, mips_zero);
477
478         mips_load (code, mips_t9, mips_class_init_trampoline);
479         mips_jalr (code, mips_t9, mips_ra);
480         mips_nop (code);
481
482         mips_lw (code, mips_ra, mips_sp, MIPS_MINIMAL_STACK_SIZE + MIPS_RET_ADDR_OFFSET);
483         mips_addiu (code, mips_sp, mips_sp, MIPS_MINIMAL_STACK_SIZE);
484         mips_jr (code, mips_ra);
485         mips_nop (code);
486
487         /* Flush instruction cache, since we've generated code */
488         mono_arch_flush_icache (buf, code - buf);
489                 
490         /* Sanity check */
491         g_assert ((code - buf) <= METHOD_TRAMPOLINE_SIZE);
492         mono_jit_stats.method_trampolines++;
493
494         return buf;
495 }
496
497 /*
498  * This method is only called when running in the Mono Debugger.
499  */
500 gpointer
501 mono_debugger_create_notification_function (void)
502 {
503 #if 0
504         guint8 *ptr, *buf;
505
506         ptr = buf = mono_global_codeman_reserve (16);
507         mips_break (buf, 0xd0);
508         mips_jr (buf, mips_ra);
509         mips_nop (buf);
510         mono_arch_flush_icache (ptr, buf - ptr);
511
512         return ptr;
513 #else
514         return NULL;
515 #endif
516 }
517