dwarf2.h (DW_OP_GNU_const_type, [...]): New.
authorJakub Jelinek <jakub@redhat.com>
Sat, 30 Apr 2011 10:06:18 +0000 (12:06 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Sat, 30 Apr 2011 10:06:18 +0000 (12:06 +0200)
* 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): Add 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.

From-SVN: r173210

gcc/ChangeLog
gcc/dwarf2out.c
include/ChangeLog
include/dwarf2.h

index ee2340471ba2508f1d7b63fc66e959180989a115..0655cf644a76954d16559d336757ccd51a8acee3 100644 (file)
@@ -1,5 +1,37 @@
 2011-04-30  Jakub Jelinek  <jakub@redhat.com>
 
+       * dwarf2out.c (get_address_mode): New inline.
+       (mem_loc_descriptor): Add 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.
+
        PR tree-optimization/48809
        * tree-switch-conversion.c (build_arrays): Compute tidx in unsigned
        type.
index fb1dd9c77826914343ee13fa27d902efbdd1efb9..83653d2984e9bd72c0601c4be26b07474dc9e435 100644 (file)
@@ -482,7 +482,8 @@ static struct dw_loc_descr_struct *build_cfa_aligned_loc
   (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
@@ -2053,6 +2054,17 @@ dwarf2out_frame_debug_cfa_register (rtx set, const char *label)
   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
@@ -2073,8 +2085,8 @@ dwarf2out_frame_debug_cfa_expression (rtx set, const char *label)
   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.  */
@@ -4785,6 +4797,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>";
@@ -4892,6 +4914,7 @@ loc_list_plus_const (dw_loc_list_ref list_head, HOST_WIDE_INT offset)
   (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.  */
 
@@ -5014,6 +5037,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;
     }
@@ -5306,6 +5373,96 @@ output_loc_operands (dw_loc_descr_ref loc, int for_eh_or_skip)
       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 = HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR;
+
+             dw2_asm_output_data (1, 2 * 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 (l, first, NULL);
+             dw2_asm_output_data (l, 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);
+       gcc_assert (o);
+       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));
+         }
+       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;
@@ -5483,6 +5640,11 @@ output_loc_operands_raw (dw_loc_descr_ref loc)
 
     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;
 
@@ -6310,6 +6472,8 @@ static GTY(()) VEC(tree,gc) *generic_type_instances;
    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);
@@ -6442,6 +6606,7 @@ static void output_location_lists (dw_die_ref);
 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);
@@ -6780,6 +6945,21 @@ get_ref_die_offset_label (char *label, dw_die_ref ref)
   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 *
@@ -10811,6 +10991,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);
 
@@ -10821,6 +11003,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
@@ -13168,7 +13380,8 @@ reg_loc_descriptor (rtx rtl, enum var_init_status initialized)
 
       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));
@@ -13596,6 +13809,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
@@ -13606,22 +13846,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
@@ -13629,12 +13872,15 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
 
   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)
@@ -13644,15 +13890,72 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
         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
@@ -13682,11 +13985,12 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
 
     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)));
@@ -13701,37 +14005,75 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
          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;
 
     case LO_SUM:
-      return mem_loc_descriptor (XEXP (rtl, 1), mode, initialized);
+      return mem_loc_descriptor (XEXP (rtl, 1), mode, mem_mode, initialized);
 
     case LABEL_REF:
       /* Some ports can transform a symbol ref into a label ref, because
@@ -13739,6 +14081,9 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
         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)
        {
@@ -13784,23 +14129,32 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
     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:
@@ -13813,32 +14167,35 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
     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;
@@ -13869,15 +14226,31 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
 
     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;
@@ -13892,9 +14265,9 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
       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)
@@ -13906,9 +14279,9 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
       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)
@@ -13936,7 +14309,7 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
       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)
@@ -13947,7 +14320,75 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
       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:
@@ -13975,74 +14416,75 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
       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;
@@ -14073,87 +14515,111 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
       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)
@@ -14164,26 +14630,44 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
       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));
@@ -14217,13 +14701,14 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
          && 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)
@@ -14255,11 +14740,13 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
     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;
@@ -14277,6 +14764,70 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
       }
       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:
@@ -14308,12 +14859,6 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
     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:
@@ -14332,6 +14877,10 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
     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.  */
@@ -14469,7 +15018,10 @@ loc_descriptor (rtx rtl, enum machine_mode mode,
         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:
@@ -14477,8 +15029,8 @@ loc_descriptor (rtx rtl, enum machine_mode mode,
       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)
@@ -14673,13 +15225,15 @@ loc_descriptor (rtx rtl, enum machine_mode mode,
          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));
@@ -14754,18 +15308,20 @@ dw_loc_list_1 (tree loc, rtx varloc, int want_address,
          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;
@@ -15187,7 +15743,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))
     {
@@ -15205,9 +15760,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
@@ -15452,7 +16006,7 @@ loc_list_from_tree (tree loc, int want_address)
          }
        else
          {
-           enum machine_mode mode;
+           enum machine_mode mode, mem_mode;
 
            /* Certain constructs can only be represented at top-level.  */
            if (want_address == 2)
@@ -15464,12 +16018,16 @@ loc_list_from_tree (tree loc, int want_address)
            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,
@@ -19513,8 +20071,7 @@ gen_subprogram_die (tree decl, dw_die_ref context_die)
                      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)
                    {
@@ -19533,14 +20090,19 @@ gen_subprogram_die (tree decl, dw_die_ref context_die)
                    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;
@@ -19552,8 +20114,12 @@ gen_subprogram_die (tree decl, dw_die_ref context_die)
                  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);
@@ -19567,13 +20133,19 @@ gen_subprogram_die (tree decl, dw_die_ref context_die)
                  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,
@@ -22838,6 +23410,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.  */
@@ -22865,6 +23438,8 @@ prune_unused_types (void)
      pubname_table.  */
   FOR_EACH_VEC_ELT (pubname_entry, pubname_table, i, pub)
     prune_unused_types_mark (pub->die, 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)
@@ -22953,6 +23528,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 decreasing usage count, they will be added again in that
+     order later on.  */
+  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.  */
@@ -23073,7 +23759,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 (loc == *start)
              loc->resolved_addr = 1;
@@ -23095,6 +23784,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
@@ -23262,6 +23953,56 @@ hash_loc_operands (dw_loc_descr_ref loc, hashval_t hash)
     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.  */
@@ -23421,6 +24162,33 @@ compare_loc_operands (dw_loc_descr_ref x, dw_loc_descr_ref y)
             && 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;
@@ -23602,7 +24370,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)
     {
index fcabfb8384c0c05e3c0fdd7aa35e14a12a2cca68..7ad246e44a700f6dc7338917901a6d86231bc1e7 100644 (file)
@@ -1,3 +1,8 @@
+2011-04-30  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.
+
 2011-04-25  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
        * ansidecl.h (ENUM_BITFIELD): New, from gcc/system.h.
index 44b4328de5a3c9b7484ea76d884db9524bd58d8a..0b008661f964d672de61eb38dad8003e46718825 100644 (file)
@@ -556,6 +556,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.dwarfstd.org/doc/040408.1.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 = 0xf9,
     /* HP extensions.  */
     DW_OP_HP_unknown     = 0xe0, /* Ouch, the same as GNU_push_tls_address.  */
     DW_OP_HP_is_value    = 0xe1,