aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2017-02-25 09:18:24 +0100
committerJakub Jelinek <jakub@gcc.gnu.org>2017-02-25 09:18:24 +0100
commit680c60feb43035aa3b1ee6ac3bcc9055bf50fd5e (patch)
tree5fb3988baa9b0eb009e0de677ef621c40c56981e
parenta920ed32a76182218dff13bb0b2ee1f21532559b (diff)
re PR debug/77589 (fortran: Missing DW_AT_byte_stride for an array record field selection)
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
-rw-r--r--gcc/ChangeLog43
-rw-r--r--gcc/dwarf2out.c637
-rw-r--r--include/ChangeLog5
-rw-r--r--include/dwarf2.def3
4 files changed, 546 insertions, 142 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 5f0ee4f2816..a38fd2063ec 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index 541d86824bd..87060a33804 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -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;
}
diff --git a/include/ChangeLog b/include/ChangeLog
index 43b20717b69..2e927cbd5a1 100644
--- a/include/ChangeLog
+++ b/include/ChangeLog
@@ -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.
diff --git a/include/dwarf2.def b/include/dwarf2.def
index ddadaccb25c..ea6194ef33e 100644
--- a/include/dwarf2.def
+++ b/include/dwarf2.def
@@ -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)