OSDN Git Service

2008-04-30 Paul Thomas <pault@gcc.gnu.org>
[pf3gnuchains/gcc-fork.git] / gcc / fwprop.c
index a3c8bc1..9332987 100644 (file)
@@ -6,7 +6,7 @@ This file is part of GCC.
 
 GCC is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
+Software Foundation; either version 3, or (at your option) any later
 version.
 
 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
@@ -15,9 +15,8 @@ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 for more details.
 
 You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING.  If not, write to the Free
-Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA.  */
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
 #include "system.h"
@@ -209,47 +208,76 @@ should_replace_address (rtx old, rtx new, enum machine_mode mode)
   return (gain > 0);
 }
 
+
+/* Flags for the last parameter of propagate_rtx_1.  */
+
+enum {
+  /* If PR_CAN_APPEAR is true, propagate_rtx_1 always returns true;
+     if it is false, propagate_rtx_1 returns false if, for at least
+     one occurrence OLD, it failed to collapse the result to a constant.
+     For example, (mult:M (reg:M A) (minus:M (reg:M B) (reg:M A))) may
+     collapse to zero if replacing (reg:M B) with (reg:M A).
+
+     PR_CAN_APPEAR is disregarded inside MEMs: in that case,
+     propagate_rtx_1 just tries to make cheaper and valid memory
+     addresses.  */
+  PR_CAN_APPEAR = 1,
+
+  /* If PR_HANDLE_MEM is not set, propagate_rtx_1 won't attempt any replacement
+     outside memory addresses.  This is needed because propagate_rtx_1 does
+     not do any analysis on memory; thus it is very conservative and in general
+     it will fail if non-read-only MEMs are found in the source expression.
+
+     PR_HANDLE_MEM is set when the source of the propagation was not
+     another MEM.  Then, it is safe not to treat non-read-only MEMs as
+     ``opaque'' objects.  */
+  PR_HANDLE_MEM = 2
+};
+
+
 /* Replace all occurrences of OLD in *PX with NEW and try to simplify the
    resulting expression.  Replace *PX with a new RTL expression if an
    occurrence of OLD was found.
 
-   If CAN_APPEAR is true, we always return true; if it is false, we
-   can return false if, for at least one occurrence OLD, we failed to
-   collapse the result to a constant.  For example, (mult:M (reg:M A)
-   (minus:M (reg:M B) (reg:M A))) may collapse to zero if replacing
-   (reg:M B) with (reg:M A).
-
-   CAN_APPEAR is disregarded inside MEMs: in that case, we always return
-   true if the simplification is a cheaper and valid memory address.
-
    This is only a wrapper around simplify-rtx.c: do not add any pattern
    matching code here.  (The sole exception is the handling of LO_SUM, but
    that is because there is no simplify_gen_* function for LO_SUM).  */
 
 static bool
-propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear)
+propagate_rtx_1 (rtx *px, rtx old, rtx new, int flags)
 {
   rtx x = *px, tem = NULL_RTX, op0, op1, op2;
   enum rtx_code code = GET_CODE (x);
   enum machine_mode mode = GET_MODE (x);
   enum machine_mode op_mode;
+  bool can_appear = (flags & PR_CAN_APPEAR) != 0;
   bool valid_ops = true;
 
-  /* If X is OLD_RTX, return NEW_RTX.  Otherwise, if this is an expression,
-     try to build a new expression from recursive substitution.  */
+  if (!(flags & PR_HANDLE_MEM) && MEM_P (x) && !MEM_READONLY_P (x))
+    {
+      /* If unsafe, change MEMs to CLOBBERs or SCRATCHes (to preserve whether
+        they have side effects or not).  */
+      *px = (side_effects_p (x)
+            ? gen_rtx_CLOBBER (GET_MODE (x), const0_rtx)
+            : gen_rtx_SCRATCH (GET_MODE (x)));
+      return false;
+    }
 
+  /* If X is OLD_RTX, return NEW_RTX.  But not if replacing only within an
+     address, and we are *not* inside one.  */
   if (x == old)
     {
       *px = new;
       return can_appear;
     }
 
+  /* If this is an expression, try recursive substitution.  */
   switch (GET_RTX_CLASS (code))
     {
     case RTX_UNARY:
       op0 = XEXP (x, 0);
       op_mode = GET_MODE (op0);
-      valid_ops &= propagate_rtx_1 (&op0, old, new, can_appear);
+      valid_ops &= propagate_rtx_1 (&op0, old, new, flags);
       if (op0 == XEXP (x, 0))
        return true;
       tem = simplify_gen_unary (code, mode, op0, op_mode);
@@ -259,8 +287,8 @@ propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear)
     case RTX_COMM_ARITH:
       op0 = XEXP (x, 0);
       op1 = XEXP (x, 1);
-      valid_ops &= propagate_rtx_1 (&op0, old, new, can_appear);
-      valid_ops &= propagate_rtx_1 (&op1, old, new, can_appear);
+      valid_ops &= propagate_rtx_1 (&op0, old, new, flags);
+      valid_ops &= propagate_rtx_1 (&op1, old, new, flags);
       if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1))
        return true;
       tem = simplify_gen_binary (code, mode, op0, op1);
@@ -271,8 +299,8 @@ propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear)
       op0 = XEXP (x, 0);
       op1 = XEXP (x, 1);
       op_mode = GET_MODE (op0) != VOIDmode ? GET_MODE (op0) : GET_MODE (op1);
-      valid_ops &= propagate_rtx_1 (&op0, old, new, can_appear);
-      valid_ops &= propagate_rtx_1 (&op1, old, new, can_appear);
+      valid_ops &= propagate_rtx_1 (&op0, old, new, flags);
+      valid_ops &= propagate_rtx_1 (&op1, old, new, flags);
       if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1))
        return true;
       tem = simplify_gen_relational (code, mode, op_mode, op0, op1);
@@ -284,9 +312,9 @@ propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear)
       op1 = XEXP (x, 1);
       op2 = XEXP (x, 2);
       op_mode = GET_MODE (op0);
-      valid_ops &= propagate_rtx_1 (&op0, old, new, can_appear);
-      valid_ops &= propagate_rtx_1 (&op1, old, new, can_appear);
-      valid_ops &= propagate_rtx_1 (&op2, old, new, can_appear);
+      valid_ops &= propagate_rtx_1 (&op0, old, new, flags);
+      valid_ops &= propagate_rtx_1 (&op1, old, new, flags);
+      valid_ops &= propagate_rtx_1 (&op2, old, new, flags);
       if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1) && op2 == XEXP (x, 2))
        return true;
       if (op_mode == VOIDmode)
@@ -299,7 +327,7 @@ propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear)
       if (code == SUBREG)
        {
           op0 = XEXP (x, 0);
-         valid_ops &= propagate_rtx_1 (&op0, old, new, can_appear);
+         valid_ops &= propagate_rtx_1 (&op0, old, new, flags);
           if (op0 == XEXP (x, 0))
            return true;
          tem = simplify_gen_subreg (mode, op0, GET_MODE (SUBREG_REG (x)),
@@ -318,7 +346,8 @@ propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear)
            return true;
 
          op0 = new_op0 = targetm.delegitimize_address (op0);
-         valid_ops &= propagate_rtx_1 (&new_op0, old, new, true);
+         valid_ops &= propagate_rtx_1 (&new_op0, old, new,
+                                       flags | PR_CAN_APPEAR);
 
          /* Dismiss transformation that we do not want to carry on.  */
          if (!valid_ops
@@ -345,8 +374,8 @@ propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear)
          /* The only simplification we do attempts to remove references to op0
             or make it constant -- in both cases, op0's invalidity will not
             make the result invalid.  */
-         propagate_rtx_1 (&op0, old, new, true);
-         valid_ops &= propagate_rtx_1 (&op1, old, new, can_appear);
+         propagate_rtx_1 (&op0, old, new, flags | PR_CAN_APPEAR);
+         valid_ops &= propagate_rtx_1 (&op1, old, new, flags);
           if (op0 == XEXP (x, 0) && op1 == XEXP (x, 1))
            return true;
 
@@ -388,6 +417,18 @@ propagate_rtx_1 (rtx *px, rtx old, rtx new, bool can_appear)
   return valid_ops || can_appear || CONSTANT_P (tem);
 }
 
+
+/* for_each_rtx traversal function that returns 1 if BODY points to
+   a non-constant mem.  */
+
+static int
+varying_mem_p (rtx *body, void *data ATTRIBUTE_UNUSED)
+{
+  rtx x = *body;
+  return MEM_P (x) && !MEM_READONLY_P (x);
+}
+
+
 /* Replace all occurrences of OLD in X with NEW and try to simplify the
    resulting expression (in mode MODE).  Return a new expression if it is
    a constant, otherwise X.
@@ -401,14 +442,19 @@ propagate_rtx (rtx x, enum machine_mode mode, rtx old, rtx new)
 {
   rtx tem;
   bool collapsed;
+  int flags;
 
   if (REG_P (new) && REGNO (new) < FIRST_PSEUDO_REGISTER)
     return NULL_RTX;
 
-  new = copy_rtx (new);
+  flags = 0;
+  if (REG_P (new) || CONSTANT_P (new))
+    flags |= PR_CAN_APPEAR;
+  if (!for_each_rtx (&new, varying_mem_p, NULL))
+    flags |= PR_HANDLE_MEM;
 
   tem = x;
-  collapsed = propagate_rtx_1 (&tem, old, new, REG_P (new) || CONSTANT_P (new));
+  collapsed = propagate_rtx_1 (&tem, old, copy_rtx (new), flags);
   if (tem == x || !collapsed)
     return NULL_RTX;
 
@@ -517,16 +563,6 @@ use_killed_between (struct df_ref *use, rtx def_insn, rtx target_insn)
 }
 
 
-/* for_each_rtx traversal function that returns 1 if BODY points to
-   a non-constant mem.  */
-
-static int
-varying_mem_p (rtx *body, void *data ATTRIBUTE_UNUSED)
-{
-  rtx x = *body;
-  return MEM_P (x) && !MEM_READONLY_P (x);
-}
-
 /* Check if all uses in DEF_INSN can be used in TARGET_INSN.  This
    would require full computation of available expressions;
    we check only restricted conditions, see use_killed_between.  */
@@ -578,9 +614,7 @@ all_uses_available_at (rtx def_insn, rtx target_insn)
        }
     }
 
-  /* We don't do any analysis of memories or aliasing.  Reject any
-     instruction that involves references to non-constant memory.  */
-  return !for_each_rtx (&SET_SRC (def_set), varying_mem_p, NULL);
+  return true;
 }
 
 \f
@@ -643,17 +677,28 @@ update_df (rtx insn, rtx *loc, struct df_ref **use_rec, enum df_ref_type type,
     {
       struct df_ref *use = *use_rec;
       struct df_ref *orig_use = use, *new_use;
+      int width = -1;
+      int offset = -1;
+      enum machine_mode mode = 0;
       rtx *new_loc = find_occurrence (loc, DF_REF_REG (orig_use));
       use_rec++;
 
       if (!new_loc)
        continue;
 
+      if (DF_REF_FLAGS_IS_SET (orig_use, DF_REF_SIGN_EXTRACT | DF_REF_ZERO_EXTRACT))
+       {
+         width = DF_REF_EXTRACT_WIDTH (orig_use);
+         offset = DF_REF_EXTRACT_OFFSET (orig_use);
+         mode = DF_REF_EXTRACT_MODE (orig_use);
+       }
+
       /* Add a new insn use.  Use the original type, because it says if the
          use was within a MEM.  */
       new_use = df_ref_create (DF_REF_REG (orig_use), new_loc,
                               insn, BLOCK_FOR_INSN (insn),
-                              type, DF_REF_FLAGS (orig_use) | new_flags);
+                              type, DF_REF_FLAGS (orig_use) | new_flags, 
+                              width, offset, mode);
 
       /* Set up the use-def chain.  */
       df_chain_copy (new_use, DF_REF_CHAIN (orig_use));
@@ -676,6 +721,9 @@ try_fwprop_subst (struct df_ref *use, rtx *loc, rtx new, rtx def_insn, bool set_
   rtx insn = DF_REF_INSN (use);
   enum df_ref_type type = DF_REF_TYPE (use);
   int flags = DF_REF_FLAGS (use);
+  rtx set = single_set (insn);
+  int old_cost = rtx_cost (SET_SRC (set), SET);
+  bool ok;
 
   if (dump_file)
     {
@@ -686,11 +734,35 @@ try_fwprop_subst (struct df_ref *use, rtx *loc, rtx new, rtx def_insn, bool set_
       fprintf (dump_file, "\n");
     }
 
-  if (validate_change (insn, loc, new, false))
+  validate_unshare_change (insn, loc, new, true);
+  if (!verify_changes (0))
+    {
+      if (dump_file)
+       fprintf (dump_file, "Changes to insn %d not recognized\n",
+                INSN_UID (insn));
+      ok = false;
+    }
+
+  else if (DF_REF_TYPE (use) == DF_REF_REG_USE
+          && rtx_cost (SET_SRC (set), SET) > old_cost)
+    {
+      if (dump_file)
+       fprintf (dump_file, "Changes to insn %d not profitable\n",
+                INSN_UID (insn));
+      ok = false;
+    }
+
+  else
     {
-      num_changes++;
       if (dump_file)
        fprintf (dump_file, "Changed insn %d\n", INSN_UID (insn));
+      ok = true;
+    }
+
+  if (ok)
+    {
+      confirm_change_group ();
+      num_changes++;
 
       df_ref_remove (use);
       if (!CONSTANT_P (new))
@@ -698,17 +770,17 @@ try_fwprop_subst (struct df_ref *use, rtx *loc, rtx new, rtx def_insn, bool set_
          update_df (insn, loc, DF_INSN_USES (def_insn), type, flags);
          update_df (insn, loc, DF_INSN_EQ_USES (def_insn), type, flags);
        }
-      return true;
     }
   else
     {
-      if (dump_file)
-       fprintf (dump_file, "Changes to insn %d not recognized\n",
-                INSN_UID (insn));
-
-      /* Can also record a simplified value in a REG_EQUAL note, making a
-        new one if one does not already exist.  */
-      if (set_reg_equal)
+      cancel_changes (0);
+
+      /* Can also record a simplified value in a REG_EQUAL note,
+        making a new one if one does not already exist.
+        Don't do this if the insn has a REG_RETVAL note, because the
+        combined presence means that the REG_EQUAL note refers to the
+        (full) contents of the libcall value.  */
+      if (set_reg_equal && !find_reg_note (insn, REG_RETVAL, NULL_RTX))
        {
          if (dump_file)
            fprintf (dump_file, " Setting REG_EQUAL note\n");
@@ -725,9 +797,9 @@ try_fwprop_subst (struct df_ref *use, rtx *loc, rtx new, rtx def_insn, bool set_
                         type, DF_REF_IN_NOTE);
            }
        }
-
-      return false;
     }
+
+  return ok;
 }
 
 
@@ -898,7 +970,7 @@ forward_propagate_into (struct df_ref *use)
   else
     parent = PATTERN (use_insn);
 
-  if (!loc_mentioned_in_p (DF_REF_LOC (use), parent))
+  if (!reg_mentioned_p (DF_REF_REG (use), parent))
     return;
 
   def_insn = DF_REF_INSN (def);
@@ -987,8 +1059,10 @@ fwprop (void)
   return 0;
 }
 
-struct tree_opt_pass pass_rtl_fwprop =
+struct rtl_opt_pass pass_rtl_fwprop =
 {
+ {
+  RTL_PASS,
   "fwprop1",                            /* name */
   gate_fwprop,                         /* gate */
   fwprop,                              /* execute */
@@ -1000,9 +1074,9 @@ struct tree_opt_pass pass_rtl_fwprop =
   0,                                    /* properties_provided */
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
-  TODO_df_finish |
-  TODO_dump_func,                       /* todo_flags_finish */
-  0                                     /* letter */
+  TODO_df_finish | TODO_verify_rtl_sharing |
+  TODO_dump_func                        /* todo_flags_finish */
+ }
 };
 
 static unsigned int
@@ -1029,8 +1103,10 @@ fwprop_addr (void)
   return 0;
 }
 
-struct tree_opt_pass pass_rtl_fwprop_addr =
+struct rtl_opt_pass pass_rtl_fwprop_addr =
 {
+ {
+  RTL_PASS,
   "fwprop2",                            /* name */
   gate_fwprop,                         /* gate */
   fwprop_addr,                         /* execute */
@@ -1042,7 +1118,7 @@ struct tree_opt_pass pass_rtl_fwprop_addr =
   0,                                    /* properties_provided */
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
-  TODO_df_finish |
-  TODO_dump_func,                       /* todo_flags_finish */
-  0                                     /* letter */
+  TODO_df_finish | TODO_verify_rtl_sharing |
+  TODO_dump_func                        /* todo_flags_finish */
+ }
 };