re PR debug/77589 (fortran: Missing DW_AT_byte_stride for an array record field selec...
authorJakub Jelinek <jakub@redhat.com>
Sat, 25 Feb 2017 08:18:24 +0000 (09:18 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Sat, 25 Feb 2017 08:18:24 +0000 (09:18 +0100)
PR debug/77589
include/
* dwarf2.def (DW_OP_GNU_variable_value): New opcode.
gcc/
* dwarf2out.c (struct dw_loc_list_struct): Add noted_variable_value
bitfield.
(size_of_loc_descr): Handle DW_OP_GNU_variable_value.
(output_loc_operands): Handle DW_OP_call_ref and
DW_OP_GNU_variable_value.
(struct variable_value_struct): New type.
(struct variable_value_hasher): Likewise.
(variable_value_hash): New variable.
(string_types): Remove.
(copy_loc_descr): New function.
(add_loc_descr_to_each): Clarify comment.  Use copy_loc_descr.
(prepend_loc_descr_to_each): New function.
(add_loc_list): Fix comment typo.  Use prepend_loc_descr_to_each
instead of add_loc_descr_to_each if the first argument is single
location list and the second has multiple.
(resolve_args_picking_1): Handle DW_OP_GNU_variable_value.
(loc_list_from_tree_1): For early_dwarf, emit DW_OP_GNU_variable_value
when looking for variable value which doesn't have other location info.
(loc_list_from_tree): Formatting fix.
(gen_array_type_die): Simplify DW_AT_string_length handling.
(adjust_string_types): Remove.
(gen_subprogram_die): Don't call adjust_string_types nor test/set
string_types.  Call resolve_variable_values.
(prune_unused_types_walk_loc_descr): Handle DW_OP_GNU_variable_value.
(resolve_addr_in_expr): Likewise.  Add A argument.
(copy_deref_exprloc): Remove deref argument.  Adjust for the
original expression being DW_OP_GNU_variable_value with optionally
DW_OP_stack_value after it instead of DW_OP_call4 with DW_OP_deref
optionally after it.
(optimize_string_length): Rework for DW_OP_GNU_variable_value.
(resolve_addr): Adjust optimize_string_length and resolve_addr_in_expr
callers.  Set remove_AT_byte_size if removing DW_AT_string_length.
(variable_value_hasher::hash, variable_value_hasher::equal): New
methods.
(resolve_variable_value_in_expr, resolve_variable_value,
resolve_variable_values, note_variable_value_in_expr,
note_variable_value): New functions.
(dwarf2out_early_finish): Call note_variable_value on all toplevel
DIEs.

From-SVN: r245733

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

index 5f0ee4f2816c71578a6acdbb27cc7240b4507378..a38fd2063ecc6c6dd45bcdaccc7cbc02a5337c59 100644 (file)
@@ -1,3 +1,46 @@
+2017-02-25  Jakub Jelinek  <jakub@redhat.com>
+
+       PR debug/77589
+       * dwarf2out.c (struct dw_loc_list_struct): Add noted_variable_value
+       bitfield.
+       (size_of_loc_descr): Handle DW_OP_GNU_variable_value.
+       (output_loc_operands): Handle DW_OP_call_ref and
+       DW_OP_GNU_variable_value.
+       (struct variable_value_struct): New type.
+       (struct variable_value_hasher): Likewise.
+       (variable_value_hash): New variable.
+       (string_types): Remove.
+       (copy_loc_descr): New function.
+       (add_loc_descr_to_each): Clarify comment.  Use copy_loc_descr.
+       (prepend_loc_descr_to_each): New function.
+       (add_loc_list): Fix comment typo.  Use prepend_loc_descr_to_each
+       instead of add_loc_descr_to_each if the first argument is single
+       location list and the second has multiple.
+       (resolve_args_picking_1): Handle DW_OP_GNU_variable_value.
+       (loc_list_from_tree_1): For early_dwarf, emit DW_OP_GNU_variable_value
+       when looking for variable value which doesn't have other location info.
+       (loc_list_from_tree): Formatting fix.
+       (gen_array_type_die): Simplify DW_AT_string_length handling.
+       (adjust_string_types): Remove.
+       (gen_subprogram_die): Don't call adjust_string_types nor test/set
+       string_types.  Call resolve_variable_values.
+       (prune_unused_types_walk_loc_descr): Handle DW_OP_GNU_variable_value.
+       (resolve_addr_in_expr): Likewise.  Add A argument.
+       (copy_deref_exprloc): Remove deref argument.  Adjust for the
+       original expression being DW_OP_GNU_variable_value with optionally
+       DW_OP_stack_value after it instead of DW_OP_call4 with DW_OP_deref
+       optionally after it.
+       (optimize_string_length): Rework for DW_OP_GNU_variable_value.
+       (resolve_addr): Adjust optimize_string_length and resolve_addr_in_expr
+       callers.  Set remove_AT_byte_size if removing DW_AT_string_length.
+       (variable_value_hasher::hash, variable_value_hasher::equal): New
+       methods.
+       (resolve_variable_value_in_expr, resolve_variable_value,
+       resolve_variable_values, note_variable_value_in_expr,
+       note_variable_value): New functions.
+       (dwarf2out_early_finish): Call note_variable_value on all toplevel
+       DIEs.
+
 2017-02-24  Jakub Jelinek  <jakub@redhat.com>
 
        PR c/79677
index 541d86824bdcd96c93e6495f8964a931596384d3..87060a33804e88093941eac501e1da5c0fa7d1b9 100644 (file)
@@ -1293,6 +1293,8 @@ typedef struct GTY(()) dw_loc_list_struct {
   unsigned char num_assigned : 1;
   /* True if .debug_loclists.dwo offset has been emitted for it already.  */
   unsigned char offset_emitted : 1;
+  /* True if note_variable_value_in_expr has been called on it.  */
+  unsigned char noted_variable_value : 1;
   /* True if the range should be emitted even if begin and end
      are the same.  */
   bool force;
@@ -1791,6 +1793,7 @@ size_of_loc_descr (dw_loc_descr_ref loc)
       size += 4;
       break;
     case DW_OP_call_ref:
+    case DW_OP_GNU_variable_value:
       size += DWARF_REF_SIZE;
       break;
     case DW_OP_implicit_value:
@@ -2214,6 +2217,17 @@ output_loc_operands (dw_loc_descr_ref loc, int for_eh_or_skip)
       }
       break;
 
+    case DW_OP_call_ref:
+    case DW_OP_GNU_variable_value:
+      {
+       char label[MAX_ARTIFICIAL_LABEL_BYTES
+                  + HOST_BITS_PER_WIDE_INT / 2 + 2];
+       gcc_assert (val1->val_class == dw_val_class_die_ref);
+       get_ref_die_offset_label (label, val1->v.val_die_ref.die);
+       dw2_asm_output_offset (DWARF_REF_SIZE, label, debug_info_section, NULL);
+      }
+      break;
+
     case DW_OP_implicit_pointer:
     case DW_OP_GNU_implicit_pointer:
       {
@@ -3097,6 +3111,23 @@ struct decl_die_hasher : ggc_ptr_hash<die_node>
    The key is a DECL_UID() which is a unique number identifying each decl.  */
 static GTY (()) hash_table<decl_die_hasher> *decl_die_table;
 
+struct GTY ((for_user)) variable_value_struct {
+  unsigned int decl_id;
+  vec<dw_die_ref, va_gc> *dies;
+};
+
+struct variable_value_hasher : ggc_ptr_hash<variable_value_struct>
+{
+  typedef tree compare_type;
+
+  static hashval_t hash (variable_value_struct *);
+  static bool equal (variable_value_struct *, tree);
+};
+/* A hash table of DIEs that contain DW_OP_GNU_variable_value with
+   dw_val_class_decl_ref class, indexed by FUNCTION_DECLs which is
+   DECL_CONTEXT of the referenced VAR_DECLs.  */
+static GTY (()) hash_table<variable_value_hasher> *variable_value_hash;
+
 struct block_die_hasher : ggc_ptr_hash<die_struct>
 {
   static hashval_t hash (die_struct *);
@@ -3287,10 +3318,6 @@ static bool frame_pointer_fb_offset_valid;
 
 static vec<dw_die_ref> base_types;
 
-/* Pointer to vector of DW_TAG_string_type DIEs that need finalization
-   once all arguments are parsed.  */
-static vec<dw_die_ref> *string_types;
-
 /* Flags to represent a set of attribute classes for attributes that represent
    a scalar value (bounds, pointers, ...).  */
 enum dw_scalar_form
@@ -3605,6 +3632,7 @@ static void gen_remaining_tmpl_value_param_die_attribute (void);
 static bool generic_type_p (tree);
 static void schedule_generic_params_dies_gen (tree t);
 static void gen_scheduled_generic_parms_dies (void);
+static void resolve_variable_values (void);
 
 static const char *comp_dir_string (void);
 
@@ -16292,7 +16320,17 @@ single_element_loc_list_p (dw_loc_list_ref list)
   return !list->ll_symbol;
 }
 
-/* To each location in list LIST add loc descr REF.  */
+/* Duplicate a single element of location list.  */
+
+static inline dw_loc_descr_ref
+copy_loc_descr (dw_loc_descr_ref ref)
+{
+  dw_loc_descr_ref copy = ggc_alloc<dw_loc_descr_node> ();
+  memcpy (copy, ref, sizeof (dw_loc_descr_node));
+  return copy;
+}
+
+/* To each location in list LIST append loc descr REF.  */
 
 static void
 add_loc_descr_to_each (dw_loc_list_ref list, dw_loc_descr_ref ref)
@@ -16302,16 +16340,31 @@ add_loc_descr_to_each (dw_loc_list_ref list, dw_loc_descr_ref ref)
   list = list->dw_loc_next;
   while (list)
     {
-      copy = ggc_alloc<dw_loc_descr_node> ();
-      memcpy (copy, ref, sizeof (dw_loc_descr_node));
+      copy = copy_loc_descr (ref);
       add_loc_descr (&list->expr, copy);
       while (copy->dw_loc_next)
-       {
-         dw_loc_descr_ref new_copy = ggc_alloc<dw_loc_descr_node> ();
-         memcpy (new_copy, copy->dw_loc_next, sizeof (dw_loc_descr_node));
-         copy->dw_loc_next = new_copy;
-         copy = new_copy;
-       }
+       copy = copy->dw_loc_next = copy_loc_descr (copy->dw_loc_next);
+      list = list->dw_loc_next;
+    }
+}
+
+/* To each location in list LIST prepend loc descr REF.  */
+
+static void
+prepend_loc_descr_to_each (dw_loc_list_ref list, dw_loc_descr_ref ref)
+{
+  dw_loc_descr_ref copy;
+  dw_loc_descr_ref ref_end = list->expr;
+  add_loc_descr (&ref, list->expr);
+  list->expr = ref;
+  list = list->dw_loc_next;
+  while (list)
+    {
+      dw_loc_descr_ref end = list->expr;
+      list->expr = copy = copy_loc_descr (ref);
+      while (copy->dw_loc_next != ref_end)
+       copy = copy->dw_loc_next = copy_loc_descr (copy->dw_loc_next);
+      copy->dw_loc_next = end;
       list = list->dw_loc_next;
     }
 }
@@ -16322,7 +16375,7 @@ add_loc_descr_to_each (dw_loc_list_ref list, dw_loc_descr_ref ref)
    Might be destructive on both RET and LIST.
 
    TODO: We handle only simple cases of RET or LIST having at most one
-   element. General case would inolve sorting the lists in program order
+   element.  General case would involve sorting the lists in program order
    and merging them that will need some additional work.
    Adding that will improve quality of debug info especially for SRA-ed
    structures.  */
@@ -16344,7 +16397,7 @@ add_loc_list (dw_loc_list_ref *ret, dw_loc_list_ref list)
     }
   if (!(*ret)->dw_loc_next)
     {
-      add_loc_descr_to_each (list, (*ret)->expr);
+      prepend_loc_descr_to_each (list, (*ret)->expr);
       *ret = list;
       return;
     }
@@ -16824,6 +16877,7 @@ resolve_args_picking_1 (dw_loc_descr_ref loc, unsigned initial_frame_offset,
        case DW_OP_fbreg:
        case DW_OP_push_object_address:
        case DW_OP_call_frame_cfa:
+       case DW_OP_GNU_variable_value:
          ++frame_offset_;
          break;
 
@@ -17299,6 +17353,31 @@ loc_list_from_tree_1 (tree loc, int want_address,
        rtl = rtl_for_decl_location (loc);
        if (rtl == NULL_RTX)
          {
+           if (TREE_CODE (loc) != FUNCTION_DECL
+               && early_dwarf
+               && current_function_decl
+               && want_address != 1
+               && (INTEGRAL_TYPE_P (TREE_TYPE (loc))
+                   || POINTER_TYPE_P (TREE_TYPE (loc)))
+               && DECL_CONTEXT (loc) == current_function_decl
+               && (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (loc)))
+                   <= DWARF2_ADDR_SIZE))
+             {
+               dw_die_ref ref = lookup_decl_die (loc);
+               ret = new_loc_descr (DW_OP_GNU_variable_value, 0, 0);
+               if (ref)
+                 {
+                   ret->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+                   ret->dw_loc_oprnd1.v.val_die_ref.die = ref;
+                   ret->dw_loc_oprnd1.v.val_die_ref.external = 0;
+                 }
+               else
+                 {
+                   ret->dw_loc_oprnd1.val_class = dw_val_class_decl_ref;
+                   ret->dw_loc_oprnd1.v.val_decl_ref = loc;
+                 }
+               break;
+             }
            expansion_failed (loc, NULL_RTX, "DECL has no RTL");
            return 0;
          }
@@ -17873,8 +17952,7 @@ loc_list_from_tree (tree loc, int want_address,
   dw_loc_list_ref result = loc_list_from_tree_1 (loc, want_address, context);
 
   for (dw_loc_list_ref loc_cur = result;
-       loc_cur != NULL; loc_cur =
-       loc_cur->dw_loc_next)
+       loc_cur != NULL; loc_cur = loc_cur->dw_loc_next)
     loc_descr_without_nops (loc_cur->expr);
   return result;
 }
