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