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

login
register
mail settings
Submitter Alexandre Oliva
Date Sept. 19, 2011, 9:17 p.m.
Message ID <or8vpknsff.fsf@livre.localdomain>
Download mbox | patch
Permalink /patch/115394/
State New
Headers show

Comments

Alexandre Oliva - Sept. 19, 2011, 9:17 p.m.
var-tracking has various algorithms that are known to be inefficient.
This patch tackles one of them, that took exponential time in the worst
case, for something that could be done in linear time.

While before we kept back-links for all locs, with this patch we keep
them only for active locs, i.e., those that determine cur_loc.  Thus,
when the location of a VALUE changes, we only notify those that actually
relied on the earlier location of that VALUE.  Also, we don't insist in
computing locations for all VALUEs, only user variables and values they
depend on.

The new location expansion algorithm saves memory through RTL sharing,
and it's much faster because, for each round of changes, it won't expand
any value more than once, caching the full expansion until a change
comes about.  If the expansion of a value hits a recursion cycle, we may
return and cache a tentative NULL, that will only be recomputed, if
needed, after any of the dependencies gets a non-NULL expansion.  So, we
may get O(n^2) expansions (total, not per n) in a very worst case, with
convoluted expressions and dependency graphs whose m approaches n^2.  In
regular cases, we'll just visit each relevant VALUE once and be done
with it, while irrelevant VALUEs cost little as they are left with the
changed flag set and an empty backlinks list.

Other minor improvements are that I took some padding space to hold what
kind of decl/value/debug_expr is in a variable, so we don't have to test
it that often.  I also used an RTL flag in VALUEs and DEBUG_EXPRs to
indicate whether their current expansion is NULL, so we can refrain from
looking them up in the variable table.

One change that may be detrimental to performance (not sure) is that I
decided that using the implied cselib_val locations in location
expansion was confusing and potentially wrong, so I made them explicit
in the var-tracking table for all values.  This makes room for releasing
cselib information after initial analysis, in a future patch.

Another future improvement is to split up changed_variables upfront,
inserting values and expr_decls in another table, so that we can further
simplify the processing of values, avoiding the iteration over
changed_variables that we perform now.  (Hey, but it's already much
better than the 3 or 4 passes that we did before, right? :-)

I think this sums up the main points of the patch.  It doesn't make much
of a difference in overall regstrap time, but it does makes the reported
testcase about 10% faster, in the cases in which it didn't hit
exponential behavior, and the case that did now takes about the same
time as those that didn't.  Yay!

Regstrapped on x86_64-linux-gnu and i686-pc-linux-gnu.  Ok to install?
Jakub Jelinek - Sept. 20, 2011, 10:44 a.m.
Hi!

First of all, I'm worried about generated debug info quality.

obj81{4,5,6,7}/gcc/cc1plus below are x86_64/i686 unpatched trunk
stage3 compilers (with var-tracking.c patched afterwards and make cc1plus
in the stage3 gcc directory, so it is the same code, just built with
different compiler) and x86_64/i686 patched trunk stage3, all of
them --enable-checking=yes,rtl.

What worries me is that we suddenly emit far less than half of the
previous call_site_value parameters:
for i in 81{4,5,6,7}; do readelf -wio obj$i/gcc/cc1plus | grep 'DW_AT_GNU_call_site_value' | wc -l; done
945514
901463
398378
211542
Those need to be emitted uncached, while it seems you are now caching them
too.

Another thing, is it really necessary to add the cselib locations to the
hash tables, and if yes, can't it be postponed to start of vt_emit* phase
as opposed to making all the hash table data larger already during the
second phase which is for many testcases very memory hungry?
And, do you do anything special for ENTRY_VALUEs?  The current code
is trying hard to emit something non-ENTRY_VALUE if at all possible,
those really should be the last options if nothing else can be used.
Looking at the counts
for i in 81{4,5,6,7}; do readelf -wio obj$i/gcc/cc1plus | grep 'DW_OP_GNU_entry_value' | wc -l; done
121713
36158
116585
37687
suggests that the entry_value count increased for i686 and somewhat
decreased for x86_64 (but wonder if the decreasing isn't because of less
than half of DW_AT_GNU_call_site_value which often use entry_value).

Attached below are
for i in 81{4,5,6,7}; do locstats --tabulate=0.0:5,99:1 obj$i/gcc/cc1plus 2>/dev/null > $i; done
numbers, from quick look the number of vars with 0.0% coverage went slightly
down, but the cumulative numbers for all other percentages are worse with
the patch (i.e. fewer variables with 100% coverage, etc.), though not by a
huge margin.

And just a few comments so far:

On Mon, Sep 19, 2011 at 06:17:08PM -0300, Alexandre Oliva wrote:
> +/* 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.  */

