Merge pull request #1659 from alexanderkyte/stringbuilder-referencesource
[mono.git] / mono / mini / mini-llvm.c
index 59956f25edf368a1a0ac5393e63e1fcf7cf9009a..2fa1da978d424eed7b04ba70685dd45e0a4da8e1 100644 (file)
 extern void *memset(void *, int, size_t);
 void bzero (void *to, size_t count) { memset (to, 0, count); }
 
+#endif
+
+#if LLVM_API_VERSION < 4
+#error "The version of the mono llvm repository is too old."
 #endif
 
  /*
@@ -327,6 +331,43 @@ type_to_simd_type (int type)
        }
 }
 
+static LLVMTypeRef
+create_llvm_type_for_type (MonoClass *klass)
+{
+       int i, size, nfields, esize;
+       LLVMTypeRef *eltypes;
+       char *name;
+       MonoType *t;
+       LLVMTypeRef ltype;
+
+       t = &klass->byval_arg;
+
+       if (mini_type_is_hfa (t, &nfields, &esize)) {
+               /*
+                * This is needed on arm64 where HFAs are returned in
+                * registers.
+                */
+               size = nfields;
+               eltypes = g_new (LLVMTypeRef, size);
+               for (i = 0; i < size; ++i)
+                       eltypes [i] = esize == 4 ? LLVMFloatType () : LLVMDoubleType ();
+       } else {
+               size = get_vtype_size (t);
+
+               eltypes = g_new (LLVMTypeRef, size);
+               for (i = 0; i < size; ++i)
+                       eltypes [i] = LLVMInt8Type ();
+       }
+
+       name = mono_type_full_name (&klass->byval_arg);
+       ltype = LLVMStructCreateNamed (LLVMGetGlobalContext (), name);
+       LLVMStructSetBody (ltype, eltypes, size, FALSE);
+       g_free (eltypes);
+       g_free (name);
+
+       return ltype;
+}
+
 /*
  * type_to_llvm_type:
  *
@@ -335,10 +376,10 @@ type_to_simd_type (int type)
 static LLVMTypeRef
 type_to_llvm_type (EmitContext *ctx, MonoType *t)
 {
-       t = mini_replace_type (t);
-
        if (t->byref)
                return LLVMPointerType (LLVMInt8Type (), 0);
+
+       t = mini_get_underlying_type (ctx->cfg, t);
        switch (t->type) {
        case MONO_TYPE_VOID:
                return LLVMVoidType ();
@@ -398,22 +439,8 @@ type_to_llvm_type (EmitContext *ctx, MonoType *t)
 
                ltype = g_hash_table_lookup (ctx->lmodule->llvm_types, klass);
                if (!ltype) {
-                       int i, size;
-                       LLVMTypeRef *eltypes;
-                       char *name;
-
-                       size = get_vtype_size (t);
-
-                       eltypes = g_new (LLVMTypeRef, size);
-                       for (i = 0; i < size; ++i)
-                               eltypes [i] = LLVMInt8Type ();
-
-                       name = mono_type_full_name (&klass->byval_arg);
-                       ltype = LLVMStructCreateNamed (LLVMGetGlobalContext (), name);
-                       LLVMStructSetBody (ltype, eltypes, size, FALSE);
+                       ltype = create_llvm_type_for_type (klass);
                        g_hash_table_insert (ctx->lmodule->llvm_types, klass, ltype);
-                       g_free (eltypes);
-                       g_free (name);
                }
                return ltype;
        }
@@ -482,7 +509,7 @@ type_to_llvm_arg_type (EmitContext *ctx, MonoType *t)
  * on the IL stack.
  */
 static G_GNUC_UNUSED LLVMTypeRef
