OSDN Git Service

Fix bad regexp
[pf3gnuchains/gcc-fork.git] / gcc / regrename.c
index 3236a43..6463879 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,8 +784,10 @@ 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);
+         if (! constrain_operands (1))
+           fatal_insn_not_found (insn);        
          preprocess_constraints ();
          alt = which_alternative;
          n_ops = recog_data.n_operands;
@@ -819,8 +829,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 +854,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 +940,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];
@@ -998,9 +1050,8 @@ static int kill_autoinc_value PARAMS ((rtx *, void *));
 static void copy_value PARAMS ((rtx, rtx, 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, unsigned int,
-                                           enum machine_mode,
-                                           struct value_data *));
+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,
@@ -1055,6 +1106,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);
@@ -1074,7 +1133,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);
@@ -1138,10 +1197,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);
     }
 }
 
@@ -1179,6 +1239,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
@@ -1195,12 +1256,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;
@@ -1238,19 +1312,38 @@ mode_change_ok (orig_mode, new_mode, regno)
    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 (TEST_HARD_REG_BIT (reg_class_contents[class], i)
        && (vd->e[i].mode == mode
-           || mode_change_ok (vd->e[i].mode, mode, regno)))
-      return gen_rtx_REG (mode, i);
+           || 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;
 }
@@ -1265,7 +1358,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)
@@ -1376,8 +1469,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;
       }
 
@@ -1422,7 +1516,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);
 }
 
@@ -1439,6 +1534,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))
@@ -1451,10 +1547,12 @@ copyprop_hardreg_forward_1 (bb, vd)
 
       set = single_set (insn);
       extract_insn (insn);
-      constrain_operands (1);
+      if (! constrain_operands (1))
+       fatal_insn_not_found (insn);    
       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
@@ -1494,17 +1592,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)
@@ -1519,11 +1626,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",
@@ -1533,6 +1642,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.  */
@@ -1546,6 +1656,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)
@@ -1684,7 +1800,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;