2009-07-01 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / mini / decompose.c
index 0fe18d5da7be4eebe5abc42b3e296870ee71bbff..f6f51b08aa79a70fa991109a82a57b07d4bf1891 100644 (file)
@@ -10,6 +10,8 @@
 #include "mini.h"
 #include "ir-emit.h"
 
+#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);
 void mini_emit_stobj (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *klass, gboolean native);
@@ -20,10 +22,17 @@ void mini_emit_initobj (MonoCompile *cfg, MonoInst *dest, const guchar *ip, Mono
  *
  *   Decompose complex opcodes into ones closer to opcodes supported by
  * the given architecture.
+ * Returns a MonoInst which represents the result of the decomposition, and can
+ * be pushed on the IL stack. This is needed because the original instruction is
+ * nullified.
  */
-void
+MonoInst*
 mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
 {
+       MonoInst *repl = NULL;
+       int type = ins->type;
+       int dreg = ins->dreg;
+
        /* FIXME: Instead of = NOP, don't emit the original ins at all */
 
 #ifdef MONO_ARCH_HAVE_DECOMPOSE_OPTS
@@ -39,18 +48,26 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
        /* this doesn't make sense on ppc and other architectures */
 #if !defined(MONO_ARCH_NO_IOV_CHECK)
        case OP_IADD_OVF:
+               if (COMPILE_LLVM (cfg))
+                       break;
                ins->opcode = OP_IADDCC;
                MONO_EMIT_NEW_COND_EXC (cfg, IOV, "OverflowException");
                break;
        case OP_IADD_OVF_UN:
+               if (COMPILE_LLVM (cfg))
+                       break;
                ins->opcode = OP_IADDCC;
                MONO_EMIT_NEW_COND_EXC (cfg, IC, "OverflowException");
                break;
        case OP_ISUB_OVF:
+               if (COMPILE_LLVM (cfg))
+                       break;
                ins->opcode = OP_ISUBCC;
                MONO_EMIT_NEW_COND_EXC (cfg, IOV, "OverflowException");
                break;
        case OP_ISUB_OVF_UN:
+               if (COMPILE_LLVM (cfg))
+                       break;
                ins->opcode = OP_ISUBCC;
                MONO_EMIT_NEW_COND_EXC (cfg, IC, "OverflowException");
                break;
@@ -61,14 +78,14 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
                MONO_EMIT_NEW_ICOMPARE_IMM (cfg, ins->sreg1, -128);
                MONO_EMIT_NEW_COND_EXC (cfg, ILT, "OverflowException");
                MONO_EMIT_NEW_UNALU (cfg, OP_ICONV_TO_I1, ins->dreg, ins->sreg1);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_ICONV_TO_OVF_I1_UN:
                /* probe values between 0 to 127 */
                MONO_EMIT_NEW_ICOMPARE_IMM (cfg, ins->sreg1, 127);
                MONO_EMIT_NEW_COND_EXC (cfg, IGT_UN, "OverflowException");
                MONO_EMIT_NEW_UNALU (cfg, OP_ICONV_TO_I1, ins->dreg, ins->sreg1);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_ICONV_TO_OVF_U1:
        case OP_ICONV_TO_OVF_U1_UN:
@@ -76,7 +93,7 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
                MONO_EMIT_NEW_COMPARE_IMM (cfg, ins->sreg1, 255);
                MONO_EMIT_NEW_COND_EXC (cfg, IGT_UN, "OverflowException");
                MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, ins->dreg, ins->sreg1, 0xff);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_ICONV_TO_OVF_I2:
                /* Probe value to be within -32768 and 32767 */
@@ -85,14 +102,14 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
                MONO_EMIT_NEW_ICOMPARE_IMM (cfg, ins->sreg1, -32768);
                MONO_EMIT_NEW_COND_EXC (cfg, ILT, "OverflowException");
                MONO_EMIT_NEW_UNALU (cfg, OP_ICONV_TO_I2, ins->dreg, ins->sreg1);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_ICONV_TO_OVF_I2_UN:
                /* Convert uint value into short, value within 0 and 32767 */
                MONO_EMIT_NEW_ICOMPARE_IMM (cfg, ins->sreg1, 32767);
                MONO_EMIT_NEW_COND_EXC (cfg, IGT_UN, "OverflowException");
                MONO_EMIT_NEW_UNALU (cfg, OP_ICONV_TO_I2, ins->dreg, ins->sreg1);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_ICONV_TO_OVF_U2:
        case OP_ICONV_TO_OVF_U2_UN:
@@ -100,37 +117,37 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
                MONO_EMIT_NEW_ICOMPARE_IMM (cfg, ins->sreg1, 0xffff);
                MONO_EMIT_NEW_COND_EXC (cfg, IGT_UN, "OverflowException");
                MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, ins->dreg, ins->sreg1, 0xffff);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_ICONV_TO_OVF_U4:
        case OP_ICONV_TO_OVF_I4_UN:
-#if SIZEOF_VOID_P == 4
+#if SIZEOF_REGISTER == 4
        case OP_ICONV_TO_OVF_U:
        case OP_ICONV_TO_OVF_I_UN:
 #endif
                MONO_EMIT_NEW_ICOMPARE_IMM (cfg, ins->sreg1, 0);
                MONO_EMIT_NEW_COND_EXC (cfg, ILT, "OverflowException");
                MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, ins->dreg, ins->sreg1);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_ICONV_TO_I4:
        case OP_ICONV_TO_U4:
        case OP_ICONV_TO_OVF_I4:
-#if SIZEOF_VOID_P == 4
+#if SIZEOF_REGISTER == 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_VOID_P == 8
+#if SIZEOF_REGISTER == 8
                ins->opcode = OP_SEXT_I4;
 #else
                ins->opcode = OP_MOVE;
 #endif
                break;
        case OP_ICONV_TO_U:
-#if SIZEOF_VOID_P == 8
+#if SIZEOF_REGISTER == 8
                ins->opcode = OP_ZEXT_I4;
 #else
                ins->opcode = OP_MOVE;
@@ -142,10 +159,10 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
                break;
 
                /* Long opcodes on 64 bit machines */
-#if SIZEOF_VOID_P == 8
+#if SIZEOF_REGISTER == 8
        case OP_LCONV_TO_I4:
                MONO_EMIT_NEW_BIALU_IMM (cfg, OP_LSHR_IMM, ins->dreg, ins->sreg1, 0);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_LCONV_TO_I8:
        case OP_LCONV_TO_I:
@@ -162,28 +179,38 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
        case OP_LCONV_TO_U4:
                /* Clean out the upper word */
                MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHR_UN_IMM, ins->dreg, ins->sreg1, 0);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_LADD_OVF:
-               MONO_EMIT_NEW_BIALU (cfg, OP_ADDCC, ins->dreg, ins->sreg1, ins->sreg2);
+               if (COMPILE_LLVM (cfg))
+                       break;
+               EMIT_NEW_BIALU (cfg, repl, OP_ADDCC, ins->dreg, ins->sreg1, ins->sreg2);
                MONO_EMIT_NEW_COND_EXC (cfg, OV, "OverflowException");
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_LADD_OVF_UN:
-               MONO_EMIT_NEW_BIALU (cfg, OP_ADDCC, ins->dreg, ins->sreg1, ins->sreg2);
+               if (COMPILE_LLVM (cfg))
+                       break;
+               EMIT_NEW_BIALU (cfg, repl, OP_ADDCC, ins->dreg, ins->sreg1, ins->sreg2);
                MONO_EMIT_NEW_COND_EXC (cfg, C, "OverflowException");
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
+#ifndef __mono_ppc64__
        case OP_LSUB_OVF:
-               MONO_EMIT_NEW_BIALU (cfg, OP_SUBCC, ins->dreg, ins->sreg1, ins->sreg2);
+               if (COMPILE_LLVM (cfg))
+                       break;
+               EMIT_NEW_BIALU (cfg, repl, OP_SUBCC, ins->dreg, ins->sreg1, ins->sreg2);
                MONO_EMIT_NEW_COND_EXC (cfg, OV, "OverflowException");
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_LSUB_OVF_UN:
-               MONO_EMIT_NEW_BIALU (cfg, OP_SUBCC, ins->dreg, ins->sreg1, ins->sreg2);
+               if (COMPILE_LLVM (cfg))
+                       break;
+               EMIT_NEW_BIALU (cfg, repl, OP_SUBCC, ins->dreg, ins->sreg1, ins->sreg2);
                MONO_EMIT_NEW_COND_EXC (cfg, C, "OverflowException");
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
+#endif
                
        case OP_ICONV_TO_OVF_I8:
        case OP_ICONV_TO_OVF_I:
@@ -194,7 +221,7 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
                MONO_EMIT_NEW_COMPARE_IMM (cfg,ins->sreg1, 0);
                MONO_EMIT_NEW_COND_EXC (cfg, LT, "OverflowException");
                MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, ins->dreg, ins->sreg1);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_ICONV_TO_OVF_I8_UN:
        case OP_ICONV_TO_OVF_U8_UN:
@@ -203,7 +230,7 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
                /* an unsigned 32 bit num always fits in an (un)signed 64 bit one */
                /* Clean out the upper word */
                MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHR_UN_IMM, ins->dreg, ins->sreg1, 0);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_LCONV_TO_OVF_I1:
                MONO_EMIT_NEW_COMPARE_IMM (cfg, ins->sreg1, 127);
@@ -211,27 +238,27 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
                MONO_EMIT_NEW_COMPARE_IMM (cfg, ins->sreg1, -128);
                MONO_EMIT_NEW_COND_EXC (cfg, LT, "OverflowException");
                MONO_EMIT_NEW_UNALU (cfg, OP_LCONV_TO_I1, ins->dreg, ins->sreg1);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_LCONV_TO_OVF_I1_UN:
                MONO_EMIT_NEW_COMPARE_IMM (cfg, ins->sreg1, 127);
                MONO_EMIT_NEW_COND_EXC (cfg, GT_UN, "OverflowException");
                MONO_EMIT_NEW_UNALU (cfg, OP_LCONV_TO_I1, ins->dreg, ins->sreg1);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_LCONV_TO_OVF_U1:
                /* probe value to be within 0 to 255 */
                MONO_EMIT_NEW_COMPARE_IMM (cfg, ins->sreg1, 255);
                MONO_EMIT_NEW_COND_EXC (cfg, GT_UN, "OverflowException");
                MONO_EMIT_NEW_BIALU_IMM (cfg, OP_AND_IMM, ins->dreg, ins->sreg1, 0xff);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_LCONV_TO_OVF_U1_UN:
                /* probe value to be within 0 to 255 */
                MONO_EMIT_NEW_COMPARE_IMM (cfg, ins->sreg1, 255);
                MONO_EMIT_NEW_COND_EXC (cfg, GT_UN, "OverflowException");
                MONO_EMIT_NEW_BIALU_IMM (cfg, OP_AND_IMM, ins->dreg, ins->sreg1, 0xff);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_LCONV_TO_OVF_I2:
                /* Probe value to be within -32768 and 32767 */
@@ -240,28 +267,28 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
                MONO_EMIT_NEW_COMPARE_IMM (cfg, ins->sreg1, -32768);
                MONO_EMIT_NEW_COND_EXC (cfg, LT, "OverflowException");
                MONO_EMIT_NEW_UNALU (cfg, OP_LCONV_TO_I2, ins->dreg, ins->sreg1);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_LCONV_TO_OVF_I2_UN:
                /* Probe value to be within 0 and 32767 */
                MONO_EMIT_NEW_COMPARE_IMM (cfg, ins->sreg1, 32767);
                MONO_EMIT_NEW_COND_EXC (cfg, GT_UN, "OverflowException");
                MONO_EMIT_NEW_UNALU (cfg, OP_LCONV_TO_I2, ins->dreg, ins->sreg1);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_LCONV_TO_OVF_U2:
                /* Probe value to be within 0 and 65535 */
                MONO_EMIT_NEW_COMPARE_IMM (cfg, ins->sreg1, 0xffff);
                MONO_EMIT_NEW_COND_EXC (cfg, GT_UN, "OverflowException");
                MONO_EMIT_NEW_BIALU_IMM (cfg, OP_AND_IMM, ins->dreg, ins->sreg1, 0xffff);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_LCONV_TO_OVF_U2_UN:
                /* Probe value to be within 0 and 65535 */
                MONO_EMIT_NEW_COMPARE_IMM (cfg, ins->sreg1, 0xffff);
                MONO_EMIT_NEW_COND_EXC (cfg, GT_UN, "OverflowException");
                MONO_EMIT_NEW_BIALU_IMM (cfg, OP_AND_IMM, ins->dreg, ins->sreg1, 0xffff);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_LCONV_TO_OVF_I4:
                MONO_EMIT_NEW_COMPARE_IMM (cfg, ins->sreg1, 0x7fffffff);
@@ -270,13 +297,13 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
                MONO_EMIT_NEW_COMPARE_IMM (cfg, ins->sreg1, ((int)-2147483648));
                MONO_EMIT_NEW_COND_EXC (cfg, LT, "OverflowException");
                MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, ins->dreg, ins->sreg1);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_LCONV_TO_OVF_I4_UN:
                MONO_EMIT_NEW_COMPARE_IMM (cfg, ins->sreg1, 0x7fffffff);
                MONO_EMIT_NEW_COND_EXC (cfg, GT_UN, "OverflowException");
                MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, ins->dreg, ins->sreg1);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_LCONV_TO_OVF_U4:
                MONO_EMIT_NEW_COMPARE_IMM (cfg, ins->sreg1, 0xffffffffUL);