DEBUG_EXPR.

> +/* A narrower type used to hold a onepart_enum while saving
> +   memory.  */
> +typedef char onepart_enum_t;
> +
>  /* Structure describing where the variable is located.  */
>  typedef struct variable_def
>  {
> @@ -330,10 +397,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.  */
> +  onepart_enum_t onepart;

Can't you use ENUM_BITFIELD (onepart_enum) onepart : 8; instead?

> @@ -420,7 +515,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 *);
> @@ -672,15 +766,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;
>  
> @@ -5020,9 +5026,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
> @@ -5382,8 +5396,6 @@ 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)
>  	    {

Why the above?  Isn't it too expensive to enter it for all the registers,
even when they aren't going to be used for argument passing nor needed
explicitly for anything debug info related?

> @@ -5893,7 +5905,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_IF_SET (dtemp));

Generating CONCAT with NULL second argument?  Ugh.  Plus, when will
that happen?

	Jakub
cov%	samples	cumul
0.0	219173/31%	219173/31%
0..5	6579/0%	225752/32%
6..10	5342/0%	231094/33%
11..15	5371/0%	236465/34%
16..20	6080/0%	242545/35%
21..25	12476/1%	255021/37%
26..30	5474/0%	260495/37%
31..35	7413/1%	267908/39%
36..40	5668/0%	273576/39%
41..45	7813/1%	281389/41%
46..50	8710/1%	290099/42%
51..55	6909/1%	297008/43%
56..60	7451/1%	304459/44%
61..65	5772/0%	310231/45%
66..70	7230/1%	317461/46%
71..75	9040/1%	326501/47%
76..80	9801/1%	336302/49%
81..85	8839/1%	345141/50%
86..90	16557/2%	361698/52%
91..95	24964/3%	386662/56%
96..99	52713/7%	439375/64%
100	246505/35%	685880/100%
cov%	samples	cumul
0.0	209920/31%	209920/31%
0..5	6825/1%	216745/32%
6..10	5694/0%	222439/33%
11..15	5667/0%	228106/34%
16..20	5905/0%	234011/35%
21..25	11175/1%	245186/36%
26..30	4769/0%	249955/37%
31..35	6442/0%	256397/38%
36..40	6099/0%	262496/39%
41..45	7022/1%	269518/40%
46..50	8882/1%	278400/41%
51..55	6119/0%	284519/42%
56..60	6702/1%	291221/43%
61..65	5475/0%	296696/44%
66..70	9310/1%	306006/46%
71..75	6728/1%	312734/47%
76..80	7895/1%	320629/48%
81..85	8063/1%	328692/49%
86..90	11418/1%	340110/51%
91..95	25606/3%	365716/55%
96..99	53217/8%	418933/63%
100	244349/36%	663282/100%
cov%	samples	cumul
0.0	218500/31%	218500/31%
0..5	10785/1%	229285/33%
6..10	8836/1%	238121/34%
11..15	6748/0%	244869/35%
16..20	6874/1%	251743/36%
21..25	12015/1%	263758/38%
26..30	5910/0%	269668/39%
31..35	7650/1%	277318/40%
36..40	7630/1%	284948/41%
41..45	7931/1%	292879/42%
46..50	8529/1%	301408/43%
51..55	6943/1%	308351/44%
56..60	6787/0%	315138/45%
61..65	5748/0%	320886/46%
66..70	7387/1%	328273/47%
71..75	9770/1%	338043/49%
76..80	10165/1%	348208/50%
81..85	9072/1%	357280/52%
86..90	15499/2%	372779/54%
91..95	22655/3%	395434/57%
96..99	47049/6%	442483/64%
100	243400/35%	685883/100%
cov%	samples	cumul
0.0	213248/32%	213248/32%
0..5	15882/2%	229130/34%
6..10	8427/1%	237557/35%
11..15	7635/1%	245192/36%
16..20	7442/1%	252634/38%
21..25	10426/1%	263060/39%
26..30	5997/0%	269057/40%
31..35	6915/1%	275972/41%
36..40	6876/1%	282848/42%
41..45	7326/1%	290174/43%
46..50	8254/1%	298428/44%
51..55	6048/0%	304476/45%
56..60	6450/0%	310926/46%
61..65	5147/0%	316073/47%
66..70	8485/1%	324558/48%
71..75	6264/0%	330822/49%
76..80	7297/1%	338119/50%
81..85	7218/1%	345337/52%
86..90	11314/1%	356651/53%
91..95	23635/3%	380286/57%
96..99	48066/7%	428352/64%
100	234927/35%	663279/100%
Alexandre Oliva - Sept. 20, 2011, 11:19 a.m.
On Sep 20, 2011, Jakub Jelinek <jakub@redhat.com> wrote:

> What worries me is that we suddenly emit far less than half of the
> previous call_site_value parameters:

Thanks for the measurements, I'll look into that.

> Those need to be emitted uncached

Would you please remind me why that is?  The reason totally escapes me.

> is it really necessary to add the cselib locations to the hash tables

Not sure, I'll have to think some more about it.

> can't it be postponed to start of vt_emit* phase

If my hunch is correct, no.  My concern is precisely that the equivalent
may cease to hold (say once we cross a val_reset within a loop), but
we'll keep on relying on it.

> And, do you do anything special for ENTRY_VALUEs?

Not really.  They should be at the end of the location list, so they're
only used as a last resort, but I haven't checked that the canonical
sort order does indeed push them to the very end, so they might be among
complex expressions, but not before REGs, MEMs and other VALUE
equivalences.

> suggests that the entry_value count increased for i686

Will look.

> On Mon, Sep 19, 2011 at 06:17:08PM -0300, Alexandre Oliva wrote:
>> /* 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.  */
>> +  onepart_enum_t onepart;

> Can't you use ENUM_BITFIELD (onepart_enum) onepart : 8; instead?

I don't think it will then be packed in the same word as n_var_parts.

>> 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)
>> {

> Why the above?

That's what “stop relying on cselib-implied locs” is about.

> Isn't it too expensive to enter it for all the registers,
> even when they aren't going to be used for argument passing nor needed
> explicitly for anything debug info related?

How do you know they're not going to be used?  If the register is
overwritten afterwards, the original expression still serves as an
alternate location for the VALUE.  If we took it from the cselib_val loc
list, we'd still have it (assuming it's right), but if we stop using
cselib locs (like this patch does), if we didn't record the equivalent
in the dataflow_set table, we'd lose it.

>> -	      item = gen_rtx_CONCAT (mode, item, DECL_RTL (dtemp));
>> +	      item = gen_rtx_CONCAT (mode, item, DECL_RTL_IF_SET (dtemp));

> Generating CONCAT with NULL second argument?

No, it is always set for dtemp, but DECL_RTL_IF_SET is cheaper because
it doesn't test whether it's set and call the setter.
Jakub Jelinek - Sept. 20, 2011, 11:37 a.m.
On Tue, Sep 20, 2011 at 08:19:02AM -0300, Alexandre Oliva wrote:
> > Those need to be emitted uncached
> 
> Would you please remind me why that is?  The reason totally escapes me.

For NOTE_INSN_VAR_LOCATION, we are caching because we prefer to create
shorter .debug_loc location lists over longer ones (and also to save
memory), if an old location is still valid, we can just extend its range
instead of saying the var now lives somewhere else.
For NOTE_INSN_CALL_ARG_LOCATION, the locations aren't location lists, but
a single location at the point of the call.  They are independent of
all other locations, so any kind of caching only decreases the chance
that a suitable location is found (and as the numbers show, it decreases
it a lot).

> > can't it be postponed to start of vt_emit* phase
> 
> If my hunch is correct, no.  My concern is precisely that the equivalent
> may cease to hold (say once we cross a val_reset within a loop), but
> we'll keep on relying on it.

After vt_initialize, all cselib locations should hold are just some
expressions containing VALUEs, constants, ENTRY_VALUEs and nothing else,
all REGs and MEMs are supposed to be flushed.  Those are equivalences that
are always constant, they don't need any kind of resetting and they never
cease to hold.  Say VALUE3 is always VALUE1 + VALUE2.

> > Can't you use ENUM_BITFIELD (onepart_enum) onepart : 8; instead?
> 
> I don't think it will then be packed in the same word as n_var_parts.

enum A { B, C, D };
struct S { char a; enum A b : 8; char c; char d; };
int i = sizeof (struct S);

results in i = 4 for all the cc1 and cc1plus cross compilers on my box I've
tried, with various ABI options.

> That's what “stop relying on cselib-implied locs” is about.
> 
> > Isn't it too expensive to enter it for all the registers,
> > even when they aren't going to be used for argument passing nor needed
> > explicitly for anything debug info related?
> 
> How do you know they're not going to be used?  If the register is
> overwritten afterwards, the original expression still serves as an
> alternate location for the VALUE.  If we took it from the cselib_val loc
> list, we'd still have it (assuming it's right), but if we stop using
> cselib locs (like this patch does), if we didn't record the equivalent
> in the dataflow_set table, we'd lose it.

They weren't used by var-tracking before at all, for call arg locations
that resulted in worse debug info though, so I've special cased the
registers using for passing arguments.  I guess we could meassure the
memory/hash table size/time requirements of handling all registers here,
or just argument passing ones.

> >> -	      item = gen_rtx_CONCAT (mode, item, DECL_RTL (dtemp));
> >> +	      item = gen_rtx_CONCAT (mode, item, DECL_RTL_IF_SET (dtemp));
> 
> > Generating CONCAT with NULL second argument?
> 
> No, it is always set for dtemp, but DECL_RTL_IF_SET is cheaper because
> it doesn't test whether it's set and call the setter.

If it is just performance optimization, I'd say there should be
  gcc_checking_assert (DECL_RTL_SET_P (dtemp));
before it to verify and make it obvious that you aren't expecting it to be
NULL.

	Jakub
Alexandre Oliva - Sept. 27, 2011, 7:26 p.m.
On Sep 20, 2011, Jakub Jelinek <jakub@redhat.com> wrote:

> For NOTE_INSN_CALL_ARG_LOCATION, the locations aren't location lists, but
> a single location at the point of the call.  They are independent of
> all other locations, so any kind of caching only decreases the chance
> that a suitable location

With the proposed patch, we cache full expressions, and we should have
an expression if there's a location for the expression (save for
expression depth limits).

> is found (and as the numbers show, it decreases it a lot).

It decreased because of a bug: many equivalence expressions that used to
be only in the cselib equivalence lists were no longer used with the
proposed patch.  I have a fix to bring them back in, but see below.

>> > can't it be postponed to start of vt_emit* phase

>> If my hunch is correct, no.  My concern is precisely that the equivalent
>> may cease to hold (say once we cross a val_reset within a loop), but
>> we'll keep on relying on it.

> After vt_initialize, all cselib locations should hold are just some
> expressions containing VALUEs, constants, ENTRY_VALUEs and nothing else,
> all REGs and MEMs are supposed to be flushed.  Those are equivalences that
> are always constant, they don't need any kind of resetting and they never
> cease to hold.  Say VALUE3 is always VALUE1 + VALUE2.

I realize that.  The problem is, I had observed nonsensical equivalences
in dataflow_set loc lists such as (plus (value) (const_int)) in the
location list of the value itself (and a nonzero constant).  After much
pondering, I've concluded that this was a symptom of the first round of
dataflow analysis in the initial implementation of VTA, in which we used
union rather than intersection semantics.  This is no longer the case,
and I've now convinced myself this can't happen any more, so we can rely
on cselib equivalences, not just for expanding location expressions as
we did before my patch (and will do again in a subsequent version of
it), but also for dataflow set merging (the next algorithm I'm going to
try to optimize).

>> > Can't you use ENUM_BITFIELD (onepart_enum) onepart : 8; instead?

>> I don't think it will then be packed in the same word as n_var_parts.

> enum A { B, C, D };
> struct S { char a; enum A b : 8; char c; char d; };
> int i = sizeof (struct S);

> results in i = 4 for all the cc1 and cc1plus cross compilers on my box I've
> tried, with various ABI options.

Yeah, I'm pretty sure GCC will behave like that on nearly all ABIs, but
that's not mandated by standards.  Indeed, IIRC even enum bitfields
aren't mandated by standards.  That's why we have ENUM_BITFIELD, after
all!  Anyway, since ENUM_BITFIELD was supposed to address this very
issue, I guess it just makes sense for me to go with it ;-)

>> if we stop using cselib locs (like this patch does), if we didn't
>> record the equivalent in the dataflow_set table, we'd lose it.

> They weren't used by var-tracking before at all

Actually, they were, but in a somewhat obscure way: when a
cselib_expand_value_rtx_cb callback returns the VALUE it was asked to
expand, cselib proceeds to expand it using its own equivalence list.
vt_expand_loc_callback would often return it, when it couldn't find an
expansion on its own.

I'll probably make it explicit in vt_expand_loc_callback now, so as to
be able to select the best expansion among all.

> If it is just performance optimization, I'd say there should be
>   gcc_checking_assert (DECL_RTL_SET_P (dtemp));
> before it to verify and make it obvious that you aren't expecting it to be
> NULL.

I'm adding DECL_RTL_KNOWN_SET, with a DECL_RTL_SET_P checking_assert,
and using that.  How's that?
Jakub Jelinek - Sept. 30, 2011, 2:35 p.m.
On Tue, Sep 27, 2011 at 04:26:47PM -0300, Alexandre Oliva wrote:
> On Sep 20, 2011, Jakub Jelinek <jakub@redhat.com> wrote:
> 
> > For NOTE_INSN_CALL_ARG_LOCATION, the locations aren't location lists, but
> > a single location at the point of the call.  They are independent of
> > all other locations, so any kind of caching only decreases the chance
> > that a suitable location
> 
> With the proposed patch, we cache full expressions, and we should have
> an expression if there's a location for the expression (save for
> expression depth limits).
> 
> > is found (and as the numbers show, it decreases it a lot).
> 
> It decreased because of a bug: many equivalence expressions that used to
> be only in the cselib equivalence lists were no longer used with the
> proposed patch.  I have a fix to bring them back in, but see below.

