OSDN Git Service

PR middle-end/43125
[pf3gnuchains/gcc-fork.git] / gcc / dwarf2out.c
index 3e3cf31..2e8712f 100644 (file)
@@ -1,6 +1,7 @@
 /* Output Dwarf2 format symbol table information from GCC.
    Copyright (C) 1992, 1993, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-   2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+   Free Software Foundation, Inc.
    Contributed by Gary Funck (gary@intrepid.com).
    Derived from DWARF 1 implementation of Ron Guilmette (rfg@monkeys.com).
    Extensively modified by Jason Merrill (jason@cygnus.com).
@@ -5983,7 +5984,7 @@ static hashval_t decl_loc_table_hash (const void *);
 static int decl_loc_table_eq (const void *, const void *);
 static var_loc_list *lookup_decl_loc (const_tree);
 static void equate_decl_number_to_die (tree, dw_die_ref);
-static void add_var_loc_to_decl (tree, struct var_loc_node *);
+static struct var_loc_node *add_var_loc_to_decl (tree, rtx);
 static void print_spaces (FILE *);
 static void print_die (dw_die_ref, FILE *);
 static void print_dwarf_line_table (FILE *);
@@ -6062,7 +6063,8 @@ static void add_arange (tree, dw_die_ref);
 static void output_aranges (void);
 static unsigned int add_ranges_num (int);
 static unsigned int add_ranges (const_tree);
-static unsigned int add_ranges_by_labels (const char *, const char *);
+static void add_ranges_by_labels (dw_die_ref, const char *, const char *,
+                                 bool *);
 static void output_ranges (void);
 static void output_line_info (void);
 static void output_file_names (void);
@@ -6177,10 +6179,7 @@ static void gen_generic_params_dies (tree);
 static void splice_child_die (dw_die_ref, dw_die_ref);
 static int file_info_cmp (const void *, const void *);
 static dw_loc_list_ref new_loc_list (dw_loc_descr_ref, const char *,
-                                    const char *, const char *, unsigned);
-static void add_loc_descr_to_loc_list (dw_loc_list_ref *, dw_loc_descr_ref,
-                                      const char *, const char *,
-                                      const char *);
+                                    const char *, const char *);
 static void output_loc_list (dw_loc_list_ref);
 static char *gen_internal_sym (const char *);
 
@@ -7214,6 +7213,13 @@ AT_loc_list (dw_attr_ref a)
   return a->dw_attr_val.v.val_loc_list;
 }
 
+static inline dw_loc_list_ref *
+AT_loc_list_ptr (dw_attr_ref a)
+{
+  gcc_assert (a && AT_class (a) == dw_val_class_loc_list);
+  return &a->dw_attr_val.v.val_loc_list;
+}
+
 /* Add an address constant attribute value to a DIE.  */
 
 static inline void
@@ -7757,12 +7763,13 @@ equate_decl_number_to_die (tree decl, dw_die_ref decl_die)
 
 /* Add a variable location node to the linked list for DECL.  */
 
-static void
-add_var_loc_to_decl (tree decl, struct var_loc_node *loc)
+static struct var_loc_node *
+add_var_loc_to_decl (tree decl, rtx loc_note)
 {
   unsigned int decl_id = DECL_UID (decl);
   var_loc_list *temp;
   void **slot;
+  struct var_loc_node *loc = NULL;
 
   slot = htab_find_slot_with_hash (decl_loc_table, decl, decl_id, INSERT);
   if (*slot == NULL)
@@ -7780,25 +7787,27 @@ add_var_loc_to_decl (tree decl, struct var_loc_node *loc)
         and either both or neither of the locations is uninitialized,
         we have nothing to do.  */
       if ((!rtx_equal_p (NOTE_VAR_LOCATION_LOC (temp->last->var_loc_note),
-                        NOTE_VAR_LOCATION_LOC (loc->var_loc_note)))
+                        NOTE_VAR_LOCATION_LOC (loc_note)))
          || ((NOTE_VAR_LOCATION_STATUS (temp->last->var_loc_note)
-              != NOTE_VAR_LOCATION_STATUS (loc->var_loc_note))
+              != NOTE_VAR_LOCATION_STATUS (loc_note))
              && ((NOTE_VAR_LOCATION_STATUS (temp->last->var_loc_note)
                   == VAR_INIT_STATUS_UNINITIALIZED)
-                 || (NOTE_VAR_LOCATION_STATUS (loc->var_loc_note)
+                 || (NOTE_VAR_LOCATION_STATUS (loc_note)
                      == VAR_INIT_STATUS_UNINITIALIZED))))
        {
          /* Add LOC to the end of list and update LAST.  */
+         loc = GGC_CNEW (struct var_loc_node);
          temp->last->next = loc;
          temp->last = loc;
        }
     }
