* Exception.cs: Cosmetic change to default exception message on
[mono.git] / mono / mini / mini-arm.c
index b56db4a2fd052649bc3ae0792f20dd07611ed126..3a194e370f1cbb643951cae50a75b0e2a12487aa 100644 (file)
 #include "mono/arch/arm/arm-vfp-codegen.h"
 #endif
 
+/* This mutex protects architecture specific caches */
+#define mono_mini_arch_lock() EnterCriticalSection (&mini_arch_mutex)
+#define mono_mini_arch_unlock() LeaveCriticalSection (&mini_arch_mutex)
+static CRITICAL_SECTION mini_arch_mutex;
+
 static int v5_supported = 0;
 static int thumb_supported = 0;
 
@@ -48,6 +53,14 @@ int mono_exc_esp_offset = 0;
 #define arm_is_imm8(v) ((v) > -256 && (v) < 256)
 #define arm_is_fpimm8(v) ((v) >= -1020 && (v) <= 1020)
 
+#define LDR_MASK ((0xf << ARMCOND_SHIFT) | (3 << 26) | (1 << 22) | (1 << 20) | (15 << 12))
+#define LDR_PC_VAL ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 26) | (0 << 22) | (1 << 20) | (15 << 12))
+#define IS_LDR_PC(val) (((val) & LDR_MASK) == LDR_PC_VAL)
+
+#define ADD_LR_PC_4 ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 25) | (1 << 23) | (ARMREG_PC << 16) | (ARMREG_LR << 12) | 4)
+#define MOV_LR_PC ((ARMCOND_AL << ARMCOND_SHIFT) | (1 << 24) | (0xa << 20) |  (ARMREG_LR << 12) | ARMREG_PC)
+#define DEBUG_IMT 0
+
 const char*
 mono_arch_regname (int reg) {
        static const char * rnames[] = {
@@ -149,6 +162,21 @@ emit_call_reg (guint8 *code, int reg)
        return code;
 }
 
+static guint8*
+emit_call_seq (MonoCompile *cfg, guint8 *code)
+{
+       if (cfg->method->dynamic) {
+               ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
+               ARM_B (code, 0);
+               *(gpointer*)code = NULL;
+               code += 4;
+               code = emit_call_reg (code, ARMREG_IP);
+       } else {
+               ARM_BL (code, 0);
+       }
+       return code;
+}
+
 /*
  * mono_arch_get_argument_info:
  * @csig:  a method signature
@@ -186,7 +214,7 @@ mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJit
                if (csig->pinvoke)
                        size = mono_type_native_stack_size (csig->params [k], &align);
                else
-                       size = mono_type_stack_size (csig->params [k], &align);
+                       size = mini_type_stack_size (NULL, csig->params [k], &align);
 
                /* ignore alignment for now */
                align = 1;
@@ -208,6 +236,162 @@ mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJit
        return frame_size;
 }
 
