OSDN Git Service

Do not FAIL in miscompiled runtime tests.
[pf3gnuchains/gcc-fork.git] / gcc / var-tracking.c
index 61ce32b..7fa7574 100644 (file)
@@ -1,5 +1,5 @@
 /* Variable tracking routines for the GNU compiler.
-   Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009
+   Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010
    Free Software Foundation, Inc.
 
    This file is part of GCC.
 #include "expr.h"
 #include "timevar.h"
 #include "tree-pass.h"
+#include "tree-flow.h"
 #include "cselib.h"
 #include "target.h"
 
+/* var-tracking.c assumes that tree code with the same value as VALUE rtx code
+   has no chance to appear in REG_EXPR/MEM_EXPRs and isn't a decl.
+   Currently the value is the same as IDENTIFIER_NODE, which has such
+   a property.  If this compile time assertion ever fails, make sure that
+   the new tree code that equals (int) VALUE has the same property.  */
+extern char check_value_val[(int) VALUE == (int) IDENTIFIER_NODE ? 1 : -1];
+
 /* Type of micro operation.  */
 enum micro_operation_type
 {
@@ -337,9 +345,6 @@ typedef struct value_chain_def
 } *value_chain;
 typedef const struct value_chain_def *const_value_chain;
 
-/* Hash function for DECL for VARIABLE_HTAB.  */
-#define VARIABLE_HASH_VAL(decl) (DECL_UID (decl))
-
 /* Pointer to the BB's information specific to variable tracking pass.  */
 #define VTI(BB) ((variable_tracking_info) (BB)->aux)
 
@@ -407,12 +412,12 @@ static int vars_copy_1 (void **, void *);
 static void vars_copy (htab_t, htab_t);
 static tree var_debug_decl (tree);
 static void var_reg_set (dataflow_set *, rtx, enum var_init_status, rtx);
-static void var_reg_delete_and_set (dataflow_set *, rtx, bool, 
+static void var_reg_delete_and_set (dataflow_set *, rtx, bool,
                                    enum var_init_status, rtx);
 static void var_reg_delete (dataflow_set *, rtx, bool);
 static void var_regno_delete (dataflow_set *, int);
 static void var_mem_set (dataflow_set *, rtx, enum var_init_status, rtx);
-static void var_mem_delete_and_set (dataflow_set *, rtx, bool, 
+static void var_mem_delete_and_set (dataflow_set *, rtx, bool,
                                    enum var_init_status, rtx);
 static void var_mem_delete (dataflow_set *, rtx, bool);
 
@@ -446,8 +451,8 @@ static bool compute_bb_dataflow (basic_block);
 static void vt_find_locations (void);
 
 static void dump_attrs_list (attrs);
-static int dump_variable_slot (void **, void *);
-static void dump_variable (variable);
+static int dump_var_slot (void **, void *);
+static void dump_var (variable);
 static void dump_vars (htab_t);
 static void dump_dataflow_set (dataflow_set *);
 static void dump_dataflow_sets (void);
@@ -521,7 +526,7 @@ stack_adjust_offset_pre_post (rtx pattern, HOST_WIDE_INT *pre,
              /* We handle only adjustments by constant amount.  */
              gcc_assert (GET_CODE (XEXP (src, 1)) == PLUS &&
                          CONST_INT_P (val));
-             
+
              if (code == PRE_MODIFY)
                *pre -= INTVAL (val);
              else
@@ -722,26 +727,7 @@ adjust_stack_reference (rtx mem, HOST_WIDE_INT adjustment)
 static inline bool
 dv_is_decl_p (decl_or_value dv)
 {
-  if (!dv)
-    return true;
-
-  /* Make sure relevant codes don't overlap.  */
-  switch ((int)TREE_CODE ((tree)dv))
-    {
-    case (int)VAR_DECL:
-    case (int)PARM_DECL:
-    case (int)RESULT_DECL:
-    case (int)FUNCTION_DECL:
-    case (int)DEBUG_EXPR_DECL:
-    case (int)COMPONENT_REF:
-      return true;
-
-    case (int)VALUE:
-      return false;
-
-    default:
-      gcc_unreachable ();
-    }
+  return !dv || (int) TREE_CODE ((tree) dv) != (int) VALUE;
 }
 
 /* Return true if a decl_or_value is a VALUE rtl.  */
@@ -755,7 +741,9 @@ dv_is_value_p (decl_or_value dv)
 static inline tree
 dv_as_decl (decl_or_value dv)
 {
+#ifdef ENABLE_CHECKING
   gcc_assert (dv_is_decl_p (dv));
+#endif
   return (tree) dv;
 }
 
@@ -763,7 +751,9 @@ dv_as_decl (decl_or_value dv)
 static inline rtx
 dv_as_value (decl_or_value dv)
 {
+#ifdef ENABLE_CHECKING
   gcc_assert (dv_is_value_p (dv));
+#endif
   return (rtx)dv;
 }
 
@@ -809,7 +799,9 @@ dv_from_decl (tree decl)
 {
   decl_or_value dv;
   dv = decl;
+#ifdef ENABLE_CHECKING
   gcc_assert (dv_is_decl_p (dv));
+#endif
   return dv;
 }
 
@@ -819,17 +811,39 @@ dv_from_value (rtx value)
 {
   decl_or_value dv;
   dv = value;
+#ifdef ENABLE_CHECKING
   gcc_assert (dv_is_value_p (dv));
+#endif
   return dv;
 }
 
-static inline hashval_t
-dv_htab_hash (decl_or_value dv)
+typedef unsigned int dvuid;
+
+/* Return the uid of DV.  */
+
+static inline dvuid
+dv_uid (decl_or_value dv)
 {
   if (dv_is_value_p (dv))
-    return -(hashval_t)(CSELIB_VAL_PTR (dv_as_value (dv))->value);
+    return CSELIB_VAL_PTR (dv_as_value (dv))->uid;
   else
-    return (VARIABLE_HASH_VAL (dv_as_decl (dv)));
+    return DECL_UID (dv_as_decl (dv));
+}
+
+/* Compute the hash from the uid.  */
+
+static inline hashval_t
+dv_uid2hash (dvuid uid)
+{
+  return uid;
+}
+
+/* The hash function for a mask table in a shared_htab chain.  */
+
+static inline hashval_t
+dv_htab_hash (decl_or_value dv)
+{
+  return dv_uid2hash (dv_uid (dv));
 }
 
 /* The hash function for variable_htab, computes the hash value
@@ -851,29 +865,7 @@ variable_htab_eq (const void *x, const void *y)
   const_variable const v = (const_variable) x;
   decl_or_value dv = CONST_CAST2 (decl_or_value, const void *, y);
 
-  if (dv_as_opaque (v->dv) == dv_as_opaque (dv))
-    return true;
-
-#if ENABLE_CHECKING
-  {
-    bool visv, dvisv;
-
-    visv = dv_is_value_p (v->dv);
-    dvisv = dv_is_value_p (dv);
-
-    if (visv != dvisv)
-      return false;
-
-    if (visv)
-      gcc_assert (CSELIB_VAL_PTR (dv_as_value (v->dv))
-                 != CSELIB_VAL_PTR (dv_as_value (dv)));
-    else
-      gcc_assert (VARIABLE_HASH_VAL (dv_as_decl (v->dv))
-                 != VARIABLE_HASH_VAL (dv_as_decl (dv)));
-  }
-#endif
-
-  return false;
+  return (dv_as_opaque (v->dv) == dv_as_opaque (dv));
 }
 
 /* Free the element of VARIABLE_HTAB (its type is struct variable_def).  */
@@ -1154,23 +1146,6 @@ shared_hash_find (shared_hash vars, decl_or_value dv)
   return shared_hash_find_1 (vars, dv, dv_htab_hash (dv));
 }
 
-/* Determine a total order between two distinct pointers.  Compare the
-   pointers as integral types if size_t is wide enough, otherwise
-   resort to bitwise memory compare.  The actual order does not
-   matter, we just need to be consistent, so endianness is
-   irrelevant.  */
-
-static int
-tie_break_pointers (const void *p1, const void *p2)
-{
-  gcc_assert (p1 != p2);
-
-  if (sizeof (size_t) >= sizeof (void*))
-    return (size_t)p1 < (size_t)p2 ? -1 : 1;
-  else
-    return memcmp (&p1, &p2, sizeof (p1));
-}
-
 /* Return true if TVAL is better than CVAL as a canonival value.  We
    choose lowest-numbered VALUEs, using the RTX address as a
    tie-breaker.  The idea is to arrange them into a star topology,
@@ -1184,9 +1159,7 @@ static inline bool
 canon_value_cmp (rtx tval, rtx cval)
 {
   return !cval
-    || CSELIB_VAL_PTR (tval)->value < CSELIB_VAL_PTR (cval)->value
-    || (CSELIB_VAL_PTR (tval)->value == CSELIB_VAL_PTR (cval)->value
-       && tie_break_pointers (tval, cval) < 0);
+    || CSELIB_VAL_PTR (tval)->uid < CSELIB_VAL_PTR (cval)->uid;
 }
 
 static bool dst_can_be_shared;
@@ -1367,7 +1340,7 @@ get_init_value (dataflow_set *set, rtx loc, decl_or_value dv)
    part.  */
 
 static void
-var_reg_delete_and_set (dataflow_set *set, rtx loc, bool modify, 
+var_reg_delete_and_set (dataflow_set *set, rtx loc, bool modify,
                        enum var_init_status initialized, rtx set_src)
 {
   tree decl = REG_EXPR (loc);
@@ -1401,14 +1374,15 @@ var_reg_delete_and_set (dataflow_set *set, rtx loc, bool modify,
   var_reg_set (set, loc, initialized, set_src);
 }
 
-/* Delete current content of register LOC in dataflow set SET.  If
-   CLOBBER is true, also delete any other live copies of the same
-   variable part.  */
+/* Delete the association of register LOC in dataflow set SET with any
+   variables that aren't onepart.  If CLOBBER is true, also delete any
+   other live copies of the same variable part, and delete the
+   association with onepart dvs too.  */
 
 static void
 var_reg_delete (dataflow_set *set, rtx loc, bool clobber)
 {
-  attrs *reg = &set->regs[REGNO (loc)];
+  attrs *nextp = &set->regs[REGNO (loc)];
   attrs node, next;
 
   if (clobber)
@@ -1421,13 +1395,18 @@ var_reg_delete (dataflow_set *set, rtx loc, bool clobber)
       clobber_variable_part (set, NULL, dv_from_decl (decl), offset, NULL);
     }
 
-  for (node = *reg; node; node = next)
+  for (node = *nextp; node; node = next)
     {
       next = node->next;
-      delete_variable_part (set, node->loc, node->dv, node->offset);
-      pool_free (attrs_pool, node);
+      if (clobber || !dv_onepart_p (node->dv))
+       {
+         delete_variable_part (set, node->loc, node->dv, node->offset);
+         pool_free (attrs_pool, node);
+         *nextp = next;
+       }
+      else
+       nextp = &node->next;
     }
-  *reg = NULL;
 }
 
 /* Delete content of register with number REGNO in dataflow set SET.  */
@@ -1465,7 +1444,7 @@ var_mem_decl_set (dataflow_set *set, rtx loc, enum var_init_status initialized,
    Adjust the address first if it is stack pointer based.  */
 
 static void
-var_mem_set (dataflow_set *set, rtx loc, enum var_init_status initialized, 
+var_mem_set (dataflow_set *set, rtx loc, enum var_init_status initialized,
             rtx set_src)
 {
   tree decl = MEM_EXPR (loc);
@@ -1483,7 +1462,7 @@ var_mem_set (dataflow_set *set, rtx loc, enum var_init_status initialized,
    Adjust the address first if it is stack pointer based.  */
 
 static void
-var_mem_delete_and_set (dataflow_set *set, rtx loc, bool modify, 
+var_mem_delete_and_set (dataflow_set *set, rtx loc, bool modify,
                        enum var_init_status initialized, rtx set_src)
 {
   tree decl = MEM_EXPR (loc);
@@ -1515,10 +1494,12 @@ var_mem_delete (dataflow_set *set, rtx loc, bool clobber)
   delete_variable_part (set, loc, dv_from_decl (decl), offset);
 }
 
-/* Map a value to a location it was just stored in.  */
+/* Bind a value to a location it was just stored in.  If MODIFIED
+   holds, assume the location was modified, detaching it from any
+   values bound to it.  */
 
 static void
-val_store (dataflow_set *set, rtx val, rtx loc, rtx insn)
+val_store (dataflow_set *set, rtx val, rtx loc, rtx insn, bool modified)
 {
   cselib_val *v = CSELIB_VAL_PTR (val);
 
@@ -1544,7 +1525,8 @@ val_store (dataflow_set *set, rtx val, rtx loc, rtx insn)
 
   if (REG_P (loc))
     {
-      var_regno_delete (set, REGNO (loc));
+      if (modified)
+       var_regno_delete (set, REGNO (loc));
       var_reg_decl_set (set, loc, VAR_INIT_STATUS_INITIALIZED,
                        dv_from_value (val), 0, NULL_RTX, INSERT);
     }
@@ -1687,7 +1669,7 @@ val_resolve (dataflow_set *set, rtx val, rtx loc, rtx insn)
                       VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT);
 }
 
-/* Initialize dataflow set SET to be empty. 
+/* Initialize dataflow set SET to be empty.
    VARS_SIZE is the initial size of hash table VARS.  */
 
 static void
@@ -1758,7 +1740,7 @@ variable_union_info_cmp_pos (const void *n1, const void *n2)
 
   if (i1->pos != i2->pos)
     return i1->pos - i2->pos;
-  
+
   return (i1->pos_dst - i2->pos_dst);
 }
 
@@ -2252,12 +2234,18 @@ dv_changed_p (decl_or_value dv)
          : DECL_CHANGED (dv_as_decl (dv)));
 }
 
-/* Return a location list node whose loc is rtx_equal to LOC, in the
+/* Vector of VALUEs that should have VALUE_RECURSED_INTO bit cleared
+   at the end of find_loc_in_1pdv.  Not a static variable in find_loc_in_1pdv
+   to avoid constant allocation/freeing of it.  */
+static VEC(rtx, heap) *values_to_unmark;
+
+/* Helper function for find_loc_in_1pdv.
+   Return a location list node whose loc is rtx_equal to LOC, in the
    location list of a one-part variable or value VAR, or in that of
    any values recursively mentioned in the location lists.  */
 
 static location_chain
-find_loc_in_1pdv (rtx loc, variable var, htab_t vars)
+find_loc_in_1pdv_1 (rtx loc, variable var, htab_t vars)
 {
   location_chain node;
 
@@ -2285,18 +2273,33 @@ find_loc_in_1pdv (rtx loc, variable var, htab_t vars)
          {
            location_chain where;
            VALUE_RECURSED_INTO (node->loc) = true;
-           if ((where = find_loc_in_1pdv (loc, var, vars)))
-             {
-               VALUE_RECURSED_INTO (node->loc) = false;
-               return where;
-             }
-           VALUE_RECURSED_INTO (node->loc) = false;
+           VEC_safe_push (rtx, heap, values_to_unmark, node->loc);
+           if ((where = find_loc_in_1pdv_1 (loc, var, vars)))
+             return where;
          }
       }
 
   return NULL;
 }
 
+/* Return a location list node whose loc is rtx_equal to LOC, in the
+   location list of a one-part variable or value VAR, or in that of
+   any values recursively mentioned in the location lists.  */
+
+static location_chain
+find_loc_in_1pdv (rtx loc, variable var, htab_t vars)
+{
+  location_chain ret;
+  unsigned int i;
+  rtx value;
+
+  ret = find_loc_in_1pdv_1 (loc, var, vars);
+  for (i = 0; VEC_iterate (rtx, values_to_unmark, i, value); i++)
+    VALUE_RECURSED_INTO (value) = false;
+  VEC_truncate (rtx, values_to_unmark, 0);
+  return ret;
+}
+
 /* Hash table iteration argument passed to variable_merge.  */
 struct dfset_merge
 {
@@ -3536,8 +3539,8 @@ variable_post_merge_new_vals (void **slot, void *info)
                      cdv = dv_from_value (cval);
                      if (dump_file)
                        fprintf (dump_file,
-                                "Created new value %i for reg %i\n",
-                                v->value, REGNO (node->loc));
+                                "Created new value %u:%u for reg %i\n",
+                                v->uid, v->hash, REGNO (node->loc));
                    }
 
                  var_reg_decl_set (*dfpm->permp, node->loc,
@@ -3689,13 +3692,32 @@ find_mem_expr_in_1pdv (tree expr, rtx val, htab_t vars)
   return where;
 }
 
+/* Return TRUE if the value of MEM may vary across a call.  */
+
+static bool
+mem_dies_at_call (rtx mem)
+{
+  tree expr = MEM_EXPR (mem);
+  tree decl;
+
+  if (!expr)
+    return true;
+
+  decl = get_base_address (expr);
+
+  if (!decl)
+    return true;
+
+  if (!DECL_P (decl))
+    return true;
+
+  return (may_be_aliased (decl)
+         || (!TREE_READONLY (decl) && is_global_var (decl)));
+}
+
 /* Remove all MEMs from the location list of a hash table entry for a
    one-part variable, except those whose MEM attributes map back to
-   the variable itself, directly or within a VALUE.
-
-   ??? We could also preserve MEMs that reference stack slots that are
-   annotated as not addressable.  This is arguably even more reliable
-   than the current heuristic.  */
+   the variable itself, directly or within a VALUE.  */
 
 static int
 dataflow_set_preserve_mem_locs (void **slot, void *data)
@@ -3717,16 +3739,18 @@ dataflow_set_preserve_mem_locs (void **slot, void *data)
        {
          for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
            {
-             /* We want to remove a MEM that doesn't refer to DECL.  */
+             /* We want to remove dying MEMs that doesn't refer to
+                DECL.  */
              if (GET_CODE (loc->loc) == MEM
                  && (MEM_EXPR (loc->loc) != decl
-                     || MEM_OFFSET (loc->loc)))
+                     || MEM_OFFSET (loc->loc))
+                 && !mem_dies_at_call (loc->loc))
                break;
-             /* We want to move here a MEM that does refer to DECL.  */
+             /* We want to move here MEMs that do refer to DECL.  */
              else if (GET_CODE (loc->loc) == VALUE
                       && find_mem_expr_in_1pdv (decl, loc->loc,
                                                 shared_hash_htab (set->vars)))
-             break;
+               break;
            }
 
          if (!loc)
@@ -3763,7 +3787,8 @@ dataflow_set_preserve_mem_locs (void **slot, void *data)
 
          if (GET_CODE (loc->loc) != MEM
              || (MEM_EXPR (loc->loc) == decl
-                 && MEM_OFFSET (loc->loc) == 0))
+                 && MEM_OFFSET (loc->loc) == 0)
+             || !mem_dies_at_call (loc->loc))
            {
              if (old_loc != loc->loc && emit_notes)
                {
@@ -3811,7 +3836,8 @@ dataflow_set_remove_mem_locs (void **slot, void *data)
       if (var->refcount > 1 || shared_hash_shared (set->vars))
        {
          for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
-           if (GET_CODE (loc->loc) == MEM)
+           if (GET_CODE (loc->loc) == MEM
+               && mem_dies_at_call (loc->loc))
              break;
 
          if (!loc)
@@ -3825,7 +3851,8 @@ dataflow_set_remove_mem_locs (void **slot, void *data)
       for (locp = &var->var_part[0].loc_chain, loc = *locp;
           loc; loc = *locp)
        {
-         if (GET_CODE (loc->loc) != MEM)
+         if (GET_CODE (loc->loc) != MEM
+             || !mem_dies_at_call (loc->loc))
            {
              locp = &loc->next;
              continue;
@@ -4007,7 +4034,7 @@ dataflow_set_different_1 (void **slot, void *data)
       if (dump_file && (dump_flags & TDF_DETAILS))
        {
          fprintf (dump_file, "dataflow difference found: removal of:\n");
-         dump_variable (var1);
+         dump_var (var1);
        }
 
       /* Stop traversing the hash table.  */
@@ -4021,8 +4048,8 @@ dataflow_set_different_1 (void **slot, void *data)
       if (dump_file && (dump_flags & TDF_DETAILS))
        {
          fprintf (dump_file, "dataflow difference found: old and new follow:\n");
-         dump_variable (var1);
-         dump_variable (var2);
+         dump_var (var1);
+         dump_var (var2);
        }
 
       /* Stop traversing the hash table.  */
@@ -4128,8 +4155,8 @@ track_expr_p (tree expr, bool need_rtl)
   decl_rtl = DECL_RTL_IF_SET (expr);
   if (!decl_rtl && need_rtl)
     return 0;
-  
-  /* If this expression is really a debug alias of some other declaration, we 
+
+  /* If this expression is really a debug alias of some other declaration, we
      don't need to track this expression if the ultimate declaration is
      ignored.  */
   realdecl = expr;
@@ -4143,7 +4170,7 @@ track_expr_p (tree expr, bool need_rtl)
     }
 
   /* Do not track EXPR if REALDECL it should be ignored for debugging
-     purposes.  */ 
+     purposes.  */
   if (DECL_IGNORED_P (realdecl))
     return 0;
 
@@ -4375,19 +4402,18 @@ replace_expr_with_values (rtx loc)
    MO_CLOBBER if no micro operation is to be generated.  */
 
 static enum micro_operation_type
-use_type (rtx *loc, struct count_use_info *cui, enum machine_mode *modep)
+use_type (rtx loc, struct count_use_info *cui, enum machine_mode *modep)
 {
   tree expr;
-  cselib_val *val;
 
   if (cui && cui->sets)
     {
-      if (GET_CODE (*loc) == VAR_LOCATION)
+      if (GET_CODE (loc) == VAR_LOCATION)
        {
-         if (track_expr_p (PAT_VAR_LOCATION_DECL (*loc), false))
+         if (track_expr_p (PAT_VAR_LOCATION_DECL (loc), false))
            {
-             rtx ploc = PAT_VAR_LOCATION_LOC (*loc);
-             cselib_val *val = cselib_lookup (ploc, GET_MODE (*loc), 1);
+             rtx ploc = PAT_VAR_LOCATION_LOC (loc);
+             cselib_val *val = cselib_lookup (ploc, GET_MODE (loc), 1);
 
              /* ??? flag_float_store and volatile mems are never
                 given values, but we could in theory use them for
@@ -4399,47 +4425,52 @@ use_type (rtx *loc, struct count_use_info *cui, enum machine_mode *modep)
            return MO_CLOBBER;
        }
 
-      if ((REG_P (*loc) || MEM_P (*loc))
-         && (val = find_use_val (*loc, GET_MODE (*loc), cui)))
+      if (REG_P (loc) || MEM_P (loc))
        {
          if (modep)
-           *modep = GET_MODE (*loc);
+           *modep = GET_MODE (loc);
          if (cui->store_p)
            {
-             if (REG_P (*loc)
-                 || cselib_lookup (XEXP (*loc, 0), GET_MODE (*loc), 0))
+             if (REG_P (loc)
+                 || (find_use_val (loc, GET_MODE (loc), cui)
+                     && cselib_lookup (XEXP (loc, 0), GET_MODE (loc), 0)))
                return MO_VAL_SET;
            }
-         else if (!cselib_preserved_value_p (val))
-           return MO_VAL_USE;
+         else
+           {
+             cselib_val *val = find_use_val (loc, GET_MODE (loc), cui);
+
+             if (val && !cselib_preserved_value_p (val))
+               return MO_VAL_USE;
+           }
        }
     }
 
-  if (REG_P (*loc))
+  if (REG_P (loc))
     {
-      gcc_assert (REGNO (*loc) < FIRST_PSEUDO_REGISTER);
+      gcc_assert (REGNO (loc) < FIRST_PSEUDO_REGISTER);
 
-      expr = REG_EXPR (*loc);
+      expr = REG_EXPR (loc);
 
       if (!expr)
        return MO_USE_NO_VAR;
       else if (target_for_debug_bind (var_debug_decl (expr)))
        return MO_CLOBBER;
-      else if (track_loc_p (*loc, expr, REG_OFFSET (*loc),
+      else if (track_loc_p (loc, expr, REG_OFFSET (loc),
                            false, modep, NULL))
        return MO_USE;
       else
        return MO_USE_NO_VAR;
     }
-  else if (MEM_P (*loc))
+  else if (MEM_P (loc))
     {
-      expr = MEM_EXPR (*loc);
+      expr = MEM_EXPR (loc);
 
       if (!expr)
        return MO_CLOBBER;
       else if (target_for_debug_bind (var_debug_decl (expr)))
        return MO_CLOBBER;
-      else if (track_loc_p (*loc, expr, INT_MEM_OFFSET (*loc),
+      else if (track_loc_p (loc, expr, INT_MEM_OFFSET (loc),
                            false, modep, NULL))
        return MO_USE;
       else
@@ -4467,56 +4498,84 @@ log_op_type (rtx x, basic_block bb, rtx insn,
    INSN is instruction which the LOC is part of.  */
 
 static int
-count_uses (rtx *loc, void *cuip)
+count_uses (rtx *ploc, void *cuip)
 {
+  rtx loc = *ploc;
   struct count_use_info *cui = (struct count_use_info *) cuip;
   enum micro_operation_type mopt = use_type (loc, cui, NULL);
 
   if (mopt != MO_CLOBBER)
     {
       cselib_val *val;
-      enum machine_mode mode = GET_MODE (*loc);
-
-      VTI (cui->bb)->n_mos++;
-
-      if (dump_file && (dump_flags & TDF_DETAILS))
-       log_op_type (*loc, cui->bb, cui->insn, mopt, dump_file);
+      enum machine_mode mode = GET_MODE (loc);
 
       switch (mopt)
        {
        case MO_VAL_LOC:
-         loc = &PAT_VAR_LOCATION_LOC (*loc);
-         if (VAR_LOC_UNKNOWN_P (*loc))
+         loc = PAT_VAR_LOCATION_LOC (loc);
+         if (VAR_LOC_UNKNOWN_P (loc))
            break;
          /* Fall through.  */
 
        case MO_VAL_USE:
        case MO_VAL_SET:
-         if (MEM_P (*loc)
-             && !REG_P (XEXP (*loc, 0)) && !MEM_P (XEXP (*loc, 0)))
+         if (MEM_P (loc)
+             && !REG_P (XEXP (loc, 0)) && !MEM_P (XEXP (loc, 0)))
            {
              enum machine_mode address_mode
-               = targetm.addr_space.address_mode (MEM_ADDR_SPACE (*loc));
-             val = cselib_lookup (XEXP (*loc, 0), address_mode, false);
+               = targetm.addr_space.address_mode (MEM_ADDR_SPACE (loc));
+             val = cselib_lookup (XEXP (loc, 0), address_mode, 0);
 
              if (val && !cselib_preserved_value_p (val))
                {
                  VTI (cui->bb)->n_mos++;
                  cselib_preserve_value (val);
+                 if (dump_file && (dump_flags & TDF_DETAILS))
+                   log_op_type (XEXP (loc, 0), cui->bb, cui->insn,
+                                MO_VAL_USE, dump_file);
                }
            }
 
-         val = find_use_val (*loc, mode, cui);
+         val = find_use_val (loc, mode, cui);
          if (val)
-           cselib_preserve_value (val);
+           {
+             if (mopt == MO_VAL_SET
+                 && GET_CODE (PATTERN (cui->insn)) == COND_EXEC
+                 && (REG_P (loc)
+                     || (MEM_P (loc)
+                         && (use_type (loc, NULL, NULL) == MO_USE
+                             || cui->sets))))
+               {
+                 cselib_val *oval = cselib_lookup (loc, GET_MODE (loc), 0);
+
+                 gcc_assert (oval != val);
+                 gcc_assert (REG_P (loc) || MEM_P (loc));
+
+                 if (!cselib_preserved_value_p (oval))
+                   {
+                     VTI (cui->bb)->n_mos++;
+                     cselib_preserve_value (oval);
+                     if (dump_file && (dump_flags & TDF_DETAILS))
+                       log_op_type (loc, cui->bb, cui->insn,
+                                    MO_VAL_USE, dump_file);
+                   }
+               }
+
+             cselib_preserve_value (val);
+           }
          else
-           gcc_assert (mopt == MO_VAL_LOC);
+           gcc_assert (mopt == MO_VAL_LOC
+                       || (mopt == MO_VAL_SET && cui->store_p));
 
          break;
 
        default:
          break;
        }
+
+      VTI (cui->bb)->n_mos++;
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       log_op_type (loc, cui->bb, cui->insn, mopt, dump_file);
     }
 
   return 0;
@@ -4590,8 +4649,9 @@ count_with_sets (rtx insn, struct cselib_set *sets, int n_sets)
    to VTI (bb)->mos.  INSN is instruction which the LOC is part of.  */
 
 static int
-add_uses (rtx *loc, void *data)
+add_uses (rtx *ploc, void *data)
 {
+  rtx loc = *ploc;
   enum machine_mode mode = VOIDmode;
   struct count_use_info *cui = (struct count_use_info *)data;
   enum micro_operation_type type = use_type (loc, cui, &mode);
@@ -4602,12 +4662,12 @@ add_uses (rtx *loc, void *data)
       micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
       mo->type = type;
-      mo->u.loc = type == MO_USE ? var_lowpart (mode, *loc) : *loc;
+      mo->u.loc = type == MO_USE ? var_lowpart (mode, loc) : loc;
       mo->insn = cui->insn;
 
       if (type == MO_VAL_LOC)
        {
-         rtx oloc = *loc;
+         rtx oloc = loc;
          rtx vloc = PAT_VAR_LOCATION_LOC (oloc);
          cselib_val *val;
 
@@ -4655,7 +4715,7 @@ add_uses (rtx *loc, void *data)
 
              oloc = gen_rtx_CONCAT (mode, val->val_rtx, oloc);
 
-             type2 = use_type (&vloc, 0, &mode2);
+             type2 = use_type (vloc, 0, &mode2);
 
              gcc_assert (type2 == MO_USE || type2 == MO_USE_NO_VAR
                          || type2 == MO_CLOBBER);
@@ -4679,8 +4739,8 @@ add_uses (rtx *loc, void *data)
        {
          enum machine_mode mode2 = VOIDmode;
          enum micro_operation_type type2;
-         cselib_val *val = find_use_val (*loc, GET_MODE (*loc), cui);
-         rtx vloc, oloc = *loc, nloc;
+         cselib_val *val = find_use_val (loc, GET_MODE (loc), cui);
+         rtx vloc, oloc = loc, nloc;
 
          gcc_assert (cui->sets);
 
@@ -4718,7 +4778,7 @@ add_uses (rtx *loc, void *data)
                      || type2 == MO_CLOBBER);
 
          if (type2 == MO_USE)
-           vloc = var_lowpart (mode2, *loc);
+           vloc = var_lowpart (mode2, loc);
          else
            vloc = oloc;
 
@@ -4732,7 +4792,7 @@ add_uses (rtx *loc, void *data)
 
          */
 
-         nloc = replace_expr_with_values (*loc);
+         nloc = replace_expr_with_values (loc);
          if (!nloc)
            nloc = oloc;
 
@@ -4781,7 +4841,7 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
   basic_block bb = cui->bb;
   micro_operation *mo;
   rtx oloc = loc, nloc, src = NULL;
-  enum micro_operation_type type = use_type (&loc, cui, &mode);
+  enum micro_operation_type type = use_type (loc, cui, &mode);
   bool track_p = false;
   cselib_val *v;
   bool resolve, preserve;
@@ -4796,7 +4856,7 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
       mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
       if ((GET_CODE (expr) == CLOBBER && type != MO_VAL_SET)
-         || !(track_p = use_type (&loc, NULL, &mode2) == MO_USE)
+         || !(track_p = use_type (loc, NULL, &mode2) == MO_USE)
          || GET_CODE (expr) == CLOBBER)
        {
          mo->type = MO_CLOBBER;
@@ -4815,19 +4875,21 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
            }
          else
            {
+             rtx xexpr = CONST_CAST_RTX (expr);
+
              if (SET_SRC (expr) != src)
-               expr = gen_rtx_SET (VOIDmode, loc, src);
+               xexpr = gen_rtx_SET (VOIDmode, loc, src);
              if (same_variable_part_p (src, REG_EXPR (loc), REG_OFFSET (loc)))
                mo->type = MO_COPY;
              else
                mo->type = MO_SET;
-             mo->u.loc = CONST_CAST_RTX (expr);
+             mo->u.loc = xexpr;
            }
        }
       mo->insn = cui->insn;
     }
   else if (MEM_P (loc)
-          && ((track_p = use_type (&loc, NULL, &mode2) == MO_USE)
+          && ((track_p = use_type (loc, NULL, &mode2) == MO_USE)
               || cui->sets))
     {
       mo = VTI (bb)->mos + VTI (bb)->n_mos++;
@@ -4872,15 +4934,17 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
            }
          else
            {
+             rtx xexpr = CONST_CAST_RTX (expr);
+
              if (SET_SRC (expr) != src)
-               expr = gen_rtx_SET (VOIDmode, loc, src);
-             if (same_variable_part_p (SET_SRC (expr),
+               xexpr = gen_rtx_SET (VOIDmode, loc, src);
+             if (same_variable_part_p (SET_SRC (xexpr),
                                        MEM_EXPR (loc),
                                        INT_MEM_OFFSET (loc)))
                mo->type = MO_COPY;
              else
                mo->type = MO_SET;
-             mo->u.loc = CONST_CAST_RTX (expr);
+             mo->u.loc = xexpr;
            }
        }
       mo->insn = cui->insn;
@@ -4893,15 +4957,50 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
 
   v = find_use_val (oloc, mode, cui);
 
+  if (!v)
+    goto log_and_return;
+
   resolve = preserve = !cselib_preserved_value_p (v);
 
   nloc = replace_expr_with_values (oloc);
   if (nloc)
     oloc = nloc;
 
-  if (resolve && GET_CODE (mo->u.loc) == SET)
+  if (GET_CODE (PATTERN (cui->insn)) == COND_EXEC)
     {
-      nloc = replace_expr_with_values (SET_SRC (mo->u.loc));
+      cselib_val *oval = cselib_lookup (oloc, GET_MODE (oloc), 0);
+
+      gcc_assert (oval != v);
+      gcc_assert (REG_P (oloc) || MEM_P (oloc));
+
+      if (!cselib_preserved_value_p (oval))
+       {
+         micro_operation *nmo = VTI (bb)->mos + VTI (bb)->n_mos++;
+
+         cselib_preserve_value (oval);
+
+         nmo->type = MO_VAL_USE;
+         nmo->u.loc = gen_rtx_CONCAT (mode, oval->val_rtx, oloc);
+         VAL_NEEDS_RESOLUTION (nmo->u.loc) = 1;
+         nmo->insn = mo->insn;
+
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           log_op_type (nmo->u.loc, cui->bb, cui->insn,
+                        nmo->type, dump_file);
+       }
+
+      resolve = false;
+    }
+  else if (resolve && GET_CODE (mo->u.loc) == SET)
+    {
+      nloc = replace_expr_with_values (SET_SRC (expr));
+
+      /* Avoid the mode mismatch between oexpr and expr.  */
+      if (!nloc && mode != mode2)
+       {
+         nloc = SET_SRC (expr);
+         gcc_assert (oloc == SET_DEST (expr));
+       }
 
       if (nloc)
        oloc = gen_rtx_SET (GET_MODE (mo->u.loc), oloc, nloc);
@@ -4940,7 +5039,8 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
 
      (concat (concat val dstv) (set dst src)): dst now holds val,
      copied from src.  dstv is a value-based representation of dst, if
-     it differs from dst.  If resolution is needed, src is a REG.
+     it differs from dst.  If resolution is needed, src is a REG, and
+     its mode is the same as that of val.
 
      (concat (concat val (set dstv srcv)) (set dst src)): src
      copied to dst, holding val.  dstv and srcv are value-based
@@ -5116,14 +5216,14 @@ find_src_set_src (dataflow_set *set, rtx src)
        {
          found = false;
          for (i = 0; i < var->n_var_parts && !found; i++)
-           for (nextp = var->var_part[i].loc_chain; nextp && !found; 
+           for (nextp = var->var_part[i].loc_chain; nextp && !found;
                 nextp = nextp->next)
              if (rtx_equal_p (nextp->loc, src))
                {
                  set_src = nextp->set_src;
                  found = true;
                }
-             
+
        }
     }
 
@@ -5215,6 +5315,8 @@ compute_bb_dataflow (basic_block bb)
 
              if (VAL_NEEDS_RESOLUTION (loc))
                val_resolve (out, val, vloc, insn);
+             else
+               val_store (out, val, uloc, insn, false);
 
              if (VAL_HOLDS_TRACK_EXPR (loc))
                {
@@ -5306,7 +5408,7 @@ compute_bb_dataflow (basic_block bb)
              else if (REG_P (uloc))
                var_regno_delete (out, REGNO (uloc));
 
-             val_store (out, val, vloc, insn);
+             val_store (out, val, vloc, insn, true);
            }
            break;
 
@@ -5588,6 +5690,7 @@ vt_find_locations (void)
     FOR_EACH_BB (bb)
       gcc_assert (VTI (bb)->flooded);
 
+  VEC_free (rtx, heap, values_to_unmark);
   free (bb_order);
   fibheap_delete (worklist);
   fibheap_delete (pending);
@@ -5615,11 +5718,11 @@ dump_attrs_list (attrs list)
 /* Print the information about variable *SLOT to dump file.  */
 
 static int
-dump_variable_slot (void **slot, void *data ATTRIBUTE_UNUSED)
+dump_var_slot (void **slot, void *data ATTRIBUTE_UNUSED)
 {
   variable var = (variable) *slot;
 
-  dump_variable (var);
+  dump_var (var);
 
   /* Continue traversing the hash table.  */
   return 1;
@@ -5628,7 +5731,7 @@ dump_variable_slot (void **slot, void *data ATTRIBUTE_UNUSED)
 /* Print the information about variable VAR to dump file.  */
 
 static void
-dump_variable (variable var)
+dump_var (variable var)
 {
   int i;
   location_chain node;
@@ -5675,7 +5778,7 @@ dump_vars (htab_t vars)
   if (htab_elements (vars) > 0)
     {
       fprintf (dump_file, "Variables:\n");
-      htab_traverse (vars, dump_variable_slot, NULL);
+      htab_traverse (vars, dump_var_slot, NULL);
     }
 }
 
@@ -6428,6 +6531,7 @@ emit_note_insn_var_location (void **varp, void *data)
       if (j < var->n_var_parts
          && wider_mode != VOIDmode
          && mode == GET_MODE (var->var_part[j].loc_chain->loc)
+         && (REG_P (loc[n_var_parts]) || MEM_P (loc[n_var_parts]))
          && (loc2 = vt_expand_loc (var->var_part[j].loc_chain->loc, vars))
          && GET_CODE (loc[n_var_parts]) == GET_CODE (loc2)
          && last_limit == var->var_part[j].offset)
@@ -6512,7 +6616,7 @@ emit_note_insn_var_location (void **varp, void *data)
        = gen_rtx_EXPR_LIST (VOIDmode, loc[0], GEN_INT (offsets[0]));
 
       NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, decl,
-                                                      expr_list, 
+                                                      expr_list,
                                                       (int) initialized);
     }
   else if (n_var_parts)
@@ -6526,7 +6630,7 @@ emit_note_insn_var_location (void **varp, void *data)
       parallel = gen_rtx_PARALLEL (VOIDmode,
                                   gen_rtvec_v (n_var_parts, loc));
       NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, decl,
-                                                      parallel, 
+                                                      parallel,
                                                       (int) initialized);
     }
 
@@ -6833,6 +6937,8 @@ emit_notes_in_bb (basic_block bb, dataflow_set *set)
 
              if (VAL_NEEDS_RESOLUTION (loc))
                val_resolve (set, val, vloc, insn);
+             else
+               val_store (set, val, uloc, insn, false);
 
              if (VAL_HOLDS_TRACK_EXPR (loc))
                {
@@ -6920,7 +7026,7 @@ emit_notes_in_bb (basic_block bb, dataflow_set *set)
              else if (REG_P (uloc))
                var_regno_delete (set, REGNO (uloc));
 
-             val_store (set, val, vloc, insn);
+             val_store (set, val, vloc, insn, true);
 
              emit_notes_for_changes (NEXT_INSN (insn), EMIT_NOTE_BEFORE_INSN,
                                      set->vars);
@@ -7094,7 +7200,7 @@ static void
 vt_add_function_parameters (void)
 {
   tree parm;
-  
+
   for (parm = DECL_ARGUMENTS (current_function_decl);
        parm; parm = TREE_CHAIN (parm))
     {
@@ -7206,7 +7312,7 @@ vt_add_function_parameters (void)
   if (MAY_HAVE_DEBUG_INSNS)
     {
       cselib_preserve_only_values (true);
-      cselib_reset_table_with_next_value (cselib_get_next_unknown_value ());
+      cselib_reset_table (cselib_get_next_uid ());
     }
 
 }
@@ -7239,15 +7345,15 @@ vt_initialize (void)
       rtx insn;
       HOST_WIDE_INT pre, post = 0;
       int count;
-      unsigned int next_value_before = cselib_get_next_unknown_value ();
-      unsigned int next_value_after = next_value_before;
+      unsigned int next_uid_before = cselib_get_next_uid ();
+      unsigned int next_uid_after = next_uid_before;
 
       if (MAY_HAVE_DEBUG_INSNS)
        {
          cselib_record_sets_hook = count_with_sets;
          if (dump_file && (dump_flags & TDF_DETAILS))
            fprintf (dump_file, "first value: %i\n",
-                    cselib_get_next_unknown_value ());
+                    cselib_get_next_uid ());
        }
 
       /* Count the number of micro operations.  */
@@ -7302,12 +7408,12 @@ vt_initialize (void)
       if (MAY_HAVE_DEBUG_INSNS)
        {
          cselib_preserve_only_values (false);
-         next_value_after = cselib_get_next_unknown_value ();
-         cselib_reset_table_with_next_value (next_value_before);
+         next_uid_after = cselib_get_next_uid ();
+         cselib_reset_table (next_uid_before);
          cselib_record_sets_hook = add_with_sets;
          if (dump_file && (dump_flags & TDF_DETAILS))
            fprintf (dump_file, "first value: %i\n",
-                    cselib_get_next_unknown_value ());
+                    cselib_get_next_uid ());
        }
 
       /* Add the micro-operations to the array.  */
@@ -7366,8 +7472,8 @@ vt_initialize (void)
       if (MAY_HAVE_DEBUG_INSNS)
        {
          cselib_preserve_only_values (true);
-         gcc_assert (next_value_after == cselib_get_next_unknown_value ());
-         cselib_reset_table_with_next_value (next_value_after);
+         gcc_assert (next_uid_after == cselib_get_next_uid ());
+         cselib_reset_table (next_uid_after);
          cselib_record_sets_hook = NULL;
        }
     }