Ok.

> >> > can't it be postponed to start of vt_emit* phase
> 
> >> If my hunch is correct, no.  My concern is precisely that the equivalent
> >> may cease to hold (say once we cross a val_reset within a loop), but
> >> we'll keep on relying on it.
> 
> > After vt_initialize, all cselib locations should hold are just some
> > expressions containing VALUEs, constants, ENTRY_VALUEs and nothing else,
> > all REGs and MEMs are supposed to be flushed.  Those are equivalences that
> > are always constant, they don't need any kind of resetting and they never
> > cease to hold.  Say VALUE3 is always VALUE1 + VALUE2.
> 
> I realize that.  The problem is, I had observed nonsensical equivalences
> in dataflow_set loc lists such as (plus (value) (const_int)) in the
> location list of the value itself (and a nonzero constant).  After much
> pondering, I've concluded that this was a symptom of the first round of
> dataflow analysis in the initial implementation of VTA, in which we used
> union rather than intersection semantics.  This is no longer the case,
> and I've now convinced myself this can't happen any more, so we can rely
> on cselib equivalences, not just for expanding location expressions as
> we did before my patch (and will do again in a subsequent version of
> it), but also for dataflow set merging (the next algorithm I'm going to
> try to optimize).

Ok.

> >> > Can't you use ENUM_BITFIELD (onepart_enum) onepart : 8; instead?
> 
> >> I don't think it will then be packed in the same word as n_var_parts.
> 
> > enum A { B, C, D };
> > struct S { char a; enum A b : 8; char c; char d; };
> > int i = sizeof (struct S);
> 
> > results in i = 4 for all the cc1 and cc1plus cross compilers on my box I've
> > tried, with various ABI options.
> 
> Yeah, I'm pretty sure GCC will behave like that on nearly all ABIs, but
> that's not mandated by standards.  Indeed, IIRC even enum bitfields
> aren't mandated by standards.  That's why we have ENUM_BITFIELD, after
> all!  Anyway, since ENUM_BITFIELD was supposed to address this very
> issue, I guess it just makes sense for me to go with it ;-)

While C doesn't have enum bitfields in the standard, C++ does, so it isn't
something very strange.  Furthermore, nothing relies on it being packed up
in the struct, it is just an optimization, so if some compiler doesn't
support it or doesn't pack it well, nothing bad happens.

> > If it is just performance optimization, I'd say there should be
> >   gcc_checking_assert (DECL_RTL_SET_P (dtemp));
> > before it to verify and make it obvious that you aren't expecting it to be
> > NULL.
> 
> I'm adding DECL_RTL_KNOWN_SET, with a DECL_RTL_SET_P checking_assert,
> and using that.  How's that?

Fine with me.

	Jakub

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.
	(onepart_enum, onepart_enum_t): New enum-like type.
	(variable_def): Drop cur_loc_changed, add onepart.
	(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.
	(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.
	(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.
	(val_resolve): Record cselib locs explicitly in set.
	(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.
	(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.
	(EXPR_DEPTH): Unlimit.
	(EXPR_USE_DEPTH): Repurpose PARAM_MAX_VARTRACK_EXPR_DEPTH.
	(add_stores): Use non-argument src exprs in dataflow set.
	(prepare_call_arguments): Use DECL_RTL_IF_SET.
	(dump_var): Adjust access to offset.
	(variable_was_changed): Drop cur_loc_changed.  Use onepart.
	Preserve onepart aux.
	(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, first_child.
	(loc_exp_dep_alloc, loc_exp_dep_clear): New.
	(loc_exp_dep_insert, loc_exp_dep_set): New.
	(notify_dependents_of_resolved_value): New.
	(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.
	(emit_notes_for_differences_2): Drop value chains.
	(emit_notes_in_bb): Adjust.
	(vt_emit_notes): Drop value chains, changed_variables_stack.
	(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.
	* rtl.h: Document various pass-local uses of RTL flags.
	* doc/invoke.texi (param max-vartrack-expr-depth): Update
	description and default.

Index: gcc/var-tracking.c
===================================================================
--- gcc/var-tracking.c.orig	2011-09-18 16:00:54.949739365 -0300
+++ gcc/var-tracking.c	2011-09-18 23:26:07.256863952 -0300
@@ -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;
+  /* One plus the maximum depth among the active dependencies.  */
+  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,38 @@  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.  */
+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,
+  /* Some other kind of tree expression.  */
+  ONEPART_OTHER = 2,
+  /* A DEBUG_EXPR_DECL.  */
+  ONEPART_DEXPR = 3,
+  /* A VALUE.  */
+  ONEPART_VALUE = 4
+};
+
+/* A narrower type used to hold a onepart_enum while saving
+   memory.  */
+typedef char onepart_enum_t;
+
 /* Structure describing where the variable is located.  */
 typedef struct variable_def
 {
@@ -330,10 +397,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.  */
+  onepart_enum_t onepart;
 
   /* True if this variable_def struct is currently in the
      changed_variables hash table.  */
@@ -367,6 +432,42 @@  typedef const struct value_chain_def *co
 /* 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,15 +483,9 @@  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;
 
@@ -420,7 +515,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 *);
@@ -672,15 +766,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 +1223,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_IF_SET (decl);
+}
+
 /* Return the opaque pointer in the decl_or_value.  */
 static inline void *
 dv_as_opaque (decl_or_value dv)
@@ -1140,36 +1245,39 @@  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;
+    return ONEPART_OTHER;
 
   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 +1300,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_IF_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 +1386,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 +1412,14 @@  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)
+    {
+      loc_exp_dep_clear (var);
+      if (VAR_LOC_DEP_LST (var))
+	VAR_LOC_DEP_LST (var)->pprev = NULL;
+      XDELETE (VAR_LOC_1PAUX (var));
+    }
+  pool_free (onepart_pool (var->onepart), var);
 }
 
 /* Initialize the set (array) SET of attrs to empty lists.  */
@@ -1569,13 +1688,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 +1704,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)
 	{
@@ -1904,10 +2033,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;
@@ -2029,6 +2158,20 @@  val_resolve (dataflow_set *set, rtx val,
 
   val_reset (set, dv);
 
+  if (insn)
+    {
+      cselib_val *v = CSELIB_VAL_PTR (val);
+      struct elt_loc_list *l;
+
+      /* Take all equivalent expressions recorded by cselib at this
+	 insn, so that complete information is available in our
+	 dataflow sets.  The exception is the very expressions we were
+	 asked to record, that gets special treatment below.  */
+      for (l = v->locs; l; l = l->next)
+	if (l->setting_insn == insn && l->loc != loc)
+	  val_store (set, val, l->loc, insn, false);
+    }
+
   if (REG_P (loc))
     {
       attrs node, found = NULL;
@@ -2176,10 +2319,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 +2377,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 +2398,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 +2415,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 +2463,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 +2601,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 +2638,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 +2689,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_IF_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 +2752,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);
@@ -2700,11 +2866,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;
@@ -2933,186 +3098,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 +3188,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 +3410,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 +3463,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 +3477,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 +3494,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 +3530,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 +3666,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 +3722,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 +3868,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 +3919,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 +4150,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 +4208,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 +4279,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 +4287,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 +4318,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 +4341,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 +4356,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 +4469,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 +5026,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
@@ -5255,7 +5258,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,8 +5396,6 @@  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)
 	    {
@@ -5893,7 +5905,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_IF_SET (dtemp));
 	      call_arguments = gen_rtx_EXPR_LIST (VOIDmode, item,
 						  call_arguments);
 	    }
@@ -6705,7 +6717,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, "      ");
@@ -6777,7 +6789,6 @@  variable_was_changed (variable var, data
   if (emit_notes)
     {
       void **slot;
-      bool old_cur_loc_changed = false;
 
       /* Remember this decl or VALUE has been added to changed_variables.  */
       set_dv_changed (var->dv, true);
@@ -6791,30 +6802,43 @@  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;
 
-	  empty_var = (variable) pool_alloc (dv_pool (var->dv));
+	  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->cur_loc_changed = true;
+	  empty_var->onepart = onepart;
 	  empty_var->in_changed_variables = true;
 	  *slot = empty_var;
+	  /* ??? It might be wise to retain onepart values that have
+	     active backlinks, lest they get bound to expressions but
+	     fail to notify dependent variables.  */
+	  if (onepart)
+	    {
+	      VAR_LOC_1PAUX (empty_var) = VAR_LOC_1PAUX (var);
+	      VAR_LOC_1PAUX (var) = NULL;
+	    }
 	  goto drop_var;
 	}
       else
 	{
 	  var->refcount++;
 	  var->in_changed_variables = true;
-	  /* If within processing one uop a variable is deleted
-	     and then readded, we need to assume it has changed.  */
-	  if (old_cur_loc_changed)
-	    var->cur_loc_changed = true;
 	  *slot = var;
 	}
     }
@@ -6849,13 +6873,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 +6900,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 +6915,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_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 +7097,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 +7105,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 +7127,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 +7147,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 +7276,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 +7297,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 +7312,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 +7334,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 +7362,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,215 +7375,492 @@  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;
+
+  /* Index of the first child of the uppermost recursed-into value in
+     EXPANDING.  */
+  int first_child;
 };
 
-/* 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.  */
+/* Allocate the one-part auxiliary data structure for VAR, with enough
+   room for COUNT dependencies.  */
 
-static rtx
-vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data)
+static void
+loc_exp_dep_alloc (variable var, int count)
 {
-  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;
-
-  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;
-	}
+  size_t allocsize;
 
-      subreg = cselib_expand_value_rtx_cb (SUBREG_REG (x), regs,
-					   max_depth - 1,
-					   vt_expand_loc_callback, data);
+  gcc_checking_assert (var->onepart);
 
-      if (!subreg)
-	return NULL;
+  /* 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)));
 
-      result = simplify_gen_subreg (GET_MODE (x), subreg,
-				    GET_MODE (SUBREG_REG (x)),
-				    SUBREG_BYTE (x));
+  if (VAR_LOC_1PAUX (var)
+      && VEC_space (loc_exp_dep, VAR_LOC_DEP_VEC (var), count))
+    return;
 
-      /* Invalid SUBREGs are ok in debug info.  ??? We could try
-	 alternate expansions for the VALUE as well.  */
-      if (!result)
-	result = gen_rtx_raw_SUBREG (GET_MODE (x), subreg, SUBREG_BYTE (x));
+  allocsize = offsetof (struct onepart_aux, deps)
+    + VEC_embedded_size (loc_exp_dep, count);
 
-      return result;
+  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);
+}
 