@@ -20685,7 +20763,6 @@ gen_array_type_die (tree type, dw_die_ref context_die)
        {
          tree szdecl = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
          tree rszdecl = szdecl;
-         HOST_WIDE_INT rsize = 0;
 
          size = int_size_in_bytes (TREE_TYPE (szdecl));
          if (!DECL_P (szdecl))
@@ -20694,8 +20771,8 @@ gen_array_type_die (tree type, dw_die_ref context_die)
                  && DECL_P (TREE_OPERAND (szdecl, 0)))
                {
                  rszdecl = TREE_OPERAND (szdecl, 0);
-                 rsize = int_size_in_bytes (TREE_TYPE (rszdecl));
-                 if (rsize <= 0)
+                 if (int_size_in_bytes (TREE_TYPE (rszdecl))
+                     != DWARF2_ADDR_SIZE)
                    size = 0;
                }
              else
@@ -20703,41 +20780,9 @@ gen_array_type_die (tree type, dw_die_ref context_die)
            }
          if (size > 0)
            {
-             dw_loc_list_ref loc = loc_list_from_tree (szdecl, 2, NULL);
-             if (loc == NULL
-                 && early_dwarf
-                 && current_function_decl
-                 && DECL_CONTEXT (rszdecl) == current_function_decl)
-               {
-                 dw_die_ref ref = lookup_decl_die (rszdecl);
-                 dw_loc_descr_ref l = NULL;
-                 if (ref)
-                   {
-                     l = new_loc_descr (DW_OP_call4, 0, 0);
-                     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;
-                   }
-                 else if (TREE_CODE (rszdecl) == PARM_DECL
-                          && string_types)
-                   {
-                     l = new_loc_descr (DW_OP_call4, 0, 0);
-                     l->dw_loc_oprnd1.val_class = dw_val_class_decl_ref;
-                     l->dw_loc_oprnd1.v.val_decl_ref = rszdecl;
-                     string_types->safe_push (array_die);
-                   }
-                 if (l && rszdecl != szdecl)
-                   {
-                     if (rsize == DWARF2_ADDR_SIZE)
-                       add_loc_descr (&l, new_loc_descr (DW_OP_deref,
-                                                         0, 0));
-                     else
-                       add_loc_descr (&l, new_loc_descr (DW_OP_deref_size,
-                                                         rsize, 0));
-                   }
-                 if (l)
-                   loc = new_loc_list (l, NULL, NULL, NULL);
-               }
+             dw_loc_list_ref loc
+               = loc_list_from_tree (rszdecl, szdecl == rszdecl ? 2 : 0,
+                                     NULL);
              if (loc)
                {
                  add_AT_location_description (array_die, DW_AT_string_length,
@@ -20814,39 +20859,6 @@ gen_array_type_die (tree type, dw_die_ref context_die)
   add_alignment_attribute (array_die, type);
 }
 
-/* After all arguments are created, adjust any DW_TAG_string_type
-   DIEs DW_AT_string_length attributes.  */
-
-static void
-adjust_string_types (void)
-{
-  dw_die_ref array_die;
-  unsigned int i;
-  FOR_EACH_VEC_ELT (*string_types, i, array_die)
-    {
-      dw_attr_node *a = get_AT (array_die, DW_AT_string_length);
-      if (a == NULL)
-       continue;
-      dw_loc_descr_ref loc = AT_loc (a);
-      gcc_assert (loc->dw_loc_opc == DW_OP_call4
-                 && loc->dw_loc_oprnd1.val_class == dw_val_class_decl_ref);
-      dw_die_ref ref = lookup_decl_die (loc->dw_loc_oprnd1.v.val_decl_ref);
-      if (ref)
-       {
-         loc->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
-         loc->dw_loc_oprnd1.v.val_die_ref.die = ref;
-         loc->dw_loc_oprnd1.v.val_die_ref.external = 0;
-       }
-      else
-       {
-         remove_AT (array_die, DW_AT_string_length);
-         remove_AT (array_die, dwarf_version >= 5
-                               ? DW_AT_string_length_byte_size
-                               : DW_AT_byte_size);
-       }
-    }
-}
-
 /* This routine generates DIE for array with hidden descriptor, details
    are filled into *info by a langhook.  */
 
@@ -22289,6 +22301,8 @@ gen_subprogram_die (tree decl, dw_die_ref context_die)
          add_AT_location_description (subr_die, DW_AT_static_link,
                                       loc_list_from_tree (fb_expr, 0, NULL));
        }
+
+      resolve_variable_values ();
     }
 
   /* Generate child dies for template paramaters.  */
