This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Fix regressions caused by early debug (DW_OP_GNU_variable_value extension 161109.2; PR debug/77589)


Hi!

As mentioned in the PR (and other PRs related to DW_AT_string_length, for
which a partial workaround has been committed already), the early debug
approach regresses the debug info for VLAs and other dynamic properties
of the types; when trying to emit DIEs for such types early, we don't
have RTL information for the various artificial temporaries used in
TYPE_SIZE_UNIT etc. and those variables don't have a DIE either and
the current way of expanding that is that we drop DWARF expressions
or location descriptions that need that on the floor.  We have some hack
to add it to VLA DW_AT_upper_bound, but it doesn't really work too well
and can't cover all the various attributes.  For DWARF5 DW_AT_string_length
now can be newly a reference, which fixes it if we don't need to dereference
it, but this PR is about DW_AT_byte_stride where the value of the artificial
variable is needed in the middle of a complex expression.  Especially when
looking forward to GCC 8? LTO debug changes where the type DIEs ought to be
imutable after being emitted early, this patch introduces a new DWARF
extension (proposed for DWARF5 as
http://www.dwarfstd.org/ShowIssue.php?issue=161109.2
but deferred to DWARF6, so we want to implement it as an extension for now
and then codify in DWARF6 after getting implementation experience).

The extension is DW_OP_GNU_variable_value, a new DWARF expression opcode,
which has a single argument - a DIE reference (like DW_OP_call_ref,
DW_OP_implicit_pointer or DW_OP_GNU_parameter).  The intended behavior is
that the debug info consumer computes the value of that referenced variable
at the current PC, and if it can compute it and pushes the value as
a generic type integer into the DWARF stack (it is really only meaningful
when referring to integral/pointer typed variables).  It doesn't have to be
using DW_AT_location or DW_AT_const_value exactly on the referenced
DIE, it can be from other DIE that supplies location for that DIE etc.,
simply the debugger should use its standard means for computing a value of
something, similarly how it handles e.g. reference class (DW_FORM_ref)
DW_AT_upper_bound etc.  If the value of the variable can't be computed at
the current PC (missing DW_AT_location/DW_AT_const_value, or no info
for the current PC, whatever), then it is expected that the whole DWARF
expression containing DW_OP_GNU_variable_value will be handled as something
that can't be computed (<optimized away> or whatever).

The patch is larger than I hoped in order to avoid emitting
DW_OP_GNU_variable_value everywhere, even in places where we could emit
something equivalent (which of course breaks if the debugger doesn't
support the extension).  The patch removes some ugly hacks I've added
earlier for DW_AT_string_length, and uses DW_OP_GNU_variable_value
internally and then attempts to transform it to something else.  Initially
when it is created, the referenced decl might not have a DIE, so it is
created either as a decl reference or die reference depending on
lookup_decl_die.  Then there is new code at the end of
dwarf2out_early_finish, which looks for all DW_OP_GNU_variable_value which
are still decl references and don't have corresponding dies (this is
note_variable_value{,_in_expr}).  For GCC8 and -flto, the intent is to create
DIEs for the artificial temporaries here, otherwise we just note it in
a hash table.  Another step (resolve_variable_value{s,,_in_expr}) is during
gen_subprogram_die during emitting assembly for a function, here we use the
above prepared hash table and try to do something about all
DW_OP_GNU_variable_value pointing to temporaries in the current function.
Here we do have RTL for the current function, so can use loc_list_from_tree
and can in certain cases (especially if it returns a single location list)
replace the DW_OP_GNU_variable_value with DWARF expression computing that
value.  For attributes that support both exprloc and loclist classes like
DW_AT_location, we can also replace a DW_FORM_exprloc with a loclist, and
there is also an option to create a DIE for the artificial temporary under
the current DW_TAG_subprogram.  And finally there is another step during
resolve_addr during dwarf2out_finish, where we optimize DW_AT_string_length
(slightly adjusted from earlier, because that was adjustment for the
often invalid DW_OP_call4 + optional DW_OP_deref*, while now we optimize
DW_OP_GNU_variable_value + optional DW_OP_stack_value; and it now also
allows for -gdwarf-5 to use DW_FORM_ref), where we also try to look up
referenced DIE again, transform it for certain dynamic attributes that
accept both exprloc and reference from exprloc of DW_OP_GNU_variable
to just reference and for -gstrict-dwarf drop DW_OP_GNU_variable together
with whole expression if we couldn't transform it to something strict DWARF
compliant.

For the Fortran testcase in the PR, DW_OP_GNU_variable is what remains
in the debug info, so for that we'll need corresponding debugger changes.

Bootstrapped/regtested successfully on x86_64-linux and i686-linux (without
the resolve_variable_value{s,,_in_expr} and note_variable_value{,_in_expr}
additions there were quite a few regressions especially in guality testsuite,
but now there aren't).  Ok for trunk?

2017-02-24  Jakub Jelinek  <jakub@redhat.com>

	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.
	(add_loc_descr_to_each): Clarify comment.
	(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.

--- include/dwarf2.def.jj	2017-02-23 23:01:14.410952922 +0100
+++ include/dwarf2.def	2017-02-24 09:37:58.943132731 +0100
@@ -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)
--- gcc/dwarf2out.c.jj	2017-02-23 23:00:48.802287878 +0100
+++ gcc/dwarf2out.c	2017-02-24 10:01:22.538447008 +0100
@@ -1293,6 +1293,8 @@ typedef struct GTY(()) dw_loc_list_struc
   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 lo
       }
       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<di
    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_vali
 
 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_par
 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,7 @@ single_element_loc_list_p (dw_loc_list_r
   return !list->ll_symbol;
 }
 
-/* To each location in list LIST add loc descr REF.  */
+/* 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)
@@ -16316,13 +16344,41 @@ add_loc_descr_to_each (dw_loc_list_ref l
     }
 }
 
+/* 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;
+      copy = ggc_alloc<dw_loc_descr_node> ();
+      memcpy (copy, ref, sizeof (dw_loc_descr_node));
+      list->expr = copy;
+      while (copy->dw_loc_next != ref_end)
+	{
+	  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->dw_loc_next = end;
+      list = list->dw_loc_next;
+    }
+}
+
 /* Given two lists RET and LIST
    produce location list that is result of adding expression in LIST
    to expression in RET on each position in program.
    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 +16400,7 @@ add_loc_list (dw_loc_list_ref *ret, dw_l
     }
   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 +16880,7 @@ resolve_args_picking_1 (dw_loc_descr_ref
 	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 +17356,31 @@ loc_list_from_tree_1 (tree loc, int want
 	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 +17955,7 @@ loc_list_from_tree (tree loc, int want_a
   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 +20766,6 @@ gen_array_type_die (tree type, dw_die_re
 	{
 	  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 +20774,8 @@ gen_array_type_die (tree type, dw_die_re
 		  && 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 +20783,9 @@ gen_array_type_die (tree type, dw_die_re
 	    }
 	  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 +20862,6 @@ gen_array_type_die (tree type, dw_die_re
   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 +22304,8 @@ gen_subprogram_die (tree decl, dw_die_re
 	  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 +22338,6 @@ gen_subprogram_die (tree decl, dw_die_re
       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 +22400,6 @@ gen_subprogram_die (tree decl, dw_die_re
 	  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 +27538,18 @@ prune_unused_types_walk_loc_descr (dw_lo
 	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 +28318,7 @@ optimize_one_addr_into_implicit_ptr (dw_
    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 +28378,7 @@ resolve_addr_in_expr (dw_loc_descr_ref l
       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 +28389,37 @@ resolve_addr_in_expr (dw_loc_descr_ref l
 	    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 +28594,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 +28616,24 @@ copy_deref_exprloc (dw_loc_descr_ref exp
   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 +28644,55 @@ copy_deref_exprloc (dw_loc_descr_ref exp
       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 +28703,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 +28741,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 +28805,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 +28842,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 +28884,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 +28901,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 +29963,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 +30345,17 @@ dwarf2out_early_finish (const char *file
 	}
     }
 
+  /* 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;
 }

	Jakub


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]