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>
21 #include "mono/arch/arm/arm-fpa-codegen.h"
22 #elif defined(ARM_FPU_VFP)
23 #include "mono/arch/arm/arm-vfp-codegen.h"
26 #if defined(__ARM_EABI__) && defined(__linux__) && !defined(PLATFORM_ANDROID)
27 #define HAVE_AEABI_READ_TP 1
30 static gint lmf_tls_offset = -1;
31 static gint lmf_addr_tls_offset = -1;
33 /* This mutex protects architecture specific caches */
34 #define mono_mini_arch_lock() EnterCriticalSection (&mini_arch_mutex)
35 #define mono_mini_arch_unlock() LeaveCriticalSection (&mini_arch_mutex)
36 static CRITICAL_SECTION mini_arch_mutex;
38 static int v5_supported = 0;
39 static int v7_supported = 0;
40 static int thumb_supported = 0;
44 * floating point support: on ARM it is a mess, there are at least 3
45 * different setups, each of which binary incompat with the other.
46 * 1) FPA: old and ugly, but unfortunately what current distros use
47 * the double binary format has the two words swapped. 8 double registers.
48 * Implemented usually by kernel emulation.
49 * 2) softfloat: the compiler emulates all the fp ops. Usually uses the
50 * ugly swapped double format (I guess a softfloat-vfp exists, too, though).
51 * 3) VFP: the new and actually sensible and useful FP support. Implemented
52 * in HW or kernel-emulated, requires new tools. I think this is what symbian uses.
54 * The plan is to write the FPA support first. softfloat can be tested in a chroot.
56 int mono_exc_esp_offset = 0;
58 #define arm_is_imm12(v) ((v) > -4096 && (v) < 4096)
59 #define arm_is_imm8(v) ((v) > -256 && (v) < 256)
60 #define arm_is_fpimm8(v) ((v) >= -1020 && (v) <= 1020)
62 #define LDR_MASK ((0xf << ARMCOND_SHIFT) | (3 << 26) | (1 << 22) | (1 << 20) | (15 << 12))
63 #define LDR_PC_VAL ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 26) | (0 << 22) | (1 << 20) | (15 << 12))
64 #define IS_LDR_PC(val) (((val) & LDR_MASK) == LDR_PC_VAL)
66 #define ADD_LR_PC_4 ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 25) | (1 << 23) | (ARMREG_PC << 16) | (ARMREG_LR << 12) | 4)
67 #define MOV_LR_PC ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 24) | (0xa << 20) | (ARMREG_LR << 12) | ARMREG_PC)
71 mono_arch_regname (int reg)
73 static const char * rnames[] = {
74 "arm_r0", "arm_r1", "arm_r2", "arm_r3", "arm_v1",
75 "arm_v2", "arm_v3", "arm_v4", "arm_v5", "arm_v6",
76 "arm_v7", "arm_fp", "arm_ip", "arm_sp", "arm_lr",
79 if (reg >= 0 && reg < 16)
85 mono_arch_fregname (int reg)
87 static const char * rnames[] = {
88 "arm_f0", "arm_f1", "arm_f2", "arm_f3", "arm_f4",
89 "arm_f5", "arm_f6", "arm_f7", "arm_f8", "arm_f9",
90 "arm_f10", "arm_f11", "arm_f12", "arm_f13", "arm_f14",
91 "arm_f15", "arm_f16", "arm_f17", "arm_f18", "arm_f19",
92 "arm_f20", "arm_f21", "arm_f22", "arm_f23", "arm_f24",
93 "arm_f25", "arm_f26", "arm_f27", "arm_f28", "arm_f29",
96 if (reg >= 0 && reg < 32)
102 emit_big_add (guint8 *code, int dreg, int sreg, int imm)
104 int imm8, rot_amount;
105 if ((imm8 = mono_arm_is_rotated_imm8 (imm, &rot_amount)) >= 0) {
106 ARM_ADD_REG_IMM (code, dreg, sreg, imm8, rot_amount);
109 g_assert (dreg != sreg);
110 code = mono_arm_emit_load_imm (code, dreg, imm);
111 ARM_ADD_REG_REG (code, dreg, dreg, sreg);
116 emit_memcpy (guint8 *code, int size, int dreg, int doffset, int sreg, int soffset)
118 /* we can use r0-r3, since this is called only for incoming args on the stack */
119 if (size > sizeof (gpointer) * 4) {
121 code = emit_big_add (code, ARMREG_R0, sreg, soffset);
122 code = emit_big_add (code, ARMREG_R1, dreg, doffset);
123 start_loop = code = mono_arm_emit_load_imm (code, ARMREG_R2, size);
124 ARM_LDR_IMM (code, ARMREG_R3, ARMREG_R0, 0);
125 ARM_STR_IMM (code, ARMREG_R3, ARMREG_R1, 0);
126 ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_R0, 4);
127 ARM_ADD_REG_IMM8 (code, ARMREG_R1, ARMREG_R1, 4);
128 ARM_SUBS_REG_IMM8 (code, ARMREG_R2, ARMREG_R2, 4);
129 ARM_B_COND (code, ARMCOND_NE, 0);
130 arm_patch (code - 4, start_loop);
133 if (arm_is_imm12 (doffset) && arm_is_imm12 (doffset + size) &&
134 arm_is_imm12 (soffset) && arm_is_imm12 (soffset + size)) {
136 ARM_LDR_IMM (code, ARMREG_LR, sreg, soffset);
137 ARM_STR_IMM (code, ARMREG_LR, dreg, doffset);
143 code = emit_big_add (code, ARMREG_R0, sreg, soffset);
144 code = emit_big_add (code, ARMREG_R1, dreg, doffset);
145 doffset = soffset = 0;
147 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_R0, soffset);
148 ARM_STR_IMM (code, ARMREG_LR, ARMREG_R1, doffset);
154 g_assert (size == 0);
159 emit_call_reg (guint8 *code, int reg)
162 ARM_BLX_REG (code, reg);
164 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
168 ARM_MOV_REG_REG (code, ARMREG_PC, reg);
174 emit_call_seq (MonoCompile *cfg, guint8 *code)
176 if (cfg->method->dynamic) {
177 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
179 *(gpointer*)code = NULL;
181 code = emit_call_reg (code, ARMREG_IP);
189 emit_move_return_value (MonoCompile *cfg, MonoInst *ins, guint8 *code)
191 switch (ins->opcode) {
194 case OP_FCALL_MEMBASE:
196 if (ins->dreg != ARM_FPA_F0)
197 ARM_MVFD (code, ins->dreg, ARM_FPA_F0);
198 #elif defined(ARM_FPU_VFP)
199 if (((MonoCallInst*)ins)->signature->ret->type == MONO_TYPE_R4) {
200 ARM_FMSR (code, ins->dreg, ARMREG_R0);
201 ARM_CVTS (code, ins->dreg, ins->dreg);
203 ARM_FMDRR (code, ARMREG_R0, ARMREG_R1, ins->dreg);
213 * mono_arch_get_argument_info:
214 * @csig: a method signature
215 * @param_count: the number of parameters to consider
216 * @arg_info: an array to store the result infos
218 * Gathers information on parameters such as size, alignment and
219 * padding. arg_info should be large enought to hold param_count + 1 entries.
221 * Returns the size of the activation frame.
224 mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJitArgumentInfo *arg_info)
226 int k, frame_size = 0;
227 guint32 size, align, pad;
230 if (MONO_TYPE_ISSTRUCT (csig->ret)) {
231 frame_size += sizeof (gpointer);
235 arg_info [0].offset = offset;
238 frame_size += sizeof (gpointer);
242 arg_info [0].size = frame_size;
244 for (k = 0; k < param_count; k++) {
245 size = mini_type_stack_size_full (NULL, csig->params [k], &align, csig->pinvoke);
247 /* ignore alignment for now */
250 frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1);
251 arg_info [k].pad = pad;
253 arg_info [k + 1].pad = 0;
254 arg_info [k + 1].size = size;
256 arg_info [k + 1].offset = offset;
260 align = MONO_ARCH_FRAME_ALIGNMENT;
261 frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1);
262 arg_info [k].pad = pad;
268 decode_vcall_slot_from_ldr (guint32 ldr, mgreg_t *regs, int *displacement)
272 reg = (ldr >> 16 ) & 0xf;
273 offset = ldr & 0xfff;
274 if (((ldr >> 23) & 1) == 0) /*U bit, 0 means negative and 1 positive*/
276 /*g_print ("found vcall at r%d + %d for code at %p 0x%x\n", reg, offset, code, *code);*/
277 o = (gpointer)regs [reg];
279 *displacement = offset;
284 mono_arch_get_vcall_slot (guint8 *code_ptr, mgreg_t *regs, int *displacement)
286 guint32* code = (guint32*)code_ptr;
288 /* Locate the address of the method-specific trampoline. The call using
289 the vtable slot that took the processing flow to 'arch_create_jit_trampoline'
290 looks something like this:
299 The call sequence could be also:
302 function pointer literal
306 Note that on ARM5+ we can use one instruction instead of the last two.
307 Therefore, we need to locate the 'ldr rA' instruction to know which
308 register was used to hold the method addrs.
311 /* This is the instruction after "ldc pc, xxx", "mov pc, xxx" or "bl xxx" could be either the IMT value or some other instruction*/
314 /* Three possible code sequences can happen here:
318 * ldr pc, [rX - #offset]
324 * ldr pc, [rX - #offset]
326 * direct branch with bl:
330 * direct branch with mov:
334 * We only need to identify interface and virtual calls, the others can be ignored.
337 if (IS_LDR_PC (code [-1]) && code [-2] == ADD_LR_PC_4)
338 return decode_vcall_slot_from_ldr (code [-1], regs, displacement);
340 if (IS_LDR_PC (code [0]) && code [-1] == MOV_LR_PC)
341 return decode_vcall_slot_from_ldr (code [0], regs, displacement);
346 #define MAX_ARCH_DELEGATE_PARAMS 3
349 get_delegate_invoke_impl (gboolean has_target, gboolean param_count, guint32 *code_size)
351 guint8 *code, *start;
354 start = code = mono_global_codeman_reserve (12);
356 /* Replace the this argument with the target */
357 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, method_ptr));
358 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, target));
359 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
361 g_assert ((code - start) <= 12);
363 mono_arch_flush_icache (start, 12);
367 size = 8 + param_count * 4;
368 start = code = mono_global_codeman_reserve (size);
370 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, method_ptr));
371 /* slide down the arguments */
372 for (i = 0; i < param_count; ++i) {
373 ARM_MOV_REG_REG (code, (ARMREG_R0 + i), (ARMREG_R0 + i + 1));
375 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
377 g_assert ((code - start) <= size);
379 mono_arch_flush_icache (start, size);
383 *code_size = code - start;
389 * mono_arch_get_delegate_invoke_impls:
391 * Return a list of MonoAotTrampInfo structures for the delegate invoke impl
395 mono_arch_get_delegate_invoke_impls (void)
402 code = get_delegate_invoke_impl (TRUE, 0, &code_len);
403 res = g_slist_prepend (res, mono_aot_tramp_info_create (g_strdup ("delegate_invoke_impl_has_target"), code, code_len));
405 for (i = 0; i <= MAX_ARCH_DELEGATE_PARAMS; ++i) {
406 code = get_delegate_invoke_impl (FALSE, i, &code_len);
407 res = g_slist_prepend (res, mono_aot_tramp_info_create (g_strdup_printf ("delegate_invoke_impl_target_%d", i), code, code_len));
414 mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target)
416 guint8 *code, *start;
418 /* FIXME: Support more cases */
419 if (MONO_TYPE_ISSTRUCT (sig->ret))
423 static guint8* cached = NULL;
424 mono_mini_arch_lock ();
426 mono_mini_arch_unlock ();
431 start = mono_aot_get_named_code ("delegate_invoke_impl_has_target");
433 start = get_delegate_invoke_impl (TRUE, 0, NULL);
435 mono_mini_arch_unlock ();
438 static guint8* cache [MAX_ARCH_DELEGATE_PARAMS + 1] = {NULL};
441 if (sig->param_count > MAX_ARCH_DELEGATE_PARAMS)
443 for (i = 0; i < sig->param_count; ++i)
444 if (!mono_is_regsize_var (sig->params [i]))
447 mono_mini_arch_lock ();
448 code = cache [sig->param_count];
450 mono_mini_arch_unlock ();
455 char *name = g_strdup_printf ("delegate_invoke_impl_target_%d", sig->param_count);
456 start = mono_aot_get_named_code (name);
459 start = get_delegate_invoke_impl (FALSE, sig->param_count, NULL);
461 cache [sig->param_count] = start;
462 mono_mini_arch_unlock ();
470 mono_arch_get_this_arg_from_call (MonoGenericSharingContext *gsctx, MonoMethodSignature *sig, mgreg_t *regs, guint8 *code)
472 /* FIXME: handle returning a struct */
473 if (MONO_TYPE_ISSTRUCT (sig->ret))
474 return (gpointer)regs [ARMREG_R1];
475 return (gpointer)regs [ARMREG_R0];
479 * Initialize the cpu to execute managed code.
482 mono_arch_cpu_init (void)
487 * Initialize architecture specific code.
490 mono_arch_init (void)
492 InitializeCriticalSection (&mini_arch_mutex);
496 * Cleanup architecture specific code.
499 mono_arch_cleanup (void)
504 * This function returns the optimizations supported on this cpu.
507 mono_arch_cpu_optimizazions (guint32 *exclude_mask)
511 thumb_supported = TRUE;
516 FILE *file = fopen ("/proc/cpuinfo", "r");
518 while ((line = fgets (buf, 512, file))) {
519 if (strncmp (line, "Processor", 9) == 0) {
520 char *ver = strstr (line, "(v");
521 if (ver && (ver [2] == '5' || ver [2] == '6' || ver [2] == '7'))
523 if (ver && (ver [2] == '7'))
527 if (strncmp (line, "Features", 8) == 0) {
528 char *th = strstr (line, "thumb");
530 thumb_supported = TRUE;
538 /*printf ("features: v5: %d, thumb: %d\n", v5_supported, thumb_supported);*/
542 /* no arm-specific optimizations yet */
548 is_regsize_var (MonoType *t) {
551 t = mini_type_get_underlying_type (NULL, t);
558 case MONO_TYPE_FNPTR:
560 case MONO_TYPE_OBJECT:
561 case MONO_TYPE_STRING:
562 case MONO_TYPE_CLASS:
563 case MONO_TYPE_SZARRAY:
564 case MONO_TYPE_ARRAY:
566 case MONO_TYPE_GENERICINST:
567 if (!mono_type_generic_inst_is_valuetype (t))
570 case MONO_TYPE_VALUETYPE:
577 mono_arch_get_allocatable_int_vars (MonoCompile *cfg)
582 for (i = 0; i < cfg->num_varinfo; i++) {
583 MonoInst *ins = cfg->varinfo [i];
584 MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
587 if (vmv->range.first_use.abs_pos >= vmv->range.last_use.abs_pos)
590 if (ins->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT) || (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG))
593 /* we can only allocate 32 bit values */
594 if (is_regsize_var (ins->inst_vtype)) {
595 g_assert (MONO_VARINFO (cfg, i)->reg == -1);
596 g_assert (i == vmv->idx);
597 vars = mono_varlist_insert_sorted (cfg, vars, vmv, FALSE);
604 #define USE_EXTRA_TEMPS 0
607 mono_arch_get_global_int_regs (MonoCompile *cfg)
610 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V1));
611 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V2));
612 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V3));
613 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V4));
614 if (!(cfg->compile_aot || cfg->uses_rgctx_reg))
615 /* V5 is reserved for passing the vtable/rgctx/IMT method */
616 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V5));
617 /*regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V6));*/
618 /*regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V7));*/
624 * mono_arch_regalloc_cost:
626 * Return the cost, in number of memory references, of the action of
627 * allocating the variable VMV into a register during global register
631 mono_arch_regalloc_cost (MonoCompile *cfg, MonoMethodVar *vmv)
637 #ifndef __GNUC_PREREQ
638 #define __GNUC_PREREQ(maj, min) (0)
642 mono_arch_flush_icache (guint8 *code, gint size)
645 sys_icache_invalidate (code, size);
646 #elif __GNUC_PREREQ(4, 1)
647 __clear_cache (code, code + size);
648 #elif defined(PLATFORM_ANDROID)
649 const int syscall = 0xf0002;
657 : "r" (code), "r" (code + size), "r" (syscall)
661 __asm __volatile ("mov r0, %0\n"
664 "swi 0x9f0002 @ sys_cacheflush"
666 : "r" (code), "r" (code + size), "r" (0)
667 : "r0", "r1", "r3" );
682 guint16 vtsize; /* in param area */
684 guint8 regtype : 4; /* 0 general, 1 basereg, 2 floating point register, see RegType* */
685 guint8 size : 4; /* 1, 2, 4, 8, or regs used by RegTypeStructByVal */
700 add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple)
703 if (*gr > ARMREG_R3) {
704 ainfo->offset = *stack_size;
705 ainfo->reg = ARMREG_SP; /* in the caller */
706 ainfo->regtype = RegTypeBase;
717 /* first word in r3 and the second on the stack */
718 ainfo->offset = *stack_size;
719 ainfo->reg = ARMREG_SP; /* in the caller */
720 ainfo->regtype = RegTypeBaseGen;
722 } else if (*gr >= ARMREG_R3) {
727 ainfo->offset = *stack_size;
728 ainfo->reg = ARMREG_SP; /* in the caller */
729 ainfo->regtype = RegTypeBase;
744 get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke)
747 int n = sig->hasthis + sig->param_count;
748 MonoType *simpletype;
749 guint32 stack_size = 0;
750 CallInfo *cinfo = g_malloc0 (sizeof (CallInfo) + sizeof (ArgInfo) * n);
754 /* FIXME: handle returning a struct */
755 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
756 add_general (&gr, &stack_size, &cinfo->ret, TRUE);
757 cinfo->struct_ret = ARMREG_R0;
762 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
765 DEBUG(printf("params: %d\n", sig->param_count));
766 for (i = 0; i < sig->param_count; ++i) {
767 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
768 /* Prevent implicit arguments and sig_cookie from
769 being passed in registers */
771 /* Emit the signature cookie just before the implicit arguments */
772 add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE);
774 DEBUG(printf("param %d: ", i));
775 if (sig->params [i]->byref) {
776 DEBUG(printf("byref\n"));
777 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
781 simpletype = mini_type_get_underlying_type (NULL, sig->params [i]);
782 switch (simpletype->type) {
783 case MONO_TYPE_BOOLEAN:
786 cinfo->args [n].size = 1;
787 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
793 cinfo->args [n].size = 2;
794 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
799 cinfo->args [n].size = 4;
800 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
806 case MONO_TYPE_FNPTR:
807 case MONO_TYPE_CLASS:
808 case MONO_TYPE_OBJECT:
809 case MONO_TYPE_STRING:
810 case MONO_TYPE_SZARRAY:
811 case MONO_TYPE_ARRAY:
813 cinfo->args [n].size = sizeof (gpointer);
814 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
817 case MONO_TYPE_GENERICINST:
818 if (!mono_type_generic_inst_is_valuetype (sig->params [i])) {
819 cinfo->args [n].size = sizeof (gpointer);
820 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
825 case MONO_TYPE_TYPEDBYREF:
826 case MONO_TYPE_VALUETYPE: {
832 if (simpletype->type == MONO_TYPE_TYPEDBYREF) {
833 size = sizeof (MonoTypedRef);
834 align = sizeof (gpointer);
836 MonoClass *klass = mono_class_from_mono_type (sig->params [i]);
838 size = mono_class_native_size (klass, &align);
840 size = mono_class_value_size (klass, &align);
842 DEBUG(printf ("load %d bytes struct\n",
843 mono_class_native_size (sig->params [i]->data.klass, NULL)));
846 align_size += (sizeof (gpointer) - 1);
847 align_size &= ~(sizeof (gpointer) - 1);
848 nwords = (align_size + sizeof (gpointer) -1 ) / sizeof (gpointer);
849 cinfo->args [n].regtype = RegTypeStructByVal;
850 /* FIXME: align stack_size if needed */
852 if (align >= 8 && (gr & 1))
855 if (gr > ARMREG_R3) {
856 cinfo->args [n].size = 0;
857 cinfo->args [n].vtsize = nwords;
859 int rest = ARMREG_R3 - gr + 1;
860 int n_in_regs = rest >= nwords? nwords: rest;
862 cinfo->args [n].size = n_in_regs;
863 cinfo->args [n].vtsize = nwords - n_in_regs;
864 cinfo->args [n].reg = gr;
868 cinfo->args [n].offset = stack_size;
869 /*g_print ("offset for arg %d at %d\n", n, stack_size);*/
870 stack_size += nwords * sizeof (gpointer);
877 cinfo->args [n].size = 8;
878 add_general (&gr, &stack_size, cinfo->args + n, FALSE);
882 g_error ("Can't trampoline 0x%x", sig->params [i]->type);
887 simpletype = mini_type_get_underlying_type (NULL, sig->ret);
888 switch (simpletype->type) {
889 case MONO_TYPE_BOOLEAN:
900 case MONO_TYPE_FNPTR:
901 case MONO_TYPE_CLASS:
902 case MONO_TYPE_OBJECT:
903 case MONO_TYPE_SZARRAY:
904 case MONO_TYPE_ARRAY:
905 case MONO_TYPE_STRING:
906 cinfo->ret.reg = ARMREG_R0;
910 cinfo->ret.reg = ARMREG_R0;
914 cinfo->ret.reg = ARMREG_R0;
915 /* FIXME: cinfo->ret.reg = ???;
916 cinfo->ret.regtype = RegTypeFP;*/
918 case MONO_TYPE_GENERICINST:
919 if (!mono_type_generic_inst_is_valuetype (sig->ret)) {
920 cinfo->ret.reg = ARMREG_R0;
924 case MONO_TYPE_VALUETYPE:
926 case MONO_TYPE_TYPEDBYREF:
930 g_error ("Can't handle as return value 0x%x", sig->ret->type);
934 /* align stack size to 8 */
935 DEBUG (printf (" stack size: %d (%d)\n", (stack_size + 15) & ~15, stack_size));
936 stack_size = (stack_size + 7) & ~7;
938 cinfo->stack_usage = stack_size;
944 * Set var information according to the calling convention. arm version.
945 * The locals var stuff should most likely be split in another method.
948 mono_arch_allocate_vars (MonoCompile *cfg)
950 MonoMethodSignature *sig;
951 MonoMethodHeader *header;
953 int i, offset, size, align, curinst;
954 int frame_reg = ARMREG_SP;
956 /* FIXME: this will change when we use FP as gcc does */
957 cfg->flags |= MONO_CFG_HAS_SPILLUP;
959 /* allow room for the vararg method args: void* and long/double */
960 if (mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method))
961 cfg->param_area = MAX (cfg->param_area, sizeof (gpointer)*8);
963 header = mono_method_get_header (cfg->method);
966 * We use the frame register also for any method that has
967 * exception clauses. This way, when the handlers are called,
968 * the code will reference local variables using the frame reg instead of
969 * the stack pointer: if we had to restore the stack pointer, we'd
970 * corrupt the method frames that are already on the stack (since
971 * filters get called before stack unwinding happens) when the filter
972 * code would call any method (this also applies to finally etc.).
974 if ((cfg->flags & MONO_CFG_HAS_ALLOCA) || header->num_clauses)
975 frame_reg = ARMREG_FP;
976 cfg->frame_reg = frame_reg;
977 if (frame_reg != ARMREG_SP) {
978 cfg->used_int_regs |= 1 << frame_reg;
981 if (cfg->compile_aot || cfg->uses_rgctx_reg)
982 /* V5 is reserved for passing the vtable/rgctx/IMT method */
983 cfg->used_int_regs |= (1 << ARMREG_V5);
985 sig = mono_method_signature (cfg->method);
989 if (!MONO_TYPE_ISSTRUCT (sig->ret)) {
990 /* FIXME: handle long and FP values */
991 switch (mini_type_get_underlying_type (NULL, sig->ret)->type) {
995 cfg->ret->opcode = OP_REGVAR;
996 cfg->ret->inst_c0 = ARMREG_R0;
1000 /* local vars are at a positive offset from the stack pointer */
1002 * also note that if the function uses alloca, we use FP
1003 * to point at the local variables.
1005 offset = 0; /* linkage area */
1006 /* align the offset to 16 bytes: not sure this is needed here */
1008 //offset &= ~(8 - 1);
1010 /* add parameter area size for called functions */
1011 offset += cfg->param_area;
1014 if (cfg->flags & MONO_CFG_HAS_FPOUT)
1017 /* allow room to save the return value */
1018 if (mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method))
1021 /* the MonoLMF structure is stored just below the stack pointer */
1023 if (sig->call_convention == MONO_CALL_VARARG) {
1024 cfg->sig_cookie = 0;
1027 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
1028 inst = cfg->vret_addr;
1029 offset += sizeof(gpointer) - 1;
1030 offset &= ~(sizeof(gpointer) - 1);
1031 inst->inst_offset = offset;
1032 inst->opcode = OP_REGOFFSET;
1033 inst->inst_basereg = frame_reg;
1034 if (G_UNLIKELY (cfg->verbose_level > 1)) {
1035 printf ("vret_addr =");
1036 mono_print_ins (cfg->vret_addr);
1038 offset += sizeof(gpointer);
1039 if (sig->call_convention == MONO_CALL_VARARG)
1040 cfg->sig_cookie += sizeof (gpointer);
1043 curinst = cfg->locals_start;
1044 for (i = curinst; i < cfg->num_varinfo; ++i) {
1045 inst = cfg->varinfo [i];
1046 if ((inst->flags & MONO_INST_IS_DEAD) || inst->opcode == OP_REGVAR)
1049 /* inst->backend.is_pinvoke indicates native sized value types, this is used by the
1050 * pinvoke wrappers when they call functions returning structure */
1051 if (inst->backend.is_pinvoke && MONO_TYPE_ISSTRUCT (inst->inst_vtype) && inst->inst_vtype->type != MONO_TYPE_TYPEDBYREF) {
1053 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), &ualign);
1057 size = mono_type_size (inst->inst_vtype, &align);
1059 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
1060 * since it loads/stores misaligned words, which don't do the right thing.
1062 if (align < 4 && size >= 4)
1064 offset += align - 1;
1065 offset &= ~(align - 1);
1066 inst->inst_offset = offset;
1067 inst->opcode = OP_REGOFFSET;
1068 inst->inst_basereg = frame_reg;
1070 //g_print ("allocating local %d to %d\n", i, inst->inst_offset);
1075 inst = cfg->args [curinst];
1076 if (inst->opcode != OP_REGVAR) {
1077 inst->opcode = OP_REGOFFSET;
1078 inst->inst_basereg = frame_reg;
1079 offset += sizeof (gpointer) - 1;
1080 offset &= ~(sizeof (gpointer) - 1);
1081 inst->inst_offset = offset;
1082 offset += sizeof (gpointer);
1083 if (sig->call_convention == MONO_CALL_VARARG)
1084 cfg->sig_cookie += sizeof (gpointer);
1089 for (i = 0; i < sig->param_count; ++i) {
1090 inst = cfg->args [curinst];
1091 if (inst->opcode != OP_REGVAR) {
1092 inst->opcode = OP_REGOFFSET;
1093 inst->inst_basereg = frame_reg;
1094 size = mono_type_size (sig->params [i], &align);
1095 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
1096 * since it loads/stores misaligned words, which don't do the right thing.
1098 if (align < 4 && size >= 4)
1100 offset += align - 1;
1101 offset &= ~(align - 1);
1102 inst->inst_offset = offset;
1104 if ((sig->call_convention == MONO_CALL_VARARG) && (i < sig->sentinelpos))
1105 cfg->sig_cookie += size;
1110 /* align the offset to 8 bytes */
1115 cfg->stack_offset = offset;
1119 mono_arch_create_vars (MonoCompile *cfg)
1121 MonoMethodSignature *sig;
1123 sig = mono_method_signature (cfg->method);
1125 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
1126 cfg->vret_addr = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_ARG);
1127 if (G_UNLIKELY (cfg->verbose_level > 1)) {
1128 printf ("vret_addr = ");
1129 mono_print_ins (cfg->vret_addr);
1135 mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
1138 MonoMethodSignature *sig;
1142 sig = call->signature;
1143 n = sig->param_count + sig->hasthis;
1145 cinfo = get_call_info (sig, sig->pinvoke);
1147 for (i = 0; i < n; ++i) {
1148 ArgInfo *ainfo = cinfo->args + i;
1151 if (i >= sig->hasthis)
1152 t = sig->params [i - sig->hasthis];
1154 t = &mono_defaults.int_class->byval_arg;
1155 t = mini_type_get_underlying_type (NULL, t);
1157 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
1162 in = call->args [i];
1164 switch (ainfo->regtype) {
1165 case RegTypeGeneral:
1166 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1167 MONO_INST_NEW (cfg, ins, OP_MOVE);
1168 ins->dreg = mono_alloc_ireg (cfg);
1169 ins->sreg1 = in->dreg + 1;
1170 MONO_ADD_INS (cfg->cbb, ins);
1171 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1173 MONO_INST_NEW (cfg, ins, OP_MOVE);
1174 ins->dreg = mono_alloc_ireg (cfg);
1175 ins->sreg1 = in->dreg + 2;
1176 MONO_ADD_INS (cfg->cbb, ins);
1177 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg + 1, FALSE);
1178 } else if (!t->byref && ((t->type == MONO_TYPE_R8) || (t->type == MONO_TYPE_R4))) {
1179 #ifndef MONO_ARCH_SOFT_FLOAT
1183 if (ainfo->size == 4) {
1184 #ifdef MONO_ARCH_SOFT_FLOAT
1185 /* mono_emit_call_args () have already done the r8->r4 conversion */
1186 /* The converted value is in an int vreg */
1187 MONO_INST_NEW (cfg, ins, OP_MOVE);
1188 ins->dreg = mono_alloc_ireg (cfg);
1189 ins->sreg1 = in->dreg;
1190 MONO_ADD_INS (cfg->cbb, ins);
1191 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1193 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1194 creg = mono_alloc_ireg (cfg);
1195 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1196 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg, FALSE);
1199 #ifdef MONO_ARCH_SOFT_FLOAT
1200 MONO_INST_NEW (cfg, ins, OP_FGETLOW32);
1201 ins->dreg = mono_alloc_ireg (cfg);
1202 ins->sreg1 = in->dreg;
1203 MONO_ADD_INS (cfg->cbb, ins);
1204 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1206 MONO_INST_NEW (cfg, ins, OP_FGETHIGH32);
1207 ins->dreg = mono_alloc_ireg (cfg);
1208 ins->sreg1 = in->dreg;
1209 MONO_ADD_INS (cfg->cbb, ins);
1210 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg + 1, FALSE);
1212 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1213 creg = mono_alloc_ireg (cfg);
1214 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1215 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg, FALSE);
1216 creg = mono_alloc_ireg (cfg);
1217 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8 + 4));
1218 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg + 1, FALSE);
1221 cfg->flags |= MONO_CFG_HAS_FPOUT;
1223 MONO_INST_NEW (cfg, ins, OP_MOVE);
1224 ins->dreg = mono_alloc_ireg (cfg);
1225 ins->sreg1 = in->dreg;
1226 MONO_ADD_INS (cfg->cbb, ins);
1228 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1231 case RegTypeStructByAddr:
1234 /* FIXME: where si the data allocated? */
1235 arg->backend.reg3 = ainfo->reg;
1236 call->used_iregs |= 1 << ainfo->reg;
1237 g_assert_not_reached ();
1240 case RegTypeStructByVal:
1241 MONO_INST_NEW (cfg, ins, OP_OUTARG_VT);
1242 ins->opcode = OP_OUTARG_VT;
1243 ins->sreg1 = in->dreg;
1244 ins->klass = in->klass;
1245 ins->inst_p0 = call;
1246 ins->inst_p1 = mono_mempool_alloc (cfg->mempool, sizeof (ArgInfo));
1247 memcpy (ins->inst_p1, ainfo, sizeof (ArgInfo));
1248 MONO_ADD_INS (cfg->cbb, ins);
1251 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1252 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1253 } else if (!t->byref && ((t->type == MONO_TYPE_R4) || (t->type == MONO_TYPE_R8))) {
1254 if (t->type == MONO_TYPE_R8) {
1255 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1257 #ifdef MONO_ARCH_SOFT_FLOAT
1258 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1260 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1264 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1267 case RegTypeBaseGen:
1268 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1269 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);
1270 MONO_INST_NEW (cfg, ins, OP_MOVE);
1271 ins->dreg = mono_alloc_ireg (cfg);
1272 ins->sreg1 = G_BYTE_ORDER == G_BIG_ENDIAN ? in->dreg + 2 : in->dreg + 1;
1273 MONO_ADD_INS (cfg->cbb, ins);
1274 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ARMREG_R3, FALSE);
1275 } else if (!t->byref && (t->type == MONO_TYPE_R8)) {
1278 #ifdef MONO_ARCH_SOFT_FLOAT
1279 g_assert_not_reached ();
1282 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1283 creg = mono_alloc_ireg (cfg);
1284 mono_call_inst_add_outarg_reg (cfg, call, creg, ARMREG_R3, FALSE);
1285 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1286 creg = mono_alloc_ireg (cfg);
1287 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 4));
1288 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, creg);
1289 cfg->flags |= MONO_CFG_HAS_FPOUT;
1291 g_assert_not_reached ();
1298 arg->backend.reg3 = ainfo->reg;
1299 /* FP args are passed in int regs */
1300 call->used_iregs |= 1 << ainfo->reg;
1301 if (ainfo->size == 8) {
1302 arg->opcode = OP_OUTARG_R8;
1303 call->used_iregs |= 1 << (ainfo->reg + 1);
1305 arg->opcode = OP_OUTARG_R4;
1308 cfg->flags |= MONO_CFG_HAS_FPOUT;
1312 g_assert_not_reached ();
1316 if (sig->ret && MONO_TYPE_ISSTRUCT (sig->ret)) {
1319 MONO_INST_NEW (cfg, vtarg, OP_MOVE);
1320 vtarg->sreg1 = call->vret_var->dreg;
1321 vtarg->dreg = mono_alloc_preg (cfg);
1322 MONO_ADD_INS (cfg->cbb, vtarg);
1324 mono_call_inst_add_outarg_reg (cfg, call, vtarg->dreg, cinfo->ret.reg, FALSE);
1327 call->stack_usage = cinfo->stack_usage;
1333 mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src)
1335 MonoCallInst *call = (MonoCallInst*)ins->inst_p0;
1336 ArgInfo *ainfo = ins->inst_p1;
1337 int ovf_size = ainfo->vtsize;
1338 int doffset = ainfo->offset;
1339 int i, soffset, dreg;
1342 for (i = 0; i < ainfo->size; ++i) {
1343 dreg = mono_alloc_ireg (cfg);
1344 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, src->dreg, soffset);
1345 mono_call_inst_add_outarg_reg (cfg, call, dreg, ainfo->reg + i, FALSE);
1346 soffset += sizeof (gpointer);
1348 //g_print ("vt size: %d at R%d + %d\n", doffset, vt->inst_basereg, vt->inst_offset);
1350 mini_emit_memcpy (cfg, ARMREG_SP, doffset, src->dreg, soffset, ovf_size * sizeof (gpointer), 0);
1354 mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val)
1356 MonoType *ret = mini_type_get_underlying_type (cfg->generic_sharing_context, mono_method_signature (method)->ret);
1359 if (ret->type == MONO_TYPE_I8 || ret->type == MONO_TYPE_U8) {
1362 MONO_INST_NEW (cfg, ins, OP_SETLRET);
1363 ins->sreg1 = val->dreg + 1;
1364 ins->sreg2 = val->dreg + 2;
1365 MONO_ADD_INS (cfg->cbb, ins);
1368 #ifdef MONO_ARCH_SOFT_FLOAT
1369 if (ret->type == MONO_TYPE_R8) {
1372 MONO_INST_NEW (cfg, ins, OP_SETFRET);
1373 ins->dreg = cfg->ret->dreg;
1374 ins->sreg1 = val->dreg;
1375 MONO_ADD_INS (cfg->cbb, ins);
1378 if (ret->type == MONO_TYPE_R4) {
1379 /* Already converted to an int in method_to_ir () */
1380 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
1383 #elif defined(ARM_FPU_VFP)
1384 if (ret->type == MONO_TYPE_R8 || ret->type == MONO_TYPE_R4) {
1387 MONO_INST_NEW (cfg, ins, OP_SETFRET);
1388 ins->dreg = cfg->ret->dreg;
1389 ins->sreg1 = val->dreg;
1390 MONO_ADD_INS (cfg->cbb, ins);
1394 if (ret->type == MONO_TYPE_R4 || ret->type == MONO_TYPE_R8) {
1395 MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, cfg->ret->dreg, val->dreg);
1402 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
1406 mono_arch_is_inst_imm (gint64 imm)
1412 * Allow tracing to work with this interface (with an optional argument)
1416 mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
1420 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1421 ARM_MOV_REG_IMM8 (code, ARMREG_R1, 0); /* NULL ebp for now */
1422 code = mono_arm_emit_load_imm (code, ARMREG_R2, (guint32)func);
1423 code = emit_call_reg (code, ARMREG_R2);
1436 mono_arch_instrument_epilog_full (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments, gboolean preserve_argument_registers)
1439 int save_mode = SAVE_NONE;
1441 MonoMethod *method = cfg->method;
1442 int rtype = mini_type_get_underlying_type (cfg->generic_sharing_context, mono_method_signature (method)->ret)->type;
1443 int save_offset = cfg->param_area;
1447 offset = code - cfg->native_code;
1448 /* we need about 16 instructions */
1449 if (offset > (cfg->code_size - 16 * 4)) {
1450 cfg->code_size *= 2;
1451 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
1452 code = cfg->native_code + offset;
1455 case MONO_TYPE_VOID:
1456 /* special case string .ctor icall */
1457 if (strcmp (".ctor", method->name) && method->klass == mono_defaults.string_class)
1458 save_mode = SAVE_ONE;
1460 save_mode = SAVE_NONE;
1464 save_mode = SAVE_TWO;
1468 save_mode = SAVE_FP;
1470 case MONO_TYPE_VALUETYPE:
1471 save_mode = SAVE_STRUCT;
1474 save_mode = SAVE_ONE;
1478 switch (save_mode) {
1480 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1481 ARM_STR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1482 if (enable_arguments) {
1483 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_R1);
1484 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1488 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1489 if (enable_arguments) {
1490 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1494 /* FIXME: what reg? */
1495 if (enable_arguments) {
1496 /* FIXME: what reg? */
1500 if (enable_arguments) {
1501 /* FIXME: get the actual address */
1502 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1510 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1511 code = mono_arm_emit_load_imm (code, ARMREG_IP, (guint32)func);
1512 code = emit_call_reg (code, ARMREG_IP);
1514 switch (save_mode) {
1516 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1517 ARM_LDR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1520 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1534 * The immediate field for cond branches is big enough for all reasonable methods
1536 #define EMIT_COND_BRANCH_FLAGS(ins,condcode) \
1537 if (0 && ins->inst_true_bb->native_offset) { \
1538 ARM_B_COND (code, (condcode), (code - cfg->native_code + ins->inst_true_bb->native_offset) & 0xffffff); \
1540 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_true_bb); \
1541 ARM_B_COND (code, (condcode), 0); \
1544 #define EMIT_COND_BRANCH(ins,cond) EMIT_COND_BRANCH_FLAGS(ins, branch_cc_table [(cond)])
1546 /* emit an exception if condition is fail
1548 * We assign the extra code used to throw the implicit exceptions
1549 * to cfg->bb_exit as far as the big branch handling is concerned
1551 #define EMIT_COND_SYSTEM_EXCEPTION_FLAGS(condcode,exc_name) \
1553 mono_add_patch_info (cfg, code - cfg->native_code, \
1554 MONO_PATCH_INFO_EXC, exc_name); \
1555 ARM_BL_COND (code, (condcode), 0); \
1558 #define EMIT_COND_SYSTEM_EXCEPTION(cond,exc_name) EMIT_COND_SYSTEM_EXCEPTION_FLAGS(branch_cc_table [(cond)], (exc_name))
1561 mono_arch_peephole_pass_1 (MonoCompile *cfg, MonoBasicBlock *bb)
1566 mono_arch_peephole_pass_2 (MonoCompile *cfg, MonoBasicBlock *bb)
1568 MonoInst *ins, *n, *last_ins = NULL;
1570 MONO_BB_FOR_EACH_INS_SAFE (bb, n, ins) {
1571 switch (ins->opcode) {
1574 /* Already done by an arch-independent pass */
1576 case OP_LOAD_MEMBASE:
1577 case OP_LOADI4_MEMBASE:
1579 * OP_STORE_MEMBASE_REG reg, offset(basereg)
1580 * OP_LOAD_MEMBASE offset(basereg), reg
1582 if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG
1583 || last_ins->opcode == OP_STORE_MEMBASE_REG) &&
1584 ins->inst_basereg == last_ins->inst_destbasereg &&
1585 ins->inst_offset == last_ins->inst_offset) {
1586 if (ins->dreg == last_ins->sreg1) {
1587 MONO_DELETE_INS (bb, ins);
1590 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1591 ins->opcode = OP_MOVE;
1592 ins->sreg1 = last_ins->sreg1;
1596 * Note: reg1 must be different from the basereg in the second load
1597 * OP_LOAD_MEMBASE offset(basereg), reg1
1598 * OP_LOAD_MEMBASE offset(basereg), reg2
1600 * OP_LOAD_MEMBASE offset(basereg), reg1
1601 * OP_MOVE reg1, reg2
1603 } if (last_ins && (last_ins->opcode == OP_LOADI4_MEMBASE
1604 || last_ins->opcode == OP_LOAD_MEMBASE) &&
1605 ins->inst_basereg != last_ins->dreg &&
1606 ins->inst_basereg == last_ins->inst_basereg &&
1607 ins->inst_offset == last_ins->inst_offset) {
1609 if (ins->dreg == last_ins->dreg) {
1610 MONO_DELETE_INS (bb, ins);
1613 ins->opcode = OP_MOVE;
1614 ins->sreg1 = last_ins->dreg;
1617 //g_assert_not_reached ();
1621 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1622 * OP_LOAD_MEMBASE offset(basereg), reg
1624 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1625 * OP_ICONST reg, imm
1627 } else if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_IMM
1628 || last_ins->opcode == OP_STORE_MEMBASE_IMM) &&
1629 ins->inst_basereg == last_ins->inst_destbasereg &&
1630 ins->inst_offset == last_ins->inst_offset) {
1631 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1632 ins->opcode = OP_ICONST;
1633 ins->inst_c0 = last_ins->inst_imm;
1634 g_assert_not_reached (); // check this rule
1638 case OP_LOADU1_MEMBASE:
1639 case OP_LOADI1_MEMBASE:
1640 if (last_ins && (last_ins->opcode == OP_STOREI1_MEMBASE_REG) &&
1641 ins->inst_basereg == last_ins->inst_destbasereg &&
1642 ins->inst_offset == last_ins->inst_offset) {
1643 ins->opcode = (ins->opcode == OP_LOADI1_MEMBASE) ? OP_ICONV_TO_I1 : OP_ICONV_TO_U1;
1644 ins->sreg1 = last_ins->sreg1;
1647 case OP_LOADU2_MEMBASE:
1648 case OP_LOADI2_MEMBASE:
1649 if (last_ins && (last_ins->opcode == OP_STOREI2_MEMBASE_REG) &&
1650 ins->inst_basereg == last_ins->inst_destbasereg &&
1651 ins->inst_offset == last_ins->inst_offset) {
1652 ins->opcode = (ins->opcode == OP_LOADI2_MEMBASE) ? OP_ICONV_TO_I2 : OP_ICONV_TO_U2;
1653 ins->sreg1 = last_ins->sreg1;
1657 ins->opcode = OP_MOVE;
1661 if (ins->dreg == ins->sreg1) {
1662 MONO_DELETE_INS (bb, ins);
1666 * OP_MOVE sreg, dreg
1667 * OP_MOVE dreg, sreg
1669 if (last_ins && last_ins->opcode == OP_MOVE &&
1670 ins->sreg1 == last_ins->dreg &&
1671 ins->dreg == last_ins->sreg1) {
1672 MONO_DELETE_INS (bb, ins);
1680 bb->last_ins = last_ins;
1684 * the branch_cc_table should maintain the order of these
1698 branch_cc_table [] = {
1712 #define NEW_INS(cfg,dest,op) do { \
1713 MONO_INST_NEW ((cfg), (dest), (op)); \
1714 mono_bblock_insert_before_ins (bb, ins, (dest)); \
1718 map_to_reg_reg_op (int op)
1727 case OP_COMPARE_IMM:
1729 case OP_ICOMPARE_IMM:
1743 case OP_LOAD_MEMBASE:
1744 return OP_LOAD_MEMINDEX;
1745 case OP_LOADI4_MEMBASE:
1746 return OP_LOADI4_MEMINDEX;
1747 case OP_LOADU4_MEMBASE:
1748 return OP_LOADU4_MEMINDEX;
1749 case OP_LOADU1_MEMBASE:
1750 return OP_LOADU1_MEMINDEX;
1751 case OP_LOADI2_MEMBASE:
1752 return OP_LOADI2_MEMINDEX;
1753 case OP_LOADU2_MEMBASE:
1754 return OP_LOADU2_MEMINDEX;
1755 case OP_LOADI1_MEMBASE:
1756 return OP_LOADI1_MEMINDEX;
1757 case OP_STOREI1_MEMBASE_REG:
1758 return OP_STOREI1_MEMINDEX;
1759 case OP_STOREI2_MEMBASE_REG:
1760 return OP_STOREI2_MEMINDEX;
1761 case OP_STOREI4_MEMBASE_REG:
1762 return OP_STOREI4_MEMINDEX;
1763 case OP_STORE_MEMBASE_REG:
1764 return OP_STORE_MEMINDEX;
1765 case OP_STORER4_MEMBASE_REG:
1766 return OP_STORER4_MEMINDEX;
1767 case OP_STORER8_MEMBASE_REG:
1768 return OP_STORER8_MEMINDEX;
1769 case OP_STORE_MEMBASE_IMM:
1770 return OP_STORE_MEMBASE_REG;
1771 case OP_STOREI1_MEMBASE_IMM:
1772 return OP_STOREI1_MEMBASE_REG;
1773 case OP_STOREI2_MEMBASE_IMM:
1774 return OP_STOREI2_MEMBASE_REG;
1775 case OP_STOREI4_MEMBASE_IMM:
1776 return OP_STOREI4_MEMBASE_REG;
1778 g_assert_not_reached ();
1782 * Remove from the instruction list the instructions that can't be
1783 * represented with very simple instructions with no register
1787 mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb)
1789 MonoInst *ins, *temp, *last_ins = NULL;
1790 int rot_amount, imm8, low_imm;
1792 MONO_BB_FOR_EACH_INS (bb, ins) {
1794 switch (ins->opcode) {
1798 case OP_COMPARE_IMM:
1799 case OP_ICOMPARE_IMM:
1813 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount)) < 0) {
1814 NEW_INS (cfg, temp, OP_ICONST);
1815 temp->inst_c0 = ins->inst_imm;
1816 temp->dreg = mono_alloc_ireg (cfg);
1817 ins->sreg2 = temp->dreg;
1818 ins->opcode = mono_op_imm_to_op (ins->opcode);
1820 if (ins->opcode == OP_SBB || ins->opcode == OP_ISBB || ins->opcode == OP_SUBCC)
1826 if (ins->inst_imm == 1) {
1827 ins->opcode = OP_MOVE;
1830 if (ins->inst_imm == 0) {
1831 ins->opcode = OP_ICONST;
1835 imm8 = mono_is_power_of_two (ins->inst_imm);
1837 ins->opcode = OP_SHL_IMM;
1838 ins->inst_imm = imm8;
1841 NEW_INS (cfg, temp, OP_ICONST);
1842 temp->inst_c0 = ins->inst_imm;
1843 temp->dreg = mono_alloc_ireg (cfg);
1844 ins->sreg2 = temp->dreg;
1845 ins->opcode = OP_IMUL;
1851 if (ins->next && (ins->next->opcode == OP_COND_EXC_C || ins->next->opcode == OP_COND_EXC_IC))
1852 /* ARM sets the C flag to 1 if there was _no_ overflow */
1853 ins->next->opcode = OP_COND_EXC_NC;
1855 case OP_LOCALLOC_IMM:
1856 NEW_INS (cfg, temp, OP_ICONST);
1857 temp->inst_c0 = ins->inst_imm;
1858 temp->dreg = mono_alloc_ireg (cfg);
1859 ins->sreg1 = temp->dreg;
1860 ins->opcode = OP_LOCALLOC;
1862 case OP_LOAD_MEMBASE:
1863 case OP_LOADI4_MEMBASE:
1864 case OP_LOADU4_MEMBASE:
1865 case OP_LOADU1_MEMBASE:
1866 /* we can do two things: load the immed in a register
1867 * and use an indexed load, or see if the immed can be
1868 * represented as an ad_imm + a load with a smaller offset
1869 * that fits. We just do the first for now, optimize later.
1871 if (arm_is_imm12 (ins->inst_offset))
1873 NEW_INS (cfg, temp, OP_ICONST);
1874 temp->inst_c0 = ins->inst_offset;
1875 temp->dreg = mono_alloc_ireg (cfg);
1876 ins->sreg2 = temp->dreg;
1877 ins->opcode = map_to_reg_reg_op (ins->opcode);
1879 case OP_LOADI2_MEMBASE:
1880 case OP_LOADU2_MEMBASE:
1881 case OP_LOADI1_MEMBASE:
1882 if (arm_is_imm8 (ins->inst_offset))
1884 NEW_INS (cfg, temp, OP_ICONST);
1885 temp->inst_c0 = ins->inst_offset;
1886 temp->dreg = mono_alloc_ireg (cfg);
1887 ins->sreg2 = temp->dreg;
1888 ins->opcode = map_to_reg_reg_op (ins->opcode);
1890 case OP_LOADR4_MEMBASE:
1891 case OP_LOADR8_MEMBASE:
1892 if (arm_is_fpimm8 (ins->inst_offset))
1894 low_imm = ins->inst_offset & 0x1ff;
1895 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~0x1ff, &rot_amount)) >= 0) {
1896 NEW_INS (cfg, temp, OP_ADD_IMM);
1897 temp->inst_imm = ins->inst_offset & ~0x1ff;
1898 temp->sreg1 = ins->inst_basereg;
1899 temp->dreg = mono_alloc_ireg (cfg);
1900 ins->inst_basereg = temp->dreg;
1901 ins->inst_offset = low_imm;
1904 /* VFP/FPA doesn't have indexed load instructions */
1905 g_assert_not_reached ();
1907 case OP_STORE_MEMBASE_REG:
1908 case OP_STOREI4_MEMBASE_REG:
1909 case OP_STOREI1_MEMBASE_REG:
1910 if (arm_is_imm12 (ins->inst_offset))
1912 NEW_INS (cfg, temp, OP_ICONST);
1913 temp->inst_c0 = ins->inst_offset;
1914 temp->dreg = mono_alloc_ireg (cfg);
1915 ins->sreg2 = temp->dreg;
1916 ins->opcode = map_to_reg_reg_op (ins->opcode);
1918 case OP_STOREI2_MEMBASE_REG:
1919 if (arm_is_imm8 (ins->inst_offset))
1921 NEW_INS (cfg, temp, OP_ICONST);
1922 temp->inst_c0 = ins->inst_offset;
1923 temp->dreg = mono_alloc_ireg (cfg);
1924 ins->sreg2 = temp->dreg;
1925 ins->opcode = map_to_reg_reg_op (ins->opcode);
1927 case OP_STORER4_MEMBASE_REG:
1928 case OP_STORER8_MEMBASE_REG:
1929 if (arm_is_fpimm8 (ins->inst_offset))
1931 low_imm = ins->inst_offset & 0x1ff;
1932 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~ 0x1ff, &rot_amount)) >= 0 && arm_is_fpimm8 (low_imm)) {
1933 NEW_INS (cfg, temp, OP_ADD_IMM);
1934 temp->inst_imm = ins->inst_offset & ~0x1ff;
1935 temp->sreg1 = ins->inst_destbasereg;
1936 temp->dreg = mono_alloc_ireg (cfg);
1937 ins->inst_destbasereg = temp->dreg;
1938 ins->inst_offset = low_imm;
1941 /*g_print ("fail with: %d (%d, %d)\n", ins->inst_offset, ins->inst_offset & ~0x1ff, low_imm);*/
1942 /* VFP/FPA doesn't have indexed store instructions */
1943 g_assert_not_reached ();
1945 case OP_STORE_MEMBASE_IMM:
1946 case OP_STOREI1_MEMBASE_IMM:
1947 case OP_STOREI2_MEMBASE_IMM:
1948 case OP_STOREI4_MEMBASE_IMM:
1949 NEW_INS (cfg, temp, OP_ICONST);
1950 temp->inst_c0 = ins->inst_imm;
1951 temp->dreg = mono_alloc_ireg (cfg);
1952 ins->sreg1 = temp->dreg;
1953 ins->opcode = map_to_reg_reg_op (ins->opcode);
1955 goto loop_start; /* make it handle the possibly big ins->inst_offset */
1957 gboolean swap = FALSE;
1960 /* Some fp compares require swapped operands */
1961 g_assert (ins->next);
1962 switch (ins->next->opcode) {
1964 ins->next->opcode = OP_FBLT;
1968 ins->next->opcode = OP_FBLT_UN;
1972 ins->next->opcode = OP_FBGE;
1976 ins->next->opcode = OP_FBGE_UN;
1984 ins->sreg1 = ins->sreg2;
1993 bb->last_ins = last_ins;
1994 bb->max_vreg = cfg->next_vreg;
1998 mono_arch_decompose_long_opts (MonoCompile *cfg, MonoInst *long_ins)
2002 if (long_ins->opcode == OP_LNEG) {
2004 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ARM_RSBS_IMM, ins->dreg + 1, ins->sreg1 + 1, 0);
2005 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ARM_RSC_IMM, ins->dreg + 2, ins->sreg1 + 2, 0);
2011 emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size, gboolean is_signed)
2013 /* sreg is a float, dreg is an integer reg */
2015 ARM_FIXZ (code, dreg, sreg);
2016 #elif defined(ARM_FPU_VFP)
2018 ARM_TOSIZD (code, ARM_VFP_F0, sreg);
2020 ARM_TOUIZD (code, ARM_VFP_F0, sreg);
2021 ARM_FMRS (code, dreg, ARM_VFP_F0);
2025 ARM_AND_REG_IMM8 (code, dreg, dreg, 0xff);
2026 else if (size == 2) {
2027 ARM_SHL_IMM (code, dreg, dreg, 16);
2028 ARM_SHR_IMM (code, dreg, dreg, 16);
2032 ARM_SHL_IMM (code, dreg, dreg, 24);
2033 ARM_SAR_IMM (code, dreg, dreg, 24);
2034 } else if (size == 2) {
2035 ARM_SHL_IMM (code, dreg, dreg, 16);
2036 ARM_SAR_IMM (code, dreg, dreg, 16);
2044 const guchar *target;
2049 #define is_call_imm(diff) ((gint)(diff) >= -33554432 && (gint)(diff) <= 33554431)
2052 search_thunk_slot (void *data, int csize, int bsize, void *user_data) {
2053 PatchData *pdata = (PatchData*)user_data;
2054 guchar *code = data;
2055 guint32 *thunks = data;
2056 guint32 *endthunks = (guint32*)(code + bsize);
2058 int difflow, diffhigh;
2060 /* always ensure a call from pdata->code can reach to the thunks without further thunks */
2061 difflow = (char*)pdata->code - (char*)thunks;
2062 diffhigh = (char*)pdata->code - (char*)endthunks;
2063 if (!((is_call_imm (thunks) && is_call_imm (endthunks)) || (is_call_imm (difflow) && is_call_imm (diffhigh))))
2067 * The thunk is composed of 3 words:
2068 * load constant from thunks [2] into ARM_IP
2071 * Note that the LR register is already setup
2073 //g_print ("thunk nentries: %d\n", ((char*)endthunks - (char*)thunks)/16);
2074 if ((pdata->found == 2) || (pdata->code >= code && pdata->code <= code + csize)) {
2075 while (thunks < endthunks) {
2076 //g_print ("looking for target: %p at %p (%08x-%08x)\n", pdata->target, thunks, thunks [0], thunks [1]);
2077 if (thunks [2] == (guint32)pdata->target) {
2078 arm_patch (pdata->code, (guchar*)thunks);
2079 mono_arch_flush_icache (pdata->code, 4);
2082 } else if ((thunks [0] == 0) && (thunks [1] == 0) && (thunks [2] == 0)) {
2083 /* found a free slot instead: emit thunk */
2084 /* ARMREG_IP is fine to use since this can't be an IMT call
2087 code = (guchar*)thunks;
2088 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
2089 if (thumb_supported)
2090 ARM_BX (code, ARMREG_IP);
2092 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
2093 thunks [2] = (guint32)pdata->target;
2094 mono_arch_flush_icache ((guchar*)thunks, 12);
2096 arm_patch (pdata->code, (guchar*)thunks);
2097 mono_arch_flush_icache (pdata->code, 4);
2101 /* skip 12 bytes, the size of the thunk */
2105 //g_print ("failed thunk lookup for %p from %p at %p (%d entries)\n", pdata->target, pdata->code, data, count);
2111 handle_thunk (int absolute, guchar *code, const guchar *target) {
2112 MonoDomain *domain = mono_domain_get ();
2116 pdata.target = target;
2117 pdata.absolute = absolute;
2120 mono_domain_lock (domain);
2121 mono_domain_code_foreach (domain, search_thunk_slot, &pdata);
2124 /* this uses the first available slot */
2126 mono_domain_code_foreach (domain, search_thunk_slot, &pdata);
2128 mono_domain_unlock (domain);
2130 if (pdata.found != 1)
2131 g_print ("thunk failed for %p from %p\n", target, code);
2132 g_assert (pdata.found == 1);
2136 arm_patch (guchar *code, const guchar *target)
2138 guint32 *code32 = (void*)code;
2139 guint32 ins = *code32;
2140 guint32 prim = (ins >> 25) & 7;
2141 guint32 tval = GPOINTER_TO_UINT (target);
2143 //g_print ("patching 0x%08x (0x%08x) to point to 0x%08x\n", code, ins, target);
2144 if (prim == 5) { /* 101b */
2145 /* the diff starts 8 bytes from the branch opcode */
2146 gint diff = target - code - 8;
2148 gint tmask = 0xffffffff;
2149 if (tval & 1) { /* entering thumb mode */
2150 diff = target - 1 - code - 8;
2151 g_assert (thumb_supported);
2152 tbits = 0xf << 28; /* bl->blx bit pattern */
2153 g_assert ((ins & (1 << 24))); /* it must be a bl, not b instruction */
2154 /* this low bit of the displacement is moved to bit 24 in the instruction encoding */
2158 tmask = ~(1 << 24); /* clear the link bit */
2159 /*g_print ("blx to thumb: target: %p, code: %p, diff: %d, mask: %x\n", target, code, diff, tmask);*/
2164 if (diff <= 33554431) {
2166 ins = (ins & 0xff000000) | diff;
2168 *code32 = ins | tbits;
2172 /* diff between 0 and -33554432 */
2173 if (diff >= -33554432) {
2175 ins = (ins & 0xff000000) | (diff & ~0xff000000);
2177 *code32 = ins | tbits;
2182 handle_thunk (TRUE, code, target);
2187 * The alternative call sequences looks like this:
2189 * ldr ip, [pc] // loads the address constant
2190 * b 1f // jumps around the constant
2191 * address constant embedded in the code
2196 * There are two cases for patching:
2197 * a) at the end of method emission: in this case code points to the start
2198 * of the call sequence
2199 * b) during runtime patching of the call site: in this case code points
2200 * to the mov pc, ip instruction
2202 * We have to handle also the thunk jump code sequence:
2206 * address constant // execution never reaches here
2208 if ((ins & 0x0ffffff0) == 0x12fff10) {
2209 /* Branch and exchange: the address is constructed in a reg
2210 * We can patch BX when the code sequence is the following:
2211 * ldr ip, [pc, #0] ; 0x8
2218 guint8 *emit = (guint8*)ccode;
2219 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2221 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
2222 ARM_BX (emit, ARMREG_IP);
2224 /*patching from magic trampoline*/
2225 if (ins == ccode [3]) {
2226 g_assert (code32 [-4] == ccode [0]);
2227 g_assert (code32 [-3] == ccode [1]);
2228 g_assert (code32 [-1] == ccode [2]);
2229 code32 [-2] = (guint32)target;
2232 /*patching from JIT*/
2233 if (ins == ccode [0]) {
2234 g_assert (code32 [1] == ccode [1]);
2235 g_assert (code32 [3] == ccode [2]);
2236 g_assert (code32 [4] == ccode [3]);
2237 code32 [2] = (guint32)target;
2240 g_assert_not_reached ();
2241 } else if ((ins & 0x0ffffff0) == 0x12fff30) {
2249 guint8 *emit = (guint8*)ccode;
2250 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2252 ARM_BLX_REG (emit, ARMREG_IP);
2254 g_assert (code32 [-3] == ccode [0]);
2255 g_assert (code32 [-2] == ccode [1]);
2256 g_assert (code32 [0] == ccode [2]);
2258 code32 [-1] = (guint32)target;
2261 guint32 *tmp = ccode;
2262 guint8 *emit = (guint8*)tmp;
2263 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2264 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
2265 ARM_MOV_REG_REG (emit, ARMREG_PC, ARMREG_IP);
2266 ARM_BX (emit, ARMREG_IP);
2267 if (ins == ccode [2]) {
2268 g_assert_not_reached (); // should be -2 ...
2269 code32 [-1] = (guint32)target;
2272 if (ins == ccode [0]) {
2273 /* handles both thunk jump code and the far call sequence */
2274 code32 [2] = (guint32)target;
2277 g_assert_not_reached ();
2279 // g_print ("patched with 0x%08x\n", ins);
2283 * Return the >= 0 uimm8 value if val can be represented with a byte + rotation
2284 * (with the rotation amount in *rot_amount. rot_amount is already adjusted
2285 * to be used with the emit macros.
2286 * Return -1 otherwise.
2289 mono_arm_is_rotated_imm8 (guint32 val, gint *rot_amount)
2292 for (i = 0; i < 31; i+= 2) {
2293 res = (val << (32 - i)) | (val >> i);
2296 *rot_amount = i? 32 - i: 0;
2303 * Emits in code a sequence of instructions that load the value 'val'
2304 * into the dreg register. Uses at most 4 instructions.
2307 mono_arm_emit_load_imm (guint8 *code, int dreg, guint32 val)
2309 int imm8, rot_amount;
2311 ARM_LDR_IMM (code, dreg, ARMREG_PC, 0);
2312 /* skip the constant pool */
2318 if ((imm8 = mono_arm_is_rotated_imm8 (val, &rot_amount)) >= 0) {
2319 ARM_MOV_REG_IMM (code, dreg, imm8, rot_amount);
2320 } else if ((imm8 = mono_arm_is_rotated_imm8 (~val, &rot_amount)) >= 0) {
2321 ARM_MVN_REG_IMM (code, dreg, imm8, rot_amount);
2324 ARM_MOVW_REG_IMM (code, dreg, val & 0xffff);
2326 ARM_MOVT_REG_IMM (code, dreg, (val >> 16) & 0xffff);
2330 ARM_MOV_REG_IMM8 (code, dreg, (val & 0xFF));
2332 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF00) >> 8, 24);
2334 if (val & 0xFF0000) {
2335 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
2337 if (val & 0xFF000000) {
2338 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2340 } else if (val & 0xFF00) {
2341 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF00) >> 8, 24);
2342 if (val & 0xFF0000) {
2343 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
2345 if (val & 0xFF000000) {
2346 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2348 } else if (val & 0xFF0000) {
2349 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF0000) >> 16, 16);
2350 if (val & 0xFF000000) {
2351 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2354 //g_assert_not_reached ();
2360 * emit_load_volatile_arguments:
2362 * Load volatile arguments from the stack to the original input registers.
2363 * Required before a tail call.
2366 emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code)
2368 MonoMethod *method = cfg->method;
2369 MonoMethodSignature *sig;
2374 /* FIXME: Generate intermediate code instead */
2376 sig = mono_method_signature (method);
2378 /* This is the opposite of the code in emit_prolog */
2382 cinfo = get_call_info (sig, sig->pinvoke);
2384 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
2385 ArgInfo *ainfo = &cinfo->ret;
2386 inst = cfg->vret_addr;
2387 g_assert (arm_is_imm12 (inst->inst_offset));
2388 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2390 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
2391 ArgInfo *ainfo = cinfo->args + i;
2392 inst = cfg->args [pos];
2394 if (cfg->verbose_level > 2)
2395 g_print ("Loading argument %d (type: %d)\n", i, ainfo->regtype);
2396 if (inst->opcode == OP_REGVAR) {
2397 if (ainfo->regtype == RegTypeGeneral)
2398 ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
2399 else if (ainfo->regtype == RegTypeFP) {
2400 g_assert_not_reached ();
2401 } else if (ainfo->regtype == RegTypeBase) {
2405 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
2406 ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset));
2408 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
2409 ARM_LDR_REG_REG (code, inst->dreg, ARMREG_SP, ARMREG_IP);
2413 g_assert_not_reached ();
2415 if (ainfo->regtype == RegTypeGeneral) {
2416 switch (ainfo->size) {
2423 g_assert (arm_is_imm12 (inst->inst_offset));
2424 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2425 g_assert (arm_is_imm12 (inst->inst_offset + 4));
2426 ARM_LDR_IMM (code, ainfo->reg + 1, inst->inst_basereg, inst->inst_offset + 4);
2429 if (arm_is_imm12 (inst->inst_offset)) {
2430 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2432 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
2433 ARM_LDR_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
2437 } else if (ainfo->regtype == RegTypeBaseGen) {
2440 } else if (ainfo->regtype == RegTypeBase) {
2442 } else if (ainfo->regtype == RegTypeFP) {
2443 g_assert_not_reached ();
2444 } else if (ainfo->regtype == RegTypeStructByVal) {
2445 int doffset = inst->inst_offset;
2449 if (mono_class_from_mono_type (inst->inst_vtype))
2450 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), NULL);
2451 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
2452 if (arm_is_imm12 (doffset)) {
2453 ARM_LDR_IMM (code, ainfo->reg + cur_reg, inst->inst_basereg, doffset);
2455 code = mono_arm_emit_load_imm (code, ARMREG_IP, doffset);
2456 ARM_LDR_REG_REG (code, ainfo->reg + cur_reg, inst->inst_basereg, ARMREG_IP);
2458 soffset += sizeof (gpointer);
2459 doffset += sizeof (gpointer);
2464 } else if (ainfo->regtype == RegTypeStructByAddr) {
2481 mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
2486 guint8 *code = cfg->native_code + cfg->code_len;
2487 MonoInst *last_ins = NULL;
2488 guint last_offset = 0;
2490 int imm8, rot_amount;
2492 /* we don't align basic blocks of loops on arm */
2494 if (cfg->verbose_level > 2)
2495 g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset);
2497 cpos = bb->max_offset;
2499 if (cfg->prof_options & MONO_PROFILE_COVERAGE) {
2500 //MonoCoverageInfo *cov = mono_get_coverage_info (cfg->method);
2501 //g_assert (!mono_compile_aot);
2504 // cov->data [bb->dfn].iloffset = bb->cil_code - cfg->cil_code;
2505 /* this is not thread save, but good enough */
2506 /* fixme: howto handle overflows? */
2507 //x86_inc_mem (code, &cov->data [bb->dfn].count);
2510 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) {
2511 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2512 (gpointer)"mono_break");
2513 code = emit_call_seq (cfg, code);
2516 MONO_BB_FOR_EACH_INS (bb, ins) {
2517 offset = code - cfg->native_code;
2519 max_len = ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
2521 if (offset > (cfg->code_size - max_len - 16)) {
2522 cfg->code_size *= 2;
2523 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
2524 code = cfg->native_code + offset;
2526 // if (ins->cil_code)
2527 // g_print ("cil code\n");
2528 mono_debug_record_line_number (cfg, ins, offset);
2530 switch (ins->opcode) {
2531 case OP_MEMORY_BARRIER:
2534 #ifdef HAVE_AEABI_READ_TP
2535 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2536 (gpointer)"__aeabi_read_tp");
2537 code = emit_call_seq (cfg, code);
2539 ARM_LDR_IMM (code, ins->dreg, ARMREG_R0, ins->inst_offset);
2541 g_assert_not_reached ();
2545 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2546 ppc_mulhw (code, ppc_r3, ins->sreg1, ins->sreg2);
2549 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2550 ppc_mulhwu (code, ppc_r3, ins->sreg1, ins->sreg2);
2552 case OP_STOREI1_MEMBASE_IMM:
2553 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFF);
2554 g_assert (arm_is_imm12 (ins->inst_offset));
2555 ARM_STRB_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2557 case OP_STOREI2_MEMBASE_IMM:
2558 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFFFF);
2559 g_assert (arm_is_imm8 (ins->inst_offset));
2560 ARM_STRH_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2562 case OP_STORE_MEMBASE_IMM:
2563 case OP_STOREI4_MEMBASE_IMM:
2564 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm);
2565 g_assert (arm_is_imm12 (ins->inst_offset));
2566 ARM_STR_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2568 case OP_STOREI1_MEMBASE_REG:
2569 g_assert (arm_is_imm12 (ins->inst_offset));
2570 ARM_STRB_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2572 case OP_STOREI2_MEMBASE_REG:
2573 g_assert (arm_is_imm8 (ins->inst_offset));
2574 ARM_STRH_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2576 case OP_STORE_MEMBASE_REG:
2577 case OP_STOREI4_MEMBASE_REG:
2578 /* this case is special, since it happens for spill code after lowering has been called */
2579 if (arm_is_imm12 (ins->inst_offset)) {
2580 ARM_STR_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2582 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2583 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ARMREG_LR);
2586 case OP_STOREI1_MEMINDEX:
2587 ARM_STRB_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2589 case OP_STOREI2_MEMINDEX:
2590 ARM_STRH_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2592 case OP_STORE_MEMINDEX:
2593 case OP_STOREI4_MEMINDEX:
2594 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2597 g_assert_not_reached ();
2599 case OP_LOAD_MEMINDEX:
2600 case OP_LOADI4_MEMINDEX:
2601 case OP_LOADU4_MEMINDEX:
2602 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2604 case OP_LOADI1_MEMINDEX:
2605 ARM_LDRSB_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2607 case OP_LOADU1_MEMINDEX:
2608 ARM_LDRB_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2610 case OP_LOADI2_MEMINDEX:
2611 ARM_LDRSH_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2613 case OP_LOADU2_MEMINDEX:
2614 ARM_LDRH_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2616 case OP_LOAD_MEMBASE:
2617 case OP_LOADI4_MEMBASE:
2618 case OP_LOADU4_MEMBASE:
2619 /* this case is special, since it happens for spill code after lowering has been called */
2620 if (arm_is_imm12 (ins->inst_offset)) {
2621 ARM_LDR_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2623 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2624 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ARMREG_LR);
2627 case OP_LOADI1_MEMBASE:
2628 g_assert (arm_is_imm8 (ins->inst_offset));
2629 ARM_LDRSB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2631 case OP_LOADU1_MEMBASE:
2632 g_assert (arm_is_imm12 (ins->inst_offset));
2633 ARM_LDRB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2635 case OP_LOADU2_MEMBASE:
2636 g_assert (arm_is_imm8 (ins->inst_offset));
2637 ARM_LDRH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2639 case OP_LOADI2_MEMBASE:
2640 g_assert (arm_is_imm8 (ins->inst_offset));
2641 ARM_LDRSH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2643 case OP_ICONV_TO_I1:
2644 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 24);
2645 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 24);
2647 case OP_ICONV_TO_I2:
2648 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2649 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 16);
2651 case OP_ICONV_TO_U1:
2652 ARM_AND_REG_IMM8 (code, ins->dreg, ins->sreg1, 0xff);
2654 case OP_ICONV_TO_U2:
2655 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2656 ARM_SHR_IMM (code, ins->dreg, ins->dreg, 16);
2660 ARM_CMP_REG_REG (code, ins->sreg1, ins->sreg2);
2662 case OP_COMPARE_IMM:
2663 case OP_ICOMPARE_IMM:
2664 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2665 g_assert (imm8 >= 0);
2666 ARM_CMP_REG_IMM (code, ins->sreg1, imm8, rot_amount);
2670 * gdb does not like encountering the hw breakpoint ins in the debugged code.
2671 * So instead of emitting a trap, we emit a call a C function and place a
2674 //*(int*)code = 0xef9f0001;
2677 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2678 (gpointer)"mono_break");
2679 code = emit_call_seq (cfg, code);
2681 case OP_RELAXED_NOP:
2686 case OP_DUMMY_STORE:
2687 case OP_NOT_REACHED:
2692 ARM_ADDS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2695 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2699 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2702 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2703 g_assert (imm8 >= 0);
2704 ARM_ADDS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2708 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2709 g_assert (imm8 >= 0);
2710 ARM_ADD_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2714 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2715 g_assert (imm8 >= 0);
2716 ARM_ADCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2719 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2720 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2722 case OP_IADD_OVF_UN:
2723 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2724 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2727 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2728 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2730 case OP_ISUB_OVF_UN:
2731 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2732 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2734 case OP_ADD_OVF_CARRY:
2735 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2736 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2738 case OP_ADD_OVF_UN_CARRY:
2739 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2740 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2742 case OP_SUB_OVF_CARRY:
2743 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2744 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2746 case OP_SUB_OVF_UN_CARRY:
2747 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2748 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2752 ARM_SUBS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2755 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2756 g_assert (imm8 >= 0);
2757 ARM_SUBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2760 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2764 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2768 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2769 g_assert (imm8 >= 0);
2770 ARM_SUB_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2774 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2775 g_assert (imm8 >= 0);
2776 ARM_SBCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2778 case OP_ARM_RSBS_IMM:
2779 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2780 g_assert (imm8 >= 0);
2781 ARM_RSBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2783 case OP_ARM_RSC_IMM:
2784 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2785 g_assert (imm8 >= 0);
2786 ARM_RSC_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2789 ARM_AND_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2793 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2794 g_assert (imm8 >= 0);
2795 ARM_AND_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2803 /* crappy ARM arch doesn't have a DIV instruction */
2804 g_assert_not_reached ();
2806 ARM_ORR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2810 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2811 g_assert (imm8 >= 0);
2812 ARM_ORR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2815 ARM_EOR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2819 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2820 g_assert (imm8 >= 0);
2821 ARM_EOR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2824 ARM_SHL_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2829 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2830 else if (ins->dreg != ins->sreg1)
2831 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2834 ARM_SAR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2839 ARM_SAR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2840 else if (ins->dreg != ins->sreg1)
2841 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2844 case OP_ISHR_UN_IMM:
2846 ARM_SHR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2847 else if (ins->dreg != ins->sreg1)
2848 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2851 ARM_SHR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2854 ARM_MVN_REG_REG (code, ins->dreg, ins->sreg1);
2857 ARM_RSB_REG_IMM8 (code, ins->dreg, ins->sreg1, 0);
2860 if (ins->dreg == ins->sreg2)
2861 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2863 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg2, ins->sreg1);
2866 g_assert_not_reached ();
2869 /* FIXME: handle ovf/ sreg2 != dreg */
2870 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2871 /* FIXME: MUL doesn't set the C/O flags on ARM */
2873 case OP_IMUL_OVF_UN:
2874 /* FIXME: handle ovf/ sreg2 != dreg */
2875 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2876 /* FIXME: MUL doesn't set the C/O flags on ARM */
2879 code = mono_arm_emit_load_imm (code, ins->dreg, ins->inst_c0);
2882 /* Load the GOT offset */
2883 mono_add_patch_info (cfg, offset, (MonoJumpInfoType)ins->inst_i1, ins->inst_p0);
2884 ARM_LDR_IMM (code, ins->dreg, ARMREG_PC, 0);
2886 *(gpointer*)code = NULL;
2888 /* Load the value from the GOT */
2889 ARM_LDR_REG_REG (code, ins->dreg, ARMREG_PC, ins->dreg);
2891 case OP_ICONV_TO_I4:
2892 case OP_ICONV_TO_U4:
2894 if (ins->dreg != ins->sreg1)
2895 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2898 int saved = ins->sreg2;
2899 if (ins->sreg2 == ARM_LSW_REG) {
2900 ARM_MOV_REG_REG (code, ARMREG_LR, ins->sreg2);
2903 if (ins->sreg1 != ARM_LSW_REG)
2904 ARM_MOV_REG_REG (code, ARM_LSW_REG, ins->sreg1);
2905 if (saved != ARM_MSW_REG)
2906 ARM_MOV_REG_REG (code, ARM_MSW_REG, saved);
2911 ARM_MVFD (code, ins->dreg, ins->sreg1);
2912 #elif defined(ARM_FPU_VFP)
2913 ARM_CPYD (code, ins->dreg, ins->sreg1);
2916 case OP_FCONV_TO_R4:
2918 ARM_MVFS (code, ins->dreg, ins->sreg1);
2919 #elif defined(ARM_FPU_VFP)
2920 ARM_CVTD (code, ins->dreg, ins->sreg1);
2921 ARM_CVTS (code, ins->dreg, ins->dreg);
2926 * Keep in sync with mono_arch_emit_epilog
2928 g_assert (!cfg->method->save_lmf);
2930 code = emit_load_volatile_arguments (cfg, code);
2932 code = emit_big_add (code, ARMREG_SP, cfg->frame_reg, cfg->stack_usage);
2933 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP)) | ((1 << ARMREG_LR)));
2934 mono_add_patch_info (cfg, (guint8*) code - cfg->native_code, MONO_PATCH_INFO_METHOD_JUMP, ins->inst_p0);
2935 if (cfg->compile_aot) {
2936 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
2938 *(gpointer*)code = NULL;
2940 ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_IP);
2946 /* ensure ins->sreg1 is not NULL */
2947 ARM_LDR_IMM (code, ARMREG_LR, ins->sreg1, 0);
2951 if (ppc_is_imm16 (cfg->sig_cookie + cfg->stack_usage)) {
2952 ppc_addi (code, ppc_r11, cfg->frame_reg, cfg->sig_cookie + cfg->stack_usage);
2954 ppc_load (code, ppc_r11, cfg->sig_cookie + cfg->stack_usage);
2955 ppc_add (code, ppc_r11, cfg->frame_reg, ppc_r11);
2957 ppc_stw (code, ppc_r11, 0, ins->sreg1);
2967 call = (MonoCallInst*)ins;
2968 if (ins->flags & MONO_INST_HAS_METHOD)
2969 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, call->method);
2971 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, call->fptr);
2972 code = emit_call_seq (cfg, code);
2973 code = emit_move_return_value (cfg, ins, code);
2979 case OP_VOIDCALL_REG:
2981 code = emit_call_reg (code, ins->sreg1);
2982 code = emit_move_return_value (cfg, ins, code);
2984 case OP_FCALL_MEMBASE:
2985 case OP_LCALL_MEMBASE:
2986 case OP_VCALL_MEMBASE:
2987 case OP_VCALL2_MEMBASE:
2988 case OP_VOIDCALL_MEMBASE:
2989 case OP_CALL_MEMBASE:
2990 g_assert (arm_is_imm12 (ins->inst_offset));
2991 g_assert (ins->sreg1 != ARMREG_LR);
2992 call = (MonoCallInst*)ins;
2993 if (call->method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
2994 ARM_ADD_REG_IMM8 (code, ARMREG_LR, ARMREG_PC, 4);
2995 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
2997 * We can't embed the method in the code stream in PIC code, or
2999 * Instead, we put it in V5 in code emitted by
3000 * mono_arch_emit_imt_argument (), and embed NULL here to
3001 * signal the IMT thunk that the value is in V5.
3003 if (call->dynamic_imt_arg)
3004 *((gpointer*)code) = NULL;
3006 *((gpointer*)code) = (gpointer)call->method;
3009 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
3010 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
3012 code = emit_move_return_value (cfg, ins, code);
3015 /* keep alignment */
3016 int alloca_waste = cfg->param_area;
3019 /* round the size to 8 bytes */
3020 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->sreg1, 7);
3021 ARM_BIC_REG_IMM8 (code, ins->dreg, ins->dreg, 7);
3023 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->dreg, alloca_waste);
3024 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ins->dreg);
3025 /* memzero the area: dreg holds the size, sp is the pointer */
3026 if (ins->flags & MONO_INST_INIT) {
3027 guint8 *start_loop, *branch_to_cond;
3028 ARM_MOV_REG_IMM8 (code, ARMREG_LR, 0);
3029 branch_to_cond = code;
3032 ARM_STR_REG_REG (code, ARMREG_LR, ARMREG_SP, ins->dreg);
3033 arm_patch (branch_to_cond, code);
3034 /* decrement by 4 and set flags */
3035 ARM_SUBS_REG_IMM8 (code, ins->dreg, ins->dreg, 4);
3036 ARM_B_COND (code, ARMCOND_GE, 0);
3037 arm_patch (code - 4, start_loop);
3039 ARM_ADD_REG_IMM8 (code, ins->dreg, ARMREG_SP, alloca_waste);
3043 if (ins->sreg1 != ARMREG_R0)
3044 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
3045 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3046 (gpointer)"mono_arch_throw_exception");
3047 code = emit_call_seq (cfg, code);
3051 if (ins->sreg1 != ARMREG_R0)
3052 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
3053 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3054 (gpointer)"mono_arch_rethrow_exception");
3055 code = emit_call_seq (cfg, code);
3058 case OP_START_HANDLER: {
3059 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3061 if (arm_is_imm12 (spvar->inst_offset)) {
3062 ARM_STR_IMM (code, ARMREG_LR, spvar->inst_basereg, spvar->inst_offset);
3064 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3065 ARM_STR_REG_REG (code, ARMREG_LR, spvar->inst_basereg, ARMREG_IP);
3069 case OP_ENDFILTER: {
3070 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3072 if (ins->sreg1 != ARMREG_R0)
3073 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
3074 if (arm_is_imm12 (spvar->inst_offset)) {
3075 ARM_LDR_IMM (code, ARMREG_IP, spvar->inst_basereg, spvar->inst_offset);
3077 g_assert (ARMREG_IP != spvar->inst_basereg);
3078 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3079 ARM_LDR_REG_REG (code, ARMREG_IP, spvar->inst_basereg, ARMREG_IP);
3081 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
3084 case OP_ENDFINALLY: {
3085 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3087 if (arm_is_imm12 (spvar->inst_offset)) {
3088 ARM_LDR_IMM (code, ARMREG_IP, spvar->inst_basereg, spvar->inst_offset);
3090 g_assert (ARMREG_IP != spvar->inst_basereg);
3091 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3092 ARM_LDR_REG_REG (code, ARMREG_IP, spvar->inst_basereg, ARMREG_IP);
3094 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
3097 case OP_CALL_HANDLER:
3098 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb);
3102 ins->inst_c0 = code - cfg->native_code;
3105 /*if (ins->inst_target_bb->native_offset) {
3107 //x86_jump_code (code, cfg->native_code + ins->inst_target_bb->native_offset);
3109 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb);
3114 ARM_MOV_REG_REG (code, ARMREG_PC, ins->sreg1);
3118 * In the normal case we have:
3119 * ldr pc, [pc, ins->sreg1 << 2]
3122 * ldr lr, [pc, ins->sreg1 << 2]
3124 * After follows the data.
3125 * FIXME: add aot support.
3127 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_SWITCH, ins->inst_p0);
3128 max_len += 4 * GPOINTER_TO_INT (ins->klass);
3129 if (offset > (cfg->code_size - max_len - 16)) {
3130 cfg->code_size += max_len;
3131 cfg->code_size *= 2;
3132 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
3133 code = cfg->native_code + offset;
3135 ARM_LDR_REG_REG_SHIFT (code, ARMREG_PC, ARMREG_PC, ins->sreg1, ARMSHIFT_LSL, 2);
3137 code += 4 * GPOINTER_TO_INT (ins->klass);
3141 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
3142 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
3146 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3147 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LT);
3151 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3152 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LO);
3156 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3157 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_GT);
3161 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3162 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_HI);
3164 case OP_COND_EXC_EQ:
3165 case OP_COND_EXC_NE_UN:
3166 case OP_COND_EXC_LT:
3167 case OP_COND_EXC_LT_UN:
3168 case OP_COND_EXC_GT:
3169 case OP_COND_EXC_GT_UN:
3170 case OP_COND_EXC_GE:
3171 case OP_COND_EXC_GE_UN:
3172 case OP_COND_EXC_LE:
3173 case OP_COND_EXC_LE_UN:
3174 EMIT_COND_SYSTEM_EXCEPTION (ins->opcode - OP_COND_EXC_EQ, ins->inst_p1);
3176 case OP_COND_EXC_IEQ:
3177 case OP_COND_EXC_INE_UN:
3178 case OP_COND_EXC_ILT:
3179 case OP_COND_EXC_ILT_UN:
3180 case OP_COND_EXC_IGT:
3181 case OP_COND_EXC_IGT_UN:
3182 case OP_COND_EXC_IGE:
3183 case OP_COND_EXC_IGE_UN:
3184 case OP_COND_EXC_ILE:
3185 case OP_COND_EXC_ILE_UN:
3186 EMIT_COND_SYSTEM_EXCEPTION (ins->opcode - OP_COND_EXC_IEQ, ins->inst_p1);
3189 case OP_COND_EXC_IC:
3190 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_CS, ins->inst_p1);
3192 case OP_COND_EXC_OV:
3193 case OP_COND_EXC_IOV:
3194 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VS, ins->inst_p1);
3196 case OP_COND_EXC_NC:
3197 case OP_COND_EXC_INC:
3198 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_CC, ins->inst_p1);
3200 case OP_COND_EXC_NO:
3201 case OP_COND_EXC_INO:
3202 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VC, ins->inst_p1);
3214 EMIT_COND_BRANCH (ins, ins->opcode - OP_IBEQ);
3217 /* floating point opcodes */
3220 if (cfg->compile_aot) {
3221 ARM_LDFD (code, ins->dreg, ARMREG_PC, 0);
3223 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3225 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
3228 /* FIXME: we can optimize the imm load by dealing with part of
3229 * the displacement in LDFD (aligning to 512).
3231 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3232 ARM_LDFD (code, ins->dreg, ARMREG_LR, 0);
3236 if (cfg->compile_aot) {
3237 ARM_LDFS (code, ins->dreg, ARMREG_PC, 0);
3239 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3242 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3243 ARM_LDFS (code, ins->dreg, ARMREG_LR, 0);
3246 case OP_STORER8_MEMBASE_REG:
3247 /* This is generated by the local regalloc pass which runs after the lowering pass */
3248 if (!arm_is_fpimm8 (ins->inst_offset)) {
3249 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3250 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_destbasereg);
3251 ARM_STFD (code, ins->sreg1, ARMREG_LR, 0);
3253 ARM_STFD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3256 case OP_LOADR8_MEMBASE:
3257 /* This is generated by the local regalloc pass which runs after the lowering pass */
3258 if (!arm_is_fpimm8 (ins->inst_offset)) {
3259 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3260 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_basereg);
3261 ARM_LDFD (code, ins->dreg, ARMREG_LR, 0);
3263 ARM_LDFD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3266 case OP_STORER4_MEMBASE_REG:
3267 g_assert (arm_is_fpimm8 (ins->inst_offset));
3268 ARM_STFS (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3270 case OP_LOADR4_MEMBASE:
3271 g_assert (arm_is_fpimm8 (ins->inst_offset));
3272 ARM_LDFS (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3274 case OP_ICONV_TO_R_UN: {
3276 tmpreg = ins->dreg == 0? 1: 0;
3277 ARM_CMP_REG_IMM8 (code, ins->sreg1, 0);
3278 ARM_FLTD (code, ins->dreg, ins->sreg1);
3279 ARM_B_COND (code, ARMCOND_GE, 8);
3280 /* save the temp register */
3281 ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
3282 ARM_STFD (code, tmpreg, ARMREG_SP, 0);
3283 ARM_LDFD (code, tmpreg, ARMREG_PC, 12);
3284 ARM_FPA_ADFD (code, ins->dreg, ins->dreg, tmpreg);
3285 ARM_LDFD (code, tmpreg, ARMREG_SP, 0);
3286 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
3287 /* skip the constant pool */
3290 *(int*)code = 0x41f00000;
3295 * ldfltd ftemp, [pc, #8] 0x41f00000 0x00000000
3296 * adfltd fdest, fdest, ftemp
3300 case OP_ICONV_TO_R4:
3301 ARM_FLTS (code, ins->dreg, ins->sreg1);
3303 case OP_ICONV_TO_R8:
3304 ARM_FLTD (code, ins->dreg, ins->sreg1);
3307 #elif defined(ARM_FPU_VFP)
3310 if (cfg->compile_aot) {
3311 ARM_FLDD (code, ins->dreg, ARMREG_PC, 0);
3313 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3315 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
3318 /* FIXME: we can optimize the imm load by dealing with part of
3319 * the displacement in LDFD (aligning to 512).
3321 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3322 ARM_FLDD (code, ins->dreg, ARMREG_LR, 0);
3326 if (cfg->compile_aot) {
3327 ARM_FLDS (code, ins->dreg, ARMREG_PC, 0);
3329 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3331 ARM_CVTS (code, ins->dreg, ins->dreg);
3333 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3334 ARM_FLDS (code, ins->dreg, ARMREG_LR, 0);
3335 ARM_CVTS (code, ins->dreg, ins->dreg);
3338 case OP_STORER8_MEMBASE_REG:
3339 /* This is generated by the local regalloc pass which runs after the lowering pass */
3340 if (!arm_is_fpimm8 (ins->inst_offset)) {
3341 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3342 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_destbasereg);
3343 ARM_FSTD (code, ins->sreg1, ARMREG_LR, 0);
3345 ARM_FSTD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3348 case OP_LOADR8_MEMBASE:
3349 /* This is generated by the local regalloc pass which runs after the lowering pass */
3350 if (!arm_is_fpimm8 (ins->inst_offset)) {
3351 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3352 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_basereg);
3353 ARM_FLDD (code, ins->dreg, ARMREG_LR, 0);
3355 ARM_FLDD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3358 case OP_STORER4_MEMBASE_REG:
3359 g_assert (arm_is_fpimm8 (ins->inst_offset));
3360 ARM_CVTD (code, ARM_VFP_F0, ins->sreg1);
3361 ARM_FSTS (code, ARM_VFP_F0, ins->inst_destbasereg, ins->inst_offset);
3363 case OP_LOADR4_MEMBASE:
3364 g_assert (arm_is_fpimm8 (ins->inst_offset));
3365 ARM_FLDS (code, ARM_VFP_F0, ins->inst_basereg, ins->inst_offset);
3366 ARM_CVTS (code, ins->dreg, ARM_VFP_F0);
3368 case OP_ICONV_TO_R_UN: {
3369 g_assert_not_reached ();
3372 case OP_ICONV_TO_R4:
3373 ARM_FMSR (code, ARM_VFP_F0, ins->sreg1);
3374 ARM_FSITOS (code, ARM_VFP_F0, ARM_VFP_F0);
3375 ARM_CVTS (code, ins->dreg, ARM_VFP_F0);
3377 case OP_ICONV_TO_R8:
3378 ARM_FMSR (code, ARM_VFP_F0, ins->sreg1);
3379 ARM_FSITOD (code, ins->dreg, ARM_VFP_F0);
3383 if (mono_method_signature (cfg->method)->ret->type == MONO_TYPE_R4) {
3384 ARM_CVTD (code, ARM_VFP_F0, ins->sreg1);
3385 ARM_FMRS (code, ARMREG_R0, ARM_VFP_F0);
3387 ARM_FMRRD (code, ARMREG_R0, ARMREG_R1, ins->sreg1);
3393 case OP_FCONV_TO_I1:
3394 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, TRUE);
3396 case OP_FCONV_TO_U1:
3397 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, FALSE);
3399 case OP_FCONV_TO_I2:
3400 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, TRUE);
3402 case OP_FCONV_TO_U2:
3403 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, FALSE);
3405 case OP_FCONV_TO_I4:
3407 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, TRUE);
3409 case OP_FCONV_TO_U4:
3411 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, FALSE);
3413 case OP_FCONV_TO_I8:
3414 case OP_FCONV_TO_U8:
3415 g_assert_not_reached ();
3416 /* Implemented as helper calls */
3418 case OP_LCONV_TO_R_UN:
3419 g_assert_not_reached ();
3420 /* Implemented as helper calls */
3422 case OP_LCONV_TO_OVF_I:
3423 case OP_LCONV_TO_OVF_I4_2: {
3424 guint8 *high_bit_not_set, *valid_negative, *invalid_negative, *valid_positive;
3426 * Valid ints: 0xffffffff:8000000 to 00000000:0x7f000000
3429 ARM_CMP_REG_IMM8 (code, ins->sreg1, 0);
3430 high_bit_not_set = code;
3431 ARM_B_COND (code, ARMCOND_GE, 0); /*branch if bit 31 of the lower part is not set*/
3433 ARM_CMN_REG_IMM8 (code, ins->sreg2, 1); /*This have the same effect as CMP reg, 0xFFFFFFFF */
3434 valid_negative = code;
3435 ARM_B_COND (code, ARMCOND_EQ, 0); /*branch if upper part == 0xFFFFFFFF (lower part has bit 31 set) */
3436 invalid_negative = code;
3437 ARM_B_COND (code, ARMCOND_AL, 0);
3439 arm_patch (high_bit_not_set, code);
3441 ARM_CMP_REG_IMM8 (code, ins->sreg2, 0);
3442 valid_positive = code;
3443 ARM_B_COND (code, ARMCOND_EQ, 0); /*branch if upper part == 0 (lower part has bit 31 clear)*/
3445 arm_patch (invalid_negative, code);
3446 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_AL, "OverflowException");
3448 arm_patch (valid_negative, code);
3449 arm_patch (valid_positive, code);
3451 if (ins->dreg != ins->sreg1)
3452 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
3457 ARM_FPA_ADFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3460 ARM_FPA_SUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3463 ARM_FPA_MUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3466 ARM_FPA_DVFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3469 ARM_MNFD (code, ins->dreg, ins->sreg1);
3471 #elif defined(ARM_FPU_VFP)
3473 ARM_VFP_ADDD (code, ins->dreg, ins->sreg1, ins->sreg2);
3476 ARM_VFP_SUBD (code, ins->dreg, ins->sreg1, ins->sreg2);
3479 ARM_VFP_MULD (code, ins->dreg, ins->sreg1, ins->sreg2);
3482 ARM_VFP_DIVD (code, ins->dreg, ins->sreg1, ins->sreg2);
3485 ARM_NEGD (code, ins->dreg, ins->sreg1);
3490 g_assert_not_reached ();
3494 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3495 #elif defined(ARM_FPU_VFP)
3496 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3502 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3503 #elif defined(ARM_FPU_VFP)
3504 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3507 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
3508 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
3512 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3513 #elif defined(ARM_FPU_VFP)
3514 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3517 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3518 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3522 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3523 #elif defined(ARM_FPU_VFP)
3524 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3527 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3528 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3529 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
3534 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3535 #elif defined(ARM_FPU_VFP)
3536 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3539 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3540 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3545 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3546 #elif defined(ARM_FPU_VFP)
3547 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3550 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3551 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3552 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
3554 /* ARM FPA flags table:
3555 * N Less than ARMCOND_MI
3556 * Z Equal ARMCOND_EQ
3557 * C Greater Than or Equal ARMCOND_CS
3558 * V Unordered ARMCOND_VS
3561 EMIT_COND_BRANCH (ins, OP_IBEQ - OP_IBEQ);
3564 EMIT_COND_BRANCH (ins, OP_IBNE_UN - OP_IBEQ);
3567 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
3570 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3571 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
3577 g_assert_not_reached ();
3581 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_GE);
3583 /* FPA requires EQ even thou the docs suggests that just CS is enough */
3584 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_EQ);
3585 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_CS);
3589 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3590 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_GE);
3595 if (ins->dreg != ins->sreg1)
3596 ARM_MVFD (code, ins->dreg, ins->sreg1);
3597 #elif defined(ARM_FPU_VFP)
3598 ARM_ABSD (code, ARM_VFP_D1, ins->sreg1);
3599 ARM_FLDD (code, ARM_VFP_D0, ARMREG_PC, 0);
3601 *(guint32*)code = 0xffffffff;
3603 *(guint32*)code = 0x7fefffff;
3605 ARM_CMPD (code, ARM_VFP_D1, ARM_VFP_D0);
3607 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_GT, "ArithmeticException");
3608 ARM_CMPD (code, ins->sreg1, ins->sreg1);
3610 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VS, "ArithmeticException");
3612 ARM_CPYD (code, ins->dreg, ins->sreg1);
3617 g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
3618 g_assert_not_reached ();
3621 if ((cfg->opt & MONO_OPT_BRANCH) && ((code - cfg->native_code - offset) > max_len)) {
3622 g_warning ("wrong maximal instruction length of instruction %s (expected %d, got %d)",
3623 mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset);
3624 g_assert_not_reached ();
3630 last_offset = offset;
3633 cfg->code_len = code - cfg->native_code;
3636 #endif /* DISABLE_JIT */
3638 #ifdef HAVE_AEABI_READ_TP
3639 void __aeabi_read_tp (void);
3643 mono_arch_register_lowlevel_calls (void)
3645 /* The signature doesn't matter */
3646 mono_register_jit_icall (mono_arm_throw_exception, "mono_arm_throw_exception", mono_create_icall_signature ("void"), TRUE);
3647 mono_register_jit_icall (mono_arm_throw_exception_by_token, "mono_arm_throw_exception_by_token", mono_create_icall_signature ("void"), TRUE);
3649 #ifdef HAVE_AEABI_READ_TP
3650 mono_register_jit_icall (__aeabi_read_tp, "__aeabi_read_tp", mono_create_icall_signature ("void"), TRUE);
3654 #define patch_lis_ori(ip,val) do {\
3655 guint16 *__lis_ori = (guint16*)(ip); \
3656 __lis_ori [1] = (((guint32)(val)) >> 16) & 0xffff; \
3657 __lis_ori [3] = ((guint32)(val)) & 0xffff; \
3661 mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, gboolean run_cctors)
3663 MonoJumpInfo *patch_info;
3664 gboolean compile_aot = !run_cctors;
3666 for (patch_info = ji; patch_info; patch_info = patch_info->next) {
3667 unsigned char *ip = patch_info->ip.i + code;
3668 const unsigned char *target;
3670 if (patch_info->type == MONO_PATCH_INFO_SWITCH && !compile_aot) {
3671 gpointer *jt = (gpointer*)(ip + 8);
3673 /* jt is the inlined jump table, 2 instructions after ip
3674 * In the normal case we store the absolute addresses,
3675 * otherwise the displacements.
3677 for (i = 0; i < patch_info->data.table->table_size; i++)
3678 jt [i] = code + (int)patch_info->data.table->table [i];
3681 target = mono_resolve_patch_target (method, domain, code, patch_info, run_cctors);
3684 switch (patch_info->type) {
3685 case MONO_PATCH_INFO_BB:
3686 case MONO_PATCH_INFO_LABEL:
3689 /* No need to patch these */
3694 switch (patch_info->type) {
3695 case MONO_PATCH_INFO_IP:
3696 g_assert_not_reached ();
3697 patch_lis_ori (ip, ip);
3699 case MONO_PATCH_INFO_METHOD_REL:
3700 g_assert_not_reached ();
3701 *((gpointer *)(ip)) = code + patch_info->data.offset;
3703 case MONO_PATCH_INFO_METHODCONST:
3704 case MONO_PATCH_INFO_CLASS:
3705 case MONO_PATCH_INFO_IMAGE:
3706 case MONO_PATCH_INFO_FIELD:
3707 case MONO_PATCH_INFO_VTABLE:
3708 case MONO_PATCH_INFO_IID:
3709 case MONO_PATCH_INFO_SFLDA:
3710 case MONO_PATCH_INFO_LDSTR:
3711 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
3712 case MONO_PATCH_INFO_LDTOKEN:
3713 g_assert_not_reached ();
3714 /* from OP_AOTCONST : lis + ori */
3715 patch_lis_ori (ip, target);
3717 case MONO_PATCH_INFO_R4:
3718 case MONO_PATCH_INFO_R8:
3719 g_assert_not_reached ();
3720 *((gconstpointer *)(ip + 2)) = patch_info->data.target;
3722 case MONO_PATCH_INFO_EXC_NAME:
3723 g_assert_not_reached ();
3724 *((gconstpointer *)(ip + 1)) = patch_info->data.name;
3726 case MONO_PATCH_INFO_NONE:
3727 case MONO_PATCH_INFO_BB_OVF:
3728 case MONO_PATCH_INFO_EXC_OVF:
3729 /* everything is dealt with at epilog output time */
3734 arm_patch (ip, target);
3739 * Stack frame layout:
3741 * ------------------- fp
3742 * MonoLMF structure or saved registers
3743 * -------------------
3745 * -------------------
3747 * -------------------
3748 * optional 8 bytes for tracing
3749 * -------------------
3750 * param area size is cfg->param_area
3751 * ------------------- sp
3754 mono_arch_emit_prolog (MonoCompile *cfg)
3756 MonoMethod *method = cfg->method;
3758 MonoMethodSignature *sig;
3760 int alloc_size, pos, max_offset, i, rot_amount;
3765 int prev_sp_offset, reg_offset;
3767 if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
3770 sig = mono_method_signature (method);
3771 cfg->code_size = 256 + sig->param_count * 20;
3772 code = cfg->native_code = g_malloc (cfg->code_size);
3774 mono_emit_unwind_op_def_cfa (cfg, code, ARMREG_SP, 0);
3776 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
3778 alloc_size = cfg->stack_offset;
3781 if (!method->save_lmf) {
3782 /* We save SP by storing it into IP and saving IP */
3783 ARM_PUSH (code, (cfg->used_int_regs | (1 << ARMREG_IP) | (1 << ARMREG_LR)));
3784 prev_sp_offset = 8; /* ip and lr */
3785 for (i = 0; i < 16; ++i) {
3786 if (cfg->used_int_regs & (1 << i))
3787 prev_sp_offset += 4;
3789 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset);
3791 for (i = 0; i < 16; ++i) {
3792 if ((cfg->used_int_regs & (1 << i)) || (i == ARMREG_IP) || (i == ARMREG_LR)) {
3793 mono_emit_unwind_op_offset (cfg, code, i, (- prev_sp_offset) + reg_offset);
3798 ARM_PUSH (code, 0x5ff0);
3799 prev_sp_offset = 4 * 10; /* all but r0-r3, sp and pc */
3800 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset);
3802 for (i = 0; i < 16; ++i) {
3803 if ((i > ARMREG_R3) && (i != ARMREG_SP) && (i != ARMREG_PC)) {
3804 mono_emit_unwind_op_offset (cfg, code, i, (- prev_sp_offset) + reg_offset);
3808 pos += sizeof (MonoLMF) - prev_sp_offset;
3812 // align to MONO_ARCH_FRAME_ALIGNMENT bytes
3813 if (alloc_size & (MONO_ARCH_FRAME_ALIGNMENT - 1)) {
3814 alloc_size += MONO_ARCH_FRAME_ALIGNMENT - 1;
3815 alloc_size &= ~(MONO_ARCH_FRAME_ALIGNMENT - 1);
3818 /* the stack used in the pushed regs */
3819 if (prev_sp_offset & 4)
3821 cfg->stack_usage = alloc_size;
3823 if ((i = mono_arm_is_rotated_imm8 (alloc_size, &rot_amount)) >= 0) {
3824 ARM_SUB_REG_IMM (code, ARMREG_SP, ARMREG_SP, i, rot_amount);
3826 code = mono_arm_emit_load_imm (code, ARMREG_IP, alloc_size);
3827 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
3829 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset + alloc_size);
3831 if (cfg->frame_reg != ARMREG_SP) {
3832 ARM_MOV_REG_REG (code, cfg->frame_reg, ARMREG_SP);
3833 mono_emit_unwind_op_def_cfa_reg (cfg, code, cfg->frame_reg);
3835 //g_print ("prev_sp_offset: %d, alloc_size:%d\n", prev_sp_offset, alloc_size);
3836 prev_sp_offset += alloc_size;
3838 /* compute max_offset in order to use short forward jumps
3839 * we could skip do it on arm because the immediate displacement
3840 * for jumps is large enough, it may be useful later for constant pools
3843 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
3844 MonoInst *ins = bb->code;
3845 bb->max_offset = max_offset;
3847 if (cfg->prof_options & MONO_PROFILE_COVERAGE)
3850 MONO_BB_FOR_EACH_INS (bb, ins)
3851 max_offset += ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
3854 /* store runtime generic context */
3855 if (cfg->rgctx_var) {
3856 MonoInst *ins = cfg->rgctx_var;
3858 g_assert (ins->opcode == OP_REGOFFSET);
3860 if (arm_is_imm12 (ins->inst_offset)) {
3861 ARM_STR_IMM (code, MONO_ARCH_RGCTX_REG, ins->inst_basereg, ins->inst_offset);
3863 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3864 ARM_STR_REG_REG (code, MONO_ARCH_RGCTX_REG, ins->inst_basereg, ARMREG_LR);
3868 /* load arguments allocated to register from the stack */
3871 cinfo = get_call_info (sig, sig->pinvoke);
3873 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
3874 ArgInfo *ainfo = &cinfo->ret;
3875 inst = cfg->vret_addr;
3876 g_assert (arm_is_imm12 (inst->inst_offset));
3877 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3879 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
3880 ArgInfo *ainfo = cinfo->args + i;
3881 inst = cfg->args [pos];
3883 if (cfg->verbose_level > 2)
3884 g_print ("Saving argument %d (type: %d)\n", i, ainfo->regtype);
3885 if (inst->opcode == OP_REGVAR) {
3886 if (ainfo->regtype == RegTypeGeneral)
3887 ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
3888 else if (ainfo->regtype == RegTypeFP) {
3889 g_assert_not_reached ();
3890 } else if (ainfo->regtype == RegTypeBase) {
3891 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
3892 ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3894 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3895 ARM_LDR_REG_REG (code, inst->dreg, ARMREG_SP, ARMREG_IP);
3898 g_assert_not_reached ();
3900 if (cfg->verbose_level > 2)
3901 g_print ("Argument %d assigned to register %s\n", pos, mono_arch_regname (inst->dreg));
3903 /* the argument should be put on the stack: FIXME handle size != word */
3904 if (ainfo->regtype == RegTypeGeneral) {
3905 switch (ainfo->size) {
3907 if (arm_is_imm12 (inst->inst_offset))
3908 ARM_STRB_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3910 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3911 ARM_STRB_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3915 if (arm_is_imm8 (inst->inst_offset)) {
3916 ARM_STRH_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3918 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3919 ARM_STRH_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3923 g_assert (arm_is_imm12 (inst->inst_offset));
3924 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3925 g_assert (arm_is_imm12 (inst->inst_offset + 4));
3926 ARM_STR_IMM (code, ainfo->reg + 1, inst->inst_basereg, inst->inst_offset + 4);
3929 if (arm_is_imm12 (inst->inst_offset)) {
3930 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3932 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3933 ARM_STR_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3937 } else if (ainfo->regtype == RegTypeBaseGen) {
3938 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
3939 g_assert (arm_is_imm12 (inst->inst_offset));
3940 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3941 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3942 ARM_STR_IMM (code, ARMREG_R3, inst->inst_basereg, inst->inst_offset);
3943 } else if (ainfo->regtype == RegTypeBase) {
3944 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
3945 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3947 code = mono_arm_emit_load_imm (code, ARMREG_IP, prev_sp_offset + ainfo->offset);
3948 ARM_LDR_REG_REG (code, ARMREG_LR, ARMREG_SP, ARMREG_IP);
3951 switch (ainfo->size) {
3953 if (arm_is_imm8 (inst->inst_offset)) {
3954 ARM_STRB_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3956 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3957 ARM_STRB_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3961 if (arm_is_imm8 (inst->inst_offset)) {
3962 ARM_STRH_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3964 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3965 ARM_STRH_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3969 if (arm_is_imm12 (inst->inst_offset)) {
3970 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3972 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3973 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3975 if (arm_is_imm12 (prev_sp_offset + ainfo->offset + 4)) {
3976 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset + 4));
3978 code = mono_arm_emit_load_imm (code, ARMREG_IP, prev_sp_offset + ainfo->offset + 4);
3979 ARM_LDR_REG_REG (code, ARMREG_LR, ARMREG_SP, ARMREG_IP);
3981 if (arm_is_imm12 (inst->inst_offset + 4)) {
3982 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3984 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset + 4);
3985 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3989 if (arm_is_imm12 (inst->inst_offset)) {
3990 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3992 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3993 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3997 } else if (ainfo->regtype == RegTypeFP) {
3998 g_assert_not_reached ();
3999 } else if (ainfo->regtype == RegTypeStructByVal) {
4000 int doffset = inst->inst_offset;
4004 size = mini_type_stack_size_full (cfg->generic_sharing_context, inst->inst_vtype, NULL, sig->pinvoke);
4005 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
4006 if (arm_is_imm12 (doffset)) {
4007 ARM_STR_IMM (code, ainfo->reg + cur_reg, inst->inst_basereg, doffset);
4009 code = mono_arm_emit_load_imm (code, ARMREG_IP, doffset);
4010 ARM_STR_REG_REG (code, ainfo->reg + cur_reg, inst->inst_basereg, ARMREG_IP);
4012 soffset += sizeof (gpointer);
4013 doffset += sizeof (gpointer);
4015 if (ainfo->vtsize) {
4016 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
4017 //g_print ("emit_memcpy (prev_sp_ofs: %d, ainfo->offset: %d, soffset: %d)\n", prev_sp_offset, ainfo->offset, soffset);
4018 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, doffset, ARMREG_SP, prev_sp_offset + ainfo->offset);
4020 } else if (ainfo->regtype == RegTypeStructByAddr) {
4021 g_assert_not_reached ();
4022 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
4023 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, inst->inst_offset, ainfo->reg, 0);
4025 g_assert_not_reached ();
4030 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
4031 if (cfg->compile_aot)
4032 /* AOT code is only used in the root domain */
4033 code = mono_arm_emit_load_imm (code, ARMREG_R0, 0);
4035 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->domain);
4036 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
4037 (gpointer)"mono_jit_thread_attach");
4038 code = emit_call_seq (cfg, code);
4041 if (method->save_lmf) {
4042 gboolean get_lmf_fast = FALSE;
4044 #ifdef HAVE_AEABI_READ_TP
4045 gint32 lmf_addr_tls_offset = mono_get_lmf_addr_tls_offset ();
4047 if (lmf_addr_tls_offset != -1) {
4048 get_lmf_fast = TRUE;
4050 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
4051 (gpointer)"__aeabi_read_tp");
4052 code = emit_call_seq (cfg, code);
4054 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, lmf_addr_tls_offset);
4055 get_lmf_fast = TRUE;
4058 if (!get_lmf_fast) {
4059 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
4060 (gpointer)"mono_get_lmf_addr");
4061 code = emit_call_seq (cfg, code);
4063 /* we build the MonoLMF structure on the stack - see mini-arm.h */
4064 /* lmf_offset is the offset from the previous stack pointer,
4065 * alloc_size is the total stack space allocated, so the offset
4066 * of MonoLMF from the current stack ptr is alloc_size - lmf_offset.
4067 * The pointer to the struct is put in r1 (new_lmf).
4068 * r2 is used as scratch
4069 * The callee-saved registers are already in the MonoLMF structure
4071 code = emit_big_add (code, ARMREG_R1, ARMREG_SP, alloc_size - lmf_offset);
4072 /* r0 is the result from mono_get_lmf_addr () */
4073 ARM_STR_IMM (code, ARMREG_R0, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
4074 /* new_lmf->previous_lmf = *lmf_addr */
4075 ARM_LDR_IMM (code, ARMREG_R2, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4076 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4077 /* *(lmf_addr) = r1 */
4078 ARM_STR_IMM (code, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4079 /* Skip method (only needed for trampoline LMF frames) */
4080 ARM_STR_IMM (code, ARMREG_SP, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, ebp));
4081 /* save the current IP */
4082 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_PC);
4083 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, eip));
4087 code = mono_arch_instrument_prolog (cfg, mono_trace_enter_method, code, TRUE);
4089 cfg->code_len = code - cfg->native_code;
4090 g_assert (cfg->code_len < cfg->code_size);
4097 mono_arch_emit_epilog (MonoCompile *cfg)
4099 MonoMethod *method = cfg->method;
4100 int pos, i, rot_amount;
4101 int max_epilog_size = 16 + 20*4;
4104 if (cfg->method->save_lmf)
4105 max_epilog_size += 128;
4107 if (mono_jit_trace_calls != NULL)
4108 max_epilog_size += 50;
4110 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE)
4111 max_epilog_size += 50;
4113 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
4114 cfg->code_size *= 2;
4115 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
4116 mono_jit_stats.code_reallocs++;
4120 * Keep in sync with OP_JMP
4122 code = cfg->native_code + cfg->code_len;
4124 if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) {
4125 code = mono_arch_instrument_epilog (cfg, mono_trace_leave_method, code, TRUE);
4129 if (method->save_lmf) {
4131 /* all but r0-r3, sp and pc */
4132 pos += sizeof (MonoLMF) - (4 * 10);
4134 /* r2 contains the pointer to the current LMF */
4135 code = emit_big_add (code, ARMREG_R2, cfg->frame_reg, cfg->stack_usage - lmf_offset);
4136 /* ip = previous_lmf */
4137 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4139 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
4140 /* *(lmf_addr) = previous_lmf */
4141 ARM_STR_IMM (code, ARMREG_IP, ARMREG_LR, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4142 /* FIXME: speedup: there is no actual need to restore the registers if
4143 * we didn't actually change them (idea from Zoltan).
4146 /* point sp at the registers to restore: 10 is 14 -4, because we skip r0-r3 */
4147 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_R2, (sizeof (MonoLMF) - 10 * sizeof (gulong)));
4148 ARM_POP_NWB (code, 0xaff0); /* restore ip to sp and lr to pc */
4150 if ((i = mono_arm_is_rotated_imm8 (cfg->stack_usage, &rot_amount)) >= 0) {
4151 ARM_ADD_REG_IMM (code, ARMREG_SP, cfg->frame_reg, i, rot_amount);
4153 code = mono_arm_emit_load_imm (code, ARMREG_IP, cfg->stack_usage);
4154 ARM_ADD_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
4156 /* FIXME: add v4 thumb interworking support */
4157 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP) | (1 << ARMREG_PC)));
4160 cfg->code_len = code - cfg->native_code;
4162 g_assert (cfg->code_len < cfg->code_size);
4166 /* remove once throw_exception_by_name is eliminated */
4168 exception_id_by_name (const char *name)
4170 if (strcmp (name, "IndexOutOfRangeException") == 0)
4171 return MONO_EXC_INDEX_OUT_OF_RANGE;
4172 if (strcmp (name, "OverflowException") == 0)
4173 return MONO_EXC_OVERFLOW;
4174 if (strcmp (name, "ArithmeticException") == 0)
4175 return MONO_EXC_ARITHMETIC;
4176 if (strcmp (name, "DivideByZeroException") == 0)
4177 return MONO_EXC_DIVIDE_BY_ZERO;
4178 if (strcmp (name, "InvalidCastException") == 0)
4179 return MONO_EXC_INVALID_CAST;
4180 if (strcmp (name, "NullReferenceException") == 0)
4181 return MONO_EXC_NULL_REF;
4182 if (strcmp (name, "ArrayTypeMismatchException") == 0)
4183 return MONO_EXC_ARRAY_TYPE_MISMATCH;
4184 g_error ("Unknown intrinsic exception %s\n", name);
4189 mono_arch_emit_exceptions (MonoCompile *cfg)
4191 MonoJumpInfo *patch_info;
4194 const guint8* exc_throw_pos [MONO_EXC_INTRINS_NUM] = {NULL};
4195 guint8 exc_throw_found [MONO_EXC_INTRINS_NUM] = {0};
4196 int max_epilog_size = 50;
4198 /* count the number of exception infos */
4201 * make sure we have enough space for exceptions
4203 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
4204 if (patch_info->type == MONO_PATCH_INFO_EXC) {
4205 i = exception_id_by_name (patch_info->data.target);
4206 if (!exc_throw_found [i]) {
4207 max_epilog_size += 32;
4208 exc_throw_found [i] = TRUE;
4213 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
4214 cfg->code_size *= 2;
4215 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
4216 mono_jit_stats.code_reallocs++;
4219 code = cfg->native_code + cfg->code_len;
4221 /* add code to raise exceptions */
4222 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
4223 switch (patch_info->type) {
4224 case MONO_PATCH_INFO_EXC: {
4225 MonoClass *exc_class;
4226 unsigned char *ip = patch_info->ip.i + cfg->native_code;
4228 i = exception_id_by_name (patch_info->data.target);
4229 if (exc_throw_pos [i]) {
4230 arm_patch (ip, exc_throw_pos [i]);
4231 patch_info->type = MONO_PATCH_INFO_NONE;
4234 exc_throw_pos [i] = code;
4236 arm_patch (ip, code);
4238 exc_class = mono_class_from_name (mono_defaults.corlib, "System", patch_info->data.name);
4239 g_assert (exc_class);
4241 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_LR);
4242 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
4243 patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD;
4244 patch_info->data.name = "mono_arch_throw_corlib_exception";
4245 patch_info->ip.i = code - cfg->native_code;
4247 *(guint32*)(gpointer)code = exc_class->type_token;
4257 cfg->code_len = code - cfg->native_code;
4259 g_assert (cfg->code_len < cfg->code_size);
4263 static gboolean tls_offset_inited = FALSE;
4266 mono_arch_setup_jit_tls_data (MonoJitTlsData *tls)
4268 if (!tls_offset_inited) {
4269 tls_offset_inited = TRUE;
4271 lmf_tls_offset = mono_get_lmf_tls_offset ();
4272 lmf_addr_tls_offset = mono_get_lmf_addr_tls_offset ();
4277 mono_arch_free_jit_tls_data (MonoJitTlsData *tls)
4282 mono_arch_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
4289 mono_arch_print_tree (MonoInst *tree, int arity)
4295 mono_arch_get_domain_intrinsic (MonoCompile* cfg)
4297 return mono_get_domain_intrinsic (cfg);
4301 mono_arch_get_thread_intrinsic (MonoCompile* cfg)
4303 return mono_get_thread_intrinsic (cfg);
4307 mono_arch_get_patch_offset (guint8 *code)
4314 mono_arch_flush_register_windows (void)
4318 #ifdef MONO_ARCH_HAVE_IMT
4321 mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoInst *imt_arg)
4323 if (cfg->compile_aot) {
4324 int method_reg = mono_alloc_ireg (cfg);
4327 call->dynamic_imt_arg = TRUE;
4330 mono_call_inst_add_outarg_reg (cfg, call, imt_arg->dreg, ARMREG_V5, FALSE);
4332 MONO_INST_NEW (cfg, ins, OP_AOTCONST);
4333 ins->dreg = method_reg;
4334 ins->inst_p0 = call->method;
4335 ins->inst_c1 = MONO_PATCH_INFO_METHODCONST;
4336 MONO_ADD_INS (cfg->cbb, ins);
4338 mono_call_inst_add_outarg_reg (cfg, call, method_reg, ARMREG_V5, FALSE);
4340 } else if (cfg->generic_context) {
4342 /* Always pass in a register for simplicity */
4343 call->dynamic_imt_arg = TRUE;
4345 cfg->uses_rgctx_reg = TRUE;
4348 mono_call_inst_add_outarg_reg (cfg, call, imt_arg->dreg, ARMREG_V5, FALSE);
4351 int method_reg = mono_alloc_preg (cfg);
4353 MONO_INST_NEW (cfg, ins, OP_PCONST);
4354 ins->inst_p0 = call->method;
4355 ins->dreg = method_reg;
4356 MONO_ADD_INS (cfg->cbb, ins);
4358 mono_call_inst_add_outarg_reg (cfg, call, method_reg, ARMREG_V5, FALSE);
4364 mono_arch_find_imt_method (mgreg_t *regs, guint8 *code)
4366 guint32 *code_ptr = (guint32*)code;
4368 /* The IMT value is stored in the code stream right after the LDC instruction. */
4369 if (!IS_LDR_PC (code_ptr [0])) {
4370 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]);
4371 g_assert (IS_LDR_PC (code_ptr [0]));
4373 if (code_ptr [1] == 0)
4374 /* This is AOTed code, the IMT method is in V5 */
4375 return (MonoMethod*)regs [ARMREG_V5];
4377 return (MonoMethod*) code_ptr [1];
4381 mono_arch_find_this_argument (mgreg_t *regs, MonoMethod *method, MonoGenericSharingContext *gsctx)
4383 return mono_arch_get_this_arg_from_call (gsctx, mono_method_signature (method), regs, NULL);
4387 mono_arch_find_static_call_vtable (mgreg_t *regs, guint8 *code)
4389 return (MonoVTable*) regs [MONO_ARCH_RGCTX_REG];
4392 #define ENABLE_WRONG_METHOD_CHECK 0
4393 #define BASE_SIZE (6 * 4)
4394 #define BSEARCH_ENTRY_SIZE (4 * 4)
4395 #define CMP_SIZE (3 * 4)
4396 #define BRANCH_SIZE (1 * 4)
4397 #define CALL_SIZE (2 * 4)
4398 #define WMC_SIZE (5 * 4)
4399 #define DISTANCE(A, B) (((gint32)(B)) - ((gint32)(A)))
4402 arm_emit_value_and_patch_ldr (arminstr_t *code, arminstr_t *target, guint32 value)
4404 guint32 delta = DISTANCE (target, code);
4406 g_assert (delta >= 0 && delta <= 0xFFF);
4407 *target = *target | delta;
4413 mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count,
4414 gpointer fail_tramp)
4416 int size, i, extra_space = 0;
4417 arminstr_t *code, *start, *vtable_target = NULL;
4418 gboolean large_offsets = FALSE;
4419 guint32 **constant_pool_starts;
4422 constant_pool_starts = g_new0 (guint32*, count);
4425 * We might be called with a fail_tramp from the IMT builder code even if
4426 * MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK is not defined.
4428 //g_assert (!fail_tramp);
4430 for (i = 0; i < count; ++i) {
4431 MonoIMTCheckItem *item = imt_entries [i];
4432 if (item->is_equals) {
4433 if (!arm_is_imm12 (DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]))) {
4434 item->chunk_size += 32;
4435 large_offsets = TRUE;
4438 if (item->check_target_idx) {
4439 if (!item->compare_done)
4440 item->chunk_size += CMP_SIZE;
4441 item->chunk_size += BRANCH_SIZE;
4443 #if ENABLE_WRONG_METHOD_CHECK
4444 item->chunk_size += WMC_SIZE;
4447 item->chunk_size += CALL_SIZE;
4449 item->chunk_size += BSEARCH_ENTRY_SIZE;
4450 imt_entries [item->check_target_idx]->compare_done = TRUE;
4452 size += item->chunk_size;
4456 size += 4 * count; /* The ARM_ADD_REG_IMM to pop the stack */
4458 start = code = mono_domain_code_reserve (domain, size);
4461 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);
4462 for (i = 0; i < count; ++i) {
4463 MonoIMTCheckItem *item = imt_entries [i];
4464 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);
4469 ARM_PUSH4 (code, ARMREG_R0, ARMREG_R1, ARMREG_IP, ARMREG_PC);
4471 ARM_PUSH2 (code, ARMREG_R0, ARMREG_R1);
4472 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, -4);
4473 vtable_target = code;
4474 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
4476 /* R0 == 0 means we are called from AOT code. In this case, V5 contains the IMT method */
4477 ARM_CMP_REG_IMM8 (code, ARMREG_R0, 0);
4478 ARM_MOV_REG_REG_COND (code, ARMREG_R0, ARMREG_V5, ARMCOND_EQ);
4480 for (i = 0; i < count; ++i) {
4481 MonoIMTCheckItem *item = imt_entries [i];
4482 arminstr_t *imt_method = NULL, *vtable_offset_ins = NULL;
4483 gint32 vtable_offset;
4485 item->code_target = (guint8*)code;
4487 if (item->is_equals) {
4488 if (item->check_target_idx) {
4489 if (!item->compare_done) {
4491 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4492 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
4494 item->jmp_code = (guint8*)code;
4495 ARM_B_COND (code, ARMCOND_NE, 0);
4497 /*Enable the commented code to assert on wrong method*/
4498 #if ENABLE_WRONG_METHOD_CHECK
4500 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4501 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
4502 ARM_B_COND (code, ARMCOND_NE, 1);
4508 vtable_offset = DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]);
4509 if (!arm_is_imm12 (vtable_offset)) {
4511 * We need to branch to a computed address but we don't have
4512 * a free register to store it, since IP must contain the
4513 * vtable address. So we push the two values to the stack, and
4514 * load them both using LDM.
4516 /* Compute target address */
4517 vtable_offset_ins = code;
4518 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4519 ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_IP, ARMREG_R1);
4520 /* Save it to the fourth slot */
4521 ARM_STR_IMM (code, ARMREG_R1, ARMREG_SP, 3 * sizeof (gpointer));
4522 /* Restore registers and branch */
4523 ARM_POP4 (code, ARMREG_R0, ARMREG_R1, ARMREG_IP, ARMREG_PC);
4525 code = arm_emit_value_and_patch_ldr (code, vtable_offset_ins, vtable_offset);
4527 ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
4529 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 2 * sizeof (gpointer));
4530 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, vtable_offset);
4534 code = arm_emit_value_and_patch_ldr (code, imt_method, (guint32)item->key);
4536 /*must emit after unconditional branch*/
4537 if (vtable_target) {
4538 code = arm_emit_value_and_patch_ldr (code, vtable_target, (guint32)vtable);
4539 item->chunk_size += 4;
4540 vtable_target = NULL;
4543 /*We reserve the space for bsearch IMT values after the first entry with an absolute jump*/
4544 constant_pool_starts [i] = code;
4546 code += extra_space;
4550 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4551 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
4553 item->jmp_code = (guint8*)code;
4554 ARM_B_COND (code, ARMCOND_GE, 0);
4559 for (i = 0; i < count; ++i) {
4560 MonoIMTCheckItem *item = imt_entries [i];
4561 if (item->jmp_code) {
4562 if (item->check_target_idx)
4563 arm_patch (item->jmp_code, imt_entries [item->check_target_idx]->code_target);
4565 if (i > 0 && item->is_equals) {
4567 arminstr_t *space_start = constant_pool_starts [i];
4568 for (j = i - 1; j >= 0 && !imt_entries [j]->is_equals; --j) {
4569 space_start = arm_emit_value_and_patch_ldr (space_start, (arminstr_t*)imt_entries [j]->code_target, (guint32)imt_entries [j]->key);
4576 char *buff = g_strdup_printf ("thunk_for_class_%s_%s_entries_%d", vtable->klass->name_space, vtable->klass->name, count);
4577 mono_disassemble_code (NULL, (guint8*)start, size, buff);
4582 g_free (constant_pool_starts);
4584 mono_arch_flush_icache ((guint8*)start, size);
4585 mono_stats.imt_thunks_size += code - start;
4587 g_assert (DISTANCE (start, code) <= size);
4594 mono_arch_context_get_int_reg (MonoContext *ctx, int reg)
4596 if (reg >= 4 && reg <= 11)
4597 return (gpointer)ctx->regs [reg - 4];
4598 else if (reg == ARMREG_IP)
4599 return (gpointer)ctx->regs [8];
4600 else if (reg == ARMREG_LR)
4601 return (gpointer)ctx->regs [9];
4603 g_assert_not_reached ();