diff mbox

[VTA,PR49310] O(n+m)-ish emit_notes

Message ID or1uua2w2s.fsf@livre.localdomain
State New
Headers show

Commit Message

Alexandre Oliva Oct. 18, 2011, 2:50 p.m. UTC
On Oct 17, 2011, Jakub Jelinek <jakub@redhat.com> wrote:

> On Tue, Oct 11, 2011 at 04:19:52PM -0300, Alexandre Oliva wrote:
>> Here's what I've got so far.  Regstrapped on x86_64-linux-gnu and
>> i686-linux-gnu.  Ok to install?

> I see
> +FAIL: gcc.c-torture/compile/pr19080.c  -O3 -g  (internal compiler error)
> +FAIL: gcc.c-torture/compile/pr19080.c  -O3 -g  (test for excess errors)
> regression with this patch on x86_64-linux (--enable-checking=yes,rtl), ICE
> in var-tracking.  Can you please look at that?

Hey, I'd already fixed that!  It turns out that I posted an outdated
version of the patch, from before moving an incorrect assertion check
into a test.

> Just a style nit, isn't { supposed to go on the next line?

Not sure.  Nearly all multi-line enums in gcc/ have brackets at the end
of the line rather than in the beginning of the subsequent line; I could
only find one that followed what we both think the conventions require.
I changed it, thanks.

> Otherwise looks ok.

Thanks, here's what I'm going to install as soon as I complete a new
regression test with the corrected patch, just to be sure I didn't
accidentally drop any other fixes.

Comments

H.J. Lu Oct. 19, 2011, 5:49 p.m. UTC | #1
On Tue, Oct 18, 2011 at 7:50 AM, Alexandre Oliva <aoliva@redhat.com> wrote:
> On Oct 17, 2011, Jakub Jelinek <jakub@redhat.com> wrote:
>
>> On Tue, Oct 11, 2011 at 04:19:52PM -0300, Alexandre Oliva wrote:
>>> Here's what I've got so far.  Regstrapped on x86_64-linux-gnu and
>>> i686-linux-gnu.  Ok to install?
>
>> I see
>> +FAIL: gcc.c-torture/compile/pr19080.c  -O3 -g  (internal compiler error)
>> +FAIL: gcc.c-torture/compile/pr19080.c  -O3 -g  (test for excess errors)
>> regression with this patch on x86_64-linux (--enable-checking=yes,rtl), ICE
>> in var-tracking.  Can you please look at that?
>
> Hey, I'd already fixed that!  It turns out that I posted an outdated
> version of the patch, from before moving an incorrect assertion check
> into a test.
>
>> Just a style nit, isn't { supposed to go on the next line?
>
> Not sure.  Nearly all multi-line enums in gcc/ have brackets at the end
> of the line rather than in the beginning of the subsequent line; I could
> only find one that followed what we both think the conventions require.
> I changed it, thanks.
>
>> Otherwise looks ok.
>
> Thanks, here's what I'm going to install as soon as I complete a new
> regression test with the corrected patch, just to be sure I didn't
> accidentally drop any other fixes.
>

This caused:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=50799
diff mbox

Patch

for  gcc/ChangeLog
from  Alexandre Oliva  <aoliva@redhat.com>

	PR debug/49310
	* var-tracking.c (loc_exp_dep, onepart_aux): New structs.
	(variable_part): Replace offset with union.
	(enum onepart_enum, onepart_enum_t): New.
	(variable_def): Drop cur_loc_changed, add onepart.
	(value_chain_def, const_value_chain): Remove.
	(VAR_PART_OFFSET, VAR_LOC_1PAUX): New macros, with checking.
	(VAR_LOC_DEP_LST, VAR_LOC_DEP_LSTP): New macros.
	(VAR_LOC_FROM, VAR_LOC_DEPTH, VAR_LOC_DEP_VEC): Likewise.
	(value_chain_pool, value_chains): Remove.
	(dropped_values): New.
	(struct parm_reg): Only if HAVE_window_save.
	(vt_stack_adjustments): Don't record register arguments.
	(dv_as_rtx): New.
	(dv_onepart_p): Return a onepart_enum_t.
	(onepart_pool): New.
	(dv_pool): Remove.
	(dv_from_rtx): New.
	(variable_htab_free): Release onepart aux data.  Reset flags.
	(value_chain_htab_hash, value_chain_htab_eq): Remove.
	(unshare_variable): Use onepart field.  Propagate onepart aux
	data or offset.  Drop cur_loc_changed.
	(val_store): Cope with NULL insn.  Rephrase dump output.  Check
	for unsuitable locs.  Add FIXME on using cselib locs.
	(val_reset): Remove FIXME of unfounded concerns.
	(val_resolve): Check for unsuitable locs.  Add FIXME on using
	cselib locs.
	(variable_union): Use onepart field, adjust access to offset.
	(NO_LOC_P): New.
	(VALUE_CHANGED, DECL_CHANGED): Update doc.
	(set_dv_changed): Clear NO_LOC_P when changed.
	(find_loc_in_1pdv): Use onepart field.
	(intersect_loc_chains): Likewise.
	(unsuitable_loc): New.
	(loc_cmp): Keep ENTRY_VALUEs at the end of the loc list.
	(add_value_chain, add_value_chains): Remove.
	(add_cselib_value_chains, remove_value_chain): Likewise.
	(remove_value_chains, remove_cselib_value_chains): Likewise.
	(canonicalize_loc_order_check): Use onepart.  Drop cur_loc_changed.
	(canonicalize_values_star, canonicalize_vars_star): Use onepart.
	(variable_merge_over_cur): Likewise.  Adjust access to offset.
	Drop cur_loc_changed.
	(variable_merge_over_src): Use onepart field.
	(remove_duplicate_values): Likewise.
	(variable_post_merge_new_vals): Likewise.
	(find_mem_expr_in_1pdv): Likewise.
	(dataflow_set_preserve_mem_locs): Likewise.  Drop cur_loc_changed
	and value chains.
	(dataflow_set_remove_mem_locs): Likewise.  Use VAR_LOC_FROM.
	(variable_different_p): Use onepart field.  Move onepart test out
	of the loop.
	(argument_reg_set): Drop.
	(add_uses, add_stores): Preserve but do not record in dynamic
	tables equivalences for ENTRY_VALUEs and CFA_based addresses.
	Avoid unsuitable address expressions.
	(EXPR_DEPTH): Unlimit.
	(EXPR_USE_DEPTH): Repurpose PARAM_MAX_VARTRACK_EXPR_DEPTH.
	(prepare_call_arguments): Use DECL_RTL_IF_SET.
	(dump_var): Adjust access to offset.
	(variable_from_dropped, recover_dropped_1paux): New.
	(variable_was_changed): Drop cur_loc_changed.  Use onepart.
	Preserve onepart aux in empty_var.  Recover empty_var and onepart
	aux from dropped_values.
	(find_variable_location_part): Special-case onepart.  Adjust
	access to offset.
	(set_slot_part): Use onepart.  Drop cur_loc_changed.  Adjust
	access to offset.  Initialize onepaux.  Drop value chains.
	(delete_slot_part): Drop value chains.  Use VAR_LOC_FROM.
	(VEC (variable, heap), VEC (rtx, stack)): Define.
	(expand_loc_callback_data): Drop dummy, cur_loc_changed,
	ignore_cur_loc.  Add expanding, pending, depth.
	(loc_exp_dep_alloc, loc_exp_dep_clear): New.
	(loc_exp_dep_insert, loc_exp_dep_set): New.
	(notify_dependents_of_resolved_value): New.
	(update_depth, vt_expand_var_loc_chain): New.
	(vt_expand_loc_callback): Revamped.
	(resolve_expansions_pending_recursion): New.
	(INIT_ELCD, FINI_ELCD): New.
	(vt_expand_loc): Use the new macros above.  Drop ignore_cur_loc
	parameter, adjust all callers.
	(vt_expand_loc_dummy): Drop.
	(vt_expand_1pvar): New.
	(emit_note_insn_var_location): Operate on non-debug decls only.
	Revamp multi-part cur_loc recomputation and one-part expansion.
	Drop cur_loc_changed.  Adjust access to offset.
	(VEC (variable, heap)): Drop.
	(changed_variables_stack, changed_values_stack): Drop.
	(check_changed_vars_0, check_changed_vars_1): Remove.
	(check_changed_vars_2, check_changed_vars_3): Remove.
	(values_to_stack, remove_value_from_changed_variables): New.
	(notify_dependents_of_changed_value, process_changed_values): New.
	(emit_notes_for_changes): Revamp onepart updates.
	(emit_notes_for_differences_1): Use onepart.  Drop cur_loc_changed
	and value chains.  Propagate onepaux.  Recover empty_var and onepaux
	from dropped_values.
	(emit_notes_for_differences_2): Drop value chains.
	(emit_notes_in_bb): Adjust.
	(vt_emit_notes): Drop value chains, changed_variables_stack.
	Initialize and release dropped_values.
	(create_entry_value): Revamp.
	(vt_add_function_parameter): Use new interface.
	(note_register_arguments): Remove.
	(vt_initialize): Drop value chains and register arguments.
	(vt_finalize): Drop value chains.  Release windowed_parm_regs only
	if HAVE_window_save.
	* rtl.h: Document various pass-local uses of RTL flags.
	* tree.h (DECL_RTL_KNOWN_SET): New.
	* doc/invoke.texi (param max-vartrack-expr-depth): Update
	description and default.

Index: gcc/var-tracking.c
===================================================================
--- gcc/var-tracking.c.orig	2011-10-18 11:37:14.435008204 -0200
+++ gcc/var-tracking.c	2011-10-18 12:37:35.969008114 -0200
@@ -301,6 +301,48 @@  typedef struct location_chain_def
   enum var_init_status init;
 } *location_chain;
 
+/* A vector of loc_exp_dep holds the active dependencies of a one-part
+   DV on VALUEs, i.e., the VALUEs expanded so as to form the current
+   location of DV.  Each entry is also part of VALUE' s linked-list of
+   backlinks back to DV.  */
+typedef struct loc_exp_dep_s
+{
+  /* The dependent DV.  */
+  decl_or_value dv;
+  /* The dependency VALUE or DECL_DEBUG.  */
+  rtx value;
+  /* The next entry in VALUE's backlinks list.  */
+  struct loc_exp_dep_s *next;
+  /* A pointer to the pointer to this entry (head or prev's next) in
+     the doubly-linked list.  */
+  struct loc_exp_dep_s **pprev;
+} loc_exp_dep;
+
+DEF_VEC_O (loc_exp_dep);
+
+/* This data structure is allocated for one-part variables at the time
+   of emitting notes.  */
+struct onepart_aux
+{
+  /* Doubly-linked list of dependent DVs.  These are DVs whose cur_loc
+     computation used the expansion of this variable, and that ought
+     to be notified should this variable change.  If the DV's cur_loc
+     expanded to NULL, all components of the loc list are regarded as
+     active, so that any changes in them give us a chance to get a
+     location.  Otherwise, only components of the loc that expanded to
+     non-NULL are regarded as active dependencies.  */
+  loc_exp_dep *backlinks;
+  /* This holds the LOC that was expanded into cur_loc.  We need only
+     mark a one-part variable as changed if the FROM loc is removed,
+     or if it has no known location and a loc is added, or if it gets
+     a change notification from any of its active dependencies.  */
+  rtx from;
+  /* The depth of the cur_loc expression.  */
+  int depth;
+  /* Dependencies actively used when expand FROM into cur_loc.  */
+  VEC (loc_exp_dep, none) deps;
+};
+
 /* Structure describing one part of variable.  */
 typedef struct variable_part_def
 {
@@ -310,13 +352,33 @@  typedef struct variable_part_def
   /* Location which was last emitted to location list.  */
   rtx cur_loc;
 
-  /* The offset in the variable.  */
-  HOST_WIDE_INT offset;
+  union variable_aux
+  {
+    /* The offset in the variable, if !var->onepart.  */
+    HOST_WIDE_INT offset;
+
+    /* Pointer to auxiliary data, if var->onepart and emit_notes.  */
+    struct onepart_aux *onepaux;
+  } aux;
 } variable_part;
 
 /* Maximum number of location parts.  */
 #define MAX_VAR_PARTS 16
 
+/* Enumeration type used to discriminate various types of one-part
+   variables.  */
+typedef enum onepart_enum
+{
+  /* Not a one-part variable.  */
+  NOT_ONEPART = 0,
+  /* A one-part DECL that is not a DEBUG_EXPR_DECL.  */
+  ONEPART_VDECL = 1,
+  /* A DEBUG_EXPR_DECL.  */
+  ONEPART_DEXPR = 2,
+  /* A VALUE.  */
+  ONEPART_VALUE = 3
+} onepart_enum_t;
+
 /* Structure describing where the variable is located.  */
 typedef struct variable_def
 {
@@ -330,10 +392,8 @@  typedef struct variable_def
   /* Number of variable 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;
+  /* What type of DV this is, according to enum onepart_enum.  */
+  ENUM_BITFIELD (onepart_enum) onepart : CHAR_BIT;
 
   /* True if this variable_def struct is currently in the
      changed_variables hash table.  */
@@ -344,29 +404,48 @@  typedef struct variable_def
 } *variable;
 typedef const struct variable_def *const_variable;
 
-/* Structure for chaining backlinks from referenced VALUEs to
-   DVs that are referencing them.  */
-typedef struct value_chain_def
-{
-  /* Next value_chain entry.  */
-  struct value_chain_def *next;
-
-  /* The declaration of the variable, or an RTL value
-     being handled like a declaration, whose var_parts[0].loc_chain
-     references the VALUE owning this value_chain.  */
-  decl_or_value dv;
-
-  /* Reference count.  */
-  int refcount;
-} *value_chain;
-typedef const struct value_chain_def *const_value_chain;
-
 /* Pointer to the BB's information specific to variable tracking pass.  */
 #define VTI(BB) ((variable_tracking_info) (BB)->aux)
 
 /* Macro to access MEM_OFFSET as an HOST_WIDE_INT.  Evaluates MEM twice.  */
 #define INT_MEM_OFFSET(mem) (MEM_OFFSET_KNOWN_P (mem) ? MEM_OFFSET (mem) : 0)
 
+#if ENABLE_CHECKING && (GCC_VERSION >= 2007)
+
+/* Access VAR's Ith part's offset, checking that it's not a one-part
+   variable.  */
+#define VAR_PART_OFFSET(var, i) __extension__			\
+(*({  variable const __v = (var);				\
+      gcc_checking_assert (!__v->onepart);			\
+      &__v->var_part[(i)].aux.offset; }))
+
+/* Access VAR's one-part auxiliary data, checking that it is a
+   one-part variable.  */
+#define VAR_LOC_1PAUX(var) __extension__			\
+(*({  variable const __v = (var);				\
+      gcc_checking_assert (__v->onepart);			\
+      &__v->var_part[0].aux.onepaux; }))
+
+#else
+#define VAR_PART_OFFSET(var, i) ((var)->var_part[(i)].aux.offset)
+#define VAR_LOC_1PAUX(var) ((var)->var_part[0].aux.onepaux)
+#endif
+
+/* These are accessor macros for the one-part auxiliary data.  When
+   convenient for users, they're guarded by tests that the data was
+   allocated.  */
+#define VAR_LOC_DEP_LST(var) (VAR_LOC_1PAUX (var)		  \
+			      ? VAR_LOC_1PAUX (var)->backlinks	  \
+			      : NULL)
+#define VAR_LOC_DEP_LSTP(var) (VAR_LOC_1PAUX (var)		  \
+			       ? &VAR_LOC_1PAUX (var)->backlinks  \
+			       : NULL)
+#define VAR_LOC_FROM(var) (VAR_LOC_1PAUX (var)->from)
+#define VAR_LOC_DEPTH(var) (VAR_LOC_1PAUX (var)->depth)
+#define VAR_LOC_DEP_VEC(var) (VAR_LOC_1PAUX (var)		  \
+			      ? &VAR_LOC_1PAUX (var)->deps	  \
+			      : NULL)
+
 /* Alloc pool for struct attrs_def.  */
 static alloc_pool attrs_pool;
 
@@ -382,24 +461,24 @@  static alloc_pool loc_chain_pool;
 /* Alloc pool for struct shared_hash_def.  */
 static alloc_pool shared_hash_pool;
 
-/* Alloc pool for struct value_chain_def.  */
-static alloc_pool value_chain_pool;
-
 /* Changed variables, notes will be emitted for them.  */
 static htab_t changed_variables;
 
-/* Links from VALUEs to DVs referencing them in their current loc_chains.  */
-static htab_t value_chains;
-
 /* Shall notes be emitted?  */
 static bool emit_notes;
 
+/* Values whose dynamic location lists have gone empty, but whose
+   cselib location lists are still usable.  Use this to hold the
+   current location, the backlinks, etc, during emit_notes.  */
+static htab_t dropped_values;
+
 /* Empty shared hashtable.  */
 static shared_hash empty_shared_hash;
 
 /* Scratch register bitmap used by cselib_expand_value_rtx.  */
 static bitmap scratch_regs = NULL;
 
+#ifdef HAVE_window_save
 typedef struct GTY(()) parm_reg {
   rtx outgoing;
   rtx incoming;
@@ -410,6 +489,7 @@  DEF_VEC_ALLOC_O(parm_reg_t, gc);
 
 /* Vector of windowed parameter registers, if any.  */
 static VEC(parm_reg_t, gc) *windowed_parm_regs = NULL;
+#endif
 
 /* Variable used to tell whether cselib_process_insn called our hook.  */
 static bool cselib_hook_called;
@@ -420,7 +500,6 @@  static void stack_adjust_offset_pre_post
 static void insn_stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
 					       HOST_WIDE_INT *);
 static bool vt_stack_adjustments (void);
-static void note_register_arguments (rtx);
 static hashval_t variable_htab_hash (const void *);
 static int variable_htab_eq (const void *, const void *);
 static void variable_htab_free (void *);
@@ -476,6 +555,7 @@  static void dump_vars (htab_t);
 static void dump_dataflow_set (dataflow_set *);
 static void dump_dataflow_sets (void);
 
+static void set_dv_changed (decl_or_value, bool);
 static void variable_was_changed (variable, dataflow_set *);
 static void **set_slot_part (dataflow_set *, rtx, void **,
 			     decl_or_value, HOST_WIDE_INT,
@@ -672,15 +752,11 @@  vt_stack_adjustments (void)
 	    for (insn = BB_HEAD (dest);
 		 insn != NEXT_INSN (BB_END (dest));
 		 insn = NEXT_INSN (insn))
-	      {
-		if (INSN_P (insn))
-		  {
-		    insn_stack_adjust_offset_pre_post (insn, &pre, &post);
-		    offset += pre + post;
-		  }
-		if (CALL_P (insn))
-		  note_register_arguments (insn);
-	      }
+	      if (INSN_P (insn))
+		{
+		  insn_stack_adjust_offset_pre_post (insn, &pre, &post);
+		  offset += pre + post;
+		}
 
 	  VTI (dest)->out.stack_adjust = offset;
 
@@ -1133,6 +1209,21 @@  dv_as_value (decl_or_value dv)
   return (rtx)dv;
 }
 
+/* Return the DEBUG_EXPR of a DEBUG_EXPR_DECL or the VALUE in DV.  */
+static inline rtx
+dv_as_rtx (decl_or_value dv)
+{
+  tree decl;
+
+  if (dv_is_value_p (dv))
+    return dv_as_value (dv);
+
+  decl = dv_as_decl (dv);
+
+  gcc_checking_assert (TREE_CODE (decl) == DEBUG_EXPR_DECL);
+  return DECL_RTL_KNOWN_SET (decl);
+}
+
 /* Return the opaque pointer in the decl_or_value.  */
 static inline void *
 dv_as_opaque (decl_or_value dv)
@@ -1140,36 +1231,36 @@  dv_as_opaque (decl_or_value dv)
   return dv;
 }
 
-/* Return true if a decl_or_value must not have more than one variable
-   part.  */
-static inline bool
+/* Return nonzero if a decl_or_value must not have more than one
+   variable part.  The returned value discriminates among various
+   kinds of one-part DVs ccording to enum onepart_enum.  */
+static inline onepart_enum_t
 dv_onepart_p (decl_or_value dv)
 {
   tree decl;
 
   if (!MAY_HAVE_DEBUG_INSNS)
-    return false;
+    return NOT_ONEPART;
 
   if (dv_is_value_p (dv))
-    return true;
+    return ONEPART_VALUE;
 
   decl = dv_as_decl (dv);
 
-  if (!decl)
-    return true;
-
   if (TREE_CODE (decl) == DEBUG_EXPR_DECL)
-    return true;
+    return ONEPART_DEXPR;
+
+  if (target_for_debug_bind (decl) != NULL_TREE)
+    return ONEPART_VDECL;
 
-  return (target_for_debug_bind (decl) != NULL_TREE);
+  return NOT_ONEPART;
 }
 
-/* Return the variable pool to be used for dv, depending on whether it
-   can have multiple parts or not.  */
+/* Return the variable pool to be used for a dv of type ONEPART.  */
 static inline alloc_pool
-dv_pool (decl_or_value dv)
+onepart_pool (onepart_enum_t onepart)
 {
-  return dv_onepart_p (dv) ? valvar_pool : var_pool;
+  return onepart ? valvar_pool : var_pool;
 }
 
 /* Build a decl_or_value out of a decl.  */
@@ -1192,6 +1283,30 @@  dv_from_value (rtx value)
   return dv;
 }
 
+/* Return a value or the decl of a debug_expr as a decl_or_value.  */
+static inline decl_or_value
+dv_from_rtx (rtx x)
+{
+  decl_or_value dv;
+
+  switch (GET_CODE (x))
+    {
+    case DEBUG_EXPR:
+      dv = dv_from_decl (DEBUG_EXPR_TREE_DECL (x));
+      gcc_checking_assert (DECL_RTL_KNOWN_SET (DEBUG_EXPR_TREE_DECL (x)) == x);
+      break;
+
+    case VALUE:
+      dv = dv_from_value (x);
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  return dv;
+}
+
 extern void debug_dv (decl_or_value dv);
 
 DEBUG_FUNCTION void
@@ -1254,6 +1369,8 @@  variable_htab_eq (const void *x, const v
   return (dv_as_opaque (v->dv) == dv_as_opaque (dv));
 }
 
+static void loc_exp_dep_clear (variable var);
+
 /* Free the element of VARIABLE_HTAB (its type is struct variable_def).  */
 
 static void
@@ -1278,29 +1395,18 @@  variable_htab_free (void *elem)
 	}
       var->var_part[i].loc_chain = NULL;
     }
-  pool_free (dv_pool (var->dv), var);
-}
-
-/* The hash function for value_chains htab, computes the hash value
-   from the VALUE.  */
-
-static hashval_t
-value_chain_htab_hash (const void *x)
-{
-  const_value_chain const v = (const_value_chain) x;
-
-  return dv_htab_hash (v->dv);
-}
-
-/* Compare the VALUE X with VALUE Y.  */
-
-static int
-value_chain_htab_eq (const void *x, const void *y)
-{
-  const_value_chain const v = (const_value_chain) x;
-  decl_or_value dv = CONST_CAST2 (decl_or_value, const void *, y);
-
-  return dv_as_opaque (v->dv) == dv_as_opaque (dv);
+  if (var->onepart && VAR_LOC_1PAUX (var))
+    {
+      loc_exp_dep_clear (var);
+      if (VAR_LOC_DEP_LST (var))
+	VAR_LOC_DEP_LST (var)->pprev = NULL;
+      XDELETE (VAR_LOC_1PAUX (var));
+      /* These may be reused across functions, so reset
+	 e.g. NO_LOC_P.  */
+      if (var->onepart == ONEPART_DEXPR)
+	set_dv_changed (var->dv, true);
+    }
+  pool_free (onepart_pool (var->onepart), var);
 }
 
 /* Initialize the set (array) SET of attrs to empty lists.  */
