New tests.
[mono.git] / mono / mini / decompose.c
index 2d2ae39c28973363c8a857da8039bb7d0d7e3f0d..8fa6bb3eb3c00b8d2165ed54cc2eef8dcee851d6 100644 (file)
@@ -9,6 +9,9 @@
 
 #include "mini.h"
 #include "ir-emit.h"
+#include "jit-icalls.h"
+
+#include <mono/metadata/gc-internal.h>
 
 #ifndef DISABLE_JIT
 
@@ -22,10 +25,18 @@ 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.
+ * Sets the cfg exception if an opcode is not supported.
  */
-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
@@ -41,18 +52,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;
@@ -63,14 +82,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:
@@ -78,7 +97,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 */
@@ -87,14 +106,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:
@@ -102,7 +121,7 @@ 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:
@@ -113,11 +132,12 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
                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:
+       case OP_ICONV_TO_OVF_U4_UN:
 #if SIZEOF_REGISTER == 4
        case OP_ICONV_TO_OVF_I:
        case OP_ICONV_TO_OVF_U_UN:
@@ -143,11 +163,25 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
                ins->opcode = OP_FMOVE;
                break;
 
+       case OP_FCONV_TO_OVF_I1_UN:
+       case OP_FCONV_TO_OVF_I2_UN:
+       case OP_FCONV_TO_OVF_I4_UN:
+       case OP_FCONV_TO_OVF_I8_UN:
+       case OP_FCONV_TO_OVF_U1_UN:
+       case OP_FCONV_TO_OVF_U2_UN:
+       case OP_FCONV_TO_OVF_U4_UN:
+       case OP_FCONV_TO_OVF_U8_UN:
+       case OP_FCONV_TO_OVF_I_UN:
+       case OP_FCONV_TO_OVF_U_UN:
+               cfg->exception_type = MONO_EXCEPTION_INVALID_PROGRAM;
+               cfg->exception_message = g_strdup_printf ("float conv.ovf.un opcodes not supported.");
+               break;
+
                /* Long opcodes on 64 bit machines */
 #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:
@@ -164,60 +198,37 @@ 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;
-#if defined(__mono_ppc__) && !defined(__mono_ppc64__)
        case OP_LADD_OVF:
-               /* ADC sets the condition code */
-               MONO_EMIT_NEW_BIALU (cfg, OP_ADDCC, ins->dreg + 1, ins->sreg1 + 1, ins->sreg2 + 1);
-               MONO_EMIT_NEW_BIALU (cfg, OP_ADD_OVF_CARRY, ins->dreg + 2, ins->sreg1 + 2, ins->sreg2 + 2);
-               ins->opcode = OP_NOP;
-               g_assert_not_reached ();
-               break;
-       case OP_LADD_OVF_UN:
-               /* ADC sets the condition code */
-               MONO_EMIT_NEW_BIALU (cfg, OP_ADDCC, ins->dreg + 1, ins->sreg1 + 1, ins->sreg2 + 1);
-               MONO_EMIT_NEW_BIALU (cfg, OP_ADD_OVF_UN_CARRY, ins->dreg + 2, ins->sreg1 + 2, ins->sreg2 + 2);
-               ins->opcode = OP_NOP;
-               g_assert_not_reached ();
-               break;
-       case OP_LSUB_OVF:
-               /* SBB sets the condition code */
-               MONO_EMIT_NEW_BIALU (cfg, OP_SUBCC, ins->dreg + 1, ins->sreg1 + 1, ins->sreg2 + 1);
-               MONO_EMIT_NEW_BIALU (cfg, OP_SUB_OVF_CARRY, ins->dreg + 2, ins->sreg1 + 2, ins->sreg2 + 2);
-               ins->opcode = OP_NOP;
-               g_assert_not_reached ();
-               break;
-       case OP_LSUB_OVF_UN:
-               /* SBB sets the condition code */
-               MONO_EMIT_NEW_BIALU (cfg, OP_SUBCC, ins->dreg + 1, ins->sreg1 + 1, ins->sreg2 + 1);
-               MONO_EMIT_NEW_BIALU (cfg, OP_SUB_OVF_UN_CARRY, ins->dreg + 2, ins->sreg1 + 2, ins->sreg2 + 2);
-               ins->opcode = OP_NOP;
-               g_assert_not_reached ();
-               break;
-#else
-       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
 #endif
                
        case OP_ICONV_TO_OVF_I8:
@@ -229,7 +240,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:
@@ -238,7 +249,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);
@@ -246,27 +257,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 */
@@ -275,28 +286,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);
@@ -305,13 +316,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);
@@ -319,17 +330,18 @@ 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:
        case OP_LCONV_TO_OVF_U8_UN:
+       case OP_LCONV_TO_OVF_I8:
                ins->opcode = OP_MOVE;
                break;
        case OP_LCONV_TO_OVF_I_UN:
@@ -337,14 +349,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
 
@@ -358,26 +370,44 @@ 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_REGISTER == 4
@@ -454,6 +484,7 @@ mono_decompose_long_opts (MonoCompile *cfg)
                        case OP_LCONV_TO_U8:
                        case OP_LCONV_TO_I8:
                        case OP_LCONV_TO_OVF_U8_UN:
+                       case OP_LCONV_TO_OVF_I8:
                                MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, tree->dreg + 1, tree->sreg1 + 1);
                                MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, tree->dreg + 2, tree->sreg1 + 2);
                                break;
@@ -670,28 +701,6 @@ mono_decompose_long_opts (MonoCompile *cfg)
                                MONO_EMIT_NEW_BIALU (cfg, OP_ISBB, tree->dreg + 2, tree->sreg1 + 2, tree->sreg2 + 2);
                                break;
 
-#if defined(__ppc__) || defined(__powerpc__)
-                       case OP_LADD_OVF:
-                               /* ADC sets the condition code */
-                               MONO_EMIT_NEW_BIALU (cfg, OP_ADDCC, tree->dreg + 1, tree->sreg1 + 1, tree->sreg2 + 1);
-                               MONO_EMIT_NEW_BIALU (cfg, OP_ADD_OVF_CARRY, tree->dreg + 2, tree->sreg1 + 2, tree->sreg2 + 2);
-                               break;
-                       case OP_LADD_OVF_UN:
-                               /* ADC sets the condition code */
-                               MONO_EMIT_NEW_BIALU (cfg, OP_ADDCC, tree->dreg + 1, tree->sreg1 + 1, tree->sreg2 + 1);
-                               MONO_EMIT_NEW_BIALU (cfg, OP_ADD_OVF_UN_CARRY, tree->dreg + 2, tree->sreg1 + 2, tree->sreg2 + 2);
-                               break;
-                       case OP_LSUB_OVF:
-                               /* SBB sets the condition code */
-                               MONO_EMIT_NEW_BIALU (cfg, OP_SUBCC, tree->dreg + 1, tree->sreg1 + 1, tree->sreg2 + 1);
-                               MONO_EMIT_NEW_BIALU (cfg, OP_SUB_OVF_CARRY, tree->dreg + 2, tree->sreg1 + 2, tree->sreg2 + 2);
-                               break;
-                       case OP_LSUB_OVF_UN:
-                               /* SBB sets the condition code */
-                               MONO_EMIT_NEW_BIALU (cfg, OP_SUBCC, tree->dreg + 1, tree->sreg1 + 1, tree->sreg2 + 1);
-                               MONO_EMIT_NEW_BIALU (cfg, OP_SUB_OVF_UN_CARRY, tree->dreg + 2, tree->sreg1 + 2, tree->sreg2 + 2);
-                               break;
-#else
                        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);
@@ -716,7 +725,6 @@ mono_decompose_long_opts (MonoCompile *cfg)
                                MONO_EMIT_NEW_BIALU (cfg, OP_ISBB, tree->dreg + 2, tree->sreg1 + 2, tree->sreg2 + 2);
                                MONO_EMIT_NEW_COND_EXC (cfg, C, "OverflowException");
                                break;
