2 * tramp-arm.c: JIT trampoline code for ARM
5 * Paolo Molaro (lupus@ximian.com)
7 * (C) 2001-2003 Ximian, Inc.
8 * Copyright 2003-2011 Novell Inc
9 * Copyright 2011 Xamarin Inc
15 #include <mono/metadata/appdomain.h>
16 #include <mono/metadata/marshal.h>
17 #include <mono/metadata/tabledefs.h>
18 #include <mono/arch/arm/arm-codegen.h>
23 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
25 static guint8
* nullified_class_init_trampoline
;
28 mono_arch_patch_callsite (guint8
*method_start
, guint8
*code_ptr
, guint8
*addr
)
30 guint32
*code
= (guint32
*)code_ptr
;
32 /* This is the 'bl' or the 'mov pc' instruction */
36 * Note that methods are called also with the bl opcode.
38 if ((((*code
) >> 25) & 7) == 5) {
39 /*g_print ("direct patching\n");*/
40 arm_patch ((guint8
*)code
, addr
);
41 mono_arch_flush_icache ((guint8
*)code
, 4);
45 if ((((*code
) >> 20) & 0xFF) == 0x12) {
46 /*g_print ("patching bx\n");*/
47 arm_patch ((guint8
*)code
, addr
);
48 mono_arch_flush_icache ((guint8
*)(code
- 2), 4);
52 g_assert_not_reached ();
56 mono_arch_patch_plt_entry (guint8
*code
, gpointer
*got
, mgreg_t
*regs
, guint8
*addr
)
60 /* Patch the jump table entry used by the plt entry */
61 if (*(guint32
*)code
== 0xe59fc000) {
62 /* ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0); */
63 guint32 offset
= ((guint32
*)code
)[2];
65 jump_entry
= code
+ offset
+ 12;
66 } else if (*(guint16
*)(code
- 4) == 0xf8df) {
68 * Thumb PLT entry, begins with ldr.w ip, [pc, #8], code points to entry + 4, see
69 * mono_arm_get_thumb_plt_entry ().
74 offset
= *(guint32
*)(code
+ 12);
75 jump_entry
= code
+ offset
+ 8;
77 g_assert_not_reached ();
80 *(guint8
**)jump_entry
= addr
;
84 mono_arch_nullify_class_init_trampoline (guint8
*code
, mgreg_t
*regs
)
86 mono_arch_patch_callsite (NULL
, code
, nullified_class_init_trampoline
);
90 mono_arch_nullify_plt_entry (guint8
*code
, mgreg_t
*regs
)
92 if (mono_aot_only
&& !nullified_class_init_trampoline
)
93 nullified_class_init_trampoline
= mono_aot_get_trampoline ("nullified_class_init_trampoline");
95 mono_arch_patch_plt_entry (code
, NULL
, regs
, nullified_class_init_trampoline
);
100 #define arm_is_imm12(v) ((int)(v) > -4096 && (int)(v) < 4096)
103 * Return the instruction to jump from code to target, 0 if not
104 * reachable with a single instruction
107 branch_for_target_reachable (guint8
*branch
, guint8
*target
)
109 gint diff
= target
- branch
- 8;
110 g_assert ((diff
& 3) == 0);
112 if (diff
<= 33554431)
113 return (ARMCOND_AL
<< ARMCOND_SHIFT
) | (ARM_BR_TAG
) | (diff
>> 2);
115 /* diff between 0 and -33554432 */
116 if (diff
>= -33554432)
117 return (ARMCOND_AL
<< ARMCOND_SHIFT
) | (ARM_BR_TAG
) | ((diff
>> 2) & ~0xff000000);
122 static inline guint8
*
123 emit_bx (guint8
* code
, int reg
)
125 if (mono_arm_thumb_supported ())
128 ARM_MOV_REG_REG (code
, ARMREG_PC
, reg
);
132 /* Stack size for trampoline function
134 #define STACK ALIGN_TO (sizeof (MonoLMF), 8)
136 /* Method-specific trampoline code fragment size */
137 #define METHOD_TRAMPOLINE_SIZE 64
139 /* Jump-specific trampoline code fragment size */
140 #define JUMP_TRAMPOLINE_SIZE 64
143 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type
, MonoTrampInfo
**info
, gboolean aot
)
145 guint8
*buf
, *code
= NULL
;
146 guint8
*load_get_lmf_addr
, *load_trampoline
;
148 int cfa_offset
, lmf_offset
, regsave_size
, lr_offset
;
149 GSList
*unwind_ops
= NULL
;
150 MonoJumpInfo
*ji
= NULL
;
153 /* Now we'll create in 'buf' the ARM trampoline code. This
154 is the trampoline code common to all methods */
157 code
= buf
= mono_global_codeman_reserve (buf_len
);
160 * At this point lr points to the specific arg and sp points to the saved
161 * regs on the stack (all but PC and SP). The original LR value has been
162 * saved as sp + LR_OFFSET by the push in the specific trampoline
165 /* The offset of lmf inside the stack frame */
166 lmf_offset
= STACK
- sizeof (MonoLMF
);
167 /* The size of the area already allocated by the push in the specific trampoline */
168 regsave_size
= 14 * sizeof (mgreg_t
);
169 /* The offset where lr was saved inside the regsave area */
170 lr_offset
= 13 * sizeof (mgreg_t
);
172 // FIXME: Finish the unwind info, the current info allows us to unwind
173 // when the trampoline is not in the epilog
175 // CFA = SP + (num registers pushed) * 4
176 cfa_offset
= 14 * sizeof (mgreg_t
);
177 mono_add_unwind_op_def_cfa (unwind_ops
, code
, buf
, ARMREG_SP
, cfa_offset
);
178 // PC saved at sp+LR_OFFSET
179 mono_add_unwind_op_offset (unwind_ops
, code
, buf
, ARMREG_LR
, -4);
181 if (aot
&& tramp_type
!= MONO_TRAMPOLINE_GENERIC_CLASS_INIT
) {
183 * The trampoline contains a pc-relative offset to the got slot
184 * preceeding the got slot where the value is stored. The offset can be
187 ARM_LDR_IMM (code
, ARMREG_V2
, ARMREG_LR
, 0);
188 ARM_ADD_REG_IMM (code
, ARMREG_V2
, ARMREG_V2
, 4, 0);
189 ARM_LDR_REG_REG (code
, ARMREG_V2
, ARMREG_V2
, ARMREG_LR
);
191 if (tramp_type
!= MONO_TRAMPOLINE_GENERIC_CLASS_INIT
)
192 ARM_LDR_IMM (code
, ARMREG_V2
, ARMREG_LR
, 0);
194 ARM_MOV_REG_REG (code
, ARMREG_V2
, MONO_ARCH_VTABLE_REG
);
196 ARM_LDR_IMM (code
, ARMREG_V3
, ARMREG_SP
, lr_offset
);
198 /* ok, now we can continue with the MonoLMF setup, mostly untouched
199 * from emit_prolog in mini-arm.c
200 * This is a synthetized call to mono_get_lmf_addr ()
203 ji
= mono_patch_info_list_prepend (ji
, code
- buf
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, "mono_get_lmf_addr");
204 ARM_LDR_IMM (code
, ARMREG_R0
, ARMREG_PC
, 0);
206 *(gpointer
*)code
= NULL
;
208 ARM_LDR_REG_REG (code
, ARMREG_R0
, ARMREG_PC
, ARMREG_R0
);
210 load_get_lmf_addr
= code
;
213 ARM_MOV_REG_REG (code
, ARMREG_LR
, ARMREG_PC
);
214 code
= emit_bx (code
, ARMREG_R0
);
216 /* we build the MonoLMF structure on the stack - see mini-arm.h
217 * The pointer to the struct is put in r1.
218 * the iregs array is already allocated on the stack by push.
220 ARM_SUB_REG_IMM8 (code
, ARMREG_SP
, ARMREG_SP
, STACK
- regsave_size
);
221 cfa_offset
+= STACK
- regsave_size
;
222 mono_add_unwind_op_def_cfa_offset (unwind_ops
, code
, buf
, cfa_offset
);
224 ARM_ADD_REG_IMM8 (code
, ARMREG_V1
, ARMREG_SP
, STACK
- sizeof (MonoLMF
));
227 * The stack now looks like:
229 * v1 -> <rest of LMF>
233 /* r0 is the result from mono_get_lmf_addr () */
234 ARM_STR_IMM (code
, ARMREG_R0
, ARMREG_V1
, G_STRUCT_OFFSET (MonoLMF
, lmf_addr
));
235 /* new_lmf->previous_lmf = *lmf_addr */
236 ARM_LDR_IMM (code
, ARMREG_R2
, ARMREG_R0
, G_STRUCT_OFFSET (MonoLMF
, previous_lmf
));
237 ARM_STR_IMM (code
, ARMREG_R2
, ARMREG_V1
, G_STRUCT_OFFSET (MonoLMF
, previous_lmf
));
238 /* *(lmf_addr) = r1 */
239 ARM_STR_IMM (code
, ARMREG_V1
, ARMREG_R0
, G_STRUCT_OFFSET (MonoLMF
, previous_lmf
));
240 /* save method info (it's in v2) */
241 if ((tramp_type
== MONO_TRAMPOLINE_JIT
) || (tramp_type
== MONO_TRAMPOLINE_JUMP
))
242 ARM_STR_IMM (code
, ARMREG_V2
, ARMREG_V1
, G_STRUCT_OFFSET (MonoLMF
, method
));
244 ARM_MOV_REG_IMM8 (code
, ARMREG_R2
, 0);
245 ARM_STR_IMM (code
, ARMREG_R2
, ARMREG_V1
, G_STRUCT_OFFSET (MonoLMF
, method
));
248 ARM_ADD_REG_IMM8 (code
, ARMREG_R2
, ARMREG_SP
, cfa_offset
);
249 ARM_STR_IMM (code
, ARMREG_R2
, ARMREG_V1
, G_STRUCT_OFFSET (MonoLMF
, sp
));
251 ARM_LDR_IMM (code
, ARMREG_R2
, ARMREG_V1
, (G_STRUCT_OFFSET (MonoLMF
, iregs
) + ARMREG_FP
*4));
252 ARM_STR_IMM (code
, ARMREG_R2
, ARMREG_V1
, G_STRUCT_OFFSET (MonoLMF
, fp
));
253 /* save the IP (caller ip) */
254 if (tramp_type
== MONO_TRAMPOLINE_JUMP
) {
255 ARM_MOV_REG_IMM8 (code
, ARMREG_R2
, 0);
257 ARM_LDR_IMM (code
, ARMREG_R2
, ARMREG_V1
, (G_STRUCT_OFFSET (MonoLMF
, iregs
) + 13*4));
259 ARM_STR_IMM (code
, ARMREG_R2
, ARMREG_V1
, G_STRUCT_OFFSET (MonoLMF
, ip
));
262 * Now we're ready to call xxx_trampoline ().
264 /* Arg 1: the saved registers */
265 ARM_ADD_REG_IMM8 (code
, ARMREG_R0
, ARMREG_V1
, G_STRUCT_OFFSET (MonoLMF
, iregs
));
267 /* Arg 2: code (next address to the instruction that called us) */
268 if (tramp_type
== MONO_TRAMPOLINE_JUMP
) {
269 ARM_MOV_REG_IMM8 (code
, ARMREG_R1
, 0);
271 ARM_MOV_REG_REG (code
, ARMREG_R1
, ARMREG_V3
);
274 /* Arg 3: the specific argument, stored in v2
276 ARM_MOV_REG_REG (code
, ARMREG_R2
, ARMREG_V2
);
279 char *icall_name
= g_strdup_printf ("trampoline_func_%d", tramp_type
);
280 ji
= mono_patch_info_list_prepend (ji
, code
- buf
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, icall_name
);
281 ARM_LDR_IMM (code
, ARMREG_IP
, ARMREG_PC
, 0);
283 *(gpointer
*)code
= NULL
;
285 ARM_LDR_REG_REG (code
, ARMREG_IP
, ARMREG_PC
, ARMREG_IP
);
287 load_trampoline
= code
;
291 ARM_MOV_REG_REG (code
, ARMREG_LR
, ARMREG_PC
);
292 code
= emit_bx (code
, ARMREG_IP
);
294 /* OK, code address is now on r0. Move it to the place on the stack
295 * where IP was saved (it is now no more useful to us and it can be
296 * clobbered). This way we can just restore all the regs in one inst
299 ARM_STR_IMM (code
, ARMREG_R0
, ARMREG_V1
, G_STRUCT_OFFSET (MonoLMF
, iregs
) + (ARMREG_R12
* sizeof (mgreg_t
)));
301 /* Check for thread interruption */
302 /* This is not perf critical code so no need to check the interrupt flag */
304 * Have to call the _force_ variant, since there could be a protected wrapper on the top of the stack.
307 ji
= mono_patch_info_list_prepend (ji
, code
- buf
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, "mono_thread_force_interruption_checkpoint");
308 ARM_LDR_IMM (code
, ARMREG_IP
, ARMREG_PC
, 0);
310 *(gpointer
*)code
= NULL
;
312 ARM_LDR_REG_REG (code
, ARMREG_IP
, ARMREG_PC
, ARMREG_IP
);
314 ARM_LDR_IMM (code
, ARMREG_IP
, ARMREG_PC
, 0);
316 *(gpointer
*)code
= mono_thread_force_interruption_checkpoint
;
319 ARM_MOV_REG_REG (code
, ARMREG_LR
, ARMREG_PC
);
320 code
= emit_bx (code
, ARMREG_IP
);
323 * Now we restore the MonoLMF (see emit_epilogue in mini-arm.c)
324 * and the rest of the registers, so the method called will see
325 * the same state as before we executed.
327 /* ip = previous_lmf */
328 ARM_LDR_IMM (code
, ARMREG_IP
, ARMREG_V1
, G_STRUCT_OFFSET (MonoLMF
, previous_lmf
));
330 ARM_LDR_IMM (code
, ARMREG_LR
, ARMREG_V1
, G_STRUCT_OFFSET (MonoLMF
, lmf_addr
));
331 /* *(lmf_addr) = previous_lmf */
332 ARM_STR_IMM (code
, ARMREG_IP
, ARMREG_LR
, G_STRUCT_OFFSET (MonoLMF
, previous_lmf
));
334 /* Non-standard function epilogue. Instead of doing a proper
335 * return, we just jump to the compiled code.
337 /* Restore the registers and jump to the code:
338 * Note that IP has been conveniently set to the method addr.
340 ARM_ADD_REG_IMM8 (code
, ARMREG_SP
, ARMREG_SP
, STACK
- regsave_size
);
341 ARM_POP_NWB (code
, 0x5fff);
342 if (tramp_type
== MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
)
343 ARM_MOV_REG_REG (code
, ARMREG_R0
, ARMREG_IP
);
344 ARM_ADD_REG_IMM8 (code
, ARMREG_SP
, ARMREG_SP
, regsave_size
);
345 if ((tramp_type
== MONO_TRAMPOLINE_CLASS_INIT
) || (tramp_type
== MONO_TRAMPOLINE_GENERIC_CLASS_INIT
) || (tramp_type
== MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
))
346 code
= emit_bx (code
, ARMREG_LR
);
348 code
= emit_bx (code
, ARMREG_IP
);
350 constants
= (gpointer
*)code
;
351 constants
[0] = mono_get_lmf_addr
;
352 constants
[1] = (gpointer
)mono_get_trampoline_func (tramp_type
);
355 /* backpatch by emitting the missing instructions skipped above */
356 ARM_LDR_IMM (load_get_lmf_addr
, ARMREG_R0
, ARMREG_PC
, (code
- load_get_lmf_addr
- 8));
357 ARM_LDR_IMM (load_trampoline
, ARMREG_IP
, ARMREG_PC
, (code
+ 4 - load_trampoline
- 8));
362 /* Flush instruction cache, since we've generated code */
363 mono_arch_flush_icache (buf
, code
- buf
);
366 g_assert ((code
- buf
) <= buf_len
);
368 if (tramp_type
== MONO_TRAMPOLINE_CLASS_INIT
)
369 /* Initialize the nullified class init trampoline used in the AOT case */
370 nullified_class_init_trampoline
= mono_arch_get_nullified_class_init_trampoline (NULL
);
373 *info
= mono_tramp_info_create (mono_get_generic_trampoline_name (tramp_type
), buf
, code
- buf
, ji
, unwind_ops
);
379 mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo
**info
)
383 code
= buf
= mono_global_codeman_reserve (16);
385 code
= emit_bx (code
, ARMREG_LR
);
387 mono_arch_flush_icache (buf
, code
- buf
);
390 *info
= mono_tramp_info_create (g_strdup_printf ("nullified_class_init_trampoline"), buf
, code
- buf
, NULL
, NULL
);
395 #define SPEC_TRAMP_SIZE 24
398 mono_arch_create_specific_trampoline (gpointer arg1
, MonoTrampolineType tramp_type
, MonoDomain
*domain
, guint32
*code_len
)
400 guint8
*code
, *buf
, *tramp
;
402 guint32 short_branch
, size
= SPEC_TRAMP_SIZE
;
404 tramp
= mono_get_trampoline_code (tramp_type
);
406 mono_domain_lock (domain
);
407 code
= buf
= mono_domain_code_reserve_align (domain
, size
, 4);
408 if ((short_branch
= branch_for_target_reachable (code
+ 4, tramp
))) {
410 mono_domain_code_commit (domain
, code
, SPEC_TRAMP_SIZE
, size
);
412 mono_domain_unlock (domain
);
414 /* we could reduce this to 12 bytes if tramp is within reach:
418 * The called code can access method using the lr register
419 * A 20 byte sequence could be:
421 * ARM_MOV_REG_REG (lr, pc)
422 * ARM_LDR_IMM (pc, pc, 0)
426 /* We save all the registers, except PC and SP */
427 ARM_PUSH (code
, 0x5fff);
429 constants
= (gpointer
*)code
;
430 constants
[0] = GUINT_TO_POINTER (short_branch
| (1 << 24));
431 constants
[1] = arg1
;
434 ARM_LDR_IMM (code
, ARMREG_R1
, ARMREG_PC
, 8); /* temp reg */
435 ARM_MOV_REG_REG (code
, ARMREG_LR
, ARMREG_PC
);
436 code
= emit_bx (code
, ARMREG_R1
);
438 constants
= (gpointer
*)code
;
439 constants
[0] = arg1
;
440 constants
[1] = tramp
;
444 /* Flush instruction cache, since we've generated code */
445 mono_arch_flush_icache (buf
, code
- buf
);
447 g_assert ((code
- buf
) <= size
);
450 *code_len
= code
- buf
;
456 * mono_arch_get_unbox_trampoline:
458 * @addr: pointer to native code for @m
460 * when value type methods are called through the vtable we need to unbox the
461 * this argument. This method returns a pointer to a trampoline which does
462 * unboxing before calling the method
465 mono_arch_get_unbox_trampoline (MonoMethod
*m
, gpointer addr
)
467 guint8
*code
, *start
;
468 MonoDomain
*domain
= mono_domain_get ();
470 start
= code
= mono_domain_code_reserve (domain
, 16);
472 ARM_LDR_IMM (code
, ARMREG_IP
, ARMREG_PC
, 4);
473 ARM_ADD_REG_IMM8 (code
, ARMREG_R0
, ARMREG_R0
, sizeof (MonoObject
));
474 code
= emit_bx (code
, ARMREG_IP
);
475 *(guint32
*)code
= (guint32
)addr
;
477 mono_arch_flush_icache (start
, code
- start
);
478 g_assert ((code
- start
) <= 16);
479 /*g_print ("unbox trampoline at %d for %s:%s\n", this_pos, m->klass->name, m->name);
480 g_print ("unbox code is at %p for method at %p\n", start, addr);*/
486 mono_arch_get_static_rgctx_trampoline (MonoMethod
*m
, MonoMethodRuntimeGenericContext
*mrgctx
, gpointer addr
)
488 guint8
*code
, *start
;
491 MonoDomain
*domain
= mono_domain_get ();
495 start
= code
= mono_domain_code_reserve (domain
, buf_len
);
497 ARM_LDR_IMM (code
, MONO_ARCH_RGCTX_REG
, ARMREG_PC
, 0);
498 ARM_LDR_IMM (code
, ARMREG_PC
, ARMREG_PC
, 0);
499 *(guint32
*)code
= (guint32
)mrgctx
;
501 *(guint32
*)code
= (guint32
)addr
;
504 g_assert ((code
- start
) <= buf_len
);
506 mono_arch_flush_icache (start
, code
- start
);
512 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot
, MonoTrampInfo
**info
, gboolean aot
)
518 guint8
**rgctx_null_jumps
;
522 MonoJumpInfo
*ji
= NULL
;
523 GSList
*unwind_ops
= NULL
;
525 mrgctx
= MONO_RGCTX_SLOT_IS_MRGCTX (slot
);
526 index
= MONO_RGCTX_SLOT_INDEX (slot
);
528 index
+= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT
/ sizeof (gpointer
);
529 for (depth
= 0; ; ++depth
) {
530 int size
= mono_class_rgctx_get_array_size (depth
, mrgctx
);
532 if (index
< size
- 1)
537 tramp_size
= 64 + 16 * depth
;
539 code
= buf
= mono_global_codeman_reserve (tramp_size
);
541 mono_add_unwind_op_def_cfa (unwind_ops
, code
, buf
, ARMREG_SP
, 0);
543 rgctx_null_jumps
= g_malloc (sizeof (guint8
*) * (depth
+ 2));
546 /* The vtable/mrgctx is in R0 */
547 g_assert (MONO_ARCH_VTABLE_REG
== ARMREG_R0
);
551 ARM_MOV_REG_REG (code
, ARMREG_R1
, ARMREG_R0
);
553 /* load rgctx ptr from vtable */
554 g_assert (arm_is_imm12 (G_STRUCT_OFFSET (MonoVTable
, runtime_generic_context
)));
555 ARM_LDR_IMM (code
, ARMREG_R1
, ARMREG_R0
, G_STRUCT_OFFSET (MonoVTable
, runtime_generic_context
));
556 /* is the rgctx ptr null? */
557 ARM_CMP_REG_IMM (code
, ARMREG_R1
, 0, 0);
558 /* if yes, jump to actual trampoline */
559 rgctx_null_jumps
[njumps
++] = code
;
560 ARM_B_COND (code
, ARMCOND_EQ
, 0);
563 for (i
= 0; i
< depth
; ++i
) {
564 /* load ptr to next array */
565 if (mrgctx
&& i
== 0) {
566 g_assert (arm_is_imm12 (MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT
));
567 ARM_LDR_IMM (code
, ARMREG_R1
, ARMREG_R1
, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT
);
569 ARM_LDR_IMM (code
, ARMREG_R1
, ARMREG_R1
, 0);
571 /* is the ptr null? */
572 ARM_CMP_REG_IMM (code
, ARMREG_R1
, 0, 0);
573 /* if yes, jump to actual trampoline */
574 rgctx_null_jumps
[njumps
++] = code
;
575 ARM_B_COND (code
, ARMCOND_EQ
, 0);
579 code
= mono_arm_emit_load_imm (code
, ARMREG_R2
, sizeof (gpointer
) * (index
+ 1));
580 ARM_LDR_REG_REG (code
, ARMREG_R1
, ARMREG_R1
, ARMREG_R2
);
581 /* is the slot null? */
582 ARM_CMP_REG_IMM (code
, ARMREG_R1
, 0, 0);
583 /* if yes, jump to actual trampoline */
584 rgctx_null_jumps
[njumps
++] = code
;
585 ARM_B_COND (code
, ARMCOND_EQ
, 0);
586 /* otherwise return, result is in R1 */
587 ARM_MOV_REG_REG (code
, ARMREG_R0
, ARMREG_R1
);
588 code
= emit_bx (code
, ARMREG_LR
);
590 g_assert (njumps
<= depth
+ 2);
591 for (i
= 0; i
< njumps
; ++i
)
592 arm_patch (rgctx_null_jumps
[i
], code
);
594 g_free (rgctx_null_jumps
);
598 /* The vtable/mrgctx is still in R0 */
601 ji
= mono_patch_info_list_prepend (ji
, code
- buf
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", slot
));
602 ARM_LDR_IMM (code
, ARMREG_R1
, ARMREG_PC
, 0);
604 *(gpointer
*)code
= NULL
;
606 ARM_LDR_REG_REG (code
, ARMREG_PC
, ARMREG_PC
, ARMREG_R1
);
608 tramp
= mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot
), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH
, mono_get_root_domain (), &code_len
);
610 /* Jump to the actual trampoline */
611 ARM_LDR_IMM (code
, ARMREG_R1
, ARMREG_PC
, 0); /* temp reg */
612 code
= emit_bx (code
, ARMREG_R1
);
613 *(gpointer
*)code
= tramp
;
617 mono_arch_flush_icache (buf
, code
- buf
);
619 g_assert (code
- buf
<= tramp_size
);
622 *info
= mono_tramp_info_create (mono_get_rgctx_fetch_trampoline_name (slot
), buf
, code
- buf
, ji
, unwind_ops
);
627 #define arm_is_imm8(v) ((v) > -256 && (v) < 256)
630 mono_arch_create_generic_class_init_trampoline (MonoTrampInfo
**info
, gboolean aot
)
634 static int byte_offset
= -1;
635 static guint8 bitmask
;
638 guint32 code_len
, imm8
;
640 GSList
*unwind_ops
= NULL
;
641 MonoJumpInfo
*ji
= NULL
;
645 code
= buf
= mono_global_codeman_reserve (tramp_size
);
648 mono_marshal_find_bitfield_offset (MonoVTable
, initialized
, &byte_offset
, &bitmask
);
650 g_assert (arm_is_imm8 (byte_offset
));
651 ARM_LDRSB_IMM (code
, ARMREG_IP
, MONO_ARCH_VTABLE_REG
, byte_offset
);
652 imm8
= mono_arm_is_rotated_imm8 (bitmask
, &rot_amount
);
653 g_assert (imm8
>= 0);
654 ARM_AND_REG_IMM (code
, ARMREG_IP
, ARMREG_IP
, imm8
, rot_amount
);
655 ARM_CMP_REG_IMM (code
, ARMREG_IP
, 0, 0);
657 ARM_B_COND (code
, ARMCOND_EQ
, 0);
659 /* Initialized case */
660 ARM_MOV_REG_REG (code
, ARMREG_PC
, ARMREG_LR
);
662 /* Uninitialized case */
663 arm_patch (jump
, code
);
666 ji
= mono_patch_info_list_prepend (ji
, code
- buf
, MONO_PATCH_INFO_JIT_ICALL_ADDR
, "specific_trampoline_generic_class_init");
667 ARM_LDR_IMM (code
, ARMREG_R1
, ARMREG_PC
, 0);
669 *(gpointer
*)code
= NULL
;
671 ARM_LDR_REG_REG (code
, ARMREG_PC
, ARMREG_PC
, ARMREG_R1
);
673 tramp
= mono_arch_create_specific_trampoline (NULL
, MONO_TRAMPOLINE_GENERIC_CLASS_INIT
, mono_get_root_domain (), &code_len
);
675 /* Jump to the actual trampoline */
676 ARM_LDR_IMM (code
, ARMREG_R1
, ARMREG_PC
, 0); /* temp reg */
677 code
= emit_bx (code
, ARMREG_R1
);
678 *(gpointer
*)code
= tramp
;
682 mono_arch_flush_icache (buf
, code
- buf
);
684 g_assert (code
- buf
<= tramp_size
);
687 *info
= mono_tramp_info_create (g_strdup_printf ("generic_class_init_trampoline"), buf
, code
- buf
, ji
, unwind_ops
);
695 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type
, MonoTrampInfo
**info
, gboolean aot
)
697 g_assert_not_reached ();
702 mono_arch_create_specific_trampoline (gpointer arg1
, MonoTrampolineType tramp_type
, MonoDomain
*domain
, guint32
*code_len
)
704 g_assert_not_reached ();
709 mono_arch_get_unbox_trampoline (MonoMethod
*m
, gpointer addr
)
711 g_assert_not_reached ();
716 mono_arch_get_static_rgctx_trampoline (MonoMethod
*m
, MonoMethodRuntimeGenericContext
*mrgctx
, gpointer addr
)
718 g_assert_not_reached ();
723 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot
, MonoTrampInfo
**info
, gboolean aot
)
725 g_assert_not_reached ();
730 mono_arch_create_generic_class_init_trampoline (MonoTrampInfo
**info
, gboolean aot
)
732 g_assert_not_reached ();
736 #endif /* DISABLE_JIT */
739 mono_arch_get_call_target (guint8
*code
)
741 guint32 ins
= ((guint32
*)(gpointer
)code
) [-1];
743 /* Should be a 'bl' */
744 if ((((ins
>> 25) & 0x7) == 0x5) && (((ins
>> 24) & 0x1) == 0x1)) {
745 gint32 disp
= ((gint32
)ins
) & 0xffffff;
746 guint8
*target
= code
- 4 + 8 + (disp
* 4);
755 mono_arch_get_plt_info_offset (guint8
*plt_entry
, mgreg_t
*regs
, guint8
*code
)
757 /* The offset is stored as the 4th word of the plt entry */
758 return ((guint32
*)plt_entry
) [3];
762 * Return the address of the PLT entry called by the thumb code CODE.
765 mono_arm_get_thumb_plt_entry (guint8
*code
)
767 int s
, j1
, j2
, imm10
, imm11
, i1
, i2
, imm32
;
772 /* code should be right after a BL */
773 code
= (guint8
*)((mgreg_t
)code
& ~1);
774 base
= (guint8
*)((mgreg_t
)code
& ~3);
776 t1
= ((guint16
*)bl
) [0];
777 t2
= ((guint16
*)bl
) [1];
779 g_assert ((t1
>> 11) == 0x1e);
781 s
= (t1
>> 10) & 0x1;
782 imm10
= (t1
>> 0) & 0x3ff;
783 j1
= (t2
>> 13) & 0x1;
784 j2
= (t2
>> 11) & 0x1;
787 i1
= (s
^ j1
) ? 0 : 1;
788 i2
= (s
^ j2
) ? 0 : 1;
790 imm32
= (imm11
<< 1) | (imm10
<< 12) | (i2
<< 22) | (i1
<< 23);
794 target
= code
+ imm32
;
796 /* target now points to the thumb plt entry */
797 /* ldr.w r12, [pc, #8] */
798 g_assert (((guint16
*)target
) [0] == 0xf8df);
799 g_assert (((guint16
*)target
) [1] == 0xc008);
802 * The PLT info offset is at offset 16, but mono_arch_get_plt_entry_offset () returns
803 * the 3rd word, so compensate by returning a different value.