Merge pull request #2865 from akoeplinger/maccore-fixes
[mono.git] / mono / mini / mini-arm.c
index b617d3fb96d50221bc960a7df1b993d35ce2ef7c..86d4e123919cb97908c28153de6b512f1bc5d644 100644 (file)
@@ -8,6 +8,7 @@
  * (C) 2003 Ximian, Inc.
  * Copyright 2003-2011 Novell, Inc (http://www.novell.com)
  * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
  */
 #include "mini.h"
 #include <string.h>
@@ -29,9 +30,9 @@
 #include "mini-gc.h"
 #include "mono/arch/arm/arm-vfp-codegen.h"
 
-#if defined(HAVE_KW_THREAD) && defined(__linux__) \
+#if (defined(HAVE_KW_THREAD) && defined(__linux__) && defined(__ARM_EABI__)) \
        || defined(TARGET_ANDROID) \
-       || defined(TARGET_IOS)
+       || (defined(TARGET_IOS) && !defined(TARGET_WATCHOS))
 #define HAVE_FAST_TLS
 #endif
 
@@ -95,14 +96,15 @@ void sys_icache_invalidate (void *start, size_t len);
 #endif
 
 /* This mutex protects architecture specific caches */
-#define mono_mini_arch_lock() mono_mutex_lock (&mini_arch_mutex)
-#define mono_mini_arch_unlock() mono_mutex_unlock (&mini_arch_mutex)
+#define mono_mini_arch_lock() mono_os_mutex_lock (&mini_arch_mutex)
+#define mono_mini_arch_unlock() mono_os_mutex_unlock (&mini_arch_mutex)
 static mono_mutex_t mini_arch_mutex;
 
 static gboolean v5_supported = FALSE;
 static gboolean v6_supported = FALSE;
 static gboolean v7_supported = FALSE;
 static gboolean v7s_supported = FALSE;
+static gboolean v7k_supported = FALSE;
 static gboolean thumb_supported = FALSE;
 static gboolean thumb2_supported = FALSE;
 /*
@@ -146,8 +148,6 @@ static int vfp_scratch2 = ARM_VFP_D1;
 
 static int i8_align;
 
-static volatile int ss_trigger_var = 0;
-
 static gpointer single_step_tramp, breakpoint_tramp;
 
 /*
@@ -646,30 +646,6 @@ emit_restore_lmf (MonoCompile *cfg, guint8 *code, gint32 lmf_offset)
 
 #endif /* #ifndef DISABLE_JIT */
 
-#ifndef MONO_CROSS_COMPILE
-static gboolean
-mono_arm_have_fast_tls (void)
-{
-       if (mini_get_debug_options ()->arm_use_fallback_tls)
-               return FALSE;
-#if (defined(HAVE_KW_THREAD) && defined(__linux__)) \
-       || defined(TARGET_ANDROID)
-       guint32* kuser_get_tls = (void*)0xffff0fe0;
-       guint32 expected [] = {0xee1d0f70, 0xe12fff1e};
-
-       /* Expecting mrc + bx lr in the kuser_get_tls kernel helper */
-       return memcmp (kuser_get_tls, expected, 8) == 0;
-#elif defined(TARGET_IOS)
-       guint32 expected [] = {0x1f70ee1d, 0x0103f021, 0x0020f851, 0xbf004770};
-       /* Discard thumb bit */
-       guint32* pthread_getspecific_addr = (guint32*) ((guint32)pthread_getspecific & 0xfffffffe);
-       return memcmp ((void*)pthread_getspecific_addr, expected, 16) == 0;
-#else
-       return FALSE;
-#endif
-}
-#endif
-
 /*
  * mono_arm_have_tls_get:
  *
@@ -916,9 +892,8 @@ mono_arch_init (void)
 {
        const char *cpu_arch;
 
-       mono_mutex_init_recursive (&mini_arch_mutex);
+       mono_os_mutex_init_recursive (&mini_arch_mutex);
        if (mini_get_debug_options ()->soft_breakpoints) {
-               single_step_tramp = mini_get_single_step_trampoline ();
                breakpoint_tramp = mini_get_breakpoint_trampoline ();
        } else {
                ss_trigger_page = mono_valloc (NULL, mono_pagesize (), MONO_MMAP_READ|MONO_MMAP_32BIT);
@@ -929,7 +904,7 @@ mono_arch_init (void)
        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);
        mono_aot_register_jit_icall ("mono_arm_resume_unwind", mono_arm_resume_unwind);
-#if defined(ENABLE_GSHAREDVT)
+#if defined(MONO_ARCH_GSHAREDVT_SUPPORTED)
        mono_aot_register_jit_icall ("mono_arm_start_gsharedvt_call", mono_arm_start_gsharedvt_call);
 #endif
        mono_aot_register_jit_icall ("mono_arm_unaligned_stack", mono_arm_unaligned_stack);
@@ -969,8 +944,6 @@ mono_arch_init (void)
        v5_supported = mono_hwcap_arm_is_v5;
        v6_supported = mono_hwcap_arm_is_v6;
        v7_supported = mono_hwcap_arm_is_v7;
-       v7s_supported = mono_hwcap_arm_is_v7s;
-       v7k_supported = mono_hwcap_arm_is_v7k;
 
 #if defined(__APPLE__)
        /* iOS is special-cased here because we don't yet
@@ -1174,93 +1147,19 @@ mono_arch_regalloc_cost (MonoCompile *cfg, MonoMethodVar *vmv)
 
 #endif /* #ifndef DISABLE_JIT */
 
-#ifndef __GNUC_PREREQ
-#define __GNUC_PREREQ(maj, min) (0)
-#endif
-
 void
 mono_arch_flush_icache (guint8 *code, gint size)
 {
-#if defined(__native_client__)
+#if defined(MONO_CROSS_COMPILE) || defined(__native_client__)
   // For Native Client we don't have to flush i-cache here,
   // as it's being done by dyncode interface.
-#else
-
-#ifdef MONO_CROSS_COMPILE
 #elif __APPLE__
        sys_icache_invalidate (code, size);
-#elif __GNUC_PREREQ(4, 3)
-    __builtin___clear_cache (code, code + size);
-#elif __GNUC_PREREQ(4, 1)
-       __clear_cache (code, code + size);
-#elif defined(PLATFORM_ANDROID)
-       const int syscall = 0xf0002;
-       __asm __volatile (
-               "mov     r0, %0\n"                      
-               "mov     r1, %1\n"
-               "mov     r7, %2\n"
-               "mov     r2, #0x0\n"
-               "svc     0x00000000\n"
-               :
-               :       "r" (code), "r" (code + size), "r" (syscall)
-               :       "r0", "r1", "r7", "r2"
-               );
 #else
-       __asm __volatile ("mov r0, %0\n"
-                       "mov r1, %1\n"
-                       "mov r2, %2\n"
-                       "swi 0x9f0002       @ sys_cacheflush"
-                       : /* no outputs */
-                       : "r" (code), "r" (code + size), "r" (0)
-                       : "r0", "r1", "r3" );
+    __builtin___clear_cache (code, code + size);
 #endif
-#endif /* !__native_client__ */
 }
 
-typedef enum {
-       RegTypeNone,
-       /* Passed/returned in an ireg */
-       RegTypeGeneral,
-       /* Passed/returned in a pair of iregs */
-       RegTypeIRegPair,
-       /* Passed on the stack */
-       RegTypeBase,
-       /* First word in r3, second word on the stack */
-       RegTypeBaseGen,
-       /* FP value passed in either an ireg or a vfp reg */
-       RegTypeFP,
-       RegTypeStructByVal,
-       RegTypeStructByAddr,
-       /* gsharedvt argument passed by addr in greg */
-       RegTypeGSharedVtInReg,
-       /* gsharedvt argument passed by addr on stack */
-       RegTypeGSharedVtOnStack,
-       RegTypeHFA
-} ArgStorage;
-
-typedef struct {
-       gint32  offset;
-       guint16 vtsize; /* in param area */
-       /* RegTypeHFA */
-       int esize;
-       /* RegTypeHFA */
-       int nregs;
-       guint8  reg;
-       ArgStorage  storage;
-       gint32  struct_size;
-       guint8  size    : 4; /* 1, 2, 4, 8, or regs used by RegTypeStructByVal */
-} ArgInfo;
-
-typedef struct {
-       int nargs;
-       guint32 stack_usage;
-       /* The index of the vret arg in the argument list for RegTypeStructByAddr */
-       int vret_arg_index;
-       ArgInfo ret;
-       ArgInfo sig_cookie;
-       ArgInfo args [1];
-} CallInfo;
-
 #define DEBUG(a)
 
 static void inline
@@ -1583,7 +1482,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
                }
        }
 