@@ -22321,9 +22335,6 @@ gen_subprogram_die (tree decl, dw_die_ref context_die)
       tree generic_decl_parm = generic_decl
                                ? DECL_ARGUMENTS (generic_decl)
                                : NULL;
-      auto_vec<dw_die_ref> string_types_vec;
-      if (string_types == NULL)
-       string_types = &string_types_vec;
 
       /* Now we want to walk the list of parameters of the function and
         emit their relevant DIEs.
@@ -22386,14 +22397,6 @@ gen_subprogram_die (tree decl, dw_die_ref context_die)
          else if (DECL_INITIAL (decl) == NULL_TREE)
            gen_unspecified_parameters_die (decl, subr_die);
        }
-
-      /* Adjust DW_TAG_string_type DIEs if needed, now that all arguments
-        have DIEs.  */
-      if (string_types == &string_types_vec)
-       {
-         adjust_string_types ();
-         string_types = NULL;
-       }
     }
 
   if (subr_die != old_die)
@@ -27532,6 +27535,18 @@ prune_unused_types_walk_loc_descr (dw_loc_descr_ref loc)
        if (loc->dw_loc_oprnd1.val_class == dw_val_class_die_ref)
          prune_unused_types_mark (loc->dw_loc_oprnd1.v.val_die_ref.die, 1);
        break;
