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