]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/cse.c
PR fortran/95090 - ICE: identifier overflow
[thirdparty/gcc.git] / gcc / cse.c
index 53ca9661b69dc08cdfe8b5b0cf470c90304b579e..36bcfc354d8140fecb7e0776bd4a755b2a1ee3c7 100644 (file)
--- a/gcc/cse.c
+++ b/gcc/cse.c
@@ -1,5 +1,5 @@
 /* Common subexpression elimination for GNU compiler.
-   Copyright (C) 1987-2015 Free Software Foundation, Inc.
+   Copyright (C) 1987-2020 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -20,39 +20,29 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "tm.h"
+#include "backend.h"
+#include "target.h"
 #include "rtl.h"
+#include "tree.h"
+#include "cfghooks.h"
+#include "df.h"
+#include "memmodel.h"
 #include "tm_p.h"
-#include "hard-reg-set.h"
+#include "insn-config.h"
 #include "regs.h"
-#include "predict.h"
-#include "vec.h"
-#include "hashtab.h"
-#include "hash-set.h"
-#include "machmode.h"
-#include "input.h"
-#include "function.h"
-#include "dominance.h"
-#include "cfg.h"
+#include "emit-rtl.h"
+#include "recog.h"
 #include "cfgrtl.h"
 #include "cfganal.h"
 #include "cfgcleanup.h"
-#include "basic-block.h"
-#include "flags.h"
-#include "insn-config.h"
-#include "recog.h"
-#include "expr.h"
-#include "diagnostic-core.h"
+#include "alias.h"
 #include "toplev.h"
-#include "ggc.h"
-#include "except.h"
-#include "target.h"
-#include "params.h"
 #include "rtlhooks-def.h"
 #include "tree-pass.h"
-#include "df.h"
 #include "dbgcnt.h"
 #include "rtl-iter.h"
+#include "regs.h"
+#include "function-abi.h"
 
 /* The basic idea of common subexpression elimination is to go
    through the code, keeping a record of expressions that would
@@ -265,7 +255,6 @@ struct qty_table_elem
 /* The table of all qtys, indexed by qty number.  */
 static struct qty_table_elem *qty_table;
 
-#ifdef HAVE_cc0
 /* For machines that have a CC0, we do not record its value in the hash
    table since its use is guaranteed to be the insn immediately following
    its definition and any other insn is presumed to invalidate it.
@@ -277,7 +266,6 @@ static struct qty_table_elem *qty_table;
 
 static rtx this_insn_cc0, prev_insn_cc0;
 static machine_mode this_insn_cc0_mode, prev_insn_cc0_mode;
-#endif
 
 /* Insn being scanned.  */
 
@@ -475,8 +463,10 @@ struct table_elt
    || (HARD_REGISTER_NUM_P (N)                                         \
        && FIXED_REGNO_P (N) && REGNO_REG_CLASS (N) != NO_REGS))
 
-#define COST(X) (REG_P (X) ? 0 : notreg_cost (X, SET, 1))
-#define COST_IN(X, OUTER, OPNO) (REG_P (X) ? 0 : notreg_cost (X, OUTER, OPNO))
+#define COST(X, MODE)                                                  \
+  (REG_P (X) ? 0 : notreg_cost (X, MODE, SET, 1))
+#define COST_IN(X, MODE, OUTER, OPNO)                                  \
+  (REG_P (X) ? 0 : notreg_cost (X, MODE, OUTER, OPNO))
 
 /* Get the number of times this register has been updated in this
    basic block.  */
@@ -552,7 +542,7 @@ static bitmap cse_ebb_live_in, cse_ebb_live_out;
 static sbitmap cse_visited_basic_blocks;
 
 static bool fixed_base_plus_p (rtx x);
-static int notreg_cost (rtx, enum rtx_code, int);
+static int notreg_cost (rtx, machine_mode, enum rtx_code, int);
 static int preferable (int, int, int, int);
 static void new_basic_block (void);
 static void make_new_qty (unsigned int, machine_mode);
@@ -572,11 +562,10 @@ static struct table_elt *insert (rtx, struct table_elt *, unsigned,
 static void merge_equiv_classes (struct table_elt *, struct table_elt *);
 static void invalidate (rtx, machine_mode);
 static void remove_invalid_refs (unsigned int);
-static void remove_invalid_subreg_refs (unsigned int, unsigned int,
+static void remove_invalid_subreg_refs (unsigned int, poly_uint64,
                                        machine_mode);
 static void rehash_using_reg (rtx);
 static void invalidate_memory (void);
-static void invalidate_for_call (void);
 static rtx use_related_value (rtx, struct table_elt *);
 
 static inline unsigned canon_hash (rtx, machine_mode);
@@ -596,7 +585,6 @@ static void cse_insn (rtx_insn *);
 static void cse_prescan_path (struct cse_basic_block_data *);
 static void invalidate_from_clobbers (rtx_insn *);
 static void invalidate_from_sets_and_clobbers (rtx_insn *);
-static rtx cse_process_notes (rtx, rtx, bool *);
 static void cse_extended_basic_block (struct cse_basic_block_data *);
 extern void dump_class (struct table_elt*);
 static void get_cse_reg_info_1 (unsigned int regno);
@@ -729,19 +717,18 @@ preferable (int cost_a, int regcost_a, int cost_b, int regcost_b)
    from COST macro to keep it simple.  */
 
 static int
-notreg_cost (rtx x, enum rtx_code outer, int opno)
+notreg_cost (rtx x, machine_mode mode, enum rtx_code outer, int opno)
 {
+  scalar_int_mode int_mode, inner_mode;
   return ((GET_CODE (x) == SUBREG
           && REG_P (SUBREG_REG (x))
-          && GET_MODE_CLASS (GET_MODE (x)) == MODE_INT
-          && GET_MODE_CLASS (GET_MODE (SUBREG_REG (x))) == MODE_INT
-          && (GET_MODE_SIZE (GET_MODE (x))
-              < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+          && is_int_mode (mode, &int_mode)
+          && is_int_mode (GET_MODE (SUBREG_REG (x)), &inner_mode)
+          && GET_MODE_SIZE (int_mode) < GET_MODE_SIZE (inner_mode)
           && subreg_lowpart_p (x)
-          && TRULY_NOOP_TRUNCATION_MODES_P (GET_MODE (x),
-                                            GET_MODE (SUBREG_REG (x))))
+          && TRULY_NOOP_TRUNCATION_MODES_P (int_mode, inner_mode))
          ? 0
-         : rtx_cost (x, outer, opno, optimize_this_for_speed_p) * 2);
+         : rtx_cost (x, mode, outer, opno, optimize_this_for_speed_p) * 2);
 }
 
 \f
@@ -868,9 +855,7 @@ new_basic_block (void)
        }
     }
 
-#ifdef HAVE_cc0
   prev_insn_cc0 = 0;
-#endif
 }
 
 /* Say that register REG contains a quantity in mode MODE not in any
@@ -1247,7 +1232,7 @@ insert_const_anchor (HOST_WIDE_INT anchor, rtx reg, HOST_WIDE_INT offs,
      don't prefer pseudos over hard regs so that we derive constants in
      argument registers from other argument registers rather than from the
      original pseudo that was used to synthesize the constant.  */
-  insert_with_costs (exp, elt, hash, mode, COST (reg), 1);
+  insert_with_costs (exp, elt, hash, mode, COST (reg, mode), 1);
 }
 
 /* The constant CST is equivalent to the register REG.  Create
@@ -1728,8 +1713,8 @@ static struct table_elt *
 insert (rtx x, struct table_elt *classp, unsigned int hash,
        machine_mode mode)
 {
-  return
-    insert_with_costs (x, classp, hash, mode, COST (x), approx_reg_cost (x));
+  return insert_with_costs (x, classp, hash, mode,
+                           COST (x, mode), approx_reg_cost (x));
 }
 
 \f
@@ -1791,6 +1776,8 @@ merge_equiv_classes (struct table_elt *class1, struct table_elt *class2)
            }
          new_elt = insert (exp, class1, hash, mode);
          new_elt->in_memory = hash_arg_in_memory;
+         if (GET_CODE (exp) == ASM_OPERANDS && elt->cost == MAX_COST)
+           new_elt->cost = MAX_COST;
        }
     }
 }
@@ -1830,7 +1817,72 @@ check_dependence (const_rtx x, rtx exp, machine_mode mode, rtx addr)
     }
   return false;
 }
-\f
+
+/* Remove from the hash table, or mark as invalid, all expressions whose
+   values could be altered by storing in register X.  */
+
+static void
+invalidate_reg (rtx x)
+{
+  gcc_assert (GET_CODE (x) == REG);
+
+  /* If X is a register, dependencies on its contents are recorded
+     through the qty number mechanism.  Just change the qty number of
+     the register, mark it as invalid for expressions that refer to it,
+     and remove it itself.  */
+  unsigned int regno = REGNO (x);
+  unsigned int hash = HASH (x, GET_MODE (x));
+
+  /* Remove REGNO from any quantity list it might be on and indicate
+     that its value might have changed.  If it is a pseudo, remove its
+     entry from the hash table.
+
+     For a hard register, we do the first two actions above for any
+     additional hard registers corresponding to X.  Then, if any of these
+     registers are in the table, we must remove any REG entries that
+     overlap these registers.  */
+
+  delete_reg_equiv (regno);
+  REG_TICK (regno)++;
+  SUBREG_TICKED (regno) = -1;
+
+  if (regno >= FIRST_PSEUDO_REGISTER)
+    remove_pseudo_from_table (x, hash);
+  else
+    {
+      HOST_WIDE_INT in_table = TEST_HARD_REG_BIT (hard_regs_in_table, regno);
+      unsigned int endregno = END_REGNO (x);
+      unsigned int rn;
+      struct table_elt *p, *next;
+
+      CLEAR_HARD_REG_BIT (hard_regs_in_table, regno);
+
+      for (rn = regno + 1; rn < endregno; rn++)
+       {
+         in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, rn);
+         CLEAR_HARD_REG_BIT (hard_regs_in_table, rn);
+         delete_reg_equiv (rn);
+         REG_TICK (rn)++;
+         SUBREG_TICKED (rn) = -1;
+       }
+
+      if (in_table)
+       for (hash = 0; hash < HASH_SIZE; hash++)
+         for (p = table[hash]; p; p = next)
+           {
+             next = p->next_same_hash;
+
+             if (!REG_P (p->exp) || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
+               continue;
+
+             unsigned int tregno = REGNO (p->exp);
+             unsigned int tendregno = END_REGNO (p->exp);
+             if (tendregno > regno && tregno < endregno)
+               remove_from_table (p, hash);
+           }
+    }
+}
+
 /* Remove from the hash table, or mark as invalid, all expressions whose
    values could be altered by storing in X.  X is a register, a subreg, or
    a memory reference with nonvarying address (because, when a memory
@@ -1853,65 +1905,7 @@ invalidate (rtx x, machine_mode full_mode)
   switch (GET_CODE (x))
     {
     case REG:
-      {
-       /* If X is a register, dependencies on its contents are recorded
-          through the qty number mechanism.  Just change the qty number of
-          the register, mark it as invalid for expressions that refer to it,
-          and remove it itself.  */
-       unsigned int regno = REGNO (x);
-       unsigned int hash = HASH (x, GET_MODE (x));
-
-       /* Remove REGNO from any quantity list it might be on and indicate
-          that its value might have changed.  If it is a pseudo, remove its
-          entry from the hash table.
-
-          For a hard register, we do the first two actions above for any
-          additional hard registers corresponding to X.  Then, if any of these
-          registers are in the table, we must remove any REG entries that
-          overlap these registers.  */
-
-       delete_reg_equiv (regno);
-       REG_TICK (regno)++;
-       SUBREG_TICKED (regno) = -1;
-
-       if (regno >= FIRST_PSEUDO_REGISTER)
-         remove_pseudo_from_table (x, hash);
-       else
-         {
-           HOST_WIDE_INT in_table
-             = TEST_HARD_REG_BIT (hard_regs_in_table, regno);
-           unsigned int endregno = END_HARD_REGNO (x);
-           unsigned int tregno, tendregno, rn;
-           struct table_elt *p, *next;
-
-           CLEAR_HARD_REG_BIT (hard_regs_in_table, regno);
-
-           for (rn = regno + 1; rn < endregno; rn++)
-             {
-               in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, rn);
-               CLEAR_HARD_REG_BIT (hard_regs_in_table, rn);
-               delete_reg_equiv (rn);
-               REG_TICK (rn)++;
-               SUBREG_TICKED (rn) = -1;
-             }
-
-           if (in_table)
-             for (hash = 0; hash < HASH_SIZE; hash++)
-               for (p = table[hash]; p; p = next)
-                 {
-                   next = p->next_same_hash;
-
-                   if (!REG_P (p->exp)
-                       || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
-                     continue;
-
-                   tregno = REGNO (p->exp);
-                   tendregno = END_HARD_REGNO (p->exp);
-                   if (tendregno > regno && tregno < endregno)
-                     remove_from_table (p, hash);
-                 }
-         }
-      }
+      invalidate_reg (x);
       return;
 
     case SUBREG:
@@ -1966,6 +1960,22 @@ invalidate (rtx x, machine_mode full_mode)
       gcc_unreachable ();
     }
 }
+
+/* Invalidate DEST.  Used when DEST is not going to be added
+   into the hash table for some reason, e.g. do_not_record
+   flagged on it.  */
+
+static void
+invalidate_dest (rtx dest)
+{
+  if (REG_P (dest)
+      || GET_CODE (dest) == SUBREG
+      || MEM_P (dest))
+    invalidate (dest, VOIDmode);
+  else if (GET_CODE (dest) == STRICT_LOW_PART
+          || GET_CODE (dest) == ZERO_EXTRACT)
+    invalidate (XEXP (dest, 0), GET_MODE (dest));
+}
 \f
 /* Remove all expressions that refer to register REGNO,
    since they are already invalid, and we are about to
@@ -1990,12 +2000,11 @@ remove_invalid_refs (unsigned int regno)
 /* Likewise for a subreg with subreg_reg REGNO, subreg_byte OFFSET,
    and mode MODE.  */
 static void
-remove_invalid_subreg_refs (unsigned int regno, unsigned int offset,
+remove_invalid_subreg_refs (unsigned int regno, poly_uint64 offset,
                            machine_mode mode)
 {
   unsigned int i;
   struct table_elt *p, *next;
-  unsigned int end = offset + (GET_MODE_SIZE (mode) - 1);
 
   for (i = 0; i < HASH_SIZE; i++)
     for (p = table[i]; p; p = next)
@@ -2007,9 +2016,9 @@ remove_invalid_subreg_refs (unsigned int regno, unsigned int offset,
            && (GET_CODE (exp) != SUBREG
                || !REG_P (SUBREG_REG (exp))
                || REGNO (SUBREG_REG (exp)) != regno
-               || (((SUBREG_BYTE (exp)
-                     + (GET_MODE_SIZE (GET_MODE (exp)) - 1)) >= offset)
-                   && SUBREG_BYTE (exp) <= end))
+               || ranges_maybe_overlap_p (SUBREG_BYTE (exp),
+                                          GET_MODE_SIZE (GET_MODE (exp)),
+                                          offset, GET_MODE_SIZE (mode)))
            && refers_to_regno_p (regno, p->exp))
          remove_from_table (p, i);
       }
@@ -2067,23 +2076,31 @@ rehash_using_reg (rtx x)
 }
 \f
 /* Remove from the hash table any expression that is a call-clobbered
-   register.  Also update their TICK values.  */
+   register in INSN.  Also update their TICK values.  */
 
 static void
-invalidate_for_call (void)
+invalidate_for_call (rtx_insn *insn)
 {
-  unsigned int regno, endregno;
-  unsigned int i;
+  unsigned int regno;
   unsigned hash;
   struct table_elt *p, *next;
   int in_table = 0;
   hard_reg_set_iterator hrsi;
 
-  /* Go through all the hard registers.  For each that is clobbered in
-     a CALL_INSN, remove the register from quantity chains and update
+  /* Go through all the hard registers.  For each that might be clobbered
+     in call insn INSN, remove the register from quantity chains and update
      reg_tick if defined.  Also see if any of these registers is currently
-     in the table.  */
-  EXECUTE_IF_SET_IN_HARD_REG_SET (regs_invalidated_by_call, 0, regno, hrsi)
+     in the table.
+
+     ??? We could be more precise for partially-clobbered registers,
+     and only invalidate values that actually occupy the clobbered part
+     of the registers.  It doesn't seem worth the effort though, since
+     we shouldn't see this situation much before RA.  Whatever choice
+     we make here has to be consistent with the table walk below,
+     so any change to this test will require a change there too.  */
+  HARD_REG_SET callee_clobbers
+    = insn_callee_abi (insn).full_and_partial_reg_clobbers ();
+  EXECUTE_IF_SET_IN_HARD_REG_SET (callee_clobbers, 0, regno, hrsi)
     {
       delete_reg_equiv (regno);
       if (REG_TICK (regno) >= 0)
@@ -2108,15 +2125,11 @@ invalidate_for_call (void)
              || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
            continue;
 
-         regno = REGNO (p->exp);
-         endregno = END_HARD_REGNO (p->exp);
-
-         for (i = regno; i < endregno; i++)
-           if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i))
-             {
-               remove_from_table (p, hash);
-               break;
-             }
+         /* This must use the same test as above rather than the
+            more accurate clobbers_reg_p.  */
+         if (overlaps_hard_reg_set_p (callee_clobbers, GET_MODE (p->exp),
+                                      REGNO (p->exp)))
+           remove_from_table (p, hash);
        }
 }
 \f
@@ -2303,7 +2316,8 @@ hash_rtx_cb (const_rtx x, machine_mode mode,
          {
            hash += (((unsigned int) SUBREG << 7)
                     + REGNO (SUBREG_REG (x))
-                    + (SUBREG_BYTE (x) / UNITS_PER_WORD));
+                    + (constant_lower_bound (SUBREG_BYTE (x))
+                       / UNITS_PER_WORD));
            return hash;
          }
        break;
