Daily bump.
[gcc.git] / gcc / cselib.c
index 4dfc55778b9236fe62581ef39b76264c30645cf4..3e0c69d67b81c1f95d0d0026a292ba97898d96bd 100644 (file)
@@ -1,5 +1,5 @@
 /* Common subexpression elimination library for GNU compiler.
-   Copyright (C) 1987-2014 Free Software Foundation, Inc.
+   Copyright (C) 1987-2020 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -20,38 +20,26 @@ 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"/* FIXME: For hashing DEBUG_EXPR & friends.  */
+#include "tree.h"
+#include "df.h"
+#include "memmodel.h"
 #include "tm_p.h"
 #include "regs.h"
-#include "hard-reg-set.h"
-#include "flags.h"
-#include "insn-config.h"
-#include "recog.h"
-#include "function.h"
 #include "emit-rtl.h"
-#include "diagnostic-core.h"
-#include "ggc.h"
-#include "hash-table.h"
 #include "dumpfile.h"
 #include "cselib.h"
-#include "valtrack.h"
-#include "params.h"
-#include "alloc-pool.h"
-#include "target.h"
-#include "bitmap.h"
+#include "function-abi.h"
 
 /* A list of cselib_val structures.  */
-struct elt_list {
-    struct elt_list *next;
-    cselib_val *elt;
+struct elt_list
+{
+  struct elt_list *next;
+  cselib_val *elt;
 };
 
-/* See the documentation of cselib_find_slot below.  */
-static enum machine_mode find_slot_memmode;
-
 static bool cselib_record_memory;
 static bool cselib_preserve_constants;
 static bool cselib_any_perm_equivs;
@@ -62,15 +50,14 @@ static void unchain_one_value (cselib_val *);
 static void unchain_one_elt_list (struct elt_list **);
 static void unchain_one_elt_loc_list (struct elt_loc_list **);
 static void remove_useless_values (void);
-static int rtx_equal_for_cselib_1 (rtx, rtx, enum machine_mode);
-static unsigned int cselib_hash_rtx (rtx, int, enum machine_mode);
-static cselib_val *new_cselib_val (unsigned int, enum machine_mode, rtx);
+static unsigned int cselib_hash_rtx (rtx, int, machine_mode);
+static cselib_val *new_cselib_val (unsigned int, machine_mode, rtx);
 static void add_mem_for_addr (cselib_val *, cselib_val *, rtx);
 static cselib_val *cselib_lookup_mem (rtx, int);
-static void cselib_invalidate_regno (unsigned int, enum machine_mode);
+static void cselib_invalidate_regno (unsigned int, machine_mode);
 static void cselib_invalidate_mem (rtx);
 static void cselib_record_set (rtx, cselib_val *, cselib_val *);
-static void cselib_record_sets (rtx);
+static void cselib_record_sets (rtx_insn *);
 
 struct expand_value_data
 {
@@ -91,12 +78,19 @@ static rtx cselib_expand_value_rtx_1 (rtx, struct expand_value_data *, int);
      this involves walking the table entries for a given value and comparing
      the locations of the entries with the rtx we are looking up.  */
 
-struct cselib_hasher : typed_noop_remove <cselib_val>
+struct cselib_hasher : nofree_ptr_hash <cselib_val>
 {
-  typedef cselib_val value_type;
-  typedef rtx_def compare_type;
-  static inline hashval_t hash (const value_type *);
-  static inline bool equal (const value_type *, const compare_type *);
+  struct key {
+    /* The rtx value and its mode (needed separately for constant
+       integers).  */
+    machine_mode mode;
+    rtx x;
+    /* The mode of the contaning MEM, if any, otherwise VOIDmode.  */
+    machine_mode memmode;
+  };
+  typedef key *compare_type;
+  static inline hashval_t hash (const cselib_val *);
+  static inline bool equal (const cselib_val *, const key *);
 };
 
 /* The hash function for our hash table.  The value is always computed with
@@ -104,7 +98,7 @@ struct cselib_hasher : typed_noop_remove <cselib_val>
    hash value from a cselib_val structure.  */
 
 inline hashval_t
-cselib_hasher::hash (const value_type *v)
+cselib_hasher::hash (const cselib_val *v)
 {
   return v->hash;
 }
@@ -115,30 +109,23 @@ cselib_hasher::hash (const value_type *v)
    CONST of an appropriate mode.  */
 
 inline bool
-cselib_hasher::equal (const value_type *v, const compare_type *x_arg)
+cselib_hasher::equal (const cselib_val *v, const key *x_arg)
 {
   struct elt_loc_list *l;
-  rtx x = CONST_CAST_RTX (x_arg);
-  enum machine_mode mode = GET_MODE (x);
-
-  gcc_assert (!CONST_SCALAR_INT_P (x) && GET_CODE (x) != CONST_FIXED);
+  rtx x = x_arg->x;
+  machine_mode mode = x_arg->mode;
+  machine_mode memmode = x_arg->memmode;
 
   if (mode != GET_MODE (v->val_rtx))
     return false;
 
-  /* Unwrap X if necessary.  */
-  if (GET_CODE (x) == CONST
-      && (CONST_SCALAR_INT_P (XEXP (x, 0))
-         || GET_CODE (XEXP (x, 0)) == CONST_FIXED))
-    x = XEXP (x, 0);
-
   if (GET_CODE (x) == VALUE)
     return x == v->val_rtx;
 
   /* We don't guarantee that distinct rtx's have different hash values,
      so we need to do a comparison.  */
   for (l = v->locs; l; l = l->next)
-    if (rtx_equal_for_cselib_1 (l->loc, x, find_slot_memmode))
+    if (rtx_equal_for_cselib_1 (l->loc, x, memmode, 0))
       {
        promote_debug_loc (l);
        return true;
@@ -148,14 +135,14 @@ cselib_hasher::equal (const value_type *v, const compare_type *x_arg)
 }
 
 /* A table that enables us to look up elts by their value.  */
-static hash_table <cselib_hasher> cselib_hash_table;
+static hash_table<cselib_hasher> *cselib_hash_table;
 
 /* A table to hold preserved values.  */
-static hash_table <cselib_hasher> cselib_preserved_hash_table;
+static hash_table<cselib_hasher> *cselib_preserved_hash_table;
 
 /* This is a global so we don't have to pass this through every function.
    It is used in new_elt_loc_list to set SETTING_INSN.  */
-static rtx cselib_current_insn;
+static rtx_insn *cselib_current_insn;
 
 /* The unique id that the next create value will take.  */
 static unsigned int next_uid;
@@ -250,7 +237,12 @@ static unsigned int cfa_base_preserved_regno = INVALID_REGNUM;
    May or may not contain the useless values - the list is compacted
    each time memory is invalidated.  */
 static cselib_val *first_containing_mem = &dummy_val;
-static alloc_pool elt_loc_list_pool, elt_list_pool, cselib_val_pool, value_pool;
+
+static object_allocator<elt_list> elt_list_pool ("elt_list");
+static object_allocator<elt_loc_list> elt_loc_list_pool ("elt_loc_list");
+static object_allocator<cselib_val> cselib_val_pool ("cselib_val_list");
+
+static pool_allocator value_pool ("value", RTX_CODE_SIZE (VALUE));
 
 /* If nonnull, cselib will call this function before freeing useless
    VALUEs.  A VALUE is deemed useless if its "locs" field is null.  */
@@ -261,7 +253,7 @@ void (*cselib_discard_hook) (cselib_val *);
    represented in the array sets[n_sets].  new_val_min can be used to
    tell whether values present in sets are introduced by this
    instruction.  */
-void (*cselib_record_sets_hook) (rtx insn, struct cselib_set *sets,
+void (*cselib_record_sets_hook) (rtx_insn *insn, struct cselib_set *sets,
                                 int n_sets);
 
 #define PRESERVED_VALUE_P(RTX) \
@@ -278,8 +270,7 @@ void (*cselib_record_sets_hook) (rtx insn, struct cselib_set *sets,
 static inline struct elt_list *
 new_elt_list (struct elt_list *next, cselib_val *elt)
 {
-  struct elt_list *el;
-  el = (struct elt_list *) pool_alloc (elt_list_pool);
+  elt_list *el = elt_list_pool.allocate ();
   el->next = next;
   el->elt = elt;
   return el;
@@ -363,14 +354,14 @@ new_elt_loc_list (cselib_val *val, rtx loc)
        }
 
       /* Chain LOC back to VAL.  */
-      el = (struct elt_loc_list *) pool_alloc (elt_loc_list_pool);
+      el = elt_loc_list_pool.allocate ();
       el->loc = val->val_rtx;
       el->setting_insn = cselib_current_insn;
       el->next = NULL;
       CSELIB_VAL_PTR (loc)->locs = el;
     }
 
-  el = (struct elt_loc_list *) pool_alloc (elt_loc_list_pool);
+  el = elt_loc_list_pool.allocate ();
   el->loc = loc;
   el->setting_insn = cselib_current_insn;
   el->next = next;
@@ -410,7 +401,7 @@ unchain_one_elt_list (struct elt_list **pl)
   struct elt_list *l = *pl;
 
   *pl = l->next;
-  pool_free (elt_list_pool, l);
+  elt_list_pool.remove (l);
 }
 
 /* Likewise for elt_loc_lists.  */
@@ -421,7 +412,7 @@ unchain_one_elt_loc_list (struct elt_loc_list **pl)
   struct elt_loc_list *l = *pl;
 
   *pl = l->next;
-  pool_free (elt_loc_list_pool, l);
+  elt_loc_list_pool.remove (l);
 }
 
 /* Likewise for cselib_vals.  This also frees the addr_list associated with
@@ -433,7 +424,7 @@ unchain_one_value (cselib_val *v)
   while (v->addr_list)
     unchain_one_elt_list (&v->addr_list);
 
-  pool_free (cselib_val_pool, v);
+  cselib_val_pool.remove (v);
 }
 
 /* Remove all entries from the hash table.  Also used during
@@ -498,14 +489,17 @@ preserve_constants_and_equivs (cselib_val **x, void *info ATTRIBUTE_UNUSED)
 
   if (invariant_or_equiv_p (v))
     {
+      cselib_hasher::key lookup = {
+       GET_MODE (v->val_rtx), v->val_rtx, VOIDmode
+      };
       cselib_val **slot
-       = cselib_preserved_hash_table.find_slot_with_hash (v->val_rtx,
+       = cselib_preserved_hash_table->find_slot_with_hash (&lookup,
                                                           v->hash, INSERT);
       gcc_assert (!*slot);
       *slot = v;
     }
 
-  cselib_hash_table.clear_slot (x);
+  cselib_hash_table->clear_slot (x);
 
   return 1;
 }
@@ -536,7 +530,8 @@ cselib_reset_table (unsigned int num)
       n_used_regs = new_used_regs;
       used_regs[0] = regno;
       max_value_regs
-       = hard_regno_nregs[regno][GET_MODE (cfa_base_preserved_val->locs->loc)];
+       = hard_regno_nregs (regno,
+                           GET_MODE (cfa_base_preserved_val->locs->loc));
     }
   else
     {
@@ -546,10 +541,11 @@ cselib_reset_table (unsigned int num)
     }
 
   if (cselib_preserve_constants)
-    cselib_hash_table.traverse <void *, preserve_constants_and_equivs> (NULL);
+    cselib_hash_table->traverse <void *, preserve_constants_and_equivs>
+      (NULL);
   else
     {
-      cselib_hash_table.empty ();
+      cselib_hash_table->empty ();
       gcc_checking_assert (!cselib_any_perm_equivs);
     }
 
@@ -572,22 +568,19 @@ cselib_get_next_uid (void)
 
 /* Search for X, whose hashcode is HASH, in CSELIB_HASH_TABLE,
    INSERTing if requested.  When X is part of the address of a MEM,
-   MEMMODE should specify the mode of the MEM.  While searching the
-   table, MEMMODE is held in FIND_SLOT_MEMMODE, so that autoinc RTXs
-   in X can be resolved.  */
+   MEMMODE should specify the mode of the MEM.  */
 
 static cselib_val **
-cselib_find_slot (rtx x, hashval_t hash, enum insert_option insert,
-                 enum machine_mode memmode)
+cselib_find_slot (machine_mode mode, rtx x, hashval_t hash,
+                 enum insert_option insert, machine_mode memmode)
 {
   cselib_val **slot = NULL;
-  find_slot_memmode = memmode;
+  cselib_hasher::key lookup = { mode, x, memmode };
   if (cselib_preserve_constants)
-    slot = cselib_preserved_hash_table.find_slot_with_hash (x, hash,
-                                                           NO_INSERT);
+    slot = cselib_preserved_hash_table->find_slot_with_hash (&lookup, hash,
+                                                            NO_INSERT);
   if (!slot)
-    slot = cselib_hash_table.find_slot_with_hash (x, hash, insert);
-  find_slot_memmode = VOIDmode;
+    slot = cselib_hash_table->find_slot_with_hash (&lookup, hash, insert);
   return slot;
 }
 
@@ -631,7 +624,7 @@ discard_useless_locs (cselib_val **x, void *info ATTRIBUTE_UNUSED)
   cselib_val *v = *x;
   struct elt_loc_list **p = &v->locs;
   bool had_locs = v->locs != NULL;
-  rtx setting_insn = v->locs ? v->locs->setting_insn : NULL;
+  rtx_insn *setting_insn = v->locs ? v->locs->setting_insn : NULL;
 
   while (*p)
     {
@@ -665,7 +658,7 @@ discard_useless_values (cselib_val **x, void *info ATTRIBUTE_UNUSED)
        cselib_discard_hook (v);
 
       CSELIB_VAL_PTR (v->val_rtx) = NULL;
-      cselib_hash_table.clear_slot (x);
+      cselib_hash_table->clear_slot (x);
       unchain_one_value (v);
       n_useless_values--;
     }
@@ -686,7 +679,7 @@ remove_useless_values (void)
   do
     {
       values_became_useless = 0;
-      cselib_hash_table.traverse <void *, discard_useless_locs> (NULL);
+      cselib_hash_table->traverse <void *, discard_useless_locs> (NULL);
     }
   while (values_became_useless);
 
@@ -705,7 +698,7 @@ remove_useless_values (void)
   n_debug_values -= n_useless_debug_values;
   n_useless_debug_values = 0;
 
-  cselib_hash_table.traverse <void *, discard_useless_values> (NULL);
+  cselib_hash_table->traverse <void *, discard_useless_values> (NULL);
 
   gcc_assert (!n_useless_values);
 }
@@ -783,7 +776,7 @@ cselib_sp_based_value_p (cselib_val *v)
    set is not known, or the value was already clobbered, return
    VOIDmode.  */
 
-enum machine_mode
+machine_mode
 cselib_reg_set_mode (const_rtx x)
 {
   if (!REG_P (x))
@@ -796,20 +789,11 @@ cselib_reg_set_mode (const_rtx x)
   return GET_MODE (REG_VALUES (REGNO (x))->elt->val_rtx);
 }
 
-/* Return nonzero if we can prove that X and Y contain the same value, taking
-   our gathered information into account.  */
-
-int
-rtx_equal_for_cselib_p (rtx x, rtx y)
-{
-  return rtx_equal_for_cselib_1 (x, y, VOIDmode);
-}
-
 /* If x is a PLUS or an autoinc operation, expand the operation,
    storing the offset, if any, in *OFF.  */
 
 static rtx
-autoinc_split (rtx x, rtx *off, enum machine_mode memmode)
+autoinc_split (rtx x, rtx *off, machine_mode memmode)
 {
   switch (GET_CODE (x))
     {
@@ -821,15 +805,14 @@ autoinc_split (rtx x, rtx *off, enum machine_mode memmode)
       if (memmode == VOIDmode)
        return x;
 
-      *off = GEN_INT (-GET_MODE_SIZE (memmode));
+      *off = gen_int_mode (-GET_MODE_SIZE (memmode), GET_MODE (x));
       return XEXP (x, 0);
-      break;
 
     case PRE_INC:
       if (memmode == VOIDmode)
        return x;
 
-      *off = GEN_INT (GET_MODE_SIZE (memmode));
+      *off = gen_int_mode (GET_MODE_SIZE (memmode), GET_MODE (x));
       return XEXP (x, 0);
 
     case PRE_MODIFY:
@@ -851,8 +834,8 @@ autoinc_split (rtx x, rtx *off, enum machine_mode memmode)
    addressing modes.  If X and Y are not (known to be) part of
    addresses, MEMMODE should be VOIDmode.  */
 
-static int
-rtx_equal_for_cselib_1 (rtx x, rtx y, enum machine_mode memmode)
+int
+rtx_equal_for_cselib_1 (rtx x, rtx y, machine_mode memmode, int depth)
 {
   enum rtx_code code;
   const char *fmt;
@@ -885,6 +868,9 @@ rtx_equal_for_cselib_1 (rtx x, rtx y, enum machine_mode memmode)
       if (GET_CODE (y) == VALUE)
        return e == canonical_cselib_val (CSELIB_VAL_PTR (y));
 
+      if (depth == 128)
+       return 0;
+
       for (l = e->locs; l; l = l->next)
        {
          rtx t = l->loc;
@@ -894,7 +880,7 @@ rtx_equal_for_cselib_1 (rtx x, rtx y, enum machine_mode memmode)
             list.  */
          if (REG_P (t) || MEM_P (t) || GET_CODE (t) == VALUE)
            continue;
-         else if (rtx_equal_for_cselib_1 (t, y, memmode))
+         else if (rtx_equal_for_cselib_1 (t, y, memmode, depth + 1))
            return 1;
        }
 
@@ -905,13 +891,16 @@ rtx_equal_for_cselib_1 (rtx x, rtx y, enum machine_mode memmode)
       cselib_val *e = canonical_cselib_val (CSELIB_VAL_PTR (y));
       struct elt_loc_list *l;
 
+      if (depth == 128)
+       return 0;
+
       for (l = e->locs; l; l = l->next)
        {
          rtx t = l->loc;
 
          if (REG_P (t) || MEM_P (t) || GET_CODE (t) == VALUE)
            continue;
-         else if (rtx_equal_for_cselib_1 (x, t, memmode))
+         else if (rtx_equal_for_cselib_1 (x, t, memmode, depth + 1))
            return 1;
        }
 
@@ -932,12 +921,12 @@ rtx_equal_for_cselib_1 (rtx x, rtx y, enum machine_mode memmode)
       if (!xoff != !yoff)
        return 0;
 
-      if (xoff && !rtx_equal_for_cselib_1 (xoff, yoff, memmode))
+      if (xoff && !rtx_equal_for_cselib_1 (xoff, yoff, memmode, depth))
        return 0;
 
       /* Don't recurse if nothing changed.  */
       if (x != xorig || y != yorig)
-       return rtx_equal_for_cselib_1 (x, y, memmode);
+       return rtx_equal_for_cselib_1 (x, y, memmode, depth);
 
       return 0;
     }
@@ -945,8 +934,7 @@ rtx_equal_for_cselib_1 (rtx x, rtx y, enum machine_mode memmode)
   /* These won't be handled correctly by the code below.  */
   switch (GET_CODE (x))
     {
-    case CONST_DOUBLE:
-    case CONST_FIXED:
+    CASE_CONST_UNIQUE:
     case DEBUG_EXPR:
       return 0;
 
@@ -964,12 +952,16 @@ rtx_equal_for_cselib_1 (rtx x, rtx y, enum machine_mode memmode)
       return rtx_equal_p (ENTRY_VALUE_EXP (x), ENTRY_VALUE_EXP (y));
 
     case LABEL_REF:
-      return XEXP (x, 0) == XEXP (y, 0);
+      return label_ref_label (x) == label_ref_label (y);
+
+    case REG:
+      return REGNO (x) == REGNO (y);
 
     case MEM:
       /* We have to compare any autoinc operations in the addresses
         using this MEM's mode.  */
-      return rtx_equal_for_cselib_1 (XEXP (x, 0), XEXP (y, 0), GET_MODE (x));
+      return rtx_equal_for_cselib_1 (XEXP (x, 0), XEXP (y, 0), GET_MODE (x),
+                                    depth);
 
     default:
       break;
@@ -995,6 +987,11 @@ rtx_equal_for_cselib_1 (rtx x, rtx y, enum machine_mode memmode)
            return 0;
          break;
 
+       case 'p':
+         if (maybe_ne (SUBREG_BYTE (x), SUBREG_BYTE (y)))
+           return 0;
+         break;
+
        case 'V':
        case 'E':
          /* Two vectors must have the same length.  */
@@ -1004,17 +1001,20 @@ rtx_equal_for_cselib_1 (rtx x, rtx y, enum machine_mode memmode)
          /* And the corresponding elements must match.  */
          for (j = 0; j < XVECLEN (x, i); j++)
            if (! rtx_equal_for_cselib_1 (XVECEXP (x, i, j),
-                                         XVECEXP (y, i, j), memmode))
+                                         XVECEXP (y, i, j), memmode, depth))
              return 0;
          break;
 
        case 'e':
          if (i == 1
              && targetm.commutative_p (x, UNKNOWN)
-             && rtx_equal_for_cselib_1 (XEXP (x, 1), XEXP (y, 0), memmode)
-             && rtx_equal_for_cselib_1 (XEXP (x, 0), XEXP (y, 1), memmode))
+             && rtx_equal_for_cselib_1 (XEXP (x, 1), XEXP (y, 0), memmode,
+                                        depth)
+             && rtx_equal_for_cselib_1 (XEXP (x, 0), XEXP (y, 1), memmode,
+                                        depth))
            return 1;
-         if (! rtx_equal_for_cselib_1 (XEXP (x, i), XEXP (y, i), memmode))
+         if (! rtx_equal_for_cselib_1 (XEXP (x, i), XEXP (y, i), memmode,
+                                       depth))
            return 0;
          break;
 
@@ -1042,18 +1042,6 @@ rtx_equal_for_cselib_1 (rtx x, rtx y, enum machine_mode memmode)
   return 1;
 }
 
-/* We need to pass down the mode of constants through the hash table
-   functions.  For that purpose, wrap them in a CONST of the appropriate
-   mode.  */
-static rtx
-wrap_constant (enum machine_mode mode, rtx x)
-{
-  if (!CONST_SCALAR_INT_P (x) && GET_CODE (x) != CONST_FIXED)
-    return x;
-  gcc_assert (mode != VOIDmode);
-  return gen_rtx_CONST (mode, x);
-}
-
 /* Hash an rtx.  Return 0 if we couldn't hash the rtx.
    For registers and memory locations, we look up their cselib_val structure
    and return its VALUE element.
@@ -1077,9 +1065,10 @@ wrap_constant (enum machine_mode mode, rtx x)
    in a comparison anyway, since relying on hash differences is unsafe.  */
 
 static unsigned int
-cselib_hash_rtx (rtx x, int create, enum machine_mode memmode)
+cselib_hash_rtx (rtx x, int create, machine_mode memmode)
 {
   cselib_val *e;
+  poly_int64 offset;
   int i, j;
   enum rtx_code code;
   const char *fmt;
@@ -1137,18 +1126,32 @@ cselib_hash_rtx (rtx x, int create, enum machine_mode memmode)
       return hash ? hash : (unsigned int) ENTRY_VALUE;
 
     case CONST_INT:
-      hash += ((unsigned) CONST_INT << 7) + INTVAL (x);
+      hash += ((unsigned) CONST_INT << 7) + UINTVAL (x);
       return hash ? hash : (unsigned int) CONST_INT;
 
+    case CONST_WIDE_INT:
+      for (i = 0; i < CONST_WIDE_INT_NUNITS (x); i++)
+       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.  */
       hash += (unsigned) code + (unsigned) GET_MODE (x);
-      if (GET_MODE (x) != VOIDmode)
-       hash += real_hash (CONST_DOUBLE_REAL_VALUE (x));
-      else
+      if (TARGET_SUPPORTS_WIDE_INT == 0 && GET_MODE (x) == VOIDmode)
        hash += ((unsigned) CONST_DOUBLE_LOW (x)
                 + (unsigned) CONST_DOUBLE_HIGH (x));
+      else
+       hash += real_hash (CONST_DOUBLE_REAL_VALUE (x));
       return hash ? hash : (unsigned int) CONST_DOUBLE;
 
     case CONST_FIXED:
@@ -1161,11 +1164,11 @@ cselib_hash_rtx (rtx x, int create, enum machine_mode memmode)
        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 += cselib_hash_rtx (elt, 0, memmode);
          }
 
