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