@@ -2319,6 +2333,15 @@ hash_rtx_cb (const_rtx x, machine_mode mode,
        hash += CONST_WIDE_INT_ELT (x, i);
       return hash;
 
+    case CONST_POLY_INT:
+      {
+       inchash::hash h;
+       h.add_int (hash);
+       for (unsigned int i = 0; i < NUM_POLY_INT_COEFFS; ++i)
+         h.add_wide_int (CONST_POLY_INT_COEFFS (x)[i]);
+       return h.end ();
+      }
+
     case CONST_DOUBLE:
       /* This is like the general case, except that it only counts
         the integers representing the constant.  */
@@ -2340,11 +2363,11 @@ hash_rtx_cb (const_rtx x, machine_mode mode,
        int units;
        rtx elt;
 
-       units = CONST_VECTOR_NUNITS (x);
+       units = const_vector_encoded_nelts (x);
 
        for (i = 0; i < units; ++i)
          {
-           elt = CONST_VECTOR_ELT (x, i);
+           elt = CONST_VECTOR_ENCODED_ELT (x, i);
            hash += hash_rtx_cb (elt, GET_MODE (elt),
                                  do_not_record_p, hash_arg_in_memory_p,
                                  have_reg_qty, cb);
@@ -2358,7 +2381,7 @@ hash_rtx_cb (const_rtx x, machine_mode mode,
       /* We don't hash on the address of the CODE_LABEL to avoid bootstrap
         differences and differences between each stage's debugging dumps.  */
         hash += (((unsigned int) LABEL_REF << 7)
-                 + CODE_LABEL_NUMBER (LABEL_REF_LABEL (x)));
+                 + CODE_LABEL_NUMBER (label_ref_label (x)));
       return hash;
 
     case SYMBOL_REF:
@@ -2513,6 +2536,10 @@ hash_rtx_cb (const_rtx x, machine_mode mode,
          hash += (unsigned int) XINT (x, i);
          break;
 
+       case 'p':
+         hash += constant_lower_bound (SUBREG_BYTE (x));
+         break;
+
        case '0': case 't':
          /* Unused.  */
          break;
@@ -2611,7 +2638,7 @@ exp_equiv_p (const_rtx x, const_rtx y, int validate, bool for_gcse)
       return x == y;
 
     case LABEL_REF:
-      return LABEL_REF_LABEL (x) == LABEL_REF_LABEL (y);
+      return label_ref_label (x) == label_ref_label (y);
 
     case SYMBOL_REF:
       return XSTR (x, 0) == XSTR (y, 0);
@@ -2763,6 +2790,11 @@ exp_equiv_p (const_rtx x, const_rtx y, int validate, bool for_gcse)
            return 0;
          break;
 
+       case 'p':
+         if (maybe_ne (SUBREG_BYTE (x), SUBREG_BYTE (y)))
+           return 0;
+         break;
+
        case '0':
        case 't':
          break;
@@ -3043,12 +3075,12 @@ find_comparison_args (enum rtx_code code, rtx *parg1, rtx *parg2,
       if (x == 0)
        break;
 
-      /* If we need to reverse the comparison, make sure that that is
+      /* If we need to reverse the comparison, make sure that is
         possible -- we can't necessarily infer the value of GE from LT
         with floating-point operands.  */
       if (reverse_code)
        {
-         enum rtx_code reversed = reversed_comparison_code (x, NULL_RTX);
+         enum rtx_code reversed = reversed_comparison_code (x, NULL);
          if (reversed == UNKNOWN)
            break;
          else
@@ -3090,10 +3122,13 @@ fold_rtx (rtx x, rtx_insn *insn)
   int i;
   rtx new_rtx = 0;
   int changed = 0;
+  poly_int64 xval;
 
   /* Operands of X.  */
-  rtx folded_arg0;
-  rtx folded_arg1;
+  /* Workaround -Wmaybe-uninitialized false positive during
+     profiledbootstrap by initializing them.  */
+  rtx folded_arg0 = NULL_RTX;
+  rtx folded_arg1 = NULL_RTX;
 
   /* Constant equivalents of first three operands of X;
      0 when no such equivalent is known.  */
@@ -3114,6 +3149,15 @@ fold_rtx (rtx x, rtx_insn *insn)
     {
     case MEM:
     case SUBREG:
+    /* The first operand of a SIGN/ZERO_EXTRACT has a different meaning
+       than it would in other contexts.  Basically its mode does not
+       signify the size of the object read.  That information is carried
+       by size operand.    If we happen to have a MEM of the appropriate
+       mode in our tables with a constant value we could simplify the
+       extraction incorrectly if we allowed substitution of that value
+       for the MEM.   */
+    case ZERO_EXTRACT:
+    case SIGN_EXTRACT:
       if ((new_rtx = equiv_constant (x)) != NULL_RTX)
         return new_rtx;
       return x;
@@ -3130,10 +3174,8 @@ fold_rtx (rtx x, rtx_insn *insn)
     case EXPR_LIST:
       return x;
 
-#ifdef HAVE_cc0
     case CC0:
       return prev_insn_cc0;
-#endif
 
     case ASM_OPERANDS:
       if (insn)
@@ -3144,12 +3186,10 @@ fold_rtx (rtx x, rtx_insn *insn)
        }
       return x;
 
-#ifdef NO_FUNCTION_CSE
     case CALL:
-      if (CONSTANT_P (XEXP (XEXP (x, 0), 0)))
+      if (NO_FUNCTION_CSE && CONSTANT_P (XEXP (XEXP (x, 0), 0)))
        return x;
       break;
-#endif
 
     /* Anything else goes through the loop below.  */
     default:
@@ -3187,7 +3227,6 @@ fold_rtx (rtx x, rtx_insn *insn)
            const_arg = folded_arg;
            break;
 
-#ifdef HAVE_cc0
          case CC0:
            /* The cc0-user and cc0-setter may be in different blocks if
               the cc0-setter potentially traps.  In that case PREV_INSN_CC0
@@ -3211,7 +3250,6 @@ fold_rtx (rtx x, rtx_insn *insn)
                const_arg = equiv_constant (folded_arg);
              }
            break;
-#endif
 
          default:
            folded_arg = fold_rtx (folded_arg, insn);
@@ -3241,7 +3279,8 @@ fold_rtx (rtx x, rtx_insn *insn)
           argument.  */
        if (const_arg != 0
            && const_arg != folded_arg
-           && COST_IN (const_arg, code, i) <= COST_IN (folded_arg, code, i)
+           && (COST_IN (const_arg, mode_arg, code, i)
+               <= COST_IN (folded_arg, mode_arg, code, i))
 
            /* It's not safe to substitute the operand of a conversion
               operator with a constant, as the conversion's identity
@@ -3275,9 +3314,8 @@ fold_rtx (rtx x, rtx_insn *insn)
         consistent with the order in X.  */
       if (canonicalize_change_group (insn, x))
        {
-         rtx tem;
-         tem = const_arg0, const_arg0 = const_arg1, const_arg1 = tem;
-         tem = folded_arg0, folded_arg0 = folded_arg1, folded_arg1 = tem;
+         std::swap (const_arg0, const_arg1);
+         std::swap (folded_arg0, folded_arg1);
        }
 
       apply_change_group ();
@@ -3321,7 +3359,7 @@ fold_rtx (rtx x, rtx_insn *insn)
          if (SCALAR_FLOAT_MODE_P (mode))
            {
 #ifdef FLOAT_STORE_FLAG_VALUE
-             true_rtx = (CONST_DOUBLE_FROM_REAL_VALUE
+             true_rtx = (const_double_from_real_value
                          (FLOAT_STORE_FLAG_VALUE (mode), mode));
 #else
              true_rtx = NULL_RTX;
@@ -3368,7 +3406,7 @@ fold_rtx (rtx x, rtx_insn *insn)
                  if (p != NULL)
                    {
                      cheapest_simplification = x;
-                     cheapest_cost = COST (x);
+                     cheapest_cost = COST (x, mode);
 
                      for (p = p->first_same_value; p != NULL; p = p->next_same_value)
                        {
@@ -3388,7 +3426,7 @@ fold_rtx (rtx x, rtx_insn *insn)
                          if (simp_result == NULL)
                            continue;
 
-                         cost = COST (simp_result);
+                         cost = COST (simp_result, mode);
                          if (cost < cheapest_cost)
                            {
                              cheapest_cost = cost;
@@ -3495,7 +3533,7 @@ fold_rtx (rtx x, rtx_insn *insn)
                : lookup_as_function (folded_arg0, MINUS);
 
              if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF
-                 && LABEL_REF_LABEL (XEXP (y, 1)) == LABEL_REF_LABEL (const_arg1))
+                 && label_ref_label (XEXP (y, 1)) == label_ref_label (const_arg1))
                return XEXP (y, 0);
 
              /* Now try for a CONST of a MINUS like the above.  */
@@ -3503,7 +3541,7 @@ fold_rtx (rtx x, rtx_insn *insn)
                        : lookup_as_function (folded_arg0, CONST))) != 0
                  && GET_CODE (XEXP (y, 0)) == MINUS
                  && GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF
-                 && LABEL_REF_LABEL (XEXP (XEXP (y, 0), 1)) == LABEL_REF_LABEL (const_arg1))
+                 && label_ref_label (XEXP (XEXP (y, 0), 1)) == label_ref_label (const_arg1))
                return XEXP (XEXP (y, 0), 0);
            }
 
@@ -3515,7 +3553,7 @@ fold_rtx (rtx x, rtx_insn *insn)
                : lookup_as_function (folded_arg1, MINUS);
 
              if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF
-                 && LABEL_REF_LABEL (XEXP (y, 1)) == LABEL_REF_LABEL (const_arg0))
+                 && label_ref_label (XEXP (y, 1)) == label_ref_label (const_arg0))
                return XEXP (y, 0);
 
              /* Now try for a CONST of a MINUS like the above.  */
@@ -3523,7 +3561,7 @@ fold_rtx (rtx x, rtx_insn *insn)
                        : lookup_as_function (folded_arg1, CONST))) != 0
                  && GET_CODE (XEXP (y, 0)) == MINUS
                  && GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF
-                 && LABEL_REF_LABEL (XEXP (XEXP (y, 0), 1)) == LABEL_REF_LABEL (const_arg0))
+                 && label_ref_label (XEXP (XEXP (y, 0), 1)) == label_ref_label (const_arg0))
                return XEXP (XEXP (y, 0), 0);
            }
 
@@ -3547,7 +3585,7 @@ fold_rtx (rtx x, rtx_insn *insn)
                 instead we test for the problematic value in a more direct
                 manner and hope the Sun compilers get it correct.  */
              && INTVAL (const_arg1) !=
-               ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1))
+               (HOST_WIDE_INT_1 << (HOST_BITS_PER_WIDE_INT - 1))
              && REG_P (folded_arg1))
            {
              rtx new_const = GEN_INT (-INTVAL (const_arg1));
@@ -3565,12 +3603,11 @@ fold_rtx (rtx x, rtx_insn *insn)
        case MINUS:
          /* If we have (MINUS Y C), see if Y is known to be (PLUS Z C2).
             If so, produce (PLUS Z C2-C).  */
-         if (const_arg1 != 0 && CONST_INT_P (const_arg1))
+         if (const_arg1 != 0 && poly_int_rtx_p (const_arg1, &xval))
            {
              rtx y = lookup_as_function (XEXP (x, 0), PLUS);
-             if (y && CONST_INT_P (XEXP (y, 1)))
-               return fold_rtx (plus_constant (mode, copy_rtx (y),
-                                               -INTVAL (const_arg1)),
+             if (y && poly_int_rtx_p (XEXP (y, 1)))
+               return fold_rtx (plus_constant (mode, copy_rtx (y), -xval),
                                 NULL);
            }
 
@@ -3598,13 +3635,13 @@ fold_rtx (rtx x, rtx_insn *insn)
              enum rtx_code associate_code;
 
              if (is_shift
-                 && (INTVAL (const_arg1) >= GET_MODE_PRECISION (mode)
+                 && (INTVAL (const_arg1) >= GET_MODE_UNIT_PRECISION (mode)
                      || INTVAL (const_arg1) < 0))
                {
                  if (SHIFT_COUNT_TRUNCATED)
-                   canon_const_arg1 = GEN_INT (INTVAL (const_arg1)
-                                               & (GET_MODE_BITSIZE (mode)
-                                                  - 1));
+                   canon_const_arg1 = gen_int_shift_amount
+                     (mode, (INTVAL (const_arg1)
+                             & (GET_MODE_UNIT_BITSIZE (mode) - 1)));
                  else
                    break;
                }
@@ -3632,13 +3669,13 @@ fold_rtx (rtx x, rtx_insn *insn)
 
              if (code == PLUS && const_arg1 == inner_const
                  && ((HAVE_PRE_INCREMENT
-                         && exact_log2 (INTVAL (const_arg1)) >= 0)
+                         && pow2p_hwi (INTVAL (const_arg1)))
                      || (HAVE_POST_INCREMENT
-                         && exact_log2 (INTVAL (const_arg1)) >= 0)
+                         && pow2p_hwi (INTVAL (const_arg1)))
                      || (HAVE_PRE_DECREMENT
-                         && exact_log2 (- INTVAL (const_arg1)) >= 0)
+                         && pow2p_hwi (- INTVAL (const_arg1)))
                      || (HAVE_POST_DECREMENT
-                         && exact_log2 (- INTVAL (const_arg1)) >= 0)))
+                         && pow2p_hwi (- INTVAL (const_arg1)))))
                break;
 
              /* ??? Vector mode shifts by scalar
@@ -3647,12 +3684,13 @@ fold_rtx (rtx x, rtx_insn *insn)
                 break;
 
              if (is_shift
-                 && (INTVAL (inner_const) >= GET_MODE_PRECISION (mode)
+                 && (INTVAL (inner_const) >= GET_MODE_UNIT_PRECISION (mode)
                      || INTVAL (inner_const) < 0))
                {
                  if (SHIFT_COUNT_TRUNCATED)
-                   inner_const = GEN_INT (INTVAL (inner_const)
-                                          & (GET_MODE_BITSIZE (mode) - 1));
+                   inner_const = gen_int_shift_amount
+                     (mode, (INTVAL (inner_const)
+                             & (GET_MODE_UNIT_BITSIZE (mode) - 1)));
                  else
                    break;
                }
@@ -3677,12 +3715,13 @@ fold_rtx (rtx x, rtx_insn *insn)
 
              if (is_shift
                  && CONST_INT_P (new_const)
-                 && INTVAL (new_const) >= GET_MODE_PRECISION (mode))
+                 && INTVAL (new_const) >= GET_MODE_UNIT_PRECISION (mode))
                {
                  /* As an exception, we can turn an ASHIFTRT of this
                     form into a shift of the number of bits - 1.  */
                  if (code == ASHIFTRT)
-                   new_const = GEN_INT (GET_MODE_BITSIZE (mode) - 1);
+                   new_const = gen_int_shift_amount
+                     (mode, GET_MODE_UNIT_BITSIZE (mode) - 1);
                  else if (!side_effects_p (XEXP (y, 0)))
                    return CONST0_RTX (mode);
                  else
@@ -3770,17 +3809,20 @@ equiv_constant (rtx x)
       /* See if we previously assigned a constant value to this SUBREG.  */
       if ((new_rtx = lookup_as_function (x, CONST_INT)) != 0
          || (new_rtx = lookup_as_function (x, CONST_WIDE_INT)) != 0
+         || (NUM_POLY_INT_COEFFS > 1
+             && (new_rtx = lookup_as_function (x, CONST_POLY_INT)) != 0)
           || (new_rtx = lookup_as_function (x, CONST_DOUBLE)) != 0
           || (new_rtx = lookup_as_function (x, CONST_FIXED)) != 0)
         return new_rtx;
 
       /* If we didn't and if doing so makes sense, see if we previously
         assigned a constant value to the enclosing word mode SUBREG.  */
-      if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (word_mode)
-         && GET_MODE_SIZE (word_mode) < GET_MODE_SIZE (imode))
+      if (known_lt (GET_MODE_SIZE (mode), UNITS_PER_WORD)
+         && known_lt (UNITS_PER_WORD, GET_MODE_SIZE (imode)))
        {
-         int byte = SUBREG_BYTE (x) - subreg_lowpart_offset (mode, word_mode);
-         if (byte >= 0 && (byte % UNITS_PER_WORD) == 0)
+         poly_int64 byte = (SUBREG_BYTE (x)
+                            - subreg_lowpart_offset (mode, word_mode));
+         if (known_ge (byte, 0) && multiple_p (byte, UNITS_PER_WORD))
            {
              rtx y = gen_rtx_SUBREG (word_mode, SUBREG_REG (x), byte);
              new_rtx = lookup_as_function (y, CONST_INT);
@@ -3794,7 +3836,7 @@ equiv_constant (rtx x)
         the subreg.  Note that the upper bits of paradoxical subregs
         are undefined, so they cannot be said to equal anything.  */
       if (REG_P (SUBREG_REG (x))
-         && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (imode)
+         && !paradoxical_subreg_p (x)
          && (new_rtx = equiv_constant (SUBREG_REG (x))) != 0)
         return simplify_subreg (mode, new_rtx, imode, SUBREG_BYTE (x));
 
@@ -3863,6 +3905,13 @@ record_jump_equiv (rtx_insn *insn, bool taken)
   op0 = fold_rtx (XEXP (XEXP (SET_SRC (set), 0), 0), insn);
   op1 = fold_rtx (XEXP (XEXP (SET_SRC (set), 0), 1), insn);
 
+  /* On a cc0 target the cc0-setter and cc0-user may end up in different
+     blocks.  When that happens the tracking of the cc0-setter via
+     PREV_INSN_CC0 is spoiled.  That means that fold_rtx may return
+     NULL_RTX.  In those cases, there's nothing to record.  */
+  if (op0 == NULL_RTX || op1 == NULL_RTX)
+    return;
+
   code = find_comparison_args (code, &op0, &op1, &mode0, &mode1);
   if (! cond_known_true)
     {
@@ -3937,10 +3986,9 @@ record_jump_cond (enum rtx_code code, machine_mode mode, rtx op0,
      if we test MODE instead, we can get an infinite recursion
      alternating between two modes each wider than MODE.  */
 
-  if (code == NE && GET_CODE (op0) == SUBREG
-      && subreg_lowpart_p (op0)
-      && (GET_MODE_SIZE (GET_MODE (op0))
-         < GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0)))))
+  if (code == NE
+      && partial_subreg_p (op0)
+      && subreg_lowpart_p (op0))
     {
       machine_mode inner_mode = GET_MODE (SUBREG_REG (op0));
       rtx tem = record_jump_cond_subreg (inner_mode, op1);
@@ -3949,10 +3997,9 @@ record_jump_cond (enum rtx_code code, machine_mode mode, rtx op0,
                          reversed_nonequality);
     }
 
-  if (code == NE && GET_CODE (op1) == SUBREG
-      && subreg_lowpart_p (op1)
-      && (GET_MODE_SIZE (GET_MODE (op1))
-         < GET_MODE_SIZE (GET_MODE (SUBREG_REG (op1)))))
+  if (code == NE
+      && partial_subreg_p (op1)
+      && subreg_lowpart_p (op1))
     {
       machine_mode inner_mode = GET_MODE (SUBREG_REG (op1));
       rtx tem = record_jump_cond_subreg (inner_mode, op0);
@@ -4139,10 +4186,10 @@ struct set
      The size of this field should match the size of the mode
      field of struct rtx_def (see rtl.h).  */
   ENUM_BITFIELD(machine_mode) mode : 8;
-  /* A constant equivalent for SET_SRC, if any.  */
-  rtx src_const;
   /* Hash value of constant equivalent for SET_SRC.  */
   unsigned src_const_hash;
+  /* A constant equivalent for SET_SRC, if any.  */
+  rtx src_const;
   /* Table entry for constant equivalent for SET_SRC, if any.  */
   struct table_elt *src_const_elt;
   /* Table entry for the destination address.  */
@@ -4219,6 +4266,15 @@ try_back_substitute_reg (rtx set, rtx_insn *insn)
                  && (reg_mentioned_p (dest, XEXP (note, 0))
                      || rtx_equal_p (src, XEXP (note, 0))))
                remove_note (insn, note);
+
+             /* If INSN has a REG_ARGS_SIZE note, move it to PREV.  */
+             note = find_reg_note (insn, REG_ARGS_SIZE, NULL_RTX);
+             if (note != 0)
+               {
+                 remove_note (insn, note);
+                 gcc_assert (!find_reg_note (prev, REG_ARGS_SIZE, NULL_RTX));
+                 set_unique_reg_note (prev, REG_ARGS_SIZE, XEXP (note, 0));
+               }
            }
        }
     }
@@ -4257,7 +4313,7 @@ find_sets_in_insn (rtx_insn *insn, struct set **psets)
     {
       int i, lim = XVECLEN (x, 0);
 
-      /* Go over the epressions of the PARALLEL in forward order, to
+      /* Go over the expressions of the PARALLEL in forward order, to
         put them in the same order in the SETS array.  */
       for (i = 0; i < lim; i++)
        {
@@ -4280,8 +4336,24 @@ find_sets_in_insn (rtx_insn *insn, struct set **psets)
   return n_sets;
 }
 \f
+/* Subroutine of canonicalize_insn.  X is an ASM_OPERANDS in INSN.  */
+
+static void
+canon_asm_operands (rtx x, rtx_insn *insn)
+{
+  for (int i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--)
+    {
+      rtx input = ASM_OPERANDS_INPUT (x, i);
+      if (!(REG_P (input) && HARD_REGISTER_P (input)))
+       {
+         input = canon_reg (input, insn);
+         validate_change (insn, &ASM_OPERANDS_INPUT (x, i), input, 1);
+       }
+    }
+}
+
 /* Where possible, substitute every register reference in the N_SETS
-   number of SETS in INSN with the the canonical register.
+   number of SETS in INSN with the canonical register.
 
    Register canonicalization propagatest the earliest register (i.e.
    one that is set before INSN) with the same value.  This is a very
@@ -4343,17 +4415,7 @@ canonicalize_insn (rtx_insn *insn, struct set **psets, int n_sets)
     /* Canonicalize a USE of a pseudo register or memory location.  */
     canon_reg (x, insn);
   else if (GET_CODE (x) == ASM_OPERANDS)
-    {
-      for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--)
-       {
-         rtx input = ASM_OPERANDS_INPUT (x, i);
-         if (!(REG_P (input) && REGNO (input) < FIRST_PSEUDO_REGISTER))
-           {
-             input = canon_reg (input, insn);
-             validate_change (insn, &ASM_OPERANDS_INPUT (x, i), input, 1);
-           }
-       }
-    }
+    canon_asm_operands (x, insn);
   else if (GET_CODE (x) == CALL)
     {
       canon_reg (x, insn);
@@ -4382,6 +4444,8 @@ canonicalize_insn (rtx_insn *insn, struct set **psets, int n_sets)
                   && ! (REG_P (XEXP (y, 0))
                         && REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER))
            canon_reg (y, insn);
+         else if (GET_CODE (y) == ASM_OPERANDS)
+           canon_asm_operands (y, insn);
          else if (GET_CODE (y) == CALL)
            {
              canon_reg (y, insn);
@@ -4486,11 +4550,9 @@ cse_insn (rtx_insn *insn)
     sets = XALLOCAVEC (struct set, XVECLEN (x, 0));
 
   this_insn = insn;
-#ifdef HAVE_cc0
   /* Records what this insn does to set CC0.  */
   this_insn_cc0 = 0;
   this_insn_cc0_mode = VOIDmode;
-#endif
 
   /* Find all regs explicitly clobbered in this insn,
      to ensure they are not replaced with any other regs
@@ -4504,14 +4566,53 @@ cse_insn (rtx_insn *insn)
   canonicalize_insn (insn, &sets, n_sets);
 
   /* If this insn has a REG_EQUAL note, store the equivalent value in SRC_EQV,
-     if different, or if the DEST is a STRICT_LOW_PART.  The latter condition
-     is necessary because SRC_EQV is handled specially for this case, and if
-     it isn't set, then there will be no equivalence for the destination.  */
+     if different, or if the DEST is a STRICT_LOW_PART/ZERO_EXTRACT.  The
+     latter condition is necessary because SRC_EQV is handled specially for
+     this case, and if it isn't set, then there will be no equivalence
+     for the destination.  */
   if (n_sets == 1 && REG_NOTES (insn) != 0
-      && (tem = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0
-      && (! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl))
-         || GET_CODE (SET_DEST (sets[0].rtl)) == STRICT_LOW_PART))
-    src_eqv = copy_rtx (XEXP (tem, 0));
+      && (tem = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0)
+    {
+
+      if (GET_CODE (SET_DEST (sets[0].rtl)) != ZERO_EXTRACT
+         && (! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl))
+             || GET_CODE (SET_DEST (sets[0].rtl)) == STRICT_LOW_PART))
+       src_eqv = copy_rtx (XEXP (tem, 0));
+      /* If DEST is of the form ZERO_EXTACT, as in:
+        (set (zero_extract:SI (reg:SI 119)
+                 (const_int 16 [0x10])
+                 (const_int 16 [0x10]))
+             (const_int 51154 [0xc7d2]))
+        REG_EQUAL note will specify the value of register (reg:SI 119) at this
+        point.  Note that this is different from SRC_EQV. We can however
+        calculate SRC_EQV with the position and width of ZERO_EXTRACT.  */
+      else if (GET_CODE (SET_DEST (sets[0].rtl)) == ZERO_EXTRACT
+              && CONST_INT_P (XEXP (tem, 0))
+              && CONST_INT_P (XEXP (SET_DEST (sets[0].rtl), 1))
+              && CONST_INT_P (XEXP (SET_DEST (sets[0].rtl), 2)))
+       {
+         rtx dest_reg = XEXP (SET_DEST (sets[0].rtl), 0);
+         /* This is the mode of XEXP (tem, 0) as well.  */
+         scalar_int_mode dest_mode
+           = as_a <scalar_int_mode> (GET_MODE (dest_reg));
+         rtx width = XEXP (SET_DEST (sets[0].rtl), 1);
+         rtx pos = XEXP (SET_DEST (sets[0].rtl), 2);
+         HOST_WIDE_INT val = INTVAL (XEXP (tem, 0));
+         HOST_WIDE_INT mask;
+         unsigned int shift;
+         if (BITS_BIG_ENDIAN)
+           shift = (GET_MODE_PRECISION (dest_mode)
+                    - INTVAL (pos) - INTVAL (width));
+         else
+           shift = INTVAL (pos);
+         if (INTVAL (width) == HOST_BITS_PER_WIDE_INT)
+           mask = HOST_WIDE_INT_M1;
+         else
+           mask = (HOST_WIDE_INT_1 << INTVAL (width)) - 1;
+         val = (val >> shift) & mask;
+         src_eqv = GEN_INT (val);
+       }
+    }
 
   /* Set sets[i].src_elt to the class each source belongs to.
      Detect assignments from or to volatile things
@@ -4523,6 +4624,7 @@ cse_insn (rtx_insn *insn)
   for (i = 0; i < n_sets; i++)
     {
       bool repeat = false;
+      bool noop_insn = false;
       rtx src, dest;
       rtx src_folded;
       struct table_elt *elt = 0, *p;
@@ -4545,6 +4647,7 @@ cse_insn (rtx_insn *insn)
       /* Set nonzero if we need to call force_const_mem on with the
         contents of src_folded before using it.  */
       int src_folded_force_flag = 0;
