2010-06-21 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / mini / tramp-arm.c
blob8e1a1c61cf59b1fbb7cbb09974cd05d088c97b3b
1 /*
2 * tramp-arm.c: JIT trampoline code for ARM
4 * Authors:
5 * Paolo Molaro (lupus@ximian.com)
7 * (C) 2001 Ximian, Inc.
8 */
10 #include <config.h>
11 #include <glib.h>
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>
18 #include "mini.h"
19 #include "mini-arm.h"
21 static guint8* nullified_class_init_trampoline;
23 void
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 */
29 --code;
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);
38 return;
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);
45 return;
48 g_assert_not_reached ();
51 void
52 mono_arch_patch_plt_entry (guint8 *code, gpointer *got, mgreg_t *regs, guint8 *addr)
54 guint8 *jump_entry;
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;
62 } else {
63 g_assert_not_reached ();
66 *(guint8**)jump_entry = addr;
69 void
70 mono_arch_nullify_class_init_trampoline (guint8 *code, mgreg_t *regs)
72 mono_arch_patch_callsite (NULL, code, nullified_class_init_trampoline);
75 void
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);
84 #ifndef DISABLE_JIT
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
92 static guint32
93 branch_for_target_reachable (guint8 *branch, guint8 *target)
95 gint diff = target - branch - 8;
96 g_assert ((diff & 3) == 0);
97 if (diff >= 0) {
98 if (diff <= 33554431)
99 return (ARMCOND_AL << ARMCOND_SHIFT) | (ARM_BR_TAG) | (diff >> 2);
100 } else {
101 /* diff between 0 and -33554432 */
102 if (diff >= -33554432)
103 return (ARMCOND_AL << ARMCOND_SHIFT) | (ARM_BR_TAG) | ((diff >> 2) & ~0xff000000);
105 return 0;
108 static inline guint8*
109 emit_bx (guint8* code, int reg)
111 if (mono_arm_thumb_supported ())
112 ARM_BX (code, reg);
113 else
114 ARM_MOV_REG_REG (code, ARMREG_PC, reg);
115 return code;
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
130 guchar*
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;
135 gpointer *constants;
136 int cfa_offset;
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
166 * found at [lr + 0].
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);
171 } else {
172 if (tramp_type != MONO_TRAMPOLINE_GENERIC_CLASS_INIT)
173 ARM_LDR_IMM (code, ARMREG_V2, ARMREG_LR, 0);
174 else
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 ()
183 if (aot) {
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);
186 ARM_B (code, 0);
187 *(gpointer*)code = NULL;
188 code += 4;
189 ARM_LDR_REG_REG (code, ARMREG_R0, ARMREG_PC, ARMREG_R0);
190 } else {
191 load_get_lmf_addr = code;
192 code += 4;
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));
215 else {
216 ARM_MOV_REG_IMM8 (code, ARMREG_R2, 0);
217 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, method));
219 ARM_STR_IMM (code, ARMREG_SP, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, ebp));
220 /* save the IP (caller ip) */
221 if (tramp_type == MONO_TRAMPOLINE_JUMP) {
222 ARM_MOV_REG_IMM8 (code, ARMREG_R2, 0);
223 } else {
224 /* assumes STACK == sizeof (MonoLMF) */
225 ARM_LDR_IMM (code, ARMREG_R2, ARMREG_SP, (G_STRUCT_OFFSET (MonoLMF, iregs) + 13*4));
227 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, eip));
230 * Now we're ready to call xxx_trampoline ().
232 /* Arg 1: the saved registers. It was put in v1 */
233 ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_V1);
235 /* Arg 2: code (next address to the instruction that called us) */
236 if (tramp_type == MONO_TRAMPOLINE_JUMP) {
237 ARM_MOV_REG_IMM8 (code, ARMREG_R1, 0);
238 } else {
239 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_V3);
242 /* Arg 3: the specific argument, stored in v2
244 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_V2);
246 if (aot) {
247 char *icall_name = g_strdup_printf ("trampoline_func_%d", tramp_type);
248 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name);
249 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
250 ARM_B (code, 0);
251 *(gpointer*)code = NULL;
252 code += 4;
253 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
254 } else {
255 load_trampoline = code;
256 code += 4;
259 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
260 code = emit_bx (code, ARMREG_IP);
262 /* OK, code address is now on r0. Move it to the place on the stack
263 * where IP was saved (it is now no more useful to us and it can be
264 * clobbered). This way we can just restore all the regs in one inst
265 * and branch to IP.
267 ARM_STR_IMM (code, ARMREG_R0, ARMREG_V1, (ARMREG_R12 * 4));
269 /* Check for thread interruption */
270 /* This is not perf critical code so no need to check the interrupt flag */
272 * Have to call the _force_ variant, since there could be a protected wrapper on the top of the stack.
274 if (aot) {
275 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_thread_force_interruption_checkpoint");
276 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
277 ARM_B (code, 0);
278 *(gpointer*)code = NULL;
279 code += 4;
280 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
281 } else {
282 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
283 ARM_B (code, 0);
284 *(gpointer*)code = mono_thread_force_interruption_checkpoint;
285 code += 4;
287 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
288 code = emit_bx (code, ARMREG_IP);
291 * Now we restore the MonoLMF (see emit_epilogue in mini-arm.c)
292 * and the rest of the registers, so the method called will see
293 * the same state as before we executed.
294 * The pointer to MonoLMF is in r2.
296 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_SP);
297 /* ip = previous_lmf */
298 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
299 /* lr = lmf_addr */
300 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
301 /* *(lmf_addr) = previous_lmf */
302 ARM_STR_IMM (code, ARMREG_IP, ARMREG_LR, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
304 /* Non-standard function epilogue. Instead of doing a proper
305 * return, we just jump to the compiled code.
307 /* Restore the registers and jump to the code:
308 * Note that IP has been conveniently set to the method addr.
310 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, sizeof (MonoLMF) - sizeof (guint) * 14);
311 ARM_POP_NWB (code, 0x5fff);
312 if (tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH)
313 ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_IP);
314 /* do we need to set sp? */
315 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, (14 * 4));
316 if ((tramp_type == MONO_TRAMPOLINE_CLASS_INIT) || (tramp_type == MONO_TRAMPOLINE_GENERIC_CLASS_INIT) || (tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH))
317 code = emit_bx (code, ARMREG_LR);
318 else
319 code = emit_bx (code, ARMREG_IP);
321 constants = (gpointer*)code;
322 constants [0] = mono_get_lmf_addr;
323 constants [1] = (gpointer)mono_get_trampoline_func (tramp_type);
325 if (!aot) {
326 /* backpatch by emitting the missing instructions skipped above */
327 ARM_LDR_IMM (load_get_lmf_addr, ARMREG_R0, ARMREG_PC, (code - load_get_lmf_addr - 8));
328 ARM_LDR_IMM (load_trampoline, ARMREG_IP, ARMREG_PC, (code + 4 - load_trampoline - 8));
331 code += 8;
333 /* Flush instruction cache, since we've generated code */
334 mono_arch_flush_icache (buf, code - buf);
336 /* Sanity check */
337 g_assert ((code - buf) <= GEN_TRAMP_SIZE);
339 if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
340 /* Initialize the nullified class init trampoline used in the AOT case */
341 nullified_class_init_trampoline = mono_arch_get_nullified_class_init_trampoline (NULL);
343 if (info)
344 *info = mono_tramp_info_create (g_strdup_printf ("generic_trampoline_%d", tramp_type), buf, code - buf, ji, unwind_ops);
346 return buf;
349 gpointer
350 mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo **info)
352 guint8 *buf, *code;
354 code = buf = mono_global_codeman_reserve (16);
356 code = emit_bx (code, ARMREG_LR);
358 mono_arch_flush_icache (buf, code - buf);
360 if (info)
361 *info = mono_tramp_info_create (g_strdup_printf ("nullified_class_init_trampoline"), buf, code - buf, NULL, NULL);
363 return buf;
366 #define SPEC_TRAMP_SIZE 24
368 gpointer
369 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
371 guint8 *code, *buf, *tramp;
372 gpointer *constants;
373 guint32 short_branch, size = SPEC_TRAMP_SIZE;
375 tramp = mono_get_trampoline_code (tramp_type);
377 mono_domain_lock (domain);
378 code = buf = mono_domain_code_reserve_align (domain, size, 4);
379 if ((short_branch = branch_for_target_reachable (code + 8, tramp))) {
380 size = 12;
381 mono_domain_code_commit (domain, code, SPEC_TRAMP_SIZE, size);
383 mono_domain_unlock (domain);
385 /* we could reduce this to 12 bytes if tramp is within reach:
386 * ARM_PUSH ()
387 * ARM_BL ()
388 * method-literal
389 * The called code can access method using the lr register
390 * A 20 byte sequence could be:
391 * ARM_PUSH ()
392 * ARM_MOV_REG_REG (lr, pc)
393 * ARM_LDR_IMM (pc, pc, 0)
394 * method-literal
395 * tramp-literal
397 /* We save all the registers, except PC and SP */
398 ARM_PUSH (code, 0x5fff);
399 if (short_branch) {
400 constants = (gpointer*)code;
401 constants [0] = GUINT_TO_POINTER (short_branch | (1 << 24));
402 constants [1] = arg1;
403 code += 8;
404 } else {
405 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 8); /* temp reg */
406 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
407 code = emit_bx (code, ARMREG_R1);
409 constants = (gpointer*)code;
410 constants [0] = arg1;
411 constants [1] = tramp;
412 code += 8;
415 /* Flush instruction cache, since we've generated code */
416 mono_arch_flush_icache (buf, code - buf);
418 g_assert ((code - buf) <= size);
420 if (code_len)
421 *code_len = code - buf;
423 return buf;
427 * mono_arch_get_unbox_trampoline:
428 * @gsctx: the generic sharing context
429 * @m: method pointer
430 * @addr: pointer to native code for @m
432 * when value type methods are called through the vtable we need to unbox the
433 * this argument. This method returns a pointer to a trampoline which does
434 * unboxing before calling the method
436 gpointer
437 mono_arch_get_unbox_trampoline (MonoGenericSharingContext *gsctx, MonoMethod *m, gpointer addr)
439 guint8 *code, *start;
440 int this_pos = 0;
441 MonoDomain *domain = mono_domain_get ();
443 if (MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret))
444 this_pos = 1;
446 start = code = mono_domain_code_reserve (domain, 16);
448 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 4);
449 ARM_ADD_REG_IMM8 (code, this_pos, this_pos, sizeof (MonoObject));
450 code = emit_bx (code, ARMREG_IP);
451 *(guint32*)code = (guint32)addr;
452 code += 4;
453 mono_arch_flush_icache (start, code - start);
454 g_assert ((code - start) <= 16);
455 /*g_print ("unbox trampoline at %d for %s:%s\n", this_pos, m->klass->name, m->name);
456 g_print ("unbox code is at %p for method at %p\n", start, addr);*/
458 return start;
461 gpointer
462 mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr)
464 guint8 *code, *start;
465 int buf_len;
467 MonoDomain *domain = mono_domain_get ();
469 buf_len = 16;
471 start = code = mono_domain_code_reserve (domain, buf_len);
473 ARM_LDR_IMM (code, MONO_ARCH_RGCTX_REG, ARMREG_PC, 0);
474 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_PC, 0);
475 *(guint32*)code = (guint32)mrgctx;
476 code += 4;
477 *(guint32*)code = (guint32)addr;
478 code += 4;
480 g_assert ((code - start) <= buf_len);
482 mono_arch_flush_icache (start, code - start);
484 return start;
487 gpointer
488 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
490 guint8 *tramp;
491 guint8 *code, *buf;
492 int tramp_size;
493 guint32 code_len;
494 guint8 **rgctx_null_jumps;
495 int depth, index;
496 int i, njumps;
497 gboolean mrgctx;
498 MonoJumpInfo *ji = NULL;
499 GSList *unwind_ops = NULL;
501 mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
502 index = MONO_RGCTX_SLOT_INDEX (slot);
503 if (mrgctx)
504 index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
505 for (depth = 0; ; ++depth) {
506 int size = mono_class_rgctx_get_array_size (depth, mrgctx);
508 if (index < size - 1)
509 break;
510 index -= size - 1;
513 tramp_size = 64 + 16 * depth;
515 code = buf = mono_global_codeman_reserve (tramp_size);
517 rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2));
518 njumps = 0;
520 /* The vtable/mrgctx is in R0 */
521 g_assert (MONO_ARCH_VTABLE_REG == ARMREG_R0);
523 if (mrgctx) {
524 /* get mrgctx ptr */
525 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
526 } else {
527 /* load rgctx ptr from vtable */
528 g_assert (arm_is_imm12 (G_STRUCT_OFFSET (MonoVTable, runtime_generic_context)));
529 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoVTable, runtime_generic_context));
530 /* is the rgctx ptr null? */
531 ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
532 /* if yes, jump to actual trampoline */
533 rgctx_null_jumps [njumps ++] = code;
534 ARM_B_COND (code, ARMCOND_EQ, 0);
537 for (i = 0; i < depth; ++i) {
538 /* load ptr to next array */
539 if (mrgctx && i == 0) {
540 g_assert (arm_is_imm12 (MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT));
541 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R1, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT);
542 } else {
543 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R1, 0);
545 /* is the ptr null? */
546 ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
547 /* if yes, jump to actual trampoline */
548 rgctx_null_jumps [njumps ++] = code;
549 ARM_B_COND (code, ARMCOND_EQ, 0);
552 /* fetch slot */
553 code = mono_arm_emit_load_imm (code, ARMREG_R2, sizeof (gpointer) * (index + 1));
554 ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_R1, ARMREG_R2);
555 /* is the slot null? */
556 ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
557 /* if yes, jump to actual trampoline */
558 rgctx_null_jumps [njumps ++] = code;
559 ARM_B_COND (code, ARMCOND_EQ, 0);
560 /* otherwise return, result is in R1 */
561 ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_R1);
562 code = emit_bx (code, ARMREG_LR);
564 g_assert (njumps <= depth + 2);
565 for (i = 0; i < njumps; ++i)
566 arm_patch (rgctx_null_jumps [i], code);
568 g_free (rgctx_null_jumps);
570 /* Slowpath */
572 /* The vtable/mrgctx is still in R0 */
574 if (aot) {
575 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", slot));
576 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
577 ARM_B (code, 0);
578 *(gpointer*)code = NULL;
579 code += 4;
580 ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_R1);
581 } else {
582 tramp = mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), &code_len);
584 /* Jump to the actual trampoline */
585 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0); /* temp reg */
586 code = emit_bx (code, ARMREG_R1);
587 *(gpointer*)code = tramp;
588 code += 4;
591 mono_arch_flush_icache (buf, code - buf);
593 g_assert (code - buf <= tramp_size);
595 if (info)
596 *info = mono_tramp_info_create (g_strdup_printf ("rgctx_fetch_trampoline_%u", slot), buf, code - buf, ji, unwind_ops);
598 return buf;
601 #define arm_is_imm8(v) ((v) > -256 && (v) < 256)
603 gpointer
604 mono_arch_create_generic_class_init_trampoline (MonoTrampInfo **info, gboolean aot)
606 guint8 *tramp;
607 guint8 *code, *buf;
608 static int byte_offset = -1;
609 static guint8 bitmask;
610 guint8 *jump;
611 int tramp_size;
612 guint32 code_len, imm8;
613 gint rot_amount;
614 GSList *unwind_ops = NULL;
615 MonoJumpInfo *ji = NULL;
617 tramp_size = 64;
619 code = buf = mono_global_codeman_reserve (tramp_size);
621 if (byte_offset < 0)
622 mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask);
624 g_assert (arm_is_imm8 (byte_offset));
625 ARM_LDRSB_IMM (code, ARMREG_IP, MONO_ARCH_VTABLE_REG, byte_offset);
626 imm8 = mono_arm_is_rotated_imm8 (bitmask, &rot_amount);
627 g_assert (imm8 >= 0);
628 ARM_AND_REG_IMM (code, ARMREG_IP, ARMREG_IP, imm8, rot_amount);
629 ARM_CMP_REG_IMM (code, ARMREG_IP, 0, 0);
630 jump = code;
631 ARM_B_COND (code, ARMCOND_EQ, 0);
633 /* Initialized case */
634 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_LR);
636 /* Uninitialized case */
637 arm_patch (jump, code);
639 if (aot) {
640 ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "specific_trampoline_generic_class_init");
641 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
642 ARM_B (code, 0);
643 *(gpointer*)code = NULL;
644 code += 4;
645 ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_R1);
646 } else {
647 tramp = mono_arch_create_specific_trampoline (NULL, MONO_TRAMPOLINE_GENERIC_CLASS_INIT, mono_get_root_domain (), &code_len);
649 /* Jump to the actual trampoline */
650 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0); /* temp reg */
651 code = emit_bx (code, ARMREG_R1);
652 *(gpointer*)code = tramp;
653 code += 4;
656 mono_arch_flush_icache (buf, code - buf);
658 g_assert (code - buf <= tramp_size);
660 if (info)
661 *info = mono_tramp_info_create (g_strdup_printf ("generic_class_init_trampoline"), buf, code - buf, ji, unwind_ops);
663 return buf;
666 #else
668 guchar*
669 mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
671 g_assert_not_reached ();
672 return NULL;
675 gpointer
676 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
678 g_assert_not_reached ();
679 return NULL;
682 gpointer
683 mono_arch_get_unbox_trampoline (MonoGenericSharingContext *gsctx, MonoMethod *m, gpointer addr)
685 g_assert_not_reached ();
686 return NULL;
689 gpointer
690 mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr)
692 g_assert_not_reached ();
693 return NULL;
696 gpointer
697 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
699 g_assert_not_reached ();
700 return NULL;
703 gpointer
704 mono_arch_create_generic_class_init_trampoline (MonoTrampInfo **info, gboolean aot)
706 g_assert_not_reached ();
707 return NULL;
710 #endif /* DISABLE_JIT */
712 guint8*
713 mono_arch_get_call_target (guint8 *code)
715 guint32 ins = ((guint32*)(gpointer)code) [-1];
717 /* Should be a 'bl' */
718 if ((((ins >> 25) & 0x7) == 0x5) && (((ins >> 24) & 0x1) == 0x1)) {
719 gint32 disp = ((gint32)ins) & 0xffffff;
720 guint8 *target = code - 4 + 8 + (disp * 4);
722 return target;
723 } else {
724 return NULL;
728 guint32
729 mono_arch_get_plt_info_offset (guint8 *plt_entry, mgreg_t *regs, guint8 *code)
731 /* The offset is stored as the 4th word of the plt entry */
732 return ((guint32*)plt_entry) [3];