+static gpointer*
+decode_vcall_slot_from_ldr (guint32 ldr, gpointer *regs)
+{
+       char *o = NULL;
+       int reg, offset = 0;
+       reg = (ldr >> 16 ) & 0xf;
+       offset = ldr & 0xfff;
+       if (((ldr >> 23) & 1) == 0) /*U bit, 0 means negative and 1 positive*/
+               offset = -offset;
+       /*g_print ("found vcall at r%d + %d for code at %p 0x%x\n", reg, offset, code, *code);*/
+       o = regs [reg];
+       return (gpointer*)(o + offset);
+}
+
+gpointer*
+mono_arch_get_vcall_slot_addr (guint8 *code_ptr, gpointer *regs)
+{
+       guint32* code = (guint32*)code_ptr;
+
+       /* Locate the address of the method-specific trampoline. The call using
+       the vtable slot that took the processing flow to 'arch_create_jit_trampoline' 
+       looks something like this:
+
+               ldr rA, rX, #offset
+               mov lr, pc
+               mov pc, rA
+       or better:
+               mov lr, pc
+               ldr pc, rX, #offset
+
+       The call sequence could be also:
+               ldr ip, pc, 0
+               b skip
+               function pointer literal
+               skip:
+               mov lr, pc
+               mov pc, ip
+       Note that on ARM5+ we can use one instruction instead of the last two.
+       Therefore, we need to locate the 'ldr rA' instruction to know which
+       register was used to hold the method addrs.
+       */
+
+       /* This is the instruction after "ldc pc, xxx", "mov pc, xxx" or "bl xxx" could be either the IMT value or some other instruction*/
+       --code;
+
+       /* Three possible code sequences can happen here:
+        * interface call:
+        * 
+        * add lr, [pc + #4]
+        * ldr pc, [rX - #offset]
+        * .word IMT value
+        * 
+        * virtual call:
+        * 
+        * mov lr, pc
+        * ldr pc, [rX - #offset] 
+        * 
+        * direct branch with bl:
+        * 
+        * bl #offset
+        * 
+        * direct branch with mov: 
+        * 
+        * mv pc, rX
+        * 
+        * We only need to identify interface and virtual calls, the others can be ignored.
+        * 
+        */
+       if (IS_LDR_PC (code [-1]) && code [-2] == ADD_LR_PC_4)
+               return decode_vcall_slot_from_ldr (code [-1], regs);
+
+       if (IS_LDR_PC (code [0]) && code [-1] == MOV_LR_PC)
+               return decode_vcall_slot_from_ldr (code [0], regs);
+
+       return NULL;
+}
+
+#define MAX_ARCH_DELEGATE_PARAMS 3
+
+gpointer
+mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target)
+{
+       guint8 *code, *start;
+
+       /* FIXME: Support more cases */
+       if (MONO_TYPE_ISSTRUCT (sig->ret))
+               return NULL;
+
+       if (has_target) {
+               static guint8* cached = NULL;
+               mono_mini_arch_lock ();
+               if (cached) {
+                       mono_mini_arch_unlock ();
+                       return cached;
+               }
+               
+               start = code = mono_global_codeman_reserve (12);
+
+               /* Replace the this argument with the target */
+               ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, method_ptr));
+               ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, target));
+               ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
+
+               g_assert ((code - start) <= 12);
+
+               mono_arch_flush_icache (code, 12);
+               cached = start;
+               mono_mini_arch_unlock ();
+               return cached;
+       } else {
+               static guint8* cache [MAX_ARCH_DELEGATE_PARAMS + 1] = {NULL};
+               int size, i;
+
+               if (sig->param_count > MAX_ARCH_DELEGATE_PARAMS)
+                       return NULL;
+               for (i = 0; i < sig->param_count; ++i)
+                       if (!mono_is_regsize_var (sig->params [i]))
+                               return NULL;
+
+               mono_mini_arch_lock ();
+               code = cache [sig->param_count];
+               if (code) {
+                       mono_mini_arch_unlock ();
+                       return code;
+               }
+
+               size = 8 + sig->param_count * 4;
+               start = code = mono_global_codeman_reserve (size);
+
+               ARM_LDR_IMM (code, ARMREG_IP, ARMREG_R0, G_STRUCT_OFFSET (MonoDelegate, method_ptr));
+               /* slide down the arguments */
+               for (i = 0; i < sig->param_count; ++i) {
+                       ARM_MOV_REG_REG (code, (ARMREG_R0 + i), (ARMREG_R0 + i + 1));
+               }
+               ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
+
+               g_assert ((code - start) <= size);
+
+               mono_arch_flush_icache (code, size);
+               cache [sig->param_count] = start;
+               mono_mini_arch_unlock ();
+               return start;
+       }
+
+       return NULL;
+}
+
+gpointer
+mono_arch_get_this_arg_from_call (MonoMethodSignature *sig, gssize *regs, guint8 *code)
+{
+       /* FIXME: handle returning a struct */
+       if (MONO_TYPE_ISSTRUCT (sig->ret))
+               return (gpointer)regs [ARMREG_R1];
+       return (gpointer)regs [ARMREG_R0];
+}
+
 /*
  * Initialize the cpu to execute managed code.
  */
@@ -216,6 +400,23 @@ mono_arch_cpu_init (void)
 {
 }
 
+/*
+ * Initialize architecture specific code.
+ */
+void
+mono_arch_init (void)
+{
+       InitializeCriticalSection (&mini_arch_mutex);   
+}
+
+/*
+ * Cleanup architecture specific code.
+ */
+void
+mono_arch_cleanup (void)
+{
+}
+
 /*
  * This function returns the optimizations supported on this cpu.
  */
