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 /* This mutex protects architecture specific caches */
27 #define mono_mini_arch_lock() EnterCriticalSection (&mini_arch_mutex)
28 #define mono_mini_arch_unlock() LeaveCriticalSection (&mini_arch_mutex)
29 static CRITICAL_SECTION mini_arch_mutex;
31 static int v5_supported = 0;
32 static int thumb_supported = 0;
34 static int mono_arm_is_rotated_imm8 (guint32 val, gint *rot_amount);
38 * floating point support: on ARM it is a mess, there are at least 3
39 * different setups, each of which binary incompat with the other.
40 * 1) FPA: old and ugly, but unfortunately what current distros use
41 * the double binary format has the two words swapped. 8 double registers.
42 * Implemented usually by kernel emulation.
43 * 2) softfloat: the compiler emulates all the fp ops. Usually uses the
44 * ugly swapped double format (I guess a softfloat-vfp exists, too, though).
45 * 3) VFP: the new and actually sensible and useful FP support. Implemented
46 * in HW or kernel-emulated, requires new tools. I think this ios what symbian uses.
48 * The plan is to write the FPA support first. softfloat can be tested in a chroot.
50 int mono_exc_esp_offset = 0;
52 #define arm_is_imm12(v) ((v) > -4096 && (v) < 4096)
53 #define arm_is_imm8(v) ((v) > -256 && (v) < 256)
54 #define arm_is_fpimm8(v) ((v) >= -1020 && (v) <= 1020)
56 #define LDR_MASK ((0xf << ARMCOND_SHIFT) | (3 << 26) | (1 << 22) | (1 << 20) | (15 << 12))
57 #define LDR_PC_VAL ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 26) | (0 << 22) | (1 << 20) | (15 << 12))
58 #define IS_LDR_PC(val) (((val) & LDR_MASK) == LDR_PC_VAL)
60 #define ADD_LR_PC_4 ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 25) | (1 << 23) | (ARMREG_PC << 16) | (ARMREG_LR << 12) | 4)
61 #define MOV_LR_PC ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 24) | (0xa << 20) | (ARMREG_LR << 12) | ARMREG_PC)
65 mono_arch_regname (int reg) {
66 static const char * rnames[] = {
67 "arm_r0", "arm_r1", "arm_r2", "arm_r3", "arm_v1",
68 "arm_v2", "arm_v3", "arm_v4", "arm_v5", "arm_v6",
69 "arm_v7", "arm_fp", "arm_ip", "arm_sp", "arm_lr",
72 if (reg >= 0 && reg < 16)
78 mono_arch_fregname (int reg) {
79 static const char * rnames[] = {
80 "arm_f0", "arm_f1", "arm_f2", "arm_f3", "arm_f4",
81 "arm_f5", "arm_f6", "arm_f7", "arm_f8", "arm_f9",
82 "arm_f10", "arm_f11", "arm_f12", "arm_f13", "arm_f14",
83 "arm_f15", "arm_f16", "arm_f17", "arm_f18", "arm_f19",
84 "arm_f20", "arm_f21", "arm_f22", "arm_f23", "arm_f24",
85 "arm_f25", "arm_f26", "arm_f27", "arm_f28", "arm_f29",
88 if (reg >= 0 && reg < 32)
94 emit_big_add (guint8 *code, int dreg, int sreg, int imm)
97 if ((imm8 = mono_arm_is_rotated_imm8 (imm, &rot_amount)) >= 0) {
98 ARM_ADD_REG_IMM (code, dreg, sreg, imm8, rot_amount);
101 g_assert (dreg != sreg);
102 code = mono_arm_emit_load_imm (code, dreg, imm);
103 ARM_ADD_REG_REG (code, dreg, dreg, sreg);
108 emit_memcpy (guint8 *code, int size, int dreg, int doffset, int sreg, int soffset)
110 /* we can use r0-r3, since this is called only for incoming args on the stack */
111 if (size > sizeof (gpointer) * 4) {
113 code = emit_big_add (code, ARMREG_R0, sreg, soffset);
114 code = emit_big_add (code, ARMREG_R1, dreg, doffset);
115 start_loop = code = mono_arm_emit_load_imm (code, ARMREG_R2, size);
116 ARM_LDR_IMM (code, ARMREG_R3, ARMREG_R0, 0);
117 ARM_STR_IMM (code, ARMREG_R3, ARMREG_R1, 0);
118 ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_R0, 4);
119 ARM_ADD_REG_IMM8 (code, ARMREG_R1, ARMREG_R1, 4);
120 ARM_SUBS_REG_IMM8 (code, ARMREG_R2, ARMREG_R2, 4);
121 ARM_B_COND (code, ARMCOND_NE, 0);
122 arm_patch (code - 4, start_loop);
125 if (arm_is_imm12 (doffset) && arm_is_imm12 (doffset + size) &&
126 arm_is_imm12 (soffset) && arm_is_imm12 (soffset + size)) {
128 ARM_LDR_IMM (code, ARMREG_LR, sreg, soffset);
129 ARM_STR_IMM (code, ARMREG_LR, dreg, doffset);
135 code = emit_big_add (code, ARMREG_R0, sreg, soffset);
136 code = emit_big_add (code, ARMREG_R1, dreg, doffset);
137 doffset = soffset = 0;
139 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_R0, soffset);
140 ARM_STR_IMM (code, ARMREG_LR, ARMREG_R1, doffset);
146 g_assert (size == 0);
151 emit_call_reg (guint8 *code, int reg)
154 ARM_BLX_REG (code, reg);
156 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
160 ARM_MOV_REG_REG (code, ARMREG_PC, reg);
166 emit_call_seq (MonoCompile *cfg, guint8 *code)
168 if (cfg->method->dynamic) {
169 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
171 *(gpointer*)code = NULL;
173 code = emit_call_reg (code, ARMREG_IP);
181 * mono_arch_get_argument_info:
182 * @csig: a method signature
183 * @param_count: the number of parameters to consider
184 * @arg_info: an array to store the result infos
186 * Gathers information on parameters such as size, alignment and
187 * padding. arg_info should be large enought to hold param_count + 1 entries.
189 * Returns the size of the activation frame.
192 mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJitArgumentInfo *arg_info)
194 int k, frame_size = 0;
195 int size, align, pad;
198 if (MONO_TYPE_ISSTRUCT (csig->ret)) {
199 frame_size += sizeof (gpointer);
203 arg_info [0].offset = offset;
206 frame_size += sizeof (gpointer);
210 arg_info [0].size = frame_size;
212 for (k = 0; k < param_count; k++) {
215 size = mono_type_native_stack_size (csig->params [k], &align);
217 size = mini_type_stack_size (NULL, csig->params [k], &align);
219 /* ignore alignment for now */
222 frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1);
223 arg_info [k].pad = pad;
225 arg_info [k + 1].pad = 0;
226 arg_info [k + 1].size = size;
228 arg_info [k + 1].offset = offset;
232 align = MONO_ARCH_FRAME_ALIGNMENT;
233 frame_size += pad = (align - (frame_size & (align - 1))) & (align - 1);
234 arg_info [k].pad = pad;
240 decode_vcall_slot_from_ldr (guint32 ldr, gpointer *regs)
244 reg = (ldr >> 16 ) & 0xf;
245 offset = ldr & 0xfff;
246 if (((ldr >> 23) & 1) == 0) /*U bit, 0 means negative and 1 positive*/
248 /*g_print ("found vcall at r%d + %d for code at %p 0x%x\n", reg, offset, code, *code);*/
250 return (gpointer*)(o + offset);
254 mono_arch_get_vcall_slot_addr (guint8 *code_ptr, gpointer *regs)
256 guint32* code = (guint32*)code_ptr;
258 /* Locate the address of the method-specific trampoline. The call using
259 the vtable slot that took the processing flow to 'arch_create_jit_trampoline'
260 looks something like this:
269 The call sequence could be also:
272 function pointer literal
276 Note that on ARM5+ we can use one instruction instead of the last two.
277 Therefore, we need to locate the 'ldr rA' instruction to know which
278 register was used to hold the method addrs.
281 /* This is the instruction after "ldc pc, xxx", "mov pc, xxx" or "bl xxx" could be either the IMT value or some other instruction*/
284 /* Three possible code sequences can happen here:
288 * ldr pc, [rX - #offset]
294 * ldr pc, [rX - #offset]
296 * direct branch with bl:
300 * direct branch with mov:
304 * We only need to identify interface and virtual calls, the others can be ignored.
307 if (IS_LDR_PC (code [-1]) && code [-2] == ADD_LR_PC_4)
308 return decode_vcall_slot_from_ldr (code [-1], regs);
310 if (IS_LDR_PC (code [0]) && code [-1] == MOV_LR_PC)
311 return decode_vcall_slot_from_ldr (code [0], regs);
316 #define MAX_ARCH_DELEGATE_PARAMS 3
319 mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target)
321 guint8 *code, *start;
323 /* FIXME: Support more cases */
324 if (MONO_TYPE_ISSTRUCT (sig->ret))
328 static guint8* cached = NULL;
329 mono_mini_arch_lock ();
331 mono_mini_arch_unlock ();
335 start = code = mono_global_codeman_reserve (12);
337 /* Replace the this argument with the target */
338 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, method_ptr));
339 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, target));
340 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
342 g_assert ((code - start) <= 12);
344 mono_arch_flush_icache (code, 12);
346 mono_mini_arch_unlock ();
349 static guint8* cache [MAX_ARCH_DELEGATE_PARAMS + 1] = {NULL};
352 if (sig->param_count > MAX_ARCH_DELEGATE_PARAMS)
354 for (i = 0; i < sig->param_count; ++i)
355 if (!mono_is_regsize_var (sig->params [i]))
358 mono_mini_arch_lock ();
359 code = cache [sig->param_count];
361 mono_mini_arch_unlock ();
365 size = 8 + sig->param_count * 4;
366 start = code = mono_global_codeman_reserve (size);
368 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, method_ptr));
369 /* slide down the arguments */
370 for (i = 0; i < sig->param_count; ++i) {
371 ARM_MOV_REG_REG (code, (ARMREG_R0 + i), (ARMREG_R0 + i + 1));
373 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
375 g_assert ((code - start) <= size);
377 mono_arch_flush_icache (code, size);
378 cache [sig->param_count] = start;
379 mono_mini_arch_unlock ();
387 mono_arch_get_this_arg_from_call (MonoMethodSignature *sig, gssize *regs, guint8 *code)
389 /* FIXME: handle returning a struct */
390 if (MONO_TYPE_ISSTRUCT (sig->ret))
391 return (gpointer)regs [ARMREG_R1];
392 return (gpointer)regs [ARMREG_R0];
396 * Initialize the cpu to execute managed code.
399 mono_arch_cpu_init (void)
404 * Initialize architecture specific code.
407 mono_arch_init (void)
409 InitializeCriticalSection (&mini_arch_mutex);
413 * Cleanup architecture specific code.
416 mono_arch_cleanup (void)
421 * This function returns the optimizations supported on this cpu.
424 mono_arch_cpu_optimizazions (guint32 *exclude_mask)
429 FILE *file = fopen ("/proc/cpuinfo", "r");
431 while ((line = fgets (buf, 512, file))) {
432 if (strncmp (line, "Processor", 9) == 0) {
433 char *ver = strstr (line, "(v");
434 if (ver && (ver [2] == '5' || ver [2] == '6' || ver [2] == '7')) {
439 if (strncmp (line, "Features", 8) == 0) {
440 char *th = strstr (line, "thumb");
442 thumb_supported = TRUE;
450 /*printf ("features: v5: %d, thumb: %d\n", v5_supported, thumb_supported);*/
453 /* no arm-specific optimizations yet */
459 is_regsize_var (MonoType *t) {
462 t = mono_type_get_underlying_type (t);
469 case MONO_TYPE_FNPTR:
471 case MONO_TYPE_OBJECT:
472 case MONO_TYPE_STRING:
473 case MONO_TYPE_CLASS:
474 case MONO_TYPE_SZARRAY:
475 case MONO_TYPE_ARRAY:
477 case MONO_TYPE_GENERICINST:
478 if (!mono_type_generic_inst_is_valuetype (t))
481 case MONO_TYPE_VALUETYPE:
488 mono_arch_get_allocatable_int_vars (MonoCompile *cfg)
493 for (i = 0; i < cfg->num_varinfo; i++) {
494 MonoInst *ins = cfg->varinfo [i];
495 MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
498 if (vmv->range.first_use.abs_pos >= vmv->range.last_use.abs_pos)
501 if (ins->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT) || (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG))
504 /* we can only allocate 32 bit values */
505 if (is_regsize_var (ins->inst_vtype)) {
506 g_assert (MONO_VARINFO (cfg, i)->reg == -1);
507 g_assert (i == vmv->idx);
508 vars = mono_varlist_insert_sorted (cfg, vars, vmv, FALSE);
515 #define USE_EXTRA_TEMPS 0
518 mono_arch_get_global_int_regs (MonoCompile *cfg)
521 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V1));
522 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V2));
523 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V3));
524 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V4));
525 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V5));
526 /*regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V6));*/
527 /*regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V7));*/
533 * mono_arch_regalloc_cost:
535 * Return the cost, in number of memory references, of the action of
536 * allocating the variable VMV into a register during global register
540 mono_arch_regalloc_cost (MonoCompile *cfg, MonoMethodVar *vmv)
547 mono_arch_flush_icache (guint8 *code, gint size)
549 __asm __volatile ("mov r0, %0\n"
552 "swi 0x9f0002 @ sys_cacheflush"
554 : "r" (code), "r" (code + size), "r" (0)
555 : "r0", "r1", "r3" );
559 #define NOT_IMPLEMENTED(x) \
560 g_error ("FIXME: %s is not yet implemented. (trampoline)", x);
573 guint16 vtsize; /* in param area */
575 guint8 regtype : 4; /* 0 general, 1 basereg, 2 floating point register, see RegType* */
576 guint8 size : 4; /* 1, 2, 4, 8, or regs used by RegTypeStructByVal */
591 add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple)
594 if (*gr > ARMREG_R3) {
595 ainfo->offset = *stack_size;
596 ainfo->reg = ARMREG_SP; /* in the caller */
597 ainfo->regtype = RegTypeBase;
608 /* first word in r3 and the second on the stack */
609 ainfo->offset = *stack_size;
610 ainfo->reg = ARMREG_SP; /* in the caller */
611 ainfo->regtype = RegTypeBaseGen;
613 } else if (*gr > ARMREG_R3) {
618 ainfo->offset = *stack_size;
619 ainfo->reg = ARMREG_SP; /* in the caller */
620 ainfo->regtype = RegTypeBase;
635 calculate_sizes (MonoMethodSignature *sig, gboolean is_pinvoke)
638 int n = sig->hasthis + sig->param_count;
640 guint32 stack_size = 0;
641 CallInfo *cinfo = g_malloc0 (sizeof (CallInfo) + sizeof (ArgInfo) * n);
645 /* FIXME: handle returning a struct */
646 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
647 add_general (&gr, &stack_size, &cinfo->ret, TRUE);
648 cinfo->struct_ret = ARMREG_R0;
653 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
656 DEBUG(printf("params: %d\n", sig->param_count));
657 for (i = 0; i < sig->param_count; ++i) {
658 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
659 /* Prevent implicit arguments and sig_cookie from
660 being passed in registers */
662 /* Emit the signature cookie just before the implicit arguments */
663 add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE);
665 DEBUG(printf("param %d: ", i));
666 if (sig->params [i]->byref) {
667 DEBUG(printf("byref\n"));
668 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
672 simpletype = mono_type_get_underlying_type (sig->params [i])->type;
673 switch (simpletype) {
674 case MONO_TYPE_BOOLEAN:
677 cinfo->args [n].size = 1;
678 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
684 cinfo->args [n].size = 2;
685 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
690 cinfo->args [n].size = 4;
691 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
697 case MONO_TYPE_FNPTR:
698 case MONO_TYPE_CLASS:
699 case MONO_TYPE_OBJECT:
700 case MONO_TYPE_STRING:
701 case MONO_TYPE_SZARRAY:
702 case MONO_TYPE_ARRAY:
704 cinfo->args [n].size = sizeof (gpointer);
705 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
708 case MONO_TYPE_GENERICINST:
709 if (!mono_type_generic_inst_is_valuetype (sig->params [i])) {
710 cinfo->args [n].size = sizeof (gpointer);
711 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
716 case MONO_TYPE_TYPEDBYREF:
717 case MONO_TYPE_VALUETYPE: {
722 if (simpletype == MONO_TYPE_TYPEDBYREF) {
723 size = sizeof (MonoTypedRef);
725 MonoClass *klass = mono_class_from_mono_type (sig->params [i]);
727 size = mono_class_native_size (klass, NULL);
729 size = mono_class_value_size (klass, NULL);
731 DEBUG(printf ("load %d bytes struct\n",
732 mono_class_native_size (sig->params [i]->data.klass, NULL)));
735 align_size += (sizeof (gpointer) - 1);
736 align_size &= ~(sizeof (gpointer) - 1);
737 nwords = (align_size + sizeof (gpointer) -1 ) / sizeof (gpointer);
738 cinfo->args [n].regtype = RegTypeStructByVal;
739 /* FIXME: align gr and stack_size if needed */
740 if (gr > ARMREG_R3) {
741 cinfo->args [n].size = 0;
742 cinfo->args [n].vtsize = nwords;
744 int rest = ARMREG_R3 - gr + 1;
745 int n_in_regs = rest >= nwords? nwords: rest;
746 cinfo->args [n].size = n_in_regs;
747 cinfo->args [n].vtsize = nwords - n_in_regs;
748 cinfo->args [n].reg = gr;
751 cinfo->args [n].offset = stack_size;
752 /*g_print ("offset for arg %d at %d\n", n, stack_size);*/
753 stack_size += nwords * sizeof (gpointer);
760 cinfo->args [n].size = 8;
761 add_general (&gr, &stack_size, cinfo->args + n, FALSE);
765 g_error ("Can't trampoline 0x%x", sig->params [i]->type);
770 simpletype = mono_type_get_underlying_type (sig->ret)->type;
771 switch (simpletype) {
772 case MONO_TYPE_BOOLEAN:
783 case MONO_TYPE_FNPTR:
784 case MONO_TYPE_CLASS:
785 case MONO_TYPE_OBJECT:
786 case MONO_TYPE_SZARRAY:
787 case MONO_TYPE_ARRAY:
788 case MONO_TYPE_STRING:
789 cinfo->ret.reg = ARMREG_R0;
793 cinfo->ret.reg = ARMREG_R0;
797 cinfo->ret.reg = ARMREG_R0;
798 /* FIXME: cinfo->ret.reg = ???;
799 cinfo->ret.regtype = RegTypeFP;*/
801 case MONO_TYPE_GENERICINST:
802 if (!mono_type_generic_inst_is_valuetype (sig->ret)) {
803 cinfo->ret.reg = ARMREG_R0;
807 case MONO_TYPE_VALUETYPE:
809 case MONO_TYPE_TYPEDBYREF:
813 g_error ("Can't handle as return value 0x%x", sig->ret->type);
817 /* align stack size to 8 */
818 DEBUG (printf (" stack size: %d (%d)\n", (stack_size + 15) & ~15, stack_size));
819 stack_size = (stack_size + 7) & ~7;
821 cinfo->stack_usage = stack_size;
827 * Set var information according to the calling convention. arm version.
828 * The locals var stuff should most likely be split in another method.
831 mono_arch_allocate_vars (MonoCompile *m)
833 MonoMethodSignature *sig;
834 MonoMethodHeader *header;
836 int i, offset, size, align, curinst;
837 int frame_reg = ARMREG_FP;
839 /* FIXME: this will change when we use FP as gcc does */
840 m->flags |= MONO_CFG_HAS_SPILLUP;
842 /* allow room for the vararg method args: void* and long/double */
843 if (mono_jit_trace_calls != NULL && mono_trace_eval (m->method))
844 m->param_area = MAX (m->param_area, sizeof (gpointer)*8);
846 header = mono_method_get_header (m->method);
849 * We use the frame register also for any method that has
850 * exception clauses. This way, when the handlers are called,
851 * the code will reference local variables using the frame reg instead of
852 * the stack pointer: if we had to restore the stack pointer, we'd
853 * corrupt the method frames that are already on the stack (since
854 * filters get called before stack unwinding happens) when the filter
855 * code would call any method (this also applies to finally etc.).
857 if ((m->flags & MONO_CFG_HAS_ALLOCA) || header->num_clauses)
858 frame_reg = ARMREG_FP;
859 m->frame_reg = frame_reg;
860 if (frame_reg != ARMREG_SP) {
861 m->used_int_regs |= 1 << frame_reg;
864 sig = mono_method_signature (m->method);
868 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
869 m->ret->opcode = OP_REGVAR;
870 m->ret->inst_c0 = ARMREG_R0;
872 /* FIXME: handle long and FP values */
873 switch (mono_type_get_underlying_type (sig->ret)->type) {
877 m->ret->opcode = OP_REGVAR;
878 m->ret->inst_c0 = ARMREG_R0;
882 /* local vars are at a positive offset from the stack pointer */
884 * also note that if the function uses alloca, we use FP
885 * to point at the local variables.
887 offset = 0; /* linkage area */
888 /* align the offset to 16 bytes: not sure this is needed here */
890 //offset &= ~(8 - 1);
892 /* add parameter area size for called functions */
893 offset += m->param_area;
896 if (m->flags & MONO_CFG_HAS_FPOUT)
899 /* allow room to save the return value */
900 if (mono_jit_trace_calls != NULL && mono_trace_eval (m->method))
903 /* the MonoLMF structure is stored just below the stack pointer */
905 if (sig->call_convention == MONO_CALL_VARARG) {
909 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
911 offset += sizeof(gpointer) - 1;
912 offset &= ~(sizeof(gpointer) - 1);
913 inst->inst_offset = offset;
914 inst->opcode = OP_REGOFFSET;
915 inst->inst_basereg = frame_reg;
916 offset += sizeof(gpointer);
917 if (sig->call_convention == MONO_CALL_VARARG)
918 m->sig_cookie += sizeof (gpointer);
921 curinst = m->locals_start;
922 for (i = curinst; i < m->num_varinfo; ++i) {
923 inst = m->varinfo [i];
924 if ((inst->flags & MONO_INST_IS_DEAD) || inst->opcode == OP_REGVAR)
927 /* inst->backend.is_pinvoke indicates native sized value types, this is used by the
928 * pinvoke wrappers when they call functions returning structure */
929 if (inst->backend.is_pinvoke && MONO_TYPE_ISSTRUCT (inst->inst_vtype) && inst->inst_vtype->type != MONO_TYPE_TYPEDBYREF)
930 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), &align);
932 size = mono_type_size (inst->inst_vtype, &align);
934 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
935 * since it loads/stores misaligned words, which don't do the right thing.
937 if (align < 4 && size >= 4)
940 offset &= ~(align - 1);
941 inst->inst_offset = offset;
942 inst->opcode = OP_REGOFFSET;
943 inst->inst_basereg = frame_reg;
945 //g_print ("allocating local %d to %d\n", i, inst->inst_offset);
950 inst = m->args [curinst];
951 if (inst->opcode != OP_REGVAR) {
952 inst->opcode = OP_REGOFFSET;
953 inst->inst_basereg = frame_reg;
954 offset += sizeof (gpointer) - 1;
955 offset &= ~(sizeof (gpointer) - 1);
956 inst->inst_offset = offset;
957 offset += sizeof (gpointer);
958 if (sig->call_convention == MONO_CALL_VARARG)
959 m->sig_cookie += sizeof (gpointer);
964 for (i = 0; i < sig->param_count; ++i) {
965 inst = m->args [curinst];
966 if (inst->opcode != OP_REGVAR) {
967 inst->opcode = OP_REGOFFSET;
968 inst->inst_basereg = frame_reg;
969 size = mono_type_size (sig->params [i], &align);
970 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
971 * since it loads/stores misaligned words, which don't do the right thing.
973 if (align < 4 && size >= 4)
976 offset &= ~(align - 1);
977 inst->inst_offset = offset;
979 if ((sig->call_convention == MONO_CALL_VARARG) && (i < sig->sentinelpos))
980 m->sig_cookie += size;
985 /* align the offset to 8 bytes */
990 m->stack_offset = offset;
994 /* Fixme: we need an alignment solution for enter_method and mono_arch_call_opcode,
995 * currently alignment in mono_arch_call_opcode is computed without arch_get_argument_info
999 * take the arguments and generate the arch-specific
1000 * instructions to properly call the function in call.
1001 * This includes pushing, moving arguments to the right register
1003 * Issue: who does the spilling if needed, and when?
1006 mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call, int is_virtual) {
1008 MonoMethodSignature *sig;
1013 sig = call->signature;
1014 n = sig->param_count + sig->hasthis;
1016 cinfo = calculate_sizes (sig, sig->pinvoke);
1017 if (cinfo->struct_ret)
1018 call->used_iregs |= 1 << cinfo->struct_ret;
1020 for (i = 0; i < n; ++i) {
1021 ainfo = cinfo->args + i;
1022 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
1024 cfg->disable_aot = TRUE;
1026 MONO_INST_NEW (cfg, sig_arg, OP_ICONST);
1027 sig_arg->inst_p0 = call->signature;
1029 MONO_INST_NEW (cfg, arg, OP_OUTARG);
1030 arg->inst_imm = cinfo->sig_cookie.offset;
1031 arg->inst_left = sig_arg;
1033 /* prepend, so they get reversed */
1034 arg->next = call->out_args;
1035 call->out_args = arg;
1037 if (is_virtual && i == 0) {
1038 /* the argument will be attached to the call instrucion */
1039 in = call->args [i];
1040 call->used_iregs |= 1 << ainfo->reg;
1042 MONO_INST_NEW (cfg, arg, OP_OUTARG);
1043 in = call->args [i];
1044 arg->cil_code = in->cil_code;
1045 arg->inst_left = in;
1046 arg->inst_right = (MonoInst*)call;
1047 arg->type = in->type;
1048 /* prepend, we'll need to reverse them later */
1049 arg->next = call->out_args;
1050 call->out_args = arg;
1051 if (ainfo->regtype == RegTypeGeneral) {
1052 arg->backend.reg3 = ainfo->reg;
1053 call->used_iregs |= 1 << ainfo->reg;
1054 if (arg->type == STACK_I8)
1055 call->used_iregs |= 1 << (ainfo->reg + 1);
1056 if (arg->type == STACK_R8) {
1057 if (ainfo->size == 4) {
1058 #ifndef MONO_ARCH_SOFT_FLOAT
1059 arg->opcode = OP_OUTARG_R4;
1062 call->used_iregs |= 1 << (ainfo->reg + 1);
1064 cfg->flags |= MONO_CFG_HAS_FPOUT;
1066 } else if (ainfo->regtype == RegTypeStructByAddr) {
1067 /* FIXME: where si the data allocated? */
1068 arg->backend.reg3 = ainfo->reg;
1069 call->used_iregs |= 1 << ainfo->reg;
1070 g_assert_not_reached ();
1071 } else if (ainfo->regtype == RegTypeStructByVal) {
1073 /* mark the used regs */
1074 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
1075 call->used_iregs |= 1 << (ainfo->reg + cur_reg);
1077 arg->opcode = OP_OUTARG_VT;
1078 /* vtsize and offset have just 12 bits of encoding in number of words */
1079 g_assert (((ainfo->vtsize | (ainfo->offset / 4)) & 0xfffff000) == 0);
1080 arg->backend.arg_info = ainfo->reg | (ainfo->size << 4) | (ainfo->vtsize << 8) | ((ainfo->offset / 4) << 20);
1081 } else if (ainfo->regtype == RegTypeBase) {
1082 arg->opcode = OP_OUTARG_MEMBASE;
1083 arg->backend.arg_info = (ainfo->offset << 8) | ainfo->size;
1084 } else if (ainfo->regtype == RegTypeBaseGen) {
1085 call->used_iregs |= 1 << ARMREG_R3;
1086 arg->opcode = OP_OUTARG_MEMBASE;
1087 arg->backend.arg_info = (ainfo->offset << 8) | 0xff;
1088 if (arg->type == STACK_R8)
1089 cfg->flags |= MONO_CFG_HAS_FPOUT;
1090 } else if (ainfo->regtype == RegTypeFP) {
1091 arg->backend.reg3 = ainfo->reg;
1092 /* FP args are passed in int regs */
1093 call->used_iregs |= 1 << ainfo->reg;
1094 if (ainfo->size == 8) {
1095 arg->opcode = OP_OUTARG_R8;
1096 call->used_iregs |= 1 << (ainfo->reg + 1);
1098 arg->opcode = OP_OUTARG_R4;
1100 cfg->flags |= MONO_CFG_HAS_FPOUT;
1102 g_assert_not_reached ();
1107 * Reverse the call->out_args list.
1110 MonoInst *prev = NULL, *list = call->out_args, *next;
1117 call->out_args = prev;
1119 call->stack_usage = cinfo->stack_usage;
1120 cfg->param_area = MAX (cfg->param_area, cinfo->stack_usage);
1121 cfg->flags |= MONO_CFG_HAS_CALLS;
1123 * should set more info in call, such as the stack space
1124 * used by the args that needs to be added back to esp
1132 * Allow tracing to work with this interface (with an optional argument)
1136 mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
1140 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1141 ARM_MOV_REG_IMM8 (code, ARMREG_R1, 0); /* NULL ebp for now */
1142 code = mono_arm_emit_load_imm (code, ARMREG_R2, (guint32)func);
1143 code = emit_call_reg (code, ARMREG_R2);
1156 mono_arch_instrument_epilog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
1159 int save_mode = SAVE_NONE;
1161 MonoMethod *method = cfg->method;
1162 int rtype = mono_type_get_underlying_type (mono_method_signature (method)->ret)->type;
1163 int save_offset = cfg->param_area;
1167 offset = code - cfg->native_code;
1168 /* we need about 16 instructions */
1169 if (offset > (cfg->code_size - 16 * 4)) {
1170 cfg->code_size *= 2;
1171 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
1172 code = cfg->native_code + offset;
1175 case MONO_TYPE_VOID:
1176 /* special case string .ctor icall */
1177 if (strcmp (".ctor", method->name) && method->klass == mono_defaults.string_class)
1178 save_mode = SAVE_ONE;
1180 save_mode = SAVE_NONE;
1184 save_mode = SAVE_TWO;
1188 save_mode = SAVE_FP;
1190 case MONO_TYPE_VALUETYPE:
1191 save_mode = SAVE_STRUCT;
1194 save_mode = SAVE_ONE;
1198 switch (save_mode) {
1200 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1201 ARM_STR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1202 if (enable_arguments) {
1203 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_R1);
1204 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1208 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1209 if (enable_arguments) {
1210 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1214 /* FIXME: what reg? */
1215 if (enable_arguments) {
1216 /* FIXME: what reg? */
1220 if (enable_arguments) {
1221 /* FIXME: get the actual address */
1222 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1230 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1231 code = mono_arm_emit_load_imm (code, ARMREG_IP, (guint32)func);
1232 code = emit_call_reg (code, ARMREG_IP);
1234 switch (save_mode) {
1236 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1237 ARM_LDR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1240 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1254 * The immediate field for cond branches is big enough for all reasonable methods
1256 #define EMIT_COND_BRANCH_FLAGS(ins,condcode) \
1257 if (ins->flags & MONO_INST_BRLABEL) { \
1258 if (0 && ins->inst_i0->inst_c0) { \
1259 ARM_B_COND (code, (condcode), (code - cfg->native_code + ins->inst_i0->inst_c0) & 0xffffff); \
1261 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_LABEL, ins->inst_i0); \
1262 ARM_B_COND (code, (condcode), 0); \
1265 if (0 && ins->inst_true_bb->native_offset) { \
1266 ARM_B_COND (code, (condcode), (code - cfg->native_code + ins->inst_true_bb->native_offset) & 0xffffff); \
1268 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_true_bb); \
1269 ARM_B_COND (code, (condcode), 0); \
1273 #define EMIT_COND_BRANCH(ins,cond) EMIT_COND_BRANCH_FLAGS(ins, branch_cc_table [(cond)])
1275 /* emit an exception if condition is fail
1277 * We assign the extra code used to throw the implicit exceptions
1278 * to cfg->bb_exit as far as the big branch handling is concerned
1280 #define EMIT_COND_SYSTEM_EXCEPTION_FLAGS(condcode,exc_name) \
1282 mono_add_patch_info (cfg, code - cfg->native_code, \
1283 MONO_PATCH_INFO_EXC, exc_name); \
1284 ARM_BL_COND (code, (condcode), 0); \
1287 #define EMIT_COND_SYSTEM_EXCEPTION(cond,exc_name) EMIT_COND_SYSTEM_EXCEPTION_FLAGS(branch_cc_table [(cond)], (exc_name))
1290 peephole_pass (MonoCompile *cfg, MonoBasicBlock *bb)
1292 MonoInst *ins, *last_ins = NULL;
1297 switch (ins->opcode) {
1299 /* remove unnecessary multiplication with 1 */
1300 if (ins->inst_imm == 1) {
1301 if (ins->dreg != ins->sreg1) {
1302 ins->opcode = OP_MOVE;
1304 last_ins->next = ins->next;
1309 int power2 = mono_is_power_of_two (ins->inst_imm);
1311 ins->opcode = OP_SHL_IMM;
1312 ins->inst_imm = power2;
1316 case OP_LOAD_MEMBASE:
1317 case OP_LOADI4_MEMBASE:
1319 * OP_STORE_MEMBASE_REG reg, offset(basereg)
1320 * OP_LOAD_MEMBASE offset(basereg), reg
1322 if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG
1323 || last_ins->opcode == OP_STORE_MEMBASE_REG) &&
1324 ins->inst_basereg == last_ins->inst_destbasereg &&
1325 ins->inst_offset == last_ins->inst_offset) {
1326 if (ins->dreg == last_ins->sreg1) {
1327 last_ins->next = ins->next;
1331 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1332 ins->opcode = OP_MOVE;
1333 ins->sreg1 = last_ins->sreg1;
1337 * Note: reg1 must be different from the basereg in the second load
1338 * OP_LOAD_MEMBASE offset(basereg), reg1
1339 * OP_LOAD_MEMBASE offset(basereg), reg2
1341 * OP_LOAD_MEMBASE offset(basereg), reg1
1342 * OP_MOVE reg1, reg2
1344 } if (last_ins && (last_ins->opcode == OP_LOADI4_MEMBASE
1345 || last_ins->opcode == OP_LOAD_MEMBASE) &&
1346 ins->inst_basereg != last_ins->dreg &&
1347 ins->inst_basereg == last_ins->inst_basereg &&
1348 ins->inst_offset == last_ins->inst_offset) {
1350 if (ins->dreg == last_ins->dreg) {
1351 last_ins->next = ins->next;
1355 ins->opcode = OP_MOVE;
1356 ins->sreg1 = last_ins->dreg;
1359 //g_assert_not_reached ();
1363 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1364 * OP_LOAD_MEMBASE offset(basereg), reg
1366 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1367 * OP_ICONST reg, imm
1369 } else if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_IMM
1370 || last_ins->opcode == OP_STORE_MEMBASE_IMM) &&
1371 ins->inst_basereg == last_ins->inst_destbasereg &&
1372 ins->inst_offset == last_ins->inst_offset) {
1373 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1374 ins->opcode = OP_ICONST;
1375 ins->inst_c0 = last_ins->inst_imm;
1376 g_assert_not_reached (); // check this rule
1380 case OP_LOADU1_MEMBASE:
1381 case OP_LOADI1_MEMBASE:
1382 if (last_ins && (last_ins->opcode == OP_STOREI1_MEMBASE_REG) &&
1383 ins->inst_basereg == last_ins->inst_destbasereg &&
1384 ins->inst_offset == last_ins->inst_offset) {
1385 ins->opcode = (ins->opcode == OP_LOADI1_MEMBASE) ? CEE_CONV_I1 : CEE_CONV_U1;
1386 ins->sreg1 = last_ins->sreg1;
1389 case OP_LOADU2_MEMBASE:
1390 case OP_LOADI2_MEMBASE:
1391 if (last_ins && (last_ins->opcode == OP_STOREI2_MEMBASE_REG) &&
1392 ins->inst_basereg == last_ins->inst_destbasereg &&
1393 ins->inst_offset == last_ins->inst_offset) {
1394 ins->opcode = (ins->opcode == OP_LOADI2_MEMBASE) ? CEE_CONV_I2 : CEE_CONV_U2;
1395 ins->sreg1 = last_ins->sreg1;
1402 ins->opcode = OP_MOVE;
1406 if (ins->dreg == ins->sreg1) {
1408 last_ins->next = ins->next;
1413 * OP_MOVE sreg, dreg
1414 * OP_MOVE dreg, sreg
1416 if (last_ins && last_ins->opcode == OP_MOVE &&
1417 ins->sreg1 == last_ins->dreg &&
1418 ins->dreg == last_ins->sreg1) {
1419 last_ins->next = ins->next;
1428 bb->last_ins = last_ins;
1432 * the branch_cc_table should maintain the order of these
1446 branch_cc_table [] = {
1462 insert_after_ins (MonoBasicBlock *bb, MonoInst *ins, MonoInst *to_insert)
1466 bb->code = to_insert;
1467 to_insert->next = ins;
1469 to_insert->next = ins->next;
1470 ins->next = to_insert;
1474 #define NEW_INS(cfg,dest,op) do { \
1475 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
1476 (dest)->opcode = (op); \
1477 insert_after_ins (bb, last_ins, (dest)); \
1481 map_to_reg_reg_op (int op)
1490 case OP_COMPARE_IMM:
1504 case OP_LOAD_MEMBASE:
1505 return OP_LOAD_MEMINDEX;
1506 case OP_LOADI4_MEMBASE:
1507 return OP_LOADI4_MEMINDEX;
1508 case OP_LOADU4_MEMBASE:
1509 return OP_LOADU4_MEMINDEX;
1510 case OP_LOADU1_MEMBASE:
1511 return OP_LOADU1_MEMINDEX;
1512 case OP_LOADI2_MEMBASE:
1513 return OP_LOADI2_MEMINDEX;
1514 case OP_LOADU2_MEMBASE:
1515 return OP_LOADU2_MEMINDEX;
1516 case OP_LOADI1_MEMBASE:
1517 return OP_LOADI1_MEMINDEX;
1518 case OP_STOREI1_MEMBASE_REG:
1519 return OP_STOREI1_MEMINDEX;
1520 case OP_STOREI2_MEMBASE_REG:
1521 return OP_STOREI2_MEMINDEX;
1522 case OP_STOREI4_MEMBASE_REG:
1523 return OP_STOREI4_MEMINDEX;
1524 case OP_STORE_MEMBASE_REG:
1525 return OP_STORE_MEMINDEX;
1526 case OP_STORER4_MEMBASE_REG:
1527 return OP_STORER4_MEMINDEX;
1528 case OP_STORER8_MEMBASE_REG:
1529 return OP_STORER8_MEMINDEX;
1530 case OP_STORE_MEMBASE_IMM:
1531 return OP_STORE_MEMBASE_REG;
1532 case OP_STOREI1_MEMBASE_IMM:
1533 return OP_STOREI1_MEMBASE_REG;
1534 case OP_STOREI2_MEMBASE_IMM:
1535 return OP_STOREI2_MEMBASE_REG;
1536 case OP_STOREI4_MEMBASE_IMM:
1537 return OP_STOREI4_MEMBASE_REG;
1539 g_assert_not_reached ();
1543 * Remove from the instruction list the instructions that can't be
1544 * represented with very simple instructions with no register
1548 mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb)
1550 MonoInst *ins, *temp, *last_ins = NULL;
1551 int rot_amount, imm8, low_imm;
1553 /* setup the virtual reg allocator */
1554 if (bb->max_vreg > cfg->rs->next_vreg)
1555 cfg->rs->next_vreg = bb->max_vreg;
1560 switch (ins->opcode) {
1564 case OP_COMPARE_IMM:
1571 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount)) < 0) {
1572 NEW_INS (cfg, temp, OP_ICONST);
1573 temp->inst_c0 = ins->inst_imm;
1574 temp->dreg = mono_regstate_next_int (cfg->rs);
1575 ins->sreg2 = temp->dreg;
1576 ins->opcode = map_to_reg_reg_op (ins->opcode);
1580 if (ins->inst_imm == 1) {
1581 ins->opcode = OP_MOVE;
1584 if (ins->inst_imm == 0) {
1585 ins->opcode = OP_ICONST;
1589 imm8 = mono_is_power_of_two (ins->inst_imm);
1591 ins->opcode = OP_SHL_IMM;
1592 ins->inst_imm = imm8;
1595 NEW_INS (cfg, temp, OP_ICONST);
1596 temp->inst_c0 = ins->inst_imm;
1597 temp->dreg = mono_regstate_next_int (cfg->rs);
1598 ins->sreg2 = temp->dreg;
1599 ins->opcode = CEE_MUL;
1601 case OP_LOAD_MEMBASE:
1602 case OP_LOADI4_MEMBASE:
1603 case OP_LOADU4_MEMBASE:
1604 case OP_LOADU1_MEMBASE:
1605 /* we can do two things: load the immed in a register
1606 * and use an indexed load, or see if the immed can be
1607 * represented as an ad_imm + a load with a smaller offset
1608 * that fits. We just do the first for now, optimize later.
1610 if (arm_is_imm12 (ins->inst_offset))
1612 NEW_INS (cfg, temp, OP_ICONST);
1613 temp->inst_c0 = ins->inst_offset;
1614 temp->dreg = mono_regstate_next_int (cfg->rs);
1615 ins->sreg2 = temp->dreg;
1616 ins->opcode = map_to_reg_reg_op (ins->opcode);
1618 case OP_LOADI2_MEMBASE:
1619 case OP_LOADU2_MEMBASE:
1620 case OP_LOADI1_MEMBASE:
1621 if (arm_is_imm8 (ins->inst_offset))
1623 NEW_INS (cfg, temp, OP_ICONST);
1624 temp->inst_c0 = ins->inst_offset;
1625 temp->dreg = mono_regstate_next_int (cfg->rs);
1626 ins->sreg2 = temp->dreg;
1627 ins->opcode = map_to_reg_reg_op (ins->opcode);
1629 case OP_LOADR4_MEMBASE:
1630 case OP_LOADR8_MEMBASE:
1631 if (arm_is_fpimm8 (ins->inst_offset))
1633 low_imm = ins->inst_offset & 0x1ff;
1634 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~0x1ff, &rot_amount)) >= 0) {
1635 NEW_INS (cfg, temp, OP_ADD_IMM);
1636 temp->inst_imm = ins->inst_offset & ~0x1ff;
1637 temp->sreg1 = ins->inst_basereg;
1638 temp->dreg = mono_regstate_next_int (cfg->rs);
1639 ins->inst_basereg = temp->dreg;
1640 ins->inst_offset = low_imm;
1643 /* VFP/FPA doesn't have indexed load instructions */
1644 g_assert_not_reached ();
1646 case OP_STORE_MEMBASE_REG:
1647 case OP_STOREI4_MEMBASE_REG:
1648 case OP_STOREI1_MEMBASE_REG:
1649 if (arm_is_imm12 (ins->inst_offset))
1651 NEW_INS (cfg, temp, OP_ICONST);
1652 temp->inst_c0 = ins->inst_offset;
1653 temp->dreg = mono_regstate_next_int (cfg->rs);
1654 ins->sreg2 = temp->dreg;
1655 ins->opcode = map_to_reg_reg_op (ins->opcode);
1657 case OP_STOREI2_MEMBASE_REG:
1658 if (arm_is_imm8 (ins->inst_offset))
1660 NEW_INS (cfg, temp, OP_ICONST);
1661 temp->inst_c0 = ins->inst_offset;
1662 temp->dreg = mono_regstate_next_int (cfg->rs);
1663 ins->sreg2 = temp->dreg;
1664 ins->opcode = map_to_reg_reg_op (ins->opcode);
1666 case OP_STORER4_MEMBASE_REG:
1667 case OP_STORER8_MEMBASE_REG:
1668 if (arm_is_fpimm8 (ins->inst_offset))
1670 low_imm = ins->inst_offset & 0x1ff;
1671 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~ 0x1ff, &rot_amount)) >= 0 && arm_is_fpimm8 (low_imm)) {
1672 NEW_INS (cfg, temp, OP_ADD_IMM);
1673 temp->inst_imm = ins->inst_offset & ~0x1ff;
1674 temp->sreg1 = ins->inst_destbasereg;
1675 temp->dreg = mono_regstate_next_int (cfg->rs);
1676 ins->inst_destbasereg = temp->dreg;
1677 ins->inst_offset = low_imm;
1680 /*g_print ("fail with: %d (%d, %d)\n", ins->inst_offset, ins->inst_offset & ~0x1ff, low_imm);*/
1681 /* VFP/FPA doesn't have indexed store instructions */
1682 g_assert_not_reached ();
1684 case OP_STORE_MEMBASE_IMM:
1685 case OP_STOREI1_MEMBASE_IMM:
1686 case OP_STOREI2_MEMBASE_IMM:
1687 case OP_STOREI4_MEMBASE_IMM:
1688 NEW_INS (cfg, temp, OP_ICONST);
1689 temp->inst_c0 = ins->inst_imm;
1690 temp->dreg = mono_regstate_next_int (cfg->rs);
1691 ins->sreg1 = temp->dreg;
1692 ins->opcode = map_to_reg_reg_op (ins->opcode);
1694 goto loop_start; /* make it handle the possibly big ins->inst_offset */
1699 bb->last_ins = last_ins;
1700 bb->max_vreg = cfg->rs->next_vreg;
1705 mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
1709 mono_arch_lowering_pass (cfg, bb);
1710 mono_local_regalloc (cfg, bb);
1714 emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size, gboolean is_signed)
1716 /* sreg is a float, dreg is an integer reg */
1718 ARM_FIXZ (code, dreg, sreg);
1719 #elif defined(ARM_FPU_VFP)
1721 ARM_TOSIZD (code, ARM_VFP_F0, sreg);
1723 ARM_TOUIZD (code, ARM_VFP_F0, sreg);
1724 ARM_FMRS (code, dreg, ARM_VFP_F0);
1728 ARM_AND_REG_IMM8 (code, dreg, dreg, 0xff);
1729 else if (size == 2) {
1730 ARM_SHL_IMM (code, dreg, dreg, 16);
1731 ARM_SHR_IMM (code, dreg, dreg, 16);
1735 ARM_SHL_IMM (code, dreg, dreg, 24);
1736 ARM_SAR_IMM (code, dreg, dreg, 24);
1737 } else if (size == 2) {
1738 ARM_SHL_IMM (code, dreg, dreg, 16);
1739 ARM_SAR_IMM (code, dreg, dreg, 16);
1747 const guchar *target;
1752 #define is_call_imm(diff) ((gint)(diff) >= -33554432 && (gint)(diff) <= 33554431)
1755 search_thunk_slot (void *data, int csize, int bsize, void *user_data) {
1756 PatchData *pdata = (PatchData*)user_data;
1757 guchar *code = data;
1758 guint32 *thunks = data;
1759 guint32 *endthunks = (guint32*)(code + bsize);
1761 int difflow, diffhigh;
1763 /* always ensure a call from pdata->code can reach to the thunks without further thunks */
1764 difflow = (char*)pdata->code - (char*)thunks;
1765 diffhigh = (char*)pdata->code - (char*)endthunks;
1766 if (!((is_call_imm (thunks) && is_call_imm (endthunks)) || (is_call_imm (difflow) && is_call_imm (diffhigh))))
1770 * The thunk is composed of 3 words:
1771 * load constant from thunks [2] into ARM_IP
1774 * Note that the LR register is already setup
1776 //g_print ("thunk nentries: %d\n", ((char*)endthunks - (char*)thunks)/16);
1777 if ((pdata->found == 2) || (pdata->code >= code && pdata->code <= code + csize)) {
1778 while (thunks < endthunks) {
1779 //g_print ("looking for target: %p at %p (%08x-%08x)\n", pdata->target, thunks, thunks [0], thunks [1]);
1780 if (thunks [2] == (guint32)pdata->target) {
1781 arm_patch (pdata->code, (guchar*)thunks);
1782 mono_arch_flush_icache (pdata->code, 4);
1785 } else if ((thunks [0] == 0) && (thunks [1] == 0) && (thunks [2] == 0)) {
1786 /* found a free slot instead: emit thunk */
1787 /* ARMREG_IP is fine to use since this can't be an IMT call
1790 code = (guchar*)thunks;
1791 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
1792 if (thumb_supported)
1793 ARM_BX (code, ARMREG_IP);
1795 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
1796 thunks [2] = (guint32)pdata->target;
1797 mono_arch_flush_icache ((guchar*)thunks, 12);
1799 arm_patch (pdata->code, (guchar*)thunks);
1800 mono_arch_flush_icache (pdata->code, 4);
1804 /* skip 12 bytes, the size of the thunk */
1808 //g_print ("failed thunk lookup for %p from %p at %p (%d entries)\n", pdata->target, pdata->code, data, count);
1814 handle_thunk (int absolute, guchar *code, const guchar *target) {
1815 MonoDomain *domain = mono_domain_get ();
1819 pdata.target = target;
1820 pdata.absolute = absolute;
1823 mono_domain_lock (domain);
1824 mono_code_manager_foreach (domain->code_mp, search_thunk_slot, &pdata);
1827 /* this uses the first available slot */
1829 mono_code_manager_foreach (domain->code_mp, search_thunk_slot, &pdata);
1831 mono_domain_unlock (domain);
1833 if (pdata.found != 1)
1834 g_print ("thunk failed for %p from %p\n", target, code);
1835 g_assert (pdata.found == 1);
1839 arm_patch (guchar *code, const guchar *target)
1841 guint32 *code32 = (void*)code;
1842 guint32 ins = *code32;
1843 guint32 prim = (ins >> 25) & 7;
1844 guint32 tval = GPOINTER_TO_UINT (target);
1846 //g_print ("patching 0x%08x (0x%08x) to point to 0x%08x\n", code, ins, target);
1847 if (prim == 5) { /* 101b */
1848 /* the diff starts 8 bytes from the branch opcode */
1849 gint diff = target - code - 8;
1851 gint tmask = 0xffffffff;
1852 if (tval & 1) { /* entering thumb mode */
1853 diff = target - 1 - code - 8;
1854 g_assert (thumb_supported);
1855 tbits = 0xf << 28; /* bl->blx bit pattern */
1856 g_assert ((ins & (1 << 24))); /* it must be a bl, not b instruction */
1857 /* this low bit of the displacement is moved to bit 24 in the instruction encoding */
1861 tmask = ~(1 << 24); /* clear the link bit */
1862 /*g_print ("blx to thumb: target: %p, code: %p, diff: %d, mask: %x\n", target, code, diff, tmask);*/
1867 if (diff <= 33554431) {
1869 ins = (ins & 0xff000000) | diff;
1871 *code32 = ins | tbits;
1875 /* diff between 0 and -33554432 */
1876 if (diff >= -33554432) {
1878 ins = (ins & 0xff000000) | (diff & ~0xff000000);
1880 *code32 = ins | tbits;
1885 handle_thunk (TRUE, code, target);
1890 * The alternative call sequences looks like this:
1892 * ldr ip, [pc] // loads the address constant
1893 * b 1f // jumps around the constant
1894 * address constant embedded in the code
1899 * There are two cases for patching:
1900 * a) at the end of method emission: in this case code points to the start
1901 * of the call sequence
1902 * b) during runtime patching of the call site: in this case code points
1903 * to the mov pc, ip instruction
1905 * We have to handle also the thunk jump code sequence:
1909 * address constant // execution never reaches here
1911 if ((ins & 0x0ffffff0) == 0x12fff10) {
1912 /* Branch and exchange: the address is constructed in a reg
1913 * We can patch BX when the code sequence is the following:
1914 * ldr ip, [pc, #0] ; 0x8
1921 guint8 *emit = (guint8*)ccode;
1922 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
1924 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
1925 ARM_BX (emit, ARMREG_IP);
1927 /*patching from magic trampoline*/
1928 if (ins == ccode [3]) {
1929 g_assert (code32 [-4] == ccode [0]);
1930 g_assert (code32 [-3] == ccode [1]);
1931 g_assert (code32 [-1] == ccode [2]);
1932 code32 [-2] = (guint32)target;
1935 /*patching from JIT*/
1936 if (ins == ccode [0]) {
1937 g_assert (code32 [1] == ccode [1]);
1938 g_assert (code32 [3] == ccode [2]);
1939 g_assert (code32 [4] == ccode [3]);
1940 code32 [2] = (guint32)target;
1943 g_assert_not_reached ();
1946 guint32 *tmp = ccode;
1947 guint8 *emit = (guint8*)tmp;
1948 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
1949 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
1950 ARM_MOV_REG_REG (emit, ARMREG_PC, ARMREG_IP);
1951 ARM_BX (emit, ARMREG_IP);
1952 if (ins == ccode [2]) {
1953 g_assert_not_reached (); // should be -2 ...
1954 code32 [-1] = (guint32)target;
1957 if (ins == ccode [0]) {
1958 /* handles both thunk jump code and the far call sequence */
1959 code32 [2] = (guint32)target;
1962 g_assert_not_reached ();
1964 // g_print ("patched with 0x%08x\n", ins);
1968 * Return the >= 0 uimm8 value if val can be represented with a byte + rotation
1969 * (with the rotation amount in *rot_amount. rot_amount is already adjusted
1970 * to be used with the emit macros.
1971 * Return -1 otherwise.
1974 mono_arm_is_rotated_imm8 (guint32 val, gint *rot_amount)
1977 for (i = 0; i < 31; i+= 2) {
1978 res = (val << (32 - i)) | (val >> i);
1981 *rot_amount = i? 32 - i: 0;
1988 * Emits in code a sequence of instructions that load the value 'val'
1989 * into the dreg register. Uses at most 4 instructions.
1992 mono_arm_emit_load_imm (guint8 *code, int dreg, guint32 val)
1994 int imm8, rot_amount;
1996 ARM_LDR_IMM (code, dreg, ARMREG_PC, 0);
1997 /* skip the constant pool */
2003 if ((imm8 = mono_arm_is_rotated_imm8 (val, &rot_amount)) >= 0) {
2004 ARM_MOV_REG_IMM (code, dreg, imm8, rot_amount);
2005 } else if ((imm8 = mono_arm_is_rotated_imm8 (~val, &rot_amount)) >= 0) {
2006 ARM_MVN_REG_IMM (code, dreg, imm8, rot_amount);
2009 ARM_MOV_REG_IMM8 (code, dreg, (val & 0xFF));
2011 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF00) >> 8, 24);
2013 if (val & 0xFF0000) {
2014 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
2016 if (val & 0xFF000000) {
2017 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2019 } else if (val & 0xFF00) {
2020 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF00) >> 8, 24);
2021 if (val & 0xFF0000) {
2022 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
2024 if (val & 0xFF000000) {
2025 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2027 } else if (val & 0xFF0000) {
2028 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF0000) >> 16, 16);
2029 if (val & 0xFF000000) {
2030 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2033 //g_assert_not_reached ();
2039 mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
2044 guint8 *code = cfg->native_code + cfg->code_len;
2045 MonoInst *last_ins = NULL;
2046 guint last_offset = 0;
2048 int imm8, rot_amount;
2050 if (cfg->opt & MONO_OPT_PEEPHOLE)
2051 peephole_pass (cfg, bb);
2053 /* we don't align basic blocks of loops on arm */
2055 if (cfg->verbose_level > 2)
2056 g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset);
2058 cpos = bb->max_offset;
2060 if (cfg->prof_options & MONO_PROFILE_COVERAGE) {
2061 //MonoCoverageInfo *cov = mono_get_coverage_info (cfg->method);
2062 //g_assert (!mono_compile_aot);
2065 // cov->data [bb->dfn].iloffset = bb->cil_code - cfg->cil_code;
2066 /* this is not thread save, but good enough */
2067 /* fixme: howto handle overflows? */
2068 //x86_inc_mem (code, &cov->data [bb->dfn].count);
2073 offset = code - cfg->native_code;
2075 max_len = ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
2077 if (offset > (cfg->code_size - max_len - 16)) {
2078 cfg->code_size *= 2;
2079 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
2080 code = cfg->native_code + offset;
2082 // if (ins->cil_code)
2083 // g_print ("cil code\n");
2084 mono_debug_record_line_number (cfg, ins, offset);
2086 switch (ins->opcode) {
2087 case OP_MEMORY_BARRIER:
2090 g_assert_not_reached ();
2093 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2094 ppc_mulhw (code, ppc_r3, ins->sreg1, ins->sreg2);
2097 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2098 ppc_mulhwu (code, ppc_r3, ins->sreg1, ins->sreg2);
2100 case OP_STOREI1_MEMBASE_IMM:
2101 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFF);
2102 g_assert (arm_is_imm12 (ins->inst_offset));
2103 ARM_STRB_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2105 case OP_STOREI2_MEMBASE_IMM:
2106 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFFFF);
2107 g_assert (arm_is_imm8 (ins->inst_offset));
2108 ARM_STRH_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2110 case OP_STORE_MEMBASE_IMM:
2111 case OP_STOREI4_MEMBASE_IMM:
2112 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm);
2113 g_assert (arm_is_imm12 (ins->inst_offset));
2114 ARM_STR_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2116 case OP_STOREI1_MEMBASE_REG:
2117 g_assert (arm_is_imm12 (ins->inst_offset));
2118 ARM_STRB_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2120 case OP_STOREI2_MEMBASE_REG:
2121 g_assert (arm_is_imm8 (ins->inst_offset));
2122 ARM_STRH_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2124 case OP_STORE_MEMBASE_REG:
2125 case OP_STOREI4_MEMBASE_REG:
2126 /* this case is special, since it happens for spill code after lowering has been called */
2127 if (arm_is_imm12 (ins->inst_offset)) {
2128 ARM_STR_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2130 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2131 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ARMREG_LR);
2134 case OP_STOREI1_MEMINDEX:
2135 ARM_STRB_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2137 case OP_STOREI2_MEMINDEX:
2138 /* note: the args are reversed in the macro */
2139 ARM_STRH_REG_REG (code, ins->inst_destbasereg, ins->sreg1, ins->sreg2);
2141 case OP_STORE_MEMINDEX:
2142 case OP_STOREI4_MEMINDEX:
2143 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2148 g_assert_not_reached ();
2151 g_assert_not_reached ();
2153 case OP_LOAD_MEMINDEX:
2154 case OP_LOADI4_MEMINDEX:
2155 case OP_LOADU4_MEMINDEX:
2156 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2158 case OP_LOADI1_MEMINDEX:
2159 /* note: the args are reversed in the macro */
2160 ARM_LDRSB_REG_REG (code, ins->inst_basereg, ins->dreg, ins->sreg2);
2162 case OP_LOADU1_MEMINDEX:
2163 ARM_LDRB_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2165 case OP_LOADI2_MEMINDEX:
2166 /* note: the args are reversed in the macro */
2167 ARM_LDRSH_REG_REG (code, ins->inst_basereg, ins->dreg, ins->sreg2);
2169 case OP_LOADU2_MEMINDEX:
2170 /* note: the args are reversed in the macro */
2171 ARM_LDRH_REG_REG (code, ins->inst_basereg, ins->dreg, ins->sreg2);
2173 case OP_LOAD_MEMBASE:
2174 case OP_LOADI4_MEMBASE:
2175 case OP_LOADU4_MEMBASE:
2176 /* this case is special, since it happens for spill code after lowering has been called */
2177 if (arm_is_imm12 (ins->inst_offset)) {
2178 ARM_LDR_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2180 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2181 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ARMREG_LR);
2184 case OP_LOADI1_MEMBASE:
2185 g_assert (arm_is_imm8 (ins->inst_offset));
2186 ARM_LDRSB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2188 case OP_LOADU1_MEMBASE:
2189 g_assert (arm_is_imm12 (ins->inst_offset));
2190 ARM_LDRB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2192 case OP_LOADU2_MEMBASE:
2193 g_assert (arm_is_imm8 (ins->inst_offset));
2194 ARM_LDRH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2196 case OP_LOADI2_MEMBASE:
2197 g_assert (arm_is_imm8 (ins->inst_offset));
2198 ARM_LDRSH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2201 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 24);
2202 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 24);
2205 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2206 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 16);
2209 ARM_AND_REG_IMM8 (code, ins->dreg, ins->sreg1, 0xff);
2212 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2213 ARM_SHR_IMM (code, ins->dreg, ins->dreg, 16);
2216 ARM_CMP_REG_REG (code, ins->sreg1, ins->sreg2);
2218 case OP_COMPARE_IMM:
2219 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2220 g_assert (imm8 >= 0);
2221 ARM_CMP_REG_IMM (code, ins->sreg1, imm8, rot_amount);
2224 *(int*)code = 0xe7f001f0;
2225 *(int*)code = 0xef9f0001;
2230 ARM_ADDS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2233 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2236 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2239 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2240 g_assert (imm8 >= 0);
2241 ARM_ADDS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2244 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2245 g_assert (imm8 >= 0);
2246 ARM_ADD_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2249 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2250 g_assert (imm8 >= 0);
2251 ARM_ADCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2254 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2255 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2257 case CEE_ADD_OVF_UN:
2258 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2259 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2262 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2263 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2265 case CEE_SUB_OVF_UN:
2266 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2267 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2269 case OP_ADD_OVF_CARRY:
2270 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2271 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2273 case OP_ADD_OVF_UN_CARRY:
2274 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2275 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2277 case OP_SUB_OVF_CARRY:
2278 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2279 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2281 case OP_SUB_OVF_UN_CARRY:
2282 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2283 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2286 ARM_SUBS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2289 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2290 g_assert (imm8 >= 0);
2291 ARM_SUBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2294 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2297 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2300 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2301 g_assert (imm8 >= 0);
2302 ARM_SUB_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2305 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2306 g_assert (imm8 >= 0);
2307 ARM_SBCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2309 case OP_ARM_RSBS_IMM:
2310 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2311 g_assert (imm8 >= 0);
2312 ARM_RSBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2314 case OP_ARM_RSC_IMM:
2315 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2316 g_assert (imm8 >= 0);
2317 ARM_RSC_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2320 ARM_AND_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2323 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2324 g_assert (imm8 >= 0);
2325 ARM_AND_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2333 /* crappy ARM arch doesn't have a DIV instruction */
2334 g_assert_not_reached ();
2336 ARM_ORR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2339 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2340 g_assert (imm8 >= 0);
2341 ARM_ORR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2344 ARM_EOR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2347 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2348 g_assert (imm8 >= 0);
2349 ARM_EOR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2352 ARM_SHL_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2356 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2359 ARM_SAR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2363 ARM_SAR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2367 ARM_SHR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2370 ARM_SHR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2373 ARM_MVN_REG_REG (code, ins->dreg, ins->sreg1);
2376 ARM_RSB_REG_IMM8 (code, ins->dreg, ins->sreg1, 0);
2379 if (ins->dreg == ins->sreg2)
2380 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2382 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg2, ins->sreg1);
2385 g_assert_not_reached ();
2388 /* FIXME: handle ovf/ sreg2 != dreg */
2389 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2391 case CEE_MUL_OVF_UN:
2392 /* FIXME: handle ovf/ sreg2 != dreg */
2393 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2397 code = mono_arm_emit_load_imm (code, ins->dreg, ins->inst_c0);
2400 g_assert_not_reached ();
2401 mono_add_patch_info (cfg, offset, (MonoJumpInfoType)ins->inst_i1, ins->inst_p0);
2407 if (ins->dreg != ins->sreg1)
2408 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2411 int saved = ins->sreg2;
2412 if (ins->sreg2 == ARM_LSW_REG) {
2413 ARM_MOV_REG_REG (code, ARMREG_LR, ins->sreg2);
2416 if (ins->sreg1 != ARM_LSW_REG)
2417 ARM_MOV_REG_REG (code, ARM_LSW_REG, ins->sreg1);
2418 if (saved != ARM_MSW_REG)
2419 ARM_MOV_REG_REG (code, ARM_MSW_REG, saved);
2425 ARM_MVFD (code, ins->dreg, ins->sreg1);
2426 #elif defined(ARM_FPU_VFP)
2427 ARM_CPYD (code, ins->dreg, ins->sreg1);
2430 case OP_FCONV_TO_R4:
2432 ARM_MVFS (code, ins->dreg, ins->sreg1);
2433 #elif defined(ARM_FPU_VFP)
2434 ARM_CVTD (code, ins->dreg, ins->sreg1);
2435 ARM_CVTS (code, ins->dreg, ins->dreg);
2440 * Keep in sync with mono_arch_emit_epilog
2442 g_assert (!cfg->method->save_lmf);
2443 code = emit_big_add (code, ARMREG_SP, cfg->frame_reg, cfg->stack_usage);
2444 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP)) | ((1 << ARMREG_LR)));
2445 mono_add_patch_info (cfg, (guint8*) code - cfg->native_code, MONO_PATCH_INFO_METHOD_JUMP, ins->inst_p0);
2449 /* ensure ins->sreg1 is not NULL */
2450 ARM_LDR_IMM (code, ARMREG_LR, ins->sreg1, 0);
2454 if (ppc_is_imm16 (cfg->sig_cookie + cfg->stack_usage)) {
2455 ppc_addi (code, ppc_r11, cfg->frame_reg, cfg->sig_cookie + cfg->stack_usage);
2457 ppc_load (code, ppc_r11, cfg->sig_cookie + cfg->stack_usage);
2458 ppc_add (code, ppc_r11, cfg->frame_reg, ppc_r11);
2460 ppc_stw (code, ppc_r11, 0, ins->sreg1);
2469 call = (MonoCallInst*)ins;
2470 if (ins->flags & MONO_INST_HAS_METHOD)
2471 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, call->method);
2473 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, call->fptr);
2474 code = emit_call_seq (cfg, code);
2479 case OP_VOIDCALL_REG:
2481 code = emit_call_reg (code, ins->sreg1);
2483 case OP_FCALL_MEMBASE:
2484 case OP_LCALL_MEMBASE:
2485 case OP_VCALL_MEMBASE:
2486 case OP_VOIDCALL_MEMBASE:
2487 case OP_CALL_MEMBASE:
2488 g_assert (arm_is_imm12 (ins->inst_offset));
2489 g_assert (ins->sreg1 != ARMREG_LR);
2490 call = (MonoCallInst*)ins;
2491 if (call->method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
2492 ARM_ADD_REG_IMM8 (code, ARMREG_LR, ARMREG_PC, 4);
2493 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
2494 *((gpointer*)code) = (gpointer)call->method;
2497 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
2498 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
2502 g_assert_not_reached ();
2505 /* keep alignment */
2506 int alloca_waste = cfg->param_area;
2509 /* round the size to 8 bytes */
2510 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->sreg1, 7);
2511 ARM_BIC_REG_IMM8 (code, ins->dreg, ins->sreg1, 7);
2512 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->dreg, alloca_waste);
2513 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ins->dreg);
2514 /* memzero the area: dreg holds the size, sp is the pointer */
2515 if (ins->flags & MONO_INST_INIT) {
2516 guint8 *start_loop, *branch_to_cond;
2517 ARM_MOV_REG_IMM8 (code, ARMREG_LR, 0);
2518 branch_to_cond = code;
2521 ARM_STR_REG_REG (code, ARMREG_LR, ARMREG_SP, ins->dreg);
2522 arm_patch (branch_to_cond, code);
2523 /* decrement by 4 and set flags */
2524 ARM_SUBS_REG_IMM8 (code, ins->dreg, ins->dreg, 4);
2525 ARM_B_COND (code, ARMCOND_LT, 0);
2526 arm_patch (code - 4, start_loop);
2528 ARM_ADD_REG_IMM8 (code, ins->dreg, ARMREG_SP, alloca_waste);
2532 g_assert_not_reached ();
2533 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_LR);
2536 if (ins->sreg1 != ARMREG_R0)
2537 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
2538 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2539 (gpointer)"mono_arch_throw_exception");
2540 code = emit_call_seq (cfg, code);
2544 if (ins->sreg1 != ARMREG_R0)
2545 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
2546 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2547 (gpointer)"mono_arch_rethrow_exception");
2548 code = emit_call_seq (cfg, code);
2551 case OP_START_HANDLER:
2552 if (arm_is_imm12 (ins->inst_left->inst_offset)) {
2553 ARM_STR_IMM (code, ARMREG_LR, ins->inst_left->inst_basereg, ins->inst_left->inst_offset);
2555 code = mono_arm_emit_load_imm (code, ARMREG_IP, ins->inst_left->inst_offset);
2556 ARM_STR_REG_REG (code, ARMREG_LR, ins->inst_left->inst_basereg, ARMREG_IP);
2560 if (ins->sreg1 != ARMREG_R0)
2561 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
2562 if (arm_is_imm12 (ins->inst_left->inst_offset)) {
2563 ARM_LDR_IMM (code, ARMREG_IP, ins->inst_left->inst_basereg, ins->inst_left->inst_offset);
2565 g_assert (ARMREG_IP != ins->inst_left->inst_basereg);
2566 code = mono_arm_emit_load_imm (code, ARMREG_IP, ins->inst_left->inst_offset);
2567 ARM_LDR_REG_REG (code, ARMREG_IP, ins->inst_left->inst_basereg, ARMREG_IP);
2569 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
2572 if (arm_is_imm12 (ins->inst_left->inst_offset)) {
2573 ARM_LDR_IMM (code, ARMREG_IP, ins->inst_left->inst_basereg, ins->inst_left->inst_offset);
2575 g_assert (ARMREG_IP != ins->inst_left->inst_basereg);
2576 code = mono_arm_emit_load_imm (code, ARMREG_IP, ins->inst_left->inst_offset);
2577 ARM_LDR_REG_REG (code, ARMREG_IP, ins->inst_left->inst_basereg, ARMREG_IP);
2579 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
2581 case OP_CALL_HANDLER:
2582 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb);
2586 ins->inst_c0 = code - cfg->native_code;
2589 if (ins->flags & MONO_INST_BRLABEL) {
2590 /*if (ins->inst_i0->inst_c0) {
2592 //x86_jump_code (code, cfg->native_code + ins->inst_i0->inst_c0);
2594 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_LABEL, ins->inst_i0);
2598 /*if (ins->inst_target_bb->native_offset) {
2600 //x86_jump_code (code, cfg->native_code + ins->inst_target_bb->native_offset);
2602 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb);
2608 ARM_MOV_REG_REG (code, ARMREG_PC, ins->sreg1);
2612 * In the normal case we have:
2613 * ldr pc, [pc, ins->sreg1 << 2]
2616 * ldr lr, [pc, ins->sreg1 << 2]
2618 * After follows the data.
2619 * FIXME: add aot support.
2621 max_len += 4 * GPOINTER_TO_INT (ins->klass);
2622 if (offset > (cfg->code_size - max_len - 16)) {
2623 cfg->code_size += max_len;
2624 cfg->code_size *= 2;
2625 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
2626 code = cfg->native_code + offset;
2628 ARM_LDR_REG_REG_SHIFT (code, ARMREG_PC, ARMREG_PC, ins->sreg1, ARMSHIFT_LSL, 2);
2630 code += 4 * GPOINTER_TO_INT (ins->klass);
2633 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
2634 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
2637 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2638 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LT);
2641 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2642 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LO);
2645 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2646 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_GT);
2649 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2650 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_HI);
2652 case OP_COND_EXC_EQ:
2653 case OP_COND_EXC_NE_UN:
2654 case OP_COND_EXC_LT:
2655 case OP_COND_EXC_LT_UN:
2656 case OP_COND_EXC_GT:
2657 case OP_COND_EXC_GT_UN:
2658 case OP_COND_EXC_GE:
2659 case OP_COND_EXC_GE_UN:
2660 case OP_COND_EXC_LE:
2661 case OP_COND_EXC_LE_UN:
2662 EMIT_COND_SYSTEM_EXCEPTION (ins->opcode - OP_COND_EXC_EQ, ins->inst_p1);
2665 case OP_COND_EXC_OV:
2666 case OP_COND_EXC_NC:
2667 case OP_COND_EXC_NO:
2668 g_assert_not_reached ();
2680 EMIT_COND_BRANCH (ins, ins->opcode - CEE_BEQ);
2683 /* floating point opcodes */
2686 /* FIXME: we can optimize the imm load by dealing with part of
2687 * the displacement in LDFD (aligning to 512).
2689 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
2690 ARM_LDFD (code, ins->dreg, ARMREG_LR, 0);
2693 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
2694 ARM_LDFS (code, ins->dreg, ARMREG_LR, 0);
2696 case OP_STORER8_MEMBASE_REG:
2697 g_assert (arm_is_fpimm8 (ins->inst_offset));
2698 ARM_STFD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2700 case OP_LOADR8_MEMBASE:
2701 g_assert (arm_is_fpimm8 (ins->inst_offset));
2702 ARM_LDFD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2704 case OP_STORER4_MEMBASE_REG:
2705 g_assert (arm_is_fpimm8 (ins->inst_offset));
2706 ARM_STFS (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2708 case OP_LOADR4_MEMBASE:
2709 g_assert (arm_is_fpimm8 (ins->inst_offset));
2710 ARM_LDFS (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2712 case CEE_CONV_R_UN: {
2714 tmpreg = ins->dreg == 0? 1: 0;
2715 ARM_CMP_REG_IMM8 (code, ins->sreg1, 0);
2716 ARM_FLTD (code, ins->dreg, ins->sreg1);
2717 ARM_B_COND (code, ARMCOND_GE, 8);
2718 /* save the temp register */
2719 ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
2720 ARM_STFD (code, tmpreg, ARMREG_SP, 0);
2721 ARM_LDFD (code, tmpreg, ARMREG_PC, 12);
2722 ARM_FPA_ADFD (code, ins->dreg, ins->dreg, tmpreg);
2723 ARM_LDFD (code, tmpreg, ARMREG_SP, 0);
2724 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
2725 /* skip the constant pool */
2728 *(int*)code = 0x41f00000;
2733 * ldfltd ftemp, [pc, #8] 0x41f00000 0x00000000
2734 * adfltd fdest, fdest, ftemp
2739 ARM_FLTS (code, ins->dreg, ins->sreg1);
2742 ARM_FLTD (code, ins->dreg, ins->sreg1);
2744 #elif defined(ARM_FPU_VFP)
2746 /* FIXME: we can optimize the imm load by dealing with part of
2747 * the displacement in LDFD (aligning to 512).
2749 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
2750 ARM_FLDD (code, ins->dreg, ARMREG_LR, 0);
2753 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
2754 ARM_FLDS (code, ins->dreg, ARMREG_LR, 0);
2755 ARM_CVTS (code, ins->dreg, ins->dreg);
2757 case OP_STORER8_MEMBASE_REG:
2758 g_assert (arm_is_fpimm8 (ins->inst_offset));
2759 ARM_FSTD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2761 case OP_LOADR8_MEMBASE:
2762 g_assert (arm_is_fpimm8 (ins->inst_offset));
2763 ARM_FLDD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2765 case OP_STORER4_MEMBASE_REG:
2766 g_assert (arm_is_fpimm8 (ins->inst_offset));
2767 ARM_FSTS (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2769 case OP_LOADR4_MEMBASE:
2770 g_assert (arm_is_fpimm8 (ins->inst_offset));
2771 ARM_FLDS (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2773 case CEE_CONV_R_UN: {
2774 g_assert_not_reached ();
2778 g_assert_not_reached ();
2779 //ARM_FLTS (code, ins->dreg, ins->sreg1);
2782 g_assert_not_reached ();
2783 //ARM_FLTD (code, ins->dreg, ins->sreg1);
2786 case OP_FCONV_TO_I1:
2787 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, TRUE);
2789 case OP_FCONV_TO_U1:
2790 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, FALSE);
2792 case OP_FCONV_TO_I2:
2793 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, TRUE);
2795 case OP_FCONV_TO_U2:
2796 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, FALSE);
2798 case OP_FCONV_TO_I4:
2800 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, TRUE);
2802 case OP_FCONV_TO_U4:
2804 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, FALSE);
2806 case OP_FCONV_TO_I8:
2807 case OP_FCONV_TO_U8:
2808 g_assert_not_reached ();
2809 /* Implemented as helper calls */
2811 case OP_LCONV_TO_R_UN:
2812 g_assert_not_reached ();
2813 /* Implemented as helper calls */
2815 case OP_LCONV_TO_OVF_I: {
2817 guint32 *negative_branch, *msword_positive_branch, *msword_negative_branch, *ovf_ex_target;
2818 // Check if its negative
2819 ppc_cmpi (code, 0, 0, ins->sreg1, 0);
2820 negative_branch = code;
2821 ppc_bc (code, PPC_BR_TRUE, PPC_BR_LT, 0);
2822 // Its positive msword == 0
2823 ppc_cmpi (code, 0, 0, ins->sreg2, 0);
2824 msword_positive_branch = code;
2825 ppc_bc (code, PPC_BR_TRUE, PPC_BR_EQ, 0);
2827 ovf_ex_target = code;
2828 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_ALWAYS, 0, "OverflowException");
2830 ppc_patch (negative_branch, code);
2831 ppc_cmpi (code, 0, 0, ins->sreg2, -1);
2832 msword_negative_branch = code;
2833 ppc_bc (code, PPC_BR_FALSE, PPC_BR_EQ, 0);
2834 ppc_patch (msword_negative_branch, ovf_ex_target);
2836 ppc_patch (msword_positive_branch, code);
2837 if (ins->dreg != ins->sreg1)
2838 ppc_mr (code, ins->dreg, ins->sreg1);
2840 if (ins->dreg != ins->sreg1)
2841 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2846 ARM_FPA_ADFD (code, ins->dreg, ins->sreg1, ins->sreg2);
2849 ARM_FPA_SUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
2852 ARM_FPA_MUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
2855 ARM_FPA_DVFD (code, ins->dreg, ins->sreg1, ins->sreg2);
2858 ARM_MNFD (code, ins->dreg, ins->sreg1);
2860 #elif defined(ARM_FPU_VFP)
2862 ARM_VFP_ADDD (code, ins->dreg, ins->sreg1, ins->sreg2);
2865 ARM_VFP_SUBD (code, ins->dreg, ins->sreg1, ins->sreg2);
2868 ARM_VFP_MULD (code, ins->dreg, ins->sreg1, ins->sreg2);
2871 ARM_VFP_DIVD (code, ins->dreg, ins->sreg1, ins->sreg2);
2874 ARM_NEGD (code, ins->dreg, ins->sreg1);
2879 g_assert_not_reached ();
2882 /* each fp compare op needs to do its own */
2883 g_assert_not_reached ();
2884 //ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2888 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2889 #elif defined(ARM_FPU_VFP)
2890 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2892 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
2893 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
2897 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2898 #elif defined(ARM_FPU_VFP)
2899 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2901 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2902 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
2906 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2907 #elif defined(ARM_FPU_VFP)
2908 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2910 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2911 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
2912 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
2917 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
2918 #elif defined(ARM_FPU_VFP)
2919 ARM_CMPD (code, ins->sreg2, ins->sreg1);
2921 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2922 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
2927 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
2928 #elif defined(ARM_FPU_VFP)
2929 ARM_CMPD (code, ins->sreg2, ins->sreg1);
2931 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2932 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
2933 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
2935 /* ARM FPA flags table:
2936 * N Less than ARMCOND_MI
2937 * Z Equal ARMCOND_EQ
2938 * C Greater Than or Equal ARMCOND_CS
2939 * V Unordered ARMCOND_VS
2943 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2944 #elif defined(ARM_FPU_VFP)
2945 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2947 EMIT_COND_BRANCH (ins, CEE_BEQ - CEE_BEQ);
2951 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2952 #elif defined(ARM_FPU_VFP)
2953 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2955 EMIT_COND_BRANCH (ins, CEE_BNE_UN - CEE_BEQ);
2959 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2960 #elif defined(ARM_FPU_VFP)
2961 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2963 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
2967 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2968 #elif defined(ARM_FPU_VFP)
2969 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2971 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
2972 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
2976 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
2977 #elif defined(ARM_FPU_VFP)
2978 ARM_CMPD (code, ins->sreg2, ins->sreg1);
2980 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set, swapped args */
2984 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
2985 #elif defined(ARM_FPU_VFP)
2986 ARM_CMPD (code, ins->sreg2, ins->sreg1);
2988 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
2989 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set, swapped args */
2993 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2994 #elif defined(ARM_FPU_VFP)
2995 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2997 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_CS);
3001 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3002 #elif defined(ARM_FPU_VFP)
3003 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3005 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3006 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_GE);
3010 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3011 #elif defined(ARM_FPU_VFP)
3012 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3014 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_CS); /* swapped */
3018 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3019 #elif defined(ARM_FPU_VFP)
3020 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3022 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3023 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_GE); /* swapped */
3026 /*ppc_stfd (code, ins->sreg1, -8, ppc_sp);
3027 ppc_lwz (code, ppc_r11, -8, ppc_sp);
3028 ppc_rlwinm (code, ppc_r11, ppc_r11, 0, 1, 31);
3029 ppc_addis (code, ppc_r11, ppc_r11, -32752);
3030 ppc_rlwinmd (code, ppc_r11, ppc_r11, 1, 31, 31);
3031 EMIT_COND_SYSTEM_EXCEPTION (CEE_BEQ - CEE_BEQ, "ArithmeticException");*/
3032 g_assert_not_reached ();
3036 g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
3037 g_assert_not_reached ();
3040 if ((cfg->opt & MONO_OPT_BRANCH) && ((code - cfg->native_code - offset) > max_len)) {
3041 g_warning ("wrong maximal instruction length of instruction %s (expected %d, got %d)",
3042 mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset);
3043 g_assert_not_reached ();
3049 last_offset = offset;
3054 cfg->code_len = code - cfg->native_code;
3058 mono_arch_register_lowlevel_calls (void)
3062 #define patch_lis_ori(ip,val) do {\
3063 guint16 *__lis_ori = (guint16*)(ip); \
3064 __lis_ori [1] = (((guint32)(val)) >> 16) & 0xffff; \
3065 __lis_ori [3] = ((guint32)(val)) & 0xffff; \
3069 mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, gboolean run_cctors)
3071 MonoJumpInfo *patch_info;
3073 for (patch_info = ji; patch_info; patch_info = patch_info->next) {
3074 unsigned char *ip = patch_info->ip.i + code;
3075 const unsigned char *target;
3077 if (patch_info->type == MONO_PATCH_INFO_SWITCH) {
3078 gpointer *jt = (gpointer*)(ip + 8);
3080 /* jt is the inlined jump table, 2 instructions after ip
3081 * In the normal case we store the absolute addresses,
3082 * otherwise the displacements.
3084 for (i = 0; i < patch_info->data.table->table_size; i++) {
3085 jt [i] = code + (int)patch_info->data.table->table [i];
3089 target = mono_resolve_patch_target (method, domain, code, patch_info, run_cctors);
3091 switch (patch_info->type) {
3092 case MONO_PATCH_INFO_IP:
3093 g_assert_not_reached ();
3094 patch_lis_ori (ip, ip);
3096 case MONO_PATCH_INFO_METHOD_REL:
3097 g_assert_not_reached ();
3098 *((gpointer *)(ip)) = code + patch_info->data.offset;
3100 case MONO_PATCH_INFO_METHODCONST:
3101 case MONO_PATCH_INFO_CLASS:
3102 case MONO_PATCH_INFO_IMAGE:
3103 case MONO_PATCH_INFO_FIELD:
3104 case MONO_PATCH_INFO_VTABLE:
3105 case MONO_PATCH_INFO_IID:
3106 case MONO_PATCH_INFO_SFLDA:
3107 case MONO_PATCH_INFO_LDSTR:
3108 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
3109 case MONO_PATCH_INFO_LDTOKEN:
3110 g_assert_not_reached ();
3111 /* from OP_AOTCONST : lis + ori */
3112 patch_lis_ori (ip, target);
3114 case MONO_PATCH_INFO_R4:
3115 case MONO_PATCH_INFO_R8:
3116 g_assert_not_reached ();
3117 *((gconstpointer *)(ip + 2)) = patch_info->data.target;
3119 case MONO_PATCH_INFO_EXC_NAME:
3120 g_assert_not_reached ();
3121 *((gconstpointer *)(ip + 1)) = patch_info->data.name;
3123 case MONO_PATCH_INFO_NONE:
3124 case MONO_PATCH_INFO_BB_OVF:
3125 case MONO_PATCH_INFO_EXC_OVF:
3126 /* everything is dealt with at epilog output time */
3131 arm_patch (ip, target);
3136 * Stack frame layout:
3138 * ------------------- fp
3139 * MonoLMF structure or saved registers
3140 * -------------------
3142 * -------------------
3144 * -------------------
3145 * optional 8 bytes for tracing
3146 * -------------------
3147 * param area size is cfg->param_area
3148 * ------------------- sp
3151 mono_arch_emit_prolog (MonoCompile *cfg)
3153 MonoMethod *method = cfg->method;
3155 MonoMethodSignature *sig;
3157 int alloc_size, pos, max_offset, i, rot_amount;
3164 if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
3167 sig = mono_method_signature (method);
3168 cfg->code_size = 256 + sig->param_count * 20;
3169 code = cfg->native_code = g_malloc (cfg->code_size);
3171 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
3173 alloc_size = cfg->stack_offset;
3176 if (!method->save_lmf) {
3177 ARM_PUSH (code, (cfg->used_int_regs | (1 << ARMREG_IP) | (1 << ARMREG_LR)));
3178 prev_sp_offset = 8; /* ip and lr */
3179 for (i = 0; i < 16; ++i) {
3180 if (cfg->used_int_regs & (1 << i))
3181 prev_sp_offset += 4;
3184 ARM_PUSH (code, 0x5ff0);
3185 prev_sp_offset = 4 * 10; /* all but r0-r3, sp and pc */
3186 pos += sizeof (MonoLMF) - prev_sp_offset;
3190 // align to MONO_ARCH_FRAME_ALIGNMENT bytes
3191 if (alloc_size & (MONO_ARCH_FRAME_ALIGNMENT - 1)) {
3192 alloc_size += MONO_ARCH_FRAME_ALIGNMENT - 1;
3193 alloc_size &= ~(MONO_ARCH_FRAME_ALIGNMENT - 1);
3196 /* the stack used in the pushed regs */
3197 if (prev_sp_offset & 4)
3199 cfg->stack_usage = alloc_size;
3201 if ((i = mono_arm_is_rotated_imm8 (alloc_size, &rot_amount)) >= 0) {
3202 ARM_SUB_REG_IMM (code, ARMREG_SP, ARMREG_SP, i, rot_amount);
3204 code = mono_arm_emit_load_imm (code, ARMREG_IP, alloc_size);
3205 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
3208 if (cfg->frame_reg != ARMREG_SP)
3209 ARM_MOV_REG_REG (code, cfg->frame_reg, ARMREG_SP);
3210 //g_print ("prev_sp_offset: %d, alloc_size:%d\n", prev_sp_offset, alloc_size);
3211 prev_sp_offset += alloc_size;
3213 /* compute max_offset in order to use short forward jumps
3214 * we could skip do it on arm because the immediate displacement
3215 * for jumps is large enough, it may be useful later for constant pools
3218 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
3219 MonoInst *ins = bb->code;
3220 bb->max_offset = max_offset;
3222 if (cfg->prof_options & MONO_PROFILE_COVERAGE)
3226 max_offset += ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
3231 /* load arguments allocated to register from the stack */
3234 cinfo = calculate_sizes (sig, sig->pinvoke);
3236 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
3237 ArgInfo *ainfo = &cinfo->ret;
3239 g_assert (arm_is_imm12 (inst->inst_offset));
3240 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3242 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
3243 ArgInfo *ainfo = cinfo->args + i;
3244 inst = cfg->args [pos];
3246 if (cfg->verbose_level > 2)
3247 g_print ("Saving argument %d (type: %d)\n", i, ainfo->regtype);
3248 if (inst->opcode == OP_REGVAR) {
3249 if (ainfo->regtype == RegTypeGeneral)
3250 ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
3251 else if (ainfo->regtype == RegTypeFP) {
3252 g_assert_not_reached ();
3253 } else if (ainfo->regtype == RegTypeBase) {
3254 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
3255 ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3257 g_assert_not_reached ();
3259 if (cfg->verbose_level > 2)
3260 g_print ("Argument %d assigned to register %s\n", pos, mono_arch_regname (inst->dreg));
3262 /* the argument should be put on the stack: FIXME handle size != word */
3263 if (ainfo->regtype == RegTypeGeneral) {
3264 switch (ainfo->size) {
3266 if (arm_is_imm12 (inst->inst_offset))
3267 ARM_STRB_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3269 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3270 ARM_STRB_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3274 if (arm_is_imm8 (inst->inst_offset)) {
3275 ARM_STRH_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3277 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3278 ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, inst->inst_basereg);
3279 ARM_STRH_IMM (code, ainfo->reg, ARMREG_IP, 0);
3283 g_assert (arm_is_imm12 (inst->inst_offset));
3284 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3285 g_assert (arm_is_imm12 (inst->inst_offset + 4));
3286 ARM_STR_IMM (code, ainfo->reg + 1, inst->inst_basereg, inst->inst_offset + 4);
3289 if (arm_is_imm12 (inst->inst_offset)) {
3290 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3292 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3293 ARM_STR_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3297 } else if (ainfo->regtype == RegTypeBaseGen) {
3298 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
3299 g_assert (arm_is_imm12 (inst->inst_offset));
3300 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3301 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3302 ARM_STR_IMM (code, ARMREG_R3, inst->inst_basereg, inst->inst_offset);
3303 } else if (ainfo->regtype == RegTypeBase) {
3304 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
3305 switch (ainfo->size) {
3307 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3308 g_assert (arm_is_imm12 (inst->inst_offset));
3309 ARM_STRB_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3312 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3313 if (arm_is_imm8 (inst->inst_offset)) {
3314 ARM_STRH_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3316 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3317 ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, inst->inst_basereg);
3318 ARM_STRH_IMM (code, ARMREG_LR, ARMREG_IP, 0);
3322 g_assert (arm_is_imm12 (inst->inst_offset));
3323 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3324 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3325 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset + 4));
3326 g_assert (arm_is_imm12 (inst->inst_offset + 4));
3327 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset + 4));
3328 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3331 g_assert (arm_is_imm12 (inst->inst_offset));
3332 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3333 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3336 } else if (ainfo->regtype == RegTypeFP) {
3337 g_assert_not_reached ();
3338 } else if (ainfo->regtype == RegTypeStructByVal) {
3339 int doffset = inst->inst_offset;
3343 if (mono_class_from_mono_type (inst->inst_vtype))
3344 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), NULL);
3345 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
3346 g_assert (arm_is_imm12 (doffset));
3347 ARM_STR_IMM (code, ainfo->reg + cur_reg, inst->inst_basereg, doffset);
3348 soffset += sizeof (gpointer);
3349 doffset += sizeof (gpointer);
3351 if (ainfo->vtsize) {
3352 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
3353 //g_print ("emit_memcpy (prev_sp_ofs: %d, ainfo->offset: %d, soffset: %d)\n", prev_sp_offset, ainfo->offset, soffset);
3354 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, doffset, ARMREG_SP, prev_sp_offset + ainfo->offset);
3356 } else if (ainfo->regtype == RegTypeStructByAddr) {
3357 g_assert_not_reached ();
3358 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
3359 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, inst->inst_offset, ainfo->reg, 0);
3361 g_assert_not_reached ();
3366 if (method->save_lmf) {
3368 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3369 (gpointer)"mono_get_lmf_addr");
3370 code = emit_call_seq (cfg, code);
3371 /* we build the MonoLMF structure on the stack - see mini-arm.h */
3372 /* lmf_offset is the offset from the previous stack pointer,
3373 * alloc_size is the total stack space allocated, so the offset
3374 * of MonoLMF from the current stack ptr is alloc_size - lmf_offset.
3375 * The pointer to the struct is put in r1 (new_lmf).
3376 * r2 is used as scratch
3377 * The callee-saved registers are already in the MonoLMF structure
3379 code = emit_big_add (code, ARMREG_R1, ARMREG_SP, alloc_size - lmf_offset);
3380 /* r0 is the result from mono_get_lmf_addr () */
3381 ARM_STR_IMM (code, ARMREG_R0, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
3382 /* new_lmf->previous_lmf = *lmf_addr */
3383 ARM_LDR_IMM (code, ARMREG_R2, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3384 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3385 /* *(lmf_addr) = r1 */
3386 ARM_STR_IMM (code, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3387 /* save method info */
3388 code = mono_arm_emit_load_imm (code, ARMREG_R2, GPOINTER_TO_INT (method));
3389 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, method));
3390 ARM_STR_IMM (code, ARMREG_SP, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, ebp));
3391 /* save the current IP */
3392 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_PC);
3393 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, eip));
3397 code = mono_arch_instrument_prolog (cfg, mono_trace_enter_method, code, TRUE);
3399 cfg->code_len = code - cfg->native_code;
3400 g_assert (cfg->code_len < cfg->code_size);
3407 mono_arch_emit_epilog (MonoCompile *cfg)
3409 MonoMethod *method = cfg->method;
3410 int pos, i, rot_amount;
3411 int max_epilog_size = 16 + 20*4;
3414 if (cfg->method->save_lmf)
3415 max_epilog_size += 128;
3417 if (mono_jit_trace_calls != NULL)
3418 max_epilog_size += 50;
3420 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE)
3421 max_epilog_size += 50;
3423 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
3424 cfg->code_size *= 2;
3425 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
3426 mono_jit_stats.code_reallocs++;
3430 * Keep in sync with OP_JMP
3432 code = cfg->native_code + cfg->code_len;
3434 if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) {
3435 code = mono_arch_instrument_epilog (cfg, mono_trace_leave_method, code, TRUE);
3439 if (method->save_lmf) {
3441 /* all but r0-r3, sp and pc */
3442 pos += sizeof (MonoLMF) - (4 * 10);
3444 /* r2 contains the pointer to the current LMF */
3445 code = emit_big_add (code, ARMREG_R2, cfg->frame_reg, cfg->stack_usage - lmf_offset);
3446 /* ip = previous_lmf */
3447 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3449 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
3450 /* *(lmf_addr) = previous_lmf */
3451 ARM_STR_IMM (code, ARMREG_IP, ARMREG_LR, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3452 /* FIXME: speedup: there is no actual need to restore the registers if
3453 * we didn't actually change them (idea from Zoltan).
3456 /* point sp at the registers to restore: 10 is 14 -4, because we skip r0-r3 */
3457 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_R2, (sizeof (MonoLMF) - 10 * sizeof (gulong)));
3458 ARM_POP_NWB (code, 0xaff0); /* restore ip to sp and lr to pc */
3460 if ((i = mono_arm_is_rotated_imm8 (cfg->stack_usage, &rot_amount)) >= 0) {
3461 ARM_ADD_REG_IMM (code, ARMREG_SP, cfg->frame_reg, i, rot_amount);
3463 code = mono_arm_emit_load_imm (code, ARMREG_IP, cfg->stack_usage);
3464 ARM_ADD_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
3466 /* FIXME: add v4 thumb interworking support */
3467 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP) | (1 << ARMREG_PC)));
3470 cfg->code_len = code - cfg->native_code;
3472 g_assert (cfg->code_len < cfg->code_size);
3476 /* remove once throw_exception_by_name is eliminated */
3478 exception_id_by_name (const char *name)
3480 if (strcmp (name, "IndexOutOfRangeException") == 0)
3481 return MONO_EXC_INDEX_OUT_OF_RANGE;
3482 if (strcmp (name, "OverflowException") == 0)
3483 return MONO_EXC_OVERFLOW;
3484 if (strcmp (name, "ArithmeticException") == 0)
3485 return MONO_EXC_ARITHMETIC;
3486 if (strcmp (name, "DivideByZeroException") == 0)
3487 return MONO_EXC_DIVIDE_BY_ZERO;
3488 if (strcmp (name, "InvalidCastException") == 0)
3489 return MONO_EXC_INVALID_CAST;
3490 if (strcmp (name, "NullReferenceException") == 0)
3491 return MONO_EXC_NULL_REF;
3492 if (strcmp (name, "ArrayTypeMismatchException") == 0)
3493 return MONO_EXC_ARRAY_TYPE_MISMATCH;
3494 g_error ("Unknown intrinsic exception %s\n", name);
3499 mono_arch_emit_exceptions (MonoCompile *cfg)
3501 MonoJumpInfo *patch_info;
3504 const guint8* exc_throw_pos [MONO_EXC_INTRINS_NUM] = {NULL};
3505 guint8 exc_throw_found [MONO_EXC_INTRINS_NUM] = {0};
3506 int max_epilog_size = 50;
3508 /* count the number of exception infos */
3511 * make sure we have enough space for exceptions
3512 * 12 is the simulated call to throw_exception_by_name
3514 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
3515 if (patch_info->type == MONO_PATCH_INFO_EXC) {
3516 i = exception_id_by_name (patch_info->data.target);
3517 if (!exc_throw_found [i]) {
3518 max_epilog_size += 12;
3519 exc_throw_found [i] = TRUE;
3524 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
3525 cfg->code_size *= 2;
3526 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
3527 mono_jit_stats.code_reallocs++;
3530 code = cfg->native_code + cfg->code_len;
3532 /* add code to raise exceptions */
3533 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
3534 switch (patch_info->type) {
3535 case MONO_PATCH_INFO_EXC: {
3536 unsigned char *ip = patch_info->ip.i + cfg->native_code;
3537 const char *ex_name = patch_info->data.target;
3538 i = exception_id_by_name (patch_info->data.target);
3539 if (exc_throw_pos [i]) {
3540 arm_patch (ip, exc_throw_pos [i]);
3541 patch_info->type = MONO_PATCH_INFO_NONE;
3544 exc_throw_pos [i] = code;
3546 arm_patch (ip, code);
3547 //*(int*)code = 0xef9f0001;
3549 /*mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC_NAME, patch_info->data.target);*/
3550 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
3551 /* we got here from a conditional call, so the calling ip is set in lr already */
3552 patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD;
3553 patch_info->data.name = "mono_arch_throw_exception_by_name";
3554 patch_info->ip.i = code - cfg->native_code;
3556 *(gconstpointer*)code = ex_name;
3566 cfg->code_len = code - cfg->native_code;
3568 g_assert (cfg->code_len < cfg->code_size);
3573 mono_arch_setup_jit_tls_data (MonoJitTlsData *tls)
3578 mono_arch_free_jit_tls_data (MonoJitTlsData *tls)
3583 mono_arch_emit_this_vret_args (MonoCompile *cfg, MonoCallInst *inst, int this_reg, int this_type, int vt_reg)
3586 int this_dreg = ARMREG_R0;
3589 this_dreg = ARMREG_R1;
3591 /* add the this argument */
3592 if (this_reg != -1) {
3594 MONO_INST_NEW (cfg, this, OP_SETREG);
3595 this->type = this_type;
3596 this->sreg1 = this_reg;
3597 this->dreg = mono_regstate_next_int (cfg->rs);
3598 mono_bblock_add_inst (cfg->cbb, this);
3599 mono_call_inst_add_outarg_reg (cfg, inst, this->dreg, this_dreg, FALSE);
3604 MONO_INST_NEW (cfg, vtarg, OP_SETREG);
3605 vtarg->type = STACK_MP;
3606 vtarg->sreg1 = vt_reg;
3607 vtarg->dreg = mono_regstate_next_int (cfg->rs);
3608 mono_bblock_add_inst (cfg->cbb, vtarg);
3609 mono_call_inst_add_outarg_reg (cfg, inst, vtarg->dreg, ARMREG_R0, FALSE);
3614 mono_arch_get_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
3616 MonoInst *ins = NULL;
3617 if (cmethod->klass == mono_defaults.thread_class &&
3618 strcmp (cmethod->name, "MemoryBarrier") == 0) {
3619 MONO_INST_NEW (cfg, ins, OP_MEMORY_BARRIER);
3625 mono_arch_print_tree (MonoInst *tree, int arity)
3630 MonoInst* mono_arch_get_domain_intrinsic (MonoCompile* cfg)
3636 mono_arch_get_thread_intrinsic (MonoCompile* cfg)
3642 mono_arch_flush_register_windows (void)
3647 mono_arch_fixup_jinfo (MonoCompile *cfg)
3649 /* max encoded stack usage is 64KB * 4 */
3650 g_assert ((cfg->stack_usage & ~(0xffff << 2)) == 0);
3651 cfg->jit_info->used_regs |= cfg->stack_usage << 14;
3654 #ifdef MONO_ARCH_HAVE_IMT
3657 mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call)
3662 mono_arch_find_imt_method (gpointer *regs, guint8 *code)
3664 guint32 *code_ptr = (guint32*)code;
3666 /* The IMT value is stored in the code stream right after the LDC instruction. */
3667 if (!IS_LDR_PC (code_ptr [0])) {
3668 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]);
3669 g_assert (IS_LDR_PC (code_ptr [0]));
3671 return (MonoMethod*) code_ptr [1];
3675 mono_arch_find_this_argument (gpointer *regs, MonoMethod *method)
3677 return mono_arch_get_this_arg_from_call (mono_method_signature (method), (gssize*)regs, NULL);
3681 #define ENABLE_WRONG_METHOD_CHECK 0
3682 #define BASE_SIZE (4 * 4)
3683 #define BSEARCH_ENTRY_SIZE (4 * 4)
3684 #define CMP_SIZE (3 * 4)
3685 #define BRANCH_SIZE (1 * 4)
3686 #define CALL_SIZE (2 * 4)
3687 #define WMC_SIZE (5 * 4)
3688 #define DISTANCE(A, B) (((gint32)(B)) - ((gint32)(A)))
3691 arm_emit_value_and_patch_ldr (arminstr_t *code, arminstr_t *target, guint32 value)
3693 guint32 delta = DISTANCE (target, code);
3695 g_assert (delta >= 0 && delta <= 0xFFF);
3696 *target = *target | delta;
3702 mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count)
3704 int size, i, extra_space = 0;
3705 arminstr_t *code, *start, *vtable_target = NULL;
3708 for (i = 0; i < count; ++i) {
3709 MonoIMTCheckItem *item = imt_entries [i];
3710 if (item->is_equals) {
3711 g_assert (arm_is_imm12 (DISTANCE (vtable, &vtable->vtable[item->vtable_slot])));
3713 if (item->check_target_idx) {
3714 if (!item->compare_done)
3715 item->chunk_size += CMP_SIZE;
3716 item->chunk_size += BRANCH_SIZE;
3718 #if ENABLE_WRONG_METHOD_CHECK
3719 item->chunk_size += WMC_SIZE;
3722 item->chunk_size += CALL_SIZE;
3724 item->chunk_size += BSEARCH_ENTRY_SIZE;
3725 imt_entries [item->check_target_idx]->compare_done = TRUE;
3727 size += item->chunk_size;
3730 start = code = mono_code_manager_reserve (domain->code_mp, size);
3733 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);
3734 for (i = 0; i < count; ++i) {
3735 MonoIMTCheckItem *item = imt_entries [i];
3736 printf ("method %d (%p) %s vtable slot %p is_equals %d chunk size %d\n", i, item->method, item->method->name, &vtable->vtable [item->vtable_slot], item->is_equals, item->chunk_size);
3740 ARM_PUSH2 (code, ARMREG_R0, ARMREG_R1);
3741 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, -4);
3742 vtable_target = code;
3743 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
3745 for (i = 0; i < count; ++i) {
3746 MonoIMTCheckItem *item = imt_entries [i];
3747 arminstr_t *imt_method = NULL;
3748 item->code_target = (guint8*)code;
3750 if (item->is_equals) {
3751 if (item->check_target_idx) {
3752 if (!item->compare_done) {
3754 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
3755 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
3757 item->jmp_code = (guint8*)code;
3758 ARM_B_COND (code, ARMCOND_NE, 0);
3760 ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
3761 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, DISTANCE (vtable, &vtable->vtable[item->vtable_slot]));
3763 /*Enable the commented code to assert on wrong method*/
3764 #if ENABLE_WRONG_METHOD_CHECK
3766 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
3767 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
3768 ARM_B_COND (code, ARMCOND_NE, 1);
3770 ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
3771 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, DISTANCE (vtable, &vtable->vtable[item->vtable_slot]));
3773 #if ENABLE_WRONG_METHOD_CHECK
3779 code = arm_emit_value_and_patch_ldr (code, imt_method, (guint32)item->method);
3781 /*must emit after unconditional branch*/
3782 if (vtable_target) {
3783 code = arm_emit_value_and_patch_ldr (code, vtable_target, (guint32)vtable);
3784 item->chunk_size += 4;
3785 vtable_target = NULL;
3788 /*We reserve the space for bsearch IMT values after the first entry with an absolute jump*/
3790 code += extra_space;
3794 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
3795 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
3797 item->jmp_code = (guint8*)code;
3798 ARM_B_COND (code, ARMCOND_GE, 0);
3803 for (i = 0; i < count; ++i) {
3804 MonoIMTCheckItem *item = imt_entries [i];
3805 if (item->jmp_code) {
3806 if (item->check_target_idx)
3807 arm_patch (item->jmp_code, imt_entries [item->check_target_idx]->code_target);
3809 if (i > 0 && item->is_equals) {
3811 arminstr_t *space_start = (arminstr_t*)(item->code_target + item->chunk_size);
3812 for (j = i - 1; j >= 0 && !imt_entries [j]->is_equals; --j) {
3813 space_start = arm_emit_value_and_patch_ldr (space_start, (arminstr_t*)imt_entries [j]->code_target, (guint32)imt_entries [j]->method);
3820 char *buff = g_strdup_printf ("thunk_for_class_%s_%s_entries_%d", vtable->klass->name_space, vtable->klass->name, count);
3821 mono_disassemble_code (NULL, (guint8*)start, size, buff);
3826 mono_arch_flush_icache ((guint8*)start, size);
3827 mono_stats.imt_thunks_size += code - start;
3829 g_assert (DISTANCE (start, code) <= size);