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);
345 #define MAX_ARCH_DELEGATE_PARAMS 3
348 get_delegate_invoke_impl (gboolean has_target, gboolean param_count, guint32 *code_size)
350 guint8 *code, *start;
353 start = code = mono_global_codeman_reserve (12);
355 /* Replace the this argument with the target */
356 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, method_ptr));
357 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, target));
358 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
360 g_assert ((code - start) <= 12);
362 mono_arch_flush_icache (start, 12);
366 size = 8 + param_count * 4;
367 start = code = mono_global_codeman_reserve (size);
369 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, method_ptr));
370 /* slide down the arguments */
371 for (i = 0; i < param_count; ++i) {
372 ARM_MOV_REG_REG (code, (ARMREG_R0 + i), (ARMREG_R0 + i + 1));
374 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
376 g_assert ((code - start) <= size);
378 mono_arch_flush_icache (start, size);
382 *code_size = code - start;
388 * mono_arch_get_delegate_invoke_impls:
390 * Return a list of MonoAotTrampInfo structures for the delegate invoke impl
394 mono_arch_get_delegate_invoke_impls (void)
401 code = get_delegate_invoke_impl (TRUE, 0, &code_len);
402 res = g_slist_prepend (res, mono_aot_tramp_info_create (g_strdup ("delegate_invoke_impl_has_target"), code, code_len));
404 for (i = 0; i < MAX_ARCH_DELEGATE_PARAMS; ++i) {
405 code = get_delegate_invoke_impl (FALSE, i, &code_len);
406 res = g_slist_prepend (res, mono_aot_tramp_info_create (g_strdup_printf ("delegate_invoke_impl_target_%d", i), code, code_len));
413 mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target)
415 guint8 *code, *start;
417 /* FIXME: Support more cases */
418 if (MONO_TYPE_ISSTRUCT (sig->ret))
422 static guint8* cached = NULL;
423 mono_mini_arch_lock ();
425 mono_mini_arch_unlock ();
430 start = mono_aot_get_named_code ("delegate_invoke_impl_has_target");
432 start = get_delegate_invoke_impl (TRUE, 0, NULL);
434 mono_mini_arch_unlock ();
437 static guint8* cache [MAX_ARCH_DELEGATE_PARAMS + 1] = {NULL};
440 if (sig->param_count > MAX_ARCH_DELEGATE_PARAMS)
442 for (i = 0; i < sig->param_count; ++i)
443 if (!mono_is_regsize_var (sig->params [i]))
446 mono_mini_arch_lock ();
447 code = cache [sig->param_count];
449 mono_mini_arch_unlock ();
454 char *name = g_strdup_printf ("delegate_invoke_impl_target_%d", sig->param_count);
455 start = mono_aot_get_named_code (name);
458 start = get_delegate_invoke_impl (FALSE, sig->param_count, NULL);
460 cache [sig->param_count] = start;
461 mono_mini_arch_unlock ();
469 mono_arch_get_this_arg_from_call (MonoGenericSharingContext *gsctx, MonoMethodSignature *sig, gssize *regs, guint8 *code)
471 /* FIXME: handle returning a struct */
472 if (MONO_TYPE_ISSTRUCT (sig->ret))
473 return (gpointer)regs [ARMREG_R1];
474 return (gpointer)regs [ARMREG_R0];
478 * Initialize the cpu to execute managed code.
481 mono_arch_cpu_init (void)
486 * Initialize architecture specific code.
489 mono_arch_init (void)
491 InitializeCriticalSection (&mini_arch_mutex);
495 * Cleanup architecture specific code.
498 mono_arch_cleanup (void)
503 * This function returns the optimizations supported on this cpu.
506 mono_arch_cpu_optimizazions (guint32 *exclude_mask)
510 thumb_supported = TRUE;
515 FILE *file = fopen ("/proc/cpuinfo", "r");
517 while ((line = fgets (buf, 512, file))) {
518 if (strncmp (line, "Processor", 9) == 0) {
519 char *ver = strstr (line, "(v");
520 if (ver && (ver [2] == '5' || ver [2] == '6' || ver [2] == '7')) {
525 if (strncmp (line, "Features", 8) == 0) {
526 char *th = strstr (line, "thumb");
528 thumb_supported = TRUE;
536 /*printf ("features: v5: %d, thumb: %d\n", v5_supported, thumb_supported);*/
540 /* no arm-specific optimizations yet */
546 is_regsize_var (MonoType *t) {
549 t = mini_type_get_underlying_type (NULL, t);
556 case MONO_TYPE_FNPTR:
558 case MONO_TYPE_OBJECT:
559 case MONO_TYPE_STRING:
560 case MONO_TYPE_CLASS:
561 case MONO_TYPE_SZARRAY:
562 case MONO_TYPE_ARRAY:
564 case MONO_TYPE_GENERICINST:
565 if (!mono_type_generic_inst_is_valuetype (t))
568 case MONO_TYPE_VALUETYPE:
575 mono_arch_get_allocatable_int_vars (MonoCompile *cfg)
580 for (i = 0; i < cfg->num_varinfo; i++) {
581 MonoInst *ins = cfg->varinfo [i];
582 MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
585 if (vmv->range.first_use.abs_pos >= vmv->range.last_use.abs_pos)
588 if (ins->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT) || (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG))
591 /* we can only allocate 32 bit values */
592 if (is_regsize_var (ins->inst_vtype)) {
593 g_assert (MONO_VARINFO (cfg, i)->reg == -1);
594 g_assert (i == vmv->idx);
595 vars = mono_varlist_insert_sorted (cfg, vars, vmv, FALSE);
602 #define USE_EXTRA_TEMPS 0
605 mono_arch_get_global_int_regs (MonoCompile *cfg)
608 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V1));
609 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V2));
610 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V3));
611 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V4));
612 if (!(cfg->compile_aot || cfg->uses_rgctx_reg))
613 /* V5 is reserved for passing the vtable/rgctx/IMT method */
614 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V5));
615 /*regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V6));*/
616 /*regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V7));*/
622 * mono_arch_regalloc_cost:
624 * Return the cost, in number of memory references, of the action of
625 * allocating the variable VMV into a register during global register
629 mono_arch_regalloc_cost (MonoCompile *cfg, MonoMethodVar *vmv)
635 #ifndef __GNUC_PREREQ
636 #define __GNUC_PREREQ(maj, min) (0)
640 mono_arch_flush_icache (guint8 *code, gint size)
643 sys_icache_invalidate (code, size);
644 #elif __GNUC_PREREQ(4, 1)
645 __clear_cache (code, code + size);
646 #elif defined(PLATFORM_ANDROID)
647 const int syscall = 0xf0002;
655 : "r" (code), "r" (code + size), "r" (syscall)
659 __asm __volatile ("mov r0, %0\n"
662 "swi 0x9f0002 @ sys_cacheflush"
664 : "r" (code), "r" (code + size), "r" (0)
665 : "r0", "r1", "r3" );
680 guint16 vtsize; /* in param area */
682 guint8 regtype : 4; /* 0 general, 1 basereg, 2 floating point register, see RegType* */
683 guint8 size : 4; /* 1, 2, 4, 8, or regs used by RegTypeStructByVal */
698 add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple)
701 if (*gr > ARMREG_R3) {
702 ainfo->offset = *stack_size;
703 ainfo->reg = ARMREG_SP; /* in the caller */
704 ainfo->regtype = RegTypeBase;
715 /* first word in r3 and the second on the stack */
716 ainfo->offset = *stack_size;
717 ainfo->reg = ARMREG_SP; /* in the caller */
718 ainfo->regtype = RegTypeBaseGen;
720 } else if (*gr >= ARMREG_R3) {
725 ainfo->offset = *stack_size;
726 ainfo->reg = ARMREG_SP; /* in the caller */
727 ainfo->regtype = RegTypeBase;
742 get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke)
745 int n = sig->hasthis + sig->param_count;
746 MonoType *simpletype;
747 guint32 stack_size = 0;
748 CallInfo *cinfo = g_malloc0 (sizeof (CallInfo) + sizeof (ArgInfo) * n);
752 /* FIXME: handle returning a struct */
753 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
754 add_general (&gr, &stack_size, &cinfo->ret, TRUE);
755 cinfo->struct_ret = ARMREG_R0;
760 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
763 DEBUG(printf("params: %d\n", sig->param_count));
764 for (i = 0; i < sig->param_count; ++i) {
765 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
766 /* Prevent implicit arguments and sig_cookie from
767 being passed in registers */
769 /* Emit the signature cookie just before the implicit arguments */
770 add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE);
772 DEBUG(printf("param %d: ", i));
773 if (sig->params [i]->byref) {
774 DEBUG(printf("byref\n"));
775 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
779 simpletype = mini_type_get_underlying_type (NULL, sig->params [i]);
780 switch (simpletype->type) {
781 case MONO_TYPE_BOOLEAN:
784 cinfo->args [n].size = 1;
785 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
791 cinfo->args [n].size = 2;
792 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
797 cinfo->args [n].size = 4;
798 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
804 case MONO_TYPE_FNPTR:
805 case MONO_TYPE_CLASS:
806 case MONO_TYPE_OBJECT:
807 case MONO_TYPE_STRING:
808 case MONO_TYPE_SZARRAY:
809 case MONO_TYPE_ARRAY:
811 cinfo->args [n].size = sizeof (gpointer);
812 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
815 case MONO_TYPE_GENERICINST:
816 if (!mono_type_generic_inst_is_valuetype (sig->params [i])) {
817 cinfo->args [n].size = sizeof (gpointer);
818 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
823 case MONO_TYPE_TYPEDBYREF:
824 case MONO_TYPE_VALUETYPE: {
830 if (simpletype->type == MONO_TYPE_TYPEDBYREF) {
831 size = sizeof (MonoTypedRef);
832 align = sizeof (gpointer);
834 MonoClass *klass = mono_class_from_mono_type (sig->params [i]);
836 size = mono_class_native_size (klass, &align);
838 size = mono_class_value_size (klass, &align);
840 DEBUG(printf ("load %d bytes struct\n",
841 mono_class_native_size (sig->params [i]->data.klass, NULL)));
844 align_size += (sizeof (gpointer) - 1);
845 align_size &= ~(sizeof (gpointer) - 1);
846 nwords = (align_size + sizeof (gpointer) -1 ) / sizeof (gpointer);
847 cinfo->args [n].regtype = RegTypeStructByVal;
848 /* FIXME: align stack_size if needed */
850 if (align >= 8 && (gr & 1))
853 if (gr > ARMREG_R3) {
854 cinfo->args [n].size = 0;
855 cinfo->args [n].vtsize = nwords;
857 int rest = ARMREG_R3 - gr + 1;
858 int n_in_regs = rest >= nwords? nwords: rest;
860 cinfo->args [n].size = n_in_regs;
861 cinfo->args [n].vtsize = nwords - n_in_regs;
862 cinfo->args [n].reg = gr;
865 cinfo->args [n].offset = stack_size;
866 /*g_print ("offset for arg %d at %d\n", n, stack_size);*/
867 stack_size += nwords * sizeof (gpointer);
874 cinfo->args [n].size = 8;
875 add_general (&gr, &stack_size, cinfo->args + n, FALSE);
879 g_error ("Can't trampoline 0x%x", sig->params [i]->type);
884 simpletype = mini_type_get_underlying_type (NULL, sig->ret);
885 switch (simpletype->type) {
886 case MONO_TYPE_BOOLEAN:
897 case MONO_TYPE_FNPTR:
898 case MONO_TYPE_CLASS:
899 case MONO_TYPE_OBJECT:
900 case MONO_TYPE_SZARRAY:
901 case MONO_TYPE_ARRAY:
902 case MONO_TYPE_STRING:
903 cinfo->ret.reg = ARMREG_R0;
907 cinfo->ret.reg = ARMREG_R0;
911 cinfo->ret.reg = ARMREG_R0;
912 /* FIXME: cinfo->ret.reg = ???;
913 cinfo->ret.regtype = RegTypeFP;*/
915 case MONO_TYPE_GENERICINST:
916 if (!mono_type_generic_inst_is_valuetype (sig->ret)) {
917 cinfo->ret.reg = ARMREG_R0;
921 case MONO_TYPE_VALUETYPE:
923 case MONO_TYPE_TYPEDBYREF:
927 g_error ("Can't handle as return value 0x%x", sig->ret->type);
931 /* align stack size to 8 */
932 DEBUG (printf (" stack size: %d (%d)\n", (stack_size + 15) & ~15, stack_size));
933 stack_size = (stack_size + 7) & ~7;
935 cinfo->stack_usage = stack_size;
941 * Set var information according to the calling convention. arm version.
942 * The locals var stuff should most likely be split in another method.
945 mono_arch_allocate_vars (MonoCompile *cfg)
947 MonoMethodSignature *sig;
948 MonoMethodHeader *header;
950 int i, offset, size, align, curinst;
951 int frame_reg = ARMREG_FP;
953 /* FIXME: this will change when we use FP as gcc does */
954 cfg->flags |= MONO_CFG_HAS_SPILLUP;
956 /* allow room for the vararg method args: void* and long/double */
957 if (mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method))
958 cfg->param_area = MAX (cfg->param_area, sizeof (gpointer)*8);
960 header = mono_method_get_header (cfg->method);
963 * We use the frame register also for any method that has
964 * exception clauses. This way, when the handlers are called,
965 * the code will reference local variables using the frame reg instead of
966 * the stack pointer: if we had to restore the stack pointer, we'd
967 * corrupt the method frames that are already on the stack (since
968 * filters get called before stack unwinding happens) when the filter
969 * code would call any method (this also applies to finally etc.).
971 if ((cfg->flags & MONO_CFG_HAS_ALLOCA) || header->num_clauses)
972 frame_reg = ARMREG_FP;
973 cfg->frame_reg = frame_reg;
974 if (frame_reg != ARMREG_SP) {
975 cfg->used_int_regs |= 1 << frame_reg;
978 if (cfg->compile_aot || cfg->uses_rgctx_reg)
979 /* V5 is reserved for passing the vtable/rgctx/IMT method */
980 cfg->used_int_regs |= (1 << ARMREG_V5);
982 sig = mono_method_signature (cfg->method);
986 if (!MONO_TYPE_ISSTRUCT (sig->ret)) {
987 /* FIXME: handle long and FP values */
988 switch (mini_type_get_underlying_type (NULL, sig->ret)->type) {
992 cfg->ret->opcode = OP_REGVAR;
993 cfg->ret->inst_c0 = ARMREG_R0;
997 /* local vars are at a positive offset from the stack pointer */
999 * also note that if the function uses alloca, we use FP
1000 * to point at the local variables.
1002 offset = 0; /* linkage area */
1003 /* align the offset to 16 bytes: not sure this is needed here */
1005 //offset &= ~(8 - 1);
1007 /* add parameter area size for called functions */
1008 offset += cfg->param_area;
1011 if (cfg->flags & MONO_CFG_HAS_FPOUT)
1014 /* allow room to save the return value */
1015 if (mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method))
1018 /* the MonoLMF structure is stored just below the stack pointer */
1020 if (sig->call_convention == MONO_CALL_VARARG) {
1021 cfg->sig_cookie = 0;
1024 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
1025 inst = cfg->vret_addr;
1026 offset += sizeof(gpointer) - 1;
1027 offset &= ~(sizeof(gpointer) - 1);
1028 inst->inst_offset = offset;
1029 inst->opcode = OP_REGOFFSET;
1030 inst->inst_basereg = frame_reg;
1031 if (G_UNLIKELY (cfg->verbose_level > 1)) {
1032 printf ("vret_addr =");
1033 mono_print_ins (cfg->vret_addr);
1035 offset += sizeof(gpointer);
1036 if (sig->call_convention == MONO_CALL_VARARG)
1037 cfg->sig_cookie += sizeof (gpointer);
1040 curinst = cfg->locals_start;
1041 for (i = curinst; i < cfg->num_varinfo; ++i) {
1042 inst = cfg->varinfo [i];
1043 if ((inst->flags & MONO_INST_IS_DEAD) || inst->opcode == OP_REGVAR)
1046 /* inst->backend.is_pinvoke indicates native sized value types, this is used by the
1047 * pinvoke wrappers when they call functions returning structure */
1048 if (inst->backend.is_pinvoke && MONO_TYPE_ISSTRUCT (inst->inst_vtype) && inst->inst_vtype->type != MONO_TYPE_TYPEDBYREF) {
1050 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), &ualign);
1054 size = mono_type_size (inst->inst_vtype, &align);
1056 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
1057 * since it loads/stores misaligned words, which don't do the right thing.
1059 if (align < 4 && size >= 4)
1061 offset += align - 1;
1062 offset &= ~(align - 1);
1063 inst->inst_offset = offset;
1064 inst->opcode = OP_REGOFFSET;
1065 inst->inst_basereg = frame_reg;
1067 //g_print ("allocating local %d to %d\n", i, inst->inst_offset);
1072 inst = cfg->args [curinst];
1073 if (inst->opcode != OP_REGVAR) {
1074 inst->opcode = OP_REGOFFSET;
1075 inst->inst_basereg = frame_reg;
1076 offset += sizeof (gpointer) - 1;
1077 offset &= ~(sizeof (gpointer) - 1);
1078 inst->inst_offset = offset;
1079 offset += sizeof (gpointer);
1080 if (sig->call_convention == MONO_CALL_VARARG)
1081 cfg->sig_cookie += sizeof (gpointer);
1086 for (i = 0; i < sig->param_count; ++i) {
1087 inst = cfg->args [curinst];
1088 if (inst->opcode != OP_REGVAR) {
1089 inst->opcode = OP_REGOFFSET;
1090 inst->inst_basereg = frame_reg;
1091 size = mono_type_size (sig->params [i], &align);
1092 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
1093 * since it loads/stores misaligned words, which don't do the right thing.
1095 if (align < 4 && size >= 4)
1097 offset += align - 1;
1098 offset &= ~(align - 1);
1099 inst->inst_offset = offset;
1101 if ((sig->call_convention == MONO_CALL_VARARG) && (i < sig->sentinelpos))
1102 cfg->sig_cookie += size;
1107 /* align the offset to 8 bytes */
1112 cfg->stack_offset = offset;
1116 mono_arch_create_vars (MonoCompile *cfg)
1118 MonoMethodSignature *sig;
1120 sig = mono_method_signature (cfg->method);
1122 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
1123 cfg->vret_addr = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_ARG);
1124 if (G_UNLIKELY (cfg->verbose_level > 1)) {
1125 printf ("vret_addr = ");
1126 mono_print_ins (cfg->vret_addr);
1132 mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
1135 MonoMethodSignature *sig;
1139 sig = call->signature;
1140 n = sig->param_count + sig->hasthis;
1142 cinfo = get_call_info (sig, sig->pinvoke);
1144 for (i = 0; i < n; ++i) {
1145 ArgInfo *ainfo = cinfo->args + i;
1148 if (i >= sig->hasthis)
1149 t = sig->params [i - sig->hasthis];
1151 t = &mono_defaults.int_class->byval_arg;
1152 t = mini_type_get_underlying_type (NULL, t);
1154 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
1159 in = call->args [i];
1161 switch (ainfo->regtype) {
1162 case RegTypeGeneral:
1163 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1164 MONO_INST_NEW (cfg, ins, OP_MOVE);
1165 ins->dreg = mono_alloc_ireg (cfg);
1166 ins->sreg1 = in->dreg + 1;
1167 MONO_ADD_INS (cfg->cbb, ins);
1168 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1170 MONO_INST_NEW (cfg, ins, OP_MOVE);
1171 ins->dreg = mono_alloc_ireg (cfg);
1172 ins->sreg1 = in->dreg + 2;
1173 MONO_ADD_INS (cfg->cbb, ins);
1174 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg + 1, FALSE);
1175 } else if (!t->byref && ((t->type == MONO_TYPE_R8) || (t->type == MONO_TYPE_R4))) {
1176 #ifndef MONO_ARCH_SOFT_FLOAT
1180 if (ainfo->size == 4) {
1181 #ifdef MONO_ARCH_SOFT_FLOAT
1182 /* mono_emit_call_args () have already done the r8->r4 conversion */
1183 /* The converted value is in an int vreg */
1184 MONO_INST_NEW (cfg, ins, OP_MOVE);
1185 ins->dreg = mono_alloc_ireg (cfg);
1186 ins->sreg1 = in->dreg;
1187 MONO_ADD_INS (cfg->cbb, ins);
1188 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1190 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1191 creg = mono_alloc_ireg (cfg);
1192 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1193 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg, FALSE);
1196 #ifdef MONO_ARCH_SOFT_FLOAT
1197 MONO_INST_NEW (cfg, ins, OP_FGETLOW32);
1198 ins->dreg = mono_alloc_ireg (cfg);
1199 ins->sreg1 = in->dreg;
1200 MONO_ADD_INS (cfg->cbb, ins);
1201 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1203 MONO_INST_NEW (cfg, ins, OP_FGETHIGH32);
1204 ins->dreg = mono_alloc_ireg (cfg);
1205 ins->sreg1 = in->dreg;
1206 MONO_ADD_INS (cfg->cbb, ins);
1207 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg + 1, FALSE);
1209 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1210 creg = mono_alloc_ireg (cfg);
1211 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1212 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg, FALSE);
1213 creg = mono_alloc_ireg (cfg);
1214 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8 + 4));
1215 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg + 1, FALSE);
1218 cfg->flags |= MONO_CFG_HAS_FPOUT;
1220 MONO_INST_NEW (cfg, ins, OP_MOVE);
1221 ins->dreg = mono_alloc_ireg (cfg);
1222 ins->sreg1 = in->dreg;
1223 MONO_ADD_INS (cfg->cbb, ins);
1225 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1228 case RegTypeStructByAddr:
1231 /* FIXME: where si the data allocated? */
1232 arg->backend.reg3 = ainfo->reg;
1233 call->used_iregs |= 1 << ainfo->reg;
1234 g_assert_not_reached ();
1237 case RegTypeStructByVal:
1238 MONO_INST_NEW (cfg, ins, OP_OUTARG_VT);
1239 ins->opcode = OP_OUTARG_VT;
1240 ins->sreg1 = in->dreg;
1241 ins->klass = in->klass;
1242 ins->inst_p0 = call;
1243 ins->inst_p1 = mono_mempool_alloc (cfg->mempool, sizeof (ArgInfo));
1244 memcpy (ins->inst_p1, ainfo, sizeof (ArgInfo));
1245 MONO_ADD_INS (cfg->cbb, ins);
1248 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1249 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1250 } else if (!t->byref && ((t->type == MONO_TYPE_R4) || (t->type == MONO_TYPE_R8))) {
1251 if (t->type == MONO_TYPE_R8) {
1252 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1254 #ifdef MONO_ARCH_SOFT_FLOAT
1255 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1257 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1261 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1264 case RegTypeBaseGen:
1265 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1266 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);
1267 MONO_INST_NEW (cfg, ins, OP_MOVE);
1268 ins->dreg = mono_alloc_ireg (cfg);
1269 ins->sreg1 = G_BYTE_ORDER == G_BIG_ENDIAN ? in->dreg + 2 : in->dreg + 1;
1270 MONO_ADD_INS (cfg->cbb, ins);
1271 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ARMREG_R3, FALSE);
1272 } else if (!t->byref && (t->type == MONO_TYPE_R8)) {
1275 #ifdef MONO_ARCH_SOFT_FLOAT
1276 g_assert_not_reached ();
1279 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1280 creg = mono_alloc_ireg (cfg);
1281 mono_call_inst_add_outarg_reg (cfg, call, creg, ARMREG_R3, FALSE);
1282 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1283 creg = mono_alloc_ireg (cfg);
1284 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 4));
1285 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, creg);
1286 cfg->flags |= MONO_CFG_HAS_FPOUT;
1288 g_assert_not_reached ();
1295 arg->backend.reg3 = ainfo->reg;
1296 /* FP args are passed in int regs */
1297 call->used_iregs |= 1 << ainfo->reg;
1298 if (ainfo->size == 8) {
1299 arg->opcode = OP_OUTARG_R8;
1300 call->used_iregs |= 1 << (ainfo->reg + 1);
1302 arg->opcode = OP_OUTARG_R4;
1305 cfg->flags |= MONO_CFG_HAS_FPOUT;
1309 g_assert_not_reached ();
1313 if (sig->ret && MONO_TYPE_ISSTRUCT (sig->ret)) {
1316 MONO_INST_NEW (cfg, vtarg, OP_MOVE);
1317 vtarg->sreg1 = call->vret_var->dreg;
1318 vtarg->dreg = mono_alloc_preg (cfg);
1319 MONO_ADD_INS (cfg->cbb, vtarg);
1321 mono_call_inst_add_outarg_reg (cfg, call, vtarg->dreg, cinfo->ret.reg, FALSE);
1324 call->stack_usage = cinfo->stack_usage;
1330 mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src)
1332 MonoCallInst *call = (MonoCallInst*)ins->inst_p0;
1333 ArgInfo *ainfo = ins->inst_p1;
1334 int ovf_size = ainfo->vtsize;
1335 int doffset = ainfo->offset;
1336 int i, soffset, dreg;
1339 for (i = 0; i < ainfo->size; ++i) {
1340 dreg = mono_alloc_ireg (cfg);
1341 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, src->dreg, soffset);
1342 mono_call_inst_add_outarg_reg (cfg, call, dreg, ainfo->reg + i, FALSE);
1343 soffset += sizeof (gpointer);
1345 //g_print ("vt size: %d at R%d + %d\n", doffset, vt->inst_basereg, vt->inst_offset);
1347 mini_emit_memcpy (cfg, ARMREG_SP, doffset, src->dreg, soffset, ovf_size * sizeof (gpointer), 0);
1351 mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val)
1353 MonoType *ret = mini_type_get_underlying_type (cfg->generic_sharing_context, mono_method_signature (method)->ret);
1356 if (ret->type == MONO_TYPE_I8 || ret->type == MONO_TYPE_U8) {
1359 MONO_INST_NEW (cfg, ins, OP_SETLRET);
1360 ins->sreg1 = val->dreg + 1;
1361 ins->sreg2 = val->dreg + 2;
1362 MONO_ADD_INS (cfg->cbb, ins);
1365 #ifdef MONO_ARCH_SOFT_FLOAT
1366 if (ret->type == MONO_TYPE_R8) {
1369 MONO_INST_NEW (cfg, ins, OP_SETFRET);
1370 ins->dreg = cfg->ret->dreg;
1371 ins->sreg1 = val->dreg;
1372 MONO_ADD_INS (cfg->cbb, ins);
1375 if (ret->type == MONO_TYPE_R4) {
1376 /* Already converted to an int in method_to_ir () */
1377 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
1380 #elif defined(ARM_FPU_VFP)
1381 if (ret->type == MONO_TYPE_R8 || ret->type == MONO_TYPE_R4) {
1384 MONO_INST_NEW (cfg, ins, OP_SETFRET);
1385 ins->dreg = cfg->ret->dreg;
1386 ins->sreg1 = val->dreg;
1387 MONO_ADD_INS (cfg->cbb, ins);
1391 if (ret->type == MONO_TYPE_R4 || ret->type == MONO_TYPE_R8) {
1392 MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, cfg->ret->dreg, val->dreg);
1399 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
1403 mono_arch_is_inst_imm (gint64 imm)
1409 * Allow tracing to work with this interface (with an optional argument)
1413 mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
1417 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1418 ARM_MOV_REG_IMM8 (code, ARMREG_R1, 0); /* NULL ebp for now */
1419 code = mono_arm_emit_load_imm (code, ARMREG_R2, (guint32)func);
1420 code = emit_call_reg (code, ARMREG_R2);
1433 mono_arch_instrument_epilog_full (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments, gboolean preserve_argument_registers)
1436 int save_mode = SAVE_NONE;
1438 MonoMethod *method = cfg->method;
1439 int rtype = mini_type_get_underlying_type (cfg->generic_sharing_context, mono_method_signature (method)->ret)->type;
1440 int save_offset = cfg->param_area;
1444 offset = code - cfg->native_code;
1445 /* we need about 16 instructions */
1446 if (offset > (cfg->code_size - 16 * 4)) {
1447 cfg->code_size *= 2;
1448 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
1449 code = cfg->native_code + offset;
1452 case MONO_TYPE_VOID:
1453 /* special case string .ctor icall */
1454 if (strcmp (".ctor", method->name) && method->klass == mono_defaults.string_class)
1455 save_mode = SAVE_ONE;
1457 save_mode = SAVE_NONE;
1461 save_mode = SAVE_TWO;
1465 save_mode = SAVE_FP;
1467 case MONO_TYPE_VALUETYPE:
1468 save_mode = SAVE_STRUCT;
1471 save_mode = SAVE_ONE;
1475 switch (save_mode) {
1477 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1478 ARM_STR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1479 if (enable_arguments) {
1480 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_R1);
1481 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1485 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1486 if (enable_arguments) {
1487 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1491 /* FIXME: what reg? */
1492 if (enable_arguments) {
1493 /* FIXME: what reg? */
1497 if (enable_arguments) {
1498 /* FIXME: get the actual address */
1499 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1507 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1508 code = mono_arm_emit_load_imm (code, ARMREG_IP, (guint32)func);
1509 code = emit_call_reg (code, ARMREG_IP);
1511 switch (save_mode) {
1513 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1514 ARM_LDR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1517 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1531 * The immediate field for cond branches is big enough for all reasonable methods
1533 #define EMIT_COND_BRANCH_FLAGS(ins,condcode) \
1534 if (0 && ins->inst_true_bb->native_offset) { \
1535 ARM_B_COND (code, (condcode), (code - cfg->native_code + ins->inst_true_bb->native_offset) & 0xffffff); \
1537 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_true_bb); \
1538 ARM_B_COND (code, (condcode), 0); \
1541 #define EMIT_COND_BRANCH(ins,cond) EMIT_COND_BRANCH_FLAGS(ins, branch_cc_table [(cond)])
1543 /* emit an exception if condition is fail
1545 * We assign the extra code used to throw the implicit exceptions
1546 * to cfg->bb_exit as far as the big branch handling is concerned
1548 #define EMIT_COND_SYSTEM_EXCEPTION_FLAGS(condcode,exc_name) \
1550 mono_add_patch_info (cfg, code - cfg->native_code, \
1551 MONO_PATCH_INFO_EXC, exc_name); \
1552 ARM_BL_COND (code, (condcode), 0); \
1555 #define EMIT_COND_SYSTEM_EXCEPTION(cond,exc_name) EMIT_COND_SYSTEM_EXCEPTION_FLAGS(branch_cc_table [(cond)], (exc_name))
1558 mono_arch_peephole_pass_1 (MonoCompile *cfg, MonoBasicBlock *bb)
1563 mono_arch_peephole_pass_2 (MonoCompile *cfg, MonoBasicBlock *bb)
1565 MonoInst *ins, *n, *last_ins = NULL;
1567 MONO_BB_FOR_EACH_INS_SAFE (bb, n, ins) {
1568 switch (ins->opcode) {
1571 /* Already done by an arch-independent pass */
1573 case OP_LOAD_MEMBASE:
1574 case OP_LOADI4_MEMBASE:
1576 * OP_STORE_MEMBASE_REG reg, offset(basereg)
1577 * OP_LOAD_MEMBASE offset(basereg), reg
1579 if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG
1580 || last_ins->opcode == OP_STORE_MEMBASE_REG) &&
1581 ins->inst_basereg == last_ins->inst_destbasereg &&
1582 ins->inst_offset == last_ins->inst_offset) {
1583 if (ins->dreg == last_ins->sreg1) {
1584 MONO_DELETE_INS (bb, ins);
1587 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1588 ins->opcode = OP_MOVE;
1589 ins->sreg1 = last_ins->sreg1;
1593 * Note: reg1 must be different from the basereg in the second load
1594 * OP_LOAD_MEMBASE offset(basereg), reg1
1595 * OP_LOAD_MEMBASE offset(basereg), reg2
1597 * OP_LOAD_MEMBASE offset(basereg), reg1
1598 * OP_MOVE reg1, reg2
1600 } if (last_ins && (last_ins->opcode == OP_LOADI4_MEMBASE
1601 || last_ins->opcode == OP_LOAD_MEMBASE) &&
1602 ins->inst_basereg != last_ins->dreg &&
1603 ins->inst_basereg == last_ins->inst_basereg &&
1604 ins->inst_offset == last_ins->inst_offset) {
1606 if (ins->dreg == last_ins->dreg) {
1607 MONO_DELETE_INS (bb, ins);
1610 ins->opcode = OP_MOVE;
1611 ins->sreg1 = last_ins->dreg;
1614 //g_assert_not_reached ();
1618 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1619 * OP_LOAD_MEMBASE offset(basereg), reg
1621 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1622 * OP_ICONST reg, imm
1624 } else if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_IMM
1625 || last_ins->opcode == OP_STORE_MEMBASE_IMM) &&
1626 ins->inst_basereg == last_ins->inst_destbasereg &&
1627 ins->inst_offset == last_ins->inst_offset) {
1628 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1629 ins->opcode = OP_ICONST;
1630 ins->inst_c0 = last_ins->inst_imm;
1631 g_assert_not_reached (); // check this rule
1635 case OP_LOADU1_MEMBASE:
1636 case OP_LOADI1_MEMBASE:
1637 if (last_ins && (last_ins->opcode == OP_STOREI1_MEMBASE_REG) &&
1638 ins->inst_basereg == last_ins->inst_destbasereg &&
1639 ins->inst_offset == last_ins->inst_offset) {
1640 ins->opcode = (ins->opcode == OP_LOADI1_MEMBASE) ? OP_ICONV_TO_I1 : OP_ICONV_TO_U1;
1641 ins->sreg1 = last_ins->sreg1;
1644 case OP_LOADU2_MEMBASE:
1645 case OP_LOADI2_MEMBASE:
1646 if (last_ins && (last_ins->opcode == OP_STOREI2_MEMBASE_REG) &&
1647 ins->inst_basereg == last_ins->inst_destbasereg &&
1648 ins->inst_offset == last_ins->inst_offset) {
1649 ins->opcode = (ins->opcode == OP_LOADI2_MEMBASE) ? OP_ICONV_TO_I2 : OP_ICONV_TO_U2;
1650 ins->sreg1 = last_ins->sreg1;
1654 ins->opcode = OP_MOVE;
1658 if (ins->dreg == ins->sreg1) {
1659 MONO_DELETE_INS (bb, ins);
1663 * OP_MOVE sreg, dreg
1664 * OP_MOVE dreg, sreg
1666 if (last_ins && last_ins->opcode == OP_MOVE &&
1667 ins->sreg1 == last_ins->dreg &&
1668 ins->dreg == last_ins->sreg1) {
1669 MONO_DELETE_INS (bb, ins);
1677 bb->last_ins = last_ins;
1681 * the branch_cc_table should maintain the order of these
1695 branch_cc_table [] = {
1709 #define NEW_INS(cfg,dest,op) do { \
1710 MONO_INST_NEW ((cfg), (dest), (op)); \
1711 mono_bblock_insert_before_ins (bb, ins, (dest)); \
1715 map_to_reg_reg_op (int op)
1724 case OP_COMPARE_IMM:
1726 case OP_ICOMPARE_IMM:
1740 case OP_LOAD_MEMBASE:
1741 return OP_LOAD_MEMINDEX;
1742 case OP_LOADI4_MEMBASE:
1743 return OP_LOADI4_MEMINDEX;
1744 case OP_LOADU4_MEMBASE:
1745 return OP_LOADU4_MEMINDEX;
1746 case OP_LOADU1_MEMBASE:
1747 return OP_LOADU1_MEMINDEX;
1748 case OP_LOADI2_MEMBASE:
1749 return OP_LOADI2_MEMINDEX;
1750 case OP_LOADU2_MEMBASE:
1751 return OP_LOADU2_MEMINDEX;
1752 case OP_LOADI1_MEMBASE:
1753 return OP_LOADI1_MEMINDEX;
1754 case OP_STOREI1_MEMBASE_REG:
1755 return OP_STOREI1_MEMINDEX;
1756 case OP_STOREI2_MEMBASE_REG:
1757 return OP_STOREI2_MEMINDEX;
1758 case OP_STOREI4_MEMBASE_REG:
1759 return OP_STOREI4_MEMINDEX;
1760 case OP_STORE_MEMBASE_REG:
1761 return OP_STORE_MEMINDEX;
1762 case OP_STORER4_MEMBASE_REG:
1763 return OP_STORER4_MEMINDEX;
1764 case OP_STORER8_MEMBASE_REG:
1765 return OP_STORER8_MEMINDEX;
1766 case OP_STORE_MEMBASE_IMM:
1767 return OP_STORE_MEMBASE_REG;
1768 case OP_STOREI1_MEMBASE_IMM:
1769 return OP_STOREI1_MEMBASE_REG;
1770 case OP_STOREI2_MEMBASE_IMM:
1771 return OP_STOREI2_MEMBASE_REG;
1772 case OP_STOREI4_MEMBASE_IMM:
1773 return OP_STOREI4_MEMBASE_REG;
1775 g_assert_not_reached ();
1779 * Remove from the instruction list the instructions that can't be
1780 * represented with very simple instructions with no register
1784 mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb)
1786 MonoInst *ins, *temp, *last_ins = NULL;
1787 int rot_amount, imm8, low_imm;
1789 MONO_BB_FOR_EACH_INS (bb, ins) {
1791 switch (ins->opcode) {
1795 case OP_COMPARE_IMM:
1796 case OP_ICOMPARE_IMM:
1810 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount)) < 0) {
1811 NEW_INS (cfg, temp, OP_ICONST);
1812 temp->inst_c0 = ins->inst_imm;
1813 temp->dreg = mono_alloc_ireg (cfg);
1814 ins->sreg2 = temp->dreg;
1815 ins->opcode = mono_op_imm_to_op (ins->opcode);
1817 if (ins->opcode == OP_SBB || ins->opcode == OP_ISBB || ins->opcode == OP_SUBCC)
1823 if (ins->inst_imm == 1) {
1824 ins->opcode = OP_MOVE;
1827 if (ins->inst_imm == 0) {
1828 ins->opcode = OP_ICONST;
1832 imm8 = mono_is_power_of_two (ins->inst_imm);
1834 ins->opcode = OP_SHL_IMM;
1835 ins->inst_imm = imm8;
1838 NEW_INS (cfg, temp, OP_ICONST);
1839 temp->inst_c0 = ins->inst_imm;
1840 temp->dreg = mono_alloc_ireg (cfg);
1841 ins->sreg2 = temp->dreg;
1842 ins->opcode = OP_IMUL;
1848 if (ins->next && (ins->next->opcode == OP_COND_EXC_C || ins->next->opcode == OP_COND_EXC_IC))
1849 /* ARM sets the C flag to 1 if there was _no_ overflow */
1850 ins->next->opcode = OP_COND_EXC_NC;
1852 case OP_LOCALLOC_IMM:
1853 NEW_INS (cfg, temp, OP_ICONST);
1854 temp->inst_c0 = ins->inst_imm;
1855 temp->dreg = mono_alloc_ireg (cfg);
1856 ins->sreg1 = temp->dreg;
1857 ins->opcode = OP_LOCALLOC;
1859 case OP_LOAD_MEMBASE:
1860 case OP_LOADI4_MEMBASE:
1861 case OP_LOADU4_MEMBASE:
1862 case OP_LOADU1_MEMBASE:
1863 /* we can do two things: load the immed in a register
1864 * and use an indexed load, or see if the immed can be
1865 * represented as an ad_imm + a load with a smaller offset
1866 * that fits. We just do the first for now, optimize later.
1868 if (arm_is_imm12 (ins->inst_offset))
1870 NEW_INS (cfg, temp, OP_ICONST);
1871 temp->inst_c0 = ins->inst_offset;
1872 temp->dreg = mono_alloc_ireg (cfg);
1873 ins->sreg2 = temp->dreg;
1874 ins->opcode = map_to_reg_reg_op (ins->opcode);
1876 case OP_LOADI2_MEMBASE:
1877 case OP_LOADU2_MEMBASE:
1878 case OP_LOADI1_MEMBASE:
1879 if (arm_is_imm8 (ins->inst_offset))
1881 NEW_INS (cfg, temp, OP_ICONST);
1882 temp->inst_c0 = ins->inst_offset;
1883 temp->dreg = mono_alloc_ireg (cfg);
1884 ins->sreg2 = temp->dreg;
1885 ins->opcode = map_to_reg_reg_op (ins->opcode);
1887 case OP_LOADR4_MEMBASE:
1888 case OP_LOADR8_MEMBASE:
1889 if (arm_is_fpimm8 (ins->inst_offset))
1891 low_imm = ins->inst_offset & 0x1ff;
1892 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~0x1ff, &rot_amount)) >= 0) {
1893 NEW_INS (cfg, temp, OP_ADD_IMM);
1894 temp->inst_imm = ins->inst_offset & ~0x1ff;
1895 temp->sreg1 = ins->inst_basereg;
1896 temp->dreg = mono_alloc_ireg (cfg);
1897 ins->inst_basereg = temp->dreg;
1898 ins->inst_offset = low_imm;
1901 /* VFP/FPA doesn't have indexed load instructions */
1902 g_assert_not_reached ();
1904 case OP_STORE_MEMBASE_REG:
1905 case OP_STOREI4_MEMBASE_REG:
1906 case OP_STOREI1_MEMBASE_REG:
1907 if (arm_is_imm12 (ins->inst_offset))
1909 NEW_INS (cfg, temp, OP_ICONST);
1910 temp->inst_c0 = ins->inst_offset;
1911 temp->dreg = mono_alloc_ireg (cfg);
1912 ins->sreg2 = temp->dreg;
1913 ins->opcode = map_to_reg_reg_op (ins->opcode);
1915 case OP_STOREI2_MEMBASE_REG:
1916 if (arm_is_imm8 (ins->inst_offset))
1918 NEW_INS (cfg, temp, OP_ICONST);
1919 temp->inst_c0 = ins->inst_offset;
1920 temp->dreg = mono_alloc_ireg (cfg);
1921 ins->sreg2 = temp->dreg;
1922 ins->opcode = map_to_reg_reg_op (ins->opcode);
1924 case OP_STORER4_MEMBASE_REG:
1925 case OP_STORER8_MEMBASE_REG:
1926 if (arm_is_fpimm8 (ins->inst_offset))
1928 low_imm = ins->inst_offset & 0x1ff;
1929 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~ 0x1ff, &rot_amount)) >= 0 && arm_is_fpimm8 (low_imm)) {
1930 NEW_INS (cfg, temp, OP_ADD_IMM);
1931 temp->inst_imm = ins->inst_offset & ~0x1ff;
1932 temp->sreg1 = ins->inst_destbasereg;
1933 temp->dreg = mono_alloc_ireg (cfg);
1934 ins->inst_destbasereg = temp->dreg;
1935 ins->inst_offset = low_imm;
1938 /*g_print ("fail with: %d (%d, %d)\n", ins->inst_offset, ins->inst_offset & ~0x1ff, low_imm);*/
1939 /* VFP/FPA doesn't have indexed store instructions */
1940 g_assert_not_reached ();
1942 case OP_STORE_MEMBASE_IMM:
1943 case OP_STOREI1_MEMBASE_IMM:
1944 case OP_STOREI2_MEMBASE_IMM:
1945 case OP_STOREI4_MEMBASE_IMM:
1946 NEW_INS (cfg, temp, OP_ICONST);
1947 temp->inst_c0 = ins->inst_imm;
1948 temp->dreg = mono_alloc_ireg (cfg);
1949 ins->sreg1 = temp->dreg;
1950 ins->opcode = map_to_reg_reg_op (ins->opcode);
1952 goto loop_start; /* make it handle the possibly big ins->inst_offset */
1954 gboolean swap = FALSE;
1957 /* Some fp compares require swapped operands */
1958 g_assert (ins->next);
1959 switch (ins->next->opcode) {
1961 ins->next->opcode = OP_FBLT;
1965 ins->next->opcode = OP_FBLT_UN;
1969 ins->next->opcode = OP_FBGE;
1973 ins->next->opcode = OP_FBGE_UN;
1981 ins->sreg1 = ins->sreg2;
1990 bb->last_ins = last_ins;
1991 bb->max_vreg = cfg->next_vreg;
1995 mono_arch_decompose_long_opts (MonoCompile *cfg, MonoInst *long_ins)
2000 if (long_ins->opcode == OP_LNEG) {
2002 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ARM_RSBS_IMM, ins->dreg + 1, ins->sreg1 + 1, 0);
2003 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ARM_RSC_IMM, ins->dreg + 2, ins->sreg1 + 2, 0);
2009 emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size, gboolean is_signed)
2011 /* sreg is a float, dreg is an integer reg */
2013 ARM_FIXZ (code, dreg, sreg);
2014 #elif defined(ARM_FPU_VFP)
2016 ARM_TOSIZD (code, ARM_VFP_F0, sreg);
2018 ARM_TOUIZD (code, ARM_VFP_F0, sreg);
2019 ARM_FMRS (code, dreg, ARM_VFP_F0);
2023 ARM_AND_REG_IMM8 (code, dreg, dreg, 0xff);
2024 else if (size == 2) {
2025 ARM_SHL_IMM (code, dreg, dreg, 16);
2026 ARM_SHR_IMM (code, dreg, dreg, 16);
2030 ARM_SHL_IMM (code, dreg, dreg, 24);
2031 ARM_SAR_IMM (code, dreg, dreg, 24);
2032 } else if (size == 2) {
2033 ARM_SHL_IMM (code, dreg, dreg, 16);
2034 ARM_SAR_IMM (code, dreg, dreg, 16);
2042 const guchar *target;
2047 #define is_call_imm(diff) ((gint)(diff) >= -33554432 && (gint)(diff) <= 33554431)
2050 search_thunk_slot (void *data, int csize, int bsize, void *user_data) {
2051 PatchData *pdata = (PatchData*)user_data;
2052 guchar *code = data;
2053 guint32 *thunks = data;
2054 guint32 *endthunks = (guint32*)(code + bsize);
2056 int difflow, diffhigh;
2058 /* always ensure a call from pdata->code can reach to the thunks without further thunks */
2059 difflow = (char*)pdata->code - (char*)thunks;
2060 diffhigh = (char*)pdata->code - (char*)endthunks;
2061 if (!((is_call_imm (thunks) && is_call_imm (endthunks)) || (is_call_imm (difflow) && is_call_imm (diffhigh))))
2065 * The thunk is composed of 3 words:
2066 * load constant from thunks [2] into ARM_IP
2069 * Note that the LR register is already setup
2071 //g_print ("thunk nentries: %d\n", ((char*)endthunks - (char*)thunks)/16);
2072 if ((pdata->found == 2) || (pdata->code >= code && pdata->code <= code + csize)) {
2073 while (thunks < endthunks) {
2074 //g_print ("looking for target: %p at %p (%08x-%08x)\n", pdata->target, thunks, thunks [0], thunks [1]);
2075 if (thunks [2] == (guint32)pdata->target) {
2076 arm_patch (pdata->code, (guchar*)thunks);
2077 mono_arch_flush_icache (pdata->code, 4);
2080 } else if ((thunks [0] == 0) && (thunks [1] == 0) && (thunks [2] == 0)) {
2081 /* found a free slot instead: emit thunk */
2082 /* ARMREG_IP is fine to use since this can't be an IMT call
2085 code = (guchar*)thunks;
2086 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
2087 if (thumb_supported)
2088 ARM_BX (code, ARMREG_IP);
2090 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
2091 thunks [2] = (guint32)pdata->target;
2092 mono_arch_flush_icache ((guchar*)thunks, 12);
2094 arm_patch (pdata->code, (guchar*)thunks);
2095 mono_arch_flush_icache (pdata->code, 4);
2099 /* skip 12 bytes, the size of the thunk */
2103 //g_print ("failed thunk lookup for %p from %p at %p (%d entries)\n", pdata->target, pdata->code, data, count);
2109 handle_thunk (int absolute, guchar *code, const guchar *target) {
2110 MonoDomain *domain = mono_domain_get ();
2114 pdata.target = target;
2115 pdata.absolute = absolute;
2118 mono_domain_lock (domain);
2119 mono_domain_code_foreach (domain, search_thunk_slot, &pdata);
2122 /* this uses the first available slot */
2124 mono_domain_code_foreach (domain, search_thunk_slot, &pdata);
2126 mono_domain_unlock (domain);
2128 if (pdata.found != 1)
2129 g_print ("thunk failed for %p from %p\n", target, code);
2130 g_assert (pdata.found == 1);
2134 arm_patch (guchar *code, const guchar *target)
2136 guint32 *code32 = (void*)code;
2137 guint32 ins = *code32;
2138 guint32 prim = (ins >> 25) & 7;
2139 guint32 tval = GPOINTER_TO_UINT (target);
2141 //g_print ("patching 0x%08x (0x%08x) to point to 0x%08x\n", code, ins, target);
2142 if (prim == 5) { /* 101b */
2143 /* the diff starts 8 bytes from the branch opcode */
2144 gint diff = target - code - 8;
2146 gint tmask = 0xffffffff;
2147 if (tval & 1) { /* entering thumb mode */
2148 diff = target - 1 - code - 8;
2149 g_assert (thumb_supported);
2150 tbits = 0xf << 28; /* bl->blx bit pattern */
2151 g_assert ((ins & (1 << 24))); /* it must be a bl, not b instruction */
2152 /* this low bit of the displacement is moved to bit 24 in the instruction encoding */
2156 tmask = ~(1 << 24); /* clear the link bit */
2157 /*g_print ("blx to thumb: target: %p, code: %p, diff: %d, mask: %x\n", target, code, diff, tmask);*/
2162 if (diff <= 33554431) {
2164 ins = (ins & 0xff000000) | diff;
2166 *code32 = ins | tbits;
2170 /* diff between 0 and -33554432 */
2171 if (diff >= -33554432) {
2173 ins = (ins & 0xff000000) | (diff & ~0xff000000);
2175 *code32 = ins | tbits;
2180 handle_thunk (TRUE, code, target);
2185 * The alternative call sequences looks like this:
2187 * ldr ip, [pc] // loads the address constant
2188 * b 1f // jumps around the constant
2189 * address constant embedded in the code
2194 * There are two cases for patching:
2195 * a) at the end of method emission: in this case code points to the start
2196 * of the call sequence
2197 * b) during runtime patching of the call site: in this case code points
2198 * to the mov pc, ip instruction
2200 * We have to handle also the thunk jump code sequence:
2204 * address constant // execution never reaches here
2206 if ((ins & 0x0ffffff0) == 0x12fff10) {
2207 /* Branch and exchange: the address is constructed in a reg
2208 * We can patch BX when the code sequence is the following:
2209 * ldr ip, [pc, #0] ; 0x8
2216 guint8 *emit = (guint8*)ccode;
2217 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2219 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
2220 ARM_BX (emit, ARMREG_IP);
2222 /*patching from magic trampoline*/
2223 if (ins == ccode [3]) {
2224 g_assert (code32 [-4] == ccode [0]);
2225 g_assert (code32 [-3] == ccode [1]);
2226 g_assert (code32 [-1] == ccode [2]);
2227 code32 [-2] = (guint32)target;
2230 /*patching from JIT*/
2231 if (ins == ccode [0]) {
2232 g_assert (code32 [1] == ccode [1]);
2233 g_assert (code32 [3] == ccode [2]);
2234 g_assert (code32 [4] == ccode [3]);
2235 code32 [2] = (guint32)target;
2238 g_assert_not_reached ();
2239 } else if ((ins & 0x0ffffff0) == 0x12fff30) {
2247 guint8 *emit = (guint8*)ccode;
2248 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2250 ARM_BLX_REG (emit, ARMREG_IP);
2252 g_assert (code32 [-3] == ccode [0]);
2253 g_assert (code32 [-2] == ccode [1]);
2254 g_assert (code32 [0] == ccode [2]);
2256 code32 [-1] = (guint32)target;
2259 guint32 *tmp = ccode;
2260 guint8 *emit = (guint8*)tmp;
2261 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2262 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
2263 ARM_MOV_REG_REG (emit, ARMREG_PC, ARMREG_IP);
2264 ARM_BX (emit, ARMREG_IP);
2265 if (ins == ccode [2]) {
2266 g_assert_not_reached (); // should be -2 ...
2267 code32 [-1] = (guint32)target;
2270 if (ins == ccode [0]) {
2271 /* handles both thunk jump code and the far call sequence */
2272 code32 [2] = (guint32)target;
2275 g_assert_not_reached ();
2277 // g_print ("patched with 0x%08x\n", ins);
2281 * Return the >= 0 uimm8 value if val can be represented with a byte + rotation
2282 * (with the rotation amount in *rot_amount. rot_amount is already adjusted
2283 * to be used with the emit macros.
2284 * Return -1 otherwise.
2287 mono_arm_is_rotated_imm8 (guint32 val, gint *rot_amount)
2290 for (i = 0; i < 31; i+= 2) {
2291 res = (val << (32 - i)) | (val >> i);
2294 *rot_amount = i? 32 - i: 0;
2301 * Emits in code a sequence of instructions that load the value 'val'
2302 * into the dreg register. Uses at most 4 instructions.
2305 mono_arm_emit_load_imm (guint8 *code, int dreg, guint32 val)
2307 int imm8, rot_amount;
2309 ARM_LDR_IMM (code, dreg, ARMREG_PC, 0);
2310 /* skip the constant pool */
2316 if ((imm8 = mono_arm_is_rotated_imm8 (val, &rot_amount)) >= 0) {
2317 ARM_MOV_REG_IMM (code, dreg, imm8, rot_amount);
2318 } else if ((imm8 = mono_arm_is_rotated_imm8 (~val, &rot_amount)) >= 0) {
2319 ARM_MVN_REG_IMM (code, dreg, imm8, rot_amount);
2322 ARM_MOV_REG_IMM8 (code, dreg, (val & 0xFF));
2324 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF00) >> 8, 24);
2326 if (val & 0xFF0000) {
2327 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
2329 if (val & 0xFF000000) {
2330 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2332 } else if (val & 0xFF00) {
2333 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF00) >> 8, 24);
2334 if (val & 0xFF0000) {
2335 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
2337 if (val & 0xFF000000) {
2338 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2340 } else if (val & 0xFF0000) {
2341 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF0000) >> 16, 16);
2342 if (val & 0xFF000000) {
2343 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2346 //g_assert_not_reached ();
2352 * emit_load_volatile_arguments:
2354 * Load volatile arguments from the stack to the original input registers.
2355 * Required before a tail call.
2358 emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code)
2360 MonoMethod *method = cfg->method;
2361 MonoMethodSignature *sig;
2366 /* FIXME: Generate intermediate code instead */
2368 sig = mono_method_signature (method);
2370 /* This is the opposite of the code in emit_prolog */
2374 cinfo = get_call_info (sig, sig->pinvoke);
2376 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
2377 ArgInfo *ainfo = &cinfo->ret;
2378 inst = cfg->vret_addr;
2379 g_assert (arm_is_imm12 (inst->inst_offset));
2380 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2382 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
2383 ArgInfo *ainfo = cinfo->args + i;
2384 inst = cfg->args [pos];
2386 if (cfg->verbose_level > 2)
2387 g_print ("Loading argument %d (type: %d)\n", i, ainfo->regtype);
2388 if (inst->opcode == OP_REGVAR) {
2389 if (ainfo->regtype == RegTypeGeneral)
2390 ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
2391 else if (ainfo->regtype == RegTypeFP) {
2392 g_assert_not_reached ();
2393 } else if (ainfo->regtype == RegTypeBase) {
2397 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
2398 ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset));
2400 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
2401 ARM_LDR_REG_REG (code, inst->dreg, ARMREG_SP, ARMREG_IP);
2405 g_assert_not_reached ();
2407 if (ainfo->regtype == RegTypeGeneral) {
2408 switch (ainfo->size) {
2415 g_assert (arm_is_imm12 (inst->inst_offset));
2416 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2417 g_assert (arm_is_imm12 (inst->inst_offset + 4));
2418 ARM_LDR_IMM (code, ainfo->reg + 1, inst->inst_basereg, inst->inst_offset + 4);
2421 if (arm_is_imm12 (inst->inst_offset)) {
2422 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2424 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
2425 ARM_LDR_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
2429 } else if (ainfo->regtype == RegTypeBaseGen) {
2432 } else if (ainfo->regtype == RegTypeBase) {
2434 } else if (ainfo->regtype == RegTypeFP) {
2435 g_assert_not_reached ();
2436 } else if (ainfo->regtype == RegTypeStructByVal) {
2437 int doffset = inst->inst_offset;
2441 if (mono_class_from_mono_type (inst->inst_vtype))
2442 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), NULL);
2443 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
2444 if (arm_is_imm12 (doffset)) {
2445 ARM_LDR_IMM (code, ainfo->reg + cur_reg, inst->inst_basereg, doffset);
2447 code = mono_arm_emit_load_imm (code, ARMREG_IP, doffset);
2448 ARM_LDR_REG_REG (code, ainfo->reg + cur_reg, inst->inst_basereg, ARMREG_IP);
2450 soffset += sizeof (gpointer);
2451 doffset += sizeof (gpointer);
2456 } else if (ainfo->regtype == RegTypeStructByAddr) {
2473 mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
2478 guint8 *code = cfg->native_code + cfg->code_len;
2479 MonoInst *last_ins = NULL;
2480 guint last_offset = 0;
2482 int imm8, rot_amount;
2484 /* we don't align basic blocks of loops on arm */
2486 if (cfg->verbose_level > 2)
2487 g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset);
2489 cpos = bb->max_offset;
2491 if (cfg->prof_options & MONO_PROFILE_COVERAGE) {
2492 //MonoCoverageInfo *cov = mono_get_coverage_info (cfg->method);
2493 //g_assert (!mono_compile_aot);
2496 // cov->data [bb->dfn].iloffset = bb->cil_code - cfg->cil_code;
2497 /* this is not thread save, but good enough */
2498 /* fixme: howto handle overflows? */
2499 //x86_inc_mem (code, &cov->data [bb->dfn].count);
2502 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) {
2503 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2504 (gpointer)"mono_break");
2505 code = emit_call_seq (cfg, code);
2508 MONO_BB_FOR_EACH_INS (bb, ins) {
2509 offset = code - cfg->native_code;
2511 max_len = ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
2513 if (offset > (cfg->code_size - max_len - 16)) {
2514 cfg->code_size *= 2;
2515 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
2516 code = cfg->native_code + offset;
2518 // if (ins->cil_code)
2519 // g_print ("cil code\n");
2520 mono_debug_record_line_number (cfg, ins, offset);
2522 switch (ins->opcode) {
2523 case OP_MEMORY_BARRIER:
2526 #ifdef HAVE_AEABI_READ_TP
2527 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2528 (gpointer)"__aeabi_read_tp");
2529 code = emit_call_seq (cfg, code);
2531 ARM_LDR_IMM (code, ins->dreg, ARMREG_R0, ins->inst_offset);
2533 g_assert_not_reached ();
2537 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2538 ppc_mulhw (code, ppc_r3, ins->sreg1, ins->sreg2);
2541 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2542 ppc_mulhwu (code, ppc_r3, ins->sreg1, ins->sreg2);
2544 case OP_STOREI1_MEMBASE_IMM:
2545 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFF);
2546 g_assert (arm_is_imm12 (ins->inst_offset));
2547 ARM_STRB_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2549 case OP_STOREI2_MEMBASE_IMM:
2550 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFFFF);
2551 g_assert (arm_is_imm8 (ins->inst_offset));
2552 ARM_STRH_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2554 case OP_STORE_MEMBASE_IMM:
2555 case OP_STOREI4_MEMBASE_IMM:
2556 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm);
2557 g_assert (arm_is_imm12 (ins->inst_offset));
2558 ARM_STR_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2560 case OP_STOREI1_MEMBASE_REG:
2561 g_assert (arm_is_imm12 (ins->inst_offset));
2562 ARM_STRB_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2564 case OP_STOREI2_MEMBASE_REG:
2565 g_assert (arm_is_imm8 (ins->inst_offset));
2566 ARM_STRH_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2568 case OP_STORE_MEMBASE_REG:
2569 case OP_STOREI4_MEMBASE_REG:
2570 /* this case is special, since it happens for spill code after lowering has been called */
2571 if (arm_is_imm12 (ins->inst_offset)) {
2572 ARM_STR_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2574 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2575 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ARMREG_LR);
2578 case OP_STOREI1_MEMINDEX:
2579 ARM_STRB_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2581 case OP_STOREI2_MEMINDEX:
2582 ARM_STRH_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2584 case OP_STORE_MEMINDEX:
2585 case OP_STOREI4_MEMINDEX:
2586 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2589 g_assert_not_reached ();
2591 case OP_LOAD_MEMINDEX:
2592 case OP_LOADI4_MEMINDEX:
2593 case OP_LOADU4_MEMINDEX:
2594 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2596 case OP_LOADI1_MEMINDEX:
2597 ARM_LDRSB_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2599 case OP_LOADU1_MEMINDEX:
2600 ARM_LDRB_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2602 case OP_LOADI2_MEMINDEX:
2603 ARM_LDRSH_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2605 case OP_LOADU2_MEMINDEX:
2606 ARM_LDRH_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2608 case OP_LOAD_MEMBASE:
2609 case OP_LOADI4_MEMBASE:
2610 case OP_LOADU4_MEMBASE:
2611 /* this case is special, since it happens for spill code after lowering has been called */
2612 if (arm_is_imm12 (ins->inst_offset)) {
2613 ARM_LDR_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2615 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2616 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ARMREG_LR);
2619 case OP_LOADI1_MEMBASE:
2620 g_assert (arm_is_imm8 (ins->inst_offset));
2621 ARM_LDRSB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2623 case OP_LOADU1_MEMBASE:
2624 g_assert (arm_is_imm12 (ins->inst_offset));
2625 ARM_LDRB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2627 case OP_LOADU2_MEMBASE:
2628 g_assert (arm_is_imm8 (ins->inst_offset));
2629 ARM_LDRH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2631 case OP_LOADI2_MEMBASE:
2632 g_assert (arm_is_imm8 (ins->inst_offset));
2633 ARM_LDRSH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2635 case OP_ICONV_TO_I1:
2636 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 24);
2637 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 24);
2639 case OP_ICONV_TO_I2:
2640 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2641 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 16);
2643 case OP_ICONV_TO_U1:
2644 ARM_AND_REG_IMM8 (code, ins->dreg, ins->sreg1, 0xff);
2646 case OP_ICONV_TO_U2:
2647 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2648 ARM_SHR_IMM (code, ins->dreg, ins->dreg, 16);
2652 ARM_CMP_REG_REG (code, ins->sreg1, ins->sreg2);
2654 case OP_COMPARE_IMM:
2655 case OP_ICOMPARE_IMM:
2656 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2657 g_assert (imm8 >= 0);
2658 ARM_CMP_REG_IMM (code, ins->sreg1, imm8, rot_amount);
2662 * gdb does not like encountering the hw breakpoint ins in the debugged code.
2663 * So instead of emitting a trap, we emit a call a C function and place a
2666 //*(int*)code = 0xef9f0001;
2669 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2670 (gpointer)"mono_break");
2671 code = emit_call_seq (cfg, code);
2673 case OP_RELAXED_NOP:
2678 case OP_DUMMY_STORE:
2679 case OP_NOT_REACHED:
2684 ARM_ADDS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2687 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2691 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2694 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2695 g_assert (imm8 >= 0);
2696 ARM_ADDS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2700 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2701 g_assert (imm8 >= 0);
2702 ARM_ADD_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2706 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2707 g_assert (imm8 >= 0);
2708 ARM_ADCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2711 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2712 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2714 case OP_IADD_OVF_UN:
2715 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2716 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2719 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2720 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2722 case OP_ISUB_OVF_UN:
2723 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2724 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2726 case OP_ADD_OVF_CARRY:
2727 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2728 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2730 case OP_ADD_OVF_UN_CARRY:
2731 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2732 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2734 case OP_SUB_OVF_CARRY:
2735 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2736 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2738 case OP_SUB_OVF_UN_CARRY:
2739 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2740 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2744 ARM_SUBS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2747 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2748 g_assert (imm8 >= 0);
2749 ARM_SUBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2752 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2756 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2760 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2761 g_assert (imm8 >= 0);
2762 ARM_SUB_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2766 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2767 g_assert (imm8 >= 0);
2768 ARM_SBCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2770 case OP_ARM_RSBS_IMM:
2771 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2772 g_assert (imm8 >= 0);
2773 ARM_RSBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2775 case OP_ARM_RSC_IMM:
2776 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2777 g_assert (imm8 >= 0);
2778 ARM_RSC_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2781 ARM_AND_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2785 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2786 g_assert (imm8 >= 0);
2787 ARM_AND_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2795 /* crappy ARM arch doesn't have a DIV instruction */
2796 g_assert_not_reached ();
2798 ARM_ORR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2802 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2803 g_assert (imm8 >= 0);
2804 ARM_ORR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2807 ARM_EOR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2811 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2812 g_assert (imm8 >= 0);
2813 ARM_EOR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2816 ARM_SHL_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2821 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2822 else if (ins->dreg != ins->sreg1)
2823 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2826 ARM_SAR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2831 ARM_SAR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2832 else if (ins->dreg != ins->sreg1)
2833 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2836 case OP_ISHR_UN_IMM:
2838 ARM_SHR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2839 else if (ins->dreg != ins->sreg1)
2840 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2843 ARM_SHR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2846 ARM_MVN_REG_REG (code, ins->dreg, ins->sreg1);
2849 ARM_RSB_REG_IMM8 (code, ins->dreg, ins->sreg1, 0);
2852 if (ins->dreg == ins->sreg2)
2853 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2855 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg2, ins->sreg1);
2858 g_assert_not_reached ();
2861 /* FIXME: handle ovf/ sreg2 != dreg */
2862 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2863 /* FIXME: MUL doesn't set the C/O flags on ARM */
2865 case OP_IMUL_OVF_UN:
2866 /* FIXME: handle ovf/ sreg2 != dreg */
2867 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2868 /* FIXME: MUL doesn't set the C/O flags on ARM */
2871 code = mono_arm_emit_load_imm (code, ins->dreg, ins->inst_c0);
2874 /* Load the GOT offset */
2875 mono_add_patch_info (cfg, offset, (MonoJumpInfoType)ins->inst_i1, ins->inst_p0);
2876 ARM_LDR_IMM (code, ins->dreg, ARMREG_PC, 0);
2878 *(gpointer*)code = NULL;
2880 /* Load the value from the GOT */
2881 ARM_LDR_REG_REG (code, ins->dreg, ARMREG_PC, ins->dreg);
2883 case OP_ICONV_TO_I4:
2884 case OP_ICONV_TO_U4:
2886 if (ins->dreg != ins->sreg1)
2887 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2890 int saved = ins->sreg2;
2891 if (ins->sreg2 == ARM_LSW_REG) {
2892 ARM_MOV_REG_REG (code, ARMREG_LR, ins->sreg2);
2895 if (ins->sreg1 != ARM_LSW_REG)
2896 ARM_MOV_REG_REG (code, ARM_LSW_REG, ins->sreg1);
2897 if (saved != ARM_MSW_REG)
2898 ARM_MOV_REG_REG (code, ARM_MSW_REG, saved);
2903 ARM_MVFD (code, ins->dreg, ins->sreg1);
2904 #elif defined(ARM_FPU_VFP)
2905 ARM_CPYD (code, ins->dreg, ins->sreg1);
2908 case OP_FCONV_TO_R4:
2910 ARM_MVFS (code, ins->dreg, ins->sreg1);
2911 #elif defined(ARM_FPU_VFP)
2912 ARM_CVTD (code, ins->dreg, ins->sreg1);
2913 ARM_CVTS (code, ins->dreg, ins->dreg);
2918 * Keep in sync with mono_arch_emit_epilog
2920 g_assert (!cfg->method->save_lmf);
2922 code = emit_load_volatile_arguments (cfg, code);
2924 code = emit_big_add (code, ARMREG_SP, cfg->frame_reg, cfg->stack_usage);
2925 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP)) | ((1 << ARMREG_LR)));
2926 mono_add_patch_info (cfg, (guint8*) code - cfg->native_code, MONO_PATCH_INFO_METHOD_JUMP, ins->inst_p0);
2927 if (cfg->compile_aot) {
2928 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
2930 *(gpointer*)code = NULL;
2932 ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_IP);
2938 /* ensure ins->sreg1 is not NULL */
2939 ARM_LDR_IMM (code, ARMREG_LR, ins->sreg1, 0);
2943 if (ppc_is_imm16 (cfg->sig_cookie + cfg->stack_usage)) {
2944 ppc_addi (code, ppc_r11, cfg->frame_reg, cfg->sig_cookie + cfg->stack_usage);
2946 ppc_load (code, ppc_r11, cfg->sig_cookie + cfg->stack_usage);
2947 ppc_add (code, ppc_r11, cfg->frame_reg, ppc_r11);
2949 ppc_stw (code, ppc_r11, 0, ins->sreg1);
2959 call = (MonoCallInst*)ins;
2960 if (ins->flags & MONO_INST_HAS_METHOD)
2961 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, call->method);
2963 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, call->fptr);
2964 code = emit_call_seq (cfg, code);
2965 code = emit_move_return_value (cfg, ins, code);
2971 case OP_VOIDCALL_REG:
2973 code = emit_call_reg (code, ins->sreg1);
2974 code = emit_move_return_value (cfg, ins, code);
2976 case OP_FCALL_MEMBASE:
2977 case OP_LCALL_MEMBASE:
2978 case OP_VCALL_MEMBASE:
2979 case OP_VCALL2_MEMBASE:
2980 case OP_VOIDCALL_MEMBASE:
2981 case OP_CALL_MEMBASE:
2982 g_assert (arm_is_imm12 (ins->inst_offset));
2983 g_assert (ins->sreg1 != ARMREG_LR);
2984 call = (MonoCallInst*)ins;
2985 if (call->method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
2986 ARM_ADD_REG_IMM8 (code, ARMREG_LR, ARMREG_PC, 4);
2987 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
2989 * We can't embed the method in the code stream in PIC code, or
2991 * Instead, we put it in V5 in code emitted by
2992 * mono_arch_emit_imt_argument (), and embed NULL here to
2993 * signal the IMT thunk that the value is in V5.
2995 if (call->dynamic_imt_arg)
2996 *((gpointer*)code) = NULL;
2998 *((gpointer*)code) = (gpointer)call->method;
3001 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
3002 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
3004 code = emit_move_return_value (cfg, ins, code);
3007 /* keep alignment */
3008 int alloca_waste = cfg->param_area;
3011 /* round the size to 8 bytes */
3012 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->sreg1, 7);
3013 ARM_BIC_REG_IMM8 (code, ins->dreg, ins->dreg, 7);
3015 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->dreg, alloca_waste);
3016 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ins->dreg);
3017 /* memzero the area: dreg holds the size, sp is the pointer */
3018 if (ins->flags & MONO_INST_INIT) {
3019 guint8 *start_loop, *branch_to_cond;
3020 ARM_MOV_REG_IMM8 (code, ARMREG_LR, 0);
3021 branch_to_cond = code;
3024 ARM_STR_REG_REG (code, ARMREG_LR, ARMREG_SP, ins->dreg);
3025 arm_patch (branch_to_cond, code);
3026 /* decrement by 4 and set flags */
3027 ARM_SUBS_REG_IMM8 (code, ins->dreg, ins->dreg, 4);
3028 ARM_B_COND (code, ARMCOND_GE, 0);
3029 arm_patch (code - 4, start_loop);
3031 ARM_ADD_REG_IMM8 (code, ins->dreg, ARMREG_SP, alloca_waste);
3035 if (ins->sreg1 != ARMREG_R0)
3036 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
3037 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3038 (gpointer)"mono_arch_throw_exception");
3039 code = emit_call_seq (cfg, code);
3043 if (ins->sreg1 != ARMREG_R0)
3044 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
3045 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3046 (gpointer)"mono_arch_rethrow_exception");
3047 code = emit_call_seq (cfg, code);
3050 case OP_START_HANDLER: {
3051 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3053 if (arm_is_imm12 (spvar->inst_offset)) {
3054 ARM_STR_IMM (code, ARMREG_LR, spvar->inst_basereg, spvar->inst_offset);
3056 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3057 ARM_STR_REG_REG (code, ARMREG_LR, spvar->inst_basereg, ARMREG_IP);
3061 case OP_ENDFILTER: {
3062 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3064 if (ins->sreg1 != ARMREG_R0)
3065 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
3066 if (arm_is_imm12 (spvar->inst_offset)) {
3067 ARM_LDR_IMM (code, ARMREG_IP, spvar->inst_basereg, spvar->inst_offset);
3069 g_assert (ARMREG_IP != spvar->inst_basereg);
3070 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3071 ARM_LDR_REG_REG (code, ARMREG_IP, spvar->inst_basereg, ARMREG_IP);
3073 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
3076 case OP_ENDFINALLY: {
3077 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3079 if (arm_is_imm12 (spvar->inst_offset)) {
3080 ARM_LDR_IMM (code, ARMREG_IP, spvar->inst_basereg, spvar->inst_offset);
3082 g_assert (ARMREG_IP != spvar->inst_basereg);
3083 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3084 ARM_LDR_REG_REG (code, ARMREG_IP, spvar->inst_basereg, ARMREG_IP);
3086 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
3089 case OP_CALL_HANDLER:
3090 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb);
3094 ins->inst_c0 = code - cfg->native_code;
3097 /*if (ins->inst_target_bb->native_offset) {
3099 //x86_jump_code (code, cfg->native_code + ins->inst_target_bb->native_offset);
3101 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb);
3106 ARM_MOV_REG_REG (code, ARMREG_PC, ins->sreg1);
3110 * In the normal case we have:
3111 * ldr pc, [pc, ins->sreg1 << 2]
3114 * ldr lr, [pc, ins->sreg1 << 2]
3116 * After follows the data.
3117 * FIXME: add aot support.
3119 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_SWITCH, ins->inst_p0);
3120 max_len += 4 * GPOINTER_TO_INT (ins->klass);
3121 if (offset > (cfg->code_size - max_len - 16)) {
3122 cfg->code_size += max_len;
3123 cfg->code_size *= 2;
3124 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
3125 code = cfg->native_code + offset;
3127 ARM_LDR_REG_REG_SHIFT (code, ARMREG_PC, ARMREG_PC, ins->sreg1, ARMSHIFT_LSL, 2);
3129 code += 4 * GPOINTER_TO_INT (ins->klass);
3133 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
3134 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
3138 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3139 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LT);
3143 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3144 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LO);
3148 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3149 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_GT);
3153 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3154 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_HI);
3156 case OP_COND_EXC_EQ:
3157 case OP_COND_EXC_NE_UN:
3158 case OP_COND_EXC_LT:
3159 case OP_COND_EXC_LT_UN:
3160 case OP_COND_EXC_GT:
3161 case OP_COND_EXC_GT_UN:
3162 case OP_COND_EXC_GE:
3163 case OP_COND_EXC_GE_UN:
3164 case OP_COND_EXC_LE:
3165 case OP_COND_EXC_LE_UN:
3166 EMIT_COND_SYSTEM_EXCEPTION (ins->opcode - OP_COND_EXC_EQ, ins->inst_p1);
3168 case OP_COND_EXC_IEQ:
3169 case OP_COND_EXC_INE_UN:
3170 case OP_COND_EXC_ILT:
3171 case OP_COND_EXC_ILT_UN:
3172 case OP_COND_EXC_IGT:
3173 case OP_COND_EXC_IGT_UN:
3174 case OP_COND_EXC_IGE:
3175 case OP_COND_EXC_IGE_UN:
3176 case OP_COND_EXC_ILE:
3177 case OP_COND_EXC_ILE_UN:
3178 EMIT_COND_SYSTEM_EXCEPTION (ins->opcode - OP_COND_EXC_IEQ, ins->inst_p1);
3181 case OP_COND_EXC_IC:
3182 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_CS, ins->inst_p1);
3184 case OP_COND_EXC_OV:
3185 case OP_COND_EXC_IOV:
3186 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VS, ins->inst_p1);
3188 case OP_COND_EXC_NC:
3189 case OP_COND_EXC_INC:
3190 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_CC, ins->inst_p1);
3192 case OP_COND_EXC_NO:
3193 case OP_COND_EXC_INO:
3194 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VC, ins->inst_p1);
3206 EMIT_COND_BRANCH (ins, ins->opcode - OP_IBEQ);
3209 /* floating point opcodes */
3212 if (cfg->compile_aot) {
3213 ARM_LDFD (code, ins->dreg, ARMREG_PC, 0);
3215 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3217 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
3220 /* FIXME: we can optimize the imm load by dealing with part of
3221 * the displacement in LDFD (aligning to 512).
3223 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3224 ARM_LDFD (code, ins->dreg, ARMREG_LR, 0);
3228 if (cfg->compile_aot) {
3229 ARM_LDFS (code, ins->dreg, ARMREG_PC, 0);
3231 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3234 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3235 ARM_LDFS (code, ins->dreg, ARMREG_LR, 0);
3238 case OP_STORER8_MEMBASE_REG:
3239 /* This is generated by the local regalloc pass which runs after the lowering pass */
3240 if (!arm_is_fpimm8 (ins->inst_offset)) {
3241 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3242 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_destbasereg);
3243 ARM_STFD (code, ins->sreg1, ARMREG_LR, 0);
3245 ARM_STFD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3248 case OP_LOADR8_MEMBASE:
3249 /* This is generated by the local regalloc pass which runs after the lowering pass */
3250 if (!arm_is_fpimm8 (ins->inst_offset)) {
3251 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3252 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_basereg);
3253 ARM_LDFD (code, ins->dreg, ARMREG_LR, 0);
3255 ARM_LDFD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3258 case OP_STORER4_MEMBASE_REG:
3259 g_assert (arm_is_fpimm8 (ins->inst_offset));
3260 ARM_STFS (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3262 case OP_LOADR4_MEMBASE:
3263 g_assert (arm_is_fpimm8 (ins->inst_offset));
3264 ARM_LDFS (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3266 case OP_ICONV_TO_R_UN: {
3268 tmpreg = ins->dreg == 0? 1: 0;
3269 ARM_CMP_REG_IMM8 (code, ins->sreg1, 0);
3270 ARM_FLTD (code, ins->dreg, ins->sreg1);
3271 ARM_B_COND (code, ARMCOND_GE, 8);
3272 /* save the temp register */
3273 ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
3274 ARM_STFD (code, tmpreg, ARMREG_SP, 0);
3275 ARM_LDFD (code, tmpreg, ARMREG_PC, 12);
3276 ARM_FPA_ADFD (code, ins->dreg, ins->dreg, tmpreg);
3277 ARM_LDFD (code, tmpreg, ARMREG_SP, 0);
3278 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
3279 /* skip the constant pool */
3282 *(int*)code = 0x41f00000;
3287 * ldfltd ftemp, [pc, #8] 0x41f00000 0x00000000
3288 * adfltd fdest, fdest, ftemp
3292 case OP_ICONV_TO_R4:
3293 ARM_FLTS (code, ins->dreg, ins->sreg1);
3295 case OP_ICONV_TO_R8:
3296 ARM_FLTD (code, ins->dreg, ins->sreg1);
3299 #elif defined(ARM_FPU_VFP)
3302 if (cfg->compile_aot) {
3303 ARM_FLDD (code, ins->dreg, ARMREG_PC, 0);
3305 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3307 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
3310 /* FIXME: we can optimize the imm load by dealing with part of
3311 * the displacement in LDFD (aligning to 512).
3313 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3314 ARM_FLDD (code, ins->dreg, ARMREG_LR, 0);
3318 if (cfg->compile_aot) {
3319 ARM_FLDS (code, ins->dreg, ARMREG_PC, 0);
3321 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3323 ARM_CVTS (code, ins->dreg, ins->dreg);
3325 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3326 ARM_FLDS (code, ins->dreg, ARMREG_LR, 0);
3327 ARM_CVTS (code, ins->dreg, ins->dreg);
3330 case OP_STORER8_MEMBASE_REG:
3331 /* This is generated by the local regalloc pass which runs after the lowering pass */
3332 if (!arm_is_fpimm8 (ins->inst_offset)) {
3333 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3334 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_destbasereg);
3335 ARM_FSTD (code, ins->sreg1, ARMREG_LR, 0);
3337 ARM_FSTD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3340 case OP_LOADR8_MEMBASE:
3341 /* This is generated by the local regalloc pass which runs after the lowering pass */
3342 if (!arm_is_fpimm8 (ins->inst_offset)) {
3343 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3344 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_basereg);
3345 ARM_FLDD (code, ins->dreg, ARMREG_LR, 0);
3347 ARM_FLDD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3350 case OP_STORER4_MEMBASE_REG:
3351 g_assert (arm_is_fpimm8 (ins->inst_offset));
3352 ARM_CVTD (code, ARM_VFP_F0, ins->sreg1);
3353 ARM_FSTS (code, ARM_VFP_F0, ins->inst_destbasereg, ins->inst_offset);
3355 case OP_LOADR4_MEMBASE:
3356 g_assert (arm_is_fpimm8 (ins->inst_offset));
3357 ARM_FLDS (code, ARM_VFP_F0, ins->inst_basereg, ins->inst_offset);
3358 ARM_CVTS (code, ins->dreg, ARM_VFP_F0);
3360 case OP_ICONV_TO_R_UN: {
3361 g_assert_not_reached ();
3364 case OP_ICONV_TO_R4:
3365 ARM_FMSR (code, ARM_VFP_F0, ins->sreg1);
3366 ARM_FSITOS (code, ARM_VFP_F0, ARM_VFP_F0);
3367 ARM_CVTS (code, ins->dreg, ARM_VFP_F0);
3369 case OP_ICONV_TO_R8:
3370 ARM_FMSR (code, ARM_VFP_F0, ins->sreg1);
3371 ARM_FSITOD (code, ins->dreg, ARM_VFP_F0);
3375 if (mono_method_signature (cfg->method)->ret->type == MONO_TYPE_R4) {
3376 ARM_CVTD (code, ARM_VFP_F0, ins->sreg1);
3377 ARM_FMRS (code, ARMREG_R0, ARM_VFP_F0);
3379 ARM_FMRRD (code, ARMREG_R0, ARMREG_R1, ins->sreg1);
3385 case OP_FCONV_TO_I1:
3386 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, TRUE);
3388 case OP_FCONV_TO_U1:
3389 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, FALSE);
3391 case OP_FCONV_TO_I2:
3392 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, TRUE);
3394 case OP_FCONV_TO_U2:
3395 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, FALSE);
3397 case OP_FCONV_TO_I4:
3399 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, TRUE);
3401 case OP_FCONV_TO_U4:
3403 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, FALSE);
3405 case OP_FCONV_TO_I8:
3406 case OP_FCONV_TO_U8:
3407 g_assert_not_reached ();
3408 /* Implemented as helper calls */
3410 case OP_LCONV_TO_R_UN:
3411 g_assert_not_reached ();
3412 /* Implemented as helper calls */
3414 case OP_LCONV_TO_OVF_I:
3415 case OP_LCONV_TO_OVF_I4_2: {
3416 guint32 *high_bit_not_set, *valid_negative, *invalid_negative, *valid_positive;
3418 * Valid ints: 0xffffffff:8000000 to 00000000:0x7f000000
3421 ARM_CMP_REG_IMM8 (code, ins->sreg1, 0);
3422 high_bit_not_set = code;
3423 ARM_B_COND (code, ARMCOND_GE, 0); /*branch if bit 31 of the lower part is not set*/
3425 ARM_CMN_REG_IMM8 (code, ins->sreg2, 1); /*This have the same effect as CMP reg, 0xFFFFFFFF */
3426 valid_negative = code;
3427 ARM_B_COND (code, ARMCOND_EQ, 0); /*branch if upper part == 0xFFFFFFFF (lower part has bit 31 set) */
3428 invalid_negative = code;
3429 ARM_B_COND (code, ARMCOND_AL, 0);
3431 arm_patch (high_bit_not_set, code);
3433 ARM_CMP_REG_IMM8 (code, ins->sreg2, 0);
3434 valid_positive = code;
3435 ARM_B_COND (code, ARMCOND_EQ, 0); /*branch if upper part == 0 (lower part has bit 31 clear)*/
3437 arm_patch (invalid_negative, code);
3438 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_AL, "OverflowException");
3440 arm_patch (valid_negative, code);
3441 arm_patch (valid_positive, code);
3443 if (ins->dreg != ins->sreg1)
3444 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
3449 ARM_FPA_ADFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3452 ARM_FPA_SUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3455 ARM_FPA_MUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3458 ARM_FPA_DVFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3461 ARM_MNFD (code, ins->dreg, ins->sreg1);
3463 #elif defined(ARM_FPU_VFP)
3465 ARM_VFP_ADDD (code, ins->dreg, ins->sreg1, ins->sreg2);
3468 ARM_VFP_SUBD (code, ins->dreg, ins->sreg1, ins->sreg2);
3471 ARM_VFP_MULD (code, ins->dreg, ins->sreg1, ins->sreg2);
3474 ARM_VFP_DIVD (code, ins->dreg, ins->sreg1, ins->sreg2);
3477 ARM_NEGD (code, ins->dreg, ins->sreg1);
3482 g_assert_not_reached ();
3486 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3487 #elif defined(ARM_FPU_VFP)
3488 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3494 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3495 #elif defined(ARM_FPU_VFP)
3496 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3499 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
3500 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
3504 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3505 #elif defined(ARM_FPU_VFP)
3506 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3509 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3510 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3514 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3515 #elif defined(ARM_FPU_VFP)
3516 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3519 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3520 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3521 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
3526 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3527 #elif defined(ARM_FPU_VFP)
3528 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3531 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3532 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3537 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3538 #elif defined(ARM_FPU_VFP)
3539 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3542 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3543 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3544 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
3546 /* ARM FPA flags table:
3547 * N Less than ARMCOND_MI
3548 * Z Equal ARMCOND_EQ
3549 * C Greater Than or Equal ARMCOND_CS
3550 * V Unordered ARMCOND_VS
3553 EMIT_COND_BRANCH (ins, OP_IBEQ - OP_IBEQ);
3556 EMIT_COND_BRANCH (ins, OP_IBNE_UN - OP_IBEQ);
3559 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
3562 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3563 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
3569 g_assert_not_reached ();
3572 /* FIXME does VFP requires both conds?
3573 * FPA requires EQ even thou the docs suggests that just CS is enough
3575 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_EQ);
3576 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_CS);
3579 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3580 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_GE);
3585 if (ins->dreg != ins->sreg1)
3586 ARM_MVFD (code, ins->dreg, ins->sreg1);
3587 #elif defined(ARM_FPU_VFP)
3588 ARM_CPYD (code, ins->dreg, ins->sreg1);
3593 g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
3594 g_assert_not_reached ();
3597 if ((cfg->opt & MONO_OPT_BRANCH) && ((code - cfg->native_code - offset) > max_len)) {
3598 g_warning ("wrong maximal instruction length of instruction %s (expected %d, got %d)",
3599 mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset);
3600 g_assert_not_reached ();
3606 last_offset = offset;
3609 cfg->code_len = code - cfg->native_code;
3612 #endif /* DISABLE_JIT */
3614 #ifdef HAVE_AEABI_READ_TP
3615 void __aeabi_read_tp (void);
3619 mono_arch_register_lowlevel_calls (void)
3621 /* The signature doesn't matter */
3622 mono_register_jit_icall (mono_arm_throw_exception, "mono_arm_throw_exception", mono_create_icall_signature ("void"), TRUE);
3623 mono_register_jit_icall (mono_arm_throw_exception_by_token, "mono_arm_throw_exception_by_token", mono_create_icall_signature ("void"), TRUE);
3625 #ifdef HAVE_AEABI_READ_TP
3626 mono_register_jit_icall (__aeabi_read_tp, "__aeabi_read_tp", mono_create_icall_signature ("void"), TRUE);
3630 #define patch_lis_ori(ip,val) do {\
3631 guint16 *__lis_ori = (guint16*)(ip); \
3632 __lis_ori [1] = (((guint32)(val)) >> 16) & 0xffff; \
3633 __lis_ori [3] = ((guint32)(val)) & 0xffff; \
3637 mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, gboolean run_cctors)
3639 MonoJumpInfo *patch_info;
3640 gboolean compile_aot = !run_cctors;
3642 for (patch_info = ji; patch_info; patch_info = patch_info->next) {
3643 unsigned char *ip = patch_info->ip.i + code;
3644 const unsigned char *target;
3646 if (patch_info->type == MONO_PATCH_INFO_SWITCH && !compile_aot) {
3647 gpointer *jt = (gpointer*)(ip + 8);
3649 /* jt is the inlined jump table, 2 instructions after ip
3650 * In the normal case we store the absolute addresses,
3651 * otherwise the displacements.
3653 for (i = 0; i < patch_info->data.table->table_size; i++)
3654 jt [i] = code + (int)patch_info->data.table->table [i];
3657 target = mono_resolve_patch_target (method, domain, code, patch_info, run_cctors);
3660 switch (patch_info->type) {
3661 case MONO_PATCH_INFO_BB:
3662 case MONO_PATCH_INFO_LABEL:
3665 /* No need to patch these */
3670 switch (patch_info->type) {
3671 case MONO_PATCH_INFO_IP:
3672 g_assert_not_reached ();
3673 patch_lis_ori (ip, ip);
3675 case MONO_PATCH_INFO_METHOD_REL:
3676 g_assert_not_reached ();
3677 *((gpointer *)(ip)) = code + patch_info->data.offset;
3679 case MONO_PATCH_INFO_METHODCONST:
3680 case MONO_PATCH_INFO_CLASS:
3681 case MONO_PATCH_INFO_IMAGE:
3682 case MONO_PATCH_INFO_FIELD:
3683 case MONO_PATCH_INFO_VTABLE:
3684 case MONO_PATCH_INFO_IID:
3685 case MONO_PATCH_INFO_SFLDA:
3686 case MONO_PATCH_INFO_LDSTR:
3687 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
3688 case MONO_PATCH_INFO_LDTOKEN:
3689 g_assert_not_reached ();
3690 /* from OP_AOTCONST : lis + ori */
3691 patch_lis_ori (ip, target);
3693 case MONO_PATCH_INFO_R4:
3694 case MONO_PATCH_INFO_R8:
3695 g_assert_not_reached ();
3696 *((gconstpointer *)(ip + 2)) = patch_info->data.target;
3698 case MONO_PATCH_INFO_EXC_NAME:
3699 g_assert_not_reached ();
3700 *((gconstpointer *)(ip + 1)) = patch_info->data.name;
3702 case MONO_PATCH_INFO_NONE:
3703 case MONO_PATCH_INFO_BB_OVF:
3704 case MONO_PATCH_INFO_EXC_OVF:
3705 /* everything is dealt with at epilog output time */
3710 arm_patch (ip, target);
3715 * Stack frame layout:
3717 * ------------------- fp
3718 * MonoLMF structure or saved registers
3719 * -------------------
3721 * -------------------
3723 * -------------------
3724 * optional 8 bytes for tracing
3725 * -------------------
3726 * param area size is cfg->param_area
3727 * ------------------- sp
3730 mono_arch_emit_prolog (MonoCompile *cfg)
3732 MonoMethod *method = cfg->method;
3734 MonoMethodSignature *sig;
3736 int alloc_size, pos, max_offset, i, rot_amount;
3741 int prev_sp_offset, reg_offset;
3743 if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
3746 sig = mono_method_signature (method);
3747 cfg->code_size = 256 + sig->param_count * 20;
3748 code = cfg->native_code = g_malloc (cfg->code_size);
3750 mono_emit_unwind_op_def_cfa (cfg, code, ARMREG_SP, 0);
3752 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
3754 alloc_size = cfg->stack_offset;
3757 if (!method->save_lmf) {
3758 /* We save SP by storing it into IP and saving IP */
3759 ARM_PUSH (code, (cfg->used_int_regs | (1 << ARMREG_IP) | (1 << ARMREG_LR)));
3760 prev_sp_offset = 8; /* ip and lr */
3761 for (i = 0; i < 16; ++i) {
3762 if (cfg->used_int_regs & (1 << i))
3763 prev_sp_offset += 4;
3765 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset);
3767 for (i = 0; i < 16; ++i) {
3768 if ((cfg->used_int_regs & (1 << i)) || (i == ARMREG_IP) || (i == ARMREG_LR)) {
3769 mono_emit_unwind_op_offset (cfg, code, i, (- prev_sp_offset) + reg_offset);
3774 ARM_PUSH (code, 0x5ff0);
3775 prev_sp_offset = 4 * 10; /* all but r0-r3, sp and pc */
3776 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset);
3778 for (i = 0; i < 16; ++i) {
3779 if ((i > ARMREG_R3) && (i != ARMREG_SP) && (i != ARMREG_PC)) {
3780 mono_emit_unwind_op_offset (cfg, code, i, (- prev_sp_offset) + reg_offset);
3784 pos += sizeof (MonoLMF) - prev_sp_offset;
3788 // align to MONO_ARCH_FRAME_ALIGNMENT bytes
3789 if (alloc_size & (MONO_ARCH_FRAME_ALIGNMENT - 1)) {
3790 alloc_size += MONO_ARCH_FRAME_ALIGNMENT - 1;
3791 alloc_size &= ~(MONO_ARCH_FRAME_ALIGNMENT - 1);
3794 /* the stack used in the pushed regs */
3795 if (prev_sp_offset & 4)
3797 cfg->stack_usage = alloc_size;
3799 if ((i = mono_arm_is_rotated_imm8 (alloc_size, &rot_amount)) >= 0) {
3800 ARM_SUB_REG_IMM (code, ARMREG_SP, ARMREG_SP, i, rot_amount);
3802 code = mono_arm_emit_load_imm (code, ARMREG_IP, alloc_size);
3803 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
3805 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset + alloc_size);
3807 if (cfg->frame_reg != ARMREG_SP) {
3808 ARM_MOV_REG_REG (code, cfg->frame_reg, ARMREG_SP);
3809 mono_emit_unwind_op_def_cfa_reg (cfg, code, cfg->frame_reg);
3811 //g_print ("prev_sp_offset: %d, alloc_size:%d\n", prev_sp_offset, alloc_size);
3812 prev_sp_offset += alloc_size;
3814 /* compute max_offset in order to use short forward jumps
3815 * we could skip do it on arm because the immediate displacement
3816 * for jumps is large enough, it may be useful later for constant pools
3819 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
3820 MonoInst *ins = bb->code;
3821 bb->max_offset = max_offset;
3823 if (cfg->prof_options & MONO_PROFILE_COVERAGE)
3826 MONO_BB_FOR_EACH_INS (bb, ins)
3827 max_offset += ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
3830 /* store runtime generic context */
3831 if (cfg->rgctx_var) {
3832 MonoInst *ins = cfg->rgctx_var;
3834 g_assert (ins->opcode == OP_REGOFFSET);
3836 if (arm_is_imm12 (ins->inst_offset)) {
3837 ARM_STR_IMM (code, MONO_ARCH_RGCTX_REG, ins->inst_basereg, ins->inst_offset);
3839 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3840 ARM_STR_REG_REG (code, MONO_ARCH_RGCTX_REG, ins->inst_basereg, ARMREG_LR);
3844 /* load arguments allocated to register from the stack */
3847 cinfo = get_call_info (sig, sig->pinvoke);
3849 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
3850 ArgInfo *ainfo = &cinfo->ret;
3851 inst = cfg->vret_addr;
3852 g_assert (arm_is_imm12 (inst->inst_offset));
3853 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3855 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
3856 ArgInfo *ainfo = cinfo->args + i;
3857 inst = cfg->args [pos];
3859 if (cfg->verbose_level > 2)
3860 g_print ("Saving argument %d (type: %d)\n", i, ainfo->regtype);
3861 if (inst->opcode == OP_REGVAR) {
3862 if (ainfo->regtype == RegTypeGeneral)
3863 ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
3864 else if (ainfo->regtype == RegTypeFP) {
3865 g_assert_not_reached ();
3866 } else if (ainfo->regtype == RegTypeBase) {
3867 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
3868 ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3870 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3871 ARM_LDR_REG_REG (code, inst->dreg, ARMREG_SP, ARMREG_IP);
3874 g_assert_not_reached ();
3876 if (cfg->verbose_level > 2)
3877 g_print ("Argument %d assigned to register %s\n", pos, mono_arch_regname (inst->dreg));
3879 /* the argument should be put on the stack: FIXME handle size != word */
3880 if (ainfo->regtype == RegTypeGeneral) {
3881 switch (ainfo->size) {
3883 if (arm_is_imm12 (inst->inst_offset))
3884 ARM_STRB_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3886 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3887 ARM_STRB_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3891 if (arm_is_imm8 (inst->inst_offset)) {
3892 ARM_STRH_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3894 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3895 ARM_STRH_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3899 g_assert (arm_is_imm12 (inst->inst_offset));
3900 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3901 g_assert (arm_is_imm12 (inst->inst_offset + 4));
3902 ARM_STR_IMM (code, ainfo->reg + 1, inst->inst_basereg, inst->inst_offset + 4);
3905 if (arm_is_imm12 (inst->inst_offset)) {
3906 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3908 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3909 ARM_STR_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3913 } else if (ainfo->regtype == RegTypeBaseGen) {
3914 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
3915 g_assert (arm_is_imm12 (inst->inst_offset));
3916 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3917 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3918 ARM_STR_IMM (code, ARMREG_R3, inst->inst_basereg, inst->inst_offset);
3919 } else if (ainfo->regtype == RegTypeBase) {
3920 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
3921 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3923 code = mono_arm_emit_load_imm (code, ARMREG_IP, prev_sp_offset + ainfo->offset);
3924 ARM_LDR_REG_REG (code, ARMREG_LR, ARMREG_SP, ARMREG_IP);
3927 switch (ainfo->size) {
3929 if (arm_is_imm8 (inst->inst_offset)) {
3930 ARM_STRB_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3932 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3933 ARM_STRB_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3937 if (arm_is_imm8 (inst->inst_offset)) {
3938 ARM_STRH_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3940 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3941 ARM_STRH_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3945 if (arm_is_imm12 (inst->inst_offset)) {
3946 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3948 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3949 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3951 if (arm_is_imm12 (prev_sp_offset + ainfo->offset + 4)) {
3952 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset + 4));
3954 code = mono_arm_emit_load_imm (code, ARMREG_IP, prev_sp_offset + ainfo->offset + 4);
3955 ARM_LDR_REG_REG (code, ARMREG_LR, ARMREG_SP, ARMREG_IP);
3957 if (arm_is_imm12 (inst->inst_offset + 4)) {
3958 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3960 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset + 4);
3961 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3965 if (arm_is_imm12 (inst->inst_offset)) {
3966 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3968 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3969 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3973 } else if (ainfo->regtype == RegTypeFP) {
3974 g_assert_not_reached ();
3975 } else if (ainfo->regtype == RegTypeStructByVal) {
3976 int doffset = inst->inst_offset;
3980 size = mini_type_stack_size_full (cfg->generic_sharing_context, inst->inst_vtype, NULL, sig->pinvoke);
3981 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
3982 if (arm_is_imm12 (doffset)) {
3983 ARM_STR_IMM (code, ainfo->reg + cur_reg, inst->inst_basereg, doffset);
3985 code = mono_arm_emit_load_imm (code, ARMREG_IP, doffset);
3986 ARM_STR_REG_REG (code, ainfo->reg + cur_reg, inst->inst_basereg, ARMREG_IP);
3988 soffset += sizeof (gpointer);
3989 doffset += sizeof (gpointer);
3991 if (ainfo->vtsize) {
3992 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
3993 //g_print ("emit_memcpy (prev_sp_ofs: %d, ainfo->offset: %d, soffset: %d)\n", prev_sp_offset, ainfo->offset, soffset);
3994 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, doffset, ARMREG_SP, prev_sp_offset + ainfo->offset);
3996 } else if (ainfo->regtype == RegTypeStructByAddr) {
3997 g_assert_not_reached ();
3998 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
3999 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, inst->inst_offset, ainfo->reg, 0);
4001 g_assert_not_reached ();
4006 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
4007 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->domain);
4008 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
4009 (gpointer)"mono_jit_thread_attach");
4010 code = emit_call_seq (cfg, code);
4013 if (method->save_lmf) {
4014 gboolean get_lmf_fast = FALSE;
4016 #ifdef HAVE_AEABI_READ_TP
4017 gint32 lmf_addr_tls_offset = mono_get_lmf_addr_tls_offset ();
4019 if (lmf_addr_tls_offset != -1) {
4020 get_lmf_fast = TRUE;
4022 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
4023 (gpointer)"__aeabi_read_tp");
4024 code = emit_call_seq (cfg, code);
4026 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, lmf_addr_tls_offset);
4027 get_lmf_fast = TRUE;
4030 if (!get_lmf_fast) {
4031 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
4032 (gpointer)"mono_get_lmf_addr");
4033 code = emit_call_seq (cfg, code);
4035 /* we build the MonoLMF structure on the stack - see mini-arm.h */
4036 /* lmf_offset is the offset from the previous stack pointer,
4037 * alloc_size is the total stack space allocated, so the offset
4038 * of MonoLMF from the current stack ptr is alloc_size - lmf_offset.
4039 * The pointer to the struct is put in r1 (new_lmf).
4040 * r2 is used as scratch
4041 * The callee-saved registers are already in the MonoLMF structure
4043 code = emit_big_add (code, ARMREG_R1, ARMREG_SP, alloc_size - lmf_offset);
4044 /* r0 is the result from mono_get_lmf_addr () */
4045 ARM_STR_IMM (code, ARMREG_R0, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
4046 /* new_lmf->previous_lmf = *lmf_addr */
4047 ARM_LDR_IMM (code, ARMREG_R2, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4048 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4049 /* *(lmf_addr) = r1 */
4050 ARM_STR_IMM (code, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4051 /* Skip method (only needed for trampoline LMF frames) */
4052 ARM_STR_IMM (code, ARMREG_SP, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, ebp));
4053 /* save the current IP */
4054 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_PC);
4055 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, eip));
4059 code = mono_arch_instrument_prolog (cfg, mono_trace_enter_method, code, TRUE);
4061 cfg->code_len = code - cfg->native_code;
4062 g_assert (cfg->code_len < cfg->code_size);
4069 mono_arch_emit_epilog (MonoCompile *cfg)
4071 MonoMethod *method = cfg->method;
4072 int pos, i, rot_amount;
4073 int max_epilog_size = 16 + 20*4;
4076 if (cfg->method->save_lmf)
4077 max_epilog_size += 128;
4079 if (mono_jit_trace_calls != NULL)
4080 max_epilog_size += 50;
4082 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE)
4083 max_epilog_size += 50;
4085 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
4086 cfg->code_size *= 2;
4087 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
4088 mono_jit_stats.code_reallocs++;
4092 * Keep in sync with OP_JMP
4094 code = cfg->native_code + cfg->code_len;
4096 if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) {
4097 code = mono_arch_instrument_epilog (cfg, mono_trace_leave_method, code, TRUE);
4101 if (method->save_lmf) {
4103 /* all but r0-r3, sp and pc */
4104 pos += sizeof (MonoLMF) - (4 * 10);
4106 /* r2 contains the pointer to the current LMF */
4107 code = emit_big_add (code, ARMREG_R2, cfg->frame_reg, cfg->stack_usage - lmf_offset);
4108 /* ip = previous_lmf */
4109 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4111 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
4112 /* *(lmf_addr) = previous_lmf */
4113 ARM_STR_IMM (code, ARMREG_IP, ARMREG_LR, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4114 /* FIXME: speedup: there is no actual need to restore the registers if
4115 * we didn't actually change them (idea from Zoltan).
4118 /* point sp at the registers to restore: 10 is 14 -4, because we skip r0-r3 */
4119 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_R2, (sizeof (MonoLMF) - 10 * sizeof (gulong)));
4120 ARM_POP_NWB (code, 0xaff0); /* restore ip to sp and lr to pc */
4122 if ((i = mono_arm_is_rotated_imm8 (cfg->stack_usage, &rot_amount)) >= 0) {
4123 ARM_ADD_REG_IMM (code, ARMREG_SP, cfg->frame_reg, i, rot_amount);
4125 code = mono_arm_emit_load_imm (code, ARMREG_IP, cfg->stack_usage);
4126 ARM_ADD_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
4128 /* FIXME: add v4 thumb interworking support */
4129 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP) | (1 << ARMREG_PC)));
4132 cfg->code_len = code - cfg->native_code;
4134 g_assert (cfg->code_len < cfg->code_size);
4138 /* remove once throw_exception_by_name is eliminated */
4140 exception_id_by_name (const char *name)
4142 if (strcmp (name, "IndexOutOfRangeException") == 0)
4143 return MONO_EXC_INDEX_OUT_OF_RANGE;
4144 if (strcmp (name, "OverflowException") == 0)
4145 return MONO_EXC_OVERFLOW;
4146 if (strcmp (name, "ArithmeticException") == 0)
4147 return MONO_EXC_ARITHMETIC;
4148 if (strcmp (name, "DivideByZeroException") == 0)
4149 return MONO_EXC_DIVIDE_BY_ZERO;
4150 if (strcmp (name, "InvalidCastException") == 0)
4151 return MONO_EXC_INVALID_CAST;
4152 if (strcmp (name, "NullReferenceException") == 0)
4153 return MONO_EXC_NULL_REF;
4154 if (strcmp (name, "ArrayTypeMismatchException") == 0)
4155 return MONO_EXC_ARRAY_TYPE_MISMATCH;
4156 g_error ("Unknown intrinsic exception %s\n", name);
4161 mono_arch_emit_exceptions (MonoCompile *cfg)
4163 MonoJumpInfo *patch_info;
4166 const guint8* exc_throw_pos [MONO_EXC_INTRINS_NUM] = {NULL};
4167 guint8 exc_throw_found [MONO_EXC_INTRINS_NUM] = {0};
4168 int max_epilog_size = 50;
4170 /* count the number of exception infos */
4173 * make sure we have enough space for exceptions
4175 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
4176 if (patch_info->type == MONO_PATCH_INFO_EXC) {
4177 i = exception_id_by_name (patch_info->data.target);
4178 if (!exc_throw_found [i]) {
4179 max_epilog_size += 32;
4180 exc_throw_found [i] = TRUE;
4185 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
4186 cfg->code_size *= 2;
4187 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
4188 mono_jit_stats.code_reallocs++;
4191 code = cfg->native_code + cfg->code_len;
4193 /* add code to raise exceptions */
4194 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
4195 switch (patch_info->type) {
4196 case MONO_PATCH_INFO_EXC: {
4197 MonoClass *exc_class;
4198 unsigned char *ip = patch_info->ip.i + cfg->native_code;
4200 i = exception_id_by_name (patch_info->data.target);
4201 if (exc_throw_pos [i]) {
4202 arm_patch (ip, exc_throw_pos [i]);
4203 patch_info->type = MONO_PATCH_INFO_NONE;
4206 exc_throw_pos [i] = code;
4208 arm_patch (ip, code);
4210 exc_class = mono_class_from_name (mono_defaults.corlib, "System", patch_info->data.name);
4211 g_assert (exc_class);
4213 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_LR);
4214 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
4215 patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD;
4216 patch_info->data.name = "mono_arch_throw_corlib_exception";
4217 patch_info->ip.i = code - cfg->native_code;
4219 *(guint32*)(gpointer)code = exc_class->type_token;
4229 cfg->code_len = code - cfg->native_code;
4231 g_assert (cfg->code_len < cfg->code_size);
4235 static gboolean tls_offset_inited = FALSE;
4238 mono_arch_setup_jit_tls_data (MonoJitTlsData *tls)
4240 if (!tls_offset_inited) {
4241 tls_offset_inited = TRUE;
4243 lmf_tls_offset = mono_get_lmf_tls_offset ();
4244 lmf_addr_tls_offset = mono_get_lmf_addr_tls_offset ();
4249 mono_arch_free_jit_tls_data (MonoJitTlsData *tls)
4254 mono_arch_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
4261 mono_arch_print_tree (MonoInst *tree, int arity)
4267 mono_arch_get_domain_intrinsic (MonoCompile* cfg)
4269 return mono_get_domain_intrinsic (cfg);
4273 mono_arch_get_thread_intrinsic (MonoCompile* cfg)
4275 return mono_get_thread_intrinsic (cfg);
4279 mono_arch_get_patch_offset (guint8 *code)
4286 mono_arch_flush_register_windows (void)
4290 #ifdef MONO_ARCH_HAVE_IMT
4293 mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoInst *imt_arg)
4295 if (cfg->compile_aot) {
4296 int method_reg = mono_alloc_ireg (cfg);
4299 call->dynamic_imt_arg = TRUE;
4302 mono_call_inst_add_outarg_reg (cfg, call, imt_arg->dreg, ARMREG_V5, FALSE);
4304 MONO_INST_NEW (cfg, ins, OP_AOTCONST);
4305 ins->dreg = method_reg;
4306 ins->inst_p0 = call->method;
4307 ins->inst_c1 = MONO_PATCH_INFO_METHODCONST;
4308 MONO_ADD_INS (cfg->cbb, ins);
4310 mono_call_inst_add_outarg_reg (cfg, call, method_reg, ARMREG_V5, FALSE);
4312 } else if (cfg->generic_context) {
4314 /* Always pass in a register for simplicity */
4315 call->dynamic_imt_arg = TRUE;
4317 cfg->uses_rgctx_reg = TRUE;
4320 mono_call_inst_add_outarg_reg (cfg, call, imt_arg->dreg, ARMREG_V5, FALSE);
4323 int method_reg = mono_alloc_preg (cfg);
4325 MONO_INST_NEW (cfg, ins, OP_PCONST);
4326 ins->inst_p0 = call->method;
4327 ins->dreg = method_reg;
4328 MONO_ADD_INS (cfg->cbb, ins);
4330 mono_call_inst_add_outarg_reg (cfg, call, method_reg, ARMREG_V5, FALSE);
4336 mono_arch_find_imt_method (gpointer *regs, guint8 *code)
4338 guint32 *code_ptr = (guint32*)code;
4340 /* The IMT value is stored in the code stream right after the LDC instruction. */
4341 if (!IS_LDR_PC (code_ptr [0])) {
4342 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]);
4343 g_assert (IS_LDR_PC (code_ptr [0]));
4345 if (code_ptr [1] == 0)
4346 /* This is AOTed code, the IMT method is in V5 */
4347 return (MonoMethod*)regs [ARMREG_V5];
4349 return (MonoMethod*) code_ptr [1];
4353 mono_arch_find_this_argument (gpointer *regs, MonoMethod *method, MonoGenericSharingContext *gsctx)
4355 return mono_arch_get_this_arg_from_call (gsctx, mono_method_signature (method), (gssize*)regs, NULL);
4359 mono_arch_find_static_call_vtable (gpointer *regs, guint8 *code)
4361 return (MonoVTable*) regs [MONO_ARCH_RGCTX_REG];
4364 #define ENABLE_WRONG_METHOD_CHECK 0
4365 #define BASE_SIZE (6 * 4)
4366 #define BSEARCH_ENTRY_SIZE (4 * 4)
4367 #define CMP_SIZE (3 * 4)
4368 #define BRANCH_SIZE (1 * 4)
4369 #define CALL_SIZE (2 * 4)
4370 #define WMC_SIZE (5 * 4)
4371 #define DISTANCE(A, B) (((gint32)(B)) - ((gint32)(A)))
4374 arm_emit_value_and_patch_ldr (arminstr_t *code, arminstr_t *target, guint32 value)
4376 guint32 delta = DISTANCE (target, code);
4378 g_assert (delta >= 0 && delta <= 0xFFF);
4379 *target = *target | delta;
4385 mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count,
4386 gpointer fail_tramp)
4388 int size, i, extra_space = 0;
4389 arminstr_t *code, *start, *vtable_target = NULL;
4390 gboolean large_offsets = FALSE;
4391 guint32 **constant_pool_starts;
4394 constant_pool_starts = g_new0 (guint32*, count);
4397 * We might be called with a fail_tramp from the IMT builder code even if
4398 * MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK is not defined.
4400 //g_assert (!fail_tramp);
4402 for (i = 0; i < count; ++i) {
4403 MonoIMTCheckItem *item = imt_entries [i];
4404 if (item->is_equals) {
4405 if (!arm_is_imm12 (DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]))) {
4406 item->chunk_size += 32;
4407 large_offsets = TRUE;
4410 if (item->check_target_idx) {
4411 if (!item->compare_done)
4412 item->chunk_size += CMP_SIZE;
4413 item->chunk_size += BRANCH_SIZE;
4415 #if ENABLE_WRONG_METHOD_CHECK
4416 item->chunk_size += WMC_SIZE;
4419 item->chunk_size += CALL_SIZE;
4421 item->chunk_size += BSEARCH_ENTRY_SIZE;
4422 imt_entries [item->check_target_idx]->compare_done = TRUE;
4424 size += item->chunk_size;
4428 size += 4 * count; /* The ARM_ADD_REG_IMM to pop the stack */
4430 start = code = mono_domain_code_reserve (domain, size);
4433 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);
4434 for (i = 0; i < count; ++i) {
4435 MonoIMTCheckItem *item = imt_entries [i];
4436 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);
4441 ARM_PUSH4 (code, ARMREG_R0, ARMREG_R1, ARMREG_IP, ARMREG_PC);
4443 ARM_PUSH2 (code, ARMREG_R0, ARMREG_R1);
4444 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, -4);
4445 vtable_target = code;
4446 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
4448 /* R0 == 0 means we are called from AOT code. In this case, V5 contains the IMT method */
4449 ARM_CMP_REG_IMM8 (code, ARMREG_R0, 0);
4450 ARM_MOV_REG_REG_COND (code, ARMREG_R0, ARMREG_V5, ARMCOND_EQ);
4452 for (i = 0; i < count; ++i) {
4453 MonoIMTCheckItem *item = imt_entries [i];
4454 arminstr_t *imt_method = NULL, *vtable_offset_ins = NULL;
4455 gint32 vtable_offset;
4457 item->code_target = (guint8*)code;
4459 if (item->is_equals) {
4460 if (item->check_target_idx) {
4461 if (!item->compare_done) {
4463 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4464 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
4466 item->jmp_code = (guint8*)code;
4467 ARM_B_COND (code, ARMCOND_NE, 0);
4469 /*Enable the commented code to assert on wrong method*/
4470 #if ENABLE_WRONG_METHOD_CHECK
4472 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4473 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
4474 ARM_B_COND (code, ARMCOND_NE, 1);
4480 vtable_offset = DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]);
4481 if (!arm_is_imm12 (vtable_offset)) {
4483 * We need to branch to a computed address but we don't have
4484 * a free register to store it, since IP must contain the
4485 * vtable address. So we push the two values to the stack, and
4486 * load them both using LDM.
4488 /* Compute target address */
4489 vtable_offset_ins = code;
4490 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4491 ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_IP, ARMREG_R1);
4492 /* Save it to the fourth slot */
4493 ARM_STR_IMM (code, ARMREG_R1, ARMREG_SP, 3 * sizeof (gpointer));
4494 /* Restore registers and branch */
4495 ARM_POP4 (code, ARMREG_R0, ARMREG_R1, ARMREG_IP, ARMREG_PC);
4497 code = arm_emit_value_and_patch_ldr (code, vtable_offset_ins, vtable_offset);
4499 ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
4501 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 2 * sizeof (gpointer));
4502 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, vtable_offset);
4506 code = arm_emit_value_and_patch_ldr (code, imt_method, (guint32)item->key);
4508 /*must emit after unconditional branch*/
4509 if (vtable_target) {
4510 code = arm_emit_value_and_patch_ldr (code, vtable_target, (guint32)vtable);
4511 item->chunk_size += 4;
4512 vtable_target = NULL;
4515 /*We reserve the space for bsearch IMT values after the first entry with an absolute jump*/
4516 constant_pool_starts [i] = code;
4518 code += extra_space;
4522 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4523 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
4525 item->jmp_code = (guint8*)code;
4526 ARM_B_COND (code, ARMCOND_GE, 0);
4531 for (i = 0; i < count; ++i) {
4532 MonoIMTCheckItem *item = imt_entries [i];
4533 if (item->jmp_code) {
4534 if (item->check_target_idx)
4535 arm_patch (item->jmp_code, imt_entries [item->check_target_idx]->code_target);
4537 if (i > 0 && item->is_equals) {
4539 arminstr_t *space_start = constant_pool_starts [i];
4540 for (j = i - 1; j >= 0 && !imt_entries [j]->is_equals; --j) {
4541 space_start = arm_emit_value_and_patch_ldr (space_start, (arminstr_t*)imt_entries [j]->code_target, (guint32)imt_entries [j]->key);
4548 char *buff = g_strdup_printf ("thunk_for_class_%s_%s_entries_%d", vtable->klass->name_space, vtable->klass->name, count);
4549 mono_disassemble_code (NULL, (guint8*)start, size, buff);
4554 g_free (constant_pool_starts);
4556 mono_arch_flush_icache ((guint8*)start, size);
4557 mono_stats.imt_thunks_size += code - start;
4559 g_assert (DISTANCE (start, code) <= size);
4566 mono_arch_context_get_int_reg (MonoContext *ctx, int reg)
4568 if (reg >= 4 && reg <= 11)
4569 return (gpointer)ctx->regs [reg - 4];
4570 else if (reg == ARMREG_IP)
4571 return (gpointer)ctx->regs [8];
4572 else if (reg == ARMREG_LR)
4573 return (gpointer)ctx->regs [9];
4575 g_assert_not_reached ();