-    case DEBUG_EXPR:
-      dv = dv_from_decl (DEBUG_EXPR_TREE_DECL (x));
-      xret = NULL;
-      break;
+/* 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.  Return the depth of the expression.  */
+
+static int
+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)
+    return 0;
+
+  /* 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 VAR_LOC_DEPTH (xvar);
+
+  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;
+
+  return VAR_LOC_DEPTH (xvar);
+}
+
+/* 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;
+  int depth = result ? 1 : 0;
+
+  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++;
+      int xdepth;
+
+      if (!pending_recursion)
+	pending_recursion = !result && VALUE_RECURSED_INTO (x);
+
+      xdepth = loc_exp_insert_dep (var, x, vars);
+      if (xdepth >= depth && xdepth && result)
+	depth = xdepth + 1;
+    }
+
+  VAR_LOC_DEPTH (var) = depth;
+
+  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)
+	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);
+
+/* 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;
+  rtx result = NULL;
+  int first_child, last_child;
+  bool pending_recursion;
+
+  /* Clear all backlinks pointing at this, so that we're not notified
+     while we're active.  */
+  loc_exp_dep_clear (var);
+
+  first_child = last_child = elcd->first_child
+    = VEC_length (rtx, elcd->expanding);
+
+  /* Attempt to expand each available location in turn.  */
+  for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
+    {
+      result = cselib_expand_value_rtx_cb (loc->loc, regs, EXPR_DEPTH,
+					   vt_expand_loc_callback, data);
+      if (result)
+	break;
+      else if (loc->next)
+	/* Set it for the next iteration.  */
+	last_child = VEC_length (rtx, elcd->expanding);
+      else
+	/* The search is over and we couldn't find anything, prepare
+	   to use the dependencies from all loc nodes.  */
+	last_child = first_child;
+    }
+
+  /* Register all encountered dependencies as active.  */
+  pending_recursion = loc_exp_dep_set
+    (var, result,
+     VEC_address (rtx, elcd->expanding) + last_child,
+     VEC_length (rtx, elcd->expanding) - last_child,
+     elcd->vars);
+  VEC_truncate (rtx, elcd->expanding, first_child);
+
+  /* Record where the expansion came from.  */
+  if (loc)
+    {
+      VAR_LOC_FROM (var) = loc->loc;
+      gcc_checking_assert (!pending_recursion);
+    }
+  else
+    VAR_LOC_FROM (var) = NULL;
+
+  /* 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 ATTRIBUTE_UNUSED,
+			void *data)
+{
+  struct expand_loc_callback_data *elcd
+    = (struct expand_loc_callback_data *) data;
+  decl_or_value dv;
+  variable var;
+  rtx result, subreg;
+  bool pending_recursion = false;
+
+  switch (GET_CODE (x))
+    {
+    case SUBREG:
+      subreg = cselib_expand_value_rtx_cb (SUBREG_REG (x), regs,
+					   EXPR_DEPTH,
+					   vt_expand_loc_callback, data);
+
+      if (!subreg)
+	return NULL;
+
+      result = simplify_gen_subreg (GET_MODE (x), subreg,
+				    GET_MODE (SUBREG_REG (x)),
+				    SUBREG_BYTE (x));
+
+      /* Invalid SUBREGs are ok in debug info.  ??? We could try
+	 alternate expansions for the VALUE as well.  */
+      if (!result)
+	result = gen_rtx_raw_SUBREG (GET_MODE (x), subreg, SUBREG_BYTE (x));
+
+      return result;
+
+    case DEBUG_EXPR:
     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;
+      NO_LOC_P (x) = true;
+      set_dv_changed (dv, false);
+      return NULL;
     }
 
-  if (var->n_var_parts == 0)
+  if (!dv_changed_p (dv))
     {
-      if (dummy)
-	elcd->cur_loc_changed = true;
-      return xret;
+      gcc_checking_assert (NO_LOC_P (x) == !var->var_part[0].cur_loc);
+      return var->var_part[0].cur_loc;
     }
 
-  gcc_assert (var->n_var_parts == 1);
-
   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)
+  if (var)
     {
-      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 (var->n_var_parts == 1);
+
+      result = vt_expand_var_loc_chain (var, regs, data, &pending_recursion);
     }
   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;
+      pending_recursion = false;
+      result = NULL;
+    }
+
+  if (pending_recursion)
+    {
+      gcc_checking_assert (!result);
+      VEC_safe_push (rtx, stack, elcd->pending, x);
+    }
   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).first_child = 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);
+
+  if (VAR_LOC_DEPTH (var) > EXPR_USE_DEPTH)
+    loc = 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 +7885,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 +7943,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 +8071,149 @@  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_IF_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);
+  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 +8226,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;
@@ -7943,76 +8253,34 @@  emit_notes_for_differences_1 (void **slo
       /* Variable has disappeared.  */
       variable empty_var;
 
-      empty_var = (variable) pool_alloc (dv_pool (old_var->dv));
+      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->cur_loc_changed = false;
+      empty_var->onepart = old_var->onepart;
       empty_var->in_changed_variables = false;
-      if (dv_onepart_p (old_var->dv))
+      if (empty_var->onepart)
 	{
-	  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);
+	  /* Propagate the auxiliary data to (ultimately)
+	     changed_variables.  */
+	  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;
     }
-  if (variable_different_p (old_var, new_var))
-    {
-      if (dv_onepart_p (old_var->dv))
-	{
-	  location_chain lc1, lc2;
-
-	  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);
-	}
-      variable_was_changed (new_var, NULL);
-    }
-  /* Update cur_loc.  */
-  if (old_var != new_var)
-    {
-      int i;
-      for (i = 0; i < new_var->n_var_parts; i++)
-	{
-	  new_var->var_part[i].cur_loc = NULL;
-	  if (old_var->n_var_parts != new_var->n_var_parts
-	      || old_var->var_part[i].offset != new_var->var_part[i].offset)
-	    new_var->cur_loc_changed = true;
-	  else if (old_var->var_part[i].cur_loc != NULL)
-	    {
-	      location_chain lc;
-	      rtx cur_loc = old_var->var_part[i].cur_loc;
-
-	      for (lc = new_var->var_part[i].loc_chain; lc; lc = lc->next)
-		if (lc->loc == cur_loc
-		    || rtx_equal_p (cur_loc, lc->loc))
-		  {
-		    new_var->var_part[i].cur_loc = lc->loc;
-		    break;
-		  }
-	      if (lc == NULL)
-		new_var->cur_loc_changed = true;
-	    }
-	}
+  /* 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 +8301,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 +8370,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);
@@ -8411,17 +8670,6 @@  vt_emit_notes (void)
      delete_variable_part).  */
   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);
-    }
-
   dataflow_set_init (&cur);
 
   FOR_EACH_BB (bb)
@@ -8441,24 +8689,9 @@  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);
-    }
-
   emit_notes = false;
 }
 
@@ -8489,37 +8722,28 @@  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);
+      val_store (out, val->val_rtx, eqval, NULL, false);
+      val_store (out, eqval, val->val_rtx, NULL, false);
     }
+
+  val_store (out, eqval, ev, NULL, false);
 }
 
 /* Insert function parameter PARM in IN and OUT sets of ENTRY_BLOCK.  */
@@ -8678,20 +8902,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 +8981,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 +9052,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 +9077,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 +9161,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 +9346,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 ();
Index: gcc/rtl.h
===================================================================
--- gcc/rtl.h.orig	2011-09-18 16:00:55.068736987 -0300
+++ gcc/rtl.h	2011-09-18 16:38:12.846550000 -0300
@@ -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-09-18 16:00:55.239733569 -0300
+++ gcc/doc/invoke.texi	2011-09-18 16:38:12.978547072 -0300
@@ -9013,8 +9013,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