[jit] Add a new jit icall mono_interruption_checkpoint_from_trampoline () and use...
[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/abi-details.h>
16 #include <mono/metadata/appdomain.h>
17 #include <mono/metadata/marshal.h>
18 #include <mono/metadata/tabledefs.h>
19 #include <mono/metadata/profiler-private.h>
20 #include <mono/arch/arm/arm-codegen.h>
21 #include <mono/arch/arm/arm-vfp-codegen.h>
22
23 #include "mini.h"
24 #include "mini-arm.h"
25 #include "debugger-agent.h"
26
27 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
28
29 #ifdef USE_JUMP_TABLES
30
31 static guint16
32 decode_imm16 (guint32 insn)
33 {
34         return (((insn >> 16) & 0xf) << 12) | (insn & 0xfff);
35 }
36
37 #define INSN_MASK 0xff00000
38 #define MOVW_MASK ((3 << 24) | (0 << 20))
39 #define MOVT_MASK ((3 << 24) | (4 << 20))
40
41 gpointer*
42 mono_arch_jumptable_entry_from_code (guint8 *code)
43 {
44         guint32 insn1 = ((guint32*)code) [0];
45         guint32 insn2 = ((guint32*)code) [1];
46
47         if (((insn1 & INSN_MASK) == MOVW_MASK) &&
48             ((insn2 & INSN_MASK) == MOVT_MASK) ) {
49                 guint32 imm_lo = decode_imm16 (insn1);
50                 guint32 imm_hi = decode_imm16 (insn2);
51                 return (gpointer*) GUINT_TO_POINTER (imm_lo | (imm_hi << 16));
52         } else {
53                 g_assert_not_reached ();
54                 return NULL;
55         }
56 }
57
58 #undef INSN_MASK
59 #undef MOVW_MASK
60 #undef MOVT_MASK
61
62 void
63 mono_arch_patch_callsite (guint8 *method_start, guint8 *code_ptr, guint8 *addr)
64 {
65         gpointer *jte;
66         /*
67          * code_ptr is 4 instructions after MOVW/MOVT used to address
68          * jumptable entry.
69          */
70         jte = mono_jumptable_get_entry (code_ptr - 16);
71         g_assert ( jte != NULL);
72         *jte = addr;
73 }
74 #else
75 void
76 mono_arch_patch_callsite (guint8 *method_start, guint8 *code_ptr, guint8 *addr)
77 {
78         guint32 *code = (guint32*)code_ptr;
79
80         /* This is the 'bl' or the 'mov pc' instruction */
81         --code;
82         
83         /*
84          * Note that methods are called also with the bl opcode.
85          */
86         if ((((*code) >> 25)  & 7) == 5) {
87                 /*g_print ("direct patching\n");*/
88                 arm_patch ((guint8*)code, addr);
89                 mono_arch_flush_icache ((guint8*)code, 4);
90                 return;
91         }
92
93         if ((((*code) >> 20) & 0xFF) == 0x12) {
94                 /*g_print ("patching bx\n");*/
95                 arm_patch ((guint8*)code, addr);
96                 mono_arch_flush_icache ((guint8*)(code - 2), 4);
97                 return;
98         }
99
100         g_assert_not_reached ();
101 }
102 #endif
103
104 void
105 mono_arch_patch_plt_entry (guint8 *code, gpointer *got, mgreg_t *regs, guint8 *addr)
106 {
107         guint8 *jump_entry;
108
109         /* Patch the jump table entry used by the plt entry */
110         if (*(guint32*)code == 0xe59fc000) {
111                 /* ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0); */
112                 guint32 offset = ((guint32*)code)[2];
113                 
114                 jump_entry = code + offset + 12;
115         } else if (*(guint16*)(code - 4) == 0xf8df) {
116                 /* 
117                  * Thumb PLT entry, begins with ldr.w ip, [pc, #8], code points to entry + 4, see
118                  * mono_arm_get_thumb_plt_entry ().
119                  */
120                 guint32 offset;
121
122                 code -= 4;
123                 offset = *(guint32*)(code + 12);
124                 jump_entry = code + offset + 8;
125         } else {
126                 g_assert_not_reached ();
127         }
128
129         *(guint8**)jump_entry = addr;
130 }
131
132 #ifndef DISABLE_JIT
133
134 #define arm_is_imm12(v) ((int)(v) > -4096 && (int)(v) < 4096)
135
136 #ifndef USE_JUMP_TABLES
137 /*
138  * Return the instruction to jump from code to target, 0 if not
139  * reachable with a single instruction
140  */
141 static guint32
142 branch_for_target_reachable (guint8 *branch, guint8 *target)
143 {
144         gint diff = target - branch - 8;
145         g_assert ((diff & 3) == 0);
146         if (diff >= 0) {
147                 if (diff <= 33554431)
148                         return (ARMCOND_AL << ARMCOND_SHIFT) | (ARM_BR_TAG) | (diff >> 2);
149         } else {
150                 /* diff between 0 and -33554432 */
151                 if (diff >= -33554432)
152                         return (ARMCOND_AL << ARMCOND_SHIFT) | (ARM_BR_TAG) | ((diff >> 2) & ~0xff000000);
153         }
154         return 0;
155 }
156 #endif
157
158 static inline guint8*
159 emit_bx (guint8* code, int reg)
160 {
161         if (mono_arm_thumb_supported ())
162                 ARM_BX (code, reg);
163         else
164                 ARM_MOV_REG_REG (code, ARMREG_PC, reg);
165         return code;
166 }
167
168 /* Stack size for trampoline function
169  */
170 #define STACK ALIGN_TO (sizeof (MonoLMF), MONO_ARCH_FRAME_ALIGNMENT)
171
172 /* Method-specific trampoline code fragment size */
173 #define METHOD_TRAMPOLINE_SIZE 64
174
175 /* Jump-specific trampoline code fragment size */
176 #define JUMP_TRAMPOLINE_SIZE   64
177
178 guchar*
179 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
180 {
181         char *tramp_name;
182         guint8 *buf, *code = NULL;
183 #ifdef USE_JUMP_TABLES
184         gpointer *load_get_lmf_addr = NULL, *load_trampoline = NULL;
185 #else
186         guint8 *load_get_lmf_addr  = NULL, *load_trampoline  = NULL;
187         gpointer *constants;
188 #endif
189         int i, cfa_offset, regsave_size, lr_offset;
190         GSList *unwind_ops = NULL;
191         MonoJumpInfo *ji = NULL;
192         int buf_len;
193
194 #ifdef USE_JUMP_TABLES
195         g_assert (!aot);
196 #endif
197
198         /* Now we'll create in 'buf' the ARM trampoline code. This
199          is the trampoline code common to all methods  */
200
201         buf_len = 272;
202
203         /* Add space for saving/restoring VFP regs. */
204         if (mono_arm_is_hard_float ())
205                 buf_len += 8 * 2;
206
207         code = buf = mono_global_codeman_reserve (buf_len);
208
209         /*
210          * At this point lr points to the specific arg and sp points to the saved
211          * regs on the stack (all but PC and SP). The original LR value has been
212          * saved as sp + LR_OFFSET by the push in the specific trampoline
213          */
214
215         /* The size of the area already allocated by the push in the specific trampoline */
216         regsave_size = 14 * sizeof (mgreg_t);
217         /* The offset where lr was saved inside the regsave area */
218         lr_offset = 13 * sizeof (mgreg_t);
219
220         // CFA = SP + (num registers pushed) * 4
221         cfa_offset = 14 * sizeof (mgreg_t);
222         mono_add_unwind_op_def_cfa (unwind_ops, code, buf, ARMREG_SP, cfa_offset);
223         // PC saved at sp+LR_OFFSET
224         mono_add_unwind_op_offset (unwind_ops, code, buf, ARMREG_LR, -4);
225         /* Callee saved regs */
226         for (i = 0; i < 8; ++i)
227                 mono_add_unwind_op_offset (unwind_ops, code, buf, ARMREG_R4 + i, -regsave_size + ((4 + i) * 4));
228
229         if (aot) {
230                 /* 
231                  * For page trampolines the data is in r1, so just move it, otherwise use the got slot as below.
232                  * The trampoline contains a pc-relative offset to the got slot 
233                  * preceeding the got slot where the value is stored. The offset can be
234                  * found at [lr + 0].
235                  */
236                 /* See if emit_trampolines () in aot-compiler.c for the '2' */
237                 if (aot == 2) {
238                         ARM_MOV_REG_REG (code, ARMREG_V2, ARMREG_R1);
239                 } else {
240                         ARM_LDR_IMM (code, ARMREG_V2, ARMREG_LR, 0);
241                         ARM_ADD_REG_IMM (code, ARMREG_V2, ARMREG_V2, 4, 0);
242                         ARM_LDR_REG_REG (code, ARMREG_V2, ARMREG_V2, ARMREG_LR);
243                 }
244         } else {
245                 ARM_LDR_IMM (code, ARMREG_V2, ARMREG_LR, 0);
246         }
247         ARM_LDR_IMM (code, ARMREG_V3, ARMREG_SP, lr_offset);
248
249         /* we build the MonoLMF structure on the stack - see mini-arm.h
250          * The pointer to the struct is put in r1.
251          * the iregs array is already allocated on the stack by push.
252          */
253         code = mono_arm_emit_load_imm (code, ARMREG_R2, STACK - regsave_size);
254         ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_R2);
255         cfa_offset += STACK - regsave_size;
256         mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
257         /* V1 == lmf */
258         code = mono_arm_emit_load_imm (code, ARMREG_R2, STACK - sizeof (MonoLMF));
259         ARM_ADD_REG_REG (code, ARMREG_V1, ARMREG_SP, ARMREG_R2);
260
261         /* ok, now we can continue with the MonoLMF setup, mostly untouched 
262          * from emit_prolog in mini-arm.c
263          * This is a synthetized call to mono_get_lmf_addr ()
264          */
265         if (aot) {
266                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_get_lmf_addr");
267                 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
268                 ARM_B (code, 0);
269                 *(gpointer*)code = NULL;
270                 code += 4;
271                 ARM_LDR_REG_REG (code, ARMREG_R0, ARMREG_PC, ARMREG_R0);
272         } else {
273 #ifdef USE_JUMP_TABLES
274                 load_get_lmf_addr = mono_jumptable_add_entry ();
275                 code = mono_arm_load_jumptable_entry (code, load_get_lmf_addr, ARMREG_R0);
276 #else
277                 load_get_lmf_addr = code;
278                 code += 4;
279 #endif
280         }
281         ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
282         code = emit_bx (code, ARMREG_R0);
283
284         /*
285          * The stack now looks like:
286          *       <saved regs>
287          * v1 -> <rest of LMF>
288          * sp -> <alignment>
289          */
290
291         /* r0 is the result from mono_get_lmf_addr () */
292         ARM_STR_IMM (code, ARMREG_R0, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, lmf_addr));
293         /* new_lmf->previous_lmf = *lmf_addr */
294         ARM_LDR_IMM (code, ARMREG_R2, ARMREG_R0, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
295         ARM_STR_IMM (code, ARMREG_R2, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
296         /* *(lmf_addr) = r1 */
297         ARM_STR_IMM (code, ARMREG_V1, ARMREG_R0, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
298         /* save method info (it's in v2) */
299         if ((tramp_type == MONO_TRAMPOLINE_JIT) || (tramp_type == MONO_TRAMPOLINE_JUMP))
300                 ARM_STR_IMM (code, ARMREG_V2, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, method));
301         else {
302                 ARM_MOV_REG_IMM8 (code, ARMREG_R2, 0);
303                 ARM_STR_IMM (code, ARMREG_R2, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, method));
304         }
305         /* save caller SP */
306         code = mono_arm_emit_load_imm (code, ARMREG_R2, cfa_offset);
307         ARM_ADD_REG_REG (code, ARMREG_R2, ARMREG_SP, ARMREG_R2);
308         ARM_STR_IMM (code, ARMREG_R2, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, sp));
309         /* save caller FP */
310         ARM_LDR_IMM (code, ARMREG_R2, ARMREG_V1, (MONO_STRUCT_OFFSET (MonoLMF, iregs) + ARMREG_FP*4));
311         ARM_STR_IMM (code, ARMREG_R2, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, fp));
312         /* save the IP (caller ip) */
313         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
314                 ARM_MOV_REG_IMM8 (code, ARMREG_R2, 0);
315         } else {
316                 ARM_LDR_IMM (code, ARMREG_R2, ARMREG_V1, (MONO_STRUCT_OFFSET (MonoLMF, iregs) + 13*4));
317         }
318         ARM_STR_IMM (code, ARMREG_R2, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, ip));
319
320         /* Save VFP registers. */
321         if (mono_arm_is_hard_float ()) {
322                 /*
323                  * Strictly speaking, we don't have to save d0-d7 in the LMF, but
324                  * it's easier than attempting to store them on the stack since
325                  * this trampoline code is pretty messy.
326                  */
327                 ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, fregs));
328                 ARM_FSTMD (code, ARM_VFP_D0, 8, ARMREG_R0);
329         }
330
331         /*
332          * Now we're ready to call xxx_trampoline ().
333          */
334         /* Arg 1: the saved registers */
335         ARM_ADD_REG_IMM (code, ARMREG_R0, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, iregs), 0);
336
337         /* Arg 2: code (next address to the instruction that called us) */
338         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
339                 ARM_MOV_REG_IMM8 (code, ARMREG_R1, 0);
340         } else {
341                 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_V3);
342         }
343         
344         /* Arg 3: the specific argument, stored in v2
345          */
346         ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_V2);
347
348         if (aot) {
349                 char *icall_name = g_strdup_printf ("trampoline_func_%d", tramp_type);
350                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name);
351                 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
352                 ARM_B (code, 0);
353                 *(gpointer*)code = NULL;
354                 code += 4;
355                 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
356         } else {
357 #ifdef USE_JUMP_TABLES
358                 load_trampoline = mono_jumptable_add_entry ();
359                 code = mono_arm_load_jumptable_entry (code, load_trampoline, ARMREG_IP);
360 #else
361                 load_trampoline = code;
362                 code += 4;
363 #endif
364         }
365
366         ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
367         code = emit_bx (code, ARMREG_IP);
368
369         /* OK, code address is now on r0. Move it to the place on the stack
370          * where IP was saved (it is now no more useful to us and it can be
371          * clobbered). This way we can just restore all the regs in one inst
372          * and branch to IP.
373          */
374         ARM_STR_IMM (code, ARMREG_R0, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, iregs) + (ARMREG_R12 * sizeof (mgreg_t)));
375
376         /* Check for thread interruption */
377         /* This is not perf critical code so no need to check the interrupt flag */
378         /* 
379          * Have to call the _force_ variant, since there could be a protected wrapper on the top of the stack.
380          */
381         if (aot) {
382                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_interruption_checkpoint_from_trampoline");
383                 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
384                 ARM_B (code, 0);
385                 *(gpointer*)code = NULL;
386                 code += 4;
387                 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
388         } else {
389 #ifdef USE_JUMP_TABLES
390                 gpointer *jte = mono_jumptable_add_entry ();
391                 code = mono_arm_load_jumptable_entry (code, jte, ARMREG_IP);
392                 jte [0] = mono_interruption_checkpoint_from_trampoline;
393 #else
394                 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
395                 ARM_B (code, 0);
396                 *(gpointer*)code = mono_interruption_checkpoint_from_trampoline;
397                 code += 4;
398 #endif
399         }
400         ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
401         code = emit_bx (code, ARMREG_IP);
402
403         /*
404          * Now we restore the MonoLMF (see emit_epilogue in mini-arm.c)
405          * and the rest of the registers, so the method called will see
406          * the same state as before we executed.
407          */
408         /* ip = previous_lmf */
409         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
410         /* lr = lmf_addr */
411         ARM_LDR_IMM (code, ARMREG_LR, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, lmf_addr));
412         /* *(lmf_addr) = previous_lmf */
413         ARM_STR_IMM (code, ARMREG_IP, ARMREG_LR, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
414
415         /* Restore VFP registers. */
416         if (mono_arm_is_hard_float ()) {
417                 ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_V1, MONO_STRUCT_OFFSET (MonoLMF, fregs));
418                 ARM_FLDMD (code, ARM_VFP_D0, 8, ARMREG_R0);
419         }
420
421         /* Non-standard function epilogue. Instead of doing a proper
422          * return, we just jump to the compiled code.
423          */
424         /* Restore the registers and jump to the code:
425          * Note that IP has been conveniently set to the method addr.
426          */
427         ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, STACK - regsave_size);
428         cfa_offset -= STACK - regsave_size;
429         mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
430         ARM_POP_NWB (code, 0x5fff);
431         mono_add_unwind_op_same_value (unwind_ops, code, buf, ARMREG_LR);
432         if (tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH)
433                 ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_IP);
434         ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, regsave_size);
435         cfa_offset -= regsave_size;
436         g_assert (cfa_offset == 0);
437         mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
438         if (MONO_TRAMPOLINE_TYPE_MUST_RETURN (tramp_type))
439                 code = emit_bx (code, ARMREG_LR);
440         else
441                 code = emit_bx (code, ARMREG_IP);
442
443 #ifdef USE_JUMP_TABLES
444         load_get_lmf_addr [0] = mono_get_lmf_addr;
445         load_trampoline [0] = (gpointer)mono_get_trampoline_func (tramp_type);
446 #else
447         constants = (gpointer*)code;
448         constants [0] = mono_get_lmf_addr;
449         constants [1] = (gpointer)mono_get_trampoline_func (tramp_type);
450
451         if (!aot) {
452                 /* backpatch by emitting the missing instructions skipped above */
453                 ARM_LDR_IMM (load_get_lmf_addr, ARMREG_R0, ARMREG_PC, (code - load_get_lmf_addr - 8));
454                 ARM_LDR_IMM (load_trampoline, ARMREG_IP, ARMREG_PC, (code + 4 - load_trampoline - 8));
455         }
456
457         code += 8;
458 #endif
459
460         /* Flush instruction cache, since we've generated code */
461         mono_arch_flush_icache (buf, code - buf);
462         mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_HELPER, NULL);
463
464         /* Sanity check */
465         g_assert ((code - buf) <= buf_len);
466
467         g_assert (info);
468         tramp_name = mono_get_generic_trampoline_name (tramp_type);
469         *info = mono_tramp_info_create (tramp_name, buf, code - buf, ji, unwind_ops);
470         g_free (tramp_name);
471
472         return buf;
473 }
474
475 #define SPEC_TRAMP_SIZE 24
476
477 gpointer
478 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
479 {
480         guint8 *code, *buf, *tramp;
481         gpointer *constants;
482 #ifndef USE_JUMP_TABLES
483         guint32 short_branch = FALSE;
484 #endif
485         guint32 size = SPEC_TRAMP_SIZE;
486
487         tramp = mono_get_trampoline_code (tramp_type);
488
489         if (domain) {
490                 mono_domain_lock (domain);
491 #ifdef USE_JUMP_TABLES
492                 code = buf = mono_domain_code_reserve_align (domain, size, 4);
493 #else
494                 code = buf = mono_domain_code_reserve_align (domain, size, 4);
495                 if ((short_branch = branch_for_target_reachable (code + 4, tramp))) {
496                         size = 12;
497                         mono_domain_code_commit (domain, code, SPEC_TRAMP_SIZE, size);
498         }
499 #endif
500                 mono_domain_unlock (domain);
501         } else {
502                 code = buf = mono_global_codeman_reserve (size);
503                 short_branch = FALSE;
504         }
505
506 #ifdef USE_JUMP_TABLES
507         /* For jumptables case we always generate the same code for trampolines,
508          * namely
509          *   push {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}
510          *   movw lr, lo(jte)
511          *   movt lr, hi(jte)
512          *   ldr r1, [lr + 4]
513          *   bx r1
514          */
515         ARM_PUSH (code, 0x5fff);
516         constants = mono_jumptable_add_entries (2);
517         code = mono_arm_load_jumptable_entry_addr (code, constants, ARMREG_LR);
518         ARM_LDR_IMM (code, ARMREG_R1, ARMREG_LR, 4);
519         code = emit_bx (code, ARMREG_R1);
520         constants [0] = arg1;
521         constants [1] = tramp;
522 #else
523         /* we could reduce this to 12 bytes if tramp is within reach:
524          * ARM_PUSH ()
525          * ARM_BL ()
526          * method-literal
527          * The called code can access method using the lr register
528          * A 20 byte sequence could be:
529          * ARM_PUSH ()
530          * ARM_MOV_REG_REG (lr, pc)
531          * ARM_LDR_IMM (pc, pc, 0)
532          * method-literal
533          * tramp-literal
534          */
535         /* We save all the registers, except PC and SP */
536         ARM_PUSH (code, 0x5fff);
537         if (short_branch) {
538                 constants = (gpointer*)code;
539                 constants [0] = GUINT_TO_POINTER (short_branch | (1 << 24));
540                 constants [1] = arg1;
541                 code += 8;
542         } else {
543                 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 8); /* temp reg */
544                 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
545                 code = emit_bx (code, ARMREG_R1);
546
547                 constants = (gpointer*)code;
548                 constants [0] = arg1;
549                 constants [1] = tramp;
550                 code += 8;
551         }
552 #endif
553
554         /* Flush instruction cache, since we've generated code */
555         mono_arch_flush_icache (buf, code - buf);
556         mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE, mono_get_generic_trampoline_simple_name (tramp_type));
557
558         g_assert ((code - buf) <= size);
559
560         if (code_len)
561                 *code_len = code - buf;
562
563         return buf;
564 }
565
566 /*
567  * mono_arch_get_unbox_trampoline:
568  * @m: method pointer
569  * @addr: pointer to native code for @m
570  *
571  * when value type methods are called through the vtable we need to unbox the
572  * this argument. This method returns a pointer to a trampoline which does
573  * unboxing before calling the method
574  */
575 gpointer
576 mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
577 {
578         guint8 *code, *start;
579         MonoDomain *domain = mono_domain_get ();
580         GSList *unwind_ops;
581 #ifdef USE_JUMP_TABLES
582         gpointer *jte;
583         guint32 size = 20;
584 #else
585         guint32 size = 16;
586 #endif
587
588         start = code = mono_domain_code_reserve (domain, size);
589
590         unwind_ops = mono_arch_get_cie_program ();
591
592 #ifdef USE_JUMP_TABLES
593         jte = mono_jumptable_add_entry ();
594         code = mono_arm_load_jumptable_entry (code, jte, ARMREG_IP);
595         ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_R0, sizeof (MonoObject));
596         code = emit_bx (code, ARMREG_IP);
597         jte [0] = addr;
598 #else
599         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 4);
600         ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_R0, sizeof (MonoObject));
601         code = emit_bx (code, ARMREG_IP);
602         *(guint32*)code = (guint32)addr;
603         code += 4;
604 #endif
605         mono_arch_flush_icache (start, code - start);
606         mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE, m);
607         g_assert ((code - start) <= size);
608         /*g_print ("unbox trampoline at %d for %s:%s\n", this_pos, m->klass->name, m->name);
609         g_print ("unbox code is at %p for method at %p\n", start, addr);*/
610
611         mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), domain);
612
613         return start;
614 }
615
616 gpointer
617 mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr)
618 {
619         guint8 *code, *start;
620         GSList *unwind_ops;
621 #ifdef USE_JUMP_TABLES
622         int buf_len = 20;
623         gpointer *jte;
624 #else
625         int buf_len = 16;
626 #endif
627         MonoDomain *domain = mono_domain_get ();
628
629         start = code = mono_domain_code_reserve (domain, buf_len);
630
631         unwind_ops = mono_arch_get_cie_program ();
632
633 #ifdef USE_JUMP_TABLES
634         jte = mono_jumptable_add_entries (2);
635         code = mono_arm_load_jumptable_entry_addr (code, jte, ARMREG_IP);
636         ARM_LDR_IMM (code, MONO_ARCH_RGCTX_REG, ARMREG_IP, 0);
637         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, 4);
638         ARM_BX (code, ARMREG_IP);
639         jte [0] = mrgctx;
640         jte [1] = addr;
641 #else
642         ARM_LDR_IMM (code, MONO_ARCH_RGCTX_REG, ARMREG_PC, 0);
643         ARM_LDR_IMM (code, ARMREG_PC, ARMREG_PC, 0);
644         *(guint32*)code = (guint32)mrgctx;
645         code += 4;
646         *(guint32*)code = (guint32)addr;
647         code += 4;
648 #endif
649
650         g_assert ((code - start) <= buf_len);
651
652         mono_arch_flush_icache (start, code - start);
653         mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL);
654
655         mono_tramp_info_register (mono_tramp_info_create (NULL, start, code - start, NULL, unwind_ops), domain);
656
657         return start;
658 }
659
660 gpointer
661 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
662 {
663         guint8 *tramp;
664         guint8 *code, *buf;
665         int tramp_size;
666         guint32 code_len;
667         guint8 **rgctx_null_jumps;
668         int depth, index;
669         int i, njumps;
670         gboolean mrgctx;
671         MonoJumpInfo *ji = NULL;
672         GSList *unwind_ops = NULL;
673 #ifdef USE_JUMP_TABLES
674         gpointer *jte;
675 #endif
676
677         mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
678         index = MONO_RGCTX_SLOT_INDEX (slot);
679         if (mrgctx)
680                 index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
681         for (depth = 0; ; ++depth) {
682                 int size = mono_class_rgctx_get_array_size (depth, mrgctx);
683
684                 if (index < size - 1)
685                         break;
686                 index -= size - 1;
687         }
688
689         tramp_size = 64 + 16 * depth;
690
691         code = buf = mono_global_codeman_reserve (tramp_size);
692
693         unwind_ops = mono_arch_get_cie_program ();
694
695         rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2));
696         njumps = 0;
697
698         /* The vtable/mrgctx is in R0 */
699         g_assert (MONO_ARCH_VTABLE_REG == ARMREG_R0);
700
701         if (mrgctx) {
702                 /* get mrgctx ptr */
703                 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
704         } else {
705                 /* load rgctx ptr from vtable */
706                 g_assert (arm_is_imm12 (MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context)));
707                 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R0, MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context));
708                 /* is the rgctx ptr null? */
709                 ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
710                 /* if yes, jump to actual trampoline */
711                 rgctx_null_jumps [njumps ++] = code;
712                 ARM_B_COND (code, ARMCOND_EQ, 0);
713         }
714
715         for (i = 0; i < depth; ++i) {
716                 /* load ptr to next array */
717                 if (mrgctx && i == 0) {
718                         g_assert (arm_is_imm12 (MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT));
719                         ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R1, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT);
720                 } else {
721                         ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R1, 0);
722                 }
723                 /* is the ptr null? */
724                 ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
725                 /* if yes, jump to actual trampoline */
726                 rgctx_null_jumps [njumps ++] = code;
727                 ARM_B_COND (code, ARMCOND_EQ, 0);
728         }
729
730         /* fetch slot */
731         code = mono_arm_emit_load_imm (code, ARMREG_R2, sizeof (gpointer) * (index + 1));
732         ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_R1, ARMREG_R2);
733         /* is the slot null? */
734         ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
735         /* if yes, jump to actual trampoline */
736         rgctx_null_jumps [njumps ++] = code;
737         ARM_B_COND (code, ARMCOND_EQ, 0);
738         /* otherwise return, result is in R1 */
739         ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_R1);
740         code = emit_bx (code, ARMREG_LR);
741
742         g_assert (njumps <= depth + 2);
743         for (i = 0; i < njumps; ++i)
744                 arm_patch (rgctx_null_jumps [i], code);
745
746         g_free (rgctx_null_jumps);
747
748         /* Slowpath */
749
750         /* The vtable/mrgctx is still in R0 */
751
752         if (aot) {
753                 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", slot));
754                 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
755                 ARM_B (code, 0);
756                 *(gpointer*)code = NULL;
757                 code += 4;
758                 ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_R1);
759         } else {
760                 tramp = mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), &code_len);
761
762                 /* Jump to the actual trampoline */
763 #ifdef USE_JUMP_TABLES
764                 jte = mono_jumptable_add_entry ();
765                 jte [0] = tramp;
766                 code = mono_arm_load_jumptable_entry (code, jte, ARMREG_R1);
767                 code = emit_bx (code, ARMREG_R1);
768 #else
769                 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0); /* temp reg */
770                 code = emit_bx (code, ARMREG_R1);
771                 *(gpointer*)code = tramp;
772                 code += 4;
773 #endif
774         }
775
776         mono_arch_flush_icache (buf, code - buf);
777         mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL);
778
779         g_assert (code - buf <= tramp_size);
780
781         char *name = mono_get_rgctx_fetch_trampoline_name (slot);
782         *info = mono_tramp_info_create (name, buf, code - buf, ji, unwind_ops);
783         g_free (name);
784
785         return buf;
786 }
787
788 gpointer
789 mono_arch_create_general_rgctx_lazy_fetch_trampoline (MonoTrampInfo **info, gboolean aot)
790 {
791         guint8 *code, *buf;
792         int tramp_size;
793         MonoJumpInfo *ji = NULL;
794         GSList *unwind_ops = NULL;
795
796         g_assert (aot);
797
798         tramp_size = 32;
799
800         code = buf = mono_global_codeman_reserve (tramp_size);
801
802         unwind_ops = mono_arch_get_cie_program ();
803
804         // FIXME: Currently, we always go to the slow path.
805         /* Load trampoline addr */
806         ARM_LDR_IMM (code, ARMREG_R1, MONO_ARCH_RGCTX_REG, 4);
807         /* The vtable/mrgctx is in R0 */
808         g_assert (MONO_ARCH_VTABLE_REG == ARMREG_R0);
809         code = emit_bx (code, ARMREG_R1);
810
811         mono_arch_flush_icache (buf, code - buf);
812         mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL);
813
814         g_assert (code - buf <= tramp_size);
815
816         *info = mono_tramp_info_create ("rgctx_fetch_trampoline_general", buf, code - buf, ji, unwind_ops);
817
818         return buf;
819 }
820
821 static gpointer
822 handler_block_trampoline_helper (gpointer *ptr)
823 {
824         MonoJitTlsData *jit_tls = mono_native_tls_get_value (mono_jit_tls_id);
825         return jit_tls->handler_block_return_address;
826 }
827
828 gpointer
829 mono_arch_create_handler_block_trampoline (MonoTrampInfo **info, gboolean aot)
830 {
831         guint8 *tramp;
832         guint8 *code, *buf;
833         int tramp_size = 64;
834         MonoJumpInfo *ji = NULL;
835         GSList *unwind_ops = NULL;
836
837         g_assert (!aot);
838
839         code = buf = mono_global_codeman_reserve (tramp_size);
840
841         unwind_ops = mono_arch_get_cie_program ();
842
843         tramp = mono_arch_create_specific_trampoline (NULL, MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD, NULL, NULL);
844
845         /*
846         This trampoline restore the call chain of the handler block then jumps into the code that deals with it.
847         */
848
849         /*
850          * We are in a method frame after the call emitted by OP_CALL_HANDLER.
851          */
852         /* Obtain jit_tls->handler_block_return_address */
853         ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
854         ARM_B (code, 0);
855         *(gpointer*)code = handler_block_trampoline_helper;
856         code += 4;
857
858         /* Set it as the return address so the trampoline will return to it */
859         ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_R0);
860
861         /* Call the trampoline */
862         ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
863         code = emit_bx (code, ARMREG_R0);
864         *(gpointer*)code = tramp;
865         code += 4;
866
867         mono_arch_flush_icache (buf, code - buf);
868         mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_HELPER, NULL);
869         g_assert (code - buf <= tramp_size);
870
871         *info = mono_tramp_info_create ("handler_block_trampoline", buf, code - buf, ji, unwind_ops);
872
873         return buf;
874 }
875
876 guint8*
877 mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gboolean aot)
878 {
879         guint8 *buf, *code;
880         GSList *unwind_ops = NULL;
881         MonoJumpInfo *ji = NULL;
882         int frame_size;
883
884         buf = code = mono_global_codeman_reserve (96);
885
886         /*
887          * Construct the MonoContext structure on the stack.
888          */
889
890         frame_size = sizeof (MonoContext);
891         frame_size = ALIGN_TO (frame_size, MONO_ARCH_FRAME_ALIGNMENT);
892         ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, frame_size);
893
894         /* save ip, lr and pc into their correspodings ctx.regs slots. */
895         ARM_STR_IMM (code, ARMREG_IP, ARMREG_SP, MONO_STRUCT_OFFSET (MonoContext, regs) + sizeof (mgreg_t) * ARMREG_IP);
896         ARM_STR_IMM (code, ARMREG_LR, ARMREG_SP, MONO_STRUCT_OFFSET (MonoContext, regs) + 4 * ARMREG_LR);
897         ARM_STR_IMM (code, ARMREG_LR, ARMREG_SP, MONO_STRUCT_OFFSET (MonoContext, regs) + 4 * ARMREG_PC);
898
899         /* save r0..r10 and fp */
900         ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_SP, MONO_STRUCT_OFFSET (MonoContext, regs));
901         ARM_STM (code, ARMREG_IP, 0x0fff);
902
903         /* now we can update fp. */
904         ARM_MOV_REG_REG (code, ARMREG_FP, ARMREG_SP);
905
906         /* make ctx.esp hold the actual value of sp at the beginning of this method. */
907         ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_FP, frame_size);
908         ARM_STR_IMM (code, ARMREG_R0, ARMREG_IP, 4 * ARMREG_SP);
909         ARM_STR_IMM (code, ARMREG_R0, ARMREG_FP, MONO_STRUCT_OFFSET (MonoContext, regs) + 4 * ARMREG_SP);
910
911         /* make ctx.eip hold the address of the call. */
912         ARM_SUB_REG_IMM8 (code, ARMREG_LR, ARMREG_LR, 4);
913         ARM_STR_IMM (code, ARMREG_LR, ARMREG_FP, MONO_STRUCT_OFFSET (MonoContext, pc));
914
915         /* r0 now points to the MonoContext */
916         ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_FP);
917
918         /* call */
919         // FIXME: AOT
920 #ifdef USE_JUMP_TABLES
921         {
922                 gpointer *jte = mono_jumptable_add_entry ();
923                 code = mono_arm_load_jumptable_entry (code, jte, ARMREG_IP);
924                 jte [0] = function;
925         }
926 #else
927         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
928         ARM_B (code, 0);
929         if (single_step)
930                 *(gpointer*)code = debugger_agent_single_step_from_context;
931         else
932                 *(gpointer*)code = debugger_agent_breakpoint_from_context;
933         code += 4;
934 #endif
935         ARM_BLX_REG (code, ARMREG_IP);
936
937         /* we're back; save ctx.eip and ctx.esp into the corresponding regs slots. */
938         ARM_LDR_IMM (code, ARMREG_R0, ARMREG_FP, MONO_STRUCT_OFFSET (MonoContext, pc));
939         ARM_STR_IMM (code, ARMREG_R0, ARMREG_FP, MONO_STRUCT_OFFSET (MonoContext, regs) + 4 * ARMREG_LR);
940         ARM_STR_IMM (code, ARMREG_R0, ARMREG_FP, MONO_STRUCT_OFFSET (MonoContext, regs) + 4 * ARMREG_PC);
941
942         /* make ip point to the regs array, then restore everything, including pc. */
943         ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_FP, MONO_STRUCT_OFFSET (MonoContext, regs));
944         ARM_LDM (code, ARMREG_IP, 0xffff);
945
946         mono_arch_flush_icache (buf, code - buf);
947         mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_HELPER, NULL);
948
949         const char *tramp_name = single_step ? "sdb_single_step_trampoline" : "sdb_breakpoint_trampoline";
950         *info = mono_tramp_info_create (tramp_name, buf, code - buf, ji, unwind_ops);
951
952         return buf;
953 }
954
955 #else
956
957 guchar*
958 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
959 {
960         g_assert_not_reached ();
961         return NULL;
962 }
963
964 gpointer
965 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
966 {
967         g_assert_not_reached ();
968         return NULL;
969 }
970
971 gpointer
972 mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
973 {
974         g_assert_not_reached ();
975         return NULL;
976 }
977
978 gpointer
979 mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr)
980 {
981         g_assert_not_reached ();
982         return NULL;
983 }
984
985 gpointer
986 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
987 {
988         g_assert_not_reached ();
989         return NULL;
990 }
991
992 gpointer
993 mono_arch_create_handler_block_trampoline (MonoTrampInfo **info, gboolean aot)
994 {
995         g_assert_not_reached ();
996         return NULL;
997 }
998
999 guint8*
1000 mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gboolean aot)
1001 {
1002         g_assert_not_reached ();
1003         return NULL;
1004 }
1005         
1006 #endif /* DISABLE_JIT */
1007
1008 guint8*
1009 mono_arch_get_call_target (guint8 *code)
1010 {
1011         guint32 ins = ((guint32*)(gpointer)code) [-1];
1012
1013 #if MONOTOUCH
1014         /* Should be a 'bl' or a 'b' */
1015         if (((ins >> 25) & 0x7) == 0x5) {
1016 #else
1017         /* Should be a 'bl' */
1018         if ((((ins >> 25) & 0x7) == 0x5) && (((ins >> 24) & 0x1) == 0x1)) {
1019 #endif
1020                 gint32 disp = ((((gint32)ins) & 0xffffff) << 8) >> 8;
1021                 guint8 *target = code - 4 + 8 + (disp * 4);
1022
1023                 return target;
1024         } else {
1025                 return NULL;
1026         }
1027 }
1028
1029 guint32
1030 mono_arch_get_plt_info_offset (guint8 *plt_entry, mgreg_t *regs, guint8 *code)
1031 {
1032         /* The offset is stored as the 4th word of the plt entry */
1033         return ((guint32*)plt_entry) [3];
1034 }
1035
1036 /*
1037  * Return the address of the PLT entry called by the thumb code CODE.
1038  */
1039 guint8*
1040 mono_arm_get_thumb_plt_entry (guint8 *code)
1041 {
1042         int s, j1, j2, imm10, imm11, i1, i2, imm32;
1043         guint8 *bl, *base;
1044         guint16 t1, t2;
1045         guint8 *target;
1046
1047         /* code should be right after a BL */
1048         code = (guint8*)((mgreg_t)code & ~1);
1049         base = (guint8*)((mgreg_t)code & ~3);
1050         bl = code - 4;
1051         t1 = ((guint16*)bl) [0];
1052         t2 = ((guint16*)bl) [1];
1053
1054         g_assert ((t1 >> 11) == 0x1e);
1055
1056         s = (t1 >> 10) & 0x1;
1057         imm10 = (t1 >> 0) & 0x3ff;
1058         j1 = (t2 >> 13) & 0x1;
1059         j2 = (t2 >> 11) & 0x1;
1060         imm11 = t2 & 0x7ff;
1061
1062         i1 = (s ^ j1) ? 0 : 1;
1063         i2 = (s ^ j2) ? 0 : 1;
1064
1065         imm32 = (imm11 << 1) | (imm10 << 12) | (i2 << 22) | (i1 << 23);
1066         if (s)
1067                 /* Sign extend from 24 bits to 32 bits */
1068                 imm32 = ((gint32)imm32 << 8) >> 8;
1069
1070         target = code + imm32;
1071
1072         /* target now points to the thumb plt entry */
1073         /* ldr.w r12, [pc, #8] */
1074         g_assert (((guint16*)target) [0] == 0xf8df);
1075         g_assert (((guint16*)target) [1] == 0xc008);
1076
1077         /* 
1078          * The PLT info offset is at offset 16, but mono_arch_get_plt_entry_offset () returns
1079          * the 3rd word, so compensate by returning a different value.
1080          */
1081         target += 4;
1082
1083         return target;
1084 }
1085
1086 #ifndef DISABLE_JIT
1087
1088 /*
1089  * mono_arch_get_gsharedvt_arg_trampoline:
1090  *
1091  *   See tramp-x86.c for documentation.
1092  */
1093 gpointer
1094 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
1095 {
1096         guint8 *code, *buf;
1097         int buf_len;
1098         gpointer *constants;
1099
1100         buf_len = 24;
1101
1102         buf = code = mono_domain_code_reserve (domain, buf_len);
1103
1104         /* Similar to the specialized trampoline code */
1105         ARM_PUSH (code, (1 << ARMREG_R0) | (1 << ARMREG_R1) | (1 << ARMREG_R2) | (1 << ARMREG_R3) | (1 << ARMREG_LR));
1106         ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 8);
1107         /* arg is passed in LR */
1108         ARM_LDR_IMM (code, ARMREG_LR, ARMREG_PC, 0);
1109         code = emit_bx (code, ARMREG_IP);
1110         constants = (gpointer*)code;
1111         constants [0] = arg;
1112         constants [1] = addr;
1113         code += 8;
1114
1115         g_assert ((code - buf) <= buf_len);
1116
1117         nacl_domain_code_validate (domain, &buf, buf_len, &code);
1118         mono_arch_flush_icache (buf, code - buf);
1119         mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL);
1120
1121         mono_tramp_info_register (mono_tramp_info_create (NULL, buf, code - buf, NULL, NULL), domain);
1122
1123         return buf;
1124 }
1125
1126 #else
1127
1128 gpointer
1129 mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
1130 {
1131         g_assert_not_reached ();
1132         return NULL;
1133 }
1134
1135 #endif
1136
1137 #if defined(ENABLE_GSHAREDVT)
1138
1139 #include "../../../mono-extensions/mono/mini/tramp-arm-gsharedvt.c"
1140
1141 #else
1142
1143 gpointer
1144 mono_arm_start_gsharedvt_call (GSharedVtCallInfo *info, gpointer *caller, gpointer *callee, gpointer mrgctx_reg)
1145 {
1146         g_assert_not_reached ();
1147         return NULL;
1148 }
1149
1150 gpointer
1151 mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
1152 {
1153         *info = NULL;
1154         return NULL;
1155 }
1156
1157 #endif /* !MONOTOUCH */