Ternary IR ops in mini.
authorMark Probst <mark.probst@gmail.com>
Sat, 21 Mar 2009 08:04:35 +0000 (08:04 -0000)
committerMark Probst <mark.probst@gmail.com>
Sat, 21 Mar 2009 08:04:35 +0000 (08:04 -0000)
2009-03-21  Mark Probst  <mark.probst@gmail.com>

        * cfold.c, cprop.c, decompose.c, genmdesc.c, helpers.c, ir-emit.h,
        liveness.c, local-propagation.c, method-to-ir.c, mini-codegen.c,
        mini.c, mini.h, simd-intrinsics.c, ssa.c: Support for ternary IR
        operations.

svn path=/trunk/mono/; revision=129944

15 files changed:
mono/mini/ChangeLog
mono/mini/cfold.c
mono/mini/cprop.c
mono/mini/decompose.c
mono/mini/genmdesc.c
mono/mini/helpers.c
mono/mini/ir-emit.h
mono/mini/liveness.c
mono/mini/local-propagation.c
mono/mini/method-to-ir.c
mono/mini/mini-codegen.c
mono/mini/mini.c
mono/mini/mini.h
mono/mini/simd-intrinsics.c
mono/mini/ssa.c

index 27d4d1654e9bce62faca96850e69e9f6fa4f57c4..985eca38eb25a5a15f38b98232b94a2b54ef8ce8 100644 (file)
@@ -1,3 +1,10 @@
+2009-03-21  Mark Probst  <mark.probst@gmail.com>
+
+       * cfold.c, cprop.c, decompose.c, genmdesc.c, helpers.c, ir-emit.h,
+       liveness.c, local-propagation.c, method-to-ir.c, mini-codegen.c,
+       mini.c, mini.h, simd-intrinsics.c, ssa.c: Support for ternary IR
+       operations.
+
 2009-03-20  Zoltan Varga  <vargaz@gmail.com>
 
        * driver.c: Change location of gc_wrapper.h.
index c3d1652817e301bb0becfc43eb9ed667e5be23ac..0d69f26cbdb9e427a07d8a640cec02d0d2f49a12 100644 (file)
@@ -8,6 +8,7 @@
  * (C) 2003 Ximian, Inc.  http://www.ximian.com
  */
 #include "mini.h"
+#include "ir-emit.h"
 
 int
 mono_is_power_of_two (guint32 val)
@@ -58,15 +59,6 @@ mono_is_power_of_two (guint32 val)
            res = (cast)arg1->inst_c0 op (cast)arg2->inst_c0;   \
         break; \
 
-#undef MONO_INST_NEW
-#define MONO_INST_NEW(cfg,dest,op) do {        \
-               (dest) = mono_mempool_alloc ((cfg)->mempool, sizeof (MonoInst));        \
-        (dest)->inst_p0 = (dest)->inst_p1 = (dest)->next = NULL; \
-               (dest)->opcode = (op);  \
-        (dest)->flags = 0; \
-        (dest)->dreg = (dest)->sreg1 = (dest)->sreg2 = -1;  \
-       } while (0)
-
 #define ALLOC_DEST(cfg, dest, ins) do { \
     if (!(dest)) { \
         MONO_INST_NEW ((cfg), (dest), -1); \
@@ -107,7 +99,7 @@ mono_constant_fold_ins (MonoCompile *cfg, MonoInst *ins, MonoInst *arg1, MonoIns
                                        FOLD_BINOP (OP_IXOR, ^);
                                }
                                dest->opcode = OP_ICONST;
-                               dest->sreg1 = dest->sreg2 = -1;
+                               MONO_INST_NULLIFY_SREGS (dest);
                        }
                } else if (arg1->opcode == OP_ICONST) {
                        /* 
@@ -148,7 +140,7 @@ mono_constant_fold_ins (MonoCompile *cfg, MonoInst *ins, MonoInst *arg1, MonoIns
                                FOLD_BINOP2_IMM (OP_SHL_IMM, <<);
                        }
                        dest->opcode = OP_ICONST;
-                       dest->sreg1 = dest->sreg2 = -1;
+                       MONO_INST_NULLIFY_SREGS (dest);
                }
                break;
        case OP_ISUB:
@@ -164,7 +156,7 @@ mono_constant_fold_ins (MonoCompile *cfg, MonoInst *ins, MonoInst *arg1, MonoIns
                                FOLD_BINOPC (OP_ISHR_UN, >>, guint32);
                        }
                        dest->opcode = OP_ICONST;
-                       dest->sreg1 = dest->sreg2 = -1;
+                       MONO_INST_NULLIFY_SREGS (dest);
                }
                break;
        case OP_IDIV:
@@ -182,7 +174,7 @@ mono_constant_fold_ins (MonoCompile *cfg, MonoInst *ins, MonoInst *arg1, MonoIns
                                FOLD_BINOPC (OP_IREM_UN, %, guint32);
                        }
                        dest->opcode = OP_ICONST;
-                       dest->sreg1 = dest->sreg2 = -1;
+                       MONO_INST_NULLIFY_SREGS (dest);
                }
                break;
        case OP_IDIV_IMM:
@@ -202,7 +194,7 @@ mono_constant_fold_ins (MonoCompile *cfg, MonoInst *ins, MonoInst *arg1, MonoIns
                                g_assert_not_reached ();
                        }
                        dest->opcode = OP_ICONST;
-                       dest->sreg1 = dest->sreg2 = -1;
+                       MONO_INST_NULLIFY_SREGS (dest);
                }
                break;
                /* case OP_INEG: */
@@ -218,7 +210,7 @@ mono_constant_fold_ins (MonoCompile *cfg, MonoInst *ins, MonoInst *arg1, MonoIns
                                FOLD_UNOP (OP_INOT,~);
                        }
                        dest->opcode = OP_ICONST;
-                       dest->sreg1 = dest->sreg2 = -1;
+                       MONO_INST_NULLIFY_SREGS (dest);
                }
                break;
        case OP_MOVE:
@@ -229,7 +221,7 @@ mono_constant_fold_ins (MonoCompile *cfg, MonoInst *ins, MonoInst *arg1, MonoIns
 #endif
                        ALLOC_DEST (cfg, dest, ins);
                        dest->opcode = arg1->opcode;
-                       dest->sreg1 = dest->sreg2 = -1;
+                       MONO_INST_NULLIFY_SREGS (dest);
                        dest->inst_c0 = arg1->inst_c0;
                }
                break;
@@ -290,7 +282,7 @@ mono_constant_fold_ins (MonoCompile *cfg, MonoInst *ins, MonoInst *arg1, MonoIns
                                        NULLIFY_INS (ins);
                                        next->opcode = OP_ICONST;
                                        next->inst_c0 = res;
-                                       next->sreg1 = next->sreg2 = -1;
+                                       MONO_INST_NULLIFY_SREGS (next);
                                } else {
                                        ALLOC_DEST (cfg, dest, ins);
                                        dest->opcode = OP_ICONST;
index e1ad2d712d25169357298d579681ba04a7e82bd7..0cb236f042c3482c7fc9c73ea54d783200797b1f 100644 (file)
@@ -96,6 +96,9 @@ local_copy_prop (MonoCompile *cfg, MonoInst *code)
                        }
                }
 
+               if (mono_inst_get_src_registers (ins, NULL) > 2)
+                       NOT_IMPLEMENTED;
+
                /* invalidate pairs */
                if (spec [MONO_INST_DEST] == 'f') {
                        acp = remove_acp (acp, ins->dreg, 'f');
index 2d2ae39c28973363c8a857da8039bb7d0d7e3f0d..b03e779a524895883a84d21cd41965e377296b65 100644 (file)
@@ -358,16 +358,18 @@ 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);
index 7b28861029a0482b6c1e2ba707a1c530ca698150..6f1c2be73b7a5978935d540014965370135e2eed 100644 (file)
@@ -79,6 +79,9 @@ load_file (const char *name) {
                        } else if (strncmp (p, "src2:", 5) == 0) {
                                desc->spec [MONO_INST_SRC2] = p [5];
                                p += 6;
+                       } else if (strncmp (p, "src3:", 5) == 0) {
+                               desc->spec [MONO_INST_SRC3] = p [5];
+                               p += 6;
                        } else if (strncmp (p, "clob:", 5) == 0) {
                                desc->spec [MONO_INST_CLOB] = p [5];
                                p += 6;
index 679eb869a93e0b181057aa840fa85db54de4641d..1b17098ec5db040dc6fb631693b1ee8c3839a773 100644 (file)
 #ifdef MINI_OP
 #undef MINI_OP
 #endif
+#ifdef MINI_OP3
+#undef MINI_OP3
+#endif
 
 #ifdef HAVE_ARRAY_ELEM_INIT
 #define MSGSTRFIELD(line) MSGSTRFIELD1(line)
 #define MSGSTRFIELD1(line) str##line
 static const struct msgstr_t {
 #define MINI_OP(a,b,dest,src1,src2) char MSGSTRFIELD(__LINE__) [sizeof (b)];
+#define MINI_OP3(a,b,dest,src1,src2,src3) char MSGSTRFIELD(__LINE__) [sizeof (b)];
 #include "mini-ops.h"
 #undef MINI_OP
+#undef MINI_OP3
 } opstr = {
 #define MINI_OP(a,b,dest,src1,src2) b,
+#define MINI_OP3(a,b,dest,src1,src2,src3) b,
 #include "mini-ops.h"
 #undef MINI_OP
+#undef MINI_OP3
 };
 static const gint16 opidx [] = {
 #define MINI_OP(a,b,dest,src1,src2) [a - OP_LOAD] = offsetof (struct msgstr_t, MSGSTRFIELD(__LINE__)),
+#define MINI_OP3(a,b,dest,src1,src2,src3) [a - OP_LOAD] = offsetof (struct msgstr_t, MSGSTRFIELD(__LINE__)),
 #include "mini-ops.h"
 #undef MINI_OP
+#undef MINI_OP3
 };
 
 #else
 
 #define MINI_OP(a,b,dest,src1,src2) b,
+#define MINI_OP3(a,b,dest,src1,src2,src3) b,
 /* keep in sync with the enum in mini.h */
 static const char* const
 opnames[] = {
 #include "mini-ops.h"
 };
 #undef MINI_OP
+#undef MINI_OP3
 
 #endif
 
index 460d653bc0ff496cf4fa1f0be7ffe35aa610d246..b46d21e07fb56d7243c3a573cfbabba053212bb9 100644 (file)
@@ -86,7 +86,8 @@ alloc_dreg (MonoCompile *cfg, MonoStackType stack_type)
                (dest)->opcode = (op);  \
         (dest)->flags = 0; \
         (dest)->type = 0; \
-        (dest)->dreg = (dest)->sreg1 = (dest)->sreg2 = -1;  \
+        (dest)->dreg = -1;  \
+       MONO_INST_NULLIFY_SREGS ((dest));                   \
         (dest)->cil_code = (cfg)->ip;  \
        } while (0)
 
index ba25a0457ac4a8956738aa699282ca7db0edd29d..20e9aa47533a7af1fd6792229db3dea3e6c2a759 100644 (file)
@@ -78,7 +78,8 @@ visit_bb (MonoCompile *cfg, MonoBasicBlock *bb, GSList **visited)
 
        for (ins = bb->code; ins; ins = ins->next) {
                const char *spec = INS_INFO (ins->opcode);
-               int regtype, srcindex, sreg;
+               int regtype, srcindex, sreg, num_sregs;
+               int sregs [MONO_MAX_SRC_REGS];
 
                if (ins->opcode == OP_NOP)
                        continue;
@@ -96,12 +97,14 @@ visit_bb (MonoCompile *cfg, MonoBasicBlock *bb, GSList **visited)
                }
                        
                /* SREGS */
