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" );
572 #define NOT_IMPLEMENTED(x) \
573 g_error ("FIXME: %s is not yet implemented. (trampoline)", x);
586 guint16 vtsize; /* in param area */
588 guint8 regtype : 4; /* 0 general, 1 basereg, 2 floating point register, see RegType* */
589 guint8 size : 4; /* 1, 2, 4, 8, or regs used by RegTypeStructByVal */
604 add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple)
607 if (*gr > ARMREG_R3) {
608 ainfo->offset = *stack_size;
609 ainfo->reg = ARMREG_SP; /* in the caller */
610 ainfo->regtype = RegTypeBase;
621 /* first word in r3 and the second on the stack */
622 ainfo->offset = *stack_size;
623 ainfo->reg = ARMREG_SP; /* in the caller */
624 ainfo->regtype = RegTypeBaseGen;
626 } else if (*gr > ARMREG_R3) {
631 ainfo->offset = *stack_size;
632 ainfo->reg = ARMREG_SP; /* in the caller */
633 ainfo->regtype = RegTypeBase;
648 calculate_sizes (MonoMethodSignature *sig, gboolean is_pinvoke)
651 int n = sig->hasthis + sig->param_count;
653 guint32 stack_size = 0;
654 CallInfo *cinfo = g_malloc0 (sizeof (CallInfo) + sizeof (ArgInfo) * n);
658 /* FIXME: handle returning a struct */
659 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
660 add_general (&gr, &stack_size, &cinfo->ret, TRUE);
661 cinfo->struct_ret = ARMREG_R0;
666 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
669 DEBUG(printf("params: %d\n", sig->param_count));
670 for (i = 0; i < sig->param_count; ++i) {
671 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
672 /* Prevent implicit arguments and sig_cookie from
673 being passed in registers */
675 /* Emit the signature cookie just before the implicit arguments */
676 add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE);
678 DEBUG(printf("param %d: ", i));
679 if (sig->params [i]->byref) {
680 DEBUG(printf("byref\n"));
681 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
685 simpletype = mono_type_get_underlying_type (sig->params [i])->type;
686 switch (simpletype) {
687 case MONO_TYPE_BOOLEAN:
690 cinfo->args [n].size = 1;
691 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
697 cinfo->args [n].size = 2;
698 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
703 cinfo->args [n].size = 4;
704 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
710 case MONO_TYPE_FNPTR:
711 case MONO_TYPE_CLASS:
712 case MONO_TYPE_OBJECT:
713 case MONO_TYPE_STRING:
714 case MONO_TYPE_SZARRAY:
715 case MONO_TYPE_ARRAY:
717 cinfo->args [n].size = sizeof (gpointer);
718 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
721 case MONO_TYPE_GENERICINST:
722 if (!mono_type_generic_inst_is_valuetype (sig->params [i])) {
723 cinfo->args [n].size = sizeof (gpointer);
724 add_general (&gr, &stack_size, cinfo->args + n, TRUE);
729 case MONO_TYPE_TYPEDBYREF:
730 case MONO_TYPE_VALUETYPE: {
735 if (simpletype == MONO_TYPE_TYPEDBYREF) {
736 size = sizeof (MonoTypedRef);
738 MonoClass *klass = mono_class_from_mono_type (sig->params [i]);
740 size = mono_class_native_size (klass, NULL);
742 size = mono_class_value_size (klass, NULL);
744 DEBUG(printf ("load %d bytes struct\n",
745 mono_class_native_size (sig->params [i]->data.klass, NULL)));
748 align_size += (sizeof (gpointer) - 1);
749 align_size &= ~(sizeof (gpointer) - 1);
750 nwords = (align_size + sizeof (gpointer) -1 ) / sizeof (gpointer);
751 cinfo->args [n].regtype = RegTypeStructByVal;
752 /* FIXME: align gr and stack_size if needed */
753 if (gr > ARMREG_R3) {
754 cinfo->args [n].size = 0;
755 cinfo->args [n].vtsize = nwords;
757 int rest = ARMREG_R3 - gr + 1;
758 int n_in_regs = rest >= nwords? nwords: rest;
759 cinfo->args [n].size = n_in_regs;
760 cinfo->args [n].vtsize = nwords - n_in_regs;
761 cinfo->args [n].reg = gr;
764 cinfo->args [n].offset = stack_size;
765 /*g_print ("offset for arg %d at %d\n", n, stack_size);*/
766 stack_size += nwords * sizeof (gpointer);
773 cinfo->args [n].size = 8;
774 add_general (&gr, &stack_size, cinfo->args + n, FALSE);
778 g_error ("Can't trampoline 0x%x", sig->params [i]->type);
783 simpletype = mono_type_get_underlying_type (sig->ret)->type;
784 switch (simpletype) {
785 case MONO_TYPE_BOOLEAN:
796 case MONO_TYPE_FNPTR:
797 case MONO_TYPE_CLASS:
798 case MONO_TYPE_OBJECT:
799 case MONO_TYPE_SZARRAY:
800 case MONO_TYPE_ARRAY:
801 case MONO_TYPE_STRING:
802 cinfo->ret.reg = ARMREG_R0;
806 cinfo->ret.reg = ARMREG_R0;
810 cinfo->ret.reg = ARMREG_R0;
811 /* FIXME: cinfo->ret.reg = ???;
812 cinfo->ret.regtype = RegTypeFP;*/
814 case MONO_TYPE_GENERICINST:
815 if (!mono_type_generic_inst_is_valuetype (sig->ret)) {
816 cinfo->ret.reg = ARMREG_R0;
820 case MONO_TYPE_VALUETYPE:
822 case MONO_TYPE_TYPEDBYREF:
826 g_error ("Can't handle as return value 0x%x", sig->ret->type);
830 /* align stack size to 8 */
831 DEBUG (printf (" stack size: %d (%d)\n", (stack_size + 15) & ~15, stack_size));
832 stack_size = (stack_size + 7) & ~7;
834 cinfo->stack_usage = stack_size;
840 * Set var information according to the calling convention. arm version.
841 * The locals var stuff should most likely be split in another method.
844 mono_arch_allocate_vars (MonoCompile *m)
846 MonoMethodSignature *sig;
847 MonoMethodHeader *header;
849 int i, offset, size, align, curinst;
850 int frame_reg = ARMREG_FP;
852 /* FIXME: this will change when we use FP as gcc does */
853 m->flags |= MONO_CFG_HAS_SPILLUP;
855 /* allow room for the vararg method args: void* and long/double */
856 if (mono_jit_trace_calls != NULL && mono_trace_eval (m->method))
857 m->param_area = MAX (m->param_area, sizeof (gpointer)*8);
859 header = mono_method_get_header (m->method);
862 * We use the frame register also for any method that has
863 * exception clauses. This way, when the handlers are called,
864 * the code will reference local variables using the frame reg instead of
865 * the stack pointer: if we had to restore the stack pointer, we'd
866 * corrupt the method frames that are already on the stack (since
867 * filters get called before stack unwinding happens) when the filter
868 * code would call any method (this also applies to finally etc.).
870 if ((m->flags & MONO_CFG_HAS_ALLOCA) || header->num_clauses)
871 frame_reg = ARMREG_FP;
872 m->frame_reg = frame_reg;
873 if (frame_reg != ARMREG_SP) {
874 m->used_int_regs |= 1 << frame_reg;
877 sig = mono_method_signature (m->method);
881 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
882 m->ret->opcode = OP_REGVAR;
883 m->ret->inst_c0 = ARMREG_R0;
885 /* FIXME: handle long and FP values */
886 switch (mono_type_get_underlying_type (sig->ret)->type) {
890 m->ret->opcode = OP_REGVAR;
891 m->ret->inst_c0 = ARMREG_R0;
895 /* local vars are at a positive offset from the stack pointer */
897 * also note that if the function uses alloca, we use FP
898 * to point at the local variables.
900 offset = 0; /* linkage area */
901 /* align the offset to 16 bytes: not sure this is needed here */
903 //offset &= ~(8 - 1);
905 /* add parameter area size for called functions */
906 offset += m->param_area;
909 if (m->flags & MONO_CFG_HAS_FPOUT)
912 /* allow room to save the return value */
913 if (mono_jit_trace_calls != NULL && mono_trace_eval (m->method))
916 /* the MonoLMF structure is stored just below the stack pointer */
918 if (sig->call_convention == MONO_CALL_VARARG) {
922 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
924 offset += sizeof(gpointer) - 1;
925 offset &= ~(sizeof(gpointer) - 1);
926 inst->inst_offset = offset;
927 inst->opcode = OP_REGOFFSET;
928 inst->inst_basereg = frame_reg;
929 offset += sizeof(gpointer);
930 if (sig->call_convention == MONO_CALL_VARARG)
931 m->sig_cookie += sizeof (gpointer);
934 curinst = m->locals_start;
935 for (i = curinst; i < m->num_varinfo; ++i) {
936 inst = m->varinfo [i];
937 if ((inst->flags & MONO_INST_IS_DEAD) || inst->opcode == OP_REGVAR)
940 /* inst->backend.is_pinvoke indicates native sized value types, this is used by the
941 * pinvoke wrappers when they call functions returning structure */
942 if (inst->backend.is_pinvoke && MONO_TYPE_ISSTRUCT (inst->inst_vtype) && inst->inst_vtype->type != MONO_TYPE_TYPEDBYREF)
943 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), &align);
945 size = mono_type_size (inst->inst_vtype, &align);
947 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
948 * since it loads/stores misaligned words, which don't do the right thing.
950 if (align < 4 && size >= 4)
953 offset &= ~(align - 1);
954 inst->inst_offset = offset;
955 inst->opcode = OP_REGOFFSET;
956 inst->inst_basereg = frame_reg;
958 //g_print ("allocating local %d to %d\n", i, inst->inst_offset);
963 inst = m->args [curinst];
964 if (inst->opcode != OP_REGVAR) {
965 inst->opcode = OP_REGOFFSET;
966 inst->inst_basereg = frame_reg;
967 offset += sizeof (gpointer) - 1;
968 offset &= ~(sizeof (gpointer) - 1);
969 inst->inst_offset = offset;
970 offset += sizeof (gpointer);
971 if (sig->call_convention == MONO_CALL_VARARG)
972 m->sig_cookie += sizeof (gpointer);
977 for (i = 0; i < sig->param_count; ++i) {
978 inst = m->args [curinst];
979 if (inst->opcode != OP_REGVAR) {
980 inst->opcode = OP_REGOFFSET;
981 inst->inst_basereg = frame_reg;
982 size = mono_type_size (sig->params [i], &align);
983 /* FIXME: if a structure is misaligned, our memcpy doesn't work,
984 * since it loads/stores misaligned words, which don't do the right thing.
986 if (align < 4 && size >= 4)
989 offset &= ~(align - 1);
990 inst->inst_offset = offset;
992 if ((sig->call_convention == MONO_CALL_VARARG) && (i < sig->sentinelpos))
993 m->sig_cookie += size;
998 /* align the offset to 8 bytes */
1003 m->stack_offset = offset;
1007 /* Fixme: we need an alignment solution for enter_method and mono_arch_call_opcode,
1008 * currently alignment in mono_arch_call_opcode is computed without arch_get_argument_info
1012 * take the arguments and generate the arch-specific
1013 * instructions to properly call the function in call.
1014 * This includes pushing, moving arguments to the right register
1016 * Issue: who does the spilling if needed, and when?
1019 mono_arch_call_opcode (MonoCompile *cfg, MonoBasicBlock* bb, MonoCallInst *call, int is_virtual) {
1021 MonoMethodSignature *sig;
1026 sig = call->signature;
1027 n = sig->param_count + sig->hasthis;
1029 cinfo = calculate_sizes (sig, sig->pinvoke);
1030 if (cinfo->struct_ret)
1031 call->used_iregs |= 1 << cinfo->struct_ret;
1033 for (i = 0; i < n; ++i) {
1034 ainfo = cinfo->args + i;
1035 if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
1037 cfg->disable_aot = TRUE;
1039 MONO_INST_NEW (cfg, sig_arg, OP_ICONST);
1040 sig_arg->inst_p0 = call->signature;
1042 MONO_INST_NEW (cfg, arg, OP_OUTARG);
1043 arg->inst_imm = cinfo->sig_cookie.offset;
1044 arg->inst_left = sig_arg;
1046 /* prepend, so they get reversed */
1047 arg->next = call->out_args;
1048 call->out_args = arg;
1050 if (is_virtual && i == 0) {
1051 /* the argument will be attached to the call instrucion */
1052 in = call->args [i];
1053 call->used_iregs |= 1 << ainfo->reg;
1055 MONO_INST_NEW (cfg, arg, OP_OUTARG);
1056 in = call->args [i];
1057 arg->cil_code = in->cil_code;
1058 arg->inst_left = in;
1059 arg->inst_right = (MonoInst*)call;
1060 arg->type = in->type;
1061 /* prepend, we'll need to reverse them later */
1062 arg->next = call->out_args;
1063 call->out_args = arg;
1064 if (ainfo->regtype == RegTypeGeneral) {
1065 arg->backend.reg3 = ainfo->reg;
1066 call->used_iregs |= 1 << ainfo->reg;
1067 if (arg->type == STACK_I8)
1068 call->used_iregs |= 1 << (ainfo->reg + 1);
1069 if (arg->type == STACK_R8) {
1070 if (ainfo->size == 4) {
1071 #ifndef MONO_ARCH_SOFT_FLOAT
1072 arg->opcode = OP_OUTARG_R4;
1075 call->used_iregs |= 1 << (ainfo->reg + 1);
1077 cfg->flags |= MONO_CFG_HAS_FPOUT;
1079 } else if (ainfo->regtype == RegTypeStructByAddr) {
1080 /* FIXME: where si the data allocated? */
1081 arg->backend.reg3 = ainfo->reg;
1082 call->used_iregs |= 1 << ainfo->reg;
1083 g_assert_not_reached ();
1084 } else if (ainfo->regtype == RegTypeStructByVal) {
1086 /* mark the used regs */
1087 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
1088 call->used_iregs |= 1 << (ainfo->reg + cur_reg);
1090 arg->opcode = OP_OUTARG_VT;
1091 /* vtsize and offset have just 12 bits of encoding in number of words */
1092 g_assert (((ainfo->vtsize | (ainfo->offset / 4)) & 0xfffff000) == 0);
1093 arg->backend.arg_info = ainfo->reg | (ainfo->size << 4) | (ainfo->vtsize << 8) | ((ainfo->offset / 4) << 20);
1094 } else if (ainfo->regtype == RegTypeBase) {
1095 arg->opcode = OP_OUTARG_MEMBASE;
1096 arg->backend.arg_info = (ainfo->offset << 8) | ainfo->size;
1097 } else if (ainfo->regtype == RegTypeBaseGen) {
1098 call->used_iregs |= 1 << ARMREG_R3;
1099 arg->opcode = OP_OUTARG_MEMBASE;
1100 arg->backend.arg_info = (ainfo->offset << 8) | 0xff;
1101 if (arg->type == STACK_R8)
1102 cfg->flags |= MONO_CFG_HAS_FPOUT;
1103 } else if (ainfo->regtype == RegTypeFP) {
1104 arg->backend.reg3 = ainfo->reg;
1105 /* FP args are passed in int regs */
1106 call->used_iregs |= 1 << ainfo->reg;
1107 if (ainfo->size == 8) {
1108 arg->opcode = OP_OUTARG_R8;
1109 call->used_iregs |= 1 << (ainfo->reg + 1);
1111 arg->opcode = OP_OUTARG_R4;
1113 cfg->flags |= MONO_CFG_HAS_FPOUT;
1115 g_assert_not_reached ();
1120 * Reverse the call->out_args list.
1123 MonoInst *prev = NULL, *list = call->out_args, *next;
1130 call->out_args = prev;
1132 call->stack_usage = cinfo->stack_usage;
1133 cfg->param_area = MAX (cfg->param_area, cinfo->stack_usage);
1134 cfg->flags |= MONO_CFG_HAS_CALLS;
1136 * should set more info in call, such as the stack space
1137 * used by the args that needs to be added back to esp
1145 * Allow tracing to work with this interface (with an optional argument)
1149 mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
1153 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1154 ARM_MOV_REG_IMM8 (code, ARMREG_R1, 0); /* NULL ebp for now */
1155 code = mono_arm_emit_load_imm (code, ARMREG_R2, (guint32)func);
1156 code = emit_call_reg (code, ARMREG_R2);
1169 mono_arch_instrument_epilog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
1172 int save_mode = SAVE_NONE;
1174 MonoMethod *method = cfg->method;
1175 int rtype = mono_type_get_underlying_type (mono_method_signature (method)->ret)->type;
1176 int save_offset = cfg->param_area;
1180 offset = code - cfg->native_code;
1181 /* we need about 16 instructions */
1182 if (offset > (cfg->code_size - 16 * 4)) {
1183 cfg->code_size *= 2;
1184 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
1185 code = cfg->native_code + offset;
1188 case MONO_TYPE_VOID:
1189 /* special case string .ctor icall */
1190 if (strcmp (".ctor", method->name) && method->klass == mono_defaults.string_class)
1191 save_mode = SAVE_ONE;
1193 save_mode = SAVE_NONE;
1197 save_mode = SAVE_TWO;
1201 save_mode = SAVE_FP;
1203 case MONO_TYPE_VALUETYPE:
1204 save_mode = SAVE_STRUCT;
1207 save_mode = SAVE_ONE;
1211 switch (save_mode) {
1213 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1214 ARM_STR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1215 if (enable_arguments) {
1216 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_R1);
1217 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1221 ARM_STR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1222 if (enable_arguments) {
1223 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1227 /* FIXME: what reg? */
1228 if (enable_arguments) {
1229 /* FIXME: what reg? */
1233 if (enable_arguments) {
1234 /* FIXME: get the actual address */
1235 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
1243 code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->method);
1244 code = mono_arm_emit_load_imm (code, ARMREG_IP, (guint32)func);
1245 code = emit_call_reg (code, ARMREG_IP);
1247 switch (save_mode) {
1249 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1250 ARM_LDR_IMM (code, ARMREG_R1, cfg->frame_reg, save_offset + 4);
1253 ARM_LDR_IMM (code, ARMREG_R0, cfg->frame_reg, save_offset);
1267 * The immediate field for cond branches is big enough for all reasonable methods
1269 #define EMIT_COND_BRANCH_FLAGS(ins,condcode) \
1270 if (ins->flags & MONO_INST_BRLABEL) { \
1271 if (0 && ins->inst_i0->inst_c0) { \
1272 ARM_B_COND (code, (condcode), (code - cfg->native_code + ins->inst_i0->inst_c0) & 0xffffff); \
1274 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_LABEL, ins->inst_i0); \
1275 ARM_B_COND (code, (condcode), 0); \
1278 if (0 && ins->inst_true_bb->native_offset) { \
1279 ARM_B_COND (code, (condcode), (code - cfg->native_code + ins->inst_true_bb->native_offset) & 0xffffff); \
1281 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_true_bb); \
1282 ARM_B_COND (code, (condcode), 0); \
1286 #define EMIT_COND_BRANCH(ins,cond) EMIT_COND_BRANCH_FLAGS(ins, branch_cc_table [(cond)])
1288 /* emit an exception if condition is fail
1290 * We assign the extra code used to throw the implicit exceptions
1291 * to cfg->bb_exit as far as the big branch handling is concerned
1293 #define EMIT_COND_SYSTEM_EXCEPTION_FLAGS(condcode,exc_name) \
1295 mono_add_patch_info (cfg, code - cfg->native_code, \
1296 MONO_PATCH_INFO_EXC, exc_name); \
1297 ARM_BL_COND (code, (condcode), 0); \
1300 #define EMIT_COND_SYSTEM_EXCEPTION(cond,exc_name) EMIT_COND_SYSTEM_EXCEPTION_FLAGS(branch_cc_table [(cond)], (exc_name))
1303 peephole_pass (MonoCompile *cfg, MonoBasicBlock *bb)
1305 MonoInst *ins, *last_ins = NULL;
1310 switch (ins->opcode) {
1312 /* remove unnecessary multiplication with 1 */
1313 if (ins->inst_imm == 1) {
1314 if (ins->dreg != ins->sreg1) {
1315 ins->opcode = OP_MOVE;
1317 last_ins->next = ins->next;
1322 int power2 = mono_is_power_of_two (ins->inst_imm);
1324 ins->opcode = OP_SHL_IMM;
1325 ins->inst_imm = power2;
1329 case OP_LOAD_MEMBASE:
1330 case OP_LOADI4_MEMBASE:
1332 * OP_STORE_MEMBASE_REG reg, offset(basereg)
1333 * OP_LOAD_MEMBASE offset(basereg), reg
1335 if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_REG
1336 || last_ins->opcode == OP_STORE_MEMBASE_REG) &&
1337 ins->inst_basereg == last_ins->inst_destbasereg &&
1338 ins->inst_offset == last_ins->inst_offset) {
1339 if (ins->dreg == last_ins->sreg1) {
1340 last_ins->next = ins->next;
1344 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1345 ins->opcode = OP_MOVE;
1346 ins->sreg1 = last_ins->sreg1;
1350 * Note: reg1 must be different from the basereg in the second load
1351 * OP_LOAD_MEMBASE offset(basereg), reg1
1352 * OP_LOAD_MEMBASE offset(basereg), reg2
1354 * OP_LOAD_MEMBASE offset(basereg), reg1
1355 * OP_MOVE reg1, reg2
1357 } if (last_ins && (last_ins->opcode == OP_LOADI4_MEMBASE
1358 || last_ins->opcode == OP_LOAD_MEMBASE) &&
1359 ins->inst_basereg != last_ins->dreg &&
1360 ins->inst_basereg == last_ins->inst_basereg &&
1361 ins->inst_offset == last_ins->inst_offset) {
1363 if (ins->dreg == last_ins->dreg) {
1364 last_ins->next = ins->next;
1368 ins->opcode = OP_MOVE;
1369 ins->sreg1 = last_ins->dreg;
1372 //g_assert_not_reached ();
1376 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1377 * OP_LOAD_MEMBASE offset(basereg), reg
1379 * OP_STORE_MEMBASE_IMM imm, offset(basereg)
1380 * OP_ICONST reg, imm
1382 } else if (last_ins && (last_ins->opcode == OP_STOREI4_MEMBASE_IMM
1383 || last_ins->opcode == OP_STORE_MEMBASE_IMM) &&
1384 ins->inst_basereg == last_ins->inst_destbasereg &&
1385 ins->inst_offset == last_ins->inst_offset) {
1386 //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
1387 ins->opcode = OP_ICONST;
1388 ins->inst_c0 = last_ins->inst_imm;
1389 g_assert_not_reached (); // check this rule
1393 case OP_LOADU1_MEMBASE:
1394 case OP_LOADI1_MEMBASE:
1395 if (last_ins && (last_ins->opcode == OP_STOREI1_MEMBASE_REG) &&
1396 ins->inst_basereg == last_ins->inst_destbasereg &&
1397 ins->inst_offset == last_ins->inst_offset) {
1398 ins->opcode = (ins->opcode == OP_LOADI1_MEMBASE) ? CEE_CONV_I1 : CEE_CONV_U1;
1399 ins->sreg1 = last_ins->sreg1;
1402 case OP_LOADU2_MEMBASE:
1403 case OP_LOADI2_MEMBASE:
1404 if (last_ins && (last_ins->opcode == OP_STOREI2_MEMBASE_REG) &&
1405 ins->inst_basereg == last_ins->inst_destbasereg &&
1406 ins->inst_offset == last_ins->inst_offset) {
1407 ins->opcode = (ins->opcode == OP_LOADI2_MEMBASE) ? CEE_CONV_I2 : CEE_CONV_U2;
1408 ins->sreg1 = last_ins->sreg1;
1415 ins->opcode = OP_MOVE;
1419 if (ins->dreg == ins->sreg1) {
1421 last_ins->next = ins->next;
1426 * OP_MOVE sreg, dreg
1427 * OP_MOVE dreg, sreg
1429 if (last_ins && last_ins->opcode == OP_MOVE &&
1430 ins->sreg1 == last_ins->dreg &&
1431 ins->dreg == last_ins->sreg1) {
1432 last_ins->next = ins->next;
1441 bb->last_ins = last_ins;
1445 * the branch_cc_table should maintain the order of these
1459 branch_cc_table [] = {
1475 insert_after_ins (MonoBasicBlock *bb, MonoInst *ins, MonoInst *to_insert)
1479 bb->code = to_insert;
1480 to_insert->next = ins;
1482 to_insert->next = ins->next;
1483 ins->next = to_insert;
1487 #define NEW_INS(cfg,dest,op) do { \
1488 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
1489 (dest)->opcode = (op); \
1490 insert_after_ins (bb, last_ins, (dest)); \
1494 map_to_reg_reg_op (int op)
1503 case OP_COMPARE_IMM:
1517 case OP_LOAD_MEMBASE:
1518 return OP_LOAD_MEMINDEX;
1519 case OP_LOADI4_MEMBASE:
1520 return OP_LOADI4_MEMINDEX;
1521 case OP_LOADU4_MEMBASE:
1522 return OP_LOADU4_MEMINDEX;
1523 case OP_LOADU1_MEMBASE:
1524 return OP_LOADU1_MEMINDEX;
1525 case OP_LOADI2_MEMBASE:
1526 return OP_LOADI2_MEMINDEX;
1527 case OP_LOADU2_MEMBASE:
1528 return OP_LOADU2_MEMINDEX;
1529 case OP_LOADI1_MEMBASE:
1530 return OP_LOADI1_MEMINDEX;
1531 case OP_STOREI1_MEMBASE_REG:
1532 return OP_STOREI1_MEMINDEX;
1533 case OP_STOREI2_MEMBASE_REG:
1534 return OP_STOREI2_MEMINDEX;
1535 case OP_STOREI4_MEMBASE_REG:
1536 return OP_STOREI4_MEMINDEX;
1537 case OP_STORE_MEMBASE_REG:
1538 return OP_STORE_MEMINDEX;
1539 case OP_STORER4_MEMBASE_REG:
1540 return OP_STORER4_MEMINDEX;
1541 case OP_STORER8_MEMBASE_REG:
1542 return OP_STORER8_MEMINDEX;
1543 case OP_STORE_MEMBASE_IMM:
1544 return OP_STORE_MEMBASE_REG;
1545 case OP_STOREI1_MEMBASE_IMM:
1546 return OP_STOREI1_MEMBASE_REG;
1547 case OP_STOREI2_MEMBASE_IMM:
1548 return OP_STOREI2_MEMBASE_REG;
1549 case OP_STOREI4_MEMBASE_IMM:
1550 return OP_STOREI4_MEMBASE_REG;
1552 g_assert_not_reached ();
1556 * Remove from the instruction list the instructions that can't be
1557 * represented with very simple instructions with no register
1561 mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb)
1563 MonoInst *ins, *temp, *last_ins = NULL;
1564 int rot_amount, imm8, low_imm;
1566 /* setup the virtual reg allocator */
1567 if (bb->max_vreg > cfg->rs->next_vreg)
1568 cfg->rs->next_vreg = bb->max_vreg;
1573 switch (ins->opcode) {
1577 case OP_COMPARE_IMM:
1584 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount)) < 0) {
1585 NEW_INS (cfg, temp, OP_ICONST);
1586 temp->inst_c0 = ins->inst_imm;
1587 temp->dreg = mono_regstate_next_int (cfg->rs);
1588 ins->sreg2 = temp->dreg;
1589 ins->opcode = map_to_reg_reg_op (ins->opcode);
1593 if (ins->inst_imm == 1) {
1594 ins->opcode = OP_MOVE;
1597 if (ins->inst_imm == 0) {
1598 ins->opcode = OP_ICONST;
1602 imm8 = mono_is_power_of_two (ins->inst_imm);
1604 ins->opcode = OP_SHL_IMM;
1605 ins->inst_imm = imm8;
1608 NEW_INS (cfg, temp, OP_ICONST);
1609 temp->inst_c0 = ins->inst_imm;
1610 temp->dreg = mono_regstate_next_int (cfg->rs);
1611 ins->sreg2 = temp->dreg;
1612 ins->opcode = CEE_MUL;
1614 case OP_LOAD_MEMBASE:
1615 case OP_LOADI4_MEMBASE:
1616 case OP_LOADU4_MEMBASE:
1617 case OP_LOADU1_MEMBASE:
1618 /* we can do two things: load the immed in a register
1619 * and use an indexed load, or see if the immed can be
1620 * represented as an ad_imm + a load with a smaller offset
1621 * that fits. We just do the first for now, optimize later.
1623 if (arm_is_imm12 (ins->inst_offset))
1625 NEW_INS (cfg, 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_LOADI2_MEMBASE:
1632 case OP_LOADU2_MEMBASE:
1633 case OP_LOADI1_MEMBASE:
1634 if (arm_is_imm8 (ins->inst_offset))
1636 NEW_INS (cfg, temp, OP_ICONST);
1637 temp->inst_c0 = ins->inst_offset;
1638 temp->dreg = mono_regstate_next_int (cfg->rs);
1639 ins->sreg2 = temp->dreg;
1640 ins->opcode = map_to_reg_reg_op (ins->opcode);
1642 case OP_LOADR4_MEMBASE:
1643 case OP_LOADR8_MEMBASE:
1644 if (arm_is_fpimm8 (ins->inst_offset))
1646 low_imm = ins->inst_offset & 0x1ff;
1647 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~0x1ff, &rot_amount)) >= 0) {
1648 NEW_INS (cfg, temp, OP_ADD_IMM);
1649 temp->inst_imm = ins->inst_offset & ~0x1ff;
1650 temp->sreg1 = ins->inst_basereg;
1651 temp->dreg = mono_regstate_next_int (cfg->rs);
1652 ins->inst_basereg = temp->dreg;
1653 ins->inst_offset = low_imm;
1656 /* VFP/FPA doesn't have indexed load instructions */
1657 g_assert_not_reached ();
1659 case OP_STORE_MEMBASE_REG:
1660 case OP_STOREI4_MEMBASE_REG:
1661 case OP_STOREI1_MEMBASE_REG:
1662 if (arm_is_imm12 (ins->inst_offset))
1664 NEW_INS (cfg, temp, OP_ICONST);
1665 temp->inst_c0 = ins->inst_offset;
1666 temp->dreg = mono_regstate_next_int (cfg->rs);
1667 ins->sreg2 = temp->dreg;
1668 ins->opcode = map_to_reg_reg_op (ins->opcode);
1670 case OP_STOREI2_MEMBASE_REG:
1671 if (arm_is_imm8 (ins->inst_offset))
1673 NEW_INS (cfg, temp, OP_ICONST);
1674 temp->inst_c0 = ins->inst_offset;
1675 temp->dreg = mono_regstate_next_int (cfg->rs);
1676 ins->sreg2 = temp->dreg;
1677 ins->opcode = map_to_reg_reg_op (ins->opcode);
1679 case OP_STORER4_MEMBASE_REG:
1680 case OP_STORER8_MEMBASE_REG:
1681 if (arm_is_fpimm8 (ins->inst_offset))
1683 low_imm = ins->inst_offset & 0x1ff;
1684 if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_offset & ~ 0x1ff, &rot_amount)) >= 0 && arm_is_fpimm8 (low_imm)) {
1685 NEW_INS (cfg, temp, OP_ADD_IMM);
1686 temp->inst_imm = ins->inst_offset & ~0x1ff;
1687 temp->sreg1 = ins->inst_destbasereg;
1688 temp->dreg = mono_regstate_next_int (cfg->rs);
1689 ins->inst_destbasereg = temp->dreg;
1690 ins->inst_offset = low_imm;
1693 /*g_print ("fail with: %d (%d, %d)\n", ins->inst_offset, ins->inst_offset & ~0x1ff, low_imm);*/
1694 /* VFP/FPA doesn't have indexed store instructions */
1695 g_assert_not_reached ();
1697 case OP_STORE_MEMBASE_IMM:
1698 case OP_STOREI1_MEMBASE_IMM:
1699 case OP_STOREI2_MEMBASE_IMM:
1700 case OP_STOREI4_MEMBASE_IMM:
1701 NEW_INS (cfg, temp, OP_ICONST);
1702 temp->inst_c0 = ins->inst_imm;
1703 temp->dreg = mono_regstate_next_int (cfg->rs);
1704 ins->sreg1 = temp->dreg;
1705 ins->opcode = map_to_reg_reg_op (ins->opcode);
1707 goto loop_start; /* make it handle the possibly big ins->inst_offset */
1712 bb->last_ins = last_ins;
1713 bb->max_vreg = cfg->rs->next_vreg;
1718 mono_arch_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
1722 mono_arch_lowering_pass (cfg, bb);
1723 mono_local_regalloc (cfg, bb);
1727 emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size, gboolean is_signed)
1729 /* sreg is a float, dreg is an integer reg */
1731 ARM_FIXZ (code, dreg, sreg);
1732 #elif defined(ARM_FPU_VFP)
1734 ARM_TOSIZD (code, ARM_VFP_F0, sreg);
1736 ARM_TOUIZD (code, ARM_VFP_F0, sreg);
1737 ARM_FMRS (code, dreg, ARM_VFP_F0);
1741 ARM_AND_REG_IMM8 (code, dreg, dreg, 0xff);
1742 else if (size == 2) {
1743 ARM_SHL_IMM (code, dreg, dreg, 16);
1744 ARM_SHR_IMM (code, dreg, dreg, 16);
1748 ARM_SHL_IMM (code, dreg, dreg, 24);
1749 ARM_SAR_IMM (code, dreg, dreg, 24);
1750 } else if (size == 2) {
1751 ARM_SHL_IMM (code, dreg, dreg, 16);
1752 ARM_SAR_IMM (code, dreg, dreg, 16);
1760 const guchar *target;
1765 #define is_call_imm(diff) ((gint)(diff) >= -33554432 && (gint)(diff) <= 33554431)
1768 search_thunk_slot (void *data, int csize, int bsize, void *user_data) {
1769 PatchData *pdata = (PatchData*)user_data;
1770 guchar *code = data;
1771 guint32 *thunks = data;
1772 guint32 *endthunks = (guint32*)(code + bsize);
1774 int difflow, diffhigh;
1776 /* always ensure a call from pdata->code can reach to the thunks without further thunks */
1777 difflow = (char*)pdata->code - (char*)thunks;
1778 diffhigh = (char*)pdata->code - (char*)endthunks;
1779 if (!((is_call_imm (thunks) && is_call_imm (endthunks)) || (is_call_imm (difflow) && is_call_imm (diffhigh))))
1783 * The thunk is composed of 3 words:
1784 * load constant from thunks [2] into ARM_IP
1787 * Note that the LR register is already setup
1789 //g_print ("thunk nentries: %d\n", ((char*)endthunks - (char*)thunks)/16);
1790 if ((pdata->found == 2) || (pdata->code >= code && pdata->code <= code + csize)) {
1791 while (thunks < endthunks) {
1792 //g_print ("looking for target: %p at %p (%08x-%08x)\n", pdata->target, thunks, thunks [0], thunks [1]);
1793 if (thunks [2] == (guint32)pdata->target) {
1794 arm_patch (pdata->code, (guchar*)thunks);
1795 mono_arch_flush_icache (pdata->code, 4);
1798 } else if ((thunks [0] == 0) && (thunks [1] == 0) && (thunks [2] == 0)) {
1799 /* found a free slot instead: emit thunk */
1800 /* ARMREG_IP is fine to use since this can't be an IMT call
1803 code = (guchar*)thunks;
1804 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
1805 if (thumb_supported)
1806 ARM_BX (code, ARMREG_IP);
1808 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
1809 thunks [2] = (guint32)pdata->target;
1810 mono_arch_flush_icache ((guchar*)thunks, 12);
1812 arm_patch (pdata->code, (guchar*)thunks);
1813 mono_arch_flush_icache (pdata->code, 4);
1817 /* skip 12 bytes, the size of the thunk */
1821 //g_print ("failed thunk lookup for %p from %p at %p (%d entries)\n", pdata->target, pdata->code, data, count);
1827 handle_thunk (int absolute, guchar *code, const guchar *target) {
1828 MonoDomain *domain = mono_domain_get ();
1832 pdata.target = target;
1833 pdata.absolute = absolute;
1836 mono_domain_lock (domain);
1837 mono_code_manager_foreach (domain->code_mp, search_thunk_slot, &pdata);
1840 /* this uses the first available slot */
1842 mono_code_manager_foreach (domain->code_mp, search_thunk_slot, &pdata);
1844 mono_domain_unlock (domain);
1846 if (pdata.found != 1)
1847 g_print ("thunk failed for %p from %p\n", target, code);
1848 g_assert (pdata.found == 1);
1852 arm_patch (guchar *code, const guchar *target)
1854 guint32 *code32 = (void*)code;
1855 guint32 ins = *code32;
1856 guint32 prim = (ins >> 25) & 7;
1857 guint32 tval = GPOINTER_TO_UINT (target);
1859 //g_print ("patching 0x%08x (0x%08x) to point to 0x%08x\n", code, ins, target);
1860 if (prim == 5) { /* 101b */
1861 /* the diff starts 8 bytes from the branch opcode */
1862 gint diff = target - code - 8;
1864 gint tmask = 0xffffffff;
1865 if (tval & 1) { /* entering thumb mode */
1866 diff = target - 1 - code - 8;
1867 g_assert (thumb_supported);
1868 tbits = 0xf << 28; /* bl->blx bit pattern */
1869 g_assert ((ins & (1 << 24))); /* it must be a bl, not b instruction */
1870 /* this low bit of the displacement is moved to bit 24 in the instruction encoding */
1874 tmask = ~(1 << 24); /* clear the link bit */
1875 /*g_print ("blx to thumb: target: %p, code: %p, diff: %d, mask: %x\n", target, code, diff, tmask);*/
1880 if (diff <= 33554431) {
1882 ins = (ins & 0xff000000) | diff;
1884 *code32 = ins | tbits;
1888 /* diff between 0 and -33554432 */
1889 if (diff >= -33554432) {
1891 ins = (ins & 0xff000000) | (diff & ~0xff000000);
1893 *code32 = ins | tbits;
1898 handle_thunk (TRUE, code, target);
1903 * The alternative call sequences looks like this:
1905 * ldr ip, [pc] // loads the address constant
1906 * b 1f // jumps around the constant
1907 * address constant embedded in the code
1912 * There are two cases for patching:
1913 * a) at the end of method emission: in this case code points to the start
1914 * of the call sequence
1915 * b) during runtime patching of the call site: in this case code points
1916 * to the mov pc, ip instruction
1918 * We have to handle also the thunk jump code sequence:
1922 * address constant // execution never reaches here
1924 if ((ins & 0x0ffffff0) == 0x12fff10) {
1925 /* Branch and exchange: the address is constructed in a reg
1926 * We can patch BX when the code sequence is the following:
1927 * ldr ip, [pc, #0] ; 0x8
1934 guint8 *emit = (guint8*)ccode;
1935 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
1937 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
1938 ARM_BX (emit, ARMREG_IP);
1940 /*patching from magic trampoline*/
1941 if (ins == ccode [3]) {
1942 g_assert (code32 [-4] == ccode [0]);
1943 g_assert (code32 [-3] == ccode [1]);
1944 g_assert (code32 [-1] == ccode [2]);
1945 code32 [-2] = (guint32)target;
1948 /*patching from JIT*/
1949 if (ins == ccode [0]) {
1950 g_assert (code32 [1] == ccode [1]);
1951 g_assert (code32 [3] == ccode [2]);
1952 g_assert (code32 [4] == ccode [3]);
1953 code32 [2] = (guint32)target;
1956 g_assert_not_reached ();
1959 guint32 *tmp = ccode;
1960 guint8 *emit = (guint8*)tmp;
1961 ARM_LDR_IMM (emit, ARMREG_IP, ARMREG_PC, 0);
1962 ARM_MOV_REG_REG (emit, ARMREG_LR, ARMREG_PC);
1963 ARM_MOV_REG_REG (emit, ARMREG_PC, ARMREG_IP);
1964 ARM_BX (emit, ARMREG_IP);
1965 if (ins == ccode [2]) {
1966 g_assert_not_reached (); // should be -2 ...
1967 code32 [-1] = (guint32)target;
1970 if (ins == ccode [0]) {
1971 /* handles both thunk jump code and the far call sequence */
1972 code32 [2] = (guint32)target;
1975 g_assert_not_reached ();
1977 // g_print ("patched with 0x%08x\n", ins);
1981 * Return the >= 0 uimm8 value if val can be represented with a byte + rotation
1982 * (with the rotation amount in *rot_amount. rot_amount is already adjusted
1983 * to be used with the emit macros.
1984 * Return -1 otherwise.
1987 mono_arm_is_rotated_imm8 (guint32 val, gint *rot_amount)
1990 for (i = 0; i < 31; i+= 2) {
1991 res = (val << (32 - i)) | (val >> i);
1994 *rot_amount = i? 32 - i: 0;
2001 * Emits in code a sequence of instructions that load the value 'val'
2002 * into the dreg register. Uses at most 4 instructions.
2005 mono_arm_emit_load_imm (guint8 *code, int dreg, guint32 val)
2007 int imm8, rot_amount;
2009 ARM_LDR_IMM (code, dreg, ARMREG_PC, 0);
2010 /* skip the constant pool */
2016 if ((imm8 = mono_arm_is_rotated_imm8 (val, &rot_amount)) >= 0) {
2017 ARM_MOV_REG_IMM (code, dreg, imm8, rot_amount);
2018 } else if ((imm8 = mono_arm_is_rotated_imm8 (~val, &rot_amount)) >= 0) {
2019 ARM_MVN_REG_IMM (code, dreg, imm8, rot_amount);
2022 ARM_MOV_REG_IMM8 (code, dreg, (val & 0xFF));
2024 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF00) >> 8, 24);
2026 if (val & 0xFF0000) {
2027 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
2029 if (val & 0xFF000000) {
2030 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2032 } else if (val & 0xFF00) {
2033 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF00) >> 8, 24);
2034 if (val & 0xFF0000) {
2035 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
2037 if (val & 0xFF000000) {
2038 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2040 } else if (val & 0xFF0000) {
2041 ARM_MOV_REG_IMM (code, dreg, (val & 0xFF0000) >> 16, 16);
2042 if (val & 0xFF000000) {
2043 ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF000000) >> 24, 8);
2046 //g_assert_not_reached ();
2052 mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
2057 guint8 *code = cfg->native_code + cfg->code_len;
2058 MonoInst *last_ins = NULL;
2059 guint last_offset = 0;
2061 int imm8, rot_amount;
2063 if (cfg->opt & MONO_OPT_PEEPHOLE)
2064 peephole_pass (cfg, bb);
2066 /* we don't align basic blocks of loops on arm */
2068 if (cfg->verbose_level > 2)
2069 g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset);
2071 cpos = bb->max_offset;
2073 if (cfg->prof_options & MONO_PROFILE_COVERAGE) {
2074 //MonoCoverageInfo *cov = mono_get_coverage_info (cfg->method);
2075 //g_assert (!mono_compile_aot);
2078 // cov->data [bb->dfn].iloffset = bb->cil_code - cfg->cil_code;
2079 /* this is not thread save, but good enough */
2080 /* fixme: howto handle overflows? */
2081 //x86_inc_mem (code, &cov->data [bb->dfn].count);
2086 offset = code - cfg->native_code;
2088 max_len = ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
2090 if (offset > (cfg->code_size - max_len - 16)) {
2091 cfg->code_size *= 2;
2092 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
2093 code = cfg->native_code + offset;
2095 // if (ins->cil_code)
2096 // g_print ("cil code\n");
2097 mono_debug_record_line_number (cfg, ins, offset);
2099 switch (ins->opcode) {
2100 case OP_MEMORY_BARRIER:
2103 g_assert_not_reached ();
2106 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2107 ppc_mulhw (code, ppc_r3, ins->sreg1, ins->sreg2);
2110 ppc_mullw (code, ppc_r4, ins->sreg1, ins->sreg2);
2111 ppc_mulhwu (code, ppc_r3, ins->sreg1, ins->sreg2);
2113 case OP_STOREI1_MEMBASE_IMM:
2114 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFF);
2115 g_assert (arm_is_imm12 (ins->inst_offset));
2116 ARM_STRB_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2118 case OP_STOREI2_MEMBASE_IMM:
2119 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm & 0xFFFF);
2120 g_assert (arm_is_imm8 (ins->inst_offset));
2121 ARM_STRH_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2123 case OP_STORE_MEMBASE_IMM:
2124 case OP_STOREI4_MEMBASE_IMM:
2125 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_imm);
2126 g_assert (arm_is_imm12 (ins->inst_offset));
2127 ARM_STR_IMM (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
2129 case OP_STOREI1_MEMBASE_REG:
2130 g_assert (arm_is_imm12 (ins->inst_offset));
2131 ARM_STRB_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2133 case OP_STOREI2_MEMBASE_REG:
2134 g_assert (arm_is_imm8 (ins->inst_offset));
2135 ARM_STRH_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2137 case OP_STORE_MEMBASE_REG:
2138 case OP_STOREI4_MEMBASE_REG:
2139 /* this case is special, since it happens for spill code after lowering has been called */
2140 if (arm_is_imm12 (ins->inst_offset)) {
2141 ARM_STR_IMM (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2143 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2144 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ARMREG_LR);
2147 case OP_STOREI1_MEMINDEX:
2148 ARM_STRB_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2150 case OP_STOREI2_MEMINDEX:
2151 /* note: the args are reversed in the macro */
2152 ARM_STRH_REG_REG (code, ins->inst_destbasereg, ins->sreg1, ins->sreg2);
2154 case OP_STORE_MEMINDEX:
2155 case OP_STOREI4_MEMINDEX:
2156 ARM_STR_REG_REG (code, ins->sreg1, ins->inst_destbasereg, ins->sreg2);
2161 g_assert_not_reached ();
2164 g_assert_not_reached ();
2166 case OP_LOAD_MEMINDEX:
2167 case OP_LOADI4_MEMINDEX:
2168 case OP_LOADU4_MEMINDEX:
2169 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2171 case OP_LOADI1_MEMINDEX:
2172 /* note: the args are reversed in the macro */
2173 ARM_LDRSB_REG_REG (code, ins->inst_basereg, ins->dreg, ins->sreg2);
2175 case OP_LOADU1_MEMINDEX:
2176 ARM_LDRB_REG_REG (code, ins->dreg, ins->inst_basereg, ins->sreg2);
2178 case OP_LOADI2_MEMINDEX:
2179 /* note: the args are reversed in the macro */
2180 ARM_LDRSH_REG_REG (code, ins->inst_basereg, ins->dreg, ins->sreg2);
2182 case OP_LOADU2_MEMINDEX:
2183 /* note: the args are reversed in the macro */
2184 ARM_LDRH_REG_REG (code, ins->inst_basereg, ins->dreg, ins->sreg2);
2186 case OP_LOAD_MEMBASE:
2187 case OP_LOADI4_MEMBASE:
2188 case OP_LOADU4_MEMBASE:
2189 /* this case is special, since it happens for spill code after lowering has been called */
2190 if (arm_is_imm12 (ins->inst_offset)) {
2191 ARM_LDR_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2193 code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
2194 ARM_LDR_REG_REG (code, ins->dreg, ins->inst_basereg, ARMREG_LR);
2197 case OP_LOADI1_MEMBASE:
2198 g_assert (arm_is_imm8 (ins->inst_offset));
2199 ARM_LDRSB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2201 case OP_LOADU1_MEMBASE:
2202 g_assert (arm_is_imm12 (ins->inst_offset));
2203 ARM_LDRB_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2205 case OP_LOADU2_MEMBASE:
2206 g_assert (arm_is_imm8 (ins->inst_offset));
2207 ARM_LDRH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2209 case OP_LOADI2_MEMBASE:
2210 g_assert (arm_is_imm8 (ins->inst_offset));
2211 ARM_LDRSH_IMM (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2214 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 24);
2215 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 24);
2218 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2219 ARM_SAR_IMM (code, ins->dreg, ins->dreg, 16);
2222 ARM_AND_REG_IMM8 (code, ins->dreg, ins->sreg1, 0xff);
2225 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, 16);
2226 ARM_SHR_IMM (code, ins->dreg, ins->dreg, 16);
2229 ARM_CMP_REG_REG (code, ins->sreg1, ins->sreg2);
2231 case OP_COMPARE_IMM:
2232 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2233 g_assert (imm8 >= 0);
2234 ARM_CMP_REG_IMM (code, ins->sreg1, imm8, rot_amount);
2237 *(int*)code = 0xe7f001f0;
2238 *(int*)code = 0xef9f0001;
2243 ARM_ADDS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2246 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2249 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2252 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2253 g_assert (imm8 >= 0);
2254 ARM_ADDS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2257 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2258 g_assert (imm8 >= 0);
2259 ARM_ADD_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2262 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2263 g_assert (imm8 >= 0);
2264 ARM_ADCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2267 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2268 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2270 case CEE_ADD_OVF_UN:
2271 ARM_ADD_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2272 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2275 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2276 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2278 case CEE_SUB_OVF_UN:
2279 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2280 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2282 case OP_ADD_OVF_CARRY:
2283 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2284 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2286 case OP_ADD_OVF_UN_CARRY:
2287 ARM_ADCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2288 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2290 case OP_SUB_OVF_CARRY:
2291 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2292 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_FALSE, PPC_BR_EQ, "OverflowException");
2294 case OP_SUB_OVF_UN_CARRY:
2295 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2296 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_TRUE, PPC_BR_EQ, "OverflowException");
2299 ARM_SUBS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2302 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2303 g_assert (imm8 >= 0);
2304 ARM_SUBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2307 ARM_SUB_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2310 ARM_SBCS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2313 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2314 g_assert (imm8 >= 0);
2315 ARM_SUB_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2318 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2319 g_assert (imm8 >= 0);
2320 ARM_SBCS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2322 case OP_ARM_RSBS_IMM:
2323 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2324 g_assert (imm8 >= 0);
2325 ARM_RSBS_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2327 case OP_ARM_RSC_IMM:
2328 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2329 g_assert (imm8 >= 0);
2330 ARM_RSC_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2333 ARM_AND_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2336 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2337 g_assert (imm8 >= 0);
2338 ARM_AND_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2346 /* crappy ARM arch doesn't have a DIV instruction */
2347 g_assert_not_reached ();
2349 ARM_ORR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2352 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2353 g_assert (imm8 >= 0);
2354 ARM_ORR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2357 ARM_EOR_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2360 imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount);
2361 g_assert (imm8 >= 0);
2362 ARM_EOR_REG_IMM (code, ins->dreg, ins->sreg1, imm8, rot_amount);
2365 ARM_SHL_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2369 ARM_SHL_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2372 ARM_SAR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2376 ARM_SAR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2380 ARM_SHR_IMM (code, ins->dreg, ins->sreg1, (ins->inst_imm & 0x1f));
2383 ARM_SHR_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2386 ARM_MVN_REG_REG (code, ins->dreg, ins->sreg1);
2389 ARM_RSB_REG_IMM8 (code, ins->dreg, ins->sreg1, 0);
2392 if (ins->dreg == ins->sreg2)
2393 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2395 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg2, ins->sreg1);
2398 g_assert_not_reached ();
2401 /* FIXME: handle ovf/ sreg2 != dreg */
2402 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2404 case CEE_MUL_OVF_UN:
2405 /* FIXME: handle ovf/ sreg2 != dreg */
2406 ARM_MUL_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
2410 code = mono_arm_emit_load_imm (code, ins->dreg, ins->inst_c0);
2413 /* Load the GOT offset */
2414 mono_add_patch_info (cfg, offset, (MonoJumpInfoType)ins->inst_i1, ins->inst_p0);
2415 ARM_LDR_IMM (code, ins->dreg, ARMREG_PC, 0);
2417 *(gpointer*)code = NULL;
2419 /* Load the value from the GOT */
2420 ARM_LDR_REG_REG (code, ins->dreg, ARMREG_PC, ins->dreg);
2426 if (ins->dreg != ins->sreg1)
2427 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2430 int saved = ins->sreg2;
2431 if (ins->sreg2 == ARM_LSW_REG) {
2432 ARM_MOV_REG_REG (code, ARMREG_LR, ins->sreg2);
2435 if (ins->sreg1 != ARM_LSW_REG)
2436 ARM_MOV_REG_REG (code, ARM_LSW_REG, ins->sreg1);
2437 if (saved != ARM_MSW_REG)
2438 ARM_MOV_REG_REG (code, ARM_MSW_REG, saved);
2444 ARM_MVFD (code, ins->dreg, ins->sreg1);
2445 #elif defined(ARM_FPU_VFP)
2446 ARM_CPYD (code, ins->dreg, ins->sreg1);
2449 case OP_FCONV_TO_R4:
2451 ARM_MVFS (code, ins->dreg, ins->sreg1);
2452 #elif defined(ARM_FPU_VFP)
2453 ARM_CVTD (code, ins->dreg, ins->sreg1);
2454 ARM_CVTS (code, ins->dreg, ins->dreg);
2459 * Keep in sync with mono_arch_emit_epilog
2461 g_assert (!cfg->method->save_lmf);
2462 code = emit_big_add (code, ARMREG_SP, cfg->frame_reg, cfg->stack_usage);
2463 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP)) | ((1 << ARMREG_LR)));
2464 mono_add_patch_info (cfg, (guint8*) code - cfg->native_code, MONO_PATCH_INFO_METHOD_JUMP, ins->inst_p0);
2468 /* ensure ins->sreg1 is not NULL */
2469 ARM_LDR_IMM (code, ARMREG_LR, ins->sreg1, 0);
2473 if (ppc_is_imm16 (cfg->sig_cookie + cfg->stack_usage)) {
2474 ppc_addi (code, ppc_r11, cfg->frame_reg, cfg->sig_cookie + cfg->stack_usage);
2476 ppc_load (code, ppc_r11, cfg->sig_cookie + cfg->stack_usage);
2477 ppc_add (code, ppc_r11, cfg->frame_reg, ppc_r11);
2479 ppc_stw (code, ppc_r11, 0, ins->sreg1);
2488 call = (MonoCallInst*)ins;
2489 if (ins->flags & MONO_INST_HAS_METHOD)
2490 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, call->method);
2492 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, call->fptr);
2493 code = emit_call_seq (cfg, code);
2498 case OP_VOIDCALL_REG:
2500 code = emit_call_reg (code, ins->sreg1);
2502 case OP_FCALL_MEMBASE:
2503 case OP_LCALL_MEMBASE:
2504 case OP_VCALL_MEMBASE:
2505 case OP_VOIDCALL_MEMBASE:
2506 case OP_CALL_MEMBASE:
2507 g_assert (arm_is_imm12 (ins->inst_offset));
2508 g_assert (ins->sreg1 != ARMREG_LR);
2509 call = (MonoCallInst*)ins;
2510 if (call->method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
2511 if (cfg->compile_aot)
2513 cfg->disable_aot = 1;
2514 ARM_ADD_REG_IMM8 (code, ARMREG_LR, ARMREG_PC, 4);
2515 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
2516 *((gpointer*)code) = (gpointer)call->method;
2519 ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
2520 ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
2524 g_assert_not_reached ();
2527 /* keep alignment */
2528 int alloca_waste = cfg->param_area;
2531 /* round the size to 8 bytes */
2532 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->sreg1, 7);
2533 ARM_BIC_REG_IMM8 (code, ins->dreg, ins->sreg1, 7);
2534 ARM_ADD_REG_IMM8 (code, ins->dreg, ins->dreg, alloca_waste);
2535 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ins->dreg);
2536 /* memzero the area: dreg holds the size, sp is the pointer */
2537 if (ins->flags & MONO_INST_INIT) {
2538 guint8 *start_loop, *branch_to_cond;
2539 ARM_MOV_REG_IMM8 (code, ARMREG_LR, 0);
2540 branch_to_cond = code;
2543 ARM_STR_REG_REG (code, ARMREG_LR, ARMREG_SP, ins->dreg);
2544 arm_patch (branch_to_cond, code);
2545 /* decrement by 4 and set flags */
2546 ARM_SUBS_REG_IMM8 (code, ins->dreg, ins->dreg, 4);
2547 ARM_B_COND (code, ARMCOND_LT, 0);
2548 arm_patch (code - 4, start_loop);
2550 ARM_ADD_REG_IMM8 (code, ins->dreg, ARMREG_SP, alloca_waste);
2554 g_assert_not_reached ();
2555 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_LR);
2558 if (ins->sreg1 != ARMREG_R0)
2559 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
2560 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2561 (gpointer)"mono_arch_throw_exception");
2562 code = emit_call_seq (cfg, code);
2566 if (ins->sreg1 != ARMREG_R0)
2567 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
2568 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
2569 (gpointer)"mono_arch_rethrow_exception");
2570 code = emit_call_seq (cfg, code);
2573 case OP_START_HANDLER:
2574 if (arm_is_imm12 (ins->inst_left->inst_offset)) {
2575 ARM_STR_IMM (code, ARMREG_LR, ins->inst_left->inst_basereg, ins->inst_left->inst_offset);
2577 code = mono_arm_emit_load_imm (code, ARMREG_IP, ins->inst_left->inst_offset);
2578 ARM_STR_REG_REG (code, ARMREG_LR, ins->inst_left->inst_basereg, ARMREG_IP);
2582 if (ins->sreg1 != ARMREG_R0)
2583 ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
2584 if (arm_is_imm12 (ins->inst_left->inst_offset)) {
2585 ARM_LDR_IMM (code, ARMREG_IP, ins->inst_left->inst_basereg, ins->inst_left->inst_offset);
2587 g_assert (ARMREG_IP != ins->inst_left->inst_basereg);
2588 code = mono_arm_emit_load_imm (code, ARMREG_IP, ins->inst_left->inst_offset);
2589 ARM_LDR_REG_REG (code, ARMREG_IP, ins->inst_left->inst_basereg, ARMREG_IP);
2591 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
2594 if (arm_is_imm12 (ins->inst_left->inst_offset)) {
2595 ARM_LDR_IMM (code, ARMREG_IP, ins->inst_left->inst_basereg, ins->inst_left->inst_offset);
2597 g_assert (ARMREG_IP != ins->inst_left->inst_basereg);
2598 code = mono_arm_emit_load_imm (code, ARMREG_IP, ins->inst_left->inst_offset);
2599 ARM_LDR_REG_REG (code, ARMREG_IP, ins->inst_left->inst_basereg, ARMREG_IP);
2601 ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
2603 case OP_CALL_HANDLER:
2604 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb);
2608 ins->inst_c0 = code - cfg->native_code;
2611 if (ins->flags & MONO_INST_BRLABEL) {
2612 /*if (ins->inst_i0->inst_c0) {
2614 //x86_jump_code (code, cfg->native_code + ins->inst_i0->inst_c0);
2616 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_LABEL, ins->inst_i0);
2620 /*if (ins->inst_target_bb->native_offset) {
2622 //x86_jump_code (code, cfg->native_code + ins->inst_target_bb->native_offset);
2624 mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb);
2630 ARM_MOV_REG_REG (code, ARMREG_PC, ins->sreg1);
2634 * In the normal case we have:
2635 * ldr pc, [pc, ins->sreg1 << 2]
2638 * ldr lr, [pc, ins->sreg1 << 2]
2640 * After follows the data.
2641 * FIXME: add aot support.
2643 max_len += 4 * GPOINTER_TO_INT (ins->klass);
2644 if (offset > (cfg->code_size - max_len - 16)) {
2645 cfg->code_size += max_len;
2646 cfg->code_size *= 2;
2647 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
2648 code = cfg->native_code + offset;
2650 ARM_LDR_REG_REG_SHIFT (code, ARMREG_PC, ARMREG_PC, ins->sreg1, ARMSHIFT_LSL, 2);
2652 code += 4 * GPOINTER_TO_INT (ins->klass);
2655 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
2656 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
2659 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2660 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LT);
2663 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2664 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_LO);
2667 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2668 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_GT);
2671 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2672 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_HI);
2674 case OP_COND_EXC_EQ:
2675 case OP_COND_EXC_NE_UN:
2676 case OP_COND_EXC_LT:
2677 case OP_COND_EXC_LT_UN:
2678 case OP_COND_EXC_GT:
2679 case OP_COND_EXC_GT_UN:
2680 case OP_COND_EXC_GE:
2681 case OP_COND_EXC_GE_UN:
2682 case OP_COND_EXC_LE:
2683 case OP_COND_EXC_LE_UN:
2684 EMIT_COND_SYSTEM_EXCEPTION (ins->opcode - OP_COND_EXC_EQ, ins->inst_p1);
2687 case OP_COND_EXC_OV:
2688 case OP_COND_EXC_NC:
2689 case OP_COND_EXC_NO:
2690 g_assert_not_reached ();
2702 EMIT_COND_BRANCH (ins, ins->opcode - CEE_BEQ);
2705 /* floating point opcodes */
2708 if (cfg->compile_aot) {
2709 ARM_LDFD (code, ins->dreg, ARMREG_PC, 0);
2711 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
2713 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
2716 /* FIXME: we can optimize the imm load by dealing with part of
2717 * the displacement in LDFD (aligning to 512).
2719 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
2720 ARM_LDFD (code, ins->dreg, ARMREG_LR, 0);
2724 if (cfg->compile_aot) {
2725 ARM_LDFS (code, ins->dreg, ARMREG_PC, 0);
2727 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
2730 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
2731 ARM_LDFS (code, ins->dreg, ARMREG_LR, 0);
2734 case OP_STORER8_MEMBASE_REG:
2735 g_assert (arm_is_fpimm8 (ins->inst_offset));
2736 ARM_STFD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2738 case OP_LOADR8_MEMBASE:
2739 g_assert (arm_is_fpimm8 (ins->inst_offset));
2740 ARM_LDFD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2742 case OP_STORER4_MEMBASE_REG:
2743 g_assert (arm_is_fpimm8 (ins->inst_offset));
2744 ARM_STFS (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2746 case OP_LOADR4_MEMBASE:
2747 g_assert (arm_is_fpimm8 (ins->inst_offset));
2748 ARM_LDFS (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2750 case CEE_CONV_R_UN: {
2752 tmpreg = ins->dreg == 0? 1: 0;
2753 ARM_CMP_REG_IMM8 (code, ins->sreg1, 0);
2754 ARM_FLTD (code, ins->dreg, ins->sreg1);
2755 ARM_B_COND (code, ARMCOND_GE, 8);
2756 /* save the temp register */
2757 ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
2758 ARM_STFD (code, tmpreg, ARMREG_SP, 0);
2759 ARM_LDFD (code, tmpreg, ARMREG_PC, 12);
2760 ARM_FPA_ADFD (code, ins->dreg, ins->dreg, tmpreg);
2761 ARM_LDFD (code, tmpreg, ARMREG_SP, 0);
2762 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);
2763 /* skip the constant pool */
2766 *(int*)code = 0x41f00000;
2771 * ldfltd ftemp, [pc, #8] 0x41f00000 0x00000000
2772 * adfltd fdest, fdest, ftemp
2777 ARM_FLTS (code, ins->dreg, ins->sreg1);
2780 ARM_FLTD (code, ins->dreg, ins->sreg1);
2782 #elif defined(ARM_FPU_VFP)
2784 if (cfg->compile_aot) {
2785 ARM_LDFD (code, ins->dreg, ARMREG_PC, 0);
2787 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
2789 *(guint32*)code = ((guint32*)(ins->inst_p0))[1];
2792 /* FIXME: we can optimize the imm load by dealing with part of
2793 * the displacement in LDFD (aligning to 512).
2795 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
2796 ARM_FLDD (code, ins->dreg, ARMREG_LR, 0);
2800 if (cfg->compile_aot) {
2801 ARM_FLDS (code, ins->dreg, ARMREG_PC, 0);
2803 *(guint32*)code = ((guint32*)(ins->inst_p0))[0];
2805 ARM_CVTS (code, ins->dreg, ins->dreg);
2807 code = mono_arm_emit_load_imm (code, ARMREG_LR, (guint32)ins->inst_p0);
2808 ARM_FLDS (code, ins->dreg, ARMREG_LR, 0);
2809 ARM_CVTS (code, ins->dreg, ins->dreg);
2812 case OP_STORER8_MEMBASE_REG:
2813 g_assert (arm_is_fpimm8 (ins->inst_offset));
2814 ARM_FSTD (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2816 case OP_LOADR8_MEMBASE:
2817 g_assert (arm_is_fpimm8 (ins->inst_offset));
2818 ARM_FLDD (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2820 case OP_STORER4_MEMBASE_REG:
2821 g_assert (arm_is_fpimm8 (ins->inst_offset));
2822 ARM_FSTS (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset);
2824 case OP_LOADR4_MEMBASE:
2825 g_assert (arm_is_fpimm8 (ins->inst_offset));
2826 ARM_FLDS (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
2828 case CEE_CONV_R_UN: {
2829 g_assert_not_reached ();
2833 g_assert_not_reached ();
2834 //ARM_FLTS (code, ins->dreg, ins->sreg1);
2837 g_assert_not_reached ();
2838 //ARM_FLTD (code, ins->dreg, ins->sreg1);
2841 case OP_FCONV_TO_I1:
2842 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, TRUE);
2844 case OP_FCONV_TO_U1:
2845 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 1, FALSE);
2847 case OP_FCONV_TO_I2:
2848 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, TRUE);
2850 case OP_FCONV_TO_U2:
2851 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 2, FALSE);
2853 case OP_FCONV_TO_I4:
2855 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, TRUE);
2857 case OP_FCONV_TO_U4:
2859 code = emit_float_to_int (cfg, code, ins->dreg, ins->sreg1, 4, FALSE);
2861 case OP_FCONV_TO_I8:
2862 case OP_FCONV_TO_U8:
2863 g_assert_not_reached ();
2864 /* Implemented as helper calls */
2866 case OP_LCONV_TO_R_UN:
2867 g_assert_not_reached ();
2868 /* Implemented as helper calls */
2870 case OP_LCONV_TO_OVF_I: {
2872 guint32 *negative_branch, *msword_positive_branch, *msword_negative_branch, *ovf_ex_target;
2873 // Check if its negative
2874 ppc_cmpi (code, 0, 0, ins->sreg1, 0);
2875 negative_branch = code;
2876 ppc_bc (code, PPC_BR_TRUE, PPC_BR_LT, 0);
2877 // Its positive msword == 0
2878 ppc_cmpi (code, 0, 0, ins->sreg2, 0);
2879 msword_positive_branch = code;
2880 ppc_bc (code, PPC_BR_TRUE, PPC_BR_EQ, 0);
2882 ovf_ex_target = code;
2883 //EMIT_COND_SYSTEM_EXCEPTION_FLAGS (PPC_BR_ALWAYS, 0, "OverflowException");
2885 ppc_patch (negative_branch, code);
2886 ppc_cmpi (code, 0, 0, ins->sreg2, -1);
2887 msword_negative_branch = code;
2888 ppc_bc (code, PPC_BR_FALSE, PPC_BR_EQ, 0);
2889 ppc_patch (msword_negative_branch, ovf_ex_target);
2891 ppc_patch (msword_positive_branch, code);
2892 if (ins->dreg != ins->sreg1)
2893 ppc_mr (code, ins->dreg, ins->sreg1);
2895 if (ins->dreg != ins->sreg1)
2896 ARM_MOV_REG_REG (code, ins->dreg, ins->sreg1);
2901 ARM_FPA_ADFD (code, ins->dreg, ins->sreg1, ins->sreg2);
2904 ARM_FPA_SUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
2907 ARM_FPA_MUFD (code, ins->dreg, ins->sreg1, ins->sreg2);
2910 ARM_FPA_DVFD (code, ins->dreg, ins->sreg1, ins->sreg2);
2913 ARM_MNFD (code, ins->dreg, ins->sreg1);
2915 #elif defined(ARM_FPU_VFP)
2917 ARM_VFP_ADDD (code, ins->dreg, ins->sreg1, ins->sreg2);
2920 ARM_VFP_SUBD (code, ins->dreg, ins->sreg1, ins->sreg2);
2923 ARM_VFP_MULD (code, ins->dreg, ins->sreg1, ins->sreg2);
2926 ARM_VFP_DIVD (code, ins->dreg, ins->sreg1, ins->sreg2);
2929 ARM_NEGD (code, ins->dreg, ins->sreg1);
2934 g_assert_not_reached ();
2937 /* each fp compare op needs to do its own */
2938 g_assert_not_reached ();
2939 //ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2943 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2944 #elif defined(ARM_FPU_VFP)
2945 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2947 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 0, ARMCOND_NE);
2948 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_EQ);
2952 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2953 #elif defined(ARM_FPU_VFP)
2954 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2956 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2957 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
2961 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2962 #elif defined(ARM_FPU_VFP)
2963 ARM_CMPD (code, ins->sreg1, ins->sreg2);
2965 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2966 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
2967 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
2972 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
2973 #elif defined(ARM_FPU_VFP)
2974 ARM_CMPD (code, ins->sreg2, ins->sreg1);
2976 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2977 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
2982 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
2983 #elif defined(ARM_FPU_VFP)
2984 ARM_CMPD (code, ins->sreg2, ins->sreg1);
2986 ARM_MOV_REG_IMM8 (code, ins->dreg, 0);
2987 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_MI);
2988 ARM_MOV_REG_IMM8_COND (code, ins->dreg, 1, ARMCOND_VS);
2990 /* ARM FPA flags table:
2991 * N Less than ARMCOND_MI
2992 * Z Equal ARMCOND_EQ
2993 * C Greater Than or Equal ARMCOND_CS
2994 * V Unordered ARMCOND_VS
2998 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
2999 #elif defined(ARM_FPU_VFP)
3000 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3002 EMIT_COND_BRANCH (ins, CEE_BEQ - CEE_BEQ);
3006 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3007 #elif defined(ARM_FPU_VFP)
3008 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3010 EMIT_COND_BRANCH (ins, CEE_BNE_UN - CEE_BEQ);
3014 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3015 #elif defined(ARM_FPU_VFP)
3016 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3018 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
3022 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3023 #elif defined(ARM_FPU_VFP)
3024 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3026 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3027 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set */
3031 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3032 #elif defined(ARM_FPU_VFP)
3033 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3035 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set, swapped args */
3039 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3040 #elif defined(ARM_FPU_VFP)
3041 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3043 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3044 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_MI); /* N set, swapped args */
3048 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3049 #elif defined(ARM_FPU_VFP)
3050 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3052 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_CS);
3056 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg1, ins->sreg2);
3057 #elif defined(ARM_FPU_VFP)
3058 ARM_CMPD (code, ins->sreg1, ins->sreg2);
3060 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3061 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_GE);
3065 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3066 #elif defined(ARM_FPU_VFP)
3067 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3069 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_CS); /* swapped */
3073 ARM_FCMP (code, ARM_FPA_CMF, ins->sreg2, ins->sreg1);
3074 #elif defined(ARM_FPU_VFP)
3075 ARM_CMPD (code, ins->sreg2, ins->sreg1);
3077 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
3078 EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_GE); /* swapped */
3081 /*ppc_stfd (code, ins->sreg1, -8, ppc_sp);
3082 ppc_lwz (code, ppc_r11, -8, ppc_sp);
3083 ppc_rlwinm (code, ppc_r11, ppc_r11, 0, 1, 31);
3084 ppc_addis (code, ppc_r11, ppc_r11, -32752);
3085 ppc_rlwinmd (code, ppc_r11, ppc_r11, 1, 31, 31);
3086 EMIT_COND_SYSTEM_EXCEPTION (CEE_BEQ - CEE_BEQ, "ArithmeticException");*/
3087 g_assert_not_reached ();
3091 g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
3092 g_assert_not_reached ();
3095 if ((cfg->opt & MONO_OPT_BRANCH) && ((code - cfg->native_code - offset) > max_len)) {
3096 g_warning ("wrong maximal instruction length of instruction %s (expected %d, got %d)",
3097 mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset);
3098 g_assert_not_reached ();
3104 last_offset = offset;
3109 cfg->code_len = code - cfg->native_code;
3113 mono_arch_register_lowlevel_calls (void)
3117 #define patch_lis_ori(ip,val) do {\
3118 guint16 *__lis_ori = (guint16*)(ip); \
3119 __lis_ori [1] = (((guint32)(val)) >> 16) & 0xffff; \
3120 __lis_ori [3] = ((guint32)(val)) & 0xffff; \
3124 mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, gboolean run_cctors)
3126 MonoJumpInfo *patch_info;
3127 gboolean compile_aot = !run_cctors;
3129 for (patch_info = ji; patch_info; patch_info = patch_info->next) {
3130 unsigned char *ip = patch_info->ip.i + code;
3131 const unsigned char *target;
3133 if (patch_info->type == MONO_PATCH_INFO_SWITCH && !compile_aot) {
3134 gpointer *jt = (gpointer*)(ip + 8);
3136 /* jt is the inlined jump table, 2 instructions after ip
3137 * In the normal case we store the absolute addresses,
3138 * otherwise the displacements.
3140 for (i = 0; i < patch_info->data.table->table_size; i++)
3141 jt [i] = code + (int)patch_info->data.table->table [i];
3144 target = mono_resolve_patch_target (method, domain, code, patch_info, run_cctors);
3147 switch (patch_info->type) {
3148 case MONO_PATCH_INFO_BB:
3149 case MONO_PATCH_INFO_LABEL:
3152 /* No need to patch these */
3157 switch (patch_info->type) {
3158 case MONO_PATCH_INFO_IP:
3159 g_assert_not_reached ();
3160 patch_lis_ori (ip, ip);
3162 case MONO_PATCH_INFO_METHOD_REL:
3163 g_assert_not_reached ();
3164 *((gpointer *)(ip)) = code + patch_info->data.offset;
3166 case MONO_PATCH_INFO_METHODCONST:
3167 case MONO_PATCH_INFO_CLASS:
3168 case MONO_PATCH_INFO_IMAGE:
3169 case MONO_PATCH_INFO_FIELD:
3170 case MONO_PATCH_INFO_VTABLE:
3171 case MONO_PATCH_INFO_IID:
3172 case MONO_PATCH_INFO_SFLDA:
3173 case MONO_PATCH_INFO_LDSTR:
3174 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
3175 case MONO_PATCH_INFO_LDTOKEN:
3176 g_assert_not_reached ();
3177 /* from OP_AOTCONST : lis + ori */
3178 patch_lis_ori (ip, target);
3180 case MONO_PATCH_INFO_R4:
3181 case MONO_PATCH_INFO_R8:
3182 g_assert_not_reached ();
3183 *((gconstpointer *)(ip + 2)) = patch_info->data.target;
3185 case MONO_PATCH_INFO_EXC_NAME:
3186 g_assert_not_reached ();
3187 *((gconstpointer *)(ip + 1)) = patch_info->data.name;
3189 case MONO_PATCH_INFO_NONE:
3190 case MONO_PATCH_INFO_BB_OVF:
3191 case MONO_PATCH_INFO_EXC_OVF:
3192 /* everything is dealt with at epilog output time */
3197 arm_patch (ip, target);
3202 * Stack frame layout:
3204 * ------------------- fp
3205 * MonoLMF structure or saved registers
3206 * -------------------
3208 * -------------------
3210 * -------------------
3211 * optional 8 bytes for tracing
3212 * -------------------
3213 * param area size is cfg->param_area
3214 * ------------------- sp
3217 mono_arch_emit_prolog (MonoCompile *cfg)
3219 MonoMethod *method = cfg->method;
3221 MonoMethodSignature *sig;
3223 int alloc_size, pos, max_offset, i, rot_amount;
3230 if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
3233 sig = mono_method_signature (method);
3234 cfg->code_size = 256 + sig->param_count * 20;
3235 code = cfg->native_code = g_malloc (cfg->code_size);
3237 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
3239 alloc_size = cfg->stack_offset;
3242 if (!method->save_lmf) {
3243 ARM_PUSH (code, (cfg->used_int_regs | (1 << ARMREG_IP) | (1 << ARMREG_LR)));
3244 prev_sp_offset = 8; /* ip and lr */
3245 for (i = 0; i < 16; ++i) {
3246 if (cfg->used_int_regs & (1 << i))
3247 prev_sp_offset += 4;
3250 ARM_PUSH (code, 0x5ff0);
3251 prev_sp_offset = 4 * 10; /* all but r0-r3, sp and pc */
3252 pos += sizeof (MonoLMF) - prev_sp_offset;
3256 // align to MONO_ARCH_FRAME_ALIGNMENT bytes
3257 if (alloc_size & (MONO_ARCH_FRAME_ALIGNMENT - 1)) {
3258 alloc_size += MONO_ARCH_FRAME_ALIGNMENT - 1;
3259 alloc_size &= ~(MONO_ARCH_FRAME_ALIGNMENT - 1);
3262 /* the stack used in the pushed regs */
3263 if (prev_sp_offset & 4)
3265 cfg->stack_usage = alloc_size;
3267 if ((i = mono_arm_is_rotated_imm8 (alloc_size, &rot_amount)) >= 0) {
3268 ARM_SUB_REG_IMM (code, ARMREG_SP, ARMREG_SP, i, rot_amount);
3270 code = mono_arm_emit_load_imm (code, ARMREG_IP, alloc_size);
3271 ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
3274 if (cfg->frame_reg != ARMREG_SP)
3275 ARM_MOV_REG_REG (code, cfg->frame_reg, ARMREG_SP);
3276 //g_print ("prev_sp_offset: %d, alloc_size:%d\n", prev_sp_offset, alloc_size);
3277 prev_sp_offset += alloc_size;
3279 /* compute max_offset in order to use short forward jumps
3280 * we could skip do it on arm because the immediate displacement
3281 * for jumps is large enough, it may be useful later for constant pools
3284 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
3285 MonoInst *ins = bb->code;
3286 bb->max_offset = max_offset;
3288 if (cfg->prof_options & MONO_PROFILE_COVERAGE)
3292 max_offset += ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
3297 /* load arguments allocated to register from the stack */
3300 cinfo = calculate_sizes (sig, sig->pinvoke);
3302 if (MONO_TYPE_ISSTRUCT (sig->ret)) {
3303 ArgInfo *ainfo = &cinfo->ret;
3305 g_assert (arm_is_imm12 (inst->inst_offset));
3306 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3308 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
3309 ArgInfo *ainfo = cinfo->args + i;
3310 inst = cfg->args [pos];
3312 if (cfg->verbose_level > 2)
3313 g_print ("Saving argument %d (type: %d)\n", i, ainfo->regtype);
3314 if (inst->opcode == OP_REGVAR) {
3315 if (ainfo->regtype == RegTypeGeneral)
3316 ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
3317 else if (ainfo->regtype == RegTypeFP) {
3318 g_assert_not_reached ();
3319 } else if (ainfo->regtype == RegTypeBase) {
3320 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
3321 ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3323 g_assert_not_reached ();
3325 if (cfg->verbose_level > 2)
3326 g_print ("Argument %d assigned to register %s\n", pos, mono_arch_regname (inst->dreg));
3328 /* the argument should be put on the stack: FIXME handle size != word */
3329 if (ainfo->regtype == RegTypeGeneral) {
3330 switch (ainfo->size) {
3332 if (arm_is_imm12 (inst->inst_offset))
3333 ARM_STRB_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3335 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3336 ARM_STRB_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3340 if (arm_is_imm8 (inst->inst_offset)) {
3341 ARM_STRH_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3343 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3344 ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, inst->inst_basereg);
3345 ARM_STRH_IMM (code, ainfo->reg, ARMREG_IP, 0);
3349 g_assert (arm_is_imm12 (inst->inst_offset));
3350 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3351 g_assert (arm_is_imm12 (inst->inst_offset + 4));
3352 ARM_STR_IMM (code, ainfo->reg + 1, inst->inst_basereg, inst->inst_offset + 4);
3355 if (arm_is_imm12 (inst->inst_offset)) {
3356 ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
3358 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3359 ARM_STR_REG_REG (code, ainfo->reg, inst->inst_basereg, ARMREG_IP);
3363 } else if (ainfo->regtype == RegTypeBaseGen) {
3364 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
3365 g_assert (arm_is_imm12 (inst->inst_offset));
3366 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3367 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3368 ARM_STR_IMM (code, ARMREG_R3, inst->inst_basereg, inst->inst_offset);
3369 } else if (ainfo->regtype == RegTypeBase) {
3370 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
3371 switch (ainfo->size) {
3373 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3374 g_assert (arm_is_imm12 (inst->inst_offset));
3375 ARM_STRB_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3378 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3379 if (arm_is_imm8 (inst->inst_offset)) {
3380 ARM_STRH_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3382 code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset);
3383 ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, inst->inst_basereg);
3384 ARM_STRH_IMM (code, ARMREG_LR, ARMREG_IP, 0);
3388 g_assert (arm_is_imm12 (inst->inst_offset));
3389 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3390 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3391 g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset + 4));
3392 g_assert (arm_is_imm12 (inst->inst_offset + 4));
3393 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset + 4));
3394 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
3397 g_assert (arm_is_imm12 (inst->inst_offset));
3398 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
3399 ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset);
3402 } else if (ainfo->regtype == RegTypeFP) {
3403 g_assert_not_reached ();
3404 } else if (ainfo->regtype == RegTypeStructByVal) {
3405 int doffset = inst->inst_offset;
3409 if (mono_class_from_mono_type (inst->inst_vtype))
3410 size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), NULL);
3411 for (cur_reg = 0; cur_reg < ainfo->size; ++cur_reg) {
3412 g_assert (arm_is_imm12 (doffset));
3413 ARM_STR_IMM (code, ainfo->reg + cur_reg, inst->inst_basereg, doffset);
3414 soffset += sizeof (gpointer);
3415 doffset += sizeof (gpointer);
3417 if (ainfo->vtsize) {
3418 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
3419 //g_print ("emit_memcpy (prev_sp_ofs: %d, ainfo->offset: %d, soffset: %d)\n", prev_sp_offset, ainfo->offset, soffset);
3420 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, doffset, ARMREG_SP, prev_sp_offset + ainfo->offset);
3422 } else if (ainfo->regtype == RegTypeStructByAddr) {
3423 g_assert_not_reached ();
3424 /* FIXME: handle overrun! with struct sizes not multiple of 4 */
3425 code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, inst->inst_offset, ainfo->reg, 0);
3427 g_assert_not_reached ();
3432 if (method->save_lmf) {
3434 mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD,
3435 (gpointer)"mono_get_lmf_addr");
3436 code = emit_call_seq (cfg, code);
3437 /* we build the MonoLMF structure on the stack - see mini-arm.h */
3438 /* lmf_offset is the offset from the previous stack pointer,
3439 * alloc_size is the total stack space allocated, so the offset
3440 * of MonoLMF from the current stack ptr is alloc_size - lmf_offset.
3441 * The pointer to the struct is put in r1 (new_lmf).
3442 * r2 is used as scratch
3443 * The callee-saved registers are already in the MonoLMF structure
3445 code = emit_big_add (code, ARMREG_R1, ARMREG_SP, alloc_size - lmf_offset);
3446 /* r0 is the result from mono_get_lmf_addr () */
3447 ARM_STR_IMM (code, ARMREG_R0, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
3448 /* new_lmf->previous_lmf = *lmf_addr */
3449 ARM_LDR_IMM (code, ARMREG_R2, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3450 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3451 /* *(lmf_addr) = r1 */
3452 ARM_STR_IMM (code, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3453 /* save method info */
3454 code = mono_arm_emit_load_imm (code, ARMREG_R2, GPOINTER_TO_INT (method));
3455 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, method));
3456 ARM_STR_IMM (code, ARMREG_SP, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, ebp));
3457 /* save the current IP */
3458 ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_PC);
3459 ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, eip));
3463 code = mono_arch_instrument_prolog (cfg, mono_trace_enter_method, code, TRUE);
3465 cfg->code_len = code - cfg->native_code;
3466 g_assert (cfg->code_len < cfg->code_size);
3473 mono_arch_emit_epilog (MonoCompile *cfg)
3475 MonoMethod *method = cfg->method;
3476 int pos, i, rot_amount;
3477 int max_epilog_size = 16 + 20*4;
3480 if (cfg->method->save_lmf)
3481 max_epilog_size += 128;
3483 if (mono_jit_trace_calls != NULL)
3484 max_epilog_size += 50;
3486 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE)
3487 max_epilog_size += 50;
3489 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
3490 cfg->code_size *= 2;
3491 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
3492 mono_jit_stats.code_reallocs++;
3496 * Keep in sync with OP_JMP
3498 code = cfg->native_code + cfg->code_len;
3500 if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) {
3501 code = mono_arch_instrument_epilog (cfg, mono_trace_leave_method, code, TRUE);
3505 if (method->save_lmf) {
3507 /* all but r0-r3, sp and pc */
3508 pos += sizeof (MonoLMF) - (4 * 10);
3510 /* r2 contains the pointer to the current LMF */
3511 code = emit_big_add (code, ARMREG_R2, cfg->frame_reg, cfg->stack_usage - lmf_offset);
3512 /* ip = previous_lmf */
3513 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3515 ARM_LDR_IMM (code, ARMREG_LR, ARMREG_R2, G_STRUCT_OFFSET (MonoLMF, lmf_addr));
3516 /* *(lmf_addr) = previous_lmf */
3517 ARM_STR_IMM (code, ARMREG_IP, ARMREG_LR, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
3518 /* FIXME: speedup: there is no actual need to restore the registers if
3519 * we didn't actually change them (idea from Zoltan).
3522 /* point sp at the registers to restore: 10 is 14 -4, because we skip r0-r3 */
3523 ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_R2, (sizeof (MonoLMF) - 10 * sizeof (gulong)));
3524 ARM_POP_NWB (code, 0xaff0); /* restore ip to sp and lr to pc */
3526 if ((i = mono_arm_is_rotated_imm8 (cfg->stack_usage, &rot_amount)) >= 0) {
3527 ARM_ADD_REG_IMM (code, ARMREG_SP, cfg->frame_reg, i, rot_amount);
3529 code = mono_arm_emit_load_imm (code, ARMREG_IP, cfg->stack_usage);
3530 ARM_ADD_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
3532 /* FIXME: add v4 thumb interworking support */
3533 ARM_POP_NWB (code, cfg->used_int_regs | ((1 << ARMREG_SP) | (1 << ARMREG_PC)));
3536 cfg->code_len = code - cfg->native_code;
3538 g_assert (cfg->code_len < cfg->code_size);
3542 /* remove once throw_exception_by_name is eliminated */
3544 exception_id_by_name (const char *name)
3546 if (strcmp (name, "IndexOutOfRangeException") == 0)
3547 return MONO_EXC_INDEX_OUT_OF_RANGE;
3548 if (strcmp (name, "OverflowException") == 0)
3549 return MONO_EXC_OVERFLOW;
3550 if (strcmp (name, "ArithmeticException") == 0)
3551 return MONO_EXC_ARITHMETIC;
3552 if (strcmp (name, "DivideByZeroException") == 0)
3553 return MONO_EXC_DIVIDE_BY_ZERO;
3554 if (strcmp (name, "InvalidCastException") == 0)
3555 return MONO_EXC_INVALID_CAST;
3556 if (strcmp (name, "NullReferenceException") == 0)
3557 return MONO_EXC_NULL_REF;
3558 if (strcmp (name, "ArrayTypeMismatchException") == 0)
3559 return MONO_EXC_ARRAY_TYPE_MISMATCH;
3560 g_error ("Unknown intrinsic exception %s\n", name);
3565 mono_arch_emit_exceptions (MonoCompile *cfg)
3567 MonoJumpInfo *patch_info;
3570 const guint8* exc_throw_pos [MONO_EXC_INTRINS_NUM] = {NULL};
3571 guint8 exc_throw_found [MONO_EXC_INTRINS_NUM] = {0};
3572 int max_epilog_size = 50;
3574 /* count the number of exception infos */
3577 * make sure we have enough space for exceptions
3578 * 12 is the simulated call to throw_exception_by_name
3580 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
3581 if (patch_info->type == MONO_PATCH_INFO_EXC) {
3582 i = exception_id_by_name (patch_info->data.target);
3583 if (!exc_throw_found [i]) {
3584 max_epilog_size += 12;
3585 exc_throw_found [i] = TRUE;
3590 while (cfg->code_len + max_epilog_size > (cfg->code_size - 16)) {
3591 cfg->code_size *= 2;
3592 cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
3593 mono_jit_stats.code_reallocs++;
3596 code = cfg->native_code + cfg->code_len;
3598 /* add code to raise exceptions */
3599 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
3600 switch (patch_info->type) {
3601 case MONO_PATCH_INFO_EXC: {
3602 unsigned char *ip = patch_info->ip.i + cfg->native_code;
3603 const char *ex_name = patch_info->data.target;
3604 i = exception_id_by_name (patch_info->data.target);
3605 if (exc_throw_pos [i]) {
3606 arm_patch (ip, exc_throw_pos [i]);
3607 patch_info->type = MONO_PATCH_INFO_NONE;
3610 exc_throw_pos [i] = code;
3612 arm_patch (ip, code);
3613 //*(int*)code = 0xef9f0001;
3616 /*mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC_NAME, patch_info->data.target);*/
3617 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
3618 /* we got here from a conditional call, so the calling ip is set in lr already */
3619 patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD;
3620 patch_info->data.name = "mono_arch_throw_exception_by_name";
3621 patch_info->ip.i = code - cfg->native_code;
3623 *(gconstpointer*)code = ex_name;
3633 cfg->code_len = code - cfg->native_code;
3635 g_assert (cfg->code_len < cfg->code_size);
3640 mono_arch_setup_jit_tls_data (MonoJitTlsData *tls)
3645 mono_arch_free_jit_tls_data (MonoJitTlsData *tls)
3650 mono_arch_emit_this_vret_args (MonoCompile *cfg, MonoCallInst *inst, int this_reg, int this_type, int vt_reg)
3653 int this_dreg = ARMREG_R0;
3656 this_dreg = ARMREG_R1;
3658 /* add the this argument */
3659 if (this_reg != -1) {
3661 MONO_INST_NEW (cfg, this, OP_SETREG);
3662 this->type = this_type;
3663 this->sreg1 = this_reg;
3664 this->dreg = mono_regstate_next_int (cfg->rs);
3665 mono_bblock_add_inst (cfg->cbb, this);
3666 mono_call_inst_add_outarg_reg (cfg, inst, this->dreg, this_dreg, FALSE);
3671 MONO_INST_NEW (cfg, vtarg, OP_SETREG);
3672 vtarg->type = STACK_MP;
3673 vtarg->sreg1 = vt_reg;
3674 vtarg->dreg = mono_regstate_next_int (cfg->rs);
3675 mono_bblock_add_inst (cfg->cbb, vtarg);
3676 mono_call_inst_add_outarg_reg (cfg, inst, vtarg->dreg, ARMREG_R0, FALSE);
3681 mono_arch_get_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
3683 MonoInst *ins = NULL;
3684 if (cmethod->klass == mono_defaults.thread_class &&
3685 strcmp (cmethod->name, "MemoryBarrier") == 0) {
3686 MONO_INST_NEW (cfg, ins, OP_MEMORY_BARRIER);
3692 mono_arch_print_tree (MonoInst *tree, int arity)
3697 MonoInst* mono_arch_get_domain_intrinsic (MonoCompile* cfg)
3703 mono_arch_get_thread_intrinsic (MonoCompile* cfg)
3709 mono_arch_get_patch_offset (guint8 *code)
3716 mono_arch_flush_register_windows (void)
3721 mono_arch_fixup_jinfo (MonoCompile *cfg)
3723 /* max encoded stack usage is 64KB * 4 */
3724 g_assert ((cfg->stack_usage & ~(0xffff << 2)) == 0);
3725 cfg->jit_info->used_regs |= cfg->stack_usage << 14;
3728 #ifdef MONO_ARCH_HAVE_IMT
3731 mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call)
3736 mono_arch_find_imt_method (gpointer *regs, guint8 *code)
3738 guint32 *code_ptr = (guint32*)code;
3740 /* The IMT value is stored in the code stream right after the LDC instruction. */
3741 if (!IS_LDR_PC (code_ptr [0])) {
3742 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]);
3743 g_assert (IS_LDR_PC (code_ptr [0]));
3745 return (MonoMethod*) code_ptr [1];
3749 mono_arch_find_this_argument (gpointer *regs, MonoMethod *method)
3751 return mono_arch_get_this_arg_from_call (mono_method_signature (method), (gssize*)regs, NULL);
3755 #define ENABLE_WRONG_METHOD_CHECK 0
3756 #define BASE_SIZE (4 * 4)
3757 #define BSEARCH_ENTRY_SIZE (4 * 4)
3758 #define CMP_SIZE (3 * 4)
3759 #define BRANCH_SIZE (1 * 4)
3760 #define CALL_SIZE (2 * 4)
3761 #define WMC_SIZE (5 * 4)
3762 #define DISTANCE(A, B) (((gint32)(B)) - ((gint32)(A)))
3765 arm_emit_value_and_patch_ldr (arminstr_t *code, arminstr_t *target, guint32 value)
3767 guint32 delta = DISTANCE (target, code);
3769 g_assert (delta >= 0 && delta <= 0xFFF);
3770 *target = *target | delta;
3776 mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count)
3778 int size, i, extra_space = 0;
3779 arminstr_t *code, *start, *vtable_target = NULL;
3782 for (i = 0; i < count; ++i) {
3783 MonoIMTCheckItem *item = imt_entries [i];
3784 if (item->is_equals) {
3785 g_assert (arm_is_imm12 (DISTANCE (vtable, &vtable->vtable[item->vtable_slot])));
3787 if (item->check_target_idx) {
3788 if (!item->compare_done)
3789 item->chunk_size += CMP_SIZE;
3790 item->chunk_size += BRANCH_SIZE;
3792 #if ENABLE_WRONG_METHOD_CHECK
3793 item->chunk_size += WMC_SIZE;
3796 item->chunk_size += CALL_SIZE;
3798 item->chunk_size += BSEARCH_ENTRY_SIZE;
3799 imt_entries [item->check_target_idx]->compare_done = TRUE;
3801 size += item->chunk_size;
3804 start = code = mono_code_manager_reserve (domain->code_mp, size);
3807 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);
3808 for (i = 0; i < count; ++i) {
3809 MonoIMTCheckItem *item = imt_entries [i];
3810 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);
3814 ARM_PUSH2 (code, ARMREG_R0, ARMREG_R1);
3815 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, -4);
3816 vtable_target = code;
3817 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
3819 for (i = 0; i < count; ++i) {
3820 MonoIMTCheckItem *item = imt_entries [i];
3821 arminstr_t *imt_method = NULL;
3822 item->code_target = (guint8*)code;
3824 if (item->is_equals) {
3825 if (item->check_target_idx) {
3826 if (!item->compare_done) {
3828 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
3829 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
3831 item->jmp_code = (guint8*)code;
3832 ARM_B_COND (code, ARMCOND_NE, 0);
3834 ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
3835 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, DISTANCE (vtable, &vtable->vtable[item->vtable_slot]));
3837 /*Enable the commented code to assert on wrong method*/
3838 #if ENABLE_WRONG_METHOD_CHECK
3840 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
3841 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
3842 ARM_B_COND (code, ARMCOND_NE, 1);
3844 ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
3845 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, DISTANCE (vtable, &vtable->vtable[item->vtable_slot]));
3847 #if ENABLE_WRONG_METHOD_CHECK
3853 code = arm_emit_value_and_patch_ldr (code, imt_method, (guint32)item->method);
3855 /*must emit after unconditional branch*/
3856 if (vtable_target) {
3857 code = arm_emit_value_and_patch_ldr (code, vtable_target, (guint32)vtable);
3858 item->chunk_size += 4;
3859 vtable_target = NULL;
3862 /*We reserve the space for bsearch IMT values after the first entry with an absolute jump*/
3864 code += extra_space;
3868 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
3869 ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
3871 item->jmp_code = (guint8*)code;
3872 ARM_B_COND (code, ARMCOND_GE, 0);
3877 for (i = 0; i < count; ++i) {
3878 MonoIMTCheckItem *item = imt_entries [i];
3879 if (item->jmp_code) {
3880 if (item->check_target_idx)
3881 arm_patch (item->jmp_code, imt_entries [item->check_target_idx]->code_target);
3883 if (i > 0 && item->is_equals) {
3885 arminstr_t *space_start = (arminstr_t*)(item->code_target + item->chunk_size);
3886 for (j = i - 1; j >= 0 && !imt_entries [j]->is_equals; --j) {
3887 space_start = arm_emit_value_and_patch_ldr (space_start, (arminstr_t*)imt_entries [j]->code_target, (guint32)imt_entries [j]->method);
3894 char *buff = g_strdup_printf ("thunk_for_class_%s_%s_entries_%d", vtable->klass->name_space, vtable->klass->name, count);
3895 mono_disassemble_code (NULL, (guint8*)start, size, buff);
3900 mono_arch_flush_icache ((guint8*)start, size);
3901 mono_stats.imt_thunks_size += code - start;
3903 g_assert (DISTANCE (start, code) <= size);