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;
866 cinfo->args [n].offset = stack_size;
867 /*g_print ("offset for arg %d at %d\n", n, stack_size);*/
868 stack_size += nwords * sizeof (gpointer);
875 cinfo->args [n].size = 8;
876 add_general (&gr, &stack_size, cinfo->args + n, FALSE);
880 g_error ("Can't trampoline 0x%x", sig->params [i]->type);
885 simpletype = mini_type_get_underlying_type (NULL, sig->ret);
886 switch (simpletype->type) {
887 case MONO_TYPE_BOOLEAN:
898 case MONO_TYPE_FNPTR:
899 case MONO_TYPE_CLASS:
900 case MONO_TYPE_OBJECT:
901 case MONO_TYPE_SZARRAY:
902 case MONO_TYPE_ARRAY:
903 case MONO_TYPE_STRING:
904 cinfo->ret.reg = ARMREG_R0;
908 cinfo->ret.reg = ARMREG_R0;
912 cinfo->ret.reg = ARMREG_R0;
913 /* FIXME: cinfo->ret.reg = ???;
914 cinfo->ret.regtype = RegTypeFP;*/
916 case MONO_TYPE_GENERICINST:
917 if (!mono_type_generic_inst_is_valuetype (sig->ret)) {
918 cinfo->ret.reg = ARMREG_R0;
922 case MONO_TYPE_VALUETYPE:
924 case MONO_TYPE_TYPEDBYREF:
928 g_error ("Can't handle as return value 0x%x", sig->ret->type);
932 /* align stack size to 8 */
933 DEBUG (printf (" stack size: %d (%d)\n", (stack_size + 15) & ~15, stack_size));
934 stack_size = (stack_size + 7) & ~7;
936 cinfo->stack_usage = stack_size;
942 * Set var information according to the calling convention. arm version.
943 * The locals var stuff should most likely be split in another method.
946 mono_arch_allocate_vars (MonoCompile *cfg)
948 MonoMethodSignature *sig;
949 MonoMethodHeader *header;
951 int i, offset, size, align, curinst;
952 int frame_reg = ARMREG_FP;
954 /* FIXME: this will change when we use FP as gcc does */
955 cfg->flags |= MONO_CFG_HAS_SPILLUP;
957 /* allow room for the vararg method args: void* and long/double */
958 if (mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method))
959 cfg->param_area = MAX (cfg->param_area, sizeof (gpointer)*8);
961 header = mono_method_get_header (cfg->method);
964 * We use the frame register also for any method that has
965 * exception clauses. This way, when the handlers are called,
966 * the code will reference local variables using the frame reg instead of
967 * the stack pointer: if we had to restore the stack pointer, we'd
968 * corrupt the method frames that are already on the stack (since
969 * filters get called before stack unwinding happens) when the filter
970 * code would call any method (this also applies to finally etc.).
972 if ((cfg->flags & MONO_CFG_HAS_ALLOCA) || header->num_clauses)
973 frame_reg = ARMREG_FP;
974 cfg->frame_reg = frame_reg;
975 if (frame_reg != ARMREG_SP) {
976 cfg->used_int_regs |= 1 << frame_reg;
979 if (cfg->compile_aot || cfg->uses_rgctx_reg)
980 /* V5 is reserved for passing the vtable/rgctx/IMT method */
981 cfg->used_int_regs |= (1 << ARMREG_V5);
983 sig = mono_method_signature (cfg->method);
987 if (!MONO_TYPE_ISSTRUCT (sig->ret)) {
988 /* FIXME: handle long and FP values */
989 switch (mini_type_get_underlying_type (NULL, sig->ret)->type) {
993 cfg->ret->opcode = OP_REGVAR;
994 cfg->ret->inst_c0 = ARMREG_R0;
998 /* local vars are at a positive offset from the stack pointer */
1000 * also note that if the function uses alloca, we use FP
1001 * to point at the local variables.
1003 offset = 0; /* linkage area */
1004 /* align the offset to 16 bytes: not sure this is needed here */
1006 //offset &= ~(8 - 1);
1008 /* add parameter area size for called functions */
1009 offset += cfg->param_area;
1012 if (cfg->flags & MONO_CFG_HAS_FPOUT)
1015 /* allow room to save the return value */
1016 if (mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method))
1019 /* the MonoLMF structure is stored just below the stack pointer */
1021 if (sig->call_convention == MONO_CALL_VARARG) {
1022 cfg->sig_cookie = 0;
1025 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
1026 inst = cfg->vret_addr;
1027 offset += sizeof(gpointer) - 1;
1028 offset &= ~(sizeof(gpointer) - 1);
1029 inst->inst_offset = offset;
1030 inst->opcode = OP_REGOFFSET;
1031 inst->inst_basereg = frame_reg;
1032 if (G_UNLIKELY (cfg->verbose_level > 1)) {
1033 printf ("vret_addr =");
1034 mono_print_ins (cfg->vret_addr);
1036 offset += sizeof(gpointer);
1037 if (sig->call_convention == MONO_CALL_VARARG)
1038 cfg->sig_cookie += sizeof (gpointer);
1041 curinst = cfg->locals_start;
1042 for (i = curinst; i < cfg->num_varinfo; ++i) {
1043 inst = cfg->varinfo [i];
1044 if ((inst->flags & MONO_INST_IS_DEAD) || inst->opcode == OP_REGVAR)
1047 /* inst->backend.is_pinvoke indicates native sized value types, this is used by the
1048 * pinvoke wrappers when they call functions returning structure */
1049 if (inst->backend.is_pinvoke && MONO_TYPE_ISSTRUCT (inst->inst_vtype) && inst->inst_vtype->type != MONO_TYPE_TYPEDBYREF) {
1051 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), &ualign);
1055 size = mono_type_size (inst->inst_vtype, &align);
1057 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
1058 * since it loads/stores misaligned words, which don't do the right thing.
1060 if (align < 4 && size >= 4)
1062 offset += align - 1;
1063 offset &= ~(align - 1);
1064 inst->inst_offset = offset;
1065 inst->opcode = OP_REGOFFSET;
1066 inst->inst_basereg = frame_reg;
1068 //g_print ("allocating local %d to %d\n", i, inst->inst_offset);
1073 inst = cfg->args [curinst];
1074 if (inst->opcode != OP_REGVAR) {
1075 inst->opcode = OP_REGOFFSET;
1076 inst->inst_basereg = frame_reg;
1077 offset += sizeof (gpointer) - 1;
1078 offset &= ~(sizeof (gpointer) - 1);
1079 inst->inst_offset = offset;
1080 offset += sizeof (gpointer);
1081 if (sig->call_convention == MONO_CALL_VARARG)
1082 cfg->sig_cookie += sizeof (gpointer);
1087 for (i = 0; i < sig->param_count; ++i) {
1088 inst = cfg->args [curinst];
1089 if (inst->opcode != OP_REGVAR) {
1090 inst->opcode = OP_REGOFFSET;
1091 inst->inst_basereg = frame_reg;
1092 size = mono_type_size (sig->params [i], &align);
1093 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
1094 * since it loads/stores misaligned words, which don't do the right thing.
1096 if (align < 4 && size >= 4)
1098 offset += align - 1;
1099 offset &= ~(align - 1);
1100 inst->inst_offset = offset;
1102 if ((sig->call_convention == MONO_CALL_VARARG) && (i < sig->sentinelpos))
1103 cfg->sig_cookie += size;
1108 /* align the offset to 8 bytes */
1113 cfg->stack_offset = offset;
1117 mono_arch_create_vars (MonoCompile *cfg)
1119 MonoMethodSignature *sig;
1121 sig = mono_method_signature (cfg->method);
1123 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
1124 cfg->vret_addr = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_ARG);
1125 if (G_UNLIKELY (cfg->verbose_level > 1)) {
1126 printf ("vret_addr = ");
1127 mono_print_ins (cfg->vret_addr);
1133 mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
1136 MonoMethodSignature *sig;
1140 sig = call->signature;
1141 n = sig->param_count + sig->hasthis;
1143 cinfo = get_call_info (sig, sig->pinvoke);
1145 for (i = 0; i < n; ++i) {
1146 ArgInfo *ainfo = cinfo->args + i;
1149 if (i >= sig->hasthis)
1150 t = sig->params [i - sig->hasthis];
1152 t = &mono_defaults.int_class->byval_arg;
1153 t = mini_type_get_underlying_type (NULL, t);
1155 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
1160 in = call->args [i];
1162 switch (ainfo->regtype) {
1163 case RegTypeGeneral:
1164 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1165 MONO_INST_NEW (cfg, ins, OP_MOVE);
1166 ins->dreg = mono_alloc_ireg (cfg);
1167 ins->sreg1 = in->dreg + 1;
1168 MONO_ADD_INS (cfg->cbb, ins);
1169 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1171 MONO_INST_NEW (cfg, ins, OP_MOVE);
1172 ins->dreg = mono_alloc_ireg (cfg);
1173 ins->sreg1 = in->dreg + 2;
1174 MONO_ADD_INS (cfg->cbb, ins);
1175 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg + 1, FALSE);
1176 } else if (!t->byref && ((t->type == MONO_TYPE_R8) || (t->type == MONO_TYPE_R4))) {
1177 #ifndef MONO_ARCH_SOFT_FLOAT
1181 if (ainfo->size == 4) {
1182 #ifdef MONO_ARCH_SOFT_FLOAT
1183 /* mono_emit_call_args () have already done the r8->r4 conversion */
1184 /* The converted value is in an int vreg */
1185 MONO_INST_NEW (cfg, ins, OP_MOVE);
1186 ins->dreg = mono_alloc_ireg (cfg);
1187 ins->sreg1 = in->dreg;
1188 MONO_ADD_INS (cfg->cbb, ins);
1189 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1191 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1192 creg = mono_alloc_ireg (cfg);
1193 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1194 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg, FALSE);
1197 #ifdef MONO_ARCH_SOFT_FLOAT
1198 MONO_INST_NEW (cfg, ins, OP_FGETLOW32);
1199 ins->dreg = mono_alloc_ireg (cfg);
1200 ins->sreg1 = in->dreg;
1201 MONO_ADD_INS (cfg->cbb, ins);
1202 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1204 MONO_INST_NEW (cfg, ins, OP_FGETHIGH32);
1205 ins->dreg = mono_alloc_ireg (cfg);
1206 ins->sreg1 = in->dreg;
1207 MONO_ADD_INS (cfg->cbb, ins);
1208 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg + 1, FALSE);
1210 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1211 creg = mono_alloc_ireg (cfg);
1212 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1213 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg, FALSE);
1214 creg = mono_alloc_ireg (cfg);
1215 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8 + 4));
1216 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg + 1, FALSE);
1219 cfg->flags |= MONO_CFG_HAS_FPOUT;
1221 MONO_INST_NEW (cfg, ins, OP_MOVE);
1222 ins->dreg = mono_alloc_ireg (cfg);
1223 ins->sreg1 = in->dreg;
1224 MONO_ADD_INS (cfg->cbb, ins);
1226 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1229 case RegTypeStructByAddr:
1232 /* FIXME: where si the data allocated? */
1233 arg->backend.reg3 = ainfo->reg;
1234 call->used_iregs |= 1 << ainfo->reg;
1235 g_assert_not_reached ();
1238 case RegTypeStructByVal:
1239 MONO_INST_NEW (cfg, ins, OP_OUTARG_VT);
1240 ins->opcode = OP_OUTARG_VT;
1241 ins->sreg1 = in->dreg;
1242 ins->klass = in->klass;
1243 ins->inst_p0 = call;
1244 ins->inst_p1 = mono_mempool_alloc (cfg->mempool, sizeof (ArgInfo));
1245 memcpy (ins->inst_p1, ainfo, sizeof (ArgInfo));
1246 MONO_ADD_INS (cfg->cbb, ins);
1249 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1250 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1251 } else if (!t->byref && ((t->type == MONO_TYPE_R4) || (t->type == MONO_TYPE_R8))) {
1252 if (t->type == MONO_TYPE_R8) {
1253 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1255 #ifdef MONO_ARCH_SOFT_FLOAT
1256 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1258 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1262 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1265 case RegTypeBaseGen:
1266 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1267 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);
1268 MONO_INST_NEW (cfg, ins, OP_MOVE);
1269 ins->dreg = mono_alloc_ireg (cfg);
1270 ins->sreg1 = G_BYTE_ORDER == G_BIG_ENDIAN ? in->dreg + 2 : in->dreg + 1;
1271 MONO_ADD_INS (cfg->cbb, ins);
1272 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ARMREG_R3, FALSE);
1273 } else if (!t->byref && (t->type == MONO_TYPE_R8)) {
1276 #ifdef MONO_ARCH_SOFT_FLOAT
1277 g_assert_not_reached ();
1280 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1281 creg = mono_alloc_ireg (cfg);
1282 mono_call_inst_add_outarg_reg (cfg, call, creg, ARMREG_R3, FALSE);
1283 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1284 creg = mono_alloc_ireg (cfg);
1285 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 4));
1286 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, creg);
1287 cfg->flags |= MONO_CFG_HAS_FPOUT;
1289 g_assert_not_reached ();
1296 arg->backend.reg3 = ainfo->reg;
1297 /* FP args are passed in int regs */
1298 call->used_iregs |= 1 << ainfo->reg;
1299 if (ainfo->size == 8) {
1300 arg->opcode = OP_OUTARG_R8;
1301 call->used_iregs |= 1 << (ainfo->reg + 1);
1303 arg->opcode = OP_OUTARG_R4;
1306 cfg->flags |= MONO_CFG_HAS_FPOUT;
1310 g_assert_not_reached ();
1314 if (sig->ret && MONO_TYPE_ISSTRUCT (sig->ret)) {
1317 MONO_INST_NEW (cfg, vtarg, OP_MOVE);
1318 vtarg->sreg1 = call->vret_var->dreg;
1319 vtarg->dreg = mono_alloc_preg (cfg);
1320 MONO_ADD_INS (cfg->cbb, vtarg);
1322 mono_call_inst_add_outarg_reg (cfg, call, vtarg->dreg, cinfo->ret.reg, FALSE);
1325 call->stack_usage = cinfo->stack_usage;
1331 mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src)
1333 MonoCallInst *call = (MonoCallInst*)ins->inst_p0;
1334 ArgInfo *ainfo = ins->inst_p1;
1335 int ovf_size = ainfo->vtsize;
1336 int doffset = ainfo->offset;
1337 int i, soffset, dreg;
1340 for (i = 0; i < ainfo->size; ++i) {
1341 dreg = mono_alloc_ireg (cfg);
1342 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, src->dreg, soffset);
1343 mono_call_inst_add_outarg_reg (cfg, call, dreg, ainfo->reg + i, FALSE);
1344 soffset += sizeof (gpointer);
1346 //g_print ("vt size: %d at R%d + %d\n", doffset, vt->inst_basereg, vt->inst_offset);
1348 mini_emit_memcpy (cfg, ARMREG_SP, doffset, src->dreg, soffset, ovf_size * sizeof (gpointer), 0);
1352 mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val)
1354 MonoType *ret = mini_type_get_underlying_type (cfg->generic_sharing_context, mono_method_signature (method)->ret);
1357 if (ret->type == MONO_TYPE_I8 || ret->type == MONO_TYPE_U8) {
1360 MONO_INST_NEW (cfg, ins, OP_SETLRET);
1361 ins->sreg1 = val->dreg + 1;
1362 ins->sreg2 = val->dreg + 2;
1363 MONO_ADD_INS (cfg->cbb, ins);
1366 #ifdef MONO_ARCH_SOFT_FLOAT
1367 if (ret->type == MONO_TYPE_R8) {
1370 MONO_INST_NEW (cfg, ins, OP_SETFRET);
1371 ins->dreg = cfg->ret->dreg;
1372 ins->sreg1 = val->dreg;
1373 MONO_ADD_INS (cfg->cbb, ins);
1376 if (ret->type == MONO_TYPE_R4) {
1377 /* Already converted to an int in method_to_ir () */
1378 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
1381 #elif defined(ARM_FPU_VFP)
1382 if (ret->type == MONO_TYPE_R8 || ret->type == MONO_TYPE_R4) {
1385 MONO_INST_NEW (cfg, ins, OP_SETFRET);
1386 ins->dreg = cfg->ret->dreg;
1387 ins->sreg1 = val->dreg;
1388 MONO_ADD_INS (cfg->cbb, ins);
1392 if (ret->type == MONO_TYPE_R4 || ret->type == MONO_TYPE_R8) {
1393 MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, cfg->ret->dreg, val->dreg);
1400 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
1404 mono_arch_is_inst_imm (gint64 imm)
1410 * Allow tracing to work with this interface (with an optional argument)
1414 mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
1418 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1419 ARM_MOV_REG_IMM8 (code, ARMREG_R1, 0); /* NULL ebp for now */
1420 code = mono_arm_emit_load_imm (code, ARMREG_R2, (guint32)func);
1421 code = emit_call_reg (code, ARMREG_R2);
1434 mono_arch_instrument_epilog_full (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments, gboolean preserve_argument_registers)
1437 int save_mode = SAVE_NONE;
1439 MonoMethod *method = cfg->method;
1440 int rtype = mini_type_get_underlying_type (cfg->generic_sharing_context, mono_method_signature (method)->ret)->type;
1441 int save_offset = cfg->param_area;
1445 offset = code - cfg->native_code;
1446 /* we need about 16 instructions */
1447 if (offset > (cfg->code_size - 16 * 4)) {
1448 cfg->code_size *= 2;
1449 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
1450 code = cfg->native_code + offset;
1453 case MONO_TYPE_VOID:
1454 /* special case string .ctor icall */
1455 if (strcmp (".ctor", method->name) && method->klass == mono_defaults.string_class)
1456 save_mode = SAVE_ONE;
1458 save_mode = SAVE_NONE;
1462 save_mode = SAVE_TWO;
1466 save_mode = SAVE_FP;
1468 case MONO_TYPE_VALUETYPE:
1469 save_mode = SAVE_STRUCT;
1472 save_mode = SAVE_ONE;
1476 switch (save_mode) {
1478 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1479 ARM_STR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1480 if (enable_arguments) {
1481 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_R1);
1482 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1486 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1487 if (enable_arguments) {
1488 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1492 /* FIXME: what reg? */
1493 if (enable_arguments) {
1494 /* FIXME: what reg? */
1498 if (enable_arguments) {
1499 /* FIXME: get the actual address */
1500 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1508 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1509 code = mono_arm_emit_load_imm (code, ARMREG_IP, (guint32)func);
1510 code = emit_call_reg (code, ARMREG_IP);
1512 switch (save_mode) {
1514 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1515 ARM_LDR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1518 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1532 * The immediate field for cond branches is big enough for all reasonable methods
1534 #define EMIT_COND_BRANCH_FLAGS(ins,condcode) \
1535 if (0 && ins->inst_true_bb->native_offset) { \
1536 ARM_B_COND (code, (condcode), (code - cfg->native_code + ins->inst_true_bb->native_offset) & 0xffffff); \
1538 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_true_bb); \
1539 ARM_B_COND (code, (condcode), 0); \
1542 #define EMIT_COND_BRANCH(ins,cond) EMIT_COND_BRANCH_FLAGS(ins, branch_cc_table [(cond)])
1544 /* emit an exception if condition is fail
1546 * We assign the extra code used to throw the implicit exceptions
1547 * to cfg->bb_exit as far as the big branch handling is concerned
1549 #define EMIT_COND_SYSTEM_EXCEPTION_FLAGS(condcode,exc_name) \
1551 mono_add_patch_info (cfg, code - cfg->native_code, \
1552 MONO_PATCH_INFO_EXC, exc_name); \
1553 ARM_BL_COND (code, (condcode), 0); \
1556 #define EMIT_COND_SYSTEM_EXCEPTION(cond,exc_name) EMIT_COND_SYSTEM_EXCEPTION_FLAGS(branch_cc_table [(cond)], (exc_name))
1559 mono_arch_peephole_pass_1 (MonoCompile *cfg, MonoBasicBlock *bb)
1564 mono_arch_peephole_pass_2 (MonoCompile *cfg, MonoBasicBlock *bb)
1566 MonoInst *ins, *n, *last_ins = NULL;
1568 MONO_BB_FOR_EACH_INS_SAFE (bb, n, ins) {
1569 switch (ins->opcode) {
1572 /* Already done by an arch-independent pass */
1574 case OP_LOAD_MEMBASE:
1575 case OP_LOADI4_MEMBASE:
1577 * OP_STORE_MEMBASE_REG reg, offset(basereg)
1578 * OP_LOAD_MEMBASE offset(basereg), reg
1580 if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG
1581 || last_ins->opcode == OP_STORE_MEMBASE_REG) &&
1582 ins->inst_basereg == last_ins->inst_destbasereg &&
1583 ins->inst_offset == last_ins->inst_offset) {
1584 if (ins->dreg == last_ins->sreg1) {
1585 MONO_DELETE_INS (bb, ins);
1588 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1589 ins->opcode = OP_MOVE;
1590 ins->sreg1 = last_ins->sreg1;
1594 * Note: reg1 must be different from the basereg in the second load
1595 * OP_LOAD_MEMBASE offset(basereg), reg1
1596 * OP_LOAD_MEMBASE offset(basereg), reg2
1598 * OP_LOAD_MEMBASE offset(basereg), reg1
1599 * OP_MOVE reg1, reg2
1601 } if (last_ins && (last_ins->opcode == OP_LOADI4_MEMBASE
1602 || last_ins->opcode == OP_LOAD_MEMBASE) &&
1603 ins->inst_basereg != last_ins->dreg &&
1604 ins->inst_basereg == last_ins->inst_basereg &&
1605 ins->inst_offset == last_ins->inst_offset) {
1607 if (ins->dreg == last_ins->dreg) {
1608 MONO_DELETE_INS (bb, ins);
1611 ins->opcode = OP_MOVE;
1612 ins->sreg1 = last_ins->dreg;
1615 //g_assert_not_reached ();
1619 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1620 * OP_LOAD_MEMBASE offset(basereg), reg
1622 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1623 * OP_ICONST reg, imm
1625 } else if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_IMM
1626 || last_ins->opcode == OP_STORE_MEMBASE_IMM) &&
1627 ins->inst_basereg == last_ins->inst_destbasereg &&
1628 ins->inst_offset == last_ins->inst_offset) {
1629 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1630 ins->opcode = OP_ICONST;
1631 ins->inst_c0 = last_ins->inst_imm;
1632 g_assert_not_reached (); // check this rule
1636 case OP_LOADU1_MEMBASE:
1637 case OP_LOADI1_MEMBASE:
1638 if (last_ins && (last_ins->opcode == OP_STOREI1_MEMBASE_REG) &&
1639 ins->inst_basereg == last_ins->inst_destbasereg &&
1640 ins->inst_offset == last_ins->inst_offset) {
1641 ins->opcode = (ins->opcode == OP_LOADI1_MEMBASE) ? OP_ICONV_TO_I1 : OP_ICONV_TO_U1;
1642 ins->sreg1 = last_ins->sreg1;
1645 case OP_LOADU2_MEMBASE:
1646 case OP_LOADI2_MEMBASE:
1647 if (last_ins && (last_ins->opcode == OP_STOREI2_MEMBASE_REG) &&
1648 ins->inst_basereg == last_ins->inst_destbasereg &&
1649 ins->inst_offset == last_ins->inst_offset) {
1650 ins->opcode = (ins->opcode == OP_LOADI2_MEMBASE) ? OP_ICONV_TO_I2 : OP_ICONV_TO_U2;
1651 ins->sreg1 = last_ins->sreg1;
1655 ins->opcode = OP_MOVE;
1659 if (ins->dreg == ins->sreg1) {
1660 MONO_DELETE_INS (bb, ins);
1664 * OP_MOVE sreg, dreg
1665 * OP_MOVE dreg, sreg
1667 if (last_ins && last_ins->opcode == OP_MOVE &&
1668 ins->sreg1 == last_ins->dreg &&
1669 ins->dreg == last_ins->sreg1) {
1670 MONO_DELETE_INS (bb, ins);
1678 bb->last_ins = last_ins;
1682 * the branch_cc_table should maintain the order of these
1696 branch_cc_table [] = {
1710 #define NEW_INS(cfg,dest,op) do { \
1711 MONO_INST_NEW ((cfg), (dest), (op)); \
1712 mono_bblock_insert_before_ins (bb, ins, (dest)); \
1716 map_to_reg_reg_op (int op)
1725 case OP_COMPARE_IMM:
1727 case OP_ICOMPARE_IMM:
1741 case OP_LOAD_MEMBASE:
1742 return OP_LOAD_MEMINDEX;
1743 case OP_LOADI4_MEMBASE:
1744 return OP_LOADI4_MEMINDEX;
1745 case OP_LOADU4_MEMBASE:
1746 return OP_LOADU4_MEMINDEX;
1747 case OP_LOADU1_MEMBASE:
1748 return OP_LOADU1_MEMINDEX;
1749 case OP_LOADI2_MEMBASE:
1750 return OP_LOADI2_MEMINDEX;
1751 case OP_LOADU2_MEMBASE:
1752 return OP_LOADU2_MEMINDEX;
1753 case OP_LOADI1_MEMBASE:
1754 return OP_LOADI1_MEMINDEX;
1755 case OP_STOREI1_MEMBASE_REG:
1756 return OP_STOREI1_MEMINDEX;
1757 case OP_STOREI2_MEMBASE_REG:
1758 return OP_STOREI2_MEMINDEX;
1759 case OP_STOREI4_MEMBASE_REG:
1760 return OP_STOREI4_MEMINDEX;
1761 case OP_STORE_MEMBASE_REG:
1762 return OP_STORE_MEMINDEX;
1763 case OP_STORER4_MEMBASE_REG:
1764 return OP_STORER4_MEMINDEX;
1765 case OP_STORER8_MEMBASE_REG:
1766 return OP_STORER8_MEMINDEX;
1767 case OP_STORE_MEMBASE_IMM:
1768 return OP_STORE_MEMBASE_REG;
1769 case OP_STOREI1_MEMBASE_IMM:
1770 return OP_STOREI1_MEMBASE_REG;
1771 case OP_STOREI2_MEMBASE_IMM:
1772 return OP_STOREI2_MEMBASE_REG;
1773 case OP_STOREI4_MEMBASE_IMM:
1774 return OP_STOREI4_MEMBASE_REG;
1776 g_assert_not_reached ();
1780 * Remove from the instruction list the instructions that can't be
1781 * represented with very simple instructions with no register
1785 mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb)
1787 MonoInst *ins, *temp, *last_ins = NULL;
1788 int rot_amount, imm8, low_imm;
1790 MONO_BB_FOR_EACH_INS (bb, ins) {
1792 switch (ins->opcode) {
1796 case OP_COMPARE_IMM:
1797 case OP_ICOMPARE_IMM:
1811 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount)) < 0) {
1812 NEW_INS (cfg, temp, OP_ICONST);
1813 temp->inst_c0 = ins->inst_imm;
1814 temp->dreg = mono_alloc_ireg (cfg);
1815 ins->sreg2 = temp->dreg;
1816 ins->opcode = mono_op_imm_to_op (ins->opcode);
1818 if (ins->opcode == OP_SBB || ins->opcode == OP_ISBB || ins->opcode == OP_SUBCC)
1824 if (ins->inst_imm == 1) {
1825 ins->opcode = OP_MOVE;
1828 if (ins->inst_imm == 0) {
1829 ins->opcode = OP_ICONST;
1833 imm8 = mono_is_power_of_two (ins->inst_imm);
1835 ins->opcode = OP_SHL_IMM;
1836 ins->inst_imm = imm8;
1839 NEW_INS (cfg, temp, OP_ICONST);
1840 temp->inst_c0 = ins->inst_imm;
1841 temp->dreg = mono_alloc_ireg (cfg);
1842 ins->sreg2 = temp->dreg;
1843 ins->opcode = OP_IMUL;
1849 if (ins->next && (ins->next->opcode == OP_COND_EXC_C || ins->next->opcode == OP_COND_EXC_IC))
1850 /* ARM sets the C flag to 1 if there was _no_ overflow */
1851 ins->next->opcode = OP_COND_EXC_NC;
1853 case OP_LOCALLOC_IMM:
1854 NEW_INS (cfg, temp, OP_ICONST);
1855 temp->inst_c0 = ins->inst_imm;
1856 temp->dreg = mono_alloc_ireg (cfg);
1857 ins->sreg1 = temp->dreg;
1858 ins->opcode = OP_LOCALLOC;
1860 case OP_LOAD_MEMBASE:
1861 case OP_LOADI4_MEMBASE:
1862 case OP_LOADU4_MEMBASE:
1863 case OP_LOADU1_MEMBASE:
1864 /* we can do two things: load the immed in a register
1865 * and use an indexed load, or see if the immed can be
1866 * represented as an ad_imm + a load with a smaller offset
1867 * that fits. We just do the first for now, optimize later.
1869 if (arm_is_imm12 (ins->inst_offset))
1871 NEW_INS (cfg, temp, OP_ICONST);
1872 temp->inst_c0 = ins->inst_offset;
1873 temp->dreg = mono_alloc_ireg (cfg);
1874 ins->sreg2 = temp->dreg;
1875 ins->opcode = map_to_reg_reg_op (ins->opcode);
1877 case OP_LOADI2_MEMBASE:
1878 case OP_LOADU2_MEMBASE:
1879 case OP_LOADI1_MEMBASE:
1880 if (arm_is_imm8 (ins->inst_offset))
1882 NEW_INS (cfg, temp, OP_ICONST);
1883 temp->inst_c0 = ins->inst_offset;
1884 temp->dreg = mono_alloc_ireg (cfg);
1885 ins->sreg2 = temp->dreg;
1886 ins->opcode = map_to_reg_reg_op (ins->opcode);
1888 case OP_LOADR4_MEMBASE:
1889 case OP_LOADR8_MEMBASE:
1890 if (arm_is_fpimm8 (ins->inst_offset))
1892 low_imm = ins->inst_offset & 0x1ff;
1893 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~0x1ff, &rot_amount)) >= 0) {
1894 NEW_INS (cfg, temp, OP_ADD_IMM);
1895 temp->inst_imm = ins->inst_offset & ~0x1ff;
1896 temp->sreg1 = ins->inst_basereg;
1897 temp->dreg = mono_alloc_ireg (cfg);
1898 ins->inst_basereg = temp->dreg;
1899 ins->inst_offset = low_imm;
1902 /* VFP/FPA doesn't have indexed load instructions */
1903 g_assert_not_reached ();
1905 case OP_STORE_MEMBASE_REG:
1906 case OP_STOREI4_MEMBASE_REG:
1907 case OP_STOREI1_MEMBASE_REG:
1908 if (arm_is_imm12 (ins->inst_offset))
1910 NEW_INS (cfg, temp, OP_ICONST);
1911 temp->inst_c0 = ins->inst_offset;
1912 temp->dreg = mono_alloc_ireg (cfg);
1913 ins->sreg2 = temp->dreg;
1914 ins->opcode = map_to_reg_reg_op (ins->opcode);
1916 case OP_STOREI2_MEMBASE_REG:
1917 if (arm_is_imm8 (ins->inst_offset))
1919 NEW_INS (cfg, temp, OP_ICONST);
1920 temp->inst_c0 = ins->inst_offset;
1921 temp->dreg = mono_alloc_ireg (cfg);
1922 ins->sreg2 = temp->dreg;
1923 ins->opcode = map_to_reg_reg_op (ins->opcode);
1925 case OP_STORER4_MEMBASE_REG:
1926 case OP_STORER8_MEMBASE_REG:
1927 if (arm_is_fpimm8 (ins->inst_offset))
1929 low_imm = ins->inst_offset & 0x1ff;
1930 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~ 0x1ff, &rot_amount)) >= 0 && arm_is_fpimm8 (low_imm)) {
1931 NEW_INS (cfg, temp, OP_ADD_IMM);
1932 temp->inst_imm = ins->inst_offset & ~0x1ff;
1933 temp->sreg1 = ins->inst_destbasereg;
1934 temp->dreg = mono_alloc_ireg (cfg);
1935 ins->inst_destbasereg = temp->dreg;
1936 ins->inst_offset = low_imm;
1939 /*g_print ("fail with: %d (%d, %d)\n", ins->inst_offset, ins->inst_offset & ~0x1ff, low_imm);*/
1940 /* VFP/FPA doesn't have indexed store instructions */
1941 g_assert_not_reached ();
1943 case OP_STORE_MEMBASE_IMM:
1944 case OP_STOREI1_MEMBASE_IMM:
1945 case OP_STOREI2_MEMBASE_IMM:
1946 case OP_STOREI4_MEMBASE_IMM:
1947 NEW_INS (cfg, temp, OP_ICONST);
1948 temp->inst_c0 = ins->inst_imm;
1949 temp->dreg = mono_alloc_ireg (cfg);
1950 ins->sreg1 = temp->dreg;
1951 ins->opcode = map_to_reg_reg_op (ins->opcode);
1953 goto loop_start; /* make it handle the possibly big ins->inst_offset */
1955 gboolean swap = FALSE;
1958 /* Some fp compares require swapped operands */
1959 g_assert (ins->next);
1960 switch (ins->next->opcode) {
1962 ins->next->opcode = OP_FBLT;
1966 ins->next->opcode = OP_FBLT_UN;
1970 ins->next->opcode = OP_FBGE;
1974 ins->next->opcode = OP_FBGE_UN;
1982 ins->sreg1 = ins->sreg2;
1991 bb->last_ins = last_ins;
1992 bb->max_vreg = cfg->next_vreg;
1996 mono_arch_decompose_long_opts (MonoCompile *cfg, MonoInst *long_ins)
2001 if (long_ins->opcode == OP_LNEG) {
2003 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ARM_RSBS_IMM, ins->dreg + 1, ins->sreg1 + 1, 0);
2004 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ARM_RSC_IMM, ins->dreg + 2, ins->sreg1 + 2, 0);
2010 emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size, gboolean is_signed)
2012 /* sreg is a float, dreg is an integer reg */
2014 ARM_FIXZ (code, dreg, sreg);
2015 #elif defined(ARM_FPU_VFP)
2017 ARM_TOSIZD (code, ARM_VFP_F0, sreg);
2019 ARM_TOUIZD (code, ARM_VFP_F0, sreg);
2020 ARM_FMRS (code, dreg, ARM_VFP_F0);
2024 ARM_AND_REG_IMM8 (code, dreg, dreg, 0xff);
2025 else if (size == 2) {
2026 ARM_SHL_IMM (code, dreg, dreg, 16);
2027 ARM_SHR_IMM (code, dreg, dreg, 16);
2031 ARM_SHL_IMM (code, dreg, dreg, 24);
2032 ARM_SAR_IMM (code, dreg, dreg, 24);
2033 } else if (size == 2) {
2034 ARM_SHL_IMM (code, dreg, dreg, 16);
2035 ARM_SAR_IMM (code, dreg, dreg, 16);
2043 const guchar *target;
2048 #define is_call_imm(diff) ((gint)(diff) >= -33554432 && (gint)(diff) <= 33554431)
2051 search_thunk_slot (void *data, int csize, int bsize, void *user_data) {
2052 PatchData *pdata = (PatchData*)user_data;
2053 guchar *code = data;
2054 guint32 *thunks = data;
2055 guint32 *endthunks = (guint32*)(code + bsize);
2057 int difflow, diffhigh;
2059 /* always ensure a call from pdata->code can reach to the thunks without further thunks */
2060 difflow = (char*)pdata->code - (char*)thunks;
2061 diffhigh = (char*)pdata->code - (char*)endthunks;
2062 if (!((is_call_imm (thunks) && is_call_imm (endthunks)) || (is_call_imm (difflow) && is_call_imm (diffhigh))))
2066 * The thunk is composed of 3 words:
2067 * load constant from thunks [2] into ARM_IP
2070 * Note that the LR register is already setup
2072 //g_print ("thunk nentries: %d\n", ((char*)endthunks - (char*)thunks)/16);
2073 if ((pdata->found == 2) || (pdata->code >= code && pdata->code <= code + csize)) {
2074 while (thunks < endthunks) {
2075 //g_print ("looking for target: %p at %p (%08x-%08x)\n", pdata->target, thunks, thunks [0], thunks [1]);
2076 if (thunks [2] == (guint32)pdata->target) {
2077 arm_patch (pdata->code, (guchar*)thunks);
2078 mono_arch_flush_icache (pdata->code, 4);
2081 } else if ((thunks [0] == 0) && (thunks [1] == 0) && (thunks [2] == 0)) {
2082 /* found a free slot instead: emit thunk */
2083 /* ARMREG_IP is fine to use since this can't be an IMT call
2086 code = (guchar*)thunks;
2087 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
2088 if (thumb_supported)
2089 ARM_BX (code, ARMREG_IP);
2091 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
2092 thunks [2] = (guint32)pdata->target;
2093 mono_arch_flush_icache ((guchar*)thunks, 12);
2095 arm_patch (pdata->code, (guchar*)thunks);
2096 mono_arch_flush_icache (pdata->code, 4);
2100 /* skip 12 bytes, the size of the thunk */
2104 //g_print ("failed thunk lookup for %p from %p at %p (%d entries)\n", pdata->target, pdata->code, data, count);
2110 handle_thunk (int absolute, guchar *code, const guchar *target) {
2111 MonoDomain *domain = mono_domain_get ();
2115 pdata.target = target;
2116 pdata.absolute = absolute;
2119 mono_domain_lock (domain);
2120 mono_domain_code_foreach (domain, search_thunk_slot, &pdata);
2123 /* this uses the first available slot */
2125 mono_domain_code_foreach (domain, search_thunk_slot, &pdata);
2127 mono_domain_unlock (domain);
2129 if (pdata.found != 1)
2130 g_print ("thunk failed for %p from %p\n", target, code);
2131 g_assert (pdata.found == 1);
2135 arm_patch (guchar *code, const guchar *target)
2137 guint32 *code32 = (void*)code;
2138 guint32 ins = *code32;
2139 guint32 prim = (ins >> 25) & 7;
2140 guint32 tval = GPOINTER_TO_UINT (target);
2142 //g_print ("patching 0x%08x (0x%08x) to point to 0x%08x\n", code, ins, target);
2143 if (prim == 5) { /* 101b */
2144 /* the diff starts 8 bytes from the branch opcode */
2145 gint diff = target - code - 8;
2147 gint tmask = 0xffffffff;
2148 if (tval & 1) { /* entering thumb mode */
2149 diff = target - 1 - code - 8;
2150 g_assert (thumb_supported);
2151 tbits = 0xf << 28; /* bl->blx bit pattern */
2152 g_assert ((ins & (1 << 24))); /* it must be a bl, not b instruction */
2153 /* this low bit of the displacement is moved to bit 24 in the instruction encoding */
2157 tmask = ~(1 << 24); /* clear the link bit */
2158 /*g_print ("blx to thumb: target: %p, code: %p, diff: %d, mask: %x\n", target, code, diff, tmask);*/
2163 if (diff <= 33554431) {
2165 ins = (ins & 0xff000000) | diff;
2167 *code32 = ins | tbits;
2171 /* diff between 0 and -33554432 */
2172 if (diff >= -33554432) {
2174 ins = (ins & 0xff000000) | (diff & ~0xff000000);
2176 *code32 = ins | tbits;
2181 handle_thunk (TRUE, code, target);
2186 * The alternative call sequences looks like this:
2188 * ldr ip, [pc] // loads the address constant
2189 * b 1f // jumps around the constant
2190 * address constant embedded in the code
2195 * There are two cases for patching:
2196 * a) at the end of method emission: in this case code points to the start
2197 * of the call sequence
2198 * b) during runtime patching of the call site: in this case code points
2199 * to the mov pc, ip instruction
2201 * We have to handle also the thunk jump code sequence:
2205 * address constant // execution never reaches here
2207 if ((ins & 0x0ffffff0) == 0x12fff10) {
2208 /* Branch and exchange: the address is constructed in a reg
2209 * We can patch BX when the code sequence is the following:
2210 * ldr ip, [pc, #0] ; 0x8
2217 guint8 *emit = (guint8*)ccode;
2218 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2220 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
2221 ARM_BX (emit, ARMREG_IP);
2223 /*patching from magic trampoline*/
2224 if (ins == ccode [3]) {
2225 g_assert (code32 [-4] == ccode [0]);
2226 g_assert (code32 [-3] == ccode [1]);
2227 g_assert (code32 [-1] == ccode [2]);
2228 code32 [-2] = (guint32)target;
2231 /*patching from JIT*/
2232 if (ins == ccode [0]) {
2233 g_assert (code32 [1] == ccode [1]);
2234 g_assert (code32 [3] == ccode [2]);
2235 g_assert (code32 [4] == ccode [3]);
2236 code32 [2] = (guint32)target;
2239 g_assert_not_reached ();
2240 } else if ((ins & 0x0ffffff0) == 0x12fff30) {
2248 guint8 *emit = (guint8*)ccode;
2249 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2251 ARM_BLX_REG (emit, ARMREG_IP);
2253 g_assert (code32 [-3] == ccode [0]);
2254 g_assert (code32 [-2] == ccode [1]);
2255 g_assert (code32 [0] == ccode [2]);
2257 code32 [-1] = (guint32)target;
2260 guint32 *tmp = ccode;
2261 guint8 *emit = (guint8*)tmp;
2262 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2263 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
2264 ARM_MOV_REG_REG (emit, ARMREG_PC, ARMREG_IP);
2265 ARM_BX (emit, ARMREG_IP);
2266 if (ins == ccode [2]) {
2267 g_assert_not_reached (); // should be -2 ...
2268 code32 [-1] = (guint32)target;
2271 if (ins == ccode [0]) {
2272 /* handles both thunk jump code and the far call sequence */
2273 code32 [2] = (guint32)target;
2276 g_assert_not_reached ();
2278 // g_print ("patched with 0x%08x\n", ins);
2282 * Return the >= 0 uimm8 value if val can be represented with a byte + rotation
2283 * (with the rotation amount in *rot_amount. rot_amount is already adjusted
2284 * to be used with the emit macros.
2285 * Return -1 otherwise.
2288 mono_arm_is_rotated_imm8 (guint32 val, gint *rot_amount)
2291 for (i = 0; i < 31; i+= 2) {
2292 res = (val << (32 - i)) | (val >> i);
2295 *rot_amount = i? 32 - i: 0;
2302 * Emits in code a sequence of instructions that load the value 'val'
2303 * into the dreg register. Uses at most 4 instructions.
2306 mono_arm_emit_load_imm (guint8 *code, int dreg, guint32 val)
2308 int imm8, rot_amount;
2310 ARM_LDR_IMM (code, dreg, ARMREG_PC, 0);
2311 /* skip the constant pool */
2317 if ((imm8 = mono_arm_is_rotated_imm8 (val, &rot_amount)) >= 0) {
2318 ARM_MOV_REG_IMM (code, dreg, imm8, rot_amount);
2319 } else if ((imm8 = mono_arm_is_rotated_imm8 (~val, &rot_amount)) >= 0) {
2320 ARM_MVN_REG_IMM (code, dreg, imm8, rot_amount);
2323 ARM_MOV_REG_IMM8 (code, dreg, (val & 0xFF));
2325 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF00) >> 8, 24);
2327 if (val & 0xFF0000) {
2328 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
2330 if (val & 0xFF000000) {
2331 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2333 } else if (val & 0xFF00) {
2334 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF00) >> 8, 24);
2335 if (val & 0xFF0000) {
2336 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
2338 if (val & 0xFF000000) {
2339 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2341 } else if (val & 0xFF0000) {
2342 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF0000) >> 16, 16);
2343 if (val & 0xFF000000) {
2344 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2347 //g_assert_not_reached ();
2353 * emit_load_volatile_arguments:
2355 * Load volatile arguments from the stack to the original input registers.
2356 * Required before a tail call.
2359 emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code)
2361 MonoMethod *method = cfg->method;
2362 MonoMethodSignature *sig;
2367 /* FIXME: Generate intermediate code instead */
2369 sig = mono_method_signature (method);
2371 /* This is the opposite of the code in emit_prolog */
2375 cinfo = get_call_info (sig, sig->pinvoke);
2377 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
2378 ArgInfo *ainfo = &cinfo->ret;
2379 inst = cfg->vret_addr;
2380 g_assert (arm_is_imm12 (inst->inst_offset));
2381 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2383 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
2384 ArgInfo *ainfo = cinfo->args + i;
2385 inst = cfg->args [pos];
2387 if (cfg->verbose_level > 2)
2388 g_print ("Loading argument %d (type: %d)\n", i, ainfo->regtype);
2389 if (inst->opcode == OP_REGVAR) {
2390 if (ainfo->regtype == RegTypeGeneral)
2391 ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
2392 else if (ainfo->regtype == RegTypeFP) {
2393 g_assert_not_reached ();
2394 } else if (ainfo->regtype == RegTypeBase) {
2398 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
2399 ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset));
2401 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
2402 ARM_LDR_REG_REG (code, inst->dreg, ARMREG_SP, ARMREG_IP);
2406 g_assert_not_reached ();
2408 if (ainfo->regtype == RegTypeGeneral) {
2409 switch (ainfo->size) {
2416 g_assert (arm_is_imm12 (inst->inst_offset));
2417 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2418 g_assert (arm_is_imm12 (inst->inst_offset + 4));
2419 ARM_LDR_IMM (code, ainfo->reg + 1, inst->inst_basereg, inst->inst_offset + 4);
2422 if (arm_is_imm12 (inst->inst_offset)) {
2423 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2425 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
2426 ARM_LDR_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
2430 } else if (ainfo->regtype == RegTypeBaseGen) {
2433 } else if (ainfo->regtype == RegTypeBase) {
2435 } else if (ainfo->regtype == RegTypeFP) {
2436 g_assert_not_reached ();
2437 } else if (ainfo->regtype == RegTypeStructByVal) {
2438 int doffset = inst->inst_offset;
2442 if (mono_class_from_mono_type (inst->inst_vtype))
2443 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), NULL);
2444 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
2445 if (arm_is_imm12 (doffset)) {
2446 ARM_LDR_IMM (code, ainfo->reg + cur_reg, inst->inst_basereg, doffset);
2448 code = mono_arm_emit_load_imm (code, ARMREG_IP, doffset);
2449 ARM_LDR_REG_REG (code, ainfo->reg + cur_reg, inst->inst_basereg, ARMREG_IP);
2451 soffset += sizeof (gpointer);
2452 doffset += sizeof (gpointer);
2457 } else if (ainfo->regtype == RegTypeStructByAddr) {
2474 mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
2479 guint8 *code = cfg->native_code + cfg->code_len;
2480 MonoInst *last_ins = NULL;
2481 guint last_offset = 0;
2483 int imm8, rot_amount;
2485 /* we don't align basic blocks of loops on arm */
2487 if (cfg->verbose_level > 2)
2488 g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset);
2490 cpos = bb->max_offset;
2492 if (cfg->prof_options & MONO_PROFILE_COVERAGE) {
2493 //MonoCoverageInfo *cov = mono_get_coverage_info (cfg->method);
2494 //g_assert (!mono_compile_aot);
2497 // cov->data [bb->dfn].iloffset = bb->cil_code - cfg->cil_code;
2498 /* this is not thread save, but good enough */
2499 /* fixme: howto handle overflows? */
2500 //x86_inc_mem (code, &cov->data [bb->dfn].count);
2503 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) {
2504 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2505 (gpointer)"mono_break");
2506 code = emit_call_seq (cfg, code);
2509 MONO_BB_FOR_EACH_INS (bb, ins) {
2510 offset = code - cfg->native_code;
2512 max_len = ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
2514 if (offset > (cfg->code_size - max_len - 16)) {
2515 cfg->code_size *= 2;
2516 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
2517 code = cfg->native_code + offset;
2519 // if (ins->cil_code)
2520 // g_print ("cil code\n");
2521 mono_debug_record_line_number (cfg, ins, offset);
2523 switch (ins->opcode) {
2524 case OP_MEMORY_BARRIER:
2527 #ifdef HAVE_AEABI_READ_TP
2528 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2529 (gpointer)"__aeabi_read_tp");
2530 code = emit_call_seq (cfg, code);
2532 ARM_LDR_IMM (code, ins->dreg, ARMREG_R0, ins->inst_offset);
2534 g_assert_not_reached ();
2538 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2539 ppc_mulhw (code, ppc_r3, ins->sreg1, ins->sreg2);
2542 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2543 ppc_mulhwu (code, ppc_r3, ins->sreg1, ins->sreg2);
2545 case OP_STOREI1_MEMBASE_IMM:
2546 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFF);
2547 g_assert (arm_is_imm12 (ins->inst_offset));
2548 ARM_STRB_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2550 case OP_STOREI2_MEMBASE_IMM:
2551 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFFFF);
2552 g_assert (arm_is_imm8 (ins->inst_offset));
2553 ARM_STRH_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2555 case OP_STORE_MEMBASE_IMM:
2556 case OP_STOREI4_MEMBASE_IMM:
2557 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm);
2558 g_assert (arm_is_imm12 (ins->inst_offset));
2559 ARM_STR_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2561 case OP_STOREI1_MEMBASE_REG:
2562 g_assert (arm_is_imm12 (ins->inst_offset));
2563 ARM_STRB_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2565 case OP_STOREI2_MEMBASE_REG:
2566 g_assert (arm_is_imm8 (ins->inst_offset));
2567 ARM_STRH_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2569 case OP_STORE_MEMBASE_REG:
2570 case OP_STOREI4_MEMBASE_REG:
2571 /* this case is special, since it happens for spill code after lowering has been called */
2572 if (arm_is_imm12 (ins->inst_offset)) {
2573 ARM_STR_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2575 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2576 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ARMREG_LR);
2579 case OP_STOREI1_MEMINDEX:
2580 ARM_STRB_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2582 case OP_STOREI2_MEMINDEX:
2583 ARM_STRH_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2585 case OP_STORE_MEMINDEX:
2586 case OP_STOREI4_MEMINDEX:
2587 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2590 g_assert_not_reached ();
2592 case OP_LOAD_MEMINDEX:
2593 case OP_LOADI4_MEMINDEX:
2594 case OP_LOADU4_MEMINDEX:
2595 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2597 case OP_LOADI1_MEMINDEX:
2598 ARM_LDRSB_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2600 case OP_LOADU1_MEMINDEX:
2601 ARM_LDRB_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2603 case OP_LOADI2_MEMINDEX:
2604 ARM_LDRSH_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2606 case OP_LOADU2_MEMINDEX:
2607 ARM_LDRH_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2609 case OP_LOAD_MEMBASE:
2610 case OP_LOADI4_MEMBASE:
2611 case OP_LOADU4_MEMBASE:
2612 /* this case is special, since it happens for spill code after lowering has been called */
2613 if (arm_is_imm12 (ins->inst_offset)) {
2614 ARM_LDR_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2616 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2617 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ARMREG_LR);
2620 case OP_LOADI1_MEMBASE:
2621 g_assert (arm_is_imm8 (ins->inst_offset));
2622 ARM_LDRSB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2624 case OP_LOADU1_MEMBASE:
2625 g_assert (arm_is_imm12 (ins->inst_offset));
2626 ARM_LDRB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2628 case OP_LOADU2_MEMBASE:
2629 g_assert (arm_is_imm8 (ins->inst_offset));
2630 ARM_LDRH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2632 case OP_LOADI2_MEMBASE:
2633 g_assert (arm_is_imm8 (ins->inst_offset));
2634 ARM_LDRSH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2636 case OP_ICONV_TO_I1:
2637 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 24);
2638 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 24);
2640 case OP_ICONV_TO_I2:
2641 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2642 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 16);
2644 case OP_ICONV_TO_U1:
2645 ARM_AND_REG_IMM8 (code, ins->dreg, ins->sreg1, 0xff);
2647 case OP_ICONV_TO_U2:
2648 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2649 ARM_SHR_IMM (code, ins->dreg, ins->dreg, 16);
2653 ARM_CMP_REG_REG (code, ins->sreg1, ins->sreg2);
2655 case OP_COMPARE_IMM:
2656 case OP_ICOMPARE_IMM:
2657 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2658 g_assert (imm8 >= 0);
2659 ARM_CMP_REG_IMM (code, ins->sreg1, imm8, rot_amount);
2663 * gdb does not like encountering the hw breakpoint ins in the debugged code.
2664 * So instead of emitting a trap, we emit a call a C function and place a
2667 //*(int*)code = 0xef9f0001;
2670 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2671 (gpointer)"mono_break");
2672 code = emit_call_seq (cfg, code);
2674 case OP_RELAXED_NOP:
2679 case OP_DUMMY_STORE:
2680 case OP_NOT_REACHED:
2685 ARM_ADDS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2688 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2692 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2695 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2696 g_assert (imm8 >= 0);
2697 ARM_ADDS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2701 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2702 g_assert (imm8 >= 0);
2703 ARM_ADD_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2707 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2708 g_assert (imm8 >= 0);
2709 ARM_ADCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2712 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2713 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2715 case OP_IADD_OVF_UN:
2716 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2717 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2720 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2721 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2723 case OP_ISUB_OVF_UN:
2724 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2725 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2727 case OP_ADD_OVF_CARRY:
2728 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2729 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2731 case OP_ADD_OVF_UN_CARRY:
2732 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2733 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2735 case OP_SUB_OVF_CARRY:
2736 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2737 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2739 case OP_SUB_OVF_UN_CARRY:
2740 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2741 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2745 ARM_SUBS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2748 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2749 g_assert (imm8 >= 0);
2750 ARM_SUBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2753 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2757 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2761 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2762 g_assert (imm8 >= 0);
2763 ARM_SUB_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2767 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2768 g_assert (imm8 >= 0);
2769 ARM_SBCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2771 case OP_ARM_RSBS_IMM:
2772 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2773 g_assert (imm8 >= 0);
2774 ARM_RSBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2776 case OP_ARM_RSC_IMM:
2777 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2778 g_assert (imm8 >= 0);
2779 ARM_RSC_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2782 ARM_AND_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2786 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2787 g_assert (imm8 >= 0);
2788 ARM_AND_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2796 /* crappy ARM arch doesn't have a DIV instruction */
2797 g_assert_not_reached ();
2799 ARM_ORR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2803 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2804 g_assert (imm8 >= 0);
2805 ARM_ORR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2808 ARM_EOR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2812 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2813 g_assert (imm8 >= 0);
2814 ARM_EOR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2817 ARM_SHL_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2822 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2823 else if (ins->dreg != ins->sreg1)
2824 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2827 ARM_SAR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2832 ARM_SAR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2833 else if (ins->dreg != ins->sreg1)
2834 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2837 case OP_ISHR_UN_IMM:
2839 ARM_SHR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2840 else if (ins->dreg != ins->sreg1)
2841 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2844 ARM_SHR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2847 ARM_MVN_REG_REG (code, ins->dreg, ins->sreg1);
2850 ARM_RSB_REG_IMM8 (code, ins->dreg, ins->sreg1, 0);
2853 if (ins->dreg == ins->sreg2)
2854 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2856 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg2, ins->sreg1);
2859 g_assert_not_reached ();
2862 /* FIXME: handle ovf/ sreg2 != dreg */
2863 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2864 /* FIXME: MUL doesn't set the C/O flags on ARM */
2866 case OP_IMUL_OVF_UN:
2867 /* FIXME: handle ovf/ sreg2 != dreg */
2868 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2869 /* FIXME: MUL doesn't set the C/O flags on ARM */
2872 code = mono_arm_emit_load_imm (code, ins->dreg, ins->inst_c0);
2875 /* Load the GOT offset */
2876 mono_add_patch_info (cfg, offset, (MonoJumpInfoType)ins->inst_i1, ins->inst_p0);
2877 ARM_LDR_IMM (code, ins->dreg, ARMREG_PC, 0);
2879 *(gpointer*)code = NULL;
2881 /* Load the value from the GOT */
2882 ARM_LDR_REG_REG (code, ins->dreg, ARMREG_PC, ins->dreg);
2884 case OP_ICONV_TO_I4:
2885 case OP_ICONV_TO_U4:
2887 if (ins->dreg != ins->sreg1)
2888 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2891 int saved = ins->sreg2;
2892 if (ins->sreg2 == ARM_LSW_REG) {
2893 ARM_MOV_REG_REG (code, ARMREG_LR, ins->sreg2);
2896 if (ins->sreg1 != ARM_LSW_REG)
2897 ARM_MOV_REG_REG (code, ARM_LSW_REG, ins->sreg1);
2898 if (saved != ARM_MSW_REG)
2899 ARM_MOV_REG_REG (code, ARM_MSW_REG, saved);
2904 ARM_MVFD (code, ins->dreg, ins->sreg1);
2905 #elif defined(ARM_FPU_VFP)
2906 ARM_CPYD (code, ins->dreg, ins->sreg1);
2909 case OP_FCONV_TO_R4:
2911 ARM_MVFS (code, ins->dreg, ins->sreg1);
2912 #elif defined(ARM_FPU_VFP)
2913 ARM_CVTD (code, ins->dreg, ins->sreg1);
2914 ARM_CVTS (code, ins->dreg, ins->dreg);
2919 * Keep in sync with mono_arch_emit_epilog
2921 g_assert (!cfg->method->save_lmf);
2923 code = emit_load_volatile_arguments (cfg, code);
2925 code = emit_big_add (code, ARMREG_SP, cfg->frame_reg, cfg->stack_usage);
2926 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP)) | ((1 << ARMREG_LR)));
2927 mono_add_patch_info (cfg, (guint8*) code - cfg->native_code, MONO_PATCH_INFO_METHOD_JUMP, ins->inst_p0);
2928 if (cfg->compile_aot) {
2929 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
2931 *(gpointer*)code = NULL;
2933 ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_IP);
2939 /* ensure ins->sreg1 is not NULL */
2940 ARM_LDR_IMM (code, ARMREG_LR, ins->sreg1, 0);
2944 if (ppc_is_imm16 (cfg->sig_cookie + cfg->stack_usage)) {
2945 ppc_addi (code, ppc_r11, cfg->frame_reg, cfg->sig_cookie + cfg->stack_usage);
2947 ppc_load (code, ppc_r11, cfg->sig_cookie + cfg->stack_usage);
2948 ppc_add (code, ppc_r11, cfg->frame_reg, ppc_r11);
2950 ppc_stw (code, ppc_r11, 0, ins->sreg1);
2960 call = (MonoCallInst*)ins;
2961 if (ins->flags & MONO_INST_HAS_METHOD)
2962 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, call->method);
2964 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, call->fptr);
2965 code = emit_call_seq (cfg, code);
2966 code = emit_move_return_value (cfg, ins, code);
2972 case OP_VOIDCALL_REG:
2974 code = emit_call_reg (code, ins->sreg1);
2975 code = emit_move_return_value (cfg, ins, code);
2977 case OP_FCALL_MEMBASE:
2978 case OP_LCALL_MEMBASE:
2979 case OP_VCALL_MEMBASE:
2980 case OP_VCALL2_MEMBASE:
2981 case OP_VOIDCALL_MEMBASE:
2982 case OP_CALL_MEMBASE:
2983 g_assert (arm_is_imm12 (ins->inst_offset));
2984 g_assert (ins->sreg1 != ARMREG_LR);
2985 call = (MonoCallInst*)ins;
2986 if (call->method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
2987 ARM_ADD_REG_IMM8 (code, ARMREG_LR, ARMREG_PC, 4);
2988 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
2990 * We can't embed the method in the code stream in PIC code, or
2992 * Instead, we put it in V5 in code emitted by
2993 * mono_arch_emit_imt_argument (), and embed NULL here to
2994 * signal the IMT thunk that the value is in V5.
2996 if (call->dynamic_imt_arg)
2997 *((gpointer*)code) = NULL;
2999 *((gpointer*)code) = (gpointer)call->method;
3002 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
3003 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
3005 code = emit_move_return_value (cfg, ins, code);
3008 /* keep alignment */
3009 int alloca_waste = cfg->param_area;
3012 /* round the size to 8 bytes */
3013 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->sreg1, 7);
3014 ARM_BIC_REG_IMM8 (code, ins->dreg, ins->dreg, 7);
3016 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->dreg, alloca_waste);
3017 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ins->dreg);
3018 /* memzero the area: dreg holds the size, sp is the pointer */
3019 if (ins->flags & MONO_INST_INIT) {
3020 guint8 *start_loop, *branch_to_cond;
3021 ARM_MOV_REG_IMM8 (code, ARMREG_LR, 0);
3022 branch_to_cond = code;
3025 ARM_STR_REG_REG (code, ARMREG_LR, ARMREG_SP, ins->dreg);
3026 arm_patch (branch_to_cond, code);
3027 /* decrement by 4 and set flags */
3028 ARM_SUBS_REG_IMM8 (code, ins->dreg, ins->dreg, 4);
3029 ARM_B_COND (code, ARMCOND_GE, 0);
3030 arm_patch (code - 4, start_loop);
3032 ARM_ADD_REG_IMM8 (code, ins->dreg, ARMREG_SP, alloca_waste);
3036 if (ins->sreg1 != ARMREG_R0)
3037 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
3038 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3039 (gpointer)"mono_arch_throw_exception");
3040 code = emit_call_seq (cfg, code);
3044 if (ins->sreg1 != ARMREG_R0)
3045 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
3046 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3047 (gpointer)"mono_arch_rethrow_exception");
3048 code = emit_call_seq (cfg, code);
3051 case OP_START_HANDLER: {
3052 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3054 if (arm_is_imm12 (spvar->inst_offset)) {
3055 ARM_STR_IMM (code, ARMREG_LR, spvar->inst_basereg, spvar->inst_offset);
3057 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3058 ARM_STR_REG_REG (code, ARMREG_LR, spvar->inst_basereg, ARMREG_IP);
3062 case OP_ENDFILTER: {
3063 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3065 if (ins->sreg1 != ARMREG_R0)
3066 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
3067 if (arm_is_imm12 (spvar->inst_offset)) {
3068 ARM_LDR_IMM (code, ARMREG_IP, spvar->inst_basereg, spvar->inst_offset);
3070 g_assert (ARMREG_IP != spvar->inst_basereg);
3071 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3072 ARM_LDR_REG_REG (code, ARMREG_IP, spvar->inst_basereg, ARMREG_IP);
3074 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
3077 case OP_ENDFINALLY: {
3078 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3080 if (arm_is_imm12 (spvar->inst_offset)) {
3081 ARM_LDR_IMM (code, ARMREG_IP, spvar->inst_basereg, spvar->inst_offset);
3083 g_assert (ARMREG_IP != spvar->inst_basereg);
3084 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3085 ARM_LDR_REG_REG (code, ARMREG_IP, spvar->inst_basereg, ARMREG_IP);
3087 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
3090 case OP_CALL_HANDLER:
3091 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb);
3095 ins->inst_c0 = code - cfg->native_code;
3098 /*if (ins->inst_target_bb->native_offset) {
3100 //x86_jump_code (code, cfg->native_code + ins->inst_target_bb->native_offset);
3102 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb);
3107 ARM_MOV_REG_REG (code, ARMREG_PC, ins->sreg1);
3111 * In the normal case we have:
3112 * ldr pc, [pc, ins->sreg1 << 2]
3115 * ldr lr, [pc, ins->sreg1 << 2]
3117 * After follows the data.
3118 * FIXME: add aot support.
3120 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_SWITCH, ins->inst_p0);
3121 max_len += 4 * GPOINTER_TO_INT (ins->klass);
3122 if (offset > (cfg->code_size - max_len - 16)) {
3123 cfg->code_size += max_len;
3124 cfg->code_size *= 2;
3125 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
3126 code = cfg->native_code + offset;
3128 ARM_LDR_REG_REG_SHIFT (code, ARMREG_PC, ARMREG_PC, ins->sreg1, ARMSHIFT_LSL, 2);
3130 code += 4 * GPOINTER_TO_INT (ins->klass);
3134 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
3135 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
3139 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3140 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LT);
3144 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3145 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LO);
3149 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3150 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_GT);
3154 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3155 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_HI);
3157 case OP_COND_EXC_EQ:
3158 case OP_COND_EXC_NE_UN:
3159 case OP_COND_EXC_LT:
3160 case OP_COND_EXC_LT_UN:
3161 case OP_COND_EXC_GT:
3162 case OP_COND_EXC_GT_UN:
3163 case OP_COND_EXC_GE:
3164 case OP_COND_EXC_GE_UN:
3165 case OP_COND_EXC_LE:
3166 case OP_COND_EXC_LE_UN:
3167 EMIT_COND_SYSTEM_EXCEPTION (ins->opcode - OP_COND_EXC_EQ, ins->inst_p1);
3169 case OP_COND_EXC_IEQ:
3170 case OP_COND_EXC_INE_UN:
3171 case OP_COND_EXC_ILT:
3172 case OP_COND_EXC_ILT_UN:
3173 case OP_COND_EXC_IGT:
3174 case OP_COND_EXC_IGT_UN:
3175 case OP_COND_EXC_IGE:
3176 case OP_COND_EXC_IGE_UN:
3177 case OP_COND_EXC_ILE:
3178 case OP_COND_EXC_ILE_UN:
3179 EMIT_COND_SYSTEM_EXCEPTION (ins->opcode - OP_COND_EXC_IEQ, ins->inst_p1);
3182 case OP_COND_EXC_IC:
3183 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_CS, ins->inst_p1);
3185 case OP_COND_EXC_OV:
3186 case OP_COND_EXC_IOV:
3187 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VS, ins->inst_p1);
3189 case OP_COND_EXC_NC:
3190 case OP_COND_EXC_INC:
3191 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_CC, ins->inst_p1);
3193 case OP_COND_EXC_NO:
3194 case OP_COND_EXC_INO:
3195 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VC, ins->inst_p1);
3207 EMIT_COND_BRANCH (ins, ins->opcode - OP_IBEQ);
3210 /* floating point opcodes */
3213 if (cfg->compile_aot) {
3214 ARM_LDFD (code, ins->dreg, ARMREG_PC, 0);
3216 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3218 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
3221 /* FIXME: we can optimize the imm load by dealing with part of
3222 * the displacement in LDFD (aligning to 512).
3224 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3225 ARM_LDFD (code, ins->dreg, ARMREG_LR, 0);
3229 if (cfg->compile_aot) {
3230 ARM_LDFS (code, ins->dreg, ARMREG_PC, 0);
3232 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3235 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3236 ARM_LDFS (code, ins->dreg, ARMREG_LR, 0);
3239 case OP_STORER8_MEMBASE_REG:
3240 /* This is generated by the local regalloc pass which runs after the lowering pass */
3241 if (!arm_is_fpimm8 (ins->inst_offset)) {
3242 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3243 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_destbasereg);
3244 ARM_STFD (code, ins->sreg1, ARMREG_LR, 0);
3246 ARM_STFD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3249 case OP_LOADR8_MEMBASE:
3250 /* This is generated by the local regalloc pass which runs after the lowering pass */
3251 if (!arm_is_fpimm8 (ins->inst_offset)) {
3252 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3253 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_basereg);
3254 ARM_LDFD (code, ins->dreg, ARMREG_LR, 0);
3256 ARM_LDFD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3259 case OP_STORER4_MEMBASE_REG:
3260 g_assert (arm_is_fpimm8 (ins->inst_offset));
3261 ARM_STFS (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3263 case OP_LOADR4_MEMBASE:
3264 g_assert (arm_is_fpimm8 (ins->inst_offset));
3265 ARM_LDFS (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3267 case OP_ICONV_TO_R_UN: {
3269 tmpreg = ins->dreg == 0? 1: 0;
3270 ARM_CMP_REG_IMM8 (code, ins->sreg1, 0);
3271 ARM_FLTD (code, ins->dreg, ins->sreg1);
3272 ARM_B_COND (code, ARMCOND_GE, 8);
3273 /* save the temp register */
3274 ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
3275 ARM_STFD (code, tmpreg, ARMREG_SP, 0);
3276 ARM_LDFD (code, tmpreg, ARMREG_PC, 12);
3277 ARM_FPA_ADFD (code, ins->dreg, ins->dreg, tmpreg);
3278 ARM_LDFD (code, tmpreg, ARMREG_SP, 0);
3279 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
3280 /* skip the constant pool */
3283 *(int*)code = 0x41f00000;
3288 * ldfltd ftemp, [pc, #8] 0x41f00000 0x00000000
3289 * adfltd fdest, fdest, ftemp
3293 case OP_ICONV_TO_R4:
3294 ARM_FLTS (code, ins->dreg, ins->sreg1);
3296 case OP_ICONV_TO_R8:
3297 ARM_FLTD (code, ins->dreg, ins->sreg1);
3300 #elif defined(ARM_FPU_VFP)
3303 if (cfg->compile_aot) {
3304 ARM_FLDD (code, ins->dreg, ARMREG_PC, 0);
3306 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3308 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
3311 /* FIXME: we can optimize the imm load by dealing with part of
3312 * the displacement in LDFD (aligning to 512).
3314 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3315 ARM_FLDD (code, ins->dreg, ARMREG_LR, 0);
3319 if (cfg->compile_aot) {
3320 ARM_FLDS (code, ins->dreg, ARMREG_PC, 0);
3322 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3324 ARM_CVTS (code, ins->dreg, ins->dreg);
3326 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3327 ARM_FLDS (code, ins->dreg, ARMREG_LR, 0);
3328 ARM_CVTS (code, ins->dreg, ins->dreg);
3331 case OP_STORER8_MEMBASE_REG:
3332 /* This is generated by the local regalloc pass which runs after the lowering pass */
3333 if (!arm_is_fpimm8 (ins->inst_offset)) {
3334 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3335 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_destbasereg);
3336 ARM_FSTD (code, ins->sreg1, ARMREG_LR, 0);
3338 ARM_FSTD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3341 case OP_LOADR8_MEMBASE:
3342 /* This is generated by the local regalloc pass which runs after the lowering pass */
3343 if (!arm_is_fpimm8 (ins->inst_offset)) {
3344 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3345 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_basereg);
3346 ARM_FLDD (code, ins->dreg, ARMREG_LR, 0);
3348 ARM_FLDD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3351 case OP_STORER4_MEMBASE_REG:
3352 g_assert (arm_is_fpimm8 (ins->inst_offset));
3353 ARM_CVTD (code, ARM_VFP_F0, ins->sreg1);
3354 ARM_FSTS (code, ARM_VFP_F0, ins->inst_destbasereg, ins->inst_offset);
3356 case OP_LOADR4_MEMBASE:
3357 g_assert (arm_is_fpimm8 (ins->inst_offset));
3358 ARM_FLDS (code, ARM_VFP_F0, ins->inst_basereg, ins->inst_offset);
3359 ARM_CVTS (code, ins->dreg, ARM_VFP_F0);
3361 case OP_ICONV_TO_R_UN: {
3362 g_assert_not_reached ();
3365 case OP_ICONV_TO_R4:
3366 ARM_FMSR (code, ARM_VFP_F0, ins->sreg1);
3367 ARM_FSITOS (code, ARM_VFP_F0, ARM_VFP_F0);
3368 ARM_CVTS (code, ins->dreg, ARM_VFP_F0);
3370 case OP_ICONV_TO_R8:
3371 ARM_FMSR (code, ARM_VFP_F0, ins->sreg1);
3372 ARM_FSITOD (code, ins->dreg, ARM_VFP_F0);
3376 if (mono_method_signature (cfg->method)->ret->type == MONO_TYPE_R4) {
3377 ARM_CVTD (code, ARM_VFP_F0, ins->sreg1);
3378 ARM_FMRS (code, ARMREG_R0, ARM_VFP_F0);
3380 ARM_FMRRD (code, ARMREG_R0, ARMREG_R1, ins->sreg1);
3386 case OP_FCONV_TO_I1:
3387 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, TRUE);
3389 case OP_FCONV_TO_U1:
3390 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, FALSE);
3392 case OP_FCONV_TO_I2:
3393 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, TRUE);
3395 case OP_FCONV_TO_U2:
3396 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, FALSE);
3398 case OP_FCONV_TO_I4:
3400 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, TRUE);
3402 case OP_FCONV_TO_U4:
3404 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, FALSE);
3406 case OP_FCONV_TO_I8:
3407 case OP_FCONV_TO_U8:
3408 g_assert_not_reached ();
3409 /* Implemented as helper calls */
3411 case OP_LCONV_TO_R_UN:
3412 g_assert_not_reached ();
3413 /* Implemented as helper calls */
3415 case OP_LCONV_TO_OVF_I:
3416 case OP_LCONV_TO_OVF_I4_2: {
3417 guint32 *high_bit_not_set, *valid_negative, *invalid_negative, *valid_positive;
3419 * Valid ints: 0xffffffff:8000000 to 00000000:0x7f000000
3422 ARM_CMP_REG_IMM8 (code, ins->sreg1, 0);
3423 high_bit_not_set = code;
3424 ARM_B_COND (code, ARMCOND_GE, 0); /*branch if bit 31 of the lower part is not set*/
3426 ARM_CMN_REG_IMM8 (code, ins->sreg2, 1); /*This have the same effect as CMP reg, 0xFFFFFFFF */
3427 valid_negative = code;
3428 ARM_B_COND (code, ARMCOND_EQ, 0); /*branch if upper part == 0xFFFFFFFF (lower part has bit 31 set) */
3429 invalid_negative = code;
3430 ARM_B_COND (code, ARMCOND_AL, 0);
3432 arm_patch (high_bit_not_set, code);
3434 ARM_CMP_REG_IMM8 (code, ins->sreg2, 0);
3435 valid_positive = code;
3436 ARM_B_COND (code, ARMCOND_EQ, 0); /*branch if upper part == 0 (lower part has bit 31 clear)*/
3438 arm_patch (invalid_negative, code);
3439 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_AL, "OverflowException");
3441 arm_patch (valid_negative, code);
3442 arm_patch (valid_positive, code);
3444 if (ins->dreg != ins->sreg1)
3445 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
3450 ARM_FPA_ADFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3453 ARM_FPA_SUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3456 ARM_FPA_MUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3459 ARM_FPA_DVFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3462 ARM_MNFD (code, ins->dreg, ins->sreg1);
3464 #elif defined(ARM_FPU_VFP)
3466 ARM_VFP_ADDD (code, ins->dreg, ins->sreg1, ins->sreg2);
3469 ARM_VFP_SUBD (code, ins->dreg, ins->sreg1, ins->sreg2);
3472 ARM_VFP_MULD (code, ins->dreg, ins->sreg1, ins->sreg2);
3475 ARM_VFP_DIVD (code, ins->dreg, ins->sreg1, ins->sreg2);
3478 ARM_NEGD (code, ins->dreg, ins->sreg1);
3483 g_assert_not_reached ();
3487 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3488 #elif defined(ARM_FPU_VFP)
3489 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3495 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3496 #elif defined(ARM_FPU_VFP)
3497 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3500 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
3501 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
3505 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3506 #elif defined(ARM_FPU_VFP)
3507 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3510 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3511 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3515 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3516 #elif defined(ARM_FPU_VFP)
3517 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3520 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3521 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3522 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
3527 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3528 #elif defined(ARM_FPU_VFP)
3529 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3532 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3533 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3538 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3539 #elif defined(ARM_FPU_VFP)
3540 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3543 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3544 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3545 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
3547 /* ARM FPA flags table:
3548 * N Less than ARMCOND_MI
3549 * Z Equal ARMCOND_EQ
3550 * C Greater Than or Equal ARMCOND_CS
3551 * V Unordered ARMCOND_VS
3554 EMIT_COND_BRANCH (ins, OP_IBEQ - OP_IBEQ);
3557 EMIT_COND_BRANCH (ins, OP_IBNE_UN - OP_IBEQ);
3560 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
3563 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3564 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
3570 g_assert_not_reached ();
3574 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_GE);
3576 /* FPA requires EQ even thou the docs suggests that just CS is enough */
3577 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_EQ);
3578 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_CS);
3582 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3583 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_GE);
3588 if (ins->dreg != ins->sreg1)
3589 ARM_MVFD (code, ins->dreg, ins->sreg1);
3590 #elif defined(ARM_FPU_VFP)
3591 ARM_ABSD (code, ARM_VFP_D1, ins->sreg1);
3592 ARM_FLDD (code, ARM_VFP_D0, ARMREG_PC, 0);
3594 *(guint32*)code = 0xffffffff;
3596 *(guint32*)code = 0x7fefffff;
3598 ARM_CMPD (code, ARM_VFP_D1, ARM_VFP_D0);
3600 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_GT, "ArithmeticException");
3601 ARM_CMPD (code, ins->sreg1, ins->sreg1);
3603 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VS, "ArithmeticException");
3605 ARM_CPYD (code, ins->dreg, ins->sreg1);
3610 g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
3611 g_assert_not_reached ();
3614 if ((cfg->opt & MONO_OPT_BRANCH) && ((code - cfg->native_code - offset) > max_len)) {
3615 g_warning ("wrong maximal instruction length of instruction %s (expected %d, got %d)",
3616 mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset);
3617 g_assert_not_reached ();
3623 last_offset = offset;
3626 cfg->code_len = code - cfg->native_code;
3629 #endif /* DISABLE_JIT */
3631 #ifdef HAVE_AEABI_READ_TP
3632 void __aeabi_read_tp (void);
3636 mono_arch_register_lowlevel_calls (void)
3638 /* The signature doesn't matter */
3639 mono_register_jit_icall (mono_arm_throw_exception, "mono_arm_throw_exception", mono_create_icall_signature ("void"), TRUE);
3640 mono_register_jit_icall (mono_arm_throw_exception_by_token, "mono_arm_throw_exception_by_token", mono_create_icall_signature ("void"), TRUE);
3642 #ifdef HAVE_AEABI_READ_TP
3643 mono_register_jit_icall (__aeabi_read_tp, "__aeabi_read_tp", mono_create_icall_signature ("void"), TRUE);
3647 #define patch_lis_ori(ip,val) do {\
3648 guint16 *__lis_ori = (guint16*)(ip); \
3649 __lis_ori [1] = (((guint32)(val)) >> 16) & 0xffff; \
3650 __lis_ori [3] = ((guint32)(val)) & 0xffff; \
3654 mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, gboolean run_cctors)
3656 MonoJumpInfo *patch_info;
3657 gboolean compile_aot = !run_cctors;
3659 for (patch_info = ji; patch_info; patch_info = patch_info->next) {
3660 unsigned char *ip = patch_info->ip.i + code;
3661 const unsigned char *target;
3663 if (patch_info->type == MONO_PATCH_INFO_SWITCH && !compile_aot) {
3664 gpointer *jt = (gpointer*)(ip + 8);
3666 /* jt is the inlined jump table, 2 instructions after ip
3667 * In the normal case we store the absolute addresses,
3668 * otherwise the displacements.
3670 for (i = 0; i < patch_info->data.table->table_size; i++)
3671 jt [i] = code + (int)patch_info->data.table->table [i];
3674 target = mono_resolve_patch_target (method, domain, code, patch_info, run_cctors);
3677 switch (patch_info->type) {
3678 case MONO_PATCH_INFO_BB:
3679 case MONO_PATCH_INFO_LABEL:
3682 /* No need to patch these */
3687 switch (patch_info->type) {
3688 case MONO_PATCH_INFO_IP:
3689 g_assert_not_reached ();
3690 patch_lis_ori (ip, ip);
3692 case MONO_PATCH_INFO_METHOD_REL:
3693 g_assert_not_reached ();
3694 *((gpointer *)(ip)) = code + patch_info->data.offset;
3696 case MONO_PATCH_INFO_METHODCONST:
3697 case MONO_PATCH_INFO_CLASS:
3698 case MONO_PATCH_INFO_IMAGE:
3699 case MONO_PATCH_INFO_FIELD:
3700 case MONO_PATCH_INFO_VTABLE:
3701 case MONO_PATCH_INFO_IID:
3702 case MONO_PATCH_INFO_SFLDA:
3703 case MONO_PATCH_INFO_LDSTR:
3704 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
3705 case MONO_PATCH_INFO_LDTOKEN:
3706 g_assert_not_reached ();
3707 /* from OP_AOTCONST : lis + ori */
3708 patch_lis_ori (ip, target);
3710 case MONO_PATCH_INFO_R4:
3711 case MONO_PATCH_INFO_R8:
3712 g_assert_not_reached ();
3713 *((gconstpointer *)(ip + 2)) = patch_info->data.target;
3715 case MONO_PATCH_INFO_EXC_NAME:
3716 g_assert_not_reached ();
3717 *((gconstpointer *)(ip + 1)) = patch_info->data.name;
3719 case MONO_PATCH_INFO_NONE:
3720 case MONO_PATCH_INFO_BB_OVF:
3721 case MONO_PATCH_INFO_EXC_OVF:
3722 /* everything is dealt with at epilog output time */
3727 arm_patch (ip, target);
3732 * Stack frame layout:
3734 * ------------------- fp
3735 * MonoLMF structure or saved registers
3736 * -------------------
3738 * -------------------
3740 * -------------------
3741 * optional 8 bytes for tracing
3742 * -------------------
3743 * param area size is cfg->param_area
3744 * ------------------- sp
3747 mono_arch_emit_prolog (MonoCompile *cfg)
3749 MonoMethod *method = cfg->method;
3751 MonoMethodSignature *sig;
3753 int alloc_size, pos, max_offset, i, rot_amount;
3758 int prev_sp_offset, reg_offset;
3760 if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
3763 sig = mono_method_signature (method);
3764 cfg->code_size = 256 + sig->param_count * 20;
3765 code = cfg->native_code = g_malloc (cfg->code_size);
3767 mono_emit_unwind_op_def_cfa (cfg, code, ARMREG_SP, 0);
3769 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
3771 alloc_size = cfg->stack_offset;
3774 if (!method->save_lmf) {
3775 /* We save SP by storing it into IP and saving IP */
3776 ARM_PUSH (code, (cfg->used_int_regs | (1 << ARMREG_IP) | (1 << ARMREG_LR)));
3777 prev_sp_offset = 8; /* ip and lr */
3778 for (i = 0; i < 16; ++i) {
3779 if (cfg->used_int_regs & (1 << i))
3780 prev_sp_offset += 4;
3782 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset);
3784 for (i = 0; i < 16; ++i) {
3785 if ((cfg->used_int_regs & (1 << i)) || (i == ARMREG_IP) || (i == ARMREG_LR)) {
3786 mono_emit_unwind_op_offset (cfg, code, i, (- prev_sp_offset) + reg_offset);
3791 ARM_PUSH (code, 0x5ff0);
3792 prev_sp_offset = 4 * 10; /* all but r0-r3, sp and pc */
3793 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset);
3795 for (i = 0; i < 16; ++i) {
3796 if ((i > ARMREG_R3) && (i != ARMREG_SP) && (i != ARMREG_PC)) {
3797 mono_emit_unwind_op_offset (cfg, code, i, (- prev_sp_offset) + reg_offset);
3801 pos += sizeof (MonoLMF) - prev_sp_offset;
3805 // align to MONO_ARCH_FRAME_ALIGNMENT bytes
3806 if (alloc_size & (MONO_ARCH_FRAME_ALIGNMENT - 1)) {
3807 alloc_size += MONO_ARCH_FRAME_ALIGNMENT - 1;
3808 alloc_size &= ~(MONO_ARCH_FRAME_ALIGNMENT - 1);
3811 /* the stack used in the pushed regs */
3812 if (prev_sp_offset & 4)
3814 cfg->stack_usage = alloc_size;
3816 if ((i = mono_arm_is_rotated_imm8 (alloc_size, &rot_amount)) >= 0) {
3817 ARM_SUB_REG_IMM (code, ARMREG_SP, ARMREG_SP, i, rot_amount);
3819 code = mono_arm_emit_load_imm (code, ARMREG_IP, alloc_size);
3820 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
3822 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset + alloc_size);
3824 if (cfg->frame_reg != ARMREG_SP) {
3825 ARM_MOV_REG_REG (code, cfg->frame_reg, ARMREG_SP);
3826 mono_emit_unwind_op_def_cfa_reg (cfg, code, cfg->frame_reg);
3828 //g_print ("prev_sp_offset: %d, alloc_size:%d\n", prev_sp_offset, alloc_size);
3829 prev_sp_offset += alloc_size;
3831 /* compute max_offset in order to use short forward jumps
3832 * we could skip do it on arm because the immediate displacement
3833 * for jumps is large enough, it may be useful later for constant pools
3836 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
3837 MonoInst *ins = bb->code;
3838 bb->max_offset = max_offset;
3840 if (cfg->prof_options & MONO_PROFILE_COVERAGE)
3843 MONO_BB_FOR_EACH_INS (bb, ins)
3844 max_offset += ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
3847 /* store runtime generic context */
3848 if (cfg->rgctx_var) {
3849 MonoInst *ins = cfg->rgctx_var;
3851 g_assert (ins->opcode == OP_REGOFFSET);
3853 if (arm_is_imm12 (ins->inst_offset)) {
3854 ARM_STR_IMM (code, MONO_ARCH_RGCTX_REG, ins->inst_basereg, ins->inst_offset);
3856 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3857 ARM_STR_REG_REG (code, MONO_ARCH_RGCTX_REG, ins->inst_basereg, ARMREG_LR);
3861 /* load arguments allocated to register from the stack */
3864 cinfo = get_call_info (sig, sig->pinvoke);
3866 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
3867 ArgInfo *ainfo = &cinfo->ret;
3868 inst = cfg->vret_addr;
3869 g_assert (arm_is_imm12 (inst->inst_offset));
3870 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3872 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
3873 ArgInfo *ainfo = cinfo->args + i;
3874 inst = cfg->args [pos];
3876 if (cfg->verbose_level > 2)
3877 g_print ("Saving argument %d (type: %d)\n", i, ainfo->regtype);
3878 if (inst->opcode == OP_REGVAR) {
3879 if (ainfo->regtype == RegTypeGeneral)
3880 ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
3881 else if (ainfo->regtype == RegTypeFP) {
3882 g_assert_not_reached ();
3883 } else if (ainfo->regtype == RegTypeBase) {
3884 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
3885 ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3887 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3888 ARM_LDR_REG_REG (code, inst->dreg, ARMREG_SP, ARMREG_IP);
3891 g_assert_not_reached ();
3893 if (cfg->verbose_level > 2)
3894 g_print ("Argument %d assigned to register %s\n", pos, mono_arch_regname (inst->dreg));
3896 /* the argument should be put on the stack: FIXME handle size != word */
3897 if (ainfo->regtype == RegTypeGeneral) {
3898 switch (ainfo->size) {
3900 if (arm_is_imm12 (inst->inst_offset))
3901 ARM_STRB_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3903 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3904 ARM_STRB_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3908 if (arm_is_imm8 (inst->inst_offset)) {
3909 ARM_STRH_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3911 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3912 ARM_STRH_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3916 g_assert (arm_is_imm12 (inst->inst_offset));
3917 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3918 g_assert (arm_is_imm12 (inst->inst_offset + 4));
3919 ARM_STR_IMM (code, ainfo->reg + 1, inst->inst_basereg, inst->inst_offset + 4);
3922 if (arm_is_imm12 (inst->inst_offset)) {
3923 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3925 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3926 ARM_STR_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3930 } else if (ainfo->regtype == RegTypeBaseGen) {
3931 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
3932 g_assert (arm_is_imm12 (inst->inst_offset));
3933 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3934 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3935 ARM_STR_IMM (code, ARMREG_R3, inst->inst_basereg, inst->inst_offset);
3936 } else if (ainfo->regtype == RegTypeBase) {
3937 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
3938 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3940 code = mono_arm_emit_load_imm (code, ARMREG_IP, prev_sp_offset + ainfo->offset);
3941 ARM_LDR_REG_REG (code, ARMREG_LR, ARMREG_SP, ARMREG_IP);
3944 switch (ainfo->size) {
3946 if (arm_is_imm8 (inst->inst_offset)) {
3947 ARM_STRB_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3949 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3950 ARM_STRB_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3954 if (arm_is_imm8 (inst->inst_offset)) {
3955 ARM_STRH_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3957 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3958 ARM_STRH_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3962 if (arm_is_imm12 (inst->inst_offset)) {
3963 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3965 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3966 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3968 if (arm_is_imm12 (prev_sp_offset + ainfo->offset + 4)) {
3969 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset + 4));
3971 code = mono_arm_emit_load_imm (code, ARMREG_IP, prev_sp_offset + ainfo->offset + 4);
3972 ARM_LDR_REG_REG (code, ARMREG_LR, ARMREG_SP, ARMREG_IP);
3974 if (arm_is_imm12 (inst->inst_offset + 4)) {
3975 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3977 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset + 4);
3978 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3982 if (arm_is_imm12 (inst->inst_offset)) {
3983 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3985 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3986 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3990 } else if (ainfo->regtype == RegTypeFP) {
3991 g_assert_not_reached ();
3992 } else if (ainfo->regtype == RegTypeStructByVal) {
3993 int doffset = inst->inst_offset;
3997 size = mini_type_stack_size_full (cfg->generic_sharing_context, inst->inst_vtype, NULL, sig->pinvoke);
3998 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
3999 if (arm_is_imm12 (doffset)) {
4000 ARM_STR_IMM (code, ainfo->reg + cur_reg, inst->inst_basereg, doffset);
4002 code = mono_arm_emit_load_imm (code, ARMREG_IP, doffset);
4003 ARM_STR_REG_REG (code, ainfo->reg + cur_reg, inst->inst_basereg, ARMREG_IP);
4005 soffset += sizeof (gpointer);
4006 doffset += sizeof (gpointer);
4008 if (ainfo->vtsize) {
4009 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
4010 //g_print ("emit_memcpy (prev_sp_ofs: %d, ainfo->offset: %d, soffset: %d)\n", prev_sp_offset, ainfo->offset, soffset);
4011 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, doffset, ARMREG_SP, prev_sp_offset + ainfo->offset);
4013 } else if (ainfo->regtype == RegTypeStructByAddr) {
4014 g_assert_not_reached ();
4015 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
4016 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, inst->inst_offset, ainfo->reg, 0);
4018 g_assert_not_reached ();
4023 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
4024 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->domain);
4025 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
4026 (gpointer)"mono_jit_thread_attach");
4027 code = emit_call_seq (cfg, code);
4030 if (method->save_lmf) {
4031 gboolean get_lmf_fast = FALSE;
4033 #ifdef HAVE_AEABI_READ_TP
4034 gint32 lmf_addr_tls_offset = mono_get_lmf_addr_tls_offset ();
4036 if (lmf_addr_tls_offset != -1) {
4037 get_lmf_fast = TRUE;
4039 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
4040 (gpointer)"__aeabi_read_tp");
4041 code = emit_call_seq (cfg, code);
4043 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, lmf_addr_tls_offset);
4044 get_lmf_fast = TRUE;
4047 if (!get_lmf_fast) {
4048 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
4049 (gpointer)"mono_get_lmf_addr");
4050 code = emit_call_seq (cfg, code);
4052 /* we build the MonoLMF structure on the stack - see mini-arm.h */
4053 /* lmf_offset is the offset from the previous stack pointer,
4054 * alloc_size is the total stack space allocated, so the offset
4055 * of MonoLMF from the current stack ptr is alloc_size - lmf_offset.
4056 * The pointer to the struct is put in r1 (new_lmf).
4057 * r2 is used as scratch
4058 * The callee-saved registers are already in the MonoLMF structure
4060 code = emit_big_add (code, ARMREG_R1, ARMREG_SP, alloc_size - lmf_offset);
4061 /* r0 is the result from mono_get_lmf_addr () */
4062 ARM_STR_IMM (code, ARMREG_R0, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
4063 /* new_lmf->previous_lmf = *lmf_addr */
4064 ARM_LDR_IMM (code, ARMREG_R2, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4065 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4066 /* *(lmf_addr) = r1 */
4067 ARM_STR_IMM (code, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4068 /* Skip method (only needed for trampoline LMF frames) */
4069 ARM_STR_IMM (code, ARMREG_SP, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, ebp));
4070 /* save the current IP */
4071 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_PC);
4072 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, eip));
4076 code = mono_arch_instrument_prolog (cfg, mono_trace_enter_method, code, TRUE);
4078 cfg->code_len = code - cfg->native_code;
4079 g_assert (cfg->code_len < cfg->code_size);
4086 mono_arch_emit_epilog (MonoCompile *cfg)
4088 MonoMethod *method = cfg->method;
4089 int pos, i, rot_amount;
4090 int max_epilog_size = 16 + 20*4;
4093 if (cfg->method->save_lmf)
4094 max_epilog_size += 128;
4096 if (mono_jit_trace_calls != NULL)
4097 max_epilog_size += 50;
4099 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE)
4100 max_epilog_size += 50;
4102 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
4103 cfg->code_size *= 2;
4104 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
4105 mono_jit_stats.code_reallocs++;
4109 * Keep in sync with OP_JMP
4111 code = cfg->native_code + cfg->code_len;
4113 if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) {
4114 code = mono_arch_instrument_epilog (cfg, mono_trace_leave_method, code, TRUE);
4118 if (method->save_lmf) {
4120 /* all but r0-r3, sp and pc */
4121 pos += sizeof (MonoLMF) - (4 * 10);
4123 /* r2 contains the pointer to the current LMF */
4124 code = emit_big_add (code, ARMREG_R2, cfg->frame_reg, cfg->stack_usage - lmf_offset);
4125 /* ip = previous_lmf */
4126 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4128 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
4129 /* *(lmf_addr) = previous_lmf */
4130 ARM_STR_IMM (code, ARMREG_IP, ARMREG_LR, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4131 /* FIXME: speedup: there is no actual need to restore the registers if
4132 * we didn't actually change them (idea from Zoltan).
4135 /* point sp at the registers to restore: 10 is 14 -4, because we skip r0-r3 */
4136 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_R2, (sizeof (MonoLMF) - 10 * sizeof (gulong)));
4137 ARM_POP_NWB (code, 0xaff0); /* restore ip to sp and lr to pc */
4139 if ((i = mono_arm_is_rotated_imm8 (cfg->stack_usage, &rot_amount)) >= 0) {
4140 ARM_ADD_REG_IMM (code, ARMREG_SP, cfg->frame_reg, i, rot_amount);
4142 code = mono_arm_emit_load_imm (code, ARMREG_IP, cfg->stack_usage);
4143 ARM_ADD_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
4145 /* FIXME: add v4 thumb interworking support */
4146 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP) | (1 << ARMREG_PC)));
4149 cfg->code_len = code - cfg->native_code;
4151 g_assert (cfg->code_len < cfg->code_size);
4155 /* remove once throw_exception_by_name is eliminated */
4157 exception_id_by_name (const char *name)
4159 if (strcmp (name, "IndexOutOfRangeException") == 0)
4160 return MONO_EXC_INDEX_OUT_OF_RANGE;
4161 if (strcmp (name, "OverflowException") == 0)
4162 return MONO_EXC_OVERFLOW;
4163 if (strcmp (name, "ArithmeticException") == 0)
4164 return MONO_EXC_ARITHMETIC;
4165 if (strcmp (name, "DivideByZeroException") == 0)
4166 return MONO_EXC_DIVIDE_BY_ZERO;
4167 if (strcmp (name, "InvalidCastException") == 0)
4168 return MONO_EXC_INVALID_CAST;
4169 if (strcmp (name, "NullReferenceException") == 0)
4170 return MONO_EXC_NULL_REF;
4171 if (strcmp (name, "ArrayTypeMismatchException") == 0)
4172 return MONO_EXC_ARRAY_TYPE_MISMATCH;
4173 g_error ("Unknown intrinsic exception %s\n", name);
4178 mono_arch_emit_exceptions (MonoCompile *cfg)
4180 MonoJumpInfo *patch_info;
4183 const guint8* exc_throw_pos [MONO_EXC_INTRINS_NUM] = {NULL};
4184 guint8 exc_throw_found [MONO_EXC_INTRINS_NUM] = {0};
4185 int max_epilog_size = 50;
4187 /* count the number of exception infos */
4190 * make sure we have enough space for exceptions
4192 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
4193 if (patch_info->type == MONO_PATCH_INFO_EXC) {
4194 i = exception_id_by_name (patch_info->data.target);
4195 if (!exc_throw_found [i]) {
4196 max_epilog_size += 32;
4197 exc_throw_found [i] = TRUE;
4202 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
4203 cfg->code_size *= 2;
4204 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
4205 mono_jit_stats.code_reallocs++;
4208 code = cfg->native_code + cfg->code_len;
4210 /* add code to raise exceptions */
4211 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
4212 switch (patch_info->type) {
4213 case MONO_PATCH_INFO_EXC: {
4214 MonoClass *exc_class;
4215 unsigned char *ip = patch_info->ip.i + cfg->native_code;
4217 i = exception_id_by_name (patch_info->data.target);
4218 if (exc_throw_pos [i]) {
4219 arm_patch (ip, exc_throw_pos [i]);
4220 patch_info->type = MONO_PATCH_INFO_NONE;
4223 exc_throw_pos [i] = code;
4225 arm_patch (ip, code);
4227 exc_class = mono_class_from_name (mono_defaults.corlib, "System", patch_info->data.name);
4228 g_assert (exc_class);
4230 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_LR);
4231 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
4232 patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD;
4233 patch_info->data.name = "mono_arch_throw_corlib_exception";
4234 patch_info->ip.i = code - cfg->native_code;
4236 *(guint32*)(gpointer)code = exc_class->type_token;
4246 cfg->code_len = code - cfg->native_code;
4248 g_assert (cfg->code_len < cfg->code_size);
4252 static gboolean tls_offset_inited = FALSE;
4255 mono_arch_setup_jit_tls_data (MonoJitTlsData *tls)
4257 if (!tls_offset_inited) {
4258 tls_offset_inited = TRUE;
4260 lmf_tls_offset = mono_get_lmf_tls_offset ();
4261 lmf_addr_tls_offset = mono_get_lmf_addr_tls_offset ();
4266 mono_arch_free_jit_tls_data (MonoJitTlsData *tls)
4271 mono_arch_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
4278 mono_arch_print_tree (MonoInst *tree, int arity)
4284 mono_arch_get_domain_intrinsic (MonoCompile* cfg)
4286 return mono_get_domain_intrinsic (cfg);
4290 mono_arch_get_thread_intrinsic (MonoCompile* cfg)
4292 return mono_get_thread_intrinsic (cfg);
4296 mono_arch_get_patch_offset (guint8 *code)
4303 mono_arch_flush_register_windows (void)
4307 #ifdef MONO_ARCH_HAVE_IMT
4310 mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoInst *imt_arg)
4312 if (cfg->compile_aot) {
4313 int method_reg = mono_alloc_ireg (cfg);
4316 call->dynamic_imt_arg = TRUE;
4319 mono_call_inst_add_outarg_reg (cfg, call, imt_arg->dreg, ARMREG_V5, FALSE);
4321 MONO_INST_NEW (cfg, ins, OP_AOTCONST);
4322 ins->dreg = method_reg;
4323 ins->inst_p0 = call->method;
4324 ins->inst_c1 = MONO_PATCH_INFO_METHODCONST;
4325 MONO_ADD_INS (cfg->cbb, ins);
4327 mono_call_inst_add_outarg_reg (cfg, call, method_reg, ARMREG_V5, FALSE);
4329 } else if (cfg->generic_context) {
4331 /* Always pass in a register for simplicity */
4332 call->dynamic_imt_arg = TRUE;
4334 cfg->uses_rgctx_reg = TRUE;
4337 mono_call_inst_add_outarg_reg (cfg, call, imt_arg->dreg, ARMREG_V5, FALSE);
4340 int method_reg = mono_alloc_preg (cfg);
4342 MONO_INST_NEW (cfg, ins, OP_PCONST);
4343 ins->inst_p0 = call->method;
4344 ins->dreg = method_reg;
4345 MONO_ADD_INS (cfg->cbb, ins);
4347 mono_call_inst_add_outarg_reg (cfg, call, method_reg, ARMREG_V5, FALSE);
4353 mono_arch_find_imt_method (gpointer *regs, guint8 *code)
4355 guint32 *code_ptr = (guint32*)code;
4357 /* The IMT value is stored in the code stream right after the LDC instruction. */
4358 if (!IS_LDR_PC (code_ptr [0])) {
4359 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]);
4360 g_assert (IS_LDR_PC (code_ptr [0]));
4362 if (code_ptr [1] == 0)
4363 /* This is AOTed code, the IMT method is in V5 */
4364 return (MonoMethod*)regs [ARMREG_V5];
4366 return (MonoMethod*) code_ptr [1];
4370 mono_arch_find_this_argument (gpointer *regs, MonoMethod *method, MonoGenericSharingContext *gsctx)
4372 return mono_arch_get_this_arg_from_call (gsctx, mono_method_signature (method), (gssize*)regs, NULL);
4376 mono_arch_find_static_call_vtable (gpointer *regs, guint8 *code)
4378 return (MonoVTable*) regs [MONO_ARCH_RGCTX_REG];
4381 #define ENABLE_WRONG_METHOD_CHECK 0
4382 #define BASE_SIZE (6 * 4)
4383 #define BSEARCH_ENTRY_SIZE (4 * 4)
4384 #define CMP_SIZE (3 * 4)
4385 #define BRANCH_SIZE (1 * 4)
4386 #define CALL_SIZE (2 * 4)
4387 #define WMC_SIZE (5 * 4)
4388 #define DISTANCE(A, B) (((gint32)(B)) - ((gint32)(A)))
4391 arm_emit_value_and_patch_ldr (arminstr_t *code, arminstr_t *target, guint32 value)
4393 guint32 delta = DISTANCE (target, code);
4395 g_assert (delta >= 0 && delta <= 0xFFF);
4396 *target = *target | delta;
4402 mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count,
4403 gpointer fail_tramp)
4405 int size, i, extra_space = 0;
4406 arminstr_t *code, *start, *vtable_target = NULL;
4407 gboolean large_offsets = FALSE;
4408 guint32 **constant_pool_starts;
4411 constant_pool_starts = g_new0 (guint32*, count);
4414 * We might be called with a fail_tramp from the IMT builder code even if
4415 * MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK is not defined.
4417 //g_assert (!fail_tramp);
4419 for (i = 0; i < count; ++i) {
4420 MonoIMTCheckItem *item = imt_entries [i];
4421 if (item->is_equals) {
4422 if (!arm_is_imm12 (DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]))) {
4423 item->chunk_size += 32;
4424 large_offsets = TRUE;
4427 if (item->check_target_idx) {
4428 if (!item->compare_done)
4429 item->chunk_size += CMP_SIZE;
4430 item->chunk_size += BRANCH_SIZE;
4432 #if ENABLE_WRONG_METHOD_CHECK
4433 item->chunk_size += WMC_SIZE;
4436 item->chunk_size += CALL_SIZE;
4438 item->chunk_size += BSEARCH_ENTRY_SIZE;
4439 imt_entries [item->check_target_idx]->compare_done = TRUE;
4441 size += item->chunk_size;
4445 size += 4 * count; /* The ARM_ADD_REG_IMM to pop the stack */
4447 start = code = mono_domain_code_reserve (domain, size);
4450 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);
4451 for (i = 0; i < count; ++i) {
4452 MonoIMTCheckItem *item = imt_entries [i];
4453 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);
4458 ARM_PUSH4 (code, ARMREG_R0, ARMREG_R1, ARMREG_IP, ARMREG_PC);
4460 ARM_PUSH2 (code, ARMREG_R0, ARMREG_R1);
4461 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, -4);
4462 vtable_target = code;
4463 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
4465 /* R0 == 0 means we are called from AOT code. In this case, V5 contains the IMT method */
4466 ARM_CMP_REG_IMM8 (code, ARMREG_R0, 0);
4467 ARM_MOV_REG_REG_COND (code, ARMREG_R0, ARMREG_V5, ARMCOND_EQ);
4469 for (i = 0; i < count; ++i) {
4470 MonoIMTCheckItem *item = imt_entries [i];
4471 arminstr_t *imt_method = NULL, *vtable_offset_ins = NULL;
4472 gint32 vtable_offset;
4474 item->code_target = (guint8*)code;
4476 if (item->is_equals) {
4477 if (item->check_target_idx) {
4478 if (!item->compare_done) {
4480 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4481 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
4483 item->jmp_code = (guint8*)code;
4484 ARM_B_COND (code, ARMCOND_NE, 0);
4486 /*Enable the commented code to assert on wrong method*/
4487 #if ENABLE_WRONG_METHOD_CHECK
4489 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4490 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
4491 ARM_B_COND (code, ARMCOND_NE, 1);
4497 vtable_offset = DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]);
4498 if (!arm_is_imm12 (vtable_offset)) {
4500 * We need to branch to a computed address but we don't have
4501 * a free register to store it, since IP must contain the
4502 * vtable address. So we push the two values to the stack, and
4503 * load them both using LDM.
4505 /* Compute target address */
4506 vtable_offset_ins = code;
4507 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4508 ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_IP, ARMREG_R1);
4509 /* Save it to the fourth slot */
4510 ARM_STR_IMM (code, ARMREG_R1, ARMREG_SP, 3 * sizeof (gpointer));
4511 /* Restore registers and branch */
4512 ARM_POP4 (code, ARMREG_R0, ARMREG_R1, ARMREG_IP, ARMREG_PC);
4514 code = arm_emit_value_and_patch_ldr (code, vtable_offset_ins, vtable_offset);
4516 ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
4518 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 2 * sizeof (gpointer));
4519 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, vtable_offset);
4523 code = arm_emit_value_and_patch_ldr (code, imt_method, (guint32)item->key);
4525 /*must emit after unconditional branch*/
4526 if (vtable_target) {
4527 code = arm_emit_value_and_patch_ldr (code, vtable_target, (guint32)vtable);
4528 item->chunk_size += 4;
4529 vtable_target = NULL;
4532 /*We reserve the space for bsearch IMT values after the first entry with an absolute jump*/
4533 constant_pool_starts [i] = code;
4535 code += extra_space;
4539 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4540 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
4542 item->jmp_code = (guint8*)code;
4543 ARM_B_COND (code, ARMCOND_GE, 0);
4548 for (i = 0; i < count; ++i) {
4549 MonoIMTCheckItem *item = imt_entries [i];
4550 if (item->jmp_code) {
4551 if (item->check_target_idx)
4552 arm_patch (item->jmp_code, imt_entries [item->check_target_idx]->code_target);
4554 if (i > 0 && item->is_equals) {
4556 arminstr_t *space_start = constant_pool_starts [i];
4557 for (j = i - 1; j >= 0 && !imt_entries [j]->is_equals; --j) {
4558 space_start = arm_emit_value_and_patch_ldr (space_start, (arminstr_t*)imt_entries [j]->code_target, (guint32)imt_entries [j]->key);
4565 char *buff = g_strdup_printf ("thunk_for_class_%s_%s_entries_%d", vtable->klass->name_space, vtable->klass->name, count);
4566 mono_disassemble_code (NULL, (guint8*)start, size, buff);
4571 g_free (constant_pool_starts);
4573 mono_arch_flush_icache ((guint8*)start, size);
4574 mono_stats.imt_thunks_size += code - start;
4576 g_assert (DISTANCE (start, code) <= size);
4583 mono_arch_context_get_int_reg (MonoContext *ctx, int reg)
4585 if (reg >= 4 && reg <= 11)
4586 return (gpointer)ctx->regs [reg - 4];
4587 else if (reg == ARMREG_IP)
4588 return (gpointer)ctx->regs [8];
4589 else if (reg == ARMREG_LR)
4590 return (gpointer)ctx->regs [9];
4592 g_assert_not_reached ();