-               for (srcindex = 0; srcindex < 2; ++srcindex) {
+               num_sregs = mono_inst_get_src_registers (ins, sregs);
+               g_assert (num_sregs <= 2);
+               for (srcindex = 0; srcindex < num_sregs; ++srcindex) {
                        regtype = spec [(srcindex == 0) ? MONO_INST_SRC1 : MONO_INST_SRC2];
-                       sreg = srcindex == 0 ? ins->sreg1 : ins->sreg2;
-                       
-                       g_assert (((sreg == -1) && (regtype == ' ')) || ((sreg != -1) && (regtype != ' ')));
-                       if ((sreg != -1) && get_vreg_to_inst (cfg, sreg)) {
+                       sreg = sregs [srcindex];
+
+                       g_assert (sreg != -1);
+                       if (get_vreg_to_inst (cfg, sreg)) {
                                MonoInst *var = get_vreg_to_inst (cfg, sreg);
                                int idx = var->inst_c0;
                                MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
@@ -166,6 +169,8 @@ analyze_liveness_bb (MonoCompile *cfg, MonoBasicBlock *bb)
        
        for (inst_num = 0, ins = bb->code; ins; ins = ins->next, inst_num += 2) {
                const char *spec = INS_INFO (ins->opcode);
+               int num_sregs, i;
+               int sregs [MONO_MAX_SRC_REGS];
 
 #ifdef DEBUG_LIVENESS
                        printf ("\t"); mono_print_ins (ins);
@@ -189,37 +194,22 @@ analyze_liveness_bb (MonoCompile *cfg, MonoBasicBlock *bb)
                }                               
 
                /* SREGs must come first, so MOVE r <- r is handled correctly */
-
-               /* SREG1 */
-               sreg = ins->sreg1;
-               if ((spec [MONO_INST_SRC1] != ' ') && get_vreg_to_inst (cfg, sreg)) {
-                       MonoInst *var = get_vreg_to_inst (cfg, sreg);
-                       int idx = var->inst_c0;
-                       MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
-
-#ifdef DEBUG_LIVENESS
-                       printf ("\tGEN: R%d(%d)\n", sreg, idx);
-#endif
-                       update_live_range (&vars [idx], abs_pos + inst_num); 
-                       if (!mono_bitset_test_fast (bb->kill_set, idx))
-                               mono_bitset_set_fast (bb->gen_set, idx);
-                       vi->spill_costs += SPILL_COST_INCREMENT;
-               }
-
-               /* SREG2 */
-               sreg = ins->sreg2;
-               if ((spec [MONO_INST_SRC2] != ' ') && get_vreg_to_inst (cfg, sreg)) {
-                       MonoInst *var = get_vreg_to_inst (cfg, sreg);
-                       int idx = var->inst_c0;
-                       MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
+               num_sregs = mono_inst_get_src_registers (ins, sregs);
+               for (i = 0; i < num_sregs; ++i) {
+                       sreg = sregs [i];
+                       if ((spec [MONO_INST_SRC1 + i] != ' ') && get_vreg_to_inst (cfg, sreg)) {
+                               MonoInst *var = get_vreg_to_inst (cfg, sreg);
+                               int idx = var->inst_c0;
+                               MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
 
 #ifdef DEBUG_LIVENESS
-                       printf ("\tGEN: R%d(%d)\n", sreg, idx);
+                               printf ("\tGEN: R%d(%d)\n", sreg, idx);
 #endif
-                       update_live_range (&vars [idx], abs_pos + inst_num); 
-                       if (!mono_bitset_test_fast (bb->kill_set, idx))
-                               mono_bitset_set_fast (bb->gen_set, idx);
-                       vi->spill_costs += SPILL_COST_INCREMENT;
+                               update_live_range (&vars [idx], abs_pos + inst_num); 
+                               if (!mono_bitset_test_fast (bb->kill_set, idx))
+                                       mono_bitset_set_fast (bb->gen_set, idx);
+                               vi->spill_costs += SPILL_COST_INCREMENT;
+                       }
                }
 
                /* DREG */
@@ -512,12 +502,13 @@ optimize_initlocals (MonoCompile *cfg)
        mono_bitset_clear_all (used);
        initlocals_bb = cfg->bb_entry->next_bb;
        for (ins = initlocals_bb->code; ins; ins = ins->next) {
-               const char *spec = INS_INFO (ins->opcode);
+               int num_sregs, i;
+               int sregs [MONO_MAX_SRC_REGS];
+
+               num_sregs = mono_inst_get_src_registers (ins, sregs);
+               for (i = 0; i < num_sregs; ++i)
+                       mono_bitset_set_fast (used, sregs [i]);
 
-               if (spec [MONO_INST_SRC1] != ' ')
-                       mono_bitset_set_fast (used, ins->sreg1);
-               if (spec [MONO_INST_SRC2] != ' ')
-                       mono_bitset_set_fast (used, ins->sreg2);
                if (MONO_IS_STORE_MEMBASE (ins))
                        mono_bitset_set_fast (used, ins->dreg);
        }
@@ -699,6 +690,8 @@ update_liveness2 (MonoCompile *cfg, MonoInst *ins, gboolean set_volatile, int in
 {
        const char *spec = INS_INFO (ins->opcode);
        int sreg;
+       int num_sregs, i;
+       int sregs [MONO_MAX_SRC_REGS];
 
        LIVENESS_DEBUG (printf ("\t%x: ", inst_num); mono_print_ins (ins));
 
@@ -727,7 +720,8 @@ update_liveness2 (MonoCompile *cfg, MonoInst *ins, gboolean set_volatile, int in
                                if ((var != cfg->ret) && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)) && ((ins->opcode == OP_ICONST) || (ins->opcode == OP_I8CONST) || (ins->opcode == OP_R8CONST)) && !(var->flags & MONO_INST_VOLATILE)) {
                                        LIVENESS_DEBUG (printf ("\tdead def of R%d, eliminated\n", ins->dreg));
                                        ins->opcode = OP_NOP;
-                                       ins->dreg = ins->sreg1 = ins->sreg2 = -1;
+                                       ins->dreg = -1;
+                                       MONO_INST_NULLIFY_SREGS (ins);
                                        return;
                                }
 
@@ -737,27 +731,18 @@ update_liveness2 (MonoCompile *cfg, MonoInst *ins, gboolean set_volatile, int in
                }
        }
 
-       /* SREG1 */
-       sreg = ins->sreg1;
-       if ((spec [MONO_INST_SRC1] != ' ') && get_vreg_to_inst (cfg, sreg)) {
-               MonoInst *var = get_vreg_to_inst (cfg, sreg);
-               int idx = var->inst_c0;
-
-               if (last_use [idx] == 0) {
-                       LIVENESS_DEBUG (printf ("\tlast use of R%d set to %x\n", sreg, inst_num));
-                       last_use [idx] = inst_num;
-               }
-       }
-
-       /* SREG2 */
-       sreg = ins->sreg2;
-       if ((spec [MONO_INST_SRC2] != ' ') && get_vreg_to_inst (cfg, sreg)) {
-               MonoInst *var = get_vreg_to_inst (cfg, sreg);
-               int idx = var->inst_c0;
+       /* SREGs */
+       num_sregs = mono_inst_get_src_registers (ins, sregs);
+       for (i = 0; i < num_sregs; ++i) {
+               sreg = sregs [i];
+               if ((spec [MONO_INST_SRC1 + i] != ' ') && get_vreg_to_inst (cfg, sreg)) {
+                       MonoInst *var = get_vreg_to_inst (cfg, sreg);
+                       int idx = var->inst_c0;
 
-               if (last_use [idx] == 0) {
-                       LIVENESS_DEBUG (printf ("\tlast use of R%d set to %x\n", sreg, inst_num));
-                       last_use [idx] = inst_num;
+                       if (last_use [idx] == 0) {
+                               LIVENESS_DEBUG (printf ("\tlast use of R%d set to %x\n", sreg, inst_num));
+                               last_use [idx] = inst_num;
+                       }
                }
        }
 }
index 626df58ff8965cc645462ac9424a1cd204a6be07..31ed7dcd427b7adeb91285b640895c0bfa9c67c7 100644 (file)
@@ -64,23 +64,25 @@ restart:
 
                /* Manually init the defs entries used by the bblock */
                MONO_BB_FOR_EACH_INS (bb, ins) {
+                       int sregs [MONO_MAX_SRC_REGS];
+                       int num_sregs, i;
+
                        if ((ins->dreg != -1) && (ins->dreg < max)) {
                                defs [ins->dreg] = NULL;
 #if SIZEOF_REGISTER == 4
                                defs [ins->dreg + 1] = NULL;
 #endif
                        }
-                       if ((ins->sreg1 != -1) && (ins->sreg1 < max)) {
-                               defs [ins->sreg1] = NULL;
-#if SIZEOF_REGISTER == 4
-                               defs [ins->sreg1 + 1] = NULL;
-#endif
-                       }
-                       if ((ins->sreg2 != -1) && (ins->sreg2 < max)) {
-                               defs [ins->sreg2] = NULL;
+
+                       num_sregs = mono_inst_get_src_registers (ins, sregs);
+                       for (i = 0; i < num_sregs; ++i) {
+                               int sreg = sregs [i];
+                               if (sreg < max) {
+                                       defs [sreg] = NULL;
 #if SIZEOF_REGISTER == 4
-                               defs [ins->sreg2 + 1] = NULL;
+                                       defs [sreg + 1] = NULL;
 #endif
+                               }
                        }
                }
 
@@ -89,6 +91,8 @@ restart:
                MONO_BB_FOR_EACH_INS (bb, ins) {
                        const char *spec = INS_INFO (ins->opcode);
                        int regtype, srcindex, sreg;
+                       int num_sregs;
+                       int sregs [MONO_MAX_SRC_REGS];
 
                        if (ins->opcode == OP_NOP) {
                                MONO_DELETE_INS (bb, ins);
@@ -123,11 +127,14 @@ restart:
                                }
                        }
 
-                       for (srcindex = 0; srcindex < 2; ++srcindex) {
+                       num_sregs = mono_inst_get_src_registers (ins, sregs);
+                       for (srcindex = 0; srcindex < num_sregs; ++srcindex) {
                                MonoInst *def;
 
-                               regtype = srcindex == 0 ? spec [MONO_INST_SRC1] : spec [MONO_INST_SRC2];
-                               sreg = srcindex == 0 ? ins->sreg1 : ins->sreg2;
+                               mono_inst_get_src_registers (ins, sregs);
+
+                               regtype = spec [MONO_INST_SRC1 + srcindex];
+                               sreg = sregs [srcindex];
 
                                if ((regtype == ' ') || (sreg == -1) || (!defs [sreg]))
                                        continue;
@@ -163,10 +170,8 @@ restart:
                                        int vreg = def->sreg1;
 
                                        //printf ("CCOPY: R%d -> R%d\n", sreg, vreg);
-                                       if (srcindex == 0)
-                                               ins->sreg1 = vreg;
-                                       else
-                                               ins->sreg2 = vreg;
+                                       sregs [srcindex] = vreg;
+                                       mono_inst_set_src_registers (ins, sregs);
 
                                        /* Allow further iterations */
                                        srcindex = -1;
@@ -209,10 +214,8 @@ restart:
                                                } else {
                                                        ins->inst_imm = def->inst_c0;
                                                }
-                                               if (srcindex == 0)
-                                                       ins->sreg1 = -1;
-                                               else
-                                                       ins->sreg2 = -1;
+                                               sregs [srcindex] = -1;
+                                               mono_inst_set_src_registers (ins, sregs);
 
                                                if ((opcode2 == OP_VOIDCALL) || (opcode2 == OP_CALL) || (opcode2 == OP_LCALL) || (opcode2 == OP_FCALL))
                                                        ((MonoCallInst*)ins)->fptr = (gpointer)ins->inst_imm;
@@ -453,6 +456,8 @@ mono_local_deadce (MonoCompile *cfg)
                /* Manually init the defs entries used by the bblock */
                MONO_BB_FOR_EACH_INS (bb, ins) {
                        const char *spec = INS_INFO (ins->opcode);
+                       int sregs [MONO_MAX_SRC_REGS];
+                       int num_sregs, i;
 
                        if (spec [MONO_INST_DEST] != ' ') {
                                mono_bitset_clear_fast (used, ins->dreg);
@@ -463,16 +468,11 @@ mono_local_deadce (MonoCompile *cfg)
                                mono_bitset_clear_fast (defined, ins->dreg + 1);
 #endif
                        }
-                       if (spec [MONO_INST_SRC1] != ' ') {
-                               mono_bitset_clear_fast (used, ins->sreg1);
-#if SIZEOF_REGISTER == 4
-                               mono_bitset_clear_fast (used, ins->sreg1 + 1);
-#endif
-                       }
-                       if (spec [MONO_INST_SRC2] != ' ') {
-                               mono_bitset_clear_fast (used, ins->sreg2);
+                       num_sregs = mono_inst_get_src_registers (ins, sregs);
+                       for (i = 0; i < num_sregs; ++i) {
+                               mono_bitset_clear_fast (used, sregs [i]);
 #if SIZEOF_REGISTER == 4
-                               mono_bitset_clear_fast (used, ins->sreg2 + 1);
+                               mono_bitset_clear_fast (used, sregs [i] + 1);
 #endif
                        }
                }
@@ -482,6 +482,8 @@ mono_local_deadce (MonoCompile *cfg)
                 */
                MONO_BB_FOR_EACH_INS_REVERSE_SAFE (bb, prev, ins) {
                        const char *spec = INS_INFO (ins->opcode);
+                       int sregs [MONO_MAX_SRC_REGS];
+                       int num_sregs, i;
 
                        if (ins->opcode == OP_NOP) {
                                MONO_DELETE_INS (bb, ins);
@@ -546,10 +548,9 @@ mono_local_deadce (MonoCompile *cfg)
 
                        if (spec [MONO_INST_DEST] != ' ')
                                mono_bitset_set_fast (defined, ins->dreg);
-                       if (spec [MONO_INST_SRC1] != ' ')
-                               mono_bitset_set_fast (used, ins->sreg1);
-                       if (spec [MONO_INST_SRC2] != ' ')
-                               mono_bitset_set_fast (used, ins->sreg2);
+                       num_sregs = mono_inst_get_src_registers (ins, sregs);
+                       for (i = 0; i < num_sregs; ++i)
+                               mono_bitset_set_fast (used, sregs [i]);
                        if (MONO_IS_STORE_MEMBASE (ins))
                                mono_bitset_set_fast (used, ins->dreg);
 
index b661d677fac45d5e16903d35d272a4a152f9ba8c..ab903bb667a050ed31347d0797a1dea9791c2dc7 100644 (file)
@@ -121,7 +121,11 @@ extern MonoMethodSignature *helper_sig_monitor_enter_exit_trampoline;
 #ifdef MINI_OP
 #undef MINI_OP
 #endif
-#define MINI_OP(a,b,dest,src1,src2) dest, src1, src2,
+#ifdef MINI_OP3
+#undef MINI_OP3
+#endif
+#define MINI_OP(a,b,dest,src1,src2) dest, src1, src2, ' ',
+#define MINI_OP3(a,b,dest,src1,src2,src3) dest, src1, src2, src3,
 #define NONE ' '
 #define IREG 'i'
 #define FREG 'f'
@@ -138,6 +142,15 @@ ins_info[] = {
 #include "mini-ops.h"
 };
 #undef MINI_OP
+#undef MINI_OP3
+
+#define MINI_OP(a,b,dest,src1,src2) (((src1) != NONE) + ((src2) != NONE)),
+#define MINI_OP3(a,b,dest,src1,src2,src3) (((src1) != NONE) + ((src2) != NONE) + ((src3) != NONE))
+const gint8 ins_sreg_counts[] = {
+#include "mini-ops.h"
+};
+#undef MINI_OP
+#undef MINI_OP3
 
 extern GHashTable *jit_icall_name_hash;
 
@@ -147,6 +160,43 @@ extern GHashTable *jit_icall_name_hash;
        (vi)->idx = (id); \
 } while (0)
 
+void
+mini_init_op_sreg_counts (void)
+{
+       int i;
+
+       g_assert (sizeof (ins_sreg_counts) == sizeof (ins_info) / 4);
+
+       for (i = 0; i < sizeof (ins_sreg_counts); ++i) {
+               int opcode = i + OP_START + 1;
+               const char *spec = INS_INFO (opcode);
+               int count;
+
+               if (spec [MONO_INST_SRC1] == ' ') {
+                       g_assert (spec [MONO_INST_SRC2] == ' ');
+                       g_assert (spec [MONO_INST_SRC3] == ' ');
+                       count = 0;
+               } else if (spec [MONO_INST_SRC2] == ' ') {
+                       g_assert (spec [MONO_INST_SRC3] == ' ');
+                       count = 1;
+               } else if (spec [MONO_INST_SRC3] == ' ') {
+                       count = 2;
+               } else {
+                       count = 3;
+               }
+
+               g_assert (ins_sreg_counts [i] == count);
+       }
+}
+
+void
+mono_inst_set_src_registers (MonoInst *ins, int *regs)
+{
+       ins->sreg1 = regs [0];
+       ins->sreg2 = regs [1];
+       ins->sreg3 = regs [2];
+}
+
 guint32
 mono_alloc_ireg (MonoCompile *cfg)
 {
@@ -10389,8 +10439,9 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
                cfg->cbb = bb;
                MONO_BB_FOR_EACH_INS (bb, ins) {
                        const char *spec = INS_INFO (ins->opcode);
-                       int regtype, srcindex, sreg, tmp_reg, prev_dreg;
+                       int regtype, srcindex, sreg, tmp_reg, prev_dreg, num_sregs;
                        gboolean store, no_lvreg;
+                       int sregs [MONO_MAX_SRC_REGS];
 
                        if (G_UNLIKELY (cfg->verbose_level > 2))
                                mono_print_ins (ins);
@@ -10611,6 +10662,8 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
                                        g_assert (load_opcode != OP_LOADV_MEMBASE);
 
                                        if (vreg_to_lvreg [sreg]) {
+                                               g_assert (vreg_to_lvreg [sreg] != -1);
+
                                                /* The variable is already loaded to an lvreg */
                                                if (G_UNLIKELY (cfg->verbose_level > 2))
                                                        printf ("\t\tUse lvreg R%d for R%d.\n", vreg_to_lvreg [sreg], sreg);
@@ -10648,6 +10701,7 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
                                                                         */
                                                                        sreg = ins->dreg;
                                                                }
+                                                               g_assert (sreg != -1);
                                                                vreg_to_lvreg [var->dreg] = sreg;
                                                                g_assert (lvregs_len < 1024);
                                                                lvregs [lvregs_len ++] = var->dreg;
@@ -10684,6 +10738,7 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
                        }
 
                        if (dest_has_lvreg) {
+                               g_assert (ins->dreg != -1);
                                vreg_to_lvreg [prev_dreg] = ins->dreg;
                                g_assert (lvregs_len < 1024);
                                lvregs [lvregs_len ++] = prev_dreg;
@@ -10701,6 +10756,9 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
                                for (i = 0; i < lvregs_len; i++)
                                        vreg_to_lvreg [lvregs [i]] = 0;
                                lvregs_len = 0;
+                       } else if (ins->opcode == OP_NOP) {
+                               ins->dreg = -1;
+                               MONO_INST_NULLIFY_SREGS (ins);
                        }
 
                        if (cfg->verbose_level > 2)
index 03904815ea121759e2441e50707dfe48c1082633..8cfe829e95c21c02e1670f34702f62fdddb805f8 100644 (file)
@@ -304,8 +304,9 @@ mono_spillvar_offset (MonoCompile *cfg, int spillvar, int bank)
 
 #define reg_is_fp(desc) (MONO_ARCH_INST_IS_FLOAT (desc))
 #define dreg_is_fp(spec)  (MONO_ARCH_INST_IS_FLOAT (spec [MONO_INST_DEST]))
-#define sreg1_is_fp(spec) (MONO_ARCH_INST_IS_FLOAT (spec [MONO_INST_SRC1]))
-#define sreg2_is_fp(spec) (MONO_ARCH_INST_IS_FLOAT (spec [MONO_INST_SRC2]))
+#define sreg_is_fp(n,spec) (MONO_ARCH_INST_IS_FLOAT (spec [MONO_INST_SRC1+(n)]))
+#define sreg1_is_fp(spec) sreg_is_fp (0,(spec))
+#define sreg2_is_fp(spec) sreg_is_fp (1,(spec))
 
 #define reg_is_simd(desc) ((desc) == 'x') 
 
@@ -319,12 +320,14 @@ mono_spillvar_offset (MonoCompile *cfg, int spillvar, int bank)
 
 #endif
 
-#define sreg1_bank(spec) reg_bank ((spec)[MONO_INST_SRC1])
-#define sreg2_bank(spec) reg_bank ((spec)[MONO_INST_SRC2])
+#define sreg_bank(n,spec) reg_bank ((spec)[MONO_INST_SRC1+(n)])
+#define sreg1_bank(spec) sreg_bank (0, (spec))
+#define sreg2_bank(spec) sreg_bank (1, (spec))
 #define dreg_bank(spec) reg_bank ((spec)[MONO_INST_DEST])
 
-#define sreg1_bank_ins(ins) sreg1_bank (ins_get_spec ((ins)->opcode))
-#define sreg2_bank_ins(ins) sreg2_bank (ins_get_spec ((ins)->opcode))
+#define sreg_bank_ins(n,ins) sreg_bank ((n), ins_get_spec ((ins)->opcode))
+#define sreg1_bank_ins(ins) sreg_bank_ins (0, (ins))
+#define sreg2_bank_ins(ins) sreg_bank_ins (1, (ins))
 #define dreg_bank_ins(ins) dreg_bank (ins_get_spec ((ins)->opcode))
 
 #define regpair_reg2_mask(desc,hreg1) ((MONO_ARCH_INST_REGPAIR_REG2 (desc,hreg1) != -1) ? (regmask (MONO_ARCH_INST_REGPAIR_REG2 (desc,hreg1))) : MONO_ARCH_CALLEE_REGS)
@@ -348,6 +351,8 @@ void
 mono_print_ins_index (int i, MonoInst *ins)
 {
        const char *spec = ins_get_spec (ins->opcode);
+       int num_sregs, j;
+       int sregs [MONO_MAX_SRC_REGS];
 
        if (i != -1)
                printf ("\t%-2d %s", i, mono_inst_name (ins->opcode));
@@ -361,6 +366,8 @@ mono_print_ins_index (int i, MonoInst *ins)
                        printf (" R%d", ins->sreg1);
                if (ins->sreg2 != -1)
                        printf (" R%d", ins->sreg2);
+               if (ins->sreg3 != -1)
+                       printf (" R%d", ins->sreg3);
 
                switch (ins->opcode) {
                case OP_LBNE_UN:
@@ -440,12 +447,13 @@ mono_print_ins_index (int i, MonoInst *ins)
                else
                        printf (" %s", mono_regname_full (ins->sreg1, bank));
        }
-       if (spec [MONO_INST_SRC2]) {
-               int bank = sreg2_bank (spec);
-               if (is_soft_reg (ins->sreg2, bank))
-                       printf (" R%d", ins->sreg2);
+       num_sregs = mono_inst_get_src_registers (ins, sregs);
+       for (j = 1; j < num_sregs; ++j) {
+               int bank = sreg_bank (j, spec);
+               if (is_soft_reg (sregs [j], bank))
+                       printf (" R%d", sregs [j]);
                else
-                       printf (" %s", mono_regname_full (ins->sreg2, bank));
+                       printf (" %s", mono_regname_full (sregs [j], bank));
        }
 
        switch (ins->opcode) {
@@ -684,7 +692,8 @@ static int
 get_register_spilling (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst **last, MonoInst *ins, regmask_t regmask, int reg, int bank)
 {
        MonoInst *load;
-       int i, sel, spill;
+       int i, sel, spill, num_sregs;
+       int sregs [MONO_MAX_SRC_REGS];
        int *symbolic;
        MonoRegState *rs = cfg->rs;
 
@@ -692,21 +701,17 @@ get_register_spilling (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst **last, Mo
 
        g_assert (bank < MONO_NUM_REGBANKS);
 
-       DEBUG (printf ("\tstart regmask to assign R%d: 0x%08" G_GUINT64_FORMAT " (R%d <- R%d R%d)\n", reg, (guint64)regmask, ins->dreg, ins->sreg1, ins->sreg2));
+       DEBUG (printf ("\tstart regmask to assign R%d: 0x%08" G_GUINT64_FORMAT " (R%d <- R%d R%d R%d)\n", reg, (guint64)regmask, ins->dreg, ins->sreg1, ins->sreg2, ins->sreg3));
        /* exclude the registers in the current instruction */
-       if ((sreg1_bank_ins (ins) == bank) && (reg != ins->sreg1) && (reg_is_freeable (ins->sreg1, bank) || (is_soft_reg (ins->sreg1, bank) && rs->vassign [ins->sreg1] >= 0))) {
-               if (is_soft_reg (ins->sreg1, bank))
-                       regmask &= ~ (regmask (rs->vassign [ins->sreg1]));
-               else
-                       regmask &= ~ (regmask (ins->sreg1));
-               DEBUG (printf ("\t\texcluding sreg1 %s\n", mono_regname_full (ins->sreg1, bank)));
-       }
-       if ((sreg2_bank_ins (ins) == bank) && (reg != ins->sreg2) && (reg_is_freeable (ins->sreg2, bank) || (is_soft_reg (ins->sreg2, bank) && rs->vassign [ins->sreg2] >= 0))) {
-               if (is_soft_reg (ins->sreg2, bank))
-                       regmask &= ~ (regmask (rs->vassign [ins->sreg2]));
-               else
-                       regmask &= ~ (regmask (ins->sreg2));
-               DEBUG (printf ("\t\texcluding sreg2 %s %d\n", mono_regname_full (ins->sreg2, bank), ins->sreg2));
+       num_sregs = mono_inst_get_src_registers (ins, sregs);
+       for (i = 0; i < num_sregs; ++i) {
+               if ((sreg_bank_ins (i, ins) == bank) && (reg != sregs [i]) && (reg_is_freeable (sregs [i], bank) || (is_soft_reg (sregs [i], bank) && rs->vassign [sregs [i]] >= 0))) {
+                       if (is_soft_reg (sregs [i], bank))
+                               regmask &= ~ (regmask (rs->vassign [sregs [i]]));
+                       else
+                               regmask &= ~ (regmask (sregs [i]));
+                       DEBUG (printf ("\t\texcluding sreg%d %s %d\n", i + 1, mono_regname_full (sregs [i], bank), sregs [i]));
+               }
        }
        if ((dreg_bank_ins (ins) == bank) && (reg != ins->dreg) && reg_is_freeable (ins->dreg, bank)) {
                regmask &= ~ (regmask (ins->dreg));
@@ -914,16 +919,18 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
        MonoInst *ins, *prev, *last;
        MonoInst **tmp;
        MonoRegState *rs = cfg->rs;
-       int i, val, max;
+       int i, j, val, max;
        RegTrack *reginfo;
        const char *spec;
-       unsigned char spec_src1, spec_src2, spec_dest;
+       unsigned char spec_src1, spec_dest;
        int bank = 0;
 #if MONO_ARCH_USE_FPSTACK
        gboolean has_fp = FALSE;
        int fpstack [8];
        int sp = 0;
 #endif
+       int num_sregs;
+       int sregs [MONO_MAX_SRC_REGS];
 
        if (!bb->code)
                return;
@@ -981,24 +988,21 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                        }
 #endif
                }
-               if ((ins->sreg1 != -1) && (ins->sreg1 < max)) {
-                       memset (&reginfo [ins->sreg1], 0, sizeof (RegTrack));
+
+               num_sregs = mono_inst_get_src_registers (ins, sregs);
+               for (j = 0; j < num_sregs; ++j) {
+                       g_assert (sregs [j] != -1);
+                       if (sregs [j] < max) {
+                               memset (&reginfo [sregs [j]], 0, sizeof (RegTrack));
 #if SIZEOF_REGISTER == 4
-                       if (MONO_ARCH_INST_IS_REGPAIR (spec [MONO_INST_SRC1])) {
-                               ins->sreg1 ++;
-                               memset (&reginfo [ins->sreg1 + 1], 0, sizeof (RegTrack));
-                       }
+                               if (MONO_ARCH_INST_IS_REGPAIR (spec [MONO_INST_SRC1 + j])) {
+                                       sregs [j]++;
+                                       memset (&reginfo [sregs [j] + 1], 0, sizeof (RegTrack));
+                               }
 #endif
-               }
-               if ((ins->sreg2 != -1) && (ins->sreg2 < max)) {
-                       memset (&reginfo [ins->sreg2], 0, sizeof (RegTrack));
-#if SIZEOF_REGISTER == 4
-                       if (MONO_ARCH_INST_IS_REGPAIR (spec [MONO_INST_SRC2])) {
-                               ins->sreg2 ++;
-                               memset (&reginfo [ins->sreg2 + 1], 0, sizeof (RegTrack));
                        }
-#endif
                }
+               mono_inst_set_src_registers (ins, sregs);
        }
 
        /*if (cfg->opt & MONO_OPT_COPYPROP)
@@ -1009,8 +1013,6 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
        /* forward pass on the instructions to collect register liveness info */
        MONO_BB_FOR_EACH_INS (bb, ins) {
                spec = ins_get_spec (ins->opcode);
-               spec_src1 = spec [MONO_INST_SRC1];
-               spec_src2 = spec [MONO_INST_SRC2];
                spec_dest = spec [MONO_INST_DEST];
 
                if (G_UNLIKELY (spec == MONO_ARCH_CPU_SPEC)) {
@@ -1019,51 +1021,45 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                
                DEBUG (mono_print_ins_index (i, ins));
 
+               num_sregs = mono_inst_get_src_registers (ins, sregs);
+
 #if MONO_ARCH_USE_FPSTACK
-               if (sreg1_is_fp (spec) || sreg2_is_fp (spec) || dreg_is_fp (spec))
+               if (dreg_is_fp (spec)) {
                        has_fp = TRUE;
-#endif
-
-               if (spec_src1) {
-                       bank = sreg1_bank (spec);
-                       g_assert (ins->sreg1 != -1);
-                       if (is_soft_reg (ins->sreg1, bank))
-                               /* This means the vreg is not local to this bb */
-                               g_assert (reginfo [ins->sreg1].born_in > 0);
-                       rs->vassign [ins->sreg1] = -1;
-                       //reginfo [ins->sreg1].prev_use = reginfo [ins->sreg1].last_use;
-                       //reginfo [ins->sreg1].last_use = i;
-                       if (MONO_ARCH_INST_IS_REGPAIR (spec_src2)) {
-                               /* The virtual register is allocated sequentially */
-                               rs->vassign [ins->sreg1 + 1] = -1;
-                               //reginfo [ins->sreg1 + 1].prev_use = reginfo [ins->sreg1 + 1].last_use;
-                               //reginfo [ins->sreg1 + 1].last_use = i;
-                               if (reginfo [ins->sreg1 + 1].born_in == 0 || reginfo [ins->sreg1 + 1].born_in > i)
-                                       reginfo [ins->sreg1 + 1].born_in = i;
-                       }
                } else {
-                       ins->sreg1 = -1;
+                       for (j = 0; j < num_sregs; ++j) {
+                               if (sreg_is_fp (j, spec))
+                                       has_fp = TRUE;
+                       }
                }
-               if (spec_src2) {
-                       bank = sreg2_bank (spec);
-                       g_assert (ins->sreg2 != -1);
-                       if (is_soft_reg (ins->sreg2, bank))
-                               /* This means the vreg is not local to this bb */
-                               g_assert (reginfo [ins->sreg2].born_in > 0);
-                       rs->vassign [ins->sreg2] = -1;
-                       //reginfo [ins->sreg2].prev_use = reginfo [ins->sreg2].last_use;
-                       //reginfo [ins->sreg2].last_use = i;
-                       if (MONO_ARCH_INST_IS_REGPAIR (spec_src2)) {
-                               /* The virtual register is allocated sequentially */
-                               rs->vassign [ins->sreg2 + 1] = -1;
-                               //reginfo [ins->sreg2 + 1].prev_use = reginfo [ins->sreg2 + 1].last_use;
-                               //reginfo [ins->sreg2 + 1].last_use = i;
-                               if (reginfo [ins->sreg2 + 1].born_in == 0 || reginfo [ins->sreg2 + 1].born_in > i)
-                                       reginfo [ins->sreg2 + 1].born_in = i;
+#endif
+
+               for (j = 0; j < num_sregs; ++j) {
+                       int sreg = sregs [j];
+                       int sreg_spec = spec [MONO_INST_SRC1 + j];
+                       if (sreg_spec) {
+                               bank = sreg_bank (j, spec);
+                               g_assert (sreg != -1);
+                               if (is_soft_reg (sreg, bank))
+                                       /* This means the vreg is not local to this bb */
+                                       g_assert (reginfo [sreg].born_in > 0);
+                               rs->vassign [sreg] = -1;
+                               //reginfo [ins->sreg2].prev_use = reginfo [ins->sreg2].last_use;
+                               //reginfo [ins->sreg2].last_use = i;
+                               if (MONO_ARCH_INST_IS_REGPAIR (sreg_spec)) {
+                                       /* The virtual register is allocated sequentially */
+                                       rs->vassign [sreg + 1] = -1;
+                                       //reginfo [ins->sreg2 + 1].prev_use = reginfo [ins->sreg2 + 1].last_use;
+                                       //reginfo [ins->sreg2 + 1].last_use = i;
+                                       if (reginfo [sreg + 1].born_in == 0 || reginfo [sreg + 1].born_in > i)
+                                               reginfo [sreg + 1].born_in = i;
+                               }
+                       } else {
+                               sregs [j] = -1;
                        }
-               } else {
-                       ins->sreg2 = -1;
                }
+               mono_inst_set_src_registers (ins, sregs);
+
                if (spec_dest) {
                        int dest_dreg;
 
@@ -1144,30 +1140,35 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
 
        DEBUG (print_regtrack (reginfo, rs->next_vreg));
        MONO_BB_FOR_EACH_INS_REVERSE_SAFE (bb, prev, ins) {
-               int prev_dreg, prev_sreg1, prev_sreg2, clob_dreg;
-               int dest_dreg, dest_sreg1, dest_sreg2, clob_reg;
+               int prev_dreg, clob_dreg;
+               int dest_dreg, clob_reg;
+               int dest_sregs [MONO_MAX_SRC_REGS], prev_sregs [MONO_MAX_SRC_REGS];
                int dreg_high, sreg1_high;
-               regmask_t dreg_mask, sreg1_mask, sreg2_mask, mask;
-               regmask_t dreg_fixed_mask, sreg1_fixed_mask, sreg2_fixed_mask;
+               regmask_t dreg_mask, mask;
+               regmask_t sreg_masks [MONO_MAX_SRC_REGS], sreg_fixed_masks [MONO_MAX_SRC_REGS];
+               regmask_t dreg_fixed_mask;
                const unsigned char *ip;
                --i;
                spec = ins_get_spec (ins->opcode);
                spec_src1 = spec [MONO_INST_SRC1];
-               spec_src2 = spec [MONO_INST_SRC2];
                spec_dest = spec [MONO_INST_DEST];
                prev_dreg = -1;
-               prev_sreg2 = -1;
                clob_dreg = -1;
                clob_reg = -1;
                dest_dreg = -1;
-               dest_sreg1 = -1;
-               dest_sreg2 = -1;
-               prev_sreg1 = -1;
                dreg_high = -1;
                sreg1_high = -1;
                dreg_mask = get_callee_mask (spec_dest);
-               sreg1_mask = get_callee_mask (spec_src1);
-               sreg2_mask = get_callee_mask (spec_src2);
+               for (j = 0; j < MONO_MAX_SRC_REGS; ++j) {
+                       prev_sregs [j] = -1;
+                       sreg_masks [j] = get_callee_mask (spec [MONO_INST_SRC1 + j]);
+                       dest_sregs [j] = desc_to_fixed_reg [spec [MONO_INST_SRC1 + j]];
+#ifdef MONO_ARCH_INST_FIXED_MASK
+                       sreg_fixed_masks [j] = MONO_ARCH_INST_FIXED_MASK (spec [MONO_INST_SRC1 + j]);
+#else
+                       sreg_fixed_masks [j] = 0;
+#endif
+               }
 
                DEBUG (printf ("processing:"));
                DEBUG (mono_print_ins_index (i, ins));
@@ -1179,121 +1180,128 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                /*
                 * FIXED REGS
                 */
-               dest_sreg1 = desc_to_fixed_reg [spec_src1];
-               dest_sreg2 = desc_to_fixed_reg [spec_src2];
                dest_dreg = desc_to_fixed_reg [spec_dest];
                clob_reg = desc_to_fixed_reg [(int)spec [MONO_INST_CLOB]];
-               sreg2_mask &= ~ (MONO_ARCH_INST_SREG2_MASK (spec));
+               sreg_masks [1] &= ~ (MONO_ARCH_INST_SREG2_MASK (spec));
 
 #ifdef MONO_ARCH_INST_FIXED_MASK
-               sreg1_fixed_mask = MONO_ARCH_INST_FIXED_MASK (spec_src1);
-               sreg2_fixed_mask = MONO_ARCH_INST_FIXED_MASK (spec_src2);
                dreg_fixed_mask = MONO_ARCH_INST_FIXED_MASK (spec_dest);
 #else
-               sreg1_fixed_mask = sreg2_fixed_mask = dreg_fixed_mask = 0;
+               dreg_fixed_mask = 0;
 #endif
 
+               num_sregs = mono_inst_get_src_registers (ins, sregs);
+
                /*
-                * TRACK FIXED SREG2
+                * TRACK FIXED SREG2, 3, ...
                 */
-               if (dest_sreg2 != -1) {
-                       if (rs->ifree_mask & (regmask (dest_sreg2))) {
-                               if (is_global_ireg (ins->sreg2)) {
-                                       /* Argument already in hard reg, need to copy */
-                                       MonoInst *copy = create_copy_ins (cfg, bb, tmp, dest_sreg2, ins->sreg2, NULL, ip, 0);
-                                       insert_before_ins (bb, ins, copy);
-                               }
-                               else {
-                                       val = rs->vassign [ins->sreg2];
-                                       if (val == -1) {
-                                               DEBUG (printf ("\tshortcut assignment of R%d to %s\n", ins->sreg2, mono_arch_regname (dest_sreg2)));
-                                               assign_reg (cfg, rs, ins->sreg2, dest_sreg2, 0);
-                                       } else if (val < -1) {
-                                               /* FIXME: */
-                                               g_assert_not_reached ();
-                                       } else {
+               for (j = 1; j < num_sregs; ++j) {
+                       int sreg = sregs [j];
+                       int dest_sreg = dest_sregs [j];
+                       if (dest_sreg != -1) {
+                               if (rs->ifree_mask & (regmask (dest_sreg))) {
+                                       if (is_global_ireg (sreg)) {
                                                /* Argument already in hard reg, need to copy */
-                                               MonoInst *copy = create_copy_ins (cfg, bb, tmp, dest_sreg2, val, NULL, ip, 0);
+                                               MonoInst *copy = create_copy_ins (cfg, bb, tmp, dest_sreg, sreg, NULL, ip, 0);
                                                insert_before_ins (bb, ins, copy);
                                        }
-                               }
-                       } else {
-                               gboolean need_spill = TRUE;
-                               gboolean need_assign = TRUE;
-
-                               dreg_mask &= ~ (regmask (dest_sreg2));
-                               sreg1_mask &= ~ (regmask (dest_sreg2));
+                                       else {
+                                               val = rs->vassign [sreg];
+                                               if (val == -1) {
+                                                       DEBUG (printf ("\tshortcut assignment of R%d to %s\n", sreg, mono_arch_regname (dest_sreg)));
+                                                       assign_reg (cfg, rs, sreg, dest_sreg, 0);
+                                               } else if (val < -1) {
+                                                       /* FIXME: */
+                                                       g_assert_not_reached ();
+                                               } else {
+                                                       /* Argument already in hard reg, need to copy */
+                                                       MonoInst *copy = create_copy_ins (cfg, bb, tmp, dest_sreg, val, NULL, ip, 0);
+                                                       insert_before_ins (bb, ins, copy);
+                                               }
+                                       }
+                               } else {
+                                       gboolean need_spill = TRUE;
+                                       gboolean need_assign = TRUE;
+                                       int k;
+
+                                       dreg_mask &= ~ (regmask (dest_sreg));
+                                       for (k = 0; k < num_sregs; ++k) {
+                                               if (k != j)
+                                                       sreg_masks [k] &= ~ (regmask (dest_sreg));
+                                       }
 
-                               /* 
-                                * First check if dreg is assigned to dest_sreg2, since we
-                                * can't spill a dreg.
-                                */
-                               val = rs->vassign [ins->dreg];
-                               if (val == dest_sreg2 && ins->dreg != ins->sreg2) {
                                        /* 
-                                        * the destination register is already assigned to 
-                                        * dest_sreg2: we need to allocate another register for it 
-                                        * and then copy from this to dest_sreg2.
+                                        * First check if dreg is assigned to dest_sreg2, since we
+                                        * can't spill a dreg.
                                         */
-                                       int new_dest;
-                                       new_dest = alloc_int_reg (cfg, bb, tmp, ins, dreg_mask, ins->dreg, &reginfo [ins->dreg]);
-                                       g_assert (new_dest >= 0);
-                                       DEBUG (printf ("\tchanging dreg R%d to %s from %s\n", ins->dreg, mono_arch_regname (new_dest), mono_arch_regname (dest_sreg2)));
-
-                                       prev_dreg = ins->dreg;
-                                       assign_reg (cfg, rs, ins->dreg, new_dest, 0);
-                                       clob_dreg = ins->dreg;
-                                       create_copy_ins (cfg, bb, tmp, dest_sreg2, new_dest, ins, ip, 0);
-                                       mono_regstate_free_int (rs, dest_sreg2);
-                                       need_spill = FALSE;
-                               }
-
-                               if (is_global_ireg (ins->sreg2)) {
-                                       MonoInst *copy = create_copy_ins (cfg, bb, tmp, dest_sreg2, ins->sreg2, NULL, ip, 0);
-                                       insert_before_ins (bb, ins, copy);
-                                       need_assign = FALSE;
-                               }
-                               else {
-                                       val = rs->vassign [ins->sreg2];
-                                       if (val == dest_sreg2) {
-                                               /* sreg2 is already assigned to the correct register */
-                                               need_spill = FALSE;
-                                       } else if (val < -1) {
-                                               /* sreg2 is spilled, it can be assigned to dest_sreg2 */
-                                       } else if (val >= 0) {
-                                               /* sreg2 already assigned to another register */
-                                               /*
-                                                * We couldn't emit a copy from val to dest_sreg2, because
-                                                * val might be spilled later while processing this 
-                                                * instruction. So we spill sreg2 so it can be allocated to
-                                                * dest_sreg2.
+                                       val = rs->vassign [ins->dreg];
+                                       if (val == dest_sreg && ins->dreg != sreg) {
+                                               /* 
+                                                * the destination register is already assigned to 
+                                                * dest_sreg2: we need to allocate another register for it 
+                                                * and then copy from this to dest_sreg2.
                                                 */
-                                               DEBUG (printf ("\tforced spill of R%d\n", ins->sreg2));
-                                               free_up_reg (cfg, bb, tmp, ins, val, 0);
+                                               int new_dest;
+                                               new_dest = alloc_int_reg (cfg, bb, tmp, ins, dreg_mask, ins->dreg, &reginfo [ins->dreg]);
+                                               g_assert (new_dest >= 0);
+                                               DEBUG (printf ("\tchanging dreg R%d to %s from %s\n", ins->dreg, mono_arch_regname (new_dest), mono_arch_regname (dest_sreg)));
+
+                                               prev_dreg = ins->dreg;
+                                               assign_reg (cfg, rs, ins->dreg, new_dest, 0);
+                                               clob_dreg = ins->dreg;
+                                               create_copy_ins (cfg, bb, tmp, dest_sreg, new_dest, ins, ip, 0);
+                                               mono_regstate_free_int (rs, dest_sreg);
+                                               need_spill = FALSE;
                                        }
-                               }
 
-                               if (need_spill) {
-                                       DEBUG (printf ("\tforced spill of R%d\n", rs->isymbolic [dest_sreg2]));
-                                       free_up_reg (cfg, bb, tmp, ins, dest_sreg2, 0);
-                               }
+                                       if (is_global_ireg (sreg)) {
+                                               MonoInst *copy = create_copy_ins (cfg, bb, tmp, dest_sreg, sreg, NULL, ip, 0);
+                                               insert_before_ins (bb, ins, copy);
+                                               need_assign = FALSE;
+                                       }
+                                       else {
+                                               val = rs->vassign [sreg];
+                                               if (val == dest_sreg) {
+                                                       /* sreg2 is already assigned to the correct register */
+                                                       need_spill = FALSE;
+                                               } else if (val < -1) {
+                                                       /* sreg2 is spilled, it can be assigned to dest_sreg2 */
+                                               } else if (val >= 0) {
+                                                       /* sreg2 already assigned to another register */
+                                                       /*
+                                                        * We couldn't emit a copy from val to dest_sreg2, because
+                                                        * val might be spilled later while processing this 
+                                                        * instruction. So we spill sreg2 so it can be allocated to
+                                                        * dest_sreg2.
+                                                        */
+                                                       DEBUG (printf ("\tforced spill of R%d\n", sreg));
+                                                       free_up_reg (cfg, bb, tmp, ins, val, 0);
+                                               }
+                                       }
 
-                               if (need_assign) {
-                                       if (rs->vassign [ins->sreg2] < -1) {
-                                               MonoInst *store;
-                                               int spill;
+                                       if (need_spill) {
+                                               DEBUG (printf ("\tforced spill of R%d\n", rs->isymbolic [dest_sreg]));
+                                               free_up_reg (cfg, bb, tmp, ins, dest_sreg, 0);
+                                       }
 
-                                               /* Need to emit a spill store */
-                                               spill = - rs->vassign [ins->sreg2] - 1;
-                                               store = create_spilled_store (cfg, bb, spill, dest_sreg2, ins->sreg2, tmp, NULL, bank);                                         
-                                               insert_before_ins (bb, ins, store);
+                                       if (need_assign) {
+                                               if (rs->vassign [sreg] < -1) {
+                                                       MonoInst *store;
+                                                       int spill;
+
+                                                       /* Need to emit a spill store */
+                                                       spill = - rs->vassign [sreg] - 1;
+                                                       store = create_spilled_store (cfg, bb, spill, dest_sreg, sreg, tmp, NULL, bank);
+                                                       insert_before_ins (bb, ins, store);
+                                               }
+                                               /* force-set sreg2 */
+                                               assign_reg (cfg, rs, sregs [j], dest_sreg, 0);
                                        }
-                                       /* force-set sreg2 */
-                                       assign_reg (cfg, rs, ins->sreg2, dest_sreg2, 0);
                                }
+                               sregs [j] = dest_sreg;
                        }
-                       ins->sreg2 = dest_sreg2;
                }
+               mono_inst_set_src_registers (ins, sregs);
 
                /*
                 * TRACK DREG
@@ -1308,10 +1316,12 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                         * The dest reg is read by the instruction, not written, so
                         * avoid allocating sreg1/sreg2 to the same reg.
                         */
-                       if (dest_sreg1 != -1)
-                               dreg_mask &= ~ (regmask (dest_sreg1));
-                       if (dest_sreg2 != -1)
-                               dreg_mask &= ~ (regmask (dest_sreg2));
+                       if (dest_sregs [0] != -1)
+                               dreg_mask &= ~ (regmask (dest_sregs [0]));
+                       for (j = 1; j < num_sregs; ++j) {
+                               if (dest_sregs [j] != -1)
+                                       dreg_mask &= ~ (regmask (dest_sregs [j]));
+                       }
 
                        val = rs->vassign [ins->dreg];
                        if (is_soft_reg (ins->dreg, bank) && (val >= 0) && (!(regmask (val) & dreg_mask))) {
@@ -1467,10 +1477,9 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                         * The dest reg is read by the instruction, not written, so
                         * avoid allocating sreg1/sreg2 to the same reg.
                         */
-                       if (!sreg1_bank (spec))
-                               sreg1_mask &= ~ (regmask (ins->dreg));
-                       if (!sreg2_bank (spec))
-                               sreg2_mask &= ~ (regmask (ins->dreg));
+                       for (j = 0; j < num_sregs; ++j)
+                               if (!sreg_bank (j, spec))
+                                       sreg_masks [j] &= ~ (regmask (ins->dreg));
                }
 
                /*
@@ -1568,7 +1577,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
 
                                        assign_reg (cfg, rs, reg, hreg, 0);
 
-                                       sreg1_mask &= ~(regmask (hreg));
+                                       sreg_masks [0] &= ~(regmask (hreg));
 
                                        DEBUG (printf ("\tassigned arg reg %s to R%d\n", mono_arch_regname (hreg), reg));
 
@@ -1600,7 +1609,10 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                 */
                bank = sreg1_bank (spec);
                if (MONO_ARCH_INST_IS_REGPAIR (spec_dest) && (spec [MONO_INST_CLOB] == '1')) {
-                       g_assert (is_soft_reg (ins->sreg1, bank));
+                       int sreg1 = sregs [0];
+                       int dest_sreg1 = dest_sregs [0];
+
+                       g_assert (is_soft_reg (sreg1, bank));
 
                        /* To simplify things, we allocate the same regpair to sreg1 and dreg */
                        if (dest_sreg1 != -1)
@@ -1608,68 +1620,68 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                        val = mono_regstate_alloc_int (rs, regmask (ins->dreg));
                        g_assert (val >= 0);
 
-                       if (rs->vassign [ins->sreg1] >= 0 && rs->vassign [ins->sreg1] != val)
+                       if (rs->vassign [sreg1] >= 0 && rs->vassign [sreg1] != val)
                                // FIXME:
                                g_assert_not_reached ();
 
-                       assign_reg (cfg, rs, ins->sreg1, val, bank);
+                       assign_reg (cfg, rs, sreg1, val, bank);
 
-                       DEBUG (printf ("\tassigned sreg1-low %s to R%d\n", mono_regname_full (val, bank), ins->sreg1));
+                       DEBUG (printf ("\tassigned sreg1-low %s to R%d\n", mono_regname_full (val, bank), sreg1));
 
                        g_assert ((regmask (dreg_high)) & regpair_reg2_mask (spec_src1, ins->dreg));
                        val = mono_regstate_alloc_int (rs, regmask (dreg_high));
                        g_assert (val >= 0);
 
-                       if (rs->vassign [ins->sreg1 + 1] >= 0 && rs->vassign [ins->sreg1 + 1] != val)
+                       if (rs->vassign [sreg1 + 1] >= 0 && rs->vassign [sreg1 + 1] != val)
                                // FIXME:
                                g_assert_not_reached ();
 
-                       assign_reg (cfg, rs, ins->sreg1 + 1, val, bank);
+                       assign_reg (cfg, rs, sreg1 + 1, val, bank);
 
-                       DEBUG (printf ("\tassigned sreg1-high %s to R%d\n", mono_regname_full (val, bank), ins->sreg1 + 1));
+                       DEBUG (printf ("\tassigned sreg1-high %s to R%d\n", mono_regname_full (val, bank), sreg1 + 1));
 
                        /* Skip rest of this section */
-                       dest_sreg1 = -1;
+                       dest_sregs [0] = -1;
                }
 
-               if (sreg1_fixed_mask) {
+               if (sreg_fixed_masks [0]) {
                        g_assert (!bank);
-                       if (is_global_ireg (ins->sreg1)) {
+                       if (is_global_ireg (sregs [0])) {
                                /* 
                                 * The argument is already in a hard reg, but that reg is
                                 * not usable by this instruction, so allocate a new one.
                                 */
-                               val = mono_regstate_alloc_int (rs, sreg1_fixed_mask);
+                               val = mono_regstate_alloc_int (rs, sreg_fixed_masks [0]);
                                if (val < 0)
-                                       val = get_register_spilling (cfg, bb, tmp, ins, sreg1_fixed_mask, -1, bank);
+                                       val = get_register_spilling (cfg, bb, tmp, ins, sreg_fixed_masks [0], -1, bank);
                                mono_regstate_free_int (rs, val);
-                               dest_sreg1 = val;
+                               dest_sregs [0] = val;
 
                                /* Fall through to the dest_sreg1 != -1 case */
                        }
                        else
-                               sreg1_mask &= sreg1_fixed_mask;
+                               sreg_masks [0] &= sreg_fixed_masks [0];
                }
 
-               if (dest_sreg1 != -1) {
-                       sreg1_mask = regmask (dest_sreg1);
+               if (dest_sregs [0] != -1) {
+                       sreg_masks [0] = regmask (dest_sregs [0]);
 
-                       if ((rs->vassign [ins->sreg1] != dest_sreg1) && !(rs->ifree_mask & (regmask (dest_sreg1)))) {
-                               DEBUG (printf ("\tforced spill of R%d\n", rs->isymbolic [dest_sreg1]));
-                               get_register_force_spilling (cfg, bb, tmp, ins, rs->isymbolic [dest_sreg1], 0);
-                               mono_regstate_free_int (rs, dest_sreg1);
+                       if ((rs->vassign [sregs [0]] != dest_sregs [0]) && !(rs->ifree_mask & (regmask (dest_sregs [0])))) {
+                               DEBUG (printf ("\tforced spill of R%d\n", rs->isymbolic [dest_sregs [0]]));
+                               get_register_force_spilling (cfg, bb, tmp, ins, rs->isymbolic [dest_sregs [0]], 0);
+                               mono_regstate_free_int (rs, dest_sregs [0]);
                        }
-                       if (is_global_ireg (ins->sreg1)) {
+                       if (is_global_ireg (sregs [0])) {
                                /* The argument is already in a hard reg, need to copy */
-                               MonoInst *copy = create_copy_ins (cfg, bb, tmp, dest_sreg1, ins->sreg1, NULL, ip, 0);
+                               MonoInst *copy = create_copy_ins (cfg, bb, tmp, dest_sregs [0], sregs [0], NULL, ip, 0);
                                insert_before_ins (bb, ins, copy);
-                               ins->sreg1 = dest_sreg1;
+                               sregs [0] = dest_sregs [0];
                        }
                }
 
-               if (is_soft_reg (ins->sreg1, bank)) {
-                       val = rs->vassign [ins->sreg1];
-                       prev_sreg1 = ins->sreg1;
+               if (is_soft_reg (sregs [0], bank)) {
+                       val = rs->vassign [sregs [0]];
+                       prev_sregs [0] = sregs [0];
                        if (val < 0) {
                                int spill = 0;
                                if (val < -1) {
@@ -1682,19 +1694,19 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                         * Allocate the same hreg to sreg1 as well so the 
                                         * peephole can get rid of the move.
                                         */
-                                       sreg1_mask = regmask (ins->dreg);
+                                       sreg_masks [0] = regmask (ins->dreg);
                                }
 
                                if (spec [MONO_INST_CLOB] == '1' && !dreg_bank (spec) && (rs->ifree_mask & (regmask (ins->dreg))))
                                        /* Allocate the same reg to sreg1 to avoid a copy later */
-                                       sreg1_mask = regmask (ins->dreg);
+                                       sreg_masks [0] = regmask (ins->dreg);
 
-                               val = alloc_reg (cfg, bb, tmp, ins, sreg1_mask, ins->sreg1, &reginfo [ins->sreg1], bank);
-                               assign_reg (cfg, rs, ins->sreg1, val, bank);
-                               DEBUG (printf ("\tassigned sreg1 %s to R%d\n", mono_regname_full (val, bank), ins->sreg1));
+                               val = alloc_reg (cfg, bb, tmp, ins, sreg_masks [0], sregs [0], &reginfo [sregs [0]], bank);
+                               assign_reg (cfg, rs, sregs [0], val, bank);
+                               DEBUG (printf ("\tassigned sreg1 %s to R%d\n", mono_regname_full (val, bank), sregs [0]));
 
                                if (spill) {
-                                       MonoInst *store = create_spilled_store (cfg, bb, spill, val, prev_sreg1, tmp, NULL, bank);
+                                       MonoInst *store = create_spilled_store (cfg, bb, spill, val, prev_sregs [0], tmp, NULL, bank);
                                        /*
                                         * Need to insert before the instruction since it can
                                         * overwrite sreg1.
@@ -1702,28 +1714,32 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                        insert_before_ins (bb, ins, store);
                                }
                        }
-                       else if ((dest_sreg1 != -1) && (dest_sreg1 != val)) {
-                               MonoInst *copy = create_copy_ins (cfg, bb, tmp, dest_sreg1, val, NULL, ip, bank);
+                       else if ((dest_sregs [0] != -1) && (dest_sregs [0] != val)) {
+                               MonoInst *copy = create_copy_ins (cfg, bb, tmp, dest_sregs [0], val, NULL, ip, bank);
                                insert_before_ins (bb, ins, copy);
-                               sreg2_mask &= ~(regmask (dest_sreg1));
-                               val = dest_sreg1;
+                               for (j = 1; j < num_sregs; ++j)
+                                       sreg_masks [j] &= ~(regmask (dest_sregs [0]));
+                               val = dest_sregs [0];
                        }
                                
-                       ins->sreg1 = val;
+                       sregs [0] = val;
                }
                else {
-                       prev_sreg1 = -1;
+                       prev_sregs [0] = -1;
                }
-               sreg2_mask &= ~(regmask (ins->sreg1));
+               mono_inst_set_src_registers (ins, sregs);
+
+               for (j = 1; j < num_sregs; ++j)
+                       sreg_masks [j] &= ~(regmask (sregs [0]));
 
                /* Handle the case when sreg1 is a regpair but dreg is not */
                if (MONO_ARCH_INST_IS_REGPAIR (spec_src1) && (spec [MONO_INST_CLOB] != '1')) {
-                       int reg2 = prev_sreg1 + 1;
+                       int reg2 = prev_sregs [0] + 1;
 
                        g_assert (!bank);
-                       g_assert (prev_sreg1 > -1);
-                       g_assert (!is_global_ireg (rs->vassign [prev_sreg1]));
-                       mask = regpair_reg2_mask (spec_src1, rs->vassign [prev_sreg1]);
+                       g_assert (prev_sregs [0] > -1);
+                       g_assert (!is_global_ireg (rs->vassign [prev_sregs [0]]));
+                       mask = regpair_reg2_mask (spec_src1, rs->vassign [prev_sregs [0]]);
                        val = rs->vassign [reg2];
                        if (val < 0) {
                                int spill = 0;
@@ -1761,21 +1777,21 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                }
 
                /* Handle dreg==sreg1 */
-               if (((dreg_is_fp (spec) && sreg1_is_fp (spec)) || spec [MONO_INST_CLOB] == '1') && ins->dreg != ins->sreg1) {
+               if (((dreg_is_fp (spec) && sreg1_is_fp (spec)) || spec [MONO_INST_CLOB] == '1') && ins->dreg != sregs [0]) {
                        MonoInst *sreg2_copy = NULL;
                        MonoInst *copy;
                        int bank = reg_bank (spec_src1);
 
-                       if (ins->dreg == ins->sreg2) {
+                       if (ins->dreg == sregs [1]) {
                                /* 
                                 * copying sreg1 to dreg could clobber sreg2, so allocate a new
                                 * register for it.
                                 */
-                               int reg2 = alloc_reg (cfg, bb, tmp, ins, dreg_mask, ins->sreg2, NULL, bank);
+                               int reg2 = alloc_reg (cfg, bb, tmp, ins, dreg_mask, sregs [1], NULL, bank);
 
-                               DEBUG (printf ("\tneed to copy sreg2 %s to reg %s\n", mono_regname_full (ins->sreg2, bank), mono_regname_full (reg2, bank)));
-                               sreg2_copy = create_copy_ins (cfg, bb, tmp, reg2, ins->sreg2, NULL, ip, bank);
-                               prev_sreg2 = ins->sreg2 = reg2;
+                               DEBUG (printf ("\tneed to copy sreg2 %s to reg %s\n", mono_regname_full (sregs [1], bank), mono_regname_full (reg2, bank)));
+                               sreg2_copy = create_copy_ins (cfg, bb, tmp, reg2, sregs [1], NULL, ip, bank);
+                               prev_sregs [1] = sregs [1] = reg2;
 
                                if (G_UNLIKELY (bank))
                                        mono_regstate_free_general (rs, reg2, bank);
@@ -1785,7 +1801,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
 
                        if (MONO_ARCH_INST_IS_REGPAIR (spec_src1)) {
                                /* Copying sreg1_high to dreg could also clobber sreg2 */
-                               if (rs->vassign [prev_sreg1 + 1] == ins->sreg2)
+                               if (rs->vassign [prev_sregs [0] + 1] == sregs [1])
                                        /* FIXME: */
                                        g_assert_not_reached ();
 
@@ -1793,12 +1809,12 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                 * sreg1 and dest are already allocated to the same regpair by the
                                 * SREG1 allocation code.
                                 */
-                               g_assert (ins->sreg1 == ins->dreg);
+                               g_assert (sregs [0] == ins->dreg);
                                g_assert (dreg_high == sreg1_high);
                        }
 
-                       DEBUG (printf ("\tneed to copy sreg1 %s to dreg %s\n", mono_regname_full (ins->sreg1, bank), mono_regname_full (ins->dreg, bank)));
-                       copy = create_copy_ins (cfg, bb, tmp, ins->dreg, ins->sreg1, NULL, ip, bank);
+                       DEBUG (printf ("\tneed to copy sreg1 %s to dreg %s\n", mono_regname_full (sregs [0], bank), mono_regname_full (ins->dreg, bank)));
+                       copy = create_copy_ins (cfg, bb, tmp, ins->dreg, sregs [0], NULL, ip, bank);
                        insert_before_ins (bb, ins, copy);
 
                        if (sreg2_copy)
@@ -1808,44 +1824,52 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                         * Need to prevent sreg2 to be allocated to sreg1, since that
                         * would screw up the previous copy.
                         */
-                       sreg2_mask &= ~ (regmask (ins->sreg1));
+                       sreg_masks [1] &= ~ (regmask (sregs [0]));
                        /* we set sreg1 to dest as well */
-                       prev_sreg1 = ins->sreg1 = ins->dreg;
-                       sreg2_mask &= ~ (regmask (ins->dreg));
+                       prev_sregs [0] = sregs [0] = ins->dreg;
+                       sreg_masks [1] &= ~ (regmask (ins->dreg));
                }
+               mono_inst_set_src_registers (ins, sregs);
 
                /*
-                * TRACK SREG2
+                * TRACK SREG2, 3, ...
                 */
-               bank = sreg2_bank (spec);
-               if (MONO_ARCH_INST_IS_REGPAIR (spec_src2))
-                       g_assert_not_reached ();
-               if (is_soft_reg (ins->sreg2, bank)) {
-                       val = rs->vassign [ins->sreg2];
+               for (j = 1; j < num_sregs; ++j) {
+                       int k;
 
-                       if (val < 0) {
-                               int spill = 0;
-                               if (val < -1) {
-                                       /* the register gets spilled after this inst */
-                                       spill = -val -1;
-                               }
-                               val = alloc_reg (cfg, bb, tmp, ins, sreg2_mask, ins->sreg2, &reginfo [ins->sreg2], bank);
-                               assign_reg (cfg, rs, ins->sreg2, val, bank);
-                               DEBUG (printf ("\tassigned sreg2 %s to R%d\n", mono_regname_full (val, bank), ins->sreg2));
-                               if (spill) {
-                                       MonoInst *store = create_spilled_store (cfg, bb, spill, val, prev_sreg2, tmp, NULL, bank);
-                                       /*
-                                        * Need to insert before the instruction since it can
-                                        * overwrite sreg2.
-                                        */
-                                       insert_before_ins (bb, ins, store);
+                       bank = sreg_bank (j, spec);
+                       if (MONO_ARCH_INST_IS_REGPAIR (spec [MONO_INST_SRC1 + j]))
+                               g_assert_not_reached ();
+                       if (is_soft_reg (sregs [j], bank)) {
+                               val = rs->vassign [sregs [j]];
+
+                               if (val < 0) {
+                                       int spill = 0;
+                                       if (val < -1) {
+                                               /* the register gets spilled after this inst */
+                                               spill = -val -1;
+                                       }
+                                       val = alloc_reg (cfg, bb, tmp, ins, sreg_masks [j], sregs [j], &reginfo [sregs [j]], bank);
+                                       assign_reg (cfg, rs, sregs [j], val, bank);
+                                       DEBUG (printf ("\tassigned sreg%d %s to R%d\n", j + 1, mono_regname_full (val, bank), sregs [j]));
+                                       if (spill) {
+                                               MonoInst *store = create_spilled_store (cfg, bb, spill, val, prev_sregs [j], tmp, NULL, bank);
+                                               /*
+                                                * Need to insert before the instruction since it can
+                                                * overwrite sreg2.
+                                                */
+                                               insert_before_ins (bb, ins, store);
+                                       }
                                }
+                               sregs [j] = val;
+                               for (k = j + 1; k < num_sregs; ++k)
+                                       sreg_masks [k] &= ~ (regmask (sregs [j]));
+                       }
+                       else {
+                               prev_sregs [j] = -1;
                        }
-                       ins->sreg2 = val;
-               }
-               else {
-                       prev_sreg2 = -1;
                }
+               mono_inst_set_src_registers (ins, sregs);
 
                /*if (reg_is_freeable (ins->sreg1) && prev_sreg1 >= 0 && reginfo [prev_sreg1].born_in >= i) {
                        DEBUG (printf ("freeable %s\n", mono_arch_regname (ins->sreg1)));
@@ -1871,6 +1895,8 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                MonoInst *fxch;
                int tmp;
 
+               g_assert (num_sregs <= 2);
+
                for (ins = bb->code; ins; ins = ins->next) {
                        spec = ins_get_spec (ins->opcode);
 
index 2979e514a9e6828bbb085a220f5b09c22380a3c8..11dd3497ab68d95f7847d52729487613ca508b6c 100644 (file)
@@ -4544,6 +4544,8 @@ mini_init (const char *filename, const char *runtime_version)
                global_codeman = mono_code_manager_new ();
        jit_icall_name_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
+       mini_init_op_sreg_counts ();
+
        mono_arch_cpu_init ();
 
        mono_arch_init ();
index bea0febc8977f0119c026813556da0fc5cc73e11..e6003b073eb060463cc9b412726e85f0d0119f6c 100644 (file)
@@ -149,17 +149,23 @@ enum {
 
 #define MONO_VARINFO(cfg,varnum) (&(cfg)->vars [varnum])
 
+#define MONO_INST_NULLIFY_SREGS(dest) do {                             \
+               (dest)->sreg1 = (dest)->sreg2 = (dest)->sreg3 = -1;     \
+       } while (0)
+
 #define MONO_INST_NEW(cfg,dest,op) do {        \
                (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
                (dest)->opcode = (op);  \
-        (dest)->dreg = (dest)->sreg1 = (dest)->sreg2 = -1;  \
+               (dest)->dreg = -1;                          \
+               MONO_INST_NULLIFY_SREGS ((dest));           \
         (dest)->cil_code = (cfg)->ip;  \
        } while (0)
 
 #define MONO_INST_NEW_CALL(cfg,dest,op) do {   \
                (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoCallInst));   \
                (dest)->inst.opcode = (op);     \
-        (dest)->inst.dreg = (dest)->inst.sreg1 = (dest)->inst.sreg2 = -1;  \
+               (dest)->inst.dreg = -1;                                 \
+               MONO_INST_NULLIFY_SREGS (&(dest)->inst);                \
         (dest)->inst.cil_code = (cfg)->ip;  \
        } while (0)
 
@@ -180,7 +186,8 @@ enum {
 
 #define NULLIFY_INS(ins) do { \
         (ins)->opcode = OP_NOP; \
-        (ins)->dreg = (ins)->sreg1 = (ins)->sreg2 = -1; \
+        (ins)->dreg = -1;                              \
+       MONO_INST_NULLIFY_SREGS ((ins));                \
                (ins)->ssa_op = MONO_SSA_NOP; \
     } while (0)
 
@@ -285,9 +292,13 @@ extern gboolean mono_do_x86_stack_align;
 extern const char *mono_build_date;
 extern gboolean mono_do_signal_chaining;
 
-#define INS_INFO(opcode) (&ins_info [((opcode) - OP_START - 1) * 3])
+#define INS_INFO(opcode) (&ins_info [((opcode) - OP_START - 1) * 4])
 
 extern const char ins_info[];
+extern const gint8 ins_sreg_counts [];
+
+#define mono_inst_get_num_src_registers(ins) (ins_sreg_counts [(ins)->opcode - OP_START - 1])
+#define mono_inst_get_src_registers(ins, regs) (((regs) [0] = (ins)->sreg1), ((regs) [1] = (ins)->sreg2), ((regs) [2] = (ins)->sreg3), mono_inst_get_num_src_registers ((ins)))
 
 #define MONO_BB_FOR_EACH_INS(bb, ins) for ((ins) = (bb)->code; (ins); (ins) = (ins)->next)
 
@@ -430,6 +441,8 @@ typedef struct MonoMemcpyArgs {
        int size, align;
 } MonoMemcpyArgs;
 
+#define MONO_MAX_SRC_REGS      3
+
 struct MonoInst {
        guint16 opcode;
        guint8  type; /* stack type */
@@ -437,7 +450,7 @@ struct MonoInst {
        guint8  flags  : 5;
        
        /* used by the register allocator */
-       gint32 dreg, sreg1, sreg2;
+       gint32 dreg, sreg1, sreg2, sreg3;
 
        MonoInst *next, *prev;
 
@@ -582,8 +595,9 @@ enum {
 /* instruction description for use in regalloc/scheduling */
 enum {
        MONO_INST_DEST,
-       MONO_INST_SRC1,
+       MONO_INST_SRC1,         /* we depend on the SRCs to be consecutive */
        MONO_INST_SRC2,
+       MONO_INST_SRC3,
        MONO_INST_LEN,
        MONO_INST_CLOB,
        /* Unused, commented out to reduce the size of the mdesc tables
@@ -1001,13 +1015,18 @@ enum {
 #ifdef MINI_OP
 #undef MINI_OP
 #endif
+#ifdef MINI_OP3
+#undef MINI_OP3
+#endif
 #define MINI_OP(a,b,dest,src1,src2) a,
+#define MINI_OP3(a,b,dest,src1,src2,src3) a,
 enum {
        OP_START = MONO_CEE_LAST - 1,
 #include "mini-ops.h"
        OP_LAST
 };
 #undef MINI_OP
+#undef MINI_OP3
 
 #if SIZEOF_VOID_P == 8
 #define OP_PCONST OP_I8CONST
@@ -1223,6 +1242,8 @@ void      mono_print_code                   (MonoCompile *cfg, const char *msg)
 void      mono_print_method_from_ip         (void *ip);
 char     *mono_pmip                         (void *ip);
 const char* mono_inst_name                  (int op);
+void     mini_init_op_sreg_counts          (void) MONO_INTERNAL;
+void      mono_inst_set_src_registers       (MonoInst *ins, int *regs) MONO_INTERNAL;
 int       mono_op_to_op_imm                 (int opcode) MONO_INTERNAL;
 int       mono_op_imm_to_op                 (int opcode) MONO_INTERNAL;
 int       mono_load_membase_to_load_mem     (int opcode) MONO_INTERNAL;
index 010d8dddd7bcab4ddd87f2420057f052850f2067..d7ddc17f17a128337679b6b55a096a47beb1fba3 100644 (file)
@@ -676,6 +676,9 @@ mono_simd_simplify_indirection (MonoCompile *cfg)
 
        /*Scan the first basic block looking xzeros not used*/
        for (ins = first_bb->code; ins; ins = ins->next) {
+               int num_sregs;
+               int sregs [MONO_MAX_SRC_REGS];
+
                if (ins->opcode == OP_XZERO) {
                        if (!(vreg_flags [ins->dreg] & VREG_HAS_OTHER_OP_BB0)) {
                                DEBUG (printf ("[simd-simplify] R%d has vzero: ", ins->dreg); mono_print_ins(ins));
@@ -685,13 +688,13 @@ mono_simd_simplify_indirection (MonoCompile *cfg)
                }
                if (ins->opcode == OP_LDADDR && apply_vreg_first_block_interference (cfg, ins, ((MonoInst*)ins->inst_p0)->dreg, max_vreg, vreg_flags))
                        continue;
-               
                if (apply_vreg_first_block_interference (cfg, ins, ins->dreg, max_vreg, vreg_flags))
                        continue;
-               if (apply_vreg_first_block_interference (cfg, ins, ins->sreg1, max_vreg, vreg_flags))
-                       continue;
-               if (apply_vreg_first_block_interference (cfg, ins, ins->sreg2, max_vreg, vreg_flags))
-                       continue;
+               num_sregs = mono_inst_get_src_registers (ins, sregs);
+               for (i = 0; i < num_sregs; ++i) {
+                       if (apply_vreg_first_block_interference (cfg, ins, sregs [i], max_vreg, vreg_flags))
+                               break;
+               }
        }
 
        if (IS_DEBUG_ON (cfg)) {
@@ -721,15 +724,19 @@ mono_simd_simplify_indirection (MonoCompile *cfg)
 
        for (bb = first_bb->next_bb; bb; bb = bb->next_bb) {
                for (ins = bb->code; ins; ins = ins->next) {
-                       
+                       int num_sregs;
+                       int sregs [MONO_MAX_SRC_REGS];
+
                        if (ins->opcode == OP_LDADDR && apply_vreg_following_block_interference (cfg, ins, ((MonoInst*)ins->inst_p0)->dreg, bb, max_vreg, vreg_flags, target_bb))
                                continue;
                        if (apply_vreg_following_block_interference (cfg, ins, ins->dreg, bb, max_vreg, vreg_flags, target_bb))
                                continue;
-                       if (apply_vreg_following_block_interference (cfg, ins, ins->sreg1, bb, max_vreg, vreg_flags, target_bb))
-                               continue;
-                       if (apply_vreg_following_block_interference (cfg, ins, ins->sreg2, bb, max_vreg, vreg_flags, target_bb))
-                               continue;
+                       num_sregs = mono_inst_get_src_registers (ins, sregs);
+                       for (i = 0; i < num_sregs; ++i) {
+                               if (apply_vreg_following_block_interference (cfg, ins, sregs [i], bb,
+                                               max_vreg, vreg_flags, target_bb))
+                                       continue;
+                       }
                }
        }
 
@@ -745,10 +752,19 @@ mono_simd_simplify_indirection (MonoCompile *cfg)
                if (!(vreg_flags [var->dreg] & VREG_SINGLE_BB_USE))
                        continue;
                for (ins = target_bb [var->dreg]->code; ins; ins = ins->next) {
+                       int num_sregs, j;
+                       int sregs [MONO_MAX_SRC_REGS];
+                       gboolean found = FALSE;
+
+                       num_sregs = mono_inst_get_src_registers (ins, sregs);
+                       for (j = 0; j < num_sregs; ++j) {
+                               if (sregs [i] == var->dreg)
+                                       found = TRUE;
+                       }
                        /*We can avoid inserting the XZERO if the first use doesn't depend on the zero'ed value.*/
-                       if (ins->dreg == var->dreg && ins->sreg1 != var->dreg && ins->sreg2 != var->dreg) {
+                       if (ins->dreg == var->dreg && !found) {
                                break;
-                       } else if (ins->sreg1 == var->dreg || ins->sreg2 == var->dreg) {
+                       } else if (found) {
                                MonoInst *tmp;
                                MONO_INST_NEW (cfg, tmp, OP_XZERO);
                                tmp->dreg = var->dreg;
index 4b286b3329fc8fe3a1d230ce8cf24926ee92e240..25bc360bcab93864c78dd834aa573ff5e3f35068 100644 (file)
@@ -178,6 +178,8 @@ mono_ssa_rename_vars (MonoCompile *cfg, int max_vars, MonoBasicBlock *bb, gboole
        /* First pass: Create new vars */
        for (ins = bb->code; ins; ins = ins->next) {
                const char *spec = INS_INFO (ins->opcode);
+               int num_sregs;
+               int sregs [MONO_MAX_SRC_REGS];
 
 #ifdef DEBUG_SSA
                printf ("\tProcessing "); mono_print_ins (ins);
@@ -185,42 +187,27 @@ mono_ssa_rename_vars (MonoCompile *cfg, int max_vars, MonoBasicBlock *bb, gboole
                if (ins->opcode == OP_NOP)
                        continue;
 
-               /* SREG1 */
-               if (spec [MONO_INST_SRC1] != ' ') {
-                       MonoInst *var = get_vreg_to_inst (cfg, ins->sreg1);
-                       if (var && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT))) {
-                               int idx = var->inst_c0;
-                               if (stack [idx]) {
-                                       if (var->opcode != OP_ARG)
-                                               g_assert (stack [idx]);
-                                       ins->sreg1 = stack [idx]->dreg;
-                                       record_use (cfg, stack [idx], bb, ins);
-                               }
-                               else
-                                       record_use (cfg, var, bb, ins);
-                       }
-                       else if (G_UNLIKELY (!var && lvreg_stack [ins->sreg1]))
-                               ins->sreg1 = lvreg_stack [ins->sreg1];
-               }                                       
-
-               /* SREG2 */
-               if (spec [MONO_INST_SRC2] != ' ') {
-                       MonoInst *var = get_vreg_to_inst (cfg, ins->sreg2);
-                       if (var && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT))) {
-                               int idx = var->inst_c0;
-                               if (stack [idx]) {
-                                       if (var->opcode != OP_ARG)
-                                               g_assert (stack [idx]);
-
-                                       ins->sreg2 = stack [idx]->dreg;
-                                       record_use (cfg, stack [idx], bb, ins);
+               /* SREGs */
+               num_sregs = mono_inst_get_src_registers (ins, sregs);
+               for (i = 0; i < num_sregs; ++i) {
+                       if (spec [MONO_INST_SRC1 + i] != ' ') {
+                               MonoInst *var = get_vreg_to_inst (cfg, sregs [i]);
+                               if (var && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT))) {
+                                       int idx = var->inst_c0;
+                                       if (stack [idx]) {
+                                               if (var->opcode != OP_ARG)
+                                                       g_assert (stack [idx]);
+                                               sregs [i] = stack [idx]->dreg;
+                                               record_use (cfg, stack [idx], bb, ins);
+                                       }
+                                       else
+                                               record_use (cfg, var, bb, ins);
                                }
-                               else
-                                       record_use (cfg, var, bb, ins);
+                               else if (G_UNLIKELY (!var && lvreg_stack [sregs [i]]))
+                                       sregs [i] = lvreg_stack [sregs [i]];
                        }
-                       else if (G_UNLIKELY (!var && lvreg_stack [ins->sreg2]))
-                               ins->sreg2 = lvreg_stack [ins->sreg2];
                }
+               mono_inst_set_src_registers (ins, sregs);
 
                if (MONO_IS_STORE_MEMBASE (ins)) {
                        MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
@@ -565,6 +552,8 @@ mono_ssa_remove (MonoCompile *cfg)
 
                for (ins = bb->code; ins; ins = ins->next) {
                        const char *spec = INS_INFO (ins->opcode);
+                       int num_sregs, j;
+                       int sregs [MONO_MAX_SRC_REGS];
 
                        if (ins->opcode == OP_NOP)
                                continue;
@@ -586,32 +575,20 @@ mono_ssa_remove (MonoCompile *cfg)
                                }
                        }
 
-                       if (spec [MONO_INST_SRC1] != ' ') {
-                               MonoInst *var = get_vreg_to_inst (cfg, ins->sreg1);
-
-                               if (var) {
-                                       MonoMethodVar *vmv = MONO_VARINFO (cfg, var->inst_c0);
-
-                                       if ((vmv->reg != -1) && (vmv->idx != vmv->reg) && (MONO_VARINFO (cfg, vmv->reg)->reg != -1)) {
-                                               printf ("COALESCE: R%d -> R%d\n", ins->sreg1, cfg->varinfo [vmv->reg]->dreg);
-                                               ins->sreg1 = cfg->varinfo [vmv->reg]->dreg; 
-                                       }
-                               }
-                       }
-
-                       if (spec [MONO_INST_SRC2] != ' ') {
-                               MonoInst *var = get_vreg_to_inst (cfg, ins->sreg2);
+                       num_sregs = mono_inst_get_src_registers (ins, sregs);
+                       for (j = 0; j < num_sregs; ++j) {
+                               MonoInst *var = get_vreg_to_inst (cfg, sregs [i]);
 
                                if (var) {
                                        MonoMethodVar *vmv = MONO_VARINFO (cfg, var->inst_c0);
 
                                        if ((vmv->reg != -1) && (vmv->idx != vmv->reg) && (MONO_VARINFO (cfg, vmv->reg)->reg != -1)) {
-                                               printf ("COALESCE: R%d -> R%d\n", ins->sreg2, cfg->varinfo [vmv->reg]->dreg);
-                                               ins->sreg2 = cfg->varinfo [vmv->reg]->dreg; 
+                                               printf ("COALESCE: R%d -> R%d\n", sregs [i], cfg->varinfo [vmv->reg]->dreg);
+                                               sregs [i] = cfg->varinfo [vmv->reg]->dreg;
                                        }
                                }
                        }
-
+                       mono_inst_set_src_registers (ins, sregs);
                }
        }
 
@@ -640,24 +617,20 @@ mono_ssa_create_def_use (MonoCompile *cfg)
                for (ins = bb->code; ins; ins = ins->next) {
                        const char *spec = INS_INFO (ins->opcode);
                        MonoMethodVar *info;
+                       int num_sregs;
+                       int sregs [MONO_MAX_SRC_REGS];
 
                        if (ins->opcode == OP_NOP)
                                continue;
 
-                       /* SREG1 */
-                       if (spec [MONO_INST_SRC1] != ' ') {
-                               MonoInst *var = get_vreg_to_inst (cfg, ins->sreg1);
+                       /* SREGs */
+                       num_sregs = mono_inst_get_src_registers (ins, sregs);
+                       for (i = 0; i < num_sregs; ++i) {
+                               MonoInst *var = get_vreg_to_inst (cfg, sregs [i]);
                                if (var && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)))
                                        record_use (cfg, var, bb, ins);
                        }
 
-                       /* SREG2 */
-                       if (spec [MONO_INST_SRC2] != ' ') {
-                               MonoInst *var = get_vreg_to_inst (cfg, ins->sreg2);
-                               if (var && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)))
-                                       record_use (cfg, var, bb, ins);
-                       }
-                               
                        if (MONO_IS_STORE_MEMBASE (ins)) {
                                MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
                                if (var && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)))
@@ -713,13 +686,20 @@ mono_ssa_copyprop (MonoCompile *cfg)
                                        MonoVarUsageInfo *u = (MonoVarUsageInfo*)l->data;
                                        MonoInst *ins = u->inst;
                                        GList *next = l->next;
+                                       int num_sregs;
+                                       int sregs [MONO_MAX_SRC_REGS];
 
                                        spec = INS_INFO (ins->opcode);
 
-                                       if (spec [MONO_INST_SRC1] != ' ' && ins->sreg1 == dreg) {
-                                               ins->sreg1 = sreg1;
-                                       } else if (spec [MONO_INST_SRC2] != ' ' && ins->sreg2 == dreg) {
-                                               ins->sreg2 = sreg1;
+                                       num_sregs = mono_inst_get_src_registers (ins, sregs);
+                                       for (i = 0; i < num_sregs; ++i) {
+                                               if (sregs [i] == dreg)
+                                                       break;
+                                       }
+                                       if (i < num_sregs) {
+                                               g_assert (sregs [i] == dreg);
+                                               sregs [i] = sreg1;
+                                               mono_inst_set_src_registers (ins, sregs);
                                        } else if (MONO_IS_STORE_MEMBASE (ins) && ins->dreg == dreg) {
                                                ins->dreg = sreg1;
                                        } else if (MONO_IS_PHI (ins)) {
@@ -755,10 +735,13 @@ mono_ssa_copyprop (MonoCompile *cfg)
 static int
 evaluate_ins (MonoCompile *cfg, MonoInst *ins, MonoInst **res, MonoInst **carray)
 {
-       MonoInst *arg0, *arg1, *c0;
-       int r1, r2;
-       gboolean const_args = FALSE;
+       MonoInst *args [MONO_MAX_SRC_REGS];
+       int rs [MONO_MAX_SRC_REGS];
+       MonoInst *c0;
+       gboolean const_args = TRUE;
        const char *spec = INS_INFO (ins->opcode);
+       int num_sregs, i;
+       int sregs [MONO_MAX_SRC_REGS];
 
        /* Short-circuit this */
        if (ins->opcode == OP_ICONST) {
@@ -769,52 +752,32 @@ evaluate_ins (MonoCompile *cfg, MonoInst *ins, MonoInst **res, MonoInst **carray
        if (ins->opcode == OP_NOP)
                return 2;
 
-       arg0 = NULL;
-       if (spec [MONO_INST_SRC1] != ' ') {
-               MonoInst *var = get_vreg_to_inst (cfg, ins->sreg1);
-
-               r1 = 2;
-               arg0 = carray [ins->sreg1];
-               if (arg0)
-                       r1 = 1;
-               else if (var && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)))
-                       r1 = MONO_VARINFO (cfg, var->inst_c0)->cpstate;
-       } else {
-               r1 = 2;
-       }
+       num_sregs = mono_inst_get_src_registers (ins, sregs);
+       for (i = 0; i < MONO_MAX_SRC_REGS; ++i)
+               args [i] = NULL;
+       for (i = 0; i < num_sregs; ++i) {
+               MonoInst *var = get_vreg_to_inst (cfg, sregs [i]);
 
-       arg1 = NULL;
-       if (spec [MONO_INST_SRC2] != ' ') {
-               MonoInst *var = get_vreg_to_inst (cfg, ins->sreg2);
-
-               r2 = 2;
-               arg1 = carray [ins->sreg2];
-               if (arg1)
-                       r2 = 1;
+               rs [i] = 2;
+               args [i] = carray [sregs [i]];
+               if (args [i])
+                       rs [i] = 1;
                else if (var && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)))
-                       r2 = MONO_VARINFO (cfg, var->inst_c0)->cpstate;
-       }
-       else {
-               r2 = 0;
+                       rs [i] = MONO_VARINFO (cfg, var->inst_c0)->cpstate;
+               if (rs [i] != 1)
+                       const_args = FALSE;
        }
 
        c0 = NULL;
-       if ((spec [MONO_INST_SRC1] != ' ') && (spec [MONO_INST_SRC2] != ' ')) {
-               /* Binop */
-               const_args = (r1 == 1) && (r2 == 1);
-       }
-       else if (spec [MONO_INST_SRC1] != ' ') {
-               /* Unop */
-               const_args = (r1 == 1);
-       }
 
-       if (const_args) {
+       if (num_sregs > 0 && const_args) {
+               g_assert (num_sregs <= 2);
                if ((spec [MONO_INST_DEST] != ' ') && carray [ins->dreg]) {
                        // Cached value
                        *res = carray [ins->dreg];
                        return 1;
                }
-               c0 = mono_constant_fold_ins (cfg, ins, arg0, arg1, FALSE);
+               c0 = mono_constant_fold_ins (cfg, ins, args [0], args [1], FALSE);
                if (c0) {
                        if (G_UNLIKELY (cfg->verbose_level > 1)) {
                                printf ("\t cfold -> ");
@@ -828,22 +791,13 @@ evaluate_ins (MonoCompile *cfg, MonoInst *ins, MonoInst **res, MonoInst **carray
                        return 2;
        }
 
-       if ((spec [MONO_INST_SRC1] != ' ') && (spec [MONO_INST_SRC2] != ' ')) {
-               /* Binop */
-               if ((r1 == 2) || (r2 == 2))
-                       return 2;
-               else
-                       return 0;
-       }
-       else if (spec [MONO_INST_SRC1] != ' ') {
-               /* Unop */
-               if (r1 == 2)
+       if (num_sregs == 0)
+               return 2;
+       for (i = 0; i < num_sregs; ++i) {
+               if (rs [i] == 2)
                        return 2;
-               else
-                       return 0;
        }
-       else
-               return 2;
+       return 0;
 }
 
 static inline void
@@ -1070,6 +1024,7 @@ fold_ins (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *ins, MonoInst **carray
 {
        const char *spec = INS_INFO (ins->opcode);
        int opcode2;
+       int num_sregs = mono_inst_get_num_src_registers (ins);
 
        if ((ins->opcode != OP_NOP) && (ins->dreg != -1) && !MONO_IS_STORE_MEMBASE (ins)) {
                if (carray [ins->dreg] && (spec [MONO_INST_DEST] == 'i') && (ins->dreg >= MONO_MAX_IREGS)) {
@@ -1078,9 +1033,8 @@ fold_ins (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *ins, MonoInst **carray
                        g_assert (carray [ins->dreg]->opcode == OP_ICONST);
                        ins->opcode = OP_ICONST;
                        ins->inst_c0 = carray [ins->dreg]->inst_c0;
-                       ins->sreg1 = ins->sreg2 = -1;
-               }
-               else if ((spec [MONO_INST_SRC2] != ' ') && carray [ins->sreg2]) {
+                       MONO_INST_NULLIFY_SREGS (ins);
+               } else if (num_sregs == 2 && carray [ins->sreg2]) {
                        /* Perform op->op_imm conversion */
                        opcode2 = mono_op_to_op_imm (ins->opcode);
                        if (opcode2 != -1) {
@@ -1091,6 +1045,8 @@ fold_ins (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *ins, MonoInst **carray
                                if ((opcode2 == OP_VOIDCALL) || (opcode2 == OP_CALL) || (opcode2 == OP_LCALL) || (opcode2 == OP_FCALL))
                                        ((MonoCallInst*)ins)->fptr = (gpointer)ins->inst_imm;
                        }
+               } else {
+                       /* FIXME: Handle 3 op insns */
                }
 
                if (MONO_IS_JUMP_TABLE (ins)) {