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, int *displacement)
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);*/
251 *displacement = offset;
256 mono_arch_get_vcall_slot (guint8 *code_ptr, gpointer *regs, int *displacement)
258 guint32* code = (guint32*)code_ptr;
260 /* Locate the address of the method-specific trampoline. The call using
261 the vtable slot that took the processing flow to 'arch_create_jit_trampoline'
262 looks something like this:
271 The call sequence could be also:
274 function pointer literal
278 Note that on ARM5+ we can use one instruction instead of the last two.
279 Therefore, we need to locate the 'ldr rA' instruction to know which
280 register was used to hold the method addrs.
283 /* This is the instruction after "ldc pc, xxx", "mov pc, xxx" or "bl xxx" could be either the IMT value or some other instruction*/
286 /* Three possible code sequences can happen here:
290 * ldr pc, [rX - #offset]
296 * ldr pc, [rX - #offset]
298 * direct branch with bl:
302 * direct branch with mov:
306 * We only need to identify interface and virtual calls, the others can be ignored.
309 if (IS_LDR_PC (code [-1]) && code [-2] == ADD_LR_PC_4)
310 return decode_vcall_slot_from_ldr (code [-1], regs, displacement);
312 if (IS_LDR_PC (code [0]) && code [-1] == MOV_LR_PC)
313 return decode_vcall_slot_from_ldr (code [0], regs, displacement);
319 mono_arch_get_vcall_slot_addr (guint8* code, gpointer *regs)
323 vt = mono_arch_get_vcall_slot (code, regs, &displacement);
326 return (gpointer*)((char*)vt + displacement);
329 #define MAX_ARCH_DELEGATE_PARAMS 3
332 mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target)
334 guint8 *code, *start;
336 /* FIXME: Support more cases */
337 if (MONO_TYPE_ISSTRUCT (sig->ret))
341 static guint8* cached = NULL;
342 mono_mini_arch_lock ();
344 mono_mini_arch_unlock ();
348 start = code = mono_global_codeman_reserve (12);
350 /* Replace the this argument with the target */
351 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, method_ptr));
352 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, target));
353 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
355 g_assert ((code - start) <= 12);
357 mono_arch_flush_icache (code, 12);
359 mono_mini_arch_unlock ();
362 static guint8* cache [MAX_ARCH_DELEGATE_PARAMS + 1] = {NULL};
365 if (sig->param_count > MAX_ARCH_DELEGATE_PARAMS)
367 for (i = 0; i < sig->param_count; ++i)
368 if (!mono_is_regsize_var (sig->params [i]))
371 mono_mini_arch_lock ();
372 code = cache [sig->param_count];
374 mono_mini_arch_unlock ();
378 size = 8 + sig->param_count * 4;
379 start = code = mono_global_codeman_reserve (size);
381 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, method_ptr));
382 /* slide down the arguments */
383 for (i = 0; i < sig->param_count; ++i) {
384 ARM_MOV_REG_REG (code, (ARMREG_R0 + i), (ARMREG_R0 + i + 1));
386 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
388 g_assert ((code - start) <= size);
390 mono_arch_flush_icache (code, size);
391 cache [sig->param_count] = start;
392 mono_mini_arch_unlock ();
400 mono_arch_get_this_arg_from_call (MonoMethodSignature *sig, gssize *regs, guint8 *code)
402 /* FIXME: handle returning a struct */
403 if (MONO_TYPE_ISSTRUCT (sig->ret))
404 return (gpointer)regs [ARMREG_R1];
405 return (gpointer)regs [ARMREG_R0];
409 * Initialize the cpu to execute managed code.
412 mono_arch_cpu_init (void)
417 * Initialize architecture specific code.
420 mono_arch_init (void)
422 InitializeCriticalSection (&mini_arch_mutex);
426 * Cleanup architecture specific code.
429 mono_arch_cleanup (void)
434 * This function returns the optimizations supported on this cpu.
437 mono_arch_cpu_optimizazions (guint32 *exclude_mask)
442 FILE *file = fopen ("/proc/cpuinfo", "r");
444 while ((line = fgets (buf, 512, file))) {
445 if (strncmp (line, "Processor", 9) == 0) {
446 char *ver = strstr (line, "(v");
447 if (ver && (ver [2] == '5' || ver [2] == '6' || ver [2] == '7')) {
452 if (strncmp (line, "Features", 8) == 0) {
453 char *th = strstr (line, "thumb");
455 thumb_supported = TRUE;
463 /*printf ("features: v5: %d, thumb: %d\n", v5_supported, thumb_supported);*/
466 /* no arm-specific optimizations yet */
472 is_regsize_var (MonoType *t) {
475 t = mono_type_get_underlying_type (t);
482 case MONO_TYPE_FNPTR:
484 case MONO_TYPE_OBJECT:
485 case MONO_TYPE_STRING:
486 case MONO_TYPE_CLASS:
487 case MONO_TYPE_SZARRAY:
488 case MONO_TYPE_ARRAY:
490 case MONO_TYPE_GENERICINST:
491 if (!mono_type_generic_inst_is_valuetype (t))
494 case MONO_TYPE_VALUETYPE:
501 mono_arch_get_allocatable_int_vars (MonoCompile *cfg)
506 for (i = 0; i < cfg->num_varinfo; i++) {
507 MonoInst *ins = cfg->varinfo [i];
508 MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
511 if (vmv->range.first_use.abs_pos >= vmv->range.last_use.abs_pos)
514 if (ins->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT) || (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG))
517 /* we can only allocate 32 bit values */
518 if (is_regsize_var (ins->inst_vtype)) {
519 g_assert (MONO_VARINFO (cfg, i)->reg == -1);
520 g_assert (i == vmv->idx);
521 vars = mono_varlist_insert_sorted (cfg, vars, vmv, FALSE);
528 #define USE_EXTRA_TEMPS 0
531 mono_arch_get_global_int_regs (MonoCompile *cfg)
534 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V1));
535 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V2));
536 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V3));
537 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V4));
538 regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V5));
539 /*regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V6));*/
540 /*regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V7));*/
546 * mono_arch_regalloc_cost:
548 * Return the cost, in number of memory references, of the action of
549 * allocating the variable VMV into a register during global register
553 mono_arch_regalloc_cost (MonoCompile *cfg, MonoMethodVar *vmv)
560 mono_arch_flush_icache (guint8 *code, gint size)
562 __asm __volatile ("mov r0, %0\n"
565 "swi 0x9f0002 @ sys_cacheflush"
567 : "r" (code), "r" (code + size), "r" (0)
568 : "r0", "r1", "r3" );
583 guint16 vtsize; /* in param area */
585 guint8 regtype : 4; /* 0 general, 1 basereg, 2 floating point register, see RegType* */
586 guint8 size : 4; /* 1, 2, 4, 8, or regs used by RegTypeStructByVal */
601 add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple)
604 if (*gr > ARMREG_R3) {
605 ainfo->offset = *stack_size;
606 ainfo->reg = ARMREG_SP; /* in the caller */
607 ainfo->regtype = RegTypeBase;
618 /* first word in r3 and the second on the stack */
619 ainfo->offset = *stack_size;
620 ainfo->reg = ARMREG_SP; /* in the caller */
621 ainfo->regtype = RegTypeBaseGen;
623 } else if (*gr >= ARMREG_R3) {
628 ainfo->offset = *stack_size;
629 ainfo->reg = ARMREG_SP; /* in the caller */
630 ainfo->regtype = RegTypeBase;
645 calculate_sizes (MonoMethodSignature *sig, gboolean is_pinvoke)
648 int n = sig->hasthis + sig->param_count;
650 guint32 stack_size = 0;
651 CallInfo *cinfo = g_malloc0 (sizeof (CallInfo) + sizeof (ArgInfo) * n);
655 /* FIXME: handle returning a struct */
656 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
657 add_general (&gr, &stack_size, &cinfo->ret, TRUE);
658 cinfo->struct_ret = ARMREG_R0;
663 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
666 DEBUG(printf("params: %d\n", sig->param_count));
667 for (i = 0; i < sig->param_count; ++i) {
668 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
669 /* Prevent implicit arguments and sig_cookie from
670 being passed in registers */
672 /* Emit the signature cookie just before the implicit arguments */
673 add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE);
675 DEBUG(printf("param %d: ", i));
676 if (sig->params [i]->byref) {
677 DEBUG(printf("byref\n"));
678 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
682 simpletype = mono_type_get_underlying_type (sig->params [i])->type;
683 switch (simpletype) {
684 case MONO_TYPE_BOOLEAN:
687 cinfo->args [n].size = 1;
688 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
694 cinfo->args [n].size = 2;
695 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
700 cinfo->args [n].size = 4;
701 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
707 case MONO_TYPE_FNPTR:
708 case MONO_TYPE_CLASS:
709 case MONO_TYPE_OBJECT:
710 case MONO_TYPE_STRING:
711 case MONO_TYPE_SZARRAY:
712 case MONO_TYPE_ARRAY:
714 cinfo->args [n].size = sizeof (gpointer);
715 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
718 case MONO_TYPE_GENERICINST:
719 if (!mono_type_generic_inst_is_valuetype (sig->params [i])) {
720 cinfo->args [n].size = sizeof (gpointer);
721 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
726 case MONO_TYPE_TYPEDBYREF:
727 case MONO_TYPE_VALUETYPE: {
732 if (simpletype == MONO_TYPE_TYPEDBYREF) {
733 size = sizeof (MonoTypedRef);
735 MonoClass *klass = mono_class_from_mono_type (sig->params [i]);
737 size = mono_class_native_size (klass, NULL);
739 size = mono_class_value_size (klass, NULL);
741 DEBUG(printf ("load %d bytes struct\n",
742 mono_class_native_size (sig->params [i]->data.klass, NULL)));
745 align_size += (sizeof (gpointer) - 1);
746 align_size &= ~(sizeof (gpointer) - 1);
747 nwords = (align_size + sizeof (gpointer) -1 ) / sizeof (gpointer);
748 cinfo->args [n].regtype = RegTypeStructByVal;
749 /* FIXME: align gr and stack_size if needed */
750 if (gr > ARMREG_R3) {
751 cinfo->args [n].size = 0;
752 cinfo->args [n].vtsize = nwords;
754 int rest = ARMREG_R3 - gr + 1;
755 int n_in_regs = rest >= nwords? nwords: rest;
756 cinfo->args [n].size = n_in_regs;
757 cinfo->args [n].vtsize = nwords - n_in_regs;
758 cinfo->args [n].reg = gr;
761 cinfo->args [n].offset = stack_size;
762 /*g_print ("offset for arg %d at %d\n", n, stack_size);*/
763 stack_size += nwords * sizeof (gpointer);
770 cinfo->args [n].size = 8;
771 add_general (&gr, &stack_size, cinfo->args + n, FALSE);
775 g_error ("Can't trampoline 0x%x", sig->params [i]->type);
780 simpletype = mono_type_get_underlying_type (sig->ret)->type;
781 switch (simpletype) {
782 case MONO_TYPE_BOOLEAN:
793 case MONO_TYPE_FNPTR:
794 case MONO_TYPE_CLASS:
795 case MONO_TYPE_OBJECT:
796 case MONO_TYPE_SZARRAY:
797 case MONO_TYPE_ARRAY:
798 case MONO_TYPE_STRING:
799 cinfo->ret.reg = ARMREG_R0;
803 cinfo->ret.reg = ARMREG_R0;
807 cinfo->ret.reg = ARMREG_R0;
808 /* FIXME: cinfo->ret.reg = ???;
809 cinfo->ret.regtype = RegTypeFP;*/
811 case MONO_TYPE_GENERICINST:
812 if (!mono_type_generic_inst_is_valuetype (sig->ret)) {
813 cinfo->ret.reg = ARMREG_R0;
817 case MONO_TYPE_VALUETYPE:
819 case MONO_TYPE_TYPEDBYREF:
823 g_error ("Can't handle as return value 0x%x", sig->ret->type);
827 /* align stack size to 8 */
828 DEBUG (printf (" stack size: %d (%d)\n", (stack_size + 15) & ~15, stack_size));
829 stack_size = (stack_size + 7) & ~7;
831 cinfo->stack_usage = stack_size;
837 * Set var information according to the calling convention. arm version.
838 * The locals var stuff should most likely be split in another method.
841 mono_arch_allocate_vars (MonoCompile *m)
843 MonoMethodSignature *sig;
844 MonoMethodHeader *header;
846 int i, offset, size, align, curinst;
847 int frame_reg = ARMREG_FP;
849 /* FIXME: this will change when we use FP as gcc does */
850 m->flags |= MONO_CFG_HAS_SPILLUP;
852 /* allow room for the vararg method args: void* and long/double */
853 if (mono_jit_trace_calls != NULL && mono_trace_eval (m->method))
854 m->param_area = MAX (m->param_area, sizeof (gpointer)*8);
856 header = mono_method_get_header (m->method);
859 * We use the frame register also for any method that has
860 * exception clauses. This way, when the handlers are called,
861 * the code will reference local variables using the frame reg instead of
862 * the stack pointer: if we had to restore the stack pointer, we'd
863 * corrupt the method frames that are already on the stack (since
864 * filters get called before stack unwinding happens) when the filter
865 * code would call any method (this also applies to finally etc.).
867 if ((m->flags & MONO_CFG_HAS_ALLOCA) || header->num_clauses)
868 frame_reg = ARMREG_FP;
869 m->frame_reg = frame_reg;
870 if (frame_reg != ARMREG_SP) {
871 m->used_int_regs |= 1 << frame_reg;
874 sig = mono_method_signature (m->method);
878 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
879 m->ret->opcode = OP_REGVAR;
880 m->ret->inst_c0 = ARMREG_R0;
882 /* FIXME: handle long and FP values */
883 switch (mono_type_get_underlying_type (sig->ret)->type) {
887 m->ret->opcode = OP_REGVAR;
888 m->ret->inst_c0 = ARMREG_R0;
892 /* local vars are at a positive offset from the stack pointer */
894 * also note that if the function uses alloca, we use FP
895 * to point at the local variables.
897 offset = 0; /* linkage area */
898 /* align the offset to 16 bytes: not sure this is needed here */
900 //offset &= ~(8 - 1);
902 /* add parameter area size for called functions */
903 offset += m->param_area;
906 if (m->flags & MONO_CFG_HAS_FPOUT)
909 /* allow room to save the return value */
910 if (mono_jit_trace_calls != NULL && mono_trace_eval (m->method))
913 /* the MonoLMF structure is stored just below the stack pointer */
915 if (sig->call_convention == MONO_CALL_VARARG) {
919 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
921 offset += sizeof(gpointer) - 1;
922 offset &= ~(sizeof(gpointer) - 1);
923 inst->inst_offset = offset;
924 inst->opcode = OP_REGOFFSET;
925 inst->inst_basereg = frame_reg;
926 offset += sizeof(gpointer);
927 if (sig->call_convention == MONO_CALL_VARARG)
928 m->sig_cookie += sizeof (gpointer);
931 curinst = m->locals_start;
932 for (i = curinst; i < m->num_varinfo; ++i) {
933 inst = m->varinfo [i];
934 if ((inst->flags & MONO_INST_IS_DEAD) || inst->opcode == OP_REGVAR)
937 /* inst->backend.is_pinvoke indicates native sized value types, this is used by the
938 * pinvoke wrappers when they call functions returning structure */
939 if (inst->backend.is_pinvoke && MONO_TYPE_ISSTRUCT (inst->inst_vtype) && inst->inst_vtype->type != MONO_TYPE_TYPEDBYREF)
940 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), &align);
942 size = mono_type_size (inst->inst_vtype, &align);
944 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
945 * since it loads/stores misaligned words, which don't do the right thing.
947 if (align < 4 && size >= 4)
950 offset &= ~(align - 1);
951 inst->inst_offset = offset;
952 inst->opcode = OP_REGOFFSET;
953 inst->inst_basereg = frame_reg;
955 //g_print ("allocating local %d to %d\n", i, inst->inst_offset);
960 inst = m->args [curinst];
961 if (inst->opcode != OP_REGVAR) {
962 inst->opcode = OP_REGOFFSET;
963 inst->inst_basereg = frame_reg;
964 offset += sizeof (gpointer) - 1;
965 offset &= ~(sizeof (gpointer) - 1);
966 inst->inst_offset = offset;
967 offset += sizeof (gpointer);
968 if (sig->call_convention == MONO_CALL_VARARG)
969 m->sig_cookie += sizeof (gpointer);
974 for (i = 0; i < sig->param_count; ++i) {
975 inst = m->args [curinst];
976 if (inst->opcode != OP_REGVAR) {
977 inst->opcode = OP_REGOFFSET;
978 inst->inst_basereg = frame_reg;
979 size = mono_type_size (sig->params [i], &align);
980 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
981 * since it loads/stores misaligned words, which don't do the right thing.
983 if (align < 4 && size >= 4)
986 offset &= ~(align - 1);
987 inst->inst_offset = offset;
989 if ((sig->call_convention == MONO_CALL_VARARG) && (i < sig->sentinelpos))
990 m->sig_cookie += size;
995 /* align the offset to 8 bytes */
1000 m->stack_offset = offset;
1004 /* Fixme: we need an alignment solution for enter_method and mono_arch_call_opcode,
1005 * currently alignment in mono_arch_call_opcode is computed without arch_get_argument_info
1009 * take the arguments and generate the arch-specific
1010 * instructions to properly call the function in call.
1011 * This includes pushing, moving arguments to the right register
1013 * Issue: who does the spilling if needed, and when?
1016 mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call, int is_virtual) {
1018 MonoMethodSignature *sig;
1023 sig = call->signature;
1024 n = sig->param_count + sig->hasthis;
1026 cinfo = calculate_sizes (sig, sig->pinvoke);
1027 if (cinfo->struct_ret)
1028 call->used_iregs |= 1 << cinfo->struct_ret;
1030 for (i = 0; i < n; ++i) {
1031 ainfo = cinfo->args + i;
1032 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
1034 cfg->disable_aot = TRUE;
1036 MONO_INST_NEW (cfg, sig_arg, OP_ICONST);
1037 sig_arg->inst_p0 = call->signature;
1039 MONO_INST_NEW (cfg, arg, OP_OUTARG);
1040 arg->inst_imm = cinfo->sig_cookie.offset;
1041 arg->inst_left = sig_arg;
1042 MONO_INST_LIST_ADD_TAIL (&arg->node, &call->out_args);
1044 if (is_virtual && i == 0) {
1045 /* the argument will be attached to the call instrucion */
1046 in = call->args [i];
1047 call->used_iregs |= 1 << ainfo->reg;
1049 MONO_INST_NEW (cfg, arg, OP_OUTARG);
1050 in = call->args [i];
1051 arg->cil_code = in->cil_code;
1052 arg->inst_left = in;
1053 arg->inst_right = (MonoInst*)call;
1054 arg->type = in->type;
1055 MONO_INST_LIST_ADD_TAIL (&arg->node, &call->out_args);
1056 if (ainfo->regtype == RegTypeGeneral) {
1057 arg->backend.reg3 = ainfo->reg;
1058 call->used_iregs |= 1 << ainfo->reg;
1059 if (arg->type == STACK_I8)
1060 call->used_iregs |= 1 << (ainfo->reg + 1);
1061 if (arg->type == STACK_R8) {
1062 if (ainfo->size == 4) {
1063 #ifndef MONO_ARCH_SOFT_FLOAT
1064 arg->opcode = OP_OUTARG_R4;
1067 call->used_iregs |= 1 << (ainfo->reg + 1);
1069 cfg->flags |= MONO_CFG_HAS_FPOUT;
1071 } else if (ainfo->regtype == RegTypeStructByAddr) {
1072 /* FIXME: where si the data allocated? */
1073 arg->backend.reg3 = ainfo->reg;
1074 call->used_iregs |= 1 << ainfo->reg;
1075 g_assert_not_reached ();
1076 } else if (ainfo->regtype == RegTypeStructByVal) {
1078 /* mark the used regs */
1079 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
1080 call->used_iregs |= 1 << (ainfo->reg + cur_reg);
1082 arg->opcode = OP_OUTARG_VT;
1083 /* vtsize and offset have just 12 bits of encoding in number of words */
1084 g_assert (((ainfo->vtsize | (ainfo->offset / 4)) & 0xfffff000) == 0);
1085 arg->backend.arg_info = ainfo->reg | (ainfo->size << 4) | (ainfo->vtsize << 8) | ((ainfo->offset / 4) << 20);
1086 } else if (ainfo->regtype == RegTypeBase) {
1087 arg->opcode = OP_OUTARG_MEMBASE;
1088 arg->backend.arg_info = (ainfo->offset << 8) | ainfo->size;
1089 } else if (ainfo->regtype == RegTypeBaseGen) {
1090 call->used_iregs |= 1 << ARMREG_R3;
1091 arg->opcode = OP_OUTARG_MEMBASE;
1092 arg->backend.arg_info = (ainfo->offset << 8) | 0xff;
1093 if (arg->type == STACK_R8)
1094 cfg->flags |= MONO_CFG_HAS_FPOUT;
1095 } else if (ainfo->regtype == RegTypeFP) {
1096 arg->backend.reg3 = ainfo->reg;
1097 /* FP args are passed in int regs */
1098 call->used_iregs |= 1 << ainfo->reg;
1099 if (ainfo->size == 8) {
1100 arg->opcode = OP_OUTARG_R8;
1101 call->used_iregs |= 1 << (ainfo->reg + 1);
1103 arg->opcode = OP_OUTARG_R4;
1105 cfg->flags |= MONO_CFG_HAS_FPOUT;
1107 g_assert_not_reached ();
1111 call->stack_usage = cinfo->stack_usage;
1112 cfg->param_area = MAX (cfg->param_area, cinfo->stack_usage);
1113 cfg->flags |= MONO_CFG_HAS_CALLS;
1115 * should set more info in call, such as the stack space
1116 * used by the args that needs to be added back to esp
1124 * Allow tracing to work with this interface (with an optional argument)
1128 mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
1132 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1133 ARM_MOV_REG_IMM8 (code, ARMREG_R1, 0); /* NULL ebp for now */
1134 code = mono_arm_emit_load_imm (code, ARMREG_R2, (guint32)func);
1135 code = emit_call_reg (code, ARMREG_R2);
1148 mono_arch_instrument_epilog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
1151 int save_mode = SAVE_NONE;
1153 MonoMethod *method = cfg->method;
1154 int rtype = mono_type_get_underlying_type (mono_method_signature (method)->ret)->type;
1155 int save_offset = cfg->param_area;
1159 offset = code - cfg->native_code;
1160 /* we need about 16 instructions */
1161 if (offset > (cfg->code_size - 16 * 4)) {
1162 cfg->code_size *= 2;
1163 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
1164 code = cfg->native_code + offset;
1167 case MONO_TYPE_VOID:
1168 /* special case string .ctor icall */
1169 if (strcmp (".ctor", method->name) && method->klass == mono_defaults.string_class)
1170 save_mode = SAVE_ONE;
1172 save_mode = SAVE_NONE;
1176 save_mode = SAVE_TWO;
1180 save_mode = SAVE_FP;
1182 case MONO_TYPE_VALUETYPE:
1183 save_mode = SAVE_STRUCT;
1186 save_mode = SAVE_ONE;
1190 switch (save_mode) {
1192 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1193 ARM_STR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1194 if (enable_arguments) {
1195 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_R1);
1196 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1200 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1201 if (enable_arguments) {
1202 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1206 /* FIXME: what reg? */
1207 if (enable_arguments) {
1208 /* FIXME: what reg? */
1212 if (enable_arguments) {
1213 /* FIXME: get the actual address */
1214 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1222 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1223 code = mono_arm_emit_load_imm (code, ARMREG_IP, (guint32)func);
1224 code = emit_call_reg (code, ARMREG_IP);
1226 switch (save_mode) {
1228 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1229 ARM_LDR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1232 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1246 * The immediate field for cond branches is big enough for all reasonable methods
1248 #define EMIT_COND_BRANCH_FLAGS(ins,condcode) \
1249 if (ins->flags & MONO_INST_BRLABEL) { \
1250 if (0 && ins->inst_i0->inst_c0) { \
1251 ARM_B_COND (code, (condcode), (code - cfg->native_code + ins->inst_i0->inst_c0) & 0xffffff); \
1253 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_LABEL, ins->inst_i0); \
1254 ARM_B_COND (code, (condcode), 0); \
1257 if (0 && ins->inst_true_bb->native_offset) { \
1258 ARM_B_COND (code, (condcode), (code - cfg->native_code + ins->inst_true_bb->native_offset) & 0xffffff); \
1260 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_true_bb); \
1261 ARM_B_COND (code, (condcode), 0); \
1265 #define EMIT_COND_BRANCH(ins,cond) EMIT_COND_BRANCH_FLAGS(ins, branch_cc_table [(cond)])
1267 /* emit an exception if condition is fail
1269 * We assign the extra code used to throw the implicit exceptions
1270 * to cfg->bb_exit as far as the big branch handling is concerned
1272 #define EMIT_COND_SYSTEM_EXCEPTION_FLAGS(condcode,exc_name) \
1274 mono_add_patch_info (cfg, code - cfg->native_code, \
1275 MONO_PATCH_INFO_EXC, exc_name); \
1276 ARM_BL_COND (code, (condcode), 0); \
1279 #define EMIT_COND_SYSTEM_EXCEPTION(cond,exc_name) EMIT_COND_SYSTEM_EXCEPTION_FLAGS(branch_cc_table [(cond)], (exc_name))
1282 peephole_pass (MonoCompile *cfg, MonoBasicBlock *bb)
1286 MONO_INST_LIST_FOR_EACH_ENTRY_SAFE (ins, n, &bb->ins_list, node) {
1287 MonoInst *last_ins = mono_inst_list_prev (&ins->node, &bb->ins_list);
1289 switch (ins->opcode) {
1291 /* remove unnecessary multiplication with 1 */
1292 if (ins->inst_imm == 1) {
1293 if (ins->dreg != ins->sreg1) {
1294 ins->opcode = OP_MOVE;
1300 int power2 = mono_is_power_of_two (ins->inst_imm);
1302 ins->opcode = OP_SHL_IMM;
1303 ins->inst_imm = power2;
1307 case OP_LOAD_MEMBASE:
1308 case OP_LOADI4_MEMBASE:
1310 * OP_STORE_MEMBASE_REG reg, offset(basereg)
1311 * OP_LOAD_MEMBASE offset(basereg), reg
1313 if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG
1314 || last_ins->opcode == OP_STORE_MEMBASE_REG) &&
1315 ins->inst_basereg == last_ins->inst_destbasereg &&
1316 ins->inst_offset == last_ins->inst_offset) {
1317 if (ins->dreg == last_ins->sreg1) {
1321 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1322 ins->opcode = OP_MOVE;
1323 ins->sreg1 = last_ins->sreg1;
1327 * Note: reg1 must be different from the basereg in the second load
1328 * OP_LOAD_MEMBASE offset(basereg), reg1
1329 * OP_LOAD_MEMBASE offset(basereg), reg2
1331 * OP_LOAD_MEMBASE offset(basereg), reg1
1332 * OP_MOVE reg1, reg2
1334 } if (last_ins && (last_ins->opcode == OP_LOADI4_MEMBASE
1335 || last_ins->opcode == OP_LOAD_MEMBASE) &&
1336 ins->inst_basereg != last_ins->dreg &&
1337 ins->inst_basereg == last_ins->inst_basereg &&
1338 ins->inst_offset == last_ins->inst_offset) {
1340 if (ins->dreg == last_ins->dreg) {
1344 ins->opcode = OP_MOVE;
1345 ins->sreg1 = last_ins->dreg;
1348 //g_assert_not_reached ();
1352 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1353 * OP_LOAD_MEMBASE offset(basereg), reg
1355 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1356 * OP_ICONST reg, imm
1358 } else if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_IMM
1359 || last_ins->opcode == OP_STORE_MEMBASE_IMM) &&
1360 ins->inst_basereg == last_ins->inst_destbasereg &&
1361 ins->inst_offset == last_ins->inst_offset) {
1362 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1363 ins->opcode = OP_ICONST;
1364 ins->inst_c0 = last_ins->inst_imm;
1365 g_assert_not_reached (); // check this rule
1369 case OP_LOADU1_MEMBASE:
1370 case OP_LOADI1_MEMBASE:
1371 if (last_ins && (last_ins->opcode == OP_STOREI1_MEMBASE_REG) &&
1372 ins->inst_basereg == last_ins->inst_destbasereg &&
1373 ins->inst_offset == last_ins->inst_offset) {
1374 ins->opcode = (ins->opcode == OP_LOADI1_MEMBASE) ? CEE_CONV_I1 : CEE_CONV_U1;
1375 ins->sreg1 = last_ins->sreg1;
1378 case OP_LOADU2_MEMBASE:
1379 case OP_LOADI2_MEMBASE:
1380 if (last_ins && (last_ins->opcode == OP_STOREI2_MEMBASE_REG) &&
1381 ins->inst_basereg == last_ins->inst_destbasereg &&
1382 ins->inst_offset == last_ins->inst_offset) {
1383 ins->opcode = (ins->opcode == OP_LOADI2_MEMBASE) ? CEE_CONV_I2 : CEE_CONV_U2;
1384 ins->sreg1 = last_ins->sreg1;
1391 ins->opcode = OP_MOVE;
1395 if (ins->dreg == ins->sreg1) {
1400 * OP_MOVE sreg, dreg
1401 * OP_MOVE dreg, sreg
1403 if (last_ins && last_ins->opcode == OP_MOVE &&
1404 ins->sreg1 == last_ins->dreg &&
1405 ins->dreg == last_ins->sreg1) {
1415 * the branch_cc_table should maintain the order of these
1429 branch_cc_table [] = {
1444 #define NEW_INS(cfg,ins,dest,op) do { \
1445 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
1446 (dest)->opcode = (op); \
1447 MONO_INST_LIST_ADD_TAIL (&(dest)->node, &(ins)->node); \
1451 map_to_reg_reg_op (int op)
1460 case OP_COMPARE_IMM:
1474 case OP_LOAD_MEMBASE:
1475 return OP_LOAD_MEMINDEX;
1476 case OP_LOADI4_MEMBASE:
1477 return OP_LOADI4_MEMINDEX;
1478 case OP_LOADU4_MEMBASE:
1479 return OP_LOADU4_MEMINDEX;
1480 case OP_LOADU1_MEMBASE:
1481 return OP_LOADU1_MEMINDEX;
1482 case OP_LOADI2_MEMBASE:
1483 return OP_LOADI2_MEMINDEX;
1484 case OP_LOADU2_MEMBASE:
1485 return OP_LOADU2_MEMINDEX;
1486 case OP_LOADI1_MEMBASE:
1487 return OP_LOADI1_MEMINDEX;
1488 case OP_STOREI1_MEMBASE_REG:
1489 return OP_STOREI1_MEMINDEX;
1490 case OP_STOREI2_MEMBASE_REG:
1491 return OP_STOREI2_MEMINDEX;
1492 case OP_STOREI4_MEMBASE_REG:
1493 return OP_STOREI4_MEMINDEX;
1494 case OP_STORE_MEMBASE_REG:
1495 return OP_STORE_MEMINDEX;
1496 case OP_STORER4_MEMBASE_REG:
1497 return OP_STORER4_MEMINDEX;
1498 case OP_STORER8_MEMBASE_REG:
1499 return OP_STORER8_MEMINDEX;
1500 case OP_STORE_MEMBASE_IMM:
1501 return OP_STORE_MEMBASE_REG;
1502 case OP_STOREI1_MEMBASE_IMM:
1503 return OP_STOREI1_MEMBASE_REG;
1504 case OP_STOREI2_MEMBASE_IMM:
1505 return OP_STOREI2_MEMBASE_REG;
1506 case OP_STOREI4_MEMBASE_IMM:
1507 return OP_STOREI4_MEMBASE_REG;
1509 g_assert_not_reached ();
1513 * Remove from the instruction list the instructions that can't be
1514 * represented with very simple instructions with no register
1518 mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb)
1520 int rot_amount, imm8, low_imm;
1521 MonoInst *ins, *temp;
1523 /* setup the virtual reg allocator */
1524 if (bb->max_vreg > cfg->rs->next_vreg)
1525 cfg->rs->next_vreg = bb->max_vreg;
1527 MONO_BB_FOR_EACH_INS (bb, ins) {
1531 last_ins = mono_inst_list_prev (&ins->node, &bb->ins_list);
1532 switch (ins->opcode) {
1536 case OP_COMPARE_IMM:
1543 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount)) < 0) {
1544 NEW_INS (cfg, ins, temp, OP_ICONST);
1545 temp->inst_c0 = ins->inst_imm;
1546 temp->dreg = mono_regstate_next_int (cfg->rs);
1547 ins->sreg2 = temp->dreg;
1548 ins->opcode = map_to_reg_reg_op (ins->opcode);
1552 if (ins->inst_imm == 1) {
1553 ins->opcode = OP_MOVE;
1556 if (ins->inst_imm == 0) {
1557 ins->opcode = OP_ICONST;
1561 imm8 = mono_is_power_of_two (ins->inst_imm);
1563 ins->opcode = OP_SHL_IMM;
1564 ins->inst_imm = imm8;
1567 NEW_INS (cfg, ins, temp, OP_ICONST);
1568 temp->inst_c0 = ins->inst_imm;
1569 temp->dreg = mono_regstate_next_int (cfg->rs);
1570 ins->sreg2 = temp->dreg;
1571 ins->opcode = CEE_MUL;
1573 case OP_LOAD_MEMBASE:
1574 case OP_LOADI4_MEMBASE:
1575 case OP_LOADU4_MEMBASE:
1576 case OP_LOADU1_MEMBASE:
1577 /* we can do two things: load the immed in a register
1578 * and use an indexed load, or see if the immed can be
1579 * represented as an ad_imm + a load with a smaller offset
1580 * that fits. We just do the first for now, optimize later.
1582 if (arm_is_imm12 (ins->inst_offset))
1584 NEW_INS (cfg, ins, temp, OP_ICONST);
1585 temp->inst_c0 = ins->inst_offset;
1586 temp->dreg = mono_regstate_next_int (cfg->rs);
1587 ins->sreg2 = temp->dreg;
1588 ins->opcode = map_to_reg_reg_op (ins->opcode);
1590 case OP_LOADI2_MEMBASE:
1591 case OP_LOADU2_MEMBASE:
1592 case OP_LOADI1_MEMBASE:
1593 if (arm_is_imm8 (ins->inst_offset))
1595 NEW_INS (cfg, ins, temp, OP_ICONST);
1596 temp->inst_c0 = ins->inst_offset;
1597 temp->dreg = mono_regstate_next_int (cfg->rs);
1598 ins->sreg2 = temp->dreg;
1599 ins->opcode = map_to_reg_reg_op (ins->opcode);
1601 case OP_LOADR4_MEMBASE:
1602 case OP_LOADR8_MEMBASE:
1603 if (arm_is_fpimm8 (ins->inst_offset))
1605 low_imm = ins->inst_offset & 0x1ff;
1606 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~0x1ff, &rot_amount)) >= 0) {
1607 NEW_INS (cfg, ins, temp, OP_ADD_IMM);
1608 temp->inst_imm = ins->inst_offset & ~0x1ff;
1609 temp->sreg1 = ins->inst_basereg;
1610 temp->dreg = mono_regstate_next_int (cfg->rs);
1611 ins->inst_basereg = temp->dreg;
1612 ins->inst_offset = low_imm;
1615 /* VFP/FPA doesn't have indexed load instructions */
1616 g_assert_not_reached ();
1618 case OP_STORE_MEMBASE_REG:
1619 case OP_STOREI4_MEMBASE_REG:
1620 case OP_STOREI1_MEMBASE_REG:
1621 if (arm_is_imm12 (ins->inst_offset))
1623 NEW_INS (cfg, ins, 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_STOREI2_MEMBASE_REG:
1630 if (arm_is_imm8 (ins->inst_offset))
1632 NEW_INS (cfg, ins, temp, OP_ICONST);
1633 temp->inst_c0 = ins->inst_offset;
1634 temp->dreg = mono_regstate_next_int (cfg->rs);
1635 ins->sreg2 = temp->dreg;
1636 ins->opcode = map_to_reg_reg_op (ins->opcode);
1638 case OP_STORER4_MEMBASE_REG:
1639 case OP_STORER8_MEMBASE_REG:
1640 if (arm_is_fpimm8 (ins->inst_offset))
1642 low_imm = ins->inst_offset & 0x1ff;
1643 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~ 0x1ff, &rot_amount)) >= 0 && arm_is_fpimm8 (low_imm)) {
1644 NEW_INS (cfg, ins, temp, OP_ADD_IMM);
1645 temp->inst_imm = ins->inst_offset & ~0x1ff;
1646 temp->sreg1 = ins->inst_destbasereg;
1647 temp->dreg = mono_regstate_next_int (cfg->rs);
1648 ins->inst_destbasereg = temp->dreg;
1649 ins->inst_offset = low_imm;
1652 /*g_print ("fail with: %d (%d, %d)\n", ins->inst_offset, ins->inst_offset & ~0x1ff, low_imm);*/
1653 /* VFP/FPA doesn't have indexed store instructions */
1654 g_assert_not_reached ();
1656 case OP_STORE_MEMBASE_IMM:
1657 case OP_STOREI1_MEMBASE_IMM:
1658 case OP_STOREI2_MEMBASE_IMM:
1659 case OP_STOREI4_MEMBASE_IMM:
1660 NEW_INS (cfg, ins, temp, OP_ICONST);
1661 temp->inst_c0 = ins->inst_imm;
1662 temp->dreg = mono_regstate_next_int (cfg->rs);
1663 ins->sreg1 = temp->dreg;
1664 ins->opcode = map_to_reg_reg_op (ins->opcode);
1665 goto loop_start; /* make it handle the possibly big ins->inst_offset */
1668 bb->max_vreg = cfg->rs->next_vreg;
1672 mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
1674 if (MONO_INST_LIST_EMPTY (&bb->ins_list))
1676 mono_arch_lowering_pass (cfg, bb);
1677 mono_local_regalloc (cfg, bb);
1681 emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size, gboolean is_signed)
1683 /* sreg is a float, dreg is an integer reg */
1685 ARM_FIXZ (code, dreg, sreg);
1686 #elif defined(ARM_FPU_VFP)
1688 ARM_TOSIZD (code, ARM_VFP_F0, sreg);
1690 ARM_TOUIZD (code, ARM_VFP_F0, sreg);
1691 ARM_FMRS (code, dreg, ARM_VFP_F0);
1695 ARM_AND_REG_IMM8 (code, dreg, dreg, 0xff);
1696 else if (size == 2) {
1697 ARM_SHL_IMM (code, dreg, dreg, 16);
1698 ARM_SHR_IMM (code, dreg, dreg, 16);
1702 ARM_SHL_IMM (code, dreg, dreg, 24);
1703 ARM_SAR_IMM (code, dreg, dreg, 24);
1704 } else if (size == 2) {
1705 ARM_SHL_IMM (code, dreg, dreg, 16);
1706 ARM_SAR_IMM (code, dreg, dreg, 16);
1714 const guchar *target;
1719 #define is_call_imm(diff) ((gint)(diff) >= -33554432 && (gint)(diff) <= 33554431)
1722 search_thunk_slot (void *data, int csize, int bsize, void *user_data) {
1723 PatchData *pdata = (PatchData*)user_data;
1724 guchar *code = data;
1725 guint32 *thunks = data;
1726 guint32 *endthunks = (guint32*)(code + bsize);
1728 int difflow, diffhigh;
1730 /* always ensure a call from pdata->code can reach to the thunks without further thunks */
1731 difflow = (char*)pdata->code - (char*)thunks;
1732 diffhigh = (char*)pdata->code - (char*)endthunks;
1733 if (!((is_call_imm (thunks) && is_call_imm (endthunks)) || (is_call_imm (difflow) && is_call_imm (diffhigh))))
1737 * The thunk is composed of 3 words:
1738 * load constant from thunks [2] into ARM_IP
1741 * Note that the LR register is already setup
1743 //g_print ("thunk nentries: %d\n", ((char*)endthunks - (char*)thunks)/16);
1744 if ((pdata->found == 2) || (pdata->code >= code && pdata->code <= code + csize)) {
1745 while (thunks < endthunks) {
1746 //g_print ("looking for target: %p at %p (%08x-%08x)\n", pdata->target, thunks, thunks [0], thunks [1]);
1747 if (thunks [2] == (guint32)pdata->target) {
1748 arm_patch (pdata->code, (guchar*)thunks);
1749 mono_arch_flush_icache (pdata->code, 4);
1752 } else if ((thunks [0] == 0) && (thunks [1] == 0) && (thunks [2] == 0)) {
1753 /* found a free slot instead: emit thunk */
1754 /* ARMREG_IP is fine to use since this can't be an IMT call
1757 code = (guchar*)thunks;
1758 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
1759 if (thumb_supported)
1760 ARM_BX (code, ARMREG_IP);
1762 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
1763 thunks [2] = (guint32)pdata->target;
1764 mono_arch_flush_icache ((guchar*)thunks, 12);
1766 arm_patch (pdata->code, (guchar*)thunks);
1767 mono_arch_flush_icache (pdata->code, 4);
1771 /* skip 12 bytes, the size of the thunk */
1775 //g_print ("failed thunk lookup for %p from %p at %p (%d entries)\n", pdata->target, pdata->code, data, count);
1781 handle_thunk (int absolute, guchar *code, const guchar *target) {
1782 MonoDomain *domain = mono_domain_get ();
1786 pdata.target = target;
1787 pdata.absolute = absolute;
1790 mono_domain_lock (domain);
1791 mono_code_manager_foreach (domain->code_mp, search_thunk_slot, &pdata);
1794 /* this uses the first available slot */
1796 mono_code_manager_foreach (domain->code_mp, search_thunk_slot, &pdata);
1798 mono_domain_unlock (domain);
1800 if (pdata.found != 1)
1801 g_print ("thunk failed for %p from %p\n", target, code);
1802 g_assert (pdata.found == 1);
1806 arm_patch (guchar *code, const guchar *target)
1808 guint32 *code32 = (void*)code;
1809 guint32 ins = *code32;
1810 guint32 prim = (ins >> 25) & 7;
1811 guint32 tval = GPOINTER_TO_UINT (target);
1813 //g_print ("patching 0x%08x (0x%08x) to point to 0x%08x\n", code, ins, target);
1814 if (prim == 5) { /* 101b */
1815 /* the diff starts 8 bytes from the branch opcode */
1816 gint diff = target - code - 8;
1818 gint tmask = 0xffffffff;
1819 if (tval & 1) { /* entering thumb mode */
1820 diff = target - 1 - code - 8;
1821 g_assert (thumb_supported);
1822 tbits = 0xf << 28; /* bl->blx bit pattern */
1823 g_assert ((ins & (1 << 24))); /* it must be a bl, not b instruction */
1824 /* this low bit of the displacement is moved to bit 24 in the instruction encoding */
1828 tmask = ~(1 << 24); /* clear the link bit */
1829 /*g_print ("blx to thumb: target: %p, code: %p, diff: %d, mask: %x\n", target, code, diff, tmask);*/
1834 if (diff <= 33554431) {
1836 ins = (ins & 0xff000000) | diff;
1838 *code32 = ins | tbits;
1842 /* diff between 0 and -33554432 */
1843 if (diff >= -33554432) {
1845 ins = (ins & 0xff000000) | (diff & ~0xff000000);
1847 *code32 = ins | tbits;
1852 handle_thunk (TRUE, code, target);
1857 * The alternative call sequences looks like this:
1859 * ldr ip, [pc] // loads the address constant
1860 * b 1f // jumps around the constant
1861 * address constant embedded in the code
1866 * There are two cases for patching:
1867 * a) at the end of method emission: in this case code points to the start
1868 * of the call sequence
1869 * b) during runtime patching of the call site: in this case code points
1870 * to the mov pc, ip instruction
1872 * We have to handle also the thunk jump code sequence:
1876 * address constant // execution never reaches here
1878 if ((ins & 0x0ffffff0) == 0x12fff10) {
1879 /* Branch and exchange: the address is constructed in a reg
1880 * We can patch BX when the code sequence is the following:
1881 * ldr ip, [pc, #0] ; 0x8
1888 guint8 *emit = (guint8*)ccode;
1889 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
1891 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
1892 ARM_BX (emit, ARMREG_IP);
1894 /*patching from magic trampoline*/
1895 if (ins == ccode [3]) {
1896 g_assert (code32 [-4] == ccode [0]);
1897 g_assert (code32 [-3] == ccode [1]);
1898 g_assert (code32 [-1] == ccode [2]);
1899 code32 [-2] = (guint32)target;
1902 /*patching from JIT*/
1903 if (ins == ccode [0]) {
1904 g_assert (code32 [1] == ccode [1]);
1905 g_assert (code32 [3] == ccode [2]);
1906 g_assert (code32 [4] == ccode [3]);
1907 code32 [2] = (guint32)target;
1910 g_assert_not_reached ();
1913 guint32 *tmp = ccode;
1914 guint8 *emit = (guint8*)tmp;
1915 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
1916 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
1917 ARM_MOV_REG_REG (emit, ARMREG_PC, ARMREG_IP);
1918 ARM_BX (emit, ARMREG_IP);
1919 if (ins == ccode [2]) {
1920 g_assert_not_reached (); // should be -2 ...
1921 code32 [-1] = (guint32)target;
1924 if (ins == ccode [0]) {
1925 /* handles both thunk jump code and the far call sequence */
1926 code32 [2] = (guint32)target;
1929 g_assert_not_reached ();
1931 // g_print ("patched with 0x%08x\n", ins);
1935 * Return the >= 0 uimm8 value if val can be represented with a byte + rotation
1936 * (with the rotation amount in *rot_amount. rot_amount is already adjusted
1937 * to be used with the emit macros.
1938 * Return -1 otherwise.
1941 mono_arm_is_rotated_imm8 (guint32 val, gint *rot_amount)
1944 for (i = 0; i < 31; i+= 2) {
1945 res = (val << (32 - i)) | (val >> i);
1948 *rot_amount = i? 32 - i: 0;
1955 * Emits in code a sequence of instructions that load the value 'val'
1956 * into the dreg register. Uses at most 4 instructions.
1959 mono_arm_emit_load_imm (guint8 *code, int dreg, guint32 val)
1961 int imm8, rot_amount;
1963 ARM_LDR_IMM (code, dreg, ARMREG_PC, 0);
1964 /* skip the constant pool */
1970 if ((imm8 = mono_arm_is_rotated_imm8 (val, &rot_amount)) >= 0) {
1971 ARM_MOV_REG_IMM (code, dreg, imm8, rot_amount);
1972 } else if ((imm8 = mono_arm_is_rotated_imm8 (~val, &rot_amount)) >= 0) {
1973 ARM_MVN_REG_IMM (code, dreg, imm8, rot_amount);
1976 ARM_MOV_REG_IMM8 (code, dreg, (val & 0xFF));
1978 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF00) >> 8, 24);
1980 if (val & 0xFF0000) {
1981 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
1983 if (val & 0xFF000000) {
1984 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
1986 } else if (val & 0xFF00) {
1987 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF00) >> 8, 24);
1988 if (val & 0xFF0000) {
1989 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
1991 if (val & 0xFF000000) {
1992 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
1994 } else if (val & 0xFF0000) {
1995 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF0000) >> 16, 16);
1996 if (val & 0xFF000000) {
1997 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2000 //g_assert_not_reached ();
2006 mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
2011 guint8 *code = cfg->native_code + cfg->code_len;
2012 guint last_offset = 0;
2014 int imm8, rot_amount;
2016 if (cfg->opt & MONO_OPT_PEEPHOLE)
2017 peephole_pass (cfg, bb);
2019 /* we don't align basic blocks of loops on arm */
2021 if (cfg->verbose_level > 2)
2022 g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset);
2024 cpos = bb->max_offset;
2026 if (cfg->prof_options & MONO_PROFILE_COVERAGE) {
2027 //MonoCoverageInfo *cov = mono_get_coverage_info (cfg->method);
2028 //g_assert (!mono_compile_aot);
2031 // cov->data [bb->dfn].iloffset = bb->cil_code - cfg->cil_code;
2032 /* this is not thread save, but good enough */
2033 /* fixme: howto handle overflows? */
2034 //x86_inc_mem (code, &cov->data [bb->dfn].count);
2037 MONO_BB_FOR_EACH_INS (bb, ins) {
2038 offset = code - cfg->native_code;
2040 max_len = ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
2042 if (offset > (cfg->code_size - max_len - 16)) {
2043 cfg->code_size *= 2;
2044 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
2045 code = cfg->native_code + offset;
2047 // if (ins->cil_code)
2048 // g_print ("cil code\n");
2049 mono_debug_record_line_number (cfg, ins, offset);
2051 switch (ins->opcode) {
2052 case OP_MEMORY_BARRIER:
2055 g_assert_not_reached ();
2058 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2059 ppc_mulhw (code, ppc_r3, ins->sreg1, ins->sreg2);
2062 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2063 ppc_mulhwu (code, ppc_r3, ins->sreg1, ins->sreg2);
2065 case OP_STOREI1_MEMBASE_IMM:
2066 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFF);
2067 g_assert (arm_is_imm12 (ins->inst_offset));
2068 ARM_STRB_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2070 case OP_STOREI2_MEMBASE_IMM:
2071 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFFFF);
2072 g_assert (arm_is_imm8 (ins->inst_offset));
2073 ARM_STRH_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2075 case OP_STORE_MEMBASE_IMM:
2076 case OP_STOREI4_MEMBASE_IMM:
2077 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm);
2078 g_assert (arm_is_imm12 (ins->inst_offset));
2079 ARM_STR_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2081 case OP_STOREI1_MEMBASE_REG:
2082 g_assert (arm_is_imm12 (ins->inst_offset));
2083 ARM_STRB_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2085 case OP_STOREI2_MEMBASE_REG:
2086 g_assert (arm_is_imm8 (ins->inst_offset));
2087 ARM_STRH_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2089 case OP_STORE_MEMBASE_REG:
2090 case OP_STOREI4_MEMBASE_REG:
2091 /* this case is special, since it happens for spill code after lowering has been called */
2092 if (arm_is_imm12 (ins->inst_offset)) {
2093 ARM_STR_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2095 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2096 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ARMREG_LR);
2099 case OP_STOREI1_MEMINDEX:
2100 ARM_STRB_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2102 case OP_STOREI2_MEMINDEX:
2103 /* note: the args are reversed in the macro */
2104 ARM_STRH_REG_REG (code, ins->inst_destbasereg, ins->sreg1, ins->sreg2);
2106 case OP_STORE_MEMINDEX:
2107 case OP_STOREI4_MEMINDEX:
2108 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2113 g_assert_not_reached ();
2116 g_assert_not_reached ();
2118 case OP_LOAD_MEMINDEX:
2119 case OP_LOADI4_MEMINDEX:
2120 case OP_LOADU4_MEMINDEX:
2121 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2123 case OP_LOADI1_MEMINDEX:
2124 /* note: the args are reversed in the macro */
2125 ARM_LDRSB_REG_REG (code, ins->inst_basereg, ins->dreg, ins->sreg2);
2127 case OP_LOADU1_MEMINDEX:
2128 ARM_LDRB_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2130 case OP_LOADI2_MEMINDEX:
2131 /* note: the args are reversed in the macro */
2132 ARM_LDRSH_REG_REG (code, ins->inst_basereg, ins->dreg, ins->sreg2);
2134 case OP_LOADU2_MEMINDEX:
2135 /* note: the args are reversed in the macro */
2136 ARM_LDRH_REG_REG (code, ins->inst_basereg, ins->dreg, ins->sreg2);
2138 case OP_LOAD_MEMBASE:
2139 case OP_LOADI4_MEMBASE:
2140 case OP_LOADU4_MEMBASE:
2141 /* this case is special, since it happens for spill code after lowering has been called */
2142 if (arm_is_imm12 (ins->inst_offset)) {
2143 ARM_LDR_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2145 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2146 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ARMREG_LR);
2149 case OP_LOADI1_MEMBASE:
2150 g_assert (arm_is_imm8 (ins->inst_offset));
2151 ARM_LDRSB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2153 case OP_LOADU1_MEMBASE:
2154 g_assert (arm_is_imm12 (ins->inst_offset));
2155 ARM_LDRB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2157 case OP_LOADU2_MEMBASE:
2158 g_assert (arm_is_imm8 (ins->inst_offset));
2159 ARM_LDRH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2161 case OP_LOADI2_MEMBASE:
2162 g_assert (arm_is_imm8 (ins->inst_offset));
2163 ARM_LDRSH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2166 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 24);
2167 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 24);
2170 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2171 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 16);
2174 ARM_AND_REG_IMM8 (code, ins->dreg, ins->sreg1, 0xff);
2177 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2178 ARM_SHR_IMM (code, ins->dreg, ins->dreg, 16);
2181 ARM_CMP_REG_REG (code, ins->sreg1, ins->sreg2);
2183 case OP_COMPARE_IMM:
2184 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2185 g_assert (imm8 >= 0);
2186 ARM_CMP_REG_IMM (code, ins->sreg1, imm8, rot_amount);
2189 *(int*)code = 0xe7f001f0;
2190 *(int*)code = 0xef9f0001;
2195 ARM_ADDS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2198 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2201 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2204 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2205 g_assert (imm8 >= 0);
2206 ARM_ADDS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2209 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2210 g_assert (imm8 >= 0);
2211 ARM_ADD_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2214 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2215 g_assert (imm8 >= 0);
2216 ARM_ADCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2219 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2220 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2222 case CEE_ADD_OVF_UN:
2223 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2224 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2227 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2228 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2230 case CEE_SUB_OVF_UN:
2231 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2232 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2234 case OP_ADD_OVF_CARRY:
2235 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2236 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2238 case OP_ADD_OVF_UN_CARRY:
2239 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2240 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2242 case OP_SUB_OVF_CARRY:
2243 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2244 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2246 case OP_SUB_OVF_UN_CARRY:
2247 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2248 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2251 ARM_SUBS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2254 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2255 g_assert (imm8 >= 0);
2256 ARM_SUBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2259 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2262 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2265 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2266 g_assert (imm8 >= 0);
2267 ARM_SUB_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2270 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2271 g_assert (imm8 >= 0);
2272 ARM_SBCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2274 case OP_ARM_RSBS_IMM:
2275 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2276 g_assert (imm8 >= 0);
2277 ARM_RSBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2279 case OP_ARM_RSC_IMM:
2280 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2281 g_assert (imm8 >= 0);
2282 ARM_RSC_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2285 ARM_AND_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2288 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2289 g_assert (imm8 >= 0);
2290 ARM_AND_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2298 /* crappy ARM arch doesn't have a DIV instruction */
2299 g_assert_not_reached ();
2301 ARM_ORR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2304 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2305 g_assert (imm8 >= 0);
2306 ARM_ORR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2309 ARM_EOR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2312 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2313 g_assert (imm8 >= 0);
2314 ARM_EOR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2317 ARM_SHL_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2321 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2324 ARM_SAR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2328 ARM_SAR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2332 ARM_SHR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2335 ARM_SHR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2338 ARM_MVN_REG_REG (code, ins->dreg, ins->sreg1);
2341 ARM_RSB_REG_IMM8 (code, ins->dreg, ins->sreg1, 0);
2344 if (ins->dreg == ins->sreg2)
2345 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2347 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg2, ins->sreg1);
2350 g_assert_not_reached ();
2353 /* FIXME: handle ovf/ sreg2 != dreg */
2354 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2356 case CEE_MUL_OVF_UN:
2357 /* FIXME: handle ovf/ sreg2 != dreg */
2358 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2362 code = mono_arm_emit_load_imm (code, ins->dreg, ins->inst_c0);
2365 /* Load the GOT offset */
2366 mono_add_patch_info (cfg, offset, (MonoJumpInfoType)ins->inst_i1, ins->inst_p0);
2367 ARM_LDR_IMM (code, ins->dreg, ARMREG_PC, 0);
2369 *(gpointer*)code = NULL;
2371 /* Load the value from the GOT */
2372 ARM_LDR_REG_REG (code, ins->dreg, ARMREG_PC, ins->dreg);
2378 if (ins->dreg != ins->sreg1)
2379 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2382 int saved = ins->sreg2;
2383 if (ins->sreg2 == ARM_LSW_REG) {
2384 ARM_MOV_REG_REG (code, ARMREG_LR, ins->sreg2);
2387 if (ins->sreg1 != ARM_LSW_REG)
2388 ARM_MOV_REG_REG (code, ARM_LSW_REG, ins->sreg1);
2389 if (saved != ARM_MSW_REG)
2390 ARM_MOV_REG_REG (code, ARM_MSW_REG, saved);
2396 ARM_MVFD (code, ins->dreg, ins->sreg1);
2397 #elif defined(ARM_FPU_VFP)
2398 ARM_CPYD (code, ins->dreg, ins->sreg1);
2401 case OP_FCONV_TO_R4:
2403 ARM_MVFS (code, ins->dreg, ins->sreg1);
2404 #elif defined(ARM_FPU_VFP)
2405 ARM_CVTD (code, ins->dreg, ins->sreg1);
2406 ARM_CVTS (code, ins->dreg, ins->dreg);
2411 * Keep in sync with mono_arch_emit_epilog
2413 g_assert (!cfg->method->save_lmf);
2414 code = emit_big_add (code, ARMREG_SP, cfg->frame_reg, cfg->stack_usage);
2415 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP)) | ((1 << ARMREG_LR)));
2416 mono_add_patch_info (cfg, (guint8*) code - cfg->native_code, MONO_PATCH_INFO_METHOD_JUMP, ins->inst_p0);
2420 /* ensure ins->sreg1 is not NULL */
2421 ARM_LDR_IMM (code, ARMREG_LR, ins->sreg1, 0);
2425 if (ppc_is_imm16 (cfg->sig_cookie + cfg->stack_usage)) {
2426 ppc_addi (code, ppc_r11, cfg->frame_reg, cfg->sig_cookie + cfg->stack_usage);
2428 ppc_load (code, ppc_r11, cfg->sig_cookie + cfg->stack_usage);
2429 ppc_add (code, ppc_r11, cfg->frame_reg, ppc_r11);
2431 ppc_stw (code, ppc_r11, 0, ins->sreg1);
2440 call = (MonoCallInst*)ins;
2441 if (ins->flags & MONO_INST_HAS_METHOD)
2442 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, call->method);
2444 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, call->fptr);
2445 code = emit_call_seq (cfg, code);
2450 case OP_VOIDCALL_REG:
2452 code = emit_call_reg (code, ins->sreg1);
2454 case OP_FCALL_MEMBASE:
2455 case OP_LCALL_MEMBASE:
2456 case OP_VCALL_MEMBASE:
2457 case OP_VOIDCALL_MEMBASE:
2458 case OP_CALL_MEMBASE:
2459 g_assert (arm_is_imm12 (ins->inst_offset));
2460 g_assert (ins->sreg1 != ARMREG_LR);
2461 call = (MonoCallInst*)ins;
2462 if (call->method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
2463 if (cfg->compile_aot)
2465 cfg->disable_aot = 1;
2466 ARM_ADD_REG_IMM8 (code, ARMREG_LR, ARMREG_PC, 4);
2467 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
2468 *((gpointer*)code) = (gpointer)call->method;
2471 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
2472 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
2476 g_assert_not_reached ();
2479 /* keep alignment */
2480 int alloca_waste = cfg->param_area;
2483 /* round the size to 8 bytes */
2484 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->sreg1, 7);
2485 ARM_BIC_REG_IMM8 (code, ins->dreg, ins->dreg, 7);
2487 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->dreg, alloca_waste);
2488 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ins->dreg);
2489 /* memzero the area: dreg holds the size, sp is the pointer */
2490 if (ins->flags & MONO_INST_INIT) {
2491 guint8 *start_loop, *branch_to_cond;
2492 ARM_MOV_REG_IMM8 (code, ARMREG_LR, 0);
2493 branch_to_cond = code;
2496 ARM_STR_REG_REG (code, ARMREG_LR, ARMREG_SP, ins->dreg);
2497 arm_patch (branch_to_cond, code);
2498 /* decrement by 4 and set flags */
2499 ARM_SUBS_REG_IMM8 (code, ins->dreg, ins->dreg, 4);
2500 ARM_B_COND (code, ARMCOND_GE, 0);
2501 arm_patch (code - 4, start_loop);
2503 ARM_ADD_REG_IMM8 (code, ins->dreg, ARMREG_SP, alloca_waste);
2507 g_assert_not_reached ();
2508 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_LR);
2511 if (ins->sreg1 != ARMREG_R0)
2512 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
2513 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2514 (gpointer)"mono_arch_throw_exception");
2515 code = emit_call_seq (cfg, code);
2519 if (ins->sreg1 != ARMREG_R0)
2520 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
2521 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2522 (gpointer)"mono_arch_rethrow_exception");
2523 code = emit_call_seq (cfg, code);
2526 case OP_START_HANDLER:
2527 if (arm_is_imm12 (ins->inst_left->inst_offset)) {
2528 ARM_STR_IMM (code, ARMREG_LR, ins->inst_left->inst_basereg, ins->inst_left->inst_offset);
2530 code = mono_arm_emit_load_imm (code, ARMREG_IP, ins->inst_left->inst_offset);
2531 ARM_STR_REG_REG (code, ARMREG_LR, ins->inst_left->inst_basereg, ARMREG_IP);
2535 if (ins->sreg1 != ARMREG_R0)
2536 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
2537 if (arm_is_imm12 (ins->inst_left->inst_offset)) {
2538 ARM_LDR_IMM (code, ARMREG_IP, ins->inst_left->inst_basereg, ins->inst_left->inst_offset);
2540 g_assert (ARMREG_IP != ins->inst_left->inst_basereg);
2541 code = mono_arm_emit_load_imm (code, ARMREG_IP, ins->inst_left->inst_offset);
2542 ARM_LDR_REG_REG (code, ARMREG_IP, ins->inst_left->inst_basereg, ARMREG_IP);
2544 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
2547 if (arm_is_imm12 (ins->inst_left->inst_offset)) {
2548 ARM_LDR_IMM (code, ARMREG_IP, ins->inst_left->inst_basereg, ins->inst_left->inst_offset);
2550 g_assert (ARMREG_IP != ins->inst_left->inst_basereg);
2551 code = mono_arm_emit_load_imm (code, ARMREG_IP, ins->inst_left->inst_offset);
2552 ARM_LDR_REG_REG (code, ARMREG_IP, ins->inst_left->inst_basereg, ARMREG_IP);
2554 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
2556 case OP_CALL_HANDLER:
2557 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb);
2561 ins->inst_c0 = code - cfg->native_code;
2564 if (ins->flags & MONO_INST_BRLABEL) {
2565 /*if (ins->inst_i0->inst_c0) {
2567 //x86_jump_code (code, cfg->native_code + ins->inst_i0->inst_c0);
2569 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_LABEL, ins->inst_i0);
2573 /*if (ins->inst_target_bb->native_offset) {
2575 //x86_jump_code (code, cfg->native_code + ins->inst_target_bb->native_offset);
2577 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb);
2583 ARM_MOV_REG_REG (code, ARMREG_PC, ins->sreg1);
2587 * In the normal case we have:
2588 * ldr pc, [pc, ins->sreg1 << 2]
2591 * ldr lr, [pc, ins->sreg1 << 2]
2593 * After follows the data.
2594 * FIXME: add aot support.
2596 max_len += 4 * GPOINTER_TO_INT (ins->klass);
2597 if (offset > (cfg->code_size - max_len - 16)) {
2598 cfg->code_size += max_len;
2599 cfg->code_size *= 2;
2600 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
2601 code = cfg->native_code + offset;
2603 ARM_LDR_REG_REG_SHIFT (code, ARMREG_PC, ARMREG_PC, ins->sreg1, ARMSHIFT_LSL, 2);
2605 code += 4 * GPOINTER_TO_INT (ins->klass);
2608 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
2609 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
2612 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2613 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LT);
2616 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2617 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LO);
2620 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2621 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_GT);
2624 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2625 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_HI);
2627 case OP_COND_EXC_EQ:
2628 case OP_COND_EXC_NE_UN:
2629 case OP_COND_EXC_LT:
2630 case OP_COND_EXC_LT_UN:
2631 case OP_COND_EXC_GT:
2632 case OP_COND_EXC_GT_UN:
2633 case OP_COND_EXC_GE:
2634 case OP_COND_EXC_GE_UN:
2635 case OP_COND_EXC_LE:
2636 case OP_COND_EXC_LE_UN:
2637 EMIT_COND_SYSTEM_EXCEPTION (ins->opcode - OP_COND_EXC_EQ, ins->inst_p1);
2640 case OP_COND_EXC_OV:
2641 case OP_COND_EXC_NC:
2642 case OP_COND_EXC_NO:
2643 g_assert_not_reached ();
2655 EMIT_COND_BRANCH (ins, ins->opcode - CEE_BEQ);
2658 /* floating point opcodes */
2661 if (cfg->compile_aot) {
2662 ARM_LDFD (code, ins->dreg, ARMREG_PC, 0);
2664 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
2666 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
2669 /* FIXME: we can optimize the imm load by dealing with part of
2670 * the displacement in LDFD (aligning to 512).
2672 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
2673 ARM_LDFD (code, ins->dreg, ARMREG_LR, 0);
2677 if (cfg->compile_aot) {
2678 ARM_LDFS (code, ins->dreg, ARMREG_PC, 0);
2680 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
2683 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
2684 ARM_LDFS (code, ins->dreg, ARMREG_LR, 0);
2687 case OP_STORER8_MEMBASE_REG:
2688 g_assert (arm_is_fpimm8 (ins->inst_offset));
2689 ARM_STFD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2691 case OP_LOADR8_MEMBASE:
2692 g_assert (arm_is_fpimm8 (ins->inst_offset));
2693 ARM_LDFD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2695 case OP_STORER4_MEMBASE_REG:
2696 g_assert (arm_is_fpimm8 (ins->inst_offset));
2697 ARM_STFS (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2699 case OP_LOADR4_MEMBASE:
2700 g_assert (arm_is_fpimm8 (ins->inst_offset));
2701 ARM_LDFS (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2703 case CEE_CONV_R_UN: {
2705 tmpreg = ins->dreg == 0? 1: 0;
2706 ARM_CMP_REG_IMM8 (code, ins->sreg1, 0);
2707 ARM_FLTD (code, ins->dreg, ins->sreg1);
2708 ARM_B_COND (code, ARMCOND_GE, 8);
2709 /* save the temp register */
2710 ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
2711 ARM_STFD (code, tmpreg, ARMREG_SP, 0);
2712 ARM_LDFD (code, tmpreg, ARMREG_PC, 12);
2713 ARM_FPA_ADFD (code, ins->dreg, ins->dreg, tmpreg);
2714 ARM_LDFD (code, tmpreg, ARMREG_SP, 0);
2715 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
2716 /* skip the constant pool */
2719 *(int*)code = 0x41f00000;
2724 * ldfltd ftemp, [pc, #8] 0x41f00000 0x00000000
2725 * adfltd fdest, fdest, ftemp
2730 ARM_FLTS (code, ins->dreg, ins->sreg1);
2733 ARM_FLTD (code, ins->dreg, ins->sreg1);
2735 #elif defined(ARM_FPU_VFP)
2737 if (cfg->compile_aot) {
2738 ARM_LDFD (code, ins->dreg, ARMREG_PC, 0);
2740 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
2742 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
2745 /* FIXME: we can optimize the imm load by dealing with part of
2746 * the displacement in LDFD (aligning to 512).
2748 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
2749 ARM_FLDD (code, ins->dreg, ARMREG_LR, 0);
2753 if (cfg->compile_aot) {
2754 ARM_FLDS (code, ins->dreg, ARMREG_PC, 0);
2756 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
2758 ARM_CVTS (code, ins->dreg, ins->dreg);
2760 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
2761 ARM_FLDS (code, ins->dreg, ARMREG_LR, 0);
2762 ARM_CVTS (code, ins->dreg, ins->dreg);
2765 case OP_STORER8_MEMBASE_REG:
2766 g_assert (arm_is_fpimm8 (ins->inst_offset));
2767 ARM_FSTD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2769 case OP_LOADR8_MEMBASE:
2770 g_assert (arm_is_fpimm8 (ins->inst_offset));
2771 ARM_FLDD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2773 case OP_STORER4_MEMBASE_REG:
2774 g_assert (arm_is_fpimm8 (ins->inst_offset));
2775 ARM_FSTS (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2777 case OP_LOADR4_MEMBASE:
2778 g_assert (arm_is_fpimm8 (ins->inst_offset));
2779 ARM_FLDS (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2781 case CEE_CONV_R_UN: {
2782 g_assert_not_reached ();
2786 g_assert_not_reached ();
2787 //ARM_FLTS (code, ins->dreg, ins->sreg1);
2790 g_assert_not_reached ();
2791 //ARM_FLTD (code, ins->dreg, ins->sreg1);
2794 case OP_FCONV_TO_I1:
2795 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, TRUE);
2797 case OP_FCONV_TO_U1:
2798 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, FALSE);
2800 case OP_FCONV_TO_I2:
2801 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, TRUE);
2803 case OP_FCONV_TO_U2:
2804 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, FALSE);
2806 case OP_FCONV_TO_I4:
2808 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, TRUE);
2810 case OP_FCONV_TO_U4:
2812 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, FALSE);
2814 case OP_FCONV_TO_I8:
2815 case OP_FCONV_TO_U8:
2816 g_assert_not_reached ();
2817 /* Implemented as helper calls */
2819 case OP_LCONV_TO_R_UN:
2820 g_assert_not_reached ();
2821 /* Implemented as helper calls */
2823 case OP_LCONV_TO_OVF_I: {
2825 guint32 *negative_branch, *msword_positive_branch, *msword_negative_branch, *ovf_ex_target;
2826 // Check if its negative
2827 ppc_cmpi (code, 0, 0, ins->sreg1, 0);
2828 negative_branch = code;
2829 ppc_bc (code, PPC_BR_TRUE, PPC_BR_LT, 0);
2830 // Its positive msword == 0
2831 ppc_cmpi (code, 0, 0, ins->sreg2, 0);
2832 msword_positive_branch = code;
2833 ppc_bc (code, PPC_BR_TRUE, PPC_BR_EQ, 0);
2835 ovf_ex_target = code;
2836 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_ALWAYS, 0, "OverflowException");
2838 ppc_patch (negative_branch, code);
2839 ppc_cmpi (code, 0, 0, ins->sreg2, -1);
2840 msword_negative_branch = code;
2841 ppc_bc (code, PPC_BR_FALSE, PPC_BR_EQ, 0);
2842 ppc_patch (msword_negative_branch, ovf_ex_target);
2844 ppc_patch (msword_positive_branch, code);
2845 if (ins->dreg != ins->sreg1)
2846 ppc_mr (code, ins->dreg, ins->sreg1);
2848 if (ins->dreg != ins->sreg1)
2849 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2854 ARM_FPA_ADFD (code, ins->dreg, ins->sreg1, ins->sreg2);
2857 ARM_FPA_SUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
2860 ARM_FPA_MUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
2863 ARM_FPA_DVFD (code, ins->dreg, ins->sreg1, ins->sreg2);
2866 ARM_MNFD (code, ins->dreg, ins->sreg1);
2868 #elif defined(ARM_FPU_VFP)
2870 ARM_VFP_ADDD (code, ins->dreg, ins->sreg1, ins->sreg2);
2873 ARM_VFP_SUBD (code, ins->dreg, ins->sreg1, ins->sreg2);
2876 ARM_VFP_MULD (code, ins->dreg, ins->sreg1, ins->sreg2);
2879 ARM_VFP_DIVD (code, ins->dreg, ins->sreg1, ins->sreg2);
2882 ARM_NEGD (code, ins->dreg, ins->sreg1);
2887 g_assert_not_reached ();
2890 /* each fp compare op needs to do its own */
2891 g_assert_not_reached ();
2892 //ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2896 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2897 #elif defined(ARM_FPU_VFP)
2898 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2900 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
2901 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
2905 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2906 #elif defined(ARM_FPU_VFP)
2907 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2909 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2910 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
2914 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2915 #elif defined(ARM_FPU_VFP)
2916 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2918 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2919 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
2920 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
2925 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
2926 #elif defined(ARM_FPU_VFP)
2927 ARM_CMPD (code, ins->sreg2, ins->sreg1);
2929 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2930 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
2935 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
2936 #elif defined(ARM_FPU_VFP)
2937 ARM_CMPD (code, ins->sreg2, ins->sreg1);
2939 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2940 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
2941 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
2943 /* ARM FPA flags table:
2944 * N Less than ARMCOND_MI
2945 * Z Equal ARMCOND_EQ
2946 * C Greater Than or Equal ARMCOND_CS
2947 * V Unordered ARMCOND_VS
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_BEQ - 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 (ins, CEE_BNE_UN - CEE_BEQ);
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_MI); /* N set */
2975 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2976 #elif defined(ARM_FPU_VFP)
2977 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2979 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
2980 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
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_MI); /* N set, swapped args */
2992 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
2993 #elif defined(ARM_FPU_VFP)
2994 ARM_CMPD (code, ins->sreg2, ins->sreg1);
2996 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
2997 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set, swapped args */
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_CS);
3009 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3010 #elif defined(ARM_FPU_VFP)
3011 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3013 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3014 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_GE);
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_CS); /* swapped */
3026 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3027 #elif defined(ARM_FPU_VFP)
3028 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3030 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3031 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_GE); /* swapped */
3034 /*ppc_stfd (code, ins->sreg1, -8, ppc_sp);
3035 ppc_lwz (code, ppc_r11, -8, ppc_sp);
3036 ppc_rlwinm (code, ppc_r11, ppc_r11, 0, 1, 31);
3037 ppc_addis (code, ppc_r11, ppc_r11, -32752);
3038 ppc_rlwinmd (code, ppc_r11, ppc_r11, 1, 31, 31);
3039 EMIT_COND_SYSTEM_EXCEPTION (CEE_BEQ - CEE_BEQ, "ArithmeticException");*/
3040 g_assert_not_reached ();
3044 g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
3045 g_assert_not_reached ();
3048 if ((cfg->opt & MONO_OPT_BRANCH) && ((code - cfg->native_code - offset) > max_len)) {
3049 g_warning ("wrong maximal instruction length of instruction %s (expected %d, got %d)",
3050 mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset);
3051 g_assert_not_reached ();
3056 last_offset = offset;
3059 cfg->code_len = code - cfg->native_code;
3063 mono_arch_register_lowlevel_calls (void)
3067 #define patch_lis_ori(ip,val) do {\
3068 guint16 *__lis_ori = (guint16*)(ip); \
3069 __lis_ori [1] = (((guint32)(val)) >> 16) & 0xffff; \
3070 __lis_ori [3] = ((guint32)(val)) & 0xffff; \
3074 mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, gboolean run_cctors)
3076 MonoJumpInfo *patch_info;
3077 gboolean compile_aot = !run_cctors;
3079 for (patch_info = ji; patch_info; patch_info = patch_info->next) {
3080 unsigned char *ip = patch_info->ip.i + code;
3081 const unsigned char *target;
3083 if (patch_info->type == MONO_PATCH_INFO_SWITCH && !compile_aot) {
3084 gpointer *jt = (gpointer*)(ip + 8);
3086 /* jt is the inlined jump table, 2 instructions after ip
3087 * In the normal case we store the absolute addresses,
3088 * otherwise the displacements.
3090 for (i = 0; i < patch_info->data.table->table_size; i++)
3091 jt [i] = code + (int)patch_info->data.table->table [i];
3094 target = mono_resolve_patch_target (method, domain, code, patch_info, run_cctors);
3097 switch (patch_info->type) {
3098 case MONO_PATCH_INFO_BB:
3099 case MONO_PATCH_INFO_LABEL:
3102 /* No need to patch these */
3107 switch (patch_info->type) {
3108 case MONO_PATCH_INFO_IP:
3109 g_assert_not_reached ();
3110 patch_lis_ori (ip, ip);
3112 case MONO_PATCH_INFO_METHOD_REL:
3113 g_assert_not_reached ();
3114 *((gpointer *)(ip)) = code + patch_info->data.offset;
3116 case MONO_PATCH_INFO_METHODCONST:
3117 case MONO_PATCH_INFO_CLASS:
3118 case MONO_PATCH_INFO_IMAGE:
3119 case MONO_PATCH_INFO_FIELD:
3120 case MONO_PATCH_INFO_VTABLE:
3121 case MONO_PATCH_INFO_IID:
3122 case MONO_PATCH_INFO_SFLDA:
3123 case MONO_PATCH_INFO_LDSTR:
3124 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
3125 case MONO_PATCH_INFO_LDTOKEN:
3126 g_assert_not_reached ();
3127 /* from OP_AOTCONST : lis + ori */
3128 patch_lis_ori (ip, target);
3130 case MONO_PATCH_INFO_R4:
3131 case MONO_PATCH_INFO_R8:
3132 g_assert_not_reached ();
3133 *((gconstpointer *)(ip + 2)) = patch_info->data.target;
3135 case MONO_PATCH_INFO_EXC_NAME:
3136 g_assert_not_reached ();
3137 *((gconstpointer *)(ip + 1)) = patch_info->data.name;
3139 case MONO_PATCH_INFO_NONE:
3140 case MONO_PATCH_INFO_BB_OVF:
3141 case MONO_PATCH_INFO_EXC_OVF:
3142 /* everything is dealt with at epilog output time */
3147 arm_patch (ip, target);
3152 * Stack frame layout:
3154 * ------------------- fp
3155 * MonoLMF structure or saved registers
3156 * -------------------
3158 * -------------------
3160 * -------------------
3161 * optional 8 bytes for tracing
3162 * -------------------
3163 * param area size is cfg->param_area
3164 * ------------------- sp
3167 mono_arch_emit_prolog (MonoCompile *cfg)
3169 MonoMethod *method = cfg->method;
3171 MonoMethodSignature *sig;
3173 int alloc_size, pos, max_offset, i, rot_amount;
3180 if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
3183 sig = mono_method_signature (method);
3184 cfg->code_size = 256 + sig->param_count * 20;
3185 code = cfg->native_code = g_malloc (cfg->code_size);
3187 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
3189 alloc_size = cfg->stack_offset;
3192 if (!method->save_lmf) {
3193 ARM_PUSH (code, (cfg->used_int_regs | (1 << ARMREG_IP) | (1 << ARMREG_LR)));
3194 prev_sp_offset = 8; /* ip and lr */
3195 for (i = 0; i < 16; ++i) {
3196 if (cfg->used_int_regs & (1 << i))
3197 prev_sp_offset += 4;
3200 ARM_PUSH (code, 0x5ff0);
3201 prev_sp_offset = 4 * 10; /* all but r0-r3, sp and pc */
3202 pos += sizeof (MonoLMF) - prev_sp_offset;
3206 // align to MONO_ARCH_FRAME_ALIGNMENT bytes
3207 if (alloc_size & (MONO_ARCH_FRAME_ALIGNMENT - 1)) {
3208 alloc_size += MONO_ARCH_FRAME_ALIGNMENT - 1;
3209 alloc_size &= ~(MONO_ARCH_FRAME_ALIGNMENT - 1);
3212 /* the stack used in the pushed regs */
3213 if (prev_sp_offset & 4)
3215 cfg->stack_usage = alloc_size;
3217 if ((i = mono_arm_is_rotated_imm8 (alloc_size, &rot_amount)) >= 0) {
3218 ARM_SUB_REG_IMM (code, ARMREG_SP, ARMREG_SP, i, rot_amount);
3220 code = mono_arm_emit_load_imm (code, ARMREG_IP, alloc_size);
3221 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
3224 if (cfg->frame_reg != ARMREG_SP)
3225 ARM_MOV_REG_REG (code, cfg->frame_reg, ARMREG_SP);
3226 //g_print ("prev_sp_offset: %d, alloc_size:%d\n", prev_sp_offset, alloc_size);
3227 prev_sp_offset += alloc_size;
3229 /* compute max_offset in order to use short forward jumps
3230 * we could skip do it on arm because the immediate displacement
3231 * for jumps is large enough, it may be useful later for constant pools
3234 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
3236 bb->max_offset = max_offset;
3238 if (cfg->prof_options & MONO_PROFILE_COVERAGE)
3241 MONO_BB_FOR_EACH_INS (bb, ins)
3242 max_offset += ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
3245 /* load arguments allocated to register from the stack */
3248 cinfo = calculate_sizes (sig, sig->pinvoke);
3250 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
3251 ArgInfo *ainfo = &cinfo->ret;
3253 g_assert (arm_is_imm12 (inst->inst_offset));
3254 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3256 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
3257 ArgInfo *ainfo = cinfo->args + i;
3258 inst = cfg->args [pos];
3260 if (cfg->verbose_level > 2)
3261 g_print ("Saving argument %d (type: %d)\n", i, ainfo->regtype);
3262 if (inst->opcode == OP_REGVAR) {
3263 if (ainfo->regtype == RegTypeGeneral)
3264 ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
3265 else if (ainfo->regtype == RegTypeFP) {
3266 g_assert_not_reached ();
3267 } else if (ainfo->regtype == RegTypeBase) {
3268 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
3269 ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3271 g_assert_not_reached ();
3273 if (cfg->verbose_level > 2)
3274 g_print ("Argument %d assigned to register %s\n", pos, mono_arch_regname (inst->dreg));
3276 /* the argument should be put on the stack: FIXME handle size != word */
3277 if (ainfo->regtype == RegTypeGeneral) {
3278 switch (ainfo->size) {
3280 if (arm_is_imm12 (inst->inst_offset))
3281 ARM_STRB_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3283 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3284 ARM_STRB_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3288 if (arm_is_imm8 (inst->inst_offset)) {
3289 ARM_STRH_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3291 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3292 ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, inst->inst_basereg);
3293 ARM_STRH_IMM (code, ainfo->reg, ARMREG_IP, 0);
3297 g_assert (arm_is_imm12 (inst->inst_offset));
3298 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3299 g_assert (arm_is_imm12 (inst->inst_offset + 4));
3300 ARM_STR_IMM (code, ainfo->reg + 1, inst->inst_basereg, inst->inst_offset + 4);
3303 if (arm_is_imm12 (inst->inst_offset)) {
3304 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3306 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3307 ARM_STR_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3311 } else if (ainfo->regtype == RegTypeBaseGen) {
3312 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
3313 g_assert (arm_is_imm12 (inst->inst_offset));
3314 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3315 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3316 ARM_STR_IMM (code, ARMREG_R3, inst->inst_basereg, inst->inst_offset);
3317 } else if (ainfo->regtype == RegTypeBase) {
3318 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
3319 switch (ainfo->size) {
3321 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3322 g_assert (arm_is_imm12 (inst->inst_offset));
3323 ARM_STRB_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3326 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3327 if (arm_is_imm8 (inst->inst_offset)) {
3328 ARM_STRH_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3330 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3331 ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, inst->inst_basereg);
3332 ARM_STRH_IMM (code, ARMREG_LR, ARMREG_IP, 0);
3336 g_assert (arm_is_imm12 (inst->inst_offset));
3337 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3338 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3339 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset + 4));
3340 g_assert (arm_is_imm12 (inst->inst_offset + 4));
3341 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset + 4));
3342 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3345 g_assert (arm_is_imm12 (inst->inst_offset));
3346 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3347 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3350 } else if (ainfo->regtype == RegTypeFP) {
3351 g_assert_not_reached ();
3352 } else if (ainfo->regtype == RegTypeStructByVal) {
3353 int doffset = inst->inst_offset;
3357 if (mono_class_from_mono_type (inst->inst_vtype))
3358 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), NULL);
3359 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
3360 g_assert (arm_is_imm12 (doffset));
3361 ARM_STR_IMM (code, ainfo->reg + cur_reg, inst->inst_basereg, doffset);
3362 soffset += sizeof (gpointer);
3363 doffset += sizeof (gpointer);
3365 if (ainfo->vtsize) {
3366 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
3367 //g_print ("emit_memcpy (prev_sp_ofs: %d, ainfo->offset: %d, soffset: %d)\n", prev_sp_offset, ainfo->offset, soffset);
3368 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, doffset, ARMREG_SP, prev_sp_offset + ainfo->offset);
3370 } else if (ainfo->regtype == RegTypeStructByAddr) {
3371 g_assert_not_reached ();
3372 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
3373 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, inst->inst_offset, ainfo->reg, 0);
3375 g_assert_not_reached ();
3380 if (method->save_lmf) {
3382 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3383 (gpointer)"mono_get_lmf_addr");
3384 code = emit_call_seq (cfg, code);
3385 /* we build the MonoLMF structure on the stack - see mini-arm.h */
3386 /* lmf_offset is the offset from the previous stack pointer,
3387 * alloc_size is the total stack space allocated, so the offset
3388 * of MonoLMF from the current stack ptr is alloc_size - lmf_offset.
3389 * The pointer to the struct is put in r1 (new_lmf).
3390 * r2 is used as scratch
3391 * The callee-saved registers are already in the MonoLMF structure
3393 code = emit_big_add (code, ARMREG_R1, ARMREG_SP, alloc_size - lmf_offset);
3394 /* r0 is the result from mono_get_lmf_addr () */
3395 ARM_STR_IMM (code, ARMREG_R0, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
3396 /* new_lmf->previous_lmf = *lmf_addr */
3397 ARM_LDR_IMM (code, ARMREG_R2, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3398 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3399 /* *(lmf_addr) = r1 */
3400 ARM_STR_IMM (code, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3401 /* save method info */
3402 code = mono_arm_emit_load_imm (code, ARMREG_R2, GPOINTER_TO_INT (method));
3403 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, method));
3404 ARM_STR_IMM (code, ARMREG_SP, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, ebp));
3405 /* save the current IP */
3406 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_PC);
3407 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, eip));
3411 code = mono_arch_instrument_prolog (cfg, mono_trace_enter_method, code, TRUE);
3413 cfg->code_len = code - cfg->native_code;
3414 g_assert (cfg->code_len < cfg->code_size);
3421 mono_arch_emit_epilog (MonoCompile *cfg)
3423 MonoMethod *method = cfg->method;
3424 int pos, i, rot_amount;
3425 int max_epilog_size = 16 + 20*4;
3428 if (cfg->method->save_lmf)
3429 max_epilog_size += 128;
3431 if (mono_jit_trace_calls != NULL)
3432 max_epilog_size += 50;
3434 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE)
3435 max_epilog_size += 50;
3437 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
3438 cfg->code_size *= 2;
3439 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
3440 mono_jit_stats.code_reallocs++;
3444 * Keep in sync with OP_JMP
3446 code = cfg->native_code + cfg->code_len;
3448 if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) {
3449 code = mono_arch_instrument_epilog (cfg, mono_trace_leave_method, code, TRUE);
3453 if (method->save_lmf) {
3455 /* all but r0-r3, sp and pc */
3456 pos += sizeof (MonoLMF) - (4 * 10);
3458 /* r2 contains the pointer to the current LMF */
3459 code = emit_big_add (code, ARMREG_R2, cfg->frame_reg, cfg->stack_usage - lmf_offset);
3460 /* ip = previous_lmf */
3461 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3463 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
3464 /* *(lmf_addr) = previous_lmf */
3465 ARM_STR_IMM (code, ARMREG_IP, ARMREG_LR, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3466 /* FIXME: speedup: there is no actual need to restore the registers if
3467 * we didn't actually change them (idea from Zoltan).
3470 /* point sp at the registers to restore: 10 is 14 -4, because we skip r0-r3 */
3471 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_R2, (sizeof (MonoLMF) - 10 * sizeof (gulong)));
3472 ARM_POP_NWB (code, 0xaff0); /* restore ip to sp and lr to pc */
3474 if ((i = mono_arm_is_rotated_imm8 (cfg->stack_usage, &rot_amount)) >= 0) {
3475 ARM_ADD_REG_IMM (code, ARMREG_SP, cfg->frame_reg, i, rot_amount);
3477 code = mono_arm_emit_load_imm (code, ARMREG_IP, cfg->stack_usage);
3478 ARM_ADD_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
3480 /* FIXME: add v4 thumb interworking support */
3481 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP) | (1 << ARMREG_PC)));
3484 cfg->code_len = code - cfg->native_code;
3486 g_assert (cfg->code_len < cfg->code_size);
3490 /* remove once throw_exception_by_name is eliminated */
3492 exception_id_by_name (const char *name)
3494 if (strcmp (name, "IndexOutOfRangeException") == 0)
3495 return MONO_EXC_INDEX_OUT_OF_RANGE;
3496 if (strcmp (name, "OverflowException") == 0)
3497 return MONO_EXC_OVERFLOW;
3498 if (strcmp (name, "ArithmeticException") == 0)
3499 return MONO_EXC_ARITHMETIC;
3500 if (strcmp (name, "DivideByZeroException") == 0)
3501 return MONO_EXC_DIVIDE_BY_ZERO;
3502 if (strcmp (name, "InvalidCastException") == 0)
3503 return MONO_EXC_INVALID_CAST;
3504 if (strcmp (name, "NullReferenceException") == 0)
3505 return MONO_EXC_NULL_REF;
3506 if (strcmp (name, "ArrayTypeMismatchException") == 0)
3507 return MONO_EXC_ARRAY_TYPE_MISMATCH;
3508 g_error ("Unknown intrinsic exception %s\n", name);
3513 mono_arch_emit_exceptions (MonoCompile *cfg)
3515 MonoJumpInfo *patch_info;
3518 const guint8* exc_throw_pos [MONO_EXC_INTRINS_NUM] = {NULL};
3519 guint8 exc_throw_found [MONO_EXC_INTRINS_NUM] = {0};
3520 int max_epilog_size = 50;
3522 /* count the number of exception infos */
3525 * make sure we have enough space for exceptions
3526 * 12 is the simulated call to throw_exception_by_name
3528 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
3529 if (patch_info->type == MONO_PATCH_INFO_EXC) {
3530 i = exception_id_by_name (patch_info->data.target);
3531 if (!exc_throw_found [i]) {
3532 max_epilog_size += 12;
3533 exc_throw_found [i] = TRUE;
3538 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
3539 cfg->code_size *= 2;
3540 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
3541 mono_jit_stats.code_reallocs++;
3544 code = cfg->native_code + cfg->code_len;
3546 /* add code to raise exceptions */
3547 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
3548 switch (patch_info->type) {
3549 case MONO_PATCH_INFO_EXC: {
3550 unsigned char *ip = patch_info->ip.i + cfg->native_code;
3551 const char *ex_name = patch_info->data.target;
3552 i = exception_id_by_name (patch_info->data.target);
3553 if (exc_throw_pos [i]) {
3554 arm_patch (ip, exc_throw_pos [i]);
3555 patch_info->type = MONO_PATCH_INFO_NONE;
3558 exc_throw_pos [i] = code;
3560 arm_patch (ip, code);
3561 //*(int*)code = 0xef9f0001;
3564 /*mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC_NAME, patch_info->data.target);*/
3565 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
3566 /* we got here from a conditional call, so the calling ip is set in lr already */
3567 patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD;
3568 patch_info->data.name = "mono_arch_throw_exception_by_name";
3569 patch_info->ip.i = code - cfg->native_code;
3571 *(gconstpointer*)code = ex_name;
3581 cfg->code_len = code - cfg->native_code;
3583 g_assert (cfg->code_len < cfg->code_size);
3588 mono_arch_setup_jit_tls_data (MonoJitTlsData *tls)
3593 mono_arch_free_jit_tls_data (MonoJitTlsData *tls)
3598 mono_arch_emit_this_vret_args (MonoCompile *cfg, MonoCallInst *inst, int this_reg, int this_type, int vt_reg)
3601 int this_dreg = ARMREG_R0;
3604 this_dreg = ARMREG_R1;
3606 /* add the this argument */
3607 if (this_reg != -1) {
3609 MONO_INST_NEW (cfg, this, OP_SETREG);
3610 this->type = this_type;
3611 this->sreg1 = this_reg;
3612 this->dreg = mono_regstate_next_int (cfg->rs);
3613 mono_bblock_add_inst (cfg->cbb, this);
3614 mono_call_inst_add_outarg_reg (cfg, inst, this->dreg, this_dreg, FALSE);
3619 MONO_INST_NEW (cfg, vtarg, OP_SETREG);
3620 vtarg->type = STACK_MP;
3621 vtarg->sreg1 = vt_reg;
3622 vtarg->dreg = mono_regstate_next_int (cfg->rs);
3623 mono_bblock_add_inst (cfg->cbb, vtarg);
3624 mono_call_inst_add_outarg_reg (cfg, inst, vtarg->dreg, ARMREG_R0, FALSE);
3629 mono_arch_get_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
3631 MonoInst *ins = NULL;
3632 if (cmethod->klass == mono_defaults.thread_class &&
3633 strcmp (cmethod->name, "MemoryBarrier") == 0) {
3634 MONO_INST_NEW (cfg, ins, OP_MEMORY_BARRIER);
3640 mono_arch_print_tree (MonoInst *tree, int arity)
3645 MonoInst* mono_arch_get_domain_intrinsic (MonoCompile* cfg)
3651 mono_arch_get_thread_intrinsic (MonoCompile* cfg)
3657 mono_arch_get_patch_offset (guint8 *code)
3664 mono_arch_flush_register_windows (void)
3669 mono_arch_fixup_jinfo (MonoCompile *cfg)
3671 /* max encoded stack usage is 64KB * 4 */
3672 g_assert ((cfg->stack_usage & ~(0xffff << 2)) == 0);
3673 cfg->jit_info->used_regs |= cfg->stack_usage << 14;
3676 #ifdef MONO_ARCH_HAVE_IMT
3679 mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call)
3684 mono_arch_find_imt_method (gpointer *regs, guint8 *code)
3686 guint32 *code_ptr = (guint32*)code;
3688 /* The IMT value is stored in the code stream right after the LDC instruction. */
3689 if (!IS_LDR_PC (code_ptr [0])) {
3690 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]);
3691 g_assert (IS_LDR_PC (code_ptr [0]));
3693 return (MonoMethod*) code_ptr [1];
3697 mono_arch_find_this_argument (gpointer *regs, MonoMethod *method)
3699 return mono_arch_get_this_arg_from_call (mono_method_signature (method), (gssize*)regs, NULL);
3703 #define ENABLE_WRONG_METHOD_CHECK 0
3704 #define BASE_SIZE (4 * 4)
3705 #define BSEARCH_ENTRY_SIZE (4 * 4)
3706 #define CMP_SIZE (3 * 4)
3707 #define BRANCH_SIZE (1 * 4)
3708 #define CALL_SIZE (2 * 4)
3709 #define WMC_SIZE (5 * 4)
3710 #define DISTANCE(A, B) (((gint32)(B)) - ((gint32)(A)))
3713 arm_emit_value_and_patch_ldr (arminstr_t *code, arminstr_t *target, guint32 value)
3715 guint32 delta = DISTANCE (target, code);
3717 g_assert (delta >= 0 && delta <= 0xFFF);
3718 *target = *target | delta;
3724 mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count)
3726 int size, i, extra_space = 0;
3727 arminstr_t *code, *start, *vtable_target = NULL;
3730 for (i = 0; i < count; ++i) {
3731 MonoIMTCheckItem *item = imt_entries [i];
3732 if (item->is_equals) {
3733 g_assert (arm_is_imm12 (DISTANCE (vtable, &vtable->vtable[item->vtable_slot])));
3735 if (item->check_target_idx) {
3736 if (!item->compare_done)
3737 item->chunk_size += CMP_SIZE;
3738 item->chunk_size += BRANCH_SIZE;
3740 #if ENABLE_WRONG_METHOD_CHECK
3741 item->chunk_size += WMC_SIZE;
3744 item->chunk_size += CALL_SIZE;
3746 item->chunk_size += BSEARCH_ENTRY_SIZE;
3747 imt_entries [item->check_target_idx]->compare_done = TRUE;
3749 size += item->chunk_size;
3752 start = code = mono_code_manager_reserve (domain->code_mp, size);
3755 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);
3756 for (i = 0; i < count; ++i) {
3757 MonoIMTCheckItem *item = imt_entries [i];
3758 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);
3762 ARM_PUSH2 (code, ARMREG_R0, ARMREG_R1);
3763 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, -4);
3764 vtable_target = code;
3765 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
3767 for (i = 0; i < count; ++i) {
3768 MonoIMTCheckItem *item = imt_entries [i];
3769 arminstr_t *imt_method = NULL;
3770 item->code_target = (guint8*)code;
3772 if (item->is_equals) {
3773 if (item->check_target_idx) {
3774 if (!item->compare_done) {
3776 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
3777 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
3779 item->jmp_code = (guint8*)code;
3780 ARM_B_COND (code, ARMCOND_NE, 0);
3782 ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
3783 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, DISTANCE (vtable, &vtable->vtable[item->vtable_slot]));
3785 /*Enable the commented code to assert on wrong method*/
3786 #if ENABLE_WRONG_METHOD_CHECK
3788 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
3789 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
3790 ARM_B_COND (code, ARMCOND_NE, 1);
3792 ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
3793 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, DISTANCE (vtable, &vtable->vtable[item->vtable_slot]));
3795 #if ENABLE_WRONG_METHOD_CHECK
3801 code = arm_emit_value_and_patch_ldr (code, imt_method, (guint32)item->method);
3803 /*must emit after unconditional branch*/
3804 if (vtable_target) {
3805 code = arm_emit_value_and_patch_ldr (code, vtable_target, (guint32)vtable);
3806 item->chunk_size += 4;
3807 vtable_target = NULL;
3810 /*We reserve the space for bsearch IMT values after the first entry with an absolute jump*/
3812 code += extra_space;
3816 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
3817 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
3819 item->jmp_code = (guint8*)code;
3820 ARM_B_COND (code, ARMCOND_GE, 0);
3825 for (i = 0; i < count; ++i) {
3826 MonoIMTCheckItem *item = imt_entries [i];
3827 if (item->jmp_code) {
3828 if (item->check_target_idx)
3829 arm_patch (item->jmp_code, imt_entries [item->check_target_idx]->code_target);
3831 if (i > 0 && item->is_equals) {
3833 arminstr_t *space_start = (arminstr_t*)(item->code_target + item->chunk_size);
3834 for (j = i - 1; j >= 0 && !imt_entries [j]->is_equals; --j) {
3835 space_start = arm_emit_value_and_patch_ldr (space_start, (arminstr_t*)imt_entries [j]->code_target, (guint32)imt_entries [j]->method);
3842 char *buff = g_strdup_printf ("thunk_for_class_%s_%s_entries_%d", vtable->klass->name_space, vtable->klass->name, count);
3843 mono_disassemble_code (NULL, (guint8*)start, size, buff);
3848 mono_arch_flush_icache ((guint8*)start, size);
3849 mono_stats.imt_thunks_size += code - start;
3851 g_assert (DISTANCE (start, code) <= size);