diff mbox series

[3/4] Write S_INLINEELINES CodeView subsection

Message ID 20241107043915.2112-3-mark@harmstone.com
State New
Headers show
Series [1/4] Add block parameter to begin_block debug hook | expand

Commit Message

Mark Harmstone Nov. 7, 2024, 4:39 a.m. UTC
When outputting the .debug$S CodeView section, also write an
S_INLINEELINES subsection, which records the filename and line number of
the start of each inlined function.

gcc/
	* dwarf2codeview.cc (DEBUG_S_INLINEELINES): Define.
	(CV_INLINEE_SOURCE_LINE_SIGNATURE): Define.
	(struct codeview_inlinee_lines): Define.
	(struct inlinee_lines_hasher): Define.
	(func_htab, inlinee_lines_htab): New global variables.
	(get_file_id): New function.
	(codeview_source_line): Move file_id logic to get_file_id.
	(write_inlinee_lines_entry): New function.
	(write_inlinee_lines): New function.
	(codeview_debug_finish): Call write_inlinee_lines, and free func_htab
	and inlinee_lines_htab.
	(get_func_id): New function.
	(add_function): Move func_id logic to get_func_id.
	(codeview_abstract_function): New function.
	* dwarf2codeview.h (codeview_abstract_function): Add declaration.
	* dwarf2out.cc (dwarf2out_abstract_function): Call
	codeview_abstract_function if outputting CodeView debug info.
---
 gcc/dwarf2codeview.cc | 251 ++++++++++++++++++++++++++++++++++++------
 gcc/dwarf2codeview.h  |   1 +
 gcc/dwarf2out.cc      |   5 +
 3 files changed, 224 insertions(+), 33 deletions(-)
diff mbox series

Patch

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index df59fbcf288..4fcf3f5f1e2 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -44,6 +44,7 @@  along with GCC; see the file COPYING3.  If not see
 #define DEBUG_S_LINES		0xf2
 #define DEBUG_S_STRINGTABLE     0xf3
 #define DEBUG_S_FILECHKSMS      0xf4
+#define DEBUG_S_INLINEELINES	0xf6
 
 #define CHKSUM_TYPE_MD5		1
 
@@ -53,6 +54,8 @@  along with GCC; see the file COPYING3.  If not see
 #define CV_CFL_C		0x00
 #define CV_CFL_CXX		0x01
 
+#define CV_INLINEE_SOURCE_LINE_SIGNATURE	0x0
+
 #define FIRST_TYPE		0x1000
 
 #define LINE_LABEL	"Lcvline"
@@ -1140,6 +1143,14 @@  struct codeview_line_block
   codeview_line *lines, *last_line;
 };
 
+struct codeview_inlinee_lines
+{
+  codeview_inlinee_lines *next;
+  uint32_t func_id;
+  uint32_t file_id;
+  uint32_t starting_line;
+};
+
 struct codeview_function
 {
   codeview_function *next;
@@ -1411,6 +1422,21 @@  struct method_hasher : nofree_ptr_hash <struct codeview_method>
   }
 };
 
+struct inlinee_lines_hasher : free_ptr_hash <struct codeview_inlinee_lines>
+{
+  typedef uint32_t compare_type;
+
+  static hashval_t hash (const codeview_inlinee_lines *il)
+  {
+    return il->func_id;
+  }
+
+  static bool equal (const codeview_inlinee_lines *il, uint32_t func_id)
+  {
+    return il->func_id == func_id;
+  }
+};
+
 static unsigned int line_label_num;
 static unsigned int func_label_num;
 static unsigned int sym_label_num;
@@ -1423,10 +1449,11 @@  static codeview_function *funcs, *last_func, *cur_func;
 static const char* last_filename;
 static uint32_t last_file_id;
 static codeview_symbol *sym, *last_sym;
-static hash_table<die_hasher> *types_htab;
+static hash_table<die_hasher> *types_htab, *func_htab;
 static codeview_custom_type *custom_types, *last_custom_type;
 static codeview_deferred_type *deferred_types, *last_deferred_type;
 static hash_table<string_id_hasher> *string_id_htab;
+static hash_table<inlinee_lines_hasher> *inlinee_lines_htab;
 
 static uint32_t get_type_num (dw_die_ref type, bool in_struct, bool no_fwd_ref);
 static uint32_t get_type_num_subroutine_type (dw_die_ref type, bool in_struct,
@@ -1435,6 +1462,39 @@  static uint32_t get_type_num_subroutine_type (dw_die_ref type, bool in_struct,
 					      int32_t this_adjustment);
 static void write_cv_padding (size_t padding);
 
+/* Return the file ID corresponding to a given source filename.  */
+
+static uint32_t
+get_file_id (const char *filename)
+{
+  codeview_source_file *sf = files;
+
+  if (filename == last_filename)
+    return last_file_id;
+
+  while (sf)
+    {
+      if (!strcmp (sf->filename, filename))
+	{
+	  uint32_t file_id;
+
+	  /* 0x18 is the size of the checksum entry for each file.
+	     0x6 bytes for the header, plus 0x10 bytes for the hash,
+	     then padded to a multiple of 4.  */
+
+	  file_id = sf->file_num * 0x18;
+	  last_filename = filename;
+	  last_file_id = file_id;
+
+	  return file_id;
+	}
+
+      sf = sf->next;
+    }
+
+  return 0;
+}
+
 /* Allocate and initialize a codeview_function struct.  */
 
 static codeview_function *
@@ -1465,7 +1525,7 @@  void
 codeview_source_line (unsigned int line_no, const char *filename)
 {
   codeview_line *l;
-  uint32_t file_id = last_file_id;
+  uint32_t file_id = get_file_id (filename);
   unsigned int label_num = ++line_label_num;
 
   targetm.asm_out.internal_label (asm_out_file, LINE_LABEL, label_num);
@@ -1479,28 +1539,6 @@  codeview_source_line (unsigned int line_no, const char *filename)
       cur_func = f;
     }
 
-  if (filename != last_filename)
-    {
-      codeview_source_file *sf = files;
-
-      while (sf)
-	{
-	  if (!strcmp (sf->filename, filename))
-	    {
-	      /* 0x18 is the size of the checksum entry for each file.
-		 0x6 bytes for the header, plus 0x10 bytes for the hash,
-		 then padded to a multiple of 4.  */
-
-	      file_id = sf->file_num * 0x18;
-	      last_filename = filename;
-	      last_file_id = file_id;
-	      break;
-	    }
-
-	  sf = sf->next;
-	}
-    }
-
   if (!cur_func->last_block || cur_func->last_block->file_id != file_id)
     {
       codeview_line_block *b;
@@ -1899,6 +1937,66 @@  write_line_numbers (void)
     }
 }
 
+/* Write an entry in the S_INLINEELINES subsection of .debug$S.  */
+
+static int
+write_inlinee_lines_entry (codeview_inlinee_lines **slot,
+			   void *ctx ATTRIBUTE_UNUSED)
+{
+  codeview_inlinee_lines *il = *slot;
+
+  /* The inlinee lines data consists of a version uint32_t (0), followed by
+     an array of struct inlinee_source_line:
+
+      struct inlinee_source_line
+      {
+	  uint32_t function_id;
+	  uint32_t file_id;
+	  uint32_t line_no;
+      };
+
+    (see InlineeSourceLine in cvinfo.h)
+  */
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, il->func_id);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, il->file_id);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, il->starting_line);
+  putc ('\n', asm_out_file);
+
+  return 1;
+}
+
+/* Write the S_INLINEELINES subsection of .debug$S, which lists the filename
+   and line number for the start of each inlined function.  */
+
+static void
+write_inlinee_lines (void)
+{
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, DEBUG_S_INLINEELINES);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  asm_fprintf (asm_out_file,
+	       "%LLcv_inlineelines_end - %LLcv_inlineelines_start\n");
+  asm_fprintf (asm_out_file, "%LLcv_inlineelines_start:\n");
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, CV_INLINEE_SOURCE_LINE_SIGNATURE);
+  putc ('\n', asm_out_file);
+
+  inlinee_lines_htab->traverse <void*, write_inlinee_lines_entry> (NULL);
+
+  asm_fprintf (asm_out_file, "%LLcv_inlineelines_end:\n");
+}
+
 /* Treat cold sections as separate functions, for the purposes of line
    numbers.  */
 
@@ -4714,6 +4812,10 @@  codeview_debug_finish (void)
   write_strings_table ();
   write_source_files ();
   write_line_numbers ();
+
+  if (inlinee_lines_htab)
+    write_inlinee_lines ();
+
   write_codeview_symbols ();
 
   if (custom_types)
@@ -4746,8 +4848,14 @@  codeview_debug_finish (void)
   if (types_htab)
     delete types_htab;
 
+  if (func_htab)
+    delete func_htab;
+
   if (string_id_htab)
     delete string_id_htab;
+
+  if (inlinee_lines_htab)
+    delete inlinee_lines_htab;
 }
 
 /* Translate a DWARF base type (DW_TAG_base_type) into its CodeView
@@ -6573,20 +6681,29 @@  add_lf_mfunc_id (dw_die_ref die, const char *name)
   return ct->num;
 }
 
-/* Process a DW_TAG_subprogram DIE, and add an S_GPROC32_ID or S_LPROC32_ID
-   symbol for this.  */
+/* Generate a new LF_FUNC_ID or LF_MFUNC_ID type for a DW_TAG_subprogram DIE
+   and return its number, or return the existing type number if already
+   present.  */
 
-static void
-add_function (dw_die_ref die)
+static uint32_t
+get_func_id (dw_die_ref die)
 {
   const char *name = get_AT_string (die, DW_AT_name);
-  uint32_t func_id_type;
-  codeview_symbol *s;
   dw_die_ref spec = get_AT_ref (die, DW_AT_specification);
   bool do_mfunc_id = false;
+  codeview_type **slot, *t;
+  uint32_t num;
 
   if (!name)
-    return;
+    return 0;
+
+  if (!func_htab)
+    func_htab = new hash_table<die_hasher> (10);
+
+  slot = func_htab->find_slot_with_hash (die, htab_hash_pointer (die), INSERT);
+
+  if (slot && *slot)
+    return (*slot)->num;
 
   if (spec && dw_get_die_parent (spec))
     {
@@ -6604,9 +6721,33 @@  add_function (dw_die_ref die)
     }
 
   if (do_mfunc_id)
-    func_id_type = add_lf_mfunc_id (die, name);
+    num = add_lf_mfunc_id (die, name);
   else
-    func_id_type = add_lf_func_id (die, name);
+    num = add_lf_func_id (die, name);
+
+  t = (codeview_type *) xmalloc (sizeof (codeview_type));
+
+  t->die = die;
+  t->num = num;
+  t->is_fwd_ref = false;
+
+  *slot = t;
+
+  return num;
+}
+
+/* Process a DW_TAG_subprogram DIE, and add an S_GPROC32_ID or S_LPROC32_ID
+   symbol for this.  */
+
+static void
+add_function (dw_die_ref die)
+{
+  uint32_t func_id_type;
+  codeview_symbol *s;
+
+  func_id_type = get_func_id (die);
+  if (func_id_type == 0)
+    return;
 
   /* Add an S_GPROC32_ID / S_LPROC32_ID symbol.  */
 
@@ -6630,6 +6771,50 @@  add_function (dw_die_ref die)
   last_sym = s;
 }
 
+/* If we have encountered a new inlined function, add this to
+   inlinee_lines_htab so that it can be output to the S_INLINEELINES subsection
+   of .debug$S.  */
+
+void
+codeview_abstract_function (tree decl)
+{
+  codeview_inlinee_lines *il, **slot;
+  dw_die_ref die;
+  uint32_t func_id;
+  struct dwarf_file_data *file;
+
+  if (!DECL_DECLARED_INLINE_P (decl))
+    return;
+
+  die = lookup_decl_die (decl);
+  if (!die)
+    return;
+
+  func_id = get_func_id (die);
+  if (func_id == 0)
+    return;
+
+  file = get_AT_file (die, DW_AT_decl_file);
+  if (!file)
+    return;
+
+  if (!inlinee_lines_htab)
+    inlinee_lines_htab = new hash_table<inlinee_lines_hasher> (10);
+
+  slot = inlinee_lines_htab->find_slot_with_hash (func_id, func_id, INSERT);
+  if (*slot)
+    return;
+
+  il = (codeview_inlinee_lines *) xmalloc (sizeof (codeview_inlinee_lines));
+
+  il->next = NULL;
+  il->func_id = func_id;
+  il->file_id = get_file_id (file->filename);
+  il->starting_line = get_AT_unsigned (die, DW_AT_decl_line);
+
+  *slot = il;
+}
+
 /* Loop through the DIEs that have been output for our TU, and add CodeView
    symbols for them.  */
 
diff --git a/gcc/dwarf2codeview.h b/gcc/dwarf2codeview.h
index 367334d68cb..3829bab093d 100644
--- a/gcc/dwarf2codeview.h
+++ b/gcc/dwarf2codeview.h
@@ -97,5 +97,6 @@  extern void codeview_end_epilogue (void);
 extern void codeview_debug_early_finish (dw_die_ref die);
 extern void codeview_begin_block (unsigned int, unsigned int, tree);
 extern void codeview_end_block (unsigned int, unsigned int);
+extern void codeview_abstract_function (tree);
 
 #endif /* GCC_DWARF2CODEVIEW_H */
diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
index 2efcb52723a..d6774a843f0 100644
--- a/gcc/dwarf2out.cc
+++ b/gcc/dwarf2out.cc
@@ -23409,6 +23409,11 @@  dwarf2out_abstract_function (tree decl)
   if (DECL_IGNORED_P (decl))
     return;
 
+#ifdef CODEVIEW_DEBUGGING_INFO
+  if (codeview_debuginfo_p ())
+    codeview_abstract_function (decl);
+#endif
+
   /* In LTO we're all set.  We already created abstract instances
      early and we want to avoid creating a concrete instance of that
      if we don't output it.  */