diff mbox

[RFC] Typed DWARF stack

Message ID 20110325113237.GY18914@tyan-ft48-01.lab.bos.redhat.com
State New
Headers show

Commit Message

Jakub Jelinek March 25, 2011, 11:32 a.m. UTC
Hi!

This patch on top of
http://gcc.gnu.org/ml/gcc-patches/2011-03/msg01224.html
and
http://gcc.gnu.org/ml/gcc-patches/2011-03/msg01723.html
implements parts of Cary's typed DWARF stack proposal:
http://www.dwarfstd.org/doc/040408.1.html

As I said in my GCC Summit talk, currently we just give up on
any floating point/_Decimal*/__int128 and for 32-bit targets even
long long expressions, as those can't be represented in DWARF4,
while var-tracking has all that often available.

As we are quickly running out of available extension opcodes
in the GNU namespace (unless we go back to 0xeX range, with
this proposal we have just 7 left and it would be good
to reserve at least one such opcode as multiplexor for new
opcodes (DW_OP_GNU_multiplex whose first operand would be uleb128
extended opcode number followed by its arguments) and perhaps another
one to signal some location expression compression technique using
abbreviation table), I've chosen to add just those that are useful
for GCC and consumers of its debug info right now.
DW_OP_GNU_const{1,2,4,8}_type would allow us to save one byte, but that
doesn't seem worth wasting 4 opcodes in GNU namespace (the standard
has more, so it could add them).  Similarly, as GCC currently
never emits DW_OP_xderef*, I've not added DW_OP_GNU_xderef_type.
And DW_OP_GNU_readonly is something I have no idea how to get out
of var-tracking.  DW_OP_stack_value or DW_OP_implicit_value is
implicitly readonly, and for REGs/MEMs I don't think var-tracking
allows us to find out if changing this REG or MEM is the right way
to change the value of the variable, it is at each spot just
one of possibly more locations holding the value.

The added
DW_OP_GNU_const_type
DW_OP_GNU_regval_type
DW_OP_GNU_deref_type
DW_OP_GNU_convert
match (sans _GNU) what is written in the proposal and one extra
DW_OP_GNU_reinterpret
opcode has been added, which is like DW_OP_GNU_convert (so just one uleb128
argument with base type offset), but it is not a conversion (say
convert a floating point value to integer), but reinterpretation of
the bits (VIEW_CONVERT_EXPR-like, or store one value into a union, read
another value from another union field).

Some small problems on the producer side were caused by the base
type references being uleb128 instead of 4 byte number (in particular,
size of say DW_AT_location block depends on the offset number being
already known, but to compute offset numbers one has to know the sizes;
solved both for these and debug info size reasons by making sure
base types referenced from such ops are present very early in the CU
and can be sized and layed out early - base types shouldn't have
attributes with location expressions anyway - and the earlier they
are in the CU, the smaller the uleb128s are), but having them all
4 byte would be too large, so I actually like them being uleb128s.

On the consumer side I expect if a consumer decides to support these,
it implements the DWARF stack as pairs of some type id (or
encoding/byte size) and union of the supported typed fields.

Attached is also a guality testcase that shows some of what it can
e.g. represent, with the exception of two vars for -m32 in second routine
it actually has a location expression for all vars on both x86_64
and i?86.  If anyone from the debug info consumer crowd is interested,
I could post the assembly files for -m{32,64} -g -dA -O2.

	Jakub
2011-03-25  Jakub Jelinek  <jakub@redhat.com>

	* dwarf2.h (DW_OP_GNU_const_type, DW_OP_GNU_regval_type,
	DW_OP_GNU_deref_type, DW_OP_GNU_convert, DW_OP_GNU_reinterpret): New.

	* dwarf2out.c (get_address_mode): New inline.
	(mem_loc_descriptor): Ad MEM_MODE parameter, adjust recursive calls,
	if not dwarf_strict emit
	DW_OP_GNU_{{const,regval,deref}_type,convert,reinterpret} when
	desirable.  Handle FLOAT_EXTEND, FLOAT_TRUNCATE, FLOAT,
	UNSIGNED_FLOAT, FIX and UNSIGNED_FIX.  Just return NULL for
	FMA, STRICT_LOW_PART, CONST_VECTOR and CONST_FIXED.
	(dwarf2out_frame_debug_cfa_expression, reg_loc_descriptor,
	dw_loc_list_1, cst_pool_loc_descr, loc_list_from_tree): Adjust
	mem_loc_descriptor callers.
	(dwarf_stack_op_name, size_of_loc_descr, output_loc_operands,
	output_loc_operands_raw, hash_loc_operands, compare_loc_operands):
	Handle DW_OP_GNU_const_type, DW_OP_GNU_regval_type,
	DW_OP_GNU_deref_type, DW_OP_GNU_convert and DW_OP_GNU_reinterpret.
	(base_types): New variable.
	(get_base_type_offset, calc_base_type_die_sizes,
	base_type_for_mode, mark_base_types, base_type_cmp,
	move_marked_base_types): New functions.
	(calc_die_sizes): Assert that die_offset is 0 or equal to
	next_die_offset.
	(loc_descriptor): Only handle here lowpart SUBREGs of REG, for
	others defer to mem_loc_descriptor.  Adjust mem_loc_descriptor
	callers.  If not dwarf_strict, call mem_loc_descriptor even for
	non-MODE_INT modes or MODE_INT modes larger than DWARF2_ADDR_SIZE.
	(gen_subprogram_die): Don't give up on call site parameters
	with non-integral or large integral modes.  Adjust
	mem_loc_descriptor callers.
	(prune_unused_types): Call prune_unused_types_mark on base_types
	vector entries.
	(resolve_addr): Call mark_base_types.
	(dwarf2out_finish): Call move_marked_base_types.
	* cfgexpand.c (expand_debug_expr) <case FLOAT_EXPR> Don't use
	unsignedp but TYPE_UNSIGNED on argument to decide when to emit
	UNSIGNED_FLOAT.
/* { dg-do run { target { i?86-*-* x86_64-*-* } } } */
/* { dg-options "-g" } */

typedef __SIZE_TYPE__ size_t;
volatile int vv;
extern void *memcpy (void *, const void *, size_t);

__attribute__((noinline, noclone)) void
f1 (double a, double b, double c, float d, float e, int f, unsigned int g, long long h, unsigned long long i)
{
  double j = d;			/* { dg-final { gdb-test 29 "j" "4" } } */
  long long l;			/* { dg-final { gdb-test 29 "l" "4616189618054758400" } } */
  memcpy (&l, &j, sizeof (l));
  long long m;			/* { dg-final { gdb-test 29 "m" "4613937818241073152" } } */
  memcpy (&m, &c, sizeof (l));
  float n = i;			/* { dg-final { gdb-test 29 "n" "9" } } */
  double o = h;			/* { dg-final { gdb-test 29 "o" "8" } } */
  float p = g;			/* { dg-final { gdb-test 29 "p" "7" } } */
  double q = f;			/* { dg-final { gdb-test 29 "q" "6" } } */
  unsigned long long r = a;	/* { dg-final { gdb-test 29 "r" "1" } } */
  long long s = c;		/* { dg-final { gdb-test 29 "s" "3" } } */
  unsigned t = d;		/* { dg-final { gdb-test 29 "t" "4" } } */
  int u = b;			/* { dg-final { gdb-test 29 "u" "2" } } */
  float v = a;			/* { dg-final { gdb-test 29 "v" "1" } } */
  double w = d / 4.0;		/* { dg-final { gdb-test 29 "w" "1" } } */
  double x = a + b + 1.0;	/* { dg-final { gdb-test 29 "x" "4" } } */
  double y = b + c + 2.0;	/* { dg-final { gdb-test 29 "y" "7" } } */
  float z = d + e + 3.0f;	/* { dg-final { gdb-test 29 "z" "12" } } */
  vv++;
}

__attribute__((noinline, noclone)) void
f2 (double a, double b, double c, float d, float e, int f, unsigned int g, long long h, unsigned long long i)
{
  double j = d;			/* { dg-final { gdb-test 53 "j" "4" } } */
  long long l;			/* { dg-final { gdb-test 53 "l" "4616189618054758400" } } */
  memcpy (&l, &j, sizeof (l));
  long long m;			/* { dg-final { gdb-test 53 "m" "4613937818241073152" } } */
  memcpy (&m, &c, sizeof (l));
  float n = i;			/* { dg-final { gdb-test 53 "n" "9" } } */
  double o = h;			/* { dg-final { gdb-test 53 "o" "8" } } */
  float p = g;			/* { dg-final { gdb-test 53 "p" "7" } } */
  double q = f;			/* { dg-final { gdb-test 53 "q" "6" } } */
  unsigned long long r = a;	/* { dg-final { gdb-test 53 "r" "1" } } */
  long long s = c;		/* { dg-final { gdb-test 53 "s" "3" } } */
  unsigned t = d;		/* { dg-final { gdb-test 53 "t" "4" } } */
  int u = b;			/* { dg-final { gdb-test 53 "u" "2" } } */
  float v = a;			/* { dg-final { gdb-test 53 "v" "1" } } */
  double w = d / 4.0;		/* { dg-final { gdb-test 53 "w" "1" } } */
  double x = a + b - 3 + 1.0e20;/* { dg-final { gdb-test 53 "x" "1e+20" } } */
  double y = b + c * 7.0;	/* { dg-final { gdb-test 53 "y" "23" } } */
  float z = d + e + 3.0f;	/* { dg-final { gdb-test 53 "z" "12" } } */
  vv++;
  vv = a;
  vv = b;
  vv = c;
  vv = d;
  vv = e;
  vv = f;
  vv = g;
  vv = h;
  vv = i;
  vv = j;
}

__attribute__((noinline, noclone)) void
f3 (long long a, int b, long long c, unsigned d)
{
  long long w = (a > d) ? a : d;/* { dg-final { gdb-test 73 "w" "4" } } */
  long long x = a + b + 7;	/* { dg-final { gdb-test 73 "x" "10" } } */
  long long y = c + d + 0x912345678LL;/* { dg-final { gdb-test 73 "y" "38960125567" } } */
  int z = (x + y);		/* { dg-final { gdb-test 73 "z" "305419913" } } */
  vv++;
}

int
main ()
{
  f1 (1.0, 2.0, 3.0, 4.0f, 5.0f, 6, 7, 8, 9);
  f2 (1.0, 2.0, 3.0, 4.0f, 5.0f, 6, 7, 8, 9);
  f3 (1, 2, 3, 4);
  return 0;
}

Comments

Roland McGrath March 25, 2011, 4:44 p.m. UTC | #1
It's been a while since I read Cary's proposal, and I am no longer
likely to do much work of my own in this area.  So I'll just respond at
the high level.

I like very much the essential notion of the stack being of typed
entities with no specification of how the consumer actually implements
it.  In particular, this also brings implicit-pointer into the fold as a
new type of a stack element, i.e. "imaginary pointer".  Obviously only
the operations that amount to addition/subtraction with an integer
actually make sense on such a stack element.  This makes the second
operand of DW_OP(GNU_)implicit_pointer superfluous (and hence the common
case of zero a byte smaller), since nonzero cases can just be followed
by an appropriate DW_OP_plus_uconst or suchlike.

To make clear that this should be supported, the specification wording
would have to be somewhat more abstract than suggesting that a union of
target bit-pattern types of various sizes would suffice.  But it does
not seem much of a stretch to me, and IMHO it's most appropriate that
DWARF not say very much about consumer implementation.

This is more of a stretch, but IMHO it would also make sense to exploit
this "typed" concept to roll in the distinction between values and
locations.  This notion is not very well-formed, but perhaps worth
investigating.  That is, one could consider DW_OP_reg* a stack operation
that pushes the location in a register.  Implicitly, traditional
operations push the location in an address.  But DW_OP_stack_value
"pops" the address location and "pushes" the address literal as a value.
If the TOS at the end of an expression is a location, then it's a
mutable location, otherwise it's a value.  When an expression is used in
a value-only context (DW_AT_frame_base, etc.) then TOS addresses are
always (identity-)converted integer values and TOS registers are always
fetched values.  This way of thinking is natural to me, and it makes the
DW_AT_frame_base specification a natural and straightforward instance of
a general thing rather than being a one-off special case.  But perhaps
it is too convoluted for other people.


As to encoding, I have a fancy idea that I discussed off the cuff at the
Summit with Jakub and Richard, and still quite like, though I haven't
fleshed it out any more than I did then.  I hope to inspire someone else
to actually want to implement it.  It's rather more ambitious than the
things that Jakub will just add while the rest of us sleep, so I
wouldn't suggest that such DW_OP_GNU_* extensions be delayed for this
plan.  But perhaps it can become coherent enough and get done enough to
seriously propose it for DWARF 5.

The basic notion is extreme extensibility for DWARF operations, done in
a way that could yield in practice even smaller encodings that we get
today, while supporting an arbitrarily larger vocabulary of operations.
It's modelled on the DIE encoding, using abbrevs.

Add a section ".debug_opabbrev".  A chunk of this would be pointed to by
a CU's DW_AT_op_list attribute--or it could be a CU header field, but
there's not much reason to go that route, except perhaps if such a CU
would always be version=5 anyway so as to make old consumers know that
they don't know how to inerpret expressions therein.  (It seems very
unlikely that any .debug_frame/.eh_frame DW_CFA_*expression would ever
need to go beyond the DWARF 4 operation vocabuluary.  But if they do,
that header could be likewise extended with a .{debug,eh}_opabbrev
pointer.  Though that's pretty clearly beyond the pale for introspective
in-process .eh_frame decoding.)  The presence of this attribute signals
that this opabbrev table controls the interpretation of all expressions
found in that CU (i.e. in its DW_FORM_exprloc attributes and in any
.debug_loc lists it points to).

The opabbrev table is much like the DIE abbrev table.  It maps an opcode
to an operation and a list of operand encodings.  Each operand is
indicated by a DW_FORM_* encoding and the list terminated with zero, as
with DIE abbrevs.  For generality, opcodes would be ULEB128 and that
seems fine since any one CU using more than 127 seems like an outlier.
But if real-world CU's might not too infrequently use 128-255 different
opcodes, then a table header byte could give the encoding of opcodes
too, so it might be DW_FORM_data1 instead to optimize the packing of
such a case.

I'm somewhat undecided on how best to encode operations in an opabbrev
table.  My first instinct is towards easy extensibility, by encoding
each as a pair of DW_FORM_strp, being "family" and "operation".
(Perhaps for compactness a table would contain several runs of
subtables, each subtable using a single family string with only the
operation string in each subtable element that defines a particular
opcode.)  Then family is either unspecified and conventional (perhaps
instead called "vendor", but I prefer "family" so as to indicate side
standards on common families), or perhaps a domain name, reverse domain
name, or URL (where might live a pointer to the full specification of
the family's operation names).  The owner of a given family name defines
the set of operation names valid therein, their permissible operand list
lengths and encodings, and their meanings.

As with DIE abbrevs, the two key features of this scheme are compactness
of exprloc blocks given wise encoding choices, and foreign extensions
that are always structurally comprehensible to all consumers even when
they don't know how to interpret them semantically.  There are some
other things I like about it too.

There's no longer any need for separate operations distinguished only by
their operand encodings.  e.g., you need just one operation "constant"
and that can be used in opabbrev's each with a DW_FORM_data* operand, in
others with a DW_FORM_block* operand, etc.  This makes the family
specification of operations IMHO more natural and focussed on semantics,
though it has to be very clear about the semantics of extension of
operands to word size and so forth.  Similarly, "breg" can be one
operation that sometimes has one operand (register number) and sometimes
has two (register number and offset), to use the optimal encoding
without the extra zero byte now common on a DW_OP_breg*.  I haven't
considered a special encoding for regN/bregN style ranges of opcodes;
something like that could make sense to keep the opabbrev table size
down, but this probably doesn't matter in comparison to keeping the
actual exprloc blocks small--it could just be a normal opabbrev making
one opcode be operation "reg2" and another "reg17", or an extension like
DW_FORM_direct that says an operand value appears directly in the abbrev
and has no encoding in the op itself (which I've been considering for
attribute values in DIE abbrevs too).

This also opens the possibility of defining a different operation family
like "ieee754" where its "add" and "mul" operations et al are explicitly
defined with unambiguous semantics.  IMHO this is far better than the
current plan of overloading the single DW_OP_add et al to have implicit
semantics based on the types of the top two stack elements.

In a first implementation, a compiler could just use a single canned
opabbrev table.  (It could emit that in COMDAT, or even just as an
undefined symbol provided inside a .debug_opabbrev section in some
libgcc.a object.)  In fact, such a table could be trivially written out
retroactively to describe all the DWARF 4 opcodes and extant GNU
extensions with DW_FORM_data1 encoding for opcodes and appropriate
operand lists.  Post-compile DWARF processors (linkers, compressors,
rewriters) could tack that onto today's object files to make them fully
navigable by non-GNU consumers without breaking compatibility with
existing tools that grok the GNU extension formats today.

A fancy implementation would choose opabbrev's carefully based on what
its CU's really use, so as to pack the smallest possible actual exprloc
blocks.


As I say, I'm not really working on this stuff any more except maybe for
(as yet wholly absent) spare time.  But, food for thought.


Thanks,
Roland
Cary Coutant March 25, 2011, 5:51 p.m. UTC | #2
> This patch on top of
> http://gcc.gnu.org/ml/gcc-patches/2011-03/msg01224.html
> and
> http://gcc.gnu.org/ml/gcc-patches/2011-03/msg01723.html
> implements parts of Cary's typed DWARF stack proposal:
> http://www.dwarfstd.org/doc/040408.1.html

I haven't looked at the patch yet, but this sounds great; thanks for
doing this! I think that with an actual implementation in place and
some experience with it, there's a better chance at getting this
adopted into DWARF 5, even though I was unsuccessful at getting it
accepted for DWARF 4.

-cary
Jakub Jelinek March 28, 2011, 9:20 a.m. UTC | #3
On Fri, Mar 25, 2011 at 09:44:40AM -0700, Roland McGrath wrote:
> It's been a while since I read Cary's proposal, and I am no longer
> likely to do much work of my own in this area.  So I'll just respond at
> the high level.
> 
> I like very much the essential notion of the stack being of typed
> entities with no specification of how the consumer actually implements
> it.  In particular, this also brings implicit-pointer into the fold as a
> new type of a stack element, i.e. "imaginary pointer".  Obviously only
> the operations that amount to addition/subtraction with an integer
> actually make sense on such a stack element.  This makes the second
> operand of DW_OP(GNU_)implicit_pointer superfluous (and hence the common
> case of zero a byte smaller), since nonzero cases can just be followed
> by an appropriate DW_OP_plus_uconst or suchlike.

For DW_OP_GNU_implicit_pointer it is too late to remove the parameter now,
because e.g. GCC 4.6 has been shipped with it and several tools have support
for it already.
What we still could do is reword it based on the typed stack, say that
DW_OP_*implicit_pointer pushes an entry of a special type on the stack
and what operations are allowed with that type (from arithmetics
I think only addition of integral stack value to it (leading again to
implicit pointer type) or subtraction of two implicit pointers for the same
DIE, leading to an integral difference.  With that rewording,
DW_OP_GNU_implicit_pointer could be no longer a terminal, but could appear
within the DWARF expressions.
DW_OP_implicit_pointer can of course be designed without the operand.

> As to encoding, I have a fancy idea that I discussed off the cuff at the
> Summit with Jakub and Richard, and still quite like, though I haven't
> fleshed it out any more than I did then.  I hope to inspire someone else
> to actually want to implement it.  It's rather more ambitious than the
> things that Jakub will just add while the rest of us sleep, so I
> wouldn't suggest that such DW_OP_GNU_* extensions be delayed for this
> plan.  But perhaps it can become coherent enough and get done enough to
> seriously propose it for DWARF 5.

I view such encoding changes primarily as compression and thus IMHO
we don't need to delay any changes waiting for the compression technique
specification.  We have DW_OP_call{2,4} right now as a simple compression
technique and can (and should) improve that, but in the mean time we can
just add new ops as we'd normally do.

> As I say, I'm not really working on this stuff any more except maybe for
> (as yet wholly absent) spare time.  But, food for thought.

IMHO the typed stack as Cary proposed is usable as is, and as additional
bonus it is extensible, e.g. by saying that the referenced DIE doesn't
have to be just DW_TAG_base_type, but also this and that (where the
referenced DIE tags could newly be either current tags where we would give
it a particular meaning, or it could be some newly added tag, perhaps just
for that purpose) we could extend it easily.

	Jakub
Tom Tromey May 4, 2011, 6:04 p.m. UTC | #4
>>>>> "Jakub" == Jakub Jelinek <jakub@redhat.com> writes:

Jakub> This patch on top of
Jakub> http://gcc.gnu.org/ml/gcc-patches/2011-03/msg01224.html
Jakub> and
Jakub> http://gcc.gnu.org/ml/gcc-patches/2011-03/msg01723.html
Jakub> implements parts of Cary's typed DWARF stack proposal:
Jakub> http://www.dwarfstd.org/doc/040408.1.html

I've been implementing this in GDB and I have a couple of questions.

Should DW_OP_bra be restricted to integral types?  Several other opcodes
are restricted in this way, and it seems like an oversight to me that
DW_OP_bra is lacking this restriction.  (I've added this restriction in
GDB.)

Currently, the comparison operators are all defined as performing signed
operations.  So, what should happen in this case:

   DW_OP_lit0
   DW_OP_GNU_convert <unsigned int>
   DW_OP_GNU_const_type <unsigned int> -1
   DW_OP_gt

That is, should this ignore the type (perhaps just using the type
width), or is this a bug in the spec?

Either answer here has problems.

You can't really ignore the type, because that rules out floating point
comparisons.  I suppose you could special-case integral types.

However, since "ordinary" (that is, pre-typed-DWARF) DWARF values do not
have a consistent type, I think answering "bug" means having a special
case for such values -- because they are treated as unsigned in most
places, but signed in a few, and signed/unsigned type conversion should
presumably only be done for such "typeless" values, not all values.

I think I will implement the latter ("bug") approach.

Tom
Cary Coutant May 4, 2011, 6:22 p.m. UTC | #5
> Should DW_OP_bra be restricted to integral types?  Several other opcodes
> are restricted in this way, and it seems like an oversight to me that
> DW_OP_bra is lacking this restriction.  (I've added this restriction in
> GDB.)

Yes, that was an oversight.

> Currently, the comparison operators are all defined as performing signed
> operations.  So, what should happen in this case:
>
>   DW_OP_lit0
>   DW_OP_GNU_convert <unsigned int>
>   DW_OP_GNU_const_type <unsigned int> -1
>   DW_OP_gt
>
> That is, should this ignore the type (perhaps just using the type
> width), or is this a bug in the spec?
>
> Either answer here has problems.
>
> You can't really ignore the type, because that rules out floating point
> comparisons.  I suppose you could special-case integral types.
>
> However, since "ordinary" (that is, pre-typed-DWARF) DWARF values do not
> have a consistent type, I think answering "bug" means having a special
> case for such values -- because they are treated as unsigned in most
> places, but signed in a few, and signed/unsigned type conversion should
> presumably only be done for such "typeless" values, not all values.
>
> I think I will implement the latter ("bug") approach.

Yes, another oversight; I should have removed the sentence "The
comparisons are done as signed operations." For untyped values, it
should be reasonable to treat them as signed integers here as you
propose.

Technically, there weren't supposed to be untyped values at all in
this proposal; I should have also gone through and specified an
explicit type for each of the legacy operators that push a value onto
the stack, but signed-vs-unsigned is such a mess that I didn't bother
to go that far for a draft proposal that didn't get very far in
committee. I think a signed/unsigned cleanup in DWARF would have to go
far beyond the expression evaluation mechanism.

-cary
Jakub Jelinek May 4, 2011, 6:33 p.m. UTC | #6
On Wed, May 04, 2011 at 12:04:40PM -0600, Tom Tromey wrote:
> Should DW_OP_bra be restricted to integral types?  Several other opcodes

Yes.  If you want to test other types, just compare them against 0
using DW_OP_ne etc.

> Currently, the comparison operators are all defined as performing signed
> operations.  So, what should happen in this case:
> 
>    DW_OP_lit0
>    DW_OP_GNU_convert <unsigned int>
>    DW_OP_GNU_const_type <unsigned int> -1
>    DW_OP_gt
> 
> That is, should this ignore the type (perhaps just using the type
> width), or is this a bug in the spec?

Whenever you have explicit type, use that type for comparisons, including
its signedness.

	Jakub
Tom Tromey May 4, 2011, 7:53 p.m. UTC | #7
>>>>> "Jakub" == Jakub Jelinek <jakub@redhat.com> writes:

Tom> Should DW_OP_bra be restricted to integral types?  Several other opcodes

Jakub> Yes.  If you want to test other types, just compare them against 0
Jakub> using DW_OP_ne etc.

Great, thanks.

Tom> Currently, the comparison operators are all defined as performing signed
Tom> operations.  So, what should happen in this case:
Tom> 
Tom> DW_OP_lit0
Tom> DW_OP_GNU_convert <unsigned int>
Tom> DW_OP_GNU_const_type <unsigned int> -1
Tom> DW_OP_gt
Tom> 
Tom> That is, should this ignore the type (perhaps just using the type
Tom> width), or is this a bug in the spec?

Jakub> Whenever you have explicit type, use that type for comparisons,
Jakub> including its signedness.

DW_OP_mod also need special treatment.

What my patch does now is that it generally uses a signed integer type
of the appropriate width for "legacy" DWARF values.  Then, for mod, if
the value's type is this special type, it converts it to an
identically-sized unsigned type, and converts back after the operation.

For DW_OP_shr, I always convert the LHS to an appropriately-sized
unsigned type first.

Tom
Tom Tromey May 12, 2011, 5:51 p.m. UTC | #8
Just for the record...

Tom> What my patch does now is that it generally uses a signed integer type
Tom> of the appropriate width for "legacy" DWARF values.  Then, for mod, if
Tom> the value's type is this special type, it converts it to an
Tom> identically-sized unsigned type, and converts back after the operation.

Tom> For DW_OP_shr, I always convert the LHS to an appropriately-sized
Tom> unsigned type first.

I have since changed this.  For explicitly-typed values, I tried
DW_OP_shr and DW_OP_shra identically.  The type encodes the desired
operation.

For "old-style" untyped values, DW_OP_shr converts to unsigned.

I thought this approach was most consistent with the rest of the
specification.

Tom
Tom Tromey May 12, 2011, 6:31 p.m. UTC | #9
Tom> I have since changed this.  For explicitly-typed values, I tried
Tom> DW_OP_shr and DW_OP_shra identically.  The type encodes the desired
Tom> operation.

Jakub pinged me on irc to say that this is not what GCC actually emits.
So, I am going to change GDB to follow.  That is, GDB will pick a
suitably-sized unsigned type to use for the LHS of DW_OP_shr.

Tom
Jakub Jelinek June 9, 2011, 2:53 p.m. UTC | #10
Hi!

On Fri, Mar 25, 2011 at 12:32:37PM +0100, Jakub Jelinek wrote:
> This patch on top of
> http://gcc.gnu.org/ml/gcc-patches/2011-03/msg01224.html
> and
> http://gcc.gnu.org/ml/gcc-patches/2011-03/msg01723.html
> implements parts of Cary's typed DWARF stack proposal:
> http://www.dwarfstd.org/doc/040408.1.html

GCC currently doesn't fully adhere to the above SPEC (even with the
agreed on slight changes for some binary ops), in particular to the
binary ops operand need to have the same type.

Currently once a value on the stack is typed, the typedness
propagates forever through unary/binary ops (the only exception
are comparisons that push untyped result even for typed operands).

I'd like to propose convert to untyped operation, e.g.
DW_OP_GNU_convert <0> could do it (and maybe DW_OP_GNU_reinterpret <0>),
these would convert to an integral value of the same size as DWARF
address and make it untyped.  As DW_OP_GNU_convert operand is
uleb128 DIE offset within current CU, offset 0 certainly won't
contain any DIEs, because it is the first byte of the CU header.

Without this, currently GCC will sometimes end up with one
operand typed, another untyped.  While it is solvable even just on
the GCC side (mem_loc_descriptor would need to track whether the
TOS of the expression it returned is typed, untyped (or uncertain,
e.g. for DW_OP_GNU_parameter_ref), as that would be determined
by the matching DW_AT_GNU_call_site_value, and when e.g. for binary
operation one argument is determined as typed, the other argument
would need to be converted to typed as well if untyped, and any
uncertain operand of a binary op or unary that cares about the
sign would need to be converted to typed), I think it would
generate unnecessarily bloated code, as e.g. after a float is
converted to int or other small integer suddenly all further
additions etc. would need to convert constants etc. to typed values
(or use DW_OP_const_type).  With an convert to untyped operation,
mem_loc_descriptor could just ensure that for integral modes <=
DWARF_ADDR_SIZE the result is untyped (current gcc code sort of
assumes that) and only when a larger mode is needed or non-integral,
it would be typed, and when sign matters for operation of integral small
modes, it could just temporarily convert to typed and back.

Does this sound reasonable, or is it too ugly?

	Jakub
Tom Tromey June 13, 2011, 3:30 p.m. UTC | #11
>>>>> "Jakub" == Jakub Jelinek <jakub@redhat.com> writes:

Jakub> I'd like to propose convert to untyped operation, e.g.
Jakub> DW_OP_GNU_convert <0> could do it (and maybe DW_OP_GNU_reinterpret <0>),
Jakub> these would convert to an integral value of the same size as DWARF
Jakub> address and make it untyped.  As DW_OP_GNU_convert operand is
Jakub> uleb128 DIE offset within current CU, offset 0 certainly won't
Jakub> contain any DIEs, because it is the first byte of the CU header.

FWIW, I wrote the GDB change for this.  It is ready to go in whenever
this patch is accepted for GCC.

Tom
diff mbox

Patch

--- include/dwarf2.h.jj	2011-03-16 18:30:13.000000000 +0100
+++ include/dwarf2.h	2011-03-23 17:20:09.000000000 +0100
@@ -557,6 +557,13 @@  enum dwarf_location_atom
     /* The GNU entry value extension.
        See http://www.dwarfstd.org/ShowIssue.php?issue=100909.1&type=open .  */
     DW_OP_GNU_entry_value = 0xf3,
+    /* The GNU typed stack extension.
+       See http://www.bayarea.net/~cary/dwarf/dwarf-expressions.html .  */
+    DW_OP_GNU_const_type = 0xf4,
+    DW_OP_GNU_regval_type = 0xf5,
+    DW_OP_GNU_deref_type = 0xf6,
+    DW_OP_GNU_convert = 0xf7,
+    DW_OP_GNU_reinterpret = 0xf8,
     /* HP extensions.  */
     DW_OP_HP_unknown     = 0xe0, /* Ouch, the same as GNU_push_tls_address.  */
     DW_OP_HP_is_value    = 0xe1,
--- gcc/dwarf2out.c.jj	2011-03-24 11:16:07.000000000 +0100
+++ gcc/dwarf2out.c	2011-03-25 08:25:58.000000000 +0100
@@ -483,7 +483,8 @@  static struct dw_loc_descr_struct *build
   (HOST_WIDE_INT, HOST_WIDE_INT);
 static void def_cfa_1 (const char *, dw_cfa_location *);
 static struct dw_loc_descr_struct *mem_loc_descriptor
-  (rtx, enum machine_mode mode, enum var_init_status);
+  (rtx, enum machine_mode mode, enum machine_mode mem_mode,
+   enum var_init_status);
 
 /* How to start an assembler comment.  */
 #ifndef ASM_COMMENT_START
@@ -2056,6 +2057,17 @@  dwarf2out_frame_debug_cfa_register (rtx 
   reg_save (label, sregno, dregno, 0);
 }
 
+/* Helper function to get mode of MEM's address.  */
+
+static inline enum machine_mode
+get_address_mode (rtx mem)
+{
+  enum machine_mode mode = GET_MODE (XEXP (mem, 0));
+  if (mode != VOIDmode)
+    return mode;
+  return targetm.addr_space.address_mode (MEM_ADDR_SPACE (mem));
+}
+
 /* A subroutine of dwarf2out_frame_debug, process a REG_CFA_EXPRESSION note. */
 
 static void
@@ -2076,8 +2088,8 @@  dwarf2out_frame_debug_cfa_expression (rt
   cfi->dw_cfi_opc = DW_CFA_expression;
   cfi->dw_cfi_oprnd1.dw_cfi_reg_num = DWARF_FRAME_REGNUM (REGNO (src));
   cfi->dw_cfi_oprnd2.dw_cfi_loc
-    = mem_loc_descriptor (XEXP (dest, 0), GET_MODE (dest),
-			  VAR_INIT_STATUS_INITIALIZED);
+    = mem_loc_descriptor (XEXP (dest, 0), get_address_mode (dest),
+			  GET_MODE (dest), VAR_INIT_STATUS_INITIALIZED);
 
   /* ??? We'd like to use queue_reg_save, were the interface different,
      and, as above, we could manage flushing for epilogues.  */
@@ -4799,6 +4811,16 @@  dwarf_stack_op_name (unsigned int op)
       return "DW_OP_GNU_implicit_pointer";
     case DW_OP_GNU_entry_value:
       return "DW_OP_GNU_entry_value";
+    case DW_OP_GNU_const_type:
+      return "DW_OP_GNU_const_type";
+    case DW_OP_GNU_regval_type:
+      return "DW_OP_GNU_regval_type";
+    case DW_OP_GNU_deref_type:
+      return "DW_OP_GNU_deref_type";
+    case DW_OP_GNU_convert:
+      return "DW_OP_GNU_convert";
+    case DW_OP_GNU_reinterpret:
+      return "DW_OP_GNU_reinterpret";
 
     default:
       return "OP_<unknown>";
@@ -4906,6 +4928,7 @@  loc_list_plus_const (dw_loc_list_ref lis
   (dwarf_version == 2 ? DWARF2_ADDR_SIZE : DWARF_OFFSET_SIZE)
 
 static unsigned long size_of_locs (dw_loc_descr_ref);
+static unsigned long int get_base_type_offset (dw_die_ref);
 
 /* Return the size of a location descriptor.  */
 
@@ -5028,6 +5051,50 @@  size_of_loc_descr (dw_loc_descr_ref loc)
 	size += size_of_uleb128 (op_size) + op_size;
 	break;
       }
+    case DW_OP_GNU_const_type:
+      {
+	unsigned long o
+	  = get_base_type_offset (loc->dw_loc_oprnd1.v.val_die_ref.die);
+	size += size_of_uleb128 (o) + 1;
+	switch (loc->dw_loc_oprnd2.val_class)
+	  {
+	  case dw_val_class_vec:
+	    size += loc->dw_loc_oprnd2.v.val_vec.length
+		    * loc->dw_loc_oprnd2.v.val_vec.elt_size;
+	    break;
+	  case dw_val_class_const:
+	    size += HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT;
+	    break;
+	  case dw_val_class_const_double:
+	    size += 2 * HOST_BITS_PER_WIDE_INT / BITS_PER_UNIT;
+	    break;
+	  default:
+	    gcc_unreachable ();
+	  }
+	break;
+      }
+    case DW_OP_GNU_regval_type:
+      {
+	unsigned long o
+	  = get_base_type_offset (loc->dw_loc_oprnd2.v.val_die_ref.die);
+	size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned)
+		+ size_of_uleb128 (o);
+      }
+      break;
+    case DW_OP_GNU_deref_type:
+      {
+	unsigned long o
+	  = get_base_type_offset (loc->dw_loc_oprnd2.v.val_die_ref.die);
+	size += 1 + size_of_uleb128 (o);
+      }
+      break;
+    case DW_OP_GNU_convert:
+    case DW_OP_GNU_reinterpret:
+      {
+	unsigned long o
+	  = get_base_type_offset (loc->dw_loc_oprnd1.v.val_die_ref.die);
+	size += size_of_uleb128 (o);
+      }
     default:
       break;
     }
@@ -5320,6 +5387,95 @@  output_loc_operands (dw_loc_descr_ref lo
       output_loc_sequence (val1->v.val_loc, for_eh_or_skip);
       break;
 
+    case DW_OP_GNU_const_type:
+      {
+	unsigned long o = get_base_type_offset (val1->v.val_die_ref.die), l;
+	gcc_assert (o);
+	dw2_asm_output_data_uleb128 (o, NULL);
+	switch (val2->val_class)
+	  {
+	  case dw_val_class_const:
+	    l = HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR;
+	    dw2_asm_output_data (1, l, NULL);
+	    dw2_asm_output_data (l, val2->v.val_int, NULL);
+	    break;
+	  case dw_val_class_vec:
+	    {
+	      unsigned int elt_size = val2->v.val_vec.elt_size;
+	      unsigned int len = val2->v.val_vec.length;
+	      unsigned int i;
+	      unsigned char *p;
+
+	      l = len * elt_size;
+	      dw2_asm_output_data (1, l, NULL);
+	      if (elt_size > sizeof (HOST_WIDE_INT))
+		{
+		  elt_size /= 2;
+		  len *= 2;
+		}
+	      for (i = 0, p = val2->v.val_vec.array;
+		   i < len;
+		   i++, p += elt_size)
+		dw2_asm_output_data (elt_size, extract_int (p, elt_size),
+				     "fp or vector constant word %u", i);
+	    }
+	    break;
+	  case dw_val_class_const_double:
+	    {
+	      unsigned HOST_WIDE_INT first, second;
+	      l = 2 * HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR;
+
+	      dw2_asm_output_data (1, l, NULL);
+	      if (WORDS_BIG_ENDIAN)
+		{
+		  first = val2->v.val_double.high;
+		  second = val2->v.val_double.low;
+		}
+	      else
+		{
+		  first = val2->v.val_double.low;
+		  second = val2->v.val_double.high;
+		}
+	      dw2_asm_output_data (HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR,
+				   first, NULL);
+	      dw2_asm_output_data (HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR,
+				   second, NULL);
+	    }
+	    break;
+	  default:
+	    gcc_unreachable ();
+	  }
+      }
+      break;
+    case DW_OP_GNU_regval_type:
+      {
+	unsigned r = val1->v.val_unsigned;
+	unsigned long o = get_base_type_offset (val2->v.val_die_ref.die);
+	if (for_eh_or_skip >= 0)
+	  r = DWARF2_FRAME_REG_OUT (r, for_eh_or_skip);
+	gcc_assert (size_of_uleb128 (r)
+		    == size_of_uleb128 (val1->v.val_unsigned) && o);
+	dw2_asm_output_data_uleb128 (r, NULL);
+	dw2_asm_output_data_uleb128 (o, NULL);
+      }
+      break;
+    case DW_OP_GNU_deref_type:
+      {
+	unsigned long o = get_base_type_offset (val2->v.val_die_ref.die);
+	gcc_assert (o);
+	dw2_asm_output_data (1, val1->v.val_int, NULL);
+	dw2_asm_output_data_uleb128 (o, NULL);
+      }
+      break;
+    case DW_OP_GNU_convert:
+    case DW_OP_GNU_reinterpret:
+      {
+	unsigned long o = get_base_type_offset (val1->v.val_die_ref.die);
+	gcc_assert (o);
+	dw2_asm_output_data_uleb128 (o, NULL);
+      }
+      break;
+
     default:
       /* Other codes have no operands.  */
       break;
@@ -5497,6 +5653,11 @@  output_loc_operands_raw (dw_loc_descr_re
 
     case DW_OP_GNU_implicit_pointer:
     case DW_OP_GNU_entry_value:
+    case DW_OP_GNU_const_type:
+    case DW_OP_GNU_regval_type:
+    case DW_OP_GNU_deref_type:
+    case DW_OP_GNU_convert:
+    case DW_OP_GNU_reinterpret:
       gcc_unreachable ();
       break;
 
@@ -6281,6 +6442,8 @@  static GTY(()) VEC(tree,gc) *generic_typ
    within the current function.  */
 static HOST_WIDE_INT frame_pointer_fb_offset;
 
+static VEC (dw_die_ref, heap) *base_types;
+
 /* Forward declarations for functions defined in this file.  */
 
 static int is_pseudo_reg (const_rtx);
@@ -6414,6 +6577,7 @@  static void output_location_lists (dw_di
 static int constant_size (unsigned HOST_WIDE_INT);
 static unsigned long size_of_die (dw_die_ref);
 static void calc_die_sizes (dw_die_ref);
+static void calc_base_type_die_sizes (void);
 static void mark_dies (dw_die_ref);
 static void unmark_dies (dw_die_ref);
 static void unmark_all_dies (dw_die_ref);
@@ -6754,6 +6918,21 @@  get_ref_die_offset_label (char *label, d
   sprintf (label, "%s+%ld", debug_info_section_label, ref->die_offset);
 }
 
+/* Return die_offset of a DIE reference to a base type.  */
+
+static unsigned long int
+get_base_type_offset (dw_die_ref ref)
+{
+  if (ref->die_offset)
+    return ref->die_offset;
+  if (comp_unit_die ()->die_abbrev)
+    {
+      calc_base_type_die_sizes ();
+      gcc_assert (ref->die_offset);
+    }
+  return ref->die_offset;
+}
+
 /* Convert a DIE tag into its string name.  */
 
 static const char *
@@ -10786,6 +10965,8 @@  calc_die_sizes (dw_die_ref die)
 {
   dw_die_ref c;
 
+  gcc_assert (die->die_offset == 0
+	      || (unsigned long int) die->die_offset == next_die_offset);
   die->die_offset = next_die_offset;
   next_die_offset += size_of_die (die);
 
@@ -10796,6 +10977,36 @@  calc_die_sizes (dw_die_ref die)
     next_die_offset += 1;
 }
 
+/* Size just the base type children at the start of the CU.
+   This is needed because build_abbrev needs to size locs
+   and sizing of type based stack ops needs to know die_offset
+   values for the base types.  */
+
+static void
+calc_base_type_die_sizes (void)
+{
+  unsigned long die_offset = DWARF_COMPILE_UNIT_HEADER_SIZE;
+  unsigned int i;
+  dw_die_ref base_type;
+#if ENABLE_ASSERT_CHECKING
+  dw_die_ref prev = comp_unit_die ()->die_child;
+#endif
+
+  die_offset += size_of_die (comp_unit_die ());
+  for (i = 0; VEC_iterate (dw_die_ref, base_types, i, base_type); i++)
+    {
+#if ENABLE_ASSERT_CHECKING
+      gcc_assert (base_type->die_offset == 0
+		  && prev->die_sib == base_type
+		  && base_type->die_child == NULL
+		  && base_type->die_abbrev);
+      prev = base_type;
+#endif
+      base_type->die_offset = die_offset;
+      die_offset += size_of_die (base_type);
+    }
+}
+
 /* Set the marks for a die and its children.  We do this so
    that we know whether or not a reference needs to use FORM_ref_addr; only
    DIEs in the same CU will be marked.  We used to clear out the offset
@@ -13259,7 +13470,8 @@  reg_loc_descriptor (rtx rtl, enum var_in
 
       if (dwarf_version >= 4 || !dwarf_strict)
 	{
-	  result = mem_loc_descriptor (rtl, VOIDmode, initialized);
+	  result = mem_loc_descriptor (rtl, GET_MODE (rtl), VOIDmode,
+				       initialized);
 	  if (result)
 	    add_loc_descr (&result,
 			   new_loc_descr (DW_OP_stack_value, 0, 0));
@@ -13687,6 +13899,33 @@  const_ok_for_output (rtx rtl)
   return true;
 }
 
+/* Return a reference to DW_TAG_base_type corresponding to MODE and UNSIGNEDP
+   if possible, NULL otherwise.  */
+
+static dw_die_ref
+base_type_for_mode (enum machine_mode mode, bool unsignedp)
+{
+  dw_die_ref type_die;
+  tree type = lang_hooks.types.type_for_mode (mode, unsignedp);
+
+  if (type == NULL)
+    return NULL;
+  switch (TREE_CODE (type))
+    {
+    case INTEGER_TYPE:
+    case REAL_TYPE:
+      break;
+    default:
+      return NULL;
+    }
+  type_die = lookup_type_die (type);
+  if (!type_die)
+    type_die = modified_type_die (type, false, false, comp_unit_die ());
+  if (type_die == NULL || type_die->die_tag != DW_TAG_base_type)
+    return NULL;
+  return type_die;
+}
+
 /* The following routine converts the RTL for a variable or parameter
    (resident in memory) into an equivalent Dwarf representation of a
    mechanism for getting the address of that same variable onto the top of a
@@ -13697,22 +13936,25 @@  const_ok_for_output (rtx rtl)
    equivalent.  This routine recursively descends an RTL tree, turning
    it into Dwarf postfix code as it goes.
 
-   MODE is the mode of the memory reference, needed to handle some
-   autoincrement addressing modes.
+   MODE is the mode that should be assumed for the rtl if it is VOIDmode.
 
-   CAN_USE_FBREG is a flag whether we can use DW_AT_frame_base in the
-   location list for RTL.
+   MEM_MODE is the mode of the memory reference, needed to handle some
+   autoincrement addressing modes.
 
    Return 0 if we can't represent the location.  */
 
 static dw_loc_descr_ref
 mem_loc_descriptor (rtx rtl, enum machine_mode mode,
+		    enum machine_mode mem_mode,
 		    enum var_init_status initialized)
 {
   dw_loc_descr_ref mem_loc_result = NULL;
   enum dwarf_location_atom op;
   dw_loc_descr_ref op0, op1;
 
+  if (mode == VOIDmode)
+    mode = GET_MODE (rtl);
+
   /* Note that for a dynamically sized array, the location we will generate a
      description of here will be the lowest numbered location which is
      actually within the array.  That's *not* necessarily the same as the
@@ -13720,12 +13962,15 @@  mem_loc_descriptor (rtx rtl, enum machin
 
   rtl = targetm.delegitimize_address (rtl);
 
+  if (mode != GET_MODE (rtl) && GET_MODE (rtl) != VOIDmode)
+    return NULL;
+
   switch (GET_CODE (rtl))
     {
     case POST_INC:
     case POST_DEC:
     case POST_MODIFY:
-      return mem_loc_descriptor (XEXP (rtl, 0), mode, initialized);
+      return mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode, initialized);
 
     case SUBREG:
       /* The case of a subreg may arise when we have a local (register)
@@ -13735,15 +13980,72 @@  mem_loc_descriptor (rtx rtl, enum machin
 	 contains the given subreg.  */
       if (!subreg_lowpart_p (rtl))
 	break;
-      rtl = SUBREG_REG (rtl);
-      if (GET_MODE_SIZE (GET_MODE (rtl)) > DWARF2_ADDR_SIZE)
+      if (GET_MODE_CLASS (mode) == MODE_INT
+	  && GET_MODE_CLASS (GET_MODE (SUBREG_REG (rtl))) == MODE_INT
+	  && GET_MODE_SIZE (mode) <= DWARF2_ADDR_SIZE
+	  && GET_MODE_SIZE (GET_MODE (SUBREG_REG (rtl))) <= DWARF2_ADDR_SIZE)
+	{
+	  mem_loc_result = mem_loc_descriptor (SUBREG_REG (rtl),
+					       GET_MODE (SUBREG_REG (rtl)),
+					       mem_mode, initialized);
+	  break;
+	}
+      if (dwarf_strict)
+	break;
+      if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (rtl))))
 	break;
-      if (GET_MODE_CLASS (GET_MODE (rtl)) != MODE_INT)
+      if (GET_MODE_SIZE (mode) != GET_MODE_SIZE (GET_MODE (SUBREG_REG (rtl)))
+	  && (GET_MODE_CLASS (mode) != MODE_INT
+	      || GET_MODE_CLASS (GET_MODE (SUBREG_REG (rtl))) != MODE_INT))
 	break;
-      mem_loc_result = mem_loc_descriptor (rtl, mode, initialized);
+      else
+	{
+	  dw_die_ref type_die;
+	  dw_loc_descr_ref cvt;
+
+	  mem_loc_result = mem_loc_descriptor (SUBREG_REG (rtl),
+					       GET_MODE (SUBREG_REG (rtl)),
+					       mode, initialized);
+	  if (mem_loc_result == NULL)
+	    break;
+	  type_die = base_type_for_mode (mode, 0);
+	  if (type_die == NULL)
+	    {
+	      mem_loc_result = NULL;
+	      break;
+	    }
+	  if (GET_MODE_SIZE (mode)
+	      != GET_MODE_SIZE (GET_MODE (SUBREG_REG (rtl))))
+	    cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0);
+	  else
+	    cvt = new_loc_descr (DW_OP_GNU_reinterpret, 0, 0);
+	  cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+	  cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die;
+	  cvt->dw_loc_oprnd1.v.val_die_ref.external = 0;
+	  add_loc_descr (&mem_loc_result, cvt);
+	}
       break;
 
     case REG:
+      if (GET_MODE_CLASS (mode) != MODE_INT
+	  || GET_MODE_SIZE (mode) > DWARF2_ADDR_SIZE)
+	{
+	  dw_die_ref type_die;
+
+	  if (dwarf_strict)
+	    break;
+	  if (REGNO (rtl) > FIRST_PSEUDO_REGISTER)
+	    break;
+	  type_die = base_type_for_mode (mode, 0);
+	  if (type_die == NULL)
+	    break;
+	  mem_loc_result = new_loc_descr (DW_OP_GNU_regval_type,
+					  dbx_reg_number (rtl), 0);
+	  mem_loc_result->dw_loc_oprnd2.val_class = dw_val_class_die_ref;
+	  mem_loc_result->dw_loc_oprnd2.v.val_die_ref.die = type_die;
+	  mem_loc_result->dw_loc_oprnd2.v.val_die_ref.external = 0;
+	  break;
+	}
       /* Whenever a register number forms a part of the description of the
 	 method for calculating the (dynamic) address of a memory resident
 	 object, DWARF rules require the register number be referred to as
@@ -13773,11 +14075,12 @@  mem_loc_descriptor (rtx rtl, enum machin
 
     case SIGN_EXTEND:
     case ZERO_EXTEND:
-      op0 = mem_loc_descriptor (XEXP (rtl, 0), mode,
-				VAR_INIT_STATUS_INITIALIZED);
+      gcc_assert (GET_MODE_CLASS (mode) == MODE_INT);
+      op0 = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (XEXP (rtl, 0)),
+				mem_mode, VAR_INIT_STATUS_INITIALIZED);
       if (op0 == 0)
 	break;
-      else
+      else if (GET_MODE_SIZE (mode) <= DWARF2_ADDR_SIZE)
 	{
 	  int shift = DWARF2_ADDR_SIZE
 		      - GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0)));
@@ -13792,32 +14095,70 @@  mem_loc_descriptor (rtx rtl, enum machin
 	  add_loc_descr (&mem_loc_result, int_loc_descriptor (shift));
 	  add_loc_descr (&mem_loc_result, new_loc_descr (op, 0, 0));
 	}
+      else if (!dwarf_strict)
+	{
+	  dw_die_ref type_die1, type_die2;
+	  dw_loc_descr_ref cvt;
+
+	  type_die1 = base_type_for_mode (GET_MODE (XEXP (rtl, 0)),
+					  GET_CODE (rtl) == ZERO_EXTEND);
+	  if (type_die1 == NULL)
+	    break;
+	  type_die2 = base_type_for_mode (mode, 0);
+	  if (type_die2 == NULL)
+	    break;
+	  mem_loc_result = op0;
+	  cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0);
+	  cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+	  cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die1;
+	  cvt->dw_loc_oprnd1.v.val_die_ref.external = 0;
+	  add_loc_descr (&mem_loc_result, cvt);
+	  cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0);
+	  cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+	  cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die2;
+	  cvt->dw_loc_oprnd1.v.val_die_ref.external = 0;
+	  add_loc_descr (&mem_loc_result, cvt);
+	}
       break;
 
     case MEM:
-      mem_loc_result = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (rtl),
+      mem_loc_result = mem_loc_descriptor (XEXP (rtl, 0),
+					   get_address_mode (rtl), mode,
 					   VAR_INIT_STATUS_INITIALIZED);
       if (mem_loc_result == NULL)
 	mem_loc_result = tls_mem_loc_descriptor (rtl);
       if (mem_loc_result != 0)
 	{
-	  if (GET_MODE_SIZE (GET_MODE (rtl)) > DWARF2_ADDR_SIZE)
+	  if (GET_MODE_SIZE (mode) > DWARF2_ADDR_SIZE
+	      || GET_MODE_CLASS (mode) != MODE_INT)
 	    {
-	      expansion_failed (NULL_TREE, rtl, "DWARF address size mismatch");
-	      return 0;
+	      dw_die_ref type_die;
+	      dw_loc_descr_ref deref;
+
+	      if (dwarf_strict)
+		return NULL;
+	      type_die = base_type_for_mode (mode, 0);
+	      if (type_die == NULL)
+		return NULL;
+	      deref = new_loc_descr (DW_OP_GNU_deref_type,
+				     GET_MODE_SIZE (mode), 0);
+	      deref->dw_loc_oprnd2.val_class = dw_val_class_die_ref;
+	      deref->dw_loc_oprnd2.v.val_die_ref.die = type_die;
+	      deref->dw_loc_oprnd2.v.val_die_ref.external = 0;
+	      add_loc_descr (&mem_loc_result, deref);
 	    }
-	  else if (GET_MODE_SIZE (GET_MODE (rtl)) == DWARF2_ADDR_SIZE)
+	  else if (GET_MODE_SIZE (mode) == DWARF2_ADDR_SIZE)
 	    add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_deref, 0, 0));
 	  else
 	    add_loc_descr (&mem_loc_result,
 			   new_loc_descr (DW_OP_deref_size,
-					  GET_MODE_SIZE (GET_MODE (rtl)), 0));
+					  GET_MODE_SIZE (mode), 0));
 	}
       else
 	{
 	  rtx new_rtl = avoid_constant_pool_reference (rtl);
 	  if (new_rtl != rtl)
-	    return mem_loc_descriptor (new_rtl, mode, initialized);
+	    return mem_loc_descriptor (new_rtl, mode, mem_mode, initialized);
 	}
       break;
 
@@ -13832,6 +14173,9 @@  mem_loc_descriptor (rtx rtl, enum machin
 	 pool.  */
     case CONST:
     case SYMBOL_REF:
+      if (GET_MODE_SIZE (mode) > DWARF2_ADDR_SIZE
+	  || GET_MODE_CLASS (mode) != MODE_INT)
+	break;
       if (GET_CODE (rtl) == SYMBOL_REF
 	  && SYMBOL_REF_TLS_MODEL (rtl) != TLS_MODEL_NONE)
 	{
@@ -13877,23 +14221,32 @@  mem_loc_descriptor (rtx rtl, enum machin
     case ENTRY_VALUE:
       if (dwarf_strict)
 	return NULL;
-      mem_loc_result = new_loc_descr (DW_OP_GNU_entry_value, 0, 0);
-      mem_loc_result->dw_loc_oprnd1.val_class = dw_val_class_loc;
       if (REG_P (ENTRY_VALUE_EXP (rtl)))
-	mem_loc_result->dw_loc_oprnd1.v.val_loc
-	  = one_reg_loc_descriptor (dbx_reg_number (ENTRY_VALUE_EXP (rtl)),
-				    VAR_INIT_STATUS_INITIALIZED);
-      else if (MEM_P (ENTRY_VALUE_EXP (rtl)) && REG_P (XEXP (ENTRY_VALUE_EXP (rtl), 0)))
 	{
-	  dw_loc_descr_ref ref
-	    = mem_loc_descriptor (ENTRY_VALUE_EXP (rtl), GET_MODE (rtl),
-				  VAR_INIT_STATUS_INITIALIZED);
-	  if (ref == NULL || ref->dw_loc_opc == DW_OP_fbreg)
+	  if (GET_MODE_CLASS (mode) != MODE_INT
+	      || GET_MODE_SIZE (mode) > DWARF2_ADDR_SIZE)
+	    op0 = mem_loc_descriptor (ENTRY_VALUE_EXP (rtl), mode,
+				      VOIDmode, VAR_INIT_STATUS_INITIALIZED);
+	  else
+	    op0
+	      = one_reg_loc_descriptor (dbx_reg_number (ENTRY_VALUE_EXP (rtl)),
+					VAR_INIT_STATUS_INITIALIZED);
+	}
+      else if (MEM_P (ENTRY_VALUE_EXP (rtl))
+	       && REG_P (XEXP (ENTRY_VALUE_EXP (rtl), 0)))
+	{
+	  op0 = mem_loc_descriptor (ENTRY_VALUE_EXP (rtl), mode,
+				    VOIDmode, VAR_INIT_STATUS_INITIALIZED);
+	  if (op0 && op0->dw_loc_opc == DW_OP_fbreg)
 	    return NULL;
-	  mem_loc_result->dw_loc_oprnd1.v.val_loc = ref;
 	}
       else
 	gcc_unreachable ();
+      if (op0 == NULL)
+	return NULL;
+      mem_loc_result = new_loc_descr (DW_OP_GNU_entry_value, 0, 0);
+      mem_loc_result->dw_loc_oprnd1.val_class = dw_val_class_loc;
+      mem_loc_result->dw_loc_oprnd1.v.val_loc = op0;
       return mem_loc_result;
 
     case PRE_MODIFY:
@@ -13906,32 +14259,35 @@  mem_loc_descriptor (rtx rtl, enum machin
     case PRE_DEC:
       /* Turn these into a PLUS expression and fall into the PLUS code
 	 below.  */
-      rtl = gen_rtx_PLUS (word_mode, XEXP (rtl, 0),
+      rtl = gen_rtx_PLUS (mode, XEXP (rtl, 0),
 			  GEN_INT (GET_CODE (rtl) == PRE_INC
-				   ? GET_MODE_UNIT_SIZE (mode)
-				   : -GET_MODE_UNIT_SIZE (mode)));
+				   ? GET_MODE_UNIT_SIZE (mem_mode)
+				   : -GET_MODE_UNIT_SIZE (mem_mode)));
 
       /* ... fall through ...  */
 
     case PLUS:
     plus:
-      if (is_based_loc (rtl))
+      if (is_based_loc (rtl)
+	  && GET_MODE_SIZE (mode) <= DWARF2_ADDR_SIZE
+	  && GET_MODE_CLASS (mode) == MODE_INT)
 	mem_loc_result = based_loc_descr (XEXP (rtl, 0),
 					  INTVAL (XEXP (rtl, 1)),
 					  VAR_INIT_STATUS_INITIALIZED);
       else
 	{
-	  mem_loc_result = mem_loc_descriptor (XEXP (rtl, 0), mode,
+	  mem_loc_result = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode,
 					       VAR_INIT_STATUS_INITIALIZED);
 	  if (mem_loc_result == 0)
 	    break;
 
-	  if (CONST_INT_P (XEXP (rtl, 1)))
+	  if (CONST_INT_P (XEXP (rtl, 1))
+	      && GET_MODE_SIZE (mode) <= DWARF2_ADDR_SIZE)
 	    loc_descr_plus_const (&mem_loc_result, INTVAL (XEXP (rtl, 1)));
 	  else
 	    {
 	      dw_loc_descr_ref mem_loc_result2
-		= mem_loc_descriptor (XEXP (rtl, 1), mode,
+		= mem_loc_descriptor (XEXP (rtl, 1), mode, mem_mode,
 				      VAR_INIT_STATUS_INITIALIZED);
 	      if (mem_loc_result2 == 0)
 		break;
@@ -13962,15 +14318,31 @@  mem_loc_descriptor (rtx rtl, enum machin
 
     case ASHIFT:
       op = DW_OP_shl;
-      goto do_binop;
+      goto do_shift;
 
     case ASHIFTRT:
       op = DW_OP_shra;
-      goto do_binop;
+      goto do_shift;
 
     case LSHIFTRT:
       op = DW_OP_shr;
-      goto do_binop;
+      goto do_shift;
+
+    do_shift:
+      op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode,
+				VAR_INIT_STATUS_INITIALIZED);
+      op1 = mem_loc_descriptor (XEXP (rtl, 1),
+				GET_MODE (XEXP (rtl, 1)) == VOIDmode
+				? mode : GET_MODE (XEXP (rtl, 1)), mem_mode,
+				VAR_INIT_STATUS_INITIALIZED);
+
+      if (op0 == 0 || op1 == 0)
+	break;
+
+      mem_loc_result = op0;
+      add_loc_descr (&mem_loc_result, op1);
+      add_loc_descr (&mem_loc_result, new_loc_descr (op, 0, 0));
+      break;
 
     case AND:
       op = DW_OP_and;
@@ -13985,9 +14357,9 @@  mem_loc_descriptor (rtx rtl, enum machin
       goto do_binop;
 
     do_binop:
-      op0 = mem_loc_descriptor (XEXP (rtl, 0), mode,
+      op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode,
 				VAR_INIT_STATUS_INITIALIZED);
-      op1 = mem_loc_descriptor (XEXP (rtl, 1), mode,
+      op1 = mem_loc_descriptor (XEXP (rtl, 1), mode, mem_mode,
 				VAR_INIT_STATUS_INITIALIZED);
 
       if (op0 == 0 || op1 == 0)
@@ -13999,9 +14371,9 @@  mem_loc_descriptor (rtx rtl, enum machin
       break;
 
     case MOD:
-      op0 = mem_loc_descriptor (XEXP (rtl, 0), mode,
+      op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode,
 				VAR_INIT_STATUS_INITIALIZED);
-      op1 = mem_loc_descriptor (XEXP (rtl, 1), mode,
+      op1 = mem_loc_descriptor (XEXP (rtl, 1), mode, mem_mode,
 				VAR_INIT_STATUS_INITIALIZED);
 
       if (op0 == 0 || op1 == 0)
@@ -14029,7 +14401,7 @@  mem_loc_descriptor (rtx rtl, enum machin
       goto do_unop;
 
     do_unop:
-      op0 = mem_loc_descriptor (XEXP (rtl, 0), mode,
+      op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode,
 				VAR_INIT_STATUS_INITIALIZED);
 
       if (op0 == 0)
@@ -14040,7 +14412,75 @@  mem_loc_descriptor (rtx rtl, enum machin
       break;
 
     case CONST_INT:
-      mem_loc_result = int_loc_descriptor (INTVAL (rtl));
+      if (GET_MODE_SIZE (mode) <= DWARF2_ADDR_SIZE)
+	{
+	  mem_loc_result = int_loc_descriptor (INTVAL (rtl));
+	  break;
+	}
+      if (!dwarf_strict
+	  && (GET_MODE_BITSIZE (mode) == HOST_BITS_PER_WIDE_INT
+	      || GET_MODE_BITSIZE (mode) == 2 * HOST_BITS_PER_WIDE_INT))
+	{
+	  dw_die_ref type_die = base_type_for_mode (mode, 0);
+	  if (type_die == NULL)
+	    return NULL;
+	  mem_loc_result = new_loc_descr (DW_OP_GNU_const_type, 0,
+					  INTVAL (rtl));
+	  mem_loc_result->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+	  mem_loc_result->dw_loc_oprnd1.v.val_die_ref.die = type_die;
+	  mem_loc_result->dw_loc_oprnd1.v.val_die_ref.external = 0;
+	  if (GET_MODE_BITSIZE (mode) == HOST_BITS_PER_WIDE_INT)
+	    mem_loc_result->dw_loc_oprnd2.val_class = dw_val_class_const;
+	  else
+	    {
+	      mem_loc_result->dw_loc_oprnd2.val_class
+		= dw_val_class_const_double;
+	      mem_loc_result->dw_loc_oprnd2.v.val_double
+		= shwi_to_double_int (INTVAL (rtl));
+	    }
+	}
+      break;
+
+    case CONST_DOUBLE:
+      if (!dwarf_strict)
+	{
+	  dw_die_ref type_die;
+
+	  /* Note that a CONST_DOUBLE rtx could represent either an integer
+	     or a floating-point constant.  A CONST_DOUBLE is used whenever
+	     the constant requires more than one word in order to be
+	     adequately represented.  We output CONST_DOUBLEs as blocks.  */
+	  if (mode == VOIDmode
+	      || (GET_MODE (rtl) == VOIDmode
+		  && GET_MODE_BITSIZE (mode) != 2 * HOST_BITS_PER_WIDE_INT))
+	    break;
+	  type_die = base_type_for_mode (mode, 0);
+	  if (type_die == NULL)
+	    return NULL;
+	  mem_loc_result = new_loc_descr (DW_OP_GNU_const_type, 0, 0);
+	  mem_loc_result->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+	  mem_loc_result->dw_loc_oprnd1.v.val_die_ref.die = type_die;
+	  mem_loc_result->dw_loc_oprnd1.v.val_die_ref.external = 0;
+	  if (SCALAR_FLOAT_MODE_P (mode))
+	    {
+	      unsigned int length = GET_MODE_SIZE (mode);
+	      unsigned char *array
+		  = (unsigned char*) ggc_alloc_atomic (length);
+
+	      insert_float (rtl, array);
+	      mem_loc_result->dw_loc_oprnd2.val_class = dw_val_class_vec;
+	      mem_loc_result->dw_loc_oprnd2.v.val_vec.length = length / 4;
+	      mem_loc_result->dw_loc_oprnd2.v.val_vec.elt_size = 4;
+	      mem_loc_result->dw_loc_oprnd2.v.val_vec.array = array;
+	    }
+	  else
+	    {
+	      mem_loc_result->dw_loc_oprnd2.val_class
+		= dw_val_class_const_double;
+	      mem_loc_result->dw_loc_oprnd2.v.val_double
+		= rtx_to_double_int (rtl);
+	    }
+	}
       break;
 
     case EQ:
@@ -14068,74 +14508,75 @@  mem_loc_descriptor (rtx rtl, enum machin
       goto do_scompare;
 
     do_scompare:
-      if (GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))) > DWARF2_ADDR_SIZE
-	  || GET_MODE_SIZE (GET_MODE (XEXP (rtl, 1))) > DWARF2_ADDR_SIZE)
-	break;
-      else
-	{
-	  enum machine_mode op_mode = GET_MODE (XEXP (rtl, 0));
+      {
+	enum machine_mode op_mode = GET_MODE (XEXP (rtl, 0));
 
-	  if (op_mode == VOIDmode)
-	    op_mode = GET_MODE (XEXP (rtl, 1));
-	  if (op_mode != VOIDmode && GET_MODE_CLASS (op_mode) != MODE_INT)
-	    break;
+	if (op_mode == VOIDmode)
+	  op_mode = GET_MODE (XEXP (rtl, 1));
+	if (op_mode == VOIDmode)
+	  break;
 
-	  op0 = mem_loc_descriptor (XEXP (rtl, 0), mode,
-				    VAR_INIT_STATUS_INITIALIZED);
-	  op1 = mem_loc_descriptor (XEXP (rtl, 1), mode,
-				    VAR_INIT_STATUS_INITIALIZED);
+	if (dwarf_strict
+	    && (GET_MODE_CLASS (op_mode) != MODE_INT
+		|| GET_MODE_SIZE (op_mode) > DWARF2_ADDR_SIZE))
+	  break;
 
-	  if (op0 == 0 || op1 == 0)
-	    break;
+	op0 = mem_loc_descriptor (XEXP (rtl, 0), op_mode, mem_mode,
+				  VAR_INIT_STATUS_INITIALIZED);
+	op1 = mem_loc_descriptor (XEXP (rtl, 1), op_mode, mem_mode,
+				  VAR_INIT_STATUS_INITIALIZED);
 
-	  if (op_mode != VOIDmode
-	      && GET_MODE_SIZE (op_mode) < DWARF2_ADDR_SIZE)
-	    {
-	      int shift = DWARF2_ADDR_SIZE - GET_MODE_SIZE (op_mode);
-	      shift *= BITS_PER_UNIT;
-	      /* For eq/ne, if the operands are known to be zero-extended,
-		 there is no need to do the fancy shifting up.  */
-	      if (op == DW_OP_eq || op == DW_OP_ne)
-		{
-		  dw_loc_descr_ref last0, last1;
-		  for (last0 = op0;
-		       last0->dw_loc_next != NULL;
-		       last0 = last0->dw_loc_next)
-		    ;
-		  for (last1 = op1;
-		       last1->dw_loc_next != NULL;
-		       last1 = last1->dw_loc_next)
-		    ;
-		  /* deref_size zero extends, and for constants we can check
-		     whether they are zero extended or not.  */
-		  if (((last0->dw_loc_opc == DW_OP_deref_size
-			&& last0->dw_loc_oprnd1.v.val_int
-			   <= GET_MODE_SIZE (op_mode))
-		       || (CONST_INT_P (XEXP (rtl, 0))
-			    && (unsigned HOST_WIDE_INT) INTVAL (XEXP (rtl, 0))
-			       == (INTVAL (XEXP (rtl, 0))
-				   & GET_MODE_MASK (op_mode))))
-		      && ((last1->dw_loc_opc == DW_OP_deref_size
-			   && last1->dw_loc_oprnd1.v.val_int
-			      <= GET_MODE_SIZE (op_mode))
-			  || (CONST_INT_P (XEXP (rtl, 1))
-			      && (unsigned HOST_WIDE_INT)
-				 INTVAL (XEXP (rtl, 1))
-				 == (INTVAL (XEXP (rtl, 1))
-				     & GET_MODE_MASK (op_mode)))))
-		    goto do_compare;
-		}
-	      add_loc_descr (&op0, int_loc_descriptor (shift));
-	      add_loc_descr (&op0, new_loc_descr (DW_OP_shl, 0, 0));
-	      if (CONST_INT_P (XEXP (rtl, 1)))
-		op1 = int_loc_descriptor (INTVAL (XEXP (rtl, 1)) << shift);
-	      else
-		{
-		  add_loc_descr (&op1, int_loc_descriptor (shift));
-		  add_loc_descr (&op1, new_loc_descr (DW_OP_shl, 0, 0));
-		}
-	    }
-	}
+	if (op0 == 0 || op1 == 0)
+	  break;
+
+	if (GET_MODE_CLASS (op_mode) == MODE_INT
+	    && GET_MODE_SIZE (op_mode) < DWARF2_ADDR_SIZE)
+	  {
+	    int shift = DWARF2_ADDR_SIZE - GET_MODE_SIZE (op_mode);
+	    shift *= BITS_PER_UNIT;
+	    /* For eq/ne, if the operands are known to be zero-extended,
+	       there is no need to do the fancy shifting up.  */
+	    if (op == DW_OP_eq || op == DW_OP_ne)
+	      {
+		dw_loc_descr_ref last0, last1;
+		for (last0 = op0;
+		     last0->dw_loc_next != NULL;
+		     last0 = last0->dw_loc_next)
+		  ;
+		for (last1 = op1;
+		     last1->dw_loc_next != NULL;
+		     last1 = last1->dw_loc_next)
+		  ;
+		/* deref_size zero extends, and for constants we can check
+		   whether they are zero extended or not.  */
+		if (((last0->dw_loc_opc == DW_OP_deref_size
+		      && last0->dw_loc_oprnd1.v.val_int
+			 <= GET_MODE_SIZE (op_mode))
+		     || (CONST_INT_P (XEXP (rtl, 0))
+			 && (unsigned HOST_WIDE_INT) INTVAL (XEXP (rtl, 0))
+			     == (INTVAL (XEXP (rtl, 0))
+				 & GET_MODE_MASK (op_mode))))
+		    && ((last1->dw_loc_opc == DW_OP_deref_size
+			 && last1->dw_loc_oprnd1.v.val_int
+			    <= GET_MODE_SIZE (op_mode))
+			|| (CONST_INT_P (XEXP (rtl, 1))
+			    && (unsigned HOST_WIDE_INT)
+			       INTVAL (XEXP (rtl, 1))
+			       == (INTVAL (XEXP (rtl, 1))
+				   & GET_MODE_MASK (op_mode)))))
+		  goto do_compare;
+	      }
+	    add_loc_descr (&op0, int_loc_descriptor (shift));
+	    add_loc_descr (&op0, new_loc_descr (DW_OP_shl, 0, 0));
+	    if (CONST_INT_P (XEXP (rtl, 1)))
+	      op1 = int_loc_descriptor (INTVAL (XEXP (rtl, 1)) << shift);
+	    else
+	      {
+		add_loc_descr (&op1, int_loc_descriptor (shift));
+		add_loc_descr (&op1, new_loc_descr (DW_OP_shl, 0, 0));
+	      }
+	  }
+      }
 
     do_compare:
       mem_loc_result = op0;
@@ -14166,87 +14607,111 @@  mem_loc_descriptor (rtx rtl, enum machin
       goto do_ucompare;
 
     do_ucompare:
-      if (GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))) > DWARF2_ADDR_SIZE
-	  || GET_MODE_SIZE (GET_MODE (XEXP (rtl, 1))) > DWARF2_ADDR_SIZE)
-	break;
-      else
-	{
-	  enum machine_mode op_mode = GET_MODE (XEXP (rtl, 0));
+      {
+	enum machine_mode op_mode = GET_MODE (XEXP (rtl, 0));
 
-	  if (op_mode == VOIDmode)
-	    op_mode = GET_MODE (XEXP (rtl, 1));
-	  if (op_mode != VOIDmode && GET_MODE_CLASS (op_mode) != MODE_INT)
-	    break;
+	if (op_mode == VOIDmode)
+	  op_mode = GET_MODE (XEXP (rtl, 1));
+	if (op_mode == VOIDmode)
+	  break;
+	if (GET_MODE_CLASS (op_mode) != MODE_INT)
+	  break;
 
-	  op0 = mem_loc_descriptor (XEXP (rtl, 0), mode,
-				    VAR_INIT_STATUS_INITIALIZED);
-	  op1 = mem_loc_descriptor (XEXP (rtl, 1), mode,
-				    VAR_INIT_STATUS_INITIALIZED);
+	if (dwarf_strict && GET_MODE_SIZE (op_mode) > DWARF2_ADDR_SIZE)
+	  break;
 
-	  if (op0 == 0 || op1 == 0)
+	if (op_mode != VOIDmode && GET_MODE_CLASS (op_mode) != MODE_INT)
 	    break;
 
-	  if (op_mode != VOIDmode
-	      && GET_MODE_SIZE (op_mode) < DWARF2_ADDR_SIZE)
-	    {
-	      HOST_WIDE_INT mask = GET_MODE_MASK (op_mode);
-	      dw_loc_descr_ref last0, last1;
-	      for (last0 = op0;
-		   last0->dw_loc_next != NULL;
-		   last0 = last0->dw_loc_next)
-		;
-	      for (last1 = op1;
-		   last1->dw_loc_next != NULL;
-		   last1 = last1->dw_loc_next)
-		;
-	      if (CONST_INT_P (XEXP (rtl, 0)))
-		op0 = int_loc_descriptor (INTVAL (XEXP (rtl, 0)) & mask);
-	      /* deref_size zero extends, so no need to mask it again.  */
-	      else if (last0->dw_loc_opc != DW_OP_deref_size
-		       || last0->dw_loc_oprnd1.v.val_int
-			  > GET_MODE_SIZE (op_mode))
-		{
-		  add_loc_descr (&op0, int_loc_descriptor (mask));
-		  add_loc_descr (&op0, new_loc_descr (DW_OP_and, 0, 0));
-		}
-	      if (CONST_INT_P (XEXP (rtl, 1)))
-		op1 = int_loc_descriptor (INTVAL (XEXP (rtl, 1)) & mask);
-	      /* deref_size zero extends, so no need to mask it again.  */
-	      else if (last1->dw_loc_opc != DW_OP_deref_size
-		       || last1->dw_loc_oprnd1.v.val_int
-			  > GET_MODE_SIZE (op_mode))
-		{
-		  add_loc_descr (&op1, int_loc_descriptor (mask));
-		  add_loc_descr (&op1, new_loc_descr (DW_OP_and, 0, 0));
-		}
-	    }
-	  else
-	    {
-	      HOST_WIDE_INT bias = 1;
-	      bias <<= (DWARF2_ADDR_SIZE * BITS_PER_UNIT - 1);
-	      add_loc_descr (&op0, new_loc_descr (DW_OP_plus_uconst, bias, 0));
-	      if (CONST_INT_P (XEXP (rtl, 1)))
-		op1 = int_loc_descriptor ((unsigned HOST_WIDE_INT) bias
-					  + INTVAL (XEXP (rtl, 1)));
-	      else
-		add_loc_descr (&op1, new_loc_descr (DW_OP_plus_uconst,
-						    bias, 0));
-	    }
-	}
+	op0 = mem_loc_descriptor (XEXP (rtl, 0), op_mode, mem_mode,
+				  VAR_INIT_STATUS_INITIALIZED);
+	op1 = mem_loc_descriptor (XEXP (rtl, 1), op_mode, mem_mode,
+				  VAR_INIT_STATUS_INITIALIZED);
+
+	if (op0 == 0 || op1 == 0)
+	  break;
+
+	if (GET_MODE_SIZE (op_mode) < DWARF2_ADDR_SIZE)
+	  {
+	    HOST_WIDE_INT mask = GET_MODE_MASK (op_mode);
+	    dw_loc_descr_ref last0, last1;
+	    for (last0 = op0;
+		 last0->dw_loc_next != NULL;
+		 last0 = last0->dw_loc_next)
+	      ;
+	    for (last1 = op1;
+		 last1->dw_loc_next != NULL;
+		 last1 = last1->dw_loc_next)
+	      ;
+	    if (CONST_INT_P (XEXP (rtl, 0)))
+	      op0 = int_loc_descriptor (INTVAL (XEXP (rtl, 0)) & mask);
+	    /* deref_size zero extends, so no need to mask it again.  */
+	    else if (last0->dw_loc_opc != DW_OP_deref_size
+		     || last0->dw_loc_oprnd1.v.val_int
+			> GET_MODE_SIZE (op_mode))
+	      {
+		add_loc_descr (&op0, int_loc_descriptor (mask));
+		add_loc_descr (&op0, new_loc_descr (DW_OP_and, 0, 0));
+	      }
+	    if (CONST_INT_P (XEXP (rtl, 1)))
+	      op1 = int_loc_descriptor (INTVAL (XEXP (rtl, 1)) & mask);
+	    /* deref_size zero extends, so no need to mask it again.  */
+	    else if (last1->dw_loc_opc != DW_OP_deref_size
+		     || last1->dw_loc_oprnd1.v.val_int
+			> GET_MODE_SIZE (op_mode))
+	      {
+		add_loc_descr (&op1, int_loc_descriptor (mask));
+		add_loc_descr (&op1, new_loc_descr (DW_OP_and, 0, 0));
+	      }
+	  }
+	else if (GET_MODE_SIZE (op_mode) == DWARF2_ADDR_SIZE)
+	  {
+	    HOST_WIDE_INT bias = 1;
+	    bias <<= (DWARF2_ADDR_SIZE * BITS_PER_UNIT - 1);
+	    add_loc_descr (&op0, new_loc_descr (DW_OP_plus_uconst, bias, 0));
+	    if (CONST_INT_P (XEXP (rtl, 1)))
+	      op1 = int_loc_descriptor ((unsigned HOST_WIDE_INT) bias
+					+ INTVAL (XEXP (rtl, 1)));
+	    else
+	      add_loc_descr (&op1, new_loc_descr (DW_OP_plus_uconst,
+						  bias, 0));
+	  }
+	else
+	  {
+	    dw_die_ref type_die = base_type_for_mode (mode, 1);
+	    dw_loc_descr_ref cvt;
+
+	    if (type_die == NULL)
+	      break;
+	    cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0);
+	    cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+	    cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die;
+	    cvt->dw_loc_oprnd1.v.val_die_ref.external = 0;
+	    add_loc_descr (&op0, cvt);
+	    cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0);
+	    cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+	    cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die;
+	    cvt->dw_loc_oprnd1.v.val_die_ref.external = 0;
+	    add_loc_descr (&op1, cvt);
+	  }
+      }
       goto do_compare;
 
-    case SMIN:
-    case SMAX:
     case UMIN:
     case UMAX:
-      if (GET_MODE_CLASS (GET_MODE (XEXP (rtl, 0))) != MODE_INT
-	  || GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))) > DWARF2_ADDR_SIZE
-	  || GET_MODE (XEXP (rtl, 0)) != GET_MODE (XEXP (rtl, 1)))
+      if (GET_MODE_CLASS (mode) != MODE_INT)
+	break;
+      /* FALLTHRU */
+    case SMIN:
+    case SMAX:
+      if (dwarf_strict
+	  && (GET_MODE_CLASS (mode) != MODE_INT
+	      || GET_MODE_SIZE (mode) > DWARF2_ADDR_SIZE))
 	break;
 
-      op0 = mem_loc_descriptor (XEXP (rtl, 0), mode,
+      op0 = mem_loc_descriptor (XEXP (rtl, 0), mode, mem_mode,
 				VAR_INIT_STATUS_INITIALIZED);
-      op1 = mem_loc_descriptor (XEXP (rtl, 1), mode,
+      op1 = mem_loc_descriptor (XEXP (rtl, 1), mode, mem_mode,
 				VAR_INIT_STATUS_INITIALIZED);
 
       if (op0 == 0 || op1 == 0)
@@ -14257,26 +14722,44 @@  mem_loc_descriptor (rtx rtl, enum machin
       add_loc_descr (&op1, new_loc_descr (DW_OP_over, 0, 0));
       if (GET_CODE (rtl) == UMIN || GET_CODE (rtl) == UMAX)
 	{
-	  if (GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))) < DWARF2_ADDR_SIZE)
+	  if (GET_MODE_SIZE (mode) < DWARF2_ADDR_SIZE)
 	    {
-	      HOST_WIDE_INT mask = GET_MODE_MASK (GET_MODE (XEXP (rtl, 0)));
+	      HOST_WIDE_INT mask = GET_MODE_MASK (mode);
 	      add_loc_descr (&op0, int_loc_descriptor (mask));
 	      add_loc_descr (&op0, new_loc_descr (DW_OP_and, 0, 0));
 	      add_loc_descr (&op1, int_loc_descriptor (mask));
 	      add_loc_descr (&op1, new_loc_descr (DW_OP_and, 0, 0));
 	    }
-	  else
+	  else if (GET_MODE_SIZE (mode) == DWARF2_ADDR_SIZE)
 	    {
 	      HOST_WIDE_INT bias = 1;
 	      bias <<= (DWARF2_ADDR_SIZE * BITS_PER_UNIT - 1);
 	      add_loc_descr (&op0, new_loc_descr (DW_OP_plus_uconst, bias, 0));
 	      add_loc_descr (&op1, new_loc_descr (DW_OP_plus_uconst, bias, 0));
 	    }
+	  else
+	    {
+	      dw_die_ref type_die = base_type_for_mode (mode, 1);
+	      dw_loc_descr_ref cvt;
+
+	      if (type_die == NULL)
+		break;
+	      cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0);
+	      cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+	      cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die;
+	      cvt->dw_loc_oprnd1.v.val_die_ref.external = 0;
+	      add_loc_descr (&op0, cvt);
+	      cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0);
+	      cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+	      cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die;
+	      cvt->dw_loc_oprnd1.v.val_die_ref.external = 0;
+	      add_loc_descr (&op1, cvt);
+	    }
 	}
-      else if (GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))) < DWARF2_ADDR_SIZE)
+      else if (GET_MODE_CLASS (mode) == MODE_INT
+	       && GET_MODE_SIZE (mode) < DWARF2_ADDR_SIZE)
 	{
-	  int shift = DWARF2_ADDR_SIZE
-		      - GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0)));
+	  int shift = DWARF2_ADDR_SIZE - GET_MODE_SIZE (mode);
 	  shift *= BITS_PER_UNIT;
 	  add_loc_descr (&op0, int_loc_descriptor (shift));
 	  add_loc_descr (&op0, new_loc_descr (DW_OP_shl, 0, 0));
@@ -14310,13 +14793,14 @@  mem_loc_descriptor (rtx rtl, enum machin
 	  && CONST_INT_P (XEXP (rtl, 2))
 	  && ((unsigned) INTVAL (XEXP (rtl, 1))
 	      + (unsigned) INTVAL (XEXP (rtl, 2))
-	      <= GET_MODE_BITSIZE (GET_MODE (rtl)))
-	  && GET_MODE_SIZE (GET_MODE (rtl)) <= DWARF2_ADDR_SIZE
+	      <= GET_MODE_BITSIZE (mode))
+	  && GET_MODE_CLASS (mode) == MODE_INT
+	  && GET_MODE_SIZE (mode) <= DWARF2_ADDR_SIZE
 	  && GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))) <= DWARF2_ADDR_SIZE)
 	{
 	  int shift, size;
-	  op0 = mem_loc_descriptor (XEXP (rtl, 0), mode,
-				    VAR_INIT_STATUS_INITIALIZED);
+	  op0 = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (XEXP (rtl, 0)),
+				    mem_mode, VAR_INIT_STATUS_INITIALIZED);
 	  if (op0 == 0)
 	    break;
 	  if (GET_CODE (rtl) == SIGN_EXTRACT)
