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

Reply via email to