@@ -284,13 +311,13 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
                MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, ins->sreg1, 0);
                MONO_EMIT_NEW_COND_EXC (cfg, LT, "OverflowException");
                MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, ins->dreg, ins->sreg1);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_LCONV_TO_OVF_U4_UN:
                MONO_EMIT_NEW_COMPARE_IMM (cfg, ins->sreg1, 0xffffffff);
                MONO_EMIT_NEW_COND_EXC (cfg, GT_UN, "OverflowException");
                MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, ins->dreg, ins->sreg1);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_LCONV_TO_OVF_I:
        case OP_LCONV_TO_OVF_U_UN:
@@ -302,14 +329,14 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
                MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, ins->sreg1, 0);
                MONO_EMIT_NEW_COND_EXC (cfg, LT, "OverflowException");
                MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, ins->dreg, ins->sreg1);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
        case OP_LCONV_TO_OVF_U8:
        case OP_LCONV_TO_OVF_U:
                MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, ins->sreg1, 0);
                MONO_EMIT_NEW_COND_EXC (cfg, LT, "OverflowException");
                MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, ins->dreg, ins->sreg1);
-               ins->opcode = OP_NOP;
+               NULLIFY_INS (ins);
                break;
 #endif
 
@@ -323,29 +350,47 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
 
                        /* Create dummy MonoInst's for the arguments */
                        g_assert (!info->sig->hasthis);
-                       g_assert (info->sig->param_count <= 2);
+                       g_assert (info->sig->param_count <= MONO_MAX_SRC_REGS);
 
                        args = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * info->sig->param_count);
                        if (info->sig->param_count > 0) {
-                               MONO_INST_NEW (cfg, args [0], OP_ARG);
-                               args [0]->dreg = ins->sreg1;
-                       }
-                       if (info->sig->param_count > 1) {
-                               MONO_INST_NEW (cfg, args [1], OP_ARG);
-                               args [1]->dreg = ins->sreg2;
+                               int sregs [MONO_MAX_SRC_REGS];
+                               int num_sregs, i;
+                               num_sregs = mono_inst_get_src_registers (ins, sregs);
+                               g_assert (num_sregs == info->sig->param_count);
+                               for (i = 0; i < num_sregs; ++i) {
+                                       MONO_INST_NEW (cfg, args [i], OP_ARG);
+                                       args [i]->dreg = sregs [i];
+                               }
                        }
 
                        call = mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, args);
                        call->dreg = ins->dreg;
 
-                       ins->opcode = OP_NOP;
+                       NULLIFY_INS (ins);
                }
                break;
        }
        }
+
+       if (ins->opcode == OP_NOP) {
+               if (repl) {
+                       repl->type = type;
+                       return repl;
+               } else {
+                       /* Use the last emitted instruction */
+                       ins = cfg->cbb->last_ins;
+                       g_assert (ins);
+                       ins->type = type;
+                       g_assert (ins->dreg == dreg);
+                       return ins;
+               }
+       } else {
+               return ins;
+       }
 }
 