-       DEBUG(printf("params: %d\n", sig->param_count));
+       DEBUG(g_print("params: %d\n", sig->param_count));
        for (i = pstart; i < sig->param_count; ++i) {
                ArgInfo *ainfo = &cinfo->args [n];
 
@@ -1595,9 +1494,9 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
                        /* Emit the signature cookie just before the implicit arguments */
                        add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE);
                }
-               DEBUG(printf("param %d: ", i));
+               DEBUG(g_print("param %d: ", i));
                if (sig->params [i]->byref) {
-                        DEBUG(printf("byref\n"));
+                        DEBUG(g_print("byref\n"));
                        add_general (&gr, &stack_size, ainfo, TRUE);
                        n++;
                        continue;
@@ -1667,7 +1566,10 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
                                        ainfo->reg = fpr;
                                        ainfo->nregs = nfields;
                                        ainfo->esize = esize;
-                                       fpr += nfields;
+                                       if (esize == 4)
+                                               fpr += nfields;
+                                       else
+                                               fpr += nfields * 2;
                                        break;
                                } else {
                                        fpr = ARM_VFP_F16;
@@ -1684,7 +1586,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
                                else
                                        size = mini_type_stack_size_full (t, &align, FALSE);
                        }
-                       DEBUG(printf ("load %d bytes struct\n", size));
+                       DEBUG(g_print ("load %d bytes struct\n", size));
                        align_size = size;
                        nwords = 0;
                        align_size += (sizeof (gpointer) - 1);
