[llvm] Load rgctx/imt arguments using a volatile load on arm to prevent LLVM from...
[mono.git] / mono / mini / decompose.c
index 7885bf9f1e6d2bbbb09491a0419c85381aaa7ceb..88097b2257897627e388ca8babc88c40cddb2a88 100644 (file)
@@ -5,6 +5,7 @@
  *   Zoltan Varga (vargaz@gmail.com)
  *
  * (C) 2002 Ximian, Inc.
+ * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
  */
 
 #include "mini.h"
@@ -16,7 +17,7 @@
 #ifndef DISABLE_JIT
 
 /* FIXME: This conflicts with the definition in mini.c, so it cannot be moved to mini.h */
-MonoInst* mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature *sig, MonoInst **args);
+MONO_API MonoInst* mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature *sig, MonoInst **args);
 void mini_emit_stobj (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *klass, gboolean native);
 void mini_emit_initobj (MonoCompile *cfg, MonoInst *dest, const guchar *ip, MonoClass *klass);
 
@@ -30,8 +31,7 @@ decompose_long_opcode (MonoCompile *cfg, MonoInst *ins, MonoInst **repl_ins)
 
        switch (ins->opcode) {
        case OP_LCONV_TO_I4:
-               MONO_EMIT_NEW_BIALU_IMM (cfg, OP_LSHR_IMM, ins->dreg, ins->sreg1, 0);
-               NULLIFY_INS (ins);
+               ins->opcode = OP_SEXT_I4;
                break;
        case OP_LCONV_TO_I8:
        case OP_LCONV_TO_I:
@@ -53,14 +53,30 @@ decompose_long_opcode (MonoCompile *cfg, MonoInst *ins, MonoInst **repl_ins)
        case OP_LADD_OVF:
                if (COMPILE_LLVM (cfg))
                        break;
-               EMIT_NEW_BIALU (cfg, repl, OP_ADDCC, ins->dreg, ins->sreg1, ins->sreg2);
+               {
+                       int opcode;
+#if defined(__mono_ilp32__) && SIZEOF_REGISTER == 8
+                       opcode = OP_LADDCC;
+#else
+                       opcode = OP_ADDCC;
+#endif
+                       EMIT_NEW_BIALU (cfg, repl, opcode, ins->dreg, ins->sreg1, ins->sreg2);
+               }
                MONO_EMIT_NEW_COND_EXC (cfg, OV, "OverflowException");
                NULLIFY_INS (ins);
                break;
        case OP_LADD_OVF_UN:
                if (COMPILE_LLVM (cfg))
                        break;
-               EMIT_NEW_BIALU (cfg, repl, OP_ADDCC, ins->dreg, ins->sreg1, ins->sreg2);
+               {
+                       int opcode;
+#if defined(__mono_ilp32__) && SIZEOF_REGISTER == 8
+                       opcode = OP_LADDCC;
+#else
+                       opcode = OP_ADDCC;
+#endif
+                       EMIT_NEW_BIALU (cfg, repl, opcode, ins->dreg, ins->sreg1, ins->sreg2);
+               }
                MONO_EMIT_NEW_COND_EXC (cfg, C, "OverflowException");
                NULLIFY_INS (ins);
                break;
@@ -68,14 +84,30 @@ decompose_long_opcode (MonoCompile *cfg, MonoInst *ins, MonoInst **repl_ins)
        case OP_LSUB_OVF:
                if (COMPILE_LLVM (cfg))
                        break;
-               EMIT_NEW_BIALU (cfg, repl, OP_SUBCC, ins->dreg, ins->sreg1, ins->sreg2);
+               {
+                       int opcode;
+#if defined(__mono_ilp32__) && SIZEOF_REGISTER == 8
+                       opcode = OP_LSUBCC;
+#else
+                       opcode = OP_SUBCC;
+#endif
+                       EMIT_NEW_BIALU (cfg, repl, opcode, ins->dreg, ins->sreg1, ins->sreg2);
+               }
                MONO_EMIT_NEW_COND_EXC (cfg, OV, "OverflowException");
                NULLIFY_INS (ins);
                break;
        case OP_LSUB_OVF_UN:
                if (COMPILE_LLVM (cfg))
                        break;
-               EMIT_NEW_BIALU (cfg, repl, OP_SUBCC, ins->dreg, ins->sreg1, ins->sreg2);
+               {
+                       int opcode;
+#if defined(__mono_ilp32__) && SIZEOF_REGISTER == 8
+                       opcode = OP_LSUBCC;
+#else
+                       opcode = OP_SUBCC;
+#endif
+                       EMIT_NEW_BIALU (cfg, repl, opcode, ins->dreg, ins->sreg1, ins->sreg2);
+               }
                MONO_EMIT_NEW_COND_EXC (cfg, C, "OverflowException");
                NULLIFY_INS (ins);
                break;
@@ -237,6 +269,7 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
        MonoInst *repl = NULL;
        int type = ins->type;
        int dreg = ins->dreg;
+       gboolean emulate = FALSE;
 
        /* FIXME: Instead of = NOP, don't emit the original ins at all */
 
@@ -326,7 +359,7 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
                break;
        case OP_ICONV_TO_OVF_U4:
        case OP_ICONV_TO_OVF_I4_UN:
-#if SIZEOF_REGISTER == 4
+#if SIZEOF_VOID_P == 4
        case OP_ICONV_TO_OVF_U:
        case OP_ICONV_TO_OVF_I_UN:
 #endif
@@ -339,21 +372,21 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
        case OP_ICONV_TO_U4:
        case OP_ICONV_TO_OVF_I4:
        case OP_ICONV_TO_OVF_U4_UN:
-#if SIZEOF_REGISTER == 4
+#if SIZEOF_VOID_P == 4
        case OP_ICONV_TO_OVF_I:
        case OP_ICONV_TO_OVF_U_UN:
 #endif
                ins->opcode = OP_MOVE;
                break;
        case OP_ICONV_TO_I:
-#if SIZEOF_REGISTER == 8
+#if SIZEOF_VOID_P == 8
                ins->opcode = OP_SEXT_I4;
 #else
                ins->opcode = OP_MOVE;
 #endif
                break;
        case OP_ICONV_TO_U:
-#if SIZEOF_REGISTER == 8
+#if SIZEOF_VOID_P == 8
                ins->opcode = OP_ZEXT_I4;
 #else
                ins->opcode = OP_MOVE;
@@ -378,18 +411,55 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
                cfg->exception_message = g_strdup_printf ("float conv.ovf.un opcodes not supported.");
                break;
 
-       default: {
-               MonoJitICallInfo *info;
+#if defined(MONO_ARCH_EMULATE_DIV) && defined(MONO_ARCH_HAVE_OPCODE_NEEDS_EMULATION)
+       case OP_IDIV:
+       case OP_IREM:
+       case OP_IDIV_UN:
+       case OP_IREM_UN:
+               if (!mono_arch_opcode_needs_emulation (cfg, ins->opcode)) {
+#ifdef MONO_ARCH_NEED_DIV_CHECK
+                       int reg1 = alloc_ireg (cfg);
+                       int reg2 = alloc_ireg (cfg);
+                       /* b == 0 */
+                       MONO_EMIT_NEW_ICOMPARE_IMM (cfg, ins->sreg2, 0);
+                       MONO_EMIT_NEW_COND_EXC (cfg, IEQ, "DivideByZeroException");
+                       if (ins->opcode == OP_IDIV || ins->opcode == OP_IREM) {
+                               /* b == -1 && a == 0x80000000 */
+                               MONO_EMIT_NEW_ICOMPARE_IMM (cfg, ins->sreg2, -1);
+                               MONO_EMIT_NEW_UNALU (cfg, OP_ICEQ, reg1, -1);
+                               MONO_EMIT_NEW_ICOMPARE_IMM (cfg, ins->sreg1, 0x80000000);
+                               MONO_EMIT_NEW_UNALU (cfg, OP_ICEQ, reg2, -1);
+                               MONO_EMIT_NEW_BIALU (cfg, OP_IAND, reg1, reg1, reg2);
+                               MONO_EMIT_NEW_ICOMPARE_IMM (cfg, reg1, 1);
+                               MONO_EMIT_NEW_COND_EXC (cfg, IEQ, "DivideByZeroException");
+                       }
+#endif
+                       MONO_EMIT_NEW_BIALU (cfg, ins->opcode, ins->dreg, ins->sreg1, ins->sreg2);
+                       ins->opcode = OP_NOP;
+               } else {
+                       emulate = TRUE;
+               }
+               break;
+#endif
+
+       default:
+               emulate = TRUE;
+               break;
+       }
+
+       if (emulate) {
+               MonoJitICallInfo *info = NULL;
 
 #if SIZEOF_REGISTER == 8
                if (decompose_long_opcode (cfg, ins, &repl))
-                       break;
+                       emulate = FALSE;
 #else
                if (COMPILE_LLVM (cfg) && decompose_long_opcode (cfg, ins, &repl))
-                       break;
+                       emulate = FALSE;
 #endif
 
-               info = mono_find_jit_opcode_emulation (ins->opcode);
+               if (emulate)
+                       info = mono_find_jit_opcode_emulation (ins->opcode);
                if (info) {
                        MonoInst **args;
                        MonoInst *call;
@@ -415,8 +485,6 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
 
                        NULLIFY_INS (ins);
                }
-               break;
-       }
        }
 
        if (ins->opcode == OP_NOP) {
@@ -1137,6 +1205,7 @@ mono_decompose_vtype_opts (MonoCompile *cfg)
                                        g_assert (ins->klass);
 
                                        dest_var = get_vreg_to_inst (cfg, ins->dreg);
+                                       // FIXME-VT:
                                        // FIXME:
                                        if (!dest_var)
                                                dest_var = mono_compile_create_var_for_vreg (cfg, &ins->klass->byval_arg, OP_LOCAL, ins->dreg);
@@ -1217,9 +1286,13 @@ mono_decompose_vtype_opts (MonoCompile *cfg)
                                                case 2:
                                                        MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, dest->dreg, 0, call2->inst.dreg);
                                                        break;