-  /* Do not add empty location to the beginning of the list.  */
-  else if (NOTE_VAR_LOCATION_LOC (loc->var_loc_note) != NULL_RTX)
+  else
     {
+      loc = GGC_CNEW (struct var_loc_node);
       temp->first = loc;
       temp->last = loc;
     }
+  return loc;
 }
 \f
 /* Keep track of the number of spaces used to indent the
@@ -10302,12 +10311,11 @@ output_die_symbol (dw_die_ref die)
 }
 
 /* Return a new location list, given the begin and end range, and the
-   expression. gensym tells us whether to generate a new internal symbol for
-   this location list node, which is done for the head of the list only.  */
+   expression.  */
 
 static inline dw_loc_list_ref
 new_loc_list (dw_loc_descr_ref expr, const char *begin, const char *end,
-             const char *section, unsigned int gensym)
+             const char *section)
 {
   dw_loc_list_ref retlist = GGC_CNEW (dw_loc_list_node);
 
@@ -10315,27 +10323,18 @@ new_loc_list (dw_loc_descr_ref expr, const char *begin, const char *end,
   retlist->end = end;
   retlist->expr = expr;
   retlist->section = section;
-  if (gensym)
-    retlist->ll_symbol = gen_internal_sym ("LLST");
 
   return retlist;
 }
 
-/* Add a location description expression to a location list.  */
+/* Generate a new internal symbol for this location list node, if it
+   hasn't got one yet.  */
 
 static inline void
-add_loc_descr_to_loc_list (dw_loc_list_ref *list_head, dw_loc_descr_ref descr,
-                          const char *begin, const char *end,
-                          const char *section)
+gen_llsym (dw_loc_list_ref list)
 {
-  dw_loc_list_ref *d;
-
-  /* Find the end of the chain.  */
-  for (d = list_head; (*d) != NULL; d = &(*d)->dw_loc_next)
-    ;
-
-  /* Add a new location list node to the list.  */
-  *d = new_loc_list (descr, begin, end, section, 0);
+  gcc_assert (!list->ll_symbol);
+  list->ll_symbol = gen_internal_sym ("LLST");
 }
 
 /* Output the location list given to us.  */
@@ -11028,10 +11027,12 @@ add_ranges (const_tree block)
 /* Add a new entry to .debug_ranges corresponding to a pair of
    labels.  */
 
-static unsigned int
-add_ranges_by_labels (const char *begin, const char *end)
+static void
+add_ranges_by_labels (dw_die_ref die, const char *begin, const char *end,
+                     bool *added)
 {
   unsigned int in_use = ranges_by_label_in_use;
+  unsigned int offset;
 
   if (in_use == ranges_by_label_allocated)
     {
@@ -11048,7 +11049,12 @@ add_ranges_by_labels (const char *begin, const char *end)
   ranges_by_label[in_use].end = end;
   ranges_by_label_in_use = in_use + 1;
 
-  return add_ranges_num (-(int)in_use - 1);
+  offset = add_ranges_num (-(int)in_use - 1);
+  if (!*added)
+    {
+      add_AT_range_list (die, DW_AT_ranges, offset);
+      *added = true;
+    }
 }
 
 static void
@@ -12989,7 +12995,25 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
       if (mem_loc_result == NULL)
        mem_loc_result = tls_mem_loc_descriptor (rtl);
       if (mem_loc_result != 0)
-       add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_deref, 0, 0));
+       {
+         if (GET_MODE_SIZE (GET_MODE (rtl)) > DWARF2_ADDR_SIZE)
+           {
+             expansion_failed (NULL_TREE, rtl, "DWARF address size mismatch");
+             return 0;
+           }
+         else if (GET_MODE_SIZE (GET_MODE (rtl)) == 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));
+       }
+      else
+       {
+         rtx new_rtl = avoid_constant_pool_reference (rtl);
+         if (new_rtl != rtl)
+           return mem_loc_descriptor (new_rtl, mode, initialized);
+       }
       break;
 
     case LO_SUM:
@@ -13003,34 +13027,6 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
         pool.  */
     case CONST:
     case SYMBOL_REF:
