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 mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target)
361 guint8 *code, *start;
363 /* FIXME: Support more cases */
364 if (MONO_TYPE_ISSTRUCT (sig->ret))
368 static guint8* cached = NULL;
369 mono_mini_arch_lock ();
371 mono_mini_arch_unlock ();
375 start = code = mono_global_codeman_reserve (12);
377 /* Replace the this argument with the target */
378 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, method_ptr));
379 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, target));
380 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
382 g_assert ((code - start) <= 12);
384 mono_arch_flush_icache (start, 12);
386 mono_mini_arch_unlock ();
389 static guint8* cache [MAX_ARCH_DELEGATE_PARAMS + 1] = {NULL};
392 if (sig->param_count > MAX_ARCH_DELEGATE_PARAMS)
394 for (i = 0; i < sig->param_count; ++i)
395 if (!mono_is_regsize_var (sig->params [i]))
398 mono_mini_arch_lock ();
399 code = cache [sig->param_count];
401 mono_mini_arch_unlock ();
405 size = 8 + sig->param_count * 4;
406 start = code = mono_global_codeman_reserve (size);
408 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, method_ptr));
409 /* slide down the arguments */
410 for (i = 0; i < sig->param_count; ++i) {
411 ARM_MOV_REG_REG (code, (ARMREG_R0 + i), (ARMREG_R0 + i + 1));
413 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
415 g_assert ((code - start) <= size);
417 mono_arch_flush_icache (start, size);
418 cache [sig->param_count] = start;
419 mono_mini_arch_unlock ();
427 mono_arch_get_this_arg_from_call (MonoGenericSharingContext *gsctx, MonoMethodSignature *sig, gssize *regs, guint8 *code)
429 /* FIXME: handle returning a struct */
430 if (MONO_TYPE_ISSTRUCT (sig->ret))
431 return (gpointer)regs [ARMREG_R1];
432 return (gpointer)regs [ARMREG_R0];
436 * Initialize the cpu to execute managed code.
439 mono_arch_cpu_init (void)
444 * Initialize architecture specific code.
447 mono_arch_init (void)
449 InitializeCriticalSection (&mini_arch_mutex);
453 * Cleanup architecture specific code.
456 mono_arch_cleanup (void)
461 * This function returns the optimizations supported on this cpu.
464 mono_arch_cpu_optimizazions (guint32 *exclude_mask)
468 thumb_supported = TRUE;
473 FILE *file = fopen ("/proc/cpuinfo", "r");
475 while ((line = fgets (buf, 512, file))) {
476 if (strncmp (line, "Processor", 9) == 0) {
477 char *ver = strstr (line, "(v");
478 if (ver && (ver [2] == '5' || ver [2] == '6' || ver [2] == '7')) {
483 if (strncmp (line, "Features", 8) == 0) {
484 char *th = strstr (line, "thumb");
486 thumb_supported = TRUE;
494 /*printf ("features: v5: %d, thumb: %d\n", v5_supported, thumb_supported);*/
498 /* no arm-specific optimizations yet */
504 is_regsize_var (MonoType *t) {
507 t = mini_type_get_underlying_type (NULL, t);
514 case MONO_TYPE_FNPTR:
516 case MONO_TYPE_OBJECT:
517 case MONO_TYPE_STRING:
518 case MONO_TYPE_CLASS:
519 case MONO_TYPE_SZARRAY:
520 case MONO_TYPE_ARRAY:
522 case MONO_TYPE_GENERICINST:
523 if (!mono_type_generic_inst_is_valuetype (t))
526 case MONO_TYPE_VALUETYPE:
533 mono_arch_get_allocatable_int_vars (MonoCompile *cfg)
538 for (i = 0; i < cfg->num_varinfo; i++) {
539 MonoInst *ins = cfg->varinfo [i];
540 MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
543 if (vmv->range.first_use.abs_pos >= vmv->range.last_use.abs_pos)
546 if (ins->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT) || (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG))
549 /* we can only allocate 32 bit values */
550 if (is_regsize_var (ins->inst_vtype)) {
551 g_assert (MONO_VARINFO (cfg, i)->reg == -1);
552 g_assert (i == vmv->idx);
553 vars = mono_varlist_insert_sorted (cfg, vars, vmv, FALSE);
560 #define USE_EXTRA_TEMPS 0
563 mono_arch_get_global_int_regs (MonoCompile *cfg)
566 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V1));
567 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V2));
568 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V3));
569 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V4));
570 if (!(cfg->compile_aot || cfg->uses_rgctx_reg))
571 /* V5 is reserved for passing the vtable/rgctx/IMT method */
572 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V5));
573 /*regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V6));*/
574 /*regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V7));*/
580 * mono_arch_regalloc_cost:
582 * Return the cost, in number of memory references, of the action of
583 * allocating the variable VMV into a register during global register
587 mono_arch_regalloc_cost (MonoCompile *cfg, MonoMethodVar *vmv)
593 #ifndef __GNUC_PREREQ
594 #define __GNUC_PREREQ(maj, min) (0)
598 mono_arch_flush_icache (guint8 *code, gint size)
601 sys_icache_invalidate (code, size);
602 #elif __GNUC_PREREQ(4, 1)
603 __clear_cache (code, code + size);
604 #elif defined(PLATFORM_ANDROID)
605 const int syscall = 0xf0002;
613 : "r" (code), "r" (code + size), "r" (syscall)
617 __asm __volatile ("mov r0, %0\n"
620 "swi 0x9f0002 @ sys_cacheflush"
622 : "r" (code), "r" (code + size), "r" (0)
623 : "r0", "r1", "r3" );
638 guint16 vtsize; /* in param area */
640 guint8 regtype : 4; /* 0 general, 1 basereg, 2 floating point register, see RegType* */
641 guint8 size : 4; /* 1, 2, 4, 8, or regs used by RegTypeStructByVal */
656 add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple)
659 if (*gr > ARMREG_R3) {
660 ainfo->offset = *stack_size;
661 ainfo->reg = ARMREG_SP; /* in the caller */
662 ainfo->regtype = RegTypeBase;
673 /* first word in r3 and the second on the stack */
674 ainfo->offset = *stack_size;
675 ainfo->reg = ARMREG_SP; /* in the caller */
676 ainfo->regtype = RegTypeBaseGen;
678 } else if (*gr >= ARMREG_R3) {
683 ainfo->offset = *stack_size;
684 ainfo->reg = ARMREG_SP; /* in the caller */
685 ainfo->regtype = RegTypeBase;
700 calculate_sizes (MonoMethodSignature *sig, gboolean is_pinvoke)
703 int n = sig->hasthis + sig->param_count;
704 MonoType *simpletype;
705 guint32 stack_size = 0;
706 CallInfo *cinfo = g_malloc0 (sizeof (CallInfo) + sizeof (ArgInfo) * n);
710 /* FIXME: handle returning a struct */
711 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
712 add_general (&gr, &stack_size, &cinfo->ret, TRUE);
713 cinfo->struct_ret = ARMREG_R0;
718 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
721 DEBUG(printf("params: %d\n", sig->param_count));
722 for (i = 0; i < sig->param_count; ++i) {
723 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
724 /* Prevent implicit arguments and sig_cookie from
725 being passed in registers */
727 /* Emit the signature cookie just before the implicit arguments */
728 add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE);
730 DEBUG(printf("param %d: ", i));
731 if (sig->params [i]->byref) {
732 DEBUG(printf("byref\n"));
733 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
737 simpletype = mini_type_get_underlying_type (NULL, sig->params [i]);
738 switch (simpletype->type) {
739 case MONO_TYPE_BOOLEAN:
742 cinfo->args [n].size = 1;
743 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
749 cinfo->args [n].size = 2;
750 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
755 cinfo->args [n].size = 4;
756 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
762 case MONO_TYPE_FNPTR:
763 case MONO_TYPE_CLASS:
764 case MONO_TYPE_OBJECT:
765 case MONO_TYPE_STRING:
766 case MONO_TYPE_SZARRAY:
767 case MONO_TYPE_ARRAY:
769 cinfo->args [n].size = sizeof (gpointer);
770 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
773 case MONO_TYPE_GENERICINST:
774 if (!mono_type_generic_inst_is_valuetype (sig->params [i])) {
775 cinfo->args [n].size = sizeof (gpointer);
776 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
781 case MONO_TYPE_TYPEDBYREF:
782 case MONO_TYPE_VALUETYPE: {
787 if (simpletype->type == MONO_TYPE_TYPEDBYREF) {
788 size = sizeof (MonoTypedRef);
790 MonoClass *klass = mono_class_from_mono_type (sig->params [i]);
792 size = mono_class_native_size (klass, NULL);
794 size = mono_class_value_size (klass, NULL);
796 DEBUG(printf ("load %d bytes struct\n",
797 mono_class_native_size (sig->params [i]->data.klass, NULL)));
800 align_size += (sizeof (gpointer) - 1);
801 align_size &= ~(sizeof (gpointer) - 1);
802 nwords = (align_size + sizeof (gpointer) -1 ) / sizeof (gpointer);
803 cinfo->args [n].regtype = RegTypeStructByVal;
804 /* FIXME: align gr and stack_size if needed */
805 if (gr > ARMREG_R3) {
806 cinfo->args [n].size = 0;
807 cinfo->args [n].vtsize = nwords;
809 int rest = ARMREG_R3 - gr + 1;
810 int n_in_regs = rest >= nwords? nwords: rest;
811 cinfo->args [n].size = n_in_regs;
812 cinfo->args [n].vtsize = nwords - n_in_regs;
813 cinfo->args [n].reg = gr;
816 cinfo->args [n].offset = stack_size;
817 /*g_print ("offset for arg %d at %d\n", n, stack_size);*/
818 stack_size += nwords * sizeof (gpointer);
825 cinfo->args [n].size = 8;
826 add_general (&gr, &stack_size, cinfo->args + n, FALSE);
830 g_error ("Can't trampoline 0x%x", sig->params [i]->type);
835 simpletype = mini_type_get_underlying_type (NULL, sig->ret);
836 switch (simpletype->type) {
837 case MONO_TYPE_BOOLEAN:
848 case MONO_TYPE_FNPTR:
849 case MONO_TYPE_CLASS:
850 case MONO_TYPE_OBJECT:
851 case MONO_TYPE_SZARRAY:
852 case MONO_TYPE_ARRAY:
853 case MONO_TYPE_STRING:
854 cinfo->ret.reg = ARMREG_R0;
858 cinfo->ret.reg = ARMREG_R0;
862 cinfo->ret.reg = ARMREG_R0;
863 /* FIXME: cinfo->ret.reg = ???;
864 cinfo->ret.regtype = RegTypeFP;*/
866 case MONO_TYPE_GENERICINST:
867 if (!mono_type_generic_inst_is_valuetype (sig->ret)) {
868 cinfo->ret.reg = ARMREG_R0;
872 case MONO_TYPE_VALUETYPE:
874 case MONO_TYPE_TYPEDBYREF:
878 g_error ("Can't handle as return value 0x%x", sig->ret->type);
882 /* align stack size to 8 */
883 DEBUG (printf (" stack size: %d (%d)\n", (stack_size + 15) & ~15, stack_size));
884 stack_size = (stack_size + 7) & ~7;
886 cinfo->stack_usage = stack_size;
892 * Set var information according to the calling convention. arm version.
893 * The locals var stuff should most likely be split in another method.
896 mono_arch_allocate_vars (MonoCompile *cfg)
898 MonoMethodSignature *sig;
899 MonoMethodHeader *header;
901 int i, offset, size, align, curinst;
902 int frame_reg = ARMREG_FP;
904 /* FIXME: this will change when we use FP as gcc does */
905 cfg->flags |= MONO_CFG_HAS_SPILLUP;
907 /* allow room for the vararg method args: void* and long/double */
908 if (mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method))
909 cfg->param_area = MAX (cfg->param_area, sizeof (gpointer)*8);
911 header = mono_method_get_header (cfg->method);
914 * We use the frame register also for any method that has
915 * exception clauses. This way, when the handlers are called,
916 * the code will reference local variables using the frame reg instead of
917 * the stack pointer: if we had to restore the stack pointer, we'd
918 * corrupt the method frames that are already on the stack (since
919 * filters get called before stack unwinding happens) when the filter
920 * code would call any method (this also applies to finally etc.).
922 if ((cfg->flags & MONO_CFG_HAS_ALLOCA) || header->num_clauses)
923 frame_reg = ARMREG_FP;
924 cfg->frame_reg = frame_reg;
925 if (frame_reg != ARMREG_SP) {
926 cfg->used_int_regs |= 1 << frame_reg;
929 if (!cfg->compile_aot || cfg->uses_rgctx_reg)
930 /* V5 is reserved for passing the vtable/rgctx/IMT method */
931 cfg->used_int_regs |= (1 << ARMREG_V5);
933 sig = mono_method_signature (cfg->method);
937 if (!MONO_TYPE_ISSTRUCT (sig->ret)) {
938 /* FIXME: handle long and FP values */
939 switch (mini_type_get_underlying_type (NULL, sig->ret)->type) {
943 cfg->ret->opcode = OP_REGVAR;
944 cfg->ret->inst_c0 = ARMREG_R0;
948 /* local vars are at a positive offset from the stack pointer */
950 * also note that if the function uses alloca, we use FP
951 * to point at the local variables.
953 offset = 0; /* linkage area */
954 /* align the offset to 16 bytes: not sure this is needed here */
956 //offset &= ~(8 - 1);
958 /* add parameter area size for called functions */
959 offset += cfg->param_area;
962 if (cfg->flags & MONO_CFG_HAS_FPOUT)
965 /* allow room to save the return value */
966 if (mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method))
969 /* the MonoLMF structure is stored just below the stack pointer */
971 if (sig->call_convention == MONO_CALL_VARARG) {
975 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
976 inst = cfg->vret_addr;
977 offset += sizeof(gpointer) - 1;
978 offset &= ~(sizeof(gpointer) - 1);
979 inst->inst_offset = offset;
980 inst->opcode = OP_REGOFFSET;
981 inst->inst_basereg = frame_reg;
982 if (G_UNLIKELY (cfg->verbose_level > 1)) {
983 printf ("vret_addr =");
984 mono_print_ins (cfg->vret_addr);
986 offset += sizeof(gpointer);
987 if (sig->call_convention == MONO_CALL_VARARG)
988 cfg->sig_cookie += sizeof (gpointer);
991 curinst = cfg->locals_start;
992 for (i = curinst; i < cfg->num_varinfo; ++i) {
993 inst = cfg->varinfo [i];
994 if ((inst->flags & MONO_INST_IS_DEAD) || inst->opcode == OP_REGVAR)
997 /* inst->backend.is_pinvoke indicates native sized value types, this is used by the
998 * pinvoke wrappers when they call functions returning structure */
999 if (inst->backend.is_pinvoke && MONO_TYPE_ISSTRUCT (inst->inst_vtype) && inst->inst_vtype->type != MONO_TYPE_TYPEDBYREF) {
1001 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), &ualign);
1005 size = mono_type_size (inst->inst_vtype, &align);
1007 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
1008 * since it loads/stores misaligned words, which don't do the right thing.
1010 if (align < 4 && size >= 4)
1012 offset += align - 1;
1013 offset &= ~(align - 1);
1014 inst->inst_offset = offset;
1015 inst->opcode = OP_REGOFFSET;
1016 inst->inst_basereg = frame_reg;
1018 //g_print ("allocating local %d to %d\n", i, inst->inst_offset);
1023 inst = cfg->args [curinst];
1024 if (inst->opcode != OP_REGVAR) {
1025 inst->opcode = OP_REGOFFSET;
1026 inst->inst_basereg = frame_reg;
1027 offset += sizeof (gpointer) - 1;
1028 offset &= ~(sizeof (gpointer) - 1);
1029 inst->inst_offset = offset;
1030 offset += sizeof (gpointer);
1031 if (sig->call_convention == MONO_CALL_VARARG)
1032 cfg->sig_cookie += sizeof (gpointer);
1037 for (i = 0; i < sig->param_count; ++i) {
1038 inst = cfg->args [curinst];
1039 if (inst->opcode != OP_REGVAR) {
1040 inst->opcode = OP_REGOFFSET;
1041 inst->inst_basereg = frame_reg;
1042 size = mono_type_size (sig->params [i], &align);
1043 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
1044 * since it loads/stores misaligned words, which don't do the right thing.
1046 if (align < 4 && size >= 4)
1048 offset += align - 1;
1049 offset &= ~(align - 1);
1050 inst->inst_offset = offset;
1052 if ((sig->call_convention == MONO_CALL_VARARG) && (i < sig->sentinelpos))
1053 cfg->sig_cookie += size;
1058 /* align the offset to 8 bytes */
1063 cfg->stack_offset = offset;
1067 mono_arch_create_vars (MonoCompile *cfg)
1069 MonoMethodSignature *sig;
1071 sig = mono_method_signature (cfg->method);
1073 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
1074 cfg->vret_addr = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_ARG);
1075 if (G_UNLIKELY (cfg->verbose_level > 1)) {
1076 printf ("vret_addr = ");
1077 mono_print_ins (cfg->vret_addr);
1083 mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
1086 MonoMethodSignature *sig;
1090 sig = call->signature;
1091 n = sig->param_count + sig->hasthis;
1093 cinfo = calculate_sizes (sig, sig->pinvoke);
1095 for (i = 0; i < n; ++i) {
1096 ArgInfo *ainfo = cinfo->args + i;
1099 if (i >= sig->hasthis)
1100 t = sig->params [i - sig->hasthis];
1102 t = &mono_defaults.int_class->byval_arg;
1103 t = mini_type_get_underlying_type (NULL, t);
1105 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
1110 in = call->args [i];
1112 switch (ainfo->regtype) {
1113 case RegTypeGeneral:
1114 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1115 MONO_INST_NEW (cfg, ins, OP_MOVE);
1116 ins->dreg = mono_alloc_ireg (cfg);
1117 ins->sreg1 = in->dreg + 1;
1118 MONO_ADD_INS (cfg->cbb, ins);
1119 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1121 MONO_INST_NEW (cfg, ins, OP_MOVE);
1122 ins->dreg = mono_alloc_ireg (cfg);
1123 ins->sreg1 = in->dreg + 2;
1124 MONO_ADD_INS (cfg->cbb, ins);
1125 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg + 1, FALSE);
1126 } else if (!t->byref && ((t->type == MONO_TYPE_R8) || (t->type == MONO_TYPE_R4))) {
1127 #ifndef MONO_ARCH_SOFT_FLOAT
1131 if (ainfo->size == 4) {
1132 #ifdef MONO_ARCH_SOFT_FLOAT
1133 /* mono_emit_call_args () have already done the r8->r4 conversion */
1134 /* The converted value is in an int vreg */
1135 MONO_INST_NEW (cfg, ins, OP_MOVE);
1136 ins->dreg = mono_alloc_ireg (cfg);
1137 ins->sreg1 = in->dreg;
1138 MONO_ADD_INS (cfg->cbb, ins);
1139 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1141 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1142 creg = mono_alloc_ireg (cfg);
1143 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1144 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg, FALSE);
1147 #ifdef MONO_ARCH_SOFT_FLOAT
1148 MONO_INST_NEW (cfg, ins, OP_FGETLOW32);
1149 ins->dreg = mono_alloc_ireg (cfg);
1150 ins->sreg1 = in->dreg;
1151 MONO_ADD_INS (cfg->cbb, ins);
1152 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1154 MONO_INST_NEW (cfg, ins, OP_FGETHIGH32);
1155 ins->dreg = mono_alloc_ireg (cfg);
1156 ins->sreg1 = in->dreg;
1157 MONO_ADD_INS (cfg->cbb, ins);
1158 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg + 1, FALSE);
1160 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1161 creg = mono_alloc_ireg (cfg);
1162 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1163 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg, FALSE);
1164 creg = mono_alloc_ireg (cfg);
1165 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8 + 4));
1166 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg + 1, FALSE);
1169 cfg->flags |= MONO_CFG_HAS_FPOUT;
1171 MONO_INST_NEW (cfg, ins, OP_MOVE);
1172 ins->dreg = mono_alloc_ireg (cfg);
1173 ins->sreg1 = in->dreg;
1174 MONO_ADD_INS (cfg->cbb, ins);
1176 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1179 case RegTypeStructByAddr:
1182 /* FIXME: where si the data allocated? */
1183 arg->backend.reg3 = ainfo->reg;
1184 call->used_iregs |= 1 << ainfo->reg;
1185 g_assert_not_reached ();
1188 case RegTypeStructByVal:
1189 MONO_INST_NEW (cfg, ins, OP_OUTARG_VT);
1190 ins->opcode = OP_OUTARG_VT;
1191 ins->sreg1 = in->dreg;
1192 ins->klass = in->klass;
1193 ins->inst_p0 = call;
1194 ins->inst_p1 = mono_mempool_alloc (cfg->mempool, sizeof (ArgInfo));
1195 memcpy (ins->inst_p1, ainfo, sizeof (ArgInfo));
1196 MONO_ADD_INS (cfg->cbb, ins);
1199 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1200 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1201 } else if (!t->byref && ((t->type == MONO_TYPE_R4) || (t->type == MONO_TYPE_R8))) {
1202 if (t->type == MONO_TYPE_R8) {
1203 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1205 #ifdef MONO_ARCH_SOFT_FLOAT
1206 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1208 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1212 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1215 case RegTypeBaseGen:
1216 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1217 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);
1218 MONO_INST_NEW (cfg, ins, OP_MOVE);
1219 ins->dreg = mono_alloc_ireg (cfg);
1220 ins->sreg1 = G_BYTE_ORDER == G_BIG_ENDIAN ? in->dreg + 2 : in->dreg + 1;
1221 MONO_ADD_INS (cfg->cbb, ins);
1222 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ARMREG_R3, FALSE);
1223 } else if (!t->byref && (t->type == MONO_TYPE_R8)) {
1226 #ifdef MONO_ARCH_SOFT_FLOAT
1227 g_assert_not_reached ();
1230 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1231 creg = mono_alloc_ireg (cfg);
1232 mono_call_inst_add_outarg_reg (cfg, call, creg, ARMREG_R3, FALSE);
1233 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1234 creg = mono_alloc_ireg (cfg);
1235 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 4));
1236 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, creg);
1237 cfg->flags |= MONO_CFG_HAS_FPOUT;
1239 g_assert_not_reached ();
1246 arg->backend.reg3 = ainfo->reg;
1247 /* FP args are passed in int regs */
1248 call->used_iregs |= 1 << ainfo->reg;
1249 if (ainfo->size == 8) {
1250 arg->opcode = OP_OUTARG_R8;
1251 call->used_iregs |= 1 << (ainfo->reg + 1);
1253 arg->opcode = OP_OUTARG_R4;
1256 cfg->flags |= MONO_CFG_HAS_FPOUT;
1260 g_assert_not_reached ();
1264 if (sig->ret && MONO_TYPE_ISSTRUCT (sig->ret)) {
1267 MONO_INST_NEW (cfg, vtarg, OP_MOVE);
1268 vtarg->sreg1 = call->vret_var->dreg;
1269 vtarg->dreg = mono_alloc_preg (cfg);
1270 MONO_ADD_INS (cfg->cbb, vtarg);
1272 mono_call_inst_add_outarg_reg (cfg, call, vtarg->dreg, cinfo->ret.reg, FALSE);
1275 call->stack_usage = cinfo->stack_usage;
1281 mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src)
1283 MonoCallInst *call = (MonoCallInst*)ins->inst_p0;
1284 ArgInfo *ainfo = ins->inst_p1;
1285 int ovf_size = ainfo->vtsize;
1286 int doffset = ainfo->offset;
1287 int i, soffset, dreg;
1290 for (i = 0; i < ainfo->size; ++i) {
1291 dreg = mono_alloc_ireg (cfg);
1292 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, src->dreg, soffset);
1293 mono_call_inst_add_outarg_reg (cfg, call, dreg, ainfo->reg + i, FALSE);
1294 soffset += sizeof (gpointer);
1296 //g_print ("vt size: %d at R%d + %d\n", doffset, vt->inst_basereg, vt->inst_offset);
1298 mini_emit_memcpy (cfg, ARMREG_SP, doffset, src->dreg, soffset, ovf_size * sizeof (gpointer), 0);
1302 mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val)
1304 MonoType *ret = mini_type_get_underlying_type (cfg->generic_sharing_context, mono_method_signature (method)->ret);
1307 if (ret->type == MONO_TYPE_I8 || ret->type == MONO_TYPE_U8) {
1310 MONO_INST_NEW (cfg, ins, OP_SETLRET);
1311 ins->sreg1 = val->dreg + 1;
1312 ins->sreg2 = val->dreg + 2;
1313 MONO_ADD_INS (cfg->cbb, ins);
1316 #ifdef MONO_ARCH_SOFT_FLOAT
1317 if (ret->type == MONO_TYPE_R8) {
1320 MONO_INST_NEW (cfg, ins, OP_SETFRET);
1321 ins->dreg = cfg->ret->dreg;
1322 ins->sreg1 = val->dreg;
1323 MONO_ADD_INS (cfg->cbb, ins);
1326 if (ret->type == MONO_TYPE_R4) {
1327 /* Already converted to an int in method_to_ir () */
1328 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
1331 #elif defined(ARM_FPU_VFP)
1332 if (ret->type == MONO_TYPE_R8 || ret->type == MONO_TYPE_R4) {
1335 MONO_INST_NEW (cfg, ins, OP_SETFRET);
1336 ins->dreg = cfg->ret->dreg;
1337 ins->sreg1 = val->dreg;
1338 MONO_ADD_INS (cfg->cbb, ins);
1342 if (ret->type == MONO_TYPE_R4 || ret->type == MONO_TYPE_R8) {
1343 MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, cfg->ret->dreg, val->dreg);
1350 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
1354 mono_arch_is_inst_imm (gint64 imm)
1360 * Allow tracing to work with this interface (with an optional argument)
1364 mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
1368 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1369 ARM_MOV_REG_IMM8 (code, ARMREG_R1, 0); /* NULL ebp for now */
1370 code = mono_arm_emit_load_imm (code, ARMREG_R2, (guint32)func);
1371 code = emit_call_reg (code, ARMREG_R2);
1384 mono_arch_instrument_epilog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
1387 int save_mode = SAVE_NONE;
1389 MonoMethod *method = cfg->method;
1390 int rtype = mini_type_get_underlying_type (cfg->generic_sharing_context, mono_method_signature (method)->ret)->type;
1391 int save_offset = cfg->param_area;
1395 offset = code - cfg->native_code;
1396 /* we need about 16 instructions */
1397 if (offset > (cfg->code_size - 16 * 4)) {
1398 cfg->code_size *= 2;
1399 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
1400 code = cfg->native_code + offset;
1403 case MONO_TYPE_VOID:
1404 /* special case string .ctor icall */
1405 if (strcmp (".ctor", method->name) && method->klass == mono_defaults.string_class)
1406 save_mode = SAVE_ONE;
1408 save_mode = SAVE_NONE;
1412 save_mode = SAVE_TWO;
1416 save_mode = SAVE_FP;
1418 case MONO_TYPE_VALUETYPE:
1419 save_mode = SAVE_STRUCT;
1422 save_mode = SAVE_ONE;
1426 switch (save_mode) {
1428 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1429 ARM_STR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1430 if (enable_arguments) {
1431 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_R1);
1432 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1436 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1437 if (enable_arguments) {
1438 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1442 /* FIXME: what reg? */
1443 if (enable_arguments) {
1444 /* FIXME: what reg? */
1448 if (enable_arguments) {
1449 /* FIXME: get the actual address */
1450 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1458 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1459 code = mono_arm_emit_load_imm (code, ARMREG_IP, (guint32)func);
1460 code = emit_call_reg (code, ARMREG_IP);
1462 switch (save_mode) {
1464 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1465 ARM_LDR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1468 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1482 * The immediate field for cond branches is big enough for all reasonable methods
1484 #define EMIT_COND_BRANCH_FLAGS(ins,condcode) \
1485 if (ins->flags & MONO_INST_BRLABEL) { \
1486 if (0 && ins->inst_i0->inst_c0) { \
1487 ARM_B_COND (code, (condcode), (code - cfg->native_code + ins->inst_i0->inst_c0) & 0xffffff); \
1489 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_LABEL, ins->inst_i0); \
1490 ARM_B_COND (code, (condcode), 0); \
1493 if (0 && ins->inst_true_bb->native_offset) { \
1494 ARM_B_COND (code, (condcode), (code - cfg->native_code + ins->inst_true_bb->native_offset) & 0xffffff); \
1496 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_true_bb); \
1497 ARM_B_COND (code, (condcode), 0); \
1501 #define EMIT_COND_BRANCH(ins,cond) EMIT_COND_BRANCH_FLAGS(ins, branch_cc_table [(cond)])
1503 /* emit an exception if condition is fail
1505 * We assign the extra code used to throw the implicit exceptions
1506 * to cfg->bb_exit as far as the big branch handling is concerned
1508 #define EMIT_COND_SYSTEM_EXCEPTION_FLAGS(condcode,exc_name) \
1510 mono_add_patch_info (cfg, code - cfg->native_code, \
1511 MONO_PATCH_INFO_EXC, exc_name); \
1512 ARM_BL_COND (code, (condcode), 0); \
1515 #define EMIT_COND_SYSTEM_EXCEPTION(cond,exc_name) EMIT_COND_SYSTEM_EXCEPTION_FLAGS(branch_cc_table [(cond)], (exc_name))
1518 mono_arch_peephole_pass_1 (MonoCompile *cfg, MonoBasicBlock *bb)
1523 mono_arch_peephole_pass_2 (MonoCompile *cfg, MonoBasicBlock *bb)
1525 MonoInst *ins, *n, *last_ins = NULL;
1527 MONO_BB_FOR_EACH_INS_SAFE (bb, n, ins) {
1528 switch (ins->opcode) {
1531 /* Already done by an arch-independent pass */
1533 case OP_LOAD_MEMBASE:
1534 case OP_LOADI4_MEMBASE:
1536 * OP_STORE_MEMBASE_REG reg, offset(basereg)
1537 * OP_LOAD_MEMBASE offset(basereg), reg
1539 if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG
1540 || last_ins->opcode == OP_STORE_MEMBASE_REG) &&
1541 ins->inst_basereg == last_ins->inst_destbasereg &&
1542 ins->inst_offset == last_ins->inst_offset) {
1543 if (ins->dreg == last_ins->sreg1) {
1544 MONO_DELETE_INS (bb, ins);
1547 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1548 ins->opcode = OP_MOVE;
1549 ins->sreg1 = last_ins->sreg1;
1553 * Note: reg1 must be different from the basereg in the second load
1554 * OP_LOAD_MEMBASE offset(basereg), reg1
1555 * OP_LOAD_MEMBASE offset(basereg), reg2
1557 * OP_LOAD_MEMBASE offset(basereg), reg1
1558 * OP_MOVE reg1, reg2
1560 } if (last_ins && (last_ins->opcode == OP_LOADI4_MEMBASE
1561 || last_ins->opcode == OP_LOAD_MEMBASE) &&
1562 ins->inst_basereg != last_ins->dreg &&
1563 ins->inst_basereg == last_ins->inst_basereg &&
1564 ins->inst_offset == last_ins->inst_offset) {
1566 if (ins->dreg == last_ins->dreg) {
1567 MONO_DELETE_INS (bb, ins);
1570 ins->opcode = OP_MOVE;
1571 ins->sreg1 = last_ins->dreg;
1574 //g_assert_not_reached ();
1578 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1579 * OP_LOAD_MEMBASE offset(basereg), reg
1581 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1582 * OP_ICONST reg, imm
1584 } else if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_IMM
1585 || last_ins->opcode == OP_STORE_MEMBASE_IMM) &&
1586 ins->inst_basereg == last_ins->inst_destbasereg &&
1587 ins->inst_offset == last_ins->inst_offset) {
1588 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1589 ins->opcode = OP_ICONST;
1590 ins->inst_c0 = last_ins->inst_imm;
1591 g_assert_not_reached (); // check this rule
1595 case OP_LOADU1_MEMBASE:
1596 case OP_LOADI1_MEMBASE:
1597 if (last_ins && (last_ins->opcode == OP_STOREI1_MEMBASE_REG) &&
1598 ins->inst_basereg == last_ins->inst_destbasereg &&
1599 ins->inst_offset == last_ins->inst_offset) {
1600 ins->opcode = (ins->opcode == OP_LOADI1_MEMBASE) ? OP_ICONV_TO_I1 : OP_ICONV_TO_U1;
1601 ins->sreg1 = last_ins->sreg1;
1604 case OP_LOADU2_MEMBASE:
1605 case OP_LOADI2_MEMBASE:
1606 if (last_ins && (last_ins->opcode == OP_STOREI2_MEMBASE_REG) &&
1607 ins->inst_basereg == last_ins->inst_destbasereg &&
1608 ins->inst_offset == last_ins->inst_offset) {
1609 ins->opcode = (ins->opcode == OP_LOADI2_MEMBASE) ? OP_ICONV_TO_I2 : OP_ICONV_TO_U2;
1610 ins->sreg1 = last_ins->sreg1;
1614 ins->opcode = OP_MOVE;
1618 if (ins->dreg == ins->sreg1) {
1619 MONO_DELETE_INS (bb, ins);
1623 * OP_MOVE sreg, dreg
1624 * OP_MOVE dreg, sreg
1626 if (last_ins && last_ins->opcode == OP_MOVE &&
1627 ins->sreg1 == last_ins->dreg &&
1628 ins->dreg == last_ins->sreg1) {
1629 MONO_DELETE_INS (bb, ins);
1637 bb->last_ins = last_ins;
1641 * the branch_cc_table should maintain the order of these
1655 branch_cc_table [] = {
1669 #define NEW_INS(cfg,dest,op) do { \
1670 MONO_INST_NEW ((cfg), (dest), (op)); \
1671 mono_bblock_insert_before_ins (bb, ins, (dest)); \
1675 map_to_reg_reg_op (int op)
1684 case OP_COMPARE_IMM:
1686 case OP_ICOMPARE_IMM:
1700 case OP_LOAD_MEMBASE:
1701 return OP_LOAD_MEMINDEX;
1702 case OP_LOADI4_MEMBASE:
1703 return OP_LOADI4_MEMINDEX;
1704 case OP_LOADU4_MEMBASE:
1705 return OP_LOADU4_MEMINDEX;
1706 case OP_LOADU1_MEMBASE:
1707 return OP_LOADU1_MEMINDEX;
1708 case OP_LOADI2_MEMBASE:
1709 return OP_LOADI2_MEMINDEX;
1710 case OP_LOADU2_MEMBASE:
1711 return OP_LOADU2_MEMINDEX;
1712 case OP_LOADI1_MEMBASE:
1713 return OP_LOADI1_MEMINDEX;
1714 case OP_STOREI1_MEMBASE_REG:
1715 return OP_STOREI1_MEMINDEX;
1716 case OP_STOREI2_MEMBASE_REG:
1717 return OP_STOREI2_MEMINDEX;
1718 case OP_STOREI4_MEMBASE_REG:
1719 return OP_STOREI4_MEMINDEX;
1720 case OP_STORE_MEMBASE_REG:
1721 return OP_STORE_MEMINDEX;
1722 case OP_STORER4_MEMBASE_REG:
1723 return OP_STORER4_MEMINDEX;
1724 case OP_STORER8_MEMBASE_REG:
1725 return OP_STORER8_MEMINDEX;
1726 case OP_STORE_MEMBASE_IMM:
1727 return OP_STORE_MEMBASE_REG;
1728 case OP_STOREI1_MEMBASE_IMM:
1729 return OP_STOREI1_MEMBASE_REG;
1730 case OP_STOREI2_MEMBASE_IMM:
1731 return OP_STOREI2_MEMBASE_REG;
1732 case OP_STOREI4_MEMBASE_IMM:
1733 return OP_STOREI4_MEMBASE_REG;
1735 g_assert_not_reached ();
1739 * Remove from the instruction list the instructions that can't be
1740 * represented with very simple instructions with no register
1744 mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb)
1746 MonoInst *ins, *temp, *last_ins = NULL;
1747 int rot_amount, imm8, low_imm;
1749 MONO_BB_FOR_EACH_INS (bb, ins) {
1751 switch (ins->opcode) {
1755 case OP_COMPARE_IMM:
1756 case OP_ICOMPARE_IMM:
1770 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount)) < 0) {
1771 NEW_INS (cfg, temp, OP_ICONST);
1772 temp->inst_c0 = ins->inst_imm;
1773 temp->dreg = mono_alloc_ireg (cfg);
1774 ins->sreg2 = temp->dreg;
1775 ins->opcode = mono_op_imm_to_op (ins->opcode);
1777 if (ins->opcode == OP_SBB || ins->opcode == OP_ISBB || ins->opcode == OP_SUBCC)
1783 if (ins->inst_imm == 1) {
1784 ins->opcode = OP_MOVE;
1787 if (ins->inst_imm == 0) {
1788 ins->opcode = OP_ICONST;
1792 imm8 = mono_is_power_of_two (ins->inst_imm);
1794 ins->opcode = OP_SHL_IMM;
1795 ins->inst_imm = imm8;
1798 NEW_INS (cfg, temp, OP_ICONST);
1799 temp->inst_c0 = ins->inst_imm;
1800 temp->dreg = mono_alloc_ireg (cfg);
1801 ins->sreg2 = temp->dreg;
1802 ins->opcode = OP_IMUL;
1808 if (ins->next && (ins->next->opcode == OP_COND_EXC_C || ins->next->opcode == OP_COND_EXC_IC))
1809 /* ARM sets the C flag to 1 if there was _no_ overflow */
1810 ins->next->opcode = OP_COND_EXC_NC;
1812 case OP_LOCALLOC_IMM:
1813 NEW_INS (cfg, temp, OP_ICONST);
1814 temp->inst_c0 = ins->inst_imm;
1815 temp->dreg = mono_alloc_ireg (cfg);
1816 ins->sreg1 = temp->dreg;
1817 ins->opcode = OP_LOCALLOC;
1819 case OP_LOAD_MEMBASE:
1820 case OP_LOADI4_MEMBASE:
1821 case OP_LOADU4_MEMBASE:
1822 case OP_LOADU1_MEMBASE:
1823 /* we can do two things: load the immed in a register
1824 * and use an indexed load, or see if the immed can be
1825 * represented as an ad_imm + a load with a smaller offset
1826 * that fits. We just do the first for now, optimize later.
1828 if (arm_is_imm12 (ins->inst_offset))
1830 NEW_INS (cfg, temp, OP_ICONST);
1831 temp->inst_c0 = ins->inst_offset;
1832 temp->dreg = mono_alloc_ireg (cfg);
1833 ins->sreg2 = temp->dreg;
1834 ins->opcode = map_to_reg_reg_op (ins->opcode);
1836 case OP_LOADI2_MEMBASE:
1837 case OP_LOADU2_MEMBASE:
1838 case OP_LOADI1_MEMBASE:
1839 if (arm_is_imm8 (ins->inst_offset))
1841 NEW_INS (cfg, temp, OP_ICONST);
1842 temp->inst_c0 = ins->inst_offset;
1843 temp->dreg = mono_alloc_ireg (cfg);
1844 ins->sreg2 = temp->dreg;
1845 ins->opcode = map_to_reg_reg_op (ins->opcode);
1847 case OP_LOADR4_MEMBASE:
1848 case OP_LOADR8_MEMBASE:
1849 if (arm_is_fpimm8 (ins->inst_offset))
1851 low_imm = ins->inst_offset & 0x1ff;
1852 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~0x1ff, &rot_amount)) >= 0) {
1853 NEW_INS (cfg, temp, OP_ADD_IMM);
1854 temp->inst_imm = ins->inst_offset & ~0x1ff;
1855 temp->sreg1 = ins->inst_basereg;
1856 temp->dreg = mono_alloc_ireg (cfg);
1857 ins->inst_basereg = temp->dreg;
1858 ins->inst_offset = low_imm;
1861 /* VFP/FPA doesn't have indexed load instructions */
1862 g_assert_not_reached ();
1864 case OP_STORE_MEMBASE_REG:
1865 case OP_STOREI4_MEMBASE_REG:
1866 case OP_STOREI1_MEMBASE_REG:
1867 if (arm_is_imm12 (ins->inst_offset))
1869 NEW_INS (cfg, temp, OP_ICONST);
1870 temp->inst_c0 = ins->inst_offset;
1871 temp->dreg = mono_alloc_ireg (cfg);
1872 ins->sreg2 = temp->dreg;
1873 ins->opcode = map_to_reg_reg_op (ins->opcode);
1875 case OP_STOREI2_MEMBASE_REG:
1876 if (arm_is_imm8 (ins->inst_offset))
1878 NEW_INS (cfg, temp, OP_ICONST);
1879 temp->inst_c0 = ins->inst_offset;
1880 temp->dreg = mono_alloc_ireg (cfg);
1881 ins->sreg2 = temp->dreg;
1882 ins->opcode = map_to_reg_reg_op (ins->opcode);
1884 case OP_STORER4_MEMBASE_REG:
1885 case OP_STORER8_MEMBASE_REG:
1886 if (arm_is_fpimm8 (ins->inst_offset))
1888 low_imm = ins->inst_offset & 0x1ff;
1889 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~ 0x1ff, &rot_amount)) >= 0 && arm_is_fpimm8 (low_imm)) {
1890 NEW_INS (cfg, temp, OP_ADD_IMM);
1891 temp->inst_imm = ins->inst_offset & ~0x1ff;
1892 temp->sreg1 = ins->inst_destbasereg;
1893 temp->dreg = mono_alloc_ireg (cfg);
1894 ins->inst_destbasereg = temp->dreg;
1895 ins->inst_offset = low_imm;
1898 /*g_print ("fail with: %d (%d, %d)\n", ins->inst_offset, ins->inst_offset & ~0x1ff, low_imm);*/
1899 /* VFP/FPA doesn't have indexed store instructions */
1900 g_assert_not_reached ();
1902 case OP_STORE_MEMBASE_IMM:
1903 case OP_STOREI1_MEMBASE_IMM:
1904 case OP_STOREI2_MEMBASE_IMM:
1905 case OP_STOREI4_MEMBASE_IMM:
1906 NEW_INS (cfg, temp, OP_ICONST);
1907 temp->inst_c0 = ins->inst_imm;
1908 temp->dreg = mono_alloc_ireg (cfg);
1909 ins->sreg1 = temp->dreg;
1910 ins->opcode = map_to_reg_reg_op (ins->opcode);
1912 goto loop_start; /* make it handle the possibly big ins->inst_offset */
1914 gboolean swap = FALSE;
1917 /* Some fp compares require swapped operands */
1918 g_assert (ins->next);
1919 switch (ins->next->opcode) {
1921 ins->next->opcode = OP_FBLT;
1925 ins->next->opcode = OP_FBLT_UN;
1929 ins->next->opcode = OP_FBGE;
1933 ins->next->opcode = OP_FBGE_UN;
1941 ins->sreg1 = ins->sreg2;
1950 bb->last_ins = last_ins;
1951 bb->max_vreg = cfg->next_vreg;
1955 emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size, gboolean is_signed)
1957 /* sreg is a float, dreg is an integer reg */
1959 ARM_FIXZ (code, dreg, sreg);
1960 #elif defined(ARM_FPU_VFP)
1962 ARM_TOSIZD (code, ARM_VFP_F0, sreg);
1964 ARM_TOUIZD (code, ARM_VFP_F0, sreg);
1965 ARM_FMRS (code, dreg, ARM_VFP_F0);
1969 ARM_AND_REG_IMM8 (code, dreg, dreg, 0xff);
1970 else if (size == 2) {
1971 ARM_SHL_IMM (code, dreg, dreg, 16);
1972 ARM_SHR_IMM (code, dreg, dreg, 16);
1976 ARM_SHL_IMM (code, dreg, dreg, 24);
1977 ARM_SAR_IMM (code, dreg, dreg, 24);
1978 } else if (size == 2) {
1979 ARM_SHL_IMM (code, dreg, dreg, 16);
1980 ARM_SAR_IMM (code, dreg, dreg, 16);
1988 const guchar *target;
1993 #define is_call_imm(diff) ((gint)(diff) >= -33554432 && (gint)(diff) <= 33554431)
1996 search_thunk_slot (void *data, int csize, int bsize, void *user_data) {
1997 PatchData *pdata = (PatchData*)user_data;
1998 guchar *code = data;
1999 guint32 *thunks = data;
2000 guint32 *endthunks = (guint32*)(code + bsize);
2002 int difflow, diffhigh;
2004 /* always ensure a call from pdata->code can reach to the thunks without further thunks */
2005 difflow = (char*)pdata->code - (char*)thunks;
2006 diffhigh = (char*)pdata->code - (char*)endthunks;
2007 if (!((is_call_imm (thunks) && is_call_imm (endthunks)) || (is_call_imm (difflow) && is_call_imm (diffhigh))))
2011 * The thunk is composed of 3 words:
2012 * load constant from thunks [2] into ARM_IP
2015 * Note that the LR register is already setup
2017 //g_print ("thunk nentries: %d\n", ((char*)endthunks - (char*)thunks)/16);
2018 if ((pdata->found == 2) || (pdata->code >= code && pdata->code <= code + csize)) {
2019 while (thunks < endthunks) {
2020 //g_print ("looking for target: %p at %p (%08x-%08x)\n", pdata->target, thunks, thunks [0], thunks [1]);
2021 if (thunks [2] == (guint32)pdata->target) {
2022 arm_patch (pdata->code, (guchar*)thunks);
2023 mono_arch_flush_icache (pdata->code, 4);
2026 } else if ((thunks [0] == 0) && (thunks [1] == 0) && (thunks [2] == 0)) {
2027 /* found a free slot instead: emit thunk */
2028 /* ARMREG_IP is fine to use since this can't be an IMT call
2031 code = (guchar*)thunks;
2032 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
2033 if (thumb_supported)
2034 ARM_BX (code, ARMREG_IP);
2036 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
2037 thunks [2] = (guint32)pdata->target;
2038 mono_arch_flush_icache ((guchar*)thunks, 12);
2040 arm_patch (pdata->code, (guchar*)thunks);
2041 mono_arch_flush_icache (pdata->code, 4);
2045 /* skip 12 bytes, the size of the thunk */
2049 //g_print ("failed thunk lookup for %p from %p at %p (%d entries)\n", pdata->target, pdata->code, data, count);
2055 handle_thunk (int absolute, guchar *code, const guchar *target) {
2056 MonoDomain *domain = mono_domain_get ();
2060 pdata.target = target;
2061 pdata.absolute = absolute;
2064 mono_domain_lock (domain);
2065 mono_domain_code_foreach (domain, search_thunk_slot, &pdata);
2068 /* this uses the first available slot */
2070 mono_domain_code_foreach (domain, search_thunk_slot, &pdata);
2072 mono_domain_unlock (domain);
2074 if (pdata.found != 1)
2075 g_print ("thunk failed for %p from %p\n", target, code);
2076 g_assert (pdata.found == 1);
2080 arm_patch (guchar *code, const guchar *target)
2082 guint32 *code32 = (void*)code;
2083 guint32 ins = *code32;
2084 guint32 prim = (ins >> 25) & 7;
2085 guint32 tval = GPOINTER_TO_UINT (target);
2087 //g_print ("patching 0x%08x (0x%08x) to point to 0x%08x\n", code, ins, target);
2088 if (prim == 5) { /* 101b */
2089 /* the diff starts 8 bytes from the branch opcode */
2090 gint diff = target - code - 8;
2092 gint tmask = 0xffffffff;
2093 if (tval & 1) { /* entering thumb mode */
2094 diff = target - 1 - code - 8;
2095 g_assert (thumb_supported);
2096 tbits = 0xf << 28; /* bl->blx bit pattern */
2097 g_assert ((ins & (1 << 24))); /* it must be a bl, not b instruction */
2098 /* this low bit of the displacement is moved to bit 24 in the instruction encoding */
2102 tmask = ~(1 << 24); /* clear the link bit */
2103 /*g_print ("blx to thumb: target: %p, code: %p, diff: %d, mask: %x\n", target, code, diff, tmask);*/
2108 if (diff <= 33554431) {
2110 ins = (ins & 0xff000000) | diff;
2112 *code32 = ins | tbits;
2116 /* diff between 0 and -33554432 */
2117 if (diff >= -33554432) {
2119 ins = (ins & 0xff000000) | (diff & ~0xff000000);
2121 *code32 = ins | tbits;
2126 handle_thunk (TRUE, code, target);
2131 * The alternative call sequences looks like this:
2133 * ldr ip, [pc] // loads the address constant
2134 * b 1f // jumps around the constant
2135 * address constant embedded in the code
2140 * There are two cases for patching:
2141 * a) at the end of method emission: in this case code points to the start
2142 * of the call sequence
2143 * b) during runtime patching of the call site: in this case code points
2144 * to the mov pc, ip instruction
2146 * We have to handle also the thunk jump code sequence:
2150 * address constant // execution never reaches here
2152 if ((ins & 0x0ffffff0) == 0x12fff10) {
2153 /* Branch and exchange: the address is constructed in a reg
2154 * We can patch BX when the code sequence is the following:
2155 * ldr ip, [pc, #0] ; 0x8
2162 guint8 *emit = (guint8*)ccode;
2163 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2165 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
2166 ARM_BX (emit, ARMREG_IP);
2168 /*patching from magic trampoline*/
2169 if (ins == ccode [3]) {
2170 g_assert (code32 [-4] == ccode [0]);
2171 g_assert (code32 [-3] == ccode [1]);
2172 g_assert (code32 [-1] == ccode [2]);
2173 code32 [-2] = (guint32)target;
2176 /*patching from JIT*/
2177 if (ins == ccode [0]) {
2178 g_assert (code32 [1] == ccode [1]);
2179 g_assert (code32 [3] == ccode [2]);
2180 g_assert (code32 [4] == ccode [3]);
2181 code32 [2] = (guint32)target;
2184 g_assert_not_reached ();
2185 } else if ((ins & 0x0ffffff0) == 0x12fff30) {
2193 guint8 *emit = (guint8*)ccode;
2194 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2196 ARM_BLX_REG (emit, ARMREG_IP);
2198 g_assert (code32 [-3] == ccode [0]);
2199 g_assert (code32 [-2] == ccode [1]);
2200 g_assert (code32 [0] == ccode [2]);
2202 code32 [-1] = (guint32)target;
2205 guint32 *tmp = ccode;
2206 guint8 *emit = (guint8*)tmp;
2207 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2208 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
2209 ARM_MOV_REG_REG (emit, ARMREG_PC, ARMREG_IP);
2210 ARM_BX (emit, ARMREG_IP);
2211 if (ins == ccode [2]) {
2212 g_assert_not_reached (); // should be -2 ...
2213 code32 [-1] = (guint32)target;
2216 if (ins == ccode [0]) {
2217 /* handles both thunk jump code and the far call sequence */
2218 code32 [2] = (guint32)target;
2221 g_assert_not_reached ();
2223 // g_print ("patched with 0x%08x\n", ins);
2227 * Return the >= 0 uimm8 value if val can be represented with a byte + rotation
2228 * (with the rotation amount in *rot_amount. rot_amount is already adjusted
2229 * to be used with the emit macros.
2230 * Return -1 otherwise.
2233 mono_arm_is_rotated_imm8 (guint32 val, gint *rot_amount)
2236 for (i = 0; i < 31; i+= 2) {
2237 res = (val << (32 - i)) | (val >> i);
2240 *rot_amount = i? 32 - i: 0;
2247 * Emits in code a sequence of instructions that load the value 'val'
2248 * into the dreg register. Uses at most 4 instructions.
2251 mono_arm_emit_load_imm (guint8 *code, int dreg, guint32 val)
2253 int imm8, rot_amount;
2255 ARM_LDR_IMM (code, dreg, ARMREG_PC, 0);
2256 /* skip the constant pool */
2262 if ((imm8 = mono_arm_is_rotated_imm8 (val, &rot_amount)) >= 0) {
2263 ARM_MOV_REG_IMM (code, dreg, imm8, rot_amount);
2264 } else if ((imm8 = mono_arm_is_rotated_imm8 (~val, &rot_amount)) >= 0) {
2265 ARM_MVN_REG_IMM (code, dreg, imm8, rot_amount);
2268 ARM_MOV_REG_IMM8 (code, dreg, (val & 0xFF));
2270 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF00) >> 8, 24);
2272 if (val & 0xFF0000) {
2273 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
2275 if (val & 0xFF000000) {
2276 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2278 } else if (val & 0xFF00) {
2279 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF00) >> 8, 24);
2280 if (val & 0xFF0000) {
2281 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
2283 if (val & 0xFF000000) {
2284 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2286 } else if (val & 0xFF0000) {
2287 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF0000) >> 16, 16);
2288 if (val & 0xFF000000) {
2289 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2292 //g_assert_not_reached ();
2298 * emit_load_volatile_arguments:
2300 * Load volatile arguments from the stack to the original input registers.
2301 * Required before a tail call.
2304 emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code)
2306 MonoMethod *method = cfg->method;
2307 MonoMethodSignature *sig;
2312 /* FIXME: Generate intermediate code instead */
2314 sig = mono_method_signature (method);
2316 /* This is the opposite of the code in emit_prolog */
2320 cinfo = calculate_sizes (sig, sig->pinvoke);
2322 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
2323 ArgInfo *ainfo = &cinfo->ret;
2324 inst = cfg->vret_addr;
2325 g_assert (arm_is_imm12 (inst->inst_offset));
2326 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2328 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
2329 ArgInfo *ainfo = cinfo->args + i;
2330 inst = cfg->args [pos];
2332 if (cfg->verbose_level > 2)
2333 g_print ("Loading argument %d (type: %d)\n", i, ainfo->regtype);
2334 if (inst->opcode == OP_REGVAR) {
2335 if (ainfo->regtype == RegTypeGeneral)
2336 ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
2337 else if (ainfo->regtype == RegTypeFP) {
2338 g_assert_not_reached ();
2339 } else if (ainfo->regtype == RegTypeBase) {
2343 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
2344 ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset));
2346 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
2347 ARM_LDR_REG_REG (code, inst->dreg, ARMREG_SP, ARMREG_IP);
2351 g_assert_not_reached ();
2353 if (ainfo->regtype == RegTypeGeneral) {
2354 switch (ainfo->size) {
2361 g_assert (arm_is_imm12 (inst->inst_offset));
2362 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2363 g_assert (arm_is_imm12 (inst->inst_offset + 4));
2364 ARM_LDR_IMM (code, ainfo->reg + 1, inst->inst_basereg, inst->inst_offset + 4);
2367 if (arm_is_imm12 (inst->inst_offset)) {
2368 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2370 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
2371 ARM_LDR_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
2375 } else if (ainfo->regtype == RegTypeBaseGen) {
2378 } else if (ainfo->regtype == RegTypeBase) {
2380 } else if (ainfo->regtype == RegTypeFP) {
2381 g_assert_not_reached ();
2382 } else if (ainfo->regtype == RegTypeStructByVal) {
2383 int doffset = inst->inst_offset;
2387 if (mono_class_from_mono_type (inst->inst_vtype))
2388 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), NULL);
2389 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
2390 if (arm_is_imm12 (doffset)) {
2391 ARM_LDR_IMM (code, ainfo->reg + cur_reg, inst->inst_basereg, doffset);
2393 code = mono_arm_emit_load_imm (code, ARMREG_IP, doffset);
2394 ARM_LDR_REG_REG (code, ainfo->reg + cur_reg, inst->inst_basereg, ARMREG_IP);
2396 soffset += sizeof (gpointer);
2397 doffset += sizeof (gpointer);
2402 } else if (ainfo->regtype == RegTypeStructByAddr) {
2419 mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
2424 guint8 *code = cfg->native_code + cfg->code_len;
2425 MonoInst *last_ins = NULL;
2426 guint last_offset = 0;
2428 int imm8, rot_amount;
2430 /* we don't align basic blocks of loops on arm */
2432 if (cfg->verbose_level > 2)
2433 g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset);
2435 cpos = bb->max_offset;
2437 if (cfg->prof_options & MONO_PROFILE_COVERAGE) {
2438 //MonoCoverageInfo *cov = mono_get_coverage_info (cfg->method);
2439 //g_assert (!mono_compile_aot);
2442 // cov->data [bb->dfn].iloffset = bb->cil_code - cfg->cil_code;
2443 /* this is not thread save, but good enough */
2444 /* fixme: howto handle overflows? */
2445 //x86_inc_mem (code, &cov->data [bb->dfn].count);
2448 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) {
2449 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2450 (gpointer)"mono_break");
2451 code = emit_call_seq (cfg, code);
2454 MONO_BB_FOR_EACH_INS (bb, ins) {
2455 offset = code - cfg->native_code;
2457 max_len = ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
2459 if (offset > (cfg->code_size - max_len - 16)) {
2460 cfg->code_size *= 2;
2461 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
2462 code = cfg->native_code + offset;
2464 // if (ins->cil_code)
2465 // g_print ("cil code\n");
2466 mono_debug_record_line_number (cfg, ins, offset);
2468 switch (ins->opcode) {
2469 case OP_MEMORY_BARRIER:
2472 #ifdef HAVE_AEABI_READ_TP
2473 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2474 (gpointer)"__aeabi_read_tp");
2475 code = emit_call_seq (cfg, code);
2477 ARM_LDR_IMM (code, ins->dreg, ARMREG_R0, ins->inst_offset);
2479 g_assert_not_reached ();
2483 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2484 ppc_mulhw (code, ppc_r3, ins->sreg1, ins->sreg2);
2487 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2488 ppc_mulhwu (code, ppc_r3, ins->sreg1, ins->sreg2);
2490 case OP_STOREI1_MEMBASE_IMM:
2491 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFF);
2492 g_assert (arm_is_imm12 (ins->inst_offset));
2493 ARM_STRB_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2495 case OP_STOREI2_MEMBASE_IMM:
2496 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFFFF);
2497 g_assert (arm_is_imm8 (ins->inst_offset));
2498 ARM_STRH_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2500 case OP_STORE_MEMBASE_IMM:
2501 case OP_STOREI4_MEMBASE_IMM:
2502 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm);
2503 g_assert (arm_is_imm12 (ins->inst_offset));
2504 ARM_STR_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2506 case OP_STOREI1_MEMBASE_REG:
2507 g_assert (arm_is_imm12 (ins->inst_offset));
2508 ARM_STRB_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2510 case OP_STOREI2_MEMBASE_REG:
2511 g_assert (arm_is_imm8 (ins->inst_offset));
2512 ARM_STRH_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2514 case OP_STORE_MEMBASE_REG:
2515 case OP_STOREI4_MEMBASE_REG:
2516 /* this case is special, since it happens for spill code after lowering has been called */
2517 if (arm_is_imm12 (ins->inst_offset)) {
2518 ARM_STR_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2520 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2521 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ARMREG_LR);
2524 case OP_STOREI1_MEMINDEX:
2525 ARM_STRB_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2527 case OP_STOREI2_MEMINDEX:
2528 ARM_STRH_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2530 case OP_STORE_MEMINDEX:
2531 case OP_STOREI4_MEMINDEX:
2532 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2535 g_assert_not_reached ();
2537 case OP_LOAD_MEMINDEX:
2538 case OP_LOADI4_MEMINDEX:
2539 case OP_LOADU4_MEMINDEX:
2540 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2542 case OP_LOADI1_MEMINDEX:
2543 ARM_LDRSB_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2545 case OP_LOADU1_MEMINDEX:
2546 ARM_LDRB_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2548 case OP_LOADI2_MEMINDEX:
2549 ARM_LDRSH_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2551 case OP_LOADU2_MEMINDEX:
2552 ARM_LDRH_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2554 case OP_LOAD_MEMBASE:
2555 case OP_LOADI4_MEMBASE:
2556 case OP_LOADU4_MEMBASE:
2557 /* this case is special, since it happens for spill code after lowering has been called */
2558 if (arm_is_imm12 (ins->inst_offset)) {
2559 ARM_LDR_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2561 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2562 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ARMREG_LR);
2565 case OP_LOADI1_MEMBASE:
2566 g_assert (arm_is_imm8 (ins->inst_offset));
2567 ARM_LDRSB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2569 case OP_LOADU1_MEMBASE:
2570 g_assert (arm_is_imm12 (ins->inst_offset));
2571 ARM_LDRB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2573 case OP_LOADU2_MEMBASE:
2574 g_assert (arm_is_imm8 (ins->inst_offset));
2575 ARM_LDRH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2577 case OP_LOADI2_MEMBASE:
2578 g_assert (arm_is_imm8 (ins->inst_offset));
2579 ARM_LDRSH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2581 case OP_ICONV_TO_I1:
2582 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 24);
2583 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 24);
2585 case OP_ICONV_TO_I2:
2586 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2587 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 16);
2589 case OP_ICONV_TO_U1:
2590 ARM_AND_REG_IMM8 (code, ins->dreg, ins->sreg1, 0xff);
2592 case OP_ICONV_TO_U2:
2593 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2594 ARM_SHR_IMM (code, ins->dreg, ins->dreg, 16);
2598 ARM_CMP_REG_REG (code, ins->sreg1, ins->sreg2);
2600 case OP_COMPARE_IMM:
2601 case OP_ICOMPARE_IMM:
2602 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2603 g_assert (imm8 >= 0);
2604 ARM_CMP_REG_IMM (code, ins->sreg1, imm8, rot_amount);
2608 * gdb does not like encountering the hw breakpoint ins in the debugged code.
2609 * So instead of emitting a trap, we emit a call a C function and place a
2612 //*(int*)code = 0xef9f0001;
2615 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2616 (gpointer)"mono_break");
2617 code = emit_call_seq (cfg, code);
2619 case OP_RELAXED_NOP:
2624 case OP_DUMMY_STORE:
2625 case OP_NOT_REACHED:
2630 ARM_ADDS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2633 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2637 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2640 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2641 g_assert (imm8 >= 0);
2642 ARM_ADDS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2646 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2647 g_assert (imm8 >= 0);
2648 ARM_ADD_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2652 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2653 g_assert (imm8 >= 0);
2654 ARM_ADCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2657 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2658 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2660 case OP_IADD_OVF_UN:
2661 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2662 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2665 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2666 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2668 case OP_ISUB_OVF_UN:
2669 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2670 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2672 case OP_ADD_OVF_CARRY:
2673 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2674 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2676 case OP_ADD_OVF_UN_CARRY:
2677 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2678 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2680 case OP_SUB_OVF_CARRY:
2681 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2682 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2684 case OP_SUB_OVF_UN_CARRY:
2685 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2686 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2690 ARM_SUBS_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_SUBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2698 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2702 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2706 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2707 g_assert (imm8 >= 0);
2708 ARM_SUB_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2712 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2713 g_assert (imm8 >= 0);
2714 ARM_SBCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2716 case OP_ARM_RSBS_IMM:
2717 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2718 g_assert (imm8 >= 0);
2719 ARM_RSBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2721 case OP_ARM_RSC_IMM:
2722 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2723 g_assert (imm8 >= 0);
2724 ARM_RSC_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2727 ARM_AND_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2731 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2732 g_assert (imm8 >= 0);
2733 ARM_AND_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2741 /* crappy ARM arch doesn't have a DIV instruction */
2742 g_assert_not_reached ();
2744 ARM_ORR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2748 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2749 g_assert (imm8 >= 0);
2750 ARM_ORR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2753 ARM_EOR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2757 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2758 g_assert (imm8 >= 0);
2759 ARM_EOR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2762 ARM_SHL_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2767 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2768 else if (ins->dreg != ins->sreg1)
2769 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2772 ARM_SAR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2777 ARM_SAR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2778 else if (ins->dreg != ins->sreg1)
2779 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2782 case OP_ISHR_UN_IMM:
2784 ARM_SHR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2785 else if (ins->dreg != ins->sreg1)
2786 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2789 ARM_SHR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2792 ARM_MVN_REG_REG (code, ins->dreg, ins->sreg1);
2795 ARM_RSB_REG_IMM8 (code, ins->dreg, ins->sreg1, 0);
2798 if (ins->dreg == ins->sreg2)
2799 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2801 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg2, ins->sreg1);
2804 g_assert_not_reached ();
2807 /* FIXME: handle ovf/ sreg2 != dreg */
2808 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2809 /* FIXME: MUL doesn't set the C/O flags on ARM */
2811 case OP_IMUL_OVF_UN:
2812 /* FIXME: handle ovf/ sreg2 != dreg */
2813 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2814 /* FIXME: MUL doesn't set the C/O flags on ARM */
2817 code = mono_arm_emit_load_imm (code, ins->dreg, ins->inst_c0);
2820 /* Load the GOT offset */
2821 mono_add_patch_info (cfg, offset, (MonoJumpInfoType)ins->inst_i1, ins->inst_p0);
2822 ARM_LDR_IMM (code, ins->dreg, ARMREG_PC, 0);
2824 *(gpointer*)code = NULL;
2826 /* Load the value from the GOT */
2827 ARM_LDR_REG_REG (code, ins->dreg, ARMREG_PC, ins->dreg);
2829 case OP_ICONV_TO_I4:
2830 case OP_ICONV_TO_U4:
2832 if (ins->dreg != ins->sreg1)
2833 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2836 int saved = ins->sreg2;
2837 if (ins->sreg2 == ARM_LSW_REG) {
2838 ARM_MOV_REG_REG (code, ARMREG_LR, ins->sreg2);
2841 if (ins->sreg1 != ARM_LSW_REG)
2842 ARM_MOV_REG_REG (code, ARM_LSW_REG, ins->sreg1);
2843 if (saved != ARM_MSW_REG)
2844 ARM_MOV_REG_REG (code, ARM_MSW_REG, saved);
2849 ARM_MVFD (code, ins->dreg, ins->sreg1);
2850 #elif defined(ARM_FPU_VFP)
2851 ARM_CPYD (code, ins->dreg, ins->sreg1);
2854 case OP_FCONV_TO_R4:
2856 ARM_MVFS (code, ins->dreg, ins->sreg1);
2857 #elif defined(ARM_FPU_VFP)
2858 ARM_CVTD (code, ins->dreg, ins->sreg1);
2859 ARM_CVTS (code, ins->dreg, ins->dreg);
2864 * Keep in sync with mono_arch_emit_epilog
2866 g_assert (!cfg->method->save_lmf);
2868 code = emit_load_volatile_arguments (cfg, code);
2870 code = emit_big_add (code, ARMREG_SP, cfg->frame_reg, cfg->stack_usage);
2871 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP)) | ((1 << ARMREG_LR)));
2872 mono_add_patch_info (cfg, (guint8*) code - cfg->native_code, MONO_PATCH_INFO_METHOD_JUMP, ins->inst_p0);
2873 if (cfg->compile_aot) {
2874 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
2876 *(gpointer*)code = NULL;
2878 ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_IP);
2884 /* ensure ins->sreg1 is not NULL */
2885 ARM_LDR_IMM (code, ARMREG_LR, ins->sreg1, 0);
2889 if (ppc_is_imm16 (cfg->sig_cookie + cfg->stack_usage)) {
2890 ppc_addi (code, ppc_r11, cfg->frame_reg, cfg->sig_cookie + cfg->stack_usage);
2892 ppc_load (code, ppc_r11, cfg->sig_cookie + cfg->stack_usage);
2893 ppc_add (code, ppc_r11, cfg->frame_reg, ppc_r11);
2895 ppc_stw (code, ppc_r11, 0, ins->sreg1);
2905 call = (MonoCallInst*)ins;
2906 if (ins->flags & MONO_INST_HAS_METHOD)
2907 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, call->method);
2909 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, call->fptr);
2910 code = emit_call_seq (cfg, code);
2911 code = emit_move_return_value (cfg, ins, code);
2917 case OP_VOIDCALL_REG:
2919 code = emit_call_reg (code, ins->sreg1);
2920 code = emit_move_return_value (cfg, ins, code);
2922 case OP_FCALL_MEMBASE:
2923 case OP_LCALL_MEMBASE:
2924 case OP_VCALL_MEMBASE:
2925 case OP_VCALL2_MEMBASE:
2926 case OP_VOIDCALL_MEMBASE:
2927 case OP_CALL_MEMBASE:
2928 g_assert (arm_is_imm12 (ins->inst_offset));
2929 g_assert (ins->sreg1 != ARMREG_LR);
2930 call = (MonoCallInst*)ins;
2931 if (call->method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
2932 ARM_ADD_REG_IMM8 (code, ARMREG_LR, ARMREG_PC, 4);
2933 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
2935 * We can't embed the method in the code stream in PIC code, or
2937 * Instead, we put it in V5 in code emitted by
2938 * mono_arch_emit_imt_argument (), and embed NULL here to
2939 * signal the IMT thunk that the value is in V5.
2941 if (call->dynamic_imt_arg)
2942 *((gpointer*)code) = NULL;
2944 *((gpointer*)code) = (gpointer)call->method;
2947 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
2948 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
2950 code = emit_move_return_value (cfg, ins, code);
2953 /* keep alignment */
2954 int alloca_waste = cfg->param_area;
2957 /* round the size to 8 bytes */
2958 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->sreg1, 7);
2959 ARM_BIC_REG_IMM8 (code, ins->dreg, ins->dreg, 7);
2961 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->dreg, alloca_waste);
2962 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ins->dreg);
2963 /* memzero the area: dreg holds the size, sp is the pointer */
2964 if (ins->flags & MONO_INST_INIT) {
2965 guint8 *start_loop, *branch_to_cond;
2966 ARM_MOV_REG_IMM8 (code, ARMREG_LR, 0);
2967 branch_to_cond = code;
2970 ARM_STR_REG_REG (code, ARMREG_LR, ARMREG_SP, ins->dreg);
2971 arm_patch (branch_to_cond, code);
2972 /* decrement by 4 and set flags */
2973 ARM_SUBS_REG_IMM8 (code, ins->dreg, ins->dreg, 4);
2974 ARM_B_COND (code, ARMCOND_GE, 0);
2975 arm_patch (code - 4, start_loop);
2977 ARM_ADD_REG_IMM8 (code, ins->dreg, ARMREG_SP, alloca_waste);
2981 if (ins->sreg1 != ARMREG_R0)
2982 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
2983 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2984 (gpointer)"mono_arch_throw_exception");
2985 code = emit_call_seq (cfg, code);
2989 if (ins->sreg1 != ARMREG_R0)
2990 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
2991 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2992 (gpointer)"mono_arch_rethrow_exception");
2993 code = emit_call_seq (cfg, code);
2996 case OP_START_HANDLER: {
2997 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
2999 if (arm_is_imm12 (spvar->inst_offset)) {
3000 ARM_STR_IMM (code, ARMREG_LR, spvar->inst_basereg, spvar->inst_offset);
3002 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3003 ARM_STR_REG_REG (code, ARMREG_LR, spvar->inst_basereg, ARMREG_IP);
3007 case OP_ENDFILTER: {
3008 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3010 if (ins->sreg1 != ARMREG_R0)
3011 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
3012 if (arm_is_imm12 (spvar->inst_offset)) {
3013 ARM_LDR_IMM (code, ARMREG_IP, spvar->inst_basereg, spvar->inst_offset);
3015 g_assert (ARMREG_IP != spvar->inst_basereg);
3016 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3017 ARM_LDR_REG_REG (code, ARMREG_IP, spvar->inst_basereg, ARMREG_IP);
3019 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
3022 case OP_ENDFINALLY: {
3023 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3025 if (arm_is_imm12 (spvar->inst_offset)) {
3026 ARM_LDR_IMM (code, ARMREG_IP, spvar->inst_basereg, spvar->inst_offset);
3028 g_assert (ARMREG_IP != spvar->inst_basereg);
3029 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3030 ARM_LDR_REG_REG (code, ARMREG_IP, spvar->inst_basereg, ARMREG_IP);
3032 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
3035 case OP_CALL_HANDLER:
3036 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb);
3040 ins->inst_c0 = code - cfg->native_code;
3043 if (ins->flags & MONO_INST_BRLABEL) {
3044 /*if (ins->inst_i0->inst_c0) {
3046 //x86_jump_code (code, cfg->native_code + ins->inst_i0->inst_c0);
3048 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_LABEL, ins->inst_i0);
3052 /*if (ins->inst_target_bb->native_offset) {
3054 //x86_jump_code (code, cfg->native_code + ins->inst_target_bb->native_offset);
3056 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb);
3062 ARM_MOV_REG_REG (code, ARMREG_PC, ins->sreg1);
3066 * In the normal case we have:
3067 * ldr pc, [pc, ins->sreg1 << 2]
3070 * ldr lr, [pc, ins->sreg1 << 2]
3072 * After follows the data.
3073 * FIXME: add aot support.
3075 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_SWITCH, ins->inst_p0);
3076 max_len += 4 * GPOINTER_TO_INT (ins->klass);
3077 if (offset > (cfg->code_size - max_len - 16)) {
3078 cfg->code_size += max_len;
3079 cfg->code_size *= 2;
3080 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
3081 code = cfg->native_code + offset;
3083 ARM_LDR_REG_REG_SHIFT (code, ARMREG_PC, ARMREG_PC, ins->sreg1, ARMSHIFT_LSL, 2);
3085 code += 4 * GPOINTER_TO_INT (ins->klass);
3089 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
3090 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
3094 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3095 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LT);
3099 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3100 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LO);
3104 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3105 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_GT);
3109 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3110 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_HI);
3112 case OP_COND_EXC_EQ:
3113 case OP_COND_EXC_NE_UN:
3114 case OP_COND_EXC_LT:
3115 case OP_COND_EXC_LT_UN:
3116 case OP_COND_EXC_GT:
3117 case OP_COND_EXC_GT_UN:
3118 case OP_COND_EXC_GE:
3119 case OP_COND_EXC_GE_UN:
3120 case OP_COND_EXC_LE:
3121 case OP_COND_EXC_LE_UN:
3122 EMIT_COND_SYSTEM_EXCEPTION (ins->opcode - OP_COND_EXC_EQ, ins->inst_p1);
3124 case OP_COND_EXC_IEQ:
3125 case OP_COND_EXC_INE_UN:
3126 case OP_COND_EXC_ILT:
3127 case OP_COND_EXC_ILT_UN:
3128 case OP_COND_EXC_IGT:
3129 case OP_COND_EXC_IGT_UN:
3130 case OP_COND_EXC_IGE:
3131 case OP_COND_EXC_IGE_UN:
3132 case OP_COND_EXC_ILE:
3133 case OP_COND_EXC_ILE_UN:
3134 EMIT_COND_SYSTEM_EXCEPTION (ins->opcode - OP_COND_EXC_IEQ, ins->inst_p1);
3137 case OP_COND_EXC_IC:
3138 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_CS, ins->inst_p1);
3140 case OP_COND_EXC_OV:
3141 case OP_COND_EXC_IOV:
3142 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VS, ins->inst_p1);
3144 case OP_COND_EXC_NC:
3145 case OP_COND_EXC_INC:
3146 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_CC, ins->inst_p1);
3148 case OP_COND_EXC_NO:
3149 case OP_COND_EXC_INO:
3150 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VC, ins->inst_p1);
3162 EMIT_COND_BRANCH (ins, ins->opcode - OP_IBEQ);
3165 /* floating point opcodes */
3168 if (cfg->compile_aot) {
3169 ARM_LDFD (code, ins->dreg, ARMREG_PC, 0);
3171 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3173 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
3176 /* FIXME: we can optimize the imm load by dealing with part of
3177 * the displacement in LDFD (aligning to 512).
3179 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3180 ARM_LDFD (code, ins->dreg, ARMREG_LR, 0);
3184 if (cfg->compile_aot) {
3185 ARM_LDFS (code, ins->dreg, ARMREG_PC, 0);
3187 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3190 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3191 ARM_LDFS (code, ins->dreg, ARMREG_LR, 0);
3194 case OP_STORER8_MEMBASE_REG:
3195 /* This is generated by the local regalloc pass which runs after the lowering pass */
3196 if (!arm_is_fpimm8 (ins->inst_offset)) {
3197 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3198 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_destbasereg);
3199 ARM_STFD (code, ins->sreg1, ARMREG_LR, 0);
3201 ARM_STFD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3204 case OP_LOADR8_MEMBASE:
3205 /* This is generated by the local regalloc pass which runs after the lowering pass */
3206 if (!arm_is_fpimm8 (ins->inst_offset)) {
3207 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3208 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_basereg);
3209 ARM_LDFD (code, ins->dreg, ARMREG_LR, 0);
3211 ARM_LDFD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3214 case OP_STORER4_MEMBASE_REG:
3215 g_assert (arm_is_fpimm8 (ins->inst_offset));
3216 ARM_STFS (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3218 case OP_LOADR4_MEMBASE:
3219 g_assert (arm_is_fpimm8 (ins->inst_offset));
3220 ARM_LDFS (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3222 case OP_ICONV_TO_R_UN: {
3224 tmpreg = ins->dreg == 0? 1: 0;
3225 ARM_CMP_REG_IMM8 (code, ins->sreg1, 0);
3226 ARM_FLTD (code, ins->dreg, ins->sreg1);
3227 ARM_B_COND (code, ARMCOND_GE, 8);
3228 /* save the temp register */
3229 ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
3230 ARM_STFD (code, tmpreg, ARMREG_SP, 0);
3231 ARM_LDFD (code, tmpreg, ARMREG_PC, 12);
3232 ARM_FPA_ADFD (code, ins->dreg, ins->dreg, tmpreg);
3233 ARM_LDFD (code, tmpreg, ARMREG_SP, 0);
3234 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
3235 /* skip the constant pool */
3238 *(int*)code = 0x41f00000;
3243 * ldfltd ftemp, [pc, #8] 0x41f00000 0x00000000
3244 * adfltd fdest, fdest, ftemp
3248 case OP_ICONV_TO_R4:
3249 ARM_FLTS (code, ins->dreg, ins->sreg1);
3251 case OP_ICONV_TO_R8:
3252 ARM_FLTD (code, ins->dreg, ins->sreg1);
3255 #elif defined(ARM_FPU_VFP)
3258 if (cfg->compile_aot) {
3259 ARM_FLDD (code, ins->dreg, ARMREG_PC, 0);
3261 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3263 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
3266 /* FIXME: we can optimize the imm load by dealing with part of
3267 * the displacement in LDFD (aligning to 512).
3269 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3270 ARM_FLDD (code, ins->dreg, ARMREG_LR, 0);
3274 if (cfg->compile_aot) {
3275 ARM_FLDS (code, ins->dreg, ARMREG_PC, 0);
3277 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3279 ARM_CVTS (code, ins->dreg, ins->dreg);
3281 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3282 ARM_FLDS (code, ins->dreg, ARMREG_LR, 0);
3283 ARM_CVTS (code, ins->dreg, ins->dreg);
3286 case OP_STORER8_MEMBASE_REG:
3287 /* This is generated by the local regalloc pass which runs after the lowering pass */
3288 if (!arm_is_fpimm8 (ins->inst_offset)) {
3289 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3290 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_destbasereg);
3291 ARM_FSTD (code, ins->sreg1, ARMREG_LR, 0);
3293 ARM_FSTD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3296 case OP_LOADR8_MEMBASE:
3297 /* This is generated by the local regalloc pass which runs after the lowering pass */
3298 if (!arm_is_fpimm8 (ins->inst_offset)) {
3299 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3300 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_basereg);
3301 ARM_FLDD (code, ins->dreg, ARMREG_LR, 0);
3303 ARM_FLDD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3306 case OP_STORER4_MEMBASE_REG:
3307 g_assert (arm_is_fpimm8 (ins->inst_offset));
3308 ARM_CVTD (code, ARM_VFP_F0, ins->sreg1);
3309 ARM_FSTS (code, ARM_VFP_F0, ins->inst_destbasereg, ins->inst_offset);
3311 case OP_LOADR4_MEMBASE:
3312 g_assert (arm_is_fpimm8 (ins->inst_offset));
3313 ARM_FLDS (code, ARM_VFP_F0, ins->inst_basereg, ins->inst_offset);
3314 ARM_CVTS (code, ins->dreg, ARM_VFP_F0);
3316 case OP_ICONV_TO_R_UN: {
3317 g_assert_not_reached ();
3320 case OP_ICONV_TO_R4:
3321 ARM_FMSR (code, ARM_VFP_F0, ins->sreg1);
3322 ARM_FSITOS (code, ARM_VFP_F0, ARM_VFP_F0);
3323 ARM_CVTS (code, ins->dreg, ARM_VFP_F0);
3325 case OP_ICONV_TO_R8:
3326 ARM_FMSR (code, ARM_VFP_F0, ins->sreg1);
3327 ARM_FSITOD (code, ins->dreg, ARM_VFP_F0);
3331 if (mono_method_signature (cfg->method)->ret->type == MONO_TYPE_R4) {
3332 ARM_CVTD (code, ARM_VFP_F0, ins->sreg1);
3333 ARM_FMRS (code, ARMREG_R0, ARM_VFP_F0);
3335 ARM_FMRRD (code, ARMREG_R0, ARMREG_R1, ins->sreg1);
3341 case OP_FCONV_TO_I1:
3342 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, TRUE);
3344 case OP_FCONV_TO_U1:
3345 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, FALSE);
3347 case OP_FCONV_TO_I2:
3348 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, TRUE);
3350 case OP_FCONV_TO_U2:
3351 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, FALSE);
3353 case OP_FCONV_TO_I4:
3355 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, TRUE);
3357 case OP_FCONV_TO_U4:
3359 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, FALSE);
3361 case OP_FCONV_TO_I8:
3362 case OP_FCONV_TO_U8:
3363 g_assert_not_reached ();
3364 /* Implemented as helper calls */
3366 case OP_LCONV_TO_R_UN:
3367 g_assert_not_reached ();
3368 /* Implemented as helper calls */
3370 case OP_LCONV_TO_OVF_I:
3371 case OP_LCONV_TO_OVF_I4_2: {
3372 guint32 *high_bit_not_set, *valid_negative, *invalid_negative, *valid_positive;
3374 * Valid ints: 0xffffffff:8000000 to 00000000:0x7f000000
3377 ARM_CMP_REG_IMM8 (code, ins->sreg1, 0);
3378 high_bit_not_set = code;
3379 ARM_B_COND (code, ARMCOND_GE, 0); /*branch if bit 31 of the lower part is not set*/
3381 ARM_CMN_REG_IMM8 (code, ins->sreg2, 1); /*This have the same effect as CMP reg, 0xFFFFFFFF */
3382 valid_negative = code;
3383 ARM_B_COND (code, ARMCOND_EQ, 0); /*branch if upper part == 0xFFFFFFFF (lower part has bit 31 set) */
3384 invalid_negative = code;
3385 ARM_B_COND (code, ARMCOND_AL, 0);
3387 arm_patch (high_bit_not_set, code);
3389 ARM_CMP_REG_IMM8 (code, ins->sreg2, 0);
3390 valid_positive = code;
3391 ARM_B_COND (code, ARMCOND_EQ, 0); /*branch if upper part == 0 (lower part has bit 31 clear)*/
3393 arm_patch (invalid_negative, code);
3394 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_AL, "OverflowException");
3396 arm_patch (valid_negative, code);
3397 arm_patch (valid_positive, code);
3399 if (ins->dreg != ins->sreg1)
3400 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
3405 ARM_FPA_ADFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3408 ARM_FPA_SUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3411 ARM_FPA_MUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3414 ARM_FPA_DVFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3417 ARM_MNFD (code, ins->dreg, ins->sreg1);
3419 #elif defined(ARM_FPU_VFP)
3421 ARM_VFP_ADDD (code, ins->dreg, ins->sreg1, ins->sreg2);
3424 ARM_VFP_SUBD (code, ins->dreg, ins->sreg1, ins->sreg2);
3427 ARM_VFP_MULD (code, ins->dreg, ins->sreg1, ins->sreg2);
3430 ARM_VFP_DIVD (code, ins->dreg, ins->sreg1, ins->sreg2);
3433 ARM_NEGD (code, ins->dreg, ins->sreg1);
3438 g_assert_not_reached ();
3442 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3443 #elif defined(ARM_FPU_VFP)
3444 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3450 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3451 #elif defined(ARM_FPU_VFP)
3452 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3455 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
3456 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
3460 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3461 #elif defined(ARM_FPU_VFP)
3462 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3465 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3466 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3470 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3471 #elif defined(ARM_FPU_VFP)
3472 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3475 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3476 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3477 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
3482 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3483 #elif defined(ARM_FPU_VFP)
3484 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3487 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3488 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3493 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3494 #elif defined(ARM_FPU_VFP)
3495 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3498 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3499 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3500 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
3502 /* ARM FPA flags table:
3503 * N Less than ARMCOND_MI
3504 * Z Equal ARMCOND_EQ
3505 * C Greater Than or Equal ARMCOND_CS
3506 * V Unordered ARMCOND_VS
3509 EMIT_COND_BRANCH (ins, OP_IBEQ - OP_IBEQ);
3512 EMIT_COND_BRANCH (ins, OP_IBNE_UN - OP_IBEQ);
3515 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
3518 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3519 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
3525 g_assert_not_reached ();
3528 /* FIXME does VFP requires both conds?
3529 * FPA requires EQ even thou the docs suggests that just CS is enough
3531 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_EQ);
3532 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_CS);
3535 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3536 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_GE);
3541 if (ins->dreg != ins->sreg1)
3542 ARM_MVFD (code, ins->dreg, ins->sreg1);
3543 #elif defined(ARM_FPU_VFP)
3544 ARM_CPYD (code, ins->dreg, ins->sreg1);
3549 g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
3550 g_assert_not_reached ();
3553 if ((cfg->opt & MONO_OPT_BRANCH) && ((code - cfg->native_code - offset) > max_len)) {
3554 g_warning ("wrong maximal instruction length of instruction %s (expected %d, got %d)",
3555 mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset);
3556 g_assert_not_reached ();
3562 last_offset = offset;
3565 cfg->code_len = code - cfg->native_code;
3568 #endif /* DISABLE_JIT */
3570 #ifdef HAVE_AEABI_READ_TP
3571 void __aeabi_read_tp (void);
3575 mono_arch_register_lowlevel_calls (void)
3577 /* The signature doesn't matter */
3578 mono_register_jit_icall (mono_arm_throw_exception, "mono_arm_throw_exception", mono_create_icall_signature ("void"), TRUE);
3579 mono_register_jit_icall (mono_arm_throw_exception_by_token, "mono_arm_throw_exception_by_token", mono_create_icall_signature ("void"), TRUE);
3581 #ifdef HAVE_AEABI_READ_TP
3582 mono_register_jit_icall (__aeabi_read_tp, "__aeabi_read_tp", mono_create_icall_signature ("void"), TRUE);
3586 #define patch_lis_ori(ip,val) do {\
3587 guint16 *__lis_ori = (guint16*)(ip); \
3588 __lis_ori [1] = (((guint32)(val)) >> 16) & 0xffff; \
3589 __lis_ori [3] = ((guint32)(val)) & 0xffff; \
3593 mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, gboolean run_cctors)
3595 MonoJumpInfo *patch_info;
3596 gboolean compile_aot = !run_cctors;
3598 for (patch_info = ji; patch_info; patch_info = patch_info->next) {
3599 unsigned char *ip = patch_info->ip.i + code;
3600 const unsigned char *target;
3602 if (patch_info->type == MONO_PATCH_INFO_SWITCH && !compile_aot) {
3603 gpointer *jt = (gpointer*)(ip + 8);
3605 /* jt is the inlined jump table, 2 instructions after ip
3606 * In the normal case we store the absolute addresses,
3607 * otherwise the displacements.
3609 for (i = 0; i < patch_info->data.table->table_size; i++)
3610 jt [i] = code + (int)patch_info->data.table->table [i];
3613 target = mono_resolve_patch_target (method, domain, code, patch_info, run_cctors);
3616 switch (patch_info->type) {
3617 case MONO_PATCH_INFO_BB:
3618 case MONO_PATCH_INFO_LABEL:
3621 /* No need to patch these */
3626 switch (patch_info->type) {
3627 case MONO_PATCH_INFO_IP:
3628 g_assert_not_reached ();
3629 patch_lis_ori (ip, ip);
3631 case MONO_PATCH_INFO_METHOD_REL:
3632 g_assert_not_reached ();
3633 *((gpointer *)(ip)) = code + patch_info->data.offset;
3635 case MONO_PATCH_INFO_METHODCONST:
3636 case MONO_PATCH_INFO_CLASS:
3637 case MONO_PATCH_INFO_IMAGE:
3638 case MONO_PATCH_INFO_FIELD:
3639 case MONO_PATCH_INFO_VTABLE:
3640 case MONO_PATCH_INFO_IID:
3641 case MONO_PATCH_INFO_SFLDA:
3642 case MONO_PATCH_INFO_LDSTR:
3643 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
3644 case MONO_PATCH_INFO_LDTOKEN:
3645 g_assert_not_reached ();
3646 /* from OP_AOTCONST : lis + ori */
3647 patch_lis_ori (ip, target);
3649 case MONO_PATCH_INFO_R4:
3650 case MONO_PATCH_INFO_R8:
3651 g_assert_not_reached ();
3652 *((gconstpointer *)(ip + 2)) = patch_info->data.target;
3654 case MONO_PATCH_INFO_EXC_NAME:
3655 g_assert_not_reached ();
3656 *((gconstpointer *)(ip + 1)) = patch_info->data.name;
3658 case MONO_PATCH_INFO_NONE:
3659 case MONO_PATCH_INFO_BB_OVF:
3660 case MONO_PATCH_INFO_EXC_OVF:
3661 /* everything is dealt with at epilog output time */
3666 arm_patch (ip, target);
3671 * Stack frame layout:
3673 * ------------------- fp
3674 * MonoLMF structure or saved registers
3675 * -------------------
3677 * -------------------
3679 * -------------------
3680 * optional 8 bytes for tracing
3681 * -------------------
3682 * param area size is cfg->param_area
3683 * ------------------- sp
3686 mono_arch_emit_prolog (MonoCompile *cfg)
3688 MonoMethod *method = cfg->method;
3690 MonoMethodSignature *sig;
3692 int alloc_size, pos, max_offset, i, rot_amount;
3697 int prev_sp_offset, reg_offset;
3699 if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
3702 sig = mono_method_signature (method);
3703 cfg->code_size = 256 + sig->param_count * 20;
3704 code = cfg->native_code = g_malloc (cfg->code_size);
3706 mono_emit_unwind_op_def_cfa (cfg, code, ARMREG_SP, 0);
3708 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
3710 alloc_size = cfg->stack_offset;
3713 if (!method->save_lmf) {
3714 /* We save SP by storing it into IP and saving IP */
3715 ARM_PUSH (code, (cfg->used_int_regs | (1 << ARMREG_IP) | (1 << ARMREG_LR)));
3716 prev_sp_offset = 8; /* ip and lr */
3717 for (i = 0; i < 16; ++i) {
3718 if (cfg->used_int_regs & (1 << i))
3719 prev_sp_offset += 4;
3721 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset);
3723 for (i = 0; i < 16; ++i) {
3724 if ((cfg->used_int_regs & (1 << i)) || (i == ARMREG_IP) || (i == ARMREG_LR)) {
3725 mono_emit_unwind_op_offset (cfg, code, i, (- prev_sp_offset) + reg_offset);
3730 ARM_PUSH (code, 0x5ff0);
3731 prev_sp_offset = 4 * 10; /* all but r0-r3, sp and pc */
3732 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset);
3734 for (i = 0; i < 16; ++i) {
3735 if ((i > ARMREG_R3) && (i != ARMREG_SP) && (i != ARMREG_PC)) {
3736 mono_emit_unwind_op_offset (cfg, code, i, (- prev_sp_offset) + reg_offset);
3740 pos += sizeof (MonoLMF) - prev_sp_offset;
3744 // align to MONO_ARCH_FRAME_ALIGNMENT bytes
3745 if (alloc_size & (MONO_ARCH_FRAME_ALIGNMENT - 1)) {
3746 alloc_size += MONO_ARCH_FRAME_ALIGNMENT - 1;
3747 alloc_size &= ~(MONO_ARCH_FRAME_ALIGNMENT - 1);
3750 /* the stack used in the pushed regs */
3751 if (prev_sp_offset & 4)
3753 cfg->stack_usage = alloc_size;
3755 if ((i = mono_arm_is_rotated_imm8 (alloc_size, &rot_amount)) >= 0) {
3756 ARM_SUB_REG_IMM (code, ARMREG_SP, ARMREG_SP, i, rot_amount);
3758 code = mono_arm_emit_load_imm (code, ARMREG_IP, alloc_size);
3759 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
3761 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset + alloc_size);
3763 if (cfg->frame_reg != ARMREG_SP) {
3764 ARM_MOV_REG_REG (code, cfg->frame_reg, ARMREG_SP);
3765 mono_emit_unwind_op_def_cfa_reg (cfg, code, cfg->frame_reg);
3767 //g_print ("prev_sp_offset: %d, alloc_size:%d\n", prev_sp_offset, alloc_size);
3768 prev_sp_offset += alloc_size;
3770 /* compute max_offset in order to use short forward jumps
3771 * we could skip do it on arm because the immediate displacement
3772 * for jumps is large enough, it may be useful later for constant pools
3775 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
3776 MonoInst *ins = bb->code;
3777 bb->max_offset = max_offset;
3779 if (cfg->prof_options & MONO_PROFILE_COVERAGE)
3782 MONO_BB_FOR_EACH_INS (bb, ins)
3783 max_offset += ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
3786 /* store runtime generic context */
3787 if (cfg->rgctx_var) {
3788 MonoInst *ins = cfg->rgctx_var;
3790 g_assert (ins->opcode == OP_REGOFFSET);
3792 if (arm_is_imm12 (ins->inst_offset)) {
3793 ARM_STR_IMM (code, MONO_ARCH_RGCTX_REG, ins->inst_basereg, ins->inst_offset);
3795 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3796 ARM_STR_REG_REG (code, MONO_ARCH_RGCTX_REG, ins->inst_basereg, ARMREG_LR);
3800 /* load arguments allocated to register from the stack */
3803 cinfo = calculate_sizes (sig, sig->pinvoke);
3805 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
3806 ArgInfo *ainfo = &cinfo->ret;
3807 inst = cfg->vret_addr;
3808 g_assert (arm_is_imm12 (inst->inst_offset));
3809 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3811 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
3812 ArgInfo *ainfo = cinfo->args + i;
3813 inst = cfg->args [pos];
3815 if (cfg->verbose_level > 2)
3816 g_print ("Saving argument %d (type: %d)\n", i, ainfo->regtype);
3817 if (inst->opcode == OP_REGVAR) {
3818 if (ainfo->regtype == RegTypeGeneral)
3819 ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
3820 else if (ainfo->regtype == RegTypeFP) {
3821 g_assert_not_reached ();
3822 } else if (ainfo->regtype == RegTypeBase) {
3823 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
3824 ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3826 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3827 ARM_LDR_REG_REG (code, inst->dreg, ARMREG_SP, ARMREG_IP);
3830 g_assert_not_reached ();
3832 if (cfg->verbose_level > 2)
3833 g_print ("Argument %d assigned to register %s\n", pos, mono_arch_regname (inst->dreg));
3835 /* the argument should be put on the stack: FIXME handle size != word */
3836 if (ainfo->regtype == RegTypeGeneral) {
3837 switch (ainfo->size) {
3839 if (arm_is_imm12 (inst->inst_offset))
3840 ARM_STRB_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3842 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3843 ARM_STRB_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3847 if (arm_is_imm8 (inst->inst_offset)) {
3848 ARM_STRH_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3850 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3851 ARM_STRH_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3855 g_assert (arm_is_imm12 (inst->inst_offset));
3856 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3857 g_assert (arm_is_imm12 (inst->inst_offset + 4));
3858 ARM_STR_IMM (code, ainfo->reg + 1, inst->inst_basereg, inst->inst_offset + 4);
3861 if (arm_is_imm12 (inst->inst_offset)) {
3862 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3864 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3865 ARM_STR_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3869 } else if (ainfo->regtype == RegTypeBaseGen) {
3870 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
3871 g_assert (arm_is_imm12 (inst->inst_offset));
3872 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3873 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3874 ARM_STR_IMM (code, ARMREG_R3, inst->inst_basereg, inst->inst_offset);
3875 } else if (ainfo->regtype == RegTypeBase) {
3876 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
3877 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3879 code = mono_arm_emit_load_imm (code, ARMREG_IP, prev_sp_offset + ainfo->offset);
3880 ARM_LDR_REG_REG (code, ARMREG_LR, ARMREG_SP, ARMREG_IP);
3883 switch (ainfo->size) {
3885 if (arm_is_imm8 (inst->inst_offset)) {
3886 ARM_STRB_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3888 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3889 ARM_STRB_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3893 if (arm_is_imm8 (inst->inst_offset)) {
3894 ARM_STRH_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3896 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3897 ARM_STRH_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3901 if (arm_is_imm12 (inst->inst_offset)) {
3902 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3904 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3905 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3907 if (arm_is_imm12 (prev_sp_offset + ainfo->offset + 4)) {
3908 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset + 4));
3910 code = mono_arm_emit_load_imm (code, ARMREG_IP, prev_sp_offset + ainfo->offset + 4);
3911 ARM_LDR_REG_REG (code, ARMREG_LR, ARMREG_SP, ARMREG_IP);
3913 if (arm_is_imm12 (inst->inst_offset + 4)) {
3914 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3916 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset + 4);
3917 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3921 if (arm_is_imm12 (inst->inst_offset)) {
3922 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3924 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3925 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3929 } else if (ainfo->regtype == RegTypeFP) {
3930 g_assert_not_reached ();
3931 } else if (ainfo->regtype == RegTypeStructByVal) {
3932 int doffset = inst->inst_offset;
3936 size = mini_type_stack_size_full (cfg->generic_sharing_context, inst->inst_vtype, NULL, sig->pinvoke);
3937 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
3938 if (arm_is_imm12 (doffset)) {
3939 ARM_STR_IMM (code, ainfo->reg + cur_reg, inst->inst_basereg, doffset);
3941 code = mono_arm_emit_load_imm (code, ARMREG_IP, doffset);
3942 ARM_STR_REG_REG (code, ainfo->reg + cur_reg, inst->inst_basereg, ARMREG_IP);
3944 soffset += sizeof (gpointer);
3945 doffset += sizeof (gpointer);
3947 if (ainfo->vtsize) {
3948 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
3949 //g_print ("emit_memcpy (prev_sp_ofs: %d, ainfo->offset: %d, soffset: %d)\n", prev_sp_offset, ainfo->offset, soffset);
3950 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, doffset, ARMREG_SP, prev_sp_offset + ainfo->offset);
3952 } else if (ainfo->regtype == RegTypeStructByAddr) {
3953 g_assert_not_reached ();
3954 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
3955 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, inst->inst_offset, ainfo->reg, 0);
3957 g_assert_not_reached ();
3962 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
3963 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->domain);
3964 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3965 (gpointer)"mono_jit_thread_attach");
3966 code = emit_call_seq (cfg, code);
3969 if (method->save_lmf) {
3970 gboolean get_lmf_fast = FALSE;
3972 #ifdef HAVE_AEABI_READ_TP
3973 gint32 lmf_addr_tls_offset = mono_get_lmf_addr_tls_offset ();
3975 if (lmf_addr_tls_offset != -1) {
3976 get_lmf_fast = TRUE;
3978 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3979 (gpointer)"__aeabi_read_tp");
3980 code = emit_call_seq (cfg, code);
3982 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, lmf_addr_tls_offset);
3983 get_lmf_fast = TRUE;
3986 if (!get_lmf_fast) {
3987 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3988 (gpointer)"mono_get_lmf_addr");
3989 code = emit_call_seq (cfg, code);
3991 /* we build the MonoLMF structure on the stack - see mini-arm.h */
3992 /* lmf_offset is the offset from the previous stack pointer,
3993 * alloc_size is the total stack space allocated, so the offset
3994 * of MonoLMF from the current stack ptr is alloc_size - lmf_offset.
3995 * The pointer to the struct is put in r1 (new_lmf).
3996 * r2 is used as scratch
3997 * The callee-saved registers are already in the MonoLMF structure
3999 code = emit_big_add (code, ARMREG_R1, ARMREG_SP, alloc_size - lmf_offset);
4000 /* r0 is the result from mono_get_lmf_addr () */
4001 ARM_STR_IMM (code, ARMREG_R0, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
4002 /* new_lmf->previous_lmf = *lmf_addr */
4003 ARM_LDR_IMM (code, ARMREG_R2, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4004 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4005 /* *(lmf_addr) = r1 */
4006 ARM_STR_IMM (code, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4007 /* Skip method (only needed for trampoline LMF frames) */
4008 ARM_STR_IMM (code, ARMREG_SP, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, ebp));
4009 /* save the current IP */
4010 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_PC);
4011 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, eip));
4015 code = mono_arch_instrument_prolog (cfg, mono_trace_enter_method, code, TRUE);
4017 cfg->code_len = code - cfg->native_code;
4018 g_assert (cfg->code_len < cfg->code_size);
4025 mono_arch_emit_epilog (MonoCompile *cfg)
4027 MonoMethod *method = cfg->method;
4028 int pos, i, rot_amount;
4029 int max_epilog_size = 16 + 20*4;
4032 if (cfg->method->save_lmf)
4033 max_epilog_size += 128;
4035 if (mono_jit_trace_calls != NULL)
4036 max_epilog_size += 50;
4038 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE)
4039 max_epilog_size += 50;
4041 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
4042 cfg->code_size *= 2;
4043 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
4044 mono_jit_stats.code_reallocs++;
4048 * Keep in sync with OP_JMP
4050 code = cfg->native_code + cfg->code_len;
4052 if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) {
4053 code = mono_arch_instrument_epilog (cfg, mono_trace_leave_method, code, TRUE);
4057 if (method->save_lmf) {
4059 /* all but r0-r3, sp and pc */
4060 pos += sizeof (MonoLMF) - (4 * 10);
4062 /* r2 contains the pointer to the current LMF */
4063 code = emit_big_add (code, ARMREG_R2, cfg->frame_reg, cfg->stack_usage - lmf_offset);
4064 /* ip = previous_lmf */
4065 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4067 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
4068 /* *(lmf_addr) = previous_lmf */
4069 ARM_STR_IMM (code, ARMREG_IP, ARMREG_LR, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4070 /* FIXME: speedup: there is no actual need to restore the registers if
4071 * we didn't actually change them (idea from Zoltan).
4074 /* point sp at the registers to restore: 10 is 14 -4, because we skip r0-r3 */
4075 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_R2, (sizeof (MonoLMF) - 10 * sizeof (gulong)));
4076 ARM_POP_NWB (code, 0xaff0); /* restore ip to sp and lr to pc */
4078 if ((i = mono_arm_is_rotated_imm8 (cfg->stack_usage, &rot_amount)) >= 0) {
4079 ARM_ADD_REG_IMM (code, ARMREG_SP, cfg->frame_reg, i, rot_amount);
4081 code = mono_arm_emit_load_imm (code, ARMREG_IP, cfg->stack_usage);
4082 ARM_ADD_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
4084 /* FIXME: add v4 thumb interworking support */
4085 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP) | (1 << ARMREG_PC)));
4088 cfg->code_len = code - cfg->native_code;
4090 g_assert (cfg->code_len < cfg->code_size);
4094 /* remove once throw_exception_by_name is eliminated */
4096 exception_id_by_name (const char *name)
4098 if (strcmp (name, "IndexOutOfRangeException") == 0)
4099 return MONO_EXC_INDEX_OUT_OF_RANGE;
4100 if (strcmp (name, "OverflowException") == 0)
4101 return MONO_EXC_OVERFLOW;
4102 if (strcmp (name, "ArithmeticException") == 0)
4103 return MONO_EXC_ARITHMETIC;
4104 if (strcmp (name, "DivideByZeroException") == 0)
4105 return MONO_EXC_DIVIDE_BY_ZERO;
4106 if (strcmp (name, "InvalidCastException") == 0)
4107 return MONO_EXC_INVALID_CAST;
4108 if (strcmp (name, "NullReferenceException") == 0)
4109 return MONO_EXC_NULL_REF;
4110 if (strcmp (name, "ArrayTypeMismatchException") == 0)
4111 return MONO_EXC_ARRAY_TYPE_MISMATCH;
4112 g_error ("Unknown intrinsic exception %s\n", name);
4117 mono_arch_emit_exceptions (MonoCompile *cfg)
4119 MonoJumpInfo *patch_info;
4122 const guint8* exc_throw_pos [MONO_EXC_INTRINS_NUM] = {NULL};
4123 guint8 exc_throw_found [MONO_EXC_INTRINS_NUM] = {0};
4124 int max_epilog_size = 50;
4126 /* count the number of exception infos */
4129 * make sure we have enough space for exceptions
4131 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
4132 if (patch_info->type == MONO_PATCH_INFO_EXC) {
4133 i = exception_id_by_name (patch_info->data.target);
4134 if (!exc_throw_found [i]) {
4135 max_epilog_size += 32;
4136 exc_throw_found [i] = TRUE;
4141 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
4142 cfg->code_size *= 2;
4143 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
4144 mono_jit_stats.code_reallocs++;
4147 code = cfg->native_code + cfg->code_len;
4149 /* add code to raise exceptions */
4150 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
4151 switch (patch_info->type) {
4152 case MONO_PATCH_INFO_EXC: {
4153 MonoClass *exc_class;
4154 unsigned char *ip = patch_info->ip.i + cfg->native_code;
4156 i = exception_id_by_name (patch_info->data.target);
4157 if (exc_throw_pos [i]) {
4158 arm_patch (ip, exc_throw_pos [i]);
4159 patch_info->type = MONO_PATCH_INFO_NONE;
4162 exc_throw_pos [i] = code;
4164 arm_patch (ip, code);
4166 exc_class = mono_class_from_name (mono_defaults.corlib, "System", patch_info->data.name);
4167 g_assert (exc_class);
4169 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_LR);
4170 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
4171 patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD;
4172 patch_info->data.name = "mono_arch_throw_corlib_exception";
4173 patch_info->ip.i = code - cfg->native_code;
4175 *(guint32*)(gpointer)code = exc_class->type_token;
4185 cfg->code_len = code - cfg->native_code;
4187 g_assert (cfg->code_len < cfg->code_size);
4191 static gboolean tls_offset_inited = FALSE;
4194 mono_arch_setup_jit_tls_data (MonoJitTlsData *tls)
4196 if (!tls_offset_inited) {
4197 tls_offset_inited = TRUE;
4199 lmf_tls_offset = mono_get_lmf_tls_offset ();
4200 lmf_addr_tls_offset = mono_get_lmf_addr_tls_offset ();
4205 mono_arch_free_jit_tls_data (MonoJitTlsData *tls)
4210 mono_arch_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
4217 mono_arch_print_tree (MonoInst *tree, int arity)
4223 mono_arch_get_domain_intrinsic (MonoCompile* cfg)
4225 return mono_get_domain_intrinsic (cfg);
4229 mono_arch_get_thread_intrinsic (MonoCompile* cfg)
4231 return mono_get_thread_intrinsic (cfg);
4235 mono_arch_get_patch_offset (guint8 *code)
4242 mono_arch_flush_register_windows (void)
4247 mono_arch_fixup_jinfo (MonoCompile *cfg)
4251 #ifdef MONO_ARCH_HAVE_IMT
4254 mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoInst *imt_arg)
4256 if (cfg->compile_aot) {
4257 int method_reg = mono_alloc_ireg (cfg);
4260 call->dynamic_imt_arg = TRUE;
4262 MONO_INST_NEW (cfg, ins, OP_AOTCONST);
4263 ins->dreg = method_reg;
4264 ins->inst_p0 = call->method;
4265 ins->inst_c1 = MONO_PATCH_INFO_METHODCONST;
4266 MONO_ADD_INS (cfg->cbb, ins);
4268 mono_call_inst_add_outarg_reg (cfg, call, method_reg, ARMREG_V5, FALSE);
4269 } else if (cfg->generic_context) {
4271 /* Always pass in a register for simplicity */
4272 call->dynamic_imt_arg = TRUE;
4274 cfg->uses_rgctx_reg = TRUE;
4277 mono_call_inst_add_outarg_reg (cfg, call, imt_arg->dreg, ARMREG_V5, FALSE);
4280 int method_reg = mono_alloc_preg (cfg);
4282 MONO_INST_NEW (cfg, ins, OP_PCONST);
4283 ins->inst_p0 = call->method;
4284 ins->dreg = method_reg;
4285 MONO_ADD_INS (cfg->cbb, ins);
4287 mono_call_inst_add_outarg_reg (cfg, call, method_reg, ARMREG_V5, FALSE);
4293 mono_arch_find_imt_method (gpointer *regs, guint8 *code)
4295 guint32 *code_ptr = (guint32*)code;
4297 /* The IMT value is stored in the code stream right after the LDC instruction. */
4298 if (!IS_LDR_PC (code_ptr [0])) {
4299 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]);
4300 g_assert (IS_LDR_PC (code_ptr [0]));
4302 if (code_ptr [1] == 0)
4303 /* This is AOTed code, the IMT method is in V5 */
4304 return (MonoMethod*)regs [ARMREG_V5];
4306 return (MonoMethod*) code_ptr [1];
4310 mono_arch_find_this_argument (gpointer *regs, MonoMethod *method, MonoGenericSharingContext *gsctx)
4312 return mono_arch_get_this_arg_from_call (gsctx, mono_method_signature (method), (gssize*)regs, NULL);
4316 mono_arch_find_static_call_vtable (gpointer *regs, guint8 *code)
4318 return (MonoVTable*) regs [MONO_ARCH_RGCTX_REG];
4321 #define ENABLE_WRONG_METHOD_CHECK 0
4322 #define BASE_SIZE (6 * 4)
4323 #define BSEARCH_ENTRY_SIZE (4 * 4)
4324 #define CMP_SIZE (3 * 4)
4325 #define BRANCH_SIZE (1 * 4)
4326 #define CALL_SIZE (2 * 4)
4327 #define WMC_SIZE (5 * 4)
4328 #define DISTANCE(A, B) (((gint32)(B)) - ((gint32)(A)))
4331 arm_emit_value_and_patch_ldr (arminstr_t *code, arminstr_t *target, guint32 value)
4333 guint32 delta = DISTANCE (target, code);
4335 g_assert (delta >= 0 && delta <= 0xFFF);
4336 *target = *target | delta;
4342 mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count,
4343 gpointer fail_tramp)
4345 int size, i, extra_space = 0;
4346 arminstr_t *code, *start, *vtable_target = NULL;
4347 gboolean large_offsets = FALSE;
4348 guint32 **constant_pool_starts;
4351 constant_pool_starts = g_new0 (guint32*, count);
4354 * We might be called with a fail_tramp from the IMT builder code even if
4355 * MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK is not defined.
4357 //g_assert (!fail_tramp);
4359 for (i = 0; i < count; ++i) {
4360 MonoIMTCheckItem *item = imt_entries [i];
4361 if (item->is_equals) {
4362 if (!arm_is_imm12 (DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]))) {
4363 item->chunk_size += 32;
4364 large_offsets = TRUE;
4367 if (item->check_target_idx) {
4368 if (!item->compare_done)
4369 item->chunk_size += CMP_SIZE;
4370 item->chunk_size += BRANCH_SIZE;
4372 #if ENABLE_WRONG_METHOD_CHECK
4373 item->chunk_size += WMC_SIZE;
4376 item->chunk_size += CALL_SIZE;
4378 item->chunk_size += BSEARCH_ENTRY_SIZE;
4379 imt_entries [item->check_target_idx]->compare_done = TRUE;
4381 size += item->chunk_size;
4385 size += 4 * count; /* The ARM_ADD_REG_IMM to pop the stack */
4387 start = code = mono_domain_code_reserve (domain, size);
4390 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);
4391 for (i = 0; i < count; ++i) {
4392 MonoIMTCheckItem *item = imt_entries [i];
4393 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);
4398 ARM_PUSH4 (code, ARMREG_R0, ARMREG_R1, ARMREG_IP, ARMREG_PC);
4400 ARM_PUSH2 (code, ARMREG_R0, ARMREG_R1);
4401 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, -4);
4402 vtable_target = code;
4403 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
4405 /* R0 == 0 means we are called from AOT code. In this case, V5 contains the IMT method */
4406 ARM_CMP_REG_IMM8 (code, ARMREG_R0, 0);
4407 ARM_MOV_REG_REG_COND (code, ARMREG_R0, ARMREG_V5, ARMCOND_EQ);
4409 for (i = 0; i < count; ++i) {
4410 MonoIMTCheckItem *item = imt_entries [i];
4411 arminstr_t *imt_method = NULL, *vtable_offset_ins = NULL;
4412 gint32 vtable_offset;
4414 item->code_target = (guint8*)code;
4416 if (item->is_equals) {
4417 if (item->check_target_idx) {
4418 if (!item->compare_done) {
4420 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4421 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
4423 item->jmp_code = (guint8*)code;
4424 ARM_B_COND (code, ARMCOND_NE, 0);
4426 /*Enable the commented code to assert on wrong method*/
4427 #if ENABLE_WRONG_METHOD_CHECK
4429 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4430 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
4431 ARM_B_COND (code, ARMCOND_NE, 1);
4437 vtable_offset = DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]);
4438 if (!arm_is_imm12 (vtable_offset)) {
4440 * We need to branch to a computed address but we don't have
4441 * a free register to store it, since IP must contain the
4442 * vtable address. So we push the two values to the stack, and
4443 * load them both using LDM.
4445 /* Compute target address */
4446 vtable_offset_ins = code;
4447 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4448 ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_IP, ARMREG_R1);
4449 /* Save it to the fourth slot */
4450 ARM_STR_IMM (code, ARMREG_R1, ARMREG_SP, 3 * sizeof (gpointer));
4451 /* Restore registers and branch */
4452 ARM_POP4 (code, ARMREG_R0, ARMREG_R1, ARMREG_IP, ARMREG_PC);
4454 code = arm_emit_value_and_patch_ldr (code, vtable_offset_ins, vtable_offset);
4456 ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
4458 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 2 * sizeof (gpointer));
4459 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, vtable_offset);
4463 code = arm_emit_value_and_patch_ldr (code, imt_method, (guint32)item->key);
4465 /*must emit after unconditional branch*/
4466 if (vtable_target) {
4467 code = arm_emit_value_and_patch_ldr (code, vtable_target, (guint32)vtable);
4468 item->chunk_size += 4;
4469 vtable_target = NULL;
4472 /*We reserve the space for bsearch IMT values after the first entry with an absolute jump*/
4473 constant_pool_starts [i] = code;
4475 code += extra_space;
4479 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4480 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
4482 item->jmp_code = (guint8*)code;
4483 ARM_B_COND (code, ARMCOND_GE, 0);
4488 for (i = 0; i < count; ++i) {
4489 MonoIMTCheckItem *item = imt_entries [i];
4490 if (item->jmp_code) {
4491 if (item->check_target_idx)
4492 arm_patch (item->jmp_code, imt_entries [item->check_target_idx]->code_target);
4494 if (i > 0 && item->is_equals) {
4496 arminstr_t *space_start = constant_pool_starts [i];
4497 for (j = i - 1; j >= 0 && !imt_entries [j]->is_equals; --j) {
4498 space_start = arm_emit_value_and_patch_ldr (space_start, (arminstr_t*)imt_entries [j]->code_target, (guint32)imt_entries [j]->key);
4505 char *buff = g_strdup_printf ("thunk_for_class_%s_%s_entries_%d", vtable->klass->name_space, vtable->klass->name, count);
4506 mono_disassemble_code (NULL, (guint8*)start, size, buff);
4511 g_free (constant_pool_starts);
4513 mono_arch_flush_icache ((guint8*)start, size);
4514 mono_stats.imt_thunks_size += code - start;
4516 g_assert (DISTANCE (start, code) <= size);
4523 mono_arch_context_get_int_reg (MonoContext *ctx, int reg)
4525 if (reg >= 4 && reg <= 11)
4526 return (gpointer)ctx->regs [reg - 4];
4527 else if (reg == ARMREG_IP)
4528 return (gpointer)ctx->regs [8];
4529 else if (reg == ARMREG_LR)
4530 return (gpointer)ctx->regs [9];
4532 g_assert_not_reached ();