Hi!

This patch adds DW_AT_const_value or DW_AT_location for unused static vars
(thus, not really modified and their DECL_INITIAL can be used for
location/constant value info), and optimizes various cases using
DW_OP_GNU_implicit_pointer (e.g. DW_OP_addr <symbol> DW_OP_stack_value
where symbol is either an optimized away static var (but with DW_AT_location
or DW_AT_const_value on it's DIE), which would be otherwise dropped
to avoid referencing non-existent symbol, can be replaced with
DW_OP_GNU_implicit_pointer to that DIE.  Or, if symbol above is a constant 
string
literal, we can create DW_TAG_dwarf_procedure with DW_AT_location
DW_OP_implicit_value (and, surprisingly, GDB handles that out of the box).

In cc1plus .debug_loc grew with this by about 3% and .debug_info grew by
0.25%, in libstdc++ the changes were in the noise.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2012-12-06  Jakub Jelinek  <ja...@redhat.com>

        PR debug/55608
        * dwarf2out.c (tree_add_const_value_attribute): Call ggc_free (array)
        on failure.
        (resolve_one_addr): Fail if referenced STRING_CST hasn't been written.
        (string_cst_pool_decl): New function.
        (resolve_addr_in_expr): Optimize DWARF location expression
        DW_OP_addr DW_OP_stack_value where DW_OP_addr refers to some variable
        which doesn't live in memory, but has DW_AT_location or
        DW_AT_const_value, or refers to a string literal, into
        DW_OP_GNU_implicit_pointer.
        (resolve_addr): If removing DW_AT_location of a variable because
        it was DW_OP_addr of address of the variable, but the variable doesn't
        live in memory, try to emit const value attribute for the initializer.

--- gcc/dwarf2out.c.jj  2012-11-21 18:44:25.847837373 +0100
+++ gcc/dwarf2out.c     2012-12-06 17:22:14.348761149 +0100
@@ -15492,6 +15492,7 @@ tree_add_const_value_attribute (dw_die_r
              add_AT_vec (die, DW_AT_const_value, size, 1, array);
              return true;
            }
+         ggc_free (array);
        }
     }
   return false;