+      scalar_int_mode int_mode;
 
       dest = SET_DEST (sets[i].rtl);
       src = SET_SRC (sets[i].rtl);
@@ -4584,7 +4687,7 @@ cse_insn (rtx_insn *insn)
 
       /* Simplify and foldable subexpressions in SRC.  Then get the fully-
         simplified result, which may not necessarily be valid.  */
-      src_folded = fold_rtx (src, insn);
+      src_folded = fold_rtx (src, NULL);
 
 #if 0
       /* ??? This caused bad code to be generated for the m68k port with -O2.
@@ -4604,15 +4707,27 @@ cse_insn (rtx_insn *insn)
              && INTVAL (width) < HOST_BITS_PER_WIDE_INT
              && (INTVAL (src) & ((HOST_WIDE_INT) (-1) << INTVAL (width))))
            src_folded
-             = GEN_INT (INTVAL (src) & (((HOST_WIDE_INT) 1
+             = GEN_INT (INTVAL (src) & ((HOST_WIDE_INT_1
                                          << INTVAL (width)) - 1));
        }
 #endif
 
       /* Compute SRC's hash code, and also notice if it
         should not be recorded at all.  In that case,
-        prevent any further processing of this assignment.  */
-      do_not_record = 0;
+        prevent any further processing of this assignment.
+
+        We set DO_NOT_RECORD if the destination has a REG_UNUSED note.
+        This avoids getting the source register into the tables, where it
+        may be invalidated later (via REG_QTY), then trigger an ICE upon
+        re-insertion.
+
+        This is only a problem in multi-set insns.  If it were a single
+        set the dead copy would have been removed.  If the RHS were anything
+        but a simple REG, then we won't call insert_regs and thus there's
+        no potential for triggering the ICE.  */
+      do_not_record = (REG_P (dest)
+                      && REG_P (src)
+                      && find_reg_note (insn, REG_UNUSED, dest));
       hash_arg_in_memory = 0;
 
       sets[i].src = src;
@@ -4633,12 +4748,27 @@ cse_insn (rtx_insn *insn)
          && REGNO (dest) >= FIRST_PSEUDO_REGISTER)
        sets[i].src_volatile = 1;
 
-      /* Also do not record result of a non-volatile inline asm with
-        more than one result or with clobbers, we do not want CSE to
-        break the inline asm apart.  */
       else if (GET_CODE (src) == ASM_OPERANDS
               && GET_CODE (x) == PARALLEL)
-       sets[i].src_volatile = 1;
+       {
+         /* Do not record result of a non-volatile inline asm with
+            more than one result.  */
+         if (n_sets > 1)
+           sets[i].src_volatile = 1;
+
+         int j, lim = XVECLEN (x, 0);
+         for (j = 0; j < lim; j++)
+           {
+             rtx y = XVECEXP (x, 0, j);
+             /* And do not record result of a non-volatile inline asm
+                with "memory" clobber.  */
+             if (GET_CODE (y) == CLOBBER && MEM_P (XEXP (y, 0)))
+               {
+                 sets[i].src_volatile = 1;
+                 break;
+               }
+           }
+       }
 
 #if 0
       /* It is no longer clear why we used to do this, but it doesn't
@@ -4767,17 +4897,16 @@ cse_insn (rtx_insn *insn)
         wider mode.  */
 
       if (src_const && src_related == 0 && CONST_INT_P (src_const)
-         && GET_MODE_CLASS (mode) == MODE_INT
-         && GET_MODE_PRECISION (mode) < BITS_PER_WORD)
+         && is_int_mode (mode, &int_mode)
+         && GET_MODE_PRECISION (int_mode) < BITS_PER_WORD)
        {
-         machine_mode wider_mode;
-
-         for (wider_mode = GET_MODE_WIDER_MODE (mode);
-              wider_mode != VOIDmode
-              && GET_MODE_PRECISION (wider_mode) <= BITS_PER_WORD
-              && src_related == 0;
-              wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+         opt_scalar_int_mode wider_mode_iter;
+         FOR_EACH_WIDER_MODE (wider_mode_iter, int_mode)
            {
+             scalar_int_mode wider_mode = wider_mode_iter.require ();
+             if (GET_MODE_PRECISION (wider_mode) > BITS_PER_WORD)
+               break;
+
              struct table_elt *const_elt
                = lookup (src_const, HASH (src_const, wider_mode), wider_mode);
 
@@ -4788,9 +4917,12 @@ cse_insn (rtx_insn *insn)
                   const_elt; const_elt = const_elt->next_same_value)
                if (REG_P (const_elt->exp))
                  {
-                   src_related = gen_lowpart (mode, const_elt->exp);
+                   src_related = gen_lowpart (int_mode, const_elt->exp);
                    break;
                  }
+
+             if (src_related != 0)
+               break;
            }
        }
 
@@ -4801,16 +4933,19 @@ cse_insn (rtx_insn *insn)
         value.  */
 
       if (flag_expensive_optimizations && ! src_related
+         && is_a <scalar_int_mode> (mode, &int_mode)
          && GET_CODE (src) == AND && CONST_INT_P (XEXP (src, 1))
-         && GET_MODE_SIZE (mode) < UNITS_PER_WORD)
+         && GET_MODE_SIZE (int_mode) < UNITS_PER_WORD)
        {
-         machine_mode tmode;
+         opt_scalar_int_mode tmode_iter;
          rtx new_and = gen_rtx_AND (VOIDmode, NULL_RTX, XEXP (src, 1));
 
-         for (tmode = GET_MODE_WIDER_MODE (mode);
-              GET_MODE_SIZE (tmode) <= UNITS_PER_WORD;
-              tmode = GET_MODE_WIDER_MODE (tmode))
+         FOR_EACH_WIDER_MODE (tmode_iter, int_mode)
            {
+             scalar_int_mode tmode = tmode_iter.require ();
+             if (GET_MODE_SIZE (tmode) > UNITS_PER_WORD)
+               break;
+
              rtx inner = gen_lowpart (tmode, XEXP (src, 0));
              struct table_elt *larger_elt;
 
@@ -4827,7 +4962,7 @@ cse_insn (rtx_insn *insn)
                    if (REG_P (larger_elt->exp))
                      {
                        src_related
-                         = gen_lowpart (mode, larger_elt->exp);
+                         = gen_lowpart (int_mode, larger_elt->exp);
                        break;
                      }
 
@@ -4837,34 +4972,35 @@ cse_insn (rtx_insn *insn)
            }
        }
 
-#ifdef LOAD_EXTEND_OP
       /* See if a MEM has already been loaded with a widening operation;
         if it has, we can use a subreg of that.  Many CISC machines
         also have such operations, but this is only likely to be
         beneficial on these machines.  */
 
+      rtx_code extend_op;
       if (flag_expensive_optimizations && src_related == 0
-         && (GET_MODE_SIZE (mode) < UNITS_PER_WORD)
-         && GET_MODE_CLASS (mode) == MODE_INT
          && MEM_P (src) && ! do_not_record
-         && LOAD_EXTEND_OP (mode) != UNKNOWN)
+         && is_a <scalar_int_mode> (mode, &int_mode)
+         && (extend_op = load_extend_op (int_mode)) != UNKNOWN)
        {
          struct rtx_def memory_extend_buf;
          rtx memory_extend_rtx = &memory_extend_buf;
-         machine_mode tmode;
 
          /* Set what we are trying to extend and the operation it might
             have been extended with.  */
          memset (memory_extend_rtx, 0, sizeof (*memory_extend_rtx));
-         PUT_CODE (memory_extend_rtx, LOAD_EXTEND_OP (mode));
+         PUT_CODE (memory_extend_rtx, extend_op);
          XEXP (memory_extend_rtx, 0) = src;
 
-         for (tmode = GET_MODE_WIDER_MODE (mode);
-              GET_MODE_SIZE (tmode) <= UNITS_PER_WORD;
-              tmode = GET_MODE_WIDER_MODE (tmode))
+         opt_scalar_int_mode tmode_iter;
+         FOR_EACH_WIDER_MODE (tmode_iter, int_mode)
            {
              struct table_elt *larger_elt;
 
+             scalar_int_mode tmode = tmode_iter.require ();
+             if (GET_MODE_SIZE (tmode) > UNITS_PER_WORD)
+               break;
+
              PUT_MODE (memory_extend_rtx, tmode);
              larger_elt = lookup (memory_extend_rtx,
                                   HASH (memory_extend_rtx, tmode), tmode);
@@ -4875,7 +5011,7 @@ cse_insn (rtx_insn *insn)
                   larger_elt; larger_elt = larger_elt->next_same_value)
                if (REG_P (larger_elt->exp))
                  {
-                   src_related = gen_lowpart (mode, larger_elt->exp);
+                   src_related = gen_lowpart (int_mode, larger_elt->exp);
                    break;
                  }
 
@@ -4883,7 +5019,6 @@ cse_insn (rtx_insn *insn)
                break;
            }
        }
-#endif /* LOAD_EXTEND_OP */
 
       /* Try to express the constant using a register+offset expression
         derived from a constant anchor.  */
@@ -4930,8 +5065,8 @@ cse_insn (rtx_insn *insn)
              && ! (src != 0
                    && GET_CODE (src) == SUBREG
                    && GET_MODE (src) == GET_MODE (p->exp)
-                   && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))
-                       < GET_MODE_SIZE (GET_MODE (SUBREG_REG (p->exp))))))
+                   && partial_subreg_p (GET_MODE (SUBREG_REG (src)),
+                                        GET_MODE (SUBREG_REG (p->exp)))))
            continue;
 
          if (src && GET_CODE (src) == code && rtx_equal_p (src, p->exp))
@@ -4950,7 +5085,7 @@ cse_insn (rtx_insn *insn)
             to prefer it.  Copy it to src_related.  The code below will
             then give it a negative cost.  */
          if (GET_CODE (dest) == code && rtx_equal_p (p->exp, dest))
