2007-03-23 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / mini / tramp-arm.c
1 /*
2  * tramp-arm.c: JIT trampoline code for ARM
3  *
4  * Authors:
5  *   Paolo Molaro (lupus@ximian.com)
6  *
7  * (C) 2001 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12
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
18 #include "mini.h"
19 #include "mini-arm.h"
20
21 /*
22  * get_unbox_trampoline:
23  * @m: method pointer
24  * @addr: pointer to native code for @m
25  *
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
29  */
30 static gpointer
31 get_unbox_trampoline (MonoMethod *m, gpointer addr)
32 {
33         guint8 *code, *start;
34         int this_pos = 0;
35         MonoDomain *domain = mono_domain_get ();
36
37         if (!mono_method_signature (m)->ret->byref && MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret))
38                 this_pos = 1;
39
40         mono_domain_lock (domain);
41         start = code = mono_code_manager_reserve (domain->code_mp, 16);
42         mono_domain_unlock (domain);
43
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;
48         code += 4;
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);*/
53
54         return start;
55 }
56
57 /* Stack size for trampoline function 
58  */
59 #define STACK (sizeof (MonoLMF))
60
61 /* Method-specific trampoline code fragment size */
62 #define METHOD_TRAMPOLINE_SIZE 64
63
64 /* Jump-specific trampoline code fragment size */
65 #define JUMP_TRAMPOLINE_SIZE   64
66
67 /**
68  * arm_magic_trampoline:
69  * @code: pointer into caller code
70  * @method: the method to translate
71  * @sp: stack pointer
72  *
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' 
80  */
81 static gpointer
82 arm_magic_trampoline (MonoMethod *method, guint32 *code, gchar **sp)
83 {
84         char *o = NULL;
85         gpointer addr;
86         MonoJitInfo *ji, *target_ji;
87         int reg, offset = 0;
88
89         addr = mono_compile_method (method);
90         /*g_print ("method code at %p for %s:%s\n", addr, method->klass->name, method->name);*/
91         g_assert(addr);
92
93         if (!code) {
94                 return addr;
95         }
96
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))
101                 return addr;
102
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:
106
107                 ldr rA, rX, #offset
108                 mov lr, pc
109                 mov pc, rA
110         or better:
111                 mov lr, pc
112                 ldr pc, rX, #offset
113
114         The call sequence could be also:
115                 ldr ip, pc, 0
116                 b skip
117                 function pointer literal
118                 skip:
119                 mov lr, pc
120                 mov pc, ip
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.
124         */
125         
126         /* This is the 'bl' or the 'mov pc' instruction */
127         --code;
128         
129         /*
130          * Note that methods are called also with the bl opcode.
131          */
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);
136                 return addr;
137         }
138
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);*/
146                 o = sp [reg];
147         }
148
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
152          */
153         if (method->klass->valuetype && !mono_aot_is_got_entry (code, o))
154                 addr = get_unbox_trampoline (method, addr);
155
156         if (o) {
157                 o += offset;
158                 if (mono_aot_is_got_entry (code, o) || mono_domain_owns_vtable_slot (mono_domain_get (), o))
159                         *((gpointer *)o) = addr;
160         } else {
161                 /*g_print ("no callsite patching\n");
162                 mono_disassemble_code (code -3, 16, "callsite");*/
163         }
164
165         return addr;
166 }
167
168 static void
169 arm_class_init_trampoline (void *vtable, guint32 *code, char *sp)
170 {
171         mono_runtime_class_init (vtable);
172
173 #if 0
174         /* This is the 'bl' instruction */
175         --code;
176         
177         if ((((*code) >> 25)  & 7) == 5) {
178                 ARM_NOP (code); /* nop */
179                 mono_arch_flush_icache (code, 4);
180                 return;
181         } else {
182                 g_assert_not_reached ();
183         }
184 #endif
185 }
186
187 /*
188  * Stack frame description when the generic trampoline is called.
189  * caller frame
190  * ------------------- old sp
191  *  MonoLMF
192  * ------------------- sp
193  */
194 guchar*
195 mono_arch_create_trampoline_code (MonoTrampolineType tramp_type)
196 {
197         guint8 *buf, *code = NULL;
198         int i, offset;
199         guint8 *load_get_lmf_addr, *load_trampoline;
200         gpointer *constants;
201
202         /* Now we'll create in 'buf' the ARM trampoline code. This
203          is the trampoline code common to all methods  */
204         
205         code = buf = g_malloc (148);
206
207         /*
208          * At this point r0 has the method and sp points to the saved
209          * regs on the stack (all but PC and SP).
210          */
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);
214
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 ()
218          */
219         load_get_lmf_addr = buf;
220         buf += 4;
221         ARM_MOV_REG_REG (buf, ARMREG_LR, ARMREG_PC);
222         ARM_MOV_REG_REG (buf, ARMREG_PC, ARMREG_R0);
223
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.
227          */
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);
243         } else {
244                 /* assumes STACK == sizeof (MonoLMF) */
245                 ARM_LDR_IMM (buf, ARMREG_R2, ARMREG_SP, (G_STRUCT_OFFSET (MonoLMF, iregs) + 13*4));
246         }
247         ARM_STR_IMM (buf, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, eip));
248
249         /*
250          * Now we're ready to call arm_magic_trampoline ().
251          */
252         /* Arg 1: MonoMethod *method. It was put in v2 */
253         ARM_MOV_REG_REG (buf, ARMREG_R0, ARMREG_V2);
254
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);
258         } else {
259                 ARM_MOV_REG_REG (buf, ARMREG_R1, ARMREG_V3);
260         }
261         
262         /* Arg 3: stack pointer so that the magic trampoline can access the
263          * registers we saved above
264          */
265         ARM_MOV_REG_REG (buf, ARMREG_R2, ARMREG_V1);
266
267         load_trampoline = buf;
268         buf += 4;
269
270         ARM_MOV_REG_REG (buf, ARMREG_LR, ARMREG_PC);
271         ARM_MOV_REG_REG (buf, ARMREG_PC, ARMREG_IP);
272         
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
276          * and branch to IP.
277          */
278         ARM_STR_IMM (buf, ARMREG_R0, ARMREG_V1, (ARMREG_R12 * 4));
279
280         /*
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.
285          */
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));
289         /* lr = lmf_addr */
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));
293
294         /* Non-standard function epilogue. Instead of doing a proper
295          * return, we just jump to the compiled code.
296          */
297         /* Restore the registers and jump to the code:
298          * Note that IP has been conveniently set to the method addr.
299          */
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);
305
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;
310         } else {
311                 constants [1] = arm_magic_trampoline;
312         }
313
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));
317
318         buf += 8;
319
320         /* Flush instruction cache, since we've generated code */
321         mono_arch_flush_icache (code, buf - code);
322
323         /* Sanity check */
324         g_assert ((buf - code) <= 512);
325
326         return code;
327 }
328
329 static MonoJitInfo*
330 create_specific_tramp (MonoMethod *method, guint8* tramp, MonoDomain *domain) {
331         guint8 *code, *buf;
332         MonoJitInfo *ji;
333         gpointer *constants;
334
335         mono_domain_lock (domain);
336         code = buf = mono_code_manager_reserve (domain->code_mp, 24);
337         mono_domain_unlock (domain);
338
339         /* we could reduce this to 12 bytes if tramp is within reach:
340          * ARM_PUSH ()
341          * ARM_BL ()
342          * method-literal
343          * The called code can access method using the lr register
344          * A 20 byte sequence could be:
345          * ARM_PUSH ()
346          * ARM_MOV_REG_REG (lr, pc)
347          * ARM_LDR_IMM (pc, pc, 0)
348          * method-literal
349          * tramp-literal
350          */
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);
356
357         constants = (gpointer*)buf;
358         constants [0] = method;
359         constants [1] = tramp;
360         buf += 8;
361
362         /* Flush instruction cache, since we've generated code */
363         mono_arch_flush_icache (code, buf - code);
364
365         g_assert ((buf - code) <= 24);
366
367         ji = g_new0 (MonoJitInfo, 1);
368         ji->method = method;
369         ji->code_start = code;
370         ji->code_size = buf - code;
371
372         mono_jit_stats.method_trampolines++;
373
374         return ji;
375 }
376
377 MonoJitInfo*
378 mono_arch_create_jump_trampoline (MonoMethod *method)
379 {
380         guint8 *tramp;
381         MonoDomain* domain = mono_domain_get ();
382         
383         tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_JUMP);
384         return create_specific_tramp (method, tramp, domain);
385 }
386
387 /**
388  * arch_create_jit_trampoline:
389  * @method: pointer to the method info
390  *
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).
395  *
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.
399  *
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.
403  * 
404  * Returns: a pointer to the newly created code 
405  */
406 gpointer
407 mono_arch_create_jit_trampoline (MonoMethod *method)
408 {
409         guint8 *tramp;
410         MonoJitInfo *ji;
411         MonoDomain* domain = mono_domain_get ();
412         gpointer code_start;
413
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;
418         g_free (ji);
419
420         return code_start;
421 }
422
423 /**
424  * mono_arch_create_class_init_trampoline:
425  *  @vtable: the type to initialize
426  *
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
430  * more.
431  * 
432  * Returns: a pointer to the newly created code 
433  */
434 gpointer
435 mono_arch_create_class_init_trampoline (MonoVTable *vtable)
436 {
437         guint8 *code, *buf, *tramp;
438         gpointer *constants;
439
440         tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
441
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);
445
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 */
451         /* make the call */
452         ARM_MOV_REG_REG (buf, ARMREG_LR, ARMREG_PC);
453         ARM_MOV_REG_REG (buf, ARMREG_PC, ARMREG_R3);
454
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;
460         buf += 8;
461
462         /* Flush instruction cache, since we've generated code */
463         mono_arch_flush_icache (code, buf - code);
464                 
465         /* Sanity check */
466         g_assert ((buf - code) <= METHOD_TRAMPOLINE_SIZE);
467
468         mono_jit_stats.method_trampolines++;
469
470         return code;
471 }
472
473 /*
474  * This method is only called when running in the Mono Debugger.
475  */
476 gpointer
477 mono_debugger_create_notification_function (void)
478 {
479         guint8 *ptr, *buf;
480
481         ptr = buf = mono_global_codeman_reserve (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);
485
486         return ptr;
487 }
488