1aeeeffa6bd5cb8f8a1c6a0977adbdb5c23c1100
[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-2003 Ximian, Inc.
8  * Copyright 2003-2011 Novell Inc
9  * Copyright 2011 Xamarin Inc
10  */
11
12 #include <config.h>
13 #include <glib.h>
14
15 #include <mono/metadata/appdomain.h>
16 #include <mono/metadata/marshal.h>
17 #include <mono/metadata/tabledefs.h>
18 #include <mono/arch/arm/arm-codegen.h>
19
20 #include "mini.h"
21 #include "mini-arm.h"
22
23 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
24
25 static guint8* nullified_class_init_trampoline;
26
27 void
28 mono_arch_patch_callsite (guint8 *method_start, guint8 *code_ptr, guint8 *addr)
29 {
30         guint32 *code = (guint32*)code_ptr;
31
32         /* This is the 'bl' or the 'mov pc' instruction */
33         --code;
34         
35         /*
36          * Note that methods are called also with the bl opcode.
37          */
38         if ((((*code) >> 25)  & 7) == 5) {
39                 /*g_print ("direct patching\n");*/
40                 arm_patch ((guint8*)code, addr);
41                 mono_arch_flush_icache ((guint8*)code, 4);
42                 return;
43         }
44
45         if ((((*code) >> 20) & 0xFF) == 0x12) {
46                 /*g_print ("patching bx\n");*/
47                 arm_patch ((guint8*)code, addr);
48                 mono_arch_flush_icache ((guint8*)(code - 2), 4);
49                 return;
50         }
51
52         g_assert_not_reached ();
53 }
54
55 void
56 mono_arch_patch_plt_entry (guint8 *code, gpointer *got, mgreg_t *regs, guint8 *addr)
57 {
58         guint8 *jump_entry;
59
60         /* Patch the jump table entry used by the plt entry */
61         if (*(guint32*)code == 0xe59fc000) {
62                 /* ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0); */
63                 guint32 offset = ((guint32*)code)[2];
64                 
65                 jump_entry = code + offset + 12;
66         } else if (*(guint16*)(code - 4) == 0xf8df) {
67                 /* 
68                  * Thumb PLT entry, begins with ldr.w ip, [pc, #8], code points to entry + 4, see
69                  * mono_arm_get_thumb_plt_entry ().
70                  */
71                 guint32 offset;
72
73                 code -= 4;
74                 offset = *(guint32*)(code + 12);
75                 jump_entry = code + offset + 8;
76         } else {
77                 g_assert_not_reached ();
78         }
79
80         *(guint8**)jump_entry = addr;
81 }
82
83 void
84 mono_arch_nullify_class_init_trampoline (guint8 *code, mgreg_t *regs)
85 {
86         mono_arch_patch_callsite (NULL, code, nullified_class_init_trampoline);
87 }
88
89 void
90 mono_arch_nullify_plt_entry (guint8 *code, mgreg_t *regs)
91 {
92         if (mono_aot_only && !nullified_class_init_trampoline)
93                 nullified_class_init_trampoline = mono_aot_get_trampoline ("nullified_class_init_trampoline");
94
95         mono_arch_patch_plt_entry (code, NULL, regs, nullified_class_init_trampoline);
96 }
97
98 #ifndef DISABLE_JIT
99
100 #define arm_is_imm12(v) ((int)(v) > -4096 && (int)(v) < 4096)
101
102 /*
103  * Return the instruction to jump from code to target, 0 if not
104  * reachable with a single instruction
105  */
106 static guint32
107 branch_for_target_reachable (guint8 *branch, guint8 *target)
108 {
109         gint diff = target - branch - 8;
110         g_assert ((diff & 3) == 0);
111         if (diff >= 0) {
112                 if (diff <= 33554431)
113                         return (ARMCOND_AL << ARMCOND_SHIFT) | (ARM_BR_TAG) | (diff >> 2);
114         } else {
115                 /* diff between 0 and -33554432 */
116                 if (diff >= -33554432)
117                         return (ARMCOND_AL << ARMCOND_SHIFT) | (ARM_BR_TAG) | ((diff >> 2) & ~0xff000000);
118         }
119         return 0;
120 }
121
122 static inline guint8*
123 emit_bx (guint8* code, int reg)
124 {
125         if (mono_arm_thumb_supported ())
126                 ARM_BX (code, reg);
127         else
128                 ARM_MOV_REG_REG (code, ARMREG_PC, reg);
129         return code;
130 }
131
132 /* Stack size for trampoline function 
133  */
134 #define STACK ALIGN_TO (sizeof (MonoLMF), 8)
135
136 /* Method-specific trampoline code fragment size */
137 #define METHOD_TRAMPOLINE_SIZE 64
138
139 /* Jump-specific trampoline code fragment size */
140 #define JUMP_TRAMPOLINE_SIZE   64
141
142 guchar*
143 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
144 {
145         guint8 *buf, *code = NULL;
146         guint8 *load_get_lmf_addr, *load_trampoline;
147         gpointer *constants;
148         int cfa_offset, lmf_offset, regsave_size, lr_offset;
149         GSList *unwind_ops = NULL;
150         MonoJumpInfo *ji = NULL;
151         int buf_len;
152
153         /* Now we'll create in 'buf' the ARM trampoline code. This
154          is the trampoline code common to all methods  */
155
156         buf_len = 212;
157         code = buf = mono_global_codeman_reserve (buf_len);
158
159         /*
160          * At this point lr points to the specific arg and sp points to the saved
161          * regs on the stack (all but PC and SP). The original LR value has been
162          * saved as sp + LR_OFFSET by the push in the specific trampoline
163          */
164
165         /* The offset of lmf inside the stack frame */
166         lmf_offset = STACK - sizeof (MonoLMF);
167         /* The size of the area already allocated by the push in the specific trampoline */
168         regsave_size = 14 * sizeof (mgreg_t);
169         /* The offset where lr was saved inside the regsave area */
170         lr_offset = 13 * sizeof (mgreg_t);
171
172         // FIXME: Finish the unwind info, the current info allows us to unwind
173         // when the trampoline is not in the epilog
174
175         // CFA = SP + (num registers pushed) * 4
176         cfa_offset = 14 * sizeof (mgreg_t);
177         mono_add_unwind_op_def_cfa (unwind_ops, code, buf, ARMREG_SP, cfa_offset);
178         // PC saved at sp+LR_OFFSET
179         mono_add_unwind_op_offset (unwind_ops, code, buf, ARMREG_LR, -4);
180
181         if (aot && tramp_type != MONO_TRAMPOLINE_GENERIC_CLASS_INIT) {
182                 /* 
183                  * The trampoline contains a pc-relative offset to the got slot 
184                  * preceeding the got slot where the value is stored. The offset can be
185                  * found at [lr + 0].
186                  */
187                 ARM_LDR_IMM (code, ARMREG_V2, ARMREG_LR, 0);
188                 ARM_ADD_REG_IMM (code, ARMREG_V2, ARMREG_V2, 4, 0);
189                 ARM_LDR_REG_REG (code, ARMREG_V2, ARMREG_V2, ARMREG_LR);
190         } else {
191                 if (tramp_type != MONO_TRAMPOLINE_GENERIC_CLASS_INIT)
192                         ARM_LDR_IMM (code, ARMREG_V2, ARMREG_LR, 0);
193                 else
194                         ARM_MOV_REG_REG (code, ARMREG_V2, MONO_ARCH_VTABLE_REG);
195         }
196         ARM_LDR_IMM (code, ARMREG_V3, ARMREG_SP, lr_offset);
197
198         /* ok, now we can continue with the MonoLMF setup, mostly untouched 
199          * from emit_prolog in mini-arm.c
200          * This is a synthetized call to mono_get_lmf_addr ()
201          */
202         if (aot) {
203                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_get_lmf_addr");
204                 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
205                 ARM_B (code, 0);
206                 *(gpointer*)code = NULL;
207                 code += 4;
208                 ARM_LDR_REG_REG (code, ARMREG_R0, ARMREG_PC, ARMREG_R0);
209         } else {
210                 load_get_lmf_addr = code;
211                 code += 4;
212         }
213         ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
214         code = emit_bx (code, ARMREG_R0);
215
216         /* we build the MonoLMF structure on the stack - see mini-arm.h
217          * The pointer to the struct is put in r1.
218          * the iregs array is already allocated on the stack by push.
219          */
220         ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, STACK - regsave_size);
221         cfa_offset += STACK - regsave_size;
222         mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
223         /* V1 == lmf */
224         ARM_ADD_REG_IMM8 (code, ARMREG_V1, ARMREG_SP, STACK - sizeof (MonoLMF));
225
226         /*
227          * The stack now looks like:
228          *       <saved regs>
229          * v1 -> <rest of LMF>
230          * sp -> <alignment>
231          */
232
233         /* r0 is the result from mono_get_lmf_addr () */
234         ARM_STR_IMM (code, ARMREG_R0, ARMREG_V1, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
235         /* new_lmf->previous_lmf = *lmf_addr */
236         ARM_LDR_IMM (code, ARMREG_R2, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
237         ARM_STR_IMM (code, ARMREG_R2, ARMREG_V1, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
238         /* *(lmf_addr) = r1 */
239         ARM_STR_IMM (code, ARMREG_V1, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
240         /* save method info (it's in v2) */
241         if ((tramp_type == MONO_TRAMPOLINE_JIT) || (tramp_type == MONO_TRAMPOLINE_JUMP))
242                 ARM_STR_IMM (code, ARMREG_V2, ARMREG_V1, G_STRUCT_OFFSET (MonoLMF, method));
243         else {
244                 ARM_MOV_REG_IMM8 (code, ARMREG_R2, 0);
245                 ARM_STR_IMM (code, ARMREG_R2, ARMREG_V1, G_STRUCT_OFFSET (MonoLMF, method));
246         }
247         /* save caller SP */
248         ARM_ADD_REG_IMM8 (code, ARMREG_R2, ARMREG_SP, cfa_offset);
249         ARM_STR_IMM (code, ARMREG_R2, ARMREG_V1, G_STRUCT_OFFSET (MonoLMF, sp));
250         /* save caller FP */
251         ARM_LDR_IMM (code, ARMREG_R2, ARMREG_V1, (G_STRUCT_OFFSET (MonoLMF, iregs) + ARMREG_FP*4));
252         ARM_STR_IMM (code, ARMREG_R2, ARMREG_V1, G_STRUCT_OFFSET (MonoLMF, fp));
253         /* save the IP (caller ip) */
254         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
255                 ARM_MOV_REG_IMM8 (code, ARMREG_R2, 0);
256         } else {
257                 ARM_LDR_IMM (code, ARMREG_R2, ARMREG_V1, (G_STRUCT_OFFSET (MonoLMF, iregs) + 13*4));
258         }
259         ARM_STR_IMM (code, ARMREG_R2, ARMREG_V1, G_STRUCT_OFFSET (MonoLMF, ip));
260
261         /*
262          * Now we're ready to call xxx_trampoline ().
263          */
264         /* Arg 1: the saved registers */
265         ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_V1, G_STRUCT_OFFSET (MonoLMF, iregs));
266
267         /* Arg 2: code (next address to the instruction that called us) */
268         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
269                 ARM_MOV_REG_IMM8 (code, ARMREG_R1, 0);
270         } else {
271                 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_V3);
272         }
273         
274         /* Arg 3: the specific argument, stored in v2
275          */
276         ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_V2);
277
278         if (aot) {
279                 char *icall_name = g_strdup_printf ("trampoline_func_%d", tramp_type);
280                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name);
281                 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
282                 ARM_B (code, 0);
283                 *(gpointer*)code = NULL;
284                 code += 4;
285                 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
286         } else {
287                 load_trampoline = code;
288                 code += 4;
289         }
290
291         ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
292         code = emit_bx (code, ARMREG_IP);
293
294         /* OK, code address is now on r0. Move it to the place on the stack
295          * where IP was saved (it is now no more useful to us and it can be
296          * clobbered). This way we can just restore all the regs in one inst
297          * and branch to IP.
298          */
299         ARM_STR_IMM (code, ARMREG_R0, ARMREG_V1, G_STRUCT_OFFSET (MonoLMF, iregs) + (ARMREG_R12 * sizeof (mgreg_t)));
300
301         /* Check for thread interruption */
302         /* This is not perf critical code so no need to check the interrupt flag */
303         /* 
304          * Have to call the _force_ variant, since there could be a protected wrapper on the top of the stack.
305          */
306         if (aot) {
307                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_thread_force_interruption_checkpoint");
308                 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
309                 ARM_B (code, 0);
310                 *(gpointer*)code = NULL;
311                 code += 4;
312                 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
313         } else {
314                 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
315                 ARM_B (code, 0);
316                 *(gpointer*)code = mono_thread_force_interruption_checkpoint;
317                 code += 4;
318         }
319         ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
320         code = emit_bx (code, ARMREG_IP);
321
322         /*
323          * Now we restore the MonoLMF (see emit_epilogue in mini-arm.c)
324          * and the rest of the registers, so the method called will see
325          * the same state as before we executed.
326          */
327         /* ip = previous_lmf */
328         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_V1, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
329         /* lr = lmf_addr */
330         ARM_LDR_IMM (code, ARMREG_LR, ARMREG_V1, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
331         /* *(lmf_addr) = previous_lmf */
332         ARM_STR_IMM (code, ARMREG_IP, ARMREG_LR, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
333
334         /* Non-standard function epilogue. Instead of doing a proper
335          * return, we just jump to the compiled code.
336          */
337         /* Restore the registers and jump to the code:
338          * Note that IP has been conveniently set to the method addr.
339          */
340         ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, STACK - regsave_size);
341         ARM_POP_NWB (code, 0x5fff);
342         if (tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH)
343                 ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_IP);
344         ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, regsave_size);
345         if ((tramp_type == MONO_TRAMPOLINE_CLASS_INIT) || (tramp_type == MONO_TRAMPOLINE_GENERIC_CLASS_INIT) || (tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH))
346                 code = emit_bx (code, ARMREG_LR);
347         else
348                 code = emit_bx (code, ARMREG_IP);
349
350         constants = (gpointer*)code;
351         constants [0] = mono_get_lmf_addr;
352         constants [1] = (gpointer)mono_get_trampoline_func (tramp_type);
353
354         if (!aot) {
355                 /* backpatch by emitting the missing instructions skipped above */
356                 ARM_LDR_IMM (load_get_lmf_addr, ARMREG_R0, ARMREG_PC, (code - load_get_lmf_addr - 8));
357                 ARM_LDR_IMM (load_trampoline, ARMREG_IP, ARMREG_PC, (code + 4 - load_trampoline - 8));
358         }
359
360         code += 8;
361
362         /* Flush instruction cache, since we've generated code */
363         mono_arch_flush_icache (buf, code - buf);
364
365         /* Sanity check */
366         g_assert ((code - buf) <= buf_len);
367
368         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
369                 /* Initialize the nullified class init trampoline used in the AOT case */
370                 nullified_class_init_trampoline = mono_arch_get_nullified_class_init_trampoline (NULL);
371
372         if (info)
373                 *info = mono_tramp_info_create (mono_get_generic_trampoline_name (tramp_type), buf, code - buf, ji, unwind_ops);
374
375         return buf;
376 }
377
378 gpointer
379 mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo **info)
380 {
381         guint8 *buf, *code;
382
383         code = buf = mono_global_codeman_reserve (16);
384
385         code = emit_bx (code, ARMREG_LR);
386
387         mono_arch_flush_icache (buf, code - buf);
388
389         if (info)
390                 *info = mono_tramp_info_create (g_strdup_printf ("nullified_class_init_trampoline"), buf, code - buf, NULL, NULL);
391
392         return buf;
393 }
394
395 #define SPEC_TRAMP_SIZE 24
396
397 gpointer
398 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
399 {
400         guint8 *code, *buf, *tramp;
401         gpointer *constants;
402         guint32 short_branch, size = SPEC_TRAMP_SIZE;
403
404         tramp = mono_get_trampoline_code (tramp_type);
405
406         mono_domain_lock (domain);
407         code = buf = mono_domain_code_reserve_align (domain, size, 4);
408         if ((short_branch = branch_for_target_reachable (code + 4, tramp))) {
409                 size = 12;
410                 mono_domain_code_commit (domain, code, SPEC_TRAMP_SIZE, size);
411         }
412         mono_domain_unlock (domain);
413
414         /* we could reduce this to 12 bytes if tramp is within reach:
415          * ARM_PUSH ()
416          * ARM_BL ()
417          * method-literal
418          * The called code can access method using the lr register
419          * A 20 byte sequence could be:
420          * ARM_PUSH ()
421          * ARM_MOV_REG_REG (lr, pc)
422          * ARM_LDR_IMM (pc, pc, 0)
423          * method-literal
424          * tramp-literal
425          */
426         /* We save all the registers, except PC and SP */
427         ARM_PUSH (code, 0x5fff);
428         if (short_branch) {
429                 constants = (gpointer*)code;
430                 constants [0] = GUINT_TO_POINTER (short_branch | (1 << 24));
431                 constants [1] = arg1;
432                 code += 8;
433         } else {
434                 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 8); /* temp reg */
435                 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
436                 code = emit_bx (code, ARMREG_R1);
437
438                 constants = (gpointer*)code;
439                 constants [0] = arg1;
440                 constants [1] = tramp;
441                 code += 8;
442         }
443
444         /* Flush instruction cache, since we've generated code */
445         mono_arch_flush_icache (buf, code - buf);
446
447         g_assert ((code - buf) <= size);
448
449         if (code_len)
450                 *code_len = code - buf;
451
452         return buf;
453 }
454
455 /*
456  * mono_arch_get_unbox_trampoline:
457  * @m: method pointer
458  * @addr: pointer to native code for @m
459  *
460  * when value type methods are called through the vtable we need to unbox the
461  * this argument. This method returns a pointer to a trampoline which does
462  * unboxing before calling the method
463  */
464 gpointer
465 mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
466 {
467         guint8 *code, *start;
468         MonoDomain *domain = mono_domain_get ();
469
470         start = code = mono_domain_code_reserve (domain, 16);
471
472         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 4);
473         ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_R0, sizeof (MonoObject));
474         code = emit_bx (code, ARMREG_IP);
475         *(guint32*)code = (guint32)addr;
476         code += 4;
477         mono_arch_flush_icache (start, code - start);
478         g_assert ((code - start) <= 16);
479         /*g_print ("unbox trampoline at %d for %s:%s\n", this_pos, m->klass->name, m->name);
480         g_print ("unbox code is at %p for method at %p\n", start, addr);*/
481
482         return start;
483 }
484
485 gpointer
486 mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr)
487 {
488         guint8 *code, *start;
489         int buf_len;
490
491         MonoDomain *domain = mono_domain_get ();
492
493         buf_len = 16;
494
495         start = code = mono_domain_code_reserve (domain, buf_len);
496
497         ARM_LDR_IMM (code, MONO_ARCH_RGCTX_REG, ARMREG_PC, 0);
498         ARM_LDR_IMM (code, ARMREG_PC, ARMREG_PC, 0);
499         *(guint32*)code = (guint32)mrgctx;
500         code += 4;
501         *(guint32*)code = (guint32)addr;
502         code += 4;
503
504         g_assert ((code - start) <= buf_len);
505
506         mono_arch_flush_icache (start, code - start);
507
508         return start;
509 }
510
511 gpointer
512 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
513 {
514         guint8 *tramp;
515         guint8 *code, *buf;
516         int tramp_size;
517         guint32 code_len;
518         guint8 **rgctx_null_jumps;
519         int depth, index;
520         int i, njumps;
521         gboolean mrgctx;
522         MonoJumpInfo *ji = NULL;
523         GSList *unwind_ops = NULL;
524
525         mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
526         index = MONO_RGCTX_SLOT_INDEX (slot);
527         if (mrgctx)
528                 index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
529         for (depth = 0; ; ++depth) {
530                 int size = mono_class_rgctx_get_array_size (depth, mrgctx);
531
532                 if (index < size - 1)
533                         break;
534                 index -= size - 1;
535         }
536
537         tramp_size = 64 + 16 * depth;
538
539         code = buf = mono_global_codeman_reserve (tramp_size);
540
541         mono_add_unwind_op_def_cfa (unwind_ops, code, buf, ARMREG_SP, 0);
542
543         rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2));
544         njumps = 0;
545
546         /* The vtable/mrgctx is in R0 */
547         g_assert (MONO_ARCH_VTABLE_REG == ARMREG_R0);
548
549         if (mrgctx) {
550                 /* get mrgctx ptr */
551                 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
552         } else {
553                 /* load rgctx ptr from vtable */
554                 g_assert (arm_is_imm12 (G_STRUCT_OFFSET (MonoVTable, runtime_generic_context)));
555                 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoVTable, runtime_generic_context));
556                 /* is the rgctx ptr null? */
557                 ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
558                 /* if yes, jump to actual trampoline */
559                 rgctx_null_jumps [njumps ++] = code;
560                 ARM_B_COND (code, ARMCOND_EQ, 0);
561         }
562
563         for (i = 0; i < depth; ++i) {
564                 /* load ptr to next array */
565                 if (mrgctx && i == 0) {
566                         g_assert (arm_is_imm12 (MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT));
567                         ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R1, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT);
568                 } else {
569                         ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R1, 0);
570                 }
571                 /* is the ptr null? */
572                 ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
573                 /* if yes, jump to actual trampoline */
574                 rgctx_null_jumps [njumps ++] = code;
575                 ARM_B_COND (code, ARMCOND_EQ, 0);
576         }
577
578         /* fetch slot */
579         code = mono_arm_emit_load_imm (code, ARMREG_R2, sizeof (gpointer) * (index + 1));
580         ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_R1, ARMREG_R2);
581         /* is the slot null? */
582         ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
583         /* if yes, jump to actual trampoline */
584         rgctx_null_jumps [njumps ++] = code;
585         ARM_B_COND (code, ARMCOND_EQ, 0);
586         /* otherwise return, result is in R1 */
587         ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_R1);
588         code = emit_bx (code, ARMREG_LR);
589
590         g_assert (njumps <= depth + 2);
591         for (i = 0; i < njumps; ++i)
592                 arm_patch (rgctx_null_jumps [i], code);
593
594         g_free (rgctx_null_jumps);
595
596         /* Slowpath */
597
598         /* The vtable/mrgctx is still in R0 */
599
600         if (aot) {
601                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", slot));
602                 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
603                 ARM_B (code, 0);
604                 *(gpointer*)code = NULL;
605                 code += 4;
606                 ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_R1);
607         } else {
608                 tramp = mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), &code_len);
609
610                 /* Jump to the actual trampoline */
611                 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0); /* temp reg */
612                 code = emit_bx (code, ARMREG_R1);
613                 *(gpointer*)code = tramp;
614                 code += 4;
615         }
616
617         mono_arch_flush_icache (buf, code - buf);
618
619         g_assert (code - buf <= tramp_size);
620
621         if (info)
622                 *info = mono_tramp_info_create (mono_get_rgctx_fetch_trampoline_name (slot), buf, code - buf, ji, unwind_ops);
623
624         return buf;
625 }
626
627 #define arm_is_imm8(v) ((v) > -256 && (v) < 256)
628
629 gpointer
630 mono_arch_create_generic_class_init_trampoline (MonoTrampInfo **info, gboolean aot)
631 {
632         guint8 *tramp;
633         guint8 *code, *buf;
634         static int byte_offset = -1;
635         static guint8 bitmask;
636         guint8 *jump;
637         int tramp_size;
638         guint32 code_len, imm8;
639         gint rot_amount;
640         GSList *unwind_ops = NULL;
641         MonoJumpInfo *ji = NULL;
642
643         tramp_size = 64;
644
645         code = buf = mono_global_codeman_reserve (tramp_size);
646
647         if (byte_offset < 0)
648                 mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask);
649
650         g_assert (arm_is_imm8 (byte_offset));
651         ARM_LDRSB_IMM (code, ARMREG_IP, MONO_ARCH_VTABLE_REG, byte_offset);
652         imm8 = mono_arm_is_rotated_imm8 (bitmask, &rot_amount);
653         g_assert (imm8 >= 0);
654         ARM_AND_REG_IMM (code, ARMREG_IP, ARMREG_IP, imm8, rot_amount);
655         ARM_CMP_REG_IMM (code, ARMREG_IP, 0, 0);
656         jump = code;
657         ARM_B_COND (code, ARMCOND_EQ, 0);
658
659         /* Initialized case */
660         ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_LR);   
661
662         /* Uninitialized case */
663         arm_patch (jump, code);
664
665         if (aot) {
666                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "specific_trampoline_generic_class_init");
667                 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
668                 ARM_B (code, 0);
669                 *(gpointer*)code = NULL;
670                 code += 4;
671                 ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_R1);
672         } else {
673                 tramp = mono_arch_create_specific_trampoline (NULL, MONO_TRAMPOLINE_GENERIC_CLASS_INIT, mono_get_root_domain (), &code_len);
674
675                 /* Jump to the actual trampoline */
676                 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0); /* temp reg */
677                 code = emit_bx (code, ARMREG_R1);
678                 *(gpointer*)code = tramp;
679                 code += 4;
680         }
681
682         mono_arch_flush_icache (buf, code - buf);
683
684         g_assert (code - buf <= tramp_size);
685
686         if (info)
687                 *info = mono_tramp_info_create (g_strdup_printf ("generic_class_init_trampoline"), buf, code - buf, ji, unwind_ops);
688
689         return buf;
690 }
691
692 #else
693
694 guchar*
695 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
696 {
697         g_assert_not_reached ();
698         return NULL;
699 }
700
701 gpointer
702 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
703 {
704         g_assert_not_reached ();
705         return NULL;
706 }
707
708 gpointer
709 mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
710 {
711         g_assert_not_reached ();
712         return NULL;
713 }
714
715 gpointer
716 mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr)
717 {
718         g_assert_not_reached ();
719         return NULL;
720 }
721
722 gpointer
723 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
724 {
725         g_assert_not_reached ();
726         return NULL;
727 }
728
729 gpointer
730 mono_arch_create_generic_class_init_trampoline (MonoTrampInfo **info, gboolean aot)
731 {
732         g_assert_not_reached ();
733         return NULL;
734 }
735         
736 #endif /* DISABLE_JIT */
737
738 guint8*
739 mono_arch_get_call_target (guint8 *code)
740 {
741         guint32 ins = ((guint32*)(gpointer)code) [-1];
742
743 #if MONOTOUCH
744         /* Should be a 'bl' or a 'b' */
745         if (((ins >> 25) & 0x7) == 0x5) {
746 #else
747         /* Should be a 'bl' */
748         if ((((ins >> 25) & 0x7) == 0x5) && (((ins >> 24) & 0x1) == 0x1)) {
749 #endif
750                 gint32 disp = ((gint32)ins) & 0xffffff;
751                 guint8 *target = code - 4 + 8 + (disp * 4);
752
753                 return target;
754         } else {
755                 return NULL;
756         }
757 }
758
759 guint32
760 mono_arch_get_plt_info_offset (guint8 *plt_entry, mgreg_t *regs, guint8 *code)
761 {
762         /* The offset is stored as the 4th word of the plt entry */
763         return ((guint32*)plt_entry) [3];
764 }
765
766 /*
767  * Return the address of the PLT entry called by the thumb code CODE.
768  */
769 guint8*
770 mono_arm_get_thumb_plt_entry (guint8 *code)
771 {
772         int s, j1, j2, imm10, imm11, i1, i2, imm32;
773         guint8 *bl, *base;
774         guint16 t1, t2;
775         guint8 *target;
776
777         /* code should be right after a BL */
778         code = (guint8*)((mgreg_t)code & ~1);
779         base = (guint8*)((mgreg_t)code & ~3);
780         bl = code - 4;
781         t1 = ((guint16*)bl) [0];
782         t2 = ((guint16*)bl) [1];
783
784         g_assert ((t1 >> 11) == 0x1e);
785
786         s = (t1 >> 10) & 0x1;
787         imm10 = (t1 >> 0) & 0x3ff;
788         j1 = (t2 >> 13) & 0x1;
789         j2 = (t2 >> 11) & 0x1;
790         imm11 = t2 & 0x7ff;
791
792         i1 = (s ^ j1) ? 0 : 1;
793         i2 = (s ^ j2) ? 0 : 1;
794
795         imm32 = (imm11 << 1) | (imm10 << 12) | (i2 << 22) | (i1 << 23);
796         // FIXME:
797         g_assert (s == 0);
798
799         target = code + imm32;
800
801         /* target now points to the thumb plt entry */
802         /* ldr.w r12, [pc, #8] */
803         g_assert (((guint16*)target) [0] == 0xf8df);
804         g_assert (((guint16*)target) [1] == 0xc008);
805
806         /* 
807          * The PLT info offset is at offset 16, but mono_arch_get_plt_entry_offset () returns
808          * the 3rd word, so compensate by returning a different value.
809          */
810         target += 4;
811
812         return target;
813 }