OSDN Git Service

* config/s390/s390.c (s390_fixup_clobbered_return_reg):
[pf3gnuchains/gcc-fork.git] / gcc / rtlanal.c
index c1fdb76..f7923c2 100644 (file)
@@ -22,6 +22,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 
 #include "config.h"
 #include "system.h"
+#include "coretypes.h"
+#include "tm.h"
 #include "toplev.h"
 #include "rtl.h"
 #include "hard-reg-set.h"
@@ -30,11 +32,13 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "tm_p.h"
 #include "flags.h"
 #include "basic-block.h"
+#include "real.h"
 
 /* Forward declarations */
 static int global_reg_mentioned_p_1 PARAMS ((rtx *, void *));
 static void set_of_1           PARAMS ((rtx, rtx, void *));
 static void insn_dependent_p_1 PARAMS ((rtx, rtx, void *));
+static int rtx_referenced_p_1  PARAMS ((rtx *, void *));
 static int computed_jump_p_1   PARAMS ((rtx));
 static void parms_set          PARAMS ((rtx, rtx, void *));
 static bool hoist_test_store           PARAMS ((rtx, rtx, regset));
@@ -152,6 +156,10 @@ rtx_varies_p (x, for_alias)
     case LABEL_REF:
       return 0;
 
+    case ADDRESSOF:
+      /* This will resolve to some offset from the frame pointer.  */
+      return 0;
+
     case REG:
       /* Note that we have to test for the actual rtx used for the frame
         and arg pointers and not just the register number in case we have
@@ -224,6 +232,10 @@ rtx_addr_can_trap_p (x)
     case LABEL_REF:
       return 0;
 
+    case ADDRESSOF:
+      /* This will resolve to some offset from the frame pointer.  */
+      return 0;
+
     case REG:
       /* As in rtx_varies_p, we have to use the actual rtx, not reg number.  */
       if (x == frame_pointer_rtx || x == hard_frame_pointer_rtx
@@ -268,6 +280,90 @@ rtx_addr_can_trap_p (x)
   return 1;
 }
 
+/* Return true if X is an address that is known to not be zero.  */
+
+bool
+nonzero_address_p (x)
+     rtx x;
+{
+  enum rtx_code code = GET_CODE (x);
+
+  switch (code)
+    {
+    case SYMBOL_REF:
+      return !SYMBOL_REF_WEAK (x);
+
+    case LABEL_REF:
+      return true;
+
+    case ADDRESSOF:
+      /* This will resolve to some offset from the frame pointer.  */
+      return true;
+
+    case REG:
+      /* As in rtx_varies_p, we have to use the actual rtx, not reg number.  */
+      if (x == frame_pointer_rtx || x == hard_frame_pointer_rtx
+         || x == stack_pointer_rtx
+         || (x == arg_pointer_rtx && fixed_regs[ARG_POINTER_REGNUM]))
+       return true;
+      /* All of the virtual frame registers are stack references.  */
+      if (REGNO (x) >= FIRST_VIRTUAL_REGISTER
+         && REGNO (x) <= LAST_VIRTUAL_REGISTER)
+       return true;
+      return false;
+
+    case CONST:
+      return nonzero_address_p (XEXP (x, 0));
+
+    case PLUS:
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+       {
+         /* Pointers aren't allowed to wrap.  If we've got a register
+            that is known to be a pointer, and a positive offset, then
+            the composite can't be zero.  */
+         if (INTVAL (XEXP (x, 1)) > 0
+             && REG_P (XEXP (x, 0))
+             && REG_POINTER (XEXP (x, 0)))
+           return true;
+
+         return nonzero_address_p (XEXP (x, 0));
+       }
+      /* Handle PIC references.  */
+      else if (XEXP (x, 0) == pic_offset_table_rtx
+              && CONSTANT_P (XEXP (x, 1)))
+       return true;
+      return false;
+
+    case PRE_MODIFY:
+      /* Similar to the above; allow positive offsets.  Further, since
+        auto-inc is only allowed in memories, the register must be a
+        pointer.  */
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT
+         && INTVAL (XEXP (x, 1)) > 0)
+       return true;
+      return nonzero_address_p (XEXP (x, 0));
+
+    case PRE_INC:
+      /* Similarly.  Further, the offset is always positive.  */
+      return true;
+
+    case PRE_DEC:
+    case POST_DEC:
+    case POST_INC:
+    case POST_MODIFY:
+      return nonzero_address_p (XEXP (x, 0));
+
+    case LO_SUM:
+      return nonzero_address_p (XEXP (x, 1));
+
+    default:
+      break;
+    }
+
+  /* If it isn't one of the case above, might be zero.  */
+  return false;
+}
+
 /* Return 1 if X refers to a memory location whose address
    cannot be compared reliably with constant addresses,
    or if X refers to a BLKmode memory object.
@@ -352,7 +448,7 @@ get_related_value (x)
    into the jump table.  If the offset cannot be determined, then return
    NULL_RTX.
 
-   If EARLIEST is non-zero, it is a pointer to a place where the earliest
+   If EARLIEST is nonzero, it is a pointer to a place where the earliest
    insn used in locating the offset was found.  */
 
 rtx
