[26/46] Make more use of dyn_cast in tree-vect*
[gcc.git] / gcc / regrename.c
index 6d237d162ca9c5734793de38a339dace4034de58..84240930e3a5b14ec648fb977b9323580b585233 100644 (file)
@@ -1,6 +1,5 @@
 /* Register renaming for the GNU compiler.
-   Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
-   2010, 2011, 2012 Free Software Foundation, Inc.
+   Copyright (C) 2000-2018 Free Software Foundation, Inc.
 
    This file is part of GCC.
 
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "tm.h"
-#include "rtl-error.h"
+#include "backend.h"
+#include "target.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 "output.h"
-#include "function.h"
+#include "emit-rtl.h"
 #include "recog.h"
-#include "flags.h"
-#include "obstack.h"
+#include "addresses.h"
+#include "cfganal.h"
 #include "tree-pass.h"
-#include "df.h"
-#include "target.h"
-#include "emit-rtl.h"
 #include "regrename.h"
 
 /* This file implements the RTL register renaming pass of the compiler.  It is
      5. If a renaming register has been found, it is substituted in the chain.
 
   Targets can parameterize the pass by specifying a preferred class for the
-  renaming register for a given (super)class of registers to be renamed.  */
+  renaming register for a given (super)class of registers to be renamed.
+
+  DEBUG_INSNs are treated specially, in particular registers occurring inside
+  them are treated as requiring ALL_REGS as a class.  */
 
 #if HOST_BITS_PER_WIDE_INT <= MAX_RECOG_OPERANDS
 #error "Use a different bitmap implementation for untracked_operands."
@@ -108,7 +105,7 @@ static struct obstack rename_obstack;
    information about insn operands, and we store it here.  */
 vec<insn_rr_info> insn_rr;
 
