OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / rtlanal.c
index 188fb93..031d103 100644 (file)
@@ -1,5 +1,5 @@
 /* Analyze RTL for C-Compiler
-   Copyright (C) 1987, 88, 91, 92, 93, 1994 Free Software Foundation, Inc.
+   Copyright (C) 1987, 88, 92-97, 1998 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -15,14 +15,21 @@ 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"
+#include "system.h"
 #include "rtl.h"
 
-void note_stores ();
-int reg_set_p ();
+static int rtx_addr_can_trap_p PROTO((rtx));
+static void reg_set_p_1                PROTO((rtx, rtx));
+static void reg_set_last_1     PROTO((rtx, rtx));
+
+
+/* Forward declarations */
+static int jmp_uses_reg_or_mem         PROTO((rtx));
 
 /* Bit flags that specify the machine subtype we are compiling for.
    Bits are tested using macros TARGET_... defined in the tm.h file
@@ -98,12 +105,15 @@ rtx_varies_p (x)
         eliminated the frame and/or arg pointer and are using it
         for pseudos.  */
       return ! (x == frame_pointer_rtx || x == hard_frame_pointer_rtx
-               || x == arg_pointer_rtx);
+               || x == arg_pointer_rtx || x == pic_offset_table_rtx);
 
     case LO_SUM:
       /* The operand 0 of a LO_SUM is considered constant
         (in fact is it related specifically to operand 1).  */
       return rtx_varies_p (XEXP (x, 1));
+      
+    default:
+      break;
     }
 
   fmt = GET_RTX_FORMAT (code);
@@ -116,7 +126,7 @@ rtx_varies_p (x)
 
 /* Return 0 if the use of X as an address in a MEM can cause a trap.  */
 
-int
+static int
 rtx_addr_can_trap_p (x)
      register rtx x;
 {
@@ -148,6 +158,9 @@ rtx_addr_can_trap_p (x)
 
     case LO_SUM:
       return rtx_addr_can_trap_p (XEXP (x, 1));
+      
+    default:
+      break;
     }
 
   /* If it isn't one of the case above, it can cause a trap.  */
@@ -176,8 +189,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
@@ -264,6 +286,9 @@ reg_mentioned_p (reg, in)
     case CONST_DOUBLE:
       /* These are kept unique for a given value.  */
       return 0;
+      
+    default:
+      break;
     }
 
   if (GET_CODE (reg) == code && rtx_equal_p (reg, in))
@@ -354,13 +379,13 @@ reg_referenced_p (x, body)
                         + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)))
          && reg_overlap_mentioned_p (x, SET_DEST (body)))
        return 1;
-      break;
+      return 0;
 
     case ASM_OPERANDS:
       for (i = ASM_OPERANDS_INPUT_LENGTH (body) - 1; i >= 0; i--)
        if (reg_overlap_mentioned_p (x, ASM_OPERANDS_INPUT (body, i)))
          return 1;
-      break;
+      return 0;
 
     case CALL:
     case USE:
@@ -375,15 +400,16 @@ reg_referenced_p (x, body)
       for (i = XVECLEN (body, 0) - 1; i >= 0; i--)
        if (reg_referenced_p (x, XVECEXP (body, 0, i)))
          return 1;
-      break;
+      return 0;
+      
+    default:
+      return 0;
     }
-
-  return 0;
 }
 
 /* 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)
@@ -427,9 +453,10 @@ 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;
+     rtx pat ATTRIBUTE_UNUSED;
 {
   /* We don't want to return 1 if X is a MEM that contains a register
      within REG_SET_REG.  */
@@ -507,6 +534,9 @@ modified_between_p (x, start, end)
 
     case REG:
       return reg_set_between_p (x, start, end);
+      
+    default:
+      break;
     }
 
   fmt = GET_RTX_FORMAT (code);
@@ -559,6 +589,9 @@ modified_in_p (x, insn)
 
     case REG:
       return reg_set_p (x, insn);
+
+    default:
+      break;
     }
 
   fmt = GET_RTX_FORMAT (code);
@@ -739,6 +772,9 @@ refers_to_regno_p (regno, endregno, x, loc)
        return 0;
       x = SET_SRC (x);
       goto repeat;