-      /* Alternatively, the symbol in the constant pool might be referenced
-        by a different symbol.  */
-      if (GET_CODE (rtl) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (rtl))
-       {
-         bool marked;
-         rtx tmp = get_pool_constant_mark (rtl, &marked);
-
-         if (GET_CODE (tmp) == SYMBOL_REF)
-           {
-             rtl = tmp;
-             if (CONSTANT_POOL_ADDRESS_P (tmp))
-               get_pool_constant_mark (tmp, &marked);
-             else
-               marked = true;
-           }
-
-         /* If all references to this pool constant were optimized away,
-            it was not output and thus we can't represent it.
-            FIXME: might try to use DW_OP_const_value here, though
-            DW_OP_piece complicates it.  */
-         if (!marked)
-           {
-             expansion_failed (NULL_TREE, rtl,
-                               "Constant was removed from constant pool.\n");
-             return 0;
-           }
-       }
-
       if (GET_CODE (rtl) == SYMBOL_REF
          && SYMBOL_REF_TLS_MODEL (rtl) != TLS_MODEL_NONE)
        {
@@ -13128,7 +13124,7 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
       op = DW_OP_div;
       goto do_binop;
 
-    case MOD:
+    case UMOD:
       op = DW_OP_mod;
       goto do_binop;
 
@@ -13170,6 +13166,24 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
       add_loc_descr (&mem_loc_result, new_loc_descr (op, 0, 0));
       break;
 
+    case MOD:
+      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 (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 (DW_OP_over, 0, 0));
+      add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_over, 0, 0));
+      add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_div, 0, 0));
+      add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_mul, 0, 0));
+      add_loc_descr (&mem_loc_result, new_loc_descr (DW_OP_minus, 0, 0));
+      break;
+
     case NOT:
       op = DW_OP_not;
       goto do_unop;
@@ -13222,32 +13236,72 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
       goto do_scompare;
 
     do_scompare:
-      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_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));
 
-      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 (op_mode == VOIDmode)
+           op_mode = GET_MODE (XEXP (rtl, 1));
+         if (op_mode != VOIDmode && GET_MODE_CLASS (op_mode) != MODE_INT)
+           break;
 
-      if (op0 == 0 || op1 == 0)
-       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 (GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))) < DWARF2_ADDR_SIZE)
-       {
-         int shift = DWARF2_ADDR_SIZE
-                     - GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0)));
-         shift *= BITS_PER_UNIT;
-         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
+         if (op0 == 0 || op1 == 0)
+           break;
+
+         if (op_mode != VOIDmode
+             && GET_MODE_SIZE (op_mode) < DWARF2_ADDR_SIZE)
            {
-             add_loc_descr (&op1, int_loc_descriptor (shift));
-             add_loc_descr (&op1, new_loc_descr (DW_OP_shl, 0, 0));
+             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));
+               }
            }
        }
 
@@ -13280,42 +13334,72 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
       goto do_ucompare;
 
     do_ucompare:
-      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_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));
 
-      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 (op_mode == VOIDmode)
+           op_mode = GET_MODE (XEXP (rtl, 1));
+         if (op_mode != VOIDmode && GET_MODE_CLASS (op_mode) != MODE_INT)
+           break;
 
-      if (op0 == 0 || op1 == 0)
-       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 (GET_MODE_SIZE (GET_MODE (XEXP (rtl, 0))) < DWARF2_ADDR_SIZE)
-       {
-         HOST_WIDE_INT mask = GET_MODE_MASK (GET_MODE (XEXP (rtl, 0)));
-         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);
-         else
+         if (op0 == 0 || op1 == 0)
+           break;
+
+         if (op_mode != VOIDmode
+             && GET_MODE_SIZE (op_mode) < DWARF2_ADDR_SIZE)
            {
-             add_loc_descr (&op1, int_loc_descriptor (mask));
-             add_loc_descr (&op1, new_loc_descr (DW_OP_and, 0, 0));
+             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));
+           {
+             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));
+           }
        }
       goto do_compare;
 
@@ -13441,8 +13525,18 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
     case US_MULT:
     case SS_DIV:
     case US_DIV:
+    case SS_PLUS:
+    case US_PLUS:
+    case SS_MINUS:
+    case US_MINUS:
+    case SS_NEG:
+    case US_NEG:
+    case SS_ABS:
+    case SS_ASHIFT:
+    case US_ASHIFT:
+    case SS_TRUNCATE:
+    case US_TRUNCATE:
     case UDIV:
-    case UMOD:
     case UNORDERED:
     case ORDERED:
     case UNEQ:
@@ -13594,6 +13688,12 @@ loc_descriptor (rtx rtl, enum machine_mode mode,
                                       initialized);
       if (loc_result == NULL)
        loc_result = tls_mem_loc_descriptor (rtl);
+      if (loc_result == NULL)
+       {
+         rtx new_rtl = avoid_constant_pool_reference (rtl);
+         if (new_rtl != rtl)
+           loc_result = loc_descriptor (new_rtl, mode, initialized);
+       }
       break;
 
     case CONCAT:
@@ -13653,15 +13753,17 @@ loc_descriptor (rtx rtl, enum machine_mode mode,
       break;
 
     case CONST_DOUBLE:
+      if (mode == VOIDmode)
+       mode = GET_MODE (rtl);
+
       if (mode != VOIDmode && (dwarf_version >= 4 || !dwarf_strict))
        {
+         gcc_assert (mode == GET_MODE (rtl) || VOIDmode == GET_MODE (rtl));
+
          /* 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 (GET_MODE (rtl) != VOIDmode)
-           mode = GET_MODE (rtl);
-
          loc_result = new_loc_descr (DW_OP_implicit_value,
                                      GET_MODE_SIZE (mode), 0);
          if (SCALAR_FLOAT_MODE_P (mode))
@@ -13687,6 +13789,9 @@ loc_descriptor (rtx rtl, enum machine_mode mode,
       break;
 
     case CONST_VECTOR:
+      if (mode == VOIDmode)
+       mode = GET_MODE (rtl);
+
       if (mode != VOIDmode && (dwarf_version >= 4 || !dwarf_strict))
        {
          unsigned int elt_size = GET_MODE_UNIT_SIZE (GET_MODE (rtl));
@@ -13695,7 +13800,7 @@ loc_descriptor (rtx rtl, enum machine_mode mode,
          unsigned int i;
          unsigned char *p;
 
-         mode = GET_MODE (rtl);
+         gcc_assert (mode == GET_MODE (rtl) || VOIDmode == GET_MODE (rtl));
          switch (GET_MODE_CLASS (mode))
            {
            case MODE_VECTOR_INT:
@@ -13777,10 +13882,10 @@ loc_descriptor (rtx rtl, enum machine_mode mode,
       if (mode != VOIDmode && GET_MODE_SIZE (mode) == DWARF2_ADDR_SIZE
          && (dwarf_version >= 4 || !dwarf_strict))
        {
-         loc_result = new_loc_descr (DW_OP_implicit_value,
-                                     DWARF2_ADDR_SIZE, 0);
-         loc_result->dw_loc_oprnd2.val_class = dw_val_class_addr;
-         loc_result->dw_loc_oprnd2.v.val_addr = rtl;
+         loc_result = new_loc_descr (DW_OP_addr, 0, 0);
+         loc_result->dw_loc_oprnd1.val_class = dw_val_class_addr;
+         loc_result->dw_loc_oprnd1.v.val_addr = rtl;
+         add_loc_descr (&loc_result, new_loc_descr (DW_OP_stack_value, 0, 0));
          VEC_safe_push (rtx, gc, used_rtx_array, rtl);
        }
       break;
@@ -13842,14 +13947,6 @@ decl_by_reference_p (tree decl)
          && DECL_BY_REFERENCE (decl));
 }
 
-/* Return single element location list containing loc descr REF.  */
-
-static dw_loc_list_ref
-single_element_loc_list (dw_loc_descr_ref ref)
-{
-  return new_loc_list (ref, NULL, NULL, NULL, 0);
-}
-
 /* Helper function for dw_loc_list.  Compute proper Dwarf location descriptor
    for VARLOC.  */
 
@@ -13871,10 +13968,19 @@ dw_loc_list_1 (tree loc, rtx varloc, int want_address,
          mode = GET_MODE (varloc);
          if (MEM_P (varloc))
            {
-             varloc = XEXP (varloc, 0);
-             have_address = 1;
+             rtx addr = XEXP (varloc, 0);
+             descr = mem_loc_descriptor (addr, 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 (varloc, mode, initialized);
+         else
+           descr = mem_loc_descriptor (varloc, mode, initialized);
        }
       else
        return 0;
@@ -13931,20 +14037,21 @@ dw_loc_list_1 (tree loc, rtx varloc, int want_address,
   return descr;
 }
 
-/* Return dwarf representation of location list representing for
-   LOC_LIST of DECL.  WANT_ADDRESS has the same meaning as in
-   loc_list_from_tree function.  */
+/* Return the dwarf representation of the location list LOC_LIST of
+   DECL.  WANT_ADDRESS has the same meaning as in loc_list_from_tree
+   function.  */
 
 static dw_loc_list_ref
-dw_loc_list (var_loc_list * loc_list, tree decl, int want_address)
+dw_loc_list (var_loc_list *loc_list, tree decl, int want_address)
 {
   const char *endname, *secname;
-  dw_loc_list_ref list;
   rtx varloc;
   enum var_init_status initialized;
   struct var_loc_node *node;
   dw_loc_descr_ref descr;
   char label_id[MAX_ARTIFICIAL_LABEL_BYTES];
+  dw_loc_list_ref list = NULL;
+  dw_loc_list_ref *listp = &list;
 
   /* Now that we know what section we are using for a base,
      actually construct the list of locations.
@@ -13957,26 +14064,9 @@ dw_loc_list (var_loc_list * loc_list, tree decl, int want_address)
      This means we have to special case the last node, and generate
      a range of [last location start, end of function label].  */
 
-  node = loc_list->first;
   secname = secname_for_decl (decl);
 
-  if (NOTE_VAR_LOCATION_LOC (node->var_loc_note))
-    initialized = NOTE_VAR_LOCATION_STATUS (node->var_loc_note);
-  else
-    initialized = VAR_INIT_STATUS_INITIALIZED;
-  varloc = NOTE_VAR_LOCATION (node->var_loc_note);
-  descr = dw_loc_list_1 (decl, varloc, want_address, initialized);
-
-  if (loc_list && loc_list->first != loc_list->last)
-    list = new_loc_list (descr, node->label, node->next->label, secname, 1);
-  else
-    return single_element_loc_list (descr);
-  node = node->next;
-
-  if (!node)
-    return NULL;
-
-  for (; node->next; node = node->next)
+  for (node = loc_list->first; node->next; node = node->next)
     if (NOTE_VAR_LOCATION_LOC (node->var_loc_note) != NULL_RTX)
       {
        /* The variable has a location between NODE->LABEL and
@@ -13984,28 +14074,46 @@ dw_loc_list (var_loc_list * loc_list, tree decl, int want_address)
        initialized = NOTE_VAR_LOCATION_STATUS (node->var_loc_note);
        varloc = NOTE_VAR_LOCATION (node->var_loc_note);
        descr = dw_loc_list_1 (decl, varloc, want_address, initialized);
-       add_loc_descr_to_loc_list (&list, descr,
-                                  node->label, node->next->label, secname);
+       if (descr)
+         {
+           *listp = new_loc_list (descr, node->label, node->next->label,
+                                  secname);
+           listp = &(*listp)->dw_loc_next;
+         }
       }
 
   /* If the variable has a location at the last label
      it keeps its location until the end of function.  */
   if (NOTE_VAR_LOCATION_LOC (node->var_loc_note) != NULL_RTX)
     {
-      if (!current_function_decl)
-       endname = text_end_label;
-      else
-       {
-         ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_END_LABEL,
-                                      current_function_funcdef_no);
-         endname = ggc_strdup (label_id);
-       }
-
       initialized = NOTE_VAR_LOCATION_STATUS (node->var_loc_note);
       varloc = NOTE_VAR_LOCATION (node->var_loc_note);
       descr = dw_loc_list_1 (decl, varloc, want_address, initialized);
-      add_loc_descr_to_loc_list (&list, descr, node->label, endname, secname);
+      if (descr)
+       {
+         if (!current_function_decl)
+           endname = text_end_label;
+         else
+           {
+             ASM_GENERATE_INTERNAL_LABEL (label_id, FUNC_END_LABEL,
+                                          current_function_funcdef_no);
+             endname = ggc_strdup (label_id);
+           }
+
+         *listp = new_loc_list (descr, node->label, endname, secname);
+         listp = &(*listp)->dw_loc_next;
+       }
     }
+
+  /* Try to avoid the overhead of a location list emitting a location
+     expression instead, but only if we didn't have more than one
+     location entry in the first place.  If some entries were not
+     representable, we don't want to pretend a single entry that was
+     applies to the entire scope in which the variable is
+     available.  */
+  if (list && loc_list->first->next)
+    gen_llsym (list);
+
   return list;
 }
 
@@ -14015,7 +14123,8 @@ dw_loc_list (var_loc_list * loc_list, tree decl, int want_address)
 static bool
 single_element_loc_list_p (dw_loc_list_ref list)
 {
-  return (!list->dw_loc_next && !list->begin && !list->end);
+  gcc_assert (!list->dw_loc_next || list->ll_symbol);
+  return !list->ll_symbol;
 }
 
 /* To each location in list LIST add loc descr REF.  */
@@ -14315,9 +14424,9 @@ loc_list_from_tree (tree loc, int want_address)
        rtx rtl;
        var_loc_list *loc_list = lookup_decl_loc (loc);
 
-       if (loc_list && loc_list->first
-           && (list_ret = dw_loc_list (loc_list, loc, want_address)))
+       if (loc_list && loc_list->first)
          {
+           list_ret = dw_loc_list (loc_list, loc, want_address);
            have_address = want_address != 0;
            break;
          }
@@ -14496,6 +14605,8 @@ loc_list_from_tree (tree loc, int want_address)
     case CEIL_DIV_EXPR:
     case ROUND_DIV_EXPR:
     case TRUNC_DIV_EXPR:
+      if (TYPE_UNSIGNED (TREE_TYPE (loc)))
+       return 0;
       op = DW_OP_div;
       goto do_binop;
 
@@ -14507,8 +14618,25 @@ loc_list_from_tree (tree loc, int want_address)
     case CEIL_MOD_EXPR:
     case ROUND_MOD_EXPR:
     case TRUNC_MOD_EXPR:
-      op = DW_OP_mod;
-      goto do_binop;
+      if (TYPE_UNSIGNED (TREE_TYPE (loc)))
+       {
+         op = DW_OP_mod;
+         goto do_binop;
+       }
+      list_ret = loc_list_from_tree (TREE_OPERAND (loc, 0), 0);
+      list_ret1 = loc_list_from_tree (TREE_OPERAND (loc, 1), 0);
+      if (list_ret == 0 || list_ret1 == 0)
+       return 0;
+
+      add_loc_list (&list_ret, list_ret1);
+      if (list_ret == 0)
+       return 0;
+      add_loc_descr_to_each (list_ret, new_loc_descr (DW_OP_over, 0, 0));
+      add_loc_descr_to_each (list_ret, new_loc_descr (DW_OP_over, 0, 0));
+      add_loc_descr_to_each (list_ret, new_loc_descr (DW_OP_div, 0, 0));
+      add_loc_descr_to_each (list_ret, new_loc_descr (DW_OP_mul, 0, 0));
+      add_loc_descr_to_each (list_ret, new_loc_descr (DW_OP_minus, 0, 0));
+      break;
 
     case MULT_EXPR:
       op = DW_OP_mul;
@@ -14728,7 +14856,7 @@ loc_list_from_tree (tree loc, int want_address)
        add_loc_descr_to_each (list_ret, new_loc_descr (op, size, 0));
     }
   if (ret)
-    list_ret = single_element_loc_list (ret);
+    list_ret = new_loc_list (ret, NULL, NULL, NULL);
 
   return list_ret;
 }
@@ -15230,10 +15358,20 @@ add_const_value_attribute (dw_die_ref die, rtx rtl)
       return true;
 
     case CONST_STRING:
-      resolve_one_addr (&rtl, NULL);
-      add_AT_addr (die, DW_AT_const_value, rtl);
-      VEC_safe_push (rtx, gc, used_rtx_array, rtl);
-      return true;
+      if (dwarf_version >= 4 || !dwarf_strict)
+       {
+         dw_loc_descr_ref loc_result;
+         resolve_one_addr (&rtl, NULL);
+       rtl_addr:
+         loc_result = new_loc_descr (DW_OP_addr, 0, 0);
+         loc_result->dw_loc_oprnd1.val_class = dw_val_class_addr;
+         loc_result->dw_loc_oprnd1.v.val_addr = rtl;
+         add_loc_descr (&loc_result, new_loc_descr (DW_OP_stack_value, 0, 0));
+         add_AT_loc (die, DW_AT_location, loc_result);
+         VEC_safe_push (rtx, gc, used_rtx_array, rtl);
+         return true;
+       }
+      return false;
 
     case CONST:
       if (CONSTANT_P (XEXP (rtl, 0)))
@@ -15243,9 +15381,9 @@ add_const_value_attribute (dw_die_ref die, rtx rtl)
       if (!const_ok_for_output (rtl))
        return false;
     case LABEL_REF:
-      add_AT_addr (die, DW_AT_const_value, rtl);
-      VEC_safe_push (rtx, gc, used_rtx_array, rtl);
-      return true;
+      if (dwarf_version >= 4 || !dwarf_strict)
+       goto rtl_addr;
+      return false;
 
     case PLUS:
       /* In cases where an inlined instance of an inline function is passed
@@ -15722,15 +15860,18 @@ add_location_or_const_value_attribute (dw_die_ref die, tree decl,
      a constant value.  That way we are better to use add_const_value_attribute
      rather than expanding constant value equivalent.  */
   loc_list = lookup_decl_loc (decl);
-  if (loc_list && loc_list->first && loc_list->first == loc_list->last)
+  if (loc_list
+      && loc_list->first
+      && loc_list->first == loc_list->last
+      && NOTE_VAR_LOCATION (loc_list->first->var_loc_note)
+      && NOTE_VAR_LOCATION_LOC (loc_list->first->var_loc_note))
     {
       struct var_loc_node *node;
 
       node = loc_list->first;
-      rtl = NOTE_VAR_LOCATION (node->var_loc_note);
-      if (GET_CODE (rtl) == VAR_LOCATION
-         && GET_CODE (XEXP (rtl, 1)) != PARALLEL)
-       rtl = XEXP (XEXP (rtl, 1), 0);
+      rtl = NOTE_VAR_LOCATION_LOC (node->var_loc_note);
+      if (GET_CODE (rtl) != PARALLEL)
+       rtl = XEXP (rtl, 0);
       if ((CONSTANT_P (rtl) || GET_CODE (rtl) == CONST_STRING)
          && add_const_value_attribute (die, rtl))
         return true;
@@ -16017,8 +16158,7 @@ convert_cfa_to_fb_loc_list (HOST_WIDE_INT offset)
        if (!cfa_equal_p (&last_cfa, &next_cfa))
          {
            *list_tail = new_loc_list (build_cfa_loc (&last_cfa, offset),
-                                      start_label, last_label, section,
-                                      list == NULL);
+                                      start_label, last_label, section);
 
            list_tail = &(*list_tail)->dw_loc_next;
            last_cfa = next_cfa;
@@ -16039,14 +16179,16 @@ convert_cfa_to_fb_loc_list (HOST_WIDE_INT offset)
   if (!cfa_equal_p (&last_cfa, &next_cfa))
     {
       *list_tail = new_loc_list (build_cfa_loc (&last_cfa, offset),
-                                start_label, last_label, section,
-                                list == NULL);
+                                start_label, last_label, section);
       list_tail = &(*list_tail)->dw_loc_next;
       start_label = last_label;
     }
+
   *list_tail = new_loc_list (build_cfa_loc (&next_cfa, offset),
-                            start_label, fde->dw_fde_end, section,
-                            list == NULL);
+                            start_label, fde->dw_fde_end, section);
+
+  if (list && list->dw_loc_next)
+    gen_llsym (list);
 
   return list;
 }
@@ -16476,7 +16618,8 @@ add_pure_or_virtual_attribute (dw_die_ref die, tree func_decl)
                                   0));
 
       /* GNU extension: Record what type this method came from originally.  */
-      if (debug_info_level > DINFO_LEVEL_TERSE)
+      if (debug_info_level > DINFO_LEVEL_TERSE
+         && DECL_CONTEXT (func_decl))
        add_AT_die_ref (die, DW_AT_containing_type,
                        lookup_type_die (DECL_CONTEXT (func_decl)));
     }
@@ -17106,7 +17249,9 @@ retry_incomplete_types (void)
   int i;
 
   for (i = VEC_length (tree, incomplete_types) - 1; i >= 0; i--)
-    gen_type_die (VEC_index (tree, incomplete_types, i), comp_unit_die);
+    if (should_emit_struct_debug (VEC_index (tree, incomplete_types, i),
+                                 DINFO_USAGE_DIR_USE))
+      gen_type_die (VEC_index (tree, incomplete_types, i), comp_unit_die);
 }
 
 /* Determine what tag to use for a record type.  */
@@ -17396,7 +17541,8 @@ gen_type_die_for_member (tree type, tree member, dw_die_ref context_die)
 
   /* If we're trying to avoid duplicate debug info, we may not have
      emitted the member decl for this function.  Emit it now.  */
-  if (TYPE_DECL_SUPPRESS_DEBUG (TYPE_STUB_DECL (type))
+  if (TYPE_STUB_DECL (type)
+      && TYPE_DECL_SUPPRESS_DEBUG (TYPE_STUB_DECL (type))
       && ! lookup_decl_die (member))
     {
       dw_die_ref type_die;
@@ -17434,7 +17580,7 @@ dwarf2out_abstract_function (tree decl)
   dw_die_ref old_die;
   tree save_fn;
   tree context;
-  int was_abstract = DECL_ABSTRACT (decl);
+  int was_abstract;
   htab_t old_decl_loc_table;
 
   /* Make sure we have the actual abstract inline, not a clone.  */
@@ -17466,6 +17612,7 @@ dwarf2out_abstract_function (tree decl)
   current_function_decl = decl;
   push_cfun (DECL_STRUCT_FUNCTION (decl));
 
+  was_abstract = DECL_ABSTRACT (decl);
   set_decl_abstract_flags (decl, 1);
   dwarf2out_decl (decl);
   if (! was_abstract)
@@ -20110,7 +20257,11 @@ dwarf2out_var_location (rtx loc_note)
   if (next_real == NULL_RTX)
     return;
 
-  newloc = GGC_CNEW (struct var_loc_node);
+  decl = NOTE_VAR_LOCATION_DECL (loc_note);
+  newloc = add_var_loc_to_decl (decl, loc_note);
+  if (newloc == NULL)
+    return;
+
   /* If there were no real insns between note we processed last time
      and this note, use the label we emitted last time.  */
   if (last_var_location_insn == NULL_RTX
@@ -20145,8 +20296,6 @@ dwarf2out_var_location (rtx loc_note)
 
   last_var_location_insn = next_real;
   last_in_cold_section_p = in_cold_section_p;
-  decl = NOTE_VAR_LOCATION_DECL (loc_note);
-  add_var_loc_to_decl (decl, newloc);
 }
 
 /* We need to reset the locations at the beginning of each
@@ -20977,28 +21126,48 @@ resolve_addr (dw_die_ref die)
 {
   dw_die_ref c;
   dw_attr_ref a;
-  dw_loc_list_ref curr;
+  dw_loc_list_ref *curr;
   unsigned ix;
 
   for (ix = 0; VEC_iterate (dw_attr_node, die->die_attr, ix, a); ix++)
     switch (AT_class (a))
       {
       case dw_val_class_loc_list:
-       for (curr = AT_loc_list (a); curr != NULL; curr = curr->dw_loc_next)
-         if (!resolve_addr_in_expr (curr->expr))
-           curr->expr = NULL;
+       curr = AT_loc_list_ptr (a);
+       while (*curr)
+         {
+           if (!resolve_addr_in_expr ((*curr)->expr))
+             {
+               dw_loc_list_ref next = (*curr)->dw_loc_next;
+               if (next && (*curr)->ll_symbol)
+                 {
+                   gcc_assert (!next->ll_symbol);
+                   next->ll_symbol = (*curr)->ll_symbol;
+                 }
+               *curr = next;
+             }
+           else
+             curr = &(*curr)->dw_loc_next;
+         }
+       if (!AT_loc_list (a))
+         {
+           remove_AT (die, a->dw_attr);
+           ix--;
+         }
        break;
       case dw_val_class_loc:
        if (!resolve_addr_in_expr (AT_loc (a)))
-         a->dw_attr_val.v.val_loc = NULL;
+         {
+           remove_AT (die, a->dw_attr);
+           ix--;
+         }
        break;
       case dw_val_class_addr:
        if (a->dw_attr == DW_AT_const_value
            && resolve_one_addr (&a->dw_attr_val.v.val_addr, NULL))
          {
-           a->dw_attr = DW_AT_location;
-           a->dw_attr_val.val_class = dw_val_class_loc;
-           a->dw_attr_val.v.val_loc = NULL;
+           remove_AT (die, a->dw_attr);
+           ix--;
          }
        break;
       default:
@@ -21179,6 +21348,7 @@ dwarf2out_finish (const char *filename)
   else
     {
       unsigned fde_idx = 0;
+      bool range_list_added = false;
 
       /* We need to give .debug_loc and .debug_ranges an appropriate
         "base address".  Use zero so that these addresses become
@@ -21188,12 +21358,12 @@ dwarf2out_finish (const char *filename)
       add_AT_addr (comp_unit_die, DW_AT_low_pc, const0_rtx);
       add_AT_addr (comp_unit_die, DW_AT_entry_pc, const0_rtx);
 
-      add_AT_range_list (comp_unit_die, DW_AT_ranges,
-                        add_ranges_by_labels (text_section_label,
-                                              text_end_label));
-      if (flag_reorder_blocks_and_partition)
-       add_ranges_by_labels (cold_text_section_label,
-                             cold_end_label);
+      if (text_section_used)
+       add_ranges_by_labels (comp_unit_die, text_section_label,
+                             text_end_label, &range_list_added);
+      if (flag_reorder_blocks_and_partition && cold_text_section_used)
+       add_ranges_by_labels (comp_unit_die, cold_text_section_label,
+                             cold_end_label, &range_list_added);
 
       for (fde_idx = 0; fde_idx < fde_table_in_use; fde_idx++)
        {
@@ -21202,18 +21372,23 @@ dwarf2out_finish (const char *filename)
          if (fde->dw_fde_switched_sections)
            {
              if (!fde->in_std_section)
-               add_ranges_by_labels (fde->dw_fde_hot_section_label,
-                                     fde->dw_fde_hot_section_end_label);
+               add_ranges_by_labels (comp_unit_die,
+                                     fde->dw_fde_hot_section_label,
+                                     fde->dw_fde_hot_section_end_label,
+                                     &range_list_added);
              if (!fde->cold_in_std_section)
-               add_ranges_by_labels (fde->dw_fde_unlikely_section_label,
-                                     fde->dw_fde_unlikely_section_end_label);
+               add_ranges_by_labels (comp_unit_die,
+                                     fde->dw_fde_unlikely_section_label,
+                                     fde->dw_fde_unlikely_section_end_label,
+                                     &range_list_added);
            }
          else if (!fde->in_std_section)
-           add_ranges_by_labels (fde->dw_fde_begin,
-                                 fde->dw_fde_end);
+           add_ranges_by_labels (comp_unit_die, fde->dw_fde_begin,
+                                 fde->dw_fde_end, &range_list_added);
        }
 
-      add_ranges (NULL);
+      if (range_list_added)
+       add_ranges (NULL);
     }
 
   /* Output location list section if necessary.  */