OSDN Git Service

* gcc.dg/asm-6.c,
[pf3gnuchains/gcc-fork.git] / gcc / regrename.c
index 4683de4..efdd9f9 100644 (file)
@@ -1,5 +1,5 @@
 /* Register renaming for the GNU compiler.
-   Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+   Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
 
    This file is part of GCC.
 
@@ -325,7 +325,12 @@ regrename_optimize ()
              /* See whether it accepts all modes that occur in
                 definition and uses.  */
              for (tmp = this; tmp; tmp = tmp->next_use)
-               if (! HARD_REGNO_MODE_OK (new_reg, GET_MODE (*tmp->loc)))
+               if (! HARD_REGNO_MODE_OK (new_reg, GET_MODE (*tmp->loc))
+                   || (tmp->need_caller_save_reg
+                       && ! (HARD_REGNO_CALL_PART_CLOBBERED
+                             (reg, GET_MODE (*tmp->loc)))
+                       && (HARD_REGNO_CALL_PART_CLOBBERED
+                           (new_reg, GET_MODE (*tmp->loc)))))
                  break;
              if (! tmp)
                {
@@ -611,7 +616,7 @@ scan_rtx_address (insn, loc, class, action, mode)
        if (locI)
          scan_rtx_address (insn, locI, INDEX_REG_CLASS, action, mode);
        if (locB)
-         scan_rtx_address (insn, locB, BASE_REG_CLASS, action, mode);
+         scan_rtx_address (insn, locB, MODE_BASE_REG_CLASS (mode), action, mode);
        return;
       }
 
@@ -629,7 +634,8 @@ scan_rtx_address (insn, loc, class, action, mode)
       break;
 
     case MEM:
-      scan_rtx_address (insn, &XEXP (x, 0), BASE_REG_CLASS, action,
+      scan_rtx_address (insn, &XEXP (x, 0),
+                       MODE_BASE_REG_CLASS (GET_MODE (x)), action,
                        GET_MODE (x));
       return;
 
@@ -672,6 +678,7 @@ scan_rtx (insn, loc, class, action, type, earlyclobber)
     case CONST:
     case CONST_INT:
     case CONST_DOUBLE:
+    case CONST_VECTOR:
     case SYMBOL_REF:
     case LABEL_REF:
     case CC0:
@@ -683,7 +690,8 @@ scan_rtx (insn, loc, class, action, type, earlyclobber)
       return;
 
     case MEM:
-      scan_rtx_address (insn, &XEXP (x, 0), BASE_REG_CLASS, action,
+      scan_rtx_address (insn, &XEXP (x, 0),
+                       MODE_BASE_REG_CLASS (GET_MODE (x)), action,
                        GET_MODE (x));
       return;
 
@@ -756,7 +764,7 @@ build_def_use (bb)
          rtx note;
          rtx old_operands[MAX_RECOG_OPERANDS];
          rtx old_dups[MAX_DUP_OPERANDS];
-         int i;
+         int i, icode;
          int alt;
          int predicated;
 
@@ -776,6 +784,7 @@ build_def_use (bb)
             (6) For any write we find in an operand, make a new chain.
             (7) For any REG_UNUSED, close any chains we just opened.  */
 
+         icode = recog_memoized (insn);
          extract_insn (insn);
          constrain_operands (1);
          preprocess_constraints ();
@@ -819,8 +828,16 @@ build_def_use (bb)
            }
          for (i = 0; i < recog_data.n_dups; i++)
            {
+             int dup_num = recog_data.dup_num[i];
+
              old_dups[i] = *recog_data.dup_loc[i];
              *recog_data.dup_loc[i] = cc0_rtx;
+
+             /* For match_dup of match_operator or match_parallel, share
+                them, so that we don't miss changes in the dup.  */
+             if (icode >= 0
+                 && insn_data[icode].operand[dup_num].eliminable == 0)
+               old_dups[i] = recog_data.operand[dup_num];
            }
 
          scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_all_read,
@@ -836,6 +853,21 @@ build_def_use (bb)
            scan_rtx (insn, &CALL_INSN_FUNCTION_USAGE (insn),
                      NO_REGS, terminate_all_read, OP_IN, 0);
 
+         /* Step 2C: Can't rename asm operands that were originally
+            hard registers.  */
+         if (asm_noperands (PATTERN (insn)) > 0)
+           for (i = 0; i < n_ops; i++)
+             {
+               rtx *loc = recog_data.operand_loc[i];
+               rtx op = *loc;
+
+               if (GET_CODE (op) == REG
+                   && REGNO (op) == ORIGINAL_REGNO (op)
+                   && (recog_data.operand_type[i] == OP_IN
+                       || recog_data.operand_type[i] == OP_INOUT))
+                 scan_rtx (insn, loc, NO_REGS, terminate_all_read, OP_IN, 0);
+             }
+
          /* Step 3: Append to chains for reads inside operands.  */
          for (i = 0; i < n_ops + recog_data.n_dups; i++)
            {
@@ -907,8 +939,27 @@ build_def_use (bb)
          /* Step 6: Begin new chains for writes inside operands.  */
          /* ??? Many targets have output constraints on the SET_DEST
             of a call insn, which is stupid, since these are certainly
-            ABI defined hard registers.  Don't change calls at all.  */
-         if (GET_CODE (insn) != CALL_INSN)
+            ABI defined hard registers.  Don't change calls at all.
+            Similarly take special care for asm statement that originally
+            referenced hard registers.  */
+         if (asm_noperands (PATTERN (insn)) > 0)
+           {
+             for (i = 0; i < n_ops; i++)
+               if (recog_data.operand_type[i] == OP_OUT)
+                 {
+                   rtx *loc = recog_data.operand_loc[i];
+                   rtx op = *loc;
+                   enum reg_class class = recog_op_alt[i][alt].class;
+
+                   if (GET_CODE (op) == REG
+                       && REGNO (op) == ORIGINAL_REGNO (op))
+                     continue;
+
+                   scan_rtx (insn, loc, class, mark_write, OP_OUT,
+                             recog_op_alt[i][alt].earlyclobber);
+                 }
+           }
+         else if (GET_CODE (insn) != CALL_INSN)
            for (i = 0; i < n_ops + recog_data.n_dups; i++)
              {
                int opn = i < n_ops ? i : recog_data.dup_num[i - n_ops];
@@ -996,9 +1047,10 @@ static void kill_clobbered_value PARAMS ((rtx, rtx, void *));
 static void kill_set_value PARAMS ((rtx, rtx, void *));
 static int kill_autoinc_value PARAMS ((rtx *, void *));
 static void copy_value PARAMS ((rtx, rtx, struct value_data *));
-static rtx find_oldest_value_reg PARAMS ((enum reg_class, unsigned int,
-                                           enum machine_mode,
-                                           struct value_data *));
+static bool mode_change_ok PARAMS ((enum machine_mode, enum machine_mode,
+                                   unsigned int));
+static rtx find_oldest_value_reg PARAMS ((enum reg_class, rtx,
+                                         struct value_data *));
 static bool replace_oldest_value_reg PARAMS ((rtx *, enum reg_class, rtx,
                                              struct value_data *));
 static bool replace_oldest_value_addr PARAMS ((rtx *, enum reg_class,
@@ -1053,6 +1105,14 @@ kill_value (x, vd)
      rtx x;
      struct value_data *vd;
 {
+  /* SUBREGS are supposed to have been eliminated by now.  But some
+     ports, e.g. i386 sse, use them to smuggle vector type information
+     through to instruction selection.  Each such SUBREG should simplify,
+     so if we get a NULL  we've done something wrong elsewhere. */
+
+  if (GET_CODE (x) == SUBREG)
+    x = simplify_subreg (GET_MODE (x), SUBREG_REG (x),
+                        GET_MODE (SUBREG_REG (x)), SUBREG_BYTE (x));
   if (REG_P (x))
     {
       unsigned int regno = REGNO (x);
@@ -1072,7 +1132,7 @@ kill_value (x, vd)
        {
          if (vd->e[j].mode == VOIDmode)
            continue;
-         n = HARD_REGNO_NREGS (regno, vd->e[j].mode);
+         n = HARD_REGNO_NREGS (j, vd->e[j].mode);
          if (j + n > regno)
            for (i = 0; i < n; ++i)
              kill_value_regno (j + i, vd);
@@ -1136,10 +1196,11 @@ kill_set_value (x, set, data)
      void *data;
 {
   struct value_data *vd = data;
-  if (GET_CODE (set) != CLOBBER && REG_P (x))
+  if (GET_CODE (set) != CLOBBER)
     {
       kill_value (x, vd);
-      set_value_regno (REGNO (x), GET_MODE (x), vd);
+      if (REG_P (x))
+        set_value_regno (REGNO (x), GET_MODE (x), vd);
     }
 }
 
@@ -1177,6 +1238,7 @@ copy_value (dest, src, vd)
 {
   unsigned int dr = REGNO (dest);
   unsigned int sr = REGNO (src);
+  unsigned int dn, sn;
   unsigned int i;
 
   /* ??? At present, it's possible to see noop sets.  It'd be nice if
@@ -1193,12 +1255,25 @@ copy_value (dest, src, vd)
   if (frame_pointer_needed && dr == HARD_FRAME_POINTER_REGNUM)
     return;
 
+  /* If SRC and DEST overlap, don't record anything.  */
+  dn = HARD_REGNO_NREGS (dr, GET_MODE (dest));
+  sn = HARD_REGNO_NREGS (sr, GET_MODE (dest));
+  if ((dr > sr && dr < sr + sn)
+      || (sr > dr && sr < dr + dn))
+    return;
+
   /* If SRC had no assigned mode (i.e. we didn't know it was live)
      assign it now and assume the value came from an input argument
      or somesuch.  */
   if (vd->e[sr].mode == VOIDmode)
     set_value_regno (sr, vd->e[dr].mode, vd);
 
+  /* If SRC had been assigned a mode narrower than the copy, we can't
+     link DEST into the chain, because not all of the pieces of the
+     copy came from oldest_regno.  */
+  else if (sn > (unsigned int) HARD_REGNO_NREGS (sr, vd->e[sr].mode))
+    return;
+
   /* Link DR at the end of the value chain used by SR.  */
 
   vd->e[dr].oldest_regno = vd->e[sr].oldest_regno;
@@ -1212,23 +1287,62 @@ copy_value (dest, src, vd)
 #endif
 }
 
+/* Return true if a mode change from ORIG to NEW is allowed for REGNO.  */
+
+static bool
+mode_change_ok (orig_mode, new_mode, regno)
+     enum machine_mode orig_mode, new_mode;
+     unsigned int regno ATTRIBUTE_UNUSED;
+{
+  if (GET_MODE_SIZE (orig_mode) < GET_MODE_SIZE (new_mode))
+    return false;
+
+#ifdef CLASS_CANNOT_CHANGE_MODE
+  if (TEST_HARD_REG_BIT (reg_class_contents[CLASS_CANNOT_CHANGE_MODE], regno)
+      && CLASS_CANNOT_CHANGE_MODE_P (orig_mode, new_mode))
+    return false;
+#endif
+
+  return true;
+}
+
 /* Find the oldest copy of the value contained in REGNO that is in
    register class CLASS and has mode MODE.  If found, return an rtx
    of that oldest register, otherwise return NULL.  */
 
 static rtx
-find_oldest_value_reg (class, regno, mode, vd)
+find_oldest_value_reg (class, reg, vd)
      enum reg_class class;
-     unsigned int regno;
-     enum machine_mode mode;
+     rtx reg;
      struct value_data *vd;
 {
+  unsigned int regno = REGNO (reg);
+  enum machine_mode mode = GET_MODE (reg);
   unsigned int i;
 
+  /* If we are accessing REG in some mode other that what we set it in,
+     make sure that the replacement is valid.  In particular, consider
+       (set (reg:DI r11) (...))
+       (set (reg:SI r9) (reg:SI r11))
+       (set (reg:SI r10) (...))
+       (set (...) (reg:DI r9))
+     Replacing r9 with r11 is invalid.  */
+  if (mode != vd->e[regno].mode)
+    {
+      if (HARD_REGNO_NREGS (regno, mode)
+         > HARD_REGNO_NREGS (regno, vd->e[regno].mode))
+       return NULL_RTX;
+    }
+
   for (i = vd->e[regno].oldest_regno; i != regno; i = vd->e[i].next_regno)
-    if (vd->e[i].mode == mode
-       && TEST_HARD_REG_BIT (reg_class_contents[class], i))
-      return gen_rtx_REG (mode, i);
+    if (TEST_HARD_REG_BIT (reg_class_contents[class], i)
+       && (vd->e[i].mode == mode
+           || mode_change_ok (vd->e[i].mode, mode, i)))
+      {
+       rtx new = gen_rtx_raw_REG (mode, i);
+       ORIGINAL_REGNO (new) = ORIGINAL_REGNO (reg);
+       return new;
+      }
 
   return NULL_RTX;
 }
@@ -1243,7 +1357,7 @@ replace_oldest_value_reg (loc, class, insn, vd)
      rtx insn;
      struct value_data *vd;
 {
-  rtx new = find_oldest_value_reg (class, REGNO (*loc), GET_MODE (*loc), vd);
+  rtx new = find_oldest_value_reg (class, *loc, vd);
   if (new)
     {
       if (rtl_dump_file)
@@ -1354,8 +1468,9 @@ replace_oldest_value_addr (loc, class, mode, insn, vd)
          changed |= replace_oldest_value_addr (locI, INDEX_REG_CLASS, mode,
                                                insn, vd);
        if (locB)
-         changed |= replace_oldest_value_addr (locB, BASE_REG_CLASS, mode,
-                                               insn, vd);
+         changed |= replace_oldest_value_addr (locB,
+                                               MODE_BASE_REG_CLASS (mode),
+                                               mode, insn, vd);
        return changed;
       }
 
@@ -1400,7 +1515,8 @@ replace_oldest_value_mem (x, insn, vd)
      rtx insn;
      struct value_data *vd;
 {
-  return replace_oldest_value_addr (&XEXP (x, 0), BASE_REG_CLASS,
+  return replace_oldest_value_addr (&XEXP (x, 0),
+                                   MODE_BASE_REG_CLASS (GET_MODE (x)),
                                    GET_MODE (x), insn, vd);
 }
 
@@ -1417,6 +1533,7 @@ copyprop_hardreg_forward_1 (bb, vd)
   for (insn = bb->head; ; insn = NEXT_INSN (insn))
     {
       int n_ops, i, alt, predicated;
+      bool is_asm;
       rtx set;
 
       if (! INSN_P (insn))
@@ -1433,6 +1550,7 @@ copyprop_hardreg_forward_1 (bb, vd)
       preprocess_constraints ();
       alt = which_alternative;
       n_ops = recog_data.n_operands;
+      is_asm = asm_noperands (PATTERN (insn)) >= 0;
 
       /* Simplify the code below by rewriting things to reflect
         matching constraints.  Also promote OP_OUT to OP_INOUT
@@ -1472,17 +1590,26 @@ copyprop_hardreg_forward_1 (bb, vd)
         be able to do the move from a different register class.  */
       if (set && REG_P (SET_SRC (set)))
        {
-         unsigned int regno = REGNO (SET_SRC (set));
-         enum machine_mode mode = GET_MODE (SET_SRC (set));
+         rtx src = SET_SRC (set);
+         unsigned int regno = REGNO (src);
+         enum machine_mode mode = GET_MODE (src);
          unsigned int i;
          rtx new;
 
+         /* If we are accessing SRC in some mode other that what we
+            set it in, make sure that the replacement is valid.  */
+         if (mode != vd->e[regno].mode)
+           {
+             if (HARD_REGNO_NREGS (regno, mode)
+                 > HARD_REGNO_NREGS (regno, vd->e[regno].mode))
+               goto no_move_special_case;
+           }
+
          /* If the destination is also a register, try to find a source
             register in the same class.  */
          if (REG_P (SET_DEST (set)))
            {
-             new = find_oldest_value_reg (REGNO_REG_CLASS (regno),
-                                          regno, mode, vd);
+             new = find_oldest_value_reg (REGNO_REG_CLASS (regno), src, vd);
              if (new && validate_change (insn, &SET_SRC (set), new, 0))
                {
                  if (rtl_dump_file)
@@ -1497,11 +1624,13 @@ copyprop_hardreg_forward_1 (bb, vd)
          /* Otherwise, try all valid registers and see if its valid.  */
          for (i = vd->e[regno].oldest_regno; i != regno;
               i = vd->e[i].next_regno)
-           if (mode == vd->e[regno].mode)
+           if (vd->e[i].mode == mode
+               || mode_change_ok (vd->e[i].mode, mode, i))
              {
-               new = gen_rtx_REG (mode, i);
+               new = gen_rtx_raw_REG (mode, i);
                if (validate_change (insn, &SET_SRC (set), new, 0))
                  {
+                   ORIGINAL_REGNO (new) = ORIGINAL_REGNO (src);
                    if (rtl_dump_file)
                      fprintf (rtl_dump_file,
                               "insn %u: replaced reg %u with %u\n",
@@ -1511,6 +1640,7 @@ copyprop_hardreg_forward_1 (bb, vd)
                  }
              }
        }
+      no_move_special_case:
 
       /* For each input operand, replace a hard register with the
         eldest live copy that's in an appropriate register class.  */
@@ -1524,6 +1654,12 @@ copyprop_hardreg_forward_1 (bb, vd)
          if (recog_data.constraints[i][0] == '\0')
            continue;
 
+         /* Don't replace in asms intentionally referencing hard regs.  */
+         if (is_asm && GET_CODE (recog_data.operand[i]) == REG
+             && (REGNO (recog_data.operand[i])
+                 == ORIGINAL_REGNO (recog_data.operand[i])))
+           continue;
+
          if (recog_data.operand_type[i] == OP_IN)
            {
              if (recog_op_alt[i][alt].is_address)
@@ -1604,6 +1740,7 @@ copyprop_hardreg_forward ()
       /* ??? Ought to use more intelligent queueing of blocks.  */
       if (bb->pred
          && ! bb->pred->pred_next 
+         && ! (bb->pred->flags & (EDGE_ABNORMAL_CALL | EDGE_EH))
          && bb->pred->src->index != ENTRY_BLOCK
          && bb->pred->src->index < b)
        all_vd[b] = all_vd[bb->pred->src->index];
@@ -1661,7 +1798,7 @@ debug_value_data (vd)
             j != INVALID_REGNUM;
             j = vd->e[j].next_regno)
          {
-           if (TEST_HARD_REG_BIT (set, vd->e[j].next_regno))
+           if (TEST_HARD_REG_BIT (set, j))
              {
                fprintf (stderr, "[%u] Loop in regno chain\n", j);
                return;