+      case DW_OP_GNU_variable_value:
+       if (loc->dw_loc_oprnd1.val_class == dw_val_class_decl_ref)
+         {
+           dw_die_ref ref
+             = lookup_decl_die (loc->dw_loc_oprnd1.v.val_decl_ref);
+           if (ref == NULL)
+             break;
+           loc->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+           loc->dw_loc_oprnd1.v.val_die_ref.die = ref;
+           loc->dw_loc_oprnd1.v.val_die_ref.external = 0;
+         }
+       /* FALLTHRU */
       case DW_OP_call2:
       case DW_OP_call4:
       case DW_OP_call_ref:
@@ -28300,7 +28315,7 @@ optimize_one_addr_into_implicit_ptr (dw_loc_descr_ref loc)
    the location list couldn't be resolved.  */
 
 static bool
-resolve_addr_in_expr (dw_loc_descr_ref loc)
+resolve_addr_in_expr (dw_attr_node *a, dw_loc_descr_ref loc)
 {
   dw_loc_descr_ref keep = NULL;
   for (dw_loc_descr_ref prev = NULL; loc; prev = loc, loc = loc->dw_loc_next)
@@ -28360,6 +28375,7 @@ resolve_addr_in_expr (dw_loc_descr_ref loc)
       case DW_OP_implicit_pointer:
       case DW_OP_GNU_implicit_pointer:
       case DW_OP_GNU_parameter_ref:
+      case DW_OP_GNU_variable_value:
        if (loc->dw_loc_oprnd1.val_class == dw_val_class_decl_ref)
          {
            dw_die_ref ref
@@ -28370,6 +28386,37 @@ resolve_addr_in_expr (dw_loc_descr_ref loc)
            loc->dw_loc_oprnd1.v.val_die_ref.die = ref;
            loc->dw_loc_oprnd1.v.val_die_ref.external = 0;
          }
+       if (loc->dw_loc_opc == DW_OP_GNU_variable_value)
+         {
+           if (prev == NULL
+               && loc->dw_loc_next == NULL
+               && AT_class (a) == dw_val_class_loc)
+             switch (a->dw_attr)
+               {
+                 /* Following attributes allow both exprloc and reference,
+                    so if the whole expression is DW_OP_GNU_variable_value
+                    alone we could transform it into reference.  */
+               case DW_AT_byte_size:
+               case DW_AT_bit_size:
+               case DW_AT_lower_bound:
+               case DW_AT_upper_bound:
+               case DW_AT_bit_stride:
+               case DW_AT_count:
+               case DW_AT_allocated:
+               case DW_AT_associated:
+               case DW_AT_byte_stride:
+                 a->dw_attr_val.val_class = dw_val_class_die_ref;
+                 a->dw_attr_val.val_entry = NULL;
+                 a->dw_attr_val.v.val_die_ref.die
+                   = loc->dw_loc_oprnd1.v.val_die_ref.die;
+                 a->dw_attr_val.v.val_die_ref.external = 0;
+                 return true;
+               default:
+                 break;
+               }
+           if (dwarf_strict)
+             return false;
+         }
        break;
       case DW_OP_const_type:
       case DW_OP_regval_type:
@@ -28544,18 +28591,18 @@ non_dwarf_expression (dw_loc_descr_ref l)
 /* Return adjusted copy of EXPR:
    If it is empty DWARF expression, return it.
    If it is valid non-empty DWARF expression,
-   return copy of EXPR with copy of DEREF appended to it.
+   return copy of EXPR with DW_OP_deref appended to it.
    If it is DWARF expression followed by DW_OP_reg{N,x}, return
-   copy of the DWARF expression with DW_OP_breg{N,x} <0> appended
-   and no DEREF.
+   copy of the DWARF expression with DW_OP_breg{N,x} <0> appended.
    If it is DWARF expression followed by DW_OP_stack_value, return
    copy of the DWARF expression without anything appended.
    Otherwise, return NULL.  */
 
 static dw_loc_descr_ref
-copy_deref_exprloc (dw_loc_descr_ref expr, dw_loc_descr_ref deref)
+copy_deref_exprloc (dw_loc_descr_ref expr)
 {
-  
+  dw_loc_descr_ref tail = NULL;
+
   if (expr == NULL)
     return NULL;
 
@@ -28566,26 +28613,24 @@ copy_deref_exprloc (dw_loc_descr_ref expr, dw_loc_descr_ref deref)
   if (l)
     {
       if (l->dw_loc_opc >= DW_OP_reg0 && l->dw_loc_opc <= DW_OP_reg31)
-       deref = new_loc_descr ((enum dwarf_location_atom)
-                              (DW_OP_breg0 + (l->dw_loc_opc - DW_OP_reg0)),
-                              0, 0);
+       tail = new_loc_descr ((enum dwarf_location_atom)
+                             (DW_OP_breg0 + (l->dw_loc_opc - DW_OP_reg0)),
+                             0, 0);
       else
        switch (l->dw_loc_opc)
          {
          case DW_OP_regx:
-           deref = new_loc_descr (DW_OP_bregx,
-                                  l->dw_loc_oprnd1.v.val_unsigned, 0);
+           tail = new_loc_descr (DW_OP_bregx,
+                                 l->dw_loc_oprnd1.v.val_unsigned, 0);
            break;
          case DW_OP_stack_value:
-           deref = NULL;
            break;
          default:
            return NULL;
          }
     }
   else
-    deref = new_loc_descr (deref->dw_loc_opc,
-                          deref->dw_loc_oprnd1.v.val_int, 0);
+    tail = new_loc_descr (DW_OP_deref, 0, 0);
 
   dw_loc_descr_ref ret = NULL, *p = &ret;
   while (expr != l)
@@ -28596,29 +28641,55 @@ copy_deref_exprloc (dw_loc_descr_ref expr, dw_loc_descr_ref deref)
       p = &(*p)->dw_loc_next;
       expr = expr->dw_loc_next;
     }
-  *p = deref;
+  *p = tail;
   return ret;
 }
 