-#if SIZEOF_VOID_P == 4
+#if SIZEOF_REGISTER == 4
 static int lbr_decomp [][2] = {
        {0, 0}, /* BEQ */
        {OP_IBGT, OP_IBGE_UN}, /* BGE */
@@ -376,7 +421,7 @@ static int lcset_decomp [][2] = {
 void
 mono_decompose_long_opts (MonoCompile *cfg)
 {
-#if SIZEOF_VOID_P == 4
+#if SIZEOF_REGISTER == 4
        MonoBasicBlock *bb, *first_bb;
 
        /*
@@ -634,6 +679,7 @@ mono_decompose_long_opts (MonoCompile *cfg)
                                MONO_EMIT_NEW_BIALU (cfg, OP_ISUBCC, tree->dreg + 1, tree->sreg1 + 1, tree->sreg2 + 1);
                                MONO_EMIT_NEW_BIALU (cfg, OP_ISBB, tree->dreg + 2, tree->sreg1 + 2, tree->sreg2 + 2);
                                break;
+
                        case OP_LADD_OVF:
                                /* ADC sets the condition code */
                                MONO_EMIT_NEW_BIALU (cfg, OP_IADDCC, tree->dreg + 1, tree->sreg1 + 1, tree->sreg2 + 1);
@@ -675,24 +721,8 @@ mono_decompose_long_opts (MonoCompile *cfg)
                                MONO_EMIT_NEW_UNALU (cfg, OP_INOT, tree->dreg + 2, tree->sreg1 + 2);
                                break;
                        case OP_LNEG:
-                               /* 
-                                * FIXME: The original version in inssel-long32.brg does not work
-                                * on x86, and the x86 version might not work on other archs ?
-                                */
-                               /* FIXME: Move these to mono_arch_decompose_long_opts () */
-#if defined(__i386__)
-                               MONO_EMIT_NEW_UNALU (cfg, OP_INEG, tree->dreg + 1, tree->sreg1 + 1);
-                               MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ADC_IMM, tree->dreg + 2, tree->sreg1 + 2, 0);
-                               MONO_EMIT_NEW_UNALU (cfg, OP_INEG, tree->dreg + 2, tree->dreg + 2);
-#elif defined(__sparc__)
-                               MONO_EMIT_NEW_BIALU (cfg, OP_SUBCC, tree->dreg + 1, 0, tree->sreg1 + 1);
-                               MONO_EMIT_NEW_BIALU (cfg, OP_SBB, tree->dreg + 2, 0, tree->sreg1 + 2);
-#elif defined(__arm__)
-                               MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ARM_RSBS_IMM, tree->dreg + 1, tree->sreg1 + 1, 0);
-                               MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ARM_RSC_IMM, tree->dreg + 2, tree->sreg1 + 2, 0);
-#else
-                               NOT_IMPLEMENTED;
-#endif
+                               /* Handled in mono_arch_decompose_long_opts () */
+                               g_assert_not_reached ();
                                break;
                        case OP_LMUL:
                                /* Emulated */
@@ -991,7 +1021,7 @@ mono_decompose_vtype_opts (MonoCompile *cfg)
                gboolean restart;
                int dreg;
 
-               if (cfg->verbose_level > 1) mono_print_bb (bb, "BEFORE LOWER-VTYPE-OPTS ");
+               if (cfg->verbose_level > 2) mono_print_bb (bb, "BEFORE LOWER-VTYPE-OPTS ");
 
                cfg->cbb->code = cfg->cbb->last_ins = NULL;
                restart = TRUE;
@@ -1118,7 +1148,7 @@ mono_decompose_vtype_opts (MonoCompile *cfg)
 
                                                /* Save the result */
                                                if (dest_var->backend.is_pinvoke)
-                                                       size = mono_class_native_size (dest->inst_vtype->data.klass, NULL);
+                                                       size = mono_class_native_size (mono_class_from_mono_type (dest_var->inst_vtype), NULL);
                                                else
                                                        size = mono_type_size (dest_var->inst_vtype, NULL);
                                                switch (size) {
@@ -1132,7 +1162,28 @@ mono_decompose_vtype_opts (MonoCompile *cfg)
                                                        MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, dest->dreg, 0, call2->inst.dreg);
                                                        break;
                                                case 8:
+#if SIZEOF_REGISTER == 4
+                                                       /*
+                                                       FIXME Other ABIs might return in different regs than the ones used for LCALL.
+                                                       FIXME It would be even nicer to be able to leverage the long decompose stuff.
+                                                       */
+                                                       switch (call2->inst.opcode) {
+                                                       case OP_CALL:
+                                                               call2->inst.opcode = OP_LCALL;
+                                                               break;
+                                                       case OP_CALL_REG:
+                                                               call2->inst.opcode = OP_LCALL_REG;
+                                                               break;
+                                                       case OP_CALL_MEMBASE:
+                                                               call2->inst.opcode = OP_LCALL_MEMBASE;
+                                                               break;
+                                                       }
+                                                       call2->inst.dreg = alloc_lreg (cfg);
+                                                       MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, dest->dreg, MINI_MS_WORD_OFFSET, call2->inst.dreg + 2);
+                                                       MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, dest->dreg, MINI_LS_WORD_OFFSET, call2->inst.dreg + 1);
+#else
                                                        MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, dest->dreg, 0, call2->inst.dreg);
+#endif
                                                        break;
                                                default:
                                                        /* This assumes the vtype is sizeof (gpointer) long */
@@ -1174,6 +1225,8 @@ mono_decompose_vtype_opts (MonoCompile *cfg)
                        }
                }
 
-               if (cfg->verbose_level > 1) mono_print_bb (bb, "AFTER LOWER-VTYPE-OPTS ");
+               if (cfg->verbose_level > 2) mono_print_bb (bb, "AFTER LOWER-VTYPE-OPTS ");
        }
 }
+
+#endif /* DISABLE_JIT */