]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/regcprop.c
PR fortran/95090 - ICE: identifier overflow
[thirdparty/gcc.git] / gcc / regcprop.c
index 262de1be046ff971f892941d2d533cb279f99a43..d2a01130fe14ca9a7c2de17ab2ccf5c4fc52065d 100644 (file)
@@ -1,5 +1,5 @@
 /* Copy propagation on hard registers for the GNU compiler.
-   Copyright (C) 2000-2014 Free Software Foundation, Inc.
+   Copyright (C) 2000-2020 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 "memmodel.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 "hashtab.h"
-#include "hash-set.h"
-#include "vec.h"
-#include "machmode.h"
-#include "input.h"
-#include "function.h"
+#include "emit-rtl.h"
 #include "recog.h"
-#include "flags.h"
 #include "diagnostic-core.h"
-#include "obstack.h"
+#include "addresses.h"
 #include "tree-pass.h"
-#include "df.h"
 #include "rtl-iter.h"
+#include "cfgrtl.h"
+#include "target.h"
+#include "function-abi.h"
 
 /* The following code does forward propagation of hard register copies.
    The object is to eliminate as many dependencies as possible, so that
@@ -69,7 +63,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;
@@ -82,33 +76,33 @@ 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");
+
 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 (const_rtx, struct value_data *);
-static void set_value_regno (unsigned, enum machine_mode, 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 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_insn *,
                                      struct value_data *);
 static bool replace_oldest_value_addr (rtx *, enum reg_class,
-                                      enum machine_mode, addr_space_t,
+                                      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
 static void validate_value_data (struct value_data *);
-#endif
 
 /* Free all queued updates for DEBUG_INSNs that change some reg to
    register REGNO.  */
@@ -121,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;
 }
@@ -156,9 +150,8 @@ kill_value_one_regno (unsigned int regno, struct value_data *vd)
   if (vd->e[regno].debug_insn_changes)
     free_debug_insn_changes (vd, regno);
 
-#ifdef ENABLE_CHECKING
-  validate_value_data (vd);
-#endif
+  if (flag_checking)
+    validate_value_data (vd);
 }
 
 /* Kill the value in register REGNO for NREGS, and any other registers
@@ -184,7 +177,7 @@ kill_value_regno (unsigned int regno, unsigned int nregs,
       unsigned int i, n;
       if (vd->e[j].mode == VOIDmode)
        continue;
-      n = hard_regno_nregs[j][vd->e[j].mode];
+      n = hard_regno_nregs (j, vd->e[j].mode);
       if (j + n > regno)
        for (i = 0; i < n; ++i)
          kill_value_one_regno (j + i, vd);
@@ -204,25 +197,20 @@ kill_value (const_rtx x, struct value_data *vd)
       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;
 
   vd->e[regno].mode = mode;
 
-  nregs = hard_regno_nregs[regno][mode];
+  nregs = hard_regno_nregs (regno, mode);
   if (nregs > vd->max_value_regs)
     vd->max_value_regs = nregs;
 }
@@ -250,6 +238,7 @@ static void
 kill_clobbered_value (rtx x, const_rtx set, void *data)
 {
   struct value_data *const vd = (struct value_data *) data;
+
   if (GET_CODE (set) == CLOBBER)
     kill_value (x, vd);
 }
@@ -270,6 +259,7 @@ kill_set_value (rtx x, const_rtx set, void *data)
   struct kill_set_value_data *ksvd = (struct kill_set_value_data *) data;
   if (rtx_equal_p (x, ksvd->ignore_set_reg))
     return;
+
   if (GET_CODE (set) != CLOBBER)
     {
       kill_value (x, ksvd->vd);
@@ -282,7 +272,7 @@ kill_set_value (rtx x, const_rtx set, void *data)
    and install that register as the root of its own value list.  */
 
 static void
