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;
236 int max_code_len = 768;
238 /* Now we'll create in 'buf' the MIPS trampoline code. This
239 is the trampoline code common to all methods */
241 code = buf = mono_global_codeman_reserve (max_code_len);
243 /* Allocate the stack frame, and save the return address */
244 mips_addiu (code, mips_sp, mips_sp, -STACK);
245 mips_sw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
247 /* we build the MonoLMF structure on the stack - see mini-mips.h */
248 /* offset of MonoLMF from sp */
249 lmf = STACK - sizeof (MonoLMF) - 8;
251 for (i = 0; i < MONO_MAX_IREGS; i++)
252 mips_sw (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
253 for (i = 0; i < MONO_MAX_FREGS; i++)
254 mips_swc1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
256 /* Set the magic number */
257 mips_load_const (code, mips_at, MIPS_LMF_MAGIC2);
258 mips_sw (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, magic));
260 /* save method info (it was in t8) */
261 mips_sw (code, mips_t8, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, method));
263 /* save frame pointer (caller fp) */
264 mips_sw (code, mips_fp, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, ebp));
266 /* save the IP (caller ip) */
267 if (tramp_type == MONO_TRAMPOLINE_JUMP) {
268 mips_sw (code, mips_zero, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, eip));
270 mips_sw (code, mips_ra, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, eip));
273 /* jump to mono_get_lmf_addr here */
274 mips_load (code, mips_t9, mono_get_lmf_addr);
275 mips_jalr (code, mips_t9, mips_ra);
278 /* v0 now points at the (MonoLMF **) for the current thread */
280 /* new_lmf->lmf_addr = lmf_addr -- useful when unwinding */
281 mips_sw (code, mips_v0, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
283 /* new_lmf->previous_lmf = *lmf_addr */
284 mips_lw (code, mips_at, mips_v0, 0);
285 mips_sw (code, mips_at, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
287 /* *(lmf_addr) = new_lmf */
288 mips_addiu (code, mips_at, mips_sp, lmf);
289 mips_sw (code, mips_at, mips_v0, 0);
292 * Now we're ready to call mips_magic_trampoline ().
295 /* Arg 1: MonoMethod *method. */
296 mips_lw (code, mips_a0, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, method));
298 /* Arg 2: code (next address to the instruction that called us) */
299 if (tramp_type == MONO_TRAMPOLINE_JUMP) {
300 mips_move (code, mips_a1, mips_zero);
302 mips_lw (code, mips_a1, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
305 /* Arg 3: stack pointer so that the magic trampoline can access the
306 * registers we saved above
308 mips_move (code, mips_a2, mips_sp);
310 /* Now go to the trampoline */
311 if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT) {
312 mips_load (code, mips_t9, (guint32)mips_class_init_trampoline);
314 mips_load (code, mips_t9, (guint32)mips_magic_trampoline);
316 mips_jalr (code, mips_t9, mips_ra);
319 /* Code address is now in v0, move it to at */
320 mips_move (code, mips_at, mips_v0);
323 * Now unwind the MonoLMF
326 /* t0 = current_lmf->previous_lmf */
327 mips_lw (code, mips_t0, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
329 mips_lw (code, mips_t1, mips_sp, lmf + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
330 /* (*lmf_addr) = previous_lmf */
331 mips_sw (code, mips_t0, mips_t1, 0);
333 /* Restore the callee-saved & argument registers */
334 for (i = 0; i < MONO_MAX_IREGS; i++) {
335 if ((MONO_ARCH_CALLEE_SAVED_REGS | MONO_ARCH_CALLEE_REGS | MIPS_ARG_REGS) & (1 << i))
336 mips_lw (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, iregs[i]));
338 for (i = 0; i < MONO_MAX_FREGS; i++)
339 mips_lwc1 (code, i, mips_sp, lmf + G_STRUCT_OFFSET (MonoLMF, fregs[i]));
341 /* Non-standard function epilogue. Instead of doing a proper
342 * return, we just jump to the compiled code.
344 /* Restore ra & stack pointer, and jump to the code */
346 mips_lw (code, mips_ra, mips_sp, STACK + MIPS_RET_ADDR_OFFSET);
347 mips_addiu (code, mips_sp, mips_sp, STACK);
348 mips_jr (code, mips_at);
351 /* Flush instruction cache, since we've generated code */
352 mono_arch_flush_icache (buf, code - buf);
355 g_assert ((code - buf) <= max_code_len);
361 create_specific_tramp (MonoMethod *method, guint8* tramp, MonoDomain *domain) {
365 mono_domain_lock (domain);
366 code = buf = mono_code_manager_reserve (domain->code_mp, 32);
367 mono_domain_unlock (domain);
369 /* Prepare the jump to the generic trampoline code
370 * mono_arch_create_trampoline_code() knows we're putting this in t8
372 mips_load (code, mips_t8, method);
374 /* Now jump to the generic trampoline code */
375 mips_load (code, mips_at, tramp);
376 mips_jr (code, mips_at);
379 /* Flush instruction cache, since we've generated code */
380 mono_arch_flush_icache (buf, code - buf);
382 g_assert ((code - buf) <= 32);
384 ji = g_new0 (MonoJitInfo, 1);
386 ji->code_start = buf;
387 ji->code_size = code - buf;
389 mono_jit_stats.method_trampolines++;
395 mono_arch_create_jump_trampoline (MonoMethod *method)
398 MonoDomain* domain = mono_domain_get ();
400 tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_JUMP);
401 return create_specific_tramp (method, tramp, domain);
405 * arch_create_jit_trampoline:
406 * @method: pointer to the method info
408 * Creates a trampoline function for virtual methods. If the created
409 * code is called it first starts JIT compilation of method,
410 * and then calls the newly created method. It also replaces the
411 * corresponding vtable entry (see mips_magic_trampoline).
413 * A trampoline consists of two parts: a main fragment, shared by all method
414 * trampolines, and some code specific to each method, which hard-codes a
415 * reference to that method and then calls the main fragment.
417 * The main fragment contains a call to 'mips_magic_trampoline', which performs
418 * call to the JIT compiler and substitutes the method-specific fragment with
419 * some code that directly calls the JIT-compiled method.
421 * Returns: a pointer to the newly created code
424 mono_arch_create_jit_trampoline (MonoMethod *method)
428 MonoDomain* domain = mono_domain_get ();
431 tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_GENERIC);
432 /* FIXME: should pass the domain down to this function */
433 ji = create_specific_tramp (method, tramp, domain);
434 code_start = ji->code_start;
441 * mono_arch_create_class_init_trampoline:
442 * @vtable: the type to initialize
444 * Creates a trampoline function to run a type initializer.
445 * If the trampoline is called, it calls mono_runtime_class_init with the
446 * given vtable, then patches the caller code so it does not get called any
449 * Returns: a pointer to the newly created code
452 mono_arch_create_class_init_trampoline (MonoVTable *vtable)
454 guint8 *code, *buf, *tramp;
456 tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
458 /* This is the method-specific part of the trampoline. Its purpose is
459 to provide the generic part with the MonoMethod *method pointer. We'll
460 use r11 to keep that value, for instance. However, the generic part of
461 the trampoline relies on r11 having the same value it had before coming
462 here, so we must save it before. */
463 mono_domain_lock (vtable->domain);
464 code = buf = mono_code_manager_reserve (vtable->domain->code_mp, METHOD_TRAMPOLINE_SIZE);
465 mono_domain_unlock (vtable->domain);
467 //g_print ("mips_class_init_tramp buf=%p tramp=%p\n", buf, tramp);
469 mips_addiu (code, mips_sp, mips_sp, -MIPS_MINIMAL_STACK_SIZE);
470 mips_sw (code, mips_ra, mips_sp, MIPS_MINIMAL_STACK_SIZE + MIPS_RET_ADDR_OFFSET);
472 /* Probably need to save/restore a0-a3 here */
474 mips_load (code, mips_a0, vtable);
475 mips_move (code, mips_a1, mips_ra);
476 mips_move (code, mips_a2, mips_zero);
478 mips_load (code, mips_t9, mips_class_init_trampoline);
479 mips_jalr (code, mips_t9, mips_ra);
482 mips_lw (code, mips_ra, mips_sp, MIPS_MINIMAL_STACK_SIZE + MIPS_RET_ADDR_OFFSET);
483 mips_addiu (code, mips_sp, mips_sp, MIPS_MINIMAL_STACK_SIZE);
484 mips_jr (code, mips_ra);
487 /* Flush instruction cache, since we've generated code */
488 mono_arch_flush_icache (buf, code - buf);
491 g_assert ((code - buf) <= METHOD_TRAMPOLINE_SIZE);
492 mono_jit_stats.method_trampolines++;
498 * This method is only called when running in the Mono Debugger.
501 mono_debugger_create_notification_function (void)
506 ptr = buf = mono_global_codeman_reserve (16);
507 mips_break (buf, 0xd0);
508 mips_jr (buf, mips_ra);
510 mono_arch_flush_icache (ptr, buf - ptr);