+                                               case 3:
                                                case 4:
                                                        MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, dest->dreg, 0, call2->inst.dreg);
                                                        break;
+                                               case 5:
+                                               case 6:
+                                               case 7:
                                                case 8:
 #if SIZEOF_REGISTER == 4
                                                        /*
@@ -1288,6 +1361,71 @@ mono_decompose_vtype_opts (MonoCompile *cfg)
        }
 }
 
+void
+mono_decompose_vtype_opts_llvm (MonoCompile *cfg)
+{
+       MonoBasicBlock *bb, *first_bb;
+
+       /* Decompose only the OP_STOREV_MEMBASE opcodes, which need write barriers */
+
+       cfg->cbb = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock));
+       first_bb = cfg->cbb;
+
+       for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+               MonoInst *ins;
+               MonoInst *prev = NULL;
+               MonoInst *src_var, *src, *dest;
+               gboolean restart;
+               int dreg;
+
+               if (cfg->verbose_level > 2) mono_print_bb (bb, "BEFORE LOWER-VTYPE-OPTS(LLVM) ");
+
+               cfg->cbb->code = cfg->cbb->last_ins = NULL;
+               restart = TRUE;
+
+               while (restart) {
+                       restart = FALSE;
+
+                       for (ins = bb->code; ins; ins = ins->next) {
+                               switch (ins->opcode) {
+                               case OP_STOREV_MEMBASE: {
+                                       src_var = get_vreg_to_inst (cfg, ins->sreg1);
+
+                                       if (!src_var) {
+                                               g_assert (ins->klass);
+                                               src_var = mono_compile_create_var_for_vreg (cfg, &ins->klass->byval_arg, OP_LOCAL, ins->sreg1);
+                                       }
+
+                                       EMIT_NEW_VARLOADA_VREG ((cfg), (src), ins->sreg1, &ins->klass->byval_arg);
+
+                                       dreg = alloc_preg (cfg);
+                                       EMIT_NEW_BIALU_IMM (cfg, dest, OP_ADD_IMM, dreg, ins->inst_destbasereg, ins->inst_offset);
+                                       mini_emit_stobj (cfg, dest, src, src_var->klass, src_var->backend.is_pinvoke);
+                                       break;
+                               }
+                               default:
+                                       break;
+                               }
+
+                               g_assert (cfg->cbb == first_bb);
+
+                               if (cfg->cbb->code || (cfg->cbb != first_bb)) {
+                                       /* Replace the original instruction with the new code sequence */
+
+                                       mono_replace_ins (cfg, bb, ins, &prev, first_bb, cfg->cbb);
+                                       first_bb->code = first_bb->last_ins = NULL;
+                                       first_bb->in_count = first_bb->out_count = 0;
+                                       cfg->cbb = first_bb;
+                               }
+                               else
+                                       prev = ins;
+                       }
+               }
+
+               if (cfg->verbose_level > 2) mono_print_bb (bb, "AFTER LOWER-VTYPE-OPTS(LLVM) ");
+       }
+}
+
 inline static MonoInst *
 mono_get_domainvar (MonoCompile *cfg)
 {
@@ -1340,7 +1478,7 @@ mono_decompose_array_access_opts (MonoCompile *cfg)
                                switch (ins->opcode) {
                                case OP_LDLEN:
                                        NEW_LOAD_MEMBASE_FLAGS (cfg, dest, OP_LOADI4_MEMBASE, ins->dreg, ins->sreg1,
-                                                                                       G_STRUCT_OFFSET (MonoArray, max_length), ins->flags | MONO_INST_CONSTANT_LOAD);
+                                                                                       G_STRUCT_OFFSET (MonoArray, max_length), ins->flags | MONO_INST_INVARIANT_LOAD);
                                        MONO_ADD_INS (cfg->cbb, dest);
                                        break;
                                case OP_BOUNDS_CHECK:
@@ -1360,8 +1498,9 @@ mono_decompose_array_access_opts (MonoCompile *cfg)
                                                dest = mono_emit_jit_icall (cfg, mono_array_new, iargs);
                                                dest->dreg = ins->dreg;
                                        } else {
-                                               MonoVTable *vtable = mono_class_vtable (cfg->domain, mono_array_class_get (ins->inst_newa_class, 1));
-                                               MonoMethod *managed_alloc = mono_gc_get_managed_array_allocator (vtable, 1);
+                                               MonoClass *array_class = mono_array_class_get (ins->inst_newa_class, 1);
+                                               MonoVTable *vtable = mono_class_vtable (cfg->domain, array_class);
+                                               MonoMethod *managed_alloc = mono_gc_get_managed_array_allocator (array_class);
 
                                                g_assert (vtable); /*This shall not fail since we check for this condition on OP_NEWARR creation*/
                                                NEW_VTABLECONST (cfg, iargs [0], vtable);
@@ -1378,7 +1517,7 @@ mono_decompose_array_access_opts (MonoCompile *cfg)
                                        break;
                                case OP_STRLEN:
                                        MONO_EMIT_NEW_LOAD_MEMBASE_OP_FLAGS (cfg, OP_LOADI4_MEMBASE, ins->dreg,
-                                                                                                                ins->sreg1, G_STRUCT_OFFSET (MonoString, length), ins->flags | MONO_INST_CONSTANT_LOAD);
+                                                                                                                ins->sreg1, G_STRUCT_OFFSET (MonoString, length), ins->flags | MONO_INST_INVARIANT_LOAD);
                                        break;
                                default:
                                        break;
