OSDN Git Service

PR debug/43176
authorjakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 7 Mar 2010 15:44:11 +0000 (15:44 +0000)
committerjakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 7 Mar 2010 15:44:11 +0000 (15:44 +0000)
* Makefile.in (var-tracking.o): Depend on pointer-set.h.
* cselib.c (struct expand_value_data): Add dummy field.
(cselib_expand_value_rtx, cselib_expand_value_rtx_cb): Initialize
dummy to false.
(cselib_dummy_expand_value_rtx_cb): New function.
(cselib_expand_value_rtx_1): If evd->dummy is true, don't allocate
any rtl.
* cselib.h (cselib_dummy_expand_value_rtx_cb): New prototype.
* var-tracking.c: Include pointer-set.h.
(variable): Change n_var_parts to char from int.  Add
cur_loc_changed and in_changed_variables fields.
(variable_canonicalize): Remove.
(shared_var_p): New inline function.
(unshare_variable): Maintain cur_loc_changed and
in_changed_variables fields.  If var was in changed_variables,
replace it there with new_var.  Just copy cur_loc instead of
resetting it to something else.
(variable_union): Don't recompute cur_loc.  Use shared_var_p.
(dataflow_set_union): Don't call variable_canonicalize.
(loc_cmp): If both x and y are DEBUG_EXPRs, compare uids
of their DEBUG_EXPR_TREE_DECLs.
(canonicalize_loc_order_check): Verify that cur_loc is NULL
and in_changed_variables and cur_loc_changed is false.
(variable_merge_over_cur): Clear cur_loc, in_changed_variables
and cur_loc_changed.  Don't update cur_loc here.
(variable_merge_over_src): Don't call variable_canonicalize.
(dataflow_set_preserve_mem_locs): Use shared_var_p.  When
removing loc that is equal to cur_loc, clear cur_loc,
set cur_loc_changed and ensure variable_was_changed is called.
(dataflow_set_remove_mem_locs): Use shared_var_p.  Only
compare pointers in cur_loc check, if it is equal to loc,
clear cur_loc and set cur_loc_changed.  Don't recompute cur_loc here.
(variable_different_p): Remove compare_current_location argument,
don't compare cur_loc.
(dataflow_set_different_1): Adjust variable_different_p caller.
(variable_was_changed): If dv had some var in changed_variables
already, reset in_changed_variables flag for it and propagate
cur_loc_changed over to the new variable.  On empty var
always set cur_loc_changed.  Set in_changed_variables on whatever
var is added to changed_variables.
(set_slot_part): Clear cur_loc_changed and in_changed_variables.
Use shared_var_p.  When removing loc that is equal to cur_loc,
clear cur_loc and set cur_loc_changed.  If cur_loc is NULL at the
end, don't set it to something else, just call variable_was_changed.
(delete_slot_part): Use shared_var_p.  When cur_loc equals to
loc being removed, clear cur_loc and set cur_loc_changed.
Set cur_loc_changed if all locations have been removed.
(struct expand_loc_callback_data): New type.
(vt_expand_loc_callback): Add dummy mode in which no rtxes are
allocated.  Always create SUBREGs if simplify_subreg failed.
Prefer to use cur_loc, when that fails and still in
changed_variables (and seen first time) recompute it.  Set
cur_loc_changed of variables which had to change cur_loc and
compute elcd->cur_loc_changed if any of the subexpressions used
had to change cur_loc.
(vt_expand_loc): Adjust to pass arguments in
expand_loc_callback_data structure.
(vt_expand_loc_dummy): New function.
(emitted_notes): New variable.
(emit_note_insn_var_location): For VALUEs and DEBUG_EXPR_DECLs
that weren't used for any other decl in current
emit_notes_for_changes call call vt_expand_loc_dummy to update
cur_loc.  For -fno-var-tracking-assignments, set cur_loc to
first loc_chain location if NULL before.  Always use just
cur_loc instead of first loc_chain location.  When cur_loc_changed
is false, when not --enable-checking=rtl just don't emit any note.
When rtl checking, compute the note and assert it is the same
as previous note.  Clear cur_loc_changed and in_changed_variables
at the end before removing from changed_variables.
(check_changed_vars_3): New function.
(emit_notes_for_changes): Traverse changed_vars to call
check_changed_vars_3 on each changed var.
(emit_notes_for_differences_1): Clear cur_loc_changed and
in_changed_variables.  Recompute cur_loc of new_var.
(emit_notes_for_differences_2): Clear cur_loc if new variable
appears.
(vt_emit_notes): Initialize and destroy emitted_notes.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@157264 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/Makefile.in
gcc/cselib.c
gcc/cselib.h
gcc/var-tracking.c

index 25c0efd..c495c34 100644 (file)
@@ -1,3 +1,84 @@
+2010-03-07  Jakub Jelinek  <jakub@redhat.com>
+
+       PR debug/43176
+       * Makefile.in (var-tracking.o): Depend on pointer-set.h.
+       * cselib.c (struct expand_value_data): Add dummy field.
+       (cselib_expand_value_rtx, cselib_expand_value_rtx_cb): Initialize
+       dummy to false.
+       (cselib_dummy_expand_value_rtx_cb): New function.
+       (cselib_expand_value_rtx_1): If evd->dummy is true, don't allocate
+       any rtl.
+       * cselib.h (cselib_dummy_expand_value_rtx_cb): New prototype.
+       * var-tracking.c: Include pointer-set.h.
+       (variable): Change n_var_parts to char from int.  Add
+       cur_loc_changed and in_changed_variables fields.
+       (variable_canonicalize): Remove.
+       (shared_var_p): New inline function.
+       (unshare_variable): Maintain cur_loc_changed and
+       in_changed_variables fields.  If var was in changed_variables,
+       replace it there with new_var.  Just copy cur_loc instead of
+       resetting it to something else.
+       (variable_union): Don't recompute cur_loc.  Use shared_var_p.
+       (dataflow_set_union): Don't call variable_canonicalize.
+       (loc_cmp): If both x and y are DEBUG_EXPRs, compare uids
+       of their DEBUG_EXPR_TREE_DECLs.
+       (canonicalize_loc_order_check): Verify that cur_loc is NULL
+       and in_changed_variables and cur_loc_changed is false.
+       (variable_merge_over_cur): Clear cur_loc, in_changed_variables
+       and cur_loc_changed.  Don't update cur_loc here.
+       (variable_merge_over_src): Don't call variable_canonicalize.
+       (dataflow_set_preserve_mem_locs): Use shared_var_p.  When
+       removing loc that is equal to cur_loc, clear cur_loc,
+       set cur_loc_changed and ensure variable_was_changed is called.
+       (dataflow_set_remove_mem_locs): Use shared_var_p.  Only
+       compare pointers in cur_loc check, if it is equal to loc,
+       clear cur_loc and set cur_loc_changed.  Don't recompute cur_loc here.
+       (variable_different_p): Remove compare_current_location argument,
+       don't compare cur_loc.
+       (dataflow_set_different_1): Adjust variable_different_p caller.
+       (variable_was_changed): If dv had some var in changed_variables
+       already, reset in_changed_variables flag for it and propagate
+       cur_loc_changed over to the new variable.  On empty var
+       always set cur_loc_changed.  Set in_changed_variables on whatever
+       var is added to changed_variables.
+       (set_slot_part): Clear cur_loc_changed and in_changed_variables.
+       Use shared_var_p.  When removing loc that is equal to cur_loc,
+       clear cur_loc and set cur_loc_changed.  If cur_loc is NULL at the
+       end, don't set it to something else, just call variable_was_changed.
+       (delete_slot_part): Use shared_var_p.  When cur_loc equals to
+       loc being removed, clear cur_loc and set cur_loc_changed.
+       Set cur_loc_changed if all locations have been removed.
+       (struct expand_loc_callback_data): New type.
+       (vt_expand_loc_callback): Add dummy mode in which no rtxes are
+       allocated.  Always create SUBREGs if simplify_subreg failed.
+       Prefer to use cur_loc, when that fails and still in
+       changed_variables (and seen first time) recompute it.  Set
+       cur_loc_changed of variables which had to change cur_loc and
+       compute elcd->cur_loc_changed if any of the subexpressions used
+       had to change cur_loc.
+       (vt_expand_loc): Adjust to pass arguments in
+       expand_loc_callback_data structure.
+       (vt_expand_loc_dummy): New function.
+       (emitted_notes): New variable.
+       (emit_note_insn_var_location): For VALUEs and DEBUG_EXPR_DECLs
+       that weren't used for any other decl in current
+       emit_notes_for_changes call call vt_expand_loc_dummy to update
+       cur_loc.  For -fno-var-tracking-assignments, set cur_loc to
+       first loc_chain location if NULL before.  Always use just
+       cur_loc instead of first loc_chain location.  When cur_loc_changed
+       is false, when not --enable-checking=rtl just don't emit any note.
+       When rtl checking, compute the note and assert it is the same
+       as previous note.  Clear cur_loc_changed and in_changed_variables
+       at the end before removing from changed_variables.
+       (check_changed_vars_3): New function.
+       (emit_notes_for_changes): Traverse changed_vars to call
+       check_changed_vars_3 on each changed var.
+       (emit_notes_for_differences_1): Clear cur_loc_changed and
+       in_changed_variables.  Recompute cur_loc of new_var.
+       (emit_notes_for_differences_2): Clear cur_loc if new variable
+       appears.
+       (vt_emit_notes): Initialize and destroy emitted_notes.
+
 2010-03-07  Bernd Schmidt  <bernd.schmidt@analog.com>
 
        PR rtl-optimization/42220
