Remove the MonoGenericContext/MonoMethod arguments from the get_this_arg () and get_u...
[mono.git] / mono / mini / mini-arm.c
index 5e3654f86c2c6ca87217531b23371a43fab9f112..0454997f72cf09aa6a2f2e6ad4601630b49ece00 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <mono/metadata/appdomain.h>
 #include <mono/metadata/debug-helpers.h>
+#include <mono/utils/mono-mmap.h>
 
 #include "mini-arm.h"
 #include "cpu-arm.h"
@@ -36,8 +37,25 @@ static gint lmf_addr_tls_offset = -1;
 static CRITICAL_SECTION mini_arch_mutex;
 
 static int v5_supported = 0;
+static int v7_supported = 0;
 static int thumb_supported = 0;
 
+/*
+ * The code generated for sequence points reads from this location, which is
+ * made read-only when single stepping is enabled.
+ */
+static gpointer ss_trigger_page;
+
+/* Enabled breakpoints read from this trigger page */
+static gpointer bp_trigger_page;
+
+/* Structure used by the sequence points in AOTed code */
+typedef struct {
+       gpointer ss_trigger_page;
+       gpointer bp_trigger_page;
+       guint8* bp_addrs [MONO_ZERO_LEN_ARRAY];
+} SeqPointInfo;
+
 /*
  * TODO:
  * floating point support: on ARM it is a mess, there are at least 3
@@ -65,6 +83,27 @@ int mono_exc_esp_offset = 0;
 #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
+/* A variant of ARM_LDR_IMM which can handle large offsets */
+#define ARM_LDR_IMM_GENERAL(code, dreg, basereg, offset, scratch_reg) do { \
+       if (arm_is_imm12 ((offset))) { \
+               ARM_LDR_IMM (code, (dreg), (basereg), (offset));        \
+       } else {                                                                                                \
+               g_assert ((scratch_reg) != (basereg));                                     \
+               code = mono_arm_emit_load_imm (code, (scratch_reg), (offset));  \
+               ARM_LDR_REG_REG (code, (dreg), (basereg), (scratch_reg));               \
+       }                                                                                                                                       \
+       } while (0)
+
+#define ARM_STR_IMM_GENERAL(code, dreg, basereg, offset, scratch_reg) do {     \
+       if (arm_is_imm12 ((offset))) { \
+               ARM_STR_IMM (code, (dreg), (basereg), (offset));        \
+       } else {                                                                                                \
+               g_assert ((scratch_reg) != (basereg));                                     \
+               code = mono_arm_emit_load_imm (code, (scratch_reg), (offset));  \
+               ARM_STR_REG_REG (code, (dreg), (basereg), (scratch_reg));               \
+       }                                                                                                                                       \
+       } while (0)
 
 const char*
 mono_arch_regname (int reg)
@@ -97,6 +136,8 @@ mono_arch_fregname (int reg)
        return "unknown";
 }
 
+#ifndef DISABLE_JIT
+
 static guint8*
 emit_big_add (guint8 *code, int dreg, int sreg, int imm)
 {
@@ -208,6 +249,8 @@ emit_move_return_value (MonoCompile *cfg, MonoInst *ins, guint8 *code)
        return code;
 }
 
+#endif /* #ifndef DISABLE_JIT */
+
 /*
  * mono_arch_get_argument_info:
  * @csig:  a method signature
@@ -263,85 +306,6 @@ mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJit
        return frame_size;
 }
 
-static gpointer
-decode_vcall_slot_from_ldr (guint32 ldr, mgreg_t *regs, int *displacement)
-{
-       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];
-
-       *displacement = offset;
-       return o;
-}
-
-gpointer
-mono_arch_get_vcall_slot (guint8 *code_ptr, mgreg_t *regs, int *displacement)
-{
-       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, displacement);
-
-       if (IS_LDR_PC (code [0]) && code [-1] == MOV_LR_PC)
-               return decode_vcall_slot_from_ldr (code [0], regs, displacement);
-
-       return NULL;
-}
-
 #define MAX_ARCH_DELEGATE_PARAMS 3
 
 static gpointer
@@ -399,11 +363,11 @@ mono_arch_get_delegate_invoke_impls (void)
        int i;
 
        code = get_delegate_invoke_impl (TRUE, 0, &code_len);
-       res = g_slist_prepend (res, mono_aot_tramp_info_create (g_strdup ("delegate_invoke_impl_has_target"), code, code_len));
+       res = g_slist_prepend (res, mono_tramp_info_create (g_strdup ("delegate_invoke_impl_has_target"), code, code_len, NULL, NULL));
 
-       for (i = 0; i < MAX_ARCH_DELEGATE_PARAMS; ++i) {
+       for (i = 0; i <= MAX_ARCH_DELEGATE_PARAMS; ++i) {
                code = get_delegate_invoke_impl (FALSE, i, &code_len);
-               res = g_slist_prepend (res, mono_aot_tramp_info_create (g_strdup_printf ("delegate_invoke_impl_target_%d", i), code, code_len));
+               res = g_slist_prepend (res, mono_tramp_info_create (g_strdup_printf ("delegate_invoke_impl_target_%d", i), code, code_len, NULL, NULL));
        }
 
        return res;
@@ -427,7 +391,7 @@ mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_targe
                }
 
                if (mono_aot_only)
-                       start = mono_aot_get_named_code ("delegate_invoke_impl_has_target");
+                       start = mono_aot_get_trampoline ("delegate_invoke_impl_has_target");
                else
                        start = get_delegate_invoke_impl (TRUE, 0, NULL);
                cached = start;
@@ -452,7 +416,7 @@ mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_targe
 
                if (mono_aot_only) {
                        char *name = g_strdup_printf ("delegate_invoke_impl_target_%d", sig->param_count);
-                       start = mono_aot_get_named_code (name);
+                       start = mono_aot_get_trampoline (name);
                        g_free (name);
                } else {
                        start = get_delegate_invoke_impl (FALSE, sig->param_count, NULL);
@@ -466,11 +430,8 @@ mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_targe
 }
 
 gpointer
-mono_arch_get_this_arg_from_call (MonoGenericSharingContext *gsctx, MonoMethodSignature *sig, mgreg_t *regs, guint8 *code)
+mono_arch_get_this_arg_from_call (mgreg_t *regs, guint8 *code)
 {
-       /* FIXME: handle returning a struct */
-       if (MONO_TYPE_ISSTRUCT (sig->ret))
-               return (gpointer)regs [ARMREG_R1];
        return (gpointer)regs [ARMREG_R0];
 }
 
@@ -488,7 +449,14 @@ mono_arch_cpu_init (void)
 void
 mono_arch_init (void)
 {
-       InitializeCriticalSection (&mini_arch_mutex);   
+       InitializeCriticalSection (&mini_arch_mutex);
+
+       ss_trigger_page = mono_valloc (NULL, mono_pagesize (), MONO_MMAP_READ|MONO_MMAP_32BIT);
+       bp_trigger_page = mono_valloc (NULL, mono_pagesize (), MONO_MMAP_READ|MONO_MMAP_32BIT);
+       mono_mprotect (bp_trigger_page, mono_pagesize (), 0);
+
+       mono_aot_register_jit_icall ("mono_arm_throw_exception", mono_arm_throw_exception);
+       mono_aot_register_jit_icall ("mono_arm_throw_exception_by_token", mono_arm_throw_exception_by_token);
 }
 
 /*
@@ -506,6 +474,14 @@ guint32
 mono_arch_cpu_optimizazions (guint32 *exclude_mask)
 {
        guint32 opts = 0;
+       const char *cpu_arch = getenv ("MONO_CPU_ARCH");
+       if (cpu_arch != NULL) {
+               thumb_supported = strstr (cpu_arch, "thumb") != NULL;
+               if (strncmp (cpu_arch, "armv", 4) == 0) {
+                       v5_supported = cpu_arch [4] >= '5';
+                       v7_supported = cpu_arch [4] >= '7';
+               }
+       } else {
 #if __APPLE__
        thumb_supported = TRUE;
        v5_supported = TRUE;
@@ -517,9 +493,10 @@ mono_arch_cpu_optimizazions (guint32 *exclude_mask)
                while ((line = fgets (buf, 512, file))) {
                        if (strncmp (line, "Processor", 9) == 0) {
                                char *ver = strstr (line, "(v");
-                               if (ver && (ver [2] == '5' || ver [2] == '6' || ver [2] == '7')) {
+                               if (ver && (ver [2] == '5' || ver [2] == '6' || ver [2] == '7'))
                                        v5_supported = TRUE;
-                               }
+                               if (ver && (ver [2] == '7'))
+                                       v7_supported = TRUE;
                                continue;
                        }
                        if (strncmp (line, "Features", 8) == 0) {
@@ -536,12 +513,15 @@ mono_arch_cpu_optimizazions (guint32 *exclude_mask)
                /*printf ("features: v5: %d, thumb: %d\n", v5_supported, thumb_supported);*/
        }
 #endif
+       }
 
        /* no arm-specific optimizations yet */
        *exclude_mask = 0;
        return opts;
 }
 
+#ifndef DISABLE_JIT
+
 static gboolean
 is_regsize_var (MonoType *t) {
        if (t->byref)
@@ -605,11 +585,20 @@ GList *
 mono_arch_get_global_int_regs (MonoCompile *cfg)
 {
        GList *regs = NULL;
+
+       /* 
+        * FIXME: Interface calls might go through a static rgctx trampoline which
+        * sets V5, but it doesn't save it, so we need to save it ourselves, and
+        * avoid using it.
+        */
+       if (cfg->flags & MONO_CFG_HAS_CALLS)
+               cfg->uses_rgctx_reg = TRUE;
+
        regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V1));
        regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V2));
        regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V3));
        regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V4));
-       if (!(cfg->compile_aot || cfg->uses_rgctx_reg))
+       if (!(cfg->compile_aot || cfg->uses_rgctx_reg || COMPILE_LLVM (cfg)))
                /* V5 is reserved for passing the vtable/rgctx/IMT method */
                regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V5));
        /*regs = g_list_prepend (regs, GUINT_TO_POINTER (ARMREG_V6));*/
@@ -632,6 +621,8 @@ mono_arch_regalloc_cost (MonoCompile *cfg, MonoMethodVar *vmv)
        return 2;
 }
 
+#endif /* #ifndef DISABLE_JIT */
+
 #ifndef __GNUC_PREREQ
 #define __GNUC_PREREQ(maj, min) (0)
 #endif
@@ -653,7 +644,7 @@ mono_arch_flush_icache (guint8 *code, gint size)
                "svc     0x00000000\n"
                :
                :       "r" (code), "r" (code + size), "r" (syscall)
-               :       "r0", "r1", "r7"
+               :       "r0", "r1", "r7", "r2"
                );
 #else
        __asm __volatile ("mov r0, %0\n"
@@ -666,27 +657,31 @@ mono_arch_flush_icache (guint8 *code, gint size)
 #endif
 }
 