-llvm_type_to_stack_type (LLVMTypeRef type)
+llvm_type_to_stack_type (MonoCompile *cfg, LLVMTypeRef type)
 {
        if (type == NULL)
                return NULL;
@@ -490,7 +517,7 @@ llvm_type_to_stack_type (LLVMTypeRef type)
                return LLVMInt32Type ();
        else if (type == LLVMInt16Type ())
                return LLVMInt32Type ();
-       else if (type == LLVMFloatType ())
+       else if (!cfg->r4fp && type == LLVMFloatType ())
                return LLVMDoubleType ();
        else
                return type;
@@ -558,9 +585,13 @@ op_to_llvm_type (int opcode)
                return LLVMInt64Type ();
        case OP_FCONV_TO_I1:
        case OP_FCONV_TO_U1:
+       case OP_RCONV_TO_I1:
+       case OP_RCONV_TO_U1:
                return LLVMInt8Type ();
        case OP_FCONV_TO_I2:
        case OP_FCONV_TO_U2:
+       case OP_RCONV_TO_I2:
+       case OP_RCONV_TO_U2:
                return LLVMInt16Type ();
        case OP_FCONV_TO_I:
        case OP_FCONV_TO_U:
@@ -602,22 +633,30 @@ load_store_to_llvm_type (int opcode, int *size, gboolean *sext, gboolean *zext)
        case OP_LOADI1_MEMBASE:
        case OP_STOREI1_MEMBASE_REG:
        case OP_STOREI1_MEMBASE_IMM:
+       case OP_ATOMIC_LOAD_I1:
+       case OP_ATOMIC_STORE_I1:
                *size = 1;
                *sext = TRUE;
                return LLVMInt8Type ();
        case OP_LOADU1_MEMBASE:
        case OP_LOADU1_MEM:
+       case OP_ATOMIC_LOAD_U1:
+       case OP_ATOMIC_STORE_U1:
                *size = 1;
                *zext = TRUE;
                return LLVMInt8Type ();
        case OP_LOADI2_MEMBASE:
        case OP_STOREI2_MEMBASE_REG:
        case OP_STOREI2_MEMBASE_IMM:
+       case OP_ATOMIC_LOAD_I2:
+       case OP_ATOMIC_STORE_I2:
                *size = 2;
                *sext = TRUE;
                return LLVMInt16Type ();
        case OP_LOADU2_MEMBASE:
        case OP_LOADU2_MEM:
+       case OP_ATOMIC_LOAD_U2:
+       case OP_ATOMIC_STORE_U2:
                *size = 2;
                *zext = TRUE;
                return LLVMInt16Type ();
@@ -627,20 +666,32 @@ load_store_to_llvm_type (int opcode, int *size, gboolean *sext, gboolean *zext)
        case OP_LOADU4_MEM:
        case OP_STOREI4_MEMBASE_REG:
        case OP_STOREI4_MEMBASE_IMM:
+       case OP_ATOMIC_LOAD_I4:
+       case OP_ATOMIC_STORE_I4:
+       case OP_ATOMIC_LOAD_U4:
+       case OP_ATOMIC_STORE_U4:
                *size = 4;
                return LLVMInt32Type ();
        case OP_LOADI8_MEMBASE:
        case OP_LOADI8_MEM:
        case OP_STOREI8_MEMBASE_REG:
        case OP_STOREI8_MEMBASE_IMM:
+       case OP_ATOMIC_LOAD_I8:
+       case OP_ATOMIC_STORE_I8:
+       case OP_ATOMIC_LOAD_U8:
+       case OP_ATOMIC_STORE_U8:
                *size = 8;
                return LLVMInt64Type ();
        case OP_LOADR4_MEMBASE:
        case OP_STORER4_MEMBASE_REG:
+       case OP_ATOMIC_LOAD_R4:
+       case OP_ATOMIC_STORE_R4:
                *size = 4;
                return LLVMFloatType ();
        case OP_LOADR8_MEMBASE:
        case OP_STORER8_MEMBASE_REG:
+       case OP_ATOMIC_LOAD_R8:
+       case OP_ATOMIC_STORE_R8:
                *size = 8;
                return LLVMDoubleType ();
        case OP_LOAD_MEMBASE:
@@ -1105,28 +1156,40 @@ sig_to_llvm_sig_full (EmitContext *ctx, MonoMethodSignature *sig, LLVMCallInfo *
        if (sinfo)
                memset (sinfo, 0, sizeof (LLVMSigInfo));
 
-       rtype = mini_replace_type (sig->ret);
+       rtype = mini_get_underlying_type (ctx->cfg, sig->ret);
        ret_type = type_to_llvm_type (ctx, rtype);
        CHECK_FAILURE (ctx);
 
-       if (cinfo && cinfo->ret.storage == LLVMArgVtypeInReg) {
-               /* LLVM models this by returning an aggregate value */
-               if (cinfo->ret.pair_storage [0] == LLVMArgInIReg && cinfo->ret.pair_storage [1] == LLVMArgNone) {
-                       LLVMTypeRef members [2];
+       if (cinfo) {
+               if (cinfo->ret.storage == LLVMArgVtypeInReg) {
+                       /* LLVM models this by returning an aggregate value */
+                       if (cinfo->ret.pair_storage [0] == LLVMArgInIReg && cinfo->ret.pair_storage [1] == LLVMArgNone) {
+                               LLVMTypeRef members [2];
 
-                       members [0] = IntPtrType ();
-                       ret_type = LLVMStructType (members, 1, FALSE);
-               } else {
-                       g_assert_not_reached ();
+                               members [0] = IntPtrType ();
+                               ret_type = LLVMStructType (members, 1, FALSE);
+                       } else {
+                               g_assert_not_reached ();
+                       }
+               } else if (cinfo->ret.storage == LLVMArgVtypeByVal) {
+                       /* Vtype returned normally by val */
+               } else if (cinfo->ret.storage == LLVMArgFpStruct) {
+                       /* Vtype returned as a fp struct */
+                       LLVMTypeRef members [16];
+
+                       /* Have to create our own structure since we don't map fp structures to LLVM fp structures yet */
+                       for (i = 0; i < cinfo->ret.nslots; ++i)
+                               members [i] = cinfo->ret.esize == 8 ? LLVMDoubleType () : LLVMFloatType ();
+                       ret_type = LLVMStructType (members, cinfo->ret.nslots, FALSE);
+               } else if (mini_type_is_vtype (ctx->cfg, rtype)) {
+                       g_assert (cinfo->ret.storage == LLVMArgVtypeRetAddr);
+                       vretaddr = TRUE;
+                       ret_type = LLVMVoidType ();
                }
-       } else if (cinfo && mini_type_is_vtype (ctx->cfg, rtype)) {
-               g_assert (cinfo->ret.storage == LLVMArgVtypeRetAddr);
-               vretaddr = TRUE;
-               ret_type = LLVMVoidType ();
        }
 
        pindexes = g_new0 (int, sig->param_count);
-       param_types = g_new0 (LLVMTypeRef, (sig->param_count * 2) + 3);
+       param_types = g_new0 (LLVMTypeRef, (sig->param_count * 8) + 3);
        pindex = 0;
        if (cinfo && cinfo->rgctx_arg) {
                if (sinfo)
@@ -1172,12 +1235,21 @@ sig_to_llvm_sig_full (EmitContext *ctx, MonoMethodSignature *sig, LLVMCallInfo *
        if (vretaddr && vret_arg_pindex == pindex)
                param_types [pindex ++] = IntPtrType ();
        for (i = 0; i < sig->param_count; ++i) {
+               LLVMArgInfo *ainfo = cinfo ? &cinfo->args [i + sig->hasthis] : NULL;
+
                if (vretaddr && vret_arg_pindex == pindex)
                        param_types [pindex ++] = IntPtrType ();
                pindexes [i] = pindex;
-               if (cinfo && cinfo->args [i + sig->hasthis].storage == LLVMArgVtypeInReg) {
+
+               if (!ainfo) {
+                       param_types [pindex ++] = type_to_llvm_arg_type (ctx, sig->params [i]);
+                       continue;
+               }
+
+               switch (ainfo->storage) {
+               case LLVMArgVtypeInReg:
                        for (j = 0; j < 2; ++j) {
-                               switch (cinfo->args [i + sig->hasthis].pair_storage [j]) {
+                               switch (ainfo->pair_storage [j]) {
                                case LLVMArgInIReg:
                                        param_types [pindex ++] = LLVMIntType (sizeof (gpointer) * 8);
                                        break;
@@ -1187,14 +1259,29 @@ sig_to_llvm_sig_full (EmitContext *ctx, MonoMethodSignature *sig, LLVMCallInfo *
                                        g_assert_not_reached ();
                                }
                        }
-               } else if (cinfo && cinfo->args [i + sig->hasthis].storage == LLVMArgVtypeByVal) {
+                       break;
+               case LLVMArgVtypeByVal:
                        param_types [pindex] = type_to_llvm_arg_type (ctx, sig->params [i]);
                        CHECK_FAILURE (ctx);
                        param_types [pindex] = LLVMPointerType (param_types [pindex], 0);
                        pindex ++;
-               } else {
+                       break;
+               case LLVMArgAsIArgs:
+                       param_types [pindex] = LLVMArrayType (IntPtrType (), ainfo->nslots);
+                       pindex ++;
+                       break;
+               case LLVMArgAsFpArgs: {
+                       int j;
+
+                       for (j = 0; j < ainfo->nslots; ++j)
+                               param_types [pindex + j] = ainfo->esize == 8 ? LLVMDoubleType () : LLVMFloatType ();
+                       pindex += ainfo->nslots;
+                       break;
+               }
+               default:
                        param_types [pindex ++] = type_to_llvm_arg_type (ctx, sig->params [i]);
-               }                       
+                       break;
+               }
        }
        if (vretaddr && vret_arg_pindex == pindex)
                param_types [pindex ++] = IntPtrType ();
@@ -1434,13 +1521,30 @@ emit_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, LL
 }
 
 static LLVMValueRef
-emit_load (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef addr, const char *name, gboolean is_faulting)
+emit_load_general (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef addr, const char *name, gboolean is_faulting, BarrierKind barrier)
 {
        const char *intrins_name;
        LLVMValueRef args [16], res;
        LLVMTypeRef addr_type;
 
        if (is_faulting && bb->region != -1) {
+               LLVMAtomicOrdering ordering;
+
+               switch (barrier) {
+               case LLVM_BARRIER_NONE:
+                       ordering = LLVMAtomicOrderingNotAtomic;
+                       break;
+               case LLVM_BARRIER_ACQ:
+                       ordering = LLVMAtomicOrderingAcquire;
+                       break;
+               case LLVM_BARRIER_SEQ:
+                       ordering = LLVMAtomicOrderingSequentiallyConsistent;
+                       break;
+               default:
+                       g_assert_not_reached ();
+                       break;
+               }
+
                /*
                 * We handle loads which can fault by calling a mono specific intrinsic
                 * using an invoke, so they are handled properly inside try blocks.
@@ -1471,7 +1575,8 @@ emit_load (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, in
                args [0] = addr;
                args [1] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
                args [2] = LLVMConstInt (LLVMInt1Type (), TRUE, FALSE);
-               res = emit_call (ctx, bb, builder_ref, LLVMGetNamedFunction (ctx->module, intrins_name), args, 3);
+               args [3] = LLVMConstInt (LLVMInt32Type (), ordering, FALSE);
+               res = emit_call (ctx, bb, builder_ref, LLVMGetNamedFunction (ctx->module, intrins_name), args, 4);
 
                if (addr_type == LLVMPointerType (LLVMDoubleType (), 0))
                        res = LLVMBuildBitCast (*builder_ref, res, LLVMDoubleType (), "");
@@ -1487,7 +1592,7 @@ emit_load (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, in
                 * LLVM will generate invalid code when encountering a load from a
                 * NULL address.
                 */
-                res = mono_llvm_build_load (*builder_ref, addr, name, is_faulting);
+                res = mono_llvm_build_load (*builder_ref, addr, name, is_faulting, barrier);
 
                 /* Mark it with a custom metadata */
                 /*
@@ -1499,13 +1604,36 @@ emit_load (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, in
        }
 }
 
+static LLVMValueRef
+emit_load (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef addr, const char *name, gboolean is_faulting)
+{
+       return emit_load_general (ctx, bb, builder_ref, size, addr, name, is_faulting, LLVM_BARRIER_NONE);
+}
+
 static void
-emit_store (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef value, LLVMValueRef addr, gboolean is_faulting)
+emit_store_general (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef value, LLVMValueRef addr, gboolean is_faulting, BarrierKind barrier)
 {
        const char *intrins_name;
        LLVMValueRef args [16];
 
        if (is_faulting && bb->region != -1) {
+               LLVMAtomicOrdering ordering;
+
+               switch (barrier) {
+               case LLVM_BARRIER_NONE:
+                       ordering = LLVMAtomicOrderingNotAtomic;
+                       break;
+               case LLVM_BARRIER_REL:
+                       ordering = LLVMAtomicOrderingRelease;
+                       break;
+               case LLVM_BARRIER_SEQ:
+                       ordering = LLVMAtomicOrderingSequentiallyConsistent;
+                       break;
+               default:
+                       g_assert_not_reached ();
+                       break;
+               }
+
                switch (size) {
                case 1:
                        intrins_name = "llvm.mono.store.i8.p0i8";
@@ -1532,12 +1660,19 @@ emit_store (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, i
                args [1] = addr;
                args [2] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
                args [3] = LLVMConstInt (LLVMInt1Type (), TRUE, FALSE);
-               emit_call (ctx, bb, builder_ref, LLVMGetNamedFunction (ctx->module, intrins_name), args, 4);
+               args [4] = LLVMConstInt (LLVMInt32Type (), ordering, FALSE);
+               emit_call (ctx, bb, builder_ref, LLVMGetNamedFunction (ctx->module, intrins_name), args, 5);
        } else {
-               LLVMBuildStore (*builder_ref, value, addr);
+               mono_llvm_build_store (*builder_ref, value, addr, is_faulting, barrier);
        }
 }
 
+static void
+emit_store (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef value, LLVMValueRef addr, gboolean is_faulting)
+{
+       emit_store_general (ctx, bb, builder_ref, size, value, addr, is_faulting, LLVM_BARRIER_NONE);
+}
+
 /*
  * emit_cond_system_exception:
  *
@@ -1618,14 +1753,14 @@ emit_cond_system_exception (EmitContext *ctx, MonoBasicBlock *bb, const char *ex
 }
 
 /*
- * emit_reg_to_vtype:
+ * emit_args_to_vtype:
  *
- *   Emit code to store the vtype in the registers REGS to the address ADDRESS.
+ *   Emit code to store the vtype in the arguments args to the address ADDRESS.
  */
 static void
-emit_reg_to_vtype (EmitContext *ctx, LLVMBuilderRef builder, MonoType *t, LLVMValueRef address, LLVMArgInfo *ainfo, LLVMValueRef *regs)
+emit_args_to_vtype (EmitContext *ctx, LLVMBuilderRef builder, MonoType *t, LLVMValueRef address, LLVMArgInfo *ainfo, LLVMValueRef *args)
 {
-       int j, size;
+       int j, size, nslots;
 
        size = get_vtype_size (t);
 
@@ -1633,27 +1768,47 @@ emit_reg_to_vtype (EmitContext *ctx, LLVMBuilderRef builder, MonoType *t, LLVMVa
                address = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (LLVMInt8Type (), 0), "");
        }
 
-       for (j = 0; j < 2; ++j) {
-               LLVMValueRef index [2], addr;
+       if (ainfo->storage == LLVMArgAsFpArgs)
+               nslots = ainfo->nslots;
+       else
+               nslots = 2;
+
+       for (j = 0; j < nslots; ++j) {
+               LLVMValueRef index [2], addr, daddr;
                int part_size = size > sizeof (gpointer) ? sizeof (gpointer) : size;
                LLVMTypeRef part_type;
 
                if (ainfo->pair_storage [j] == LLVMArgNone)
                        continue;
 
-               part_type = LLVMIntType (part_size * 8);
-               if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (t))) {
-                       index [0] = LLVMConstInt (LLVMInt32Type (), j * sizeof (gpointer), FALSE);
-                       addr = LLVMBuildGEP (builder, address, index, 1, "");
-               } else {
-                       index [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
-                       index [1] = LLVMConstInt (LLVMInt32Type (), j * sizeof (gpointer), FALSE);
-                       addr = LLVMBuildGEP (builder, address, index, 2, "");
-               }
                switch (ainfo->pair_storage [j]) {
-               case LLVMArgInIReg:
-                       LLVMBuildStore (builder, convert (ctx, regs [j], part_type), LLVMBuildBitCast (ctx->builder, addr, LLVMPointerType (part_type, 0), ""));
+               case LLVMArgInIReg: {
+                       part_type = LLVMIntType (part_size * 8);
+                       if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (t))) {
+                               index [0] = LLVMConstInt (LLVMInt32Type (), j * sizeof (gpointer), FALSE);
+                               addr = LLVMBuildGEP (builder, address, index, 1, "");
+                       } else {
+                               daddr = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (IntPtrType (), 0), "");
+                               index [0] = LLVMConstInt (LLVMInt32Type (), j, FALSE);
+                               addr = LLVMBuildGEP (builder, daddr, index, 1, "");
+                       }
+                       LLVMBuildStore (builder, convert (ctx, args [j], part_type), LLVMBuildBitCast (ctx->builder, addr, LLVMPointerType (part_type, 0), ""));
                        break;
+               }
+               case LLVMArgInFPReg: {
+                       LLVMTypeRef arg_type;
+
+                       if (ainfo->esize == 8)
+                               arg_type = LLVMDoubleType ();
+                       else
+                               arg_type = LLVMFloatType ();
+
+                       index [0] = LLVMConstInt (LLVMInt32Type (), j, FALSE);
+                       daddr = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (arg_type, 0), "");
+                       addr = LLVMBuildGEP (builder, daddr, index, 1, "");
+                       LLVMBuildStore (builder, args [j], addr);
+                       break;
+               }
                case LLVMArgNone:
                        break;
                default:
@@ -1665,41 +1820,55 @@ emit_reg_to_vtype (EmitContext *ctx, LLVMBuilderRef builder, MonoType *t, LLVMVa
 }
 
 /*
- * emit_vtype_to_reg:
+ * emit_vtype_to_args:
  *
- *   Emit code to load a vtype at address ADDRESS into registers. Store the registers
- * into REGS, and the number of registers into NREGS.
+ *   Emit code to load a vtype at address ADDRESS into scalar arguments. Store the arguments
+ * into ARGS, and the number of arguments into NARGS.
  */
 static void
-emit_vtype_to_reg (EmitContext *ctx, LLVMBuilderRef builder, MonoType *t, LLVMValueRef address, LLVMArgInfo *ainfo, LLVMValueRef *regs, guint32 *nregs)
+emit_vtype_to_args (EmitContext *ctx, LLVMBuilderRef builder, MonoType *t, LLVMValueRef address, LLVMArgInfo *ainfo, LLVMValueRef *args, guint32 *nargs)
 {
        int pindex = 0;
-       int j, size;
+       int j, size, nslots;
+       LLVMTypeRef arg_type;
 
        size = get_vtype_size (t);
 
-       if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (t))) {
+       if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (t)))
                address = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (LLVMInt8Type (), 0), "");
-       }
 
-       for (j = 0; j < 2; ++j) {
-               LLVMValueRef index [2], addr;
+       if (ainfo->storage == LLVMArgAsFpArgs)
+               nslots = ainfo->nslots;
+       else
+               nslots = 2;
+       for (j = 0; j < nslots; ++j) {
+               LLVMValueRef index [2], addr, daddr;
                int partsize = size > sizeof (gpointer) ? sizeof (gpointer) : size;
 
                if (ainfo->pair_storage [j] == LLVMArgNone)
                        continue;
 
-               if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (t))) {
-                       index [0] = LLVMConstInt (LLVMInt32Type (), j * sizeof (gpointer), FALSE);
-                       addr = LLVMBuildGEP (builder, address, index, 1, "");
-               } else {
-                       index [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
-                       index [1] = LLVMConstInt (LLVMInt32Type (), j * sizeof (gpointer), FALSE);                              
-                       addr = LLVMBuildGEP (builder, address, index, 2, "");
-               }
                switch (ainfo->pair_storage [j]) {
                case LLVMArgInIReg:
-                       regs [pindex ++] = convert (ctx, LLVMBuildLoad (builder, LLVMBuildBitCast (ctx->builder, addr, LLVMPointerType (LLVMIntType (partsize * 8), 0), ""), ""), IntPtrType ());
+                       if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (t))) {
+                               index [0] = LLVMConstInt (LLVMInt32Type (), j * sizeof (gpointer), FALSE);
+                               addr = LLVMBuildGEP (builder, address, index, 1, "");
+                       } else {
+                               daddr = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (IntPtrType (), 0), "");
+                               index [0] = LLVMConstInt (LLVMInt32Type (), j, FALSE);
+                               addr = LLVMBuildGEP (builder, daddr, index, 1, "");
+                       }
+                       args [pindex ++] = convert (ctx, LLVMBuildLoad (builder, LLVMBuildBitCast (ctx->builder, addr, LLVMPointerType (LLVMIntType (partsize * 8), 0), ""), ""), IntPtrType ());
+                       break;
+               case LLVMArgInFPReg:
+                       if (ainfo->esize == 8)
+                               arg_type = LLVMDoubleType ();
+                       else
+                               arg_type = LLVMFloatType ();
+                       daddr = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (arg_type, 0), "");
+                       index [0] = LLVMConstInt (LLVMInt32Type (), j, FALSE);
+                       addr = LLVMBuildGEP (builder, daddr, index, 1, "");
+                       args [pindex ++] = LLVMBuildLoad (builder, addr, "");
                        break;
                case LLVMArgNone:
                        break;
@@ -1709,7 +1878,7 @@ emit_vtype_to_reg (EmitContext *ctx, LLVMBuilderRef builder, MonoType *t, LLVMVa
                size -= sizeof (gpointer);
        }
 
-       *nregs = pindex;
+       *nargs = pindex;
 }
 
 static LLVMValueRef