-/* For DW_AT_string_length attribute with DW_OP_call4 reference to a variable
-   or argument, adjust it if needed and return:
+/* For DW_AT_string_length attribute with DW_OP_GNU_variable_value
+   reference to a variable or argument, adjust it if needed and return:
    -1 if the DW_AT_string_length attribute and DW_AT_{string_length_,}byte_size
       attribute if present should be removed
-   0 keep the attribute as is if the referenced var or argument has
-     only DWARF expression that covers all ranges
+   0 keep the attribute perhaps with minor modifications, no need to rescan
    1 if the attribute has been successfully adjusted.  */
 
 static int
 optimize_string_length (dw_attr_node *a)
 {
   dw_loc_descr_ref l = AT_loc (a), lv;
-  dw_die_ref die = l->dw_loc_oprnd1.v.val_die_ref.die;
+  dw_die_ref die;
+  if (l->dw_loc_oprnd1.val_class == dw_val_class_decl_ref)
+    {
+      tree decl = l->dw_loc_oprnd1.v.val_decl_ref;
+      die = lookup_decl_die (decl);
+      if (die)
+       {
+         l->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+         l->dw_loc_oprnd1.v.val_die_ref.die = die;
+         l->dw_loc_oprnd1.v.val_die_ref.external = 0;
+       }
+      else
+       return -1;
+    }
+  else
+    die = l->dw_loc_oprnd1.v.val_die_ref.die;
+
+  /* DWARF5 allows reference class, so we can then reference the DIE.
+     Only do this for DW_OP_GNU_variable_value DW_OP_stack_value.  */
+  if (l->dw_loc_next != NULL && dwarf_version >= 5)
+    {
+      a->dw_attr_val.val_class = dw_val_class_die_ref;
+      a->dw_attr_val.val_entry = NULL;
+      a->dw_attr_val.v.val_die_ref.die = die;
+      a->dw_attr_val.v.val_die_ref.external = 0;
+      return 0;
+    }
+
   dw_attr_node *av = get_AT (die, DW_AT_location);
   dw_loc_list_ref d;
   bool non_dwarf_expr = false;
 
   if (av == NULL)
-    return -1;
+    return dwarf_strict ? -1 : 0;
   switch (AT_class (av))
     {
     case dw_val_class_loc_list:
@@ -28629,22 +28700,31 @@ optimize_string_length (dw_attr_node *a)
     case dw_val_class_loc:
       lv = AT_loc (av);
       if (lv == NULL)
-       return -1;
+       return dwarf_strict ? -1 : 0;
       if (non_dwarf_expression (lv))
        non_dwarf_expr = true;
       break;
     default:
-      return -1;
+      return dwarf_strict ? -1 : 0;
     }
 
-  /* If it is safe to keep DW_OP_call4 in, keep it.  */
+  /* If it is safe to transform DW_OP_GNU_variable_value DW_OP_stack_value
+     into DW_OP_call4  or DW_OP_GNU_variable_value into
+     DW_OP_call4 DW_OP_deref, do so.  */
   if (!non_dwarf_expr
-      && (l->dw_loc_next == NULL || AT_class (av) == dw_val_class_loc))
-    return 0;
+      && (l->dw_loc_next != NULL || AT_class (av) == dw_val_class_loc))
+    {
+      l->dw_loc_opc = DW_OP_call4;
+      if (l->dw_loc_next)
+       l->dw_loc_next = NULL;
+      else
+       l->dw_loc_next = new_loc_descr (DW_OP_deref, 0, 0);
+      return 0;
+    }
 
-  /* If not dereferencing the DW_OP_call4 afterwards, we can just
+  /* For DW_OP_GNU_variable_value DW_OP_stack_value, we can just
      copy over the DW_AT_location attribute from die to a.  */
-  if (l->dw_loc_next == NULL)
+  if (l->dw_loc_next != NULL)
     {
       a->dw_attr_val = av->dw_attr_val;
       return 1;
@@ -28658,23 +28738,25 @@ optimize_string_length (dw_attr_node *a)
       list = NULL;
       for (d = AT_loc_list (av); d != NULL; d = d->dw_loc_next)
        {
-         lv = copy_deref_exprloc (d->expr, l->dw_loc_next);
+         lv = copy_deref_exprloc (d->expr);
          if (lv)
            {
              *p = new_loc_list (lv, d->begin, d->end, d->section);
              p = &(*p)->dw_loc_next;
            }
+         else if (!dwarf_strict && d->expr)
+           return 0;
        }
       if (list == NULL)
-       return -1;
+       return dwarf_strict ? -1 : 0;
       a->dw_attr_val.val_class = dw_val_class_loc_list;
       gen_llsym (list);
       *AT_loc_list_ptr (a) = list;
       return 1;
     case dw_val_class_loc:
-      lv = copy_deref_exprloc (AT_loc (av), l->dw_loc_next);
+      lv = copy_deref_exprloc (AT_loc (av));
       if (lv == NULL)
-       return -1;
+       return dwarf_strict ? -1 : 0;
       a->dw_attr_val.v.val_loc = lv;
       return 1;
     default:
@@ -28720,7 +28802,7 @@ resolve_addr (dw_die_ref die)
            while (*curr)
              {
                gcc_assert (!(*curr)->replaced && !(*curr)->resolved_addr);
-               if (!resolve_addr_in_expr ((*curr)->expr))
+               if (!resolve_addr_in_expr (a, (*curr)->expr))
                  {
                    dw_loc_list_ref next = (*curr)->dw_loc_next;
                     dw_loc_descr_ref l = (*curr)->expr;
@@ -28757,19 +28839,19 @@ resolve_addr (dw_die_ref die)
       case dw_val_class_loc:
        {
          dw_loc_descr_ref l = AT_loc (a);
-         /* Using DW_OP_call4 or DW_OP_call4 DW_OP_deref in
-            DW_AT_string_length is only a rough approximation; unfortunately
-            DW_AT_string_length can't be a reference to a DIE.  DW_OP_call4
-            needs a DWARF expression, while DW_AT_location of the referenced
-            variable or argument might be any location description.  */
+         /* DW_OP_GNU_variable_value DW_OP_stack_value or
+            DW_OP_GNU_variable_value in DW_AT_string_length can be converted
+            into DW_OP_call4 or DW_OP_call4 DW_OP_deref, which is standard
+            DWARF4 unlike DW_OP_GNU_variable_value.  Or for DWARF5
+            DW_OP_GNU_variable_value DW_OP_stack_value can be replaced
+            with DW_FORM_ref referencing the same DIE as
+            DW_OP_GNU_variable_value used to reference.  */
          if (a->dw_attr == DW_AT_string_length
              && l
-             && l->dw_loc_opc == DW_OP_call4
-             && l->dw_loc_oprnd1.val_class == dw_val_class_die_ref
+             && l->dw_loc_opc == DW_OP_GNU_variable_value
              && (l->dw_loc_next == NULL
                  || (l->dw_loc_next->dw_loc_next == NULL
-                     && (l->dw_loc_next->dw_loc_opc == DW_OP_deref
-                         || l->dw_loc_next->dw_loc_opc != DW_OP_deref_size))))
+                     && l->dw_loc_next->dw_loc_opc == DW_OP_stack_value)))
            {
              switch (optimize_string_length (a))
                {
@@ -28799,7 +28881,7 @@ resolve_addr (dw_die_ref die)
               || l == NULL
               || l->dw_loc_opc != DW_OP_plus_uconst
               || l->dw_loc_next != NULL)
-             && !resolve_addr_in_expr (l))
+             && !resolve_addr_in_expr (a, l))
            {
              if (dwarf_split_debug_info)
                remove_loc_list_addr_table_entries (l);
@@ -28816,6 +28898,10 @@ resolve_addr (dw_die_ref die)
                  optimize_location_into_implicit_ptr (die, decl);
                  break;
                }
+             if (a->dw_attr == DW_AT_string_length)
+               /* If we drop DW_AT_string_length, we need to drop also
+                  DW_AT_{string_length_,}byte_size.  */
+               remove_AT_byte_size = true;
              remove_AT (die, a->dw_attr);
              ix--;
            }
@@ -29874,6 +29960,262 @@ dwarf2out_finish (const char *)
     }
 }
 
+/* Returns a hash value for X (which really is a variable_value_struct).  */
+
+inline hashval_t
+variable_value_hasher::hash (variable_value_struct *x)
+{
+  return (hashval_t) x->decl_id;
+}
+
+/* Return nonzero if decl_id of variable_value_struct X is the same as
+   UID of decl Y.  */
+
+inline bool
+variable_value_hasher::equal (variable_value_struct *x, tree y)
+{
+  return x->decl_id == DECL_UID (y);
+}
+
+/* Helper function for resolve_variable_value, handle
+   DW_OP_GNU_variable_value in one location expression.
+   Return true if exprloc has been changed into loclist.  */
+
+static bool
+resolve_variable_value_in_expr (dw_attr_node *a, dw_loc_descr_ref loc)
+{
+  dw_loc_descr_ref next;
+  for (dw_loc_descr_ref prev = NULL; loc; prev = loc, loc = next)
+    {
+      next = loc->dw_loc_next;
+      if (loc->dw_loc_opc != DW_OP_GNU_variable_value
+         || loc->dw_loc_oprnd1.val_class != dw_val_class_decl_ref)
+       continue;
+
+      tree decl = loc->dw_loc_oprnd1.v.val_decl_ref;
+      if (DECL_CONTEXT (decl) != current_function_decl)
+       continue;
+
+      dw_die_ref ref = lookup_decl_die (decl);
+      if (ref)
+       {
+         loc->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+         loc->dw_loc_oprnd1.v.val_die_ref.die = ref;
+         loc->dw_loc_oprnd1.v.val_die_ref.external = 0;
+         continue;
+       }
+      dw_loc_list_ref l = loc_list_from_tree (decl, 0, NULL);
+      if (l == NULL)
+       continue;
+      if (l->dw_loc_next)
+       {
+         if (AT_class (a) != dw_val_class_loc)
+           continue;
+         switch (a->dw_attr)
+           {
+           /* Following attributes allow both exprloc and loclist
+              classes, so we can change them into a loclist.  */
+           case DW_AT_location:
+           case DW_AT_string_length:
+           case DW_AT_return_addr:
+           case DW_AT_data_member_location:
+           case DW_AT_frame_base:
+           case DW_AT_segment:
+           case DW_AT_static_link:
+           case DW_AT_use_location:
+           case DW_AT_vtable_elem_location:
+             if (prev)
+               {
+                 prev->dw_loc_next = NULL;
+                 prepend_loc_descr_to_each (l, AT_loc (a));
+               }
+             if (next)
+               add_loc_descr_to_each (l, next);
+             a->dw_attr_val.val_class = dw_val_class_loc_list;
+             a->dw_attr_val.val_entry = NULL;
+             a->dw_attr_val.v.val_loc_list = l;
+             have_location_lists = true;
+             return true;
+           /* Following attributes allow both exprloc and reference,
+              so if the whole expression is DW_OP_GNU_variable_value alone
+              we could transform it into reference.  */
+           case DW_AT_byte_size:
+           case DW_AT_bit_size:
+           case DW_AT_lower_bound:
+           case DW_AT_upper_bound:
+           case DW_AT_bit_stride:
+           case DW_AT_count:
+           case DW_AT_allocated:
+           case DW_AT_associated:
+           case DW_AT_byte_stride:
+             if (prev == NULL && next == NULL)
+               break;
+             /* FALLTHRU */
+           default:
+             if (dwarf_strict)
+               continue;
+             break;
+           }
+         /* Create DW_TAG_variable that we can refer to.  */
+         ref = gen_decl_die (decl, NULL_TREE, NULL,
+                             lookup_decl_die (current_function_decl));
+         if (ref)
+           {
+             loc->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+             loc->dw_loc_oprnd1.v.val_die_ref.die = ref;
+             loc->dw_loc_oprnd1.v.val_die_ref.external = 0;
+           }
+         continue;
+       }
+      if (prev)
+       {
+         prev->dw_loc_next = l->expr;
+         add_loc_descr (&prev->dw_loc_next, next);
+         free_loc_descr (loc, NULL);
+         next = prev->dw_loc_next;
+       }
+      else
+       {
+         memcpy (loc, l->expr, sizeof (dw_loc_descr_node));
+         add_loc_descr (&loc, next);
+         next = loc;
+       }
+      loc = prev;
+    }
+  return false;
+}
+
+/* Attempt to resolve DW_OP_GNU_variable_value using loc_list_from_tree.  */
+
+static void
+resolve_variable_value (dw_die_ref die)
+{
+  dw_attr_node *a;
+  dw_loc_list_ref loc;
+  unsigned ix;
+
+  FOR_EACH_VEC_SAFE_ELT (die->die_attr, ix, a)
+    switch (AT_class (a))
+      {
+      case dw_val_class_loc:
+       if (!resolve_variable_value_in_expr (a, AT_loc (a)))
+         break;
+       /* FALLTHRU */
+      case dw_val_class_loc_list:
+       loc = AT_loc_list (a);
+       gcc_assert (loc);
+       for (; loc; loc = loc->dw_loc_next)
+         resolve_variable_value_in_expr (a, loc->expr);
+       break;
+      default:
+       break;
+      }
+}
+
+/* Attempt to optimize DW_OP_GNU_variable_value refering to
+   temporaries in the current function.  */
+
+static void
+resolve_variable_values (void)
+{
+  if (!variable_value_hash || !current_function_decl)
+    return;
+
+  struct variable_value_struct *node
+    = variable_value_hash->find_with_hash (current_function_decl,
+                                          DECL_UID (current_function_decl));
+
+  if (node == NULL)
+    return;
+
+  unsigned int i;
+  dw_die_ref die;
+  FOR_EACH_VEC_SAFE_ELT (node->dies, i, die)
+    resolve_variable_value (die);
+}
+
+/* Helper function for note_variable_value, handle one location
+   expression.  */
+
+static void
+note_variable_value_in_expr (dw_die_ref die, dw_loc_descr_ref loc)
+{
+  for (; loc; loc = loc->dw_loc_next)
+    if (loc->dw_loc_opc == DW_OP_GNU_variable_value
+       && loc->dw_loc_oprnd1.val_class == dw_val_class_decl_ref)
+      {
+       tree decl = loc->dw_loc_oprnd1.v.val_decl_ref;
+       dw_die_ref ref = lookup_decl_die (decl);
+       if (ref)
+         {
+           loc->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+           loc->dw_loc_oprnd1.v.val_die_ref.die = ref;
+           loc->dw_loc_oprnd1.v.val_die_ref.external = 0;
+         }
+       if (VAR_P (decl)
+           && DECL_CONTEXT (decl)
+           && TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL
+           && lookup_decl_die (DECL_CONTEXT (decl)))
+         {
+           if (!variable_value_hash)
+             variable_value_hash
+               = hash_table<variable_value_hasher>::create_ggc (10);
+
+           tree fndecl = DECL_CONTEXT (decl);
+           struct variable_value_struct *node;
+           struct variable_value_struct **slot
+             = variable_value_hash->find_slot_with_hash (fndecl,
+                                                         DECL_UID (fndecl),
+                                                         INSERT);
+           if (*slot == NULL)
+             {
+               node = ggc_cleared_alloc<variable_value_struct> ();
+               node->decl_id = DECL_UID (fndecl);
+               *slot = node;
+             }
+           else
+             node = *slot;
+
+           vec_safe_push (node->dies, die);
+         }
+      }
+}
+
+/* Walk the tree DIE and note DIEs with DW_OP_GNU_variable_value still
+   with dw_val_class_decl_ref operand.  */
+
+static void
+note_variable_value (dw_die_ref die)
+{
+  dw_die_ref c;
+  dw_attr_node *a;
+  dw_loc_list_ref loc;
+  unsigned ix;
+
+  FOR_EACH_VEC_SAFE_ELT (die->die_attr, ix, a)
+    switch (AT_class (a))
+      {
+      case dw_val_class_loc_list:
+       loc = AT_loc_list (a);
+       gcc_assert (loc);
+       if (!loc->noted_variable_value)
+         {
+           loc->noted_variable_value = 1;
+           for (; loc; loc = loc->dw_loc_next)
+             note_variable_value_in_expr (die, loc->expr);
+         }
+       break;
+      case dw_val_class_loc:
+       note_variable_value_in_expr (die, AT_loc (a));
+       break;
+      default:
+       break;
+      }
+
+  /* Mark children.  */
+  FOR_EACH_CHILD (die, c, note_variable_value (c));
+}
+
 /* Perform any cleanups needed after the early debug generation pass
    has run.  */
 
@@ -30000,6 +30342,17 @@ dwarf2out_early_finish (const char *filename)
        }
     }
 
+  /* Traverse the DIE's and note DIEs with DW_OP_GNU_variable_value still
+     with dw_val_class_decl_ref operand.  */
+  note_variable_value (comp_unit_die ());
+  for (limbo_die_node *node = cu_die_list; node; node = node->next)
+    note_variable_value (node->die);
+  for (comdat_type_node *ctnode = comdat_type_list; ctnode != NULL;
+       ctnode = ctnode->next)
+    note_variable_value (ctnode->root_die);
+  for (limbo_die_node *node = limbo_die_list; node; node = node->next)
+    note_variable_value (node->die);
+
   /* The early debug phase is now finished.  */
   early_dwarf_finished = true;
 }
index 43b20717b69dc5f2cc3a428767f0dbf0fd4d60cb..2e927cbd5a1a56e17fad15a0c7383d1e720c6238 100644 (file)
@@ -1,3 +1,8 @@
+2017-02-25  Jakub Jelinek  <jakub@redhat.com>
+
+       PR debug/77589
+       * dwarf2.def (DW_OP_GNU_variable_value): New opcode.
+
 2017-01-30  Alexandre Oliva <aoliva@redhat.com>
 
        Introduce C++ support in libcc1.
index ddadaccb25c2324c23b6fdc24b56b6e9127b5c9c..ea6194ef33e5a82f5159090aeb967fc7acefb239 100644 (file)
@@ -675,6 +675,9 @@ DW_OP (DW_OP_GNU_parameter_ref, 0xfa)
 /* Extensions for Fission.  See http://gcc.gnu.org/wiki/DebugFission.  */
 DW_OP (DW_OP_GNU_addr_index, 0xfb)
 DW_OP (DW_OP_GNU_const_index, 0xfc)
+/* The GNU variable value extension.
+   See http://dwarfstd.org/ShowIssue.php?issue=161109.2 . */
+DW_OP (DW_OP_GNU_variable_value, 0xfd)
 /* HP extensions.  */
 DW_OP_DUP (DW_OP_HP_unknown, 0xe0) /* Ouch, the same as GNU_push_tls_address.  */
 DW_OP (DW_OP_HP_is_value, 0xe1)