-enum {
+typedef enum {
+       RegTypeNone,
        RegTypeGeneral,
+       RegTypeIRegPair,
        RegTypeBase,
        RegTypeBaseGen,
        RegTypeFP,
        RegTypeStructByVal,
        RegTypeStructByAddr
-};
+} ArgStorage;
 
 typedef struct {
        gint32  offset;
        guint16 vtsize; /* in param area */
        guint8  reg;
-       guint8  regtype : 4; /* 0 general, 1 basereg, 2 floating point register, see RegType* */
+       ArgStorage  storage;
        guint8  size    : 4; /* 1, 2, 4, 8, or regs used by RegTypeStructByVal */
 } ArgInfo;
 
 typedef struct {
        int nargs;
        guint32 stack_usage;
-       guint32 struct_ret;
+       gboolean vtype_retaddr;
+       /* The index of the vret arg in the argument list */
+       int vret_arg_index;
        ArgInfo ret;
        ArgInfo sig_cookie;
        ArgInfo args [1];
@@ -694,6 +689,13 @@ typedef struct {
 
 #define DEBUG(a)
 
+#ifndef __GNUC__
+/*#define __alignof__(a) sizeof(a)*/
+#define __alignof__(type) G_STRUCT_OFFSET(struct { char c; type x; }, x)
+#endif
+
+#define PARAM_REGS 4
+
 static void inline
 add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple)
 {
@@ -701,36 +703,49 @@ add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple)
                if (*gr > ARMREG_R3) {
                        ainfo->offset = *stack_size;
                        ainfo->reg = ARMREG_SP; /* in the caller */
-                       ainfo->regtype = RegTypeBase;
+                       ainfo->storage = RegTypeBase;
                        *stack_size += 4;
                } else {
+                       ainfo->storage = RegTypeGeneral;
                        ainfo->reg = *gr;
                }
        } else {
-               if (*gr == ARMREG_R3
-#ifdef __ARM_EABI__
-                               && 0
+#if defined(__APPLE__) && defined(MONO_CROSS_COMPILE)
+               int i8_align = 4;
+#else
+               int i8_align = __alignof__ (gint64);
+#endif
+
+#if __ARM_EABI__
+               gboolean split = i8_align == 4;
+#else
+               gboolean split = TRUE;
 #endif
-                                       ) {
+               
+               if (*gr == ARMREG_R3 && split) {
                        /* first word in r3 and the second on the stack */
                        ainfo->offset = *stack_size;
                        ainfo->reg = ARMREG_SP; /* in the caller */
-                       ainfo->regtype = RegTypeBaseGen;
+                       ainfo->storage = RegTypeBaseGen;
                        *stack_size += 4;
                } else if (*gr >= ARMREG_R3) {
 #ifdef __ARM_EABI__
-                       *stack_size += 7;
-                       *stack_size &= ~7;
+                       /* darwin aligns longs to 4 byte only */
+                       if (i8_align == 8) {
+                               *stack_size += 7;
+                               *stack_size &= ~7;
+                       }
 #endif
                        ainfo->offset = *stack_size;
                        ainfo->reg = ARMREG_SP; /* in the caller */
-                       ainfo->regtype = RegTypeBase;
+                       ainfo->storage = RegTypeBase;
                        *stack_size += 8;
                } else {
 #ifdef __ARM_EABI__
-                       if ((*gr) & 1)
+                       if (i8_align == 8 && ((*gr) & 1))
                                (*gr) ++;
 #endif
+                       ainfo->storage = RegTypeIRegPair;
                        ainfo->reg = *gr;
                }
                (*gr) ++;
@@ -739,37 +754,73 @@ add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple)
 }
 
 static CallInfo*
-get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke)
+get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSignature *sig, gboolean is_pinvoke)
 {
-       guint i, gr;
+       guint i, gr, pstart;
        int n = sig->hasthis + sig->param_count;
        MonoType *simpletype;
        guint32 stack_size = 0;
-       CallInfo *cinfo = g_malloc0 (sizeof (CallInfo) + sizeof (ArgInfo) * n);
+       CallInfo *cinfo;
 
+       if (mp)
+               cinfo = mono_mempool_alloc0 (mp, sizeof (CallInfo) + (sizeof (ArgInfo) * n));
+       else
+               cinfo = g_malloc0 (sizeof (CallInfo) + (sizeof (ArgInfo) * n));
+
+       cinfo->nargs = n;
        gr = ARMREG_R0;
 
        /* FIXME: handle returning a struct */
        if (MONO_TYPE_ISSTRUCT (sig->ret)) {
-               add_general (&gr, &stack_size, &cinfo->ret, TRUE);
-               cinfo->struct_ret = ARMREG_R0;
+               guint32 align;
+
+               if (is_pinvoke && mono_class_native_size (mono_class_from_mono_type (sig->ret), &align) <= sizeof (gpointer)) {
+                       cinfo->ret.storage = RegTypeStructByVal;
+               } else {
+                       cinfo->vtype_retaddr = TRUE;
+               }
        }
 
+       pstart = 0;
        n = 0;
-       if (sig->hasthis) {
-               add_general (&gr, &stack_size, cinfo->args + n, TRUE);
-               n++;
+       /*
+        * To simplify get_this_arg_reg () and LLVM integration, emit the vret arg after
+        * the first argument, allowing 'this' to be always passed in the first arg reg.
+        * Also do this if the first argument is a reference type, since virtual calls
+        * are sometimes made using calli without sig->hasthis set, like in the delegate
+        * invoke wrappers.
+        */
+       if (cinfo->vtype_retaddr && !is_pinvoke && (sig->hasthis || (sig->param_count > 0 && MONO_TYPE_IS_REFERENCE (mini_type_get_underlying_type (gsctx, sig->params [0]))))) {
+               if (sig->hasthis) {
+                       add_general (&gr, &stack_size, cinfo->args + 0, TRUE);
+               } else {
+                       add_general (&gr, &stack_size, &cinfo->args [sig->hasthis + 0], TRUE);
+                       pstart = 1;
+               }
+               n ++;
+               add_general (&gr, &stack_size, &cinfo->ret, TRUE);
+               cinfo->vret_arg_index = 1;
+       } else {
+               /* this */
+               if (sig->hasthis) {
+                       add_general (&gr, &stack_size, cinfo->args + 0, TRUE);
+                       n ++;
+               }
+
+               if (cinfo->vtype_retaddr)
+                       add_general (&gr, &stack_size, &cinfo->ret, TRUE);
        }
-        DEBUG(printf("params: %d\n", sig->param_count));
-       for (i = 0; i < sig->param_count; ++i) {
+
+       DEBUG(printf("params: %d\n", sig->param_count));
+       for (i = pstart; i < sig->param_count; ++i) {
                if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
-                        /* Prevent implicit arguments and sig_cookie from
+                       /* Prevent implicit arguments and sig_cookie from
                           being passed in registers */
-                        gr = ARMREG_R3 + 1;
-                        /* Emit the signature cookie just before the implicit arguments */
-                        add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE);
-                }
-                DEBUG(printf("param %d: ", i));
+                       gr = ARMREG_R3 + 1;
+                       /* Emit the signature cookie just before the implicit arguments */
+                       add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE);
+               }
+               DEBUG(printf("param %d: ", i));
                if (sig->params [i]->byref) {
                         DEBUG(printf("byref\n"));
                        add_general (&gr, &stack_size, cinfo->args + n, TRUE);
@@ -813,7 +864,7 @@ get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke)
                        n++;
                        break;
                case MONO_TYPE_GENERICINST:
-                       if (!mono_type_generic_inst_is_valuetype (sig->params [i])) {
+                       if (!mono_type_generic_inst_is_valuetype (simpletype)) {
                                cinfo->args [n].size = sizeof (gpointer);
                                add_general (&gr, &stack_size, cinfo->args + n, TRUE);
                                n++;
@@ -844,7 +895,7 @@ get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke)
                        align_size += (sizeof (gpointer) - 1);
                        align_size &= ~(sizeof (gpointer) - 1);
                        nwords = (align_size + sizeof (gpointer) -1 ) / sizeof (gpointer);
-                       cinfo->args [n].regtype = RegTypeStructByVal;
+                       cinfo->args [n].storage = RegTypeStructByVal;
                        /* FIXME: align stack_size if needed */
 #ifdef __ARM_EABI__
                        if (align >= 8 && (gr & 1))
@@ -881,6 +932,15 @@ get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke)
                }
        }
 
+       /* Handle the case where there are no implicit arguments */
+       if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
+               /* Prevent implicit arguments and sig_cookie from
+                  being passed in registers */
+               gr = ARMREG_R3 + 1;
+               /* Emit the signature cookie just before the implicit arguments */
+               add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE);
+       }
+
        {
                simpletype = mini_type_get_underlying_type (NULL, sig->ret);
                switch (simpletype->type) {
@@ -901,27 +961,33 @@ get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke)
                case MONO_TYPE_SZARRAY:
                case MONO_TYPE_ARRAY:
                case MONO_TYPE_STRING:
+                       cinfo->ret.storage = RegTypeGeneral;
                        cinfo->ret.reg = ARMREG_R0;
                        break;
                case MONO_TYPE_U8:
                case MONO_TYPE_I8:
+                       cinfo->ret.storage = RegTypeIRegPair;
                        cinfo->ret.reg = ARMREG_R0;
                        break;
                case MONO_TYPE_R4:
                case MONO_TYPE_R8:
+                       cinfo->ret.storage = RegTypeFP;
                        cinfo->ret.reg = ARMREG_R0;
                        /* FIXME: cinfo->ret.reg = ???;
-                       cinfo->ret.regtype = RegTypeFP;*/
+                       cinfo->ret.storage = RegTypeFP;*/
                        break;
                case MONO_TYPE_GENERICINST:
-                       if (!mono_type_generic_inst_is_valuetype (sig->ret)) {
+                       if (!mono_type_generic_inst_is_valuetype (simpletype)) {
+                               cinfo->ret.storage = RegTypeGeneral;
                                cinfo->ret.reg = ARMREG_R0;
                                break;
                        }
-                       break;
+                       /* Fall through */
                case MONO_TYPE_VALUETYPE:
-                       break;
                case MONO_TYPE_TYPEDBYREF:
+                       if (cinfo->ret.storage != RegTypeStructByVal)
+                               cinfo->ret.storage = RegTypeStructByAddr;
+                       break;
                case MONO_TYPE_VOID:
                        break;
                default:
@@ -937,6 +1003,7 @@ get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke)
        return cinfo;
 }
 
+#ifndef DISABLE_JIT
 
 /*
  * Set var information according to the calling convention. arm version.
@@ -947,9 +1014,17 @@ mono_arch_allocate_vars (MonoCompile *cfg)
 {
        MonoMethodSignature *sig;
        MonoMethodHeader *header;
-       MonoInst *inst;
+       MonoInst *ins;
        int i, offset, size, align, curinst;
        int frame_reg = ARMREG_FP;
+       CallInfo *cinfo;
+       guint32 ualign;
+
+       sig = mono_method_signature (cfg->method);
+
+       if (!cfg->arch.cinfo)
+               cfg->arch.cinfo = get_call_info (cfg->generic_sharing_context, cfg->mempool, sig, sig->pinvoke);
+       cinfo = cfg->arch.cinfo;
 
        /* FIXME: this will change when we use FP as gcc does */
        cfg->flags |= MONO_CFG_HAS_SPILLUP;
@@ -958,7 +1033,7 @@ mono_arch_allocate_vars (MonoCompile *cfg)
        if (mono_jit_trace_calls != NULL && mono_trace_eval (cfg->method))
                cfg->param_area = MAX (cfg->param_area, sizeof (gpointer)*8);
 
-       header = mono_method_get_header (cfg->method);
+       header = cfg->header;
 
        /* 
         * We use the frame register also for any method that has
@@ -976,16 +1051,13 @@ mono_arch_allocate_vars (MonoCompile *cfg)
                cfg->used_int_regs |= 1 << frame_reg;
        }
 
-       if (cfg->compile_aot || cfg->uses_rgctx_reg)
+       if (cfg->compile_aot || cfg->uses_rgctx_reg || COMPILE_LLVM (cfg))
                /* V5 is reserved for passing the vtable/rgctx/IMT method */
                cfg->used_int_regs |= (1 << ARMREG_V5);
 
