3 * JIT trampoline code for MIPS
6 * Mark Mason (mason@broadcom.com)
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>
14 * (C) 2001 Ximian, Inc.
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>
27 #include "mini-mips.h"
28 #include "mini-runtime.h"
29 #include "mono/utils/mono-tls-inline.h"
32 * get_unbox_trampoline:
34 * @addr: pointer to native code for @m
36 * when value type methods are called through the vtable we need to unbox the
37 * this argument. This method returns a pointer to a trampoline which does
38 * unboxing before calling the method
41 mono_arch_get_unbox_trampoline (MonoMethod
*m
, gpointer addr
)
44 MonoDomain
*domain
= mono_domain_get ();
46 start
= code
= mono_domain_code_reserve (domain
, 20);
48 mips_load (code
, mips_t9
, addr
);
49 /* The this pointer is kept in a0 */
50 mips_addiu (code
, mips_a0
, mips_a0
, MONO_ABI_SIZEOF (MonoObject
));
51 mips_jr (code
, mips_t9
);
54 mono_arch_flush_icache (start
, code
- start
);
55 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE
, m
));
57 g_assert ((code
- start
) <= 20);
58 /*g_print ("unbox trampoline at %d for %s:%s\n", this_pos, m->klass->name, m->name);
59 g_print ("unbox code is at %p for method at %p\n", start, addr);*/
61 mono_tramp_info_register (mono_tramp_info_create (NULL
, start
, code
- start
, NULL
, NULL
), domain
);
67 mono_arch_patch_callsite (guint8
*method_start
, guint8
*orig_code
, guint8
*addr
)
69 guint32
*code
= (guint32
*)orig_code
;
71 /* Locate the address of the method-specific trampoline.
72 The call using the vtable slot that took the processing flow to
73 'arch_create_jit_trampoline' looks something like one of these:
83 On entry, 'code' points just after one of the above sequences.
87 if ((code
[-2] >> 26) == 0x03) {
88 //g_print ("direct patching\n");
89 mips_patch ((code
-2), (gsize
)addr
);
92 /* Look for the jalr */
93 if ((code
[-2] & 0xfc1f003f) == 0x00000009) {
94 /* The lui / addiu / jalr case */
95 if ((code
[-4] >> 26) == 0x0f && (code
[-3] >> 26) == 0x09
96 && (code
[-2] >> 26) == 0) {
97 mips_patch ((code
-4), (gsize
)addr
);
101 g_print("error: bad patch at 0x%08x\n", code
);
102 g_assert_not_reached ();
106 mono_arch_patch_plt_entry (guint8
*code
, gpointer
*got
, host_mgreg_t
*regs
, guint8
*addr
)
108 g_assert_not_reached ();
111 /* Stack size for trampoline function
112 * MIPS_MINIMAL_STACK_SIZE + 16 (args + alignment to mips_magic_trampoline)
113 * + MonoLMF + 14 fp regs + 13 gregs + alignment
114 * #define STACK (MIPS_MINIMAL_STACK_SIZE + 4 * sizeof (gulong) + sizeof (MonoLMF) + 14 * sizeof (double) + 13 * (sizeof (gulong)))
115 * STACK would be 444 for 32 bit darwin
118 #define STACK (int)(ALIGN_TO(4*IREG_SIZE + 8 + sizeof(MonoLMF) + 32, 8))
121 * Stack frame description when the generic trampoline is called.
123 * --------------------
125 * -------------------
126 * Saved FP registers 0-13
127 * -------------------
128 * Saved general registers 0-12
129 * -------------------
130 * param area for 3 args to mips_magic_trampoline
131 * -------------------
133 * -------------------
136 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type
, MonoTrampInfo
**info
, gboolean aot
)
138 const char *tramp_name
;
139 guint8
*buf
, *tramp
, *code
= NULL
;
141 GSList
*unwind_ops
= NULL
;
142 MonoJumpInfo
*ji
= NULL
;
143 int max_code_len
= 768;
145 /* AOT not supported on MIPS yet */
148 /* Now we'll create in 'buf' the MIPS trampoline code. This
149 is the trampoline code common to all methods */
151 code
= buf
= mono_global_codeman_reserve (max_code_len
);
153 /* Allocate the stack frame, and save the return address */
154 mips_addiu (code
, mips_sp
, mips_sp
, -STACK
);
155 mips_sw (code
, mips_ra
, mips_sp
, STACK
+ MIPS_RET_ADDR_OFFSET
);
157 /* we build the MonoLMF structure on the stack - see mini-mips.h */
158 /* offset of MonoLMF from sp */
159 lmf
= STACK
- sizeof (MonoLMF
) - 8;
161 for (i
= 0; i
< MONO_MAX_IREGS
; i
++)
162 MIPS_SW (code
, i
, mips_sp
, lmf
+ G_STRUCT_OFFSET (MonoLMF
, iregs
[i
]));
163 for (i
= 0; i
< MONO_MAX_FREGS
; i
++)
164 MIPS_SWC1 (code
, i
, mips_sp
, lmf
+ G_STRUCT_OFFSET (MonoLMF
, fregs
[i
]));
166 /* Set the magic number */
167 mips_load_const (code
, mips_at
, MIPS_LMF_MAGIC2
);
168 mips_sw (code
, mips_at
, mips_sp
, lmf
+ G_STRUCT_OFFSET(MonoLMF
, magic
));
171 mips_addiu (code
, mips_at
, mips_sp
, STACK
);
172 MIPS_SW (code
, mips_at
, mips_sp
, lmf
+ G_STRUCT_OFFSET (MonoLMF
, iregs
[mips_sp
]));
174 /* save method info (it was in t8) */
175 mips_sw (code
, mips_t8
, mips_sp
, lmf
+ G_STRUCT_OFFSET(MonoLMF
, method
));
177 /* save the IP (caller ip) */
178 if (tramp_type
== MONO_TRAMPOLINE_JUMP
) {
179 mips_sw (code
, mips_zero
, mips_sp
, lmf
+ G_STRUCT_OFFSET(MonoLMF
, eip
));
181 mips_sw (code
, mips_ra
, mips_sp
, lmf
+ G_STRUCT_OFFSET(MonoLMF
, eip
));
184 /* jump to mono_get_lmf_addr here */
185 mips_load (code
, mips_t9
, mono_get_lmf_addr
);
186 mips_jalr (code
, mips_t9
, mips_ra
);
189 /* v0 now points at the (MonoLMF **) for the current thread */
191 /* new_lmf->lmf_addr = lmf_addr -- useful when unwinding */
192 mips_sw (code
, mips_v0
, mips_sp
, lmf
+ G_STRUCT_OFFSET(MonoLMF
, lmf_addr
));
194 /* new_lmf->previous_lmf = *lmf_addr */
195 mips_lw (code
, mips_at
, mips_v0
, 0);
196 mips_sw (code
, mips_at
, mips_sp
, lmf
+ G_STRUCT_OFFSET(MonoLMF
, previous_lmf
));
198 /* *(lmf_addr) = new_lmf */
199 mips_addiu (code
, mips_at
, mips_sp
, lmf
);
200 mips_sw (code
, mips_at
, mips_v0
, 0);
203 * Now we're ready to call mips_magic_trampoline ().
206 /* Arg 1: pointer to registers so that the magic trampoline can
207 * access what we saved above
209 mips_addiu (code
, mips_a0
, mips_sp
, lmf
+ G_STRUCT_OFFSET (MonoLMF
, iregs
[0]));
211 /* Arg 2: code (next address to the instruction that called us) */
212 if (tramp_type
== MONO_TRAMPOLINE_JUMP
) {
213 mips_move (code
, mips_a1
, mips_zero
);
215 mips_lw (code
, mips_a1
, mips_sp
, STACK
+ MIPS_RET_ADDR_OFFSET
);
218 /* Arg 3: MonoMethod *method. */
219 mips_lw (code
, mips_a2
, mips_sp
, lmf
+ G_STRUCT_OFFSET (MonoLMF
, method
));
221 /* Arg 4: Trampoline */
222 mips_move (code
, mips_a3
, mips_zero
);
224 /* Now go to the trampoline */
225 tramp
= (guint8
*)mono_get_trampoline_func (tramp_type
);
226 mips_load (code
, mips_t9
, (guint32
)tramp
);
227 mips_jalr (code
, mips_t9
, mips_ra
);
230 /* Code address is now in v0, move it to at */
231 mips_move (code
, mips_at
, mips_v0
);
234 * Now unwind the MonoLMF
237 /* t0 = current_lmf->previous_lmf */
238 mips_lw (code
, mips_t0
, mips_sp
, lmf
+ G_STRUCT_OFFSET(MonoLMF
, previous_lmf
));
240 mips_lw (code
, mips_t1
, mips_sp
, lmf
+ G_STRUCT_OFFSET(MonoLMF
, lmf_addr
));
241 /* (*lmf_addr) = previous_lmf */
242 mips_sw (code
, mips_t0
, mips_t1
, 0);
244 /* Restore the callee-saved & argument registers */
245 for (i
= 0; i
< MONO_MAX_IREGS
; i
++) {
246 if ((MONO_ARCH_CALLEE_SAVED_REGS
| MONO_ARCH_CALLEE_REGS
| MIPS_ARG_REGS
) & (1 << i
))
247 MIPS_LW (code
, i
, mips_sp
, lmf
+ G_STRUCT_OFFSET (MonoLMF
, iregs
[i
]));
249 for (i
= 0; i
< MONO_MAX_FREGS
; i
++)
250 MIPS_LWC1 (code
, i
, mips_sp
, lmf
+ G_STRUCT_OFFSET (MonoLMF
, fregs
[i
]));
252 /* Non-standard function epilogue. Instead of doing a proper
253 * return, we just jump to the compiled code.
255 /* Restore ra & stack pointer, and jump to the code */
257 if (tramp_type
== MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
)
258 mips_move (code
, mips_v0
, mips_at
);
259 mips_lw (code
, mips_ra
, mips_sp
, STACK
+ MIPS_RET_ADDR_OFFSET
);
260 mips_addiu (code
, mips_sp
, mips_sp
, STACK
);
261 if (MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type
))
262 mips_jr (code
, mips_ra
);
264 mips_jr (code
, mips_at
);
267 /* Flush instruction cache, since we've generated code */
268 mono_arch_flush_icache (buf
, code
- buf
);
269 MONO_PROFILER_RAISE (jit_code_buffer
, (buf
, code
- buf
, MONO_PROFILER_CODE_BUFFER_HELPER
, NULL
));
272 g_assert ((code
- buf
) <= max_code_len
);
275 tramp_name
= mono_get_generic_trampoline_name (tramp_type
);
276 *info
= mono_tramp_info_create (tramp_name
, buf
, code
- buf
, ji
, unwind_ops
);
282 mono_arch_create_specific_trampoline (gpointer arg1
, MonoTrampolineType tramp_type
, MonoDomain
*domain
, guint32
*code_len
)
284 guint8
*code
, *buf
, *tramp
;
286 tramp
= mono_get_trampoline_code (tramp_type
);
288 code
= buf
= mono_domain_code_reserve (domain
, 32);
290 /* Prepare the jump to the generic trampoline code
291 * mono_arch_create_trampoline_code() knows we're putting this in t8
293 mips_load (code
, mips_t8
, arg1
);
295 /* Now jump to the generic trampoline code */
296 mips_load (code
, mips_at
, tramp
);
297 mips_jr (code
, mips_at
);
300 /* Flush instruction cache, since we've generated code */
301 mono_arch_flush_icache (buf
, code
- buf
);
302 MONO_PROFILER_RAISE (jit_code_buffer
, (buf
, code
- buf
, MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE
, mono_get_generic_trampoline_simple_name (tramp_type
)));
304 g_assert ((code
- buf
) <= 32);
307 *code_len
= code
- buf
;
313 mono_arch_get_static_rgctx_trampoline (gpointer arg
, gpointer addr
)
315 guint8
*code
, *start
;
318 MonoDomain
*domain
= mono_domain_get ();
322 start
= code
= mono_domain_code_reserve (domain
, buf_len
);
324 mips_load (code
, MONO_ARCH_RGCTX_REG
, arg
);
325 mips_load (code
, mips_at
, addr
);
326 mips_jr (code
, mips_at
);
329 g_assert ((code
- start
) <= buf_len
);
331 mono_arch_flush_icache (start
, code
- start
);
332 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE
, NULL
));
334 mono_tramp_info_register (mono_tramp_info_create (NULL
, start
, code
- start
, NULL
, NULL
), domain
);
340 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot
, MonoTrampInfo
**info
, gboolean aot
)
346 guint8
**rgctx_null_jumps
;
350 MonoJumpInfo
*ji
= NULL
;
351 GSList
*unwind_ops
= NULL
;
353 mrgctx
= MONO_RGCTX_SLOT_IS_MRGCTX (slot
);
354 index
= MONO_RGCTX_SLOT_INDEX (slot
);
356 index
+= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT
/ sizeof (target_mgreg_t
);
357 for (depth
= 0; ; ++depth
) {
358 int size
= mono_class_rgctx_get_array_size (depth
, mrgctx
);
360 if (index
< size
- 1)
365 tramp_size
= 64 + 16 * depth
;
367 code
= buf
= mono_global_codeman_reserve (tramp_size
);
369 mono_add_unwind_op_def_cfa (unwind_ops
, code
, buf
, mips_sp
, 0);
371 rgctx_null_jumps
= g_malloc (sizeof (guint8
*) * (depth
+ 2));
374 /* The vtable/mrgctx is in a0 */
375 g_assert (MONO_ARCH_VTABLE_REG
== mips_a0
);
378 mips_move (code
, mips_a1
, mips_a0
);
380 /* load rgctx ptr from vtable */
381 g_assert (mips_is_imm16 (MONO_STRUCT_OFFSET (MonoVTable
, runtime_generic_context
)));
382 mips_lw (code
, mips_a1
, mips_a0
, MONO_STRUCT_OFFSET (MonoVTable
, runtime_generic_context
));
383 /* is the rgctx ptr null? */
384 /* if yes, jump to actual trampoline */
385 rgctx_null_jumps
[njumps
++] = code
;
386 mips_beq (code
, mips_a1
, mips_zero
, 0);
390 for (i
= 0; i
< depth
; ++i
) {
391 /* load ptr to next array */
392 if (mrgctx
&& i
== 0) {
393 g_assert (mips_is_imm16 (MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT
));
394 mips_lw (code
, mips_a1
, mips_a1
, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT
);
396 mips_lw (code
, mips_a1
, mips_a1
, 0);
398 /* is the ptr null? */
399 /* if yes, jump to actual trampoline */
400 rgctx_null_jumps
[njumps
++] = code
;
401 mips_beq (code
, mips_a1
, mips_zero
, 0);
406 g_assert (mips_is_imm16 (sizeof (target_mgreg_t
) * (index
+ 1)));
407 mips_lw (code
, mips_a1
, mips_a1
, sizeof (target_mgreg_t
) * (index
+ 1));
408 /* is the slot null? */
409 /* if yes, jump to actual trampoline */
410 rgctx_null_jumps
[njumps
++] = code
;
411 mips_beq (code
, mips_a1
, mips_zero
, 0);
413 /* otherwise return, result is in R1 */
414 mips_move (code
, mips_v0
, mips_a1
);
415 mips_jr (code
, mips_ra
);
418 g_assert (njumps
<= depth
+ 2);
419 for (i
= 0; i
< njumps
; ++i
)
420 mips_patch ((guint32
*)rgctx_null_jumps
[i
], (guint32
)code
);
422 g_free (rgctx_null_jumps
);
426 /* The vtable/mrgctx is still in a0 */
429 ji
= mono_patch_info_list_prepend (ji
, code
- buf
, MONO_PATCH_INFO_SPECIFIC_TRAMPOLINE_LAZY_FETCH_ADDR
, GUINT_TO_POINTER (slot
));
430 mips_load (code
, mips_at
, 0);
431 mips_jr (code
, mips_at
);
434 tramp
= (guint8
*)mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot
), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
, mono_get_root_domain (), &code_len
);
435 mips_load (code
, mips_at
, tramp
);
436 mips_jr (code
, mips_at
);
440 mono_arch_flush_icache (buf
, code
- buf
);
441 MONO_PROFILER_RAISE (jit_code_buffer
, (buf
, code
- buf
, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE
, NULL
));
443 g_assert (code
- buf
<= tramp_size
);
446 char *name
= mono_get_rgctx_fetch_trampoline_name (slot
);
447 *info
= mono_tramp_info_create (name
, buf
, code
- buf
, ji
, unwind_ops
);