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