@@ -14348,11 +14832,13 @@  mem_loc_descriptor (rtx rtl, enum machin
     case IF_THEN_ELSE:
       {
 	dw_loc_descr_ref op2, bra_node, drop_node;
-	op0 = mem_loc_descriptor (XEXP (rtl, 0), mode,
-				  VAR_INIT_STATUS_INITIALIZED);
-	op1 = mem_loc_descriptor (XEXP (rtl, 1), mode,
+	op0 = mem_loc_descriptor (XEXP (rtl, 0),
+				  GET_MODE (XEXP (rtl, 0)) == VOIDmode
+				  ? word_mode : GET_MODE (XEXP (rtl, 0)),
+				  mem_mode, VAR_INIT_STATUS_INITIALIZED);
+	op1 = mem_loc_descriptor (XEXP (rtl, 1), mode, mem_mode,
 				  VAR_INIT_STATUS_INITIALIZED);
-	op2 = mem_loc_descriptor (XEXP (rtl, 2), mode,
+	op2 = mem_loc_descriptor (XEXP (rtl, 2), mode, mem_mode,
 				  VAR_INIT_STATUS_INITIALIZED);
 	if (op0 == NULL || op1 == NULL || op2 == NULL)
 	  break;
@@ -14370,6 +14856,70 @@  mem_loc_descriptor (rtx rtl, enum machin
       }
       break;
 
+    case FLOAT_EXTEND:
+    case FLOAT_TRUNCATE:
+    case FLOAT:
+    case UNSIGNED_FLOAT:
+    case FIX:
+    case UNSIGNED_FIX:
+      if (!dwarf_strict)
+	{
+	  dw_die_ref type_die;
+	  dw_loc_descr_ref cvt;
+
+	  op0 = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (XEXP (rtl, 0)),
+				    mem_mode, VAR_INIT_STATUS_INITIALIZED);
+	  if (op0 == NULL)
+	    break;
+	  if (GET_MODE_CLASS (GET_MODE (XEXP (rtl, 0))) == MODE_INT
+	      && (GET_CODE (rtl) == UNSIGNED_FLOAT
+		  || GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0)))
+		     <= DWARF2_ADDR_SIZE))
+	    {
+	      type_die = base_type_for_mode (GET_MODE (XEXP (rtl, 0)),
+					     GET_CODE (rtl) == UNSIGNED_FLOAT);
+	      if (type_die == NULL)
+		break;
+	      cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0);
+	      cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+	      cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die;
+	      cvt->dw_loc_oprnd1.v.val_die_ref.external = 0;
+	      add_loc_descr (&op0, cvt);
+	    }
+	  type_die = base_type_for_mode (mode, GET_CODE (rtl) == UNSIGNED_FIX);
+	  if (type_die == NULL)
+	    break;
+	  cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0);
+	  cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+	  cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die;
+	  cvt->dw_loc_oprnd1.v.val_die_ref.external = 0;
+	  add_loc_descr (&op0, cvt);
+	  if (GET_MODE_CLASS (mode) == MODE_INT
+	      && (GET_CODE (rtl) == UNSIGNED_FIX
+		  || GET_MODE_SIZE (mode) < DWARF2_ADDR_SIZE))
+	    {
+	      enum machine_mode outer_mode = mode;
+	      if (GET_MODE_SIZE (mode) < DWARF2_ADDR_SIZE)
+		{
+		  outer_mode = mode_for_size (DWARF2_ADDR_SIZE * BITS_PER_UNIT,
+					      MODE_INT, 0);
+		  if (outer_mode == BLKmode
+		      || GET_MODE_SIZE (outer_mode) != DWARF2_ADDR_SIZE)
+		    break;
+		}
+	      type_die = base_type_for_mode (outer_mode, 0);
+	      if (type_die == NULL)
+		break;
+	      cvt = new_loc_descr (DW_OP_GNU_convert, 0, 0);
+	      cvt->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+	      cvt->dw_loc_oprnd1.v.val_die_ref.die = type_die;
+	      cvt->dw_loc_oprnd1.v.val_die_ref.external = 0;
+	      add_loc_descr (&op0, cvt);
+	    }
+	  mem_loc_result = op0;
+	}
+      break;
+
     case COMPARE:
     case ROTATE:
     case ROTATERT:
@@ -14401,12 +14951,6 @@  mem_loc_descriptor (rtx rtl, enum machin
     case UNLE:
     case UNLT:
     case LTGT:
-    case FLOAT_EXTEND:
-    case FLOAT_TRUNCATE:
-    case FLOAT:
-    case UNSIGNED_FLOAT:
-    case FIX:
-    case UNSIGNED_FIX:
     case FRACT_CONVERT:
     case UNSIGNED_FRACT_CONVERT:
     case SAT_FRACT:
@@ -14425,6 +14969,10 @@  mem_loc_descriptor (rtx rtl, enum machin
     case VEC_DUPLICATE:
     case UNSPEC:
     case HIGH:
+    case FMA:
+    case STRICT_LOW_PART:
+    case CONST_VECTOR:
+    case CONST_FIXED:
       /* If delegitimize_address couldn't do anything with the UNSPEC, we
 	 can't express it in the debug info.  This can happen e.g. with some
 	 TLS UNSPECs.  */
@@ -14562,7 +15110,10 @@  loc_descriptor (rtx rtl, enum machine_mo
 	 up an entire register.  For now, just assume that it is
 	 legitimate to make the Dwarf info refer to the whole register which
 	 contains the given subreg.  */
-      loc_result = loc_descriptor (SUBREG_REG (rtl), mode, initialized);
+      if (REG_P (SUBREG_REG (rtl)) && subreg_lowpart_p (rtl))
+	loc_result = loc_descriptor (SUBREG_REG (rtl), mode, initialized);
+      else
+	goto do_default;
       break;
 
     case REG:
@@ -14570,8 +15121,8 @@  loc_descriptor (rtx rtl, enum machine_mo
       break;
 
     case MEM:
-      loc_result = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (rtl),
-				       initialized);
+      loc_result = mem_loc_descriptor (XEXP (rtl, 0), get_address_mode (rtl),
+				       GET_MODE (rtl), initialized);
       if (loc_result == NULL)
 	loc_result = tls_mem_loc_descriptor (rtl);
       if (loc_result == NULL)
@@ -14766,13 +15317,15 @@  loc_descriptor (rtx rtl, enum machine_mo
 	  break;
 	}
       /* FALLTHRU */
+    do_default:
     default:
-      if (GET_MODE_CLASS (mode) == MODE_INT && GET_MODE (rtl) == mode
-	  && GET_MODE_SIZE (GET_MODE (rtl)) <= DWARF2_ADDR_SIZE
-	  && (dwarf_version >= 4 || !dwarf_strict))
+      if ((GET_MODE_CLASS (mode) == MODE_INT && GET_MODE (rtl) == mode
+	   && GET_MODE_SIZE (GET_MODE (rtl)) <= DWARF2_ADDR_SIZE
+	   && dwarf_version >= 4)
+	  || (!dwarf_strict && mode != VOIDmode && mode != BLKmode))
 	{
 	  /* Value expression.  */
-	  loc_result = mem_loc_descriptor (rtl, VOIDmode, initialized);
+	  loc_result = mem_loc_descriptor (rtl, mode, VOIDmode, initialized);
 	  if (loc_result)
 	    add_loc_descr (&loc_result,
 			   new_loc_descr (DW_OP_stack_value, 0, 0));
@@ -14847,18 +15400,20 @@  dw_loc_list_1 (tree loc, rtx varloc, int
 	  if (MEM_P (varloc))
 	    {
 	      rtx addr = XEXP (varloc, 0);
-	      descr = mem_loc_descriptor (addr, mode, initialized);
+	      descr = mem_loc_descriptor (addr, get_address_mode (varloc),
+					  mode, initialized);
 	      if (descr)
 		have_address = 1;
 	      else
 		{
 		  rtx x = avoid_constant_pool_reference (varloc);
 		  if (x != varloc)
-		    descr = mem_loc_descriptor (x, mode, initialized);
+		    descr = mem_loc_descriptor (x, mode, VOIDmode,
+						initialized);
 		}
 	    }
 	  else
-	    descr = mem_loc_descriptor (varloc, mode, initialized);
+	    descr = mem_loc_descriptor (varloc, mode, VOIDmode, initialized);
 	}
       else
 	return 0;
@@ -15242,7 +15797,6 @@  cst_pool_loc_descr (tree loc)
 {
   /* Get an RTL for this, if something has been emitted.  */
   rtx rtl = lookup_constant_def (loc);
-  enum machine_mode mode;
 
   if (!rtl || !MEM_P (rtl))
     {
@@ -15260,9 +15814,8 @@  cst_pool_loc_descr (tree loc)
 			"CST value in contant pool but not marked.");
       return 0;
     }
-  mode = GET_MODE (rtl);
-  rtl = XEXP (rtl, 0);
-  return mem_loc_descriptor (rtl, mode, VAR_INIT_STATUS_INITIALIZED);
+  return mem_loc_descriptor (XEXP (rtl, 0), get_address_mode (rtl),
+			     GET_MODE (rtl), VAR_INIT_STATUS_INITIALIZED);
 }
 
 /* Return dw_loc_list representing address of addr_expr LOC
@@ -15507,7 +16060,7 @@  loc_list_from_tree (tree loc, int want_a
 	  }
 	else
 	  {
-	    enum machine_mode mode;
+	    enum machine_mode mode, mem_mode;
 
 	    /* Certain constructs can only be represented at top-level.  */
 	    if (want_address == 2)
@@ -15519,12 +16072,16 @@  loc_list_from_tree (tree loc, int want_a
 	    else
 	      {
 		mode = GET_MODE (rtl);
+		mem_mode = VOIDmode;
 		if (MEM_P (rtl))
 		  {
+		    mem_mode = mode;
+		    mode = get_address_mode (rtl);
 		    rtl = XEXP (rtl, 0);
 		    have_address = 1;
 		  }
-		ret = mem_loc_descriptor (rtl, mode, VAR_INIT_STATUS_INITIALIZED);
+		ret = mem_loc_descriptor (rtl, mode, mem_mode,
+					  VAR_INIT_STATUS_INITIALIZED);
 	      }
 	    if (!ret)
 	      expansion_failed (loc, rtl,
@@ -19497,8 +20054,7 @@  gen_subprogram_die (tree decl, dw_die_re
 		      if (mode == VOIDmode)
 			mode = GET_MODE (XEXP (arg, 0));
 		    }
-		  if (GET_MODE_CLASS (mode) != MODE_INT
-		      || GET_MODE_SIZE (mode) > DWARF2_ADDR_SIZE)
+		  if (mode == VOIDmode || mode == BLKmode)
 		    continue;
 		  if (XEXP (XEXP (arg, 0), 0) == pc_rtx)
 		    {
@@ -19517,14 +20073,19 @@  gen_subprogram_die (tree decl, dw_die_re
 		    reg = reg_loc_descriptor (XEXP (XEXP (arg, 0), 0),
 					      VAR_INIT_STATUS_INITIALIZED);
 		  else if (MEM_P (XEXP (XEXP (arg, 0), 0)))
-		    reg = mem_loc_descriptor (XEXP (XEXP (XEXP (arg, 0),
-							  0), 0), mode,
-					      VAR_INIT_STATUS_INITIALIZED);
+		    {
+		      rtx mem = XEXP (XEXP (arg, 0), 0);
+		      reg = mem_loc_descriptor (XEXP (mem, 0),
+						get_address_mode (mem),
+						GET_MODE (mem),
+						VAR_INIT_STATUS_INITIALIZED);
+		    }
 		  else
 		    continue;
 		  if (reg == NULL)
 		    continue;
-		  val = mem_loc_descriptor (XEXP (XEXP (arg, 0), 1), VOIDmode,
+		  val = mem_loc_descriptor (XEXP (XEXP (arg, 0), 1), mode,
+					    VOIDmode,
 					    VAR_INIT_STATUS_INITIALIZED);
 		  if (val == NULL)
 		    continue;
@@ -19536,8 +20097,12 @@  gen_subprogram_die (tree decl, dw_die_re
 		  add_AT_loc (cdie, DW_AT_GNU_call_site_value, val);
 		  if (next_arg != XEXP (arg, 1))
 		    {
+		      mode = GET_MODE (XEXP (XEXP (XEXP (arg, 1), 0), 1));
+		      if (mode == VOIDmode)
+			mode = GET_MODE (XEXP (XEXP (XEXP (arg, 1), 0), 0));
 		      val = mem_loc_descriptor (XEXP (XEXP (XEXP (arg, 1),
-							    0), 1), VOIDmode,
+							    0), 1),
+						mode, VOIDmode,
 						VAR_INIT_STATUS_INITIALIZED);
 		      if (val != NULL)
 			add_AT_loc (cdie, DW_AT_GNU_call_site_data_value, val);
@@ -19551,13 +20116,19 @@  gen_subprogram_die (tree decl, dw_die_re
 		  dw_loc_descr_ref tval = NULL;
 
 		  if (tloc != NULL_RTX)
-		    tval = mem_loc_descriptor (tloc, VOIDmode,
+		    tval = mem_loc_descriptor (tloc,
+					       GET_MODE (tloc) == VOIDmode
+					       ? Pmode : GET_MODE (tloc),
+					       VOIDmode,
 					       VAR_INIT_STATUS_INITIALIZED);
 		  if (tval)
 		    add_AT_loc (die, DW_AT_GNU_call_site_target, tval);
 		  else if (tlocc != NULL_RTX)
 		    {
-		      tval = mem_loc_descriptor (tlocc, VOIDmode,
+		      tval = mem_loc_descriptor (tlocc,
+						 GET_MODE (tlocc) == VOIDmode
+						 ? Pmode : GET_MODE (tlocc),
+						 VOIDmode,
 						 VAR_INIT_STATUS_INITIALIZED);
 		      if (tval)
 			add_AT_loc (die, DW_AT_GNU_call_site_target_clobbered,
@@ -22727,6 +23298,7 @@  prune_unused_types (void)
   limbo_die_node *node;
   comdat_type_node *ctnode;
   pubname_ref pub;
+  dw_die_ref base_type;
 
 #if ENABLE_ASSERT_CHECKING
   /* All the marks should already be clear.  */
@@ -22756,6 +23328,8 @@  prune_unused_types (void)
     prune_unused_types_mark (pub->die, 1);
   for (i = 0; i < arange_table_in_use; i++)
     prune_unused_types_mark (arange_table[i], 1);
+  for (i = 0; VEC_iterate (dw_die_ref, base_types, i, base_type); i++)
+    prune_unused_types_mark (base_type, 1);
 
   /* Get rid of nodes that aren't marked; and update the string counts.  */
   if (debug_str_hash && debug_str_hash_forced)
@@ -22844,6 +23418,117 @@  move_linkage_attr (dw_die_ref die)
     }
 }
 
+/* Helper function for resolve_addr, mark DW_TAG_base_type nodes
+   referenced from typed stack ops and count how often they are used.  */
+
+static void
+mark_base_types (dw_loc_descr_ref loc)
+{
+  dw_die_ref base_type = NULL;
+
+  for (; loc; loc = loc->dw_loc_next)
+    {
+      switch (loc->dw_loc_opc)
+	{
+	case DW_OP_GNU_regval_type:
+	case DW_OP_GNU_deref_type:
+	  base_type = loc->dw_loc_oprnd2.v.val_die_ref.die;
+	  break;
+	case DW_OP_GNU_const_type:
+	case DW_OP_GNU_convert:
+	case DW_OP_GNU_reinterpret:
+	  base_type = loc->dw_loc_oprnd1.v.val_die_ref.die;
+	  break;
+	case DW_OP_GNU_entry_value:
+	  mark_base_types (loc->dw_loc_oprnd1.v.val_loc);
+	  continue;
+	default:
+	  continue;
+	}
+      gcc_assert (base_type->die_parent == comp_unit_die ());
+      if (base_type->die_mark)
+	base_type->die_mark++;
+      else
+	{
+	  VEC_safe_push (dw_die_ref, heap, base_types, base_type);
+	  base_type->die_mark = 1;
+	}
+    }
+}
+
+/* Comparison function for sorting marked base types.  */
+
+static int
+base_type_cmp (const void *x, const void *y)
+{
+  dw_die_ref dx = *(const dw_die_ref *) x;
+  dw_die_ref dy = *(const dw_die_ref *) y;
+  unsigned int byte_size1, byte_size2;
+  unsigned int encoding1, encoding2;
+  if (dx->die_mark > dy->die_mark)
+    return -1;
+  if (dx->die_mark < dy->die_mark)
+    return 1;
+  byte_size1 = get_AT_unsigned (dx, DW_AT_byte_size);
+  byte_size2 = get_AT_unsigned (dy, DW_AT_byte_size);
+  if (byte_size1 < byte_size2)
+    return 1;
+  if (byte_size1 > byte_size2)
+    return -1;
+  encoding1 = get_AT_unsigned (dx, DW_AT_encoding);
+  encoding2 = get_AT_unsigned (dy, DW_AT_encoding);
+  if (encoding1 < encoding2)
+    return 1;
+  if (encoding1 > encoding2)
+    return -1;
+  return 0;
+}
+
+/* Move base types marked by mark_base_types as early as possible
+   in the CU, sorted by decreasing usage count both to make the
+   uleb128 references as small as possible and to make sure they
+   will have die_offset already computed by calc_die_sizes when
+   sizes of typed stack loc ops is computed.  */
+
+static void
+move_marked_base_types (void)
+{
+  unsigned int i;
+  dw_die_ref base_type, die, c;
+
+  if (VEC_empty (dw_die_ref, base_types))
+    return;
+
+  /* Sort by increasing usage count, as when readding the last
+     vector entry will be the first child.  */
+  VEC_qsort (dw_die_ref, base_types, base_type_cmp);
+  die = comp_unit_die ();
+  c = die->die_child;
+  do
+    {
+      dw_die_ref prev = c;
+      c = c->die_sib;
+      while (c->die_mark)
+	{
+	  remove_child_with_prev (c, prev);
+	  /* As base types got marked, there must be at least
+	     one node other than DW_TAG_base_type.  */
+	  gcc_assert (c != c->die_sib);
+	  c = c->die_sib;
+	}
+    }
+  while (c != die->die_child);
+  gcc_assert (die->die_child);
+  c = die->die_child;
+  for (i = 0; VEC_iterate (dw_die_ref, base_types, i, base_type); i++)
+    {
+      base_type->die_mark = 0;
+      base_type->die_sib = c->die_sib;
+      c->die_sib = base_type;
+      c = base_type;
+    }
+}
+
 /* Helper function for resolve_addr, attempt to resolve
    one CONST_STRING, return non-zero if not successful.  Similarly verify that
    SYMBOL_REFs refer to variables emitted in the current CU.  */
@@ -22949,7 +23634,10 @@  resolve_addr (dw_die_ref die)
 		*curr = next;
 	      }
 	    else
-	      curr = &(*curr)->dw_loc_next;
+	      {
+		mark_base_types ((*curr)->expr);
+		curr = &(*curr)->dw_loc_next;
+	      }
 	  }
 	if (!AT_loc_list (a))
 	  {
@@ -22963,6 +23651,8 @@  resolve_addr (dw_die_ref die)
 	    remove_AT (die, a->dw_attr);
 	    ix--;
 	  }
+	else
+	  mark_base_types (AT_loc (a));
 	break;
       case dw_val_class_addr:
 	if (a->dw_attr == DW_AT_const_value
@@ -23128,6 +23818,56 @@  hash_loc_operands (dw_loc_descr_ref loc,
     case DW_OP_GNU_entry_value:
       hash = hash_loc_operands (val1->v.val_loc, hash);
       break;
+    case DW_OP_GNU_regval_type:
+    case DW_OP_GNU_deref_type:
+      {
+	unsigned int byte_size
+	  = get_AT_unsigned (val2->v.val_die_ref.die, DW_AT_byte_size);
+	unsigned int encoding
+	  = get_AT_unsigned (val2->v.val_die_ref.die, DW_AT_encoding);
+	hash = iterative_hash_object (val1->v.val_int, hash);
+	hash = iterative_hash_object (byte_size, hash);
+	hash = iterative_hash_object (encoding, hash);
+      }
+      break;
+    case DW_OP_GNU_convert:
+    case DW_OP_GNU_reinterpret:
+    case DW_OP_GNU_const_type:
+      {
+	unsigned int byte_size
+	  = get_AT_unsigned (val1->v.val_die_ref.die, DW_AT_byte_size);
+	unsigned int encoding
+	  = get_AT_unsigned (val1->v.val_die_ref.die, DW_AT_encoding);
+	hash = iterative_hash_object (byte_size, hash);
+	hash = iterative_hash_object (encoding, hash);
+	if (loc->dw_loc_opc != DW_OP_GNU_const_type)
+	  break;
+	hash = iterative_hash_object (val2->val_class, hash);
+	switch (val2->val_class)
+	  {
+	  case dw_val_class_const:
+	    hash = iterative_hash_object (val2->v.val_int, hash);
+	    break;
+	  case dw_val_class_vec:
+	    {
+	      unsigned int elt_size = val2->v.val_vec.elt_size;
+	      unsigned int len = val2->v.val_vec.length;
+
+	      hash = iterative_hash_object (elt_size, hash);
+	      hash = iterative_hash_object (len, hash);
+	      hash = iterative_hash (val2->v.val_vec.array,
+				     len * elt_size, hash);
+	    }
+	    break;
+	  case dw_val_class_const_double:
+	    hash = iterative_hash_object (val2->v.val_double.low, hash);
+	    hash = iterative_hash_object (val2->v.val_double.high, hash);
+	    break;
+	  default:
+	    gcc_unreachable ();
+	  }
+      }
+      break;
 
     default:
       /* Other codes have no operands.  */
@@ -23287,6 +24027,33 @@  compare_loc_operands (dw_loc_descr_ref x
 	     && valx2->v.val_int == valy2->v.val_int;
     case DW_OP_GNU_entry_value:
       return compare_loc_operands (valx1->v.val_loc, valy1->v.val_loc);
+    case DW_OP_GNU_const_type:
+      if (valx1->v.val_die_ref.die != valy1->v.val_die_ref.die
+	  || valx2->val_class != valy2->val_class)
+	return false;
+      switch (valx2->val_class)
+	{
+	case dw_val_class_const:
+	  return valx2->v.val_int == valy2->v.val_int;
+	case dw_val_class_vec:
+	  return valx2->v.val_vec.elt_size == valy2->v.val_vec.elt_size
+		 && valx2->v.val_vec.length == valy2->v.val_vec.length
+		 && memcmp (valx2->v.val_vec.array, valy2->v.val_vec.array,
+			    valx2->v.val_vec.elt_size
+			    * valx2->v.val_vec.length) == 0;
+	case dw_val_class_const_double:
+	  return valx2->v.val_double.low == valy2->v.val_double.low
+		 && valx2->v.val_double.high == valy2->v.val_double.high;
+	default:
+	  gcc_unreachable ();
+	}
+    case DW_OP_GNU_regval_type:
+    case DW_OP_GNU_deref_type:
+      return valx1->v.val_int == valy1->v.val_int
+	     && valx2->v.val_die_ref.die == valy2->v.val_die_ref.die;
+    case DW_OP_GNU_convert:
+    case DW_OP_GNU_reinterpret:
+      return valx1->v.val_die_ref.die == valy1->v.val_die_ref.die;
     default:
       /* Other codes have no operands.  */
       return true;
@@ -23467,7 +24234,14 @@  dwarf2out_finish (const char *filename)
 
   limbo_die_list = NULL;
 
+#if ENABLE_ASSERT_CHECKING
+  {
+    dw_die_ref die = comp_unit_die (), c;
+    FOR_EACH_CHILD (die, c, gcc_assert (! c->die_mark));
+  }
+#endif
   resolve_addr (comp_unit_die ());
+  move_marked_base_types ();
 
   for (node = deferred_asm_name; node; node = node->next)
     {
--- gcc/cfgexpand.c.jj	2011-03-23 09:34:13.000000000 +0100
+++ gcc/cfgexpand.c	2011-03-24 17:33:08.000000000 +0100
@@ -2789,7 +2789,7 @@  expand_debug_expr (tree exp)
       return gen_rtx_NOT (mode, op0);
 
     case FLOAT_EXPR:
-      if (unsignedp)
+      if (TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))))
 	return gen_rtx_UNSIGNED_FLOAT (mode, op0);
       else
 	return gen_rtx_FLOAT (mode, op0);