@@ -1409,7 +1548,7 @@ typedef union {
        double vald;
 } DVal;
 
-#ifdef MONO_ARCH_SOFT_FLOAT
+#ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
 
 /**
  * mono_decompose_soft_float:
@@ -1527,6 +1666,7 @@ mono_decompose_soft_float (MonoCompile *cfg)
                                                MonoCallInst *call2;
                                                MonoInst *iargs [1];
                                                MonoInst *conv;
+                                               GSList *l;
 
                                                /* Convert the call into a call returning an int */
                                                MONO_INST_NEW_CALL (cfg, call2, OP_CALL);
@@ -1547,6 +1687,10 @@ mono_decompose_soft_float (MonoCompile *cfg)
                                                call2->inst.dreg = mono_alloc_ireg (cfg);
                                                MONO_ADD_INS (cfg->cbb, (MonoInst*)call2);
 
+                                               /* Remap OUTARG_VT instructions referencing this call */
+                                               for (l = call->outarg_vts; l; l = l->next)
+                                                       ((MonoInst*)(l->data))->inst_p0 = call2;
+
                                                /* FIXME: Optimize this */
 
                                                /* Emit an r4->r8 conversion */
@@ -1580,8 +1724,18 @@ mono_decompose_soft_float (MonoCompile *cfg)
 
                                        /* Convert fcompare+fbcc to icall+icompare+beq */
 
+                                       if (!ins->next) {
+                                               /* The branch might be optimized away */
+                                               NULLIFY_INS (ins);
+                                               break;
+                                       }
+
                                        info = mono_find_jit_opcode_emulation (ins->next->opcode);
-                                       g_assert (info);
+                                       if (!info) {
+                                               /* The branch might be optimized away */
+                                               NULLIFY_INS (ins);
+                                               break;
+                                       }
 
                                        /* Create dummy MonoInst's for the arguments */
                                        MONO_INST_NEW (cfg, iargs [0], OP_ARG);