-           src_related = dest;
+           src_related = p->exp;
        }
 
       /* Find the cheapest valid equivalent, trying all the available
@@ -4965,7 +5100,7 @@ cse_insn (rtx_insn *insn)
            src_cost = src_regcost = -1;
          else
            {
-             src_cost = COST (src);
+             src_cost = COST (src, mode);
              src_regcost = approx_reg_cost (src);
            }
        }
@@ -4976,7 +5111,7 @@ cse_insn (rtx_insn *insn)
            src_eqv_cost = src_eqv_regcost = -1;
          else
            {
-             src_eqv_cost = COST (src_eqv_here);
+             src_eqv_cost = COST (src_eqv_here, mode);
              src_eqv_regcost = approx_reg_cost (src_eqv_here);
            }
        }
@@ -4987,7 +5122,7 @@ cse_insn (rtx_insn *insn)
            src_folded_cost = src_folded_regcost = -1;
          else
            {
-             src_folded_cost = COST (src_folded);
+             src_folded_cost = COST (src_folded, mode);
              src_folded_regcost = approx_reg_cost (src_folded);
            }
        }
@@ -4998,7 +5133,7 @@ cse_insn (rtx_insn *insn)
            src_related_cost = src_related_regcost = -1;
          else
            {
-             src_related_cost = COST (src_related);
+             src_related_cost = COST (src_related, mode);
              src_related_regcost = approx_reg_cost (src_related);
 
              /* If a const-anchor is used to synthesize a constant that
@@ -5041,8 +5176,8 @@ cse_insn (rtx_insn *insn)
              && ! (src != 0
                    && GET_CODE (src) == SUBREG
                    && GET_MODE (src) == GET_MODE (elt->exp)
-                   && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))
-                       < GET_MODE_SIZE (GET_MODE (SUBREG_REG (elt->exp))))))
+                   && partial_subreg_p (GET_MODE (SUBREG_REG (src)),
+                                        GET_MODE (SUBREG_REG (elt->exp)))))
            {
              elt = elt->next_same_value;
              continue;
@@ -5100,23 +5235,6 @@ cse_insn (rtx_insn *insn)
              src_elt_cost = MAX_COST;
            }
 
-         /* Avoid creation of overlapping memory moves.  */
-         if (MEM_P (trial) && MEM_P (SET_DEST (sets[i].rtl)))
-           {
-             rtx src, dest;
-
-             /* BLKmode moves are not handled by cse anyway.  */
-             if (GET_MODE (trial) == BLKmode)
-               break;
-
-             src = canon_rtx (trial);
-             dest = canon_rtx (SET_DEST (sets[i].rtl));
-
-             if (!MEM_P (src) || !MEM_P (dest)
-                 || !nonoverlapping_memrefs_p (src, dest, false))
-               break;
-           }
-
          /* Try to optimize
             (set (reg:M N) (const_int A))
             (set (reg:M2 O) (const_int B))
@@ -5127,8 +5245,9 @@ cse_insn (rtx_insn *insn)
              && CONST_INT_P (XEXP (SET_DEST (sets[i].rtl), 1))
              && CONST_INT_P (XEXP (SET_DEST (sets[i].rtl), 2))
              && REG_P (XEXP (SET_DEST (sets[i].rtl), 0))
-             && (GET_MODE_PRECISION (GET_MODE (SET_DEST (sets[i].rtl)))
-                 >= INTVAL (XEXP (SET_DEST (sets[i].rtl), 1)))
+             && (known_ge
+                 (GET_MODE_PRECISION (GET_MODE (SET_DEST (sets[i].rtl))),
+                  INTVAL (XEXP (SET_DEST (sets[i].rtl), 1))))
              && ((unsigned) INTVAL (XEXP (SET_DEST (sets[i].rtl), 1))
                  + (unsigned) INTVAL (XEXP (SET_DEST (sets[i].rtl), 2))
                  <= HOST_BITS_PER_WIDE_INT))
@@ -5153,18 +5272,21 @@ cse_insn (rtx_insn *insn)
                  HOST_WIDE_INT val = INTVAL (dest_cst);
                  HOST_WIDE_INT mask;
                  unsigned int shift;
+                 /* This is the mode of DEST_CST as well.  */
+                 scalar_int_mode dest_mode
+                   = as_a <scalar_int_mode> (GET_MODE (dest_reg));
                  if (BITS_BIG_ENDIAN)
-                   shift = GET_MODE_PRECISION (GET_MODE (dest_reg))
+                   shift = GET_MODE_PRECISION (dest_mode)
                            - INTVAL (pos) - INTVAL (width);
                  else
                    shift = INTVAL (pos);
                  if (INTVAL (width) == HOST_BITS_PER_WIDE_INT)
-                   mask = ~(HOST_WIDE_INT) 0;
+                   mask = HOST_WIDE_INT_M1;
                  else
-                   mask = ((HOST_WIDE_INT) 1 << INTVAL (width)) - 1;
+                   mask = (HOST_WIDE_INT_1 << INTVAL (width)) - 1;
                  val &= ~(mask << shift);
                  val |= (INTVAL (trial) & mask) << shift;
-                 val = trunc_int_for_mode (val, GET_MODE (dest_reg));
+                 val = trunc_int_for_mode (val, dest_mode);
                  validate_unshare_change (insn, &SET_DEST (sets[i].rtl),
                                           dest_reg, 1);
                  validate_unshare_change (insn, &SET_SRC (sets[i].rtl),
@@ -5212,6 +5334,31 @@ cse_insn (rtx_insn *insn)
              break;
            }
 
+         /* Similarly, lots of targets don't allow no-op
+            (set (mem x) (mem x)) moves.  Even (set (reg x) (reg x))
+            might be impossible for certain registers (like CC registers).  */
+         else if (n_sets == 1
+                  && !CALL_P (insn)
+                  && (MEM_P (trial) || REG_P (trial))
+                  && rtx_equal_p (trial, dest)
+                  && !side_effects_p (dest)
+                  && (cfun->can_delete_dead_exceptions
+                      || insn_nothrow_p (insn))
+                  /* We can only remove the later store if the earlier aliases
+                     at least all accesses the later one.  */
+                  && (!MEM_P (trial)
+                      || ((MEM_ALIAS_SET (dest) == MEM_ALIAS_SET (trial)
+                           || alias_set_subset_of (MEM_ALIAS_SET (dest),
+                                                   MEM_ALIAS_SET (trial)))
+                           && (!MEM_EXPR (trial)
+                               || refs_same_for_tbaa_p (MEM_EXPR (trial),
+                                                        MEM_EXPR (dest))))))
+           {
+             SET_SRC (sets[i].rtl) = trial;
+             noop_insn = true;
+             break;
+           }
+
          /* Reject certain invalid forms of CONST that we create.  */
          else if (CONSTANT_P (trial)
                   && GET_CODE (trial) == CONST
@@ -5228,9 +5375,15 @@ cse_insn (rtx_insn *insn)
            /* Do nothing for this case.  */
            ;
 
+         /* Do not replace anything with a MEM, except the replacement
+            is a no-op.  This allows this loop to terminate.  */
+         else if (MEM_P (trial) && !rtx_equal_p (trial, SET_SRC(sets[i].rtl)))
+           /* Do nothing for this case.  */
+           ;
+
          /* Look for a substitution that makes a valid insn.  */
-         else if (validate_unshare_change
-                    (insn, &SET_SRC (sets[i].rtl), trial, 0))
+         else if (validate_unshare_change (insn, &SET_SRC (sets[i].rtl),
+                                           trial, 0))
            {
              rtx new_rtx = canon_reg (SET_SRC (sets[i].rtl), insn);
 
@@ -5311,7 +5464,7 @@ cse_insn (rtx_insn *insn)
                  /* If we had a constant that is cheaper than what we are now
                     setting SRC to, use that constant.  We ignored it when we
                     thought we could make this into a no-op.  */
-                 if (src_const && COST (src_const) < COST (src)
+                 if (src_const && COST (src_const, mode) < COST (src, mode)
                      && validate_change (insn, &SET_SRC (sets[i].rtl),
                                          src_const, 0))
                    src = src_const;
@@ -5424,12 +5577,22 @@ cse_insn (rtx_insn *insn)
       else if (n_sets == 1 && dest == pc_rtx && src == pc_rtx)
        {
          /* One less use of the label this insn used to jump to.  */
-         delete_insn_and_edges (insn);
+         cse_cfg_altered |= delete_insn_and_edges (insn);
          cse_jumps_altered = true;
          /* No more processing for this set.  */
          sets[i].rtl = 0;
        }
 
+      /* Similarly for no-op moves.  */
+      else if (noop_insn)
+       {
+         if (cfun->can_throw_non_call_exceptions && can_throw_internal (insn))
+           cse_cfg_altered = true;
+         cse_cfg_altered |= delete_insn_and_edges (insn);
+         /* No more processing for this set.  */
+         sets[i].rtl = 0;
+       }
+
       /* If this SET is now setting PC to a label, we know it used to
         be a conditional or computed branch.  */
       else if (dest == pc_rtx && GET_CODE (src) == LABEL_REF
@@ -5444,10 +5607,11 @@ cse_insn (rtx_insn *insn)
             and hope for the best.  */
          if (n_sets == 1)
            {
-             rtx_insn *new_rtx;
+             rtx_jump_insn *new_rtx;
              rtx note;
 
-             new_rtx = emit_jump_insn_before (gen_jump (XEXP (src, 0)), insn);
+             rtx_insn *seq = targetm.gen_jump (XEXP (src, 0));
+             new_rtx = emit_jump_insn_before (seq, insn);
              JUMP_LABEL (new_rtx) = XEXP (src, 0);
              LABEL_NUSES (XEXP (src, 0))++;
 
@@ -5459,7 +5623,7 @@ cse_insn (rtx_insn *insn)
                  REG_NOTES (new_rtx) = note;
                }
 
-             delete_insn_and_edges (insn);
+             cse_cfg_altered |= delete_insn_and_edges (insn);
              insn = new_rtx;
            }
          else
@@ -5475,20 +5639,21 @@ cse_insn (rtx_insn *insn)
 
       else if (do_not_record)
        {
-         if (REG_P (dest) || GET_CODE (dest) == SUBREG)
-           invalidate (dest, VOIDmode);
-         else if (MEM_P (dest))
-           invalidate (dest, VOIDmode);
-         else if (GET_CODE (dest) == STRICT_LOW_PART
-                  || GET_CODE (dest) == ZERO_EXTRACT)
-           invalidate (XEXP (dest, 0), GET_MODE (dest));
+         invalidate_dest (dest);
          sets[i].rtl = 0;
        }
 
       if (sets[i].rtl != 0 && dest != SET_DEST (sets[i].rtl))
-       sets[i].dest_hash = HASH (SET_DEST (sets[i].rtl), mode);
+       {
+         do_not_record = 0;
+         sets[i].dest_hash = HASH (SET_DEST (sets[i].rtl), mode);
+         if (do_not_record)
+           {
+             invalidate_dest (SET_DEST (sets[i].rtl));
+             sets[i].rtl = 0;
+           }
+       }
 
-#ifdef HAVE_cc0
       /* If setting CC0, record what it was set to, or a constant, if it
         is equivalent to a constant.  If it is being set to a floating-point
         value, make a COMPARE with the appropriate constant of 0.  If we
@@ -5503,7 +5668,6 @@ cse_insn (rtx_insn *insn)
            this_insn_cc0 = gen_rtx_COMPARE (VOIDmode, this_insn_cc0,
                                             CONST0_RTX (mode));
        }
-#endif
     }
 
   /* Now enter all non-volatile source expressions in the hash table
@@ -5592,6 +5756,12 @@ cse_insn (rtx_insn *insn)
                  }
                elt = insert (src, classp, sets[i].src_hash, mode);
                elt->in_memory = sets[i].src_in_memory;
+               /* If inline asm has any clobbers, ensure we only reuse
+                  existing inline asms and never try to put the ASM_OPERANDS
+                  into an insn that isn't inline asm.  */
+               if (GET_CODE (src) == ASM_OPERANDS
+                   && GET_CODE (x) == PARALLEL)
+                 elt->cost = MAX_COST;
                sets[i].src_elt = classp = elt;
              }
            if (sets[i].src_const && sets[i].src_const_elt == 0
@@ -5653,7 +5823,14 @@ cse_insn (rtx_insn *insn)
     {
       if (!(RTL_CONST_OR_PURE_CALL_P (insn)))
        invalidate_memory ();
-      invalidate_for_call ();
+      else
+       /* For const/pure calls, invalidate any argument slots, because
+          those are owned by the callee.  */
+       for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1))
+         if (GET_CODE (XEXP (tem, 0)) == USE
+             && MEM_P (XEXP (XEXP (tem, 0), 0)))
+           invalidate (XEXP (XEXP (tem, 0), 0), VOIDmode);
+      invalidate_for_call (insn);
     }
 
   /* Now invalidate everything set by this instruction.
@@ -5785,15 +5962,7 @@ cse_insn (rtx_insn *insn)
            || GET_MODE (dest) == BLKmode
            /* If we didn't put a REG_EQUAL value or a source into the hash
               table, there is no point is recording DEST.  */
-           || sets[i].src_elt == 0
-           /* If DEST is a paradoxical SUBREG and SRC is a ZERO_EXTEND
-              or SIGN_EXTEND, don't record DEST since it can cause
-              some tracking to be wrong.
-
-              ??? Think about this more later.  */
-           || (paradoxical_subreg_p (dest)
-               && (GET_CODE (sets[i].src) == SIGN_EXTEND
-                   || GET_CODE (sets[i].src) == ZERO_EXTEND)))
+           || sets[i].src_elt == 0)
          continue;
 
        /* STRICT_LOW_PART isn't part of the value BEING set,
@@ -5812,6 +5981,11 @@ cse_insn (rtx_insn *insn)
              sets[i].dest_hash = HASH (dest, GET_MODE (dest));
            }
 
+       /* If DEST is a paradoxical SUBREG, don't record DEST since the bits
+          outside the mode of GET_MODE (SUBREG_REG (dest)) are undefined.  */
+       if (paradoxical_subreg_p (dest))
+         continue;
+
        elt = insert (dest, sets[i].src_elt,
                      sets[i].dest_hash, GET_MODE (dest));
 