+
+    default:
+      break;
     }
 
   /* X does not match, so try its subexpressions.  */
@@ -781,7 +817,14 @@ reg_overlap_mentioned_p (x, in)
 {
   int regno, endregno;
 
-  if (GET_CODE (x) == SUBREG)
+  /* Overly conservative.  */
+  if (GET_CODE (x) == STRICT_LOW_PART)
+    x = XEXP (x, 0);
+
+  /* If either argument is a constant, then modifying X can not affect IN.  */
+  if (CONSTANT_P (x) || CONSTANT_P (in))
+    return 0;
+  else if (GET_CODE (x) == SUBREG)
     {
       regno = REGNO (SUBREG_REG (x));
       if (regno < FIRST_PSEUDO_REGISTER)
@@ -789,8 +832,6 @@ reg_overlap_mentioned_p (x, in)
     }
   else if (GET_CODE (x) == REG)
     regno = REGNO (x);
-  else if (CONSTANT_P (x))
-    return 0;
   else if (GET_CODE (x) == MEM)
     {
       char *fmt;
@@ -810,6 +851,18 @@ reg_overlap_mentioned_p (x, in)
   else if (GET_CODE (x) == SCRATCH || GET_CODE (x) == PC
           || GET_CODE (x) == CC0)
     return reg_mentioned_p (x, in);
+  else if (GET_CODE (x) == PARALLEL
+          && GET_MODE (x) == BLKmode)
+    {
+      register int i;
+
+      /* If any register in here refers to it
+        we return true.  */
+      for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
+       if (reg_overlap_mentioned_p (SET_DEST (XVECEXP (x, 0, i)), in))
+         return 1;
+      return 0;
+    }
   else
     abort ();
 
@@ -904,7 +957,7 @@ reg_set_last (x, insn)
                || ((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;
@@ -914,7 +967,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.
@@ -1045,7 +1098,16 @@ note_stores (x, fun)
             || GET_CODE (dest) == SIGN_EXTRACT
             || GET_CODE (dest) == STRICT_LOW_PART)
        dest = XEXP (dest, 0);
-      (*fun) (dest, x);
+
+      if (GET_CODE (dest) == PARALLEL
+         && GET_MODE (dest) == BLKmode)
+       {
+         register int i;
+         for (i = XVECLEN (dest, 0) - 1; i >= 0; i--)
+           (*fun) (SET_DEST (XVECEXP (dest, 0, i)), x);
+       }
+      else
+       (*fun) (dest, x);
     }
   else if (GET_CODE (x) == PARALLEL)
     {
@@ -1064,7 +1126,15 @@ note_stores (x, fun)
                     || GET_CODE (dest) == SIGN_EXTRACT
                     || GET_CODE (dest) == STRICT_LOW_PART)
                dest = XEXP (dest, 0);
-             (*fun) (dest, y);
+             if (GET_CODE (dest) == PARALLEL
+                 && GET_MODE (dest) == BLKmode)
+               {
+                 register int i;
+                 for (i = XVECLEN (dest, 0) - 1; i >= 0; i--)
+                   (*fun) (SET_DEST (XVECEXP (dest, 0, i)), y);
+               }
+             else
+               (*fun) (dest, y);
            }
        }
     }
@@ -1124,10 +1194,12 @@ dead_or_set_regno_p (insn, test_regno)
   int regno, endregno;
   rtx link;
 
-  /* See if there is a death note for something that includes TEST_REGNO.  */
+  /* See if there is a death note for something that includes
+     TEST_REGNO.  */
   for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
     {
-      if (REG_NOTE_KIND (link) != REG_DEAD || GET_CODE (XEXP (link, 0)) != REG)
+      if (REG_NOTE_KIND (link) != REG_DEAD
+         || GET_CODE (XEXP (link, 0)) != REG)
        continue;
 
       regno = REGNO (XEXP (link, 0));
@@ -1150,7 +1222,7 @@ dead_or_set_regno_p (insn, test_regno)
       /* A value is totally replaced if it is the destination or the
         destination is a SUBREG of REGNO that does not change the number of
         words in it.  */
-     if (GET_CODE (dest) == SUBREG
+      if (GET_CODE (dest) == SUBREG
          && (((GET_MODE_SIZE (GET_MODE (dest))
                + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
              == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest)))
@@ -1212,6 +1284,10 @@ find_reg_note (insn, kind, datum)
 {
   register rtx link;
 
+  /* Ignore anything that is not an INSN, JUMP_INSN or CALL_INSN.  */
+  if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+    return 0;
+
   for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
     if (REG_NOTE_KIND (link) == kind
        && (datum == 0 || datum == XEXP (link, 0)))
@@ -1232,6 +1308,10 @@ find_regno_note (insn, kind, regno)
 {
   register rtx link;
 
+  /* Ignore anything that is not an INSN, JUMP_INSN or CALL_INSN.  */
+  if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+    return 0;
+
   for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
     if (REG_NOTE_KIND (link) == kind
        /* Verify that it is a register, so that scratch and MEM won't cause a
@@ -1394,6 +1474,9 @@ volatile_insn_p (x)
     case ASM_OPERANDS:
       if (MEM_VOLATILE_P (x))
        return 1;
+
+    default:
+      break;
     }
 
   /* Recursively scan the operands of this expression.  */
@@ -1457,6 +1540,9 @@ volatile_refs_p (x)
     case ASM_OPERANDS:
       if (MEM_VOLATILE_P (x))
        return 1;
+
+    default:
+      break;
     }
 
   /* Recursively scan the operands of this expression.  */
@@ -1529,6 +1615,9 @@ side_effects_p (x)
     case ASM_OPERANDS:
       if (MEM_VOLATILE_P (x))
        return 1;
+
+    default:
+      break;
     }
 
   /* Recursively scan the operands of this expression.  */
@@ -1597,12 +1686,20 @@ may_trap_p (x)
     case MOD:
     case UDIV:
     case UMOD:
-      if (! CONSTANT_P (XEXP (x, 1)))
+      if (! CONSTANT_P (XEXP (x, 1))
+         || GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
        return 1;
       /* This was const0_rtx, but by not using that,
         we can link this file into other programs.  */
       if (GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) == 0)
        return 1;
+      break;
+
+    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)
@@ -1661,6 +1758,9 @@ inequality_comparisons_p (x)
     case GE:
     case GEU:
       return 1;
+      
+    default:
+      break;
     }
 
   len = GET_RTX_LENGTH (code);
@@ -1685,7 +1785,8 @@ inequality_comparisons_p (x)
   return 0;
 }
 \f
-/* Replace any occurrence of FROM in X with TO.
+/* Replace any occurrence of FROM in X with TO.  The function does
+   not enter into CONST_DOUBLE for the replace.
 
    Note that copying is not done so X must not be shared unless all copies
    are to be modified.  */
@@ -1697,6 +1798,11 @@ replace_rtx (x, from, to)
   register int i, j;
   register char *fmt;
 
+  /* The following prevents loops occurrence when we change MEM in
+     CONST_DOUBLE onto the same CONST_DOUBLE. */
+  if (x != 0 && GET_CODE (x) == CONST_DOUBLE)
+    return x;
+
   if (x == from)
     return to;
 
@@ -1816,6 +1922,9 @@ replace_regs (x, reg_map, nregs, replace_dest)
 
       SET_SRC (x) = replace_regs (SET_SRC (x), reg_map, nregs, 0);
       return x;
+      
+    default:
+      break;
     }
 
   fmt = GET_RTX_FORMAT (code);
@@ -1833,3 +1942,170 @@ replace_regs (x, reg_map, nregs, replace_dest)
     }
   return x;
 }