@@ -370,13 +466,7 @@ get_jump_table_offset (insn, earliest)
   rtx old_y;
   int i;
 
-  if (GET_CODE (insn) != JUMP_INSN
-      || ! (label = JUMP_LABEL (insn))
-      || ! (table = NEXT_INSN (label))
-      || GET_CODE (table) != JUMP_INSN
-      || (GET_CODE (PATTERN (table)) != ADDR_VEC
-         && GET_CODE (PATTERN (table)) != ADDR_DIFF_VEC)
-      || ! (set = single_set (insn)))
+  if (!tablejump_p (insn, &label, &table) || !(set = single_set (insn)))
     return NULL_RTX;
 
   x = SET_SRC (set);
@@ -540,7 +630,7 @@ global_reg_mentioned_p_1 (loc, data)
   return 0;
 }
 
-/* Returns non-zero if X mentions a global register.  */
+/* Returns nonzero if X mentions a global register.  */
 
 int
 global_reg_mentioned_p (x)
@@ -873,13 +963,10 @@ int
 reg_set_p (reg, insn)
      rtx reg, insn;
 {
-  rtx body = insn;
-
   /* We can be passed an insn or part of one.  If we are passed an insn,
      check if a side-effect of the insn clobbers REG.  */
-  if (INSN_P (insn))
-    {
-      if (FIND_REG_INC_NOTE (insn, reg)
+  if (INSN_P (insn)
+      && (FIND_REG_INC_NOTE (insn, reg)
          || (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
@@ -890,11 +977,8 @@ reg_set_p (reg, insn)
              && ((GET_CODE (reg) == REG
                   && REGNO (reg) < FIRST_PSEUDO_REGISTER)
                  || GET_CODE (reg) == MEM
-                 || find_reg_fusage (insn, CLOBBER, reg))))
-       return 1;
-
-      body = PATTERN (insn);
-    }
+                 || find_reg_fusage (insn, CLOBBER, reg)))))
+    return 1;
 
   return set_of (reg, insn) != NULL_RTX;
 }
@@ -948,7 +1032,7 @@ regs_set_between_p (x, start, end)
 
 /* Similar to reg_set_between_p, but check all registers in X.  Return 0
    only if none of them are modified between START and END.  Return 1 if
-   X contains a MEM; this routine does not perform any memory aliasing.  */
+   X contains a MEM; this routine does usememory aliasing.  */
 
 int
 modified_between_p (x, start, end)
@@ -958,6 +1042,10 @@ modified_between_p (x, start, end)
   enum rtx_code code = GET_CODE (x);
   const char *fmt;
   int i, j;
+  rtx insn;
+
+  if (start == end)
+    return 0;
 
   switch (code)
     {
@@ -974,10 +1062,14 @@ modified_between_p (x, start, end)
       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))
+      if (RTX_UNCHANGING_P (x))
+       return 0;
+      if (modified_between_p (XEXP (x, 0), start, end))
        return 1;
+      for (insn = NEXT_INSN (start); insn != end; insn = NEXT_INSN (insn))
+       if (memory_modified_in_insn_p (x, insn))
+         return 1;
+      return 0;
       break;
 
     case REG:
@@ -1004,7 +1096,7 @@ modified_between_p (x, start, end)
 
 /* 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.  */
+   does use memory aliasing.  */
 
 int
 modified_in_p (x, insn)
@@ -1030,10 +1122,13 @@ modified_in_p (x, insn)
       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))
+      if (RTX_UNCHANGING_P (x))
+       return 0;
+      if (modified_in_p (XEXP (x, 0), insn))
+       return 1;
+      if (memory_modified_in_insn_p (x, insn))
        return 1;
+      return 0;
       break;
 
     case REG:
@@ -1226,19 +1321,17 @@ set_noop_p (set)
   rtx src = SET_SRC (set);
   rtx dst = SET_DEST (set);
 
-  if (side_effects_p (src) || side_effects_p (dst))
-    return 0;
-
-  if (GET_CODE (dst) == MEM && GET_CODE (src) == MEM)
-    return rtx_equal_p (dst, src);
-
   if (dst == pc_rtx && src == pc_rtx)
     return 1;
 
+  if (GET_CODE (dst) == MEM && GET_CODE (src) == MEM)
+    return rtx_equal_p (dst, src) && !side_effects_p (dst);
+
   if (GET_CODE (dst) == SIGN_EXTRACT
       || GET_CODE (dst) == ZERO_EXTRACT)
     return rtx_equal_p (XEXP (dst, 0), src)
-          && ! BYTES_BIG_ENDIAN && XEXP (dst, 2) == const0_rtx;
+          && ! BYTES_BIG_ENDIAN && XEXP (dst, 2) == const0_rtx
+          && !side_effects_p (src);
 
   if (GET_CODE (dst) == STRICT_LOW_PART)
     dst = XEXP (dst, 0);
@@ -1458,7 +1551,7 @@ refers_to_regno_p (regno, endregno, x, loc)
       else if (fmt[i] == 'E')
        {
          int j;
-         for (j = XVECLEN (x, i) - 1; j >=0; j--)
+         for (j = XVECLEN (x, i) - 1; j >= 0; j--)
            if (loc != &XVECEXP (x, i, j)
                && refers_to_regno_p (regno, endregno, XVECEXP (x, i, j), loc))
              return 1;
@@ -1797,7 +1890,7 @@ dead_or_set_regno_p (insn, test_regno)
 
   if (GET_CODE (pattern) == SET)
     {
-      rtx dest = SET_DEST (PATTERN (insn));
+      rtx dest = SET_DEST (pattern);
 
       /* 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
@@ -1917,14 +2010,19 @@ rtx
 find_reg_equal_equiv_note (insn)
      rtx insn;
 {
-  rtx note;
+  rtx link;
 
-  if (single_set (insn) == 0)
+  if (!INSN_P (insn))
     return 0;
-  else if ((note = find_reg_note (insn, REG_EQUIV, NULL_RTX)) != 0)
-    return note;
-  else
-    return find_reg_note (insn, REG_EQUAL, NULL_RTX);
+  for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+    if (REG_NOTE_KIND (link) == REG_EQUAL
+       || REG_NOTE_KIND (link) == REG_EQUIV)
+      {
+       if (single_set (insn) == 0)
+         return 0;
+       return link;
+      }
+  return NULL;
 }
 
 /* Return true if DATUM, or any overlap of DATUM, of kind CODE is found
@@ -2137,7 +2235,6 @@ volatile_insn_p (x)
     case REG:
     case SCRATCH:
     case CLOBBER:
-    case ASM_INPUT:
     case ADDR_VEC:
     case ADDR_DIFF_VEC:
     case CALL:
@@ -2148,6 +2245,7 @@ volatile_insn_p (x)
  /* case TRAP_IF: This isn't clear yet.  */
       return 1;
 
+    case ASM_INPUT:
     case ASM_OPERANDS:
       if (MEM_VOLATILE_P (x))
        return 1;
@@ -2204,7 +2302,6 @@ volatile_refs_p (x)
     case REG:
     case SCRATCH:
     case CLOBBER:
-    case ASM_INPUT:
     case ADDR_VEC:
     case ADDR_DIFF_VEC:
       return 0;
@@ -2213,6 +2310,7 @@ volatile_refs_p (x)
       return 1;
 
     case MEM:
+    case ASM_INPUT:
     case ASM_OPERANDS:
       if (MEM_VOLATILE_P (x))
        return 1;
@@ -2268,7 +2366,6 @@ side_effects_p (x)
     case PC:
     case REG:
     case SCRATCH:
-    case ASM_INPUT:
     case ADDR_VEC:
     case ADDR_DIFF_VEC:
       return 0;
@@ -2291,6 +2388,7 @@ side_effects_p (x)
       return 1;
 
     case MEM:
+    case ASM_INPUT:
     case ASM_OPERANDS:
       if (MEM_VOLATILE_P (x))
        return 1;
@@ -2369,6 +2467,8 @@ may_trap_p (x)
     case MOD:
     case UDIV:
     case UMOD:
+      if (HONOR_SNANS (GET_MODE (x)))
+       return 1;
       if (! CONSTANT_P (XEXP (x, 1))
          || (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT
              && flag_trapping_math))
@@ -2396,12 +2496,28 @@ may_trap_p (x)
         when COMPARE is used, though many targets do make this distinction.
         For instance, sparc uses CCFPE for compares which generate exceptions
         and CCFP for compares which do not generate exceptions.  */
-      if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+      if (HONOR_NANS (GET_MODE (x)))
        return 1;
       /* But often the compare has some CC mode, so check operand
         modes as well.  */
-      if (GET_MODE_CLASS (GET_MODE (XEXP (x, 0))) == MODE_FLOAT
-         || GET_MODE_CLASS (GET_MODE (XEXP (x, 1))) == MODE_FLOAT)
+      if (HONOR_NANS (GET_MODE (XEXP (x, 0)))
+         || HONOR_NANS (GET_MODE (XEXP (x, 1))))
+       return 1;
+      break;
+
+    case EQ:
+    case NE:
+      if (HONOR_SNANS (GET_MODE (x)))
+       return 1;
+      /* Often comparison is CC mode, so check operand modes.  */
+      if (HONOR_SNANS (GET_MODE (XEXP (x, 0)))
+         || HONOR_SNANS (GET_MODE (XEXP (x, 1))))
+       return 1;
+      break;
+
+    case FIX:
+      /* Conversion of floating point might trap.  */
+      if (flag_trapping_math && HONOR_NANS (GET_MODE (XEXP (x, 0))))
        return 1;
       break;
 
@@ -2673,6 +2789,137 @@ replace_regs (x, reg_map, nregs, replace_dest)
   return x;
 }
 
+/* Replace occurrences of the old label in *X with the new one.
+   DATA is a REPLACE_LABEL_DATA containing the old and new labels.  */
+
+int
+replace_label (x, data)
+     rtx *x;
+     void *data;
+{
+  rtx l = *x;
+  rtx tmp;
+  rtx old_label = ((replace_label_data *) data)->r1;
+  rtx new_label = ((replace_label_data *) data)->r2;
+  bool update_label_nuses = ((replace_label_data *) data)->update_label_nuses;
+
+  if (l == NULL_RTX)
+    return 0;
+
+  if (GET_CODE (l) == MEM
+      && (tmp = XEXP (l, 0)) != NULL_RTX
+      && GET_CODE (tmp) == SYMBOL_REF
+      && CONSTANT_POOL_ADDRESS_P (tmp))
+    {
+      rtx c = get_pool_constant (tmp);
+      if (rtx_referenced_p (old_label, c))
+       {
+         rtx new_c, new_l;
+         replace_label_data *d = (replace_label_data *) data;
+         
+         /* Create a copy of constant C; replace the label inside
+            but do not update LABEL_NUSES because uses in constant pool
+            are not counted.  */
+         new_c = copy_rtx (c);
+         d->update_label_nuses = false;
+         for_each_rtx (&new_c, replace_label, data);
+         d->update_label_nuses = update_label_nuses;
+
+         /* Add the new constant NEW_C to constant pool and replace
+            the old reference to constant by new reference.  */
+         new_l = force_const_mem (get_pool_mode (tmp), new_c);
+         *x = replace_rtx (l, l, new_l);
+       }
+      return 0;
+    }
+
+  /* If this is a JUMP_INSN, then we also need to fix the JUMP_LABEL
+     field.  This is not handled by for_each_rtx because it doesn't
+     handle unprinted ('0') fields.  */
+  if (GET_CODE (l) == JUMP_INSN && JUMP_LABEL (l) == old_label)
+    JUMP_LABEL (l) = new_label;
+
+  if ((GET_CODE (l) == LABEL_REF
+       || GET_CODE (l) == INSN_LIST)
+      && XEXP (l, 0) == old_label)
+    {
+      XEXP (l, 0) = new_label;
+      if (update_label_nuses)
+       {
+         ++LABEL_NUSES (new_label);
+         --LABEL_NUSES (old_label);
+       }
+      return 0;
+    }
+
+  return 0;
+}
+
+/* When *BODY is equal to X or X is directly referenced by *BODY
+   return nonzero, thus FOR_EACH_RTX stops traversing and returns nonzero
+   too, otherwise FOR_EACH_RTX continues traversing *BODY.  */
+
+static int
+rtx_referenced_p_1 (body, x)
+     rtx *body;
+     void *x;
+{
+  rtx y = (rtx) x;
+
+  if (*body == NULL_RTX)
+    return y == NULL_RTX;
+
+  /* Return true if a label_ref *BODY refers to label Y.  */
+  if (GET_CODE (*body) == LABEL_REF && GET_CODE (y) == CODE_LABEL)
+    return XEXP (*body, 0) == y;
+
+  /* If *BODY is a reference to pool constant traverse the constant.  */
+  if (GET_CODE (*body) == SYMBOL_REF
+      && CONSTANT_POOL_ADDRESS_P (*body))
+    return rtx_referenced_p (y, get_pool_constant (*body));
+
+  /* By default, compare the RTL expressions.  */
+  return rtx_equal_p (*body, y);
+}
+
+/* Return true if X is referenced in BODY.  */
+
+int
+rtx_referenced_p (x, body)
+     rtx x;
+     rtx body;
+{
+  return for_each_rtx (&body, rtx_referenced_p_1, x);
+}
+
+/* If INSN is a jump to jumptable insn rturn true and store the label (which
+   INSN jumps to) to *LABEL and the tablejump insn to *TABLE.
+   LABEL and TABLE may be NULL.  */
+
+bool
+tablejump_p (insn, label, table)
+     rtx insn;
+     rtx *label;
+     rtx *table;
+{
+  rtx l, t;
+
+  if (onlyjump_p (insn)
+      && (l = JUMP_LABEL (insn)) != NULL_RTX
+      && (t = NEXT_INSN (l)) != NULL_RTX
+      && GET_CODE (t) == JUMP_INSN
+      && (GET_CODE (PATTERN (t)) == ADDR_VEC
+         || GET_CODE (PATTERN (t)) == ADDR_DIFF_VEC))
+    {
+      if (label)
+       *label = l;
+      if (table)
+       *table = t;
+      return true;
+    }
+  return false;
+}
+
 /* A subroutine of computed_jump_p, return 1 if X contains a REG or MEM or
    constant that is not in the constant pool and not in the condition
    of an IF_THEN_ELSE.  */
@@ -2773,7 +3020,7 @@ computed_jump_p (insn)
    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
+   nonzero 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
@@ -3026,7 +3273,7 @@ insns_safe_to_move_p (from, to, new_to)
   return 0;
 }
 
-/* Return non-zero if IN contains a piece of rtl that has the address LOC */
+/* Return nonzero if IN contains a piece of rtl that has the address LOC */
 int
 loc_mentioned_in_p (loc, in)
      rtx *loc, in;
@@ -3235,7 +3482,7 @@ find_first_parameter_load (call_insn, boundary)
   return before;
 }
 
-/* Return true if we should avoid inserting code between INSN and preceeding
+/* Return true if we should avoid inserting code between INSN and preceding
    call instruction.  */
 
 bool
@@ -3247,6 +3494,7 @@ keep_with_call_p (insn)
   if (INSN_P (insn) && (set = single_set (insn)) != NULL)
     {
       if (GET_CODE (SET_DEST (set)) == REG
+         && REGNO (SET_DEST (set)) < FIRST_PSEUDO_REGISTER
          && fixed_regs[REGNO (SET_DEST (set))]
          && general_operand (SET_SRC (set), VOIDmode))
        return true;
@@ -3307,7 +3555,7 @@ hoist_test_store (x, val, live)
   /* Pseudo registers can be allways replaced by another pseudo to avoid
      the side effect, for hard register we must ensure that they are dead.
      Eventually we may want to add code to try turn pseudos to hards, but it
-     is unlikely usefull.  */
+     is unlikely useful.  */
 
   if (REGNO (x) < FIRST_PSEUDO_REGISTER)
     {
@@ -3377,7 +3625,7 @@ can_hoist_insn_p (insn, val, live)
            case USE:
              /* We need to fix callers to really ensure availability
                 of all values inisn uses, but for now it is safe to prohibit
-                hoisting of any insn having such a hiden uses.  */
+                hoisting of any insn having such a hidden uses.  */
              return false;
              break;
            case CLOBBER: