2 * tramp-arm.c: JIT trampoline code for ARM
5 * Paolo Molaro (lupus@ximian.com)
7 * (C) 2001 Ximian, Inc.
13 #include <mono/metadata/appdomain.h>
14 #include <mono/metadata/marshal.h>
15 #include <mono/metadata/tabledefs.h>
16 #include <mono/arch/arm/arm-codegen.h>
22 * get_unbox_trampoline:
24 * @addr: pointer to native code for @m
26 * when value type methods are called through the vtable we need to unbox the
27 * this argument. This method returns a pointer to a trampoline which does
28 * unboxing before calling the method
31 get_unbox_trampoline (MonoMethod *m, gpointer addr)
35 MonoDomain *domain = mono_domain_get ();
37 if (!mono_method_signature (m)->ret->byref && MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret))
40 mono_domain_lock (domain);
41 start = code = mono_code_manager_reserve (domain->code_mp, 16);
42 mono_domain_unlock (domain);
44 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 4);
45 ARM_ADD_REG_IMM8 (code, this_pos, this_pos, sizeof (MonoObject));
46 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
47 *(guint32*)code = (guint32)addr;
49 mono_arch_flush_icache (start, code - start);
50 g_assert ((code - start) <= 16);
51 /*g_print ("unbox trampoline at %d for %s:%s\n", this_pos, m->klass->name, m->name);
52 g_print ("unbox code is at %p for method at %p\n", start, addr);*/
57 /* Stack size for trampoline function
59 #define STACK (sizeof (MonoLMF))
61 /* Method-specific trampoline code fragment size */
62 #define METHOD_TRAMPOLINE_SIZE 64
64 /* Jump-specific trampoline code fragment size */
65 #define JUMP_TRAMPOLINE_SIZE 64
68 * arm_magic_trampoline:
69 * @code: pointer into caller code
70 * @method: the method to translate
73 * This method is called by the function 'arch_create_jit_trampoline', which in
74 * turn is called by the trampoline functions for virtual methods.
75 * After having called the JIT compiler to compile the method, it inspects the
76 * caller code to find the address of the method-specific part of the
77 * trampoline vtable slot for this method, updates it with a fragment that calls
78 * the newly compiled code and returns this address of the compiled code to
79 * 'arch_create_jit_trampoline'
82 arm_magic_trampoline (MonoMethod *method, guint32 *code, gchar **sp)
86 MonoJitInfo *ji, *target_ji;
89 addr = mono_compile_method (method);
90 /*g_print ("method code at %p for %s:%s\n", addr, method->klass->name, method->name);*/
97 /* We can't trampoline across domains */
98 ji = mono_jit_info_table_find (mono_domain_get (), code);
99 target_ji = mono_jit_info_table_find (mono_domain_get (), addr);
100 if (!mono_method_same_domain (ji, target_ji))
103 /* Locate the address of the method-specific trampoline. The call using
104 the vtable slot that took the processing flow to 'arch_create_jit_trampoline'
105 looks something like this:
114 The call sequence could be also:
117 function pointer literal
121 Note that on ARM5+ we can use one instruction instead of the last two.
122 Therefore, we need to locate the 'ldr rA' instruction to know which
123 register was used to hold the method addrs.
126 /* This is the 'bl' or the 'mov pc' instruction */
130 * Note that methods are called also with the bl opcode.
132 if ((((*code) >> 25) & 7) == 5) {
133 /*g_print ("direct patching\n");*/
134 arm_patch ((char*)code, addr);
135 mono_arch_flush_icache ((char*)code, 4);
139 /* ldr pc, rX, #offset */
140 #define LDR_MASK ((0xf << ARMCOND_SHIFT) | (3 << 26) | (1 << 22) | (1 << 20) | (15 << 12))
141 #define LDR_PC_VAL ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 26) | (0 << 22) | (1 << 20) | (15 << 12))
142 if ((*code & LDR_MASK) == LDR_PC_VAL) {
143 reg = (*code >> 16 ) & 0xf;
144 offset = *code & 0xfff;
145 /*g_print ("found vcall at r%d + %d\n", reg, offset);*/
149 /* this is not done for non-virtual calls, because in that case
150 we won't have an object, but the actual pointer to the
151 valuetype as the this argument
153 if (method->klass->valuetype && !mono_aot_is_got_entry (code, o))
154 addr = get_unbox_trampoline (method, addr);
158 if (mono_aot_is_got_entry (code, o) || mono_domain_owns_vtable_slot (mono_domain_get (), o))
159 *((gpointer *)o) = addr;
161 /*g_print ("no callsite patching\n");
162 mono_disassemble_code (code -3, 16, "callsite");*/
169 arm_class_init_trampoline (void *vtable, guint32 *code, char *sp)
171 mono_runtime_class_init (vtable);
174 /* This is the 'bl' instruction */
177 if ((((*code) >> 25) & 7) == 5) {
178 ARM_NOP (code); /* nop */
179 mono_arch_flush_icache (code, 4);
182 g_assert_not_reached ();
188 * Stack frame description when the generic trampoline is called.
190 * ------------------- old sp
192 * ------------------- sp
195 mono_arch_create_trampoline_code (MonoTrampolineType tramp_type)
197 guint8 *buf, *code = NULL;
199 guint8 *load_get_lmf_addr, *load_trampoline;
202 /* Now we'll create in 'buf' the ARM trampoline code. This
203 is the trampoline code common to all methods */
205 code = buf = g_malloc (148);
208 * At this point r0 has the method and sp points to the saved
209 * regs on the stack (all but PC and SP).
211 ARM_MOV_REG_REG (buf, ARMREG_V1, ARMREG_SP);
212 ARM_MOV_REG_REG (buf, ARMREG_V2, ARMREG_R0);
213 ARM_MOV_REG_REG (buf, ARMREG_V3, ARMREG_LR);
215 /* ok, now we can continue with the MonoLMF setup, mostly untouched
216 * from emit_prolog in mini-arm.c
217 * This is a sinthetized call to mono_get_lmf_addr ()
219 load_get_lmf_addr = buf;
221 ARM_MOV_REG_REG (buf, ARMREG_LR, ARMREG_PC);
222 ARM_MOV_REG_REG (buf, ARMREG_PC, ARMREG_R0);
224 /* we build the MonoLMF structure on the stack - see mini-arm.h
225 * The pointer to the struct is put in r1.
226 * the iregs array is already allocated on the stack by push.
228 ARM_SUB_REG_IMM8 (buf, ARMREG_SP, ARMREG_SP, sizeof (MonoLMF) - sizeof (guint) * 14);
229 ARM_ADD_REG_IMM8 (buf, ARMREG_R1, ARMREG_SP, STACK - sizeof (MonoLMF));
230 /* r0 is the result from mono_get_lmf_addr () */
231 ARM_STR_IMM (buf, ARMREG_R0, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
232 /* new_lmf->previous_lmf = *lmf_addr */
233 ARM_LDR_IMM (buf, ARMREG_R2, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
234 ARM_STR_IMM (buf, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
235 /* *(lmf_addr) = r1 */
236 ARM_STR_IMM (buf, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
237 /* save method info (it's in v2) */
238 ARM_STR_IMM (buf, ARMREG_V2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, method));
239 ARM_STR_IMM (buf, ARMREG_SP, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, ebp));
240 /* save the IP (caller ip) */
241 if (tramp_type == MONO_TRAMPOLINE_JUMP) {
242 ARM_MOV_REG_IMM8 (buf, ARMREG_R2, 0);
244 /* assumes STACK == sizeof (MonoLMF) */
245 ARM_LDR_IMM (buf, ARMREG_R2, ARMREG_SP, (G_STRUCT_OFFSET (MonoLMF, iregs) + 13*4));
247 ARM_STR_IMM (buf, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, eip));
250 * Now we're ready to call arm_magic_trampoline ().
252 /* Arg 1: MonoMethod *method. It was put in v2 */
253 ARM_MOV_REG_REG (buf, ARMREG_R0, ARMREG_V2);
255 /* Arg 2: code (next address to the instruction that called us) */
256 if (tramp_type == MONO_TRAMPOLINE_JUMP) {
257 ARM_MOV_REG_IMM8 (buf, ARMREG_R1, 0);
259 ARM_MOV_REG_REG (buf, ARMREG_R1, ARMREG_V3);
262 /* Arg 3: stack pointer so that the magic trampoline can access the
263 * registers we saved above
265 ARM_MOV_REG_REG (buf, ARMREG_R2, ARMREG_V1);
267 load_trampoline = buf;
270 ARM_MOV_REG_REG (buf, ARMREG_LR, ARMREG_PC);
271 ARM_MOV_REG_REG (buf, ARMREG_PC, ARMREG_IP);
273 /* OK, code address is now on r0. Move it to the place on the stack
274 * where IP was saved (it is now no more useful to us and it can be
275 * clobbered). This way we can just restore all the regs in one inst
278 ARM_STR_IMM (buf, ARMREG_R0, ARMREG_V1, (ARMREG_R12 * 4));
281 * Now we restore the MonoLMF (see emit_epilogue in mini-arm.c)
282 * and the rest of the registers, so the method called will see
283 * the same state as before we executed.
284 * The pointer to MonoLMF is in r2.
286 ARM_MOV_REG_REG (buf, ARMREG_R2, ARMREG_SP);
287 /* ip = previous_lmf */
288 ARM_LDR_IMM (buf, ARMREG_IP, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
290 ARM_LDR_IMM (buf, ARMREG_LR, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
291 /* *(lmf_addr) = previous_lmf */
292 ARM_STR_IMM (buf, ARMREG_IP, ARMREG_LR, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
294 /* Non-standard function epilogue. Instead of doing a proper
295 * return, we just jump to the compiled code.
297 /* Restore the registers and jump to the code:
298 * Note that IP has been conveniently set to the method addr.
300 ARM_ADD_REG_IMM8 (buf, ARMREG_SP, ARMREG_SP, sizeof (MonoLMF) - sizeof (guint) * 14);
301 ARM_POP_NWB (buf, 0x5fff);
302 /* do we need to set sp? */
303 ARM_ADD_REG_IMM8 (buf, ARMREG_SP, ARMREG_SP, (14 * 4));
304 ARM_MOV_REG_REG (buf, ARMREG_PC, ARMREG_IP);
306 constants = (gpointer*)buf;
307 constants [0] = mono_get_lmf_addr;
308 if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT) {
309 constants [1] = arm_class_init_trampoline;
311 constants [1] = arm_magic_trampoline;
314 /* backpatch by emitting the missing instructions skipped above */
315 ARM_LDR_IMM (load_get_lmf_addr, ARMREG_R0, ARMREG_PC, (buf - load_get_lmf_addr - 8));
316 ARM_LDR_IMM (load_trampoline, ARMREG_IP, ARMREG_PC, (buf + 4 - load_trampoline - 8));
320 /* Flush instruction cache, since we've generated code */
321 mono_arch_flush_icache (code, buf - code);
324 g_assert ((buf - code) <= 512);
330 create_specific_tramp (MonoMethod *method, guint8* tramp, MonoDomain *domain) {
335 mono_domain_lock (domain);
336 code = buf = mono_code_manager_reserve (domain->code_mp, 24);
337 mono_domain_unlock (domain);
339 /* we could reduce this to 12 bytes if tramp is within reach:
343 * The called code can access method using the lr register
344 * A 20 byte sequence could be:
346 * ARM_MOV_REG_REG (lr, pc)
347 * ARM_LDR_IMM (pc, pc, 0)
351 /* We save all the registers, except PC and SP */
352 ARM_PUSH (buf, 0x5fff);
353 ARM_LDR_IMM (buf, ARMREG_R0, ARMREG_PC, 4); /* method is the only arg */
354 ARM_LDR_IMM (buf, ARMREG_R1, ARMREG_PC, 4); /* temp reg */
355 ARM_MOV_REG_REG (buf, ARMREG_PC, ARMREG_R1);
357 constants = (gpointer*)buf;
358 constants [0] = method;
359 constants [1] = tramp;
362 /* Flush instruction cache, since we've generated code */
363 mono_arch_flush_icache (code, buf - code);
365 g_assert ((buf - code) <= 24);
367 ji = g_new0 (MonoJitInfo, 1);
369 ji->code_start = code;
370 ji->code_size = buf - code;
372 mono_jit_stats.method_trampolines++;
378 mono_arch_create_jump_trampoline (MonoMethod *method)
381 MonoDomain* domain = mono_domain_get ();
383 tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_JUMP);
384 return create_specific_tramp (method, tramp, domain);
388 * arch_create_jit_trampoline:
389 * @method: pointer to the method info
391 * Creates a trampoline function for virtual methods. If the created
392 * code is called it first starts JIT compilation of method,
393 * and then calls the newly created method. It also replaces the
394 * corresponding vtable entry (see arm_magic_trampoline).
396 * A trampoline consists of two parts: a main fragment, shared by all method
397 * trampolines, and some code specific to each method, which hard-codes a
398 * reference to that method and then calls the main fragment.
400 * The main fragment contains a call to 'arm_magic_trampoline', which performs
401 * call to the JIT compiler and substitutes the method-specific fragment with
402 * some code that directly calls the JIT-compiled method.
404 * Returns: a pointer to the newly created code
407 mono_arch_create_jit_trampoline (MonoMethod *method)
411 MonoDomain* domain = mono_domain_get ();
414 tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_GENERIC);
415 /* FIXME: should pass the domain down to this function */
416 ji = create_specific_tramp (method, tramp, domain);
417 code_start = ji->code_start;
424 * mono_arch_create_class_init_trampoline:
425 * @vtable: the type to initialize
427 * Creates a trampoline function to run a type initializer.
428 * If the trampoline is called, it calls mono_runtime_class_init with the
429 * given vtable, then patches the caller code so it does not get called any
432 * Returns: a pointer to the newly created code
435 mono_arch_create_class_init_trampoline (MonoVTable *vtable)
437 guint8 *code, *buf, *tramp;
440 tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
442 mono_domain_lock (vtable->domain);
443 code = buf = mono_code_manager_reserve (vtable->domain->code_mp, METHOD_TRAMPOLINE_SIZE);
444 mono_domain_unlock (vtable->domain);
446 ARM_MOV_REG_REG (buf, ARMREG_IP, ARMREG_SP);
447 ARM_PUSH (buf, ((1 << ARMREG_IP) | (1 << ARMREG_LR)));
448 ARM_MOV_REG_REG (buf, ARMREG_R1, ARMREG_LR);
449 ARM_LDR_IMM (buf, ARMREG_R0, ARMREG_PC, 12); /* load vtable */
450 ARM_LDR_IMM (buf, ARMREG_R3, ARMREG_PC, 12); /* load the func address */
452 ARM_MOV_REG_REG (buf, ARMREG_LR, ARMREG_PC);
453 ARM_MOV_REG_REG (buf, ARMREG_PC, ARMREG_R3);
455 /* restore and return */
456 ARM_POP_NWB (buf, ((1 << ARMREG_SP) | (1 << ARMREG_PC)));
457 constants = (gpointer*)buf;
458 constants [0] = vtable;
459 constants [1] = arm_class_init_trampoline;
462 /* Flush instruction cache, since we've generated code */
463 mono_arch_flush_icache (code, buf - code);
466 g_assert ((buf - code) <= METHOD_TRAMPOLINE_SIZE);
468 mono_jit_stats.method_trampolines++;
474 * This method is only called when running in the Mono Debugger.
477 mono_debugger_create_notification_function (MonoCodeManager *codeman)
481 ptr = buf = mono_code_manager_reserve (codeman, 8);
482 //FIXME: ARM_SWI (buf, 0x9F0001);
483 ARM_MOV_REG_REG (buf, ARMREG_PC, ARMREG_LR);
484 mono_arch_flush_icache (ptr, buf - ptr);