@@ -1813,37 +1982,55 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
                LLVMArgInfo *ainfo = &linfo->args [i + sig->hasthis];
                int reg = cfg->args [i + sig->hasthis]->dreg;
 
-               if (ainfo->storage == LLVMArgVtypeInReg) {
-                       LLVMValueRef regs [2];
+               switch (ainfo->storage) {
+               case LLVMArgVtypeInReg:
+               case LLVMArgAsFpArgs: {
+                       LLVMValueRef args [8];
+                       int j;
 
-                       /* 
-                        * Emit code to save the argument from the registers to 
-                        * the real argument.
-                        */
+                       /* The argument is received as a set of int/fp arguments, store them into the real argument */
+                       memset (args, 0, sizeof (args));
                        pindex = ctx->pindexes [i];
-                       regs [0] = LLVMGetParam (ctx->lmethod, pindex);
-                       if (ainfo->pair_storage [1] != LLVMArgNone)
-                               regs [1] = LLVMGetParam (ctx->lmethod, pindex + 1);
-                       else
-                               regs [1] = NULL;
-
+                       if (ainfo->storage == LLVMArgVtypeInReg) {
+                               args [0] = LLVMGetParam (ctx->lmethod, pindex);
+                               if (ainfo->pair_storage [1] != LLVMArgNone)
+                                       args [1] = LLVMGetParam (ctx->lmethod, pindex + 1);
+                       } else {
+                               g_assert (ainfo->nslots <= 8);
+                               for (j = 0; j < ainfo->nslots; ++j)
+                                       args [j] = LLVMGetParam (ctx->lmethod, ctx->pindexes [i] + j);
+                       }
                        ctx->addresses [reg] = build_alloca (ctx, sig->params [i]);
 
-                       emit_reg_to_vtype (ctx, builder, sig->params [i], ctx->addresses [reg], ainfo, regs);
+                       emit_args_to_vtype (ctx, builder, sig->params [i], ctx->addresses [reg], ainfo, args);
 
-                       if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (sig->params [i]))) {
+                       if (ainfo->storage == LLVMArgVtypeInReg && MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (sig->params [i]))) {
                                /* Treat these as normal values */
                                ctx->values [reg] = LLVMBuildLoad (builder, ctx->addresses [reg], "");
                        }
-               } else if (ainfo->storage == LLVMArgVtypeByVal) {
+                       break;
+               }
+               case LLVMArgVtypeByVal: {
                        ctx->addresses [reg] = LLVMGetParam (ctx->lmethod, ctx->pindexes [i]);
 
                        if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (sig->params [i]))) {
                                /* Treat these as normal values */
                                ctx->values [reg] = LLVMBuildLoad (builder, ctx->addresses [reg], "");
                        }
