Add -march=interaptiv.
[platform/upstream/gcc.git] / gcc / regcprop.c
index 97a023f..97433f0 100644 (file)
@@ -1,5 +1,5 @@
 /* Copy propagation on hard registers for the GNU compiler.
-   Copyright (C) 2000-2013 Free Software Foundation, Inc.
+   Copyright (C) 2000-2015 Free Software Foundation, Inc.
 
    This file is part of GCC.
 
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "tm.h"
+#include "backend.h"
 #include "rtl.h"
+#include "df.h"
 #include "tm_p.h"
 #include "insn-config.h"
 #include "regs.h"
 #include "addresses.h"
-#include "hard-reg-set.h"
-#include "basic-block.h"
 #include "reload.h"
-#include "function.h"
 #include "recog.h"
 #include "flags.h"
 #include "diagnostic-core.h"
-#include "obstack.h"
 #include "tree-pass.h"
-#include "df.h"
+#include "rtl-iter.h"
+#include "emit-rtl.h"
 
 /* The following code does forward propagation of hard register copies.
    The object is to eliminate as many dependencies as possible, so that
@@ -50,7 +48,7 @@
 struct queued_debug_insn_change
 {
   struct queued_debug_insn_change *next;
-  rtx insn;
+  rtx_insn *insn;
   rtx *loc;
   rtx new_rtx;
 };
@@ -63,7 +61,7 @@ struct queued_debug_insn_change
 
 struct value_data_entry
 {
-  enum machine_mode mode;
+  machine_mode mode;
   unsigned int oldest_regno;
   unsigned int next_regno;
   struct queued_debug_insn_change *debug_insn_changes;
@@ -76,28 +74,30 @@ struct value_data
   unsigned int n_debug_insn_changes;
 };
 
-static alloc_pool debug_insn_changes_pool;
+static object_allocator<queued_debug_insn_change> queued_debug_insn_change_pool
+  ("debug insn changes pool", 256);
+
+static bool skip_debug_insn_p;
 
 static void kill_value_one_regno (unsigned, struct value_data *);
 static void kill_value_regno (unsigned, unsigned, struct value_data *);
-static void kill_value (rtx, struct value_data *);
-static void set_value_regno (unsigned, enum machine_mode, struct value_data *);
+static void kill_value (const_rtx, struct value_data *);
+static void set_value_regno (unsigned, machine_mode, struct value_data *);
 static void init_value_data (struct value_data *);
 static void kill_clobbered_value (rtx, const_rtx, void *);
 static void kill_set_value (rtx, const_rtx, void *);
-static int kill_autoinc_value (rtx *, void *);
 static void copy_value (rtx, rtx, struct value_data *);
-static bool mode_change_ok (enum machine_mode, enum machine_mode,
+static bool mode_change_ok (machine_mode, machine_mode,
                            unsigned int);
-static rtx maybe_mode_change (enum machine_mode, enum machine_mode,
-                             enum machine_mode, unsigned int, unsigned int);
+static rtx maybe_mode_change (machine_mode, machine_mode,
+                             machine_mode, unsigned int, unsigned int);
 static rtx find_oldest_value_reg (enum reg_class, rtx, struct value_data *);
-static bool replace_oldest_value_reg (rtx *, enum reg_class, rtx,
+static bool replace_oldest_value_reg (rtx *, enum reg_class, rtx_insn *,
                                      struct value_data *);
 static bool replace_oldest_value_addr (rtx *, enum reg_class,
-                                      enum machine_mode, addr_space_t, rtx,
-                                      struct value_data *);
-static bool replace_oldest_value_mem (rtx, rtx, struct value_data *);
+                                      machine_mode, addr_space_t,
+                                      rtx_insn *, struct value_data *);
+static bool replace_oldest_value_mem (rtx, rtx_insn *, struct value_data *);
 static bool copyprop_hardreg_forward_1 (basic_block, struct value_data *);
 extern void debug_value_data (struct value_data *);
 #ifdef ENABLE_CHECKING
@@ -115,7 +115,7 @@ free_debug_insn_changes (struct value_data *vd, unsigned int regno)
     {
       next = cur->next;
       --vd->n_debug_insn_changes;
-      pool_free (debug_insn_changes_pool, cur);
+      queued_debug_insn_change_pool.remove (cur);
     }
   vd->e[regno].debug_insn_changes = NULL;
 }
@@ -189,30 +189,22 @@ kill_value_regno (unsigned int regno, unsigned int nregs,
    so that we mind the mode the register is in.  */
 
 static void
-kill_value (rtx x, struct value_data *vd)
+kill_value (const_rtx x, struct value_data *vd)
 {
-  rtx orig_rtx = x;
-
   if (GET_CODE (x) == SUBREG)
     {
-      x = simplify_subreg (GET_MODE (x), SUBREG_REG (x),
-                          GET_MODE (SUBREG_REG (x)), SUBREG_BYTE (x));
-      if (x == NULL_RTX)
-       x = SUBREG_REG (orig_rtx);
+      rtx tmp = simplify_subreg (GET_MODE (x), SUBREG_REG (x),
+                                GET_MODE (SUBREG_REG (x)), SUBREG_BYTE (x));
+      x = tmp ? tmp : SUBREG_REG (x);
     }
   if (REG_P (x))
-    {
-      unsigned int regno = REGNO (x);
-      unsigned int n = hard_regno_nregs[regno][GET_MODE (x)];
-
-      kill_value_regno (regno, n, vd);
-    }
+    kill_value_regno (REGNO (x), REG_NREGS (x), vd);
 }
 
 /* Remember that REGNO is valid in MODE.  */
 
 static void
-set_value_regno (unsigned int regno, enum machine_mode mode,
+set_value_regno (unsigned int regno, machine_mode mode,
                 struct value_data *vd)
 {
   unsigned int nregs;
@@ -275,25 +267,24 @@ kill_set_value (rtx x, const_rtx set, void *data)
     }
 }
 
-/* Called through for_each_rtx.  Kill any register used as the base of an
-   auto-increment expression, and install that register as the root of its
-   own value list.  */
+/* Kill any register used in X as the base of an auto-increment expression,
+   and install that register as the root of its own value list.  */
 
-static int
-kill_autoinc_value (rtx *px, void *data)
+static void
+kill_autoinc_value (rtx_insn *insn, struct value_data *vd)
 {
-  rtx x = *px;
-  struct value_data *const vd = (struct value_data *) data;
-
-  if (GET_RTX_CLASS (GET_CODE (x)) == RTX_AUTOINC)
+  subrtx_iterator::array_type array;
+  FOR_EACH_SUBRTX (iter, array, PATTERN (insn), NONCONST)
     {
-      x = XEXP (x, 0);
-      kill_value (x, vd);
-      set_value_regno (REGNO (x), GET_MODE (x), vd);
-      return -1;
+      const_rtx x = *iter;
+      if (GET_RTX_CLASS (GET_CODE (x)) == RTX_AUTOINC)
+       {
+         x = XEXP (x, 0);
+         kill_value (x, vd);
+         set_value_regno (REGNO (x), GET_MODE (x), vd);
+         iter.skip_subrtxes ();
+       }
     }
-
-  return 0;
 }
 
 /* Assert that SRC has been copied to DEST.  Adjust the data structures
@@ -328,8 +319,8 @@ copy_value (rtx dest, rtx src, struct value_data *vd)
     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)];
+  dn = REG_NREGS (dest);
+  sn = REG_NREGS (src);
   if ((dr > sr && dr < sr + sn)
       || (sr > dr && sr < dr + dn))
     return;
@@ -346,7 +337,7 @@ copy_value (rtx dest, rtx src, struct value_data *vd)
      we must not do the same for the high part.
      Note we can still get low parts for the same mode combination through
      a two-step copy involving differently sized hard regs.
-     Assume hard regs fr* are 32 bits bits each, while r* are 64 bits each:
+     Assume hard regs fr* are 32 bits each, while r* are 64 bits each:
      (set (reg:DI r0) (reg:DI fr0))
      (set (reg:SI fr2) (reg:SI r0))
      loads the low part of (reg:DI fr0) - i.e. fr1 - into fr2, while:
@@ -382,7 +373,7 @@ copy_value (rtx dest, rtx src, struct value_data *vd)
 /* Return true if a mode change from ORIG to NEW is allowed for REGNO.  */
 
 static bool
-mode_change_ok (enum machine_mode orig_mode, enum machine_mode new_mode,
+mode_change_ok (machine_mode orig_mode, machine_mode new_mode,
                unsigned int regno ATTRIBUTE_UNUSED)
 {
   if (GET_MODE_SIZE (orig_mode) < GET_MODE_SIZE (new_mode))
@@ -401,8 +392,8 @@ mode_change_ok (enum machine_mode orig_mode, enum machine_mode new_mode,
    Return a NEW_MODE rtx for REGNO if that's OK, otherwise return NULL_RTX.  */
 
 static rtx
-maybe_mode_change (enum machine_mode orig_mode, enum machine_mode copy_mode,
-                  enum machine_mode new_mode, unsigned int regno,
+maybe_mode_change (machine_mode orig_mode, machine_mode copy_mode,
+                  machine_mode new_mode, unsigned int regno,
                   unsigned int copy_regno ATTRIBUTE_UNUSED)
 {
   if (GET_MODE_SIZE (copy_mode) < GET_MODE_SIZE (orig_mode)
@@ -410,7 +401,7 @@ maybe_mode_change (enum machine_mode orig_mode, enum machine_mode copy_mode,
     return NULL_RTX;
 
   if (orig_mode == new_mode)
-    return gen_rtx_raw_REG (new_mode, regno);
+    return gen_raw_REG (new_mode, regno);
   else if (mode_change_ok (orig_mode, new_mode, regno))
     {
       int copy_nregs = hard_regno_nregs[copy_regno][copy_mode];
@@ -426,7 +417,7 @@ maybe_mode_change (enum machine_mode orig_mode, enum machine_mode copy_mode,
                + (BYTES_BIG_ENDIAN ? byteoffset : 0));
       regno += subreg_regno_offset (regno, orig_mode, offset, new_mode);
       if (HARD_REGNO_MODE_OK (regno, new_mode))
-       return gen_rtx_raw_REG (new_mode, regno);
+       return gen_raw_REG (new_mode, regno);
     }
   return NULL_RTX;
 }
@@ -439,7 +430,7 @@ static rtx
 find_oldest_value_reg (enum reg_class cl, rtx reg, struct value_data *vd)
 {
   unsigned int regno = REGNO (reg);
-  enum machine_mode mode = GET_MODE (reg);
+  machine_mode mode = GET_MODE (reg);
   unsigned int i;
 
   /* If we are accessing REG in some mode other that what we set it in,
@@ -458,7 +449,7 @@ find_oldest_value_reg (enum reg_class cl, rtx reg, struct value_data *vd)
 
   for (i = vd->e[regno].oldest_regno; i != regno; i = vd->e[i].next_regno)
     {
-      enum machine_mode oldmode = vd->e[i].mode;
+      machine_mode oldmode = vd->e[i].mode;
       rtx new_rtx;
 
       if (!in_hard_reg_set_p (reg_class_contents[cl], mode, i))
@@ -481,11 +472,11 @@ find_oldest_value_reg (enum reg_class cl, rtx reg, struct value_data *vd)
    in register class CL.  Return true if successfully replaced.  */
 
 static bool
-replace_oldest_value_reg (rtx *loc, enum reg_class cl, rtx insn,
+replace_oldest_value_reg (rtx *loc, enum reg_class cl, rtx_insn *insn,
                          struct value_data *vd)
 {
   rtx new_rtx = find_oldest_value_reg (cl, *loc, vd);
-  if (new_rtx)
+  if (new_rtx && (!DEBUG_INSN_P (insn) || !skip_debug_insn_p))
     {
       if (DEBUG_INSN_P (insn))
        {
@@ -495,8 +486,7 @@ replace_oldest_value_reg (rtx *loc, enum reg_class cl, rtx insn,
            fprintf (dump_file, "debug_insn %u: queued replacing reg %u with %u\n",
                     INSN_UID (insn), REGNO (*loc), REGNO (new_rtx));
 
-         change = (struct queued_debug_insn_change *)
-                  pool_alloc (debug_insn_changes_pool);
+         change = queued_debug_insn_change_pool.allocate ();
          change->next = vd->e[REGNO (new_rtx)].debug_insn_changes;
          change->insn = insn;
          change->loc = loc;
@@ -521,8 +511,8 @@ replace_oldest_value_reg (rtx *loc, enum reg_class cl, rtx insn,
 
 static bool
 replace_oldest_value_addr (rtx *loc, enum reg_class cl,
-                          enum machine_mode mode, addr_space_t as,
-                          rtx insn, struct value_data *vd)
+                          machine_mode mode, addr_space_t as,
+                          rtx_insn *insn, struct value_data *vd)
 {
   rtx x = *loc;
   RTX_CODE code = GET_CODE (x);
@@ -668,7 +658,7 @@ replace_oldest_value_addr (rtx *loc, enum reg_class cl,
 /* Similar to replace_oldest_value_reg, but X contains a memory.  */
 
 static bool
-replace_oldest_value_mem (rtx x, rtx insn, struct value_data *vd)
+replace_oldest_value_mem (rtx x, rtx_insn *insn, struct value_data *vd)
 {
   enum reg_class cl;
 
@@ -689,7 +679,7 @@ static void
 apply_debug_insn_changes (struct value_data *vd, unsigned int regno)
 {
   struct queued_debug_insn_change *change;
-  rtx last_insn = vd->e[regno].debug_insn_changes->insn;
+  rtx_insn *last_insn = vd->e[regno].debug_insn_changes->insn;
 
   for (change = vd->e[regno].debug_insn_changes;
        change;
@@ -705,33 +695,48 @@ apply_debug_insn_changes (struct value_data *vd, unsigned int regno)
   apply_change_group ();
 }
 
-/* Called via for_each_rtx, for all used registers in a real
-   insn apply DEBUG_INSN changes that change registers to the
-   used register.  */
+/* Called via note_uses, for all used registers in a real insn
+   apply DEBUG_INSN changes that change registers to the used
+   registers.  */
 
-static int
-cprop_find_used_regs_1 (rtx *loc, void *data)
+static void
+cprop_find_used_regs (rtx *loc, void *data)
 {
-  if (REG_P (*loc))
+  struct value_data *const vd = (struct value_data *) data;
+  subrtx_iterator::array_type array;
+  FOR_EACH_SUBRTX (iter, array, *loc, NONCONST)
     {
-      struct value_data *vd = (struct value_data *) data;
-      if (vd->e[REGNO (*loc)].debug_insn_changes)
+      const_rtx x = *iter;
+      if (REG_P (x))
        {
-         apply_debug_insn_changes (vd, REGNO (*loc));
-         free_debug_insn_changes (vd, REGNO (*loc));
+         unsigned int regno = REGNO (x);
+         if (vd->e[regno].debug_insn_changes)
+           {
+             apply_debug_insn_changes (vd, regno);
+             free_debug_insn_changes (vd, regno);
+           }
        }
     }
-  return 0;
 }
 
-/* Called via note_uses, for all used registers in a real insn
-   apply DEBUG_INSN changes that change registers to the used
-   registers.  */
+/* Apply clobbers of INSN in PATTERN and C_I_F_U to value_data VD.  */
 
 static void
-cprop_find_used_regs (rtx *loc, void *vd)
+kill_clobbered_values (rtx_insn *insn, struct value_data *vd)
 {
-  for_each_rtx (loc, cprop_find_used_regs_1, vd);
+  note_stores (PATTERN (insn), kill_clobbered_value, vd);
+
+  if (CALL_P (insn))
+    {
+      rtx exp;
+
+      for (exp = CALL_INSN_FUNCTION_USAGE (insn); exp; exp = XEXP (exp, 1))
+       {
+         rtx x = XEXP (exp, 0);
+         if (GET_CODE (x) == CLOBBER)
+           kill_value (SET_DEST (x), vd);
+       }
+    }
 }
 
 /* Perform the forward copy propagation on basic block BB.  */
@@ -740,13 +745,14 @@ static bool
 copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
 {
   bool anything_changed = false;
-  rtx insn;
+  rtx_insn *insn;
 
   for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn))
     {
-      int n_ops, i, alt, predicated;
+      int n_ops, i, predicated;
       bool is_asm, any_replacements;
       rtx set;
+      rtx link;
       bool replaced[MAX_RECOG_OPERANDS];
       bool changed = false;
       struct kill_set_value_data ksvd;
@@ -769,25 +775,20 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
        }
 
       set = single_set (insn);
-      extract_insn (insn);
-      if (! constrain_operands (1))
-       fatal_insn_not_found (insn);
-      preprocess_constraints ();
-      alt = which_alternative;
+      extract_constrain_insn (insn);
+      preprocess_constraints (insn);
+      const operand_alternative *op_alt = which_op_alt ();
       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
+      /* Simplify the code below by promoting OP_OUT to OP_INOUT
         in predicated instructions.  */
 
       predicated = GET_CODE (PATTERN (insn)) == COND_EXEC;
       for (i = 0; i < n_ops; ++i)
        {
-         int matches = recog_op_alt[i][alt].matches;
-         if (matches >= 0)
-           recog_op_alt[i][alt].cl = recog_op_alt[matches][alt].cl;
-         if (matches >= 0 || recog_op_alt[i][alt].matched >= 0
+         int matches = op_alt[i].matches;
+         if (matches >= 0 || op_alt[i].matched >= 0
              || (predicated && recog_data.operand_type[i] == OP_OUT))
            recog_data.operand_type[i] = OP_INOUT;
        }
@@ -798,30 +799,47 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
 
       /* For each earlyclobber operand, zap the value data.  */
       for (i = 0; i < n_ops; i++)
-       if (recog_op_alt[i][alt].earlyclobber)
+       if (op_alt[i].earlyclobber)
          kill_value (recog_data.operand[i], vd);
 
       /* Within asms, a clobber cannot overlap inputs or outputs.
         I wouldn't think this were true for regular insns, but
         scan_rtx treats them like that...  */
-      note_stores (PATTERN (insn), kill_clobbered_value, vd);
+      kill_clobbered_values (insn, vd);
 
       /* Kill all auto-incremented values.  */
       /* ??? REG_INC is useless, since stack pushes aren't done that way.  */
-      for_each_rtx (&PATTERN (insn), kill_autoinc_value, vd);
+      kill_autoinc_value (insn, vd);
 
       /* Kill all early-clobbered operands.  */
       for (i = 0; i < n_ops; i++)
-       if (recog_op_alt[i][alt].earlyclobber)
+       if (op_alt[i].earlyclobber)
          kill_value (recog_data.operand[i], vd);
 
+      /* If we have dead sets in the insn, then we need to note these as we
+        would clobbers.  */
+      for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+       {
+         if (REG_NOTE_KIND (link) == REG_UNUSED)
+           {
+             kill_value (XEXP (link, 0), vd);
+             /* Furthermore, if the insn looked like a single-set,
+                but the dead store kills the source value of that
+                set, then we can no-longer use the plain move
+                special case below.  */
+             if (set
+                 && reg_overlap_mentioned_p (XEXP (link, 0), SET_SRC (set)))
+               set = NULL;
+           }
+       }
+
       /* Special-case plain move instructions, since we may well
         be able to do the move from a different register class.  */
       if (set && REG_P (SET_SRC (set)))
        {
          rtx src = SET_SRC (set);
          unsigned int regno = REGNO (src);
-         enum machine_mode mode = GET_MODE (src);
+         machine_mode mode = GET_MODE (src);
          unsigned int i;
          rtx new_rtx;
 
@@ -858,10 +876,8 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
                }
              /* We need to re-extract as validate_change clobbers
                 recog_data.  */
-             extract_insn (insn);
-             if (! constrain_operands (1))
-               fatal_insn_not_found (insn);
-             preprocess_constraints ();
+             extract_constrain_insn (insn);
+             preprocess_constraints (insn);
            }
 
          /* Otherwise, try all valid registers and see if its valid.  */
@@ -886,10 +902,8 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
                    }
                  /* We need to re-extract as validate_change clobbers
                     recog_data.  */
-                 extract_insn (insn);
-                 if (! constrain_operands (1))
-                   fatal_insn_not_found (insn);
-                 preprocess_constraints ();
+                 extract_constrain_insn (insn);
+                 preprocess_constraints (insn);
                }
            }
        }
@@ -917,16 +931,16 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
 
          if (recog_data.operand_type[i] == OP_IN)
            {
-             if (recog_op_alt[i][alt].is_address)
+             if (op_alt[i].is_address)
                replaced[i]
                  = replace_oldest_value_addr (recog_data.operand_loc[i],
-                                              recog_op_alt[i][alt].cl,
+                                              alternative_class (op_alt, i),
                                               VOIDmode, ADDR_SPACE_GENERIC,
                                               insn, vd);
              else if (REG_P (recog_data.operand[i]))
                replaced[i]
                  = replace_oldest_value_reg (recog_data.operand_loc[i],
-                                             recog_op_alt[i][alt].cl,
+                                             alternative_class (op_alt, i),
                                              insn, vd);
              else if (MEM_P (recog_data.operand[i]))
                replaced[i] = replace_oldest_value_mem (recog_data.operand[i],
@@ -993,7 +1007,7 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
          unsigned int set_nregs = 0;
          unsigned int regno;
          rtx exp;
-         hard_reg_set_iterator hrsi;
+         HARD_REG_SET regs_invalidated_by_this_call;
 
          for (exp = CALL_INSN_FUNCTION_USAGE (insn); exp; exp = XEXP (exp, 1))
            {
@@ -1006,116 +1020,49 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
                  copy_value (dest, SET_SRC (x), vd);
                  ksvd.ignore_set_reg = dest;
                  set_regno = REGNO (dest);
-                 set_nregs
-                   = hard_regno_nregs[set_regno][GET_MODE (dest)];
+                 set_nregs = REG_NREGS (dest);
                  break;
                }
            }
 
-         EXECUTE_IF_SET_IN_HARD_REG_SET (regs_invalidated_by_call, 0, regno, hrsi)
-           if (regno < set_regno || regno >= set_regno + set_nregs)
+         get_call_reg_set_usage (insn,
+                                 &regs_invalidated_by_this_call,
+                                 regs_invalidated_by_call);
+         for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+           if ((TEST_HARD_REG_BIT (regs_invalidated_by_this_call, regno)
+                || HARD_REGNO_CALL_PART_CLOBBERED (regno, vd->e[regno].mode))
+               && (regno < set_regno || regno >= set_regno + set_nregs))
              kill_value_regno (regno, 1, vd);
-       }
-
-      /* Notice stores.  */
-      note_stores (PATTERN (insn), kill_set_value, &ksvd);
-
-      /* Notice copies.  */
-      if (set && REG_P (SET_DEST (set)) && REG_P (SET_SRC (set)))
-       copy_value (SET_DEST (set), SET_SRC (set), vd);
-
-      if (insn == BB_END (bb))
-       break;
-    }
-
-  return anything_changed;
-}
-
-/* Main entry point for the forward copy propagation optimization.  */
-
-static unsigned int
-copyprop_hardreg_forward (void)
-{
-  struct value_data *all_vd;
-  basic_block bb;
-  sbitmap visited;
-  bool analyze_called = false;
 
-  all_vd = XNEWVEC (struct value_data, last_basic_block);
-
-  visited = sbitmap_alloc (last_basic_block);
-  bitmap_clear (visited);
+         /* If SET was seen in CALL_INSN_FUNCTION_USAGE, and SET_SRC
+            of the SET isn't in regs_invalidated_by_call hard reg set,
+            but instead among CLOBBERs on the CALL_INSN, we could wrongly
+            assume the value in it is still live.  */
+         if (ksvd.ignore_set_reg)
+           kill_clobbered_values (insn, vd);
+       }
 
-  if (MAY_HAVE_DEBUG_INSNS)
-    debug_insn_changes_pool
-      = create_alloc_pool ("debug insn changes pool",
-                          sizeof (struct queued_debug_insn_change), 256);
+      bool copy_p = (set
+                    && REG_P (SET_DEST (set))
+                    && REG_P (SET_SRC (set)));
+      bool noop_p = (copy_p
+                    && rtx_equal_p (SET_DEST (set), SET_SRC (set)));
 
-  FOR_EACH_BB (bb)
-    {
-      bitmap_set_bit (visited, bb->index);
-
-      /* If a block has a single predecessor, that we've already
-        processed, begin with the value data that was live at
-        the end of the predecessor block.  */
-      /* ??? Ought to use more intelligent queuing of blocks.  */
-      if (single_pred_p (bb)
-         && bitmap_bit_p (visited, single_pred (bb)->index)
-         && ! (single_pred_edge (bb)->flags & (EDGE_ABNORMAL_CALL | EDGE_EH)))
+      if (!noop_p)
        {
-         all_vd[bb->index] = all_vd[single_pred (bb)->index];
-         if (all_vd[bb->index].n_debug_insn_changes)
-           {
-             unsigned int regno;
+         /* Notice stores.  */
+         note_stores (PATTERN (insn), kill_set_value, &ksvd);
 
-             for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
-               {
-                 if (all_vd[bb->index].e[regno].debug_insn_changes)
-                   {
-                     all_vd[bb->index].e[regno].debug_insn_changes = NULL;
-                     if (--all_vd[bb->index].n_debug_insn_changes == 0)
-                       break;
-                   }
-               }
-           }
+         /* Notice copies.  */
+         if (copy_p)
+           copy_value (SET_DEST (set), SET_SRC (set), vd);
        }
-      else
-       init_value_data (all_vd + bb->index);
-
-      copyprop_hardreg_forward_1 (bb, all_vd + bb->index);
-    }
-
-  if (MAY_HAVE_DEBUG_INSNS)
-    {
-      FOR_EACH_BB (bb)
-       if (bitmap_bit_p (visited, bb->index)
-           && all_vd[bb->index].n_debug_insn_changes)
-         {
-           unsigned int regno;
-           bitmap live;
-
-           if (!analyze_called)
-             {
-               df_analyze ();
-               analyze_called = true;
-             }
-           live = df_get_live_out (bb);
-           for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
-             if (all_vd[bb->index].e[regno].debug_insn_changes)
-               {
-                 if (REGNO_REG_SET_P (live, regno))
-                   apply_debug_insn_changes (all_vd + bb->index, regno);
-                 if (all_vd[bb->index].n_debug_insn_changes == 0)
-                   break;
-               }
-         }
 
-      free_alloc_pool (debug_insn_changes_pool);
+      if (insn == BB_END (bb))
+       break;
     }
 
-  sbitmap_free (visited);
-  free (all_vd);
-  return 0;
+  return anything_changed;
 }
 
 /* Dump the value chain data to stderr.  */
@@ -1174,6 +1121,26 @@ debug_value_data (struct value_data *vd)
               vd->e[i].next_regno);
 }
 
+/* Do copyprop_hardreg_forward_1 for a single basic block BB.
+   DEBUG_INSN is skipped since we do not want to involve DF related
+   staff as how it is handled in function pass_cprop_hardreg::execute.
+
+   NOTE: Currently it is only used for shrink-wrap.  Maybe extend it
+   to handle DEBUG_INSN for other uses.  */
+
+void
+copyprop_hardreg_forward_bb_without_debug_insn (basic_block bb)
+{
+  struct value_data *vd;
+  vd = XNEWVEC (struct value_data, 1);
+  init_value_data (vd);
+
+  skip_debug_insn_p = true;
+  copyprop_hardreg_forward_1 (bb, vd);
+  free (vd);
+  skip_debug_insn_p = false;
+}
+
 #ifdef ENABLE_CHECKING
 static void
 validate_value_data (struct value_data *vd)
@@ -1222,30 +1189,122 @@ validate_value_data (struct value_data *vd)
 }
 #endif
 \f
-static bool
-gate_handle_cprop (void)
+namespace {
+
+const pass_data pass_data_cprop_hardreg =
+{
+  RTL_PASS, /* type */
+  "cprop_hardreg", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_CPROP_REGISTERS, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_df_finish, /* todo_flags_finish */
+};
+
+class pass_cprop_hardreg : public rtl_opt_pass
+{
+public:
+  pass_cprop_hardreg (gcc::context *ctxt)
+    : rtl_opt_pass (pass_data_cprop_hardreg, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *)
+    {
+      return (optimize > 0 && (flag_cprop_registers));
+    }
+
+  virtual unsigned int execute (function *);
+
+}; // class pass_cprop_hardreg
+
+unsigned int
+pass_cprop_hardreg::execute (function *fun)
 {
-  return (optimize > 0 && (flag_cprop_registers));
+  struct value_data *all_vd;
+  basic_block bb;
+  sbitmap visited;
+  bool analyze_called = false;
+
+  all_vd = XNEWVEC (struct value_data, last_basic_block_for_fn (fun));
+
+  visited = sbitmap_alloc (last_basic_block_for_fn (fun));
+  bitmap_clear (visited);
+
+  FOR_EACH_BB_FN (bb, fun)
+    {
+      bitmap_set_bit (visited, bb->index);
+
+      /* If a block has a single predecessor, that we've already
+        processed, begin with the value data that was live at
+        the end of the predecessor block.  */
+      /* ??? Ought to use more intelligent queuing of blocks.  */
+      if (single_pred_p (bb)
+         && bitmap_bit_p (visited, single_pred (bb)->index)
+         && ! (single_pred_edge (bb)->flags & (EDGE_ABNORMAL_CALL | EDGE_EH)))
+       {
+         all_vd[bb->index] = all_vd[single_pred (bb)->index];
+         if (all_vd[bb->index].n_debug_insn_changes)
+           {
+             unsigned int regno;
+
+             for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+               {
+                 if (all_vd[bb->index].e[regno].debug_insn_changes)
+                   {
+                     all_vd[bb->index].e[regno].debug_insn_changes = NULL;
+                     if (--all_vd[bb->index].n_debug_insn_changes == 0)
+                       break;
+                   }
+               }
+           }
+       }
+      else
+       init_value_data (all_vd + bb->index);
+
+      copyprop_hardreg_forward_1 (bb, all_vd + bb->index);
+    }
+
+  if (MAY_HAVE_DEBUG_INSNS)
+    {
+      FOR_EACH_BB_FN (bb, fun)
+       if (bitmap_bit_p (visited, bb->index)
+           && all_vd[bb->index].n_debug_insn_changes)
+         {
+           unsigned int regno;
+           bitmap live;
+
+           if (!analyze_called)
+             {
+               df_analyze ();
+               analyze_called = true;
+             }
+           live = df_get_live_out (bb);
+           for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+             if (all_vd[bb->index].e[regno].debug_insn_changes)
+               {
+                 if (REGNO_REG_SET_P (live, regno))
+                   apply_debug_insn_changes (all_vd + bb->index, regno);
+                 if (all_vd[bb->index].n_debug_insn_changes == 0)
+                   break;
+               }
+         }
+
+      queued_debug_insn_change_pool.release ();
+    }
+
+  sbitmap_free (visited);
+  free (all_vd);
+  return 0;
 }
 
+} // anon namespace
 
-struct rtl_opt_pass pass_cprop_hardreg =
+rtl_opt_pass *
+make_pass_cprop_hardreg (gcc::context *ctxt)
 {
- {
-  RTL_PASS,
-  "cprop_hardreg",                      /* name */
-  OPTGROUP_NONE,                        /* optinfo_flags */
-  gate_handle_cprop,                    /* gate */
-  copyprop_hardreg_forward,             /* execute */
-  NULL,                                 /* sub */
-  NULL,                                 /* next */
-  0,                                    /* static_pass_number */
-  TV_CPROP_REGISTERS,                   /* tv_id */
-  0,                                    /* properties_required */
-  0,                                    /* properties_provided */
-  0,                                    /* properties_destroyed */
-  0,                                    /* todo_flags_start */
-  TODO_df_finish
-  | TODO_verify_rtl_sharing            /* todo_flags_finish */
- }
-};
+  return new pass_cprop_hardreg (ctxt);
+}