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