-               } else {
-                       ctx->values [reg] = convert_full (ctx, ctx->values [reg], llvm_type_to_stack_type (type_to_llvm_type (ctx, sig->params [i])), type_is_unsigned (ctx, sig->params [i]));
+                       break;
+               }
+               case LLVMArgAsIArgs: {
+                       LLVMValueRef arg = LLVMGetParam (ctx->lmethod, ctx->pindexes [i]);
+
+                       ctx->addresses [reg] = build_alloca (ctx, sig->params [i]);
+
+                       /* The argument is received as an array of ints, store it into the real argument */
+                       LLVMBuildStore (ctx->builder, arg, convert (ctx, ctx->addresses [reg], LLVMPointerType (LLVMTypeOf (arg), 0)));
+                       break;
+               }
+               default:
+                       ctx->values [reg] = convert_full (ctx, ctx->values [reg], llvm_type_to_stack_type (cfg, type_to_llvm_type (ctx, sig->params [i])), type_is_unsigned (ctx, sig->params [i]));
+                       break;
                }
        }
 
@@ -1866,7 +2053,7 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
                 */
                this_alloc = mono_llvm_build_alloca (builder, ThisType (), LLVMConstInt (LLVMInt32Type (), 1, FALSE), 0, "");
                /* This volatile store will keep the alloca alive */
-               mono_llvm_build_store (builder, ctx->values [cfg->args [0]->dreg], this_alloc, TRUE);
+               mono_llvm_build_store (builder, ctx->values [cfg->args [0]->dreg], this_alloc, TRUE, LLVM_BARRIER_NONE);
 
                set_metadata_flag (this_alloc, "mono.this");
        }
@@ -1880,7 +2067,7 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
                g_assert (ctx->addresses [cfg->rgctx_var->dreg]);
                rgctx_alloc = ctx->addresses [cfg->rgctx_var->dreg];
                /* This volatile store will keep the alloca alive */
-               store = mono_llvm_build_store (builder, convert (ctx, ctx->rgctx_arg, IntPtrType ()), rgctx_alloc, TRUE);
+               store = mono_llvm_build_store (builder, convert (ctx, ctx->rgctx_arg, IntPtrType ()), rgctx_alloc, TRUE, LLVM_BARRIER_NONE);
 
                set_metadata_flag (rgctx_alloc, "mono.this");
        }
@@ -1917,9 +2104,6 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
 }
 
 /* Have to export this for AOT */
-void
-mono_personality (void);
-       
 void
 mono_personality (void)
 {
@@ -1962,8 +2146,8 @@ process_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref,
        llvm_sig = sig_to_llvm_sig_full (ctx, sig, cinfo, &sinfo);
        CHECK_FAILURE (ctx);
 
-       virtual = (ins->opcode == OP_VOIDCALL_MEMBASE || ins->opcode == OP_CALL_MEMBASE || ins->opcode == OP_VCALL_MEMBASE || ins->opcode == OP_LCALL_MEMBASE || ins->opcode == OP_FCALL_MEMBASE);
-       calli = (ins->opcode == OP_VOIDCALL_REG || ins->opcode == OP_CALL_REG || ins->opcode == OP_VCALL_REG || ins->opcode == OP_LCALL_REG || ins->opcode == OP_FCALL_REG);
+       virtual = (ins->opcode == OP_VOIDCALL_MEMBASE || ins->opcode == OP_CALL_MEMBASE || ins->opcode == OP_VCALL_MEMBASE || ins->opcode == OP_LCALL_MEMBASE || ins->opcode == OP_FCALL_MEMBASE || ins->opcode == OP_RCALL_MEMBASE);
+       calli = !call->fptr_is_patch && (ins->opcode == OP_VOIDCALL_REG || ins->opcode == OP_CALL_REG || ins->opcode == OP_VCALL_REG || ins->opcode == OP_LCALL_REG || ins->opcode == OP_FCALL_REG || ins->opcode == OP_RCALL_REG);
 
        /* FIXME: Avoid creating duplicate methods */
 
@@ -2035,7 +2219,8 @@ process_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref,
                                                 * their own calling convention on some platforms.
                                                 */
 #ifndef TARGET_AMD64
-                                               if (abs_ji->type == MONO_PATCH_INFO_MONITOR_ENTER || abs_ji->type == MONO_PATCH_INFO_MONITOR_EXIT || abs_ji->type == MONO_PATCH_INFO_GENERIC_CLASS_INIT)
+                                               if (abs_ji->type == MONO_PATCH_INFO_MONITOR_ENTER || abs_ji->type == MONO_PATCH_INFO_MONITOR_ENTER_V4 ||
+                                                               abs_ji->type == MONO_PATCH_INFO_MONITOR_EXIT || abs_ji->type == MONO_PATCH_INFO_GENERIC_CLASS_INIT)
                                                        LLVM_FAILURE (ctx, "trampoline with own cconv");
 #endif
                                                target = mono_resolve_patch_target (cfg->method, cfg->domain, NULL, abs_ji, FALSE);
@@ -2066,7 +2251,7 @@ process_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref,
        /* 
         * Collect and convert arguments
         */
-       nargs = (sig->param_count * 2) + sig->hasthis + vretaddr + call->rgctx_reg + call->imt_arg_reg;
+       nargs = (sig->param_count * 16) + sig->hasthis + vretaddr + call->rgctx_reg + call->imt_arg_reg;
        len = sizeof (LLVMValueRef) * nargs;
        args = alloca (len);
        memset (args, 0, len);
@@ -2084,7 +2269,7 @@ process_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref,
                if (!ctx->imt_rgctx_loc)
                        ctx->imt_rgctx_loc = build_alloca_llvm_type (ctx, ctx->lmodule->ptr_type, sizeof (gpointer));
                LLVMBuildStore (builder, convert (ctx, ctx->values [call->rgctx_arg_reg], ctx->lmodule->ptr_type), ctx->imt_rgctx_loc);
-               args [sinfo.rgctx_arg_pindex] = mono_llvm_build_load (builder, ctx->imt_rgctx_loc, "", TRUE);
+               args [sinfo.rgctx_arg_pindex] = mono_llvm_build_load (builder, ctx->imt_rgctx_loc, "", TRUE, LLVM_BARRIER_NONE);
 #else
                args [sinfo.rgctx_arg_pindex] = convert (ctx, values [call->rgctx_arg_reg], ctx->lmodule->ptr_type);
 #endif
@@ -2096,7 +2281,7 @@ process_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref,
                if (!ctx->imt_rgctx_loc)
                        ctx->imt_rgctx_loc = build_alloca_llvm_type (ctx, ctx->lmodule->ptr_type, sizeof (gpointer));
                LLVMBuildStore (builder, convert (ctx, ctx->values [call->imt_arg_reg], ctx->lmodule->ptr_type), ctx->imt_rgctx_loc);
-               args [sinfo.imt_arg_pindex] = mono_llvm_build_load (builder, ctx->imt_rgctx_loc, "", TRUE);
+               args [sinfo.imt_arg_pindex] = mono_llvm_build_load (builder, ctx->imt_rgctx_loc, "", TRUE, LLVM_BARRIER_NONE);
 #else
                args [sinfo.imt_arg_pindex] = convert (ctx, values [call->imt_arg_reg], ctx->lmodule->ptr_type);
 #endif
@@ -2126,31 +2311,36 @@ process_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref,
                regpair = (guint32)(gssize)(l->data);
                reg = regpair & 0xffffff;
                args [pindex] = values [reg];
-               if (ainfo->storage == LLVMArgVtypeInReg) {
-                       int j;
-                       LLVMValueRef regs [2];
-                       guint32 nregs;
-
-                       g_assert (ainfo);
+               switch (ainfo->storage) {
+               case LLVMArgVtypeInReg:
+               case LLVMArgAsFpArgs: {
+                       guint32 nargs;
 
                        g_assert (addresses [reg]);
-
-                       emit_vtype_to_reg (ctx, builder, sig->params [i - sig->hasthis], addresses [reg], ainfo, regs, &nregs);
-                       for (j = 0; j < nregs; ++j)
-                               args [pindex ++] = regs [j];
+                       emit_vtype_to_args (ctx, builder, sig->params [i - sig->hasthis], addresses [reg], ainfo, args + pindex, &nargs);
+                       pindex += nargs;
 
                        // FIXME: alignment
                        // FIXME: Get rid of the VMOVE
-               } else if (ainfo->storage == LLVMArgVtypeByVal) {
+                       break;
+               }
+               case LLVMArgVtypeByVal:
                        g_assert (addresses [reg]);
                        args [pindex] = addresses [reg];
-               } else {
+                       break;
+               case LLVMArgAsIArgs:
+                       g_assert (addresses [reg]);
+                       args [pindex] = LLVMBuildLoad (ctx->builder, convert (ctx, addresses [reg], LLVMPointerType (LLVMArrayType (IntPtrType (), ainfo->nslots), 0)), "");
+                       break;
+               default:
                        g_assert (args [pindex]);
                        if (i == 0 && sig->hasthis)
                                args [pindex] = convert (ctx, args [pindex], ThisType ());
                        else
                                args [pindex] = convert (ctx, args [pindex], type_to_llvm_arg_type (ctx, sig->params [i - sig->hasthis]));
+                       break;
                }
+               g_assert (pindex <= nargs);
 
                l = l->next;
        }
@@ -2191,21 +2381,47 @@ process_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref,
        /*
         * Convert the result
         */
-       if (cinfo && cinfo->ret.storage == LLVMArgVtypeInReg) {
-               LLVMValueRef regs [2];
+       if (cinfo) {
+               switch (cinfo->ret.storage) {
+               case LLVMArgVtypeInReg: {
+                       LLVMValueRef regs [2];
 
-               if (!addresses [ins->dreg])
-                       addresses [ins->dreg] = build_alloca (ctx, sig->ret);
+                       if (!addresses [ins->dreg])
+                               addresses [ins->dreg] = build_alloca (ctx, sig->ret);
 
-               regs [0] = LLVMBuildExtractValue (builder, lcall, 0, "");
-               if (cinfo->ret.pair_storage [1] != LLVMArgNone)
-                       regs [1] = LLVMBuildExtractValue (builder, lcall, 1, "");
-                                       
-               emit_reg_to_vtype (ctx, builder, sig->ret, addresses [ins->dreg], &cinfo->ret, regs);
-       } else if (sig->ret->type != MONO_TYPE_VOID && !vretaddr) {
-               /* If the method returns an unsigned value, need to zext it */
+                       regs [0] = LLVMBuildExtractValue (builder, lcall, 0, "");
+                       if (cinfo->ret.pair_storage [1] != LLVMArgNone)
+                               regs [1] = LLVMBuildExtractValue (builder, lcall, 1, "");
+                       emit_args_to_vtype (ctx, builder, sig->ret, addresses [ins->dreg], &cinfo->ret, regs);
+                       break;
+               }
+               case LLVMArgVtypeByVal:
+                       if (!addresses [call->inst.dreg])
+                               addresses [call->inst.dreg] = build_alloca (ctx, sig->ret);
+                       LLVMBuildStore (builder, lcall, addresses [call->inst.dreg]);
+                       break;
+               case LLVMArgFpStruct:
+                       if (!addresses [call->inst.dreg])
+                               addresses [call->inst.dreg] = build_alloca (ctx, sig->ret);
+                       LLVMBuildStore (builder, lcall, convert_full (ctx, addresses [call->inst.dreg], LLVMPointerType (LLVMTypeOf (lcall), 0), FALSE));
+                       break;
+               default:
+                       if (sig->ret->type != MONO_TYPE_VOID && !vretaddr)
+                               /* If the method returns an unsigned value, need to zext it */
+                               values [ins->dreg] = convert_full (ctx, lcall, llvm_type_to_stack_type (cfg, type_to_llvm_type (ctx, sig->ret)), type_is_unsigned (ctx, sig->ret));
+                       break;
+               }
+       } else {
+               if (sig->ret->type != MONO_TYPE_VOID && !vretaddr)
+                       /* If the method returns an unsigned value, need to zext it */
+                       values [ins->dreg] = convert_full (ctx, lcall, llvm_type_to_stack_type (cfg, type_to_llvm_type (ctx, sig->ret)), type_is_unsigned (ctx, sig->ret));
+       }
 
-               values [ins->dreg] = convert_full (ctx, lcall, llvm_type_to_stack_type (type_to_llvm_type (ctx, sig->ret)), type_is_unsigned (ctx, sig->ret));
+       if (vretaddr) {
+               if (!addresses [call->inst.dreg])
+                       addresses [call->inst.dreg] = build_alloca (ctx, sig->ret);
+               g_assert (sinfo.vret_arg_pindex < nargs);
+               args [sinfo.vret_arg_pindex] = LLVMBuildPtrToInt (builder, addresses [call->inst.dreg], IntPtrType (), "");
        }
 
        *builder_ref = ctx->builder;
@@ -2225,7 +2441,6 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
        LLVMValueRef method = ctx->lmethod;
        LLVMValueRef *values = ctx->values;
        LLVMValueRef *addresses = ctx->addresses;
-       int i;
        LLVMCallInfo *linfo = ctx->linfo;
        LLVMModuleRef module = ctx->module;
        BBInfo *bblocks = ctx->bblocks;
@@ -2431,7 +2646,10 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                        values [ins->dreg] = LLVMConstReal (LLVMDoubleType (), *(double*)ins->inst_p0);
                        break;
                case OP_R4CONST:
-                       values [ins->dreg] = LLVMConstFPExt (LLVMConstReal (LLVMFloatType (), *(float*)ins->inst_p0), LLVMDoubleType ());
+                       if (cfg->r4fp)
+                               values [ins->dreg] = LLVMConstReal (LLVMFloatType (), *(float*)ins->inst_p0);
+                       else
+                               values [ins->dreg] = LLVMConstFPExt (LLVMConstReal (LLVMFloatType (), *(float*)ins->inst_p0), LLVMDoubleType ());
                        break;
                case OP_DUMMY_ICONST:
                        values [ins->dreg] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
@@ -2499,11 +2717,30 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                                break;
                        }
 
+                       if (linfo->ret.storage == LLVMArgVtypeByVal) {
+                               LLVMValueRef retval;
+
+                               g_assert (addresses [ins->sreg1]);
+                               retval = LLVMBuildLoad (builder, addresses [ins->sreg1], "");
+                               LLVMBuildRet (builder, retval);
+                               break;
+                       }
+
                        if (linfo->ret.storage == LLVMArgVtypeRetAddr) {
                                LLVMBuildRetVoid (builder);
                                break;
                        }
 
+                       if (linfo->ret.storage == LLVMArgFpStruct) {
+                               LLVMTypeRef ret_type = LLVMGetReturnType (LLVMGetElementType (LLVMTypeOf (method)));
+                               LLVMValueRef retval;
+
+                               g_assert (addresses [ins->sreg1]);
+                               retval = LLVMBuildLoad (builder, convert (ctx, addresses [ins->sreg1], LLVMPointerType (ret_type, 0)), "");
+                               LLVMBuildRet (builder, retval);
+                               break;
+                       }
+
                        if (!lhs || ctx->is_dead [ins->sreg1]) {
                                /* 
                                 * The method did not set its return value, probably because it
@@ -2520,6 +2757,7 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                        break;
                case OP_ICOMPARE:
                case OP_FCOMPARE:
+               case OP_RCOMPARE:
                case OP_LCOMPARE:
                case OP_COMPARE:
                case OP_ICOMPARE_IMM:
@@ -2562,9 +2800,11 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                        }
 
                        /* We use COMPARE+SETcc/Bcc, llvm uses SETcc+br cond */
-                       if (ins->opcode == OP_FCOMPARE)
+                       if (ins->opcode == OP_FCOMPARE) {
                                cmp = LLVMBuildFCmp (builder, fpcond_to_llvm_cond [rel], convert (ctx, lhs, LLVMDoubleType ()), convert (ctx, rhs, LLVMDoubleType ()), "");
-                       else if (ins->opcode == OP_COMPARE_IMM) {
+                       } else if (ins->opcode == OP_RCOMPARE) {
+                               cmp = LLVMBuildFCmp (builder, fpcond_to_llvm_cond [rel], convert (ctx, lhs, LLVMFloatType ()), convert (ctx, rhs, LLVMFloatType ()), "");
+                       } else if (ins->opcode == OP_COMPARE_IMM) {
                                if (LLVMGetTypeKind (LLVMTypeOf (lhs)) == LLVMPointerTypeKind && ins->inst_imm == 0)
                                        cmp = LLVMBuildICmp (builder, cond_to_llvm_cond [rel], lhs, LLVMConstNull (LLVMTypeOf (lhs)), "");
                                else
@@ -2630,6 +2870,20 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                        values [ins->dreg] = LLVMBuildZExt (builder, cmp, LLVMInt32Type (), dname);
                        break;
                }
+               case OP_RCEQ:
+               case OP_RCLT:
+               case OP_RCLT_UN:
+               case OP_RCGT:
+               case OP_RCGT_UN: {
+                       CompRelation rel;
+                       LLVMValueRef cmp;
+
+                       rel = mono_opcode_to_cond (ins->opcode);
+
+                       cmp = LLVMBuildFCmp (builder, fpcond_to_llvm_cond [rel], convert (ctx, lhs, LLVMFloatType ()), convert (ctx, rhs, LLVMFloatType ()), "");
+                       values [ins->dreg] = LLVMBuildZExt (builder, cmp, LLVMInt32Type (), dname);
+                       break;
+               }
                case OP_PHI:
                case OP_FPHI:
                case OP_VPHI:
@@ -2693,7 +2947,8 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                        g_assert (lhs);
                        values [ins->dreg] = lhs;
                        break;
-               case OP_FMOVE: {
+               case OP_FMOVE:
+               case OP_RMOVE: {
                        MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
                                
                        g_assert (lhs);
@@ -2708,6 +2963,22 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                        }
                        break;
                }
+               case OP_MOVE_F_TO_I4: {
+                       values [ins->dreg] = LLVMBuildBitCast (builder, LLVMBuildFPTrunc (builder, lhs, LLVMFloatType (), ""), LLVMInt32Type (), "");
+                       break;
+               }
+               case OP_MOVE_I4_TO_F: {
+                       values [ins->dreg] = LLVMBuildFPExt (builder, LLVMBuildBitCast (builder, lhs, LLVMFloatType (), ""), LLVMDoubleType (), "");
+                       break;
+               }
+               case OP_MOVE_F_TO_I8: {
+                       values [ins->dreg] = LLVMBuildBitCast (builder, lhs, LLVMInt64Type (), "");
+                       break;
+               }
+               case OP_MOVE_I8_TO_F: {
+                       values [ins->dreg] = LLVMBuildBitCast (builder, lhs, LLVMDoubleType (), "");
+                       break;
+               }
                case OP_IADD:
                case OP_ISUB:
                case OP_IAND:
@@ -2741,6 +3012,29 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                        lhs = convert (ctx, lhs, regtype_to_llvm_type (spec [MONO_INST_DEST]));
                        rhs = convert (ctx, rhs, regtype_to_llvm_type (spec [MONO_INST_DEST]));
 
+#ifdef MONO_ARCH_NEED_DIV_CHECK
+                       switch (ins->opcode) {
+                       case OP_IDIV:
+                       case OP_LDIV:
+                       case OP_IREM:
+                       case OP_LREM:
+                       case OP_IDIV_UN:
+                       case OP_LDIV_UN:
+                       case OP_IREM_UN:
+                       case OP_LREM_UN: {
+                               LLVMValueRef cmp;
+
+                               cmp = LLVMBuildICmp (builder, LLVMIntEQ, rhs, LLVMConstInt (LLVMTypeOf (rhs), 0, FALSE), "");
+                               emit_cond_system_exception (ctx, bb, "DivideByZeroException", cmp);
+                               CHECK_FAILURE (ctx);
+                               builder = ctx->builder;
+                               break;
+                       }
+                       default:
+                               break;
+                       }
+#endif
+
                        switch (ins->opcode) {
                        case OP_IADD:
                        case OP_LADD:
@@ -2771,6 +3065,7 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                                values [ins->dreg] = LLVMBuildUDiv (builder, lhs, rhs, dname);
                                break;
                        case OP_FDIV:
+                       case OP_RDIV:
                                values [ins->dreg] = LLVMBuildFDiv (builder, lhs, rhs, dname);
                                break;
                        case OP_IAND:
@@ -2812,6 +3107,31 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                                g_assert_not_reached ();
                        }
                        break;
+               case OP_RADD:
+               case OP_RSUB:
+               case OP_RMUL:
+               case OP_RDIV: {
+                       lhs = convert (ctx, lhs, LLVMFloatType ());
+                       rhs = convert (ctx, rhs, LLVMFloatType ());
+                       switch (ins->opcode) {
+                       case OP_RADD:
+                               values [ins->dreg] = LLVMBuildFAdd (builder, lhs, rhs, dname);
+                               break;
+                       case OP_RSUB:
+                               values [ins->dreg] = LLVMBuildFSub (builder, lhs, rhs, dname);
+                               break;
+                       case OP_RMUL:
+                               values [ins->dreg] = LLVMBuildFMul (builder, lhs, rhs, dname);
+                               break;
+                       case OP_RDIV:
+                               values [ins->dreg] = LLVMBuildFDiv (builder, lhs, rhs, dname);
+                               break;
+                       default:
+                               g_assert_not_reached ();
+                               break;
+                       }
+                       break;
+               }
                case OP_IADD_IMM:
                case OP_ISUB_IMM:
                case OP_IMUL_IMM:
@@ -2933,6 +3253,10 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                        lhs = convert (ctx, lhs, LLVMDoubleType ());
                        values [ins->dreg] = LLVMBuildFSub (builder, LLVMConstReal (LLVMDoubleType (), 0.0), lhs, dname);
                        break;
+               case OP_RNEG:
+                       lhs = convert (ctx, lhs, LLVMFloatType ());
+                       values [ins->dreg] = LLVMBuildFSub (builder, LLVMConstReal (LLVMFloatType (), 0.0), lhs, dname);
+                       break;
                case OP_INOT: {
                        guint32 v = 0xffffffff;
                        values [ins->dreg] = LLVMBuildXor (builder, LLVMConstInt (LLVMInt32Type (), v, FALSE), convert (ctx, lhs, LLVMInt32Type ()), dname);
@@ -2984,21 +3308,27 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                        values [ins->dreg] = LLVMBuildZExt (builder, lhs, LLVMInt64Type (), dname);
                        break;
                case OP_FCONV_TO_I4:
+               case OP_RCONV_TO_I4:
                        values [ins->dreg] = LLVMBuildFPToSI (builder, lhs, LLVMInt32Type (), dname);
                        break;
                case OP_FCONV_TO_I1:
+               case OP_RCONV_TO_I1:
                        values [ins->dreg] = LLVMBuildSExt (builder, LLVMBuildFPToSI (builder, lhs, LLVMInt8Type (), dname), LLVMInt32Type (), "");
                        break;
                case OP_FCONV_TO_U1:
+               case OP_RCONV_TO_U1:
                        values [ins->dreg] = LLVMBuildZExt (builder, LLVMBuildFPToUI (builder, lhs, LLVMInt8Type (), dname), LLVMInt32Type (), "");
                        break;
                case OP_FCONV_TO_I2:
+               case OP_RCONV_TO_I2:
                        values [ins->dreg] = LLVMBuildSExt (builder, LLVMBuildFPToSI (builder, lhs, LLVMInt16Type (), dname), LLVMInt32Type (), "");
                        break;
                case OP_FCONV_TO_U2:
+               case OP_RCONV_TO_U2:
                        values [ins->dreg] = LLVMBuildZExt (builder, LLVMBuildFPToUI (builder, lhs, LLVMInt16Type (), dname), LLVMInt32Type (), "");
                        break;
                case OP_FCONV_TO_I8:
+               case OP_RCONV_TO_I8:
                        values [ins->dreg] = LLVMBuildFPToSI (builder, lhs, LLVMInt64Type (), dname);
                        break;
                case OP_FCONV_TO_I:
@@ -3020,11 +3350,23 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                case OP_ICONV_TO_R4:
                case OP_LCONV_TO_R4:
                        v = LLVMBuildSIToFP (builder, lhs, LLVMFloatType (), "");
-                       values [ins->dreg] = LLVMBuildFPExt (builder, v, LLVMDoubleType (), dname);
+                       if (cfg->r4fp)
+                               values [ins->dreg] = v;
+                       else
+                               values [ins->dreg] = LLVMBuildFPExt (builder, v, LLVMDoubleType (), dname);
                        break;
                case OP_FCONV_TO_R4:
                        v = LLVMBuildFPTrunc (builder, lhs, LLVMFloatType (), "");
-                       values [ins->dreg] = LLVMBuildFPExt (builder, v, LLVMDoubleType (), dname);
+                       if (cfg->r4fp)
+                               values [ins->dreg] = v;
+                       else
+                               values [ins->dreg] = LLVMBuildFPExt (builder, v, LLVMDoubleType (), dname);
+                       break;
+               case OP_RCONV_TO_R8:
+                       values [ins->dreg] = LLVMBuildFPExt (builder, lhs, LLVMDoubleType (), dname);
+                       break;
+               case OP_RCONV_TO_R4:
+                       values [ins->dreg] = lhs;
                        break;
                case OP_SEXT_I4:
                        values [ins->dreg] = LLVMBuildSExt (builder, convert (ctx, lhs, LLVMInt32Type ()), LLVMInt64Type (), dname);
@@ -3140,7 +3482,7 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                                values [ins->dreg] = LLVMBuildSExt (builder, values [ins->dreg], LLVMInt32Type (), dname);
                        else if (zext)
                                values [ins->dreg] = LLVMBuildZExt (builder, values [ins->dreg], LLVMInt32Type (), dname);
-                       else if (ins->opcode == OP_LOADR4_MEMBASE)
+                       else if (!cfg->r4fp && ins->opcode == OP_LOADR4_MEMBASE)
                                values [ins->dreg] = LLVMBuildFPExt (builder, values [ins->dreg], LLVMDoubleType (), dname);
                        break;
                }
@@ -3209,16 +3551,19 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                case OP_CALL:
                case OP_LCALL:
                case OP_FCALL:
+               case OP_RCALL:
                case OP_VCALL:
                case OP_VOIDCALL_MEMBASE:
                case OP_CALL_MEMBASE:
                case OP_LCALL_MEMBASE:
                case OP_FCALL_MEMBASE:
+               case OP_RCALL_MEMBASE:
                case OP_VCALL_MEMBASE:
                case OP_VOIDCALL_REG:
                case OP_CALL_REG:
                case OP_LCALL_REG:
                case OP_FCALL_REG:
+               case OP_RCALL_REG:
                case OP_VCALL_REG: {
                        process_call (ctx, bb, &builder, ins);
                        CHECK_FAILURE (ctx);
@@ -3402,16 +3747,84 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                        /* new value */
                        args [2] = convert (ctx, values [ins->sreg2], t);
                        val = mono_llvm_build_cmpxchg (builder, args [0], args [1], args [2]);
-#if LLVM_API_VERSION >= 1
                        /* cmpxchg returns a pair */
                        values [ins->dreg] = LLVMBuildExtractValue (builder, val, 0, "");
-#else
-                       values [ins->dreg] = val;
-#endif
                        break;
                }
                case OP_MEMORY_BARRIER: {
-                       mono_llvm_build_fence (builder);
+                       mono_llvm_build_fence (builder, (BarrierKind) ins->backend.memory_barrier_kind);
+                       break;
+               }
+               case OP_ATOMIC_LOAD_I1:
+               case OP_ATOMIC_LOAD_I2:
+               case OP_ATOMIC_LOAD_I4:
+               case OP_ATOMIC_LOAD_I8:
+               case OP_ATOMIC_LOAD_U1:
+               case OP_ATOMIC_LOAD_U2:
+               case OP_ATOMIC_LOAD_U4:
+               case OP_ATOMIC_LOAD_U8:
+               case OP_ATOMIC_LOAD_R4:
+               case OP_ATOMIC_LOAD_R8: {
+                       LLVM_FAILURE (ctx, "atomic mono.load intrinsic");
+
+                       int size;
+                       gboolean sext, zext;
+                       LLVMTypeRef t;
+                       gboolean is_volatile = (ins->flags & MONO_INST_FAULT);
+                       BarrierKind barrier = (BarrierKind) ins->backend.memory_barrier_kind;
+                       LLVMValueRef index, addr;
+
+                       t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext);
+
+                       if (sext || zext)
+                               dname = (char *)"";
+
+                       if (ins->inst_offset != 0) {
+                               index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);
+                               addr = LLVMBuildGEP (builder, convert (ctx, lhs, LLVMPointerType (t, 0)), &index, 1, "");
+                       } else {
+                               addr = lhs;
+                       }
+
+                       addr = convert (ctx, addr, LLVMPointerType (t, 0));
+
+                       values [ins->dreg] = emit_load_general (ctx, bb, &builder, size, addr, dname, is_volatile, barrier);
+
+                       if (sext)
+                               values [ins->dreg] = LLVMBuildSExt (builder, values [ins->dreg], LLVMInt32Type (), dname);
+                       else if (zext)
+                               values [ins->dreg] = LLVMBuildZExt (builder, values [ins->dreg], LLVMInt32Type (), dname);
+                       break;
+               }
+               case OP_ATOMIC_STORE_I1:
+               case OP_ATOMIC_STORE_I2:
+               case OP_ATOMIC_STORE_I4:
+               case OP_ATOMIC_STORE_I8:
+               case OP_ATOMIC_STORE_U1:
+               case OP_ATOMIC_STORE_U2:
+               case OP_ATOMIC_STORE_U4:
+               case OP_ATOMIC_STORE_U8:
+               case OP_ATOMIC_STORE_R4:
+               case OP_ATOMIC_STORE_R8: {
+                       LLVM_FAILURE (ctx, "atomic mono.load intrinsic");
+
+                       int size;
+                       gboolean sext, zext;
+                       LLVMTypeRef t;
+                       gboolean is_volatile = (ins->flags & MONO_INST_FAULT);
+                       BarrierKind barrier = (BarrierKind) ins->backend.memory_barrier_kind;
+                       LLVMValueRef index, addr, value;
+
+                       if (!values [ins->inst_destbasereg])
+                               LLVM_FAILURE (ctx, "inst_destbasereg");
+
+                       t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext);
+
+                       index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);
+                       addr = LLVMBuildGEP (builder, convert (ctx, values [ins->inst_destbasereg], LLVMPointerType (t, 0)), &index, 1, "");
+                       value = convert (ctx, values [ins->sreg1], t);
+
+                       emit_store_general (ctx, bb, &builder, size, value, addr, is_volatile, barrier);
                        break;
                }
                case OP_RELAXED_NOP: {
@@ -3818,6 +4231,7 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                case OP_EXPAND_R8: {
                        LLVMTypeRef t = simd_op_to_llvm_type (ins->opcode);
                        LLVMValueRef mask [16], v;
+                       int i;
 
                        for (i = 0; i < 16; ++i)
                                mask [i] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
@@ -3869,11 +4283,6 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                        break;
                }
 
-               case OP_ICONV_TO_R8_RAW:
-                       /* Same as OP_ICONV_TO_R8 */
-                       values [ins->dreg] = convert (ctx, LLVMBuildBitCast (builder, lhs, LLVMFloatType (), ""), LLVMDoubleType ());
-                       break;
-
                case OP_COMPPS:
                case OP_COMPPD: {
                        LLVMValueRef args [3];
@@ -4450,10 +4859,6 @@ mono_llvm_emit_method (MonoCompile *cfg)
 
        if (cfg->compile_aot) {
                LLVMSetLinkage (method, LLVMInternalLinkage);
-#if LLVM_API_VERSION == 0
-               /* This causes an assertion in later LLVM versions */
-               LLVMSetVisibility (method, LLVMHiddenVisibility);
-#endif
                if (ctx->lmodule->external_symbols) {
                        LLVMSetLinkage (method, LLVMExternalLinkage);
                        LLVMSetVisibility (method, LLVMHiddenVisibility);
@@ -4474,6 +4879,9 @@ mono_llvm_emit_method (MonoCompile *cfg)
                if (clause->flags != MONO_EXCEPTION_CLAUSE_FINALLY && clause->flags != MONO_EXCEPTION_CLAUSE_NONE)
                        LLVM_FAILURE (ctx, "non-finally/catch clause.");
        }
+       if (header->num_clauses || (cfg->method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING))
+               /* We can't handle inlined methods with clauses */
+               LLVMAddFunctionAttr (method, LLVMNoInlineAttribute);
 
        if (linfo->rgctx_arg) {
                ctx->rgctx_arg = LLVMGetParam (method, sinfo.rgctx_arg_pindex);
@@ -4564,7 +4972,7 @@ mono_llvm_emit_method (MonoCompile *cfg)
                        case OP_FPHI:
                        case OP_VPHI:
                        case OP_XPHI: {
-                               LLVMTypeRef phi_type = llvm_type_to_stack_type (type_to_llvm_type (ctx, &ins->klass->byval_arg));
+                               LLVMTypeRef phi_type = llvm_type_to_stack_type (cfg, type_to_llvm_type (ctx, &ins->klass->byval_arg));
 
                                CHECK_FAILURE (ctx);
 
@@ -4835,6 +5243,8 @@ mono_llvm_emit_call (MonoCompile *cfg, MonoCallInst *call)
                }
                case LLVMArgVtypeByVal:
                case LLVMArgVtypeInReg:
+               case LLVMArgAsIArgs:
+               case LLVMArgAsFpArgs:
                        MONO_INST_NEW (cfg, ins, OP_LLVM_OUTARG_VT);
                        ins->dreg = mono_alloc_ireg (cfg);
                        ins->sreg1 = in->dreg;
@@ -5236,15 +5646,17 @@ add_intrinsics (LLVMModuleRef module)
                        arg_types [0] = LLVMPointerType (LLVMIntType (i * 8), 0);
                        arg_types [1] = LLVMInt32Type ();
                        arg_types [2] = LLVMInt1Type ();
+                       arg_types [3] = LLVMInt32Type ();
                        sprintf (name, "llvm.mono.load.i%d.p0i%d", i * 8, i * 8);
-                       LLVMAddFunction (module, name, LLVMFunctionType (LLVMIntType (i * 8), arg_types, 3, FALSE));
+                       LLVMAddFunction (module, name, LLVMFunctionType (LLVMIntType (i * 8), arg_types, 4, FALSE));
 
                        arg_types [0] = LLVMIntType (i * 8);
                        arg_types [1] = LLVMPointerType (LLVMIntType (i * 8), 0);
                        arg_types [2] = LLVMInt32Type ();
                        arg_types [3] = LLVMInt1Type ();
+                       arg_types [4] = LLVMInt32Type ();
                        sprintf (name, "llvm.mono.store.i%d.p0i%d", i * 8, i * 8);
-                       LLVMAddFunction (module, name, LLVMFunctionType (LLVMVoidType (), arg_types, 4, FALSE));
+                       LLVMAddFunction (module, name, LLVMFunctionType (LLVMVoidType (), arg_types, 5, FALSE));
                }
        }
 }
@@ -5386,6 +5798,7 @@ mono_llvm_create_aot_module (const char *got_symbol, gboolean external_symbols,
                lbuilder = LLVMCreateBuilder ();
                LLVMPositionBuilderAtEnd (lbuilder, lbb);
                LLVMBuildRetVoid (lbuilder);
+               mark_as_used (&aot_module, personality);
        }
 
        aot_module.llvm_types = g_hash_table_new (NULL, NULL);
@@ -5768,9 +6181,9 @@ emit_dbg_loc (EmitContext *ctx, LLVMBuilderRef builder, const unsigned char *cil
 
 /*
   AOT SUPPORT:
-  Emit LLVM bytecode into a .bc file, compile it using llc into a .s file, then 
-  append the AOT data structures to that file. For methods which cannot be
-  handled by LLVM, the normal JIT compiled versions are used.
+  Emit LLVM bytecode into a .bc file, compile it using llc into a .s file, then link
+  it with the file containing the methods emitted by the JIT and the AOT data
+  structures.
 */
 
 /* FIXME: Normalize some aspects of the mono IR to allow easier translation, like: