OSDN Git Service

* cselib.c (cselib_init): Use special MEM rtx form for callmem.
[pf3gnuchains/gcc-fork.git] / gcc / cselib.c
index 606eb97..4605388 100644 (file)
@@ -1,27 +1,28 @@
 /* Common subexpression elimination library for GNU compiler.
    Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2003, 2004, 2005 Free Software Foundation, Inc.
 
-This file is part of GNU CC.
+This file is part of GCC.
 
-GNU CC is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
 
-GNU CC is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
 
 You should have received a copy of the GNU General Public License
-along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+along with GCC; see the file COPYING.  If not, write to the Free
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.  */
 
 #include "config.h"
 #include "system.h"
-#include <setjmp.h>
+#include "coretypes.h"
+#include "tm.h"
 
 #include "rtl.h"
 #include "tm_p.h"
@@ -32,44 +33,36 @@ Boston, MA 02111-1307, USA.  */
 #include "insn-config.h"
 #include "recog.h"
 #include "function.h"
-#include "expr.h"
+#include "emit-rtl.h"
 #include "toplev.h"
 #include "output.h"
 #include "ggc.h"
-#include "obstack.h"
 #include "hashtab.h"
 #include "cselib.h"
-
-static int entry_and_rtx_equal_p       PARAMS ((const void *, const void *));
-static unsigned int get_value_hash     PARAMS ((const void *));
-static struct elt_list *new_elt_list   PARAMS ((struct elt_list *,
-                                                cselib_val *));
-static struct elt_loc_list *new_elt_loc_list PARAMS ((struct elt_loc_list *,
-                                                     rtx));
-static void unchain_one_value          PARAMS ((cselib_val *));
-static void unchain_one_elt_list       PARAMS ((struct elt_list **));
-static void unchain_one_elt_loc_list   PARAMS ((struct elt_loc_list **));
-static void clear_table                        PARAMS ((int));
-static int discard_useless_locs                PARAMS ((void **, void *));
-static int discard_useless_values      PARAMS ((void **, void *));
-static void remove_useless_values      PARAMS ((void));
-static rtx wrap_constant               PARAMS ((enum machine_mode, rtx));
-static unsigned int hash_rtx           PARAMS ((rtx, enum machine_mode, int));
-static cselib_val *new_cselib_val      PARAMS ((unsigned int,
-                                                enum machine_mode));
-static void add_mem_for_addr           PARAMS ((cselib_val *, cselib_val *,
-                                                rtx));
-static cselib_val *cselib_lookup_mem   PARAMS ((rtx, int));
-static rtx cselib_subst_to_values      PARAMS ((rtx));
-static void cselib_invalidate_regno    PARAMS ((unsigned int,
-                                                enum machine_mode));
-static int cselib_mem_conflict_p       PARAMS ((rtx, rtx));
-static int cselib_invalidate_mem_1     PARAMS ((void **, void *));
-static void cselib_invalidate_mem      PARAMS ((rtx));
-static void cselib_invalidate_rtx      PARAMS ((rtx, rtx, void *));
-static void cselib_record_set          PARAMS ((rtx, cselib_val *,
-                                                cselib_val *));
-static void cselib_record_sets         PARAMS ((rtx));
+#include "params.h"
+#include "alloc-pool.h"
+#include "target.h"
+
+static bool cselib_record_memory;
+static int entry_and_rtx_equal_p (const void *, const void *);
+static hashval_t get_value_hash (const void *);
+static struct elt_list *new_elt_list (struct elt_list *, cselib_val *);
+static struct elt_loc_list *new_elt_loc_list (struct elt_loc_list *, rtx);
+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 int discard_useless_locs (void **, void *);
+static int discard_useless_values (void **, void *);
+static void remove_useless_values (void);
+static rtx wrap_constant (enum machine_mode, rtx);
+static unsigned int cselib_hash_rtx (rtx, int);
+static cselib_val *new_cselib_val (unsigned int, enum machine_mode);
+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_mem (rtx);
+static void cselib_record_set (rtx, cselib_val *, cselib_val *);
+static void cselib_record_sets (rtx);
 
 /* There are three ways in which cselib can look up an rtx:
    - for a REG, the reg_values table (which is indexed by regno) is used
@@ -81,11 +74,12 @@ static void cselib_record_sets              PARAMS ((rtx));
      the locations of the entries with the rtx we are looking up.  */
 
 /* A table that enables us to look up elts by their value.  */
-static htab_t hash_table;
+static htab_t cselib_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 bool cselib_current_insn_in_libcall;
 
 /* Every new unknown value gets a unique number.  */
 static unsigned int next_unknown_value;
@@ -100,51 +94,53 @@ static int n_useless_values;
 /* Number of useless values before we remove them from the hash table.  */
 #define MAX_USELESS_VALUES 32
 
-/* This table maps from register number to values.  It does not contain
-   pointers to cselib_val structures, but rather elt_lists.  The purpose is
-   to be able to refer to the same register in different modes.  */
-static varray_type reg_values;
-#define REG_VALUES(I) VARRAY_ELT_LIST (reg_values, (I))
+/* This table maps from register number to values.  It does not
+   contain pointers to cselib_val structures, but rather elt_lists.
+   The purpose is to be able to refer to the same register in
+   different modes.  The first element of the list defines the mode in
+   which the register was set; if the mode is unknown or the value is
+   no longer valid in that mode, ELT will be NULL for the first
+   element.  */
+static struct elt_list **reg_values;
+static unsigned int reg_values_size;
+#define REG_VALUES(i) reg_values[i]
+
+/* The largest number of hard regs used by any entry added to the
+   REG_VALUES table.  Cleared on each cselib_clear_table() invocation.  */
+static unsigned int max_value_regs;
 
 /* Here the set of indices I with REG_VALUES(I) != 0 is saved.  This is used
-   in clear_table() for fast emptying.  */
-static varray_type used_regs;
+   in cselib_clear_table() for fast emptying.  */
+static unsigned int *used_regs;
+static unsigned int n_used_regs;
 
 /* We pass this to cselib_invalidate_mem to invalidate all of
    memory for a non-const call instruction.  */
-static rtx callmem;
-
-/* Memory for our structures is allocated from this obstack.  */
-static struct obstack cselib_obstack;
-
-/* Used to quickly free all memory.  */
-static char *cselib_startobj;
-
-/* Caches for unused structures.  */
-static cselib_val *empty_vals;
-static struct elt_list *empty_elt_lists;
-static struct elt_loc_list *empty_elt_loc_lists;
+static GTY(()) rtx callmem;
 
 /* Set by discard_useless_locs if it deleted the last location of any
    value.  */
 static int values_became_useless;
+
+/* Used as stop element of the containing_mem list so we can check
+   presence in the list by checking the next pointer.  */
+static cselib_val dummy_val;
+
+/* Used to list all values that contain memory reference.
+   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;
 \f
 
 /* Allocate a struct elt_list and fill in its two elements with the
    arguments.  */
 
