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 calculate_sizes (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: {
829 if (simpletype->type == MONO_TYPE_TYPEDBYREF) {
830 size = sizeof (MonoTypedRef);
832 MonoClass *klass = mono_class_from_mono_type (sig->params [i]);
834 size = mono_class_native_size (klass, NULL);
836 size = mono_class_value_size (klass, NULL);
838 DEBUG(printf ("load %d bytes struct\n",
839 mono_class_native_size (sig->params [i]->data.klass, NULL)));
842 align_size += (sizeof (gpointer) - 1);
843 align_size &= ~(sizeof (gpointer) - 1);
844 nwords = (align_size + sizeof (gpointer) -1 ) / sizeof (gpointer);
845 cinfo->args [n].regtype = RegTypeStructByVal;
846 /* FIXME: align gr and stack_size if needed */
847 if (gr > ARMREG_R3) {
848 cinfo->args [n].size = 0;
849 cinfo->args [n].vtsize = nwords;
851 int rest = ARMREG_R3 - gr + 1;
852 int n_in_regs = rest >= nwords? nwords: rest;
853 cinfo->args [n].size = n_in_regs;
854 cinfo->args [n].vtsize = nwords - n_in_regs;
855 cinfo->args [n].reg = gr;
858 cinfo->args [n].offset = stack_size;
859 /*g_print ("offset for arg %d at %d\n", n, stack_size);*/
860 stack_size += nwords * sizeof (gpointer);
867 cinfo->args [n].size = 8;
868 add_general (&gr, &stack_size, cinfo->args + n, FALSE);
872 g_error ("Can't trampoline 0x%x", sig->params [i]->type);
877 simpletype = mini_type_get_underlying_type (NULL, sig->ret);
878 switch (simpletype->type) {
879 case MONO_TYPE_BOOLEAN:
890 case MONO_TYPE_FNPTR:
891 case MONO_TYPE_CLASS:
892 case MONO_TYPE_OBJECT:
893 case MONO_TYPE_SZARRAY:
894 case MONO_TYPE_ARRAY:
895 case MONO_TYPE_STRING:
896 cinfo->ret.reg = ARMREG_R0;
900 cinfo->ret.reg = ARMREG_R0;
904 cinfo->ret.reg = ARMREG_R0;
905 /* FIXME: cinfo->ret.reg = ???;
906 cinfo->ret.regtype = RegTypeFP;*/
908 case MONO_TYPE_GENERICINST:
909 if (!mono_type_generic_inst_is_valuetype (sig->ret)) {
910 cinfo->ret.reg = ARMREG_R0;
914 case MONO_TYPE_VALUETYPE:
916 case MONO_TYPE_TYPEDBYREF:
920 g_error ("Can't handle as return value 0x%x", sig->ret->type);
924 /* align stack size to 8 */
925 DEBUG (printf (" stack size: %d (%d)\n", (stack_size + 15) & ~15, stack_size));
926 stack_size = (stack_size + 7) & ~7;
928 cinfo->stack_usage = stack_size;
934 * Set var information according to the calling convention. arm version.
935 * The locals var stuff should most likely be split in another method.
938 mono_arch_allocate_vars (MonoCompile *cfg)
940 MonoMethodSignature *sig;
941 MonoMethodHeader *header;
943 int i, offset, size, align, curinst;
944 int frame_reg = ARMREG_FP;
946 /* FIXME: this will change when we use FP as gcc does */
947 cfg->flags |= MONO_CFG_HAS_SPILLUP;
949 /* allow room for the vararg method args: void* and long/double */
950 if (mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method))
951 cfg->param_area = MAX (cfg->param_area, sizeof (gpointer)*8);
953 header = mono_method_get_header (cfg->method);
956 * We use the frame register also for any method that has
957 * exception clauses. This way, when the handlers are called,
958 * the code will reference local variables using the frame reg instead of
959 * the stack pointer: if we had to restore the stack pointer, we'd
960 * corrupt the method frames that are already on the stack (since
961 * filters get called before stack unwinding happens) when the filter
962 * code would call any method (this also applies to finally etc.).
964 if ((cfg->flags & MONO_CFG_HAS_ALLOCA) || header->num_clauses)
965 frame_reg = ARMREG_FP;
966 cfg->frame_reg = frame_reg;
967 if (frame_reg != ARMREG_SP) {
968 cfg->used_int_regs |= 1 << frame_reg;
971 if (cfg->compile_aot || cfg->uses_rgctx_reg)
972 /* V5 is reserved for passing the vtable/rgctx/IMT method */
973 cfg->used_int_regs |= (1 << ARMREG_V5);
975 sig = mono_method_signature (cfg->method);
979 if (!MONO_TYPE_ISSTRUCT (sig->ret)) {
980 /* FIXME: handle long and FP values */
981 switch (mini_type_get_underlying_type (NULL, sig->ret)->type) {
985 cfg->ret->opcode = OP_REGVAR;
986 cfg->ret->inst_c0 = ARMREG_R0;
990 /* local vars are at a positive offset from the stack pointer */
992 * also note that if the function uses alloca, we use FP
993 * to point at the local variables.
995 offset = 0; /* linkage area */
996 /* align the offset to 16 bytes: not sure this is needed here */
998 //offset &= ~(8 - 1);
1000 /* add parameter area size for called functions */
1001 offset += cfg->param_area;
1004 if (cfg->flags & MONO_CFG_HAS_FPOUT)
1007 /* allow room to save the return value */
1008 if (mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method))
1011 /* the MonoLMF structure is stored just below the stack pointer */
1013 if (sig->call_convention == MONO_CALL_VARARG) {
1014 cfg->sig_cookie = 0;
1017 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
1018 inst = cfg->vret_addr;
1019 offset += sizeof(gpointer) - 1;
1020 offset &= ~(sizeof(gpointer) - 1);
1021 inst->inst_offset = offset;
1022 inst->opcode = OP_REGOFFSET;
1023 inst->inst_basereg = frame_reg;
1024 if (G_UNLIKELY (cfg->verbose_level > 1)) {
1025 printf ("vret_addr =");
1026 mono_print_ins (cfg->vret_addr);
1028 offset += sizeof(gpointer);
1029 if (sig->call_convention == MONO_CALL_VARARG)
1030 cfg->sig_cookie += sizeof (gpointer);
1033 curinst = cfg->locals_start;
1034 for (i = curinst; i < cfg->num_varinfo; ++i) {
1035 inst = cfg->varinfo [i];
1036 if ((inst->flags & MONO_INST_IS_DEAD) || inst->opcode == OP_REGVAR)
1039 /* inst->backend.is_pinvoke indicates native sized value types, this is used by the
1040 * pinvoke wrappers when they call functions returning structure */
1041 if (inst->backend.is_pinvoke && MONO_TYPE_ISSTRUCT (inst->inst_vtype) && inst->inst_vtype->type != MONO_TYPE_TYPEDBYREF) {
1043 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), &ualign);
1047 size = mono_type_size (inst->inst_vtype, &align);
1049 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
1050 * since it loads/stores misaligned words, which don't do the right thing.
1052 if (align < 4 && size >= 4)
1054 offset += align - 1;
1055 offset &= ~(align - 1);
1056 inst->inst_offset = offset;
1057 inst->opcode = OP_REGOFFSET;
1058 inst->inst_basereg = frame_reg;
1060 //g_print ("allocating local %d to %d\n", i, inst->inst_offset);
1065 inst = cfg->args [curinst];
1066 if (inst->opcode != OP_REGVAR) {
1067 inst->opcode = OP_REGOFFSET;
1068 inst->inst_basereg = frame_reg;
1069 offset += sizeof (gpointer) - 1;
1070 offset &= ~(sizeof (gpointer) - 1);
1071 inst->inst_offset = offset;
1072 offset += sizeof (gpointer);
1073 if (sig->call_convention == MONO_CALL_VARARG)
1074 cfg->sig_cookie += sizeof (gpointer);
1079 for (i = 0; i < sig->param_count; ++i) {
1080 inst = cfg->args [curinst];
1081 if (inst->opcode != OP_REGVAR) {
1082 inst->opcode = OP_REGOFFSET;
1083 inst->inst_basereg = frame_reg;
1084 size = mono_type_size (sig->params [i], &align);
1085 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
1086 * since it loads/stores misaligned words, which don't do the right thing.
1088 if (align < 4 && size >= 4)
1090 offset += align - 1;
1091 offset &= ~(align - 1);
1092 inst->inst_offset = offset;
1094 if ((sig->call_convention == MONO_CALL_VARARG) && (i < sig->sentinelpos))
1095 cfg->sig_cookie += size;
1100 /* align the offset to 8 bytes */
1105 cfg->stack_offset = offset;
1109 mono_arch_create_vars (MonoCompile *cfg)
1111 MonoMethodSignature *sig;
1113 sig = mono_method_signature (cfg->method);
1115 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
1116 cfg->vret_addr = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_ARG);
1117 if (G_UNLIKELY (cfg->verbose_level > 1)) {
1118 printf ("vret_addr = ");
1119 mono_print_ins (cfg->vret_addr);
1125 mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
1128 MonoMethodSignature *sig;
1132 sig = call->signature;
1133 n = sig->param_count + sig->hasthis;
1135 cinfo = calculate_sizes (sig, sig->pinvoke);
1137 for (i = 0; i < n; ++i) {
1138 ArgInfo *ainfo = cinfo->args + i;
1141 if (i >= sig->hasthis)
1142 t = sig->params [i - sig->hasthis];
1144 t = &mono_defaults.int_class->byval_arg;
1145 t = mini_type_get_underlying_type (NULL, t);
1147 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
1152 in = call->args [i];
1154 switch (ainfo->regtype) {
1155 case RegTypeGeneral:
1156 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1157 MONO_INST_NEW (cfg, ins, OP_MOVE);
1158 ins->dreg = mono_alloc_ireg (cfg);
1159 ins->sreg1 = in->dreg + 1;
1160 MONO_ADD_INS (cfg->cbb, ins);
1161 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1163 MONO_INST_NEW (cfg, ins, OP_MOVE);
1164 ins->dreg = mono_alloc_ireg (cfg);
1165 ins->sreg1 = in->dreg + 2;
1166 MONO_ADD_INS (cfg->cbb, ins);
1167 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg + 1, FALSE);
1168 } else if (!t->byref && ((t->type == MONO_TYPE_R8) || (t->type == MONO_TYPE_R4))) {
1169 #ifndef MONO_ARCH_SOFT_FLOAT
1173 if (ainfo->size == 4) {
1174 #ifdef MONO_ARCH_SOFT_FLOAT
1175 /* mono_emit_call_args () have already done the r8->r4 conversion */
1176 /* The converted value is in an int vreg */
1177 MONO_INST_NEW (cfg, ins, OP_MOVE);
1178 ins->dreg = mono_alloc_ireg (cfg);
1179 ins->sreg1 = in->dreg;
1180 MONO_ADD_INS (cfg->cbb, ins);
1181 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1183 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1184 creg = mono_alloc_ireg (cfg);
1185 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1186 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg, FALSE);
1189 #ifdef MONO_ARCH_SOFT_FLOAT
1190 MONO_INST_NEW (cfg, ins, OP_FGETLOW32);
1191 ins->dreg = mono_alloc_ireg (cfg);
1192 ins->sreg1 = in->dreg;
1193 MONO_ADD_INS (cfg->cbb, ins);
1194 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1196 MONO_INST_NEW (cfg, ins, OP_FGETHIGH32);
1197 ins->dreg = mono_alloc_ireg (cfg);
1198 ins->sreg1 = in->dreg;
1199 MONO_ADD_INS (cfg->cbb, ins);
1200 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg + 1, FALSE);
1202 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1203 creg = mono_alloc_ireg (cfg);
1204 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1205 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg, FALSE);
1206 creg = mono_alloc_ireg (cfg);
1207 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8 + 4));
1208 mono_call_inst_add_outarg_reg (cfg, call, creg, ainfo->reg + 1, FALSE);
1211 cfg->flags |= MONO_CFG_HAS_FPOUT;
1213 MONO_INST_NEW (cfg, ins, OP_MOVE);
1214 ins->dreg = mono_alloc_ireg (cfg);
1215 ins->sreg1 = in->dreg;
1216 MONO_ADD_INS (cfg->cbb, ins);
1218 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
1221 case RegTypeStructByAddr:
1224 /* FIXME: where si the data allocated? */
1225 arg->backend.reg3 = ainfo->reg;
1226 call->used_iregs |= 1 << ainfo->reg;
1227 g_assert_not_reached ();
1230 case RegTypeStructByVal:
1231 MONO_INST_NEW (cfg, ins, OP_OUTARG_VT);
1232 ins->opcode = OP_OUTARG_VT;
1233 ins->sreg1 = in->dreg;
1234 ins->klass = in->klass;
1235 ins->inst_p0 = call;
1236 ins->inst_p1 = mono_mempool_alloc (cfg->mempool, sizeof (ArgInfo));
1237 memcpy (ins->inst_p1, ainfo, sizeof (ArgInfo));
1238 MONO_ADD_INS (cfg->cbb, ins);
1241 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1242 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1243 } else if (!t->byref && ((t->type == MONO_TYPE_R4) || (t->type == MONO_TYPE_R8))) {
1244 if (t->type == MONO_TYPE_R8) {
1245 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1247 #ifdef MONO_ARCH_SOFT_FLOAT
1248 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1250 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1254 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, in->dreg);
1257 case RegTypeBaseGen:
1258 if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
1259 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);
1260 MONO_INST_NEW (cfg, ins, OP_MOVE);
1261 ins->dreg = mono_alloc_ireg (cfg);
1262 ins->sreg1 = G_BYTE_ORDER == G_BIG_ENDIAN ? in->dreg + 2 : in->dreg + 1;
1263 MONO_ADD_INS (cfg->cbb, ins);
1264 mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ARMREG_R3, FALSE);
1265 } else if (!t->byref && (t->type == MONO_TYPE_R8)) {
1268 #ifdef MONO_ARCH_SOFT_FLOAT
1269 g_assert_not_reached ();
1272 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
1273 creg = mono_alloc_ireg (cfg);
1274 mono_call_inst_add_outarg_reg (cfg, call, creg, ARMREG_R3, FALSE);
1275 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
1276 creg = mono_alloc_ireg (cfg);
1277 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 4));
1278 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, creg);
1279 cfg->flags |= MONO_CFG_HAS_FPOUT;
1281 g_assert_not_reached ();
1288 arg->backend.reg3 = ainfo->reg;
1289 /* FP args are passed in int regs */
1290 call->used_iregs |= 1 << ainfo->reg;
1291 if (ainfo->size == 8) {
1292 arg->opcode = OP_OUTARG_R8;
1293 call->used_iregs |= 1 << (ainfo->reg + 1);
1295 arg->opcode = OP_OUTARG_R4;
1298 cfg->flags |= MONO_CFG_HAS_FPOUT;
1302 g_assert_not_reached ();
1306 if (sig->ret && MONO_TYPE_ISSTRUCT (sig->ret)) {
1309 MONO_INST_NEW (cfg, vtarg, OP_MOVE);
1310 vtarg->sreg1 = call->vret_var->dreg;
1311 vtarg->dreg = mono_alloc_preg (cfg);
1312 MONO_ADD_INS (cfg->cbb, vtarg);
1314 mono_call_inst_add_outarg_reg (cfg, call, vtarg->dreg, cinfo->ret.reg, FALSE);
1317 call->stack_usage = cinfo->stack_usage;
1323 mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src)
1325 MonoCallInst *call = (MonoCallInst*)ins->inst_p0;
1326 ArgInfo *ainfo = ins->inst_p1;
1327 int ovf_size = ainfo->vtsize;
1328 int doffset = ainfo->offset;
1329 int i, soffset, dreg;
1332 for (i = 0; i < ainfo->size; ++i) {
1333 dreg = mono_alloc_ireg (cfg);
1334 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, src->dreg, soffset);
1335 mono_call_inst_add_outarg_reg (cfg, call, dreg, ainfo->reg + i, FALSE);
1336 soffset += sizeof (gpointer);
1338 //g_print ("vt size: %d at R%d + %d\n", doffset, vt->inst_basereg, vt->inst_offset);
1340 mini_emit_memcpy (cfg, ARMREG_SP, doffset, src->dreg, soffset, ovf_size * sizeof (gpointer), 0);
1344 mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val)
1346 MonoType *ret = mini_type_get_underlying_type (cfg->generic_sharing_context, mono_method_signature (method)->ret);
1349 if (ret->type == MONO_TYPE_I8 || ret->type == MONO_TYPE_U8) {
1352 MONO_INST_NEW (cfg, ins, OP_SETLRET);
1353 ins->sreg1 = val->dreg + 1;
1354 ins->sreg2 = val->dreg + 2;
1355 MONO_ADD_INS (cfg->cbb, ins);
1358 #ifdef MONO_ARCH_SOFT_FLOAT
1359 if (ret->type == MONO_TYPE_R8) {
1362 MONO_INST_NEW (cfg, ins, OP_SETFRET);
1363 ins->dreg = cfg->ret->dreg;
1364 ins->sreg1 = val->dreg;
1365 MONO_ADD_INS (cfg->cbb, ins);
1368 if (ret->type == MONO_TYPE_R4) {
1369 /* Already converted to an int in method_to_ir () */
1370 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
1373 #elif defined(ARM_FPU_VFP)
1374 if (ret->type == MONO_TYPE_R8 || ret->type == MONO_TYPE_R4) {
1377 MONO_INST_NEW (cfg, ins, OP_SETFRET);
1378 ins->dreg = cfg->ret->dreg;
1379 ins->sreg1 = val->dreg;
1380 MONO_ADD_INS (cfg->cbb, ins);
1384 if (ret->type == MONO_TYPE_R4 || ret->type == MONO_TYPE_R8) {
1385 MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, cfg->ret->dreg, val->dreg);
1392 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
1396 mono_arch_is_inst_imm (gint64 imm)
1402 * Allow tracing to work with this interface (with an optional argument)
1406 mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
1410 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1411 ARM_MOV_REG_IMM8 (code, ARMREG_R1, 0); /* NULL ebp for now */
1412 code = mono_arm_emit_load_imm (code, ARMREG_R2, (guint32)func);
1413 code = emit_call_reg (code, ARMREG_R2);
1426 mono_arch_instrument_epilog_full (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments, gboolean preserve_argument_registers)
1429 int save_mode = SAVE_NONE;
1431 MonoMethod *method = cfg->method;
1432 int rtype = mini_type_get_underlying_type (cfg->generic_sharing_context, mono_method_signature (method)->ret)->type;
1433 int save_offset = cfg->param_area;
1437 offset = code - cfg->native_code;
1438 /* we need about 16 instructions */
1439 if (offset > (cfg->code_size - 16 * 4)) {
1440 cfg->code_size *= 2;
1441 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
1442 code = cfg->native_code + offset;
1445 case MONO_TYPE_VOID:
1446 /* special case string .ctor icall */
1447 if (strcmp (".ctor", method->name) && method->klass == mono_defaults.string_class)
1448 save_mode = SAVE_ONE;
1450 save_mode = SAVE_NONE;
1454 save_mode = SAVE_TWO;
1458 save_mode = SAVE_FP;
1460 case MONO_TYPE_VALUETYPE:
1461 save_mode = SAVE_STRUCT;
1464 save_mode = SAVE_ONE;
1468 switch (save_mode) {
1470 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1471 ARM_STR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1472 if (enable_arguments) {
1473 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_R1);
1474 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1478 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1479 if (enable_arguments) {
1480 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1484 /* FIXME: what reg? */
1485 if (enable_arguments) {
1486 /* FIXME: what reg? */
1490 if (enable_arguments) {
1491 /* FIXME: get the actual address */
1492 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1500 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1501 code = mono_arm_emit_load_imm (code, ARMREG_IP, (guint32)func);
1502 code = emit_call_reg (code, ARMREG_IP);
1504 switch (save_mode) {
1506 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1507 ARM_LDR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1510 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1524 * The immediate field for cond branches is big enough for all reasonable methods
1526 #define EMIT_COND_BRANCH_FLAGS(ins,condcode) \
1527 if (0 && ins->inst_true_bb->native_offset) { \
1528 ARM_B_COND (code, (condcode), (code - cfg->native_code + ins->inst_true_bb->native_offset) & 0xffffff); \
1530 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_true_bb); \
1531 ARM_B_COND (code, (condcode), 0); \
1534 #define EMIT_COND_BRANCH(ins,cond) EMIT_COND_BRANCH_FLAGS(ins, branch_cc_table [(cond)])
1536 /* emit an exception if condition is fail
1538 * We assign the extra code used to throw the implicit exceptions
1539 * to cfg->bb_exit as far as the big branch handling is concerned
1541 #define EMIT_COND_SYSTEM_EXCEPTION_FLAGS(condcode,exc_name) \
1543 mono_add_patch_info (cfg, code - cfg->native_code, \
1544 MONO_PATCH_INFO_EXC, exc_name); \
1545 ARM_BL_COND (code, (condcode), 0); \
1548 #define EMIT_COND_SYSTEM_EXCEPTION(cond,exc_name) EMIT_COND_SYSTEM_EXCEPTION_FLAGS(branch_cc_table [(cond)], (exc_name))
1551 mono_arch_peephole_pass_1 (MonoCompile *cfg, MonoBasicBlock *bb)
1556 mono_arch_peephole_pass_2 (MonoCompile *cfg, MonoBasicBlock *bb)
1558 MonoInst *ins, *n, *last_ins = NULL;
1560 MONO_BB_FOR_EACH_INS_SAFE (bb, n, ins) {
1561 switch (ins->opcode) {
1564 /* Already done by an arch-independent pass */
1566 case OP_LOAD_MEMBASE:
1567 case OP_LOADI4_MEMBASE:
1569 * OP_STORE_MEMBASE_REG reg, offset(basereg)
1570 * OP_LOAD_MEMBASE offset(basereg), reg
1572 if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG
1573 || last_ins->opcode == OP_STORE_MEMBASE_REG) &&
1574 ins->inst_basereg == last_ins->inst_destbasereg &&
1575 ins->inst_offset == last_ins->inst_offset) {
1576 if (ins->dreg == last_ins->sreg1) {
1577 MONO_DELETE_INS (bb, ins);
1580 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1581 ins->opcode = OP_MOVE;
1582 ins->sreg1 = last_ins->sreg1;
1586 * Note: reg1 must be different from the basereg in the second load
1587 * OP_LOAD_MEMBASE offset(basereg), reg1
1588 * OP_LOAD_MEMBASE offset(basereg), reg2
1590 * OP_LOAD_MEMBASE offset(basereg), reg1
1591 * OP_MOVE reg1, reg2
1593 } if (last_ins && (last_ins->opcode == OP_LOADI4_MEMBASE
1594 || last_ins->opcode == OP_LOAD_MEMBASE) &&
1595 ins->inst_basereg != last_ins->dreg &&
1596 ins->inst_basereg == last_ins->inst_basereg &&
1597 ins->inst_offset == last_ins->inst_offset) {
1599 if (ins->dreg == last_ins->dreg) {
1600 MONO_DELETE_INS (bb, ins);
1603 ins->opcode = OP_MOVE;
1604 ins->sreg1 = last_ins->dreg;
1607 //g_assert_not_reached ();
1611 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1612 * OP_LOAD_MEMBASE offset(basereg), reg
1614 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1615 * OP_ICONST reg, imm
1617 } else if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_IMM
1618 || last_ins->opcode == OP_STORE_MEMBASE_IMM) &&
1619 ins->inst_basereg == last_ins->inst_destbasereg &&
1620 ins->inst_offset == last_ins->inst_offset) {
1621 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1622 ins->opcode = OP_ICONST;
1623 ins->inst_c0 = last_ins->inst_imm;
1624 g_assert_not_reached (); // check this rule
1628 case OP_LOADU1_MEMBASE:
1629 case OP_LOADI1_MEMBASE:
1630 if (last_ins && (last_ins->opcode == OP_STOREI1_MEMBASE_REG) &&
1631 ins->inst_basereg == last_ins->inst_destbasereg &&
1632 ins->inst_offset == last_ins->inst_offset) {
1633 ins->opcode = (ins->opcode == OP_LOADI1_MEMBASE) ? OP_ICONV_TO_I1 : OP_ICONV_TO_U1;
1634 ins->sreg1 = last_ins->sreg1;
1637 case OP_LOADU2_MEMBASE:
1638 case OP_LOADI2_MEMBASE:
1639 if (last_ins && (last_ins->opcode == OP_STOREI2_MEMBASE_REG) &&
1640 ins->inst_basereg == last_ins->inst_destbasereg &&
1641 ins->inst_offset == last_ins->inst_offset) {
1642 ins->opcode = (ins->opcode == OP_LOADI2_MEMBASE) ? OP_ICONV_TO_I2 : OP_ICONV_TO_U2;
1643 ins->sreg1 = last_ins->sreg1;
1647 ins->opcode = OP_MOVE;
1651 if (ins->dreg == ins->sreg1) {
1652 MONO_DELETE_INS (bb, ins);
1656 * OP_MOVE sreg, dreg
1657 * OP_MOVE dreg, sreg
1659 if (last_ins && last_ins->opcode == OP_MOVE &&
1660 ins->sreg1 == last_ins->dreg &&
1661 ins->dreg == last_ins->sreg1) {
1662 MONO_DELETE_INS (bb, ins);
1670 bb->last_ins = last_ins;
1674 * the branch_cc_table should maintain the order of these
1688 branch_cc_table [] = {
1702 #define NEW_INS(cfg,dest,op) do { \
1703 MONO_INST_NEW ((cfg), (dest), (op)); \
1704 mono_bblock_insert_before_ins (bb, ins, (dest)); \
1708 map_to_reg_reg_op (int op)
1717 case OP_COMPARE_IMM:
1719 case OP_ICOMPARE_IMM:
1733 case OP_LOAD_MEMBASE:
1734 return OP_LOAD_MEMINDEX;
1735 case OP_LOADI4_MEMBASE:
1736 return OP_LOADI4_MEMINDEX;
1737 case OP_LOADU4_MEMBASE:
1738 return OP_LOADU4_MEMINDEX;
1739 case OP_LOADU1_MEMBASE:
1740 return OP_LOADU1_MEMINDEX;
1741 case OP_LOADI2_MEMBASE:
1742 return OP_LOADI2_MEMINDEX;
1743 case OP_LOADU2_MEMBASE:
1744 return OP_LOADU2_MEMINDEX;
1745 case OP_LOADI1_MEMBASE:
1746 return OP_LOADI1_MEMINDEX;
1747 case OP_STOREI1_MEMBASE_REG:
1748 return OP_STOREI1_MEMINDEX;
1749 case OP_STOREI2_MEMBASE_REG:
1750 return OP_STOREI2_MEMINDEX;
1751 case OP_STOREI4_MEMBASE_REG:
1752 return OP_STOREI4_MEMINDEX;
1753 case OP_STORE_MEMBASE_REG:
1754 return OP_STORE_MEMINDEX;
1755 case OP_STORER4_MEMBASE_REG:
1756 return OP_STORER4_MEMINDEX;
1757 case OP_STORER8_MEMBASE_REG:
1758 return OP_STORER8_MEMINDEX;
1759 case OP_STORE_MEMBASE_IMM:
1760 return OP_STORE_MEMBASE_REG;
1761 case OP_STOREI1_MEMBASE_IMM:
1762 return OP_STOREI1_MEMBASE_REG;
1763 case OP_STOREI2_MEMBASE_IMM:
1764 return OP_STOREI2_MEMBASE_REG;
1765 case OP_STOREI4_MEMBASE_IMM:
1766 return OP_STOREI4_MEMBASE_REG;
1768 g_assert_not_reached ();
1772 * Remove from the instruction list the instructions that can't be
1773 * represented with very simple instructions with no register
1777 mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb)
1779 MonoInst *ins, *temp, *last_ins = NULL;
1780 int rot_amount, imm8, low_imm;
1782 MONO_BB_FOR_EACH_INS (bb, ins) {
1784 switch (ins->opcode) {
1788 case OP_COMPARE_IMM:
1789 case OP_ICOMPARE_IMM:
1803 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount)) < 0) {
1804 NEW_INS (cfg, temp, OP_ICONST);
1805 temp->inst_c0 = ins->inst_imm;
1806 temp->dreg = mono_alloc_ireg (cfg);
1807 ins->sreg2 = temp->dreg;
1808 ins->opcode = mono_op_imm_to_op (ins->opcode);
1810 if (ins->opcode == OP_SBB || ins->opcode == OP_ISBB || ins->opcode == OP_SUBCC)
1816 if (ins->inst_imm == 1) {
1817 ins->opcode = OP_MOVE;
1820 if (ins->inst_imm == 0) {
1821 ins->opcode = OP_ICONST;
1825 imm8 = mono_is_power_of_two (ins->inst_imm);
1827 ins->opcode = OP_SHL_IMM;
1828 ins->inst_imm = imm8;
1831 NEW_INS (cfg, temp, OP_ICONST);
1832 temp->inst_c0 = ins->inst_imm;
1833 temp->dreg = mono_alloc_ireg (cfg);
1834 ins->sreg2 = temp->dreg;
1835 ins->opcode = OP_IMUL;
1841 if (ins->next && (ins->next->opcode == OP_COND_EXC_C || ins->next->opcode == OP_COND_EXC_IC))
1842 /* ARM sets the C flag to 1 if there was _no_ overflow */
1843 ins->next->opcode = OP_COND_EXC_NC;
1845 case OP_LOCALLOC_IMM:
1846 NEW_INS (cfg, temp, OP_ICONST);
1847 temp->inst_c0 = ins->inst_imm;
1848 temp->dreg = mono_alloc_ireg (cfg);
1849 ins->sreg1 = temp->dreg;
1850 ins->opcode = OP_LOCALLOC;
1852 case OP_LOAD_MEMBASE:
1853 case OP_LOADI4_MEMBASE:
1854 case OP_LOADU4_MEMBASE:
1855 case OP_LOADU1_MEMBASE:
1856 /* we can do two things: load the immed in a register
1857 * and use an indexed load, or see if the immed can be
1858 * represented as an ad_imm + a load with a smaller offset
1859 * that fits. We just do the first for now, optimize later.
1861 if (arm_is_imm12 (ins->inst_offset))
1863 NEW_INS (cfg, temp, OP_ICONST);
1864 temp->inst_c0 = ins->inst_offset;
1865 temp->dreg = mono_alloc_ireg (cfg);
1866 ins->sreg2 = temp->dreg;
1867 ins->opcode = map_to_reg_reg_op (ins->opcode);
1869 case OP_LOADI2_MEMBASE:
1870 case OP_LOADU2_MEMBASE:
1871 case OP_LOADI1_MEMBASE:
1872 if (arm_is_imm8 (ins->inst_offset))
1874 NEW_INS (cfg, temp, OP_ICONST);
1875 temp->inst_c0 = ins->inst_offset;
1876 temp->dreg = mono_alloc_ireg (cfg);
1877 ins->sreg2 = temp->dreg;
1878 ins->opcode = map_to_reg_reg_op (ins->opcode);
1880 case OP_LOADR4_MEMBASE:
1881 case OP_LOADR8_MEMBASE:
1882 if (arm_is_fpimm8 (ins->inst_offset))
1884 low_imm = ins->inst_offset & 0x1ff;
1885 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~0x1ff, &rot_amount)) >= 0) {
1886 NEW_INS (cfg, temp, OP_ADD_IMM);
1887 temp->inst_imm = ins->inst_offset & ~0x1ff;
1888 temp->sreg1 = ins->inst_basereg;
1889 temp->dreg = mono_alloc_ireg (cfg);
1890 ins->inst_basereg = temp->dreg;
1891 ins->inst_offset = low_imm;
1894 /* VFP/FPA doesn't have indexed load instructions */
1895 g_assert_not_reached ();
1897 case OP_STORE_MEMBASE_REG:
1898 case OP_STOREI4_MEMBASE_REG:
1899 case OP_STOREI1_MEMBASE_REG:
1900 if (arm_is_imm12 (ins->inst_offset))
1902 NEW_INS (cfg, temp, OP_ICONST);
1903 temp->inst_c0 = ins->inst_offset;
1904 temp->dreg = mono_alloc_ireg (cfg);
1905 ins->sreg2 = temp->dreg;
1906 ins->opcode = map_to_reg_reg_op (ins->opcode);
1908 case OP_STOREI2_MEMBASE_REG:
1909 if (arm_is_imm8 (ins->inst_offset))
1911 NEW_INS (cfg, temp, OP_ICONST);
1912 temp->inst_c0 = ins->inst_offset;
1913 temp->dreg = mono_alloc_ireg (cfg);
1914 ins->sreg2 = temp->dreg;
1915 ins->opcode = map_to_reg_reg_op (ins->opcode);
1917 case OP_STORER4_MEMBASE_REG:
1918 case OP_STORER8_MEMBASE_REG:
1919 if (arm_is_fpimm8 (ins->inst_offset))
1921 low_imm = ins->inst_offset & 0x1ff;
1922 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~ 0x1ff, &rot_amount)) >= 0 && arm_is_fpimm8 (low_imm)) {
1923 NEW_INS (cfg, temp, OP_ADD_IMM);
1924 temp->inst_imm = ins->inst_offset & ~0x1ff;
1925 temp->sreg1 = ins->inst_destbasereg;
1926 temp->dreg = mono_alloc_ireg (cfg);
1927 ins->inst_destbasereg = temp->dreg;
1928 ins->inst_offset = low_imm;
1931 /*g_print ("fail with: %d (%d, %d)\n", ins->inst_offset, ins->inst_offset & ~0x1ff, low_imm);*/
1932 /* VFP/FPA doesn't have indexed store instructions */
1933 g_assert_not_reached ();
1935 case OP_STORE_MEMBASE_IMM:
1936 case OP_STOREI1_MEMBASE_IMM:
1937 case OP_STOREI2_MEMBASE_IMM:
1938 case OP_STOREI4_MEMBASE_IMM:
1939 NEW_INS (cfg, temp, OP_ICONST);
1940 temp->inst_c0 = ins->inst_imm;
1941 temp->dreg = mono_alloc_ireg (cfg);
1942 ins->sreg1 = temp->dreg;
1943 ins->opcode = map_to_reg_reg_op (ins->opcode);
1945 goto loop_start; /* make it handle the possibly big ins->inst_offset */
1947 gboolean swap = FALSE;
1950 /* Some fp compares require swapped operands */
1951 g_assert (ins->next);
1952 switch (ins->next->opcode) {
1954 ins->next->opcode = OP_FBLT;
1958 ins->next->opcode = OP_FBLT_UN;
1962 ins->next->opcode = OP_FBGE;
1966 ins->next->opcode = OP_FBGE_UN;
1974 ins->sreg1 = ins->sreg2;
1983 bb->last_ins = last_ins;
1984 bb->max_vreg = cfg->next_vreg;
1988 mono_arch_decompose_long_opts (MonoCompile *cfg, MonoInst *long_ins)
1993 if (long_ins->opcode == OP_LNEG) {
1995 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ARM_RSBS_IMM, ins->dreg + 1, ins->sreg1 + 1, 0);
1996 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ARM_RSC_IMM, ins->dreg + 2, ins->sreg1 + 2, 0);
2002 emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size, gboolean is_signed)
2004 /* sreg is a float, dreg is an integer reg */
2006 ARM_FIXZ (code, dreg, sreg);
2007 #elif defined(ARM_FPU_VFP)
2009 ARM_TOSIZD (code, ARM_VFP_F0, sreg);
2011 ARM_TOUIZD (code, ARM_VFP_F0, sreg);
2012 ARM_FMRS (code, dreg, ARM_VFP_F0);
2016 ARM_AND_REG_IMM8 (code, dreg, dreg, 0xff);
2017 else if (size == 2) {
2018 ARM_SHL_IMM (code, dreg, dreg, 16);
2019 ARM_SHR_IMM (code, dreg, dreg, 16);
2023 ARM_SHL_IMM (code, dreg, dreg, 24);
2024 ARM_SAR_IMM (code, dreg, dreg, 24);
2025 } else if (size == 2) {
2026 ARM_SHL_IMM (code, dreg, dreg, 16);
2027 ARM_SAR_IMM (code, dreg, dreg, 16);
2035 const guchar *target;
2040 #define is_call_imm(diff) ((gint)(diff) >= -33554432 && (gint)(diff) <= 33554431)
2043 search_thunk_slot (void *data, int csize, int bsize, void *user_data) {
2044 PatchData *pdata = (PatchData*)user_data;
2045 guchar *code = data;
2046 guint32 *thunks = data;
2047 guint32 *endthunks = (guint32*)(code + bsize);
2049 int difflow, diffhigh;
2051 /* always ensure a call from pdata->code can reach to the thunks without further thunks */
2052 difflow = (char*)pdata->code - (char*)thunks;
2053 diffhigh = (char*)pdata->code - (char*)endthunks;
2054 if (!((is_call_imm (thunks) && is_call_imm (endthunks)) || (is_call_imm (difflow) && is_call_imm (diffhigh))))
2058 * The thunk is composed of 3 words:
2059 * load constant from thunks [2] into ARM_IP
2062 * Note that the LR register is already setup
2064 //g_print ("thunk nentries: %d\n", ((char*)endthunks - (char*)thunks)/16);
2065 if ((pdata->found == 2) || (pdata->code >= code && pdata->code <= code + csize)) {
2066 while (thunks < endthunks) {
2067 //g_print ("looking for target: %p at %p (%08x-%08x)\n", pdata->target, thunks, thunks [0], thunks [1]);
2068 if (thunks [2] == (guint32)pdata->target) {
2069 arm_patch (pdata->code, (guchar*)thunks);
2070 mono_arch_flush_icache (pdata->code, 4);
2073 } else if ((thunks [0] == 0) && (thunks [1] == 0) && (thunks [2] == 0)) {
2074 /* found a free slot instead: emit thunk */
2075 /* ARMREG_IP is fine to use since this can't be an IMT call
2078 code = (guchar*)thunks;
2079 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
2080 if (thumb_supported)
2081 ARM_BX (code, ARMREG_IP);
2083 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
2084 thunks [2] = (guint32)pdata->target;
2085 mono_arch_flush_icache ((guchar*)thunks, 12);
2087 arm_patch (pdata->code, (guchar*)thunks);
2088 mono_arch_flush_icache (pdata->code, 4);
2092 /* skip 12 bytes, the size of the thunk */
2096 //g_print ("failed thunk lookup for %p from %p at %p (%d entries)\n", pdata->target, pdata->code, data, count);
2102 handle_thunk (int absolute, guchar *code, const guchar *target) {
2103 MonoDomain *domain = mono_domain_get ();
2107 pdata.target = target;
2108 pdata.absolute = absolute;
2111 mono_domain_lock (domain);
2112 mono_domain_code_foreach (domain, search_thunk_slot, &pdata);
2115 /* this uses the first available slot */
2117 mono_domain_code_foreach (domain, search_thunk_slot, &pdata);
2119 mono_domain_unlock (domain);
2121 if (pdata.found != 1)
2122 g_print ("thunk failed for %p from %p\n", target, code);
2123 g_assert (pdata.found == 1);
2127 arm_patch (guchar *code, const guchar *target)
2129 guint32 *code32 = (void*)code;
2130 guint32 ins = *code32;
2131 guint32 prim = (ins >> 25) & 7;
2132 guint32 tval = GPOINTER_TO_UINT (target);
2134 //g_print ("patching 0x%08x (0x%08x) to point to 0x%08x\n", code, ins, target);
2135 if (prim == 5) { /* 101b */
2136 /* the diff starts 8 bytes from the branch opcode */
2137 gint diff = target - code - 8;
2139 gint tmask = 0xffffffff;
2140 if (tval & 1) { /* entering thumb mode */
2141 diff = target - 1 - code - 8;
2142 g_assert (thumb_supported);
2143 tbits = 0xf << 28; /* bl->blx bit pattern */
2144 g_assert ((ins & (1 << 24))); /* it must be a bl, not b instruction */
2145 /* this low bit of the displacement is moved to bit 24 in the instruction encoding */
2149 tmask = ~(1 << 24); /* clear the link bit */
2150 /*g_print ("blx to thumb: target: %p, code: %p, diff: %d, mask: %x\n", target, code, diff, tmask);*/
2155 if (diff <= 33554431) {
2157 ins = (ins & 0xff000000) | diff;
2159 *code32 = ins | tbits;
2163 /* diff between 0 and -33554432 */
2164 if (diff >= -33554432) {
2166 ins = (ins & 0xff000000) | (diff & ~0xff000000);
2168 *code32 = ins | tbits;
2173 handle_thunk (TRUE, code, target);
2178 * The alternative call sequences looks like this:
2180 * ldr ip, [pc] // loads the address constant
2181 * b 1f // jumps around the constant
2182 * address constant embedded in the code
2187 * There are two cases for patching:
2188 * a) at the end of method emission: in this case code points to the start
2189 * of the call sequence
2190 * b) during runtime patching of the call site: in this case code points
2191 * to the mov pc, ip instruction
2193 * We have to handle also the thunk jump code sequence:
2197 * address constant // execution never reaches here
2199 if ((ins & 0x0ffffff0) == 0x12fff10) {
2200 /* Branch and exchange: the address is constructed in a reg
2201 * We can patch BX when the code sequence is the following:
2202 * ldr ip, [pc, #0] ; 0x8
2209 guint8 *emit = (guint8*)ccode;
2210 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2212 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
2213 ARM_BX (emit, ARMREG_IP);
2215 /*patching from magic trampoline*/
2216 if (ins == ccode [3]) {
2217 g_assert (code32 [-4] == ccode [0]);
2218 g_assert (code32 [-3] == ccode [1]);
2219 g_assert (code32 [-1] == ccode [2]);
2220 code32 [-2] = (guint32)target;
2223 /*patching from JIT*/
2224 if (ins == ccode [0]) {
2225 g_assert (code32 [1] == ccode [1]);
2226 g_assert (code32 [3] == ccode [2]);
2227 g_assert (code32 [4] == ccode [3]);
2228 code32 [2] = (guint32)target;
2231 g_assert_not_reached ();
2232 } else if ((ins & 0x0ffffff0) == 0x12fff30) {
2240 guint8 *emit = (guint8*)ccode;
2241 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2243 ARM_BLX_REG (emit, ARMREG_IP);
2245 g_assert (code32 [-3] == ccode [0]);
2246 g_assert (code32 [-2] == ccode [1]);
2247 g_assert (code32 [0] == ccode [2]);
2249 code32 [-1] = (guint32)target;
2252 guint32 *tmp = ccode;
2253 guint8 *emit = (guint8*)tmp;
2254 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
2255 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
2256 ARM_MOV_REG_REG (emit, ARMREG_PC, ARMREG_IP);
2257 ARM_BX (emit, ARMREG_IP);
2258 if (ins == ccode [2]) {
2259 g_assert_not_reached (); // should be -2 ...
2260 code32 [-1] = (guint32)target;
2263 if (ins == ccode [0]) {
2264 /* handles both thunk jump code and the far call sequence */
2265 code32 [2] = (guint32)target;
2268 g_assert_not_reached ();
2270 // g_print ("patched with 0x%08x\n", ins);
2274 * Return the >= 0 uimm8 value if val can be represented with a byte + rotation
2275 * (with the rotation amount in *rot_amount. rot_amount is already adjusted
2276 * to be used with the emit macros.
2277 * Return -1 otherwise.
2280 mono_arm_is_rotated_imm8 (guint32 val, gint *rot_amount)
2283 for (i = 0; i < 31; i+= 2) {
2284 res = (val << (32 - i)) | (val >> i);
2287 *rot_amount = i? 32 - i: 0;
2294 * Emits in code a sequence of instructions that load the value 'val'
2295 * into the dreg register. Uses at most 4 instructions.
2298 mono_arm_emit_load_imm (guint8 *code, int dreg, guint32 val)
2300 int imm8, rot_amount;
2302 ARM_LDR_IMM (code, dreg, ARMREG_PC, 0);
2303 /* skip the constant pool */
2309 if ((imm8 = mono_arm_is_rotated_imm8 (val, &rot_amount)) >= 0) {
2310 ARM_MOV_REG_IMM (code, dreg, imm8, rot_amount);
2311 } else if ((imm8 = mono_arm_is_rotated_imm8 (~val, &rot_amount)) >= 0) {
2312 ARM_MVN_REG_IMM (code, dreg, imm8, rot_amount);
2315 ARM_MOV_REG_IMM8 (code, dreg, (val & 0xFF));
2317 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF00) >> 8, 24);
2319 if (val & 0xFF0000) {
2320 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
2322 if (val & 0xFF000000) {
2323 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2325 } else if (val & 0xFF00) {
2326 ARM_MOV_REG_IMM (code, 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 & 0xFF0000) {
2334 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF0000) >> 16, 16);
2335 if (val & 0xFF000000) {
2336 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2339 //g_assert_not_reached ();
2345 * emit_load_volatile_arguments:
2347 * Load volatile arguments from the stack to the original input registers.
2348 * Required before a tail call.
2351 emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code)
2353 MonoMethod *method = cfg->method;
2354 MonoMethodSignature *sig;
2359 /* FIXME: Generate intermediate code instead */
2361 sig = mono_method_signature (method);
2363 /* This is the opposite of the code in emit_prolog */
2367 cinfo = calculate_sizes (sig, sig->pinvoke);
2369 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
2370 ArgInfo *ainfo = &cinfo->ret;
2371 inst = cfg->vret_addr;
2372 g_assert (arm_is_imm12 (inst->inst_offset));
2373 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2375 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
2376 ArgInfo *ainfo = cinfo->args + i;
2377 inst = cfg->args [pos];
2379 if (cfg->verbose_level > 2)
2380 g_print ("Loading argument %d (type: %d)\n", i, ainfo->regtype);
2381 if (inst->opcode == OP_REGVAR) {
2382 if (ainfo->regtype == RegTypeGeneral)
2383 ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
2384 else if (ainfo->regtype == RegTypeFP) {
2385 g_assert_not_reached ();
2386 } else if (ainfo->regtype == RegTypeBase) {
2390 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
2391 ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset));
2393 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
2394 ARM_LDR_REG_REG (code, inst->dreg, ARMREG_SP, ARMREG_IP);
2398 g_assert_not_reached ();
2400 if (ainfo->regtype == RegTypeGeneral) {
2401 switch (ainfo->size) {
2408 g_assert (arm_is_imm12 (inst->inst_offset));
2409 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2410 g_assert (arm_is_imm12 (inst->inst_offset + 4));
2411 ARM_LDR_IMM (code, ainfo->reg + 1, inst->inst_basereg, inst->inst_offset + 4);
2414 if (arm_is_imm12 (inst->inst_offset)) {
2415 ARM_LDR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
2417 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
2418 ARM_LDR_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
2422 } else if (ainfo->regtype == RegTypeBaseGen) {
2425 } else if (ainfo->regtype == RegTypeBase) {
2427 } else if (ainfo->regtype == RegTypeFP) {
2428 g_assert_not_reached ();
2429 } else if (ainfo->regtype == RegTypeStructByVal) {
2430 int doffset = inst->inst_offset;
2434 if (mono_class_from_mono_type (inst->inst_vtype))
2435 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), NULL);
2436 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
2437 if (arm_is_imm12 (doffset)) {
2438 ARM_LDR_IMM (code, ainfo->reg + cur_reg, inst->inst_basereg, doffset);
2440 code = mono_arm_emit_load_imm (code, ARMREG_IP, doffset);
2441 ARM_LDR_REG_REG (code, ainfo->reg + cur_reg, inst->inst_basereg, ARMREG_IP);
2443 soffset += sizeof (gpointer);
2444 doffset += sizeof (gpointer);
2449 } else if (ainfo->regtype == RegTypeStructByAddr) {
2466 mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
2471 guint8 *code = cfg->native_code + cfg->code_len;
2472 MonoInst *last_ins = NULL;
2473 guint last_offset = 0;
2475 int imm8, rot_amount;
2477 /* we don't align basic blocks of loops on arm */
2479 if (cfg->verbose_level > 2)
2480 g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset);
2482 cpos = bb->max_offset;
2484 if (cfg->prof_options & MONO_PROFILE_COVERAGE) {
2485 //MonoCoverageInfo *cov = mono_get_coverage_info (cfg->method);
2486 //g_assert (!mono_compile_aot);
2489 // cov->data [bb->dfn].iloffset = bb->cil_code - cfg->cil_code;
2490 /* this is not thread save, but good enough */
2491 /* fixme: howto handle overflows? */
2492 //x86_inc_mem (code, &cov->data [bb->dfn].count);
2495 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) {
2496 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2497 (gpointer)"mono_break");
2498 code = emit_call_seq (cfg, code);
2501 MONO_BB_FOR_EACH_INS (bb, ins) {
2502 offset = code - cfg->native_code;
2504 max_len = ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
2506 if (offset > (cfg->code_size - max_len - 16)) {
2507 cfg->code_size *= 2;
2508 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
2509 code = cfg->native_code + offset;
2511 // if (ins->cil_code)
2512 // g_print ("cil code\n");
2513 mono_debug_record_line_number (cfg, ins, offset);
2515 switch (ins->opcode) {
2516 case OP_MEMORY_BARRIER:
2519 #ifdef HAVE_AEABI_READ_TP
2520 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2521 (gpointer)"__aeabi_read_tp");
2522 code = emit_call_seq (cfg, code);
2524 ARM_LDR_IMM (code, ins->dreg, ARMREG_R0, ins->inst_offset);
2526 g_assert_not_reached ();
2530 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2531 ppc_mulhw (code, ppc_r3, ins->sreg1, ins->sreg2);
2534 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2535 ppc_mulhwu (code, ppc_r3, ins->sreg1, ins->sreg2);
2537 case OP_STOREI1_MEMBASE_IMM:
2538 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFF);
2539 g_assert (arm_is_imm12 (ins->inst_offset));
2540 ARM_STRB_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2542 case OP_STOREI2_MEMBASE_IMM:
2543 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFFFF);
2544 g_assert (arm_is_imm8 (ins->inst_offset));
2545 ARM_STRH_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2547 case OP_STORE_MEMBASE_IMM:
2548 case OP_STOREI4_MEMBASE_IMM:
2549 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm);
2550 g_assert (arm_is_imm12 (ins->inst_offset));
2551 ARM_STR_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2553 case OP_STOREI1_MEMBASE_REG:
2554 g_assert (arm_is_imm12 (ins->inst_offset));
2555 ARM_STRB_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2557 case OP_STOREI2_MEMBASE_REG:
2558 g_assert (arm_is_imm8 (ins->inst_offset));
2559 ARM_STRH_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2561 case OP_STORE_MEMBASE_REG:
2562 case OP_STOREI4_MEMBASE_REG:
2563 /* this case is special, since it happens for spill code after lowering has been called */
2564 if (arm_is_imm12 (ins->inst_offset)) {
2565 ARM_STR_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2567 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2568 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ARMREG_LR);
2571 case OP_STOREI1_MEMINDEX:
2572 ARM_STRB_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2574 case OP_STOREI2_MEMINDEX:
2575 ARM_STRH_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2577 case OP_STORE_MEMINDEX:
2578 case OP_STOREI4_MEMINDEX:
2579 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2582 g_assert_not_reached ();
2584 case OP_LOAD_MEMINDEX:
2585 case OP_LOADI4_MEMINDEX:
2586 case OP_LOADU4_MEMINDEX:
2587 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2589 case OP_LOADI1_MEMINDEX:
2590 ARM_LDRSB_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2592 case OP_LOADU1_MEMINDEX:
2593 ARM_LDRB_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2595 case OP_LOADI2_MEMINDEX:
2596 ARM_LDRSH_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2598 case OP_LOADU2_MEMINDEX:
2599 ARM_LDRH_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2601 case OP_LOAD_MEMBASE:
2602 case OP_LOADI4_MEMBASE:
2603 case OP_LOADU4_MEMBASE:
2604 /* this case is special, since it happens for spill code after lowering has been called */
2605 if (arm_is_imm12 (ins->inst_offset)) {
2606 ARM_LDR_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2608 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2609 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ARMREG_LR);
2612 case OP_LOADI1_MEMBASE:
2613 g_assert (arm_is_imm8 (ins->inst_offset));
2614 ARM_LDRSB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2616 case OP_LOADU1_MEMBASE:
2617 g_assert (arm_is_imm12 (ins->inst_offset));
2618 ARM_LDRB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2620 case OP_LOADU2_MEMBASE:
2621 g_assert (arm_is_imm8 (ins->inst_offset));
2622 ARM_LDRH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2624 case OP_LOADI2_MEMBASE:
2625 g_assert (arm_is_imm8 (ins->inst_offset));
2626 ARM_LDRSH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2628 case OP_ICONV_TO_I1:
2629 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 24);
2630 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 24);
2632 case OP_ICONV_TO_I2:
2633 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2634 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 16);
2636 case OP_ICONV_TO_U1:
2637 ARM_AND_REG_IMM8 (code, ins->dreg, ins->sreg1, 0xff);
2639 case OP_ICONV_TO_U2:
2640 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2641 ARM_SHR_IMM (code, ins->dreg, ins->dreg, 16);
2645 ARM_CMP_REG_REG (code, ins->sreg1, ins->sreg2);
2647 case OP_COMPARE_IMM:
2648 case OP_ICOMPARE_IMM:
2649 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2650 g_assert (imm8 >= 0);
2651 ARM_CMP_REG_IMM (code, ins->sreg1, imm8, rot_amount);
2655 * gdb does not like encountering the hw breakpoint ins in the debugged code.
2656 * So instead of emitting a trap, we emit a call a C function and place a
2659 //*(int*)code = 0xef9f0001;
2662 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2663 (gpointer)"mono_break");
2664 code = emit_call_seq (cfg, code);
2666 case OP_RELAXED_NOP:
2671 case OP_DUMMY_STORE:
2672 case OP_NOT_REACHED:
2677 ARM_ADDS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2680 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2684 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2687 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2688 g_assert (imm8 >= 0);
2689 ARM_ADDS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2693 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2694 g_assert (imm8 >= 0);
2695 ARM_ADD_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2699 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2700 g_assert (imm8 >= 0);
2701 ARM_ADCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2704 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2705 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2707 case OP_IADD_OVF_UN:
2708 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2709 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2712 ARM_SUB_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_ISUB_OVF_UN:
2716 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2717 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2719 case OP_ADD_OVF_CARRY:
2720 ARM_ADCS_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_ADD_OVF_UN_CARRY:
2724 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2725 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2727 case OP_SUB_OVF_CARRY:
2728 ARM_SBCS_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_SUB_OVF_UN_CARRY:
2732 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2733 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2737 ARM_SUBS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2740 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2741 g_assert (imm8 >= 0);
2742 ARM_SUBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2745 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2749 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2753 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2754 g_assert (imm8 >= 0);
2755 ARM_SUB_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2759 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2760 g_assert (imm8 >= 0);
2761 ARM_SBCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2763 case OP_ARM_RSBS_IMM:
2764 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2765 g_assert (imm8 >= 0);
2766 ARM_RSBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2768 case OP_ARM_RSC_IMM:
2769 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2770 g_assert (imm8 >= 0);
2771 ARM_RSC_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2774 ARM_AND_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2778 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2779 g_assert (imm8 >= 0);
2780 ARM_AND_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2788 /* crappy ARM arch doesn't have a DIV instruction */
2789 g_assert_not_reached ();
2791 ARM_ORR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2795 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2796 g_assert (imm8 >= 0);
2797 ARM_ORR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2800 ARM_EOR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2804 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2805 g_assert (imm8 >= 0);
2806 ARM_EOR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2809 ARM_SHL_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2814 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2815 else if (ins->dreg != ins->sreg1)
2816 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2819 ARM_SAR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2824 ARM_SAR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2825 else if (ins->dreg != ins->sreg1)
2826 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2829 case OP_ISHR_UN_IMM:
2831 ARM_SHR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2832 else if (ins->dreg != ins->sreg1)
2833 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2836 ARM_SHR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2839 ARM_MVN_REG_REG (code, ins->dreg, ins->sreg1);
2842 ARM_RSB_REG_IMM8 (code, ins->dreg, ins->sreg1, 0);
2845 if (ins->dreg == ins->sreg2)
2846 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2848 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg2, ins->sreg1);
2851 g_assert_not_reached ();
2854 /* FIXME: handle ovf/ sreg2 != dreg */
2855 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2856 /* FIXME: MUL doesn't set the C/O flags on ARM */
2858 case OP_IMUL_OVF_UN:
2859 /* FIXME: handle ovf/ sreg2 != dreg */
2860 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2861 /* FIXME: MUL doesn't set the C/O flags on ARM */
2864 code = mono_arm_emit_load_imm (code, ins->dreg, ins->inst_c0);
2867 /* Load the GOT offset */
2868 mono_add_patch_info (cfg, offset, (MonoJumpInfoType)ins->inst_i1, ins->inst_p0);
2869 ARM_LDR_IMM (code, ins->dreg, ARMREG_PC, 0);
2871 *(gpointer*)code = NULL;
2873 /* Load the value from the GOT */
2874 ARM_LDR_REG_REG (code, ins->dreg, ARMREG_PC, ins->dreg);
2876 case OP_ICONV_TO_I4:
2877 case OP_ICONV_TO_U4:
2879 if (ins->dreg != ins->sreg1)
2880 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2883 int saved = ins->sreg2;
2884 if (ins->sreg2 == ARM_LSW_REG) {
2885 ARM_MOV_REG_REG (code, ARMREG_LR, ins->sreg2);
2888 if (ins->sreg1 != ARM_LSW_REG)
2889 ARM_MOV_REG_REG (code, ARM_LSW_REG, ins->sreg1);
2890 if (saved != ARM_MSW_REG)
2891 ARM_MOV_REG_REG (code, ARM_MSW_REG, saved);
2896 ARM_MVFD (code, ins->dreg, ins->sreg1);
2897 #elif defined(ARM_FPU_VFP)
2898 ARM_CPYD (code, ins->dreg, ins->sreg1);
2901 case OP_FCONV_TO_R4:
2903 ARM_MVFS (code, ins->dreg, ins->sreg1);
2904 #elif defined(ARM_FPU_VFP)
2905 ARM_CVTD (code, ins->dreg, ins->sreg1);
2906 ARM_CVTS (code, ins->dreg, ins->dreg);
2911 * Keep in sync with mono_arch_emit_epilog
2913 g_assert (!cfg->method->save_lmf);
2915 code = emit_load_volatile_arguments (cfg, code);
2917 code = emit_big_add (code, ARMREG_SP, cfg->frame_reg, cfg->stack_usage);
2918 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP)) | ((1 << ARMREG_LR)));
2919 mono_add_patch_info (cfg, (guint8*) code - cfg->native_code, MONO_PATCH_INFO_METHOD_JUMP, ins->inst_p0);
2920 if (cfg->compile_aot) {
2921 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
2923 *(gpointer*)code = NULL;
2925 ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_IP);
2931 /* ensure ins->sreg1 is not NULL */
2932 ARM_LDR_IMM (code, ARMREG_LR, ins->sreg1, 0);
2936 if (ppc_is_imm16 (cfg->sig_cookie + cfg->stack_usage)) {
2937 ppc_addi (code, ppc_r11, cfg->frame_reg, cfg->sig_cookie + cfg->stack_usage);
2939 ppc_load (code, ppc_r11, cfg->sig_cookie + cfg->stack_usage);
2940 ppc_add (code, ppc_r11, cfg->frame_reg, ppc_r11);
2942 ppc_stw (code, ppc_r11, 0, ins->sreg1);
2952 call = (MonoCallInst*)ins;
2953 if (ins->flags & MONO_INST_HAS_METHOD)
2954 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, call->method);
2956 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, call->fptr);
2957 code = emit_call_seq (cfg, code);
2958 code = emit_move_return_value (cfg, ins, code);
2964 case OP_VOIDCALL_REG:
2966 code = emit_call_reg (code, ins->sreg1);
2967 code = emit_move_return_value (cfg, ins, code);
2969 case OP_FCALL_MEMBASE:
2970 case OP_LCALL_MEMBASE:
2971 case OP_VCALL_MEMBASE:
2972 case OP_VCALL2_MEMBASE:
2973 case OP_VOIDCALL_MEMBASE:
2974 case OP_CALL_MEMBASE:
2975 g_assert (arm_is_imm12 (ins->inst_offset));
2976 g_assert (ins->sreg1 != ARMREG_LR);
2977 call = (MonoCallInst*)ins;
2978 if (call->method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
2979 ARM_ADD_REG_IMM8 (code, ARMREG_LR, ARMREG_PC, 4);
2980 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
2982 * We can't embed the method in the code stream in PIC code, or
2984 * Instead, we put it in V5 in code emitted by
2985 * mono_arch_emit_imt_argument (), and embed NULL here to
2986 * signal the IMT thunk that the value is in V5.
2988 if (call->dynamic_imt_arg)
2989 *((gpointer*)code) = NULL;
2991 *((gpointer*)code) = (gpointer)call->method;
2994 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
2995 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
2997 code = emit_move_return_value (cfg, ins, code);
3000 /* keep alignment */
3001 int alloca_waste = cfg->param_area;
3004 /* round the size to 8 bytes */
3005 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->sreg1, 7);
3006 ARM_BIC_REG_IMM8 (code, ins->dreg, ins->dreg, 7);
3008 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->dreg, alloca_waste);
3009 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ins->dreg);
3010 /* memzero the area: dreg holds the size, sp is the pointer */
3011 if (ins->flags & MONO_INST_INIT) {
3012 guint8 *start_loop, *branch_to_cond;
3013 ARM_MOV_REG_IMM8 (code, ARMREG_LR, 0);
3014 branch_to_cond = code;
3017 ARM_STR_REG_REG (code, ARMREG_LR, ARMREG_SP, ins->dreg);
3018 arm_patch (branch_to_cond, code);
3019 /* decrement by 4 and set flags */
3020 ARM_SUBS_REG_IMM8 (code, ins->dreg, ins->dreg, 4);
3021 ARM_B_COND (code, ARMCOND_GE, 0);
3022 arm_patch (code - 4, start_loop);
3024 ARM_ADD_REG_IMM8 (code, ins->dreg, ARMREG_SP, alloca_waste);
3028 if (ins->sreg1 != ARMREG_R0)
3029 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
3030 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3031 (gpointer)"mono_arch_throw_exception");
3032 code = emit_call_seq (cfg, code);
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_rethrow_exception");
3040 code = emit_call_seq (cfg, code);
3043 case OP_START_HANDLER: {
3044 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3046 if (arm_is_imm12 (spvar->inst_offset)) {
3047 ARM_STR_IMM (code, ARMREG_LR, spvar->inst_basereg, spvar->inst_offset);
3049 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3050 ARM_STR_REG_REG (code, ARMREG_LR, spvar->inst_basereg, ARMREG_IP);
3054 case OP_ENDFILTER: {
3055 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3057 if (ins->sreg1 != ARMREG_R0)
3058 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
3059 if (arm_is_imm12 (spvar->inst_offset)) {
3060 ARM_LDR_IMM (code, ARMREG_IP, spvar->inst_basereg, spvar->inst_offset);
3062 g_assert (ARMREG_IP != spvar->inst_basereg);
3063 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3064 ARM_LDR_REG_REG (code, ARMREG_IP, spvar->inst_basereg, ARMREG_IP);
3066 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
3069 case OP_ENDFINALLY: {
3070 MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
3072 if (arm_is_imm12 (spvar->inst_offset)) {
3073 ARM_LDR_IMM (code, ARMREG_IP, spvar->inst_basereg, spvar->inst_offset);
3075 g_assert (ARMREG_IP != spvar->inst_basereg);
3076 code = mono_arm_emit_load_imm (code, ARMREG_IP, spvar->inst_offset);
3077 ARM_LDR_REG_REG (code, ARMREG_IP, spvar->inst_basereg, ARMREG_IP);
3079 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
3082 case OP_CALL_HANDLER:
3083 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb);
3087 ins->inst_c0 = code - cfg->native_code;
3090 /*if (ins->inst_target_bb->native_offset) {
3092 //x86_jump_code (code, cfg->native_code + ins->inst_target_bb->native_offset);
3094 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb);
3099 ARM_MOV_REG_REG (code, ARMREG_PC, ins->sreg1);
3103 * In the normal case we have:
3104 * ldr pc, [pc, ins->sreg1 << 2]
3107 * ldr lr, [pc, ins->sreg1 << 2]
3109 * After follows the data.
3110 * FIXME: add aot support.
3112 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_SWITCH, ins->inst_p0);
3113 max_len += 4 * GPOINTER_TO_INT (ins->klass);
3114 if (offset > (cfg->code_size - max_len - 16)) {
3115 cfg->code_size += max_len;
3116 cfg->code_size *= 2;
3117 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
3118 code = cfg->native_code + offset;
3120 ARM_LDR_REG_REG_SHIFT (code, ARMREG_PC, ARMREG_PC, ins->sreg1, ARMSHIFT_LSL, 2);
3122 code += 4 * GPOINTER_TO_INT (ins->klass);
3126 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
3127 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
3131 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3132 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LT);
3136 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3137 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LO);
3141 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3142 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_GT);
3146 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3147 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_HI);
3149 case OP_COND_EXC_EQ:
3150 case OP_COND_EXC_NE_UN:
3151 case OP_COND_EXC_LT:
3152 case OP_COND_EXC_LT_UN:
3153 case OP_COND_EXC_GT:
3154 case OP_COND_EXC_GT_UN:
3155 case OP_COND_EXC_GE:
3156 case OP_COND_EXC_GE_UN:
3157 case OP_COND_EXC_LE:
3158 case OP_COND_EXC_LE_UN:
3159 EMIT_COND_SYSTEM_EXCEPTION (ins->opcode - OP_COND_EXC_EQ, ins->inst_p1);
3161 case OP_COND_EXC_IEQ:
3162 case OP_COND_EXC_INE_UN:
3163 case OP_COND_EXC_ILT:
3164 case OP_COND_EXC_ILT_UN:
3165 case OP_COND_EXC_IGT:
3166 case OP_COND_EXC_IGT_UN:
3167 case OP_COND_EXC_IGE:
3168 case OP_COND_EXC_IGE_UN:
3169 case OP_COND_EXC_ILE:
3170 case OP_COND_EXC_ILE_UN:
3171 EMIT_COND_SYSTEM_EXCEPTION (ins->opcode - OP_COND_EXC_IEQ, ins->inst_p1);
3174 case OP_COND_EXC_IC:
3175 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_CS, ins->inst_p1);
3177 case OP_COND_EXC_OV:
3178 case OP_COND_EXC_IOV:
3179 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VS, ins->inst_p1);
3181 case OP_COND_EXC_NC:
3182 case OP_COND_EXC_INC:
3183 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_CC, ins->inst_p1);
3185 case OP_COND_EXC_NO:
3186 case OP_COND_EXC_INO:
3187 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VC, ins->inst_p1);
3199 EMIT_COND_BRANCH (ins, ins->opcode - OP_IBEQ);
3202 /* floating point opcodes */
3205 if (cfg->compile_aot) {
3206 ARM_LDFD (code, ins->dreg, ARMREG_PC, 0);
3208 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3210 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
3213 /* FIXME: we can optimize the imm load by dealing with part of
3214 * the displacement in LDFD (aligning to 512).
3216 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3217 ARM_LDFD (code, ins->dreg, ARMREG_LR, 0);
3221 if (cfg->compile_aot) {
3222 ARM_LDFS (code, ins->dreg, ARMREG_PC, 0);
3224 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3227 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3228 ARM_LDFS (code, ins->dreg, ARMREG_LR, 0);
3231 case OP_STORER8_MEMBASE_REG:
3232 /* This is generated by the local regalloc pass which runs after the lowering pass */
3233 if (!arm_is_fpimm8 (ins->inst_offset)) {
3234 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3235 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_destbasereg);
3236 ARM_STFD (code, ins->sreg1, ARMREG_LR, 0);
3238 ARM_STFD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3241 case OP_LOADR8_MEMBASE:
3242 /* This is generated by the local regalloc pass which runs after the lowering pass */
3243 if (!arm_is_fpimm8 (ins->inst_offset)) {
3244 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3245 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_basereg);
3246 ARM_LDFD (code, ins->dreg, ARMREG_LR, 0);
3248 ARM_LDFD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3251 case OP_STORER4_MEMBASE_REG:
3252 g_assert (arm_is_fpimm8 (ins->inst_offset));
3253 ARM_STFS (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3255 case OP_LOADR4_MEMBASE:
3256 g_assert (arm_is_fpimm8 (ins->inst_offset));
3257 ARM_LDFS (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3259 case OP_ICONV_TO_R_UN: {
3261 tmpreg = ins->dreg == 0? 1: 0;
3262 ARM_CMP_REG_IMM8 (code, ins->sreg1, 0);
3263 ARM_FLTD (code, ins->dreg, ins->sreg1);
3264 ARM_B_COND (code, ARMCOND_GE, 8);
3265 /* save the temp register */
3266 ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
3267 ARM_STFD (code, tmpreg, ARMREG_SP, 0);
3268 ARM_LDFD (code, tmpreg, ARMREG_PC, 12);
3269 ARM_FPA_ADFD (code, ins->dreg, ins->dreg, tmpreg);
3270 ARM_LDFD (code, tmpreg, ARMREG_SP, 0);
3271 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
3272 /* skip the constant pool */
3275 *(int*)code = 0x41f00000;
3280 * ldfltd ftemp, [pc, #8] 0x41f00000 0x00000000
3281 * adfltd fdest, fdest, ftemp
3285 case OP_ICONV_TO_R4:
3286 ARM_FLTS (code, ins->dreg, ins->sreg1);
3288 case OP_ICONV_TO_R8:
3289 ARM_FLTD (code, ins->dreg, ins->sreg1);
3292 #elif defined(ARM_FPU_VFP)
3295 if (cfg->compile_aot) {
3296 ARM_FLDD (code, ins->dreg, ARMREG_PC, 0);
3298 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3300 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
3303 /* FIXME: we can optimize the imm load by dealing with part of
3304 * the displacement in LDFD (aligning to 512).
3306 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3307 ARM_FLDD (code, ins->dreg, ARMREG_LR, 0);
3311 if (cfg->compile_aot) {
3312 ARM_FLDS (code, ins->dreg, ARMREG_PC, 0);
3314 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
3316 ARM_CVTS (code, ins->dreg, ins->dreg);
3318 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
3319 ARM_FLDS (code, ins->dreg, ARMREG_LR, 0);
3320 ARM_CVTS (code, ins->dreg, ins->dreg);
3323 case OP_STORER8_MEMBASE_REG:
3324 /* This is generated by the local regalloc pass which runs after the lowering pass */
3325 if (!arm_is_fpimm8 (ins->inst_offset)) {
3326 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3327 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_destbasereg);
3328 ARM_FSTD (code, ins->sreg1, ARMREG_LR, 0);
3330 ARM_FSTD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
3333 case OP_LOADR8_MEMBASE:
3334 /* This is generated by the local regalloc pass which runs after the lowering pass */
3335 if (!arm_is_fpimm8 (ins->inst_offset)) {
3336 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3337 ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ins->inst_basereg);
3338 ARM_FLDD (code, ins->dreg, ARMREG_LR, 0);
3340 ARM_FLDD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
3343 case OP_STORER4_MEMBASE_REG:
3344 g_assert (arm_is_fpimm8 (ins->inst_offset));
3345 ARM_CVTD (code, ARM_VFP_F0, ins->sreg1);
3346 ARM_FSTS (code, ARM_VFP_F0, ins->inst_destbasereg, ins->inst_offset);
3348 case OP_LOADR4_MEMBASE:
3349 g_assert (arm_is_fpimm8 (ins->inst_offset));
3350 ARM_FLDS (code, ARM_VFP_F0, ins->inst_basereg, ins->inst_offset);
3351 ARM_CVTS (code, ins->dreg, ARM_VFP_F0);
3353 case OP_ICONV_TO_R_UN: {
3354 g_assert_not_reached ();
3357 case OP_ICONV_TO_R4:
3358 ARM_FMSR (code, ARM_VFP_F0, ins->sreg1);
3359 ARM_FSITOS (code, ARM_VFP_F0, ARM_VFP_F0);
3360 ARM_CVTS (code, ins->dreg, ARM_VFP_F0);
3362 case OP_ICONV_TO_R8:
3363 ARM_FMSR (code, ARM_VFP_F0, ins->sreg1);
3364 ARM_FSITOD (code, ins->dreg, ARM_VFP_F0);
3368 if (mono_method_signature (cfg->method)->ret->type == MONO_TYPE_R4) {
3369 ARM_CVTD (code, ARM_VFP_F0, ins->sreg1);
3370 ARM_FMRS (code, ARMREG_R0, ARM_VFP_F0);
3372 ARM_FMRRD (code, ARMREG_R0, ARMREG_R1, ins->sreg1);
3378 case OP_FCONV_TO_I1:
3379 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, TRUE);
3381 case OP_FCONV_TO_U1:
3382 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, FALSE);
3384 case OP_FCONV_TO_I2:
3385 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, TRUE);
3387 case OP_FCONV_TO_U2:
3388 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, FALSE);
3390 case OP_FCONV_TO_I4:
3392 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, TRUE);
3394 case OP_FCONV_TO_U4:
3396 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, FALSE);
3398 case OP_FCONV_TO_I8:
3399 case OP_FCONV_TO_U8:
3400 g_assert_not_reached ();
3401 /* Implemented as helper calls */
3403 case OP_LCONV_TO_R_UN:
3404 g_assert_not_reached ();
3405 /* Implemented as helper calls */
3407 case OP_LCONV_TO_OVF_I:
3408 case OP_LCONV_TO_OVF_I4_2: {
3409 guint32 *high_bit_not_set, *valid_negative, *invalid_negative, *valid_positive;
3411 * Valid ints: 0xffffffff:8000000 to 00000000:0x7f000000
3414 ARM_CMP_REG_IMM8 (code, ins->sreg1, 0);
3415 high_bit_not_set = code;
3416 ARM_B_COND (code, ARMCOND_GE, 0); /*branch if bit 31 of the lower part is not set*/
3418 ARM_CMN_REG_IMM8 (code, ins->sreg2, 1); /*This have the same effect as CMP reg, 0xFFFFFFFF */
3419 valid_negative = code;
3420 ARM_B_COND (code, ARMCOND_EQ, 0); /*branch if upper part == 0xFFFFFFFF (lower part has bit 31 set) */
3421 invalid_negative = code;
3422 ARM_B_COND (code, ARMCOND_AL, 0);
3424 arm_patch (high_bit_not_set, code);
3426 ARM_CMP_REG_IMM8 (code, ins->sreg2, 0);
3427 valid_positive = code;
3428 ARM_B_COND (code, ARMCOND_EQ, 0); /*branch if upper part == 0 (lower part has bit 31 clear)*/
3430 arm_patch (invalid_negative, code);
3431 EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_AL, "OverflowException");
3433 arm_patch (valid_negative, code);
3434 arm_patch (valid_positive, code);
3436 if (ins->dreg != ins->sreg1)
3437 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
3442 ARM_FPA_ADFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3445 ARM_FPA_SUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3448 ARM_FPA_MUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3451 ARM_FPA_DVFD (code, ins->dreg, ins->sreg1, ins->sreg2);
3454 ARM_MNFD (code, ins->dreg, ins->sreg1);
3456 #elif defined(ARM_FPU_VFP)
3458 ARM_VFP_ADDD (code, ins->dreg, ins->sreg1, ins->sreg2);
3461 ARM_VFP_SUBD (code, ins->dreg, ins->sreg1, ins->sreg2);
3464 ARM_VFP_MULD (code, ins->dreg, ins->sreg1, ins->sreg2);
3467 ARM_VFP_DIVD (code, ins->dreg, ins->sreg1, ins->sreg2);
3470 ARM_NEGD (code, ins->dreg, ins->sreg1);
3475 g_assert_not_reached ();
3479 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3480 #elif defined(ARM_FPU_VFP)
3481 ARM_CMPD (code, ins->sreg1, ins->sreg2);
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);
3492 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
3493 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
3497 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3498 #elif defined(ARM_FPU_VFP)
3499 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3502 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3503 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3507 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3508 #elif defined(ARM_FPU_VFP)
3509 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3512 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3513 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3514 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
3519 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3520 #elif defined(ARM_FPU_VFP)
3521 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3524 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3525 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3530 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3531 #elif defined(ARM_FPU_VFP)
3532 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3535 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
3536 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
3537 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
3539 /* ARM FPA flags table:
3540 * N Less than ARMCOND_MI
3541 * Z Equal ARMCOND_EQ
3542 * C Greater Than or Equal ARMCOND_CS
3543 * V Unordered ARMCOND_VS
3546 EMIT_COND_BRANCH (ins, OP_IBEQ - OP_IBEQ);
3549 EMIT_COND_BRANCH (ins, OP_IBNE_UN - OP_IBEQ);
3552 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
3555 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3556 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
3562 g_assert_not_reached ();
3565 /* FIXME does VFP requires both conds?
3566 * FPA requires EQ even thou the docs suggests that just CS is enough
3568 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_EQ);
3569 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_CS);
3572 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3573 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_GE);
3578 if (ins->dreg != ins->sreg1)
3579 ARM_MVFD (code, ins->dreg, ins->sreg1);
3580 #elif defined(ARM_FPU_VFP)
3581 ARM_CPYD (code, ins->dreg, ins->sreg1);
3586 g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
3587 g_assert_not_reached ();
3590 if ((cfg->opt & MONO_OPT_BRANCH) && ((code - cfg->native_code - offset) > max_len)) {
3591 g_warning ("wrong maximal instruction length of instruction %s (expected %d, got %d)",
3592 mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset);
3593 g_assert_not_reached ();
3599 last_offset = offset;
3602 cfg->code_len = code - cfg->native_code;
3605 #endif /* DISABLE_JIT */
3607 #ifdef HAVE_AEABI_READ_TP
3608 void __aeabi_read_tp (void);
3612 mono_arch_register_lowlevel_calls (void)
3614 /* The signature doesn't matter */
3615 mono_register_jit_icall (mono_arm_throw_exception, "mono_arm_throw_exception", mono_create_icall_signature ("void"), TRUE);
3616 mono_register_jit_icall (mono_arm_throw_exception_by_token, "mono_arm_throw_exception_by_token", mono_create_icall_signature ("void"), TRUE);
3618 #ifdef HAVE_AEABI_READ_TP
3619 mono_register_jit_icall (__aeabi_read_tp, "__aeabi_read_tp", mono_create_icall_signature ("void"), TRUE);
3623 #define patch_lis_ori(ip,val) do {\
3624 guint16 *__lis_ori = (guint16*)(ip); \
3625 __lis_ori [1] = (((guint32)(val)) >> 16) & 0xffff; \
3626 __lis_ori [3] = ((guint32)(val)) & 0xffff; \
3630 mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, gboolean run_cctors)
3632 MonoJumpInfo *patch_info;
3633 gboolean compile_aot = !run_cctors;
3635 for (patch_info = ji; patch_info; patch_info = patch_info->next) {
3636 unsigned char *ip = patch_info->ip.i + code;
3637 const unsigned char *target;
3639 if (patch_info->type == MONO_PATCH_INFO_SWITCH && !compile_aot) {
3640 gpointer *jt = (gpointer*)(ip + 8);
3642 /* jt is the inlined jump table, 2 instructions after ip
3643 * In the normal case we store the absolute addresses,
3644 * otherwise the displacements.
3646 for (i = 0; i < patch_info->data.table->table_size; i++)
3647 jt [i] = code + (int)patch_info->data.table->table [i];
3650 target = mono_resolve_patch_target (method, domain, code, patch_info, run_cctors);
3653 switch (patch_info->type) {
3654 case MONO_PATCH_INFO_BB:
3655 case MONO_PATCH_INFO_LABEL:
3658 /* No need to patch these */
3663 switch (patch_info->type) {
3664 case MONO_PATCH_INFO_IP:
3665 g_assert_not_reached ();
3666 patch_lis_ori (ip, ip);
3668 case MONO_PATCH_INFO_METHOD_REL:
3669 g_assert_not_reached ();
3670 *((gpointer *)(ip)) = code + patch_info->data.offset;
3672 case MONO_PATCH_INFO_METHODCONST:
3673 case MONO_PATCH_INFO_CLASS:
3674 case MONO_PATCH_INFO_IMAGE:
3675 case MONO_PATCH_INFO_FIELD:
3676 case MONO_PATCH_INFO_VTABLE:
3677 case MONO_PATCH_INFO_IID:
3678 case MONO_PATCH_INFO_SFLDA:
3679 case MONO_PATCH_INFO_LDSTR:
3680 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
3681 case MONO_PATCH_INFO_LDTOKEN:
3682 g_assert_not_reached ();
3683 /* from OP_AOTCONST : lis + ori */
3684 patch_lis_ori (ip, target);
3686 case MONO_PATCH_INFO_R4:
3687 case MONO_PATCH_INFO_R8:
3688 g_assert_not_reached ();
3689 *((gconstpointer *)(ip + 2)) = patch_info->data.target;
3691 case MONO_PATCH_INFO_EXC_NAME:
3692 g_assert_not_reached ();
3693 *((gconstpointer *)(ip + 1)) = patch_info->data.name;
3695 case MONO_PATCH_INFO_NONE:
3696 case MONO_PATCH_INFO_BB_OVF:
3697 case MONO_PATCH_INFO_EXC_OVF:
3698 /* everything is dealt with at epilog output time */
3703 arm_patch (ip, target);
3708 * Stack frame layout:
3710 * ------------------- fp
3711 * MonoLMF structure or saved registers
3712 * -------------------
3714 * -------------------
3716 * -------------------
3717 * optional 8 bytes for tracing
3718 * -------------------
3719 * param area size is cfg->param_area
3720 * ------------------- sp
3723 mono_arch_emit_prolog (MonoCompile *cfg)
3725 MonoMethod *method = cfg->method;
3727 MonoMethodSignature *sig;
3729 int alloc_size, pos, max_offset, i, rot_amount;
3734 int prev_sp_offset, reg_offset;
3736 if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
3739 sig = mono_method_signature (method);
3740 cfg->code_size = 256 + sig->param_count * 20;
3741 code = cfg->native_code = g_malloc (cfg->code_size);
3743 mono_emit_unwind_op_def_cfa (cfg, code, ARMREG_SP, 0);
3745 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
3747 alloc_size = cfg->stack_offset;
3750 if (!method->save_lmf) {
3751 /* We save SP by storing it into IP and saving IP */
3752 ARM_PUSH (code, (cfg->used_int_regs | (1 << ARMREG_IP) | (1 << ARMREG_LR)));
3753 prev_sp_offset = 8; /* ip and lr */
3754 for (i = 0; i < 16; ++i) {
3755 if (cfg->used_int_regs & (1 << i))
3756 prev_sp_offset += 4;
3758 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset);
3760 for (i = 0; i < 16; ++i) {
3761 if ((cfg->used_int_regs & (1 << i)) || (i == ARMREG_IP) || (i == ARMREG_LR)) {
3762 mono_emit_unwind_op_offset (cfg, code, i, (- prev_sp_offset) + reg_offset);
3767 ARM_PUSH (code, 0x5ff0);
3768 prev_sp_offset = 4 * 10; /* all but r0-r3, sp and pc */
3769 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset);
3771 for (i = 0; i < 16; ++i) {
3772 if ((i > ARMREG_R3) && (i != ARMREG_SP) && (i != ARMREG_PC)) {
3773 mono_emit_unwind_op_offset (cfg, code, i, (- prev_sp_offset) + reg_offset);
3777 pos += sizeof (MonoLMF) - prev_sp_offset;
3781 // align to MONO_ARCH_FRAME_ALIGNMENT bytes
3782 if (alloc_size & (MONO_ARCH_FRAME_ALIGNMENT - 1)) {
3783 alloc_size += MONO_ARCH_FRAME_ALIGNMENT - 1;
3784 alloc_size &= ~(MONO_ARCH_FRAME_ALIGNMENT - 1);
3787 /* the stack used in the pushed regs */
3788 if (prev_sp_offset & 4)
3790 cfg->stack_usage = alloc_size;
3792 if ((i = mono_arm_is_rotated_imm8 (alloc_size, &rot_amount)) >= 0) {
3793 ARM_SUB_REG_IMM (code, ARMREG_SP, ARMREG_SP, i, rot_amount);
3795 code = mono_arm_emit_load_imm (code, ARMREG_IP, alloc_size);
3796 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
3798 mono_emit_unwind_op_def_cfa_offset (cfg, code, prev_sp_offset + alloc_size);
3800 if (cfg->frame_reg != ARMREG_SP) {
3801 ARM_MOV_REG_REG (code, cfg->frame_reg, ARMREG_SP);
3802 mono_emit_unwind_op_def_cfa_reg (cfg, code, cfg->frame_reg);
3804 //g_print ("prev_sp_offset: %d, alloc_size:%d\n", prev_sp_offset, alloc_size);
3805 prev_sp_offset += alloc_size;
3807 /* compute max_offset in order to use short forward jumps
3808 * we could skip do it on arm because the immediate displacement
3809 * for jumps is large enough, it may be useful later for constant pools
3812 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
3813 MonoInst *ins = bb->code;
3814 bb->max_offset = max_offset;
3816 if (cfg->prof_options & MONO_PROFILE_COVERAGE)
3819 MONO_BB_FOR_EACH_INS (bb, ins)
3820 max_offset += ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
3823 /* store runtime generic context */
3824 if (cfg->rgctx_var) {
3825 MonoInst *ins = cfg->rgctx_var;
3827 g_assert (ins->opcode == OP_REGOFFSET);
3829 if (arm_is_imm12 (ins->inst_offset)) {
3830 ARM_STR_IMM (code, MONO_ARCH_RGCTX_REG, ins->inst_basereg, ins->inst_offset);
3832 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
3833 ARM_STR_REG_REG (code, MONO_ARCH_RGCTX_REG, ins->inst_basereg, ARMREG_LR);
3837 /* load arguments allocated to register from the stack */
3840 cinfo = calculate_sizes (sig, sig->pinvoke);
3842 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
3843 ArgInfo *ainfo = &cinfo->ret;
3844 inst = cfg->vret_addr;
3845 g_assert (arm_is_imm12 (inst->inst_offset));
3846 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3848 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
3849 ArgInfo *ainfo = cinfo->args + i;
3850 inst = cfg->args [pos];
3852 if (cfg->verbose_level > 2)
3853 g_print ("Saving argument %d (type: %d)\n", i, ainfo->regtype);
3854 if (inst->opcode == OP_REGVAR) {
3855 if (ainfo->regtype == RegTypeGeneral)
3856 ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
3857 else if (ainfo->regtype == RegTypeFP) {
3858 g_assert_not_reached ();
3859 } else if (ainfo->regtype == RegTypeBase) {
3860 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
3861 ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3863 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3864 ARM_LDR_REG_REG (code, inst->dreg, ARMREG_SP, ARMREG_IP);
3867 g_assert_not_reached ();
3869 if (cfg->verbose_level > 2)
3870 g_print ("Argument %d assigned to register %s\n", pos, mono_arch_regname (inst->dreg));
3872 /* the argument should be put on the stack: FIXME handle size != word */
3873 if (ainfo->regtype == RegTypeGeneral) {
3874 switch (ainfo->size) {
3876 if (arm_is_imm12 (inst->inst_offset))
3877 ARM_STRB_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3879 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3880 ARM_STRB_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3884 if (arm_is_imm8 (inst->inst_offset)) {
3885 ARM_STRH_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3887 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3888 ARM_STRH_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3892 g_assert (arm_is_imm12 (inst->inst_offset));
3893 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3894 g_assert (arm_is_imm12 (inst->inst_offset + 4));
3895 ARM_STR_IMM (code, ainfo->reg + 1, inst->inst_basereg, inst->inst_offset + 4);
3898 if (arm_is_imm12 (inst->inst_offset)) {
3899 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3901 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3902 ARM_STR_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3906 } else if (ainfo->regtype == RegTypeBaseGen) {
3907 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
3908 g_assert (arm_is_imm12 (inst->inst_offset));
3909 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3910 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3911 ARM_STR_IMM (code, ARMREG_R3, inst->inst_basereg, inst->inst_offset);
3912 } else if (ainfo->regtype == RegTypeBase) {
3913 if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
3914 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3916 code = mono_arm_emit_load_imm (code, ARMREG_IP, prev_sp_offset + ainfo->offset);
3917 ARM_LDR_REG_REG (code, ARMREG_LR, ARMREG_SP, ARMREG_IP);
3920 switch (ainfo->size) {
3922 if (arm_is_imm8 (inst->inst_offset)) {
3923 ARM_STRB_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3925 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3926 ARM_STRB_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3930 if (arm_is_imm8 (inst->inst_offset)) {
3931 ARM_STRH_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3933 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3934 ARM_STRH_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3938 if (arm_is_imm12 (inst->inst_offset)) {
3939 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3941 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3942 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3944 if (arm_is_imm12 (prev_sp_offset + ainfo->offset + 4)) {
3945 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset + 4));
3947 code = mono_arm_emit_load_imm (code, ARMREG_IP, prev_sp_offset + ainfo->offset + 4);
3948 ARM_LDR_REG_REG (code, ARMREG_LR, ARMREG_SP, ARMREG_IP);
3950 if (arm_is_imm12 (inst->inst_offset + 4)) {
3951 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3953 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset + 4);
3954 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3958 if (arm_is_imm12 (inst->inst_offset)) {
3959 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3961 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3962 ARM_STR_REG_REG (code, ARMREG_LR, inst->inst_basereg, ARMREG_IP);
3966 } else if (ainfo->regtype == RegTypeFP) {
3967 g_assert_not_reached ();
3968 } else if (ainfo->regtype == RegTypeStructByVal) {
3969 int doffset = inst->inst_offset;
3973 size = mini_type_stack_size_full (cfg->generic_sharing_context, inst->inst_vtype, NULL, sig->pinvoke);
3974 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
3975 if (arm_is_imm12 (doffset)) {
3976 ARM_STR_IMM (code, ainfo->reg + cur_reg, inst->inst_basereg, doffset);
3978 code = mono_arm_emit_load_imm (code, ARMREG_IP, doffset);
3979 ARM_STR_REG_REG (code, ainfo->reg + cur_reg, inst->inst_basereg, ARMREG_IP);
3981 soffset += sizeof (gpointer);
3982 doffset += sizeof (gpointer);
3984 if (ainfo->vtsize) {
3985 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
3986 //g_print ("emit_memcpy (prev_sp_ofs: %d, ainfo->offset: %d, soffset: %d)\n", prev_sp_offset, ainfo->offset, soffset);
3987 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, doffset, ARMREG_SP, prev_sp_offset + ainfo->offset);
3989 } else if (ainfo->regtype == RegTypeStructByAddr) {
3990 g_assert_not_reached ();
3991 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
3992 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, inst->inst_offset, ainfo->reg, 0);
3994 g_assert_not_reached ();
3999 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
4000 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->domain);
4001 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
4002 (gpointer)"mono_jit_thread_attach");
4003 code = emit_call_seq (cfg, code);
4006 if (method->save_lmf) {
4007 gboolean get_lmf_fast = FALSE;
4009 #ifdef HAVE_AEABI_READ_TP
4010 gint32 lmf_addr_tls_offset = mono_get_lmf_addr_tls_offset ();
4012 if (lmf_addr_tls_offset != -1) {
4013 get_lmf_fast = TRUE;
4015 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
4016 (gpointer)"__aeabi_read_tp");
4017 code = emit_call_seq (cfg, code);
4019 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, lmf_addr_tls_offset);
4020 get_lmf_fast = TRUE;
4023 if (!get_lmf_fast) {
4024 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
4025 (gpointer)"mono_get_lmf_addr");
4026 code = emit_call_seq (cfg, code);
4028 /* we build the MonoLMF structure on the stack - see mini-arm.h */
4029 /* lmf_offset is the offset from the previous stack pointer,
4030 * alloc_size is the total stack space allocated, so the offset
4031 * of MonoLMF from the current stack ptr is alloc_size - lmf_offset.
4032 * The pointer to the struct is put in r1 (new_lmf).
4033 * r2 is used as scratch
4034 * The callee-saved registers are already in the MonoLMF structure
4036 code = emit_big_add (code, ARMREG_R1, ARMREG_SP, alloc_size - lmf_offset);
4037 /* r0 is the result from mono_get_lmf_addr () */
4038 ARM_STR_IMM (code, ARMREG_R0, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
4039 /* new_lmf->previous_lmf = *lmf_addr */
4040 ARM_LDR_IMM (code, ARMREG_R2, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4041 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4042 /* *(lmf_addr) = r1 */
4043 ARM_STR_IMM (code, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4044 /* Skip method (only needed for trampoline LMF frames) */
4045 ARM_STR_IMM (code, ARMREG_SP, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, ebp));
4046 /* save the current IP */
4047 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_PC);
4048 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, eip));
4052 code = mono_arch_instrument_prolog (cfg, mono_trace_enter_method, code, TRUE);
4054 cfg->code_len = code - cfg->native_code;
4055 g_assert (cfg->code_len < cfg->code_size);
4062 mono_arch_emit_epilog (MonoCompile *cfg)
4064 MonoMethod *method = cfg->method;
4065 int pos, i, rot_amount;
4066 int max_epilog_size = 16 + 20*4;
4069 if (cfg->method->save_lmf)
4070 max_epilog_size += 128;
4072 if (mono_jit_trace_calls != NULL)
4073 max_epilog_size += 50;
4075 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE)
4076 max_epilog_size += 50;
4078 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
4079 cfg->code_size *= 2;
4080 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
4081 mono_jit_stats.code_reallocs++;
4085 * Keep in sync with OP_JMP
4087 code = cfg->native_code + cfg->code_len;
4089 if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) {
4090 code = mono_arch_instrument_epilog (cfg, mono_trace_leave_method, code, TRUE);
4094 if (method->save_lmf) {
4096 /* all but r0-r3, sp and pc */
4097 pos += sizeof (MonoLMF) - (4 * 10);
4099 /* r2 contains the pointer to the current LMF */
4100 code = emit_big_add (code, ARMREG_R2, cfg->frame_reg, cfg->stack_usage - lmf_offset);
4101 /* ip = previous_lmf */
4102 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4104 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
4105 /* *(lmf_addr) = previous_lmf */
4106 ARM_STR_IMM (code, ARMREG_IP, ARMREG_LR, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
4107 /* FIXME: speedup: there is no actual need to restore the registers if
4108 * we didn't actually change them (idea from Zoltan).
4111 /* point sp at the registers to restore: 10 is 14 -4, because we skip r0-r3 */
4112 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_R2, (sizeof (MonoLMF) - 10 * sizeof (gulong)));
4113 ARM_POP_NWB (code, 0xaff0); /* restore ip to sp and lr to pc */
4115 if ((i = mono_arm_is_rotated_imm8 (cfg->stack_usage, &rot_amount)) >= 0) {
4116 ARM_ADD_REG_IMM (code, ARMREG_SP, cfg->frame_reg, i, rot_amount);
4118 code = mono_arm_emit_load_imm (code, ARMREG_IP, cfg->stack_usage);
4119 ARM_ADD_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
4121 /* FIXME: add v4 thumb interworking support */
4122 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP) | (1 << ARMREG_PC)));
4125 cfg->code_len = code - cfg->native_code;
4127 g_assert (cfg->code_len < cfg->code_size);
4131 /* remove once throw_exception_by_name is eliminated */
4133 exception_id_by_name (const char *name)
4135 if (strcmp (name, "IndexOutOfRangeException") == 0)
4136 return MONO_EXC_INDEX_OUT_OF_RANGE;
4137 if (strcmp (name, "OverflowException") == 0)
4138 return MONO_EXC_OVERFLOW;
4139 if (strcmp (name, "ArithmeticException") == 0)
4140 return MONO_EXC_ARITHMETIC;
4141 if (strcmp (name, "DivideByZeroException") == 0)
4142 return MONO_EXC_DIVIDE_BY_ZERO;
4143 if (strcmp (name, "InvalidCastException") == 0)
4144 return MONO_EXC_INVALID_CAST;
4145 if (strcmp (name, "NullReferenceException") == 0)
4146 return MONO_EXC_NULL_REF;
4147 if (strcmp (name, "ArrayTypeMismatchException") == 0)
4148 return MONO_EXC_ARRAY_TYPE_MISMATCH;
4149 g_error ("Unknown intrinsic exception %s\n", name);
4154 mono_arch_emit_exceptions (MonoCompile *cfg)
4156 MonoJumpInfo *patch_info;
4159 const guint8* exc_throw_pos [MONO_EXC_INTRINS_NUM] = {NULL};
4160 guint8 exc_throw_found [MONO_EXC_INTRINS_NUM] = {0};
4161 int max_epilog_size = 50;
4163 /* count the number of exception infos */
4166 * make sure we have enough space for exceptions
4168 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
4169 if (patch_info->type == MONO_PATCH_INFO_EXC) {
4170 i = exception_id_by_name (patch_info->data.target);
4171 if (!exc_throw_found [i]) {
4172 max_epilog_size += 32;
4173 exc_throw_found [i] = TRUE;
4178 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
4179 cfg->code_size *= 2;
4180 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
4181 mono_jit_stats.code_reallocs++;
4184 code = cfg->native_code + cfg->code_len;
4186 /* add code to raise exceptions */
4187 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
4188 switch (patch_info->type) {
4189 case MONO_PATCH_INFO_EXC: {
4190 MonoClass *exc_class;
4191 unsigned char *ip = patch_info->ip.i + cfg->native_code;
4193 i = exception_id_by_name (patch_info->data.target);
4194 if (exc_throw_pos [i]) {
4195 arm_patch (ip, exc_throw_pos [i]);
4196 patch_info->type = MONO_PATCH_INFO_NONE;
4199 exc_throw_pos [i] = code;
4201 arm_patch (ip, code);
4203 exc_class = mono_class_from_name (mono_defaults.corlib, "System", patch_info->data.name);
4204 g_assert (exc_class);
4206 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_LR);
4207 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
4208 patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD;
4209 patch_info->data.name = "mono_arch_throw_corlib_exception";
4210 patch_info->ip.i = code - cfg->native_code;
4212 *(guint32*)(gpointer)code = exc_class->type_token;
4222 cfg->code_len = code - cfg->native_code;
4224 g_assert (cfg->code_len < cfg->code_size);
4228 static gboolean tls_offset_inited = FALSE;
4231 mono_arch_setup_jit_tls_data (MonoJitTlsData *tls)
4233 if (!tls_offset_inited) {
4234 tls_offset_inited = TRUE;
4236 lmf_tls_offset = mono_get_lmf_tls_offset ();
4237 lmf_addr_tls_offset = mono_get_lmf_addr_tls_offset ();
4242 mono_arch_free_jit_tls_data (MonoJitTlsData *tls)
4247 mono_arch_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
4254 mono_arch_print_tree (MonoInst *tree, int arity)
4260 mono_arch_get_domain_intrinsic (MonoCompile* cfg)
4262 return mono_get_domain_intrinsic (cfg);
4266 mono_arch_get_thread_intrinsic (MonoCompile* cfg)
4268 return mono_get_thread_intrinsic (cfg);
4272 mono_arch_get_patch_offset (guint8 *code)
4279 mono_arch_flush_register_windows (void)
4283 #ifdef MONO_ARCH_HAVE_IMT
4286 mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoInst *imt_arg)
4288 if (cfg->compile_aot) {
4289 int method_reg = mono_alloc_ireg (cfg);
4292 call->dynamic_imt_arg = TRUE;
4295 mono_call_inst_add_outarg_reg (cfg, call, imt_arg->dreg, ARMREG_V5, FALSE);
4297 MONO_INST_NEW (cfg, ins, OP_AOTCONST);
4298 ins->dreg = method_reg;
4299 ins->inst_p0 = call->method;
4300 ins->inst_c1 = MONO_PATCH_INFO_METHODCONST;
4301 MONO_ADD_INS (cfg->cbb, ins);
4303 mono_call_inst_add_outarg_reg (cfg, call, method_reg, ARMREG_V5, FALSE);
4305 } else if (cfg->generic_context) {
4307 /* Always pass in a register for simplicity */
4308 call->dynamic_imt_arg = TRUE;
4310 cfg->uses_rgctx_reg = TRUE;
4313 mono_call_inst_add_outarg_reg (cfg, call, imt_arg->dreg, ARMREG_V5, FALSE);
4316 int method_reg = mono_alloc_preg (cfg);
4318 MONO_INST_NEW (cfg, ins, OP_PCONST);
4319 ins->inst_p0 = call->method;
4320 ins->dreg = method_reg;
4321 MONO_ADD_INS (cfg->cbb, ins);
4323 mono_call_inst_add_outarg_reg (cfg, call, method_reg, ARMREG_V5, FALSE);
4329 mono_arch_find_imt_method (gpointer *regs, guint8 *code)
4331 guint32 *code_ptr = (guint32*)code;
4333 /* The IMT value is stored in the code stream right after the LDC instruction. */
4334 if (!IS_LDR_PC (code_ptr [0])) {
4335 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]);
4336 g_assert (IS_LDR_PC (code_ptr [0]));
4338 if (code_ptr [1] == 0)
4339 /* This is AOTed code, the IMT method is in V5 */
4340 return (MonoMethod*)regs [ARMREG_V5];
4342 return (MonoMethod*) code_ptr [1];
4346 mono_arch_find_this_argument (gpointer *regs, MonoMethod *method, MonoGenericSharingContext *gsctx)
4348 return mono_arch_get_this_arg_from_call (gsctx, mono_method_signature (method), (gssize*)regs, NULL);
4352 mono_arch_find_static_call_vtable (gpointer *regs, guint8 *code)
4354 return (MonoVTable*) regs [MONO_ARCH_RGCTX_REG];
4357 #define ENABLE_WRONG_METHOD_CHECK 0
4358 #define BASE_SIZE (6 * 4)
4359 #define BSEARCH_ENTRY_SIZE (4 * 4)
4360 #define CMP_SIZE (3 * 4)
4361 #define BRANCH_SIZE (1 * 4)
4362 #define CALL_SIZE (2 * 4)
4363 #define WMC_SIZE (5 * 4)
4364 #define DISTANCE(A, B) (((gint32)(B)) - ((gint32)(A)))
4367 arm_emit_value_and_patch_ldr (arminstr_t *code, arminstr_t *target, guint32 value)
4369 guint32 delta = DISTANCE (target, code);
4371 g_assert (delta >= 0 && delta <= 0xFFF);
4372 *target = *target | delta;
4378 mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count,
4379 gpointer fail_tramp)
4381 int size, i, extra_space = 0;
4382 arminstr_t *code, *start, *vtable_target = NULL;
4383 gboolean large_offsets = FALSE;
4384 guint32 **constant_pool_starts;
4387 constant_pool_starts = g_new0 (guint32*, count);
4390 * We might be called with a fail_tramp from the IMT builder code even if
4391 * MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK is not defined.
4393 //g_assert (!fail_tramp);
4395 for (i = 0; i < count; ++i) {
4396 MonoIMTCheckItem *item = imt_entries [i];
4397 if (item->is_equals) {
4398 if (!arm_is_imm12 (DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]))) {
4399 item->chunk_size += 32;
4400 large_offsets = TRUE;
4403 if (item->check_target_idx) {
4404 if (!item->compare_done)
4405 item->chunk_size += CMP_SIZE;
4406 item->chunk_size += BRANCH_SIZE;
4408 #if ENABLE_WRONG_METHOD_CHECK
4409 item->chunk_size += WMC_SIZE;
4412 item->chunk_size += CALL_SIZE;
4414 item->chunk_size += BSEARCH_ENTRY_SIZE;
4415 imt_entries [item->check_target_idx]->compare_done = TRUE;
4417 size += item->chunk_size;
4421 size += 4 * count; /* The ARM_ADD_REG_IMM to pop the stack */
4423 start = code = mono_domain_code_reserve (domain, size);
4426 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);
4427 for (i = 0; i < count; ++i) {
4428 MonoIMTCheckItem *item = imt_entries [i];
4429 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);
4434 ARM_PUSH4 (code, ARMREG_R0, ARMREG_R1, ARMREG_IP, ARMREG_PC);
4436 ARM_PUSH2 (code, ARMREG_R0, ARMREG_R1);
4437 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, -4);
4438 vtable_target = code;
4439 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
4441 /* R0 == 0 means we are called from AOT code. In this case, V5 contains the IMT method */
4442 ARM_CMP_REG_IMM8 (code, ARMREG_R0, 0);
4443 ARM_MOV_REG_REG_COND (code, ARMREG_R0, ARMREG_V5, ARMCOND_EQ);
4445 for (i = 0; i < count; ++i) {
4446 MonoIMTCheckItem *item = imt_entries [i];
4447 arminstr_t *imt_method = NULL, *vtable_offset_ins = NULL;
4448 gint32 vtable_offset;
4450 item->code_target = (guint8*)code;
4452 if (item->is_equals) {
4453 if (item->check_target_idx) {
4454 if (!item->compare_done) {
4456 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4457 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
4459 item->jmp_code = (guint8*)code;
4460 ARM_B_COND (code, ARMCOND_NE, 0);
4462 /*Enable the commented code to assert on wrong method*/
4463 #if ENABLE_WRONG_METHOD_CHECK
4465 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4466 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
4467 ARM_B_COND (code, ARMCOND_NE, 1);
4473 vtable_offset = DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]);
4474 if (!arm_is_imm12 (vtable_offset)) {
4476 * We need to branch to a computed address but we don't have
4477 * a free register to store it, since IP must contain the
4478 * vtable address. So we push the two values to the stack, and
4479 * load them both using LDM.
4481 /* Compute target address */
4482 vtable_offset_ins = code;
4483 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4484 ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_IP, ARMREG_R1);
4485 /* Save it to the fourth slot */
4486 ARM_STR_IMM (code, ARMREG_R1, ARMREG_SP, 3 * sizeof (gpointer));
4487 /* Restore registers and branch */
4488 ARM_POP4 (code, ARMREG_R0, ARMREG_R1, ARMREG_IP, ARMREG_PC);
4490 code = arm_emit_value_and_patch_ldr (code, vtable_offset_ins, vtable_offset);
4492 ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
4494 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 2 * sizeof (gpointer));
4495 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, vtable_offset);
4499 code = arm_emit_value_and_patch_ldr (code, imt_method, (guint32)item->key);
4501 /*must emit after unconditional branch*/
4502 if (vtable_target) {
4503 code = arm_emit_value_and_patch_ldr (code, vtable_target, (guint32)vtable);
4504 item->chunk_size += 4;
4505 vtable_target = NULL;
4508 /*We reserve the space for bsearch IMT values after the first entry with an absolute jump*/
4509 constant_pool_starts [i] = code;
4511 code += extra_space;
4515 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
4516 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
4518 item->jmp_code = (guint8*)code;
4519 ARM_B_COND (code, ARMCOND_GE, 0);
4524 for (i = 0; i < count; ++i) {
4525 MonoIMTCheckItem *item = imt_entries [i];
4526 if (item->jmp_code) {
4527 if (item->check_target_idx)
4528 arm_patch (item->jmp_code, imt_entries [item->check_target_idx]->code_target);
4530 if (i > 0 && item->is_equals) {
4532 arminstr_t *space_start = constant_pool_starts [i];
4533 for (j = i - 1; j >= 0 && !imt_entries [j]->is_equals; --j) {
4534 space_start = arm_emit_value_and_patch_ldr (space_start, (arminstr_t*)imt_entries [j]->code_target, (guint32)imt_entries [j]->key);
4541 char *buff = g_strdup_printf ("thunk_for_class_%s_%s_entries_%d", vtable->klass->name_space, vtable->klass->name, count);
4542 mono_disassemble_code (NULL, (guint8*)start, size, buff);
4547 g_free (constant_pool_starts);
4549 mono_arch_flush_icache ((guint8*)start, size);
4550 mono_stats.imt_thunks_size += code - start;
4552 g_assert (DISTANCE (start, code) <= size);
4559 mono_arch_context_get_int_reg (MonoContext *ctx, int reg)
4561 if (reg >= 4 && reg <= 11)
4562 return (gpointer)ctx->regs [reg - 4];
4563 else if (reg == ARMREG_IP)
4564 return (gpointer)ctx->regs [8];
4565 else if (reg == ARMREG_LR)
4566 return (gpointer)ctx->regs [9];
4568 g_assert_not_reached ();