If a function is contained in two sections, for instance if it is partially cold, it gets a DW_AT_ranges attribute in its DIE rather than the normal DW_AT_low_pc and DW_AT_high_pc.
There's no way to express this in CodeView, so rather than skipping the function entirely, use the labels in the first entry in the range list. gcc/ * dwarf2codeview.cc (write_function): If no DW_AT_low_pc or DW_AT_high_pc in DIE, handle DW_AT_ranges instead. * dwarf2out.cc (struct dw_ranges): Move to dwarf2out.h. (get_AT_range_list): New function. (get_range_list_labels): New function. * dwarf2out.h (struct dw_ranges): Move from dwarf2out.cc. (get_AT_range_list, get_range_list_labels): Add declarations. --- gcc/dwarf2codeview.cc | 41 +++++++++++++++++++++++------------- gcc/dwarf2out.cc | 49 ++++++++++++++++++++++++++----------------- gcc/dwarf2out.h | 21 +++++++++++++++++++ 3 files changed, 77 insertions(+), 34 deletions(-) diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc index 5e8a4ab39e7..e12b65bc67b 100644 --- a/gcc/dwarf2codeview.cc +++ b/gcc/dwarf2codeview.cc @@ -3209,29 +3209,40 @@ write_function (codeview_symbol *s) */ loc_low = get_AT (s->function.die, DW_AT_low_pc); - if (!loc_low) - goto end; + loc_high = get_AT (s->function.die, DW_AT_high_pc); - if (loc_low->dw_attr_val.val_class != dw_val_class_lbl_id) - goto end; + if (loc_low && loc_high) + { + if (loc_low->dw_attr_val.val_class != dw_val_class_lbl_id) + goto end; - label_low = loc_low->dw_attr_val.v.val_lbl_id; - if (!label_low) - goto end; + label_low = loc_low->dw_attr_val.v.val_lbl_id; - rtx_low = gen_rtx_SYMBOL_REF (Pmode, label_low); + if (loc_high->dw_attr_val.val_class != dw_val_class_high_pc) + goto end; - loc_high = get_AT (s->function.die, DW_AT_high_pc); - if (!loc_high) - goto end; + label_high = loc_high->dw_attr_val.v.val_lbl_id; + } + else + { + /* If a function is split over multiple sections, such as if it is + partially cold, its DIE gets a DW_AT_ranges rather than a + DW_AT_low_pc and DW_AT_high_pc. + There's no easy way to represent this in CodeView, so just choose the + first pair of labels rather than skipping the function entirely. */ - if (loc_high->dw_attr_val.val_class != dw_val_class_high_pc) - goto end; + dw_ranges *ranges = get_AT_range_list (s->function.die, DW_AT_ranges); - label_high = loc_high->dw_attr_val.v.val_lbl_id; - if (!label_high) + if (!ranges) + goto end; + + get_range_list_labels (ranges, &label_low, &label_high); + } + + if (!label_low || !label_high) goto end; + rtx_low = gen_rtx_SYMBOL_REF (Pmode, label_low); rtx_high = gen_rtx_SYMBOL_REF (Pmode, label_high); /* Output the S_GPROC32_ID / S_LPROC32_ID record. */ diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc index a2621ec7c76..97911d6f264 100644 --- a/gcc/dwarf2out.cc +++ b/gcc/dwarf2out.cc @@ -3206,25 +3206,6 @@ typedef struct GTY(()) pubname_struct { } pubname_entry; - -struct GTY(()) dw_ranges { - const char *label; - /* If this is positive, it's a block number, otherwise it's a - bitwise-negated index into dw_ranges_by_label. */ - int num; - /* If idx is equal to DW_RANGES_IDX_SKELETON, it should be emitted - into .debug_rnglists section rather than .debug_rnglists.dwo - for -gsplit-dwarf and DWARF >= 5. */ -#define DW_RANGES_IDX_SKELETON ((1U << 31) - 1) - /* Index for the range list for DW_FORM_rnglistx. */ - unsigned int idx : 31; - /* True if this range might be possibly in a different section - from previous entry. */ - unsigned int maybe_new_sec : 1; - addr_table_entry *begin_entry; - addr_table_entry *end_entry; -}; - /* A structure to hold a macinfo entry. */ typedef struct GTY(()) macinfo_struct { @@ -5532,6 +5513,36 @@ get_AT_file (dw_die_ref die, enum dwarf_attribute attr_kind) return a ? AT_file (a) : NULL; } +dw_ranges * +get_AT_range_list (dw_die_ref die, enum dwarf_attribute attr_kind) +{ + dw_attr_node *a = get_AT (die, attr_kind); + + if (!a) + return NULL; + + return &(*ranges_table)[a->dw_attr_val.v.val_offset]; +} + +void +get_range_list_labels (dw_ranges *r, const char **begin, const char **end) +{ + int lab_idx = - r->num - 1; + + /* Positive r->num values are block nums, negative values are indexes + into ranges_by_label. */ + + if (r->num > 0) + { + *begin = NULL; + *end = NULL; + return; + } + + *begin = (*ranges_by_label)[lab_idx].begin; + *end = (*ranges_by_label)[lab_idx].end; +} + /* Return TRUE if the language is C. */ static inline bool diff --git a/gcc/dwarf2out.h b/gcc/dwarf2out.h index 27919594bef..60623eee96b 100644 --- a/gcc/dwarf2out.h +++ b/gcc/dwarf2out.h @@ -532,4 +532,25 @@ struct GTY((for_user)) dwarf_file_data { extern struct dwarf_file_data *get_AT_file (dw_die_ref, enum dwarf_attribute); +struct GTY(()) dw_ranges { + const char *label; + /* If this is positive, it's a block number, otherwise it's a + bitwise-negated index into dw_ranges_by_label. */ + int num; + /* If idx is equal to DW_RANGES_IDX_SKELETON, it should be emitted + into .debug_rnglists section rather than .debug_rnglists.dwo + for -gsplit-dwarf and DWARF >= 5. */ +#define DW_RANGES_IDX_SKELETON ((1U << 31) - 1) + /* Index for the range list for DW_FORM_rnglistx. */ + unsigned int idx : 31; + /* True if this range might be possibly in a different section + from previous entry. */ + unsigned int maybe_new_sec : 1; + addr_table_entry *begin_entry; + addr_table_entry *end_entry; +}; + +extern dw_ranges *get_AT_range_list (dw_die_ref, enum dwarf_attribute); +extern void get_range_list_labels (dw_ranges *, const char **, const char **); + #endif /* GCC_DWARF2OUT_H */ -- 2.45.2