[PATCH] Fix var-tracking cur_loc handling (PR debug/43176)

Richard Guenther rguenther@suse.de
Sun Mar 7 15:50:00 GMT 2010


On Sat, 6 Mar 2010, Jakub Jelinek wrote:

> Hi!
> 
> The problem in this PR is that var-tracking uses cur_loc field (of variable
> parts) to decide if a variable (or VALUE/DEBUG_EXPR_DECL some variable uses
> in computing its actual value) has changed (== a new location note needs to
> be emitted for it and the location of the variable recomputed), but what
> actually is emitted into the location might be very different from that.
> E.g. in vla-1.c (f2) on x86_64 the emitted note uses r12 register to compute
> array bound, but cur_loc says rdi register.  At that point rdi and r12 hold
> the same value, but when a few insns later r12 is changed, and thus the
> expression is no longer correct, var-tracking doesn't notice, as it thinks
> the location is based on rdi register instead.  When rdi finally is
> clobbered, a new location note is emitted.
> 
> The following patch rewrites the cur_loc handling.  cur_loc is now supposed
> to be always NULL before vt_emit_notes (mainly during vt_find_locations),
> is maintained only in the current variable set, set to location chain entry
> that was actually used to compute the location emitted into notes and at bb
> boundary is propagated to the variables in the new set (if the old cur_loc
> is in the new bb's in variables location chain, it is set to the new
> location chain's loc, otherwise it is reset).
> To avoid creating GC garbage, there is a new special variant of
> cselib_expand_value_rtx_cb that calls the callback with the same values as
> the normal routine would do, but doesn't actually create any new rtxs and
> attempt to simplify them, it only returns true whether normal
> cselib_expand_value_rtx_cb would return non-NULL and false if it would
> return NULL.  This routine is used to decide which among the set of
> changed_variables in the current emit_notes_for_changes call need to
> actually change cur_loc.  emit_note_insn_var_location then just uses the
> computed cur_loc location.
> 
> The patch should decrease GC memory allocation during vt_emit_notes, because
> it won't emit notes for variables whose location didn't actually change.
> 
> There are several bugfixes I've discovered when testing the patch, e.g.
> loc_cmp returned 0 (the same value) for (debug_expr D#1) and (debug_expr
> D#2), so at bb boundaries var-tracking failed to notice a change.
> Or handling of changes to variables for which variable_was_changed had been
> already called.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux
> (--enable-checking=yes,rtl, also --enable-checking=yes and
> --enable-checking=yes,rtl with a hack to disable -fvar-tracking-assignments
> by default).  A backport of this patch (identical except trainling
> whitespace changes) to redhat/gcc-4_4-branch has been bootstrapped/regtested
> with --enable-checking=yes,rtl on {x86_64,i686,ppc,ppc64,s390,s390x}-linux.
> 
> Ok for trunk?

Ok.

Thanks,
Richard.

> 2010-03-06  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.
> 
> --- gcc/Makefile.in.jj	2010-03-05 17:13:54.000000000 +0100
> +++ gcc/Makefile.in	2010-03-05 17:14:02.000000000 +0100
> @@ -3025,7 +3025,7 @@ var-tracking.o : var-tracking.c $(CONFIG
>     $(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 \
> --- gcc/cselib.c.jj	2010-03-05 13:03:03.000000000 +0100
> +++ gcc/cselib.c	2010-03-05 13:04:54.000000000 +0100
> @@ -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, bitma
>    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, bi
>    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, str
>       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, str
>  						    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, str
>        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, str
>  	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,
> --- gcc/cselib.h.jj	2010-03-05 13:03:03.000000000 +0100
> +++ gcc/cselib.h	2010-03-05 13:04:54.000000000 +0100
> @@ -81,7 +81,9 @@ extern int references_value_p (const_rtx
>  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);
>  
> --- gcc/var-tracking.c.jj	2010-03-05 13:03:03.000000000 +0100
> +++ gcc/var-tracking.c	2010-03-05 17:13:05.000000000 +0100
> @@ -112,6 +112,7 @@
>  #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
>  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, voi
>    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, voi
>  	  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, voi
>    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, d
>      {
>        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);
> @@ -2500,6 +2466,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])
> @@ -2739,6 +2717,13 @@ canonicalize_loc_order_check (void **slo
>    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;
>  
> @@ -3101,9 +3086,11 @@ variable_merge_over_cur (void **s1slot, 
>  	      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,
> @@ -3233,6 +3220,8 @@ variable_merge_over_cur (void **s1slot, 
>  		      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;
> @@ -3269,11 +3258,7 @@ variable_merge_over_cur (void **s1slot, 
>        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;
>  }
> @@ -3297,7 +3282,7 @@ variable_merge_over_src (void **s2slot, 
>        void **dstp = shared_hash_find_slot (dst->vars, dv);
>        *dstp = s2var;
>        s2var->refcount++;
> -      return variable_canonicalize (dstp, dst);
> +      return 1;
>      }
>  
>    dsm->src_onepart_cnt++;
> @@ -3766,13 +3751,14 @@ dataflow_set_preserve_mem_locs (void **s
>      {
>        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)
>  	    {
> @@ -3829,6 +3815,12 @@ dataflow_set_preserve_mem_locs (void **s
>  	    {
>  	      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);
>  		}
> @@ -3837,7 +3829,15 @@ dataflow_set_preserve_mem_locs (void **s
>  	    }
>  
>  	  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);
>  	}
> @@ -3847,8 +3847,10 @@ dataflow_set_preserve_mem_locs (void **s
>  	  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;
> @@ -3870,7 +3872,7 @@ dataflow_set_remove_mem_locs (void **slo
>  
>        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
> @@ -3901,9 +3903,12 @@ dataflow_set_remove_mem_locs (void **slo
>  	  /* 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);
>  	}
>  
> @@ -3912,14 +3917,10 @@ dataflow_set_remove_mem_locs (void **slo
>  	  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;
> @@ -4007,13 +4008,10 @@ onepart_variable_different_p (variable v
>    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;
>  
> @@ -4027,16 +4025,6 @@ variable_different_p (variable var1, var
>      {
>        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))
>  	{
> @@ -4078,7 +4066,7 @@ dataflow_set_different_1 (void **slot, v
>        return 0;
>      }
>  
> -  if (variable_different_p (var1, var2, false))
> +  if (variable_different_p (var1, var2))
>      {
>        dataflow_set_different_value = true;
>  
> @@ -5999,6 +5987,7 @@ variable_was_changed (variable var, data
>    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);
> @@ -6007,6 +5996,14 @@ variable_was_changed (variable var, data
>  				       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;
> @@ -6015,12 +6012,19 @@ variable_was_changed (variable var, data
>  	  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;
>  	}
>      }
> @@ -6103,6 +6107,8 @@ set_slot_part (dataflow_set *set, rtx lo
>        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;
> @@ -6200,7 +6206,7 @@ set_slot_part (dataflow_set *set, rtx lo
>        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;
> @@ -6239,7 +6245,7 @@ set_slot_part (dataflow_set *set, rtx lo
>  	  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;
> @@ -6251,7 +6257,7 @@ set_slot_part (dataflow_set *set, rtx lo
>  	  /* 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;
> @@ -6288,6 +6294,11 @@ set_slot_part (dataflow_set *set, rtx lo
>  		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;
> @@ -6312,10 +6323,7 @@ set_slot_part (dataflow_set *set, rtx lo
>  
>    /* 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;
>  }
> @@ -6443,7 +6451,7 @@ delete_slot_part (dataflow_set *set, rtx
>        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.  */
> @@ -6463,6 +6471,7 @@ delete_slot_part (dataflow_set *set, rtx
>  	}
>  
>        /* Delete the location part.  */
> +      changed = false;
>        nextp = &var->var_part[pos].loc_chain;
>        for (node = *nextp; node; node = next)
>  	{
> @@ -6473,6 +6482,15 @@ delete_slot_part (dataflow_set *set, rtx
>  	    {
>  	      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;
> @@ -6481,28 +6499,16 @@ delete_slot_part (dataflow_set *set, rtx
>  	    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];
> @@ -6531,6 +6537,27 @@ delete_variable_part (dataflow_set *set,
>    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.  */
> @@ -6538,7 +6565,10 @@ delete_variable_part (dataflow_set *set,
>  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;
> @@ -6547,11 +6577,6 @@ vt_expand_loc_callback (rtx x, bitmap re
>    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);
> @@ -6559,13 +6584,16 @@ vt_expand_loc_callback (rtx x, bitmap re
>        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;
> @@ -6587,25 +6615,78 @@ vt_expand_loc_callback (rtx x, bitmap re
>    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;
> @@ -6621,18 +6702,46 @@ vt_expand_loc_callback (rtx x, bitmap re
>  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.  */
> @@ -6644,7 +6753,7 @@ emit_note_insn_var_location (void **varp
>    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;
> @@ -6653,20 +6762,34 @@ emit_note_insn_var_location (void **varp
>    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;
> @@ -6680,15 +6803,26 @@ emit_note_insn_var_location (void **varp
>        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.  */
> @@ -6698,11 +6832,12 @@ emit_note_insn_var_location (void **varp
>  	  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;
>  
> @@ -6761,31 +6896,20 @@ emit_note_insn_var_location (void **varp
>    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)
>      {
> @@ -6797,17 +6921,64 @@ emit_note_insn_var_location (void **varp
>  
>        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);
> @@ -6879,6 +7050,48 @@ check_changed_vars_2 (variable var, 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.  */
> @@ -6902,6 +7115,7 @@ emit_notes_for_changes (rtx insn, enum e
>        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;
> @@ -6933,6 +7147,8 @@ emit_notes_for_differences_1 (void **slo
>        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;
> @@ -6944,8 +7160,10 @@ emit_notes_for_differences_1 (void **slo
>  	    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))
>  	{
> @@ -6970,6 +7188,33 @@ emit_notes_for_differences_1 (void **slo
>  	}
>        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;
> @@ -6989,6 +7234,7 @@ emit_notes_for_differences_2 (void **slo
>  					    dv_htab_hash (new_var->dv));
>    if (!old_var)
>      {
> +      int i;
>        /* Variable has appeared.  */
>        if (dv_onepart_p (new_var->dv))
>  	{
> @@ -7000,6 +7246,8 @@ emit_notes_for_differences_2 (void **slo
>  	  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);
>      }
>  
> @@ -7304,6 +7552,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
> @@ -7345,6 +7596,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;
>  }
>  
> 
> 	Jakub
> 
> 

-- 
Richard Guenther <rguenther@suse.de>
Novell / SUSE Labs
SUSE LINUX Products GmbH - Nuernberg - AG Nuernberg - HRB 16746 - GF: Markus Rex



More information about the Gcc-patches mailing list