New tests.
[mono.git] / mono / mini / mini-arm.c
index 1f3cc7bae629f910590649a26a15a08b170f72d4..0a72637f062ad53b5efd0e90572c5925241fab7c 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"
@@ -39,6 +40,22 @@ 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
@@ -66,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)
@@ -98,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)
 {
@@ -209,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
@@ -489,7 +531,11 @@ 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);
 }
 
 /*
@@ -507,6 +553,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;
@@ -538,12 +592,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)
@@ -643,6 +700,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
@@ -664,7 +723,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"
@@ -677,20 +736,22 @@ 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;
 
@@ -698,6 +759,7 @@ typedef struct {
        int nargs;
        guint32 stack_usage;
        guint32 struct_ret;
+       gboolean vtype_retaddr;
        ArgInfo ret;
        ArgInfo sig_cookie;
        ArgInfo args [1];
@@ -719,9 +781,10 @@ 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 {
@@ -741,7 +804,7 @@ add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple)
                        /* 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__
@@ -753,13 +816,14 @@ add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple)
 #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 (i8_align == 8 && ((*gr) & 1))
                                (*gr) ++;
 #endif
+                       ainfo->storage = RegTypeIRegPair;
                        ainfo->reg = *gr;
                }
                (*gr) ++;
@@ -768,20 +832,33 @@ add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple)
 }
 
 static CallInfo*
-get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke)
+get_call_info (MonoMemPool *mp, MonoMethodSignature *sig, gboolean is_pinvoke)
 {
        guint i, gr;
        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 {
+                       add_general (&gr, &stack_size, &cinfo->ret, TRUE);
+                       cinfo->struct_ret = ARMREG_R0;
+                       cinfo->vtype_retaddr = TRUE;
+               }
        }
 
        n = 0;
@@ -792,13 +869,13 @@ get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke)
         DEBUG(printf("params: %d\n", sig->param_count));
        for (i = 0; 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);
@@ -842,7 +919,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++;
@@ -873,7 +950,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))
@@ -910,6 +987,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) {
@@ -930,27 +1016,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:
@@ -966,6 +1058,7 @@ get_call_info (MonoMethodSignature *sig, gboolean is_pinvoke)
        return cinfo;
 }
 
+#ifndef DISABLE_JIT
 
 /*
  * Set var information according to the calling convention. arm version.
@@ -976,9 +1069,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->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;
@@ -987,7 +1088,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
@@ -1009,12 +1110,9 @@ mono_arch_allocate_vars (MonoCompile *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;
@@ -1046,42 +1144,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.
@@ -1090,46 +1214,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++;
        }
@@ -1146,18 +1282,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->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->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)
 {
@@ -1169,7 +1406,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 (NULL, sig, sig->pinvoke);
 
        for (i = 0; i < n; ++i) {
                ArgInfo *ainfo = cinfo->args + i;
@@ -1182,14 +1419,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);
@@ -1340,15 +1578,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;
@@ -1386,10 +1633,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
@@ -1429,158 +1680,167 @@ 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 (MonoMethodSignature *sig)
+dyn_call_supported (CallInfo *cinfo, MonoMethodSignature *sig)
 {
        int i;
 
-#ifdef PLATFORM_WIN32
-       return FALSE;
-#endif
-
-       if (sig->hasthis + sig->param_count > PARAM_REGS)
+       if (sig->hasthis + sig->param_count > PARAM_REGS + DYN_CALL_STACK_ARGS)
                return FALSE;
-       switch (sig->ret->type) {
-       case MONO_TYPE_VOID:
-       case MONO_TYPE_I1:
-       case MONO_TYPE_U1:
-       case MONO_TYPE_I2:
-       case MONO_TYPE_U2:
-       case MONO_TYPE_I4:
-       case MONO_TYPE_U4:
-       case MONO_TYPE_I:
-       case MONO_TYPE_U:
-               /*
-       case MONO_TYPE_I8:
-       case MONO_TYPE_U8:
-               */
-       case MONO_TYPE_BOOLEAN:
-       case MONO_TYPE_CHAR:
-       case MONO_TYPE_STRING:
-       case MONO_TYPE_CLASS:  
-       case MONO_TYPE_ARRAY:
-       case MONO_TYPE_SZARRAY:
-       case MONO_TYPE_OBJECT:
-       case MONO_TYPE_PTR:
-               break;
-       case MONO_TYPE_GENERICINST:
-               if (!MONO_TYPE_IS_REFERENCE (sig->ret))
-                       return FALSE;
+
+       switch (cinfo->ret.storage) {
+       case RegTypeNone:
+       case RegTypeGeneral:
+       case RegTypeIRegPair:
+       case RegTypeStructByAddr:
                break;
-       case MONO_TYPE_VAR:
-       case MONO_TYPE_MVAR:
-               /* assume gshared */
+       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;
 
-       handle_enum:
                switch (t->type) {
                case MONO_TYPE_R4:
                case MONO_TYPE_R8:
-               case MONO_TYPE_TYPEDBYREF:
-                       return FALSE;
-               case MONO_TYPE_VALUETYPE:
-                       if (t->data.klass->enumtype) {
-                               t = mono_class_enum_basetype (t->data.klass);
-                               goto handle_enum;
-                       }
+#ifdef MONO_ARCH_SOFT_FLOAT
                        return FALSE;
-               case MONO_TYPE_GENERICINST:
-                       if (!MONO_TYPE_IS_REFERENCE (t))
-                               return FALSE;
+#else
                        break;
-               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_BOOLEAN:
-               case MONO_TYPE_CHAR:
-               case MONO_TYPE_I1:
-               case MONO_TYPE_U1:
-               case MONO_TYPE_I2:
-               case MONO_TYPE_U2:
-               case MONO_TYPE_I4:
-               case MONO_TYPE_U4:
-               case MONO_TYPE_I:
-               case MONO_TYPE_U:
+#endif
                        /*
                case MONO_TYPE_I8:
                case MONO_TYPE_U8:
+                       return FALSE;
                        */
-                       break;
-               case MONO_TYPE_VAR:
-               case MONO_TYPE_MVAR:
-                       /* assume gshared */
-                       break;
                default:
-                       return FALSE;
                        break;
                }
        }