-#endif
                        case OP_LAND:
                                MONO_EMIT_NEW_BIALU (cfg, OP_IAND, tree->dreg + 1, tree->sreg1 + 1, tree->sreg2 + 1);
                                MONO_EMIT_NEW_BIALU (cfg, OP_IAND, tree->dreg + 2, tree->sreg1 + 2, tree->sreg2 + 2);
@@ -734,31 +742,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);
-#elif defined(__ppc__) || defined(__powerpc__)
-                               /* This is the old version from inssel-long32.brg */
-                               MONO_EMIT_NEW_UNALU (cfg, OP_INOT, tree->dreg + 1, tree->sreg1 + 1);
-                               MONO_EMIT_NEW_UNALU (cfg, OP_INOT, tree->dreg + 2, tree->sreg1 + 2);
-                               /* ADC sets the condition codes */
-                               MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ADC_IMM, tree->dreg + 1, tree->dreg + 1, 1);
-                               MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ADC_IMM, tree->dreg + 2, tree->dreg + 2, 0);
-#else
-                               NOT_IMPLEMENTED;
-#endif
+                               /* Handled in mono_arch_decompose_long_opts () */
+                               g_assert_not_reached ();
                                break;
                        case OP_LMUL:
                                /* Emulated */
@@ -1184,7 +1169,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) {
@@ -1200,7 +1185,7 @@ mono_decompose_vtype_opts (MonoCompile *cfg)
                                                case 8:
 #if SIZEOF_REGISTER == 4
                                                        /*
-                                                       FIXME It would be nice to fix the operding of OP_CALL to make it possible to use numbering voodoo
+                                                       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) {
@@ -1265,4 +1250,407 @@ mono_decompose_vtype_opts (MonoCompile *cfg)
        }
 }
 
+inline static MonoInst *
+mono_get_domainvar (MonoCompile *cfg)
+{
+       if (!cfg->domainvar)
+               cfg->domainvar = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
+       return cfg->domainvar;
+}
+
+/**
+ * mono_decompose_array_access_opts:
+ *
+ *  Decompose array access opcodes.
+ */
+void
+mono_decompose_array_access_opts (MonoCompile *cfg)
+{
+       MonoBasicBlock *bb, *first_bb;
+
+       /*
+        * Unlike decompose_long_opts, this pass does not alter the CFG of the method so it 
+        * can be executed anytime. It should be run before decompose_long
+        */
+
+       /**
+        * Create a dummy bblock and emit code into it so we can use the normal 
+        * code generation macros.
+        */
+       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 *dest;
+               MonoInst *iargs [3];
+               gboolean restart;
+
+               if (!bb->has_array_access)
+                       continue;
+
+               if (cfg->verbose_level > 3) mono_print_bb (bb, "BEFORE DECOMPOSE-ARRAY-ACCESS-OPTS ");
+
+               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_LDLEN:
+                                       NEW_LOAD_MEMBASE_FAULT (cfg, dest, OP_LOADI4_MEMBASE, ins->dreg, ins->sreg1,
+                                                                                       G_STRUCT_OFFSET (MonoArray, max_length));
+                                       MONO_ADD_INS (cfg->cbb, dest);
+                                       break;
+                               case OP_BOUNDS_CHECK:
+                                       MONO_EMIT_NULL_CHECK (cfg, ins->sreg1);
+                                       MONO_ARCH_EMIT_BOUNDS_CHECK (cfg, ins->sreg1, ins->inst_imm, ins->sreg2);
+                                       break;
+                               case OP_NEWARR:
+                                       if (cfg->opt & MONO_OPT_SHARED) {
+                                               EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
+                                               EMIT_NEW_CLASSCONST (cfg, iargs [1], ins->inst_newa_class);
+                                               MONO_INST_NEW (cfg, iargs [2], OP_MOVE);
+                                               iargs [2]->dreg = ins->sreg1;
+
+                                               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);
+
+                                               g_assert (vtable); /*This shall not fail since we check for this condition on OP_NEWARR creation*/
+                                               NEW_VTABLECONST (cfg, iargs [0], vtable);
+                                               MONO_ADD_INS (cfg->cbb, iargs [0]);
+                                               MONO_INST_NEW (cfg, iargs [1], OP_MOVE);
+                                               iargs [1]->dreg = ins->sreg1;
+
+                                               if (managed_alloc)
+                                                       dest = mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
+                                               else
+                                                       dest = mono_emit_jit_icall (cfg, mono_array_new_specific, iargs);
+                                               dest->dreg = ins->dreg;
+                                       }
+                                       break;
+                               case OP_STRLEN:
+                                       MONO_EMIT_NEW_LOAD_MEMBASE_OP_FAULT (cfg, OP_LOADI4_MEMBASE, ins->dreg,
+                                                                                                                ins->sreg1, G_STRUCT_OFFSET (MonoString, length));
+                                       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 > 3) mono_print_bb (bb, "AFTER DECOMPOSE-ARRAY-ACCESS-OPTS ");
+       }
+}
+
+typedef union {
+       guint32 vali [2];
+       gint64 vall;
+       double vald;
+} DVal;
+
+#ifdef MONO_ARCH_SOFT_FLOAT
+
+/**
+ * mono_decompose_soft_float:
+ *
+ *  Soft float support on ARM. We store each double value in a pair of integer vregs,
+ * similar to long support on 32 bit platforms. 32 bit float values require special
+ * handling when used as locals, arguments, and in calls.
+ * One big problem with soft-float is that there are few r4 test cases in our test suite.
+ */
+void
+mono_decompose_soft_float (MonoCompile *cfg)
+{
+       MonoBasicBlock *bb, *first_bb;
+
+       /*
+        * This pass creates long opcodes, so it should be run before decompose_long_opts ().
+        */
+
+       /**
+        * Create a dummy bblock and emit code into it so we can use the normal 
+        * code generation macros.
+        */
+       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;
+               gboolean restart;
+
+               if (cfg->verbose_level > 3) mono_print_bb (bb, "BEFORE HANDLE-SOFT-FLOAT ");
+
+               cfg->cbb->code = cfg->cbb->last_ins = NULL;
+               restart = TRUE;
+
+               while (restart) {
+                       restart = FALSE;
+
+                       for (ins = bb->code; ins; ins = ins->next) {
+                               const char *spec = INS_INFO (ins->opcode);
+
+                               /* Most fp operations are handled automatically by opcode emulation */
+
+                               switch (ins->opcode) {
+                               case OP_R8CONST: {
+                                       DVal d;
+                                       d.vald = *(double*)ins->inst_p0;
+                                       MONO_EMIT_NEW_I8CONST (cfg, ins->dreg, d.vall);
+                                       break;
+                               }
+                               case OP_R4CONST: {
+                                       DVal d;
+                                       /* We load the r8 value */
+                                       d.vald = *(float*)ins->inst_p0;
+                                       MONO_EMIT_NEW_I8CONST (cfg, ins->dreg, d.vall);
+                                       break;
+                               }
+                               case OP_FMOVE:
+                                       ins->opcode = OP_LMOVE;
+                                       break;
+                               case OP_FGETLOW32:
+                                       ins->opcode = OP_MOVE;
+                                       ins->sreg1 = ins->sreg1 + 1;
+                                       break;
+                               case OP_FGETHIGH32:
+                                       ins->opcode = OP_MOVE;
+                                       ins->sreg1 = ins->sreg1 + 2;
+                                       break;
+                               case OP_SETFRET: {
+                                       int reg = ins->sreg1;
+
+                                       ins->opcode = OP_SETLRET;
+                                       ins->dreg = -1;
+                                       ins->sreg1 = reg + 1;
+                                       ins->sreg2 = reg + 2;
+                                       break;
+                               }
+                               case OP_LOADR8_MEMBASE:
+                                       ins->opcode = OP_LOADI8_MEMBASE;
+                                       break;
+                               case OP_STORER8_MEMBASE_REG:
+                                       ins->opcode = OP_STOREI8_MEMBASE_REG;
+                                       break;
+                               case OP_STORER4_MEMBASE_REG: {
+                                       MonoInst *iargs [2];
+                                       int addr_reg;
+
+                                       /* Arg 1 is the double value */
+                                       MONO_INST_NEW (cfg, iargs [0], OP_ARG);
+                                       iargs [0]->dreg = ins->sreg1;
+
+                                       /* Arg 2 is the address to store to */
+                                       addr_reg = mono_alloc_preg (cfg);
+                                       EMIT_NEW_BIALU_IMM (cfg, iargs [1], OP_PADD_IMM, addr_reg, ins->inst_destbasereg, ins->inst_offset);
+                                       mono_emit_jit_icall (cfg, mono_fstore_r4, iargs);
+                                       restart = TRUE;
+                                       break;
+                               }
+                               case OP_LOADR4_MEMBASE: {
+                                       MonoInst *iargs [1];
+                                       MonoInst *conv;
+                                       int addr_reg;
+
+                                       addr_reg = mono_alloc_preg (cfg);
+                                       EMIT_NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, addr_reg, ins->inst_basereg, ins->inst_offset);
+                                       conv = mono_emit_jit_icall (cfg, mono_fload_r4, iargs);
+                                       conv->dreg = ins->dreg;
+                                       break;
+                               }                                       
+                               case OP_FCALL:
+                               case OP_FCALL_REG:
+                               case OP_FCALL_MEMBASE: {
+                                       MonoCallInst *call = (MonoCallInst*)ins;
+                                       if (call->signature->ret->type == MONO_TYPE_R4) {
+                                               MonoCallInst *call2;
+                                               MonoInst *iargs [1];
+                                               MonoInst *conv;
+
+                                               /* Convert the call into a call returning an int */
+                                               MONO_INST_NEW_CALL (cfg, call2, OP_CALL);
+                                               memcpy (call2, call, sizeof (MonoCallInst));
+                                               switch (ins->opcode) {
+                                               case OP_FCALL:
+                                                       call2->inst.opcode = OP_CALL;
+                                                       break;
+                                               case OP_FCALL_REG:
+                                                       call2->inst.opcode = OP_CALL_REG;
+                                                       break;
+                                               case OP_FCALL_MEMBASE:
+                                                       call2->inst.opcode = OP_CALL_MEMBASE;
+                                                       break;
+                                               default:
+                                                       g_assert_not_reached ();
+                                               }
+                                               call2->inst.dreg = mono_alloc_ireg (cfg);
+                                               MONO_ADD_INS (cfg->cbb, (MonoInst*)call2);
+
+                                               /* FIXME: Optimize this */
+
+                                               /* Emit an r4->r8 conversion */
+                                               EMIT_NEW_VARLOADA_VREG (cfg, iargs [0], call2->inst.dreg, &mono_defaults.int32_class->byval_arg);
+                                               conv = mono_emit_jit_icall (cfg, mono_fload_r4, iargs);
+                                               conv->dreg = ins->dreg;
+
+                                               /* The call sequence might include fp ins */
+                                               restart = TRUE;
+                                       } else {
+                                               switch (ins->opcode) {
+                                               case OP_FCALL:
+                                                       ins->opcode = OP_LCALL;
+                                                       break;
+                                               case OP_FCALL_REG:
+                                                       ins->opcode = OP_LCALL_REG;
+                                                       break;
+                                               case OP_FCALL_MEMBASE:
+                                                       ins->opcode = OP_LCALL_MEMBASE;
+                                                       break;
+                                               default:
+                                                       g_assert_not_reached ();
+                                               }
+                                       }
+                                       break;
+                               }
+                               case OP_FCOMPARE: {
+                                       MonoJitICallInfo *info;
+                                       MonoInst *iargs [2];
+                                       MonoInst *call, *cmp, *br;
+
+                                       /* Convert fcompare+fbcc to icall+icompare+beq */
+
+                                       info = mono_find_jit_opcode_emulation (ins->next->opcode);
+                                       g_assert (info);
+
+                                       /* Create dummy MonoInst's for the arguments */
+                                       MONO_INST_NEW (cfg, iargs [0], OP_ARG);
+                                       iargs [0]->dreg = ins->sreg1;
+                                       MONO_INST_NEW (cfg, iargs [1], OP_ARG);
+                                       iargs [1]->dreg = ins->sreg2;
+
+                                       call = mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, iargs);
+
+                                       MONO_INST_NEW (cfg, cmp, OP_ICOMPARE_IMM);
+                                       cmp->sreg1 = call->dreg;
+                                       cmp->inst_imm = 0;
+                                       MONO_ADD_INS (cfg->cbb, cmp);
+                                       
+                                       MONO_INST_NEW (cfg, br, OP_IBNE_UN);
+                                       br->inst_many_bb = mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * 2);
+                                       br->inst_true_bb = ins->next->inst_true_bb;
+                                       br->inst_false_bb = ins->next->inst_false_bb;
+                                       MONO_ADD_INS (cfg->cbb, br);
+
+                                       /* The call sequence might include fp ins */
+                                       restart = TRUE;
+
+                                       /* Skip fbcc or fccc */
+                                       NULLIFY_INS (ins->next);
+                                       break;
+                               }
+                               case OP_FCEQ:
+                               case OP_FCGT:
+                               case OP_FCGT_UN:
+                               case OP_FCLT:
+                               case OP_FCLT_UN: {
+                                       MonoJitICallInfo *info;
+                                       MonoInst *iargs [2];
+                                       MonoInst *call;
+
+                                       /* Convert fccc to icall+icompare+iceq */
+
+                                       info = mono_find_jit_opcode_emulation (ins->opcode);
+                                       g_assert (info);
+
+                                       /* Create dummy MonoInst's for the arguments */
+                                       MONO_INST_NEW (cfg, iargs [0], OP_ARG);
+                                       iargs [0]->dreg = ins->sreg1;
+                                       MONO_INST_NEW (cfg, iargs [1], OP_ARG);
+                                       iargs [1]->dreg = ins->sreg2;
+
+                                       call = mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, iargs);
+
+                                       MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ICOMPARE_IMM, -1, call->dreg, 1);
+                                       MONO_EMIT_NEW_UNALU (cfg, OP_ICEQ, ins->dreg, -1);
+
+                                       /* The call sequence might include fp ins */
+                                       restart = TRUE;
+                                       break;
+                               }
+                               case OP_CKFINITE: {
+                                       MonoInst *iargs [2];
+                                       MonoInst *call, *cmp;
+
+                                       /* Convert to icall+icompare+cond_exc+move */
+
+                                       /* Create dummy MonoInst's for the arguments */
+                                       MONO_INST_NEW (cfg, iargs [0], OP_ARG);
+                                       iargs [0]->dreg = ins->sreg1;
+
+                                       call = mono_emit_jit_icall (cfg, mono_isfinite, iargs);
+
+                                       MONO_INST_NEW (cfg, cmp, OP_ICOMPARE_IMM);
+                                       cmp->sreg1 = call->dreg;
+                                       cmp->inst_imm = 1;
+                                       MONO_ADD_INS (cfg->cbb, cmp);
+
+                                       MONO_EMIT_NEW_COND_EXC (cfg, INE_UN, "ArithmeticException");
+
+                                       /* Do the assignment if the value is finite */
+                                       MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, ins->dreg, ins->sreg1);
+
+                                       restart = TRUE;
+                                       break;
+                               }
+                               default:
+                                       if (spec [MONO_INST_SRC1] == 'f' || spec [MONO_INST_SRC2] == 'f' || spec [MONO_INST_DEST] == 'f') {
+                                               mono_print_ins (ins);
+                                               g_assert_not_reached ();
+                                       }
+                                       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 > 3) mono_print_bb (bb, "AFTER HANDLE-SOFT-FLOAT ");
+       }
+
+       mono_decompose_long_opts (cfg);
+}
+
+#endif
+
 #endif /* DISABLE_JIT */