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 thumb_supported = 0;
43 * floating point support: on ARM it is a mess, there are at least 3
44 * different setups, each of which binary incompat with the other.
45 * 1) FPA: old and ugly, but unfortunately what current distros use
46 * the double binary format has the two words swapped. 8 double registers.
47 * Implemented usually by kernel emulation.
48 * 2) softfloat: the compiler emulates all the fp ops. Usually uses the
49 * ugly swapped double format (I guess a softfloat-vfp exists, too, though).
50 * 3) VFP: the new and actually sensible and useful FP support. Implemented
51 * in HW or kernel-emulated, requires new tools. I think this is what symbian uses.
53 * The plan is to write the FPA support first. softfloat can be tested in a chroot.
55 int mono_exc_esp_offset = 0;
57 #define arm_is_imm12(v) ((v) > -4096 && (v) < 4096)
58 #define arm_is_imm8(v) ((v) > -256 && (v) < 256)
59 #define arm_is_fpimm8(v) ((v) >= -1020 && (v) <= 1020)
61 #define LDR_MASK ((0xf << ARMCOND_SHIFT) | (3 << 26) | (1 << 22) | (1 << 20) | (15 << 12))
62 #define LDR_PC_VAL ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 26) | (0 << 22) | (1 << 20) | (15 << 12))
63 #define IS_LDR_PC(val) (((val) & LDR_MASK) == LDR_PC_VAL)
65 #define ADD_LR_PC_4 ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 25) | (1 << 23) | (ARMREG_PC << 16) | (ARMREG_LR << 12) | 4)
66 #define MOV_LR_PC ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 24) | (0xa << 20) | (ARMREG_LR << 12) | ARMREG_PC)
70 mono_arch_regname (int reg)
72 static const char * rnames[] = {
73 "arm_r0", "arm_r1", "arm_r2", "arm_r3", "arm_v1",
74 "arm_v2", "arm_v3", "arm_v4", "arm_v5", "arm_v6",
75 "arm_v7", "arm_fp", "arm_ip", "arm_sp", "arm_lr",
78 if (reg >= 0 && reg < 16)
84 mono_arch_fregname (int reg)
86 static const char * rnames[] = {
87 "arm_f0", "arm_f1", "arm_f2", "arm_f3", "arm_f4",
88 "arm_f5", "arm_f6", "arm_f7", "arm_f8", "arm_f9",
89 "arm_f10", "arm_f11", "arm_f12", "arm_f13", "arm_f14",
90 "arm_f15", "arm_f16", "arm_f17", "arm_f18", "arm_f19",
91 "arm_f20", "arm_f21", "arm_f22", "arm_f23", "arm_f24",
92 "arm_f25", "arm_f26", "arm_f27", "arm_f28", "arm_f29",
95 if (reg >= 0 && reg < 32)
101 emit_big_add (guint8 *code, int dreg, int sreg, int imm)
103 int imm8, rot_amount;
104 if ((imm8 = mono_arm_is_rotated_imm8 (imm, &rot_amount)) >= 0) {
105 ARM_ADD_REG_IMM (code, dreg, sreg, imm8, rot_amount);
108 g_assert (dreg != sreg);
109 code = mono_arm_emit_load_imm (code, dreg, imm);
110 ARM_ADD_REG_REG (code, dreg, dreg, sreg);
115 emit_memcpy (guint8 *code, int size, int dreg, int doffset, int sreg, int soffset)
117 /* we can use r0-r3, since this is called only for incoming args on the stack */
118 if (size > sizeof (gpointer) * 4) {
120 code = emit_big_add (code, ARMREG_R0, sreg, soffset);
121 code = emit_big_add (code, ARMREG_R1, dreg, doffset);
122 start_loop = code = mono_arm_emit_load_imm (code, ARMREG_R2, size);
123 ARM_LDR_IMM (code, ARMREG_R3, ARMREG_R0, 0);
124 ARM_STR_IMM (code, ARMREG_R3, ARMREG_R1, 0);
125 ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_R0, 4);
126 ARM_ADD_REG_IMM8 (code, ARMREG_R1, ARMREG_R1, 4);
127 ARM_SUBS_REG_IMM8 (code, ARMREG_R2, ARMREG_R2, 4);
128 ARM_B_COND (code, ARMCOND_NE, 0);
129 arm_patch (code - 4, start_loop);
132 if (arm_is_imm12 (doffset) && arm_is_imm12 (doffset + size) &&
133 arm_is_imm12 (soffset) && arm_is_imm12 (soffset + size)) {
135 ARM_LDR_IMM (code, ARMREG_LR, sreg, soffset);
136 ARM_STR_IMM (code, ARMREG_LR, dreg, doffset);
142 code = emit_big_add (code, ARMREG_R0, sreg, soffset);
143 code = emit_big_add (code, ARMREG_R1, dreg, doffset);
144 doffset = soffset = 0;
146 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_R0, soffset);
147 ARM_STR_IMM (code, ARMREG_LR, ARMREG_R1, doffset);
153 g_assert (size == 0);
158 emit_call_reg (guint8 *code, int reg)
161 ARM_BLX_REG (code, reg);
163 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
167 ARM_MOV_REG_REG (code, ARMREG_PC, reg);
173 emit_call_seq (MonoCompile *cfg, guint8 *code)
175 if (cfg->method->dynamic) {
176 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
178 *(gpointer*)code = NULL;
180 code = emit_call_reg (code, ARMREG_IP);
188 emit_move_return_value (MonoCompile *cfg, MonoInst *ins, guint8 *code)
190 switch (ins->opcode) {
193 case OP_FCALL_MEMBASE:
195 if (ins->dreg != ARM_FPA_F0)
196 ARM_MVFD (code, ins->dreg, ARM_FPA_F0);
197 #elif defined(ARM_FPU_VFP)
198 if (((MonoCallInst*)ins)->signature->ret->type == MONO_TYPE_R4) {
199 ARM_FMSR (code, ins->dreg, ARMREG_R0);
200 ARM_CVTS (code, ins->dreg, ins->dreg);
202 ARM_FMDRR (code, ARMREG_R0, ARMREG_R1, ins->dreg);
212 * mono_arch_get_argument_info:
213 * @csig: a method signature
214 * @param_count: the number of parameters to consider
215 * @arg_info: an array to store the result infos
217 * Gathers information on parameters such as size, alignment and
218 * padding. arg_info should be large enought to hold param_count + 1 entries.
220 * Returns the size of the activation frame.
223 mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJitArgumentInfo *arg_info)
225 int k, frame_size = 0;
226 guint32 size, align, pad;
229 if (MONO_TYPE_ISSTRUCT (csig->ret)) {
230 frame_size += sizeof (gpointer);
234 arg_info [0].offset = offset;
237 frame_size += sizeof (gpointer);
241 arg_info [0].size = frame_size;
243 for (k = 0; k < param_count; k++) {
244 size = mini_type_stack_size_full (NULL, csig->params [k], &align, csig->pinvoke);
246 /* ignore alignment for now */
249 frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1);
250 arg_info [k].pad = pad;
252 arg_info [k + 1].pad = 0;
253 arg_info [k + 1].size = size;
255 arg_info [k + 1].offset = offset;
259 align = MONO_ARCH_FRAME_ALIGNMENT;
260 frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1);
261 arg_info [k].pad = pad;
267 decode_vcall_slot_from_ldr (guint32 ldr, gpointer *regs, int *displacement)
271 reg = (ldr >> 16 ) & 0xf;
272 offset = ldr & 0xfff;
273 if (((ldr >> 23) & 1) == 0) /*U bit, 0 means negative and 1 positive*/
275 /*g_print ("found vcall at r%d + %d for code at %p 0x%x\n", reg, offset, code, *code);*/
278 *displacement = offset;
283 mono_arch_get_vcall_slot (guint8 *code_ptr, gpointer *regs, int *displacement)
285 guint32* code = (guint32*)code_ptr;
287 /* Locate the address of the method-specific trampoline. The call using
288 the vtable slot that took the processing flow to 'arch_create_jit_trampoline'
289 looks something like this:
298 The call sequence could be also:
301 function pointer literal
305 Note that on ARM5+ we can use one instruction instead of the last two.
306 Therefore, we need to locate the 'ldr rA' instruction to know which
307 register was used to hold the method addrs.
310 /* This is the instruction after "ldc pc, xxx", "mov pc, xxx" or "bl xxx" could be either the IMT value or some other instruction*/
313 /* Three possible code sequences can happen here:
317 * ldr pc, [rX - #offset]
323 * ldr pc, [rX - #offset]
325 * direct branch with bl:
329 * direct branch with mov:
333 * We only need to identify interface and virtual calls, the others can be ignored.
336 if (IS_LDR_PC (code [-1]) && code [-2] == ADD_LR_PC_4)
337 return decode_vcall_slot_from_ldr (code [-1], regs, displacement);
339 if (IS_LDR_PC (code [0]) && code [-1] == MOV_LR_PC)
340 return decode_vcall_slot_from_ldr (code [0], regs, displacement);
346 mono_arch_get_vcall_slot_addr (guint8* code, gpointer *regs)
350 vt = mono_arch_get_vcall_slot (code, regs, &displacement);
353 return (gpointer*)((char*)vt + displacement);
356 #define MAX_ARCH_DELEGATE_PARAMS 3
359 get_delegate_invoke_impl (gboolean has_target, gboolean param_count, guint32 *code_size)
361 guint8 *code, *start;
364 start = code = mono_global_codeman_reserve (12);
366 /* Replace the this argument with the target */
367 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, method_ptr));
368 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, target));
369 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
371 g_assert ((code - start) <= 12);
373 mono_arch_flush_icache (start, 12);
377 size = 8 + param_count * 4;
378 start = code = mono_global_codeman_reserve (size);
380 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, method_ptr));
381 /* slide down the arguments */
382 for (i = 0; i < param_count; ++i) {
383 ARM_MOV_REG_REG (code, (ARMREG_R0 + i), (ARMREG_R0 + i + 1));
385 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
387 g_assert ((code - start) <= size);
389 mono_arch_flush_icache (start, size);
393 *code_size = code - start;
399 * mono_arch_get_delegate_invoke_impls:
401 * Return a list of MonoAotTrampInfo structures for the delegate invoke impl
405 mono_arch_get_delegate_invoke_impls (void)
412 code = get_delegate_invoke_impl (TRUE, 0, &code_len);
413 res = g_slist_prepend (res, mono_aot_tramp_info_create (g_strdup ("delegate_invoke_impl_has_target"), code, code_len));
415 for (i = 0; i < MAX_ARCH_DELEGATE_PARAMS; ++i) {
416 code = get_delegate_invoke_impl (FALSE, i, &code_len);
417 res = g_slist_prepend (res, mono_aot_tramp_info_create (g_strdup_printf ("delegate_invoke_impl_target_%d", i), code, code_len));
424 mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target)
426 guint8 *code, *start;
428 /* FIXME: Support more cases */
429 if (MONO_TYPE_ISSTRUCT (sig->ret))
433 static guint8* cached = NULL;
434 mono_mini_arch_lock ();
436 mono_mini_arch_unlock ();
441 start = mono_aot_get_named_code ("delegate_invoke_impl_has_target");
443 start = get_delegate_invoke_impl (TRUE, 0, NULL);
445 mono_mini_arch_unlock ();
448 static guint8* cache [MAX_ARCH_DELEGATE_PARAMS + 1] = {NULL};
451 if (sig->param_count > MAX_ARCH_DELEGATE_PARAMS)
453 for (i = 0; i < sig->param_count; ++i)
454 if (!mono_is_regsize_var (sig->params [i]))
457 mono_mini_arch_lock ();
458 code = cache [sig->param_count];
460 mono_mini_arch_unlock ();
465 char *name = g_strdup_printf ("delegate_invoke_impl_target_%d", sig->param_count);
466 start = mono_aot_get_named_code (name);
469 start = get_delegate_invoke_impl (FALSE, sig->param_count, NULL);
471 cache [sig->param_count] = start;
472 mono_mini_arch_unlock ();
480 mono_arch_get_this_arg_from_call (MonoGenericSharingContext *gsctx, MonoMethodSignature *sig, gssize *regs, guint8 *code)
482 /* FIXME: handle returning a struct */
483 if (MONO_TYPE_ISSTRUCT (sig->ret))
484 return (gpointer)regs [ARMREG_R1];
485 return (gpointer)regs [ARMREG_R0];
489 * Initialize the cpu to execute managed code.
492 mono_arch_cpu_init (void)
497 * Initialize architecture specific code.
500 mono_arch_init (void)
502 InitializeCriticalSection (&mini_arch_mutex);
506 * Cleanup architecture specific code.
509 mono_arch_cleanup (void)
514 * This function returns the optimizations supported on this cpu.
517 mono_arch_cpu_optimizazions (guint32 *exclude_mask)
521 thumb_supported = TRUE;
526 FILE *file = fopen ("/proc/cpuinfo", "r");
528 while ((line = fgets (buf, 512, file))) {
529 if (strncmp (line, "Processor", 9) == 0) {
530 char *ver = strstr (line, "(v");
531 if (ver && (ver [2] == '5' || ver [2] == '6' || ver [2] == '7')) {
536 if (strncmp (line, "Features", 8) == 0) {
537 char *th = strstr (line, "thumb");
539 thumb_supported = TRUE;
547 /*printf ("features: v5: %d, thumb: %d\n", v5_supported, thumb_supported);*/
551 /* no arm-specific optimizations yet */
557 is_regsize_var (MonoType *t) {
560 t = mini_type_get_underlying_type (NULL, t);
567 case MONO_TYPE_FNPTR:
569 case MONO_TYPE_OBJECT:
570 case MONO_TYPE_STRING:
571 case MONO_TYPE_CLASS:
572 case MONO_TYPE_SZARRAY:
573 case MONO_TYPE_ARRAY:
575 case MONO_TYPE_GENERICINST:
576 if (!mono_type_generic_inst_is_valuetype (t))
579 case MONO_TYPE_VALUETYPE:
586 mono_arch_get_allocatable_int_vars (MonoCompile *cfg)
591 for (i = 0; i < cfg->num_varinfo; i++) {
592 MonoInst *ins = cfg->varinfo [i];
593 MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
596 if (vmv->range.first_use.abs_pos >= vmv->range.last_use.abs_pos)
599 if (ins->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT) || (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG))
602 /* we can only allocate 32 bit values */
603 if (is_regsize_var (ins->inst_vtype)) {
604 g_assert (MONO_VARINFO (cfg, i)->reg == -1);
605 g_assert (i == vmv->idx);
606 vars = mono_varlist_insert_sorted (cfg, vars, vmv, FALSE);
613 #define USE_EXTRA_TEMPS 0
616 mono_arch_get_global_int_regs (MonoCompile *cfg)
619 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V1));
620 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V2));
621 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V3));
622 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V4));
623 if (!(cfg->compile_aot || cfg->uses_rgctx_reg))
624 /* V5 is reserved for passing the vtable/rgctx/IMT method */
625 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V5));
626 /*regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V6));*/
627 /*regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V7));*/
633 * mono_arch_regalloc_cost:
635 * Return the cost, in number of memory references, of the action of
636 * allocating the variable VMV into a register during global register
640 mono_arch_regalloc_cost (MonoCompile *cfg, MonoMethodVar *vmv)
646 #ifndef __GNUC_PREREQ
647 #define __GNUC_PREREQ(maj, min) (0)
651 mono_arch_flush_icache (guint8 *code, gint size)
654 sys_icache_invalidate (code, size);
655 #elif __GNUC_PREREQ(4, 1)
656 __clear_cache (code, code + size);
657 #elif defined(PLATFORM_ANDROID)
658 const int syscall = 0xf0002;
666 : "r" (code), "r" (code + size), "r" (syscall)
670 __asm __volatile ("mov r0, %0\n"
673 "swi 0x9f0002 @ sys_cacheflush"
675 : "r" (code), "r" (code + size), "r" (0)
676 : "r0", "r1", "r3" );
691 guint16 vtsize; /* in param area */
693 guint8 regtype : 4; /* 0 general, 1 basereg, 2 floating point register, see RegType* */
694 guint8 size : 4; /* 1, 2, 4, 8, or regs used by RegTypeStructByVal */
709 add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple)
712 if (*gr > ARMREG_R3) {
713 ainfo->offset = *stack_size;
714 ainfo->reg = ARMREG_SP; /* in the caller */
715 ainfo->regtype = RegTypeBase;
726 /* first word in r3 and the second on the stack */
727 ainfo->offset = *stack_size;
728 ainfo->reg = ARMREG_SP; /* in the caller */
729 ainfo->regtype = RegTypeBaseGen;
731 } else if (*gr >= ARMREG_R3) {
736 ainfo->offset = *stack_size;
737 ainfo->reg = ARMREG_SP; /* in the caller */
738 ainfo->regtype = RegTypeBase;
753 calculate_sizes (MonoMethodSignature *sig, gboolean is_pinvoke)
756 int n = sig->hasthis + sig->param_count;
757 MonoType *simpletype;
758 guint32 stack_size = 0;
759 CallInfo *cinfo = g_malloc0 (sizeof (CallInfo) + sizeof (ArgInfo) * n);
763 /* FIXME: handle returning a struct */
764 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
765 add_general (&gr, &stack_size, &cinfo->ret, TRUE);
766 cinfo->struct_ret = ARMREG_R0;
771 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
774 DEBUG(printf("params: %d\n", sig->param_count));
775 for (i = 0; i < sig->param_count; ++i) {
776 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
777 /* Prevent implicit arguments and sig_cookie from
778 being passed in registers */
780 /* Emit the signature cookie just before the implicit arguments */
781 add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE);
783 DEBUG(printf("param %d: ", i));
784 if (sig->params [i]->byref) {
785 DEBUG(printf("byref\n"));
786 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
790 simpletype = mini_type_get_underlying_type (NULL, sig->params [i]);
791 switch (simpletype->type) {
792 case MONO_TYPE_BOOLEAN:
795 cinfo->args [n].size = 1;
796 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
802 cinfo->args [n].size = 2;
803 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
808 cinfo->args [n].size = 4;
809 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
815 case MONO_TYPE_FNPTR:
816 case MONO_TYPE_CLASS:
817 case MONO_TYPE_OBJECT:
818 case MONO_TYPE_STRING:
819 case MONO_TYPE_SZARRAY:
820 case MONO_TYPE_ARRAY:
822 cinfo->args [n].size = sizeof (gpointer);
823 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
826 case MONO_TYPE_GENERICINST:
827 if (!mono_type_generic_inst_is_valuetype (sig->params [i])) {
828 cinfo->args [n].size = sizeof (gpointer);
829 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
834 case MONO_TYPE_TYPEDBYREF:
835 case MONO_TYPE_VALUETYPE: {
840 if (simpletype->type == MONO_TYPE_TYPEDBYREF) {
841 size = sizeof (MonoTypedRef);
843 MonoClass *klass = mono_class_from_mono_type (sig->params [i]);
845 size = mono_class_native_size (klass, NULL);
847 size = mono_class_value_size (klass, NULL);
849 DEBUG(printf ("load %d bytes struct\n",
850 mono_class_native_size (sig->params [i]->data.klass, NULL)));
853 align_size += (sizeof (gpointer) - 1);
854 align_size &= ~(sizeof (gpointer) - 1);
855 nwords = (align_size + sizeof (gpointer) -1 ) / sizeof (gpointer);
856 cinfo->args [n].regtype = RegTypeStructByVal;
857 /* FIXME: align gr and stack_size if needed */
858 if (gr > ARMREG_R3) {
859 cinfo->args [n].size = 0;
860 cinfo->args [n].vtsize = nwords;
862 int rest = ARMREG_R3 - gr + 1;
863 int n_in_regs = rest >= nwords? nwords: rest;
864 cinfo->args [n].size = n_in_regs;
865 cinfo->args [n].vtsize = nwords - n_in_regs;
866 cinfo->args [n].reg = gr;
869 cinfo->args [n].offset = stack_size;
870 /*g_print ("offset for arg %d at %d\n", n, stack_size);*/
871 stack_size += nwords * sizeof (gpointer);
878 cinfo->args [n].size = 8;
879 add_general (&gr, &stack_size, cinfo->args + n, FALSE);
883 g_error ("Can't trampoline 0x%x", sig->params [i]->type);
888 simpletype = mini_type_get_underlying_type (NULL, sig->ret);
889 switch (simpletype->type) {
890 case MONO_TYPE_BOOLEAN:
901 case MONO_TYPE_FNPTR:
902 case MONO_TYPE_CLASS:
903 case MONO_TYPE_OBJECT:
904 case MONO_TYPE_SZARRAY:
905 case MONO_TYPE_ARRAY:
906 case MONO_TYPE_STRING:
907 cinfo->ret.reg = ARMREG_R0;
911 cinfo->ret.reg = ARMREG_R0;
915 cinfo->ret.reg = ARMREG_R0;
916 /* FIXME: cinfo->ret.reg = ???;
917 cinfo->ret.regtype = RegTypeFP;*/
919 case MONO_TYPE_GENERICINST:
920 if (!mono_type_generic_inst_is_valuetype (sig->ret)) {
921 cinfo->ret.reg = ARMREG_R0;
925 case MONO_TYPE_VALUETYPE:
927 case MONO_TYPE_TYPEDBYREF:
931 g_error ("Can't handle as return value 0x%x", sig->ret->type);
935 /* align stack size to 8 */
936 DEBUG (printf (" stack size: %d (%d)\n", (stack_size + 15) & ~15, stack_size));
937 stack_size = (stack_size + 7) & ~7;
939 cinfo->stack_usage = stack_size;
945 * Set var information according to the calling convention. arm version.
946 * The locals var stuff should most likely be split in another method.
949 mono_arch_allocate_vars (MonoCompile *cfg)
951 MonoMethodSignature *sig;
952 MonoMethodHeader *header;
954 int i, offset, size, align, curinst;
955 int frame_reg = ARMREG_FP;
957 /* FIXME: this will change when we use FP as gcc does */
958 cfg->flags |= MONO_CFG_HAS_SPILLUP;
960 /* allow room for the vararg method args: void* and long/double */
961 if (mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method))
962 cfg->param_area = MAX (cfg->param_area, sizeof (gpointer)*8);
964 header = mono_method_get_header (cfg->method);
967 * We use the frame register also for any method that has
968 * exception clauses. This way, when the handlers are called,
969 * the code will reference local variables using the frame reg instead of
970 * the stack pointer: if we had to restore the stack pointer, we'd
971 * corrupt the method frames that are already on the stack (since
972 * filters get called before stack unwinding happens) when the filter
973 * code would call any method (this also applies to finally etc.).
975 if ((cfg->flags & MONO_CFG_HAS_ALLOCA) || header->num_clauses)
976 frame_reg = ARMREG_FP;
977 cfg->frame_reg = frame_reg;
978 if (frame_reg != ARMREG_SP) {
979 cfg->used_int_regs |= 1 << frame_reg;
982 if (!cfg->compile_aot || cfg->uses_rgctx_reg)
983 /* V5 is reserved for passing the vtable/rgctx/IMT method */
984 cfg->used_int_regs |= (1 << ARMREG_V5);
986 sig = mono_method_signature (cfg->method);
990 if (!MONO_TYPE_ISSTRUCT (sig->ret)) {
991 /* FIXME: handle long and FP values */
992 switch (mini_type_get_underlying_type (NULL, sig->ret)->type) {
996 cfg->ret->opcode = OP_REGVAR;
997 cfg->ret->inst_c0 = ARMREG_R0;
1001 /* local vars are at a positive offset from the stack pointer */
1003 * also note that if the function uses alloca, we use FP
1004 * to point at the local variables.
1006 offset = 0; /* linkage area */
1007 /* align the offset to 16 bytes: not sure this is needed here */
1009 //offset &= ~(8 - 1);
1011 /* add parameter area size for called functions */
1012 offset += cfg->param_area;
1015 if (cfg->flags & MONO_CFG_HAS_FPOUT)
1018 /* allow room to save the return value */
1019 if (mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method))
1022 /* the MonoLMF structure is stored just below the stack pointer */
1024 if (sig->call_convention == MONO_CALL_VARARG) {
1025 cfg->sig_cookie = 0;
1028 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
1029 inst = cfg->vret_addr;
1030 offset += sizeof(gpointer) - 1;
1031 offset &= ~(sizeof(gpointer) - 1);
1032 inst->inst_offset = offset;
1033 inst->opcode = OP_REGOFFSET;
1034 inst->inst_basereg = frame_reg;
1035 if (G_UNLIKELY (cfg->verbose_level > 1)) {
1036 printf ("vret_addr =");
1037 mono_print_ins (cfg->vret_addr);
1039 offset += sizeof(gpointer);
1040 if (sig->call_convention == MONO_CALL_VARARG)
1041 cfg->sig_cookie += sizeof (gpointer);
1044 curinst = cfg->locals_start;
1045 for (i = curinst; i < cfg->num_varinfo; ++i) {
1046 inst = cfg->varinfo [i];
1047 if ((inst->flags & MONO_INST_IS_DEAD) || inst->opcode == OP_REGVAR)
1050 /* inst->backend.is_pinvoke indicates native sized value types, this is used by the
1051 * pinvoke wrappers when they call functions returning structure */
1052 if (inst->backend.is_pinvoke && MONO_TYPE_ISSTRUCT (inst->inst_vtype) && inst->inst_vtype->type != MONO_TYPE_TYPEDBYREF) {
1054 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), &ualign);
1058 size = mono_type_size (inst->inst_vtype, &align);
1060 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
1061 * since it loads/stores misaligned words, which don't do the right thing.
1063 if (align < 4 && size >= 4)
1065 offset += align - 1;
1066 offset &= ~(align - 1);
1067 inst->inst_offset = offset;
1068 inst->opcode = OP_REGOFFSET;
1069 inst->inst_basereg = frame_reg;
1071 //g_print ("allocating local %d to %d\n", i, inst->inst_offset);
1076 inst = cfg->args [curinst];
1077 if (inst->opcode != OP_REGVAR) {
1078 inst->opcode = OP_REGOFFSET;
1079 inst->inst_basereg = frame_reg;
1080 offset += sizeof (gpointer) - 1;
1081 offset &= ~(sizeof (gpointer) - 1);
1082 inst->inst_offset = offset;
1083 offset += sizeof (gpointer);
1084 if (sig->call_convention == MONO_CALL_VARARG)
1085 cfg->sig_cookie += sizeof (gpointer);
1090 for (i = 0; i < sig->param_count; ++i) {
1091 inst = cfg->args [curinst];
1092 if (inst->opcode != OP_REGVAR) {
1093 inst->opcode = OP_REGOFFSET;
1094 inst->inst_basereg = frame_reg;
1095 size = mono_type_size (sig->params [i], &align);
1096 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
1097 * since it loads/stores misaligned words, which don't do the right thing.
1099 if (align < 4 && size >= 4)
1101 offset += align - 1;
1102 offset &= ~(align - 1);
1103 inst->inst_offset = offset;
1105 if ((sig->call_convention == MONO_CALL_VARARG) && (i < sig->sentinelpos))
1106 cfg->sig_cookie += size;
1111 /* align the offset to 8 bytes */
1116 cfg->stack_offset = offset;
1120 mono_arch_create_vars (MonoCompile *cfg)
1122 MonoMethodSignature *sig;
1124 sig = mono_method_signature (cfg->method);
1126 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
1127 cfg->vret_addr = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_ARG);
1128 if (G_UNLIKELY (cfg->verbose_level > 1)) {
1129 printf ("vret_addr = ");
1130 mono_print_ins (cfg->vret_addr);
1136 mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
1139 MonoMethodSignature *sig;
1143 sig = call->signature;
1144 n = sig->param_count + sig->hasthis;
1146 cinfo = calculate_sizes (sig, sig->pinvoke);
1148 for (i = 0; i < n; ++i) {
1149 ArgInfo *ainfo = cinfo->args + i;
1152 if (i >= sig->hasthis)
1153 t = sig->params [i - sig->hasthis];
1155 t = &mono_defaults.int_class->byval_arg;
1156 t = mini_type_get_underlying_type (NULL, t);
1158 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
1163 in = call->args [i];
1165 switch (ainfo->regtype) {
1166 case RegTypeGeneral:
1167 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1168 MONO_INST_NEW (cfg, ins, OP_MOVE);
1169 ins->dreg = mono_alloc_ireg (cfg);
1170 ins->sreg1 = in->dreg + 1;
1171 MONO_ADD_INS (cfg->cbb, ins);
1172 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1174 MONO_INST_NEW (cfg, ins, OP_MOVE);
1175 ins->dreg = mono_alloc_ireg (cfg);
1176 ins->sreg1 = in->dreg + 2;
1177 MONO_ADD_INS (cfg->cbb, ins);
1178 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg + 1, FALSE);
1179 } else if (!t->byref && ((t->type == MONO_TYPE_R8) || (t->type == MONO_TYPE_R4))) {
1180 #ifndef MONO_ARCH_SOFT_FLOAT
1184 if (ainfo->size == 4) {
1185 #ifdef MONO_ARCH_SOFT_FLOAT
1186 /* mono_emit_call_args () have already done the r8->r4 conversion */
1187 /* The converted value is in an int vreg */
1188 MONO_INST_NEW (cfg, ins, OP_MOVE);
1189 ins->dreg = mono_alloc_ireg (cfg);
1190 ins->sreg1 = in->dreg;
1191 MONO_ADD_INS (cfg->cbb, ins);
1192 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1194 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1195 creg = mono_alloc_ireg (cfg);
1196 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1197 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg, FALSE);
1200 #ifdef MONO_ARCH_SOFT_FLOAT
1201 MONO_INST_NEW (cfg, ins, OP_FGETLOW32);
1202 ins->dreg = mono_alloc_ireg (cfg);
1203 ins->sreg1 = in->dreg;
1204 MONO_ADD_INS (cfg->cbb, ins);
1205 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1207 MONO_INST_NEW (cfg, ins, OP_FGETHIGH32);
1208 ins->dreg = mono_alloc_ireg (cfg);
1209 ins->sreg1 = in->dreg;
1210 MONO_ADD_INS (cfg->cbb, ins);
1211 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg + 1, FALSE);
1213 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1214 creg = mono_alloc_ireg (cfg);
1215 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1216 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg, FALSE);
1217 creg = mono_alloc_ireg (cfg);
1218 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8 + 4));
1219 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg + 1, FALSE);
1222 cfg->flags |= MONO_CFG_HAS_FPOUT;
1224 MONO_INST_NEW (cfg, ins, OP_MOVE);
1225 ins->dreg = mono_alloc_ireg (cfg);
1226 ins->sreg1 = in->dreg;
1227 MONO_ADD_INS (cfg->cbb, ins);
1229 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1232 case RegTypeStructByAddr:
1235 /* FIXME: where si the data allocated? */
1236 arg->backend.reg3 = ainfo->reg;
1237 call->used_iregs |= 1 << ainfo->reg;
1238 g_assert_not_reached ();
1241 case RegTypeStructByVal:
1242 MONO_INST_NEW (cfg, ins, OP_OUTARG_VT);
1243 ins->opcode = OP_OUTARG_VT;
1244 ins->sreg1 = in->dreg;
1245 ins->klass = in->klass;
1246 ins->inst_p0 = call;
1247 ins->inst_p1 = mono_mempool_alloc (cfg->mempool, sizeof (ArgInfo));
1248 memcpy (ins->inst_p1, ainfo, sizeof (ArgInfo));
1249 MONO_ADD_INS (cfg->cbb, ins);
1252 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1253 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1254 } else if (!t->byref && ((t->type == MONO_TYPE_R4) || (t->type == MONO_TYPE_R8))) {
1255 if (t->type == MONO_TYPE_R8) {
1256 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1258 #ifdef MONO_ARCH_SOFT_FLOAT
1259 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1261 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1265 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1268 case RegTypeBaseGen:
1269 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1270 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);
1271 MONO_INST_NEW (cfg, ins, OP_MOVE);
1272 ins->dreg = mono_alloc_ireg (cfg);
1273 ins->sreg1 = G_BYTE_ORDER == G_BIG_ENDIAN ? in->dreg + 2 : in->dreg + 1;
1274 MONO_ADD_INS (cfg->cbb, ins);
1275 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ARMREG_R3, FALSE);
1276 } else if (!t->byref && (t->type == MONO_TYPE_R8)) {
1279 #ifdef MONO_ARCH_SOFT_FLOAT
1280 g_assert_not_reached ();
1283 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1284 creg = mono_alloc_ireg (cfg);
1285 mono_call_inst_add_outarg_reg (cfg, call, creg, ARMREG_R3, FALSE);
1286 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1287 creg = mono_alloc_ireg (cfg);
1288 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 4));
1289 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, creg);
1290 cfg->flags |= MONO_CFG_HAS_FPOUT;
1292 g_assert_not_reached ();
1299 arg->backend.reg3 = ainfo->reg;
1300 /* FP args are passed in int regs */
1301 call->used_iregs |= 1 << ainfo->reg;
1302 if (ainfo->size == 8) {
1303 arg->opcode = OP_OUTARG_R8;
1304 call->used_iregs |= 1 << (ainfo->reg + 1);
1306 arg->opcode = OP_OUTARG_R4;
1309 cfg->flags |= MONO_CFG_HAS_FPOUT;
1313 g_assert_not_reached ();
1317 if (sig->ret && MONO_TYPE_ISSTRUCT (sig->ret)) {
1320 MONO_INST_NEW (cfg, vtarg, OP_MOVE);
1321 vtarg->sreg1 = call->vret_var->dreg;
1322 vtarg->dreg = mono_alloc_preg (cfg);
1323 MONO_ADD_INS (cfg->cbb, vtarg);
1325 mono_call_inst_add_outarg_reg (cfg, call, vtarg->dreg, cinfo->ret.reg, FALSE);
1328 call->stack_usage = cinfo->stack_usage;
1334 mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src)
1336 MonoCallInst *call = (MonoCallInst*)ins->inst_p0;
1337 ArgInfo *ainfo = ins->inst_p1;
1338 int ovf_size = ainfo->vtsize;
1339 int doffset = ainfo->offset;
1340 int i, soffset, dreg;
1343 for (i = 0; i < ainfo->size; ++i) {
1344 dreg = mono_alloc_ireg (cfg);
1345 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, src->dreg, soffset);
1346 mono_call_inst_add_outarg_reg (cfg, call, dreg, ainfo->reg + i, FALSE);
1347 soffset += sizeof (gpointer);
1349 //g_print ("vt size: %d at R%d + %d\n", doffset, vt->inst_basereg, vt->inst_offset);
1351 mini_emit_memcpy (cfg, ARMREG_SP, doffset, src->dreg, soffset, ovf_size * sizeof (gpointer), 0);
1355 mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val)
1357 MonoType *ret = mini_type_get_underlying_type (cfg->generic_sharing_context, mono_method_signature (method)->ret);
1360 if (ret->type == MONO_TYPE_I8 || ret->type == MONO_TYPE_U8) {
1363 MONO_INST_NEW (cfg, ins, OP_SETLRET);
1364 ins->sreg1 = val->dreg + 1;
1365 ins->sreg2 = val->dreg + 2;
1366 MONO_ADD_INS (cfg->cbb, ins);
1369 #ifdef MONO_ARCH_SOFT_FLOAT
1370 if (ret->type == MONO_TYPE_R8) {
1373 MONO_INST_NEW (cfg, ins, OP_SETFRET);
1374 ins->dreg = cfg->ret->dreg;
1375 ins->sreg1 = val->dreg;
1376 MONO_ADD_INS (cfg->cbb, ins);
1379 if (ret->type == MONO_TYPE_R4) {
1380 /* Already converted to an int in method_to_ir () */
1381 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
1384 #elif defined(ARM_FPU_VFP)
1385 if (ret->type == MONO_TYPE_R8 || ret->type == MONO_TYPE_R4) {
1388 MONO_INST_NEW (cfg, ins, OP_SETFRET);
1389 ins->dreg = cfg->ret->dreg;
1390 ins->sreg1 = val->dreg;
1391 MONO_ADD_INS (cfg->cbb, ins);
1395 if (ret->type == MONO_TYPE_R4 || ret->type == MONO_TYPE_R8) {
1396 MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, cfg->ret->dreg, val->dreg);
1403 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
1407 mono_arch_is_inst_imm (gint64 imm)
1413 * Allow tracing to work with this interface (with an optional argument)
1417 mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
1421 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1422 ARM_MOV_REG_IMM8 (code, ARMREG_R1, 0); /* NULL ebp for now */
1423 code = mono_arm_emit_load_imm (code, ARMREG_R2, (guint32)func);
1424 code = emit_call_reg (code, ARMREG_R2);
1437 mono_arch_instrument_epilog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
1440 int save_mode = SAVE_NONE;
1442 MonoMethod *method = cfg->method;
1443 int rtype = mini_type_get_underlying_type (cfg->generic_sharing_context, mono_method_signature (method)->ret)->type;
1444 int save_offset = cfg->param_area;
1448 offset = code - cfg->native_code;
1449 /* we need about 16 instructions */
1450 if (offset > (cfg->code_size - 16 * 4)) {
1451 cfg->code_size *= 2;
1452 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
1453 code = cfg->native_code + offset;
1456 case MONO_TYPE_VOID:
1457 /* special case string .ctor icall */
1458 if (strcmp (".ctor", method->name) && method->klass == mono_defaults.string_class)
1459 save_mode = SAVE_ONE;
1461 save_mode = SAVE_NONE;
1465 save_mode = SAVE_TWO;
1469 save_mode = SAVE_FP;
1471 case MONO_TYPE_VALUETYPE:
1472 save_mode = SAVE_STRUCT;
1475 save_mode = SAVE_ONE;
1479 switch (save_mode) {
1481 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1482 ARM_STR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1483 if (enable_arguments) {
1484 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_R1);
1485 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1489 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1490 if (enable_arguments) {
1491 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1495 /* FIXME: what reg? */
1496 if (enable_arguments) {
1497 /* FIXME: what reg? */
1501 if (enable_arguments) {
1502 /* FIXME: get the actual address */
1503 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1511 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1512 code = mono_arm_emit_load_imm (code, ARMREG_IP, (guint32)func);
1513 code = emit_call_reg (code, ARMREG_IP);
1515 switch (save_mode) {
1517 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1518 ARM_LDR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1521 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1535 * The immediate field for cond branches is big enough for all reasonable methods
1537 #define EMIT_COND_BRANCH_FLAGS(ins,condcode) \
1538 if (ins->flags & MONO_INST_BRLABEL) { \
1539 if (0 && ins->inst_i0->inst_c0) { \
1540 ARM_B_COND (code, (condcode), (code - cfg->native_code + ins->inst_i0->inst_c0) & 0xffffff); \
1542 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_LABEL, ins->inst_i0); \
1543 ARM_B_COND (code, (condcode), 0); \
1546 if (0 && ins->inst_true_bb->native_offset) { \
1547 ARM_B_COND (code, (condcode), (code - cfg->native_code + ins->inst_true_bb->native_offset) & 0xffffff); \
1549 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_true_bb); \
1550 ARM_B_COND (code, (condcode), 0); \
1554 #define EMIT_COND_BRANCH(ins,cond) EMIT_COND_BRANCH_FLAGS(ins, branch_cc_table [(cond)])
1556 /* emit an exception if condition is fail
1558 * We assign the extra code used to throw the implicit exceptions
1559 * to cfg->bb_exit as far as the big branch handling is concerned
1561 #define EMIT_COND_SYSTEM_EXCEPTION_FLAGS(condcode,exc_name) \
1563 mono_add_patch_info (cfg, code - cfg->native_code, \
1564 MONO_PATCH_INFO_EXC, exc_name); \
1565 ARM_BL_COND (code, (condcode), 0); \
1568 #define EMIT_COND_SYSTEM_EXCEPTION(cond,exc_name) EMIT_COND_SYSTEM_EXCEPTION_FLAGS(branch_cc_table [(cond)], (exc_name))
1571 mono_arch_peephole_pass_1 (MonoCompile *cfg, MonoBasicBlock *bb)
1576 mono_arch_peephole_pass_2 (MonoCompile *cfg, MonoBasicBlock *bb)
1578 MonoInst *ins, *n, *last_ins = NULL;
1580 MONO_BB_FOR_EACH_INS_SAFE (bb, n, ins) {
1581 switch (ins->opcode) {
1584 /* Already done by an arch-independent pass */
1586 case OP_LOAD_MEMBASE:
1587 case OP_LOADI4_MEMBASE:
1589 * OP_STORE_MEMBASE_REG reg, offset(basereg)
1590 * OP_LOAD_MEMBASE offset(basereg), reg
1592 if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG
1593 || last_ins->opcode == OP_STORE_MEMBASE_REG) &&
1594 ins->inst_basereg == last_ins->inst_destbasereg &&
1595 ins->inst_offset == last_ins->inst_offset) {
1596 if (ins->dreg == last_ins->sreg1) {
1597 MONO_DELETE_INS (bb, ins);
1600 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1601 ins->opcode = OP_MOVE;
1602 ins->sreg1 = last_ins->sreg1;
1606 * Note: reg1 must be different from the basereg in the second load
1607 * OP_LOAD_MEMBASE offset(basereg), reg1
1608 * OP_LOAD_MEMBASE offset(basereg), reg2
1610 * OP_LOAD_MEMBASE offset(basereg), reg1
1611 * OP_MOVE reg1, reg2
1613 } if (last_ins && (last_ins->opcode == OP_LOADI4_MEMBASE
1614 || last_ins->opcode == OP_LOAD_MEMBASE) &&
1615 ins->inst_basereg != last_ins->dreg &&
1616 ins->inst_basereg == last_ins->inst_basereg &&
1617 ins->inst_offset == last_ins->inst_offset) {
1619 if (ins->dreg == last_ins->dreg) {
1620 MONO_DELETE_INS (bb, ins);
1623 ins->opcode = OP_MOVE;
1624 ins->sreg1 = last_ins->dreg;
1627 //g_assert_not_reached ();
1631 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1632 * OP_LOAD_MEMBASE offset(basereg), reg
1634 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1635 * OP_ICONST reg, imm
1637 } else if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_IMM
1638 || last_ins->opcode == OP_STORE_MEMBASE_IMM) &&
1639 ins->inst_basereg == last_ins->inst_destbasereg &&
1640 ins->inst_offset == last_ins->inst_offset) {
1641 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1642 ins->opcode = OP_ICONST;
1643 ins->inst_c0 = last_ins->inst_imm;
1644 g_assert_not_reached (); // check this rule
1648 case OP_LOADU1_MEMBASE:
1649 case OP_LOADI1_MEMBASE:
1650 if (last_ins && (last_ins->opcode == OP_STOREI1_MEMBASE_REG) &&
1651 ins->inst_basereg == last_ins->inst_destbasereg &&
1652 ins->inst_offset == last_ins->inst_offset) {
1653 ins->opcode = (ins->opcode == OP_LOADI1_MEMBASE) ? OP_ICONV_TO_I1 : OP_ICONV_TO_U1;
1654 ins->sreg1 = last_ins->sreg1;
1657 case OP_LOADU2_MEMBASE:
1658 case OP_LOADI2_MEMBASE:
1659 if (last_ins && (last_ins->opcode == OP_STOREI2_MEMBASE_REG) &&
1660 ins->inst_basereg == last_ins->inst_destbasereg &&
1661 ins->inst_offset == last_ins->inst_offset) {
1662 ins->opcode = (ins->opcode == OP_LOADI2_MEMBASE) ? OP_ICONV_TO_I2 : OP_ICONV_TO_U2;
1663 ins->sreg1 = last_ins->sreg1;
1667 ins->opcode = OP_MOVE;
1671 if (ins->dreg == ins->sreg1) {
1672 MONO_DELETE_INS (bb, ins);
1676 * OP_MOVE sreg, dreg
1677 * OP_MOVE dreg, sreg
1679 if (last_ins && last_ins->opcode == OP_MOVE &&
1680 ins->sreg1 == last_ins->dreg &&
1681 ins->dreg == last_ins->sreg1) {
1682 MONO_DELETE_INS (bb, ins);
1690 bb->last_ins = last_ins;
1694 * the branch_cc_table should maintain the order of these
1708 branch_cc_table [] = {
1722 #define NEW_INS(cfg,dest,op) do { \
1723 MONO_INST_NEW ((cfg), (dest), (op)); \
1724 mono_bblock_insert_before_ins (bb, ins, (dest)); \
1728 map_to_reg_reg_op (int op)
1737 case OP_COMPARE_IMM:
1739 case OP_ICOMPARE_IMM:
1753 case OP_LOAD_MEMBASE:
1754 return OP_LOAD_MEMINDEX;
1755 case OP_LOADI4_MEMBASE:
1756 return OP_LOADI4_MEMINDEX;
1757 case OP_LOADU4_MEMBASE:
1758 return OP_LOADU4_MEMINDEX;
1759 case OP_LOADU1_MEMBASE:
1760 return OP_LOADU1_MEMINDEX;
1761 case OP_LOADI2_MEMBASE:
1762 return OP_LOADI2_MEMINDEX;
1763 case OP_LOADU2_MEMBASE:
1764 return OP_LOADU2_MEMINDEX;
1765 case OP_LOADI1_MEMBASE:
1766 return OP_LOADI1_MEMINDEX;
1767 case OP_STOREI1_MEMBASE_REG:
1768 return OP_STOREI1_MEMINDEX;
1769 case OP_STOREI2_MEMBASE_REG:
1770 return OP_STOREI2_MEMINDEX;
1771 case OP_STOREI4_MEMBASE_REG:
1772 return OP_STOREI4_MEMINDEX;
1773 case OP_STORE_MEMBASE_REG:
1774 return OP_STORE_MEMINDEX;
1775 case OP_STORER4_MEMBASE_REG:
1776 return OP_STORER4_MEMINDEX;
1777 case OP_STORER8_MEMBASE_REG:
1778 return OP_STORER8_MEMINDEX;
1779 case OP_STORE_MEMBASE_IMM:
1780 return OP_STORE_MEMBASE_REG;
1781 case OP_STOREI1_MEMBASE_IMM:
1782 return OP_STOREI1_MEMBASE_REG;
1783 case OP_STOREI2_MEMBASE_IMM:
1784 return OP_STOREI2_MEMBASE_REG;
1785 case OP_STOREI4_MEMBASE_IMM:
1786 return OP_STOREI4_MEMBASE_REG;
1788 g_assert_not_reached ();
1792 * Remove from the instruction list the instructions that can't be
1793 * represented with very simple instructions with no register
1797 mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb)
1799 MonoInst *ins, *temp, *last_ins = NULL;
1800 int rot_amount, imm8, low_imm;
1802 MONO_BB_FOR_EACH_INS (bb, ins) {
1804 switch (ins->opcode) {
1808 case OP_COMPARE_IMM:
1809 case OP_ICOMPARE_IMM:
1823 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount)) < 0) {
1824 NEW_INS (cfg, temp, OP_ICONST);
1825 temp->inst_c0 = ins->inst_imm;
1826 temp->dreg = mono_alloc_ireg (cfg);
1827 ins->sreg2 = temp->dreg;
1828 ins->opcode = mono_op_imm_to_op (ins->opcode);
1830 if (ins->opcode == OP_SBB || ins->opcode == OP_ISBB || ins->opcode == OP_SUBCC)
1836 if (ins->inst_imm == 1) {
1837 ins->opcode = OP_MOVE;
1840 if (ins->inst_imm == 0) {
1841 ins->opcode = OP_ICONST;
1845 imm8 = mono_is_power_of_two (ins->inst_imm);
1847 ins->opcode = OP_SHL_IMM;
1848 ins->inst_imm = imm8;
1851 NEW_INS (cfg, temp, OP_ICONST);
1852 temp->inst_c0 = ins->inst_imm;
1853 temp->dreg = mono_alloc_ireg (cfg);
1854 ins->sreg2 = temp->dreg;
1855 ins->opcode = OP_IMUL;
1861 if (ins->next && (ins->next->opcode == OP_COND_EXC_C || ins->next->opcode == OP_COND_EXC_IC))
1862 /* ARM sets the C flag to 1 if there was _no_ overflow */
1863 ins->next->opcode = OP_COND_EXC_NC;
1865 case OP_LOCALLOC_IMM:
1866 NEW_INS (cfg, temp, OP_ICONST);
1867 temp->inst_c0 = ins->inst_imm;
1868 temp->dreg = mono_alloc_ireg (cfg);
1869 ins->sreg1 = temp->dreg;
1870 ins->opcode = OP_LOCALLOC;
1872 case OP_LOAD_MEMBASE:
1873 case OP_LOADI4_MEMBASE:
1874 case OP_LOADU4_MEMBASE:
1875 case OP_LOADU1_MEMBASE:
1876 /* we can do two things: load the immed in a register
1877 * and use an indexed load, or see if the immed can be
1878 * represented as an ad_imm + a load with a smaller offset
1879 * that fits. We just do the first for now, optimize later.
1881 if (arm_is_imm12 (ins->inst_offset))
1883 NEW_INS (cfg, temp, OP_ICONST);
1884 temp->inst_c0 = ins->inst_offset;
1885 temp->dreg = mono_alloc_ireg (cfg);
1886 ins->sreg2 = temp->dreg;
1887 ins->opcode = map_to_reg_reg_op (ins->opcode);
1889 case OP_LOADI2_MEMBASE:
1890 case OP_LOADU2_MEMBASE:
1891 case OP_LOADI1_MEMBASE:
1892 if (arm_is_imm8 (ins->inst_offset))
1894 NEW_INS (cfg, temp, OP_ICONST);
1895 temp->inst_c0 = ins->inst_offset;
1896 temp->dreg = mono_alloc_ireg (cfg);
1897 ins->sreg2 = temp->dreg;
1898 ins->opcode = map_to_reg_reg_op (ins->opcode);
1900 case OP_LOADR4_MEMBASE:
1901 case OP_LOADR8_MEMBASE:
1902 if (arm_is_fpimm8 (ins->inst_offset))
1904 low_imm = ins->inst_offset & 0x1ff;
1905 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~0x1ff, &rot_amount)) >= 0) {
1906 NEW_INS (cfg, temp, OP_ADD_IMM);
1907 temp->inst_imm = ins->inst_offset & ~0x1ff;
1908 temp->sreg1 = ins->inst_basereg;
1909 temp->dreg = mono_alloc_ireg (cfg);
1910 ins->inst_basereg = temp->dreg;
1911 ins->inst_offset = low_imm;
1914 /* VFP/FPA doesn't have indexed load instructions */
1915 g_assert_not_reached ();
1917 case OP_STORE_MEMBASE_REG:
1918 case OP_STOREI4_MEMBASE_REG:
1919 case OP_STOREI1_MEMBASE_REG:
1920 if (arm_is_imm12 (ins->inst_offset))
1922 NEW_INS (cfg, temp, OP_ICONST);
1923 temp->inst_c0 = ins->inst_offset;
1924 temp->dreg = mono_alloc_ireg (cfg);
1925 ins->sreg2 = temp->dreg;
1926 ins->opcode = map_to_reg_reg_op (ins->opcode);
1928 case OP_STOREI2_MEMBASE_REG:
1929 if (arm_is_imm8 (ins->inst_offset))
1931 NEW_INS (cfg, temp, OP_ICONST);
1932 temp->inst_c0 = ins->inst_offset;
1933 temp->dreg = mono_alloc_ireg (cfg);
1934 ins->sreg2 = temp->dreg;
1935 ins->opcode = map_to_reg_reg_op (ins->opcode);
1937 case OP_STORER4_MEMBASE_REG:
1938 case OP_STORER8_MEMBASE_REG:
1939 if (arm_is_fpimm8 (ins->inst_offset))
1941 low_imm = ins->inst_offset & 0x1ff;
1942 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~ 0x1ff, &rot_amount)) >= 0 && arm_is_fpimm8 (low_imm)) {
1943 NEW_INS (cfg, temp, OP_ADD_IMM);
1944 temp->inst_imm = ins->inst_offset & ~0x1ff;
1945 temp->sreg1 = ins->inst_destbasereg;
1946 temp->dreg = mono_alloc_ireg (cfg);
1947 ins->inst_destbasereg = temp->dreg;
1948 ins->inst_offset = low_imm;
1951 /*g_print ("fail with: %d (%d, %d)\n", ins->inst_offset, ins->inst_offset & ~0x1ff, low_imm);*/
1952 /* VFP/FPA doesn't have indexed store instructions */
1953 g_assert_not_reached ();
1955 case OP_STORE_MEMBASE_IMM:
1956 case OP_STOREI1_MEMBASE_IMM:
1957 case OP_STOREI2_MEMBASE_IMM:
1958 case OP_STOREI4_MEMBASE_IMM:
1959 NEW_INS (cfg, temp, OP_ICONST);
1960 temp->inst_c0 = ins->inst_imm;
1961 temp->dreg = mono_alloc_ireg (cfg);
1962 ins->sreg1 = temp->dreg;
1963 ins->opcode = map_to_reg_reg_op (ins->opcode);
1965 goto loop_start; /* make it handle the possibly big ins->inst_offset */
1967 gboolean swap = FALSE;
1970 /* Some fp compares require swapped operands */
1971 g_assert (ins->next);
1972 switch (ins->next->opcode) {
1974 ins->next->opcode = OP_FBLT;
1978 ins->next->opcode = OP_FBLT_UN;
1982 ins->next->opcode = OP_FBGE;
1986 ins->next->opcode = OP_FBGE_UN;
1994 ins->sreg1 = ins->sreg2;
2003 bb->last_ins = last_ins;
2004 bb->max_vreg = cfg->next_vreg;
2008 emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size, gboolean is_signed)
2010 /* sreg is a float, dreg is an integer reg */
2012 ARM_FIXZ (code, dreg, sreg);
2013 #elif defined(ARM_FPU_VFP)
2015 ARM_TOSIZD (code, ARM_VFP_F0, sreg);
2017 ARM_TOUIZD (code, ARM_VFP_F0, sreg);
2018 ARM_FMRS (code, dreg, ARM_VFP_F0);
2022 ARM_AND_REG_IMM8 (code, dreg, dreg, 0xff);
2023 else if (size == 2) {
2024 ARM_SHL_IMM (code, dreg, dreg, 16);
2025 ARM_SHR_IMM (code, dreg, dreg, 16);
2029 ARM_SHL_IMM (code, dreg, dreg, 24);
2030 ARM_SAR_IMM (code, dreg, dreg, 24);
2031 } else if (size == 2) {
2032 ARM_SHL_IMM (code, dreg, dreg, 16);
2033 ARM_SAR_IMM (code, dreg, dreg, 16);
2041 const guchar *target;
2046 #define is_call_imm(diff) ((gint)(diff) >= -33554432 && (gint)(diff) <= 33554431)
2049 search_thunk_slot (void *data, int csize, int bsize, void *user_data) {
2050 PatchData *pdata = (PatchData*)user_data;
2051 guchar *code = data;
2052 guint32 *thunks = data;
2053 guint32 *endthunks = (guint32*)(code + bsize);
2055 int difflow, diffhigh;
2057 /* always ensure a call from pdata->code can reach to the thunks without further thunks */
2058 difflow = (char*)pdata->code - (char*)thunks;
2059 diffhigh = (char*)pdata->code - (char*)endthunks;
2060 if (!((is_call_imm (thunks) && is_call_imm (endthunks)) || (is_call_imm (difflow) && is_call_imm (diffhigh))))
2064 * The thunk is composed of 3 words:
2065 * load constant from thunks [2] into ARM_IP
2068 * Note that the LR register is already setup
2070 //g_print ("thunk nentries: %d\n", ((char*)endthunks - (char*)thunks)/16);
2071 if ((pdata->found == 2) || (pdata->code >= code && pdata->code <= code + csize)) {
2072 while (thunks < endthunks) {
2073 //g_print ("looking for target: %p at %p (%08x-%08x)\n", pdata->target, thunks, thunks [0], thunks [1]);
2074 if (thunks [2] == (guint32)pdata->target) {
2075 arm_patch (pdata->code, (guchar*)thunks);
2076 mono_arch_flush_icache (pdata->code, 4);
2079 } else if ((thunks [0] == 0) && (thunks [1] == 0) && (thunks [2] == 0)) {
2080 /* found a free slot instead: emit thunk */
2081 /* ARMREG_IP is fine to use since this can't be an IMT call
2084 code = (guchar*)thunks;
2085 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
2086 if (thumb_supported)
2087 ARM_BX (code, ARMREG_IP);
2089 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
2090 thunks [2] = (guint32)pdata->target;
2091 mono_arch_flush_icache ((guchar*)thunks, 12);
2093 arm_patch (pdata->code, (guchar*)thunks);
2094 mono_arch_flush_icache (pdata->code, 4);
2098 /* skip 12 bytes, the size of the thunk */
2102 //g_print ("failed thunk lookup for %p from %p at %p (%d entries)\n", pdata->target, pdata->code, data, count);
2108 handle_thunk (int absolute, guchar *code, const guchar *target) {
2109 MonoDomain *domain = mono_domain_get ();
2113 pdata.target = target;
2114 pdata.absolute = absolute;
2117 mono_domain_lock (domain);
2118 mono_domain_code_foreach (domain, search_thunk_slot, &pdata);
2121 /* this uses the first available slot */
2123 mono_domain_code_foreach (domain, search_thunk_slot, &pdata);
2125 mono_domain_unlock (domain);
2127 if (pdata.found != 1)
2128 g_print ("thunk failed for %p from %p\n", target, code);
2129 g_assert (pdata.found == 1);
2133 arm_patch (guchar *code, const guchar *target)
2135 guint32 *code32 = (void*)code;
2136 guint32 ins = *code32;
2137 guint32 prim = (ins >> 25) & 7;
2138 guint32 tval = GPOINTER_TO_UINT (target);
2140 //g_print ("patching 0x%08x (0x%08x) to point to 0x%08x\n", code, ins, target);
2141 if (prim == 5) { /* 101b */
2142 /* the diff starts 8 bytes from the branch opcode */
2143 gint diff = target - code - 8;
2145 gint tmask = 0xffffffff;
2146 if (tval & 1) { /* entering thumb mode */
2147 diff = target - 1 - code - 8;
2148 g_assert (thumb_supported);
2149 tbits = 0xf << 28; /* bl->blx bit pattern */
2150 g_assert ((ins & (1 << 24))); /* it must be a bl, not b instruction */
2151 /* this low bit of the displacement is moved to bit 24 in the instruction encoding */
2155 tmask = ~(1 << 24); /* clear the link bit */
2156 /*g_print ("blx to thumb: target: %p, code: %p, diff: %d, mask: %x\n", target, code, diff, tmask);*/
2161 if (diff <= 33554431) {
2163 ins = (ins & 0xff000000) | diff;
2165 *code32 = ins | tbits;
2169 /* diff between 0 and -33554432 */
2170 if (diff >= -33554432) {
2172 ins = (ins & 0xff000000) | (diff & ~0xff000000);
2174 *code32 = ins | tbits;
2179 handle_thunk (TRUE, code, target);
2184 * The alternative call sequences looks like this:
2186 * ldr ip, [pc] // loads the address constant
2187 * b 1f // jumps around the constant
2188 * address constant embedded in the code
2193 * There are two cases for patching:
2194 * a) at the end of method emission: in this case code points to the start
2195 * of the call sequence
2196 * b) during runtime patching of the call site: in this case code points
2197 * to the mov pc, ip instruction
2199 * We have to handle also the thunk jump code sequence:
2203 * address constant // execution never reaches here
2205 if ((ins & 0x0ffffff0) == 0x12fff10) {
2206 /* Branch and exchange: the address is constructed in a reg
2207 * We can patch BX when the code sequence is the following:
2208 * ldr ip, [pc, #0] ; 0x8
2215 guint8 *emit = (guint8*)ccode;
2216 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2218 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
2219 ARM_BX (emit, ARMREG_IP);
2221 /*patching from magic trampoline*/
2222 if (ins == ccode [3]) {
2223 g_assert (code32 [-4] == ccode [0]);
2224 g_assert (code32 [-3] == ccode [1]);
2225 g_assert (code32 [-1] == ccode [2]);
2226 code32 [-2] = (guint32)target;
2229 /*patching from JIT*/
2230 if (ins == ccode [0]) {
2231 g_assert (code32 [1] == ccode [1]);
2232 g_assert (code32 [3] == ccode [2]);
2233 g_assert (code32 [4] == ccode [3]);
2234 code32 [2] = (guint32)target;
2237 g_assert_not_reached ();
2238 } else if ((ins & 0x0ffffff0) == 0x12fff30) {
2246 guint8 *emit = (guint8*)ccode;
2247 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2249 ARM_BLX_REG (emit, ARMREG_IP);
2251 g_assert (code32 [-3] == ccode [0]);
2252 g_assert (code32 [-2] == ccode [1]);
2253 g_assert (code32 [0] == ccode [2]);
2255 code32 [-1] = (guint32)target;
2258 guint32 *tmp = ccode;
2259 guint8 *emit = (guint8*)tmp;
2260 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2261 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
2262 ARM_MOV_REG_REG (emit, ARMREG_PC, ARMREG_IP);
2263 ARM_BX (emit, ARMREG_IP);
2264 if (ins == ccode [2]) {
2265 g_assert_not_reached (); // should be -2 ...
2266 code32 [-1] = (guint32)target;
2269 if (ins == ccode [0]) {
2270 /* handles both thunk jump code and the far call sequence */
2271 code32 [2] = (guint32)target;
2274 g_assert_not_reached ();
2276 // g_print ("patched with 0x%08x\n", ins);
2280 * Return the >= 0 uimm8 value if val can be represented with a byte + rotation
2281 * (with the rotation amount in *rot_amount. rot_amount is already adjusted
2282 * to be used with the emit macros.
2283 * Return -1 otherwise.
2286 mono_arm_is_rotated_imm8 (guint32 val, gint *rot_amount)
2289 for (i = 0; i < 31; i+= 2) {
2290 res = (val << (32 - i)) | (val >> i);
2293 *rot_amount = i? 32 - i: 0;
2300 * Emits in code a sequence of instructions that load the value 'val'
2301 * into the dreg register. Uses at most 4 instructions.
2304 mono_arm_emit_load_imm (guint8 *code, int dreg, guint32 val)
2306 int imm8, rot_amount;
2308 ARM_LDR_IMM (code, dreg, ARMREG_PC, 0);
2309 /* skip the constant pool */
2315 if ((imm8 = mono_arm_is_rotated_imm8 (val, &rot_amount)) >= 0) {
2316 ARM_MOV_REG_IMM (code, dreg, imm8, rot_amount);
2317 } else if ((imm8 = mono_arm_is_rotated_imm8 (~val, &rot_amount)) >= 0) {
2318 ARM_MVN_REG_IMM (code, dreg, imm8, rot_amount);
2321 ARM_MOV_REG_IMM8 (code, dreg, (val & 0xFF));
2323 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF00) >> 8, 24);
2325 if (val & 0xFF0000) {
2326 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
2328 if (val & 0xFF000000) {
2329 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2331 } else if (val & 0xFF00) {
2332 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF00) >> 8, 24);
2333 if (val & 0xFF0000) {
2334 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
2336 if (val & 0xFF000000) {
2337 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2339 } else if (val & 0xFF0000) {
2340 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF0000) >> 16, 16);
2341 if (val & 0xFF000000) {
2342 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2345 //g_assert_not_reached ();
2351 * emit_load_volatile_arguments:
2353 * Load volatile arguments from the stack to the original input registers.
2354 * Required before a tail call.
2357 emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code)
2359 MonoMethod *method = cfg->method;
2360 MonoMethodSignature *sig;
2365 /* FIXME: Generate intermediate code instead */
2367 sig = mono_method_signature (method);
2369 /* This is the opposite of the code in emit_prolog */
2373 cinfo = calculate_sizes (sig, sig->pinvoke);
2375 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
2376 ArgInfo *ainfo = &cinfo->ret;
2377 inst = cfg->vret_addr;
2378 g_assert (arm_is_imm12 (inst->inst_offset));
2379 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2381 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
2382 ArgInfo *ainfo = cinfo->args + i;
2383 inst = cfg->args [pos];
2385 if (cfg->verbose_level > 2)
2386 g_print ("Loading argument %d (type: %d)\n", i, ainfo->regtype);
2387 if (inst->opcode == OP_REGVAR) {
2388 if (ainfo->regtype == RegTypeGeneral)
2389 ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
2390 else if (ainfo->regtype == RegTypeFP) {
2391 g_assert_not_reached ();
2392 } else if (ainfo->regtype == RegTypeBase) {
2396 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
2397 ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset));
2399 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
2400 ARM_LDR_REG_REG (code, inst->dreg, ARMREG_SP, ARMREG_IP);
2404 g_assert_not_reached ();
2406 if (ainfo->regtype == RegTypeGeneral) {
2407 switch (ainfo->size) {
2414 g_assert (arm_is_imm12 (inst->inst_offset));
2415 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2416 g_assert (arm_is_imm12 (inst->inst_offset + 4));
2417 ARM_LDR_IMM (code, ainfo->reg + 1, inst->inst_basereg, inst->inst_offset + 4);
2420 if (arm_is_imm12 (inst->inst_offset)) {
2421 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2423 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
2424 ARM_LDR_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
2428 } else if (ainfo->regtype == RegTypeBaseGen) {
2431 } else if (ainfo->regtype == RegTypeBase) {
2433 } else if (ainfo->regtype == RegTypeFP) {
2434 g_assert_not_reached ();
2435 } else if (ainfo->regtype == RegTypeStructByVal) {
2436 int doffset = inst->inst_offset;
2440 if (mono_class_from_mono_type (inst->inst_vtype))
2441 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), NULL);
2442 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
2443 if (arm_is_imm12 (doffset)) {
2444 ARM_LDR_IMM (code, ainfo->reg + cur_reg, inst->inst_basereg, doffset);
2446 code = mono_arm_emit_load_imm (code, ARMREG_IP, doffset);
2447 ARM_LDR_REG_REG (code, ainfo->reg + cur_reg, inst->inst_basereg, ARMREG_IP);
2449 soffset += sizeof (gpointer);
2450 doffset += sizeof (gpointer);
2455 } else if (ainfo->regtype == RegTypeStructByAddr) {
2472 mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
2477 guint8 *code = cfg->native_code + cfg->code_len;
2478 MonoInst *last_ins = NULL;
2479 guint last_offset = 0;
2481 int imm8, rot_amount;
2483 /* we don't align basic blocks of loops on arm */
2485 if (cfg->verbose_level > 2)
2486 g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset);
2488 cpos = bb->max_offset;
2490 if (cfg->prof_options & MONO_PROFILE_COVERAGE) {
2491 //MonoCoverageInfo *cov = mono_get_coverage_info (cfg->method);
2492 //g_assert (!mono_compile_aot);
2495 // cov->data [bb->dfn].iloffset = bb->cil_code - cfg->cil_code;
2496 /* this is not thread save, but good enough */
2497 /* fixme: howto handle overflows? */
2498 //x86_inc_mem (code, &cov->data [bb->dfn].count);
2501 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) {
2502 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2503 (gpointer)"mono_break");
2504 code = emit_call_seq (cfg, code);
2507 MONO_BB_FOR_EACH_INS (bb, ins) {
2508 offset = code - cfg->native_code;
2510 max_len = ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
2512 if (offset > (cfg->code_size - max_len - 16)) {
2513 cfg->code_size *= 2;
2514 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
2515 code = cfg->native_code + offset;
2517 // if (ins->cil_code)
2518 // g_print ("cil code\n");
2519 mono_debug_record_line_number (cfg, ins, offset);
2521 switch (ins->opcode) {
2522 case OP_MEMORY_BARRIER:
2525 #ifdef HAVE_AEABI_READ_TP
2526 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2527 (gpointer)"__aeabi_read_tp");
2528 code = emit_call_seq (cfg, code);
2530 ARM_LDR_IMM (code, ins->dreg, ARMREG_R0, ins->inst_offset);
2532 g_assert_not_reached ();
2536 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2537 ppc_mulhw (code, ppc_r3, ins->sreg1, ins->sreg2);
2540 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2541 ppc_mulhwu (code, ppc_r3, ins->sreg1, ins->sreg2);
2543 case OP_STOREI1_MEMBASE_IMM:
2544 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFF);
2545 g_assert (arm_is_imm12 (ins->inst_offset));
2546 ARM_STRB_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2548 case OP_STOREI2_MEMBASE_IMM:
2549 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFFFF);
2550 g_assert (arm_is_imm8 (ins->inst_offset));
2551 ARM_STRH_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2553 case OP_STORE_MEMBASE_IMM:
2554 case OP_STOREI4_MEMBASE_IMM:
2555 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm);
2556 g_assert (arm_is_imm12 (ins->inst_offset));
2557 ARM_STR_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2559 case OP_STOREI1_MEMBASE_REG:
2560 g_assert (arm_is_imm12 (ins->inst_offset));
2561 ARM_STRB_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2563 case OP_STOREI2_MEMBASE_REG:
2564 g_assert (arm_is_imm8 (ins->inst_offset));
2565 ARM_STRH_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2567 case OP_STORE_MEMBASE_REG:
2568 case OP_STOREI4_MEMBASE_REG:
2569 /* this case is special, since it happens for spill code after lowering has been called */
2570 if (arm_is_imm12 (ins->inst_offset)) {
2571 ARM_STR_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2573 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2574 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ARMREG_LR);
2577 case OP_STOREI1_MEMINDEX:
2578 ARM_STRB_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2580 case OP_STOREI2_MEMINDEX:
2581 ARM_STRH_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2583 case OP_STORE_MEMINDEX:
2584 case OP_STOREI4_MEMINDEX:
2585 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2588 g_assert_not_reached ();
2590 case OP_LOAD_MEMINDEX:
2591 case OP_LOADI4_MEMINDEX:
2592 case OP_LOADU4_MEMINDEX:
2593 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2595 case OP_LOADI1_MEMINDEX:
2596 ARM_LDRSB_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2598 case OP_LOADU1_MEMINDEX:
2599 ARM_LDRB_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2601 case OP_LOADI2_MEMINDEX:
2602 ARM_LDRSH_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2604 case OP_LOADU2_MEMINDEX:
2605 ARM_LDRH_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2607 case OP_LOAD_MEMBASE:
2608 case OP_LOADI4_MEMBASE:
2609 case OP_LOADU4_MEMBASE:
2610 /* this case is special, since it happens for spill code after lowering has been called */
2611 if (arm_is_imm12 (ins->inst_offset)) {
2612 ARM_LDR_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2614 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2615 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ARMREG_LR);
2618 case OP_LOADI1_MEMBASE:
2619 g_assert (arm_is_imm8 (ins->inst_offset));
2620 ARM_LDRSB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2622 case OP_LOADU1_MEMBASE:
2623 g_assert (arm_is_imm12 (ins->inst_offset));
2624 ARM_LDRB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2626 case OP_LOADU2_MEMBASE:
2627 g_assert (arm_is_imm8 (ins->inst_offset));
2628 ARM_LDRH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2630 case OP_LOADI2_MEMBASE:
2631 g_assert (arm_is_imm8 (ins->inst_offset));
2632 ARM_LDRSH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2634 case OP_ICONV_TO_I1:
2635 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 24);
2636 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 24);
2638 case OP_ICONV_TO_I2:
2639 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2640 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 16);
2642 case OP_ICONV_TO_U1:
2643 ARM_AND_REG_IMM8 (code, ins->dreg, ins->sreg1, 0xff);
2645 case OP_ICONV_TO_U2:
2646 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2647 ARM_SHR_IMM (code, ins->dreg, ins->dreg, 16);
2651 ARM_CMP_REG_REG (code, ins->sreg1, ins->sreg2);
2653 case OP_COMPARE_IMM:
2654 case OP_ICOMPARE_IMM:
2655 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2656 g_assert (imm8 >= 0);
2657 ARM_CMP_REG_IMM (code, ins->sreg1, imm8, rot_amount);
2661 * gdb does not like encountering the hw breakpoint ins in the debugged code.
2662 * So instead of emitting a trap, we emit a call a C function and place a
2665 //*(int*)code = 0xef9f0001;
2668 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2669 (gpointer)"mono_break");
2670 code = emit_call_seq (cfg, code);
2672 case OP_RELAXED_NOP:
2677 case OP_DUMMY_STORE:
2678 case OP_NOT_REACHED:
2683 ARM_ADDS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2686 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2690 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2693 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2694 g_assert (imm8 >= 0);
2695 ARM_ADDS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2699 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2700 g_assert (imm8 >= 0);
2701 ARM_ADD_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2705 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2706 g_assert (imm8 >= 0);
2707 ARM_ADCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2710 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2711 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2713 case OP_IADD_OVF_UN:
2714 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2715 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2718 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2719 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2721 case OP_ISUB_OVF_UN:
2722 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2723 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2725 case OP_ADD_OVF_CARRY:
2726 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2727 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2729 case OP_ADD_OVF_UN_CARRY:
2730 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2731 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2733 case OP_SUB_OVF_CARRY:
2734 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2735 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2737 case OP_SUB_OVF_UN_CARRY:
2738 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2739 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2743 ARM_SUBS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2746 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2747 g_assert (imm8 >= 0);
2748 ARM_SUBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2751 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2755 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2759 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2760 g_assert (imm8 >= 0);
2761 ARM_SUB_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2765 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2766 g_assert (imm8 >= 0);
2767 ARM_SBCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2769 case OP_ARM_RSBS_IMM:
2770 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2771 g_assert (imm8 >= 0);
2772 ARM_RSBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2774 case OP_ARM_RSC_IMM:
2775 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2776 g_assert (imm8 >= 0);
2777 ARM_RSC_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2780 ARM_AND_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2784 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2785 g_assert (imm8 >= 0);
2786 ARM_AND_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2794 /* crappy ARM arch doesn't have a DIV instruction */
2795 g_assert_not_reached ();
2797 ARM_ORR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2801 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2802 g_assert (imm8 >= 0);
2803 ARM_ORR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2806 ARM_EOR_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_EOR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2815 ARM_SHL_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2820 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2821 else if (ins->dreg != ins->sreg1)
2822 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2825 ARM_SAR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2830 ARM_SAR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2831 else if (ins->dreg != ins->sreg1)
2832 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2835 case OP_ISHR_UN_IMM:
2837 ARM_SHR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2838 else if (ins->dreg != ins->sreg1)
2839 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2842 ARM_SHR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2845 ARM_MVN_REG_REG (code, ins->dreg, ins->sreg1);
2848 ARM_RSB_REG_IMM8 (code, ins->dreg, ins->sreg1, 0);
2851 if (ins->dreg == ins->sreg2)
2852 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2854 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg2, ins->sreg1);
2857 g_assert_not_reached ();
2860 /* FIXME: handle ovf/ sreg2 != dreg */
2861 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2862 /* FIXME: MUL doesn't set the C/O flags on ARM */
2864 case OP_IMUL_OVF_UN:
2865 /* FIXME: handle ovf/ sreg2 != dreg */
2866 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2867 /* FIXME: MUL doesn't set the C/O flags on ARM */
2870 code = mono_arm_emit_load_imm (code, ins->dreg, ins->inst_c0);
2873 /* Load the GOT offset */
2874 mono_add_patch_info (cfg, offset, (MonoJumpInfoType)ins->inst_i1, ins->inst_p0);
2875 ARM_LDR_IMM (code, ins->dreg, ARMREG_PC, 0);
2877 *(gpointer*)code = NULL;
2879 /* Load the value from the GOT */
2880 ARM_LDR_REG_REG (code, ins->dreg, ARMREG_PC, ins->dreg);
2882 case OP_ICONV_TO_I4:
2883 case OP_ICONV_TO_U4:
2885 if (ins->dreg != ins->sreg1)
2886 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2889 int saved = ins->sreg2;
2890 if (ins->sreg2 == ARM_LSW_REG) {
2891 ARM_MOV_REG_REG (code, ARMREG_LR, ins->sreg2);
2894 if (ins->sreg1 != ARM_LSW_REG)
2895 ARM_MOV_REG_REG (code, ARM_LSW_REG, ins->sreg1);
2896 if (saved != ARM_MSW_REG)
2897 ARM_MOV_REG_REG (code, ARM_MSW_REG, saved);
2902 ARM_MVFD (code, ins->dreg, ins->sreg1);
2903 #elif defined(ARM_FPU_VFP)
2904 ARM_CPYD (code, ins->dreg, ins->sreg1);
2907 case OP_FCONV_TO_R4:
2909 ARM_MVFS (code, ins->dreg, ins->sreg1);
2910 #elif defined(ARM_FPU_VFP)
2911 ARM_CVTD (code, ins->dreg, ins->sreg1);
2912 ARM_CVTS (code, ins->dreg, ins->dreg);
2917 * Keep in sync with mono_arch_emit_epilog
2919 g_assert (!cfg->method->save_lmf);
2921 code = emit_load_volatile_arguments (cfg, code);
2923 code = emit_big_add (code, ARMREG_SP, cfg->frame_reg, cfg->stack_usage);
2924 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP)) | ((1 << ARMREG_LR)));
2925 mono_add_patch_info (cfg, (guint8*) code - cfg->native_code, MONO_PATCH_INFO_METHOD_JUMP, ins->inst_p0);
2926 if (cfg->compile_aot) {
2927 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
2929 *(gpointer*)code = NULL;
2931 ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_IP);
2937 /* ensure ins->sreg1 is not NULL */
2938 ARM_LDR_IMM (code, ARMREG_LR, ins->sreg1, 0);
2942 if (ppc_is_imm16 (cfg->sig_cookie + cfg->stack_usage)) {
2943 ppc_addi (code, ppc_r11, cfg->frame_reg, cfg->sig_cookie + cfg->stack_usage);
2945 ppc_load (code, ppc_r11, cfg->sig_cookie + cfg->stack_usage);
2946 ppc_add (code, ppc_r11, cfg->frame_reg, ppc_r11);
2948 ppc_stw (code, ppc_r11, 0, ins->sreg1);
2958 call = (MonoCallInst*)ins;
2959 if (ins->flags & MONO_INST_HAS_METHOD)
2960 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, call->method);
2962 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, call->fptr);
2963 code = emit_call_seq (cfg, code);
2964 code = emit_move_return_value (cfg, ins, code);
2970 case OP_VOIDCALL_REG:
2972 code = emit_call_reg (code, ins->sreg1);
2973 code = emit_move_return_value (cfg, ins, code);
2975 case OP_FCALL_MEMBASE:
2976 case OP_LCALL_MEMBASE:
2977 case OP_VCALL_MEMBASE:
2978 case OP_VCALL2_MEMBASE:
2979 case OP_VOIDCALL_MEMBASE:
2980 case OP_CALL_MEMBASE:
2981 g_assert (arm_is_imm12 (ins->inst_offset));
2982 g_assert (ins->sreg1 != ARMREG_LR);
2983 call = (MonoCallInst*)ins;
2984 if (call->method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
2985 ARM_ADD_REG_IMM8 (code, ARMREG_LR, ARMREG_PC, 4);
2986 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
2988 * We can't embed the method in the code stream in PIC code, or
2990 * Instead, we put it in V5 in code emitted by
2991 * mono_arch_emit_imt_argument (), and embed NULL here to
2992 * signal the IMT thunk that the value is in V5.
2994 if (call->dynamic_imt_arg)
2995 *((gpointer*)code) = NULL;
2997 *((gpointer*)code) = (gpointer)call->method;
3000 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
3001 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
3003 code = emit_move_return_value (cfg, ins, code);
3006 /* keep alignment */
3007 int alloca_waste = cfg->param_area;
3010 /* round the size to 8 bytes */
3011 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->sreg1, 7);
3012 ARM_BIC_REG_IMM8 (code, ins->dreg, ins->dreg, 7);
3014 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->dreg, alloca_waste);
3015 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ins->dreg);
3016 /* memzero the area: dreg holds the size, sp is the pointer */
3017 if (ins->flags & MONO_INST_INIT) {
3018 guint8 *start_loop, *branch_to_cond;
3019 ARM_MOV_REG_IMM8 (code, ARMREG_LR, 0);
3020 branch_to_cond = code;
3023 ARM_STR_REG_REG (code, ARMREG_LR, ARMREG_SP, ins->dreg);
3024 arm_patch (branch_to_cond, code);
3025 /* decrement by 4 and set flags */
3026 ARM_SUBS_REG_IMM8 (code, ins->dreg, ins->dreg, 4);
3027 ARM_B_COND (code, ARMCOND_GE, 0);
3028 arm_patch (code - 4, start_loop);
3030 ARM_ADD_REG_IMM8 (code, ins->dreg, ARMREG_SP, alloca_waste);
3034 if (ins->sreg1 != ARMREG_R0)
3035 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
3036 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3037 (gpointer)"mono_arch_throw_exception");
3038 code = emit_call_seq (cfg, code);
3042 if (ins->sreg1 != ARMREG_R0)
3043 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
3044 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3045 (gpointer)"mono_arch_rethrow_exception");
3046 code = emit_call_seq (cfg, code);
3049 case OP_START_HANDLER: {
3050 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3052 if (arm_is_imm12 (spvar->inst_offset)) {
3053 ARM_STR_IMM (code, ARMREG_LR, spvar->inst_basereg, spvar->inst_offset);
3055 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3056 ARM_STR_REG_REG (code, ARMREG_LR, spvar->inst_basereg, ARMREG_IP);
3060 case OP_ENDFILTER: {
3061 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3063 if (ins->sreg1 != ARMREG_R0)
3064 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
3065 if (arm_is_imm12 (spvar->inst_offset)) {
3066 ARM_LDR_IMM (code, ARMREG_IP, spvar->inst_basereg, spvar->inst_offset);
3068 g_assert (ARMREG_IP != spvar->inst_basereg);
3069 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3070 ARM_LDR_REG_REG (code, ARMREG_IP, spvar->inst_basereg, ARMREG_IP);
3072 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
3075 case OP_ENDFINALLY: {
3076 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3078 if (arm_is_imm12 (spvar->inst_offset)) {
3079 ARM_LDR_IMM (code, ARMREG_IP, spvar->inst_basereg, spvar->inst_offset);
3081 g_assert (ARMREG_IP != spvar->inst_basereg);
3082 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3083 ARM_LDR_REG_REG (code, ARMREG_IP, spvar->inst_basereg, ARMREG_IP);
3085 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
3088 case OP_CALL_HANDLER:
3089 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb);
3093 ins->inst_c0 = code - cfg->native_code;
3096 if (ins->flags & MONO_INST_BRLABEL) {
3097 /*if (ins->inst_i0->inst_c0) {
3099 //x86_jump_code (code, cfg->native_code + ins->inst_i0->inst_c0);
3101 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_LABEL, ins->inst_i0);
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);
3115 ARM_MOV_REG_REG (code, ARMREG_PC, ins->sreg1);
3119 * In the normal case we have:
3120 * ldr pc, [pc, ins->sreg1 << 2]
3123 * ldr lr, [pc, ins->sreg1 << 2]
3125 * After follows the data.
3126 * FIXME: add aot support.
3128 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_SWITCH, ins->inst_p0);
3129 max_len += 4 * GPOINTER_TO_INT (ins->klass);
3130 if (offset > (cfg->code_size - max_len - 16)) {
3131 cfg->code_size += max_len;
3132 cfg->code_size *= 2;
3133 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
3134 code = cfg->native_code + offset;
3136 ARM_LDR_REG_REG_SHIFT (code, ARMREG_PC, ARMREG_PC, ins->sreg1, ARMSHIFT_LSL, 2);
3138 code += 4 * GPOINTER_TO_INT (ins->klass);
3142 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
3143 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
3147 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3148 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LT);
3152 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3153 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LO);
3157 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3158 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_GT);
3162 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3163 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_HI);
3165 case OP_COND_EXC_EQ:
3166 case OP_COND_EXC_NE_UN:
3167 case OP_COND_EXC_LT:
3168 case OP_COND_EXC_LT_UN:
3169 case OP_COND_EXC_GT:
3170 case OP_COND_EXC_GT_UN:
3171 case OP_COND_EXC_GE:
3172 case OP_COND_EXC_GE_UN:
3173 case OP_COND_EXC_LE:
3174 case OP_COND_EXC_LE_UN:
3175 EMIT_COND_SYSTEM_EXCEPTION (ins->opcode - OP_COND_EXC_EQ, ins->inst_p1);
3177 case OP_COND_EXC_IEQ:
3178 case OP_COND_EXC_INE_UN:
3179 case OP_COND_EXC_ILT:
3180 case OP_COND_EXC_ILT_UN:
3181 case OP_COND_EXC_IGT:
3182 case OP_COND_EXC_IGT_UN:
3183 case OP_COND_EXC_IGE:
3184 case OP_COND_EXC_IGE_UN:
3185 case OP_COND_EXC_ILE:
3186 case OP_COND_EXC_ILE_UN:
3187 EMIT_COND_SYSTEM_EXCEPTION (ins->opcode - OP_COND_EXC_IEQ, ins->inst_p1);
3190 case OP_COND_EXC_IC:
3191 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_CS, ins->inst_p1);
3193 case OP_COND_EXC_OV:
3194 case OP_COND_EXC_IOV:
3195 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VS, ins->inst_p1);
3197 case OP_COND_EXC_NC:
3198 case OP_COND_EXC_INC:
3199 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_CC, ins->inst_p1);
3201 case OP_COND_EXC_NO:
3202 case OP_COND_EXC_INO:
3203 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VC, ins->inst_p1);
3215 EMIT_COND_BRANCH (ins, ins->opcode - OP_IBEQ);
3218 /* floating point opcodes */
3221 if (cfg->compile_aot) {
3222 ARM_LDFD (code, ins->dreg, ARMREG_PC, 0);
3224 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3226 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
3229 /* FIXME: we can optimize the imm load by dealing with part of
3230 * the displacement in LDFD (aligning to 512).
3232 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3233 ARM_LDFD (code, ins->dreg, ARMREG_LR, 0);
3237 if (cfg->compile_aot) {
3238 ARM_LDFS (code, ins->dreg, ARMREG_PC, 0);
3240 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3243 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3244 ARM_LDFS (code, ins->dreg, ARMREG_LR, 0);
3247 case OP_STORER8_MEMBASE_REG:
3248 /* This is generated by the local regalloc pass which runs after the lowering pass */
3249 if (!arm_is_fpimm8 (ins->inst_offset)) {
3250 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3251 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_destbasereg);
3252 ARM_STFD (code, ins->sreg1, ARMREG_LR, 0);
3254 ARM_STFD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3257 case OP_LOADR8_MEMBASE:
3258 /* This is generated by the local regalloc pass which runs after the lowering pass */
3259 if (!arm_is_fpimm8 (ins->inst_offset)) {
3260 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3261 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_basereg);
3262 ARM_LDFD (code, ins->dreg, ARMREG_LR, 0);
3264 ARM_LDFD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3267 case OP_STORER4_MEMBASE_REG:
3268 g_assert (arm_is_fpimm8 (ins->inst_offset));
3269 ARM_STFS (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3271 case OP_LOADR4_MEMBASE:
3272 g_assert (arm_is_fpimm8 (ins->inst_offset));
3273 ARM_LDFS (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3275 case OP_ICONV_TO_R_UN: {
3277 tmpreg = ins->dreg == 0? 1: 0;
3278 ARM_CMP_REG_IMM8 (code, ins->sreg1, 0);
3279 ARM_FLTD (code, ins->dreg, ins->sreg1);
3280 ARM_B_COND (code, ARMCOND_GE, 8);
3281 /* save the temp register */
3282 ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
3283 ARM_STFD (code, tmpreg, ARMREG_SP, 0);
3284 ARM_LDFD (code, tmpreg, ARMREG_PC, 12);
3285 ARM_FPA_ADFD (code, ins->dreg, ins->dreg, tmpreg);
3286 ARM_LDFD (code, tmpreg, ARMREG_SP, 0);
3287 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
3288 /* skip the constant pool */
3291 *(int*)code = 0x41f00000;
3296 * ldfltd ftemp, [pc, #8] 0x41f00000 0x00000000
3297 * adfltd fdest, fdest, ftemp
3301 case OP_ICONV_TO_R4:
3302 ARM_FLTS (code, ins->dreg, ins->sreg1);
3304 case OP_ICONV_TO_R8:
3305 ARM_FLTD (code, ins->dreg, ins->sreg1);
3308 #elif defined(ARM_FPU_VFP)
3311 if (cfg->compile_aot) {
3312 ARM_FLDD (code, ins->dreg, ARMREG_PC, 0);
3314 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3316 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
3319 /* FIXME: we can optimize the imm load by dealing with part of
3320 * the displacement in LDFD (aligning to 512).
3322 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3323 ARM_FLDD (code, ins->dreg, ARMREG_LR, 0);
3327 if (cfg->compile_aot) {
3328 ARM_FLDS (code, ins->dreg, ARMREG_PC, 0);
3330 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3332 ARM_CVTS (code, ins->dreg, ins->dreg);
3334 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3335 ARM_FLDS (code, ins->dreg, ARMREG_LR, 0);
3336 ARM_CVTS (code, ins->dreg, ins->dreg);
3339 case OP_STORER8_MEMBASE_REG:
3340 /* This is generated by the local regalloc pass which runs after the lowering pass */
3341 if (!arm_is_fpimm8 (ins->inst_offset)) {
3342 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3343 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_destbasereg);
3344 ARM_FSTD (code, ins->sreg1, ARMREG_LR, 0);
3346 ARM_FSTD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3349 case OP_LOADR8_MEMBASE:
3350 /* This is generated by the local regalloc pass which runs after the lowering pass */
3351 if (!arm_is_fpimm8 (ins->inst_offset)) {
3352 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3353 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_basereg);
3354 ARM_FLDD (code, ins->dreg, ARMREG_LR, 0);
3356 ARM_FLDD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3359 case OP_STORER4_MEMBASE_REG:
3360 g_assert (arm_is_fpimm8 (ins->inst_offset));
3361 ARM_CVTD (code, ARM_VFP_F0, ins->sreg1);
3362 ARM_FSTS (code, ARM_VFP_F0, ins->inst_destbasereg, ins->inst_offset);
3364 case OP_LOADR4_MEMBASE:
3365 g_assert (arm_is_fpimm8 (ins->inst_offset));
3366 ARM_FLDS (code, ARM_VFP_F0, ins->inst_basereg, ins->inst_offset);
3367 ARM_CVTS (code, ins->dreg, ARM_VFP_F0);
3369 case OP_ICONV_TO_R_UN: {
3370 g_assert_not_reached ();
3373 case OP_ICONV_TO_R4:
3374 ARM_FMSR (code, ARM_VFP_F0, ins->sreg1);
3375 ARM_FSITOS (code, ARM_VFP_F0, ARM_VFP_F0);
3376 ARM_CVTS (code, ins->dreg, ARM_VFP_F0);
3378 case OP_ICONV_TO_R8:
3379 ARM_FMSR (code, ARM_VFP_F0, ins->sreg1);
3380 ARM_FSITOD (code, ins->dreg, ARM_VFP_F0);
3384 if (mono_method_signature (cfg->method)->ret->type == MONO_TYPE_R4) {
3385 ARM_CVTD (code, ARM_VFP_F0, ins->sreg1);
3386 ARM_FMRS (code, ARMREG_R0, ARM_VFP_F0);
3388 ARM_FMRRD (code, ARMREG_R0, ARMREG_R1, ins->sreg1);
3394 case OP_FCONV_TO_I1:
3395 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, TRUE);
3397 case OP_FCONV_TO_U1:
3398 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, FALSE);
3400 case OP_FCONV_TO_I2:
3401 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, TRUE);
3403 case OP_FCONV_TO_U2:
3404 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, FALSE);
3406 case OP_FCONV_TO_I4:
3408 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, TRUE);
3410 case OP_FCONV_TO_U4:
3412 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, FALSE);
3414 case OP_FCONV_TO_I8:
3415 case OP_FCONV_TO_U8:
3416 g_assert_not_reached ();
3417 /* Implemented as helper calls */
3419 case OP_LCONV_TO_R_UN:
3420 g_assert_not_reached ();
3421 /* Implemented as helper calls */
3423 case OP_LCONV_TO_OVF_I:
3424 case OP_LCONV_TO_OVF_I4_2: {
3425 guint32 *high_bit_not_set, *valid_negative, *invalid_negative, *valid_positive;
3427 * Valid ints: 0xffffffff:8000000 to 00000000:0x7f000000
3430 ARM_CMP_REG_IMM8 (code, ins->sreg1, 0);
3431 high_bit_not_set = code;
3432 ARM_B_COND (code, ARMCOND_GE, 0); /*branch if bit 31 of the lower part is not set*/
3434 ARM_CMN_REG_IMM8 (code, ins->sreg2, 1); /*This have the same effect as CMP reg, 0xFFFFFFFF */
3435 valid_negative = code;
3436 ARM_B_COND (code, ARMCOND_EQ, 0); /*branch if upper part == 0xFFFFFFFF (lower part has bit 31 set) */
3437 invalid_negative = code;
3438 ARM_B_COND (code, ARMCOND_AL, 0);
3440 arm_patch (high_bit_not_set, code);
3442 ARM_CMP_REG_IMM8 (code, ins->sreg2, 0);
3443 valid_positive = code;
3444 ARM_B_COND (code, ARMCOND_EQ, 0); /*branch if upper part == 0 (lower part has bit 31 clear)*/
3446 arm_patch (invalid_negative, code);
3447 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_AL, "OverflowException");
3449 arm_patch (valid_negative, code);
3450 arm_patch (valid_positive, code);
3452 if (ins->dreg != ins->sreg1)
3453 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
3458 ARM_FPA_ADFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3461 ARM_FPA_SUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3464 ARM_FPA_MUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3467 ARM_FPA_DVFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3470 ARM_MNFD (code, ins->dreg, ins->sreg1);
3472 #elif defined(ARM_FPU_VFP)
3474 ARM_VFP_ADDD (code, ins->dreg, ins->sreg1, ins->sreg2);
3477 ARM_VFP_SUBD (code, ins->dreg, ins->sreg1, ins->sreg2);
3480 ARM_VFP_MULD (code, ins->dreg, ins->sreg1, ins->sreg2);
3483 ARM_VFP_DIVD (code, ins->dreg, ins->sreg1, ins->sreg2);
3486 ARM_NEGD (code, ins->dreg, ins->sreg1);
3491 g_assert_not_reached ();
3495 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3496 #elif defined(ARM_FPU_VFP)
3497 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3503 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3504 #elif defined(ARM_FPU_VFP)
3505 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3508 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
3509 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
3513 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3514 #elif defined(ARM_FPU_VFP)
3515 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3518 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3519 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3523 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3524 #elif defined(ARM_FPU_VFP)
3525 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3528 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3529 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3530 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
3535 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3536 #elif defined(ARM_FPU_VFP)
3537 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3540 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3541 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3546 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3547 #elif defined(ARM_FPU_VFP)
3548 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3551 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3552 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3553 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
3555 /* ARM FPA flags table:
3556 * N Less than ARMCOND_MI
3557 * Z Equal ARMCOND_EQ
3558 * C Greater Than or Equal ARMCOND_CS
3559 * V Unordered ARMCOND_VS
3562 EMIT_COND_BRANCH (ins, OP_IBEQ - OP_IBEQ);
3565 EMIT_COND_BRANCH (ins, OP_IBNE_UN - OP_IBEQ);
3568 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
3571 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3572 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
3578 g_assert_not_reached ();
3581 /* FIXME does VFP requires both conds?
3582 * 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);
3588 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3589 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_GE);
3594 if (ins->dreg != ins->sreg1)
3595 ARM_MVFD (code, ins->dreg, ins->sreg1);
3596 #elif defined(ARM_FPU_VFP)
3597 ARM_CPYD (code, ins->dreg, ins->sreg1);
3602 g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
3603 g_assert_not_reached ();
3606 if ((cfg->opt & MONO_OPT_BRANCH) && ((code - cfg->native_code - offset) > max_len)) {
3607 g_warning ("wrong maximal instruction length of instruction %s (expected %d, got %d)",
3608 mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset);
3609 g_assert_not_reached ();
3615 last_offset = offset;
3618 cfg->code_len = code - cfg->native_code;
3621 #endif /* DISABLE_JIT */
3623 #ifdef HAVE_AEABI_READ_TP
3624 void __aeabi_read_tp (void);
3628 mono_arch_register_lowlevel_calls (void)
3630 /* The signature doesn't matter */
3631 mono_register_jit_icall (mono_arm_throw_exception, "mono_arm_throw_exception", mono_create_icall_signature ("void"), TRUE);
3632 mono_register_jit_icall (mono_arm_throw_exception_by_token, "mono_arm_throw_exception_by_token", mono_create_icall_signature ("void"), TRUE);
3634 #ifdef HAVE_AEABI_READ_TP
3635 mono_register_jit_icall (__aeabi_read_tp, "__aeabi_read_tp", mono_create_icall_signature ("void"), TRUE);
3639 #define patch_lis_ori(ip,val) do {\
3640 guint16 *__lis_ori = (guint16*)(ip); \
3641 __lis_ori [1] = (((guint32)(val)) >> 16) & 0xffff; \
3642 __lis_ori [3] = ((guint32)(val)) & 0xffff; \
3646 mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, gboolean run_cctors)
3648 MonoJumpInfo *patch_info;
3649 gboolean compile_aot = !run_cctors;
3651 for (patch_info = ji; patch_info; patch_info = patch_info->next) {
3652 unsigned char *ip = patch_info->ip.i + code;
3653 const unsigned char *target;
3655 if (patch_info->type == MONO_PATCH_INFO_SWITCH && !compile_aot) {
3656 gpointer *jt = (gpointer*)(ip + 8);
3658 /* jt is the inlined jump table, 2 instructions after ip
3659 * In the normal case we store the absolute addresses,
3660 * otherwise the displacements.
3662 for (i = 0; i < patch_info->data.table->table_size; i++)
3663 jt [i] = code + (int)patch_info->data.table->table [i];
3666 target = mono_resolve_patch_target (method, domain, code, patch_info, run_cctors);
3669 switch (patch_info->type) {
3670 case MONO_PATCH_INFO_BB:
3671 case MONO_PATCH_INFO_LABEL:
3674 /* No need to patch these */
3679 switch (patch_info->type) {
3680 case MONO_PATCH_INFO_IP:
3681 g_assert_not_reached ();
3682 patch_lis_ori (ip, ip);
3684 case MONO_PATCH_INFO_METHOD_REL:
3685 g_assert_not_reached ();
3686 *((gpointer *)(ip)) = code + patch_info->data.offset;
3688 case MONO_PATCH_INFO_METHODCONST:
3689 case MONO_PATCH_INFO_CLASS:
3690 case MONO_PATCH_INFO_IMAGE:
3691 case MONO_PATCH_INFO_FIELD:
3692 case MONO_PATCH_INFO_VTABLE:
3693 case MONO_PATCH_INFO_IID:
3694 case MONO_PATCH_INFO_SFLDA:
3695 case MONO_PATCH_INFO_LDSTR:
3696 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
3697 case MONO_PATCH_INFO_LDTOKEN:
3698 g_assert_not_reached ();
3699 /* from OP_AOTCONST : lis + ori */
3700 patch_lis_ori (ip, target);
3702 case MONO_PATCH_INFO_R4:
3703 case MONO_PATCH_INFO_R8:
3704 g_assert_not_reached ();
3705 *((gconstpointer *)(ip + 2)) = patch_info->data.target;
3707 case MONO_PATCH_INFO_EXC_NAME:
3708 g_assert_not_reached ();
3709 *((gconstpointer *)(ip + 1)) = patch_info->data.name;
3711 case MONO_PATCH_INFO_NONE:
3712 case MONO_PATCH_INFO_BB_OVF:
3713 case MONO_PATCH_INFO_EXC_OVF:
3714 /* everything is dealt with at epilog output time */
3719 arm_patch (ip, target);
3724 * Stack frame layout:
3726 * ------------------- fp
3727 * MonoLMF structure or saved registers
3728 * -------------------
3730 * -------------------
3732 * -------------------
3733 * optional 8 bytes for tracing
3734 * -------------------
3735 * param area size is cfg->param_area
3736 * ------------------- sp
3739 mono_arch_emit_prolog (MonoCompile *cfg)
3741 MonoMethod *method = cfg->method;
3743 MonoMethodSignature *sig;
3745 int alloc_size, pos, max_offset, i, rot_amount;
3750 int prev_sp_offset, reg_offset;
3752 if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
3755 sig = mono_method_signature (method);
3756 cfg->code_size = 256 + sig->param_count * 20;
3757 code = cfg->native_code = g_malloc (cfg->code_size);
3759 mono_emit_unwind_op_def_cfa (cfg, code, ARMREG_SP, 0);
3761 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
3763 alloc_size = cfg->stack_offset;
3766 if (!method->save_lmf) {
3767 /* We save SP by storing it into IP and saving IP */
3768 ARM_PUSH (code, (cfg->used_int_regs | (1 << ARMREG_IP) | (1 << ARMREG_LR)));
3769 prev_sp_offset = 8; /* ip and lr */
3770 for (i = 0; i < 16; ++i) {
3771 if (cfg->used_int_regs & (1 << i))
3772 prev_sp_offset += 4;
3774 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset);
3776 for (i = 0; i < 16; ++i) {
3777 if ((cfg->used_int_regs & (1 << i)) || (i == ARMREG_IP) || (i == ARMREG_LR)) {
3778 mono_emit_unwind_op_offset (cfg, code, i, (- prev_sp_offset) + reg_offset);
3783 ARM_PUSH (code, 0x5ff0);
3784 prev_sp_offset = 4 * 10; /* all but r0-r3, sp and pc */
3785 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset);
3787 for (i = 0; i < 16; ++i) {
3788 if ((i > ARMREG_R3) && (i != ARMREG_SP) && (i != ARMREG_PC)) {
3789 mono_emit_unwind_op_offset (cfg, code, i, (- prev_sp_offset) + reg_offset);
3793 pos += sizeof (MonoLMF) - prev_sp_offset;
3797 // align to MONO_ARCH_FRAME_ALIGNMENT bytes
3798 if (alloc_size & (MONO_ARCH_FRAME_ALIGNMENT - 1)) {
3799 alloc_size += MONO_ARCH_FRAME_ALIGNMENT - 1;
3800 alloc_size &= ~(MONO_ARCH_FRAME_ALIGNMENT - 1);
3803 /* the stack used in the pushed regs */
3804 if (prev_sp_offset & 4)
3806 cfg->stack_usage = alloc_size;
3808 if ((i = mono_arm_is_rotated_imm8 (alloc_size, &rot_amount)) >= 0) {
3809 ARM_SUB_REG_IMM (code, ARMREG_SP, ARMREG_SP, i, rot_amount);
3811 code = mono_arm_emit_load_imm (code, ARMREG_IP, alloc_size);
3812 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
3814 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset + alloc_size);
3816 if (cfg->frame_reg != ARMREG_SP) {
3817 ARM_MOV_REG_REG (code, cfg->frame_reg, ARMREG_SP);
3818 mono_emit_unwind_op_def_cfa_reg (cfg, code, cfg->frame_reg);
3820 //g_print ("prev_sp_offset: %d, alloc_size:%d\n", prev_sp_offset, alloc_size);
3821 prev_sp_offset += alloc_size;
3823 /* compute max_offset in order to use short forward jumps
3824 * we could skip do it on arm because the immediate displacement
3825 * for jumps is large enough, it may be useful later for constant pools
3828 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
3829 MonoInst *ins = bb->code;
3830 bb->max_offset = max_offset;
3832 if (cfg->prof_options & MONO_PROFILE_COVERAGE)
3835 MONO_BB_FOR_EACH_INS (bb, ins)
3836 max_offset += ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
3839 /* store runtime generic context */
3840 if (cfg->rgctx_var) {
3841 MonoInst *ins = cfg->rgctx_var;
3843 g_assert (ins->opcode == OP_REGOFFSET);
3845 if (arm_is_imm12 (ins->inst_offset)) {
3846 ARM_STR_IMM (code, MONO_ARCH_RGCTX_REG, ins->inst_basereg, ins->inst_offset);
3848 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3849 ARM_STR_REG_REG (code, MONO_ARCH_RGCTX_REG, ins->inst_basereg, ARMREG_LR);
3853 /* load arguments allocated to register from the stack */
3856 cinfo = calculate_sizes (sig, sig->pinvoke);
3858 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
3859 ArgInfo *ainfo = &cinfo->ret;
3860 inst = cfg->vret_addr;
3861 g_assert (arm_is_imm12 (inst->inst_offset));
3862 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3864 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
3865 ArgInfo *ainfo = cinfo->args + i;
3866 inst = cfg->args [pos];
3868 if (cfg->verbose_level > 2)
3869 g_print ("Saving argument %d (type: %d)\n", i, ainfo->regtype);
3870 if (inst->opcode == OP_REGVAR) {
3871 if (ainfo->regtype == RegTypeGeneral)
3872 ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
3873 else if (ainfo->regtype == RegTypeFP) {
3874 g_assert_not_reached ();
3875 } else if (ainfo->regtype == RegTypeBase) {
3876 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
3877 ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3879 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3880 ARM_LDR_REG_REG (code, inst->dreg, ARMREG_SP, ARMREG_IP);
3883 g_assert_not_reached ();
3885 if (cfg->verbose_level > 2)
3886 g_print ("Argument %d assigned to register %s\n", pos, mono_arch_regname (inst->dreg));
3888 /* the argument should be put on the stack: FIXME handle size != word */
3889 if (ainfo->regtype == RegTypeGeneral) {
3890 switch (ainfo->size) {
3892 if (arm_is_imm12 (inst->inst_offset))
3893 ARM_STRB_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3895 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3896 ARM_STRB_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3900 if (arm_is_imm8 (inst->inst_offset)) {
3901 ARM_STRH_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3903 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3904 ARM_STRH_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3908 g_assert (arm_is_imm12 (inst->inst_offset));
3909 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3910 g_assert (arm_is_imm12 (inst->inst_offset + 4));
3911 ARM_STR_IMM (code, ainfo->reg + 1, inst->inst_basereg, inst->inst_offset + 4);
3914 if (arm_is_imm12 (inst->inst_offset)) {
3915 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3917 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3918 ARM_STR_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3922 } else if (ainfo->regtype == RegTypeBaseGen) {
3923 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
3924 g_assert (arm_is_imm12 (inst->inst_offset));
3925 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3926 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3927 ARM_STR_IMM (code, ARMREG_R3, inst->inst_basereg, inst->inst_offset);
3928 } else if (ainfo->regtype == RegTypeBase) {
3929 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
3930 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3932 code = mono_arm_emit_load_imm (code, ARMREG_IP, prev_sp_offset + ainfo->offset);
3933 ARM_LDR_REG_REG (code, ARMREG_LR, ARMREG_SP, ARMREG_IP);
3936 switch (ainfo->size) {
3938 if (arm_is_imm8 (inst->inst_offset)) {
3939 ARM_STRB_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3941 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3942 ARM_STRB_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3946 if (arm_is_imm8 (inst->inst_offset)) {
3947 ARM_STRH_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3949 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3950 ARM_STRH_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3954 if (arm_is_imm12 (inst->inst_offset)) {
3955 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3957 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3958 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3960 if (arm_is_imm12 (prev_sp_offset + ainfo->offset + 4)) {
3961 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset + 4));
3963 code = mono_arm_emit_load_imm (code, ARMREG_IP, prev_sp_offset + ainfo->offset + 4);
3964 ARM_LDR_REG_REG (code, ARMREG_LR, ARMREG_SP, ARMREG_IP);
3966 if (arm_is_imm12 (inst->inst_offset + 4)) {
3967 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3969 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset + 4);
3970 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3974 if (arm_is_imm12 (inst->inst_offset)) {
3975 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3977 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3978 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3982 } else if (ainfo->regtype == RegTypeFP) {
3983 g_assert_not_reached ();
3984 } else if (ainfo->regtype == RegTypeStructByVal) {
3985 int doffset = inst->inst_offset;
3989 size = mini_type_stack_size_full (cfg->generic_sharing_context, inst->inst_vtype, NULL, sig->pinvoke);
3990 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
3991 if (arm_is_imm12 (doffset)) {
3992 ARM_STR_IMM (code, ainfo->reg + cur_reg, inst->inst_basereg, doffset);
3994 code = mono_arm_emit_load_imm (code, ARMREG_IP, doffset);
3995 ARM_STR_REG_REG (code, ainfo->reg + cur_reg, inst->inst_basereg, ARMREG_IP);
3997 soffset += sizeof (gpointer);
3998 doffset += sizeof (gpointer);
4000 if (ainfo->vtsize) {
4001 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
4002 //g_print ("emit_memcpy (prev_sp_ofs: %d, ainfo->offset: %d, soffset: %d)\n", prev_sp_offset, ainfo->offset, soffset);
4003 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, doffset, ARMREG_SP, prev_sp_offset + ainfo->offset);
4005 } else if (ainfo->regtype == RegTypeStructByAddr) {
4006 g_assert_not_reached ();
4007 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
4008 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, inst->inst_offset, ainfo->reg, 0);
4010 g_assert_not_reached ();
4015 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
4016 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->domain);
4017 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
4018 (gpointer)"mono_jit_thread_attach");
4019 code = emit_call_seq (cfg, code);
4022 if (method->save_lmf) {
4023 gboolean get_lmf_fast = FALSE;
4025 #ifdef HAVE_AEABI_READ_TP
4026 gint32 lmf_addr_tls_offset = mono_get_lmf_addr_tls_offset ();
4028 if (lmf_addr_tls_offset != -1) {
4029 get_lmf_fast = TRUE;
4031 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
4032 (gpointer)"__aeabi_read_tp");
4033 code = emit_call_seq (cfg, code);
4035 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, lmf_addr_tls_offset);
4036 get_lmf_fast = TRUE;
4039 if (!get_lmf_fast) {
4040 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
4041 (gpointer)"mono_get_lmf_addr");
4042 code = emit_call_seq (cfg, code);
4044 /* we build the MonoLMF structure on the stack - see mini-arm.h */
4045 /* lmf_offset is the offset from the previous stack pointer,
4046 * alloc_size is the total stack space allocated, so the offset
4047 * of MonoLMF from the current stack ptr is alloc_size - lmf_offset.
4048 * The pointer to the struct is put in r1 (new_lmf).
4049 * r2 is used as scratch
4050 * The callee-saved registers are already in the MonoLMF structure
4052 code = emit_big_add (code, ARMREG_R1, ARMREG_SP, alloc_size - lmf_offset);
4053 /* r0 is the result from mono_get_lmf_addr () */
4054 ARM_STR_IMM (code, ARMREG_R0, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
4055 /* new_lmf->previous_lmf = *lmf_addr */
4056 ARM_LDR_IMM (code, ARMREG_R2, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4057 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4058 /* *(lmf_addr) = r1 */
4059 ARM_STR_IMM (code, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4060 /* Skip method (only needed for trampoline LMF frames) */
4061 ARM_STR_IMM (code, ARMREG_SP, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, ebp));
4062 /* save the current IP */
4063 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_PC);
4064 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, eip));
4068 code = mono_arch_instrument_prolog (cfg, mono_trace_enter_method, code, TRUE);
4070 cfg->code_len = code - cfg->native_code;
4071 g_assert (cfg->code_len < cfg->code_size);
4078 mono_arch_emit_epilog (MonoCompile *cfg)
4080 MonoMethod *method = cfg->method;
4081 int pos, i, rot_amount;
4082 int max_epilog_size = 16 + 20*4;
4085 if (cfg->method->save_lmf)
4086 max_epilog_size += 128;
4088 if (mono_jit_trace_calls != NULL)
4089 max_epilog_size += 50;
4091 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE)
4092 max_epilog_size += 50;
4094 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
4095 cfg->code_size *= 2;
4096 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
4097 mono_jit_stats.code_reallocs++;
4101 * Keep in sync with OP_JMP
4103 code = cfg->native_code + cfg->code_len;
4105 if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) {
4106 code = mono_arch_instrument_epilog (cfg, mono_trace_leave_method, code, TRUE);
4110 if (method->save_lmf) {
4112 /* all but r0-r3, sp and pc */
4113 pos += sizeof (MonoLMF) - (4 * 10);
4115 /* r2 contains the pointer to the current LMF */
4116 code = emit_big_add (code, ARMREG_R2, cfg->frame_reg, cfg->stack_usage - lmf_offset);
4117 /* ip = previous_lmf */
4118 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4120 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
4121 /* *(lmf_addr) = previous_lmf */
4122 ARM_STR_IMM (code, ARMREG_IP, ARMREG_LR, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4123 /* FIXME: speedup: there is no actual need to restore the registers if
4124 * we didn't actually change them (idea from Zoltan).
4127 /* point sp at the registers to restore: 10 is 14 -4, because we skip r0-r3 */
4128 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_R2, (sizeof (MonoLMF) - 10 * sizeof (gulong)));
4129 ARM_POP_NWB (code, 0xaff0); /* restore ip to sp and lr to pc */
4131 if ((i = mono_arm_is_rotated_imm8 (cfg->stack_usage, &rot_amount)) >= 0) {
4132 ARM_ADD_REG_IMM (code, ARMREG_SP, cfg->frame_reg, i, rot_amount);
4134 code = mono_arm_emit_load_imm (code, ARMREG_IP, cfg->stack_usage);
4135 ARM_ADD_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
4137 /* FIXME: add v4 thumb interworking support */
4138 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP) | (1 << ARMREG_PC)));
4141 cfg->code_len = code - cfg->native_code;
4143 g_assert (cfg->code_len < cfg->code_size);
4147 /* remove once throw_exception_by_name is eliminated */
4149 exception_id_by_name (const char *name)
4151 if (strcmp (name, "IndexOutOfRangeException") == 0)
4152 return MONO_EXC_INDEX_OUT_OF_RANGE;
4153 if (strcmp (name, "OverflowException") == 0)
4154 return MONO_EXC_OVERFLOW;
4155 if (strcmp (name, "ArithmeticException") == 0)
4156 return MONO_EXC_ARITHMETIC;
4157 if (strcmp (name, "DivideByZeroException") == 0)
4158 return MONO_EXC_DIVIDE_BY_ZERO;
4159 if (strcmp (name, "InvalidCastException") == 0)
4160 return MONO_EXC_INVALID_CAST;
4161 if (strcmp (name, "NullReferenceException") == 0)
4162 return MONO_EXC_NULL_REF;
4163 if (strcmp (name, "ArrayTypeMismatchException") == 0)
4164 return MONO_EXC_ARRAY_TYPE_MISMATCH;
4165 g_error ("Unknown intrinsic exception %s\n", name);
4170 mono_arch_emit_exceptions (MonoCompile *cfg)
4172 MonoJumpInfo *patch_info;
4175 const guint8* exc_throw_pos [MONO_EXC_INTRINS_NUM] = {NULL};
4176 guint8 exc_throw_found [MONO_EXC_INTRINS_NUM] = {0};
4177 int max_epilog_size = 50;
4179 /* count the number of exception infos */
4182 * make sure we have enough space for exceptions
4184 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
4185 if (patch_info->type == MONO_PATCH_INFO_EXC) {
4186 i = exception_id_by_name (patch_info->data.target);
4187 if (!exc_throw_found [i]) {
4188 max_epilog_size += 32;
4189 exc_throw_found [i] = TRUE;
4194 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
4195 cfg->code_size *= 2;
4196 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
4197 mono_jit_stats.code_reallocs++;
4200 code = cfg->native_code + cfg->code_len;
4202 /* add code to raise exceptions */
4203 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
4204 switch (patch_info->type) {
4205 case MONO_PATCH_INFO_EXC: {
4206 MonoClass *exc_class;
4207 unsigned char *ip = patch_info->ip.i + cfg->native_code;
4209 i = exception_id_by_name (patch_info->data.target);
4210 if (exc_throw_pos [i]) {
4211 arm_patch (ip, exc_throw_pos [i]);
4212 patch_info->type = MONO_PATCH_INFO_NONE;
4215 exc_throw_pos [i] = code;
4217 arm_patch (ip, code);
4219 exc_class = mono_class_from_name (mono_defaults.corlib, "System", patch_info->data.name);
4220 g_assert (exc_class);
4222 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_LR);
4223 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
4224 patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD;
4225 patch_info->data.name = "mono_arch_throw_corlib_exception";
4226 patch_info->ip.i = code - cfg->native_code;
4228 *(guint32*)(gpointer)code = exc_class->type_token;
4238 cfg->code_len = code - cfg->native_code;
4240 g_assert (cfg->code_len < cfg->code_size);
4244 static gboolean tls_offset_inited = FALSE;
4247 mono_arch_setup_jit_tls_data (MonoJitTlsData *tls)
4249 if (!tls_offset_inited) {
4250 tls_offset_inited = TRUE;
4252 lmf_tls_offset = mono_get_lmf_tls_offset ();
4253 lmf_addr_tls_offset = mono_get_lmf_addr_tls_offset ();
4258 mono_arch_free_jit_tls_data (MonoJitTlsData *tls)
4263 mono_arch_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
4270 mono_arch_print_tree (MonoInst *tree, int arity)
4276 mono_arch_get_domain_intrinsic (MonoCompile* cfg)
4278 return mono_get_domain_intrinsic (cfg);
4282 mono_arch_get_thread_intrinsic (MonoCompile* cfg)
4284 return mono_get_thread_intrinsic (cfg);
4288 mono_arch_get_patch_offset (guint8 *code)
4295 mono_arch_flush_register_windows (void)
4300 mono_arch_fixup_jinfo (MonoCompile *cfg)
4304 #ifdef MONO_ARCH_HAVE_IMT
4307 mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoInst *imt_arg)
4309 if (cfg->compile_aot) {
4310 int method_reg = mono_alloc_ireg (cfg);
4313 call->dynamic_imt_arg = TRUE;
4315 MONO_INST_NEW (cfg, ins, OP_AOTCONST);
4316 ins->dreg = method_reg;
4317 ins->inst_p0 = call->method;
4318 ins->inst_c1 = MONO_PATCH_INFO_METHODCONST;
4319 MONO_ADD_INS (cfg->cbb, ins);
4321 mono_call_inst_add_outarg_reg (cfg, call, method_reg, ARMREG_V5, FALSE);
4322 } else if (cfg->generic_context) {
4324 /* Always pass in a register for simplicity */
4325 call->dynamic_imt_arg = TRUE;
4327 cfg->uses_rgctx_reg = TRUE;
4330 mono_call_inst_add_outarg_reg (cfg, call, imt_arg->dreg, ARMREG_V5, FALSE);
4333 int method_reg = mono_alloc_preg (cfg);
4335 MONO_INST_NEW (cfg, ins, OP_PCONST);
4336 ins->inst_p0 = call->method;
4337 ins->dreg = method_reg;
4338 MONO_ADD_INS (cfg->cbb, ins);
4340 mono_call_inst_add_outarg_reg (cfg, call, method_reg, ARMREG_V5, FALSE);
4346 mono_arch_find_imt_method (gpointer *regs, guint8 *code)
4348 guint32 *code_ptr = (guint32*)code;
4350 /* The IMT value is stored in the code stream right after the LDC instruction. */
4351 if (!IS_LDR_PC (code_ptr [0])) {
4352 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]);
4353 g_assert (IS_LDR_PC (code_ptr [0]));
4355 if (code_ptr [1] == 0)
4356 /* This is AOTed code, the IMT method is in V5 */
4357 return (MonoMethod*)regs [ARMREG_V5];
4359 return (MonoMethod*) code_ptr [1];
4363 mono_arch_find_this_argument (gpointer *regs, MonoMethod *method, MonoGenericSharingContext *gsctx)
4365 return mono_arch_get_this_arg_from_call (gsctx, mono_method_signature (method), (gssize*)regs, NULL);
4369 mono_arch_find_static_call_vtable (gpointer *regs, guint8 *code)
4371 return (MonoVTable*) regs [MONO_ARCH_RGCTX_REG];
4374 #define ENABLE_WRONG_METHOD_CHECK 0
4375 #define BASE_SIZE (6 * 4)
4376 #define BSEARCH_ENTRY_SIZE (4 * 4)
4377 #define CMP_SIZE (3 * 4)
4378 #define BRANCH_SIZE (1 * 4)
4379 #define CALL_SIZE (2 * 4)
4380 #define WMC_SIZE (5 * 4)
4381 #define DISTANCE(A, B) (((gint32)(B)) - ((gint32)(A)))
4384 arm_emit_value_and_patch_ldr (arminstr_t *code, arminstr_t *target, guint32 value)
4386 guint32 delta = DISTANCE (target, code);
4388 g_assert (delta >= 0 && delta <= 0xFFF);
4389 *target = *target | delta;
4395 mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count,
4396 gpointer fail_tramp)
4398 int size, i, extra_space = 0;
4399 arminstr_t *code, *start, *vtable_target = NULL;
4400 gboolean large_offsets = FALSE;
4401 guint32 **constant_pool_starts;
4404 constant_pool_starts = g_new0 (guint32*, count);
4407 * We might be called with a fail_tramp from the IMT builder code even if
4408 * MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK is not defined.
4410 //g_assert (!fail_tramp);
4412 for (i = 0; i < count; ++i) {
4413 MonoIMTCheckItem *item = imt_entries [i];
4414 if (item->is_equals) {
4415 if (!arm_is_imm12 (DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]))) {
4416 item->chunk_size += 32;
4417 large_offsets = TRUE;
4420 if (item->check_target_idx) {
4421 if (!item->compare_done)
4422 item->chunk_size += CMP_SIZE;
4423 item->chunk_size += BRANCH_SIZE;
4425 #if ENABLE_WRONG_METHOD_CHECK
4426 item->chunk_size += WMC_SIZE;
4429 item->chunk_size += CALL_SIZE;
4431 item->chunk_size += BSEARCH_ENTRY_SIZE;
4432 imt_entries [item->check_target_idx]->compare_done = TRUE;
4434 size += item->chunk_size;
4438 size += 4 * count; /* The ARM_ADD_REG_IMM to pop the stack */
4440 start = code = mono_domain_code_reserve (domain, size);
4443 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);
4444 for (i = 0; i < count; ++i) {
4445 MonoIMTCheckItem *item = imt_entries [i];
4446 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);
4451 ARM_PUSH4 (code, ARMREG_R0, ARMREG_R1, ARMREG_IP, ARMREG_PC);
4453 ARM_PUSH2 (code, ARMREG_R0, ARMREG_R1);
4454 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, -4);
4455 vtable_target = code;
4456 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
4458 /* R0 == 0 means we are called from AOT code. In this case, V5 contains the IMT method */
4459 ARM_CMP_REG_IMM8 (code, ARMREG_R0, 0);
4460 ARM_MOV_REG_REG_COND (code, ARMREG_R0, ARMREG_V5, ARMCOND_EQ);
4462 for (i = 0; i < count; ++i) {
4463 MonoIMTCheckItem *item = imt_entries [i];
4464 arminstr_t *imt_method = NULL, *vtable_offset_ins = NULL;
4465 gint32 vtable_offset;
4467 item->code_target = (guint8*)code;
4469 if (item->is_equals) {
4470 if (item->check_target_idx) {
4471 if (!item->compare_done) {
4473 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4474 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
4476 item->jmp_code = (guint8*)code;
4477 ARM_B_COND (code, ARMCOND_NE, 0);
4479 /*Enable the commented code to assert on wrong method*/
4480 #if ENABLE_WRONG_METHOD_CHECK
4482 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4483 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
4484 ARM_B_COND (code, ARMCOND_NE, 1);
4490 vtable_offset = DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]);
4491 if (!arm_is_imm12 (vtable_offset)) {
4493 * We need to branch to a computed address but we don't have
4494 * a free register to store it, since IP must contain the
4495 * vtable address. So we push the two values to the stack, and
4496 * load them both using LDM.
4498 /* Compute target address */
4499 vtable_offset_ins = code;
4500 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4501 ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_IP, ARMREG_R1);
4502 /* Save it to the fourth slot */
4503 ARM_STR_IMM (code, ARMREG_R1, ARMREG_SP, 3 * sizeof (gpointer));
4504 /* Restore registers and branch */
4505 ARM_POP4 (code, ARMREG_R0, ARMREG_R1, ARMREG_IP, ARMREG_PC);
4507 code = arm_emit_value_and_patch_ldr (code, vtable_offset_ins, vtable_offset);
4509 ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
4511 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 2 * sizeof (gpointer));
4512 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, vtable_offset);
4516 code = arm_emit_value_and_patch_ldr (code, imt_method, (guint32)item->key);
4518 /*must emit after unconditional branch*/
4519 if (vtable_target) {
4520 code = arm_emit_value_and_patch_ldr (code, vtable_target, (guint32)vtable);
4521 item->chunk_size += 4;
4522 vtable_target = NULL;
4525 /*We reserve the space for bsearch IMT values after the first entry with an absolute jump*/
4526 constant_pool_starts [i] = code;
4528 code += extra_space;
4532 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4533 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
4535 item->jmp_code = (guint8*)code;
4536 ARM_B_COND (code, ARMCOND_GE, 0);
4541 for (i = 0; i < count; ++i) {
4542 MonoIMTCheckItem *item = imt_entries [i];
4543 if (item->jmp_code) {
4544 if (item->check_target_idx)
4545 arm_patch (item->jmp_code, imt_entries [item->check_target_idx]->code_target);
4547 if (i > 0 && item->is_equals) {
4549 arminstr_t *space_start = constant_pool_starts [i];
4550 for (j = i - 1; j >= 0 && !imt_entries [j]->is_equals; --j) {
4551 space_start = arm_emit_value_and_patch_ldr (space_start, (arminstr_t*)imt_entries [j]->code_target, (guint32)imt_entries [j]->key);
4558 char *buff = g_strdup_printf ("thunk_for_class_%s_%s_entries_%d", vtable->klass->name_space, vtable->klass->name, count);
4559 mono_disassemble_code (NULL, (guint8*)start, size, buff);
4564 g_free (constant_pool_starts);
4566 mono_arch_flush_icache ((guint8*)start, size);
4567 mono_stats.imt_thunks_size += code - start;
4569 g_assert (DISTANCE (start, code) <= size);
4576 mono_arch_context_get_int_reg (MonoContext *ctx, int reg)
4578 if (reg >= 4 && reg <= 11)
4579 return (gpointer)ctx->regs [reg - 4];
4580 else if (reg == ARMREG_IP)
4581 return (gpointer)ctx->regs [8];
4582 else if (reg == ARMREG_LR)
4583 return (gpointer)ctx->regs [9];
4585 g_assert_not_reached ();