[RFC PATCH] Typed DWARF stack

Jakub Jelinek jakub@redhat.com
Fri Mar 25 11:33:00 GMT 2011


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
-------------- next part --------------
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.

--- 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);
-------------- next part --------------
/* { 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;
}


More information about the Gcc-patches mailing list