Merge pull request #1926 from alexrp/profiler-improvements
[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/abi-details.h>
20 #include <mono/metadata/appdomain.h>
21 #include <mono/metadata/marshal.h>
22 #include <mono/metadata/tabledefs.h>
23 #include <mono/arch/mips/mips-codegen.h>
24
25 #include "mini.h"
26 #include "mini-mips.h"
27
28 /*
29  * get_unbox_trampoline:
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 (MonoMethod *m, gpointer addr)
39 {
40         guint8 *code, *start;
41         MonoDomain *domain = mono_domain_get ();
42             
43         start = code = mono_domain_code_reserve (domain, 20);
44
45         mips_load (code, mips_t9, addr);
46         /* The this pointer is kept in a0 */
47         mips_addiu (code, mips_a0, mips_a0, sizeof (MonoObject));
48         mips_jr (code, mips_t9);
49         mips_nop (code);
50
51         mono_arch_flush_icache (start, code - start);
52         g_assert ((code - start) <= 20);
53         /*g_print ("unbox trampoline at %d for %s:%s\n", this_pos, m->klass->name, m->name);
54         g_print ("unbox code is at %p for method at %p\n", start, addr);*/
55
56         return start;
57 }
58
59 void
60 mono_arch_patch_callsite (guint8 *method_start, guint8 *orig_code, guint8 *addr)
61 {
62         guint32 *code = (guint32*)orig_code;
63
64         /* Locate the address of the method-specific trampoline.
65         The call using the vtable slot that took the processing flow to
66         'arch_create_jit_trampoline' looks something like one of these:
67
68                 jal     XXXXYYYY
69                 nop
70
71                 lui     t9, XXXX
72                 addiu   t9, YYYY
73                 jalr    t9
74                 nop
75
76         On entry, 'code' points just after one of the above sequences.
77         */
78         
79         /* The jal case */
80         if ((code[-2] >> 26) == 0x03) {
81                 //g_print ("direct patching\n");
82                 mips_patch ((code-2), (gsize)addr);
83                 return;
84         }
85         /* Look for the jalr */
86         if ((code[-2] & 0xfc1f003f) == 0x00000009) {
87                 /* The lui / addiu / jalr case */
88                 if ((code [-4] >> 26) == 0x0f && (code [-3] >> 26) == 0x09
89                     && (code [-2] >> 26) == 0) {
90                         mips_patch ((code-4), (gsize)addr);
91                         return;
92                 }
93         }
94         g_print("error: bad patch at 0x%08x\n", code);
95         g_assert_not_reached ();
96 }
97
98 void
99 mono_arch_patch_plt_entry (guint8 *code, gpointer *got, mgreg_t *regs, guint8 *addr)
100 {
101         g_assert_not_reached ();
102 }
103
104 /* Stack size for trampoline function 
105  * MIPS_MINIMAL_STACK_SIZE + 16 (args + alignment to mips_magic_trampoline)
106  * + MonoLMF + 14 fp regs + 13 gregs + alignment
107  * #define STACK (MIPS_MINIMAL_STACK_SIZE + 4 * sizeof (gulong) + sizeof (MonoLMF) + 14 * sizeof (double) + 13 * (sizeof (gulong)))
108  * STACK would be 444 for 32 bit darwin
109  */
110
111 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
112
113 #define STACK (int)(ALIGN_TO(4*IREG_SIZE + 8 + sizeof(MonoLMF) + 32, 8))
114
115 /*
116  * Stack frame description when the generic trampoline is called.
117  * caller frame
118  * --------------------
119  *  MonoLMF
120  *  -------------------
121  *  Saved FP registers 0-13
122  *  -------------------
123  *  Saved general registers 0-12
124  *  -------------------
125  *  param area for 3 args to mips_magic_trampoline
126  *  -------------------
127  *  linkage area
128  *  -------------------
129  */
130 guchar*
131 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
132 {
133         char *tramp_name;
134         guint8 *buf, *tramp, *code = NULL;
135         int i, lmf;
136         GSList *unwind_ops = NULL;
137         MonoJumpInfo *ji = NULL;
138         int max_code_len = 768;
139
140         /* AOT not supported on MIPS yet */
141         g_assert (!aot);
142
143         /* Now we'll create in 'buf' the MIPS trampoline code. This
144            is the trampoline code common to all methods  */
145
146         code = buf = mono_global_codeman_reserve (max_code_len);
147
148         /* Allocate the stack frame, and save the return address */
149         mips_addiu (code, mips_sp, mips_sp, -STACK);
150         mips_sw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
151
152         /* we build the MonoLMF structure on the stack - see mini-mips.h */
153         /* offset of MonoLMF from sp */
154         lmf = STACK - sizeof (MonoLMF) - 8;
155
156         for (i = 0; i < MONO_MAX_IREGS; i++)
157                 MIPS_SW (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
158         for (i = 0; i < MONO_MAX_FREGS; i++)
159                 MIPS_SWC1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
160
161         /* Set the magic number */
162         mips_load_const (code, mips_at, MIPS_LMF_MAGIC2);
163         mips_sw (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, magic));
164
165         /* Save caller sp */
166         mips_addiu (code, mips_at, mips_sp, STACK);
167         MIPS_SW (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[mips_sp]));
168
169         /* save method info (it was in t8) */
170         mips_sw (code, mips_t8, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, method));
171
172         /* save the IP (caller ip) */
173         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
174                 mips_sw (code, mips_zero, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, eip));
175         } else {
176                 mips_sw (code, mips_ra, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, eip));
177         }
178
179         /* jump to mono_get_lmf_addr here */
180         mips_load (code, mips_t9, mono_get_lmf_addr);
181         mips_jalr (code, mips_t9, mips_ra);
182         mips_nop (code);
183
184         /* v0 now points at the (MonoLMF **) for the current thread */
185
186         /* new_lmf->lmf_addr = lmf_addr -- useful when unwinding */
187         mips_sw (code, mips_v0, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
188
189         /* new_lmf->previous_lmf = *lmf_addr */
190         mips_lw (code, mips_at, mips_v0, 0);
191         mips_sw (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
192
193         /* *(lmf_addr) = new_lmf */
194         mips_addiu (code, mips_at, mips_sp, lmf);
195         mips_sw (code, mips_at, mips_v0, 0);
196
197         /*
198          * Now we're ready to call mips_magic_trampoline ().
199          */
200
201         /* Arg 1: pointer to registers so that the magic trampoline can
202          * access what we saved above
203          */
204         mips_addiu (code, mips_a0, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[0]));
205
206         /* Arg 2: code (next address to the instruction that called us) */
207         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
208                 mips_move (code, mips_a1, mips_zero);
209         } else {
210                 mips_lw (code, mips_a1, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
211         }
212
213         /* Arg 3: MonoMethod *method. */
214         mips_lw (code, mips_a2, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, method));
215
216         /* Arg 4: Trampoline */
217         mips_move (code, mips_a3, mips_zero);
218                 
219         /* Now go to the trampoline */
220         tramp = (guint8*)mono_get_trampoline_func (tramp_type);
221         mips_load (code, mips_t9, (guint32)tramp);
222         mips_jalr (code, mips_t9, mips_ra);
223         mips_nop (code);
224                 
225         /* Code address is now in v0, move it to at */
226         mips_move (code, mips_at, mips_v0);
227
228         /*
229          * Now unwind the MonoLMF
230          */
231
232         /* t0 = current_lmf->previous_lmf */
233         mips_lw (code, mips_t0, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
234         /* t1 = lmf_addr */
235         mips_lw (code, mips_t1, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
236         /* (*lmf_addr) = previous_lmf */
237         mips_sw (code, mips_t0, mips_t1, 0);
238
239         /* Restore the callee-saved & argument registers */
240         for (i = 0; i < MONO_MAX_IREGS; i++) {
241                 if ((MONO_ARCH_CALLEE_SAVED_REGS | MONO_ARCH_CALLEE_REGS | MIPS_ARG_REGS) & (1 << i))
242                     MIPS_LW (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
243         }
244         for (i = 0; i < MONO_MAX_FREGS; i++)
245                 MIPS_LWC1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
246
247         /* Non-standard function epilogue. Instead of doing a proper
248          * return, we just jump to the compiled code.
249          */
250         /* Restore ra & stack pointer, and jump to the code */
251
252         if (tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH)
253                 mips_move (code, mips_v0, mips_at);
254         mips_lw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
255         mips_addiu (code, mips_sp, mips_sp, STACK);
256         if (MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type))
257                 mips_jr (code, mips_ra);
258         else
259                 mips_jr (code, mips_at);
260         mips_nop (code);
261
262         /* Flush instruction cache, since we've generated code */
263         mono_arch_flush_icache (buf, code - buf);
264         
265         /* Sanity check */
266         g_assert ((code - buf) <= max_code_len);
267
268         g_assert (info);
269         tramp_name = mono_get_generic_trampoline_name (tramp_type);
270         *info = mono_tramp_info_create (tramp_name, buf, code - buf, ji, unwind_ops);
271         g_free (tramp_name);
272
273         return buf;
274 }
275
276 gpointer
277 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
278 {
279         guint8 *code, *buf, *tramp;
280
281         tramp = mono_get_trampoline_code (tramp_type);
282
283         code = buf = mono_domain_code_reserve (domain, 32);
284
285         /* Prepare the jump to the generic trampoline code
286          * mono_arch_create_trampoline_code() knows we're putting this in t8
287          */
288         mips_load (code, mips_t8, arg1);
289         
290         /* Now jump to the generic trampoline code */
291         mips_load (code, mips_at, tramp);
292         mips_jr (code, mips_at);
293         mips_nop (code);
294
295         /* Flush instruction cache, since we've generated code */
296         mono_arch_flush_icache (buf, code - buf);
297
298         g_assert ((code - buf) <= 32);
299
300         if (code_len)
301                 *code_len = code - buf;
302
303         return buf;
304 }
305
306 gpointer
307 mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr)
308 {
309         guint8 *code, *start;
310         int buf_len;
311
312         MonoDomain *domain = mono_domain_get ();
313
314         buf_len = 24;
315
316         start = code = mono_domain_code_reserve (domain, buf_len);
317
318         mips_load (code, MONO_ARCH_RGCTX_REG, mrgctx);
319         mips_load (code, mips_at, addr);
320         mips_jr (code, mips_at);
321         mips_nop (code);
322
323         g_assert ((code - start) <= buf_len);
324
325         mono_arch_flush_icache (start, code - start);
326
327         return start;
328 }
329
330 gpointer
331 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
332 {
333         guint8 *tramp;
334         guint8 *code, *buf;
335         int tramp_size;
336         guint32 code_len;
337         guint8 **rgctx_null_jumps;
338         int depth, index;
339         int i, njumps;
340         gboolean mrgctx;
341         MonoJumpInfo *ji = NULL;
342         GSList *unwind_ops = NULL;
343
344         mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
345         index = MONO_RGCTX_SLOT_INDEX (slot);
346         if (mrgctx)
347                 index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
348         for (depth = 0; ; ++depth) {
349                 int size = mono_class_rgctx_get_array_size (depth, mrgctx);
350
351                 if (index < size - 1)
352                         break;
353                 index -= size - 1;
354         }
355
356         tramp_size = 64 + 16 * depth;
357
358         code = buf = mono_global_codeman_reserve (tramp_size);
359
360         mono_add_unwind_op_def_cfa (unwind_ops, code, buf, mips_sp, 0);
361
362         rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2));
363         njumps = 0;
364
365         /* The vtable/mrgctx is in a0 */
366         g_assert (MONO_ARCH_VTABLE_REG == mips_a0);
367         if (mrgctx) {
368                 /* get mrgctx ptr */
369                 mips_move (code, mips_a1, mips_a0);
370         } else {
371                 /* load rgctx ptr from vtable */
372                 g_assert (mips_is_imm16 (MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context)));
373                 mips_lw (code, mips_a1, mips_a0, MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context));
374                 /* is the rgctx ptr null? */
375                 /* if yes, jump to actual trampoline */
376                 rgctx_null_jumps [njumps ++] = code;
377                 mips_beq (code, mips_a1, mips_zero, 0);
378                 mips_nop (code);
379         }
380
381         for (i = 0; i < depth; ++i) {
382                 /* load ptr to next array */
383                 if (mrgctx && i == 0) {
384                         g_assert (mips_is_imm16 (MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT));
385                         mips_lw (code, mips_a1, mips_a1, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT);
386                 } else {
387                         mips_lw (code, mips_a1, mips_a1, 0);
388                 }
389                 /* is the ptr null? */
390                 /* if yes, jump to actual trampoline */
391                 rgctx_null_jumps [njumps ++] = code;
392                 mips_beq (code, mips_a1, mips_zero, 0);
393                 mips_nop (code);
394         }
395
396         /* fetch slot */
397         g_assert (mips_is_imm16 (sizeof (gpointer) * (index + 1)));
398         mips_lw (code, mips_a1, mips_a1, sizeof (gpointer) * (index + 1));
399         /* is the slot null? */
400         /* if yes, jump to actual trampoline */
401         rgctx_null_jumps [njumps ++] = code;
402         mips_beq (code, mips_a1, mips_zero, 0);
403         mips_nop (code);
404         /* otherwise return, result is in R1 */
405         mips_move (code, mips_v0, mips_a1);
406         mips_jr (code, mips_ra);
407         mips_nop (code);
408
409         g_assert (njumps <= depth + 2);
410         for (i = 0; i < njumps; ++i)
411                 mips_patch ((guint32*)rgctx_null_jumps [i], (guint32)code);
412
413         g_free (rgctx_null_jumps);
414
415         /* Slowpath */
416
417         /* The vtable/mrgctx is still in a0 */
418
419         if (aot) {
420                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", slot));
421                 mips_load (code, mips_at, 0);
422                 mips_jr (code, mips_at);
423                 mips_nop (code);
424         } else {
425                 tramp = mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), &code_len);
426                 mips_load (code, mips_at, tramp);
427                 mips_jr (code, mips_at);
428                 mips_nop (code);
429         }
430
431         mono_arch_flush_icache (buf, code - buf);
432
433         g_assert (code - buf <= tramp_size);
434
435         if (info) {
436                 char *name = mono_get_rgctx_fetch_trampoline_name (slot);
437                 *info = mono_tramp_info_create (name, buf, code - buf, ji, unwind_ops);
438                 g_free (name);
439         }
440
441         return buf;
442 }