-static void scan_rtx (rtx, rtx *, enum reg_class, enum scan_actions,
+static void scan_rtx (rtx_insn *, rtx *, enum reg_class, enum scan_actions,
                      enum op_type);
 static bool build_def_use (basic_block);
 
@@ -137,6 +134,9 @@ static HARD_REG_SET live_hard_regs;
    record_operand_use.  */
 static operand_rr_info *cur_operand;
 
+/* Set while scanning RTL if a register dies.  Used to tie chains.  */
+static struct du_head *terminated_this_insn;
+
 /* Return the chain corresponding to id number ID.  Take into account that
    chains may have been merged.  */
 du_head_p
@@ -207,8 +207,13 @@ mark_conflict (struct du_head *chains, unsigned id)
 static void
 record_operand_use (struct du_head *head, struct du_chain *this_du)
 {
-  if (cur_operand == NULL)
+  if (cur_operand == NULL || cur_operand->failed)
     return;
+  if (head->cannot_rename)
+    {
+      cur_operand->failed = true;
+      return;
+    }
   gcc_assert (cur_operand->n_chains < MAX_REGS_PER_ADDRESS);
   cur_operand->heads[cur_operand->n_chains] = head;
   cur_operand->chains[cur_operand->n_chains++] = this_du;
@@ -220,17 +225,16 @@ record_operand_use (struct du_head *head, struct du_chain *this_du)
 
 static du_head_p
 create_new_chain (unsigned this_regno, unsigned this_nregs, rtx *loc,
-                 rtx insn, enum reg_class cl)
+                 rtx_insn *insn, enum reg_class cl)
 {
   struct du_head *head = XOBNEW (&rename_obstack, struct du_head);
   struct du_chain *this_du;
   int nregs;
 
+  memset (head, 0, sizeof *head);
   head->next_chain = open_chains;
   head->regno = this_regno;
   head->nregs = this_nregs;
-  head->need_caller_save_reg = 0;
-  head->cannot_rename = 0;
 
   id_to_chain.safe_push (head);
   head->id = current_id++;
@@ -307,8 +311,8 @@ static bool
 check_new_reg_p (int reg ATTRIBUTE_UNUSED, int new_reg,
                 struct du_head *this_head, HARD_REG_SET this_unavailable)
 {
-  enum machine_mode mode = GET_MODE (*this_head->first->loc);
-  int nregs = hard_regno_nregs[new_reg][mode];
+  machine_mode mode = GET_MODE (*this_head->first->loc);
+  int nregs = hard_regno_nregs (new_reg, mode);
   int i;
   struct du_chain *tmp;
 
@@ -325,21 +329,18 @@ check_new_reg_p (int reg ATTRIBUTE_UNUSED, int new_reg,
        || (crtl->is_leaf
            && !LEAF_REGISTERS[new_reg + i])
 #endif
-#ifdef HARD_REGNO_RENAME_OK
-       || ! HARD_REGNO_RENAME_OK (reg + i, new_reg + i)
-#endif
-       )
+       || ! HARD_REGNO_RENAME_OK (reg + i, new_reg + i))
       return false;
 
   /* See whether it accepts all modes that occur in
      definition and uses.  */
   for (tmp = this_head->first; tmp; tmp = tmp->next_use)
-    if ((! HARD_REGNO_MODE_OK (new_reg, GET_MODE (*tmp->loc))
+    if ((!targetm.hard_regno_mode_ok (new_reg, GET_MODE (*tmp->loc))
         && ! DEBUG_INSN_P (tmp->insn))
        || (this_head->need_caller_save_reg
-           && ! (HARD_REGNO_CALL_PART_CLOBBERED
+           && ! (targetm.hard_regno_call_part_clobbered
                  (reg, GET_MODE (*tmp->loc)))
-           && (HARD_REGNO_CALL_PART_CLOBBERED
+           && (targetm.hard_regno_call_part_clobbered
                (new_reg, GET_MODE (*tmp->loc)))))
       return false;
 
@@ -349,11 +350,13 @@ check_new_reg_p (int reg ATTRIBUTE_UNUSED, int new_reg,
 /* For the chain THIS_HEAD, compute and return the best register to
    rename to.  SUPER_CLASS is the superunion of register classes in
    the chain.  UNAVAILABLE is a set of registers that cannot be used.
-   OLD_REG is the register currently used for the chain.  */
+   OLD_REG is the register currently used for the chain.  BEST_RENAME
+   controls whether the register chosen must be better than the
+   current one or just respect the given constraint.  */
 
 int
-find_best_rename_reg (du_head_p this_head, enum reg_class super_class,
-                     HARD_REG_SET *unavailable, int old_reg)
+find_rename_reg (du_head_p this_head, enum reg_class super_class,
+                HARD_REG_SET *unavailable, int old_reg, bool best_rename)
 {
   bool has_preferred_class;
   enum reg_class preferred_class;
@@ -374,6 +377,13 @@ find_best_rename_reg (du_head_p this_head, enum reg_class super_class,
   preferred_class
     = (enum reg_class) targetm.preferred_rename_class (super_class);
 
+  /* Pick and check the register from the tied chain iff the tied chain
+     is not renamed.  */
+  if (this_head->tied_chain && !this_head->tied_chain->renamed
+      && check_new_reg_p (old_reg, this_head->tied_chain->regno,
+                         this_head, *unavailable))
+    return this_head->tied_chain->regno;
+
   /* If PREFERRED_CLASS is not NO_REGS, we iterate in the first pass
      over registers that belong to PREFERRED_CLASS and try to find the
      best register within the class.  If that failed, we iterate in
@@ -392,15 +402,19 @@ find_best_rename_reg (du_head_p this_head, enum reg_class super_class,
                                    new_reg))
            continue;
 
+         if (!check_new_reg_p (old_reg, new_reg, this_head, *unavailable))
+           continue;
+
+         if (!best_rename)
+           return new_reg;
+
          /* In the first pass, we force the renaming of registers that
             don't belong to PREFERRED_CLASS to registers that do, even
             though the latters were used not very long ago.  */
-         if (check_new_reg_p (old_reg, new_reg, this_head,
-                              *unavailable)
-             && ((pass == 0
-                  && !TEST_HARD_REG_BIT (reg_class_contents[preferred_class],
-                                         best_new_reg))
-                 || tick[best_new_reg] > tick[new_reg]))
+         if ((pass == 0
+             && !TEST_HARD_REG_BIT (reg_class_contents[preferred_class],
+                                    best_new_reg))
+             || tick[best_new_reg] > tick[new_reg])
            best_new_reg = new_reg;
        }
       if (pass == 0 && best_new_reg != old_reg)
@@ -409,6 +423,33 @@ find_best_rename_reg (du_head_p this_head, enum reg_class super_class,
   return best_new_reg;
 }
 
+/* Iterate over elements in the chain HEAD in order to:
+   1. Count number of uses, storing it in *PN_USES.
+   2. Narrow the set of registers we can use for renaming, adding
+      unavailable registers to *PUNAVAILABLE, which must be
+      initialized by the caller.
+   3. Compute the superunion of register classes in this chain
+      and return it.  */
+reg_class
+regrename_find_superclass (du_head_p head, int *pn_uses,
+                          HARD_REG_SET *punavailable)
+{
+  int n_uses = 0;
+  reg_class super_class = NO_REGS;
+  for (du_chain *tmp = head->first; tmp; tmp = tmp->next_use)
+    {
+      if (DEBUG_INSN_P (tmp->insn))
+       continue;
+      n_uses++;
+      IOR_COMPL_HARD_REG_SET (*punavailable,
+                             reg_class_contents[tmp->cl]);
+      super_class
+       = reg_class_superunion[(int) super_class][(int) tmp->cl];
+    }
+  *pn_uses = n_uses;
+  return super_class;
+}
+
 /* Perform register renaming on the current function.  */
 static void
 rename_chains (void)
@@ -424,56 +465,36 @@ rename_chains (void)
   if (frame_pointer_needed)
     {
       add_to_hard_reg_set (&unavailable, Pmode, FRAME_POINTER_REGNUM);
-#if !HARD_FRAME_POINTER_IS_FRAME_POINTER
-      add_to_hard_reg_set (&unavailable, Pmode, HARD_FRAME_POINTER_REGNUM);
-#endif
+      if (!HARD_FRAME_POINTER_IS_FRAME_POINTER)
+       add_to_hard_reg_set (&unavailable, Pmode, HARD_FRAME_POINTER_REGNUM);
     }
 
   FOR_EACH_VEC_ELT (id_to_chain, i, this_head)
     {
       int best_new_reg;
       int n_uses;
-      struct du_chain *tmp;
       HARD_REG_SET this_unavailable;
       int reg = this_head->regno;
-      enum reg_class super_class = NO_REGS;
 
       if (this_head->cannot_rename)
        continue;
 
       if (fixed_regs[reg] || global_regs[reg]
-#if !HARD_FRAME_POINTER_IS_FRAME_POINTER
-         || (frame_pointer_needed && reg == HARD_FRAME_POINTER_REGNUM)
-#else
-         || (frame_pointer_needed && reg == FRAME_POINTER_REGNUM)
-#endif
-         )
+         || (!HARD_FRAME_POINTER_IS_FRAME_POINTER && frame_pointer_needed
+             && reg == HARD_FRAME_POINTER_REGNUM)
+         || (HARD_FRAME_POINTER_IS_FRAME_POINTER && frame_pointer_needed
+             && reg == FRAME_POINTER_REGNUM))
        continue;
 
       COPY_HARD_REG_SET (this_unavailable, unavailable);
 
-      /* Iterate over elements in the chain in order to:
-        1. Count number of uses, and narrow the set of registers we can
-           use for renaming.
-        2. Compute the superunion of register classes in this chain.  */
-      n_uses = 0;
-      super_class = NO_REGS;
-      for (tmp = this_head->first; tmp; tmp = tmp->next_use)
-       {
-         if (DEBUG_INSN_P (tmp->insn))
-           continue;
-         n_uses++;
-         IOR_COMPL_HARD_REG_SET (this_unavailable,
-                                 reg_class_contents[tmp->cl]);
-         super_class
-           = reg_class_superunion[(int) super_class][(int) tmp->cl];
-       }
-
+      reg_class super_class = regrename_find_superclass (this_head, &n_uses,
+                                                        &this_unavailable);
       if (n_uses < 2)
        continue;
 
-      best_new_reg = find_best_rename_reg (this_head, super_class,
-                                          &this_unavailable, reg);
+      best_new_reg = find_rename_reg (this_head, super_class,
+                                     &this_unavailable, reg, true);
 
       if (dump_file)
        {
@@ -491,12 +512,20 @@ rename_chains (void)
          continue;
        }
 
-      if (dump_file)
-       fprintf (dump_file, ", renamed as %s\n", reg_names[best_new_reg]);
-
-      regrename_do_replace (this_head, best_new_reg);
-      tick[best_new_reg] = ++this_tick;
-      df_set_regs_ever_live (best_new_reg, true);
+      if (regrename_do_replace (this_head, best_new_reg))
+       {
+         if (dump_file)
+           fprintf (dump_file, ", renamed as %s\n", reg_names[best_new_reg]);
+         tick[best_new_reg] = ++this_tick;
+         df_set_regs_ever_live (best_new_reg, true);
+       }
+      else
+       {
+         if (dump_file)
+           fprintf (dump_file, ", renaming as %s failed\n",
+                    reg_names[best_new_reg]);
+         tick[reg] = ++this_tick;
+       }
     }
 }
 
@@ -534,7 +563,7 @@ static void
 init_rename_info (struct bb_rename_info *p, basic_block bb)
 {
   int i;
-  df_ref *def_rec;
+  df_ref def;
   HARD_REG_SET start_chains_set;
 
   p->bb = bb;
@@ -546,12 +575,9 @@ init_rename_info (struct bb_rename_info *p, basic_block bb)
 
   CLEAR_HARD_REG_SET (live_in_chains);
   REG_SET_TO_HARD_REG_SET (live_hard_regs, df_get_live_in (bb));
-  for (def_rec = df_get_artificial_defs (bb->index); *def_rec; def_rec++)
-    {
-      df_ref def = *def_rec;
-      if (DF_REF_FLAGS (def) & DF_REF_AT_TOP)
-       SET_HARD_REG_BIT (live_hard_regs, DF_REF_REGNO (def));
-    }
+  FOR_EACH_ARTIFICIAL_DEF (def, bb->index)
+    if (DF_REF_FLAGS (def) & DF_REF_AT_TOP)
+      SET_HARD_REG_BIT (live_hard_regs, DF_REF_REGNO (def));
 
   /* Open chains based on information from (at least one) predecessor
      block.  This gives us a chance later on to combine chains across
@@ -580,7 +606,7 @@ init_rename_info (struct bb_rename_info *p, basic_block bb)
          du_head_p chain;
          if (dump_file)
            fprintf (dump_file, "opening incoming chain\n");
-         chain = create_new_chain (i, iri->nregs, NULL, NULL_RTX, NO_REGS);
+         chain = create_new_chain (i, iri->nregs, NULL, NULL, NO_REGS);
          bitmap_set_bit (&p->incoming_open_chains_set, chain->id);
        }
     }
@@ -669,13 +695,13 @@ regrename_analyze (bitmap bb_mask)
   int n_bbs;
   int *inverse_postorder;
 
-  inverse_postorder = XNEWVEC (int, last_basic_block);
+  inverse_postorder = XNEWVEC (int, last_basic_block_for_fn (cfun));
   n_bbs = pre_and_rev_post_order_compute (NULL, inverse_postorder, false);
 
   /* Gather some information about the blocks in this function.  */
-  rename_info = XCNEWVEC (struct bb_rename_info, n_basic_blocks);
+  rename_info = XCNEWVEC (struct bb_rename_info, n_basic_blocks_for_fn (cfun));
   i = 0;
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, cfun)
     {
       struct bb_rename_info *ri = rename_info + i;
       ri->bb = bb;
@@ -697,7 +723,7 @@ regrename_analyze (bitmap bb_mask)
 
   for (i = 0; i < n_bbs; i++)
     {
-      basic_block bb1 = BASIC_BLOCK (inverse_postorder[i]);
+      basic_block bb1 = BASIC_BLOCK_FOR_FN (cfun, inverse_postorder[i]);
       struct bb_rename_info *this_info;
       bool success;
       edge e;
@@ -725,7 +751,7 @@ regrename_analyze (bitmap bb_mask)
          open_chains = NULL;
          if (insn_rr.exists ())
            {
-             rtx insn;
+             rtx_insn *insn;
              FOR_BB_INSNS (bb1, insn)
                {
                  insn_rr_info *p = &insn_rr[INSN_UID (insn)];
@@ -779,7 +805,7 @@ regrename_analyze (bitmap bb_mask)
      We perform the analysis for both incoming and outgoing edges, but we
      only need to merge once (in the second part, after verifying outgoing
      edges).  */
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, cfun)
     {
       struct bb_rename_info *bb_ri = (struct bb_rename_info *) bb->aux;
       unsigned j;
@@ -844,7 +870,7 @@ regrename_analyze (bitmap bb_mask)
            }
        }
     }
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, cfun)
     {
       struct bb_rename_info *bb_ri = (struct bb_rename_info *) bb->aux;
       unsigned j;
@@ -921,16 +947,23 @@ regrename_analyze (bitmap bb_mask)
 
   free (rename_info);
 
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, cfun)
     bb->aux = NULL;
 }
 
-void
+/* Attempt to replace all uses of the register in the chain beginning with
+   HEAD with REG.  Returns true on success and false if the replacement is
+   rejected because the insns would not validate.  The latter can happen
+   e.g. if a match_parallel predicate enforces restrictions on register
+   numbering in its subpatterns.  */
+
+bool
 regrename_do_replace (struct du_head *head, int reg)
 {
   struct du_chain *chain;
   unsigned int base_regno = head->regno;
-  enum machine_mode mode;
+  machine_mode mode;
+  rtx last_reg = NULL_RTX, last_repl = NULL_RTX;
 
   for (chain = head->first; chain; chain = chain->next_use)
     {
@@ -939,22 +972,31 @@ regrename_do_replace (struct du_head *head, int reg)
       int reg_ptr = REG_POINTER (*chain->loc);
 
       if (DEBUG_INSN_P (chain->insn) && REGNO (*chain->loc) != base_regno)
-       INSN_VAR_LOCATION_LOC (chain->insn) = gen_rtx_UNKNOWN_VAR_LOC ();
+       validate_change (chain->insn, &(INSN_VAR_LOCATION_LOC (chain->insn)),
+                        gen_rtx_UNKNOWN_VAR_LOC (), true);
       else
        {
-         *chain->loc = gen_raw_REG (GET_MODE (*chain->loc), reg);
-         if (regno >= FIRST_PSEUDO_REGISTER)
-           ORIGINAL_REGNO (*chain->loc) = regno;
-         REG_ATTRS (*chain->loc) = attr;
-         REG_POINTER (*chain->loc) = reg_ptr;
+         if (*chain->loc != last_reg)
+           {
+             last_repl = gen_raw_REG (GET_MODE (*chain->loc), reg);
+             if (regno >= FIRST_PSEUDO_REGISTER)
+               ORIGINAL_REGNO (last_repl) = regno;
+             REG_ATTRS (last_repl) = attr;
+             REG_POINTER (last_repl) = reg_ptr;
+             last_reg = *chain->loc;
+           }
+         validate_change (chain->insn, chain->loc, last_repl, true);
        }
-
-      df_insn_rescan (chain->insn);
     }
 
+  if (!apply_change_group ())
+    return false;
+
   mode = GET_MODE (*head->first->loc);
+  head->renamed = 1;
   head->regno = reg;
-  head->nregs = hard_regno_nregs[reg][mode];
+  head->nregs = hard_regno_nregs (reg, mode);
+  return true;
 }
 
 
@@ -976,7 +1018,7 @@ verify_reg_in_set (rtx op, HARD_REG_SET *pset)
     return false;
 
   regno = REGNO (op);
-  nregs = hard_regno_nregs[regno][GET_MODE (op)];
+  nregs = REG_NREGS (op);
   all_live = all_dead = true;
   while (nregs-- > 0)
     if (TEST_HARD_REG_BIT (*pset, regno + nregs))
@@ -1024,19 +1066,47 @@ note_sets_clobbers (rtx x, const_rtx set, void *data)
 }
 
 static void
-scan_rtx_reg (rtx insn, rtx *loc, enum reg_class cl, enum scan_actions action,
+scan_rtx_reg (rtx_insn *insn, rtx *loc, enum reg_class cl, enum scan_actions action,
              enum op_type type)
 {
   struct du_head **p;
   rtx x = *loc;
-  enum machine_mode mode = GET_MODE (x);
   unsigned this_regno = REGNO (x);
-  int this_nregs = hard_regno_nregs[this_regno][mode];
+  int this_nregs = REG_NREGS (x);
 
   if (action == mark_write)
     {
       if (type == OP_OUT)
-       create_new_chain (this_regno, this_nregs, loc, insn, cl);
+       {
+         du_head_p c;
+         rtx pat = PATTERN (insn);
+
+         c = create_new_chain (this_regno, this_nregs, loc, insn, cl);
+
+         /* We try to tie chains in a move instruction for
+            a single output.  */
+         if (recog_data.n_operands == 2
+             && GET_CODE (pat) == SET
+             && GET_CODE (SET_DEST (pat)) == REG
+             && GET_CODE (SET_SRC (pat)) == REG
+             && terminated_this_insn
+             && terminated_this_insn->nregs
+                == REG_NREGS (recog_data.operand[1]))
+           {
+             gcc_assert (terminated_this_insn->regno
+                         == REGNO (recog_data.operand[1]));
+
+             c->tied_chain = terminated_this_insn;
+             terminated_this_insn->tied_chain = c;
+
+             if (dump_file)
+               fprintf (dump_file, "Tying chain %s (%d) with %s (%d)\n",
+                        reg_names[c->regno], c->id,
+                        reg_names[terminated_this_insn->regno],
+                        terminated_this_insn->id);
+           }
+       }
+
       return;
     }
 
@@ -1144,6 +1214,8 @@ scan_rtx_reg (rtx insn, rtx *loc, enum reg_class cl, enum scan_actions action,
                SET_HARD_REG_BIT (live_hard_regs, head->regno + nregs);
            }
 
+         if (action == terminate_dead)
+           terminated_this_insn = *p;
          *p = next;
          if (dump_file)
            fprintf (dump_file,
@@ -1175,12 +1247,25 @@ scan_rtx_reg (rtx insn, rtx *loc, enum reg_class cl, enum scan_actions action,
     }
 }
 
+/* A wrapper around base_reg_class which returns ALL_REGS if INSN is a
+   DEBUG_INSN.  The arguments MODE, AS, CODE and INDEX_CODE are as for
+   base_reg_class.  */
+
+static reg_class
+base_reg_class_for_rename (rtx_insn *insn, machine_mode mode, addr_space_t as,
+                          rtx_code code, rtx_code index_code)
+{
+  if (DEBUG_INSN_P (insn))
+    return ALL_REGS;
+  return base_reg_class (mode, as, code, index_code);
+}
+
 /* Adapted from find_reloads_address_1.  CL is INDEX_REG_CLASS or
    BASE_REG_CLASS depending on how the register is being considered.  */
 
 static void
-scan_rtx_address (rtx insn, rtx *loc, enum reg_class cl,
-                 enum scan_actions action, enum machine_mode mode,
+scan_rtx_address (rtx_insn *insn, rtx *loc, enum reg_class cl,
+                 enum scan_actions action, machine_mode mode,
                  addr_space_t as)
 {
   rtx x = *loc;
@@ -1280,12 +1365,16 @@ scan_rtx_address (rtx insn, rtx *loc, enum reg_class cl,
          }
 
        if (locI)
-         scan_rtx_address (insn, locI, INDEX_REG_CLASS, action, mode, as);
+         {
+           reg_class iclass = DEBUG_INSN_P (insn) ? ALL_REGS : INDEX_REG_CLASS;
+           scan_rtx_address (insn, locI, iclass, action, mode, as);
+         }
        if (locB)
-         scan_rtx_address (insn, locB,
-                           base_reg_class (mode, as, PLUS, index_code),
-                           action, mode, as);
-
+         {
+           reg_class bclass = base_reg_class_for_rename (insn, mode, as, PLUS,
+                                                         index_code);
+           scan_rtx_address (insn, locB, bclass, action, mode, as);
+         }
        return;
       }
 
@@ -1295,18 +1384,21 @@ scan_rtx_address (rtx insn, rtx *loc, enum reg_class cl,
     case PRE_INC:
     case PRE_DEC:
     case PRE_MODIFY:
-#ifndef AUTO_INC_DEC
       /* If the target doesn't claim to handle autoinc, this must be
         something special, like a stack push.  Kill this chain.  */
-      action = mark_all_read;
-#endif
+      if (!AUTO_INC_DEC)
+       action = mark_all_read;
+
       break;
 
     case MEM:
-      scan_rtx_address (insn, &XEXP (x, 0),
-                       base_reg_class (GET_MODE (x), MEM_ADDR_SPACE (x),
-                                       MEM, SCRATCH),
-                       action, GET_MODE (x), MEM_ADDR_SPACE (x));
+      {
+       reg_class bclass = base_reg_class_for_rename (insn, GET_MODE (x),
+                                                     MEM_ADDR_SPACE (x),
+                                                     MEM, SCRATCH);
+       scan_rtx_address (insn, &XEXP (x, 0), bclass, action, GET_MODE (x),
+                         MEM_ADDR_SPACE (x));
+      }
       return;
 
     case REG:
@@ -1329,7 +1421,7 @@ scan_rtx_address (rtx insn, rtx *loc, enum reg_class cl,
 }
 
 static void
-scan_rtx (rtx insn, rtx *loc, enum reg_class cl, enum scan_actions action,
+scan_rtx (rtx_insn *insn, rtx *loc, enum reg_class cl, enum scan_actions action,
          enum op_type type)
 {
   const char *fmt;
@@ -1353,10 +1445,14 @@ scan_rtx (rtx insn, rtx *loc, enum reg_class cl, enum scan_actions action,
       return;
 
     case MEM:
-      scan_rtx_address (insn, &XEXP (x, 0),
-                       base_reg_class (GET_MODE (x), MEM_ADDR_SPACE (x),
-                                       MEM, SCRATCH),
-                       action, GET_MODE (x), MEM_ADDR_SPACE (x));
+      {
+       reg_class bclass = base_reg_class_for_rename (insn, GET_MODE (x),
+                                                     MEM_ADDR_SPACE (x),
+                                                     MEM, SCRATCH);
+
+       scan_rtx_address (insn, &XEXP (x, 0), bclass, action, GET_MODE (x),
+                         MEM_ADDR_SPACE (x));
+      }
       return;
 
     case SET:
@@ -1428,7 +1524,7 @@ hide_operands (int n_ops, rtx *old_operands, rtx *old_dups,
               unsigned HOST_WIDE_INT do_not_hide, bool inout_and_ec_only)
 {
   int i;
-  int alt = which_alternative;
+  const operand_alternative *op_alt = which_op_alt ();
   for (i = 0; i < n_ops; i++)
     {
       old_operands[i] = recog_data.operand[i];
@@ -1440,7 +1536,7 @@ hide_operands (int n_ops, rtx *old_operands, rtx *old_dups,
       if (do_not_hide & (1 << i))
        continue;
       if (!inout_and_ec_only || recog_data.operand_type[i] == OP_INOUT
-         || recog_op_alt[i][alt].earlyclobber)
+         || op_alt[i].earlyclobber)
        *recog_data.operand_loc[i] = cc0_rtx;
     }
   for (i = 0; i < recog_data.n_dups; i++)
@@ -1450,7 +1546,7 @@ hide_operands (int n_ops, rtx *old_operands, rtx *old_dups,
       if (do_not_hide & (1 << opn))
        continue;
       if (!inout_and_ec_only || recog_data.operand_type[opn] == OP_INOUT
-         || recog_op_alt[opn][alt].earlyclobber)
+         || op_alt[opn].earlyclobber)
        *recog_data.dup_loc[i] = cc0_rtx;
     }
 }
@@ -1459,7 +1555,7 @@ hide_operands (int n_ops, rtx *old_operands, rtx *old_dups,
    are processing; the arguments are the same as in hide_operands.  */
 
 static void
-restore_operands (rtx insn, int n_ops, rtx *old_operands, rtx *old_dups)
+restore_operands (rtx_insn *insn, int n_ops, rtx *old_operands, rtx *old_dups)
 {
   int i;
   for (i = 0; i < recog_data.n_dups; i++)
@@ -1476,10 +1572,10 @@ restore_operands (rtx insn, int n_ops, rtx *old_operands, rtx *old_dups)
    record information about the operands in the insn.  */
 
 static void
-record_out_operands (rtx insn, bool earlyclobber, insn_rr_info *insn_info)
+record_out_operands (rtx_insn *insn, bool earlyclobber, insn_rr_info *insn_info)
 {
   int n_ops = recog_data.n_operands;
-  int alt = which_alternative;
+  const operand_alternative *op_alt = which_op_alt ();
 
   int i;
 
@@ -1490,18 +1586,20 @@ record_out_operands (rtx insn, bool earlyclobber, insn_rr_info *insn_info)
                  ? recog_data.operand_loc[opn]
                  : recog_data.dup_loc[i - n_ops]);
       rtx op = *loc;
-      enum reg_class cl = recog_op_alt[opn][alt].cl;
+      enum reg_class cl = alternative_class (op_alt, opn);
 
       struct du_head *prev_open;
 
       if (recog_data.operand_type[opn] != OP_OUT
-         || recog_op_alt[opn][alt].earlyclobber != earlyclobber)
+         || op_alt[opn].earlyclobber != earlyclobber)
        continue;
 
       if (insn_info)
        cur_operand = insn_info->op_info + i;
 
       prev_open = open_chains;
+      if (earlyclobber)
+       scan_rtx (insn, loc, cl, terminate_write, OP_OUT);
       scan_rtx (insn, loc, cl, mark_write, OP_OUT);
 
       /* ??? Many targets have output constraints on the SET_DEST
@@ -1526,7 +1624,7 @@ record_out_operands (rtx insn, bool earlyclobber, insn_rr_info *insn_info)
 static bool
 build_def_use (basic_block bb)
 {
-  rtx insn;
+  rtx_insn *insn;
   unsigned HOST_WIDE_INT untracked_operands;
 
   fail_current_block = false;
@@ -1540,11 +1638,11 @@ build_def_use (basic_block bb)
          rtx old_operands[MAX_RECOG_OPERANDS];
          rtx old_dups[MAX_DUP_OPERANDS];
          int i;
-         int alt;
          int predicated;
          enum rtx_code set_code = SET;
          enum rtx_code clobber_code = CLOBBER;
          insn_rr_info *insn_info = NULL;
+         terminated_this_insn = NULL;
 
          /* Process the insn, determining its effect on the def-use
             chains and live hard registers.  We perform the following
@@ -1563,17 +1661,17 @@ build_def_use (basic_block bb)
             (6) For any non-earlyclobber write we find in an operand, make
                 a new chain or mark the hard register as live.
             (7) For any REG_UNUSED, close any chains we just opened.
+            (8) For any REG_CFA_RESTORE or REG_CFA_REGISTER, kill any chain
+                containing its dest.
 
             We cannot deal with situations where we track a reg in one mode
             and see a reference in another mode; these will cause the chain
             to be marked unrenamable or even cause us to abort the entire
             basic block.  */
 
-         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;
          untracked_operands = 0;
 
@@ -1586,8 +1684,7 @@ build_def_use (basic_block bb)
                      sizeof (operand_rr_info) * recog_data.n_operands);
            }
 
-         /* Simplify the code below by rewriting things to reflect
-            matching constraints.  Also promote OP_OUT to OP_INOUT in
+         /* Simplify the code below by promoting OP_OUT to OP_INOUT in
             predicated instructions, but only for register operands
             that are already tracked, so that we can create a chain
             when the first SET makes a register live.  */
@@ -1596,10 +1693,8 @@ build_def_use (basic_block bb)
          for (i = 0; i < n_ops; ++i)
            {
              rtx op = recog_data.operand[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;
@@ -1608,28 +1703,35 @@ build_def_use (basic_block bb)
                     not already tracking such a reg, we won't start here,
                     and we must instead make sure to make the operand visible
                     to the machinery that tracks hard registers.  */
-                 if (matches >= 0
-                     && (GET_MODE_SIZE (recog_data.operand_mode[i])
-                         != GET_MODE_SIZE (recog_data.operand_mode[matches]))
-                     && !verify_reg_in_set (op, &live_in_chains))
+                 machine_mode i_mode = recog_data.operand_mode[i];
+                 if (matches >= 0)
                    {
-                     untracked_operands |= 1 << i;
-                     untracked_operands |= 1 << matches;
+                     machine_mode matches_mode
+                       = recog_data.operand_mode[matches];
+
+                     if (maybe_ne (GET_MODE_SIZE (i_mode),
+                                   GET_MODE_SIZE (matches_mode))
+                         && !verify_reg_in_set (op, &live_in_chains))
+                       {
+                         untracked_operands |= 1 << i;
+                         untracked_operands |= 1 << matches;
+                       }
                    }
                }
+#ifdef STACK_REGS
+             if (regstack_completed
+                 && REG_P (op)
+                 && IN_RANGE (REGNO (op), FIRST_STACK_REG, LAST_STACK_REG))
+               untracked_operands |= 1 << i;
+#endif
              /* If there's an in-out operand with a register that is not
                 being tracked at all yet, open a chain.  */
              if (recog_data.operand_type[i] == OP_INOUT
                  && !(untracked_operands & (1 << i))
                  && REG_P (op)
                  && !verify_reg_tracked (op))
-               {
-                 enum machine_mode mode = GET_MODE (op);
-                 unsigned this_regno = REGNO (op);
-                 unsigned this_nregs = hard_regno_nregs[this_regno][mode];
-                 create_new_chain (this_regno, this_nregs, NULL, NULL_RTX,
-                                   NO_REGS);
-               }
+               create_new_chain (REGNO (op), REG_NREGS (op), NULL, NULL,
+                                 NO_REGS);
            }
 
          if (fail_current_block)
@@ -1683,7 +1785,7 @@ build_def_use (basic_block bb)
              rtx *loc = (i < n_ops
                          ? recog_data.operand_loc[opn]
                          : recog_data.dup_loc[i - n_ops]);
-             enum reg_class cl = recog_op_alt[opn][alt].cl;
+             enum reg_class cl = alternative_class (op_alt, opn);
              enum op_type type = recog_data.operand_type[opn];
 
              /* Don't scan match_operand here, since we've no reg class
@@ -1695,7 +1797,7 @@ build_def_use (basic_block bb)
 
              if (insn_info)
                cur_operand = i == opn ? insn_info->op_info + i : NULL;
-             if (recog_op_alt[opn][alt].is_address)
+             if (op_alt[opn].is_address)
                scan_rtx_address (insn, loc, cl, mark_read,
                                  VOIDmode, ADDR_SPACE_GENERIC);
              else
@@ -1711,9 +1813,16 @@ build_def_use (basic_block bb)
              scan_rtx (insn, &XEXP (note, 0), ALL_REGS, mark_read,
                        OP_INOUT);
 
-         /* Step 4: Close chains for registers that die here.  */
+         /* Step 4: Close chains for registers that die here, unless
+            the register is mentioned in a REG_UNUSED note.  In that
+            case we keep the chain open until step #7 below to ensure
+            it conflicts with other output operands of this insn.
+            See PR 52573.  Arguably the insn should not have both
+            notes; it has proven difficult to fix that without
+            other undesirable side effects.  */
          for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
-           if (REG_NOTE_KIND (note) == REG_DEAD)
+           if (REG_NOTE_KIND (note) == REG_DEAD
+               && !find_regno_note (insn, REG_UNUSED, REGNO (XEXP (note, 0))))
              {
                remove_from_hard_reg_set (&live_hard_regs,
                                          GET_MODE (XEXP (note, 0)),
@@ -1772,8 +1881,24 @@ build_def_use (basic_block bb)
                scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
                          OP_IN);
              }
+
+         /* Step 8: Kill the chains involving register restores.  Those
+            should restore _that_ register.  Similar for REG_CFA_REGISTER.  */
+         for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
+           if (REG_NOTE_KIND (note) == REG_CFA_RESTORE
+               || REG_NOTE_KIND (note) == REG_CFA_REGISTER)
+             {
+               rtx *x = &XEXP (note, 0);
+               if (!*x)
+                 x = &PATTERN (insn);
+               if (GET_CODE (*x) == PARALLEL)
+                 x = &XVECEXP (*x, 0, 0);
+               if (GET_CODE (*x) == SET)
+                 x = &SET_DEST (*x);
+               scan_rtx (insn, x, NO_REGS, mark_all_read, OP_IN);
+             }
        }
-      else if (DEBUG_INSN_P (insn)
+      else if (DEBUG_BIND_INSN_P (insn)
               && !VAR_LOC_UNKNOWN_P (INSN_VAR_LOCATION_LOC (insn)))
        {
          scan_rtx (insn, &INSN_VAR_LOCATION_LOC (insn),
@@ -1830,29 +1955,42 @@ regrename_optimize (void)
   return 0;
 }
 \f
-static bool
-gate_handle_regrename (void)
-{
-  return (optimize > 0 && (flag_rename_registers));
-}
+namespace {
 
-struct rtl_opt_pass pass_regrename =
+const pass_data pass_data_regrename =
 {
- {
-  RTL_PASS,
-  "rnreg",                              /* name */
-  OPTGROUP_NONE,                        /* optinfo_flags */
-  gate_handle_regrename,                /* gate */
-  regrename_optimize,                   /* execute */
-  NULL,                                 /* sub */
-  NULL,                                 /* next */
-  0,                                    /* static_pass_number */
-  TV_RENAME_REGISTERS,                  /* tv_id */
-  0,                                    /* properties_required */
-  0,                                    /* properties_provided */
-  0,                                    /* properties_destroyed */
-  0,                                    /* todo_flags_start */
-  TODO_df_finish | TODO_verify_rtl_sharing |
-  0                                     /* todo_flags_finish */
- }
+  RTL_PASS, /* type */
+  "rnreg", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_RENAME_REGISTERS, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_df_finish, /* todo_flags_finish */
 };
+
+class pass_regrename : public rtl_opt_pass
+{
+public:
+  pass_regrename (gcc::context *ctxt)
+    : rtl_opt_pass (pass_data_regrename, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *)
+    {
+      return (optimize > 0 && (flag_rename_registers));
+    }
+
+  virtual unsigned int execute (function *) { return regrename_optimize (); }
+
+}; // class pass_regrename
+
+} // anon namespace
+
+rtl_opt_pass *
+make_pass_regrename (gcc::context *ctxt)
+{
+  return new pass_regrename (ctxt);
+}