-static struct elt_list *
-new_elt_list (next, elt)
-     struct elt_list *next;
-     cselib_val *elt;
+static inline struct elt_list *
+new_elt_list (struct elt_list *next, cselib_val *elt)
 {
-  struct elt_list *el = empty_elt_lists;
-
-  if (el)
-    empty_elt_lists = el->next;
-  else
-    el = (struct elt_list *) obstack_alloc (&cselib_obstack,
-                                           sizeof (struct elt_list));
+  struct elt_list *el;
+  el = pool_alloc (elt_list_pool);
   el->next = next;
   el->elt = elt;
   return el;
@@ -153,93 +149,76 @@ new_elt_list (next, elt)
 /* Allocate a struct elt_loc_list and fill in its two elements with the
    arguments.  */
 
-static struct elt_loc_list *
-new_elt_loc_list (next, loc)
-     struct elt_loc_list *next;
-     rtx loc;
+static inline struct elt_loc_list *
+new_elt_loc_list (struct elt_loc_list *next, rtx loc)
 {
-  struct elt_loc_list *el = empty_elt_loc_lists;
-
-  if (el)
-    empty_elt_loc_lists = el->next;
-  else
-    el = (struct elt_loc_list *) obstack_alloc (&cselib_obstack,
-                                               sizeof (struct elt_loc_list));
+  struct elt_loc_list *el;
+  el = pool_alloc (elt_loc_list_pool);
   el->next = next;
   el->loc = loc;
   el->setting_insn = cselib_current_insn;
+  el->in_libcall = cselib_current_insn_in_libcall;
   return el;
 }
 
 /* The elt_list at *PL is no longer needed.  Unchain it and free its
    storage.  */
 
-static void
-unchain_one_elt_list (pl)
-     struct elt_list **pl;
+static inline void
+unchain_one_elt_list (struct elt_list **pl)
 {
   struct elt_list *l = *pl;
 
   *pl = l->next;
-  l->next = empty_elt_lists;
-  empty_elt_lists = l;
+  pool_free (elt_list_pool, l);
 }
 
 /* Likewise for elt_loc_lists.  */
 
 static void
-unchain_one_elt_loc_list (pl)
-     struct elt_loc_list **pl;
+unchain_one_elt_loc_list (struct elt_loc_list **pl)
 {
   struct elt_loc_list *l = *pl;
 
   *pl = l->next;
-  l->next = empty_elt_loc_lists;
-  empty_elt_loc_lists = l;
+  pool_free (elt_loc_list_pool, l);
 }
 
 /* Likewise for cselib_vals.  This also frees the addr_list associated with
    V.  */
 
 static void
-unchain_one_value (v)
-     cselib_val *v;
+unchain_one_value (cselib_val *v)
 {
   while (v->addr_list)
     unchain_one_elt_list (&v->addr_list);
 
-  v->u.next_free = empty_vals;
-  empty_vals = v;
+  pool_free (cselib_val_pool, v);
 }
 
 /* Remove all entries from the hash table.  Also used during
    initialization.  If CLEAR_ALL isn't set, then only clear the entries
    which are known to have been used.  */
 
-static void
-clear_table (clear_all)
-     int clear_all;
+void
+cselib_clear_table (void)
 {
   unsigned int i;
 
-  if (clear_all)
-    for (i = 0; i < cselib_nregs; i++)
-      REG_VALUES (i) = 0;
-  else
-    for (i = 0; i < VARRAY_ACTIVE_SIZE (used_regs); i++)
-      REG_VALUES (VARRAY_UINT (used_regs, i)) = 0;
+  for (i = 0; i < n_used_regs; i++)
+    REG_VALUES (used_regs[i]) = 0;
+
+  max_value_regs = 0;
 
-  VARRAY_POP_ALL (used_regs);
+  n_used_regs = 0;
 
-  htab_empty (hash_table);
-  obstack_free (&cselib_obstack, cselib_startobj);
+  htab_empty (cselib_hash_table);
 
-  empty_vals = 0;
-  empty_elt_lists = 0;
-  empty_elt_loc_lists = 0;
   n_useless_values = 0;
 
   next_unknown_value = 0;
+
+  first_containing_mem = &dummy_val;
 }
 
 /* The equality test for our hash table.  The first argument ENTRY is a table
@@ -248,17 +227,16 @@ clear_table (clear_all)
    CONST of an appropriate mode.  */
 
 static int
-entry_and_rtx_equal_p (entry, x_arg)
-     const void *entry, *x_arg;
+entry_and_rtx_equal_p (const void *entry, const void *x_arg)
 {
   struct elt_loc_list *l;
   const cselib_val *v = (const cselib_val *) entry;
   rtx x = (rtx) x_arg;
   enum machine_mode mode = GET_MODE (x);
 
-  if (GET_CODE (x) == CONST_INT
-      || (mode == VOIDmode && GET_CODE (x) == CONST_DOUBLE))
-    abort ();
+  gcc_assert (GET_CODE (x) != CONST_INT
+             && (mode != VOIDmode || GET_CODE (x) != CONST_DOUBLE));
+  
   if (mode != GET_MODE (v->u.val_rtx))
     return 0;
 
@@ -267,7 +245,7 @@ entry_and_rtx_equal_p (entry, x_arg)
       && (GET_CODE (XEXP (x, 0)) == CONST_INT
          || GET_CODE (XEXP (x, 0)) == CONST_DOUBLE))
     x = XEXP (x, 0);
-  
+
   /* 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)
@@ -278,12 +256,11 @@ entry_and_rtx_equal_p (entry, x_arg)
 }
 
 /* The hash function for our hash table.  The value is always computed with
-   hash_rtx when adding an element; this function just extracts the hash
-   value from a cselib_val structure.  */
+   cselib_hash_rtx when adding an element; this function just extracts the
+   hash value from a cselib_val structure.  */
 
-static unsigned int
-get_value_hash (entry)
-     const void *entry;
+static hashval_t
+get_value_hash (const void *entry)
 {
   const cselib_val *v = (const cselib_val *) entry;
   return v->value;
@@ -295,9 +272,7 @@ get_value_hash (entry)
    removed.  */
 
 int
-references_value_p (x, only_useless)
-     rtx x;
-     int only_useless;
+references_value_p (rtx x, int only_useless)
 {
   enum rtx_code code = GET_CODE (x);
   const char *fmt = GET_RTX_FORMAT (code);
@@ -325,9 +300,7 @@ references_value_p (x, only_useless)
    htab_traverse.  */
 
 static int
-discard_useless_locs (x, info)
-     void **x;
-     void *info ATTRIBUTE_UNUSED;
+discard_useless_locs (void **x, void *info ATTRIBUTE_UNUSED)
 {
   cselib_val *v = (cselib_val *)*x;
   struct elt_loc_list **p = &v->locs;
@@ -352,15 +325,14 @@ discard_useless_locs (x, info)
 /* If X is a value with no locations, remove it from the hashtable.  */
 
 static int
-discard_useless_values (x, info)
-     void **x;
-     void *info ATTRIBUTE_UNUSED;
+discard_useless_values (void **x, void *info ATTRIBUTE_UNUSED)
 {
   cselib_val *v = (cselib_val *)*x;
 
   if (v->locs == 0)
     {
-      htab_clear_slot (hash_table, x);
+      CSELIB_VAL_PTR (v->u.val_rtx) = NULL;
+      htab_clear_slot (cselib_hash_table, x);
       unchain_one_value (v);
       n_useless_values--;
     }
@@ -372,36 +344,63 @@ discard_useless_values (x, info)
    associated with them) from the hash table.  */
 
 static void
-remove_useless_values ()
+remove_useless_values (void)
 {
+  cselib_val **p, *v;
   /* First pass: eliminate locations that reference the value.  That in
      turn can make more values useless.  */
   do
     {
       values_became_useless = 0;
-      htab_traverse (hash_table, discard_useless_locs, 0);
+      htab_traverse (cselib_hash_table, discard_useless_locs, 0);
     }
   while (values_became_useless);
 
   /* Second pass: actually remove the values.  */
-  htab_traverse (hash_table, discard_useless_values, 0);
 
-  if (n_useless_values != 0)
-    abort ();
+  p = &first_containing_mem;
+  for (v = *p; v != &dummy_val; v = v->next_containing_mem)
+    if (v->locs)
+      {
+       *p = v;
+       p = &(*p)->next_containing_mem;
+      }
+  *p = &dummy_val;
+
+  htab_traverse (cselib_hash_table, discard_useless_values, 0);
+
+  gcc_assert (!n_useless_values);
+}
+
+/* Return the mode in which a register was last set.  If X is not a
+   register, return its mode.  If the mode in which the register was
+   set is not known, or the value was already clobbered, return
+   VOIDmode.  */
+
+enum machine_mode
+cselib_reg_set_mode (rtx x)
+{
+  if (!REG_P (x))
+    return GET_MODE (x);
+
+  if (REG_VALUES (REGNO (x)) == NULL
+      || REG_VALUES (REGNO (x))->elt == NULL)
+    return VOIDmode;
+
+  return GET_MODE (REG_VALUES (REGNO (x))->elt->u.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 (x, y)
-     rtx x, y;
+rtx_equal_for_cselib_p (rtx x, rtx y)
 {
   enum rtx_code code;
   const char *fmt;
   int i;
-  
-  if (GET_CODE (x) == REG || GET_CODE (x) == MEM)
+
+  if (REG_P (x) || MEM_P (x))
     {
       cselib_val *e = cselib_lookup (x, GET_MODE (x), 0);
 
@@ -409,7 +408,7 @@ rtx_equal_for_cselib_p (x, y)
        x = e->u.val_rtx;
     }
 
-  if (GET_CODE (y) == REG || GET_CODE (y) == MEM)
+  if (REG_P (y) || MEM_P (y))
     {
       cselib_val *e = cselib_lookup (y, GET_MODE (y), 0);
 
@@ -433,12 +432,12 @@ rtx_equal_for_cselib_p (x, y)
          rtx t = l->loc;
 
          /* Avoid infinite recursion.  */
-         if (GET_CODE (t) == REG || GET_CODE (t) == MEM)
+         if (REG_P (t) || MEM_P (t))
            continue;
          else if (rtx_equal_for_cselib_p (t, y))
            return 1;
        }
-      
+
       return 0;
     }
 
@@ -451,22 +450,31 @@ rtx_equal_for_cselib_p (x, y)
        {
          rtx t = l->loc;
 
-         if (GET_CODE (t) == REG || GET_CODE (t) == MEM)
+         if (REG_P (t) || MEM_P (t))
            continue;
          else if (rtx_equal_for_cselib_p (x, t))
            return 1;
        }
-      
+
       return 0;
     }
 
   if (GET_CODE (x) != GET_CODE (y) || GET_MODE (x) != GET_MODE (y))
     return 0;
 
-  /* This won't be handled correctly by the code below.  */
-  if (GET_CODE (x) == LABEL_REF)
-    return XEXP (x, 0) == XEXP (y, 0);
-  
+  /* These won't be handled correctly by the code below.  */
+  switch (GET_CODE (x))
+    {
+    case CONST_DOUBLE:
+      return 0;
+
+    case LABEL_REF:
+      return XEXP (x, 0) == XEXP (y, 0);
+
+    default:
+      break;
+    }
+
   code = GET_CODE (x);
   fmt = GET_RTX_FORMAT (code);
 
@@ -501,6 +509,11 @@ rtx_equal_for_cselib_p (x, y)
          break;
 
        case 'e':
+         if (i == 1
+             && targetm.commutative_p (x, UNKNOWN)
+             && rtx_equal_for_cselib_p (XEXP (x, 1), XEXP (y, 0))
+             && rtx_equal_for_cselib_p (XEXP (x, 0), XEXP (y, 1)))
+           return 1;
          if (! rtx_equal_for_cselib_p (XEXP (x, i), XEXP (y, i)))
            return 0;
          break;
@@ -523,7 +536,7 @@ rtx_equal_for_cselib_p (x, y)
             contain anything but integers and other rtx's,
             except for within LABEL_REFs and SYMBOL_REFs.  */
        default:
-         abort ();
+         gcc_unreachable ();
        }
     }
   return 1;
@@ -533,15 +546,12 @@ rtx_equal_for_cselib_p (x, y)
    functions.  For that purpose, wrap them in a CONST of the appropriate
    mode.  */
 static rtx
-wrap_constant (mode, x)
-     enum machine_mode mode;
-     rtx x;
+wrap_constant (enum machine_mode mode, rtx x)
 {
   if (GET_CODE (x) != CONST_INT
       && (GET_CODE (x) != CONST_DOUBLE || GET_MODE (x) != VOIDmode))
     return x;
-  if (mode == VOIDmode)
-    abort ();
+  gcc_assert (mode != VOIDmode);
   return gen_rtx_CONST (mode, x);
 }
 
@@ -551,14 +561,22 @@ wrap_constant (mode, x)
    Possible reasons for return 0 are: the object is volatile, or we couldn't
    find a register or memory location in the table and CREATE is zero.  If
    CREATE is nonzero, table elts are created for regs and mem.
-   MODE is used in hashing for CONST_INTs only;
-   otherwise the mode of X is used.  */
+   N.B. this hash function returns the same hash value for RTXes that
+   differ only in the order of operands, thus it is suitable for comparisons
+   that take commutativity into account.
+   If we wanted to also support associative rules, we'd have to use a different
+   strategy to avoid returning spurious 0, e.g. return ~(~0U >> 1) .
+   We used to have a MODE argument for hashing for CONST_INTs, but that
+   didn't make sense, since it caused spurious hash differences between
+    (set (reg:SI 1) (const_int))
+    (plus:SI (reg:SI 2) (reg:SI 1))
+   and
+    (plus:SI (reg:SI 2) (const_int))
+   If the mode is important in any context, it must be checked specifically
+   in a comparison anyway, since relying on hash differences is unsafe.  */
 
 static unsigned int
-hash_rtx (x, mode, create)
-     rtx x;
-     enum machine_mode mode;
-     int create;
+cselib_hash_rtx (rtx x, int create)
 {
   cselib_val *e;
   int i, j;
@@ -566,8 +584,6 @@ hash_rtx (x, mode, create)
   const char *fmt;
   unsigned int hash = 0;
 
-  /* repeat is used to turn tail-recursion into iteration.  */
- repeat:
   code = GET_CODE (x);
   hash += (unsigned) code + (unsigned) GET_MODE (x);
 
@@ -579,35 +595,63 @@ hash_rtx (x, mode, create)
       if (! e)
        return 0;
 
-      hash += e->value;
-      return hash;
+      return e->value;
 
     case CONST_INT:
-      hash += ((unsigned) CONST_INT << 7) + (unsigned) mode + INTVAL (x);
-      return hash ? hash : CONST_INT;
+      hash += ((unsigned) CONST_INT << 7) + INTVAL (x);
+      return hash ? hash : (unsigned int) CONST_INT;
 
     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)
-       for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++)
-         hash += XWINT (x, i);
+       hash += real_hash (CONST_DOUBLE_REAL_VALUE (x));
       else
        hash += ((unsigned) CONST_DOUBLE_LOW (x)
                 + (unsigned) CONST_DOUBLE_HIGH (x));
-      return hash ? hash : CONST_DOUBLE;
+      return hash ? hash : (unsigned int) CONST_DOUBLE;
+
+    case CONST_VECTOR:
+      {
+       int units;
+       rtx elt;
+
+       units = CONST_VECTOR_NUNITS (x);
+
+       for (i = 0; i < units; ++i)
+         {
+           elt = CONST_VECTOR_ELT (x, i);
+           hash += cselib_hash_rtx (elt, 0);
+         }
+
+       return hash;
+      }
 
       /* Assume there is only one rtx object for any given label.  */
     case LABEL_REF:
-      hash
-       += ((unsigned) LABEL_REF << 7) + (unsigned long) XEXP (x, 0);
-      return hash ? hash : LABEL_REF;
+      /* 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)));
+      return hash ? hash : (unsigned int) LABEL_REF;
 
     case SYMBOL_REF:
-      hash
-       += ((unsigned) SYMBOL_REF << 7) + (unsigned long) XSTR (x, 0);
-      return hash ? hash : SYMBOL_REF;
+      {
+       /* Don't hash on the symbol's address to avoid bootstrap differences.
+          Different hash values may cause expressions to be recorded in
+          different orders and thus different registers to be used in the
+          final assembler.  This also avoids differences in the dump files
+          between various stages.  */
+       unsigned int h = 0;
+       const unsigned char *p = (const unsigned char *) XSTR (x, 0);
+
+       while (*p)
+         h += (h << 7) + *p++; /* ??? revisit */
+
+       hash += ((unsigned int) SYMBOL_REF << 7) + h;
+       return hash ? hash : (unsigned int) SYMBOL_REF;
+      }
 
     case PRE_DEC:
     case PRE_INC:
@@ -626,7 +670,7 @@ hash_rtx (x, mode, create)
        return 0;
 
       break;
-      
+
     default:
       break;
     }