index bea6cf2..20ab71f 100644 (file)
@@ -3031,7 +3031,7 @@ var-tracking.o : var-tracking.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
    $(RTL_H) $(TREE_H) hard-reg-set.h insn-config.h reload.h $(FLAGS_H) \
    $(BASIC_BLOCK_H) output.h sbitmap.h alloc-pool.h $(FIBHEAP_H) $(HASHTAB_H) \
    $(REGS_H) $(EXPR_H) $(TIMEVAR_H) $(TREE_PASS_H) $(TREE_FLOW_H) \
-   cselib.h $(TARGET_H) $(TOPLEV_H) $(PARAMS_H) $(DIAGNOSTIC_H)
+   cselib.h $(TARGET_H) $(TOPLEV_H) $(PARAMS_H) $(DIAGNOSTIC_H) pointer-set.h
 profile.o : profile.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    $(TREE_H) $(FLAGS_H) output.h $(REGS_H) $(EXPR_H) $(FUNCTION_H) \
    $(TOPLEV_H) $(COVERAGE_H) $(TREE_FLOW_H) value-prof.h cfghooks.h \
index deac835..515fc32 100644 (file)
@@ -69,6 +69,7 @@ struct expand_value_data
   bitmap regs_active;
   cselib_expand_callback callback;
   void *callback_arg;
+  bool dummy;
 };
 
 static rtx cselib_expand_value_rtx_1 (rtx, struct expand_value_data *, int);
@@ -1069,6 +1070,7 @@ cselib_expand_value_rtx (rtx orig, bitmap regs_active, int max_depth)
   evd.regs_active = regs_active;
   evd.callback = NULL;
   evd.callback_arg = NULL;
+  evd.dummy = false;
 
   return cselib_expand_value_rtx_1 (orig, &evd, max_depth);
 }
@@ -1088,10 +1090,29 @@ cselib_expand_value_rtx_cb (rtx orig, bitmap regs_active, int max_depth,
   evd.regs_active = regs_active;
   evd.callback = cb;
   evd.callback_arg = data;
+  evd.dummy = false;
 
   return cselib_expand_value_rtx_1 (orig, &evd, max_depth);
 }
 
+/* Similar to cselib_expand_value_rtx_cb, but no rtxs are actually copied
+   or simplified.  Useful to find out whether cselib_expand_value_rtx_cb
+   would return NULL or non-NULL, without allocating new rtx.  */
+
+bool
+cselib_dummy_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;
+  evd.dummy = true;
+
+  return cselib_expand_value_rtx_1 (orig, &evd, max_depth) != NULL;
+}
+
 /* Internal implementation of cselib_expand_value_rtx and
    cselib_expand_value_rtx_cb.  */
 
@@ -1249,7 +1270,10 @@ cselib_expand_value_rtx_1 (rtx orig, struct expand_value_data *evd,
      that all fields need copying, and then clear the fields that should
      not be copied.  That is the sensible default behavior, and forces
      us to explicitly document why we are *not* copying a flag.  */
-  copy = shallow_copy_rtx (orig);
+  if (evd->dummy)
+    copy = NULL;
+  else
+    copy = shallow_copy_rtx (orig);
 
   format_ptr = GET_RTX_FORMAT (code);
 
@@ -1263,7 +1287,8 @@ cselib_expand_value_rtx_1 (rtx orig, struct expand_value_data *evd,
                                                    max_depth - 1);
            if (!result)
              return NULL;
-           XEXP (copy, i) = result;
+           if (copy)
+             XEXP (copy, i) = result;
          }
        break;
 
@@ -1271,14 +1296,16 @@ cselib_expand_value_rtx_1 (rtx orig, struct expand_value_data *evd,
       case 'V':
        if (XVEC (orig, i) != NULL)
          {
-           XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i));
-           for (j = 0; j < XVECLEN (copy, i); j++)
+           if (copy)
+             XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i));
+           for (j = 0; j < XVECLEN (orig, i); j++)
              {
                rtx result = cselib_expand_value_rtx_1 (XVECEXP (orig, i, j),
                                                        evd, max_depth - 1);
                if (!result)
                  return NULL;
-               XVECEXP (copy, i, j) = result;
+               if (copy)
+                 XVECEXP (copy, i, j) = result;
              }
          }
        break;
@@ -1299,6 +1326,9 @@ cselib_expand_value_rtx_1 (rtx orig, struct expand_value_data *evd,
        gcc_unreachable ();
       }
 
+  if (evd->dummy)
+    return orig;
+
   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,
index 75ff457..2cdf6ad 100644 (file)
@@ -81,7 +81,9 @@ extern int references_value_p (const_rtx, int);
 extern rtx cselib_expand_value_rtx (rtx, bitmap, int);
 typedef rtx (*cselib_expand_callback)(rtx, bitmap, int, void *);
 extern rtx cselib_expand_value_rtx_cb (rtx, bitmap, int,
-                                      cselib_expand_callback, void*);
+                                      cselib_expand_callback, void *);
+extern bool cselib_dummy_expand_value_rtx_cb (rtx, bitmap, int,
+                                             cselib_expand_callback, void *);
 extern rtx cselib_subst_to_values (rtx);
 extern void cselib_invalidate_rtx (rtx);
 
index 8cc7675..1878e90 100644 (file)
 #include "toplev.h"
 #include "params.h"
 #include "diagnostic.h"
+#include "pointer-set.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.
@@ -324,7 +325,16 @@ typedef struct variable_def
   int refcount;
 
   /* Number of variable parts.  */
-  int n_var_parts;
+  char n_var_parts;
+
+  /* True if this variable changed (any of its) cur_loc fields
+     during the current emit_notes_for_changes resp.
+     emit_notes_for_differences call.  */
+  bool cur_loc_changed;
+
+  /* True if this variable_def struct is currently in the
+     changed_variables hash table.  */
+  bool in_changed_variables;
 
   /* The variable parts.  */
   variable_part var_part[1];
@@ -429,14 +439,13 @@ static void dataflow_set_clear (dataflow_set *);
 static void dataflow_set_copy (dataflow_set *, dataflow_set *);
 static int variable_union_info_cmp_pos (const void *, const void *);
 static int variable_union (void **, void *);
-static int variable_canonicalize (void **, void *);
 static void dataflow_set_union (dataflow_set *, dataflow_set *);
 static location_chain find_loc_in_1pdv (rtx, variable, htab_t);
 static bool canon_value_cmp (rtx, rtx);
 static int loc_cmp (rtx, rtx);
 static bool variable_part_different_p (variable_part *, variable_part *);
 static bool onepart_variable_different_p (variable, variable);
-static bool variable_different_p (variable, variable, bool);
+static bool variable_different_p (variable, variable);
 static int dataflow_set_different_1 (void **, void *);
 static bool dataflow_set_different (dataflow_set *, dataflow_set *);
 static void dataflow_set_destroy (dataflow_set *);
@@ -1056,6 +1065,16 @@ shared_hash_htab (shared_hash vars)
   return vars->htab;
 }
 
+/* Return true if VAR is shared, or maybe because VARS is shared.  */
+
+static inline bool
+shared_var_p (variable var, shared_hash vars)
+{
+  /* Don't count an entry in the changed_variables table as a duplicate.  */
+  return ((var->refcount > 1 + (int) var->in_changed_variables)
+         || shared_hash_shared (vars));
+}
+
 /* Copy variables into a new hash table.  */
 
 static shared_hash
@@ -1195,6 +1214,9 @@ unshare_variable (dataflow_set *set, void **slot, variable var,
   new_var->refcount = 1;
   var->refcount--;
   new_var->n_var_parts = var->n_var_parts;
+  new_var->cur_loc_changed = var->cur_loc_changed;
+  var->cur_loc_changed = false;
+  new_var->in_changed_variables = false;
 
   if (! flag_var_tracking_uninit)
     initialized = VAR_INIT_STATUS_INITIALIZED;
@@ -1226,12 +1248,7 @@ unshare_variable (dataflow_set *set, void **slot, variable var,
          nextp = &new_lc->next;
        }
 
-      /* We are at the basic block boundary when copying variable description
-        so set the CUR_LOC to be the first element of the chain.  */
-      if (new_var->var_part[i].loc_chain)
-       new_var->var_part[i].cur_loc = new_var->var_part[i].loc_chain->loc;
-      else
-       new_var->var_part[i].cur_loc = NULL;
+      new_var->var_part[i].cur_loc = var->var_part[i].cur_loc;
     }
 
   dst_can_be_shared = false;
@@ -1240,6 +1257,17 @@ unshare_variable (dataflow_set *set, void **slot, variable var,
   else if (set->traversed_vars && set->vars != set->traversed_vars)
     slot = shared_hash_find_slot_noinsert (set->vars, var->dv);
   *slot = new_var;
+  if (var->in_changed_variables)
+    {
+      void **cslot
+       = htab_find_slot_with_hash (changed_variables, var->dv,
+                                   dv_htab_hash (var->dv), NO_INSERT);
+      gcc_assert (*cslot == (void *) var);
+      var->in_changed_variables = false;
+      variable_htab_free (var);
+      *cslot = new_var;
+      new_var->in_changed_variables = true;
+    }
   return slot;
 }
 
@@ -1791,23 +1819,6 @@ variable_union (void **slot, void *data)
 
       *dstp = src;
 
-      /* If CUR_LOC of some variable part is not the first element of
-        the location chain we are going to change it so we have to make
-        a copy of the variable.  */
-      for (k = 0; k < src->n_var_parts; k++)
-       {
-         gcc_assert (!src->var_part[k].loc_chain
-                     == !src->var_part[k].cur_loc);
-         if (src->var_part[k].loc_chain)
-           {
-             gcc_assert (src->var_part[k].cur_loc);
-             if (src->var_part[k].cur_loc != src->var_part[k].loc_chain->loc)
-               break;
-           }
-       }
-      if (k < src->n_var_parts)
-       dstp = unshare_variable (set, dstp, src, VAR_INIT_STATUS_UNKNOWN);
-
       /* Continue traversing the hash table.  */
       return 1;
     }
@@ -1841,7 +1852,7 @@ variable_union (void **slot, void *data)
            {
              location_chain nnode;
 
-             if (dst->refcount != 1 || shared_hash_shared (set->vars))
+             if (shared_var_p (dst, set->vars))
                {
                  dstp = unshare_variable (set, dstp, dst,
                                           VAR_INIT_STATUS_INITIALIZED);
@@ -1871,8 +1882,6 @@ variable_union (void **slot, void *data)
          dnode = *nodep;
        }
 
-      dst->var_part[0].cur_loc = dst->var_part[0].loc_chain->loc;
-
       return 1;
     }
 
@@ -1897,8 +1906,7 @@ variable_union (void **slot, void *data)
      thus there are at most MAX_VAR_PARTS different offsets.  */
   gcc_assert (dv_onepart_p (dst->dv) ? k == 1 : k <= MAX_VAR_PARTS);
 
-  if ((dst->refcount > 1 || shared_hash_shared (set->vars))
-      && dst->n_var_parts != k)
+  if (dst->n_var_parts != k && shared_var_p (dst, set->vars))
     {
       dstp = unshare_variable (set, dstp, dst, VAR_INIT_STATUS_UNKNOWN);
       dst = (variable)*dstp;
@@ -1925,7 +1933,7 @@ variable_union (void **slot, void *data)
          /* If DST is shared compare the location chains.
             If they are different we will modify the chain in DST with
             high probability so make a copy of DST.  */
-         if (dst->refcount > 1 || shared_hash_shared (set->vars))
+         if (shared_var_p (dst, set->vars))
            {
              for (node = src->var_part[i].loc_chain,
                   node2 = dst->var_part[j].loc_chain; node && node2;
@@ -2139,13 +2147,7 @@ variable_union (void **slot, void *data)
          dst->var_part[k].offset = src->var_part[i].offset;
          i--;
        }
-
-      /* We are at the basic block boundary when computing union
-        so set the CUR_LOC to be the first element of the chain.  */
-      if (dst->var_part[k].loc_chain)
-       dst->var_part[k].cur_loc = dst->var_part[k].loc_chain->loc;
-      else
-       dst->var_part[k].cur_loc = NULL;
+      dst->var_part[k].cur_loc = NULL;
     }
 
   if (flag_var_tracking_uninit)
@@ -2165,39 +2167,6 @@ variable_union (void **slot, void *data)
   return 1;
 }
 
-/* Like variable_union, but only used when doing dataflow_set_union
-   into an empty hashtab.  To allow sharing, dst is initially shared
-   with src (so all variables are "copied" from src to dst hashtab),
-   so only unshare_variable for variables that need canonicalization
-   are needed.  */
-
-static int
-variable_canonicalize (void **slot, void *data)
-{
-  variable src;
-  dataflow_set *set = (dataflow_set *) data;
-  int k;
-
-  src = *(variable *) slot;
-
-  /* If CUR_LOC of some variable part is not the first element of
-     the location chain we are going to change it so we have to make
-     a copy of the variable.  */
-  for (k = 0; k < src->n_var_parts; k++)
-    {
-      gcc_assert (!src->var_part[k].loc_chain == !src->var_part[k].cur_loc);
-      if (src->var_part[k].loc_chain)
-       {
-         gcc_assert (src->var_part[k].cur_loc);
-         if (src->var_part[k].cur_loc != src->var_part[k].loc_chain->loc)
-           break;
-       }
-    }
-  if (k < src->n_var_parts)
-    slot = unshare_variable (set, slot, src, VAR_INIT_STATUS_UNKNOWN);
-  return 1;
-}
-
 /* Compute union of dataflow sets SRC and DST and store it to DST.  */
 
 static void
@@ -2212,9 +2181,6 @@ dataflow_set_union (dataflow_set *dst, dataflow_set *src)
     {
       shared_hash_destroy (dst->vars);
       dst->vars = shared_hash_copy (src->vars);
-      dst->traversed_vars = dst->vars;
-      htab_traverse (shared_hash_htab (dst->vars), variable_canonicalize, dst);
-      dst->traversed_vars = NULL;
     }
   else
     htab_traverse (shared_hash_htab (src->vars), variable_union, dst);
@@ -2479,6 +2445,18 @@ loc_cmp (rtx x, rtx y)
 
   gcc_assert (GET_MODE (x) == GET_MODE (y));
 
+  if (GET_CODE (x) == DEBUG_EXPR)
+    {
+      if (DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (x))
+         < DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (y)))
+       return -1;
+#ifdef ENABLE_CHECKING
+      gcc_assert (DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (x))
+                 > DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (y)));
+#endif
+      return 1;
+    }
+
   fmt = GET_RTX_FORMAT (code);
   for (i = 0; i < GET_RTX_LENGTH (code); i++)
     switch (fmt[i])
@@ -2718,6 +2696,13 @@ canonicalize_loc_order_check (void **slot, void *data ATTRIBUTE_UNUSED)
   decl_or_value dv = var->dv;
   location_chain node, next;
 
+#ifdef ENABLE_RTL_CHECKING
+  int i;
+  for (i = 0; i < var->n_var_parts; i++)
+    gcc_assert (var->var_part[0].cur_loc == NULL);
+  gcc_assert (!var->cur_loc_changed && !var->in_changed_variables);
+#endif
+
   if (!dv_onepart_p (dv))
     return 1;
 
@@ -3080,9 +3065,11 @@ variable_merge_over_cur (void **s1slot, void *data)
              dvar->dv = dv;
              dvar->refcount = 1;
              dvar->n_var_parts = 1;
+             dvar->cur_loc_changed = false;
+             dvar->in_changed_variables = false;
              dvar->var_part[0].offset = 0;
              dvar->var_part[0].loc_chain = node;
-             dvar->var_part[0].cur_loc = node->loc;
+             dvar->var_part[0].cur_loc = NULL;
 
              dstslot
                = shared_hash_find_slot_unshare_1 (&dst->vars, dv, dvhash,
@@ -3212,6 +3199,8 @@ variable_merge_over_cur (void **s1slot, void *data)
                      var->dv = dv;
                      var->refcount = 1;
                      var->n_var_parts = 1;
+                     var->cur_loc_changed = false;
+                     var->in_changed_variables = false;
                      var->var_part[0].offset = 0;
                      var->var_part[0].loc_chain = NULL;
                      var->var_part[0].cur_loc = NULL;
@@ -3248,11 +3237,7 @@ variable_merge_over_cur (void **s1slot, void *data)
       dst_can_be_shared = false;
     }
   else
-    {
-      if (dvar->refcount == 1)
-        dvar->var_part[0].cur_loc = dvar->var_part[0].loc_chain->loc;
-      dst_can_be_shared = false;
-    }
+    dst_can_be_shared = false;
 
   return 1;
 }
@@ -3276,7 +3261,7 @@ variable_merge_over_src (void **s2slot, void *data)
       void **dstp = shared_hash_find_slot (dst->vars, dv);
       *dstp = s2var;
       s2var->refcount++;
-      return variable_canonicalize (dstp, dst);
+      return 1;
     }
 
   dsm->src_onepart_cnt++;
@@ -3746,13 +3731,14 @@ dataflow_set_preserve_mem_locs (void **slot, void *data)
     {
       tree decl = dv_as_decl (var->dv);
       location_chain loc, *locp;
+      bool changed = false;
 
       if (!var->n_var_parts)
        return 1;
 
       gcc_assert (var->n_var_parts == 1);
 
-      if (var->refcount > 1 || shared_hash_shared (set->vars))
+      if (shared_var_p (var, set->vars))
        {
          for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
            {
@@ -3809,6 +3795,12 @@ dataflow_set_preserve_mem_locs (void **slot, void *data)
            {
              if (old_loc != loc->loc && emit_notes)
                {
+                 if (old_loc == var->var_part[0].cur_loc)
+                   {
+                     changed = true;
+                     var->var_part[0].cur_loc = NULL;
+                     var->cur_loc_changed = true;
+                   }
                  add_value_chains (var->dv, loc->loc);
                  remove_value_chains (var->dv, old_loc);
                }
@@ -3817,7 +3809,15 @@ dataflow_set_preserve_mem_locs (void **slot, void *data)
            }
 
          if (emit_notes)
-           remove_value_chains (var->dv, old_loc);
+           {
+             remove_value_chains (var->dv, old_loc);
+             if (old_loc == var->var_part[0].cur_loc)
+               {
+                 changed = true;
+                 var->var_part[0].cur_loc = NULL;
+                 var->cur_loc_changed = true;
+               }
+           }
          *locp = loc->next;
          pool_free (loc_chain_pool, loc);
        }
@@ -3827,8 +3827,10 @@ dataflow_set_preserve_mem_locs (void **slot, void *data)
          var->n_var_parts--;
          if (emit_notes && dv_is_value_p (var->dv))
            remove_cselib_value_chains (var->dv);
-         variable_was_changed (var, set);
+         changed = true;
        }
+      if (changed)
+       variable_was_changed (var, set);
     }
 
   return 1;
@@ -3850,7 +3852,7 @@ dataflow_set_remove_mem_locs (void **slot, void *data)
 
       gcc_assert (var->n_var_parts == 1);
 
-      if (var->refcount > 1 || shared_hash_shared (set->vars))
+      if (shared_var_p (var, set->vars))
        {
          for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
            if (GET_CODE (loc->loc) == MEM
@@ -3881,9 +3883,12 @@ dataflow_set_remove_mem_locs (void **slot, void *data)
          /* If we have deleted the location which was last emitted
             we have to emit new location so add the variable to set
             of changed variables.  */
-         if (var->var_part[0].cur_loc
-             && rtx_equal_p (loc->loc, var->var_part[0].cur_loc))
-           changed = true;
+         if (var->var_part[0].cur_loc == loc->loc)
+           {
+             changed = true;
+             var->var_part[0].cur_loc = NULL;
+             var->cur_loc_changed = true;
+           }
          pool_free (loc_chain_pool, loc);
        }
 
@@ -3892,14 +3897,10 @@ dataflow_set_remove_mem_locs (void **slot, void *data)
          var->n_var_parts--;
          if (emit_notes && dv_is_value_p (var->dv))
            remove_cselib_value_chains (var->dv);
-         gcc_assert (changed);
+         changed = true;
        }
       if (changed)
-       {
-         if (var->n_var_parts && var->var_part[0].loc_chain)
-           var->var_part[0].cur_loc = var->var_part[0].loc_chain->loc;
-         variable_was_changed (var, set);
-       }
+       variable_was_changed (var, set);
     }
 
   return 1;
@@ -3987,13 +3988,10 @@ onepart_variable_different_p (variable var1, variable var2)
   return lc1 != lc2;
 }
 
-/* Return true if variables VAR1 and VAR2 are different.
-   If COMPARE_CURRENT_LOCATION is true compare also the cur_loc of each
-   variable part.  */
+/* Return true if variables VAR1 and VAR2 are different.  */
 
 static bool
-variable_different_p (variable var1, variable var2,
-                     bool compare_current_location)
+variable_different_p (variable var1, variable var2)
 {
   int i;
 
@@ -4007,16 +4005,6 @@ variable_different_p (variable var1, variable var2,
     {
       if (var1->var_part[i].offset != var2->var_part[i].offset)
        return true;
-      if (compare_current_location)
-       {
-         if (!((REG_P (var1->var_part[i].cur_loc)
-                && REG_P (var2->var_part[i].cur_loc)
-                && (REGNO (var1->var_part[i].cur_loc)
-                    == REGNO (var2->var_part[i].cur_loc)))
-               || rtx_equal_p (var1->var_part[i].cur_loc,
-                               var2->var_part[i].cur_loc)))
-           return true;
-       }
       /* One-part values have locations in a canonical order.  */
       if (i == 0 && var1->var_part[i].offset == 0 && dv_onepart_p (var1->dv))
        {
@@ -4058,7 +4046,7 @@ dataflow_set_different_1 (void **slot, void *data)
       return 0;
     }
 
-  if (variable_different_p (var1, var2, false))
+  if (variable_different_p (var1, var2))
     {
       dataflow_set_different_value = true;
 
@@ -5978,6 +5966,7 @@ variable_was_changed (variable var, dataflow_set *set)
   if (emit_notes)
     {
       void **slot;
+      bool old_cur_loc_changed = false;
 
       /* Remember this decl or VALUE has been added to changed_variables.  */
       set_dv_changed (var->dv, true);
@@ -5986,6 +5975,14 @@ variable_was_changed (variable var, dataflow_set *set)
                                       var->dv,
                                       hash, INSERT);
 
+      if (*slot)
+       {
+         variable old_var = (variable) *slot;
+         gcc_assert (old_var->in_changed_variables);
+         old_var->in_changed_variables = false;
+         old_cur_loc_changed = old_var->cur_loc_changed;
+         variable_htab_free (*slot);
+       }
       if (set && var->n_var_parts == 0)
        {
          variable empty_var;
@@ -5994,12 +5991,19 @@ variable_was_changed (variable var, dataflow_set *set)
          empty_var->dv = var->dv;
          empty_var->refcount = 1;
          empty_var->n_var_parts = 0;
+         empty_var->cur_loc_changed = true;
+         empty_var->in_changed_variables = true;
          *slot = empty_var;
          goto drop_var;
        }
       else
        {
          var->refcount++;
+         var->in_changed_variables = true;
+         /* If within processing one uop a variable is deleted
+            and then readded, we need to assume it has changed.  */
+         if (old_cur_loc_changed)
+           var->cur_loc_changed = true;
          *slot = var;
        }
     }
@@ -6082,6 +6086,8 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
       var->dv = dv;
       var->refcount = 1;
       var->n_var_parts = 1;
+      var->cur_loc_changed = false;
+      var->in_changed_variables = false;
       var->var_part[0].offset = offset;
       var->var_part[0].loc_chain = NULL;
       var->var_part[0].cur_loc = NULL;
@@ -6179,7 +6185,7 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
       if (r == 0)
        return slot;
 
-      if (var->refcount > 1 || shared_hash_shared (set->vars))
+      if (shared_var_p (var, set->vars))
        {
          slot = unshare_variable (set, slot, var, initialized);
          var = (variable)*slot;
@@ -6218,7 +6224,7 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
          else
            {
              /* We have to make a copy of a shared variable.  */
-             if (var->refcount > 1 || shared_hash_shared (set->vars))
+             if (shared_var_p (var, set->vars))
                {
                  slot = unshare_variable (set, slot, var, initialized);
                  var = (variable)*slot;
@@ -6230,7 +6236,7 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
          /* We have not found the location part, new one will be created.  */
 
          /* We have to make a copy of the shared variable.  */
-         if (var->refcount > 1 || shared_hash_shared (set->vars))
+         if (shared_var_p (var, set->vars))
            {
              slot = unshare_variable (set, slot, var, initialized);
              var = (variable)*slot;
@@ -6267,6 +6273,11 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
                initialized = node->init;
              if (node->set_src != NULL && set_src == NULL)
                set_src = node->set_src;
+             if (var->var_part[pos].cur_loc == node->loc)
+               {
+                 var->var_part[pos].cur_loc = NULL;
+                 var->cur_loc_changed = true;
+               }
              pool_free (loc_chain_pool, node);
              *nextp = next;
              break;
@@ -6291,10 +6302,7 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
 
   /* If no location was emitted do so.  */
   if (var->var_part[pos].cur_loc == NULL)
-    {
-      var->var_part[pos].cur_loc = loc;
-      variable_was_changed (var, set);
-    }
+    variable_was_changed (var, set);
 
   return slot;
 }
@@ -6422,7 +6430,7 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot,
       location_chain *nextp;
       bool changed;
 
-      if (var->refcount > 1 || shared_hash_shared (set->vars))
+      if (shared_var_p (var, set->vars))
        {
          /* If the variable contains the location part we have to
             make a copy of the variable.  */
@@ -6442,6 +6450,7 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot,
        }
 
       /* Delete the location part.  */
+      changed = false;
       nextp = &var->var_part[pos].loc_chain;
       for (node = *nextp; node; node = next)
        {
@@ -6452,6 +6461,15 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot,
            {
              if (emit_notes && pos == 0 && dv_onepart_p (var->dv))
                remove_value_chains (var->dv, node->loc);
+             /* If we have deleted the location which was last emitted
+                we have to emit new location so add the variable to set
+                of changed variables.  */
+             if (var->var_part[pos].cur_loc == node->loc)
+               {
+                 changed = true;
+                 var->var_part[pos].cur_loc = NULL;
+                 var->cur_loc_changed = true;
+               }
              pool_free (loc_chain_pool, node);
              *nextp = next;
              break;
@@ -6460,28 +6478,16 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot,
            nextp = &node->next;
        }
 
-      /* If we have deleted the location which was last emitted
-        we have to emit new location so add the variable to set
-        of changed variables.  */
-      if (var->var_part[pos].cur_loc
-         && ((REG_P (loc)
-              && REG_P (var->var_part[pos].cur_loc)
-              && REGNO (loc) == REGNO (var->var_part[pos].cur_loc))
-             || rtx_equal_p (loc, var->var_part[pos].cur_loc)))
-       {
-         changed = true;
-         if (var->var_part[pos].loc_chain)
-           var->var_part[pos].cur_loc = var->var_part[pos].loc_chain->loc;
-       }
-      else
-       changed = false;
-
       if (var->var_part[pos].loc_chain == NULL)
        {
-         gcc_assert (changed);
+         changed = true;
          var->n_var_parts--;
-         if (emit_notes && var->n_var_parts == 0 && dv_is_value_p (var->dv))
-           remove_cselib_value_chains (var->dv);
+         if (emit_notes)
+           {
+             var->cur_loc_changed = true;
+             if (var->n_var_parts == 0 && dv_is_value_p (var->dv))
+               remove_cselib_value_chains (var->dv);
+           }
          while (pos < var->n_var_parts)
            {
              var->var_part[pos] = var->var_part[pos + 1];
@@ -6510,6 +6516,27 @@ delete_variable_part (dataflow_set *set, rtx loc, decl_or_value dv,
   slot = delete_slot_part (set, loc, slot, offset);
 }
 
+/* Structure for passing some other parameters to function
+   vt_expand_loc_callback.  */
+struct expand_loc_callback_data
+{
+  /* The variables and values active at this point.  */
+  htab_t vars;
+
+  /* True in vt_expand_loc_dummy calls, no rtl should be allocated.
+     Non-NULL should be returned if vt_expand_loc would return
+     non-NULL in that case, NULL otherwise.  cur_loc_changed should be
+     computed and cur_loc recomputed when possible (but just once
+     per emit_notes_for_changes call).  */
+  bool dummy;
+
+  /* True if expansion of subexpressions had to recompute some
+     VALUE/DEBUG_EXPR_DECL's cur_loc or used a VALUE/DEBUG_EXPR_DECL
+     whose cur_loc has been already recomputed during current
+     emit_notes_for_changes call.  */
+  bool cur_loc_changed;
+};
+
 /* Callback for cselib_expand_value, that looks for expressions
    holding the value in the var-tracking hash tables.  Return X for
    standard processing, anything else is to be used as-is.  */
@@ -6517,7 +6544,10 @@ delete_variable_part (dataflow_set *set, rtx loc, decl_or_value dv,
 static rtx
 vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data)
 {
-  htab_t vars = (htab_t)data;
+  struct expand_loc_callback_data *elcd
+    = (struct expand_loc_callback_data *) data;
+  bool dummy = elcd->dummy;
+  bool cur_loc_changed = elcd->cur_loc_changed;
   decl_or_value dv;
   variable var;
   location_chain loc;
@@ -6526,11 +6556,6 @@ vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data)
   switch (GET_CODE (x))
     {
     case SUBREG:
-      subreg = SUBREG_REG (x);
-
-      if (GET_CODE (SUBREG_REG (x)) != VALUE)
-       return x;
-
       subreg = cselib_expand_value_rtx_cb (SUBREG_REG (x), regs,
                                           max_depth - 1,
                                           vt_expand_loc_callback, data);
@@ -6538,13 +6563,16 @@ vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data)
       if (!subreg)
        return NULL;
 
+      if (dummy)
+       return pc_rtx;
+
       result = simplify_gen_subreg (GET_MODE (x), subreg,
                                    GET_MODE (SUBREG_REG (x)),
                                    SUBREG_BYTE (x));
 
       /* Invalid SUBREGs are ok in debug info.  ??? We could try
         alternate expansions for the VALUE as well.  */
-      if (!result && (REG_P (subreg) || MEM_P (subreg)))
+      if (!result)
        result = gen_rtx_raw_SUBREG (GET_MODE (x), subreg, SUBREG_BYTE (x));
 
       return result;
@@ -6566,25 +6594,78 @@ vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data)
   if (VALUE_RECURSED_INTO (x))
     return NULL;
 
-  var = (variable) htab_find_with_hash (vars, dv, dv_htab_hash (dv));
+  var = (variable) htab_find_with_hash (elcd->vars, dv, dv_htab_hash (dv));
 
   if (!var)
-    return xret;
+    {
+      if (dummy && dv_changed_p (dv))
+       elcd->cur_loc_changed = true;
+      return xret;
+    }
 
   if (var->n_var_parts == 0)
-    return xret;
+    {
+      if (dummy)
+       elcd->cur_loc_changed = true;
+      return xret;
+    }
 
   gcc_assert (var->n_var_parts == 1);
 
   VALUE_RECURSED_INTO (x) = true;
   result = NULL;
 
-  for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
+  if (var->var_part[0].cur_loc)
     {
-      result = cselib_expand_value_rtx_cb (loc->loc, regs, max_depth,
-                                          vt_expand_loc_callback, vars);
+      if (dummy)
+       {
+         if (cselib_dummy_expand_value_rtx_cb (var->var_part[0].cur_loc, regs,
+                                               max_depth,
+                                               vt_expand_loc_callback, data))
+           result = pc_rtx;
+       }
+      else
+       result = cselib_expand_value_rtx_cb (var->var_part[0].cur_loc, regs,
+                                            max_depth,
+                                            vt_expand_loc_callback, data);
       if (result)
-       break;
+       set_dv_changed (dv, false);
+    }
+  if (!result && dv_changed_p (dv))
+    {
+      set_dv_changed (dv, false);
+      for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
+       if (loc->loc == var->var_part[0].cur_loc)
+         continue;
+       else if (dummy)
+         {
+           elcd->cur_loc_changed = cur_loc_changed;
+           if (cselib_dummy_expand_value_rtx_cb (loc->loc, regs, max_depth,
+                                                 vt_expand_loc_callback,
+                                                 data))
+             {
+               result = pc_rtx;
+               break;
+             }
+           else
+             {
+               result = cselib_expand_value_rtx_cb (loc->loc, regs, max_depth,
+                                                    vt_expand_loc_callback,
+                                                    data);
+               if (result)
+                 break;
+             }
+         }
+      if (dummy && (result || var->var_part[0].cur_loc))
+       var->cur_loc_changed = true;
+      var->var_part[0].cur_loc = loc ? loc->loc : NULL_RTX;
+    }
+  if (dummy)
+    {
+      if (var->cur_loc_changed)
+       elcd->cur_loc_changed = true;
+      else if (!result && var->var_part[0].cur_loc == NULL_RTX)
+       elcd->cur_loc_changed = cur_loc_changed;
     }
 
   VALUE_RECURSED_INTO (x) = false;
@@ -6600,18 +6681,46 @@ vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data)
 static rtx
 vt_expand_loc (rtx loc, htab_t vars)
 {
+  struct expand_loc_callback_data data;
+
   if (!MAY_HAVE_DEBUG_INSNS)
     return loc;
 
+  data.vars = vars;
+  data.dummy = false;
+  data.cur_loc_changed = false;
   loc = cselib_expand_value_rtx_cb (loc, scratch_regs, 5,
-                                   vt_expand_loc_callback, vars);
+                                   vt_expand_loc_callback, &data);
 
   if (loc && MEM_P (loc))
     loc = targetm.delegitimize_address (loc);
-
   return loc;
 }
 
+/* Like vt_expand_loc, but only return true/false (whether vt_expand_loc
+   would succeed or not, without actually allocating new rtxes.  */
+
+static bool
+vt_expand_loc_dummy (rtx loc, htab_t vars, bool *pcur_loc_changed)
+{
+  struct expand_loc_callback_data data;
+  bool ret;
+
+  gcc_assert (MAY_HAVE_DEBUG_INSNS);
+  data.vars = vars;
+  data.dummy = true;
+  data.cur_loc_changed = false;
+  ret = cselib_dummy_expand_value_rtx_cb (loc, scratch_regs, 5,
+                                         vt_expand_loc_callback, &data);
+  *pcur_loc_changed = data.cur_loc_changed;
+  return ret;
+}
+
+#ifdef ENABLE_RTL_CHECKING
+/* Used to verify that cur_loc_changed updating is safe.  */
+static struct pointer_map_t *emitted_notes;
+#endif
+
 /* Emit the NOTE_INSN_VAR_LOCATION for variable *VARP.  DATA contains
    additional parameters: WHERE specifies whether the note shall be emitted
    before or after instruction INSN.  */
@@ -6623,7 +6732,7 @@ emit_note_insn_var_location (void **varp, void *data)
   rtx insn = ((emit_note_data *)data)->insn;
   enum emit_note_where where = ((emit_note_data *)data)->where;
   htab_t vars = ((emit_note_data *)data)->vars;
-  rtx note;
+  rtx note, note_vl;
   int i, j, n_var_parts;
   bool complete;
   enum var_init_status initialized = VAR_INIT_STATUS_UNINITIALIZED;
@@ -6632,20 +6741,34 @@ emit_note_insn_var_location (void **varp, void *data)
   HOST_WIDE_INT offsets[MAX_VAR_PARTS];
   rtx loc[MAX_VAR_PARTS];
   tree decl;
+  location_chain lc;
 
   if (dv_is_value_p (var->dv))
-    goto clear;
+    goto value_or_debug_decl;
 
   decl = dv_as_decl (var->dv);
 
   if (TREE_CODE (decl) == DEBUG_EXPR_DECL)
-    goto clear;
-
-  gcc_assert (decl);
+    goto value_or_debug_decl;
 
   complete = true;
   last_limit = 0;
   n_var_parts = 0;
+  if (!MAY_HAVE_DEBUG_STMTS)
+    {
+      for (i = 0; i < var->n_var_parts; i++)
+       if (var->var_part[i].cur_loc == NULL && var->var_part[i].loc_chain)
+         {
+           var->var_part[i].cur_loc = var->var_part[i].loc_chain->loc;
+           var->cur_loc_changed = true;
+         }
+      if (var->n_var_parts == 0)
+       var->cur_loc_changed = true;
+    }
+#ifndef ENABLE_RTL_CHECKING
+  if (!var->cur_loc_changed)
+    goto clear;
+#endif
   for (i = 0; i < var->n_var_parts; i++)
     {
       enum machine_mode mode, wider_mode;
@@ -6659,15 +6782,26 @@ emit_note_insn_var_location (void **varp, void *data)
       else if (last_limit > var->var_part[i].offset)
        continue;
       offsets[n_var_parts] = var->var_part[i].offset;
-      loc2 = vt_expand_loc (var->var_part[i].loc_chain->loc, vars);
+      if (!var->var_part[i].cur_loc)
+       {
+         complete = false;
+         continue;
+       }
+      loc2 = vt_expand_loc (var->var_part[i].cur_loc, vars);
       if (!loc2)
        {
          complete = false;
          continue;
        }
       loc[n_var_parts] = loc2;
-      mode = GET_MODE (var->var_part[i].loc_chain->loc);
-      initialized = var->var_part[i].loc_chain->init;
+      mode = GET_MODE (var->var_part[i].cur_loc);
+      for (lc = var->var_part[i].loc_chain; lc; lc = lc->next)
+       if (var->var_part[i].cur_loc == lc->loc)
+         {
+           initialized = lc->init;
+           break;
+         }
+      gcc_assert (lc);
       last_limit = offsets[n_var_parts] + GET_MODE_SIZE (mode);
 
       /* Attempt to merge adjacent registers or memory.  */
@@ -6677,11 +6811,12 @@ emit_note_insn_var_location (void **varp, void *data)
          break;
       if (j < var->n_var_parts
          && wider_mode != VOIDmode
-         && mode == GET_MODE (var->var_part[j].loc_chain->loc)
+         && var->var_part[j].cur_loc
+         && mode == GET_MODE (var->var_part[j].cur_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)
+         && last_limit == var->var_part[j].offset
+         && (loc2 = vt_expand_loc (var->var_part[j].cur_loc, vars))
+         && GET_CODE (loc[n_var_parts]) == GET_CODE (loc2))
        {
          rtx new_loc = NULL;
 
@@ -6740,31 +6875,20 @@ emit_note_insn_var_location (void **varp, void *data)
   if ((unsigned HOST_WIDE_INT) last_limit < TREE_INT_CST_LOW (type_size_unit))
     complete = false;
 
-  if (where != EMIT_NOTE_BEFORE_INSN)
-    {
-      note = emit_note_after (NOTE_INSN_VAR_LOCATION, insn);
-      if (where == EMIT_NOTE_AFTER_CALL_INSN)
-       NOTE_DURING_CALL_P (note) = true;
-    }
-  else
-    note = emit_note_before (NOTE_INSN_VAR_LOCATION, insn);
-
   if (! flag_var_tracking_uninit)
     initialized = VAR_INIT_STATUS_INITIALIZED;
 
+  note_vl = NULL_RTX;
   if (!complete)
-    {
-      NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, decl,
-                                                      NULL_RTX, (int) initialized);
-    }
+    note_vl = gen_rtx_VAR_LOCATION (VOIDmode, decl, NULL_RTX,
+                                   (int) initialized);
   else if (n_var_parts == 1)
     {
       rtx expr_list
        = gen_rtx_EXPR_LIST (VOIDmode, loc[0], GEN_INT (offsets[0]));
 
-      NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, decl,
-                                                      expr_list,
-                                                      (int) initialized);
+      note_vl = gen_rtx_VAR_LOCATION (VOIDmode, decl, expr_list,
+                                     (int) initialized);
     }
   else if (n_var_parts)
     {
@@ -6776,17 +6900,64 @@ 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,
-                                                      (int) initialized);
+      note_vl = gen_rtx_VAR_LOCATION (VOIDmode, decl,
+                                     parallel, (int) initialized);
     }
 
+#ifdef ENABLE_RTL_CHECKING
+  if (note_vl)
+    {
+      void **note_slot = pointer_map_insert (emitted_notes, decl);
+      rtx pnote = (rtx) *note_slot;
+      if (!var->cur_loc_changed && (pnote || PAT_VAR_LOCATION_LOC (note_vl)))
+       {
+         gcc_assert (pnote);
+         gcc_assert (rtx_equal_p (PAT_VAR_LOCATION_LOC (pnote),
+                                  PAT_VAR_LOCATION_LOC (note_vl)));
+       }
+      *note_slot = (void *) note_vl;
+    }
+  if (!var->cur_loc_changed)
+    goto clear;
+#endif
+
+  if (where != EMIT_NOTE_BEFORE_INSN)
+    {
+      note = emit_note_after (NOTE_INSN_VAR_LOCATION, insn);
+      if (where == EMIT_NOTE_AFTER_CALL_INSN)
+       NOTE_DURING_CALL_P (note) = true;
+    }
+  else
+    note = emit_note_before (NOTE_INSN_VAR_LOCATION, insn);
+  NOTE_VAR_LOCATION (note) = note_vl;
+
  clear:
   set_dv_changed (var->dv, false);
+  var->cur_loc_changed = false;
+  gcc_assert (var->in_changed_variables);
+  var->in_changed_variables = false;
   htab_clear_slot (changed_variables, varp);
 
   /* Continue traversing the hash table.  */
   return 1;
+
+ value_or_debug_decl:
+  if (dv_changed_p (var->dv) && var->n_var_parts)
+    {
+      location_chain lc;
+      bool cur_loc_changed;
+
+      if (var->var_part[0].cur_loc
+         && vt_expand_loc_dummy (var->var_part[0].cur_loc, vars,
+                                 &cur_loc_changed))
+       goto clear;
+      for (lc = var->var_part[0].loc_chain; lc; lc = lc->next)
+       if (lc->loc != var->var_part[0].cur_loc
+           && vt_expand_loc_dummy (lc->loc, vars, &cur_loc_changed))
+         break;
+      var->var_part[0].cur_loc = lc ? lc->loc : NULL_RTX;
+    }
+  goto clear;
 }
 
 DEF_VEC_P (variable);
@@ -6858,6 +7029,48 @@ check_changed_vars_2 (variable var, htab_t htab)
     }
 }
 
+/* For each changed decl (except DEBUG_EXPR_DECLs) recompute
+   cur_loc if needed (and cur_loc of all VALUEs and DEBUG_EXPR_DECLs
+   it needs and are also in changed variables) and track whether
+   cur_loc (or anything it uses to compute location) had to change
+   during the current emit_notes_for_changes call.  */
+
+static int
+check_changed_vars_3 (void **slot, void *data)
+{
+  variable var = (variable) *slot;
+  htab_t vars = (htab_t) data;
+  int i;
+  location_chain lc;
+  bool cur_loc_changed;
+
+  if (dv_is_value_p (var->dv)
+      || TREE_CODE (dv_as_decl (var->dv)) == DEBUG_EXPR_DECL)
+    return 1;
+
+  for (i = 0; i < var->n_var_parts; i++)
+    {
+      if (var->var_part[i].cur_loc
+         && vt_expand_loc_dummy (var->var_part[i].cur_loc, vars,
+                                 &cur_loc_changed))
+       {
+         if (cur_loc_changed)
+           var->cur_loc_changed = true;
+         continue;
+       }
+      for (lc = var->var_part[i].loc_chain; lc; lc = lc->next)
+       if (lc->loc != var->var_part[i].cur_loc
+           && vt_expand_loc_dummy (lc->loc, vars, &cur_loc_changed))
+         break;
+      if (lc || var->var_part[i].cur_loc)
+       var->cur_loc_changed = true;
+      var->var_part[i].cur_loc = lc ? lc->loc : NULL_RTX;
+    }
+  if (var->n_var_parts == 0)
+    var->cur_loc_changed = true;
+  return 1;
+}
+
 /* Emit NOTE_INSN_VAR_LOCATION note for each variable from a chain
    CHANGED_VARIABLES and delete this chain.  WHERE specifies whether the notes
    shall be emitted before of after instruction INSN.  */
@@ -6881,6 +7094,7 @@ emit_notes_for_changes (rtx insn, enum emit_note_where where,
       while (VEC_length (variable, changed_variables_stack) > 0)
        check_changed_vars_2 (VEC_pop (variable, changed_variables_stack),
                              htab);
+      htab_traverse (changed_variables, check_changed_vars_3, htab);
     }
 
   data.insn = insn;
@@ -6912,6 +7126,8 @@ emit_notes_for_differences_1 (void **slot, void *data)
       empty_var->dv = old_var->dv;
       empty_var->refcount = 0;
       empty_var->n_var_parts = 0;
+      empty_var->cur_loc_changed = false;
+      empty_var->in_changed_variables = false;
       if (dv_onepart_p (old_var->dv))
        {
          location_chain lc;
@@ -6923,8 +7139,10 @@ emit_notes_for_differences_1 (void **slot, void *data)
            remove_cselib_value_chains (old_var->dv);
        }
       variable_was_changed (empty_var, NULL);
+      /* Continue traversing the hash table.  */
+      return 1;
     }
-  else if (variable_different_p (old_var, new_var, true))
+  if (variable_different_p (old_var, new_var))
     {
       if (dv_onepart_p (old_var->dv))
        {
@@ -6949,6 +7167,33 @@ emit_notes_for_differences_1 (void **slot, void *data)
        }
       variable_was_changed (new_var, NULL);
     }
+  /* Update cur_loc.  */
+  if (old_var != new_var)
+    {
+      int i;
+      for (i = 0; i < new_var->n_var_parts; i++)
+       {
+         new_var->var_part[i].cur_loc = NULL;
+         if (old_var->n_var_parts != new_var->n_var_parts
+             || old_var->var_part[i].offset != new_var->var_part[i].offset)
+           new_var->cur_loc_changed = true;
+         else if (old_var->var_part[i].cur_loc != NULL)
+           {
+             location_chain lc;
+             rtx cur_loc = old_var->var_part[i].cur_loc;
+
+             for (lc = new_var->var_part[i].loc_chain; lc; lc = lc->next)
+               if (lc->loc == cur_loc
+                   || rtx_equal_p (cur_loc, lc->loc))
+                 {
+                   new_var->var_part[i].cur_loc = lc->loc;
+                   break;
+                 }
+             if (lc == NULL)
+               new_var->cur_loc_changed = true;
+           }
+       }
+    }
 
   /* Continue traversing the hash table.  */
   return 1;
@@ -6968,6 +7213,7 @@ emit_notes_for_differences_2 (void **slot, void *data)
                                            dv_htab_hash (new_var->dv));
   if (!old_var)
     {
+      int i;
       /* Variable has appeared.  */
       if (dv_onepart_p (new_var->dv))
        {
@@ -6979,6 +7225,8 @@ emit_notes_for_differences_2 (void **slot, void *data)
          if (dv_is_value_p (new_var->dv))
            add_cselib_value_chains (new_var->dv);
        }
+      for (i = 0; i < new_var->n_var_parts; i++)
+       new_var->var_part[i].cur_loc = NULL;
       variable_was_changed (new_var, NULL);
     }
 
@@ -7283,6 +7531,9 @@ vt_emit_notes (void)
   basic_block bb;
   dataflow_set cur;
 
+#ifdef ENABLE_RTL_CHECKING
+  emitted_notes = pointer_map_create ();
+#endif
   gcc_assert (!htab_elements (changed_variables));
 
   /* Free memory occupied by the out hash tables, as they aren't used
@@ -7324,6 +7575,9 @@ vt_emit_notes (void)
   if (MAY_HAVE_DEBUG_INSNS)
     VEC_free (variable, heap, changed_variables_stack);
 
+#ifdef ENABLE_RTL_CHECKING
+  pointer_map_destroy (emitted_notes);
+#endif
   emit_notes = false;
 }