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 mono_arch_peephole_pass_1 (MonoCompile *cfg, MonoBasicBlock *bb)
1287 mono_arch_peephole_pass_2 (MonoCompile *cfg, MonoBasicBlock *bb)
1291 MONO_INST_LIST_FOR_EACH_ENTRY_SAFE (ins, n, &bb->ins_list, node) {
1292 MonoInst *last_ins = mono_inst_list_prev (&ins->node, &bb->ins_list);
1294 switch (ins->opcode) {
1296 /* remove unnecessary multiplication with 1 */
1297 if (ins->inst_imm == 1) {
1298 if (ins->dreg != ins->sreg1) {
1299 ins->opcode = OP_MOVE;
1305 int power2 = mono_is_power_of_two (ins->inst_imm);
1307 ins->opcode = OP_SHL_IMM;
1308 ins->inst_imm = power2;
1312 case OP_LOAD_MEMBASE:
1313 case OP_LOADI4_MEMBASE:
1315 * OP_STORE_MEMBASE_REG reg, offset(basereg)
1316 * OP_LOAD_MEMBASE offset(basereg), reg
1318 if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG
1319 || last_ins->opcode == OP_STORE_MEMBASE_REG) &&
1320 ins->inst_basereg == last_ins->inst_destbasereg &&
1321 ins->inst_offset == last_ins->inst_offset) {
1322 if (ins->dreg == last_ins->sreg1) {
1326 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1327 ins->opcode = OP_MOVE;
1328 ins->sreg1 = last_ins->sreg1;
1332 * Note: reg1 must be different from the basereg in the second load
1333 * OP_LOAD_MEMBASE offset(basereg), reg1
1334 * OP_LOAD_MEMBASE offset(basereg), reg2
1336 * OP_LOAD_MEMBASE offset(basereg), reg1
1337 * OP_MOVE reg1, reg2
1339 } if (last_ins && (last_ins->opcode == OP_LOADI4_MEMBASE
1340 || last_ins->opcode == OP_LOAD_MEMBASE) &&
1341 ins->inst_basereg != last_ins->dreg &&
1342 ins->inst_basereg == last_ins->inst_basereg &&
1343 ins->inst_offset == last_ins->inst_offset) {
1345 if (ins->dreg == last_ins->dreg) {
1349 ins->opcode = OP_MOVE;
1350 ins->sreg1 = last_ins->dreg;
1353 //g_assert_not_reached ();
1357 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1358 * OP_LOAD_MEMBASE offset(basereg), reg
1360 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1361 * OP_ICONST reg, imm
1363 } else if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_IMM
1364 || last_ins->opcode == OP_STORE_MEMBASE_IMM) &&
1365 ins->inst_basereg == last_ins->inst_destbasereg &&
1366 ins->inst_offset == last_ins->inst_offset) {
1367 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1368 ins->opcode = OP_ICONST;
1369 ins->inst_c0 = last_ins->inst_imm;
1370 g_assert_not_reached (); // check this rule
1374 case OP_LOADU1_MEMBASE:
1375 case OP_LOADI1_MEMBASE:
1376 if (last_ins && (last_ins->opcode == OP_STOREI1_MEMBASE_REG) &&
1377 ins->inst_basereg == last_ins->inst_destbasereg &&
1378 ins->inst_offset == last_ins->inst_offset) {
1379 ins->opcode = (ins->opcode == OP_LOADI1_MEMBASE) ? OP_ICONV_TO_I1 : OP_ICONV_TO_U1;
1380 ins->sreg1 = last_ins->sreg1;
1383 case OP_LOADU2_MEMBASE:
1384 case OP_LOADI2_MEMBASE:
1385 if (last_ins && (last_ins->opcode == OP_STOREI2_MEMBASE_REG) &&
1386 ins->inst_basereg == last_ins->inst_destbasereg &&
1387 ins->inst_offset == last_ins->inst_offset) {
1388 ins->opcode = (ins->opcode == OP_LOADI2_MEMBASE) ? OP_ICONV_TO_I2 : OP_ICONV_TO_U2;
1389 ins->sreg1 = last_ins->sreg1;
1393 ins->opcode = OP_MOVE;
1397 if (ins->dreg == ins->sreg1) {
1402 * OP_MOVE sreg, dreg
1403 * OP_MOVE dreg, sreg
1405 if (last_ins && last_ins->opcode == OP_MOVE &&
1406 ins->sreg1 == last_ins->dreg &&
1407 ins->dreg == last_ins->sreg1) {
1417 * the branch_cc_table should maintain the order of these
1431 branch_cc_table [] = {
1446 #define NEW_INS(cfg,ins,dest,op) do { \
1447 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
1448 (dest)->opcode = (op); \
1449 MONO_INST_LIST_ADD_TAIL (&(dest)->node, &(ins)->node); \
1453 map_to_reg_reg_op (int op)
1462 case OP_COMPARE_IMM:
1476 case OP_LOAD_MEMBASE:
1477 return OP_LOAD_MEMINDEX;
1478 case OP_LOADI4_MEMBASE:
1479 return OP_LOADI4_MEMINDEX;
1480 case OP_LOADU4_MEMBASE:
1481 return OP_LOADU4_MEMINDEX;
1482 case OP_LOADU1_MEMBASE:
1483 return OP_LOADU1_MEMINDEX;
1484 case OP_LOADI2_MEMBASE:
1485 return OP_LOADI2_MEMINDEX;
1486 case OP_LOADU2_MEMBASE:
1487 return OP_LOADU2_MEMINDEX;
1488 case OP_LOADI1_MEMBASE:
1489 return OP_LOADI1_MEMINDEX;
1490 case OP_STOREI1_MEMBASE_REG:
1491 return OP_STOREI1_MEMINDEX;
1492 case OP_STOREI2_MEMBASE_REG:
1493 return OP_STOREI2_MEMINDEX;
1494 case OP_STOREI4_MEMBASE_REG:
1495 return OP_STOREI4_MEMINDEX;
1496 case OP_STORE_MEMBASE_REG:
1497 return OP_STORE_MEMINDEX;
1498 case OP_STORER4_MEMBASE_REG:
1499 return OP_STORER4_MEMINDEX;
1500 case OP_STORER8_MEMBASE_REG:
1501 return OP_STORER8_MEMINDEX;
1502 case OP_STORE_MEMBASE_IMM:
1503 return OP_STORE_MEMBASE_REG;
1504 case OP_STOREI1_MEMBASE_IMM:
1505 return OP_STOREI1_MEMBASE_REG;
1506 case OP_STOREI2_MEMBASE_IMM:
1507 return OP_STOREI2_MEMBASE_REG;
1508 case OP_STOREI4_MEMBASE_IMM:
1509 return OP_STOREI4_MEMBASE_REG;
1511 g_assert_not_reached ();
1515 * Remove from the instruction list the instructions that can't be
1516 * represented with very simple instructions with no register
1520 mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb)
1522 int rot_amount, imm8, low_imm;
1523 MonoInst *ins, *temp;
1525 /* setup the virtual reg allocator */
1526 if (bb->max_vreg > cfg->rs->next_vreg)
1527 cfg->rs->next_vreg = bb->max_vreg;
1529 MONO_BB_FOR_EACH_INS (bb, ins) {
1533 last_ins = mono_inst_list_prev (&ins->node, &bb->ins_list);
1534 switch (ins->opcode) {
1538 case OP_COMPARE_IMM:
1545 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount)) < 0) {
1546 NEW_INS (cfg, ins, temp, OP_ICONST);
1547 temp->inst_c0 = ins->inst_imm;
1548 temp->dreg = mono_regstate_next_int (cfg->rs);
1549 ins->sreg2 = temp->dreg;
1550 ins->opcode = map_to_reg_reg_op (ins->opcode);
1554 if (ins->inst_imm == 1) {
1555 ins->opcode = OP_MOVE;
1558 if (ins->inst_imm == 0) {
1559 ins->opcode = OP_ICONST;
1563 imm8 = mono_is_power_of_two (ins->inst_imm);
1565 ins->opcode = OP_SHL_IMM;
1566 ins->inst_imm = imm8;
1569 NEW_INS (cfg, ins, temp, OP_ICONST);
1570 temp->inst_c0 = ins->inst_imm;
1571 temp->dreg = mono_regstate_next_int (cfg->rs);
1572 ins->sreg2 = temp->dreg;
1573 ins->opcode = OP_IMUL;
1575 case OP_LOAD_MEMBASE:
1576 case OP_LOADI4_MEMBASE:
1577 case OP_LOADU4_MEMBASE:
1578 case OP_LOADU1_MEMBASE:
1579 /* we can do two things: load the immed in a register
1580 * and use an indexed load, or see if the immed can be
1581 * represented as an ad_imm + a load with a smaller offset
1582 * that fits. We just do the first for now, optimize later.
1584 if (arm_is_imm12 (ins->inst_offset))
1586 NEW_INS (cfg, ins, temp, OP_ICONST);
1587 temp->inst_c0 = ins->inst_offset;
1588 temp->dreg = mono_regstate_next_int (cfg->rs);
1589 ins->sreg2 = temp->dreg;
1590 ins->opcode = map_to_reg_reg_op (ins->opcode);
1592 case OP_LOADI2_MEMBASE:
1593 case OP_LOADU2_MEMBASE:
1594 case OP_LOADI1_MEMBASE:
1595 if (arm_is_imm8 (ins->inst_offset))
1597 NEW_INS (cfg, ins, temp, OP_ICONST);
1598 temp->inst_c0 = ins->inst_offset;
1599 temp->dreg = mono_regstate_next_int (cfg->rs);
1600 ins->sreg2 = temp->dreg;
1601 ins->opcode = map_to_reg_reg_op (ins->opcode);
1603 case OP_LOADR4_MEMBASE:
1604 case OP_LOADR8_MEMBASE:
1605 if (arm_is_fpimm8 (ins->inst_offset))
1607 low_imm = ins->inst_offset & 0x1ff;
1608 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~0x1ff, &rot_amount)) >= 0) {
1609 NEW_INS (cfg, ins, temp, OP_ADD_IMM);
1610 temp->inst_imm = ins->inst_offset & ~0x1ff;
1611 temp->sreg1 = ins->inst_basereg;
1612 temp->dreg = mono_regstate_next_int (cfg->rs);
1613 ins->inst_basereg = temp->dreg;
1614 ins->inst_offset = low_imm;
1617 /* VFP/FPA doesn't have indexed load instructions */
1618 g_assert_not_reached ();
1620 case OP_STORE_MEMBASE_REG:
1621 case OP_STOREI4_MEMBASE_REG:
1622 case OP_STOREI1_MEMBASE_REG:
1623 if (arm_is_imm12 (ins->inst_offset))
1625 NEW_INS (cfg, ins, temp, OP_ICONST);
1626 temp->inst_c0 = ins->inst_offset;
1627 temp->dreg = mono_regstate_next_int (cfg->rs);
1628 ins->sreg2 = temp->dreg;
1629 ins->opcode = map_to_reg_reg_op (ins->opcode);
1631 case OP_STOREI2_MEMBASE_REG:
1632 if (arm_is_imm8 (ins->inst_offset))
1634 NEW_INS (cfg, ins, temp, OP_ICONST);
1635 temp->inst_c0 = ins->inst_offset;
1636 temp->dreg = mono_regstate_next_int (cfg->rs);
1637 ins->sreg2 = temp->dreg;
1638 ins->opcode = map_to_reg_reg_op (ins->opcode);
1640 case OP_STORER4_MEMBASE_REG:
1641 case OP_STORER8_MEMBASE_REG:
1642 if (arm_is_fpimm8 (ins->inst_offset))
1644 low_imm = ins->inst_offset & 0x1ff;
1645 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~ 0x1ff, &rot_amount)) >= 0 && arm_is_fpimm8 (low_imm)) {
1646 NEW_INS (cfg, ins, temp, OP_ADD_IMM);
1647 temp->inst_imm = ins->inst_offset & ~0x1ff;
1648 temp->sreg1 = ins->inst_destbasereg;
1649 temp->dreg = mono_regstate_next_int (cfg->rs);
1650 ins->inst_destbasereg = temp->dreg;
1651 ins->inst_offset = low_imm;
1654 /*g_print ("fail with: %d (%d, %d)\n", ins->inst_offset, ins->inst_offset & ~0x1ff, low_imm);*/
1655 /* VFP/FPA doesn't have indexed store instructions */
1656 g_assert_not_reached ();
1658 case OP_STORE_MEMBASE_IMM:
1659 case OP_STOREI1_MEMBASE_IMM:
1660 case OP_STOREI2_MEMBASE_IMM:
1661 case OP_STOREI4_MEMBASE_IMM:
1662 NEW_INS (cfg, ins, temp, OP_ICONST);
1663 temp->inst_c0 = ins->inst_imm;
1664 temp->dreg = mono_regstate_next_int (cfg->rs);
1665 ins->sreg1 = temp->dreg;
1666 ins->opcode = map_to_reg_reg_op (ins->opcode);
1667 goto loop_start; /* make it handle the possibly big ins->inst_offset */
1670 bb->max_vreg = cfg->rs->next_vreg;
1674 emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size, gboolean is_signed)
1676 /* sreg is a float, dreg is an integer reg */
1678 ARM_FIXZ (code, dreg, sreg);
1679 #elif defined(ARM_FPU_VFP)
1681 ARM_TOSIZD (code, ARM_VFP_F0, sreg);
1683 ARM_TOUIZD (code, ARM_VFP_F0, sreg);
1684 ARM_FMRS (code, dreg, ARM_VFP_F0);
1688 ARM_AND_REG_IMM8 (code, dreg, dreg, 0xff);
1689 else if (size == 2) {
1690 ARM_SHL_IMM (code, dreg, dreg, 16);
1691 ARM_SHR_IMM (code, dreg, dreg, 16);
1695 ARM_SHL_IMM (code, dreg, dreg, 24);
1696 ARM_SAR_IMM (code, dreg, dreg, 24);
1697 } else if (size == 2) {
1698 ARM_SHL_IMM (code, dreg, dreg, 16);
1699 ARM_SAR_IMM (code, dreg, dreg, 16);
1707 const guchar *target;
1712 #define is_call_imm(diff) ((gint)(diff) >= -33554432 && (gint)(diff) <= 33554431)
1715 search_thunk_slot (void *data, int csize, int bsize, void *user_data) {
1716 PatchData *pdata = (PatchData*)user_data;
1717 guchar *code = data;
1718 guint32 *thunks = data;
1719 guint32 *endthunks = (guint32*)(code + bsize);
1721 int difflow, diffhigh;
1723 /* always ensure a call from pdata->code can reach to the thunks without further thunks */
1724 difflow = (char*)pdata->code - (char*)thunks;
1725 diffhigh = (char*)pdata->code - (char*)endthunks;
1726 if (!((is_call_imm (thunks) && is_call_imm (endthunks)) || (is_call_imm (difflow) && is_call_imm (diffhigh))))
1730 * The thunk is composed of 3 words:
1731 * load constant from thunks [2] into ARM_IP
1734 * Note that the LR register is already setup
1736 //g_print ("thunk nentries: %d\n", ((char*)endthunks - (char*)thunks)/16);
1737 if ((pdata->found == 2) || (pdata->code >= code && pdata->code <= code + csize)) {
1738 while (thunks < endthunks) {
1739 //g_print ("looking for target: %p at %p (%08x-%08x)\n", pdata->target, thunks, thunks [0], thunks [1]);
1740 if (thunks [2] == (guint32)pdata->target) {
1741 arm_patch (pdata->code, (guchar*)thunks);
1742 mono_arch_flush_icache (pdata->code, 4);
1745 } else if ((thunks [0] == 0) && (thunks [1] == 0) && (thunks [2] == 0)) {
1746 /* found a free slot instead: emit thunk */
1747 /* ARMREG_IP is fine to use since this can't be an IMT call
1750 code = (guchar*)thunks;
1751 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
1752 if (thumb_supported)
1753 ARM_BX (code, ARMREG_IP);
1755 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
1756 thunks [2] = (guint32)pdata->target;
1757 mono_arch_flush_icache ((guchar*)thunks, 12);
1759 arm_patch (pdata->code, (guchar*)thunks);
1760 mono_arch_flush_icache (pdata->code, 4);
1764 /* skip 12 bytes, the size of the thunk */
1768 //g_print ("failed thunk lookup for %p from %p at %p (%d entries)\n", pdata->target, pdata->code, data, count);
1774 handle_thunk (int absolute, guchar *code, const guchar *target) {
1775 MonoDomain *domain = mono_domain_get ();
1779 pdata.target = target;
1780 pdata.absolute = absolute;
1783 mono_domain_lock (domain);
1784 mono_code_manager_foreach (domain->code_mp, search_thunk_slot, &pdata);
1787 /* this uses the first available slot */
1789 mono_code_manager_foreach (domain->code_mp, search_thunk_slot, &pdata);
1791 mono_domain_unlock (domain);
1793 if (pdata.found != 1)
1794 g_print ("thunk failed for %p from %p\n", target, code);
1795 g_assert (pdata.found == 1);
1799 arm_patch (guchar *code, const guchar *target)
1801 guint32 *code32 = (void*)code;
1802 guint32 ins = *code32;
1803 guint32 prim = (ins >> 25) & 7;
1804 guint32 tval = GPOINTER_TO_UINT (target);
1806 //g_print ("patching 0x%08x (0x%08x) to point to 0x%08x\n", code, ins, target);
1807 if (prim == 5) { /* 101b */
1808 /* the diff starts 8 bytes from the branch opcode */
1809 gint diff = target - code - 8;
1811 gint tmask = 0xffffffff;
1812 if (tval & 1) { /* entering thumb mode */
1813 diff = target - 1 - code - 8;
1814 g_assert (thumb_supported);
1815 tbits = 0xf << 28; /* bl->blx bit pattern */
1816 g_assert ((ins & (1 << 24))); /* it must be a bl, not b instruction */
1817 /* this low bit of the displacement is moved to bit 24 in the instruction encoding */
1821 tmask = ~(1 << 24); /* clear the link bit */
1822 /*g_print ("blx to thumb: target: %p, code: %p, diff: %d, mask: %x\n", target, code, diff, tmask);*/
1827 if (diff <= 33554431) {
1829 ins = (ins & 0xff000000) | diff;
1831 *code32 = ins | tbits;
1835 /* diff between 0 and -33554432 */
1836 if (diff >= -33554432) {
1838 ins = (ins & 0xff000000) | (diff & ~0xff000000);
1840 *code32 = ins | tbits;
1845 handle_thunk (TRUE, code, target);
1850 * The alternative call sequences looks like this:
1852 * ldr ip, [pc] // loads the address constant
1853 * b 1f // jumps around the constant
1854 * address constant embedded in the code
1859 * There are two cases for patching:
1860 * a) at the end of method emission: in this case code points to the start
1861 * of the call sequence
1862 * b) during runtime patching of the call site: in this case code points
1863 * to the mov pc, ip instruction
1865 * We have to handle also the thunk jump code sequence:
1869 * address constant // execution never reaches here
1871 if ((ins & 0x0ffffff0) == 0x12fff10) {
1872 /* Branch and exchange: the address is constructed in a reg
1873 * We can patch BX when the code sequence is the following:
1874 * ldr ip, [pc, #0] ; 0x8
1881 guint8 *emit = (guint8*)ccode;
1882 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
1884 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
1885 ARM_BX (emit, ARMREG_IP);
1887 /*patching from magic trampoline*/
1888 if (ins == ccode [3]) {
1889 g_assert (code32 [-4] == ccode [0]);
1890 g_assert (code32 [-3] == ccode [1]);
1891 g_assert (code32 [-1] == ccode [2]);
1892 code32 [-2] = (guint32)target;
1895 /*patching from JIT*/
1896 if (ins == ccode [0]) {
1897 g_assert (code32 [1] == ccode [1]);
1898 g_assert (code32 [3] == ccode [2]);
1899 g_assert (code32 [4] == ccode [3]);
1900 code32 [2] = (guint32)target;
1903 g_assert_not_reached ();
1904 } else if ((ins & 0x0ffffff0) == 0x12fff30) {
1912 guint8 *emit = (guint8*)ccode;
1913 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
1915 ARM_BLX_REG (emit, ARMREG_IP);
1917 g_assert (code32 [-3] == ccode [0]);
1918 g_assert (code32 [-2] == ccode [1]);
1919 g_assert (code32 [0] == ccode [2]);
1921 code32 [-1] = (guint32)target;
1924 guint32 *tmp = ccode;
1925 guint8 *emit = (guint8*)tmp;
1926 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
1927 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
1928 ARM_MOV_REG_REG (emit, ARMREG_PC, ARMREG_IP);
1929 ARM_BX (emit, ARMREG_IP);
1930 if (ins == ccode [2]) {
1931 g_assert_not_reached (); // should be -2 ...
1932 code32 [-1] = (guint32)target;
1935 if (ins == ccode [0]) {
1936 /* handles both thunk jump code and the far call sequence */
1937 code32 [2] = (guint32)target;
1940 g_assert_not_reached ();
1942 // g_print ("patched with 0x%08x\n", ins);
1946 * Return the >= 0 uimm8 value if val can be represented with a byte + rotation
1947 * (with the rotation amount in *rot_amount. rot_amount is already adjusted
1948 * to be used with the emit macros.
1949 * Return -1 otherwise.
1952 mono_arm_is_rotated_imm8 (guint32 val, gint *rot_amount)
1955 for (i = 0; i < 31; i+= 2) {
1956 res = (val << (32 - i)) | (val >> i);
1959 *rot_amount = i? 32 - i: 0;
1966 * Emits in code a sequence of instructions that load the value 'val'
1967 * into the dreg register. Uses at most 4 instructions.
1970 mono_arm_emit_load_imm (guint8 *code, int dreg, guint32 val)
1972 int imm8, rot_amount;
1974 ARM_LDR_IMM (code, dreg, ARMREG_PC, 0);
1975 /* skip the constant pool */
1981 if ((imm8 = mono_arm_is_rotated_imm8 (val, &rot_amount)) >= 0) {
1982 ARM_MOV_REG_IMM (code, dreg, imm8, rot_amount);
1983 } else if ((imm8 = mono_arm_is_rotated_imm8 (~val, &rot_amount)) >= 0) {
1984 ARM_MVN_REG_IMM (code, dreg, imm8, rot_amount);
1987 ARM_MOV_REG_IMM8 (code, dreg, (val & 0xFF));
1989 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF00) >> 8, 24);
1991 if (val & 0xFF0000) {
1992 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
1994 if (val & 0xFF000000) {
1995 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
1997 } else if (val & 0xFF00) {
1998 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF00) >> 8, 24);
1999 if (val & 0xFF0000) {
2000 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
2002 if (val & 0xFF000000) {
2003 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2005 } else if (val & 0xFF0000) {
2006 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF0000) >> 16, 16);
2007 if (val & 0xFF000000) {
2008 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2011 //g_assert_not_reached ();
2017 mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
2022 guint8 *code = cfg->native_code + cfg->code_len;
2023 guint last_offset = 0;
2025 int imm8, rot_amount;
2027 /* we don't align basic blocks of loops on arm */
2029 if (cfg->verbose_level > 2)
2030 g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset);
2032 cpos = bb->max_offset;
2034 if (cfg->prof_options & MONO_PROFILE_COVERAGE) {
2035 //MonoCoverageInfo *cov = mono_get_coverage_info (cfg->method);
2036 //g_assert (!mono_compile_aot);
2039 // cov->data [bb->dfn].iloffset = bb->cil_code - cfg->cil_code;
2040 /* this is not thread save, but good enough */
2041 /* fixme: howto handle overflows? */
2042 //x86_inc_mem (code, &cov->data [bb->dfn].count);
2045 MONO_BB_FOR_EACH_INS (bb, ins) {
2046 offset = code - cfg->native_code;
2048 max_len = ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
2050 if (offset > (cfg->code_size - max_len - 16)) {
2051 cfg->code_size *= 2;
2052 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
2053 code = cfg->native_code + offset;
2055 // if (ins->cil_code)
2056 // g_print ("cil code\n");
2057 mono_debug_record_line_number (cfg, ins, offset);
2059 switch (ins->opcode) {
2060 case OP_MEMORY_BARRIER:
2063 g_assert_not_reached ();
2066 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2067 ppc_mulhw (code, ppc_r3, ins->sreg1, ins->sreg2);
2070 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2071 ppc_mulhwu (code, ppc_r3, ins->sreg1, ins->sreg2);
2073 case OP_STOREI1_MEMBASE_IMM:
2074 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFF);
2075 g_assert (arm_is_imm12 (ins->inst_offset));
2076 ARM_STRB_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2078 case OP_STOREI2_MEMBASE_IMM:
2079 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFFFF);
2080 g_assert (arm_is_imm8 (ins->inst_offset));
2081 ARM_STRH_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2083 case OP_STORE_MEMBASE_IMM:
2084 case OP_STOREI4_MEMBASE_IMM:
2085 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm);
2086 g_assert (arm_is_imm12 (ins->inst_offset));
2087 ARM_STR_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2089 case OP_STOREI1_MEMBASE_REG:
2090 g_assert (arm_is_imm12 (ins->inst_offset));
2091 ARM_STRB_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2093 case OP_STOREI2_MEMBASE_REG:
2094 g_assert (arm_is_imm8 (ins->inst_offset));
2095 ARM_STRH_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2097 case OP_STORE_MEMBASE_REG:
2098 case OP_STOREI4_MEMBASE_REG:
2099 /* this case is special, since it happens for spill code after lowering has been called */
2100 if (arm_is_imm12 (ins->inst_offset)) {
2101 ARM_STR_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2103 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2104 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ARMREG_LR);
2107 case OP_STOREI1_MEMINDEX:
2108 ARM_STRB_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2110 case OP_STOREI2_MEMINDEX:
2111 /* note: the args are reversed in the macro */
2112 ARM_STRH_REG_REG (code, ins->inst_destbasereg, ins->sreg1, ins->sreg2);
2114 case OP_STORE_MEMINDEX:
2115 case OP_STOREI4_MEMINDEX:
2116 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2119 g_assert_not_reached ();
2121 case OP_LOAD_MEMINDEX:
2122 case OP_LOADI4_MEMINDEX:
2123 case OP_LOADU4_MEMINDEX:
2124 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2126 case OP_LOADI1_MEMINDEX:
2127 /* note: the args are reversed in the macro */
2128 ARM_LDRSB_REG_REG (code, ins->inst_basereg, ins->dreg, ins->sreg2);
2130 case OP_LOADU1_MEMINDEX:
2131 ARM_LDRB_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2133 case OP_LOADI2_MEMINDEX:
2134 /* note: the args are reversed in the macro */
2135 ARM_LDRSH_REG_REG (code, ins->inst_basereg, ins->dreg, ins->sreg2);
2137 case OP_LOADU2_MEMINDEX:
2138 /* note: the args are reversed in the macro */
2139 ARM_LDRH_REG_REG (code, ins->inst_basereg, ins->dreg, ins->sreg2);
2141 case OP_LOAD_MEMBASE:
2142 case OP_LOADI4_MEMBASE:
2143 case OP_LOADU4_MEMBASE:
2144 /* this case is special, since it happens for spill code after lowering has been called */
2145 if (arm_is_imm12 (ins->inst_offset)) {
2146 ARM_LDR_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2148 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2149 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ARMREG_LR);
2152 case OP_LOADI1_MEMBASE:
2153 g_assert (arm_is_imm8 (ins->inst_offset));
2154 ARM_LDRSB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2156 case OP_LOADU1_MEMBASE:
2157 g_assert (arm_is_imm12 (ins->inst_offset));
2158 ARM_LDRB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2160 case OP_LOADU2_MEMBASE:
2161 g_assert (arm_is_imm8 (ins->inst_offset));
2162 ARM_LDRH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2164 case OP_LOADI2_MEMBASE:
2165 g_assert (arm_is_imm8 (ins->inst_offset));
2166 ARM_LDRSH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2168 case OP_ICONV_TO_I1:
2169 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 24);
2170 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 24);
2172 case OP_ICONV_TO_I2:
2173 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2174 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 16);
2176 case OP_ICONV_TO_U1:
2177 ARM_AND_REG_IMM8 (code, ins->dreg, ins->sreg1, 0xff);
2179 case OP_ICONV_TO_U2:
2180 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2181 ARM_SHR_IMM (code, ins->dreg, ins->dreg, 16);
2184 ARM_CMP_REG_REG (code, ins->sreg1, ins->sreg2);
2186 case OP_COMPARE_IMM:
2187 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2188 g_assert (imm8 >= 0);
2189 ARM_CMP_REG_IMM (code, ins->sreg1, imm8, rot_amount);
2192 *(int*)code = 0xe7f001f0;
2193 *(int*)code = 0xef9f0001;
2198 ARM_ADDS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2201 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2204 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2207 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2208 g_assert (imm8 >= 0);
2209 ARM_ADDS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2212 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2213 g_assert (imm8 >= 0);
2214 ARM_ADD_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2217 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2218 g_assert (imm8 >= 0);
2219 ARM_ADCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2222 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2223 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2225 case OP_IADD_OVF_UN:
2226 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2227 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2230 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2231 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2233 case OP_ISUB_OVF_UN:
2234 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2235 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2237 case OP_ADD_OVF_CARRY:
2238 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2239 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2241 case OP_ADD_OVF_UN_CARRY:
2242 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2243 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2245 case OP_SUB_OVF_CARRY:
2246 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2247 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2249 case OP_SUB_OVF_UN_CARRY:
2250 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2251 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2254 ARM_SUBS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2257 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2258 g_assert (imm8 >= 0);
2259 ARM_SUBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2262 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2265 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2268 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2269 g_assert (imm8 >= 0);
2270 ARM_SUB_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2273 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2274 g_assert (imm8 >= 0);
2275 ARM_SBCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2277 case OP_ARM_RSBS_IMM:
2278 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2279 g_assert (imm8 >= 0);
2280 ARM_RSBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2282 case OP_ARM_RSC_IMM:
2283 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2284 g_assert (imm8 >= 0);
2285 ARM_RSC_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2288 ARM_AND_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2291 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2292 g_assert (imm8 >= 0);
2293 ARM_AND_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2301 /* crappy ARM arch doesn't have a DIV instruction */
2302 g_assert_not_reached ();
2304 ARM_ORR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2307 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2308 g_assert (imm8 >= 0);
2309 ARM_ORR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2312 ARM_EOR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2315 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2316 g_assert (imm8 >= 0);
2317 ARM_EOR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2320 ARM_SHL_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2324 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2327 ARM_SAR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2331 ARM_SAR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2335 ARM_SHR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2338 ARM_SHR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2341 ARM_MVN_REG_REG (code, ins->dreg, ins->sreg1);
2344 ARM_RSB_REG_IMM8 (code, ins->dreg, ins->sreg1, 0);
2347 if (ins->dreg == ins->sreg2)
2348 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2350 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg2, ins->sreg1);
2353 g_assert_not_reached ();
2356 /* FIXME: handle ovf/ sreg2 != dreg */
2357 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2359 case OP_IMUL_OVF_UN:
2360 /* FIXME: handle ovf/ sreg2 != dreg */
2361 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2364 code = mono_arm_emit_load_imm (code, ins->dreg, ins->inst_c0);
2367 /* Load the GOT offset */
2368 mono_add_patch_info (cfg, offset, (MonoJumpInfoType)ins->inst_i1, ins->inst_p0);
2369 ARM_LDR_IMM (code, ins->dreg, ARMREG_PC, 0);
2371 *(gpointer*)code = NULL;
2373 /* Load the value from the GOT */
2374 ARM_LDR_REG_REG (code, ins->dreg, ARMREG_PC, ins->dreg);
2376 case OP_ICONV_TO_I4:
2377 case OP_ICONV_TO_U4:
2379 if (ins->dreg != ins->sreg1)
2380 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2383 int saved = ins->sreg2;
2384 if (ins->sreg2 == ARM_LSW_REG) {
2385 ARM_MOV_REG_REG (code, ARMREG_LR, ins->sreg2);
2388 if (ins->sreg1 != ARM_LSW_REG)
2389 ARM_MOV_REG_REG (code, ARM_LSW_REG, ins->sreg1);
2390 if (saved != ARM_MSW_REG)
2391 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 if (ins->sreg1 != ARMREG_R0)
2508 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
2509 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2510 (gpointer)"mono_arch_throw_exception");
2511 code = emit_call_seq (cfg, code);
2515 if (ins->sreg1 != ARMREG_R0)
2516 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
2517 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2518 (gpointer)"mono_arch_rethrow_exception");
2519 code = emit_call_seq (cfg, code);
2522 case OP_START_HANDLER:
2523 if (arm_is_imm12 (ins->inst_left->inst_offset)) {
2524 ARM_STR_IMM (code, ARMREG_LR, ins->inst_left->inst_basereg, ins->inst_left->inst_offset);
2526 code = mono_arm_emit_load_imm (code, ARMREG_IP, ins->inst_left->inst_offset);
2527 ARM_STR_REG_REG (code, ARMREG_LR, ins->inst_left->inst_basereg, ARMREG_IP);
2531 if (ins->sreg1 != ARMREG_R0)
2532 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
2533 if (arm_is_imm12 (ins->inst_left->inst_offset)) {
2534 ARM_LDR_IMM (code, ARMREG_IP, ins->inst_left->inst_basereg, ins->inst_left->inst_offset);
2536 g_assert (ARMREG_IP != ins->inst_left->inst_basereg);
2537 code = mono_arm_emit_load_imm (code, ARMREG_IP, ins->inst_left->inst_offset);
2538 ARM_LDR_REG_REG (code, ARMREG_IP, ins->inst_left->inst_basereg, ARMREG_IP);
2540 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
2543 if (arm_is_imm12 (ins->inst_left->inst_offset)) {
2544 ARM_LDR_IMM (code, ARMREG_IP, ins->inst_left->inst_basereg, ins->inst_left->inst_offset);
2546 g_assert (ARMREG_IP != ins->inst_left->inst_basereg);
2547 code = mono_arm_emit_load_imm (code, ARMREG_IP, ins->inst_left->inst_offset);
2548 ARM_LDR_REG_REG (code, ARMREG_IP, ins->inst_left->inst_basereg, ARMREG_IP);
2550 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
2552 case OP_CALL_HANDLER:
2553 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb);
2557 ins->inst_c0 = code - cfg->native_code;
2560 if (ins->flags & MONO_INST_BRLABEL) {
2561 /*if (ins->inst_i0->inst_c0) {
2563 //x86_jump_code (code, cfg->native_code + ins->inst_i0->inst_c0);
2565 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_LABEL, ins->inst_i0);
2569 /*if (ins->inst_target_bb->native_offset) {
2571 //x86_jump_code (code, cfg->native_code + ins->inst_target_bb->native_offset);
2573 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb);
2579 ARM_MOV_REG_REG (code, ARMREG_PC, ins->sreg1);
2583 * In the normal case we have:
2584 * ldr pc, [pc, ins->sreg1 << 2]
2587 * ldr lr, [pc, ins->sreg1 << 2]
2589 * After follows the data.
2590 * FIXME: add aot support.
2592 max_len += 4 * GPOINTER_TO_INT (ins->klass);
2593 if (offset > (cfg->code_size - max_len - 16)) {
2594 cfg->code_size += max_len;
2595 cfg->code_size *= 2;
2596 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
2597 code = cfg->native_code + offset;
2599 ARM_LDR_REG_REG_SHIFT (code, ARMREG_PC, ARMREG_PC, ins->sreg1, ARMSHIFT_LSL, 2);
2601 code += 4 * GPOINTER_TO_INT (ins->klass);
2604 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
2605 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
2608 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2609 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LT);
2612 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2613 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LO);
2616 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2617 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_GT);
2620 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2621 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_HI);
2623 case OP_COND_EXC_EQ:
2624 case OP_COND_EXC_NE_UN:
2625 case OP_COND_EXC_LT:
2626 case OP_COND_EXC_LT_UN:
2627 case OP_COND_EXC_GT:
2628 case OP_COND_EXC_GT_UN:
2629 case OP_COND_EXC_GE:
2630 case OP_COND_EXC_GE_UN:
2631 case OP_COND_EXC_LE:
2632 case OP_COND_EXC_LE_UN:
2633 EMIT_COND_SYSTEM_EXCEPTION (ins->opcode - OP_COND_EXC_EQ, ins->inst_p1);
2636 case OP_COND_EXC_OV:
2637 case OP_COND_EXC_NC:
2638 case OP_COND_EXC_NO:
2639 g_assert_not_reached ();
2651 EMIT_COND_BRANCH (ins, ins->opcode - OP_IBEQ);
2654 /* floating point opcodes */
2657 if (cfg->compile_aot) {
2658 ARM_LDFD (code, ins->dreg, ARMREG_PC, 0);
2660 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
2662 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
2665 /* FIXME: we can optimize the imm load by dealing with part of
2666 * the displacement in LDFD (aligning to 512).
2668 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
2669 ARM_LDFD (code, ins->dreg, ARMREG_LR, 0);
2673 if (cfg->compile_aot) {
2674 ARM_LDFS (code, ins->dreg, ARMREG_PC, 0);
2676 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
2679 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
2680 ARM_LDFS (code, ins->dreg, ARMREG_LR, 0);
2683 case OP_STORER8_MEMBASE_REG:
2684 g_assert (arm_is_fpimm8 (ins->inst_offset));
2685 ARM_STFD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2687 case OP_LOADR8_MEMBASE:
2688 g_assert (arm_is_fpimm8 (ins->inst_offset));
2689 ARM_LDFD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2691 case OP_STORER4_MEMBASE_REG:
2692 g_assert (arm_is_fpimm8 (ins->inst_offset));
2693 ARM_STFS (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2695 case OP_LOADR4_MEMBASE:
2696 g_assert (arm_is_fpimm8 (ins->inst_offset));
2697 ARM_LDFS (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2699 case OP_ICONV_TO_R_UN: {
2701 tmpreg = ins->dreg == 0? 1: 0;
2702 ARM_CMP_REG_IMM8 (code, ins->sreg1, 0);
2703 ARM_FLTD (code, ins->dreg, ins->sreg1);
2704 ARM_B_COND (code, ARMCOND_GE, 8);
2705 /* save the temp register */
2706 ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
2707 ARM_STFD (code, tmpreg, ARMREG_SP, 0);
2708 ARM_LDFD (code, tmpreg, ARMREG_PC, 12);
2709 ARM_FPA_ADFD (code, ins->dreg, ins->dreg, tmpreg);
2710 ARM_LDFD (code, tmpreg, ARMREG_SP, 0);
2711 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
2712 /* skip the constant pool */
2715 *(int*)code = 0x41f00000;
2720 * ldfltd ftemp, [pc, #8] 0x41f00000 0x00000000
2721 * adfltd fdest, fdest, ftemp
2725 case OP_ICONV_TO_R4:
2726 ARM_FLTS (code, ins->dreg, ins->sreg1);
2728 case OP_ICONV_TO_R8:
2729 ARM_FLTD (code, ins->dreg, ins->sreg1);
2731 #elif defined(ARM_FPU_VFP)
2733 if (cfg->compile_aot) {
2734 ARM_LDFD (code, ins->dreg, ARMREG_PC, 0);
2736 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
2738 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
2741 /* FIXME: we can optimize the imm load by dealing with part of
2742 * the displacement in LDFD (aligning to 512).
2744 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
2745 ARM_FLDD (code, ins->dreg, ARMREG_LR, 0);
2749 if (cfg->compile_aot) {
2750 ARM_FLDS (code, ins->dreg, ARMREG_PC, 0);
2752 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
2754 ARM_CVTS (code, ins->dreg, ins->dreg);
2756 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
2757 ARM_FLDS (code, ins->dreg, ARMREG_LR, 0);
2758 ARM_CVTS (code, ins->dreg, ins->dreg);
2761 case OP_STORER8_MEMBASE_REG:
2762 g_assert (arm_is_fpimm8 (ins->inst_offset));
2763 ARM_FSTD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2765 case OP_LOADR8_MEMBASE:
2766 g_assert (arm_is_fpimm8 (ins->inst_offset));
2767 ARM_FLDD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2769 case OP_STORER4_MEMBASE_REG:
2770 g_assert (arm_is_fpimm8 (ins->inst_offset));
2771 ARM_FSTS (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2773 case OP_LOADR4_MEMBASE:
2774 g_assert (arm_is_fpimm8 (ins->inst_offset));
2775 ARM_FLDS (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2777 case OP_ICONV_TO_R_UN: {
2778 g_assert_not_reached ();
2781 case OP_ICONV_TO_R4:
2782 g_assert_not_reached ();
2783 //ARM_FLTS (code, ins->dreg, ins->sreg1);
2785 case OP_ICONV_TO_R8:
2786 g_assert_not_reached ();
2787 //ARM_FLTD (code, ins->dreg, ins->sreg1);
2790 case OP_FCONV_TO_I1:
2791 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, TRUE);
2793 case OP_FCONV_TO_U1:
2794 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, FALSE);
2796 case OP_FCONV_TO_I2:
2797 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, TRUE);
2799 case OP_FCONV_TO_U2:
2800 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, FALSE);
2802 case OP_FCONV_TO_I4:
2804 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, TRUE);
2806 case OP_FCONV_TO_U4:
2808 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, FALSE);
2810 case OP_FCONV_TO_I8:
2811 case OP_FCONV_TO_U8:
2812 g_assert_not_reached ();
2813 /* Implemented as helper calls */
2815 case OP_LCONV_TO_R_UN:
2816 g_assert_not_reached ();
2817 /* Implemented as helper calls */
2819 case OP_LCONV_TO_OVF_I: {
2821 guint32 *negative_branch, *msword_positive_branch, *msword_negative_branch, *ovf_ex_target;
2822 // Check if its negative
2823 ppc_cmpi (code, 0, 0, ins->sreg1, 0);
2824 negative_branch = code;
2825 ppc_bc (code, PPC_BR_TRUE, PPC_BR_LT, 0);
2826 // Its positive msword == 0
2827 ppc_cmpi (code, 0, 0, ins->sreg2, 0);
2828 msword_positive_branch = code;
2829 ppc_bc (code, PPC_BR_TRUE, PPC_BR_EQ, 0);
2831 ovf_ex_target = code;
2832 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_ALWAYS, 0, "OverflowException");
2834 ppc_patch (negative_branch, code);
2835 ppc_cmpi (code, 0, 0, ins->sreg2, -1);
2836 msword_negative_branch = code;
2837 ppc_bc (code, PPC_BR_FALSE, PPC_BR_EQ, 0);
2838 ppc_patch (msword_negative_branch, ovf_ex_target);
2840 ppc_patch (msword_positive_branch, code);
2841 if (ins->dreg != ins->sreg1)
2842 ppc_mr (code, ins->dreg, ins->sreg1);
2844 if (ins->dreg != ins->sreg1)
2845 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2850 ARM_FPA_ADFD (code, ins->dreg, ins->sreg1, ins->sreg2);
2853 ARM_FPA_SUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
2856 ARM_FPA_MUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
2859 ARM_FPA_DVFD (code, ins->dreg, ins->sreg1, ins->sreg2);
2862 ARM_MNFD (code, ins->dreg, ins->sreg1);
2864 #elif defined(ARM_FPU_VFP)
2866 ARM_VFP_ADDD (code, ins->dreg, ins->sreg1, ins->sreg2);
2869 ARM_VFP_SUBD (code, ins->dreg, ins->sreg1, ins->sreg2);
2872 ARM_VFP_MULD (code, ins->dreg, ins->sreg1, ins->sreg2);
2875 ARM_VFP_DIVD (code, ins->dreg, ins->sreg1, ins->sreg2);
2878 ARM_NEGD (code, ins->dreg, ins->sreg1);
2883 g_assert_not_reached ();
2886 /* each fp compare op needs to do its own */
2887 g_assert_not_reached ();
2888 //ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2892 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2893 #elif defined(ARM_FPU_VFP)
2894 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2896 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
2897 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
2901 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2902 #elif defined(ARM_FPU_VFP)
2903 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2905 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2906 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
2910 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2911 #elif defined(ARM_FPU_VFP)
2912 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2914 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2915 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
2916 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
2921 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
2922 #elif defined(ARM_FPU_VFP)
2923 ARM_CMPD (code, ins->sreg2, ins->sreg1);
2925 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2926 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
2931 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
2932 #elif defined(ARM_FPU_VFP)
2933 ARM_CMPD (code, ins->sreg2, ins->sreg1);
2935 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2936 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
2937 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
2939 /* ARM FPA flags table:
2940 * N Less than ARMCOND_MI
2941 * Z Equal ARMCOND_EQ
2942 * C Greater Than or Equal ARMCOND_CS
2943 * V Unordered ARMCOND_VS
2947 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2948 #elif defined(ARM_FPU_VFP)
2949 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2951 EMIT_COND_BRANCH (ins, OP_IBEQ - OP_IBEQ);
2955 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2956 #elif defined(ARM_FPU_VFP)
2957 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2959 EMIT_COND_BRANCH (ins, OP_IBNE_UN - OP_IBEQ);
2963 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2964 #elif defined(ARM_FPU_VFP)
2965 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2967 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
2971 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2972 #elif defined(ARM_FPU_VFP)
2973 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2975 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
2976 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
2980 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
2981 #elif defined(ARM_FPU_VFP)
2982 ARM_CMPD (code, ins->sreg2, ins->sreg1);
2984 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set, swapped args */
2988 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
2989 #elif defined(ARM_FPU_VFP)
2990 ARM_CMPD (code, ins->sreg2, ins->sreg1);
2992 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
2993 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set, swapped args */
2997 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2998 #elif defined(ARM_FPU_VFP)
2999 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3001 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_CS);
3005 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3006 #elif defined(ARM_FPU_VFP)
3007 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3009 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3010 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_GE);
3014 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3015 #elif defined(ARM_FPU_VFP)
3016 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3018 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_CS); /* swapped */
3022 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3023 #elif defined(ARM_FPU_VFP)
3024 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3026 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3027 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_GE); /* swapped */
3030 /*ppc_stfd (code, ins->sreg1, -8, ppc_sp);
3031 ppc_lwz (code, ppc_r11, -8, ppc_sp);
3032 ppc_rlwinm (code, ppc_r11, ppc_r11, 0, 1, 31);
3033 ppc_addis (code, ppc_r11, ppc_r11, -32752);
3034 ppc_rlwinmd (code, ppc_r11, ppc_r11, 1, 31, 31);
3035 EMIT_COND_SYSTEM_EXCEPTION (OP_IBEQ - OP_IBEQ, "ArithmeticException");*/
3036 g_assert_not_reached ();
3040 g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
3041 g_assert_not_reached ();
3044 if ((cfg->opt & MONO_OPT_BRANCH) && ((code - cfg->native_code - offset) > max_len)) {
3045 g_warning ("wrong maximal instruction length of instruction %s (expected %d, got %d)",
3046 mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset);
3047 g_assert_not_reached ();
3052 last_offset = offset;
3055 cfg->code_len = code - cfg->native_code;
3059 mono_arch_register_lowlevel_calls (void)
3063 #define patch_lis_ori(ip,val) do {\
3064 guint16 *__lis_ori = (guint16*)(ip); \
3065 __lis_ori [1] = (((guint32)(val)) >> 16) & 0xffff; \
3066 __lis_ori [3] = ((guint32)(val)) & 0xffff; \
3070 mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, gboolean run_cctors)
3072 MonoJumpInfo *patch_info;
3073 gboolean compile_aot = !run_cctors;
3075 for (patch_info = ji; patch_info; patch_info = patch_info->next) {
3076 unsigned char *ip = patch_info->ip.i + code;
3077 const unsigned char *target;
3079 if (patch_info->type == MONO_PATCH_INFO_SWITCH && !compile_aot) {
3080 gpointer *jt = (gpointer*)(ip + 8);
3082 /* jt is the inlined jump table, 2 instructions after ip
3083 * In the normal case we store the absolute addresses,
3084 * otherwise the displacements.
3086 for (i = 0; i < patch_info->data.table->table_size; i++)
3087 jt [i] = code + (int)patch_info->data.table->table [i];
3090 target = mono_resolve_patch_target (method, domain, code, patch_info, run_cctors);
3093 switch (patch_info->type) {
3094 case MONO_PATCH_INFO_BB:
3095 case MONO_PATCH_INFO_LABEL:
3098 /* No need to patch these */
3103 switch (patch_info->type) {
3104 case MONO_PATCH_INFO_IP:
3105 g_assert_not_reached ();
3106 patch_lis_ori (ip, ip);
3108 case MONO_PATCH_INFO_METHOD_REL:
3109 g_assert_not_reached ();
3110 *((gpointer *)(ip)) = code + patch_info->data.offset;
3112 case MONO_PATCH_INFO_METHODCONST:
3113 case MONO_PATCH_INFO_CLASS:
3114 case MONO_PATCH_INFO_IMAGE:
3115 case MONO_PATCH_INFO_FIELD:
3116 case MONO_PATCH_INFO_VTABLE:
3117 case MONO_PATCH_INFO_IID:
3118 case MONO_PATCH_INFO_SFLDA:
3119 case MONO_PATCH_INFO_LDSTR:
3120 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
3121 case MONO_PATCH_INFO_LDTOKEN:
3122 g_assert_not_reached ();
3123 /* from OP_AOTCONST : lis + ori */
3124 patch_lis_ori (ip, target);
3126 case MONO_PATCH_INFO_R4:
3127 case MONO_PATCH_INFO_R8:
3128 g_assert_not_reached ();
3129 *((gconstpointer *)(ip + 2)) = patch_info->data.target;
3131 case MONO_PATCH_INFO_EXC_NAME:
3132 g_assert_not_reached ();
3133 *((gconstpointer *)(ip + 1)) = patch_info->data.name;
3135 case MONO_PATCH_INFO_NONE:
3136 case MONO_PATCH_INFO_BB_OVF:
3137 case MONO_PATCH_INFO_EXC_OVF:
3138 /* everything is dealt with at epilog output time */
3143 arm_patch (ip, target);
3148 * Stack frame layout:
3150 * ------------------- fp
3151 * MonoLMF structure or saved registers
3152 * -------------------
3154 * -------------------
3156 * -------------------
3157 * optional 8 bytes for tracing
3158 * -------------------
3159 * param area size is cfg->param_area
3160 * ------------------- sp
3163 mono_arch_emit_prolog (MonoCompile *cfg)
3165 MonoMethod *method = cfg->method;
3167 MonoMethodSignature *sig;
3169 int alloc_size, pos, max_offset, i, rot_amount;
3176 if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
3179 sig = mono_method_signature (method);
3180 cfg->code_size = 256 + sig->param_count * 20;
3181 code = cfg->native_code = g_malloc (cfg->code_size);
3183 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
3185 alloc_size = cfg->stack_offset;
3188 if (!method->save_lmf) {
3189 ARM_PUSH (code, (cfg->used_int_regs | (1 << ARMREG_IP) | (1 << ARMREG_LR)));
3190 prev_sp_offset = 8; /* ip and lr */
3191 for (i = 0; i < 16; ++i) {
3192 if (cfg->used_int_regs & (1 << i))
3193 prev_sp_offset += 4;
3196 ARM_PUSH (code, 0x5ff0);
3197 prev_sp_offset = 4 * 10; /* all but r0-r3, sp and pc */
3198 pos += sizeof (MonoLMF) - prev_sp_offset;
3202 // align to MONO_ARCH_FRAME_ALIGNMENT bytes
3203 if (alloc_size & (MONO_ARCH_FRAME_ALIGNMENT - 1)) {
3204 alloc_size += MONO_ARCH_FRAME_ALIGNMENT - 1;
3205 alloc_size &= ~(MONO_ARCH_FRAME_ALIGNMENT - 1);
3208 /* the stack used in the pushed regs */
3209 if (prev_sp_offset & 4)
3211 cfg->stack_usage = alloc_size;
3213 if ((i = mono_arm_is_rotated_imm8 (alloc_size, &rot_amount)) >= 0) {
3214 ARM_SUB_REG_IMM (code, ARMREG_SP, ARMREG_SP, i, rot_amount);
3216 code = mono_arm_emit_load_imm (code, ARMREG_IP, alloc_size);
3217 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
3220 if (cfg->frame_reg != ARMREG_SP)
3221 ARM_MOV_REG_REG (code, cfg->frame_reg, ARMREG_SP);
3222 //g_print ("prev_sp_offset: %d, alloc_size:%d\n", prev_sp_offset, alloc_size);
3223 prev_sp_offset += alloc_size;
3225 /* compute max_offset in order to use short forward jumps
3226 * we could skip do it on arm because the immediate displacement
3227 * for jumps is large enough, it may be useful later for constant pools
3230 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
3232 bb->max_offset = max_offset;
3234 if (cfg->prof_options & MONO_PROFILE_COVERAGE)
3237 MONO_BB_FOR_EACH_INS (bb, ins)
3238 max_offset += ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
3241 /* load arguments allocated to register from the stack */
3244 cinfo = calculate_sizes (sig, sig->pinvoke);
3246 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
3247 ArgInfo *ainfo = &cinfo->ret;
3249 g_assert (arm_is_imm12 (inst->inst_offset));
3250 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3252 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
3253 ArgInfo *ainfo = cinfo->args + i;
3254 inst = cfg->args [pos];
3256 if (cfg->verbose_level > 2)
3257 g_print ("Saving argument %d (type: %d)\n", i, ainfo->regtype);
3258 if (inst->opcode == OP_REGVAR) {
3259 if (ainfo->regtype == RegTypeGeneral)
3260 ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
3261 else if (ainfo->regtype == RegTypeFP) {
3262 g_assert_not_reached ();
3263 } else if (ainfo->regtype == RegTypeBase) {
3264 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
3265 ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3267 g_assert_not_reached ();
3269 if (cfg->verbose_level > 2)
3270 g_print ("Argument %d assigned to register %s\n", pos, mono_arch_regname (inst->dreg));
3272 /* the argument should be put on the stack: FIXME handle size != word */
3273 if (ainfo->regtype == RegTypeGeneral) {
3274 switch (ainfo->size) {
3276 if (arm_is_imm12 (inst->inst_offset))
3277 ARM_STRB_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3279 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3280 ARM_STRB_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3284 if (arm_is_imm8 (inst->inst_offset)) {
3285 ARM_STRH_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3287 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3288 ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, inst->inst_basereg);
3289 ARM_STRH_IMM (code, ainfo->reg, ARMREG_IP, 0);
3293 g_assert (arm_is_imm12 (inst->inst_offset));
3294 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3295 g_assert (arm_is_imm12 (inst->inst_offset + 4));
3296 ARM_STR_IMM (code, ainfo->reg + 1, inst->inst_basereg, inst->inst_offset + 4);
3299 if (arm_is_imm12 (inst->inst_offset)) {
3300 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3302 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3303 ARM_STR_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3307 } else if (ainfo->regtype == RegTypeBaseGen) {
3308 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
3309 g_assert (arm_is_imm12 (inst->inst_offset));
3310 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3311 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3312 ARM_STR_IMM (code, ARMREG_R3, inst->inst_basereg, inst->inst_offset);
3313 } else if (ainfo->regtype == RegTypeBase) {
3314 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
3315 switch (ainfo->size) {
3317 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3318 g_assert (arm_is_imm12 (inst->inst_offset));
3319 ARM_STRB_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3322 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3323 if (arm_is_imm8 (inst->inst_offset)) {
3324 ARM_STRH_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3326 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3327 ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, inst->inst_basereg);
3328 ARM_STRH_IMM (code, ARMREG_LR, ARMREG_IP, 0);
3332 g_assert (arm_is_imm12 (inst->inst_offset));
3333 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3334 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3335 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset + 4));
3336 g_assert (arm_is_imm12 (inst->inst_offset + 4));
3337 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset + 4));
3338 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3341 g_assert (arm_is_imm12 (inst->inst_offset));
3342 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3343 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3346 } else if (ainfo->regtype == RegTypeFP) {
3347 g_assert_not_reached ();
3348 } else if (ainfo->regtype == RegTypeStructByVal) {
3349 int doffset = inst->inst_offset;
3353 if (mono_class_from_mono_type (inst->inst_vtype))
3354 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), NULL);
3355 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
3356 g_assert (arm_is_imm12 (doffset));
3357 ARM_STR_IMM (code, ainfo->reg + cur_reg, inst->inst_basereg, doffset);
3358 soffset += sizeof (gpointer);
3359 doffset += sizeof (gpointer);
3361 if (ainfo->vtsize) {
3362 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
3363 //g_print ("emit_memcpy (prev_sp_ofs: %d, ainfo->offset: %d, soffset: %d)\n", prev_sp_offset, ainfo->offset, soffset);
3364 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, doffset, ARMREG_SP, prev_sp_offset + ainfo->offset);
3366 } else if (ainfo->regtype == RegTypeStructByAddr) {
3367 g_assert_not_reached ();
3368 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
3369 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, inst->inst_offset, ainfo->reg, 0);
3371 g_assert_not_reached ();
3376 if (method->save_lmf) {
3378 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3379 (gpointer)"mono_get_lmf_addr");
3380 code = emit_call_seq (cfg, code);
3381 /* we build the MonoLMF structure on the stack - see mini-arm.h */
3382 /* lmf_offset is the offset from the previous stack pointer,
3383 * alloc_size is the total stack space allocated, so the offset
3384 * of MonoLMF from the current stack ptr is alloc_size - lmf_offset.
3385 * The pointer to the struct is put in r1 (new_lmf).
3386 * r2 is used as scratch
3387 * The callee-saved registers are already in the MonoLMF structure
3389 code = emit_big_add (code, ARMREG_R1, ARMREG_SP, alloc_size - lmf_offset);
3390 /* r0 is the result from mono_get_lmf_addr () */
3391 ARM_STR_IMM (code, ARMREG_R0, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
3392 /* new_lmf->previous_lmf = *lmf_addr */
3393 ARM_LDR_IMM (code, ARMREG_R2, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3394 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3395 /* *(lmf_addr) = r1 */
3396 ARM_STR_IMM (code, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3397 /* save method info */
3398 code = mono_arm_emit_load_imm (code, ARMREG_R2, GPOINTER_TO_INT (method));
3399 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, method));
3400 ARM_STR_IMM (code, ARMREG_SP, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, ebp));
3401 /* save the current IP */
3402 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_PC);
3403 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, eip));
3407 code = mono_arch_instrument_prolog (cfg, mono_trace_enter_method, code, TRUE);
3409 cfg->code_len = code - cfg->native_code;
3410 g_assert (cfg->code_len < cfg->code_size);
3417 mono_arch_emit_epilog (MonoCompile *cfg)
3419 MonoMethod *method = cfg->method;
3420 int pos, i, rot_amount;
3421 int max_epilog_size = 16 + 20*4;
3424 if (cfg->method->save_lmf)
3425 max_epilog_size += 128;
3427 if (mono_jit_trace_calls != NULL)
3428 max_epilog_size += 50;
3430 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE)
3431 max_epilog_size += 50;
3433 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
3434 cfg->code_size *= 2;
3435 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
3436 mono_jit_stats.code_reallocs++;
3440 * Keep in sync with OP_JMP
3442 code = cfg->native_code + cfg->code_len;
3444 if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) {
3445 code = mono_arch_instrument_epilog (cfg, mono_trace_leave_method, code, TRUE);
3449 if (method->save_lmf) {
3451 /* all but r0-r3, sp and pc */
3452 pos += sizeof (MonoLMF) - (4 * 10);
3454 /* r2 contains the pointer to the current LMF */
3455 code = emit_big_add (code, ARMREG_R2, cfg->frame_reg, cfg->stack_usage - lmf_offset);
3456 /* ip = previous_lmf */
3457 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3459 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
3460 /* *(lmf_addr) = previous_lmf */
3461 ARM_STR_IMM (code, ARMREG_IP, ARMREG_LR, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3462 /* FIXME: speedup: there is no actual need to restore the registers if
3463 * we didn't actually change them (idea from Zoltan).
3466 /* point sp at the registers to restore: 10 is 14 -4, because we skip r0-r3 */
3467 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_R2, (sizeof (MonoLMF) - 10 * sizeof (gulong)));
3468 ARM_POP_NWB (code, 0xaff0); /* restore ip to sp and lr to pc */
3470 if ((i = mono_arm_is_rotated_imm8 (cfg->stack_usage, &rot_amount)) >= 0) {
3471 ARM_ADD_REG_IMM (code, ARMREG_SP, cfg->frame_reg, i, rot_amount);
3473 code = mono_arm_emit_load_imm (code, ARMREG_IP, cfg->stack_usage);
3474 ARM_ADD_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
3476 /* FIXME: add v4 thumb interworking support */
3477 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP) | (1 << ARMREG_PC)));
3480 cfg->code_len = code - cfg->native_code;
3482 g_assert (cfg->code_len < cfg->code_size);
3486 /* remove once throw_exception_by_name is eliminated */
3488 exception_id_by_name (const char *name)
3490 if (strcmp (name, "IndexOutOfRangeException") == 0)
3491 return MONO_EXC_INDEX_OUT_OF_RANGE;
3492 if (strcmp (name, "OverflowException") == 0)
3493 return MONO_EXC_OVERFLOW;
3494 if (strcmp (name, "ArithmeticException") == 0)
3495 return MONO_EXC_ARITHMETIC;
3496 if (strcmp (name, "DivideByZeroException") == 0)
3497 return MONO_EXC_DIVIDE_BY_ZERO;
3498 if (strcmp (name, "InvalidCastException") == 0)
3499 return MONO_EXC_INVALID_CAST;
3500 if (strcmp (name, "NullReferenceException") == 0)
3501 return MONO_EXC_NULL_REF;
3502 if (strcmp (name, "ArrayTypeMismatchException") == 0)
3503 return MONO_EXC_ARRAY_TYPE_MISMATCH;
3504 g_error ("Unknown intrinsic exception %s\n", name);
3509 mono_arch_emit_exceptions (MonoCompile *cfg)
3511 MonoJumpInfo *patch_info;
3514 const guint8* exc_throw_pos [MONO_EXC_INTRINS_NUM] = {NULL};
3515 guint8 exc_throw_found [MONO_EXC_INTRINS_NUM] = {0};
3516 int max_epilog_size = 50;
3518 /* count the number of exception infos */
3521 * make sure we have enough space for exceptions
3522 * 12 is the simulated call to throw_exception_by_name
3524 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
3525 if (patch_info->type == MONO_PATCH_INFO_EXC) {
3526 i = exception_id_by_name (patch_info->data.target);
3527 if (!exc_throw_found [i]) {
3528 max_epilog_size += 12;
3529 exc_throw_found [i] = TRUE;
3534 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
3535 cfg->code_size *= 2;
3536 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
3537 mono_jit_stats.code_reallocs++;
3540 code = cfg->native_code + cfg->code_len;
3542 /* add code to raise exceptions */
3543 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
3544 switch (patch_info->type) {
3545 case MONO_PATCH_INFO_EXC: {
3546 unsigned char *ip = patch_info->ip.i + cfg->native_code;
3547 const char *ex_name = patch_info->data.target;
3548 i = exception_id_by_name (patch_info->data.target);
3549 if (exc_throw_pos [i]) {
3550 arm_patch (ip, exc_throw_pos [i]);
3551 patch_info->type = MONO_PATCH_INFO_NONE;
3554 exc_throw_pos [i] = code;
3556 arm_patch (ip, code);
3557 //*(int*)code = 0xef9f0001;
3560 /*mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC_NAME, patch_info->data.target);*/
3561 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
3562 /* we got here from a conditional call, so the calling ip is set in lr already */
3563 patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD;
3564 patch_info->data.name = "mono_arch_throw_exception_by_name";
3565 patch_info->ip.i = code - cfg->native_code;
3567 *(gconstpointer*)code = ex_name;
3577 cfg->code_len = code - cfg->native_code;
3579 g_assert (cfg->code_len < cfg->code_size);
3584 mono_arch_setup_jit_tls_data (MonoJitTlsData *tls)
3589 mono_arch_free_jit_tls_data (MonoJitTlsData *tls)
3594 mono_arch_emit_this_vret_args (MonoCompile *cfg, MonoCallInst *inst, int this_reg, int this_type, int vt_reg)
3597 int this_dreg = ARMREG_R0;
3600 this_dreg = ARMREG_R1;
3602 /* add the this argument */
3603 if (this_reg != -1) {
3605 MONO_INST_NEW (cfg, this, OP_MOVE);
3606 this->type = this_type;
3607 this->sreg1 = this_reg;
3608 this->dreg = mono_regstate_next_int (cfg->rs);
3609 mono_bblock_add_inst (cfg->cbb, this);
3610 mono_call_inst_add_outarg_reg (cfg, inst, this->dreg, this_dreg, FALSE);
3615 MONO_INST_NEW (cfg, vtarg, OP_MOVE);
3616 vtarg->type = STACK_MP;
3617 vtarg->sreg1 = vt_reg;
3618 vtarg->dreg = mono_regstate_next_int (cfg->rs);
3619 mono_bblock_add_inst (cfg->cbb, vtarg);
3620 mono_call_inst_add_outarg_reg (cfg, inst, vtarg->dreg, ARMREG_R0, FALSE);
3625 mono_arch_get_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
3631 mono_arch_print_tree (MonoInst *tree, int arity)
3636 MonoInst* mono_arch_get_domain_intrinsic (MonoCompile* cfg)
3642 mono_arch_get_thread_intrinsic (MonoCompile* cfg)
3648 mono_arch_get_patch_offset (guint8 *code)
3655 mono_arch_flush_register_windows (void)
3660 mono_arch_fixup_jinfo (MonoCompile *cfg)
3662 /* max encoded stack usage is 64KB * 4 */
3663 g_assert ((cfg->stack_usage & ~(0xffff << 2)) == 0);
3664 cfg->jit_info->used_regs |= cfg->stack_usage << 14;
3667 #ifdef MONO_ARCH_HAVE_IMT
3670 mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call)
3675 mono_arch_find_imt_method (gpointer *regs, guint8 *code)
3677 guint32 *code_ptr = (guint32*)code;
3679 /* The IMT value is stored in the code stream right after the LDC instruction. */
3680 if (!IS_LDR_PC (code_ptr [0])) {
3681 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]);
3682 g_assert (IS_LDR_PC (code_ptr [0]));
3684 return (MonoMethod*) code_ptr [1];
3688 mono_arch_find_this_argument (gpointer *regs, MonoMethod *method)
3690 return mono_arch_get_this_arg_from_call (mono_method_signature (method), (gssize*)regs, NULL);
3694 #define ENABLE_WRONG_METHOD_CHECK 0
3695 #define BASE_SIZE (4 * 4)
3696 #define BSEARCH_ENTRY_SIZE (4 * 4)
3697 #define CMP_SIZE (3 * 4)
3698 #define BRANCH_SIZE (1 * 4)
3699 #define CALL_SIZE (2 * 4)
3700 #define WMC_SIZE (5 * 4)
3701 #define DISTANCE(A, B) (((gint32)(B)) - ((gint32)(A)))
3704 arm_emit_value_and_patch_ldr (arminstr_t *code, arminstr_t *target, guint32 value)
3706 guint32 delta = DISTANCE (target, code);
3708 g_assert (delta >= 0 && delta <= 0xFFF);
3709 *target = *target | delta;
3715 mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count)
3717 int size, i, extra_space = 0;
3718 arminstr_t *code, *start, *vtable_target = NULL;
3721 for (i = 0; i < count; ++i) {
3722 MonoIMTCheckItem *item = imt_entries [i];
3723 if (item->is_equals) {
3724 g_assert (arm_is_imm12 (DISTANCE (vtable, &vtable->vtable[item->vtable_slot])));
3726 if (item->check_target_idx) {
3727 if (!item->compare_done)
3728 item->chunk_size += CMP_SIZE;
3729 item->chunk_size += BRANCH_SIZE;
3731 #if ENABLE_WRONG_METHOD_CHECK
3732 item->chunk_size += WMC_SIZE;
3735 item->chunk_size += CALL_SIZE;
3737 item->chunk_size += BSEARCH_ENTRY_SIZE;
3738 imt_entries [item->check_target_idx]->compare_done = TRUE;
3740 size += item->chunk_size;
3743 start = code = mono_code_manager_reserve (domain->code_mp, size);
3746 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);
3747 for (i = 0; i < count; ++i) {
3748 MonoIMTCheckItem *item = imt_entries [i];
3749 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);
3753 ARM_PUSH2 (code, ARMREG_R0, ARMREG_R1);
3754 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, -4);
3755 vtable_target = code;
3756 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
3758 for (i = 0; i < count; ++i) {
3759 MonoIMTCheckItem *item = imt_entries [i];
3760 arminstr_t *imt_method = NULL;
3761 item->code_target = (guint8*)code;
3763 if (item->is_equals) {
3764 if (item->check_target_idx) {
3765 if (!item->compare_done) {
3767 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
3768 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
3770 item->jmp_code = (guint8*)code;
3771 ARM_B_COND (code, ARMCOND_NE, 0);
3773 ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
3774 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, DISTANCE (vtable, &vtable->vtable[item->vtable_slot]));
3776 /*Enable the commented code to assert on wrong method*/
3777 #if ENABLE_WRONG_METHOD_CHECK
3779 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
3780 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
3781 ARM_B_COND (code, ARMCOND_NE, 1);
3783 ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
3784 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, DISTANCE (vtable, &vtable->vtable[item->vtable_slot]));
3786 #if ENABLE_WRONG_METHOD_CHECK
3792 code = arm_emit_value_and_patch_ldr (code, imt_method, (guint32)item->method);
3794 /*must emit after unconditional branch*/
3795 if (vtable_target) {
3796 code = arm_emit_value_and_patch_ldr (code, vtable_target, (guint32)vtable);
3797 item->chunk_size += 4;
3798 vtable_target = NULL;
3801 /*We reserve the space for bsearch IMT values after the first entry with an absolute jump*/
3803 code += extra_space;
3807 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
3808 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
3810 item->jmp_code = (guint8*)code;
3811 ARM_B_COND (code, ARMCOND_GE, 0);
3816 for (i = 0; i < count; ++i) {
3817 MonoIMTCheckItem *item = imt_entries [i];
3818 if (item->jmp_code) {
3819 if (item->check_target_idx)
3820 arm_patch (item->jmp_code, imt_entries [item->check_target_idx]->code_target);
3822 if (i > 0 && item->is_equals) {
3824 arminstr_t *space_start = (arminstr_t*)(item->code_target + item->chunk_size);
3825 for (j = i - 1; j >= 0 && !imt_entries [j]->is_equals; --j) {
3826 space_start = arm_emit_value_and_patch_ldr (space_start, (arminstr_t*)imt_entries [j]->code_target, (guint32)imt_entries [j]->method);
3833 char *buff = g_strdup_printf ("thunk_for_class_%s_%s_entries_%d", vtable->klass->name_space, vtable->klass->name, count);
3834 mono_disassemble_code (NULL, (guint8*)start, size, buff);
3839 mono_arch_flush_icache ((guint8*)start, size);
3840 mono_stats.imt_thunks_size += code - start;
3842 g_assert (DISTANCE (start, code) <= size);