@@ -635,78 +679,83 @@ hash_rtx (x, mode, create)
   fmt = GET_RTX_FORMAT (code);
   for (; i >= 0; i--)
     {
-      if (fmt[i] == 'e')
+      switch (fmt[i])
        {
-         rtx tem = XEXP (x, i);
-         unsigned int tem_hash;
-
-         /* If we are about to do the last recursive call
-            needed at this level, change it into iteration.
-            This function  is called enough to be worth it.  */
-         if (i == 0)
-           {
-             x = tem;
-             goto repeat;
-           }
-
-         tem_hash = hash_rtx (tem, 0, create);
-         if (tem_hash == 0)
-           return 0;
-
-         hash += tem_hash;
-       }
-      else if (fmt[i] == 'E')
-       for (j = 0; j < XVECLEN (x, i); j++)
+       case 'e':
          {
-           unsigned int tem_hash = hash_rtx (XVECEXP (x, i, j), 0, create);
-
+           rtx tem = XEXP (x, i);
+           unsigned int tem_hash = cselib_hash_rtx (tem, create);
+           
            if (tem_hash == 0)
              return 0;
-
+           
            hash += tem_hash;
          }
-      else if (fmt[i] == 's')
-       {
-         const unsigned char *p = (const unsigned char *) XSTR (x, i);
+         break;
+       case 'E':
+         for (j = 0; j < XVECLEN (x, i); j++)
+           {
+             unsigned int tem_hash
+               = cselib_hash_rtx (XVECEXP (x, i, j), create);
+             
+             if (tem_hash == 0)
+               return 0;
+             
+             hash += tem_hash;
+           }
+         break;
+
+       case 's':
+         {
+           const unsigned char *p = (const unsigned char *) XSTR (x, i);
+           
+           if (p)
+             while (*p)
+               hash += *p++;
+           break;
+         }
+         
+       case 'i':
+         hash += XINT (x, i);
+         break;
 
-         if (p)
-           while (*p)
-             hash += *p++;
+       case '0':
+       case 't':
+         /* unused */
+         break;
+         
+       default:
+         gcc_unreachable ();
        }
-      else if (fmt[i] == 'i')
-       hash += XINT (x, i);
-      else if (fmt[i] == '0' || fmt[i] == 't')
-       /* unused */;
-      else
-       abort ();
     }
 
-  return hash ? hash : 1 + GET_CODE (x);
+  return hash ? hash : 1 + (unsigned int) GET_CODE (x);
 }
 
 /* Create a new value structure for VALUE and initialize it.  The mode of the
    value is MODE.  */
 
-static cselib_val *
-new_cselib_val (value, mode)
-     unsigned int value;
-     enum machine_mode mode;
+static inline cselib_val *
+new_cselib_val (unsigned int value, enum machine_mode mode)
 {
-  cselib_val *e = empty_vals;
+  cselib_val *e = pool_alloc (cselib_val_pool);
 
-  if (e)
-    empty_vals = e->u.next_free;
-  else
-    e = (cselib_val *) obstack_alloc (&cselib_obstack, sizeof (cselib_val));
-
-  if (value == 0)
-    abort ();
+  gcc_assert (value);
 
   e->value = value;
-  e->u.val_rtx = gen_rtx_VALUE (mode);
+  /* We use an alloc pool to allocate this RTL construct because it
+     accounts for about 8% of the overall memory usage.  We know
+     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->u.val_rtx = pool_alloc (value_pool);
+  memset (e->u.val_rtx, 0, RTX_HDR_SIZE);
+  PUT_CODE (e->u.val_rtx, VALUE);
+  PUT_MODE (e->u.val_rtx, mode);
   CSELIB_VAL_PTR (e->u.val_rtx) = e;
   e->addr_list = 0;
   e->locs = 0;
+  e->next_containing_mem = 0;
   return e;
 }
 
@@ -715,33 +764,32 @@ new_cselib_val (value, mode)
    value.  Update the two value structures to represent this situation.  */
 
 static void
-add_mem_for_addr (addr_elt, mem_elt, x)
-     cselib_val *addr_elt, *mem_elt;
-     rtx x;
+add_mem_for_addr (cselib_val *addr_elt, cselib_val *mem_elt, rtx x)
 {
-  rtx new;
   struct elt_loc_list *l;
 
   /* Avoid duplicates.  */
   for (l = mem_elt->locs; l; l = l->next)
-    if (GET_CODE (l->loc) == MEM
+    if (MEM_P (l->loc)
        && CSELIB_VAL_PTR (XEXP (l->loc, 0)) == addr_elt)
       return;
 
-  new = gen_rtx_MEM (GET_MODE (x), addr_elt->u.val_rtx);
-  MEM_COPY_ATTRIBUTES (new, x);
-
   addr_elt->addr_list = new_elt_list (addr_elt->addr_list, mem_elt);
-  mem_elt->locs = new_elt_loc_list (mem_elt->locs, new);
+  mem_elt->locs
+    = new_elt_loc_list (mem_elt->locs,
+                       replace_equiv_address_nv (x, addr_elt->u.val_rtx));
+  if (mem_elt->next_containing_mem == NULL)
+    {
+      mem_elt->next_containing_mem = first_containing_mem;
+      first_containing_mem = mem_elt;
+    }
 }
 
 /* Subroutine of cselib_lookup.  Return a value for X, which is a MEM rtx.
    If CREATE, make a new one if we haven't seen it before.  */
 
 static cselib_val *
-cselib_lookup_mem (x, create)
-     rtx x;
-     int create;
+cselib_lookup_mem (rtx x, int create)
 {
   enum machine_mode mode = GET_MODE (x);
   void **slot;
@@ -750,6 +798,7 @@ cselib_lookup_mem (x, create)
   struct elt_list *l;
 
   if (MEM_VOLATILE_P (x) || mode == BLKmode
+      || !cselib_record_memory
       || (FLOAT_MODE_P (mode) && flag_float_store))
     return 0;
 
@@ -768,7 +817,7 @@ cselib_lookup_mem (x, create)
 
   mem_elt = new_cselib_val (++next_unknown_value, mode);
   add_mem_for_addr (addr, mem_elt, x);
-  slot = htab_find_slot_with_hash (hash_table, wrap_constant (mode, x),
+  slot = htab_find_slot_with_hash (cselib_hash_table, wrap_constant (mode, x),
                                   mem_elt->value, INSERT);
   *slot = mem_elt;
   return mem_elt;
@@ -780,9 +829,8 @@ cselib_lookup_mem (x, create)
    X isn't actually modified; if modifications are needed, new rtl is
    allocated.  However, the return value can share rtl with X.  */
 
-static rtx
-cselib_subst_to_values (x)
-     rtx x;
+rtx
+cselib_subst_to_values (rtx x)
 {
   enum rtx_code code = GET_CODE (x);
   const char *fmt = GET_RTX_FORMAT (code);
@@ -794,24 +842,39 @@ cselib_subst_to_values (x)
   switch (code)
     {
     case REG:
-      for (l = REG_VALUES (REGNO (x)); l; l = l->next)
+      l = REG_VALUES (REGNO (x));
+      if (l && l->elt == NULL)
+       l = l->next;
+      for (; l; l = l->next)
        if (GET_MODE (l->elt->u.val_rtx) == GET_MODE (x))
          return l->elt->u.val_rtx;
 
-      abort ();
+      gcc_unreachable ();
 
     case MEM:
       e = cselib_lookup_mem (x, 0);
       if (! e)
-       abort ();
+       {
+         /* This happens for autoincrements.  Assign a value that doesn't
+            match any other.  */
+         e = new_cselib_val (++next_unknown_value, GET_MODE (x));
+       }
       return e->u.val_rtx;
 
-      /* CONST_DOUBLEs must be special-cased here so that we won't try to
-        look up the CONST_DOUBLE_MEM inside.  */
     case CONST_DOUBLE:
+    case CONST_VECTOR:
     case CONST_INT:
       return x;
 
+    case POST_INC:
+    case PRE_INC:
+    case POST_DEC:
+    case PRE_DEC:
+    case POST_MODIFY:
+    case PRE_MODIFY:
+      e = new_cselib_val (++next_unknown_value, GET_MODE (x));
+      return e->u.val_rtx;
+
     default:
       break;
     }
@@ -859,10 +922,7 @@ cselib_subst_to_values (x)
    (i.e. because it's a constant).  */
 
 cselib_val *
-cselib_lookup (x, mode, create)
-     rtx x;
-     enum machine_mode mode;
-     int create;
+cselib_lookup (rtx x, enum machine_mode mode, int create)
 {
   void **slot;
   cselib_val *e;
@@ -874,37 +934,54 @@ cselib_lookup (x, mode, create)
   if (GET_CODE (x) == VALUE)
     return CSELIB_VAL_PTR (x);
 
-  if (GET_CODE (x) == REG)
+  if (REG_P (x))
     {
       struct elt_list *l;
       unsigned int i = REGNO (x);
 
-      for (l = REG_VALUES (i); l; l = l->next)
+      l = REG_VALUES (i);
+      if (l && l->elt == NULL)
+       l = l->next;
+      for (; l; l = l->next)
        if (mode == GET_MODE (l->elt->u.val_rtx))
          return l->elt;
 
       if (! create)
        return 0;
 
+      if (i < FIRST_PSEUDO_REGISTER)
+       {
+         unsigned int n = hard_regno_nregs[i][mode];
+
+         if (n > max_value_regs)
+           max_value_regs = n;
+       }
+
       e = new_cselib_val (++next_unknown_value, GET_MODE (x));
       e->locs = new_elt_loc_list (e->locs, x);
       if (REG_VALUES (i) == 0)
-        VARRAY_PUSH_UINT (used_regs, i);
-      REG_VALUES (i) = new_elt_list (REG_VALUES (i), e);
-      slot = htab_find_slot_with_hash (hash_table, x, e->value, INSERT);
+       {
+         /* Maintain the invariant that the first entry of
+            REG_VALUES, if present, must be the value used to set the
+            register, or NULL.  */
+         used_regs[n_used_regs++] = i;
+         REG_VALUES (i) = new_elt_list (REG_VALUES (i), NULL);
+       }
+      REG_VALUES (i)->next = new_elt_list (REG_VALUES (i)->next, e);
+      slot = htab_find_slot_with_hash (cselib_hash_table, x, e->value, INSERT);
       *slot = e;
       return e;
     }
 
-  if (GET_CODE (x) == MEM)
+  if (MEM_P (x))
     return cselib_lookup_mem (x, create);
 
-  hashval = hash_rtx (x, mode, create);
+  hashval = cselib_hash_rtx (x, create);
   /* Can't even create if hashing is not possible.  */
   if (! hashval)
     return 0;
 
-  slot = htab_find_slot_with_hash (hash_table, wrap_constant (mode, x),
+  slot = htab_find_slot_with_hash (cselib_hash_table, wrap_constant (mode, x),
                                   hashval, create ? INSERT : NO_INSERT);
   if (slot == 0)
     return 0;
@@ -930,27 +1007,37 @@ cselib_lookup (x, mode, create)
    invalidating call clobbered registers across a call.  */
 
 static void
-cselib_invalidate_regno (regno, mode)
-     unsigned int regno;
-     enum machine_mode mode;
+cselib_invalidate_regno (unsigned int regno, enum machine_mode mode)
 {
   unsigned int endregno;
   unsigned int i;
 
   /* If we see pseudos after reload, something is _wrong_.  */
-  if (reload_completed && regno >= FIRST_PSEUDO_REGISTER
-      && reg_renumber[regno] >= 0)
-    abort ();
+  gcc_assert (!reload_completed || regno < FIRST_PSEUDO_REGISTER
+             || reg_renumber[regno] < 0);
 
   /* Determine the range of registers that must be invalidated.  For
      pseudos, only REGNO is affected.  For hard regs, we must take MODE
      into account, and we must also invalidate lower register numbers
      if they contain values that overlap REGNO.  */
-  endregno = regno + 1;
-  if (regno < FIRST_PSEUDO_REGISTER && mode != VOIDmode) 
-    endregno = regno + HARD_REGNO_NREGS (regno, mode);
+  if (regno < FIRST_PSEUDO_REGISTER)
+    {
+      gcc_assert (mode != VOIDmode);
+
+      if (regno < max_value_regs)
+       i = 0;
+      else
+       i = regno - max_value_regs;
 
-  for (i = 0; i < endregno; i++)
+      endregno = regno + hard_regno_nregs[regno][mode];
+    }
+  else
+    {
+      i = regno;
+      endregno = regno + 1;
+    }
+
+  for (; i < endregno; i++)
     {
       struct elt_list **l = &REG_VALUES (i);
 
@@ -962,17 +1049,28 @@ cselib_invalidate_regno (regno, mode)
          struct elt_loc_list **p;
          unsigned int this_last = i;
 
-         if (i < FIRST_PSEUDO_REGISTER)
-           this_last += HARD_REGNO_NREGS (i, GET_MODE (v->u.val_rtx)) - 1;
+         if (i < FIRST_PSEUDO_REGISTER && v != NULL)
+           this_last += hard_regno_nregs[i][GET_MODE (v->u.val_rtx)] - 1;
 
-         if (this_last < regno)
+         if (this_last < regno || v == NULL)
            {
              l = &(*l)->next;
              continue;
            }
 
          /* We have an overlap.  */
-         unchain_one_elt_list (l);
+         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);
 
          /* Now, we clear the mapping from value to reg.  It must exist, so
             this code will crash intentionally if it doesn't.  */
@@ -980,7 +1078,7 @@ cselib_invalidate_regno (regno, mode)
            {
              rtx x = (*p)->loc;
 
-             if (GET_CODE (x) == REG && REGNO (x) == i)
+             if (REG_P (x) && REGNO (x) == i)
                {
                  unchain_one_elt_loc_list (p);
                  break;
@@ -991,145 +1089,112 @@ cselib_invalidate_regno (regno, mode)
        }
     }
 }
-
-/* The memory at address MEM_BASE is being changed.
-   Return whether this change will invalidate VAL.  */
+\f
+/* Return 1 if X has a value that can vary even between two
+   executions of the program.  0 means X can be compared reliably
+   against certain constants or near-constants.  */
 
 static int
-cselib_mem_conflict_p (mem_base, val)
-     rtx mem_base;
-     rtx val;
+cselib_rtx_varies_p (rtx x ATTRIBUTE_UNUSED, int from_alias ATTRIBUTE_UNUSED)
 {
-  enum rtx_code code;
-  const char *fmt;
-  int i, j;
-
-  code = GET_CODE (val);
-  switch (code)
-    {
-      /* Get rid of a few simple cases quickly. */
-    case REG:
-    case PC:
-    case CC0:
-    case SCRATCH:
-    case CONST:
-    case CONST_INT:
-    case CONST_DOUBLE:
-    case SYMBOL_REF:
-    case LABEL_REF:
-      return 0;
-
-    case MEM:
-      if (GET_MODE (mem_base) == BLKmode
-         || GET_MODE (val) == BLKmode
-         || anti_dependence (val, mem_base))
-       return 1;
-
-      /* The address may contain nested MEMs.  */
-      break;
-
-    default:
-      break;
-    }
-
-  fmt = GET_RTX_FORMAT (code);
-  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
-    {
-      if (fmt[i] == 'e')
-       {
-         if (cselib_mem_conflict_p (mem_base, XEXP (val, i)))
-           return 1;
-       }
-      else if (fmt[i] == 'E')
-       for (j = 0; j < XVECLEN (val, i); j++)
-         if (cselib_mem_conflict_p (mem_base, XVECEXP (val, i, j)))
-           return 1;
-    }
-
+  /* We actually don't need to verify very hard.  This is because
+     if X has actually changed, we invalidate the memory anyway,
+     so assume that all common memory addresses are
+     invariant.  */
   return 0;
 }
 
-/* For the value found in SLOT, walk its locations to determine if any overlap
-   INFO (which is a MEM rtx).  */
+/* Invalidate any locations in the table which are changed because of a
+   store to MEM_RTX.  If this is called because of a non-const call
+   instruction, MEM_RTX is (mem:BLK const0_rtx).  */
 
-static int
-cselib_invalidate_mem_1 (slot, info)
-     void **slot;
-     void *info;
+static void
+cselib_invalidate_mem (rtx mem_rtx)
 {
-  cselib_val *v = (cselib_val *) *slot;
-  rtx mem_rtx = (rtx) info;
-  struct elt_loc_list **p = &v->locs;
-  int had_locs = v->locs != 0;
+  cselib_val **vp, *v, *next;
+  int num_mems = 0;
+  rtx mem_addr;
 
-  while (*p)
+  mem_addr = canon_rtx (get_addr (XEXP (mem_rtx, 0)));
+  mem_rtx = canon_rtx (mem_rtx);
+
+  vp = &first_containing_mem;
+  for (v = *vp; v != &dummy_val; v = next)
     {
-      rtx x = (*p)->loc;
-      cselib_val *addr;
-      struct elt_list **mem_chain;
-
-      /* MEMs may occur in locations only at the top level; below
-        that every MEM or REG is substituted by its VALUE.  */
-      if (GET_CODE (x) != MEM
-         || ! cselib_mem_conflict_p (mem_rtx, x))
-       {
-         p = &(*p)->next;
-         continue;
-       }
+      bool has_mem = false;
+      struct elt_loc_list **p = &v->locs;
+      int had_locs = v->locs != 0;
 
-      /* This one overlaps.  */
-      /* We must have a mapping from this MEM's address to the
-        value (E).  Remove that, too.  */
-      addr = cselib_lookup (XEXP (x, 0), VOIDmode, 0);
-      mem_chain = &addr->addr_list;
-      for (;;)
+      while (*p)
        {
-         if ((*mem_chain)->elt == v)
+         rtx x = (*p)->loc;
+         cselib_val *addr;
+         struct elt_list **mem_chain;
+
+         /* MEMs may occur in locations only at the top level; below
+            that every MEM or REG is substituted by its VALUE.  */
+         if (!MEM_P (x))
+           {
+             p = &(*p)->next;
+             continue;
+           }
+         if (num_mems < PARAM_VALUE (PARAM_MAX_CSELIB_MEMORY_LOCATIONS)
+             && ! canon_true_dependence (mem_rtx, GET_MODE (mem_rtx), mem_addr,
+                                         x, cselib_rtx_varies_p))
            {
-             unchain_one_elt_list (mem_chain);
-             break;
+             has_mem = true;
+             num_mems++;
+             p = &(*p)->next;
+             continue;
            }
 
-         mem_chain = &(*mem_chain)->next;
-       }
-
-      unchain_one_elt_loc_list (p);
-    }
+         /* This one overlaps.  */
+         /* We must have a mapping from this MEM's address to the
+            value (E).  Remove that, too.  */
+         addr = cselib_lookup (XEXP (x, 0), VOIDmode, 0);
+         mem_chain = &addr->addr_list;
+         for (;;)
+           {
+             if ((*mem_chain)->elt == v)
+               {
+                 unchain_one_elt_list (mem_chain);
+                 break;
+               }
 
-  if (had_locs && v->locs == 0)
-    n_useless_values++;
+             mem_chain = &(*mem_chain)->next;
+           }
 
-  return 1;
-}
+         unchain_one_elt_loc_list (p);
+       }
 
-/* Invalidate any locations in the table which are changed because of a
-   store to MEM_RTX.  If this is called because of a non-const call
-   instruction, MEM_RTX is (mem:BLK const0_rtx).  */
+      if (had_locs && v->locs == 0)
+       n_useless_values++;
 
-static void
-cselib_invalidate_mem (mem_rtx)
-     rtx mem_rtx;
-{
-  htab_traverse (hash_table, cselib_invalidate_mem_1, mem_rtx);
+      next = v->next_containing_mem;
+      if (has_mem)
+       {
+         *vp = v;
+         vp = &(*vp)->next_containing_mem;
+       }
+      else
+       v->next_containing_mem = NULL;
+    }
+  *vp = &dummy_val;
 }
 
-/* Invalidate DEST, which is being assigned to or clobbered.  The second and
-   the third parameter exist so that this function can be passed to
-   note_stores; they are ignored.  */
+/* Invalidate DEST, which is being assigned to or clobbered.  */
 
-static void
-cselib_invalidate_rtx (dest, ignore, data)
-     rtx dest;
-     rtx ignore ATTRIBUTE_UNUSED;
-     void *data ATTRIBUTE_UNUSED;
+void
+cselib_invalidate_rtx (rtx dest)
 {
-  while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SIGN_EXTRACT
-        || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SUBREG)
+  while (GET_CODE (dest) == SUBREG
+        || GET_CODE (dest) == ZERO_EXTRACT
+        || GET_CODE (dest) == STRICT_LOW_PART)
     dest = XEXP (dest, 0);
 
-  if (GET_CODE (dest) == REG)
+  if (REG_P (dest))
     cselib_invalidate_regno (REGNO (dest), GET_MODE (dest));
-  else if (GET_CODE (dest) == MEM)
+  else if (MEM_P (dest))
     cselib_invalidate_mem (dest);
 
   /* Some machines don't define AUTO_INC_DEC, but they still use push
@@ -1137,7 +1202,16 @@ cselib_invalidate_rtx (dest, ignore, data)
      invalidate the stack pointer correctly.  Note that invalidating
      the stack pointer is different from invalidating DEST.  */
   if (push_operand (dest, GET_MODE (dest)))
-    cselib_invalidate_rtx (stack_pointer_rtx, NULL_RTX, NULL);
+    cselib_invalidate_rtx (stack_pointer_rtx);
+}
+
+/* A wrapper for cselib_invalidate_rtx to be called via note_stores.  */
+
+static void
+cselib_invalidate_rtx_note_stores (rtx dest, rtx ignore ATTRIBUTE_UNUSED,
+                                  void *data ATTRIBUTE_UNUSED)
+{
+  cselib_invalidate_rtx (dest);
 }
 
 /* Record the result of a SET instruction.  DEST is being set; the source
@@ -1145,26 +1219,41 @@ cselib_invalidate_rtx (dest, ignore, data)
    describes its address.  */
 
 static void
-cselib_record_set (dest, src_elt, dest_addr_elt)
-     rtx dest;
-     cselib_val *src_elt, *dest_addr_elt;
+cselib_record_set (rtx dest, cselib_val *src_elt, cselib_val *dest_addr_elt)
 {
-  int dreg = GET_CODE (dest) == REG ? (int) REGNO (dest) : -1;
+  int dreg = REG_P (dest) ? (int) REGNO (dest) : -1;
 
   if (src_elt == 0 || side_effects_p (dest))
     return;
 
   if (dreg >= 0)
     {
+      if (dreg < FIRST_PSEUDO_REGISTER)
+       {
+         unsigned int n = hard_regno_nregs[dreg][GET_MODE (dest)];
+
+         if (n > max_value_regs)
+           max_value_regs = n;
+       }
+
       if (REG_VALUES (dreg) == 0)
-        VARRAY_PUSH_UINT (used_regs, dreg);
+       {
+         used_regs[n_used_regs++] = dreg;
+         REG_VALUES (dreg) = new_elt_list (REG_VALUES (dreg), src_elt);
+       }
+      else
+       {
+         /* The register should have been invalidated.  */
+         gcc_assert (REG_VALUES (dreg)->elt == 0);
+         REG_VALUES (dreg)->elt = src_elt;
+       }
 
-      REG_VALUES (dreg) = new_elt_list (REG_VALUES (dreg), src_elt);
       if (src_elt->locs == 0)
        n_useless_values--;
       src_elt->locs = new_elt_loc_list (src_elt->locs, dest);
     }
-  else if (GET_CODE (dest) == MEM && dest_addr_elt != 0)
+  else if (MEM_P (dest) && dest_addr_elt != 0
+          && cselib_record_memory)
     {
       if (src_elt->locs == 0)
        n_useless_values--;
@@ -1187,15 +1276,21 @@ struct set
 
 /* Record the effects of any sets in INSN.  */
 static void
-cselib_record_sets (insn)
-     rtx insn;
+cselib_record_sets (rtx insn)
 {
   int n_sets = 0;
   int i;
   struct set sets[MAX_SETS];
   rtx body = PATTERN (insn);
+  rtx cond = 0;
 
   body = PATTERN (insn);
+  if (GET_CODE (body) == COND_EXEC)
+    {
+      cond = COND_EXEC_TEST (body);
+      body = COND_EXEC_CODE (body);
+    }
+
   /* Find all sets.  */
   if (GET_CODE (body) == SET)
     {
@@ -1232,10 +1327,14 @@ cselib_record_sets (insn)
        sets[i].dest = dest = XEXP (dest, 0);
 
       /* We don't know how to record anything but REG or MEM.  */
-      if (GET_CODE (dest) == REG || GET_CODE (dest) == MEM)
+      if (REG_P (dest)
+         || (MEM_P (dest) && cselib_record_memory))
         {
-         sets[i].src_elt = cselib_lookup (sets[i].src, GET_MODE (dest), 1);
-         if (GET_CODE (dest) == MEM)
+         rtx src = sets[i].src;
+         if (cond)
+           src = gen_rtx_IF_THEN_ELSE (GET_MODE (src), cond, src, dest);
+         sets[i].src_elt = cselib_lookup (src, GET_MODE (dest), 1);
+         if (MEM_P (dest))
            sets[i].dest_addr_elt = cselib_lookup (XEXP (dest, 0), Pmode, 1);
          else
            sets[i].dest_addr_elt = 0;
@@ -1245,13 +1344,37 @@ cselib_record_sets (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, NULL);
+  note_stores (body, cselib_invalidate_rtx_note_stores, NULL);
+
+  /* If this is an asm, look for duplicate sets.  This can happen when the
+     user uses the same value as an output multiple times.  This is valid
+     if the outputs are not actually used thereafter.  Treat this case as
+     if the value isn't actually set.  We do this by smashing the destination
+     to pc_rtx, so that we won't record the value later.  */
+  if (n_sets >= 2 && asm_noperands (body) >= 0)
+    {
+      for (i = 0; i < n_sets; i++)
+       {
+         rtx dest = sets[i].dest;
+         if (REG_P (dest) || MEM_P (dest))
+           {
+             int j;
+             for (j = i + 1; j < n_sets; j++)
+               if (rtx_equal_p (dest, sets[j].dest))
+                 {
+                   sets[i].dest = pc_rtx;
+                   sets[j].dest = pc_rtx;
+                 }
+           }
+       }
+    }
 
   /* Now enter the equivalences in our tables.  */
   for (i = 0; i < n_sets; i++)
     {
       rtx dest = sets[i].dest;
-      if (GET_CODE (dest) == REG || GET_CODE (dest) == MEM)
+      if (REG_P (dest)
+         || (MEM_P (dest) && cselib_record_memory))
        cselib_record_set (dest, sets[i].src_elt, sets[i].dest_addr_elt);
     }
 }
@@ -1259,28 +1382,33 @@ cselib_record_sets (insn)
 /* Record the effects of INSN.  */
 
 void
-cselib_process_insn (insn)
-     rtx insn;
+cselib_process_insn (rtx insn)
 {
   int i;
   rtx x;
 
+  if (find_reg_note (insn, REG_LIBCALL, NULL))
+    cselib_current_insn_in_libcall = true;
   cselib_current_insn = insn;
 
   /* Forget everything at a CODE_LABEL, a volatile asm, or a setjmp.  */
-  if (GET_CODE (insn) == CODE_LABEL
-      || (GET_CODE (insn) == NOTE
-         && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)
-      || (GET_CODE (insn) == INSN
+  if (LABEL_P (insn)
+      || (CALL_P (insn)
+         && find_reg_note (insn, REG_SETJMP, NULL))
+      || (NONJUMP_INSN_P (insn)
          && GET_CODE (PATTERN (insn)) == ASM_OPERANDS
          && MEM_VOLATILE_P (PATTERN (insn))))
     {
-      clear_table (0);
+      if (find_reg_note (insn, REG_RETVAL, NULL))
+        cselib_current_insn_in_libcall = false;
+      cselib_clear_table ();
       return;
     }
 
   if (! INSN_P (insn))
     {
+      if (find_reg_note (insn, REG_RETVAL, NULL))
+        cselib_current_insn_in_libcall = false;
       cselib_current_insn = 0;
       return;
     }
@@ -1288,13 +1416,16 @@ cselib_process_insn (insn)
   /* If this is a call instruction, forget anything stored in a
      call clobbered register, or, if this is not a const call, in
      memory.  */
-  if (GET_CODE (insn) == CALL_INSN)
+  if (CALL_P (insn))
     {
       for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-       if (call_used_regs[i])
-         cselib_invalidate_regno (i, VOIDmode);
+       if (call_used_regs[i]
+           || (REG_VALUES (i) && REG_VALUES (i)->elt
+               && HARD_REGNO_CALL_PART_CLOBBERED (i, 
+                     GET_MODE (REG_VALUES (i)->elt->u.val_rtx))))
+         cselib_invalidate_regno (i, reg_raw_mode[i]);
 
-      if (! CONST_CALL_P (insn))
+      if (! CONST_OR_PURE_CALL_P (insn))
        cselib_invalidate_mem (callmem);
     }
 
@@ -1306,68 +1437,85 @@ cselib_process_insn (insn)
      unlikely to help.  */
   for (x = REG_NOTES (insn); x; x = XEXP (x, 1))
     if (REG_NOTE_KIND (x) == REG_INC)
-      cselib_invalidate_rtx (XEXP (x, 0), NULL_RTX, NULL);
+      cselib_invalidate_rtx (XEXP (x, 0));
 #endif
 
   /* Look for any CLOBBERs in CALL_INSN_FUNCTION_USAGE, but only
      after we have processed the insn.  */
-  if (GET_CODE (insn) == CALL_INSN)
+  if (CALL_P (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), NULL_RTX, NULL);
+       cselib_invalidate_rtx (XEXP (XEXP (x, 0), 0));
 
+  if (find_reg_note (insn, REG_RETVAL, NULL))
+    cselib_current_insn_in_libcall = false;
   cselib_current_insn = 0;
 
-  if (n_useless_values > MAX_USELESS_VALUES)
+  if (n_useless_values > MAX_USELESS_VALUES
+      /* remove_useless_values is linear in the hash table size.  Avoid
+         quadratic behaviour for very large hashtables with very few
+        useless elements.  */
+      && (unsigned int)n_useless_values > cselib_hash_table->n_elements / 4)
     remove_useless_values ();
 }
 
-/* Make sure our varrays are big enough.  Not called from any cselib routines;
-   it must be called by the user if it allocated new registers.  */
-
-void
-cselib_update_varray_sizes ()
-{
-  unsigned int nregs = max_reg_num ();
-
-  if (nregs == cselib_nregs)
-    return;
-
-  cselib_nregs = nregs;
-  VARRAY_GROW (reg_values, nregs);
-  VARRAY_GROW (used_regs, nregs);
-}
-
 /* Initialize cselib for one pass.  The caller must also call
    init_alias_analysis.  */
 
 void
-cselib_init ()
+cselib_init (bool record_memory)
 {
-  /* These are only created once.  */
+  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_memory;
+
+  /* (mem:BLK (scratch)) is a special mechanism to conflict with everything,
+     see canon_true_dependence.  This is only created once.  */
   if (! callmem)
-    {
-      gcc_obstack_init (&cselib_obstack);
-      cselib_startobj = obstack_alloc (&cselib_obstack, 0);
-
-      callmem = gen_rtx_MEM (BLKmode, const0_rtx);
-      ggc_add_rtx_root (&callmem, 1);
-    }
+    callmem = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode));
 
   cselib_nregs = max_reg_num ();
-  VARRAY_ELT_LIST_INIT (reg_values, cselib_nregs, "reg_values");
-  VARRAY_UINT_INIT (used_regs, cselib_nregs, "used_regs");
-  hash_table = htab_create (31, get_value_hash, entry_and_rtx_equal_p, NULL);
-  clear_table (1);
+
+  /* We preserve reg_values to allow expensive clearing of the whole thing.
+     Reallocate it however if it happens to be too large.  */
+  if (!reg_values || reg_values_size < cselib_nregs
+      || (reg_values_size > 10 && reg_values_size > cselib_nregs * 4))
+    {
+      if (reg_values)
+       free (reg_values);
+      /* Some space for newly emit instructions so we don't end up
+        reallocating in between passes.  */
+      reg_values_size = cselib_nregs + (63 + cselib_nregs) / 16;
+      reg_values = XCNEWVEC (struct elt_list *, reg_values_size);
+    }
+  used_regs = XNEWVEC (unsigned int, cselib_nregs);
+  n_used_regs = 0;
+  cselib_hash_table = htab_create (31, get_value_hash,
+                                  entry_and_rtx_equal_p, NULL);
+  cselib_current_insn_in_libcall = false;
 }
 
 /* Called when the current user is done with cselib.  */
 
 void
-cselib_finish ()
+cselib_finish (void)
 {
-  clear_table (0);
-  VARRAY_FREE (reg_values);
-  VARRAY_FREE (used_regs);
-  htab_delete (hash_table);
+  free_alloc_pool (elt_list_pool);
+  free_alloc_pool (elt_loc_list_pool);
+  free_alloc_pool (cselib_val_pool);
+  free_alloc_pool (value_pool);
+  cselib_clear_table ();
+  htab_delete (cselib_hash_table);
+  free (used_regs);
+  used_regs = 0;
+  cselib_hash_table = 0;
+  n_useless_values = 0;
+  next_unknown_value = 0;
 }
+
+#include "gt-cselib.h"