@@ -1177,7 +1180,7 @@ cselib_hash_rtx (rtx x, int create, enum machine_mode memmode)
       /* 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 (XEXP (x, 0)));
+              + CODE_LABEL_NUMBER (label_ref_label (x)));
       return hash ? hash : (unsigned int) LABEL_REF;
 
     case SYMBOL_REF:
@@ -1201,14 +1204,15 @@ cselib_hash_rtx (rtx x, int create, enum machine_mode memmode)
     case PRE_INC:
       /* We can't compute these without knowing the MEM mode.  */
       gcc_assert (memmode != VOIDmode);
-      i = GET_MODE_SIZE (memmode);
+      offset = GET_MODE_SIZE (memmode);
       if (code == PRE_DEC)
-       i = -i;
+       offset = -offset;
       /* Adjust the hash so that (mem:MEMMODE (pre_* (reg))) hashes
         like (mem:MEMMODE (plus (reg) (const_int I))).  */
       hash += (unsigned) PLUS - (unsigned)code
        + cselib_hash_rtx (XEXP (x, 0), create, memmode)
-       + cselib_hash_rtx (GEN_INT (i), create, memmode);
+       + cselib_hash_rtx (gen_int_mode (offset, GET_MODE (x)),
+                          create, memmode);
       return hash ? hash : 1 + (unsigned) PLUS;
 
     case PRE_MODIFY:
@@ -1281,6 +1285,10 @@ cselib_hash_rtx (rtx x, int create, enum machine_mode memmode)
          hash += XINT (x, i);
          break;
 
+       case 'p':
+         hash += constant_lower_bound (SUBREG_BYTE (x));
+         break;
+
        case '0':
        case 't':
          /* unused */
@@ -1298,9 +1306,9 @@ cselib_hash_rtx (rtx x, int create, enum machine_mode memmode)
    value is MODE.  */
 
 static inline cselib_val *
-new_cselib_val (unsigned int hash, enum machine_mode mode, rtx x)
+new_cselib_val (unsigned int hash, machine_mode mode, rtx x)
 {
-  cselib_val *e = (cselib_val *) pool_alloc (cselib_val_pool);
+  cselib_val *e = cselib_val_pool.allocate ();
 
   gcc_assert (hash);
   gcc_assert (next_uid);
@@ -1312,7 +1320,7 @@ new_cselib_val (unsigned int hash, enum machine_mode mode, rtx x)
      precisely when we can have VALUE RTXen (when cselib is active)
      so we don't need to put them in garbage collected memory.
      ??? Why should a VALUE be an RTX in the first place?  */
-  e->val_rtx = (rtx) pool_alloc (value_pool);
+  e->val_rtx = (rtx_def*) value_pool.allocate ();
   memset (e->val_rtx, 0, RTX_HDR_SIZE);
   PUT_CODE (e->val_rtx, VALUE);
   PUT_MODE (e->val_rtx, mode);
@@ -1342,15 +1350,15 @@ new_cselib_val (unsigned int hash, enum machine_mode mode, rtx x)
 static void
 add_mem_for_addr (cselib_val *addr_elt, cselib_val *mem_elt, rtx x)
 {
-  struct elt_loc_list *l;
-
   addr_elt = canonical_cselib_val (addr_elt);
   mem_elt = canonical_cselib_val (mem_elt);
 
   /* Avoid duplicates.  */
-  for (l = mem_elt->locs; l; l = l->next)
+  addr_space_t as = MEM_ADDR_SPACE (x);
+  for (elt_loc_list *l = mem_elt->locs; l; l = l->next)
     if (MEM_P (l->loc)
-       && CSELIB_VAL_PTR (XEXP (l->loc, 0)) == addr_elt)
+       && CSELIB_VAL_PTR (XEXP (l->loc, 0)) == addr_elt
+        && MEM_ADDR_SPACE (l->loc) == as)
       {
        promote_debug_loc (l);
        return;
@@ -1372,12 +1380,11 @@ add_mem_for_addr (cselib_val *addr_elt, cselib_val *mem_elt, rtx x)
 static cselib_val *
 cselib_lookup_mem (rtx x, int create)
 {
-  enum machine_mode mode = GET_MODE (x);
-  enum machine_mode addr_mode;
+  machine_mode mode = GET_MODE (x);
+  machine_mode addr_mode;
   cselib_val **slot;
   cselib_val *addr;
   cselib_val *mem_elt;
-  struct elt_list *l;
 
   if (MEM_VOLATILE_P (x) || mode == BLKmode
       || !cselib_record_memory
@@ -1392,14 +1399,19 @@ cselib_lookup_mem (rtx x, int create)
   addr = cselib_lookup (XEXP (x, 0), addr_mode, create, mode);
   if (! addr)
     return 0;
-
   addr = canonical_cselib_val (addr);
+
   /* Find a value that describes a value of our mode at that address.  */
-  for (l = addr->addr_list; l; l = l->next)
+  addr_space_t as = MEM_ADDR_SPACE (x);
+  for (elt_list *l = addr->addr_list; l; l = l->next)
     if (GET_MODE (l->elt->val_rtx) == mode)
       {
-       promote_debug_loc (l->elt->locs);
-       return l->elt;
+       for (elt_loc_list *l2 = l->elt->locs; l2; l2 = l2->next)
+         if (MEM_P (l2->loc) && MEM_ADDR_SPACE (l2->loc) == as)
+           {
+             promote_debug_loc (l->elt->locs);
+             return l->elt;
+           }
       }
 
   if (! create)
@@ -1407,8 +1419,7 @@ cselib_lookup_mem (rtx x, int create)
 
   mem_elt = new_cselib_val (next_uid, mode, x);
   add_mem_for_addr (addr, mem_elt, x);
-  slot = cselib_find_slot (wrap_constant (mode, x), mem_elt->hash,
-                          INSERT, mode);
+  slot = cselib_find_slot (mode, x, mem_elt->hash, INSERT, VOIDmode);
   *slot = mem_elt;
   return mem_elt;
 }
@@ -1579,7 +1590,7 @@ cselib_expand_value_rtx_1 (rtx orig, struct expand_value_data *evd,
   int i, j;
   RTX_CODE code;
   const char *format_ptr;
-  enum machine_mode mode;
+  machine_mode mode;
 
   code = GET_CODE (orig);
 
@@ -1638,6 +1649,7 @@ cselib_expand_value_rtx_1 (rtx orig, struct expand_value_data *evd,
              else
                return orig;
            }
+       return orig;
       }
 
     CASE_CONST_ANY:
@@ -1853,7 +1865,7 @@ cselib_expand_value_rtx_1 (rtx orig, struct expand_value_data *evd,
    If X is within a MEM, MEMMODE must be the mode of the MEM.  */
 
 rtx
-cselib_subst_to_values (rtx x, enum machine_mode memmode)
+cselib_subst_to_values (rtx x, machine_mode memmode)
 {
   enum rtx_code code = GET_CODE (x);
   const char *fmt = GET_RTX_FORMAT (code);
@@ -1861,6 +1873,7 @@ cselib_subst_to_values (rtx x, enum machine_mode memmode)
   struct elt_list *l;
   rtx copy = x;
   int i;
+  poly_int64 offset;
 
   switch (code)
     {
@@ -1897,11 +1910,11 @@ cselib_subst_to_values (rtx x, enum machine_mode memmode)
     case PRE_DEC:
     case PRE_INC:
       gcc_assert (memmode != VOIDmode);
-      i = GET_MODE_SIZE (memmode);
+      offset = GET_MODE_SIZE (memmode);
       if (code == PRE_DEC)
-       i = -i;
+       offset = -offset;
       return cselib_subst_to_values (plus_constant (GET_MODE (x),
-                                                   XEXP (x, 0), i),
+                                                   XEXP (x, 0), offset),
                                     memmode);
 
     case PRE_MODIFY:
@@ -1959,7 +1972,7 @@ cselib_subst_to_values (rtx x, enum machine_mode memmode)
 /* Wrapper for cselib_subst_to_values, that indicates X is in INSN.  */
 
 rtx
-cselib_subst_to_values_from_insn (rtx x, enum machine_mode memmode, rtx insn)
+cselib_subst_to_values_from_insn (rtx x, machine_mode memmode, rtx_insn *insn)
 {
   rtx ret;
   gcc_assert (!cselib_current_insn);
@@ -1977,8 +1990,8 @@ cselib_subst_to_values_from_insn (rtx x, enum machine_mode memmode, rtx insn)
    we're tracking autoinc expressions.  */
 
 static cselib_val *
-cselib_lookup_1 (rtx x, enum machine_mode mode,
-                int create, enum machine_mode memmode)
+cselib_lookup_1 (rtx x, machine_mode mode,
+                int create, machine_mode memmode)
 {
   cselib_val **slot;
   cselib_val *e;
@@ -2010,7 +2023,7 @@ cselib_lookup_1 (rtx x, enum machine_mode mode,
 
       if (i < FIRST_PSEUDO_REGISTER)
        {
-         unsigned int n = hard_regno_nregs[i][mode];
+         unsigned int n = hard_regno_nregs (i, mode);
 
          if (n > max_value_regs)
            max_value_regs = n;
@@ -2018,6 +2031,8 @@ cselib_lookup_1 (rtx x, enum machine_mode mode,
 
       e = new_cselib_val (next_uid, GET_MODE (x), x);
       new_elt_loc_list (e, x);
+
+      scalar_int_mode int_mode;
       if (REG_VALUES (i) == 0)
        {
          /* Maintain the invariant that the first entry of
@@ -2027,27 +2042,27 @@ cselib_lookup_1 (rtx x, enum machine_mode mode,
          REG_VALUES (i) = new_elt_list (REG_VALUES (i), NULL);
        }
       else if (cselib_preserve_constants
-              && GET_MODE_CLASS (mode) == MODE_INT)
+              && is_int_mode (mode, &int_mode))
        {
          /* During var-tracking, try harder to find equivalences
             for SUBREGs.  If a setter sets say a DImode register
             and user uses that register only in SImode, add a lowpart
             subreg location.  */
          struct elt_list *lwider = NULL;
+         scalar_int_mode lmode;
          l = REG_VALUES (i);
          if (l && l->elt == NULL)
            l = l->next;
          for (; l; l = l->next)
-           if (GET_MODE_CLASS (GET_MODE (l->elt->val_rtx)) == MODE_INT
-               && GET_MODE_SIZE (GET_MODE (l->elt->val_rtx))
-                  > GET_MODE_SIZE (mode)
+           if (is_int_mode (GET_MODE (l->elt->val_rtx), &lmode)
+               && GET_MODE_SIZE (lmode) > GET_MODE_SIZE (int_mode)
                && (lwider == NULL
-                   || GET_MODE_SIZE (GET_MODE (l->elt->val_rtx))
-                      < GET_MODE_SIZE (GET_MODE (lwider->elt->val_rtx))))
+                   || partial_subreg_p (lmode,
+                                        GET_MODE (lwider->elt->val_rtx))))
              {
                struct elt_loc_list *el;
                if (i < FIRST_PSEUDO_REGISTER
-                   && hard_regno_nregs[i][GET_MODE (l->elt->val_rtx)] != 1)
+                   && hard_regno_nregs (i, lmode) != 1)
                  continue;
                for (el = l->elt->locs; el; el = el->next)
                  if (!REG_P (el->loc))
@@ -2057,14 +2072,14 @@ cselib_lookup_1 (rtx x, enum machine_mode mode,
              }
          if (lwider)
            {
-             rtx sub = lowpart_subreg (mode, lwider->elt->val_rtx,
+             rtx sub = lowpart_subreg (int_mode, lwider->elt->val_rtx,
                                        GET_MODE (lwider->elt->val_rtx));
              if (sub)
                new_elt_loc_list (e, sub);
            }
        }
       REG_VALUES (i)->next = new_elt_list (REG_VALUES (i)->next, e);
-      slot = cselib_find_slot (x, e->hash, INSERT, memmode);
+      slot = cselib_find_slot (mode, x, e->hash, INSERT, memmode);
       *slot = e;
       return e;
     }
@@ -2077,7 +2092,7 @@ cselib_lookup_1 (rtx x, enum machine_mode mode,
   if (! hashval)
     return 0;
 
-  slot = cselib_find_slot (wrap_constant (mode, x), hashval,
+  slot = cselib_find_slot (mode, x, hashval,
                           create ? INSERT : NO_INSERT, memmode);
   if (slot == 0)
     return 0;
@@ -2099,8 +2114,8 @@ cselib_lookup_1 (rtx x, enum machine_mode mode,
 /* Wrapper for cselib_lookup, that indicates X is in INSN.  */
 
 cselib_val *
-cselib_lookup_from_insn (rtx x, enum machine_mode mode,
-                        int create, enum machine_mode memmode, rtx insn)
+cselib_lookup_from_insn (rtx x, machine_mode mode,
+                        int create, machine_mode memmode, rtx_insn *insn)
 {
   cselib_val *ret;
 
@@ -2118,8 +2133,8 @@ cselib_lookup_from_insn (rtx x, enum machine_mode mode,
    maintains invariants related with debug insns.  */
 
 cselib_val *
-cselib_lookup (rtx x, enum machine_mode mode,
-              int create, enum machine_mode memmode)
+cselib_lookup (rtx x, machine_mode mode,
+              int create, machine_mode memmode)
 {
   cselib_val *ret = cselib_lookup_1 (x, mode, create, memmode);
 
@@ -2141,6 +2156,52 @@ cselib_lookup (rtx x, enum machine_mode mode,
   return ret;
 }
 
+/* Invalidate the value at *L, which is part of REG_VALUES (REGNO).  */
+
+static void
+cselib_invalidate_regno_val (unsigned int regno, struct elt_list **l)
+{
+  cselib_val *v = (*l)->elt;
+  if (*l == REG_VALUES (regno))
+    {
+      /* Maintain the invariant that the first entry of
+        REG_VALUES, if present, must be the value used to set
+        the register, or NULL.  This is also nice because
+        then we won't push the same regno onto user_regs
+        multiple times.  */
+      (*l)->elt = NULL;
+      l = &(*l)->next;
+    }
+  else
+    unchain_one_elt_list (l);
+
+  v = canonical_cselib_val (v);
+
+  bool had_locs = v->locs != NULL;
+  rtx_insn *setting_insn = v->locs ? v->locs->setting_insn : NULL;
+
+  /* Now, we clear the mapping from value to reg.  It must exist, so
+     this code will crash intentionally if it doesn't.  */
+  for (elt_loc_list **p = &v->locs; ; p = &(*p)->next)
+    {
+      rtx x = (*p)->loc;
+
+      if (REG_P (x) && REGNO (x) == regno)
+       {
+         unchain_one_elt_loc_list (p);
+         break;
+       }
+    }
+
+  if (had_locs && v->locs == 0 && !PRESERVED_VALUE_P (v->val_rtx))
+    {
+      if (setting_insn && DEBUG_INSN_P (setting_insn))
+       n_useless_debug_values++;
+      else
+       n_useless_values++;
+    }
+}
+
 /* Invalidate any entries in reg_values that overlap REGNO.  This is called
    if REGNO is changing.  MODE is the mode of the assignment to REGNO, which
    is used to determine how many hard registers are being changed.  If MODE
@@ -2148,7 +2209,7 @@ cselib_lookup (rtx x, enum machine_mode mode,
    invalidating call clobbered registers across a call.  */
 
 static void
-cselib_invalidate_regno (unsigned int regno, enum machine_mode mode)
+cselib_invalidate_regno (unsigned int regno, machine_mode mode)
 {
   unsigned int endregno;
   unsigned int i;
@@ -2187,9 +2248,6 @@ cselib_invalidate_regno (unsigned int regno, enum machine_mode mode)
       while (*l)
        {
          cselib_val *v = (*l)->elt;
-         bool had_locs;
-         rtx setting_insn;
-         struct elt_loc_list **p;
          unsigned int this_last = i;
 
          if (i < FIRST_PSEUDO_REGISTER && v != NULL)
@@ -2204,44 +2262,7 @@ cselib_invalidate_regno (unsigned int regno, enum machine_mode mode)
            }
 
          /* We have an overlap.  */
-         if (*l == REG_VALUES (i))
-           {
-             /* Maintain the invariant that the first entry of
-                REG_VALUES, if present, must be the value used to set
-                the register, or NULL.  This is also nice because
-                then we won't push the same regno onto user_regs
-                multiple times.  */
-             (*l)->elt = NULL;
-             l = &(*l)->next;
-           }
-         else
-           unchain_one_elt_list (l);
-
-         v = canonical_cselib_val (v);
-
-         had_locs = v->locs != NULL;
-         setting_insn = v->locs ? v->locs->setting_insn : NULL;
-
-         /* Now, we clear the mapping from value to reg.  It must exist, so
-            this code will crash intentionally if it doesn't.  */
-         for (p = &v->locs; ; p = &(*p)->next)
-           {
-             rtx x = (*p)->loc;
-
-             if (REG_P (x) && REGNO (x) == i)
-               {
-                 unchain_one_elt_loc_list (p);
-                 break;
-               }
-           }
-
-         if (had_locs && v->locs == 0 && !PRESERVED_VALUE_P (v->val_rtx))
-           {
-             if (setting_insn && DEBUG_INSN_P (setting_insn))
-               n_useless_debug_values++;
-             else
-               n_useless_values++;
-           }
+         cselib_invalidate_regno_val (i, l);
        }
     }
 }
@@ -2266,7 +2287,7 @@ cselib_invalidate_mem (rtx mem_rtx)
       bool has_mem = false;
       struct elt_loc_list **p = &v->locs;
       bool had_locs = v->locs != NULL;
-      rtx setting_insn = v->locs ? v->locs->setting_insn : NULL;
+      rtx_insn *setting_insn = v->locs ? v->locs->setting_insn : NULL;
 
       while (*p)
        {
@@ -2281,7 +2302,7 @@ cselib_invalidate_mem (rtx mem_rtx)
              p = &(*p)->next;
              continue;
            }
-         if (num_mems < PARAM_VALUE (PARAM_MAX_CSELIB_MEMORY_LOCATIONS)
+         if (num_mems < param_max_cselib_memory_locations
              && ! canon_anti_dependence (x, false, mem_rtx,
                                          GET_MODE (mem_rtx), mem_addr))
            {
@@ -2337,7 +2358,7 @@ cselib_invalidate_mem (rtx mem_rtx)
   *vp = &dummy_val;
 }
 
-/* Invalidate DEST, which is being assigned to or clobbered.  */
+/* Invalidate DEST.  */
 
 void
 cselib_invalidate_rtx (rtx dest)
@@ -2356,7 +2377,7 @@ cselib_invalidate_rtx (rtx dest)
 /* A wrapper for cselib_invalidate_rtx to be called via note_stores.  */
 
 static void
-cselib_invalidate_rtx_note_stores (rtx dest, const_rtx ignore ATTRIBUTE_UNUSED,
+cselib_invalidate_rtx_note_stores (rtx dest, const_rtx,
                                   void *data ATTRIBUTE_UNUSED)
 {
   cselib_invalidate_rtx (dest);
@@ -2369,16 +2390,15 @@ cselib_invalidate_rtx_note_stores (rtx dest, const_rtx ignore ATTRIBUTE_UNUSED,
 static void
 cselib_record_set (rtx dest, cselib_val *src_elt, cselib_val *dest_addr_elt)
 {
-  int dreg = REG_P (dest) ? (int) REGNO (dest) : -1;
-
   if (src_elt == 0 || side_effects_p (dest))
     return;
 
-  if (dreg >= 0)
+  if (REG_P (dest))
     {
+      unsigned int dreg = REGNO (dest);
       if (dreg < FIRST_PSEUDO_REGISTER)
        {
-         unsigned int n = hard_regno_nregs[dreg][GET_MODE (dest)];
+         unsigned int n = REG_NREGS (dest);
 
          if (n > max_value_regs)
            max_value_regs = n;
@@ -2412,10 +2432,10 @@ cselib_record_set (rtx dest, cselib_val *src_elt, cselib_val *dest_addr_elt)
 /* Make ELT and X's VALUE equivalent to each other at INSN.  */
 
 void
-cselib_add_permanent_equiv (cselib_val *elt, rtx x, rtx insn)
+cselib_add_permanent_equiv (cselib_val *elt, rtx x, rtx_insn *insn)
 {
   cselib_val *nelt;
-  rtx save_cselib_current_insn = cselib_current_insn;
+  rtx_insn *save_cselib_current_insn = cselib_current_insn;
 
   gcc_checking_assert (elt);
   gcc_checking_assert (PRESERVED_VALUE_P (elt->val_rtx));
@@ -2475,22 +2495,22 @@ cselib_record_autoinc_cb (rtx mem ATTRIBUTE_UNUSED, rtx op ATTRIBUTE_UNUSED,
 
   data->n_sets++;
 
-  return -1;
+  return 0;
 }
 
 /* Record the effects of any sets and autoincs in INSN.  */
 static void
-cselib_record_sets (rtx insn)
+cselib_record_sets (rtx_insn *insn)
 {
   int n_sets = 0;
   int i;
   struct cselib_set sets[MAX_SETS];
-  rtx body = PATTERN (insn);
   rtx cond = 0;
   int n_sets_before_autoinc;
+  int n_strict_low_parts = 0;
   struct cselib_record_autoinc_data data;
 
-  body = PATTERN (insn);
+  rtx body = PATTERN (insn);
   if (GET_CODE (body) == COND_EXEC)
     {
       cond = COND_EXEC_TEST (body);
@@ -2534,7 +2554,7 @@ cselib_record_sets (rtx insn)
 
   data.sets = sets;
   data.n_sets = n_sets_before_autoinc = n_sets;
-  for_each_inc_dec (&insn, cselib_record_autoinc_cb, &data);
+  for_each_inc_dec (PATTERN (insn), cselib_record_autoinc_cb, &data);
   n_sets = data.n_sets;
 
   /* Look up the values that are read.  Do this before invalidating the
@@ -2542,6 +2562,7 @@ cselib_record_sets (rtx insn)
   for (i = 0; i < n_sets; i++)
     {
       rtx dest = sets[i].dest;
+      rtx orig = dest;
 
       /* A STRICT_LOW_PART can be ignored; we'll record the equivalence for
          the low part after invalidating any knowledge about larger modes.  */
@@ -2558,7 +2579,7 @@ cselib_record_sets (rtx insn)
          sets[i].src_elt = cselib_lookup (src, GET_MODE (dest), 1, VOIDmode);
          if (MEM_P (dest))
            {
-             enum machine_mode address_mode = get_address_mode (dest);
+             machine_mode address_mode = get_address_mode (dest);
 
              sets[i].dest_addr_elt = cselib_lookup (XEXP (dest, 0),
                                                     address_mode, 1,
@@ -2567,6 +2588,56 @@ cselib_record_sets (rtx insn)
          else
            sets[i].dest_addr_elt = 0;
        }
+
+      /* Improve handling of STRICT_LOW_PART if the current value is known
+        to be const0_rtx, then the low bits will be set to dest and higher
+        bits will remain zero.  Used in code like:
+
+        {di:SI=0;clobber flags:CC;}
+        flags:CCNO=cmp(bx:SI,0)
+        strict_low_part(di:QI)=flags:CCNO<=0
+
+        where we can note both that di:QI=flags:CCNO<=0 and
+        also that because di:SI is known to be 0 and strict_low_part(di:QI)
+        preserves the upper bits that di:SI=zero_extend(flags:CCNO<=0).  */
+      scalar_int_mode mode;
+      if (dest != orig
+         && cselib_record_sets_hook
+         && REG_P (dest)
+         && HARD_REGISTER_P (dest)
+         && sets[i].src_elt
+         && is_a <scalar_int_mode> (GET_MODE (dest), &mode)
+         && n_sets + n_strict_low_parts < MAX_SETS)
+       {
+         opt_scalar_int_mode wider_mode_iter;
+         FOR_EACH_WIDER_MODE (wider_mode_iter, mode)
+           {
+             scalar_int_mode wider_mode = wider_mode_iter.require ();
+             if (GET_MODE_PRECISION (wider_mode) > BITS_PER_WORD)
+               break;
+
+             rtx reg = gen_lowpart (wider_mode, dest);
+             if (!REG_P (reg))
+               break;
+
+             cselib_val *v = cselib_lookup (reg, wider_mode, 0, VOIDmode);
+             if (!v)
+               continue;
+
+             struct elt_loc_list *l;
+             for (l = v->locs; l; l = l->next)
+               if (l->loc == const0_rtx)
+                 break;
+
+             if (!l)
+               continue;
+
+             sets[n_sets + n_strict_low_parts].dest = reg;
+             sets[n_sets + n_strict_low_parts].src = dest;
+             sets[n_sets + n_strict_low_parts++].src_elt = sets[i].src_elt;
+             break;
+           }
+       }
     }
 
   if (cselib_record_sets_hook)
@@ -2575,7 +2646,7 @@ cselib_record_sets (rtx insn)
   /* Invalidate all locations written by this insn.  Note that the elts we
      looked up in the previous loop aren't affected, just some of their
      locations may go away.  */
-  note_stores (body, cselib_invalidate_rtx_note_stores, NULL);
+  note_pattern_stores (body, cselib_invalidate_rtx_note_stores, NULL);
 
   for (i = n_sets_before_autoinc; i < n_sets; i++)
     cselib_invalidate_rtx (sets[i].dest);
@@ -2611,12 +2682,26 @@ cselib_record_sets (rtx insn)
          || (MEM_P (dest) && cselib_record_memory))
        cselib_record_set (dest, sets[i].src_elt, sets[i].dest_addr_elt);
     }
+
+  /* And deal with STRICT_LOW_PART.  */
+  for (i = 0; i < n_strict_low_parts; i++)
+    {
+      if (! PRESERVED_VALUE_P (sets[n_sets + i].src_elt->val_rtx))
+       continue;
+      machine_mode dest_mode = GET_MODE (sets[n_sets + i].dest);
+      cselib_val *v
+       = cselib_lookup (sets[n_sets + i].dest, dest_mode, 1, VOIDmode);
+      cselib_preserve_value (v);
+      rtx r = gen_rtx_ZERO_EXTEND (dest_mode,
+                                  sets[n_sets + i].src_elt->val_rtx);
+      cselib_add_permanent_equiv (v, r, insn);
+    }
 }
 
 /* Return true if INSN in the prologue initializes hard_frame_pointer_rtx.  */
 
 bool
-fp_setter_insn (rtx insn)
+fp_setter_insn (rtx_insn *insn)
 {
   rtx expr, pat = NULL_RTX;
 
@@ -2635,32 +2720,52 @@ fp_setter_insn (rtx insn)
   return true;
 }
 
+/* V is one of the values in REG_VALUES (REGNO).  Return true if it
+   would be invalidated by CALLEE_ABI.  */
+
+static bool
+cselib_invalidated_by_call_p (const function_abi &callee_abi,
+                             unsigned int regno, cselib_val *v)
+{
+  machine_mode mode = GET_MODE (v->val_rtx);
+  if (mode == VOIDmode)
+    {
+      v = REG_VALUES (regno)->elt;
+      if (!v)
+       /* If we don't know what the mode of the constant value is, and we
+          don't know what mode the register was set in, conservatively
+          assume that the register is clobbered.  The value's going to be
+          essentially useless in this case anyway.  */
+       return true;
+      mode = GET_MODE (v->val_rtx);
+    }
+  return callee_abi.clobbers_reg_p (mode, regno);
+}
+
 /* Record the effects of INSN.  */
 
 void
-cselib_process_insn (rtx insn)
+cselib_process_insn (rtx_insn *insn)
 {
   int i;
   rtx x;
 
   cselib_current_insn = insn;
 
-  /* Forget everything at a CODE_LABEL, a volatile insn, or a setjmp.  */
+  /* Forget everything at a CODE_LABEL or a setjmp.  */
   if ((LABEL_P (insn)
        || (CALL_P (insn)
-          && find_reg_note (insn, REG_SETJMP, NULL))
-       || (NONJUMP_INSN_P (insn)
-          && volatile_insn_p (PATTERN (insn))))
+          && find_reg_note (insn, REG_SETJMP, NULL)))
       && !cselib_preserve_constants)
     {
       cselib_reset_table (next_uid);
-      cselib_current_insn = NULL_RTX;
+      cselib_current_insn = NULL;
       return;
     }
 
   if (! INSN_P (insn))
     {
-      cselib_current_insn = NULL_RTX;
+      cselib_current_insn = NULL;
       return;
     }
 
@@ -2669,12 +2774,19 @@ cselib_process_insn (rtx insn)
      memory.  */
   if (CALL_P (insn))
     {
+      function_abi callee_abi = insn_callee_abi (insn);
       for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-       if (call_used_regs[i]
-           || (REG_VALUES (i) && REG_VALUES (i)->elt
-               && HARD_REGNO_CALL_PART_CLOBBERED (i,
-                     GET_MODE (REG_VALUES (i)->elt->val_rtx))))
-         cselib_invalidate_regno (i, reg_raw_mode[i]);
+       {
+         elt_list **l = &REG_VALUES (i);
+         while (*l)
+           {
+             cselib_val *v = (*l)->elt;
+             if (v && cselib_invalidated_by_call_p (callee_abi, i, v))
+               cselib_invalidate_regno_val (i, l);
+             else
+               l = &(*l)->next;
+           }
+       }
 
       /* Since it is not clear how cselib is going to be used, be
         conservative here and treat looping pure or const functions
@@ -2682,6 +2794,13 @@ cselib_process_insn (rtx insn)
       if (RTL_LOOPING_CONST_OR_PURE_CALL_P (insn)
          || !(RTL_CONST_OR_PURE_CALL_P (insn)))
        cselib_invalidate_mem (callmem);
+      else
+       /* For const/pure calls, invalidate any argument slots because
+          they are owned by the callee.  */
+       for (x = CALL_INSN_FUNCTION_USAGE (insn); x; x = XEXP (x, 1))
+         if (GET_CODE (XEXP (x, 0)) == USE
+             && MEM_P (XEXP (XEXP (x, 0), 0)))
+           cselib_invalidate_mem (XEXP (XEXP (x, 0), 0));
     }
 
   cselib_record_sets (insn);
@@ -2693,7 +2812,8 @@ cselib_process_insn (rtx insn)
       for (x = CALL_INSN_FUNCTION_USAGE (insn); x; x = XEXP (x, 1))
        if (GET_CODE (XEXP (x, 0)) == CLOBBER)
          cselib_invalidate_rtx (XEXP (XEXP (x, 0), 0));
-      /* Flush evertything on setjmp.  */
+
+      /* Flush everything on setjmp.  */
       if (cselib_preserve_constants
          && find_reg_note (insn, REG_SETJMP, NULL))
        {
@@ -2710,14 +2830,14 @@ cselib_process_insn (rtx insn)
       && fp_setter_insn (insn))
     cselib_invalidate_rtx (stack_pointer_rtx);
 
-  cselib_current_insn = NULL_RTX;
+  cselib_current_insn = NULL;
 
   if (n_useless_values > MAX_USELESS_VALUES
       /* remove_useless_values is linear in the hash table size.  Avoid
          quadratic behavior for very large hashtables with very few
         useless elements.  */
       && ((unsigned int)n_useless_values
-         > (cselib_hash_table.elements () - n_debug_values) / 4))
+         > (cselib_hash_table->elements () - n_debug_values) / 4))
     remove_useless_values ();
 }
 
@@ -2727,13 +2847,6 @@ cselib_process_insn (rtx insn)
 void
 cselib_init (int record_what)
 {
-  elt_list_pool = create_alloc_pool ("elt_list",
-                                    sizeof (struct elt_list), 10);
-  elt_loc_list_pool = create_alloc_pool ("elt_loc_list",
-                                        sizeof (struct elt_loc_list), 10);
-  cselib_val_pool = create_alloc_pool ("cselib_val_list",
-                                      sizeof (cselib_val), 10);
-  value_pool = create_alloc_pool ("value", RTX_CODE_SIZE (VALUE), 100);
   cselib_record_memory = record_what & CSELIB_RECORD_MEMORY;
   cselib_preserve_constants = record_what & CSELIB_PRESERVE_CONSTANTS;
   cselib_any_perm_equivs = false;
@@ -2758,9 +2871,14 @@ cselib_init (int record_what)
     }
   used_regs = XNEWVEC (unsigned int, cselib_nregs);
   n_used_regs = 0;
-  cselib_hash_table.create (31);
+  /* FIXME: enable sanitization (PR87845) */
+  cselib_hash_table
+    = new hash_table<cselib_hasher> (31, /* ggc */ false,
+                                    /* sanitize_eq_and_hash */ false);
   if (cselib_preserve_constants)
-    cselib_preserved_hash_table.create (31);
+    cselib_preserved_hash_table
+      = new hash_table<cselib_hasher> (31, /* ggc */ false,
+                                      /* sanitize_eq_and_hash */ false);
   next_uid = 1;
 }
 
@@ -2775,14 +2893,16 @@ cselib_finish (void)
   cselib_any_perm_equivs = false;
   cfa_base_preserved_val = NULL;
   cfa_base_preserved_regno = INVALID_REGNUM;
-  free_alloc_pool (elt_list_pool);
-  free_alloc_pool (elt_loc_list_pool);
-  free_alloc_pool (cselib_val_pool);
-  free_alloc_pool (value_pool);
+  elt_list_pool.release ();
+  elt_loc_list_pool.release ();
+  cselib_val_pool.release ();
+  value_pool.release ();
   cselib_clear_table ();
-  cselib_hash_table.dispose ();
+  delete cselib_hash_table;
+  cselib_hash_table = NULL;
   if (preserved)
-    cselib_preserved_hash_table.dispose ();
+    delete cselib_preserved_hash_table;
+  cselib_preserved_hash_table = NULL;
   free (used_regs);
   used_regs = 0;
   n_useless_values = 0;
@@ -2871,9 +2991,9 @@ void
 dump_cselib_table (FILE *out)
 {
   fprintf (out, "cselib hash table:\n");
-  cselib_hash_table.traverse <FILE *, dump_cselib_val> (out);
+  cselib_hash_table->traverse <FILE *, dump_cselib_val> (out);
   fprintf (out, "cselib preserved hash table:\n");
-  cselib_preserved_hash_table.traverse <FILE *, dump_cselib_val> (out);
+  cselib_preserved_hash_table->traverse <FILE *, dump_cselib_val> (out);
   if (first_containing_mem != &dummy_val)
     {
       fputs ("first mem ", out);