@@ -1772,7 +1674,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
        }
 
        /* align stack size to 8 */
-       DEBUG (printf ("      stack size: %d (%d)\n", (stack_size + 15) & ~15, stack_size));
+       DEBUG (g_print ("      stack size: %d (%d)\n", (stack_size + 15) & ~15, stack_size));
        stack_size = (stack_size + 7) & ~7;
 
        cinfo->stack_usage = stack_size;
@@ -1995,7 +1897,7 @@ mono_arch_allocate_vars (MonoCompile *cfg)
                ins->opcode = OP_REGOFFSET;
                ins->inst_basereg = cfg->frame_reg;
                if (G_UNLIKELY (cfg->verbose_level > 1)) {
-                       printf ("vret_addr =");
+                       g_print ("vret_addr =");
                        mono_print_ins (cfg->vret_addr);
                }
                offset += sizeof(gpointer);
@@ -2030,20 +1932,9 @@ mono_arch_allocate_vars (MonoCompile *cfg)
                offset += size;
        }
 
-       if (cfg->arch.seq_point_read_var) {
+       if (cfg->arch.seq_point_ss_method_var) {
                MonoInst *ins;
 
-               ins = cfg->arch.seq_point_read_var;
-
-               size = 4;
-               align = 4;
-               offset += align - 1;
-               offset &= ~(align - 1);
-               ins->opcode = OP_REGOFFSET;
-               ins->inst_basereg = cfg->frame_reg;
-               ins->inst_offset = offset;
-               offset += size;
-
                ins = cfg->arch.seq_point_ss_method_var;
                size = 4;
                align = 4;
@@ -2158,7 +2049,7 @@ mono_arch_allocate_vars (MonoCompile *cfg)
                        /* These arguments are saved to the stack in the prolog */
                        ins->inst_offset = offset;
                        if (cfg->verbose_level >= 2)
-                               printf ("arg %d allocated to %s+0x%0x.\n", i, mono_arch_regname (ins->inst_basereg), (int)ins->inst_offset);
+                               g_print ("arg %d allocated to %s+0x%0x.\n", i, mono_arch_regname (ins->inst_basereg), (int)ins->inst_offset);
                        // FIXME:
                        offset += 32;
                        break;
@@ -2227,16 +2118,14 @@ mono_arch_create_vars (MonoCompile *cfg)
        if (cinfo->ret.storage == RegTypeStructByAddr) {
                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 = ");
+                       g_print ("vret_addr = ");
                        mono_print_ins (cfg->vret_addr);
                }
        }
 
        if (cfg->gen_sdb_seq_points) {
                if (cfg->soft_breakpoints) {
-                       MonoInst *ins = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
-                       ins->flags |= MONO_INST_VOLATILE;
-                       cfg->arch.seq_point_read_var = ins;
+                       MonoInst *ins;
 
                        ins = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
                        ins->flags |= MONO_INST_VOLATILE;
@@ -2336,7 +2225,7 @@ mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig)
                case RegTypeIRegPair:
                case RegTypeBase:
                case RegTypeBaseGen:
-                       linfo->args [i].storage = LLVMArgInIReg;
+                       linfo->args [i].storage = LLVMArgNormal;
                        break;
                case RegTypeStructByVal:
                        linfo->args [i].storage = LLVMArgAsIArgs;
@@ -2421,13 +2310,13 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
                        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);
-                               ins->sreg1 = in->dreg + 1;
+                               ins->sreg1 = MONO_LVREG_LS (in->dreg);
                                MONO_ADD_INS (cfg->cbb, ins);
                                mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, FALSE);
 
                                MONO_INST_NEW (cfg, ins, OP_MOVE);
                                ins->dreg = mono_alloc_ireg (cfg);
-                               ins->sreg1 = in->dreg + 2;
+                               ins->sreg1 = MONO_LVREG_MS (in->dreg);
                                MONO_ADD_INS (cfg->cbb, ins);
                                mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg + 1, FALSE);
                        } else if (!t->byref && ((t->type == MONO_TYPE_R8) || (t->type == MONO_TYPE_R4))) {
@@ -2443,6 +2332,7 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
                                        } else {
                                                int creg;
 
+                                               cfg->param_area = MAX (cfg->param_area, 8);
                                                MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
                                                creg = mono_alloc_ireg (cfg);
                                                MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
@@ -2464,6 +2354,7 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
                                        } else {
                                                int creg;
 
+                                               cfg->param_area = MAX (cfg->param_area, 8);
                                                MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
                                                creg = mono_alloc_ireg (cfg);
                                                MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, creg, ARMREG_SP, (cfg->param_area - 8));
@@ -2524,10 +2415,10 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
                        break;
                case RegTypeBaseGen:
                        if (!t->byref && ((t->type == MONO_TYPE_I8) || (t->type == MONO_TYPE_U8))) {
-                               MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, (G_BYTE_ORDER == G_BIG_ENDIAN) ? in->dreg + 1 : in->dreg + 2);
+                               MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, (G_BYTE_ORDER == G_BIG_ENDIAN) ? MONO_LVREG_LS (in->dreg) : MONO_LVREG_MS (in->dreg));
                                MONO_INST_NEW (cfg, ins, OP_MOVE);
                                ins->dreg = mono_alloc_ireg (cfg);
-                               ins->sreg1 = G_BYTE_ORDER == G_BIG_ENDIAN ? in->dreg + 2 : in->dreg + 1;
+                               ins->sreg1 = G_BYTE_ORDER == G_BIG_ENDIAN ? MONO_LVREG_MS (in->dreg) : MONO_LVREG_LS (in->dreg);
                                MONO_ADD_INS (cfg->cbb, ins);
                                mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ARMREG_R3, FALSE);
                        } else if (!t->byref && (t->type == MONO_TYPE_R8)) {
@@ -2535,6 +2426,7 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
 
                                /* This should work for soft-float as well */
 
+                               cfg->param_area = MAX (cfg->param_area, 8);
                                MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, (cfg->param_area - 8), in->dreg);
                                creg = mono_alloc_ireg (cfg);
                                mono_call_inst_add_outarg_reg (cfg, call, creg, ARMREG_R3, FALSE);
@@ -2672,7 +2564,7 @@ mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src)
 
                                call->float_args = g_slist_append_mempool (cfg->mempool, call->float_args, fad);
                        } else {
-                               add_outarg_reg (cfg, call, RegTypeFP, ainfo->reg + i, load);
+                               add_outarg_reg (cfg, call, RegTypeFP, ainfo->reg + (i * 2), load);
                        }
                }
                break;
@@ -2725,8 +2617,8 @@ mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val)
                                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;
+                               ins->sreg1 = MONO_LVREG_LS (val->dreg);
+                               ins->sreg2 = MONO_LVREG_MS (val->dreg);
                                MONO_ADD_INS (cfg->cbb, ins);
                        }
                        return;