@@ -22370,6 +22371,10 @@ resolve_one_addr (rtx *addr, void *data
       if (!rtl || !MEM_P (rtl))
        return 1;
       rtl = XEXP (rtl, 0);
+      if (GET_CODE (rtl) == SYMBOL_REF
+         && SYMBOL_REF_DECL (rtl)
+         && !TREE_ASM_WRITTEN (SYMBOL_REF_DECL (rtl)))
+       return 1;
       vec_safe_push (used_rtx_array, rtl);
       *addr = rtl;
       return 0;
@@ -22394,6 +22399,46 @@ resolve_one_addr (rtx *addr, void *data
   return 0;
 }
 
+/* For STRING_CST, return SYMBOL_REF of its constant pool entry,
+   if possible, and create DW_TAG_dwarf_procedure that can be referenced
+   from DW_OP_GNU_implicit_pointer if the string hasn't been seen yet.  */
+
+static rtx
+string_cst_pool_decl (tree t)
+{
+  rtx rtl = output_constant_def (t, 1);
+  unsigned char *array;
+  dw_loc_descr_ref l;
+  tree decl;
+  size_t len;
+  dw_die_ref ref;
+
+  if (!rtl || !MEM_P (rtl))
+    return NULL_RTX;
+  rtl = XEXP (rtl, 0);
+  if (GET_CODE (rtl) != SYMBOL_REF
+      || SYMBOL_REF_DECL (rtl) == NULL_TREE)
+    return NULL_RTX;
+
+  decl = SYMBOL_REF_DECL (rtl);
+  if (lookup_decl_die (decl))
+    return rtl;
+
+  len = TREE_STRING_LENGTH (t);
+  vec_safe_push (used_rtx_array, rtl);
+  ref = new_die (DW_TAG_dwarf_procedure, comp_unit_die (), decl);
+  array = (unsigned char *) ggc_alloc_atomic (len);
+  memcpy (array, TREE_STRING_POINTER (t), len);
+  l = new_loc_descr (DW_OP_implicit_value, len, 0);
+  l->dw_loc_oprnd2.val_class = dw_val_class_vec;
+  l->dw_loc_oprnd2.v.val_vec.length = len;
+  l->dw_loc_oprnd2.v.val_vec.elt_size = 1;
+  l->dw_loc_oprnd2.v.val_vec.array = array;
+  add_AT_loc (ref, DW_AT_location, l);
+  equate_decl_number_to_die (decl, ref);
+  return rtl;
+}
+
 /* Helper function for resolve_addr, handle one location
    expression, return false if at least one CONST_STRING or SYMBOL_REF in
    the location list couldn't be resolved.  */
@@ -22402,23 +22447,81 @@ static bool
 resolve_addr_in_expr (dw_loc_descr_ref loc)
 {
   dw_loc_descr_ref keep = NULL;
-  for (; loc; loc = loc->dw_loc_next)
+  bool start_of_dw_expr = true;
+  for (; loc; start_of_dw_expr = (loc->dw_loc_opc == DW_OP_piece
+                                 || loc->dw_loc_opc == DW_OP_bit_piece),
+             loc = loc->dw_loc_next)
     switch (loc->dw_loc_opc)
       {
       case DW_OP_addr:
        if (resolve_one_addr (&loc->dw_loc_oprnd1.v.val_addr, NULL))
-         return false;
+         {
+           if (start_of_dw_expr
+               && loc->dw_loc_next
+               && loc->dw_loc_next->dw_loc_opc == DW_OP_stack_value
+               && !dwarf_strict)
+             {
+               rtx rtl = loc->dw_loc_oprnd1.v.val_addr;
+               HOST_WIDE_INT offset = 0;
+               dw_die_ref ref = NULL;
+               tree decl;
+
+               if (GET_CODE (rtl) == CONST
+                   && GET_CODE (XEXP (rtl, 0)) == PLUS
+                   && CONST_INT_P (XEXP (XEXP (rtl, 0), 1)))
+                 {
+                   offset = INTVAL (XEXP (XEXP (rtl, 0), 1));
+                   rtl = XEXP (XEXP (rtl, 0), 0);
+                 }
+               if (GET_CODE (rtl) == CONST_STRING)
+                 {
+                   size_t len = strlen (XSTR (rtl, 0)) + 1;
+                   tree t = build_string (len, XSTR (rtl, 0));
+                   tree tlen = size_int (len - 1);
+
+                   TREE_TYPE (t)
+                     = build_array_type (char_type_node,
+                                         build_index_type (tlen));
+                   rtl = string_cst_pool_decl (t);
+                   if (!rtl)
+                     return false;
+                 }
+               if (GET_CODE (rtl) == SYMBOL_REF
+                   && SYMBOL_REF_DECL (rtl))
+                 {
+                   decl = SYMBOL_REF_DECL (rtl);
+                   if (TREE_CODE (decl) == VAR_DECL
+                       && !DECL_EXTERNAL (decl))
+                     {
+                       ref = lookup_decl_die (decl);
+                       if (ref
+                           && (get_AT (ref, DW_AT_location)
+                               || get_AT (ref, DW_AT_const_value)))
+                         {
+                           loc->dw_loc_opc = DW_OP_GNU_implicit_pointer;
+                           loc->dw_loc_oprnd1.val_class
+                             = dw_val_class_die_ref;
+                           loc->dw_loc_oprnd1.val_entry = NULL;
+                           loc->dw_loc_oprnd1.v.val_die_ref.die = ref;
+                           loc->dw_loc_oprnd1.v.val_die_ref.external = 0;
+                           loc->dw_loc_next = loc->dw_loc_next->dw_loc_next;
+                           loc->dw_loc_oprnd2.v.val_int = offset;
+                           break;
+                         }
+                     }
+                 }
+             }
+           return false;
+         }
        break;
       case DW_OP_GNU_addr_index:
       case DW_OP_GNU_const_index:
-        {
-          if ((loc->dw_loc_opc == DW_OP_GNU_addr_index
-               || (loc->dw_loc_opc == DW_OP_GNU_const_index && loc->dtprel))
-              && resolve_one_addr (&loc->dw_loc_oprnd1.val_entry->addr.rtl,
-                                   NULL))
-            return false;
-        }
-       break;
+       if ((loc->dw_loc_opc == DW_OP_GNU_addr_index
+            || (loc->dw_loc_opc == DW_OP_GNU_const_index && loc->dtprel))
+           && resolve_one_addr (&loc->dw_loc_oprnd1.val_entry->addr.rtl,
+                                NULL))
+         return false;
+       break;
       case DW_OP_const4u:
       case DW_OP_const8u:
        if (loc->dtprel
@@ -22601,8 +22704,79 @@ resolve_addr (dw_die_ref die)
               || l->dw_loc_next != NULL)
              && !resolve_addr_in_expr (l))
            {
-              if (dwarf_split_debug_info)
-                remove_loc_list_addr_table_entries (l);
+             if (dwarf_split_debug_info)
+               remove_loc_list_addr_table_entries (l);
+             if (l != NULL
+                 && l->dw_loc_next == NULL
+                 && l->dw_loc_opc == DW_OP_addr
+                 && GET_CODE (l->dw_loc_oprnd1.v.val_addr) == SYMBOL_REF
+                 && SYMBOL_REF_DECL (l->dw_loc_oprnd1.v.val_addr)
+                 && a->dw_attr == DW_AT_location)
+               {
+                 tree decl = SYMBOL_REF_DECL (l->dw_loc_oprnd1.v.val_addr);
+                 remove_AT (die, a->dw_attr);
+                 ix--;
+                 if (TREE_CODE (decl) == VAR_DECL
+                     && lookup_decl_die (decl) == die
+                     && !DECL_EXTERNAL (decl)
+                     && TREE_STATIC (decl)
+                     && DECL_INITIAL (decl)
+                     && !DECL_P (DECL_INITIAL (decl))
+                     && !get_AT (die, DW_AT_const_value))
+                   {
+                     tree init = DECL_INITIAL (decl);
+                     HOST_WIDE_INT offset = 0;
+                     /* For variables that have been optimized away and thus
+                        don't have a memory location, see if we can emit
+                        DW_AT_const_value instead.  */
+                     if (tree_add_const_value_attribute (die, init))
+                       break;
+                     if (dwarf_strict)
+                       break;
+                     STRIP_NOPS (init);
+                     if (TREE_CODE (init) == POINTER_PLUS_EXPR
+                         && host_integerp (TREE_OPERAND (init, 1), 0))
+                       {
+                         offset = tree_low_cst (TREE_OPERAND (init, 1), 0);
+                         init = TREE_OPERAND (init, 0);
+                         STRIP_NOPS (init);
+                       }
+                     if (TREE_CODE (init) != ADDR_EXPR)
+                       break;
+                     if ((TREE_CODE (TREE_OPERAND (init, 0)) == STRING_CST
+                          && !TREE_ASM_WRITTEN (TREE_OPERAND (init, 0)))
+                         || (TREE_CODE (TREE_OPERAND (init, 0)) == VAR_DECL
+                             && !DECL_EXTERNAL (TREE_OPERAND (init, 0))
+                             && TREE_OPERAND (init, 0) != decl))
+                       {
+                         dw_die_ref ref;
+                         tree decl;
+
+                         if (TREE_CODE (TREE_OPERAND (init, 0)) == STRING_CST)
+                           {
+                             rtx rtl
+                               = string_cst_pool_decl (TREE_OPERAND (init, 0));
+                             if (!rtl)
+                               break;
+                             decl = SYMBOL_REF_DECL (rtl);
+                           }
+                         else
+                           decl = TREE_OPERAND (init, 0);
+                         ref = lookup_decl_die (decl);
+                         if (ref == NULL
+                             || (!get_AT (ref, DW_AT_location)
+                                 && !get_AT (ref, DW_AT_const_value)))
+                           break;
+                         l = new_loc_descr (DW_OP_GNU_implicit_pointer, 0,
+                                            offset);
+                         l->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+                         l->dw_loc_oprnd1.v.val_die_ref.die = ref;
+                         l->dw_loc_oprnd1.v.val_die_ref.external = 0;
+                         add_AT_loc (die, DW_AT_location, l);
+                       }
+                   }
+                 break;
+               }
              remove_AT (die, a->dw_attr);
              ix--;
            }

        Jakub

Reply via email to