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>
17 #include <mono/metadata/mono-debug-debugger.h>
23 * get_unbox_trampoline:
25 * @addr: pointer to native code for @m
27 * when value type methods are called through the vtable we need to unbox the
28 * this argument. This method returns a pointer to a trampoline which does
29 * unboxing before calling the method
32 get_unbox_trampoline (MonoMethod *m, gpointer addr)
36 MonoDomain *domain = mono_domain_get ();
38 if (!mono_method_signature (m)->ret->byref && MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret))
41 mono_domain_lock (domain);
42 start = code = mono_code_manager_reserve (domain->code_mp, 16);
43 mono_domain_unlock (domain);
45 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 4);
46 ARM_ADD_REG_IMM8 (code, this_pos, this_pos, sizeof (MonoObject));
47 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
48 *(guint32*)code = (guint32)addr;
50 mono_arch_flush_icache (start, code - start);
51 g_assert ((code - start) <= 16);
52 /*g_print ("unbox trampoline at %d for %s:%s\n", this_pos, m->klass->name, m->name);
53 g_print ("unbox code is at %p for method at %p\n", start, addr);*/
58 /* Stack size for trampoline function
60 #define STACK (sizeof (MonoLMF))
62 /* Method-specific trampoline code fragment size */
63 #define METHOD_TRAMPOLINE_SIZE 64
65 /* Jump-specific trampoline code fragment size */
66 #define JUMP_TRAMPOLINE_SIZE 64
69 * arm_magic_trampoline:
70 * @code: pointer into caller code
71 * @method: the method to translate
74 * This method is called by the function 'arch_create_jit_trampoline', which in
75 * turn is called by the trampoline functions for virtual methods.
76 * After having called the JIT compiler to compile the method, it inspects the
77 * caller code to find the address of the method-specific part of the
78 * trampoline vtable slot for this method, updates it with a fragment that calls
79 * the newly compiled code and returns this address of the compiled code to
80 * 'arch_create_jit_trampoline'
83 arm_magic_trampoline (MonoMethod *method, guint32 *code, gchar **sp)
87 MonoJitInfo *ji, *target_ji;
90 addr = mono_compile_method (method);
91 /*g_print ("method code at %p for %s:%s\n", addr, method->klass->name, method->name);*/
98 /* We can't trampoline across domains */
99 ji = mono_jit_info_table_find (mono_domain_get (), code);
100 target_ji = mono_jit_info_table_find (mono_domain_get (), addr);
101 if (!mono_method_same_domain (ji, target_ji))
104 /* Locate the address of the method-specific trampoline. The call using
105 the vtable slot that took the processing flow to 'arch_create_jit_trampoline'
106 looks something like this:
115 The call sequence could be also:
118 function pointer literal
122 Note that on ARM5+ we can use one instruction instead of the last two.
123 Therefore, we need to locate the 'ldr rA' instruction to know which
124 register was used to hold the method addrs.
127 /* This is the 'bl' or the 'mov pc' instruction */
131 * Note that methods are called also with the bl opcode.
133 if ((((*code) >> 25) & 7) == 5) {
134 /*g_print ("direct patching\n");*/
135 arm_patch ((char*)code, addr);
136 mono_arch_flush_icache ((char*)code, 4);
140 /* ldr pc, rX, #offset */
141 #define LDR_MASK ((0xf << ARMCOND_SHIFT) | (3 << 26) | (1 << 22) | (1 << 20) | (15 << 12))
142 #define LDR_PC_VAL ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 26) | (0 << 22) | (1 << 20) | (15 << 12))
143 if ((*code & LDR_MASK) == LDR_PC_VAL) {
144 reg = (*code >> 16 ) & 0xf;
145 offset = *code & 0xfff;
146 /*g_print ("found vcall at r%d + %d\n", reg, offset);*/
150 /* this is not done for non-virtual calls, because in that case
151 we won't have an object, but the actual pointer to the
152 valuetype as the this argument
154 if (method->klass->valuetype && !mono_aot_is_got_entry (code, o))
155 addr = get_unbox_trampoline (method, addr);
159 if (mono_aot_is_got_entry (code, o) || mono_domain_owns_vtable_slot (mono_domain_get (), o))
160 *((gpointer *)o) = addr;
162 /*g_print ("no callsite patching\n");
163 mono_disassemble_code (code -3, 16, "callsite");*/
170 arm_class_init_trampoline (void *vtable, guint32 *code, char *sp)
172 mono_runtime_class_init (vtable);
175 /* This is the 'bl' instruction */
178 if ((((*code) >> 25) & 7) == 5) {
179 ARM_NOP (code); /* nop */
180 mono_arch_flush_icache (code, 4);
183 g_assert_not_reached ();
189 * Stack frame description when the generic trampoline is called.
191 * ------------------- old sp
193 * ------------------- sp
196 mono_arch_create_trampoline_code (MonoTrampolineType tramp_type)
198 guint8 *buf, *code = NULL;
200 guint8 *load_get_lmf_addr, *load_trampoline;
203 /* Now we'll create in 'buf' the ARM trampoline code. This
204 is the trampoline code common to all methods */
206 code = buf = g_malloc (148);
209 * At this point r0 has the method and sp points to the saved
210 * regs on the stack (all but PC and SP).
212 ARM_MOV_REG_REG (buf, ARMREG_V1, ARMREG_SP);
213 ARM_MOV_REG_REG (buf, ARMREG_V2, ARMREG_R0);
214 ARM_MOV_REG_REG (buf, ARMREG_V3, ARMREG_LR);
216 /* ok, now we can continue with the MonoLMF setup, mostly untouched
217 * from emit_prolog in mini-arm.c
218 * This is a sinthetized call to mono_get_lmf_addr ()
220 load_get_lmf_addr = buf;
222 ARM_MOV_REG_REG (buf, ARMREG_LR, ARMREG_PC);
223 ARM_MOV_REG_REG (buf, ARMREG_PC, ARMREG_R0);
225 /* we build the MonoLMF structure on the stack - see mini-arm.h
226 * The pointer to the struct is put in r1.
227 * the iregs array is already allocated on the stack by push.
229 ARM_SUB_REG_IMM8 (buf, ARMREG_SP, ARMREG_SP, sizeof (MonoLMF) - sizeof (guint) * 14);
230 ARM_ADD_REG_IMM8 (buf, ARMREG_R1, ARMREG_SP, STACK - sizeof (MonoLMF));
231 /* r0 is the result from mono_get_lmf_addr () */
232 ARM_STR_IMM (buf, ARMREG_R0, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
233 /* new_lmf->previous_lmf = *lmf_addr */
234 ARM_LDR_IMM (buf, ARMREG_R2, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
235 ARM_STR_IMM (buf, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
236 /* *(lmf_addr) = r1 */
237 ARM_STR_IMM (buf, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
238 /* save method info (it's in v2) */
239 ARM_STR_IMM (buf, ARMREG_V2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, method));
240 ARM_STR_IMM (buf, ARMREG_SP, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, ebp));
241 /* save the IP (caller ip) */
242 if (tramp_type == MONO_TRAMPOLINE_JUMP) {
243 ARM_MOV_REG_IMM8 (buf, ARMREG_R2, 0);
245 /* assumes STACK == sizeof (MonoLMF) */
246 ARM_LDR_IMM (buf, ARMREG_R2, ARMREG_SP, (G_STRUCT_OFFSET (MonoLMF, iregs) + 13*4));
248 ARM_STR_IMM (buf, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, eip));
251 * Now we're ready to call arm_magic_trampoline ().
253 /* Arg 1: MonoMethod *method. It was put in v2 */
254 ARM_MOV_REG_REG (buf, ARMREG_R0, ARMREG_V2);
256 /* Arg 2: code (next address to the instruction that called us) */
257 if (tramp_type == MONO_TRAMPOLINE_JUMP) {
258 ARM_MOV_REG_IMM8 (buf, ARMREG_R1, 0);
260 ARM_MOV_REG_REG (buf, ARMREG_R1, ARMREG_V3);
263 /* Arg 3: stack pointer so that the magic trampoline can access the
264 * registers we saved above
266 ARM_MOV_REG_REG (buf, ARMREG_R2, ARMREG_V1);
268 load_trampoline = buf;
271 ARM_MOV_REG_REG (buf, ARMREG_LR, ARMREG_PC);
272 ARM_MOV_REG_REG (buf, ARMREG_PC, ARMREG_IP);
274 /* OK, code address is now on r0. Move it to the place on the stack
275 * where IP was saved (it is now no more useful to us and it can be
276 * clobbered). This way we can just restore all the regs in one inst
279 ARM_STR_IMM (buf, ARMREG_R0, ARMREG_V1, (ARMREG_R12 * 4));
282 * Now we restore the MonoLMF (see emit_epilogue in mini-arm.c)
283 * and the rest of the registers, so the method called will see
284 * the same state as before we executed.
285 * The pointer to MonoLMF is in r2.
287 ARM_MOV_REG_REG (buf, ARMREG_R2, ARMREG_SP);
288 /* ip = previous_lmf */
289 ARM_LDR_IMM (buf, ARMREG_IP, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
291 ARM_LDR_IMM (buf, ARMREG_LR, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
292 /* *(lmf_addr) = previous_lmf */
293 ARM_STR_IMM (buf, ARMREG_IP, ARMREG_LR, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
295 /* Non-standard function epilogue. Instead of doing a proper
296 * return, we just jump to the compiled code.
298 /* Restore the registers and jump to the code:
299 * Note that IP has been conveniently set to the method addr.
301 ARM_ADD_REG_IMM8 (buf, ARMREG_SP, ARMREG_SP, sizeof (MonoLMF) - sizeof (guint) * 14);
302 ARM_POP_NWB (buf, 0x5fff);
303 /* do we need to set sp? */
304 ARM_ADD_REG_IMM8 (buf, ARMREG_SP, ARMREG_SP, (14 * 4));
305 ARM_MOV_REG_REG (buf, ARMREG_PC, ARMREG_IP);
307 constants = (gpointer*)buf;
308 constants [0] = mono_get_lmf_addr;
309 if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT) {
310 constants [1] = arm_class_init_trampoline;
312 constants [1] = arm_magic_trampoline;
315 /* backpatch by emitting the missing instructions skipped above */
316 ARM_LDR_IMM (load_get_lmf_addr, ARMREG_R0, ARMREG_PC, (buf - load_get_lmf_addr - 8));
317 ARM_LDR_IMM (load_trampoline, ARMREG_IP, ARMREG_PC, (buf + 4 - load_trampoline - 8));
321 /* Flush instruction cache, since we've generated code */
322 mono_arch_flush_icache (code, buf - code);
325 g_assert ((buf - code) <= 512);
331 create_specific_tramp (MonoMethod *method, guint8* tramp, MonoDomain *domain) {
336 mono_domain_lock (domain);
337 code = buf = mono_code_manager_reserve (domain->code_mp, 24);
338 mono_domain_unlock (domain);
340 /* we could reduce this to 12 bytes if tramp is within reach:
344 * The called code can access method using the lr register
345 * A 20 byte sequence could be:
347 * ARM_MOV_REG_REG (lr, pc)
348 * ARM_LDR_IMM (pc, pc, 0)
352 /* We save all the registers, except PC and SP */
353 ARM_PUSH (buf, 0x5fff);
354 ARM_LDR_IMM (buf, ARMREG_R0, ARMREG_PC, 4); /* method is the only arg */
355 ARM_LDR_IMM (buf, ARMREG_R1, ARMREG_PC, 4); /* temp reg */
356 ARM_MOV_REG_REG (buf, ARMREG_PC, ARMREG_R1);
358 constants = (gpointer*)buf;
359 constants [0] = method;
360 constants [1] = tramp;
363 /* Flush instruction cache, since we've generated code */
364 mono_arch_flush_icache (code, buf - code);
366 g_assert ((buf - code) <= 24);
368 ji = g_new0 (MonoJitInfo, 1);
370 ji->code_start = code;
371 ji->code_size = buf - code;
373 mono_jit_stats.method_trampolines++;
379 mono_arch_create_jump_trampoline (MonoMethod *method)
382 MonoDomain* domain = mono_domain_get ();
384 tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_JUMP);
385 return create_specific_tramp (method, tramp, domain);
389 * arch_create_jit_trampoline:
390 * @method: pointer to the method info
392 * Creates a trampoline function for virtual methods. If the created
393 * code is called it first starts JIT compilation of method,
394 * and then calls the newly created method. It also replaces the
395 * corresponding vtable entry (see arm_magic_trampoline).
397 * A trampoline consists of two parts: a main fragment, shared by all method
398 * trampolines, and some code specific to each method, which hard-codes a
399 * reference to that method and then calls the main fragment.
401 * The main fragment contains a call to 'arm_magic_trampoline', which performs
402 * call to the JIT compiler and substitutes the method-specific fragment with
403 * some code that directly calls the JIT-compiled method.
405 * Returns: a pointer to the newly created code
408 mono_arch_create_jit_trampoline (MonoMethod *method)
412 MonoDomain* domain = mono_domain_get ();
415 tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_GENERIC);
416 /* FIXME: should pass the domain down to this function */
417 ji = create_specific_tramp (method, tramp, domain);
418 code_start = ji->code_start;
425 * mono_arch_create_class_init_trampoline:
426 * @vtable: the type to initialize
428 * Creates a trampoline function to run a type initializer.
429 * If the trampoline is called, it calls mono_runtime_class_init with the
430 * given vtable, then patches the caller code so it does not get called any
433 * Returns: a pointer to the newly created code
436 mono_arch_create_class_init_trampoline (MonoVTable *vtable)
438 guint8 *code, *buf, *tramp;
441 tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
443 mono_domain_lock (vtable->domain);
444 code = buf = mono_code_manager_reserve (vtable->domain->code_mp, METHOD_TRAMPOLINE_SIZE);
445 mono_domain_unlock (vtable->domain);
447 ARM_MOV_REG_REG (buf, ARMREG_IP, ARMREG_SP);
448 ARM_PUSH (buf, ((1 << ARMREG_IP) | (1 << ARMREG_LR)));
449 ARM_MOV_REG_REG (buf, ARMREG_R1, ARMREG_LR);
450 ARM_LDR_IMM (buf, ARMREG_R0, ARMREG_PC, 12); /* load vtable */
451 ARM_LDR_IMM (buf, ARMREG_R3, ARMREG_PC, 12); /* load the func address */
453 ARM_MOV_REG_REG (buf, ARMREG_LR, ARMREG_PC);
454 ARM_MOV_REG_REG (buf, ARMREG_PC, ARMREG_R3);
456 /* restore and return */
457 ARM_POP_NWB (buf, ((1 << ARMREG_SP) | (1 << ARMREG_PC)));
458 constants = (gpointer*)buf;
459 constants [0] = vtable;
460 constants [1] = arm_class_init_trampoline;
463 /* Flush instruction cache, since we've generated code */
464 mono_arch_flush_icache (code, buf - code);
467 g_assert ((buf - code) <= METHOD_TRAMPOLINE_SIZE);
469 mono_jit_stats.method_trampolines++;
475 * This method is only called when running in the Mono Debugger.
478 mono_debugger_create_notification_function (gpointer *notification_address)
482 ptr = buf = g_malloc0 (8);
483 //FIXME: ARM_SWI (buf, 0x9F0001);
484 if (notification_address)
485 *notification_address = buf;
486 ARM_MOV_REG_REG (buf, ARMREG_PC, ARMREG_LR);
487 mono_arch_flush_icache (ptr, buf - ptr);