+
        return TRUE;
 }
 
-typedef struct {
-       MonoMethodSignature *sig;
-} DynCallInfo;
-
-typedef struct {
-       mgreg_t regs [PARAM_REGS];
-       mgreg_t res;
-} DynCallArgs;
-
 MonoDynCallInfo*
 mono_arch_dyn_call_prepare (MonoMethodSignature *sig)
 {
-       DynCallInfo *info;
+       ArchDynCallInfo *info;
+       CallInfo *cinfo;
 
-       if (!dyn_call_supported (sig))
+       cinfo = get_call_info (NULL, sig, FALSE);
+
+       if (!dyn_call_supported (cinfo, sig)) {
+               g_free (cinfo);
                return NULL;
+       }
 
-       info = g_new0 (DynCallInfo, 1);
-       // FIXME: Preprocess the info to speed up get_dyn_call_args ().
+       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_get_dyn_call_args (MonoDynCallInfo *info, gpointer **args, guint8 *buf, int buf_len)
+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;
-       MonoMethodSignature *sig = ((DynCallInfo*)info)->sig;
+       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 (sig->hasthis) {
+
+       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 [greg ++] = (mgreg_t)*(args [arg_index ++]);
+                       p->regs [slot] = (mgreg_t)*arg;
                        continue;
                }
 
@@ -1593,50 +1853,74 @@ mono_arch_get_dyn_call_args (MonoDynCallInfo *info, gpointer **args, guint8 *buf
                case MONO_TYPE_PTR:
                case MONO_TYPE_I:
                case MONO_TYPE_U:
-                       /*
-               case MONO_TYPE_I8:
-               case MONO_TYPE_U8:
-                       */
-                       p->regs [greg ++] = (mgreg_t)*(args [arg_index ++]);
-                       break;
-               case MONO_TYPE_GENERICINST:
-                       g_assert (MONO_TYPE_IS_REFERENCE (t));
-                       p->regs [greg ++] = (mgreg_t)*(args [arg_index ++]);
+                       p->regs [slot] = (mgreg_t)*arg;
                        break;
                case MONO_TYPE_BOOLEAN:
                case MONO_TYPE_U1:
-                       p->regs [greg ++] = *(guint8*)(args [arg_index ++]);
+                       p->regs [slot] = *(guint8*)arg;
                        break;
                case MONO_TYPE_I1:
-                       p->regs [greg ++] = *(gint8*)(args [arg_index ++]);
+                       p->regs [slot] = *(gint8*)arg;
                        break;
                case MONO_TYPE_I2:
-                       p->regs [greg ++] = *(gint16*)(args [arg_index ++]);
+                       p->regs [slot] = *(gint16*)arg;
                        break;
                case MONO_TYPE_U2:
                case MONO_TYPE_CHAR:
-                       p->regs [greg ++] = *(guint16*)(args [arg_index ++]);
+                       p->regs [slot] = *(guint16*)arg;
                        break;
                case MONO_TYPE_I4:
-                       p->regs [greg ++] = *(gint32*)(args [arg_index ++]);
+                       p->regs [slot] = *(gint32*)arg;
                        break;
                case MONO_TYPE_U4:
-                       p->regs [greg ++] = *(guint32*)(args [arg_index ++]);
+                       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 ();
                }
        }
-
-       g_assert (greg <= PARAM_REGS);
 }
 
 void
