OSDN Git Service

(CONST_OK_FOR_LETTER_P): Use (unsigned HOST_WIDE_INT).
[pf3gnuchains/gcc-fork.git] / gcc / rtlanal.c
index 95271f0..3e6b4ae 100644 (file)
@@ -1,5 +1,5 @@
 /* Analyze RTL for C-Compiler
-   Copyright (C) 1987-1991 Free Software Foundation, Inc.
+   Copyright (C) 1987, 88, 9-5, 1996 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -15,7 +15,8 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 
 #include "config.h"
@@ -54,6 +55,7 @@ rtx_unstable_p (x)
 
   if (code == REG)
     return ! (REGNO (x) == FRAME_POINTER_REGNUM
+             || REGNO (x) == HARD_FRAME_POINTER_REGNUM
              || REGNO (x) == ARG_POINTER_REGNUM
              || RTX_UNCHANGING_P (x));
 
@@ -96,7 +98,8 @@ rtx_varies_p (x)
         and arg pointers and not just the register number in case we have
         eliminated the frame and/or arg pointer and are using it
         for pseudos.  */
-      return ! (x == frame_pointer_rtx || x == arg_pointer_rtx);
+      return ! (x == frame_pointer_rtx || x == hard_frame_pointer_rtx
+               || x == arg_pointer_rtx);
 
     case LO_SUM:
       /* The operand 0 of a LO_SUM is considered constant
@@ -132,8 +135,8 @@ rtx_addr_can_trap_p (x)
 
     case REG:
       /* As in rtx_varies_p, we have to use the actual rtx, not reg number.  */
-      return ! (x == frame_pointer_rtx || x == stack_pointer_rtx
-               || x == arg_pointer_rtx);
+      return ! (x == frame_pointer_rtx || x == hard_frame_pointer_rtx
+               || x == stack_pointer_rtx || x == arg_pointer_rtx);
 
     case CONST:
       return rtx_addr_can_trap_p (XEXP (x, 0));
@@ -174,8 +177,17 @@ rtx_addr_varies_p (x)
   fmt = GET_RTX_FORMAT (code);
   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
     if (fmt[i] == 'e')
-      if (rtx_addr_varies_p (XEXP (x, i)))
-       return 1;
+      {
+       if (rtx_addr_varies_p (XEXP (x, i)))
+         return 1;
+      }
+    else if (fmt[i] == 'E')
+      {
+       int j;
+       for (j = 0; j < XVECLEN (x, i); j++)
+         if (rtx_addr_varies_p (XVECEXP (x, i, j)))
+           return 1;
+      }
   return 0;
 }
 \f