@@ -1569,13 +1675,12 @@  unshare_variable (dataflow_set *set, voi
   variable new_var;
   int i;
 
-  new_var = (variable) pool_alloc (dv_pool (var->dv));
+  new_var = (variable) pool_alloc (onepart_pool (var->onepart));
   new_var->dv = var->dv;
   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->onepart = var->onepart;
   new_var->in_changed_variables = false;
 
   if (! flag_var_tracking_uninit)
@@ -1586,7 +1691,18 @@  unshare_variable (dataflow_set *set, voi
       location_chain node;
       location_chain *nextp;
 
-      new_var->var_part[i].offset = var->var_part[i].offset;
+      if (i == 0 && var->onepart)
+	{
+	  /* One-part auxiliary data is only used while emitting
+	     notes, so propagate it to the new variable in the active
+	     dataflow set.  If we're not emitting notes, this will be
+	     a no-op.  */
+	  gcc_checking_assert (!VAR_LOC_1PAUX (var) || emit_notes);
+	  VAR_LOC_1PAUX (new_var) = VAR_LOC_1PAUX (var);
+	  VAR_LOC_1PAUX (var) = NULL;
+	}
+      else
+	VAR_PART_OFFSET (new_var, i) = VAR_PART_OFFSET (var, i);
       nextp = &new_var->var_part[i].loc_chain;
       for (node = var->var_part[i].loc_chain; node; node = node->next)
 	{
@@ -1891,6 +2007,26 @@  var_mem_delete (dataflow_set *set, rtx l
   delete_variable_part (set, loc, dv_from_decl (decl), offset);
 }
 
+/* Return true if LOC should not be expanded for location expressions,
+   or used in them.  */
+
+static inline bool
+unsuitable_loc (rtx loc)
+{
+  switch (GET_CODE (loc))
+    {
+    case PC:
+    case SCRATCH:
+    case CC0:
+    case ASM_INPUT:
+    case ASM_OPERANDS:
+      return true;
+
+    default:
+      return false;
+    }
+}
+
 /* Bind a value to a location it was just stored in.  If MODIFIED
    holds, assume the location was modified, detaching it from any
    values bound to it.  */
@@ -1904,10 +2040,10 @@  val_store (dataflow_set *set, rtx val, r
 
   if (dump_file)
     {
-      fprintf (dump_file, "%i: ", INSN_UID (insn));
-      print_inline_rtx (dump_file, val, 0);
-      fprintf (dump_file, " stored in ");
+      fprintf (dump_file, "%i: ", insn ? INSN_UID (insn) : 0);
       print_inline_rtx (dump_file, loc, 0);
+      fprintf (dump_file, " evaluates to ");
+      print_inline_rtx (dump_file, val, 0);
       if (v->locs)
 	{
 	  struct elt_loc_list *l;
@@ -1920,6 +2056,8 @@  val_store (dataflow_set *set, rtx val, r
       fprintf (dump_file, "\n");
     }
 
+  gcc_checking_assert (!unsuitable_loc (loc));
+
   if (REG_P (loc))
     {
       if (modified)
@@ -1931,6 +2069,8 @@  val_store (dataflow_set *set, rtx val, r
     var_mem_decl_set (set, loc, VAR_INIT_STATUS_INITIALIZED,
 		      dv_from_value (val), 0, NULL_RTX, INSERT);
   else
+    /* ??? Ideally we wouldn't get these, and use them from the static
+       cselib loc list.  */
     set_variable_part (set, loc, dv_from_value (val), 0,
 		       VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT);
 }
@@ -1997,13 +2137,6 @@  val_reset (dataflow_set *set, decl_or_va
     delete_variable_part (set, dv_as_value (dv), dv_from_value (cval), 0);
 
   clobber_variable_part (set, NULL, dv, 0, NULL);
-
-  /* ??? Should we make sure there aren't other available values or
-     variables whose values involve this one other than by
-     equivalence?  E.g., at the very least we should reset MEMs, those
-     shouldn't be too hard to find cselib-looking up the value as an
-     address, then locating the resulting value in our own hash
-     table.  */
 }
 
 /* Find the values in a given location and map the val to another
@@ -2029,6 +2162,8 @@  val_resolve (dataflow_set *set, rtx val,
 
   val_reset (set, dv);
 
+  gcc_checking_assert (!unsuitable_loc (loc));
+
   if (REG_P (loc))
     {
       attrs node, found = NULL;
@@ -2061,6 +2196,8 @@  val_resolve (dataflow_set *set, rtx val,
     var_mem_decl_set (set, loc, VAR_INIT_STATUS_INITIALIZED,
 		      dv_from_value (val), 0, NULL_RTX, INSERT);
   else
+    /* ??? Ideally we wouldn't get these, and use them from the static
+       cselib loc list.  */
     /* ??? Merge equivalent expressions.  */
     set_variable_part (set, loc, dv_from_value (val), 0,
 		       VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT);
@@ -2176,10 +2313,11 @@  variable_union (variable src, dataflow_s
     dst = (variable) *dstp;
 
   gcc_assert (src->n_var_parts);
+  gcc_checking_assert (src->onepart == dst->onepart);
 
   /* We can combine one-part variables very efficiently, because their
      entries are in canonical order.  */
-  if (dv_onepart_p (src->dv))
+  if (src->onepart)
     {
       location_chain *nodep, dnode, snode;
 
@@ -2233,16 +2371,18 @@  variable_union (variable src, dataflow_s
       return 1;
     }
 
+  gcc_checking_assert (!src->onepart);
+
   /* Count the number of location parts, result is K.  */
   for (i = 0, j = 0, k = 0;
        i < src->n_var_parts && j < dst->n_var_parts; k++)
     {
-      if (src->var_part[i].offset == dst->var_part[j].offset)
+      if (VAR_PART_OFFSET (src, i) == VAR_PART_OFFSET (dst, j))
 	{
 	  i++;
 	  j++;
 	}
-      else if (src->var_part[i].offset < dst->var_part[j].offset)
+      else if (VAR_PART_OFFSET (src, i) < VAR_PART_OFFSET (dst, j))
 	i++;
       else
 	j++;
@@ -2252,7 +2392,7 @@  variable_union (variable src, dataflow_s
 
   /* We track only variables whose size is <= MAX_VAR_PARTS bytes
      thus there are at most MAX_VAR_PARTS different offsets.  */
-  gcc_assert (dv_onepart_p (dst->dv) ? k == 1 : k <= MAX_VAR_PARTS);
+  gcc_checking_assert (dst->onepart ? k == 1 : k <= MAX_VAR_PARTS);
 
   if (dst->n_var_parts != k && shared_var_p (dst, set->vars))
     {
@@ -2269,7 +2409,7 @@  variable_union (variable src, dataflow_s
       location_chain node, node2;
 
       if (i >= 0 && j >= 0
-	  && src->var_part[i].offset == dst->var_part[j].offset)
+	  && VAR_PART_OFFSET (src, i) == VAR_PART_OFFSET (dst, j))
 	{
 	  /* Compute the "sorted" union of the chains, i.e. the locations which
 	     are in both chains go first, they are sorted by the sum of
@@ -2317,7 +2457,7 @@  variable_union (variable src, dataflow_s
 	      /* The most common case, much simpler, no qsort is needed.  */
 	      location_chain dstnode = dst->var_part[j].loc_chain;
 	      dst->var_part[k].loc_chain = dstnode;
-	      dst->var_part[k].offset = dst->var_part[j].offset;
+	      VAR_PART_OFFSET (dst, k) = VAR_PART_OFFSET(dst, j);
 	      node2 = dstnode;
 	      for (node = src->var_part[i].loc_chain; node; node = node->next)
 		if (!((REG_P (dstnode->loc)
@@ -2455,20 +2595,20 @@  variable_union (variable src, dataflow_s
 		  dst->var_part[k].loc_chain = vui[0].lc;
 		}
 
-	      dst->var_part[k].offset = dst->var_part[j].offset;
+	      VAR_PART_OFFSET (dst, k) = VAR_PART_OFFSET (dst, j);
 	    }
 	  i--;
 	  j--;
 	}
       else if ((i >= 0 && j >= 0
-		&& src->var_part[i].offset < dst->var_part[j].offset)
+		&& VAR_PART_OFFSET (src, i) < VAR_PART_OFFSET (dst, j))
 	       || i < 0)
 	{
 	  dst->var_part[k] = dst->var_part[j];
 	  j--;
 	}
       else if ((i >= 0 && j >= 0
-		&& src->var_part[i].offset > dst->var_part[j].offset)
+		&& VAR_PART_OFFSET (src, i) > VAR_PART_OFFSET (dst, j))
 	       || j < 0)
 	{
 	  location_chain *nextp;
@@ -2492,7 +2632,7 @@  variable_union (variable src, dataflow_s
 	      nextp = &new_lc->next;
 	    }
 
-	  dst->var_part[k].offset = src->var_part[i].offset;
+	  VAR_PART_OFFSET (dst, k) = VAR_PART_OFFSET (src, i);
 	  i--;
 	}
       dst->var_part[k].cur_loc = NULL;
@@ -2543,25 +2683,46 @@  dataflow_set_union (dataflow_set *dst, d
 /* Whether the value is currently being expanded.  */
 #define VALUE_RECURSED_INTO(x) \
   (RTL_FLAG_CHECK2 ("VALUE_RECURSED_INTO", (x), VALUE, DEBUG_EXPR)->used)
-/* Whether the value is in changed_variables hash table.  */
+
+/* Whether no expansion was found, saving useless lookups.
+   It must only be set when VALUE_CHANGED is clear.  */
+#define NO_LOC_P(x) \
+  (RTL_FLAG_CHECK2 ("NO_LOC_P", (x), VALUE, DEBUG_EXPR)->return_val)
+
+/* Whether cur_loc in the value needs to be (re)computed.  */
 #define VALUE_CHANGED(x) \
   (RTL_FLAG_CHECK1 ("VALUE_CHANGED", (x), VALUE)->frame_related)
-/* Whether the decl is in changed_variables hash table.  */
+/* Whether cur_loc in the decl needs to be (re)computed.  */
 #define DECL_CHANGED(x) TREE_VISITED (x)
 
-/* Record that DV has been added into resp. removed from changed_variables
-   hashtable.  */
+/* Record (if NEWV) that DV needs to have its cur_loc recomputed.  For
+   user DECLs, this means they're in changed_variables.  Values and
+   debug exprs may be left with this flag set if no user variable
+   requires them to be evaluated.  */
 
 static inline void
 set_dv_changed (decl_or_value dv, bool newv)
 {
-  if (dv_is_value_p (dv))
-    VALUE_CHANGED (dv_as_value (dv)) = newv;
-  else
-    DECL_CHANGED (dv_as_decl (dv)) = newv;
+  switch (dv_onepart_p (dv))
+    {
+    case ONEPART_VALUE:
+      if (newv)
+	NO_LOC_P (dv_as_value (dv)) = false;
+      VALUE_CHANGED (dv_as_value (dv)) = newv;
+      break;
+
+    case ONEPART_DEXPR:
+      if (newv)
+	NO_LOC_P (DECL_RTL_KNOWN_SET (dv_as_decl (dv))) = false;
+      /* Fall through...  */
+
+    default:
+      DECL_CHANGED (dv_as_decl (dv)) = newv;
+      break;
+    }
 }
 
-/* Return true if DV is present in changed_variables hash table.  */
+/* Return true if DV needs to have its cur_loc recomputed.  */
 
 static inline bool
 dv_changed_p (decl_or_value dv)
@@ -2585,12 +2746,11 @@  find_loc_in_1pdv (rtx loc, variable var,
   if (!var)
     return NULL;
 
-  gcc_checking_assert (dv_onepart_p (var->dv));
+  gcc_checking_assert (var->onepart);
 
   if (!var->n_var_parts)
     return NULL;
 
-  gcc_checking_assert (var->var_part[0].offset == 0);
   gcc_checking_assert (loc != dv_as_opaque (var->dv));
 
   loc_code = GET_CODE (loc);
@@ -2639,6 +2799,8 @@  find_loc_in_1pdv (rtx loc, variable var,
       return find_loc_in_1pdv (loc, rvar, vars);
     }
 
+  /* ??? Gotta look in cselib_val locations too.  */
+
   return NULL;
 }
 
@@ -2683,7 +2845,7 @@  insert_into_intersection (location_chain
   *nodep = node;
 }
 
-/* Insert in DEST the intersection the locations present in both
+/* Insert in DEST the intersection of the locations present in both
    S1NODE and S2VAR, directly or indirectly.  S1NODE is from a
    variable in DSM->cur, whereas S2VAR is from DSM->src.  dvar is in
    DSM->dst.  */
@@ -2700,11 +2862,10 @@  intersect_loc_chains (rtx val, location_
     {
       location_chain s2node;
 
-      gcc_checking_assert (dv_onepart_p (s2var->dv));
+      gcc_checking_assert (s2var->onepart);
 
       if (s2var->n_var_parts)
 	{
-	  gcc_checking_assert (s2var->var_part[0].offset == 0);
 	  s2node = s2var->var_part[0].loc_chain;
 
 	  for (; s1node && s2node;
@@ -2750,6 +2911,8 @@  intersect_loc_chains (rtx val, location_
 	    }
 	}
 
+      /* ??? gotta look in cselib_val locations too.  */
+
       /* ??? if the location is equivalent to any location in src,
 	 searched recursively
 
@@ -2839,6 +3002,18 @@  loc_cmp (rtx x, rtx y)
   if (GET_CODE (y) == VALUE)
     return 1;
 
+  /* Entry value is the least preferable kind of expression.  */
+  if (GET_CODE (x) == ENTRY_VALUE)
+    {
+      if (GET_CODE (y) != ENTRY_VALUE)
+	return 1;
+      gcc_assert (GET_MODE (x) == GET_MODE (y));
+      return loc_cmp (XEXP (x, 0), XEXP (y, 0));
+    }
+
+  if (GET_CODE (y) == ENTRY_VALUE)
+    return -1;
+
   if (GET_CODE (x) == GET_CODE (y))
     /* Compare operands below.  */;
   else if (GET_CODE (x) < GET_CODE (y))
@@ -2933,186 +3108,23 @@  loc_cmp (rtx x, rtx y)
   return 0;
 }
 
-/* If decl or value DVP refers to VALUE from *LOC, add backlinks
-   from VALUE to DVP.  */
-
-static int
-add_value_chain (rtx *loc, void *dvp)
-{
-  decl_or_value dv, ldv;
-  value_chain vc, nvc;
-  void **slot;
-
-  if (GET_CODE (*loc) == VALUE)
-    ldv = dv_from_value (*loc);
-  else if (GET_CODE (*loc) == DEBUG_EXPR)
-    ldv = dv_from_decl (DEBUG_EXPR_TREE_DECL (*loc));
-  else
-    return 0;
-
-  if (dv_as_opaque (ldv) == dvp)
-    return 0;
-
-  dv = (decl_or_value) dvp;
-  slot = htab_find_slot_with_hash (value_chains, ldv, dv_htab_hash (ldv),
-				   INSERT);
-  if (!*slot)
-    {
-      vc = (value_chain) pool_alloc (value_chain_pool);
-      vc->dv = ldv;
-      vc->next = NULL;
-      vc->refcount = 0;
-      *slot = (void *) vc;
-    }
-  else
-    {
-      for (vc = ((value_chain) *slot)->next; vc; vc = vc->next)
-	if (dv_as_opaque (vc->dv) == dv_as_opaque (dv))
-	  break;
-      if (vc)
-	{
-	  vc->refcount++;
-	  return 0;
-	}
-    }
-  vc = (value_chain) *slot;
-  nvc = (value_chain) pool_alloc (value_chain_pool);
-  nvc->dv = dv;
-  nvc->next = vc->next;
-  nvc->refcount = 1;
-  vc->next = nvc;
-  return 0;
-}
-
-/* If decl or value DVP refers to VALUEs from within LOC, add backlinks
-   from those VALUEs to DVP.  */
-
-static void
-add_value_chains (decl_or_value dv, rtx loc)
-{
-  if (GET_CODE (loc) == VALUE || GET_CODE (loc) == DEBUG_EXPR)
-    {
-      add_value_chain (&loc, dv_as_opaque (dv));
-      return;
-    }
-  if (REG_P (loc))
-    return;
-  if (MEM_P (loc))
-    loc = XEXP (loc, 0);
-  for_each_rtx (&loc, add_value_chain, dv_as_opaque (dv));
-}
-
-/* If CSELIB_VAL_PTR of value DV refer to VALUEs, add backlinks from those
-   VALUEs to DV.  Add the same time get rid of ASM_OPERANDS from locs list,
-   that is something we never can express in .debug_info and can prevent
-   reverse ops from being used.  */
-
-static void
-add_cselib_value_chains (decl_or_value dv)
-{
-  struct elt_loc_list **l;
-
-  for (l = &CSELIB_VAL_PTR (dv_as_value (dv))->locs; *l;)
-    if (GET_CODE ((*l)->loc) == ASM_OPERANDS)
-      *l = (*l)->next;
-    else
-      {
-	for_each_rtx (&(*l)->loc, add_value_chain, dv_as_opaque (dv));
-	l = &(*l)->next;
-      }
-}
-
-/* If decl or value DVP refers to VALUE from *LOC, remove backlinks
-   from VALUE to DVP.  */
-
-static int
-remove_value_chain (rtx *loc, void *dvp)
-{
-  decl_or_value dv, ldv;
-  value_chain vc;
-  void **slot;
-
-  if (GET_CODE (*loc) == VALUE)
-    ldv = dv_from_value (*loc);
-  else if (GET_CODE (*loc) == DEBUG_EXPR)
-    ldv = dv_from_decl (DEBUG_EXPR_TREE_DECL (*loc));
-  else
-    return 0;
-
-  if (dv_as_opaque (ldv) == dvp)
-    return 0;
-
-  dv = (decl_or_value) dvp;
-  slot = htab_find_slot_with_hash (value_chains, ldv, dv_htab_hash (ldv),
-				   NO_INSERT);
-  for (vc = (value_chain) *slot; vc->next; vc = vc->next)
-    if (dv_as_opaque (vc->next->dv) == dv_as_opaque (dv))
-      {
-	value_chain dvc = vc->next;
-	gcc_assert (dvc->refcount > 0);
-	if (--dvc->refcount == 0)
-	  {
-	    vc->next = dvc->next;
-	    pool_free (value_chain_pool, dvc);
-	    if (vc->next == NULL && vc == (value_chain) *slot)
-	      {
-		pool_free (value_chain_pool, vc);
-		htab_clear_slot (value_chains, slot);
-	      }
-	  }
-	return 0;
-      }
-  gcc_unreachable ();
-}
-
-/* If decl or value DVP refers to VALUEs from within LOC, remove backlinks
-   from those VALUEs to DVP.  */
-
-static void
-remove_value_chains (decl_or_value dv, rtx loc)
-{
-  if (GET_CODE (loc) == VALUE || GET_CODE (loc) == DEBUG_EXPR)
-    {
-      remove_value_chain (&loc, dv_as_opaque (dv));
-      return;
-    }
-  if (REG_P (loc))
-    return;
-  if (MEM_P (loc))
-    loc = XEXP (loc, 0);
-  for_each_rtx (&loc, remove_value_chain, dv_as_opaque (dv));
-}
-
 #if ENABLE_CHECKING
-/* If CSELIB_VAL_PTR of value DV refer to VALUEs, remove backlinks from those
-   VALUEs to DV.  */
-
-static void
-remove_cselib_value_chains (decl_or_value dv)
-{
-  struct elt_loc_list *l;
-
-  for (l = CSELIB_VAL_PTR (dv_as_value (dv))->locs; l; l = l->next)
-    for_each_rtx (&l->loc, remove_value_chain, dv_as_opaque (dv));
-}
-
 /* Check the order of entries in one-part variables.   */
 
 static int
 canonicalize_loc_order_check (void **slot, void *data ATTRIBUTE_UNUSED)
 {
   variable var = (variable) *slot;
-  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);
+  gcc_assert (!var->in_changed_variables);
 #endif
 
-  if (!dv_onepart_p (dv))
+  if (!var->onepart)
     return 1;
 
   gcc_assert (var->n_var_parts == 1);
@@ -3186,7 +3198,7 @@  canonicalize_values_star (void **slot, v
   bool has_value;
   bool has_marks;
 
-  if (!dv_onepart_p (dv))
+  if (!var->onepart)
     return 1;
 
   gcc_checking_assert (var->n_var_parts == 1);
@@ -3408,7 +3420,7 @@  canonicalize_vars_star (void **slot, voi
   variable cvar;
   location_chain cnode;
 
-  if (!dv_onepart_p (dv) || dv_is_value_p (dv))
+  if (!var->onepart || var->onepart == ONEPART_VALUE)
     return 1;
 
   gcc_assert (var->n_var_parts == 1);
@@ -3461,7 +3473,7 @@  variable_merge_over_cur (variable s1var,
   void **dstslot;
   variable s2var, dvar = NULL;
   decl_or_value dv = s1var->dv;
-  bool onepart = dv_onepart_p (dv);
+  onepart_enum_t onepart = s1var->onepart;
   rtx val;
   hashval_t dvhash;
   location_chain node, *nodep;
@@ -3475,8 +3487,7 @@  variable_merge_over_cur (variable s1var,
   if (!onepart)
     return variable_union (s1var, dst);
 
-  gcc_checking_assert (s1var->n_var_parts == 1
-		       && s1var->var_part[0].offset == 0);
+  gcc_checking_assert (s1var->n_var_parts == 1);
 
   dvhash = dv_htab_hash (dv);
   if (dv_is_value_p (dv))
@@ -3493,16 +3504,16 @@  variable_merge_over_cur (variable s1var,
 
   dsm->src_onepart_cnt--;
   gcc_assert (s2var->var_part[0].loc_chain
-	      && s2var->n_var_parts == 1
-	      && s2var->var_part[0].offset == 0);
+	      && s2var->onepart == onepart
+	      && s2var->n_var_parts == 1);
 
   dstslot = shared_hash_find_slot_noinsert_1 (dst->vars, dv, dvhash);
   if (dstslot)
     {
       dvar = (variable)*dstslot;
       gcc_assert (dvar->refcount == 1
-		  && dvar->n_var_parts == 1
-		  && dvar->var_part[0].offset == 0);
+		  && dvar->onepart == onepart
+		  && dvar->n_var_parts == 1);
       nodep = &dvar->var_part[0].loc_chain;
     }
   else
@@ -3529,15 +3540,18 @@  variable_merge_over_cur (variable s1var,
 	{
 	  if (node)
 	    {
-	      dvar = (variable) pool_alloc (dv_pool (dv));
+	      dvar = (variable) pool_alloc (onepart_pool (onepart));
 	      dvar->dv = dv;
 	      dvar->refcount = 1;
 	      dvar->n_var_parts = 1;
-	      dvar->cur_loc_changed = false;
+	      dvar->onepart = onepart;
 	      dvar->in_changed_variables = false;
-	      dvar->var_part[0].offset = 0;
 	      dvar->var_part[0].loc_chain = node;
 	      dvar->var_part[0].cur_loc = NULL;
+	      if (onepart)
+		VAR_LOC_1PAUX (dvar) = NULL;
+	      else
+		VAR_PART_OFFSET (dvar, 0) = 0;
 
 	      dstslot
 		= shared_hash_find_slot_unshare_1 (&dst->vars, dv, dvhash,
@@ -3662,15 +3676,16 @@  variable_merge_over_cur (variable s1var,
 							  INSERT);
 		  if (!*slot)
 		    {
-		      variable var = (variable) pool_alloc (dv_pool (dv));
+		      variable var = (variable) pool_alloc (onepart_pool
+							    (ONEPART_VALUE));
 		      var->dv = dv;
 		      var->refcount = 1;
 		      var->n_var_parts = 1;
-		      var->cur_loc_changed = false;
+		      var->onepart = ONEPART_VALUE;
 		      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;
+		      VAR_LOC_1PAUX (var) = NULL;
 		      *slot = var;
 		    }
 
@@ -3717,9 +3732,8 @@  variable_merge_over_src (variable s2var,
 {
   dataflow_set *dst = dsm->dst;
   decl_or_value dv = s2var->dv;
-  bool onepart = dv_onepart_p (dv);
 
-  if (!onepart)
+  if (!s2var->onepart)
     {
       void **dstp = shared_hash_find_slot (dst->vars, dv);
       *dstp = s2var;
@@ -3864,7 +3878,7 @@  remove_duplicate_values (variable var)
 {
   location_chain node, *nodep;
 
-  gcc_assert (dv_onepart_p (var->dv));
+  gcc_assert (var->onepart);
   gcc_assert (var->n_var_parts == 1);
   gcc_assert (var->refcount == 1);
 
@@ -3915,7 +3929,7 @@  variable_post_merge_new_vals (void **slo
   variable var = (variable)*slot;
   location_chain node;
 
-  if (!dv_onepart_p (var->dv) || !var->n_var_parts)
+  if (!var->onepart || !var->n_var_parts)
     return 1;
 
   gcc_assert (var->n_var_parts == 1);
@@ -4146,13 +4160,11 @@  find_mem_expr_in_1pdv (tree expr, rtx va
   if (!var)
     return NULL;
 
-  gcc_assert (dv_onepart_p (var->dv));
+  gcc_assert (var->onepart);
 
   if (!var->n_var_parts)
     return NULL;
 
-  gcc_assert (var->var_part[0].offset == 0);
-
   VALUE_RECURSED_INTO (val) = true;
 
   for (node = var->var_part[0].loc_chain; node; node = node->next)
@@ -4206,7 +4218,7 @@  dataflow_set_preserve_mem_locs (void **s
   dataflow_set *set = (dataflow_set *) data;
   variable var = (variable) *slot;
 
-  if (dv_is_decl_p (var->dv) && dv_onepart_p (var->dv))
+  if (var->onepart == ONEPART_VDECL || var->onepart == ONEPART_DEXPR)
     {
       tree decl = dv_as_decl (var->dv);
       location_chain loc, *locp;
@@ -4277,10 +4289,7 @@  dataflow_set_preserve_mem_locs (void **s
 		    {
 		      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);
 		}
 	      locp = &loc->next;
 	      continue;
@@ -4288,12 +4297,10 @@  dataflow_set_preserve_mem_locs (void **s
 
 	  if (emit_notes)
 	    {
-	      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;
@@ -4321,10 +4328,11 @@  dataflow_set_remove_mem_locs (void **slo
   dataflow_set *set = (dataflow_set *) data;
   variable var = (variable) *slot;
 
-  if (dv_is_value_p (var->dv))
+  if (var->onepart == ONEPART_VALUE)
     {
       location_chain loc, *locp;
       bool changed = false;
+      rtx cur_loc;
 
       gcc_assert (var->n_var_parts == 1);
 
@@ -4343,6 +4351,11 @@  dataflow_set_remove_mem_locs (void **slo
 	  gcc_assert (var->n_var_parts == 1);
 	}
 
+      if (VAR_LOC_1PAUX (var))
+	cur_loc = VAR_LOC_FROM (var);
+      else
+	cur_loc = var->var_part[0].cur_loc;
+
       for (locp = &var->var_part[0].loc_chain, loc = *locp;
 	   loc; loc = *locp)
 	{
@@ -4353,17 +4366,16 @@  dataflow_set_remove_mem_locs (void **slo
 	      continue;
 	    }
 
-	  if (emit_notes)
-	    remove_value_chains (var->dv, loc->loc);
 	  *locp = loc->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[0].cur_loc == loc->loc)
+	  if (cur_loc == loc->loc)
 	    {
 	      changed = true;
 	      var->var_part[0].cur_loc = NULL;
-	      var->cur_loc_changed = true;
+	      if (VAR_LOC_1PAUX (var))
+		VAR_LOC_FROM (var) = NULL;
 	    }
 	  pool_free (loc_chain_pool, loc);
 	}
@@ -4467,20 +4479,24 @@  variable_different_p (variable var1, var
   if (var1 == var2)
     return false;
 
+  if (var1->onepart != var2->onepart)
+    return true;
+
   if (var1->n_var_parts != var2->n_var_parts)
     return true;
 
+  if (var1->onepart && var1->n_var_parts)
+    {
+      gcc_checking_assert (dv_as_opaque (var1->dv) == dv_as_opaque (var2->dv)
+			   && var1->n_var_parts == 1);
+      /* One-part values have locations in a canonical order.  */
+      return onepart_variable_different_p (var1, var2);
+    }
+
   for (i = 0; i < var1->n_var_parts; i++)
     {
-      if (var1->var_part[i].offset != var2->var_part[i].offset)
+      if (VAR_PART_OFFSET (var1, i) != VAR_PART_OFFSET (var2, i))
 	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))
-	{
-	  gcc_assert (var1->n_var_parts == 1
-		      && dv_as_opaque (var1->dv) == dv_as_opaque (var2->dv));
-	  return onepart_variable_different_p (var1, var2);
-	}
       if (variable_part_different_p (&var1->var_part[i], &var2->var_part[i]))
 	return true;
       if (variable_part_different_p (&var2->var_part[i], &var1->var_part[i]))
@@ -5020,9 +5036,6 @@  log_op_type (rtx x, basic_block bb, rtx 
 /* All preserved VALUEs.  */
 static VEC (rtx, heap) *preserved_values;
 
-/* Registers used in the current function for passing parameters.  */
-static HARD_REG_SET argument_reg_set;
-
 /* Ensure VAL is preserved and remember it in a vector for vt_emit_notes.  */
 
 static void
@@ -5089,11 +5102,7 @@  add_uses (rtx *ploc, void *data)
 
 	  if (MEM_P (vloc)
 	      && !REG_P (XEXP (vloc, 0))
-	      && !MEM_P (XEXP (vloc, 0))
-	      && GET_CODE (XEXP (vloc, 0)) != ENTRY_VALUE
-	      && (GET_CODE (XEXP (vloc, 0)) != PLUS
-		  || XEXP (XEXP (vloc, 0), 0) != cfa_base_rtx
-		  || !CONST_INT_P (XEXP (XEXP (vloc, 0), 1))))
+	      && !MEM_P (XEXP (vloc, 0)))
 	    {
 	      rtx mloc = vloc;
 	      enum machine_mode address_mode = get_address_mode (mloc);
@@ -5105,16 +5114,24 @@  add_uses (rtx *ploc, void *data)
 		{
 		  micro_operation moa;
 		  preserve_value (val);
-		  mloc = cselib_subst_to_values (XEXP (mloc, 0),
-						 GET_MODE (mloc));
-		  moa.type = MO_VAL_USE;
-		  moa.insn = cui->insn;
-		  moa.u.loc = gen_rtx_CONCAT (address_mode,
-					      val->val_rtx, mloc);
-		  if (dump_file && (dump_flags & TDF_DETAILS))
-		    log_op_type (moa.u.loc, cui->bb, cui->insn,
-				 moa.type, dump_file);
-		  VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &moa);
+
+		  if (GET_CODE (XEXP (mloc, 0)) != ENTRY_VALUE
+		      && (GET_CODE (XEXP (mloc, 0)) != PLUS
+			  || XEXP (XEXP (mloc, 0), 0) != cfa_base_rtx
+			  || !CONST_INT_P (XEXP (XEXP (mloc, 0), 1))))
+		    {
+		      mloc = cselib_subst_to_values (XEXP (mloc, 0),
+						     GET_MODE (mloc));
+		      moa.type = MO_VAL_USE;
+		      moa.insn = cui->insn;
+		      moa.u.loc = gen_rtx_CONCAT (address_mode,
+						  val->val_rtx, mloc);
+		      if (dump_file && (dump_flags & TDF_DETAILS))
+			log_op_type (moa.u.loc, cui->bb, cui->insn,
+				     moa.type, dump_file);
+		      VEC_safe_push (micro_operation, heap, VTI (bb)->mos,
+				     &moa);
+		    }
 		}
 	    }
 
@@ -5122,7 +5139,7 @@  add_uses (rtx *ploc, void *data)
 	      && (GET_CODE (vloc) != CONST
 		  || for_each_rtx (&vloc, non_suitable_const, NULL)))
 	    /* For constants don't look up any value.  */;
-	  else if (!VAR_LOC_UNKNOWN_P (vloc)
+	  else if (!VAR_LOC_UNKNOWN_P (vloc) && !unsuitable_loc (vloc)
 		   && (val = find_use_val (vloc, GET_MODE (oloc), cui)))
 	    {
 	      enum machine_mode mode2;
@@ -5168,32 +5185,36 @@  add_uses (rtx *ploc, void *data)
 
 	  if (MEM_P (oloc)
 	      && !REG_P (XEXP (oloc, 0))
-	      && !MEM_P (XEXP (oloc, 0))
-	      && GET_CODE (XEXP (oloc, 0)) != ENTRY_VALUE
-	      && (GET_CODE (XEXP (oloc, 0)) != PLUS
-		  || XEXP (XEXP (oloc, 0), 0) != cfa_base_rtx
-		  || !CONST_INT_P (XEXP (XEXP (oloc, 0), 1))))
+	      && !MEM_P (XEXP (oloc, 0)))
 	    {
 	      rtx mloc = oloc;
 	      enum machine_mode address_mode = get_address_mode (mloc);
 	      cselib_val *val
 		= cselib_lookup (XEXP (mloc, 0), address_mode, 0,
-				GET_MODE (mloc));
+				 GET_MODE (mloc));
 
 	      if (val && !cselib_preserved_value_p (val))
 		{
 		  micro_operation moa;
 		  preserve_value (val);
-		  mloc = cselib_subst_to_values (XEXP (mloc, 0),
-						GET_MODE (mloc));
-		  moa.type = MO_VAL_USE;
-		  moa.insn = cui->insn;
-		  moa.u.loc = gen_rtx_CONCAT (address_mode,
-					      val->val_rtx, mloc);
-		  if (dump_file && (dump_flags & TDF_DETAILS))
-		    log_op_type (moa.u.loc, cui->bb, cui->insn,
-				 moa.type, dump_file);
-		  VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &moa);
+
+		  if (GET_CODE (XEXP (mloc, 0)) != ENTRY_VALUE
+		      && (GET_CODE (XEXP (mloc, 0)) != PLUS
+			  || XEXP (XEXP (mloc, 0), 0) != cfa_base_rtx
+			  || !CONST_INT_P (XEXP (XEXP (mloc, 0), 1))))
+		    {
+		      mloc = cselib_subst_to_values (XEXP (mloc, 0),
+						     GET_MODE (mloc));
+		      moa.type = MO_VAL_USE;
+		      moa.insn = cui->insn;
+		      moa.u.loc = gen_rtx_CONCAT (address_mode,
+						  val->val_rtx, mloc);
+		      if (dump_file && (dump_flags & TDF_DETAILS))
+			log_op_type (moa.u.loc, cui->bb, cui->insn,
+				     moa.type, dump_file);
+		      VEC_safe_push (micro_operation, heap, VTI (bb)->mos,
+				     &moa);
+		    }
 		}
 	    }
 
@@ -5255,7 +5276,18 @@  add_uses_1 (rtx *x, void *cui)
   for_each_rtx (x, add_uses, cui);
 }
 
-#define EXPR_DEPTH (PARAM_VALUE (PARAM_MAX_VARTRACK_EXPR_DEPTH))
+/* This is the value used during expansion of locations.  We want it
+   to be unbounded, so that variables expanded deep in a recursion
+   nest are fully evaluated, so that their values are cached
+   correctly.  We avoid recursion cycles through other means, and we
+   don't unshare RTL, so excess complexity is not a problem.  */
+#define EXPR_DEPTH (INT_MAX)
+/* We use this to keep too-complex expressions from being emitted as
+   location notes, and then to debug information.  Users can trade
+   compile time for ridiculously complex expressions, although they're
+   seldom useful, and they may often have to be discarded as not
+   representable anyway.  */
+#define EXPR_USE_DEPTH (PARAM_VALUE (PARAM_MAX_VARTRACK_EXPR_DEPTH))
 
 /* Attempt to reverse the EXPR operation in the debug info.  Say for
    reg1 = reg2 + 6 even when reg2 is no longer live we
@@ -5382,10 +5414,8 @@  add_stores (rtx loc, const_rtx expr, voi
 	  mo.u.loc = loc;
 	  if (GET_CODE (expr) == SET
 	      && SET_DEST (expr) == loc
-	      && REGNO (loc) < FIRST_PSEUDO_REGISTER
-	      && TEST_HARD_REG_BIT (argument_reg_set, REGNO (loc))
-	      && find_use_val (loc, mode, cui)
-	      && GET_CODE (SET_SRC (expr)) != ASM_OPERANDS)
+	      && !unsuitable_loc (SET_SRC (expr))
+	      && find_use_val (loc, mode, cui))
 	    {
 	      gcc_checking_assert (type == MO_VAL_SET);
 	      mo.u.loc = gen_rtx_SET (VOIDmode, loc, SET_SRC (expr));
@@ -5422,11 +5452,7 @@  add_stores (rtx loc, const_rtx expr, voi
     {
       if (MEM_P (loc) && type == MO_VAL_SET
 	  && !REG_P (XEXP (loc, 0))
-	  && !MEM_P (XEXP (loc, 0))
-	  && GET_CODE (XEXP (loc, 0)) != ENTRY_VALUE
-	  && (GET_CODE (XEXP (loc, 0)) != PLUS
-	      || XEXP (XEXP (loc, 0), 0) != cfa_base_rtx
-	      || !CONST_INT_P (XEXP (XEXP (loc, 0), 1))))
+	  && !MEM_P (XEXP (loc, 0)))
 	{
 	  rtx mloc = loc;
 	  enum machine_mode address_mode = get_address_mode (mloc);
@@ -5437,15 +5463,23 @@  add_stores (rtx loc, const_rtx expr, voi
 	  if (val && !cselib_preserved_value_p (val))
 	    {
 	      preserve_value (val);
-	      mo.type = MO_VAL_USE;
-	      mloc = cselib_subst_to_values (XEXP (mloc, 0),
-					     GET_MODE (mloc));
-	      mo.u.loc = gen_rtx_CONCAT (address_mode, val->val_rtx, mloc);
-	      mo.insn = cui->insn;
-	      if (dump_file && (dump_flags & TDF_DETAILS))
-		log_op_type (mo.u.loc, cui->bb, cui->insn,
-			     mo.type, dump_file);
-	      VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &mo);
+
+	      if (GET_CODE (XEXP (mloc, 0)) != ENTRY_VALUE
+		  && (GET_CODE (XEXP (mloc, 0)) != PLUS
+		      || XEXP (XEXP (mloc, 0), 0) != cfa_base_rtx
+		      || !CONST_INT_P (XEXP (XEXP (mloc, 0), 1))))
+		{
+		  mloc = cselib_subst_to_values (XEXP (mloc, 0),
+						 GET_MODE (mloc));
+		  mo.type = MO_VAL_USE;
+		  mo.insn = cui->insn;
+		  mo.u.loc = gen_rtx_CONCAT (address_mode,
+					     val->val_rtx, mloc);
+		  if (dump_file && (dump_flags & TDF_DETAILS))
+		    log_op_type (mo.u.loc, cui->bb, cui->insn,
+				 mo.type, dump_file);
+		  VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &mo);
+		}
 	    }
 	}
 
@@ -5893,7 +5927,7 @@  prepare_call_arguments (basic_block bb, 
 	      tree dtemp = VEC_index (tree, *debug_args, ix + 1);
 	      enum machine_mode mode = DECL_MODE (dtemp);
 	      item = gen_rtx_DEBUG_PARAMETER_REF (mode, param);
-	      item = gen_rtx_CONCAT (mode, item, DECL_RTL (dtemp));
+	      item = gen_rtx_CONCAT (mode, item, DECL_RTL_KNOWN_SET (dtemp));
 	      call_arguments = gen_rtx_EXPR_LIST (VOIDmode, item,
 						  call_arguments);
 	    }
@@ -6705,7 +6739,7 @@  dump_var (variable var)
   for (i = 0; i < var->n_var_parts; i++)
     {
       fprintf (dump_file, "    offset %ld\n",
-	       (long) var->var_part[i].offset);
+	       (long)(var->onepart ? 0 : VAR_PART_OFFSET (var, i)));
       for (node = var->var_part[i].loc_chain; node; node = node->next)
 	{
 	  fprintf (dump_file, "      ");
@@ -6766,18 +6800,84 @@  dump_dataflow_sets (void)
     }
 }
 
-/* Add variable VAR to the hash table of changed variables and
-   if it has no locations delete it from SET's hash table.  */
+/* Return the variable for DV in dropped_values, inserting one if
+   requested with INSERT.  */
 
-static void
-variable_was_changed (variable var, dataflow_set *set)
+static inline variable
+variable_from_dropped (decl_or_value dv, enum insert_option insert)
 {
-  hashval_t hash = dv_htab_hash (var->dv);
+  void **slot;
+  variable empty_var;
+  onepart_enum_t onepart;
+
+  slot = htab_find_slot_with_hash (dropped_values, dv, dv_htab_hash (dv),
+				   insert);
+
+  if (!slot)
+    return NULL;
+
+  if (*slot)
+    return (variable) *slot;
+
+  gcc_checking_assert (insert == INSERT);
+
+  onepart = dv_onepart_p (dv);
+
+  gcc_checking_assert (onepart == ONEPART_VALUE || onepart == ONEPART_DEXPR);
+
+  empty_var = (variable) pool_alloc (onepart_pool (onepart));
+  empty_var->dv = dv;
+  empty_var->refcount = 1;
+  empty_var->n_var_parts = 0;
+  empty_var->onepart = onepart;
+  empty_var->in_changed_variables = false;
+  empty_var->var_part[0].loc_chain = NULL;
+  empty_var->var_part[0].cur_loc = NULL;
+  VAR_LOC_1PAUX (empty_var) = NULL;
+  set_dv_changed (dv, true);
+
+  *slot = empty_var;
+
+  return empty_var;
+}
+
+/* Recover the one-part aux from dropped_values.  */
+
+static struct onepart_aux *
+recover_dropped_1paux (variable var)
+{
+  variable dvar;
+
+  gcc_checking_assert (var->onepart);
+
+  if (VAR_LOC_1PAUX (var))
+    return VAR_LOC_1PAUX (var);
+
+  if (var->onepart == ONEPART_VDECL)
+    return NULL;
+
+  dvar = variable_from_dropped (var->dv, NO_INSERT);
+
+  if (!dvar)
+    return NULL;
+
+  VAR_LOC_1PAUX (var) = VAR_LOC_1PAUX (dvar);
+  VAR_LOC_1PAUX (dvar) = NULL;
+
+  return VAR_LOC_1PAUX (var);
+}
+
+/* Add variable VAR to the hash table of changed variables and
+   if it has no locations delete it from SET's hash table.  */
+
+static void
+variable_was_changed (variable var, dataflow_set *set)
+{
+  hashval_t hash = dv_htab_hash (var->dv);
 
   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);
@@ -6791,30 +6891,76 @@  variable_was_changed (variable var, data
 	  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;
+	  if (var != old_var && var->onepart)
+	    {
+	      /* Restore the auxiliary info from an empty variable
+		 previously created for changed_variables, so it is
+		 not lost.  */
+	      gcc_checking_assert (!VAR_LOC_1PAUX (var));
+	      VAR_LOC_1PAUX (var) = VAR_LOC_1PAUX (old_var);
+	      VAR_LOC_1PAUX (old_var) = NULL;
+	    }
 	  variable_htab_free (*slot);
 	}
+
       if (set && var->n_var_parts == 0)
 	{
-	  variable empty_var;
+	  onepart_enum_t onepart = var->onepart;
+	  variable empty_var = NULL;
+	  void **dslot = NULL;
 
-	  empty_var = (variable) pool_alloc (dv_pool (var->dv));
-	  empty_var->dv = var->dv;
-	  empty_var->refcount = 1;
-	  empty_var->n_var_parts = 0;
-	  empty_var->cur_loc_changed = true;
+	  if (onepart == ONEPART_VALUE || onepart == ONEPART_DEXPR)
+	    {
+	      dslot = htab_find_slot_with_hash (dropped_values, var->dv,
+						dv_htab_hash (var->dv),
+						INSERT);
+	      empty_var = (variable) *dslot;
+
+	      if (empty_var)
+		{
+		  gcc_checking_assert (!empty_var->in_changed_variables);
+		  if (!VAR_LOC_1PAUX (var))
+		    {
+		      VAR_LOC_1PAUX (var) = VAR_LOC_1PAUX (empty_var);
+		      VAR_LOC_1PAUX (empty_var) = NULL;
+		    }
+		  else
+		    gcc_checking_assert (!VAR_LOC_1PAUX (empty_var));
+		}
+	    }
+
+	  if (!empty_var)
+	    {
+	      empty_var = (variable) pool_alloc (onepart_pool (onepart));
+	      empty_var->dv = var->dv;
+	      empty_var->refcount = 1;
+	      empty_var->n_var_parts = 0;
+	      empty_var->onepart = onepart;
+	      if (dslot)
+		{
+		  empty_var->refcount++;
+		  *dslot = empty_var;
+		}
+	    }
+	  else
+	    empty_var->refcount++;
 	  empty_var->in_changed_variables = true;
 	  *slot = empty_var;
+	  if (onepart)
+	    {
+	      empty_var->var_part[0].loc_chain = NULL;
+	      empty_var->var_part[0].cur_loc = NULL;
+	      VAR_LOC_1PAUX (empty_var) = VAR_LOC_1PAUX (var);
+	      VAR_LOC_1PAUX (var) = NULL;
+	    }
 	  goto drop_var;
 	}
       else
 	{
+	  if (var->onepart && !VAR_LOC_1PAUX (var))
+	    recover_dropped_1paux (var);
 	  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;
 	}
     }
@@ -6849,13 +6995,24 @@  find_variable_location_part (variable va
 {
   int pos, low, high;
 
+  if (var->onepart)
+    {
+      if (offset != 0)
+	return -1;
+
+      if (insertion_point)
+	*insertion_point = 0;
+
+      return var->n_var_parts - 1;
+    }
+
   /* Find the location part.  */
   low = 0;
   high = var->n_var_parts;
   while (low != high)
     {
       pos = (low + high) / 2;
-      if (var->var_part[pos].offset < offset)
+      if (VAR_PART_OFFSET (var, pos) < offset)
 	low = pos + 1;
       else
 	high = pos;
@@ -6865,7 +7022,7 @@  find_variable_location_part (variable va
   if (insertion_point)
     *insertion_point = pos;
 
-  if (pos < var->n_var_parts && var->var_part[pos].offset == offset)
+  if (pos < var->n_var_parts && VAR_PART_OFFSET (var, pos) == offset)
     return pos;
 
   return -1;
@@ -6880,26 +7037,34 @@  set_slot_part (dataflow_set *set, rtx lo
   location_chain node, next;
   location_chain *nextp;
   variable var;
-  bool onepart = dv_onepart_p (dv);
-
-  gcc_assert (offset == 0 || !onepart);
-  gcc_assert (loc != dv_as_opaque (dv));
+  onepart_enum_t onepart;
 
   var = (variable) *slot;
 
+  if (var)
+    onepart = var->onepart;
+  else
+    onepart = dv_onepart_p (dv);
+
+  gcc_checking_assert (offset == 0 || !onepart);
+  gcc_checking_assert (loc != dv_as_opaque (dv));
+
   if (! flag_var_tracking_uninit)
     initialized = VAR_INIT_STATUS_INITIALIZED;
 
   if (!var)
     {
       /* Create new variable information.  */
-      var = (variable) pool_alloc (dv_pool (dv));
+      var = (variable) pool_alloc (onepart_pool (onepart));
       var->dv = dv;
       var->refcount = 1;
       var->n_var_parts = 1;
-      var->cur_loc_changed = false;
+      var->onepart = onepart;
       var->in_changed_variables = false;
-      var->var_part[0].offset = offset;
+      if (var->onepart)
+	VAR_LOC_1PAUX (var) = NULL;
+      else
+	VAR_PART_OFFSET (var, 0) = offset;
       var->var_part[0].loc_chain = NULL;
       var->var_part[0].cur_loc = NULL;
       *slot = var;
@@ -7054,7 +7219,7 @@  set_slot_part (dataflow_set *set, rtx lo
 	  /* We track only variables whose size is <= MAX_VAR_PARTS bytes
 	     thus there are at most MAX_VAR_PARTS different offsets.  */
 	  gcc_assert (var->n_var_parts < MAX_VAR_PARTS
-		      && (!var->n_var_parts || !dv_onepart_p (var->dv)));
+		      && (!var->n_var_parts || !onepart));
 
 	  /* We have to move the elements of array starting at index
 	     inspos to the next position.  */
@@ -7062,7 +7227,8 @@  set_slot_part (dataflow_set *set, rtx lo
 	    var->var_part[pos] = var->var_part[pos - 1];
 
 	  var->n_var_parts++;
-	  var->var_part[pos].offset = offset;
+	  gcc_checking_assert (!onepart);
+	  VAR_PART_OFFSET (var, pos) = offset;
 	  var->var_part[pos].loc_chain = NULL;
 	  var->var_part[pos].cur_loc = NULL;
 	}
@@ -7083,10 +7249,7 @@  set_slot_part (dataflow_set *set, rtx lo
 	      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;
-		}
+		var->var_part[pos].cur_loc = NULL;
 	      pool_free (loc_chain_pool, node);
 	      *nextp = next;
 	      break;
@@ -7106,9 +7269,6 @@  set_slot_part (dataflow_set *set, rtx lo
   node->next = *nextp;
   *nextp = node;
 
-  if (onepart && emit_notes)
-    add_value_chains (var->dv, loc);
-
   /* If no location was emitted do so.  */
   if (var->var_part[pos].cur_loc == NULL)
     variable_was_changed (var, set);
@@ -7238,6 +7398,7 @@  delete_slot_part (dataflow_set *set, rtx
       location_chain node, next;
       location_chain *nextp;
       bool changed;
+      rtx cur_loc;
 
       if (shared_var_p (var, set->vars))
 	{
@@ -7258,6 +7419,11 @@  delete_slot_part (dataflow_set *set, rtx
 	    }
 	}
 
+      if (pos == 0 && var->onepart && VAR_LOC_1PAUX (var))
+	cur_loc = VAR_LOC_FROM (var);
+      else
+	cur_loc = var->var_part[pos].cur_loc;
+
       /* Delete the location part.  */
       changed = false;
       nextp = &var->var_part[pos].loc_chain;
@@ -7268,16 +7434,15 @@  delete_slot_part (dataflow_set *set, rtx
 	       && REGNO (node->loc) == REGNO (loc))
 	      || rtx_equal_p (node->loc, loc))
 	    {
-	      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)
+	      if (cur_loc == node->loc)
 		{
 		  changed = true;
 		  var->var_part[pos].cur_loc = NULL;
-		  var->cur_loc_changed = true;
+		  if (pos == 0 && var->onepart && VAR_LOC_1PAUX (var))
+		    VAR_LOC_FROM (var) = NULL;
 		}
 	      pool_free (loc_chain_pool, node);
 	      *nextp = next;
@@ -7291,8 +7456,6 @@  delete_slot_part (dataflow_set *set, rtx
 	{
 	  changed = true;
 	  var->n_var_parts--;
-	  if (emit_notes)
-	    var->cur_loc_changed = true;
 	  while (pos < var->n_var_parts)
 	    {
 	      var->var_part[pos] = var->var_part[pos + 1];
@@ -7321,6 +7484,12 @@  delete_variable_part (dataflow_set *set,
   delete_slot_part (set, loc, slot, offset);
 }
 
+DEF_VEC_P (variable);
+DEF_VEC_ALLOC_P (variable, heap);
+
+DEF_VEC_ALLOC_P_STACK (rtx);
+#define VEC_rtx_stack_alloc(alloc) VEC_stack_alloc (rtx, alloc)
+
 /* Structure for passing some other parameters to function
    vt_expand_loc_callback.  */
 struct expand_loc_callback_data
@@ -7328,56 +7497,368 @@  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;
-
-  /* True if cur_loc should be ignored and any possible location
-     returned.  */
-  bool ignore_cur_loc;
+  /* Stack of values and debug_exprs under expansion, and their
+     children.  */
+  VEC (rtx, stack) *expanding;
+
+  /* Stack of values and debug_exprs whose expansion hit recursion
+     cycles.  They will have VALUE_RECURSED_INTO marked when added to
+     this list.  This flag will be cleared if any of its dependencies
+     resolves to a valid location.  So, if the flag remains set at the
+     end of the search, we know no valid location for this one can
+     possibly exist.  */
+  VEC (rtx, stack) *pending;
+
+  /* The maximum depth among the sub-expressions under expansion.
+     Zero indicates no expansion so far.  */
+  int depth;
 };
 
+/* Allocate the one-part auxiliary data structure for VAR, with enough
+   room for COUNT dependencies.  */
+
+static void
+loc_exp_dep_alloc (variable var, int count)
+{
+  size_t allocsize;
+
+  gcc_checking_assert (var->onepart);
+
+  /* We can be called with COUNT == 0 to allocate the data structure
+     without any dependencies, e.g. for the backlinks only.  However,
+     if we are specifying a COUNT, then the dependency list must have
+     been emptied before.  It would be possible to adjust pointers or
+     force it empty here, but this is better done at an earlier point
+     in the algorithm, so we instead leave an assertion to catch
+     errors.  */
+  gcc_checking_assert (!count
+		       || VEC_empty (loc_exp_dep, VAR_LOC_DEP_VEC (var)));
+
+  if (VAR_LOC_1PAUX (var)
+      && VEC_space (loc_exp_dep, VAR_LOC_DEP_VEC (var), count))
+    return;
+
+  allocsize = offsetof (struct onepart_aux, deps)
+    + VEC_embedded_size (loc_exp_dep, count);
+
+  if (VAR_LOC_1PAUX (var))
+    {
+      VAR_LOC_1PAUX (var) = XRESIZEVAR (struct onepart_aux,
+					VAR_LOC_1PAUX (var), allocsize);
+      /* If the reallocation moves the onepaux structure, the
+	 back-pointer to BACKLINKS in the first list member will still
+	 point to its old location.  Adjust it.  */
+      if (VAR_LOC_DEP_LST (var))
+	VAR_LOC_DEP_LST (var)->pprev = VAR_LOC_DEP_LSTP (var);
+    }
+  else
+    {
+      VAR_LOC_1PAUX (var) = XNEWVAR (struct onepart_aux, allocsize);
+      *VAR_LOC_DEP_LSTP (var) = NULL;
+      VAR_LOC_FROM (var) = NULL;
+      VAR_LOC_DEPTH (var) = 0;
+    }
+  VEC_embedded_init (loc_exp_dep, VAR_LOC_DEP_VEC (var), count);
+}
+
+/* Remove all entries from the vector of active dependencies of VAR,
+   removing them from the back-links lists too.  */
+
+static void
+loc_exp_dep_clear (variable var)
+{
+  while (!VEC_empty (loc_exp_dep, VAR_LOC_DEP_VEC (var)))
+    {
+      loc_exp_dep *led = VEC_last (loc_exp_dep, VAR_LOC_DEP_VEC (var));
+      if (led->next)
+	led->next->pprev = led->pprev;
+      if (led->pprev)
+	*led->pprev = led->next;
+      VEC_pop (loc_exp_dep, VAR_LOC_DEP_VEC (var));
+    }
+}
+
+/* Insert an active dependency from VAR on X to the vector of
+   dependencies, and add the corresponding back-link to X's list of
+   back-links in VARS.  */
+
+static void
+loc_exp_insert_dep (variable var, rtx x, htab_t vars)
+{
+  decl_or_value dv;
+  variable xvar;
+  loc_exp_dep *led;
+
+  dv = dv_from_rtx (x);
+
+  /* ??? Build a vector of variables parallel to EXPANDING, to avoid
+     an additional look up?  */
+  xvar = (variable) htab_find_with_hash (vars, dv, dv_htab_hash (dv));
+
+  if (!xvar)
+    {
+      xvar = variable_from_dropped (dv, NO_INSERT);
+      gcc_checking_assert (xvar);
+    }
+
+  /* No point in adding the same backlink more than once.  This may
+     arise if say the same value appears in two complex expressions in
+     the same loc_list, or even more than once in a single
+     expression.  */
+  if (VAR_LOC_DEP_LST (xvar) && VAR_LOC_DEP_LST (xvar)->dv == var->dv)
+    return;
+
+  VEC_quick_push (loc_exp_dep, VAR_LOC_DEP_VEC (var), NULL);
+  led = VEC_last (loc_exp_dep, VAR_LOC_DEP_VEC (var));
+  led->dv = var->dv;
+  led->value = x;
+
+  loc_exp_dep_alloc (xvar, 0);
+  led->pprev = VAR_LOC_DEP_LSTP (xvar);
+  led->next = *led->pprev;
+  if (led->next)
+    led->next->pprev = &led->next;
+  *led->pprev = led;
+}
+
+/* Create active dependencies of VAR on COUNT values starting at
+   VALUE, and corresponding back-links to the entries in VARS.  Return
+   true if we found any pending-recursion results.  */
+
+static bool
+loc_exp_dep_set (variable var, rtx result, rtx *value, int count, htab_t vars)
+{
+  bool pending_recursion = false;
+
+  gcc_checking_assert (VEC_empty (loc_exp_dep, VAR_LOC_DEP_VEC (var)));
+
+  /* Set up all dependencies from last_child (as set up at the end of
+     the loop above) to the end.  */
+  loc_exp_dep_alloc (var, count);
+
+  while (count--)
+    {
+      rtx x = *value++;
+
+      if (!pending_recursion)
+	pending_recursion = !result && VALUE_RECURSED_INTO (x);
+
+      loc_exp_insert_dep (var, x, vars);
+    }
+
+  return pending_recursion;
+}
+
+/* Notify the back-links of IVAR that are pending recursion that we
+   have found a non-NIL value for it, so they are cleared for another
+   attempt to compute a current location.  */
+
+static void
+notify_dependents_of_resolved_value (variable ivar, htab_t vars)
+{
+  loc_exp_dep *led, *next;
+
+  for (led = VAR_LOC_DEP_LST (ivar); led; led = next)
+    {
+      decl_or_value dv = led->dv;
+      variable var;
+
+      next = led->next;
+
+      if (dv_is_value_p (dv))
+	{
+	  rtx value = dv_as_value (dv);
+
+	  /* If we have already resolved it, leave it alone.  */
+	  if (!VALUE_RECURSED_INTO (value))
+	    continue;
+
+	  /* Check that VALUE_RECURSED_INTO, true from the test above,
+	     implies NO_LOC_P.  */
+	  gcc_checking_assert (NO_LOC_P (value));
+
+	  /* We won't notify variables that are being expanded,
+	     because their dependency list is cleared before
+	     recursing.  */
+	  VALUE_RECURSED_INTO (value) = false;
+
+	  gcc_checking_assert (dv_changed_p (dv));
+	}
+      else if (!dv_changed_p (dv))
+	continue;
+
+      var = (variable) htab_find_with_hash (vars, dv, dv_htab_hash (dv));
+
+      if (!var)
+	var = variable_from_dropped (dv, NO_INSERT);
+
+      if (var)
+	notify_dependents_of_resolved_value (var, vars);
+
+      if (next)
+	next->pprev = led->pprev;
+      if (led->pprev)
+	*led->pprev = next;
+      led->next = NULL;
+      led->pprev = NULL;
+    }
+}
+
+static rtx vt_expand_loc_callback (rtx x, bitmap regs,
+				   int max_depth, void *data);
+
+/* Return the combined depth, when one sub-expression evaluated to
+   BEST_DEPTH and the previous known depth was SAVED_DEPTH.  */
+
+static inline int
+update_depth (int saved_depth, int best_depth)
+{
+  /* If we didn't find anything, stick with what we had.  */
+  if (!best_depth)
+    return saved_depth;
+
+  /* If we found hadn't found anything, use the depth of the current
+     expression.  Do NOT add one extra level, we want to compute the
+     maximum depth among sub-expressions.  We'll increment it later,
+     if appropriate.  */
+  if (!saved_depth)
+    return best_depth;
+
+  if (saved_depth < best_depth)
+    return best_depth;
+  else
+    return saved_depth;
+}
+
+/* Expand VAR to a location RTX, updating its cur_loc.  Use REGS and
+   DATA for cselib expand callback.  If PENDRECP is given, indicate in
+   it whether any sub-expression couldn't be fully evaluated because
+   it is pending recursion resolution.  */
+
+static inline rtx
+vt_expand_var_loc_chain (variable var, bitmap regs, void *data, bool *pendrecp)
+{
+  struct expand_loc_callback_data *elcd
+    = (struct expand_loc_callback_data *) data;
+  location_chain loc, next;
+  rtx result = NULL;
+  int first_child, result_first_child, last_child;
+  bool pending_recursion;
+  rtx loc_from = NULL;
+  struct elt_loc_list *cloc = NULL;
+  int depth, saved_depth = elcd->depth;
+
+  /* Clear all backlinks pointing at this, so that we're not notified
+     while we're active.  */
+  loc_exp_dep_clear (var);
+
+  if (var->onepart == ONEPART_VALUE)
+    {
+      cselib_val *val = CSELIB_VAL_PTR (dv_as_value (var->dv));
+
+      gcc_checking_assert (cselib_preserved_value_p (val));
+
+      cloc = val->locs;
+    }
+
+  first_child = result_first_child = last_child
+    = VEC_length (rtx, elcd->expanding);
+
+  /* Attempt to expand each available location in turn.  */
+  for (next = loc = var->n_var_parts ? var->var_part[0].loc_chain : NULL;
+       loc || cloc; loc = next)
+    {
+      result_first_child = last_child;
+
+      if (!loc || (GET_CODE (loc->loc) == ENTRY_VALUE && cloc))
+	{
+	  loc_from = cloc->loc;
+	  next = loc;
+	  cloc = cloc->next;
+	  if (unsuitable_loc (loc_from))
+	    continue;
+	}
+      else
+	{
+	  loc_from = loc->loc;
+	  next = loc->next;
+	}
+
+      gcc_checking_assert (!unsuitable_loc (loc_from));
+
+      elcd->depth = 0;
+      result = cselib_expand_value_rtx_cb (loc_from, regs, EXPR_DEPTH,
+					   vt_expand_loc_callback, data);
+      last_child = VEC_length (rtx, elcd->expanding);
+
+      if (result)
+	{
+	  depth = elcd->depth;
+
+	  gcc_checking_assert (depth || result_first_child == last_child);
+
+	  if (last_child - result_first_child != 1)
+	    depth++;
+
+	  if (depth <= EXPR_USE_DEPTH)
+	    break;
+
+	  result = NULL;
+	}
+
+      /* Set it up in case we leave the loop.  */
+      depth = 0;
+      loc_from = NULL;
+      result_first_child = first_child;
+    }
+
+  /* Register all encountered dependencies as active.  */
+  pending_recursion = loc_exp_dep_set
+    (var, result, VEC_address (rtx, elcd->expanding) + result_first_child,
+     last_child - result_first_child, elcd->vars);
+
+  VEC_truncate (rtx, elcd->expanding, first_child);
+
+  /* Record where the expansion came from.  */
+  gcc_checking_assert (!result || !pending_recursion);
+  VAR_LOC_FROM (var) = loc_from;
+  VAR_LOC_DEPTH (var) = depth;
+
+  elcd->depth = update_depth (saved_depth, depth);
+
+  /* Indicate whether any of the dependencies are pending recursion
+     resolution.  */
+  if (pendrecp)
+    *pendrecp = pending_recursion;
+
+  if (!pendrecp || !pending_recursion)
+    var->var_part[0].cur_loc = result;
+
+  return result;
+}
+
 /* 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.  */
 
 static rtx
-vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data)
+vt_expand_loc_callback (rtx x, bitmap regs,
+			int max_depth ATTRIBUTE_UNUSED,
+			void *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;
-  rtx cur_loc;
   decl_or_value dv;
   variable var;
-  location_chain loc;
-  rtx result, subreg, xret;
+  rtx result, subreg;
+  bool pending_recursion = false;
+  bool from_empty = false;
 
   switch (GET_CODE (x))
     {
     case SUBREG:
-      if (dummy)
-	{
-	  if (cselib_dummy_expand_value_rtx_cb (SUBREG_REG (x), regs,
-						max_depth - 1,
-						vt_expand_loc_callback, data))
-	    return pc_rtx;
-	  else
-	    return NULL;
-	}
-
       subreg = cselib_expand_value_rtx_cb (SUBREG_REG (x), regs,
-					   max_depth - 1,
+					   EXPR_DEPTH,
 					   vt_expand_loc_callback, data);
 
       if (!subreg)
@@ -7395,148 +7876,172 @@  vt_expand_loc_callback (rtx x, bitmap re
       return result;
 
     case DEBUG_EXPR:
-      dv = dv_from_decl (DEBUG_EXPR_TREE_DECL (x));
-      xret = NULL;
-      break;
-
     case VALUE:
-      dv = dv_from_value (x);
-      xret = x;
+      dv = dv_from_rtx (x);
       break;
 
     default:
       return x;
     }
 
-  if (VALUE_RECURSED_INTO (x))
+  VEC_safe_push (rtx, stack, elcd->expanding, x);
+
+  /* Check that VALUE_RECURSED_INTO implies NO_LOC_P.  */
+  gcc_checking_assert (!VALUE_RECURSED_INTO (x) || NO_LOC_P (x));
+
+  if (NO_LOC_P (x))
     return NULL;
 
   var = (variable) htab_find_with_hash (elcd->vars, dv, dv_htab_hash (dv));
 
   if (!var)
     {
-      if (dummy && dv_changed_p (dv))
-	elcd->cur_loc_changed = true;
-      return xret;
+      from_empty = true;
+      var = variable_from_dropped (dv, INSERT);
     }
 
-  if (var->n_var_parts == 0)
+  gcc_checking_assert (var);
+
+  if (!dv_changed_p (dv))
     {
-      if (dummy)
-	elcd->cur_loc_changed = true;
-      return xret;
-    }
+      gcc_checking_assert (!NO_LOC_P (x));
+      gcc_checking_assert (var->var_part[0].cur_loc);
+      gcc_checking_assert (VAR_LOC_1PAUX (var));
+      gcc_checking_assert (VAR_LOC_1PAUX (var)->depth);
 
-  gcc_assert (var->n_var_parts == 1);
+      elcd->depth = update_depth (elcd->depth, VAR_LOC_1PAUX (var)->depth);
+
+      return var->var_part[0].cur_loc;
+    }
 
   VALUE_RECURSED_INTO (x) = true;
-  result = NULL;
+  /* This is tentative, but it makes some tests simpler.  */
+  NO_LOC_P (x) = true;
 
-  if (var->var_part[0].cur_loc && !elcd->ignore_cur_loc)
+  gcc_checking_assert (var->n_var_parts == 1 || from_empty);
+
+  result = vt_expand_var_loc_chain (var, regs, data, &pending_recursion);
+
+  if (pending_recursion)
     {
-      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)
-	set_dv_changed (dv, false);
-      cur_loc = var->var_part[0].cur_loc;
+      gcc_checking_assert (!result);
+      VEC_safe_push (rtx, stack, elcd->pending, x);
     }
   else
-    cur_loc = NULL_RTX;
-  if (!result && (dv_changed_p (dv) || elcd->ignore_cur_loc))
     {
-      if (!elcd->ignore_cur_loc)
-	set_dv_changed (dv, false);
-      for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
-	if (loc->loc == 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;
-      if (!elcd->ignore_cur_loc)
-	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;
-  if (result)
-    return result;
-  else
-    return xret;
+      NO_LOC_P (x) = !result;
+      VALUE_RECURSED_INTO (x) = false;
+      set_dv_changed (dv, false);
+
+      if (result)
+	notify_dependents_of_resolved_value (var, elcd->vars);
+    }
+
+  return result;
+}
+
+/* While expanding variables, we may encounter recursion cycles
+   because of mutual (possibly indirect) dependencies between two
+   particular variables (or values), say A and B.  If we're trying to
+   expand A when we get to B, which in turn attempts to expand A, if
+   we can't find any other expansion for B, we'll add B to this
+   pending-recursion stack, and tentatively return NULL for its
+   location.  This tentative value will be used for any other
+   occurrences of B, unless A gets some other location, in which case
+   it will notify B that it is worth another try at computing a
+   location for it, and it will use the location computed for A then.
+   At the end of the expansion, the tentative NULL locations become
+   final for all members of PENDING that didn't get a notification.
+   This function performs this finalization of NULL locations.  */
+
+static void
+resolve_expansions_pending_recursion (VEC (rtx, stack) *pending)
+{
+  while (!VEC_empty (rtx, pending))
+    {
+      rtx x = VEC_pop (rtx, pending);
+      decl_or_value dv;
+
+      if (!VALUE_RECURSED_INTO (x))
+	continue;
+
+      gcc_checking_assert (NO_LOC_P (x));
+      VALUE_RECURSED_INTO (x) = false;
+      dv = dv_from_rtx (x);
+      gcc_checking_assert (dv_changed_p (dv));
+      set_dv_changed (dv, false);
+    }
 }
 
-/* Expand VALUEs in LOC, using VARS as well as cselib's equivalence
-   tables.  */
+/* Initialize expand_loc_callback_data D with variable hash table V.
+   It must be a macro because of alloca (VEC stack).  */
+#define INIT_ELCD(d, v)						\
+  do								\
+    {								\
+      (d).vars = (v);						\
+      (d).expanding = VEC_alloc (rtx, stack, 4);		\
+      (d).pending = VEC_alloc (rtx, stack, 4);			\
+      (d).depth = 0;						\
+    }								\
+  while (0)
+/* Finalize expand_loc_callback_data D, resolved to location L.  */
+#define FINI_ELCD(d, l)						\
+  do								\
+    {								\
+      resolve_expansions_pending_recursion ((d).pending);	\
+      VEC_free (rtx, stack, (d).pending);			\
+      VEC_free (rtx, stack, (d).expanding);			\
+								\
+      if ((l) && MEM_P (l))					\
+	(l) = targetm.delegitimize_address (l);			\
+    }								\
+  while (0)
+
+/* Expand VALUEs and DEBUG_EXPRs in LOC to a location, using the
+   equivalences in VARS, updating their CUR_LOCs in the process.  */
 
 static rtx
-vt_expand_loc (rtx loc, htab_t vars, bool ignore_cur_loc)
+vt_expand_loc (rtx loc, htab_t vars)
 {
   struct expand_loc_callback_data data;
+  rtx result;
 
   if (!MAY_HAVE_DEBUG_INSNS)
     return loc;
 
-  data.vars = vars;
-  data.dummy = false;
-  data.cur_loc_changed = false;
-  data.ignore_cur_loc = ignore_cur_loc;
-  loc = cselib_expand_value_rtx_cb (loc, scratch_regs, EXPR_DEPTH,
-				    vt_expand_loc_callback, &data);
+  INIT_ELCD (data, vars);
 
-  if (loc && MEM_P (loc))
-    loc = targetm.delegitimize_address (loc);
-  return loc;
+  result = cselib_expand_value_rtx_cb (loc, scratch_regs, EXPR_DEPTH,
+				       vt_expand_loc_callback, &data);
+
+  FINI_ELCD (data, result);
+
+  return result;
 }
 
-/* Like vt_expand_loc, but only return true/false (whether vt_expand_loc
-   would succeed or not, without actually allocating new rtxes.  */
+/* Expand the one-part VARiable to a location, using the equivalences
+   in VARS, updating their CUR_LOCs in the process.  */
 
-static bool
-vt_expand_loc_dummy (rtx loc, htab_t vars, bool *pcur_loc_changed)
+static rtx
+vt_expand_1pvar (variable var, htab_t vars)
 {
   struct expand_loc_callback_data data;
-  bool ret;
+  rtx loc;
 
-  gcc_assert (MAY_HAVE_DEBUG_INSNS);
-  data.vars = vars;
-  data.dummy = true;
-  data.cur_loc_changed = false;
-  data.ignore_cur_loc = false;
-  ret = cselib_dummy_expand_value_rtx_cb (loc, scratch_regs, EXPR_DEPTH,
-					  vt_expand_loc_callback, &data);
-  *pcur_loc_changed = data.cur_loc_changed;
-  return ret;
+  gcc_checking_assert (var->onepart && var->n_var_parts == 1);
+
+  if (!dv_changed_p (var->dv))
+    return var->var_part[0].cur_loc;
+
+  INIT_ELCD (data, vars);
+
+  loc = vt_expand_var_loc_chain (var, scratch_regs, &data, NULL);
+
+  gcc_checking_assert (VEC_empty (rtx, data.expanding));
+
+  FINI_ELCD (data, loc);
+
+  return loc;
 }
 
 /* Emit the NOTE_INSN_VAR_LOCATION for variable *VARP.  DATA contains
@@ -7561,49 +8066,57 @@  emit_note_insn_var_location (void **varp
   tree decl;
   location_chain lc;
 
-  if (dv_is_value_p (var->dv))
-    goto value_or_debug_decl;
+  gcc_checking_assert (var->onepart == NOT_ONEPART
+		       || var->onepart == ONEPART_VDECL);
 
   decl = dv_as_decl (var->dv);
 
-  if (TREE_CODE (decl) == DEBUG_EXPR_DECL)
-    goto value_or_debug_decl;
-
   complete = true;
   last_limit = 0;
   n_var_parts = 0;
-  if (!MAY_HAVE_DEBUG_INSNS)
-    {
-      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;
-    }
-  if (!var->cur_loc_changed)
-    goto clear;
+  if (!var->onepart)
+    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;
   for (i = 0; i < var->n_var_parts; i++)
     {
       enum machine_mode mode, wider_mode;
       rtx loc2;
+      HOST_WIDE_INT offset;
 
-      if (last_limit < var->var_part[i].offset)
+      if (i == 0 && var->onepart)
 	{
-	  complete = false;
-	  break;
+	  gcc_checking_assert (var->n_var_parts == 1);
+	  offset = 0;
+	  initialized = VAR_INIT_STATUS_INITIALIZED;
+	  loc2 = vt_expand_1pvar (var, vars);
 	}
-      else if (last_limit > var->var_part[i].offset)
-	continue;
-      offsets[n_var_parts] = var->var_part[i].offset;
-      if (!var->var_part[i].cur_loc)
+      else
 	{
-	  complete = false;
-	  continue;
+	  if (last_limit < VAR_PART_OFFSET (var, i))
+	    {
+	      complete = false;
+	      break;
+	    }
+	  else if (last_limit > VAR_PART_OFFSET (var, i))
+	    continue;
+	  offset = VAR_PART_OFFSET (var, i);
+	  if (!var->var_part[i].cur_loc)
+	    {
+	      complete = false;
+	      continue;
+	    }
+	  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);
+	  loc2 = var->var_part[i].cur_loc;
 	}
-      loc2 = vt_expand_loc (var->var_part[i].cur_loc, vars, false);
+
+      offsets[n_var_parts] = offset;
       if (!loc2)
 	{
 	  complete = false;
@@ -7611,29 +8124,22 @@  emit_note_insn_var_location (void **varp
 	}
       loc[n_var_parts] = loc2;
       mode = GET_MODE (var->var_part[i].cur_loc);
-      if (mode == VOIDmode && dv_onepart_p (var->dv))
+      if (mode == VOIDmode && var->onepart)
 	mode = DECL_MODE (decl);
-      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.  */
       wider_mode = GET_MODE_WIDER_MODE (mode);
       for (j = i + 1; j < var->n_var_parts; j++)
-	if (last_limit <= var->var_part[j].offset)
+	if (last_limit <= VAR_PART_OFFSET (var, j))
 	  break;
       if (j < var->n_var_parts
 	  && wider_mode != VOIDmode
 	  && 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]))
-	  && last_limit == var->var_part[j].offset
-	  && (loc2 = vt_expand_loc (var->var_part[j].cur_loc, vars, false))
+	  && last_limit == (var->onepart ? 0 : VAR_PART_OFFSET (var, j))
+	  && (loc2 = vt_expand_loc (var->var_part[j].cur_loc, vars))
 	  && GET_CODE (loc[n_var_parts]) == GET_CODE (loc2))
 	{
 	  rtx new_loc = NULL;
@@ -7746,152 +8252,152 @@  emit_note_insn_var_location (void **varp
     }
   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);
-DEF_VEC_ALLOC_P (variable, heap);
-
-/* Stack of variable_def pointers that need processing with
-   check_changed_vars_2.  */
-
-static VEC (variable, heap) *changed_variables_stack;
+/* While traversing changed_variables, push onto DATA (a stack of RTX
+   values) entries that aren't user variables.  */
 
-/* VALUEs with no variables that need set_dv_changed (val, false)
-   called before check_changed_vars_3.  */
+static int
+values_to_stack (void **slot, void *data)
+{
+  VEC (rtx, stack) **changed_values_stack = (VEC (rtx, stack) **)data;
+  variable var = (variable) *slot;
 
-static VEC (rtx, heap) *changed_values_stack;
+  if (var->onepart == ONEPART_VALUE)
+    VEC_safe_push (rtx, stack, *changed_values_stack, dv_as_value (var->dv));
+  else if (var->onepart == ONEPART_DEXPR)
+    VEC_safe_push (rtx, stack, *changed_values_stack,
+		   DECL_RTL_KNOWN_SET (dv_as_decl (var->dv)));
 
-/* Helper function for check_changed_vars_1 and check_changed_vars_2.  */
+  return 1;
+}
 
+/* Remove from changed_variables the entry whose DV corresponds to
+   value or debug_expr VAL.  */
 static void
-check_changed_vars_0 (decl_or_value dv, htab_t htab)
+remove_value_from_changed_variables (rtx val)
 {
-  value_chain vc
-    = (value_chain) htab_find_with_hash (value_chains, dv, dv_htab_hash (dv));
+  decl_or_value dv = dv_from_rtx (val);
+  void **slot;
+  variable var;
 
-  if (vc == NULL)
-    return;
-  for (vc = vc->next; vc; vc = vc->next)
-    if (!dv_changed_p (vc->dv))
-      {
-	variable vcvar
-	  = (variable) htab_find_with_hash (htab, vc->dv,
-					    dv_htab_hash (vc->dv));
-	if (vcvar)
-	  {
-	    set_dv_changed (vc->dv, true);
-	    VEC_safe_push (variable, heap, changed_variables_stack, vcvar);
-	  }
-	else if (dv_is_value_p (vc->dv))
-	  {
-	    set_dv_changed (vc->dv, true);
-	    VEC_safe_push (rtx, heap, changed_values_stack,
-			   dv_as_value (vc->dv));
-	    check_changed_vars_0 (vc->dv, htab);
-	  }
-      }
+  slot = htab_find_slot_with_hash (changed_variables,
+				   dv, dv_htab_hash (dv), NO_INSERT);
+  var = (variable) *slot;
+  var->in_changed_variables = false;
+  htab_clear_slot (changed_variables, slot);
 }
 
-/* Populate changed_variables_stack with variable_def pointers
-   that need variable_was_changed called on them.  */
+/* If VAL (a value or debug_expr) has backlinks to variables actively
+   dependent on it in HTAB or in CHANGED_VARIABLES, mark them as
+   changed, adding to CHANGED_VALUES_STACK any dependencies that may
+   have dependencies of their own to notify.  */
 
-static int
-check_changed_vars_1 (void **slot, void *data)
+static void
+notify_dependents_of_changed_value (rtx val, htab_t htab,
+				    VEC (rtx, stack) **changed_values_stack)
 {
-  variable var = (variable) *slot;
-  htab_t htab = (htab_t) data;
+  void **slot;
+  variable var;
+  loc_exp_dep *led;
+  decl_or_value dv = dv_from_rtx (val);
 
-  if (dv_is_value_p (var->dv)
-      || TREE_CODE (dv_as_decl (var->dv)) == DEBUG_EXPR_DECL)
-    check_changed_vars_0 (var->dv, htab);
-  return 1;
-}
+  slot = htab_find_slot_with_hash (changed_variables,
+				   dv, dv_htab_hash (dv), NO_INSERT);
+  if (!slot)
+    slot = htab_find_slot_with_hash (htab,
+				     dv, dv_htab_hash (dv), NO_INSERT);
+  if (!slot)
+    slot = htab_find_slot_with_hash (dropped_values,
+				     dv, dv_htab_hash (dv), NO_INSERT);
+  var = (variable) *slot;
 
-/* Add VAR to changed_variables and also for VALUEs add recursively
-   all DVs that aren't in changed_variables yet but reference the
-   VALUE from its loc_chain.  */
+  while ((led = VAR_LOC_DEP_LST (var)))
+    {
+      decl_or_value ldv = led->dv;
+      void **islot;
+      variable ivar;
+
+      /* Deactivate and remove the backlink, as it was “used up”.  It
+	 makes no sense to attempt to notify the same entity again:
+	 either it will be recomputed and re-register an active
+	 dependency, or it will still have the changed mark.  */
+      if (led->next)
+	led->next->pprev = led->pprev;
+      if (led->pprev)
+	*led->pprev = led->next;
+      led->next = NULL;
+      led->pprev = NULL;
 
-static void
-check_changed_vars_2 (variable var, htab_t htab)
-{
-  variable_was_changed (var, NULL);
-  if (dv_is_value_p (var->dv)
-      || TREE_CODE (dv_as_decl (var->dv)) == DEBUG_EXPR_DECL)
-    check_changed_vars_0 (var->dv, htab);
+      if (dv_changed_p (ldv))
+	continue;
+
+      switch (dv_onepart_p (ldv))
+	{
+	case ONEPART_VALUE:
+	case ONEPART_DEXPR:
+	  set_dv_changed (ldv, true);
+	  VEC_safe_push (rtx, stack, *changed_values_stack, dv_as_rtx (ldv));
+	  break;
+
+	default:
+	  islot = htab_find_slot_with_hash (htab, ldv, dv_htab_hash (ldv),
+					    NO_INSERT);
+	  ivar = (variable) *islot;
+	  gcc_checking_assert (!VAR_LOC_DEP_LST (ivar));
+	  variable_was_changed (ivar, NULL);
+	  break;
+	}
+    }
 }
 
-/* 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.  */
+/* Take out of changed_variables any entries that don't refer to use
+   variables.  Back-propagate change notifications from values and
+   debug_exprs to their active dependencies in HTAB or in
+   CHANGED_VARIABLES.  */
 
-static int
-check_changed_vars_3 (void **slot, void *data)
+static void
+process_changed_values (htab_t htab)
 {
-  variable var = (variable) *slot;
-  htab_t vars = (htab_t) data;
-  int i;
-  location_chain lc;
-  bool cur_loc_changed;
+  int i, n;
+  rtx val;
+  VEC (rtx, stack) *changed_values_stack = VEC_alloc (rtx, stack, 20);
 
-  if (dv_is_value_p (var->dv)
-      || TREE_CODE (dv_as_decl (var->dv)) == DEBUG_EXPR_DECL)
-    return 1;
+  /* Move values from changed_variables to changed_values_stack.  */
+  htab_traverse (changed_variables, values_to_stack, &changed_values_stack);
 
-  for (i = 0; i < var->n_var_parts; i++)
+  /* Back-propagate change notifications in values while popping
+     them from the stack.  */
+  for (n = i = VEC_length (rtx, changed_values_stack);
+       i > 0; i = VEC_length (rtx, changed_values_stack))
     {
-      if (var->var_part[i].cur_loc
-	  && vt_expand_loc_dummy (var->var_part[i].cur_loc, vars,
-				  &cur_loc_changed))
+      val = VEC_pop (rtx, changed_values_stack);
+      notify_dependents_of_changed_value (val, htab, &changed_values_stack);
+
+      /* This condition will hold when visiting each of the entries
+	 originally in changed_variables.  We can't remove them
+	 earlier because this could drop the backlinks before we got a
+	 chance to use them.  */
+      if (i == n)
 	{
-	  if (cur_loc_changed)
-	    var->cur_loc_changed = true;
-	  continue;
+	  remove_value_from_changed_variables (val);
+	  n--;
 	}
-      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;
+
+  VEC_free (rtx, stack, changed_values_stack);
 }
 
 /* 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.  */
+   CHANGED_VARIABLES and delete this chain.  WHERE specifies whether
+   the notes shall be emitted before of after instruction INSN.  */
 
 static void
 emit_notes_for_changes (rtx insn, enum emit_note_where where,
@@ -7904,19 +8410,7 @@  emit_notes_for_changes (rtx insn, enum e
     return;
 
   if (MAY_HAVE_DEBUG_INSNS)
-    {
-      /* Unfortunately this has to be done in two steps, because
-	 we can't traverse a hashtab into which we are inserting
-	 through variable_was_changed.  */
-      htab_traverse (changed_variables, check_changed_vars_1, htab);
-      while (VEC_length (variable, changed_variables_stack) > 0)
-	check_changed_vars_2 (VEC_pop (variable, changed_variables_stack),
-			      htab);
-      while (VEC_length (rtx, changed_values_stack) > 0)
-	set_dv_changed (dv_from_value (VEC_pop (rtx, changed_values_stack)),
-			false);
-      htab_traverse (changed_variables, check_changed_vars_3, htab);
-    }
+    process_changed_values (htab);
 
   data.insn = insn;
   data.where = where;
@@ -7941,78 +8435,59 @@  emit_notes_for_differences_1 (void **slo
   if (!new_var)
     {
       /* Variable has disappeared.  */
-      variable empty_var;
+      variable empty_var = NULL;
 
-      empty_var = (variable) pool_alloc (dv_pool (old_var->dv));
-      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;
-
-	  gcc_assert (old_var->n_var_parts == 1);
-	  for (lc = old_var->var_part[0].loc_chain; lc; lc = lc->next)
-	    remove_value_chains (old_var->dv, lc->loc);
-	}
-      variable_was_changed (empty_var, NULL);
-      /* Continue traversing the hash table.  */
-      return 1;
-    }
-  if (variable_different_p (old_var, new_var))
-    {
-      if (dv_onepart_p (old_var->dv))
+      if (old_var->onepart == ONEPART_VALUE
+	  || old_var->onepart == ONEPART_DEXPR)
 	{
-	  location_chain lc1, lc2;
+	  empty_var = variable_from_dropped (old_var->dv, NO_INSERT);
+	  if (empty_var)
+	    {
+	      gcc_checking_assert (!empty_var->in_changed_variables);
+	      if (!VAR_LOC_1PAUX (old_var))
+		{
+		  VAR_LOC_1PAUX (old_var) = VAR_LOC_1PAUX (empty_var);
+		  VAR_LOC_1PAUX (empty_var) = NULL;
+		}
+	      else
+		gcc_checking_assert (!VAR_LOC_1PAUX (empty_var));
+	    }
+	}
 
-	  gcc_assert (old_var->n_var_parts == 1
-		      && new_var->n_var_parts == 1);
-	  lc1 = old_var->var_part[0].loc_chain;
-	  lc2 = new_var->var_part[0].loc_chain;
-	  while (lc1
-		 && lc2
-		 && ((REG_P (lc1->loc) && REG_P (lc2->loc))
-		     || rtx_equal_p (lc1->loc, lc2->loc)))
-	    {
-	      lc1 = lc1->next;
-	      lc2 = lc2->next;
-	    }
-	  for (; lc2; lc2 = lc2->next)
-	    add_value_chains (old_var->dv, lc2->loc);
-	  for (; lc1; lc1 = lc1->next)
-	    remove_value_chains (old_var->dv, lc1->loc);
+      if (!empty_var)
+	{
+	  empty_var = (variable) pool_alloc (onepart_pool (old_var->onepart));
+	  empty_var->dv = old_var->dv;
+	  empty_var->refcount = 0;
+	  empty_var->n_var_parts = 0;
+	  empty_var->onepart = old_var->onepart;
+	  empty_var->in_changed_variables = false;
 	}
-      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++)
+
+      if (empty_var->onepart)
 	{
-	  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;
-	    }
+	  /* Propagate the auxiliary data to (ultimately)
+	     changed_variables.  */
+	  empty_var->var_part[0].loc_chain = NULL;
+	  empty_var->var_part[0].cur_loc = NULL;
+	  VAR_LOC_1PAUX (empty_var) = VAR_LOC_1PAUX (old_var);
+	  VAR_LOC_1PAUX (old_var) = NULL;
 	}
+      variable_was_changed (empty_var, NULL);
+      /* Continue traversing the hash table.  */
+      return 1;
+    }
+  /* Update cur_loc and one-part auxiliary data, before new_var goes
+     through variable_was_changed.  */
+  if (old_var != new_var && new_var->onepart)
+    {
+      gcc_checking_assert (VAR_LOC_1PAUX (new_var) == NULL);
+      VAR_LOC_1PAUX (new_var) = VAR_LOC_1PAUX (old_var);
+      VAR_LOC_1PAUX (old_var) = NULL;
+      new_var->var_part[0].cur_loc = old_var->var_part[0].cur_loc;
     }
+  if (variable_different_p (old_var, new_var))
+    variable_was_changed (new_var, NULL);
 
   /* Continue traversing the hash table.  */
   return 1;
@@ -8033,15 +8508,6 @@  emit_notes_for_differences_2 (void **slo
   if (!old_var)
     {
       int i;
-      /* Variable has appeared.  */
-      if (dv_onepart_p (new_var->dv))
-	{
-	  location_chain lc;
-
-	  gcc_assert (new_var->n_var_parts == 1);
-	  for (lc = new_var->var_part[0].loc_chain; lc; lc = lc->next)
-	    add_value_chains (new_var->dv, lc->loc);
-	}
       for (i = 0; i < new_var->n_var_parts; i++)
 	new_var->var_part[i].cur_loc = NULL;
       variable_was_changed (new_var, NULL);
@@ -8111,7 +8577,7 @@  emit_notes_in_bb (basic_block bb, datafl
 		{
 		  XEXP (XEXP (*p, 0), 1)
 		    = vt_expand_loc (XEXP (XEXP (*p, 0), 1),
-				     shared_hash_htab (set->vars), true);
+				     shared_hash_htab (set->vars));
 		  /* If expansion is successful, keep it in the list.  */
 		  if (XEXP (XEXP (*p, 0), 1))
 		    p = &XEXP (*p, 1);
@@ -8412,15 +8878,9 @@  vt_emit_notes (void)
   emit_notes = true;
 
   if (MAY_HAVE_DEBUG_INSNS)
-    {
-      unsigned int i;
-      rtx val;
-
-      FOR_EACH_VEC_ELT (rtx, preserved_values, i, val)
-	add_cselib_value_chains (dv_from_value (val));
-      changed_variables_stack = VEC_alloc (variable, heap, 40);
-      changed_values_stack = VEC_alloc (rtx, heap, 40);
-    }
+    dropped_values = htab_create (cselib_get_next_uid () * 2,
+				  variable_htab_hash, variable_htab_eq,
+				  variable_htab_free);
 
   dataflow_set_init (&cur);
 
@@ -8441,23 +8901,11 @@  vt_emit_notes (void)
   htab_traverse (shared_hash_htab (cur.vars),
 		 emit_notes_for_differences_1,
 		 shared_hash_htab (empty_shared_hash));
-  if (MAY_HAVE_DEBUG_INSNS)
-    {
-      unsigned int i;
-      rtx val;
-
-      FOR_EACH_VEC_ELT (rtx, preserved_values, i, val)
-	remove_cselib_value_chains (dv_from_value (val));
-      gcc_assert (htab_elements (value_chains) == 0);
-    }
 #endif
   dataflow_set_destroy (&cur);
 
   if (MAY_HAVE_DEBUG_INSNS)
-    {
-      VEC_free (variable, heap, changed_variables_stack);
-      VEC_free (rtx, heap, changed_values_stack);
-    }
+    htab_delete (dropped_values);
 
   emit_notes = false;
 }
@@ -8489,36 +8937,27 @@  vt_get_decl_and_offset (rtx rtl, tree *d
   return false;
 }
 
-/* Helper function for vt_add_function_parameter.  RTL is
-   the expression and VAL corresponding cselib_val pointer
-   for which ENTRY_VALUE should be created.  */
-
-static void
-create_entry_value (rtx rtl, cselib_val *val)
-{
-  cselib_val *val2;
-  struct elt_loc_list *el;
-  el = (struct elt_loc_list *) ggc_alloc_cleared_atomic (sizeof (*el));
-  el->loc = gen_rtx_ENTRY_VALUE (GET_MODE (rtl));
-  ENTRY_VALUE_EXP (el->loc) = rtl;
-  val2 = cselib_lookup_from_insn (el->loc, GET_MODE (rtl), true,
-				  VOIDmode, get_insns ());
-  el->next = val->locs;
-  el->setting_insn = get_insns ();
-  val->locs = el;
-  if (val2
-      && val2 != val
-      && val2->locs
-      && rtx_equal_p (val2->locs->loc, el->loc))
-    {
-      struct elt_loc_list *el2;
-
-      preserve_value (val2);
-      el2 = (struct elt_loc_list *) ggc_alloc_cleared_atomic (sizeof (*el2));
-      el2->next = val2->locs;
-      el2->loc = val->val_rtx;
-      el2->setting_insn = get_insns ();
-      val2->locs = el2;
+/* Mark the value for the ENTRY_VALUE of RTL as equivalent to EQVAL in
+   OUT.  */
+
+static void
+create_entry_value (dataflow_set *out, rtx eqval, rtx rtl)
+{
+  rtx ev = gen_rtx_ENTRY_VALUE (GET_MODE (rtl));
+  cselib_val *val;
+
+  ENTRY_VALUE_EXP (ev) = rtl;
+
+  val = cselib_lookup_from_insn (ev, GET_MODE (ev), true,
+				 VOIDmode, get_insns ());
+
+  if (val->val_rtx != eqval)
+    {
+      preserve_value (val);
+      set_variable_part (out, val->val_rtx, dv_from_value (eqval), 0,
+			 VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT);
+      set_variable_part (out, eqval, dv_from_value (val->val_rtx), 0,
+			 VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT);
     }
 }
 
@@ -8678,20 +9117,22 @@  vt_add_function_parameter (tree parm)
 			 VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
       if (dv_is_value_p (dv))
 	{
-	  cselib_val *val = CSELIB_VAL_PTR (dv_as_value (dv));
-	  create_entry_value (incoming, val);
+	  create_entry_value (out, dv_as_value (dv), incoming);
 	  if (TREE_CODE (TREE_TYPE (parm)) == REFERENCE_TYPE
 	      && INTEGRAL_TYPE_P (TREE_TYPE (TREE_TYPE (parm))))
 	    {
 	      enum machine_mode indmode
 		= TYPE_MODE (TREE_TYPE (TREE_TYPE (parm)));
 	      rtx mem = gen_rtx_MEM (indmode, incoming);
-	      val = cselib_lookup_from_insn (mem, indmode, true,
-					     VOIDmode, get_insns ());
+	      cselib_val *val = cselib_lookup_from_insn (mem, indmode, true,
+							 VOIDmode,
+							 get_insns ());
 	      if (val)
 		{
 		  preserve_value (val);
-		  create_entry_value (mem, val);
+		  set_variable_part (out, mem, dv_from_value (val->val_rtx), 0,
+				     VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
+		  create_entry_value (out, val->val_rtx, mem);
 		}
 	    }
 	}
@@ -8755,23 +9196,6 @@  fp_setter (rtx insn)
   return false;
 }
 
-/* Gather all registers used for passing arguments to other functions
-   called from the current routine.  */
-
-static void
-note_register_arguments (rtx insn)
-{
-  rtx link, x;
-
-  for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
-    if (GET_CODE (XEXP (link, 0)) == USE)
-      {
-	x = XEXP (XEXP (link, 0), 0);
-	if (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER)
-	  SET_HARD_REG_BIT (argument_reg_set, REGNO (x));
-      }
-}
-
 /* Initialize cfa_base_rtx, create a preserved VALUE for it and
    ensure it isn't flushed during cselib_reset_table.
    Can be called only if frame_pointer_rtx resp. arg_pointer_rtx
@@ -8843,14 +9267,6 @@  vt_initialize (void)
 		   variable_htab_free);
   changed_variables = htab_create (10, variable_htab_hash, variable_htab_eq,
 				   variable_htab_free);
-  if (MAY_HAVE_DEBUG_INSNS)
-    {
-      value_chain_pool = create_alloc_pool ("value_chain_def pool",
-					    sizeof (struct value_chain_def),
-					    1024);
-      value_chains = htab_create (32, value_chain_htab_hash,
-				  value_chain_htab_eq, NULL);
-    }
 
   /* Init the IN and OUT sets.  */
   FOR_ALL_BB (bb)
@@ -8876,8 +9292,6 @@  vt_initialize (void)
       valvar_pool = NULL;
     }
 
-  CLEAR_HARD_REG_SET (argument_reg_set);
-
   /* In order to factor out the adjustments made to the stack pointer or to
      the hard frame pointer and thus be able to use DW_OP_fbreg operations
      instead of individual location lists, we're going to rewrite MEMs based
@@ -8962,14 +9376,6 @@  vt_initialize (void)
 	}
     }
 
-  if (frame_pointer_needed)
-    {
-      rtx insn;
-      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
-	if (CALL_P (insn))
-	  note_register_arguments (insn);
-    }
-
   hard_frame_pointer_adjustment = -1;
 
   vt_add_function_parameters ();
@@ -9155,8 +9561,6 @@  vt_finalize (void)
 
   if (MAY_HAVE_DEBUG_INSNS)
     {
-      htab_delete (value_chains);
-      free_alloc_pool (value_chain_pool);
       free_alloc_pool (valvar_pool);
       VEC_free (rtx, heap, preserved_values);
       cselib_finish ();
@@ -9164,7 +9568,9 @@  vt_finalize (void)
       scratch_regs = NULL;
     }
 
+#ifdef HAVE_window_save
   VEC_free (parm_reg_t, gc, windowed_parm_regs);
+#endif
 
   if (vui_vec)
     XDELETEVEC (vui_vec);
Index: gcc/rtl.h
===================================================================
--- gcc/rtl.h.orig	2011-10-18 11:39:35.634815605 -0200
+++ gcc/rtl.h	2011-10-18 11:48:50.690205297 -0200
@@ -265,7 +265,8 @@  struct GTY((chain_next ("RTX_NEXT (&%h)"
      when we access a component.
      1 in a CALL_INSN if it is a sibling call.
      1 in a SET that is for a return.
-     In a CODE_LABEL, part of the two-bit alternate entry field.  */
+     In a CODE_LABEL, part of the two-bit alternate entry field.
+     1 in a CONCAT is VAL_EXPR_IS_COPIED in var-tracking.c.  */
   unsigned int jump : 1;
   /* In a CODE_LABEL, part of the two-bit alternate entry field.
      1 in a MEM if it cannot trap.
@@ -278,7 +279,9 @@  struct GTY((chain_next ("RTX_NEXT (&%h)"
      constants pool.
      1 in a CALL_INSN logically equivalent to ECF_CONST and TREE_READONLY.
      1 in a NOTE, or EXPR_LIST for a const call.
-     1 in a JUMP_INSN of an annulling branch.  */
+     1 in a JUMP_INSN of an annulling branch.
+     1 in a CONCAT is VAL_EXPR_IS_CLOBBERED in var-tracking.c.
+     1 in a preserved VALUE is PRESERVED_VALUE_P in cselib.c.  */
   unsigned int unchanging : 1;
   /* 1 in a MEM or ASM_OPERANDS expression if the memory reference is volatile.
      1 in an INSN, CALL_INSN, JUMP_INSN, CODE_LABEL, BARRIER, or NOTE
@@ -290,7 +293,8 @@  struct GTY((chain_next ("RTX_NEXT (&%h)"
      non-local label.
      In a SYMBOL_REF, this flag is used for machine-specific purposes.
      In a PREFETCH, this flag indicates that it should be considered a scheduling
-     barrier.  */
+     barrier.
+     1 in a CONCAT is VAL_NEEDS_RESOLUTION in var-tracking.c.  */
   unsigned int volatil : 1;
   /* 1 in a MEM referring to a field of an aggregate.
      0 if the MEM was a variable or the result of a * operator in C;
@@ -311,19 +315,24 @@  struct GTY((chain_next ("RTX_NEXT (&%h)"
      In a REG, this is not needed for that purpose, and used instead
      in `leaf_renumber_regs_insn'.
      1 in a SYMBOL_REF, means that emit_library_call
-     has used it as the function.  */
+     has used it as the function.
+     1 in a CONCAT is VAL_HOLDS_TRACK_EXPR in var-tracking.c.
+     1 in a VALUE or DEBUG_EXPR is VALUE_RECURSED_INTO in var-tracking.c.  */
   unsigned int used : 1;
   /* 1 in an INSN or a SET if this rtx is related to the call frame,
      either changing how we compute the frame address or saving and
      restoring registers in the prologue and epilogue.
      1 in a REG or MEM if it is a pointer.
      1 in a SYMBOL_REF if it addresses something in the per-function
-     constant string pool.  */
+     constant string pool.
+     1 in a VALUE is VALUE_CHANGED in var-tracking.c.  */
   unsigned frame_related : 1;
   /* 1 in a REG or PARALLEL that is the current function's return value.
      1 in a MEM if it refers to a scalar.
      1 in a SYMBOL_REF for a weak symbol.
-     1 in a CALL_INSN logically equivalent to ECF_PURE and DECL_PURE_P. */
+     1 in a CALL_INSN logically equivalent to ECF_PURE and DECL_PURE_P.
+     1 in a CONCAT is VAL_EXPR_HAS_REVERSE in var-tracking.c.
+     1 in a VALUE or DEBUG_EXPR is NO_LOC_P in var-tracking.c.  */
   unsigned return_val : 1;
 
   /* The first element of the operands of this rtx.
Index: gcc/doc/invoke.texi
===================================================================
--- gcc/doc/invoke.texi.orig	2011-10-18 11:39:35.293820899 -0200
+++ gcc/doc/invoke.texi	2011-10-18 11:48:50.727204723 -0200
@@ -9081,8 +9081,7 @@  compile time for more complete debug inf
 low, value expressions that are available and could be represented in
 debug information may end up not being used; setting this higher may
 enable the compiler to find more complex debug expressions, but compile
-time may grow exponentially, and even then, it may fail to find more
-usable expressions.  The default is 10.
+time and memory use may grow.  The default is 12.
 
 @item min-nondebug-insn-uid
 Use uids starting at this parameter for nondebug insns.  The range below
Index: gcc/tree.h
===================================================================
--- gcc/tree.h.orig	2011-10-18 11:37:14.904000920 -0200
+++ gcc/tree.h	2011-10-18 11:48:50.733204630 -0200
@@ -2967,6 +2967,17 @@  extern void decl_value_expr_insert (tree
 /* The DECL_RTL for NODE, if it is set, or NULL, if it is not set.  */
 #define DECL_RTL_IF_SET(NODE) (DECL_RTL_SET_P (NODE) ? DECL_RTL (NODE) : NULL)
 
+#if (GCC_VERSION >= 2007)
+#define DECL_RTL_KNOWN_SET(decl) __extension__				\
+({  tree const __d = (decl);						\
+    gcc_checking_assert (DECL_RTL_SET_P (__d));				\
+    /* Dereference it so the compiler knows it can't be NULL even	\
+       without assertion checking.  */					\
+    &*DECL_RTL_IF_SET (__d); })
+#else
+#define DECL_RTL_KNOWN_SET(decl) (&*DECL_RTL_IF_SET (decl))
+#endif
+
 /* In VAR_DECL and PARM_DECL nodes, nonzero means declared `register'.  */
 #define DECL_REGISTER(NODE) (DECL_WRTL_CHECK (NODE)->decl_common.decl_flag_0)