-       sig = mono_method_signature (cfg->method);
-       
        offset = 0;
        curinst = 0;
        if (!MONO_TYPE_ISSTRUCT (sig->ret)) {
-               /* FIXME: handle long and FP values */
                switch (mini_type_get_underlying_type (NULL, sig->ret)->type) {
                case MONO_TYPE_VOID:
                        break;
@@ -1017,42 +1089,68 @@ mono_arch_allocate_vars (MonoCompile *cfg)
                offset += 8;
 
        /* the MonoLMF structure is stored just below the stack pointer */
-
-       if (sig->call_convention == MONO_CALL_VARARG) {
-                cfg->sig_cookie = 0;
-        }
-
        if (MONO_TYPE_ISSTRUCT (sig->ret)) {
-               inst = cfg->vret_addr;
-               offset += sizeof(gpointer) - 1;
-               offset &= ~(sizeof(gpointer) - 1);
-               inst->inst_offset = offset;
-               inst->opcode = OP_REGOFFSET;
-               inst->inst_basereg = frame_reg;
-               if (G_UNLIKELY (cfg->verbose_level > 1)) {
-                       printf ("vret_addr =");
-                       mono_print_ins (cfg->vret_addr);
+               if (cinfo->ret.storage == RegTypeStructByVal) {
+                       cfg->ret->opcode = OP_REGOFFSET;
+                       cfg->ret->inst_basereg = cfg->frame_reg;
+                       offset += sizeof (gpointer) - 1;
+                       offset &= ~(sizeof (gpointer) - 1);
+                       cfg->ret->inst_offset = - offset;
+               } else {
+                       ins = cfg->vret_addr;
+                       offset += sizeof(gpointer) - 1;
+                       offset &= ~(sizeof(gpointer) - 1);
+                       ins->inst_offset = offset;
+                       ins->opcode = OP_REGOFFSET;
+                       ins->inst_basereg = frame_reg;
+                       if (G_UNLIKELY (cfg->verbose_level > 1)) {
+                               printf ("vret_addr =");
+                               mono_print_ins (cfg->vret_addr);
+                       }
                }
                offset += sizeof(gpointer);
-               if (sig->call_convention == MONO_CALL_VARARG)
-                       cfg->sig_cookie += sizeof (gpointer);
+       }
+
+       /* Allocate these first so they have a small offset, OP_SEQ_POINT depends on this */
+       if (cfg->arch.seq_point_info_var) {
+               MonoInst *ins;
+
+               ins = cfg->arch.seq_point_info_var;
+
+               size = 4;
+               align = 4;
+               offset += align - 1;
+               offset &= ~(align - 1);
+               ins->opcode = OP_REGOFFSET;
+               ins->inst_basereg = frame_reg;
+               ins->inst_offset = offset;
+               offset += size;
+
+               ins = cfg->arch.ss_trigger_page_var;
+               size = 4;
+               align = 4;
+               offset += align - 1;
+               offset &= ~(align - 1);
+               ins->opcode = OP_REGOFFSET;
+               ins->inst_basereg = frame_reg;
+               ins->inst_offset = offset;
+               offset += size;
        }
 
        curinst = cfg->locals_start;
        for (i = curinst; i < cfg->num_varinfo; ++i) {
-               inst = cfg->varinfo [i];
-               if ((inst->flags & MONO_INST_IS_DEAD) || inst->opcode == OP_REGVAR)
+               ins = cfg->varinfo [i];
+               if ((ins->flags & MONO_INST_IS_DEAD) || ins->opcode == OP_REGVAR || ins->opcode == OP_REGOFFSET)
                        continue;
 
                /* inst->backend.is_pinvoke indicates native sized value types, this is used by the
                * pinvoke wrappers when they call functions returning structure */
-               if (inst->backend.is_pinvoke && MONO_TYPE_ISSTRUCT (inst->inst_vtype) && inst->inst_vtype->type != MONO_TYPE_TYPEDBYREF) {
-                       guint32 ualign;
-                       size = mono_class_native_size (mono_class_from_mono_type (inst->inst_vtype), &ualign);
+               if (ins->backend.is_pinvoke && MONO_TYPE_ISSTRUCT (ins->inst_vtype) && ins->inst_vtype->type != MONO_TYPE_TYPEDBYREF) {
+                       size = mono_class_native_size (mono_class_from_mono_type (ins->inst_vtype), &ualign);
                        align = ualign;
                }
                else
-                       size = mono_type_size (inst->inst_vtype, &align);
+                       size = mono_type_size (ins->inst_vtype, &align);
 
                /* FIXME: if a structure is misaligned, our memcpy doesn't work,
                 * since it loads/stores misaligned words, which don't do the right thing.
@@ -1061,46 +1159,58 @@ mono_arch_allocate_vars (MonoCompile *cfg)
                        align = 4;
                offset += align - 1;
                offset &= ~(align - 1);
-               inst->inst_offset = offset;
-               inst->opcode = OP_REGOFFSET;
-               inst->inst_basereg = frame_reg;
+               ins->opcode = OP_REGOFFSET;
+               ins->inst_offset = offset;
+               ins->inst_basereg = frame_reg;
                offset += size;
                //g_print ("allocating local %d to %d\n", i, inst->inst_offset);
        }
 
        curinst = 0;
        if (sig->hasthis) {
-               inst = cfg->args [curinst];
-               if (inst->opcode != OP_REGVAR) {
-                       inst->opcode = OP_REGOFFSET;
-                       inst->inst_basereg = frame_reg;
+               ins = cfg->args [curinst];
+               if (ins->opcode != OP_REGVAR) {
+                       ins->opcode = OP_REGOFFSET;
+                       ins->inst_basereg = frame_reg;
                        offset += sizeof (gpointer) - 1;
                        offset &= ~(sizeof (gpointer) - 1);
-                       inst->inst_offset = offset;
+                       ins->inst_offset = offset;
                        offset += sizeof (gpointer);
-                       if (sig->call_convention == MONO_CALL_VARARG)
-                               cfg->sig_cookie += sizeof (gpointer);
                }
                curinst++;
        }
 
+       if (sig->call_convention == MONO_CALL_VARARG) {
+               size = 4;
+               align = 4;
+
+               /* Allocate a local slot to hold the sig cookie address */
+               offset += align - 1;
+               offset &= ~(align - 1);
+               cfg->sig_cookie = offset;
+               offset += size;
+       }                       
+
        for (i = 0; i < sig->param_count; ++i) {
-               inst = cfg->args [curinst];
-               if (inst->opcode != OP_REGVAR) {
-                       inst->opcode = OP_REGOFFSET;
-                       inst->inst_basereg = frame_reg;
-                       size = mono_type_size (sig->params [i], &align);
+               ins = cfg->args [curinst];
+
+               if (ins->opcode != OP_REGVAR) {
+                       ins->opcode = OP_REGOFFSET;
+                       ins->inst_basereg = frame_reg;
+                       size = mini_type_stack_size_full (NULL, sig->params [i], &ualign, sig->pinvoke);
+                       align = ualign;
                        /* FIXME: if a structure is misaligned, our memcpy doesn't work,
                         * since it loads/stores misaligned words, which don't do the right thing.
                         */
                        if (align < 4 && size >= 4)
                                align = 4;
+                       /* The code in the prolog () stores words when storing vtypes received in a register */
+                       if (MONO_TYPE_ISSTRUCT (sig->params [i]))
+                               align = 4;
                        offset += align - 1;
                        offset &= ~(align - 1);
-                       inst->inst_offset = offset;
+                       ins->inst_offset = offset;
                        offset += size;
-                       if ((sig->call_convention == MONO_CALL_VARARG) && (i < sig->sentinelpos)) 
-                               cfg->sig_cookie += size;
                }
                curinst++;
        }
@@ -1117,18 +1227,119 @@ void
 mono_arch_create_vars (MonoCompile *cfg)
 {
        MonoMethodSignature *sig;
+       CallInfo *cinfo;
 
        sig = mono_method_signature (cfg->method);
 
-       if (MONO_TYPE_ISSTRUCT (sig->ret)) {
+       if (!cfg->arch.cinfo)
+               cfg->arch.cinfo = get_call_info (cfg->generic_sharing_context, cfg->mempool, sig, sig->pinvoke);
+       cinfo = cfg->arch.cinfo;
+
+       if (cinfo->ret.storage == RegTypeStructByVal)
+               cfg->ret_var_is_local = TRUE;
+
+       if (MONO_TYPE_ISSTRUCT (sig->ret) && cinfo->ret.storage != RegTypeStructByVal) {
                cfg->vret_addr = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_ARG);
                if (G_UNLIKELY (cfg->verbose_level > 1)) {
                        printf ("vret_addr = ");
                        mono_print_ins (cfg->vret_addr);
                }
        }
+
+       if (cfg->gen_seq_points && cfg->compile_aot) {
+           MonoInst *ins = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
+               ins->flags |= MONO_INST_VOLATILE;
+               cfg->arch.seq_point_info_var = ins;
+
+               /* Allocate a separate variable for this to save 1 load per seq point */
+           ins = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
+               ins->flags |= MONO_INST_VOLATILE;
+               cfg->arch.ss_trigger_page_var = ins;
+       }
 }
 
+static void
+emit_sig_cookie (MonoCompile *cfg, MonoCallInst *call, CallInfo *cinfo)
+{
+       MonoMethodSignature *tmp_sig;
+       MonoInst *sig_arg;
+
+       if (call->tail_call)
+               NOT_IMPLEMENTED;
+
+       /* FIXME: Add support for signature tokens to AOT */
+       cfg->disable_aot = TRUE;
+
+       g_assert (cinfo->sig_cookie.storage == RegTypeBase);
+                       
+       /*
+        * mono_ArgIterator_Setup assumes the signature cookie is 
+        * passed first and all the arguments which were before it are
+        * passed on the stack after the signature. So compensate by 
+        * passing a different signature.
+        */
+       tmp_sig = mono_metadata_signature_dup (call->signature);
+       tmp_sig->param_count -= call->signature->sentinelpos;
+       tmp_sig->sentinelpos = 0;
+       memcpy (tmp_sig->params, call->signature->params + call->signature->sentinelpos, tmp_sig->param_count * sizeof (MonoType*));
+
+       MONO_INST_NEW (cfg, sig_arg, OP_ICONST);
+       sig_arg->dreg = mono_alloc_ireg (cfg);
+       sig_arg->inst_p0 = tmp_sig;
+       MONO_ADD_INS (cfg->cbb, sig_arg);
+
+       MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, cinfo->sig_cookie.offset, sig_arg->dreg);
+}
+
+#ifdef ENABLE_LLVM
+LLVMCallInfo*
+mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig)
+{
+       int i, n;
+       CallInfo *cinfo;
+       ArgInfo *ainfo;
+       LLVMCallInfo *linfo;
+
+       n = sig->param_count + sig->hasthis;
+
+       cinfo = get_call_info (cfg->generic_sharing_context, cfg->mempool, sig, sig->pinvoke);
+
+       linfo = mono_mempool_alloc0 (cfg->mempool, sizeof (LLVMCallInfo) + (sizeof (LLVMArgInfo) * n));
+
+       /*
+        * LLVM always uses the native ABI while we use our own ABI, the
+        * only difference is the handling of vtypes:
+        * - we only pass/receive them in registers in some cases, and only 
+        *   in 1 or 2 integer registers.
+        */
+       if (cinfo->ret.storage != RegTypeGeneral && cinfo->ret.storage != RegTypeNone && cinfo->ret.storage != RegTypeFP && cinfo->ret.storage != RegTypeIRegPair) {
+               cfg->exception_message = g_strdup ("unknown ret conv");
+               cfg->disable_llvm = TRUE;
+               return linfo;
+       }
+
+       for (i = 0; i < n; ++i) {
+               ainfo = cinfo->args + i;
+
+               linfo->args [i].storage = LLVMArgNone;
+
+               switch (ainfo->storage) {
+               case RegTypeGeneral:
+               case RegTypeIRegPair:
+               case RegTypeBase:
+                       linfo->args [i].storage = LLVMArgInIReg;
+                       break;
+               default:
+                       cfg->exception_message = g_strdup_printf ("ainfo->storage (%d)", ainfo->storage);
+                       cfg->disable_llvm = TRUE;
+                       break;
+               }
+       }
+
+       return linfo;
+}
+#endif
+
 void
 mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
 {
@@ -1140,7 +1351,7 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
        sig = call->signature;
        n = sig->param_count + sig->hasthis;
        
-       cinfo = get_call_info (sig, sig->pinvoke);
+       cinfo = get_call_info (cfg->generic_sharing_context, NULL, sig, sig->pinvoke);
 
        for (i = 0; i < n; ++i) {
                ArgInfo *ainfo = cinfo->args + i;
@@ -1153,14 +1364,15 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
                t = mini_type_get_underlying_type (NULL, t);
 
                if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
-                       /* FIXME: */
-                       NOT_IMPLEMENTED;
+                       /* Emit the signature cookie just before the implicit arguments */
+                       emit_sig_cookie (cfg, call, cinfo);
                }
 
                in = call->args [i];
 
-               switch (ainfo->regtype) {
+               switch (ainfo->storage) {
                case RegTypeGeneral:
+               case RegTypeIRegPair:
                        if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
                                MONO_INST_NEW (cfg, ins, OP_MOVE);
                                ins->dreg = mono_alloc_ireg (cfg);
@@ -1311,15 +1523,24 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
                }
        }
 
+       /* Handle the case where there are no implicit arguments */
+       if (!sig->pinvoke && (sig->call_convention == MONO_CALL_VARARG) && (n == sig->sentinelpos))
+               emit_sig_cookie (cfg, call, cinfo);
+
        if (sig->ret && MONO_TYPE_ISSTRUCT (sig->ret)) {
                MonoInst *vtarg;
 
-               MONO_INST_NEW (cfg, vtarg, OP_MOVE);
-               vtarg->sreg1 = call->vret_var->dreg;
-               vtarg->dreg = mono_alloc_preg (cfg);
-               MONO_ADD_INS (cfg->cbb, vtarg);
+               if (cinfo->ret.storage == RegTypeStructByVal) {
+                       /* The JIT will transform this into a normal call */
+                       call->vret_in_reg = TRUE;
+               } else {
+                       MONO_INST_NEW (cfg, vtarg, OP_MOVE);
+                       vtarg->sreg1 = call->vret_var->dreg;
+                       vtarg->dreg = mono_alloc_preg (cfg);
+                       MONO_ADD_INS (cfg->cbb, vtarg);
 
-               mono_call_inst_add_outarg_reg (cfg, call, vtarg->dreg, cinfo->ret.reg, FALSE);
+                       mono_call_inst_add_outarg_reg (cfg, call, vtarg->dreg, cinfo->ret.reg, FALSE);
+               }
        }
 
        call->stack_usage = cinfo->stack_usage;
@@ -1357,10 +1578,14 @@ mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val)
                if (ret->type == MONO_TYPE_I8 || ret->type == MONO_TYPE_U8) {
                        MonoInst *ins;
 
-                       MONO_INST_NEW (cfg, ins, OP_SETLRET);
-                       ins->sreg1 = val->dreg + 1;
-                       ins->sreg2 = val->dreg + 2;
-                       MONO_ADD_INS (cfg->cbb, ins);
+                       if (COMPILE_LLVM (cfg)) {
+                               MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
+                       } else {
+                               MONO_INST_NEW (cfg, ins, OP_SETLRET);
+                               ins->sreg1 = val->dreg + 1;
+                               ins->sreg2 = val->dreg + 2;
+                               MONO_ADD_INS (cfg->cbb, ins);
+                       }
                        return;
                }
 #ifdef MONO_ARCH_SOFT_FLOAT
@@ -1400,12 +1625,318 @@ mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val)
        MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
 }
 
+#endif /* #ifndef DISABLE_JIT */
+
 gboolean 
 mono_arch_is_inst_imm (gint64 imm)
 {
        return TRUE;
 }
 
+#define DYN_CALL_STACK_ARGS 6
+
+typedef struct {
+       MonoMethodSignature *sig;
+       CallInfo *cinfo;
+} ArchDynCallInfo;
+
+typedef struct {
+       mgreg_t regs [PARAM_REGS + DYN_CALL_STACK_ARGS];
+       mgreg_t res, res2;
+       guint8 *ret;
+} DynCallArgs;
+
+static gboolean
+dyn_call_supported (CallInfo *cinfo, MonoMethodSignature *sig)
+{
+       int i;
+
+       if (sig->hasthis + sig->param_count > PARAM_REGS + DYN_CALL_STACK_ARGS)
+               return FALSE;
+
+       switch (cinfo->ret.storage) {
+       case RegTypeNone:
+       case RegTypeGeneral:
+       case RegTypeIRegPair:
+       case RegTypeStructByAddr:
+               break;
+       case RegTypeFP:
+#ifdef ARM_FPU_FPA
+               return FALSE;
+#elif defined(ARM_FPU_VFP)
+               break;
+#else
+               return FALSE;
+#endif
+       default:
+               return FALSE;
+       }
+
+       for (i = 0; i < cinfo->nargs; ++i) {
+               switch (cinfo->args [i].storage) {
+               case RegTypeGeneral:
+                       break;
+               case RegTypeIRegPair:
+                       break;
+               case RegTypeBase:
+                       if (cinfo->args [i].offset >= (DYN_CALL_STACK_ARGS * sizeof (gpointer)))
+                               return FALSE;
+                       break;
+               case RegTypeStructByVal:
+                       if (cinfo->args [i].reg + cinfo->args [i].vtsize >= PARAM_REGS + DYN_CALL_STACK_ARGS)
+                               return FALSE;
+                       break;
+               default:
+                       return FALSE;
+               }
+       }
+
+       // FIXME: Can't use cinfo only as it doesn't contain info about I8/float */
+       for (i = 0; i < sig->param_count; ++i) {
+               MonoType *t = sig->params [i];
+
+               if (t->byref)
+                       continue;
+
+               switch (t->type) {
+               case MONO_TYPE_R4:
+               case MONO_TYPE_R8:
+#ifdef MONO_ARCH_SOFT_FLOAT
+                       return FALSE;
+#else
+                       break;
+#endif
+                       /*
+               case MONO_TYPE_I8:
+               case MONO_TYPE_U8:
+                       return FALSE;
+                       */
+               default:
+                       break;
+               }
+       }
+
+       return TRUE;
+}
+
+MonoDynCallInfo*
+mono_arch_dyn_call_prepare (MonoMethodSignature *sig)
+{
+       ArchDynCallInfo *info;
+       CallInfo *cinfo;
+
+       cinfo = get_call_info (NULL, NULL, sig, FALSE);
+
+       if (!dyn_call_supported (cinfo, sig)) {
+               g_free (cinfo);
+               return NULL;
+       }
+
+       info = g_new0 (ArchDynCallInfo, 1);
+       // FIXME: Preprocess the info to speed up start_dyn_call ()
+       info->sig = sig;
+       info->cinfo = cinfo;
+       
+       return (MonoDynCallInfo*)info;
+}
+
+void
+mono_arch_dyn_call_free (MonoDynCallInfo *info)
+{
+       ArchDynCallInfo *ainfo = (ArchDynCallInfo*)info;
+
+       g_free (ainfo->cinfo);
+       g_free (ainfo);
+}
+
+void
+mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf, int buf_len)
+{
+       ArchDynCallInfo *dinfo = (ArchDynCallInfo*)info;
+       DynCallArgs *p = (DynCallArgs*)buf;
+       int arg_index, greg, i, j;
+       MonoMethodSignature *sig = dinfo->sig;
+
+       g_assert (buf_len >= sizeof (DynCallArgs));
+
+       p->res = 0;
+       p->ret = ret;
+
+       arg_index = 0;
+       greg = 0;
+
+       if (dinfo->cinfo->vtype_retaddr)
+               p->regs [greg ++] = (mgreg_t)ret;
+
+       if (sig->hasthis)
+               p->regs [greg ++] = (mgreg_t)*(args [arg_index ++]);
+
+       for (i = 0; i < sig->param_count; i++) {
+               MonoType *t = mono_type_get_underlying_type (sig->params [i]);
+               gpointer *arg = args [arg_index ++];
+               ArgInfo *ainfo = &dinfo->cinfo->args [i + sig->hasthis];
+               int slot = -1;
+
+               if (ainfo->storage == RegTypeGeneral || ainfo->storage == RegTypeIRegPair || ainfo->storage == RegTypeStructByVal)
+                       slot = ainfo->reg;
+               else if (ainfo->storage == RegTypeBase)
+                       slot = PARAM_REGS + (ainfo->offset / 4);
+               else
+                       g_assert_not_reached ();
+
+               if (t->byref) {
+                       p->regs [slot] = (mgreg_t)*arg;
+                       continue;
+               }
+
+               switch (t->type) {
+               case MONO_TYPE_STRING:
+               case MONO_TYPE_CLASS:  
+               case MONO_TYPE_ARRAY:
+               case MONO_TYPE_SZARRAY:
+               case MONO_TYPE_OBJECT:
+               case MONO_TYPE_PTR:
+               case MONO_TYPE_I:
+               case MONO_TYPE_U:
+                       p->regs [slot] = (mgreg_t)*arg;
+                       break;
+               case MONO_TYPE_BOOLEAN:
+               case MONO_TYPE_U1:
+                       p->regs [slot] = *(guint8*)arg;
+                       break;
+               case MONO_TYPE_I1:
+                       p->regs [slot] = *(gint8*)arg;
+                       break;
+               case MONO_TYPE_I2:
+                       p->regs [slot] = *(gint16*)arg;
+                       break;
+               case MONO_TYPE_U2:
+               case MONO_TYPE_CHAR:
+                       p->regs [slot] = *(guint16*)arg;
+                       break;
+               case MONO_TYPE_I4:
+                       p->regs [slot] = *(gint32*)arg;
+                       break;
+               case MONO_TYPE_U4:
+                       p->regs [slot] = *(guint32*)arg;
+                       break;
+               case MONO_TYPE_I8:
+               case MONO_TYPE_U8:
+                       p->regs [slot ++] = (mgreg_t)arg [0];
+                       p->regs [slot] = (mgreg_t)arg [1];
+                       break;
+               case MONO_TYPE_R4:
+                       p->regs [slot] = *(mgreg_t*)arg;
+                       break;
+               case MONO_TYPE_R8:
+                       p->regs [slot ++] = (mgreg_t)arg [0];
+                       p->regs [slot] = (mgreg_t)arg [1];
+                       break;
+               case MONO_TYPE_GENERICINST:
+                       if (MONO_TYPE_IS_REFERENCE (t)) {
+                               p->regs [slot] = (mgreg_t)*arg;
+                               break;
+                       } else {
+                               /* Fall though */
+                       }
+               case MONO_TYPE_VALUETYPE:
+                       g_assert (ainfo->storage == RegTypeStructByVal);
+
+                       if (ainfo->size == 0)
+                               slot = PARAM_REGS + (ainfo->offset / 4);
+                       else
+                               slot = ainfo->reg;
+
+                       for (j = 0; j < ainfo->size + ainfo->vtsize; ++j)
+                               p->regs [slot ++] = ((mgreg_t*)arg) [j];
+                       break;
+               default:
+                       g_assert_not_reached ();
+               }
+       }
+}
+
+void
+mono_arch_finish_dyn_call (MonoDynCallInfo *info, guint8 *buf)
+{
+       ArchDynCallInfo *ainfo = (ArchDynCallInfo*)info;
+       MonoMethodSignature *sig = ((ArchDynCallInfo*)info)->sig;
+       guint8 *ret = ((DynCallArgs*)buf)->ret;
+       mgreg_t res = ((DynCallArgs*)buf)->res;
+       mgreg_t res2 = ((DynCallArgs*)buf)->res2;
+
+       switch (mono_type_get_underlying_type (sig->ret)->type) {
+       case MONO_TYPE_VOID:
+               *(gpointer*)ret = NULL;
+               break;
+       case MONO_TYPE_STRING:
+       case MONO_TYPE_CLASS:  
+       case MONO_TYPE_ARRAY:
+       case MONO_TYPE_SZARRAY:
+       case MONO_TYPE_OBJECT:
+       case MONO_TYPE_I:
+       case MONO_TYPE_U:
+       case MONO_TYPE_PTR:
+               *(gpointer*)ret = (gpointer)res;
+               break;
+       case MONO_TYPE_I1:
+               *(gint8*)ret = res;
+               break;
+       case MONO_TYPE_U1:
+       case MONO_TYPE_BOOLEAN:
+               *(guint8*)ret = res;
+               break;
+       case MONO_TYPE_I2:
+               *(gint16*)ret = res;
+               break;
+       case MONO_TYPE_U2:
+       case MONO_TYPE_CHAR:
+               *(guint16*)ret = res;
+               break;
+       case MONO_TYPE_I4:
+               *(gint32*)ret = res;
+               break;
+       case MONO_TYPE_U4:
+               *(guint32*)ret = res;
+               break;
+       case MONO_TYPE_I8:
+       case MONO_TYPE_U8:
+               /* This handles endianness as well */
+               ((gint32*)ret) [0] = res;
+               ((gint32*)ret) [1] = res2;
+               break;
+       case MONO_TYPE_GENERICINST:
+               if (MONO_TYPE_IS_REFERENCE (sig->ret)) {
+                       *(gpointer*)ret = (gpointer)res;
+                       break;
+               } else {
+                       /* Fall though */
+               }
+       case MONO_TYPE_VALUETYPE:
+               g_assert (ainfo->cinfo->vtype_retaddr);
+               /* Nothing to do */
+               break;
+#if defined(ARM_FPU_VFP)
+       case MONO_TYPE_R4:
+               *(float*)ret = *(float*)&res;
+               break;
+       case MONO_TYPE_R8: {
+               mgreg_t regs [2];
+
+               regs [0] = res;
+               regs [1] = res2;
+
+               *(double*)ret = *(double*)&regs;
+               break;
+       }
+#endif
+       default:
+               g_assert_not_reached ();
+       }
+}
+
+#ifndef DISABLE_JIT
+
 /*
  * Allow tracing to work with this interface (with an optional argument)
  */
@@ -1955,8 +2486,13 @@ loop_start:
                        gboolean swap = FALSE;
                        int reg;
 
+                       if (!ins->next) {
+                               /* Optimized away */
+                               NULLIFY_INS (ins);
+                               break;
+                       }
+
                        /* Some fp compares require swapped operands */
-                       g_assert (ins->next);
                        switch (ins->next->opcode) {
                        case OP_FBGT:
                                ins->next->opcode = OP_FBLT;
@@ -1996,7 +2532,6 @@ void
 mono_arch_decompose_long_opts (MonoCompile *cfg, MonoInst *long_ins)
 {
        MonoInst *ins;
-       int vreg;
 
        if (long_ins->opcode == OP_LNEG) {
                ins = long_ins;
@@ -2038,6 +2573,8 @@ emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size,
        return code;
 }
 
+#endif /* #ifndef DISABLE_JIT */
+
 typedef struct {
        guchar *code;
        const guchar *target;
@@ -2107,10 +2644,13 @@ search_thunk_slot (void *data, int csize, int bsize, void *user_data) {
 }
 
 static void
-handle_thunk (int absolute, guchar *code, const guchar *target) {
-       MonoDomain *domain = mono_domain_get ();
+handle_thunk (MonoDomain *domain, int absolute, guchar *code, const guchar *target)
+{
        PatchData pdata;
 
+       if (!domain)
+               domain = mono_domain_get ();
+
        pdata.code = code;
        pdata.target = target;
        pdata.absolute = absolute;
@@ -2131,8 +2671,8 @@ handle_thunk (int absolute, guchar *code, const guchar *target) {
        g_assert (pdata.found == 1);
 }
 
-void
-arm_patch (guchar *code, const guchar *target)
+static void
+arm_patch_general (MonoDomain *domain, guchar *code, const guchar *target)
 {
        guint32 *code32 = (void*)code;
        guint32 ins = *code32;
@@ -2178,7 +2718,7 @@ arm_patch (guchar *code, const guchar *target)
                        }
                }
                
-               handle_thunk (TRUE, code, target);
+               handle_thunk (domain, TRUE, code, target);
                return;
        }
 
@@ -2278,6 +2818,12 @@ arm_patch (guchar *code, const guchar *target)
 //     g_print ("patched with 0x%08x\n", ins);
 }
 
+void
+arm_patch (guchar *code, const guchar *target)
+{
+       arm_patch_general (NULL, code, target);
+}
+
 /* 
  * Return the >= 0 uimm8 value if val can be represented with a byte + rotation
  * (with the rotation amount in *rot_amount. rot_amount is already adjusted
@@ -2319,6 +2865,12 @@ mono_arm_emit_load_imm (guint8 *code, int dreg, guint32 val)
        } else if ((imm8 = mono_arm_is_rotated_imm8 (~val, &rot_amount)) >= 0) {
                ARM_MVN_REG_IMM (code, dreg, imm8, rot_amount);
        } else {
+               if (v7_supported) {
+                       ARM_MOVW_REG_IMM (code, dreg, val & 0xffff);
+                       if (val >> 16)
+                               ARM_MOVT_REG_IMM (code, dreg, (val >> 16) & 0xffff);
+                       return code;
+               }
                if (val & 0xFF) {
                        ARM_MOV_REG_IMM8 (code, dreg, (val & 0xFF));
                        if (val & 0xFF00) {
@@ -2349,6 +2901,14 @@ mono_arm_emit_load_imm (guint8 *code, int dreg, guint32 val)
        return code;
 }
 
+gboolean
+mono_arm_thumb_supported (void)
+{
+       return thumb_supported;
+}
+
+#ifndef DISABLE_JIT
+
 /*
  * emit_load_volatile_arguments:
  *
@@ -2372,7 +2932,7 @@ emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code)
 
        pos = 0;
 
-       cinfo = get_call_info (sig, sig->pinvoke);
+       cinfo = get_call_info (cfg->generic_sharing_context, NULL, sig, sig->pinvoke);
 
        if (MONO_TYPE_ISSTRUCT (sig->ret)) {
                ArgInfo *ainfo = &cinfo->ret;
@@ -2385,13 +2945,13 @@ emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code)
                inst = cfg->args [pos];
                
                if (cfg->verbose_level > 2)
-                       g_print ("Loading argument %d (type: %d)\n", i, ainfo->regtype);
+                       g_print ("Loading argument %d (type: %d)\n", i, ainfo->storage);
                if (inst->opcode == OP_REGVAR) {
-                       if (ainfo->regtype == RegTypeGeneral)
+                       if (ainfo->storage == RegTypeGeneral)
                                ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
-                       else if (ainfo->regtype == RegTypeFP) {
+                       else if (ainfo->storage == RegTypeFP) {
                                g_assert_not_reached ();
-                       } else if (ainfo->regtype == RegTypeBase) {
+                       } else if (ainfo->storage == RegTypeBase) {
                                // FIXME:
                                NOT_IMPLEMENTED;
                                /*
@@ -2405,7 +2965,7 @@ emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code)
                        } else
                                g_assert_not_reached ();
                } else {
-                       if (ainfo->regtype == RegTypeGeneral) {
+                       if (ainfo->storage == RegTypeGeneral || ainfo->storage == RegTypeIRegPair) {
                                switch (ainfo->size) {
                                case 1:
                                case 2:
@@ -2427,14 +2987,14 @@ emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code)
                                        }
                                        break;
                                }
-                       } else if (ainfo->regtype == RegTypeBaseGen) {
+                       } else if (ainfo->storage == RegTypeBaseGen) {
                                // FIXME:
                                NOT_IMPLEMENTED;
-                       } else if (ainfo->regtype == RegTypeBase) {
+                       } else if (ainfo->storage == RegTypeBase) {
                                /* Nothing to do */
-                       } else if (ainfo->regtype == RegTypeFP) {
+                       } else if (ainfo->storage == RegTypeFP) {
                                g_assert_not_reached ();
-                       } else if (ainfo->regtype == RegTypeStructByVal) {
+                       } else if (ainfo->storage == RegTypeStructByVal) {
                                int doffset = inst->inst_offset;
                                int soffset = 0;
                                int cur_reg;
@@ -2454,7 +3014,7 @@ emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code)
                                if (ainfo->vtsize)
                                        // FIXME:
                                        NOT_IMPLEMENTED;
-                       } else if (ainfo->regtype == RegTypeStructByAddr) {
+                       } else if (ainfo->storage == RegTypeStructByAddr) {
                        } else {
                                // FIXME:
                                NOT_IMPLEMENTED;
@@ -2468,8 +3028,6 @@ emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code)
        return code;
 }
 
-#ifndef DISABLE_JIT
-
 void
 mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
 {
@@ -2680,6 +3238,90 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                case OP_NOT_REACHED:
                case OP_NOT_NULL:
                        break;
+               case OP_SEQ_POINT: {
+                       int i;
+                       MonoInst *info_var = cfg->arch.seq_point_info_var;
+                       MonoInst *ss_trigger_page_var = cfg->arch.ss_trigger_page_var;
+                       MonoInst *var;
+                       int dreg = ARMREG_LR;
+
+                       /*
+                        * For AOT, we use one got slot per method, which will point to a
+                        * SeqPointInfo structure, containing all the information required
+                        * by the code below.
+                        */
+                       if (cfg->compile_aot) {
+                               g_assert (info_var);
+                               g_assert (info_var->opcode == OP_REGOFFSET);
+                               g_assert (arm_is_imm12 (info_var->inst_offset));
+                       }
+
+                       /* 
+                        * Read from the single stepping trigger page. This will cause a
+                        * SIGSEGV when single stepping is enabled.
+                        * We do this _before_ the breakpoint, so single stepping after
+                        * a breakpoint is hit will step to the next IL offset.
+                        */
+                       g_assert (((guint64)(gsize)ss_trigger_page >> 32) == 0);
+
+                       if (ins->flags & MONO_INST_SINGLE_STEP_LOC) {
+                               if (cfg->compile_aot) {
+                                       /* Load the trigger page addr from the variable initialized in the prolog */
+                                       var = ss_trigger_page_var;
+                                       g_assert (var);
+                                       g_assert (var->opcode == OP_REGOFFSET);
+                                       g_assert (arm_is_imm12 (var->inst_offset));
+                                       ARM_LDR_IMM (code, dreg, var->inst_basereg, var->inst_offset);
+                               } else {
+                                       ARM_LDR_IMM (code, dreg, ARMREG_PC, 0);
+                                       ARM_B (code, 0);
+                                       *(int*)code = (int)ss_trigger_page;
+                                       code += 4;
+                               }
+                               ARM_LDR_IMM (code, dreg, dreg, 0);
+                       }
+
+                       mono_add_seq_point (cfg, bb, ins, code - cfg->native_code);
+
+                       if (cfg->compile_aot) {
+                               guint32 offset = code - cfg->native_code;
+                               guint32 val;
+
+                               ARM_LDR_IMM (code, dreg, info_var->inst_basereg, info_var->inst_offset);
+                               /* Add the offset */
+                               val = ((offset / 4) * sizeof (guint8*)) + G_STRUCT_OFFSET (SeqPointInfo, bp_addrs);
+                               ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF), 0);
+                               /* 
+                                * Have to emit nops to keep the difference between the offset
+                                * stored in seq_points and breakpoint instruction constant,
+                                * mono_arch_get_ip_for_breakpoint () depends on this.
+                                */
+                               if (val & 0xFF00)
+                                       ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF00) >> 8, 24);
+                               else
+                                       ARM_NOP (code);
+                               if (val & 0xFF0000)
+                                       ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
+                               else
+                                       ARM_NOP (code);
+                               g_assert (!(val & 0xFF000000));
+                               /* Load the info->bp_addrs [offset], which is either 0 or the address of a trigger page */
+                               ARM_LDR_IMM (code, dreg, dreg, 0);
+
+                               /* What is faster, a branch or a load ? */
+                               ARM_CMP_REG_IMM (code, dreg, 0, 0);
+                               /* The breakpoint instruction */
+                               ARM_LDR_IMM_COND (code, dreg, dreg, 0, ARMCOND_NE);
+                       } else {
+                               /* 
+                                * A placeholder for a possible breakpoint inserted by
+                                * mono_arch_set_breakpoint ().
+                                */
+                               for (i = 0; i < 4; ++i)
+                                       ARM_NOP (code);
+                       }
+                       break;
+               }
                case OP_ADDCC:
                case OP_IADDCC:
                        ARM_ADDS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
@@ -2940,15 +3582,9 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        ARM_LDR_IMM (code, ARMREG_LR, ins->sreg1, 0);
                        break;
                case OP_ARGLIST: {
-#if ARM_PORT
-                       if (ppc_is_imm16 (cfg->sig_cookie + cfg->stack_usage)) {
-                               ppc_addi (code, ppc_r11, cfg->frame_reg, cfg->sig_cookie + cfg->stack_usage);
-                       } else {
-                               ppc_load (code, ppc_r11, cfg->sig_cookie + cfg->stack_usage);
-                               ppc_add (code, ppc_r11, cfg->frame_reg, ppc_r11);
-                       }
-                       ppc_stw (code, ppc_r11, 0, ins->sreg1);
-#endif
+                       g_assert (cfg->sig_cookie < 128);
+                       ARM_LDR_IMM (code, ARMREG_IP, cfg->frame_reg, cfg->sig_cookie);
+                       ARM_STR_IMM (code, ARMREG_IP, ins->sreg1, 0);
                        break;
                }
                case OP_FCALL:
@@ -2983,7 +3619,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        g_assert (arm_is_imm12 (ins->inst_offset));
                        g_assert (ins->sreg1 != ARMREG_LR);
                        call = (MonoCallInst*)ins;
-                       if (call->method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
+                       if (call->dynamic_imt_arg || 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);
                                /* 
@@ -3032,6 +3668,42 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        ARM_ADD_REG_IMM8 (code, ins->dreg, ARMREG_SP, alloca_waste);
                        break;
                }
+               case OP_DYN_CALL: {
+                       int i;
+                       MonoInst *var = cfg->dyn_call_var;
+
+                       g_assert (var->opcode == OP_REGOFFSET);
+                       g_assert (arm_is_imm12 (var->inst_offset));
+
+                       /* lr = args buffer filled by mono_arch_get_dyn_call_args () */
+                       ARM_MOV_REG_REG( code, ARMREG_LR, ins->sreg1);
+                       /* ip = ftn */
+                       ARM_MOV_REG_REG( code, ARMREG_IP, ins->sreg2);
+
+                       /* Save args buffer */
+                       ARM_STR_IMM (code, ARMREG_LR, var->inst_basereg, var->inst_offset);
+
+                       /* Set stack slots using R0 as scratch reg */
+                       /* MONO_ARCH_DYN_CALL_PARAM_AREA gives the size of stack space available */
+                       for (i = 0; i < DYN_CALL_STACK_ARGS; ++i) {
+                               ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, (PARAM_REGS + i) * sizeof (gpointer));
+                               ARM_STR_IMM (code, ARMREG_R0, ARMREG_SP, i * sizeof (gpointer));
+                       }
+
+                       /* Set argument registers */
+                       for (i = 0; i < PARAM_REGS; ++i)
+                               ARM_LDR_IMM (code, i, ARMREG_LR, i * sizeof (gpointer));
+
+                       /* Make the call */
+                       ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
+                       ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
+
+                       /* Save result */
+                       ARM_LDR_IMM (code, ARMREG_IP, var->inst_basereg, var->inst_offset);
+                       ARM_STR_IMM (code, ARMREG_R0, ARMREG_IP, G_STRUCT_OFFSET (DynCallArgs, res)); 
+                       ARM_STR_IMM (code, ARMREG_R1, ARMREG_IP, G_STRUCT_OFFSET (DynCallArgs, res2)); 
+                       break;
+               }
                case OP_THROW: {
                        if (ins->sreg1 != ARMREG_R0)
                                ARM_MOV_REG_REG (code, ARMREG_R0, ins->sreg1);
@@ -3090,6 +3762,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                case OP_CALL_HANDLER: 
                        mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb);
                        ARM_BL (code, 0);
+                       mono_cfg_add_try_hole (cfg, ins->inst_eh_block, code, bb);
                        break;
                case OP_LABEL:
                        ins->inst_c0 = code - cfg->native_code;
@@ -3119,7 +3792,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                         */
                        mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_SWITCH, ins->inst_p0);
                        max_len += 4 * GPOINTER_TO_INT (ins->klass);
-                       if (offset > (cfg->code_size - max_len - 16)) {
+                       if (offset + max_len > (cfg->code_size - 16)) {
                                cfg->code_size += max_len;
                                cfg->code_size *= 2;
                                cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
@@ -3412,9 +4085,8 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        g_assert_not_reached ();
                        /* Implemented as helper calls */
                        break;
-               case OP_LCONV_TO_OVF_I:
                case OP_LCONV_TO_OVF_I4_2: {
-                       guint32 *high_bit_not_set, *valid_negative, *invalid_negative, *valid_positive;
+                       guint8 *high_bit_not_set, *valid_negative, *invalid_negative, *valid_positive;
                        /* 
                         * Valid ints: 0xffffffff:8000000 to 00000000:0x7f000000
                         */
@@ -3639,9 +4311,11 @@ mono_arch_register_lowlevel_calls (void)
        mono_register_jit_icall (mono_arm_throw_exception, "mono_arm_throw_exception", mono_create_icall_signature ("void"), TRUE);
        mono_register_jit_icall (mono_arm_throw_exception_by_token, "mono_arm_throw_exception_by_token", mono_create_icall_signature ("void"), TRUE);
 
+#ifndef MONO_CROSS_COMPILE
 #ifdef HAVE_AEABI_READ_TP
        mono_register_jit_icall (__aeabi_read_tp, "__aeabi_read_tp", mono_create_icall_signature ("void"), TRUE);
 #endif
+#endif
 }
 
 #define patch_lis_ori(ip,val) do {\
@@ -3724,10 +4398,12 @@ mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, Mono
                default:
                        break;
                }
-               arm_patch (ip, target);
+               arm_patch_general (domain, ip, target);
        }
 }
 
+#ifndef DISABLE_JIT
+
 /*
  * Stack frame layout:
  * 
@@ -3861,26 +4537,39 @@ mono_arch_emit_prolog (MonoCompile *cfg)
        /* load arguments allocated to register from the stack */
        pos = 0;
 
-       cinfo = get_call_info (sig, sig->pinvoke);
+       cinfo = get_call_info (cfg->generic_sharing_context, NULL, sig, sig->pinvoke);
 
-       if (MONO_TYPE_ISSTRUCT (sig->ret)) {
+       if (MONO_TYPE_ISSTRUCT (sig->ret) && cinfo->ret.storage != RegTypeStructByVal) {
                ArgInfo *ainfo = &cinfo->ret;
                inst = cfg->vret_addr;
                g_assert (arm_is_imm12 (inst->inst_offset));
                ARM_STR_IMM (code, ainfo->reg, inst->inst_basereg, inst->inst_offset);
        }
+
+       if (sig->call_convention == MONO_CALL_VARARG) {
+               ArgInfo *cookie = &cinfo->sig_cookie;
+
+               /* Save the sig cookie address */
+               g_assert (cookie->storage == RegTypeBase);
+
+               g_assert (arm_is_imm12 (prev_sp_offset + cookie->offset));
+               g_assert (arm_is_imm12 (cfg->sig_cookie));
+               ARM_ADD_REG_IMM8 (code, ARMREG_IP, cfg->frame_reg, prev_sp_offset + cookie->offset);
+               ARM_STR_IMM (code, ARMREG_IP, cfg->frame_reg, cfg->sig_cookie);
+       }
+
        for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
                ArgInfo *ainfo = cinfo->args + i;
                inst = cfg->args [pos];
                
                if (cfg->verbose_level > 2)
-                       g_print ("Saving argument %d (type: %d)\n", i, ainfo->regtype);
+                       g_print ("Saving argument %d (type: %d)\n", i, ainfo->storage);
                if (inst->opcode == OP_REGVAR) {
-                       if (ainfo->regtype == RegTypeGeneral)
+                       if (ainfo->storage == RegTypeGeneral)
                                ARM_MOV_REG_REG (code, inst->dreg, ainfo->reg);
-                       else if (ainfo->regtype == RegTypeFP) {
+                       else if (ainfo->storage == RegTypeFP) {
                                g_assert_not_reached ();
-                       } else if (ainfo->regtype == RegTypeBase) {
+                       } else if (ainfo->storage == RegTypeBase) {
                                if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
                                        ARM_LDR_IMM (code, inst->dreg, ARMREG_SP, (prev_sp_offset + ainfo->offset));
                                } else {
@@ -3894,7 +4583,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                                g_print ("Argument %d assigned to register %s\n", pos, mono_arch_regname (inst->dreg));
                } else {
                        /* the argument should be put on the stack: FIXME handle size != word  */
-                       if (ainfo->regtype == RegTypeGeneral) {
+                       if (ainfo->storage == RegTypeGeneral || ainfo->storage == RegTypeIRegPair) {
                                switch (ainfo->size) {
                                case 1:
                                        if (arm_is_imm12 (inst->inst_offset))
@@ -3927,13 +4616,13 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                                        }
                                        break;
                                }
-                       } else if (ainfo->regtype == RegTypeBaseGen) {
+                       } else if (ainfo->storage == RegTypeBaseGen) {
                                g_assert (arm_is_imm12 (prev_sp_offset + ainfo->offset));
                                g_assert (arm_is_imm12 (inst->inst_offset));
                                ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
                                ARM_STR_IMM (code, ARMREG_LR, inst->inst_basereg, inst->inst_offset + 4);
                                ARM_STR_IMM (code, ARMREG_R3, inst->inst_basereg, inst->inst_offset);
-                       } else if (ainfo->regtype == RegTypeBase) {
+                       } else if (ainfo->storage == RegTypeBase) {
                                if (arm_is_imm12 (prev_sp_offset + ainfo->offset)) {
                                        ARM_LDR_IMM (code, ARMREG_LR, ARMREG_SP, (prev_sp_offset + ainfo->offset));
                                } else {
@@ -3987,9 +4676,9 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                                        }
                                        break;
                                }
-                       } else if (ainfo->regtype == RegTypeFP) {
+                       } else if (ainfo->storage == RegTypeFP) {
                                g_assert_not_reached ();
-                       } else if (ainfo->regtype == RegTypeStructByVal) {
+                       } else if (ainfo->storage == RegTypeStructByVal) {
                                int doffset = inst->inst_offset;
                                int soffset = 0;
                                int cur_reg;
@@ -4010,7 +4699,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                                        //g_print ("emit_memcpy (prev_sp_ofs: %d, ainfo->offset: %d, soffset: %d)\n", prev_sp_offset, ainfo->offset, soffset);
                                        code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, doffset, ARMREG_SP, prev_sp_offset + ainfo->offset);
                                }
-                       } else if (ainfo->regtype == RegTypeStructByAddr) {
+                       } else if (ainfo->storage == RegTypeStructByAddr) {
                                g_assert_not_reached ();
                                /* FIXME: handle overrun! with struct sizes not multiple of 4 */
                                code = emit_memcpy (code, ainfo->vtsize * sizeof (gpointer), inst->inst_basereg, inst->inst_offset, ainfo->reg, 0);
@@ -4021,7 +4710,11 @@ mono_arch_emit_prolog (MonoCompile *cfg)
        }
 
        if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
-               code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->domain);
+               if (cfg->compile_aot)
+                       /* AOT code is only used in the root domain */
+                       code = mono_arm_emit_load_imm (code, ARMREG_R0, 0);
+               else
+                       code = mono_arm_emit_load_imm (code, ARMREG_R0, (guint32)cfg->domain);
                mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, 
                             (gpointer)"mono_jit_thread_attach");
                code = emit_call_seq (cfg, code);
@@ -4066,7 +4759,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                /* *(lmf_addr) = r1 */
                ARM_STR_IMM (code, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoLMF, previous_lmf));
                /* Skip method (only needed for trampoline LMF frames) */
-               ARM_STR_IMM (code, ARMREG_SP, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, ebp));
+               ARM_STR_IMM (code, ARMREG_SP, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, esp));
                /* save the current IP */
                ARM_MOV_REG_REG (code, ARMREG_R2, ARMREG_PC);
                ARM_STR_IMM (code, ARMREG_R2, ARMREG_R1, G_STRUCT_OFFSET (MonoLMF, eip));
@@ -4075,6 +4768,44 @@ mono_arch_emit_prolog (MonoCompile *cfg)
        if (tracing)
                code = mono_arch_instrument_prolog (cfg, mono_trace_enter_method, code, TRUE);
 
+       if (cfg->arch.seq_point_info_var) {
+               MonoInst *ins = cfg->arch.seq_point_info_var;
+
+               /* Initialize the variable from a GOT slot */
+               mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_SEQ_POINT_INFO, cfg->method);
+               ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
+               ARM_B (code, 0);
+               *(gpointer*)code = NULL;
+               code += 4;
+               ARM_LDR_REG_REG (code, ARMREG_R0, ARMREG_PC, ARMREG_R0);
+
+               g_assert (ins->opcode == OP_REGOFFSET);
+
+               if (arm_is_imm12 (ins->inst_offset)) {
+                       ARM_STR_IMM (code, ARMREG_R0, ins->inst_basereg, ins->inst_offset);
+               } else {
+                       code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
+                       ARM_STR_REG_REG (code, ARMREG_R0, ins->inst_basereg, ARMREG_LR);
+               }
+       }
+
+       /* Initialize ss_trigger_page_var */
+       {
+               MonoInst *info_var = cfg->arch.seq_point_info_var;
+               MonoInst *ss_trigger_page_var = cfg->arch.ss_trigger_page_var;
+               int dreg = ARMREG_LR;
+
+               if (info_var) {
+                       g_assert (info_var->opcode == OP_REGOFFSET);
+                       g_assert (arm_is_imm12 (info_var->inst_offset));
+
+                       ARM_LDR_IMM (code, dreg, info_var->inst_basereg, info_var->inst_offset);
+                       /* Load the trigger page addr */
+                       ARM_LDR_IMM (code, dreg, dreg, G_STRUCT_OFFSET (SeqPointInfo, ss_trigger_page));
+                       ARM_STR_IMM (code, dreg, ss_trigger_page_var->inst_basereg, ss_trigger_page_var->inst_offset);
+               }
+       }
+
        cfg->code_len = code - cfg->native_code;
        g_assert (cfg->code_len < cfg->code_size);
        g_free (cinfo);
@@ -4089,6 +4820,7 @@ mono_arch_emit_epilog (MonoCompile *cfg)
        int pos, i, rot_amount;
        int max_epilog_size = 16 + 20*4;
        guint8 *code;
+       CallInfo *cinfo;
 
        if (cfg->method->save_lmf)
                max_epilog_size += 128;
@@ -4115,6 +4847,19 @@ mono_arch_emit_epilog (MonoCompile *cfg)
        }
        pos = 0;
 
+       /* Load returned vtypes into registers if needed */
+       cinfo = cfg->arch.cinfo;
+       if (cinfo->ret.storage == RegTypeStructByVal) {
+               MonoInst *ins = cfg->ret;
+
+               if (arm_is_imm12 (ins->inst_offset)) {
+                       ARM_LDR_IMM (code, ARMREG_R0, ins->inst_basereg, ins->inst_offset);
+               } else {
+                       code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
+                       ARM_LDR_REG_REG (code, ARMREG_R0, ins->inst_basereg, ARMREG_LR);
+               }
+       }
+
        if (method->save_lmf) {
                int lmf_offset;
                /* all but r0-r3, sp and pc */
@@ -4170,6 +4915,8 @@ exception_id_by_name (const char *name)
                return MONO_EXC_NULL_REF;
        if (strcmp (name, "ArrayTypeMismatchException") == 0)
                return MONO_EXC_ARRAY_TYPE_MISMATCH;
+       if (strcmp (name, "ArgumentException") == 0)
+               return MONO_EXC_ARGUMENT;
        g_error ("Unknown intrinsic exception %s\n", name);
        return -1;
 }
@@ -4180,10 +4927,15 @@ mono_arch_emit_exceptions (MonoCompile *cfg)
        MonoJumpInfo *patch_info;
        int i;
        guint8 *code;
-       const guint8* exc_throw_pos [MONO_EXC_INTRINS_NUM] = {NULL};
-       guint8 exc_throw_found [MONO_EXC_INTRINS_NUM] = {0};
+       guint8* exc_throw_pos [MONO_EXC_INTRINS_NUM];
+       guint8 exc_throw_found [MONO_EXC_INTRINS_NUM];
        int max_epilog_size = 50;
 
+       for (i = 0; i < MONO_EXC_INTRINS_NUM; i++) {
+               exc_throw_pos [i] = NULL;
+               exc_throw_found [i] = 0;
+       }
+
        /* count the number of exception infos */
      
        /* 
@@ -4249,6 +5001,8 @@ mono_arch_emit_exceptions (MonoCompile *cfg)
 
 }
 
+#endif /* #ifndef DISABLE_JIT */
+
 static gboolean tls_offset_inited = FALSE;
 
 void
@@ -4286,12 +5040,6 @@ mono_arch_get_domain_intrinsic (MonoCompile* cfg)
        return mono_get_domain_intrinsic (cfg);
 }
 
-MonoInst*
-mono_arch_get_thread_intrinsic (MonoCompile* cfg)
-{
-       return mono_get_thread_intrinsic (cfg);
-}
-
 guint32
 mono_arch_get_patch_offset (guint8 *code)
 {
@@ -4306,6 +5054,8 @@ mono_arch_flush_register_windows (void)
 
 #ifdef MONO_ARCH_HAVE_IMT
 
+#ifndef DISABLE_JIT
+
 void
 mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoInst *imt_arg)
 {
@@ -4326,7 +5076,7 @@ mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoInst *imt
 
                        mono_call_inst_add_outarg_reg (cfg, call, method_reg, ARMREG_V5, FALSE);
                }
-       } else if (cfg->generic_context) {
+       } else if (cfg->generic_context || imt_arg || mono_use_llvm) {
 
                /* Always pass in a register for simplicity */
                call->dynamic_imt_arg = TRUE;
@@ -4349,11 +5099,18 @@ mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoInst *imt
        }
 }
 
+#endif /* DISABLE_JIT */
+
 MonoMethod*
 mono_arch_find_imt_method (mgreg_t *regs, guint8 *code)
 {
        guint32 *code_ptr = (guint32*)code;
        code_ptr -= 2;
+
+       if (mono_use_llvm)
+               /* Passed in V5 */
+               return (MonoMethod*)regs [ARMREG_V5];
+
        /* 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]);
@@ -4366,12 +5123,6 @@ mono_arch_find_imt_method (mgreg_t *regs, guint8 *code)
                return (MonoMethod*) code_ptr [1];
 }
 
-MonoObject*
-mono_arch_find_this_argument (mgreg_t *regs, MonoMethod *method, MonoGenericSharingContext *gsctx)
-{
-       return mono_arch_get_this_arg_from_call (gsctx, mono_method_signature (method), regs, NULL);
-}
-
 MonoVTable*
 mono_arch_find_static_call_vtable (mgreg_t *regs, guint8 *code)
 {
@@ -4410,22 +5161,18 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
        size = BASE_SIZE;
        constant_pool_starts = g_new0 (guint32*, count);
 
-       /* 
-        * We might be called with a fail_tramp from the IMT builder code even if
-        * MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK is not defined.
-        */
-       //g_assert (!fail_tramp);
-
        for (i = 0; i < count; ++i) {
                MonoIMTCheckItem *item = imt_entries [i];
                if (item->is_equals) {
-                       if (!arm_is_imm12 (DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]))) {
+                       gboolean fail_case = !item->check_target_idx && fail_tramp;
+
+                       if (item->has_target_code || !arm_is_imm12 (DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]))) {
                                item->chunk_size += 32;
                                large_offsets = TRUE;
                        }
 
-                       if (item->check_target_idx) {
-                               if (!item->compare_done)
+                       if (item->check_target_idx || fail_case) {
+                               if (!item->compare_done || fail_case)
                                        item->chunk_size += CMP_SIZE;
                                item->chunk_size += BRANCH_SIZE;
                        } else {
@@ -4433,6 +5180,10 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
                                item->chunk_size += WMC_SIZE;
 #endif
                        }
+                       if (fail_case) {
+                               item->chunk_size += 16;
+                               large_offsets = TRUE;
+                       }
                        item->chunk_size += CALL_SIZE;
                } else {
                        item->chunk_size += BSEARCH_ENTRY_SIZE;
@@ -4444,7 +5195,11 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
        if (large_offsets)
                size += 4 * count; /* The ARM_ADD_REG_IMM to pop the stack */
 
-       start = code = mono_domain_code_reserve (domain, size);
+       if (fail_tramp)
+               code = mono_method_alloc_generic_virtual_thunk (domain, size);
+       else
+               code = mono_domain_code_reserve (domain, size);
+       start = code;
 
 #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);
@@ -4462,20 +5217,27 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
        vtable_target = code;
        ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
 
-       /* R0 == 0 means we are called from AOT code. In this case, V5 contains the IMT method */
-       ARM_CMP_REG_IMM8 (code, ARMREG_R0, 0);
-       ARM_MOV_REG_REG_COND (code, ARMREG_R0, ARMREG_V5, ARMCOND_EQ);
+       if (mono_use_llvm) {
+               /* LLVM always passes the IMT method in R5 */
+               ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_V5);
+       } else {
+               /* R0 == 0 means we are called from AOT code. In this case, V5 contains the IMT method */
+               ARM_CMP_REG_IMM8 (code, ARMREG_R0, 0);
+               ARM_MOV_REG_REG_COND (code, ARMREG_R0, ARMREG_V5, ARMCOND_EQ);
+       }
 
        for (i = 0; i < count; ++i) {
                MonoIMTCheckItem *item = imt_entries [i];
-               arminstr_t *imt_method = NULL, *vtable_offset_ins = NULL;
+               arminstr_t *imt_method = NULL, *vtable_offset_ins = NULL, *target_code_ins = NULL;
                gint32 vtable_offset;
 
                item->code_target = (guint8*)code;
 
                if (item->is_equals) {
-                       if (item->check_target_idx) {
-                               if (!item->compare_done) {
+                       gboolean fail_case = !item->check_target_idx && fail_tramp;
+
+                       if (item->check_target_idx || fail_case) {
+                               if (!item->compare_done || fail_case) {
                                        imt_method = code;
                                        ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
                                        ARM_CMP_REG_REG (code, ARMREG_R0, ARMREG_R1);
@@ -4494,29 +5256,56 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
 #endif
                        }
 
-                       vtable_offset = DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]);
-                       if (!arm_is_imm12 (vtable_offset)) {
-                               /* 
-                                * We need to branch to a computed address but we don't have
-                                * a free register to store it, since IP must contain the 
-                                * vtable address. So we push the two values to the stack, and
-                                * load them both using LDM.
-                                */
-                               /* Compute target address */
-                               vtable_offset_ins = code;
+                       if (item->has_target_code) {
+                               target_code_ins = code;
+                               /* Load target address */
                                ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
-                               ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_IP, ARMREG_R1);
                                /* Save it to the fourth slot */
                                ARM_STR_IMM (code, ARMREG_R1, ARMREG_SP, 3 * sizeof (gpointer));
                                /* Restore registers and branch */
                                ARM_POP4 (code, ARMREG_R0, ARMREG_R1, ARMREG_IP, ARMREG_PC);
                                
-                               code = arm_emit_value_and_patch_ldr (code, vtable_offset_ins, vtable_offset);
+                               code = arm_emit_value_and_patch_ldr (code, target_code_ins, (gsize)item->value.target_code);
                        } else {
-                               ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
-                               if (large_offsets)
-                                       ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 2 * sizeof (gpointer));
-                               ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, vtable_offset);
+                               vtable_offset = DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]);
+                               if (!arm_is_imm12 (vtable_offset)) {
+                                       /* 
+                                        * We need to branch to a computed address but we don't have
+                                        * a free register to store it, since IP must contain the 
+                                        * vtable address. So we push the two values to the stack, and
+                                        * load them both using LDM.
+                                        */
+                                       /* Compute target address */
+                                       vtable_offset_ins = code;
+                                       ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
+                                       ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_IP, ARMREG_R1);
+                                       /* Save it to the fourth slot */
+                                       ARM_STR_IMM (code, ARMREG_R1, ARMREG_SP, 3 * sizeof (gpointer));
+                                       /* Restore registers and branch */
+                                       ARM_POP4 (code, ARMREG_R0, ARMREG_R1, ARMREG_IP, ARMREG_PC);
+                               
+                                       code = arm_emit_value_and_patch_ldr (code, vtable_offset_ins, vtable_offset);
+                               } else {
+                                       ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
+                                       if (large_offsets)
+                                               ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 2 * sizeof (gpointer));
+                                       ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, vtable_offset);
+                               }
+                       }
+
+                       if (fail_case) {
+                               arm_patch (item->jmp_code, (guchar*)code);
+
+                               target_code_ins = code;
+                               /* Load target address */
+                               ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0);
+                               /* Save it to the fourth slot */
+                               ARM_STR_IMM (code, ARMREG_R1, ARMREG_SP, 3 * sizeof (gpointer));
+                               /* Restore registers and branch */
+                               ARM_POP4 (code, ARMREG_R0, ARMREG_R1, ARMREG_IP, ARMREG_PC);
+                               
+                               code = arm_emit_value_and_patch_ldr (code, target_code_ins, (gsize)fail_tramp);
+                               item->jmp_code = NULL;
                        }
 
                        if (imt_method)
@@ -4582,14 +5371,223 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
 gpointer
 mono_arch_context_get_int_reg (MonoContext *ctx, int reg)
 {
-       if (reg >= 4 && reg <= 11)
-               return (gpointer)ctx->regs [reg - 4];
-       else if (reg == ARMREG_IP)
-               return (gpointer)ctx->regs [8];
-       else if (reg == ARMREG_LR)
-               return (gpointer)ctx->regs [9];
-       else {
-               g_assert_not_reached ();
-               return NULL;
+       if (reg == ARMREG_SP)
+               return (gpointer)ctx->esp;
+       else
+               return (gpointer)ctx->regs [reg];
+}
+
+/*
+ * mono_arch_set_breakpoint:
+ *
+ *   Set a breakpoint at the native code corresponding to JI at NATIVE_OFFSET.
+ * The location should contain code emitted by OP_SEQ_POINT.
+ */
+void
+mono_arch_set_breakpoint (MonoJitInfo *ji, guint8 *ip)
+{
+       guint8 *code = ip;
+       guint32 native_offset = ip - (guint8*)ji->code_start;
+
+       if (ji->from_aot) {
+               SeqPointInfo *info = mono_arch_get_seq_point_info (mono_domain_get (), ji->code_start);
+
+               g_assert (native_offset % 4 == 0);
+               g_assert (info->bp_addrs [native_offset / 4] == 0);
+               info->bp_addrs [native_offset / 4] = bp_trigger_page;
+       } else {
+               int dreg = ARMREG_LR;
+
+               /* Read from another trigger page */
+               ARM_LDR_IMM (code, dreg, ARMREG_PC, 0);
+               ARM_B (code, 0);
+               *(int*)code = (int)bp_trigger_page;
+               code += 4;
+               ARM_LDR_IMM (code, dreg, dreg, 0);
+
+               mono_arch_flush_icache (code - 16, 16);
+
+#if 0
+               /* This is currently implemented by emitting an SWI instruction, which 
+                * qemu/linux seems to convert to a SIGILL.
+                */
+               *(int*)code = (0xef << 24) | 8;
+               code += 4;
+               mono_arch_flush_icache (code - 4, 4);
+#endif
+       }
+}
+
+/*
+ * mono_arch_clear_breakpoint:
+ *
+ *   Clear the breakpoint at IP.
+ */
+void
+mono_arch_clear_breakpoint (MonoJitInfo *ji, guint8 *ip)
+{
+       guint8 *code = ip;
+       int i;
+
+       if (ji->from_aot) {
+               guint32 native_offset = ip - (guint8*)ji->code_start;
+               SeqPointInfo *info = mono_arch_get_seq_point_info (mono_domain_get (), ji->code_start);
+
+               g_assert (native_offset % 4 == 0);
+               g_assert (info->bp_addrs [native_offset / 4] == bp_trigger_page);
+               info->bp_addrs [native_offset / 4] = 0;
+       } else {
+               for (i = 0; i < 4; ++i)
+                       ARM_NOP (code);
+
+               mono_arch_flush_icache (ip, code - ip);
+       }
+}
+       
+/*
+ * mono_arch_start_single_stepping:
+ *
+ *   Start single stepping.
+ */
+void
+mono_arch_start_single_stepping (void)
+{
+       mono_mprotect (ss_trigger_page, mono_pagesize (), 0);
+}
+       
+/*
+ * mono_arch_stop_single_stepping:
+ *
+ *   Stop single stepping.
+ */
+void
+mono_arch_stop_single_stepping (void)
+{
+       mono_mprotect (ss_trigger_page, mono_pagesize (), MONO_MMAP_READ);
+}
+
+#if __APPLE__
+#define DBG_SIGNAL SIGBUS
+#else
+#define DBG_SIGNAL SIGSEGV
+#endif
+
+/*
+ * mono_arch_is_single_step_event:
+ *
+ *   Return whenever the machine state in SIGCTX corresponds to a single
+ * step event.
+ */
+gboolean
+mono_arch_is_single_step_event (void *info, void *sigctx)
+{
+       siginfo_t *sinfo = info;
+
+       /* Sometimes the address is off by 4 */
+       if (sinfo->si_addr >= ss_trigger_page && (guint8*)sinfo->si_addr <= (guint8*)ss_trigger_page + 128)
+               return TRUE;
+       else
+               return FALSE;
+}
+
+/*
+ * mono_arch_is_breakpoint_event:
+ *
+ *   Return whenever the machine state in SIGCTX corresponds to a breakpoint event.
+ */
+gboolean
+mono_arch_is_breakpoint_event (void *info, void *sigctx)
+{
+       siginfo_t *sinfo = info;
+
+       if (sinfo->si_signo == DBG_SIGNAL) {
+               /* Sometimes the address is off by 4 */
+               if (sinfo->si_addr >= bp_trigger_page && (guint8*)sinfo->si_addr <= (guint8*)bp_trigger_page + 128)
+                       return TRUE;
+               else
+                       return FALSE;
+       } else {
+               return FALSE;
+       }
+}
+
+guint8*
+mono_arch_get_ip_for_breakpoint (MonoJitInfo *ji, MonoContext *ctx)
+{
+       guint8 *ip = MONO_CONTEXT_GET_IP (ctx);
+
+       if (ji->from_aot)
+               ip -= 6 * 4;
+       else
+               ip -= 12;
+
+       return ip;
+}
+
+guint8*
+mono_arch_get_ip_for_single_step (MonoJitInfo *ji, MonoContext *ctx)
+{
+       guint8 *ip = MONO_CONTEXT_GET_IP (ctx);
+
+       ip += 4;
+
+       return ip;
+}
+
+/*
+ * mono_arch_skip_breakpoint:
+ *
+ *   See mini-amd64.c for docs.
+ */
+void
+mono_arch_skip_breakpoint (MonoContext *ctx)
+{
+       MONO_CONTEXT_SET_IP (ctx, (guint8*)MONO_CONTEXT_GET_IP (ctx) + 4);
+}
+
+/*
+ * mono_arch_skip_single_step:
+ *
+ *   See mini-amd64.c for docs.
+ */
+void
+mono_arch_skip_single_step (MonoContext *ctx)
+{
+       MONO_CONTEXT_SET_IP (ctx, (guint8*)MONO_CONTEXT_GET_IP (ctx) + 4);
+}
+
+/*
+ * mono_arch_get_seq_point_info:
+ *
+ *   See mini-amd64.c for docs.
+ */
+gpointer
+mono_arch_get_seq_point_info (MonoDomain *domain, guint8 *code)
+{
+       SeqPointInfo *info;
+       MonoJitInfo *ji;
+
+       // FIXME: Add a free function
+
+       mono_domain_lock (domain);
+       info = g_hash_table_lookup (domain_jit_info (domain)->arch_seq_points, 
+                                                               code);
+       mono_domain_unlock (domain);
+
+       if (!info) {
+               ji = mono_jit_info_table_find (domain, (char*)code);
+               g_assert (ji);
+
+               info = g_malloc0 (sizeof (SeqPointInfo) + ji->code_size);
+
+               info->ss_trigger_page = ss_trigger_page;
+               info->bp_trigger_page = bp_trigger_page;
+
+               mono_domain_lock (domain);
+               g_hash_table_insert (domain_jit_info (domain)->arch_seq_points,
+                                                        code, info);
+               mono_domain_unlock (domain);
        }
+
+       return info;
 }