+
+/* Return 1 if X, the SRC_SRC of  SET of (pc) contain a REG or MEM that is
+   not in the constant pool and not in the condition of an IF_THEN_ELSE.  */
+
+static int
+jmp_uses_reg_or_mem (x)
+     rtx x;
+{
+  enum rtx_code code = GET_CODE (x);
+  int i, j;
+  char *fmt;
+
+  switch (code)
+    {
+    case CONST:
+    case LABEL_REF:
+    case PC:
+      return 0;
+
+    case REG:
+      return 1;
+
+    case MEM:
+      return ! (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
+               && CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)));
+
+    case IF_THEN_ELSE:
+      return (jmp_uses_reg_or_mem (XEXP (x, 1))
+             || jmp_uses_reg_or_mem (XEXP (x, 2)));
+
+    case PLUS:  case MINUS:  case MULT:
+      return (jmp_uses_reg_or_mem (XEXP (x, 0))
+             || jmp_uses_reg_or_mem (XEXP (x, 1)));
+
+    default:
+      break;
+    }
+
+  fmt = GET_RTX_FORMAT (code);
+  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'e'
+         && jmp_uses_reg_or_mem (XEXP (x, i)))
+       return 1;
+
+      if (fmt[i] == 'E')
+       for (j = 0; j < XVECLEN (x, i); j++)
+         if (jmp_uses_reg_or_mem (XVECEXP (x, i, j)))
+           return 1;
+    }
+
+  return 0;
+}
+
+/* Return nonzero if INSN is an indirect jump (aka computed jump).
+
+   Tablejumps and casesi insns are not considered indirect jumps;
+   we can recognize them by a (use (lael_ref)).  */
+
+int
+computed_jump_p (insn)
+     rtx insn;
+{
+  int i;
+  if (GET_CODE (insn) == JUMP_INSN)
+    {
+      rtx pat = PATTERN (insn);
+
+      if (GET_CODE (pat) == PARALLEL)
+       {
+         int len = XVECLEN (pat, 0);
+         int has_use_labelref = 0;
+
+         for (i = len - 1; i >= 0; i--)
+           if (GET_CODE (XVECEXP (pat, 0, i)) == USE
+               && (GET_CODE (XEXP (XVECEXP (pat, 0, i), 0))
+                   == LABEL_REF))
+             has_use_labelref = 1;
+
+         if (! has_use_labelref)
+           for (i = len - 1; i >= 0; i--)
+             if (GET_CODE (XVECEXP (pat, 0, i)) == SET
+                 && SET_DEST (XVECEXP (pat, 0, i)) == pc_rtx
+                 && jmp_uses_reg_or_mem (SET_SRC (XVECEXP (pat, 0, i))))
+               return 1;
+       }
+      else if (GET_CODE (pat) == SET
+              && SET_DEST (pat) == pc_rtx
+              && jmp_uses_reg_or_mem (SET_SRC (pat)))
+       return 1;
+    }
+  return 0;
+}
+
+/* Traverse X via depth-first search, calling F for each
+   sub-expression (including X itself).  F is also passed the DATA.
+   If F returns -1, do not traverse sub-expressions, but continue
+   traversing the rest of the tree.  If F ever returns any other
+   non-zero value, stop the traversal, and return the value returned
+   by F.  Otherwise, return 0.  This function does not traverse inside
+   tree structure that contains RTX_EXPRs, or into sub-expressions
+   whose format code is `0' since it is not known whether or not those
+   codes are actually RTL.
+
+   This routine is very general, and could (should?) be used to
+   implement many of the other routines in this file.  */
+
+int
+for_each_rtx (x, f, data)
+     rtx* x;
+     rtx_function f;
+     void* data;
+{
+  int result;
+  int length;
+  char* format;
+  int i;
+
+  /* Call F on X.  */
+  result = (*f)(x, data);
+  if (result == -1)
+    /* Do not traverse sub-expressions.  */
+    return 0;
+  else if (result != 0)
+    /* Stop the traversal.  */
+    return result;
+
+  if (*x == NULL_RTX)
+    /* There are no sub-expressions.  */
+    return 0;
+
+  length = GET_RTX_LENGTH (GET_CODE (*x));
+  format = GET_RTX_FORMAT (GET_CODE (*x));
+
+  for (i = 0; i < length; ++i) 
+    {
+      switch (format[i]) 
+       {
+       case 'e':
+         result = for_each_rtx (&XEXP (*x, i), f, data);
+         if (result != 0)
+           return result;
+         break;
+
+       case 'V':
+       case 'E':
+         if (XVEC (*x, i) != 0) 
+           {
+             int j;
+             for (j = 0; j < XVECLEN (*x, i); ++j)
+               {
+                 result = for_each_rtx (&XVECEXP (*x, i, j), f, data);
+                 if (result != 0)
+                   return result;
+               }
+           }
+         break; 
+
+       default:
+         /* Nothing to do.  */
+         break;
+       }
+
+    }
+
+  return 0;
+}