-kill_autoinc_value (rtx insn, struct value_data *vd)
+kill_autoinc_value (rtx_insn *insn, struct value_data *vd)
 {
   subrtx_iterator::array_type array;
   FOR_EACH_SUBRTX (iter, array, PATTERN (insn), NONCONST)
@@ -330,8 +320,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;
@@ -348,7 +338,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:
@@ -357,15 +347,15 @@ copy_value (rtx dest, rtx src, struct value_data *vd)
 
      We can't properly represent the latter case in our tables, so don't
      record anything then.  */
-  else if (sn < (unsigned int) hard_regno_nregs[sr][vd->e[sr].mode]
-          && (GET_MODE_SIZE (vd->e[sr].mode) > UNITS_PER_WORD
-              ? WORDS_BIG_ENDIAN : BYTES_BIG_ENDIAN))
+  else if (sn < hard_regno_nregs (sr, vd->e[sr].mode)
+          && maybe_ne (subreg_lowpart_offset (GET_MODE (dest),
+                                              vd->e[sr].mode), 0U))
     return;
 
   /* If SRC had been assigned a mode narrower than the copy, we can't
      link DEST into the chain, because not all of the pieces of the
      copy came from oldest_regno.  */
-  else if (sn > (unsigned int) hard_regno_nregs[sr][vd->e[sr].mode])
+  else if (sn > hard_regno_nregs (sr, vd->e[sr].mode))
     return;
 
   /* Link DR at the end of the value chain used by SR.  */
@@ -376,25 +366,20 @@ copy_value (rtx dest, rtx src, struct value_data *vd)
     continue;
   vd->e[i].next_regno = dr;
 
-#ifdef ENABLE_CHECKING
-  validate_value_data (vd);
-#endif
+  if (flag_checking)
+    validate_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))
+  if (partial_subreg_p (orig_mode, new_mode))
     return false;
 
-#ifdef CANNOT_CHANGE_MODE_CLASS
-  return !REG_CANNOT_CHANGE_MODE_P (regno, orig_mode, new_mode);
-#endif
-
-  return true;
+  return REG_CAN_CHANGE_MODE_P (regno, orig_mode, new_mode);
 }
 
 /* Register REGNO was originally set in ORIG_MODE.  It - or a copy of it -
@@ -403,32 +388,38 @@ 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)
-      && GET_MODE_SIZE (copy_mode) < GET_MODE_SIZE (new_mode))
+  if (partial_subreg_p (copy_mode, orig_mode)
+      && partial_subreg_p (copy_mode, new_mode))
+    return NULL_RTX;
+
+  /* Avoid creating multiple copies of the stack pointer.  Some ports
+     assume there is one and only one stack pointer.
+
+     It's unclear if we need to do the same for other special registers.  */
+  if (regno == STACK_POINTER_REGNUM)
     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];
-      int use_nregs = hard_regno_nregs[copy_regno][new_mode];
-      int copy_offset
-       = GET_MODE_SIZE (copy_mode) / copy_nregs * (copy_nregs - use_nregs);
-      int offset
-       = GET_MODE_SIZE (orig_mode) - GET_MODE_SIZE (new_mode) - copy_offset;
-      int byteoffset = offset % UNITS_PER_WORD;
-      int wordoffset = offset - byteoffset;
-
-      offset = ((WORDS_BIG_ENDIAN ? wordoffset : 0)
-               + (BYTES_BIG_ENDIAN ? byteoffset : 0));
+      int copy_nregs = hard_regno_nregs (copy_regno, copy_mode);
+      int use_nregs = hard_regno_nregs (copy_regno, new_mode);
+      poly_uint64 bytes_per_reg;
+      if (!can_div_trunc_p (GET_MODE_SIZE (copy_mode),
+                           copy_nregs, &bytes_per_reg))
+       return NULL_RTX;
+      poly_uint64 copy_offset = bytes_per_reg * (copy_nregs - use_nregs);
+      poly_uint64 offset
+       = subreg_size_lowpart_offset (GET_MODE_SIZE (new_mode) + copy_offset,
+                                     GET_MODE_SIZE (orig_mode));
       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);
+      if (targetm.hard_regno_mode_ok (regno, new_mode))
+       return gen_raw_REG (new_mode, regno);
     }
   return NULL_RTX;
 }
@@ -441,9 +432,11 @@ 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;
 
+  gcc_assert (regno < FIRST_PSEUDO_REGISTER);
+
   /* If we are accessing REG in some mode other that what we set it in,
      make sure that the replacement is valid.  In particular, consider
        (set (reg:DI r11) (...))
@@ -451,16 +444,13 @@ find_oldest_value_reg (enum reg_class cl, rtx reg, struct value_data *vd)
        (set (reg:SI r10) (...))
        (set (...) (reg:DI r9))
      Replacing r9 with r11 is invalid.  */
-  if (mode != vd->e[regno].mode)
-    {
-      if (hard_regno_nregs[regno][mode]
-         > hard_regno_nregs[regno][vd->e[regno].mode])
-       return NULL_RTX;
-    }
+  if (mode != vd->e[regno].mode
+      && REG_NREGS (reg) > hard_regno_nregs (regno, vd->e[regno].mode))
+    return NULL_RTX;
 
   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))
@@ -497,8 +487,7 @@ replace_oldest_value_reg (rtx *loc, enum reg_class cl, rtx_insn *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;
@@ -523,7 +512,7 @@ replace_oldest_value_reg (rtx *loc, enum reg_class cl, rtx_insn *insn,
 
 static bool
 replace_oldest_value_addr (rtx *loc, enum reg_class cl,
-                          enum machine_mode mode, addr_space_t as,
+                          machine_mode mode, addr_space_t as,
                           rtx_insn *insn, struct value_data *vd)
 {
   rtx x = *loc;
@@ -731,27 +720,35 @@ cprop_find_used_regs (rtx *loc, void *data)
     }
 }
 
+/* Apply clobbers of INSN in PATTERN and C_I_F_U to value_data VD.  */
+
+static void
+kill_clobbered_values (rtx_insn *insn, struct value_data *vd)
+{
+  note_stores (insn, kill_clobbered_value, vd);
+}
+
 /* Perform the forward copy propagation on basic block BB.  */
 
 static bool
 copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
 {
   bool anything_changed = false;
-  rtx_insn *insn;
+  rtx_insn *insn, *next;
 
-  for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn))
+  for (insn = BB_HEAD (bb); ; insn = next)
     {
       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;
 
+      next = NEXT_INSN (insn);
       if (!NONDEBUG_INSN_P (insn))
        {
-         if (DEBUG_INSN_P (insn))
+         if (DEBUG_BIND_INSN_P (insn))
            {
              rtx loc = INSN_VAR_LOCATION_LOC (insn);
              if (!VAR_LOC_UNKNOWN_P (loc))
@@ -767,9 +764,42 @@ 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);
+
+      /* Detect noop sets and remove them before processing side effects.  */
+      if (set && REG_P (SET_DEST (set)) && REG_P (SET_SRC (set)))
+       {
+         unsigned int regno = REGNO (SET_SRC (set));
+         rtx r1 = find_oldest_value_reg (REGNO_REG_CLASS (regno),
+                                         SET_DEST (set), vd);
+         rtx r2 = find_oldest_value_reg (REGNO_REG_CLASS (regno),
+                                         SET_SRC (set), vd);
+         if (rtx_equal_p (r1 ? r1 : SET_DEST (set), r2 ? r2 : SET_SRC (set)))
+           {
+             bool last = insn == BB_END (bb);
+             delete_insn (insn);
+             if (last)
+               break;
+             continue;
+           }
+       }
+
+      /* Detect obviously dead sets (via REG_UNUSED notes) and remove them.  */
+      if (set
+         && !RTX_FRAME_RELATED_P (insn)
+         && !may_trap_p (set)
+         && find_reg_note (insn, REG_UNUSED, SET_DEST (set))
+         && !side_effects_p (SET_SRC (set))
+         && !side_effects_p (SET_DEST (set)))
+       {
+         bool last = insn == BB_END (bb);
+         delete_insn (insn);
+         if (last)
+           break;
+         continue;
+       }
+        
+
+      extract_constrain_insn (insn);
       preprocess_constraints (insn);
       const operand_alternative *op_alt = which_op_alt ();
       n_ops = recog_data.n_operands;
@@ -799,7 +829,7 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *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.  */
@@ -825,6 +855,12 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
                  && reg_overlap_mentioned_p (XEXP (link, 0), SET_SRC (set)))
                set = NULL;
            }