@@ -3310,7 +3202,7 @@ mono_arch_peephole_pass_2 (MonoCompile *cfg, MonoBasicBlock *bb)
                                        MONO_DELETE_INS (bb, ins);
                                        continue;
                                } else {
-                                       //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
+                                       //static int c = 0; g_print ("MATCHX %s %d\n", cfg->method->name,c++);
                                        ins->opcode = OP_MOVE;
                                        ins->sreg1 = last_ins->sreg1;
                                }
@@ -3351,7 +3243,7 @@ mono_arch_peephole_pass_2 (MonoCompile *cfg, MonoBasicBlock *bb)
                                                || last_ins->opcode == OP_STORE_MEMBASE_IMM) &&
                                   ins->inst_basereg == last_ins->inst_destbasereg &&
                                   ins->inst_offset == last_ins->inst_offset) {
-                               //static int c = 0; printf ("MATCHX %s %d\n", cfg->method->name,c++);
+                               //static int c = 0; g_print ("MATCHX %s %d\n", cfg->method->name,c++);
                                ins->opcode = OP_ICONST;
                                ins->inst_c0 = last_ins->inst_imm;
                                g_assert_not_reached (); // check this rule
@@ -3531,11 +3423,14 @@ loop_start:
                case OP_IOR_IMM:
                case OP_IXOR_IMM:
                        if ((imm8 = mono_arm_is_rotated_imm8 (ins->inst_imm, &rot_amount)) < 0) {
+                               int opcode2 = mono_op_imm_to_op (ins->opcode);
                                ADD_NEW_INS (cfg, temp, OP_ICONST);
                                temp->inst_c0 = ins->inst_imm;
                                temp->dreg = mono_alloc_ireg (cfg);
                                ins->sreg2 = temp->dreg;
-                               ins->opcode = mono_op_imm_to_op (ins->opcode);
+                               if (opcode2 == -1)
+                                       g_error ("mono_op_imm_to_op failed for %s\n", mono_inst_name (ins->opcode));
+                               ins->opcode = opcode2;
                        }
                        if (ins->opcode == OP_SBB || ins->opcode == OP_ISBB || ins->opcode == OP_SUBCC)
                                goto loop_start;
@@ -3575,13 +3470,17 @@ loop_start:
                case OP_IDIV_IMM:
                case OP_IDIV_UN_IMM:
                case OP_IREM_IMM:
-               case OP_IREM_UN_IMM:
+               case OP_IREM_UN_IMM: {
+                       int opcode2 = mono_op_imm_to_op (ins->opcode);
                        ADD_NEW_INS (cfg, temp, OP_ICONST);
                        temp->inst_c0 = ins->inst_imm;
                        temp->dreg = mono_alloc_ireg (cfg);
                        ins->sreg2 = temp->dreg;
-                       ins->opcode = mono_op_imm_to_op (ins->opcode);
+                       if (opcode2 == -1)
+                               g_error ("mono_op_imm_to_op failed for %s\n", mono_inst_name (ins->opcode));
+                       ins->opcode = opcode2;
                        break;
+               }
                case OP_LOCALLOC_IMM:
                        ADD_NEW_INS (cfg, temp, OP_ICONST);
                        temp->inst_c0 = ins->inst_imm;
@@ -3758,8 +3657,8 @@ mono_arch_decompose_long_opts (MonoCompile *cfg, MonoInst *long_ins)
 
        if (long_ins->opcode == OP_LNEG) {
                ins = long_ins;
-               MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ARM_RSBS_IMM, ins->dreg + 1, ins->sreg1 + 1, 0);
-               MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ARM_RSC_IMM, ins->dreg + 2, ins->sreg1 + 2, 0);
+               MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ARM_RSBS_IMM, MONO_LVREG_LS (ins->dreg), MONO_LVREG_LS (ins->sreg1), 0);
+               MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ARM_RSC_IMM, MONO_LVREG_MS (ins->dreg), MONO_LVREG_MS (ins->sreg1), 0);
                NULLIFY_INS (ins);
        }
 }
@@ -3914,7 +3813,7 @@ handle_thunk (MonoCompile *cfg, MonoDomain *domain, guchar *code, const guchar *
                        }
                }
 
-               //printf ("THUNK: %p %p %p\n", code, target, target_thunk);
+               //g_print ("THUNK: %p %p %p\n", code, target, target_thunk);
 
                if (!target_thunk) {
                        mono_mini_arch_unlock ();
@@ -4127,6 +4026,12 @@ mono_arm_emit_load_imm (guint8 *code, int dreg, guint32 val)
        code += 4;
        return code;
 #endif
+       if (mini_get_debug_options()->single_imm_size && v7_supported) {
+               ARM_MOVW_REG_IMM (code, dreg, val & 0xffff);
+               ARM_MOVT_REG_IMM (code, dreg, (val >> 16) & 0xffff);
+               return code;
+       }
+
        if ((imm8 = mono_arm_is_rotated_imm8 (val, &rot_amount)) >= 0) {
                ARM_MOV_REG_IMM (code, dreg, imm8, rot_amount);
        } else if ((imm8 = mono_arm_is_rotated_imm8 (~val, &rot_amount)) >= 0) {
@@ -4645,7 +4550,6 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        int i;
                        MonoInst *info_var = cfg->arch.seq_point_info_var;
                        MonoInst *ss_trigger_page_var = cfg->arch.ss_trigger_page_var;
-                       MonoInst *ss_read_var = cfg->arch.seq_point_read_var;
                        MonoInst *ss_method_var = cfg->arch.seq_point_ss_method_var;
                        MonoInst *bp_method_var = cfg->arch.seq_point_bp_method_var;
                        MonoInst *var;
@@ -4666,7 +4570,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                                g_assert (arm_is_imm12 (info_var->inst_offset));
                        }
 
-                       if (!cfg->soft_breakpoints) {
+                       if (!cfg->soft_breakpoints && !cfg->compile_aot) {
                                /*
                                 * Read from the single stepping trigger page. This will cause a
                                 * SIGSEGV when single stepping is enabled.
@@ -4678,8 +4582,8 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
 
                        if (ins->flags & MONO_INST_SINGLE_STEP_LOC) {
                                if (cfg->soft_breakpoints) {
-                                       /* Load the address of the sequence point trigger variable. */
-                                       var = ss_read_var;
+                                       /* Load the address of the sequence point method variable. */
+                                       var = ss_method_var;
                                        g_assert (var);
                                        g_assert (var->opcode == OP_REGOFFSET);
                                        g_assert (arm_is_imm12 (var->inst_offset));
@@ -4688,14 +4592,6 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                                        /* Read the value and check whether it is non-zero. */
                                        ARM_LDR_IMM (code, dreg, dreg, 0);
                                        ARM_CMP_REG_IMM (code, dreg, 0, 0);
-
-                                       /* Load the address of the sequence point method. */
-                                       var = ss_method_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);
-
                                        /* Call it conditionally. */
                                        ARM_BLX_REG_COND (code, ARMCOND_NE, dreg);
                                } else {
@@ -5173,13 +5069,17 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
 
                        if (IS_HARD_FLOAT)
                                code = emit_float_args (cfg, call, code, &max_len, &offset);
-                       if (!arm_is_imm12 (ins->inst_offset))
+                       if (!arm_is_imm12 (ins->inst_offset)) {
+                               /* sreg1 might be IP */
+                               ARM_MOV_REG_REG (code, ARMREG_LR, ins->sreg1);
                                code = mono_arm_emit_load_imm (code, ARMREG_IP, ins->inst_offset);
-                       ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
-                       if (!arm_is_imm12 (ins->inst_offset))
-                               ARM_LDR_REG_REG (code, ARMREG_PC, ins->sreg1, ARMREG_IP);
-                       else
+                               ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, ARMREG_LR);
+                               ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
+                               ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, 0);
+                       } else {
+                               ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
                                ARM_LDR_IMM (code, ARMREG_PC, ins->sreg1, ins->inst_offset);
+                       }
                        ins->flags |= MONO_INST_GC_CALLSITE;
                        ins->backend.pc_offset = code - cfg->native_code;
                        code = emit_move_return_value (cfg, ins, code);
@@ -5859,10 +5759,10 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
 #endif
                                ARM_CMPD (code, vfp_scratch2, vfp_scratch1);
                                ARM_FMSTAT (code);
-                               EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_GT, "ArithmeticException");
+                               EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_GT, "OverflowException");
                                ARM_CMPD (code, ins->sreg1, ins->sreg1);
                                ARM_FMSTAT (code);
-                               EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VS, "ArithmeticException");
+                               EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_VS, "OverflowException");
                                ARM_CPYD (code, ins->dreg, ins->sreg1);
 
                                code = mono_arm_emit_vfp_scratch_restore (cfg, code, vfp_scratch1);
@@ -5990,10 +5890,11 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        bb->spill_slot_defs = g_slist_prepend_mempool (cfg->mempool, bb->spill_slot_defs, ins);
                        break;
                case OP_GC_SAFE_POINT: {
-#if defined (USE_COOP_GC)
                        const char *polling_func = NULL;
                        guint8 *buf [1];
 
+                       g_assert (mono_threads_is_coop_enabled ());
+
                        polling_func = "mono_threads_state_poll";
                        ARM_LDR_IMM (code, ARMREG_IP, ins->sreg1, 0);
                        ARM_CMP_REG_IMM (code, ARMREG_IP, 0, 0);
@@ -6002,7 +5903,6 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_INTERNAL_METHOD, polling_func);
                        code = emit_call_seq (cfg, code);
                        arm_patch (buf [0], code);
-#endif
                        break;
                }
 
@@ -6038,15 +5938,17 @@ mono_arch_register_lowlevel_calls (void)
 
 #ifndef MONO_CROSS_COMPILE
        if (mono_arm_have_tls_get ()) {
-               if (mono_arm_have_fast_tls ()) {
-                       mono_register_jit_icall (mono_fast_get_tls_key, "mono_get_tls_key", mono_create_icall_signature ("ptr ptr"), TRUE);
-                       mono_register_jit_icall (mono_fast_set_tls_key, "mono_set_tls_key", mono_create_icall_signature ("void ptr ptr"), TRUE);
+               MonoTlsImplementation tls_imp = mono_arm_get_tls_implementation ();
 
+               mono_register_jit_icall (tls_imp.get_tls_thunk, "mono_get_tls_key", mono_create_icall_signature ("ptr ptr"), TRUE);
+               mono_register_jit_icall (tls_imp.set_tls_thunk, "mono_set_tls_key", mono_create_icall_signature ("void ptr ptr"), TRUE);
+
+               if (tls_imp.get_tls_thunk_end) {
                        mono_tramp_info_register (
                                mono_tramp_info_create (
                                        "mono_get_tls_key",
-                                       (guint8*)mono_fast_get_tls_key,
-                                       (guint8*)mono_fast_get_tls_key_end - (guint8*)mono_fast_get_tls_key,
+                                       (guint8*)tls_imp.get_tls_thunk,
+                                       (guint8*)tls_imp.get_tls_thunk_end - (guint8*)tls_imp.get_tls_thunk,
                                        NULL,
                                        mono_arch_get_cie_program ()
                                        ),
@@ -6055,17 +5957,13 @@ mono_arch_register_lowlevel_calls (void)
                        mono_tramp_info_register (
                                mono_tramp_info_create (
                                        "mono_set_tls_key",
-                                       (guint8*)mono_fast_set_tls_key,
-                                       (guint8*)mono_fast_set_tls_key_end - (guint8*)mono_fast_set_tls_key,
+                                       (guint8*)tls_imp.set_tls_thunk,
+                                       (guint8*)tls_imp.set_tls_thunk_end - (guint8*)tls_imp.set_tls_thunk,
                                        NULL,
                                        mono_arch_get_cie_program ()
                                        ),
                                NULL
                                );
-               } else {
-                       g_warning ("No fast tls on device. Using fallbacks.");
-                       mono_register_jit_icall (mono_fallback_get_tls_key, "mono_get_tls_key", mono_create_icall_signature ("ptr ptr"), TRUE);
-                       mono_register_jit_icall (mono_fallback_set_tls_key, "mono_set_tls_key", mono_create_icall_signature ("void ptr ptr"), TRUE);
                }
        }
 #endif
@@ -6616,15 +6514,12 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                }
        }
 
-       if (cfg->arch.seq_point_read_var) {
-               MonoInst *read_ins = cfg->arch.seq_point_read_var;
+       if (cfg->arch.seq_point_ss_method_var) {
                MonoInst *ss_method_ins = cfg->arch.seq_point_ss_method_var;
                MonoInst *bp_method_ins = cfg->arch.seq_point_bp_method_var;
 #ifdef USE_JUMP_TABLES
                gpointer *jte;
 #endif
-               g_assert (read_ins->opcode == OP_REGOFFSET);
-               g_assert (arm_is_imm12 (read_ins->inst_offset));
                g_assert (ss_method_ins->opcode == OP_REGOFFSET);
                g_assert (arm_is_imm12 (ss_method_ins->inst_offset));
                g_assert (bp_method_ins->opcode == OP_REGOFFSET);
@@ -6632,26 +6527,21 @@ mono_arch_emit_prolog (MonoCompile *cfg)
 
 #ifdef USE_JUMP_TABLES
                jte = mono_jumptable_add_entries (3);
-               jte [0] = (gpointer)&ss_trigger_var;
-               jte [1] = single_step_tramp;
-               jte [2] = breakpoint_tramp;
+               jte [0] = &single_step_tramp;
+               jte [1] = breakpoint_tramp;
                code = mono_arm_load_jumptable_entry_addr (code, jte, ARMREG_LR);
 #else
                ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
-               ARM_B (code, 2);
-               *(volatile int **)code = &ss_trigger_var;
-               code += 4;
-               *(gpointer*)code = single_step_tramp;
+               ARM_B (code, 1);
+               *(gpointer*)code = &single_step_tramp;
                code += 4;
                *(gpointer*)code = breakpoint_tramp;
                code += 4;
 #endif
 
                ARM_LDR_IMM (code, ARMREG_IP, ARMREG_LR, 0);
-               ARM_STR_IMM (code, ARMREG_IP, read_ins->inst_basereg, read_ins->inst_offset);
-               ARM_LDR_IMM (code, ARMREG_IP, ARMREG_LR, 4);
                ARM_STR_IMM (code, ARMREG_IP, ss_method_ins->inst_basereg, ss_method_ins->inst_offset);
-               ARM_LDR_IMM (code, ARMREG_IP, ARMREG_LR, 8);
+               ARM_LDR_IMM (code, ARMREG_IP, ARMREG_LR, 4);
                ARM_STR_IMM (code, ARMREG_IP, bp_method_ins->inst_basereg, bp_method_ins->inst_offset);
        }
 
@@ -6872,8 +6762,7 @@ mono_arch_emit_exceptions (MonoCompile *cfg)
                        }
                        arm_patch (ip, code);
 
-                       exc_class = mono_class_from_name (mono_defaults.corlib, "System", patch_info->data.name);
-                       g_assert (exc_class);
+                       exc_class = mono_class_load_from_name (mono_defaults.corlib, "System", patch_info->data.name);
 
                        ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_LR);
 #ifdef USE_JUMP_TABLES
@@ -6895,7 +6784,7 @@ mono_arch_emit_exceptions (MonoCompile *cfg)
                        patch_info->ip.i = code - cfg->native_code;
                        ARM_BL (code, 0);
                        cfg->thunk_area += THUNK_SIZE;
-                       *(guint32*)(gpointer)code = exc_class->type_token;
+                       *(guint32*)(gpointer)code = exc_class->type_token - MONO_TOKEN_TYPE_DEF;
                        code += 4;
 #endif
                        break;
@@ -7522,7 +7411,7 @@ mono_arch_start_single_stepping (void)
        if (ss_trigger_page)
                mono_mprotect (ss_trigger_page, mono_pagesize (), 0);
        else
-               ss_trigger_var = 1;
+               single_step_tramp = mini_get_single_step_trampoline ();
 }
        
 /*
@@ -7536,7 +7425,7 @@ mono_arch_stop_single_stepping (void)
        if (ss_trigger_page)
                mono_mprotect (ss_trigger_page, mono_pagesize (), MONO_MMAP_READ);
        else
-               ss_trigger_var = 0;
+               single_step_tramp = NULL;
 }
 
 #if __APPLE__
@@ -7732,8 +7621,8 @@ mono_arch_opcode_supported (int opcode)
        }
 }
 
-#if defined(ENABLE_GSHAREDVT)
-
-#include "../../../mono-extensions/mono/mini/mini-arm-gsharedvt.c"
-
-#endif /* !MONOTOUCH */
+CallInfo*
+mono_arch_get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
+{
+       return get_call_info (mp, sig);
+}