OSDN Git Service

Fix for aliasing problem reported by Michael Matz.
[pf3gnuchains/gcc-fork.git] / gcc / cselib.c
index 10f17e2..8cb325d 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 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, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
 
 #include "config.h"
 #include "system.h"
-#include <setjmp.h>
+#include "coretypes.h"
+#include "tm.h"
 
 #include "rtl.h"
 #include "tm_p.h"
@@ -36,40 +37,31 @@ Boston, MA 02111-1307, USA.  */
 #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));
+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 void clear_table (void);
+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 hash_rtx (rtx, enum machine_mode, 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 int cselib_mem_conflict_p (rtx, rtx);
+static void cselib_invalidate_mem (rtx);
+static void cselib_invalidate_rtx (rtx, rtx, void *);
+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 +73,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 GTY((param_is (cselib_val))) htab_t 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 +93,61 @@ 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;
+/* 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 GTY(()) varray_type reg_values;
+static GTY((deletable (""))) varray_type reg_values_old;
 #define REG_VALUES(I) VARRAY_ELT_LIST (reg_values, (I))
 
+/* The largest number of hard regs used by any entry added to the
+   REG_VALUES table.  Cleared on each 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;
+static GTY(()) varray_type used_regs;
+static GTY((deletable (""))) varray_type used_regs_old;
 
 /* 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;
+static GTY(()) rtx callmem;
 
 /* 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((deletable (""))) cselib_val *empty_vals;
+static GTY((deletable (""))) struct elt_list *empty_elt_lists;
+static GTY((deletable (""))) struct elt_loc_list *empty_elt_loc_lists;
 
 /* 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;
 \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;
+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));
+    el = ggc_alloc (sizeof (struct elt_list));
   el->next = next;
   el->elt = elt;
   return el;
@@ -154,20 +157,18 @@ new_elt_list (next, elt)
    arguments.  */
 
 static struct elt_loc_list *
-new_elt_loc_list (next, loc)
-     struct elt_loc_list *next;
-     rtx loc;
+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));
+    el = ggc_alloc (sizeof (struct elt_loc_list));
   el->next = next;
   el->loc = loc;
   el->setting_insn = cselib_current_insn;
+  el->in_libcall = cselib_current_insn_in_libcall;
   return el;
 }
 
@@ -175,8 +176,7 @@ new_elt_loc_list (next, loc)
    storage.  */
 
 static void
-unchain_one_elt_list (pl)
-     struct elt_list **pl;
+unchain_one_elt_list (struct elt_list **pl)
 {
   struct elt_list *l = *pl;
 
@@ -188,8 +188,7 @@ unchain_one_elt_list (pl)
 /* 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;
 
@@ -202,8 +201,7 @@ unchain_one_elt_loc_list (pl)
    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);
@@ -217,29 +215,24 @@ unchain_one_value (v)
    which are known to have been used.  */
 
 static void
-clear_table (clear_all)
-     int clear_all;
+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 < VARRAY_ACTIVE_SIZE (used_regs); i++)
+    REG_VALUES (VARRAY_UINT (used_regs, i)) = 0;
+
+  max_value_regs = 0;
 
   VARRAY_POP_ALL (used_regs);
 
   htab_empty (hash_table);
-  obstack_free (&cselib_obstack, cselib_startobj);
 
-  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,8 +241,7 @@ 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;
@@ -267,7 +259,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)
@@ -281,9 +273,8 @@ entry_and_rtx_equal_p (entry, x_arg)
    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 +286,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 +314,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,9 +339,7 @@ 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;
 
@@ -372,8 +357,9 @@ 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
@@ -386,21 +372,47 @@ remove_useless_values ()
   /* Second pass: actually remove the values.  */
   htab_traverse (hash_table, discard_useless_values, 0);
 
+  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;
+
   if (n_useless_values != 0)
     abort ();
 }
 
+/* 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 (GET_CODE (x) != REG)
+    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)
     {
       cselib_val *e = cselib_lookup (x, GET_MODE (x), 0);
@@ -438,7 +450,7 @@ rtx_equal_for_cselib_p (x, y)
          else if (rtx_equal_for_cselib_p (t, y))
            return 1;
        }
-      
+
       return 0;
     }
 
@@ -456,7 +468,7 @@ rtx_equal_for_cselib_p (x, y)
          else if (rtx_equal_for_cselib_p (x, t))
            return 1;
        }
-      
+
       return 0;
     }
 
@@ -466,7 +478,7 @@ rtx_equal_for_cselib_p (x, y)
   /* This won't be handled correctly by the code below.  */
   if (GET_CODE (x) == LABEL_REF)
     return XEXP (x, 0) == XEXP (y, 0);
-  
+
   code = GET_CODE (x);
   fmt = GET_RTX_FORMAT (code);
 
@@ -533,9 +545,7 @@ 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))
@@ -555,10 +565,7 @@ wrap_constant (mode, x)
    otherwise the mode of X is used.  */
 
 static unsigned int
-hash_rtx (x, mode, create)
-     rtx x;
-     enum machine_mode mode;
-     int create;
+hash_rtx (rtx x, enum machine_mode mode, int create)
 {
   cselib_val *e;
   int i, j;
@@ -566,8 +573,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,8 +584,7 @@ hash_rtx (x, mode, create)
       if (! e)
        return 0;
 
-      hash += e->value;
-      return hash ? hash : (unsigned int) MEM;
+      return e->value;
 
     case CONST_INT:
       hash += ((unsigned) CONST_INT << 7) + (unsigned) mode + INTVAL (x);
@@ -591,13 +595,28 @@ hash_rtx (x, mode, create)
         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 : (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 += hash_rtx (elt, GET_MODE (elt), 0);
+         }
+
+       return hash;
+      }
+
       /* Assume there is only one rtx object for any given label.  */
     case LABEL_REF:
       hash
@@ -626,7 +645,7 @@ hash_rtx (x, mode, create)
        return 0;
 
       break;
-      
+
     default:
       break;
     }
@@ -638,18 +657,8 @@ hash_rtx (x, mode, create)
       if (fmt[i] == 'e')
        {
          rtx tem = XEXP (x, i);
-         unsigned int tem_hash;
+         unsigned int tem_hash = hash_rtx (tem, 0, create);
 
-         /* 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;
 
@@ -688,16 +697,14 @@ hash_rtx (x, mode, create)
    value is MODE.  */
 
 static cselib_val *
-new_cselib_val (value, mode)
-     unsigned int value;
-     enum machine_mode mode;
+new_cselib_val (unsigned int value, enum machine_mode mode)
 {
   cselib_val *e = empty_vals;
 
   if (e)
     empty_vals = e->u.next_free;
   else
-    e = (cselib_val *) obstack_alloc (&cselib_obstack, sizeof (cselib_val));
+    e = ggc_alloc (sizeof (cselib_val));
 
   if (value == 0)
     abort ();
@@ -707,6 +714,7 @@ new_cselib_val (value, mode)
   CSELIB_VAL_PTR (e->u.val_rtx) = e;
   e->addr_list = 0;
   e->locs = 0;
+  e->next_containing_mem = 0;
   return e;
 }
 
@@ -715,11 +723,8 @@ 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.  */
@@ -728,20 +733,22 @@ add_mem_for_addr (addr_elt, mem_elt, x)
        && 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;
@@ -780,9 +787,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,7 +800,10 @@ 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;
 
@@ -803,15 +812,27 @@ cselib_subst_to_values (x)
     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 +880,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;
@@ -879,18 +897,35 @@ cselib_lookup (x, mode, create)
       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);
+       {
+         /* Maintain the invariant that the first entry of
+            REG_VALUES, if present, must be the value used to set the
+            register, or NULL.  */
+         VARRAY_PUSH_UINT (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 (hash_table, x, e->value, INSERT);
       *slot = e;
       return e;
@@ -930,9 +965,7 @@ 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;
@@ -946,11 +979,25 @@ cselib_invalidate_regno (regno, mode)
      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)
+    {
+      if (mode == VOIDmode)
+       abort ();
 
-  for (i = 0; i < endregno; i++)
+      if (regno < max_value_regs)
+       i = 0;
+      else
+       i = regno - max_value_regs;
+
+      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 +1009,28 @@ cselib_invalidate_regno (regno, mode)
          struct elt_loc_list **p;
          unsigned int this_last = i;
 
-         if (i < FIRST_PSEUDO_REGISTER)
+         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.  */
@@ -996,9 +1054,7 @@ cselib_invalidate_regno (regno, mode)
    Return whether this change will invalidate VAL.  */
 
 static int
-cselib_mem_conflict_p (mem_base, val)
-     rtx mem_base;
-     rtx val;
+cselib_mem_conflict_p (rtx mem_base, rtx val)
 {
   enum rtx_code code;
   const char *fmt;
@@ -1007,7 +1063,7 @@ cselib_mem_conflict_p (mem_base, val)
   code = GET_CODE (val);
   switch (code)
     {
-      /* Get rid of a few simple cases quickly. */
+      /* Get rid of a few simple cases quickly.  */
     case REG:
     case PC:
     case CC0:
@@ -1015,6 +1071,7 @@ cselib_mem_conflict_p (mem_base, val)
     case CONST:
     case CONST_INT:
     case CONST_DOUBLE:
+    case CONST_VECTOR:
     case SYMBOL_REF:
     case LABEL_REF:
       return 0;
@@ -1049,68 +1106,74 @@ cselib_mem_conflict_p (mem_base, val)
   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;
 
-  while (*p)
+  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 (GET_CODE (x) != MEM)
+           {
+             p = &(*p)->next;
+             continue;
+           }
+         if (! cselib_mem_conflict_p (mem_rtx, x))
            {
-             unchain_one_elt_list (mem_chain);
-             break;
+             has_mem = true;
+             p = &(*p)->next;
+             continue;
            }
 
-         mem_chain = &(*mem_chain)->next;
-       }
+         /* 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;
+               }
 
-      unchain_one_elt_loc_list (p);
-    }
+             mem_chain = &(*mem_chain)->next;
+           }
 
-  if (had_locs && v->locs == 0)
-    n_useless_values++;
+         unchain_one_elt_loc_list (p);
+       }
 
-  return 1;
-}
+      if (had_locs && v->locs == 0)
+       n_useless_values++;
 
-/* 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 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
@@ -1118,10 +1181,8 @@ cselib_invalidate_mem (mem_rtx)
    note_stores; they are ignored.  */
 
 static void
-cselib_invalidate_rtx (dest, ignore, data)
-     rtx dest;
-     rtx ignore ATTRIBUTE_UNUSED;
-     void *data ATTRIBUTE_UNUSED;
+cselib_invalidate_rtx (rtx dest, rtx ignore ATTRIBUTE_UNUSED,
+                      void *data ATTRIBUTE_UNUSED)
 {
   while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SIGN_EXTRACT
         || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SUBREG)
@@ -1145,9 +1206,7 @@ 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;
 
@@ -1156,10 +1215,28 @@ cselib_record_set (dest, src_elt, dest_addr_elt)
 
   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);
+       {
+         VARRAY_PUSH_UINT (used_regs, dreg);
+         REG_VALUES (dreg) = new_elt_list (REG_VALUES (dreg), src_elt);
+       }
+      else
+       {
+         if (REG_VALUES (dreg)->elt == 0)
+           REG_VALUES (dreg)->elt = src_elt;
+         else
+           /* The register should have been invalidated.  */
+           abort ();
+       }
 
-      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);
@@ -1187,15 +1264,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)
     {
@@ -1234,7 +1317,10 @@ cselib_record_sets (insn)
       /* We don't know how to record anything but REG or MEM.  */
       if (GET_CODE (dest) == REG || GET_CODE (dest) == MEM)
         {
-         sets[i].src_elt = cselib_lookup (sets[i].src, GET_MODE (dest), 1);
+         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 (GET_CODE (dest) == MEM)
            sets[i].dest_addr_elt = cselib_lookup (XEXP (dest, 0), Pmode, 1);
          else
@@ -1259,23 +1345,26 @@ 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;
+  if (find_reg_note (insn, REG_RETVAL, NULL))
+    cselib_current_insn_in_libcall = false;
   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) == CALL_INSN
+         && find_reg_note (insn, REG_SETJMP, NULL))
       || (GET_CODE (insn) == INSN
          && GET_CODE (PATTERN (insn)) == ASM_OPERANDS
          && MEM_VOLATILE_P (PATTERN (insn))))
     {
-      clear_table (0);
+      clear_table ();
       return;
     }
 
@@ -1292,9 +1381,9 @@ cselib_process_insn (insn)
     {
       for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
        if (call_used_regs[i])
-         cselib_invalidate_regno (i, VOIDmode);
+         cselib_invalidate_regno (i, reg_raw_mode[i]);
 
-      if (! CONST_CALL_P (insn))
+      if (! CONST_OR_PURE_CALL_P (insn))
        cselib_invalidate_mem (callmem);
     }
 
@@ -1326,7 +1415,7 @@ cselib_process_insn (insn)
    it must be called by the user if it allocated new registers.  */
 
 void
-cselib_update_varray_sizes ()
+cselib_update_varray_sizes (void)
 {
   unsigned int nregs = max_reg_num ();
 
@@ -1342,32 +1431,41 @@ cselib_update_varray_sizes ()
    init_alias_analysis.  */
 
 void
-cselib_init ()
+cselib_init (void)
 {
-  /* These are only created once.  */
+  /* 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, const0_rtx);
 
   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);
+  if (reg_values_old != NULL && VARRAY_SIZE (reg_values_old) >= cselib_nregs)
+    {
+      reg_values = reg_values_old;
+      used_regs = used_regs_old;
+    }
+  else
+    {
+      VARRAY_ELT_LIST_INIT (reg_values, cselib_nregs, "reg_values");
+      VARRAY_UINT_INIT (used_regs, cselib_nregs, "used_regs");
+    }
+  hash_table = htab_create_ggc (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);
+  clear_table ();
+  reg_values_old = reg_values;
+  reg_values = 0;
+  used_regs_old = used_regs;
+  used_regs = 0;
+  hash_table = 0;
+  n_useless_values = 0;
+  next_unknown_value = 0;
 }
+
+#include "gt-cselib.h"