2 * tramp-arm.c: JIT trampoline code for ARM
5 * Paolo Molaro (lupus@ximian.com)
7 * (C) 2001 Ximian, Inc.
13 #include <mono/metadata/appdomain.h>
14 #include <mono/metadata/marshal.h>
15 #include <mono/metadata/tabledefs.h>
16 #include <mono/arch/arm/arm-codegen.h>
21 static guint8* nullified_class_init_trampoline;
24 mono_arch_patch_callsite (guint8 *method_start, guint8 *code_ptr, guint8 *addr)
26 guint32 *code = (guint32*)code_ptr;
28 /* This is the 'bl' or the 'mov pc' instruction */
32 * Note that methods are called also with the bl opcode.
34 if ((((*code) >> 25) & 7) == 5) {
35 /*g_print ("direct patching\n");*/
36 arm_patch ((guint8*)code, addr);
37 mono_arch_flush_icache ((guint8*)code, 4);
41 if ((((*code) >> 20) & 0xFF) == 0x12) {
42 /*g_print ("patching bx\n");*/
43 arm_patch ((guint8*)code, addr);
44 mono_arch_flush_icache ((guint8*)(code - 2), 4);
48 g_assert_not_reached ();
52 mono_arch_patch_plt_entry (guint8 *code, gpointer *got, mgreg_t *regs, guint8 *addr)
56 /* Patch the jump table entry used by the plt entry */
57 if (*(guint32*)code == 0xe59fc000) {
58 /* ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0); */
59 guint32 offset = ((guint32*)code)[2];
61 jump_entry = code + offset + 12;
63 g_assert_not_reached ();
66 *(guint8**)jump_entry = addr;
70 mono_arch_nullify_class_init_trampoline (guint8 *code, mgreg_t *regs)
72 mono_arch_patch_callsite (NULL, code, nullified_class_init_trampoline);
76 mono_arch_nullify_plt_entry (guint8 *code, mgreg_t *regs)
78 if (mono_aot_only && !nullified_class_init_trampoline)
79 nullified_class_init_trampoline = mono_aot_get_trampoline ("nullified_class_init_trampoline");
81 mono_arch_patch_plt_entry (code, NULL, regs, nullified_class_init_trampoline);
86 #define arm_is_imm12(v) ((int)(v) > -4096 && (int)(v) < 4096)
89 * Return the instruction to jump from code to target, 0 if not
90 * reachable with a single instruction
93 branch_for_target_reachable (guint8 *branch, guint8 *target)
95 gint diff = target - branch - 8;
96 g_assert ((diff & 3) == 0);
99 return (ARMCOND_AL << ARMCOND_SHIFT) | (ARM_BR_TAG) | (diff >> 2);
101 /* diff between 0 and -33554432 */
102 if (diff >= -33554432)
103 return (ARMCOND_AL << ARMCOND_SHIFT) | (ARM_BR_TAG) | ((diff >> 2) & ~0xff000000);
108 static inline guint8*
109 emit_bx (guint8* code, int reg)
111 if (mono_arm_thumb_supported ())
114 ARM_MOV_REG_REG (code, ARMREG_PC, reg);
118 /* Stack size for trampoline function
120 #define STACK (sizeof (MonoLMF))
122 /* Method-specific trampoline code fragment size */
123 #define METHOD_TRAMPOLINE_SIZE 64
125 /* Jump-specific trampoline code fragment size */
126 #define JUMP_TRAMPOLINE_SIZE 64
128 #define GEN_TRAMP_SIZE 196
131 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
133 guint8 *buf, *code = NULL;
134 guint8 *load_get_lmf_addr, *load_trampoline;
137 GSList *unwind_ops = NULL;
138 MonoJumpInfo *ji = NULL;
140 /* Now we'll create in 'buf' the ARM trampoline code. This
141 is the trampoline code common to all methods */
143 code = buf = mono_global_codeman_reserve (GEN_TRAMP_SIZE);
146 * At this point lr points to the specific arg and sp points to the saved
147 * regs on the stack (all but PC and SP). The original LR value has been
148 * saved as sp + LR_OFFSET by the push in the specific trampoline
150 #define LR_OFFSET (sizeof (gpointer) * 13)
152 // FIXME: Finish the unwind info, the current info allows us to unwind
153 // when the trampoline is not in the epilog
155 // CFA = SP + (num registers pushed) * 4
156 cfa_offset = 14 * sizeof (gpointer);
157 mono_add_unwind_op_def_cfa (unwind_ops, code, buf, ARMREG_SP, cfa_offset);
158 // PC saved at sp+LR_OFFSET
159 mono_add_unwind_op_offset (unwind_ops, code, buf, ARMREG_LR, -4);
161 ARM_MOV_REG_REG (code, ARMREG_V1, ARMREG_SP);
162 if (aot && tramp_type != MONO_TRAMPOLINE_GENERIC_CLASS_INIT) {
164 * The trampoline contains a pc-relative offset to the got slot
165 * preceeding the got slot where the value is stored. The offset can be
168 ARM_LDR_IMM (code, ARMREG_V2, ARMREG_LR, 0);
169 ARM_ADD_REG_IMM (code, ARMREG_V2, ARMREG_V2, 4, 0);
170 ARM_LDR_REG_REG (code, ARMREG_V2, ARMREG_V2, ARMREG_LR);
172 if (tramp_type != MONO_TRAMPOLINE_GENERIC_CLASS_INIT)
173 ARM_LDR_IMM (code, ARMREG_V2, ARMREG_LR, 0);
175 ARM_MOV_REG_REG (code, ARMREG_V2, MONO_ARCH_VTABLE_REG);
177 ARM_LDR_IMM (code, ARMREG_V3, ARMREG_SP, LR_OFFSET);
179 /* ok, now we can continue with the MonoLMF setup, mostly untouched
180 * from emit_prolog in mini-arm.c
181 * This is a synthetized call to mono_get_lmf_addr ()
184 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_get_lmf_addr");
185 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
187 *(gpointer*)code = NULL;
189 ARM_LDR_REG_REG (code, ARMREG_R0, ARMREG_PC, ARMREG_R0);
191 load_get_lmf_addr = code;
194 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
195 code = emit_bx (code, ARMREG_R0);
197 /* we build the MonoLMF structure on the stack - see mini-arm.h
198 * The pointer to the struct is put in r1.
199 * the iregs array is already allocated on the stack by push.
201 ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, sizeof (MonoLMF) - sizeof (guint) * 14);
202 cfa_offset += sizeof (MonoLMF) - sizeof (guint) * 14;
203 mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
204 ARM_ADD_REG_IMM8 (code, ARMREG_R1, ARMREG_SP, STACK - sizeof (MonoLMF));
205 /* r0 is the result from mono_get_lmf_addr () */
206 ARM_STR_IMM (code, ARMREG_R0, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
207 /* new_lmf->previous_lmf = *lmf_addr */
208 ARM_LDR_IMM (code, ARMREG_R2, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
209 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
210 /* *(lmf_addr) = r1 */
211 ARM_STR_IMM (code, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
212 /* save method info (it's in v2) */
213 if ((tramp_type == MONO_TRAMPOLINE_JIT) || (tramp_type == MONO_TRAMPOLINE_JUMP))
214 ARM_STR_IMM (code, ARMREG_V2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, method));
216 ARM_MOV_REG_IMM8 (code, ARMREG_R2, 0);
217 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, method));
219 /* Save sp into lmf->iregs, the eh code expects it to be at IP */
220 ARM_ADD_REG_IMM8 (code, ARMREG_R2, ARMREG_SP, cfa_offset);
221 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, iregs) + (ARMREG_IP * 4));
222 ARM_STR_IMM (code, ARMREG_SP, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, esp));
223 /* save the IP (caller ip) */
224 if (tramp_type == MONO_TRAMPOLINE_JUMP) {
225 ARM_MOV_REG_IMM8 (code, ARMREG_R2, 0);
227 /* assumes STACK == sizeof (MonoLMF) */
228 ARM_LDR_IMM (code, ARMREG_R2, ARMREG_SP, (G_STRUCT_OFFSET (MonoLMF, iregs) + 13*4));
230 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, eip));
233 * Now we're ready to call xxx_trampoline ().
235 /* Arg 1: the saved registers. It was put in v1 */
236 ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_V1);
238 /* Arg 2: code (next address to the instruction that called us) */
239 if (tramp_type == MONO_TRAMPOLINE_JUMP) {
240 ARM_MOV_REG_IMM8 (code, ARMREG_R1, 0);
242 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_V3);
245 /* Arg 3: the specific argument, stored in v2
247 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_V2);
250 char *icall_name = g_strdup_printf ("trampoline_func_%d", tramp_type);
251 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name);
252 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
254 *(gpointer*)code = NULL;
256 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
258 load_trampoline = code;
262 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
263 code = emit_bx (code, ARMREG_IP);
265 /* OK, code address is now on r0. Move it to the place on the stack
266 * where IP was saved (it is now no more useful to us and it can be
267 * clobbered). This way we can just restore all the regs in one inst
270 ARM_STR_IMM (code, ARMREG_R0, ARMREG_V1, (ARMREG_R12 * 4));
272 /* Check for thread interruption */
273 /* This is not perf critical code so no need to check the interrupt flag */
275 * Have to call the _force_ variant, since there could be a protected wrapper on the top of the stack.
278 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_thread_force_interruption_checkpoint");
279 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
281 *(gpointer*)code = NULL;
283 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
285 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
287 *(gpointer*)code = mono_thread_force_interruption_checkpoint;
290 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
291 code = emit_bx (code, ARMREG_IP);
294 * Now we restore the MonoLMF (see emit_epilogue in mini-arm.c)
295 * and the rest of the registers, so the method called will see
296 * the same state as before we executed.
297 * The pointer to MonoLMF is in r2.
299 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_SP);
300 /* ip = previous_lmf */
301 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
303 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
304 /* *(lmf_addr) = previous_lmf */
305 ARM_STR_IMM (code, ARMREG_IP, ARMREG_LR, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
307 /* Non-standard function epilogue. Instead of doing a proper
308 * return, we just jump to the compiled code.
310 /* Restore the registers and jump to the code:
311 * Note that IP has been conveniently set to the method addr.
313 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, sizeof (MonoLMF) - sizeof (guint) * 14);
314 ARM_POP_NWB (code, 0x5fff);
315 if (tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH)
316 ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_IP);
317 /* do we need to set sp? */
318 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, (14 * 4));
319 if ((tramp_type == MONO_TRAMPOLINE_CLASS_INIT) || (tramp_type == MONO_TRAMPOLINE_GENERIC_CLASS_INIT) || (tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH))
320 code = emit_bx (code, ARMREG_LR);
322 code = emit_bx (code, ARMREG_IP);
324 constants = (gpointer*)code;
325 constants [0] = mono_get_lmf_addr;
326 constants [1] = (gpointer)mono_get_trampoline_func (tramp_type);
329 /* backpatch by emitting the missing instructions skipped above */
330 ARM_LDR_IMM (load_get_lmf_addr, ARMREG_R0, ARMREG_PC, (code - load_get_lmf_addr - 8));
331 ARM_LDR_IMM (load_trampoline, ARMREG_IP, ARMREG_PC, (code + 4 - load_trampoline - 8));
336 /* Flush instruction cache, since we've generated code */
337 mono_arch_flush_icache (buf, code - buf);
340 g_assert ((code - buf) <= GEN_TRAMP_SIZE);
342 if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
343 /* Initialize the nullified class init trampoline used in the AOT case */
344 nullified_class_init_trampoline = mono_arch_get_nullified_class_init_trampoline (NULL);
347 *info = mono_tramp_info_create (g_strdup_printf ("generic_trampoline_%d", tramp_type), buf, code - buf, ji, unwind_ops);
353 mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo **info)
357 code = buf = mono_global_codeman_reserve (16);
359 code = emit_bx (code, ARMREG_LR);
361 mono_arch_flush_icache (buf, code - buf);
364 *info = mono_tramp_info_create (g_strdup_printf ("nullified_class_init_trampoline"), buf, code - buf, NULL, NULL);
369 #define SPEC_TRAMP_SIZE 24
372 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
374 guint8 *code, *buf, *tramp;
376 guint32 short_branch, size = SPEC_TRAMP_SIZE;
378 tramp = mono_get_trampoline_code (tramp_type);
380 mono_domain_lock (domain);
381 code = buf = mono_domain_code_reserve_align (domain, size, 4);
382 if ((short_branch = branch_for_target_reachable (code + 8, tramp))) {
384 mono_domain_code_commit (domain, code, SPEC_TRAMP_SIZE, size);
386 mono_domain_unlock (domain);
388 /* we could reduce this to 12 bytes if tramp is within reach:
392 * The called code can access method using the lr register
393 * A 20 byte sequence could be:
395 * ARM_MOV_REG_REG (lr, pc)
396 * ARM_LDR_IMM (pc, pc, 0)
400 /* We save all the registers, except PC and SP */
401 ARM_PUSH (code, 0x5fff);
403 constants = (gpointer*)code;
404 constants [0] = GUINT_TO_POINTER (short_branch | (1 << 24));
405 constants [1] = arg1;
408 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 8); /* temp reg */
409 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
410 code = emit_bx (code, ARMREG_R1);
412 constants = (gpointer*)code;
413 constants [0] = arg1;
414 constants [1] = tramp;
418 /* Flush instruction cache, since we've generated code */
419 mono_arch_flush_icache (buf, code - buf);
421 g_assert ((code - buf) <= size);
424 *code_len = code - buf;
430 * mono_arch_get_unbox_trampoline:
431 * @gsctx: the generic sharing context
433 * @addr: pointer to native code for @m
435 * when value type methods are called through the vtable we need to unbox the
436 * this argument. This method returns a pointer to a trampoline which does
437 * unboxing before calling the method
440 mono_arch_get_unbox_trampoline (MonoGenericSharingContext *gsctx, MonoMethod *m, gpointer addr)
442 guint8 *code, *start;
443 MonoDomain *domain = mono_domain_get ();
445 start = code = mono_domain_code_reserve (domain, 16);
447 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 4);
448 ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_R0, sizeof (MonoObject));
449 code = emit_bx (code, ARMREG_IP);
450 *(guint32*)code = (guint32)addr;
452 mono_arch_flush_icache (start, code - start);
453 g_assert ((code - start) <= 16);
454 /*g_print ("unbox trampoline at %d for %s:%s\n", this_pos, m->klass->name, m->name);
455 g_print ("unbox code is at %p for method at %p\n", start, addr);*/
461 mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr)
463 guint8 *code, *start;
466 MonoDomain *domain = mono_domain_get ();
470 start = code = mono_domain_code_reserve (domain, buf_len);
472 ARM_LDR_IMM (code, MONO_ARCH_RGCTX_REG, ARMREG_PC, 0);
473 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_PC, 0);
474 *(guint32*)code = (guint32)mrgctx;
476 *(guint32*)code = (guint32)addr;
479 g_assert ((code - start) <= buf_len);
481 mono_arch_flush_icache (start, code - start);
487 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
493 guint8 **rgctx_null_jumps;
497 MonoJumpInfo *ji = NULL;
498 GSList *unwind_ops = NULL;
500 mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
501 index = MONO_RGCTX_SLOT_INDEX (slot);
503 index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
504 for (depth = 0; ; ++depth) {
505 int size = mono_class_rgctx_get_array_size (depth, mrgctx);
507 if (index < size - 1)
512 tramp_size = 64 + 16 * depth;
514 code = buf = mono_global_codeman_reserve (tramp_size);
516 rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2));
519 /* The vtable/mrgctx is in R0 */
520 g_assert (MONO_ARCH_VTABLE_REG == ARMREG_R0);
524 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
526 /* load rgctx ptr from vtable */
527 g_assert (arm_is_imm12 (G_STRUCT_OFFSET (MonoVTable, runtime_generic_context)));
528 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoVTable, runtime_generic_context));
529 /* is the rgctx ptr null? */
530 ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
531 /* if yes, jump to actual trampoline */
532 rgctx_null_jumps [njumps ++] = code;
533 ARM_B_COND (code, ARMCOND_EQ, 0);
536 for (i = 0; i < depth; ++i) {
537 /* load ptr to next array */
538 if (mrgctx && i == 0) {
539 g_assert (arm_is_imm12 (MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT));
540 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R1, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT);
542 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R1, 0);
544 /* is the ptr null? */
545 ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
546 /* if yes, jump to actual trampoline */
547 rgctx_null_jumps [njumps ++] = code;
548 ARM_B_COND (code, ARMCOND_EQ, 0);
552 code = mono_arm_emit_load_imm (code, ARMREG_R2, sizeof (gpointer) * (index + 1));
553 ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_R1, ARMREG_R2);
554 /* is the slot null? */
555 ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
556 /* if yes, jump to actual trampoline */
557 rgctx_null_jumps [njumps ++] = code;
558 ARM_B_COND (code, ARMCOND_EQ, 0);
559 /* otherwise return, result is in R1 */
560 ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_R1);
561 code = emit_bx (code, ARMREG_LR);
563 g_assert (njumps <= depth + 2);
564 for (i = 0; i < njumps; ++i)
565 arm_patch (rgctx_null_jumps [i], code);
567 g_free (rgctx_null_jumps);
571 /* The vtable/mrgctx is still in R0 */
574 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", slot));
575 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
577 *(gpointer*)code = NULL;
579 ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_R1);
581 tramp = mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), &code_len);
583 /* Jump to the actual trampoline */
584 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0); /* temp reg */
585 code = emit_bx (code, ARMREG_R1);
586 *(gpointer*)code = tramp;
590 mono_arch_flush_icache (buf, code - buf);
592 g_assert (code - buf <= tramp_size);
595 *info = mono_tramp_info_create (g_strdup_printf ("rgctx_fetch_trampoline_%u", slot), buf, code - buf, ji, unwind_ops);
600 #define arm_is_imm8(v) ((v) > -256 && (v) < 256)
603 mono_arch_create_generic_class_init_trampoline (MonoTrampInfo **info, gboolean aot)
607 static int byte_offset = -1;
608 static guint8 bitmask;
611 guint32 code_len, imm8;
613 GSList *unwind_ops = NULL;
614 MonoJumpInfo *ji = NULL;
618 code = buf = mono_global_codeman_reserve (tramp_size);
621 mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask);
623 g_assert (arm_is_imm8 (byte_offset));
624 ARM_LDRSB_IMM (code, ARMREG_IP, MONO_ARCH_VTABLE_REG, byte_offset);
625 imm8 = mono_arm_is_rotated_imm8 (bitmask, &rot_amount);
626 g_assert (imm8 >= 0);
627 ARM_AND_REG_IMM (code, ARMREG_IP, ARMREG_IP, imm8, rot_amount);
628 ARM_CMP_REG_IMM (code, ARMREG_IP, 0, 0);
630 ARM_B_COND (code, ARMCOND_EQ, 0);
632 /* Initialized case */
633 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_LR);
635 /* Uninitialized case */
636 arm_patch (jump, code);
639 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "specific_trampoline_generic_class_init");
640 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
642 *(gpointer*)code = NULL;
644 ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_R1);
646 tramp = mono_arch_create_specific_trampoline (NULL, MONO_TRAMPOLINE_GENERIC_CLASS_INIT, mono_get_root_domain (), &code_len);
648 /* Jump to the actual trampoline */
649 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0); /* temp reg */
650 code = emit_bx (code, ARMREG_R1);
651 *(gpointer*)code = tramp;
655 mono_arch_flush_icache (buf, code - buf);
657 g_assert (code - buf <= tramp_size);
660 *info = mono_tramp_info_create (g_strdup_printf ("generic_class_init_trampoline"), buf, code - buf, ji, unwind_ops);
668 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
670 g_assert_not_reached ();
675 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
677 g_assert_not_reached ();
682 mono_arch_get_unbox_trampoline (MonoGenericSharingContext *gsctx, MonoMethod *m, gpointer addr)
684 g_assert_not_reached ();
689 mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr)
691 g_assert_not_reached ();
696 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
698 g_assert_not_reached ();
703 mono_arch_create_generic_class_init_trampoline (MonoTrampInfo **info, gboolean aot)
705 g_assert_not_reached ();
709 #endif /* DISABLE_JIT */
712 mono_arch_get_call_target (guint8 *code)
714 guint32 ins = ((guint32*)(gpointer)code) [-1];
716 /* Should be a 'bl' */
717 if ((((ins >> 25) & 0x7) == 0x5) && (((ins >> 24) & 0x1) == 0x1)) {
718 gint32 disp = ((gint32)ins) & 0xffffff;
719 guint8 *target = code - 4 + 8 + (disp * 4);
728 mono_arch_get_plt_info_offset (guint8 *plt_entry, mgreg_t *regs, guint8 *code)
730 /* The offset is stored as the 4th word of the plt entry */
731 return ((guint32*)plt_entry) [3];