2 * mini-arm.c: ARM backend for the Mono code generator
5 * Paolo Molaro (lupus@ximian.com)
6 * Dietmar Maurer (dietmar@ximian.com)
8 * (C) 2003 Ximian, Inc.
13 #include <mono/metadata/appdomain.h>
14 #include <mono/metadata/debug-helpers.h>
15 #include <mono/utils/mono-mmap.h>
22 #include "mono/arch/arm/arm-fpa-codegen.h"
23 #elif defined(ARM_FPU_VFP)
24 #include "mono/arch/arm/arm-vfp-codegen.h"
27 #if defined(__ARM_EABI__) && defined(__linux__) && !defined(PLATFORM_ANDROID)
28 #define HAVE_AEABI_READ_TP 1
31 static gint lmf_tls_offset = -1;
32 static gint lmf_addr_tls_offset = -1;
34 /* This mutex protects architecture specific caches */
35 #define mono_mini_arch_lock() EnterCriticalSection (&mini_arch_mutex)
36 #define mono_mini_arch_unlock() LeaveCriticalSection (&mini_arch_mutex)
37 static CRITICAL_SECTION mini_arch_mutex;
39 static int v5_supported = 0;
40 static int v7_supported = 0;
41 static int thumb_supported = 0;
44 * The code generated for sequence points reads from this location, which is
45 * made read-only when single stepping is enabled.
47 static gpointer ss_trigger_page;
49 /* Enabled breakpoints read from this trigger page */
50 static gpointer bp_trigger_page;
52 /* Structure used by the sequence points in AOTed code */
54 gpointer ss_trigger_page;
55 gpointer bp_trigger_page;
56 guint8* bp_addrs [MONO_ZERO_LEN_ARRAY];
61 * floating point support: on ARM it is a mess, there are at least 3
62 * different setups, each of which binary incompat with the other.
63 * 1) FPA: old and ugly, but unfortunately what current distros use
64 * the double binary format has the two words swapped. 8 double registers.
65 * Implemented usually by kernel emulation.
66 * 2) softfloat: the compiler emulates all the fp ops. Usually uses the
67 * ugly swapped double format (I guess a softfloat-vfp exists, too, though).
68 * 3) VFP: the new and actually sensible and useful FP support. Implemented
69 * in HW or kernel-emulated, requires new tools. I think this is what symbian uses.
71 * The plan is to write the FPA support first. softfloat can be tested in a chroot.
73 int mono_exc_esp_offset = 0;
75 #define arm_is_imm12(v) ((v) > -4096 && (v) < 4096)
76 #define arm_is_imm8(v) ((v) > -256 && (v) < 256)
77 #define arm_is_fpimm8(v) ((v) >= -1020 && (v) <= 1020)
79 #define LDR_MASK ((0xf << ARMCOND_SHIFT) | (3 << 26) | (1 << 22) | (1 << 20) | (15 << 12))
80 #define LDR_PC_VAL ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 26) | (0 << 22) | (1 << 20) | (15 << 12))
81 #define IS_LDR_PC(val) (((val) & LDR_MASK) == LDR_PC_VAL)
83 #define ADD_LR_PC_4 ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 25) | (1 << 23) | (ARMREG_PC << 16) | (ARMREG_LR << 12) | 4)
84 #define MOV_LR_PC ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 24) | (0xa << 20) | (ARMREG_LR << 12) | ARMREG_PC)
88 mono_arch_regname (int reg)
90 static const char * rnames[] = {
91 "arm_r0", "arm_r1", "arm_r2", "arm_r3", "arm_v1",
92 "arm_v2", "arm_v3", "arm_v4", "arm_v5", "arm_v6",
93 "arm_v7", "arm_fp", "arm_ip", "arm_sp", "arm_lr",
96 if (reg >= 0 && reg < 16)
102 mono_arch_fregname (int reg)
104 static const char * rnames[] = {
105 "arm_f0", "arm_f1", "arm_f2", "arm_f3", "arm_f4",
106 "arm_f5", "arm_f6", "arm_f7", "arm_f8", "arm_f9",
107 "arm_f10", "arm_f11", "arm_f12", "arm_f13", "arm_f14",
108 "arm_f15", "arm_f16", "arm_f17", "arm_f18", "arm_f19",
109 "arm_f20", "arm_f21", "arm_f22", "arm_f23", "arm_f24",
110 "arm_f25", "arm_f26", "arm_f27", "arm_f28", "arm_f29",
113 if (reg >= 0 && reg < 32)
119 emit_big_add (guint8 *code, int dreg, int sreg, int imm)
121 int imm8, rot_amount;
122 if ((imm8 = mono_arm_is_rotated_imm8 (imm, &rot_amount)) >= 0) {
123 ARM_ADD_REG_IMM (code, dreg, sreg, imm8, rot_amount);
126 g_assert (dreg != sreg);
127 code = mono_arm_emit_load_imm (code, dreg, imm);
128 ARM_ADD_REG_REG (code, dreg, dreg, sreg);
133 emit_memcpy (guint8 *code, int size, int dreg, int doffset, int sreg, int soffset)
135 /* we can use r0-r3, since this is called only for incoming args on the stack */
136 if (size > sizeof (gpointer) * 4) {
138 code = emit_big_add (code, ARMREG_R0, sreg, soffset);
139 code = emit_big_add (code, ARMREG_R1, dreg, doffset);
140 start_loop = code = mono_arm_emit_load_imm (code, ARMREG_R2, size);
141 ARM_LDR_IMM (code, ARMREG_R3, ARMREG_R0, 0);
142 ARM_STR_IMM (code, ARMREG_R3, ARMREG_R1, 0);
143 ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_R0, 4);
144 ARM_ADD_REG_IMM8 (code, ARMREG_R1, ARMREG_R1, 4);
145 ARM_SUBS_REG_IMM8 (code, ARMREG_R2, ARMREG_R2, 4);
146 ARM_B_COND (code, ARMCOND_NE, 0);
147 arm_patch (code - 4, start_loop);
150 if (arm_is_imm12 (doffset) && arm_is_imm12 (doffset + size) &&
151 arm_is_imm12 (soffset) && arm_is_imm12 (soffset + size)) {
153 ARM_LDR_IMM (code, ARMREG_LR, sreg, soffset);
154 ARM_STR_IMM (code, ARMREG_LR, dreg, doffset);
160 code = emit_big_add (code, ARMREG_R0, sreg, soffset);
161 code = emit_big_add (code, ARMREG_R1, dreg, doffset);
162 doffset = soffset = 0;
164 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_R0, soffset);
165 ARM_STR_IMM (code, ARMREG_LR, ARMREG_R1, doffset);
171 g_assert (size == 0);
176 emit_call_reg (guint8 *code, int reg)
179 ARM_BLX_REG (code, reg);
181 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
185 ARM_MOV_REG_REG (code, ARMREG_PC, reg);
191 emit_call_seq (MonoCompile *cfg, guint8 *code)
193 if (cfg->method->dynamic) {
194 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
196 *(gpointer*)code = NULL;
198 code = emit_call_reg (code, ARMREG_IP);
206 emit_move_return_value (MonoCompile *cfg, MonoInst *ins, guint8 *code)
208 switch (ins->opcode) {
211 case OP_FCALL_MEMBASE:
213 if (ins->dreg != ARM_FPA_F0)
214 ARM_MVFD (code, ins->dreg, ARM_FPA_F0);
215 #elif defined(ARM_FPU_VFP)
216 if (((MonoCallInst*)ins)->signature->ret->type == MONO_TYPE_R4) {
217 ARM_FMSR (code, ins->dreg, ARMREG_R0);
218 ARM_CVTS (code, ins->dreg, ins->dreg);
220 ARM_FMDRR (code, ARMREG_R0, ARMREG_R1, ins->dreg);
230 * mono_arch_get_argument_info:
231 * @csig: a method signature
232 * @param_count: the number of parameters to consider
233 * @arg_info: an array to store the result infos
235 * Gathers information on parameters such as size, alignment and
236 * padding. arg_info should be large enought to hold param_count + 1 entries.
238 * Returns the size of the activation frame.
241 mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJitArgumentInfo *arg_info)
243 int k, frame_size = 0;
244 guint32 size, align, pad;
247 if (MONO_TYPE_ISSTRUCT (csig->ret)) {
248 frame_size += sizeof (gpointer);
252 arg_info [0].offset = offset;
255 frame_size += sizeof (gpointer);
259 arg_info [0].size = frame_size;
261 for (k = 0; k < param_count; k++) {
262 size = mini_type_stack_size_full (NULL, csig->params [k], &align, csig->pinvoke);
264 /* ignore alignment for now */
267 frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1);
268 arg_info [k].pad = pad;
270 arg_info [k + 1].pad = 0;
271 arg_info [k + 1].size = size;
273 arg_info [k + 1].offset = offset;
277 align = MONO_ARCH_FRAME_ALIGNMENT;
278 frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1);
279 arg_info [k].pad = pad;
285 decode_vcall_slot_from_ldr (guint32 ldr, mgreg_t *regs, int *displacement)
289 reg = (ldr >> 16 ) & 0xf;
290 offset = ldr & 0xfff;
291 if (((ldr >> 23) & 1) == 0) /*U bit, 0 means negative and 1 positive*/
293 /*g_print ("found vcall at r%d + %d for code at %p 0x%x\n", reg, offset, code, *code);*/
294 o = (gpointer)regs [reg];
296 *displacement = offset;
301 mono_arch_get_vcall_slot (guint8 *code_ptr, mgreg_t *regs, int *displacement)
303 guint32* code = (guint32*)code_ptr;
305 /* Locate the address of the method-specific trampoline. The call using
306 the vtable slot that took the processing flow to 'arch_create_jit_trampoline'
307 looks something like this:
316 The call sequence could be also:
319 function pointer literal
323 Note that on ARM5+ we can use one instruction instead of the last two.
324 Therefore, we need to locate the 'ldr rA' instruction to know which
325 register was used to hold the method addrs.
328 /* This is the instruction after "ldc pc, xxx", "mov pc, xxx" or "bl xxx" could be either the IMT value or some other instruction*/
331 /* Three possible code sequences can happen here:
335 * ldr pc, [rX - #offset]
341 * ldr pc, [rX - #offset]
343 * direct branch with bl:
347 * direct branch with mov:
351 * We only need to identify interface and virtual calls, the others can be ignored.
354 if (IS_LDR_PC (code [-1]) && code [-2] == ADD_LR_PC_4)
355 return decode_vcall_slot_from_ldr (code [-1], regs, displacement);
357 if (IS_LDR_PC (code [0]) && code [-1] == MOV_LR_PC)
358 return decode_vcall_slot_from_ldr (code [0], regs, displacement);
363 #define MAX_ARCH_DELEGATE_PARAMS 3
366 get_delegate_invoke_impl (gboolean has_target, gboolean param_count, guint32 *code_size)
368 guint8 *code, *start;
371 start = code = mono_global_codeman_reserve (12);
373 /* Replace the this argument with the target */
374 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, method_ptr));
375 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, target));
376 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
378 g_assert ((code - start) <= 12);
380 mono_arch_flush_icache (start, 12);
384 size = 8 + param_count * 4;
385 start = code = mono_global_codeman_reserve (size);
387 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, method_ptr));
388 /* slide down the arguments */
389 for (i = 0; i < param_count; ++i) {
390 ARM_MOV_REG_REG (code, (ARMREG_R0 + i), (ARMREG_R0 + i + 1));
392 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
394 g_assert ((code - start) <= size);
396 mono_arch_flush_icache (start, size);
400 *code_size = code - start;
406 * mono_arch_get_delegate_invoke_impls:
408 * Return a list of MonoAotTrampInfo structures for the delegate invoke impl
412 mono_arch_get_delegate_invoke_impls (void)
419 code = get_delegate_invoke_impl (TRUE, 0, &code_len);
420 res = g_slist_prepend (res, mono_aot_tramp_info_create (g_strdup ("delegate_invoke_impl_has_target"), code, code_len));
422 for (i = 0; i <= MAX_ARCH_DELEGATE_PARAMS; ++i) {
423 code = get_delegate_invoke_impl (FALSE, i, &code_len);
424 res = g_slist_prepend (res, mono_aot_tramp_info_create (g_strdup_printf ("delegate_invoke_impl_target_%d", i), code, code_len));
431 mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target)
433 guint8 *code, *start;
435 /* FIXME: Support more cases */
436 if (MONO_TYPE_ISSTRUCT (sig->ret))
440 static guint8* cached = NULL;
441 mono_mini_arch_lock ();
443 mono_mini_arch_unlock ();
448 start = mono_aot_get_named_code ("delegate_invoke_impl_has_target");
450 start = get_delegate_invoke_impl (TRUE, 0, NULL);
452 mono_mini_arch_unlock ();
455 static guint8* cache [MAX_ARCH_DELEGATE_PARAMS + 1] = {NULL};
458 if (sig->param_count > MAX_ARCH_DELEGATE_PARAMS)
460 for (i = 0; i < sig->param_count; ++i)
461 if (!mono_is_regsize_var (sig->params [i]))
464 mono_mini_arch_lock ();
465 code = cache [sig->param_count];
467 mono_mini_arch_unlock ();
472 char *name = g_strdup_printf ("delegate_invoke_impl_target_%d", sig->param_count);
473 start = mono_aot_get_named_code (name);
476 start = get_delegate_invoke_impl (FALSE, sig->param_count, NULL);
478 cache [sig->param_count] = start;
479 mono_mini_arch_unlock ();
487 mono_arch_get_this_arg_from_call (MonoGenericSharingContext *gsctx, MonoMethodSignature *sig, mgreg_t *regs, guint8 *code)
489 /* FIXME: handle returning a struct */
490 if (MONO_TYPE_ISSTRUCT (sig->ret))
491 return (gpointer)regs [ARMREG_R1];
492 return (gpointer)regs [ARMREG_R0];
496 * Initialize the cpu to execute managed code.
499 mono_arch_cpu_init (void)
504 * Initialize architecture specific code.
507 mono_arch_init (void)
509 InitializeCriticalSection (&mini_arch_mutex);
511 ss_trigger_page = mono_valloc (NULL, mono_pagesize (), MONO_MMAP_READ|MONO_MMAP_32BIT);
512 bp_trigger_page = mono_valloc (NULL, mono_pagesize (), MONO_MMAP_READ|MONO_MMAP_32BIT);
513 mono_mprotect (bp_trigger_page, mono_pagesize (), 0);
517 * Cleanup architecture specific code.
520 mono_arch_cleanup (void)
525 * This function returns the optimizations supported on this cpu.
528 mono_arch_cpu_optimizazions (guint32 *exclude_mask)
532 thumb_supported = TRUE;
537 FILE *file = fopen ("/proc/cpuinfo", "r");
539 while ((line = fgets (buf, 512, file))) {
540 if (strncmp (line, "Processor", 9) == 0) {
541 char *ver = strstr (line, "(v");
542 if (ver && (ver [2] == '5' || ver [2] == '6' || ver [2] == '7'))
544 if (ver && (ver [2] == '7'))
548 if (strncmp (line, "Features", 8) == 0) {
549 char *th = strstr (line, "thumb");
551 thumb_supported = TRUE;
559 /*printf ("features: v5: %d, thumb: %d\n", v5_supported, thumb_supported);*/
563 /* no arm-specific optimizations yet */
569 is_regsize_var (MonoType *t) {
572 t = mini_type_get_underlying_type (NULL, t);
579 case MONO_TYPE_FNPTR:
581 case MONO_TYPE_OBJECT:
582 case MONO_TYPE_STRING:
583 case MONO_TYPE_CLASS:
584 case MONO_TYPE_SZARRAY:
585 case MONO_TYPE_ARRAY:
587 case MONO_TYPE_GENERICINST:
588 if (!mono_type_generic_inst_is_valuetype (t))
591 case MONO_TYPE_VALUETYPE:
598 mono_arch_get_allocatable_int_vars (MonoCompile *cfg)
603 for (i = 0; i < cfg->num_varinfo; i++) {
604 MonoInst *ins = cfg->varinfo [i];
605 MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
608 if (vmv->range.first_use.abs_pos >= vmv->range.last_use.abs_pos)
611 if (ins->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT) || (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG))
614 /* we can only allocate 32 bit values */
615 if (is_regsize_var (ins->inst_vtype)) {
616 g_assert (MONO_VARINFO (cfg, i)->reg == -1);
617 g_assert (i == vmv->idx);
618 vars = mono_varlist_insert_sorted (cfg, vars, vmv, FALSE);
625 #define USE_EXTRA_TEMPS 0
628 mono_arch_get_global_int_regs (MonoCompile *cfg)
633 * FIXME: Interface calls might go through a static rgctx trampoline which
634 * sets V5, but it doesn't save it, so we need to save it ourselves, and
637 if (cfg->flags & MONO_CFG_HAS_CALLS)
638 cfg->uses_rgctx_reg = TRUE;
640 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V1));
641 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V2));
642 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V3));
643 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V4));
644 if (!(cfg->compile_aot || cfg->uses_rgctx_reg))
645 /* V5 is reserved for passing the vtable/rgctx/IMT method */
646 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V5));
647 /*regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V6));*/
648 /*regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V7));*/
654 * mono_arch_regalloc_cost:
656 * Return the cost, in number of memory references, of the action of
657 * allocating the variable VMV into a register during global register
661 mono_arch_regalloc_cost (MonoCompile *cfg, MonoMethodVar *vmv)
667 #ifndef __GNUC_PREREQ
668 #define __GNUC_PREREQ(maj, min) (0)
672 mono_arch_flush_icache (guint8 *code, gint size)
675 sys_icache_invalidate (code, size);
676 #elif __GNUC_PREREQ(4, 1)
677 __clear_cache (code, code + size);
678 #elif defined(PLATFORM_ANDROID)
679 const int syscall = 0xf0002;
687 : "r" (code), "r" (code + size), "r" (syscall)
688 : "r0", "r1", "r7", "r2"
691 __asm __volatile ("mov r0, %0\n"
694 "swi 0x9f0002 @ sys_cacheflush"
696 : "r" (code), "r" (code + size), "r" (0)
697 : "r0", "r1", "r3" );
714 guint16 vtsize; /* in param area */
717 guint8 size : 4; /* 1, 2, 4, 8, or regs used by RegTypeStructByVal */
724 gboolean vtype_retaddr;
733 /*#define __alignof__(a) sizeof(a)*/
734 #define __alignof__(type) G_STRUCT_OFFSET(struct { char c; type x; }, x)
740 add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple)
743 if (*gr > ARMREG_R3) {
744 ainfo->offset = *stack_size;
745 ainfo->reg = ARMREG_SP; /* in the caller */
746 ainfo->storage = RegTypeBase;
749 ainfo->storage = RegTypeGeneral;
753 #if defined(__APPLE__) && defined(MONO_CROSS_COMPILE)
756 int i8_align = __alignof__ (gint64);
760 gboolean split = i8_align == 4;
762 gboolean split = TRUE;
765 if (*gr == ARMREG_R3 && split) {
766 /* first word in r3 and the second on the stack */
767 ainfo->offset = *stack_size;
768 ainfo->reg = ARMREG_SP; /* in the caller */
769 ainfo->storage = RegTypeBaseGen;
771 } else if (*gr >= ARMREG_R3) {
773 /* darwin aligns longs to 4 byte only */
779 ainfo->offset = *stack_size;
780 ainfo->reg = ARMREG_SP; /* in the caller */
781 ainfo->storage = RegTypeBase;
785 if (i8_align == 8 && ((*gr) & 1))
788 ainfo->storage = RegTypeIRegPair;
797 get_call_info (MonoMemPool *mp, MonoMethodSignature *sig, gboolean is_pinvoke)
800 int n = sig->hasthis + sig->param_count;
801 MonoType *simpletype;
802 guint32 stack_size = 0;
806 cinfo = mono_mempool_alloc0 (mp, sizeof (CallInfo) + (sizeof (ArgInfo) * n));
808 cinfo = g_malloc0 (sizeof (CallInfo) + (sizeof (ArgInfo) * n));
813 /* FIXME: handle returning a struct */
814 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
817 if (is_pinvoke && mono_class_native_size (mono_class_from_mono_type (sig->ret), &align) <= sizeof (gpointer)) {
818 cinfo->ret.storage = RegTypeStructByVal;
820 add_general (&gr, &stack_size, &cinfo->ret, TRUE);
821 cinfo->struct_ret = ARMREG_R0;
822 cinfo->vtype_retaddr = TRUE;
828 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
831 DEBUG(printf("params: %d\n", sig->param_count));
832 for (i = 0; i < sig->param_count; ++i) {
833 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
834 /* Prevent implicit arguments and sig_cookie from
835 being passed in registers */
837 /* Emit the signature cookie just before the implicit arguments */
838 add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE);
840 DEBUG(printf("param %d: ", i));
841 if (sig->params [i]->byref) {
842 DEBUG(printf("byref\n"));
843 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
847 simpletype = mini_type_get_underlying_type (NULL, sig->params [i]);
848 switch (simpletype->type) {
849 case MONO_TYPE_BOOLEAN:
852 cinfo->args [n].size = 1;
853 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
859 cinfo->args [n].size = 2;
860 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
865 cinfo->args [n].size = 4;
866 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
872 case MONO_TYPE_FNPTR:
873 case MONO_TYPE_CLASS:
874 case MONO_TYPE_OBJECT:
875 case MONO_TYPE_STRING:
876 case MONO_TYPE_SZARRAY:
877 case MONO_TYPE_ARRAY:
879 cinfo->args [n].size = sizeof (gpointer);
880 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
883 case MONO_TYPE_GENERICINST:
884 if (!mono_type_generic_inst_is_valuetype (sig->params [i])) {
885 cinfo->args [n].size = sizeof (gpointer);
886 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
891 case MONO_TYPE_TYPEDBYREF:
892 case MONO_TYPE_VALUETYPE: {
898 if (simpletype->type == MONO_TYPE_TYPEDBYREF) {
899 size = sizeof (MonoTypedRef);
900 align = sizeof (gpointer);
902 MonoClass *klass = mono_class_from_mono_type (sig->params [i]);
904 size = mono_class_native_size (klass, &align);
906 size = mono_class_value_size (klass, &align);
908 DEBUG(printf ("load %d bytes struct\n",
909 mono_class_native_size (sig->params [i]->data.klass, NULL)));
912 align_size += (sizeof (gpointer) - 1);
913 align_size &= ~(sizeof (gpointer) - 1);
914 nwords = (align_size + sizeof (gpointer) -1 ) / sizeof (gpointer);
915 cinfo->args [n].storage = RegTypeStructByVal;
916 /* FIXME: align stack_size if needed */
918 if (align >= 8 && (gr & 1))
921 if (gr > ARMREG_R3) {
922 cinfo->args [n].size = 0;
923 cinfo->args [n].vtsize = nwords;
925 int rest = ARMREG_R3 - gr + 1;
926 int n_in_regs = rest >= nwords? nwords: rest;
928 cinfo->args [n].size = n_in_regs;
929 cinfo->args [n].vtsize = nwords - n_in_regs;
930 cinfo->args [n].reg = gr;
934 cinfo->args [n].offset = stack_size;
935 /*g_print ("offset for arg %d at %d\n", n, stack_size);*/
936 stack_size += nwords * sizeof (gpointer);
943 cinfo->args [n].size = 8;
944 add_general (&gr, &stack_size, cinfo->args + n, FALSE);
948 g_error ("Can't trampoline 0x%x", sig->params [i]->type);
953 simpletype = mini_type_get_underlying_type (NULL, sig->ret);
954 switch (simpletype->type) {
955 case MONO_TYPE_BOOLEAN:
966 case MONO_TYPE_FNPTR:
967 case MONO_TYPE_CLASS:
968 case MONO_TYPE_OBJECT:
969 case MONO_TYPE_SZARRAY:
970 case MONO_TYPE_ARRAY:
971 case MONO_TYPE_STRING:
972 cinfo->ret.storage = RegTypeGeneral;
973 cinfo->ret.reg = ARMREG_R0;
977 cinfo->ret.storage = RegTypeIRegPair;
978 cinfo->ret.reg = ARMREG_R0;
982 cinfo->ret.storage = RegTypeFP;
983 cinfo->ret.reg = ARMREG_R0;
984 /* FIXME: cinfo->ret.reg = ???;
985 cinfo->ret.storage = RegTypeFP;*/
987 case MONO_TYPE_GENERICINST:
988 if (!mono_type_generic_inst_is_valuetype (sig->ret)) {
989 cinfo->ret.storage = RegTypeGeneral;
990 cinfo->ret.reg = ARMREG_R0;
994 case MONO_TYPE_VALUETYPE:
995 case MONO_TYPE_TYPEDBYREF:
996 if (cinfo->ret.storage != RegTypeStructByVal)
997 cinfo->ret.storage = RegTypeStructByAddr;
1002 g_error ("Can't handle as return value 0x%x", sig->ret->type);
1006 /* align stack size to 8 */
1007 DEBUG (printf (" stack size: %d (%d)\n", (stack_size + 15) & ~15, stack_size));
1008 stack_size = (stack_size + 7) & ~7;
1010 cinfo->stack_usage = stack_size;
1016 * Set var information according to the calling convention. arm version.
1017 * The locals var stuff should most likely be split in another method.
1020 mono_arch_allocate_vars (MonoCompile *cfg)
1022 MonoMethodSignature *sig;
1023 MonoMethodHeader *header;
1025 int i, offset, size, align, curinst;
1026 int frame_reg = ARMREG_FP;
1029 sig = mono_method_signature (cfg->method);
1031 if (!cfg->arch.cinfo)
1032 cfg->arch.cinfo = get_call_info (cfg->mempool, sig, sig->pinvoke);
1033 cinfo = cfg->arch.cinfo;
1035 /* FIXME: this will change when we use FP as gcc does */
1036 cfg->flags |= MONO_CFG_HAS_SPILLUP;
1038 /* allow room for the vararg method args: void* and long/double */
1039 if (mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method))
1040 cfg->param_area = MAX (cfg->param_area, sizeof (gpointer)*8);
1042 header = mono_method_get_header (cfg->method);
1045 * We use the frame register also for any method that has
1046 * exception clauses. This way, when the handlers are called,
1047 * the code will reference local variables using the frame reg instead of
1048 * the stack pointer: if we had to restore the stack pointer, we'd
1049 * corrupt the method frames that are already on the stack (since
1050 * filters get called before stack unwinding happens) when the filter
1051 * code would call any method (this also applies to finally etc.).
1053 if ((cfg->flags & MONO_CFG_HAS_ALLOCA) || header->num_clauses)
1054 frame_reg = ARMREG_FP;
1055 cfg->frame_reg = frame_reg;
1056 if (frame_reg != ARMREG_SP) {
1057 cfg->used_int_regs |= 1 << frame_reg;
1060 if (cfg->compile_aot || cfg->uses_rgctx_reg)
1061 /* V5 is reserved for passing the vtable/rgctx/IMT method */
1062 cfg->used_int_regs |= (1 << ARMREG_V5);
1066 if (!MONO_TYPE_ISSTRUCT (sig->ret)) {
1067 switch (mini_type_get_underlying_type (NULL, sig->ret)->type) {
1068 case MONO_TYPE_VOID:
1071 cfg->ret->opcode = OP_REGVAR;
1072 cfg->ret->inst_c0 = ARMREG_R0;
1076 /* local vars are at a positive offset from the stack pointer */
1078 * also note that if the function uses alloca, we use FP
1079 * to point at the local variables.
1081 offset = 0; /* linkage area */
1082 /* align the offset to 16 bytes: not sure this is needed here */
1084 //offset &= ~(8 - 1);
1086 /* add parameter area size for called functions */
1087 offset += cfg->param_area;
1090 if (cfg->flags & MONO_CFG_HAS_FPOUT)
1093 /* allow room to save the return value */
1094 if (mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method))
1097 /* the MonoLMF structure is stored just below the stack pointer */
1099 if (sig->call_convention == MONO_CALL_VARARG) {
1100 cfg->sig_cookie = 0;
1103 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
1104 if (cinfo->ret.storage == RegTypeStructByVal) {
1105 cfg->ret->opcode = OP_REGOFFSET;
1106 cfg->ret->inst_basereg = cfg->frame_reg;
1107 offset += sizeof (gpointer) - 1;
1108 offset &= ~(sizeof (gpointer) - 1);
1109 cfg->ret->inst_offset = - offset;
1111 inst = cfg->vret_addr;
1112 offset += sizeof(gpointer) - 1;
1113 offset &= ~(sizeof(gpointer) - 1);
1114 inst->inst_offset = offset;
1115 inst->opcode = OP_REGOFFSET;
1116 inst->inst_basereg = frame_reg;
1117 if (G_UNLIKELY (cfg->verbose_level > 1)) {
1118 printf ("vret_addr =");
1119 mono_print_ins (cfg->vret_addr);
1122 offset += sizeof(gpointer);
1123 if (sig->call_convention == MONO_CALL_VARARG)
1124 cfg->sig_cookie += sizeof (gpointer);
1127 curinst = cfg->locals_start;
1128 for (i = curinst; i < cfg->num_varinfo; ++i) {
1129 inst = cfg->varinfo [i];
1130 if ((inst->flags & MONO_INST_IS_DEAD) || inst->opcode == OP_REGVAR)
1133 /* inst->backend.is_pinvoke indicates native sized value types, this is used by the
1134 * pinvoke wrappers when they call functions returning structure */
1135 if (inst->backend.is_pinvoke && MONO_TYPE_ISSTRUCT (inst->inst_vtype) && inst->inst_vtype->type != MONO_TYPE_TYPEDBYREF) {
1137 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), &ualign);
1141 size = mono_type_size (inst->inst_vtype, &align);
1143 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
1144 * since it loads/stores misaligned words, which don't do the right thing.
1146 if (align < 4 && size >= 4)
1148 offset += align - 1;
1149 offset &= ~(align - 1);
1150 inst->inst_offset = offset;
1151 inst->opcode = OP_REGOFFSET;
1152 inst->inst_basereg = frame_reg;
1154 //g_print ("allocating local %d to %d\n", i, inst->inst_offset);
1159 inst = cfg->args [curinst];
1160 if (inst->opcode != OP_REGVAR) {
1161 inst->opcode = OP_REGOFFSET;
1162 inst->inst_basereg = frame_reg;
1163 offset += sizeof (gpointer) - 1;
1164 offset &= ~(sizeof (gpointer) - 1);
1165 inst->inst_offset = offset;
1166 offset += sizeof (gpointer);
1167 if (sig->call_convention == MONO_CALL_VARARG)
1168 cfg->sig_cookie += sizeof (gpointer);
1173 for (i = 0; i < sig->param_count; ++i) {
1174 inst = cfg->args [curinst];
1175 if (inst->opcode != OP_REGVAR) {
1176 inst->opcode = OP_REGOFFSET;
1177 inst->inst_basereg = frame_reg;
1178 size = mini_type_stack_size_full (NULL, sig->params [i], NULL, sig->pinvoke);
1179 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
1180 * since it loads/stores misaligned words, which don't do the right thing.
1182 if (align < 4 && size >= 4)
1184 /* The code in the prolog () stores words when storing vtypes received in a register */
1185 if (MONO_TYPE_ISSTRUCT (sig->params [i]))
1187 offset += align - 1;
1188 offset &= ~(align - 1);
1189 inst->inst_offset = offset;
1191 if ((sig->call_convention == MONO_CALL_VARARG) && (i < sig->sentinelpos))
1192 cfg->sig_cookie += size;
1197 /* align the offset to 8 bytes */
1202 cfg->stack_offset = offset;
1206 mono_arch_create_vars (MonoCompile *cfg)
1208 MonoMethodSignature *sig;
1211 sig = mono_method_signature (cfg->method);
1213 if (!cfg->arch.cinfo)
1214 cfg->arch.cinfo = get_call_info (cfg->mempool, sig, sig->pinvoke);
1215 cinfo = cfg->arch.cinfo;
1217 if (cinfo->ret.storage == RegTypeStructByVal)
1218 cfg->ret_var_is_local = TRUE;
1220 if (MONO_TYPE_ISSTRUCT (sig->ret) && cinfo->ret.storage != RegTypeStructByVal) {
1221 cfg->vret_addr = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_ARG);
1222 if (G_UNLIKELY (cfg->verbose_level > 1)) {
1223 printf ("vret_addr = ");
1224 mono_print_ins (cfg->vret_addr);
1228 if (cfg->gen_seq_points && cfg->compile_aot) {
1229 MonoInst *ins = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1230 ins->flags |= MONO_INST_VOLATILE;
1231 cfg->arch.seq_point_info_var = ins;
1233 /* Allocate a separate variable for this to save 1 load per seq point */
1234 ins = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1235 ins->flags |= MONO_INST_VOLATILE;
1236 cfg->arch.ss_trigger_page_var = ins;
1241 mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
1244 MonoMethodSignature *sig;
1248 sig = call->signature;
1249 n = sig->param_count + sig->hasthis;
1251 cinfo = get_call_info (NULL, sig, sig->pinvoke);
1253 for (i = 0; i < n; ++i) {
1254 ArgInfo *ainfo = cinfo->args + i;
1257 if (i >= sig->hasthis)
1258 t = sig->params [i - sig->hasthis];
1260 t = &mono_defaults.int_class->byval_arg;
1261 t = mini_type_get_underlying_type (NULL, t);
1263 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
1268 in = call->args [i];
1270 switch (ainfo->storage) {
1271 case RegTypeGeneral:
1272 case RegTypeIRegPair:
1273 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1274 MONO_INST_NEW (cfg, ins, OP_MOVE);
1275 ins->dreg = mono_alloc_ireg (cfg);
1276 ins->sreg1 = in->dreg + 1;
1277 MONO_ADD_INS (cfg->cbb, ins);
1278 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1280 MONO_INST_NEW (cfg, ins, OP_MOVE);
1281 ins->dreg = mono_alloc_ireg (cfg);
1282 ins->sreg1 = in->dreg + 2;
1283 MONO_ADD_INS (cfg->cbb, ins);
1284 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg + 1, FALSE);
1285 } else if (!t->byref && ((t->type == MONO_TYPE_R8) || (t->type == MONO_TYPE_R4))) {
1286 #ifndef MONO_ARCH_SOFT_FLOAT
1290 if (ainfo->size == 4) {
1291 #ifdef MONO_ARCH_SOFT_FLOAT
1292 /* mono_emit_call_args () have already done the r8->r4 conversion */
1293 /* The converted value is in an int vreg */
1294 MONO_INST_NEW (cfg, ins, OP_MOVE);
1295 ins->dreg = mono_alloc_ireg (cfg);
1296 ins->sreg1 = in->dreg;
1297 MONO_ADD_INS (cfg->cbb, ins);
1298 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1300 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1301 creg = mono_alloc_ireg (cfg);
1302 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1303 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg, FALSE);
1306 #ifdef MONO_ARCH_SOFT_FLOAT
1307 MONO_INST_NEW (cfg, ins, OP_FGETLOW32);
1308 ins->dreg = mono_alloc_ireg (cfg);
1309 ins->sreg1 = in->dreg;
1310 MONO_ADD_INS (cfg->cbb, ins);
1311 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1313 MONO_INST_NEW (cfg, ins, OP_FGETHIGH32);
1314 ins->dreg = mono_alloc_ireg (cfg);
1315 ins->sreg1 = in->dreg;
1316 MONO_ADD_INS (cfg->cbb, ins);
1317 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg + 1, FALSE);
1319 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1320 creg = mono_alloc_ireg (cfg);
1321 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1322 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg, FALSE);
1323 creg = mono_alloc_ireg (cfg);
1324 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8 + 4));
1325 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg + 1, FALSE);
1328 cfg->flags |= MONO_CFG_HAS_FPOUT;
1330 MONO_INST_NEW (cfg, ins, OP_MOVE);
1331 ins->dreg = mono_alloc_ireg (cfg);
1332 ins->sreg1 = in->dreg;
1333 MONO_ADD_INS (cfg->cbb, ins);
1335 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1338 case RegTypeStructByAddr:
1341 /* FIXME: where si the data allocated? */
1342 arg->backend.reg3 = ainfo->reg;
1343 call->used_iregs |= 1 << ainfo->reg;
1344 g_assert_not_reached ();
1347 case RegTypeStructByVal:
1348 MONO_INST_NEW (cfg, ins, OP_OUTARG_VT);
1349 ins->opcode = OP_OUTARG_VT;
1350 ins->sreg1 = in->dreg;
1351 ins->klass = in->klass;
1352 ins->inst_p0 = call;
1353 ins->inst_p1 = mono_mempool_alloc (cfg->mempool, sizeof (ArgInfo));
1354 memcpy (ins->inst_p1, ainfo, sizeof (ArgInfo));
1355 MONO_ADD_INS (cfg->cbb, ins);
1358 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1359 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1360 } else if (!t->byref && ((t->type == MONO_TYPE_R4) || (t->type == MONO_TYPE_R8))) {
1361 if (t->type == MONO_TYPE_R8) {
1362 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1364 #ifdef MONO_ARCH_SOFT_FLOAT
1365 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1367 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1371 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1374 case RegTypeBaseGen:
1375 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1376 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, (G_BYTE_ORDER == G_BIG_ENDIAN) ? in->dreg + 1 : in->dreg + 2);
1377 MONO_INST_NEW (cfg, ins, OP_MOVE);
1378 ins->dreg = mono_alloc_ireg (cfg);
1379 ins->sreg1 = G_BYTE_ORDER == G_BIG_ENDIAN ? in->dreg + 2 : in->dreg + 1;
1380 MONO_ADD_INS (cfg->cbb, ins);
1381 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ARMREG_R3, FALSE);
1382 } else if (!t->byref && (t->type == MONO_TYPE_R8)) {
1385 #ifdef MONO_ARCH_SOFT_FLOAT
1386 g_assert_not_reached ();
1389 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1390 creg = mono_alloc_ireg (cfg);
1391 mono_call_inst_add_outarg_reg (cfg, call, creg, ARMREG_R3, FALSE);
1392 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1393 creg = mono_alloc_ireg (cfg);
1394 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 4));
1395 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, creg);
1396 cfg->flags |= MONO_CFG_HAS_FPOUT;
1398 g_assert_not_reached ();
1405 arg->backend.reg3 = ainfo->reg;
1406 /* FP args are passed in int regs */
1407 call->used_iregs |= 1 << ainfo->reg;
1408 if (ainfo->size == 8) {
1409 arg->opcode = OP_OUTARG_R8;
1410 call->used_iregs |= 1 << (ainfo->reg + 1);
1412 arg->opcode = OP_OUTARG_R4;
1415 cfg->flags |= MONO_CFG_HAS_FPOUT;
1419 g_assert_not_reached ();
1423 if (sig->ret && MONO_TYPE_ISSTRUCT (sig->ret)) {
1426 if (cinfo->ret.storage == RegTypeStructByVal) {
1427 /* The JIT will transform this into a normal call */
1428 call->vret_in_reg = TRUE;
1430 MONO_INST_NEW (cfg, vtarg, OP_MOVE);
1431 vtarg->sreg1 = call->vret_var->dreg;
1432 vtarg->dreg = mono_alloc_preg (cfg);
1433 MONO_ADD_INS (cfg->cbb, vtarg);
1435 mono_call_inst_add_outarg_reg (cfg, call, vtarg->dreg, cinfo->ret.reg, FALSE);
1439 call->stack_usage = cinfo->stack_usage;
1445 mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src)
1447 MonoCallInst *call = (MonoCallInst*)ins->inst_p0;
1448 ArgInfo *ainfo = ins->inst_p1;
1449 int ovf_size = ainfo->vtsize;
1450 int doffset = ainfo->offset;
1451 int i, soffset, dreg;
1454 for (i = 0; i < ainfo->size; ++i) {
1455 dreg = mono_alloc_ireg (cfg);
1456 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, src->dreg, soffset);
1457 mono_call_inst_add_outarg_reg (cfg, call, dreg, ainfo->reg + i, FALSE);
1458 soffset += sizeof (gpointer);
1460 //g_print ("vt size: %d at R%d + %d\n", doffset, vt->inst_basereg, vt->inst_offset);
1462 mini_emit_memcpy (cfg, ARMREG_SP, doffset, src->dreg, soffset, ovf_size * sizeof (gpointer), 0);
1466 mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val)
1468 MonoType *ret = mini_type_get_underlying_type (cfg->generic_sharing_context, mono_method_signature (method)->ret);
1471 if (ret->type == MONO_TYPE_I8 || ret->type == MONO_TYPE_U8) {
1474 MONO_INST_NEW (cfg, ins, OP_SETLRET);
1475 ins->sreg1 = val->dreg + 1;
1476 ins->sreg2 = val->dreg + 2;
1477 MONO_ADD_INS (cfg->cbb, ins);
1480 #ifdef MONO_ARCH_SOFT_FLOAT
1481 if (ret->type == MONO_TYPE_R8) {
1484 MONO_INST_NEW (cfg, ins, OP_SETFRET);
1485 ins->dreg = cfg->ret->dreg;
1486 ins->sreg1 = val->dreg;
1487 MONO_ADD_INS (cfg->cbb, ins);
1490 if (ret->type == MONO_TYPE_R4) {
1491 /* Already converted to an int in method_to_ir () */
1492 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
1495 #elif defined(ARM_FPU_VFP)
1496 if (ret->type == MONO_TYPE_R8 || ret->type == MONO_TYPE_R4) {
1499 MONO_INST_NEW (cfg, ins, OP_SETFRET);
1500 ins->dreg = cfg->ret->dreg;
1501 ins->sreg1 = val->dreg;
1502 MONO_ADD_INS (cfg->cbb, ins);
1506 if (ret->type == MONO_TYPE_R4 || ret->type == MONO_TYPE_R8) {
1507 MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, cfg->ret->dreg, val->dreg);
1514 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
1518 mono_arch_is_inst_imm (gint64 imm)
1523 #define DYN_CALL_STACK_ARGS 6
1526 MonoMethodSignature *sig;
1531 mgreg_t regs [PARAM_REGS + DYN_CALL_STACK_ARGS];
1537 dyn_call_supported (CallInfo *cinfo, MonoMethodSignature *sig)
1541 if (sig->hasthis + sig->param_count > PARAM_REGS + DYN_CALL_STACK_ARGS)
1544 switch (cinfo->ret.storage) {
1546 case RegTypeGeneral:
1547 case RegTypeIRegPair:
1548 case RegTypeStructByAddr:
1553 #elif defined(ARM_FPU_VFP)
1562 for (i = 0; i < cinfo->nargs; ++i) {
1563 switch (cinfo->args [i].storage) {
1564 case RegTypeGeneral:
1566 case RegTypeIRegPair:
1569 if (cinfo->args [i].offset >= (DYN_CALL_STACK_ARGS * sizeof (gpointer)))
1572 case RegTypeStructByVal:
1573 if (cinfo->args [i].reg + cinfo->args [i].vtsize >= PARAM_REGS + DYN_CALL_STACK_ARGS)
1581 // FIXME: Can't use cinfo only as it doesn't contain info about I8/float */
1582 for (i = 0; i < sig->param_count; ++i) {
1583 MonoType *t = sig->params [i];
1591 #ifdef MONO_ARCH_SOFT_FLOAT
1610 mono_arch_dyn_call_prepare (MonoMethodSignature *sig)
1612 ArchDynCallInfo *info;
1615 cinfo = get_call_info (NULL, sig, FALSE);
1617 if (!dyn_call_supported (cinfo, sig)) {
1622 info = g_new0 (ArchDynCallInfo, 1);
1623 // FIXME: Preprocess the info to speed up start_dyn_call ()
1625 info->cinfo = cinfo;
1627 return (MonoDynCallInfo*)info;
1631 mono_arch_dyn_call_free (MonoDynCallInfo *info)
1633 ArchDynCallInfo *ainfo = (ArchDynCallInfo*)info;
1635 g_free (ainfo->cinfo);
1640 mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf, int buf_len)
1642 ArchDynCallInfo *dinfo = (ArchDynCallInfo*)info;
1643 DynCallArgs *p = (DynCallArgs*)buf;
1644 int arg_index, greg, i, j;
1645 MonoMethodSignature *sig = dinfo->sig;
1647 g_assert (buf_len >= sizeof (DynCallArgs));
1655 if (dinfo->cinfo->vtype_retaddr)
1656 p->regs [greg ++] = (mgreg_t)ret;
1659 p->regs [greg ++] = (mgreg_t)*(args [arg_index ++]);
1661 for (i = 0; i < sig->param_count; i++) {
1662 MonoType *t = mono_type_get_underlying_type (sig->params [i]);
1663 gpointer *arg = args [arg_index ++];
1664 ArgInfo *ainfo = &dinfo->cinfo->args [i + sig->hasthis];
1667 if (ainfo->storage == RegTypeGeneral || ainfo->storage == RegTypeIRegPair || ainfo->storage == RegTypeStructByVal)
1669 else if (ainfo->storage == RegTypeBase)
1670 slot = PARAM_REGS + (ainfo->offset / 4);
1672 g_assert_not_reached ();
1675 p->regs [slot] = (mgreg_t)*arg;
1680 case MONO_TYPE_STRING:
1681 case MONO_TYPE_CLASS:
1682 case MONO_TYPE_ARRAY:
1683 case MONO_TYPE_SZARRAY:
1684 case MONO_TYPE_OBJECT:
1688 p->regs [slot] = (mgreg_t)*arg;
1690 case MONO_TYPE_BOOLEAN:
1692 p->regs [slot] = *(guint8*)arg;
1695 p->regs [slot] = *(gint8*)arg;
1698 p->regs [slot] = *(gint16*)arg;
1701 case MONO_TYPE_CHAR:
1702 p->regs [slot] = *(guint16*)arg;
1705 p->regs [slot] = *(gint32*)arg;
1708 p->regs [slot] = *(guint32*)arg;
1712 p->regs [slot ++] = (mgreg_t)arg [0];
1713 p->regs [slot] = (mgreg_t)arg [1];
1716 p->regs [slot] = *(mgreg_t*)arg;
1719 p->regs [slot ++] = (mgreg_t)arg [0];
1720 p->regs [slot] = (mgreg_t)arg [1];
1722 case MONO_TYPE_GENERICINST:
1723 if (MONO_TYPE_IS_REFERENCE (t)) {
1724 p->regs [slot] = (mgreg_t)*arg;
1729 case MONO_TYPE_VALUETYPE:
1730 g_assert (ainfo->storage == RegTypeStructByVal);
1732 if (ainfo->size == 0)
1733 slot = PARAM_REGS + (ainfo->offset / 4);
1737 for (j = 0; j < ainfo->size + ainfo->vtsize; ++j)
1738 p->regs [slot ++] = ((mgreg_t*)arg) [j];
1741 g_assert_not_reached ();
1747 mono_arch_finish_dyn_call (MonoDynCallInfo *info, guint8 *buf)
1749 ArchDynCallInfo *ainfo = (ArchDynCallInfo*)info;
1750 MonoMethodSignature *sig = ((ArchDynCallInfo*)info)->sig;
1751 guint8 *ret = ((DynCallArgs*)buf)->ret;
1752 mgreg_t res = ((DynCallArgs*)buf)->res;
1753 mgreg_t res2 = ((DynCallArgs*)buf)->res2;
1755 switch (mono_type_get_underlying_type (sig->ret)->type) {
1756 case MONO_TYPE_VOID:
1757 *(gpointer*)ret = NULL;
1759 case MONO_TYPE_STRING:
1760 case MONO_TYPE_CLASS:
1761 case MONO_TYPE_ARRAY:
1762 case MONO_TYPE_SZARRAY:
1763 case MONO_TYPE_OBJECT:
1767 *(gpointer*)ret = (gpointer)res;
1773 case MONO_TYPE_BOOLEAN:
1774 *(guint8*)ret = res;
1777 *(gint16*)ret = res;
1780 case MONO_TYPE_CHAR:
1781 *(guint16*)ret = res;
1784 *(gint32*)ret = res;
1787 *(guint32*)ret = res;
1791 /* This handles endianness as well */
1792 ((gint32*)ret) [0] = res;
1793 ((gint32*)ret) [1] = res2;
1795 case MONO_TYPE_GENERICINST:
1796 if (MONO_TYPE_IS_REFERENCE (sig->ret)) {
1797 *(gpointer*)ret = (gpointer)res;
1802 case MONO_TYPE_VALUETYPE:
1803 g_assert (ainfo->cinfo->vtype_retaddr);
1806 #if defined(ARM_FPU_VFP)
1808 *(float*)ret = *(float*)&res;
1810 case MONO_TYPE_R8: {
1816 *(double*)ret = *(double*)®s;
1821 g_assert_not_reached ();
1826 * Allow tracing to work with this interface (with an optional argument)
1830 mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
1834 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1835 ARM_MOV_REG_IMM8 (code, ARMREG_R1, 0); /* NULL ebp for now */
1836 code = mono_arm_emit_load_imm (code, ARMREG_R2, (guint32)func);
1837 code = emit_call_reg (code, ARMREG_R2);
1850 mono_arch_instrument_epilog_full (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments, gboolean preserve_argument_registers)
1853 int save_mode = SAVE_NONE;
1855 MonoMethod *method = cfg->method;
1856 int rtype = mini_type_get_underlying_type (cfg->generic_sharing_context, mono_method_signature (method)->ret)->type;
1857 int save_offset = cfg->param_area;
1861 offset = code - cfg->native_code;
1862 /* we need about 16 instructions */
1863 if (offset > (cfg->code_size - 16 * 4)) {
1864 cfg->code_size *= 2;
1865 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
1866 code = cfg->native_code + offset;
1869 case MONO_TYPE_VOID:
1870 /* special case string .ctor icall */
1871 if (strcmp (".ctor", method->name) && method->klass == mono_defaults.string_class)
1872 save_mode = SAVE_ONE;
1874 save_mode = SAVE_NONE;
1878 save_mode = SAVE_TWO;
1882 save_mode = SAVE_FP;
1884 case MONO_TYPE_VALUETYPE:
1885 save_mode = SAVE_STRUCT;
1888 save_mode = SAVE_ONE;
1892 switch (save_mode) {
1894 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1895 ARM_STR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1896 if (enable_arguments) {
1897 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_R1);
1898 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1902 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1903 if (enable_arguments) {
1904 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1908 /* FIXME: what reg? */
1909 if (enable_arguments) {
1910 /* FIXME: what reg? */
1914 if (enable_arguments) {
1915 /* FIXME: get the actual address */
1916 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1924 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1925 code = mono_arm_emit_load_imm (code, ARMREG_IP, (guint32)func);
1926 code = emit_call_reg (code, ARMREG_IP);
1928 switch (save_mode) {
1930 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1931 ARM_LDR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1934 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1948 * The immediate field for cond branches is big enough for all reasonable methods
1950 #define EMIT_COND_BRANCH_FLAGS(ins,condcode) \
1951 if (0 && ins->inst_true_bb->native_offset) { \
1952 ARM_B_COND (code, (condcode), (code - cfg->native_code + ins->inst_true_bb->native_offset) & 0xffffff); \
1954 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_true_bb); \
1955 ARM_B_COND (code, (condcode), 0); \
1958 #define EMIT_COND_BRANCH(ins,cond) EMIT_COND_BRANCH_FLAGS(ins, branch_cc_table [(cond)])
1960 /* emit an exception if condition is fail
1962 * We assign the extra code used to throw the implicit exceptions
1963 * to cfg->bb_exit as far as the big branch handling is concerned
1965 #define EMIT_COND_SYSTEM_EXCEPTION_FLAGS(condcode,exc_name) \
1967 mono_add_patch_info (cfg, code - cfg->native_code, \
1968 MONO_PATCH_INFO_EXC, exc_name); \
1969 ARM_BL_COND (code, (condcode), 0); \
1972 #define EMIT_COND_SYSTEM_EXCEPTION(cond,exc_name) EMIT_COND_SYSTEM_EXCEPTION_FLAGS(branch_cc_table [(cond)], (exc_name))
1975 mono_arch_peephole_pass_1 (MonoCompile *cfg, MonoBasicBlock *bb)
1980 mono_arch_peephole_pass_2 (MonoCompile *cfg, MonoBasicBlock *bb)
1982 MonoInst *ins, *n, *last_ins = NULL;
1984 MONO_BB_FOR_EACH_INS_SAFE (bb, n, ins) {
1985 switch (ins->opcode) {
1988 /* Already done by an arch-independent pass */
1990 case OP_LOAD_MEMBASE:
1991 case OP_LOADI4_MEMBASE:
1993 * OP_STORE_MEMBASE_REG reg, offset(basereg)
1994 * OP_LOAD_MEMBASE offset(basereg), reg
1996 if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG
1997 || last_ins->opcode == OP_STORE_MEMBASE_REG) &&
1998 ins->inst_basereg == last_ins->inst_destbasereg &&
1999 ins->inst_offset == last_ins->inst_offset) {
2000 if (ins->dreg == last_ins->sreg1) {
2001 MONO_DELETE_INS (bb, ins);
2004 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
2005 ins->opcode = OP_MOVE;
2006 ins->sreg1 = last_ins->sreg1;
2010 * Note: reg1 must be different from the basereg in the second load
2011 * OP_LOAD_MEMBASE offset(basereg), reg1
2012 * OP_LOAD_MEMBASE offset(basereg), reg2
2014 * OP_LOAD_MEMBASE offset(basereg), reg1
2015 * OP_MOVE reg1, reg2
2017 } if (last_ins && (last_ins->opcode == OP_LOADI4_MEMBASE
2018 || last_ins->opcode == OP_LOAD_MEMBASE) &&
2019 ins->inst_basereg != last_ins->dreg &&
2020 ins->inst_basereg == last_ins->inst_basereg &&
2021 ins->inst_offset == last_ins->inst_offset) {
2023 if (ins->dreg == last_ins->dreg) {
2024 MONO_DELETE_INS (bb, ins);
2027 ins->opcode = OP_MOVE;
2028 ins->sreg1 = last_ins->dreg;
2031 //g_assert_not_reached ();
2035 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
2036 * OP_LOAD_MEMBASE offset(basereg), reg
2038 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
2039 * OP_ICONST reg, imm
2041 } else if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_IMM
2042 || last_ins->opcode == OP_STORE_MEMBASE_IMM) &&
2043 ins->inst_basereg == last_ins->inst_destbasereg &&
2044 ins->inst_offset == last_ins->inst_offset) {
2045 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
2046 ins->opcode = OP_ICONST;
2047 ins->inst_c0 = last_ins->inst_imm;
2048 g_assert_not_reached (); // check this rule
2052 case OP_LOADU1_MEMBASE:
2053 case OP_LOADI1_MEMBASE:
2054 if (last_ins && (last_ins->opcode == OP_STOREI1_MEMBASE_REG) &&
2055 ins->inst_basereg == last_ins->inst_destbasereg &&
2056 ins->inst_offset == last_ins->inst_offset) {
2057 ins->opcode = (ins->opcode == OP_LOADI1_MEMBASE) ? OP_ICONV_TO_I1 : OP_ICONV_TO_U1;
2058 ins->sreg1 = last_ins->sreg1;
2061 case OP_LOADU2_MEMBASE:
2062 case OP_LOADI2_MEMBASE:
2063 if (last_ins && (last_ins->opcode == OP_STOREI2_MEMBASE_REG) &&
2064 ins->inst_basereg == last_ins->inst_destbasereg &&
2065 ins->inst_offset == last_ins->inst_offset) {
2066 ins->opcode = (ins->opcode == OP_LOADI2_MEMBASE) ? OP_ICONV_TO_I2 : OP_ICONV_TO_U2;
2067 ins->sreg1 = last_ins->sreg1;
2071 ins->opcode = OP_MOVE;
2075 if (ins->dreg == ins->sreg1) {
2076 MONO_DELETE_INS (bb, ins);
2080 * OP_MOVE sreg, dreg
2081 * OP_MOVE dreg, sreg
2083 if (last_ins && last_ins->opcode == OP_MOVE &&
2084 ins->sreg1 == last_ins->dreg &&
2085 ins->dreg == last_ins->sreg1) {
2086 MONO_DELETE_INS (bb, ins);
2094 bb->last_ins = last_ins;
2098 * the branch_cc_table should maintain the order of these
2112 branch_cc_table [] = {
2126 #define NEW_INS(cfg,dest,op) do { \
2127 MONO_INST_NEW ((cfg), (dest), (op)); \
2128 mono_bblock_insert_before_ins (bb, ins, (dest)); \
2132 map_to_reg_reg_op (int op)
2141 case OP_COMPARE_IMM:
2143 case OP_ICOMPARE_IMM:
2157 case OP_LOAD_MEMBASE:
2158 return OP_LOAD_MEMINDEX;
2159 case OP_LOADI4_MEMBASE:
2160 return OP_LOADI4_MEMINDEX;
2161 case OP_LOADU4_MEMBASE:
2162 return OP_LOADU4_MEMINDEX;
2163 case OP_LOADU1_MEMBASE:
2164 return OP_LOADU1_MEMINDEX;
2165 case OP_LOADI2_MEMBASE:
2166 return OP_LOADI2_MEMINDEX;
2167 case OP_LOADU2_MEMBASE:
2168 return OP_LOADU2_MEMINDEX;
2169 case OP_LOADI1_MEMBASE:
2170 return OP_LOADI1_MEMINDEX;
2171 case OP_STOREI1_MEMBASE_REG:
2172 return OP_STOREI1_MEMINDEX;
2173 case OP_STOREI2_MEMBASE_REG:
2174 return OP_STOREI2_MEMINDEX;
2175 case OP_STOREI4_MEMBASE_REG:
2176 return OP_STOREI4_MEMINDEX;
2177 case OP_STORE_MEMBASE_REG:
2178 return OP_STORE_MEMINDEX;
2179 case OP_STORER4_MEMBASE_REG:
2180 return OP_STORER4_MEMINDEX;
2181 case OP_STORER8_MEMBASE_REG:
2182 return OP_STORER8_MEMINDEX;
2183 case OP_STORE_MEMBASE_IMM:
2184 return OP_STORE_MEMBASE_REG;
2185 case OP_STOREI1_MEMBASE_IMM:
2186 return OP_STOREI1_MEMBASE_REG;
2187 case OP_STOREI2_MEMBASE_IMM:
2188 return OP_STOREI2_MEMBASE_REG;
2189 case OP_STOREI4_MEMBASE_IMM:
2190 return OP_STOREI4_MEMBASE_REG;
2192 g_assert_not_reached ();
2196 * Remove from the instruction list the instructions that can't be
2197 * represented with very simple instructions with no register
2201 mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb)
2203 MonoInst *ins, *temp, *last_ins = NULL;
2204 int rot_amount, imm8, low_imm;
2206 MONO_BB_FOR_EACH_INS (bb, ins) {
2208 switch (ins->opcode) {
2212 case OP_COMPARE_IMM:
2213 case OP_ICOMPARE_IMM:
2227 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount)) < 0) {
2228 NEW_INS (cfg, temp, OP_ICONST);
2229 temp->inst_c0 = ins->inst_imm;
2230 temp->dreg = mono_alloc_ireg (cfg);
2231 ins->sreg2 = temp->dreg;
2232 ins->opcode = mono_op_imm_to_op (ins->opcode);
2234 if (ins->opcode == OP_SBB || ins->opcode == OP_ISBB || ins->opcode == OP_SUBCC)
2240 if (ins->inst_imm == 1) {
2241 ins->opcode = OP_MOVE;
2244 if (ins->inst_imm == 0) {
2245 ins->opcode = OP_ICONST;
2249 imm8 = mono_is_power_of_two (ins->inst_imm);
2251 ins->opcode = OP_SHL_IMM;
2252 ins->inst_imm = imm8;
2255 NEW_INS (cfg, temp, OP_ICONST);
2256 temp->inst_c0 = ins->inst_imm;
2257 temp->dreg = mono_alloc_ireg (cfg);
2258 ins->sreg2 = temp->dreg;
2259 ins->opcode = OP_IMUL;
2265 if (ins->next && (ins->next->opcode == OP_COND_EXC_C || ins->next->opcode == OP_COND_EXC_IC))
2266 /* ARM sets the C flag to 1 if there was _no_ overflow */
2267 ins->next->opcode = OP_COND_EXC_NC;
2269 case OP_LOCALLOC_IMM:
2270 NEW_INS (cfg, temp, OP_ICONST);
2271 temp->inst_c0 = ins->inst_imm;
2272 temp->dreg = mono_alloc_ireg (cfg);
2273 ins->sreg1 = temp->dreg;
2274 ins->opcode = OP_LOCALLOC;
2276 case OP_LOAD_MEMBASE:
2277 case OP_LOADI4_MEMBASE:
2278 case OP_LOADU4_MEMBASE:
2279 case OP_LOADU1_MEMBASE:
2280 /* we can do two things: load the immed in a register
2281 * and use an indexed load, or see if the immed can be
2282 * represented as an ad_imm + a load with a smaller offset
2283 * that fits. We just do the first for now, optimize later.
2285 if (arm_is_imm12 (ins->inst_offset))
2287 NEW_INS (cfg, temp, OP_ICONST);
2288 temp->inst_c0 = ins->inst_offset;
2289 temp->dreg = mono_alloc_ireg (cfg);
2290 ins->sreg2 = temp->dreg;
2291 ins->opcode = map_to_reg_reg_op (ins->opcode);
2293 case OP_LOADI2_MEMBASE:
2294 case OP_LOADU2_MEMBASE:
2295 case OP_LOADI1_MEMBASE:
2296 if (arm_is_imm8 (ins->inst_offset))
2298 NEW_INS (cfg, temp, OP_ICONST);
2299 temp->inst_c0 = ins->inst_offset;
2300 temp->dreg = mono_alloc_ireg (cfg);
2301 ins->sreg2 = temp->dreg;
2302 ins->opcode = map_to_reg_reg_op (ins->opcode);
2304 case OP_LOADR4_MEMBASE:
2305 case OP_LOADR8_MEMBASE:
2306 if (arm_is_fpimm8 (ins->inst_offset))
2308 low_imm = ins->inst_offset & 0x1ff;
2309 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~0x1ff, &rot_amount)) >= 0) {
2310 NEW_INS (cfg, temp, OP_ADD_IMM);
2311 temp->inst_imm = ins->inst_offset & ~0x1ff;
2312 temp->sreg1 = ins->inst_basereg;
2313 temp->dreg = mono_alloc_ireg (cfg);
2314 ins->inst_basereg = temp->dreg;
2315 ins->inst_offset = low_imm;
2318 /* VFP/FPA doesn't have indexed load instructions */
2319 g_assert_not_reached ();
2321 case OP_STORE_MEMBASE_REG:
2322 case OP_STOREI4_MEMBASE_REG:
2323 case OP_STOREI1_MEMBASE_REG:
2324 if (arm_is_imm12 (ins->inst_offset))
2326 NEW_INS (cfg, temp, OP_ICONST);
2327 temp->inst_c0 = ins->inst_offset;
2328 temp->dreg = mono_alloc_ireg (cfg);
2329 ins->sreg2 = temp->dreg;
2330 ins->opcode = map_to_reg_reg_op (ins->opcode);
2332 case OP_STOREI2_MEMBASE_REG:
2333 if (arm_is_imm8 (ins->inst_offset))
2335 NEW_INS (cfg, temp, OP_ICONST);
2336 temp->inst_c0 = ins->inst_offset;
2337 temp->dreg = mono_alloc_ireg (cfg);
2338 ins->sreg2 = temp->dreg;
2339 ins->opcode = map_to_reg_reg_op (ins->opcode);
2341 case OP_STORER4_MEMBASE_REG:
2342 case OP_STORER8_MEMBASE_REG:
2343 if (arm_is_fpimm8 (ins->inst_offset))
2345 low_imm = ins->inst_offset & 0x1ff;
2346 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~ 0x1ff, &rot_amount)) >= 0 && arm_is_fpimm8 (low_imm)) {
2347 NEW_INS (cfg, temp, OP_ADD_IMM);
2348 temp->inst_imm = ins->inst_offset & ~0x1ff;
2349 temp->sreg1 = ins->inst_destbasereg;
2350 temp->dreg = mono_alloc_ireg (cfg);
2351 ins->inst_destbasereg = temp->dreg;
2352 ins->inst_offset = low_imm;
2355 /*g_print ("fail with: %d (%d, %d)\n", ins->inst_offset, ins->inst_offset & ~0x1ff, low_imm);*/
2356 /* VFP/FPA doesn't have indexed store instructions */
2357 g_assert_not_reached ();
2359 case OP_STORE_MEMBASE_IMM:
2360 case OP_STOREI1_MEMBASE_IMM:
2361 case OP_STOREI2_MEMBASE_IMM:
2362 case OP_STOREI4_MEMBASE_IMM:
2363 NEW_INS (cfg, temp, OP_ICONST);
2364 temp->inst_c0 = ins->inst_imm;
2365 temp->dreg = mono_alloc_ireg (cfg);
2366 ins->sreg1 = temp->dreg;
2367 ins->opcode = map_to_reg_reg_op (ins->opcode);
2369 goto loop_start; /* make it handle the possibly big ins->inst_offset */
2371 gboolean swap = FALSE;
2374 /* Some fp compares require swapped operands */
2375 g_assert (ins->next);
2376 switch (ins->next->opcode) {
2378 ins->next->opcode = OP_FBLT;
2382 ins->next->opcode = OP_FBLT_UN;
2386 ins->next->opcode = OP_FBGE;
2390 ins->next->opcode = OP_FBGE_UN;
2398 ins->sreg1 = ins->sreg2;
2407 bb->last_ins = last_ins;
2408 bb->max_vreg = cfg->next_vreg;
2412 mono_arch_decompose_long_opts (MonoCompile *cfg, MonoInst *long_ins)
2416 if (long_ins->opcode == OP_LNEG) {
2418 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ARM_RSBS_IMM, ins->dreg + 1, ins->sreg1 + 1, 0);
2419 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ARM_RSC_IMM, ins->dreg + 2, ins->sreg1 + 2, 0);
2425 emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size, gboolean is_signed)
2427 /* sreg is a float, dreg is an integer reg */
2429 ARM_FIXZ (code, dreg, sreg);
2430 #elif defined(ARM_FPU_VFP)
2432 ARM_TOSIZD (code, ARM_VFP_F0, sreg);
2434 ARM_TOUIZD (code, ARM_VFP_F0, sreg);
2435 ARM_FMRS (code, dreg, ARM_VFP_F0);
2439 ARM_AND_REG_IMM8 (code, dreg, dreg, 0xff);
2440 else if (size == 2) {
2441 ARM_SHL_IMM (code, dreg, dreg, 16);
2442 ARM_SHR_IMM (code, dreg, dreg, 16);
2446 ARM_SHL_IMM (code, dreg, dreg, 24);
2447 ARM_SAR_IMM (code, dreg, dreg, 24);
2448 } else if (size == 2) {
2449 ARM_SHL_IMM (code, dreg, dreg, 16);
2450 ARM_SAR_IMM (code, dreg, dreg, 16);
2458 const guchar *target;
2463 #define is_call_imm(diff) ((gint)(diff) >= -33554432 && (gint)(diff) <= 33554431)
2466 search_thunk_slot (void *data, int csize, int bsize, void *user_data) {
2467 PatchData *pdata = (PatchData*)user_data;
2468 guchar *code = data;
2469 guint32 *thunks = data;
2470 guint32 *endthunks = (guint32*)(code + bsize);
2472 int difflow, diffhigh;
2474 /* always ensure a call from pdata->code can reach to the thunks without further thunks */
2475 difflow = (char*)pdata->code - (char*)thunks;
2476 diffhigh = (char*)pdata->code - (char*)endthunks;
2477 if (!((is_call_imm (thunks) && is_call_imm (endthunks)) || (is_call_imm (difflow) && is_call_imm (diffhigh))))
2481 * The thunk is composed of 3 words:
2482 * load constant from thunks [2] into ARM_IP
2485 * Note that the LR register is already setup
2487 //g_print ("thunk nentries: %d\n", ((char*)endthunks - (char*)thunks)/16);
2488 if ((pdata->found == 2) || (pdata->code >= code && pdata->code <= code + csize)) {
2489 while (thunks < endthunks) {
2490 //g_print ("looking for target: %p at %p (%08x-%08x)\n", pdata->target, thunks, thunks [0], thunks [1]);
2491 if (thunks [2] == (guint32)pdata->target) {
2492 arm_patch (pdata->code, (guchar*)thunks);
2493 mono_arch_flush_icache (pdata->code, 4);
2496 } else if ((thunks [0] == 0) && (thunks [1] == 0) && (thunks [2] == 0)) {
2497 /* found a free slot instead: emit thunk */
2498 /* ARMREG_IP is fine to use since this can't be an IMT call
2501 code = (guchar*)thunks;
2502 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
2503 if (thumb_supported)
2504 ARM_BX (code, ARMREG_IP);
2506 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
2507 thunks [2] = (guint32)pdata->target;
2508 mono_arch_flush_icache ((guchar*)thunks, 12);
2510 arm_patch (pdata->code, (guchar*)thunks);
2511 mono_arch_flush_icache (pdata->code, 4);
2515 /* skip 12 bytes, the size of the thunk */
2519 //g_print ("failed thunk lookup for %p from %p at %p (%d entries)\n", pdata->target, pdata->code, data, count);
2525 handle_thunk (MonoDomain *domain, int absolute, guchar *code, const guchar *target)
2530 domain = mono_domain_get ();
2533 pdata.target = target;
2534 pdata.absolute = absolute;
2537 mono_domain_lock (domain);
2538 mono_domain_code_foreach (domain, search_thunk_slot, &pdata);
2541 /* this uses the first available slot */
2543 mono_domain_code_foreach (domain, search_thunk_slot, &pdata);
2545 mono_domain_unlock (domain);
2547 if (pdata.found != 1)
2548 g_print ("thunk failed for %p from %p\n", target, code);
2549 g_assert (pdata.found == 1);
2553 arm_patch_general (MonoDomain *domain, guchar *code, const guchar *target)
2555 guint32 *code32 = (void*)code;
2556 guint32 ins = *code32;
2557 guint32 prim = (ins >> 25) & 7;
2558 guint32 tval = GPOINTER_TO_UINT (target);
2560 //g_print ("patching 0x%08x (0x%08x) to point to 0x%08x\n", code, ins, target);
2561 if (prim == 5) { /* 101b */
2562 /* the diff starts 8 bytes from the branch opcode */
2563 gint diff = target - code - 8;
2565 gint tmask = 0xffffffff;
2566 if (tval & 1) { /* entering thumb mode */
2567 diff = target - 1 - code - 8;
2568 g_assert (thumb_supported);
2569 tbits = 0xf << 28; /* bl->blx bit pattern */
2570 g_assert ((ins & (1 << 24))); /* it must be a bl, not b instruction */
2571 /* this low bit of the displacement is moved to bit 24 in the instruction encoding */
2575 tmask = ~(1 << 24); /* clear the link bit */
2576 /*g_print ("blx to thumb: target: %p, code: %p, diff: %d, mask: %x\n", target, code, diff, tmask);*/
2581 if (diff <= 33554431) {
2583 ins = (ins & 0xff000000) | diff;
2585 *code32 = ins | tbits;
2589 /* diff between 0 and -33554432 */
2590 if (diff >= -33554432) {
2592 ins = (ins & 0xff000000) | (diff & ~0xff000000);
2594 *code32 = ins | tbits;
2599 handle_thunk (domain, TRUE, code, target);
2604 * The alternative call sequences looks like this:
2606 * ldr ip, [pc] // loads the address constant
2607 * b 1f // jumps around the constant
2608 * address constant embedded in the code
2613 * There are two cases for patching:
2614 * a) at the end of method emission: in this case code points to the start
2615 * of the call sequence
2616 * b) during runtime patching of the call site: in this case code points
2617 * to the mov pc, ip instruction
2619 * We have to handle also the thunk jump code sequence:
2623 * address constant // execution never reaches here
2625 if ((ins & 0x0ffffff0) == 0x12fff10) {
2626 /* Branch and exchange: the address is constructed in a reg
2627 * We can patch BX when the code sequence is the following:
2628 * ldr ip, [pc, #0] ; 0x8
2635 guint8 *emit = (guint8*)ccode;
2636 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2638 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
2639 ARM_BX (emit, ARMREG_IP);
2641 /*patching from magic trampoline*/
2642 if (ins == ccode [3]) {
2643 g_assert (code32 [-4] == ccode [0]);
2644 g_assert (code32 [-3] == ccode [1]);
2645 g_assert (code32 [-1] == ccode [2]);
2646 code32 [-2] = (guint32)target;
2649 /*patching from JIT*/
2650 if (ins == ccode [0]) {
2651 g_assert (code32 [1] == ccode [1]);
2652 g_assert (code32 [3] == ccode [2]);
2653 g_assert (code32 [4] == ccode [3]);
2654 code32 [2] = (guint32)target;
2657 g_assert_not_reached ();
2658 } else if ((ins & 0x0ffffff0) == 0x12fff30) {
2666 guint8 *emit = (guint8*)ccode;
2667 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2669 ARM_BLX_REG (emit, ARMREG_IP);
2671 g_assert (code32 [-3] == ccode [0]);
2672 g_assert (code32 [-2] == ccode [1]);
2673 g_assert (code32 [0] == ccode [2]);
2675 code32 [-1] = (guint32)target;
2678 guint32 *tmp = ccode;
2679 guint8 *emit = (guint8*)tmp;
2680 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2681 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
2682 ARM_MOV_REG_REG (emit, ARMREG_PC, ARMREG_IP);
2683 ARM_BX (emit, ARMREG_IP);
2684 if (ins == ccode [2]) {
2685 g_assert_not_reached (); // should be -2 ...
2686 code32 [-1] = (guint32)target;
2689 if (ins == ccode [0]) {
2690 /* handles both thunk jump code and the far call sequence */
2691 code32 [2] = (guint32)target;
2694 g_assert_not_reached ();
2696 // g_print ("patched with 0x%08x\n", ins);
2700 arm_patch (guchar *code, const guchar *target)
2702 arm_patch_general (NULL, code, target);
2706 * Return the >= 0 uimm8 value if val can be represented with a byte + rotation
2707 * (with the rotation amount in *rot_amount. rot_amount is already adjusted
2708 * to be used with the emit macros.
2709 * Return -1 otherwise.
2712 mono_arm_is_rotated_imm8 (guint32 val, gint *rot_amount)
2715 for (i = 0; i < 31; i+= 2) {
2716 res = (val << (32 - i)) | (val >> i);
2719 *rot_amount = i? 32 - i: 0;
2726 * Emits in code a sequence of instructions that load the value 'val'
2727 * into the dreg register. Uses at most 4 instructions.
2730 mono_arm_emit_load_imm (guint8 *code, int dreg, guint32 val)
2732 int imm8, rot_amount;
2734 ARM_LDR_IMM (code, dreg, ARMREG_PC, 0);
2735 /* skip the constant pool */
2741 if ((imm8 = mono_arm_is_rotated_imm8 (val, &rot_amount)) >= 0) {
2742 ARM_MOV_REG_IMM (code, dreg, imm8, rot_amount);
2743 } else if ((imm8 = mono_arm_is_rotated_imm8 (~val, &rot_amount)) >= 0) {
2744 ARM_MVN_REG_IMM (code, dreg, imm8, rot_amount);
2747 ARM_MOVW_REG_IMM (code, dreg, val & 0xffff);
2749 ARM_MOVT_REG_IMM (code, dreg, (val >> 16) & 0xffff);
2753 ARM_MOV_REG_IMM8 (code, dreg, (val & 0xFF));
2755 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF00) >> 8, 24);
2757 if (val & 0xFF0000) {
2758 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
2760 if (val & 0xFF000000) {
2761 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2763 } else if (val & 0xFF00) {
2764 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF00) >> 8, 24);
2765 if (val & 0xFF0000) {
2766 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
2768 if (val & 0xFF000000) {
2769 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2771 } else if (val & 0xFF0000) {
2772 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF0000) >> 16, 16);
2773 if (val & 0xFF000000) {
2774 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2777 //g_assert_not_reached ();
2783 * emit_load_volatile_arguments:
2785 * Load volatile arguments from the stack to the original input registers.
2786 * Required before a tail call.
2789 emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code)
2791 MonoMethod *method = cfg->method;
2792 MonoMethodSignature *sig;
2797 /* FIXME: Generate intermediate code instead */
2799 sig = mono_method_signature (method);
2801 /* This is the opposite of the code in emit_prolog */
2805 cinfo = get_call_info (NULL, sig, sig->pinvoke);
2807 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
2808 ArgInfo *ainfo = &cinfo->ret;
2809 inst = cfg->vret_addr;
2810 g_assert (arm_is_imm12 (inst->inst_offset));
2811 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2813 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
2814 ArgInfo *ainfo = cinfo->args + i;
2815 inst = cfg->args [pos];
2817 if (cfg->verbose_level > 2)
2818 g_print ("Loading argument %d (type: %d)\n", i, ainfo->storage);
2819 if (inst->opcode == OP_REGVAR) {
2820 if (ainfo->storage == RegTypeGeneral)
2821 ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
2822 else if (ainfo->storage == RegTypeFP) {
2823 g_assert_not_reached ();
2824 } else if (ainfo->storage == RegTypeBase) {
2828 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
2829 ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset));
2831 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
2832 ARM_LDR_REG_REG (code, inst->dreg, ARMREG_SP, ARMREG_IP);
2836 g_assert_not_reached ();
2838 if (ainfo->storage == RegTypeGeneral || ainfo->storage == RegTypeIRegPair) {
2839 switch (ainfo->size) {
2846 g_assert (arm_is_imm12 (inst->inst_offset));
2847 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2848 g_assert (arm_is_imm12 (inst->inst_offset + 4));
2849 ARM_LDR_IMM (code, ainfo->reg + 1, inst->inst_basereg, inst->inst_offset + 4);
2852 if (arm_is_imm12 (inst->inst_offset)) {
2853 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2855 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
2856 ARM_LDR_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
2860 } else if (ainfo->storage == RegTypeBaseGen) {
2863 } else if (ainfo->storage == RegTypeBase) {
2865 } else if (ainfo->storage == RegTypeFP) {
2866 g_assert_not_reached ();
2867 } else if (ainfo->storage == RegTypeStructByVal) {
2868 int doffset = inst->inst_offset;
2872 if (mono_class_from_mono_type (inst->inst_vtype))
2873 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), NULL);
2874 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
2875 if (arm_is_imm12 (doffset)) {
2876 ARM_LDR_IMM (code, ainfo->reg + cur_reg, inst->inst_basereg, doffset);
2878 code = mono_arm_emit_load_imm (code, ARMREG_IP, doffset);
2879 ARM_LDR_REG_REG (code, ainfo->reg + cur_reg, inst->inst_basereg, ARMREG_IP);
2881 soffset += sizeof (gpointer);
2882 doffset += sizeof (gpointer);
2887 } else if (ainfo->storage == RegTypeStructByAddr) {
2904 mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
2909 guint8 *code = cfg->native_code + cfg->code_len;
2910 MonoInst *last_ins = NULL;
2911 guint last_offset = 0;
2913 int imm8, rot_amount;
2915 /* we don't align basic blocks of loops on arm */
2917 if (cfg->verbose_level > 2)
2918 g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset);
2920 cpos = bb->max_offset;
2922 if (cfg->prof_options & MONO_PROFILE_COVERAGE) {
2923 //MonoCoverageInfo *cov = mono_get_coverage_info (cfg->method);
2924 //g_assert (!mono_compile_aot);
2927 // cov->data [bb->dfn].iloffset = bb->cil_code - cfg->cil_code;
2928 /* this is not thread save, but good enough */
2929 /* fixme: howto handle overflows? */
2930 //x86_inc_mem (code, &cov->data [bb->dfn].count);
2933 if (mono_break_at_bb_method && mono_method_desc_full_match (mono_break_at_bb_method, cfg->method) && bb->block_num == mono_break_at_bb_bb_num) {
2934 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2935 (gpointer)"mono_break");
2936 code = emit_call_seq (cfg, code);
2939 MONO_BB_FOR_EACH_INS (bb, ins) {
2940 offset = code - cfg->native_code;
2942 max_len = ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
2944 if (offset > (cfg->code_size - max_len - 16)) {
2945 cfg->code_size *= 2;
2946 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
2947 code = cfg->native_code + offset;
2949 // if (ins->cil_code)
2950 // g_print ("cil code\n");
2951 mono_debug_record_line_number (cfg, ins, offset);
2953 switch (ins->opcode) {
2954 case OP_MEMORY_BARRIER:
2957 #ifdef HAVE_AEABI_READ_TP
2958 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2959 (gpointer)"__aeabi_read_tp");
2960 code = emit_call_seq (cfg, code);
2962 ARM_LDR_IMM (code, ins->dreg, ARMREG_R0, ins->inst_offset);
2964 g_assert_not_reached ();
2968 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2969 ppc_mulhw (code, ppc_r3, ins->sreg1, ins->sreg2);
2972 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2973 ppc_mulhwu (code, ppc_r3, ins->sreg1, ins->sreg2);
2975 case OP_STOREI1_MEMBASE_IMM:
2976 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFF);
2977 g_assert (arm_is_imm12 (ins->inst_offset));
2978 ARM_STRB_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2980 case OP_STOREI2_MEMBASE_IMM:
2981 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFFFF);
2982 g_assert (arm_is_imm8 (ins->inst_offset));
2983 ARM_STRH_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2985 case OP_STORE_MEMBASE_IMM:
2986 case OP_STOREI4_MEMBASE_IMM:
2987 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm);
2988 g_assert (arm_is_imm12 (ins->inst_offset));
2989 ARM_STR_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2991 case OP_STOREI1_MEMBASE_REG:
2992 g_assert (arm_is_imm12 (ins->inst_offset));
2993 ARM_STRB_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2995 case OP_STOREI2_MEMBASE_REG:
2996 g_assert (arm_is_imm8 (ins->inst_offset));
2997 ARM_STRH_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2999 case OP_STORE_MEMBASE_REG:
3000 case OP_STOREI4_MEMBASE_REG:
3001 /* this case is special, since it happens for spill code after lowering has been called */
3002 if (arm_is_imm12 (ins->inst_offset)) {
3003 ARM_STR_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3005 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3006 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ARMREG_LR);
3009 case OP_STOREI1_MEMINDEX:
3010 ARM_STRB_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
3012 case OP_STOREI2_MEMINDEX:
3013 ARM_STRH_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
3015 case OP_STORE_MEMINDEX:
3016 case OP_STOREI4_MEMINDEX:
3017 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
3020 g_assert_not_reached ();
3022 case OP_LOAD_MEMINDEX:
3023 case OP_LOADI4_MEMINDEX:
3024 case OP_LOADU4_MEMINDEX:
3025 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
3027 case OP_LOADI1_MEMINDEX:
3028 ARM_LDRSB_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
3030 case OP_LOADU1_MEMINDEX:
3031 ARM_LDRB_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
3033 case OP_LOADI2_MEMINDEX:
3034 ARM_LDRSH_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
3036 case OP_LOADU2_MEMINDEX:
3037 ARM_LDRH_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
3039 case OP_LOAD_MEMBASE:
3040 case OP_LOADI4_MEMBASE:
3041 case OP_LOADU4_MEMBASE:
3042 /* this case is special, since it happens for spill code after lowering has been called */
3043 if (arm_is_imm12 (ins->inst_offset)) {
3044 ARM_LDR_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3046 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3047 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ARMREG_LR);
3050 case OP_LOADI1_MEMBASE:
3051 g_assert (arm_is_imm8 (ins->inst_offset));
3052 ARM_LDRSB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3054 case OP_LOADU1_MEMBASE:
3055 g_assert (arm_is_imm12 (ins->inst_offset));
3056 ARM_LDRB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3058 case OP_LOADU2_MEMBASE:
3059 g_assert (arm_is_imm8 (ins->inst_offset));
3060 ARM_LDRH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3062 case OP_LOADI2_MEMBASE:
3063 g_assert (arm_is_imm8 (ins->inst_offset));
3064 ARM_LDRSH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3066 case OP_ICONV_TO_I1:
3067 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 24);
3068 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 24);
3070 case OP_ICONV_TO_I2:
3071 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
3072 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 16);
3074 case OP_ICONV_TO_U1:
3075 ARM_AND_REG_IMM8 (code, ins->dreg, ins->sreg1, 0xff);
3077 case OP_ICONV_TO_U2:
3078 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
3079 ARM_SHR_IMM (code, ins->dreg, ins->dreg, 16);
3083 ARM_CMP_REG_REG (code, ins->sreg1, ins->sreg2);
3085 case OP_COMPARE_IMM:
3086 case OP_ICOMPARE_IMM:
3087 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
3088 g_assert (imm8 >= 0);
3089 ARM_CMP_REG_IMM (code, ins->sreg1, imm8, rot_amount);
3093 * gdb does not like encountering the hw breakpoint ins in the debugged code.
3094 * So instead of emitting a trap, we emit a call a C function and place a
3097 //*(int*)code = 0xef9f0001;
3100 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3101 (gpointer)"mono_break");
3102 code = emit_call_seq (cfg, code);
3104 case OP_RELAXED_NOP:
3109 case OP_DUMMY_STORE:
3110 case OP_NOT_REACHED:
3113 case OP_SEQ_POINT: {
3115 MonoInst *info_var = cfg->arch.seq_point_info_var;
3116 MonoInst *ss_trigger_page_var = cfg->arch.ss_trigger_page_var;
3118 int dreg = ARMREG_LR;
3121 * For AOT, we use one got slot per method, which will point to a
3122 * SeqPointInfo structure, containing all the information required
3123 * by the code below.
3125 if (cfg->compile_aot) {
3126 g_assert (info_var);
3127 g_assert (info_var->opcode == OP_REGOFFSET);
3128 g_assert (arm_is_imm12 (info_var->inst_offset));
3132 * Read from the single stepping trigger page. This will cause a
3133 * SIGSEGV when single stepping is enabled.
3134 * We do this _before_ the breakpoint, so single stepping after
3135 * a breakpoint is hit will step to the next IL offset.
3137 g_assert (((guint64)(gsize)ss_trigger_page >> 32) == 0);
3139 if (ins->flags & MONO_INST_SINGLE_STEP_LOC) {
3140 if (cfg->compile_aot) {
3141 /* Load the trigger page addr from the variable initialized in the prolog */
3142 var = ss_trigger_page_var;
3144 g_assert (var->opcode == OP_REGOFFSET);
3145 g_assert (arm_is_imm12 (var->inst_offset));
3146 ARM_LDR_IMM (code, dreg, var->inst_basereg, var->inst_offset);
3148 ARM_LDR_IMM (code, dreg, ARMREG_PC, 0);
3150 *(int*)code = (int)ss_trigger_page;
3153 ARM_LDR_IMM (code, dreg, dreg, 0);
3156 il_offset = ins->inst_imm;
3158 if (!cfg->seq_points)
3159 cfg->seq_points = g_ptr_array_new ();
3160 g_ptr_array_add (cfg->seq_points, GUINT_TO_POINTER (il_offset));
3161 g_ptr_array_add (cfg->seq_points, GUINT_TO_POINTER (code - cfg->native_code));
3163 if (cfg->compile_aot) {
3164 guint32 offset = code - cfg->native_code;
3167 ARM_LDR_IMM (code, dreg, info_var->inst_basereg, info_var->inst_offset);
3168 /* Add the offset */
3169 val = ((offset / 4) * sizeof (guint8*)) + G_STRUCT_OFFSET (SeqPointInfo, bp_addrs);
3170 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF), 0);
3172 * Have to emit nops to keep the difference between the offset
3173 * stored in seq_points and breakpoint instruction constant,
3174 * mono_arch_get_ip_for_breakpoint () depends on this.
3177 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF00) >> 8, 24);
3181 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
3184 g_assert (!(val & 0xFF000000));
3185 /* Load the info->bp_addrs [offset], which is either 0 or the address of a trigger page */
3186 ARM_LDR_IMM (code, dreg, dreg, 0);
3188 /* What is faster, a branch or a load ? */
3189 ARM_CMP_REG_IMM (code, dreg, 0, 0);
3190 /* The breakpoint instruction */
3191 ARM_LDR_IMM_COND (code, dreg, dreg, 0, ARMCOND_NE);
3194 * A placeholder for a possible breakpoint inserted by
3195 * mono_arch_set_breakpoint ().
3197 for (i = 0; i < 4; ++i)
3204 ARM_ADDS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3207 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3211 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3214 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
3215 g_assert (imm8 >= 0);
3216 ARM_ADDS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
3220 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
3221 g_assert (imm8 >= 0);
3222 ARM_ADD_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
3226 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
3227 g_assert (imm8 >= 0);
3228 ARM_ADCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
3231 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3232 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
3234 case OP_IADD_OVF_UN:
3235 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3236 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
3239 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3240 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
3242 case OP_ISUB_OVF_UN:
3243 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3244 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
3246 case OP_ADD_OVF_CARRY:
3247 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3248 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
3250 case OP_ADD_OVF_UN_CARRY:
3251 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3252 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
3254 case OP_SUB_OVF_CARRY:
3255 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3256 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
3258 case OP_SUB_OVF_UN_CARRY:
3259 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3260 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
3264 ARM_SUBS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3267 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
3268 g_assert (imm8 >= 0);
3269 ARM_SUBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
3272 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3276 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3280 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
3281 g_assert (imm8 >= 0);
3282 ARM_SUB_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
3286 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
3287 g_assert (imm8 >= 0);
3288 ARM_SBCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
3290 case OP_ARM_RSBS_IMM:
3291 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
3292 g_assert (imm8 >= 0);
3293 ARM_RSBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
3295 case OP_ARM_RSC_IMM:
3296 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
3297 g_assert (imm8 >= 0);
3298 ARM_RSC_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
3301 ARM_AND_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3305 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
3306 g_assert (imm8 >= 0);
3307 ARM_AND_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
3315 /* crappy ARM arch doesn't have a DIV instruction */
3316 g_assert_not_reached ();
3318 ARM_ORR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3322 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
3323 g_assert (imm8 >= 0);
3324 ARM_ORR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
3327 ARM_EOR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3331 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
3332 g_assert (imm8 >= 0);
3333 ARM_EOR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
3336 ARM_SHL_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3341 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
3342 else if (ins->dreg != ins->sreg1)
3343 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
3346 ARM_SAR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3351 ARM_SAR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
3352 else if (ins->dreg != ins->sreg1)
3353 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
3356 case OP_ISHR_UN_IMM:
3358 ARM_SHR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
3359 else if (ins->dreg != ins->sreg1)
3360 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
3363 ARM_SHR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3366 ARM_MVN_REG_REG (code, ins->dreg, ins->sreg1);
3369 ARM_RSB_REG_IMM8 (code, ins->dreg, ins->sreg1, 0);
3372 if (ins->dreg == ins->sreg2)
3373 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3375 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg2, ins->sreg1);
3378 g_assert_not_reached ();
3381 /* FIXME: handle ovf/ sreg2 != dreg */
3382 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3383 /* FIXME: MUL doesn't set the C/O flags on ARM */
3385 case OP_IMUL_OVF_UN:
3386 /* FIXME: handle ovf/ sreg2 != dreg */
3387 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
3388 /* FIXME: MUL doesn't set the C/O flags on ARM */
3391 code = mono_arm_emit_load_imm (code, ins->dreg, ins->inst_c0);
3394 /* Load the GOT offset */
3395 mono_add_patch_info (cfg, offset, (MonoJumpInfoType)ins->inst_i1, ins->inst_p0);
3396 ARM_LDR_IMM (code, ins->dreg, ARMREG_PC, 0);
3398 *(gpointer*)code = NULL;
3400 /* Load the value from the GOT */
3401 ARM_LDR_REG_REG (code, ins->dreg, ARMREG_PC, ins->dreg);
3403 case OP_ICONV_TO_I4:
3404 case OP_ICONV_TO_U4:
3406 if (ins->dreg != ins->sreg1)
3407 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
3410 int saved = ins->sreg2;
3411 if (ins->sreg2 == ARM_LSW_REG) {
3412 ARM_MOV_REG_REG (code, ARMREG_LR, ins->sreg2);
3415 if (ins->sreg1 != ARM_LSW_REG)
3416 ARM_MOV_REG_REG (code, ARM_LSW_REG, ins->sreg1);
3417 if (saved != ARM_MSW_REG)
3418 ARM_MOV_REG_REG (code, ARM_MSW_REG, saved);
3423 ARM_MVFD (code, ins->dreg, ins->sreg1);
3424 #elif defined(ARM_FPU_VFP)
3425 ARM_CPYD (code, ins->dreg, ins->sreg1);
3428 case OP_FCONV_TO_R4:
3430 ARM_MVFS (code, ins->dreg, ins->sreg1);
3431 #elif defined(ARM_FPU_VFP)
3432 ARM_CVTD (code, ins->dreg, ins->sreg1);
3433 ARM_CVTS (code, ins->dreg, ins->dreg);
3438 * Keep in sync with mono_arch_emit_epilog
3440 g_assert (!cfg->method->save_lmf);
3442 code = emit_load_volatile_arguments (cfg, code);
3444 code = emit_big_add (code, ARMREG_SP, cfg->frame_reg, cfg->stack_usage);
3445 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP)) | ((1 << ARMREG_LR)));
3446 mono_add_patch_info (cfg, (guint8*) code - cfg->native_code, MONO_PATCH_INFO_METHOD_JUMP, ins->inst_p0);
3447 if (cfg->compile_aot) {
3448 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
3450 *(gpointer*)code = NULL;
3452 ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_IP);
3458 /* ensure ins->sreg1 is not NULL */
3459 ARM_LDR_IMM (code, ARMREG_LR, ins->sreg1, 0);
3463 if (ppc_is_imm16 (cfg->sig_cookie + cfg->stack_usage)) {
3464 ppc_addi (code, ppc_r11, cfg->frame_reg, cfg->sig_cookie + cfg->stack_usage);
3466 ppc_load (code, ppc_r11, cfg->sig_cookie + cfg->stack_usage);
3467 ppc_add (code, ppc_r11, cfg->frame_reg, ppc_r11);
3469 ppc_stw (code, ppc_r11, 0, ins->sreg1);
3479 call = (MonoCallInst*)ins;
3480 if (ins->flags & MONO_INST_HAS_METHOD)
3481 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, call->method);
3483 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, call->fptr);
3484 code = emit_call_seq (cfg, code);
3485 code = emit_move_return_value (cfg, ins, code);
3491 case OP_VOIDCALL_REG:
3493 code = emit_call_reg (code, ins->sreg1);
3494 code = emit_move_return_value (cfg, ins, code);
3496 case OP_FCALL_MEMBASE:
3497 case OP_LCALL_MEMBASE:
3498 case OP_VCALL_MEMBASE:
3499 case OP_VCALL2_MEMBASE:
3500 case OP_VOIDCALL_MEMBASE:
3501 case OP_CALL_MEMBASE:
3502 g_assert (arm_is_imm12 (ins->inst_offset));
3503 g_assert (ins->sreg1 != ARMREG_LR);
3504 call = (MonoCallInst*)ins;
3505 if (call->method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
3506 ARM_ADD_REG_IMM8 (code, ARMREG_LR, ARMREG_PC, 4);
3507 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
3509 * We can't embed the method in the code stream in PIC code, or
3511 * Instead, we put it in V5 in code emitted by
3512 * mono_arch_emit_imt_argument (), and embed NULL here to
3513 * signal the IMT thunk that the value is in V5.
3515 if (call->dynamic_imt_arg)
3516 *((gpointer*)code) = NULL;
3518 *((gpointer*)code) = (gpointer)call->method;
3521 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
3522 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
3524 code = emit_move_return_value (cfg, ins, code);
3527 /* keep alignment */
3528 int alloca_waste = cfg->param_area;
3531 /* round the size to 8 bytes */
3532 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->sreg1, 7);
3533 ARM_BIC_REG_IMM8 (code, ins->dreg, ins->dreg, 7);
3535 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->dreg, alloca_waste);
3536 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ins->dreg);
3537 /* memzero the area: dreg holds the size, sp is the pointer */
3538 if (ins->flags & MONO_INST_INIT) {
3539 guint8 *start_loop, *branch_to_cond;
3540 ARM_MOV_REG_IMM8 (code, ARMREG_LR, 0);
3541 branch_to_cond = code;
3544 ARM_STR_REG_REG (code, ARMREG_LR, ARMREG_SP, ins->dreg);
3545 arm_patch (branch_to_cond, code);
3546 /* decrement by 4 and set flags */
3547 ARM_SUBS_REG_IMM8 (code, ins->dreg, ins->dreg, 4);
3548 ARM_B_COND (code, ARMCOND_GE, 0);
3549 arm_patch (code - 4, start_loop);
3551 ARM_ADD_REG_IMM8 (code, ins->dreg, ARMREG_SP, alloca_waste);
3556 MonoInst *var = cfg->dyn_call_var;
3558 g_assert (var->opcode == OP_REGOFFSET);
3559 g_assert (arm_is_imm12 (var->inst_offset));
3561 /* lr = args buffer filled by mono_arch_get_dyn_call_args () */
3562 ARM_MOV_REG_REG( code, ARMREG_LR, ins->sreg1);
3564 ARM_MOV_REG_REG( code, ARMREG_IP, ins->sreg2);
3566 /* Save args buffer */
3567 ARM_STR_IMM (code, ARMREG_LR, var->inst_basereg, var->inst_offset);
3569 /* Set stack slots using R0 as scratch reg */
3570 /* MONO_ARCH_DYN_CALL_PARAM_AREA gives the size of stack space available */
3571 for (i = 0; i < DYN_CALL_STACK_ARGS; ++i) {
3572 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, (PARAM_REGS + i) * sizeof (gpointer));
3573 ARM_STR_IMM (code, ARMREG_R0, ARMREG_SP, i * sizeof (gpointer));
3576 /* Set argument registers */
3577 for (i = 0; i < PARAM_REGS; ++i)
3578 ARM_LDR_IMM (code, i, ARMREG_LR, i * sizeof (gpointer));
3581 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
3582 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
3585 ARM_LDR_IMM (code, ARMREG_IP, var->inst_basereg, var->inst_offset);
3586 ARM_STR_IMM (code, ARMREG_R0, ARMREG_IP, G_STRUCT_OFFSET (DynCallArgs, res));
3587 ARM_STR_IMM (code, ARMREG_R1, ARMREG_IP, G_STRUCT_OFFSET (DynCallArgs, res2));
3591 if (ins->sreg1 != ARMREG_R0)
3592 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
3593 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3594 (gpointer)"mono_arch_throw_exception");
3595 code = emit_call_seq (cfg, code);
3599 if (ins->sreg1 != ARMREG_R0)
3600 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
3601 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3602 (gpointer)"mono_arch_rethrow_exception");
3603 code = emit_call_seq (cfg, code);
3606 case OP_START_HANDLER: {
3607 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3609 if (arm_is_imm12 (spvar->inst_offset)) {
3610 ARM_STR_IMM (code, ARMREG_LR, spvar->inst_basereg, spvar->inst_offset);
3612 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3613 ARM_STR_REG_REG (code, ARMREG_LR, spvar->inst_basereg, ARMREG_IP);
3617 case OP_ENDFILTER: {
3618 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3620 if (ins->sreg1 != ARMREG_R0)
3621 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
3622 if (arm_is_imm12 (spvar->inst_offset)) {
3623 ARM_LDR_IMM (code, ARMREG_IP, spvar->inst_basereg, spvar->inst_offset);
3625 g_assert (ARMREG_IP != spvar->inst_basereg);
3626 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3627 ARM_LDR_REG_REG (code, ARMREG_IP, spvar->inst_basereg, ARMREG_IP);
3629 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
3632 case OP_ENDFINALLY: {
3633 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3635 if (arm_is_imm12 (spvar->inst_offset)) {
3636 ARM_LDR_IMM (code, ARMREG_IP, spvar->inst_basereg, spvar->inst_offset);
3638 g_assert (ARMREG_IP != spvar->inst_basereg);
3639 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3640 ARM_LDR_REG_REG (code, ARMREG_IP, spvar->inst_basereg, ARMREG_IP);
3642 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
3645 case OP_CALL_HANDLER:
3646 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb);
3650 ins->inst_c0 = code - cfg->native_code;
3653 /*if (ins->inst_target_bb->native_offset) {
3655 //x86_jump_code (code, cfg->native_code + ins->inst_target_bb->native_offset);
3657 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb);
3662 ARM_MOV_REG_REG (code, ARMREG_PC, ins->sreg1);
3666 * In the normal case we have:
3667 * ldr pc, [pc, ins->sreg1 << 2]
3670 * ldr lr, [pc, ins->sreg1 << 2]
3672 * After follows the data.
3673 * FIXME: add aot support.
3675 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_SWITCH, ins->inst_p0);
3676 max_len += 4 * GPOINTER_TO_INT (ins->klass);
3677 if (offset > (cfg->code_size - max_len - 16)) {
3678 cfg->code_size += max_len;
3679 cfg->code_size *= 2;
3680 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
3681 code = cfg->native_code + offset;
3683 ARM_LDR_REG_REG_SHIFT (code, ARMREG_PC, ARMREG_PC, ins->sreg1, ARMSHIFT_LSL, 2);
3685 code += 4 * GPOINTER_TO_INT (ins->klass);
3689 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
3690 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
3694 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3695 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LT);
3699 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3700 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LO);
3704 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3705 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_GT);
3709 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3710 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_HI);
3712 case OP_COND_EXC_EQ:
3713 case OP_COND_EXC_NE_UN:
3714 case OP_COND_EXC_LT:
3715 case OP_COND_EXC_LT_UN:
3716 case OP_COND_EXC_GT:
3717 case OP_COND_EXC_GT_UN:
3718 case OP_COND_EXC_GE:
3719 case OP_COND_EXC_GE_UN:
3720 case OP_COND_EXC_LE:
3721 case OP_COND_EXC_LE_UN:
3722 EMIT_COND_SYSTEM_EXCEPTION (ins->opcode - OP_COND_EXC_EQ, ins->inst_p1);
3724 case OP_COND_EXC_IEQ:
3725 case OP_COND_EXC_INE_UN:
3726 case OP_COND_EXC_ILT:
3727 case OP_COND_EXC_ILT_UN:
3728 case OP_COND_EXC_IGT:
3729 case OP_COND_EXC_IGT_UN:
3730 case OP_COND_EXC_IGE:
3731 case OP_COND_EXC_IGE_UN:
3732 case OP_COND_EXC_ILE:
3733 case OP_COND_EXC_ILE_UN:
3734 EMIT_COND_SYSTEM_EXCEPTION (ins->opcode - OP_COND_EXC_IEQ, ins->inst_p1);
3737 case OP_COND_EXC_IC:
3738 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_CS, ins->inst_p1);
3740 case OP_COND_EXC_OV:
3741 case OP_COND_EXC_IOV:
3742 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VS, ins->inst_p1);
3744 case OP_COND_EXC_NC:
3745 case OP_COND_EXC_INC:
3746 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_CC, ins->inst_p1);
3748 case OP_COND_EXC_NO:
3749 case OP_COND_EXC_INO:
3750 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VC, ins->inst_p1);
3762 EMIT_COND_BRANCH (ins, ins->opcode - OP_IBEQ);
3765 /* floating point opcodes */
3768 if (cfg->compile_aot) {
3769 ARM_LDFD (code, ins->dreg, ARMREG_PC, 0);
3771 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3773 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
3776 /* FIXME: we can optimize the imm load by dealing with part of
3777 * the displacement in LDFD (aligning to 512).
3779 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3780 ARM_LDFD (code, ins->dreg, ARMREG_LR, 0);
3784 if (cfg->compile_aot) {
3785 ARM_LDFS (code, ins->dreg, ARMREG_PC, 0);
3787 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3790 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3791 ARM_LDFS (code, ins->dreg, ARMREG_LR, 0);
3794 case OP_STORER8_MEMBASE_REG:
3795 /* This is generated by the local regalloc pass which runs after the lowering pass */
3796 if (!arm_is_fpimm8 (ins->inst_offset)) {
3797 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3798 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_destbasereg);
3799 ARM_STFD (code, ins->sreg1, ARMREG_LR, 0);
3801 ARM_STFD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3804 case OP_LOADR8_MEMBASE:
3805 /* This is generated by the local regalloc pass which runs after the lowering pass */
3806 if (!arm_is_fpimm8 (ins->inst_offset)) {
3807 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3808 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_basereg);
3809 ARM_LDFD (code, ins->dreg, ARMREG_LR, 0);
3811 ARM_LDFD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3814 case OP_STORER4_MEMBASE_REG:
3815 g_assert (arm_is_fpimm8 (ins->inst_offset));
3816 ARM_STFS (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3818 case OP_LOADR4_MEMBASE:
3819 g_assert (arm_is_fpimm8 (ins->inst_offset));
3820 ARM_LDFS (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3822 case OP_ICONV_TO_R_UN: {
3824 tmpreg = ins->dreg == 0? 1: 0;
3825 ARM_CMP_REG_IMM8 (code, ins->sreg1, 0);
3826 ARM_FLTD (code, ins->dreg, ins->sreg1);
3827 ARM_B_COND (code, ARMCOND_GE, 8);
3828 /* save the temp register */
3829 ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
3830 ARM_STFD (code, tmpreg, ARMREG_SP, 0);
3831 ARM_LDFD (code, tmpreg, ARMREG_PC, 12);
3832 ARM_FPA_ADFD (code, ins->dreg, ins->dreg, tmpreg);
3833 ARM_LDFD (code, tmpreg, ARMREG_SP, 0);
3834 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
3835 /* skip the constant pool */
3838 *(int*)code = 0x41f00000;
3843 * ldfltd ftemp, [pc, #8] 0x41f00000 0x00000000
3844 * adfltd fdest, fdest, ftemp
3848 case OP_ICONV_TO_R4:
3849 ARM_FLTS (code, ins->dreg, ins->sreg1);
3851 case OP_ICONV_TO_R8:
3852 ARM_FLTD (code, ins->dreg, ins->sreg1);
3855 #elif defined(ARM_FPU_VFP)
3858 if (cfg->compile_aot) {
3859 ARM_FLDD (code, ins->dreg, ARMREG_PC, 0);
3861 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3863 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
3866 /* FIXME: we can optimize the imm load by dealing with part of
3867 * the displacement in LDFD (aligning to 512).
3869 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3870 ARM_FLDD (code, ins->dreg, ARMREG_LR, 0);
3874 if (cfg->compile_aot) {
3875 ARM_FLDS (code, ins->dreg, ARMREG_PC, 0);
3877 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3879 ARM_CVTS (code, ins->dreg, ins->dreg);
3881 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3882 ARM_FLDS (code, ins->dreg, ARMREG_LR, 0);
3883 ARM_CVTS (code, ins->dreg, ins->dreg);
3886 case OP_STORER8_MEMBASE_REG:
3887 /* This is generated by the local regalloc pass which runs after the lowering pass */
3888 if (!arm_is_fpimm8 (ins->inst_offset)) {
3889 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3890 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_destbasereg);
3891 ARM_FSTD (code, ins->sreg1, ARMREG_LR, 0);
3893 ARM_FSTD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3896 case OP_LOADR8_MEMBASE:
3897 /* This is generated by the local regalloc pass which runs after the lowering pass */
3898 if (!arm_is_fpimm8 (ins->inst_offset)) {
3899 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3900 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_basereg);
3901 ARM_FLDD (code, ins->dreg, ARMREG_LR, 0);
3903 ARM_FLDD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3906 case OP_STORER4_MEMBASE_REG:
3907 g_assert (arm_is_fpimm8 (ins->inst_offset));
3908 ARM_CVTD (code, ARM_VFP_F0, ins->sreg1);
3909 ARM_FSTS (code, ARM_VFP_F0, ins->inst_destbasereg, ins->inst_offset);
3911 case OP_LOADR4_MEMBASE:
3912 g_assert (arm_is_fpimm8 (ins->inst_offset));
3913 ARM_FLDS (code, ARM_VFP_F0, ins->inst_basereg, ins->inst_offset);
3914 ARM_CVTS (code, ins->dreg, ARM_VFP_F0);
3916 case OP_ICONV_TO_R_UN: {
3917 g_assert_not_reached ();
3920 case OP_ICONV_TO_R4:
3921 ARM_FMSR (code, ARM_VFP_F0, ins->sreg1);
3922 ARM_FSITOS (code, ARM_VFP_F0, ARM_VFP_F0);
3923 ARM_CVTS (code, ins->dreg, ARM_VFP_F0);
3925 case OP_ICONV_TO_R8:
3926 ARM_FMSR (code, ARM_VFP_F0, ins->sreg1);
3927 ARM_FSITOD (code, ins->dreg, ARM_VFP_F0);
3931 if (mono_method_signature (cfg->method)->ret->type == MONO_TYPE_R4) {
3932 ARM_CVTD (code, ARM_VFP_F0, ins->sreg1);
3933 ARM_FMRS (code, ARMREG_R0, ARM_VFP_F0);
3935 ARM_FMRRD (code, ARMREG_R0, ARMREG_R1, ins->sreg1);
3941 case OP_FCONV_TO_I1:
3942 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, TRUE);
3944 case OP_FCONV_TO_U1:
3945 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, FALSE);
3947 case OP_FCONV_TO_I2:
3948 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, TRUE);
3950 case OP_FCONV_TO_U2:
3951 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, FALSE);
3953 case OP_FCONV_TO_I4:
3955 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, TRUE);
3957 case OP_FCONV_TO_U4:
3959 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, FALSE);
3961 case OP_FCONV_TO_I8:
3962 case OP_FCONV_TO_U8:
3963 g_assert_not_reached ();
3964 /* Implemented as helper calls */
3966 case OP_LCONV_TO_R_UN:
3967 g_assert_not_reached ();
3968 /* Implemented as helper calls */
3970 case OP_LCONV_TO_OVF_I4_2: {
3971 guint8 *high_bit_not_set, *valid_negative, *invalid_negative, *valid_positive;
3973 * Valid ints: 0xffffffff:8000000 to 00000000:0x7f000000
3976 ARM_CMP_REG_IMM8 (code, ins->sreg1, 0);
3977 high_bit_not_set = code;
3978 ARM_B_COND (code, ARMCOND_GE, 0); /*branch if bit 31 of the lower part is not set*/
3980 ARM_CMN_REG_IMM8 (code, ins->sreg2, 1); /*This have the same effect as CMP reg, 0xFFFFFFFF */
3981 valid_negative = code;
3982 ARM_B_COND (code, ARMCOND_EQ, 0); /*branch if upper part == 0xFFFFFFFF (lower part has bit 31 set) */
3983 invalid_negative = code;
3984 ARM_B_COND (code, ARMCOND_AL, 0);
3986 arm_patch (high_bit_not_set, code);
3988 ARM_CMP_REG_IMM8 (code, ins->sreg2, 0);
3989 valid_positive = code;
3990 ARM_B_COND (code, ARMCOND_EQ, 0); /*branch if upper part == 0 (lower part has bit 31 clear)*/
3992 arm_patch (invalid_negative, code);
3993 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_AL, "OverflowException");
3995 arm_patch (valid_negative, code);
3996 arm_patch (valid_positive, code);
3998 if (ins->dreg != ins->sreg1)
3999 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
4004 ARM_FPA_ADFD (code, ins->dreg, ins->sreg1, ins->sreg2);
4007 ARM_FPA_SUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
4010 ARM_FPA_MUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
4013 ARM_FPA_DVFD (code, ins->dreg, ins->sreg1, ins->sreg2);
4016 ARM_MNFD (code, ins->dreg, ins->sreg1);
4018 #elif defined(ARM_FPU_VFP)
4020 ARM_VFP_ADDD (code, ins->dreg, ins->sreg1, ins->sreg2);
4023 ARM_VFP_SUBD (code, ins->dreg, ins->sreg1, ins->sreg2);
4026 ARM_VFP_MULD (code, ins->dreg, ins->sreg1, ins->sreg2);
4029 ARM_VFP_DIVD (code, ins->dreg, ins->sreg1, ins->sreg2);
4032 ARM_NEGD (code, ins->dreg, ins->sreg1);
4037 g_assert_not_reached ();
4041 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
4042 #elif defined(ARM_FPU_VFP)
4043 ARM_CMPD (code, ins->sreg1, ins->sreg2);
4049 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
4050 #elif defined(ARM_FPU_VFP)
4051 ARM_CMPD (code, ins->sreg1, ins->sreg2);
4054 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
4055 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
4059 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
4060 #elif defined(ARM_FPU_VFP)
4061 ARM_CMPD (code, ins->sreg1, ins->sreg2);
4064 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
4065 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
4069 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
4070 #elif defined(ARM_FPU_VFP)
4071 ARM_CMPD (code, ins->sreg1, ins->sreg2);
4074 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
4075 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
4076 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
4081 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
4082 #elif defined(ARM_FPU_VFP)
4083 ARM_CMPD (code, ins->sreg2, ins->sreg1);
4086 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
4087 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
4092 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
4093 #elif defined(ARM_FPU_VFP)
4094 ARM_CMPD (code, ins->sreg2, ins->sreg1);
4097 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
4098 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
4099 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
4101 /* ARM FPA flags table:
4102 * N Less than ARMCOND_MI
4103 * Z Equal ARMCOND_EQ
4104 * C Greater Than or Equal ARMCOND_CS
4105 * V Unordered ARMCOND_VS
4108 EMIT_COND_BRANCH (ins, OP_IBEQ - OP_IBEQ);
4111 EMIT_COND_BRANCH (ins, OP_IBNE_UN - OP_IBEQ);
4114 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
4117 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
4118 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
4124 g_assert_not_reached ();
4128 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_GE);
4130 /* FPA requires EQ even thou the docs suggests that just CS is enough */
4131 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_EQ);
4132 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_CS);
4136 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
4137 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_GE);
4142 if (ins->dreg != ins->sreg1)
4143 ARM_MVFD (code, ins->dreg, ins->sreg1);
4144 #elif defined(ARM_FPU_VFP)
4145 ARM_ABSD (code, ARM_VFP_D1, ins->sreg1);
4146 ARM_FLDD (code, ARM_VFP_D0, ARMREG_PC, 0);
4148 *(guint32*)code = 0xffffffff;
4150 *(guint32*)code = 0x7fefffff;
4152 ARM_CMPD (code, ARM_VFP_D1, ARM_VFP_D0);
4154 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_GT, "ArithmeticException");
4155 ARM_CMPD (code, ins->sreg1, ins->sreg1);
4157 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VS, "ArithmeticException");
4159 ARM_CPYD (code, ins->dreg, ins->sreg1);
4164 g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
4165 g_assert_not_reached ();
4168 if ((cfg->opt & MONO_OPT_BRANCH) && ((code - cfg->native_code - offset) > max_len)) {
4169 g_warning ("wrong maximal instruction length of instruction %s (expected %d, got %d)",
4170 mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset);
4171 g_assert_not_reached ();
4177 last_offset = offset;
4180 cfg->code_len = code - cfg->native_code;
4183 #endif /* DISABLE_JIT */
4185 #ifdef HAVE_AEABI_READ_TP
4186 void __aeabi_read_tp (void);
4190 mono_arch_register_lowlevel_calls (void)
4192 /* The signature doesn't matter */
4193 mono_register_jit_icall (mono_arm_throw_exception, "mono_arm_throw_exception", mono_create_icall_signature ("void"), TRUE);
4194 mono_register_jit_icall (mono_arm_throw_exception_by_token, "mono_arm_throw_exception_by_token", mono_create_icall_signature ("void"), TRUE);
4196 #ifdef HAVE_AEABI_READ_TP
4197 mono_register_jit_icall (__aeabi_read_tp, "__aeabi_read_tp", mono_create_icall_signature ("void"), TRUE);
4201 #define patch_lis_ori(ip,val) do {\
4202 guint16 *__lis_ori = (guint16*)(ip); \
4203 __lis_ori [1] = (((guint32)(val)) >> 16) & 0xffff; \
4204 __lis_ori [3] = ((guint32)(val)) & 0xffff; \
4208 mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, gboolean run_cctors)
4210 MonoJumpInfo *patch_info;
4211 gboolean compile_aot = !run_cctors;
4213 for (patch_info = ji; patch_info; patch_info = patch_info->next) {
4214 unsigned char *ip = patch_info->ip.i + code;
4215 const unsigned char *target;
4217 if (patch_info->type == MONO_PATCH_INFO_SWITCH && !compile_aot) {
4218 gpointer *jt = (gpointer*)(ip + 8);
4220 /* jt is the inlined jump table, 2 instructions after ip
4221 * In the normal case we store the absolute addresses,
4222 * otherwise the displacements.
4224 for (i = 0; i < patch_info->data.table->table_size; i++)
4225 jt [i] = code + (int)patch_info->data.table->table [i];
4228 target = mono_resolve_patch_target (method, domain, code, patch_info, run_cctors);
4231 switch (patch_info->type) {
4232 case MONO_PATCH_INFO_BB:
4233 case MONO_PATCH_INFO_LABEL:
4236 /* No need to patch these */
4241 switch (patch_info->type) {
4242 case MONO_PATCH_INFO_IP:
4243 g_assert_not_reached ();
4244 patch_lis_ori (ip, ip);
4246 case MONO_PATCH_INFO_METHOD_REL:
4247 g_assert_not_reached ();
4248 *((gpointer *)(ip)) = code + patch_info->data.offset;
4250 case MONO_PATCH_INFO_METHODCONST:
4251 case MONO_PATCH_INFO_CLASS:
4252 case MONO_PATCH_INFO_IMAGE:
4253 case MONO_PATCH_INFO_FIELD:
4254 case MONO_PATCH_INFO_VTABLE:
4255 case MONO_PATCH_INFO_IID:
4256 case MONO_PATCH_INFO_SFLDA:
4257 case MONO_PATCH_INFO_LDSTR:
4258 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
4259 case MONO_PATCH_INFO_LDTOKEN:
4260 g_assert_not_reached ();
4261 /* from OP_AOTCONST : lis + ori */
4262 patch_lis_ori (ip, target);
4264 case MONO_PATCH_INFO_R4:
4265 case MONO_PATCH_INFO_R8:
4266 g_assert_not_reached ();
4267 *((gconstpointer *)(ip + 2)) = patch_info->data.target;
4269 case MONO_PATCH_INFO_EXC_NAME:
4270 g_assert_not_reached ();
4271 *((gconstpointer *)(ip + 1)) = patch_info->data.name;
4273 case MONO_PATCH_INFO_NONE:
4274 case MONO_PATCH_INFO_BB_OVF:
4275 case MONO_PATCH_INFO_EXC_OVF:
4276 /* everything is dealt with at epilog output time */
4281 arm_patch_general (domain, ip, target);
4286 * Stack frame layout:
4288 * ------------------- fp
4289 * MonoLMF structure or saved registers
4290 * -------------------
4292 * -------------------
4294 * -------------------
4295 * optional 8 bytes for tracing
4296 * -------------------
4297 * param area size is cfg->param_area
4298 * ------------------- sp
4301 mono_arch_emit_prolog (MonoCompile *cfg)
4303 MonoMethod *method = cfg->method;
4305 MonoMethodSignature *sig;
4307 int alloc_size, pos, max_offset, i, rot_amount;
4312 int prev_sp_offset, reg_offset;
4314 if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
4317 sig = mono_method_signature (method);
4318 cfg->code_size = 256 + sig->param_count * 20;
4319 code = cfg->native_code = g_malloc (cfg->code_size);
4321 mono_emit_unwind_op_def_cfa (cfg, code, ARMREG_SP, 0);
4323 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
4325 alloc_size = cfg->stack_offset;
4328 if (!method->save_lmf) {
4329 /* We save SP by storing it into IP and saving IP */
4330 ARM_PUSH (code, (cfg->used_int_regs | (1 << ARMREG_IP) | (1 << ARMREG_LR)));
4331 prev_sp_offset = 8; /* ip and lr */
4332 for (i = 0; i < 16; ++i) {
4333 if (cfg->used_int_regs & (1 << i))
4334 prev_sp_offset += 4;
4336 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset);
4338 for (i = 0; i < 16; ++i) {
4339 if ((cfg->used_int_regs & (1 << i)) || (i == ARMREG_IP) || (i == ARMREG_LR)) {
4340 mono_emit_unwind_op_offset (cfg, code, i, (- prev_sp_offset) + reg_offset);
4345 ARM_PUSH (code, 0x5ff0);
4346 prev_sp_offset = 4 * 10; /* all but r0-r3, sp and pc */
4347 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset);
4349 for (i = 0; i < 16; ++i) {
4350 if ((i > ARMREG_R3) && (i != ARMREG_SP) && (i != ARMREG_PC)) {
4351 mono_emit_unwind_op_offset (cfg, code, i, (- prev_sp_offset) + reg_offset);
4355 pos += sizeof (MonoLMF) - prev_sp_offset;
4359 // align to MONO_ARCH_FRAME_ALIGNMENT bytes
4360 if (alloc_size & (MONO_ARCH_FRAME_ALIGNMENT - 1)) {
4361 alloc_size += MONO_ARCH_FRAME_ALIGNMENT - 1;
4362 alloc_size &= ~(MONO_ARCH_FRAME_ALIGNMENT - 1);
4365 /* the stack used in the pushed regs */
4366 if (prev_sp_offset & 4)
4368 cfg->stack_usage = alloc_size;
4370 if ((i = mono_arm_is_rotated_imm8 (alloc_size, &rot_amount)) >= 0) {
4371 ARM_SUB_REG_IMM (code, ARMREG_SP, ARMREG_SP, i, rot_amount);
4373 code = mono_arm_emit_load_imm (code, ARMREG_IP, alloc_size);
4374 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
4376 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset + alloc_size);
4378 if (cfg->frame_reg != ARMREG_SP) {
4379 ARM_MOV_REG_REG (code, cfg->frame_reg, ARMREG_SP);
4380 mono_emit_unwind_op_def_cfa_reg (cfg, code, cfg->frame_reg);
4382 //g_print ("prev_sp_offset: %d, alloc_size:%d\n", prev_sp_offset, alloc_size);
4383 prev_sp_offset += alloc_size;
4385 /* compute max_offset in order to use short forward jumps
4386 * we could skip do it on arm because the immediate displacement
4387 * for jumps is large enough, it may be useful later for constant pools
4390 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
4391 MonoInst *ins = bb->code;
4392 bb->max_offset = max_offset;
4394 if (cfg->prof_options & MONO_PROFILE_COVERAGE)
4397 MONO_BB_FOR_EACH_INS (bb, ins)
4398 max_offset += ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
4401 /* store runtime generic context */
4402 if (cfg->rgctx_var) {
4403 MonoInst *ins = cfg->rgctx_var;
4405 g_assert (ins->opcode == OP_REGOFFSET);
4407 if (arm_is_imm12 (ins->inst_offset)) {
4408 ARM_STR_IMM (code, MONO_ARCH_RGCTX_REG, ins->inst_basereg, ins->inst_offset);
4410 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
4411 ARM_STR_REG_REG (code, MONO_ARCH_RGCTX_REG, ins->inst_basereg, ARMREG_LR);
4415 /* load arguments allocated to register from the stack */
4418 cinfo = get_call_info (NULL, sig, sig->pinvoke);
4420 if (MONO_TYPE_ISSTRUCT (sig->ret) && cinfo->ret.storage != RegTypeStructByVal) {
4421 ArgInfo *ainfo = &cinfo->ret;
4422 inst = cfg->vret_addr;
4423 g_assert (arm_is_imm12 (inst->inst_offset));
4424 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
4426 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
4427 ArgInfo *ainfo = cinfo->args + i;
4428 inst = cfg->args [pos];
4430 if (cfg->verbose_level > 2)
4431 g_print ("Saving argument %d (type: %d)\n", i, ainfo->storage);
4432 if (inst->opcode == OP_REGVAR) {
4433 if (ainfo->storage == RegTypeGeneral)
4434 ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
4435 else if (ainfo->storage == RegTypeFP) {
4436 g_assert_not_reached ();
4437 } else if (ainfo->storage == RegTypeBase) {
4438 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
4439 ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset));
4441 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
4442 ARM_LDR_REG_REG (code, inst->dreg, ARMREG_SP, ARMREG_IP);
4445 g_assert_not_reached ();
4447 if (cfg->verbose_level > 2)
4448 g_print ("Argument %d assigned to register %s\n", pos, mono_arch_regname (inst->dreg));
4450 /* the argument should be put on the stack: FIXME handle size != word */
4451 if (ainfo->storage == RegTypeGeneral || ainfo->storage == RegTypeIRegPair) {
4452 switch (ainfo->size) {
4454 if (arm_is_imm12 (inst->inst_offset))
4455 ARM_STRB_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
4457 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
4458 ARM_STRB_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
4462 if (arm_is_imm8 (inst->inst_offset)) {
4463 ARM_STRH_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
4465 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
4466 ARM_STRH_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
4470 g_assert (arm_is_imm12 (inst->inst_offset));
4471 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
4472 g_assert (arm_is_imm12 (inst->inst_offset + 4));
4473 ARM_STR_IMM (code, ainfo->reg + 1, inst->inst_basereg, inst->inst_offset + 4);
4476 if (arm_is_imm12 (inst->inst_offset)) {
4477 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
4479 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
4480 ARM_STR_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
4484 } else if (ainfo->storage == RegTypeBaseGen) {
4485 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
4486 g_assert (arm_is_imm12 (inst->inst_offset));
4487 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
4488 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
4489 ARM_STR_IMM (code, ARMREG_R3, inst->inst_basereg, inst->inst_offset);
4490 } else if (ainfo->storage == RegTypeBase) {
4491 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
4492 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
4494 code = mono_arm_emit_load_imm (code, ARMREG_IP, prev_sp_offset + ainfo->offset);
4495 ARM_LDR_REG_REG (code, ARMREG_LR, ARMREG_SP, ARMREG_IP);
4498 switch (ainfo->size) {
4500 if (arm_is_imm8 (inst->inst_offset)) {
4501 ARM_STRB_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
4503 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
4504 ARM_STRB_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
4508 if (arm_is_imm8 (inst->inst_offset)) {
4509 ARM_STRH_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
4511 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
4512 ARM_STRH_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
4516 if (arm_is_imm12 (inst->inst_offset)) {
4517 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
4519 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
4520 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
4522 if (arm_is_imm12 (prev_sp_offset + ainfo->offset + 4)) {
4523 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset + 4));
4525 code = mono_arm_emit_load_imm (code, ARMREG_IP, prev_sp_offset + ainfo->offset + 4);
4526 ARM_LDR_REG_REG (code, ARMREG_LR, ARMREG_SP, ARMREG_IP);
4528 if (arm_is_imm12 (inst->inst_offset + 4)) {
4529 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
4531 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset + 4);
4532 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
4536 if (arm_is_imm12 (inst->inst_offset)) {
4537 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
4539 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
4540 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
4544 } else if (ainfo->storage == RegTypeFP) {
4545 g_assert_not_reached ();
4546 } else if (ainfo->storage == RegTypeStructByVal) {
4547 int doffset = inst->inst_offset;
4551 size = mini_type_stack_size_full (cfg->generic_sharing_context, inst->inst_vtype, NULL, sig->pinvoke);
4552 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
4553 if (arm_is_imm12 (doffset)) {
4554 ARM_STR_IMM (code, ainfo->reg + cur_reg, inst->inst_basereg, doffset);
4556 code = mono_arm_emit_load_imm (code, ARMREG_IP, doffset);
4557 ARM_STR_REG_REG (code, ainfo->reg + cur_reg, inst->inst_basereg, ARMREG_IP);
4559 soffset += sizeof (gpointer);
4560 doffset += sizeof (gpointer);
4562 if (ainfo->vtsize) {
4563 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
4564 //g_print ("emit_memcpy (prev_sp_ofs: %d, ainfo->offset: %d, soffset: %d)\n", prev_sp_offset, ainfo->offset, soffset);
4565 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, doffset, ARMREG_SP, prev_sp_offset + ainfo->offset);
4567 } else if (ainfo->storage == RegTypeStructByAddr) {
4568 g_assert_not_reached ();
4569 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
4570 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, inst->inst_offset, ainfo->reg, 0);
4572 g_assert_not_reached ();
4577 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
4578 if (cfg->compile_aot)
4579 /* AOT code is only used in the root domain */
4580 code = mono_arm_emit_load_imm (code, ARMREG_R0, 0);
4582 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->domain);
4583 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
4584 (gpointer)"mono_jit_thread_attach");
4585 code = emit_call_seq (cfg, code);
4588 if (method->save_lmf) {
4589 gboolean get_lmf_fast = FALSE;
4591 #ifdef HAVE_AEABI_READ_TP
4592 gint32 lmf_addr_tls_offset = mono_get_lmf_addr_tls_offset ();
4594 if (lmf_addr_tls_offset != -1) {
4595 get_lmf_fast = TRUE;
4597 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
4598 (gpointer)"__aeabi_read_tp");
4599 code = emit_call_seq (cfg, code);
4601 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, lmf_addr_tls_offset);
4602 get_lmf_fast = TRUE;
4605 if (!get_lmf_fast) {
4606 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
4607 (gpointer)"mono_get_lmf_addr");
4608 code = emit_call_seq (cfg, code);
4610 /* we build the MonoLMF structure on the stack - see mini-arm.h */
4611 /* lmf_offset is the offset from the previous stack pointer,
4612 * alloc_size is the total stack space allocated, so the offset
4613 * of MonoLMF from the current stack ptr is alloc_size - lmf_offset.
4614 * The pointer to the struct is put in r1 (new_lmf).
4615 * r2 is used as scratch
4616 * The callee-saved registers are already in the MonoLMF structure
4618 code = emit_big_add (code, ARMREG_R1, ARMREG_SP, alloc_size - lmf_offset);
4619 /* r0 is the result from mono_get_lmf_addr () */
4620 ARM_STR_IMM (code, ARMREG_R0, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
4621 /* new_lmf->previous_lmf = *lmf_addr */
4622 ARM_LDR_IMM (code, ARMREG_R2, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4623 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4624 /* *(lmf_addr) = r1 */
4625 ARM_STR_IMM (code, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4626 /* Skip method (only needed for trampoline LMF frames) */
4627 ARM_STR_IMM (code, ARMREG_SP, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, ebp));
4628 /* save the current IP */
4629 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_PC);
4630 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, eip));
4634 code = mono_arch_instrument_prolog (cfg, mono_trace_enter_method, code, TRUE);
4636 if (cfg->arch.seq_point_info_var) {
4637 MonoInst *ins = cfg->arch.seq_point_info_var;
4639 /* Initialize the variable from a GOT slot */
4640 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_SEQ_POINT_INFO, cfg->method);
4641 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
4643 *(gpointer*)code = NULL;
4645 ARM_LDR_REG_REG (code, ARMREG_R0, ARMREG_PC, ARMREG_R0);
4647 g_assert (ins->opcode == OP_REGOFFSET);
4649 if (arm_is_imm12 (ins->inst_offset)) {
4650 ARM_STR_IMM (code, ARMREG_R0, ins->inst_basereg, ins->inst_offset);
4652 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
4653 ARM_STR_REG_REG (code, ARMREG_R0, ins->inst_basereg, ARMREG_LR);
4657 /* Initialize ss_trigger_page_var */
4659 MonoInst *info_var = cfg->arch.seq_point_info_var;
4660 MonoInst *ss_trigger_page_var = cfg->arch.ss_trigger_page_var;
4661 int dreg = ARMREG_LR;
4664 g_assert (info_var->opcode == OP_REGOFFSET);
4665 g_assert (arm_is_imm12 (info_var->inst_offset));
4667 ARM_LDR_IMM (code, dreg, info_var->inst_basereg, info_var->inst_offset);
4668 /* Load the trigger page addr */
4669 ARM_LDR_IMM (code, dreg, dreg, G_STRUCT_OFFSET (SeqPointInfo, ss_trigger_page));
4670 ARM_STR_IMM (code, dreg, ss_trigger_page_var->inst_basereg, ss_trigger_page_var->inst_offset);
4674 cfg->code_len = code - cfg->native_code;
4675 g_assert (cfg->code_len < cfg->code_size);
4682 mono_arch_emit_epilog (MonoCompile *cfg)
4684 MonoMethod *method = cfg->method;
4685 int pos, i, rot_amount;
4686 int max_epilog_size = 16 + 20*4;
4690 if (cfg->method->save_lmf)
4691 max_epilog_size += 128;
4693 if (mono_jit_trace_calls != NULL)
4694 max_epilog_size += 50;
4696 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE)
4697 max_epilog_size += 50;
4699 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
4700 cfg->code_size *= 2;
4701 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
4702 mono_jit_stats.code_reallocs++;
4706 * Keep in sync with OP_JMP
4708 code = cfg->native_code + cfg->code_len;
4710 if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) {
4711 code = mono_arch_instrument_epilog (cfg, mono_trace_leave_method, code, TRUE);
4715 /* Load returned vtypes into registers if needed */
4716 cinfo = cfg->arch.cinfo;
4717 if (cinfo->ret.storage == RegTypeStructByVal) {
4718 MonoInst *ins = cfg->ret;
4720 if (arm_is_imm12 (ins->inst_offset)) {
4721 ARM_LDR_IMM (code, ARMREG_R0, ins->inst_basereg, ins->inst_offset);
4723 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
4724 ARM_LDR_REG_REG (code, ARMREG_R0, ins->inst_basereg, ARMREG_LR);
4728 if (method->save_lmf) {
4730 /* all but r0-r3, sp and pc */
4731 pos += sizeof (MonoLMF) - (4 * 10);
4733 /* r2 contains the pointer to the current LMF */
4734 code = emit_big_add (code, ARMREG_R2, cfg->frame_reg, cfg->stack_usage - lmf_offset);
4735 /* ip = previous_lmf */
4736 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4738 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
4739 /* *(lmf_addr) = previous_lmf */
4740 ARM_STR_IMM (code, ARMREG_IP, ARMREG_LR, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4741 /* FIXME: speedup: there is no actual need to restore the registers if
4742 * we didn't actually change them (idea from Zoltan).
4745 /* point sp at the registers to restore: 10 is 14 -4, because we skip r0-r3 */
4746 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_R2, (sizeof (MonoLMF) - 10 * sizeof (gulong)));
4747 ARM_POP_NWB (code, 0xaff0); /* restore ip to sp and lr to pc */
4749 if ((i = mono_arm_is_rotated_imm8 (cfg->stack_usage, &rot_amount)) >= 0) {
4750 ARM_ADD_REG_IMM (code, ARMREG_SP, cfg->frame_reg, i, rot_amount);
4752 code = mono_arm_emit_load_imm (code, ARMREG_IP, cfg->stack_usage);
4753 ARM_ADD_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
4755 /* FIXME: add v4 thumb interworking support */
4756 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP) | (1 << ARMREG_PC)));
4759 cfg->code_len = code - cfg->native_code;
4761 g_assert (cfg->code_len < cfg->code_size);
4765 /* remove once throw_exception_by_name is eliminated */
4767 exception_id_by_name (const char *name)
4769 if (strcmp (name, "IndexOutOfRangeException") == 0)
4770 return MONO_EXC_INDEX_OUT_OF_RANGE;
4771 if (strcmp (name, "OverflowException") == 0)
4772 return MONO_EXC_OVERFLOW;
4773 if (strcmp (name, "ArithmeticException") == 0)
4774 return MONO_EXC_ARITHMETIC;
4775 if (strcmp (name, "DivideByZeroException") == 0)
4776 return MONO_EXC_DIVIDE_BY_ZERO;
4777 if (strcmp (name, "InvalidCastException") == 0)
4778 return MONO_EXC_INVALID_CAST;
4779 if (strcmp (name, "NullReferenceException") == 0)
4780 return MONO_EXC_NULL_REF;
4781 if (strcmp (name, "ArrayTypeMismatchException") == 0)
4782 return MONO_EXC_ARRAY_TYPE_MISMATCH;
4783 g_error ("Unknown intrinsic exception %s\n", name);
4788 mono_arch_emit_exceptions (MonoCompile *cfg)
4790 MonoJumpInfo *patch_info;
4793 const guint8* exc_throw_pos [MONO_EXC_INTRINS_NUM] = {NULL};
4794 guint8 exc_throw_found [MONO_EXC_INTRINS_NUM] = {0};
4795 int max_epilog_size = 50;
4797 /* count the number of exception infos */
4800 * make sure we have enough space for exceptions
4802 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
4803 if (patch_info->type == MONO_PATCH_INFO_EXC) {
4804 i = exception_id_by_name (patch_info->data.target);
4805 if (!exc_throw_found [i]) {
4806 max_epilog_size += 32;
4807 exc_throw_found [i] = TRUE;
4812 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
4813 cfg->code_size *= 2;
4814 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
4815 mono_jit_stats.code_reallocs++;
4818 code = cfg->native_code + cfg->code_len;
4820 /* add code to raise exceptions */
4821 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
4822 switch (patch_info->type) {
4823 case MONO_PATCH_INFO_EXC: {
4824 MonoClass *exc_class;
4825 unsigned char *ip = patch_info->ip.i + cfg->native_code;
4827 i = exception_id_by_name (patch_info->data.target);
4828 if (exc_throw_pos [i]) {
4829 arm_patch (ip, exc_throw_pos [i]);
4830 patch_info->type = MONO_PATCH_INFO_NONE;
4833 exc_throw_pos [i] = code;
4835 arm_patch (ip, code);
4837 exc_class = mono_class_from_name (mono_defaults.corlib, "System", patch_info->data.name);
4838 g_assert (exc_class);
4840 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_LR);
4841 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
4842 patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD;
4843 patch_info->data.name = "mono_arch_throw_corlib_exception";
4844 patch_info->ip.i = code - cfg->native_code;
4846 *(guint32*)(gpointer)code = exc_class->type_token;
4856 cfg->code_len = code - cfg->native_code;
4858 g_assert (cfg->code_len < cfg->code_size);
4862 static gboolean tls_offset_inited = FALSE;
4865 mono_arch_setup_jit_tls_data (MonoJitTlsData *tls)
4867 if (!tls_offset_inited) {
4868 tls_offset_inited = TRUE;
4870 lmf_tls_offset = mono_get_lmf_tls_offset ();
4871 lmf_addr_tls_offset = mono_get_lmf_addr_tls_offset ();
4876 mono_arch_free_jit_tls_data (MonoJitTlsData *tls)
4881 mono_arch_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
4888 mono_arch_print_tree (MonoInst *tree, int arity)
4894 mono_arch_get_domain_intrinsic (MonoCompile* cfg)
4896 return mono_get_domain_intrinsic (cfg);
4900 mono_arch_get_patch_offset (guint8 *code)
4907 mono_arch_flush_register_windows (void)
4911 #ifdef MONO_ARCH_HAVE_IMT
4914 mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoInst *imt_arg)
4916 if (cfg->compile_aot) {
4917 int method_reg = mono_alloc_ireg (cfg);
4920 call->dynamic_imt_arg = TRUE;
4923 mono_call_inst_add_outarg_reg (cfg, call, imt_arg->dreg, ARMREG_V5, FALSE);
4925 MONO_INST_NEW (cfg, ins, OP_AOTCONST);
4926 ins->dreg = method_reg;
4927 ins->inst_p0 = call->method;
4928 ins->inst_c1 = MONO_PATCH_INFO_METHODCONST;
4929 MONO_ADD_INS (cfg->cbb, ins);
4931 mono_call_inst_add_outarg_reg (cfg, call, method_reg, ARMREG_V5, FALSE);
4933 } else if (cfg->generic_context) {
4935 /* Always pass in a register for simplicity */
4936 call->dynamic_imt_arg = TRUE;
4938 cfg->uses_rgctx_reg = TRUE;
4941 mono_call_inst_add_outarg_reg (cfg, call, imt_arg->dreg, ARMREG_V5, FALSE);
4944 int method_reg = mono_alloc_preg (cfg);
4946 MONO_INST_NEW (cfg, ins, OP_PCONST);
4947 ins->inst_p0 = call->method;
4948 ins->dreg = method_reg;
4949 MONO_ADD_INS (cfg->cbb, ins);
4951 mono_call_inst_add_outarg_reg (cfg, call, method_reg, ARMREG_V5, FALSE);
4957 mono_arch_find_imt_method (mgreg_t *regs, guint8 *code)
4959 guint32 *code_ptr = (guint32*)code;
4961 /* The IMT value is stored in the code stream right after the LDC instruction. */
4962 if (!IS_LDR_PC (code_ptr [0])) {
4963 g_warning ("invalid code stream, instruction before IMT value is not a LDC in %s() (code %p value 0: 0x%x -1: 0x%x -2: 0x%x)", __FUNCTION__, code, code_ptr [2], code_ptr [1], code_ptr [0]);
4964 g_assert (IS_LDR_PC (code_ptr [0]));
4966 if (code_ptr [1] == 0)
4967 /* This is AOTed code, the IMT method is in V5 */
4968 return (MonoMethod*)regs [ARMREG_V5];
4970 return (MonoMethod*) code_ptr [1];
4974 mono_arch_find_this_argument (mgreg_t *regs, MonoMethod *method, MonoGenericSharingContext *gsctx)
4976 return mono_arch_get_this_arg_from_call (gsctx, mono_method_signature (method), regs, NULL);
4980 mono_arch_find_static_call_vtable (mgreg_t *regs, guint8 *code)
4982 return (MonoVTable*) regs [MONO_ARCH_RGCTX_REG];
4985 #define ENABLE_WRONG_METHOD_CHECK 0
4986 #define BASE_SIZE (6 * 4)
4987 #define BSEARCH_ENTRY_SIZE (4 * 4)
4988 #define CMP_SIZE (3 * 4)
4989 #define BRANCH_SIZE (1 * 4)
4990 #define CALL_SIZE (2 * 4)
4991 #define WMC_SIZE (5 * 4)
4992 #define DISTANCE(A, B) (((gint32)(B)) - ((gint32)(A)))
4995 arm_emit_value_and_patch_ldr (arminstr_t *code, arminstr_t *target, guint32 value)
4997 guint32 delta = DISTANCE (target, code);
4999 g_assert (delta >= 0 && delta <= 0xFFF);
5000 *target = *target | delta;
5006 mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count,
5007 gpointer fail_tramp)
5009 int size, i, extra_space = 0;
5010 arminstr_t *code, *start, *vtable_target = NULL;
5011 gboolean large_offsets = FALSE;
5012 guint32 **constant_pool_starts;
5015 constant_pool_starts = g_new0 (guint32*, count);
5018 * We might be called with a fail_tramp from the IMT builder code even if
5019 * MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK is not defined.
5021 //g_assert (!fail_tramp);
5023 for (i = 0; i < count; ++i) {
5024 MonoIMTCheckItem *item = imt_entries [i];
5025 if (item->is_equals) {
5026 if (!arm_is_imm12 (DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]))) {
5027 item->chunk_size += 32;
5028 large_offsets = TRUE;
5031 if (item->check_target_idx) {
5032 if (!item->compare_done)
5033 item->chunk_size += CMP_SIZE;
5034 item->chunk_size += BRANCH_SIZE;
5036 #if ENABLE_WRONG_METHOD_CHECK
5037 item->chunk_size += WMC_SIZE;
5040 item->chunk_size += CALL_SIZE;
5042 item->chunk_size += BSEARCH_ENTRY_SIZE;
5043 imt_entries [item->check_target_idx]->compare_done = TRUE;
5045 size += item->chunk_size;
5049 size += 4 * count; /* The ARM_ADD_REG_IMM to pop the stack */
5051 start = code = mono_domain_code_reserve (domain, size);
5054 printf ("building IMT thunk for class %s %s entries %d code size %d code at %p end %p vtable %p\n", vtable->klass->name_space, vtable->klass->name, count, size, start, ((guint8*)start) + size, vtable);
5055 for (i = 0; i < count; ++i) {
5056 MonoIMTCheckItem *item = imt_entries [i];
5057 printf ("method %d (%p) %s vtable slot %p is_equals %d chunk size %d\n", i, item->key, item->key->name, &vtable->vtable [item->value.vtable_slot], item->is_equals, item->chunk_size);
5062 ARM_PUSH4 (code, ARMREG_R0, ARMREG_R1, ARMREG_IP, ARMREG_PC);
5064 ARM_PUSH2 (code, ARMREG_R0, ARMREG_R1);
5065 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, -4);
5066 vtable_target = code;
5067 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
5069 /* R0 == 0 means we are called from AOT code. In this case, V5 contains the IMT method */
5070 ARM_CMP_REG_IMM8 (code, ARMREG_R0, 0);
5071 ARM_MOV_REG_REG_COND (code, ARMREG_R0, ARMREG_V5, ARMCOND_EQ);
5073 for (i = 0; i < count; ++i) {
5074 MonoIMTCheckItem *item = imt_entries [i];
5075 arminstr_t *imt_method = NULL, *vtable_offset_ins = NULL;
5076 gint32 vtable_offset;
5078 item->code_target = (guint8*)code;
5080 if (item->is_equals) {
5081 if (item->check_target_idx) {
5082 if (!item->compare_done) {
5084 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
5085 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
5087 item->jmp_code = (guint8*)code;
5088 ARM_B_COND (code, ARMCOND_NE, 0);
5090 /*Enable the commented code to assert on wrong method*/
5091 #if ENABLE_WRONG_METHOD_CHECK
5093 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
5094 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
5095 ARM_B_COND (code, ARMCOND_NE, 1);
5101 vtable_offset = DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]);
5102 if (!arm_is_imm12 (vtable_offset)) {
5104 * We need to branch to a computed address but we don't have
5105 * a free register to store it, since IP must contain the
5106 * vtable address. So we push the two values to the stack, and
5107 * load them both using LDM.
5109 /* Compute target address */
5110 vtable_offset_ins = code;
5111 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
5112 ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_IP, ARMREG_R1);
5113 /* Save it to the fourth slot */
5114 ARM_STR_IMM (code, ARMREG_R1, ARMREG_SP, 3 * sizeof (gpointer));
5115 /* Restore registers and branch */
5116 ARM_POP4 (code, ARMREG_R0, ARMREG_R1, ARMREG_IP, ARMREG_PC);
5118 code = arm_emit_value_and_patch_ldr (code, vtable_offset_ins, vtable_offset);
5120 ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
5122 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 2 * sizeof (gpointer));
5123 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, vtable_offset);
5127 code = arm_emit_value_and_patch_ldr (code, imt_method, (guint32)item->key);
5129 /*must emit after unconditional branch*/
5130 if (vtable_target) {
5131 code = arm_emit_value_and_patch_ldr (code, vtable_target, (guint32)vtable);
5132 item->chunk_size += 4;
5133 vtable_target = NULL;
5136 /*We reserve the space for bsearch IMT values after the first entry with an absolute jump*/
5137 constant_pool_starts [i] = code;
5139 code += extra_space;
5143 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
5144 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
5146 item->jmp_code = (guint8*)code;
5147 ARM_B_COND (code, ARMCOND_GE, 0);
5152 for (i = 0; i < count; ++i) {
5153 MonoIMTCheckItem *item = imt_entries [i];
5154 if (item->jmp_code) {
5155 if (item->check_target_idx)
5156 arm_patch (item->jmp_code, imt_entries [item->check_target_idx]->code_target);
5158 if (i > 0 && item->is_equals) {
5160 arminstr_t *space_start = constant_pool_starts [i];
5161 for (j = i - 1; j >= 0 && !imt_entries [j]->is_equals; --j) {
5162 space_start = arm_emit_value_and_patch_ldr (space_start, (arminstr_t*)imt_entries [j]->code_target, (guint32)imt_entries [j]->key);
5169 char *buff = g_strdup_printf ("thunk_for_class_%s_%s_entries_%d", vtable->klass->name_space, vtable->klass->name, count);
5170 mono_disassemble_code (NULL, (guint8*)start, size, buff);
5175 g_free (constant_pool_starts);
5177 mono_arch_flush_icache ((guint8*)start, size);
5178 mono_stats.imt_thunks_size += code - start;
5180 g_assert (DISTANCE (start, code) <= size);
5187 mono_arch_context_get_int_reg (MonoContext *ctx, int reg)
5189 if (reg == ARMREG_SP)
5190 return (gpointer)ctx->esp;
5192 return (gpointer)ctx->regs [reg];
5196 * mono_arch_set_breakpoint:
5198 * Set a breakpoint at the native code corresponding to JI at NATIVE_OFFSET.
5199 * The location should contain code emitted by OP_SEQ_POINT.
5202 mono_arch_set_breakpoint (MonoJitInfo *ji, guint8 *ip)
5205 guint32 native_offset = ip - (guint8*)ji->code_start;
5208 SeqPointInfo *info = mono_arch_get_seq_point_info (mono_domain_get (), ji->code_start);
5210 g_assert (native_offset % 4 == 0);
5211 g_assert (info->bp_addrs [native_offset / 4] == 0);
5212 info->bp_addrs [native_offset / 4] = bp_trigger_page;
5214 int dreg = ARMREG_LR;
5216 /* Read from another trigger page */
5217 ARM_LDR_IMM (code, dreg, ARMREG_PC, 0);
5219 *(int*)code = (int)bp_trigger_page;
5221 ARM_LDR_IMM (code, dreg, dreg, 0);
5223 mono_arch_flush_icache (code - 16, 16);
5226 /* This is currently implemented by emitting an SWI instruction, which
5227 * qemu/linux seems to convert to a SIGILL.
5229 *(int*)code = (0xef << 24) | 8;
5231 mono_arch_flush_icache (code - 4, 4);
5237 * mono_arch_clear_breakpoint:
5239 * Clear the breakpoint at IP.
5242 mono_arch_clear_breakpoint (MonoJitInfo *ji, guint8 *ip)
5248 guint32 native_offset = ip - (guint8*)ji->code_start;
5249 SeqPointInfo *info = mono_arch_get_seq_point_info (mono_domain_get (), ji->code_start);
5251 g_assert (native_offset % 4 == 0);
5252 g_assert (info->bp_addrs [native_offset / 4] == bp_trigger_page);
5253 info->bp_addrs [native_offset / 4] = 0;
5255 for (i = 0; i < 4; ++i)
5258 mono_arch_flush_icache (ip, code - ip);
5263 * mono_arch_start_single_stepping:
5265 * Start single stepping.
5268 mono_arch_start_single_stepping (void)
5270 mono_mprotect (ss_trigger_page, mono_pagesize (), 0);
5274 * mono_arch_stop_single_stepping:
5276 * Stop single stepping.
5279 mono_arch_stop_single_stepping (void)
5281 mono_mprotect (ss_trigger_page, mono_pagesize (), MONO_MMAP_READ);
5285 #define DBG_SIGNAL SIGBUS
5287 #define DBG_SIGNAL SIGSEGV
5291 * mono_arch_is_single_step_event:
5293 * Return whenever the machine state in SIGCTX corresponds to a single
5297 mono_arch_is_single_step_event (void *info, void *sigctx)
5299 siginfo_t *sinfo = info;
5301 /* Sometimes the address is off by 4 */
5302 if (sinfo->si_addr >= ss_trigger_page && (guint8*)sinfo->si_addr <= (guint8*)ss_trigger_page + 128)
5309 * mono_arch_is_breakpoint_event:
5311 * Return whenever the machine state in SIGCTX corresponds to a breakpoint event.
5314 mono_arch_is_breakpoint_event (void *info, void *sigctx)
5316 siginfo_t *sinfo = info;
5318 if (sinfo->si_signo == DBG_SIGNAL) {
5319 /* Sometimes the address is off by 4 */
5320 if (sinfo->si_addr >= bp_trigger_page && (guint8*)sinfo->si_addr <= (guint8*)bp_trigger_page + 128)
5330 mono_arch_get_ip_for_breakpoint (MonoJitInfo *ji, MonoContext *ctx)
5332 guint8 *ip = MONO_CONTEXT_GET_IP (ctx);
5343 mono_arch_get_ip_for_single_step (MonoJitInfo *ji, MonoContext *ctx)
5345 guint8 *ip = MONO_CONTEXT_GET_IP (ctx);
5353 * mono_arch_skip_breakpoint:
5355 * See mini-amd64.c for docs.
5358 mono_arch_skip_breakpoint (MonoContext *ctx)
5360 MONO_CONTEXT_SET_IP (ctx, (guint8*)MONO_CONTEXT_GET_IP (ctx) + 4);
5364 * mono_arch_skip_single_step:
5366 * See mini-amd64.c for docs.
5369 mono_arch_skip_single_step (MonoContext *ctx)
5371 MONO_CONTEXT_SET_IP (ctx, (guint8*)MONO_CONTEXT_GET_IP (ctx) + 4);
5375 * mono_arch_get_seq_point_info:
5377 * See mini-amd64.c for docs.
5380 mono_arch_get_seq_point_info (MonoDomain *domain, guint8 *code)
5385 // FIXME: Add a free function
5387 mono_domain_lock (domain);
5388 info = g_hash_table_lookup (domain_jit_info (domain)->arch_seq_points,
5390 mono_domain_unlock (domain);
5393 ji = mono_jit_info_table_find (domain, (char*)code);
5396 info = g_malloc0 (sizeof (SeqPointInfo) + ji->code_size);
5398 info->ss_trigger_page = ss_trigger_page;
5399 info->bp_trigger_page = bp_trigger_page;
5401 mono_domain_lock (domain);
5402 g_hash_table_insert (domain_jit_info (domain)->arch_seq_points,
5404 mono_domain_unlock (domain);