+
+         /* We need to keep CFI info correct, and the same on all paths,
+            so we cannot normally replace the registers REG_CFA_REGISTER
+            refers to.  Bail.  */
+         if (REG_NOTE_KIND (link) == REG_CFA_REGISTER)
+           goto did_replacement;
        }
 
       /* Special-case plain move instructions, since we may well
@@ -833,7 +869,7 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
        {
          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;
 
@@ -841,16 +877,15 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
             set it in, make sure that the replacement is valid.  */
          if (mode != vd->e[regno].mode)
            {
-             if (hard_regno_nregs[regno][mode]
-                 > hard_regno_nregs[regno][vd->e[regno].mode])
+             if (REG_NREGS (src)
+                 > hard_regno_nregs (regno, vd->e[regno].mode))
                goto no_move_special_case;
 
              /* And likewise, if we are narrowing on big endian the transformation
                 is also invalid.  */
-             if (hard_regno_nregs[regno][mode]
-                 < hard_regno_nregs[regno][vd->e[regno].mode]
-                 && (GET_MODE_SIZE (vd->e[regno].mode) > UNITS_PER_WORD
-                     ? WORDS_BIG_ENDIAN : BYTES_BIG_ENDIAN))
+             if (REG_NREGS (src) < hard_regno_nregs (regno, vd->e[regno].mode)
+                 && maybe_ne (subreg_lowpart_offset (mode,
+                                                     vd->e[regno].mode), 0U))
                goto no_move_special_case;
            }
 
@@ -858,7 +893,9 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
             register in the same class.  */
          if (REG_P (SET_DEST (set)))
            {
-             new_rtx = find_oldest_value_reg (REGNO_REG_CLASS (regno), src, vd);
+             new_rtx = find_oldest_value_reg (REGNO_REG_CLASS (regno),
+                                              src, vd);
+
              if (new_rtx && validate_change (insn, &SET_SRC (set), new_rtx, 0))
                {
                  if (dump_file)
@@ -870,9 +907,7 @@ 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);
+             extract_constrain_insn (insn);
              preprocess_constraints (insn);
            }
 
@@ -898,9 +933,7 @@ 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);
+                 extract_constrain_insn (insn);
                  preprocess_constraints (insn);
                }
            }
@@ -913,7 +946,7 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
         eldest live copy that's in an appropriate register class.  */
       for (i = 0; i < n_ops; i++)
        {
-         replaced[i] = false;
+         bool replaced = false;
 
          /* Don't scan match_operand here, since we've no reg class
             information to pass down.  Any operands that we could
@@ -930,26 +963,26 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
          if (recog_data.operand_type[i] == OP_IN)
            {
              if (op_alt[i].is_address)
-               replaced[i]
+               replaced
                  = replace_oldest_value_addr (recog_data.operand_loc[i],
                                               alternative_class (op_alt, i),
                                               VOIDmode, ADDR_SPACE_GENERIC,
                                               insn, vd);
              else if (REG_P (recog_data.operand[i]))
-               replaced[i]
+               replaced
                  = replace_oldest_value_reg (recog_data.operand_loc[i],
                                              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],
-                                                       insn, vd);
+               replaced = replace_oldest_value_mem (recog_data.operand[i],
+                                                    insn, vd);
            }
          else if (MEM_P (recog_data.operand[i]))
-           replaced[i] = replace_oldest_value_mem (recog_data.operand[i],
-                                                   insn, vd);
+           replaced = replace_oldest_value_mem (recog_data.operand[i],
+                                                insn, vd);
 
          /* If we performed any replacement, update match_dups.  */
-         if (replaced[i])
+         if (replaced)
            {
              int j;
              rtx new_rtx;
@@ -968,13 +1001,6 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
        {
          if (! apply_change_group ())
            {
-             for (i = 0; i < n_ops; i++)
-               if (replaced[i])
-                 {
-                   rtx old = *recog_data.operand_loc[i];
-                   recog_data.operand[i] = old;
-                 }
-
              if (dump_file)
                fprintf (dump_file,
                         "insn %u: reg replacements not verified\n",
@@ -993,6 +1019,7 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
             DEBUG_INSNs can be applied.  */
          if (vd->n_debug_insn_changes)
            note_uses (&PATTERN (insn), cprop_find_used_regs, vd);
+         df_insn_rescan (insn);
        }
 
       ksvd.vd = vd;
@@ -1005,7 +1032,6 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
          unsigned int set_nregs = 0;
          unsigned int regno;
          rtx exp;
-         HARD_REG_SET regs_invalidated_by_this_call;
 
          for (exp = CALL_INSN_FUNCTION_USAGE (insn); exp; exp = XEXP (exp, 1))
            {
@@ -1018,37 +1044,24 @@ 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;
                }
            }
 
-         get_call_reg_set_usage (insn,
-                                 &regs_invalidated_by_this_call,
-                                 regs_invalidated_by_call);
+         function_abi callee_abi = insn_callee_abi (insn);
          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))
+           if (vd->e[regno].mode != VOIDmode
+               && callee_abi.clobbers_reg_p (vd->e[regno].mode, regno)
                && (regno < set_regno || regno >= set_regno + set_nregs))
              kill_value_regno (regno, 1, vd);
 
          /* 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.  */
+            of the SET isn't clobbered by CALLEE_ABI, but instead among
+            CLOBBERs on the CALL_INSN, we could wrongly assume the
+            value in it is still live.  */
          if (ksvd.ignore_set_reg)
-           {
-             note_stores (PATTERN (insn), kill_clobbered_value, vd);
-             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);
-               }
-           }
+           kill_clobbered_values (insn, vd);
        }
 
       bool copy_p = (set
@@ -1057,14 +1070,34 @@ copyprop_hardreg_forward_1 (basic_block bb, struct value_data *vd)
       bool noop_p = (copy_p
                     && rtx_equal_p (SET_DEST (set), SET_SRC (set)));
 
+      /* If a noop move is using narrower mode than we have recorded,
+        we need to either remove the noop move, or kill_set_value.  */
+      if (noop_p
+         && partial_subreg_p (GET_MODE (SET_DEST (set)),
+                              vd->e[REGNO (SET_DEST (set))].mode))
+       {
+         if (noop_move_p (insn))
+           {
+             bool last = insn == BB_END (bb);
+             delete_insn (insn);
+             if (last)
+               break;
+           }
+         else
+           noop_p = false;
+       }
+
       if (!noop_p)
        {
          /* Notice stores.  */
-         note_stores (PATTERN (insn), kill_set_value, &ksvd);
+         note_stores (insn, kill_set_value, &ksvd);
 
          /* Notice copies.  */
          if (copy_p)
-           copy_value (SET_DEST (set), SET_SRC (set), vd);
+           {
+             df_insn_rescan (insn);
+             copy_value (SET_DEST (set), SET_SRC (set), vd);
+           }
        }
 
       if (insn == BB_END (bb))
@@ -1150,7 +1183,6 @@ copyprop_hardreg_forward_bb_without_debug_insn (basic_block bb)
   skip_debug_insn_p = false;
 }
 
-#ifdef ENABLE_CHECKING
 static void
 validate_value_data (struct value_data *vd)
 {
@@ -1165,8 +1197,8 @@ validate_value_data (struct value_data *vd)
        if (vd->e[i].mode == VOIDmode)
          {
            if (vd->e[i].next_regno != INVALID_REGNUM)
-             internal_error ("validate_value_data: [%u] Bad next_regno for empty chain (%u)",
-                             i, vd->e[i].next_regno);
+             internal_error ("%qs: [%u] bad %<next_regno%> for empty chain (%u)",
+                             __func__, i, vd->e[i].next_regno);
            continue;
          }
 
@@ -1177,11 +1209,11 @@ validate_value_data (struct value_data *vd)
             j = vd->e[j].next_regno)
          {
            if (TEST_HARD_REG_BIT (set, j))
-             internal_error ("validate_value_data: Loop in regno chain (%u)",
-                             j);
+             internal_error ("%qs: loop in %<next_regno%> chain (%u)",
+                             __func__, j);
            if (vd->e[j].oldest_regno != i)
-             internal_error ("validate_value_data: [%u] Bad oldest_regno (%u)",
-                             j, vd->e[j].oldest_regno);
+             internal_error ("%qs: [%u] bad %<oldest_regno%> (%u)",
+                             __func__, j, vd->e[j].oldest_regno);
 
            SET_HARD_REG_BIT (set, j);
          }
@@ -1192,11 +1224,12 @@ validate_value_data (struct value_data *vd)
        && (vd->e[i].mode != VOIDmode
            || vd->e[i].oldest_regno != i
            || vd->e[i].next_regno != INVALID_REGNUM))
-      internal_error ("validate_value_data: [%u] Non-empty reg in chain (%s %u %i)",
-                     i, GET_MODE_NAME (vd->e[i].mode), vd->e[i].oldest_regno,
+      internal_error ("%qs: [%u] non-empty register in chain (%s %u %i)",
+                     __func__, i,
+                     GET_MODE_NAME (vd->e[i].mode), vd->e[i].oldest_regno,
                      vd->e[i].next_regno);
 }