@@ -5842,11 +6016,11 @@ cse_insn (rtx_insn *insn)
           already entered SRC and DEST of the SET in the table.  */
 
        if (GET_CODE (dest) == SUBREG
-           && (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) - 1)
-                / UNITS_PER_WORD)
-               == (GET_MODE_SIZE (GET_MODE (dest)) - 1) / UNITS_PER_WORD)
-           && (GET_MODE_SIZE (GET_MODE (dest))
-               >= GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))))
+           && (known_equal_after_align_down
+               (GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) - 1,
+                GET_MODE_SIZE (GET_MODE (dest)) - 1,
+                UNITS_PER_WORD))
+           && !partial_subreg_p (dest)
            && sets[i].src_elt != 0)
          {
            machine_mode new_mode = GET_MODE (SUBREG_REG (dest));
@@ -5858,7 +6032,6 @@ cse_insn (rtx_insn *insn)
                rtx new_src = 0;
                unsigned src_hash;
                struct table_elt *src_elt;
-               int byte = 0;
 
                /* Ignore invalid entries.  */
                if (!REG_P (elt->exp)
@@ -5871,13 +6044,8 @@ cse_insn (rtx_insn *insn)
                  new_src = elt->exp;
                else
                  {
-                   /* Calculate big endian correction for the SUBREG_BYTE.
-                      We have already checked that M1 (GET_MODE (dest))
-                      is not narrower than M2 (new_mode).  */
-                   if (BYTES_BIG_ENDIAN)
-                     byte = (GET_MODE_SIZE (GET_MODE (dest))
-                             - GET_MODE_SIZE (new_mode));
-
+                   poly_uint64 byte
+                     = subreg_lowpart_offset (new_mode, GET_MODE (dest));
                    new_src = simplify_gen_subreg (new_mode, elt->exp,
                                                   GET_MODE (dest), byte);
                  }
@@ -5905,6 +6073,9 @@ cse_insn (rtx_insn *insn)
                      }
                    src_elt = insert (new_src, classp, src_hash, new_mode);
                    src_elt->in_memory = elt->in_memory;
+                   if (GET_CODE (new_src) == ASM_OPERANDS
+                       && elt->cost == MAX_COST)
+                     src_elt->cost = MAX_COST;
                  }
                else if (classp && classp != src_elt->first_same_value)
                  /* Show that two things that we've seen before are
@@ -6013,8 +6184,11 @@ invalidate_from_sets_and_clobbers (rtx_insn *insn)
   if (CALL_P (insn))
     {
       for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1))
-       if (GET_CODE (XEXP (tem, 0)) == CLOBBER)
-         invalidate (SET_DEST (XEXP (tem, 0)), VOIDmode);
+       {
+         rtx temx = XEXP (tem, 0);
+         if (GET_CODE (temx) == CLOBBER)
+           invalidate (SET_DEST (temx), VOIDmode);
+       }
     }
 
   /* Ensure we invalidate the destination register of a CALL insn.
@@ -6047,75 +6221,28 @@ invalidate_from_sets_and_clobbers (rtx_insn *insn)
     }
 }
 \f
-/* Process X, part of the REG_NOTES of an insn.  Look at any REG_EQUAL notes
-   and replace any registers in them with either an equivalent constant
-   or the canonical form of the register.  If we are inside an address,
-   only do this if the address remains valid.
+static rtx cse_process_note (rtx);
 
-   OBJECT is 0 except when within a MEM in which case it is the MEM.
+/* A simplify_replace_fn_rtx callback for cse_process_note.  Process X,
+   part of the REG_NOTES of an insn.  Replace any registers with either
+   an equivalent constant or the canonical form of the register.
+   Only replace addresses if the containing MEM remains valid.
 
-   Return the replacement for X.  */
+   Return the replacement for X, or null if it should be simplified
+   recursively.  */
 
 static rtx
-cse_process_notes_1 (rtx x, rtx object, bool *changed)
+cse_process_note_1 (rtx x, const_rtx, void *)
 {
-  enum rtx_code code = GET_CODE (x);
-  const char *fmt = GET_RTX_FORMAT (code);
-  int i;
-
-  switch (code)
+  if (MEM_P (x))
     {
-    case CONST:
-    case SYMBOL_REF:
-    case LABEL_REF:
-    CASE_CONST_ANY:
-    case PC:
-    case CC0:
-    case LO_SUM:
-      return x;
-
-    case MEM:
-      validate_change (x, &XEXP (x, 0),
-                      cse_process_notes (XEXP (x, 0), x, changed), 0);
-      return x;
-
-    case EXPR_LIST:
-      if (REG_NOTE_KIND (x) == REG_EQUAL)
-       XEXP (x, 0) = cse_process_notes (XEXP (x, 0), NULL_RTX, changed);
-      /* Fall through.  */
-
-    case INSN_LIST:
-    case INT_LIST:
-      if (XEXP (x, 1))
-       XEXP (x, 1) = cse_process_notes (XEXP (x, 1), NULL_RTX, changed);
+      validate_change (x, &XEXP (x, 0), cse_process_note (XEXP (x, 0)), false);
       return x;
+    }
 
-    case SIGN_EXTEND:
-    case ZERO_EXTEND:
-    case SUBREG:
-      {
-       rtx new_rtx = cse_process_notes (XEXP (x, 0), object, changed);
-       /* We don't substitute VOIDmode constants into these rtx,
-          since they would impede folding.  */
-       if (GET_MODE (new_rtx) != VOIDmode)
-         validate_change (object, &XEXP (x, 0), new_rtx, 0);
-       return x;
-      }
-
-    case UNSIGNED_FLOAT:
-      {
-       rtx new_rtx = cse_process_notes (XEXP (x, 0), object, changed);
-       /* We don't substitute negative VOIDmode constants into these rtx,
-          since they would impede folding.  */
-       if (GET_MODE (new_rtx) != VOIDmode
-           || (CONST_INT_P (new_rtx) && INTVAL (new_rtx) >= 0)
-           || (CONST_DOUBLE_P (new_rtx) && CONST_DOUBLE_HIGH (new_rtx) >= 0))
-         validate_change (object, &XEXP (x, 0), new_rtx, 0);
-       return x;
-      }
-
-    case REG:
-      i = REG_QTY (REGNO (x));
+  if (REG_P (x))
+    {
+      int i = REG_QTY (REGNO (x));
 
       /* Return a constant or a constant register.  */
       if (REGNO_QTY_VALID_P (REGNO (x)))
@@ -6134,26 +6261,19 @@ cse_process_notes_1 (rtx x, rtx object, bool *changed)
 
       /* Otherwise, canonicalize this register.  */
       return canon_reg (x, NULL);
-
-    default:
-      break;
     }
 
-  for (i = 0; i < GET_RTX_LENGTH (code); i++)
-    if (fmt[i] == 'e')
-      validate_change (object, &XEXP (x, i),
-                      cse_process_notes (XEXP (x, i), object, changed), 0);
-
-  return x;
+  return NULL_RTX;
 }
 
+/* Process X, part of the REG_NOTES of an insn.  Replace any registers in it
+   with either an equivalent constant or the canonical form of the register.
+   Only replace addresses if the containing MEM remains valid.  */
+
 static rtx
-cse_process_notes (rtx x, rtx object, bool *changed)
+cse_process_note (rtx x)
 {
-  rtx new_rtx = cse_process_notes_1 (x, object, changed);
-  if (new_rtx != x)
-    *changed = true;
-  return new_rtx;
+  return simplify_replace_fn_rtx (x, NULL_RTX, cse_process_note_1, NULL);
 }
 
 \f
@@ -6260,7 +6380,7 @@ cse_find_path (basic_block first_bb, struct cse_basic_block_data *data,
   if (follow_jumps)
     {
       bb = data->path[path_size - 1].bb;
-      while (bb && path_size < PARAM_VALUE (PARAM_MAX_CSE_PATH_LENGTH))
+      while (bb && path_size < param_max_cse_path_length)
        {
          if (single_succ_p (bb))
            e = single_succ_edge (bb);
@@ -6310,7 +6430,7 @@ cse_dump_path (struct cse_basic_block_data *data, int nsets, FILE *f)
   fprintf (f, ";; Following path with %d sets: ", nsets);
   for (path_entry = 0; path_entry < data->path_size; path_entry++)
     fprintf (f, "%d ", (data->path[path_entry].bb)->index);
-  fputc ('\n', dump_file);
+  fputc ('\n', f);
   fflush (f);
 }
 
@@ -6383,10 +6503,10 @@ check_for_label_ref (rtx_insn *insn)
       if (GET_CODE (x) == LABEL_REF
          && !LABEL_REF_NONLOCAL_P (x)
          && (!JUMP_P (insn)
-             || !label_is_jump_target_p (LABEL_REF_LABEL (x), insn))
-         && LABEL_P (LABEL_REF_LABEL (x))
-         && INSN_UID (LABEL_REF_LABEL (x)) != 0
-         && !find_reg_note (insn, REG_LABEL_OPERAND, LABEL_REF_LABEL (x)))
+             || !label_is_jump_target_p (label_ref_label (x), insn))
+         && LABEL_P (label_ref_label (x))
+         && INSN_UID (label_ref_label (x)) != 0
+         && !find_reg_note (insn, REG_LABEL_OPERAND, label_ref_label (x)))
        return true;
     }
   return false;
@@ -6438,7 +6558,7 @@ cse_extended_basic_block (struct cse_basic_block_data *ebb_data)
             FIXME: This is a real kludge and needs to be done some other
                    way.  */
          if (NONDEBUG_INSN_P (insn)
-             && num_insns++ > PARAM_VALUE (PARAM_MAX_CSE_INSNS))
+             && num_insns++ > param_max_cse_insns)
            {
              flush_hash_table ();
              num_insns = 0;
@@ -6448,14 +6568,19 @@ cse_extended_basic_block (struct cse_basic_block_data *ebb_data)
            {
              /* Process notes first so we have all notes in canonical forms
                 when looking for duplicate operations.  */
-             if (REG_NOTES (insn))
-               {
-                 bool changed = false;
-                 REG_NOTES (insn) = cse_process_notes (REG_NOTES (insn),
-                                                       NULL_RTX, &changed);
-                 if (changed)
-                   df_notes_rescan (insn);
-               }
+             bool changed = false;
+             for (rtx note = REG_NOTES (insn); note; note = XEXP (note, 1))
+               if (REG_NOTE_KIND (note) == REG_EQUAL)
+                 {
+                   rtx newval = cse_process_note (XEXP (note, 0));
+                   if (newval != XEXP (note, 0))
+                     {
+                       XEXP (note, 0) = newval;
+                       changed = true;
+                     }
+                 }
+             if (changed)
+               df_notes_rescan (insn);
 
              cse_insn (insn);
 
@@ -6465,8 +6590,7 @@ cse_extended_basic_block (struct cse_basic_block_data *ebb_data)
                  && check_for_label_ref (insn))
                recorded_label_ref = true;
 
-#ifdef HAVE_cc0
-             if (NONDEBUG_INSN_P (insn))
+             if (HAVE_cc0 && NONDEBUG_INSN_P (insn))
                {
                  /* If the previous insn sets CC0 and this insn no
                     longer references CC0, delete the previous insn.
@@ -6493,7 +6617,6 @@ cse_extended_basic_block (struct cse_basic_block_data *ebb_data)
                      prev_insn_cc0_mode = this_insn_cc0_mode;
                    }
                }
-#endif
            }
        }
 
@@ -6533,6 +6656,7 @@ cse_extended_basic_block (struct cse_basic_block_data *ebb_data)
         equivalences due to the condition being tested.  */
       insn = BB_END (bb);
       if (path_entry < path_size - 1
+         && EDGE_COUNT (bb->succs) == 2
          && JUMP_P (insn)
          && single_set (insn)
          && any_condjump_p (insn))
@@ -6542,11 +6666,9 @@ cse_extended_basic_block (struct cse_basic_block_data *ebb_data)
          record_jump_equiv (insn, taken);
        }
 
-#ifdef HAVE_cc0
       /* Clear the CC0-tracking related insns, they can't provide
         useful information across basic block boundaries.  */
       prev_insn_cc0 = 0;
-#endif
     }
 
   gcc_assert (next_qty <= max_qty);
@@ -6572,6 +6694,10 @@ cse_main (rtx_insn *f ATTRIBUTE_UNUSED, int nregs)
   int *rc_order = XNEWVEC (int, last_basic_block_for_fn (cfun));
   int i, n_blocks;
 
+  /* CSE doesn't use dominane info but can invalidate it in different ways.
+     For simplicity free dominance info here.  */
+  free_dominance_info (CDI_DOMINATORS);
+
   df_set_flags (DF_LR_RUN_DCE);
   df_note_add_problem ();
   df_analyze ();
@@ -6581,7 +6707,7 @@ cse_main (rtx_insn *f ATTRIBUTE_UNUSED, int nregs)
   init_cse_reg_info (nregs);
 
   ebb_data.path = XNEWVEC (struct branch_path,
-                          PARAM_VALUE (PARAM_MAX_CSE_PATH_LENGTH));
+                          param_max_cse_path_length);
 
   cse_cfg_altered = false;
   cse_jumps_altered = false;
@@ -6797,21 +6923,17 @@ static bool
 set_live_p (rtx set, rtx_insn *insn ATTRIBUTE_UNUSED, /* Only used with HAVE_cc0.  */
            int *counts)
 {
-#ifdef HAVE_cc0
-  rtx tem;
-#endif
+  rtx_insn *tem;
 
   if (set_noop_p (set))
     ;
 
-#ifdef HAVE_cc0
   else if (GET_CODE (SET_DEST (set)) == CC0
           && !side_effects_p (SET_SRC (set))
           && ((tem = next_nonnote_nondebug_insn (insn)) == NULL_RTX
               || !INSN_P (tem)
               || !reg_referenced_p (cc0_rtx, PATTERN (tem))))
     return false;
-#endif
   else if (!is_dead_reg (SET_DEST (set), counts)
           || side_effects_p (SET_SRC (set)))
     return true;
@@ -6848,11 +6970,18 @@ insn_live_p (rtx_insn *insn, int *counts)
     {
       rtx_insn *next;
 
+      if (DEBUG_MARKER_INSN_P (insn))
+       return true;
+
       for (next = NEXT_INSN (insn); next; next = NEXT_INSN (next))
        if (NOTE_P (next))
          continue;
        else if (!DEBUG_INSN_P (next))
          return true;
+       /* If we find an inspection point, such as a debug begin stmt,
+          we want to keep the earlier debug insn.  */
+       else if (DEBUG_MARKER_INSN_P (next))
+         return true;
        else if (INSN_VAR_LOCATION_DECL (insn) == INSN_VAR_LOCATION_DECL (next))
          return false;
 
@@ -6935,17 +7064,17 @@ delete_trivially_dead_insns (rtx_insn *insns, int nreg)
 
   timevar_push (TV_DELETE_TRIVIALLY_DEAD);
   /* First count the number of times each register is used.  */
-  if (MAY_HAVE_DEBUG_INSNS)
+  if (MAY_HAVE_DEBUG_BIND_INSNS)
     {
       counts = XCNEWVEC (int, nreg * 3);
       for (insn = insns; insn; insn = NEXT_INSN (insn))
-       if (DEBUG_INSN_P (insn))
+       if (DEBUG_BIND_INSN_P (insn))
          count_reg_usage (INSN_VAR_LOCATION_LOC (insn), counts + nreg,
                           NULL_RTX, 1);
        else if (INSN_P (insn))
          {
            count_reg_usage (insn, counts, NULL_RTX, 1);
-           note_stores (PATTERN (insn), count_stores, counts + nreg * 2);
+           note_stores (insn, count_stores, counts + nreg * 2);
          }
       /* If there can be debug insns, COUNTS are 3 consecutive arrays.
         First one counts how many times each pseudo is used outside
@@ -6997,12 +7126,15 @@ delete_trivially_dead_insns (rtx_insn *insns, int nreg)
       if (! live_insn && dbg_cnt (delete_trivial_dead))
        {
          if (DEBUG_INSN_P (insn))
-           count_reg_usage (INSN_VAR_LOCATION_LOC (insn), counts + nreg,
-                            NULL_RTX, -1);
+           {
+             if (DEBUG_BIND_INSN_P (insn))
+               count_reg_usage (INSN_VAR_LOCATION_LOC (insn), counts + nreg,
+                                NULL_RTX, -1);
+           }
          else
            {
              rtx set;
-             if (MAY_HAVE_DEBUG_INSNS
+             if (MAY_HAVE_DEBUG_BIND_INSNS
                  && (set = single_set (insn)) != NULL_RTX
                  && is_dead_reg (SET_DEST (set), counts)
                  /* Used at least once in some DEBUG_INSN.  */
@@ -7038,14 +7170,14 @@ delete_trivially_dead_insns (rtx_insn *insns, int nreg)
              count_reg_usage (insn, counts, NULL_RTX, -1);
              ndead++;
            }
-         delete_insn_and_edges (insn);
+         cse_cfg_altered |= delete_insn_and_edges (insn);
        }
     }
 
-  if (MAY_HAVE_DEBUG_INSNS)
+  if (MAY_HAVE_DEBUG_BIND_INSNS)
     {
       for (insn = get_last_insn (); insn; insn = PREV_INSN (insn))
-       if (DEBUG_INSN_P (insn))
+       if (DEBUG_BIND_INSN_P (insn))
          {
            /* If this debug insn references a dead register that wasn't replaced
               with an DEBUG_EXPR, reset the DEBUG_INSN.  */
@@ -7082,7 +7214,7 @@ delete_trivially_dead_insns (rtx_insn *insns, int nreg)
 
 static void
 cse_change_cc_mode (subrtx_ptr_iterator::array_type &array,
-                   rtx *loc, rtx insn, rtx newreg)
+                   rtx *loc, rtx_insn *insn, rtx newreg)
 {
   FOR_EACH_SUBRTX_PTR (iter, array, loc, NONCONST)
     {
@@ -7334,7 +7466,7 @@ cse_cc_succs (basic_block bb, basic_block orig_bb, rtx cc_reg, rtx cc_src,
                                    newreg);
        }
 
-      delete_insn_and_edges (insns[i]);
+      cse_cfg_altered |= delete_insn_and_edges (insns[i]);
     }
 
   return mode;
@@ -7469,11 +7601,11 @@ rest_of_handle_cse (void)
     {
       timevar_push (TV_JUMP);
       rebuild_jump_labels (get_insns ());
-      cleanup_cfg (CLEANUP_CFG_CHANGED);
+      cse_cfg_altered |= cleanup_cfg (CLEANUP_CFG_CHANGED);
       timevar_pop (TV_JUMP);
     }
   else if (tem == 1 || optimize > 1)
-    cleanup_cfg (0);
+    cse_cfg_altered |= cleanup_cfg (0);
 
   return 0;
 }
@@ -7538,11 +7670,11 @@ rest_of_handle_cse2 (void)
     {
       timevar_push (TV_JUMP);
       rebuild_jump_labels (get_insns ());
-      cleanup_cfg (CLEANUP_CFG_CHANGED);
+      cse_cfg_altered |= cleanup_cfg (CLEANUP_CFG_CHANGED);
       timevar_pop (TV_JUMP);
     }
-  else if (tem == 1)
-    cleanup_cfg (0);
+  else if (tem == 1 || cse_cfg_altered)
+    cse_cfg_altered |= cleanup_cfg (0);
 
   cse_not_expected = 1;
   return 0;
@@ -7602,7 +7734,7 @@ rest_of_handle_cse_after_global_opts (void)
 
   rebuild_jump_labels (get_insns ());
   tem = cse_main (get_insns (), max_reg_num ());
-  purge_all_dead_edges ();
+  cse_cfg_altered |= purge_all_dead_edges ();
   delete_trivially_dead_insns (get_insns (), max_reg_num ());
 
   cse_not_expected = !flag_rerun_cse_after_loop;
@@ -7612,11 +7744,11 @@ rest_of_handle_cse_after_global_opts (void)
     {
       timevar_push (TV_JUMP);
       rebuild_jump_labels (get_insns ());
-      cleanup_cfg (CLEANUP_CFG_CHANGED);
+      cse_cfg_altered |= cleanup_cfg (CLEANUP_CFG_CHANGED);
       timevar_pop (TV_JUMP);
     }
-  else if (tem == 1)
-    cleanup_cfg (0);
+  else if (tem == 1 || cse_cfg_altered)
+    cse_cfg_altered |= cleanup_cfg (0);
 
   flag_cse_follow_jumps = save_cfj;
   return 0;