@@ -399,22 +600,30 @@ add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple)
                        ainfo->reg = *gr;
                }
        } else {
-               if (*gr == ARMREG_R3) {
+               if (*gr == ARMREG_R3
+#ifdef __ARM_EABI__
+                               && 0
+#endif
+                                       ) {
                        /* first word in r3 and the second on the stack */
                        ainfo->offset = *stack_size;
                        ainfo->reg = ARMREG_SP; /* in the caller */
                        ainfo->regtype = RegTypeBaseGen;
                        *stack_size += 4;
                } else if (*gr > ARMREG_R3) {
-                       /**stack_size += 7;
-                       *stack_size &= ~7;*/
+#ifdef __ARM_EABI__
+                       *stack_size += 7;
+                       *stack_size &= ~7;
+#endif
                        ainfo->offset = *stack_size;
                        ainfo->reg = ARMREG_SP; /* in the caller */
                        ainfo->regtype = RegTypeBase;
                        *stack_size += 8;
                } else {
-                       /*if ((*gr) & 1)
-                               (*gr) ++;*/
+#ifdef __ARM_EABI__
+                       if ((*gr) & 1)
+                               (*gr) ++;
+#endif
                        ainfo->reg = *gr;
                }
                (*gr) ++;
@@ -738,7 +947,7 @@ mono_arch_allocate_vars (MonoCompile *m)
 
        curinst = 0;
        if (sig->hasthis) {
-               inst = m->varinfo [curinst];
+               inst = m->args [curinst];
                if (inst->opcode != OP_REGVAR) {
                        inst->opcode = OP_REGOFFSET;
                        inst->inst_basereg = frame_reg;
@@ -753,7 +962,7 @@ mono_arch_allocate_vars (MonoCompile *m)
        }
 
        for (i = 0; i < sig->param_count; ++i) {
-               inst = m->varinfo [curinst];
+               inst = m->args [curinst];
                if (inst->opcode != OP_REGVAR) {
                        inst->opcode = OP_REGOFFSET;
                        inst->inst_basereg = frame_reg;
@@ -1173,15 +1382,8 @@ peephole_pass (MonoCompile *cfg, MonoBasicBlock *bb)
                        if (last_ins && (last_ins->opcode == OP_STOREI1_MEMBASE_REG) &&
                                        ins->inst_basereg == last_ins->inst_destbasereg &&
                                        ins->inst_offset == last_ins->inst_offset) {
-                               if (ins->dreg == last_ins->sreg1) {
-                                       last_ins->next = ins->next;                             
-                                       ins = ins->next;                                
-                                       continue;
-                               } else {
-                                       //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
-                                       ins->opcode = OP_MOVE;
-                                       ins->sreg1 = last_ins->sreg1;
-                               }
+                               ins->opcode = (ins->opcode == OP_LOADI1_MEMBASE) ? CEE_CONV_I1 : CEE_CONV_U1;
+                               ins->sreg1 = last_ins->sreg1;                           
                        }
                        break;
                case OP_LOADU2_MEMBASE:
@@ -1189,15 +1391,8 @@ peephole_pass (MonoCompile *cfg, MonoBasicBlock *bb)
                        if (last_ins && (last_ins->opcode == OP_STOREI2_MEMBASE_REG) &&
                                        ins->inst_basereg == last_ins->inst_destbasereg &&
                                        ins->inst_offset == last_ins->inst_offset) {
-                               if (ins->dreg == last_ins->sreg1) {
-                                       last_ins->next = ins->next;                             
-                                       ins = ins->next;                                
-                                       continue;
-                               } else {
-                                       //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
-                                       ins->opcode = OP_MOVE;
-                                       ins->sreg1 = last_ins->sreg1;
-                               }
+                               ins->opcode = (ins->opcode == OP_LOADI2_MEMBASE) ? CEE_CONV_I2 : CEE_CONV_U2;
+                               ins->sreg1 = last_ins->sreg1;                           
                        }
                        break;
                case CEE_CONV_I4:
@@ -1356,8 +1551,8 @@ mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb)
        int rot_amount, imm8, low_imm;
 
        /* setup the virtual reg allocator */
-       if (bb->max_ireg > cfg->rs->next_vireg)
-               cfg->rs->next_vireg = bb->max_ireg;
+       if (bb->max_vreg > cfg->rs->next_vreg)
+               cfg->rs->next_vreg = bb->max_vreg;
 
        ins = bb->code;
        while (ins) {
@@ -1502,7 +1697,7 @@ loop_start:
                ins = ins->next;
        }
        bb->last_ins = last_ins;
-       bb->max_ireg = cfg->rs->next_vireg;
+       bb->max_vreg = cfg->rs->next_vreg;
 
 }
 
@@ -1589,6 +1784,9 @@ search_thunk_slot (void *data, int csize, int bsize, void *user_data) {
                                return 1;
                        } else if ((thunks [0] == 0) && (thunks [1] == 0) && (thunks [2] == 0)) {
                                /* found a free slot instead: emit thunk */
+                               /* ARMREG_IP is fine to use since this can't be an IMT call
+                                * which is indirect
+                                */
                                code = (guchar*)thunks;
                                ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
                                if (thumb_supported)
@@ -1844,7 +2042,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
        while (ins) {
                offset = code - cfg->native_code;
 
-               max_len = ((guint8 *)arm_cpu_desc [ins->opcode])[MONO_INST_LEN];
+               max_len = ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
 
                if (offset > (cfg->code_size - max_len - 16)) {
                        cfg->code_size *= 2;
@@ -1992,7 +2190,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        g_assert (imm8 >= 0);
                        ARM_CMP_REG_IMM (code, ins->sreg1, imm8, rot_amount);
                        break;
-               case CEE_BREAK:
+               case OP_BREAK:
                        *(int*)code = 0xe7f001f0;
                        *(int*)code = 0xef9f0001;
                        code += 4;
@@ -2207,7 +2405,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        ARM_CVTS (code, ins->dreg, ins->dreg);
 #endif
                        break;
-               case CEE_JMP:
+               case OP_JMP:
                        /*
                         * Keep in sync with mono_arch_emit_epilog
                         */
@@ -2243,15 +2441,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                                mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, call->method);
                        else
                                mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, call->fptr);
-                       if (cfg->method->dynamic) {
-                               ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
-                               ARM_B (code, 0);
-                               *(gpointer*)code = NULL;
-                               code += 4;
-                               code = emit_call_reg (code, ARMREG_IP);
-                       } else {
-                               ARM_BL (code, 0);
-                       }
+                       code = emit_call_seq (cfg, code);
                        break;
                case OP_FCALL_REG:
                case OP_LCALL_REG:
@@ -2267,8 +2457,16 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                case OP_CALL_MEMBASE:
                        g_assert (arm_is_imm12 (ins->inst_offset));
                        g_assert (ins->sreg1 != ARMREG_LR);
-                       ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
-                       ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
+                       call = (MonoCallInst*)ins;
+                       if (call->method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
+                               ARM_ADD_REG_IMM8 (code, ARMREG_LR, ARMREG_PC, 4);
+                               ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
+                               *((gpointer*)code) = (gpointer)call->method;
+                               code += 4;
+                       } else {
+                               ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
+                               ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
+                       }
                        break;
                case OP_OUTARG:
                        g_assert_not_reached ();
@@ -2304,20 +2502,12 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        g_assert_not_reached ();
                        ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_LR);
                        break;
-               case CEE_THROW: {
+               case OP_THROW: {
                        if (ins->sreg1 != ARMREG_R0)
                                ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
                        mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, 
                                             (gpointer)"mono_arch_throw_exception");
-                       if (cfg->method->dynamic) {
-                               ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
-                               ARM_B (code, 0);
-                               *(gpointer*)code = NULL;
-                               code += 4;
-                               code = emit_call_reg (code, ARMREG_IP);
-                       } else {
-                               ARM_BL (code, 0);
-                       }
+                       code = emit_call_seq (cfg, code);
                        break;
                }
                case OP_RETHROW: {
@@ -2325,15 +2515,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                                ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
                        mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, 
                                             (gpointer)"mono_arch_rethrow_exception");
-                       if (cfg->method->dynamic) {
-                               ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
-                               ARM_B (code, 0);
-                               *(gpointer*)code = NULL;
-                               code += 4;
-                               code = emit_call_reg (code, ARMREG_IP);
-                       } else {
-                               ARM_BL (code, 0);
-                       }
+                       code = emit_call_seq (cfg, code);
                        break;
                }
                case OP_START_HANDLER:
@@ -2356,7 +2538,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        }
                        ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
                        break;
-               case CEE_ENDFINALLY:
+               case OP_ENDFINALLY:
                        if (arm_is_imm12 (ins->inst_left->inst_offset)) {
                                ARM_LDR_IMM (code, ARMREG_IP, ins->inst_left->inst_basereg, ins->inst_left->inst_offset);
                        } else {
@@ -2373,7 +2555,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                case OP_LABEL:
                        ins->inst_c0 = code - cfg->native_code;
                        break;
-               case CEE_BR:
+               case OP_BR:
                        if (ins->flags & MONO_INST_BRLABEL) {
                                /*if (ins->inst_i0->inst_c0) {
                                        ARM_B (code, 0);
@@ -2810,7 +2992,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_VS); /* V set */
                        EMIT_COND_BRANCH_FLAGS (ins, ARMCOND_GE); /* swapped */
                        break;
-               case CEE_CKFINITE: {
+               case OP_CKFINITE: {
                        /*ppc_stfd (code, ins->sreg1, -8, ppc_sp);
                        ppc_lwz (code, ppc_r11, -8, ppc_sp);
                        ppc_rlwinm (code, ppc_r11, ppc_r11, 0, 1, 31);
@@ -3011,7 +3193,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                        max_offset += 6; 
 
                while (ins) {
-                       max_offset += ((guint8 *)arm_cpu_desc [ins->opcode])[MONO_INST_LEN];
+                       max_offset += ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
                        ins = ins->next;
                }
        }
@@ -3029,7 +3211,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
        }
        for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
                ArgInfo *ainfo = cinfo->args + i;
-               inst = cfg->varinfo [pos];
+               inst = cfg->args [pos];
                
                if (cfg->verbose_level > 2)
                        g_print ("Saving argument %d (type: %d)\n", i, ainfo->regtype);
@@ -3155,15 +3337,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
 
                mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, 
                             (gpointer)"mono_get_lmf_addr");
-               if (cfg->method->dynamic) {
-                       ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
-                       ARM_B (code, 0);
-                       *(gpointer*)code = NULL;
-                       code += 4;
-                       code = emit_call_reg (code, ARMREG_IP);
-               } else {
-                       ARM_BL (code, 0);
-               }
+               code = emit_call_seq (cfg, code);
                /* we build the MonoLMF structure on the stack - see mini-arm.h */
                /* lmf_offset is the offset from the previous stack pointer,
                 * alloc_size is the total stack space allocated, so the offset
@@ -3223,7 +3397,7 @@ mono_arch_emit_epilog (MonoCompile *cfg)
        }
 
        /*
-        * Keep in sync with CEE_JMP
+        * Keep in sync with OP_JMP
         */
        code = cfg->native_code + cfg->code_len;
 
@@ -3447,3 +3621,185 @@ mono_arch_fixup_jinfo (MonoCompile *cfg)
        cfg->jit_info->used_regs |= cfg->stack_usage << 14;
 }
 
+#ifdef MONO_ARCH_HAVE_IMT
+
+void
+mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call)
+{
+}
+
+MonoMethod*
+mono_arch_find_imt_method (gpointer *regs, guint8 *code)
+{
+       guint32 *code_ptr = (guint32*)code;
+       code_ptr -= 2;
+       /* The IMT value is stored in the code stream right after the LDC instruction. */
+       if (!IS_LDR_PC (code_ptr [0])) {
+               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]);
+               g_assert (IS_LDR_PC (code_ptr [0]));
+       }
+       return (MonoMethod*) code_ptr [1];
+}
+
+MonoObject*
+mono_arch_find_this_argument (gpointer *regs, MonoMethod *method)
+{
+       return mono_arch_get_this_arg_from_call (mono_method_signature (method), (gssize*)regs, NULL);
+}
+
+
+#define ENABLE_WRONG_METHOD_CHECK 0
+#define BASE_SIZE (4 * 4)
+#define BSEARCH_ENTRY_SIZE (4 * 4)
+#define CMP_SIZE (3 * 4)
+#define BRANCH_SIZE (1 * 4)
+#define CALL_SIZE (2 * 4)
+#define WMC_SIZE (5 * 4)
+#define DISTANCE(A, B) (((gint32)(B)) - ((gint32)(A)))
+
+static arminstr_t *
+arm_emit_value_and_patch_ldr (arminstr_t *code, arminstr_t *target, guint32 value)
+{
+       guint32 delta = DISTANCE (target, code);
+       delta -= 8;
+       g_assert (delta >= 0 && delta <= 0xFFF);
+       *target = *target | delta;
+       *code = value;
+       return code + 1;
+}
+
+gpointer
+mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count)
+{
+       int size, i, extra_space = 0;
+       arminstr_t *code, *start, *vtable_target = NULL;
+       size = BASE_SIZE;
+
+       for (i = 0; i < count; ++i) {
+               MonoIMTCheckItem *item = imt_entries [i];
+               if (item->is_equals) {
+                       g_assert (arm_is_imm12 (DISTANCE (vtable, &vtable->vtable[item->vtable_slot])));
+
+                       if (item->check_target_idx) {
+                               if (!item->compare_done)
+                                       item->chunk_size += CMP_SIZE;
+                               item->chunk_size += BRANCH_SIZE;
+                       } else {
+#if ENABLE_WRONG_METHOD_CHECK
+                               item->chunk_size += WMC_SIZE;
+#endif
+                       }
+                       item->chunk_size += CALL_SIZE;
+               } else {
+                       item->chunk_size += BSEARCH_ENTRY_SIZE;
+                       imt_entries [item->check_target_idx]->compare_done = TRUE;
+               }
+               size += item->chunk_size;
+       }
+
+       start = code = mono_code_manager_reserve (domain->code_mp, size);
+
+#if DEBUG_IMT
+       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);
+       for (i = 0; i < count; ++i) {
+               MonoIMTCheckItem *item = imt_entries [i];
+               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);
+       }
+#endif
+
+       ARM_PUSH2 (code, ARMREG_R0, ARMREG_R1);
+       ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, -4);
+       vtable_target = code;
+       ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
+
+       for (i = 0; i < count; ++i) {
+               MonoIMTCheckItem *item = imt_entries [i];
+               arminstr_t *imt_method = NULL;
+               item->code_target = (guint8*)code;
+
+               if (item->is_equals) {
+                       if (item->check_target_idx) {
+                               if (!item->compare_done) {
+                                       imt_method = code;
+                                       ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
+                                       ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
+                               }
+                               item->jmp_code = (guint8*)code;
+                               ARM_B_COND (code, ARMCOND_NE, 0);
+
+                               ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
+                               ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, DISTANCE (vtable, &vtable->vtable[item->vtable_slot]));
+                       } else {
+                               /*Enable the commented code to assert on wrong method*/
+#if ENABLE_WRONG_METHOD_CHECK
+                               imt_method = code;
+                               ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
+                               ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
+                               ARM_B_COND (code, ARMCOND_NE, 1);
+#endif
+                               ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
+                               ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, DISTANCE (vtable, &vtable->vtable[item->vtable_slot]));
+
+#if ENABLE_WRONG_METHOD_CHECK
+                               ARM_DBRK (code);
+#endif
+                       }
+
+                       if (imt_method)
+                               code = arm_emit_value_and_patch_ldr (code, imt_method, (guint32)item->method);
+
+                       /*must emit after unconditional branch*/
+                       if (vtable_target) {
+                               code = arm_emit_value_and_patch_ldr (code, vtable_target, (guint32)vtable);
+                               item->chunk_size += 4;
+                               vtable_target = NULL;
+                       }
+
+                       /*We reserve the space for bsearch IMT values after the first entry with an absolute jump*/
+                       if (extra_space) {
+                               code += extra_space;
+                               extra_space = 0;
+                       }
+               } else {
+                       ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
+                       ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
+
+                       item->jmp_code = (guint8*)code;
+                       ARM_B_COND (code, ARMCOND_GE, 0);
+                       ++extra_space;
+               }
+       }
+
+       for (i = 0; i < count; ++i) {
+               MonoIMTCheckItem *item = imt_entries [i];
+               if (item->jmp_code) {
+                       if (item->check_target_idx)
+                               arm_patch (item->jmp_code, imt_entries [item->check_target_idx]->code_target);
+               }
+               if (i > 0 && item->is_equals) {
+                       int j;
+                       arminstr_t *space_start = (arminstr_t*)(item->code_target + item->chunk_size);
+                       for (j = i - 1; j >= 0 && !imt_entries [j]->is_equals; --j) {
+                               space_start = arm_emit_value_and_patch_ldr (space_start, (arminstr_t*)imt_entries [j]->code_target, (guint32)imt_entries [j]->method);
+                       }
+               }
+       }
+
+#if DEBUG_IMT
+       {
+               char *buff = g_strdup_printf ("thunk_for_class_%s_%s_entries_%d", vtable->klass->name_space, vtable->klass->name, count);
+               mono_disassemble_code (NULL, (guint8*)start, size, buff);
+               g_free (buff);
+       }
+#endif
+
+       mono_arch_flush_icache ((guint8*)start, size);
+       mono_stats.imt_thunks_size += code - start;
+
+       g_assert (DISTANCE (start, code) <= size);
+       return start;
+}
+
+#endif
+
+