-#endif
+
 \f
 namespace {
 
@@ -1230,87 +1263,142 @@ public:
 
 }; // class pass_cprop_hardreg
 
+static bool
+cprop_hardreg_bb (basic_block bb, struct value_data *all_vd, sbitmap visited)
+{
+  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)
+               {
+                 struct queued_debug_insn_change *cur;
+                 for (cur = all_vd[bb->index].e[regno].debug_insn_changes;
+                      cur; cur = cur->next)
+                   --all_vd[bb->index].n_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);
+
+  return copyprop_hardreg_forward_1 (bb, all_vd + bb->index);
+}
+
+static void
+cprop_hardreg_debug (function *fun, struct value_data *all_vd)
+{
+  basic_block bb;
+
+  FOR_EACH_BB_FN (bb, fun)
+    if (all_vd[bb->index].n_debug_insn_changes)
+      {
+       unsigned int regno;
+       bitmap live;
+
+       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);
+
+             struct queued_debug_insn_change *cur;
+             for (cur = all_vd[bb->index].e[regno].debug_insn_changes;
+                  cur; cur = cur->next)
+               --all_vd[bb->index].n_debug_insn_changes;
+             all_vd[bb->index].e[regno].debug_insn_changes = NULL;
+             if (all_vd[bb->index].n_debug_insn_changes == 0)
+               break;
+           }
+      }
+
+  queued_debug_insn_change_pool.release ();
+}
+
 unsigned int
 pass_cprop_hardreg::execute (function *fun)
 {
   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));
+  auto_sbitmap visited (last_basic_block_for_fn (fun));
   bitmap_clear (visited);
 
-  if (MAY_HAVE_DEBUG_INSNS)
-    debug_insn_changes_pool
-      = create_alloc_pool ("debug insn changes pool",
-                          sizeof (struct queued_debug_insn_change), 256);
+  auto_vec<int> worklist;
+  bool any_debug_changes = false;
+
+  /* We need accurate notes.  Earlier passes such as if-conversion may
+     leave notes in an inconsistent state.  */
+  df_note_add_problem ();
+  df_analyze ();
+
+  /* It is tempting to set DF_LR_RUN_DCE, but DCE may choose to delete
+     an insn and this pass would not have visibility into the removal.
+     This pass would then potentially use the source of that
+     INSN for propagation purposes, generating invalid code.
+
+     So we just ask for updated notes and handle trivial deletions
+     within this pass where we can update this passes internal
+     data structures appropriately.  */
+  df_set_flags (DF_DEFER_INSN_RESCAN);
 
   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;
+      if (cprop_hardreg_bb (bb, all_vd, visited))
+       worklist.safe_push (bb->index);
+      if (all_vd[bb->index].n_debug_insn_changes)
+       any_debug_changes = true;
+    }
 
-             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);
+  /* We must call df_analyze here unconditionally to ensure that the
+     REG_UNUSED and REG_DEAD notes are consistent with and without -g.  */
+  df_analyze ();
 
-      copyprop_hardreg_forward_1 (bb, all_vd + bb->index);
-    }
+  if (MAY_HAVE_DEBUG_BIND_INSNS && any_debug_changes)
+    cprop_hardreg_debug (fun, all_vd);
 
-  if (MAY_HAVE_DEBUG_INSNS)
+  /* Second pass if we've changed anything, only for the bbs where we have
+     changed anything though.  */
+  if (!worklist.is_empty ())
     {
-      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;
+      unsigned int i;
+      int index;
 
-           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;
-               }
-         }
+      any_debug_changes = false;
+      bitmap_clear (visited);
+      FOR_EACH_VEC_ELT (worklist, i, index)
+       {
+         bb = BASIC_BLOCK_FOR_FN (fun, index);
+         cprop_hardreg_bb (bb, all_vd, visited);
+         if (all_vd[bb->index].n_debug_insn_changes)
+           any_debug_changes = true;
+       }
 
-      free_alloc_pool (debug_insn_changes_pool);
+      df_analyze ();
+      if (MAY_HAVE_DEBUG_BIND_INSNS && any_debug_changes)
+       cprop_hardreg_debug (fun, all_vd);
     }
 
-  sbitmap_free (visited);
   free (all_vd);
   return 0;
 }