aboutsummaryrefslogtreecommitdiff
path: root/gcc/dwarf2out.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/dwarf2out.c')
-rw-r--r--gcc/dwarf2out.c374
1 files changed, 360 insertions, 14 deletions
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index 3319c61..7e257c3 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -92,6 +92,7 @@ along with GCC; see the file COPYING3. If not see
#include "gimple.h"
#include "tree-pass.h"
#include "tree-flow.h"
+#include "cfglayout.h"
static void dwarf2out_source_line (unsigned int, const char *, int, bool);
static rtx last_var_location_insn;
@@ -4794,6 +4795,8 @@ dwarf_stack_op_name (unsigned int op)
return "DW_OP_GNU_encoded_addr";
case DW_OP_GNU_implicit_pointer:
return "DW_OP_GNU_implicit_pointer";
+ case DW_OP_GNU_entry_value:
+ return "DW_OP_GNU_entry_value";
default:
return "OP_<unknown>";
@@ -4900,6 +4903,8 @@ loc_list_plus_const (dw_loc_list_ref list_head, HOST_WIDE_INT offset)
#define DWARF_REF_SIZE \
(dwarf_version == 2 ? DWARF2_ADDR_SIZE : DWARF_OFFSET_SIZE)
+static unsigned long size_of_locs (dw_loc_descr_ref);
+
/* Return the size of a location descriptor. */
static unsigned long
@@ -5015,6 +5020,12 @@ size_of_loc_descr (dw_loc_descr_ref loc)
case DW_OP_GNU_implicit_pointer:
size += DWARF_REF_SIZE + size_of_sleb128 (loc->dw_loc_oprnd2.v.val_int);
break;
+ case DW_OP_GNU_entry_value:
+ {
+ unsigned long op_size = size_of_locs (loc->dw_loc_oprnd1.v.val_loc);
+ size += size_of_uleb128 (op_size) + op_size;
+ break;
+ }
default:
break;
}
@@ -5052,6 +5063,7 @@ size_of_locs (dw_loc_descr_ref loc)
static HOST_WIDE_INT extract_int (const unsigned char *, unsigned);
static void get_ref_die_offset_label (char *, dw_die_ref);
+static void output_loc_sequence (dw_loc_descr_ref, int);
/* Output location description stack opcode's operands (if any).
The for_eh_or_skip parameter controls whether register numbers are
@@ -5301,6 +5313,11 @@ output_loc_operands (dw_loc_descr_ref loc, int for_eh_or_skip)
}
break;
+ case DW_OP_GNU_entry_value:
+ dw2_asm_output_data_uleb128 (size_of_locs (val1->v.val_loc), NULL);
+ output_loc_sequence (val1->v.val_loc, for_eh_or_skip);
+ break;
+
default:
/* Other codes have no operands. */
break;
@@ -5477,6 +5494,7 @@ output_loc_operands_raw (dw_loc_descr_ref loc)
break;
case DW_OP_GNU_implicit_pointer:
+ case DW_OP_GNU_entry_value:
gcc_unreachable ();
break;
@@ -6115,10 +6133,33 @@ struct GTY (()) var_loc_list_def {
};
typedef struct var_loc_list_def var_loc_list;
+/* Call argument location list. */
+struct GTY ((chain_next ("%h.next"))) call_arg_loc_node {
+ rtx GTY (()) call_arg_loc_note;
+ const char * GTY (()) label;
+ tree GTY (()) block;
+ bool tail_call_p;
+ rtx GTY (()) symbol_ref;
+ struct call_arg_loc_node * GTY (()) next;
+};
+
/* Table of decl location linked lists. */
static GTY ((param_is (var_loc_list))) htab_t decl_loc_table;
+/* Head and tail of call_arg_loc chain. */
+static GTY (()) struct call_arg_loc_node *call_arg_locations;
+static struct call_arg_loc_node *call_arg_loc_last;
+
+/* Number of call sites in the current function. */
+static int call_site_count = -1;
+/* Number of tail call sites in the current function. */
+static int tail_call_site_count = -1;
+
+/* Vector mapping block numbers to DW_TAG_{lexical_block,inlined_subroutine}
+ DIEs. */
+static VEC (dw_die_ref, heap) *block_map;
+
/* A pointer to the base of a list of references to DIE's that
are uniquely identified by their tag, presence/absence of
children DIE's, and list of attribute/value pairs. */
@@ -6907,6 +6948,10 @@ dwarf_tag_name (unsigned int tag)
return "DW_TAG_GNU_EINCL";
case DW_TAG_GNU_template_template_param:
return "DW_TAG_GNU_template_template_param";
+ case DW_TAG_GNU_call_site:
+ return "DW_TAG_GNU_call_site";
+ case DW_TAG_GNU_call_site_parameter:
+ return "DW_TAG_GNU_call_site_parameter";
default:
return "DW_TAG_<unknown>";
}
@@ -7151,6 +7196,22 @@ dwarf_attr_name (unsigned int attr)
return "DW_AT_GNU_odr_signature";
case DW_AT_GNU_template_name:
return "DW_AT_GNU_template_name";
+ case DW_AT_GNU_call_site_value:
+ return "DW_AT_GNU_call_site_value";
+ case DW_AT_GNU_call_site_data_value:
+ return "DW_AT_GNU_call_site_data_value";
+ case DW_AT_GNU_call_site_target:
+ return "DW_AT_GNU_call_site_target";
+ case DW_AT_GNU_call_site_target_clobbered:
+ return "DW_AT_GNU_call_site_target_clobbered";
+ case DW_AT_GNU_tail_call:
+ return "DW_AT_GNU_tail_call";
+ case DW_AT_GNU_all_tail_call_sites:
+ return "DW_AT_GNU_all_tail_call_sites";
+ case DW_AT_GNU_all_call_sites:
+ return "DW_AT_GNU_all_call_sites";
+ case DW_AT_GNU_all_source_call_sites:
+ return "DW_AT_GNU_all_source_call_sites";
case DW_AT_VMS_rtnbeg_pd_address:
return "DW_AT_VMS_rtnbeg_pd_address";
@@ -13964,6 +14025,26 @@ mem_loc_descriptor (rtx rtl, enum machine_mode mode,
"CONCAT/CONCATN/VAR_LOCATION is handled only by loc_descriptor");
return 0;
+ case ENTRY_VALUE:
+ mem_loc_result = new_loc_descr (DW_OP_GNU_entry_value, 0, 0);
+ mem_loc_result->dw_loc_oprnd1.val_class = dw_val_class_loc;
+ if (REG_P (XEXP (rtl, 0)))
+ mem_loc_result->dw_loc_oprnd1.v.val_loc
+ = one_reg_loc_descriptor (dbx_reg_number (XEXP (rtl, 0)),
+ VAR_INIT_STATUS_INITIALIZED);
+ else if (MEM_P (XEXP (rtl, 0)) && REG_P (XEXP (XEXP (rtl, 0), 0)))
+ {
+ dw_loc_descr_ref ref
+ = mem_loc_descriptor (XEXP (rtl, 0), GET_MODE (rtl),
+ VAR_INIT_STATUS_INITIALIZED);
+ if (ref == NULL)
+ return NULL;
+ mem_loc_result->dw_loc_oprnd1.v.val_loc = ref;
+ }
+ else
+ gcc_unreachable ();
+ return mem_loc_result;
+
case PRE_MODIFY:
/* Extract the PLUS expression nested inside and fall into
PLUS code below. */
@@ -17842,8 +17923,11 @@ add_linkage_attr (dw_die_ref die, tree decl)
static void
add_src_coords_attributes (dw_die_ref die, tree decl)
{
- expanded_location s = expand_location (DECL_SOURCE_LOCATION (decl));
+ expanded_location s;
+ if (DECL_SOURCE_LOCATION (decl) == UNKNOWN_LOCATION)
+ return;
+ s = expand_location (DECL_SOURCE_LOCATION (decl));
add_AT_file (die, DW_AT_decl_file, lookup_filename (s.file));
add_AT_unsigned (die, DW_AT_decl_line, s.line);
}
@@ -18872,6 +18956,8 @@ dwarf2out_abstract_function (tree decl)
tree context;
int was_abstract;
htab_t old_decl_loc_table;
+ int old_call_site_count, old_tail_call_site_count;
+ struct call_arg_loc_node *old_call_arg_locations;
/* Make sure we have the actual abstract inline, not a clone. */
decl = DECL_ORIGIN (decl);
@@ -18886,6 +18972,12 @@ dwarf2out_abstract_function (tree decl)
get locations in abstract instantces. */
old_decl_loc_table = decl_loc_table;
decl_loc_table = NULL;
+ old_call_arg_locations = call_arg_locations;
+ call_arg_locations = NULL;
+ old_call_site_count = call_site_count;
+ call_site_count = -1;
+ old_tail_call_site_count = tail_call_site_count;
+ tail_call_site_count = -1;
/* Be sure we've emitted the in-class declaration DIE (if any) first, so
we don't get confused by DECL_ABSTRACT. */
@@ -18910,6 +19002,9 @@ dwarf2out_abstract_function (tree decl)
current_function_decl = save_fn;
decl_loc_table = old_decl_loc_table;
+ call_arg_locations = old_call_arg_locations;
+ call_site_count = old_call_site_count;
+ tail_call_site_count = old_tail_call_site_count;
pop_cfun ();
}
@@ -18985,6 +19080,43 @@ premark_types_used_by_global_vars (void)
premark_types_used_by_global_vars_helper, NULL);
}
+/* Generate a DW_TAG_GNU_call_site DIE in function DECL under SUBR_DIE
+ for CA_LOC call arg loc node. */
+
+static dw_die_ref
+gen_call_site_die (tree decl, dw_die_ref subr_die,
+ struct call_arg_loc_node *ca_loc)
+{
+ dw_die_ref stmt_die = NULL, die;
+ tree block = ca_loc->block;
+
+ while (block
+ && block != DECL_INITIAL (decl)
+ && TREE_CODE (block) == BLOCK)
+ {
+ if (VEC_length (dw_die_ref, block_map) > BLOCK_NUMBER (block))
+ stmt_die = VEC_index (dw_die_ref, block_map, BLOCK_NUMBER (block));
+ if (stmt_die)
+ break;
+ block = BLOCK_SUPERCONTEXT (block);
+ }
+ if (stmt_die == NULL)
+ stmt_die = subr_die;
+ die = new_die (DW_TAG_GNU_call_site, stmt_die, NULL_TREE);
+ add_AT_lbl_id (die, DW_AT_low_pc, ca_loc->label);
+ if (ca_loc->tail_call_p)
+ add_AT_flag (die, DW_AT_GNU_tail_call, 1);
+ if (ca_loc->symbol_ref)
+ {
+ dw_die_ref tdie = lookup_decl_die (SYMBOL_REF_DECL (ca_loc->symbol_ref));
+ if (tdie)
+ add_AT_die_ref (die, DW_AT_abstract_origin, tdie);
+ else
+ add_AT_addr (die, DW_AT_abstract_origin, ca_loc->symbol_ref);
+ }
+ return die;
+}
+
/* Generate a DIE to represent a declared function (either file-scope or
block-local). */
@@ -19467,12 +19599,113 @@ gen_subprogram_die (tree decl, dw_die_ref context_die)
constructor function. */
if (! declaration && TREE_CODE (outer_scope) != ERROR_MARK)
{
+ int call_site_note_count = 0;
+ int tail_call_site_note_count = 0;
+
/* Emit a DW_TAG_variable DIE for a named return value. */
if (DECL_NAME (DECL_RESULT (decl)))
gen_decl_die (DECL_RESULT (decl), NULL, subr_die);
current_function_has_inlines = 0;
decls_for_scope (outer_scope, subr_die, 0);
+
+ if (call_arg_locations)
+ {
+ struct call_arg_loc_node *ca_loc;
+ for (ca_loc = call_arg_locations; ca_loc; ca_loc = ca_loc->next)
+ {
+ dw_die_ref die = NULL;
+ rtx tloc = NULL_RTX;
+ rtx arg, next_arg;
+
+ for (arg = NOTE_VAR_LOCATION (ca_loc->call_arg_loc_note);
+ arg; arg = next_arg)
+ {
+ dw_loc_descr_ref reg, val;
+ enum machine_mode mode = GET_MODE (XEXP (XEXP (arg, 0), 1));
+ dw_die_ref cdie;
+
+ next_arg = XEXP (arg, 1);
+ if (REG_P (XEXP (XEXP (arg, 0), 0))
+ && next_arg
+ && MEM_P (XEXP (XEXP (next_arg, 0), 0))
+ && REG_P (XEXP (XEXP (XEXP (next_arg, 0), 0), 0))
+ && REGNO (XEXP (XEXP (arg, 0), 0))
+ == REGNO (XEXP (XEXP (XEXP (next_arg, 0), 0), 0)))
+ next_arg = XEXP (next_arg, 1);
+ if (mode == VOIDmode)
+ mode = GET_MODE (XEXP (XEXP (arg, 0), 0));
+ if (GET_MODE_CLASS (mode) != MODE_INT
+ || GET_MODE_SIZE (mode) > DWARF2_ADDR_SIZE)
+ continue;
+ if (XEXP (XEXP (arg, 0), 0) == pc_rtx)
+ {
+ gcc_assert (ca_loc->symbol_ref == NULL_RTX);
+ tloc = XEXP (XEXP (arg, 0), 1);
+ continue;
+ }
+ if (REG_P (XEXP (XEXP (arg, 0), 0)))
+ reg = reg_loc_descriptor (XEXP (XEXP (arg, 0), 0),
+ VAR_INIT_STATUS_INITIALIZED);
+ else if (MEM_P (XEXP (XEXP (arg, 0), 0)))
+ reg = mem_loc_descriptor (XEXP (XEXP (XEXP (arg, 0),
+ 0), 0), mode,
+ VAR_INIT_STATUS_INITIALIZED);
+ else
+ continue;
+ if (reg == NULL)
+ continue;
+ val = mem_loc_descriptor (XEXP (XEXP (arg, 0), 1), VOIDmode,
+ VAR_INIT_STATUS_INITIALIZED);
+ if (val == NULL)
+ continue;
+ if (die == NULL)
+ die = gen_call_site_die (decl, subr_die, ca_loc);
+ cdie = new_die (DW_TAG_GNU_call_site_parameter, die,
+ NULL_TREE);
+ add_AT_loc (cdie, DW_AT_location, reg);
+ add_AT_loc (cdie, DW_AT_GNU_call_site_value, val);
+ if (next_arg != XEXP (arg, 1))
+ {
+ val = mem_loc_descriptor (XEXP (XEXP (XEXP (arg, 1),
+ 0), 1), VOIDmode,
+ VAR_INIT_STATUS_INITIALIZED);
+ if (val != NULL)
+ add_AT_loc (cdie, DW_AT_GNU_call_site_data_value, val);
+ }
+ }
+ if (die == NULL
+ && (ca_loc->symbol_ref || tloc))
+ die = gen_call_site_die (decl, subr_die, ca_loc);
+ if (die != NULL && tloc != NULL_RTX)
+ {
+ dw_loc_descr_ref tval
+ = mem_loc_descriptor (tloc, VOIDmode,
+ VAR_INIT_STATUS_INITIALIZED);
+ if (tval)
+ add_AT_loc (die, DW_AT_GNU_call_site_target, tval);
+ }
+ if (die != NULL)
+ {
+ call_site_note_count++;
+ if (ca_loc->tail_call_p)
+ tail_call_site_note_count++;
+ }
+ }
+ call_arg_locations = NULL;
+ call_arg_loc_last = NULL;
+ }
+ if (tail_call_site_count >= 0
+ && tail_call_site_count == tail_call_site_note_count)
+ {
+ if (call_site_count >= 0
+ && call_site_count == call_site_note_count)
+ add_AT_flag (subr_die, DW_AT_GNU_all_call_sites, 1);
+ else
+ add_AT_flag (subr_die, DW_AT_GNU_all_tail_call_sites, 1);
+ }
+ call_site_count = -1;
+ tail_call_site_count = -1;
}
/* Add the calling convention attribute if requested. */
add_calling_convention_attribute (subr_die, decl);
@@ -19861,6 +20094,14 @@ gen_lexical_block_die (tree stmt, dw_die_ref context_die, int depth)
{
dw_die_ref stmt_die = new_die (DW_TAG_lexical_block, context_die, stmt);
+ if (call_arg_locations)
+ {
+ if (VEC_length (dw_die_ref, block_map) <= BLOCK_NUMBER (stmt))
+ VEC_safe_grow_cleared (dw_die_ref, heap, block_map,
+ BLOCK_NUMBER (stmt) + 1);
+ VEC_replace (dw_die_ref, block_map, BLOCK_NUMBER (stmt), stmt_die);
+ }
+
if (! BLOCK_ABSTRACT (stmt) && TREE_ASM_WRITTEN (stmt))
add_high_low_attributes (stmt, stmt_die);
@@ -19891,6 +20132,13 @@ gen_inlined_subroutine_die (tree stmt, dw_die_ref context_die, int depth)
dw_die_ref subr_die
= new_die (DW_TAG_inlined_subroutine, context_die, stmt);
+ if (call_arg_locations)
+ {
+ if (VEC_length (dw_die_ref, block_map) <= BLOCK_NUMBER (stmt))
+ VEC_safe_grow_cleared (dw_die_ref, heap, block_map,
+ BLOCK_NUMBER (stmt) + 1);
+ VEC_replace (dw_die_ref, block_map, BLOCK_NUMBER (stmt), subr_die);
+ }
add_abstract_origin_attribute (subr_die, decl);
if (TREE_ASM_WRITTEN (stmt))
add_high_low_attributes (stmt, subr_die);
@@ -21502,7 +21750,11 @@ static void
dwarf2out_function_decl (tree decl)
{
dwarf2out_decl (decl);
-
+ call_arg_locations = NULL;
+ call_arg_loc_last = NULL;
+ call_site_count = -1;
+ tail_call_site_count = -1;
+ VEC_free (dw_die_ref, heap, block_map);
htab_empty (decl_loc_table);
}
@@ -21899,16 +22151,35 @@ dwarf2out_var_location (rtx loc_note)
static const char *last_postcall_label;
static bool last_in_cold_section_p;
tree decl;
+ bool var_loc_p;
+
+ if (!NOTE_P (loc_note))
+ {
+ if (CALL_P (loc_note))
+ {
+ call_site_count++;
+ if (SIBLING_CALL_P (loc_note))
+ tail_call_site_count++;
+ }
+ return;
+ }
- if (!DECL_P (NOTE_VAR_LOCATION_DECL (loc_note)))
+ var_loc_p = NOTE_KIND (loc_note) == NOTE_INSN_VAR_LOCATION;
+ if (var_loc_p && !DECL_P (NOTE_VAR_LOCATION_DECL (loc_note)))
return;
next_real = next_real_insn (loc_note);
+
/* If there are no instructions which would be affected by this note,
don't do anything. */
- if (next_real == NULL_RTX && !NOTE_DURING_CALL_P (loc_note))
+ if (var_loc_p
+ && next_real == NULL_RTX
+ && !NOTE_DURING_CALL_P (loc_note))
return;
+ if (next_real == NULL_RTX)
+ next_real = get_last_insn ();
+
/* If there were any real insns between note we processed last time
and this note (or if it is the first note), clear
last_{,postcall_}label so that they are not reused this time. */
@@ -21920,12 +22191,20 @@ dwarf2out_var_location (rtx loc_note)
last_postcall_label = NULL;
}
- decl = NOTE_VAR_LOCATION_DECL (loc_note);
- newloc = add_var_loc_to_decl (decl, loc_note,
- NOTE_DURING_CALL_P (loc_note)
- ? last_postcall_label : last_label);
- if (newloc == NULL)
- return;
+ if (var_loc_p)
+ {
+ decl = NOTE_VAR_LOCATION_DECL (loc_note);
+ newloc = add_var_loc_to_decl (decl, loc_note,
+ NOTE_DURING_CALL_P (loc_note)
+ ? last_postcall_label : last_label);
+ if (newloc == NULL)
+ return;
+ }
+ else
+ {
+ decl = NULL_TREE;
+ newloc = NULL;
+ }
/* If there were no real insns between note we processed last time
and this note, use the label we emitted last time. Otherwise
@@ -21938,7 +22217,43 @@ dwarf2out_var_location (rtx loc_note)
last_label = ggc_strdup (loclabel);
}
- if (!NOTE_DURING_CALL_P (loc_note))
+ if (!var_loc_p)
+ {
+ struct call_arg_loc_node *ca_loc
+ = ggc_alloc_cleared_call_arg_loc_node ();
+ rtx prev = prev_real_insn (loc_note), x;
+ ca_loc->call_arg_loc_note = loc_note;
+ ca_loc->next = NULL;
+ ca_loc->label = last_label;
+ gcc_assert (prev
+ && (CALL_P (prev)
+ || (NONJUMP_INSN_P (prev)
+ && GET_CODE (PATTERN (prev)) == SEQUENCE
+ && CALL_P (XVECEXP (PATTERN (prev), 0, 0)))));
+ if (!CALL_P (prev))
+ prev = XVECEXP (PATTERN (prev), 0, 0);
+ ca_loc->tail_call_p = SIBLING_CALL_P (prev);
+ x = PATTERN (prev);
+ if (GET_CODE (x) == PARALLEL)
+ x = XVECEXP (x, 0, 0);
+ if (GET_CODE (x) == SET)
+ x = SET_SRC (x);
+ if (GET_CODE (x) == CALL && MEM_P (XEXP (x, 0)))
+ {
+ x = XEXP (XEXP (x, 0), 0);
+ if (GET_CODE (x) == SYMBOL_REF
+ && SYMBOL_REF_DECL (x)
+ && TREE_CODE (SYMBOL_REF_DECL (x)) == FUNCTION_DECL)
+ ca_loc->symbol_ref = x;
+ }
+ ca_loc->block = insn_scope (prev);
+ if (call_arg_locations)
+ call_arg_loc_last->next = ca_loc;
+ else
+ call_arg_locations = ca_loc;
+ call_arg_loc_last = ca_loc;
+ }
+ else if (!NOTE_DURING_CALL_P (loc_note))
newloc->label = last_label;
else
{
@@ -21974,6 +22289,8 @@ dwarf2out_begin_function (tree fun)
}
dwarf2out_note_section_used ();
+ call_site_count = 0;
+ tail_call_site_count = 0;
}
/* Output a label to mark the beginning of a source code line entry
@@ -22804,9 +23121,16 @@ resolve_one_addr (rtx *addr, void *data ATTRIBUTE_UNUSED)
}
if (GET_CODE (rtl) == SYMBOL_REF
- && SYMBOL_REF_DECL (rtl)
- && !TREE_ASM_WRITTEN (SYMBOL_REF_DECL (rtl)))
- return 1;
+ && SYMBOL_REF_DECL (rtl))
+ {
+ if (TREE_CONSTANT_POOL_ADDRESS_P (rtl))
+ {
+ if (!TREE_ASM_WRITTEN (DECL_INITIAL (SYMBOL_REF_DECL (rtl))))
+ return 1;
+ }
+ else if (!TREE_ASM_WRITTEN (SYMBOL_REF_DECL (rtl)))
+ return 1;
+ }
if (GET_CODE (rtl) == CONST
&& for_each_rtx (&XEXP (rtl, 0), resolve_one_addr, NULL))
@@ -22898,6 +23222,28 @@ resolve_addr (dw_die_ref die)
remove_AT (die, a->dw_attr);
ix--;
}
+ if (die->die_tag == DW_TAG_GNU_call_site
+ && a->dw_attr == DW_AT_abstract_origin)
+ {
+ tree tdecl = SYMBOL_REF_DECL (a->dw_attr_val.v.val_addr);
+ dw_die_ref tdie = lookup_decl_die (tdecl);
+ if (tdie == NULL && DECL_EXTERNAL (tdecl))
+ {
+ force_decl_die (tdecl);
+ tdie = lookup_decl_die (tdecl);
+ }
+ if (tdie)
+ {
+ a->dw_attr_val.val_class = dw_val_class_die_ref;
+ a->dw_attr_val.v.val_die_ref.die = tdie;
+ a->dw_attr_val.v.val_die_ref.external = 0;
+ }
+ else
+ {
+ remove_AT (die, a->dw_attr);
+ ix--;
+ }
+ }
break;
default:
break;