2 * tramp-mips.c: JIT trampoline code for MIPS
5 * Mark Mason (mason@broadcom.com)
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>
13 * (C) 2001 Ximian, Inc.
19 #include <mono/metadata/appdomain.h>
20 #include <mono/metadata/marshal.h>
21 #include <mono/metadata/tabledefs.h>
22 #include <mono/arch/mips/mips-codegen.h>
25 #include "mini-mips.h"
28 * get_unbox_trampoline:
30 * @addr: pointer to native code for @m
32 * when value type methods are called through the vtable we need to unbox the
33 * this argument. This method returns a pointer to a trampoline which does
34 * unboxing before calling the method
37 get_unbox_trampoline (MonoMethod *m, gpointer addr)
40 int this_pos = mips_a0;
41 MonoDomain *domain = mono_domain_get ();
43 if (!mono_method_signature (m)->ret->byref && MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret))
46 mono_domain_lock (domain);
47 start = code = mono_code_manager_reserve (domain->code_mp, 20);
48 mono_domain_unlock (domain);
50 mips_load (code, mips_t9, addr);
51 mips_addiu (code, this_pos, this_pos, sizeof (MonoObject));
52 mips_jr (code, mips_t9);
55 mono_arch_flush_icache (start, code - start);
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);*/
63 /* Stack size for trampoline function
64 * MIPS_MINIMAL_STACK_SIZE + 16 (args + alignment to mips_magic_trampoline)
65 * + MonoLMF + 14 fp regs + 13 gregs + alignment
66 * #define STACK (MIPS_MINIMAL_STACK_SIZE + 4 * sizeof (gulong) + sizeof (MonoLMF) + 14 * sizeof (double) + 13 * (sizeof (gulong)))
67 * STACK would be 444 for 32 bit darwin
70 #define STACK (4*4 + 8 + sizeof(MonoLMF) + 32)
73 /* Method-specific trampoline code fragment size */
74 #define METHOD_TRAMPOLINE_SIZE 64
76 /* Jump-specific trampoline code fragment size */
77 #define JUMP_TRAMPOLINE_SIZE 64
80 * mips_magic_trampoline:
81 * @code: pointer into caller code
82 * @method: the method to translate
85 * This method is called by the function 'arch_create_jit_trampoline', which in
86 * turn is called by the trampoline functions for virtual methods.
87 * After having called the JIT compiler to compile the method, it inspects the
88 * caller code to find the address of the method-specific part of the
89 * trampoline vtable slot for this method, updates it with a fragment that calls
90 * the newly compiled code and returns this address of the compiled code to
91 * 'arch_create_jit_trampoline'
94 mips_magic_trampoline (MonoMethod *method, guint32 *code, char *sp)
98 MonoJitInfo *ji, *target_ji;
102 addr = mono_compile_method (method);
108 /* We can't trampoline across domains */
109 ji = mono_jit_info_table_find (mono_domain_get (), code);
110 target_ji = mono_jit_info_table_find (mono_domain_get (), addr);
111 if (!mono_method_same_domain (ji, target_ji))
115 g_print ("mips_magic: method code at %p from %p for %s:%s\n",
116 addr, code, method->klass->name, method->name);
118 /* Locate the address of the method-specific trampoline. The call using
119 the vtable slot that took the processing flow to 'arch_create_jit_trampoline'
120 looks something like this:
129 On entry, 'code' points just after one of the above sequences.
133 if ((code[-2] >> 26) == 0x03) {
134 g_print ("direct patching\n");
135 mips_patch ((char*)(code-2), addr);
139 /* Sanity check: look for the jalr */
140 g_assert((code[-2] & 0xfc1f003f) == 0x00000009);
142 reg = (code[-2] >> 21) & 0x1f;
144 //printf ("mips_magic_trampoline: jalr @ 0x%0x, w/ reg %d\n", code-2, reg);
146 /* The lui / addiu / jalr case */
147 if ((code [-4] >> 26) == 0x0f && (code [-3] >> 26) == 0x09 && (code [-2] >> 26) == 0) {
148 mips_patch ((char*)(code-4), addr);
152 //printf ("mips_magic_trampoline: 0x%08x @ 0x%0x\n", *(code-2), code-2);
154 /* Probably a vtable lookup */
156 /* Walk backwards to find 'lw reg,XX(base)' */
158 guint32 mask = (0x3f << 26) | (0x1f << 16);
159 guint32 match = (0x23 << 26) | (reg << 16);
160 if((*code & mask) == match) {
164 /* lw reg,XX(base) */
165 base = (*code >> 21) & 0x1f;
166 soff = (*code & 0xffff);
170 reg_offset = STACK - sizeof (MonoLMF)
171 + G_STRUCT_OFFSET (MonoLMF, iregs[base]);
172 /* o contains now the value of register reg */
173 vtable = *((char**) (sp + reg_offset));
175 g_print ("patching reg is %d, offset %d (vtable %p) @ %p\n",
176 base, offset, vtable, code);
182 /* this is not done for non-virtual calls, because in that case
183 we won't have an object, but the actual pointer to the
184 valuetype as the this argument
186 if (method->klass->valuetype && !mono_aot_is_got_entry (code, vtable))
187 addr = get_unbox_trampoline (method, addr);
190 if (mono_aot_is_got_entry (code, vtable) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable))
191 *((gpointer *)vtable) = addr;
196 mips_class_init_trampoline (void *vtable, guint32 *code, char *sp)
198 //g_print ("mips_class_init: vtable=%p code=%p sp=%p\n", vtable, code, sp);
200 mono_runtime_class_init (vtable);
202 /* back up to the jal/jalr instruction */
205 /* Check for jal/jalr -- and NOP it out */
206 if ((((*code)&0xfc000000) == 0x0c000000)
207 || (((*code)&0xfc1f003f) == 0x00000009)) {
209 mono_arch_flush_icache (code-1, 4);
212 g_assert_not_reached ();
217 * Stack frame description when the generic trampoline is called.
219 * --------------------
221 * -------------------
222 * Saved FP registers 0-13
223 * -------------------
224 * Saved general registers 0-12
225 * -------------------
226 * param area for 3 args to mips_magic_trampoline
227 * -------------------
229 * -------------------
232 mono_arch_create_trampoline_code (MonoTrampolineType tramp_type)
234 guint8 *buf, *code = NULL;
237 /* Now we'll create in 'buf' the MIPS trampoline code. This
238 is the trampoline code common to all methods */
240 code = buf = mono_global_codeman_reserve (512);
242 /* Allocate the stack frame, and save the return address */
243 mips_addiu (code, mips_sp, mips_sp, -STACK);
244 mips_sw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
246 /* offset of MonoLMF from sp */
247 lmf = STACK - sizeof (MonoLMF);
248 for (i = 0; i < MONO_MAX_IREGS; i++)
249 mips_sw (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
250 for (i = 0; i < MONO_MAX_FREGS; i++)
251 mips_swc1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
253 /* jump to mono_get_lmf_addr here */
254 mips_load (code, mips_t9, mono_get_lmf_addr);
255 mips_jalr (code, mips_t9, mips_ra);
258 /* v0 now points at the (MonoLMF **) for the current thread */
260 /* we build the MonoLMF structure on the stack - see mini-mips.h
261 * The pointer to the struct is put in mips_s0 (new_lmf).
264 mips_addiu (code, mips_t2, mips_sp, lmf);
266 /* new_lmf->lmf_addr = lmf_addr -- useful when unwinding */
267 mips_sw (code, mips_v0, mips_t2, G_STRUCT_OFFSET(MonoLMF, lmf_addr));
269 /* new_lmf->previous_lmf = *lmf_addr */
270 mips_lw (code, mips_at, mips_v0, 0);
271 mips_sw (code, mips_at, mips_t2, G_STRUCT_OFFSET(MonoLMF, previous_lmf));
273 /* *(lmf_addr) = t2 */
274 mips_sw (code, mips_t2, mips_v0, 0);
276 /* save method info (it was in t8) */
277 mips_lw (code, mips_at, mips_t2, G_STRUCT_OFFSET(MonoLMF, iregs[mips_t8]));
278 mips_sw (code, mips_at, mips_t2, G_STRUCT_OFFSET(MonoLMF, method));
280 mips_sw (code, mips_sp, mips_t2, G_STRUCT_OFFSET(MonoLMF, ebp));
282 /* save the IP (caller ip) */
283 if (tramp_type == MONO_TRAMPOLINE_JUMP) {
284 mips_sw (code, mips_zero, mips_t2, G_STRUCT_OFFSET(MonoLMF, eip));
286 mips_lw (code, mips_at, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
287 mips_sw (code, mips_at, mips_t2, G_STRUCT_OFFSET(MonoLMF, eip));
291 * Now we're ready to call mips_magic_trampoline ().
294 /* Arg 1: MonoMethod *method. */
295 mips_lw (code, mips_a0, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[mips_t8]));
297 /* Arg 2: code (next address to the instruction that called us) */
298 if (tramp_type == MONO_TRAMPOLINE_JUMP) {
299 mips_move (code, mips_a1, mips_zero);
301 mips_lw (code, mips_a1, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
304 /* Arg 3: stack pointer so that the magic trampoline can access the
305 * registers we saved above
307 mips_move (code, mips_a2, mips_sp);
309 /* Now go to the trampoline */
310 if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT) {
311 mips_load (code, mips_t9, (guint32)mips_class_init_trampoline);
313 mips_load (code, mips_t9, (guint32)mips_magic_trampoline);
315 mips_jalr (code, mips_t9, mips_ra);
318 /* Code address is now in v0, move it to at */
319 mips_move (code, mips_at, mips_v0);
322 * Now unwind the MonoLMF
325 /* t0 = current_lmf->previous_lmf */
326 mips_lw (code, mips_t0, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
328 mips_lw (code, mips_t1, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
329 /* (*lmf_addr) = previous_lmf */
330 mips_sw (code, mips_t0, mips_t1, 0);
332 /* Restore the callee-saved & argument registers */
333 for (i = 0; i < MONO_MAX_IREGS; i++) {
334 if ((MONO_ARCH_CALLEE_SAVED_REGS | MONO_ARCH_CALLEE_REGS | MIPS_ARG_REGS) & (1 << i))
335 mips_lw (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
337 /* XXX - Restore the float registers */
339 /* Non-standard function epilogue. Instead of doing a proper
340 * return, we just jump to the compiled code.
342 /* Restore ra & stack pointer, and jump to the code */
344 mips_lw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
345 mips_addiu (code, mips_sp, mips_sp, STACK);
346 mips_jr (code, mips_at);
349 /* Flush instruction cache, since we've generated code */
350 mono_arch_flush_icache (buf, code - buf);
353 g_assert ((code - buf) <= 512);
359 create_specific_tramp (MonoMethod *method, guint8* tramp, MonoDomain *domain) {
363 mono_domain_lock (domain);
364 code = buf = mono_code_manager_reserve (domain->code_mp, 32);
365 mono_domain_unlock (domain);
367 /* Prepare the jump to the generic trampoline code
368 * mono_arch_create_trampoline_code() knows we're putting this in t8
370 mips_load (code, mips_t8, method);
372 /* Now jump to the generic trampoline code */
373 mips_load (code, mips_at, tramp);
374 mips_jr (code, mips_at);
377 /* Flush instruction cache, since we've generated code */
378 mono_arch_flush_icache (buf, code - buf);
380 g_assert ((code - buf) <= 32);
382 ji = g_new0 (MonoJitInfo, 1);
384 ji->code_start = buf;
385 ji->code_size = code - buf;
387 mono_jit_stats.method_trampolines++;
393 mono_arch_create_jump_trampoline (MonoMethod *method)
396 MonoDomain* domain = mono_domain_get ();
398 tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_JUMP);
399 return create_specific_tramp (method, tramp, domain);
403 * arch_create_jit_trampoline:
404 * @method: pointer to the method info
406 * Creates a trampoline function for virtual methods. If the created
407 * code is called it first starts JIT compilation of method,
408 * and then calls the newly created method. It also replaces the
409 * corresponding vtable entry (see mips_magic_trampoline).
411 * A trampoline consists of two parts: a main fragment, shared by all method
412 * trampolines, and some code specific to each method, which hard-codes a
413 * reference to that method and then calls the main fragment.
415 * The main fragment contains a call to 'mips_magic_trampoline', which performs
416 * call to the JIT compiler and substitutes the method-specific fragment with
417 * some code that directly calls the JIT-compiled method.
419 * Returns: a pointer to the newly created code
422 mono_arch_create_jit_trampoline (MonoMethod *method)
426 MonoDomain* domain = mono_domain_get ();
429 tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_GENERIC);
430 /* FIXME: should pass the domain down to this function */
431 ji = create_specific_tramp (method, tramp, domain);
432 code_start = ji->code_start;
439 * mono_arch_create_class_init_trampoline:
440 * @vtable: the type to initialize
442 * Creates a trampoline function to run a type initializer.
443 * If the trampoline is called, it calls mono_runtime_class_init with the
444 * given vtable, then patches the caller code so it does not get called any
447 * Returns: a pointer to the newly created code
450 mono_arch_create_class_init_trampoline (MonoVTable *vtable)
452 guint8 *code, *buf, *tramp;
454 tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
456 /* This is the method-specific part of the trampoline. Its purpose is
457 to provide the generic part with the MonoMethod *method pointer. We'll
458 use r11 to keep that value, for instance. However, the generic part of
459 the trampoline relies on r11 having the same value it had before coming
460 here, so we must save it before. */
461 mono_domain_lock (vtable->domain);
462 code = buf = mono_code_manager_reserve (vtable->domain->code_mp, METHOD_TRAMPOLINE_SIZE);
463 mono_domain_unlock (vtable->domain);
465 //g_print ("mips_class_init_tramp buf=%p tramp=%p\n", buf, tramp);
467 mips_addiu (code, mips_sp, mips_sp, -MIPS_MINIMAL_STACK_SIZE);
468 mips_sw (code, mips_ra, mips_sp, MIPS_MINIMAL_STACK_SIZE + MIPS_RET_ADDR_OFFSET);
470 /* Probably need to save/restore a0-a3 here */
472 mips_load (code, mips_a0, vtable);
473 mips_move (code, mips_a1, mips_ra);
474 mips_move (code, mips_a2, mips_zero);
476 mips_load (code, mips_t9, mips_class_init_trampoline);
477 mips_jalr (code, mips_t9, mips_ra);
480 mips_lw (code, mips_ra, mips_sp, MIPS_MINIMAL_STACK_SIZE + MIPS_RET_ADDR_OFFSET);
481 mips_addiu (code, mips_sp, mips_sp, MIPS_MINIMAL_STACK_SIZE);
482 mips_jr (code, mips_ra);
485 /* Flush instruction cache, since we've generated code */
486 mono_arch_flush_icache (buf, code - buf);
489 g_assert ((code - buf) <= METHOD_TRAMPOLINE_SIZE);
490 mono_jit_stats.method_trampolines++;
496 * This method is only called when running in the Mono Debugger.
499 mono_debugger_create_notification_function (MonoCodeManager *codeman)
504 codeman = mono_code_manager_reserve (codeman, 16);
505 mips_break (buf, 0xd0);
506 mips_jr (buf, mips_ra);
508 mono_arch_flush_icache (ptr, buf - ptr);