@@ -184,7 +196,7 @@ rtx_addr_varies_p (x)
    Only obvious integer terms are detected.
    This is used in cse.c with the `related_value' field.*/
 
-int
+HOST_WIDE_INT
 get_integer_term (x)
      rtx x;
 {
@@ -313,7 +325,10 @@ reg_used_between_p (reg, from_insn, to_insn)
 
   for (insn = NEXT_INSN (from_insn); insn != to_insn; insn = NEXT_INSN (insn))
     if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
-       && reg_overlap_mentioned_p (reg, PATTERN (insn)))
+       && (reg_overlap_mentioned_p (reg, PATTERN (insn))
+          || (GET_CODE (insn) == CALL_INSN
+             && (find_reg_fusage (insn, USE, reg)
+                 || find_reg_fusage (insn, CLOBBER, reg)))))
       return 1;
   return 0;
 }
@@ -364,6 +379,8 @@ reg_referenced_p (x, body)
     case TRAP_IF:
       return reg_overlap_mentioned_p (x, TRAP_CONDITION (body));
 
+    case UNSPEC:
+    case UNSPEC_VOLATILE:
     case PARALLEL:
       for (i = XVECLEN (body, 0) - 1; i >= 0; i--)
        if (reg_referenced_p (x, XVECEXP (body, 0, i)))
@@ -376,7 +393,7 @@ reg_referenced_p (x, body)
 
 /* Nonzero if register REG is referenced in an insn between
    FROM_INSN and TO_INSN (exclusive of those two).  Sets of REG do
-   not count. */
+   not count.  */
 
 int
 reg_referenced_between_p (reg, from_insn, to_insn)
@@ -389,7 +406,9 @@ reg_referenced_between_p (reg, from_insn, to_insn)
 
   for (insn = NEXT_INSN (from_insn); insn != to_insn; insn = NEXT_INSN (insn))
     if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
-       && reg_referenced_p (reg, PATTERN (insn)))
+       && (reg_referenced_p (reg, PATTERN (insn))
+          || (GET_CODE (insn) == CALL_INSN
+             && find_reg_fusage (insn, USE, reg))))
       return 1;
   return 0;
 }
@@ -408,7 +427,7 @@ reg_set_between_p (reg, from_insn, to_insn)
 
   for (insn = NEXT_INSN (from_insn); insn != to_insn; insn = NEXT_INSN (insn))
     if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
-       && reg_set_p (reg, PATTERN (insn)))
+       && reg_set_p (reg, insn))
       return 1;
   return 0;
 }
@@ -418,8 +437,8 @@ reg_set_between_p (reg, from_insn, to_insn)
 static rtx reg_set_reg;
 static int reg_set_flag;
 
-void
-reg_set_p_1 (x)
+static void
+reg_set_p_1 (x, pat)
      rtx x;
 {
   /* We don't want to return 1 if X is a MEM that contains a register
@@ -444,10 +463,14 @@ reg_set_p (reg, insn)
          || (GET_CODE (insn) == CALL_INSN
              /* We'd like to test call_used_regs here, but rtlanal.c can't
                 reference that variable due to its use in genattrtab.  So
-                we'll just be more conservative.  */
+                we'll just be more conservative.
+
+                ??? Unless we could ensure that the CALL_INSN_FUNCTION_USAGE
+                information holds all clobbered registers.  */
              && ((GET_CODE (reg) == REG
                   && REGNO (reg) < FIRST_PSEUDO_REGISTER)
-                 || GET_CODE (reg) == MEM)))
+                 || GET_CODE (reg) == MEM
+                 || find_reg_fusage (insn, CLOBBER, reg))))
        return 1;
 
       body = PATTERN (insn);
@@ -470,7 +493,7 @@ modified_between_p (x, start, end)
 {
   enum rtx_code code = GET_CODE (x);
   char *fmt;
-  int i;
+  int i, j;
 
   switch (code)
     {
@@ -498,10 +521,68 @@ modified_between_p (x, start, end)
 
   fmt = GET_RTX_FORMAT (code);
   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
-    if (fmt[i] == 'e'
-       && modified_between_p (XEXP (x, i), start, end))
+    {
+      if (fmt[i] == 'e' && modified_between_p (XEXP (x, i), start, end))
+       return 1;
+
+      if (fmt[i] == 'E')
+       for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+         if (modified_between_p (XVECEXP (x, i, j), start, end))
+           return 1;
+    }
+
+  return 0;
+}
+
+/* Similar to reg_set_p, but check all registers in X.  Return 0 only if none
+   of them are modified in INSN.  Return 1 if X contains a MEM; this routine
+   does not perform any memory aliasing.  */
+
+int
+modified_in_p (x, insn)
+     rtx x;
+     rtx insn;
+{
+  enum rtx_code code = GET_CODE (x);
+  char *fmt;
+  int i, j;
+
+  switch (code)
+    {
+    case CONST_INT:
+    case CONST_DOUBLE:
+    case CONST:
+    case SYMBOL_REF:
+    case LABEL_REF:
+      return 0;
+
+    case PC:
+    case CC0:
       return 1;
 
+    case MEM:
+      /* If the memory is not constant, assume it is modified.  If it is
+        constant, we still have to check the address.  */
+      if (! RTX_UNCHANGING_P (x))
+       return 1;
+      break;
+
+    case REG:
+      return reg_set_p (x, insn);
+    }
+
+  fmt = GET_RTX_FORMAT (code);
+  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'e' && modified_in_p (XEXP (x, i), insn))
+       return 1;
+
+      if (fmt[i] == 'E')
+       for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+         if (modified_in_p (XVECEXP (x, i, j), insn))
+           return 1;
+    }
+
   return 0;
 }
 \f
@@ -526,9 +607,9 @@ single_set (insn)
     {
       for (i = 0, set = 0; i < XVECLEN (PATTERN (insn), 0); i++)
        if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET
-           && ! (find_reg_note (insn, REG_UNUSED,
-                               SET_DEST (XVECEXP (PATTERN (insn), 0, i)))
-                 || side_effects_p (XVECEXP (PATTERN (insn), 0, i))))
+           && (! find_reg_note (insn, REG_UNUSED,
+                                SET_DEST (XVECEXP (PATTERN (insn), 0, i)))
+               || side_effects_p (XVECEXP (PATTERN (insn), 0, i))))
          {
            if (set)
              return 0;
@@ -559,7 +640,7 @@ find_last_value (x, pinsn, valid_to)
     if (GET_RTX_CLASS (GET_CODE (p)) == 'i')
       {
        rtx set = single_set (p);
-       rtx note = find_reg_note (p, REG_EQUAL, 0);
+       rtx note = find_reg_note (p, REG_EQUAL, NULL_RTX);
 
        if (set && rtx_equal_p (x, SET_DEST (set)))
          {
@@ -616,6 +697,18 @@ refers_to_regno_p (regno, endregno, x, loc)
     {
     case REG:
       i = REGNO (x);
+
+      /* If we modifying the stack, frame, or argument pointer, it will
+        clobber a virtual register.  In fact, we could be more precise,
+        but it isn't worth it.  */
+      if ((i == STACK_POINTER_REGNUM
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+          || i == ARG_POINTER_REGNUM
+#endif
+          || i == FRAME_POINTER_REGNUM)
+         && regno >= FIRST_VIRTUAL_REGISTER && regno <= LAST_VIRTUAL_REGISTER)
+       return 1;
+
       return (endregno > i
              && regno < i + (i < FIRST_PSEUDO_REGISTER 
                              ? HARD_REGNO_NREGS (i, GET_MODE (x))
@@ -733,7 +826,7 @@ reg_overlap_mentioned_p (x, in)
   endregno = regno + (regno < FIRST_PSEUDO_REGISTER
                      ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1);
 
-  return refers_to_regno_p (regno, endregno, in, 0);
+  return refers_to_regno_p (regno, endregno, in, NULL_PTR);
 }
 \f
 /* Used for communications between the next few functions.  */
@@ -777,8 +870,8 @@ reg_set_last_1 (x, pat)
 /* Return the last value to which REG was set prior to INSN.  If we can't
    find it easily, return 0.
 
-   We only return a REG or constant because it is too hard to check if a
-   MEM remains unchanged.  */
+   We only return a REG, SUBREG, or constant because it is too hard to
+   check if a MEM remains unchanged.  */
 
 rtx
 reg_set_last (x, insn)
@@ -803,6 +896,8 @@ reg_set_last (x, insn)
 
      If we find a set of X, ensure that its SET_SRC remains unchanged.  */
 
+  /* We compare with <= here, because reg_set_last_last_regno
+     is actually the number of the first reg *not* in X.  */
   for (;
        insn && GET_CODE (insn) != CODE_LABEL
        && ! (GET_CODE (insn) == CALL_INSN
@@ -816,9 +911,10 @@ reg_set_last (x, insn)
        else if (reg_set_last_value)
          {
            if (CONSTANT_P (reg_set_last_value)
-               || (GET_CODE (reg_set_last_value) == REG
+               || ((GET_CODE (reg_set_last_value) == REG
+                    || GET_CODE (reg_set_last_value) == SUBREG)
                    && ! reg_set_between_p (reg_set_last_value,
-                                           NEXT_INSN (insn), orig_insn)))
+                                           insn, orig_insn)))
              return reg_set_last_value;
            else
              return 0;
@@ -828,7 +924,7 @@ reg_set_last (x, insn)
   return 0;
 }
 \f
-/* This is 1 until after reload pass.  */
+/* This is 1 until after the rtl generation pass.  */
 int rtx_equal_function_value_matters;
 
 /* Return 1 if X and Y are identical-looking rtx's.
@@ -884,6 +980,11 @@ rtx_equal_p (x, y)
     {
       switch (fmt[i])
        {
+       case 'w':
+         if (XWINT (x, i) != XWINT (y, i))
+           return 0;
+         break;
+
        case 'n':
        case 'i':
          if (XINT (x, i) != XINT (y, i))
@@ -1048,6 +1149,10 @@ dead_or_set_regno_p (insn, test_regno)
        return 1;
     }
 
+  if (GET_CODE (insn) == CALL_INSN
+      && find_regno_fusage (insn, CLOBBER, test_regno))
+    return 1;
+
   if (GET_CODE (PATTERN (insn)) == SET)
     {
       rtx dest = SET_DEST (PATTERN (insn));
@@ -1125,7 +1230,9 @@ find_reg_note (insn, kind, datum)
 }
 
 /* Return the reg-note of kind KIND in insn INSN which applies to register
-   number REGNO, if any.  Return 0 if there is no such reg-note.  */
+   number REGNO, if any.  Return 0 if there is no such reg-note.  Note that
+   the REGNO of this NOTE need not be REGNO if REGNO is a hard register;
+   it might be the case that the note overlaps REGNO.  */
 
 rtx
 find_regno_note (insn, kind, regno)
@@ -1140,10 +1247,99 @@ find_regno_note (insn, kind, regno)
        /* Verify that it is a register, so that scratch and MEM won't cause a
           problem here.  */
        && GET_CODE (XEXP (link, 0)) == REG
-       && REGNO (XEXP (link, 0)) == regno)
+       && REGNO (XEXP (link, 0)) <= regno
+       && ((REGNO (XEXP (link, 0))
+            + (REGNO (XEXP (link, 0)) >= FIRST_PSEUDO_REGISTER ? 1
+               : HARD_REGNO_NREGS (REGNO (XEXP (link, 0)),
+                                   GET_MODE (XEXP (link, 0)))))
+           > regno))
       return link;
   return 0;
 }
+
+/* Return true if DATUM, or any overlap of DATUM, of kind CODE is found
+   in the CALL_INSN_FUNCTION_USAGE information of INSN.  */
+
+int
+find_reg_fusage (insn, code, datum)
+     rtx insn;
+     enum rtx_code code;
+     rtx datum;
+{
+  /* If it's not a CALL_INSN, it can't possibly have a
+     CALL_INSN_FUNCTION_USAGE field, so don't bother checking.  */
+  if (GET_CODE (insn) != CALL_INSN)
+    return 0;
+
+  if (! datum)
+    abort();
+
+  if (GET_CODE (datum) != REG)
+    {
+      register rtx link;
+
+      for (link = CALL_INSN_FUNCTION_USAGE (insn);
+           link;
+          link = XEXP (link, 1))
+        if (GET_CODE (XEXP (link, 0)) == code
+           && rtx_equal_p (datum, SET_DEST (XEXP (link, 0))))
+          return 1;
+    }
+  else
+    {
+      register int regno = REGNO (datum);
+
+      /* CALL_INSN_FUNCTION_USAGE information cannot contain references
+        to pseudo registers, so don't bother checking.  */
+
+      if (regno < FIRST_PSEUDO_REGISTER)
+        {
+         int end_regno = regno + HARD_REGNO_NREGS (regno, GET_MODE (datum));
+         int i;
+
+         for (i = regno; i < end_regno; i++)
+           if (find_regno_fusage (insn, code, i))
+             return 1;
+        }
+    }
+
+  return 0;
+}
+
+/* Return true if REGNO, or any overlap of REGNO, of kind CODE is found
+   in the CALL_INSN_FUNCTION_USAGE information of INSN.  */
+
+int
+find_regno_fusage (insn, code, regno)
+     rtx insn;
+     enum rtx_code code;
+     int regno;
+{
+  register rtx link;
+
+  /* CALL_INSN_FUNCTION_USAGE information cannot contain references
+     to pseudo registers, so don't bother checking.  */
+
+  if (regno >= FIRST_PSEUDO_REGISTER
+      || GET_CODE (insn) != CALL_INSN )
+    return 0;
+
+  for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
+   {
+    register int regnote;
+    register rtx op;
+
+    if (GET_CODE (op = XEXP (link, 0)) == code
+       && GET_CODE (SET_DEST (op)) == REG
+       && (regnote = REGNO (SET_DEST (op))) <= regno
+       && regnote
+               + HARD_REGNO_NREGS (regnote, GET_MODE (SET_DEST (op)))
+           > regno)
+      return 1;
+   }
+
+  return 0;
+}
 \f
 /* Remove register note NOTE from the REG_NOTES of INSN.  */
 
@@ -1170,8 +1366,73 @@ remove_note (insn, note)
   abort ();
 }
 \f
+/* Nonzero if X contains any volatile instructions.  These are instructions
+   which may cause unpredictable machine state instructions, and thus no
+   instructions should be moved or combined across them.  This includes
+   only volatile asms and UNSPEC_VOLATILE instructions.  */
+
+int
+volatile_insn_p (x)
+     rtx x;
+{
+  register RTX_CODE code;
+
+  code = GET_CODE (x);
+  switch (code)
+    {
+    case LABEL_REF:
+    case SYMBOL_REF:
+    case CONST_INT:
+    case CONST:
+    case CONST_DOUBLE:
+    case CC0:
+    case PC:
+    case REG:
+    case SCRATCH:
+    case CLOBBER:
+    case ASM_INPUT:
+    case ADDR_VEC:
+    case ADDR_DIFF_VEC:
+    case CALL:
+    case MEM:
+      return 0;
+
+    case UNSPEC_VOLATILE:
+ /* case TRAP_IF: This isn't clear yet.  */
+      return 1;
+
+    case ASM_OPERANDS:
+      if (MEM_VOLATILE_P (x))
+       return 1;
+    }
+
+  /* Recursively scan the operands of this expression.  */
+
+  {
+    register char *fmt = GET_RTX_FORMAT (code);
+    register int i;
+    
+    for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+      {
+       if (fmt[i] == 'e')
+         {
+           if (volatile_insn_p (XEXP (x, i)))
+             return 1;
+         }
+       if (fmt[i] == 'E')
+         {
+           register int j;
+           for (j = 0; j < XVECLEN (x, i); j++)
+             if (volatile_insn_p (XVECEXP (x, i, j)))
+               return 1;
+         }
+      }
+  }
+  return 0;
+}
+
 /* Nonzero if X contains any volatile memory references
-   or volatile ASM_OPERANDS expressions.  */
+   UNSPEC_VOLATILE operations or volatile ASM_OPERANDS expressions.  */
 
 int
 volatile_refs_p (x)
@@ -1198,6 +1459,7 @@ volatile_refs_p (x)
       return 0;
 
     case CALL:
+    case UNSPEC_VOLATILE:
  /* case TRAP_IF: This isn't clear yet.  */
       return 1;
 
@@ -1269,6 +1531,7 @@ side_effects_p (x)
     case POST_INC:
     case POST_DEC:
     case CALL:
+    case UNSPEC_VOLATILE:
  /* case TRAP_IF: This isn't clear yet.  */
       return 1;
 
@@ -1331,6 +1594,7 @@ may_trap_p (x)
       return 0;
 
       /* Conditional trap can trap!  */
+    case UNSPEC_VOLATILE:
     case TRAP_IF:
       return 1;
 
@@ -1349,6 +1613,10 @@ may_trap_p (x)
         we can link this file into other programs.  */
       if (GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) == 0)
        return 1;
+    case EXPR_LIST:
+      /* An EXPR_LIST is used to represent a function call.  This
+        certainly may trap.  */
+      return 1;
     default:
       /* Any floating arithmetic may trap.  */
       if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
@@ -1505,7 +1773,14 @@ replace_regs (x, reg_map, nregs, replace_dest)
     case REG:
       /* Verify that the register has an entry before trying to access it.  */
       if (REGNO (x) < nregs && reg_map[REGNO (x)] != 0)
-       return reg_map[REGNO (x)];
+       {
+         /* SUBREGs can't be shared.  Always return a copy to ensure that if
+            this replacement occurs more than once then each instance will
+            get distinct rtx.  */
+         if (GET_CODE (reg_map[REGNO (x)]) == SUBREG)
+           return copy_rtx (reg_map[REGNO (x)]);
+         return reg_map[REGNO (x)];
+       }
       return x;
 
     case SUBREG: