2005-12-12 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / mini / mini-codegen.c
index de67a7d3489c46463e1fcf1e4c9e6c438f6f4f02..cfc57f2a53a5f66478ee55f4fff1049dd24bb321 100644 (file)
@@ -7,7 +7,6 @@
 #include <string.h>
 #include <math.h>
 #include <unistd.h>
-#include <sys/mman.h>
 
 #include <mono/metadata/appdomain.h>
 #include <mono/metadata/debug-helpers.h>
@@ -29,11 +28,21 @@ static const char*const * ins_spec = amd64_desc;
 const char * const sparc_desc [OP_LAST];
 static const char*const * ins_spec = sparc_desc;
 #elif defined(__i386__)
+#ifdef _MSC_VER
+extern const char * const pentium_desc [OP_LAST];
+#else
 const char * const pentium_desc [OP_LAST];
+#endif
 static const char*const * ins_spec = pentium_desc;
 #elif defined(__ia64__)
 const char * const ia64_desc [OP_LAST];
 static const char*const * ins_spec = ia64_desc;
+#elif defined(__arm__)
+const char * const arm_cpu_desc [OP_LAST];
+static const char*const * ins_spec = arm_cpu_desc;
+#elif defined(__s390__)
+const char * const s390_cpu_desc [OP_LAST];
+static const char*const * ins_spec = s390_cpu_desc;
 #else
 #error "Not implemented"
 #endif
@@ -78,8 +87,13 @@ mono_spillvar_offset (MonoCompile *cfg, int spillvar)
                if (!*si) {
                        *si = info = mono_mempool_alloc (cfg->mempool, sizeof (MonoSpillInfo));
                        info->next = NULL;
-                       cfg->stack_offset += sizeof (gpointer);
-                       info->offset = - cfg->stack_offset;
+                       if (cfg->flags & MONO_CFG_HAS_SPILLUP) {
+                               info->offset = cfg->stack_offset;
+                               cfg->stack_offset += sizeof (gpointer);
+                       } else {
+                               cfg->stack_offset += sizeof (gpointer);
+                               info->offset = - cfg->stack_offset;
+                       }
                }
 
                if (i == spillvar)
@@ -111,8 +125,16 @@ mono_spillvar_offset_float (MonoCompile *cfg, int spillvar)
                if (!*si) {
                        *si = info = mono_mempool_alloc (cfg->mempool, sizeof (MonoSpillInfo));
                        info->next = NULL;
-                       cfg->stack_offset += sizeof (double);
-                       info->offset = - cfg->stack_offset;
+                       if (cfg->flags & MONO_CFG_HAS_SPILLUP) {
+                               cfg->stack_offset += 7;
+                               cfg->stack_offset &= ~7;
+                               info->offset = cfg->stack_offset;
+                               cfg->stack_offset += sizeof (double);
+                       } else {
+                               /* FIXME: align */
+                               cfg->stack_offset += sizeof (double);
+                               info->offset = - cfg->stack_offset;
+                       }
                }
 
                if (i == spillvar)
@@ -158,12 +180,14 @@ create_spilled_load_float (MonoCompile *cfg, int spill, int reg, MonoInst *ins)
        return load;
 }
 
+#define regmask(reg) (((regmask_t)1) << (reg))
+
 #define is_hard_ireg(r) ((r) >= 0 && (r) < MONO_MAX_IREGS)
 #define is_hard_freg(r) ((r) >= 0 && (r) < MONO_MAX_FREGS)
-#define is_global_ireg(r) (is_hard_ireg ((r)) && (MONO_ARCH_CALLEE_SAVED_REGS & (1 << (r))))
-#define is_local_ireg(r) (is_hard_ireg ((r)) && (MONO_ARCH_CALLEE_REGS & (1 << (r))))
-#define is_global_freg(r) (is_hard_freg ((r)) && (MONO_ARCH_CALLEE_SAVED_FREGS & (1 << (r))))
-#define is_local_freg(r) (is_hard_ireg ((r)) && (MONO_ARCH_CALLEE_FREGS & (1 << (r))))
+#define is_global_ireg(r) (is_hard_ireg ((r)) && (MONO_ARCH_CALLEE_SAVED_REGS & (regmask (r))))
+#define is_local_ireg(r) (is_hard_ireg ((r)) && (MONO_ARCH_CALLEE_REGS & (regmask (r))))
+#define is_global_freg(r) (is_hard_freg ((r)) && (MONO_ARCH_CALLEE_SAVED_FREGS & (regmask (r))))
+#define is_local_freg(r) (is_hard_ireg ((r)) && (MONO_ARCH_CALLEE_FREGS & (regmask (r))))
 #define ireg_is_freeable(r) is_local_ireg ((r))
 #define freg_is_freeable(r) is_hard_freg ((r))
 
@@ -180,7 +204,12 @@ create_spilled_load_float (MonoCompile *cfg, int spill, int reg, MonoInst *ins)
 #define dreg_is_fp(ins)  (ins_spec [(ins)->opcode] [MONO_INST_DEST] == 'f')
 #endif
 
-#define regpair_reg2_mask(desc,hreg1) ((MONO_ARCH_INST_REGPAIR_REG2 (desc,hreg1) != -1) ? (1 << MONO_ARCH_INST_REGPAIR_REG2 (desc,hreg1)) : MONO_ARCH_CALLEE_REGS)
+#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)
+
+#ifdef MONO_ARCH_IS_GLOBAL_IREG
+#undef is_global_ireg
+#define is_global_ireg(reg) MONO_ARCH_IS_GLOBAL_IREG ((reg))
+#endif
 
 typedef struct {
        int born_in;
@@ -188,7 +217,7 @@ typedef struct {
        int last_use;
        int prev_use;
        int flags;              /* used to track fp spill/load */
-       guint32 preferred_mask; /* the hreg where the register should be allocated, or 0 */
+       regmask_t preferred_mask; /* the hreg where the register should be allocated, or 0 */
 } RegTrack;
 
 static void
@@ -200,10 +229,17 @@ print_ins (int i, MonoInst *ins)
                g_error ("Unknown opcode: %s\n", mono_inst_name (ins->opcode));
 
        if (spec [MONO_INST_DEST]) {
-               gboolean fp = (spec [MONO_INST_DEST] == 'f');
-               if (is_soft_reg (ins->dreg, fp))
-                       g_print (" R%d <-", ins->dreg);
-               else if (spec [MONO_INST_DEST] == 'b') {
+               gboolean fp = dreg_is_fp (ins);
+               if (is_soft_reg (ins->dreg, fp)) {
+                       if (spec [MONO_INST_DEST] == 'b') {
+                               if (ins->inst_offset == 0)
+                                       g_print (" [R%d] <-", ins->dreg);
+                               else
+                                       g_print (" [R%d + 0x%lx] <-", ins->dreg, (long)ins->inst_offset);
+                       }
+                       else
+                               g_print (" R%d <-", ins->dreg);
+               } else if (spec [MONO_INST_DEST] == 'b') {
                        if (ins->inst_offset == 0)
                                g_print (" [%s] <-", mono_arch_regname (ins->dreg));
                        else
@@ -316,16 +352,21 @@ get_register_force_spilling (MonoCompile *cfg, InstList *item, MonoInst *ins, in
        ins->next = load;
        DEBUG (g_print ("SPILLED LOAD (%d at 0x%08lx(%%ebp)) R%d (freed %s)\n", spill, (long)load->inst_offset, i, mono_regname_full (sel, fp)));
        if (fp)
-               i = mono_regstate_alloc_float (cfg->rs, 1 << sel);
+               i = mono_regstate_alloc_float (cfg->rs, regmask (sel));
        else
-               i = mono_regstate_alloc_int (cfg->rs, 1 << sel);
+               i = mono_regstate_alloc_int (cfg->rs, regmask (sel));
        g_assert (i == sel);
 
        return sel;
 }
 
+/* This isn't defined on older glib versions and on some platforms */
+#ifndef G_GUINT64_FORMAT
+#define G_GUINT64_FORMAT "ul"
+#endif
+
 static int
-get_register_spilling (MonoCompile *cfg, InstList *item, MonoInst *ins, guint32 regmask, int reg, gboolean fp)
+get_register_spilling (MonoCompile *cfg, InstList *item, MonoInst *ins, regmask_t regmask, int reg, gboolean fp)
 {
        MonoInst *load;
        int i, sel, spill;
@@ -340,34 +381,34 @@ get_register_spilling (MonoCompile *cfg, InstList *item, MonoInst *ins, guint32
                symbolic = cfg->rs->isymbolic;
        }
 
-       DEBUG (g_print ("\tstart regmask to assign R%d: 0x%08x (R%d <- R%d R%d)\n", reg, regmask, ins->dreg, ins->sreg1, ins->sreg2));
+       DEBUG (g_print ("\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));
        /* exclude the registers in the current instruction */
        if ((sreg1_is_fp (ins) == fp) && (reg != ins->sreg1) && (reg_is_freeable (ins->sreg1, fp) || (is_soft_reg (ins->sreg1, fp) && rassign (cfg, ins->sreg1, fp) >= 0))) {
                if (is_soft_reg (ins->sreg1, fp))
-                       regmask &= ~ (1 << rassign (cfg, ins->sreg1, fp));
+                       regmask &= ~ (regmask (rassign (cfg, ins->sreg1, fp)));
                else
-                       regmask &= ~ (1 << ins->sreg1);
+                       regmask &= ~ (regmask (ins->sreg1));
                DEBUG (g_print ("\t\texcluding sreg1 %s\n", mono_regname_full (ins->sreg1, fp)));
        }
        if ((sreg2_is_fp (ins) == fp) && (reg != ins->sreg2) && (reg_is_freeable (ins->sreg2, fp) || (is_soft_reg (ins->sreg2, fp) && rassign (cfg, ins->sreg2, fp) >= 0))) {
                if (is_soft_reg (ins->sreg2, fp))
-                       regmask &= ~ (1 << rassign (cfg, ins->sreg2, fp));
+                       regmask &= ~ (regmask (rassign (cfg, ins->sreg2, fp)));
                else
-                       regmask &= ~ (1 << ins->sreg2);
+                       regmask &= ~ (regmask (ins->sreg2));
                DEBUG (g_print ("\t\texcluding sreg2 %s %d\n", mono_regname_full (ins->sreg2, fp), ins->sreg2));
        }
        if ((dreg_is_fp (ins) == fp) && (reg != ins->dreg) && reg_is_freeable (ins->dreg, fp)) {
-               regmask &= ~ (1 << ins->dreg);
+               regmask &= ~ (regmask (ins->dreg));
                DEBUG (g_print ("\t\texcluding dreg %s\n", mono_regname_full (ins->dreg, fp)));
        }
 
-       DEBUG (g_print ("\t\tavailable regmask: 0x%08x\n", regmask));
+       DEBUG (g_print ("\t\tavailable regmask: 0x%08" G_GUINT64_FORMAT "\n", (guint64)regmask));
        g_assert (regmask); /* need at least a register we can free */
        sel = -1;
        /* we should track prev_use and spill the register that's farther */
        if (fp) {
                for (i = 0; i < MONO_MAX_FREGS; ++i) {
-                       if (regmask & (1 << i)) {
+                       if (regmask & (regmask (i))) {
                                sel = i;
                                DEBUG (g_print ("\t\tselected register %s has assignment %d\n", mono_arch_fregname (sel), cfg->rs->fsymbolic [sel]));
                                break;
@@ -381,7 +422,7 @@ get_register_spilling (MonoCompile *cfg, InstList *item, MonoInst *ins, guint32
        }
        else {
                for (i = 0; i < MONO_MAX_IREGS; ++i) {
-                       if (regmask & (1 << i)) {
+                       if (regmask & (regmask (i))) {
                                sel = i;
                                DEBUG (g_print ("\t\tselected register %s has assignment %d\n", mono_arch_regname (sel), cfg->rs->isymbolic [sel]));
                                break;
@@ -407,9 +448,9 @@ get_register_spilling (MonoCompile *cfg, InstList *item, MonoInst *ins, guint32
        ins->next = load;
        DEBUG (g_print ("\tSPILLED LOAD (%d at 0x%08lx(%%ebp)) R%d (freed %s)\n", spill, (long)load->inst_offset, i, mono_regname_full (sel, fp)));
        if (fp)
-               i = mono_regstate_alloc_float (cfg->rs, 1 << sel);
+               i = mono_regstate_alloc_float (cfg->rs, regmask (sel));
        else
-               i = mono_regstate_alloc_int (cfg->rs, 1 << sel);
+               i = mono_regstate_alloc_int (cfg->rs, regmask (sel));
        g_assert (i == sel);
        
        return sel;
@@ -418,15 +459,34 @@ get_register_spilling (MonoCompile *cfg, InstList *item, MonoInst *ins, guint32
 static void
 free_up_ireg (MonoCompile *cfg, InstList *item, MonoInst *ins, int hreg)
 {
-       if (!(cfg->rs->ifree_mask & (1 << hreg))) {
+       if (!(cfg->rs->ifree_mask & (regmask (hreg)))) {
                DEBUG (g_print ("\tforced spill of R%d\n", cfg->rs->isymbolic [hreg]));
                get_register_force_spilling (cfg, item, ins, cfg->rs->isymbolic [hreg], FALSE);
                mono_regstate_free_int (cfg->rs, hreg);
        }
 }
 
+static void
+free_up_reg (MonoCompile *cfg, InstList *item, MonoInst *ins, int hreg, gboolean fp)
+{
+       if (fp) {
+               if (!(cfg->rs->ffree_mask & (regmask (hreg)))) {
+                       DEBUG (g_print ("\tforced spill of R%d\n", cfg->rs->isymbolic [hreg]));
+                       get_register_force_spilling (cfg, item, ins, cfg->rs->isymbolic [hreg], fp);
+                       mono_regstate_free_float (cfg->rs, hreg);
+               }
+       }
+       else {
+               if (!(cfg->rs->ifree_mask & (regmask (hreg)))) {
+                       DEBUG (g_print ("\tforced spill of R%d\n", cfg->rs->isymbolic [hreg]));
+                       get_register_force_spilling (cfg, item, ins, cfg->rs->isymbolic [hreg], fp);
+                       mono_regstate_free_int (cfg->rs, hreg);
+               }
+       }
+}
+
 static MonoInst*
-create_copy_ins (MonoCompile *cfg, int dest, int src, MonoInst *ins, gboolean fp)
+create_copy_ins (MonoCompile *cfg, int dest, int src, MonoInst *ins, const unsigned char *ip, gboolean fp)
 {
        MonoInst *copy;
 
@@ -437,11 +497,13 @@ create_copy_ins (MonoCompile *cfg, int dest, int src, MonoInst *ins, gboolean fp
 
        copy->dreg = dest;
        copy->sreg1 = src;
+       copy->cil_code = ip;
        if (ins) {
                copy->next = ins->next;
+               copy->cil_code = ins->cil_code;
                ins->next = copy;
        }
-       DEBUG (g_print ("\tforced copy from %s to %s\n", mono_arch_regname (src), mono_arch_regname (dest)));
+       DEBUG (g_print ("\tforced copy from %s to %s\n", mono_regname_full (src, fp), mono_regname_full (dest, fp)));
        return copy;
 }
 
@@ -484,13 +546,13 @@ insert_before_ins (MonoInst *ins, InstList *item, MonoInst* to_insert)
 
 /* flags used in reginfo->flags */
 enum {
-       MONO_FP_NEEDS_LOAD_SPILL        = 1 << 0,
-       MONO_FP_NEEDS_SPILL                     = 1 << 1,
-       MONO_FP_NEEDS_LOAD                      = 1 << 2
+       MONO_FP_NEEDS_LOAD_SPILL        = regmask (0),
+       MONO_FP_NEEDS_SPILL                     = regmask (1),
+       MONO_FP_NEEDS_LOAD                      = regmask (2)
 };
 
 static int
-alloc_int_reg (MonoCompile *cfg, InstList *tmp, MonoInst *ins, guint32 dest_mask, int sym_reg, RegTrack *info)
+alloc_int_reg (MonoCompile *cfg, InstList *tmp, MonoInst *ins, regmask_t dest_mask, int sym_reg, RegTrack *info)
 {
        int val;
 
@@ -510,7 +572,7 @@ alloc_int_reg (MonoCompile *cfg, InstList *tmp, MonoInst *ins, guint32 dest_mask
 }
 
 static int
-alloc_float_reg (MonoCompile *cfg, InstList *tmp, MonoInst *ins, guint32 dest_mask, int sym_reg)
+alloc_float_reg (MonoCompile *cfg, InstList *tmp, MonoInst *ins, regmask_t dest_mask, int sym_reg)
 {
        int val;
 
@@ -524,7 +586,7 @@ alloc_float_reg (MonoCompile *cfg, InstList *tmp, MonoInst *ins, guint32 dest_ma
 }
 
 static int
-alloc_reg (MonoCompile *cfg, InstList *tmp, MonoInst *ins, guint32 dest_mask, int sym_reg, RegTrack *info, gboolean fp)
+alloc_reg (MonoCompile *cfg, InstList *tmp, MonoInst *ins, regmask_t dest_mask, int sym_reg, RegTrack *info, gboolean fp)
 {
        if (fp)
                return alloc_float_reg (cfg, tmp, ins, dest_mask, sym_reg);
@@ -533,7 +595,7 @@ alloc_reg (MonoCompile *cfg, InstList *tmp, MonoInst *ins, guint32 dest_mask, in
 }
 
 static inline void
-assign_reg (MonoRegState *rs, int reg, int hreg, gboolean fp)
+assign_reg (MonoCompile *cfg, MonoRegState *rs, int reg, int hreg, gboolean fp)
 {
        if (fp) {
                g_assert (reg >= MONO_MAX_FREGS);
@@ -542,7 +604,7 @@ assign_reg (MonoRegState *rs, int reg, int hreg, gboolean fp)
 
                rs->fassign [reg] = hreg;
                rs->fsymbolic [hreg] = reg;
-               rs->ffree_mask &= ~ (1 << hreg);
+               rs->ffree_mask &= ~ (regmask (hreg));
        }
        else {
                g_assert (reg >= MONO_MAX_IREGS);
@@ -551,14 +613,14 @@ assign_reg (MonoRegState *rs, int reg, int hreg, gboolean fp)
 
                rs->iassign [reg] = hreg;
                rs->isymbolic [hreg] = reg;
-               rs->ifree_mask &= ~ (1 << hreg);
+               rs->ifree_mask &= ~ (regmask (hreg));
        }
 }
 
 static inline void
-assign_ireg (MonoRegState *rs, int reg, int hreg)
+assign_ireg (MonoCompile *cfg, MonoRegState *rs, int reg, int hreg)
 {
-       assign_reg (rs, reg, hreg, FALSE);
+       assign_reg (cfg, rs, reg, hreg, FALSE);
 }
 
 /*
@@ -594,7 +656,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
        rs->ffree_mask = MONO_ARCH_CALLEE_FREGS;
 
        if (use_fpstack)
-               rs->ffree_mask = 0xff & ~(1 << MONO_ARCH_FPSTACK_SIZE);
+               rs->ffree_mask = 0xff & ~(regmask (MONO_ARCH_FPSTACK_SIZE));
 
        ins = bb->code;
 
@@ -643,7 +705,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                        fpcount--;
                        }
 
-                       if (spec [MONO_INST_DEST] == 'f') {
+                       if (dreg_is_fp (ins)) {
                                if (use_fpstack && (spec [MONO_INST_CLOB] != 'm')) {
                                        if (fpcount >= MONO_ARCH_FPSTACK_SIZE) {
                                                reginfof [ins->dreg].flags |= MONO_FP_NEEDS_SPILL;
@@ -693,7 +755,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                if (spec [MONO_INST_DEST]) {
                        int dest_dreg;
 
-                       if (spec [MONO_INST_DEST] == 'f')
+                       if (dreg_is_fp (ins))
                                reginfod = reginfof;
                        else
                                reginfod = reginfo;
@@ -706,7 +768,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
 
                        dest_dreg = MONO_ARCH_INST_FIXED_REG (spec [MONO_INST_DEST]);
                        if (dest_dreg != -1)
-                               reginfod [ins->dreg].preferred_mask = (1 << dest_dreg);
+                               reginfod [ins->dreg].preferred_mask = (regmask (dest_dreg));
 
                        if (MONO_ARCH_INST_IS_REGPAIR (spec [MONO_INST_DEST])) {
                                /* The virtual register is allocated sequentially */
@@ -777,11 +839,13 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                int prev_dreg, prev_sreg1, prev_sreg2, clob_dreg;
                int dest_dreg, dest_sreg1, dest_sreg2, clob_reg;
                int dreg_high, sreg1_high;
-               guint32 dreg_mask, sreg1_mask, sreg2_mask, mask;
+               regmask_t dreg_mask, sreg1_mask, sreg2_mask, mask;
+               const unsigned char *ip;
                --i;
                ins = tmp->data;
                spec = ins_spec [ins->opcode];
                prev_dreg = -1;
+               prev_sreg2 = -1;
                clob_dreg = -1;
                clob_reg = -1;
                dest_dreg = -1;
@@ -794,6 +858,8 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                DEBUG (g_print ("processing:"));
                DEBUG (print_ins (i, ins));
 
+               ip = ins->cil_code;
+
                /*
                 * FIXED REGS
                 */
@@ -807,7 +873,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                 * TRACK FP STACK
                 */
                if (use_fpstack && (spec [MONO_INST_CLOB] != 'm')) {
-                       if (spec [MONO_INST_DEST] == 'f') {
+                       if (dreg_is_fp (ins)) {
                                if (reginfof [ins->dreg].flags & MONO_FP_NEEDS_SPILL) {
                                        GList *spill_node;
                                        MonoInst *store;
@@ -875,21 +941,21 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                 * TRACK FIXED SREG2
                 */
                if (dest_sreg2 != -1) {
-                       if (rs->ifree_mask & (1 << dest_sreg2)) {
+                       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, dest_sreg2, ins->sreg2, NULL, FALSE);
+                                       MonoInst *copy = create_copy_ins (cfg, dest_sreg2, ins->sreg2, NULL, ip, FALSE);
                                        insert_before_ins (ins, tmp, copy);
                                }
                                else {
                                        DEBUG (g_print ("\tshortcut assignment of R%d to %s\n", ins->sreg2, mono_arch_regname (dest_sreg2)));
-                                       assign_ireg (rs, ins->sreg2, dest_sreg2);
+                                       assign_ireg (cfg, rs, ins->sreg2, dest_sreg2);
                                }
                        } else {
                                int need_spill = TRUE;
 
-                               dreg_mask &= ~ (1 << dest_sreg2);
-                               sreg1_mask &= ~ (1 << dest_sreg2);
+                               dreg_mask &= ~ (regmask (dest_sreg2));
+                               sreg1_mask &= ~ (regmask (dest_sreg2));
 
                                /* 
                                 * First check if dreg is assigned to dest_sreg2, since we
@@ -908,14 +974,14 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                        DEBUG (g_print ("\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_ireg (rs, ins->dreg, new_dest);
+                                       assign_ireg (cfg, rs, ins->dreg, new_dest);
                                        clob_dreg = ins->dreg;
-                                       create_copy_ins (cfg, dest_sreg2, new_dest, ins, FALSE);
+                                       create_copy_ins (cfg, dest_sreg2, new_dest, ins, ip, FALSE);
                                        need_spill = FALSE;
                                }
 
                                if (is_global_ireg (ins->sreg2)) {
-                                       MonoInst *copy = create_copy_ins (cfg, dest_sreg2, ins->sreg2, NULL, FALSE);
+                                       MonoInst *copy = create_copy_ins (cfg, dest_sreg2, ins->sreg2, NULL, ip, FALSE);
                                        insert_before_ins (ins, tmp, copy);
                                }
                                else {
@@ -938,7 +1004,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
 
                                if (!is_global_ireg (ins->sreg2))
                                        /* force-set sreg2 */
-                                       assign_ireg (rs, ins->sreg2, dest_sreg2);
+                                       assign_ireg (cfg, rs, ins->sreg2, dest_sreg2);
                        }
                        ins->sreg2 = dest_sreg2;
                }
@@ -950,6 +1016,17 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                if (spec [MONO_INST_DEST] && (!fp || (fp && !use_fpstack)) && is_soft_reg (ins->dreg, fp))
                        prev_dreg = ins->dreg;
 
+               if (spec [MONO_INST_DEST] == 'b') {
+                       /* 
+                        * 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 dreg is a fixed regpair, free up both of the needed hregs to avoid
                 * various complex situations.
@@ -974,7 +1051,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
 
                if ((!fp || (fp && !use_fpstack)) && (is_soft_reg (ins->dreg, fp))) {
                        if (dest_dreg != -1)
-                               dreg_mask = (1 << dest_dreg);
+                               dreg_mask = (regmask (dest_dreg));
 
                        val = rassign (cfg, ins->dreg, fp);
 
@@ -985,7 +1062,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                        spill = -val -1;
                                }
                                val = alloc_reg (cfg, tmp, ins, dreg_mask, ins->dreg, &reginfo [ins->dreg], fp);
-                               assign_reg (rs, ins->dreg, val, fp);
+                               assign_reg (cfg, rs, ins->dreg, val, fp);
                                if (spill)
                                        create_spilled_store (cfg, spill, val, prev_dreg, ins, fp);
                        }
@@ -1016,20 +1093,20 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                        create_spilled_store (cfg, spill, val, reg2, ins, fp);
                        }
                        else {
-                               if (! (mask & (1 << val))) {
+                               if (! (mask & (regmask (val)))) {
                                        val = mono_regstate_alloc_int (rs, mask);
                                        if (val < 0)
                                                val = get_register_spilling (cfg, tmp, ins, mask, reg2, fp);
 
                                        /* Reallocate hreg to the correct register */
-                                       create_copy_ins (cfg, rs->iassign [reg2], val, ins, fp);
+                                       create_copy_ins (cfg, rs->iassign [reg2], val, ins, ip, fp);
 
                                        mono_regstate_free_int (rs, rs->iassign [reg2]);
                                }
                        }                                       
 
                        DEBUG (g_print ("\tassigned dreg-high %s to dest R%d\n", mono_arch_regname (val), reg2));
-                       assign_reg (rs, reg2, val, fp);
+                       assign_reg (cfg, rs, reg2, val, fp);
 
                        dreg_high = val;
                        ins->unused = val;
@@ -1057,17 +1134,32 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
 
                if ((dest_dreg != -1) && (ins->dreg != dest_dreg)) {
                        /* this instruction only outputs to dest_dreg, need to copy */
-                       create_copy_ins (cfg, ins->dreg, dest_dreg, ins, fp);
+                       create_copy_ins (cfg, ins->dreg, dest_dreg, ins, ip, fp);
                        ins->dreg = dest_dreg;
 
-                       if (rs->isymbolic [dest_dreg] >= MONO_MAX_IREGS)
-                               free_up_ireg (cfg, tmp, ins, dest_dreg);
+                       if (fp) {
+                               if (rs->fsymbolic [dest_dreg] >= MONO_MAX_FREGS)
+                                       free_up_reg (cfg, tmp, ins, dest_dreg, fp);
+                       }
+                       else {
+                               if (rs->isymbolic [dest_dreg] >= MONO_MAX_IREGS)
+                                       free_up_reg (cfg, tmp, ins, dest_dreg, fp);
+                       }
+               }
+
+               if (spec [MONO_INST_DEST] == 'b') {
+                       /* 
+                        * The dest reg is read by the instruction, not written, so
+                        * avoid allocating sreg1/sreg2 to the same reg.
+                        */
+                       sreg1_mask &= ~ (regmask (ins->dreg));
+                       sreg2_mask &= ~ (regmask (ins->dreg));
                }
 
                /*
                 * TRACK CLOBBERING
                 */
-               if ((clob_reg != -1) && (!(rs->ifree_mask & (1 << clob_reg)))) {
+               if ((clob_reg != -1) && (!(rs->ifree_mask & (regmask (clob_reg))))) {
                        DEBUG (g_print ("\tforced spill of clobbered reg R%d\n", rs->isymbolic [clob_reg]));
                        get_register_force_spilling (cfg, tmp, ins, rs->isymbolic [clob_reg], FALSE);
                        mono_regstate_free_int (rs, clob_reg);
@@ -1094,7 +1186,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                dreg2 = -1;
 
                        for (j = 0; j < MONO_MAX_IREGS; ++j) {
-                               s = 1 << j;
+                               s = regmask (j);
                                if ((clob_mask & s) && !(rs->ifree_mask & s) && (j != ins->sreg1) && (j != dreg) && (j != dreg2)) {
                                        get_register_force_spilling (cfg, tmp, ins, rs->isymbolic [j], FALSE);
                                        mono_regstate_free_int (rs, j);
@@ -1109,7 +1201,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                        dreg = -1;
 
                                for (j = 0; j < MONO_MAX_FREGS; ++j) {
-                                       s = 1 << j;
+                                       s = regmask (j);
                                        if ((clob_mask & s) && !(rs->ffree_mask & s) && (j != ins->sreg1) && (j != dreg)) {
                                                get_register_force_spilling (cfg, tmp, ins, rs->fsymbolic [j], TRUE);
                                                mono_regstate_free_float (rs, j);
@@ -1145,9 +1237,9 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                        hreg = regpair >> 24;
                                        reg = regpair & 0xffffff;
 
-                                       assign_reg (rs, reg, hreg, FALSE);
+                                       assign_reg (cfg, rs, reg, hreg, FALSE);
 
-                                       sreg1_mask &= ~(1 << hreg);
+                                       sreg1_mask &= ~(regmask (hreg));
 
                                        DEBUG (g_print ("\tassigned arg reg %s to R%d\n", mono_arch_regname (hreg), reg));
 
@@ -1166,7 +1258,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                        hreg = regpair >> 24;
                                        reg = regpair & 0xffffff;
 
-                                       assign_reg (rs, reg, hreg, TRUE);
+                                       assign_reg (cfg, rs, reg, hreg, TRUE);
 
                                        DEBUG (g_print ("\tassigned arg reg %s to R%d\n", mono_arch_fregname (hreg), reg));
 
@@ -1188,16 +1280,16 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                /* To simplify things, we allocate the same regpair to sreg1 and dreg */
                                if (dest_sreg1 != -1)
                                        g_assert (dest_sreg1 == ins->dreg);
-                               val = mono_regstate_alloc_int (rs, 1 << ins->dreg);
+                               val = mono_regstate_alloc_int (rs, regmask (ins->dreg));
                                g_assert (val >= 0);
-                               assign_reg (rs, ins->sreg1, val, fp);
+                               assign_reg (cfg, rs, ins->sreg1, val, fp);
 
                                DEBUG (g_print ("\tassigned sreg1-low %s to R%d\n", mono_regname_full (val, fp), ins->sreg1));
 
-                               g_assert ((1 << dreg_high) & regpair_reg2_mask (spec [MONO_INST_SRC1], ins->dreg));
-                               val = mono_regstate_alloc_int (rs, 1 << dreg_high);
+                               g_assert ((regmask (dreg_high)) & regpair_reg2_mask (spec [MONO_INST_SRC1], ins->dreg));
+                               val = mono_regstate_alloc_int (rs, regmask (dreg_high));
                                g_assert (val >= 0);
-                               assign_reg (rs, ins->sreg1 + 1, val, fp);
+                               assign_reg (cfg, rs, ins->sreg1 + 1, val, fp);
 
                                DEBUG (g_print ("\tassigned sreg1-high %s to R%d\n", mono_regname_full (val, fp), ins->sreg1 + 1));
 
@@ -1206,16 +1298,16 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                        }
 
                        if (dest_sreg1 != -1) {
-                               sreg1_mask = 1 << dest_sreg1;
+                               sreg1_mask = regmask (dest_sreg1);
 
-                               if (!(rs->ifree_mask & (1 << dest_sreg1))) {
+                               if (!(rs->ifree_mask & (regmask (dest_sreg1)))) {
                                        DEBUG (g_print ("\tforced spill of R%d\n", rs->isymbolic [dest_sreg1]));
                                        get_register_force_spilling (cfg, tmp, ins, rs->isymbolic [dest_sreg1], FALSE);
                                        mono_regstate_free_int (rs, dest_sreg1);
                                }
                                if (is_global_ireg (ins->sreg1)) {
                                        /* The argument is already in a hard reg, need to copy */
-                                       MonoInst *copy = create_copy_ins (cfg, dest_sreg1, ins->sreg1, NULL, FALSE);
+                                       MonoInst *copy = create_copy_ins (cfg, dest_sreg1, ins->sreg1, NULL, ip, FALSE);
                                        insert_before_ins (ins, tmp, copy);
                                        ins->sreg1 = dest_sreg1;
                                }
@@ -1231,16 +1323,16 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                                spill = -val -1;
                                        }
 
-                                       if (((ins->opcode == OP_MOVE) || (ins->opcode == OP_SETREG)) && !spill && !fp && (!is_global_ireg (ins->dreg) && (rs->ifree_mask & (1 << ins->dreg)))) {
+                                       if (((ins->opcode == OP_MOVE) || (ins->opcode == OP_SETREG)) && !spill && !fp && (!is_global_ireg (ins->dreg) && (rs->ifree_mask & (regmask (ins->dreg))))) {
                                                /* 
                                                 * Allocate the same hreg to sreg1 as well so the 
                                                 * peephole can get rid of the move.
                                                 */
-                                               sreg1_mask = 1 << ins->dreg;
+                                               sreg1_mask = regmask (ins->dreg);
                                        }
 
                                        val = alloc_reg (cfg, tmp, ins, sreg1_mask, ins->sreg1, &reginfo [ins->sreg1], fp);
-                                       assign_reg (rs, ins->sreg1, val, fp);
+                                       assign_reg (cfg, rs, ins->sreg1, val, fp);
                                        DEBUG (g_print ("\tassigned sreg1 %s to R%d\n", mono_regname_full (val, fp), ins->sreg1));
 
                                        if (spill) {
@@ -1261,7 +1353,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                        else {
                                prev_sreg1 = -1;
                        }
-                       sreg2_mask &= ~(1 << ins->sreg1);
+                       sreg2_mask &= ~(regmask (ins->sreg1));
                }
 
                /* Handle the case when sreg1 is a regpair but dreg is not */
@@ -1286,7 +1378,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                        g_assert_not_reached ();
                        }
                        else {
-                               if (! (mask & (1 << val))) {
+                               if (! (mask & (regmask (val)))) {
                                        /* The vreg is already allocated to a wrong hreg */
                                        /* FIXME: */
                                        g_assert_not_reached ();
@@ -1296,7 +1388,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                                val = get_register_spilling (cfg, tmp, ins, mask, reg2, fp);
 
                                        /* Reallocate hreg to the correct register */
-                                       create_copy_ins (cfg, rs->iassign [reg2], val, ins, fp);
+                                       create_copy_ins (cfg, rs->iassign [reg2], val, ins, ip, fp);
 
                                        mono_regstate_free_int (rs, rs->iassign [reg2]);
 #endif
@@ -1305,11 +1397,11 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
 
                        sreg1_high = val;
                        DEBUG (g_print ("\tassigned sreg1 hreg %s to dest R%d\n", mono_arch_regname (val), reg2));
-                       assign_reg (rs, reg2, val, fp);
+                       assign_reg (cfg, rs, reg2, val, fp);
                }
 
                /* Handle dreg==sreg1 */
-               if (((spec [MONO_INST_DEST] == 'f' && spec [MONO_INST_SRC1] == 'f' && !use_fpstack) || spec [MONO_INST_CLOB] == '1') && ins->dreg != ins->sreg1) {
+               if (((dreg_is_fp (ins) && spec [MONO_INST_SRC1] == 'f' && !use_fpstack) || spec [MONO_INST_CLOB] == '1') && ins->dreg != ins->sreg1) {
                        MonoInst *sreg2_copy = NULL;
                        MonoInst *copy;
                        gboolean fp = (spec [MONO_INST_SRC1] == 'f');
@@ -1322,7 +1414,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                int reg2 = alloc_reg (cfg, tmp, ins, dreg_mask, ins->sreg2, NULL, fp);
 
                                DEBUG (g_print ("\tneed to copy sreg2 %s to reg %s\n", mono_regname_full (ins->sreg2, fp), mono_regname_full (reg2, fp)));
-                               sreg2_copy = create_copy_ins (cfg, reg2, ins->sreg2, NULL, fp);
+                               sreg2_copy = create_copy_ins (cfg, reg2, ins->sreg2, NULL, ip, fp);
                                prev_sreg2 = ins->sreg2 = reg2;
 
                                if (fp)
@@ -1346,7 +1438,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                        }
 
                        DEBUG (g_print ("\tneed to copy sreg1 %s to dreg %s\n", mono_regname_full (ins->sreg1, fp), mono_regname_full (ins->dreg, fp)));
-                       copy = create_copy_ins (cfg, ins->dreg, ins->sreg1, NULL, fp);
+                       copy = create_copy_ins (cfg, ins->dreg, ins->sreg1, NULL, ip, fp);
                        insert_before_ins (ins, tmp, copy);
 
                        if (sreg2_copy)
@@ -1356,10 +1448,10 @@ 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 &= ~ (1 << ins->sreg1);
+                       sreg2_mask &= ~ (regmask (ins->sreg1));
                        /* we set sreg1 to dest as well */
                        prev_sreg1 = ins->sreg1 = ins->dreg;
-                       sreg2_mask &= ~ (1 << ins->dreg);
+                       sreg2_mask &= ~ (regmask (ins->dreg));
                }
 
                /*
@@ -1378,7 +1470,7 @@ mono_local_regalloc (MonoCompile *cfg, MonoBasicBlock *bb)
                                        spill = -val -1;
                                }
                                val = alloc_reg (cfg, tmp, ins, sreg2_mask, ins->sreg2, &reginfo [ins->sreg2], fp);
-                               assign_reg (rs, ins->sreg2, val, fp);
+                               assign_reg (cfg, rs, ins->sreg2, val, fp);
                                DEBUG (g_print ("\tassigned sreg2 %s to R%d\n", mono_regname_full (val, fp), ins->sreg2));
                                if (spill)
                                        create_spilled_store (cfg, spill, val, prev_sreg2, ins, fp);