-mono_arch_get_dyn_call_ret (MonoDynCallInfo *info, guint8 *buf, guint8 *ret)
+mono_arch_finish_dyn_call (MonoDynCallInfo *info, guint8 *buf)
 {
-       MonoMethodSignature *sig = ((DynCallInfo*)info)->sig;
+       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 (sig->ret->type) {
+       switch (mono_type_get_underlying_type (sig->ret)->type) {
        case MONO_TYPE_VOID:
                *(gpointer*)ret = NULL;
                break;
@@ -1648,44 +1932,66 @@ mono_arch_get_dyn_call_ret (MonoDynCallInfo *info, guint8 *buf, guint8 *ret)
        case MONO_TYPE_I:
        case MONO_TYPE_U:
        case MONO_TYPE_PTR:
-               *(gpointer*)ret = (gpointer)((DynCallArgs*)buf)->res;
-               break;
-       case MONO_TYPE_GENERICINST:
-               *(gpointer*)ret = (gpointer)((DynCallArgs*)buf)->res;
+               *(gpointer*)ret = (gpointer)res;
                break;
        case MONO_TYPE_I1:
-               *(gint8*)ret = ((DynCallArgs*)buf)->res;
+               *(gint8*)ret = res;
                break;
        case MONO_TYPE_U1:
        case MONO_TYPE_BOOLEAN:
-               *(guint8*)ret = ((DynCallArgs*)buf)->res;
+               *(guint8*)ret = res;
                break;
        case MONO_TYPE_I2:
-               *(gint16*)ret = ((DynCallArgs*)buf)->res;
+               *(gint16*)ret = res;
                break;
        case MONO_TYPE_U2:
        case MONO_TYPE_CHAR:
-               *(guint16*)ret = ((DynCallArgs*)buf)->res;
+               *(guint16*)ret = res;
                break;
        case MONO_TYPE_I4:
-               *(gint32*)ret = ((DynCallArgs*)buf)->res;
+               *(gint32*)ret = res;
                break;
        case MONO_TYPE_U4:
-               *(guint32*)ret = ((DynCallArgs*)buf)->res;
+               *(guint32*)ret = res;
                break;
-               /*
        case MONO_TYPE_I8:
-               *(gint64*)ret = ((DynCallArgs*)buf)->res;
-               break;
        case MONO_TYPE_U8:
-               *(guint64*)ret = ((DynCallArgs*)buf)->res;
+               /* 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)
  */
@@ -2235,8 +2541,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;
@@ -2317,6 +2628,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;
@@ -2386,10 +2699,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;
@@ -2410,8 +2726,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;
@@ -2457,7 +2773,7 @@ arm_patch (guchar *code, const guchar *target)
                        }
                }
                
-               handle_thunk (TRUE, code, target);
+               handle_thunk (domain, TRUE, code, target);
                return;
        }
 
@@ -2557,6 +2873,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
@@ -2634,6 +2956,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:
  *
@@ -2657,7 +2987,7 @@ emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code)
 
        pos = 0;
 
-       cinfo = get_call_info (sig, sig->pinvoke);
+       cinfo = get_call_info (NULL, sig, sig->pinvoke);
 
        if (MONO_TYPE_ISSTRUCT (sig->ret)) {
                ArgInfo *ainfo = &cinfo->ret;
@@ -2670,13 +3000,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;
                                /*
@@ -2690,7 +3020,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:
@@ -2712,14 +3042,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;
@@ -2739,7 +3069,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;
@@ -2753,8 +3083,6 @@ emit_load_volatile_arguments (MonoCompile *cfg, guint8 *code)
        return code;
 }
 
-#ifndef DISABLE_JIT
-
 void
 mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
 {
@@ -2965,6 +3293,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);
@@ -3225,15 +3637,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:
@@ -3332,10 +3738,17 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        /* 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);
@@ -3343,6 +3756,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        /* 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: {
@@ -3403,6 +3817,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;
@@ -3951,9 +4366,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 {\
@@ -4036,10 +4453,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:
  * 
@@ -4173,26 +4592,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 (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 {
@@ -4206,7 +4638,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))
@@ -4239,13 +4671,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 {
@@ -4299,9 +4731,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;
@@ -4322,7 +4754,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);
@@ -4391,6 +4823,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);
@@ -4405,6 +4875,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;
@@ -4431,6 +4902,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 */
@@ -4565,6 +5049,8 @@ mono_arch_emit_exceptions (MonoCompile *cfg)
 
 }
 
+#endif /* #ifndef DISABLE_JIT */
+
 static gboolean tls_offset_inited = FALSE;
 
 void
@@ -4602,12 +5088,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)
 {
@@ -4622,6 +5102,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)
 {
@@ -4665,6 +5147,8 @@ 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)
 {
@@ -4682,12 +5166,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)
 {
@@ -4898,16 +5376,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 if (reg == ARMREG_SP)
+       if (reg == ARMREG_SP)
                return (gpointer)ctx->esp;
-       else {
-               g_assert_not_reached ();
-               return NULL;
+       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;
+}