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"
31 * get_unbox_trampoline:
33 * @addr: pointer to native code for @m
35 * when value type methods are called through the vtable we need to unbox the
36 * this argument. This method returns a pointer to a trampoline which does
37 * unboxing before calling the method
40 mono_arch_get_unbox_trampoline (MonoMethod
*m
, gpointer addr
)
43 MonoDomain
*domain
= mono_domain_get ();
45 start
= code
= mono_domain_code_reserve (domain
, 20);
47 mips_load (code
, mips_t9
, addr
);
48 /* The this pointer is kept in a0 */
49 mips_addiu (code
, mips_a0
, mips_a0
, MONO_ABI_SIZEOF (MonoObject
));
50 mips_jr (code
, mips_t9
);
53 mono_arch_flush_icache (start
, code
- start
);
54 MONO_PROFILER_RAISE (jit_code_buffer
, (start
, code
- start
, MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE
, m
));
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);*/
60 mono_tramp_info_register (mono_tramp_info_create (NULL
, start
, code
- start
, NULL
, NULL
), domain
);
66 mono_arch_patch_callsite (guint8
*method_start
, guint8
*orig_code
, guint8
*addr
)
68 guint32
*code
= (guint32
*)orig_code
;
70 /* Locate the address of the method-specific trampoline.
71 The call using the vtable slot that took the processing flow to
72 'arch_create_jit_trampoline' looks something like one of these:
82 On entry, 'code' points just after one of the above sequences.
86 if ((code
[-2] >> 26) == 0x03) {
87 //g_print ("direct patching\n");
88 mips_patch ((code
-2), (gsize
)addr
);
91 /* Look for the jalr */
92 if ((code
[-2] & 0xfc1f003f) == 0x00000009) {
93 /* The lui / addiu / jalr case */
94 if ((code
[-4] >> 26) == 0x0f && (code
[-3] >> 26) == 0x09
95 && (code
[-2] >> 26) == 0) {
96 mips_patch ((code
-4), (gsize
)addr
);
100 g_print("error: bad patch at 0x%08x\n", code
);
101 g_assert_not_reached ();
105 mono_arch_patch_plt_entry (guint8
*code
, gpointer
*got
, host_mgreg_t
*regs
, guint8
*addr
)
107 g_assert_not_reached ();
110 /* Stack size for trampoline function
111 * MIPS_MINIMAL_STACK_SIZE + 16 (args + alignment to mips_magic_trampoline)
112 * + MonoLMF + 14 fp regs + 13 gregs + alignment
113 * #define STACK (MIPS_MINIMAL_STACK_SIZE + 4 * sizeof (gulong) + sizeof (MonoLMF) + 14 * sizeof (double) + 13 * (sizeof (gulong)))
114 * STACK would be 444 for 32 bit darwin
117 #define STACK (int)(ALIGN_TO(4*IREG_SIZE + 8 + sizeof(MonoLMF) + 32, 8))
120 * Stack frame description when the generic trampoline is called.
122 * --------------------
124 * -------------------
125 * Saved FP registers 0-13
126 * -------------------
127 * Saved general registers 0-12
128 * -------------------
129 * param area for 3 args to mips_magic_trampoline
130 * -------------------
132 * -------------------
135 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type
, MonoTrampInfo
**info
, gboolean aot
)
138 guint8
*buf
, *tramp
, *code
= NULL
;
140 GSList
*unwind_ops
= NULL
;
141 MonoJumpInfo
*ji
= NULL
;
142 int max_code_len
= 768;
144 /* AOT not supported on MIPS yet */
147 /* Now we'll create in 'buf' the MIPS trampoline code. This
148 is the trampoline code common to all methods */
150 code
= buf
= mono_global_codeman_reserve (max_code_len
);
152 /* Allocate the stack frame, and save the return address */
153 mips_addiu (code
, mips_sp
, mips_sp
, -STACK
);
154 mips_sw (code
, mips_ra
, mips_sp
, STACK
+ MIPS_RET_ADDR_OFFSET
);
156 /* we build the MonoLMF structure on the stack - see mini-mips.h */
157 /* offset of MonoLMF from sp */
158 lmf
= STACK
- sizeof (MonoLMF
) - 8;
160 for (i
= 0; i
< MONO_MAX_IREGS
; i
++)
161 MIPS_SW (code
, i
, mips_sp
, lmf
+ G_STRUCT_OFFSET (MonoLMF
, iregs
[i
]));
162 for (i
= 0; i
< MONO_MAX_FREGS
; i
++)
163 MIPS_SWC1 (code
, i
, mips_sp
, lmf
+ G_STRUCT_OFFSET (MonoLMF
, fregs
[i
]));
165 /* Set the magic number */
166 mips_load_const (code
, mips_at
, MIPS_LMF_MAGIC2
);
167 mips_sw (code
, mips_at
, mips_sp
, lmf
+ G_STRUCT_OFFSET(MonoLMF
, magic
));
170 mips_addiu (code
, mips_at
, mips_sp
, STACK
);
171 MIPS_SW (code
, mips_at
, mips_sp
, lmf
+ G_STRUCT_OFFSET (MonoLMF
, iregs
[mips_sp
]));
173 /* save method info (it was in t8) */
174 mips_sw (code
, mips_t8
, mips_sp
, lmf
+ G_STRUCT_OFFSET(MonoLMF
, method
));
176 /* save the IP (caller ip) */
177 if (tramp_type
== MONO_TRAMPOLINE_JUMP
) {
178 mips_sw (code
, mips_zero
, mips_sp
, lmf
+ G_STRUCT_OFFSET(MonoLMF
, eip
));
180 mips_sw (code
, mips_ra
, mips_sp
, lmf
+ G_STRUCT_OFFSET(MonoLMF
, eip
));
183 /* jump to mono_get_lmf_addr here */
184 mips_load (code
, mips_t9
, mono_get_lmf_addr
);
185 mips_jalr (code
, mips_t9
, mips_ra
);
188 /* v0 now points at the (MonoLMF **) for the current thread */
190 /* new_lmf->lmf_addr = lmf_addr -- useful when unwinding */
191 mips_sw (code
, mips_v0
, mips_sp
, lmf
+ G_STRUCT_OFFSET(MonoLMF
, lmf_addr
));
193 /* new_lmf->previous_lmf = *lmf_addr */
194 mips_lw (code
, mips_at
, mips_v0
, 0);
195 mips_sw (code
, mips_at
, mips_sp
, lmf
+ G_STRUCT_OFFSET(MonoLMF
, previous_lmf
));
197 /* *(lmf_addr) = new_lmf */
198 mips_addiu (code
, mips_at
, mips_sp
, lmf
);
199 mips_sw (code
, mips_at
, mips_v0
, 0);
202 * Now we're ready to call mips_magic_trampoline ().
205 /* Arg 1: pointer to registers so that the magic trampoline can
206 * access what we saved above
208 mips_addiu (code
, mips_a0
, mips_sp
, lmf
+ G_STRUCT_OFFSET (MonoLMF
, iregs
[0]));
210 /* Arg 2: code (next address to the instruction that called us) */
211 if (tramp_type
== MONO_TRAMPOLINE_JUMP
) {
212 mips_move (code
, mips_a1
, mips_zero
);
214 mips_lw (code
, mips_a1
, mips_sp
, STACK
+ MIPS_RET_ADDR_OFFSET
);
217 /* Arg 3: MonoMethod *method. */
218 mips_lw (code
, mips_a2
, mips_sp
, lmf
+ G_STRUCT_OFFSET (MonoLMF
, method
));
220 /* Arg 4: Trampoline */
221 mips_move (code
, mips_a3
, mips_zero
);
223 /* Now go to the trampoline */
224 tramp
= (guint8
*)mono_get_trampoline_func (tramp_type
);
225 mips_load (code
, mips_t9
, (guint32
)tramp
);
226 mips_jalr (code
, mips_t9
, mips_ra
);
229 /* Code address is now in v0, move it to at */
230 mips_move (code
, mips_at
, mips_v0
);
233 * Now unwind the MonoLMF
236 /* t0 = current_lmf->previous_lmf */
237 mips_lw (code
, mips_t0
, mips_sp
, lmf
+ G_STRUCT_OFFSET(MonoLMF
, previous_lmf
));
239 mips_lw (code
, mips_t1
, mips_sp
, lmf
+ G_STRUCT_OFFSET(MonoLMF
, lmf_addr
));
240 /* (*lmf_addr) = previous_lmf */
241 mips_sw (code
, mips_t0
, mips_t1
, 0);
243 /* Restore the callee-saved & argument registers */
244 for (i
= 0; i
< MONO_MAX_IREGS
; i
++) {
245 if ((MONO_ARCH_CALLEE_SAVED_REGS
| MONO_ARCH_CALLEE_REGS
| MIPS_ARG_REGS
) & (1 << i
))
246 MIPS_LW (code
, i
, mips_sp
, lmf
+ G_STRUCT_OFFSET (MonoLMF
, iregs
[i
]));
248 for (i
= 0; i
< MONO_MAX_FREGS
; i
++)
249 MIPS_LWC1 (code
, i
, mips_sp
, lmf
+ G_STRUCT_OFFSET (MonoLMF
, fregs
[i
]));
251 /* Non-standard function epilogue. Instead of doing a proper
252 * return, we just jump to the compiled code.
254 /* Restore ra & stack pointer, and jump to the code */
256 if (tramp_type
== MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
)
257 mips_move (code
, mips_v0
, mips_at
);
258 mips_lw (code
, mips_ra
, mips_sp
, STACK
+ MIPS_RET_ADDR_OFFSET
);
259 mips_addiu (code
, mips_sp
, mips_sp
, STACK
);
260 if (MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type
))
261 mips_jr (code
, mips_ra
);
263 mips_jr (code
, mips_at
);
266 /* Flush instruction cache, since we've generated code */
267 mono_arch_flush_icache (buf
, code
- buf
);
268 MONO_PROFILER_RAISE (jit_code_buffer
, (buf
, code
- buf
, MONO_PROFILER_CODE_BUFFER_HELPER
, NULL
));
271 g_assert ((code
- buf
) <= max_code_len
);
274 tramp_name
= mono_get_generic_trampoline_name (tramp_type
);
275 *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_JIT_ICALL_ADDR
, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", 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
);