OSDN Git Service

* matrix-reorg.c (analyze_matrix_allocation_site): Remove unused
[pf3gnuchains/gcc-fork.git] / gcc / cselib.c
index 54af8e1..66d92a0 100644 (file)
@@ -1,12 +1,13 @@
 /* Common subexpression elimination library for GNU compiler.
    Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+   Free Software Foundation, Inc.
 
 This file is part of GCC.
 
 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
+Software Foundation; either version 3, or (at your option) any later
 version.
 
 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
@@ -15,9 +16,8 @@ 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 GCC; see the file COPYING.  If not, write to the Free
-Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA.  */
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
 #include "system.h"
@@ -38,6 +38,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 #include "output.h"
 #include "ggc.h"
 #include "hashtab.h"
+#include "tree-pass.h"
 #include "cselib.h"
 #include "params.h"
 #include "alloc-pool.h"
@@ -54,9 +55,8 @@ 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 cselib_val *new_cselib_val (unsigned int, enum 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);
@@ -64,6 +64,15 @@ static void cselib_invalidate_mem (rtx);
 static void cselib_record_set (rtx, cselib_val *, cselib_val *);
 static void cselib_record_sets (rtx);
 
+struct expand_value_data
+{
+  bitmap regs_active;
+  cselib_expand_callback callback;
+  void *callback_arg;
+};
+
+static rtx cselib_expand_value_rtx_1 (rtx, struct expand_value_data *, int);
+
 /* 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
    - for a MEM, we recursively look up its address and then follow the
@@ -79,7 +88,6 @@ 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;
@@ -135,6 +143,20 @@ static alloc_pool elt_loc_list_pool, elt_list_pool, cselib_val_pool, value_pool;
 /* If nonnull, cselib will call this function before freeing useless
    VALUEs.  A VALUE is deemed useless if its "locs" field is null.  */
 void (*cselib_discard_hook) (cselib_val *);
+
+/* If nonnull, cselib will call this function before recording sets or
+   even clobbering outputs of INSN.  All the recorded sets will be
+   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,
+                                int n_sets);
+
+#define PRESERVED_VALUE_P(RTX) \
+  (RTL_FLAG_CHECK1("PRESERVED_VALUE_P", (RTX), VALUE)->unchanging)
+#define LONG_TERM_PRESERVED_VALUE_P(RTX) \
+  (RTL_FLAG_CHECK1("LONG_TERM_PRESERVED_VALUE_P", (RTX), VALUE)->in_struct)
+
 \f
 
 /* Allocate a struct elt_list and fill in its two elements with the
@@ -144,7 +166,7 @@ static inline struct elt_list *
 new_elt_list (struct elt_list *next, cselib_val *elt)
 {
   struct elt_list *el;
-  el = pool_alloc (elt_list_pool);
+  el = (struct elt_list *) pool_alloc (elt_list_pool);
   el->next = next;
   el->elt = elt;
   return el;
@@ -157,11 +179,10 @@ static inline struct elt_loc_list *
 new_elt_loc_list (struct elt_loc_list *next, rtx loc)
 {
   struct elt_loc_list *el;
-  el = pool_alloc (elt_loc_list_pool);
+  el = (struct elt_loc_list *) 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;
 }
 
@@ -201,12 +222,20 @@ unchain_one_value (cselib_val *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.  */
+   initialization.  */
 
 void
 cselib_clear_table (void)
 {
+  cselib_reset_table_with_next_value (0);
+}
+
+/* Remove all entries from the hash table, arranging for the next
+   value to be numbered NUM.  */
+
+void
+cselib_reset_table_with_next_value (unsigned int num)
+{
   unsigned int i;
 
   for (i = 0; i < n_used_regs; i++)
@@ -216,15 +245,24 @@ cselib_clear_table (void)
 
   n_used_regs = 0;
 
+  /* ??? Preserve constants?  */
   htab_empty (cselib_hash_table);
 
   n_useless_values = 0;
 
-  next_unknown_value = 0;
+  next_unknown_value = num;
 
   first_containing_mem = &dummy_val;
 }
 
+/* Return the number of the next value that will be generated.  */
+
+unsigned int
+cselib_get_next_unknown_value (void)
+{
+  return next_unknown_value;
+}
+
 /* The equality test for our hash table.  The first argument ENTRY is a table
    element (i.e. a cselib_val), while the second arg X is an rtx.  We know
    that all callers of htab_find_slot_with_hash will wrap CONST_INTs into a
@@ -235,18 +273,19 @@ entry_and_rtx_equal_p (const void *entry, const void *x_arg)
 {
   struct elt_loc_list *l;
   const cselib_val *const v = (const cselib_val *) entry;
-  rtx x = (rtx) x_arg;
+  rtx x = CONST_CAST_RTX ((const_rtx)x_arg);
   enum machine_mode mode = GET_MODE (x);
 
-  gcc_assert (GET_CODE (x) != CONST_INT
+  gcc_assert (!CONST_INT_P (x) && GET_CODE (x) != CONST_FIXED
              && (mode != VOIDmode || GET_CODE (x) != CONST_DOUBLE));
-  
+
   if (mode != GET_MODE (v->val_rtx))
     return 0;
 
   /* Unwrap X if necessary.  */
   if (GET_CODE (x) == CONST
-      && (GET_CODE (XEXP (x, 0)) == CONST_INT
+      && (CONST_INT_P (XEXP (x, 0))
+         || GET_CODE (XEXP (x, 0)) == CONST_FIXED
          || GET_CODE (XEXP (x, 0)) == CONST_DOUBLE))
     x = XEXP (x, 0);
 
@@ -266,7 +305,7 @@ entry_and_rtx_equal_p (const void *entry, const void *x_arg)
 static hashval_t
 get_value_hash (const void *entry)
 {
-  const cselib_val *v = (const cselib_val *) entry;
+  const cselib_val *const v = (const cselib_val *) entry;
   return v->value;
 }
 
@@ -276,9 +315,9 @@ get_value_hash (const void *entry)
    removed.  */
 
 int
-references_value_p (rtx x, int only_useless)
+references_value_p (const_rtx x, int only_useless)
 {
-  enum rtx_code code = GET_CODE (x);
+  const enum rtx_code code = GET_CODE (x);
   const char *fmt = GET_RTX_FORMAT (code);
   int i, j;
 
@@ -318,7 +357,7 @@ discard_useless_locs (void **x, void *info ATTRIBUTE_UNUSED)
        p = &(*p)->next;
     }
 
-  if (had_locs && v->locs == 0)
+  if (had_locs && v->locs == 0 && !PRESERVED_VALUE_P (v->val_rtx))
     {
       n_useless_values++;
       values_became_useless = 1;
@@ -333,7 +372,7 @@ discard_useless_values (void **x, void *info ATTRIBUTE_UNUSED)
 {
   cselib_val *v = (cselib_val *)*x;
 
-  if (v->locs == 0)
+  if (v->locs == 0 && !PRESERVED_VALUE_P (v->val_rtx))
     {
       if (cselib_discard_hook)
        cselib_discard_hook (v);
@@ -379,13 +418,85 @@ remove_useless_values (void)
   gcc_assert (!n_useless_values);
 }
 
+/* Arrange for a value to not be removed from the hash table even if
+   it becomes useless.  */
+
+void
+cselib_preserve_value (cselib_val *v)
+{
+  PRESERVED_VALUE_P (v->val_rtx) = 1;
+}
+
+/* Test whether a value is preserved.  */
+
+bool
+cselib_preserved_value_p (cselib_val *v)
+{
+  return PRESERVED_VALUE_P (v->val_rtx);
+}
+
+/* Mark preserved values as preserved for the long term.  */
+
+static int
+cselib_preserve_definitely (void **slot, void *info ATTRIBUTE_UNUSED)
+{
+  cselib_val *v = (cselib_val *)*slot;
+
+  if (PRESERVED_VALUE_P (v->val_rtx)
+      && !LONG_TERM_PRESERVED_VALUE_P (v->val_rtx))
+    LONG_TERM_PRESERVED_VALUE_P (v->val_rtx) = true;
+
+  return 1;
+}
+
+/* Clear the preserve marks for values not preserved for the long
+   term.  */
+
+static int
+cselib_clear_preserve (void **slot, void *info ATTRIBUTE_UNUSED)
+{
+  cselib_val *v = (cselib_val *)*slot;
+
+  if (PRESERVED_VALUE_P (v->val_rtx)
+      && !LONG_TERM_PRESERVED_VALUE_P (v->val_rtx))
+    {
+      PRESERVED_VALUE_P (v->val_rtx) = false;
+      if (!v->locs)
+       n_useless_values++;
+    }
+
+  return 1;
+}
+
+/* Clean all non-constant expressions in the hash table, but retain
+   their values.  */
+
+void
+cselib_preserve_only_values (bool retain)
+{
+  int i;
+
+  htab_traverse (cselib_hash_table,
+                retain ? cselib_preserve_definitely : cselib_clear_preserve,
+                NULL);
+
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    cselib_invalidate_regno (i, reg_raw_mode[i]);
+
+  cselib_invalidate_mem (callmem);
+
+  remove_useless_values ();
+
+  gcc_assert (first_containing_mem == &dummy_val);
+}
+
 /* 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)
+cselib_reg_set_mode (const_rtx x)
 {
   if (!REG_P (x))
     return GET_MODE (x);
@@ -473,6 +584,8 @@ rtx_equal_for_cselib_p (rtx x, rtx y)
   switch (GET_CODE (x))
     {
     case CONST_DOUBLE:
+    case CONST_FIXED:
+    case DEBUG_EXPR:
       return 0;
 
     case LABEL_REF:
@@ -555,7 +668,7 @@ rtx_equal_for_cselib_p (rtx x, rtx y)
 static rtx
 wrap_constant (enum machine_mode mode, rtx x)
 {
-  if (GET_CODE (x) != CONST_INT
+  if (!CONST_INT_P (x) && GET_CODE (x) != CONST_FIXED
       && (GET_CODE (x) != CONST_DOUBLE || GET_MODE (x) != VOIDmode))
     return x;
   gcc_assert (mode != VOIDmode);
@@ -604,6 +717,11 @@ cselib_hash_rtx (rtx x, int create)
 
       return e->value;
 
+    case DEBUG_EXPR:
+      hash += ((unsigned) DEBUG_EXPR << 7)
+             + DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (x));
+      return hash ? hash : (unsigned int) DEBUG_EXPR;
+
     case CONST_INT:
       hash += ((unsigned) CONST_INT << 7) + INTVAL (x);
       return hash ? hash : (unsigned int) CONST_INT;
@@ -619,6 +737,11 @@ cselib_hash_rtx (rtx x, int create)
                 + (unsigned) CONST_DOUBLE_HIGH (x));
       return hash ? hash : (unsigned int) CONST_DOUBLE;
 
+    case CONST_FIXED:
+      hash += (unsigned int) code + (unsigned int) GET_MODE (x);
+      hash += fixed_hash (CONST_FIXED_VALUE (x));
+      return hash ? hash : (unsigned int) CONST_FIXED;
+
     case CONST_VECTOR:
       {
        int units;
@@ -692,10 +815,10 @@ cselib_hash_rtx (rtx x, int create)
          {
            rtx tem = XEXP (x, i);
            unsigned int tem_hash = cselib_hash_rtx (tem, create);
-           
+
            if (tem_hash == 0)
              return 0;
-           
+
            hash += tem_hash;
          }
          break;
@@ -704,10 +827,10 @@ cselib_hash_rtx (rtx x, int create)
            {
              unsigned int tem_hash
                = cselib_hash_rtx (XVECEXP (x, i, j), create);
-             
+
              if (tem_hash == 0)
                return 0;
-             
+
              hash += tem_hash;
            }
          break;
@@ -715,13 +838,13 @@ cselib_hash_rtx (rtx x, int create)
        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;
@@ -730,7 +853,7 @@ cselib_hash_rtx (rtx x, int create)
        case 't':
          /* unused */
          break;
-         
+
        default:
          gcc_unreachable ();
        }
@@ -743,9 +866,9 @@ cselib_hash_rtx (rtx x, int create)
    value is MODE.  */
 
 static inline cselib_val *
-new_cselib_val (unsigned int value, enum machine_mode mode)
+new_cselib_val (unsigned int value, enum machine_mode mode, rtx x)
 {
-  cselib_val *e = pool_alloc (cselib_val_pool);
+  cselib_val *e = (cselib_val *) pool_alloc (cselib_val_pool);
 
   gcc_assert (value);
 
@@ -755,7 +878,7 @@ new_cselib_val (unsigned int value, enum machine_mode mode)
      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 = pool_alloc (value_pool);
+  e->val_rtx = (rtx) pool_alloc (value_pool);
   memset (e->val_rtx, 0, RTX_HDR_SIZE);
   PUT_CODE (e->val_rtx, VALUE);
   PUT_MODE (e->val_rtx, mode);
@@ -763,6 +886,18 @@ new_cselib_val (unsigned int value, enum machine_mode mode)
   e->addr_list = 0;
   e->locs = 0;
   e->next_containing_mem = 0;
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "cselib value %u ", value);
+      if (flag_dump_noaddr || flag_dump_unnumbered)
+       fputs ("# ", dump_file);
+      else
+       fprintf (dump_file, "%p ", (void*)e);
+      print_rtl_single (dump_file, x);
+      fputc ('\n', dump_file);
+    }
+
   return e;
 }
 
@@ -822,7 +957,7 @@ cselib_lookup_mem (rtx x, int create)
   if (! create)
     return 0;
 
-  mem_elt = new_cselib_val (++next_unknown_value, mode);
+  mem_elt = new_cselib_val (++next_unknown_value, mode, x);
   add_mem_for_addr (addr, mem_elt, x);
   slot = htab_find_slot_with_hash (cselib_hash_table, wrap_constant (mode, x),
                                   mem_elt->value, INSERT);
@@ -836,8 +971,9 @@ cselib_lookup_mem (rtx x, int create)
    non-reg results, we just take the first one because they will all
    expand to the same place.  */
 
-static rtx 
-expand_loc (struct elt_loc_list *p, bitmap regs_active, int max_depth)
+static rtx
+expand_loc (struct elt_loc_list *p, struct expand_value_data *evd,
+           int max_depth)
 {
   rtx reg_result = NULL;
   unsigned int regno = UINT_MAX;
@@ -847,52 +983,58 @@ expand_loc (struct elt_loc_list *p, bitmap regs_active, int max_depth)
     {
       /* Avoid infinite recursion trying to expand a reg into a
         the same reg.  */
-      if ((REG_P (p->loc)) 
-         && (REGNO (p->loc) < regno) 
-         && !bitmap_bit_p (regs_active, REGNO (p->loc)))
+      if ((REG_P (p->loc))
+         && (REGNO (p->loc) < regno)
+         && !bitmap_bit_p (evd->regs_active, REGNO (p->loc)))
        {
          reg_result = p->loc;
          regno = REGNO (p->loc);
        }
       /* Avoid infinite recursion and do not try to expand the
         value.  */
-      else if (GET_CODE (p->loc) == VALUE 
+      else if (GET_CODE (p->loc) == VALUE
               && CSELIB_VAL_PTR (p->loc)->locs == p_in)
        continue;
       else if (!REG_P (p->loc))
        {
-         rtx result;
-         if (dump_file)
+         rtx result, note;
+         if (dump_file && (dump_flags & TDF_DETAILS))
            {
              print_inline_rtx (dump_file, p->loc, 0);
              fprintf (dump_file, "\n");
            }
-         result = cselib_expand_value_rtx (p->loc, regs_active, max_depth - 1);
+         if (GET_CODE (p->loc) == LO_SUM
+             && GET_CODE (XEXP (p->loc, 1)) == SYMBOL_REF
+             && p->setting_insn
+             && (note = find_reg_note (p->setting_insn, REG_EQUAL, NULL_RTX))
+             && XEXP (note, 0) == XEXP (p->loc, 1))
+           return XEXP (p->loc, 1);
+         result = cselib_expand_value_rtx_1 (p->loc, evd, max_depth - 1);
          if (result)
            return result;
        }
-       
+
     }
-  
+
   if (regno != UINT_MAX)
     {
       rtx result;
-      if (dump_file)
+      if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file, "r%d\n", regno);
 
-      result = cselib_expand_value_rtx (reg_result, regs_active, max_depth - 1);
+      result = cselib_expand_value_rtx_1 (reg_result, evd, max_depth - 1);
       if (result)
        return result;
     }
 
-  if (dump_file)
+  if (dump_file && (dump_flags & TDF_DETAILS))
     {
       if (reg_result)
        {
          print_inline_rtx (dump_file, reg_result, 0);
          fprintf (dump_file, "\n");
        }
-      else 
+      else
        fprintf (dump_file, "NULL\n");
     }
   return reg_result;
@@ -903,7 +1045,7 @@ expand_loc (struct elt_loc_list *p, bitmap regs_active, int max_depth)
    This is the opposite of common subexpression.  Because local value
    numbering is such a weak optimization, the expanded expression is
    pretty much unique (not from a pointer equals point of view but
-   from a tree shape point of view.  
+   from a tree shape point of view.
 
    This function returns NULL if the expansion fails.  The expansion
    will fail if there is no value number for one of the operands or if
@@ -920,10 +1062,46 @@ expand_loc (struct elt_loc_list *p, bitmap regs_active, int max_depth)
 rtx
 cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth)
 {
+  struct expand_value_data evd;
+
+  evd.regs_active = regs_active;
+  evd.callback = NULL;
+  evd.callback_arg = NULL;
+
+  return cselib_expand_value_rtx_1 (orig, &evd, max_depth);
+}
+
+/* Same as cselib_expand_value_rtx, but using a callback to try to
+   resolve some expressions.  The CB function should return ORIG if it
+   can't or does not want to deal with a certain RTX.  Any other
+   return value, including NULL, will be used as the expansion for
+   VALUE, without any further changes.  */
+
+rtx
+cselib_expand_value_rtx_cb (rtx orig, bitmap regs_active, int max_depth,
+                           cselib_expand_callback cb, void *data)
+{
+  struct expand_value_data evd;
+
+  evd.regs_active = regs_active;
+  evd.callback = cb;
+  evd.callback_arg = data;
+
+  return cselib_expand_value_rtx_1 (orig, &evd, max_depth);
+}
+
+/* Internal implementation of cselib_expand_value_rtx and
+   cselib_expand_value_rtx_cb.  */
+
+static rtx
+cselib_expand_value_rtx_1 (rtx orig, struct expand_value_data *evd,
+                          int max_depth)
+{
   rtx copy, scopy;
   int i, j;
   RTX_CODE code;
   const char *format_ptr;
+  enum machine_mode mode;
 
   code = GET_CODE (orig);
 
@@ -946,7 +1124,7 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth)
            {
              rtx result;
              int regno = REGNO (orig);
-             
+
              /* The only thing that we are not willing to do (this
                 is requirement of dse and if others potential uses
                 need this function we should add a parm to control
@@ -954,7 +1132,7 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth)
                 STACK_POINTER_REGNUM, FRAME_POINTER or the
                 HARD_FRAME_POINTER.
 
-                Thses expansions confuses the code that notices that
+                These expansions confuses the code that notices that
                 stores into the frame go dead at the end of the
                 function and that the frame is not effected by calls
                 to subroutines.  If you allow the
@@ -968,21 +1146,21 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth)
                  || regno == HARD_FRAME_POINTER_REGNUM)
                return orig;
 
-             bitmap_set_bit (regs_active, regno);
+             bitmap_set_bit (evd->regs_active, regno);
 
-             if (dump_file)
+             if (dump_file && (dump_flags & TDF_DETAILS))
                fprintf (dump_file, "expanding: r%d into: ", regno);
 
-             result = expand_loc (l->elt->locs, regs_active, max_depth);
-             bitmap_clear_bit (regs_active, regno);
+             result = expand_loc (l->elt->locs, evd, max_depth);
+             bitmap_clear_bit (evd->regs_active, regno);
 
              if (result)
                return result;
-             else 
+             else
                return orig;
            }
       }
-      
+
     case CONST_INT:
     case CONST_DOUBLE:
     case CONST_VECTOR:
@@ -1003,25 +1181,64 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth)
        return orig;
       break;
 
+    case SUBREG:
+      {
+       rtx subreg;
+
+       if (evd->callback)
+         {
+           subreg = evd->callback (orig, evd->regs_active, max_depth,
+                                   evd->callback_arg);
+           if (subreg != orig)
+             return subreg;
+         }
+
+       subreg = cselib_expand_value_rtx_1 (SUBREG_REG (orig), evd,
+                                           max_depth - 1);
+       if (!subreg)
+         return NULL;
+       scopy = simplify_gen_subreg (GET_MODE (orig), subreg,
+                                    GET_MODE (SUBREG_REG (orig)),
+                                    SUBREG_BYTE (orig));
+       if (scopy == NULL
+           || (GET_CODE (scopy) == SUBREG
+               && !REG_P (SUBREG_REG (scopy))
+               && !MEM_P (SUBREG_REG (scopy))))
+         return NULL;
+
+       return scopy;
+      }
 
     case VALUE:
       {
        rtx result;
-       if (dump_file)
-         fprintf (dump_file, "expanding value %s into: ", GET_MODE_NAME (GET_MODE (orig)));
-       
-       result = expand_loc (CSELIB_VAL_PTR (orig)->locs, regs_active, max_depth);
-       if (result 
-           && GET_CODE (result) == CONST_INT
-           && GET_MODE (orig) != VOIDmode)
+
+       if (dump_file && (dump_flags & TDF_DETAILS))
+         {
+           fputs ("\nexpanding ", dump_file);
+           print_rtl_single (dump_file, orig);
+           fputs (" into...", dump_file);
+         }
+
+       if (evd->callback)
          {
-           result = gen_rtx_CONST (GET_MODE (orig), result);
-           if (dump_file)
-             fprintf (dump_file, "  wrapping const_int result in const to preserve mode %s\n", 
-                      GET_MODE_NAME (GET_MODE (orig)));
+           result = evd->callback (orig, evd->regs_active, max_depth,
+                                   evd->callback_arg);
+
+           if (result != orig)
+             return result;
          }
+
+       result = expand_loc (CSELIB_VAL_PTR (orig)->locs, evd, max_depth);
        return result;
       }
+
+    case DEBUG_EXPR:
+      if (evd->callback)
+       return evd->callback (orig, evd->regs_active, max_depth,
+                             evd->callback_arg);
+      return orig;
+
     default:
       break;
     }
@@ -1032,15 +1249,16 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth)
      us to explicitly document why we are *not* copying a flag.  */
   copy = shallow_copy_rtx (orig);
 
-  format_ptr = GET_RTX_FORMAT (GET_CODE (copy));
+  format_ptr = GET_RTX_FORMAT (code);
 
-  for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++)
+  for (i = 0; i < GET_RTX_LENGTH (code); i++)
     switch (*format_ptr++)
       {
       case 'e':
        if (XEXP (orig, i) != NULL)
          {
-           rtx result = cselib_expand_value_rtx (XEXP (orig, i), regs_active, max_depth - 1);
+           rtx result = cselib_expand_value_rtx_1 (XEXP (orig, i), evd,
+                                                   max_depth - 1);
            if (!result)
              return NULL;
            XEXP (copy, i) = result;
@@ -1054,7 +1272,8 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth)
            XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i));
            for (j = 0; j < XVECLEN (copy, i); j++)
              {
-               rtx result = cselib_expand_value_rtx (XVECEXP (orig, i, j), regs_active, max_depth - 1);
+               rtx result = cselib_expand_value_rtx_1 (XVECEXP (orig, i, j),
+                                                       evd, max_depth - 1);
                if (!result)
                  return NULL;
                XVECEXP (copy, i, j) = result;
@@ -1078,6 +1297,62 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth)
        gcc_unreachable ();
       }
 
+  mode = GET_MODE (copy);
+  /* If an operand has been simplified into CONST_INT, which doesn't
+     have a mode and the mode isn't derivable from whole rtx's mode,
+     try simplify_*_operation first with mode from original's operand
+     and as a fallback wrap CONST_INT into gen_rtx_CONST.  */
+  scopy = copy;
+  switch (GET_RTX_CLASS (code))
+    {
+    case RTX_UNARY:
+      if (CONST_INT_P (XEXP (copy, 0))
+         && GET_MODE (XEXP (orig, 0)) != VOIDmode)
+       {
+         scopy = simplify_unary_operation (code, mode, XEXP (copy, 0),
+                                           GET_MODE (XEXP (orig, 0)));
+         if (scopy)
+           return scopy;
+       }
+      break;
+    case RTX_COMM_ARITH:
+    case RTX_BIN_ARITH:
+      /* These expressions can derive operand modes from the whole rtx's mode.  */
+      break;
+    case RTX_TERNARY:
+    case RTX_BITFIELD_OPS:
+      if (CONST_INT_P (XEXP (copy, 0))
+         && GET_MODE (XEXP (orig, 0)) != VOIDmode)
+       {
+         scopy = simplify_ternary_operation (code, mode,
+                                             GET_MODE (XEXP (orig, 0)),
+                                             XEXP (copy, 0), XEXP (copy, 1),
+                                             XEXP (copy, 2));
+         if (scopy)
+           return scopy;
+       }
+      break;
+    case RTX_COMPARE:
+    case RTX_COMM_COMPARE:
+      if (CONST_INT_P (XEXP (copy, 0))
+         && GET_MODE (XEXP (copy, 1)) == VOIDmode
+         && (GET_MODE (XEXP (orig, 0)) != VOIDmode
+             || GET_MODE (XEXP (orig, 1)) != VOIDmode))
+       {
+         scopy = simplify_relational_operation (code, mode,
+                                                (GET_MODE (XEXP (orig, 0))
+                                                 != VOIDmode)
+                                                ? GET_MODE (XEXP (orig, 0))
+                                                : GET_MODE (XEXP (orig, 1)),
+                                                XEXP (copy, 0),
+                                                XEXP (copy, 1));
+         if (scopy)
+           return scopy;
+       }
+      break;
+    default:
+      break;
+    }
   scopy = simplify_rtx (copy);
   if (scopy)
     return scopy;
@@ -1118,13 +1393,14 @@ cselib_subst_to_values (rtx x)
        {
          /* This happens for autoincrements.  Assign a value that doesn't
             match any other.  */
-         e = new_cselib_val (++next_unknown_value, GET_MODE (x));
+         e = new_cselib_val (++next_unknown_value, GET_MODE (x), x);
        }
       return e->val_rtx;
 
     case CONST_DOUBLE:
     case CONST_VECTOR:
     case CONST_INT:
+    case CONST_FIXED:
       return x;
 
     case POST_INC:
@@ -1133,7 +1409,7 @@ cselib_subst_to_values (rtx x)
     case PRE_DEC:
     case POST_MODIFY:
     case PRE_MODIFY:
-      e = new_cselib_val (++next_unknown_value, GET_MODE (x));
+      e = new_cselib_val (++next_unknown_value, GET_MODE (x), x);
       return e->val_rtx;
 
     default:
@@ -1146,30 +1422,31 @@ cselib_subst_to_values (rtx x)
        {
          rtx t = cselib_subst_to_values (XEXP (x, i));
 
-         if (t != XEXP (x, i) && x == copy)
-           copy = shallow_copy_rtx (x);
-
-         XEXP (copy, i) = t;
+         if (t != XEXP (x, i))
+           {
+             if (x == copy)
+               copy = shallow_copy_rtx (x);
+             XEXP (copy, i) = t;
+           }
        }
       else if (fmt[i] == 'E')
        {
-         int j, k;
+         int j;
 
          for (j = 0; j < XVECLEN (x, i); j++)
            {
              rtx t = cselib_subst_to_values (XVECEXP (x, i, j));
 
-             if (t != XVECEXP (x, i, j) && XVEC (x, i) == XVEC (copy, i))
+             if (t != XVECEXP (x, i, j))
                {
-                 if (x == copy)
-                   copy = shallow_copy_rtx (x);
-
-                 XVEC (copy, i) = rtvec_alloc (XVECLEN (x, i));
-                 for (k = 0; k < j; k++)
-                   XVECEXP (copy, i, k) = XVECEXP (x, i, k);
+                 if (XVEC (x, i) == XVEC (copy, i))
+                   {
+                     if (x == copy)
+                       copy = shallow_copy_rtx (x);
+                     XVEC (copy, i) = shallow_copy_rtvec (XVEC (x, i));
+                   }
+                 XVECEXP (copy, i, j) = t;
                }
-
-             XVECEXP (copy, i, j) = t;
            }
        }
     }
@@ -1177,6 +1454,21 @@ cselib_subst_to_values (rtx x)
   return copy;
 }
 
+/* Log a lookup of X to the cselib table along with the result RET.  */
+
+static cselib_val *
+cselib_log_lookup (rtx x, cselib_val *ret)
+{
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fputs ("cselib lookup ", dump_file);
+      print_inline_rtx (dump_file, x, 2);
+      fprintf (dump_file, " => %u\n", ret ? ret->value : 0);
+    }
+
+  return ret;
+}
+
 /* Look up the rtl expression X in our tables and return the value it has.
    If CREATE is zero, we return NULL if we don't know the value.  Otherwise,
    we create a new one if possible, using mode MODE if X doesn't have a mode
@@ -1205,10 +1497,10 @@ cselib_lookup (rtx x, enum machine_mode mode, int create)
        l = l->next;
       for (; l; l = l->next)
        if (mode == GET_MODE (l->elt->val_rtx))
-         return l->elt;
+         return cselib_log_lookup (x, l->elt);
 
       if (! create)
-       return 0;
+       return cselib_log_lookup (x, 0);
 
       if (i < FIRST_PSEUDO_REGISTER)
        {
@@ -1218,7 +1510,7 @@ cselib_lookup (rtx x, enum machine_mode mode, int create)
            max_value_regs = n;
        }
 
-      e = new_cselib_val (++next_unknown_value, GET_MODE (x));
+      e = new_cselib_val (++next_unknown_value, GET_MODE (x), x);
       e->locs = new_elt_loc_list (e->locs, x);
       if (REG_VALUES (i) == 0)
        {
@@ -1231,34 +1523,34 @@ cselib_lookup (rtx x, enum machine_mode mode, int create)
       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;
+      return cselib_log_lookup (x, e);
     }
 
   if (MEM_P (x))
-    return cselib_lookup_mem (x, create);
+    return cselib_log_lookup (x, cselib_lookup_mem (x, create));
 
   hashval = cselib_hash_rtx (x, create);
   /* Can't even create if hashing is not possible.  */
   if (! hashval)
-    return 0;
+    return cselib_log_lookup (x, 0);
 
   slot = htab_find_slot_with_hash (cselib_hash_table, wrap_constant (mode, x),
                                   hashval, create ? INSERT : NO_INSERT);
   if (slot == 0)
-    return 0;
+    return cselib_log_lookup (x, 0);
 
   e = (cselib_val *) *slot;
   if (e)
-    return e;
+    return cselib_log_lookup (x, e);
 
-  e = new_cselib_val (hashval, mode);
+  e = new_cselib_val (hashval, mode, x);
 
   /* We have to fill the slot before calling cselib_subst_to_values:
      the hash table is inconsistent until we do so, and
      cselib_subst_to_values will need to do lookups.  */
   *slot = (void *) e;
   e->locs = new_elt_loc_list (e->locs, cselib_subst_to_values (x));
-  return e;
+  return cselib_log_lookup (x, e);
 }
 
 /* Invalidate any entries in reg_values that overlap REGNO.  This is called
@@ -1345,7 +1637,7 @@ cselib_invalidate_regno (unsigned int regno, enum machine_mode mode)
                  break;
                }
            }
-         if (v->locs == 0)
+         if (v->locs == 0 && !PRESERVED_VALUE_P (v->val_rtx))
            n_useless_values++;
        }
     }
@@ -1355,8 +1647,8 @@ cselib_invalidate_regno (unsigned int regno, enum machine_mode mode)
    executions of the program.  0 means X can be compared reliably
    against certain constants or near-constants.  */
 
-static int
-cselib_rtx_varies_p (rtx x ATTRIBUTE_UNUSED, int from_alias ATTRIBUTE_UNUSED)
+static bool
+cselib_rtx_varies_p (const_rtx x ATTRIBUTE_UNUSED, bool from_alias ATTRIBUTE_UNUSED)
 {
   /* We actually don't need to verify very hard.  This is because
      if X has actually changed, we invalidate the memory anyway,
@@ -1401,7 +1693,7 @@ cselib_invalidate_mem (rtx mem_rtx)
            }
          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))
+                                         x, NULL_RTX, cselib_rtx_varies_p))
            {
              has_mem = true;
              num_mems++;
@@ -1428,7 +1720,7 @@ cselib_invalidate_mem (rtx mem_rtx)
          unchain_one_elt_loc_list (p);
        }
 
-      if (had_locs && v->locs == 0)
+      if (had_locs && v->locs == 0 && !PRESERVED_VALUE_P (v->val_rtx))
        n_useless_values++;
 
       next = v->next_containing_mem;
@@ -1469,7 +1761,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, rtx ignore ATTRIBUTE_UNUSED,
+cselib_invalidate_rtx_note_stores (rtx dest, const_rtx ignore ATTRIBUTE_UNUSED,
                                   void *data ATTRIBUTE_UNUSED)
 {
   cselib_invalidate_rtx (dest);
@@ -1509,28 +1801,19 @@ cselib_record_set (rtx dest, cselib_val *src_elt, cselib_val *dest_addr_elt)
          REG_VALUES (dreg)->elt = src_elt;
        }
 
-      if (src_elt->locs == 0)
+      if (src_elt->locs == 0 && !PRESERVED_VALUE_P (src_elt->val_rtx))
        n_useless_values--;
       src_elt->locs = new_elt_loc_list (src_elt->locs, dest);
     }
   else if (MEM_P (dest) && dest_addr_elt != 0
           && cselib_record_memory)
     {
-      if (src_elt->locs == 0)
+      if (src_elt->locs == 0 && !PRESERVED_VALUE_P (src_elt->val_rtx))
        n_useless_values--;
       add_mem_for_addr (dest_addr_elt, src_elt, dest);
     }
 }
 
-/* Describe a single set that is part of an insn.  */
-struct set
-{
-  rtx src;
-  rtx dest;
-  cselib_val *src_elt;
-  cselib_val *dest_addr_elt;
-};
-
 /* There is no good way to determine how many elements there can be
    in a PARALLEL.  Since it's fairly cheap, use a really large number.  */
 #define MAX_SETS (FIRST_PSEUDO_REGISTER * 2)
@@ -1541,7 +1824,7 @@ cselib_record_sets (rtx insn)
 {
   int n_sets = 0;
   int i;
-  struct set sets[MAX_SETS];
+  struct cselib_set sets[MAX_SETS];
   rtx body = PATTERN (insn);
   rtx cond = 0;
 
@@ -1576,6 +1859,17 @@ cselib_record_sets (rtx insn)
        }
     }
 
+  if (n_sets == 1
+      && MEM_P (sets[0].src)
+      && !cselib_record_memory
+      && MEM_READONLY_P (sets[0].src))
+    {
+      rtx note = find_reg_equal_equiv_note (insn);
+
+      if (note && CONSTANT_P (XEXP (note, 0)))
+       sets[0].src = XEXP (note, 0);
+    }
+
   /* Look up the values that are read.  Do this before invalidating the
      locations that are written.  */
   for (i = 0; i < n_sets; i++)
@@ -1593,15 +1887,24 @@ cselib_record_sets (rtx insn)
         {
          rtx src = sets[i].src;
          if (cond)
-           src = gen_rtx_IF_THEN_ELSE (GET_MODE (src), cond, src, dest);
+           src = gen_rtx_IF_THEN_ELSE (GET_MODE (dest), 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);
+           {
+             enum machine_mode address_mode
+               = targetm.addr_space.address_mode (MEM_ADDR_SPACE (dest));
+
+             sets[i].dest_addr_elt = cselib_lookup (XEXP (dest, 0),
+                                                    address_mode, 1);
+           }
          else
            sets[i].dest_addr_elt = 0;
        }
     }
 
+  if (cselib_record_sets_hook)
+    cselib_record_sets_hook (insn, sets, n_sets);
+
   /* 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.  */
@@ -1648,8 +1951,6 @@ 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.  */
@@ -1660,16 +1961,12 @@ cselib_process_insn (rtx insn)
          && GET_CODE (PATTERN (insn)) == ASM_OPERANDS
          && MEM_VOLATILE_P (PATTERN (insn))))
     {
-      if (find_reg_note (insn, REG_RETVAL, NULL))
-        cselib_current_insn_in_libcall = false;
-      cselib_clear_table ();
+      cselib_reset_table_with_next_value (next_unknown_value);
       return;
     }
 
   if (! INSN_P (insn))
     {
-      if (find_reg_note (insn, REG_RETVAL, NULL))
-        cselib_current_insn_in_libcall = false;
       cselib_current_insn = 0;
       return;
     }
@@ -1682,11 +1979,15 @@ cselib_process_insn (rtx 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, 
+               && HARD_REGNO_CALL_PART_CLOBBERED (i,
                      GET_MODE (REG_VALUES (i)->elt->val_rtx))))
          cselib_invalidate_regno (i, reg_raw_mode[i]);
 
-      if (! CONST_OR_PURE_CALL_P (insn))
+      /* Since it is not clear how cselib is going to be used, be
+        conservative here and treat looping pure or const functions
+        as if they were regular functions.  */
+      if (RTL_LOOPING_CONST_OR_PURE_CALL_P (insn)
+         || !(RTL_CONST_OR_PURE_CALL_P (insn)))
        cselib_invalidate_mem (callmem);
     }
 
@@ -1708,8 +2009,6 @@ cselib_process_insn (rtx insn)
       if (GET_CODE (XEXP (x, 0)) == CLOBBER)
        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
@@ -1726,11 +2025,11 @@ cselib_process_insn (rtx insn)
 void
 cselib_init (bool record_memory)
 {
-  elt_list_pool = create_alloc_pool ("elt_list", 
+  elt_list_pool = create_alloc_pool ("elt_list",
                                     sizeof (struct elt_list), 10);
-  elt_loc_list_pool = create_alloc_pool ("elt_loc_list", 
+  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", 
+  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;
@@ -1758,7 +2057,6 @@ cselib_init (bool record_memory)
   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.  */
@@ -1780,4 +2078,92 @@ cselib_finish (void)
   next_unknown_value = 0;
 }
 
+/* Dump the cselib_val *X to FILE *info.  */
+
+static int
+dump_cselib_val (void **x, void *info)
+{
+  cselib_val *v = (cselib_val *)*x;
+  FILE *out = (FILE *)info;
+  bool need_lf = true;
+
+  print_inline_rtx (out, v->val_rtx, 0);
+
+  if (v->locs)
+    {
+      struct elt_loc_list *l = v->locs;
+      if (need_lf)
+       {
+         fputc ('\n', out);
+         need_lf = false;
+       }
+      fputs (" locs:", out);
+      do
+       {
+         fprintf (out, "\n  from insn %i ",
+                  INSN_UID (l->setting_insn));
+         print_inline_rtx (out, l->loc, 4);
+       }
+      while ((l = l->next));
+      fputc ('\n', out);
+    }
+  else
+    {
+      fputs (" no locs", out);
+      need_lf = true;
+    }
+
+  if (v->addr_list)
+    {
+      struct elt_list *e = v->addr_list;
+      if (need_lf)
+       {
+         fputc ('\n', out);
+         need_lf = false;
+       }
+      fputs (" addr list:", out);
+      do
+       {
+         fputs ("\n  ", out);
+         print_inline_rtx (out, e->elt->val_rtx, 2);
+       }
+      while ((e = e->next));
+      fputc ('\n', out);
+    }
+  else
+    {
+      fputs (" no addrs", out);
+      need_lf = true;
+    }
+
+  if (v->next_containing_mem == &dummy_val)
+    fputs (" last mem\n", out);
+  else if (v->next_containing_mem)
+    {
+      fputs (" next mem ", out);
+      print_inline_rtx (out, v->next_containing_mem->val_rtx, 2);
+      fputc ('\n', out);
+    }
+  else if (need_lf)
+    fputc ('\n', out);
+
+  return 1;
+}
+
+/* Dump to OUT everything in the CSELIB table.  */
+
+void
+dump_cselib_table (FILE *out)
+{
+  fprintf (out, "cselib hash table:\n");
+  htab_traverse (cselib_hash_table, dump_cselib_val, out);
+  if (first_containing_mem != &dummy_val)
+    {
+      fputs ("first mem ", out);
+      print_inline_rtx (out, first_containing_mem->val_rtx, 2);
+      fputc ('\n', out);
+    }
+  fprintf (out, "last unknown value %i\n", next_unknown_value);
+}
+
 #include "gt-cselib.h"